owon_spe/
lib.rs

1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "async")]
5use futures::{AsyncBufReadExt, AsyncWriteExt};
6use log::debug;
7use std::{
8    borrow::Cow,
9    io::{BufRead, BufReader},
10};
11use thiserror::Error;
12
13pub mod operations;
14use operations::iee4882::IdnOutput;
15use operations::measure::{MeasureAllInfoOutput, MeasureAllOutput};
16
17macro_rules! query_method {
18    (sync, $method: ident, $op:expr, $out:ident) => {
19        pub fn $method(&mut self) -> Result<$out, Error> {
20            self.run_operation($op)
21        }
22    };
23    (async, $method: ident, $op:expr, $out:ident) => {
24        pub async fn $method(&mut self) -> Result<$out, Error> {
25            self.run_operation($op).await
26        }
27    };
28}
29
30macro_rules! set_method {
31    (sync, $method: ident, $param: ident, $set:literal, $ty:ident) => {
32        pub fn $method(&mut self, $param: $ty) -> Result<(), Error> {
33            self.write_command(&format!($set, $param))
34        }
35    };
36
37    (async, $method: ident, $param: ident, $set:literal, $ty:ident) => {
38        pub async fn $method(&mut self, $param: $ty) -> Result<(), Error> {
39            self.write_command(&format!($set, $param)).await
40        }
41    };
42}
43
44macro_rules! write_method {
45    (sync, $method: ident, $set:literal) => {
46        pub fn $method(&mut self) -> Result<(), Error> {
47            self.write_command($set)
48        }
49    };
50
51    (async, $method: ident, $set:literal) => {
52        pub async fn $method(&mut self) -> Result<(), Error> {
53            self.write_command($set).await
54        }
55    };
56}
57
58macro_rules! implement_spe {
59    ($sync_or_async:ident) => {
60        query_method!($sync_or_async, idn, operations::iee4882::Idn {}, IdnOutput);
61        write_method!($sync_or_async, reset, "*RST");
62
63        write_method!($sync_or_async, enable_output, "OUTP ON");
64        write_method!($sync_or_async, disable_output, "OUTP OFF");
65        query_method!($sync_or_async, output, operations::output::Output {}, bool);
66
67        set_method!($sync_or_async, set_volt, volt, "VOLT {}", f32);
68        query_method!($sync_or_async, volt, operations::output::Volt {}, f32);
69
70        set_method!($sync_or_async, set_volt_limit, volt, "VOLT:LIMIT {}", f32);
71        query_method!(
72            $sync_or_async,
73            volt_limit,
74            operations::output::VoltLimit {},
75            f32
76        );
77
78        set_method!($sync_or_async, set_current, current, "CURR {}", f32);
79        query_method!($sync_or_async, current, operations::output::Current {}, f32);
80
81        set_method!(
82            $sync_or_async,
83            set_current_limit,
84            current,
85            "CURR:LIMIT {}",
86            f32
87        );
88        query_method!(
89            $sync_or_async,
90            current_limit,
91            operations::output::CurrentLimit {},
92            f32
93        );
94
95        query_method!(
96            $sync_or_async,
97            measure_volt,
98            operations::measure::Volt {},
99            f32
100        );
101        query_method!(
102            $sync_or_async,
103            measure_current,
104            operations::measure::Current {},
105            f32
106        );
107        query_method!(
108            $sync_or_async,
109            measure_power,
110            operations::measure::Power {},
111            f32
112        );
113
114        query_method!(
115            $sync_or_async,
116            measure_all,
117            operations::measure::MeasureAll {},
118            MeasureAllOutput
119        );
120
121        query_method!(
122            $sync_or_async,
123            measure_all_info,
124            operations::measure::MeasureAllInfo {},
125            MeasureAllInfoOutput
126        );
127    };
128}
129
130#[derive(Debug)]
131pub struct SPE<T>
132where
133    T: std::io::Write + std::io::Read,
134{
135    transport: BufReader<T>,
136}
137
138impl<T> SPE<T>
139where
140    T: std::io::Read + std::io::Write,
141{
142    pub fn new(transport: T) -> Self {
143        Self {
144            transport: BufReader::new(transport),
145        }
146    }
147
148    fn write_command(&mut self, cmd: &str) -> Result<(), Error> {
149        debug!("out: {}", cmd);
150        let writer = self.transport.get_mut();
151        writer.write_all(cmd.as_bytes())?;
152        writer.write_all(b"\r\n")?;
153        writer.flush()?;
154        Ok(())
155    }
156
157    fn run_operation<Op>(&mut self, operation: Op) -> Result<Op::Out, Error>
158    where
159        Op: Operation,
160    {
161        self.write_command(&operation.command())?;
162
163        let mut out: String = String::new();
164        self.transport.read_line(&mut out)?;
165        let trimmed = out.trim_end();
166
167        debug!("in: {}", trimmed);
168        operation.parse_line(trimmed)
169    }
170
171    implement_spe!(sync);
172}
173
174#[cfg(feature = "serialport")]
175#[cfg_attr(docsrs, doc(cfg(feature = "serialport")))]
176mod s {
177    use super::*;
178
179    #[cfg(unix)]
180    type SerialportNative = serialport::TTYPort;
181    #[cfg(windows)]
182    type SerialportNative = serialport::COMPort;
183
184    impl SPE<SerialportNative> {
185        pub fn from_serialport(path: &str) -> Result<Self, serialport::Error> {
186            debug!("Opening: {}", path);
187            let port = serialport::new(path, 115_200)
188                .timeout(std::time::Duration::from_secs(5))
189                .open_native()?;
190            Ok(Self::new(port))
191        }
192    }
193}
194
195#[cfg(feature = "async")]
196#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
197mod a {
198    use super::*;
199    #[derive(Debug)]
200    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
201    pub struct AsyncSPE<T>
202    where
203        T: futures::io::AsyncWrite + futures::io::AsyncRead + Unpin,
204    {
205        transport: futures::io::BufReader<T>,
206    }
207
208    impl<T> AsyncSPE<T>
209    where
210        T: futures::io::AsyncWrite + futures::io::AsyncRead + Unpin,
211    {
212        pub fn new(transport: T) -> Self {
213            Self {
214                transport: futures::io::BufReader::new(transport),
215            }
216        }
217
218        async fn write_command(&mut self, cmd: &str) -> Result<(), Error> {
219            debug!("out: {}", cmd);
220            let writer = self.transport.get_mut();
221            writer.write_all(cmd.as_bytes()).await?;
222            writer.write_all(b"\r\n").await?;
223            writer.flush().await?;
224            Ok(())
225        }
226
227        async fn run_operation<Op>(&mut self, operation: Op) -> Result<Op::Out, Error>
228        where
229            Op: Operation,
230        {
231            self.write_command(&operation.command()).await?;
232
233            let mut out: String = String::new();
234            self.transport.read_line(&mut out).await?;
235            let trimmed = out.trim_end();
236
237            debug!("in: {}", trimmed);
238            operation.parse_line(trimmed)
239        }
240
241        implement_spe!(async);
242    }
243}
244
245#[cfg(feature = "async")]
246#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
247pub use a::AsyncSPE;
248
249#[derive(Debug, Error)]
250pub enum Error {
251    #[error("Unexpected data")]
252    UnexpectedData,
253    #[error("IO Error: {0}")]
254    IOError(#[from] std::io::Error),
255}
256
257trait Operation {
258    type Out: PartialEq + std::fmt::Debug;
259
260    fn command(&self) -> Cow<'_, str>;
261    fn has_output(&self) -> bool {
262        true
263    }
264    fn parse_line(&self, line: &str) -> Result<Self::Out, Error>;
265}