QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MotorAssignment.cc
Go to the documentation of this file.
1#include "MotorAssignment.h"
2#include "MAVLinkLib.h"
3#include "AppMessages.h"
4#include "ActuatorOutputs.h"
6#include "Vehicle.h"
8
9#include <vector>
10
11QGC_LOGGING_CATEGORY(MotorAssignmentLog, "Vehicle.Actuators.MotorAssignment")
12
13MotorAssignment::MotorAssignment(QObject* parent, Vehicle* vehicle, QmlObjectListModel* actuators)
14 : QObject(parent), _vehicle(vehicle), _actuators(actuators)
15{
16 _spinTimer.setInterval(_spinTimeoutDefaultSec);
17 _spinTimer.setSingleShot(true);
18 connect(&_spinTimer, &QTimer::timeout, this, &MotorAssignment::spinTimeout);
19}
20
21bool MotorAssignment::initAssignment(int selectedActuatorIdx, int firstMotorsFunction, int numMotors)
22{
23 if (_state != State::Idle) {
24 qCWarning(MotorAssignmentLog) << "Already init/running";
25 }
26
27 // gather all the function facts
28 _functionFacts.clear();
29 for (int groupIdx = 0; groupIdx < _actuators->count(); groupIdx++) {
30 auto group = qobject_cast<ActuatorOutputs::ActuatorOutput*>(_actuators->get(groupIdx));
31 _functionFacts.append(QList<Fact*>{});
32 group->getAllChannelFunctions(_functionFacts.last());
33 }
34
35 // Check the current configuration for these cases:
36 // - no motors assigned: ask to assign to selected output
37 // - there are motors, but none assigned to the selected output: ask to remove and assign to selected output
38 // - partially assigned motors: abort with error
39 // - all assigned to current output: nothing further to do
40
41 int index = 0;
42 std::vector<bool> selectedAssignment(numMotors);
43 std::vector<bool> otherAssignment(numMotors);
44 for (auto& group : _functionFacts) {
45 bool selected = index == selectedActuatorIdx;
46 for (auto& fact : group) {
47 int currentFunction = fact->rawValue().toInt();
48 if (currentFunction >= firstMotorsFunction && currentFunction < firstMotorsFunction + numMotors) {
49 if (selected) {
50 selectedAssignment[currentFunction - firstMotorsFunction] = true;
51 } else {
52 otherAssignment[currentFunction - firstMotorsFunction] = true;
53 }
54 }
55 }
56 ++index;
57 }
58 int numAssigned = 0;
59 int numAssignedToSelected = 0;
60 for (int i = 0; i < numMotors; ++i) {
61 numAssigned += selectedAssignment[i] || otherAssignment[i];
62 numAssignedToSelected += selectedAssignment[i];
63 }
64
65 QString selectedActuatorOutputName = qobject_cast<ActuatorOutputs::ActuatorOutput*>(_actuators->get(selectedActuatorIdx))->label();
66
67 _assignMotors = false;
68 QString extraMessage;
69 if (numAssigned == 0 && _functionFacts[selectedActuatorIdx].size() >= numMotors) {
70 _assignMotors = true;
71 extraMessage = tr(
72R"(<br />No motors are assigned yet.
73By saying yes, all motors will be assigned to the first %1 channels of the selected output (%2)
74 (you can also first assign all motors, then start the identification).<br />)").arg(numMotors).arg(selectedActuatorOutputName);
75
76 } else if (numAssigned > 0 && numAssignedToSelected == 0 && _functionFacts[selectedActuatorIdx].size() >= numMotors) {
77 _assignMotors = true;
78 extraMessage = tr(
79R"(<br />Motors are currently assigned to a different output.
80By saying yes, all motors will be reassigned to the first %1 channels of the selected output (%2).<br />)")
81 .arg(numMotors).arg(selectedActuatorOutputName);
82
83 } else if (numAssigned < numMotors) {
84 _message = tr("Not all motors are assigned yet. Either clear all existing assignments or assign all motors to an output.");
85 emit messageChanged();
86 return false;
87 }
88
89 _message = tr(
90R"(This will automatically spin individual motors at 15% thrust.<br /><br />
91<b>Warning: Only proceed if you removed all propellers</b>.<br />
92%1
93<br />
94The procedure is as following:<br />
95- After confirming, the first motor starts to spin for 0.5 seconds.<br />
96- Then click on the motor that was spinning.<br />
97- The above steps are repeated for all motors.<br />
98- The motor output functions will automatically be reassigned by the selected order.<br />
99<br />
100Do you wish to proceed?)").arg(extraMessage);
101 emit messageChanged();
102
103 _selectedActuatorIdx = selectedActuatorIdx;
104 _firstMotorsFunction = firstMotorsFunction;
105 _numMotors = numMotors;
106 _selectedMotors.clear();
107 _state = State::Init;
108 emit activeChanged();
109 return true;
110}
111
113{
114 if (_state != State::Init) {
115 qCWarning(MotorAssignmentLog) << "Invalid state";
116 return;
117 }
118
119 if (_assignMotors) {
120 // clear all motors
121 for (auto& group : _functionFacts) {
122 for (auto& fact : group) {
123 int currentFunction = fact->rawValue().toInt();
124 if (currentFunction >= _firstMotorsFunction && currentFunction < _firstMotorsFunction + _numMotors) {
125 fact->setRawValue(0);
126 }
127 }
128 }
129 // assign to selected output
130 int index = 0;
131 for (auto& fact : _functionFacts[_selectedActuatorIdx]) {
132 fact->setRawValue(_firstMotorsFunction + index);
133 if (++index >= _numMotors) {
134 break;
135 }
136 }
137 }
138 _state = State::Running;
139 emit activeChanged();
140 _spinTimer.setInterval(_assignMotors ? _spinTimeoutHighSec : _spinTimeoutDefaultSec);
141 _spinTimer.start();
142}
143
144void MotorAssignment::selectMotor(int motorIndex)
145{
146 if (_state != State::Running) {
147 qCDebug(MotorAssignmentLog) << "Not running";
148 return;
149 }
150
151 _selectedMotors.append(motorIndex);
152 if (_selectedMotors.size() == _numMotors) {
153
154 // finished, change functions
155 for (auto& group : _functionFacts) {
156 for (auto& fact : group) {
157 int currentFunction = fact->rawValue().toInt();
158 if (currentFunction >= _firstMotorsFunction && currentFunction < _firstMotorsFunction + _numMotors) {
159 // this is set to a motor -> update
160 fact->setRawValue(_firstMotorsFunction + _selectedMotors[currentFunction - _firstMotorsFunction]);
162 }
163 }
164
165 _state = State::Idle;
166 emit activeChanged();
167 } else {
168 // spin the next motor after some time
169 _spinTimer.setInterval(_spinTimeoutDefaultSec);
170 _spinTimer.start();
171 }
172}
173
175{
176 if (!_commandInProgress) {
177 _spinTimer.stop();
178 int motorIndex = _selectedMotors.size();
179 sendMavlinkRequest(_firstMotorsFunction + motorIndex, 0.15f);
180 }
181}
182
184{
185 _spinTimer.stop();
186 _state = State::Idle;
187 emit activeChanged();
188 emit onAbort();
189}
190
191void MotorAssignment::spinTimeout()
192{
194}
195
196void MotorAssignment::ackHandlerEntry(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, VehicleTypes::MavCmdResultFailureCode_t failureCode)
197{
198 MotorAssignment* motorAssignment = (MotorAssignment*)resultHandlerData;
199 motorAssignment->ackHandler(static_cast<MAV_RESULT>(ack.result), failureCode);
200}
201
202void MotorAssignment::ackHandler(MAV_RESULT commandResult, VehicleTypes::MavCmdResultFailureCode_t failureCode)
203{
204 _commandInProgress = false;
205 if (failureCode != VehicleTypes::MavCmdResultFailureNoResponseToCommand && commandResult != MAV_RESULT_ACCEPTED) {
206 abort();
207 QGC::showAppMessage(tr("Actuator test command failed"));
208 }
209}
210
211void MotorAssignment::sendMavlinkRequest(int function, float value)
212{
213 qCDebug(MotorAssignmentLog) << "Sending actuator test function:" << function << "value:" << value;
214
215 Vehicle::MavCmdAckHandlerInfo_t handlerInfo = {};
216 handlerInfo.resultHandler = ackHandlerEntry;
217 handlerInfo.resultHandlerData = this;
218
220 &handlerInfo,
221 MAV_COMP_ID_AUTOPILOT1, // the ID of the autopilot
222 MAV_CMD_ACTUATOR_TEST, // the mavlink command
223 value, // value
224 0.5f, // timeout
225 0, // unused parameter
226 0, // unused parameter
227 1000+function, // function
228 0, // unused parameter
229 0);
230 _commandInProgress = true;
231}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
void activeChanged()
void selectMotor(int motorIndex)
bool initAssignment(int selectedActuatorIdx, int firstMotorsFunction, int numMotors)
void messageChanged()
Q_INVOKABLE QObject * get(int index)
int count() const override final
void sendMavCommandWithHandler(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)
Sends the command and calls the callback with the result.
Definition Vehicle.cc:2170
void showAppMessage(const QString &message, const QString &title)
Modal application message. Queued if the UI isn't ready yet.
Definition AppMessages.cc:9
Callback info bundle for sendMavCommandWithHandler.
MavCmdResultHandler resultHandler
nullptr for no handler
@ MavCmdResultFailureNoResponseToCommand
No response from vehicle to command.