1use byteorder::{ReadBytesExt, WriteBytesExt, LE};
4use std::io;
5
6use crate::point::LaserPoint;
7
8pub use self::command::Command;
9
10pub const COMMUNICATION_PORT: u16 = 7765;
12
13pub const BROADCAST_PORT: u16 = 7654;
15
16pub trait WriteBytes {
18 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
19}
20
21pub trait ReadBytes {
23 fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
24}
25
26pub trait WriteToBytes {
28 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
29}
30
31pub trait ReadFromBytes: Sized {
33 fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self>;
34}
35
36pub trait SizeBytes {
38 const SIZE_BYTES: usize;
39}
40
41#[repr(C)]
43#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
44pub struct DacStatus {
45 pub protocol: u8,
46 pub light_engine_state: u8,
47 pub playback_state: u8,
48 pub source: u8,
49 pub light_engine_flags: u16,
50 pub playback_flags: u16,
51 pub source_flags: u16,
52 pub buffer_fullness: u16,
53 pub point_rate: u32,
54 pub point_count: u32,
55}
56
57#[repr(C)]
59#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
60pub struct DacBroadcast {
61 pub mac_address: [u8; 6],
62 pub hw_revision: u16,
63 pub sw_revision: u16,
64 pub buffer_capacity: u16,
65 pub max_point_rate: u32,
66 pub dac_status: DacStatus,
67}
68
69#[repr(C)]
71#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
72pub struct DacPoint {
73 pub control: u16,
74 pub x: i16,
75 pub y: i16,
76 pub r: u16,
77 pub g: u16,
78 pub b: u16,
79 pub i: u16,
80 pub u1: u16,
81 pub u2: u16,
82}
83
84#[repr(C)]
86#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
87pub struct DacResponse {
88 pub response: u8,
89 pub command: u8,
90 pub dac_status: DacStatus,
91}
92
93impl DacStatus {
94 pub const LIGHT_ENGINE_READY: u8 = 0;
95 pub const LIGHT_ENGINE_WARMUP: u8 = 1;
96 pub const LIGHT_ENGINE_COOLDOWN: u8 = 2;
97 pub const LIGHT_ENGINE_EMERGENCY_STOP: u8 = 3;
98
99 pub const PLAYBACK_IDLE: u8 = 0;
100 pub const PLAYBACK_PREPARED: u8 = 1;
101 pub const PLAYBACK_PLAYING: u8 = 2;
102
103 pub const SOURCE_NETWORK_STREAMING: u8 = 0;
104 pub const SOURCE_ILDA_PLAYBACK_SD: u8 = 1;
105 pub const SOURCE_INTERNAL_ABSTRACT_GENERATOR: u8 = 2;
106}
107
108impl DacResponse {
109 pub const ACK: u8 = 0x61;
110 pub const NAK_FULL: u8 = 0x46;
111 pub const NAK_INVALID: u8 = 0x49;
112 pub const NAK_STOP_CONDITION: u8 = 0x21;
113}
114
115impl WriteToBytes for DacStatus {
116 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
117 writer.write_u8(self.protocol)?;
118 writer.write_u8(self.light_engine_state)?;
119 writer.write_u8(self.playback_state)?;
120 writer.write_u8(self.source)?;
121 writer.write_u16::<LE>(self.light_engine_flags)?;
122 writer.write_u16::<LE>(self.playback_flags)?;
123 writer.write_u16::<LE>(self.source_flags)?;
124 writer.write_u16::<LE>(self.buffer_fullness)?;
125 writer.write_u32::<LE>(self.point_rate)?;
126 writer.write_u32::<LE>(self.point_count)?;
127 Ok(())
128 }
129}
130
131impl WriteToBytes for DacBroadcast {
132 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
133 for &byte in &self.mac_address {
134 writer.write_u8(byte)?;
135 }
136 writer.write_u16::<LE>(self.hw_revision)?;
137 writer.write_u16::<LE>(self.sw_revision)?;
138 writer.write_u16::<LE>(self.buffer_capacity)?;
139 writer.write_u32::<LE>(self.max_point_rate)?;
140 writer.write_bytes(self.dac_status)?;
141 Ok(())
142 }
143}
144
145impl WriteToBytes for DacPoint {
146 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
147 writer.write_u16::<LE>(self.control)?;
148 writer.write_i16::<LE>(self.x)?;
149 writer.write_i16::<LE>(self.y)?;
150 writer.write_u16::<LE>(self.r)?;
151 writer.write_u16::<LE>(self.g)?;
152 writer.write_u16::<LE>(self.b)?;
153 writer.write_u16::<LE>(self.i)?;
154 writer.write_u16::<LE>(self.u1)?;
155 writer.write_u16::<LE>(self.u2)?;
156 Ok(())
157 }
158}
159
160impl WriteToBytes for DacResponse {
161 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
162 writer.write_u8(self.response)?;
163 writer.write_u8(self.command)?;
164 writer.write_bytes(self.dac_status)?;
165 Ok(())
166 }
167}
168
169impl ReadFromBytes for DacStatus {
170 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
171 Ok(DacStatus {
172 protocol: reader.read_u8()?,
173 light_engine_state: reader.read_u8()?,
174 playback_state: reader.read_u8()?,
175 source: reader.read_u8()?,
176 light_engine_flags: reader.read_u16::<LE>()?,
177 playback_flags: reader.read_u16::<LE>()?,
178 source_flags: reader.read_u16::<LE>()?,
179 buffer_fullness: reader.read_u16::<LE>()?,
180 point_rate: reader.read_u32::<LE>()?,
181 point_count: reader.read_u32::<LE>()?,
182 })
183 }
184}
185
186impl ReadFromBytes for DacBroadcast {
187 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
188 let mac_address = [
189 reader.read_u8()?,
190 reader.read_u8()?,
191 reader.read_u8()?,
192 reader.read_u8()?,
193 reader.read_u8()?,
194 reader.read_u8()?,
195 ];
196 Ok(DacBroadcast {
197 mac_address,
198 hw_revision: reader.read_u16::<LE>()?,
199 sw_revision: reader.read_u16::<LE>()?,
200 buffer_capacity: reader.read_u16::<LE>()?,
201 max_point_rate: reader.read_u32::<LE>()?,
202 dac_status: reader.read_bytes::<DacStatus>()?,
203 })
204 }
205}
206
207impl ReadFromBytes for DacPoint {
208 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
209 Ok(DacPoint {
210 control: reader.read_u16::<LE>()?,
211 x: reader.read_i16::<LE>()?,
212 y: reader.read_i16::<LE>()?,
213 r: reader.read_u16::<LE>()?,
214 g: reader.read_u16::<LE>()?,
215 b: reader.read_u16::<LE>()?,
216 i: reader.read_u16::<LE>()?,
217 u1: reader.read_u16::<LE>()?,
218 u2: reader.read_u16::<LE>()?,
219 })
220 }
221}
222
223impl ReadFromBytes for DacResponse {
224 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
225 Ok(DacResponse {
226 response: reader.read_u8()?,
227 command: reader.read_u8()?,
228 dac_status: reader.read_bytes::<DacStatus>()?,
229 })
230 }
231}
232
233impl SizeBytes for DacStatus {
234 const SIZE_BYTES: usize = 20;
235}
236
237impl SizeBytes for DacBroadcast {
238 const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 16;
239}
240
241impl SizeBytes for DacPoint {
242 const SIZE_BYTES: usize = 18;
243}
244
245impl From<&LaserPoint> for DacPoint {
246 fn from(p: &LaserPoint) -> Self {
252 DacPoint {
253 control: 0,
254 x: LaserPoint::coord_to_i16_inverted(p.x),
255 y: LaserPoint::coord_to_i16_inverted(p.y),
256 r: p.r,
257 g: p.g,
258 b: p.b,
259 i: p.intensity,
260 u1: 0,
261 u2: 0,
262 }
263 }
264}
265
266impl SizeBytes for DacResponse {
267 const SIZE_BYTES: usize = DacStatus::SIZE_BYTES + 2;
268}
269
270impl<P> WriteToBytes for &P
271where
272 P: WriteToBytes,
273{
274 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
275 (*self).write_to_bytes(writer)
276 }
277}
278
279impl<W> WriteBytes for W
280where
281 W: WriteBytesExt,
282{
283 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
284 protocol.write_to_bytes(self)
285 }
286}
287
288impl<R> ReadBytes for R
289where
290 R: ReadBytesExt,
291{
292 fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
293 P::read_from_bytes(self)
294 }
295}
296
297pub mod command {
299 use super::{DacPoint, ReadBytes, ReadFromBytes, SizeBytes, WriteBytes, WriteToBytes};
300 use byteorder::{ReadBytesExt, WriteBytesExt, LE};
301 use std::borrow::Cow;
302 use std::io;
303
304 pub trait Command {
306 const START_BYTE: u8;
307 fn start_byte(&self) -> u8 {
308 Self::START_BYTE
309 }
310 }
311
312 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
313 pub struct PrepareStream;
314
315 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
316 pub struct Begin {
317 pub low_water_mark: u16,
318 pub point_rate: u32,
319 }
320
321 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
322 pub struct Update {
323 pub low_water_mark: u16,
324 pub point_rate: u32,
325 }
326
327 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
328 pub struct PointRate(pub u32);
329
330 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
331 pub struct Data<'a> {
332 pub points: Cow<'a, [DacPoint]>,
333 }
334
335 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
336 pub struct Stop;
337
338 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
339 pub struct EmergencyStop;
340
341 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
342 pub struct EmergencyStopAlt;
343
344 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
345 pub struct ClearEmergencyStop;
346
347 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
348 pub struct Ping;
349
350 impl Begin {
351 pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
352 Ok(Begin {
353 low_water_mark: reader.read_u16::<LE>()?,
354 point_rate: reader.read_u32::<LE>()?,
355 })
356 }
357 }
358
359 impl Update {
360 pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
361 Ok(Update {
362 low_water_mark: reader.read_u16::<LE>()?,
363 point_rate: reader.read_u32::<LE>()?,
364 })
365 }
366 }
367
368 impl PointRate {
369 pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
370 Ok(PointRate(reader.read_u32::<LE>()?))
371 }
372 }
373
374 impl<'a> Data<'a> {
375 pub fn read_n_points<R: ReadBytesExt>(mut reader: R) -> io::Result<u16> {
376 reader.read_u16::<LE>()
377 }
378
379 pub fn read_points<R: ReadBytesExt>(
380 mut reader: R,
381 n_points: u16,
382 points: &mut Vec<DacPoint>,
383 ) -> io::Result<()> {
384 for _ in 0..n_points {
385 points.push(reader.read_bytes::<DacPoint>()?);
386 }
387 Ok(())
388 }
389 }
390
391 impl Data<'static> {
392 pub fn read_fields<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
393 let n_points = Self::read_n_points(&mut reader)?;
394 let mut data = Vec::with_capacity(n_points as _);
395 Self::read_points(reader, n_points, &mut data)?;
396 Ok(Data {
397 points: Cow::Owned(data),
398 })
399 }
400 }
401
402 impl<C> Command for &C
403 where
404 C: Command,
405 {
406 const START_BYTE: u8 = C::START_BYTE;
407 }
408
409 impl Command for PrepareStream {
410 const START_BYTE: u8 = 0x70;
411 }
412 impl Command for Begin {
413 const START_BYTE: u8 = 0x62;
414 }
415 impl Command for Update {
416 const START_BYTE: u8 = 0x75;
417 }
418 impl Command for PointRate {
419 const START_BYTE: u8 = 0x74;
420 }
421 impl<'a> Command for Data<'a> {
422 const START_BYTE: u8 = 0x64;
423 }
424 impl Command for Stop {
425 const START_BYTE: u8 = 0x73;
426 }
427 impl Command for EmergencyStop {
428 const START_BYTE: u8 = 0x00;
429 }
430 impl Command for EmergencyStopAlt {
431 const START_BYTE: u8 = 0xff;
432 }
433 impl Command for ClearEmergencyStop {
434 const START_BYTE: u8 = 0x63;
435 }
436 impl Command for Ping {
437 const START_BYTE: u8 = 0x3f;
438 }
439
440 impl SizeBytes for PrepareStream {
441 const SIZE_BYTES: usize = 1;
442 }
443 impl SizeBytes for Begin {
444 const SIZE_BYTES: usize = 7;
445 }
446 impl SizeBytes for Update {
447 const SIZE_BYTES: usize = 7;
448 }
449 impl SizeBytes for PointRate {
450 const SIZE_BYTES: usize = 5;
451 }
452 impl SizeBytes for Stop {
453 const SIZE_BYTES: usize = 1;
454 }
455 impl SizeBytes for EmergencyStop {
456 const SIZE_BYTES: usize = 1;
457 }
458 impl SizeBytes for ClearEmergencyStop {
459 const SIZE_BYTES: usize = 1;
460 }
461 impl SizeBytes for Ping {
462 const SIZE_BYTES: usize = 1;
463 }
464
465 macro_rules! impl_unit_command_bytes {
466 ($($cmd:ident),* $(,)?) => {
467 $(
468 impl WriteToBytes for $cmd {
469 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
470 writer.write_u8(Self::START_BYTE)
471 }
472 }
473
474 impl ReadFromBytes for $cmd {
475 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
476 if reader.read_u8()? != Self::START_BYTE {
477 return Err(io::Error::new(
478 io::ErrorKind::InvalidData,
479 "invalid command",
480 ));
481 }
482 Ok($cmd)
483 }
484 }
485 )*
486 };
487 }
488
489 impl_unit_command_bytes!(PrepareStream, Stop, ClearEmergencyStop, Ping);
490
491 impl WriteToBytes for Begin {
492 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
493 writer.write_u8(Self::START_BYTE)?;
494 writer.write_u16::<LE>(self.low_water_mark)?;
495 writer.write_u32::<LE>(self.point_rate)?;
496 Ok(())
497 }
498 }
499
500 impl WriteToBytes for Update {
501 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
502 writer.write_u8(Self::START_BYTE)?;
503 writer.write_u16::<LE>(self.low_water_mark)?;
504 writer.write_u32::<LE>(self.point_rate)?;
505 Ok(())
506 }
507 }
508
509 impl WriteToBytes for PointRate {
510 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
511 writer.write_u8(Self::START_BYTE)?;
512 writer.write_u32::<LE>(self.0)?;
513 Ok(())
514 }
515 }
516
517 impl<'a> WriteToBytes for Data<'a> {
518 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
519 if self.points.len() > u16::MAX as usize {
520 return Err(io::Error::new(
521 io::ErrorKind::InvalidData,
522 "too many points",
523 ));
524 }
525 writer.write_u8(Self::START_BYTE)?;
526 writer.write_u16::<LE>(self.points.len() as u16)?;
527 for point in self.points.iter() {
528 writer.write_bytes(point)?;
529 }
530 Ok(())
531 }
532 }
533
534 impl WriteToBytes for EmergencyStop {
535 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
536 writer.write_u8(Self::START_BYTE)
537 }
538 }
539
540 impl WriteToBytes for EmergencyStopAlt {
541 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
542 writer.write_u8(Self::START_BYTE)
543 }
544 }
545
546 impl ReadFromBytes for Begin {
547 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
548 if reader.read_u8()? != Self::START_BYTE {
549 return Err(io::Error::new(
550 io::ErrorKind::InvalidData,
551 "invalid command",
552 ));
553 }
554 Self::read_fields(reader)
555 }
556 }
557
558 impl ReadFromBytes for Update {
559 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
560 if reader.read_u8()? != Self::START_BYTE {
561 return Err(io::Error::new(
562 io::ErrorKind::InvalidData,
563 "invalid command",
564 ));
565 }
566 Self::read_fields(reader)
567 }
568 }
569
570 impl ReadFromBytes for PointRate {
571 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
572 if reader.read_u8()? != Self::START_BYTE {
573 return Err(io::Error::new(
574 io::ErrorKind::InvalidData,
575 "invalid command",
576 ));
577 }
578 Self::read_fields(reader)
579 }
580 }
581
582 impl ReadFromBytes for Data<'static> {
583 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
584 if reader.read_u8()? != Self::START_BYTE {
585 return Err(io::Error::new(
586 io::ErrorKind::InvalidData,
587 "invalid command",
588 ));
589 }
590 Self::read_fields(reader)
591 }
592 }
593
594 impl ReadFromBytes for EmergencyStop {
595 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
596 let command = reader.read_u8()?;
597 if command != Self::START_BYTE && command != EmergencyStopAlt::START_BYTE {
598 return Err(io::Error::new(
599 io::ErrorKind::InvalidData,
600 "invalid command",
601 ));
602 }
603 Ok(EmergencyStop)
604 }
605 }
606}
607
608#[cfg(test)]
609mod tests {
610 use super::*;
611 use crate::point::LaserPoint;
612
613 #[test]
622 fn test_ether_dream_conversion_center() {
623 let laser_point = LaserPoint::new(0.0, 0.0, 128 * 257, 64 * 257, 32 * 257, 200 * 257);
625 let dac_point: DacPoint = (&laser_point).into();
626
627 assert_eq!(dac_point.x, 0);
628 assert_eq!(dac_point.y, 0);
629 assert_eq!(dac_point.r, 128 * 257);
631 assert_eq!(dac_point.g, 64 * 257);
632 assert_eq!(dac_point.b, 32 * 257);
633 assert_eq!(dac_point.i, 200 * 257);
634 }
635
636 #[test]
637 fn test_ether_dream_conversion_boundaries() {
638 let min = LaserPoint::new(-1.0, -1.0, 0, 0, 0, 0);
640 let min_dac: DacPoint = (&min).into();
641 assert_eq!(min_dac.x, 32767);
642 assert_eq!(min_dac.y, 32767);
643
644 let max = LaserPoint::new(1.0, 1.0, 65535, 65535, 65535, 65535);
646 let max_dac: DacPoint = (&max).into();
647 assert_eq!(max_dac.x, -32767);
648 assert_eq!(max_dac.y, -32767);
649 }
650
651 #[test]
652 fn test_ether_dream_conversion_clamps_out_of_range() {
653 let laser_point = LaserPoint::new(2.0, -3.0, 65535, 65535, 65535, 65535);
655 let dac_point: DacPoint = (&laser_point).into();
656
657 assert_eq!(dac_point.x, -32767);
658 assert_eq!(dac_point.y, 32767);
659 }
660
661 #[test]
662 fn test_ether_dream_color_direct_passthrough() {
663 let laser_point = LaserPoint::new(0.0, 0.0, 0, 32639, 65535, 257);
665 let dac_point: DacPoint = (&laser_point).into();
666
667 assert_eq!(dac_point.r, 0);
668 assert_eq!(dac_point.g, 32639);
669 assert_eq!(dac_point.b, 65535);
670 assert_eq!(dac_point.i, 257);
671 }
672
673 #[test]
674 fn test_ether_dream_coordinate_symmetry() {
675 let p1 = LaserPoint::new(0.5, 0.0, 0, 0, 0, 0);
677 let p2 = LaserPoint::new(-0.5, 0.0, 0, 0, 0, 0);
678 let d1: DacPoint = (&p1).into();
679 let d2: DacPoint = (&p2).into();
680
681 assert_eq!(d1.x, -d2.x);
682 }
683
684 #[test]
685 fn test_ether_dream_conversion_infinity_clamps() {
686 let laser_point = LaserPoint::new(f32::INFINITY, f32::NEG_INFINITY, 0, 0, 0, 0);
687 let dac_point: DacPoint = (&laser_point).into();
688
689 assert_eq!(dac_point.x, -32767);
690 assert_eq!(dac_point.y, 32767);
691 }
692}