QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ErrorRecoveryBuilder.cc
Go to the documentation of this file.
2#include "QGCStateMachine.h"
4
5// -----------------------------------------------------------------------------
6// ErrorRecoveryBuilder
7// -----------------------------------------------------------------------------
8
10 : _machine(machine)
11 , _stateName(stateName)
12{
13}
14
16{
17 _action = std::move(action);
18 return *this;
19}
20
21ErrorRecoveryBuilder& ErrorRecoveryBuilder::retry(int maxRetries, int delayMsecs)
22{
23 _maxRetries = maxRetries;
24 _retryDelayMsecs = delayMsecs;
25 return *this;
26}
27
29{
30 _fallback = std::move(fallback);
31 return *this;
32}
33
35{
36 _rollback = std::move(rollback);
37 return *this;
38}
39
41{
42 _exhaustedBehavior = behavior;
43 return *this;
44}
45
47{
48 _timeoutMsecs = timeoutMsecs;
49 return *this;
50}
51
53{
54 auto* state = new ErrorRecoveryState(_stateName, _machine);
55
56 state->setAction(_action);
57 state->setFallback(_fallback);
58 state->setRollback(_rollback);
59 state->setRetry(_maxRetries, _retryDelayMsecs);
60 state->setTimeout(_timeoutMsecs);
61 state->setExhaustedBehavior(_exhaustedBehavior);
62
63 _machine->registerState(state);
64 return state;
65}
66
67// -----------------------------------------------------------------------------
68// ErrorRecoveryState
69// -----------------------------------------------------------------------------
70
71ErrorRecoveryState::ErrorRecoveryState(const QString& stateName, QState* parent)
72 : QGCState(stateName, parent)
73{
74 _retryTimer.setSingleShot(true);
75 connect(&_retryTimer, &QTimer::timeout, this, &ErrorRecoveryState::_executeAction);
76
77 _timeoutTimer.setSingleShot(true);
78 connect(&_timeoutTimer, &QTimer::timeout, this, &ErrorRecoveryState::_onTimeout);
79}
80
81void ErrorRecoveryState::setRetry(int maxRetries, int delayMsecs)
82{
83 _maxRetries = maxRetries;
84 _retryDelayMsecs = delayMsecs;
85}
86
87void ErrorRecoveryState::setTimeout(int timeoutMsecs)
88{
89 _timeoutMsecs = timeoutMsecs;
90}
91
93{
94 _exhaustedBehavior = behavior;
95}
96
98{
99 _currentAttempt = 0;
100 _triedFallback = false;
101 _successPhase.clear();
102
103 if (_timeoutMsecs > 0) {
104 _timeoutTimer.start(_timeoutMsecs);
105 }
106
107 _executeAction();
108}
109
110void ErrorRecoveryState::_executeAction()
111{
112 _currentAttempt++;
113 int totalAttempts = _maxRetries + 1;
114
115 qCDebug(QGCStateMachineLog) << stateName() << "attempt" << _currentAttempt << "of" << totalAttempts;
116
117 bool success = false;
118 if (_action) {
119 success = _action();
120 }
121
122 if (success) {
123 _timeoutTimer.stop();
124 _successPhase = QStringLiteral("primary");
125 qCDebug(QGCStateMachineLog) << stateName() << "primary action succeeded";
126 emit succeeded();
127 emit advance();
128 } else if (_currentAttempt <= _maxRetries) {
129 // Retry
130 qCDebug(QGCStateMachineLog) << stateName() << "retrying in" << _retryDelayMsecs << "ms";
131 emit retrying(_currentAttempt + 1, totalAttempts);
132 _retryTimer.start(_retryDelayMsecs);
133 } else {
134 // Primary exhausted
135 _handleFailure();
136 }
137}
138
139void ErrorRecoveryState::_handleFailure()
140{
141 // Try fallback if available and not already tried
142 if (_fallback && !_triedFallback) {
143 _triedFallback = true;
144 qCDebug(QGCStateMachineLog) << stateName() << "trying fallback";
145 emit tryingFallback();
146
147 bool success = _fallback();
148 if (success) {
149 _timeoutTimer.stop();
150 _successPhase = QStringLiteral("fallback");
151 qCDebug(QGCStateMachineLog) << stateName() << "fallback succeeded";
152 emit succeeded();
153 emit advance();
154 return;
155 }
156 }
157
158 // All options exhausted
159 _handleExhausted();
160}
161
162void ErrorRecoveryState::_handleExhausted()
163{
164 _timeoutTimer.stop();
165
166 // Execute rollback if available
167 if (_rollback) {
168 qCDebug(QGCStateMachineLog) << stateName() << "executing rollback";
169 emit rollingBack();
170 _rollback();
171 }
172
173 emit exhausted();
174
175 switch (_exhaustedBehavior) {
177 qCDebug(QGCStateMachineLog) << stateName() << "all options exhausted, emitting error";
178 emit error();
179 break;
180
182 qCDebug(QGCStateMachineLog) << stateName() << "all options exhausted, continuing anyway";
183 emit advance();
184 break;
185
187 qCWarning(QGCStateMachineLog) << stateName() << "all recovery options exhausted";
188 emit error();
189 break;
190
192 qCWarning(QGCStateMachineLog) << stateName() << "all recovery options exhausted, continuing";
193 emit advance();
194 break;
195 }
196}
197
198void ErrorRecoveryState::_onTimeout()
199{
200 qCDebug(QGCStateMachineLog) << stateName() << "operation timed out";
201 _retryTimer.stop();
202 _handleExhausted();
203}
ErrorRecoveryBuilder & retry(int maxRetries, int delayMsecs=1000)
ErrorRecoveryBuilder & withFallback(Action fallback)
Add a fallback action to try if primary fails.
ErrorRecoveryBuilder & withAction(Action action)
Set the primary action to execute.
ExhaustedBehavior
What to do when all recovery options are exhausted.
@ EmitAdvance
Continue anyway (skip)
@ LogAndAdvance
Log warning and continue.
@ LogAndError
Log warning and emit error()
@ EmitError
Emit error() signal (default)
ErrorRecoveryBuilder(QGCStateMachine *machine, const QString &stateName)
std::function< bool()> Action
std::function< void()> VoidAction
ErrorRecoveryBuilder & onExhausted(ExhaustedBehavior behavior)
Configure what happens when all options are exhausted.
ErrorRecoveryBuilder & withRollback(VoidAction rollback)
Add a rollback action to execute on failure.
QGCState * build()
Build and return the configured state.
ErrorRecoveryBuilder & withTimeout(int timeoutMsecs)
Set a timeout for the entire operation.
The state created by ErrorRecoveryBuilder.
void onEnter() override
Override to perform actions on state entry.
void setTimeout(int timeoutMsecs)
ErrorRecoveryState(const QString &stateName, QState *parent)
void retrying(int attempt, int maxAttempts)
void setExhaustedBehavior(ErrorRecoveryBuilder::ExhaustedBehavior behavior)
void setRetry(int maxRetries, int delayMsecs)
QString stateName() const
QGroundControl specific state machine with enhanced error handling.