laser_dac/protocols/lasercube_wifi/
protocol.rs1use byteorder::{ByteOrder, LittleEndian, WriteBytesExt};
4use std::io;
5
6use crate::point::LaserPoint;
7
8pub const CMD_PORT: u16 = 45457;
11pub const DATA_PORT: u16 = 45458;
13
14pub const CMD_GET_FULL_INFO: u8 = 0x77;
17pub const CMD_ENABLE_BUFFER_SIZE_RESPONSE: u8 = 0x78;
19pub const CMD_SET_OUTPUT: u8 = 0x80;
21pub const CMD_SET_RATE: u8 = 0x82;
23pub const CMD_GET_RINGBUFFER_EMPTY: u8 = 0x8A;
25pub const CMD_CLEAR_RINGBUFFER: u8 = 0x8D;
27pub const CMD_SAMPLE_DATA: u8 = 0xA9;
29
30pub const MAX_POINTS_PER_PACKET: usize = 140;
32
33pub const POINT_SIZE_BYTES: usize = 10;
35
36pub const DATA_HEADER_SIZE: usize = 4;
38
39pub const DEFAULT_BUFFER_CAPACITY: u16 = 6000;
41
42pub trait WriteBytes {
44 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()>;
45}
46
47pub trait WriteToBytes {
49 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()>;
50}
51
52#[repr(C)]
62#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
63pub struct Point {
64 pub x: u16,
66 pub y: u16,
68 pub r: u16,
70 pub g: u16,
72 pub b: u16,
74}
75
76impl Point {
77 pub const CENTER: u16 = 2047;
79
80 pub fn blank() -> Self {
82 Self {
83 x: Self::CENTER,
84 y: Self::CENTER,
85 r: 0,
86 g: 0,
87 b: 0,
88 }
89 }
90
91 pub fn from_signed(x: i16, y: i16, r: u16, g: u16, b: u16) -> Self {
95 Self {
96 x: (x as i32 + 2048).clamp(0, 4095) as u16,
97 y: (y as i32 + 2048).clamp(0, 4095) as u16,
98 r,
99 g,
100 b,
101 }
102 }
103
104 pub fn to_signed(&self) -> (i16, i16) {
106 let x = (self.x as i32 - 2048) as i16;
107 let y = (self.y as i32 - 2048) as i16;
108 (x, y)
109 }
110}
111
112impl WriteToBytes for Point {
113 fn write_to_bytes<W: WriteBytesExt>(&self, mut writer: W) -> io::Result<()> {
114 writer.write_u16::<LittleEndian>(self.x)?;
115 writer.write_u16::<LittleEndian>(self.y)?;
116 writer.write_u16::<LittleEndian>(self.r)?;
117 writer.write_u16::<LittleEndian>(self.g)?;
118 writer.write_u16::<LittleEndian>(self.b)?;
119 Ok(())
120 }
121}
122
123impl From<&LaserPoint> for Point {
124 fn from(p: &LaserPoint) -> Self {
130 Point {
131 x: LaserPoint::coord_to_u12(p.x),
132 y: LaserPoint::coord_to_u12_inverted(p.y),
133 r: LaserPoint::color_to_u12(p.r),
134 g: LaserPoint::color_to_u12(p.g),
135 b: LaserPoint::color_to_u12(p.b),
136 }
137 }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct DeviceInfo {
143 pub version: u8,
145 pub max_buffer_space: u16,
147}
148
149impl DeviceInfo {
150 pub fn from_discovery_response(buffer: &[u8]) -> io::Result<Self> {
158 if buffer.len() < 32 {
159 return Err(io::Error::new(
160 io::ErrorKind::InvalidData,
161 format!(
162 "discovery response too short: {} bytes, expected at least 32",
163 buffer.len()
164 ),
165 ));
166 }
167
168 if buffer[0] != CMD_GET_FULL_INFO {
170 return Err(io::Error::new(
171 io::ErrorKind::InvalidData,
172 format!(
173 "unexpected command in discovery response: 0x{:02X}",
174 buffer[0]
175 ),
176 ));
177 }
178
179 let version = buffer[2];
180 let max_buffer_space = LittleEndian::read_u16(&buffer[21..23]);
181
182 Ok(DeviceInfo {
183 version,
184 max_buffer_space,
185 })
186 }
187}
188
189#[derive(Copy, Clone, Debug, PartialEq, Eq)]
191pub struct BufferStatus {
192 pub message_number: u8,
194 pub free_space: u16,
196}
197
198impl BufferStatus {
199 pub fn from_response(buffer: &[u8]) -> io::Result<Self> {
206 if buffer.len() < 4 {
207 return Err(io::Error::new(
208 io::ErrorKind::InvalidData,
209 format!(
210 "buffer status response too short: {} bytes, expected at least 4",
211 buffer.len()
212 ),
213 ));
214 }
215
216 if buffer[0] != CMD_GET_RINGBUFFER_EMPTY {
217 return Err(io::Error::new(
218 io::ErrorKind::InvalidData,
219 format!("unexpected command in buffer status: 0x{:02X}", buffer[0]),
220 ));
221 }
222
223 let message_number = buffer[1];
224 let free_space = LittleEndian::read_u16(&buffer[2..4]);
225 Ok(BufferStatus {
226 message_number,
227 free_space,
228 })
229 }
230}
231
232pub mod command {
234 use super::*;
235
236 pub fn get_full_info() -> [u8; 1] {
238 [CMD_GET_FULL_INFO]
239 }
240
241 pub fn enable_buffer_size_response(enable: bool) -> [u8; 2] {
243 [CMD_ENABLE_BUFFER_SIZE_RESPONSE, u8::from(enable)]
244 }
245
246 pub fn set_output(enable: bool) -> [u8; 2] {
248 [CMD_SET_OUTPUT, u8::from(enable)]
249 }
250
251 pub fn set_rate(rate: u32) -> [u8; 5] {
253 let mut buf = [0u8; 5];
254 buf[0] = CMD_SET_RATE;
255 LittleEndian::write_u32(&mut buf[1..5], rate);
256 buf
257 }
258
259 pub fn clear_ringbuffer() -> [u8; 1] {
261 [CMD_CLEAR_RINGBUFFER]
262 }
263
264 pub fn sample_data_header(message_number: u8, frame_number: u8) -> [u8; DATA_HEADER_SIZE] {
268 [CMD_SAMPLE_DATA, 0x00, message_number, frame_number]
269 }
270}
271
272impl<P> WriteToBytes for &P
273where
274 P: WriteToBytes,
275{
276 fn write_to_bytes<W: WriteBytesExt>(&self, writer: W) -> io::Result<()> {
277 (*self).write_to_bytes(writer)
278 }
279}
280
281impl<W> WriteBytes for W
282where
283 W: WriteBytesExt,
284{
285 fn write_bytes<P: WriteToBytes>(&mut self, protocol: P) -> io::Result<()> {
286 protocol.write_to_bytes(self)
287 }
288}