Commit 09c26a30 authored by DonLakeFlyer's avatar DonLakeFlyer

parent 8a95bcb7
......@@ -498,6 +498,8 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin {
src/qgcunittest/TCPLinkTest.h \
src/qgcunittest/TCPLoopBackServer.h \
src/qgcunittest/UnitTest.h \
src/Vehicle/FTPManagerTest.h \
src/Vehicle/InitialConnectTest.h \
src/Vehicle/RequestMessageTest.h \
src/Vehicle/SendMavCommandWithHandlerTest.h \
src/Vehicle/SendMavCommandWithSignallingTest.h \
......@@ -543,6 +545,8 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin {
src/qgcunittest/TCPLoopBackServer.cc \
src/qgcunittest/UnitTest.cc \
src/qgcunittest/UnitTestList.cc \
src/Vehicle/FTPManagerTest.cc \
src/Vehicle/InitialConnectTest.cc \
src/Vehicle/RequestMessageTest.cc \
src/Vehicle/SendMavCommandWithHandlerTest.cc \
src/Vehicle/SendMavCommandWithSignallingTest.cc \
......@@ -679,9 +683,9 @@ HEADERS += \
src/Terrain/TerrainQuery.h \
src/TerrainTile.h \
src/Vehicle/ComponentInformationManager.h \
src/Vehicle/FTPManager.h \
src/Vehicle/GPSRTKFactGroup.h \
src/Vehicle/InitialConnectStateMachine.h \
src/Vehicle/MAVLinkFTPManager.h \
src/Vehicle/MAVLinkLogManager.h \
src/Vehicle/MultiVehicleManager.h \
src/Vehicle/StateMachine.h \
......@@ -705,7 +709,6 @@ HEADERS += \
src/uas/UASMessageHandler.h \
src/AnalyzeView/GeoTagController.h \
src/AnalyzeView/ExifParser.h \
src/uas/FileManager.h \
contains (DEFINES, QGC_ENABLE_PAIRING) {
HEADERS += \
......@@ -720,7 +723,7 @@ HEADERS += \
DebugBuild {
HEADERS += \
src/comm/MockLink.h \
src/comm/MockLinkFileServer.h \
src/comm/MockLinkFTP.h \
src/comm/MockLinkMissionItemHandler.h \
}
......@@ -895,9 +898,9 @@ SOURCES += \
src/Terrain/TerrainQuery.cc \
src/TerrainTile.cc\
src/Vehicle/ComponentInformationManager.cc \
src/Vehicle/FTPManager.cc \
src/Vehicle/GPSRTKFactGroup.cc \
src/Vehicle/InitialConnectStateMachine.cc \
src/Vehicle/MAVLinkFTPManager.cc \
src/Vehicle/MAVLinkLogManager.cc \
src/Vehicle/MultiVehicleManager.cc \
src/Vehicle/StateMachine.cc \
......@@ -921,7 +924,6 @@ SOURCES += \
src/uas/UASMessageHandler.cc \
src/AnalyzeView/GeoTagController.cc \
src/AnalyzeView/ExifParser.cc \
src/uas/FileManager.cc \
contains (DEFINES, QGC_ENABLE_PAIRING) {
SOURCES += \
......@@ -931,7 +933,7 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
DebugBuild {
SOURCES += \
src/comm/MockLink.cc \
src/comm/MockLinkFileServer.cc \
src/comm/MockLinkFTP.cc \
src/comm/MockLinkMissionItemHandler.cc \
}
......
......@@ -28,30 +28,43 @@ QGCTemporaryFile::QGCTemporaryFile(const QString& fileTemplate, QObject* parent)
}
QGCTemporaryFile::~QGCTemporaryFile()
{
if (_autoRemove) {
remove();
}
}
bool QGCTemporaryFile::open(QFile::OpenMode openMode)
{
QDir tempDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
setFileName(_newTempFileFullyQualifiedName(_template));
return QFile::open(openMode);
}
QString QGCTemporaryFile::_newTempFileFullyQualifiedName(const QString& fileTemplate)
{
QString nameTemplate = fileTemplate;
QDir tempDir(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
// Generate unique, non-existing filename
static const char rgDigits[] = "0123456789";
QString tempFilename;
do {
QString uniqueStr;
for (int i=0; i<6; i++) {
uniqueStr += rgDigits[QRandomGenerator::global()->generate() % 10];
}
if (_template.contains("XXXXXX")) {
tempFilename = _template.replace("XXXXXX", uniqueStr, Qt::CaseSensitive);
if (fileTemplate.contains("XXXXXX")) {
tempFilename = nameTemplate.replace("XXXXXX", uniqueStr, Qt::CaseSensitive);
} else {
tempFilename = _template + uniqueStr;
tempFilename = nameTemplate + uniqueStr;
}
} while (tempDir.exists(tempFilename));
setFileName(tempDir.filePath(tempFilename));
return QFile::open(openMode);
return tempDir.filePath(tempFilename);
}
......@@ -7,9 +7,7 @@
*
****************************************************************************/
#ifndef QGCTemporaryFile_H
#define QGCTemporaryFile_H
#pragma once
#include <QFile>
......@@ -30,13 +28,15 @@ public:
// directory path, only file name.
QGCTemporaryFile(const QString& fileTemplate, QObject* parent = nullptr);
/// @brief Opens the file in ReadWrite mode.
/// @returns false - open failed
~QGCTemporaryFile();
bool open(OpenMode openMode = ReadWrite);
void setAutoRemove(bool autoRemove) { _autoRemove = autoRemove; }
private:
static QString _newTempFileFullyQualifiedName(const QString& fileTemplate);
QString _template;
bool _autoRemove = false;
};
#endif
......@@ -9,6 +9,9 @@
#include "ComponentInformationManager.h"
#include "Vehicle.h"
#include "FTPManager.h"
#include <QStandardPaths>
QGC_LOGGING_CATEGORY(ComponentInformationManagerLog, "ComponentInformationManagerLog")
......@@ -22,6 +25,8 @@ int ComponentInformationManager::_cStates = sizeof(ComponentInformationManager::
RequestMetaDataTypeStateMachine::StateFn RequestMetaDataTypeStateMachine::_rgStates[]= {
RequestMetaDataTypeStateMachine::_stateRequestCompInfo,
RequestMetaDataTypeStateMachine::_stateRequestMetaDataJson,
RequestMetaDataTypeStateMachine::_stateRequestTranslationJson,
};
int RequestMetaDataTypeStateMachine::_cStates = sizeof(RequestMetaDataTypeStateMachine::_rgStates) / sizeof(RequestMetaDataTypeStateMachine::_rgStates[0]);
......@@ -43,33 +48,6 @@ const ComponentInformationManager::StateFn* ComponentInformationManager::rgState
return &_rgStates[0];
}
void ComponentInformationManager::_componentInformationReceived(const mavlink_message_t& message)
{
mavlink_component_information_t componentInformation;
mavlink_msg_component_information_decode(&message, &componentInformation);
ComponentInformation_t* pCompInfo = nullptr;
switch (componentInformation.metadata_type) {
case COMP_METADATA_TYPE_VERSION:
qCDebug(ComponentInformationManagerLog) << "COMPONENT_INFORMATION COMP_METADATA_TYPE_VERSION received";
_versionCompInfoAvailable = true;
pCompInfo = &_versionCompInfo;
break;
case COMP_METADATA_TYPE_PARAMETER:
qCDebug(ComponentInformationManagerLog) << "COMPONENT_INFORMATION COMP_METADATA_TYPE_PARAMETER received";
_paramCompInfoAvailable = true;
pCompInfo = &_parameterCompInfo;
break;
}
if (pCompInfo) {
pCompInfo->metadataUID = componentInformation.metadata_uid;
pCompInfo->metadataURI = componentInformation.metadata_uri;
pCompInfo->translationUID = componentInformation.translation_uid;
pCompInfo->translationURI = componentInformation.translation_uri;
}
}
void ComponentInformationManager::requestAllComponentInformation(RequestAllCompleteFn requestAllCompletFn, void * requestAllCompleteFnData)
{
_requestAllCompleteFn = requestAllCompletFn;
......@@ -110,8 +88,9 @@ RequestMetaDataTypeStateMachine::RequestMetaDataTypeStateMachine(ComponentInform
void RequestMetaDataTypeStateMachine::request(COMP_METADATA_TYPE type)
{
_type = type;
_stateIndex = -1;
_compInfoAvailable = false;
_type = type;
_stateIndex = -1;
start();
}
......@@ -136,12 +115,24 @@ QString RequestMetaDataTypeStateMachine::typeToString(void)
return _type == COMP_METADATA_TYPE_VERSION ? "COMP_METADATA_TYPE_VERSION" : "COMP_METADATA_TYPE_PARAM";
}
void RequestMetaDataTypeStateMachine::handleComponentInformation(const mavlink_message_t& message)
{
mavlink_component_information_t componentInformation;
mavlink_msg_component_information_decode(&message, &componentInformation);
_compInfo.metadataUID = componentInformation.metadata_uid;
_compInfo.metadataURI = componentInformation.metadata_uri;
_compInfo.translationUID = componentInformation.translation_uid;
_compInfo.translationURI = componentInformation.translation_uri;
_compInfoAvailable = true;
}
static void _requestMessageResultHandler(void* resultHandlerData, MAV_RESULT result, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t &message)
{
RequestMetaDataTypeStateMachine* requestMachine = static_cast<RequestMetaDataTypeStateMachine*>(resultHandlerData);
if (result == MAV_RESULT_ACCEPTED) {
requestMachine->compMgr()->_componentInformationReceived(message);
requestMachine->handleComponentInformation(message);
} else {
switch (failureCode) {
case Vehicle::RequestMessageFailureCommandError:
......@@ -180,3 +171,63 @@ void RequestMetaDataTypeStateMachine::_stateRequestCompInfo(StateMachine* stateM
}
}
void RequestMetaDataTypeStateMachine::_downloadComplete(const QString& file, const QString& errorMsg)
{
qDebug() << "RequestMetaDataTypeStateMachine::_downloadComplete" << file << errorMsg;
advance();
}
void RequestMetaDataTypeStateMachine::_stateRequestMetaDataJson(StateMachine* stateMachine)
{
RequestMetaDataTypeStateMachine* requestMachine = static_cast<RequestMetaDataTypeStateMachine*>(stateMachine);
Vehicle* vehicle = requestMachine->_compMgr->vehicle();
FTPManager* ftpManager = vehicle->ftpManager();
if (requestMachine->_compInfoAvailable) {
ComponentInformation_t& compInfo = requestMachine->_compInfo;
qCDebug(ComponentInformationManagerLog) << "Downloading metadata json" << compInfo.translationURI;
if (_uriIsFTP(compInfo.metadataURI)) {
connect(ftpManager, &FTPManager::downloadComplete, requestMachine, &RequestMetaDataTypeStateMachine::_downloadComplete);
ftpManager->download(compInfo.metadataURI, QStandardPaths::writableLocation(QStandardPaths::TempLocation));
} else {
// FIXME: NYI
qCDebug(ComponentInformationManagerLog) << "Skipping metadata json download. http download NYI";
}
} else {
qCDebug(ComponentInformationManagerLog) << "Skipping metadata json download. Component informatiom not available";
requestMachine->advance();
}
}
void RequestMetaDataTypeStateMachine::_stateRequestTranslationJson(StateMachine* stateMachine)
{
RequestMetaDataTypeStateMachine* requestMachine = static_cast<RequestMetaDataTypeStateMachine*>(stateMachine);
Vehicle* vehicle = requestMachine->_compMgr->vehicle();
FTPManager* ftpManager = vehicle->ftpManager();
if (requestMachine->_compInfoAvailable) {
ComponentInformation_t& compInfo = requestMachine->_compInfo;
if (compInfo.translationURI.isEmpty()) {
qCDebug(ComponentInformationManagerLog) << "Skipping translation json download. No translation json specified";
requestMachine->advance();
} else {
qCDebug(ComponentInformationManagerLog) << "Downloading translation json" << compInfo.translationURI;
if (_uriIsFTP(compInfo.translationURI)) {
connect(ftpManager, &FTPManager::downloadComplete, requestMachine, &RequestMetaDataTypeStateMachine::_downloadComplete);
ftpManager->download(compInfo.metadataURI, QStandardPaths::writableLocation(QStandardPaths::TempLocation));
} else {
// FIXME: NYI
qCDebug(ComponentInformationManagerLog) << "Skipping translation json download. http download NYI";
}
}
} else {
qCDebug(ComponentInformationManagerLog) << "Skipping translation json download. Component informatiom not available";
requestMachine->advance();
}
}
bool RequestMetaDataTypeStateMachine::_uriIsFTP(const QString& uri)
{
return uri.startsWith("mavlinkftp", Qt::CaseInsensitive);
}
......@@ -18,27 +18,44 @@ Q_DECLARE_LOGGING_CATEGORY(ComponentInformationManagerLog)
class Vehicle;
class ComponentInformationManager;
typedef struct {
uint32_t metadataUID;
QString metadataURI;
uint32_t translationUID;
QString translationURI;
} ComponentInformation_t;
class RequestMetaDataTypeStateMachine : public StateMachine
{
Q_OBJECT
public:
RequestMetaDataTypeStateMachine(ComponentInformationManager* compMgr);
void request(COMP_METADATA_TYPE type);
ComponentInformationManager* compMgr(void) { return _compMgr; }
QString typeToString(void);
void request (COMP_METADATA_TYPE type);
QString typeToString (void);
ComponentInformationManager* compMgr (void) { return _compMgr; }
void handleComponentInformation (const mavlink_message_t& message);
// Overrides from StateMachine
int stateCount (void) const final;
const StateFn* rgStates (void) const final;
void statesCompleted (void) const final;
private slots:
void _downloadComplete(const QString& file, const QString& errorMsg);
private:
static void _stateRequestCompInfo (StateMachine* stateMachine);
static void _stateRequestMetaDataJson (StateMachine* stateMachine);
static void _stateRequestTranslationJson (StateMachine* stateMachine);
static bool _uriIsFTP (const QString& uri);
ComponentInformationManager* _compMgr;
COMP_METADATA_TYPE _type = COMP_METADATA_TYPE_VERSION;
COMP_METADATA_TYPE _type = COMP_METADATA_TYPE_VERSION;
bool _compInfoAvailable = false;
ComponentInformation_t _compInfo;
static StateFn _rgStates[];
static int _cStates;
......@@ -46,30 +63,23 @@ private:
class ComponentInformationManager : public StateMachine
{
Q_OBJECT
public:
ComponentInformationManager(Vehicle* vehicle);
typedef struct {
uint32_t metadataUID;
QString metadataURI;
uint32_t translationUID;
QString translationURI;
} ComponentInformation_t;
typedef void (*RequestAllCompleteFn)(void* requestAllCompleteFnData);
void requestAllComponentInformation (RequestAllCompleteFn requestAllCompletFn, void * requestAllCompleteFnData);
Vehicle* vehicle (void) { return _vehicle; }
// These methods should only be called by RequestMetaDataTypeStateMachine
void _componentInformationReceived(const mavlink_message_t& message);
void _stateRequestCompInfoComplete(void);
// Overrides from StateMachine
int stateCount (void) const final;
const StateFn* rgStates (void) const final;
private:
void _stateRequestCompInfoComplete(void);
static void _stateRequestCompInfoVersion (StateMachine* stateMachine);
static void _stateRequestCompInfoParam (StateMachine* stateMachine);
static void _stateRequestAllCompInfoComplete (StateMachine* stateMachine);
......@@ -86,4 +96,6 @@ private:
static StateFn _rgStates[];
static int _cStates;
friend class RequestMetaDataTypeStateMachine;
};
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include <QObject>
#include <QDir>
#include <QTimer>
#include <QQueue>
#include "UASInterface.h"
#include "QGCLoggingCategory.h"
#include "QGCMAVLink.h"
Q_DECLARE_LOGGING_CATEGORY(FTPManagerLog)
class Vehicle;
class FTPManager : public QObject
{
Q_OBJECT
public:
FTPManager(Vehicle* vehicle);
/// Downloads the specified file.
/// @param from File to download from vehicle, fully qualified path. May be in the format mavlinkftp://...
/// @param toDir Local directory to download file to
/// @return true: download has started, false: error, no download
/// Signals downloadComplete, commandError, commandProgress
bool download(const QString& from, const QString& toDir);
/// Stream downloads the specified file.
/// @param from File to download from UAS, fully qualified path. May be in the format mavlinkftp://...
/// @param toDir Local directory to download file to
/// @return true: download has started, false: error, no download
/// Signals downloadComplete, commandError, commandProgress
bool burstDownload(const QString& from, const QString& toDir);
/// Lists the specified directory. Emits listEntry signal for each entry, followed by listComplete signal.
/// @param dirPath Fully qualified path to list
void listDirectory(const QString& dirPath);
/// Upload the specified file to the specified location
void upload(const QString& toPath, const QFileInfo& uploadFile);
/// Create a remote directory
void createDirectory(const QString& directory);
void mavlinkMessageReceived(mavlink_message_t message);
signals:
void downloadComplete (const QString& file, const QString& errorMsg);
void uploadComplete (const QString& errorMsg);
/// Signalled to indicate a new directory entry was received.
void listEntry(const QString& entry);
// Signals associated with all commands
/// Signalled after a command has completed
void commandComplete(void);
void commandError(const QString& msg);
/// Signalled during a lengthy command to show progress
/// @param value Amount of progress: 0.0 = none, 1.0 = complete
void commandProgress(int value);
private slots:
void _ackTimeout(void);
private:
bool _sendOpcodeOnlyCmd (MavlinkFTP::OpCode_t opcode, MavlinkFTP::OpCode_t newWaitState);
void _setupAckTimeout (void);
void _clearAckTimeout (void);
void _emitErrorMessage (const QString& msg);
void _emitListEntry (const QString& entry);
void _sendRequestExpectAck (MavlinkFTP::Request* request);
void _sendRequestNoAck (MavlinkFTP::Request* request);
void _sendMessageOnLink (LinkInterface* link, mavlink_message_t message);
void _fillRequestWithString (MavlinkFTP::Request* request, const QString& str);
void _handlOpenFileROAck (MavlinkFTP::Request* ack);
void _handleReadFileAck (MavlinkFTP::Request* ack, bool burstReadFile);
void _listAckResponse (MavlinkFTP::Request* listAck);
void _createAckResponse (MavlinkFTP::Request* createAck);
void _writeAckResponse (MavlinkFTP::Request* writeAck);
void _writeFileDatablock (void);
void _sendListCommand (void);
void _sendResetCommand (void);
void _downloadComplete (const QString& errorMsg);
void _uploadComplete (const QString& errorMsg);
bool _downloadWorker (const QString& from, const QString& toDir, bool burstReadFile);
void _requestMissingData (void);
MavlinkFTP::OpCode_t _waitState = MavlinkFTP::kCmdNone; ///< Current operation of state machine
MavlinkFTP::OpCode_t _openFileType = MavlinkFTP::kCmdReadFile; ///< Type of read to use after open file succeeds
QTimer _ackTimer; ///< Used to signal a timeout waiting for an ack
int _ackNumTries; ///< current number of tries
Vehicle* _vehicle;
LinkInterface* _dedicatedLink = nullptr; ///< Link to use for communication
MavlinkFTP::Request _lastOutgoingRequest; ///< contains the last outgoing packet
unsigned _listOffset; ///< offset for the current List operation
QString _listPath; ///< path for the current List operation
uint8_t _activeSession = 0; ///< currently active session, 0 for none
uint32_t _readOffset; ///< current read offset
uint32_t _writeOffset; ///< current write offset
uint32_t _writeSize; ///< current write data size
uint32_t _writeFileSize; ///< Size of file being uploaded
QByteArray _writeFileAccumulator; ///< Holds file being uploaded
struct MissingData {
uint32_t offset;
uint32_t size;
};
uint32_t _requestedDownloadOffset; ///< current download offset
QByteArray _readFileAccumulator; ///< Holds file being downloaded
QDir _readFileDownloadDir; ///< Directory to download file to
QString _readFileDownloadFilename; ///< Filename (no path) for download file
int _downloadFileSize; ///< Size of file being downloaded
static const int _ackTimerTimeoutMsecs = 5000;
static const int _ackTimerMaxRetries = 6;
};
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "FTPManagerTest.h"
#include "MultiVehicleManager.h"
#include "QGCApplication.h"
#include "MockLink.h"
#include "FTPManager.h"
const FTPManagerTest::TestCase_t FTPManagerTest::_rgTestCases[] = {
{ "/version.json" },
};
void FTPManagerTest::_testCaseWorker(const TestCase_t& testCase)
{
_connectMockLinkNoInitialConnectSequence();
MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager();
Vehicle* vehicle = vehicleMgr->activeVehicle();
FTPManager* ftpManager = vehicle->ftpManager();
QSignalSpy spyDownloadComplete(ftpManager, &FTPManager::downloadComplete);
// void downloadComplete (const QString& file, const QString& errorMsg);
ftpManager->download(testCase.file, QStandardPaths::writableLocation(QStandardPaths::TempLocation));
QCOMPARE(spyDownloadComplete.wait(10000), true);
QCOMPARE(spyDownloadComplete.count(), 1);
QList<QVariant> arguments = spyDownloadComplete.takeFirst();
qDebug() << arguments[0].toString();
QVERIFY(arguments[1].toString().isEmpty());
_disconnectMockLink();
}
void FTPManagerTest::_performTestCases(void)
{
int index = 0;
for (const TestCase_t& testCase: _rgTestCases) {
qDebug() << "Testing case" << index++;
_testCaseWorker(testCase);
}
}
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include "UnitTest.h"
class FTPManagerTest : public UnitTest
{
Q_OBJECT
private slots:
void _performTestCases(void);
private:
typedef struct {
const char* file;
} TestCase_t;
void _testCaseWorker(const TestCase_t& testCase);
static const TestCase_t _rgTestCases[];
};
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include "InitialConnectTest.h"
#include "MultiVehicleManager.h"
#include "QGCApplication.h"
#include "MockLink.h"
InitialConnectTest::TestCase_t InitialConnectTest::_rgTestCases[] = {
{ MockLink::FailRequestMessageNone, MAV_RESULT_ACCEPTED, Vehicle::RequestMessageNoFailure, false },
{ MockLink::FailRequestMessageCommandAcceptedMsgNotSent, MAV_RESULT_FAILED, Vehicle::RequestMessageFailureMessageNotReceived, false },
{ MockLink::FailRequestMessageCommandUnsupported, MAV_RESULT_UNSUPPORTED, Vehicle::RequestMessageFailureCommandError, false },
{ MockLink::FailRequestMessageCommandNoResponse, MAV_RESULT_FAILED, Vehicle::RequestMessageFailureCommandNotAcked, false },
//{ MockLink::FailRequestMessageCommandAcceptedSecondAttempMsgSent, MAV_RESULT_FAILED, Vehicle::RequestMessageNoFailure, false },
};
void InitialConnectTest::_requestMessageResultHandler(void* resultHandlerData, MAV_RESULT commandResult, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t& message)
{
TestCase_t* testCase = static_cast<TestCase_t*>(resultHandlerData);
testCase->resultHandlerCalled = true;
QCOMPARE((int)testCase->expectedCommandResult, (int)commandResult);
QCOMPARE((int)testCase->expectedFailureCode, (int)failureCode);
if (testCase->expectedFailureCode == Vehicle::RequestMessageNoFailure) {
QVERIFY(message.msgid == MAVLINK_MSG_ID_DEBUG);
}
}
void InitialConnectTest::_testCaseWorker(TestCase_t& testCase)
{
_connectMockLink();
MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager();
Vehicle* vehicle = vehicleMgr->activeVehicle();
_mockLink->setRequestMessageFailureMode(testCase.failureMode);
vehicle->requestMessage(_requestMessageResultHandler, &testCase, MAV_COMP_ID_ALL, MAVLINK_MSG_ID_DEBUG);
for (int i=0; i<100; i++) {
QTest::qWait(100);
if (testCase.resultHandlerCalled) {
break;
}
}
QVERIFY(testCase.resultHandlerCalled);
_disconnectMockLink();
}
void InitialConnectTest::_test(void)
{
_connectMockLink();
//MultiVehicleManager* vehicleMgr = qgcApp()->toolbox()->multiVehicleManager();
//Vehicle* vehicle = vehicleMgr->activeVehicle();
_disconnectMockLink();
}
void InitialConnectTest::_performTestCases(void)
{
int index = 0;
for (TestCase_t& testCase: _rgTestCases) {
qDebug() << "Testing case" << index++;
_testCaseWorker(testCase);
}
}
/****************************************************************************
*
* (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include "UnitTest.h"
#include "MockLink.h"
#include "Vehicle.h"
class InitialConnectTest : public UnitTest
{
Q_OBJECT
signals:
void resultHandlerCalled(void);
private slots:
void _test(void);
private:
void _performTestCases(void);
typedef struct {
MockLink::RequestMessageFailureMode_t failureMode;
MAV_RESULT expectedCommandResult;
Vehicle::RequestMessageResultHandlerFailureCode_t expectedFailureCode;
bool resultHandlerCalled;
} TestCase_t;
void _testCaseWorker(TestCase_t& testCase);
static void _requestMessageResultHandler(void* resultHandlerData, MAV_RESULT commandResult, Vehicle::RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t& message);
bool _resultHandlerCalled;
static TestCase_t _rgTestCases[];
};
This diff is collapsed.
This diff is collapsed.
......@@ -9,8 +9,12 @@
#pragma once
class StateMachine
#include <QObject>
class StateMachine : public QObject
{
Q_OBJECT
public:
typedef void (*StateFn)(StateMachine* stateMachine);
......
......@@ -45,7 +45,7 @@
#include "QGCGeo.h"
#include "TerrainProtocolHandler.h"
#include "ParameterManager.h"
#include "MAVLinkFTPManager.h"
#include "FTPManager.h"
#include "ComponentInformationManager.h"
#include "InitialConnectStateMachine.h"
......@@ -465,9 +465,9 @@ void Vehicle::_commonInit()
_parameterManager = new ParameterManager(this);
connect(_parameterManager, &ParameterManager::parametersReadyChanged, this, &Vehicle::_parametersReady);
_componentInformationManager = new ComponentInformationManager(this);
_initialConnectStateMachine = new InitialConnectStateMachine(this);
_mavlinkFTPManager = new MAVLinkFTPManager(this);
_componentInformationManager = new ComponentInformationManager (this);
_initialConnectStateMachine = new InitialConnectStateMachine (this);
_ftpManager = new FTPManager (this);
_objectAvoidance = new VehicleObjectAvoidance(this, this);
......@@ -715,6 +715,7 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes
if (!_terrainProtocolHandler->mavlinkMessageReceived(message)) {
return;
}
_ftpManager->mavlinkMessageReceived(message);
_waitForMavlinkMessageMessageReceived(message);
......
......@@ -44,7 +44,7 @@ class VehicleObjectAvoidance;
class TrajectoryPoints;
class TerrainProtocolHandler;
class ComponentInformationManager;
class MAVLinkFTPManager;
class FTPManager;
class InitialConnectStateMachine;
#if defined(QGC_AIRMAP_ENABLED)
......@@ -1009,7 +1009,7 @@ public:
ParameterManager* parameterManager () { return _parameterManager; }
ParameterManager* parameterManager () const { return _parameterManager; }
MAVLinkFTPManager* mavlinkFTPManager () { return _mavlinkFTPManager; }
FTPManager* ftpManager () { return _ftpManager; }
VehicleObjectAvoidance* objectAvoidance () { return _objectAvoidance; }
static const int cMaxRcChannels = 18;
......@@ -1460,7 +1460,7 @@ private:
RallyPointManager* _rallyPointManager;
ParameterManager* _parameterManager = nullptr;
MAVLinkFTPManager* _mavlinkFTPManager = nullptr;
FTPManager* _ftpManager = nullptr;
ComponentInformationManager* _componentInformationManager = nullptr;
InitialConnectStateMachine* _initialConnectStateMachine = nullptr;
VehicleObjectAvoidance* _objectAvoidance = nullptr;
......
......@@ -96,7 +96,7 @@ MockLink::MockLink(SharedLinkConfigurationPointer& config)
px4_cm.main_mode = PX4_CUSTOM_MAIN_MODE_MANUAL;
_mavCustomMode = px4_cm.data;
_fileServer = new MockLinkFileServer(_vehicleSystemId, _vehicleComponentId, this);
_fileServer = new MockLinkFTP(_vehicleSystemId, _vehicleComponentId, this);
Q_CHECK_PTR(_fileServer);
moveToThread(this);
......@@ -1532,7 +1532,7 @@ bool MockLink::_handleRequestMessage(const mavlink_command_long_t& request, bool
void MockLink::_sendVersionMetaData(void)
{
mavlink_message_t responseMsg;
char metaDataURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_METADATA_URI_LEN] = "mavlinkftp://version.json";
char metaDataURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_METADATA_URI_LEN] = "mavlinkftp://version.json";
char translationURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_TRANSLATION_URI_LEN] = "";
mavlink_msg_component_information_pack_chan(_vehicleSystemId,
......@@ -1551,7 +1551,7 @@ void MockLink::_sendVersionMetaData(void)
void MockLink::_sendParameterMetaData(void)
{
mavlink_message_t responseMsg;
char metaDataURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_METADATA_URI_LEN] = "mavlinkftp://parameter.json";
char metaDataURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_METADATA_URI_LEN] = "mavlinkftp://parameter.json";
char translationURI[MAVLINK_MSG_COMPONENT_INFORMATION_FIELD_TRANSLATION_URI_LEN] = "";
mavlink_msg_component_information_pack_chan(_vehicleSystemId,
......
......@@ -16,7 +16,7 @@
#include <QGeoCoordinate>
#include "MockLinkMissionItemHandler.h"
#include "MockLinkFileServer.h"
#include "MockLinkFTP.h"
#include "LinkManager.h"
#include "QGCMAVLink.h"
......@@ -119,7 +119,7 @@ public:
/// Sends the specified mavlink message to QGC
void respondWithMavlinkMessage(const mavlink_message_t& msg);
MockLinkFileServer* getFileServer(void) { return _fileServer; }
MockLinkFTP* getFileServer(void) { return _fileServer; }
// Overrides from LinkInterface
QString getName (void) const override { return _name; }
......@@ -266,7 +266,7 @@ private:
double _vehicleLongitude;
double _vehicleAltitude;
MockLinkFileServer* _fileServer;
MockLinkFTP* _fileServer;
bool _sendStatusText;
bool _apmSendHomePositionOnEmptyList;
......
......@@ -8,24 +8,22 @@
****************************************************************************/
/// @file
/// @author Don Gagne <don@thegagnes.com>
#pragma once
#include "FileManager.h"
#include "QGCMAVLink.h"
#include <QStringList>
#include <QFile>
class MockLink;
/// Mock implementation of Mavlink FTP server.
class MockLinkFileServer : public QObject
class MockLinkFTP : public QObject
{
Q_OBJECT
public:
MockLinkFileServer(uint8_t systemIdServer, uint8_t componentIdServer, MockLink* mockLink);
MockLinkFTP(uint8_t systemIdServer, uint8_t componentIdServer, MockLink* mockLink);
/// @brief Sets the list of files returned by the List command. Prepend names with F or D
/// to indicate (F)ile or (D)irectory.
......@@ -54,7 +52,7 @@ public:
/// Called to handle an FTP message
void handleFTPMessage(const mavlink_message_t& message);
/// @brief Used to represent a single test case for download testing.
struct FileTestCase {
const char* filename; ///< Filename to download
......@@ -79,33 +77,34 @@ signals:
void resetCommandReceived(void);
private:
void _sendAck(uint8_t targetSystemId, uint8_t targetComponentId, uint16_t seqNumber, FileManager::Opcode reqOpcode);
void _sendNak(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::ErrorCode error, uint16_t seqNumber, FileManager::Opcode reqOpcode);
void _sendResponse(uint8_t targetSystemId, uint8_t targetComponentId, FileManager::Request* request, uint16_t seqNumber);
void _listCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _openCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _readCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _streamCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _terminateCommand(uint8_t senderSystemId, uint8_t senderComponentId, FileManager::Request* request, uint16_t seqNumber);
void _resetCommand(uint8_t senderSystemId, uint8_t senderComponentId, uint16_t seqNumber);
uint16_t _nextSeqNumber(uint16_t seqNumber);
void _sendAck (uint8_t targetSystemId, uint8_t targetComponentId, uint16_t seqNumber, MavlinkFTP::OpCode_t reqOpCode);
void _sendNak (uint8_t targetSystemId, uint8_t targetComponentId, MavlinkFTP::ErrorCode_t error, uint16_t seqNumber, MavlinkFTP::OpCode_t reqOpCode);
void _sendNakErrno (uint8_t targetSystemId, uint8_t targetComponentId, uint8_t nakErrno, uint16_t seqNumber, MavlinkFTP::OpCode_t reqOpCode);
void _sendResponse (uint8_t targetSystemId, uint8_t targetComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _listCommand (uint8_t senderSystemId, uint8_t senderComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _openCommand (uint8_t senderSystemId, uint8_t senderComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _readCommand (uint8_t senderSystemId, uint8_t senderComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _burstReadCommand (uint8_t senderSystemId, uint8_t senderComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _terminateCommand (uint8_t senderSystemId, uint8_t senderComponentId, MavlinkFTP::Request* request, uint16_t seqNumber);
void _resetCommand (uint8_t senderSystemId, uint8_t senderComponentId, uint16_t seqNumber);
uint16_t _nextSeqNumber (uint16_t seqNumber);
QString _createTestCaseTempFile (const FileTestCase& testCase);
/// if request is a string, this ensures it's null-terminated
static void ensureNullTemination(FileManager::Request* request);
static void ensureNullTemination(MavlinkFTP::Request* request);
QStringList _fileList; ///< List of files returned by List command
static const uint8_t _sessionId;
uint8_t _readFileLength; ///< Length of active file being read
ErrorMode_t _errMode; ///< Currently set error mode, as specified by setErrorMode
const uint8_t _systemIdServer; ///< System ID for server
const uint8_t _componentIdServer; ///< Component ID for server
MockLink* _mockLink; ///< MockLink to communicate through
bool _lastReplyValid;
uint16_t _lastReplySequence;
mavlink_message_t _lastReply;
QFile _currentFile;
ErrorMode_t _errMode = errModeNone; ///< Currently set error mode, as specified by setErrorMode
const uint8_t _systemIdServer; ///< System ID for server
const uint8_t _componentIdServer; ///< Component ID for server
MockLink* _mockLink; ///< MockLink to communicate through
bool _lastReplyValid = false;
uint16_t _lastReplySequence = 0;
mavlink_message_t _lastReply;
bool _randomDropsEnabled = false;
bool _randomDropsEnabled;
static const uint8_t _sessionId = 1; ///< We only support a single fixed session
};
......@@ -80,3 +80,77 @@ QString QGCMAVLink::mavResultToString(MAV_RESULT result)
return QStringLiteral("MAV_RESULT unknown %1").arg(result);
}
}
QString MavlinkFTP::opCodeToString(OpCode_t opCode)
{
switch (opCode) {
case kCmdNone:
return "None";
case kCmdTerminateSession:
return "Terminate Session";
case kCmdResetSessions:
return "Reset Sessions";
case kCmdListDirectory:
return "List Directory";
case kCmdOpenFileRO:
return "Open File RO";
case kCmdReadFile:
return "Read File";
case kCmdCreateFile:
return "Create File";
case kCmdWriteFile:
return "Write File";
case kCmdRemoveFile:
return "Remove File";
case kCmdCreateDirectory:
return "Create Directory";
case kCmdRemoveDirectory:
return "Remove Directory";
case kCmdOpenFileWO:
return "Open File WO";
case kCmdTruncateFile:
return "Truncate File";
case kCmdRename:
return "Rename";
case kCmdCalcFileCRC32:
return "Calc File CRC32";
case kCmdBurstReadFile:
return "Burst Read File";
case kRspAck:
return "Ack";
case kRspNak:
return "Nak";
}
return "Unknown OpCode";
}
QString MavlinkFTP::errorCodeToString(ErrorCode_t errorCode)
{
switch (errorCode) {
case kErrNone:
return "None";
case kErrFail:
return "Fail";
case kErrFailErrno:
return "Fail Errorno";
case kErrInvalidDataSize:
return "Invalid Data Size";
case kErrInvalidSession:
return "Invalid Session";
case kErrNoSessionsAvailable:
return "No Sessions Available";
case kErrEOF:
return "EOF";
case kErrUnknownCommand:
return "Unknown Command";
case kErrFailFileExists:
return "File Already Exists";
case kErrFailFileProtected:
return "File Protected";
case kErrFailFileNotFound:
return "File Not Found";
}
return "Unknown Error";
}
......@@ -40,6 +40,12 @@ extern mavlink_status_t m_mavlink_status[MAVLINK_COMM_NUM_BUFFERS];
#pragma warning(pop, 0)
#endif
#ifdef __GNUC__
#define PACKED_STRUCT( __Declaration__ ) __Declaration__ __attribute__((packed))
#else
#define PACKED_STRUCT( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
#endif
class QGCMAVLink {
public:
static bool isFixedWing (MAV_TYPE mavType);
......@@ -50,4 +56,78 @@ public:
static QString mavResultToString (MAV_RESULT result);
};
class MavlinkFTP {
public:
/// This is the fixed length portion of the protocol data.
/// This needs to be packed, because it's typecasted from mavlink_file_transfer_protocol_t.payload, which starts
/// at a 3 byte offset, causing an unaligned access to seq_number and offset
PACKED_STRUCT(
typedef struct RequestHeader {
uint16_t seqNumber; ///< sequence number for message
uint8_t session; ///< Session id for read and write commands
uint8_t opcode; ///< Command opcode
uint8_t size; ///< Size of data
uint8_t req_opcode; ///< Request opcode returned in kRspAck, kRspNak message
uint8_t burstComplete; ///< Only used if req_opcode=kCmdBurstReadFile - 1: set of burst packets complete, 0: More burst packets coming.
uint8_t paddng; ///< 32 bit aligment padding
uint32_t offset; ///< Offsets for List and Read commands
}) RequestHeader;
PACKED_STRUCT(
typedef struct Request{
RequestHeader hdr;
// We use a union here instead of just casting (uint32_t)&payload[0] to not break strict aliasing rules
union {
// The entire Request must fit into the payload member of the mavlink_file_transfer_protocol_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_file_transfer_protocol_t*)0)->payload) - sizeof(RequestHeader)];
// File length returned by Open command
uint32_t openFileLength;
// Length of file chunk written by write command
uint32_t writeFileLength;
};
}) Request;
typedef enum {
kCmdNone = 0, ///< ignored, always acked
kCmdTerminateSession, ///< Terminates open Read session
kCmdResetSessions, ///< Terminates all open Read sessions
kCmdListDirectory, ///< List files in <path> from <offset>
kCmdOpenFileRO, ///< Opens file at <path> for reading, returns <session>
kCmdReadFile, ///< Reads <size> bytes from <offset> in <session>
kCmdCreateFile, ///< Creates file at <path> for writing, returns <session>
kCmdWriteFile, ///< Writes <size> bytes to <offset> in <session>
kCmdRemoveFile, ///< Remove file at <path>
kCmdCreateDirectory, ///< Creates directory at <path>
kCmdRemoveDirectory, ///< Removes Directory at <path>, must be empty
kCmdOpenFileWO, ///< Opens file at <path> for writing, returns <session>
kCmdTruncateFile, ///< Truncate file at <path> to <offset> length
kCmdRename, ///< Rename <path1> to <path2>
kCmdCalcFileCRC32, ///< Calculate CRC32 for file at <path>
kCmdBurstReadFile, ///< Burst download session file
kRspAck = 128, ///< Ack response
kRspNak, ///< Nak response
} OpCode_t;
/// @brief Error codes returned in Nak response PayloadHeader.data[0].
typedef enum {
kErrNone = 0,
kErrFail, ///< Unknown failure
kErrFailErrno, ///< errno sent back in PayloadHeader.data[1]
kErrInvalidDataSize, ///< PayloadHeader.size is invalid
kErrInvalidSession, ///< Session is not currently open
kErrNoSessionsAvailable, ///< All available Sessions in use
kErrEOF, ///< Offset past end of file for List and Read commands
kErrUnknownCommand, ///< Unknown command opcode
kErrFailFileExists, ///< File exists already
kErrFailFileProtected, ///< File is write protected
kErrFailFileNotFound
} ErrorCode_t;
static QString opCodeToString (OpCode_t opCode);
static QString errorCodeToString(ErrorCode_t errorCode);
};
......@@ -46,6 +46,8 @@
#include "CameraCalcTest.h"
#include "FWLandingPatternTest.h"
#include "RequestMessageTest.h"
#include "InitialConnectTest.h"
#include "FTPManagerTest.h"
UT_REGISTER_TEST(FactSystemTestGeneric)
UT_REGISTER_TEST(FactSystemTestPX4)
......@@ -56,6 +58,8 @@ UT_REGISTER_TEST(LinkManagerTest)
UT_REGISTER_TEST(SendMavCommandWithSignallingTest)
UT_REGISTER_TEST(SendMavCommandWithHandlerTest)
UT_REGISTER_TEST(RequestMessageTest)
UT_REGISTER_TEST(FTPManagerTest)
UT_REGISTER_TEST(InitialConnectTest)
UT_REGISTER_TEST(MissionItemTest)
UT_REGISTER_TEST(SimpleMissionItemTest)
UT_REGISTER_TEST(MissionControllerTest)
......
This diff is collapsed.
......@@ -60,10 +60,6 @@ UAS::UAS(MAVLinkProtocol* protocol, Vehicle* vehicle, FirmwarePluginManager * fi
controlYawManual(true),
controlThrustManual(true),
#ifndef __mobile__
fileManager(this, vehicle),
#endif
attitudeKnown(false),
attitudeStamped(false),
lastAttitude(0),
......@@ -113,10 +109,6 @@ UAS::UAS(MAVLinkProtocol* protocol, Vehicle* vehicle, FirmwarePluginManager * fi
_firmwarePluginManager(firmwarePluginManager)
{
#ifndef __mobile__
connect(_vehicle, &Vehicle::mavlinkMessageReceived, &fileManager, &FileManager::receiveMessage);
#endif
}
/**
......
......@@ -20,10 +20,6 @@
#include "Vehicle.h"
#include "FirmwarePluginManager.h"
#ifndef __mobile__
#include "FileManager.h"
#endif
Q_DECLARE_LOGGING_CATEGORY(UASLog)
class Vehicle;
......@@ -109,10 +105,6 @@ public:
temperature_var = var;
}
#ifndef __mobile__
friend class FileManager;
#endif
protected:
/// LINK ID AND STATUS
int uasId; ///< Unique system ID
......@@ -138,10 +130,6 @@ protected:
/// POSITION
bool isGlobalPositionKnown; ///< If the global position has been received for this MAV
#ifndef __mobile__
FileManager fileManager;
#endif
/// ATTITUDE
bool attitudeKnown; ///< True if attitude was received, false else
bool attitudeStamped; ///< Should arriving data be timestamped with the last attitude? This helps with broken system time clocks on the MAV
......@@ -182,10 +170,6 @@ public:
/** @brief Get the human-readable status message for this code */
void getStatusForCode(int statusCode, QString& uasState, QString& stateDescription);
#ifndef __mobile__
virtual FileManager* getFileManager() { return &fileManager; }
#endif
QImage getImage();
void requestImage();
......
......@@ -21,10 +21,6 @@
#include "LinkInterface.h"
#ifndef __mobile__
class FileManager;
#endif
/**
* @brief Interface for all robots.
*
......@@ -43,10 +39,6 @@ public:
/** @brief The time interval the robot is switched on **/
virtual quint64 getUptime() const = 0;
#ifndef __mobile__
virtual FileManager* getFileManager() = 0;
#endif
enum StartCalibrationType {
StartCalibrationRadio,
StartCalibrationGyro,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment