1use serde::Deserialize;
43
44use super::net_handle;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
48#[non_exhaustive]
49pub enum Measurement {
50 Vpp,
52 Vmax,
54 Vmin,
56 Vrms,
58 Vavg,
60 Period,
62 Frequency,
64 DutyCyclePositive,
66 DutyCycleNegative,
68 PulseWidthPositive,
70 PulseWidthNegative,
72 RiseTime,
74 FallTime,
76 Overshoot,
78}
79
80impl Measurement {
81 fn action(self) -> &'static str {
83 match self {
84 Measurement::Vpp => "measure_vpp",
85 Measurement::Vmax => "measure_vmax",
86 Measurement::Vmin => "measure_vmin",
87 Measurement::Vrms => "measure_vrms",
88 Measurement::Vavg => "measure_vavg",
89 Measurement::Period => "measure_period",
90 Measurement::Frequency => "measure_freq",
91 Measurement::DutyCyclePositive => "measure_dc_pos",
92 Measurement::DutyCycleNegative => "measure_dc_neg",
93 Measurement::PulseWidthPositive => "measure_pulse_width_pos",
94 Measurement::PulseWidthNegative => "measure_pulse_width_neg",
95 Measurement::RiseTime => "measure_rise_time",
96 Measurement::FallTime => "measure_fall_time",
97 Measurement::Overshoot => "measure_overshoot",
98 }
99 }
100}
101
102#[derive(Debug, Clone, Copy, Default, PartialEq, Deserialize)]
105#[non_exhaustive]
106pub struct Measurements {
107 pub vpp: Option<f64>,
109 pub vmax: Option<f64>,
111 pub vmin: Option<f64>,
113 pub vrms: Option<f64>,
115 pub vavg: Option<f64>,
117 pub period: Option<f64>,
119 pub frequency: Option<f64>,
121 pub duty_cycle_positive: Option<f64>,
123 pub duty_cycle_negative: Option<f64>,
125 pub pulse_width_positive: Option<f64>,
127 pub pulse_width_negative: Option<f64>,
129 pub rise_time: Option<f64>,
131 pub fall_time: Option<f64>,
133 pub overshoot: Option<f64>,
135}
136
137impl Measurements {
138 pub fn get(&self, m: Measurement) -> Option<f64> {
140 match m {
141 Measurement::Vpp => self.vpp,
142 Measurement::Vmax => self.vmax,
143 Measurement::Vmin => self.vmin,
144 Measurement::Vrms => self.vrms,
145 Measurement::Vavg => self.vavg,
146 Measurement::Period => self.period,
147 Measurement::Frequency => self.frequency,
148 Measurement::DutyCyclePositive => self.duty_cycle_positive,
149 Measurement::DutyCycleNegative => self.duty_cycle_negative,
150 Measurement::PulseWidthPositive => self.pulse_width_positive,
151 Measurement::PulseWidthNegative => self.pulse_width_negative,
152 Measurement::RiseTime => self.rise_time,
153 Measurement::FallTime => self.fall_time,
154 Measurement::Overshoot => self.overshoot,
155 }
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
161pub enum TriggerMode {
162 Auto,
164 Normal,
166 Single,
169}
170
171impl TriggerMode {
172 fn as_str(self) -> &'static str {
173 match self {
174 TriggerMode::Auto => "auto",
175 TriggerMode::Normal => "normal",
176 TriggerMode::Single => "single",
177 }
178 }
179}
180
181#[derive(Debug, Clone, Default, PartialEq)]
185pub struct EdgeTrigger {
186 pub source: Option<String>,
189 pub slope: Option<String>,
192 pub coupling: Option<String>,
196 pub level: Option<f64>,
198 pub mode: Option<TriggerMode>,
200}
201
202#[derive(Debug, Clone, PartialEq, Deserialize)]
205#[non_exhaustive]
206pub struct TriggerSettings {
207 #[serde(default)]
209 pub mode: Option<String>,
210 #[serde(default)]
212 pub source: Option<String>,
213 #[serde(default)]
215 pub slope: Option<String>,
216 #[serde(default)]
218 pub level: Option<f64>,
219 #[serde(default)]
221 pub holdoff: Option<f64>,
222}
223
224pub(crate) mod ops {
225 use std::time::Duration;
226
227 use serde_json::{json, Value};
228
229 use super::{EdgeTrigger, Measurement, Measurements, TriggerSettings};
230 use crate::error::{Error, Result};
231 use crate::wire::{
232 as_f64, net_command, unit, value_as, value_f64, CommandResponse, Op, Timeout,
233 };
234
235 const ROLE: &str = "scope";
238
239 fn budget() -> Timeout {
241 Timeout::After(Duration::from_secs(45))
242 }
243
244 fn cmd<T>(
245 name: &str,
246 action: &str,
247 params: Value,
248 parse: fn(CommandResponse) -> Result<T>,
249 ) -> Op<T> {
250 Op {
251 req: net_command(name, ROLE, action, params, budget()),
252 parse,
253 }
254 }
255
256 fn parse_string(resp: CommandResponse) -> Result<String> {
257 match resp.value {
258 Some(Value::String(s)) => Ok(s),
259 other => Err(Error::Decode(format!(
260 "expected string 'value', got {other:?}"
261 ))),
262 }
263 }
264
265 fn parse_bool(resp: CommandResponse) -> Result<bool> {
266 match resp.value {
267 Some(Value::Bool(b)) => Ok(b),
268 other => Err(Error::Decode(format!(
269 "expected boolean 'value', got {other:?}"
270 ))),
271 }
272 }
273
274 fn parse_measurement(resp: CommandResponse) -> Result<Option<f64>> {
277 match resp.value {
278 None | Some(Value::Null) => Ok(None),
279 Some(v) => as_f64(&v)
280 .map(Some)
281 .ok_or_else(|| Error::Decode(format!("expected numeric 'value', got {v:?}"))),
282 }
283 }
284
285 fn parse_value(resp: CommandResponse) -> Result<Value> {
286 Ok(resp.value.unwrap_or(Value::Null))
287 }
288
289 pub(crate) fn enable(name: &str) -> Op<()> {
290 cmd(name, "enable_net", json!({}), unit)
291 }
292
293 pub(crate) fn disable(name: &str) -> Op<()> {
294 cmd(name, "disable_net", json!({}), unit)
295 }
296
297 pub(crate) fn is_enabled(name: &str) -> Op<bool> {
298 cmd(name, "get_net_enabled", json!({}), parse_bool)
299 }
300
301 pub(crate) fn run(name: &str) -> Op<()> {
302 cmd(name, "start_capture", json!({}), unit)
303 }
304
305 pub(crate) fn single(name: &str) -> Op<()> {
306 cmd(name, "start_single", json!({}), unit)
307 }
308
309 pub(crate) fn stop(name: &str) -> Op<()> {
310 cmd(name, "stop_capture", json!({}), unit)
311 }
312
313 pub(crate) fn force_trigger(name: &str) -> Op<()> {
314 cmd(name, "force_trigger", json!({}), unit)
315 }
316
317 pub(crate) fn autoscale(name: &str) -> Op<()> {
318 cmd(name, "autoscale", json!({}), unit)
319 }
320
321 pub(crate) fn set_scale(name: &str, volts_per_div: f64) -> Op<()> {
322 cmd(
323 name,
324 "set_scale",
325 json!({ "volts_per_div": volts_per_div }),
326 unit,
327 )
328 }
329
330 pub(crate) fn scale(name: &str) -> Op<f64> {
331 cmd(name, "get_scale", json!({}), value_f64)
332 }
333
334 pub(crate) fn set_timebase(name: &str, seconds_per_div: f64) -> Op<()> {
335 cmd(
336 name,
337 "set_timebase",
338 json!({ "seconds_per_div": seconds_per_div }),
339 unit,
340 )
341 }
342
343 pub(crate) fn timebase(name: &str) -> Op<f64> {
344 cmd(name, "get_timebase", json!({}), value_f64)
345 }
346
347 pub(crate) fn set_coupling(name: &str, mode: &str) -> Op<()> {
348 cmd(name, "set_coupling", json!({ "mode": mode }), unit)
349 }
350
351 pub(crate) fn coupling(name: &str) -> Op<String> {
352 cmd(name, "get_coupling", json!({}), parse_string)
353 }
354
355 pub(crate) fn set_probe(name: &str, ratio: f64) -> Op<()> {
356 cmd(name, "set_probe", json!({ "ratio": ratio }), unit)
357 }
358
359 pub(crate) fn probe(name: &str) -> Op<f64> {
360 cmd(name, "get_probe", json!({}), value_f64)
361 }
362
363 pub(crate) fn set_offset(name: &str, volts: f64) -> Op<()> {
364 cmd(name, "set_offset", json!({ "offset": volts }), unit)
365 }
366
367 pub(crate) fn offset(name: &str) -> Op<f64> {
368 cmd(name, "get_offset", json!({}), value_f64)
369 }
370
371 pub(crate) fn set_time_offset(name: &str, seconds: f64) -> Op<()> {
372 cmd(name, "set_time_offset", json!({ "offset": seconds }), unit)
373 }
374
375 pub(crate) fn time_offset(name: &str) -> Op<f64> {
376 cmd(name, "get_time_offset", json!({}), value_f64)
377 }
378
379 pub(crate) fn trigger_edge(name: &str, trigger: &EdgeTrigger) -> Op<()> {
380 let mut params = json!({});
381 if let Some(source) = &trigger.source {
382 params["source"] = json!(source);
383 }
384 if let Some(slope) = &trigger.slope {
385 params["slope"] = json!(slope);
386 }
387 if let Some(coupling) = &trigger.coupling {
388 params["coupling"] = json!(coupling);
389 }
390 if let Some(level) = trigger.level {
391 params["level"] = json!(level);
392 }
393 if let Some(mode) = trigger.mode {
394 params["mode"] = json!(mode.as_str());
395 }
396 cmd(name, "trigger_edge", params, unit)
397 }
398
399 pub(crate) fn trigger(name: &str) -> Op<TriggerSettings> {
400 cmd(name, "get_trigger", json!({}), value_as::<TriggerSettings>)
401 }
402
403 pub(crate) fn measure(name: &str, m: Measurement) -> Op<Option<f64>> {
404 cmd(name, m.action(), json!({}), parse_measurement)
405 }
406
407 pub(crate) fn measure_all(name: &str) -> Op<Measurements> {
408 cmd(name, "measure_all", json!({}), value_as::<Measurements>)
409 }
410
411 pub(crate) fn command(name: &str, action: &str, params: Value) -> Op<Value> {
412 cmd(name, action, params, parse_value)
413 }
414}
415
416net_handle! {
417 sync: Scope,
421 async: AsyncScope,
422 methods: {
423 fn enable() -> () = ops::enable;
425 fn disable() -> () = ops::disable;
427 fn is_enabled() -> bool = ops::is_enabled;
429 fn run() -> () = ops::run;
431 fn single() -> () = ops::single;
433 fn stop() -> () = ops::stop;
435 fn force_trigger() -> () = ops::force_trigger;
437 fn autoscale() -> () = ops::autoscale;
439 fn set_scale(volts_per_div: f64) -> () = ops::set_scale;
441 fn scale() -> f64 = ops::scale;
443 fn set_timebase(seconds_per_div: f64) -> () = ops::set_timebase;
445 fn timebase() -> f64 = ops::timebase;
447 fn set_coupling(mode: &str) -> () = ops::set_coupling;
450 fn coupling() -> String = ops::coupling;
452 fn set_probe(ratio: f64) -> () = ops::set_probe;
454 fn probe() -> f64 = ops::probe;
456 fn set_offset(volts: f64) -> () = ops::set_offset;
458 fn offset() -> f64 = ops::offset;
460 fn set_time_offset(seconds: f64) -> () = ops::set_time_offset;
462 fn time_offset() -> f64 = ops::time_offset;
464 fn trigger_edge(trigger: &EdgeTrigger) -> () = ops::trigger_edge;
467 fn trigger() -> TriggerSettings = ops::trigger;
469 fn measure(m: Measurement) -> Option<f64> = ops::measure;
472 fn measure_all() -> Measurements = ops::measure_all;
475 fn command(action: &str, params: serde_json::Value) -> serde_json::Value = ops::command;
480 }
481}