#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DacType {
Helios,
EtherDream,
Idn,
LasercubeWifi,
LasercubeUsb,
#[cfg(feature = "oscilloscope")]
Oscilloscope,
Avb,
Custom(String),
}
impl DacType {
#[cfg(not(feature = "oscilloscope"))]
pub fn all() -> &'static [DacType] {
&[
DacType::Helios,
DacType::EtherDream,
DacType::Idn,
DacType::LasercubeWifi,
DacType::LasercubeUsb,
DacType::Avb,
]
}
#[cfg(feature = "oscilloscope")]
pub fn all() -> &'static [DacType] {
&[
DacType::Helios,
DacType::EtherDream,
DacType::Idn,
DacType::LasercubeWifi,
DacType::LasercubeUsb,
DacType::Avb,
DacType::Oscilloscope,
]
}
pub fn display_name(&self) -> &str {
match self {
DacType::Helios => "Helios",
DacType::EtherDream => "Ether Dream",
DacType::Idn => "IDN",
DacType::LasercubeWifi => "LaserCube WiFi",
DacType::LasercubeUsb => "LaserCube USB (Laserdock)",
#[cfg(feature = "oscilloscope")]
DacType::Oscilloscope => "Oscilloscope",
DacType::Avb => "AVB Audio Device",
DacType::Custom(name) => name,
}
}
pub fn description(&self) -> &'static str {
match self {
DacType::Helios => "USB laser DAC",
DacType::EtherDream => "Network laser DAC",
DacType::Idn => "ILDA Digital Network laser DAC",
DacType::LasercubeWifi => "WiFi laser DAC",
DacType::LasercubeUsb => "USB laser DAC",
#[cfg(feature = "oscilloscope")]
DacType::Oscilloscope => "Oscilloscope XY output via stereo audio",
DacType::Avb => "AVB audio network output",
DacType::Custom(_) => "Custom DAC",
}
}
}
impl fmt::Display for DacType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display_name())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EnabledDacTypes {
types: HashSet<DacType>,
}
impl EnabledDacTypes {
pub fn all() -> Self {
Self {
types: DacType::all().iter().cloned().collect(),
}
}
pub fn none() -> Self {
Self {
types: HashSet::new(),
}
}
pub fn is_enabled(&self, dac_type: DacType) -> bool {
self.types.contains(&dac_type)
}
pub fn enable(&mut self, dac_type: DacType) -> &mut Self {
self.types.insert(dac_type);
self
}
pub fn disable(&mut self, dac_type: DacType) -> &mut Self {
self.types.remove(&dac_type);
self
}
pub fn iter(&self) -> impl Iterator<Item = DacType> + '_ {
self.types.iter().cloned()
}
pub fn without(mut self, dac_type: DacType) -> Self {
self.types.remove(&dac_type);
self
}
pub fn is_empty(&self) -> bool {
self.types.is_empty()
}
}
impl Default for EnabledDacTypes {
fn default() -> Self {
Self::all()
}
}
impl std::iter::FromIterator<DacType> for EnabledDacTypes {
fn from_iter<I: IntoIterator<Item = DacType>>(iter: I) -> Self {
Self {
types: iter.into_iter().collect(),
}
}
}
impl Extend<DacType> for EnabledDacTypes {
fn extend<I: IntoIterator<Item = DacType>>(&mut self, iter: I) {
self.types.extend(iter);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DacDevice {
pub name: String,
pub dac_type: DacType,
}
impl DacDevice {
pub fn new(name: String, dac_type: DacType) -> Self {
Self { name, dac_type }
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum DacConnectionState {
Connected { name: String },
Stopped { name: String },
Lost { name: String, error: Option<String> },
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DacCapabilities {
pub pps_min: u32,
pub pps_max: u32,
pub max_points_per_chunk: usize,
pub output_model: OutputModel,
}
impl Default for DacCapabilities {
fn default() -> Self {
Self {
pps_min: 1,
pps_max: 100_000,
max_points_per_chunk: 4096,
output_model: OutputModel::NetworkFifo,
}
}
}
pub fn caps_for_dac_type(dac_type: &DacType) -> DacCapabilities {
match dac_type {
#[cfg(feature = "helios")]
DacType::Helios => crate::protocols::helios::default_capabilities(),
#[cfg(not(feature = "helios"))]
DacType::Helios => DacCapabilities::default(),
#[cfg(feature = "ether-dream")]
DacType::EtherDream => crate::protocols::ether_dream::default_capabilities(),
#[cfg(not(feature = "ether-dream"))]
DacType::EtherDream => DacCapabilities::default(),
#[cfg(feature = "idn")]
DacType::Idn => crate::protocols::idn::default_capabilities(),
#[cfg(not(feature = "idn"))]
DacType::Idn => DacCapabilities::default(),
#[cfg(feature = "lasercube-wifi")]
DacType::LasercubeWifi => crate::protocols::lasercube_wifi::default_capabilities(),
#[cfg(not(feature = "lasercube-wifi"))]
DacType::LasercubeWifi => DacCapabilities::default(),
#[cfg(feature = "lasercube-usb")]
DacType::LasercubeUsb => crate::protocols::lasercube_usb::default_capabilities(),
#[cfg(not(feature = "lasercube-usb"))]
DacType::LasercubeUsb => DacCapabilities::default(),
#[cfg(feature = "oscilloscope")]
DacType::Oscilloscope => DacCapabilities::default(),
#[cfg(feature = "avb")]
DacType::Avb => crate::protocols::avb::default_capabilities(),
#[cfg(not(feature = "avb"))]
DacType::Avb => DacCapabilities::default(),
DacType::Custom(_) => DacCapabilities::default(),
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum OutputModel {
UsbFrameSwap,
NetworkFifo,
UdpTimed,
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DacInfo {
pub id: String,
pub name: String,
pub kind: DacType,
pub caps: DacCapabilities,
}
impl DacInfo {
pub fn new(
id: impl Into<String>,
name: impl Into<String>,
kind: DacType,
caps: DacCapabilities,
) -> Self {
Self {
id: id.into(),
name: name.into(),
kind,
caps,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dac_type_all_returns_all_builtin_types() {
let all_types = DacType::all();
#[cfg(not(feature = "oscilloscope"))]
assert_eq!(all_types.len(), 6);
#[cfg(feature = "oscilloscope")]
assert_eq!(all_types.len(), 7);
assert!(all_types.contains(&DacType::Helios));
assert!(all_types.contains(&DacType::EtherDream));
assert!(all_types.contains(&DacType::Idn));
assert!(all_types.contains(&DacType::LasercubeWifi));
assert!(all_types.contains(&DacType::LasercubeUsb));
assert!(all_types.contains(&DacType::Avb));
#[cfg(feature = "oscilloscope")]
assert!(all_types.contains(&DacType::Oscilloscope));
}
#[test]
fn test_dac_type_display_uses_display_name() {
assert_eq!(
format!("{}", DacType::Helios),
DacType::Helios.display_name()
);
assert_eq!(
format!("{}", DacType::EtherDream),
DacType::EtherDream.display_name()
);
}
#[test]
fn test_dac_type_can_be_used_in_hashset() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(DacType::Helios);
set.insert(DacType::Helios);
assert_eq!(set.len(), 1);
}
#[test]
fn test_enabled_dac_types_all_enables_everything() {
let enabled = EnabledDacTypes::all();
for dac_type in DacType::all() {
assert!(
enabled.is_enabled(dac_type.clone()),
"{:?} should be enabled",
dac_type
);
}
assert!(!enabled.is_empty());
}
#[test]
fn test_enabled_dac_types_none_disables_everything() {
let enabled = EnabledDacTypes::none();
for dac_type in DacType::all() {
assert!(
!enabled.is_enabled(dac_type.clone()),
"{:?} should be disabled",
dac_type
);
}
assert!(enabled.is_empty());
}
#[test]
fn test_enabled_dac_types_enable_disable_toggles_correctly() {
let mut enabled = EnabledDacTypes::none();
enabled.enable(DacType::Helios);
assert!(enabled.is_enabled(DacType::Helios));
assert!(!enabled.is_enabled(DacType::EtherDream));
enabled.enable(DacType::EtherDream);
assert!(enabled.is_enabled(DacType::Helios));
assert!(enabled.is_enabled(DacType::EtherDream));
enabled.disable(DacType::Helios);
assert!(!enabled.is_enabled(DacType::Helios));
assert!(enabled.is_enabled(DacType::EtherDream));
}
#[test]
fn test_enabled_dac_types_iter_only_returns_enabled() {
let mut enabled = EnabledDacTypes::none();
enabled.enable(DacType::Helios);
enabled.enable(DacType::Idn);
let types: Vec<DacType> = enabled.iter().collect();
assert_eq!(types.len(), 2);
assert!(types.contains(&DacType::Helios));
assert!(types.contains(&DacType::Idn));
assert!(!types.contains(&DacType::EtherDream));
}
#[test]
fn test_enabled_dac_types_default_enables_all() {
let enabled = EnabledDacTypes::default();
for dac_type in DacType::all() {
assert!(enabled.is_enabled(dac_type.clone()));
}
}
#[test]
fn test_enabled_dac_types_idempotent_operations() {
let mut enabled = EnabledDacTypes::none();
enabled.enable(DacType::Helios);
enabled.enable(DacType::Helios);
assert!(enabled.is_enabled(DacType::Helios));
enabled.disable(DacType::Helios);
enabled.disable(DacType::Helios);
assert!(!enabled.is_enabled(DacType::Helios));
}
#[test]
fn test_enabled_dac_types_chaining() {
let mut enabled = EnabledDacTypes::none();
enabled
.enable(DacType::Helios)
.enable(DacType::EtherDream)
.disable(DacType::Helios);
assert!(!enabled.is_enabled(DacType::Helios));
assert!(enabled.is_enabled(DacType::EtherDream));
}
#[test]
fn test_dac_connection_state_equality() {
let s1 = DacConnectionState::Connected {
name: "DAC1".to_string(),
};
let s2 = DacConnectionState::Connected {
name: "DAC1".to_string(),
};
let s3 = DacConnectionState::Connected {
name: "DAC2".to_string(),
};
let s4 = DacConnectionState::Lost {
name: "DAC1".to_string(),
error: None,
};
assert_eq!(s1, s2);
assert_ne!(s1, s3); assert_ne!(s1, s4); }
}