instrument_ctl/lib.rs
1//! # Instrument Interface
2//!
3//! Connect to, command, and query intruments such as oscilloscopes.
4//!
5//! Currently only supports USBTMC connections.
6//!
7//! Supported interfaces:
8//! - [x] USBTMC
9//! - [ ] VXI-11
10//!
11//! Considered interfaces:
12//! - [ ] GPIB (reason not to: it is old)
13//! - [ ] VICP (reason not to: proprietary)
14//! - [ ] LSIB (reason not to: way too proprietary)
15//!
16//! Eventually, and depending on my motivation, this project will become a pure Rust VISA driver. However, in my current position this seems to be a pipe dream.
17//!
18//! ## Usage
19//!
20//! To use, add the following line to your project's Cargo.toml dependencies:
21//! ```toml
22//! rs-instrument-ctl = "0.1"
23//! ```
24//!
25//! ## Example
26//!
27//! The example below demonstrates how to connect to, send commands to and query the device.
28//!
29//! ```rust
30//! use rs_instrument_ctl::Instrument;
31//!
32//! const VISA_ADDRESS: &str = "USB::0x0000::0x0000::SERIALNUMBER::INSTR"
33//!
34//! fn main() {
35//! // connect to the instrument
36//! let instrument = Instrument::connect(VISA_ADDRESS).expect("failed to connect to device");
37//!
38//! // send a command
39//! instrument.command("*IDN").expect("failed to send command");
40//!
41//! // query the device and return the response as a string
42//! let response: String = instrument.query("*IDN?").expect("failed to query");
43//!
44//! // query the device and return the response as a vector of bytes
45//! let response: Vec<u8> = instrument.query("*IDN?").expect("failed to query");
46//! }
47//! ```
48//!
49
50use std::time::Duration;
51use std::sync::Arc;
52
53mod interface;
54use interface::InstrumentClient;
55use rs_usbtmc::UsbtmcClient;
56
57use anyhow::{Result, anyhow};
58
59pub struct Instrument {
60 client: Arc<dyn InstrumentClient + 'static>,
61}
62
63unsafe impl Send for Instrument {}
64unsafe impl Sync for Instrument {}
65
66
67impl Instrument {
68 /// ## Connect
69 ///
70 /// Connect to a compatible device with a VISA address
71 ///
72 /// ### Arguments
73 /// - `address` -> a valid VISA address
74 ///
75 pub fn connect(address: &str) -> Result<Instrument> {
76 // parse the address to figure out which interface to use
77 let addr: Vec<&str> = address.split("::").collect();
78
79 let interface = addr[0];
80
81 if interface.contains("USB") {
82 let vid = match addr[1].strip_prefix("0x") {
83 Some(s) => s,
84 None => addr[1],
85 };
86 let pid = match addr[2].strip_prefix("0x") {
87 Some(s) => s,
88 None => addr[2],
89 };
90
91 // the device is USB
92 let vid = u16::from_str_radix(vid, 16)?;
93 let pid = u16::from_str_radix(pid, 16)?;
94 let client = Arc::new(UsbtmcClient::connect(vid, pid)?);
95
96 return Ok(Instrument { client })
97 } else {
98 return Err(anyhow!("unrecognized protocol"))
99 }
100 }
101
102 /// ### Set Timeout
103 ///
104 /// Set a new timeout for the device connection.
105 ///
106 /// #### Arguments
107 /// - `duration` -> the duration of the timeout
108 ///
109 pub fn set_timeout(&self, duration: Duration) {
110 self.client.set_timeout(duration);
111 }
112
113 /// ### Command
114 ///
115 /// Send a command to the device.
116 ///
117 /// #### Arguments
118 /// - `cmd` -> the command to send
119 ///
120 pub fn command(&self, cmd: &str) -> Result<()> {
121 let mut cmd = String::from(cmd);
122 if !cmd.ends_with("\n") {
123 cmd.push('\n');
124 }
125 self.client.command(&cmd)
126 }
127
128 /// ### Query
129 ///
130 /// Send a command and get a response from the device.
131 /// The response is a utf-8 string.
132 ///
133 /// #### Arguments
134 /// - `cmd` -> the command to send
135 ///
136 pub fn query(&self, cmd: &str) -> Result<String> {
137 let mut cmd = String::from(cmd);
138 if !cmd.ends_with("\n") {
139 cmd.push('\n');
140 }
141 self.client.query(&cmd)
142 }
143
144 /// ### Query Raw
145 ///
146 /// Send a command and get a response from the device.
147 /// The response is a vector of bytes.
148 ///
149 /// #### Arguments
150 /// - `cmd` -> the command to send
151 ///
152 pub fn query_raw(&self, cmd: &str) -> Result<Vec<u8>> {
153 let mut cmd = String::from(cmd);
154 if !cmd.ends_with("\n") {
155 cmd.push('\n');
156 }
157 self.client.query_raw(&cmd)
158 }
159}