prologix_gpib_ethernet_controller_manager/
gpib_controller.rs

1use std::io::{Read, Write};
2use std::net::{IpAddr, SocketAddr, TcpStream};
3use std::time::Duration;
4use crate::errors::GpibControllerError;
5
6const BUFFER_SIZE: usize = 4096;
7
8pub struct GpibController{
9    /// TcpStream where the controller is
10    tcp_stream: TcpStream,
11    /// the GPIB address the controller currently has selected.
12    ///
13    /// This is tracked locally to avoid selecting a new address everytime a command is sent if you
14    /// are only using one device. 255 means the address has not yet been set although this should never appear.
15    // this should only be updated with the `set_address` function
16    current_gpib_addr: u8,
17
18    /// buffer used to avoid reallocations.
19    buffer: [u8; BUFFER_SIZE]
20}
21
22impl GpibController{
23    pub fn send_raw_data(&mut self, message: &str) -> Result<usize, GpibControllerError>{
24        self.tcp_stream.write(message.as_ref()).map_err(|e| { GpibControllerError::TcpIoError(e)})
25    }
26
27    pub fn gpib_send_to_addr(&mut self, message: &str, gpib_addr: u8) -> Result<usize, GpibControllerError>{
28        self.set_address(gpib_addr)?;
29        self.send_raw_data(message)
30    }
31
32    pub fn read_data(&mut self) -> Result<&str, GpibControllerError>{
33        let bytes_received = self.tcp_stream.read(&mut self.buffer).map_err(|e| { GpibControllerError::TcpIoError(e)})?;
34        if bytes_received > BUFFER_SIZE{
35            Err(GpibControllerError::BufferTooSmall) // Could also just read again and append results but I dont think any responses should be that big.
36        } else {
37            Ok(std::str::from_utf8(&self.buffer[0..bytes_received])?)
38        }
39    }
40
41    pub fn try_new_from(ip_addr: IpAddr) -> Result<GpibController, GpibControllerError>{
42        let sock_addr: SocketAddr = SocketAddr::from((ip_addr, 1234u16));
43        let temp_tcp_stream = TcpStream::connect_timeout(&sock_addr, Duration::from_millis(1500))
44            .map_err(|e| { GpibControllerError::TcpIoError(e)})?;
45        temp_tcp_stream.set_write_timeout(Some(Duration::from_millis(1500)))?;
46        temp_tcp_stream.set_read_timeout(Some(Duration::from_millis(1500)))?;
47        let mut temp_controller = GpibController{
48            tcp_stream: temp_tcp_stream,
49            current_gpib_addr: 255,
50            buffer: [0u8; BUFFER_SIZE]
51        };
52
53        temp_controller.send_raw_data("++addr\n")?;
54
55        let addr: u8 = temp_controller.read_data()?.trim().parse()?;
56        temp_controller.current_gpib_addr = addr;
57        temp_controller.send_raw_data("++auto 1\n")?;
58        temp_controller.send_raw_data("++mode 1\n")?;
59        Ok(temp_controller)
60    }
61
62    pub fn set_address(&mut self, address: u8) -> Result<(), GpibControllerError>{
63        if self.current_gpib_addr == address{
64            return Ok(());
65        }
66        let mut temp_message: String = String::from("++addr ");
67        temp_message.push_str(&address.to_string());
68        self.send_raw_data(&temp_message)?;
69        self.current_gpib_addr = address;
70
71        Ok(())
72    }
73
74    pub fn gpib_send_and_listen_wrapper(&mut self, message: &str, gpib_address: u8, ignore_response: bool) -> Result<Option<String>, GpibControllerError>{
75        self.gpib_send_to_addr(message, gpib_address)?;
76
77        if ignore_response {
78            return Ok(None)
79        }
80        match self.read_data() {
81            Ok(s) => {
82                Ok(Some(s.to_string()))
83            }
84            Err(GpibControllerError::TcpIoError(_)) => {
85                Ok(None)
86            }
87            Err(e) => {
88                Err(e)
89            }
90        }
91    }
92
93}
94
95#[cfg(test)]
96mod gpib_controller_tests {
97    use super::*;
98
99    #[test]
100    fn test_gpib_controller() {
101        let mut controller: GpibController = GpibController::try_new_from(IpAddr::from([192,168,1,82])).unwrap();
102        controller.gpib_send_to_addr("*IDN?\n", 16).unwrap();
103        println!("{}", controller.read_data().unwrap())
104    }
105}
106
107
108// Commands for the controller: (not GPIB protocol)
109
110//The following commands are available:
111// ++addr 0-30 [96-126]  -- specify GPIB address
112// ++addr                -- query GPIB address
113// ++auto 0|1            -- enable (1) or disable (0) read-after-write
114// ++auto                -- query read-after-write setting
115// ++clr                 -- issue device clear
116// ++eoi 0|1             -- enable (1) or disable (0) EOI with last byte
117// ++eoi                 -- query eoi setting
118// ++eos 0|1|2|3         -- EOS terminator - 0:CR+LF, 1:CR, 2:LF, 3:None
119// ++eos                 -- query eos setting
120// ++eot_enable 0|1      -- enable (1) or disable (0) appending eot_char on EOI
121// ++eot_enable          -- query eot_enable setting
122// ++eot_char <char>     -- specify eot character in decimal
123// ++eot_char            -- query eot_char character
124// ++ifc                 -- issue interface clear
125// ++loc                 -- set device to local
126// ++lon                 -- enable (1) or disable (0) listen only mode
127// ++mode 0|1            -- set mode - 0:DEVICE, 1:CONTROLLER
128// ++mode                -- query current mode
129// ++read [eoi|<char>]   -- read until EOI, <char>, or timeout
130// ++read_tmo_ms 1-3000  -- set read timeout in millisec
131// ++read_tmo_ms         -- query timeout
132// ++rst                 -- reset controller
133// ++savecfg 0|1         -- enable (1) or disable (0) saving configuration to EPROM
134// ++savecfg             -- query savecfg setting
135// ++spoll               -- serial poll currently addressed device
136// ++spoll 0-30 [96-126] -- serial poll device at specified address
137// ++srq                 -- query SRQ status
138// ++status 0-255        -- specify serial poll status byte
139// ++status              -- query serial poll status byte
140// ++trg                 -- issue device trigger
141// ++ver                 -- query controller version
142// ++help                -- display this help