Skip to main content

mabi_knx/
cemi.rs

1//! Common EMI (cEMI) frame handling.
2//!
3//! cEMI is the standardized interface for KNX communication,
4//! used in KNXnet/IP tunneling and routing.
5
6use bytes::{Buf, BufMut, BytesMut};
7
8use crate::address::{AddressType, GroupAddress, IndividualAddress};
9use crate::error::{KnxError, KnxResult};
10
11// ============================================================================
12// Message Code
13// ============================================================================
14
15/// cEMI message code.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17#[repr(u8)]
18pub enum MessageCode {
19    /// L_Data.req - Request to send data.
20    LDataReq = 0x11,
21    /// L_Data.con - Confirmation of data sent.
22    LDataCon = 0x2E,
23    /// L_Data.ind - Indication of received data.
24    LDataInd = 0x29,
25    /// L_Busmon.ind - Bus monitor indication.
26    LBusmonInd = 0x2B,
27    /// L_Raw.req - Raw request.
28    LRawReq = 0x10,
29    /// L_Raw.ind - Raw indication.
30    LRawInd = 0x2D,
31    /// L_Raw.con - Raw confirmation.
32    LRawCon = 0x2F,
33    /// M_PropRead.req - Property read request.
34    MPropReadReq = 0xFC,
35    /// M_PropRead.con - Property read confirmation.
36    MPropReadCon = 0xFB,
37    /// M_PropWrite.req - Property write request.
38    MPropWriteReq = 0xF6,
39    /// M_PropWrite.con - Property write confirmation.
40    MPropWriteCon = 0xF5,
41    /// M_Reset.req - Reset request.
42    MResetReq = 0xF1,
43    /// M_Reset.ind - Reset indication.
44    MResetInd = 0xF0,
45}
46
47impl MessageCode {
48    /// Try to create from raw byte.
49    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    /// Check if this is a data message.
69    pub fn is_data(&self) -> bool {
70        matches!(self, Self::LDataReq | Self::LDataCon | Self::LDataInd)
71    }
72
73    /// Check if this is a request.
74    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    /// Check if this is an indication.
86    pub fn is_indication(&self) -> bool {
87        matches!(
88            self,
89            Self::LDataInd | Self::LBusmonInd | Self::LRawInd | Self::MResetInd
90        )
91    }
92
93    /// Check if this is a confirmation.
94    pub fn is_confirmation(&self) -> bool {
95        matches!(
96            self,
97            Self::LDataCon | Self::LRawCon | Self::MPropReadCon | Self::MPropWriteCon
98        )
99    }
100
101    /// Check if this is a property service message.
102    pub fn is_property_service(&self) -> bool {
103        matches!(
104            self,
105            Self::MPropReadReq | Self::MPropReadCon | Self::MPropWriteReq | Self::MPropWriteCon
106        )
107    }
108
109    /// Check if this is a reset service message.
110    pub fn is_reset_service(&self) -> bool {
111        matches!(self, Self::MResetReq | Self::MResetInd)
112    }
113
114    /// Get the corresponding confirmation message code for a request.
115    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// ============================================================================
134// Priority
135// ============================================================================
136
137/// KNX telegram priority.
138#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
139#[repr(u8)]
140pub enum Priority {
141    /// System priority (highest).
142    System = 0,
143    /// Normal priority.
144    #[default]
145    Normal = 1,
146    /// Urgent priority.
147    Urgent = 2,
148    /// Low priority (lowest).
149    Low = 3,
150}
151
152impl Priority {
153    /// Create from raw value (2 bits).
154    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    /// Convert to raw bits.
164    pub fn to_bits(self) -> u8 {
165        self as u8
166    }
167}
168
169// ============================================================================
170// APCI (Application Layer Protocol Control Information)
171// ============================================================================
172
173/// APCI - Application Protocol Control Information.
174#[derive(Debug, Clone, Copy, PartialEq, Eq)]
175pub enum Apci {
176    /// Group Value Read.
177    GroupValueRead,
178    /// Group Value Response.
179    GroupValueResponse,
180    /// Group Value Write.
181    GroupValueWrite,
182    /// Individual Address Write.
183    IndividualAddressWrite,
184    /// Individual Address Read.
185    IndividualAddressRead,
186    /// Individual Address Response.
187    IndividualAddressResponse,
188    /// ADC Read.
189    AdcRead,
190    /// ADC Response.
191    AdcResponse,
192    /// Memory Read.
193    MemoryRead,
194    /// Memory Response.
195    MemoryResponse,
196    /// Memory Write.
197    MemoryWrite,
198    /// User Message.
199    UserMessage,
200    /// Device Descriptor Read.
201    DeviceDescriptorRead,
202    /// Device Descriptor Response.
203    DeviceDescriptorResponse,
204    /// Restart.
205    Restart,
206    /// Escape (for extended APCI).
207    Escape,
208    /// Unknown APCI.
209    Unknown(u16),
210}
211
212impl Apci {
213    /// Decode APCI from raw 10-bit value.
214    pub fn decode(value: u16) -> Self {
215        // APCI encoding uses bits for different services
216        // Group Value services are in range 0x0000-0x00FF
217        // Check specific patterns
218
219        // Mask to the 10-bit APCI
220        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    /// Decode APCI simplified - just match the base values.
244    pub fn decode_simple(value: u16) -> Self {
245        // Use only upper bits to determine main type
246        let base = value & 0x03C0; // Bits 9-6
247
248        match base {
249            0x0000 => Self::GroupValueRead,
250            0x0040 => Self::GroupValueResponse,
251            0x0080 => Self::GroupValueWrite,
252            0x00C0 => Self::GroupValueWrite, // Continuation
253            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    /// Encode APCI to raw 10-bit value.
270    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    /// Check if this is a group value service.
293    pub fn is_group_value(&self) -> bool {
294        matches!(
295            self,
296            Self::GroupValueRead | Self::GroupValueResponse | Self::GroupValueWrite
297        )
298    }
299
300    /// Check if this is a read request.
301    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    /// Check if this is a write.
313    pub fn is_write(&self) -> bool {
314        matches!(
315            self,
316            Self::GroupValueWrite | Self::IndividualAddressWrite | Self::MemoryWrite
317        )
318    }
319
320    /// Check if this is a response.
321    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// ============================================================================
334// Additional Info
335// ============================================================================
336
337/// cEMI additional information type.
338#[derive(Debug, Clone, Copy, PartialEq, Eq)]
339#[repr(u8)]
340pub enum AdditionalInfoType {
341    /// PL Medium Information.
342    PlMediumInfo = 0x01,
343    /// RF Medium Information.
344    RfMediumInfo = 0x02,
345    /// Bus Monitor Info.
346    BusMonitorInfo = 0x03,
347    /// Timestamp Relative.
348    TimestampRelative = 0x04,
349    /// Time Delay Until.
350    TimeDelayUntil = 0x05,
351    /// Extended Relative Timestamp.
352    ExtendedTimestamp = 0x06,
353    /// BiBat Information.
354    BiBatInfo = 0x07,
355    /// RF Multi Information.
356    RfMultiInfo = 0x08,
357    /// Preamble And Postamble.
358    PreamblePostamble = 0x09,
359    /// RF Fast Ack Information.
360    RfFastAckInfo = 0x0A,
361    /// Manufacturer Specific Data.
362    ManufacturerData = 0xFE,
363}
364
365/// cEMI additional information.
366#[derive(Debug, Clone)]
367pub struct AdditionalInfo {
368    pub info_type: u8,
369    pub data: Vec<u8>,
370}
371
372impl AdditionalInfo {
373    /// Create new additional info.
374    pub fn new(info_type: u8, data: Vec<u8>) -> Self {
375        Self { info_type, data }
376    }
377
378    /// Encode to bytes.
379    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    /// Encoded length.
386    pub fn encoded_len(&self) -> usize {
387        2 + self.data.len()
388    }
389
390    /// Decode from buffer.
391    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// ============================================================================
411// cEMI Frame
412// ============================================================================
413
414/// cEMI L_Data frame.
415#[derive(Debug, Clone)]
416pub struct CemiFrame {
417    /// Message code.
418    pub message_code: MessageCode,
419    /// Additional information.
420    pub additional_info: Vec<AdditionalInfo>,
421    /// Source address.
422    pub source: IndividualAddress,
423    /// Destination address (raw).
424    pub destination: u16,
425    /// Address type.
426    pub address_type: AddressType,
427    /// Hop count (routing counter).
428    pub hop_count: u8,
429    /// Priority.
430    pub priority: Priority,
431    /// Confirm flag (for L_Data.con).
432    pub confirm: bool,
433    /// Acknowledge request.
434    pub ack_request: bool,
435    /// System broadcast.
436    pub system_broadcast: bool,
437    /// APCI.
438    pub apci: Apci,
439    /// Data (payload after APCI).
440    pub data: Vec<u8>,
441}
442
443impl CemiFrame {
444    /// Create a Group Value Write frame.
445    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    /// Create a Group Value Read frame.
467    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    /// Create a Group Value Response frame.
485    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    /// Create a Bus Monitor indication frame (MC=0x2B).
507    ///
508    /// Bus monitor frames wrap the raw bus frame data with Additional Info TLV:
509    /// - 0x03: Bus Monitor Status (error flags)
510    /// - 0x04: Timestamp Relative (2 bytes, ms since last frame)
511    /// - 0x06: Extended Timestamp (4 bytes, microseconds)
512    pub fn bus_monitor_indication(raw_frame: &[u8], status: u8, timestamp_ms: u16) -> Self {
513        let mut additional_info = Vec::new();
514
515        // Bus Monitor Info (type 0x03): 1 byte status
516        additional_info.push(AdditionalInfo::new(
517            AdditionalInfoType::BusMonitorInfo as u8,
518            vec![status],
519        ));
520
521        // Timestamp Relative (type 0x04): 2 bytes, milliseconds since last frame
522        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    /// Create a Bus Monitor indication with extended timestamp (microseconds).
544    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        // Extended Relative Timestamp (type 0x06): 4 bytes, microseconds
553        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    /// Create an M_PropRead.con response frame.
562    ///
563    /// Responds to M_PropRead.req with the requested property value.
564    /// The data format follows KNX cEMI property service encoding:
565    /// [object_index(2) | property_id(2) | count_index(2) | data...]
566    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        // Number of elements (4 bits) | start index (12 bits)
577        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    /// Create an M_PropWrite.con response frame.
598    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        // On error: count=0 signals failure per KNX spec
609        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    /// Create an M_Reset.ind frame (response to M_Reset.req).
630    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    /// Parse property service request data.
648    ///
649    /// Returns (object_index, property_id, count, start_index, remaining_data).
650    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    /// Get destination as group address.
664    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    /// Get destination as individual address.
673    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    /// Set destination as group address.
682    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    /// Set destination as individual address.
689    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    /// Set priority.
696    pub fn with_priority(mut self, priority: Priority) -> Self {
697        self.priority = priority;
698        self
699    }
700
701    /// Set hop count.
702    pub fn with_hop_count(mut self, hop_count: u8) -> Self {
703        self.hop_count = hop_count.min(7);
704        self
705    }
706
707    /// Encode to bytes.
708    pub fn encode(&self) -> Vec<u8> {
709        let mut buf = BytesMut::new();
710
711        // Message code
712        buf.put_u8(self.message_code.into());
713
714        // Additional info length
715        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        // Additional info
719        for info in &self.additional_info {
720            info.encode(&mut buf);
721        }
722
723        // Property service and reset frames use a different body format:
724        // MC | add_info_len | [add_info...] | data
725        // No Ctrl1/Ctrl2/addresses/NPDU — just raw property data.
726        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        // Bus monitor frames: MC | add_info_len | [add_info...] | raw_frame_data
732        // The additional info carries status/timestamp, data is the raw bus frame.
733        if self.message_code == MessageCode::LBusmonInd {
734            buf.put_slice(&self.data);
735            return buf.to_vec();
736        }
737
738        // Standard L_Data frame encoding (L_Data.req/con/ind, L_Raw)
739        // Control byte 1
740        let ctrl1 = 0x80 // Standard frame
741            | (if self.ack_request { 0x00 } else { 0x20 }) // L/H (ack flag inverted)
742            | (if !self.confirm { 0x00 } else { 0x01 }) // Confirm
743            | ((self.priority.to_bits() & 0x03) << 2); // Priority
744        buf.put_u8(ctrl1);
745
746        // Control byte 2
747        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        // Source address
755        buf.put_u16(self.source.encode());
756
757        // Destination address
758        buf.put_u16(self.destination);
759
760        // NPDU
761        let apci = self.apci.encode();
762
763        // APCI internal value maps directly to first NPDU wire byte
764        let apci_byte = apci as u8;
765
766        if self.data.is_empty() {
767            // No data - APCI only (e.g., GroupValueRead)
768            buf.put_u8(1); // npdu_len = 1
769            buf.put_u8(apci_byte);
770        } else if self.data.len() == 1 && self.data[0] <= 0x3F {
771            // Small data - embed in low 6 bits of APCI byte
772            buf.put_u8(1); // npdu_len = 1
773            buf.put_u8(apci_byte | (self.data[0] & 0x3F));
774        } else {
775            // Full data: npdu_len = 1 (APCI byte) + data.len()
776            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    /// Decode from bytes.
785    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        // Message code
793        let message_code = MessageCode::try_from_u8(buf.get_u8())
794            .ok_or_else(|| KnxError::UnknownMessageCode(data[0]))?;
795
796        // Additional info
797        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        // Property service frames: MC | add_info_len | [add_info...] | data
812        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        // Bus monitor frames: MC | add_info_len | [add_info...] | raw_frame_data
830        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        // Standard L_Data frame decoding
848        if buf.len() < 7 {
849            return Err(KnxError::frame_too_short(7, buf.len()));
850        }
851
852        // Control bytes
853        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        // Addresses
865        let source = IndividualAddress::decode(buf.get_u16());
866        let destination = buf.get_u16();
867
868        // NPDU
869        // npdu_len counts bytes starting from the TPCI/APCI byte (inclusive)
870        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        // Support both the historical compact test encoding used by this crate
876        // and the two-octet TPCI/APCI layout emitted by external stacks such as
877        // XKNX. The latter uses a first byte with only TPCI/APCI high bits and a
878        // second byte carrying the APCI service plus compact payload bits.
879        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            // Compact legacy layout: first NPDU byte maps directly to APCI.
898            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, // L_Data.req
997            0x00, // additional info length
998            0xBC, // control 1
999            0xE0, // control 2, group destination
1000            0x11, 0x65, // source 1.1.101
1001            0x08, 0x02, // destination 1/0/2
1002            0x03, // TPCI/APCI plus one payload octet
1003            0x00, // TPCI + APCI high bits
1004            0x80, // GroupValueWrite
1005            0x80, // DPT5 payload
1006        ])
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        // Bus Monitor Info (type 0x03)
1082        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        // Timestamp Relative (type 0x04)
1089        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]); // 150 in BE
1094
1095        // Raw frame data
1096        assert_eq!(frame.data, raw_frame);
1097
1098        // Verify encode/decode roundtrip
1099        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        // Extended Timestamp (type 0x06)
1113        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        // Parse the property request back
1128        let parsed = frame.parse_property_request().unwrap();
1129        assert_eq!(parsed.0, 0); // object_index
1130        assert_eq!(parsed.1, 11); // property_id (serial number)
1131        assert_eq!(parsed.2, 1); // count
1132        assert_eq!(parsed.3, 1); // start_index
1133        assert_eq!(parsed.4, &[0x00, 0x00, 0x00, 0x00, 0x00, 0x01]); // value
1134
1135        // Verify encode/decode roundtrip
1136        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); // success: confirm=false
1147
1148        let parsed = frame.parse_property_request().unwrap();
1149        assert_eq!(parsed.2, 1); // count=1 on success
1150    }
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); // failure: confirm=true
1156
1157        let parsed = frame.parse_property_request().unwrap();
1158        assert_eq!(parsed.2, 0); // count=0 signals error per KNX spec
1159    }
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        // Verify encode/decode roundtrip
1168        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], // Only 2 bytes, need 6
1188        };
1189
1190        assert!(frame.parse_property_request().is_none());
1191    }
1192}