owon-spe 0.1.0

OWON SPE programmable power-suppy api crate
Documentation
use super::{parse_bool, query_digit_operation};
use crate::{Error, Operation};
use std::borrow::Cow;

query_digit_operation!(Volt, "MEAS:VOLT?", f32);
query_digit_operation!(Current, "MEAS:Current?", f32);
query_digit_operation!(Power, "MEAS:Power?", f32);

fn pop_f32<'a, I: Iterator<Item = &'a str>>(mut i: I) -> Result<f32, Error> {
    i.next()
        .ok_or(Error::UnexpectedData)?
        .parse()
        .or(Err(Error::UnexpectedData))
}

fn pop_bool<'a, I: Iterator<Item = &'a str>>(mut i: I) -> Result<bool, Error> {
    let b = i.next().ok_or(Error::UnexpectedData)?;
    parse_bool(b).ok_or(Error::UnexpectedData)
}

pub(crate) struct MeasureAll {}
#[derive(Debug, Clone, PartialEq)]
pub struct MeasureAllOutput {
    pub volt: f32,
    pub current: f32,
}

impl Operation for MeasureAll {
    type Out = MeasureAllOutput;

    fn command(&self) -> Cow<'_, str> {
        Cow::Borrowed("MEAS:ALL?")
    }

    fn parse_line(&self, line: &str) -> Result<Self::Out, Error> {
        let mut parts = line.split(',');
        Ok(MeasureAllOutput {
            volt: pop_f32(&mut parts)?,
            current: pop_f32(&mut parts)?,
        })
    }
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Mode {
    Standby = 0,
    ConstantVoltage = 1,
    ConstantCurrent = 2,
    Failed = 3,
}

impl std::fmt::Display for Mode {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Mode::Standby => f.write_str("Standby"),
            Mode::ConstantVoltage => f.write_str("Constant Voltage"),
            Mode::ConstantCurrent => f.write_str("Constant Current"),
            Mode::Failed => f.write_str("Failed"),
        }
    }
}

pub(crate) struct MeasureAllInfo {}
#[derive(Debug, Clone, PartialEq)]
pub struct MeasureAllInfoOutput {
    pub volt: f32,
    pub current: f32,
    pub power: f32,
    pub over_voltage: bool,
    pub over_current: bool,
    pub over_temperature: bool,
    pub mode: Mode,
}

impl Operation for MeasureAllInfo {
    type Out = MeasureAllInfoOutput;

    fn command(&self) -> Cow<'_, str> {
        Cow::Borrowed("MEAS:ALL:INFO?")
    }

    fn parse_line(&self, line: &str) -> Result<Self::Out, Error> {
        let mut parts = line.split(',');
        Ok(MeasureAllInfoOutput {
            volt: pop_f32(&mut parts)?,
            current: pop_f32(&mut parts)?,
            power: pop_f32(&mut parts)?,
            over_voltage: pop_bool(&mut parts)?,
            over_current: pop_bool(&mut parts)?,
            over_temperature: pop_bool(&mut parts)?,
            mode: parts
                .next()
                .and_then(|mode| {
                    let m = mode.parse().ok()?;
                    match m {
                        0 => Some(Mode::Standby),
                        1 => Some(Mode::ConstantVoltage),
                        2 => Some(Mode::ConstantCurrent),
                        3 => Some(Mode::Failed),
                        _ => None,
                    }
                })
                .ok_or(Error::UnexpectedData)?,
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::operations::test_states;

    #[test]
    fn test_measure_all() {
        test_states(
            MeasureAll {},
            &[(
                "1.000,2.000",
                Ok(MeasureAllOutput {
                    volt: 1.0,
                    current: 2.0,
                }),
            )],
        );
    }

    #[test]
    fn test_measure_all_info() {
        test_states(
            MeasureAllInfo {},
            &[(
                "4.987,0.514,2.560,OFF,OFF,OFF,1",
                Ok(MeasureAllInfoOutput {
                    volt: 4.987,
                    current: 0.514,
                    power: 2.560,
                    over_voltage: false,
                    over_current: false,
                    over_temperature: false,
                    mode: Mode::ConstantVoltage,
                }),
            )],
        );
    }
}