Skip to main content

j1939_async/
process_data.rs

1//const FILE_CODE: u8 = 0xFB;
2
3#[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, // Or Unknown if in PD ACK
19}
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..8] = pd.value.to_ne_bytes();
134
135        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 mut ret = [0u8; 8];
147
148        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/*/
221#[repr(u8)]
222#[derive(Copy, Clone, PartialEq, Eq, defmt::Format)]
223pub enum HandlePdValue {
224    NotHandled = 0,
225    Handled = 1,
226    HandledWithResponse(ProcessData),
227}
228
229impl HandlePdValue {
230    pub fn is_handled(&self) -> bool {
231        match self {
232            HandlePdValue::Handled | HandlePdValue::HandledWithResponse(_) => true,
233            _ => false,
234        }
235    }
236}
237*/
238
239#[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
290// Read only
291pub 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            //ProcessData::new(Command::Value, pd.element_number, pd.ddi, *value),
303        )),
304        _ => Ok(PdReturn::new_with_response(
305            true,
306            false,
307            pd.ack(ErrorCodes::new().set_command_not_supported()),
308        )),
309    }
310}
311
312// Read only
313pub 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            //ProcessData::new(Command::Value, pd.element_number, pd.ddi, *value),
344        )),
345        _ => handle_pd_value(pd, value),
346    }
347}
348
349pub const DDI_PROP_TOTAL_CHARGE: Ddi = 0xe000; // micro-amp-hours
350pub const DDI_PROP_CURRENT: Ddi = 0xe001; // micro-amp
351pub const DDI_PROP_VOLTAGE: Ddi = 0xe002; // micro-volts
352pub const DDI_PROP_SHUNT_RESISTANCE: Ddi = 0xe003; // micro-ohms
353pub const DDI_PROP_SHUNT_OFFSET: Ddi = 0xe004; // ADC counts (2.5 μV)
354
355pub const DDI_PROP_TEMPERATURE: Ddi = 0xe010; // micro-degrees
356pub const DDI_PROP_HUMIDITY: Ddi = 0xe011; // ppm
357pub const DDI_PROP_PRESSURE: Ddi = 0xe012; // milli-pascals