1use crate::command_class::RequestCommandClass;
2use crate::dmx_driver::{
3 ControllerDriverErrorDef, CustomStartCodeControllerDriver, DiscoveryOption,
4 DmxControllerDriver, DmxError, RdmControllerDriver,
5};
6use crate::rdm_data::{RdmData, RdmRequestData};
7use crate::rdm_packages::{
8 deserialize_identify, deserialize_status_messages, deserialize_supported_parameters,
9 RdmResponseInfo, RdmResponsePackage,
10};
11use crate::rdm_types::{
12 DeviceInfo, DiscoveryMuteResponse, DmxStartAddress, OverflowMessageResp, StatusMessages,
13 StatusType, SupportedParameters,
14};
15use crate::types::{DataPack, NackReason, ResponseType};
16use crate::unique_identifier::{PackageAddress, UniqueIdentifier};
17use crate::{pids, rdm_packages, rdm_types};
18
19#[derive(Debug)]
20pub struct DmxControllerConfig {
21 pub rdm_uid: UniqueIdentifier,
22}
23
24impl Default for DmxControllerConfig {
25 fn default() -> Self {
26 Self {
27 rdm_uid: UniqueIdentifier::new(0x7FF0, 0).unwrap(), }
29 }
30}
31
32#[derive(Debug)]
33pub struct RdmRequest {
34 pub destination_uid: PackageAddress,
36 pub parameter_id: u16,
38 pub data: DataPack,
40}
41
42impl RdmRequest {
43 pub fn empty(uid: PackageAddress, pid: u16) -> Self {
45 Self {
46 destination_uid: uid,
47 parameter_id: pid,
48 data: heapless::Vec::new(),
49 }
50 }
51}
52
53#[derive(Debug)]
54pub enum RdmResponse {
55 Response(RdmResponseInfo),
57 IncompleteResponse(RdmResponseInfo),
60 RequestWasBroadcast,
62}
63
64pub struct DmxController<C: ControllerDriverErrorDef> {
66 driver: C,
67 uid: UniqueIdentifier,
68 current_transaction_id: u8,
69 last_message_count: u8,
70}
71
72#[derive(Debug)]
73#[cfg_attr(feature = "defmt", derive(defmt::Format))]
74pub enum RdmResponseError<E> {
75 NotMatching,
77 ParameterDataNotDeserializable,
79 ErrorNotDeserializable,
81 NotReady(u16),
83 NotAcknowledged(NackReason),
85 DmxError(DmxError<E>),
87}
88
89impl<E: core::fmt::Debug> core::fmt::Display for RdmResponseError<E> {
90 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
91 core::fmt::Debug::fmt(self, f)
92 }
93}
94
95impl<E> From<DmxError<E>> for RdmResponseError<E> {
96 fn from(value: DmxError<E>) -> Self {
97 Self::DmxError(value)
98 }
99}
100
101#[cfg(feature = "std")]
102impl<E: core::fmt::Debug + core::fmt::Display> std::error::Error for RdmResponseError<E> {}
103
104impl<E> From<rdm_types::DeserializationError> for RdmResponseError<E> {
105 fn from(_: rdm_types::DeserializationError) -> Self {
106 Self::ParameterDataNotDeserializable
107 }
108}
109
110impl<D: ControllerDriverErrorDef> DmxController<D> {
111 pub fn new(driver: D, config: &DmxControllerConfig) -> Self {
113 Self {
114 driver,
115 uid: config.rdm_uid,
116 current_transaction_id: 0,
117 last_message_count: 0,
118 }
119 }
120
121 pub fn get_driver(&mut self) -> &mut D {
123 &mut self.driver
124 }
125}
126
127impl<D: CustomStartCodeControllerDriver> DmxController<D> {
128 pub fn send_custom_package(
130 &mut self,
131 start_code: u8,
132 package: &[u8],
133 ) -> Result<(), RdmResponseError<D::DriverError>> {
134 self.driver
135 .send_custom_package(start_code, package)
136 .map_err(RdmResponseError::DmxError)
137 }
138}
139
140impl<D: DmxControllerDriver> DmxController<D> {
141 pub fn send_dmx_package(
143 &mut self,
144 package: &[u8],
145 ) -> Result<(), RdmResponseError<D::DriverError>> {
146 self.driver
147 .send_dmx_package(package)
148 .map_err(RdmResponseError::DmxError)
149 }
150}
151
152impl<D: RdmControllerDriver> DmxController<D> {
153 fn rdm_request(
154 &mut self,
155 command_class: RequestCommandClass,
156 request: RdmRequest,
157 ) -> Result<RdmResponse, RdmResponseError<D::DriverError>> {
158 self.current_transaction_id = self.current_transaction_id.wrapping_add(1);
159
160 self.driver.send_rdm(RdmData::Request(RdmRequestData {
161 destination_uid: request.destination_uid,
162 source_uid: self.uid,
163 transaction_number: self.current_transaction_id,
164 port_id: 0,
165 message_count: 0,
166 sub_device: 0,
167 command_class,
168 parameter_id: request.parameter_id,
169 parameter_data: request.data,
170 }))?;
171
172 if request.destination_uid.is_broadcast() {
173 return Ok(RdmResponse::RequestWasBroadcast);
174 }
175
176 let response = loop {
177 let response = match self.driver.receive_rdm()? {
178 RdmData::Request(_) => {
179 return Err(RdmResponseError::NotMatching);
180 },
181 RdmData::Response(response) => response,
182 };
183
184 if self.current_transaction_id == response.transaction_number {
185 break response;
186 }
187 };
188
189 if response.destination_uid != PackageAddress::Device(self.uid) {
190 return Err(RdmResponseError::NotMatching);
191 }
192
193 let response_info = RdmResponseInfo {
194 parameter_id: response.parameter_id,
195 message_count: response.message_count,
196 data: response.parameter_data,
197 };
198
199 self.last_message_count = response.message_count;
200
201 match response.response_type {
202 ResponseType::ResponseTypeAck => Ok(RdmResponse::Response(response_info)),
203 ResponseType::ResponseTypeAckTimer => {
204 if response_info.data.len() != 2 {
205 return Err(RdmResponseError::ErrorNotDeserializable);
206 }
207
208 Err(RdmResponseError::NotReady(u16::from_be_bytes(
209 response_info.data[..2].try_into().unwrap(),
210 )))
211 },
212 ResponseType::ResponseTypeNackReason => {
213 if response_info.data.len() != 2 {
214 return Err(RdmResponseError::ErrorNotDeserializable);
215 }
216
217 let nack_reason = u16::from_be_bytes(response_info.data[..2].try_into().unwrap())
218 .try_into()
219 .or(Err(RdmResponseError::ErrorNotDeserializable))?;
220
221 Err(RdmResponseError::NotAcknowledged(nack_reason))
222 },
223 ResponseType::ResponseTypeAckOverflow => {
224 Ok(RdmResponse::IncompleteResponse(response_info))
225 },
226 }
227 }
228
229 pub fn rdm_get(
231 &mut self,
232 request: RdmRequest,
233 ) -> Result<RdmResponse, RdmResponseError<D::DriverError>> {
234 self.rdm_request(RequestCommandClass::GetCommand, request)
235 }
236
237 pub fn rdm_set(
239 &mut self,
240 request: RdmRequest,
241 ) -> Result<RdmResponse, RdmResponseError<D::DriverError>> {
242 self.rdm_request(RequestCommandClass::SetCommand, request)
243 }
244
245 pub fn rdm_discover(
248 &mut self,
249 first_uid: u64,
250 last_uid: u64,
251 ) -> Result<DiscoveryOption, RdmResponseError<D::DriverError>> {
252 let mut parameter_data = heapless::Vec::new();
253
254 parameter_data
255 .extend_from_slice(&first_uid.to_be_bytes()[2..8])
256 .unwrap();
257 parameter_data
258 .extend_from_slice(&last_uid.to_be_bytes()[2..8])
259 .unwrap();
260
261 self.driver.send_rdm(RdmData::Request(RdmRequestData {
262 destination_uid: PackageAddress::Broadcast,
263 source_uid: self.uid,
264 transaction_number: self.current_transaction_id,
265 port_id: 0,
266 message_count: 0,
267 sub_device: 0,
268 command_class: RequestCommandClass::DiscoveryCommand,
269 parameter_id: pids::DISC_UNIQUE_BRANCH,
270 parameter_data,
271 }))?;
272
273 Ok(self.driver.receive_rdm_discovery_response()?)
274 }
275
276 pub fn rdm_disc_mute(
279 &mut self,
280 uid: PackageAddress,
281 ) -> Result<Option<DiscoveryMuteResponse>, RdmResponseError<D::DriverError>> {
282 let response = self.rdm_request(
283 RequestCommandClass::DiscoveryCommand,
284 RdmRequest::empty(uid, pids::DISC_MUTE),
285 )?;
286
287 deserialize_discovery_mute_response::<D>(&response)
288 }
289
290 pub fn rdm_disc_un_mute(
293 &mut self,
294 uid: PackageAddress,
295 ) -> Result<Option<DiscoveryMuteResponse>, RdmResponseError<D::DriverError>> {
296 let response = self.rdm_request(
297 RequestCommandClass::DiscoveryCommand,
298 RdmRequest::empty(uid, pids::DISC_UN_MUTE),
299 )?;
300
301 deserialize_discovery_mute_response::<D>(&response)
302 }
303
304 pub fn rdm_get_identify(
306 &mut self,
307 uid: UniqueIdentifier,
308 ) -> Result<bool, RdmResponseError<D::DriverError>> {
309 let response = self.rdm_get(RdmRequest::empty(
310 PackageAddress::Device(uid),
311 pids::IDENTIFY_DEVICE,
312 ))?;
313
314 match response {
315 RdmResponse::Response(response_info) => Ok(deserialize_identify(&response_info.data)?),
316 _ => Err(RdmResponseError::ParameterDataNotDeserializable),
317 }
318 }
319
320 pub fn rdm_set_identify(
322 &mut self,
323 uid: PackageAddress,
324 enabled: bool,
325 ) -> Result<(), RdmResponseError<D::DriverError>> {
326 self.rdm_set(RdmRequest {
327 destination_uid: uid,
328 parameter_id: pids::IDENTIFY_DEVICE,
329 data: heapless::Vec::from_slice(&[enabled as u8]).unwrap(),
330 })?;
331
332 Ok(())
333 }
334
335 pub fn rdm_get_software_version_label(
337 &mut self,
338 uid: UniqueIdentifier,
339 ) -> Result<heapless::String<32>, RdmResponseError<D::DriverError>> {
340 let response_info = match self.rdm_get(RdmRequest::empty(
341 PackageAddress::Device(uid),
342 pids::SOFTWARE_VERSION_LABEL,
343 ))? {
344 RdmResponse::Response(response_info) => response_info,
345 _ => return Err(RdmResponseError::ParameterDataNotDeserializable),
346 };
347
348 Ok(rdm_packages::deserialize_software_version_label(
349 &response_info.data,
350 )?)
351 }
352
353 pub fn rdm_get_dmx_start_address(
355 &mut self,
356 uid: UniqueIdentifier,
357 ) -> Result<DmxStartAddress, RdmResponseError<D::DriverError>> {
358 let response = match self.rdm_get(RdmRequest::empty(
359 PackageAddress::Device(uid),
360 pids::DMX_START_ADDRESS,
361 ))? {
362 RdmResponse::Response(response) => response,
363 _ => return Err(RdmResponseError::ParameterDataNotDeserializable),
364 };
365
366 Ok(DmxStartAddress::deserialize(&response.data)?)
367 }
368
369 pub fn rdm_set_dmx_start_address(
371 &mut self,
372 uid: PackageAddress,
373 start_address: u16,
374 ) -> Result<(), RdmResponseError<D::DriverError>> {
375 assert!(
376 (1..=512).contains(&start_address),
377 "The requested start address is not valid."
378 );
379
380 self.rdm_set(RdmRequest {
381 destination_uid: uid,
382 parameter_id: pids::DMX_START_ADDRESS,
383 data: DataPack::from_slice(&start_address.to_be_bytes()).unwrap(),
384 })?;
385
386 Ok(())
387 }
388
389 pub fn rdm_get_queued_message(
402 &mut self,
403 uid: UniqueIdentifier,
404 status_requested: StatusType,
405 ) -> Result<RdmResponsePackage, RdmResponseError<D::DriverError>> {
406 let response = self.rdm_get(RdmRequest {
407 destination_uid: PackageAddress::Device(uid),
408 parameter_id: pids::QUEUED_MESSAGE,
409 data: DataPack::from_slice(&[status_requested as u8]).unwrap(),
410 })?;
411
412 match response {
413 RdmResponse::Response(response_info) => {
414 Ok(RdmResponsePackage::from_response_info(response_info)?)
415 },
416 _ => Err(RdmResponseError::ParameterDataNotDeserializable),
417 }
418 }
419
420 pub fn rdm_get_status_messages(
430 &mut self,
431 uid: UniqueIdentifier,
432 status_requested: StatusType,
433 ) -> Result<OverflowMessageResp<StatusMessages>, RdmResponseError<D::DriverError>> {
434 let response = self.rdm_get(RdmRequest {
435 destination_uid: PackageAddress::Device(uid),
436 parameter_id: pids::STATUS_MESSAGES,
437 data: DataPack::from_slice(&[status_requested as u8]).unwrap(),
438 })?;
439
440 match response {
441 RdmResponse::Response(response_info) => Ok(OverflowMessageResp::Complete(
442 deserialize_status_messages(&response_info.data)?,
443 )),
444 RdmResponse::IncompleteResponse(response_info) => Ok(OverflowMessageResp::Incomplete(
445 deserialize_status_messages(&response_info.data)?,
446 )),
447 _ => Err(RdmResponseError::ParameterDataNotDeserializable),
448 }
449 }
450
451 pub fn rdm_get_supported_parameters(
456 &mut self,
457 uid: UniqueIdentifier,
458 ) -> Result<OverflowMessageResp<SupportedParameters>, RdmResponseError<D::DriverError>> {
459 let response = self.rdm_get(RdmRequest::empty(
460 PackageAddress::Device(uid),
461 pids::SUPPORTED_PARAMETERS,
462 ))?;
463
464 match response {
465 RdmResponse::Response(response_info) => Ok(OverflowMessageResp::Complete(
466 deserialize_supported_parameters(&response_info.data)?,
467 )),
468 RdmResponse::IncompleteResponse(response_info) => Ok(OverflowMessageResp::Incomplete(
469 deserialize_supported_parameters(&response_info.data)?,
470 )),
471 _ => Err(RdmResponseError::ParameterDataNotDeserializable),
472 }
473 }
474
475 pub fn rdm_get_device_info(
477 &mut self,
478 uid: UniqueIdentifier,
479 ) -> Result<DeviceInfo, RdmResponseError<D::DriverError>> {
480 let response = self.rdm_get(RdmRequest::empty(
481 PackageAddress::Device(uid),
482 pids::DEVICE_INFO,
483 ))?;
484 match response {
485 RdmResponse::Response(response_info) => {
486 Ok(DeviceInfo::deserialize(&response_info.data)?)
487 },
488 _ => Err(RdmResponseError::ParameterDataNotDeserializable),
489 }
490 }
491
492 pub fn rdm_get_last_message_count(&self) -> u8 {
494 self.last_message_count
495 }
496}
497
498fn deserialize_discovery_mute_response<D: RdmControllerDriver>(
499 response: &RdmResponse,
500) -> Result<Option<DiscoveryMuteResponse>, RdmResponseError<D::DriverError>> {
501 Ok(match response {
502 RdmResponse::Response(response_info) => Some(
503 DiscoveryMuteResponse::deserialize(&response_info.data)
504 .map_err(|_| RdmResponseError::ParameterDataNotDeserializable)?,
505 ),
506 RdmResponse::RequestWasBroadcast => None,
507 RdmResponse::IncompleteResponse(_) => {
508 return Err(RdmResponseError::ParameterDataNotDeserializable)
509 },
510 })
511}