use self::packet_common::PacketIterator;
use super::*;
#[derive(Hash, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Packet {
version: COEVersion,
pub(crate) payloads: Vec<Payload>,
}
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[3] > 31 {
return Err(Self::Error::PacketLengthInconsistent(value[2], value[3]));
} else {
if value[2]
!= u8::try_from(4 + 8 * u16::from(value[3]))
.expect("Payload length should have been checked to be at most 31.")
{
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: Vec<Payload> = vec![];
for payload_nr in 0..value[3] {
payloads.push(
value[(payload_nr * 8 + 4) as usize..=(payload_nr * 8 + 11) as usize].try_into()?,
);
}
Ok(Packet { version, payloads })
}
}
impl From<Packet> for Vec<u8> {
fn from(value: Packet) -> Self {
value.serialize_into_vec()
}
}
impl IntoIterator for Packet {
type Item = Payload;
type IntoIter = alloc::vec::IntoIter<Payload>;
fn into_iter(self) -> Self::IntoIter {
self.payloads.into_iter()
}
}
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.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.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: vec![],
}
}
pub fn len(&self) -> usize {
self.payloads.len()
}
pub fn is_empty(&self) -> bool {
self.payloads.is_empty()
}
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.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.payloads.len() + 1) * 8 + 4 >= u8::MAX as usize {
return None;
};
self.payloads.push(payload);
Some(())
}
pub fn try_append_from_slice(&mut self, payloads: &[Payload]) -> Option<()> {
if (self.payloads.len() + payloads.len()) * 8 + 4 >= u8::MAX as usize {
return None;
};
self.payloads.extend_from_slice(payloads);
Some(())
}
pub fn serialize_into_vec(&self) -> Vec<u8> {
let mut buf = vec![0_u8; self.wire_size()];
self.try_serialize_into(&mut buf)
.expect("Serialization should work because we have allocated a correctly sized vec.");
buf
}
pub fn try_serialize_into(&self, buf: &mut [u8]) -> Option<usize> {
if buf.len() < 4 + self.payloads.len() * 8 {
return None;
};
buf[0] = self.version.major;
buf[1] = self.version.minor;
buf[2] = 4 + u8::try_from(self.payloads.len())
.expect("payloads should contain at most 31 values")
* 8;
buf[3] =
u8::try_from(self.payloads.len()).expect("payloads should contain at most 31 values");
for (index, payload) in self.payloads.iter().enumerate() {
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.");
assert_eq!(
packet,
crate::Packet {
version: crate::COEVersion { major: 2, minor: 0 },
payloads: alloc::vec![
crate::Payload {
node: 3,
pdo_index: 0,
value: crate::COEValue::Analogue(
crate::AnalogueCOEValue::DegreeCentigrade_Tens(95)
)
},
crate::Payload {
node: 3,
pdo_index: 0,
value: crate::COEValue::Digital(crate::DigitalCOEValue::OnOff(true))
}
]
}
);
}
#[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)
);
}
}