FileManager.h 10.9 KB
Newer Older
1 2
/****************************************************************************
 *
Gus Grubba's avatar
Gus Grubba committed
3
 * (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
4 5 6 7 8 9
 *
 * QGroundControl is licensed according to the terms in the file
 * COPYING.md in the root of the source code directory.
 *
 ****************************************************************************/

10

11
#pragma once
12 13

#include <QObject>
14
#include <QDir>
15
#include <QTimer>
16
#include <QQueue>
17

18
#include "UASInterface.h"
19
#include "QGCLoggingCategory.h"
20

21 22 23 24 25 26 27
#ifdef __GNUC__
  #define PACKED_STRUCT( __Declaration__ ) __Declaration__ __attribute__((packed))
#else
  #define PACKED_STRUCT( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop) )
#endif


28 29
Q_DECLARE_LOGGING_CATEGORY(FileManagerLog)

30 31
class Vehicle;

32
class FileManager : public QObject
33 34
{
    Q_OBJECT
Don Gagne's avatar
Don Gagne committed
35
    
36
public:
37
    FileManager(QObject* parent, Vehicle* vehicle);
Gus Grubba's avatar
Gus Grubba committed
38
    FileManager(QObject* parent, LinkInterface *link, uint8_t systemIdQGC, uint8_t systemIdServer);
39 40 41 42
    
    /// These methods are only used for testing purposes.
    bool _sendCmdTestAck(void) { return _sendOpcodeOnlyCmd(kCmdNone, kCOAck); };
    bool _sendCmdTestNoAck(void) { return _sendOpcodeOnlyCmd(kCmdTestNoAck, kCOAck); };
43
    
44
    /// Timeout in msecs to wait for an Ack time come back. This is public so we can write unit tests which wait long enough
45
    /// for the FileManager to timeout.
Gus Grubba's avatar
Gus Grubba committed
46
    static const int ackTimerTimeoutMsecs = 5000;
47 48

    static const int ackTimerMaxRetries = 6;
49

Gus Grubba's avatar
Gus Grubba committed
50 51 52 53 54
    int _ackTimerTimeoutMsecs = ackTimerTimeoutMsecs;

    int _ackTimerMaxRetries = ackTimerMaxRetries;


55 56 57 58 59 60 61 62 63 64 65 66 67 68
	/// Downloads the specified file.
	///     @param from File to download from UAS, fully qualified path
	///     @param downloadDir Local directory to download file to
	void downloadPath(const QString& from, const QDir& downloadDir);
	
	/// Stream downloads the specified file.
	///     @param from File to download from UAS, fully qualified path
	///     @param downloadDir Local directory to download file to
	void streamPath(const QString& from, const QDir& downloadDir);
	
	/// 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);
	
69 70 71
    /// Upload the specified file to the specified location
    void uploadPath(const QString& toPath, const QFileInfo& uploadFile);
    
72 73 74
    /// Create a remote directory
    void createDirectory(const QString& directory);

75
signals:
76 77
    // Signals associated with the listDirectory method
    
Don Gagne's avatar
Don Gagne committed
78
    /// Signalled to indicate a new directory entry was received.
79 80
    void listEntry(const QString& entry);
    
Don Gagne's avatar
Don Gagne committed
81
    // Signals associated with all commands
82
    
Don Gagne's avatar
Don Gagne committed
83 84
    /// Signalled after a command has completed
    void commandComplete(void);
85
    
Don Gagne's avatar
Don Gagne committed
86 87 88
    /// Signalled when an error occurs during a command. In this case a commandComplete signal will
    /// not be sent.
    void commandError(const QString& msg);
89
    
Don Gagne's avatar
Don Gagne committed
90 91 92
    /// Signalled during a lengthy command to show progress
    ///     @param value Amount of progress: 0.0 = none, 1.0 = complete
    void commandProgress(int value);
93

Gus Grubba's avatar
Gus Grubba committed
94 95 96
    /// Used internally to move sendMessage call to main thread
    void _sendMessageOnLinkOnThread(LinkInterface* link, mavlink_message_t message);

97
public slots:
98
    void receiveMessage(mavlink_message_t message);
99 100 101
	
private slots:
	void _ackTimeout(void);
102

103
private:
104 105 106 107 108
    /// @brief 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
Lorenz Meier's avatar
Lorenz Meier committed
109
        {
110 111 112 113 114 115 116 117
            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     padding;        ///< 32 bit aligment padding
            uint32_t    offset;         ///< Offsets for List and Read commands
118
        }) RequestHeader;
Lorenz Meier's avatar
Lorenz Meier committed
119

120 121
    PACKED_STRUCT(
    typedef struct _Request
122
    {
123
        RequestHeader hdr;
124

125
        // We use a union here instead of just casting (uint32_t)&payload[0] to not break strict aliasing rules
126
        union {
127
            // The entire Request must fit into the payload member of the mavlink_file_transfer_protocol_t structure. We use as many leftover bytes
128
            // after we use up space for the RequestHeader for the data portion of the Request.
129
            uint8_t data[sizeof(((mavlink_file_transfer_protocol_t*)0)->payload) - sizeof(RequestHeader)];
130
			
131 132
            // File length returned by Open command
            uint32_t openFileLength;
133 134 135

            // Length of file chunk written by write command
            uint32_t writeFileLength;
136
        };
137
    }) Request;
138

none's avatar
none committed
139
    enum Opcode
140
	{
141
		kCmdNone,				///< ignored, always acked
142
		kCmdTerminateSession,	///< Terminates open Read session
143 144 145 146 147 148 149
		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>
150 151
		kCmdCreateDirectory,	///< Creates directory at <path>
		kCmdRemoveDirectory,	///< Removes Directory at <path>, must be empty
152 153 154 155 156
		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
157
		
158
		kRspAck = 128,          ///< Ack response
159
		kRspNak,                ///< Nak response
none's avatar
none committed
160

161 162 163 164 165 166 167 168 169 170 171 172 173 174
        // Used for testing only, not part of protocol
        kCmdTestNoAck,          ///< ignored, ack not sent back, should timeout waiting for ack
	};
	
	/// @brief Error codes returned in Nak response PayloadHeader.data[0].
	enum ErrorCode
    {
		kErrNone,
		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
175 176 177
        kErrUnknownCommand,          ///< Unknown command opcode
        kErrFailFileExists,         ///< File exists already
        kErrFailFileProtected       ///< File is write protected
178
    };
none's avatar
none committed
179 180 181

    enum OperationState
        {
182 183 184 185
            kCOIdle,		// not doing anything
            kCOAck,			// waiting for an Ack
            kCOList,		// waiting for List response
            kCOOpenRead,    // waiting for Open response followed by Read download
Don Gagne's avatar
Don Gagne committed
186
			kCOOpenBurst,   // waiting for Open response, followed by Burst download
187 188 189
            kCORead,		// waiting for Read response
			kCOBurst,		// waiting for Burst response
            kCOWrite,       // waiting for Write response
190
            kCOCreate,      // waiting for Create response
191
            kCOCreateDir,   // waiting for Create Directory response
none's avatar
none committed
192
        };
193 194 195 196 197
    
    bool _sendOpcodeOnlyCmd(uint8_t opcode, OperationState newOpState);
    void _setupAckTimeout(void);
    void _clearAckTimeout(void);
    void _emitErrorMessage(const QString& msg);
198
    void _emitListEntry(const QString& entry);
199
    void _sendRequest(Request* request);
200
    void _sendRequestNoAck(Request* request);
Gus Grubba's avatar
Gus Grubba committed
201
    void _sendMessageOnLink(LinkInterface* link, mavlink_message_t message);
202
    void _fillRequestWithString(Request* request, const QString& str);
203
    void _openAckResponse(Request* openAck);
204
    void _downloadAckResponse(Request* readAck, bool readFile);
205
    void _listAckResponse(Request* listAck);
206 207 208
    void _createAckResponse(Request* createAck);
    void _writeAckResponse(Request* writeAck);
    void _writeFileDatablock(void);
209
    void _sendListCommand(void);
Don Gagne's avatar
Don Gagne committed
210
    void _sendResetCommand(void);
211
    void _closeDownloadSession(bool success);
Don Gagne's avatar
Don Gagne committed
212
    void _closeUploadSession(bool success);
213 214
    void _downloadWorker(const QString& from, const QDir& downloadDir, bool readFile);
    void _requestMissingData();
215
    
none's avatar
none committed
216
    static QString errorString(uint8_t errorCode);
217

218 219
    OperationState  _currentOperation;              ///< Current operation of state machine
    QTimer          _ackTimer;                      ///< Used to signal a timeout waiting for an ack
220
    int             _ackNumTries;                   ///< current number of tries
221
    
222 223
    Vehicle*        _vehicle;
    LinkInterface*  _dedicatedLink; ///< Link to use for communication
224
    
225
    Request  _lastOutgoingRequest; ///< contains the last outgoing packet
226

227 228 229 230
    unsigned    _listOffset;    ///< offset for the current List operation
    QString     _listPath;      ///< path for the current List operation
    
    uint8_t     _activeSession;             ///< currently active session, 0 for none
Don Gagne's avatar
Don Gagne committed
231
    
232
    uint32_t    _readOffset;                ///< current read offset
Don Gagne's avatar
Don Gagne committed
233
    
234 235
    uint32_t    _writeOffset;               ///< current write offset
    uint32_t    _writeSize;                 ///< current write data size
Don Gagne's avatar
Don Gagne committed
236 237 238
    uint32_t    _writeFileSize;             ///< Size of file being uploaded
    QByteArray  _writeFileAccumulator;      ///< Holds file being uploaded
    
239 240 241 242
    struct MissingData {
        uint32_t offset;
        uint32_t size;
    };
243
    uint32_t    _downloadOffset;            ///< current download offset
244 245 246
    uint32_t    _missingDownloadedBytes;    ///< number of missing bytes for burst download
    QQueue<MissingData> _missingData;       ///< missing chunks of downloaded file (for burst downloads)
    bool        _downloadingMissingParts;   ///< true if we are currently downloading missing parts
247 248 249
    QByteArray  _readFileAccumulator;       ///< Holds file being downloaded
    QDir        _readFileDownloadDir;       ///< Directory to download file to
    QString     _readFileDownloadFilename;  ///< Filename (no path) for download file
Don Gagne's avatar
Don Gagne committed
250
    uint32_t    _downloadFileSize;          ///< Size of file being downloaded
251

252 253
    uint8_t     _systemIdQGC;               ///< System ID for QGC
    uint8_t     _systemIdServer;            ///< System ID for server
254
    
Don Gagne's avatar
Don Gagne committed
255
    // We give MockLinkFileServer friend access so that it can use the data structures and opcodes
256
    // to build a mock mavlink file server for testing.
Don Gagne's avatar
Don Gagne committed
257
    friend class MockLinkFileServer;
258 259
};