1#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
4pub enum Command {
5 TechnicalCapabilities = 0,
6 DDOPTransfer = 1,
7 RequestValue = 2,
8 Value = 3,
9 MeasurementTimeInterval = 4,
10 MeasuermentDistanceInterval = 5,
11 MeasurementMinumum = 6,
12 MeasurementMaximum = 7,
13 MeasurementChange = 8,
14 PeerControlAssignment = 9,
15 SetValueWithAcknowledgment = 0xa,
16 ProcessDataAcknowledgment = 0xd,
17 ServerStatus = 0xe,
18 ClientTask = 0xf, }
20
21pub type ElementNumber = u16;
22pub type Ddi = u16;
23type Value = i32;
24
25#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
26pub struct ErrorCodes(i32);
27
28impl ErrorCodes {
29 pub fn new() -> Self {
30 ErrorCodes(0xFFFF_FF00u32 as i32)
31 }
32
33 pub fn set_command_not_supported(self) -> Self {
34 Self(self.0 | 0x01)
35 }
36 pub fn set_invalid_element_number(self) -> Self {
37 Self(self.0 | 0x02)
38 }
39 pub fn set_ddi_not_supported(self) -> Self {
40 Self(self.0 | 0x04)
41 }
42 pub fn set_trigger_not_supported(self) -> Self {
43 Self(self.0 | 0x08)
44 }
45 pub fn set_not_settable(self) -> Self {
46 Self(self.0 | 0x10)
47 }
48 pub fn set_invalid_threshold(self) -> Self {
49 Self(self.0 | 0x20)
50 }
51 pub fn set_not_conform_to_ddi(self) -> Self {
52 Self(self.0 | 0x40)
53 }
54 pub fn set_value_out_of_range(self) -> Self {
55 Self(self.0 | 0x80)
56 }
57 pub fn set_command(self, command: Command) -> Self {
58 Self((self.0 & 0xFFFF_00FFu32 as i32) | ((command as i32) & 0xFF) << 8)
59 }
60 pub fn get_command(&self) -> Command {
61 match self.0 & 0xFF {
62 0 => Command::TechnicalCapabilities,
63 1 => Command::DDOPTransfer,
64 2 => Command::RequestValue,
65 3 => Command::Value,
66 4 => Command::MeasurementTimeInterval,
67 5 => Command::MeasuermentDistanceInterval,
68 6 => Command::MeasurementMinumum,
69 7 => Command::MeasurementMaximum,
70 8 => Command::MeasurementChange,
71 9 => Command::PeerControlAssignment,
72 10 => Command::SetValueWithAcknowledgment,
73 13 => Command::ProcessDataAcknowledgment,
74 14 => Command::ServerStatus,
75 15 => Command::ClientTask,
76 _ => Command::TechnicalCapabilities,
77 }
78 }
79 pub fn get_command_not_supported(&self) -> bool {
80 (self.0 & 0x01) != 0
81 }
82 pub fn get_invalid_element_number(&self) -> bool {
83 (self.0 & 0x02) != 0
84 }
85 pub fn get_ddi_not_supported(&self) -> bool {
86 (self.0 & 0x04) != 0
87 }
88 pub fn get_trigger_not_supported(&self) -> bool {
89 (self.0 & 0x08) != 0
90 }
91 pub fn get_not_settable(&self) -> bool {
92 (self.0 & 0x10) != 0
93 }
94 pub fn get_invalid_threshold(&self) -> bool {
95 (self.0 & 0x20) != 0
96 }
97 pub fn get_not_conform_to_ddi(&self) -> bool {
98 (self.0 & 0x40) != 0
99 }
100 pub fn get_value_out_of_range(&self) -> bool {
101 (self.0 & 0x80) != 0
102 }
103 pub fn get(&self) -> i32 {
104 self.0
105 }
106 pub fn set(&mut self, code: i32) {
107 self.0 = code;
108 }
109 pub fn get_error_code(&self) -> u8 {
110 (self.0 & 0xFF) as u8
111 }
112}
113
114#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
115pub struct ProcessData {
116 pub command: Command,
117 pub element_number: ElementNumber,
118 pub ddi: Ddi,
119 pub value: Value,
120}
121
122impl From<&ProcessData> for [u8; 8] {
123 fn from(pd: &ProcessData) -> Self {
124 let mut ret = [0u8; 8];
125
126 let cmd = (pd.command as u8) & 0x0f;
127 let eno = pd.element_number & 0xfff;
128 ret[0] = cmd | (((eno << 4) & 0xf0) as u8);
129 ret[1] = (eno >> 4) as u8;
130 ret[2] = (pd.ddi >> 0) as u8;
131 ret[3] = (pd.ddi >> 8) as u8;
132
133 ret[4] = (pd.value >> 0) as u8;
136 ret[5] = (pd.value >> 8) as u8;
137 ret[6] = (pd.value >> 16) as u8;
138 ret[7] = (pd.value >> 24) as u8;
139
140 ret
141 }
142}
143
144impl From<&[u8]> for ProcessData {
145 fn from(data: &[u8]) -> Self {
146 let command = match data[0] & 0x0f {
149 0 => Command::TechnicalCapabilities,
150 1 => Command::DDOPTransfer,
151 2 => Command::RequestValue,
152 3 => Command::Value,
153 4 => Command::MeasurementTimeInterval,
154 5 => Command::MeasuermentDistanceInterval,
155 6 => Command::MeasurementMinumum,
156 7 => Command::MeasurementMaximum,
157 8 => Command::MeasurementChange,
158 9 => Command::PeerControlAssignment,
159 10 => Command::SetValueWithAcknowledgment,
160 13 => Command::ProcessDataAcknowledgment,
161 14 => Command::ServerStatus,
162 15 => Command::ClientTask,
163 _ => Command::TechnicalCapabilities,
164 };
165
166 let element_number: ElementNumber = ((data[0] as u16) >> 4) | ((data[1] as u16) << 4);
167
168 let ddi = Ddi::from_le_bytes(data[2..4].try_into().unwrap());
169 let value = i32::from_le_bytes(data[4..8].try_into().unwrap());
170
171 ProcessData {
172 command,
173 element_number,
174 ddi,
175 value,
176 }
177 }
178}
179
180impl ProcessData {
181 pub fn new(command: Command, element_number: ElementNumber, ddi: Ddi, value: Value) -> Self {
182 ProcessData {
183 command,
184 element_number,
185 ddi,
186 value,
187 }
188 }
189
190 pub fn ack(&self, errors: ErrorCodes) -> Self {
191 ProcessData {
192 command: Command::ProcessDataAcknowledgment,
193 element_number: self.element_number,
194 ddi: self.ddi,
195 value: errors.set_command(self.command).get(),
196 }
197 }
198
199 pub fn response(&self, value: i32) -> Self {
200 ProcessData {
201 command: Command::ProcessDataAcknowledgment,
202 element_number: self.element_number,
203 ddi: self.ddi,
204 value,
205 }
206 }
207
208 pub fn from_data(data: &[u8]) -> Option<Self> {
209 if data.len() < 8 {
210 return None;
211 }
212 Some(data.into())
213 }
214
215 pub fn data(&self) -> [u8; 8] {
216 self.into()
217 }
218}
219
220#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
240pub struct PdReturn {
241 handled: bool,
242 changed: bool,
243 response: Option<ProcessData>,
244}
245
246impl PdReturn {
247 pub fn new_not_handled() -> Self {
248 PdReturn {
249 handled: false,
250 changed: false,
251 response: None,
252 }
253 }
254 pub fn new_unchanged() -> Self {
255 PdReturn {
256 handled: true,
257 changed: false,
258 response: None,
259 }
260 }
261
262 pub fn new_changed() -> Self {
263 PdReturn {
264 handled: true,
265 changed: true,
266 response: None,
267 }
268 }
269
270 pub fn new_with_response(handled: bool, changed: bool, response: ProcessData) -> Self {
271 PdReturn {
272 handled,
273 changed,
274 response: Some(response),
275 }
276 }
277 pub fn handled(&self) -> bool {
278 self.handled
279 }
280 pub fn changed(&self) -> bool {
281 self.changed
282 }
283 pub fn response(&self) -> Option<ProcessData> {
284 self.response
285 }
286}
287
288pub type HandlePdResult = Result<PdReturn, crate::error::Error>;
289
290pub fn handle_pd_value(pd: &ProcessData, value: &i32) -> HandlePdResult {
292 match pd.command {
293 Command::Value | Command::SetValueWithAcknowledgment => Ok(PdReturn::new_with_response(
294 true,
295 false,
296 pd.ack(ErrorCodes::new().set_command_not_supported()),
297 )),
298 Command::RequestValue => Ok(PdReturn::new_with_response(
299 true,
300 false,
301 pd.response(*value),
302 )),
304 _ => Ok(PdReturn::new_with_response(
305 true,
306 false,
307 pd.ack(ErrorCodes::new().set_command_not_supported()),
308 )),
309 }
310}
311
312pub fn handle_settable_pd_value(pd: &ProcessData, value: &mut i32) -> HandlePdResult {
314 match pd.command {
315 Command::Value => {
316 defmt::println!("Value");
317 if *value == pd.value {
318 Ok(PdReturn::new_unchanged())
319 } else {
320 *value = pd.value;
321 Ok(PdReturn::new_changed())
322 }
323 }
324 Command::SetValueWithAcknowledgment => {
325 defmt::println!("SetValue");
326 let changed: bool;
327 if *value == pd.value {
328 changed = false;
329 } else {
330 *value = pd.value;
331 changed = true;
332 }
333 Ok(PdReturn::new_with_response(
334 true,
335 changed,
336 pd.ack(ErrorCodes::new()),
337 ))
338 }
339 Command::RequestValue => Ok(PdReturn::new_with_response(
340 true,
341 false,
342 pd.response(*value),
343 )),
345 _ => handle_pd_value(pd, value),
346 }
347}
348
349pub const DDI_PROP_TOTAL_CHARGE: Ddi = 0xe000; pub const DDI_PROP_CURRENT: Ddi = 0xe001; pub const DDI_PROP_VOLTAGE: Ddi = 0xe002; pub const DDI_PROP_SHUNT_RESISTANCE: Ddi = 0xe003; pub const DDI_PROP_SHUNT_OFFSET: Ddi = 0xe004; pub const DDI_PROP_TEMPERATURE: Ddi = 0xe010; pub const DDI_PROP_HUMIDITY: Ddi = 0xe011; pub const DDI_PROP_PRESSURE: Ddi = 0xe012;