roco_z21_driver/messages/
loco_state.rs1use 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 pub address: u16,
16 pub is_busy: Option<bool>,
18 pub stepping: Option<DccThrottleSteps>,
20 pub speed_percentage: Option<f64>,
23 pub double_traction: Option<bool>,
25 pub smart_search: Option<bool>,
27 pub functions: Option<[bool; 32]>,
29}
30impl TryFrom<&[u8]> for LocoState {
31 type Error = io::Error;
32
33 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 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 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 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}