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"
38#include "WaitForSignalState.h"
39#include "WaitStateBase.h"
40#include "StateContext.h"
42#include "ErrorHandlers.h"
43
44#include <QtStateMachine/QStateMachine>
45#include <QtStateMachine/QFinalState>
46#include <QtStateMachine/QHistoryState>
47#include <QtCore/QAbstractAnimation>
48#include <QtCore/QJsonArray>
49#include <QtCore/QString>
50#include <QtCore/QVariant>
51
52#include <functional>
53
54class Vehicle;
58
60class QGCStateMachine : public QStateMachine
61{
62 Q_OBJECT
63 Q_DISABLE_COPY(QGCStateMachine)
64
65
66 Q_PROPERTY(QString currentStateName READ currentStateName NOTIFY currentStateNameChanged)
67
68
69 Q_PROPERTY(float progress READ progress NOTIFY progressUpdate)
70
71
72 Q_PROPERTY(bool running READ isRunning NOTIFY runningChanged)
73
74
75 Q_PROPERTY(QStringList stateHistory READ stateHistory NOTIFY stateHistoryChanged)
76
77public:
78 using EntryCallback = std::function<void()>;
79 using ExitCallback = std::function<void()>;
80 using EventHandler = std::function<bool(QEvent*)>;
81
82 QGCStateMachine(const QString& machineName, Vehicle* vehicle, QObject* parent = nullptr);
83
84 Vehicle *vehicle() const { return _vehicle; }
85 QString machineName() const { return objectName(); }
86
89 StateContext& context() { return _context; }
90 const StateContext& context() const { return _context; }
91
94 bool active() const { return isRunning(); }
95
98 QString currentStateName() const;
99
102 QStringList stateHistory() const { return _stateHistory; }
103
105 void setStateHistoryLimit(int limit) { _stateHistoryLimit = limit; }
106
107 // -------------------------------------------------------------------------
108 // Entry/Exit Callbacks
109 // -------------------------------------------------------------------------
110
112 void setOnEntry(EntryCallback callback) { _entryCallback = std::move(callback); }
113
115 void setOnExit(ExitCallback callback) { _exitCallback = std::move(callback); }
116
118 void setCallbacks(EntryCallback onEntry, ExitCallback onExit = nullptr);
119
120 // -------------------------------------------------------------------------
121 // Event Handling
122 // -------------------------------------------------------------------------
123
126 void setEventHandler(EventHandler handler) { _eventHandler = std::move(handler); }
127
131 void enablePropertyRestore();
132
134 bool isPropertyRestoreEnabled() const;
135
138 void setGlobalErrorState(QAbstractState* errorState);
139
141 QAbstractState* globalErrorState() const { return _globalErrorState; }
142
146 void registerState(QGCState* state);
147
150 void registerState(QGCAbstractState* state);
151
156 FunctionState* addFunctionState(const QString& stateName, std::function<void()> function);
157
163 AsyncFunctionState* addAsyncFunctionState(const QString& stateName,
165 int timeoutMsecs = 0);
166
177 QGCState* addErrorRecoveryState(const QString& stateName,
179 int maxRetries = 0,
180 int retryDelayMsecs = 1000,
182 ErrorRecoveryBuilder::Action fallback = nullptr,
183 ErrorRecoveryBuilder::VoidAction rollback = nullptr,
184 int timeoutMsecs = 0);
185
189 DelayState* addDelayState(int delayMsecs);
190
194 ParallelState* addParallelState(const QString& stateName);
195
200 void postEvent(const QString& eventName, const QVariant& data = QVariant(),
201 EventPriority priority = NormalPriority);
202
208 int postDelayedEvent(const QString& eventName, int delayMsecs, const QVariant& data = QVariant());
209
213 bool cancelDelayedEvent(int eventId);
214
215 // -------------------------------------------------------------------------
216 // State Configuration
217 // -------------------------------------------------------------------------
218
222 void setInitialState(QAbstractState* state, bool autoStart = false);
223
227 QGCFinalState* addFinalState(const QString& stateName = QString());
228
234 ConditionalState* addConditionalState(const QString& stateName,
236 ConditionalState::Action action = nullptr);
237
244 template<typename Func>
245 WaitForSignalState* addWaitForSignalState(const QString& stateName,
246 const QObject* sender,
247 Func signal,
248 int timeoutMsecs = 0)
249 {
250 auto* state = new WaitForSignalState(stateName, this, sender, signal, timeoutMsecs);
251 registerState(state);
252 return state;
253 }
254
259 SubMachineState* addSubMachineState(const QString& stateName, SubMachineState::MachineFactory factory);
260
266 EventQueuedState* addEventQueuedState(const QString& stateName,
267 const QString& eventName,
268 int timeoutMsecs = 0);
269
290 QState* createTimedActionState(const QString& stateName,
291 int durationMsecs,
292 std::function<void()> onEntry = nullptr,
293 std::function<void()> onExit = nullptr);
294
295 // -------------------------------------------------------------------------
296 // State Queries
297 // -------------------------------------------------------------------------
298
302 bool isStateActive(QAbstractState* state) const;
303
306 QSet<QAbstractState*> activeStates() const { return configuration(); }
307
311 QAbstractState* findState(const QString& stateName) const;
312
314 template<typename T>
315 QList<T*> findStates() const
316 {
317 return findChildren<T*>();
318 }
319
320 // -------------------------------------------------------------------------
321 // Error Handling & Recovery
322 // -------------------------------------------------------------------------
323
325 bool isInErrorState() const;
326
328 FunctionState* addLogAndContinueErrorState(const QString& stateName,
329 QAbstractState* nextState,
330 const QString& message = QString());
331
333 FunctionState* addLogAndStopErrorState(const QString& stateName,
334 const QString& message = QString());
335
337 QString lastError() const { return errorString(); }
338
341 void clearError(bool restart = false);
342
347 bool resetToState(QAbstractState* state);
348
352 bool recoverFromError();
353
358 void setRecoveryState(QAbstractState* state) { _recoveryState = state; }
359
361 QAbstractState* recoveryState() const { return _recoveryState; }
362
365 bool attemptRecovery();
366
367 // -------------------------------------------------------------------------
368 // Lifecycle Helpers
369 // -------------------------------------------------------------------------
370
372 void ensureRunning();
373
376 void stopMachine(bool deleteOnStop = true);
377
379 void restart();
380
381 // -------------------------------------------------------------------------
382 // Transition Helpers
383 // -------------------------------------------------------------------------
384
390 template<typename Func>
391 QSignalTransition* addTransition(QState* from, Func signal, QAbstractState* to,
392 QAbstractAnimation* animation = nullptr)
393 {
394 auto* transition = from->addTransition(from, signal, to);
395 if (animation) {
396 transition->addAnimation(animation);
397 }
398 return transition;
399 }
400
407 template<typename Func>
408 GuardedTransition* addGuardedTransition(QState* from, Func signal, QAbstractState* to,
410 QAbstractAnimation* animation = nullptr)
411 {
412 auto* transition = new GuardedTransition(from, signal, to, std::move(guard));
413 from->addTransition(transition);
414 if (animation) {
415 transition->addAnimation(animation);
416 }
417 return transition;
418 }
419
425 MachineEventTransition* addEventTransition(QState* from, const QString& eventName, QAbstractState* to,
426 QAbstractAnimation* animation = nullptr);
427
433 TimeoutTransition* addTimeoutTransition(QState* from, int timeoutMsecs, QAbstractState* to,
434 QAbstractAnimation* animation = nullptr);
435
442 template<typename SenderType, typename Func>
443 RetryTransition* addRetryTransition(SenderType* from, Func signal,
444 QAbstractState* to,
445 std::function<void()> retryAction,
446 int maxRetries = 1)
447 {
448 auto* transition = new RetryTransition(from, signal, to, std::move(retryAction), maxRetries);
449 from->addTransition(transition);
450 return transition;
451 }
452
459 template<typename SenderType, typename Func>
460 GuardedTransition* addConditionalTransition(SenderType* from, Func signal,
461 QAbstractState* to,
462 std::function<bool()> guard)
463 {
464 auto* transition = new GuardedTransition(from, signal, to, std::move(guard));
465 from->addTransition(transition);
466 return transition;
467 }
468
491 template<typename Sender, typename Signal>
492 QSignalTransition* addSelfLoopTransition(QState* state,
493 Sender* sender,
494 Signal signal,
495 std::function<void()> action)
496 {
497 auto* transition = state->addTransition(sender, signal, state);
498 if (action) {
499 connect(transition, &QAbstractTransition::triggered, this, std::move(action));
500 }
501 return transition;
502 }
503
513 template<typename Sender, typename Signal>
514 InternalTransition* addInternalTransition(QState* state,
515 Sender* sender,
516 Signal signal,
517 std::function<void()> action)
518 {
519 auto* transition = new InternalTransition(sender, signal, std::move(action));
520 state->addTransition(transition);
521 return transition;
522 }
523
524 // -------------------------------------------------------------------------
525 // State Introspection
526 // -------------------------------------------------------------------------
527
531 QList<QAbstractTransition*> transitionsFrom(QAbstractState* state) const;
532
536 QList<QAbstractTransition*> transitionsTo(QAbstractState* state) const;
537
541 QList<QAbstractState*> reachableFrom(QAbstractState* state) const;
542
546 QList<QAbstractState*> predecessorsOf(QAbstractState* state) const;
547
548 // -------------------------------------------------------------------------
549 // Debugging & Visualization
550 // -------------------------------------------------------------------------
551
555 QString dumpCurrentState() const;
556
560 QString dumpConfiguration() const;
561
563 void logCurrentState() const;
564
566 void logConfiguration() const;
567
569 void setHistoryRecordingEnabled(bool enabled, int maxEntries = 1000);
570 bool historyRecordingEnabled() const;
571 QString dumpRecordedHistory() const;
572 QJsonArray recordedHistoryJson() const;
573
575 void setProfilingEnabled(bool enabled);
576 bool profilingEnabled() const;
577 QString profilingSummary() const;
578
580 void setStructuredLoggingEnabled(bool enabled);
581 bool structuredLoggingEnabled() const;
582
586 QString exportAsDot() const;
587
591 QList<QAbstractState*> unreachableStates() const;
592
596 int maxPathLength() const;
597
601 QList<QAbstractState*> deadEndStates() const;
602
603 // -------------------------------------------------------------------------
604 // Progress Tracking
605 // -------------------------------------------------------------------------
606
622 void setProgressWeights(const QList<QPair<QAbstractState*, int>>& stateWeights);
623
627 void setSubProgress(float subProgress);
628
630 float progress() const;
631
633 void resetProgress();
634
635 // -------------------------------------------------------------------------
636 // Timeout Configuration
637 // -------------------------------------------------------------------------
638
643 void setTimeoutOverride(const QString& stateName, int timeoutMsecs);
644
647 void removeTimeoutOverride(const QString& stateName);
648
650 int timeoutOverride(const QString& stateName) const;
651
653 QHash<QString, int> allTimeoutOverrides() const { return _timeoutOverrides; }
654
656 void clearTimeoutOverrides() { _timeoutOverrides.clear(); }
657
659 QHash<QString, int> timeoutStats() const { return _timeoutStats; }
660
662 void recordTimeout(const QString& stateName);
663
665 void clearTimeoutStats() { _timeoutStats.clear(); }
666
667public slots:
669 void start();
670
671signals:
672 void error();
673
676 void progressUpdate(float progress);
677
681 void machineEvent(const QString& eventName);
682
685
688
691
692protected:
694 virtual void onEnter() {}
695
697 virtual void onLeave() {}
698
699 // QStateMachine overrides
700 void onEntry(QEvent* event) override;
701 void onExit(QEvent* event) override;
702 bool event(QEvent* event) override;
703
704private:
705 void _onStateEntered();
706 float _calculateProgress() const;
707
708 Vehicle* _vehicle = nullptr;
709 QAbstractState* _globalErrorState = nullptr;
710 QAbstractState* _recoveryState = nullptr;
711 bool _deleteOnStop = false;
712 EntryCallback _entryCallback;
713 ExitCallback _exitCallback;
714 EventHandler _eventHandler;
715
716 // Progress tracking
717 QList<QAbstractState*> _progressStates;
718 QList<int> _progressWeights;
719 int _progressTotalWeight = 0;
720 int _progressCurrentIndex = -1;
721 float _progressSubProgress = 0.0f;
722 float _progressLastEmitted = 0.0f;
723
724 // Inter-state data context
725 StateContext _context;
726
727 // QML state history
728 QStringList _stateHistory;
729 int _stateHistoryLimit = 20;
730
731 // Timeout configuration
732 QHash<QString, int> _timeoutOverrides;
733 QHash<QString, int> _timeoutStats;
734
735 // Optional runtime diagnostics helpers (lazy-created)
736 StateHistoryRecorder* _historyRecorder = nullptr;
737 StateMachineProfiler* _profiler = nullptr;
738 StateMachineLogger* _logger = nullptr;
739};
QString errorString
std::function< void(AsyncFunctionState *state)> SetupFunction
std::function< void()> Action
std::function< bool()> Predicate
Delays that state machine for the specified time in milliseconds.
Definition DelayState.h:9
ExhaustedBehavior
What to do when all recovery options are exhausted.
@ EmitError
Emit error() signal (default)
std::function< bool()> Action
std::function< void()> VoidAction
std::function< bool()> Guard
Final state for a QGCStateMachine with logging support.
QGroundControl specific state machine with enhanced error handling.
void runningChanged()
Emitted when the running state changes (for QML binding)
void start()
Start the state machine with debug logging.
void progressUpdate(float progress)
void onExit(QEvent *event) override
void currentStateNameChanged()
Emitted when the current state changes (for QML binding)
void machineEvent(const QString &eventName)
bool event(QEvent *event) override
void onEntry(QEvent *event) override
virtual void onLeave()
Override to perform actions when machine stops.
void stateHistoryChanged()
Emitted when the state history changes (for QML binding)
virtual void onEnter()
Override to perform actions when machine starts.
std::function< QGCStateMachine *(SubMachineState *parent)> MachineFactory