QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MissionManager.cc
Go to the documentation of this file.
1#include "MissionManager.h"
2
3#include "AppMessages.h"
4#include "FirmwarePlugin.h"
5#include "MAVLinkLib.h"
6#include "MAVLinkProtocol.h"
10#include "Vehicle.h"
11#include "VehicleLinkManager.h"
12
13QGC_LOGGING_CATEGORY(MissionManagerLog, "PlanManager.MissionManager")
14
16 : PlanManager (vehicle, MAV_MISSION_TYPE_MISSION)
17 , _cachedLastCurrentIndex (-1)
18{
19 connect(_vehicle, &Vehicle::mavlinkMessageReceived, this, &MissionManager::_mavlinkMessageReceived);
20}
21
26
27void MissionManager::writeArduPilotGuidedMissionItem(const QGeoCoordinate& gotoCoord, bool altChangeOnly)
28{
29 if (inProgress()) {
30 qCDebug(MissionManagerLog) << "writeArduPilotGuidedMissionItem called while transaction in progress";
31 return;
32 }
33
35
37
39 if (sharedLink) {
40 mavlink_message_t messageOut;
41 mavlink_mission_item_t missionItem;
42
43 memset(&missionItem, 0, sizeof(missionItem));
44 missionItem.target_system = _vehicle->id();
45 missionItem.target_component = _vehicle->defaultComponentId();
46 missionItem.seq = 0;
47 missionItem.command = MAV_CMD_NAV_WAYPOINT;
48 missionItem.param1 = 0;
49 missionItem.param2 = 0;
50 missionItem.param3 = 0;
51 missionItem.param4 = 0;
52 missionItem.x = gotoCoord.latitude();
53 missionItem.y = gotoCoord.longitude();
54 missionItem.z = gotoCoord.altitude();
55 missionItem.frame = MAV_FRAME_GLOBAL_RELATIVE_ALT;
56 missionItem.current = altChangeOnly ? 3 : 2;
57 missionItem.autocontinue = true;
58
59 mavlink_msg_mission_item_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
61 sharedLink->mavlinkChannel(),
62 &messageOut,
63 &missionItem);
64
65 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), messageOut);
66 }
68 emit inProgressChanged(true);
69}
70
72{
74 return;
75 }
76
77 if (inProgress()) {
78 qCDebug(MissionManagerLog) << "generateResumeMission called while transaction in progress";
79 return;
80 }
81
82 for (int i=0; i<_missionItems.count(); i++) {
83 MissionItem* item = _missionItems[i];
84 if (item->command() == MAV_CMD_DO_JUMP) {
85 QGC::showAppMessage(tr("Unable to generate resume mission due to MAV_CMD_DO_JUMP command."));
86 return;
87 }
88 }
89
90 // Be anal about crap input
91 resumeIndex = qMax(0, qMin(resumeIndex, _missionItems.count() - 1));
92
93 // Adjust resume index to be a location based command
95 if (!uiInfo || uiInfo->isStandaloneCoordinate() || !uiInfo->specifiesCoordinate()) {
96 // We have to back up to the last command which the vehicle flies through
97 while (--resumeIndex > 0) {
98 uiInfo = MissionCommandTree::instance()->getUIInfo(_vehicle, _vehicle->vehicleClass(), _missionItems[resumeIndex]->command());
99 if (uiInfo && (uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate())) {
100 // Found it
101 break;
102 }
103
104 }
105 }
106 resumeIndex = qMax(0, resumeIndex);
107
108 QList<MissionItem*> resumeMission;
109
110 QList<MAV_CMD> includedResumeCommands;
111
112 // If any command in this list occurs before the resumeIndex it will be added to the front of the mission
113 includedResumeCommands << MAV_CMD_DO_CONTROL_VIDEO
114 << MAV_CMD_DO_SET_ROI
115 << MAV_CMD_DO_DIGICAM_CONFIGURE
116 << MAV_CMD_DO_DIGICAM_CONTROL
117 << MAV_CMD_DO_MOUNT_CONFIGURE
118 << MAV_CMD_DO_MOUNT_CONTROL
119 << MAV_CMD_DO_SET_CAM_TRIGG_DIST
120 << MAV_CMD_DO_FENCE_ENABLE
121 << MAV_CMD_IMAGE_START_CAPTURE
122 << MAV_CMD_IMAGE_STOP_CAPTURE
123 << MAV_CMD_VIDEO_START_CAPTURE
124 << MAV_CMD_VIDEO_STOP_CAPTURE
125 << MAV_CMD_DO_CHANGE_SPEED
126 << MAV_CMD_SET_CAMERA_MODE;
127
128 bool addHomePosition = _vehicle->firmwarePlugin()->sendHomePositionToVehicle();
129
130 int prefixCommandCount = 0;
131 for (int i=0; i<_missionItems.count(); i++) {
132 MissionItem* oldItem = _missionItems[i];
134 if ((i == 0 && addHomePosition) || i >= resumeIndex || includedResumeCommands.contains(oldItem->command()) || (loopUiInfo && loopUiInfo->isTakeoffCommand())) {
135 if (i < resumeIndex) {
136 prefixCommandCount++;
137 }
138 MissionItem* newItem = new MissionItem(*oldItem, this);
139 newItem->setIsCurrentItem(false);
140 resumeMission.append(newItem);
141 }
142 }
143 prefixCommandCount = qMax(0, qMin(prefixCommandCount, resumeMission.count())); // Anal prevention against crashes
144
145 // De-dup and remove no-ops from the commands which were added to the front of the mission
146 bool foundROI = false;
147 bool foundCameraSetMode = false;
148 bool foundCameraStartStop = false;
149 prefixCommandCount--; // Change from count to array index
150 while (prefixCommandCount >= 0) {
151 MissionItem* resumeItem = resumeMission[prefixCommandCount];
152 switch (resumeItem->command()) {
153 case MAV_CMD_SET_CAMERA_MODE:
154 // Only keep the last one
155 if (foundCameraSetMode) {
156 resumeMission.removeAt(prefixCommandCount);
157 }
158 foundCameraSetMode = true;
159 break;
160 case MAV_CMD_DO_SET_ROI:
161 // Only keep the last one
162 if (foundROI) {
163 resumeMission.removeAt(prefixCommandCount);
164 }
165 foundROI = true;
166 break;
167 case MAV_CMD_DO_SET_CAM_TRIGG_DIST:
168 case MAV_CMD_IMAGE_STOP_CAPTURE:
169 case MAV_CMD_VIDEO_START_CAPTURE:
170 case MAV_CMD_VIDEO_STOP_CAPTURE:
171 // Only keep the first of these commands that are found
172 if (foundCameraStartStop) {
173 resumeMission.removeAt(prefixCommandCount);
174 }
175 foundCameraStartStop = true;
176 break;
177 case MAV_CMD_IMAGE_START_CAPTURE:
178 if (resumeItem->param3() != 0) {
179 // Remove commands which do not trigger by time
180 resumeMission.removeAt(prefixCommandCount);
181 break;
182 }
183 if (foundCameraStartStop) {
184 // Only keep the first of these commands that are found
185 resumeMission.removeAt(prefixCommandCount);
186 }
187 foundCameraStartStop = true;
188 break;
189 default:
190 break;
191 }
192
193 prefixCommandCount--;
194 }
195
196 // Adjust sequence numbers and current item
197 int seqNum = 0;
198 for (int i=0; i<resumeMission.count(); i++) {
199 resumeMission[i]->setSequenceNumber(seqNum++);
200 }
201 int setCurrentIndex = addHomePosition ? 1 : 0;
202 resumeMission[setCurrentIndex]->setIsCurrentItem(true);
203
204 // Send to vehicle
206 for (int i=0; i<resumeMission.count(); i++) {
207 _writeMissionItems.append(new MissionItem(*resumeMission[i], this));
208 }
209 _resumeMission = true;
211}
212
214void MissionManager::_mavlinkMessageReceived(const mavlink_message_t& message)
215{
216 switch (message.msgid) {
217 case MAVLINK_MSG_ID_HIGH_LATENCY:
218 _handleHighLatency(message);
219 break;
220
221 case MAVLINK_MSG_ID_HIGH_LATENCY2:
222 _handleHighLatency2(message);
223 break;
224
225 case MAVLINK_MSG_ID_MISSION_CURRENT:
226 _handleMissionCurrent(message);
227 break;
228
229 case MAVLINK_MSG_ID_HEARTBEAT:
230 _handleHeartbeat(message);
231 break;
232 }
233}
234
235void MissionManager::_updateMissionIndex(int index)
236{
237 if (index != _currentMissionIndex) {
238 qCDebug(MissionManagerLog) << "_updateMissionIndex currentIndex:" << index;
239 _currentMissionIndex = index;
241 }
242
243 if (_currentMissionIndex != _lastCurrentIndex && _cachedLastCurrentIndex != _currentMissionIndex) {
244 // We have to be careful of an RTL sequence causing a change of index to the DO_LAND_START sequence. This also triggers
245 // a flight mode change away from mission flight mode. So we only update _lastCurrentIndex when the flight mode is mission.
246 // But we can run into problems where we may get the MISSION_CURRENT message for the RTL/DO_LAND_START sequenc change prior
247 // to the HEARTBEAT message which contains the flight mode change which will cause things to work incorrectly. To fix this
248 // We force the sequencing of HEARTBEAT following by MISSION_CURRENT by caching the possible _lastCurrentIndex update until
249 // the next HEARTBEAT comes through.
250 qCDebug(MissionManagerLog) << "_updateMissionIndex caching _lastCurrentIndex for possible update:" << _currentMissionIndex;
251 _cachedLastCurrentIndex = _currentMissionIndex;
252 }
253}
254
255void MissionManager::_handleHighLatency(const mavlink_message_t& message)
256{
257 mavlink_high_latency_t highLatency;
258 mavlink_msg_high_latency_decode(&message, &highLatency);
259 _updateMissionIndex(highLatency.wp_num);
260}
261
262void MissionManager::_handleHighLatency2(const mavlink_message_t& message)
263{
264 mavlink_high_latency2_t highLatency2;
265 mavlink_msg_high_latency2_decode(&message, &highLatency2);
266 _updateMissionIndex(highLatency2.wp_num);
267}
268
269void MissionManager::_handleMissionCurrent(const mavlink_message_t& message)
270{
271 mavlink_mission_current_t missionCurrent;
272 mavlink_msg_mission_current_decode(&message, &missionCurrent);
273 _updateMissionIndex(missionCurrent.seq);
274}
275
276void MissionManager::_handleHeartbeat(const mavlink_message_t& message)
277{
278 Q_UNUSED(message);
279
280 if (_cachedLastCurrentIndex != -1 && _vehicle->flightMode() == _vehicle->missionFlightMode()) {
281 qCDebug(MissionManagerLog) << "_handleHeartbeat updating lastCurrentIndex from cached value:" << _cachedLastCurrentIndex;
282 _lastCurrentIndex = _cachedLastCurrentIndex;
283 _cachedLastCurrentIndex = -1;
285 }
286}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_high_latency2_t mavlink_high_latency2_t
virtual bool sendHomePositionToVehicle() const
static int getComponentId()
static MAVLinkProtocol * instance()
static MissionCommandTree * instance()
const MissionCommandUIInfo * getUIInfo(Vehicle *vehicle, QGCMAVLink::VehicleClass_t vtolMode, MAV_CMD command)
UI Information associated with a mission command (MAV_CMD)
bool isTakeoffCommand(void) const
bool specifiesCoordinate(void) const
bool isStandaloneCoordinate(void) const
MAV_CMD command(void) const
Definition MissionItem.h:49
void setIsCurrentItem(bool isCurrentItem)
double param3(void) const
Definition MissionItem.h:56
void setSequenceNumber(int sequenceNumber)
void generateResumeMission(int resumeIndex)
void writeArduPilotGuidedMissionItem(const QGeoCoordinate &gotoCoord, bool altChangeOnly)
The PlanManager class is the base class for the Mission, GeoFence and Rally Point managers....
Definition PlanManager.h:14
int _currentMissionIndex
bool _resumeMission
QList< MissionItem * > _missionItems
Set of mission items on vehicle.
TransactionType_t _transactionInProgress
void currentIndexChanged(int currentIndex)
void lastCurrentIndexChanged(int lastCurrentIndex)
void _clearAndDeleteWriteMissionItems(void)
int _lastCurrentIndex
void _writeMissionItemsWorker(void)
void _connectToMavlink(void)
void _startAckTimeout(AckType_t ack)
bool inProgress(void) const
void inProgressChanged(bool inProgress)
QList< MissionItem * > _writeMissionItems
Set of mission items currently being written to vehicle.
@ AckGuidedItem
MISSION_ACK expected in response to ArduPilot guided mode single item send.
Definition PlanManager.h:90
Vehicle * _vehicle
WeakLinkInterfacePtr primaryLink() const
QGCMAVLink::VehicleClass_t vehicleClass(void) const
Definition Vehicle.h:447
QString missionFlightMode() const
Definition Vehicle.cc:2600
QString flightMode() const
Definition Vehicle.cc:1485
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:597
FirmwarePlugin * firmwarePlugin()
Provides access to the Firmware Plugin for this Vehicle.
Definition Vehicle.h:462
bool isOfflineEditingVehicle() const
Definition Vehicle.h:532
int id() const
Definition Vehicle.h:443
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
Definition Vehicle.cc:1401
void mavlinkMessageReceived(const mavlink_message_t &message)
int defaultComponentId() const
Definition Vehicle.h:700
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9