1#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8use std::collections::HashSet;
9use std::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Default)]
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25pub struct LaserPoint {
26 pub x: f32,
28 pub y: f32,
30 pub r: u16,
32 pub g: u16,
34 pub b: u16,
36 pub intensity: u16,
38}
39
40impl LaserPoint {
41 pub fn new(x: f32, y: f32, r: u16, g: u16, b: u16, intensity: u16) -> Self {
43 Self {
44 x,
45 y,
46 r,
47 g,
48 b,
49 intensity,
50 }
51 }
52
53 pub fn blanked(x: f32, y: f32) -> Self {
55 Self {
56 x,
57 y,
58 r: 0,
59 g: 0,
60 b: 0,
61 intensity: 0,
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Default)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69pub struct LaserFrame {
70 pub pps: u32,
72 pub points: Vec<LaserPoint>,
74}
75
76impl LaserFrame {
77 pub fn new(pps: u32, points: Vec<LaserPoint>) -> Self {
79 Self { pps, points }
80 }
81}
82
83#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86pub enum DacType {
87 Helios,
89 EtherDream,
91 Idn,
93 LasercubeWifi,
95 LasercubeUsb,
97}
98
99impl DacType {
100 pub fn all() -> &'static [DacType] {
102 &[
103 DacType::Helios,
104 DacType::EtherDream,
105 DacType::Idn,
106 DacType::LasercubeWifi,
107 DacType::LasercubeUsb,
108 ]
109 }
110
111 pub fn display_name(&self) -> &'static str {
113 match self {
114 DacType::Helios => "Helios",
115 DacType::EtherDream => "Ether Dream",
116 DacType::Idn => "IDN",
117 DacType::LasercubeWifi => "LaserCube WiFi",
118 DacType::LasercubeUsb => "LaserCube USB (Laserdock)",
119 }
120 }
121
122 pub fn description(&self) -> &'static str {
124 match self {
125 DacType::Helios => "USB laser DAC",
126 DacType::EtherDream => "Network laser DAC",
127 DacType::Idn => "ILDA Digital Network laser DAC",
128 DacType::LasercubeWifi => "WiFi laser DAC",
129 DacType::LasercubeUsb => "USB laser DAC",
130 }
131 }
132}
133
134impl fmt::Display for DacType {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 write!(f, "{}", self.display_name())
137 }
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
142#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
143pub struct EnabledDacTypes {
144 types: HashSet<DacType>,
145}
146
147impl EnabledDacTypes {
148 pub fn all() -> Self {
150 Self {
151 types: DacType::all().iter().copied().collect(),
152 }
153 }
154
155 #[allow(dead_code)]
157 pub fn none() -> Self {
158 Self {
159 types: HashSet::new(),
160 }
161 }
162
163 pub fn is_enabled(&self, dac_type: DacType) -> bool {
165 self.types.contains(&dac_type)
166 }
167
168 pub fn enable(&mut self, dac_type: DacType) -> &mut Self {
184 self.types.insert(dac_type);
185 self
186 }
187
188 pub fn disable(&mut self, dac_type: DacType) -> &mut Self {
204 self.types.remove(&dac_type);
205 self
206 }
207
208 #[allow(dead_code)]
210 pub fn iter(&self) -> impl Iterator<Item = DacType> + '_ {
211 self.types.iter().copied()
212 }
213
214 #[allow(dead_code)]
216 pub fn is_empty(&self) -> bool {
217 self.types.is_empty()
218 }
219}
220
221impl Default for EnabledDacTypes {
222 fn default() -> Self {
223 Self::all()
224 }
225}
226
227impl std::iter::FromIterator<DacType> for EnabledDacTypes {
228 fn from_iter<I: IntoIterator<Item = DacType>>(iter: I) -> Self {
229 Self {
230 types: iter.into_iter().collect(),
231 }
232 }
233}
234
235impl Extend<DacType> for EnabledDacTypes {
236 fn extend<I: IntoIterator<Item = DacType>>(&mut self, iter: I) {
237 self.types.extend(iter);
238 }
239}
240
241#[derive(Debug, Clone, PartialEq, Eq, Hash)]
244#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245pub struct DacDevice {
246 pub name: String,
247 pub dac_type: DacType,
248}
249
250impl DacDevice {
251 pub fn new(name: String, dac_type: DacType) -> Self {
252 Self { name, dac_type }
253 }
254}
255
256#[derive(Debug, Clone, PartialEq, Eq, Hash)]
258#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
259pub enum DacConnectionState {
260 Connected { name: String },
262 Lost { name: String, error: Option<String> },
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
268#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
269pub struct DiscoveredDac {
270 pub dac_type: DacType,
272 pub id: String,
274 pub name: String,
276 pub address: Option<String>,
278 pub metadata: Option<String>,
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
291 fn test_laser_point_blanked_sets_all_colors_to_zero() {
292 let point = LaserPoint::blanked(0.25, 0.75);
294 assert_eq!(point.x, 0.25);
295 assert_eq!(point.y, 0.75);
296 assert_eq!(point.r, 0);
297 assert_eq!(point.g, 0);
298 assert_eq!(point.b, 0);
299 assert_eq!(point.intensity, 0);
300 }
301
302 #[test]
307 fn test_dac_type_all_returns_all_five_types() {
308 let all_types = DacType::all();
309 assert_eq!(all_types.len(), 5);
310 assert!(all_types.contains(&DacType::Helios));
311 assert!(all_types.contains(&DacType::EtherDream));
312 assert!(all_types.contains(&DacType::Idn));
313 assert!(all_types.contains(&DacType::LasercubeWifi));
314 assert!(all_types.contains(&DacType::LasercubeUsb));
315 }
316
317 #[test]
318 fn test_dac_type_display_uses_display_name() {
319 assert_eq!(
321 format!("{}", DacType::Helios),
322 DacType::Helios.display_name()
323 );
324 assert_eq!(
325 format!("{}", DacType::EtherDream),
326 DacType::EtherDream.display_name()
327 );
328 }
329
330 #[test]
331 fn test_dac_type_can_be_used_in_hashset() {
332 use std::collections::HashSet;
333
334 let mut set = HashSet::new();
335 set.insert(DacType::Helios);
336 set.insert(DacType::Helios); assert_eq!(set.len(), 1);
339 }
340
341 #[test]
346 fn test_enabled_dac_types_all_enables_everything() {
347 let enabled = EnabledDacTypes::all();
348 for dac_type in DacType::all() {
349 assert!(
350 enabled.is_enabled(*dac_type),
351 "{:?} should be enabled",
352 dac_type
353 );
354 }
355 assert!(!enabled.is_empty());
356 }
357
358 #[test]
359 fn test_enabled_dac_types_none_disables_everything() {
360 let enabled = EnabledDacTypes::none();
361 for dac_type in DacType::all() {
362 assert!(
363 !enabled.is_enabled(*dac_type),
364 "{:?} should be disabled",
365 dac_type
366 );
367 }
368 assert!(enabled.is_empty());
369 }
370
371 #[test]
372 fn test_enabled_dac_types_enable_disable_toggles_correctly() {
373 let mut enabled = EnabledDacTypes::none();
374
375 enabled.enable(DacType::Helios);
377 assert!(enabled.is_enabled(DacType::Helios));
378 assert!(!enabled.is_enabled(DacType::EtherDream));
379
380 enabled.enable(DacType::EtherDream);
382 assert!(enabled.is_enabled(DacType::Helios));
383 assert!(enabled.is_enabled(DacType::EtherDream));
384
385 enabled.disable(DacType::Helios);
387 assert!(!enabled.is_enabled(DacType::Helios));
388 assert!(enabled.is_enabled(DacType::EtherDream));
389 }
390
391 #[test]
392 fn test_enabled_dac_types_iter_only_returns_enabled() {
393 let mut enabled = EnabledDacTypes::none();
394 enabled.enable(DacType::Helios);
395 enabled.enable(DacType::Idn);
396
397 let types: Vec<DacType> = enabled.iter().collect();
398 assert_eq!(types.len(), 2);
399 assert!(types.contains(&DacType::Helios));
400 assert!(types.contains(&DacType::Idn));
401 assert!(!types.contains(&DacType::EtherDream));
402 }
403
404 #[test]
405 fn test_enabled_dac_types_default_enables_all() {
406 let enabled = EnabledDacTypes::default();
407 for dac_type in DacType::all() {
409 assert!(enabled.is_enabled(*dac_type));
410 }
411 }
412
413 #[test]
414 fn test_enabled_dac_types_idempotent_operations() {
415 let mut enabled = EnabledDacTypes::none();
416
417 enabled.enable(DacType::Helios);
419 enabled.enable(DacType::Helios);
420 assert!(enabled.is_enabled(DacType::Helios));
421
422 enabled.disable(DacType::Helios);
424 enabled.disable(DacType::Helios);
425 assert!(!enabled.is_enabled(DacType::Helios));
426 }
427
428 #[test]
429 fn test_enabled_dac_types_chaining() {
430 let mut enabled = EnabledDacTypes::none();
431 enabled
432 .enable(DacType::Helios)
433 .enable(DacType::EtherDream)
434 .disable(DacType::Helios);
435
436 assert!(!enabled.is_enabled(DacType::Helios));
437 assert!(enabled.is_enabled(DacType::EtherDream));
438 }
439
440 #[test]
445 fn test_dac_connection_state_equality() {
446 let s1 = DacConnectionState::Connected {
447 name: "DAC1".to_string(),
448 };
449 let s2 = DacConnectionState::Connected {
450 name: "DAC1".to_string(),
451 };
452 let s3 = DacConnectionState::Connected {
453 name: "DAC2".to_string(),
454 };
455 let s4 = DacConnectionState::Lost {
456 name: "DAC1".to_string(),
457 error: None,
458 };
459
460 assert_eq!(s1, s2);
461 assert_ne!(s1, s3); assert_ne!(s1, s4); }
464}