Commit 8a021e74 authored by Lorenz Meier's avatar Lorenz Meier

Merged mavlink-ftp

parents 95862015 4b861414
......@@ -186,22 +186,27 @@ DebugBuild {
src/qgcunittest/MockUASManager.h \
src/qgcunittest/MockUAS.h \
src/qgcunittest/MockQGCUASParamManager.h \
src/qgcunittest/MockMavlinkInterface.h \
src/qgcunittest/MockMavlinkFileServer.h \
src/qgcunittest/MultiSignalSpy.h \
src/qgcunittest/FlightModeConfigTest.h \
src/qgcunittest/FlightGearTest.h \
src/qgcunittest/TCPLinkTest.h \
src/qgcunittest/TCPLoopBackServer.h
src/qgcunittest/TCPLoopBackServer.h \
src/qgcunittest/QGCUASFileManagerTest.h
SOURCES += \
src/qgcunittest/UASUnitTest.cc \
src/qgcunittest/MockUASManager.cc \
src/qgcunittest/MockUAS.cc \
src/qgcunittest/MockQGCUASParamManager.cc \
src/qgcunittest/MockMavlinkFileServer.cc \
src/qgcunittest/MultiSignalSpy.cc \
src/qgcunittest/FlightModeConfigTest.cc \
src/qgcunittest/FlightGearTest.cc \
src/qgcunittest/TCPLinkTest.cc \
src/qgcunittest/TCPLoopBackServer.cc
src/qgcunittest/TCPLoopBackServer.cc \
src/qgcunittest/QGCUASFileManagerTest.cc
}
#
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "MockMavlinkFileServer.h"
void MockMavlinkFileServer::sendMessage(mavlink_message_t message)
{
QGCUASFileManager::Request ackResponse;
QString path;
Q_ASSERT(message.msgid == MAVLINK_MSG_ID_ENCAPSULATED_DATA);
mavlink_encapsulated_data_t requestEncapsulatedData;
mavlink_msg_encapsulated_data_decode(&message, &requestEncapsulatedData);
QGCUASFileManager::Request* request = (QGCUASFileManager::Request*)&requestEncapsulatedData.data[0];
// Validate CRC
if (request->hdr.crc32 != QGCUASFileManager::crc32(request)) {
_sendNak(QGCUASFileManager::kErrCrc);
}
switch (request->hdr.opcode) {
case QGCUASFileManager::kCmdTestNoAck:
// ignored, ack not sent back, for testing only
break;
case QGCUASFileManager::kCmdReset:
// terminates all sessions
// Fall through to send back Ack
case QGCUASFileManager::kCmdNone:
// ignored, always acked
ackResponse.hdr.magic = 'f';
ackResponse.hdr.opcode = QGCUASFileManager::kRspAck;
ackResponse.hdr.session = 0;
ackResponse.hdr.crc32 = 0;
ackResponse.hdr.size = 0;
_emitResponse(&ackResponse);
break;
case QGCUASFileManager::kCmdList:
// We only support root path
path = (char *)&request->data[0];
if (!path.isEmpty() && path != "/") {
_sendNak(QGCUASFileManager::kErrNotDir);
break;
}
if (request->hdr.offset > (uint32_t)_fileList.size()) {
_sendNak(QGCUASFileManager::kErrEOF);
break;
}
ackResponse.hdr.magic = 'f';
ackResponse.hdr.opcode = QGCUASFileManager::kRspAck;
ackResponse.hdr.session = 0;
ackResponse.hdr.size = 0;
if (request->hdr.offset == 0) {
// Requesting first batch of file names
Q_ASSERT(_fileList.size());
char *bufPtr = (char *)&ackResponse.data[0];
for (int i=0; i<_fileList.size(); i++) {
const char *filename = _fileList[i].toStdString().c_str();
size_t cchFilename = strlen(filename);
strcpy(bufPtr, filename);
ackResponse.hdr.size += cchFilename + 1;
bufPtr += cchFilename + 1;
}
// Final double termination
*bufPtr = 0;
ackResponse.hdr.size++;
} else {
// All filenames fit in first ack, send final null terminated ack
ackResponse.data[0] = 0;
ackResponse.hdr.size = 1;
}
_emitResponse(&ackResponse);
break;
// Remainder of commands are NYI
case QGCUASFileManager::kCmdTerminate:
// releases sessionID, closes file
case QGCUASFileManager::kCmdOpen:
// opens <path> for reading, returns <session>
case QGCUASFileManager::kCmdRead:
// reads <size> bytes from <offset> in <session>
case QGCUASFileManager::kCmdCreate:
// creates <path> for writing, returns <session>
case QGCUASFileManager::kCmdWrite:
// appends <size> bytes at <offset> in <session>
case QGCUASFileManager::kCmdRemove:
// remove file (only if created by server?)
default:
// nack for all NYI opcodes
_sendNak(QGCUASFileManager::kErrUnknownCommand);
break;
}
}
void MockMavlinkFileServer::_sendNak(QGCUASFileManager::ErrorCode error)
{
QGCUASFileManager::Request nakResponse;
nakResponse.hdr.magic = 'f';
nakResponse.hdr.opcode = QGCUASFileManager::kRspNak;
nakResponse.hdr.session = 0;
nakResponse.hdr.size = sizeof(nakResponse.data[0]);
nakResponse.data[0] = error;
_emitResponse(&nakResponse);
}
void MockMavlinkFileServer::_emitResponse(QGCUASFileManager::Request* request)
{
mavlink_message_t mavlinkMessage;
request->hdr.crc32 = QGCUASFileManager::crc32(request);
mavlink_msg_encapsulated_data_pack(250, 0, &mavlinkMessage, 0 /*_encdata_seq*/, (uint8_t*)request);
emit messageReceived(NULL, mavlinkMessage);
}
\ No newline at end of file
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef MOCKMAVLINKFILESERVER_H
#define MOCKMAVLINKFILESERVER_H
#include "MockMavlinkInterface.h"
#include "QGCUASFileManager.h"
#include <QStringList>
class MockMavlinkFileServer : public MockMavlinkInterface
{
Q_OBJECT
public:
MockMavlinkFileServer(void) { };
void setFileList(QStringList& fileList) { _fileList = fileList; }
// From MockMavlinkInterface
virtual void sendMessage(mavlink_message_t message);
private:
void _sendNak(QGCUASFileManager::ErrorCode error);
void _emitResponse(QGCUASFileManager::Request* request);
QStringList _fileList;
};
#endif
//
// MockMavlinkInterface.cc
// QGroundControl
//
// Created by Donald Gagne on 6/19/14.
// Copyright (c) 2014 Donald Gagne. All rights reserved.
//
#include "MockMavlinkInterface.h"
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include <QObject>
#include "QGCMAVLink.h"
#include "LinkInterface.h"
#ifndef MOCKMAVLINKINTERFACE_H
#define MOCKMAVLINKINTERFACE_H
class MockMavlinkInterface : public QObject
{
Q_OBJECT
public:
virtual void sendMessage(mavlink_message_t message) = 0;
signals:
// link argument will always be NULL
void messageReceived(LinkInterface* link, mavlink_message_t message);
};
#endif
......@@ -27,7 +27,8 @@ QString MockUAS::_bogusStaticString;
MockUAS::MockUAS(void) :
_systemType(MAV_TYPE_QUADROTOR),
_systemId(1)
_systemId(1),
_mavlinkPlugin(NULL)
{
}
......@@ -41,4 +42,15 @@ void MockUAS::setMockParametersAndSignal(MockQGCUASParamManager::ParamMap_t& map
i.next();
emit parameterChanged(_systemId, 0, i.key(), i.value());
}
}
void MockUAS::sendMessage(mavlink_message_t message)
{
Q_UNUSED(link);
if (!_mavlinkPlugin) {
Q_ASSERT(false);
}
_mavlinkPlugin->sendMessage(message);
}
\ No newline at end of file
......@@ -26,6 +26,7 @@
#include "UASInterface.h"
#include "MockQGCUASParamManager.h"
#include "MockMavlinkInterface.h"
#include <limits>
......@@ -50,6 +51,9 @@ public:
virtual int getUASID(void) const { return _systemId; }
virtual QGCUASParamManagerInterface* getParamManager() { return &_paramManager; };
// sendMessage is only supported if a mavlink plugin is installed.
virtual void sendMessage(mavlink_message_t message);
public:
// MockUAS methods
MockUAS(void);
......@@ -63,12 +67,15 @@ public:
/// allows you to simulate parameter input and validate parameter setting
MockQGCUASParamManager* getMockQGCUASParamManager(void) { return &_paramManager; }
/// Sets the parameter map into the mock QGCUASParamManager and signals parameterChanged for
/// @brief Sets the parameter map into the mock QGCUASParamManager and signals parameterChanged for
/// each param
void setMockParametersAndSignal(MockQGCUASParamManager::ParamMap_t& map);
void emitRemoteControlChannelRawChanged(int channel, float raw) { emit remoteControlChannelRawChanged(channel, raw); }
/// @brief Installs a mavlink plugin. Only a single mavlink plugin is supported at a time.
void setMockMavlinkPlugin(MockMavlinkInterface* mavlinkPlugin) { _mavlinkPlugin = mavlinkPlugin; };
public:
// Unimplemented UASInterface overrides
virtual QString getUASName() const { Q_ASSERT(false); return _bogusString; };
......@@ -112,9 +119,7 @@ public:
virtual QGCUASFileManager* getFileManager() {Q_ASSERT(false); return NULL; }
/** @brief Send a message over this link (to this or to all UAS on this link) */
virtual void sendMessage(LinkInterface* link, mavlink_message_t message){Q_ASSERT(false);}
/** @brief Send a message over all links this UAS can be reached with (!= all links) */
virtual void sendMessage(mavlink_message_t message) {Q_ASSERT(false);}
virtual void sendMessage(LinkInterface* link, mavlink_message_t message){ Q_UNUSED(link); Q_UNUSED(message); Q_ASSERT(false); }
virtual QString getAutopilotTypeName() { Q_ASSERT(false); return _bogusString; };
virtual void setAutopilotType(int apType) { Q_UNUSED(apType); Q_ASSERT(false); };
virtual QMap<int, QString> getComponents() { Q_ASSERT(false); return _bogusMapIntQString; };
......@@ -182,6 +187,8 @@ private:
int _systemId;
MockQGCUASParamManager _paramManager;
MockMavlinkInterface* _mavlinkPlugin; ///< Mock Mavlink plugin, NULL for none
// Bogus variables used for return types of NYI methods
QString _bogusString;
......
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#include "QGCUASFileManagerTest.h"
/// @file
/// @brief QGCUASFileManager unit test
///
/// @author Don Gagne <don@thegagnes.com>
QGCUASFileManagerUnitTest::QGCUASFileManagerUnitTest(void) :
_fileManager(NULL),
_multiSpy(NULL)
{
}
// Called once before all test cases are run
void QGCUASFileManagerUnitTest::initTestCase(void)
{
_mockUAS.setMockMavlinkPlugin(&_mockFileServer);
}
// Called before every test case
void QGCUASFileManagerUnitTest::init(void)
{
Q_ASSERT(_multiSpy == NULL);
_fileManager = new QGCUASFileManager(NULL, &_mockUAS);
Q_CHECK_PTR(_fileManager);
bool connected = connect(&_mockFileServer, SIGNAL(messageReceived(LinkInterface*, mavlink_message_t)), _fileManager, SLOT(receiveMessage(LinkInterface*, mavlink_message_t)));
Q_ASSERT(connected);
connected = connect(_fileManager, SIGNAL(statusMessage(const QString&)), this, SLOT(statusMessage(const QString&)));
Q_ASSERT(connected);
_rgSignals[statusMessageSignalIndex] = SIGNAL(statusMessage(const QString&));
_rgSignals[errorMessageSignalIndex] = SIGNAL(errorMessage(const QString&));
_rgSignals[resetStatusMessagesSignalIndex] = SIGNAL(resetStatusMessages(void));
_multiSpy = new MultiSignalSpy();
Q_CHECK_PTR(_multiSpy);
QCOMPARE(_multiSpy->init(_fileManager, _rgSignals, _cSignals), true);
}
// Called after every test case
void QGCUASFileManagerUnitTest::cleanup(void)
{
Q_ASSERT(_multiSpy);
Q_ASSERT(_fileManager);
delete _fileManager;
delete _multiSpy;
_fileManager = NULL;
_multiSpy = NULL;
}
/// @brief Connected to QGCUASFileManager statusMessage signal in order to catch list command output
void QGCUASFileManagerUnitTest::statusMessage(const QString& msg)
{
// Keep a list of all names received so we can test it for correctness
_fileListReceived += msg;
}
void QGCUASFileManagerUnitTest::_ackTest(void)
{
Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// If the file manager doesn't receive an ack it will timeout and emit an error. So make sure
// we don't get any error signals.
QVERIFY(_fileManager->_sendCmdTestAck());
QVERIFY(_multiSpy->checkNoSignals());
}
void QGCUASFileManagerUnitTest::_noAckTest(void)
{
Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// This should not get the ack back and timeout.
QVERIFY(_fileManager->_sendCmdTestNoAck());
QTest::qWait(2000); // Let the file manager timeout, magic number 2 secs must be larger than file manager ack timeout
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask), true);
}
void QGCUASFileManagerUnitTest::_resetTest(void)
{
Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// Send a reset command
// We should not get any signals back from this
QVERIFY(_fileManager->_sendCmdReset());
QVERIFY(_multiSpy->checkNoSignals());
}
void QGCUASFileManagerUnitTest::_listTest(void)
{
Q_ASSERT(_fileManager);
Q_ASSERT(_multiSpy);
Q_ASSERT(_multiSpy->checkNoSignals() == true);
// Send a bogus path
// We should get a single resetStatusMessages signal
// We should get a single errorMessage signal
_fileManager->listRecursively("/bogus");
QCOMPARE(_multiSpy->checkOnlySignalByMask(errorMessageSignalMask | resetStatusMessagesSignalMask), true);
_multiSpy->clearAllSignals();
// Send a list command at the root of the directory tree
// We should get back a single resetStatusMessages signal
// We should not get back an errorMessage signal
// We should get back one or more statusMessage signals
// The returned list should match out inputs
QStringList fileList;
fileList << "Ddir" << "Ffoo" << "Fbar";
_mockFileServer.setFileList(fileList);
QStringList fileListExpected;
fileListExpected << "dir/" << "foo" << "bar";
_fileListReceived.clear();
_fileManager->listRecursively("/");
QCOMPARE(_multiSpy->checkSignalByMask(resetStatusMessagesSignalMask), true); // We should be told to reset status messages
QCOMPARE(_multiSpy->checkNoSignalByMask(errorMessageSignalMask), true); // We should not get an error signals
QVERIFY(_fileListReceived == fileListExpected);
}
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef QGCUASFILEMANAGERTEST_H
#define QGCUASFILEMANAGERTEST_H
#include <QObject>
#include <QtTest/QtTest>
#include "AutoTest.h"
#include "MockUAS.h"
#include "MockMavlinkFileServer.h"
#include "QGCUASFileManager.h"
#include "MultiSignalSpy.h"
/// @file
/// @brief QGCUASFileManager unit test
///
/// @author Don Gagne <don@thegagnes.com>
class QGCUASFileManagerUnitTest : public QObject
{
Q_OBJECT
public:
QGCUASFileManagerUnitTest(void);
private slots:
// Test case initialization
void initTestCase(void);
void init(void);
void cleanup(void);
// Test cases
void _ackTest(void);
void _noAckTest(void);
void _resetTest(void);
void _listTest(void);
// Connected to QGCUASFileManager statusMessage signal
void statusMessage(const QString&);
private:
enum {
statusMessageSignalIndex = 0,
errorMessageSignalIndex,
resetStatusMessagesSignalIndex,
maxSignalIndex
};
enum {
statusMessageSignalMask = 1 << statusMessageSignalIndex,
errorMessageSignalMask = 1 << errorMessageSignalIndex,
resetStatusMessagesSignalMask = 1 << resetStatusMessagesSignalIndex,
};
MockUAS _mockUAS;
MockMavlinkFileServer _mockFileServer;
QGCUASFileManager* _fileManager;
MultiSignalSpy* _multiSpy;
static const size_t _cSignals = maxSignalIndex;
const char* _rgSignals[_cSignals];
QStringList _fileListReceived;
};
DECLARE_TEST(QGCUASFileManagerUnitTest)
#endif
This diff is collapsed.
/*=====================================================================
QGroundControl Open Source Ground Control Station
(c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
This file is part of the QGROUNDCONTROL project
QGROUNDCONTROL is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
QGROUNDCONTROL is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
======================================================================*/
#ifndef QGCUASFILEMANAGER_H
#define QGCUASFILEMANAGER_H
#include <QObject>
#include "UASInterface.h"
class QGCUASFileManager : public QObject
......@@ -9,17 +33,22 @@ class QGCUASFileManager : public QObject
Q_OBJECT
public:
QGCUASFileManager(QObject* parent, UASInterface* uas);
/// These methods are only used for testing purposes.
bool _sendCmdTestAck(void) { return _sendOpcodeOnlyCmd(kCmdNone, kCOAck); };
bool _sendCmdTestNoAck(void) { return _sendOpcodeOnlyCmd(kCmdTestNoAck, kCOAck); };
bool _sendCmdReset(void) { return _sendOpcodeOnlyCmd(kCmdReset, kCOAck); };
signals:
void statusMessage(const QString& msg);
void resetStatusMessages();
void errorMessage(const QString& ms);
public slots:
void receiveMessage(LinkInterface* link, mavlink_message_t message);
void nothingMessage();
void listRecursively(const QString &from);
void downloadPath(const QString& from, const QString& to);
// void timedOut();
protected:
struct RequestHeader
......@@ -30,23 +59,32 @@ protected:
uint8_t size;
uint32_t crc32;
uint32_t offset;
uint8_t data[];
};
struct Request
{
struct RequestHeader hdr;
// The entire Request must fit into the data member of the mavlink_encapsulated_data_t structure. We use as many leftover bytes
// after we use up space for the RequestHeader for the data portion of the Request.
uint8_t data[sizeof(((mavlink_encapsulated_data_t*)0)->data) - sizeof(RequestHeader)];
};
enum Opcode
{
kCmdNone, // ignored, always acked
kCmdNone, // ignored, always acked
kCmdTerminate, // releases sessionID, closes file
kCmdReset, // terminates all sessions
kCmdList, // list files in <path> from <offset>
kCmdOpen, // opens <path> for reading, returns <session>
kCmdRead, // reads <size> bytes from <offset> in <session>
kCmdCreate, // creates <path> for writing, returns <session>
kCmdWrite, // appends <size> bytes at <offset> in <session>
kCmdRemove, // remove file (only if created by server?)
kCmdReset, // terminates all sessions
kCmdList, // list files in <path> from <offset>
kCmdOpen, // opens <path> for reading, returns <session>
kCmdRead, // reads <size> bytes from <offset> in <session>
kCmdCreate, // creates <path> for writing, returns <session>
kCmdWrite, // appends <size> bytes at <offset> in <session>
kCmdRemove, // remove file (only if created by server?)
kRspAck,
kRspNak
kRspNak,
kCmdTestNoAck, // ignored, ack not sent back, for testing only, should timeout waiting for ack
};
enum ErrorCode
......@@ -61,35 +99,49 @@ protected:
kErrNotAppend,
kErrTooBig,
kErrIO,
kErrPerm
kErrPerm,
kErrUnknownCommand,
kErrCrc
};
enum OperationState
{
kCOIdle, // not doing anything
kCOAck, // waiting for an Ack
kCOList