Skip to main content

autocore_std/fb/
datastore.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 file from the DataStore.
11pub struct DataStoreRead {
12    /// True while waiting for a response from the datastore.
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 file. `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 DataStoreRead {
29    /// Create a new DataStoreRead 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 file read from the DataStore.
54    ///
55    /// # Arguments
56    /// * `path` - The path to the file relative to the datastore root (e.g., `"config.json"`).
57    /// * `client` - The IPC command client.
58    pub fn start(&mut self, path: &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!("datastore.{}", path));
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("Datastore 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 DataStoreRead {
127    fn default() -> Self {
128        Self::new()
129    }
130}
131
132
133/// Function block for writing a file to the DataStore.
134pub struct DataStoreWrite {
135    /// True while waiting for a response from the datastore.
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 DataStoreWrite {
150    /// Create a new DataStoreWrite 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 file write to the DataStore.
174    ///
175    /// # Arguments
176    /// * `path` - The path to the file relative to the datastore root (e.g., `"config.json"`).
177    /// * `data` - The JSON payload to save to the file.
178    /// * `options` - Write options, such as `{"create_dirs": true}`.
179    /// * `client` - The IPC command client.
180    pub fn start(&mut self, path: &str, data: serde_json::Value, options: serde_json::Value, client: &mut CommandClient) {
181        self.error = false;
182        self.error_message.clear();
183        self.done = false;
184
185        let payload = serde_json::json!({
186            "value": data,
187            "options": options
188        });
189
190        let msg = CommandMessage::write(&format!("datastore.{}", path), payload);
191        self.pending_tid = Some(client.send_message(msg));
192        self.start_time = Some(std::time::Instant::now());
193        self.busy = true;
194        self.state = State::WaitingForResponse;
195    }
196
197    /// Cancel the write and return to idle.
198    pub fn reset(&mut self) {
199        self.state = State::Idle;
200        self.busy = false;
201        self.done = false;
202        self.pending_tid = None;
203    }
204
205    /// Execute one scan cycle of the write state machine.
206    pub fn tick(&mut self, timeout_ms: u32, client: &mut CommandClient) {
207        match self.state {
208            State::Idle => {}
209
210            State::WaitingForResponse => {
211                if self.check_timeout(timeout_ms) {
212                    return;
213                }
214
215                if let Some(tid) = self.pending_tid {
216                    if let Some(resp) = client.take_response(tid) {
217                        self.pending_tid = None;
218                        self.busy = false;
219                        
220                        if resp.success {
221                            self.done = true;
222                            self.state = State::Idle;
223                        } else {
224                            self.set_error(&resp.error_message);
225                        }
226                    }
227                }
228            }
229        }
230    }
231
232    fn check_timeout(&mut self, timeout_ms: u32) -> bool {
233        if let Some(t) = self.start_time {
234            if t.elapsed().as_millis() as u32 > timeout_ms {
235                self.set_error("Datastore write timeout");
236                return true;
237            }
238        }
239        false
240    }
241
242    fn set_error(&mut self, message: &str) {
243        self.state = State::Idle;
244        self.busy = false;
245        self.error = true;
246        self.error_message = message.to_string();
247        self.pending_tid = None;
248    }
249}
250
251impl Default for DataStoreWrite {
252    fn default() -> Self {
253        Self::new()
254    }
255}