3#include <QtCore/QTimer>
26 _responseCheckTimer.setSingleShot(
false);
27 _responseCheckTimer.setInterval(_responseCheckIntervalMSecs());
28 _responseCheckTimer.start();
29 connect(&_responseCheckTimer, &QTimer::timeout,
this, &MavCommandQueue::_responseTimeoutCheck);
34 _responseCheckTimer.stop();
35 _responseCheckTimer.disconnect();
40 qCDebug(MavCommandQueueLog) <<
"stop - clearing pending commands and halting response timer";
42 _responseCheckTimer.stop();
43 _responseCheckTimer.disconnect();
47void MavCommandQueue::sendCommand(
int compId, MAV_CMD command,
bool showError,
float param1,
float param2,
float param3,
float param4,
float param5,
float param6,
float param7)
55 param1, param2, param3, param4, param5, param6, param7);
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)
60 QTimer::singleShot(milliseconds,
this, [=,
this] {
61 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
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)
73 param1, param2, param3, param4, param5, param6, param7);
84 param1, param2, param3, param4, param5, param6, param7);
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)
95 param1, param2, param3, param4, param5, param6, param7);
100struct LambdaFallbackHandlerData {
103 std::function<void()> unsupportedLambda;
108 auto* data =
static_cast<LambdaFallbackHandlerData*
>(resultHandlerData);
111 switch (ack.result) {
112 case MAV_RESULT_ACCEPTED:
118 case MAV_RESULT_UNSUPPORTED:
120 instanceData->setCommandSupported(MAV_CMD(ack.command),
123 data->unsupportedLambda();
126 if (data->showError) {
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)
149 sendCommandInt(compId, command, frame, showError, param1, param2, param3, param4, param5, param6,
152 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
156 auto* data =
new LambdaFallbackHandlerData{_vehicle, showError, std::move(lambda)};
158 &lambdaFallbackResultHandler,
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)
177 sendCommandWithLambdaFallbackWorker(std::move(lambda),
false , compId, command, MAV_FRAME_GLOBAL, showError, param1, param2, param3, param4, param5, param6, param7);
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)
182 sendCommandWithLambdaFallbackWorker(std::move(lambda),
true , compId, command, frame, showError, param1, param2, param3, param4, param5, param6, param7);
192 for (
int i = 0; i < _list.count(); i++) {
193 const MavCommandListEntry_t& entry = _list[i];
194 if (entry.targetCompId == targetCompId && entry.command == command) {
201int MavCommandQueue::_responseCheckIntervalMSecs()
207int MavCommandQueue::_ackTimeoutMSecs()
209 return _testAckTimeoutOverride.value_or(_ackTimeoutMSecsDefault);
215 qCWarning(MavCommandQueueLog) <<
"setTestAckTimeoutOverride called outside unit tests, ignoring";
218 if (msecs.has_value() && *msecs <= 0) {
219 qCWarning(MavCommandQueueLog) <<
"setTestAckTimeoutOverride rejecting non-positive timeout:" << *msecs;
222 _testAckTimeoutOverride = msecs;
227 return _testAckTimeoutOverride;
230bool MavCommandQueue::_shouldRetry(MAV_CMD command)
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:
261bool MavCommandQueue::_canBeDuplicated(MAV_CMD command)
267 case MAV_CMD_DO_MOTOR_TEST:
268 case MAV_CMD_SET_MESSAGE_INTERVAL:
275QString MavCommandQueue::_formatCommand(MAV_CMD command,
float param1)
279 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral(
"%1 (%2)").arg(friendlyName, rawName);
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));
284 commandStr += QStringLiteral(
" [%1]").arg(info->name);
293 int targetCompId, MAV_CMD command, MAV_FRAME frame,
294 float param1,
float param2,
float param3,
float param4,
double param5,
double param6,
float param7)
304 if ((targetCompId == MAV_COMP_ID_ALL) || (
isPending(targetCompId, command) && !_canBeDuplicated(command))) {
305 bool compIdAll = targetCompId == MAV_COMP_ID_ALL;
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;
313 ack.result = MAV_RESULT_FAILED;
316 emit
commandResult(_vehicle->
id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
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.")));
326 qCDebug(MavCommandQueueLog) <<
"sendWorker: primary link gone!";
331 ack.command = command;
332 ack.result = MAV_RESULT_FAILED;
335 emit
commandResult(_vehicle->
id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
344 MavCommandListEntry_t entry;
345 entry.useCommandInt = commandInt;
346 entry.targetCompId = targetCompId;
347 entry.command = command;
349 entry.showError = showError;
350 entry.ackHandlerInfo = {};
351 if (ackHandlerInfo) {
352 entry.ackHandlerInfo = *ackHandlerInfo;
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;
362 entry.ackTimeoutMSecs = sharedLink->linkConfiguration()->isHighLatency() ? _ackTimeoutMSecsHighLatency : _ackTimeoutMSecs();
363 entry.elapsedTimer.start();
365 qCDebug(MavCommandQueueLog) <<
"Sending" << _formatCommand(command, param1) <<
"param1-7:" << command << param1 << param2 << param3 << param4 << param5 << param6 << param7;
368 _sendFromList(_list.count() - 1);
371void MavCommandQueue::_sendFromList(
int index)
373 MavCommandListEntry_t commandEntry = _list[index];
378 if (++_list[index].tryCount > commandEntry.maxTries) {
379 QString logMsg = QStringLiteral(
"Giving up sending command after max retries: %1").arg(rawCommandName);
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));
388 qCWarning(MavCommandQueueLog) << logMsg;
390 _list.removeAt(index);
391 if (commandEntry.ackHandlerInfo.resultHandler) {
393 ack.result = MAV_RESULT_FAILED;
398 if (commandEntry.showError) {
404 if (commandEntry.tryCount > 1 && !_vehicle->
px4Firmware() && commandEntry.command == MAV_CMD_START_RX_PAIR) {
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;
415 qCDebug(MavCommandQueueLog) <<
"_sendFromList: primary link gone!";
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;
437 sharedLink->mavlinkChannel(),
442 memset(&cmd, 0,
sizeof(cmd));
443 cmd.target_system = _vehicle->
id();
444 cmd.target_component = commandEntry.targetCompId;
445 cmd.command = commandEntry.command;
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;
457 sharedLink->mavlinkChannel(),
465void MavCommandQueue::_responseTimeoutCheck()
467 if (_list.isEmpty()) {
475 for (
int i = _list.count() - 1; i >= 0; i--) {
479 if (i >= _list.count()) {
483 MavCommandListEntry_t& commandEntry = _list[i];
484 if (commandEntry.elapsedTimer.elapsed() > commandEntry.ackTimeoutMSecs) {
495 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral(
"%1 (%2)").arg(friendlyName, rawName);
497 switch (ack.result) {
498 case MAV_RESULT_TEMPORARILY_REJECTED:
501 case MAV_RESULT_DENIED:
504 case MAV_RESULT_UNSUPPORTED:
507 case MAV_RESULT_FAILED:
518 int entryIndex =
findEntryIndex(message.compid,
static_cast<MAV_CMD
>(ack.command));
519 if (entryIndex == -1) {
521 qCDebug(MavCommandQueueLog) <<
"handleCommandAck Ack not in list" << rawCommandName;
525 if (ack.result == MAV_RESULT_IN_PROGRESS) {
526 MavCommandListEntry_t commandEntry;
527 if (_vehicle->
px4Firmware() && ack.command == MAV_CMD_DO_AUTOTUNE_ENABLE) {
529 commandEntry = _list.takeAt(entryIndex);
532 MavCommandListEntry_t& commandEntryRef = _list[entryIndex];
533 commandEntryRef.maxTries = 1;
534 commandEntryRef.elapsedTimer.start();
535 commandEntry = commandEntryRef;
538 if (commandEntry.ackHandlerInfo.progressHandler) {
539 (*commandEntry.ackHandlerInfo.progressHandler)(commandEntry.ackHandlerInfo.progressHandlerData, message.compid, ack);
544 MavCommandListEntry_t commandEntry = _list.takeAt(entryIndex);
545 if (commandEntry.ackHandlerInfo.resultHandler) {
546 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, message.compid, ack,
MavCmdResultCommandResultOnly);
548 if (commandEntry.showError) {
557 switch (failureCode) {
559 return QStringLiteral(
"Command Result Only");
561 return QStringLiteral(
"No Response To Command");
563 return QStringLiteral(
"Duplicate Command");
565 return QStringLiteral(
"Unknown (%1)").arg(failureCode);
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.
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_ACCEPTED
static constexpr MAV_CMD MAV_CMD_MOCKLINK_SECOND_ATTEMPT_RESULT_ACCEPTED
static constexpr MAV_CMD MAV_CMD_MOCKLINK_ALWAYS_RESULT_FAILED
static constexpr MAV_CMD MAV_CMD_MOCKLINK_SECOND_ATTEMPT_RESULT_FAILED
static constexpr MAV_CMD MAV_CMD_MOCKLINK_NO_RESPONSE
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
class FirmwarePluginInstanceData * firmwarePluginInstanceData()
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultHandler resultHandler
nullptr for no handler
struct VehicleTypes::MavCmdAckHandlerInfo_s MavCmdAckHandlerInfo_t
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultFailureCode_t
@ 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.