3#include <QtCore/QTimer>
24 _responseCheckTimer.setSingleShot(
false);
25 _responseCheckTimer.setInterval(_responseCheckIntervalMSecs());
26 _responseCheckTimer.start();
27 connect(&_responseCheckTimer, &QTimer::timeout,
this, &MavCommandQueue::_responseTimeoutCheck);
32 _responseCheckTimer.stop();
33 _responseCheckTimer.disconnect();
38 qCDebug(MavCommandQueueLog) <<
"stop - clearing pending commands and halting response timer";
40 _responseCheckTimer.stop();
41 _responseCheckTimer.disconnect();
45void MavCommandQueue::sendCommand(
int compId, MAV_CMD command,
bool showError,
float param1,
float param2,
float param3,
float param4,
float param5,
float param6,
float param7)
53 param1, param2, param3, param4, param5, param6, param7);
56void 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)
58 QTimer::singleShot(milliseconds,
this, [=,
this] {
59 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
63void 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)
71 param1, param2, param3, param4, param5, param6, param7);
82 param1, param2, param3, param4, param5, param6, param7);
85void 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)
93 param1, param2, param3, param4, param5, param6, param7);
98struct LambdaFallbackHandlerData {
101 std::function<void()> unsupportedLambda;
106 auto* data =
static_cast<LambdaFallbackHandlerData*
>(resultHandlerData);
109 switch (ack.result) {
110 case MAV_RESULT_ACCEPTED:
113 case MAV_RESULT_UNSUPPORTED:
115 data->unsupportedLambda();
118 if (data->showError) {
129void 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)
133 switch (instanceData->getCommandSupported(command)) {
139 sendCommandInt(compId, command, frame, showError, param1, param2, param3, param4, param5, param6, param7);
141 sendCommand(compId, command, showError, param1, param2, param3, param4, param5, param6, param7);
145 auto* data =
new LambdaFallbackHandlerData { _vehicle, showError, std::move(lambda) };
147 &lambdaFallbackResultHandler,
153 sendCommandIntWithHandler(&handlerInfo, compId, command, frame, param1, param2, param3, param4, param5, param6, param7);
155 sendCommandWithHandler(&handlerInfo, compId, command, param1, param2, param3, param4, param5, param6, param7);
162void 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)
164 sendCommandWithLambdaFallbackWorker(std::move(lambda),
false , compId, command, MAV_FRAME_GLOBAL, showError, param1, param2, param3, param4, param5, param6, param7);
167void 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)
169 sendCommandWithLambdaFallbackWorker(std::move(lambda),
true , compId, command, frame, showError, param1, param2, param3, param4, param5, param6, param7);
179 for (
int i = 0; i < _list.count(); i++) {
180 const MavCommandListEntry_t& entry = _list[i];
181 if (entry.targetCompId == targetCompId && entry.command == command) {
188int MavCommandQueue::_responseCheckIntervalMSecs()
194int MavCommandQueue::_ackTimeoutMSecs()
200bool MavCommandQueue::_shouldRetry(MAV_CMD command)
220 case MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES:
221 case MAV_CMD_REQUEST_MESSAGE:
222 case MAV_CMD_PREFLIGHT_STORAGE:
223 case MAV_CMD_RUN_PREARM_CHECKS:
231bool MavCommandQueue::_canBeDuplicated(MAV_CMD command)
237 case MAV_CMD_DO_MOTOR_TEST:
238 case MAV_CMD_SET_MESSAGE_INTERVAL:
245QString MavCommandQueue::_formatCommand(MAV_CMD command,
float param1)
249 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral(
"%1 (%2)").arg(friendlyName, rawName);
251 if (command == MAV_CMD_REQUEST_MESSAGE) {
252 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(
static_cast<int>(param1));
254 commandStr += QStringLiteral(
" [%1]").arg(info->name);
263 int targetCompId, MAV_CMD command, MAV_FRAME frame,
264 float param1,
float param2,
float param3,
float param4,
double param5,
double param6,
float param7)
274 if ((targetCompId == MAV_COMP_ID_ALL) || (
isPending(targetCompId, command) && !_canBeDuplicated(command))) {
275 bool compIdAll = targetCompId == MAV_COMP_ID_ALL;
278 qCDebug(MavCommandQueueLog) << QStringLiteral(
"sendWorker failing %1").arg(compIdAll ?
"MAV_COMP_ID_ALL not supported" :
"duplicate command") << rawCommandName << param1 << param2 << param3 << param4 << param5 << param6 << param7;
283 ack.result = MAV_RESULT_FAILED;
286 emit
commandResult(_vehicle->
id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
289 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.")));
296 qCDebug(MavCommandQueueLog) <<
"sendWorker: primary link gone!";
301 ack.command = command;
302 ack.result = MAV_RESULT_FAILED;
305 emit
commandResult(_vehicle->
id(), targetCompId, command, MAV_RESULT_FAILED, failureCode);
314 MavCommandListEntry_t entry;
315 entry.useCommandInt = commandInt;
316 entry.targetCompId = targetCompId;
317 entry.command = command;
319 entry.showError = showError;
320 entry.ackHandlerInfo = {};
321 if (ackHandlerInfo) {
322 entry.ackHandlerInfo = *ackHandlerInfo;
324 entry.rgParam1 = param1;
325 entry.rgParam2 = param2;
326 entry.rgParam3 = param3;
327 entry.rgParam4 = param4;
328 entry.rgParam5 = param5;
329 entry.rgParam6 = param6;
330 entry.rgParam7 = param7;
332 entry.ackTimeoutMSecs = sharedLink->linkConfiguration()->isHighLatency() ? _ackTimeoutMSecsHighLatency : _ackTimeoutMSecs();
333 entry.elapsedTimer.start();
335 qCDebug(MavCommandQueueLog) <<
"Sending" << _formatCommand(command, param1) <<
"param1-7:" << command << param1 << param2 << param3 << param4 << param5 << param6 << param7;
338 _sendFromList(_list.count() - 1);
341void MavCommandQueue::_sendFromList(
int index)
343 MavCommandListEntry_t commandEntry = _list[index];
348 if (++_list[index].tryCount > commandEntry.maxTries) {
349 QString logMsg = QStringLiteral(
"Giving up sending command after max retries: %1").arg(rawCommandName);
352 if (commandEntry.command == MAV_CMD_REQUEST_MESSAGE) {
353 int requestedMsgId =
static_cast<int>(commandEntry.rgParam1);
354 const mavlink_message_info_t *info = mavlink_get_message_info_by_id(requestedMsgId);
355 logMsg += QStringLiteral(
" requesting: %1").arg(info ? info->name : QString::number(requestedMsgId));
358 qCWarning(MavCommandQueueLog) << logMsg;
360 _list.removeAt(index);
361 if (commandEntry.ackHandlerInfo.resultHandler) {
363 ack.result = MAV_RESULT_FAILED;
368 if (commandEntry.showError) {
374 if (commandEntry.tryCount > 1 && !_vehicle->
px4Firmware() && commandEntry.command == MAV_CMD_START_RX_PAIR) {
380 qCDebug(MavCommandQueueLog) <<
"Sending" << _formatCommand(commandEntry.command, commandEntry.rgParam1)
381 <<
"tryCount:param1-7" << commandEntry.tryCount << commandEntry.rgParam1 << commandEntry.rgParam2 << commandEntry.rgParam3 << commandEntry.rgParam4 << commandEntry.rgParam5 << commandEntry.rgParam6 << commandEntry.rgParam7;
385 qCDebug(MavCommandQueueLog) <<
"_sendFromList: primary link gone!";
391 if (commandEntry.useCommandInt) {
392 mavlink_command_int_t cmd;
393 memset(&cmd, 0,
sizeof(cmd));
394 cmd.target_system = _vehicle->
id();
395 cmd.target_component = commandEntry.targetCompId;
396 cmd.command = commandEntry.command;
397 cmd.frame = commandEntry.frame;
398 cmd.param1 = commandEntry.rgParam1;
399 cmd.param2 = commandEntry.rgParam2;
400 cmd.param3 = commandEntry.rgParam3;
401 cmd.param4 = commandEntry.rgParam4;
402 cmd.x = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam5 : commandEntry.rgParam5 * 1e7;
403 cmd.y = commandEntry.frame == MAV_FRAME_MISSION ? commandEntry.rgParam6 : commandEntry.rgParam6 * 1e7;
404 cmd.z = commandEntry.rgParam7;
407 sharedLink->mavlinkChannel(),
412 memset(&cmd, 0,
sizeof(cmd));
413 cmd.target_system = _vehicle->
id();
414 cmd.target_component = commandEntry.targetCompId;
415 cmd.command = commandEntry.command;
417 cmd.confirmation =
static_cast<uint8_t
>(qMin(commandEntry.tryCount - 1, 255));
418 cmd.param1 = commandEntry.rgParam1;
419 cmd.param2 = commandEntry.rgParam2;
420 cmd.param3 = commandEntry.rgParam3;
421 cmd.param4 = commandEntry.rgParam4;
422 cmd.param5 =
static_cast<float>(commandEntry.rgParam5);
423 cmd.param6 =
static_cast<float>(commandEntry.rgParam6);
424 cmd.param7 = commandEntry.rgParam7;
427 sharedLink->mavlinkChannel(),
435void MavCommandQueue::_responseTimeoutCheck()
437 if (_list.isEmpty()) {
445 for (
int i = _list.count() - 1; i >= 0; i--) {
449 if (i >= _list.count()) {
453 MavCommandListEntry_t& commandEntry = _list[i];
454 if (commandEntry.elapsedTimer.elapsed() > commandEntry.ackTimeoutMSecs) {
465 QString commandStr = friendlyName.isEmpty() ? rawName : QStringLiteral(
"%1 (%2)").arg(friendlyName, rawName);
467 switch (ack.result) {
468 case MAV_RESULT_TEMPORARILY_REJECTED:
471 case MAV_RESULT_DENIED:
474 case MAV_RESULT_UNSUPPORTED:
477 case MAV_RESULT_FAILED:
488 int entryIndex =
findEntryIndex(message.compid,
static_cast<MAV_CMD
>(ack.command));
489 if (entryIndex == -1) {
491 qCDebug(MavCommandQueueLog) <<
"handleCommandAck Ack not in list" << rawCommandName;
495 if (ack.result == MAV_RESULT_IN_PROGRESS) {
496 MavCommandListEntry_t commandEntry;
497 if (_vehicle->
px4Firmware() && ack.command == MAV_CMD_DO_AUTOTUNE_ENABLE) {
499 commandEntry = _list.takeAt(entryIndex);
502 MavCommandListEntry_t& commandEntryRef = _list[entryIndex];
503 commandEntryRef.maxTries = 1;
504 commandEntryRef.elapsedTimer.start();
505 commandEntry = commandEntryRef;
508 if (commandEntry.ackHandlerInfo.progressHandler) {
509 (*commandEntry.ackHandlerInfo.progressHandler)(commandEntry.ackHandlerInfo.progressHandlerData, message.compid, ack);
514 MavCommandListEntry_t commandEntry = _list.takeAt(entryIndex);
515 if (commandEntry.ackHandlerInfo.resultHandler) {
516 (*commandEntry.ackHandlerInfo.resultHandler)(commandEntry.ackHandlerInfo.resultHandlerData, message.compid, ack,
MavCmdResultCommandResultOnly);
518 if (commandEntry.showError) {
527 switch (failureCode) {
529 return QStringLiteral(
"Command Result Only");
531 return QStringLiteral(
"No Response To Command");
533 return QStringLiteral(
"Duplicate Command");
535 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
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)
static constexpr int kTestAckTimeoutMs
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)
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
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.