use std::ffi::CStr;
use std::fmt;
use uldaq_sys as sys;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UlError(u32);
macro_rules! ul_error_consts {
($($name:ident => $sys_const:ident : $doc:literal),* $(,)?) => {
$(
#[doc = $doc]
pub const $name: UlError = UlError(uldaq_sys::$sys_const);
)*
};
}
impl UlError {
pub fn raw(&self) -> u32 {
self.0
}
pub fn from_raw(raw: u32) -> UlError {
UlError(raw)
}
pub fn message(&self) -> String {
const ERR_MSG_LEN: usize = sys::ERR_MSG_LEN as usize;
let mut errmsg = [0 as std::os::raw::c_char; ERR_MSG_LEN];
let err = unsafe { sys::ulGetErrMsg(self.0, errmsg.as_mut_ptr()) };
if err != sys::UlError_ERR_NO_ERROR {
return format!("(unable to obtain error message, UL error code {err})");
}
let cstr = unsafe { CStr::from_ptr(errmsg.as_ptr()) };
cstr.to_string_lossy().into_owned()
}
ul_error_consts! {
NO_ERROR => UlError_ERR_NO_ERROR: "No error has occurred.",
UNHANDLED_EXCEPTION => UlError_ERR_UNHANDLED_EXCEPTION: "Unhandled internal exception.",
BAD_DEV_HANDLE => UlError_ERR_BAD_DEV_HANDLE: "Invalid device handle.",
BAD_DEV_TYPE => UlError_ERR_BAD_DEV_TYPE: "This function cannot be used with this device.",
USB_DEV_NO_PERMISSION => UlError_ERR_USB_DEV_NO_PERMISSION: "Insufficient permission to access this device.",
USB_INTERFACE_CLAIMED => UlError_ERR_USB_INTERFACE_CLAIMED: "USB interface is already claimed.",
DEV_NOT_FOUND => UlError_ERR_DEV_NOT_FOUND: "Device not found.",
DEV_NOT_CONNECTED => UlError_ERR_DEV_NOT_CONNECTED: "Device not connected or connection lost.",
DEAD_DEV => UlError_ERR_DEAD_DEV: "Device no longer responding.",
BAD_BUFFER_SIZE => UlError_ERR_BAD_BUFFER_SIZE: "Buffer too small for operation.",
BAD_BUFFER => UlError_ERR_BAD_BUFFER: "Invalid buffer.",
BAD_MEM_TYPE => UlError_ERR_BAD_MEM_TYPE: "Invalid memory type.",
BAD_MEM_REGION => UlError_ERR_BAD_MEM_REGION: "Invalid memory region.",
BAD_RANGE => UlError_ERR_BAD_RANGE: "Invalid range.",
BAD_AI_CHAN => UlError_ERR_BAD_AI_CHAN: "Invalid analog input channel specified.",
BAD_INPUT_MODE => UlError_ERR_BAD_INPUT_MODE: "Invalid input mode specified.",
ALREADY_ACTIVE => UlError_ERR_ALREADY_ACTIVE: "A background process is already in progress.",
BAD_TRIG_TYPE => UlError_ERR_BAD_TRIG_TYPE: "Invalid trigger type specified.",
OVERRUN => UlError_ERR_OVERRUN: "FIFO overrun, data was not transferred from device fast enough.",
UNDERRUN => UlError_ERR_UNDERRUN: "FIFO underrun, data was not transferred to device fast enough.",
TIMEDOUT => UlError_ERR_TIMEDOUT: "Operation timed out.",
BAD_OPTION => UlError_ERR_BAD_OPTION: "Invalid option specified.",
BAD_RATE => UlError_ERR_BAD_RATE: "Invalid sampling rate specified.",
BAD_BURSTIO_COUNT => UlError_ERR_BAD_BURSTIO_COUNT: "Sample count cannot be greater than FIFO size for BURSTIO scans.",
CONFIG_NOT_SUPPORTED => UlError_ERR_CONFIG_NOT_SUPPORTED: "Configuration not supported.",
BAD_CONFIG_VAL => UlError_ERR_BAD_CONFIG_VAL: "Invalid configuration value.",
BAD_AI_CHAN_TYPE => UlError_ERR_BAD_AI_CHAN_TYPE: "Invalid analog input channel type specified.",
ADC_OVERRUN => UlError_ERR_ADC_OVERRUN: "ADC overrun occurred.",
BAD_TC_TYPE => UlError_ERR_BAD_TC_TYPE: "Invalid thermocouple type specified.",
BAD_UNIT => UlError_ERR_BAD_UNIT: "Invalid unit specified.",
BAD_QUEUE_SIZE => UlError_ERR_BAD_QUEUE_SIZE: "Invalid queue size.",
BAD_CONFIG_ITEM => UlError_ERR_BAD_CONFIG_ITEM: "Invalid config item specified.",
BAD_INFO_ITEM => UlError_ERR_BAD_INFO_ITEM: "Invalid info item specified.",
BAD_FLAG => UlError_ERR_BAD_FLAG: "Invalid flag specified.",
BAD_SAMPLE_COUNT => UlError_ERR_BAD_SAMPLE_COUNT: "Invalid sample count specified.",
INTERNAL => UlError_ERR_INTERNAL: "Internal error.",
BAD_COUPLING_MODE => UlError_ERR_BAD_COUPLING_MODE: "Invalid coupling mode.",
BAD_SENSOR_SENSITIVITY => UlError_ERR_BAD_SENSOR_SENSITIVITY: "Invalid sensor sensitivity.",
BAD_IEPE_MODE => UlError_ERR_BAD_IEPE_MODE: "Invalid IEPE mode.",
BAD_AI_CHAN_QUEUE => UlError_ERR_BAD_AI_CHAN_QUEUE: "Invalid channel queue specified.",
BAD_AI_GAIN_QUEUE => UlError_ERR_BAD_AI_GAIN_QUEUE: "Invalid gain queue specified.",
BAD_AI_MODE_QUEUE => UlError_ERR_BAD_AI_MODE_QUEUE: "Invalid mode queue specified.",
FPGA_FILE_NOT_FOUND => UlError_ERR_FPGA_FILE_NOT_FOUND: "FPGA file not found.",
UNABLE_TO_READ_FPGA_FILE => UlError_ERR_UNABLE_TO_READ_FPGA_FILE: "Unable to read FPGA file.",
NO_FPGA => UlError_ERR_NO_FPGA: "No FPGA installed.",
BAD_ARG => UlError_ERR_BAD_ARG: "Invalid argument.",
MIN_SLOPE_VAL_REACHED => UlError_ERR_MIN_SLOPE_VAL_REACHED: "Minimum slope value reached.",
MAX_SLOPE_VAL_REACHED => UlError_ERR_MAX_SLOPE_VAL_REACHED: "Maximum slope value reached.",
MIN_OFFSET_VAL_REACHED => UlError_ERR_MIN_OFFSET_VAL_REACHED: "Minimum offset value reached.",
MAX_OFFSET_VAL_REACHED => UlError_ERR_MAX_OFFSET_VAL_REACHED: "Maximum offset value reached.",
BAD_PORT_TYPE => UlError_ERR_BAD_PORT_TYPE: "Invalid port type.",
WRONG_DIG_CONFIG => UlError_ERR_WRONG_DIG_CONFIG: "Digital direction configuration is wrong.",
BAD_BIT_NUM => UlError_ERR_BAD_BIT_NUM: "Invalid bit number.",
BAD_PORT_VAL => UlError_ERR_BAD_PORT_VAL: "Invalid port value.",
BAD_RETRIG_COUNT => UlError_ERR_BAD_RETRIG_COUNT: "Invalid retrigger count.",
BAD_AO_CHAN => UlError_ERR_BAD_AO_CHAN: "Invalid analog output channel specified.",
BAD_DA_VAL => UlError_ERR_BAD_DA_VAL: "Invalid D/A value.",
BAD_TMR => UlError_ERR_BAD_TMR: "Invalid timer specified.",
BAD_FREQUENCY => UlError_ERR_BAD_FREQUENCY: "Invalid frequency.",
BAD_DUTY_CYCLE => UlError_ERR_BAD_DUTY_CYCLE: "Invalid duty cycle.",
BAD_INITIAL_DELAY => UlError_ERR_BAD_INITIAL_DELAY: "Invalid initial delay.",
BAD_CTR => UlError_ERR_BAD_CTR: "Invalid counter specified.",
BAD_CTR_VAL => UlError_ERR_BAD_CTR_VAL: "Invalid counter value.",
BAD_DAQI_CHAN_TYPE => UlError_ERR_BAD_DAQI_CHAN_TYPE: "Invalid DAQ input channel type specified.",
BAD_NUM_CHANS => UlError_ERR_BAD_NUM_CHANS: "Invalid number of channels specified.",
BAD_CTR_REG => UlError_ERR_BAD_CTR_REG: "Invalid counter register specified.",
BAD_CTR_MEASURE_TYPE => UlError_ERR_BAD_CTR_MEASURE_TYPE: "Invalid counter measurement type.",
BAD_CTR_MEASURE_MODE => UlError_ERR_BAD_CTR_MEASURE_MODE: "Invalid counter measurement mode.",
BAD_DEBOUNCE_TIME => UlError_ERR_BAD_DEBOUNCE_TIME: "Invalid debounce time.",
BAD_DEBOUNCE_MODE => UlError_ERR_BAD_DEBOUNCE_MODE: "Invalid debounce mode.",
BAD_EDGE_DETECTION => UlError_ERR_BAD_EDGE_DETECTION: "Invalid edge detection.",
BAD_TICK_SIZE => UlError_ERR_BAD_TICK_SIZE: "Invalid tick size.",
BAD_DAQO_CHAN_TYPE => UlError_ERR_BAD_DAQO_CHAN_TYPE: "Invalid DAQ output channel type specified.",
NO_CONNECTION_ESTABLISHED => UlError_ERR_NO_CONNECTION_ESTABLISHED: "No connection established.",
BAD_EVENT_TYPE => UlError_ERR_BAD_EVENT_TYPE: "Invalid event type.",
EVENT_ALREADY_ENABLED => UlError_ERR_EVENT_ALREADY_ENABLED: "Event is already enabled.",
BAD_EVENT_PARAMETER => UlError_ERR_BAD_EVENT_PARAMETER: "Invalid event parameter.",
BAD_CALLBACK_FUCNTION => UlError_ERR_BAD_CALLBACK_FUCNTION: "Invalid callback function.",
BAD_MEM_ADDRESS => UlError_ERR_BAD_MEM_ADDRESS: "Invalid memory address.",
MEM_ACCESS_DENIED => UlError_ERR_MEM_ACCESS_DENIED: "Memory access denied.",
DEV_UNAVAILABLE => UlError_ERR_DEV_UNAVAILABLE: "Device unavailable.",
BAD_RETRIG_TRIG_TYPE => UlError_ERR_BAD_RETRIG_TRIG_TYPE: "Invalid retrigger trigger type.",
BAD_DEV_VER => UlError_ERR_BAD_DEV_VER: "Invalid device version.",
BAD_DIG_OPERATION => UlError_ERR_BAD_DIG_OPERATION: "Invalid digital operation.",
BAD_PORT_INDEX => UlError_ERR_BAD_PORT_INDEX: "Invalid port index.",
OPEN_CONNECTION => UlError_ERR_OPEN_CONNECTION: "Unable to open connection.",
DEV_NOT_READY => UlError_ERR_DEV_NOT_READY: "Device not ready.",
PACER_OVERRUN => UlError_ERR_PACER_OVERRUN: "Pacer overrun.",
BAD_TRIG_CHANNEL => UlError_ERR_BAD_TRIG_CHANNEL: "Invalid trigger channel.",
BAD_TRIG_LEVEL => UlError_ERR_BAD_TRIG_LEVEL: "Invalid trigger level.",
BAD_CHAN_ORDER => UlError_ERR_BAD_CHAN_ORDER: "Invalid channel order.",
TEMP_OUT_OF_RANGE => UlError_ERR_TEMP_OUT_OF_RANGE: "Temperature is out of range.",
TRIG_THRESHOLD_OUT_OF_RANGE => UlError_ERR_TRIG_THRESHOLD_OUT_OF_RANGE: "Trigger threshold is out of range.",
INCOMPATIBLE_FIRMWARE => UlError_ERR_INCOMPATIBLE_FIRMWARE: "Incompatible firmware.",
BAD_NET_IFC => UlError_ERR_BAD_NET_IFC: "Invalid network interface.",
BAD_NET_HOST => UlError_ERR_BAD_NET_HOST: "Invalid network host.",
BAD_NET_PORT => UlError_ERR_BAD_NET_PORT: "Invalid network port.",
NET_IFC_UNAVAILABLE => UlError_ERR_NET_IFC_UNAVAILABLE: "Network interface unavailable.",
NET_CONNECTION_FAILED => UlError_ERR_NET_CONNECTION_FAILED: "Network connection failed.",
BAD_CONNECTION_CODE => UlError_ERR_BAD_CONNECTION_CODE: "Invalid connection code.",
CONNECTION_CODE_IGNORED => UlError_ERR_CONNECTION_CODE_IGNORED: "Connection code ignored.",
NET_DEV_IN_USE => UlError_ERR_NET_DEV_IN_USE: "Network device in use.",
BAD_NET_FRAME => UlError_ERR_BAD_NET_FRAME: "Invalid network frame.",
NET_TIMEOUT => UlError_ERR_NET_TIMEOUT: "Network timeout.",
DATA_SOCKET_CONNECTION_FAILED => UlError_ERR_DATA_SOCKET_CONNECTION_FAILED: "Data socket connection failed.",
PORT_USED_FOR_ALARM => UlError_ERR_PORT_USED_FOR_ALARM: "Port used for alarm.",
BIT_USED_FOR_ALARM => UlError_ERR_BIT_USED_FOR_ALARM: "Bit used for alarm.",
CMR_EXCEEDED => UlError_ERR_CMR_EXCEEDED: "Common mode rejection exceeded.",
NET_BUFFER_OVERRUN => UlError_ERR_NET_BUFFER_OVERRUN: "Network buffer overrun.",
BAD_NET_BUFFER => UlError_ERR_BAD_NET_BUFFER: "Invalid network buffer.",
}
}
impl fmt::Display for UlError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "UL error {}: {}", self.0, self.message())
}
}
impl std::error::Error for UlError {}
pub fn check(raw: u32) -> Result<()> {
if raw == sys::UlError_ERR_NO_ERROR {
Ok(())
} else {
Err(Error::Ul(UlError(raw)))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Error {
Ul(UlError),
DeviceCreateFailed,
InvalidArgument(&'static str),
Internal(&'static str),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Ul(e) => write!(f, "{e}"),
Error::DeviceCreateFailed => write!(
f,
"Unable to create a handle to the DAQ device. Is the device currently in use?"
),
Error::InvalidArgument(msg) => write!(f, "Invalid argument: {msg}"),
Error::Internal(msg) => write!(f, "Internal error: {msg}"),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Ul(e) => Some(e),
_ => None,
}
}
}
impl From<UlError> for Error {
fn from(e: UlError) -> Error {
Error::Ul(e)
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_code_matches_raw_value() {
assert_eq!(UlError::NO_ERROR.raw(), 0);
assert_eq!(UlError::OVERRUN.raw(), 18);
assert_eq!(UlError::UNDERRUN.raw(), 19);
assert_eq!(UlError::BAD_DEV_HANDLE.raw(), 2);
assert_eq!(UlError::INTERNAL.raw(), 35);
}
#[test]
fn check_maps_no_error_to_ok() {
assert!(check(0).is_ok());
let err = check(18).unwrap_err();
assert!(matches!(err, Error::Ul(e) if e == UlError::OVERRUN));
}
#[test]
fn display_contains_code() {
let err = UlError::from_raw(0);
assert!(!err.message().is_empty());
assert!(format!("{err}").contains("0"));
}
}