QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
QGCStateMachine.h
Go to the documentation of this file.
1#pragma once
2
5#include "ConditionalState.h"
6#include "DelayState.h"
7#include "EventQueuedState.h"
9#include "FunctionState.h"
10#include "LoopState.h"
12#include "QGCSignalTransition.h"
13#include "GuardedTransition.h"
14#include "InternalTransition.h"
17#include "RetryTransition.h"
18#include "TimeoutTransition.h"
19#include "QGCAbstractState.h"
20#include "QGCEventTransition.h"
22#include "QGCFinalState.h"
23#include "QGCHistoryState.h"
24#include "ParallelState.h"
26#include "RequestMessageState.h"
27#include "RetryState.h"
28#include "RollbackState.h"
31#include "SequenceState.h"
32#include "ShowAppMessageState.h"
33#include "SkippableAsyncState.h"
34#include "SubMachineState.h"
35#include "ProgressState.h"
39#include "WaitForSignalState.h"
40#include "WaitStateBase.h"
41#include "StateContext.h"
43#include "ErrorHandlers.h"
44
45#include <QtStateMachine/QStateMachine>
46#include <QtStateMachine/QFinalState>
47#include <QtStateMachine/QHistoryState>
48#include <QtCore/QAbstractAnimation>
49#include <QtCore/QJsonArray>
50#include <QtCore/QString>
51#include <QtCore/QVariant>
52
53#include <functional>
54
55class Vehicle;
59
62class QGCStateMachine : public QStateMachine
63{
64 Q_OBJECT
65 Q_DISABLE_COPY(QGCStateMachine)
66
67
68 Q_PROPERTY(QString currentStateName READ currentStateName NOTIFY currentStateNameChanged)
69
70
71 Q_PROPERTY(float progress READ progress NOTIFY progressUpdate)
72
73
74 Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
75
76
77 Q_PROPERTY(QStringList stateHistory READ stateHistory NOTIFY stateHistoryChanged)
78
79public:
80 using EntryCallback = std::function<void()>;
81 using ExitCallback = std::function<void()>;
82 using EventHandler = std::function<bool(QEvent*)>;
83
84 QGCStateMachine(const QString& machineName, Vehicle* vehicle, QObject* parent = nullptr);
85
86 Vehicle *vehicle() const { return _vehicle; }
87 QString machineName() const { return objectName(); }
88
91 StateContext& context() { return _context; }
92 const StateContext& context() const { return _context; }
93
96 bool active() const { return isRunning(); }
97
100 QString currentStateName() const;
101
104 QStringList stateHistory() const { return _stateHistory; }
105
107 void setStateHistoryLimit(int limit) { _stateHistoryLimit = limit; }
108
109 // -------------------------------------------------------------------------
110 // Entry/Exit Callbacks
111 // -------------------------------------------------------------------------
112
114 void setOnEntry(EntryCallback callback) { _entryCallback = std::move(callback); }
115
117 void setOnExit(ExitCallback callback) { _exitCallback = std::move(callback); }
118
121
122 // -------------------------------------------------------------------------
123 // Event Handling
124 // -------------------------------------------------------------------------
125
128 void setEventHandler(EventHandler handler) { _eventHandler = std::move(handler); }
129
134
136 bool isPropertyRestoreEnabled() const;
137
140 void setGlobalErrorState(QAbstractState* errorState);
141
143 QAbstractState* globalErrorState() const { return _globalErrorState; }
144
148 void registerState(QGCState* state);
149
152 void registerState(QGCAbstractState* state);
153
158 FunctionState* addFunctionState(const QString& stateName, std::function<void()> function);
159
165 AsyncFunctionState* addAsyncFunctionState(const QString& stateName,
167 int timeoutMsecs = 0);
168
179 QGCState* addErrorRecoveryState(const QString& stateName,
181 int maxRetries = 0,
182 int retryDelayMsecs = 1000,
184 ErrorRecoveryBuilder::Action fallback = nullptr,
185 ErrorRecoveryBuilder::VoidAction rollback = nullptr,
186 int timeoutMsecs = 0);
187
191 DelayState* addDelayState(int delayMsecs);
192
196 ParallelState* addParallelState(const QString& stateName);
197
202 void postEvent(const QString& eventName, const QVariant& data = QVariant(),
203 EventPriority priority = NormalPriority);
204
210 int postDelayedEvent(const QString& eventName, int delayMsecs, const QVariant& data = QVariant());
211
215 bool cancelDelayedEvent(int eventId);
216
217 // -------------------------------------------------------------------------
218 // State Configuration
219 // -------------------------------------------------------------------------
220
224 void setInitialState(QAbstractState* state, bool autoStart = false);
225
229 QGCFinalState* addFinalState(const QString& stateName = QString());
230
236 ConditionalState* addConditionalState(const QString& stateName,
238 ConditionalState::Action action = nullptr);
239
246 template<typename Func>
247 WaitForSignalState* addWaitForSignalState(const QString& stateName,
248 const QObject* sender,
249 Func signal,
250 int timeoutMsecs = 0)
251 {
252 auto* state = new WaitForSignalState(stateName, this, sender, signal, timeoutMsecs);
253 registerState(state);
254 return state;
255 }
256
261 SubMachineState* addSubMachineState(const QString& stateName, SubMachineState::MachineFactory factory);
262
268 EventQueuedState* addEventQueuedState(const QString& stateName,
269 const QString& eventName,
270 int timeoutMsecs = 0);
271
292 QState* createTimedActionState(const QString& stateName,
293 int durationMsecs,
294 std::function<void()> onEntry = nullptr,
295 std::function<void()> onExit = nullptr);
296
297 // -------------------------------------------------------------------------
298 // State Queries
299 // -------------------------------------------------------------------------
300
304 bool isStateActive(QAbstractState* state) const;
305
308 QSet<QAbstractState*> activeStates() const { return configuration(); }
309
313 QAbstractState* findState(const QString& stateName) const;
314
316 template<typename T>
317 QList<T*> findStates() const
318 {
319 return findChildren<T*>();
320 }
321
322 // -------------------------------------------------------------------------
323 // Error Handling & Recovery
324 // -------------------------------------------------------------------------
325
327 bool isInErrorState() const;
328
330 FunctionState* addLogAndContinueErrorState(const QString& stateName,
331 QAbstractState* nextState,
332 const QString& message = QString());
333
335 FunctionState* addLogAndStopErrorState(const QString& stateName,
336 const QString& message = QString());
337
339 QString lastError() const { return errorString(); }
340
343 void clearError(bool restart = false);
344
349 bool resetToState(QAbstractState* state);
350
354 bool recoverFromError();
355
360 void setRecoveryState(QAbstractState* state) { _recoveryState = state; }
361
363 QAbstractState* recoveryState() const { return _recoveryState; }
364
367 bool attemptRecovery();
368
369 // -------------------------------------------------------------------------
370 // Lifecycle Helpers
371 // -------------------------------------------------------------------------
372
374 void ensureRunning();
375
378 void stopMachine(bool deleteOnStop = true);
379
381 void restart();
382
383 // -------------------------------------------------------------------------
384 // Transition Helpers
385 // -------------------------------------------------------------------------
386
392 template<typename Func>
393 QSignalTransition* addTransition(QState* from, Func signal, QAbstractState* to,
394 QAbstractAnimation* animation = nullptr)
395 {
396 auto* transition = from->addTransition(from, signal, to);
397 if (animation) {
398 transition->addAnimation(animation);
399 }
400 return transition;
401 }
402
409 template<typename Func>
410 GuardedTransition* addGuardedTransition(QState* from, Func signal, QAbstractState* to,
412 QAbstractAnimation* animation = nullptr)
413 {
414 auto* transition = new GuardedTransition(from, signal, to, std::move(guard));
415 from->addTransition(transition);
416 if (animation) {
417 transition->addAnimation(animation);
418 }
419 return transition;
420 }
421
427 MachineEventTransition* addEventTransition(QState* from, const QString& eventName, QAbstractState* to,
428 QAbstractAnimation* animation = nullptr);
429
435 TimeoutTransition* addTimeoutTransition(QState* from, int timeoutMsecs, QAbstractState* to,
436 QAbstractAnimation* animation = nullptr);
437
444 template<typename SenderType, typename Func>
445 RetryTransition* addRetryTransition(SenderType* from, Func signal,
446 QAbstractState* to,
447 std::function<void()> retryAction,
448 int maxRetries = 1)
449 {
450 auto* transition = new RetryTransition(from, signal, to, std::move(retryAction), maxRetries);
451 from->addTransition(transition);
452 return transition;
453 }
454
461 template<typename SenderType, typename Func>
462 GuardedTransition* addConditionalTransition(SenderType* from, Func signal,
463 QAbstractState* to,
464 std::function<bool()> guard)
465 {
466 auto* transition = new GuardedTransition(from, signal, to, std::move(guard));
467 from->addTransition(transition);
468 return transition;
469 }
470
493 template<typename Sender, typename Signal>
494 QSignalTransition* addSelfLoopTransition(QState* state,
495 Sender* sender,
496 Signal signal,
497 std::function<void()> action)
498 {
499 auto* transition = state->addTransition(sender, signal, state);
500 if (action) {
501 connect(transition, &QAbstractTransition::triggered, this, std::move(action));
502 }
503 return transition;
504 }
505
515 template<typename Sender, typename Signal>
517 Sender* sender,
518 Signal signal,
519 std::function<void()> action)
520 {
521 auto* transition = new InternalTransition(sender, signal, std::move(action));
522 state->addTransition(transition);
523 return transition;
524 }
525
526 // -------------------------------------------------------------------------
527 // State Introspection
528 // -------------------------------------------------------------------------
529
533 QList<QAbstractTransition*> transitionsFrom(QAbstractState* state) const;
534
538 QList<QAbstractTransition*> transitionsTo(QAbstractState* state) const;
539
543 QList<QAbstractState*> reachableFrom(QAbstractState* state) const;
544
548 QList<QAbstractState*> predecessorsOf(QAbstractState* state) const;
549
550 // -------------------------------------------------------------------------
551 // Debugging & Visualization
552 // -------------------------------------------------------------------------
553
557 QString dumpCurrentState() const;
558
562 QString dumpConfiguration() const;
563
565 void logCurrentState() const;
566
568 void logConfiguration() const;
569
571 void setHistoryRecordingEnabled(bool enabled, int maxEntries = 1000);
572 bool historyRecordingEnabled() const;
573 QString dumpRecordedHistory() const;
574 QJsonArray recordedHistoryJson() const;
575
577 void setProfilingEnabled(bool enabled);
578 bool profilingEnabled() const;
579 QString profilingSummary() const;
580
582 void setStructuredLoggingEnabled(bool enabled);
583 bool structuredLoggingEnabled() const;
584
588 QString exportAsDot() const;
589
593 QList<QAbstractState*> unreachableStates() const;
594
598 int maxPathLength() const;
599
603 QList<QAbstractState*> deadEndStates() const;
604
605 // -------------------------------------------------------------------------
606 // Progress Tracking
607 // -------------------------------------------------------------------------
608
624 void setProgressWeights(const QList<QPair<QAbstractState*, int>>& stateWeights);
625
629 void setSubProgress(float subProgress);
630
632 float progress() const;
633
635 void resetProgress();
636
637 // -------------------------------------------------------------------------
638 // Timeout Configuration
639 // -------------------------------------------------------------------------
640
645 void setTimeoutOverride(const QString& stateName, int timeoutMsecs);
646
649 void removeTimeoutOverride(const QString& stateName);
650
652 int timeoutOverride(const QString& stateName) const;
653
655 QHash<QString, int> allTimeoutOverrides() const { return _timeoutOverrides; }
656
658 void clearTimeoutOverrides() { _timeoutOverrides.clear(); }
659
661 QHash<QString, int> timeoutStats() const { return _timeoutStats; }
662
664 void recordTimeout(const QString& stateName);
665
667 void clearTimeoutStats() { _timeoutStats.clear(); }
668
669public slots:
671 void start();
672
673signals:
674 void error();
675
679
683 void machineEvent(const QString& eventName);
684
687
690
693
694protected:
696 virtual void onEnter() {}
697
699 virtual void onLeave() {}
700
701 // QStateMachine overrides
702 void onEntry(QEvent* event) override;
703 void onExit(QEvent* event) override;
704 bool event(QEvent* event) override;
705
706private:
707 void _onStateEntered();
708 float _calculateProgress() const;
709
710 Vehicle* _vehicle = nullptr;
711 QAbstractState* _globalErrorState = nullptr;
712 QAbstractState* _recoveryState = nullptr;
713 bool _deleteOnStop = false;
714 EntryCallback _entryCallback;
715 ExitCallback _exitCallback;
716 EventHandler _eventHandler;
717
718 // Progress tracking
719 QList<QAbstractState*> _progressStates;
720 QList<int> _progressWeights;
721 int _progressTotalWeight = 0;
722 int _progressCurrentIndex = -1;
723 float _progressSubProgress = 0.0f;
724 float _progressLastEmitted = 0.0f;
725
726 // Inter-state data context
727 StateContext _context;
728
729 // QML state history
730 QStringList _stateHistory;
731 int _stateHistoryLimit = 20;
732
733 // Timeout configuration
734 QHash<QString, int> _timeoutOverrides;
735 QHash<QString, int> _timeoutStats;
736
737 // Optional runtime diagnostics helpers (lazy-created)
738 StateHistoryRecorder* _historyRecorder = nullptr;
739 StateMachineProfiler* _profiler = nullptr;
740 StateMachineLogger* _logger = nullptr;
741};
QString errorString
Calls a function when entered and waits for an external trigger to advance.
std::function< void(AsyncFunctionState *state)> SetupFunction
A state that evaluates a predicate on entry and either executes or skips.
std::function< void()> Action
std::function< bool()> Predicate
Delays that state machine for the specified time in milliseconds.
Definition DelayState.h:10
ExhaustedBehavior
What to do when all recovery options are exhausted.
@ EmitError
Emit error() signal (default)
std::function< bool()> Action
std::function< void()> VoidAction
Drives the MAVLink events protocol for a single component.
A state that waits for one or more named events before advancing.
A transition that only fires if a guard predicate returns true.
std::function< bool()> Guard
Transition that fires without exiting/re-entering the current state.
Transition that fires when a named event is posted to the state machine.
State that executes all child states in parallel.
Lightweight base class for simple states.
Final state for a QGCStateMachine with logging support.
QGroundControl specific state machine with enhanced error handling.
QAbstractState * recoveryState() const
Get the recovery state.
QList< T * > findStates() const
Find all states of a specific type.
FunctionState * addFunctionState(const QString &stateName, std::function< void()> function)
GuardedTransition * addGuardedTransition(QState *from, Func signal, QAbstractState *to, GuardedTransition::Guard guard, QAbstractAnimation *animation=nullptr)
void recordTimeout(const QString &stateName)
Record a timeout for statistics.
void stopMachine(bool deleteOnStop=true)
bool structuredLoggingEnabled() const
void clearError(bool restart=false)
QAbstractState * globalErrorState() const
Get the global error state.
MachineEventTransition * addEventTransition(QState *from, const QString &eventName, QAbstractState *to, QAbstractAnimation *animation=nullptr)
FunctionState * addLogAndStopErrorState(const QString &stateName, const QString &message=QString())
Create a logging error handler state that stops the machine.
void runningChanged()
Emitted when the running state changes (for QML binding)
int timeoutOverride(const QString &stateName) const
Get the timeout override for a state, or -1 if not set.
void start()
Start the state machine with debug logging.
void progressUpdate(float progress)
void setEventHandler(EventHandler handler)
bool isPropertyRestoreEnabled() const
Check if property restoration is enabled.
QList< QAbstractTransition * > transitionsFrom(QAbstractState *state) const
TimeoutTransition * addTimeoutTransition(QState *from, int timeoutMsecs, QAbstractState *to, QAbstractAnimation *animation=nullptr)
void logConfiguration() const
Log the full configuration to debug output.
bool profilingEnabled() const
ParallelState * addParallelState(const QString &stateName)
void setGlobalErrorState(QAbstractState *errorState)
QList< QAbstractState * > reachableFrom(QAbstractState *state) const
void onExit(QEvent *event) override
bool resetToState(QAbstractState *state)
QString dumpRecordedHistory() const
QGCState * addErrorRecoveryState(const QString &stateName, ErrorRecoveryBuilder::Action action, int maxRetries=0, int retryDelayMsecs=1000, ErrorRecoveryBuilder::ExhaustedBehavior exhaustedBehavior=ErrorRecoveryBuilder::EmitError, ErrorRecoveryBuilder::Action fallback=nullptr, ErrorRecoveryBuilder::VoidAction rollback=nullptr, int timeoutMsecs=0)
QList< QAbstractState * > predecessorsOf(QAbstractState *state) const
QSet< QAbstractState * > activeStates() const
std::function< void()> ExitCallback
QString exportAsDot() const
void setStructuredLoggingEnabled(bool enabled)
Enable/disable structured state-machine logger.
bool historyRecordingEnabled() const
std::function< void()> EntryCallback
Name of the currently active state (for QML binding)
void clearTimeoutOverrides()
Clear all timeout overrides.
void currentStateNameChanged()
Emitted when the current state changes (for QML binding)
EventQueuedState * addEventQueuedState(const QString &stateName, const QString &eventName, int timeoutMsecs=0)
void logCurrentState() const
Log the current state to debug output.
QJsonArray recordedHistoryJson() const
void setSubProgress(float subProgress)
QList< QAbstractState * > deadEndStates() const
const StateContext & context() const
int postDelayedEvent(const QString &eventName, int delayMsecs, const QVariant &data=QVariant())
QList< QAbstractState * > unreachableStates() const
void setStateHistoryLimit(int limit)
Set the maximum number of state history entries to keep.
QString machineName() const
QSignalTransition * addSelfLoopTransition(QState *state, Sender *sender, Signal signal, std::function< void()> action)
AsyncFunctionState * addAsyncFunctionState(const QString &stateName, AsyncFunctionState::SetupFunction setupFunction, int timeoutMsecs=0)
bool isStateActive(QAbstractState *state) const
void machineEvent(const QString &eventName)
SubMachineState * addSubMachineState(const QString &stateName, SubMachineState::MachineFactory factory)
int maxPathLength() const
QAbstractState * findState(const QString &stateName) const
StateContext & context()
void setOnEntry(EntryCallback callback)
Set a callback to be invoked when the machine starts.
QHash< QString, int > timeoutStats() const
Get timeout statistics (how often each state timed out)
void setHistoryRecordingEnabled(bool enabled, int maxEntries=1000)
Enable/disable structured transition history recording.
bool event(QEvent *event) override
void setProfilingEnabled(bool enabled)
Enable/disable state timing profiler.
bool cancelDelayedEvent(int eventId)
Vehicle * vehicle() const
void setInitialState(QAbstractState *state, bool autoStart=false)
void setOnExit(ExitCallback callback)
Set a callback to be invoked when the machine stops.
void setProgressWeights(const QList< QPair< QAbstractState *, int > > &stateWeights)
bool active() const
void setRecoveryState(QAbstractState *state)
DelayState * addDelayState(int delayMsecs)
void ensureRunning()
Start the machine if not already running.
QString dumpConfiguration() const
void clearTimeoutStats()
Clear timeout statistics.
QString lastError() const
Get the last error message (from Qt's internal error handling)
QList< QAbstractTransition * > transitionsTo(QAbstractState *state) const
QString currentStateName() const
InternalTransition * addInternalTransition(QState *state, Sender *sender, Signal signal, std::function< void()> action)
WaitForSignalState * addWaitForSignalState(const QString &stateName, const QObject *sender, Func signal, int timeoutMsecs=0)
QGCFinalState * addFinalState(const QString &stateName=QString())
void resetProgress()
Reset progress tracking (call when restarting the machine)
QStringList stateHistory() const
bool isInErrorState() const
Check if the machine is in an error state.
QHash< QString, int > allTimeoutOverrides() const
Get all timeout overrides.
void setTimeoutOverride(const QString &stateName, int timeoutMsecs)
QState * createTimedActionState(const QString &stateName, int durationMsecs, std::function< void()> onEntry=nullptr, std::function< void()> onExit=nullptr)
void onEntry(QEvent *event) override
void postEvent(const QString &eventName, const QVariant &data=QVariant(), EventPriority priority=NormalPriority)
virtual void onLeave()
Override to perform actions when machine stops.
RetryTransition * addRetryTransition(SenderType *from, Func signal, QAbstractState *to, std::function< void()> retryAction, int maxRetries=1)
void stateHistoryChanged()
Emitted when the state history changes (for QML binding)
QSignalTransition * addTransition(QState *from, Func signal, QAbstractState *to, QAbstractAnimation *animation=nullptr)
void setCallbacks(EntryCallback onEntry, ExitCallback onExit=nullptr)
Set both entry and exit callbacks.
QString profilingSummary() const
void removeTimeoutOverride(const QString &stateName)
GuardedTransition * addConditionalTransition(SenderType *from, Func signal, QAbstractState *to, std::function< bool()> guard)
float progress() const
Get the current overall progress (0.0 to 1.0)
virtual void onEnter()
Override to perform actions when machine starts.
void registerState(QGCState *state)
ConditionalState * addConditionalState(const QString &stateName, ConditionalState::Predicate predicate, ConditionalState::Action action=nullptr)
void restart()
Restart the machine (stop then start)
FunctionState * addLogAndContinueErrorState(const QString &stateName, QAbstractState *nextState, const QString &message=QString())
Create a logging error handler state that advances to nextState.
QString dumpCurrentState() const
Full-featured base class for QGroundControl state machine states.
Definition QGCState.h:23
Transition that retries an action N times before advancing to target state.
Type-safe context for passing data between states in a state machine.
Records state machine transitions for debugging and analysis.
Extended logging for state machines.
Performance profiler for state machines.
State that invokes a child state machine.
std::function< QGCStateMachine *(SubMachineState *parent)> MachineFactory
Transition that fires automatically after a specified delay.
Waits for a signal from a QObject before advancing.