QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MockLinkMissionItemHandler.cc
Go to the documentation of this file.
2
3#include <QtPositioning/QGeoCoordinate>
4
5#include "MAVLinkProtocol.h"
6#include "MockLink.h"
8
9QGC_LOGGING_CATEGORY(MockLinkMissionItemHandlerLog, "Comms.MockLink.MockLinkMissionItemHandler")
10
12 : QObject(mockLink)
13 , _mockLink(mockLink)
14{
15 // qCDebug(MockLinkMissionItemHandlerLog) << Q_FUNC_INFO << this;
16
17 Q_ASSERT(mockLink);
18
19 (void) connect(&_missionItemResponseTimer, &QTimer::timeout, this, &MockLinkMissionItemHandler::_missionItemResponseTimeout);
20}
21
23{
24 // qCDebug(MockLinkMissionItemHandlerLog) << Q_FUNC_INFO << this;
25}
26
27void MockLinkMissionItemHandler::_startMissionItemResponseTimer()
28{
29 _missionItemResponseTimer.start(500);
30}
31
33{
34 _missionItems.clear();
35
36 const QGeoCoordinate home = _mockLink->homeCoordinate();
37 const double homeLatitude = home.latitude();
38 const double homeLongitude = home.longitude();
39 constexpr float relativeAltitude = 50.0f;
40
41 const auto makeItem = [](uint16_t seq, uint16_t command, bool current, double latitude, double longitude, float altitude) {
42 mavlink_mission_item_int_t item{};
43 item.seq = seq;
44 item.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
45 item.command = command;
46 item.current = current ? 1 : 0;
47 item.autocontinue = 1;
48 item.x = static_cast<int32_t>(latitude * 1e7);
49 item.y = static_cast<int32_t>(longitude * 1e7);
50 item.z = altitude;
51 item.mission_type = MAV_MISSION_TYPE_MISSION;
52 return item;
53 };
54
55 // Seq 0: Takeoff
56 _missionItems[0] = makeItem(0, MAV_CMD_NAV_TAKEOFF, true, homeLatitude, homeLongitude, relativeAltitude);
57 // Seq 1: Waypoint
58 _missionItems[1] = makeItem(1, MAV_CMD_NAV_WAYPOINT, false, homeLatitude + 0.001, homeLongitude + 0.001, relativeAltitude);
59 // Seq 2: Return to launch
60 _missionItems[2] = makeItem(2, MAV_CMD_NAV_RETURN_TO_LAUNCH, false, 0.0, 0.0, 0.0);
61
62 qCDebug(MockLinkMissionItemHandlerLog) << "loadSimpleMultirotorMission seeded" << _missionItems.count() << "items";
63}
64
66{
67 switch (msg.msgid) {
68 case MAVLINK_MSG_ID_MISSION_REQUEST_LIST:
69 _handleMissionRequestList(msg);
70 break;
71 case MAVLINK_MSG_ID_MISSION_REQUEST_INT:
72 _handleMissionRequest(msg);
73 break;
74 case MAVLINK_MSG_ID_MISSION_ITEM_INT:
75 _handleMissionItem(msg);
76 break;
77 case MAVLINK_MSG_ID_MISSION_COUNT:
78 _handleMissionCount(msg);
79 break;
80 case MAVLINK_MSG_ID_MISSION_ACK:
81 // Acks are received back for each MISSION_ITEM message
82 break;
83 case MAVLINK_MSG_ID_MISSION_SET_CURRENT:
84 // Sets the currently active mission item
85 break;
86 case MAVLINK_MSG_ID_MISSION_CLEAR_ALL:
87 _handleMissionClearAll(msg);
88 break;
89 default:
90 return false;
91 }
92
93 return true;
94}
95
96void MockLinkMissionItemHandler::_handleMissionClearAll(const mavlink_message_t &msg)
97{
98 mavlink_mission_clear_all_t clearAll{};
99 mavlink_msg_mission_clear_all_decode(&msg, &clearAll);
100
101 Q_ASSERT(clearAll.target_system == _mockLink->vehicleId());
102
103 _requestType = static_cast<MAV_MISSION_TYPE>(clearAll.mission_type);
104 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionClearAll" << _requestType;
105
106 switch (_requestType) {
107 case MAV_MISSION_TYPE_MISSION:
108 _missionItems.clear();
109 break;
110 case MAV_MISSION_TYPE_FENCE:
111 _fenceItems.clear();
112 break;
113 case MAV_MISSION_TYPE_RALLY:
114 _rallyItems.clear();
115 break;
116 case MAV_MISSION_TYPE_ALL:
117 _missionItems.clear();
118 _fenceItems.clear();
119 _rallyItems.clear();
120 break;
121 default:
122 Q_ASSERT(false);
123 }
124
125 _sendAck(MAV_MISSION_ACCEPTED);
126}
127
128void MockLinkMissionItemHandler::_handleMissionRequestList(const mavlink_message_t &msg)
129{
130 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList read sequence";
131
132 _failReadRequest1FirstResponse = true;
133
134 mavlink_mission_request_list_t request{};
135 mavlink_msg_mission_request_list_decode(&msg, &request);
136
137 if (_failureMode == FailReadRequestListNoResponse) {
138 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList not responding due to failure mode FailReadRequestListNoResponse";
139 return;
140 }
141
142 if ((_failureMode == FailReadRequestListFirstResponse) && _failReadRequestListFirstResponse) {
143 _failReadRequestListFirstResponse = false;
144 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequestList not responding due to failure mode FailReadRequestListFirstResponse";
145 return;
146 }
147
148 _failReadRequestListFirstResponse = true;
149
150 Q_ASSERT(request.target_system == _mockLink->vehicleId());
151 _requestListCounts[static_cast<MAV_MISSION_TYPE>(request.mission_type)]++;
152
153 _requestType = static_cast<MAV_MISSION_TYPE>(request.mission_type);
154
155 int itemCount;
156 switch (_requestType) {
157 case MAV_MISSION_TYPE_MISSION:
158 itemCount = _missionItems.count();
159 if (itemCount == 0 && _sendHomePositionOnEmptyList) {
160 itemCount = 1;
161 }
162 break;
163 case MAV_MISSION_TYPE_FENCE:
164 itemCount = _fenceItems.count();
165 break;
166 case MAV_MISSION_TYPE_RALLY:
167 itemCount = _rallyItems.count();
168 break;
169 default:
170 Q_ASSERT(false);
171 }
172
173 mavlink_message_t responseMsg{};
174 (void) mavlink_msg_mission_count_pack_chan(
175 _mockLink->vehicleId(),
176 MAV_COMP_ID_AUTOPILOT1,
177 _mockLink->outgoingMavlinkChannel(),
178 &responseMsg, // Outgoing message
179 msg.sysid, // Target is original sender
180 msg.compid, // Target is original sender
181 itemCount, // Number of mission items
182 _requestType,
183 0
184 );
185
186 _mockLink->respondWithMavlinkMessage(responseMsg);
187}
188
189void MockLinkMissionItemHandler::_handleMissionRequest(const mavlink_message_t &msg)
190{
191 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest read sequence";
192
193 mavlink_mission_request_int_t request{};
194 mavlink_msg_mission_request_int_decode(&msg, &request);
195
196 Q_ASSERT(request.target_system == _mockLink->vehicleId());
197
198 if ((_failureMode == FailReadRequest0NoResponse) && (request.seq == 0)) {
199 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest0NoResponse";
200 return;
201 }
202
203 if ((_failureMode == FailReadRequest1NoResponse) && (request.seq == 1)) {
204 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest1NoResponse";
205 return;
206 }
207
208 if ((_failureMode == FailReadRequest1FirstResponse) && (request.seq == 1) && _failReadRequest1FirstResponse) {
209 _failReadRequest1FirstResponse = false;
210 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest not responding due to failure mode FailReadRequest1FirstResponse";
211 return;
212 }
213
214 // FIXME: Track whether all items are requested, or requested in sequence
215
216 if (((_failureMode == FailReadRequest0IncorrectSequence) && (request.seq == 0)) ||
217 ((_failureMode == FailReadRequest1IncorrectSequence) && (request.seq == 1))) {
218 // Send back the incorrect sequence number
219 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionRequest sending bad sequence number";
220 request.seq++;
221 }
222
223 if (((_failureMode == FailReadRequest0ErrorAck) && (request.seq == 0)) ||
224 ((_failureMode == FailReadRequest1ErrorAck) && (request.seq == 1))) {
225 _sendAck(_failureAckResult);
226 return;
227 }
228
229 mavlink_mission_item_int_t missionItemInt{};
230
231 switch (request.mission_type) {
232 case MAV_MISSION_TYPE_MISSION:
233 if (_missionItems.isEmpty() && _sendHomePositionOnEmptyList) {
234 (void) memset(&missionItemInt, 0, sizeof(missionItemInt));
235 missionItemInt.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
236 missionItemInt.command = MAV_CMD_NAV_WAYPOINT;
237 missionItemInt.autocontinue = true;
238 } else {
239 missionItemInt = _missionItems[request.seq];
240 }
241 break;
242 case MAV_MISSION_TYPE_FENCE:
243 missionItemInt = _fenceItems[request.seq];
244 break;
245 case MAV_MISSION_TYPE_RALLY:
246 missionItemInt = _rallyItems[request.seq];
247 break;
248 default:
249 Q_ASSERT(false);
250 }
251
252 mavlink_message_t responseMsg{};
253 (void) mavlink_msg_mission_item_int_pack_chan(
254 _mockLink->vehicleId(),
255 MAV_COMP_ID_AUTOPILOT1,
256 _mockLink->outgoingMavlinkChannel(),
257 &responseMsg, // Outgoing message
258 msg.sysid, // Target is original sender
259 msg.compid, // Target is original sender
260 request.seq, // Index of mission item being sent
261 missionItemInt.frame,
262 missionItemInt.command,
263 missionItemInt.current,
264 missionItemInt.autocontinue,
265 missionItemInt.param1, missionItemInt.param2, missionItemInt.param3, missionItemInt.param4,
266 missionItemInt.x, missionItemInt.y, missionItemInt.z,
267 _requestType
268 );
269
270 _mockLink->respondWithMavlinkMessage(responseMsg);
271}
272
273void MockLinkMissionItemHandler::_handleMissionCount(const mavlink_message_t &msg)
274{
275 mavlink_mission_count_t missionCount{};
276 mavlink_msg_mission_count_decode(&msg, &missionCount);
277 Q_ASSERT(missionCount.target_system == _mockLink->vehicleId());
278
279 _requestType = (MAV_MISSION_TYPE)missionCount.mission_type;
280 _writeSequenceCount = missionCount.count;
281 Q_ASSERT(_writeSequenceCount >= 0);
282
283 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionCount write sequence _writeSequenceCount:" << _writeSequenceCount;
284
285 switch (missionCount.mission_type) {
286 case MAV_MISSION_TYPE_MISSION:
287 _missionItems.clear();
288 break;
289 case MAV_MISSION_TYPE_FENCE:
290 _fenceItems.clear();
291 break;
292 case MAV_MISSION_TYPE_RALLY:
293 _rallyItems.clear();
294 break;
295 }
296
297 if (_writeSequenceCount == 0) {
298 _sendAck(MAV_MISSION_ACCEPTED);
299 return;
300 }
301
302 if (_failureMode == FailWriteMissionCountNoResponse) {
303 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionCount not responding due to failure mode FailWriteMissionCountNoResponse";
304 return;
305 }
306
307 if (_failureMode == FailWriteMissionCountFirstResponse && _failWriteMissionCountFirstResponse) {
308 _failWriteMissionCountFirstResponse = false;
309 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionCount not responding due to failure mode FailWriteMissionCountNoResponse";
310 return;
311 }
312
313 _failWriteMissionCountFirstResponse = true;
314 _writeSequenceIndex = 0;
315 _requestNextMissionItem(_writeSequenceIndex);
316}
317
318void MockLinkMissionItemHandler::_requestNextMissionItem(int sequenceNumber)
319{
320 qCDebug(MockLinkMissionItemHandlerLog) << "write sequence sequenceNumber:" << sequenceNumber << "_failureMode:" << _failureMode;
321
322 if ((_failureMode == FailWriteRequest1NoResponse) && (sequenceNumber == 1)) {
323 qCDebug(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem not responding due to failure mode FailWriteRequest1NoResponse";
324 return;
325 }
326
327 if (sequenceNumber >= _writeSequenceCount) {
328 qCWarning(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem requested seqeuence number > write count sequenceNumber::_writeSequenceCount" << sequenceNumber << _writeSequenceCount;
329 return;
330 }
331
332 if (((_failureMode == FailWriteRequest0IncorrectSequence) && (sequenceNumber == 0)) ||
333 ((_failureMode == FailWriteRequest1IncorrectSequence) && (sequenceNumber == 1))) {
334 sequenceNumber++;
335 }
336
337 if (((_failureMode == FailWriteRequest0ErrorAck) && (sequenceNumber == 0)) ||
338 ((_failureMode == FailWriteRequest1ErrorAck) && (sequenceNumber == 1))) {
339 qCDebug(MockLinkMissionItemHandlerLog) << "_requestNextMissionItem sending ack error due to failure mode";
340 _sendAck(_failureAckResult);
341 return;
342 }
343
344 mavlink_message_t message{};
345 (void) mavlink_msg_mission_request_int_pack_chan(
346 _mockLink->vehicleId(),
347 MAV_COMP_ID_AUTOPILOT1,
348 _mockLink->outgoingMavlinkChannel(),
349 &message,
352 sequenceNumber,
353 _requestType
354 );
355 _mockLink->respondWithMavlinkMessage(message);
356
357 // If response with Mission Item doesn't come before timer fires it's an error
358 _startMissionItemResponseTimer();
359}
360
361void MockLinkMissionItemHandler::_sendAck(MAV_MISSION_RESULT ackType) const
362{
363 qCDebug(MockLinkMissionItemHandlerLog) << "_sendAck write sequence complete ackType:" << ackType;
364
365 mavlink_message_t message{};
366 (void) mavlink_msg_mission_ack_pack_chan(
367 _mockLink->vehicleId(),
368 MAV_COMP_ID_AUTOPILOT1,
369 _mockLink->outgoingMavlinkChannel(),
370 &message,
373 ackType,
374 _requestType,
375 0
376 );
377
378 _mockLink->respondWithMavlinkMessage(message);
379}
380
381void MockLinkMissionItemHandler::_handleMissionItem(const mavlink_message_t &msg)
382{
383 qCDebug(MockLinkMissionItemHandlerLog) << "_handleMissionItem write sequence";
384
385 _missionItemResponseTimer.stop();
386
387 mavlink_mission_item_int_t missionItemInt{};
388 mavlink_msg_mission_item_int_decode(&msg, &missionItemInt);
389
390 const MAV_MISSION_TYPE missionType = static_cast<MAV_MISSION_TYPE>(missionItemInt.mission_type);
391 const uint16_t seq = missionItemInt.seq;
392
393 switch (missionType) {
394 case MAV_MISSION_TYPE_MISSION:
395 _missionItems[seq] = missionItemInt;
396 break;
397 case MAV_MISSION_TYPE_FENCE:
398 _fenceItems[seq] = missionItemInt;
399 break;
400 case MAV_MISSION_TYPE_RALLY:
401 _rallyItems[seq] = missionItemInt;
402 break;
403 case MAV_MISSION_TYPE_ENUM_END:
404 case MAV_MISSION_TYPE_ALL:
405 qCWarning(MockLinkMissionItemHandlerLog) << "Internal error";
406 break;
407 }
408
409 _writeSequenceIndex++;
410 if (_writeSequenceIndex < _writeSequenceCount) {
411 if ((_failureMode == FailWriteFinalAckMissingRequests) && (_writeSequenceIndex == 3)) {
412 // Send MAV_MISSION_ACCEPTED ack too early
413 _sendAck(MAV_MISSION_ACCEPTED);
414 } else {
415 _requestNextMissionItem(_writeSequenceIndex);
416 }
417
418 return;
419 }
420
421 if (_failureMode != FailWriteFinalAckNoResponse) {
422 MAV_MISSION_RESULT ack = MAV_MISSION_ACCEPTED;
423
424 if (_failureMode == FailWriteFinalAckErrorAck) {
425 ack = MAV_MISSION_ERROR;
426 }
427
428 _sendAck(ack);
429 }
430}
431
432void MockLinkMissionItemHandler::_missionItemResponseTimeout()
433{
434 qCWarning(MockLinkMissionItemHandlerLog) << "Timeout waiting for next MISSION_ITEM_INT";
435 Q_ASSERT(false);
436}
437
439{
440 _sendAck(ackType);
441}
442
444{
445 // FIXME: NYI
446 Q_ASSERT(false);
447}
448
450{
451 // FIXME: NYI
452 Q_ASSERT(false);
453}
454
455void MockLinkMissionItemHandler::setFailureMode(FailureMode_t failureMode, MAV_MISSION_RESULT failureAckResult)
456{
457 _failureMode = failureMode;
458 _failureAckResult = failureAckResult;
459}
460
462{
463 _missionItemResponseTimer.stop();
464}
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
int32_t latitude
Definition UBXPlan.cc:40
int32_t longitude
Definition UBXPlan.cc:42
static int getComponentId()
static MAVLinkProtocol * instance()
int getSystemId() const
void sendUnexpectedMissionAck(MAV_MISSION_RESULT ackType)
Called to send a MISSION_ACK message while the MissionManager is in idle state.
bool handleMavlinkMessage(const mavlink_message_t &msg)
void setFailureMode(FailureMode_t failureMode, MAV_MISSION_RESULT failureAckResult)
void sendUnexpectedMissionRequest()
Called to send a MISSION_REQUEST message while the MissionManager is in idle state.
void sendUnexpectedMissionItem()
Called to send a MISSION_ITEM message while the MissionManager is in idle state.