plato_engine_block/
protocol.rs1use crate::engine::PlatoEngine;
4
5pub struct ProtocolHandler;
7
8impl ProtocolHandler {
9 pub fn handle(engine: &mut PlatoEngine, command: &str) -> String {
12 let cmd = command.trim();
13 let parts: Vec<&str> = cmd.splitn(3, ' ').collect();
14
15 match parts.get(0).map(|s| *s) {
16 Some("tick") => {
17 let tick = engine.tick();
18 let mut lines = Vec::new();
19 lines.push(format!("tick {} @ {:.3}s", tick.index, tick.timestamp));
20 for (name, val) in &tick.data {
21 lines.push(format!(" {} = {:.4}", name, val));
22 }
23 let alarms = engine.alarm_fires.drain(..).collect::<Vec<_>>();
25 for alarm_name in alarms {
26 lines.push(format!("! ALARM: {}", alarm_name));
27 }
28 lines.join("\n")
29 }
30 Some("history") => {
31 let n: usize = parts
32 .get(1)
33 .and_then(|s| s.parse().ok())
34 .unwrap_or(10);
35 let ticks = engine.history(n);
36 if ticks.is_empty() {
37 "no history".to_string()
38 } else {
39 let mut lines = Vec::new();
40 for tick in ticks {
41 let mut data_strs = Vec::new();
42 for (name, val) in &tick.data {
43 data_strs.push(format!("{}={:.4}", name, val));
44 }
45 lines.push(format!(
46 "tick {} @ {:.3}s: {}",
47 tick.index,
48 tick.timestamp,
49 data_strs.join(", ")
50 ));
51 }
52 lines.join("\n")
53 }
54 }
55 Some("subscribe") => {
56 engine.subscribe();
57 "subscribed".to_string()
58 }
59 Some("unsubscribe") => {
60 engine.unsubscribe();
61 "unsubscribed".to_string()
62 }
63 Some("alarm") => {
64 match parts.get(1).map(|s| *s) {
65 Some("list") => {
66 let alarms = &engine.alarms;
67 if alarms.is_empty() {
68 "no alarms".to_string()
69 } else {
70 let lines: Vec<String> = alarms
71 .iter()
72 .map(|a| {
73 format!("{}: {:?} (cooldown={}ticks)", a.name, a.state, a.cooldown_ticks)
74 })
75 .collect();
76 lines.join("\n")
77 }
78 }
79 _ => "usage: alarm list".to_string(),
80 }
81 }
82 Some("help") => {
83 "Commands:\n\
84 tick — take one tick, read all sensors\n\
85 history [N] — show last N ticks (default 10)\n\
86 <actuator> <val> — set actuator to value\n\
87 alarm list — list alarm states\n\
88 subscribe — subscribe to live updates\n\
89 unsubscribe — unsubscribe from updates\n\
90 help — show this help"
91 .to_string()
92 }
93 Some(name) => {
94 if let Some(val_str) = parts.get(1) {
96 if let Ok(value) = val_str.parse::<f64>() {
97 match engine.set_actuator(name, value) {
98 Ok(true) => format!("{} <- {:.4}", name, value),
99 Ok(false) => format!("error: {} rejected value", name),
100 Err(e) => format!("error: {}", e),
101 }
102 } else {
103 format!("unknown command: {}", cmd)
104 }
105 } else {
106 format!("unknown command: {}", cmd)
107 }
108 }
109 None => "error: empty command".to_string(),
110 }
111 }
112}