1use byteorder::{ReadBytesExt, WriteBytesExt, BE};
7use std::io;
8
9use crate::point::LaserPoint;
10
11pub const IDN_PORT: u16 = 7255;
17
18pub const MAX_UDP_PAYLOAD: usize = 1454;
20
21pub const IDNCMD_VOID: u8 = 0x00;
23pub const IDNCMD_PING_REQUEST: u8 = 0x08;
24pub const IDNCMD_PING_RESPONSE: u8 = 0x09;
25pub const IDNCMD_GROUP_REQUEST: u8 = 0x0C;
26pub const IDNCMD_GROUP_RESPONSE: u8 = 0x0D;
27pub const IDNCMD_SCAN_REQUEST: u8 = 0x10;
28pub const IDNCMD_SCAN_RESPONSE: u8 = 0x11;
29pub const IDNCMD_SERVICEMAP_REQUEST: u8 = 0x12;
30pub const IDNCMD_SERVICEMAP_RESPONSE: u8 = 0x13;
31
32pub const IDNCMD_SERVICE_PARAMS_REQUEST: u8 = 0x20;
34pub const IDNCMD_SERVICE_PARAMS_RESPONSE: u8 = 0x21;
35pub const IDNCMD_UNIT_PARAMS_REQUEST: u8 = 0x22;
36pub const IDNCMD_UNIT_PARAMS_RESPONSE: u8 = 0x23;
37pub const IDNCMD_LINK_PARAMS_REQUEST: u8 = 0x28;
38pub const IDNCMD_LINK_PARAMS_RESPONSE: u8 = 0x29;
39
40pub const IDNCMD_RT_CNLMSG: u8 = 0x40;
42pub const IDNCMD_RT_CNLMSG_ACKREQ: u8 = 0x41;
43pub const IDNCMD_RT_CNLMSG_CLOSE: u8 = 0x44;
44pub const IDNCMD_RT_CNLMSG_CLOSE_ACKREQ: u8 = 0x45;
45pub const IDNCMD_RT_ABORT: u8 = 0x46;
46pub const IDNCMD_RT_ACKNOWLEDGE: u8 = 0x47;
47
48pub const IDNMSK_PKTFLAGS_GROUP: u8 = 0x0F;
50
51pub const IDNFLG_SCAN_STATUS_MALFUNCTION: u8 = 0x80;
53pub const IDNFLG_SCAN_STATUS_OFFLINE: u8 = 0x40;
54pub const IDNFLG_SCAN_STATUS_EXCLUDED: u8 = 0x20;
55pub const IDNFLG_SCAN_STATUS_OCCUPIED: u8 = 0x10;
56pub const IDNFLG_SCAN_STATUS_REALTIME: u8 = 0x01;
57
58pub const IDNVAL_GROUPOP_SUCCESS: i8 = 0x00;
60pub const IDNVAL_GROUPOP_GETMASK: i8 = 0x01;
61pub const IDNVAL_GROUPOP_SETMASK: i8 = 0x02;
62pub const IDNVAL_GROUPOP_ERR_AUTH: i8 = -3; pub const IDNVAL_GROUPOP_ERR_OPERATION: i8 = -2; pub const IDNVAL_GROUPOP_ERR_REQUEST: i8 = -1; pub const IDNVAL_STYPE_RELAY: u8 = 0x00;
68pub const IDNVAL_STYPE_UART: u8 = 0x04;
69pub const IDNVAL_STYPE_DMX512: u8 = 0x05;
70pub const IDNVAL_STYPE_LAPRO: u8 = 0x80; pub const IDNVAL_RTACK_ERR_EMPTY_CLOSE: u8 = 0xEB;
75pub const IDNVAL_RTACK_ERR_OCCUPIED: u8 = 0xEC;
77pub const IDNVAL_RTACK_ERR_EXCLUDED: u8 = 0xED;
79pub const IDNVAL_RTACK_ERR_INVALID_PAYLOAD: u8 = 0xEE;
81pub const IDNVAL_RTACK_ERR_PROCESSING_ERROR: u8 = 0xEF;
83
84pub const IDNFLG_SERVICEMAP_DSID: u8 = 0x01;
87
88pub const IDNFLG_CONTENTID_CHANNELMSG: u16 = 0x8000;
90pub const IDNFLG_CONTENTID_CONFIG_LSTFRG: u16 = 0x4000;
91pub const IDNMSK_CONTENTID_CHANNELID: u16 = 0x3F00;
92pub const IDNMSK_CONTENTID_CNKTYPE: u16 = 0x00FF;
93
94pub const IDNVAL_CNKTYPE_VOID: u8 = 0x00;
96pub const IDNVAL_CNKTYPE_LPGRF_WAVE: u8 = 0x01;
97pub const IDNVAL_CNKTYPE_LPGRF_FRAME: u8 = 0x02;
98pub const IDNVAL_CNKTYPE_LPGRF_FRAME_FIRST: u8 = 0x03;
99pub const IDNVAL_CNKTYPE_LPGRF_FRAME_SEQUEL: u8 = 0xC0;
100
101pub const IDNFLG_CHNCFG_ROUTING: u8 = 0x01;
103pub const IDNFLG_CHNCFG_CLOSE: u8 = 0x02;
104
105pub const IDNVAL_SMOD_VOID: u8 = 0x00;
107pub const IDNVAL_SMOD_LPGRF_CONTINUOUS: u8 = 0x01;
108pub const IDNVAL_SMOD_LPGRF_DISCRETE: u8 = 0x02;
109
110pub const XYRGBI_SAMPLE_SIZE: usize = 8;
112pub const XYRGB_HIGHRES_SAMPLE_SIZE: usize = 10;
113pub const EXTENDED_SAMPLE_SIZE: usize = 20;
114
115fn null_terminated_str(bytes: &[u8]) -> &str {
121 let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
122 std::str::from_utf8(&bytes[..end]).unwrap_or("")
123}
124
125pub trait WriteBytes {
127 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
128}
129
130pub trait ReadBytes {
132 fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
133}
134
135pub trait WriteToBytes {
137 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
138}
139
140pub trait ReadFromBytes: Sized {
142 fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self>;
143}
144
145pub trait SizeBytes {
147 const SIZE_BYTES: usize;
148}
149
150#[repr(C)]
156#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
157pub struct PacketHeader {
158 pub command: u8,
160 pub flags: u8,
162 pub sequence: u16,
164}
165
166impl WriteToBytes for PacketHeader {
167 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
168 writer.write_u8(self.command)?;
169 writer.write_u8(self.flags)?;
170 writer.write_u16::<BE>(self.sequence)?;
171 Ok(())
172 }
173}
174
175impl ReadFromBytes for PacketHeader {
176 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
177 let command = reader.read_u8()?;
178 let flags = reader.read_u8()?;
179 let sequence = reader.read_u16::<BE>()?;
180 Ok(PacketHeader {
181 command,
182 flags,
183 sequence,
184 })
185 }
186}
187
188impl SizeBytes for PacketHeader {
189 const SIZE_BYTES: usize = 4;
190}
191
192#[repr(C)]
198#[derive(Clone, Debug, PartialEq, Eq, Hash)]
199pub struct ScanResponse {
200 pub struct_size: u8,
202 pub protocol_version: u8,
204 pub status: u8,
206 pub reserved: u8,
208 pub unit_id: [u8; 16],
210 pub hostname: [u8; 20],
212}
213
214impl WriteToBytes for ScanResponse {
215 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
216 writer.write_u8(self.struct_size)?;
217 writer.write_u8(self.protocol_version)?;
218 writer.write_u8(self.status)?;
219 writer.write_u8(self.reserved)?;
220 for &byte in &self.unit_id {
221 writer.write_u8(byte)?;
222 }
223 for &byte in &self.hostname {
224 writer.write_u8(byte)?;
225 }
226 Ok(())
227 }
228}
229
230impl ReadFromBytes for ScanResponse {
231 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
232 let struct_size = reader.read_u8()?;
233 let protocol_version = reader.read_u8()?;
234 let status = reader.read_u8()?;
235 let reserved = reader.read_u8()?;
236 let mut unit_id = [0u8; 16];
237 for byte in &mut unit_id {
238 *byte = reader.read_u8()?;
239 }
240 let mut hostname = [0u8; 20];
241 for byte in &mut hostname {
242 *byte = reader.read_u8()?;
243 }
244 Ok(ScanResponse {
245 struct_size,
246 protocol_version,
247 status,
248 reserved,
249 unit_id,
250 hostname,
251 })
252 }
253}
254
255impl SizeBytes for ScanResponse {
256 const SIZE_BYTES: usize = 40;
257}
258
259impl ScanResponse {
260 pub fn hostname_str(&self) -> &str {
262 null_terminated_str(&self.hostname)
263 }
264}
265
266#[repr(C)]
272#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
273pub struct ServiceMapResponseHeader {
274 pub struct_size: u8,
276 pub entry_size: u8,
278 pub relay_entry_count: u8,
280 pub service_entry_count: u8,
282}
283
284impl WriteToBytes for ServiceMapResponseHeader {
285 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
286 writer.write_u8(self.struct_size)?;
287 writer.write_u8(self.entry_size)?;
288 writer.write_u8(self.relay_entry_count)?;
289 writer.write_u8(self.service_entry_count)?;
290 Ok(())
291 }
292}
293
294impl ReadFromBytes for ServiceMapResponseHeader {
295 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
296 let struct_size = reader.read_u8()?;
297 let entry_size = reader.read_u8()?;
298 let relay_entry_count = reader.read_u8()?;
299 let service_entry_count = reader.read_u8()?;
300 Ok(ServiceMapResponseHeader {
301 struct_size,
302 entry_size,
303 relay_entry_count,
304 service_entry_count,
305 })
306 }
307}
308
309impl SizeBytes for ServiceMapResponseHeader {
310 const SIZE_BYTES: usize = 4;
311}
312
313#[repr(C)]
315#[derive(Clone, Debug, PartialEq, Eq, Hash)]
316pub struct ServiceMapEntry {
317 pub service_id: u8,
319 pub service_type: u8,
321 pub flags: u8,
323 pub relay_number: u8,
325 pub name: [u8; 20],
327}
328
329impl WriteToBytes for ServiceMapEntry {
330 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
331 writer.write_u8(self.service_id)?;
332 writer.write_u8(self.service_type)?;
333 writer.write_u8(self.flags)?;
334 writer.write_u8(self.relay_number)?;
335 for &byte in &self.name {
336 writer.write_u8(byte)?;
337 }
338 Ok(())
339 }
340}
341
342impl ReadFromBytes for ServiceMapEntry {
343 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
344 let service_id = reader.read_u8()?;
345 let service_type = reader.read_u8()?;
346 let flags = reader.read_u8()?;
347 let relay_number = reader.read_u8()?;
348 let mut name = [0u8; 20];
349 for byte in &mut name {
350 *byte = reader.read_u8()?;
351 }
352 Ok(ServiceMapEntry {
353 service_id,
354 service_type,
355 flags,
356 relay_number,
357 name,
358 })
359 }
360}
361
362impl SizeBytes for ServiceMapEntry {
363 const SIZE_BYTES: usize = 24;
364}
365
366impl ServiceMapEntry {
367 pub fn name_str(&self) -> &str {
369 null_terminated_str(&self.name)
370 }
371
372 pub fn is_relay(&self) -> bool {
374 self.service_id == 0
375 }
376}
377
378#[repr(C)]
384#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
385pub struct ChannelMessageHeader {
386 pub total_size: u16,
388 pub content_id: u16,
390 pub timestamp: u32,
392}
393
394impl WriteToBytes for ChannelMessageHeader {
395 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
396 writer.write_u16::<BE>(self.total_size)?;
397 writer.write_u16::<BE>(self.content_id)?;
398 writer.write_u32::<BE>(self.timestamp)?;
399 Ok(())
400 }
401}
402
403impl ReadFromBytes for ChannelMessageHeader {
404 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
405 let total_size = reader.read_u16::<BE>()?;
406 let content_id = reader.read_u16::<BE>()?;
407 let timestamp = reader.read_u32::<BE>()?;
408 Ok(ChannelMessageHeader {
409 total_size,
410 content_id,
411 timestamp,
412 })
413 }
414}
415
416impl SizeBytes for ChannelMessageHeader {
417 const SIZE_BYTES: usize = 8;
418}
419
420#[repr(C)]
426#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
427pub struct ChannelConfigHeader {
428 pub word_count: u8,
430 pub flags: u8,
432 pub service_id: u8,
434 pub service_mode: u8,
436}
437
438impl WriteToBytes for ChannelConfigHeader {
439 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
440 writer.write_u8(self.word_count)?;
441 writer.write_u8(self.flags)?;
442 writer.write_u8(self.service_id)?;
443 writer.write_u8(self.service_mode)?;
444 Ok(())
445 }
446}
447
448impl ReadFromBytes for ChannelConfigHeader {
449 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
450 let word_count = reader.read_u8()?;
451 let flags = reader.read_u8()?;
452 let service_id = reader.read_u8()?;
453 let service_mode = reader.read_u8()?;
454 Ok(ChannelConfigHeader {
455 word_count,
456 flags,
457 service_id,
458 service_mode,
459 })
460 }
461}
462
463impl SizeBytes for ChannelConfigHeader {
464 const SIZE_BYTES: usize = 4;
465}
466
467#[repr(C)]
473#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
474pub struct SampleChunkHeader {
475 pub flags_duration: u32,
477}
478
479impl SampleChunkHeader {
480 pub fn new(flags: u8, duration_us: u32) -> Self {
482 let flags_duration = ((flags as u32) << 24) | (duration_us & 0x00FF_FFFF);
483 Self { flags_duration }
484 }
485
486 pub fn flags(&self) -> u8 {
488 (self.flags_duration >> 24) as u8
489 }
490
491 pub fn duration_us(&self) -> u32 {
493 self.flags_duration & 0x00FF_FFFF
494 }
495}
496
497impl WriteToBytes for SampleChunkHeader {
498 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
499 writer.write_u32::<BE>(self.flags_duration)?;
500 Ok(())
501 }
502}
503
504impl ReadFromBytes for SampleChunkHeader {
505 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
506 let flags_duration = reader.read_u32::<BE>()?;
507 Ok(SampleChunkHeader { flags_duration })
508 }
509}
510
511impl SizeBytes for SampleChunkHeader {
512 const SIZE_BYTES: usize = 4;
513}
514
515#[repr(C)]
521#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
522pub struct AcknowledgeResponse {
523 pub struct_size: u8,
525 pub result_code: i8,
527 pub input_event_flags: u16,
529 pub pipeline_event_flags: u16,
531 pub status_flags: u8,
533 pub link_quality: u8,
535 pub latency_us: u16,
537}
538
539impl WriteToBytes for AcknowledgeResponse {
540 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
541 writer.write_u8(self.struct_size)?;
542 writer.write_i8(self.result_code)?;
543 writer.write_u16::<BE>(self.input_event_flags)?;
544 writer.write_u16::<BE>(self.pipeline_event_flags)?;
545 writer.write_u8(self.status_flags)?;
546 writer.write_u8(self.link_quality)?;
547 writer.write_u16::<BE>(self.latency_us)?;
548 Ok(())
549 }
550}
551
552impl ReadFromBytes for AcknowledgeResponse {
553 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
554 let struct_size = reader.read_u8()?;
555 let result_code = reader.read_i8()?;
556 let input_event_flags = reader.read_u16::<BE>()?;
557 let pipeline_event_flags = reader.read_u16::<BE>()?;
558 let status_flags = reader.read_u8()?;
559 let link_quality = reader.read_u8()?;
560 let latency_us = reader.read_u16::<BE>()?;
561 Ok(AcknowledgeResponse {
562 struct_size,
563 result_code,
564 input_event_flags,
565 pipeline_event_flags,
566 status_flags,
567 link_quality,
568 latency_us,
569 })
570 }
571}
572
573impl SizeBytes for AcknowledgeResponse {
574 const SIZE_BYTES: usize = 10;
575}
576
577impl AcknowledgeResponse {
578 pub fn is_success(&self) -> bool {
580 self.result_code >= 0
581 }
582
583 pub fn latency(&self) -> std::time::Duration {
585 std::time::Duration::from_micros(self.latency_us as u64)
586 }
587}
588
589#[repr(C)]
596#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
597pub struct GroupRequest {
598 pub struct_size: u8,
600 pub op_code: i8,
602 pub group_mask: u16,
604 pub auth_code: [u8; 12],
606}
607
608impl GroupRequest {
609 pub fn get() -> Self {
611 Self {
612 struct_size: 16,
613 op_code: IDNVAL_GROUPOP_GETMASK,
614 group_mask: 0,
615 auth_code: [0u8; 12],
616 }
617 }
618
619 pub fn set(mask: u16) -> Self {
621 Self {
622 struct_size: 16,
623 op_code: IDNVAL_GROUPOP_SETMASK,
624 group_mask: mask,
625 auth_code: [0u8; 12],
626 }
627 }
628
629 pub fn set_with_auth(mask: u16, auth: &[u8]) -> Self {
631 let mut auth_code = [0u8; 12];
632 let len = auth.len().min(12);
633 auth_code[..len].copy_from_slice(&auth[..len]);
634 Self {
635 struct_size: 16,
636 op_code: IDNVAL_GROUPOP_SETMASK,
637 group_mask: mask,
638 auth_code,
639 }
640 }
641}
642
643impl WriteToBytes for GroupRequest {
644 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
645 writer.write_u8(self.struct_size)?;
646 writer.write_i8(self.op_code)?;
647 writer.write_u16::<BE>(self.group_mask)?;
648 for &byte in &self.auth_code {
649 writer.write_u8(byte)?;
650 }
651 Ok(())
652 }
653}
654
655impl ReadFromBytes for GroupRequest {
656 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
657 let struct_size = reader.read_u8()?;
658 let op_code = reader.read_i8()?;
659 let group_mask = reader.read_u16::<BE>()?;
660 let mut auth_code = [0u8; 12];
661 for byte in &mut auth_code {
662 *byte = reader.read_u8()?;
663 }
664 Ok(GroupRequest {
665 struct_size,
666 op_code,
667 group_mask,
668 auth_code,
669 })
670 }
671}
672
673impl SizeBytes for GroupRequest {
674 const SIZE_BYTES: usize = 16;
675}
676
677#[repr(C)]
679#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
680pub struct GroupResponse {
681 pub struct_size: u8,
683 pub op_code: i8,
685 pub group_mask: u16,
687}
688
689impl WriteToBytes for GroupResponse {
690 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
691 writer.write_u8(self.struct_size)?;
692 writer.write_i8(self.op_code)?;
693 writer.write_u16::<BE>(self.group_mask)?;
694 Ok(())
695 }
696}
697
698impl ReadFromBytes for GroupResponse {
699 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
700 let struct_size = reader.read_u8()?;
701 let op_code = reader.read_i8()?;
702 let group_mask = reader.read_u16::<BE>()?;
703 Ok(GroupResponse {
704 struct_size,
705 op_code,
706 group_mask,
707 })
708 }
709}
710
711impl SizeBytes for GroupResponse {
712 const SIZE_BYTES: usize = 4;
713}
714
715impl GroupResponse {
716 pub fn is_success(&self) -> bool {
718 self.op_code >= 0
719 }
720
721 pub fn is_group_enabled(&self, group: u8) -> bool {
723 if group > 15 {
724 return false;
725 }
726 (self.group_mask & (1 << group)) != 0
727 }
728
729 pub fn enabled_groups(&self) -> Vec<u8> {
731 (0..16).filter(|&g| self.is_group_enabled(g)).collect()
732 }
733}
734
735#[repr(C)]
742#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
743pub struct ParameterGetRequest {
744 pub service_id: u8,
746 pub reserved: u8,
748 pub param_id: u16,
750}
751
752impl WriteToBytes for ParameterGetRequest {
753 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
754 writer.write_u8(self.service_id)?;
755 writer.write_u8(self.reserved)?;
756 writer.write_u16::<BE>(self.param_id)?;
757 Ok(())
758 }
759}
760
761impl ReadFromBytes for ParameterGetRequest {
762 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
763 let service_id = reader.read_u8()?;
764 let reserved = reader.read_u8()?;
765 let param_id = reader.read_u16::<BE>()?;
766 Ok(ParameterGetRequest {
767 service_id,
768 reserved,
769 param_id,
770 })
771 }
772}
773
774impl SizeBytes for ParameterGetRequest {
775 const SIZE_BYTES: usize = 4;
776}
777
778#[repr(C)]
780#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
781pub struct ParameterResponse {
782 pub service_id: u8,
784 pub result_code: i8,
786 pub param_id: u16,
788 pub value: u32,
790}
791
792impl WriteToBytes for ParameterResponse {
793 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
794 writer.write_u8(self.service_id)?;
795 writer.write_i8(self.result_code)?;
796 writer.write_u16::<BE>(self.param_id)?;
797 writer.write_u32::<BE>(self.value)?;
798 Ok(())
799 }
800}
801
802impl ReadFromBytes for ParameterResponse {
803 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
804 let service_id = reader.read_u8()?;
805 let result_code = reader.read_i8()?;
806 let param_id = reader.read_u16::<BE>()?;
807 let value = reader.read_u32::<BE>()?;
808 Ok(ParameterResponse {
809 service_id,
810 result_code,
811 param_id,
812 value,
813 })
814 }
815}
816
817impl SizeBytes for ParameterResponse {
818 const SIZE_BYTES: usize = 8;
819}
820
821impl ParameterResponse {
822 pub fn is_success(&self) -> bool {
824 self.result_code >= 0
825 }
826}
827
828#[repr(C)]
830#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
831pub struct ParameterSetRequest {
832 pub service_id: u8,
834 pub reserved: u8,
836 pub param_id: u16,
838 pub value: u32,
840}
841
842impl WriteToBytes for ParameterSetRequest {
843 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
844 writer.write_u8(self.service_id)?;
845 writer.write_u8(self.reserved)?;
846 writer.write_u16::<BE>(self.param_id)?;
847 writer.write_u32::<BE>(self.value)?;
848 Ok(())
849 }
850}
851
852impl ReadFromBytes for ParameterSetRequest {
853 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
854 let service_id = reader.read_u8()?;
855 let reserved = reader.read_u8()?;
856 let param_id = reader.read_u16::<BE>()?;
857 let value = reader.read_u32::<BE>()?;
858 Ok(ParameterSetRequest {
859 service_id,
860 reserved,
861 param_id,
862 value,
863 })
864 }
865}
866
867impl SizeBytes for ParameterSetRequest {
868 const SIZE_BYTES: usize = 8;
869}
870
871#[repr(C)]
878#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
879pub struct PointXyrgbi {
880 pub x: i16,
882 pub y: i16,
884 pub r: u8,
886 pub g: u8,
888 pub b: u8,
890 pub i: u8,
892}
893
894impl PointXyrgbi {
895 pub fn new(x: i16, y: i16, r: u8, g: u8, b: u8, i: u8) -> Self {
896 Self { x, y, r, g, b, i }
897 }
898}
899
900impl WriteToBytes for PointXyrgbi {
901 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
902 writer.write_i16::<BE>(self.x)?;
903 writer.write_i16::<BE>(self.y)?;
904 writer.write_u8(self.r)?;
905 writer.write_u8(self.g)?;
906 writer.write_u8(self.b)?;
907 writer.write_u8(self.i)?;
908 Ok(())
909 }
910}
911
912impl PointXyrgbi {
913 #[inline]
920 pub fn encode_batch(points: &[Self], buf: &mut Vec<u8>) {
921 buf.reserve(points.len() * XYRGBI_SAMPLE_SIZE);
922 for p in points {
923 let xb = p.x.to_be_bytes();
924 let yb = p.y.to_be_bytes();
925 buf.extend_from_slice(&[xb[0], xb[1], yb[0], yb[1], p.r, p.g, p.b, p.i]);
926 }
927 }
928}
929
930impl ReadFromBytes for PointXyrgbi {
931 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
932 let x = reader.read_i16::<BE>()?;
933 let y = reader.read_i16::<BE>()?;
934 let r = reader.read_u8()?;
935 let g = reader.read_u8()?;
936 let b = reader.read_u8()?;
937 let i = reader.read_u8()?;
938 Ok(PointXyrgbi { x, y, r, g, b, i })
939 }
940}
941
942impl SizeBytes for PointXyrgbi {
943 const SIZE_BYTES: usize = XYRGBI_SAMPLE_SIZE;
944}
945
946impl From<&LaserPoint> for PointXyrgbi {
947 fn from(p: &LaserPoint) -> Self {
953 PointXyrgbi::new(
954 LaserPoint::coord_to_i16_inverted(p.x),
955 LaserPoint::coord_to_i16_inverted(p.y),
956 LaserPoint::color_to_u8(p.r),
957 LaserPoint::color_to_u8(p.g),
958 LaserPoint::color_to_u8(p.b),
959 LaserPoint::color_to_u8(p.intensity),
960 )
961 }
962}
963
964#[repr(C)]
967#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
968pub struct PointXyrgbHighRes {
969 pub x: i16,
971 pub y: i16,
973 pub r: u16,
975 pub g: u16,
977 pub b: u16,
979}
980
981impl PointXyrgbHighRes {
982 pub fn new(x: i16, y: i16, r: u16, g: u16, b: u16) -> Self {
983 Self { x, y, r, g, b }
984 }
985}
986
987impl WriteToBytes for PointXyrgbHighRes {
988 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
989 writer.write_i16::<BE>(self.x)?;
990 writer.write_i16::<BE>(self.y)?;
991 writer.write_u16::<BE>(self.r)?;
992 writer.write_u16::<BE>(self.g)?;
993 writer.write_u16::<BE>(self.b)?;
994 Ok(())
995 }
996}
997
998impl ReadFromBytes for PointXyrgbHighRes {
999 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
1000 let x = reader.read_i16::<BE>()?;
1001 let y = reader.read_i16::<BE>()?;
1002 let r = reader.read_u16::<BE>()?;
1003 let g = reader.read_u16::<BE>()?;
1004 let b = reader.read_u16::<BE>()?;
1005 Ok(PointXyrgbHighRes { x, y, r, g, b })
1006 }
1007}
1008
1009impl SizeBytes for PointXyrgbHighRes {
1010 const SIZE_BYTES: usize = XYRGB_HIGHRES_SAMPLE_SIZE;
1011}
1012
1013#[repr(C)]
1016#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
1017pub struct PointExtended {
1018 pub x: i16,
1020 pub y: i16,
1022 pub r: u16,
1024 pub g: u16,
1026 pub b: u16,
1028 pub i: u16,
1030 pub u1: u16,
1032 pub u2: u16,
1034 pub u3: u16,
1036 pub u4: u16,
1038}
1039
1040impl PointExtended {
1041 #[allow(clippy::too_many_arguments)]
1042 pub fn new(
1043 x: i16,
1044 y: i16,
1045 r: u16,
1046 g: u16,
1047 b: u16,
1048 i: u16,
1049 u1: u16,
1050 u2: u16,
1051 u3: u16,
1052 u4: u16,
1053 ) -> Self {
1054 Self {
1055 x,
1056 y,
1057 r,
1058 g,
1059 b,
1060 i,
1061 u1,
1062 u2,
1063 u3,
1064 u4,
1065 }
1066 }
1067}
1068
1069impl WriteToBytes for PointExtended {
1070 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
1071 writer.write_i16::<BE>(self.x)?;
1072 writer.write_i16::<BE>(self.y)?;
1073 writer.write_u16::<BE>(self.r)?;
1074 writer.write_u16::<BE>(self.g)?;
1075 writer.write_u16::<BE>(self.b)?;
1076 writer.write_u16::<BE>(self.i)?;
1077 writer.write_u16::<BE>(self.u1)?;
1078 writer.write_u16::<BE>(self.u2)?;
1079 writer.write_u16::<BE>(self.u3)?;
1080 writer.write_u16::<BE>(self.u4)?;
1081 Ok(())
1082 }
1083}
1084
1085impl ReadFromBytes for PointExtended {
1086 fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
1087 let x = reader.read_i16::<BE>()?;
1088 let y = reader.read_i16::<BE>()?;
1089 let r = reader.read_u16::<BE>()?;
1090 let g = reader.read_u16::<BE>()?;
1091 let b = reader.read_u16::<BE>()?;
1092 let i = reader.read_u16::<BE>()?;
1093 let u1 = reader.read_u16::<BE>()?;
1094 let u2 = reader.read_u16::<BE>()?;
1095 let u3 = reader.read_u16::<BE>()?;
1096 let u4 = reader.read_u16::<BE>()?;
1097 Ok(PointExtended {
1098 x,
1099 y,
1100 r,
1101 g,
1102 b,
1103 i,
1104 u1,
1105 u2,
1106 u3,
1107 u4,
1108 })
1109 }
1110}
1111
1112impl SizeBytes for PointExtended {
1113 const SIZE_BYTES: usize = EXTENDED_SAMPLE_SIZE;
1114}
1115
1116impl<P> WriteToBytes for &P
1121where
1122 P: WriteToBytes,
1123{
1124 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
1125 (*self).write_to_bytes(writer)
1126 }
1127}
1128
1129impl<W> WriteBytes for W
1130where
1131 W: WriteBytesExt,
1132{
1133 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
1134 protocol.write_to_bytes(self)
1135 }
1136}
1137
1138impl<R> ReadBytes for R
1139where
1140 R: ReadBytesExt,
1141{
1142 fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
1143 P::read_from_bytes(self)
1144 }
1145}
1146
1147pub trait Point: WriteToBytes + SizeBytes + Copy {
1153 fn x(&self) -> i16;
1155 fn y(&self) -> i16;
1157
1158 fn encode_batch_into(points: &[Self], buf: &mut Vec<u8>) {
1164 buf.reserve(points.len() * Self::SIZE_BYTES);
1165 for p in points {
1166 let _ = p.write_to_bytes(&mut *buf);
1168 }
1169 }
1170}
1171
1172impl Point for PointXyrgbi {
1173 fn x(&self) -> i16 {
1174 self.x
1175 }
1176 fn y(&self) -> i16 {
1177 self.y
1178 }
1179
1180 #[inline]
1181 fn encode_batch_into(points: &[Self], buf: &mut Vec<u8>) {
1182 Self::encode_batch(points, buf);
1183 }
1184}
1185
1186impl Point for PointXyrgbHighRes {
1187 fn x(&self) -> i16 {
1188 self.x
1189 }
1190 fn y(&self) -> i16 {
1191 self.y
1192 }
1193}
1194
1195impl Point for PointExtended {
1196 fn x(&self) -> i16 {
1197 self.x
1198 }
1199 fn y(&self) -> i16 {
1200 self.y
1201 }
1202}
1203
1204pub mod channel_descriptors {
1212 pub const XYRGBI: &[u16] = &[
1215 0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x5214, 0x51CC, 0x5C10, ];
1222
1223 pub const XYRGB_HIGHRES: &[u16] = &[
1226 0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x4010, 0x5214, 0x4010, 0x51CC, 0x4010, ];
1232
1233 pub const EXTENDED: &[u16] = &[
1236 0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x4010, 0x5214, 0x4010, 0x51CC, 0x4010, 0x5C10, 0x4010, 0x51BD, 0x4010, 0x5241, 0x4010, 0x51E8, 0x4010, 0x4201, 0x4010, ];
1247}
1248
1249#[cfg(test)]
1250mod tests {
1251 use super::*;
1252 use crate::point::LaserPoint;
1253
1254 #[test]
1259 fn test_idn_conversion_center() {
1260 let laser_point = LaserPoint::new(0.0, 0.0, 128 * 257, 64 * 257, 32 * 257, 200 * 257);
1263 let idn_point: PointXyrgbi = (&laser_point).into();
1264
1265 assert_eq!(idn_point.x, 0);
1266 assert_eq!(idn_point.y, 0);
1267 assert_eq!(idn_point.r, 128);
1269 assert_eq!(idn_point.g, 64);
1270 assert_eq!(idn_point.b, 32);
1271 assert_eq!(idn_point.i, 200);
1272 }
1273
1274 #[test]
1275 fn test_idn_conversion_min() {
1276 let laser_point = LaserPoint::new(-1.0, -1.0, 0, 0, 0, 0);
1278 let idn_point: PointXyrgbi = (&laser_point).into();
1279
1280 assert_eq!(idn_point.x, 32767);
1281 assert_eq!(idn_point.y, 32767);
1282 }
1283
1284 #[test]
1285 fn test_idn_conversion_max() {
1286 let laser_point = LaserPoint::new(1.0, 1.0, 65535, 65535, 65535, 65535);
1288 let idn_point: PointXyrgbi = (&laser_point).into();
1289
1290 assert_eq!(idn_point.x, -32767);
1291 assert_eq!(idn_point.y, -32767);
1292 assert_eq!(idn_point.r, 255);
1294 assert_eq!(idn_point.g, 255);
1295 assert_eq!(idn_point.b, 255);
1296 assert_eq!(idn_point.i, 255);
1297 }
1298
1299 #[test]
1300 fn test_idn_conversion_half_values() {
1301 let laser_point = LaserPoint::new(0.5, -0.5, 100 * 257, 100 * 257, 100 * 257, 100 * 257);
1302 let idn_point: PointXyrgbi = (&laser_point).into();
1303
1304 assert_eq!(idn_point.x, -16384);
1306 assert_eq!(idn_point.y, 16384);
1307 }
1308
1309 #[test]
1310 fn test_idn_conversion_clamps_out_of_range() {
1311 let laser_point = LaserPoint::new(2.0, -3.0, 65535, 65535, 65535, 65535);
1313 let idn_point: PointXyrgbi = (&laser_point).into();
1314
1315 assert_eq!(idn_point.x, -32767);
1316 assert_eq!(idn_point.y, 32767);
1317 }
1318
1319 #[test]
1320 fn test_idn_coordinate_symmetry() {
1321 let p1 = LaserPoint::new(0.5, 0.0, 0, 0, 0, 0);
1323 let p2 = LaserPoint::new(-0.5, 0.0, 0, 0, 0, 0);
1324 let i1: PointXyrgbi = (&p1).into();
1325 let i2: PointXyrgbi = (&p2).into();
1326
1327 assert_eq!(i1.x, -i2.x);
1328 }
1329
1330 #[test]
1331 fn test_idn_conversion_infinity_clamps() {
1332 let laser_point = LaserPoint::new(f32::INFINITY, f32::NEG_INFINITY, 0, 0, 0, 0);
1333 let idn_point: PointXyrgbi = (&laser_point).into();
1334
1335 assert_eq!(idn_point.x, -32767);
1336 assert_eq!(idn_point.y, 32767);
1337 }
1338
1339 #[test]
1344 fn test_group_request_size() {
1345 assert_eq!(GroupRequest::SIZE_BYTES, 16);
1346 }
1347
1348 #[test]
1349 fn test_group_response_size() {
1350 assert_eq!(GroupResponse::SIZE_BYTES, 4);
1351 }
1352
1353 #[test]
1354 fn test_group_request_get_constructor() {
1355 let req = GroupRequest::get();
1356 assert_eq!(req.struct_size, 16);
1357 assert_eq!(req.op_code, IDNVAL_GROUPOP_GETMASK);
1358 assert_eq!(req.group_mask, 0);
1359 assert_eq!(req.auth_code, [0u8; 12]);
1360 }
1361
1362 #[test]
1363 fn test_group_request_set_constructor() {
1364 let req = GroupRequest::set(0xABCD);
1365 assert_eq!(req.struct_size, 16);
1366 assert_eq!(req.op_code, IDNVAL_GROUPOP_SETMASK);
1367 assert_eq!(req.group_mask, 0xABCD);
1368 assert_eq!(req.auth_code, [0u8; 12]);
1369 }
1370
1371 #[test]
1372 fn test_group_request_set_with_auth() {
1373 let auth = b"secret123";
1374 let req = GroupRequest::set_with_auth(0x1234, auth);
1375 assert_eq!(req.struct_size, 16);
1376 assert_eq!(req.op_code, IDNVAL_GROUPOP_SETMASK);
1377 assert_eq!(req.group_mask, 0x1234);
1378 assert_eq!(&req.auth_code[..9], b"secret123");
1379 assert_eq!(&req.auth_code[9..], &[0u8; 3]);
1380 }
1381
1382 #[test]
1383 fn test_group_request_roundtrip() {
1384 let original = GroupRequest::set(0xFFFF);
1385 let mut buffer = Vec::new();
1386 original.write_to_bytes(&mut buffer).unwrap();
1387 assert_eq!(buffer.len(), 16);
1388
1389 let parsed = GroupRequest::read_from_bytes(&buffer[..]).unwrap();
1390 assert_eq!(parsed.struct_size, original.struct_size);
1391 assert_eq!(parsed.op_code, original.op_code);
1392 assert_eq!(parsed.group_mask, original.group_mask);
1393 assert_eq!(parsed.auth_code, original.auth_code);
1394 }
1395
1396 #[test]
1397 fn test_group_request_byte_layout() {
1398 let req = GroupRequest::set(0x1234);
1399 let mut buffer = Vec::new();
1400 req.write_to_bytes(&mut buffer).unwrap();
1401
1402 assert_eq!(buffer[0], 16);
1404 assert_eq!(buffer[1], 0x02);
1406 assert_eq!(buffer[2], 0x12);
1408 assert_eq!(buffer[3], 0x34);
1409 assert_eq!(&buffer[4..16], &[0u8; 12]);
1411 }
1412
1413 #[test]
1414 fn test_group_response_roundtrip() {
1415 let original = GroupResponse {
1416 struct_size: 4,
1417 op_code: IDNVAL_GROUPOP_SUCCESS,
1418 group_mask: 0x8000,
1419 };
1420 let mut buffer = Vec::new();
1421 original.write_to_bytes(&mut buffer).unwrap();
1422 assert_eq!(buffer.len(), 4);
1423
1424 let parsed = GroupResponse::read_from_bytes(&buffer[..]).unwrap();
1425 assert_eq!(parsed.struct_size, original.struct_size);
1426 assert_eq!(parsed.op_code, original.op_code);
1427 assert_eq!(parsed.group_mask, original.group_mask);
1428 }
1429
1430 #[test]
1431 fn test_group_response_byte_layout() {
1432 let resp = GroupResponse {
1433 struct_size: 4,
1434 op_code: IDNVAL_GROUPOP_SUCCESS,
1435 group_mask: 0xABCD,
1436 };
1437 let mut buffer = Vec::new();
1438 resp.write_to_bytes(&mut buffer).unwrap();
1439
1440 assert_eq!(buffer[0], 4);
1442 assert_eq!(buffer[1], 0x00);
1444 assert_eq!(buffer[2], 0xAB);
1446 assert_eq!(buffer[3], 0xCD);
1447 }
1448
1449 #[test]
1450 fn test_group_response_is_success() {
1451 let success = GroupResponse {
1452 struct_size: 4,
1453 op_code: IDNVAL_GROUPOP_SUCCESS,
1454 group_mask: 0,
1455 };
1456 assert!(success.is_success());
1457
1458 let error_auth = GroupResponse {
1459 struct_size: 4,
1460 op_code: IDNVAL_GROUPOP_ERR_AUTH,
1461 group_mask: 0,
1462 };
1463 assert!(!error_auth.is_success());
1464
1465 let error_op = GroupResponse {
1466 struct_size: 4,
1467 op_code: IDNVAL_GROUPOP_ERR_OPERATION,
1468 group_mask: 0,
1469 };
1470 assert!(!error_op.is_success());
1471
1472 let error_req = GroupResponse {
1473 struct_size: 4,
1474 op_code: IDNVAL_GROUPOP_ERR_REQUEST,
1475 group_mask: 0,
1476 };
1477 assert!(!error_req.is_success());
1478 }
1479
1480 #[test]
1481 fn test_group_response_is_group_enabled() {
1482 let resp = GroupResponse {
1483 struct_size: 4,
1484 op_code: 0,
1485 group_mask: 0b0000_0000_0000_0101, };
1487 assert!(resp.is_group_enabled(0));
1488 assert!(!resp.is_group_enabled(1));
1489 assert!(resp.is_group_enabled(2));
1490 assert!(!resp.is_group_enabled(3));
1491 assert!(!resp.is_group_enabled(16)); }
1493
1494 #[test]
1495 fn test_group_response_enabled_groups() {
1496 let resp = GroupResponse {
1497 struct_size: 4,
1498 op_code: 0,
1499 group_mask: 0b1000_0000_0000_0011, };
1501 assert_eq!(resp.enabled_groups(), vec![0, 1, 15]);
1502 }
1503}