roco_z21_driver/messages/
loco_state.rs

1use tokio::io;
2
3use super::XBusMessage;
4
5#[derive(Clone, Copy, Debug)]
6pub enum DccThrottleSteps {
7    Steps14 = 0x10,
8    Steps28 = 0x12,
9    Steps128 = 0x13,
10}
11
12#[derive(Debug, Clone)]
13pub struct LocoState {
14    /// Address of the locomotive.
15    pub address: u16,
16    /// Idicates if another X-Bus controller (like MultiMaus, or other PC) is controlling the loco.
17    pub is_busy: Option<bool>,
18    /// Stepping of throttle
19    pub stepping: Option<DccThrottleSteps>,
20    /// Speed of the locomotive.
21    /// Negative values indicate reverse.
22    pub speed_percentage: Option<f64>,
23    /// Is in double traction mode.
24    pub double_traction: Option<bool>,
25    /// Is in smart search (?)
26    pub smart_search: Option<bool>,
27    /// Functions flag, at index 0 is F0, at index 1 is F1, etc.
28    pub functions: Option<[bool; 32]>,
29}
30impl TryFrom<&[u8]> for LocoState {
31    type Error = io::Error;
32
33    /// Attempts to parse a `LocoState` from a n byte slice
34    ///
35    /// # Errors
36    ///
37    /// Returns an error if the provided slice is not exactly 16 bytes long.
38    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
39        Self::try_from(&XBusMessage::try_from(data)?)
40    }
41}
42
43impl TryFrom<&XBusMessage> for LocoState {
44    type Error = io::Error;
45
46    /// Attempts to parse a `LocoState` from a n byte slice
47    ///
48    /// # Errors
49    ///
50    /// Returns an error if the provided slice is not exactly 16 bytes long.
51    fn try_from(data: &XBusMessage) -> Result<Self, Self::Error> {
52        let data = data.get_dbs();
53        let len = data.len();
54        if len <= 1 {
55            return Err(io::Error::new(
56                io::ErrorKind::InvalidData,
57                "Invalid LocoState data length",
58            ));
59        }
60        // The two highest bits in Adr_MSB must be ignored
61        let addr = [data[0] & 0b00111111, data[1]];
62        let address = u16::from_be_bytes(addr);
63        let mut is_busy = None;
64        let mut stepping = None;
65        let mut speed_percentage = None;
66        let mut double_traction = None;
67        let mut smart_search = None;
68        let mut functions = None;
69
70        if len >= 3 {
71            is_busy = Some((data[2] & 0b0000_1000) != 0);
72            const STEPPING_MASK: u8 = 0b0000_0111;
73            stepping = if (data[2] & STEPPING_MASK) == 0 {
74                Some(DccThrottleSteps::Steps14)
75            } else if (data[2] & STEPPING_MASK) == 2 {
76                Some(DccThrottleSteps::Steps28)
77            } else if (data[2] & STEPPING_MASK) == 4 {
78                Some(DccThrottleSteps::Steps128)
79            } else {
80                return Err(io::Error::new(
81                    io::ErrorKind::InvalidData,
82                    "Invalid coding of DCC Stepping",
83                ));
84            };
85        }
86        if len >= 4 {
87            // To further speed calculation:
88            let is_going_forward = data[3] & 0b1000_0000 != 0;
89
90            let speed = (data[3] & 0b0111_1111) as f64;
91            let speed =
92                match stepping.expect("That could not happen err_code: DCC Stepping is NULL") {
93                    DccThrottleSteps::Steps14 => speed / 14.,
94                    DccThrottleSteps::Steps28 => speed / 28.,
95                    DccThrottleSteps::Steps128 => speed / 128.,
96                };
97
98            speed_percentage = Some((if is_going_forward { speed } else { -speed }) * 100.);
99        }
100        if len >= 5 {
101            double_traction = Some(data[4] & 0b01000000 != 0);
102            smart_search = Some(data[4] & 0b00100000 != 0);
103            let mut functions_array = [false; 32];
104            functions_array[0] = data[4] & 0b0001_0000 != 0;
105            functions_array[4] = data[4] & 0b0000_1000 != 0;
106            functions_array[3] = data[4] & 0b0000_0100 != 0;
107            functions_array[2] = data[4] & 0b0000_0010 != 0;
108            functions_array[1] = data[4] & 0b0000_0001 != 0;
109
110            if len >= 6 {
111                for i in 0..8 {
112                    functions_array[i + 5] = data[5] & (1 << i) != 0;
113                }
114            }
115            if len >= 7 {
116                for i in 0..8 {
117                    functions_array[i + 13] = data[6] & (1 << i) != 0;
118                }
119            }
120            if len >= 8 {
121                for i in 0..8 {
122                    functions_array[i + 21] = data[7] & (1 << i) != 0;
123                }
124            }
125            if len >= 9 {
126                for i in 0..3 {
127                    functions_array[i + 29] = data[8] & (1 << i) != 0;
128                }
129            }
130
131            functions = Some(functions_array);
132        }
133
134        Ok(LocoState {
135            address,
136            is_busy,
137            stepping,
138            speed_percentage,
139            double_traction,
140            smart_search,
141            functions,
142        })
143    }
144}