pub struct CemiFrame { /* private fields */ }Expand description
A cEMI L_Data frame around 1 group telegram: what the frame is, the 2 control field octets, the telegram itself, and any additional-info block.
The control octets are held as octets, not as decoded flags - a frame that
arrived keeps what its sender wrote there, and the marks this layer acts
on have their own accessors. Self::encode emits them as held, with the
single exception of the frame-type mark, which it corrects where the
payload is too wide for a standard frame to state.
Implementations§
Source§impl CemiFrame
impl CemiFrame
Sourcepub const DEFAULT_CONTROL1: u8 = 0xBC
pub const DEFAULT_CONTROL1: u8 = 0xBC
The control field a frame carries unless its payload needs the extended form. Bit 7 set marks the standard frame type.
Sourcepub const DEFAULT_CONTROL2_GROUP: u8 = 0xe0
pub const DEFAULT_CONTROL2_GROUP: u8 = 0xe0
The second control field a constructor-built group frame carries. Bit 7 set marks the destination as a group address rather than an individual one, the next 3 bits are the hop count, set to 6, and the low 4 bits state the extended frame format, 0 for a group frame.
Sourcepub fn new(message_code: CemiMessageCode, telegram: GroupTelegram) -> Self
pub fn new(message_code: CemiMessageCode, telegram: GroupTelegram) -> Self
Builds a frame around a telegram, choosing the frame type its payload requires.
The standard frame type states its length in 4 bits, so it
cannot carry a payload past that; anything wider is marked
extended. The choice follows the encoded length alone - this
layer reads no datapoint declaration - and this entry point
takes no frame-type argument. A frame marked otherwise reaches
this type through Self::decode.
Sourcepub const fn is_standard_frame(&self) -> bool
pub const fn is_standard_frame(&self) -> bool
Whether this frame is marked as the standard frame type.
This reports the mark the frame holds, which for a decoded
frame is the one its sender wrote. Self::encode
emits that mark as held, except around a payload too wide for
the standard frame type to state, where it emits the extended
mark instead.
Sourcepub const fn confirmation(&self) -> Option<ConfirmStatus>
pub const fn confirmation(&self) -> Option<ConfirmStatus>
What the server reports about the transmission it confirmed, or
None where this frame is not a confirmation.
The status lives in the control field, which a request and an
indication also carry - so the message code decides whether the
bit means anything, and this answers None rather than
reporting a bit that does not.
Sourcepub fn with_confirmation(self, status: ConfirmStatus) -> Result<Self>
pub fn with_confirmation(self, status: ConfirmStatus) -> Result<Self>
Returns the frame stating the outcome a server reports for the transmission it performed.
A server states this; a client never does. The flag is defined
on an L_Data.con alone, so this refuses on any other message
code rather than setting a bit that carries no meaning there -
the same rule Self::confirmation reads it back through.
Sourcepub fn group_value_read(
source: IndividualAddress,
destination: GroupAddress,
) -> Result<Self>
pub fn group_value_read( source: IndividualAddress, destination: GroupAddress, ) -> Result<Self>
Builds a group-value read request: the service that asks a group’s senders to state their value.
The read is the 1 group service that carries no data, so unlike its response and write siblings this takes no payload and leaves the caller no transmitted form to choose.
Sourcepub fn group_value_response(
source: IndividualAddress,
destination: GroupAddress,
payload: GroupPayload,
) -> Result<Self>
pub fn group_value_response( source: IndividualAddress, destination: GroupAddress, payload: GroupPayload, ) -> Result<Self>
Builds a group-value response frame around an already-formed payload.
This is a frame constructor, not a datapoint entry point: the caller states the transmitted form, having decided it where the datapoint’s declared width is known.
Sourcepub fn group_value_write(
source: IndividualAddress,
destination: GroupAddress,
payload: GroupPayload,
) -> Result<Self>
pub fn group_value_write( source: IndividualAddress, destination: GroupAddress, payload: GroupPayload, ) -> Result<Self>
Builds a group-value write frame around an already-formed payload.
The same frame-constructor contract as Self::group_value_response.
Sourcepub const fn message_code(&self) -> CemiMessageCode
pub const fn message_code(&self) -> CemiMessageCode
What the frame is, as its first octet states it.
Sourcepub const fn control1(&self) -> u8
pub const fn control1(&self) -> u8
The first control field octet as the frame holds it. For a decoded
frame that is the octet its sender wrote, uninterpreted; the 2 marks
this layer reads out of it have their own accessors,
Self::is_standard_frame and Self::confirmation.
Sourcepub const fn control2(&self) -> u8
pub const fn control2(&self) -> u8
The second control field octet as the frame holds it, uninterpreted.
It carries the address type, the hop count, and the extended frame
format; a constructor-built frame states
Self::DEFAULT_CONTROL2_GROUP.
Sourcepub const fn telegram(&self) -> &GroupTelegram
pub const fn telegram(&self) -> &GroupTelegram
The group telegram this frame carries.
Sourcepub fn additional_info(&self) -> &[u8] ⓘ
pub fn additional_info(&self) -> &[u8] ⓘ
Opaque cEMI additional-info bytes (empty for constructor-built frames).
Sourcepub fn with_additional_info(
self,
additional_info: impl Into<Vec<u8>>,
) -> Result<Self>
pub fn with_additional_info( self, additional_info: impl Into<Vec<u8>>, ) -> Result<Self>
Returns the frame with the given opaque additional-info block.
The cEMI additional-info length is a single octet, so the block must
be at most 255 bytes; longer input is rejected with
KnxError::InvalidFrame("additional info too long").
Sourcepub fn decode(input: &[u8]) -> Result<(Self, &[u8])>
pub fn decode(input: &[u8]) -> Result<(Self, &[u8])>
Decodes a frame, preserving the control field exactly as it arrived.
A received frame keeps the frame type its sender chose, even where that contradicts its own length octet: this layer is strict about what it emits and faithful about what it reports, so a peer’s malformed control field is visible rather than silently normalized.
That fidelity is the control field’s, and stating it of the
whole frame would be false. The APDU has 1 malformed shape this
layer does not carry forward: a group read whose APDU is 2
octets and whose service octet sets the 6 bits a well-formed
read leaves clear. Those bits are dropped, the payload decodes
as GroupPayload::None, and re-encoding emits a clean
service octet rather than the one that arrived - the decoded
frame cannot hold them, because a read carrying data is not a
representable telegram here. The wider malformed reads are
refused instead: an APDU longer than 2 octets is rejected for
setting those bits, or for carrying a payload at all.
Nothing in this workspace re-transmits a decoded frame, so no shipped path puts that difference on the bus. A consumer that needs the octets a peer actually sent must keep the input; a re-encode is not a copy of it.
Sourcepub fn encode(&self, out: &mut Vec<u8>) -> Result<()>
pub fn encode(&self, out: &mut Vec<u8>) -> Result<()>
Encodes the frame, deriving what the frame states about its own shape.
One thing here is derived rather than carried: the NPDU length,
which is the encoded APDU’s own width. The control field is
emitted as held, with one exception. The constructor is not the
only way a frame comes into being - a decoded frame keeps
whatever its sender marked - so a frame can hold the standard
mark around a payload no standard frame can state. That mark is
cleared here, through control1_with_frame_type, so such a
frame cannot leave this layer. Every other control bit, the
extended mark included, is emitted exactly as given.
What cannot be derived is refused instead: a telegram whose payload contradicts its service is rejected by the APDU encoding rather than repaired.