dmx_rdm/
utils.rs

1use crate::dmx_controller::{DmxController, RdmResponseError};
2use crate::dmx_driver::{DiscoveryOption, DmxError, RdmControllerDriver};
3use crate::unique_identifier::{PackageAddress, UniqueIdentifier};
4
5/// Blocking recursive discovery.
6///
7/// It will find and mute all devices until it captured all of them
8/// or the provided uid_array is full.
9///
10/// Before the first time this function gets called, one should unmute all rdm responders
11/// using a DISC_UN_MUTE broadcast. This can be done using the [DmxController::rdm_disc_un_mute]
12/// method.
13///
14/// The returned value is the amount of devices found. If the length of the provided array
15/// equals the amount of devices found it is advisable to run this function again with additional
16/// array space.
17///
18/// <div class="warning">Since this function is blocking and does not make polled approaches
19/// possible, it is not suitable for embedded. Use this function as a starting point only and create
20/// a custom solution that fits your platform and use-case best. Refer to Section 7 of the
21/// ANSI E1.20 specifications for this.</div>
22pub fn run_full_discovery<Driver: RdmControllerDriver>(
23    manager: &mut DmxController<Driver>,
24    uid_array: &mut [UniqueIdentifier],
25) -> Result<usize, RdmResponseError<Driver::DriverError>> {
26    let addresses_found = discover_range(manager, 0x00000001, 0xFFFFFFFFFFFE, uid_array)?;
27
28    Ok(addresses_found)
29}
30
31fn discover_range<Driver: RdmControllerDriver>(
32    manager: &mut DmxController<Driver>,
33    lower_bound: u64,
34    upper_bound: u64,
35    uid_array: &mut [UniqueIdentifier],
36) -> Result<usize, RdmResponseError<Driver::DriverError>> {
37    let discovery_option = manager.rdm_discover(lower_bound, upper_bound)?;
38
39    if uid_array.is_empty() {
40        return Ok(0);
41    }
42
43    match discovery_option {
44        DiscoveryOption::Collision => {
45            if upper_bound - lower_bound <= 1 {
46                return Ok(0);
47            }
48
49            let first_lower_bound = lower_bound;
50            let first_upper_bound = (upper_bound + lower_bound) / 2;
51
52            let second_lower_bound = first_upper_bound + 1;
53            let second_upper_bound = upper_bound;
54
55            let upper_addresses_found =
56                discover_range(manager, second_lower_bound, second_upper_bound, uid_array)?;
57
58            let lower_address_found = discover_range(
59                manager,
60                first_lower_bound,
61                first_upper_bound,
62                &mut uid_array[upper_addresses_found..],
63            )?;
64
65            Ok(upper_addresses_found + lower_address_found)
66        },
67        DiscoveryOption::NoDevice => Ok(0),
68        DiscoveryOption::Found(uid) => {
69            match manager.rdm_disc_mute(PackageAddress::Device(uid)) {
70                Err(RdmResponseError::DmxError(DmxError::TimeoutError)) => return Ok(0),
71                result => result,
72            }?;
73            uid_array[0] = uid;
74
75            Ok(1)
76        },
77    }
78}
79
80#[inline]
81pub(crate) fn calculate_checksum(data: &[u8]) -> u16 {
82    let mut checksum = 0u16;
83
84    for byte in data {
85        checksum = checksum.wrapping_add(*byte as u16);
86    }
87
88    checksum
89}
90
91pub(crate) fn encode_disc_unique(src: &[u8], dest: &mut [u8]) {
92    assert!(
93        src.len() * 2 <= dest.len(),
94        "Dest has to be twice the size of src."
95    );
96
97    for (index, byte) in src.iter().enumerate() {
98        dest[index * 2] = byte | 0xAA;
99        dest[index * 2 + 1] = byte | 0x55;
100    }
101}