use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
use std::io;
use crate::point::LaserPoint;
pub const CMD_PORT: u16 = 45457;
pub const DATA_PORT: u16 = 45458;
pub const CMD_GET_FULL_INFO: u8 = 0x77;
pub const CMD_ENABLE_BUFFER_SIZE_RESPONSE: u8 = 0x78;
pub const CMD_SET_OUTPUT: u8 = 0x80;
pub const CMD_SET_RATE: u8 = 0x82;
pub const CMD_GET_RINGBUFFER_EMPTY: u8 = 0x8A;
pub const CMD_CLEAR_RINGBUFFER: u8 = 0x8D;
pub const CMD_SAMPLE_DATA: u8 = 0xA9;
pub const MAX_POINTS_PER_PACKET: usize = 140;
pub const POINT_SIZE_BYTES: usize = 10;
pub const DATA_HEADER_SIZE: usize = 4;
pub const DEFAULT_BUFFER_CAPACITY: u16 = 6000;
pub trait WriteBytes {
fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
}
pub trait WriteToBytes {
fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct Point {
pub x: u16,
pub y: u16,
pub r: u16,
pub g: u16,
pub b: u16,
}
impl Point {
pub const CENTER: u16 = 2047;
pub fn blank() -> Self {
Self {
x: Self::CENTER,
y: Self::CENTER,
r: 0,
g: 0,
b: 0,
}
}
pub fn from_signed(x: i16, y: i16, r: u16, g: u16, b: u16) -> Self {
Self {
x: (x as i32 + 2048).clamp(0, 4095) as u16,
y: (y as i32 + 2048).clamp(0, 4095) as u16,
r,
g,
b,
}
}
pub fn to_signed(&self) -> (i16, i16) {
let x = (self.x as i32 - 2048) as i16;
let y = (self.y as i32 - 2048) as i16;
(x, y)
}
}
impl WriteToBytes for Point {
fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
writer.write_u16::<LittleEndian>(self.x)?;
writer.write_u16::<LittleEndian>(self.y)?;
writer.write_u16::<LittleEndian>(self.r)?;
writer.write_u16::<LittleEndian>(self.g)?;
writer.write_u16::<LittleEndian>(self.b)?;
Ok(())
}
}
impl From<&LaserPoint> for Point {
fn from(p: &LaserPoint) -> Self {
Point {
x: LaserPoint::coord_to_u12(p.x),
y: LaserPoint::coord_to_u12_inverted(p.y),
r: LaserPoint::color_to_u12(p.r),
g: LaserPoint::color_to_u12(p.g),
b: LaserPoint::color_to_u12(p.b),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DeviceInfo {
pub version: u8,
pub max_buffer_space: u16,
}
impl DeviceInfo {
pub fn from_discovery_response(buffer: &[u8]) -> io::Result<Self> {
if buffer.len() < 32 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"discovery response too short: {} bytes, expected at least 32",
buffer.len()
),
));
}
if buffer[0] != CMD_GET_FULL_INFO {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"unexpected command in discovery response: 0x{:02X}",
buffer[0]
),
));
}
let version = buffer[2];
let max_buffer_space = LittleEndian::read_u16(&buffer[21..23]);
Ok(DeviceInfo {
version,
max_buffer_space,
})
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct BufferStatus {
pub message_number: u8,
pub free_space: u16,
}
impl BufferStatus {
pub fn from_response(buffer: &[u8]) -> io::Result<Self> {
if buffer.len() < 4 {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"buffer status response too short: {} bytes, expected at least 4",
buffer.len()
),
));
}
if buffer[0] != CMD_GET_RINGBUFFER_EMPTY {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("unexpected command in buffer status: 0x{:02X}", buffer[0]),
));
}
let message_number = buffer[1];
let free_space = LittleEndian::read_u16(&buffer[2..4]);
Ok(BufferStatus {
message_number,
free_space,
})
}
}
pub mod command {
use super::*;
pub fn get_full_info() -> [u8; 1] {
[CMD_GET_FULL_INFO]
}
pub fn enable_buffer_size_response(enable: bool) -> [u8; 2] {
[CMD_ENABLE_BUFFER_SIZE_RESPONSE, u8::from(enable)]
}
pub fn set_output(enable: bool) -> [u8; 2] {
[CMD_SET_OUTPUT, u8::from(enable)]
}
pub fn set_rate(rate: u32) -> [u8; 5] {
let mut buf = [0u8; 5];
buf[0] = CMD_SET_RATE;
LittleEndian::write_u32(&mut buf[1..5], rate);
buf
}
pub fn clear_ringbuffer() -> [u8; 1] {
[CMD_CLEAR_RINGBUFFER]
}
pub fn sample_data_header(message_number: u8, frame_number: u8) -> [u8; DATA_HEADER_SIZE] {
[CMD_SAMPLE_DATA, 0x00, message_number, frame_number]
}
}
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)
}
}