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
use std::fmt::Display;

use thiserror::Error;
use visa_rs::{flags::AccessMode, AsResourceManager, DefaultRM, TIMEOUT_IMMEDIATE};
mod prelude;

#[derive(Debug, Error)]
pub enum Error {
    #[error(transparent)]
    StdIo(#[from] std::io::Error),
    #[error(transparent)]
    VisaRs(#[from] visa_rs::Error),
    #[error("No instrument was found")]
    NoInstrumentFound(),
}

pub type Result<T> = core::result::Result<T, Error>;

#[derive(Debug, PartialEq, strum::AsRefStr, strum::Display)]
pub enum Commands {
    #[strum(serialize = "CLS")]
    ClearStatus,
    #[strum(serialize = "*ESE")]
    EventStatusEnable,
    #[strum(serialize = "*ESE?")]
    EventStatusEnableQuery,
    #[strum(serialize = "*ESR?")]
    EventStatusEnableRegister,
    #[strum(serialize = "*IDN?")]
    Identify,
    #[strum(serialize = "*OPC")]
    OperationCompleteCommand,
    #[strum(serialize = "*OPC?")]
    OperationCompleteQuery,
    #[strum(serialize = "*OPT?")]
    IdentifyOptionsQuery,
    #[strum(serialize = "*RST")]
    Reset,
    #[strum(serialize = "*SRE")]
    ServiceRequestEnable,
    #[strum(serialize = "*SRE?")]
    ServiceRequestEnableQuery,
    #[strum(serialize = "*STB?")]
    StatusByteQuery,
    #[strum(serialize = "*TST?")]
    ResultOfSelfTestQuery,
    #[strum(serialize = "*WAI")]
    Wait,
}

#[derive(Clone, Debug)]
pub struct Idn {
    pub manufacturer: String,
    pub model: String,
    pub serial_number: String,
    pub software_version: String,
}

pub fn find_resources(rm: &visa_rs::DefaultRM) -> Result<Vec<visa_rs::VisaString>> {
    let expr = visa_rs::VisaString::from(
        std::ffi::CString::new("?*INSTR").expect("Failed to create C compatible String."),
    );
    let mut visa_rs_reslist = rm.find_res_list(&expr)?;
    let mut res_exhausted = false;
    let mut res: Vec<visa_rs::VisaString> = vec![];
    while !res_exhausted {
        let visa_rs_res = visa_rs_reslist.find_next()?;
        if let Some(visa_rs_res) = visa_rs_res {
            res.push(visa_rs_res);
        } else {
            res_exhausted = true;
        };
    }
    Ok(res)
}

fn get_idn(instrument: &mut visa_rs::Instrument) -> Result<Idn> {
    std::io::Write::write_all(instrument, Commands::Identify.as_ref().as_bytes())?;
    let mut r = std::io::BufReader::new(instrument);
    let mut buf = String::new();
    std::io::BufRead::read_line(&mut r, &mut buf)?;

    let buf = buf
        .split(',')
        .map(|s| s.trim().to_string())
        .collect::<Vec<String>>();
    Ok(Idn {
        manufacturer: buf[0].to_owned(),
        model: buf[1].to_owned(),
        serial_number: buf[2].to_owned(),
        software_version: buf[3].to_owned(),
    })
}

pub trait Visa {
    fn into_inner(&self) -> &visa_rs::Instrument;

    fn find_instrument<T>(rm: &DefaultRM, manufacturer: T, model: T) -> Result<visa_rs::Instrument>
    where
        T: Into<String> + Display,
    {
        let resources = find_resources(rm)?;
        for resource in resources {
            let mut instrument = rm.open(&resource, AccessMode::NO_LOCK, TIMEOUT_IMMEDIATE)?;
            let idn = get_idn(&mut instrument).unwrap();
            let manufacturer_and_model = format!("{} {}", idn.manufacturer, idn.model);
            if manufacturer_and_model.contains(&format!("{} {}", manufacturer, model)) {
                return Ok(instrument);
            }
        }
        Err(Error::NoInstrumentFound())
    }

    fn write<T>(&mut self, cmd: T) -> Result<()>
    where
        T: Into<String>,
    {
        let cmd: String = cmd.into();
        std::io::Write::write_all(&mut self.into_inner(), cmd.as_bytes())?;
        Ok(())
    }

    fn read(&self) -> Result<String> {
        let mut buf = String::new();
        std::io::Read::read_to_string(&mut self.into_inner(), &mut buf)?;
        Ok(buf)
    }

    fn get_idn(&mut self) -> Result<Idn> {
        self.write(Commands::Identify.as_ref())?;
        let buf = self.read()?;
        let buf = buf
            .split(',')
            .map(|s| s.trim().to_string())
            .collect::<Vec<String>>();
        Ok(Idn {
            manufacturer: buf[0].to_owned(),
            model: buf[1].to_owned(),
            serial_number: buf[2].to_owned(),
            software_version: buf[3].to_owned(),
        })
    }

    fn reset(&mut self) -> Result<()> {
        self.write(Commands::Reset.as_ref())?;
        Ok(())
    }
}