1use crate::{LaserInfo, LaserInfoParseError, Point};
4use std::convert::TryFrom;
5use thiserror::Error;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9#[repr(u8)]
10pub enum CommandType {
11 GetFullInfo = 0x77,
13 EnableBufferSizeResponseOnData = 0x78,
15 SetOutput = 0x80,
17 GetRingbufferEmptySampleCount = 0x8a,
19 SampleData = 0xa9,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum Command {
26 GetFullInfo,
28 EnableBufferSizeResponseOnData(bool),
30 SetOutput(bool),
32 GetRingbufferEmptySampleCount,
34 SampleData(SampleData),
36}
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct SampleData {
41 pub message_num: u8,
43 pub frame_num: u8,
45 pub points: Vec<Point>,
47}
48
49#[derive(Debug, Clone, PartialEq)]
51pub enum Response {
52 FullInfo(LaserInfo),
54 BufferFree(u16),
56 Ack,
58}
59
60#[derive(Debug, Error)]
62pub enum ResponseParseError {
63 #[error("Empty response")]
64 EmptyResponse,
65 #[error("Unknown command type: {0}")]
66 UnknownCommandType(u8),
67 #[error("Response too short for {command_type:?} command: expected at least {expected} bytes, got {actual}")]
68 ResponseTooShort {
69 command_type: CommandType,
70 expected: usize,
71 actual: usize,
72 },
73 #[error("Failed to parse LaserInfo: {0}")]
74 LaserInfoError(#[from] LaserInfoParseError),
75}
76
77impl TryFrom<&[u8]> for Response {
78 type Error = ResponseParseError;
79
80 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
81 if bytes.is_empty() {
82 return Err(ResponseParseError::EmptyResponse);
83 }
84
85 let cmd_type = match CommandType::try_from(bytes[0]) {
87 Ok(cmd) => cmd,
88 Err(_) => return Err(ResponseParseError::UnknownCommandType(bytes[0])),
89 };
90
91 match cmd_type {
92 CommandType::GetFullInfo => {
93 let laser_info = LaserInfo::try_from(bytes)?;
95 Ok(Response::FullInfo(laser_info))
96 }
97
98 CommandType::GetRingbufferEmptySampleCount => {
99 let minimum_len = 4;
100 if bytes.len() < minimum_len {
101 return Err(ResponseParseError::ResponseTooShort {
102 command_type: cmd_type,
103 expected: minimum_len,
104 actual: bytes.len(),
105 });
106 }
107
108 let buffer_free = u16::from_le_bytes([bytes[2], bytes[3]]);
109 Ok(Response::BufferFree(buffer_free))
110 }
111
112 CommandType::SampleData => {
114 let minimum_len = 3;
115 if bytes.len() < minimum_len {
116 return Err(ResponseParseError::ResponseTooShort {
117 command_type: cmd_type,
118 expected: minimum_len,
119 actual: bytes.len(),
120 });
121 }
122
123 let buffer_free = u16::from_le_bytes([bytes[1], bytes[2]]);
125 Ok(Response::BufferFree(buffer_free))
126 }
127
128 CommandType::EnableBufferSizeResponseOnData | CommandType::SetOutput => {
130 Ok(Response::Ack)
131 }
132 }
133 }
134}
135
136impl TryFrom<u8> for CommandType {
137 type Error = ();
138 fn try_from(value: u8) -> Result<Self, Self::Error> {
139 match value {
140 0x77 => Ok(CommandType::GetFullInfo),
141 0x78 => Ok(CommandType::EnableBufferSizeResponseOnData),
142 0x80 => Ok(CommandType::SetOutput),
143 0x8a => Ok(CommandType::GetRingbufferEmptySampleCount),
144 0xa9 => Ok(CommandType::SampleData),
145 _ => Err(()),
146 }
147 }
148}
149
150impl Command {
151 pub fn command_type(&self) -> CommandType {
153 match self {
154 Command::GetFullInfo => CommandType::GetFullInfo,
155 Command::EnableBufferSizeResponseOnData(_) => {
156 CommandType::EnableBufferSizeResponseOnData
157 }
158 Command::SetOutput(_) => CommandType::SetOutput,
159 Command::GetRingbufferEmptySampleCount => CommandType::GetRingbufferEmptySampleCount,
160 Command::SampleData { .. } => CommandType::SampleData,
161 }
162 }
163
164 pub fn size(&self) -> usize {
166 match self {
167 Command::GetFullInfo => 1,
168 Command::EnableBufferSizeResponseOnData(_) => 2,
169 Command::SetOutput(_) => 2,
170 Command::GetRingbufferEmptySampleCount => 1,
171 Command::SampleData(SampleData { points, .. }) => {
172 4 + (points.len() * 10) }
178 }
179 }
180
181 pub fn write_bytes(&self, buffer: &mut Vec<u8>) -> usize {
185 let start_len = buffer.len();
186
187 match self {
188 Command::GetFullInfo => {
189 buffer.push(CommandType::GetFullInfo as u8);
190 }
191
192 Command::EnableBufferSizeResponseOnData(enable) => {
193 buffer.push(CommandType::EnableBufferSizeResponseOnData as u8);
194 buffer.push(if *enable { 1 } else { 0 });
195 }
196
197 Command::SetOutput(enable) => {
198 buffer.push(CommandType::SetOutput as u8);
199 buffer.push(if *enable { 1 } else { 0 });
200 }
201
202 Command::GetRingbufferEmptySampleCount => {
203 buffer.push(CommandType::GetRingbufferEmptySampleCount as u8);
204 }
205
206 Command::SampleData(data) => {
207 buffer.push(CommandType::SampleData as u8);
209 buffer.push(0x00); buffer.push(data.message_num);
211 buffer.push(data.frame_num);
212
213 for point in &data.points {
215 let point_bytes: [u8; Point::SIZE] = (*point).into();
216 buffer.extend_from_slice(&point_bytes);
217 }
218 }
219 }
220
221 buffer.len() - start_len
222 }
223
224 pub fn to_bytes(&self) -> Vec<u8> {
226 let mut buffer = Vec::with_capacity(self.size());
227 self.write_bytes(&mut buffer);
228 buffer
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_parse_buffer_free_response() {
238 let response = [0x8a, 0x00, 0xe8, 0x03]; let parsed = Response::try_from(&response[..]).unwrap();
242
243 match parsed {
244 Response::BufferFree(free) => assert_eq!(free, 1000),
245 _ => panic!("Wrong response type parsed"),
246 }
247 }
248
249 #[test]
250 fn test_parse_ack_response() {
251 let response = [0x80];
253
254 let parsed = Response::try_from(&response[..]).unwrap();
255
256 match parsed {
257 Response::Ack => {}
258 _ => panic!("Wrong response type parsed"),
259 }
260 }
261
262 #[test]
263 fn test_parse_error_handling() {
264 let result = Response::try_from(&[][..]);
266 assert!(matches!(result, Err(ResponseParseError::EmptyResponse)));
267
268 let result = Response::try_from(&[0xFF][..]);
270 assert!(matches!(
271 result,
272 Err(ResponseParseError::UnknownCommandType(0xFF))
273 ));
274
275 let result = Response::try_from(&[0x8a, 0x00][..]);
277 assert!(matches!(
278 result,
279 Err(ResponseParseError::ResponseTooShort {
280 command_type: CommandType::GetRingbufferEmptySampleCount,
281 ..
282 })
283 ));
284 }
285}