use serde::Deserialize;
use super::net_handle;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Measurement {
Vpp,
Vmax,
Vmin,
Vrms,
Vavg,
Period,
Frequency,
DutyCyclePositive,
DutyCycleNegative,
PulseWidthPositive,
PulseWidthNegative,
RiseTime,
FallTime,
Overshoot,
}
impl Measurement {
fn action(self) -> &'static str {
match self {
Measurement::Vpp => "measure_vpp",
Measurement::Vmax => "measure_vmax",
Measurement::Vmin => "measure_vmin",
Measurement::Vrms => "measure_vrms",
Measurement::Vavg => "measure_vavg",
Measurement::Period => "measure_period",
Measurement::Frequency => "measure_freq",
Measurement::DutyCyclePositive => "measure_dc_pos",
Measurement::DutyCycleNegative => "measure_dc_neg",
Measurement::PulseWidthPositive => "measure_pulse_width_pos",
Measurement::PulseWidthNegative => "measure_pulse_width_neg",
Measurement::RiseTime => "measure_rise_time",
Measurement::FallTime => "measure_fall_time",
Measurement::Overshoot => "measure_overshoot",
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Deserialize)]
#[non_exhaustive]
pub struct Measurements {
pub vpp: Option<f64>,
pub vmax: Option<f64>,
pub vmin: Option<f64>,
pub vrms: Option<f64>,
pub vavg: Option<f64>,
pub period: Option<f64>,
pub frequency: Option<f64>,
pub duty_cycle_positive: Option<f64>,
pub duty_cycle_negative: Option<f64>,
pub pulse_width_positive: Option<f64>,
pub pulse_width_negative: Option<f64>,
pub rise_time: Option<f64>,
pub fall_time: Option<f64>,
pub overshoot: Option<f64>,
}
impl Measurements {
pub fn get(&self, m: Measurement) -> Option<f64> {
match m {
Measurement::Vpp => self.vpp,
Measurement::Vmax => self.vmax,
Measurement::Vmin => self.vmin,
Measurement::Vrms => self.vrms,
Measurement::Vavg => self.vavg,
Measurement::Period => self.period,
Measurement::Frequency => self.frequency,
Measurement::DutyCyclePositive => self.duty_cycle_positive,
Measurement::DutyCycleNegative => self.duty_cycle_negative,
Measurement::PulseWidthPositive => self.pulse_width_positive,
Measurement::PulseWidthNegative => self.pulse_width_negative,
Measurement::RiseTime => self.rise_time,
Measurement::FallTime => self.fall_time,
Measurement::Overshoot => self.overshoot,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TriggerMode {
Auto,
Normal,
Single,
}
impl TriggerMode {
fn as_str(self) -> &'static str {
match self {
TriggerMode::Auto => "auto",
TriggerMode::Normal => "normal",
TriggerMode::Single => "single",
}
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct EdgeTrigger {
pub source: Option<String>,
pub slope: Option<String>,
pub coupling: Option<String>,
pub level: Option<f64>,
pub mode: Option<TriggerMode>,
}
#[derive(Debug, Clone, PartialEq, Deserialize)]
#[non_exhaustive]
pub struct TriggerSettings {
#[serde(default)]
pub mode: Option<String>,
#[serde(default)]
pub source: Option<String>,
#[serde(default)]
pub slope: Option<String>,
#[serde(default)]
pub level: Option<f64>,
#[serde(default)]
pub holdoff: Option<f64>,
}
pub(crate) mod ops {
use std::time::Duration;
use serde_json::{json, Value};
use super::{EdgeTrigger, Measurement, Measurements, TriggerSettings};
use crate::error::{Error, Result};
use crate::wire::{
as_f64, net_command, unit, value_as, value_f64, CommandResponse, Op, Timeout,
};
const ROLE: &str = "scope";
fn budget() -> Timeout {
Timeout::After(Duration::from_secs(45))
}
fn cmd<T>(
name: &str,
action: &str,
params: Value,
parse: fn(CommandResponse) -> Result<T>,
) -> Op<T> {
Op {
req: net_command(name, ROLE, action, params, budget()),
parse,
}
}
fn parse_string(resp: CommandResponse) -> Result<String> {
match resp.value {
Some(Value::String(s)) => Ok(s),
other => Err(Error::Decode(format!(
"expected string 'value', got {other:?}"
))),
}
}
fn parse_bool(resp: CommandResponse) -> Result<bool> {
match resp.value {
Some(Value::Bool(b)) => Ok(b),
other => Err(Error::Decode(format!(
"expected boolean 'value', got {other:?}"
))),
}
}
fn parse_measurement(resp: CommandResponse) -> Result<Option<f64>> {
match resp.value {
None | Some(Value::Null) => Ok(None),
Some(v) => as_f64(&v)
.map(Some)
.ok_or_else(|| Error::Decode(format!("expected numeric 'value', got {v:?}"))),
}
}
fn parse_value(resp: CommandResponse) -> Result<Value> {
Ok(resp.value.unwrap_or(Value::Null))
}
pub(crate) fn enable(name: &str) -> Op<()> {
cmd(name, "enable_net", json!({}), unit)
}
pub(crate) fn disable(name: &str) -> Op<()> {
cmd(name, "disable_net", json!({}), unit)
}
pub(crate) fn is_enabled(name: &str) -> Op<bool> {
cmd(name, "get_net_enabled", json!({}), parse_bool)
}
pub(crate) fn run(name: &str) -> Op<()> {
cmd(name, "start_capture", json!({}), unit)
}
pub(crate) fn single(name: &str) -> Op<()> {
cmd(name, "start_single", json!({}), unit)
}
pub(crate) fn stop(name: &str) -> Op<()> {
cmd(name, "stop_capture", json!({}), unit)
}
pub(crate) fn force_trigger(name: &str) -> Op<()> {
cmd(name, "force_trigger", json!({}), unit)
}
pub(crate) fn autoscale(name: &str) -> Op<()> {
cmd(name, "autoscale", json!({}), unit)
}
pub(crate) fn set_scale(name: &str, volts_per_div: f64) -> Op<()> {
cmd(
name,
"set_scale",
json!({ "volts_per_div": volts_per_div }),
unit,
)
}
pub(crate) fn scale(name: &str) -> Op<f64> {
cmd(name, "get_scale", json!({}), value_f64)
}
pub(crate) fn set_timebase(name: &str, seconds_per_div: f64) -> Op<()> {
cmd(
name,
"set_timebase",
json!({ "seconds_per_div": seconds_per_div }),
unit,
)
}
pub(crate) fn timebase(name: &str) -> Op<f64> {
cmd(name, "get_timebase", json!({}), value_f64)
}
pub(crate) fn set_coupling(name: &str, mode: &str) -> Op<()> {
cmd(name, "set_coupling", json!({ "mode": mode }), unit)
}
pub(crate) fn coupling(name: &str) -> Op<String> {
cmd(name, "get_coupling", json!({}), parse_string)
}
pub(crate) fn set_probe(name: &str, ratio: f64) -> Op<()> {
cmd(name, "set_probe", json!({ "ratio": ratio }), unit)
}
pub(crate) fn probe(name: &str) -> Op<f64> {
cmd(name, "get_probe", json!({}), value_f64)
}
pub(crate) fn set_offset(name: &str, volts: f64) -> Op<()> {
cmd(name, "set_offset", json!({ "offset": volts }), unit)
}
pub(crate) fn offset(name: &str) -> Op<f64> {
cmd(name, "get_offset", json!({}), value_f64)
}
pub(crate) fn set_time_offset(name: &str, seconds: f64) -> Op<()> {
cmd(name, "set_time_offset", json!({ "offset": seconds }), unit)
}
pub(crate) fn time_offset(name: &str) -> Op<f64> {
cmd(name, "get_time_offset", json!({}), value_f64)
}
pub(crate) fn trigger_edge(name: &str, trigger: &EdgeTrigger) -> Op<()> {
let mut params = json!({});
if let Some(source) = &trigger.source {
params["source"] = json!(source);
}
if let Some(slope) = &trigger.slope {
params["slope"] = json!(slope);
}
if let Some(coupling) = &trigger.coupling {
params["coupling"] = json!(coupling);
}
if let Some(level) = trigger.level {
params["level"] = json!(level);
}
if let Some(mode) = trigger.mode {
params["mode"] = json!(mode.as_str());
}
cmd(name, "trigger_edge", params, unit)
}
pub(crate) fn trigger(name: &str) -> Op<TriggerSettings> {
cmd(name, "get_trigger", json!({}), value_as::<TriggerSettings>)
}
pub(crate) fn measure(name: &str, m: Measurement) -> Op<Option<f64>> {
cmd(name, m.action(), json!({}), parse_measurement)
}
pub(crate) fn measure_all(name: &str) -> Op<Measurements> {
cmd(name, "measure_all", json!({}), value_as::<Measurements>)
}
pub(crate) fn command(name: &str, action: &str, params: Value) -> Op<Value> {
cmd(name, action, params, parse_value)
}
}
net_handle! {
sync: Scope,
async: AsyncScope,
methods: {
fn enable() -> () = ops::enable;
fn disable() -> () = ops::disable;
fn is_enabled() -> bool = ops::is_enabled;
fn run() -> () = ops::run;
fn single() -> () = ops::single;
fn stop() -> () = ops::stop;
fn force_trigger() -> () = ops::force_trigger;
fn autoscale() -> () = ops::autoscale;
fn set_scale(volts_per_div: f64) -> () = ops::set_scale;
fn scale() -> f64 = ops::scale;
fn set_timebase(seconds_per_div: f64) -> () = ops::set_timebase;
fn timebase() -> f64 = ops::timebase;
fn set_coupling(mode: &str) -> () = ops::set_coupling;
fn coupling() -> String = ops::coupling;
fn set_probe(ratio: f64) -> () = ops::set_probe;
fn probe() -> f64 = ops::probe;
fn set_offset(volts: f64) -> () = ops::set_offset;
fn offset() -> f64 = ops::offset;
fn set_time_offset(seconds: f64) -> () = ops::set_time_offset;
fn time_offset() -> f64 = ops::time_offset;
fn trigger_edge(trigger: &EdgeTrigger) -> () = ops::trigger_edge;
fn trigger() -> TriggerSettings = ops::trigger;
fn measure(m: Measurement) -> Option<f64> = ops::measure;
fn measure_all() -> Measurements = ops::measure_all;
fn command(action: &str, params: serde_json::Value) -> serde_json::Value = ops::command;
}
}