QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
StateMachineProfiler.cc
Go to the documentation of this file.
2#include "QGCStateMachine.h"
4
5#include <QtCore/QJsonArray>
6#include <QtStateMachine/QAbstractState>
7
9 : QObject(machine)
10 , _machine(machine)
11{
12 connect(_machine, &QStateMachine::started, this, &StateMachineProfiler::_onMachineStarted);
13 connect(_machine, &QStateMachine::stopped, this, &StateMachineProfiler::_onMachineStopped);
14}
15
17{
18 if (_enabled == enabled) {
19 return;
20 }
21
22 _enabled = enabled;
23
24 if (_enabled) {
25 // Connect to all existing states
26 const auto states = _machine->findChildren<QAbstractState*>();
27 for (QAbstractState* state : states) {
28 auto enterConn = connect(state, &QAbstractState::entered, this, &StateMachineProfiler::_onStateEntered);
29 auto exitConn = connect(state, &QAbstractState::exited, this, &StateMachineProfiler::_onStateExited);
30 _stateConnections.append(enterConn);
31 _stateConnections.append(exitConn);
32 }
33 } else {
34 // Disconnect all state connections
35 for (const auto& conn : _stateConnections) {
36 disconnect(conn);
37 }
38 _stateConnections.clear();
39 }
40}
41
43{
44 _profiles.clear();
45 _totalRuntimeMs = 0;
46 _transitionCount = 0;
47 _currentStateName.clear();
48}
49
51{
52 return _profiles.value(stateName);
53}
54
56{
57 QStringList lines;
58 lines << QStringLiteral("=== State Machine Profile: %1 ===").arg(_machine->objectName());
59 lines << QStringLiteral("Total runtime: %1 ms").arg(_totalRuntimeMs);
60 lines << QStringLiteral("Transitions: %1").arg(_transitionCount);
61 lines << QString();
62
63 // Sort by total time descending
64 QList<StateProfile> sortedProfiles = _profiles.values();
65 std::sort(sortedProfiles.begin(), sortedProfiles.end(),
66 [](const StateProfile& a, const StateProfile& b) {
67 return a.totalTimeMs > b.totalTimeMs;
68 });
69
70 lines << QStringLiteral("%1 %2 %3 %4 %5 %6")
71 .arg("State", -30)
72 .arg("Count", 8)
73 .arg("Total(ms)", 10)
74 .arg("Avg(ms)", 10)
75 .arg("Min(ms)", 10)
76 .arg("Max(ms)", 10);
77 lines << QString(80, '-');
78
79 for (const auto& p : sortedProfiles) {
80 lines << QStringLiteral("%1 %2 %3 %4 %5 %6")
81 .arg(p.name.left(30), -30)
82 .arg(p.entryCount, 8)
83 .arg(p.totalTimeMs, 10)
84 .arg(p.averageTimeMs(), 10, 'f', 1)
85 .arg(p.minTimeMs == std::numeric_limits<qint64>::max() ? 0 : p.minTimeMs, 10)
86 .arg(p.maxTimeMs, 10);
87 }
88
89 return lines.join('\n');
90}
91
93{
94 QJsonObject root;
95 root["machineName"] = _machine->objectName();
96 root["totalRuntimeMs"] = _totalRuntimeMs;
97 root["transitionCount"] = _transitionCount;
98
99 QJsonArray statesArray;
100 for (auto it = _profiles.constBegin(); it != _profiles.constEnd(); ++it) {
101 QJsonObject stateObj;
102 stateObj["name"] = it.value().name;
103 stateObj["entryCount"] = it.value().entryCount;
104 stateObj["totalTimeMs"] = it.value().totalTimeMs;
105 stateObj["averageTimeMs"] = it.value().averageTimeMs();
106 stateObj["minTimeMs"] = it.value().minTimeMs == std::numeric_limits<qint64>::max()
107 ? 0 : it.value().minTimeMs;
108 stateObj["maxTimeMs"] = it.value().maxTimeMs;
109 statesArray.append(stateObj);
110 }
111 root["states"] = statesArray;
112
113 return root;
114}
115
117{
118 qCDebug(QGCStateMachineLog).noquote() << summary();
119}
120
121void StateMachineProfiler::_onMachineStarted()
122{
123 if (!_enabled) return;
124
125 _machineTimer.start();
126 qCDebug(QGCStateMachineLog) << "Profiler: Machine started -" << _machine->objectName();
127}
128
129void StateMachineProfiler::_onMachineStopped()
130{
131 if (!_enabled) return;
132
133 _totalRuntimeMs = _machineTimer.elapsed();
134 qCDebug(QGCStateMachineLog) << "Profiler: Machine stopped -" << _machine->objectName()
135 << "total runtime:" << _totalRuntimeMs << "ms";
136}
137
138void StateMachineProfiler::_onStateEntered()
139{
140 if (!_enabled) return;
141
142 auto* state = qobject_cast<QAbstractState*>(sender());
143 if (!state) return;
144
145 _currentStateName = state->objectName();
146 _stateTimer.start();
147
148 StateProfile& profile = _profiles[_currentStateName];
149 profile.name = _currentStateName;
151 profile.lastEntryTime = _machineTimer.elapsed();
152
153 _transitionCount++;
154}
155
156void StateMachineProfiler::_onStateExited()
157{
158 if (!_enabled) return;
159
160 auto* state = qobject_cast<QAbstractState*>(sender());
161 if (!state) return;
162
163 QString stateName = state->objectName();
164 qint64 elapsed = _stateTimer.elapsed();
165
166 StateProfile& profile = _profiles[stateName];
167 profile.totalTimeMs += elapsed;
168 profile.minTimeMs = qMin(profile.minTimeMs, elapsed);
169 profile.maxTimeMs = qMax(profile.maxTimeMs, elapsed);
170}
QGroundControl specific state machine with enhanced error handling.
QString summary() const
Get a human-readable summary.
void logProfile() const
Log the profile to debug output.
QJsonObject toJson() const
Export profile data as JSON.
StateMachineProfiler(QGCStateMachine *machine)
void reset()
Reset all profiling data.
StateProfile profile(const QString &stateName) const
Get profile data for a specific state.
void setEnabled(bool enabled)
Enable or disable profiling.