Skip to main content

autocore_std/fb/
memorystore.rs

1use crate::CommandClient;
2use mechutil::ipc::CommandMessage;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5enum State {
6    Idle,
7    WaitingForResponse,
8}
9
10/// Function block for reading a key from the MemoryStore.
11pub struct MemoryStoreRead {
12    /// True while waiting for a response from the memorystore.
13    pub busy: bool,
14    /// True when the read operation finishes successfully. Stays true until reset or restarted.
15    pub done: bool,
16    /// True if the operation failed.
17    pub error: bool,
18    /// Error description (empty when no error).
19    pub error_message: String,
20    /// The parsed JSON data from the key. `Some` after a successful read, `None` otherwise.
21    pub data: Option<serde_json::Value>,
22
23    state: State,
24    start_time: Option<std::time::Instant>,
25    pending_tid: Option<u32>,
26}
27
28impl MemoryStoreRead {
29    /// Create a new MemoryStoreRead function block.
30    pub fn new() -> Self {
31        Self {
32            busy: false,
33            done: false,
34            error: false,
35            error_message: String::new(),
36            data: None,
37            state: State::Idle,
38            start_time: None,
39            pending_tid: None,
40        }
41    }
42
43    /// The FB is currently awaiting a response.
44    pub fn is_busy(&self) -> bool {
45        self.busy
46    }
47
48    /// The last read resulted in an error.
49    pub fn is_error(&self) -> bool {
50        self.error
51    }
52
53    /// Start a new key read from the MemoryStore.
54    ///
55    /// # Arguments
56    /// * `key` - The path to the key (e.g., `"config.camera_settings"`).
57    /// * `client` - The IPC command client.
58    pub fn start(&mut self, key: &str, client: &mut CommandClient) {
59        self.error = false;
60        self.error_message.clear();
61        self.data = None;
62        self.done = false;
63
64        let msg = CommandMessage::read(&format!("memorystore.{}", key));
65        self.pending_tid = Some(client.send_message(msg));
66        self.start_time = Some(std::time::Instant::now());
67        self.busy = true;
68        self.state = State::WaitingForResponse;
69    }
70
71    /// Cancel the read and return to idle.
72    pub fn reset(&mut self) {
73        self.state = State::Idle;
74        self.busy = false;
75        self.done = false;
76        self.pending_tid = None;
77    }
78
79    /// Execute one scan cycle of the read state machine.
80    pub fn tick(&mut self, timeout_ms: u32, client: &mut CommandClient) {
81        match self.state {
82            State::Idle => {}
83
84            State::WaitingForResponse => {
85                if self.check_timeout(timeout_ms) {
86                    return;
87                }
88
89                if let Some(tid) = self.pending_tid {
90                    if let Some(resp) = client.take_response(tid) {
91                        self.pending_tid = None;
92                        self.busy = false;
93                        
94                        if resp.success {
95                            self.data = Some(resp.data);
96                            self.done = true;
97                            self.state = State::Idle;
98                        } else {
99                            self.set_error(&resp.error_message);
100                        }
101                    }
102                }
103            }
104        }
105    }
106
107    fn check_timeout(&mut self, timeout_ms: u32) -> bool {
108        if let Some(t) = self.start_time {
109            if t.elapsed().as_millis() as u32 > timeout_ms {
110                self.set_error("MemoryStore read timeout");
111                return true;
112            }
113        }
114        false
115    }
116
117    fn set_error(&mut self, message: &str) {
118        self.state = State::Idle;
119        self.busy = false;
120        self.error = true;
121        self.error_message = message.to_string();
122        self.pending_tid = None;
123    }
124}
125
126impl Default for MemoryStoreRead {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132
133/// Function block for writing a key to the MemoryStore.
134pub struct MemoryStoreWrite {
135    /// True while waiting for a response from the memorystore.
136    pub busy: bool,
137    /// True when the write operation finishes successfully. Stays true until reset or restarted.
138    pub done: bool,
139    /// True if the operation failed.
140    pub error: bool,
141    /// Error description (empty when no error).
142    pub error_message: String,
143
144    state: State,
145    start_time: Option<std::time::Instant>,
146    pending_tid: Option<u32>,
147}
148
149impl MemoryStoreWrite {
150    /// Create a new MemoryStoreWrite function block.
151    pub fn new() -> Self {
152        Self {
153            busy: false,
154            done: false,
155            error: false,
156            error_message: String::new(),
157            state: State::Idle,
158            start_time: None,
159            pending_tid: None,
160        }
161    }
162
163    /// The FB is currently awaiting a response.
164    pub fn is_busy(&self) -> bool {
165        self.busy
166    }
167
168    /// The last write resulted in an error.
169    pub fn is_error(&self) -> bool {
170        self.error
171    }
172
173    /// Start a new key write to the MemoryStore.
174    ///
175    /// # Arguments
176    /// * `key` - The path to the key (e.g., `"config.camera_settings"`).
177    /// * `data` - The JSON payload to save to the key.
178    /// * `client` - The IPC command client.
179    pub fn start(&mut self, key: &str, data: serde_json::Value, client: &mut CommandClient) {
180        self.error = false;
181        self.error_message.clear();
182        self.done = false;
183
184        let payload = serde_json::json!({
185            "value": data
186        });
187
188        let msg = CommandMessage::write(&format!("memorystore.{}", key), payload);
189        self.pending_tid = Some(client.send_message(msg));
190        self.start_time = Some(std::time::Instant::now());
191        self.busy = true;
192        self.state = State::WaitingForResponse;
193    }
194
195    /// Cancel the write and return to idle.
196    pub fn reset(&mut self) {
197        self.state = State::Idle;
198        self.busy = false;
199        self.done = false;
200        self.pending_tid = None;
201    }
202
203    /// Execute one scan cycle of the write state machine.
204    pub fn tick(&mut self, timeout_ms: u32, client: &mut CommandClient) {
205        match self.state {
206            State::Idle => {}
207
208            State::WaitingForResponse => {
209                if self.check_timeout(timeout_ms) {
210                    return;
211                }
212
213                if let Some(tid) = self.pending_tid {
214                    if let Some(resp) = client.take_response(tid) {
215                        self.pending_tid = None;
216                        self.busy = false;
217                        
218                        if resp.success {
219                            self.done = true;
220                            self.state = State::Idle;
221                        } else {
222                            self.set_error(&resp.error_message);
223                        }
224                    }
225                }
226            }
227        }
228    }
229
230    fn check_timeout(&mut self, timeout_ms: u32) -> bool {
231        if let Some(t) = self.start_time {
232            if t.elapsed().as_millis() as u32 > timeout_ms {
233                self.set_error("MemoryStore write timeout");
234                return true;
235            }
236        }
237        false
238    }
239
240    fn set_error(&mut self, message: &str) {
241        self.state = State::Idle;
242        self.busy = false;
243        self.error = true;
244        self.error_message = message.to_string();
245        self.pending_tid = None;
246    }
247}
248
249impl Default for MemoryStoreWrite {
250    fn default() -> Self {
251        Self::new()
252    }
253}