1use bytes::{Buf, BufMut, BytesMut};
7
8use crate::address::{AddressType, GroupAddress, IndividualAddress};
9use crate::error::{KnxError, KnxResult};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17#[repr(u8)]
18pub enum MessageCode {
19 LDataReq = 0x11,
21 LDataCon = 0x2E,
23 LDataInd = 0x29,
25 LBusmonInd = 0x2B,
27 LRawReq = 0x10,
29 LRawInd = 0x2D,
31 LRawCon = 0x2F,
33 MPropReadReq = 0xFC,
35 MPropReadCon = 0xFB,
37 MPropWriteReq = 0xF6,
39 MPropWriteCon = 0xF5,
41 MResetReq = 0xF1,
43 MResetInd = 0xF0,
45}
46
47impl MessageCode {
48 pub fn try_from_u8(value: u8) -> Option<Self> {
50 match value {
51 0x11 => Some(Self::LDataReq),
52 0x2E => Some(Self::LDataCon),
53 0x29 => Some(Self::LDataInd),
54 0x2B => Some(Self::LBusmonInd),
55 0x10 => Some(Self::LRawReq),
56 0x2D => Some(Self::LRawInd),
57 0x2F => Some(Self::LRawCon),
58 0xFC => Some(Self::MPropReadReq),
59 0xFB => Some(Self::MPropReadCon),
60 0xF6 => Some(Self::MPropWriteReq),
61 0xF5 => Some(Self::MPropWriteCon),
62 0xF1 => Some(Self::MResetReq),
63 0xF0 => Some(Self::MResetInd),
64 _ => None,
65 }
66 }
67
68 pub fn is_data(&self) -> bool {
70 matches!(self, Self::LDataReq | Self::LDataCon | Self::LDataInd)
71 }
72
73 pub fn is_request(&self) -> bool {
75 matches!(
76 self,
77 Self::LDataReq
78 | Self::LRawReq
79 | Self::MPropReadReq
80 | Self::MPropWriteReq
81 | Self::MResetReq
82 )
83 }
84
85 pub fn is_indication(&self) -> bool {
87 matches!(
88 self,
89 Self::LDataInd | Self::LBusmonInd | Self::LRawInd | Self::MResetInd
90 )
91 }
92
93 pub fn is_confirmation(&self) -> bool {
95 matches!(
96 self,
97 Self::LDataCon | Self::LRawCon | Self::MPropReadCon | Self::MPropWriteCon
98 )
99 }
100
101 pub fn is_property_service(&self) -> bool {
103 matches!(
104 self,
105 Self::MPropReadReq | Self::MPropReadCon | Self::MPropWriteReq | Self::MPropWriteCon
106 )
107 }
108
109 pub fn is_reset_service(&self) -> bool {
111 matches!(self, Self::MResetReq | Self::MResetInd)
112 }
113
114 pub fn to_confirmation(&self) -> Option<Self> {
116 match self {
117 Self::LDataReq => Some(Self::LDataCon),
118 Self::LRawReq => Some(Self::LRawCon),
119 Self::MPropReadReq => Some(Self::MPropReadCon),
120 Self::MPropWriteReq => Some(Self::MPropWriteCon),
121 Self::MResetReq => Some(Self::MResetInd),
122 _ => None,
123 }
124 }
125}
126
127impl From<MessageCode> for u8 {
128 fn from(code: MessageCode) -> Self {
129 code as u8
130 }
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
139#[repr(u8)]
140pub enum Priority {
141 System = 0,
143 #[default]
145 Normal = 1,
146 Urgent = 2,
148 Low = 3,
150}
151
152impl Priority {
153 pub fn from_bits(value: u8) -> Self {
155 match value & 0x03 {
156 0 => Self::System,
157 1 => Self::Normal,
158 2 => Self::Urgent,
159 _ => Self::Low,
160 }
161 }
162
163 pub fn to_bits(self) -> u8 {
165 self as u8
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub enum Apci {
176 GroupValueRead,
178 GroupValueResponse,
180 GroupValueWrite,
182 IndividualAddressWrite,
184 IndividualAddressRead,
186 IndividualAddressResponse,
188 AdcRead,
190 AdcResponse,
192 MemoryRead,
194 MemoryResponse,
196 MemoryWrite,
198 UserMessage,
200 DeviceDescriptorRead,
202 DeviceDescriptorResponse,
204 Restart,
206 Escape,
208 Unknown(u16),
210}
211
212impl Apci {
213 pub fn decode(value: u16) -> Self {
215 let apci = value & 0x03FF;
221
222 match apci {
223 0x0000 => Self::GroupValueRead,
224 0x0040..=0x007F => Self::GroupValueResponse,
225 0x0080..=0x00FF => Self::GroupValueWrite,
226 0x0100..=0x013F => Self::IndividualAddressWrite,
227 0x0140..=0x017F => Self::IndividualAddressRead,
228 0x0180..=0x01BF => Self::IndividualAddressResponse,
229 0x01C0..=0x01FF => Self::AdcRead,
230 0x0200..=0x023F => Self::AdcResponse,
231 0x0240..=0x027F => Self::MemoryRead,
232 0x0280..=0x02BF => Self::MemoryResponse,
233 0x02C0..=0x02FF => Self::MemoryWrite,
234 0x0300..=0x033F => Self::UserMessage,
235 0x0340..=0x037F => Self::DeviceDescriptorRead,
236 0x0380..=0x03BF => Self::DeviceDescriptorResponse,
237 0x03C0..=0x03DF => Self::Restart,
238 0x03E0..=0x03FF => Self::Escape,
239 _ => Self::Unknown(value),
240 }
241 }
242
243 pub fn decode_simple(value: u16) -> Self {
245 let base = value & 0x03C0; match base {
249 0x0000 => Self::GroupValueRead,
250 0x0040 => Self::GroupValueResponse,
251 0x0080 => Self::GroupValueWrite,
252 0x00C0 => Self::GroupValueWrite, 0x0100 => Self::IndividualAddressWrite,
254 0x0140 => Self::IndividualAddressRead,
255 0x0180 => Self::IndividualAddressResponse,
256 0x01C0 => Self::AdcRead,
257 0x0200 => Self::AdcResponse,
258 0x0240 => Self::MemoryRead,
259 0x0280 => Self::MemoryResponse,
260 0x02C0 => Self::MemoryWrite,
261 0x0300 => Self::UserMessage,
262 0x0340 => Self::DeviceDescriptorRead,
263 0x0380 => Self::DeviceDescriptorResponse,
264 0x03C0 => Self::Restart,
265 _ => Self::Unknown(value),
266 }
267 }
268
269 pub fn encode(&self) -> u16 {
271 match self {
272 Self::GroupValueRead => 0x0000,
273 Self::GroupValueResponse => 0x0040,
274 Self::GroupValueWrite => 0x0080,
275 Self::IndividualAddressWrite => 0x0100,
276 Self::IndividualAddressRead => 0x0100,
277 Self::IndividualAddressResponse => 0x0140,
278 Self::AdcRead => 0x0180,
279 Self::AdcResponse => 0x01C0,
280 Self::MemoryRead => 0x0200,
281 Self::MemoryResponse => 0x0240,
282 Self::MemoryWrite => 0x0280,
283 Self::UserMessage => 0x02C0,
284 Self::DeviceDescriptorRead => 0x0300,
285 Self::DeviceDescriptorResponse => 0x0340,
286 Self::Restart => 0x0380,
287 Self::Escape => 0x03C0,
288 Self::Unknown(v) => *v,
289 }
290 }
291
292 pub fn is_group_value(&self) -> bool {
294 matches!(
295 self,
296 Self::GroupValueRead | Self::GroupValueResponse | Self::GroupValueWrite
297 )
298 }
299
300 pub fn is_read(&self) -> bool {
302 matches!(
303 self,
304 Self::GroupValueRead
305 | Self::IndividualAddressRead
306 | Self::AdcRead
307 | Self::MemoryRead
308 | Self::DeviceDescriptorRead
309 )
310 }
311
312 pub fn is_write(&self) -> bool {
314 matches!(
315 self,
316 Self::GroupValueWrite | Self::IndividualAddressWrite | Self::MemoryWrite
317 )
318 }
319
320 pub fn is_response(&self) -> bool {
322 matches!(
323 self,
324 Self::GroupValueResponse
325 | Self::IndividualAddressResponse
326 | Self::AdcResponse
327 | Self::MemoryResponse
328 | Self::DeviceDescriptorResponse
329 )
330 }
331}
332
333#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339#[repr(u8)]
340pub enum AdditionalInfoType {
341 PlMediumInfo = 0x01,
343 RfMediumInfo = 0x02,
345 BusMonitorInfo = 0x03,
347 TimestampRelative = 0x04,
349 TimeDelayUntil = 0x05,
351 ExtendedTimestamp = 0x06,
353 BiBatInfo = 0x07,
355 RfMultiInfo = 0x08,
357 PreamblePostamble = 0x09,
359 RfFastAckInfo = 0x0A,
361 ManufacturerData = 0xFE,
363}
364
365#[derive(Debug, Clone)]
367pub struct AdditionalInfo {
368 pub info_type: u8,
369 pub data: Vec<u8>,
370}
371
372impl AdditionalInfo {
373 pub fn new(info_type: u8, data: Vec<u8>) -> Self {
375 Self { info_type, data }
376 }
377
378 pub fn encode(&self, buf: &mut BytesMut) {
380 buf.put_u8(self.info_type);
381 buf.put_u8(self.data.len() as u8);
382 buf.put_slice(&self.data);
383 }
384
385 pub fn encoded_len(&self) -> usize {
387 2 + self.data.len()
388 }
389
390 pub fn decode(buf: &mut &[u8]) -> KnxResult<Self> {
392 if buf.len() < 2 {
393 return Err(KnxError::frame_too_short(2, buf.len()));
394 }
395
396 let info_type = buf.get_u8();
397 let length = buf.get_u8() as usize;
398
399 if buf.len() < length {
400 return Err(KnxError::frame_too_short(length, buf.len()));
401 }
402
403 let data = buf[..length].to_vec();
404 *buf = &buf[length..];
405
406 Ok(Self { info_type, data })
407 }
408}
409
410#[derive(Debug, Clone)]
416pub struct CemiFrame {
417 pub message_code: MessageCode,
419 pub additional_info: Vec<AdditionalInfo>,
421 pub source: IndividualAddress,
423 pub destination: u16,
425 pub address_type: AddressType,
427 pub hop_count: u8,
429 pub priority: Priority,
431 pub confirm: bool,
433 pub ack_request: bool,
435 pub system_broadcast: bool,
437 pub apci: Apci,
439 pub data: Vec<u8>,
441}
442
443impl CemiFrame {
444 pub fn group_value_write(
446 source: IndividualAddress,
447 destination: GroupAddress,
448 data: Vec<u8>,
449 ) -> Self {
450 Self {
451 message_code: MessageCode::LDataInd,
452 additional_info: Vec::new(),
453 source,
454 destination: destination.raw(),
455 address_type: AddressType::Group,
456 hop_count: 6,
457 priority: Priority::Low,
458 confirm: false,
459 ack_request: false,
460 system_broadcast: false,
461 apci: Apci::GroupValueWrite,
462 data,
463 }
464 }
465
466 pub fn group_value_read(source: IndividualAddress, destination: GroupAddress) -> Self {
468 Self {
469 message_code: MessageCode::LDataInd,
470 additional_info: Vec::new(),
471 source,
472 destination: destination.raw(),
473 address_type: AddressType::Group,
474 hop_count: 6,
475 priority: Priority::Low,
476 confirm: false,
477 ack_request: false,
478 system_broadcast: false,
479 apci: Apci::GroupValueRead,
480 data: Vec::new(),
481 }
482 }
483
484 pub fn group_value_response(
486 source: IndividualAddress,
487 destination: GroupAddress,
488 data: Vec<u8>,
489 ) -> Self {
490 Self {
491 message_code: MessageCode::LDataInd,
492 additional_info: Vec::new(),
493 source,
494 destination: destination.raw(),
495 address_type: AddressType::Group,
496 hop_count: 6,
497 priority: Priority::Low,
498 confirm: false,
499 ack_request: false,
500 system_broadcast: false,
501 apci: Apci::GroupValueResponse,
502 data,
503 }
504 }
505
506 pub fn bus_monitor_indication(raw_frame: &[u8], status: u8, timestamp_ms: u16) -> Self {
513 let mut additional_info = Vec::new();
514
515 additional_info.push(AdditionalInfo::new(
517 AdditionalInfoType::BusMonitorInfo as u8,
518 vec![status],
519 ));
520
521 additional_info.push(AdditionalInfo::new(
523 AdditionalInfoType::TimestampRelative as u8,
524 timestamp_ms.to_be_bytes().to_vec(),
525 ));
526
527 Self {
528 message_code: MessageCode::LBusmonInd,
529 additional_info,
530 source: IndividualAddress::new(0, 0, 0),
531 destination: 0,
532 address_type: AddressType::Group,
533 hop_count: 7,
534 priority: Priority::Low,
535 confirm: false,
536 ack_request: false,
537 system_broadcast: false,
538 apci: Apci::Unknown(0),
539 data: raw_frame.to_vec(),
540 }
541 }
542
543 pub fn bus_monitor_indication_ext(
545 raw_frame: &[u8],
546 status: u8,
547 timestamp_ms: u16,
548 timestamp_us: u32,
549 ) -> Self {
550 let mut frame = Self::bus_monitor_indication(raw_frame, status, timestamp_ms);
551
552 frame.additional_info.push(AdditionalInfo::new(
554 AdditionalInfoType::ExtendedTimestamp as u8,
555 timestamp_us.to_be_bytes().to_vec(),
556 ));
557
558 frame
559 }
560
561 pub fn prop_read_con(
567 object_index: u16,
568 property_id: u16,
569 count: u8,
570 start_index: u8,
571 value: Vec<u8>,
572 ) -> Self {
573 let mut data = Vec::with_capacity(6 + value.len());
574 data.extend_from_slice(&object_index.to_be_bytes());
575 data.extend_from_slice(&property_id.to_be_bytes());
576 let count_index = ((count as u16) << 12) | (start_index as u16 & 0x0FFF);
578 data.extend_from_slice(&count_index.to_be_bytes());
579 data.extend(value);
580
581 Self {
582 message_code: MessageCode::MPropReadCon,
583 additional_info: Vec::new(),
584 source: IndividualAddress::new(0, 0, 0),
585 destination: 0,
586 address_type: AddressType::Individual,
587 hop_count: 7,
588 priority: Priority::System,
589 confirm: false,
590 ack_request: false,
591 system_broadcast: false,
592 apci: Apci::Unknown(0),
593 data,
594 }
595 }
596
597 pub fn prop_write_con(
599 object_index: u16,
600 property_id: u16,
601 count: u8,
602 start_index: u8,
603 success: bool,
604 ) -> Self {
605 let mut data = Vec::with_capacity(6);
606 data.extend_from_slice(&object_index.to_be_bytes());
607 data.extend_from_slice(&property_id.to_be_bytes());
608 let actual_count = if success { count } else { 0 };
610 let count_index = ((actual_count as u16) << 12) | (start_index as u16 & 0x0FFF);
611 data.extend_from_slice(&count_index.to_be_bytes());
612
613 Self {
614 message_code: MessageCode::MPropWriteCon,
615 additional_info: Vec::new(),
616 source: IndividualAddress::new(0, 0, 0),
617 destination: 0,
618 address_type: AddressType::Individual,
619 hop_count: 7,
620 priority: Priority::System,
621 confirm: !success,
622 ack_request: false,
623 system_broadcast: false,
624 apci: Apci::Unknown(0),
625 data,
626 }
627 }
628
629 pub fn reset_ind() -> Self {
631 Self {
632 message_code: MessageCode::MResetInd,
633 additional_info: Vec::new(),
634 source: IndividualAddress::new(0, 0, 0),
635 destination: 0,
636 address_type: AddressType::Individual,
637 hop_count: 7,
638 priority: Priority::System,
639 confirm: false,
640 ack_request: false,
641 system_broadcast: false,
642 apci: Apci::Unknown(0),
643 data: Vec::new(),
644 }
645 }
646
647 pub fn parse_property_request(&self) -> Option<(u16, u16, u8, u8, &[u8])> {
651 if self.data.len() < 6 {
652 return None;
653 }
654 let object_index = u16::from_be_bytes([self.data[0], self.data[1]]);
655 let property_id = u16::from_be_bytes([self.data[2], self.data[3]]);
656 let count_index = u16::from_be_bytes([self.data[4], self.data[5]]);
657 let count = (count_index >> 12) as u8;
658 let start_index = (count_index & 0x0FFF) as u8;
659 let remaining = &self.data[6..];
660 Some((object_index, property_id, count, start_index, remaining))
661 }
662
663 pub fn destination_group(&self) -> Option<GroupAddress> {
665 if self.address_type == AddressType::Group {
666 Some(GroupAddress::from_raw(self.destination))
667 } else {
668 None
669 }
670 }
671
672 pub fn destination_individual(&self) -> Option<IndividualAddress> {
674 if self.address_type == AddressType::Individual {
675 Some(IndividualAddress::decode(self.destination))
676 } else {
677 None
678 }
679 }
680
681 pub fn with_destination_group(mut self, addr: GroupAddress) -> Self {
683 self.destination = addr.raw();
684 self.address_type = AddressType::Group;
685 self
686 }
687
688 pub fn with_destination_individual(mut self, addr: IndividualAddress) -> Self {
690 self.destination = addr.encode();
691 self.address_type = AddressType::Individual;
692 self
693 }
694
695 pub fn with_priority(mut self, priority: Priority) -> Self {
697 self.priority = priority;
698 self
699 }
700
701 pub fn with_hop_count(mut self, hop_count: u8) -> Self {
703 self.hop_count = hop_count.min(7);
704 self
705 }
706
707 pub fn encode(&self) -> Vec<u8> {
709 let mut buf = BytesMut::new();
710
711 buf.put_u8(self.message_code.into());
713
714 let add_info_len: usize = self.additional_info.iter().map(|i| i.encoded_len()).sum();
716 buf.put_u8(add_info_len as u8);
717
718 for info in &self.additional_info {
720 info.encode(&mut buf);
721 }
722
723 if self.message_code.is_property_service() || self.message_code.is_reset_service() {
727 buf.put_slice(&self.data);
728 return buf.to_vec();
729 }
730
731 if self.message_code == MessageCode::LBusmonInd {
734 buf.put_slice(&self.data);
735 return buf.to_vec();
736 }
737
738 let ctrl1 = 0x80 | (if self.ack_request { 0x00 } else { 0x20 }) | (if !self.confirm { 0x00 } else { 0x01 }) | ((self.priority.to_bits() & 0x03) << 2); buf.put_u8(ctrl1);
745
746 let ctrl2 = (if self.address_type == AddressType::Group {
748 0x80
749 } else {
750 0x00
751 }) | (self.hop_count & 0x07);
752 buf.put_u8(ctrl2);
753
754 buf.put_u16(self.source.encode());
756
757 buf.put_u16(self.destination);
759
760 let apci = self.apci.encode();
762
763 let apci_byte = apci as u8;
765
766 if self.data.is_empty() {
767 buf.put_u8(1); buf.put_u8(apci_byte);
770 } else if self.data.len() == 1 && self.data[0] <= 0x3F {
771 buf.put_u8(1); buf.put_u8(apci_byte | (self.data[0] & 0x3F));
774 } else {
775 buf.put_u8((self.data.len() + 1) as u8);
777 buf.put_u8(apci_byte);
778 buf.put_slice(&self.data);
779 }
780
781 buf.to_vec()
782 }
783
784 pub fn decode(data: &[u8]) -> KnxResult<Self> {
786 if data.len() < 2 {
787 return Err(KnxError::frame_too_short(2, data.len()));
788 }
789
790 let mut buf = data;
791
792 let message_code = MessageCode::try_from_u8(buf.get_u8())
794 .ok_or_else(|| KnxError::UnknownMessageCode(data[0]))?;
795
796 let add_info_len = buf.get_u8() as usize;
798 let mut additional_info = Vec::new();
799
800 if add_info_len > 0 {
801 if buf.len() < add_info_len {
802 return Err(KnxError::frame_too_short(add_info_len, buf.len()));
803 }
804 let mut add_info_buf = &buf[..add_info_len];
805 while !add_info_buf.is_empty() {
806 additional_info.push(AdditionalInfo::decode(&mut add_info_buf)?);
807 }
808 buf = &buf[add_info_len..];
809 }
810
811 if message_code.is_property_service() || message_code.is_reset_service() {
813 return Ok(Self {
814 message_code,
815 additional_info,
816 source: IndividualAddress::new(0, 0, 0),
817 destination: 0,
818 address_type: AddressType::Individual,
819 hop_count: 7,
820 priority: Priority::System,
821 confirm: false,
822 ack_request: false,
823 system_broadcast: false,
824 apci: Apci::Unknown(0),
825 data: buf.to_vec(),
826 });
827 }
828
829 if message_code == MessageCode::LBusmonInd {
831 return Ok(Self {
832 message_code,
833 additional_info,
834 source: IndividualAddress::new(0, 0, 0),
835 destination: 0,
836 address_type: AddressType::Group,
837 hop_count: 7,
838 priority: Priority::Low,
839 confirm: false,
840 ack_request: false,
841 system_broadcast: false,
842 apci: Apci::Unknown(0),
843 data: buf.to_vec(),
844 });
845 }
846
847 if buf.len() < 7 {
849 return Err(KnxError::frame_too_short(7, buf.len()));
850 }
851
852 let ctrl1 = buf.get_u8();
854 let ctrl2 = buf.get_u8();
855
856 let priority = Priority::from_bits((ctrl1 >> 2) & 0x03);
857 let ack_request = ctrl1 & 0x20 == 0;
858 let confirm = ctrl1 & 0x01 != 0;
859 let system_broadcast = ctrl1 & 0x10 != 0;
860
861 let address_type = AddressType::from_bit(ctrl2 & 0x80 != 0);
862 let hop_count = ctrl2 & 0x07;
863
864 let source = IndividualAddress::decode(buf.get_u16());
866 let destination = buf.get_u16();
867
868 let npdu_len = buf.get_u8() as usize;
871 if buf.len() < npdu_len {
872 return Err(KnxError::frame_too_short(npdu_len, buf.len()));
873 }
874
875 let apci_byte1 = buf.get_u8();
880 let (apci, frame_data) = if npdu_len >= 2 && (apci_byte1 & 0xFC) == 0 {
881 let apci_byte2 = buf.get_u8();
882 let apci_raw = (((apci_byte1 & 0x03) as u16) << 8) | apci_byte2 as u16;
883 let apci = Apci::decode(apci_raw);
884 let remaining_len = npdu_len - 2;
885 let frame_data = if remaining_len == 0 {
886 let small = apci_byte2 & 0x3F;
887 if small != 0 {
888 vec![small]
889 } else {
890 Vec::new()
891 }
892 } else {
893 buf[..remaining_len].to_vec()
894 };
895 (apci, frame_data)
896 } else {
897 let apci = Apci::decode(apci_byte1 as u16);
899 let frame_data = if npdu_len <= 1 {
900 let small = apci_byte1 & 0x3F;
901 if small != 0 {
902 vec![small]
903 } else {
904 Vec::new()
905 }
906 } else {
907 buf[..npdu_len - 1].to_vec()
908 };
909 (apci, frame_data)
910 };
911
912 Ok(Self {
913 message_code,
914 additional_info,
915 source,
916 destination,
917 address_type,
918 hop_count,
919 priority,
920 confirm,
921 ack_request,
922 system_broadcast,
923 apci,
924 data: frame_data,
925 })
926 }
927}
928
929#[cfg(test)]
930mod tests {
931 use super::*;
932
933 #[test]
934 fn test_message_code() {
935 assert_eq!(MessageCode::try_from_u8(0x11), Some(MessageCode::LDataReq));
936 assert_eq!(MessageCode::try_from_u8(0x29), Some(MessageCode::LDataInd));
937 assert!(MessageCode::LDataReq.is_data());
938 assert!(MessageCode::LDataReq.is_request());
939 }
940
941 #[test]
942 fn test_priority() {
943 assert_eq!(Priority::from_bits(0), Priority::System);
944 assert_eq!(Priority::from_bits(3), Priority::Low);
945 assert_eq!(Priority::Normal.to_bits(), 1);
946 }
947
948 #[test]
949 fn test_apci_encode_decode() {
950 let tests = [
951 Apci::GroupValueRead,
952 Apci::GroupValueResponse,
953 Apci::GroupValueWrite,
954 ];
955
956 for apci in tests {
957 let encoded = apci.encode();
958 let decoded = Apci::decode(encoded);
959 assert_eq!(apci, decoded, "Failed for {:?}", apci);
960 }
961 }
962
963 #[test]
964 fn test_cemi_frame_group_value_write() {
965 let frame = CemiFrame::group_value_write(
966 IndividualAddress::new(1, 2, 3),
967 GroupAddress::three_level(1, 2, 3),
968 vec![0x01],
969 );
970
971 assert!(matches!(frame.apci, Apci::GroupValueWrite));
972 assert_eq!(frame.source.to_string(), "1.2.3");
973 assert_eq!(frame.destination_group().unwrap().to_string(), "1/2/3");
974 }
975
976 #[test]
977 fn test_cemi_frame_encode_decode() {
978 let original = CemiFrame::group_value_write(
979 IndividualAddress::new(1, 2, 3),
980 GroupAddress::three_level(1, 2, 100),
981 vec![0x55],
982 );
983
984 let encoded = original.encode();
985 let decoded = CemiFrame::decode(&encoded).unwrap();
986
987 assert!(matches!(decoded.apci, Apci::GroupValueWrite));
988 assert_eq!(decoded.source.to_string(), "1.2.3");
989 assert_eq!(decoded.destination_group().unwrap().to_string(), "1/2/100");
990 assert_eq!(decoded.data, vec![0x55]);
991 }
992
993 #[test]
994 fn test_cemi_decode_external_two_octet_apci() {
995 let decoded = CemiFrame::decode(&[
996 0x11, 0x00, 0xBC, 0xE0, 0x11, 0x65, 0x08, 0x02, 0x03, 0x00, 0x80, 0x80, ])
1007 .unwrap();
1008
1009 assert_eq!(decoded.message_code, MessageCode::LDataReq);
1010 assert!(matches!(decoded.apci, Apci::GroupValueWrite));
1011 assert_eq!(decoded.source.to_string(), "1.1.101");
1012 assert_eq!(decoded.destination_group().unwrap().to_string(), "1/0/2");
1013 assert_eq!(decoded.data, vec![0x80]);
1014 }
1015
1016 #[test]
1017 fn test_cemi_frame_group_value_read() {
1018 let frame = CemiFrame::group_value_read(
1019 IndividualAddress::new(1, 1, 1),
1020 GroupAddress::three_level(1, 0, 1),
1021 );
1022
1023 let encoded = frame.encode();
1024 let decoded = CemiFrame::decode(&encoded).unwrap();
1025
1026 assert!(matches!(decoded.apci, Apci::GroupValueRead));
1027 assert!(decoded.data.is_empty() || decoded.data == vec![0]);
1028 }
1029
1030 #[test]
1031 fn test_message_code_confirmation_mapping() {
1032 assert_eq!(
1033 MessageCode::LDataReq.to_confirmation(),
1034 Some(MessageCode::LDataCon)
1035 );
1036 assert_eq!(
1037 MessageCode::LRawReq.to_confirmation(),
1038 Some(MessageCode::LRawCon)
1039 );
1040 assert_eq!(
1041 MessageCode::MPropReadReq.to_confirmation(),
1042 Some(MessageCode::MPropReadCon)
1043 );
1044 assert_eq!(
1045 MessageCode::MPropWriteReq.to_confirmation(),
1046 Some(MessageCode::MPropWriteCon)
1047 );
1048 assert_eq!(
1049 MessageCode::MResetReq.to_confirmation(),
1050 Some(MessageCode::MResetInd)
1051 );
1052 assert_eq!(MessageCode::LDataCon.to_confirmation(), None);
1053 assert_eq!(MessageCode::LDataInd.to_confirmation(), None);
1054 }
1055
1056 #[test]
1057 fn test_message_code_categories() {
1058 assert!(MessageCode::LDataCon.is_confirmation());
1059 assert!(MessageCode::LRawCon.is_confirmation());
1060 assert!(MessageCode::MPropReadCon.is_confirmation());
1061 assert!(!MessageCode::LDataReq.is_confirmation());
1062
1063 assert!(MessageCode::MPropReadReq.is_property_service());
1064 assert!(MessageCode::MPropWriteReq.is_property_service());
1065 assert!(MessageCode::MPropReadCon.is_property_service());
1066 assert!(!MessageCode::LDataReq.is_property_service());
1067
1068 assert!(MessageCode::MResetReq.is_reset_service());
1069 assert!(MessageCode::MResetInd.is_reset_service());
1070 assert!(!MessageCode::LDataReq.is_reset_service());
1071 }
1072
1073 #[test]
1074 fn test_bus_monitor_indication() {
1075 let raw_frame = vec![0x29, 0x00, 0xBC, 0x11, 0x01, 0x09, 0x01, 0x00, 0x80, 0x01];
1076 let frame = CemiFrame::bus_monitor_indication(&raw_frame, 0x00, 150);
1077
1078 assert_eq!(frame.message_code, MessageCode::LBusmonInd);
1079 assert_eq!(frame.additional_info.len(), 2);
1080
1081 assert_eq!(
1083 frame.additional_info[0].info_type,
1084 AdditionalInfoType::BusMonitorInfo as u8
1085 );
1086 assert_eq!(frame.additional_info[0].data, vec![0x00]);
1087
1088 assert_eq!(
1090 frame.additional_info[1].info_type,
1091 AdditionalInfoType::TimestampRelative as u8
1092 );
1093 assert_eq!(frame.additional_info[1].data, vec![0x00, 0x96]); assert_eq!(frame.data, raw_frame);
1097
1098 let encoded = frame.encode();
1100 let decoded = CemiFrame::decode(&encoded).unwrap();
1101 assert_eq!(decoded.message_code, MessageCode::LBusmonInd);
1102 assert_eq!(decoded.additional_info.len(), 2);
1103 assert_eq!(decoded.data, raw_frame);
1104 }
1105
1106 #[test]
1107 fn test_bus_monitor_indication_ext() {
1108 let raw_frame = vec![0x29, 0x00, 0xBC];
1109 let frame = CemiFrame::bus_monitor_indication_ext(&raw_frame, 0x80, 100, 123456);
1110
1111 assert_eq!(frame.additional_info.len(), 3);
1112 assert_eq!(
1114 frame.additional_info[2].info_type,
1115 AdditionalInfoType::ExtendedTimestamp as u8
1116 );
1117 let ts_bytes = &frame.additional_info[2].data;
1118 let ts = u32::from_be_bytes([ts_bytes[0], ts_bytes[1], ts_bytes[2], ts_bytes[3]]);
1119 assert_eq!(ts, 123456);
1120 }
1121
1122 #[test]
1123 fn test_prop_read_con() {
1124 let frame = CemiFrame::prop_read_con(0, 11, 1, 1, vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
1125 assert_eq!(frame.message_code, MessageCode::MPropReadCon);
1126
1127 let parsed = frame.parse_property_request().unwrap();
1129 assert_eq!(parsed.0, 0); assert_eq!(parsed.1, 11); assert_eq!(parsed.2, 1); assert_eq!(parsed.3, 1); assert_eq!(parsed.4, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); let encoded = frame.encode();
1137 let decoded = CemiFrame::decode(&encoded).unwrap();
1138 assert_eq!(decoded.message_code, MessageCode::MPropReadCon);
1139 assert_eq!(decoded.data, frame.data);
1140 }
1141
1142 #[test]
1143 fn test_prop_write_con_success() {
1144 let frame = CemiFrame::prop_write_con(0, 14, 1, 1, true);
1145 assert_eq!(frame.message_code, MessageCode::MPropWriteCon);
1146 assert!(!frame.confirm); let parsed = frame.parse_property_request().unwrap();
1149 assert_eq!(parsed.2, 1); }
1151
1152 #[test]
1153 fn test_prop_write_con_failure() {
1154 let frame = CemiFrame::prop_write_con(0, 14, 1, 1, false);
1155 assert!(frame.confirm); let parsed = frame.parse_property_request().unwrap();
1158 assert_eq!(parsed.2, 0); }
1160
1161 #[test]
1162 fn test_reset_ind() {
1163 let frame = CemiFrame::reset_ind();
1164 assert_eq!(frame.message_code, MessageCode::MResetInd);
1165 assert!(frame.data.is_empty());
1166
1167 let encoded = frame.encode();
1169 let decoded = CemiFrame::decode(&encoded).unwrap();
1170 assert_eq!(decoded.message_code, MessageCode::MResetInd);
1171 }
1172
1173 #[test]
1174 fn test_parse_property_request_too_short() {
1175 let frame = CemiFrame {
1176 message_code: MessageCode::MPropReadReq,
1177 additional_info: Vec::new(),
1178 source: IndividualAddress::new(0, 0, 0),
1179 destination: 0,
1180 address_type: AddressType::Individual,
1181 hop_count: 7,
1182 priority: Priority::System,
1183 confirm: false,
1184 ack_request: false,
1185 system_broadcast: false,
1186 apci: Apci::Unknown(0),
1187 data: vec![0x00, 0x00], };
1189
1190 assert!(frame.parse_property_request().is_none());
1191 }
1192}