use byteorder::{ReadBytesExt, WriteBytesExt, BE};
use std::io;
use crate::point::LaserPoint;
pub const IDN_PORT: u16 = 7255;
pub const MAX_UDP_PAYLOAD: usize = 1454;
pub const IDNCMD_VOID: u8 = 0x00;
pub const IDNCMD_PING_REQUEST: u8 = 0x08;
pub const IDNCMD_PING_RESPONSE: u8 = 0x09;
pub const IDNCMD_GROUP_REQUEST: u8 = 0x0C;
pub const IDNCMD_GROUP_RESPONSE: u8 = 0x0D;
pub const IDNCMD_SCAN_REQUEST: u8 = 0x10;
pub const IDNCMD_SCAN_RESPONSE: u8 = 0x11;
pub const IDNCMD_SERVICEMAP_REQUEST: u8 = 0x12;
pub const IDNCMD_SERVICEMAP_RESPONSE: u8 = 0x13;
pub const IDNCMD_SERVICE_PARAMS_REQUEST: u8 = 0x20;
pub const IDNCMD_SERVICE_PARAMS_RESPONSE: u8 = 0x21;
pub const IDNCMD_UNIT_PARAMS_REQUEST: u8 = 0x22;
pub const IDNCMD_UNIT_PARAMS_RESPONSE: u8 = 0x23;
pub const IDNCMD_LINK_PARAMS_REQUEST: u8 = 0x28;
pub const IDNCMD_LINK_PARAMS_RESPONSE: u8 = 0x29;
pub const IDNCMD_RT_CNLMSG: u8 = 0x40;
pub const IDNCMD_RT_CNLMSG_ACKREQ: u8 = 0x41;
pub const IDNCMD_RT_CNLMSG_CLOSE: u8 = 0x44;
pub const IDNCMD_RT_CNLMSG_CLOSE_ACKREQ: u8 = 0x45;
pub const IDNCMD_RT_ABORT: u8 = 0x46;
pub const IDNCMD_RT_ACKNOWLEDGE: u8 = 0x47;
pub const IDNMSK_PKTFLAGS_GROUP: u8 = 0x0F;
pub const IDNFLG_SCAN_STATUS_MALFUNCTION: u8 = 0x80;
pub const IDNFLG_SCAN_STATUS_OFFLINE: u8 = 0x40;
pub const IDNFLG_SCAN_STATUS_EXCLUDED: u8 = 0x20;
pub const IDNFLG_SCAN_STATUS_OCCUPIED: u8 = 0x10;
pub const IDNFLG_SCAN_STATUS_REALTIME: u8 = 0x01;
pub const IDNVAL_GROUPOP_SUCCESS: i8 = 0x00;
pub const IDNVAL_GROUPOP_GETMASK: i8 = 0x01;
pub const IDNVAL_GROUPOP_SETMASK: i8 = 0x02;
pub 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;
pub const IDNVAL_STYPE_UART: u8 = 0x04;
pub const IDNVAL_STYPE_DMX512: u8 = 0x05;
pub const IDNVAL_STYPE_LAPRO: u8 = 0x80;
pub const IDNFLG_SERVICEMAP_DSID: u8 = 0x01;
pub const IDNFLG_CONTENTID_CHANNELMSG: u16 = 0x8000;
pub const IDNFLG_CONTENTID_CONFIG_LSTFRG: u16 = 0x4000;
pub const IDNMSK_CONTENTID_CHANNELID: u16 = 0x3F00;
pub const IDNMSK_CONTENTID_CNKTYPE: u16 = 0x00FF;
pub const IDNVAL_CNKTYPE_VOID: u8 = 0x00;
pub const IDNVAL_CNKTYPE_LPGRF_WAVE: u8 = 0x01;
pub const IDNVAL_CNKTYPE_LPGRF_FRAME: u8 = 0x02;
pub const IDNVAL_CNKTYPE_LPGRF_FRAME_FIRST: u8 = 0x03;
pub const IDNVAL_CNKTYPE_LPGRF_FRAME_SEQUEL: u8 = 0xC0;
pub const IDNFLG_CHNCFG_ROUTING: u8 = 0x01;
pub const IDNFLG_CHNCFG_CLOSE: u8 = 0x02;
pub const IDNVAL_SMOD_VOID: u8 = 0x00;
pub const IDNVAL_SMOD_LPGRF_CONTINUOUS: u8 = 0x01;
pub const IDNVAL_SMOD_LPGRF_DISCRETE: u8 = 0x02;
pub const XYRGBI_SAMPLE_SIZE: usize = 8;
pub const XYRGB_HIGHRES_SAMPLE_SIZE: usize = 10;
pub const EXTENDED_SAMPLE_SIZE: usize = 20;
fn null_terminated_str(bytes: &[u8]) -> &str {
let end = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
std::str::from_utf8(&bytes[..end]).unwrap_or("")
}
pub trait WriteBytes {
fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
}
pub trait ReadBytes {
fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P>;
}
pub trait WriteToBytes {
fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
}
pub trait ReadFromBytes: Sized {
fn read_from_bytes<R: ReadBytesExt>(reader: R) -> io::Result<Self>;
}
pub trait SizeBytes {
const SIZE_BYTES: usize;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct PacketHeader {
pub command: u8,
pub flags: u8,
pub sequence: u16,
}
impl WriteToBytes for PacketHeader {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.command)?;
writer.write_u8(self.flags)?;
writer.write_u16::<BE>(self.sequence)?;
Ok(())
}
}
impl ReadFromBytes for PacketHeader {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let command = reader.read_u8()?;
let flags = reader.read_u8()?;
let sequence = reader.read_u16::<BE>()?;
Ok(PacketHeader {
command,
flags,
sequence,
})
}
}
impl SizeBytes for PacketHeader {
const SIZE_BYTES: usize = 4;
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ScanResponse {
pub struct_size: u8,
pub protocol_version: u8,
pub status: u8,
pub reserved: u8,
pub unit_id: [u8; 16],
pub hostname: [u8; 20],
}
impl WriteToBytes for ScanResponse {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.struct_size)?;
writer.write_u8(self.protocol_version)?;
writer.write_u8(self.status)?;
writer.write_u8(self.reserved)?;
for &byte in &self.unit_id {
writer.write_u8(byte)?;
}
for &byte in &self.hostname {
writer.write_u8(byte)?;
}
Ok(())
}
}
impl ReadFromBytes for ScanResponse {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let struct_size = reader.read_u8()?;
let protocol_version = reader.read_u8()?;
let status = reader.read_u8()?;
let reserved = reader.read_u8()?;
let mut unit_id = [0u8; 16];
for byte in &mut unit_id {
*byte = reader.read_u8()?;
}
let mut hostname = [0u8; 20];
for byte in &mut hostname {
*byte = reader.read_u8()?;
}
Ok(ScanResponse {
struct_size,
protocol_version,
status,
reserved,
unit_id,
hostname,
})
}
}
impl SizeBytes for ScanResponse {
const SIZE_BYTES: usize = 40;
}
impl ScanResponse {
pub fn hostname_str(&self) -> &str {
null_terminated_str(&self.hostname)
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ServiceMapResponseHeader {
pub struct_size: u8,
pub entry_size: u8,
pub relay_entry_count: u8,
pub service_entry_count: u8,
}
impl WriteToBytes for ServiceMapResponseHeader {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.struct_size)?;
writer.write_u8(self.entry_size)?;
writer.write_u8(self.relay_entry_count)?;
writer.write_u8(self.service_entry_count)?;
Ok(())
}
}
impl ReadFromBytes for ServiceMapResponseHeader {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let struct_size = reader.read_u8()?;
let entry_size = reader.read_u8()?;
let relay_entry_count = reader.read_u8()?;
let service_entry_count = reader.read_u8()?;
Ok(ServiceMapResponseHeader {
struct_size,
entry_size,
relay_entry_count,
service_entry_count,
})
}
}
impl SizeBytes for ServiceMapResponseHeader {
const SIZE_BYTES: usize = 4;
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ServiceMapEntry {
pub service_id: u8,
pub service_type: u8,
pub flags: u8,
pub relay_number: u8,
pub name: [u8; 20],
}
impl WriteToBytes for ServiceMapEntry {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.service_id)?;
writer.write_u8(self.service_type)?;
writer.write_u8(self.flags)?;
writer.write_u8(self.relay_number)?;
for &byte in &self.name {
writer.write_u8(byte)?;
}
Ok(())
}
}
impl ReadFromBytes for ServiceMapEntry {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let service_id = reader.read_u8()?;
let service_type = reader.read_u8()?;
let flags = reader.read_u8()?;
let relay_number = reader.read_u8()?;
let mut name = [0u8; 20];
for byte in &mut name {
*byte = reader.read_u8()?;
}
Ok(ServiceMapEntry {
service_id,
service_type,
flags,
relay_number,
name,
})
}
}
impl SizeBytes for ServiceMapEntry {
const SIZE_BYTES: usize = 24;
}
impl ServiceMapEntry {
pub fn name_str(&self) -> &str {
null_terminated_str(&self.name)
}
pub fn is_relay(&self) -> bool {
self.service_id == 0
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ChannelMessageHeader {
pub total_size: u16,
pub content_id: u16,
pub timestamp: u32,
}
impl WriteToBytes for ChannelMessageHeader {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u16::<BE>(self.total_size)?;
writer.write_u16::<BE>(self.content_id)?;
writer.write_u32::<BE>(self.timestamp)?;
Ok(())
}
}
impl ReadFromBytes for ChannelMessageHeader {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let total_size = reader.read_u16::<BE>()?;
let content_id = reader.read_u16::<BE>()?;
let timestamp = reader.read_u32::<BE>()?;
Ok(ChannelMessageHeader {
total_size,
content_id,
timestamp,
})
}
}
impl SizeBytes for ChannelMessageHeader {
const SIZE_BYTES: usize = 8;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ChannelConfigHeader {
pub word_count: u8,
pub flags: u8,
pub service_id: u8,
pub service_mode: u8,
}
impl WriteToBytes for ChannelConfigHeader {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.word_count)?;
writer.write_u8(self.flags)?;
writer.write_u8(self.service_id)?;
writer.write_u8(self.service_mode)?;
Ok(())
}
}
impl ReadFromBytes for ChannelConfigHeader {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let word_count = reader.read_u8()?;
let flags = reader.read_u8()?;
let service_id = reader.read_u8()?;
let service_mode = reader.read_u8()?;
Ok(ChannelConfigHeader {
word_count,
flags,
service_id,
service_mode,
})
}
}
impl SizeBytes for ChannelConfigHeader {
const SIZE_BYTES: usize = 4;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct SampleChunkHeader {
pub flags_duration: u32,
}
impl SampleChunkHeader {
pub fn new(flags: u8, duration_us: u32) -> Self {
let flags_duration = ((flags as u32) << 24) | (duration_us & 0x00FF_FFFF);
Self { flags_duration }
}
pub fn flags(&self) -> u8 {
(self.flags_duration >> 24) as u8
}
pub fn duration_us(&self) -> u32 {
self.flags_duration & 0x00FF_FFFF
}
}
impl WriteToBytes for SampleChunkHeader {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u32::<BE>(self.flags_duration)?;
Ok(())
}
}
impl ReadFromBytes for SampleChunkHeader {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let flags_duration = reader.read_u32::<BE>()?;
Ok(SampleChunkHeader { flags_duration })
}
}
impl SizeBytes for SampleChunkHeader {
const SIZE_BYTES: usize = 4;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct AcknowledgeResponse {
pub struct_size: u8,
pub result_code: i8,
pub input_event_flags: u16,
pub pipeline_event_flags: u16,
pub status_flags: u8,
pub link_quality: u8,
pub latency_us: u16,
}
impl WriteToBytes for AcknowledgeResponse {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.struct_size)?;
writer.write_i8(self.result_code)?;
writer.write_u16::<BE>(self.input_event_flags)?;
writer.write_u16::<BE>(self.pipeline_event_flags)?;
writer.write_u8(self.status_flags)?;
writer.write_u8(self.link_quality)?;
writer.write_u16::<BE>(self.latency_us)?;
Ok(())
}
}
impl ReadFromBytes for AcknowledgeResponse {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let struct_size = reader.read_u8()?;
let result_code = reader.read_i8()?;
let input_event_flags = reader.read_u16::<BE>()?;
let pipeline_event_flags = reader.read_u16::<BE>()?;
let status_flags = reader.read_u8()?;
let link_quality = reader.read_u8()?;
let latency_us = reader.read_u16::<BE>()?;
Ok(AcknowledgeResponse {
struct_size,
result_code,
input_event_flags,
pipeline_event_flags,
status_flags,
link_quality,
latency_us,
})
}
}
impl SizeBytes for AcknowledgeResponse {
const SIZE_BYTES: usize = 10;
}
impl AcknowledgeResponse {
pub fn is_success(&self) -> bool {
self.result_code >= 0
}
pub fn latency(&self) -> std::time::Duration {
std::time::Duration::from_micros(self.latency_us as u64)
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GroupRequest {
pub struct_size: u8,
pub op_code: i8,
pub group_mask: u16,
pub auth_code: [u8; 12],
}
impl GroupRequest {
pub fn get() -> Self {
Self {
struct_size: 16,
op_code: IDNVAL_GROUPOP_GETMASK,
group_mask: 0,
auth_code: [0u8; 12],
}
}
pub fn set(mask: u16) -> Self {
Self {
struct_size: 16,
op_code: IDNVAL_GROUPOP_SETMASK,
group_mask: mask,
auth_code: [0u8; 12],
}
}
pub fn set_with_auth(mask: u16, auth: &[u8]) -> Self {
let mut auth_code = [0u8; 12];
let len = auth.len().min(12);
auth_code[..len].copy_from_slice(&auth[..len]);
Self {
struct_size: 16,
op_code: IDNVAL_GROUPOP_SETMASK,
group_mask: mask,
auth_code,
}
}
}
impl WriteToBytes for GroupRequest {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.struct_size)?;
writer.write_i8(self.op_code)?;
writer.write_u16::<BE>(self.group_mask)?;
for &byte in &self.auth_code {
writer.write_u8(byte)?;
}
Ok(())
}
}
impl ReadFromBytes for GroupRequest {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let struct_size = reader.read_u8()?;
let op_code = reader.read_i8()?;
let group_mask = reader.read_u16::<BE>()?;
let mut auth_code = [0u8; 12];
for byte in &mut auth_code {
*byte = reader.read_u8()?;
}
Ok(GroupRequest {
struct_size,
op_code,
group_mask,
auth_code,
})
}
}
impl SizeBytes for GroupRequest {
const SIZE_BYTES: usize = 16;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GroupResponse {
pub struct_size: u8,
pub op_code: i8,
pub group_mask: u16,
}
impl WriteToBytes for GroupResponse {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.struct_size)?;
writer.write_i8(self.op_code)?;
writer.write_u16::<BE>(self.group_mask)?;
Ok(())
}
}
impl ReadFromBytes for GroupResponse {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let struct_size = reader.read_u8()?;
let op_code = reader.read_i8()?;
let group_mask = reader.read_u16::<BE>()?;
Ok(GroupResponse {
struct_size,
op_code,
group_mask,
})
}
}
impl SizeBytes for GroupResponse {
const SIZE_BYTES: usize = 4;
}
impl GroupResponse {
pub fn is_success(&self) -> bool {
self.op_code >= 0
}
pub fn is_group_enabled(&self, group: u8) -> bool {
if group > 15 {
return false;
}
(self.group_mask & (1 << group)) != 0
}
pub fn enabled_groups(&self) -> Vec<u8> {
(0..16).filter(|&g| self.is_group_enabled(g)).collect()
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParameterGetRequest {
pub service_id: u8,
pub reserved: u8,
pub param_id: u16,
}
impl WriteToBytes for ParameterGetRequest {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.service_id)?;
writer.write_u8(self.reserved)?;
writer.write_u16::<BE>(self.param_id)?;
Ok(())
}
}
impl ReadFromBytes for ParameterGetRequest {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let service_id = reader.read_u8()?;
let reserved = reader.read_u8()?;
let param_id = reader.read_u16::<BE>()?;
Ok(ParameterGetRequest {
service_id,
reserved,
param_id,
})
}
}
impl SizeBytes for ParameterGetRequest {
const SIZE_BYTES: usize = 4;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParameterResponse {
pub service_id: u8,
pub result_code: i8,
pub param_id: u16,
pub value: u32,
}
impl WriteToBytes for ParameterResponse {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.service_id)?;
writer.write_i8(self.result_code)?;
writer.write_u16::<BE>(self.param_id)?;
writer.write_u32::<BE>(self.value)?;
Ok(())
}
}
impl ReadFromBytes for ParameterResponse {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let service_id = reader.read_u8()?;
let result_code = reader.read_i8()?;
let param_id = reader.read_u16::<BE>()?;
let value = reader.read_u32::<BE>()?;
Ok(ParameterResponse {
service_id,
result_code,
param_id,
value,
})
}
}
impl SizeBytes for ParameterResponse {
const SIZE_BYTES: usize = 8;
}
impl ParameterResponse {
pub fn is_success(&self) -> bool {
self.result_code >= 0
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct ParameterSetRequest {
pub service_id: u8,
pub reserved: u8,
pub param_id: u16,
pub value: u32,
}
impl WriteToBytes for ParameterSetRequest {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u8(self.service_id)?;
writer.write_u8(self.reserved)?;
writer.write_u16::<BE>(self.param_id)?;
writer.write_u32::<BE>(self.value)?;
Ok(())
}
}
impl ReadFromBytes for ParameterSetRequest {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let service_id = reader.read_u8()?;
let reserved = reader.read_u8()?;
let param_id = reader.read_u16::<BE>()?;
let value = reader.read_u32::<BE>()?;
Ok(ParameterSetRequest {
service_id,
reserved,
param_id,
value,
})
}
}
impl SizeBytes for ParameterSetRequest {
const SIZE_BYTES: usize = 8;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PointXyrgbi {
pub x: i16,
pub y: i16,
pub r: u8,
pub g: u8,
pub b: u8,
pub i: u8,
}
impl PointXyrgbi {
pub fn new(x: i16, y: i16, r: u8, g: u8, b: u8, i: u8) -> Self {
Self { x, y, r, g, b, i }
}
}
impl WriteToBytes for PointXyrgbi {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_i16::<BE>(self.x)?;
writer.write_i16::<BE>(self.y)?;
writer.write_u8(self.r)?;
writer.write_u8(self.g)?;
writer.write_u8(self.b)?;
writer.write_u8(self.i)?;
Ok(())
}
}
impl PointXyrgbi {
#[inline]
pub fn encode_batch(points: &[Self], buf: &mut Vec<u8>) {
buf.reserve(points.len() * XYRGBI_SAMPLE_SIZE);
for p in points {
let xb = p.x.to_be_bytes();
let yb = p.y.to_be_bytes();
buf.extend_from_slice(&[xb[0], xb[1], yb[0], yb[1], p.r, p.g, p.b, p.i]);
}
}
}
impl ReadFromBytes for PointXyrgbi {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let x = reader.read_i16::<BE>()?;
let y = reader.read_i16::<BE>()?;
let r = reader.read_u8()?;
let g = reader.read_u8()?;
let b = reader.read_u8()?;
let i = reader.read_u8()?;
Ok(PointXyrgbi { x, y, r, g, b, i })
}
}
impl SizeBytes for PointXyrgbi {
const SIZE_BYTES: usize = XYRGBI_SAMPLE_SIZE;
}
impl From<&LaserPoint> for PointXyrgbi {
fn from(p: &LaserPoint) -> Self {
PointXyrgbi::new(
LaserPoint::coord_to_i16_inverted(p.x),
LaserPoint::coord_to_i16_inverted(p.y),
LaserPoint::color_to_u8(p.r),
LaserPoint::color_to_u8(p.g),
LaserPoint::color_to_u8(p.b),
LaserPoint::color_to_u8(p.intensity),
)
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PointXyrgbHighRes {
pub x: i16,
pub y: i16,
pub r: u16,
pub g: u16,
pub b: u16,
}
impl PointXyrgbHighRes {
pub fn new(x: i16, y: i16, r: u16, g: u16, b: u16) -> Self {
Self { x, y, r, g, b }
}
}
impl WriteToBytes for PointXyrgbHighRes {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_i16::<BE>(self.x)?;
writer.write_i16::<BE>(self.y)?;
writer.write_u16::<BE>(self.r)?;
writer.write_u16::<BE>(self.g)?;
writer.write_u16::<BE>(self.b)?;
Ok(())
}
}
impl ReadFromBytes for PointXyrgbHighRes {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let x = reader.read_i16::<BE>()?;
let y = reader.read_i16::<BE>()?;
let r = reader.read_u16::<BE>()?;
let g = reader.read_u16::<BE>()?;
let b = reader.read_u16::<BE>()?;
Ok(PointXyrgbHighRes { x, y, r, g, b })
}
}
impl SizeBytes for PointXyrgbHighRes {
const SIZE_BYTES: usize = XYRGB_HIGHRES_SAMPLE_SIZE;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct PointExtended {
pub x: i16,
pub y: i16,
pub r: u16,
pub g: u16,
pub b: u16,
pub i: u16,
pub u1: u16,
pub u2: u16,
pub u3: u16,
pub u4: u16,
}
impl PointExtended {
#[allow(clippy::too_many_arguments)]
pub fn new(
x: i16,
y: i16,
r: u16,
g: u16,
b: u16,
i: u16,
u1: u16,
u2: u16,
u3: u16,
u4: u16,
) -> Self {
Self {
x,
y,
r,
g,
b,
i,
u1,
u2,
u3,
u4,
}
}
}
impl WriteToBytes for PointExtended {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_i16::<BE>(self.x)?;
writer.write_i16::<BE>(self.y)?;
writer.write_u16::<BE>(self.r)?;
writer.write_u16::<BE>(self.g)?;
writer.write_u16::<BE>(self.b)?;
writer.write_u16::<BE>(self.i)?;
writer.write_u16::<BE>(self.u1)?;
writer.write_u16::<BE>(self.u2)?;
writer.write_u16::<BE>(self.u3)?;
writer.write_u16::<BE>(self.u4)?;
Ok(())
}
}
impl ReadFromBytes for PointExtended {
fn read_from_bytes<R: ReadBytesExt>(mut reader: R) -> io::Result<Self> {
let x = reader.read_i16::<BE>()?;
let y = reader.read_i16::<BE>()?;
let r = reader.read_u16::<BE>()?;
let g = reader.read_u16::<BE>()?;
let b = reader.read_u16::<BE>()?;
let i = reader.read_u16::<BE>()?;
let u1 = reader.read_u16::<BE>()?;
let u2 = reader.read_u16::<BE>()?;
let u3 = reader.read_u16::<BE>()?;
let u4 = reader.read_u16::<BE>()?;
Ok(PointExtended {
x,
y,
r,
g,
b,
i,
u1,
u2,
u3,
u4,
})
}
}
impl SizeBytes for PointExtended {
const SIZE_BYTES: usize = EXTENDED_SAMPLE_SIZE;
}
impl<P> WriteToBytes for &P
where
P: WriteToBytes,
{
fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
(*self).write_to_bytes(writer)
}
}
impl<W> WriteBytes for W
where
W: WriteBytesExt,
{
fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
protocol.write_to_bytes(self)
}
}
impl<R> ReadBytes for R
where
R: ReadBytesExt,
{
fn read_bytes<P: ReadFromBytes>(&mut self) -> io::Result<P> {
P::read_from_bytes(self)
}
}
pub trait Point: WriteToBytes + SizeBytes + Copy {
fn x(&self) -> i16;
fn y(&self) -> i16;
fn encode_batch_into(points: &[Self], buf: &mut Vec<u8>) {
buf.reserve(points.len() * Self::SIZE_BYTES);
for p in points {
let _ = p.write_to_bytes(&mut *buf);
}
}
}
impl Point for PointXyrgbi {
fn x(&self) -> i16 {
self.x
}
fn y(&self) -> i16 {
self.y
}
#[inline]
fn encode_batch_into(points: &[Self], buf: &mut Vec<u8>) {
Self::encode_batch(points, buf);
}
}
impl Point for PointXyrgbHighRes {
fn x(&self) -> i16 {
self.x
}
fn y(&self) -> i16 {
self.y
}
}
impl Point for PointExtended {
fn x(&self) -> i16 {
self.x
}
fn y(&self) -> i16 {
self.y
}
}
pub mod channel_descriptors {
pub const XYRGBI: &[u16] = &[
0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x5214, 0x51CC, 0x5C10, ];
pub const XYRGB_HIGHRES: &[u16] = &[
0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x4010, 0x5214, 0x4010, 0x51CC, 0x4010, ];
pub const EXTENDED: &[u16] = &[
0x4200, 0x4010, 0x4210, 0x4010, 0x527E, 0x4010, 0x5214, 0x4010, 0x51CC, 0x4010, 0x5C10, 0x4010, 0x51BD, 0x4010, 0x5241, 0x4010, 0x51E8, 0x4010, 0x4201, 0x4010, ];
}
#[cfg(test)]
mod tests {
use super::*;
use crate::point::LaserPoint;
#[test]
fn test_idn_conversion_center() {
let laser_point = LaserPoint::new(0.0, 0.0, 128 * 257, 64 * 257, 32 * 257, 200 * 257);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, 0);
assert_eq!(idn_point.y, 0);
assert_eq!(idn_point.r, 128);
assert_eq!(idn_point.g, 64);
assert_eq!(idn_point.b, 32);
assert_eq!(idn_point.i, 200);
}
#[test]
fn test_idn_conversion_min() {
let laser_point = LaserPoint::new(-1.0, -1.0, 0, 0, 0, 0);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, 32767);
assert_eq!(idn_point.y, 32767);
}
#[test]
fn test_idn_conversion_max() {
let laser_point = LaserPoint::new(1.0, 1.0, 65535, 65535, 65535, 65535);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, -32767);
assert_eq!(idn_point.y, -32767);
assert_eq!(idn_point.r, 255);
assert_eq!(idn_point.g, 255);
assert_eq!(idn_point.b, 255);
assert_eq!(idn_point.i, 255);
}
#[test]
fn test_idn_conversion_half_values() {
let laser_point = LaserPoint::new(0.5, -0.5, 100 * 257, 100 * 257, 100 * 257, 100 * 257);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, -16384);
assert_eq!(idn_point.y, 16384);
}
#[test]
fn test_idn_conversion_clamps_out_of_range() {
let laser_point = LaserPoint::new(2.0, -3.0, 65535, 65535, 65535, 65535);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, -32767);
assert_eq!(idn_point.y, 32767);
}
#[test]
fn test_idn_coordinate_symmetry() {
let p1 = LaserPoint::new(0.5, 0.0, 0, 0, 0, 0);
let p2 = LaserPoint::new(-0.5, 0.0, 0, 0, 0, 0);
let i1: PointXyrgbi = (&p1).into();
let i2: PointXyrgbi = (&p2).into();
assert_eq!(i1.x, -i2.x);
}
#[test]
fn test_idn_conversion_infinity_clamps() {
let laser_point = LaserPoint::new(f32::INFINITY, f32::NEG_INFINITY, 0, 0, 0, 0);
let idn_point: PointXyrgbi = (&laser_point).into();
assert_eq!(idn_point.x, -32767);
assert_eq!(idn_point.y, 32767);
}
#[test]
fn test_group_request_size() {
assert_eq!(GroupRequest::SIZE_BYTES, 16);
}
#[test]
fn test_group_response_size() {
assert_eq!(GroupResponse::SIZE_BYTES, 4);
}
#[test]
fn test_group_request_get_constructor() {
let req = GroupRequest::get();
assert_eq!(req.struct_size, 16);
assert_eq!(req.op_code, IDNVAL_GROUPOP_GETMASK);
assert_eq!(req.group_mask, 0);
assert_eq!(req.auth_code, [0u8; 12]);
}
#[test]
fn test_group_request_set_constructor() {
let req = GroupRequest::set(0xABCD);
assert_eq!(req.struct_size, 16);
assert_eq!(req.op_code, IDNVAL_GROUPOP_SETMASK);
assert_eq!(req.group_mask, 0xABCD);
assert_eq!(req.auth_code, [0u8; 12]);
}
#[test]
fn test_group_request_set_with_auth() {
let auth = b"secret123";
let req = GroupRequest::set_with_auth(0x1234, auth);
assert_eq!(req.struct_size, 16);
assert_eq!(req.op_code, IDNVAL_GROUPOP_SETMASK);
assert_eq!(req.group_mask, 0x1234);
assert_eq!(&req.auth_code[..9], b"secret123");
assert_eq!(&req.auth_code[9..], &[0u8; 3]);
}
#[test]
fn test_group_request_roundtrip() {
let original = GroupRequest::set(0xFFFF);
let mut buffer = Vec::new();
original.write_to_bytes(&mut buffer).unwrap();
assert_eq!(buffer.len(), 16);
let parsed = GroupRequest::read_from_bytes(&buffer[..]).unwrap();
assert_eq!(parsed.struct_size, original.struct_size);
assert_eq!(parsed.op_code, original.op_code);
assert_eq!(parsed.group_mask, original.group_mask);
assert_eq!(parsed.auth_code, original.auth_code);
}
#[test]
fn test_group_request_byte_layout() {
let req = GroupRequest::set(0x1234);
let mut buffer = Vec::new();
req.write_to_bytes(&mut buffer).unwrap();
assert_eq!(buffer[0], 16);
assert_eq!(buffer[1], 0x02);
assert_eq!(buffer[2], 0x12);
assert_eq!(buffer[3], 0x34);
assert_eq!(&buffer[4..16], &[0u8; 12]);
}
#[test]
fn test_group_response_roundtrip() {
let original = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_SUCCESS,
group_mask: 0x8000,
};
let mut buffer = Vec::new();
original.write_to_bytes(&mut buffer).unwrap();
assert_eq!(buffer.len(), 4);
let parsed = GroupResponse::read_from_bytes(&buffer[..]).unwrap();
assert_eq!(parsed.struct_size, original.struct_size);
assert_eq!(parsed.op_code, original.op_code);
assert_eq!(parsed.group_mask, original.group_mask);
}
#[test]
fn test_group_response_byte_layout() {
let resp = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_SUCCESS,
group_mask: 0xABCD,
};
let mut buffer = Vec::new();
resp.write_to_bytes(&mut buffer).unwrap();
assert_eq!(buffer[0], 4);
assert_eq!(buffer[1], 0x00);
assert_eq!(buffer[2], 0xAB);
assert_eq!(buffer[3], 0xCD);
}
#[test]
fn test_group_response_is_success() {
let success = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_SUCCESS,
group_mask: 0,
};
assert!(success.is_success());
let error_auth = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_ERR_AUTH,
group_mask: 0,
};
assert!(!error_auth.is_success());
let error_op = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_ERR_OPERATION,
group_mask: 0,
};
assert!(!error_op.is_success());
let error_req = GroupResponse {
struct_size: 4,
op_code: IDNVAL_GROUPOP_ERR_REQUEST,
group_mask: 0,
};
assert!(!error_req.is_success());
}
#[test]
fn test_group_response_is_group_enabled() {
let resp = GroupResponse {
struct_size: 4,
op_code: 0,
group_mask: 0b0000_0000_0000_0101, };
assert!(resp.is_group_enabled(0));
assert!(!resp.is_group_enabled(1));
assert!(resp.is_group_enabled(2));
assert!(!resp.is_group_enabled(3));
assert!(!resp.is_group_enabled(16)); }
#[test]
fn test_group_response_enabled_groups() {
let resp = GroupResponse {
struct_size: 4,
op_code: 0,
group_mask: 0b1000_0000_0000_0011, };
assert_eq!(resp.enabled_groups(), vec![0, 1, 15]);
}
}