uldaqrs 0.2.0

Safe Rust bindings for the uldaq library (Measurement Computing / Data Translation DAQ devices)
Documentation
//! Typed enumerations and structures used by the uldaq library.
//!
//! The raw bindings in `uldaq-sys` expose the C enumerations as plain
//! `u32` type aliases with named constants. This module wraps them in proper
//! Rust types so that invalid values cannot be passed to the library. The
//! numeric values are taken directly from the bindings, so they cannot drift
//! from the C header.

use bitflags::bitflags;
use uldaq_sys as sys;

/// Analog input / output range.
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Range {
    Bip60Volts = sys::Range_BIP60VOLTS,
    Bip30Volts = sys::Range_BIP30VOLTS,
    Bip15Volts = sys::Range_BIP15VOLTS,
    Bip20Volts = sys::Range_BIP20VOLTS,
    Bip10Volts = sys::Range_BIP10VOLTS,
    Bip5Volts = sys::Range_BIP5VOLTS,
    Bip4Volts = sys::Range_BIP4VOLTS,
    Bip2Pt5Volts = sys::Range_BIP2PT5VOLTS,
    Bip2Volts = sys::Range_BIP2VOLTS,
    Bip1Pt25Volts = sys::Range_BIP1PT25VOLTS,
    Bip1Volts = sys::Range_BIP1VOLTS,
    BipPt625Volts = sys::Range_BIPPT625VOLTS,
    BipPt5Volts = sys::Range_BIPPT5VOLTS,
    BipPt25Volts = sys::Range_BIPPT25VOLTS,
    BipPt125Volts = sys::Range_BIPPT125VOLTS,
    BipPt2Volts = sys::Range_BIPPT2VOLTS,
    BipPt1Volts = sys::Range_BIPPT1VOLTS,
    BipPt078Volts = sys::Range_BIPPT078VOLTS,
    BipPt05Volts = sys::Range_BIPPT05VOLTS,
    BipPt01Volts = sys::Range_BIPPT01VOLTS,
    Bip3Volts = sys::Range_BIP3VOLTS,
    BipPt312Volts = sys::Range_BIPPT312VOLTS,
    BipPt156Volts = sys::Range_BIPPT156VOLTS,
    /// 0 to 20 mA.
    Ma0To20 = sys::Range_MA0TO20,
}

impl From<Range> for u32 {
    fn from(r: Range) -> u32 {
        r as u32
    }
}

/// The status of a scan operation.
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum ScanStatus {
    /// Scan is idle.
    Idle = sys::ScanStatus_SS_IDLE,
    /// Scan is running.
    Running = sys::ScanStatus_SS_RUNNING,
}

impl ScanStatus {
    /// Interpret a raw status value returned by the library.
    pub fn from_raw(raw: u32) -> Option<ScanStatus> {
        match raw {
            sys::ScanStatus_SS_IDLE => Some(ScanStatus::Idle),
            sys::ScanStatus_SS_RUNNING => Some(ScanStatus::Running),
            _ => None,
        }
    }
}

/// The type of input channel used with `ulDaqInScan`.
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum DaqInChanType {
    /// Analog input, single-ended.
    AnalogSe = sys::DaqInChanType_DAQI_ANALOG_SE,
    /// Analog input, differential.
    AnalogDiff = sys::DaqInChanType_DAQI_ANALOG_DIFF,
    /// Digital input.
    Digital = sys::DaqInChanType_DAQI_DIGITAL,
    /// 16-bit counter.
    Ctr16 = sys::DaqInChanType_DAQI_CTR16,
    /// 32-bit counter.
    Ctr32 = sys::DaqInChanType_DAQI_CTR32,
    /// 48-bit counter.
    Ctr48 = sys::DaqInChanType_DAQI_CTR48,
    /// The D/A converter (used to monitor the analog output).
    Dac = sys::DaqInChanType_DAQI_DAC,
}

impl From<DaqInChanType> for u32 {
    fn from(t: DaqInChanType) -> u32 {
        t as u32
    }
}

/// Analog input coupling mode.
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum CouplingMode {
    /// DC coupling.
    Dc = sys::CouplingMode_CM_DC,
    /// AC coupling.
    Ac = sys::CouplingMode_CM_AC,
}

impl From<CouplingMode> for u32 {
    fn from(m: CouplingMode) -> u32 {
        m as u32
    }
}

/// IEPE (constant current) excitation mode.
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum IepeMode {
    /// IEPE excitation current is disabled.
    Disabled = sys::IepeMode_IEPE_DISABLED,
    /// IEPE excitation current is enabled.
    Enabled = sys::IepeMode_IEPE_ENABLED,
}

impl From<IepeMode> for u32 {
    fn from(m: IepeMode) -> u32 {
        m as u32
    }
}

/// AI configuration items that can be set with [`DaqDevice::set_ai_config`](crate::DaqDevice::set_ai_config).
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum AiConfigItem {
    ChanType = sys::AiConfigItem_AI_CFG_CHAN_TYPE,
    ChanTcType = sys::AiConfigItem_AI_CFG_CHAN_TC_TYPE,
    ScanChanTempUnit = sys::AiConfigItem_AI_CFG_SCAN_CHAN_TEMP_UNIT,
    ScanTempUnit = sys::AiConfigItem_AI_CFG_SCAN_TEMP_UNIT,
    AdcTimingMode = sys::AiConfigItem_AI_CFG_ADC_TIMING_MODE,
    AutoZeroMode = sys::AiConfigItem_AI_CFG_AUTO_ZERO_MODE,
    CalDate = sys::AiConfigItem_AI_CFG_CAL_DATE,
    ChanIepeMode = sys::AiConfigItem_AI_CFG_CHAN_IEPE_MODE,
    ChanCouplingMode = sys::AiConfigItem_AI_CFG_CHAN_COUPLING_MODE,
    ChanSensorConnectionType = sys::AiConfigItem_AI_CFG_CHAN_SENSOR_CONNECTION_TYPE,
    ChanOtdMode = sys::AiConfigItem_AI_CFG_CHAN_OTD_MODE,
    OtdMode = sys::AiConfigItem_AI_CFG_OTD_MODE,
    CalTableType = sys::AiConfigItem_AI_CFG_CAL_TABLE_TYPE,
    RejectFreqType = sys::AiConfigItem_AI_CFG_REJECT_FREQ_TYPE,
    ExpCalDate = sys::AiConfigItem_AI_CFG_EXP_CAL_DATE,
}

impl From<AiConfigItem> for u32 {
    fn from(i: AiConfigItem) -> u32 {
        i as u32
    }
}

/// AI configuration items with a `f64` value, set with
/// [`DaqDevice::set_ai_config_dbl`](crate::DaqDevice::set_ai_config_dbl).
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum AiConfigItemDbl {
    ChanSlope = sys::AiConfigItemDbl_AI_CFG_CHAN_SLOPE,
    ChanOffset = sys::AiConfigItemDbl_AI_CFG_CHAN_OFFSET,
    ChanSensorSensitivity = sys::AiConfigItemDbl_AI_CFG_CHAN_SENSOR_SENSITIVITY,
    ChanDataRate = sys::AiConfigItemDbl_AI_CFG_CHAN_DATA_RATE,
}

impl From<AiConfigItemDbl> for u32 {
    fn from(i: AiConfigItemDbl) -> u32 {
        i as u32
    }
}

bitflags! {
    /// Scan options, passed to the scan functions. Bits can be combined.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct ScanOption: u32 {
        /// Transfers data based on the board type and sampling speed.
        const DEFAULT_IO = sys::ScanOption_SO_DEFAULTIO;
        /// Transfers one packet of data at a time.
        const SINGLE_IO = sys::ScanOption_SO_SINGLEIO;
        /// Transfers data in blocks.
        const BLOCK_IO = sys::ScanOption_SO_BLOCKIO;
        /// Transfers data from the FIFO after the scan completes.
        const BURST_IO = sys::ScanOption_SO_BURSTIO;
        /// Scans data in an endless loop. The only way to stop the operation
        /// is with the corresponding scan stop function.
        const CONTINUOUS = sys::ScanOption_SO_CONTINUOUS;
        /// Data conversions are controlled by an external clock signal.
        const EXT_CLOCK = sys::ScanOption_SO_EXTCLOCK;
        /// Sampling begins when a trigger condition is met.
        const EXT_TRIGGER = sys::ScanOption_SO_EXTTRIGGER;
        /// Re-arms the trigger after a trigger event is performed.
        const RETRIGGER = sys::ScanOption_SO_RETRIGGER;
        /// Enables burst mode sampling, minimizing the channel skew.
        const BURST_MODE = sys::ScanOption_SO_BURSTMODE;
        /// Enables or disables the internal pacer output on a DAQ device.
        const PACER_OUT = sys::ScanOption_SO_PACEROUT;
        /// Changes the internal clock's timebase to an external timebase source.
        const EXT_TIMEBASE = sys::ScanOption_SO_EXTTIMEBASE;
        /// Enables or disables the internal timebase output on a DAQ device.
        const TIMEBASE_OUT = sys::ScanOption_SO_TIMEBASEOUT;
    }
}

bitflags! {
    /// The physical connection interface of a DAQ device.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct DaqDeviceInterface: u32 {
        /// USB interface.
        const USB = sys::DaqDeviceInterface_USB_IFC;
        /// Bluetooth interface.
        const BLUETOOTH = sys::DaqDeviceInterface_BLUETOOTH_IFC;
        /// Ethernet interface.
        const ETHERNET = sys::DaqDeviceInterface_ETHERNET_IFC;
        /// Any interface.
        const ANY = sys::DaqDeviceInterface_ANY_IFC;
    }
}

impl DaqDeviceInterface {
    /// A short human-readable name for the (first) interface in this set.
    pub fn name(&self) -> &'static str {
        if self.contains(DaqDeviceInterface::USB) {
            "USB"
        } else if self.contains(DaqDeviceInterface::BLUETOOTH) {
            "Bluetooth"
        } else if self.contains(DaqDeviceInterface::ETHERNET) {
            "Ethernet"
        } else {
            "Unknown"
        }
    }
}

/// Flags for `ulDaqInScan`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DaqInScanFlag(u32);

impl DaqInScanFlag {
    /// Data is returned with scaling and calibration factors applied to analog
    /// channel data.
    pub const DEFAULT: DaqInScanFlag = DaqInScanFlag(sys::DaqInScanFlag_DAQINSCAN_FF_DEFAULT);
}

impl From<DaqInScanFlag> for u32 {
    fn from(f: DaqInScanFlag) -> u32 {
        f.0
    }
}

/// Flags for `ulAOutScan`.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AOutScanFlag(u32);

impl AOutScanFlag {
    /// Scaled data is supplied and calibration factors are applied to output.
    pub const DEFAULT: AOutScanFlag = AOutScanFlag(sys::AOutScanFlag_AOUTSCAN_FF_DEFAULT);
}

impl From<AOutScanFlag> for u32 {
    fn from(f: AOutScanFlag) -> u32 {
        f.0
    }
}

/// Information about the progress of a scan operation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct TransferStatus {
    /// The number of samples per channel transferred since the scan started.
    /// This is the same as [`current_total_count`](TransferStatus::current_total_count)
    /// for single channel scans.
    pub current_scan_count: u64,
    /// The total number of samples transferred since the scan started. This is
    /// the same as the scan count multiplied by the number of channels.
    pub current_total_count: u64,
    /// The location in the buffer where the last scan of data values are
    /// stored. For continuous scans, this value increments up to (buffer size
    /// - number of channels) and restarts from 0.
    pub current_index: i64,
}

impl From<sys::TransferStatus> for TransferStatus {
    fn from(s: sys::TransferStatus) -> TransferStatus {
        TransferStatus {
            current_scan_count: s.currentScanCount,
            current_total_count: s.currentTotalCount,
            current_index: s.currentIndex,
        }
    }
}

/// A single input channel descriptor, used with [`DaqDevice::daq_in_scan`](crate::DaqDevice::daq_in_scan).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DaqInChanDescriptor {
    /// The channel number.
    pub channel: i32,
    /// The type of input for the specified channel.
    pub chan_type: DaqInChanType,
    /// The range to be used for the specified channel; ignored if not analog.
    pub range: Range,
}

impl DaqInChanDescriptor {
    /// Create a new channel descriptor.
    pub fn new(channel: i32, chan_type: DaqInChanType, range: Range) -> DaqInChanDescriptor {
        DaqInChanDescriptor {
            channel,
            chan_type,
            range,
        }
    }

    /// Create a descriptor for a single-ended analog input channel.
    pub fn analog_se(channel: i32, range: Range) -> DaqInChanDescriptor {
        DaqInChanDescriptor::new(channel, DaqInChanType::AnalogSe, range)
    }

    /// Create a descriptor for the D/A monitor channel. This is only supported
    /// by devices with an internal output monitor.
    pub fn dac(channel: i32, range: Range) -> DaqInChanDescriptor {
        DaqInChanDescriptor::new(channel, DaqInChanType::Dac, range)
    }
}

impl From<&DaqInChanDescriptor> for sys::DaqInChanDescriptor {
    fn from(d: &DaqInChanDescriptor) -> sys::DaqInChanDescriptor {
        let mut sd: sys::DaqInChanDescriptor = unsafe { std::mem::zeroed() };
        sd.channel = d.channel;
        sd.type_ = d.chan_type.into();
        sd.range = d.range.into();
        sd
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn range_values_match_header() {
        assert_eq!(u32::from(Range::Bip10Volts), 5);
        assert_eq!(u32::from(Range::Bip1Volts), 11);
        assert_eq!(u32::from(Range::Bip3Volts), 22);
    }

    #[test]
    fn scan_status_roundtrip() {
        assert_eq!(ScanStatus::from_raw(0), Some(ScanStatus::Idle));
        assert_eq!(ScanStatus::from_raw(1), Some(ScanStatus::Running));
        assert_eq!(ScanStatus::from_raw(2), None);
    }

    #[test]
    fn scan_option_combines() {
        let opts = ScanOption::CONTINUOUS | ScanOption::EXT_TRIGGER;
        assert_eq!(opts.bits(), 8 | 32);
    }

    #[test]
    fn descriptor_converts_to_sys() {
        let d = DaqInChanDescriptor::analog_se(2, Range::Bip1Volts);
        let sd: sys::DaqInChanDescriptor = (&d).into();
        assert_eq!(sd.channel, 2);
        assert_eq!(sd.type_, sys::DaqInChanType_DAQI_ANALOG_SE);
        assert_eq!(sd.range, sys::Range_BIP1VOLTS);
    }
}