A.R.G.U.S 2.1.0
Adaptive Real-Time Guardian for Unsafe Situations
Loading...
Searching...
No Matches
GuardianStateMachine.cpp
Go to the documentation of this file.
1
7#include <iostream>
8
9/*
10 GuardianStateMachine()
11
12 Note: enums (GuardianState, GuardianEvent, GuardianAction, FrameStatus)
13
14 Purpose:
15 - Set the initial state and internal counters
16 - Store the thresholds freezeCount and recoverCount (bad frames to freeze, good frames to recover)
17 - Ensure motion starts as allowed (not blocked)
18 - Initialize callbacks to null so we can safely check before calling
19
20 Parameters:
21 - fc (freezeCount): number of consecutive bad frames required to trigger a freeze
22 - rc (recoverCount): number of consecutive good frames required to clear a freeze
23*/
25 : currentState(GuardianState::SAFE_MONITORING), // Start in normal monitoring mode
26 badCount(0), // No bad frames detected yet
27 goodCount(0), // No good frames counted for recovery yet
28 freezeCount(fc), // Store freeze trigger threshold
29 recoverCount(rc), // Store recovery threshold
30 motionBlocked(false), // Motion should be allowed at beginning/start-up
31 onFreezeCallback(nullptr), // No freeze handler registered yet
32 onClearFreezeCallback(nullptr), // No clear handler registered yet
33 onStateChangeCallback(nullptr) { // No state change handler registered yet
34
35 // Log initialization to verify freezeCount and recoverCount values
36 std::cout << "[INIT] Guardian State Machine initialized with freezeCount=" << freezeCount << ", recoverCount=" << recoverCount << std::endl;
37}
38
39
40/*
41 executeAction()
42
43 Purpose:
44 - Perform the actual side effects of a GuardianAction
45 - This is separated from transition logic to keep state changes clean and consistent
46
47 Design idea:
48 - transitionTo() decides WHEN to execute actions
49 - executeAction() defines WHAT each action does
50
51 Actions:
52 - FREEZE_NOW: block motion + call external freeze callback (if registered)
53 - CLEAR_FREEZE: allow motion + call external clear callback (if registered)
54 - NONE: do nothing
55*/
56void GuardianStateMachine::executeAction(GuardianAction action) {
57 // switch decides which action to run
58 switch (action) {
59
61 // Immediately block motion (safety lock)
62 motionBlocked = true;
63
64 // Call it if external system registered a freeze handler such as emergency stop
65 if (onFreezeCallback) {
66 onFreezeCallback();
67 }
68
69 // Log the action for debugging/traceability
70 logAction("FREEZE_NOW - Motion blocked");
71 break;
72
74 // Allow motion again where the guardian releases safety lock
75 motionBlocked = false;
76
77 // Call it if external system registered a clear handler to resume motion control again
78 if (onClearFreezeCallback) {
79 onClearFreezeCallback();
80 }
81
82 // Log the action for debugging/traceability
83 logAction("CLEAR_FREEZE - Motion allowed");
84 break;
85
87 // No action but state change may still happen in some cases
88 break;
89 }
90}
91
92
93/*
94 transitionTo()
95
96 Purpose:
97 - Central function for safe and consistent state transitions
98 - Updates currentState
99 - Executes action - freeze/clear
100 - Notifies observers via callback
101 - Logs the transition
102
103 Explanations:
104 - Prevents duplicated state-change code in multiple places
105 - Guarantees transitions always:
106 (1) update state
107 (2) perform action
108 (3) notify callback
109 (4) log
110*/
111void GuardianStateMachine::transitionTo(GuardianState newState, GuardianAction action) {
112 // Store old state to log and notify what changed
113 GuardianState oldState = currentState;
114
115 // Update to the new state
116 currentState = newState;
117
118 // Perform the associated action (freeze or clear)
119 executeAction(action);
120
121 // Notify state change only if callback is set 'AND' and state actually changed
122 if (onStateChangeCallback && oldState != newState) {
123 onStateChangeCallback(oldState, newState);
124 }
125
126 // Log the action for debugging/traceability
127 logTransition(oldState, newState);
128}
129
130
131/*
132 logTransition()
133
134 Purpose:
135 - Print transition info only when an actual state change occurs
136 - Also prints current counters for debugging and traceability
137*/
138void GuardianStateMachine::logTransition(GuardianState from, GuardianState to) {
139 // Only print if transition happened
140 if (from != to) {
141 std::cout << "[TRANSITION] " << stateToString(from)
142 << " -> " << stateToString(to)
143 << " | bad_count=" << badCount
144 << " good_count=" << goodCount << std::endl;
145 }
146}
147
148
149/*
150 logAction()
151
152 Purpose:
153 - Log actions as separate messages from transitions
154 - This separation makes logs clearer:
155 transitions = state changes
156 actions = physical/system effects (freeze/clear)
157*/
158void GuardianStateMachine::logAction(const std::string& action) {
159 std::cout << "[ACTION] " << action << std::endl;
160}
161
162
163/*
164 processEvent()
165
166 Purpose:
167 - Main section of the state machine
168 - Takes a GuardianEvent (FRAME_GOOD / FRAME_BAD / OPERATOR_ACK) and updates counters + changes state when rules are met
169
170 Key safety behavior:
171 1) SAFE_MONITORING:
172 - count consecutive bad frames
173 - freeze if badCount >= freezeCount
174 - reset badCount on a good frame
175
176 2) FROZEN_UNSAFE:
177 - stay frozen regardless of frames
178 - only OPERATOR_ACK allows move to RESET_PENDING
179
180 3) RESET_PENDING:
181 - count consecutive good frames
182 - unfreeze if goodCount >= recoverCount
183 - reset goodCount when a bad frame appears
184*/
186 // Behavior depends entirely on currentState
187 switch (currentState) {
188 // STATE: SAFE_MONITORING
190
191 // Assume a bad frame is detected
192 if (event == GuardianEvent::FRAME_BAD) {
193 // Increase consecutive bad counter
194 badCount++;
195
196 // If the threshold freezeCount is reached to max, freeze immediately
197 if (badCount >= freezeCount) {
199 }
200
201 // Assume a good frame is detected
202 } else if (event == GuardianEvent::FRAME_GOOD) {
203 // Reset badCount because we require consecutive bad frames
204 badCount = 0;
205 }
206 break;
207
208 // STATE: FROZEN_UNSAFE
210 // Only operator reset to proceed
211 if (event == GuardianEvent::OPERATOR_ACK) {
212 // Start recovery checking when operator acknowledges
213 goodCount = 0;
214
215 // Move to RESET_PENDING (still frozen: no motion action yet)
217 }
218 break;
219
220 // STATE: RESET_PENDING (RECOVERY STATE)
222 if (event == GuardianEvent::FRAME_GOOD) {
223 // Increase consecutive good counter
224 goodCount++;
225
226 // Unfreeze when consecutive good frames detected
227 if (goodCount >= recoverCount) {
228 // Reset counters for clean future monitoring
229 goodCount = 0;
230 badCount = 0;
231
232 // Transition back to SAFE_MONITORING and clear freeze
234 }
235
236 } else if (event == GuardianEvent::FRAME_BAD) {
237 // Bad frame during recovery breaks confidence so reset goodCount and keep waiting
238 goodCount = 0;
239 }
240 break;
241 }
242}
243
244
245/*
246 processFrame()
247
248 Purpose:
249 - Convenience wrapper so external code can pass FrameStatus rather than GuardianEvent
250
251 Mapping:
252 - FrameStatus::FRAME_GOOD -> GuardianEvent::FRAME_GOOD
253 - FrameStatus::FRAME_BAD -> GuardianEvent::FRAME_BAD
254*/
258
259
260/*
261 operatorAcknowledge()
262
263 Purpose:
264 - Public API for human/operator acknowledgment
265 - Logs the acknowledgment and triggers OPERATOR_ACK event
266
267 Safety workflow:
268 - Something went wrong in current situation -> freeze
269 - Human confirms situation -> operator acknowledge
270 - System proves stable -> resume
271*/
273 std::cout << "[OPERATOR] Acknowledgment received" << std::endl;
275}
276
277
278/*
279 Callback setters
280
281 Purpose:
282 - Allow the state machine to control external systems without hardcoding hardware-specific logic into this class to make GuardianStateMachine reusable
283*/
284void GuardianStateMachine::setOnFreezeCallback(std::function<void()> callback) {
285 onFreezeCallback = callback; // Store freeze callback
286}
287
288void GuardianStateMachine::setOnClearFreezeCallback(std::function<void()> callback) {
289 onClearFreezeCallback = callback; // Store clear-freeze callback
290}
291
293 onStateChangeCallback = callback; // Store transition callback
294}
295
296/*
297 getState()
298
299 Purpose:
300 - Provide safe read-only access to internal data
301*/
303 return currentState; // Return the current state enum
304}
305
307 return motionBlocked; // True = frozen
308}
309
311 return !motionBlocked; // False = Resume
312}
313
315 return badCount; // Return current consecutive bad frame count
316}
317
319 return goodCount; // Return current consecutive good frame count
320}
321
322
323/*
324 stateToString()
325
326 Purpose:
327 - Convert enum to readable text for logs and UI debugging
328*/
330 switch (state) {
331 case GuardianState::SAFE_MONITORING: return "SAFE_MONITORING";
332 case GuardianState::FROZEN_UNSAFE: return "FROZEN_UNSAFE";
333 case GuardianState::RESET_PENDING: return "RESET_PENDING";
334 default: return "UNKNOWN"; // Defensive fallback
335 }
336}
337
338
339/*
340 getCurrentStateString()
341 - To get string form of current state
342*/
344 return stateToString(currentState);
345}
346
347
348/*
349 printStatus()
350
351 Purpose:
352 - Print a full snapshot of guardian internal status for testing and debugging
353*/
355 std::cout << "\n=== GUARDIAN STATUS ===" << std::endl;
356 std::cout << "State: " << getCurrentStateString() << std::endl;
357 std::cout << "Motion: " << (motionBlocked ? "BLOCKED" : "ALLOWED") << std::endl;
358 std::cout << "Bad Count: " << badCount << "/" << freezeCount << std::endl;
359 std::cout << "Good Count: " << goodCount << "/" << recoverCount << std::endl;
360 std::cout << "=======================\n" << std::endl;
361}
Finite-state safety supervisor used by ARGUS.
FrameStatus
Per-frame safety classification delivered by vision.
@ FRAME_GOOD
Frame classified as safe.
GuardianEvent
Events consumed by the FSM transition logic.
@ OPERATOR_ACK
Operator/manual acknowledge event.
@ FRAME_BAD
Unsafe-frame event.
@ FRAME_GOOD
Safe-frame event.
GuardianState
Internal FSM states.
@ SAFE_MONITORING
Normal operation; monitoring incoming frame status.
@ RESET_PENDING
Operator acknowledged; waiting for stable good frames.
@ FROZEN_UNSAFE
Unsafe condition latched; motion remains blocked.
GuardianAction
Side effects emitted by transitions.
@ CLEAR_FREEZE
Clear freeze and allow motion.
@ NONE
No hardware action required.
@ FREEZE_NOW
Immediate freeze/block action.
std::string getCurrentStateString() const
Return readable string for current state.
void operatorAcknowledge()
Inject an operator acknowledge event.
bool isMotionAllowed() const
True when motion is currently allowed.
int getBadCount() const
Get current consecutive bad-frame count.
void setOnStateChangeCallback(std::function< void(GuardianState, GuardianState)> callback)
Register callback fired on state transitions.
void processEvent(GuardianEvent event)
Process an explicit FSM event.
void setOnClearFreezeCallback(std::function< void()> callback)
Register callback fired when freeze is cleared.
GuardianState getState() const
Get current FSM state.
std::string stateToString(GuardianState state) const
Convert a state enum to a readable string.
void setOnFreezeCallback(std::function< void()> callback)
Register callback fired when freeze is commanded.
GuardianStateMachine(int fc=30, int rc=3)
Construct the FSM with frame-count hysteresis thresholds.
void processFrame(FrameStatus status)
Convenience wrapper that maps frame status to an FSM event.
void printStatus() const
Print a status snapshot for debugging.
int getGoodCount() const
Get current consecutive good-frame count.
bool isMotionBlocked() const
True when guardian currently blocks motion.