QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ActuatorTesting.cc
Go to the documentation of this file.
1#include "ActuatorTesting.h"
2#include "MAVLinkLib.h"
3#include "Common.h"
4#include "AppMessages.h"
5#include "Vehicle.h"
7
8QGC_LOGGING_CATEGORY(ActuatorTestingLog, "Vehicle.Actuators.ActuatorTesting")
9
10using namespace ActuatorTesting;
11
13 : _vehicle(vehicle)
14{
15 _watchdogTimer.setInterval(100);
16 _watchdogTimer.setSingleShot(false);
17 connect(&_watchdogTimer, &QTimer::timeout, this, &ActuatorTest::watchdogTimeout);
18 _watchdogTimer.start();
19}
20
22{
23 _actuators->clearAndDeleteContents();
24 delete _allMotorsActuator;
25}
26
27void ActuatorTest::updateFunctions(const QList<Actuator*> &actuators)
28{
29 _actuators->clearAndDeleteContents();
30
31 if (_allMotorsActuator) {
32 _allMotorsActuator->deleteLater();
33 }
34
35 _allMotorsActuator = nullptr;
36
37 Actuator* motorActuator{nullptr};
38 for (const auto& actuator : actuators) {
39 if (actuator->isMotor()) {
40 motorActuator = actuator;
41 }
42 _actuators->append(actuator);
43 }
44 if (motorActuator) {
45 _allMotorsActuator = new Actuator(this, tr("All Motors"), motorActuator->min(), motorActuator->max(), motorActuator->defaultValue(),
46 motorActuator->function(), true);
47 }
48 resetStates();
49
50 emit actuatorsChanged();
51}
52
53void ActuatorTest::resetStates()
54{
55 _states.clear();
56 _currentState = -1;
57 for (int i = 0; i < _actuators->count(); ++i) {
58 _states.append(ActuatorState{});
59 }
60}
61
62void ActuatorTest::watchdogTimeout()
63{
64 for (int i = 0; i < _states.size(); ++i) {
65 if (_states[i].state == ActuatorState::State::Active) {
66 if (_states[i].lastUpdated.elapsed() > 100) {
67 qCWarning(ActuatorTestingLog) << "Stopping actuator due to timeout:" << i;
68 _states[i].state = ActuatorState::State::StopRequest;
69 }
70 }
71 }
72 sendNext();
73}
74
75void ActuatorTest::setChannelTo(int index, float value)
76{
77 if (!_active || index >= _states.size()) {
78 return;
79 }
80 qCDebug(ActuatorTestingLog) << "setting actuator: index:" << index << "value:" << value;
81
82 _states[index].value = value;
83 _states[index].state = ActuatorState::State::Active;
84 _states[index].lastUpdated.start();
85 sendNext();
86}
87
89{
90 if (index >= _states.size() || index < -1) {
91 return;
92 }
93 qCDebug(ActuatorTestingLog) << "stop actuator control: index:" << index;
94
95 if (index == -1) {
96 for (int i = 0; i < _states.size(); ++i) {
97 if (_states[i].state == ActuatorState::State::Active) {
98 _states[i].state = ActuatorState::State::StopRequest;
99 }
100 }
101 } else {
102 if (_states[index].state == ActuatorState::State::Active) {
103 _states[index].state = ActuatorState::State::StopRequest;
104 }
105 }
106 sendNext();
107}
108
109void ActuatorTest::setActive(bool active)
110{
111 qCDebug(ActuatorTestingLog) << "setting active: " << active;
112 if (!active) {
113 stopControl(-1);
114 }
115 _active = active;
116}
117
118void ActuatorTest::ackHandlerEntry(void* resultHandlerData, int /*compId*/, const mavlink_command_ack_t& ack, VehicleTypes::MavCmdResultFailureCode_t failureCode)
119{
120 ActuatorTest* actuatorTest = (ActuatorTest*)resultHandlerData;
121 actuatorTest->ackHandler(static_cast<MAV_RESULT>(ack.result), failureCode);
122}
123
124void ActuatorTest::ackHandler(MAV_RESULT commandResult, VehicleTypes::MavCmdResultFailureCode_t failureCode)
125{
126 // upon receiving an (n)ack, continuously cycle through the active actuators, one at a time
127 _commandInProgress = false;
129 // on timeout, just try the next one
130 sendNext();
131 } else if (commandResult == MAV_RESULT_ACCEPTED) {
132 if (_currentState != -1 && _states[_currentState].state == ActuatorState::State::Stopping) {
133 _states[_currentState].state = ActuatorState::State::NotActive;
134 }
135 sendNext();
136 } else { // failure
137 if (_currentState != -1) {
138 _states[_currentState].state = ActuatorState::State::NotActive;
139 }
140
141 if (!_hadFailure) {
142 QString message;
143 if (commandResult == MAV_RESULT_TEMPORARILY_REJECTED) {
144 message = tr("Actuator test command temporarily rejected");
145 } else if (commandResult == MAV_RESULT_DENIED) {
146 message = tr("Actuator test command denied");
147 } else if (commandResult == MAV_RESULT_UNSUPPORTED) {
148 message = tr("Actuator test command not supported");
149 } else {
150 message = tr("Actuator test command failed");
151 }
152 QGC::showAppMessage(message);
153 _hadFailure = true;
154 emit hadFailureChanged();
155 }
156 sendNext();
157 }
158}
159
160void ActuatorTest::sendNext()
161{
162 if (_commandInProgress) {
163 return;
164 }
165
166 // find the next actuator not in state NotActive
167 for (int i = 0; i < _states.size(); ++i) {
168 _currentState = (_currentState + 1) % _states.size();
169 Actuator* actuator = _actuators->value<Actuator*>(_currentState);
170 if (_states[_currentState].state == ActuatorState::State::Active) {
171 sendMavlinkRequest(actuator->function(), _states[_currentState].value, 1.f);
172 break;
173 } else if (_states[_currentState].state == ActuatorState::State::StopRequest) {
174 _states[_currentState].state = ActuatorState::State::Stopping;
175 sendMavlinkRequest(actuator->function(), NAN, 0.f);
176 break;
177 }
178 }
179}
180
181void ActuatorTest::sendMavlinkRequest(int function, float value, float timeout)
182{
183 qCDebug(ActuatorTestingLog) << "Sending actuator test function:" << function << "value:" << value;
184
185 // TODO: consider using a lower command timeout
186
187 Vehicle::MavCmdAckHandlerInfo_t handlerInfo = {};
188 handlerInfo.resultHandler = ackHandlerEntry;
189 handlerInfo.resultHandlerData = this;
190
192 &handlerInfo,
193 MAV_COMP_ID_AUTOPILOT1, // the ID of the autopilot
194 MAV_CMD_ACTUATOR_TEST, // the mavlink command
195 value, // value
196 timeout, // timeout
197 0, // unused parameter
198 0, // unused parameter
199 1000+function, // function
200 0, // unused parameter
201 0);
202 _commandInProgress = true;
203}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
struct __mavlink_command_ack_t mavlink_command_ack_t
Q_INVOKABLE void setActive(bool active)
void updateFunctions(const QList< Actuator * > &actuators)
Q_INVOKABLE void setChannelTo(int index, float value)
Q_INVOKABLE void stopControl(int index)
QmlObjectListModel * actuators()
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
T value(int index) const
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
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.