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 | Self::LRawReq | Self::MPropReadReq | Self::MPropWriteReq | Self::MResetReq
78 )
79 }
80
81 pub fn is_indication(&self) -> bool {
83 matches!(
84 self,
85 Self::LDataInd | Self::LBusmonInd | Self::LRawInd | Self::MResetInd
86 )
87 }
88
89 pub fn is_confirmation(&self) -> bool {
91 matches!(
92 self,
93 Self::LDataCon | Self::LRawCon | Self::MPropReadCon | Self::MPropWriteCon
94 )
95 }
96
97 pub fn is_property_service(&self) -> bool {
99 matches!(
100 self,
101 Self::MPropReadReq | Self::MPropReadCon | Self::MPropWriteReq | Self::MPropWriteCon
102 )
103 }
104
105 pub fn is_reset_service(&self) -> bool {
107 matches!(self, Self::MResetReq | Self::MResetInd)
108 }
109
110 pub fn to_confirmation(&self) -> Option<Self> {
112 match self {
113 Self::LDataReq => Some(Self::LDataCon),
114 Self::LRawReq => Some(Self::LRawCon),
115 Self::MPropReadReq => Some(Self::MPropReadCon),
116 Self::MPropWriteReq => Some(Self::MPropWriteCon),
117 Self::MResetReq => Some(Self::MResetInd),
118 _ => None,
119 }
120 }
121}
122
123impl From<MessageCode> for u8 {
124 fn from(code: MessageCode) -> Self {
125 code as u8
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
135#[repr(u8)]
136pub enum Priority {
137 System = 0,
139 #[default]
141 Normal = 1,
142 Urgent = 2,
144 Low = 3,
146}
147
148impl Priority {
149 pub fn from_bits(value: u8) -> Self {
151 match value & 0x03 {
152 0 => Self::System,
153 1 => Self::Normal,
154 2 => Self::Urgent,
155 _ => Self::Low,
156 }
157 }
158
159 pub fn to_bits(self) -> u8 {
161 self as u8
162 }
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum Apci {
172 GroupValueRead,
174 GroupValueResponse,
176 GroupValueWrite,
178 IndividualAddressWrite,
180 IndividualAddressRead,
182 IndividualAddressResponse,
184 AdcRead,
186 AdcResponse,
188 MemoryRead,
190 MemoryResponse,
192 MemoryWrite,
194 UserMessage,
196 DeviceDescriptorRead,
198 DeviceDescriptorResponse,
200 Restart,
202 Escape,
204 Unknown(u16),
206}
207
208impl Apci {
209 pub fn decode(value: u16) -> Self {
211 let apci = value & 0x03FF;
217
218 match apci {
219 0x0000 => Self::GroupValueRead,
220 0x0040..=0x007F => Self::GroupValueResponse,
221 0x0080..=0x00FF => Self::GroupValueWrite,
222 0x0100..=0x013F => Self::IndividualAddressWrite,
223 0x0140..=0x017F => Self::IndividualAddressRead,
224 0x0180..=0x01BF => Self::IndividualAddressResponse,
225 0x01C0..=0x01FF => Self::AdcRead,
226 0x0200..=0x023F => Self::AdcResponse,
227 0x0240..=0x027F => Self::MemoryRead,
228 0x0280..=0x02BF => Self::MemoryResponse,
229 0x02C0..=0x02FF => Self::MemoryWrite,
230 0x0300..=0x033F => Self::UserMessage,
231 0x0340..=0x037F => Self::DeviceDescriptorRead,
232 0x0380..=0x03BF => Self::DeviceDescriptorResponse,
233 0x03C0..=0x03DF => Self::Restart,
234 0x03E0..=0x03FF => Self::Escape,
235 _ => Self::Unknown(value),
236 }
237 }
238
239 pub fn decode_simple(value: u16) -> Self {
241 let base = value & 0x03C0; match base {
245 0x0000 => Self::GroupValueRead,
246 0x0040 => Self::GroupValueResponse,
247 0x0080 => Self::GroupValueWrite,
248 0x00C0 => Self::GroupValueWrite, 0x0100 => Self::IndividualAddressWrite,
250 0x0140 => Self::IndividualAddressRead,
251 0x0180 => Self::IndividualAddressResponse,
252 0x01C0 => Self::AdcRead,
253 0x0200 => Self::AdcResponse,
254 0x0240 => Self::MemoryRead,
255 0x0280 => Self::MemoryResponse,
256 0x02C0 => Self::MemoryWrite,
257 0x0300 => Self::UserMessage,
258 0x0340 => Self::DeviceDescriptorRead,
259 0x0380 => Self::DeviceDescriptorResponse,
260 0x03C0 => Self::Restart,
261 _ => Self::Unknown(value),
262 }
263 }
264
265 pub fn encode(&self) -> u16 {
267 match self {
268 Self::GroupValueRead => 0x0000,
269 Self::GroupValueResponse => 0x0040,
270 Self::GroupValueWrite => 0x0080,
271 Self::IndividualAddressWrite => 0x0100,
272 Self::IndividualAddressRead => 0x0100,
273 Self::IndividualAddressResponse => 0x0140,
274 Self::AdcRead => 0x0180,
275 Self::AdcResponse => 0x01C0,
276 Self::MemoryRead => 0x0200,
277 Self::MemoryResponse => 0x0240,
278 Self::MemoryWrite => 0x0280,
279 Self::UserMessage => 0x02C0,
280 Self::DeviceDescriptorRead => 0x0300,
281 Self::DeviceDescriptorResponse => 0x0340,
282 Self::Restart => 0x0380,
283 Self::Escape => 0x03C0,
284 Self::Unknown(v) => *v,
285 }
286 }
287
288 pub fn is_group_value(&self) -> bool {
290 matches!(
291 self,
292 Self::GroupValueRead | Self::GroupValueResponse | Self::GroupValueWrite
293 )
294 }
295
296 pub fn is_read(&self) -> bool {
298 matches!(
299 self,
300 Self::GroupValueRead
301 | Self::IndividualAddressRead
302 | Self::AdcRead
303 | Self::MemoryRead
304 | Self::DeviceDescriptorRead
305 )
306 }
307
308 pub fn is_write(&self) -> bool {
310 matches!(
311 self,
312 Self::GroupValueWrite | Self::IndividualAddressWrite | Self::MemoryWrite
313 )
314 }
315
316 pub fn is_response(&self) -> bool {
318 matches!(
319 self,
320 Self::GroupValueResponse
321 | Self::IndividualAddressResponse
322 | Self::AdcResponse
323 | Self::MemoryResponse
324 | Self::DeviceDescriptorResponse
325 )
326 }
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq)]
335#[repr(u8)]
336pub enum AdditionalInfoType {
337 PlMediumInfo = 0x01,
339 RfMediumInfo = 0x02,
341 BusMonitorInfo = 0x03,
343 TimestampRelative = 0x04,
345 TimeDelayUntil = 0x05,
347 ExtendedTimestamp = 0x06,
349 BiBatInfo = 0x07,
351 RfMultiInfo = 0x08,
353 PreamblePostamble = 0x09,
355 RfFastAckInfo = 0x0A,
357 ManufacturerData = 0xFE,
359}
360
361#[derive(Debug, Clone)]
363pub struct AdditionalInfo {
364 pub info_type: u8,
365 pub data: Vec<u8>,
366}
367
368impl AdditionalInfo {
369 pub fn new(info_type: u8, data: Vec<u8>) -> Self {
371 Self { info_type, data }
372 }
373
374 pub fn encode(&self, buf: &mut BytesMut) {
376 buf.put_u8(self.info_type);
377 buf.put_u8(self.data.len() as u8);
378 buf.put_slice(&self.data);
379 }
380
381 pub fn encoded_len(&self) -> usize {
383 2 + self.data.len()
384 }
385
386 pub fn decode(buf: &mut &[u8]) -> KnxResult<Self> {
388 if buf.len() < 2 {
389 return Err(KnxError::frame_too_short(2, buf.len()));
390 }
391
392 let info_type = buf.get_u8();
393 let length = buf.get_u8() as usize;
394
395 if buf.len() < length {
396 return Err(KnxError::frame_too_short(length, buf.len()));
397 }
398
399 let data = buf[..length].to_vec();
400 *buf = &buf[length..];
401
402 Ok(Self { info_type, data })
403 }
404}
405
406#[derive(Debug, Clone)]
412pub struct CemiFrame {
413 pub message_code: MessageCode,
415 pub additional_info: Vec<AdditionalInfo>,
417 pub source: IndividualAddress,
419 pub destination: u16,
421 pub address_type: AddressType,
423 pub hop_count: u8,
425 pub priority: Priority,
427 pub confirm: bool,
429 pub ack_request: bool,
431 pub system_broadcast: bool,
433 pub apci: Apci,
435 pub data: Vec<u8>,
437}
438
439impl CemiFrame {
440 pub fn group_value_write(
442 source: IndividualAddress,
443 destination: GroupAddress,
444 data: Vec<u8>,
445 ) -> Self {
446 Self {
447 message_code: MessageCode::LDataInd,
448 additional_info: Vec::new(),
449 source,
450 destination: destination.raw(),
451 address_type: AddressType::Group,
452 hop_count: 6,
453 priority: Priority::Low,
454 confirm: false,
455 ack_request: false,
456 system_broadcast: false,
457 apci: Apci::GroupValueWrite,
458 data,
459 }
460 }
461
462 pub fn group_value_read(source: IndividualAddress, destination: GroupAddress) -> Self {
464 Self {
465 message_code: MessageCode::LDataInd,
466 additional_info: Vec::new(),
467 source,
468 destination: destination.raw(),
469 address_type: AddressType::Group,
470 hop_count: 6,
471 priority: Priority::Low,
472 confirm: false,
473 ack_request: false,
474 system_broadcast: false,
475 apci: Apci::GroupValueRead,
476 data: Vec::new(),
477 }
478 }
479
480 pub fn group_value_response(
482 source: IndividualAddress,
483 destination: GroupAddress,
484 data: Vec<u8>,
485 ) -> Self {
486 Self {
487 message_code: MessageCode::LDataInd,
488 additional_info: Vec::new(),
489 source,
490 destination: destination.raw(),
491 address_type: AddressType::Group,
492 hop_count: 6,
493 priority: Priority::Low,
494 confirm: false,
495 ack_request: false,
496 system_broadcast: false,
497 apci: Apci::GroupValueResponse,
498 data,
499 }
500 }
501
502 pub fn bus_monitor_indication(
509 raw_frame: &[u8],
510 status: u8,
511 timestamp_ms: u16,
512 ) -> 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_raw = apci_byte1 as u16; let apci = Apci::decode(apci_raw);
882
883 let frame_data = if npdu_len <= 1 {
884 let small = apci_byte1 & 0x3F;
886 if small != 0 { vec![small] } else { Vec::new() }
887 } else {
888 buf[..npdu_len - 1].to_vec()
890 };
891
892 Ok(Self {
893 message_code,
894 additional_info,
895 source,
896 destination,
897 address_type,
898 hop_count,
899 priority,
900 confirm,
901 ack_request,
902 system_broadcast,
903 apci,
904 data: frame_data,
905 })
906 }
907}
908
909#[cfg(test)]
910mod tests {
911 use super::*;
912
913 #[test]
914 fn test_message_code() {
915 assert_eq!(MessageCode::try_from_u8(0x11), Some(MessageCode::LDataReq));
916 assert_eq!(MessageCode::try_from_u8(0x29), Some(MessageCode::LDataInd));
917 assert!(MessageCode::LDataReq.is_data());
918 assert!(MessageCode::LDataReq.is_request());
919 }
920
921 #[test]
922 fn test_priority() {
923 assert_eq!(Priority::from_bits(0), Priority::System);
924 assert_eq!(Priority::from_bits(3), Priority::Low);
925 assert_eq!(Priority::Normal.to_bits(), 1);
926 }
927
928 #[test]
929 fn test_apci_encode_decode() {
930 let tests = [
931 Apci::GroupValueRead,
932 Apci::GroupValueResponse,
933 Apci::GroupValueWrite,
934 ];
935
936 for apci in tests {
937 let encoded = apci.encode();
938 let decoded = Apci::decode(encoded);
939 assert_eq!(apci, decoded, "Failed for {:?}", apci);
940 }
941 }
942
943 #[test]
944 fn test_cemi_frame_group_value_write() {
945 let frame = CemiFrame::group_value_write(
946 IndividualAddress::new(1, 2, 3),
947 GroupAddress::three_level(1, 2, 3),
948 vec![0x01],
949 );
950
951 assert!(matches!(frame.apci, Apci::GroupValueWrite));
952 assert_eq!(frame.source.to_string(), "1.2.3");
953 assert_eq!(frame.destination_group().unwrap().to_string(), "1/2/3");
954 }
955
956 #[test]
957 fn test_cemi_frame_encode_decode() {
958 let original = CemiFrame::group_value_write(
959 IndividualAddress::new(1, 2, 3),
960 GroupAddress::three_level(1, 2, 100),
961 vec![0x55],
962 );
963
964 let encoded = original.encode();
965 let decoded = CemiFrame::decode(&encoded).unwrap();
966
967 assert!(matches!(decoded.apci, Apci::GroupValueWrite));
968 assert_eq!(decoded.source.to_string(), "1.2.3");
969 assert_eq!(decoded.destination_group().unwrap().to_string(), "1/2/100");
970 assert_eq!(decoded.data, vec![0x55]);
971 }
972
973 #[test]
974 fn test_cemi_frame_group_value_read() {
975 let frame = CemiFrame::group_value_read(
976 IndividualAddress::new(1, 1, 1),
977 GroupAddress::three_level(1, 0, 1),
978 );
979
980 let encoded = frame.encode();
981 let decoded = CemiFrame::decode(&encoded).unwrap();
982
983 assert!(matches!(decoded.apci, Apci::GroupValueRead));
984 assert!(decoded.data.is_empty() || decoded.data == vec![0]);
985 }
986
987 #[test]
988 fn test_message_code_confirmation_mapping() {
989 assert_eq!(MessageCode::LDataReq.to_confirmation(), Some(MessageCode::LDataCon));
990 assert_eq!(MessageCode::LRawReq.to_confirmation(), Some(MessageCode::LRawCon));
991 assert_eq!(MessageCode::MPropReadReq.to_confirmation(), Some(MessageCode::MPropReadCon));
992 assert_eq!(MessageCode::MPropWriteReq.to_confirmation(), Some(MessageCode::MPropWriteCon));
993 assert_eq!(MessageCode::MResetReq.to_confirmation(), Some(MessageCode::MResetInd));
994 assert_eq!(MessageCode::LDataCon.to_confirmation(), None);
995 assert_eq!(MessageCode::LDataInd.to_confirmation(), None);
996 }
997
998 #[test]
999 fn test_message_code_categories() {
1000 assert!(MessageCode::LDataCon.is_confirmation());
1001 assert!(MessageCode::LRawCon.is_confirmation());
1002 assert!(MessageCode::MPropReadCon.is_confirmation());
1003 assert!(!MessageCode::LDataReq.is_confirmation());
1004
1005 assert!(MessageCode::MPropReadReq.is_property_service());
1006 assert!(MessageCode::MPropWriteReq.is_property_service());
1007 assert!(MessageCode::MPropReadCon.is_property_service());
1008 assert!(!MessageCode::LDataReq.is_property_service());
1009
1010 assert!(MessageCode::MResetReq.is_reset_service());
1011 assert!(MessageCode::MResetInd.is_reset_service());
1012 assert!(!MessageCode::LDataReq.is_reset_service());
1013 }
1014
1015 #[test]
1016 fn test_bus_monitor_indication() {
1017 let raw_frame = vec![0x29, 0x00, 0xBC, 0x11, 0x01, 0x09, 0x01, 0x00, 0x80, 0x01];
1018 let frame = CemiFrame::bus_monitor_indication(&raw_frame, 0x00, 150);
1019
1020 assert_eq!(frame.message_code, MessageCode::LBusmonInd);
1021 assert_eq!(frame.additional_info.len(), 2);
1022
1023 assert_eq!(frame.additional_info[0].info_type, AdditionalInfoType::BusMonitorInfo as u8);
1025 assert_eq!(frame.additional_info[0].data, vec![0x00]);
1026
1027 assert_eq!(frame.additional_info[1].info_type, AdditionalInfoType::TimestampRelative as u8);
1029 assert_eq!(frame.additional_info[1].data, vec![0x00, 0x96]); assert_eq!(frame.data, raw_frame);
1033
1034 let encoded = frame.encode();
1036 let decoded = CemiFrame::decode(&encoded).unwrap();
1037 assert_eq!(decoded.message_code, MessageCode::LBusmonInd);
1038 assert_eq!(decoded.additional_info.len(), 2);
1039 assert_eq!(decoded.data, raw_frame);
1040 }
1041
1042 #[test]
1043 fn test_bus_monitor_indication_ext() {
1044 let raw_frame = vec![0x29, 0x00, 0xBC];
1045 let frame = CemiFrame::bus_monitor_indication_ext(&raw_frame, 0x80, 100, 123456);
1046
1047 assert_eq!(frame.additional_info.len(), 3);
1048 assert_eq!(frame.additional_info[2].info_type, AdditionalInfoType::ExtendedTimestamp as u8);
1050 let ts_bytes = &frame.additional_info[2].data;
1051 let ts = u32::from_be_bytes([ts_bytes[0], ts_bytes[1], ts_bytes[2], ts_bytes[3]]);
1052 assert_eq!(ts, 123456);
1053 }
1054
1055 #[test]
1056 fn test_prop_read_con() {
1057 let frame = CemiFrame::prop_read_con(0, 11, 1, 1, vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
1058 assert_eq!(frame.message_code, MessageCode::MPropReadCon);
1059
1060 let parsed = frame.parse_property_request().unwrap();
1062 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();
1070 let decoded = CemiFrame::decode(&encoded).unwrap();
1071 assert_eq!(decoded.message_code, MessageCode::MPropReadCon);
1072 assert_eq!(decoded.data, frame.data);
1073 }
1074
1075 #[test]
1076 fn test_prop_write_con_success() {
1077 let frame = CemiFrame::prop_write_con(0, 14, 1, 1, true);
1078 assert_eq!(frame.message_code, MessageCode::MPropWriteCon);
1079 assert!(!frame.confirm); let parsed = frame.parse_property_request().unwrap();
1082 assert_eq!(parsed.2, 1); }
1084
1085 #[test]
1086 fn test_prop_write_con_failure() {
1087 let frame = CemiFrame::prop_write_con(0, 14, 1, 1, false);
1088 assert!(frame.confirm); let parsed = frame.parse_property_request().unwrap();
1091 assert_eq!(parsed.2, 0); }
1093
1094 #[test]
1095 fn test_reset_ind() {
1096 let frame = CemiFrame::reset_ind();
1097 assert_eq!(frame.message_code, MessageCode::MResetInd);
1098 assert!(frame.data.is_empty());
1099
1100 let encoded = frame.encode();
1102 let decoded = CemiFrame::decode(&encoded).unwrap();
1103 assert_eq!(decoded.message_code, MessageCode::MResetInd);
1104 }
1105
1106 #[test]
1107 fn test_parse_property_request_too_short() {
1108 let frame = CemiFrame {
1109 message_code: MessageCode::MPropReadReq,
1110 additional_info: Vec::new(),
1111 source: IndividualAddress::new(0, 0, 0),
1112 destination: 0,
1113 address_type: AddressType::Individual,
1114 hop_count: 7,
1115 priority: Priority::System,
1116 confirm: false,
1117 ack_request: false,
1118 system_broadcast: false,
1119 apci: Apci::Unknown(0),
1120 data: vec![0x00, 0x00], };
1122
1123 assert!(frame.parse_property_request().is_none());
1124 }
1125}