fleascope_rs/
serial_terminal.rs1use serialport::SerialPort;
2use std::collections::VecDeque;
3use std::io::{ErrorKind, Read, Write};
4use std::time::{Duration, Instant};
5
6const PROMPT: &[u8] = b"> ";
7
8#[derive(Debug)]
9pub struct StatelessFleaTerminal {
10 serial: Box<dyn SerialPort>,
11}
12
13pub struct IdleFleaTerminal {
14 inner: StatelessFleaTerminal,
15}
16
17pub struct ConnectionLostError;
18
19#[derive(Debug, thiserror::Error)]
20pub enum FleaTerminalError {
21 #[error("Serial port error: {0}")]
22 SerialPort(#[from] serialport::Error),
23
24 #[error("IO error: {0}")]
25 Io(#[from] std::io::Error),
26
27 #[error("Timeout error: Expected prompt within {timeout:?}.")]
28 Timeout { timeout: Duration },
29
30 #[error("Connection lost while waiting for response")]
31 ConnectionLost,
32}
33
34impl StatelessFleaTerminal {
35 pub fn new(port: &str) -> Result<Self, FleaTerminalError> {
37 #[cfg(feature = "puffin")]
38 puffin::profile_function!();
39
40 let serial = serialport::new(port, 9600)
41 .timeout(Duration::from_millis(70))
42 .open()?;
43
44 let mut terminal = Self { serial };
45
46 terminal.flush()?;
47 Ok(terminal)
48 }
49
50 fn flush(&mut self) -> Result<(), FleaTerminalError> {
52 log::debug!("Flushing serial port buffers once");
53 self.serial.clear(serialport::ClearBuffer::All)?;
54 while self.serial.bytes_to_read().unwrap() > 0 {
55 log::debug!("Flushing serial port buffers twice");
56 self.serial.clear(serialport::ClearBuffer::Input)?;
57 }
58 loop {
59 let mut buf = [0u8; 1024];
60 match self.serial.read(&mut buf) {
61 Ok(n) => {
62 if n == 0 {
63 break;
64 } else {
65 log::debug!("Flushing serial port buffers thrice");
66 }
67 }
68 Err(e) if e.kind() == ErrorKind::TimedOut => break,
69 Err(e) => return Err(FleaTerminalError::Io(e)),
70 }
71 }
72 Ok(())
73 }
74
75 fn read_chunk(&mut self, response: &mut Vec<u8>) -> Result<bool, ConnectionLostError> {
76 let mut read_buffer = [0u8; 1024]; #[cfg(feature = "puffin")]
78 puffin::profile_function!();
79 match self.serial.read(&mut read_buffer) {
80 Ok(bytes_read) if bytes_read > 0 => {
81 #[cfg(feature = "puffin")]
82 puffin::profile_scope!("process_chunk_data", format!("{}", bytes_read));
83
84 response.extend_from_slice(&read_buffer[..bytes_read]);
85
86 if response.len() >= PROMPT.len() {
88 let potential_prompt = &response[response.len() - PROMPT.len()..];
89 if potential_prompt == PROMPT {
90 Ok(true)
91 } else {
92 Ok(false)
93 }
94 } else {
95 Ok(false)
96 }
97 }
98 Ok(_) => {
99 Ok(false)
101 }
102 Err(e) if e.kind() == ErrorKind::TimedOut => {
103 Ok(false)
105 }
106 Err(e) if e.kind() == ErrorKind::BrokenPipe => Err(ConnectionLostError),
107 Err(e) if e.kind() == ErrorKind::UnexpectedEof => Err(ConnectionLostError),
108 Err(e) => {
109 tracing::info!("Serial read error (kind: {:?})...{e}", e.kind());
110 panic!("Serial read error: {}", e);
111 }
112 }
113 }
114
115 fn exec_sync(
116 &mut self,
117 command: &str,
118 timeout: Option<Duration>,
119 ) -> Result<Vec<u8>, FleaTerminalError> {
120 #[cfg(feature = "puffin")]
121 puffin::profile_function!();
122
123 {
124 #[cfg(feature = "puffin")]
125 puffin::profile_scope!("serial_write_command");
126 let command_with_newline = format!("{}\n", command);
128 self.serial.write_all(command_with_newline.as_bytes())?;
129 }
130
131 #[cfg(feature = "puffin")]
133 puffin::profile_scope!("serial_read_response");
134
135 let mut response = Vec::new();
136 let now = Instant::now();
137
138 loop {
139 #[cfg(feature = "puffin")]
140 puffin::profile_scope!("serial_read_chunk sync");
141 match self.read_chunk(&mut response) {
142 Ok(true) => break,
143 Ok(false) => {}
144 Err(ConnectionLostError) => return Err(FleaTerminalError::ConnectionLost),
145 };
146 if let Some(t) = timeout {
147 if now.elapsed() >= t {
148 return Err(FleaTerminalError::Timeout { timeout: t });
149 }
150 }
151 }
152
153 let response_without_prompt = &response[..response.len() - PROMPT.len()];
155
156 Ok(response_without_prompt.to_vec())
157 }
158
159 pub fn send_ctrl_c(&mut self) -> Result<(), FleaTerminalError> {
161 self.serial.write_all(&[0x03])?;
162 Ok(())
163 }
164
165 pub fn send_reset(&mut self) -> Result<(), FleaTerminalError> {
167 self.serial.write_all(b"reset\n")?;
168 Ok(())
169 }
170}
171
172impl IdleFleaTerminal {
173 pub fn exec_async(mut self, command: &str) -> BusyFleaTerminal {
174 #[cfg(feature = "puffin")]
175 puffin::profile_function!();
176
177 let command_with_newline = format!("{}\n", command);
178 self.inner
179 .serial
180 .write_all(command_with_newline.as_bytes())
181 .expect("Failed to write command to serial port");
182
183 BusyFleaTerminal {
184 inner: self.inner,
185 response: Vec::new(),
186 }
187 }
188 pub fn exec_sync(&mut self, command: &str, timeout: Option<Duration>) -> Vec<u8> {
189 #[cfg(feature = "puffin")]
190 puffin::profile_function!();
191
192 self.inner
193 .exec_sync(command, timeout)
194 .expect("Failed to execute command")
195 }
196}
197impl TryFrom<StatelessFleaTerminal> for IdleFleaTerminal {
198 type Error = (StatelessFleaTerminal, FleaTerminalError);
199
200 fn try_from(mut value: StatelessFleaTerminal) -> Result<Self, Self::Error> {
201 #[cfg(feature = "puffin")]
202 puffin::profile_function!();
203
204 log::debug!("Connected to FleaScope. Sending CTRL-C to reset.");
205 match value.send_ctrl_c() {
206 Ok(_) => {}
207 Err(e) => return Err((value, e)),
208 };
209 if let Err(e) = value.flush() {
210 return Err((value, e));
211 };
212
213 log::debug!("Turning on prompt");
214 if let Err(e) = value.exec_sync("prompt on", Some(Duration::from_secs(1))) {
215 return Err((value, e));
216 };
217
218 if let Err(e) = value.flush() {
219 return Err((value, e));
220 };
221 Ok(IdleFleaTerminal { inner: value })
222 }
223}
224
225pub struct BusyFleaTerminal {
226 inner: StatelessFleaTerminal,
227 response: Vec<u8>,
228}
229
230impl BusyFleaTerminal {
231 pub fn cancel(mut self) -> IdleFleaTerminal {
232 self.inner.send_ctrl_c().expect("Failed to send CTRL-C");
233 const PROMPT_LEN: usize = PROMPT.len();
234 const BUFFER_LEN: usize = 1024;
235 let mut prompt_buffer = VecDeque::with_capacity(PROMPT_LEN);
236 let mut read_buffer = [0u8; BUFFER_LEN];
237 loop {
238 match self.inner.serial.read(&mut read_buffer) {
239 Ok(bytes_read) if bytes_read >= PROMPT_LEN => {
240 prompt_buffer =
241 VecDeque::from(read_buffer[bytes_read - PROMPT_LEN..bytes_read].to_vec());
242 }
243 Ok(bytes_read) if bytes_read > 0 => {
244 for _i in 0..bytes_read {
245 prompt_buffer.pop_front();
246 }
247 prompt_buffer.extend(&read_buffer[..bytes_read]);
248 }
249 Ok(_) => continue, Err(e) if e.kind() == ErrorKind::TimedOut => continue, Err(e) => panic!("Serial read error: {}", e),
252 }
253 if prompt_buffer.len() == PROMPT.len()
255 && prompt_buffer.iter().copied().eq(PROMPT.iter().copied())
256 {
257 break;
258 }
259 }
260 self.inner.flush().expect("Failed to flush serial port");
261 IdleFleaTerminal { inner: self.inner }
262 }
263
264 fn into_result(self) -> (Vec<u8>, IdleFleaTerminal) {
265 #[cfg(feature = "puffin")]
266 puffin::profile_function!();
267
268 let response_without_prompt = &self.response[..self.response.len() - PROMPT.len()];
270 let response_str = response_without_prompt.to_vec();
271
272 (response_str, IdleFleaTerminal { inner: self.inner })
273 }
274
275 pub fn try_get_result(
276 mut self,
277 ) -> Result<Result<(Vec<u8>, IdleFleaTerminal), BusyFleaTerminal>, ConnectionLostError> {
278 #[cfg(feature = "puffin")]
279 puffin::profile_function!();
280
281 match self.inner.read_chunk(&mut self.response) {
294 Ok(true) => Ok(Ok(self.into_result())),
295 Ok(false) => Ok(Err(self)),
296 Err(ConnectionLostError) => Err(ConnectionLostError),
297 }
298 }
299}
300
301impl Read for BusyFleaTerminal {
302 fn read(&mut self, buffer: &mut [u8]) -> Result<usize, std::io::Error> {
303 #[cfg(feature = "puffin")]
304 puffin::profile_function!();
305
306 self.inner.serial.read(buffer)
307 }
308}