Skip to main content

instrument_core/classifier/
probes.rs

1// @generated by tools/gen-shared-tables.ts — do not edit by hand
2use 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
50/// Validate instrument-specific reply shapes rather than accepting any transport success.
51pub 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
96/// Returns true when a probe produces valid capability evidence.
97pub 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}