twine_ctl/shell/
serial.rs1use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader, Lines, ReadHalf, WriteHalf};
9use tokio::time::Duration;
10use tokio_serial::{SerialPortBuilderExt, SerialStream};
11use twine_rs_macros::TwineShell;
12
13use crate::error::TwineCtlError;
14
15use super::{SkipResultRead, TwineCtlShell};
16
17#[derive(TwineShell)]
18#[twine_shell(crate_path = "crate")]
19pub struct TwineCtlSerialShell {
20 prompt: Option<&'static str>,
21 lines: Lines<BufReader<ReadHalf<SerialStream>>>,
22 writer: WriteHalf<SerialStream>,
23 timeout_duration: Duration,
24}
25
26impl TwineCtlSerialShell {
27 pub async fn open(path: &str, baud: u32) -> Result<Self, TwineCtlError> {
29 let port = tokio_serial::new(path, baud).open_native_async()?;
30
31 let (reader, writer) = tokio::io::split(port);
32 let lines = BufReader::new(reader).lines();
33
34 let mut shell = TwineCtlSerialShell {
35 prompt: Some(">"),
36 lines,
37 writer,
38 timeout_duration: Duration::from_millis(1_000),
39 };
40
41 shell.enter_and_wait_for_prompt().await?;
42
43 Ok(shell)
44 }
45
46 async fn enter_and_wait_for_prompt(&mut self) -> Result<(), TwineCtlError> {
48 self.writer.write_all(b"\r\n\r\n").await?;
49 self.writer.flush().await?;
50 self.wait_for_prompt(self.timeout_duration).await
51 }
52}
53
54#[async_trait::async_trait]
55impl TwineCtlShell for TwineCtlSerialShell {
56 fn cmd_timeout_duration(&self) -> Duration {
57 self.timeout_duration
58 }
59
60 fn prompt(&self) -> Option<&'static str> {
61 self.prompt
62 }
63
64 async fn next_line(&mut self) -> Result<Option<String>, TwineCtlError> {
65 let line = self.lines.next_line().await?;
66 Ok(line)
67 }
68
69 async fn run(
70 &mut self,
71 cmd: &str,
72 timeout_duration: Duration,
73 skip_result_read: SkipResultRead,
74 ) -> Result<Vec<String>, TwineCtlError> {
75 self.writer.write_all(cmd.as_bytes()).await?;
76 self.writer.write_all(b"\r\n").await?;
77 self.writer.flush().await?;
78
79 match skip_result_read {
80 SkipResultRead::True => {
81 self.enter_and_wait_for_prompt().await?;
82 Ok(Vec::new())
83 }
84 SkipResultRead::False => self.read_result(cmd, timeout_duration).await,
85 }
86 }
87}