use std::{ops::Index, path::PathBuf}; use super::api::StreamApiDescr; use super::datatype::DataType; use super::deviceinfo::DeviceInfo; use super::qty::Qty; use crate::config::*; use anyhow::Result; use serde::{Deserialize, Serialize}; /// DAQ Configuration for a single channel #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct DaqChannel { /// Whether the channel is enabled pub enabled: bool, /// Readable name for channel pub name: String, /// To convert to physical units. Divide values by this to obtain it. pub sensitivity: Flt, /// Enabled constant current power supply for sensor (if device supports it) pub IEPEEnabled: bool, /// Enabled hardware AC coupling (if) pub ACCouplingMode: bool, /// If supporting multiple input ranges: select the right index pub rangeIndex: usize, /// Physical quantity pub qty: Qty, } impl Default for DaqChannel { fn default() -> Self { DaqChannel { enabled: false, name: "".into(), sensitivity: -1.0, IEPEEnabled: false, ACCouplingMode: false, rangeIndex: 0, qty: Qty::Number, } } } impl DaqChannel { /// Default channel configuration for audio input from a certain channel pub fn defaultAudio(name: String) -> Self { DaqChannel { enabled: true, name, sensitivity: 1.0, IEPEEnabled: false, ACCouplingMode: false, rangeIndex: 0, qty: Qty::Number, } } } /// Configuration of a device. #[derive(PartialEq, Clone, Debug, Serialize, Deserialize)] pub struct DaqConfig { /// The API pub api: StreamApiDescr, /// Device name. Should match when starting a stream pub device_name: String, /// Configuration of the input channels pub inchannel_config: Vec, /// Configuration of the output channels pub outchannel_config: Vec, /// The data type to use pub dtype: DataType, /// Whether to apply a digital high pass on the input. <= 0 means disabled. > 0 means, the value specifies the cut-on frequency for the first order high pass filter. pub digitalHighPassCutOn: Flt, /// The index to use in the list of possible sample rates sampleRateIndex: usize, /// The index to use in the list of possible frames per block framesPerBlockIndex: usize, /// Used when output channels should be monitored, i.e. reverse-looped back as input channels. monitorOutput: bool, } impl DaqConfig { /// Creates a new default device configuration for a given device as specified with /// the DeviceInfo descriptor. pub fn newFromDeviceInfo(devinfo: &DeviceInfo) -> DaqConfig { let inchannel_config = (0..devinfo.iChannelCount) .map(|_| DaqChannel::default()) .collect(); let outchannel_config = (0..devinfo.oChannelCount) .map(|_| DaqChannel::default()) .collect(); let sampleRateIndex = devinfo .avSampleRates .iter() .position(|x| x == &devinfo.prefSampleRate) .unwrap_or(devinfo.avSampleRates.len() - 1); // Choose 4096 when in list, otherwise choose the highes available value in list let framesPerBlockIndex = devinfo .avFramesPerBlock .iter() .position(|x| x == &4096) .unwrap_or(devinfo.avFramesPerBlock.len() - 1); DaqConfig { api: devinfo.api.clone(), device_name: devinfo.device_name.clone(), inchannel_config, outchannel_config, dtype: devinfo.prefDataType, digitalHighPassCutOn: -1.0, sampleRateIndex, framesPerBlockIndex, monitorOutput: false, } } /// Serialize DaqConfig object to TOML. /// /// Args /// /// * writer: Output writer, can be file or string, or anything that *is* std::io::Write /// pub fn serialize_TOML(&self, writer: &mut dyn std::io::Write) -> Result<()> { let ser_str = toml::to_string(&self)?; writer.write_all(ser_str.as_bytes())?; Ok(()) } /// Deserialize structure from TOML data /// /// # Args /// /// * reader: implements the Read trait, from which we read the data. pub fn deserialize_TOML(reader: &mut T) -> Result where T: std::io::Read, { let mut read_str = vec![]; reader.read_to_end(&mut read_str)?; let read_str = String::from_utf8(read_str)?; DaqConfig::deserialize_TOML_str(&read_str) } /// Deserialize from TOML string /// /// # Args /// /// * st: string containing TOML data. pub fn deserialize_TOML_str(st: &String) -> Result { let res: DaqConfig = toml::from_str(st)?; Ok(res) } /// Write this configuration to a TOML file. /// /// Args /// /// * file: Name of file to write to /// pub fn serialize_TOML_file(&self, file: &PathBuf) -> Result<()> { let mut file = std::fs::File::create(file)?; self.serialize_TOML(&mut file)?; Ok(()) } /// Returns a list of enabled input channel numbers as indices /// in the list of all input channels (enabled and not) pub fn enabledInchannelsList(&self) -> Vec { self.inchannel_config .iter() .enumerate() .filter(|(_, ch)| ch.enabled) .map(|(i, _)| i) .collect() } /// Returns the total number of channels that appear in a running input stream. pub fn numberEnabledInChannels(&self) -> usize { self.inchannel_config.iter().filter(|ch| ch.enabled).count() } /// Returns the total number of channels that appear in a running output stream. pub fn numberEnabledOutChannels(&self) -> usize { self.outchannel_config .iter() .filter(|ch| ch.enabled) .count() } /// Provide samplerate, based on device and specified sample rate index pub fn sampleRate(&self, dev: &DeviceInfo) -> Flt { dev.avSampleRates[self.sampleRateIndex] } /// Provide samplerate, based on device and specified sample rate index pub fn framesPerBlock(&self, dev: &DeviceInfo) -> usize { dev.avFramesPerBlock[self.framesPerBlockIndex] } /// Returns vec of channel configuration for enabled input channels only pub fn enabledInchannelConfig(&self) -> Vec { self.inchannel_config .iter() .filter(|ch| ch.enabled) .cloned() .collect() } /// Returns vec of channel configuration for enabled output channels only pub fn enabledOutchannelConfig(&self) -> Vec { self.outchannel_config .iter() .filter(|ch| ch.enabled) .cloned() .collect() } }