1#[cfg(feature = "serde")]
13use serde::{Deserialize, Serialize};
14use std::collections::HashSet;
15use std::fmt;
16
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
19#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
20pub enum DacType {
21 Helios,
23 EtherDream,
25 Idn,
27 #[cfg_attr(feature = "serde", serde(alias = "LasercubeWifi"))]
29 LaserCubeNetwork,
30 #[cfg_attr(feature = "serde", serde(alias = "LasercubeUsb"))]
32 LaserCubeUsb,
33 #[cfg(feature = "oscilloscope")]
36 Oscilloscope,
37 Avb,
39 Custom(String),
41}
42
43impl DacType {
44 #[cfg(not(feature = "oscilloscope"))]
46 pub fn all() -> &'static [DacType] {
47 &[
48 DacType::Helios,
49 DacType::EtherDream,
50 DacType::Idn,
51 DacType::LaserCubeNetwork,
52 DacType::LaserCubeUsb,
53 DacType::Avb,
54 ]
55 }
56
57 #[cfg(feature = "oscilloscope")]
59 pub fn all() -> &'static [DacType] {
60 &[
61 DacType::Helios,
62 DacType::EtherDream,
63 DacType::Idn,
64 DacType::LaserCubeNetwork,
65 DacType::LaserCubeUsb,
66 DacType::Avb,
67 DacType::Oscilloscope,
68 ]
69 }
70
71 pub fn display_name(&self) -> &str {
73 match self {
74 DacType::Helios => "Helios",
75 DacType::EtherDream => "Ether Dream",
76 DacType::Idn => "IDN",
77 DacType::LaserCubeNetwork => "LaserCube Network",
78 DacType::LaserCubeUsb => "LaserCube USB (LaserDock)",
79 #[cfg(feature = "oscilloscope")]
80 DacType::Oscilloscope => "Oscilloscope",
81 DacType::Avb => "AVB Audio Device",
82 DacType::Custom(name) => name,
83 }
84 }
85
86 pub fn description(&self) -> &'static str {
88 match self {
89 DacType::Helios => "USB laser DAC",
90 DacType::EtherDream => "Network laser DAC",
91 DacType::Idn => "ILDA Digital Network laser DAC",
92 DacType::LaserCubeNetwork => "LaserCube network DAC",
93 DacType::LaserCubeUsb => "USB laser DAC",
94 #[cfg(feature = "oscilloscope")]
95 DacType::Oscilloscope => "Oscilloscope XY output via stereo audio",
96 DacType::Avb => "AVB audio network output",
97 DacType::Custom(_) => "Custom DAC",
98 }
99 }
100}
101
102impl fmt::Display for DacType {
103 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104 write!(f, "{}", self.display_name())
105 }
106}
107
108#[derive(Debug, Clone, PartialEq, Eq)]
110#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
111pub struct EnabledDacTypes {
112 types: HashSet<DacType>,
113}
114
115impl EnabledDacTypes {
116 pub fn all() -> Self {
118 Self {
119 types: DacType::all().iter().cloned().collect(),
120 }
121 }
122
123 pub fn none() -> Self {
125 Self {
126 types: HashSet::new(),
127 }
128 }
129
130 pub fn is_enabled(&self, dac_type: DacType) -> bool {
132 self.types.contains(&dac_type)
133 }
134
135 pub fn enable(&mut self, dac_type: DacType) -> &mut Self {
151 self.types.insert(dac_type);
152 self
153 }
154
155 pub fn disable(&mut self, dac_type: DacType) -> &mut Self {
171 self.types.remove(&dac_type);
172 self
173 }
174
175 pub fn iter(&self) -> impl Iterator<Item = DacType> + '_ {
177 self.types.iter().cloned()
178 }
179
180 pub fn without(mut self, dac_type: DacType) -> Self {
186 self.types.remove(&dac_type);
187 self
188 }
189
190 pub fn is_empty(&self) -> bool {
192 self.types.is_empty()
193 }
194}
195
196impl Default for EnabledDacTypes {
197 fn default() -> Self {
198 Self::all()
199 }
200}
201
202impl std::iter::FromIterator<DacType> for EnabledDacTypes {
203 fn from_iter<I: IntoIterator<Item = DacType>>(iter: I) -> Self {
204 Self {
205 types: iter.into_iter().collect(),
206 }
207 }
208}
209
210impl Extend<DacType> for EnabledDacTypes {
211 fn extend<I: IntoIterator<Item = DacType>>(&mut self, iter: I) {
212 self.types.extend(iter);
213 }
214}
215
216#[derive(Debug, Clone, PartialEq, Eq, Hash)]
219#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
220pub struct DacDevice {
221 pub name: String,
222 pub dac_type: DacType,
223}
224
225impl DacDevice {
226 pub fn new(name: String, dac_type: DacType) -> Self {
227 Self { name, dac_type }
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq, Hash)]
233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
234pub enum DacConnectionState {
235 Connected { name: String },
237 Stopped { name: String },
239 Lost { name: String, error: Option<String> },
241}
242
243#[derive(Clone, Debug)]
245#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
246pub struct DacCapabilities {
247 pub pps_min: u32,
253 pub pps_max: u32,
255 pub max_points_per_chunk: usize,
257 pub output_model: OutputModel,
259}
260
261impl Default for DacCapabilities {
262 fn default() -> Self {
263 Self {
264 pps_min: 1,
265 pps_max: 100_000,
266 max_points_per_chunk: 4096,
267 output_model: OutputModel::NetworkFifo,
268 }
269 }
270}
271
272pub fn caps_for_dac_type(dac_type: &DacType) -> DacCapabilities {
279 match dac_type {
280 #[cfg(feature = "helios")]
281 DacType::Helios => crate::protocols::helios::default_capabilities(),
282 #[cfg(not(feature = "helios"))]
283 DacType::Helios => DacCapabilities::default(),
284
285 #[cfg(feature = "ether-dream")]
286 DacType::EtherDream => crate::protocols::ether_dream::default_capabilities(),
287 #[cfg(not(feature = "ether-dream"))]
288 DacType::EtherDream => DacCapabilities::default(),
289
290 #[cfg(feature = "idn")]
291 DacType::Idn => crate::protocols::idn::default_capabilities(),
292 #[cfg(not(feature = "idn"))]
293 DacType::Idn => DacCapabilities::default(),
294
295 #[cfg(feature = "lasercube-network")]
296 DacType::LaserCubeNetwork => crate::protocols::lasercube_network::default_capabilities(),
297 #[cfg(not(feature = "lasercube-network"))]
298 DacType::LaserCubeNetwork => DacCapabilities::default(),
299
300 #[cfg(feature = "lasercube-usb")]
301 DacType::LaserCubeUsb => crate::protocols::lasercube_usb::default_capabilities(),
302 #[cfg(not(feature = "lasercube-usb"))]
303 DacType::LaserCubeUsb => DacCapabilities::default(),
304
305 #[cfg(feature = "oscilloscope")]
306 DacType::Oscilloscope => DacCapabilities::default(), #[cfg(feature = "avb")]
309 DacType::Avb => crate::protocols::avb::default_capabilities(),
310 #[cfg(not(feature = "avb"))]
311 DacType::Avb => DacCapabilities::default(),
312
313 DacType::Custom(_) => DacCapabilities::default(),
314 }
315}
316
317#[derive(Clone, Debug, PartialEq, Eq)]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320pub enum OutputModel {
321 UsbFrameSwap,
323 NetworkFifo,
325 UdpTimed,
327 BlockingFifo,
336}
337
338#[derive(Clone, Debug)]
340#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
341pub struct DacInfo {
342 pub id: String,
344 pub name: String,
346 pub kind: DacType,
348 pub caps: DacCapabilities,
350}
351
352impl DacInfo {
353 pub fn new(
355 id: impl Into<String>,
356 name: impl Into<String>,
357 kind: DacType,
358 caps: DacCapabilities,
359 ) -> Self {
360 Self {
361 id: id.into(),
362 name: name.into(),
363 kind,
364 caps,
365 }
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372
373 #[test]
378 fn test_dac_type_all_returns_all_builtin_types() {
379 let all_types = DacType::all();
380 #[cfg(not(feature = "oscilloscope"))]
381 assert_eq!(all_types.len(), 6);
382 #[cfg(feature = "oscilloscope")]
383 assert_eq!(all_types.len(), 7);
384 assert!(all_types.contains(&DacType::Helios));
385 assert!(all_types.contains(&DacType::EtherDream));
386 assert!(all_types.contains(&DacType::Idn));
387 assert!(all_types.contains(&DacType::LaserCubeNetwork));
388 assert!(all_types.contains(&DacType::LaserCubeUsb));
389 assert!(all_types.contains(&DacType::Avb));
390 #[cfg(feature = "oscilloscope")]
391 assert!(all_types.contains(&DacType::Oscilloscope));
392 }
393
394 #[test]
395 fn test_dac_type_display_uses_display_name() {
396 assert_eq!(
398 format!("{}", DacType::Helios),
399 DacType::Helios.display_name()
400 );
401 assert_eq!(
402 format!("{}", DacType::EtherDream),
403 DacType::EtherDream.display_name()
404 );
405 }
406
407 #[test]
408 fn test_dac_type_can_be_used_in_hashset() {
409 use std::collections::HashSet;
410
411 let mut set = HashSet::new();
412 set.insert(DacType::Helios);
413 set.insert(DacType::Helios); assert_eq!(set.len(), 1);
416 }
417
418 #[cfg(feature = "serde")]
419 #[test]
420 fn test_dac_type_deserializes_legacy_lasercube_names() {
421 assert_eq!(
424 serde_json::from_str::<DacType>("\"LasercubeWifi\"").unwrap(),
425 DacType::LaserCubeNetwork
426 );
427 assert_eq!(
428 serde_json::from_str::<DacType>("\"LasercubeUsb\"").unwrap(),
429 DacType::LaserCubeUsb
430 );
431 }
432
433 #[test]
438 fn test_enabled_dac_types_all_enables_everything() {
439 let enabled = EnabledDacTypes::all();
440 for dac_type in DacType::all() {
441 assert!(
442 enabled.is_enabled(dac_type.clone()),
443 "{:?} should be enabled",
444 dac_type
445 );
446 }
447 assert!(!enabled.is_empty());
448 }
449
450 #[test]
451 fn test_enabled_dac_types_none_disables_everything() {
452 let enabled = EnabledDacTypes::none();
453 for dac_type in DacType::all() {
454 assert!(
455 !enabled.is_enabled(dac_type.clone()),
456 "{:?} should be disabled",
457 dac_type
458 );
459 }
460 assert!(enabled.is_empty());
461 }
462
463 #[test]
464 fn test_enabled_dac_types_enable_disable_toggles_correctly() {
465 let mut enabled = EnabledDacTypes::none();
466
467 enabled.enable(DacType::Helios);
469 assert!(enabled.is_enabled(DacType::Helios));
470 assert!(!enabled.is_enabled(DacType::EtherDream));
471
472 enabled.enable(DacType::EtherDream);
474 assert!(enabled.is_enabled(DacType::Helios));
475 assert!(enabled.is_enabled(DacType::EtherDream));
476
477 enabled.disable(DacType::Helios);
479 assert!(!enabled.is_enabled(DacType::Helios));
480 assert!(enabled.is_enabled(DacType::EtherDream));
481 }
482
483 #[test]
484 fn test_enabled_dac_types_iter_only_returns_enabled() {
485 let mut enabled = EnabledDacTypes::none();
486 enabled.enable(DacType::Helios);
487 enabled.enable(DacType::Idn);
488
489 let types: Vec<DacType> = enabled.iter().collect();
490 assert_eq!(types.len(), 2);
491 assert!(types.contains(&DacType::Helios));
492 assert!(types.contains(&DacType::Idn));
493 assert!(!types.contains(&DacType::EtherDream));
494 }
495
496 #[test]
497 fn test_enabled_dac_types_default_enables_all() {
498 let enabled = EnabledDacTypes::default();
499 for dac_type in DacType::all() {
501 assert!(enabled.is_enabled(dac_type.clone()));
502 }
503 }
504
505 #[test]
506 fn test_enabled_dac_types_idempotent_operations() {
507 let mut enabled = EnabledDacTypes::none();
508
509 enabled.enable(DacType::Helios);
511 enabled.enable(DacType::Helios);
512 assert!(enabled.is_enabled(DacType::Helios));
513
514 enabled.disable(DacType::Helios);
516 enabled.disable(DacType::Helios);
517 assert!(!enabled.is_enabled(DacType::Helios));
518 }
519
520 #[test]
521 fn test_enabled_dac_types_chaining() {
522 let mut enabled = EnabledDacTypes::none();
523 enabled
524 .enable(DacType::Helios)
525 .enable(DacType::EtherDream)
526 .disable(DacType::Helios);
527
528 assert!(!enabled.is_enabled(DacType::Helios));
529 assert!(enabled.is_enabled(DacType::EtherDream));
530 }
531
532 #[test]
537 fn test_dac_connection_state_equality() {
538 let s1 = DacConnectionState::Connected {
539 name: "DAC1".to_string(),
540 };
541 let s2 = DacConnectionState::Connected {
542 name: "DAC1".to_string(),
543 };
544 let s3 = DacConnectionState::Connected {
545 name: "DAC2".to_string(),
546 };
547 let s4 = DacConnectionState::Lost {
548 name: "DAC1".to_string(),
549 error: None,
550 };
551
552 assert_eq!(s1, s2);
553 assert_ne!(s1, s3); assert_ne!(s1, s4); }
556}