scpi/lib.rs
1#![cfg_attr(not(feature = "std"), no_std)]
2
3//! 
4//! 
5//! [](https://codecov.io/gh/Atmelfan/scpi-rs)
6//! [](https://crates.io/crates/scpi)
7//! [](https://img.shields.io/github/license/Atmelfan/scpi-rs)
8//!
9//! This crate attempts to implement the IEE488.2 / SCPI protocol commonly used by measurement instruments and tools.
10//!
11//! * [SCPI-1999](http://www.ivifoundation.org/docs/scpi-99.pdf)
12//! * [IEEE 488.2](http://dx.doi.org/10.1109/IEEESTD.2004.95390)
13//!
14//! It does not require the std library (ie it's `no_std` compatible) or a system allocator (useful for embedded).
15//!
16//!
17//! # Scope
18//! The crate does not support any transport layer, it only reads ascii-strings (`[u8]`) and writes ascii responses.
19//!
20//! It does not implement any higher level functions/error handling other than SCPI parsing and response generation.
21//! See [scpi-contrib](https://crates.io/crates/scpi) for higher level abstractions.
22//!
23//! # Using this crate
24//! Add `scpi` to your dependencies:
25//! ```toml
26//! [dependencies]
27//! scpi = "1.0"
28//! ```
29//!
30//! # Features
31#![doc = document_features::document_features!()]
32//! (See rustdoc/docs.rs for available features)
33//!
34//! # Getting started
35//! Look at the [`example`](https://github.com/Atmelfan/scpi-rs/tree/master/example) for how to create a tree and run commands.
36//!
37//! Here's a good resource general SCPI style and good practices: [Keysight SCPI Training slides](https://www.keysight.com/us/en/assets/9921-01873/miscellaneous/SCPITrainingSlides.pdf)
38//!
39//! # Character coding
40//! SCPI is strictly ASCII and will throw a error InvalidCharacter if any non-ascii `(>127)` characters are encountered (Exception: Arbitrary data blocks).
41//!
42//! String parameters and reponse data should use byte-slices (`&[u8]`) with valid ASCII data.
43//!
44//! The str type can be decoded from either a string parameter or arbitrary block and will automatically be checked for UTF8 encoding.
45//! When used as response data a str will always return an arbitrary block.
46//!
47//! # Error handling
48//! The `Node::run(...)` function aborts execution and returns on the first error it encounters.
49//!
50//! User commands will often use functions which may return an error, these should mostly be propagated down to the parser by rusts `?` operator.
51//!
52//!
53//! # Limitations and differences
54//! * Overlapping commands are not supported, [Github issue](https://github.com/Atmelfan/scpi-rs/issues/23).
55//!
56//! # Contribution
57//! Contributions are welcome.
58//!
59//! # Project organisation:
60//! * `scpi` - Core parser crate
61//! * `scpi-contrib` - Mandatory command implementations and higher level abstractions
62//! * `scpi-derive` - Macro derive library, manly used to generate enum type parameters (see [option::ScpiEnum]).
63//!
64
65#[cfg(all(feature = "alloc", not(feature = "std")))]
66extern crate alloc;
67#[cfg(feature = "std")]
68extern crate std as alloc;
69
70use crate::error::Error;
71use core::any::Any;
72
73pub mod error;
74pub mod option;
75pub mod parser;
76pub mod tree;
77
78/// Prelude containing the most useful stuff
79///
80pub mod prelude {
81 pub use crate::{
82 error::{Error, ErrorCode},
83 Context, Device,
84 };
85}
86
87/// Re-export supported uom types if enabled
88#[cfg(feature = "uom")]
89pub mod units {
90 #[doc(no_inline)]
91 pub use uom;
92
93 #[cfg(feature = "unit-angle")]
94 pub use uom::si::f32::Angle;
95 #[cfg(feature = "unit-capacitance")]
96 pub use uom::si::f32::Capacitance;
97 #[cfg(feature = "unit-electric-charge")]
98 pub use uom::si::f32::ElectricCharge;
99 #[cfg(feature = "unit-electric-current")]
100 pub use uom::si::f32::ElectricCurrent;
101 #[cfg(feature = "unit-electric-potential")]
102 pub use uom::si::f32::ElectricPotential;
103 #[cfg(feature = "unit-electrical-conductance")]
104 pub use uom::si::f32::ElectricalConductance;
105 #[cfg(feature = "unit-electrical-resistance")]
106 pub use uom::si::f32::ElectricalResistance;
107 #[cfg(feature = "unit-energy")]
108 pub use uom::si::f32::Energy;
109 #[cfg(feature = "unit-frequency")]
110 pub use uom::si::f32::Frequency;
111 #[cfg(feature = "unit-inductance")]
112 pub use uom::si::f32::Inductance;
113 #[cfg(feature = "unit-power")]
114 pub use uom::si::f32::Power;
115 #[cfg(feature = "unit-ratio")]
116 pub use uom::si::f32::Ratio;
117 #[cfg(feature = "unit-thermodynamic-temperature")]
118 pub use uom::si::f32::ThermodynamicTemperature;
119 #[cfg(feature = "unit-time")]
120 pub use uom::si::f32::Time;
121}
122
123/// A basic device capable of executing commands and not much else
124pub trait Device {
125 /// Called when the parser encounters a syntax error or a command handler returns an error.
126 fn handle_error(&mut self, err: Error);
127}
128
129/// Context in which to execute a message.
130///
131/// Useful when multiple sources can execute commands.
132#[derive(Debug)]
133pub struct Context<'a> {
134 /// Does output buffer contain data?
135 pub mav: bool,
136
137 /// User context data.
138 ///
139 /// **Do not use this to pass application data!**
140 /// Use traits instead. It's only intended to pass along data from whatever context is running a command.
141 ///
142 /// For example: User authentication information if the call comes from an authenticated interface
143 /// or port number if the call comes from a serial port.
144 pub user: &'a dyn Any,
145}
146
147impl<'a> Default for Context<'a> {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153impl<'a> Context<'a> {
154 /// Create a new context
155 pub fn new() -> Self {
156 Context {
157 mav: false,
158 user: &(),
159 }
160 }
161
162 // Create a new context with user data
163 pub fn new_with_user(user: &'a dyn Any) -> Self {
164 Context { mav: false, user }
165 }
166
167 /// Get user context data.
168 ///
169 /// **DO NOT USE FOR APPLICATION DATA**
170 pub fn user<U: Any>(&'a self) -> Option<&'a U> {
171 self.user.downcast_ref()
172 }
173
174 /// Returns true if output buffer contains data
175 pub fn mav(&self) -> bool {
176 self.mav
177 }
178}
179
180#[cfg(test)]
181mod tests {
182 macro_rules! fixture_device {
183 ($dev:ident) => {
184 impl $crate::Device for $dev {
185 fn handle_error(&mut self, _err: $crate::error::Error) {}
186 }
187 };
188 }
189 pub(crate) use fixture_device;
190}