use super::*;
use self::packet_common::PacketIterator;
#[derive(Hash, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Packet {
version: COEVersion,
pub(crate) payloads: [Payload; 31],
payload_length: u8,
}
impl TryFrom<&[u8]> for Packet {
type Error = ParseCOEError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() < 4 {
return Err(Self::Error::PacketBelowHeaderLength);
};
let version: COEVersion = (value[0], value[1]).try_into()?;
if value[2] != 4 + 8 * value[3] {
return Err(Self::Error::PacketLengthInconsistent(value[2], value[3]));
};
if value.len() != value[2].into() {
return Err(Self::Error::PacketSizeConflictsWithHeader(
value[2],
value.len(),
));
};
let mut payloads = [Payload::default(); 31];
let mut idx = 0;
for payload_nr in 0..value[3] {
let payload: Payload =
value[(payload_nr * 8 + 4) as usize..=(payload_nr * 8 + 11) as usize].try_into()?;
payloads[idx] = payload;
idx += 1;
}
Ok(Packet {
version,
payloads,
payload_length: idx as u8,
})
}
}
impl From<Packet> for [u8; 4 + 8 * 31] {
fn from(value: Packet) -> Self {
let mut res = [0_u8; 4 + 8 * 31];
value.try_serialize_into(&mut res).unwrap();
res
}
}
pub struct PacketNoAllocIntoIter {
packet: Packet,
idx: usize,
}
impl Iterator for PacketNoAllocIntoIter {
type Item = Payload;
fn next(&mut self) -> Option<Self::Item> {
if self.idx < self.packet.len() {
let res = Some(self.packet.payloads[self.idx]);
self.idx += 1;
res
} else {
None
}
}
}
impl IntoIterator for Packet {
type Item = Payload;
type IntoIter = PacketNoAllocIntoIter;
fn into_iter(self) -> Self::IntoIter {
PacketNoAllocIntoIter {
packet: self,
idx: 0,
}
}
}
impl<'a> IntoIterator for &'a Packet {
type Item = &'a Payload;
type IntoIter = core::slice::Iter<'a, Payload>;
fn into_iter(self) -> Self::IntoIter {
self.payloads[0..self.payload_length as usize].iter()
}
}
impl<'a> IntoIterator for &'a mut Packet {
type Item = &'a mut Payload;
type IntoIter = core::slice::IterMut<'a, Payload>;
fn into_iter(self) -> Self::IntoIter {
self.payloads[0..self.payload_length as usize].iter_mut()
}
}
impl Default for Packet {
fn default() -> Self {
Self::new()
}
}
impl Packet {
pub fn new() -> Packet {
Packet {
version: COEVersion { major: 2, minor: 0 },
payloads: [Payload::default(); 31],
payload_length: 0,
}
}
pub fn len(&self) -> usize {
self.payload_length.into()
}
pub fn is_empty(&self) -> bool {
self.payload_length == 0
}
pub fn wire_size(&self) -> usize {
4 + self.len() * 8
}
pub fn version(&self) -> COEVersion {
self.version
}
pub fn iter(&self) -> PacketIterator {
PacketIterator::new(self)
}
pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Payload> {
self.payloads[0..self.payload_length as usize].iter_mut()
}
pub fn try_from_payloads(payloads: &[Payload]) -> Option<Packet> {
let mut p = Packet::new();
p.try_append_from_slice(payloads)?;
Some(p)
}
pub fn try_push(&mut self, payload: Payload) -> Option<()> {
if self.payload_length >= 31 {
return None;
};
self.payloads[self.payload_length as usize] = payload;
self.payload_length += 1;
Some(())
}
pub fn try_append_from_slice(&mut self, payloads: &[Payload]) -> Option<()> {
if (self.payload_length as usize + payloads.len()) * 8 + 4 >= u8::MAX as usize {
return None;
};
self.payloads[self.payload_length as usize..self.payload_length as usize + payloads.len()]
.clone_from_slice(payloads);
self.payload_length += payloads.len() as u8;
Some(())
}
pub fn try_serialize_into(&self, buf: &mut [u8]) -> Option<usize> {
if buf.len() < 4 + self.payload_length as usize * 8 {
return None;
};
buf[0] = self.version.major;
buf[1] = self.version.minor;
buf[2] = 4 + self.payload_length * 8;
buf[3] = self.payload_length;
for (index, payload) in self
.payloads
.iter()
.enumerate()
.take_while(|(i, _)| *i < self.payload_length as usize)
{
payload.serialize_into(&mut buf[4 + index * 8..=11 + index * 8]);
}
Some(4 + self.payloads.len() * 8)
}
}
#[cfg(test)]
mod test {
#[test]
fn parse_packet_success() {
let raw_bytes = [
2, 0, 20, 2, 3, 0, 1, 1, 95, 0, 0, 0, 3, 0, 0, 43, 1, 0, 0, 0,
];
let packet: crate::Packet = raw_bytes[0..20]
.try_into()
.expect("This Packet is parsable.");
let mut payloads = [crate::Payload::default(); 31];
payloads[0] = crate::Payload {
node: 3,
pdo_index: 0,
value: crate::COEValue::Analogue(crate::AnalogueCOEValue::DegreeCentigrade_Tens(95)),
};
payloads[1] = crate::Payload {
node: 3,
pdo_index: 0,
value: crate::COEValue::Digital(crate::DigitalCOEValue::OnOff(true)),
};
assert_eq!(
packet,
crate::Packet {
version: crate::COEVersion { major: 2, minor: 0 },
payloads,
payload_length: 2
}
);
}
#[test]
fn parse_packet_below_header_length() {
let raw_bytes = [2, 0, 20];
let err: crate::ParseCOEError = TryInto::<crate::Packet>::try_into(&raw_bytes[0..3])
.expect_err("This Packet is not parsable.");
assert_eq!(err, crate::ParseCOEError::PacketBelowHeaderLength);
}
#[test]
fn parse_packet_packet_length_inconsistent() {
let raw_bytes = [
2, 0, 21, 2, 3, 0, 1, 1, 0, 0, 0, 95, 3, 0, 0, 43, 0, 0, 0, 1,
];
let err: crate::ParseCOEError = TryInto::<crate::Packet>::try_into(&raw_bytes[0..20])
.expect_err("This Packet is not parsable");
assert_eq!(
err,
crate::ParseCOEError::PacketLengthInconsistent(21_u8, 2_u8)
);
}
#[test]
fn parse_packet_packet_length_inconsistent_2() {
let raw_bytes = [
2, 0, 20, 3, 3, 0, 1, 1, 0, 0, 0, 95, 3, 0, 0, 43, 0, 0, 0, 1,
];
let err: crate::ParseCOEError = TryInto::<crate::Packet>::try_into(&raw_bytes[0..20])
.expect_err("This Packet is not parsable");
assert_eq!(
err,
crate::ParseCOEError::PacketLengthInconsistent(20_u8, 3_u8)
);
}
#[test]
fn parse_packet_packet_size_conflicts_with_header() {
let raw_bytes = [
2, 0, 12, 1, 3, 0, 1, 1, 0, 0, 0, 95, 3, 0, 0, 43, 0, 0, 0, 1,
];
let err: crate::ParseCOEError = TryInto::<crate::Packet>::try_into(&raw_bytes[0..20])
.expect_err("This Packet is not parsable");
assert_eq!(
err,
crate::ParseCOEError::PacketSizeConflictsWithHeader(12, 20)
);
}
}