use crate::LEN_MASK;
use ethercrab_wire::{EtherCrabWireRead, EtherCrabWireSized, EtherCrabWireWrite};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, ethercrab_wire::EtherCrabWireRead)]
#[repr(u8)]
pub(crate) enum ProtocolType {
DlPdu = 0x01u8,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct EthercatFrameHeader {
pub(crate) payload_len: u16,
pub(crate) protocol: ProtocolType,
}
impl EtherCrabWireSized for EthercatFrameHeader {
const PACKED_LEN: usize = 2;
type Buffer = [u8; 2];
fn buffer() -> Self::Buffer {
[0u8; 2]
}
}
impl EtherCrabWireRead for EthercatFrameHeader {
fn unpack_from_slice(buf: &[u8]) -> Result<Self, ethercrab_wire::WireError> {
let raw = u16::unpack_from_slice(buf)?;
Ok(Self {
payload_len: raw & LEN_MASK,
protocol: ProtocolType::try_from((raw >> 12) as u8)?,
})
}
}
impl EtherCrabWireWrite for EthercatFrameHeader {
fn pack_to_slice_unchecked<'buf>(&self, buf: &'buf mut [u8]) -> &'buf [u8] {
let raw = self.payload_len | ((self.protocol as u16) << 12);
raw.pack_to_slice_unchecked(buf)
}
fn packed_len(&self) -> usize {
Self::PACKED_LEN
}
}
impl EthercatFrameHeader {
pub fn pdu(len: u16) -> Self {
debug_assert!(
len <= LEN_MASK,
"Frame length may not exceed {} bytes",
LEN_MASK
);
Self {
payload_len: len & LEN_MASK,
protocol: ProtocolType::DlPdu,
}
}
pub(crate) const fn header_len() -> usize {
Self::PACKED_LEN
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn pdu_header() {
let header = EthercatFrameHeader::pdu(0x28);
let mut buf = [0u8; 2];
let packed = header.pack_to_slice_unchecked(&mut buf);
let expected = &0b0001_0000_0010_1000u16.to_le_bytes();
assert_eq!(packed, expected);
}
#[test]
fn decode_pdu_len() {
let raw = 0b0001_0000_0010_1000u16;
let header = EthercatFrameHeader::unpack_from_slice(&raw.to_le_bytes()).unwrap();
assert_eq!(header.payload_len, 0x28);
assert_eq!(header.protocol, ProtocolType::DlPdu);
}
#[test]
fn parse() {
let raw = [0x3cu8, 0x10];
let header = EthercatFrameHeader::unpack_from_slice(&raw).unwrap();
assert_eq!(header.payload_len, 0x3c);
assert_eq!(header.protocol, ProtocolType::DlPdu);
}
}