QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
PlanManager.cc
Go to the documentation of this file.
1#include "PlanManager.h"
2
3#include "AppMessages.h"
4#include "FirmwarePlugin.h"
5#include "MAVLinkLib.h"
6#include "MAVLinkProtocol.h"
9#include "Vehicle.h"
10#include "VehicleLinkManager.h"
11
12QGC_LOGGING_CATEGORY(PlanManagerLog, "PlanManager.PlanManager")
13
14PlanManager::PlanManager(Vehicle* vehicle, MAV_MISSION_TYPE planType)
15 : QObject (vehicle)
16 , _vehicle (vehicle)
17 , _planType (planType)
18 , _ackTimeoutTimer (nullptr)
19 , _expectedAck (AckNone)
20 , _transactionInProgress (TransactionNone)
21 , _resumeMission (false)
22 , _lastMissionRequest (-1)
23 , _missionItemCountToRead (-1)
24 , _currentMissionIndex (-1)
25 , _lastCurrentIndex (-1)
26{
27 _ackTimeoutTimer = new QTimer(this);
28 _ackTimeoutTimer->setSingleShot(true);
29
30 connect(_ackTimeoutTimer, &QTimer::timeout, this, &PlanManager::_ackTimeout);
31}
32
37
39{
41
42 emit progressPctChanged(0);
43
44 qCDebug(PlanManagerLog) << QStringLiteral("writeMissionItems %1 count:").arg(_planTypeString()) << _writeMissionItems.count();
45
46 // Prime write list
47 _itemIndicesToWrite.clear();
48 for (int i=0; i<_writeMissionItems.count(); i++) {
50 }
51
52 _retryCount = 0;
53 _setTransactionInProgress(TransactionWrite);
56}
57
58
59void PlanManager::writeMissionItems(const QList<MissionItem*>& missionItems)
60{
62 return;
63 }
64
65 if (inProgress()) {
66 qCDebug(PlanManagerLog) << QStringLiteral("writeMissionItems %1 called while transaction in progress").arg(_planTypeString());
67 return;
68 }
69
71
72 bool skipFirstItem = _planType == MAV_MISSION_TYPE_MISSION && !_vehicle->firmwarePlugin()->sendHomePositionToVehicle();
73
74 if (skipFirstItem && missionItems.count() > 0) {
75 // First item is not going to be moved to _writeMissionItems, free it now.
76 delete missionItems[0];
77 }
78
79 int firstIndex = skipFirstItem ? 1 : 0;
80
81 for (int i=firstIndex; i<missionItems.count(); i++) {
82 MissionItem* item = missionItems[i];
83 _writeMissionItems.append(item); // PlanManager takes control of passed MissionItem
84
85 item->setIsCurrentItem(i == firstIndex);
86
87 if (skipFirstItem) {
88 // Home is in sequence 0, remainder of items start at sequence 1
89 item->setSequenceNumber(item->sequenceNumber() - 1);
90 if (item->command() == MAV_CMD_DO_JUMP) {
91 item->setParam1((int)item->param1() - 1);
92 }
93 }
94 }
95
97}
98
101{
102 qCDebug(PlanManagerLog) << QStringLiteral("_writeMissionCount %1 count:_retryCount").arg(_planTypeString()) << _writeMissionItems.count() << _retryCount;
103
105 if (sharedLink) {
106 mavlink_message_t message;
107
108 mavlink_msg_mission_count_pack_chan(
109 MAVLinkProtocol::instance()->getSystemId(),
111 sharedLink->mavlinkChannel(),
112 &message,
113 _vehicle->id(),
114 MAV_COMP_ID_AUTOPILOT1,
115 _writeMissionItems.count(),
116 _planType,
117 0
118 );
119
120 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
121 }
123}
124
126{
128 return;
129 }
130
131 qCDebug(PlanManagerLog) << QStringLiteral("loadFromVehicle %1 read sequence").arg(_planTypeString());
132
133 if (inProgress()) {
134 qCDebug(PlanManagerLog) << QStringLiteral("loadFromVehicle %1 called while transaction in progress").arg(_planTypeString());
135 return;
136 }
137
138 _retryCount = 0;
139 _setTransactionInProgress(TransactionRead);
141 _requestList();
142}
143
146{
147 qCDebug(PlanManagerLog) << QStringLiteral("_requestList %1 _planType:_retryCount").arg(_planTypeString()) << _planType << _retryCount;
148
149 _itemIndicesToRead.clear();
151
153 if (sharedLink){
154 mavlink_message_t message;
155 mavlink_msg_mission_request_list_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
157 sharedLink->mavlinkChannel(),
158 &message,
159 _vehicle->id(),
160 MAV_COMP_ID_AUTOPILOT1,
161 _planType);
162
163 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
164 }
166}
167
168void PlanManager::_ackTimeout(void)
169{
170 if (_expectedAck == AckNone) {
171 return;
172 }
173
174 switch (_expectedAck) {
175 case AckNone:
176 qCWarning(PlanManagerLog) << QStringLiteral("_ackTimeout %1 timeout with AckNone").arg(_planTypeString());
177 _sendError(InternalError, tr("Internal error occurred during Mission Item communication: _ackTimeOut:_expectedAck == AckNone"));
178 break;
179 case AckMissionCount:
180 // MISSION_COUNT message expected
182 _sendError(MaxRetryExceeded, tr("Mission request list failed, maximum retries exceeded."));
183 _finishTransaction(false);
184 } else {
185 _retryCount++;
186 qCDebug(PlanManagerLog) << tr("Retrying %1 REQUEST_LIST retry Count").arg(_planTypeString()) << _retryCount;
187 _requestList();
188 }
189 break;
190 case AckMissionItem:
191 // MISSION_ITEM expected
193 _sendError(MaxRetryExceeded, tr("Mission read failed, maximum retries exceeded."));
194 _finishTransaction(false);
195 } else {
196 _retryCount++;
197 qCDebug(PlanManagerLog) << tr("Retrying %1 MISSION_REQUEST retry Count").arg(_planTypeString()) << _retryCount;
199 }
200 break;
202 // MISSION_REQUEST is expected, or MISSION_ACK to end sequence
203 if (_itemIndicesToWrite.count() == 0) {
204 // Vehicle did not send final MISSION_ACK at end of sequence
205 _sendError(ProtocolError, tr("Mission write failed, vehicle failed to send final ack."));
206 _finishTransaction(false);
207 } else if (_itemIndicesToWrite[0] == 0) {
208 // Vehicle did not respond to MISSION_COUNT, try again
210 _sendError(MaxRetryExceeded, tr("Mission write mission count failed, maximum retries exceeded."));
211 _finishTransaction(false);
212 } else {
213 _retryCount++;
214 qCDebug(PlanManagerLog) << QStringLiteral("Retrying %1 MISSION_COUNT retry Count").arg(_planTypeString()) << _retryCount;
216 }
217 } else {
218 // Vehicle did not request all items from ground station
219 _sendError(ProtocolError, tr("Vehicle did not request all items from ground station: %1").arg(_ackTypeToString(_expectedAck)));
221 _finishTransaction(false);
222 }
223 break;
225 // MISSION_ACK expected
227 _sendError(MaxRetryExceeded, tr("Mission remove all, maximum retries exceeded."));
228 _finishTransaction(false);
229 } else {
230 _retryCount++;
231 qCDebug(PlanManagerLog) << tr("Retrying %1 MISSION_CLEAR_ALL retry Count").arg(_planTypeString()) << _retryCount;
233 }
234 break;
235 case AckGuidedItem:
236 // MISSION_REQUEST is expected, or MISSION_ACK to end sequence
237 default:
238 _sendError(AckTimeoutError, tr("Vehicle did not respond to mission item communication: %1").arg(_ackTypeToString(_expectedAck)));
240 _finishTransaction(false);
241 }
242}
243
245{
246 // Use much shorter timeouts in unit tests since MockLink responds instantly
247 const int retryTimeout = QGC::runningUnitTests() ? 10 : _retryTimeoutMilliseconds;
249
250 switch (ack) {
251 case AckMissionItem:
252 // We are actively trying to get the mission item, so we don't want to wait as long.
253 _ackTimeoutTimer->setInterval(retryTimeout);
254 break;
255 case AckNone:
256 // FALLTHROUGH
257 case AckMissionCount:
258 // FALLTHROUGH
260 // FALLTHROUGH
262 // FALLTHROUGH
263 case AckGuidedItem:
264 _ackTimeoutTimer->setInterval(ackTimeout);
265 break;
266 }
267
268 _expectedAck = ack;
269 _ackTimeoutTimer->start();
270}
271
275{
276 if (receivedAck == _expectedAck) {
278 _ackTimeoutTimer->stop();
279 return true;
280 } else {
281 if (_expectedAck == AckNone) {
282 // Don't worry about unexpected mission commands, just ignore them; ArduPilot updates home position using
283 // spurious MISSION_ITEMs.
284 } else {
285 // We just warn in this case, this could be crap left over from a previous transaction or the vehicle going bonkers.
286 // Whatever it is we let the ack timeout handle any error output to the user.
287 qCDebug(PlanManagerLog) << QString("Out of sequence ack %1 expected:received %2:%3").arg(_planTypeString()).arg(_ackTypeToString(_expectedAck)).arg(_ackTypeToString(receivedAck));
288 }
289 return false;
290 }
291}
292
294{
295 qCDebug(PlanManagerLog) << "_readTransactionComplete read sequence complete";
296
298 if (sharedLink) {
299 mavlink_message_t message;
300
301 mavlink_msg_mission_ack_pack_chan(
302 MAVLinkProtocol::instance()->getSystemId(),
304 sharedLink->mavlinkChannel(),
305 &message,
306 _vehicle->id(),
307 MAV_COMP_ID_AUTOPILOT1,
308 MAV_MISSION_ACCEPTED,
309 _planType,
310 0
311 );
312
313 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
314 }
315
316 _finishTransaction(true);
317}
318
320{
321 mavlink_mission_count_t missionCount;
322
323 mavlink_msg_mission_count_decode(&message, &missionCount);
324
325 if (missionCount.mission_type != _planType) {
326 // if there was a previous transaction with a different mission_type, it can happen that we receive
327 // a stale message here, for example when the MAV ran into a timeout and sent a message twice
328 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionCount %1 Incorrect mission_type received expected:actual").arg(_planTypeString()) << _planType << missionCount.mission_type;
329 return;
330 }
331
333 return;
334 }
335
336 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionCount %1 count:").arg(_planTypeString()) << missionCount.count;
337
338 _retryCount = 0;
339
340 if (missionCount.count == 0) {
342 } else {
343 // Prime read list
344 for (int i=0; i<missionCount.count; i++) {
346 }
347 _missionItemCountToRead = missionCount.count;
349 }
350}
351
353{
354 if (_itemIndicesToRead.count() == 0) {
355 _sendError(InternalError, tr("Internal Error: Call to Vehicle _requestNextMissionItem with no more indices to read"));
356 return;
357 }
358
359 qCDebug(PlanManagerLog) << QStringLiteral("_requestNextMissionItem %1 sequenceNumber:retry").arg(_planTypeString()) << _itemIndicesToRead[0] << _retryCount;
360
362 if (sharedLink) {
363 mavlink_message_t message;
364
365 mavlink_msg_mission_request_int_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
367 sharedLink->mavlinkChannel(),
368 &message,
369 _vehicle->id(),
370 MAV_COMP_ID_AUTOPILOT1,
372 _planType);
373 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
374 }
376}
377
379{
380 MAV_CMD command;
381 MAV_FRAME frame;
382 MAV_MISSION_TYPE missionType;
383 double param1;
384 double param2;
385 double param3;
386 double param4;
387 double param5;
388 double param6;
389 double param7;
390 bool autoContinue;
391 bool isCurrentItem;
392 int seq;
393
394 mavlink_mission_item_int_t missionItem;
395 mavlink_msg_mission_item_int_decode(&message, &missionItem);
396
397 command = (MAV_CMD)missionItem.command;
398 frame = (MAV_FRAME)missionItem.frame;
399 missionType = (MAV_MISSION_TYPE)missionItem.mission_type;
400 param1 = missionItem.param1;
401 param2 = missionItem.param2;
402 param3 = missionItem.param3;
403 param4 = missionItem.param4;
404 param5 = missionItem.frame == MAV_FRAME_MISSION ? (double)missionItem.x : (double)missionItem.x * 1e-7;
405 param6 = missionItem.frame == MAV_FRAME_MISSION ? (double)missionItem.y : (double)missionItem.y * 1e-7;
406 param7 = (double)missionItem.z;
407 autoContinue = missionItem.autocontinue;
408 isCurrentItem = missionItem.current;
409 seq = missionItem.seq;
410
411 // Check the mission_type field. It can happen that we receive a late duplicate message for a
412 // different mission_type request.
413 if (missionType != _planType) {
414 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionItem %1 dropping spurious item seq:command:missionType").arg(_planTypeString()) << seq << command << missionType;
415 return;
416 }
417
418 // We don't support editing ALT_INT frames so change on the way in.
419 if (frame == MAV_FRAME_GLOBAL_INT) {
420 frame = MAV_FRAME_GLOBAL;
421 } else if (frame == MAV_FRAME_GLOBAL_RELATIVE_ALT_INT) {
422 frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
423 } else if (frame == MAV_FRAME_GLOBAL_TERRAIN_ALT_INT) {
424 frame = MAV_FRAME_GLOBAL_TERRAIN_ALT;
425 }
426
427 bool ardupilotHomePositionUpdate = false;
429 if (_vehicle->apmFirmware() && seq == 0 && _planType == MAV_MISSION_TYPE_MISSION) {
430 ardupilotHomePositionUpdate = true;
431 } else {
432 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionItem %1 dropping spurious item seq:command:current").arg(_planTypeString()) << seq << command << isCurrentItem;
433 return;
434 }
435 }
436
437 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionItem %1 seq:command:current:ardupilotHomePositionUpdate").arg(_planTypeString()) << seq << command << isCurrentItem << ardupilotHomePositionUpdate;
438
439 if (ardupilotHomePositionUpdate) {
440 QGeoCoordinate newHomePosition(param5, param6, param7);
441 _vehicle->_setHomePosition(newHomePosition);
442 return;
443 }
444
445 if (_itemIndicesToRead.contains(seq)) {
446 _itemIndicesToRead.removeOne(seq);
447
448 MissionItem* item = new MissionItem(seq,
449 command,
450 frame,
451 param1,
452 param2,
453 param3,
454 param4,
455 param5,
456 param6,
457 param7,
458 autoContinue,
459 isCurrentItem,
460 this);
461
462 if (item->command() == MAV_CMD_DO_JUMP && !_vehicle->firmwarePlugin()->sendHomePositionToVehicle()) {
463 // Home is in position 0
464 item->setParam1((int)item->param1() + 1);
465 }
466
467 _missionItems.append(item);
468 } else {
469 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionItem %1 mission item received item index which was not requested, disregrarding:").arg(_planTypeString()) << seq;
470 // We have to put the ack timeout back since it was removed above
472 return;
473 }
474
475 emit progressPctChanged((double)seq / (double)_missionItemCountToRead);
476
477 _retryCount = 0;
478 if (_itemIndicesToRead.count() == 0) {
480 } else {
482 }
483}
484
490
492{
493 MAV_MISSION_TYPE missionRequestMissionType;
494 uint16_t missionRequestSeq;
495
496 mavlink_mission_request_int_t missionRequest;
497 mavlink_msg_mission_request_int_decode(&message, &missionRequest);
498 missionRequestMissionType = static_cast<MAV_MISSION_TYPE>(missionRequest.mission_type);
499 missionRequestSeq = missionRequest.seq;
500
501 if (missionRequestMissionType != _planType) {
502 // if there was a previous transaction with a different mission_type, it can happen that we receive
503 // a stale message here, for example when the MAV ran into a timeout and sent a message twice
504 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionRequest %1 Incorrect mission_type received expected:actual").arg(_planTypeString()) << _planType << missionRequestMissionType;
505 return;
506 }
507
509 return;
510 }
511
512 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionRequest %1 sequenceNumber").arg(_planTypeString()) << missionRequestSeq;
513
514 if (missionRequestSeq > _writeMissionItems.count() - 1) {
515 _sendError(RequestRangeError, tr("Vehicle requested item outside range, count:request %1:%2. Send to Vehicle failed.").arg(_writeMissionItems.count()).arg(missionRequestSeq));
516 _finishTransaction(false);
517 return;
518 }
519
520 emit progressPctChanged((double)missionRequestSeq / (double)_writeMissionItems.count());
521
522 _lastMissionRequest = missionRequestSeq;
523 if (!_itemIndicesToWrite.contains(missionRequestSeq)) {
524 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionRequest %1 sequence number requested which has already been sent, sending again:").arg(_planTypeString()) << missionRequestSeq;
525 } else {
526 _itemIndicesToWrite.removeOne(missionRequestSeq);
527 }
528
529 MissionItem* item = _writeMissionItems[missionRequestSeq];
530 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionRequest %1 sequenceNumber:command").arg(_planTypeString()) << missionRequestSeq << item->command();
531
533 if (sharedLink) {
534 mavlink_message_t messageOut;
535
536 mavlink_msg_mission_item_int_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
538 sharedLink->mavlinkChannel(),
539 &messageOut,
540 _vehicle->id(),
541 MAV_COMP_ID_AUTOPILOT1,
542 missionRequestSeq,
543 item->frame(),
544 item->command(),
545 missionRequestSeq == 0,
546 item->autoContinue(),
547 item->param1(),
548 item->param2(),
549 item->param3(),
550 item->param4(),
551 item->frame() == MAV_FRAME_MISSION ? item->param5() : item->param5() * 1e7,
552 item->frame() == MAV_FRAME_MISSION ? item->param6() : item->param6() * 1e7,
553 item->param7(),
554 _planType);
555 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), messageOut);
556 }
558}
559
561{
562 mavlink_mission_ack_t missionAck;
563
564 mavlink_msg_mission_ack_decode(&message, &missionAck);
565 if (missionAck.mission_type != _planType) {
566 // if there was a previous transaction with a different mission_type, it can happen that we receive
567 // a stale message here, for example when the MAV ran into a timeout and sent a message twice
568 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionAck %1 Incorrect mission_type received expected:actual").arg(_planTypeString()) << _planType << missionAck.mission_type;
569 return;
570 }
571
572 if (_vehicle->apmFirmware() && missionAck.type == MAV_MISSION_INVALID_SEQUENCE) {
573 // ArduPilot sends these Acks which can happen just due to noisy links causing duplicated requests being responded to.
574 // As far as I'm concerned this is incorrect protocol implementation but we need to deal with it anyway. So we just
575 // ignore it and if things really go haywire the timeouts will fire to fail the overall transaction.
576 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionAck ArduPilot sending possibly bogus MAV_MISSION_INVALID_SEQUENCE").arg(_planTypeString()) << _planType;
577 return;
578 }
579
580 // Save the retry ack before calling _checkForExpectedAck since we'll need it to determine what
581 // type of a protocol sequence we are in.
582 AckType_t savedExpectedAck = _expectedAck;
583
584 // We can get a MISSION_ACK with an error at any time, so if the Acks don't match it is not
585 // a protocol sequence error. Call _checkForExpectedAck with _retryAck so it will succeed no
586 // matter what.
588 return;
589 }
590
591 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionAck %1 type:").arg(_planTypeString()) << _missionResultToString((MAV_MISSION_RESULT)missionAck.type);
592
593 switch (savedExpectedAck) {
594 case AckNone:
595 // State machine is idle. Vehicle is confused.
596 qCDebug(PlanManagerLog) << QStringLiteral("Vehicle sent unexpected MISSION_ACK message, error: %1").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type));
597 break;
598 case AckMissionCount:
599 // MISSION_COUNT message expected
600 // FIXME: Protocol error
601 _sendError(VehicleAckError, _missionResultToString((MAV_MISSION_RESULT)missionAck.type));
602 _finishTransaction(false);
603 break;
604 case AckMissionItem:
605 // MISSION_ITEM expected
606 // FIXME: Protocol error
607 _sendError(VehicleAckError, _missionResultToString((MAV_MISSION_RESULT)missionAck.type));
608 _finishTransaction(false);
609 break;
611 // MISSION_REQUEST is expected, or MAV_MISSION_ACCEPTED to end sequence
612 if (missionAck.type == MAV_MISSION_ACCEPTED) {
613 if (_itemIndicesToWrite.count() == 0) {
614 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionAck write sequence complete %1").arg(_planTypeString());
615 _finishTransaction(true);
616 } else {
617 // FIXME: Protocol error
618 _sendError(VehicleAckError, _missionResultToString((MAV_MISSION_RESULT)missionAck.type));
619 _finishTransaction(false);
620 }
621 } else {
622 _sendError(VehicleAckError, _missionResultToString((MAV_MISSION_RESULT)missionAck.type));
623 _finishTransaction(false);
624 }
625 break;
627 // MAV_MISSION_ACCEPTED expected
628 if (missionAck.type != MAV_MISSION_ACCEPTED) {
629 _sendError(VehicleAckError, tr("Vehicle remove all failed. Error: %1").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type)));
630 }
631 _finishTransaction(missionAck.type == MAV_MISSION_ACCEPTED);
632 break;
633 case AckGuidedItem:
634 // MISSION_REQUEST is expected, or MAV_MISSION_ACCEPTED to end sequence
635 if (missionAck.type == MAV_MISSION_ACCEPTED) {
636 qCDebug(PlanManagerLog) << QStringLiteral("_handleMissionAck %1 guided mode item accepted").arg(_planTypeString());
637 _finishTransaction(true, true /* apmGuidedItemWrite */);
638 } else {
639 // FIXME: Protocol error
640 _sendError(VehicleAckError, tr("Vehicle returned error: %1. %2Vehicle did not accept guided item.").arg(_missionResultToString((MAV_MISSION_RESULT)missionAck.type)));
641 _finishTransaction(false, true /* apmGuidedItemWrite */);
642 }
643 break;
644 }
645}
646
648void PlanManager::_mavlinkMessageReceived(const mavlink_message_t& message)
649{
650 switch (message.msgid) {
651 case MAVLINK_MSG_ID_MISSION_COUNT:
652 _handleMissionCount(message);
653 break;
654
655 case MAVLINK_MSG_ID_MISSION_ITEM_INT:
656 _handleMissionItem(message);
657 break;
658
659 case MAVLINK_MSG_ID_MISSION_REQUEST:
660 case MAVLINK_MSG_ID_MISSION_REQUEST_INT:
661 _handleMissionRequest(message);
662 break;
663
664 case MAVLINK_MSG_ID_MISSION_ACK:
665 _handleMissionAck(message);
666 break;
667 }
668}
669
670void PlanManager::_sendError(ErrorCode_t errorCode, const QString& errorMsg)
671{
672 qCDebug(PlanManagerLog) << QStringLiteral("Sending error - _planTypeString(%1) errorCode(%2) errorMsg(%4)").arg(_planTypeString()).arg(errorCode).arg(errorMsg);
673
674 emit error(errorCode, errorMsg);
675}
676
678{
679 switch (ackType) {
680 case AckNone:
681 return QString("No Ack");
682 case AckMissionCount:
683 return QString("MISSION_COUNT");
684 case AckMissionItem:
685 return QString("MISSION_ITEM");
687 return QString("MISSION_REQUEST");
688 case AckGuidedItem:
689 return QString("Guided Mode Item");
690 default:
691 qCWarning(PlanManagerLog) << QStringLiteral("Fell off end of switch statement %1").arg(_planTypeString());
692 return QString("QGC Internal Error");
693 }
694}
695
696QString PlanManager::_lastMissionReqestString(MAV_MISSION_RESULT result)
697{
698 QString prefix;
699 QString postfix;
700
703
704 prefix = tr("Item #%1 Command: %2").arg(_lastMissionRequest).arg(MissionCommandTree::instance()->friendlyName(item->command()));
705
706 switch (result) {
707 case MAV_MISSION_UNSUPPORTED_FRAME:
708 postfix = tr("Frame: %1").arg(item->frame());
709 break;
710 case MAV_MISSION_UNSUPPORTED:
711 // All we need is the prefix
712 break;
713 case MAV_MISSION_INVALID_PARAM1:
714 postfix = tr("Value: %1").arg(item->param1());
715 break;
716 case MAV_MISSION_INVALID_PARAM2:
717 postfix = tr("Value: %1").arg(item->param2());
718 break;
719 case MAV_MISSION_INVALID_PARAM3:
720 postfix = tr("Value: %1").arg(item->param3());
721 break;
722 case MAV_MISSION_INVALID_PARAM4:
723 postfix = tr("Value: %1").arg(item->param4());
724 break;
725 case MAV_MISSION_INVALID_PARAM5_X:
726 postfix = tr("Value: %1").arg(item->param5());
727 break;
728 case MAV_MISSION_INVALID_PARAM6_Y:
729 postfix = tr("Value: %1").arg(item->param6());
730 break;
731 case MAV_MISSION_INVALID_PARAM7:
732 postfix = tr("Value: %1").arg(item->param7());
733 break;
734 case MAV_MISSION_INVALID_SEQUENCE:
735 // All we need is the prefix
736 break;
737 default:
738 break;
739 }
740 }
741
742 return prefix + (postfix.isEmpty() ? QStringLiteral("") : QStringLiteral(" ")) + postfix;
743}
744
745QString PlanManager::_missionResultToString(MAV_MISSION_RESULT result)
746{
747 QString error;
748
749 switch (result) {
750 case MAV_MISSION_ACCEPTED:
751 error = tr("Mission accepted.");
752 break;
753 case MAV_MISSION_ERROR:
754 error = tr("Unspecified error.");
755 break;
756 case MAV_MISSION_UNSUPPORTED_FRAME:
757 error = tr("Coordinate frame is not supported.");
758 break;
759 case MAV_MISSION_UNSUPPORTED:
760 error = tr("Command is not supported.");
761 break;
762 case MAV_MISSION_NO_SPACE:
763 error = tr("Mission item exceeds storage space.");
764 break;
765 case MAV_MISSION_INVALID:
766 error = tr("One of the parameters has an invalid value.");
767 break;
768 case MAV_MISSION_INVALID_PARAM1:
769 error = tr("Param 1 invalid value.");
770 break;
771 case MAV_MISSION_INVALID_PARAM2:
772 error = tr("Param 2 invalid value.");
773 break;
774 case MAV_MISSION_INVALID_PARAM3:
775 error = tr("Param 3 invalid value.");
776 break;
777 case MAV_MISSION_INVALID_PARAM4:
778 error = tr("Param 4 invalid value.");
779 break;
780 case MAV_MISSION_INVALID_PARAM5_X:
781 error = tr("Param 5 invalid value.");
782 break;
783 case MAV_MISSION_INVALID_PARAM6_Y:
784 error = tr("Param 6 invalid value.");
785 break;
786 case MAV_MISSION_INVALID_PARAM7:
787 error = tr("Param 7 invalid value.");
788 break;
789 case MAV_MISSION_INVALID_SEQUENCE:
790 error = tr("Received mission item out of sequence.");
791 break;
792 case MAV_MISSION_DENIED:
793 error = tr("Not accepting any mission commands.");
794 break;
795 default:
796 qCWarning(PlanManagerLog) << QStringLiteral("Fell off end of switch statement %1 %2").arg(_planTypeString()).arg(result);
797 error = tr("Unknown error: %1.").arg(result);
798 break;
799 }
800
801 QString lastRequestString = _lastMissionReqestString(result);
802 if (!lastRequestString.isEmpty()) {
803 error += QStringLiteral(" ") + lastRequestString;
804 }
805
806 return error;
807}
808
809void PlanManager::_finishTransaction(bool success, bool apmGuidedItemWrite)
810{
811 emit progressPctChanged(1);
813
814 _itemIndicesToRead.clear();
815 _itemIndicesToWrite.clear();
816
817 // First thing we do is clear the transaction. This way inProgesss is off when we signal transaction complete.
818 TransactionType_t currentTransactionType = _transactionInProgress;
819 _setTransactionInProgress(TransactionNone);
820
821 switch (currentTransactionType) {
822 case TransactionRead:
823 if (!success) {
824 // Read from vehicle failed, clear partial list
826 }
827 emit newMissionItemsAvailable(false);
828 break;
829 case TransactionWrite:
830 // No need to do anything for ArduPilot guided go to waypoint write
831 if (!apmGuidedItemWrite) {
832 if (success) {
833 // Write succeeded, update internal list to be current
834 if (_planType == MAV_MISSION_TYPE_MISSION) {
837 emit currentIndexChanged(-1);
839 }
841 for (int i=0; i<_writeMissionItems.count(); i++) {
843 }
844 _writeMissionItems.clear();
845 } else {
846 // Write failed, throw out the write list
848 }
849 emit sendComplete(!success /* error */);
850 }
851 break;
853 emit removeAllComplete(!success /* error */);
854 break;
855 default:
856 break;
857 }
858
859 if (_resumeMission) {
860 _resumeMission = false;
861 if (success) {
862 emit resumeMissionReady();
863 } else {
865 }
866 }
867}
868
870{
872}
873
875{
876 qCDebug(PlanManagerLog) << "_removeAllWorker";
877
878 emit progressPctChanged(0);
879
881
883 if (sharedLink) {
884 mavlink_message_t message;
885
886 mavlink_msg_mission_clear_all_pack_chan(MAVLinkProtocol::instance()->getSystemId(),
888 sharedLink->mavlinkChannel(),
889 &message,
890 _vehicle->id(),
891 MAV_COMP_ID_AUTOPILOT1,
892 _planType);
893 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message);
894 }
896}
897
899{
900 if (inProgress()) {
901 return;
902 }
903
904 qCDebug(PlanManagerLog) << QStringLiteral("removeAll %1").arg(_planTypeString());
905
907
908 if (_planType == MAV_MISSION_TYPE_MISSION) {
911 emit currentIndexChanged(-1);
913 }
914
915 _retryCount = 0;
916 _setTransactionInProgress(TransactionRemoveAll);
917
919}
920
922{
923 for (int i=0; i<_missionItems.count(); i++) {
924 // Using deleteLater here causes too much transient memory to stack up
925 delete _missionItems[i];
926 }
927 _missionItems.clear();
928}
929
930
932{
933 for (int i=0; i<_writeMissionItems.count(); i++) {
934 // Using deleteLater here causes too much transient memory to stack up
935 delete _writeMissionItems[i];
936 }
937 _writeMissionItems.clear();
938}
939
941{
942 connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &PlanManager::_mavlinkMessageReceived);
943}
944
946{
947 disconnect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &PlanManager::_mavlinkMessageReceived);
948}
949
951{
952 switch (_planType) {
953 case MAV_MISSION_TYPE_MISSION:
954 return QStringLiteral("T:Mission");
955 case MAV_MISSION_TYPE_FENCE:
956 return QStringLiteral("T:GeoFence");
957 case MAV_MISSION_TYPE_RALLY:
958 return QStringLiteral("T:Rally");
959 default:
960 qWarning() << "Unknown plan type" << _planType;
961 return QStringLiteral("T:Unknown");
962 }
963}
964
965void PlanManager::_setTransactionInProgress(TransactionType_t type)
966{
967 if (_transactionInProgress != type) {
968 qCDebug(PlanManagerLog) << "_setTransactionInProgress" << _planTypeString() << type;
971 }
972}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
Error error
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
virtual bool sendHomePositionToVehicle() const
static int getComponentId()
static MAVLinkProtocol * instance()
static MissionCommandTree * instance()
MAV_CMD command(void) const
Definition MissionItem.h:49
double param5(void) const
Definition MissionItem.h:58
double param7(void) const
Definition MissionItem.h:60
void setParam1(double param1)
MAV_FRAME frame(void) const
Definition MissionItem.h:52
void setIsCurrentItem(bool isCurrentItem)
bool autoContinue(void) const
Definition MissionItem.h:53
double param3(void) const
Definition MissionItem.h:56
double param6(void) const
Definition MissionItem.h:59
double param1(void) const
Definition MissionItem.h:54
int sequenceNumber(void) const
Definition MissionItem.h:51
double param4(void) const
Definition MissionItem.h:57
void setSequenceNumber(int sequenceNumber)
double param2(void) const
Definition MissionItem.h:55
The PlanManager class is the base class for the Mission, GeoFence and Rally Point managers....
Definition PlanManager.h:14
int _currentMissionIndex
bool _resumeMission
QList< MissionItem * > _missionItems
Set of mission items on vehicle.
void resumeMissionUploadFail(void)
QList< int > _itemIndicesToRead
List of mission items which still need to be requested from vehicle.
int _lastMissionRequest
Index of item last requested by MISSION_REQUEST.
TransactionType_t _transactionInProgress
bool _checkForExpectedAck(AckType_t receivedAck)
void _handleMissionRequest(const mavlink_message_t &message)
void currentIndexChanged(int currentIndex)
void lastCurrentIndexChanged(int lastCurrentIndex)
void sendComplete(bool error)
void _handleMissionItem(const mavlink_message_t &message)
void resumeMissionReady(void)
void removeAll(void)
void _clearAndDeleteWriteMissionItems(void)
QTimer * _ackTimeoutTimer
ErrorCode_t
Error codes returned in error signal.
Definition PlanManager.h:45
@ AckTimeoutError
Timed out waiting for response from vehicle.
Definition PlanManager.h:47
@ MaxRetryExceeded
Retry failed.
Definition PlanManager.h:53
@ ProtocolError
Incorrect protocol sequence from vehicle.
Definition PlanManager.h:48
@ RequestRangeError
Vehicle requested item out of range.
Definition PlanManager.h:49
@ VehicleAckError
Vehicle returned error in ack.
Definition PlanManager.h:51
int _missionItemCountToRead
Count of all mission items to read.
static constexpr int kTestAckTimeoutMs
Ack timeout used in unit tests (much shorter for faster tests)
Definition PlanManager.h:65
int _lastCurrentIndex
void newMissionItemsAvailable(bool removeAllRequested)
MAV_MISSION_TYPE _planType
QString _missionResultToString(MAV_MISSION_RESULT result)
QString _lastMissionReqestString(MAV_MISSION_RESULT result)
static constexpr int _maxRetryCount
Definition PlanManager.h:62
void _finishTransaction(bool success, bool apmGuidedItemWrite=false)
void _requestList(void)
Internal call to request list of mission items. May be called during a retry sequence.
void _writeMissionItemsWorker(void)
void _connectToMavlink(void)
AckType_t _expectedAck
static constexpr int _ackTimeoutMilliseconds
Definition PlanManager.h:59
void _startAckTimeout(AckType_t ack)
void loadFromVehicle(void)
void _sendError(ErrorCode_t errorCode, const QString &errorMsg)
void removeAllComplete(bool error)
void writeMissionItems(const QList< MissionItem * > &missionItems)
QList< int > _itemIndicesToWrite
List of mission items which still need to be written to vehicle.
void _handleMissionAck(const mavlink_message_t &message)
void _removeAllWorker(void)
@ TransactionRemoveAll
Definition PlanManager.h:97
void _clearMissionItems(void)
bool inProgress(void) const
void inProgressChanged(bool inProgress)
QList< MissionItem * > _writeMissionItems
Set of mission items currently being written to vehicle.
void progressPctChanged(double progressPercentPct)
void _clearAndDeleteMissionItems(void)
@ AckMissionItem
MISSION_ITEM expected.
Definition PlanManager.h:87
@ AckMissionRequest
MISSION_REQUEST is expected, or MISSION_ACK to end sequence.
Definition PlanManager.h:88
@ AckMissionCount
MISSION_COUNT message expected.
Definition PlanManager.h:86
@ AckMissionClearAll
MISSION_CLEAR_ALL sent, MISSION_ACK is expected.
Definition PlanManager.h:89
@ AckNone
State machine is idle.
Definition PlanManager.h:85
@ AckGuidedItem
MISSION_ACK expected in response to ArduPilot guided mode single item send.
Definition PlanManager.h:90
QString _planTypeString(void)
static constexpr int _retryTimeoutMilliseconds
Definition PlanManager.h:61
const QList< MissionItem * > & missionItems(void)
Definition PlanManager.h:22
void _requestNextMissionItem(void)
void _writeMissionCount(void)
This begins the write sequence with the vehicle. This may be called during a retry.
QString _ackTypeToString(AckType_t ackType)
void _handleMissionCount(const mavlink_message_t &message)
void _disconnectFromMavlink(void)
Vehicle * _vehicle
void _readTransactionComplete(void)
WeakLinkInterfacePtr primaryLink() const
void _setHomePosition(QGeoCoordinate &homeCoord)
Definition Vehicle.cc:1199
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:597
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:462
bool isOfflineEditingVehicle() const
Definition Vehicle.h:532
int id() const
Definition Vehicle.h:443
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1401
void mavlinkMessageReceived(const mavlink_message_t &message)
bool apmFirmware() const
Definition Vehicle.h:513
bool runningUnitTests()