9#include <QtCore/QFileInfo>
21 _ackOrNakTimeoutTimer.setSingleShot(
true);
23 _ackOrNakTimeoutTimer.setInterval(
QGC::runningUnitTests() ? kTestAckTimeoutMs : _ackOrNakTimeoutMsecs);
24 connect(&_ackOrNakTimeoutTimer, &QTimer::timeout,
this, &FTPManager::_ackOrNakTimeout);
27 static_assert(
sizeof(MavlinkFTP::RequestHeader) == 12,
"MavlinkFTP::RequestHeader must be 12 bytes");
32bool FTPManager::download(uint8_t fromCompId,
const QString& fromURI,
const QString& toDir,
const QString& fileName,
bool checksize)
34 qCDebug(FTPManagerLog) <<
"Download fromCompId:" << fromCompId
35 <<
"fromURI:" << fromURI
37 <<
"fileName:" << fileName;
39 if (!_rgStateMachine.isEmpty()) {
40 qCDebug(FTPManagerLog) <<
"Cannot download. Already in another operation";
44 _downloadState.reset();
45 _downloadState.toDir.setPath(toDir);
46 _downloadState.checksize = checksize;
48 if (!_parseURI(fromCompId, fromURI, _downloadState.fullPathOnVehicle, _ftpCompId)) {
49 qCWarning(FTPManagerLog) <<
"_parseURI failed";
55 int lastDirSlashIndex;
56 for (lastDirSlashIndex=_downloadState.fullPathOnVehicle.size()-1; lastDirSlashIndex>=0; lastDirSlashIndex--) {
57 if (_downloadState.fullPathOnVehicle[lastDirSlashIndex] ==
'/') {
63 if (fileName.isEmpty()) {
64 _downloadState.fileName = _downloadState.fullPathOnVehicle.right(_downloadState.fullPathOnVehicle.size() - lastDirSlashIndex);
69 _downloadState.fileName = QFileInfo(fileName).fileName();
72 qCDebug(FTPManagerLog) <<
"_downloadState.fullPathOnVehicle:_downloadState.fileName" << _downloadState.fullPathOnVehicle << _downloadState.fileName;
74 static const StateFunctions_t rgDownloadStateMachine[] = {
75 { &FTPManager::_openFileROBegin, &FTPManager::_openFileROAckOrNak, &FTPManager::_openFileROTimeout },
76 { &FTPManager::_burstReadFileBegin, &FTPManager::_burstReadFileAckOrNak, &FTPManager::_burstReadFileTimeout },
77 { &FTPManager::_fillMissingBlocksBegin, &FTPManager::_fillMissingBlocksAckOrNak, &FTPManager::_fillMissingBlocksTimeout },
78 { &FTPManager::_resetSessionsBegin, &FTPManager::_resetSessionsAckOrNak, &FTPManager::_resetSessionsTimeout },
79 { &FTPManager::_downloadCompleteNoError,
nullptr,
nullptr },
81 _setStateMachine(rgDownloadStateMachine, std::size(rgDownloadStateMachine));
83 _downloadState.active =
true;
91 qCDebug(FTPManagerLog) <<
"upload fromFile:" << fromFile <<
"toURI:" << toURI <<
"toCompId:" << toCompId;
93 if (!_rgStateMachine.isEmpty()) {
94 qCDebug(FTPManagerLog) <<
"Cannot upload. Already in another operation";
98 QFileInfo sourceInfo(fromFile);
99 if (!sourceInfo.exists() || !sourceInfo.isFile()) {
100 qCWarning(FTPManagerLog) <<
"Cannot upload. Source file missing" << fromFile;
104 if (sourceInfo.size() > std::numeric_limits<uint32_t>::max()) {
105 qCWarning(FTPManagerLog) <<
"Cannot upload. File too large" << fromFile << sourceInfo.size();
109 _uploadState.reset();
110 _uploadState.localFilePath = fromFile;
111 _uploadState.file.setFileName(fromFile);
112 if (!_uploadState.file.open(QFile::ReadOnly)) {
113 qCWarning(FTPManagerLog) <<
"Cannot upload. Failed to open file" << fromFile << _uploadState.file.errorString();
114 _uploadState.reset();
118 _uploadState.fileSize =
static_cast<uint32_t
>(sourceInfo.size());
119 _uploadState.totalBytesSent = 0;
120 _uploadState.lastChunkSize = 0;
121 _uploadState.retryCount = 0;
122 _uploadState.sessionId = 0;
123 _uploadState.cancelled =
false;
125 if (!_parseURI(toCompId, toURI, _uploadState.fullPathOnVehicle, _ftpCompId)) {
126 qCWarning(FTPManagerLog) <<
"_parseURI failed";
127 _uploadState.reset();
131 static const StateFunctions_t rgUploadStateMachine[] = {
132 { &FTPManager::_createFileBegin, &FTPManager::_createFileAckOrNak, &FTPManager::_createFileTimeout },
133 { &FTPManager::_writeFileBegin, &FTPManager::_writeFileAckOrNak, &FTPManager::_writeFileTimeout },
134 { &FTPManager::_resetSessionsBegin, &FTPManager::_resetSessionsAckOrNak, &FTPManager::_resetSessionsTimeout },
135 { &FTPManager::_uploadFinalize,
nullptr,
nullptr },
137 _setStateMachine(rgUploadStateMachine, std::size(rgUploadStateMachine));
139 _startStateMachine();
146 qCDebug(FTPManagerLog) <<
"list directory fromURI:" << fromURI <<
"fromCompId:" << fromCompId;
148 if (!_rgStateMachine.isEmpty()) {
149 qCDebug(FTPManagerLog) <<
"Cannot list directory. Already in another operation";
153 _listDirectoryState.reset();
156 _listDirectoryState.opCode = (_listDirWithTimeSupport == WithTimeSupport_t::Unsupported)
161 const char* supportStr =
162 (_listDirWithTimeSupport == WithTimeSupport_t::Supported) ?
"vehicle supports it" :
163 (_listDirWithTimeSupport == WithTimeSupport_t::Unsupported) ?
"vehicle Nak'ed it earlier this connection" :
164 "support unknown, probing";
166 <<
"-" << supportStr;
168 if (!_parseURI(fromCompId, fromURI, _listDirectoryState.fullPathOnVehicle, _ftpCompId)) {
169 qCWarning(FTPManagerLog) <<
"_parseURI failed";
173 qCDebug(FTPManagerLog) <<
"_listDirectoryState.fullPathOnVehicle" << _listDirectoryState.fullPathOnVehicle;
175 static const StateFunctions_t rgStateMachine[] = {
176 { &FTPManager::_listDirectoryBegin, &FTPManager::_listDirectoryAckOrNak, &FTPManager::_listDirectoryTimeout },
177 { &FTPManager::_listDirectoryCompleteNoError,
nullptr,
nullptr },
179 _setStateMachine(rgStateMachine, std::size(rgStateMachine));
181 _startStateMachine();
188 qCDebug(FTPManagerLog) <<
"delete file fromURI:" << fromURI <<
"fromCompId:" << fromCompId;
190 if (!_rgStateMachine.isEmpty()) {
191 qCDebug(FTPManagerLog) <<
"Cannot delete file. Already in another operation";
195 _deleteState.reset();
197 if (!_parseURI(fromCompId, fromURI, _deleteState.fullPathOnVehicle, _ftpCompId)) {
198 qCWarning(FTPManagerLog) <<
"_parseURI failed";
202 qCDebug(FTPManagerLog) <<
"_deleteState.fullPathOnVehicle" << _deleteState.fullPathOnVehicle;
204 static const StateFunctions_t rgStateMachine[] = {
205 { &FTPManager::_deleteFileBegin, &FTPManager::_deleteFileAckOrNak, &FTPManager::_deleteFileTimeout },
206 { &FTPManager::_deleteCompleteNoError,
nullptr,
nullptr },
208 _setStateMachine(rgStateMachine, std::size(rgStateMachine));
210 _startStateMachine();
217 if (!_downloadState.inProgress()) {
223 _downloadComplete(tr(
"Aborted"));
228 if (!_listDirectoryState.inProgress()) {
232 if (_rgStateMachine.isEmpty()) {
236 _listDirectoryComplete(tr(
"Aborted"));
241 if (!_uploadState.inProgress()) {
245 _uploadState.cancelled =
true;
246 _ackOrNakTimeoutTimer.stop();
247 _rgStateMachine.clear();
249 if (_uploadState.sessionId != 0) {
250 static const StateFunctions_t rgTerminateStateMachine[] = {
251 { &FTPManager::_terminateUploadSessionBegin, &FTPManager::_terminateUploadSessionAckOrNak, &FTPManager::_terminateUploadSessionTimeout },
252 { &FTPManager::_uploadFinalize,
nullptr,
nullptr },
254 for (
size_t i=0; i<
sizeof(rgTerminateStateMachine)/
sizeof(rgTerminateStateMachine[0]); i++) {
255 _rgStateMachine.append(rgTerminateStateMachine[i]);
257 _uploadState.retryCount = 0;
258 _startStateMachine();
260 _uploadComplete(tr(
"Aborted"));
266 if (!_deleteState.inProgress()) {
270 _deleteComplete(tr(
"Aborted"));
275void FTPManager::_downloadComplete(
const QString& errorMsg)
277 qCDebug(FTPManagerLog) << QString(
"_downloadComplete: errorMsg(%1)").arg(errorMsg);
279 QString downloadFilePath = _downloadState.toDir.absoluteFilePath(_downloadState.fileName);
280 QString
error = errorMsg;
282 _ackOrNakTimeoutTimer.stop();
283 _rgStateMachine.clear();
284 _currentStateMachineIndex = -1;
285 if (_downloadState.file.isOpen()) {
286 _downloadState.file.close();
287 if (!errorMsg.isEmpty()) {
288 _downloadState.file.remove();
294 if (!errorMsg.isEmpty()) {
295 _sendResetSessionsNoAck();
297 _downloadState.active =
false;
304void FTPManager::_listDirectoryComplete(
const QString& errorMsg)
306 qCDebug(FTPManagerLog) << QString(
"_listDirectoryComplete: errorMsg(%1)").arg(errorMsg);
308 _ackOrNakTimeoutTimer.stop();
309 _rgStateMachine.clear();
310 _currentStateMachineIndex = -1;
312 QStringList rgDirectoryList = _listDirectoryState.rgDirectoryList;
313 if (!errorMsg.isEmpty()) {
314 rgDirectoryList.clear();
317 _listDirectoryState.reset();
322void FTPManager::_deleteFileBegin(
void)
324 qCDebug(FTPManagerLog) <<
"file" << _deleteState.fullPathOnVehicle;
326 MavlinkFTP::Request request{};
327 request.hdr.session = 0;
329 request.hdr.offset = 0;
330 request.hdr.size = 0;
331 _fillRequestDataWithString(&request, _deleteState.fullPathOnVehicle);
332 _sendRequestExpectAck(&request);
335void FTPManager::_deleteFileAckOrNak(
const MavlinkFTP::Request* ackOrNak)
339 qCDebug(FTPManagerLog) <<
"_deleteFileAckOrNak: Ack disregarding ack for incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
342 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
343 qCDebug(FTPManagerLog) <<
"_deleteFileAckOrNak: Ack disregarding ack for incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
347 _ackOrNakTimeoutTimer.stop();
350 _advanceStateMachine();
352 qCDebug(FTPManagerLog) <<
"_deleteFileAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
353 _deleteComplete(tr(
"Delete failed") +
": " + _errorMsgFromNak(ackOrNak));
357void FTPManager::_deleteFileTimeout(
void)
359 if (++_deleteState.retryCount > _maxRetry) {
360 qCDebug(FTPManagerLog) << QString(
"_deleteFileTimeout retries exceeded");
361 _deleteComplete(tr(
"Delete failed"));
363 qCDebug(FTPManagerLog) << QString(
"_deleteFileTimeout: retrying - retryCount(%1)").arg(_deleteState.retryCount);
368void FTPManager::_deleteComplete(
const QString& errorMsg)
370 qCDebug(FTPManagerLog) << QString(
"_deleteComplete: errorMsg(%1)").arg(errorMsg);
372 const QString deletedPath = _deleteState.fullPathOnVehicle;
374 _ackOrNakTimeoutTimer.stop();
375 _rgStateMachine.clear();
376 _currentStateMachineIndex = -1;
378 _deleteState.reset();
383void FTPManager::_createFileBegin(
void)
385 qCDebug(FTPManagerLog) <<
"file" << _uploadState.fullPathOnVehicle;
387 MavlinkFTP::Request request{};
388 request.hdr.session = 0;
390 request.hdr.offset = 0;
391 request.hdr.size = 0;
392 _fillRequestDataWithString(&request, _uploadState.fullPathOnVehicle);
393 _sendRequestExpectAck(&request);
396void FTPManager::_createFileAckOrNak(
const MavlinkFTP::Request* ackOrNak)
400 qCDebug(FTPManagerLog) <<
"_createFileAckOrNak: Ack disregarding ack for incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
403 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
404 qCDebug(FTPManagerLog) <<
"_createFileAckOrNak: Ack disregarding ack for incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
408 _ackOrNakTimeoutTimer.stop();
411 qCDebug(FTPManagerLog) <<
"_createFileAckOrNak: Ack - sessionId" << ackOrNak->hdr.session;
413 _uploadState.sessionId = ackOrNak->hdr.session;
414 _advanceStateMachine();
416 qCDebug(FTPManagerLog) <<
"_createFileAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
417 _uploadComplete(tr(
"Upload failed for: %1 - error: %2").arg(_uploadState.fullPathOnVehicle).arg(_errorMsgFromNak(ackOrNak)));
421void FTPManager::_createFileTimeout(
void)
423 qCDebug(FTPManagerLog) <<
"_createFileTimeout";
424 _uploadComplete(tr(
"Upload failed for: %1 - no response from vehicle").arg(_uploadState.fullPathOnVehicle));
427void FTPManager::_writeFileBegin(
void)
429 _writeFileWorker(
true );
432void FTPManager::_writeFileWorker(
bool firstRequest)
434 if (!_uploadState.file.isOpen()) {
435 _uploadComplete(tr(
"Upload failed for: %1 - file not open").arg(_uploadState.fullPathOnVehicle));
439 if (_uploadState.totalBytesSent >= _uploadState.fileSize) {
440 _advanceStateMachine();
444 qCDebug(FTPManagerLog) <<
"_writeFileWorker: offset:firstRequest:retryCount" << _uploadState.totalBytesSent << firstRequest << _uploadState.retryCount;
446 MavlinkFTP::Request request{};
447 request.hdr.session = _uploadState.sessionId;
449 request.hdr.offset = _uploadState.totalBytesSent;
452 _uploadState.retryCount = 0;
454 _expectedIncomingSeqNumber -= 2;
457 qint64 bytesRemaining =
static_cast<qint64
>(_uploadState.fileSize) -
static_cast<qint64
>(_uploadState.totalBytesSent);
458 qint64 bytesToSend = bytesRemaining;
459 if (bytesToSend >
static_cast<qint64
>(
sizeof(request.data))) {
460 bytesToSend =
sizeof(request.data);
463 if (!_uploadState.file.seek(_uploadState.totalBytesSent)) {
464 qCDebug(FTPManagerLog) <<
"_writeFileWorker: seek failed" << _uploadState.file.errorString();
465 _uploadComplete(tr(
"Upload failed for: %1 - error reading file").arg(_uploadState.fullPathOnVehicle));
469 qint64 bytesRead = _uploadState.file.read(
reinterpret_cast<char*
>(request.data), bytesToSend);
470 if (bytesRead != bytesToSend) {
471 qCDebug(FTPManagerLog) <<
"_writeFileWorker: read failed" << _uploadState.file.errorString();
472 _uploadComplete(tr(
"Upload failed for: %1 - error reading file").arg(_uploadState.fullPathOnVehicle));
476 request.hdr.size =
static_cast<uint8_t
>(bytesRead);
477 _uploadState.lastChunkSize =
static_cast<uint32_t
>(bytesRead);
479 _sendRequestExpectAck(&request);
482void FTPManager::_writeFileAckOrNak(
const MavlinkFTP::Request* ackOrNak)
487 qCDebug(FTPManagerLog) <<
"_writeFileAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
490 if (ackOrNak->hdr.session != _uploadState.sessionId) {
491 qCDebug(FTPManagerLog) <<
"_writeFileAckOrNak: Disregarding due to incorrect session id actual:expected" << ackOrNak->hdr.session << _uploadState.sessionId;
495 _ackOrNakTimeoutTimer.stop();
498 if (ackOrNak->hdr.seqNumber < _expectedIncomingSeqNumber) {
499 qCDebug(FTPManagerLog) <<
"_writeFileAckOrNak: Disregarding Ack due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
503 if (ackOrNak->hdr.size != 0) {
504 qCDebug(FTPManagerLog) <<
"_writeFileAckOrNak: unexpected ack size expected:actual 0" << ackOrNak->hdr.size;
507 _uploadState.totalBytesSent += _uploadState.lastChunkSize;
508 _uploadState.lastChunkSize = 0;
509 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber;
511 if (_uploadState.fileSize != 0) {
512 emit
commandProgress(
static_cast<float>(_uploadState.totalBytesSent) /
static_cast<float>(_uploadState.fileSize));
515 if (_uploadState.totalBytesSent >= _uploadState.fileSize) {
516 _advanceStateMachine();
518 _writeFileWorker(
true );
521 qCDebug(FTPManagerLog) <<
"_writeFileAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
522 _uploadComplete(tr(
"Upload failed for: %1 - error: %2").arg(_uploadState.fullPathOnVehicle).arg(_errorMsgFromNak(ackOrNak)));
526void FTPManager::_writeFileTimeout(
void)
528 if (++_uploadState.retryCount > _maxRetry) {
529 qCDebug(FTPManagerLog) << QString(
"_writeFileTimeout retries exceeded");
530 _uploadComplete(tr(
"Upload failed for: %1 - no response from vehicle").arg(_uploadState.fullPathOnVehicle));
532 qCDebug(FTPManagerLog) << QString(
"_writeFileTimeout: retrying - retryCount(%1) offset(%2)").arg(_uploadState.retryCount).arg(_uploadState.totalBytesSent);
533 _writeFileWorker(
false );
537void FTPManager::_terminateUploadSessionBegin(
void)
539 if (_uploadState.sessionId == 0) {
540 qCWarning(FTPManagerLog) <<
"_terminateUploadSessionBegin: No session to terminate";
541 _advanceStateMachine();
545 MavlinkFTP::Request request{};
546 request.hdr.session = _uploadState.sessionId;
548 _sendRequestExpectAck(&request);
551void FTPManager::_terminateUploadSessionAckOrNak(
const MavlinkFTP::Request* ackOrNak)
555 qCDebug(FTPManagerLog) <<
"_terminateUploadSessionAckOrNak: Ack disregarding ack for incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
558 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
559 qCDebug(FTPManagerLog) <<
"_terminateUploadSessionAckOrNak: Ack disregarding ack for incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
563 _ackOrNakTimeoutTimer.stop();
566 qCDebug(FTPManagerLog) <<
"_terminateUploadSessionAckOrNak: Ack";
567 _advanceStateMachine();
569 qCDebug(FTPManagerLog) <<
"_terminateUploadSessionAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
570 _uploadComplete(tr(
"Upload failed for: %1 - error: %2").arg(_uploadState.fullPathOnVehicle).arg(_errorMsgFromNak(ackOrNak)));
574void FTPManager::_terminateUploadSessionTimeout(
void)
576 if (++_uploadState.retryCount > _maxRetry) {
577 qCDebug(FTPManagerLog) << QString(
"_terminateUploadSessionTimeout retries exceeded");
578 _uploadComplete(tr(
"Upload failed for: %1 - no response from vehicle").arg(_uploadState.fullPathOnVehicle));
580 qCDebug(FTPManagerLog) << QString(
"_terminateUploadSessionTimeout: retrying - retryCount(%1)").arg(_uploadState.retryCount);
581 _terminateUploadSessionBegin();
585void FTPManager::_uploadFinalize(
void)
587 QString
error = _uploadState.cancelled ? tr(
"Aborted for: %1").arg(_uploadState.fullPathOnVehicle) : QString();
588 _uploadComplete(
error);
591void FTPManager::_uploadComplete(
const QString& errorMsg)
593 qCDebug(FTPManagerLog) << QString(
"_uploadComplete: errorMsg(%1)").arg(errorMsg)
594 <<
"local" << _uploadState.localFilePath
595 <<
"remote" << _uploadState.fullPathOnVehicle;
597 QString remotePath = _uploadState.fullPathOnVehicle;
599 _ackOrNakTimeoutTimer.stop();
600 _rgStateMachine.clear();
601 _currentStateMachineIndex = -1;
603 if (_uploadState.file.isOpen()) {
604 _uploadState.file.close();
607 _uploadState.reset();
614 if (message.msgid != MAVLINK_MSG_ID_FILE_TRANSFER_PROTOCOL ||
615 message.sysid != _vehicle->
id() || message.compid != _ftpCompId) {
619 if (_currentStateMachineIndex == -1) {
623 mavlink_file_transfer_protocol_t data;
624 mavlink_msg_file_transfer_protocol_decode(&message, &data);
628 if (data.target_system != qgcId) {
632 MavlinkFTP::Request* request = (MavlinkFTP::Request*)&data.payload[0];
635 if (request->hdr.size >
sizeof(request->data)) {
636 qCWarning(FTPManagerLog) <<
"_mavlinkMessageReceived: hdr.size exceeds data array, discarding." << request->hdr.size;
642 uint16_t actualIncomingSeqNumber = request->hdr.seqNumber;
645 && (_rgStateMachine[_currentStateMachineIndex].ackNakFn == &FTPManager::_burstReadFileAckOrNak);
646 if (!isBurstData && (uint16_t)((_expectedIncomingSeqNumber - 1) - actualIncomingSeqNumber) < (std::numeric_limits<uint16_t>::max()/2)) {
647 qCDebug(FTPManagerLog) <<
"_mavlinkMessageReceived: Received old packet seqNum expected:actual" << _expectedIncomingSeqNumber << actualIncomingSeqNumber
653 qCDebug(FTPManagerLog) <<
"_mavlinkMessageReceived: hdr.opcode:hdr.req_opcode:seqNumber"
655 << request->hdr.seqNumber;
657 (this->*_rgStateMachine[_currentStateMachineIndex].ackNakFn)(request);
660void FTPManager::_startStateMachine(
void)
662 _currentStateMachineIndex = -1;
663 _advanceStateMachine();
666void FTPManager::_advanceStateMachine(
void)
668 _currentStateMachineIndex++;
669 (this->*_rgStateMachine[_currentStateMachineIndex].beginFn)();
672void FTPManager::_ackOrNakTimeout(
void)
674 (this->*_rgStateMachine[_currentStateMachineIndex].timeoutFn)();
677void FTPManager::_fillRequestDataWithString(MavlinkFTP::Request* request,
const QString& str)
679 strncpy((
char *)&request->data[0], str.toStdString().c_str(),
sizeof(request->data));
680 request->hdr.size =
static_cast<uint8_t
>(strnlen((
const char *)&request->data[0],
sizeof(request->data)));
683QString FTPManager::_errorMsgFromNak(
const MavlinkFTP::Request* nak)
690 errorMsg = tr(
"Invalid Nak format");
695 : tr(
"errno %1").arg(nak->data[1]);
703void FTPManager::_openFileROBegin(
void)
705 MavlinkFTP::Request request{};
706 request.hdr.session = 0;
708 request.hdr.offset = 0;
709 request.hdr.size = 0;
710 _fillRequestDataWithString(&request, _downloadState.fullPathOnVehicle);
711 _sendRequestExpectAck(&request);
714void FTPManager::_openFileROTimeout(
void)
716 qCDebug(FTPManagerLog) <<
"_openFileROTimeout";
717 _downloadComplete(tr(
"Download failed"));
720void FTPManager::_openFileROAckOrNak(
const MavlinkFTP::Request* ackOrNak)
724 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: Ack disregarding ack for incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
727 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
728 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: Ack disregarding ack for incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
732 _ackOrNakTimeoutTimer.stop();
735 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: Ack - sessionId:openFileLength" << ackOrNak->hdr.session << ackOrNak->openFileLength;
737 if (ackOrNak->hdr.size !=
sizeof(uint32_t)) {
738 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: Ack ack->hdr.size != sizeof(uint32_t)" << ackOrNak->hdr.size <<
sizeof(uint32_t);
739 _downloadComplete(tr(
"Download failed"));
743 if (_downloadState.resumeStateIndex >= 0) {
744 if (_downloadState.checksize && (ackOrNak->openFileLength != _downloadState.fileSize)) {
745 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: file size changed across re-open" << ackOrNak->openFileLength << _downloadState.fileSize;
746 _downloadComplete(tr(
"Download failed"));
749 const int resumeStateIndex = _downloadState.resumeStateIndex;
750 _downloadState.resumeStateIndex = -1;
751 _downloadState.sessionId = ackOrNak->hdr.session;
752 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: session re-opened, resuming at offset" << _downloadState.expectedOffset;
753 _currentStateMachineIndex = resumeStateIndex;
754 (this->*_rgStateMachine[resumeStateIndex].beginFn)();
758 _downloadState.sessionId = ackOrNak->hdr.session;
759 _downloadState.fileSize = ackOrNak->openFileLength;
760 _downloadState.expectedOffset = 0;
762 _downloadState.file.setFileName(_downloadState.toDir.filePath(_downloadState.fileName));
763 if (_downloadState.file.open(QFile::WriteOnly | QFile::Truncate)) {
764 _advanceStateMachine();
766 qCDebug(FTPManagerLog) <<
"_openFileROAckOrNak: Ack _downloadState.file open failed" << _downloadState.file.errorString();
767 _downloadComplete(tr(
"Download failed"));
770 qCDebug(FTPManagerLog) <<
"_handlOpenFileROAck: Nak -" << _errorMsgFromNak(ackOrNak);
771 _downloadComplete(tr(
"Download failed") +
": " + _errorMsgFromNak(ackOrNak));
775uint8_t FTPManager::_readChunkSize(
void)
const
781void FTPManager::_burstReadFileWorker(
bool firstRequest)
783 const uint8_t chunkSize = _readChunkSize();
784 qCDebug(FTPManagerLog) <<
"_burstReadFileWorker: starting burst at offset:firstRequest:retryCount:chunkSize" << _downloadState.expectedOffset << firstRequest << _downloadState.retryCount << chunkSize;
786 MavlinkFTP::Request request{};
787 request.hdr.session = _downloadState.sessionId;
789 request.hdr.offset = _downloadState.expectedOffset;
790 request.hdr.size = chunkSize;
793 _downloadState.retryCount = 0;
796 _expectedIncomingSeqNumber -= 2;
799 _sendRequestExpectAck(&request);
802void FTPManager::_burstReadFileBegin(
void)
804 _burstReadFileWorker(
true );
807void FTPManager::_burstReadFileAckOrNak(
const MavlinkFTP::Request* ackOrNak)
812 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
815 if (ackOrNak->hdr.session != _downloadState.sessionId) {
816 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: Disregarding due to incorrect session id actual:expected" << ackOrNak->hdr.session << _downloadState.sessionId;
820 _ackOrNakTimeoutTimer.stop();
823 qCDebug(FTPManagerLog) << QString(
"_burstReadFileAckOrNak: Ack offset(%1) size(%2) burstComplete(%3)").arg(ackOrNak->hdr.offset).arg(ackOrNak->hdr.size).arg(ackOrNak->hdr.burstComplete);
825 if (ackOrNak->hdr.offset < _downloadState.expectedOffset) {
828 if (_claimMissingRange(ackOrNak->hdr.offset, ackOrNak->hdr.size)) {
829 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: late packet fills hole at offset" << ackOrNak->hdr.offset;
830 _downloadState.file.seek(ackOrNak->hdr.offset);
831 if (_downloadState.file.write((
const char*)ackOrNak->data, ackOrNak->hdr.size) != ackOrNak->hdr.size) {
832 _downloadComplete(tr(
"Download failed: Error saving file"));
835 _downloadState.bytesWritten += ackOrNak->hdr.size;
837 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: duplicate packet at offset" << ackOrNak->hdr.offset;
839 _ackOrNakTimeoutTimer.start();
840 if (_downloadState.fileSize != 0) {
841 emit
commandProgress((
float)(_downloadState.bytesWritten) / (
float)_downloadState.fileSize);
846 if (ackOrNak->hdr.offset > _downloadState.expectedOffset) {
848 MissingData_t missingData;
849 missingData.offset = _downloadState.expectedOffset;
850 missingData.cBytesMissing = ackOrNak->hdr.offset - _downloadState.expectedOffset;
851 _downloadState.rgMissingData.append(missingData);
852 qCDebug(FTPManagerLog) <<
"_handleBurstReadFileAck: adding missing data offset:cBytesMissing" << missingData.offset << missingData.cBytesMissing;
855 _downloadState.file.seek(ackOrNak->hdr.offset);
856 int bytesWritten = _downloadState.file.write((
const char*)ackOrNak->data, ackOrNak->hdr.size);
857 if (bytesWritten != ackOrNak->hdr.size) {
858 _downloadComplete(tr(
"Download failed: Error saving file"));
861 _downloadState.bytesWritten += ackOrNak->hdr.size;
862 _downloadState.expectedOffset = ackOrNak->hdr.offset + ackOrNak->hdr.size;
864 if (ackOrNak->hdr.burstComplete) {
866 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber;
867 _burstReadFileWorker(
true );
870 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber + 1;
871 _ackOrNakTimeoutTimer.start();
875 if (_downloadState.fileSize != 0) {
876 emit
commandProgress((
float)(_downloadState.bytesWritten) / (
float)_downloadState.fileSize);
883 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
884 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: EOF Nak"
885 "with incorrect sequence nr actual:expected"
886 << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
888 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber;
889 _burstReadFileWorker(
true);
891 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak EOF";
892 _advanceStateMachine();
895 _downloadSessionLost();
897 qCDebug(FTPManagerLog) <<
"_burstReadFileAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
898 _downloadComplete(tr(
"Download failed"));
903bool FTPManager::_claimMissingRange(uint32_t offset, uint32_t cBytes)
905 if (cBytes == 0 || offset > std::numeric_limits<uint32_t>::max() - cBytes) {
909 for (
int i = 0; i < _downloadState.rgMissingData.count(); i++) {
910 MissingData_t& hole = _downloadState.rgMissingData[i];
911 const uint32_t holeEnd = hole.offset + hole.cBytesMissing;
912 if ((offset < hole.offset) || (offset + cBytes > holeEnd)) {
916 const MissingData_t after{ offset + cBytes, holeEnd - (offset + cBytes) };
917 if (offset == hole.offset) {
918 if (after.cBytesMissing == 0) {
919 _downloadState.rgMissingData.removeAt(i);
924 hole.cBytesMissing = offset - hole.offset;
925 if (after.cBytesMissing != 0) {
926 _downloadState.rgMissingData.insert(i + 1, after);
934void FTPManager::_downloadSessionLost(
void)
937 qCDebug(FTPManagerLog) <<
"_downloadSessionLost: re-open limit reached";
938 _downloadComplete(tr(
"Download failed"));
942 qCDebug(FTPManagerLog) <<
"_downloadSessionLost: re-opening to resume at offset" << _downloadState.expectedOffset <<
"reopenCount" << _downloadState.sessionReopenCount;
943 _downloadState.resumeStateIndex = _currentStateMachineIndex;
945 _currentStateMachineIndex = -1;
946 _advanceStateMachine();
949void FTPManager::_burstReadFileTimeout(
void)
951 if (++_downloadState.retryCount > _maxRetry) {
952 qCDebug(FTPManagerLog) << QString(
"_burstReadFileTimeout retries exceeded");
953 _downloadComplete(tr(
"Download failed"));
956 qCDebug(FTPManagerLog) << QString(
"_burstReadFileTimeout: retrying - retryCount(%1) offset(%2)").arg(_downloadState.retryCount).arg(_downloadState.expectedOffset);
957 _burstReadFileWorker(
false );
961void FTPManager::_listDirectoryWorker(
bool firstRequest)
963 qCDebug(FTPManagerLog) <<
"_listDirectoryWorker: offset:firstRequest:retryCount" << _listDirectoryState.expectedOffset << firstRequest << _listDirectoryState.retryCount;
965 MavlinkFTP::Request request{};
966 request.hdr.session = _downloadState.sessionId;
967 request.hdr.opcode = _listDirectoryState.opCode;
968 request.hdr.offset = _listDirectoryState.expectedOffset;
969 request.hdr.size =
sizeof(request.data);
970 _fillRequestDataWithString(&request, _listDirectoryState.fullPathOnVehicle);
973 _listDirectoryState.retryCount = 0;
976 _expectedIncomingSeqNumber -= 2;
979 _sendRequestExpectAck(&request);
982void FTPManager::_listDirectoryBegin(
void)
984 _listDirectoryWorker(
true );
987void FTPManager::_listDirectoryAckOrNak(
const MavlinkFTP::Request* ackOrNak)
991 if (requestOpCode != _listDirectoryState.opCode) {
992 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
996 _ackOrNakTimeoutTimer.stop();
999 if (ackOrNak->hdr.seqNumber < _expectedIncomingSeqNumber) {
1000 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak: Disregarding Ack due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
1005 _listDirWithTimeSupport = WithTimeSupport_t::Supported;
1008 qCDebug(FTPManagerLog) << QString(
"_listDirectoryAckOrNak: Ack size(%1)").arg(ackOrNak->hdr.size);
1011 const char* curDataPtr = (
const char*)ackOrNak->data;
1012 while (curDataPtr < (
const char*)ackOrNak->data + ackOrNak->hdr.size) {
1013 QString dirEntry = curDataPtr;
1014 curDataPtr += dirEntry.size() + 1;
1015 _listDirectoryState.rgDirectoryList.append(dirEntry);
1016 _listDirectoryState.expectedOffset++;
1020 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber;
1021 _listDirectoryWorker(
true );
1027 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
1028 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak: Disregarding Nak due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
1029 _ackOrNakTimeoutTimer.start();
1032 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak EOF";
1034 _listDirWithTimeSupport = WithTimeSupport_t::Supported;
1036 _advanceStateMachine();
1040 (errorCode ==
MavlinkFTP::kErrFail && _listDirWithTimeSupport == WithTimeSupport_t::Unknown))) {
1046 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak: kCmdListDirectoryWithTime unsupported, falling back to kCmdListDirectory";
1047 _listDirWithTimeSupport = WithTimeSupport_t::Unsupported;
1049 _listDirectoryState.expectedOffset = 0;
1050 _listDirectoryState.rgDirectoryList.clear();
1051 _expectedIncomingSeqNumber = ackOrNak->hdr.seqNumber;
1052 _listDirectoryWorker(
true );
1054 qCDebug(FTPManagerLog) <<
"_listDirectoryAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
1055 _listDirectoryComplete(tr(
"List directory failed"));
1060void FTPManager::_listDirectoryTimeout(
void)
1062 if (++_listDirectoryState.retryCount > _maxRetry) {
1063 qCDebug(FTPManagerLog) << QString(
"_listDirectoryTimeout retries exceeded");
1064 _listDirectoryComplete(tr(
"List directory failed"));
1067 qCDebug(FTPManagerLog) << QString(
"_listDirectoryTimeout: retrying - retryCount(%1) offset(%2)").arg(_listDirectoryState.retryCount).arg(_listDirectoryState.expectedOffset);
1068 _listDirectoryWorker(
false );
1072void FTPManager::_fillMissingBlocksWorker(
bool firstRequest)
1074 if (_downloadState.rgMissingData.count()) {
1075 MavlinkFTP::Request request{};
1076 MissingData_t& missingData = _downloadState.rgMissingData.first();
1078 uint32_t cBytesToRead = qMin(
static_cast<uint32_t
>(_readChunkSize()), missingData.cBytesMissing);
1080 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksBegin: offset:cBytesToRead" << missingData.offset << cBytesToRead;
1082 request.hdr.session = _downloadState.sessionId;
1084 request.hdr.offset = missingData.offset;
1085 request.hdr.size = cBytesToRead;
1088 _downloadState.retryCount = 0;
1091 _expectedIncomingSeqNumber -= 2;
1093 _downloadState.expectedOffset = request.hdr.offset;
1095 _sendRequestExpectAck(&request);
1098 if (_downloadState.checksize ==
false || _downloadState.bytesWritten == _downloadState.fileSize) {
1099 _advanceStateMachine();
1101 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksWorker: no missing blocks but file still incomplete - bytesWritten:fileSize" << _downloadState.bytesWritten << _downloadState.fileSize;
1102 _downloadComplete(tr(
"Download failed"));
1107void FTPManager::_fillMissingBlocksBegin(
void)
1109 _fillMissingBlocksWorker(
true );
1112void FTPManager::_fillMissingBlocksAckOrNak(
const MavlinkFTP::Request* ackOrNak)
1117 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
1120 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
1121 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Disregarding due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
1124 if (ackOrNak->hdr.session != _downloadState.sessionId) {
1125 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Disregarding due to incorrect session id actual:expected" << ackOrNak->hdr.session << _downloadState.sessionId;
1129 _ackOrNakTimeoutTimer.stop();
1132 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Ack offset:size" << ackOrNak->hdr.offset << ackOrNak->hdr.size;
1134 if (ackOrNak->hdr.offset != _downloadState.expectedOffset) {
1135 if (++_downloadState.retryCount > _maxRetry) {
1136 qCDebug(FTPManagerLog) << QString(
"_fillMissingBlocksAckOrNak: offset mismatch, retries exceeded");
1137 _downloadComplete(tr(
"Download failed"));
1142 qCDebug(FTPManagerLog) << QString(
"_fillMissingBlocksAckOrNak: Ack offset mismatch retry, retryCount(%1) offset(%2)").arg(_downloadState.retryCount).arg(_downloadState.expectedOffset);
1143 _fillMissingBlocksWorker(
false );
1147 _downloadState.file.seek(ackOrNak->hdr.offset);
1148 int bytesWritten = _downloadState.file.write((
const char*)ackOrNak->data, ackOrNak->hdr.size);
1149 if (bytesWritten != ackOrNak->hdr.size) {
1150 _downloadComplete(tr(
"Download failed: Error saving file"));
1153 _downloadState.bytesWritten += ackOrNak->hdr.size;
1155 MissingData_t& missingData = _downloadState.rgMissingData.first();
1156 missingData.offset += ackOrNak->hdr.size;
1157 missingData.cBytesMissing -= ackOrNak->hdr.size;
1158 if (missingData.cBytesMissing == 0) {
1160 _downloadState.rgMissingData.takeFirst();
1164 _fillMissingBlocksWorker(
true );
1167 if (_downloadState.fileSize != 0) {
1168 emit
commandProgress((
float)(_downloadState.bytesWritten) / (
float)_downloadState.fileSize);
1174 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak EOF";
1175 if (_downloadState.checksize ==
false || _downloadState.bytesWritten == _downloadState.fileSize) {
1177 _advanceStateMachine();
1181 _downloadSessionLost();
1185 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
1186 _downloadComplete(tr(
"Download failed"));
1190void FTPManager::_fillMissingBlocksTimeout(
void)
1192 if (++_downloadState.retryCount > _maxRetry) {
1193 qCDebug(FTPManagerLog) << QString(
"_fillMissingBlocksTimeout retries exceeded");
1194 _downloadComplete(tr(
"Download failed"));
1197 qCDebug(FTPManagerLog) << QString(
"_fillMissingBlocksTimeout: retrying - retryCount(%1) offset(%2)").arg(_downloadState.retryCount).arg(_downloadState.expectedOffset);
1198 _fillMissingBlocksWorker(
false );
1202void FTPManager::_resetSessionsBegin(
void)
1204 MavlinkFTP::Request request{};
1206 request.hdr.size = 0;
1207 _sendRequestExpectAck(&request);
1210void FTPManager::_resetSessionsAckOrNak(
const MavlinkFTP::Request* ackOrNak)
1215 qCDebug(FTPManagerLog) <<
"_fillMissingBlocksAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
1218 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
1219 qCDebug(FTPManagerLog) <<
"_resetSessionsAckOrNak: Disregarding due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
1223 _ackOrNakTimeoutTimer.stop();
1226 qCDebug(FTPManagerLog) <<
"_resetSessionsAckOrNak: Ack";
1227 _advanceStateMachine();
1229 qCDebug(FTPManagerLog) <<
"_resetSessionsAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak);
1230 _downloadComplete(QString());
1234void FTPManager::_resetSessionsTimeout(
void)
1236 qCDebug(FTPManagerLog) <<
"_resetSessionsTimeout";
1237 _downloadComplete(QString());
1240void FTPManager::_setStateMachine(
const StateFunctions_t* rgStates,
size_t cStates)
1242 _rgStateMachine.append({ &FTPManager::_clearStaleSessionsBegin, &FTPManager::_clearStaleSessionsAckOrNak, &FTPManager::_clearStaleSessionsTimeout });
1243 for (
size_t i = 0; i < cStates; i++) {
1244 _rgStateMachine.append(rgStates[i]);
1248void FTPManager::_clearStaleSessionsBegin(
void)
1250 _clearStaleSessionsRetryCount = 0;
1251 _resetSessionsBegin();
1254void FTPManager::_clearStaleSessionsAckOrNak(
const MavlinkFTP::Request* ackOrNak)
1258 qCDebug(FTPManagerLog) <<
"_clearStaleSessionsAckOrNak: Disregarding due to incorrect requestOpCode" <<
MavlinkFTP::opCodeToString(requestOpCode);
1261 if (ackOrNak->hdr.seqNumber != _expectedIncomingSeqNumber) {
1262 qCDebug(FTPManagerLog) <<
"_clearStaleSessionsAckOrNak: Disregarding due to incorrect sequence actual:expected" << ackOrNak->hdr.seqNumber << _expectedIncomingSeqNumber;
1266 _ackOrNakTimeoutTimer.stop();
1268 qCDebug(FTPManagerLog) <<
"_clearStaleSessionsAckOrNak: Nak -" << _errorMsgFromNak(ackOrNak) <<
"- continuing anyway";
1270 _advanceStateMachine();
1273void FTPManager::_clearStaleSessionsTimeout(
void)
1275 if (++_clearStaleSessionsRetryCount <= 1) {
1276 qCDebug(FTPManagerLog) <<
"_clearStaleSessionsTimeout: retrying";
1277 _resetSessionsBegin();
1280 qCDebug(FTPManagerLog) <<
"_clearStaleSessionsTimeout: no reply, continuing anyway";
1281 _advanceStateMachine();
1284void FTPManager::_sendResetSessionsNoAck(
void)
1291 MavlinkFTP::Request request{};
1293 request.hdr.size = 0;
1294 request.hdr.seqNumber = _expectedIncomingSeqNumber + 1;
1295 _expectedIncomingSeqNumber += 2;
1297 qCDebug(FTPManagerLog) <<
"_sendResetSessionsNoAck seqNumber:" << request.hdr.seqNumber;
1302 sharedLink->mavlinkChannel(),
1307 (uint8_t*)&request);
1311void FTPManager::_sendRequestExpectAck(MavlinkFTP::Request* request)
1313 _ackOrNakTimeoutTimer.start();
1317 request->hdr.seqNumber = _expectedIncomingSeqNumber + 1;
1318 _expectedIncomingSeqNumber += 2;
1325 sharedLink->mavlinkChannel(),
1333 qCDebug(FTPManagerLog) <<
"_sendRequestExpectAck No primary link. Allowing timeout to fail sequence.";
1337bool FTPManager::_parseURI(uint8_t fromCompId,
const QString& uri, QString& parsedURI, uint8_t& compId)
1340 compId = (fromCompId == MAV_COMP_ID_ALL) ? (uint8_t)MAV_COMP_ID_AUTOPILOT1 : fromCompId;
1344 if (parsedURI.startsWith(ftpPrefix, Qt::CaseInsensitive)) {
1345 parsedURI = parsedURI.mid(ftpPrefix.length());
1347 if (parsedURI.contains(
"://")) {
1348 qCWarning(FTPManagerLog) <<
"Incorrect uri scheme or format" << uri;
1353 QRegularExpression regEx(
"^/??\\[\\;comp\\=(\\d+)\\]");
1354 QRegularExpressionMatch match = regEx.match(parsedURI);
1355 if (match.hasMatch()) {
1357 compId = match.captured(1).toUInt(&ok);
1359 qCWarning(FTPManagerLog) <<
"Incorrect format for component id" << uri;
1363 qCDebug(FTPManagerLog) <<
"Found compId in MAVLink FTP URI: " << compId;
1364 parsedURI.replace(QRegularExpression(
"\\[\\;comp\\=\\d+\\]"),
"");
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
bool listDirectory(uint8_t fromCompId, const QString &fromURI)
bool deleteFile(uint8_t fromCompId, const QString &fromURI)
static constexpr uint8_t kRadioReadChunkSize
bool upload(uint8_t toCompId, const QString &toURI, const QString &fromFile)
static constexpr uint8_t kFullReadChunkSize
Bytes requested per ReadFile/BurstReadFile chunk on non-radio links: the full FTP payload.
static constexpr const char * mavlinkFTPScheme
void uploadComplete(const QString &file, const QString &errorMsg)
static constexpr int kMaxDownloadSessionReopens
Max times a download re-opens after the server expires the session (PX4 idle timer fires mid-burst)
void commandProgress(float value)
void downloadComplete(const QString &file, const QString &errorMsg)
void cancelListDirectory()
void deleteComplete(const QString &file, const QString &errorMsg)
bool download(uint8_t fromCompId, const QString &fromURI, const QString &toDir, const QString &fileName="", bool checksize=true)
void listDirectoryComplete(const QStringList &dirList, const QString &errorMsg)
static int getComponentId()
static MAVLinkProtocol * instance()
ErrorCode_t
Error codes returned in Nak response PayloadHeader.data[0].
@ kErrFail
Unknown failure.
@ kErrEOF
Offset past end of file for List and Read commands.
@ kErrUnknownCommand
Unknown command opcode.
@ kErrFailErrno
errno sent back in PayloadHeader.data[1]
@ kErrInvalidSession
Session is not currently open.
static QString errorCodeToString(ErrorCode_t errorCode)
@ kCmdRemoveFile
Remove file at <path>
@ kCmdWriteFile
Writes <size> bytes to <offset> in <session>
@ kCmdCreateFile
Creates file at <path> for writing, returns <session>
@ kCmdOpenFileRO
Opens file at <path> for reading, returns <session>
@ kCmdListDirectoryWithTime
List directory as kCmdListDirectory, each entry additionally carrying trailing \t<modification time>
@ kCmdReadFile
Reads <size> bytes from <offset> in <session>
@ kCmdResetSessions
Terminates all open Read sessions.
@ kCmdBurstReadFile
Burst download session file.
@ kCmdTerminateSession
Terminates open Read session.
@ kCmdListDirectory
List files in <path> from <offset>
static QString opCodeToString(OpCode_t opCode)
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)