laser_dac/
lib.rs

1//! Unified DAC backend abstraction for laser projectors.
2//!
3//! This crate provides a common interface for communicating with various
4//! laser DAC (Digital-to-Analog Converter) hardware, allowing you to write
5//! laser frames without worrying about device-specific protocols.
6//!
7//! # Getting Started
8//!
9//! The easiest way to use this crate is with [`DacDiscoveryWorker`], which handles
10//! device discovery and connection in the background:
11//!
12//! ```no_run
13//! use laser_dac::{DacDiscoveryWorker, EnabledDacTypes, LaserFrame, LaserPoint};
14//! use std::thread;
15//! use std::time::Duration;
16//!
17//! // Start background discovery for all DAC types
18//! let discovery = DacDiscoveryWorker::builder()
19//!     .enabled_types(EnabledDacTypes::all())
20//!     .build();
21//!
22//! // Collect discovered devices
23//! let mut workers = Vec::new();
24//! for _ in 0..50 {
25//!     for worker in discovery.poll_new_workers() {
26//!         println!("Found: {} ({})", worker.device_name(), worker.dac_type());
27//!         workers.push(worker);
28//!     }
29//!     thread::sleep(Duration::from_millis(100));
30//! }
31//!
32//! // Create a frame (simple square)
33//! let points = vec![
34//!     LaserPoint::blanked(-0.5, -0.5),
35//!     LaserPoint::new(-0.5, -0.5, 65535, 0, 0, 65535),
36//!     LaserPoint::new(0.5, -0.5, 0, 65535, 0, 65535),
37//!     LaserPoint::new(0.5, 0.5, 0, 0, 65535, 65535),
38//!     LaserPoint::new(-0.5, 0.5, 65535, 65535, 0, 65535),
39//!     LaserPoint::new(-0.5, -0.5, 65535, 0, 0, 65535),
40//! ];
41//! let frame = LaserFrame::new(30000, points);
42//!
43//! // Send frames to all connected DACs
44//! loop {
45//!     for worker in &mut workers {
46//!         worker.update();
47//!         worker.submit_frame(frame.clone());
48//!     }
49//!     thread::sleep(Duration::from_millis(33));
50//! }
51//! ```
52//!
53//! For more control over discovery and connection, use [`DacDiscovery`] directly:
54//!
55//! ```no_run
56//! use laser_dac::{DacDiscovery, DacWorker, EnabledDacTypes, LaserFrame, LaserPoint};
57//!
58//! let mut discovery = DacDiscovery::new(EnabledDacTypes::all());
59//! let devices = discovery.scan();
60//!
61//! for device in devices {
62//!     let name = device.name().to_string();
63//!     let dac_type = device.dac_type();
64//!     let backend = discovery.connect(device).expect("connection failed");
65//!     let mut worker = DacWorker::new(name, dac_type, backend);
66//!
67//!     // Use worker.update() and worker.submit_frame() in your render loop
68//! }
69//! ```
70//!
71//! # Supported DACs
72//!
73//! - **Helios** - USB laser DAC (feature: `helios`)
74//! - **Ether Dream** - Network laser DAC (feature: `ether-dream`)
75//! - **IDN** - ILDA Digital Network protocol (feature: `idn`)
76//! - **LaserCube WiFi** - WiFi-connected laser DAC (feature: `lasercube-wifi`)
77//! - **LaserCube USB** - USB laser DAC / LaserDock (feature: `lasercube-usb`)
78//!
79//! # Features
80//!
81//! - `all-dacs` (default): Enable all DAC protocols
82//! - `usb-dacs`: Enable USB DACs (Helios, LaserCube USB)
83//! - `network-dacs`: Enable network DACs (Ether Dream, IDN, LaserCube WiFi)
84//!
85//! # Coordinate System
86//!
87//! All backends use normalized coordinates:
88//! - X: -1.0 (left) to 1.0 (right)
89//! - Y: -1.0 (bottom) to 1.0 (top)
90//! - Colors: 0-65535 for R, G, B, and intensity
91//!
92//! Each backend handles conversion to its native format internally.
93
94pub mod backend;
95pub mod discovery;
96pub mod discovery_worker;
97mod error;
98pub mod protocols;
99pub mod types;
100pub mod worker;
101
102// Error types
103pub use error::{Error, Result};
104
105// Backend trait and common types
106pub use backend::{DacBackend, WriteResult};
107
108// Discovery and worker types
109pub use discovery::{CustomDiscoverySource, DacDiscovery, DiscoveredDevice, DiscoveredDeviceInfo};
110pub use discovery_worker::{DacDiscoveryWorker, DacDiscoveryWorkerBuilder};
111pub use worker::{
112    CallbackContext, CallbackError, DacCallbackWorker, DacWorker, WorkerCommand, WorkerStatus,
113};
114
115// Types
116pub use types::{
117    DacConnectionState, DacDevice, DacType, DiscoveredDac, EnabledDacTypes, LaserFrame, LaserPoint,
118};
119
120// Conditional exports based on features
121
122// Helios
123#[cfg(feature = "helios")]
124pub use backend::HeliosBackend;
125#[cfg(feature = "helios")]
126pub use protocols::helios;
127
128// Ether Dream
129#[cfg(feature = "ether-dream")]
130pub use backend::EtherDreamBackend;
131#[cfg(feature = "ether-dream")]
132pub use protocols::ether_dream;
133
134// IDN
135#[cfg(feature = "idn")]
136pub use backend::IdnBackend;
137#[cfg(feature = "idn")]
138pub use protocols::idn;
139
140// LaserCube WiFi
141#[cfg(feature = "lasercube-wifi")]
142pub use backend::LasercubeWifiBackend;
143#[cfg(feature = "lasercube-wifi")]
144pub use protocols::lasercube_wifi;
145
146// LaserCube USB
147#[cfg(feature = "lasercube-usb")]
148pub use backend::LasercubeUsbBackend;
149#[cfg(feature = "lasercube-usb")]
150pub use protocols::lasercube_usb;
151
152// Re-export rusb for consumers that need the Context type (for LaserCube USB)
153#[cfg(feature = "lasercube-usb")]
154pub use protocols::lasercube_usb::rusb;