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