instrument_core/classifier/
probes.rs1use crate::scpi::ScpiSession;
3use std::time::Duration;
4
5pub const PROBE_TIMEOUT: Duration = Duration::from_millis(800);
6
7pub const DMM_READONLY_COMMANDS: &[&str] = &[":SENS:FUNC?", "SENS:FUNC?", ":FUNC?", "FUNC?"];
8
9pub const PSU_READONLY_COMMANDS: &[&str] = &[":OUTP? 1", "OUTP? 1"];
10
11pub const FGEN_READONLY_COMMANDS: &[&str] = &[":SOUR:FUNC?", "SOUR:FUNC?"];
12
13pub const SCOPE_READONLY_COMMANDS: &[&str] = &[
14 ":TIMebase:SCALe?",
15 "TIMebase:SCALe?",
16 ":CHAN1:SCALe?",
17 "CHAN1:SCALe?",
18 ":WAVeform:SOURce?",
19 "WAVeform:SOURce?",
20];
21
22pub const SWITCH_READONLY_COMMANDS: &[&str] =
23 &[":ROUTe:CLOS?", "ROUT:CLOS?", ":ROUTe:CAT?", "ROUT:CAT?"];
24
25pub const COUNTER_READONLY_COMMANDS: &[&str] = &[
26 ":COUNter:DATA?",
27 "COUN:DATA?",
28 ":COUNter:A:DATA?",
29 "COUN:A:DATA?",
30];
31
32pub const PWRMETER_READONLY_COMMANDS: &[&str] = &[
33 ":UNIT:POW?",
34 "UNIT:POW?",
35 ":SENS:POW:RANG:AUTO?",
36 "SENS:POW:RANG:AUTO?",
37];
38
39pub const SPECAN_READONLY_COMMANDS: &[&str] = &[
40 ":FREQ:CENT?",
41 "FREQ:CENT?",
42 ":FREQ:SPAN?",
43 "FREQ:SPAN?",
44 ":BAND?",
45 "BAND?",
46];
47
48pub const DMM_ACQUISITION_COMMANDS: &[&str] = &[":MEAS:VOLT:DC?", "MEAS:VOLT:DC?"];
49
50pub fn valid_probe_reply(command: &str, response: &str) -> bool {
52 let cmd = command.trim_start_matches(':').to_ascii_uppercase();
53 let reply = response.trim().trim_matches('"').to_ascii_uppercase();
54 if reply.is_empty() || crate::scpi::is_syst_err_supported_reply(&reply) {
55 return false;
56 }
57 if cmd.contains("FUNC") {
58 return if cmd.starts_with("SOUR") {
59 ["SIN", "SQU", "RAMP", "PULS", "NOIS", "DC", "ARB", "USER"].contains(&reply.as_str())
60 } else {
61 [
62 "VOLT", "VOLT:DC", "VOLT:AC", "CURR", "CURR:DC", "CURR:AC", "RES", "FRES", "FREQ",
63 "PER", "CONT", "DIOD", "TEMP", "CAP",
64 ]
65 .contains(&reply.as_str())
66 };
67 }
68 if cmd.starts_with("OUTP") || cmd.contains("AUTO?") {
69 return ["0", "1", "OFF", "ON"].contains(&reply.as_str());
70 }
71 if cmd.starts_with("UNIT") {
72 return ["DBM", "W", "WATT"].contains(&reply.as_str());
73 }
74 if cmd.starts_with("WAV") {
75 return reply
76 .strip_prefix("CHAN")
77 .and_then(|s| s.parse::<u32>().ok())
78 .is_some_and(|n| n > 0);
79 }
80 if cmd.starts_with("ROUT") {
81 return ["0", "1"].contains(&reply.as_str())
82 || reply
83 .strip_prefix("(@")
84 .and_then(|s| s.strip_suffix(')'))
85 .is_some_and(|s| {
86 s.chars()
87 .all(|c| c.is_ascii_digit() || [',', ':', ' '].contains(&c))
88 });
89 }
90 let signed_voltage = matches!(cmd.as_str(), "MEAS:VOLT:DC?" | "VOLT? (@1)");
91 reply
92 .parse::<f64>()
93 .is_ok_and(|n| n.is_finite() && (signed_voltage || n >= 0.0))
94}
95
96pub fn probe_any(session: &mut ScpiSession, commands: &[&str], timeout: Duration) -> bool {
98 commands.iter().any(|cmd| {
99 session
100 .query_with_timeout(cmd, timeout)
101 .is_ok_and(|reply| valid_probe_reply(cmd, &reply))
102 }) && (commands != PSU_READONLY_COMMANDS
103 || session
104 .query_with_timeout(":VOLT? (@1)", timeout)
105 .is_ok_and(|reply| valid_probe_reply(":VOLT? (@1)", &reply)))
106}