knx_ip_client/packets/
emi.rs

1use super::{addresses::KnxAddress, tpdu::TPDU};
2use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
3use log::{debug, warn};
4use snafu::{whatever, Whatever};
5use std::io::{Cursor, Read};
6use tracing::instrument;
7
8#[derive(Debug)]
9pub struct LDataCon {
10    pub cemi: CEMI,
11    pub l_data: LData,
12    pub confirm: bool,
13}
14
15impl LDataCon {
16    pub fn from_cemi(cemi: CEMI) -> Result<Self, Whatever> {
17        let mut reader = Cursor::new(cemi.service_info.as_slice());
18        let control1 = match reader.read_u8() {
19            Ok(control1) => control1,
20            Err(e) => whatever!("Unable to read control 1 byte {:?}", e),
21        };
22        let _control2 = match reader.read_u8() {
23            Ok(control2) => control2,
24            Err(e) => whatever!("Unable to read control 2 byte {:?}", e),
25        };
26        let src = match reader.read_u16::<BigEndian>() {
27            Ok(src) => src,
28            Err(e) => whatever!("Unable to read source address {:?}", e),
29        };
30        let dest = match reader.read_u16::<BigEndian>() {
31            Ok(dest) => dest,
32            Err(e) => whatever!("Unable to read destination address {:?}", e),
33        };
34
35        let frame_type = (control1 & 0x80) > 0;
36        let repetition = (control1 & (1 << 5)) > 0;
37        let system_broadcast = (control1 & (1 << 4)) > 0;
38        let ack_request = (control1 & (1 << 1)) > 0;
39        let confirm = (control1 & 1) == 0;
40
41        Ok(Self {
42            cemi,
43            l_data: LData {
44                src,
45                dest,
46                frame_type,
47                repetition,
48                system_broadcast,
49                ack_request,
50            },
51            confirm,
52        })
53    }
54}
55
56#[derive(Debug)]
57pub struct LData {
58    pub src: u16,
59    pub dest: u16,
60    pub frame_type: bool,
61    pub repetition: bool, // 1: not repeated, 0: repeated
62    pub system_broadcast: bool,
63    pub ack_request: bool,
64}
65
66// 03_06_03 EMI IMI section 4.1.5.3.5
67//
68#[derive(Debug)]
69pub struct LDataInd {
70    pub cemi: CEMI,
71    pub l_data: LData,
72    pub value: Vec<u8>,
73}
74
75impl LDataInd {
76    #[instrument]
77    pub fn from_cemi(cemi: CEMI) -> Result<Self, Whatever> {
78        let mut reader = Cursor::new(cemi.service_info.as_slice());
79        let control1 = match reader.read_u8() {
80            Ok(control1) => control1,
81            Err(e) => whatever!("Unable to read control 1 byte {:?}", e),
82        };
83        let _control2 = match reader.read_u8() {
84            Ok(control2) => control2,
85            Err(e) => whatever!("Unable to read control 2 byte {:?}", e),
86        };
87        let src = match reader.read_u16::<BigEndian>() {
88            Ok(src) => src,
89            Err(e) => whatever!("Unable to read source address {:?}", e),
90        };
91        let dest = match reader.read_u16::<BigEndian>() {
92            Ok(dest) => dest,
93            Err(e) => whatever!("Unable to read destination address {:?}", e),
94        };
95        let length = match reader.read_u8() {
96            Ok(len) if len > 0 => len - 1,
97            Ok(_) => 0,
98            Err(e) => whatever!("Unable to read length {:?}", e),
99        };
100        let octects_6_7 = match reader.read_u16::<BigEndian>() {
101            Ok(word) => word,
102            Err(e) => whatever!("Unable to read octets 6 and 7 {:?}", e),
103        };
104
105        let frame_type = (control1 & 0x80) > 0;
106        let repetition = (control1 & (1 << 5)) > 0;
107        let system_broadcast = (control1 & (1 << 4)) > 0;
108        let ack_request = (control1 & (1 << 1)) > 0;
109
110        let apci = (octects_6_7 & 0x03c0) >> 6;
111        debug!("Length {:?}", length);
112        debug!("Octects 6 and 7: {:0x?}", octects_6_7);
113        debug!("Apci: {:0x?}", apci);
114
115        let mut value = vec![0; length as usize];
116        if length > 0 {
117            if let Err(e) = reader.read(&mut value) {
118                whatever!("Unable to read value of length {}, {:?}", length, e);
119            }
120        } else {
121            let data = (octects_6_7 & 0x3f) as u8;
122            value.push(data);
123        }
124        debug!("Value: {:0x?}", value);
125
126        Ok(Self {
127            cemi,
128            l_data: LData {
129                src,
130                dest,
131                frame_type,
132                repetition,
133                system_broadcast,
134                ack_request,
135            },
136            value,
137        })
138    }
139}
140
141// L_Data request message
142// 03.06.03 EMI IMI section 4.1.5.3.3
143#[derive(Debug)]
144pub struct LDataReqMessage {
145    priority: u8,
146    dest_address: KnxAddress,
147    tpdu: TPDU,
148}
149
150impl LDataReqMessage {
151    pub fn new(dest_address: KnxAddress, tpdu: TPDU) -> Self {
152        Self {
153            priority: 0b11,
154            dest_address,
155            tpdu,
156        }
157    }
158
159    pub fn set_priority(&mut self, priority: u8) {
160        self.priority = priority;
161    }
162
163    pub fn packet(&self) -> Vec<u8> {
164        let mut control = 0u8;
165        control |= 1 << 7; // Frame type standard
166        control |= 1 << 5; // No repetition on error
167        control |= 1 << 4; // Domain broadcast
168        control |= (self.priority & 0x3) << 2;
169
170        let control2 = 0xe0u8;
171        let mut packet = vec![CEMIMessageCode::LDataReq as u8, 0, control, control2, 0, 0];
172
173        // packet.write_u16::<BigEndian>(0).unwrap();
174        packet.write_u16::<BigEndian>(self.dest_address.to_u16()).unwrap();
175
176        let mut tpdu_packet = self.tpdu.packet();
177        packet.write_u8(tpdu_packet.len() as u8 - 1).unwrap(); // Count of APCI values
178        packet.append(&mut tpdu_packet);
179
180        packet
181    }
182}
183
184// cEMI
185//
186#[derive(Debug)]
187pub struct CEMI {
188    pub msg_code: u8,
189    pub additional_infos: Vec<CEMIAdditionalInfo>,
190    pub service_info: Vec<u8>,
191}
192
193impl CEMI {
194    #[instrument]
195    pub fn from_packet(packet_reader: &mut Cursor<&[u8]>) -> Result<Self, Whatever> {
196        let msg_code = match packet_reader.read_u8() {
197            Ok(code) => code,
198            Err(e) => whatever!("Unable to read message code {:?}", e),
199        };
200
201        let additional_infos_size = match packet_reader.read_u8() {
202            Ok(size) => size,
203            Err(e) => whatever!("Unable to read addition infos size {:?}", e),
204        };
205
206        let mut additional_infos = Vec::new();
207        let mut position = 0;
208        while position < additional_infos_size {
209            let additional_info_type: CEMIAdditionalInfoType = match packet_reader.read_u8() {
210                Ok(info_type) => match info_type.try_into() {
211                    Ok(t) => t,
212                    Err(e) => whatever!("Unknown additional info type {:?}", e),
213                },
214                Err(e) => whatever!("Unable to read addition info type {:?}", e),
215            };
216            let additional_info_size = match packet_reader.read_u8() {
217                Ok(size) => size,
218                Err(e) => whatever!("Unable to read addition info size {:?}", e),
219            };
220
221            let mut additional_info = vec![0; additional_info_size as usize];
222            if let Err(e) = packet_reader.read(&mut additional_info) {
223                whatever!("Unable to read additional info {:?}", e);
224            }
225
226            additional_infos.push(CEMIAdditionalInfo {
227                info_type: additional_info_type,
228                value: additional_info,
229            });
230            position += additional_info_size + 2;
231        }
232
233        let mut service_info = Vec::new();
234        if let Err(e) = packet_reader.read_to_end(&mut service_info) {
235            whatever!("Unable to read service information {:?}", e);
236        }
237
238        Ok(Self {
239            msg_code,
240            additional_infos,
241            service_info,
242        })
243    }
244}
245
246#[derive(Copy, Clone)]
247#[repr(u8)]
248pub enum CEMIMessageCode {
249    LBusmonInd = 0x2b, // NL
250    LDataReq = 0x11,   // DLL
251    LDataCon = 0x2e,   // NL
252    LDataInd = 0x29,   // NL
253
254    LRawReq = 0x10,      // DLL
255    LRawInd = 0x2d,      // NL
256    LRawCon = 0x2f,      // NL
257    LPollDataReq = 0x13, // DLL
258    LPollDataCon = 0x25, // NL
259
260    TDataConnectedReq = 0x41,
261    TDataConnectedInd = 0x89,
262    TDataIndividualReq = 0x4a,
263    TDataIndividualInd = 0x94,
264
265    MPropReadReq = 0xFC,        // CEMI Management Server
266    MPropReadCon = 0xfb,        // CEMI Management Client
267    MPropWriteReq = 0xf6,       // CEMI Management Client
268    MPropWriteCon = 0xf5,       // CEMI Management Server
269    MPropInfoInd = 0xf7,        // CEMI Management Server
270    MFuncPropCommandReq = 0xf8, // CEMI Management Client
271    MFuncPropStateReq = 0xf9,   // CEMI Management Client
272    MFuncPropCommandCon = 0xfa, // CEMI Management Server
273    MResetReq = 0xf1,           // CEMI Management Client
274    MResetInd = 0xf0,           // CEMI Management Server
275}
276
277#[derive(Debug)]
278pub enum CEMIAdditionalInfoType {
279    PLMediumInfo = 0x01,
280    RFMediumInfo = 0x02,
281    BusmonitorStatusInfo = 0x03,
282    TimestampRelative = 0x04,
283    TimeDelayUnitlSending = 0x05,
284    ExtendedRelativeTime = 0x06,
285    BiBatInfo = 0x07,
286    RFMultiInfo = 0x08,
287    PreambleAndPostamble = 0x09,
288    RFFastAckInfo = 0x0a,
289    ManufacturerSpecificData = 0xfe,
290}
291
292#[derive(Debug)]
293pub struct CEMIAdditionalInfo {
294    pub info_type: CEMIAdditionalInfoType,
295    pub value: Vec<u8>,
296}
297
298impl TryFrom<u8> for CEMIAdditionalInfoType {
299    type Error = ();
300
301    fn try_from(v: u8) -> Result<Self, Self::Error> {
302        match v {
303            x if x == CEMIAdditionalInfoType::PLMediumInfo as u8 => Ok(CEMIAdditionalInfoType::PLMediumInfo),
304            x if x == CEMIAdditionalInfoType::RFMediumInfo as u8 => Ok(CEMIAdditionalInfoType::RFMediumInfo),
305            x if x == CEMIAdditionalInfoType::BusmonitorStatusInfo as u8 => Ok(CEMIAdditionalInfoType::BusmonitorStatusInfo),
306            x if x == CEMIAdditionalInfoType::TimestampRelative as u8 => Ok(CEMIAdditionalInfoType::TimestampRelative),
307            x if x == CEMIAdditionalInfoType::TimeDelayUnitlSending as u8 => Ok(CEMIAdditionalInfoType::TimeDelayUnitlSending),
308            x if x == CEMIAdditionalInfoType::ExtendedRelativeTime as u8 => Ok(CEMIAdditionalInfoType::ExtendedRelativeTime),
309            x if x == CEMIAdditionalInfoType::BiBatInfo as u8 => Ok(CEMIAdditionalInfoType::BiBatInfo),
310            x if x == CEMIAdditionalInfoType::RFMultiInfo as u8 => Ok(CEMIAdditionalInfoType::RFMultiInfo),
311            x if x == CEMIAdditionalInfoType::PreambleAndPostamble as u8 => Ok(CEMIAdditionalInfoType::PreambleAndPostamble),
312            x if x == CEMIAdditionalInfoType::RFFastAckInfo as u8 => Ok(CEMIAdditionalInfoType::RFFastAckInfo),
313            x if x == CEMIAdditionalInfoType::ManufacturerSpecificData as u8 => Ok(CEMIAdditionalInfoType::ManufacturerSpecificData),
314            _ => Err(()),
315        }
316    }
317}
318
319#[cfg(test)]
320pub mod test {
321    use std::io::Cursor;
322
323    use crate::packets::emi::LDataInd;
324
325    use super::CEMI;
326
327    #[test]
328    pub fn bit_update() {
329        env_logger::init();
330        let packet_bytes: [u8; 11] = [0x29, 0x00, 0xbc, 0xe0, 0x11, 0x0b, 0x0a, 0x01, 0x01, 0x00, 0x81];
331        let mut cursor = Cursor::new(&packet_bytes[..]);
332        let cemi = CEMI::from_packet(&mut cursor).expect("It should be able to parse CEMI packet");
333        assert_eq!(0x29, cemi.msg_code, "Message code should be L_Data.ind");
334        assert_eq!(0, cemi.additional_infos.len(), "Additional infos should be empty");
335
336        let ind = LDataInd::from_cemi(cemi).expect("To be able to parse L_Data.ind from cemi");
337        assert_eq!(1, ind.value.len(), "Data value should have length 1");
338    }
339}