QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
RequestMessageCoordinator.cc
Go to the documentation of this file.
2
3#include "MAVLinkLib.h"
4#include "MavCommandQueue.h"
5#include "QGC.h"
7#include "Vehicle.h"
8
9QGC_LOGGING_CATEGORY(RequestMessageCoordinatorLog, "Vehicle.RequestMessageCoordinator")
10
12 : QObject(vehicle)
13 , _vehicle(vehicle)
14 , _commandQueue(commandQueue)
15{
16}
17
22
24{
25 for (auto& compIdEntry : _infoMap) {
26 qDeleteAll(compIdEntry);
27 }
28 _infoMap.clear();
29
30 for (auto& requestQueue : _queueMap) {
31 qDeleteAll(requestQueue);
32 }
33 _queueMap.clear();
34}
35
36bool RequestMessageCoordinator::_duplicate(int compId, int msgId) const
37{
38 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(msgId);
39 const QString msgName = info ? QString(info->name) : QString::number(msgId);
40
41 if (_infoMap.contains(compId) && _infoMap[compId].contains(msgId)) {
42 qCDebug(RequestMessageCoordinatorLog) << "duplicate in active map - compId:msgId" << compId << msgName;
43 return true;
44 }
45
46 if (_queueMap.contains(compId)) {
47 for (const RequestMessageInfo_t* requestMessageInfo : _queueMap[compId]) {
48 if (requestMessageInfo->msgId == msgId) {
49 qCDebug(RequestMessageCoordinatorLog) << "duplicate in queue - compId:msgId" << compId << msgName;
50 return true;
51 }
52 }
53 }
54
55 return false;
56}
57
58void RequestMessageCoordinator::_removeInfo(int compId, int msgId)
59{
60 if (_infoMap.contains(compId) && _infoMap[compId].contains(msgId)) {
61 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(msgId);
62 const QString msgName = info ? QString(info->name) : QString::number(msgId);
63
64 delete _infoMap[compId][msgId];
65 _infoMap[compId].remove(msgId);
66 qCDebug(RequestMessageCoordinatorLog)
67 << "removed active request compId:msgId"
68 << compId
69 << msgName
70 << "remainingActive"
71 << _infoMap[compId].count();
72 if (_infoMap[compId].isEmpty()) {
73 _infoMap.remove(compId);
74 }
75 _sendNextFromQueue(compId);
76 } else {
77 qWarning() << "compId:msgId not found" << compId << msgId;
78 }
79}
80
81void RequestMessageCoordinator::_sendNow(RequestMessageInfo_t* info)
82{
83 const mavlink_message_info_t* msgInfo = mavlink_get_message_info_by_id(info->msgId);
84 const QString msgName = msgInfo ? QString(msgInfo->name) : QString::number(info->msgId);
85
86 _infoMap[info->compId][info->msgId] = info;
87
88 const int queueDepth = _queueMap.contains(info->compId) ? _queueMap[info->compId].count() : 0;
89 qCDebug(RequestMessageCoordinatorLog)
90 << "sending now compId:msgId"
91 << info->compId
92 << msgName
93 << "queueDepth"
94 << queueDepth;
95
96 MavCmdAckHandlerInfo_t handlerInfo {};
97 handlerInfo.resultHandler = _cmdResultHandler;
98 handlerInfo.resultHandlerData = info;
99
100 _commandQueue->sendWorker(false, // commandInt
101 false, // showError
102 &handlerInfo,
103 info->compId,
104 MAV_CMD_REQUEST_MESSAGE,
105 MAV_FRAME_GLOBAL,
106 info->msgId,
107 info->param1,
108 info->param2,
109 info->param3,
110 info->param4,
111 info->param5,
112 0);
113}
114
115void RequestMessageCoordinator::_sendNextFromQueue(int compId)
116{
117 if (_infoMap.contains(compId) && !_infoMap[compId].isEmpty()) {
118 qCDebug(RequestMessageCoordinatorLog)
119 << "active request still in progress for compId"
120 << compId
121 << "activeCount"
122 << _infoMap[compId].count();
123 return;
124 }
125
126 if (!_queueMap.contains(compId) || _queueMap[compId].isEmpty()) {
127 qCDebug(RequestMessageCoordinatorLog) << "no queued request for compId" << compId;
128 _queueMap.remove(compId);
129 return;
130 }
131
132 RequestMessageInfo_t* info = _queueMap[compId].takeFirst();
133 const mavlink_message_info_t* msgInfo = mavlink_get_message_info_by_id(info->msgId);
134 const QString msgName = msgInfo ? QString(msgInfo->name) : QString::number(info->msgId);
135 const int remainingQueue = _queueMap[compId].count();
136 qCDebug(RequestMessageCoordinatorLog)
137 << "dequeued next request compId:msgId"
138 << compId
139 << msgName
140 << "remainingQueue"
141 << remainingQueue;
142
143 if (_queueMap[compId].isEmpty()) {
144 _queueMap.remove(compId);
145 }
146
147 _sendNow(info);
148}
149
151{
152 if (_infoMap.contains(message.compid) && _infoMap[message.compid].contains(message.msgid)) {
153 auto pInfo = _infoMap[message.compid][message.msgid];
154 auto resultHandler = pInfo->resultHandler;
155 auto resultHandlerData = pInfo->resultHandlerData;
156
157 pInfo->messageReceived = true;
158 pInfo->message = message;
159
160 const mavlink_message_info_t* info = mavlink_get_message_info_by_id(message.msgid);
161 QString msgName = info ? QString(info->name) : QString::number(message.msgid);
162 const int activeCount = _infoMap.contains(message.compid) ? _infoMap[message.compid].count() : 0;
163 const int queueDepth = _queueMap.contains(message.compid) ? _queueMap[message.compid].count() : 0;
164 qCDebug(RequestMessageCoordinatorLog)
165 << "message received - compId:msgId"
166 << message.compid
167 << msgName
168 << "activeCount"
169 << activeCount
170 << "queueDepth"
171 << queueDepth;
172
173 if (pInfo->commandAckReceived) {
174 _removeInfo(message.compid, message.msgid);
175 (*resultHandler)(resultHandlerData, MAV_RESULT_ACCEPTED, RequestMessageNoFailure, message);
176 }
177 return;
178 }
179
180 // Every inbound message doubles as a timeout tick. _removeInfo mutates _infoMap,
181 // so snapshot the first timed-out entry and handle it after the loops exit.
182 int timedOutCompId = -1;
183 int timedOutMsgId = -1;
184 RequestMessageResultHandler timedOutHandler = nullptr;
185 void* timedOutHandlerData = nullptr;
186 for (auto& compIdEntry : _infoMap) {
187 for (auto info : compIdEntry) {
188 // Unit-test environments can have enough scheduling jitter that a 50ms
189 // response deadline causes false request-message timeouts.
190 const int messageWaitTimeoutMs = QGC::runningUnitTests() ? 500 : 1000;
191 if (info->messageWaitElapsedTimer.isValid() && info->messageWaitElapsedTimer.elapsed() > messageWaitTimeoutMs) {
192 const mavlink_message_info_t* msgInfo = mavlink_get_message_info_by_id(info->msgId);
193 QString msgName = msgInfo ? msgInfo->name : QString::number(info->msgId);
194 const int queueDepth = _queueMap.contains(info->compId) ? _queueMap[info->compId].count() : 0;
195 qCDebug(RequestMessageCoordinatorLog)
196 << "request message timed out - compId:msgId"
197 << info->compId
198 << msgName
199 << "queueDepth"
200 << queueDepth;
201
202 timedOutCompId = info->compId;
203 timedOutMsgId = info->msgId;
204 timedOutHandler = info->resultHandler;
205 timedOutHandlerData = info->resultHandlerData;
206 break;
207 }
208 }
209 if (timedOutHandler) {
210 break;
211 }
212 }
213
214 if (timedOutHandler) {
215 _removeInfo(timedOutCompId, timedOutMsgId);
216 mavlink_message_t timeoutMessage = {};
217 (*timedOutHandler)(timedOutHandlerData, MAV_RESULT_FAILED, RequestMessageFailureMessageNotReceived, timeoutMessage);
218 }
219}
220
221void RequestMessageCoordinator::_cmdResultHandler(void* resultHandlerData_, [[maybe_unused]] int compId, const mavlink_command_ack_t& ack, MavCmdResultFailureCode_t failureCode)
222{
223 auto info = static_cast<RequestMessageInfo_t*>(resultHandlerData_);
224 auto resultHandler = info->resultHandler;
225 auto resultHandlerData = info->resultHandlerData;
226 Vehicle* vehicle = info->vehicle; // QPointer converts to raw pointer, null if Vehicle destroyed
227
228 // Vehicle was destroyed before callback fired - clean up and return without accessing vehicle
229 if (!vehicle) {
230 qCDebug(RequestMessageCoordinatorLog) << "Vehicle destroyed before callback - skipping";
231 delete info;
232 return;
233 }
234
235 auto* coordinator = info->coordinator;
236
237 info->commandAckReceived = true;
238 const mavlink_message_info_t* msgInfo = mavlink_get_message_info_by_id(info->msgId);
239 const QString msgName = msgInfo ? QString(msgInfo->name) : QString::number(info->msgId);
240 qCDebug(RequestMessageCoordinatorLog)
241 << "ack for requestMessage compId:msgId"
242 << info->compId
243 << msgName
244 << "ack"
245 << QGCMAVLink::mavResultToString(static_cast<MAV_RESULT>(ack.result))
246 << "failureCode"
248
249 if (ack.result != MAV_RESULT_ACCEPTED) {
250 mavlink_message_t ackMessage = {};
252
253 switch (failureCode) {
255 requestMessageFailureCode = RequestMessageFailureCommandError;
256 break;
258 requestMessageFailureCode = RequestMessageFailureCommandNotAcked;
259 break;
261 requestMessageFailureCode = RequestMessageFailureDuplicate;
262 break;
263 }
264
265 coordinator->_removeInfo(info->compId, info->msgId);
266 (*resultHandler)(resultHandlerData, static_cast<MAV_RESULT>(ack.result), requestMessageFailureCode, ackMessage);
267 return;
268 }
269
270 if (info->messageReceived) {
271 auto message = info->message;
272 coordinator->_removeInfo(info->compId, info->msgId);
273 (*resultHandler)(resultHandlerData, MAV_RESULT_ACCEPTED, RequestMessageNoFailure, message);
274 return;
275 }
276
277 // We have the ack, but we are still waiting for the message. Start the timer to wait for the message
278 info->messageWaitElapsedTimer.start();
279}
280
281void RequestMessageCoordinator::requestMessage(RequestMessageResultHandler resultHandler, void* resultHandlerData, int compId, int messageId, float param1, float param2, float param3, float param4, float param5)
282{
283 const mavlink_message_info_t* msgInfo = mavlink_get_message_info_by_id(messageId);
284 const QString msgName = msgInfo ? QString(msgInfo->name) : QString::number(messageId);
285 const int activeCount = _infoMap.contains(compId) ? _infoMap[compId].count() : 0;
286 const int queueDepth = _queueMap.contains(compId) ? _queueMap[compId].count() : 0;
287 qCDebug(RequestMessageCoordinatorLog)
288 << "incoming request compId:msgId"
289 << compId
290 << msgName
291 << "activeCount"
292 << activeCount
293 << "queueDepth"
294 << queueDepth;
295
296 auto info = new RequestMessageInfo_t;
297 info->vehicle = _vehicle;
298 info->coordinator = this;
299 info->compId = compId;
300 info->msgId = messageId;
301 info->param1 = param1;
302 info->param2 = param2;
303 info->param3 = param3;
304 info->param4 = param4;
305 info->param5 = param5;
306 info->resultHandler = resultHandler;
307 info->resultHandlerData = resultHandlerData;
308
309 if (_duplicate(compId, messageId)) {
310 mavlink_message_t ackMessage = {};
311 qCWarning(RequestMessageCoordinatorLog) << "failing exact duplicate compId:msgId" << compId << msgName;
312 (*resultHandler)(resultHandlerData,
313 MAV_RESULT_FAILED,
315 ackMessage);
316 delete info;
317 return;
318 }
319
320 if (_infoMap.contains(compId) && !_infoMap[compId].isEmpty()) {
321 _queueMap[compId].append(info);
322 qCDebug(RequestMessageCoordinatorLog)
323 << "queued request compId:msgId"
324 << compId
325 << msgName
326 << "newQueueDepth"
327 << _queueMap[compId].count();
328 return;
329 }
330
331 _sendNow(info);
332}
333
335{
336 switch (failureCode) {
338 return QStringLiteral("No Failure");
340 return QStringLiteral("Command Error");
342 return QStringLiteral("Command Not Acked");
344 return QStringLiteral("Message Not Received");
346 return QStringLiteral("Duplicate Request");
347 default:
348 return QStringLiteral("Unknown (%1)").arg(failureCode);
349 }
350}
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
static QString failureCodeToString(MavCmdResultFailureCode_t failureCode)
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 handleReceivedMessage(const mavlink_message_t &message)
static QString failureCodeToString(RequestMessageResultHandlerFailureCode_t failureCode)
void requestMessage(RequestMessageResultHandler resultHandler, void *resultHandlerData, int compId, int messageId, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f)
void stop()
Clear pending state without firing callbacks (used during vehicle shutdown).
int compId() const
Definition Vehicle.h:424
bool runningUnitTests()
True if the app is running in unit-test mode. Returns false if qgcApp() doesn't exist yet.
Definition QGC.cc:227
MavCmdResultHandler resultHandler
nullptr for no handler
void(* RequestMessageResultHandler)(void *resultHandlerData, MAV_RESULT commandResult, RequestMessageResultHandlerFailureCode_t failureCode, const mavlink_message_t &message)
Callback for requestMessage — delivered when the ack/message pair resolves or a failure occurs.
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.
RequestMessageResultHandlerFailureCode_t
@ RequestMessageFailureMessageNotReceived
@ RequestMessageFailureCommandNotAcked
@ RequestMessageFailureDuplicate
Exact duplicate request already active or queued for this component/message id.
@ RequestMessageFailureCommandError
@ RequestMessageNoFailure