1use crate::command_class::{RequestCommandClass, ResponseCommandClass};
2use crate::consts::{
3 RDM_DISCOVERY_RESPONSE_SIZE, RDM_MAX_PACKAGE_SIZE, RDM_MAX_PARAMETER_DATA_LENGTH,
4 RDM_MIN_PACKAGE_SIZE, SC_RDM, SC_SUB_MESSAGE, SEPARATOR_BYTE,
5};
6use crate::layouts::rdm_request_layout;
7use crate::types::{DataPack, ResponseType};
8use crate::unique_identifier::{PackageAddress, UniqueIdentifier};
9use crate::utils::calculate_checksum;
10
11pub type BinaryRdmPackage = heapless::Vec<u8, RDM_MAX_PACKAGE_SIZE>;
13
14#[derive(Debug)]
17#[cfg_attr(feature = "defmt", derive(defmt::Format))]
18pub struct IsBroadcastError;
19
20impl core::fmt::Display for IsBroadcastError {
21 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
22 write!(f, "tried to convert broadcast request to response")
23 }
24}
25
26#[cfg(feature = "std")]
27impl std::error::Error for IsBroadcastError {}
28
29#[derive(Debug)]
31pub struct RdmRequestData {
32 pub destination_uid: PackageAddress,
33 pub source_uid: UniqueIdentifier,
34 pub transaction_number: u8,
35 pub port_id: u8,
36 pub message_count: u8,
37 pub sub_device: u16,
38 pub command_class: RequestCommandClass,
39 pub parameter_id: u16,
40 pub parameter_data: DataPack,
41}
42
43impl RdmRequestData {
44 pub fn build_response(
45 &self,
46 response_type: ResponseType,
47 response: DataPack,
48 message_count: u8,
49 ) -> Result<RdmResponseData, IsBroadcastError> {
50 Ok(RdmResponseData {
51 destination_uid: PackageAddress::Device(self.source_uid),
52 source_uid: match self.destination_uid {
53 PackageAddress::Device(uid) => uid,
54 _ => return Err(IsBroadcastError),
55 },
56 transaction_number: self.transaction_number,
57 response_type,
58 message_count,
59 sub_device: self.sub_device,
60 command_class: self.command_class.get_response_class(),
61 parameter_id: self.parameter_id,
62 parameter_data: response,
63 })
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct RdmResponseData {
70 pub destination_uid: PackageAddress,
71 pub source_uid: UniqueIdentifier,
72 pub transaction_number: u8,
73 pub response_type: ResponseType,
74 pub message_count: u8,
75 pub sub_device: u16,
76 pub command_class: ResponseCommandClass,
77 pub parameter_id: u16,
78 pub parameter_data: DataPack,
79}
80
81#[derive(Debug, Copy, Clone, Eq, PartialEq)]
82#[cfg_attr(feature = "defmt", derive(defmt::Format))]
83pub enum RdmDeserializationError {
84 BufferTooSmall,
86 BufferTooBig,
88 CommandClassNotFound(u8),
90 ResponseTypeNotFound(u8),
92 WrongMessageLength(usize),
94 WrongChecksum,
96 WrongStartCode,
98 SourceUidIsBroadcast,
100}
101
102impl core::fmt::Display for RdmDeserializationError {
103 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
104 match self {
105 RdmDeserializationError::BufferTooSmall => write!(f, "buffer too small"),
106 RdmDeserializationError::BufferTooBig => write!(f, "buffer to big"),
107 RdmDeserializationError::CommandClassNotFound(command_class) => {
108 write!(f, "command class {} not found", command_class)
109 },
110 RdmDeserializationError::ResponseTypeNotFound(response_type) => {
111 write!(f, "response type {} is unknown", response_type)
112 },
113 RdmDeserializationError::WrongMessageLength(message_length) => {
114 write!(f, "message length {} is incorrect", message_length)
115 },
116 RdmDeserializationError::WrongChecksum => write!(f, "checksum is incorrect"),
117 RdmDeserializationError::WrongStartCode => write!(f, "start code is incorrect"),
118 RdmDeserializationError::SourceUidIsBroadcast => write!(f, "source uid is a broadcast"),
119 }
120 }
121}
122
123#[cfg(feature = "std")]
124impl std::error::Error for RdmDeserializationError {}
125
126#[derive(Debug)]
128pub enum RdmData {
129 Request(RdmRequestData),
131 Response(RdmResponseData),
133}
134
135impl RdmData {
136 pub fn deserialize(buf: &[u8]) -> Result<Self, RdmDeserializationError> {
139 deserialize_rdm_data(buf)
140 }
141
142 pub fn serialize(&self) -> BinaryRdmPackage {
145 serialize_rdm_data(self)
146 }
147}
148
149pub fn deserialize_rdm_data(buffer: &[u8]) -> Result<RdmData, RdmDeserializationError> {
153 let buffer_size = buffer.len();
154
155 if buffer_size < RDM_MIN_PACKAGE_SIZE {
156 return Err(RdmDeserializationError::BufferTooSmall);
157 }
158
159 if buffer_size > RDM_MAX_PACKAGE_SIZE {
160 return Err(RdmDeserializationError::BufferTooBig);
161 }
162
163 let expected_checksum = calculate_checksum(&buffer[..buffer_size - 2]);
165 let actual_checksum =
166 u16::from_be_bytes(buffer[buffer_size - 2..buffer_size].try_into().unwrap());
167
168 if expected_checksum != actual_checksum {
169 return Err(RdmDeserializationError::WrongChecksum);
170 }
171
172 let request_data_view = rdm_request_layout::View::new(buffer);
173
174 if request_data_view.start_code().read() != SC_RDM
175 || request_data_view.sub_start_code().read() != SC_SUB_MESSAGE
176 {
177 return Err(RdmDeserializationError::WrongStartCode);
178 }
179
180 let message_length = request_data_view.message_length().read() as usize;
182 if message_length != buffer_size - 2 {
183 return Err(RdmDeserializationError::WrongMessageLength(message_length));
184 }
185
186 let parameter_data_and_checksum = request_data_view.parameter_data_and_checksum();
187 let parameter_data =
189 DataPack::from_slice(¶meter_data_and_checksum[..parameter_data_and_checksum.len() - 2])
190 .map_err(|_| RdmDeserializationError::BufferTooBig)?;
191
192 let command_class_field = request_data_view.command_class().read();
193 let is_request = RequestCommandClass::try_from(command_class_field).is_ok();
194
195 let rdm_data = if is_request {
196 RdmData::Request(RdmRequestData {
197 destination_uid: PackageAddress::from_bytes(request_data_view.destination_uid()),
198 source_uid: match PackageAddress::from_bytes(request_data_view.source_uid()) {
199 PackageAddress::Device(device_uid) => device_uid,
200 _ => return Err(RdmDeserializationError::SourceUidIsBroadcast),
201 },
202 transaction_number: request_data_view.transaction_number().read(),
203 port_id: request_data_view.port_id_response_type().read(),
204 message_count: request_data_view.message_count().read(),
205 sub_device: request_data_view.sub_device().read(),
206 command_class: command_class_field
207 .try_into()
208 .map_err(|_| RdmDeserializationError::CommandClassNotFound(command_class_field))?,
209 parameter_id: request_data_view.parameter_id().read(),
210 parameter_data,
211 })
212 } else {
213 let response_type_field = request_data_view.port_id_response_type().read();
214 let response_type = response_type_field
215 .try_into()
216 .map_err(|_| RdmDeserializationError::ResponseTypeNotFound(response_type_field))?;
217
218 RdmData::Response(RdmResponseData {
219 destination_uid: PackageAddress::from_bytes(request_data_view.destination_uid()),
220 source_uid: match PackageAddress::from_bytes(request_data_view.source_uid()) {
221 PackageAddress::Device(uid) => uid,
222 _ => return Err(RdmDeserializationError::SourceUidIsBroadcast),
223 },
224 transaction_number: request_data_view.transaction_number().read(),
225 response_type,
226 message_count: request_data_view.message_count().read(),
227 sub_device: request_data_view.sub_device().read(),
228 command_class: command_class_field
229 .try_into()
230 .map_err(|_| RdmDeserializationError::CommandClassNotFound(command_class_field))?,
231 parameter_id: request_data_view.parameter_id().read(),
232 parameter_data,
233 })
234 };
235
236 Ok(rdm_data)
237}
238
239pub fn serialize_rdm_data(rdm_data: &RdmData) -> BinaryRdmPackage {
241 let mut dst = [0u8; RDM_MAX_PACKAGE_SIZE];
242
243 let parameter_data_length = match rdm_data {
244 RdmData::Request(ref request) => request.parameter_data.len(),
245 RdmData::Response(ref response) => response.parameter_data.len(),
246 };
247 assert!(parameter_data_length <= RDM_MAX_PARAMETER_DATA_LENGTH);
248
249 let total_package_length = parameter_data_length + 26;
251 let mut memory_view = rdm_request_layout::View::new(&mut dst[..total_package_length]);
252
253 memory_view.start_code_mut().write(SC_RDM);
254 memory_view.sub_start_code_mut().write(SC_SUB_MESSAGE);
255
256 memory_view
258 .message_length_mut()
259 .write(parameter_data_length as u8 + 24);
260
261 match rdm_data {
262 RdmData::Request(request) => {
263 memory_view
264 .destination_uid_mut()
265 .copy_from_slice(&request.destination_uid.to_bytes());
266 memory_view
267 .source_uid_mut()
268 .copy_from_slice(&request.source_uid.to_bytes());
269
270 memory_view
271 .transaction_number_mut()
272 .write(request.transaction_number);
273 memory_view
274 .port_id_response_type_mut()
275 .write(request.port_id);
276 memory_view.sub_device_mut().write(request.sub_device);
277 memory_view
278 .command_class_mut()
279 .write(request.command_class as u8);
280 memory_view.parameter_id_mut().write(request.parameter_id);
281 memory_view
282 .parameter_data_length_mut()
283 .write(parameter_data_length as u8);
284
285 memory_view.parameter_data_and_checksum_mut()[..parameter_data_length]
286 .copy_from_slice(&request.parameter_data);
287 let checksum = calculate_checksum(&dst[..total_package_length - 2]);
288 dst[total_package_length - 2..total_package_length]
289 .copy_from_slice(&checksum.to_be_bytes());
290 },
291 RdmData::Response(response) => {
292 memory_view
293 .destination_uid_mut()
294 .copy_from_slice(&response.destination_uid.to_bytes());
295 memory_view
296 .source_uid_mut()
297 .copy_from_slice(&response.source_uid.to_bytes());
298
299 memory_view
300 .transaction_number_mut()
301 .write(response.transaction_number);
302 memory_view
303 .port_id_response_type_mut()
304 .write(response.response_type as u8);
305 memory_view.sub_device_mut().write(response.sub_device);
306 memory_view
307 .command_class_mut()
308 .write(response.command_class as u8);
309 memory_view.parameter_id_mut().write(response.parameter_id);
310 memory_view
311 .parameter_data_length_mut()
312 .write(parameter_data_length as u8);
313
314 memory_view.parameter_data_and_checksum_mut()[..parameter_data_length]
315 .copy_from_slice(&response.parameter_data);
316 let checksum = calculate_checksum(&dst[..total_package_length - 2]);
317 dst[total_package_length - 2..total_package_length]
318 .copy_from_slice(&checksum.to_be_bytes());
319 },
320 }
321
322 heapless::Vec::from_slice(&dst[..total_package_length]).unwrap()
324}
325
326pub fn deserialize_discovery_response(
328 buffer: &[u8],
329) -> Result<UniqueIdentifier, RdmDeserializationError> {
330 let index_of_separator_byte = match buffer.iter().position(|&x| x == SEPARATOR_BYTE) {
331 None => {
332 return Err(RdmDeserializationError::WrongStartCode); },
334 Some(index) => index,
335 };
336
337 let start_index = index_of_separator_byte + 1;
338 let message_length = buffer.len() - start_index;
339 if message_length < RDM_DISCOVERY_RESPONSE_SIZE {
340 return Err(RdmDeserializationError::WrongMessageLength(message_length));
341 }
342
343 let calculated_checksum = calculate_checksum(&buffer[start_index..start_index + 12]);
344
345 let mut device_id_buf = [0u8; 6];
346 decode_disc_unique(&buffer[start_index..start_index + 12], &mut device_id_buf);
347 let uid = match PackageAddress::from_bytes(&device_id_buf) {
348 PackageAddress::Device(uid) => uid,
349 _ => return Err(RdmDeserializationError::SourceUidIsBroadcast),
350 };
351
352 let mut checksum_buf = [0u8; 2];
353 decode_disc_unique(
354 &buffer[start_index + 12..start_index + 16],
355 &mut checksum_buf,
356 );
357 let received_checksum = u16::from_be_bytes(checksum_buf);
358
359 if calculated_checksum != received_checksum {
360 return Err(RdmDeserializationError::WrongChecksum);
361 }
362
363 Ok(uid)
364}
365
366fn decode_disc_unique(src: &[u8], dest: &mut [u8]) {
368 assert!(
369 dest.len() * 2 >= src.len(),
370 "Destination buffer has to be at least half the size of the source buffer."
371 );
372
373 for (index, byte) in src.chunks(2).map(|chunk| chunk[0] & chunk[1]).enumerate() {
374 dest[index] = byte;
375 }
376}