QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MavCommandQueue.cc
Go to the documentation of this file.
1#include "MavCommandQueue.h"
2
3#include <QtCore/QTimer>
4
5#include "FirmwarePlugin.h"
6#include "MAVLinkLib.h"
7#include "MAVLinkProtocol.h"
9#include "AppMessages.h"
10#include "QGCLoggingCategory.h"
11#include "Vehicle.h"
12#include "VehicleLinkManager.h"
13
14#ifdef QT_DEBUG
15#include "MockLink.h"
16#endif
17
18QGC_LOGGING_CATEGORY(MavCommandQueueLog, "Vehicle.MavCommandQueue")
19
20std::optional<int> MavCommandQueue::_testAckTimeoutOverride;
21
23 : QObject(vehicle)
24 , _vehicle(vehicle)
25{
26 _responseCheckTimer.setSingleShot(false);
27 _responseCheckTimer.setInterval(_responseCheckIntervalMSecs());
28 _responseCheckTimer.start();
29 connect(&_responseCheckTimer, &QTimer::timeout, this, &MavCommandQueue::_responseTimeoutCheck);
30}
31
33{
34 _responseCheckTimer.stop();
35 _responseCheckTimer.disconnect();
36}
37
39{
40 qCDebug(MavCommandQueueLog) << "stop - clearing pending commands and halting response timer";
41 _stopped = true;
42 _responseCheckTimer.stop();
43 _responseCheckTimer.disconnect();
44 _list.clear();
45}
46
47void MavCommandQueue::sendCommand(int compId, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
48{
49 sendWorker(false, // commandInt
50 showError,
51 nullptr, // no handlers
52 compId,
53 command,
54 MAV_FRAME_GLOBAL,
55 param1, param2, param3, param4, param5, param6, param7);
56}
57
58void MavCommandQueue::sendCommandDelayed(int compId, MAV_CMD command, bool showError, int milliseconds, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
59{
60 QTimer::singleShot(milliseconds, this, [=, this] {
61 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
62 });
63}
64
65void MavCommandQueue::sendCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
66{
67 sendWorker(true, // commandInt
68 showError,
69 nullptr, // no handlers
70 compId,
71 command,
72 frame,
73 param1, param2, param3, param4, param5, param6, param7);
74}
75
76void MavCommandQueue::sendCommandWithHandler(const MavCmdAckHandlerInfo_t* ackHandlerInfo, int compId, MAV_CMD command, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
77{
78 sendWorker(false, // commandInt
79 false, // showError
80 ackHandlerInfo,
81 compId,
82 command,
83 MAV_FRAME_GLOBAL,
84 param1, param2, param3, param4, param5, param6, param7);
85}
86
87void MavCommandQueue::sendCommandIntWithHandler(const MavCmdAckHandlerInfo_t* ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
88{
89 sendWorker(true, // commandInt
90 false, // showError
91 ackHandlerInfo,
92 compId,
93 command,
94 frame,
95 param1, param2, param3, param4, param5, param6, param7);
96}
97
98namespace {
99
100struct LambdaFallbackHandlerData {
101 Vehicle* vehicle;
102 bool showError;
103 std::function<void()> unsupportedLambda;
104};
105
106void lambdaFallbackResultHandler(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, VehicleTypes::MavCmdResultFailureCode_t /*failureCode*/)
107{
108 auto* data = static_cast<LambdaFallbackHandlerData*>(resultHandlerData);
109 auto* instanceData = data->vehicle->firmwarePluginInstanceData();
110
111 switch (ack.result) {
112 case MAV_RESULT_ACCEPTED:
113 if (instanceData) {
114 instanceData->setCommandSupported(MAV_CMD(ack.command),
116 }
117 break;
118 case MAV_RESULT_UNSUPPORTED:
119 if (instanceData) {
120 instanceData->setCommandSupported(MAV_CMD(ack.command),
122 }
123 data->unsupportedLambda();
124 break;
125 default:
126 if (data->showError) {
128 }
129 break;
130 }
131
132 delete data;
133}
134
135} // namespace
136
137void MavCommandQueue::sendCommandWithLambdaFallbackWorker(std::function<void()> lambda, bool commandInt, int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
138{
139 auto* instanceData = _vehicle->firmwarePluginInstanceData();
140 const auto supported = instanceData ? instanceData->getCommandSupported(command)
142
143 switch (supported) {
145 lambda();
146 break;
148 if (commandInt) {
149 sendCommandInt(compId, command, frame, showError, param1, param2, param3, param4, param5, param6,
150 param7);
151 } else {
152 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
153 }
154 break;
156 auto* data = new LambdaFallbackHandlerData{_vehicle, showError, std::move(lambda)};
157 const MavCmdAckHandlerInfo_t handlerInfo{
158 /* .resultHandler = */ &lambdaFallbackResultHandler,
159 /* .resultHandlerData = */ data,
160 /* .progressHandler = */ nullptr,
161 /* .progressHandlerData = */ nullptr,
162 };
163 if (commandInt) {
164 sendCommandIntWithHandler(&handlerInfo, compId, command, frame, param1, param2, param3, param4, param5,
165 param6, param7);
166 } else {
167 sendCommandWithHandler(&handlerInfo, compId, command, param1, param2, param3, param4, param5, param6,
168 param7);
169 }
170 break;
171 }
172 }
173}
174
175void MavCommandQueue::sendCommandWithLambdaFallback(std::function<void()> lambda, int compId, MAV_CMD command, bool showError, float param1, float param2, float param3, float param4, float param5, float param6, float param7)
176{
177 sendCommandWithLambdaFallbackWorker(std::move(lambda), false /* commandInt */, compId, command, MAV_FRAME_GLOBAL, showError, param1, param2, param3, param4, param5, param6, param7);
178}
179
180void MavCommandQueue::sendCommandIntWithLambdaFallback(std::function<void()> lambda, int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
181{
182 sendCommandWithLambdaFallbackWorker(std::move(lambda), true /* commandInt */, compId, command, frame, showError, param1, param2, param3, param4, param5, param6, param7);
183}
184
185bool MavCommandQueue::isPending(int targetCompId, MAV_CMD command) const
186{
187 return findEntryIndex(targetCompId, command) != -1;
188}
189
190int MavCommandQueue::findEntryIndex(int targetCompId, MAV_CMD command) const
191{
192 for (int i = 0; i < _list.count(); i++) {
193 const MavCommandListEntry_t& entry = _list[i];
194 if (entry.targetCompId == targetCompId && entry.command == command) {
195 return i;
196 }
197 }
198 return -1;
199}
200
201int MavCommandQueue::_responseCheckIntervalMSecs()
202{
203 // Use shorter check interval during unit tests for faster test execution
204 return QGC::runningUnitTests() ? 50 : 500;
205}
206
207int MavCommandQueue::_ackTimeoutMSecs()
208{
209 return _testAckTimeoutOverride.value_or(_ackTimeoutMSecsDefault);
210}
211
212void MavCommandQueue::setTestAckTimeoutOverride(std::optional<int> msecs)
213{
214 if (!QGC::runningUnitTests()) {
215 qCWarning(MavCommandQueueLog) << "setTestAckTimeoutOverride called outside unit tests, ignoring";
216 return;
217 }
218 if (msecs.has_value() && *msecs <= 0) {
219 qCWarning(MavCommandQueueLog) << "setTestAckTimeoutOverride rejecting non-positive timeout:" << *msecs;
220 return;
221 }
222 _testAckTimeoutOverride = msecs;
223}
224
226{
227 return _testAckTimeoutOverride;
228}
229
230bool MavCommandQueue::_shouldRetry(MAV_CMD command)
231{
232 switch (command) {
233#ifdef QT_DEBUG
234 // These MockLink commands should be retried so we can create unit tests to test retry code
240 return true;
241#endif
242
243 // In general we should not retry any commands. This is for safety reasons. For example you don't want an ARM command
244 // to timeout with no response over a noisy link twice and then suddenly have the third try work 6 seconds later. At that
245 // point the user could have walked up to the vehicle to see what is going wrong.
246 //
247 // We do retry commands which are part of the initial vehicle connect sequence. This makes this process work better over noisy
248 // links where commands could be lost. Also these commands tend to just be requesting status so if they end up being delayed
249 // there are no safety concerns that could occur.
250 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
251 case MAV_CMD_REQUEST_MESSAGE:
252 case MAV_CMD_PREFLIGHT_STORAGE:
253 case MAV_CMD_RUN_PREARM_CHECKS:
254 return true;
255
256 default:
257 return false;
258 }
259}
260
261bool MavCommandQueue::_canBeDuplicated(MAV_CMD command)
262{
263 // For some commands we don't care about response as much as we care about sending them regularly.
264 // This test avoids commands not being sent due to an ACK not being received yet.
265 // MOTOR_TEST in ardusub is a case where we need a constant stream of commands so it doesn't time out.
266 switch (command) {
267 case MAV_CMD_DO_MOTOR_TEST:
268 case MAV_CMD_SET_MESSAGE_INTERVAL:
269 return true;
270 default:
271 return false;
272 }
273}
274
275QString MavCommandQueue::_formatCommand(MAV_CMD command, float param1)
276{
277 QString rawName = MissionCommandTree::instance()->rawName(command);
278 QString friendlyName = MissionCommandTree::instance()->friendlyName(command);
279 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral("%1 (%2)").arg(friendlyName, rawName);
280
281 if (command == MAV_CMD_REQUEST_MESSAGE || command == MAV_CMD_SET_MESSAGE_INTERVAL) {
282 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(static_cast<int>(param1));
283 if (info) {
284 commandStr += QStringLiteral(" [%1]").arg(info->name);
285 }
286 }
287
288 return commandStr;
289}
290
291void MavCommandQueue::sendWorker(bool commandInt, bool showError,
292 const MavCmdAckHandlerInfo_t* ackHandlerInfo,
293 int targetCompId, MAV_CMD command, MAV_FRAME frame,
294 float param1, float param2, float param3, float param4, double param5, double param6, float param7)
295{
296 if (_stopped) {
297 return;
298 }
299
300 // We can't send commands to compIdAll using this method. The reason being that we would get responses back possibly from multiple components
301 // which this code can't handle.
302 // We also can't send the majority of commands again if we are already waiting for a response from that same command. If we did that we would not be able to discern
303 // which ack was associated with which command.
304 if ((targetCompId == MAV_COMP_ID_ALL) || (isPending(targetCompId, command) && !_canBeDuplicated(command))) {
305 bool compIdAll = targetCompId == MAV_COMP_ID_ALL;
306 QString rawCommandName = MissionCommandTree::instance()->rawName(command);
307
308 qCDebug(MavCommandQueueLog) << QStringLiteral("sendWorker failing %1").arg(compIdAll ? "MAV_COMP_ID_ALL not supported" : "duplicate command") << rawCommandName << param1 << param2 << param3 << param4 << param5 << param6 << param7;
309
311 if (ackHandlerInfo && ackHandlerInfo->resultHandler) {
312 mavlink_command_ack_t ack = {};
313 ack.result = MAV_RESULT_FAILED;
314 (*ackHandlerInfo->resultHandler)(ackHandlerInfo->resultHandlerData, targetCompId, ack, failureCode);
315 } else {
316 emit commandResult(_vehicle->id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
317 }
318 if (showError) {
319 QGC::showAppMessage(tr("Unable to send command: %1.").arg(compIdAll ? tr("Internal error - MAV_COMP_ID_ALL not supported") : tr("Waiting on previous response to same command.")));
320 }
321 return;
322 }
323
324 SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
325 if (!sharedLink) {
326 qCDebug(MavCommandQueueLog) << "sendWorker: primary link gone!";
327
329 if (ackHandlerInfo && ackHandlerInfo->resultHandler) {
330 mavlink_command_ack_t ack = {};
331 ack.command = command;
332 ack.result = MAV_RESULT_FAILED;
333 (*ackHandlerInfo->resultHandler)(ackHandlerInfo->resultHandlerData, targetCompId, ack, failureCode);
334 } else {
335 emit commandResult(_vehicle->id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
336 }
337
338 if (showError) {
339 QGC::showAppMessage(tr("Unable to send command: Vehicle is not connected."));
340 }
341 return;
342 }
343
344 MavCommandListEntry_t entry;
345 entry.useCommandInt = commandInt;
346 entry.targetCompId = targetCompId;
347 entry.command = command;
348 entry.frame = frame;
349 entry.showError = showError;
350 entry.ackHandlerInfo = {};
351 if (ackHandlerInfo) {
352 entry.ackHandlerInfo = *ackHandlerInfo;
353 }
354 entry.rgParam1 = param1;
355 entry.rgParam2 = param2;
356 entry.rgParam3 = param3;
357 entry.rgParam4 = param4;
358 entry.rgParam5 = param5;
359 entry.rgParam6 = param6;
360 entry.rgParam7 = param7;
361 entry.maxTries = _shouldRetry(command) ? kMaxRetryCount : 1;
362 entry.ackTimeoutMSecs = sharedLink->linkConfiguration()->isHighLatency() ? _ackTimeoutMSecsHighLatency : _ackTimeoutMSecs();
363 entry.elapsedTimer.start();
364
365 qCDebug(MavCommandQueueLog) << "Sending" << _formatCommand(command, param1) << "param1-7:" << command << param1 << param2 << param3 << param4 << param5 << param6 << param7;
366
367 _list.append(entry);
368 _sendFromList(_list.count() - 1);
369}
370
371void MavCommandQueue::_sendFromList(int index)
372{
373 MavCommandListEntry_t commandEntry = _list[index];
374
375 QString rawCommandName = MissionCommandTree::instance()->rawName(commandEntry.command);
376 QString friendlyName = MissionCommandTree::instance()->friendlyName(commandEntry.command);
377
378 if (++_list[index].tryCount > commandEntry.maxTries) {
379 QString logMsg = QStringLiteral("Giving up sending command after max retries: %1").arg(rawCommandName);
380
381 // These commands identify their target message in param1.
382 if (commandEntry.command == MAV_CMD_REQUEST_MESSAGE || commandEntry.command == MAV_CMD_SET_MESSAGE_INTERVAL) {
383 int requestedMsgId = static_cast<int>(commandEntry.rgParam1);
384 const mavlink_message_info_t *info = mavlink_get_message_info_by_id(requestedMsgId);
385 logMsg += QStringLiteral(" message: %1").arg(info ? info->name : QString::number(requestedMsgId));
386 }
387
388 qCWarning(MavCommandQueueLog) << logMsg;
389
390 _list.removeAt(index);
391 if (commandEntry.ackHandlerInfo.resultHandler) {
392 mavlink_command_ack_t ack = {};
393 ack.result = MAV_RESULT_FAILED;
394 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, commandEntry.targetCompId, ack, MavCmdResultFailureNoResponseToCommand);
395 } else {
396 emit commandResult(_vehicle->id(), commandEntry.targetCompId, commandEntry.command, MAV_RESULT_FAILED, MavCmdResultFailureNoResponseToCommand);
397 }
398 if (commandEntry.showError) {
399 QGC::showAppMessage(tr("Vehicle did not respond to command: %1").arg(friendlyName));
400 }
401 return;
402 }
403
404 if (commandEntry.tryCount > 1 && !_vehicle->px4Firmware() && commandEntry.command == MAV_CMD_START_RX_PAIR) {
405 // The implementation of this command comes from the IO layer and is shared across stacks. So for other firmwares
406 // we aren't really sure whether they are correct or not.
407 return;
408 }
409
410 qCDebug(MavCommandQueueLog) << "Sending" << _formatCommand(commandEntry.command, commandEntry.rgParam1)
411 << "tryCount:param1-7" << commandEntry.tryCount << commandEntry.rgParam1 << commandEntry.rgParam2 << commandEntry.rgParam3 << commandEntry.rgParam4 << commandEntry.rgParam5 << commandEntry.rgParam6 << commandEntry.rgParam7;
412
413 SharedLinkInterfacePtr sharedLink = _vehicle->vehicleLinkManager()->primaryLink().lock();
414 if (!sharedLink) {
415 qCDebug(MavCommandQueueLog) << "_sendFromList: primary link gone!";
416 return;
417 }
418
420
421 if (commandEntry.useCommandInt) {
422 mavlink_command_int_t cmd;
423 memset(&cmd, 0, sizeof(cmd));
424 cmd.target_system = _vehicle->id();
425 cmd.target_component = commandEntry.targetCompId;
426 cmd.command = commandEntry.command;
427 cmd.frame = commandEntry.frame;
428 cmd.param1 = commandEntry.rgParam1;
429 cmd.param2 = commandEntry.rgParam2;
430 cmd.param3 = commandEntry.rgParam3;
431 cmd.param4 = commandEntry.rgParam4;
432 cmd.x = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam5 : commandEntry.rgParam5 * 1e7;
433 cmd.y = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam6 : commandEntry.rgParam6 * 1e7;
434 cmd.z = commandEntry.rgParam7;
435 mavlink_msg_command_int_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
437 sharedLink->mavlinkChannel(),
438 &msg,
439 &cmd);
440 } else {
442 memset(&cmd, 0, sizeof(cmd));
443 cmd.target_system = _vehicle->id();
444 cmd.target_component = commandEntry.targetCompId;
445 cmd.command = commandEntry.command;
446 // MAVLink spec: confirmation increments on each resend.
447 cmd.confirmation = static_cast<uint8_t>(qMin(commandEntry.tryCount - 1, 255));
448 cmd.param1 = commandEntry.rgParam1;
449 cmd.param2 = commandEntry.rgParam2;
450 cmd.param3 = commandEntry.rgParam3;
451 cmd.param4 = commandEntry.rgParam4;
452 cmd.param5 = static_cast<float>(commandEntry.rgParam5);
453 cmd.param6 = static_cast<float>(commandEntry.rgParam6);
454 cmd.param7 = commandEntry.rgParam7;
455 mavlink_msg_command_long_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
457 sharedLink->mavlinkChannel(),
458 &msg,
459 &cmd);
460 }
461
462 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
463}
464
465void MavCommandQueue::_responseTimeoutCheck()
466{
467 if (_list.isEmpty()) {
468 return;
469 }
470
471 // Walk the list backwards since _sendFromList can remove entries. Give-up result
472 // handlers invoked from _sendFromList can also re-enter the queue and remove or
473 // clear entries (e.g. closing the vehicle from within the callback), so re-validate
474 // the stop flag and index on every iteration.
475 for (int i = _list.count() - 1; i >= 0; i--) {
476 if (_stopped) {
477 return;
478 }
479 if (i >= _list.count()) {
480 i = _list.count(); // Handler removed entries below us, clamp (loop decrement follows)
481 continue;
482 }
483 MavCommandListEntry_t& commandEntry = _list[i];
484 if (commandEntry.elapsedTimer.elapsed() > commandEntry.ackTimeoutMSecs) {
485 // Try sending command again
486 _sendFromList(i);
487 }
488 }
489}
490
492{
493 QString rawName = MissionCommandTree::instance()->rawName(static_cast<MAV_CMD>(ack.command));
494 QString friendlyName = MissionCommandTree::instance()->friendlyName(static_cast<MAV_CMD>(ack.command));
495 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral("%1 (%2)").arg(friendlyName, rawName);
496
497 switch (ack.result) {
498 case MAV_RESULT_TEMPORARILY_REJECTED:
499 QGC::showAppMessage(tr("%1 command temporarily rejected").arg(commandStr));
500 break;
501 case MAV_RESULT_DENIED:
502 QGC::showAppMessage(tr("%1 command denied").arg(commandStr));
503 break;
504 case MAV_RESULT_UNSUPPORTED:
505 QGC::showAppMessage(tr("%1 command not supported").arg(commandStr));
506 break;
507 case MAV_RESULT_FAILED:
508 QGC::showAppMessage(tr("%1 command failed").arg(commandStr));
509 break;
510 default:
511 // Do nothing
512 break;
513 }
514}
515
517{
518 int entryIndex = findEntryIndex(message.compid, static_cast<MAV_CMD>(ack.command));
519 if (entryIndex == -1) {
520 QString rawCommandName = MissionCommandTree::instance()->rawName(static_cast<MAV_CMD>(ack.command));
521 qCDebug(MavCommandQueueLog) << "handleCommandAck Ack not in list" << rawCommandName;
522 return;
523 }
524
525 if (ack.result == MAV_RESULT_IN_PROGRESS) {
526 MavCommandListEntry_t commandEntry;
527 if (_vehicle->px4Firmware() && ack.command == MAV_CMD_DO_AUTOTUNE_ENABLE) {
528 // Hack to support PX4 autotune which does not send final result ack and just sends in progress
529 commandEntry = _list.takeAt(entryIndex);
530 } else {
531 // Command has not completed yet, don't remove
532 MavCommandListEntry_t& commandEntryRef = _list[entryIndex];
533 commandEntryRef.maxTries = 1; // Vehicle responded to command so don't retry
534 commandEntryRef.elapsedTimer.start(); // We've heard from vehicle, restart elapsed timer for no ack received timeout
535 commandEntry = commandEntryRef;
536 }
537
538 if (commandEntry.ackHandlerInfo.progressHandler) {
539 (*commandEntry.ackHandlerInfo.progressHandler)(commandEntry.ackHandlerInfo.progressHandlerData, message.compid, ack);
540 }
541 return;
542 }
543
544 MavCommandListEntry_t commandEntry = _list.takeAt(entryIndex);
545 if (commandEntry.ackHandlerInfo.resultHandler) {
546 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, message.compid, ack, MavCmdResultCommandResultOnly);
547 } else {
548 if (commandEntry.showError) {
550 }
551 emit commandResult(_vehicle->id(), message.compid, ack.command, ack.result, MavCmdResultCommandResultOnly);
552 }
553}
554
556{
557 switch (failureCode) {
559 return QStringLiteral("Command Result Only");
561 return QStringLiteral("No Response To Command");
563 return QStringLiteral("Duplicate Command");
564 default:
565 return QStringLiteral("Unknown (%1)").arg(failureCode);
566 }
567}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
struct __mavlink_command_long_t mavlink_command_long_t
CommandSupportedResult getCommandSupported(MAV_CMD cmd) const
void setCommandSupported(MAV_CMD cmd, CommandSupportedResult status)
static int getComponentId()
static MAVLinkProtocol * instance()
Owns the COMMAND_LONG / COMMAND_INT send/retry/ack pipeline for a single Vehicle.
void sendCommandInt(int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
static QString failureCodeToString(MavCmdResultFailureCode_t failureCode)
void commandResult(int vehicleId, int targetComponent, int command, int ackResult, int failureCode)
Emitted for every terminal ack that has no user-provided resultHandler.
void sendCommandIntWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, double param5=0.0, double param6=0.0, float param7=0.0f)
static std::optional< int > testAckTimeoutOverride()
void sendCommandWithHandler(const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
static constexpr int kMaxRetryCount
static void setTestAckTimeoutOverride(std::optional< int > msecs)
void sendWorker(bool commandInt, bool showError, const MavCmdAckHandlerInfo_t *ackHandlerInfo, int compId, MAV_CMD command, MAV_FRAME frame, float param1, float param2, float param3, float param4, double param5, double param6, float param7)
void handleCommandAck(const mavlink_message_t &message, const mavlink_command_ack_t &ack)
Process a COMMAND_ACK — match it to a pending entry and fire callbacks.
void sendCommandDelayed(int compId, MAV_CMD command, bool showError, int milliseconds, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
bool isPending(int targetCompId, MAV_CMD command) const
True if a matching (targetCompId, command) is already queued or awaiting ack.
static void showCommandAckError(const mavlink_command_ack_t &ack)
void sendCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
int findEntryIndex(int targetCompId, MAV_CMD command) const
Index of a matching entry in the pending queue, or -1. Exposed for test use.
void sendCommandIntWithLambdaFallback(std::function< void()> lambda, int compId, MAV_CMD command, MAV_FRAME frame, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, double param5=0.0, double param6=0.0, float param7=0.0f)
void sendCommandWithLambdaFallback(std::function< void()> lambda, int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
static MissionCommandTree * instance()
QString rawName(MAV_CMD command) const
Returns the raw name for the specified command.
QString friendlyName(MAV_CMD command) const
Returns the friendly name for the specified command.
WeakLinkInterfacePtr primaryLink() const
bool px4Firmware() const
Definition Vehicle.h:512
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:597
int id() const
Definition Vehicle.h:443
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1401
class FirmwarePluginInstanceData * firmwarePluginInstanceData()
Definition Vehicle.h:715
bool runningUnitTests()
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultHandler resultHandler
nullptr for no handler
struct VehicleTypes::MavCmdAckHandlerInfo_s MavCmdAckHandlerInfo_t
Callback info bundle for sendMavCommandWithHandler.
@ MavCmdResultFailureDuplicateCommand
Unable to send command since duplicate is already being waited on for response.
@ MavCmdResultCommandResultOnly
commandResult specifies full success/fail info
@ MavCmdResultFailureNoResponseToCommand
No response from vehicle to command.