QGCUASFileManager.cc 20.2 KB
Newer Older
1
/*=====================================================================
2

3
 QGroundControl Open Source Ground Control Station
4

5
 (c) 2009 - 2014 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
6

7
 This file is part of the QGROUNDCONTROL project
8

9 10 11 12
 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.
13

14 15 16 17
 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.
18

19 20
 You should have received a copy of the GNU General Public License
 along with QGROUNDCONTROL. If not, see <http://www.gnu.org/licenses/>.
21

22 23
 ======================================================================*/

24 25
#include "QGCUASFileManager.h"
#include "QGC.h"
Lorenz Meier's avatar
Lorenz Meier committed
26
#include "MAVLinkProtocol.h"
27
#include "MainWindow.h"
Lorenz Meier's avatar
Lorenz Meier committed
28 29 30

#include <QFile>
#include <QDir>
none's avatar
none committed
31
#include <string>
Lorenz Meier's avatar
Lorenz Meier committed
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67

static const quint32 crctab[] =
{
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
68

69

70
QGCUASFileManager::QGCUASFileManager(QObject* parent, UASInterface* uas, uint8_t unitTestSystemIdQGC) :
71
    QObject(parent),
72
    _currentOperation(kCOIdle),
Lorenz Meier's avatar
Lorenz Meier committed
73
    _mav(uas),
74
    _lastOutgoingSeqNumber(0),
75 76
    _activeSession(0),
    _systemIdQGC(unitTestSystemIdQGC)
Lorenz Meier's avatar
Lorenz Meier committed
77
{
78 79 80
    connect(&_ackTimer, &QTimer::timeout, this, &QGCUASFileManager::_ackTimeout);
    
    _systemIdServer = _mav->getUASID();
81 82
    
    // Make sure we don't have bad structure packing
Don Gagne's avatar
Don Gagne committed
83
    Q_ASSERT(sizeof(RequestHeader) == 16);
Lorenz Meier's avatar
Lorenz Meier committed
84 85
}

86 87 88 89 90
/// @brief Calculates a 32 bit CRC for the specified request.
///     @param request Request to calculate CRC for. request->size must be set correctly.
///     @param state previous crc state
/// @return Calculated CRC
quint32 QGCUASFileManager::crc32(Request* request, unsigned state)
91
{
92 93
    uint8_t* data = (uint8_t*)request;
    size_t cbData = sizeof(RequestHeader) + request->hdr.size;
94

95 96 97
    // Always calculate CRC with 0 initial CRC value
    quint32 crcSave = request->hdr.crc32;
    request->hdr.crc32 = 0;
Don Gagne's avatar
Don Gagne committed
98 99 100
    request->hdr.padding[0] = 0;
    request->hdr.padding[1] = 0;
    request->hdr.padding[2] = 0;
101

102 103
    for (size_t i=0; i < cbData; i++)
        state = crctab[(state ^ data[i]) & 0xff] ^ (state >> 8);
104

105 106
    request->hdr.crc32 = crcSave;

Lorenz Meier's avatar
Lorenz Meier committed
107
    return state;
108 109
}

110 111
/// @brief Respond to the Ack associated with the Open command with the next Read command.
void QGCUASFileManager::_openAckResponse(Request* openAck)
112 113 114
{
    _currentOperation = kCORead;
    _activeSession = openAck->hdr.session;
115 116 117
    
    // File length comes back in data
    Q_ASSERT(openAck->hdr.size == sizeof(uint32_t));
118 119 120
    emit downloadFileLength(openAck->openFileLength);
    
    // Start the sequence of read commands
121

122 123
    _readOffset = 0;                // Start reading at beginning of file
    _readFileAccumulator.clear();   // Start with an empty file
124

125 126 127 128 129
    Request request;
    request.hdr.session = _activeSession;
    request.hdr.opcode = kCmdRead;
    request.hdr.offset = _readOffset;
    request.hdr.size = sizeof(request.data);
130

131 132 133
    _sendRequest(&request);
}

134 135 136 137 138 139
/// @brief Closes out a read session by writing the file and doing cleanup.
///     @param success true: successful download completion, false: error during download
void QGCUASFileManager::_closeReadSession(bool success)
{
    if (success) {
        QString downloadFilePath = _readFileDownloadDir.absoluteFilePath(_readFileDownloadFilename);
140

141 142 143 144 145
        QFile file(downloadFilePath);
        if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
            _emitErrorMessage(tr("Unable to open local file for writing (%1)").arg(downloadFilePath));
            return;
        }
146

147 148 149 150 151 152 153
        qint64 bytesWritten = file.write((const char *)_readFileAccumulator, _readFileAccumulator.length());
        if (bytesWritten != _readFileAccumulator.length()) {
            file.close();
            _emitErrorMessage(tr("Unable to write data to local file (%1)").arg(downloadFilePath));
            return;
        }
        file.close();
154

155
        emit downloadFileComplete();
156
    }
157

158 159 160 161
    // Close the open session
    _sendTerminateCommand();
}

162
/// @brief Respond to the Ack associated with the Read command.
163
void QGCUASFileManager::_readAckResponse(Request* readAck)
164 165 166 167 168 169 170
{
    if (readAck->hdr.session != _activeSession) {
        _currentOperation = kCOIdle;
        _readFileAccumulator.clear();
        _emitErrorMessage(tr("Read: Incorrect session returned"));
        return;
    }
171

172 173 174 175 176 177 178 179
    if (readAck->hdr.offset != _readOffset) {
        _currentOperation = kCOIdle;
        _readFileAccumulator.clear();
        _emitErrorMessage(tr("Read: Offset returned (%1) differs from offset requested (%2)").arg(readAck->hdr.offset).arg(_readOffset));
        return;
    }

    _readFileAccumulator.append((const char*)readAck->data, readAck->hdr.size);
180
    emit downloadFileProgress(_readFileAccumulator.length());
181

182 183
    if (readAck->hdr.size == sizeof(readAck->data)) {
        // Possibly still more data to read, send next read request
184

185
        _currentOperation = kCORead;
186

187
        _readOffset += readAck->hdr.size;
188

189 190 191 192
        Request request;
        request.hdr.session = _activeSession;
        request.hdr.opcode = kCmdRead;
        request.hdr.offset = _readOffset;
193
        request.hdr.size = 0;
194

195 196
        _sendRequest(&request);
    } else {
197
        // We only receieved a partial buffer back. These means we are at EOF
198
        _currentOperation = kCOIdle;
199
        _closeReadSession(true /* success */);
200 201 202 203 204 205 206 207 208 209 210
    }
}

/// @brief Respond to the Ack associated with the List command.
void QGCUASFileManager::_listAckResponse(Request* listAck)
{
    if (listAck->hdr.offset != _listOffset) {
        _currentOperation = kCOIdle;
        _emitErrorMessage(tr("List: Offset returned (%1) differs from offset requested (%2)").arg(listAck->hdr.offset).arg(_listOffset));
        return;
    }
211

212 213 214
    uint8_t offset = 0;
    uint8_t cListEntries = 0;
    uint8_t cBytes = listAck->hdr.size;
215

216 217 218
    // parse filenames out of the buffer
    while (offset < cBytes) {
        const char * ptr = ((const char *)listAck->data) + offset;
219

220 221 222 223 224 225 226 227 228 229 230 231
        // get the length of the name
        uint8_t cBytesLeft = cBytes - offset;
        size_t nlen = strnlen(ptr, cBytesLeft);
        if (nlen < 2) {
            _currentOperation = kCOIdle;
            _emitErrorMessage(tr("Incorrectly formed list entry: '%1'").arg(ptr));
            return;
        } else if (nlen == cBytesLeft) {
            _currentOperation = kCOIdle;
            _emitErrorMessage(tr("Missing NULL termination in list entry"));
            return;
        }
232

Don Gagne's avatar
Don Gagne committed
233
        // Returned names are prepended with D for directory, F for file, U for unknown
234 235
        if (*ptr == 'F' || *ptr == 'D') {
            // put it in the view
236 237 238
            _emitListEntry(ptr);
        } else {
            qDebug() << "unknown entry" << ptr;
239
        }
240

241 242
        // account for the name + NUL
        offset += nlen + 1;
243

244 245 246
        cListEntries++;
    }

247 248 249 250
    if (listAck->hdr.size == 0) {
        // Directory is empty, we're done
        Q_ASSERT(listAck->hdr.opcode == kRspAck);
        _currentOperation = kCOIdle;
Don Gagne's avatar
Don Gagne committed
251
        emit listComplete();
252 253
    } else {
        // Possibly more entries to come, need to keep trying till we get EOF
254 255 256
        _currentOperation = kCOList;
        _listOffset += cListEntries;
        _sendListCommand();
257 258 259
    }
}

Lorenz Meier's avatar
Lorenz Meier committed
260 261
void QGCUASFileManager::receiveMessage(LinkInterface* link, mavlink_message_t message)
{
262
    Q_UNUSED(link);
263

264 265
    // receiveMessage is signalled will all mavlink messages so we need to filter everything else out but ours.
    if (message.msgid != MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL) {
266 267
        return;
    }
268
    
269 270 271 272 273 274
    mavlink_file_transfer_protocol_t data;
    mavlink_msg_file_transfer_protocol_decode(&message, &data);
    
    // Make sure we are the target system
    if (data.target_system != _systemIdQGC) {
        qDebug() << "Received MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL with possibly incorrect system id" << _systemIdQGC;
275 276
        return;
    }
277 278 279
    
    Request* request = (Request*)&data.payload[0];
    
280 281
    _clearAckTimeout();
    
282
    uint16_t incomingSeqNumber = request->hdr.seqNumber;
283
    
Don Gagne's avatar
Don Gagne committed
284
    // Make sure we have a good CRC
285 286 287
    quint32 expectedCRC = crc32(request);
    quint32 receivedCRC = request->hdr.crc32;
    if (receivedCRC != expectedCRC) {
Don Gagne's avatar
Don Gagne committed
288
        _currentOperation = kCOIdle;
289
        _emitErrorMessage(tr("Bad CRC on received message: expected(%1) received(%2)").arg(expectedCRC).arg(receivedCRC));
Don Gagne's avatar
Don Gagne committed
290 291
        return;
    }
292 293 294 295 296 297 298 299 300 301 302
    
    // Make sure we have a good sequence number
    uint16_t expectedSeqNumber = _lastOutgoingSeqNumber + 1;
    if (incomingSeqNumber != expectedSeqNumber) {
        _currentOperation = kCOIdle;
        _emitErrorMessage(tr("Bad sequence number on received message: expected(%1) received(%2)").arg(expectedSeqNumber).arg(incomingSeqNumber));
        return;
    }
    
    // Move past the incoming sequence number for next request
    _lastOutgoingSeqNumber = incomingSeqNumber;
303

304
    if (request->hdr.opcode == kRspAck) {
305

306 307 308 309 310
        switch (_currentOperation) {
            case kCOIdle:
                // we should not be seeing anything here.. shut the other guy up
                _sendCmdReset();
                break;
311

312 313 314 315
            case kCOAck:
                // We are expecting an ack back
                _currentOperation = kCOIdle;
                break;
316

317
            case kCOList:
318
                _listAckResponse(request);
319
                break;
320

321
            case kCOOpen:
322
                _openAckResponse(request);
323
                break;
324

325
            case kCORead:
326
                _readAckResponse(request);
327 328
                break;

329
            default:
Don Gagne's avatar
Don Gagne committed
330
                _emitErrorMessage(tr("Ack received in unexpected state"));
331 332 333
                break;
        }
    } else if (request->hdr.opcode == kRspNak) {
334
        Q_ASSERT(request->hdr.size == 1); // Should only have one byte of error code
335

336 337
        OperationState previousOperation = _currentOperation;
        uint8_t errorCode = request->data[0];
338

339
        _currentOperation = kCOIdle;
340

341 342
        if (previousOperation == kCOList && errorCode == kErrEOF) {
            // This is not an error, just the end of the read loop
Don Gagne's avatar
Don Gagne committed
343
            emit listComplete();
344 345 346 347 348 349 350 351 352 353 354 355 356
            return;
        } else if (previousOperation == kCORead && errorCode == kErrEOF) {
            // This is not an error, just the end of the read loop
            _closeReadSession(true /* success */);
            return;
        } else {
            // Generic Nak handling
            if (previousOperation == kCORead) {
                // Nak error during read loop, download failed
                _closeReadSession(false /* failure */);
            }
            _emitErrorMessage(tr("Nak received, error: %1").arg(errorString(request->data[0])));
        }
357 358 359
    } else {
        // Note that we don't change our operation state. If something goes wrong beyond this, the operation
        // will time out.
360
        _emitErrorMessage(tr("Unknown opcode returned from server: %1").arg(request->hdr.opcode));
Lorenz Meier's avatar
Lorenz Meier committed
361 362 363
    }
}

364
void QGCUASFileManager::listDirectory(const QString& dirPath)
Lorenz Meier's avatar
Lorenz Meier committed
365
{
366 367 368
    if (_currentOperation != kCOIdle) {
        _emitErrorMessage(tr("Command not sent. Waiting for previous command to complete."));
        return;
none's avatar
none committed
369 370 371
    }

    // initialise the lister
372
    _listPath = dirPath;
373 374
    _listOffset = 0;
    _currentOperation = kCOList;
none's avatar
none committed
375 376

    // and send the initial request
377
    _sendListCommand();
none's avatar
none committed
378 379
}

380 381 382 383 384 385
void QGCUASFileManager::_fillRequestWithString(Request* request, const QString& str)
{
    strncpy((char *)&request->data[0], str.toStdString().c_str(), sizeof(request->data));
    request->hdr.size = strnlen((const char *)&request->data[0], sizeof(request->data));
}

386
void QGCUASFileManager::_sendListCommand(void)
none's avatar
none committed
387
{
388
    Request request;
none's avatar
none committed
389

390 391 392
    request.hdr.session = 0;
    request.hdr.opcode = kCmdList;
    request.hdr.offset = _listOffset;
393
    request.hdr.size = 0;
394

395
    _fillRequestWithString(&request, _listPath);
396

397
    _sendRequest(&request);
Lorenz Meier's avatar
Lorenz Meier committed
398 399
}

400 401 402 403
/// @brief Downloads the specified file.
///     @param from File to download from UAS, fully qualified path
///     @param downloadDir Local directory to download file to
void QGCUASFileManager::downloadPath(const QString& from, const QDir& downloadDir)
Lorenz Meier's avatar
Lorenz Meier committed
404
{
405 406 407
    if (from.isEmpty()) {
        return;
    }
408

409
    _readFileDownloadDir.setPath(downloadDir.absolutePath());
410

411 412 413 414 415 416 417 418 419 420
    // We need to strip off the file name from the fully qualified path. We can't use the usual QDir
    // routines because this path does not exist locally.
    int i;
    for (i=from.size()-1; i>=0; i--) {
        if (from[i] == '/') {
            break;
        }
    }
    i++; // move past slash
    _readFileDownloadFilename = from.right(from.size() - i);
421

422
    _currentOperation = kCOOpen;
Lorenz Meier's avatar
Lorenz Meier committed
423

424 425 426 427
    Request request;
    request.hdr.session = 0;
    request.hdr.opcode = kCmdOpen;
    request.hdr.offset = 0;
428
    request.hdr.size = 0;
429 430
    _fillRequestWithString(&request, from);
    _sendRequest(&request);
431
}
none's avatar
none committed
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457

QString QGCUASFileManager::errorString(uint8_t errorCode)
{
    switch(errorCode) {
    case kErrNone:
        return QString("no error");
    case kErrNoRequest:
        return QString("bad request");
    case kErrNoSession:
        return QString("bad session");
    case kErrSequence:
        return QString("bad sequence number");
    case kErrNotDir:
        return QString("not a directory");
    case kErrNotFile:
        return QString("not a file");
    case kErrEOF:
        return QString("read beyond end of file");
    case kErrNotAppend:
        return QString("write not at end of file");
    case kErrTooBig:
        return QString("file too big");
    case kErrIO:
        return QString("device I/O error");
    case kErrPerm:
        return QString("permission denied");
458 459 460 461
    case kErrUnknownCommand:
        return QString("unknown command");
    case kErrCrc:
        return QString("bad crc");
none's avatar
none committed
462
    default:
463
        return QString("unknown error code");
none's avatar
none committed
464 465
    }
}
466 467 468 469 470 471 472 473 474 475 476

/// @brief Sends a command which only requires an opcode and no additional data
///     @param opcode Opcode to send
///     @param newOpState State to put state machine into
/// @return TRUE: command sent, FALSE: command not sent, waiting for previous command to finish
bool QGCUASFileManager::_sendOpcodeOnlyCmd(uint8_t opcode, OperationState newOpState)
{
    if (_currentOperation != kCOIdle) {
        // Can't have multiple commands in play at the same time
        return false;
    }
477

478 479 480 481 482
    Request request;
    request.hdr.session = 0;
    request.hdr.opcode = opcode;
    request.hdr.offset = 0;
    request.hdr.size = 0;
483

484
    _currentOperation = newOpState;
485

486
    _sendRequest(&request);
487

488
    return true;
489 490 491 492 493 494
}

/// @brief Starts the ack timeout timer
void QGCUASFileManager::_setupAckTimeout(void)
{
    Q_ASSERT(!_ackTimer.isActive());
495

496
    _ackTimer.setSingleShot(true);
Don Gagne's avatar
Don Gagne committed
497
    _ackTimer.start(ackTimerTimeoutMsecs);
498 499 500 501 502 503 504 505 506 507 508
}

/// @brief Clears the ack timeout timer
void QGCUASFileManager::_clearAckTimeout(void)
{
    _ackTimer.stop();
}

/// @brief Called when ack timeout timer fires
void QGCUASFileManager::_ackTimeout(void)
{
Don Gagne's avatar
Don Gagne committed
509 510 511
    // Make sure to set _currentOperation state before emitting error message. Code may respond
    // to error message signal by sending another command, which will fail if state is not back
    // to idle. FileView UI works this way with the List command.
512 513 514 515

    switch (_currentOperation) {
        case kCORead:
            _currentOperation = kCOAck;
Don Gagne's avatar
Don Gagne committed
516
            _emitErrorMessage(tr("Timeout waiting for ack: Sending Terminate command"));
517 518 519 520
            _sendTerminateCommand();
            break;
        default:
            _currentOperation = kCOIdle;
Don Gagne's avatar
Don Gagne committed
521
            _emitErrorMessage(tr("Timeout waiting for ack"));
522 523 524 525 526 527 528 529 530
            break;
    }
}

void QGCUASFileManager::_sendTerminateCommand(void)
{
    Request request;
    request.hdr.session = _activeSession;
    request.hdr.opcode = kCmdTerminate;
531
    request.hdr.size = 0;
532
    _sendRequest(&request);
533 534 535 536
}

void QGCUASFileManager::_emitErrorMessage(const QString& msg)
{
537
    qDebug() << "QGCUASFileManager: Error" << msg;
538 539 540
    emit errorMessage(msg);
}

541
void QGCUASFileManager::_emitListEntry(const QString& entry)
542
{
543 544
    qDebug() << "QGCUASFileManager: list entry" << entry;
    emit listEntry(entry);
545 546
}

547 548 549 550
/// @brief Sends the specified Request out to the UAS.
void QGCUASFileManager::_sendRequest(Request* request)
{
    mavlink_message_t message;
551

552
    _setupAckTimeout();
553 554
    
    _lastOutgoingSeqNumber++;
555

556 557
    request->hdr.seqNumber = _lastOutgoingSeqNumber;
    
558
    request->hdr.crc32 = crc32(request);
559 560 561 562 563 564 565 566 567 568 569 570 571 572
    
    if (_systemIdQGC == 0) {
        _systemIdQGC = MainWindow::instance()->getMAVLink()->getSystemId();
    }
    
    Q_ASSERT(_mav);
    mavlink_msg_file_transfer_protocol_pack(_systemIdQGC,       // QGC System ID
                                            0,                  // QGC Component ID
                                            &message,           // Mavlink Message to pack into
                                            0,                  // Target network
                                            _systemIdServer,    // Target system
                                            0,                  // Target component
                                            (uint8_t*)request); // Payload
    
573
    _mav->sendMessage(message);
Lorenz Meier's avatar
Lorenz Meier committed
574
}