traffic_light_example/
traffic_light_example.rs

1//! Real-world example: Traffic Light Control System
2//!
3//! This example demonstrates how to use different feature combinations
4//! for a practical application.
5
6use rs_statemachine::*;
7use std::sync::Arc;
8
9#[derive(Debug, Clone, Hash, Eq, PartialEq)]
10enum TrafficLightState {
11    Red,
12    Yellow,
13    Green,
14    FlashingYellow, // For maintenance or low-traffic periods
15    Emergency,      // For emergency vehicle passage
16}
17
18impl State for TrafficLightState {}
19
20#[derive(Debug, Clone, Hash, Eq, PartialEq)]
21enum TrafficLightEvent {
22    Timer,
23    EmergencyVehicleDetected,
24    EmergencyCleared,
25    MaintenanceMode,
26    NormalMode,
27    PedestrianRequest,
28}
29
30impl Event for TrafficLightEvent {}
31
32#[derive(Debug, Clone)]
33struct TrafficContext {
34    intersection_id: String,
35    traffic_density: f32, // 0.0 to 1.0
36    pedestrian_waiting: bool,
37    emergency_active: bool,
38    time_in_state: std::time::Duration,
39}
40
41impl Context for TrafficContext {}
42
43/// Build a traffic light system with configurable features
44pub fn build_traffic_light_system(
45) -> StateMachine<TrafficLightState, TrafficLightEvent, TrafficContext> {
46    let mut builder =
47        StateMachineBuilderFactory::create::<TrafficLightState, TrafficLightEvent, TrafficContext>(
48        );
49
50    // Configure the state machine based on available features
51    configure_basic_transitions(&mut builder);
52
53    #[cfg(feature = "extended")]
54    configure_entry_exit_actions(&mut builder);
55
56    #[cfg(feature = "guards")]
57    configure_priority_transitions(&mut builder);
58
59    #[cfg(feature = "timeout")]
60    configure_timeouts(&mut builder);
61
62    builder.set_fail_callback(Arc::new(|state, event, ctx| {
63        eprintln!(
64            "WARNING: Invalid transition from {:?} with {:?} at intersection {}",
65            state, event, ctx.intersection_id
66        );
67    }));
68    builder.id("TrafficLightController").build()
69}
70
71/// Configure basic state transitions
72fn configure_basic_transitions<'a>(
73    builder: &'a mut StateMachineBuilder<TrafficLightState, TrafficLightEvent, TrafficContext>,
74) -> &'a mut StateMachineBuilder<TrafficLightState, TrafficLightEvent, TrafficContext> {
75    // Normal traffic light cycle
76    builder
77        .external_transition()
78        .from(TrafficLightState::Green)
79        .to(TrafficLightState::Yellow)
80        .on(TrafficLightEvent::Timer)
81        .perform(|_s, _e, ctx| {
82            println!("[{}] Changing to YELLOW", ctx.intersection_id);
83        });
84
85    builder
86        .external_transition()
87        .from(TrafficLightState::Yellow)
88        .to(TrafficLightState::Red)
89        .on(TrafficLightEvent::Timer)
90        .perform(|_s, _e, ctx| {
91            println!("[{}] Changing to RED", ctx.intersection_id);
92        });
93
94    builder
95        .external_transition()
96        .from(TrafficLightState::Red)
97        .to(TrafficLightState::Green)
98        .on(TrafficLightEvent::Timer)
99        .when(|_s, _e, ctx| !ctx.emergency_active)
100        .perform(|_s, _e, ctx| {
101            println!("[{}] Changing to GREEN", ctx.intersection_id);
102        });
103
104    // Emergency vehicle handling
105    builder
106        .external_transitions()
107        .from_among(vec![
108            TrafficLightState::Green,
109            TrafficLightState::Yellow,
110            TrafficLightState::Red,
111        ])
112        .to(TrafficLightState::Emergency)
113        .on(TrafficLightEvent::EmergencyVehicleDetected)
114        .perform(|from, _e, ctx| {
115            println!(
116                "[{}] EMERGENCY MODE! Was in {:?}",
117                ctx.intersection_id, from
118            );
119        });
120
121    builder
122        .external_transition()
123        .from(TrafficLightState::Emergency)
124        .to(TrafficLightState::Red)
125        .on(TrafficLightEvent::EmergencyCleared)
126        .perform(|_s, _e, ctx| {
127            println!(
128                "[{}] Emergency cleared, returning to RED",
129                ctx.intersection_id
130            );
131        });
132
133    // Maintenance mode
134    builder
135        .external_transitions()
136        .from_among(vec![
137            TrafficLightState::Green,
138            TrafficLightState::Yellow,
139            TrafficLightState::Red,
140        ])
141        .to(TrafficLightState::FlashingYellow)
142        .on(TrafficLightEvent::MaintenanceMode)
143        .perform(|_s, _e, ctx| {
144            println!(
145                "[{}] Entering maintenance mode - FLASHING YELLOW",
146                ctx.intersection_id
147            );
148        });
149
150    builder
151        .external_transition()
152        .from(TrafficLightState::FlashingYellow)
153        .to(TrafficLightState::Red)
154        .on(TrafficLightEvent::NormalMode)
155        .perform(|_s, _e, ctx| {
156            println!("[{}] Exiting maintenance mode", ctx.intersection_id);
157        });
158
159    builder
160}
161
162/// Configure entry and exit actions for states
163#[cfg(feature = "extended")]
164fn configure_entry_exit_actions(
165    builder: &mut StateMachineBuilder<TrafficLightState, TrafficLightEvent, TrafficContext>,
166) {
167    // Entry actions
168    builder.with_entry_action(TrafficLightState::Green, |_state, ctx| {
169        println!(
170            "[{}] GREEN light ON - Vehicles may proceed",
171            ctx.intersection_id
172        );
173        // In a real system, this would control the actual light hardware
174    });
175
176    builder.with_entry_action(TrafficLightState::Yellow, |_state, ctx| {
177        println!(
178            "[{}] YELLOW light ON - Prepare to stop",
179            ctx.intersection_id
180        );
181    });
182
183    builder.with_entry_action(TrafficLightState::Red, |_state, ctx| {
184        println!(
185            "[{}] RED light ON - Vehicles must stop",
186            ctx.intersection_id
187        );
188        if ctx.pedestrian_waiting {
189            println!(
190                "[{}] Pedestrian crossing signal activated",
191                ctx.intersection_id
192            );
193        }
194    });
195
196    builder.with_entry_action(TrafficLightState::Emergency, |_state, ctx| {
197        println!(
198            "[{}] EMERGENCY MODE - All lights RED except emergency route",
199            ctx.intersection_id
200        );
201        // Would trigger emergency protocols in real system
202    });
203
204    // Exit actions
205    builder.with_exit_action(TrafficLightState::Green, |_state, ctx| {
206        println!("[{}] GREEN light OFF", ctx.intersection_id);
207    });
208
209    builder.with_exit_action(TrafficLightState::Emergency, |_state, ctx| {
210        println!("[{}] Exiting emergency mode", ctx.intersection_id);
211    });
212}
213
214/// Configure priority-based transitions for complex scenarios
215#[cfg(feature = "guards")]
216fn configure_priority_transitions(
217    builder: &mut StateMachineBuilder<TrafficLightState, TrafficLightEvent, TrafficContext>,
218) {
219    // High-priority pedestrian crossing during low traffic
220    builder
221        .external_transition()
222        .from(TrafficLightState::Green)
223        .to(TrafficLightState::Yellow)
224        .on(TrafficLightEvent::PedestrianRequest)
225        .when(|_s, _e, ctx| ctx.traffic_density < 0.3 && ctx.pedestrian_waiting)
226        .with_priority(100)
227        .perform(|_s, _e, ctx| {
228            println!(
229                "[{}] Pedestrian priority - changing to yellow",
230                ctx.intersection_id
231            );
232        });
233
234    // Normal pedestrian request
235    builder
236        .external_transition()
237        .from(TrafficLightState::Green)
238        .to(TrafficLightState::Yellow)
239        .on(TrafficLightEvent::PedestrianRequest)
240        .when(|_s, _e, ctx| {
241            ctx.pedestrian_waiting && ctx.time_in_state > std::time::Duration::from_secs(10)
242        })
243        .with_priority(50)
244        .perform(|_s, _e, ctx| {
245            println!("[{}] Pedestrian request accepted", ctx.intersection_id);
246        });
247
248    // Rush hour handling - extend green time
249    builder
250        .internal_transition()
251        .within(TrafficLightState::Green)
252        .on(TrafficLightEvent::Timer)
253        .when(|_s, _e, ctx| {
254            ctx.traffic_density > 0.8 && ctx.time_in_state < std::time::Duration::from_secs(90)
255        })
256        .with_priority(200)
257        .perform(|_s, _e, ctx| {
258            println!(
259                "[{}] High traffic - extending green phase",
260                ctx.intersection_id
261            );
262        });
263}
264
265/// Configure timeouts for safety
266#[cfg(feature = "timeout")]
267fn configure_timeouts(
268    builder: &mut StateMachineBuilder<TrafficLightState, TrafficLightEvent, TrafficContext>,
269) {
270    use std::time::Duration;
271
272    // Safety timeout - Yellow shouldn't last too long
273    builder.with_state_timeout(
274        TrafficLightState::Yellow,
275        Duration::from_secs(5),
276        TrafficLightState::Red,
277        TrafficLightEvent::Timer,
278    );
279
280    // Emergency timeout - Auto-clear if no manual clear
281    builder.with_state_timeout(
282        TrafficLightState::Emergency,
283        Duration::from_secs(300), // 5 minutes
284        TrafficLightState::Red,
285        TrafficLightEvent::EmergencyCleared,
286    );
287}
288
289/// Simulate the traffic light system
290pub fn simulate_traffic_light_system() {
291    println!("=== Traffic Light Control System Demo ===\n");
292
293    let state_machine = build_traffic_light_system();
294
295    let mut context = TrafficContext {
296        intersection_id: "Main-St-First-Ave".to_string(),
297        traffic_density: 0.5,
298        pedestrian_waiting: false,
299        emergency_active: false,
300        time_in_state: std::time::Duration::from_secs(0),
301    };
302
303    // Normal cycle
304    println!("--- Normal Traffic Cycle ---");
305    let states_and_events = vec![
306        (TrafficLightState::Green, TrafficLightEvent::Timer),
307        (TrafficLightState::Yellow, TrafficLightEvent::Timer),
308        (TrafficLightState::Red, TrafficLightEvent::Timer),
309    ];
310
311    for (state, event) in states_and_events {
312        match state_machine.fire_event(state, event, context.clone()) {
313            Ok(new_state) => {
314                println!("  -> Now in {:?} state\n", new_state);
315            }
316            Err(e) => {
317                eprintln!("  ERROR: {}\n", e);
318            }
319        }
320        std::thread::sleep(std::time::Duration::from_millis(500));
321    }
322
323    // Emergency vehicle scenario
324    println!("--- Emergency Vehicle Detected ---");
325    context.emergency_active = true;
326
327    match state_machine.fire_event(
328        TrafficLightState::Green,
329        TrafficLightEvent::EmergencyVehicleDetected,
330        context.clone(),
331    ) {
332        Ok(new_state) => {
333            println!("  -> Now in {:?} state\n", new_state);
334
335            // Clear emergency after some time
336            std::thread::sleep(std::time::Duration::from_secs(2));
337            context.emergency_active = false;
338
339            match state_machine.fire_event(
340                new_state,
341                TrafficLightEvent::EmergencyCleared,
342                context.clone(),
343            ) {
344                Ok(cleared_state) => {
345                    println!("  -> Emergency cleared, now in {:?} state\n", cleared_state);
346                }
347                Err(e) => eprintln!("  ERROR clearing emergency: {}\n", e),
348            }
349        }
350        Err(e) => eprintln!("  ERROR handling emergency: {}\n", e),
351    }
352
353    // Feature-specific demonstrations
354    #[cfg(feature = "history")]
355    demonstrate_history(&state_machine);
356
357    #[cfg(feature = "metrics")]
358    demonstrate_metrics(&state_machine);
359
360    #[cfg(feature = "visualization")]
361    demonstrate_visualization(&state_machine);
362}
363
364#[cfg(feature = "history")]
365fn demonstrate_history(
366    state_machine: &StateMachine<TrafficLightState, TrafficLightEvent, TrafficContext>,
367) {
368    println!("--- Transition History ---");
369    let history = state_machine.get_history();
370    for (i, record) in history.iter().enumerate() {
371        println!(
372            "  {}. {:?} -> {:?} via {:?} ({})",
373            i + 1,
374            record.from,
375            record.to,
376            record.event,
377            if record.success { "✓" } else { "✗" }
378        );
379    }
380    println!();
381}
382
383#[cfg(feature = "metrics")]
384fn demonstrate_metrics(
385    state_machine: &StateMachine<TrafficLightState, TrafficLightEvent, TrafficContext>,
386) {
387    println!("--- Performance Metrics ---");
388    let metrics = state_machine.get_metrics();
389    println!("  Total transitions: {}", metrics.total_transitions);
390    println!("  Success rate: {:.1}%", metrics.success_rate() * 100.0);
391    if let Some(avg_time) = metrics.average_transition_time() {
392        println!("  Average transition time: {:?}", avg_time);
393    }
394    println!("  State visits:");
395    for (state, count) in &metrics.state_visit_counts {
396        println!("    {}: {} times", state, count);
397    }
398    println!();
399}
400
401#[cfg(feature = "visualization")]
402fn demonstrate_visualization(
403    state_machine: &StateMachine<TrafficLightState, TrafficLightEvent, TrafficContext>,
404) {
405    println!("--- State Machine Visualization ---");
406    println!("Saving to 'traffic_light.dot' and 'traffic_light.puml'");
407
408    let dot = state_machine.to_dot();
409    let plantuml = state_machine.to_plantuml();
410
411    // In a real application, you would write these to files
412    if let Err(e) = std::fs::write("traffic_light.dot", dot) {
413        eprintln!("Failed to write DOT file: {}", e);
414    }
415
416    if let Err(e) = std::fs::write("traffic_light.puml", plantuml) {
417        eprintln!("Failed to write PlantUML file: {}", e);
418    }
419
420    println!("  ✓ Visualization files created\n");
421}
422
423/// Example of using the traffic light system with different feature sets
424fn main() {
425    // Check which features are enabled and inform the user
426    println!("Enabled features:");
427    #[cfg(feature = "history")]
428    println!("  ✓ history");
429    #[cfg(feature = "extended")]
430    println!("  ✓ extended");
431    #[cfg(feature = "metrics")]
432    println!("  ✓ metrics");
433    #[cfg(feature = "guards")]
434    println!("  ✓ guards");
435    #[cfg(feature = "timeout")]
436    println!("  ✓ timeout");
437    #[cfg(feature = "visualization")]
438    println!("  ✓ visualization");
439    println!();
440
441    // Run the simulation
442    simulate_traffic_light_system();
443
444    // Show how to use different feature combinations
445    println!("--- Feature Combination Examples ---");
446
447    #[cfg(all(feature = "history", feature = "metrics"))]
448    {
449        println!("With history + metrics: Full audit trail with performance analysis");
450    }
451
452    #[cfg(all(feature = "extended", feature = "guards"))]
453    {
454        println!("With extended + guards: Complex state logic with prioritized transitions");
455    }
456
457    #[cfg(not(any(feature = "history", feature = "extended", feature = "metrics")))]
458    {
459        println!("Running with minimal features - core functionality only");
460    }
461}