1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
//! Implement [Packet] with the alloc feature enabled
use super::*;
/// A COE Packet
///
/// This models every possible Packet that can be send via CoE.
/// It consists mostly of [Payload]s, which can be added by different means.
/// Note that a Packet can at most contain 31 Payloads, so that all methods adding new Payloads can
/// fail.
// Note: we enforce and assume that `payload.len()` never exceeds 31.
// This is required, because the packet contains its own size (in bytes) in a field containing a
// u8, so no more then 255 (`u8::MAX`) bytes may ever be contained in a packets full representation.
// The packet on wire contains 4 bytes of headers, leaving us with 251 usable bytes. A payload
// length of 8 byte per payload yields 31 full payloads that fit in the max packet length.
#[derive(Hash, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct Packet {
/// CoE Version used. Currently, only 2.0 is supported.
version: COEVersion,
/// The actual payloads.
payloads: Vec<Payload>,
}
impl TryFrom<&[u8]> for Packet {
type Error = ParseCOEError;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
// the header must be four byte long
if value.len() < 4 {
return Err(Self::Error::PacketBelowHeaderLength);
};
// parse the version number from the first two bytes
let version: COEVersion = (value[0], value[1]).try_into()?;
// assert that packet length and payload length are consistent
if value[2] != 4 + 8 * value[3] {
return Err(Self::Error::PacketLengthInconsistent(value[2], value[3]));
};
// we are now certain that the header is correctly formed.
// Assert that the packet actually has the correct size as given in the header.
if value.len() != value[2].into() {
return Err(Self::Error::PacketSizeConflictsWithHeader(
value[2],
value.len(),
));
};
// The packet has the correct length. We can chunk it and parse each value independently
// without additional checks for buffer overrun
let mut payloads: Vec<Payload> = vec![];
for payload_nr in 0..value[3] {
// each payload is exactly 8 bytes long - +4 is the header offset
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> {
/// Serialize a packet into `Vec<u8>`
///
/// This is guaranteed to succeed since a Packet can never have more then 31 payloads, such
/// that the resulting serialization will always be at most 255 bytes long.
fn from(value: Packet) -> Self {
let mut res = vec![0_u8; 4 + value.payloads.len() * 8];
// Packet always successfully serializes, since we set the size correctly
value.try_serialize_into(&mut res).unwrap();
return res;
}
}
impl Packet {
/// Create a packet without payloads
pub fn new() -> Packet {
Packet {
version: COEVersion { major: 2, minor: 0 },
payloads: vec![],
}
}
/// The number of payloads in this packet.
pub fn len(&self) -> usize {
self.payloads.len()
}
/// Create a [Packet] with [Payload]s. Fails if more then 31 payloads are given.
pub fn try_from_payloads(payloads: &[Payload]) -> Option<Packet> {
let mut p = Packet::new();
p.try_append_from_slice(payloads)?;
Some(p)
}
/// Try to append a [Payload] to a [Packet]
///
/// Fails if the final packet size would exceed 255 bytes (31 payloads).
/// On failure, the packet was left unmodified.
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(())
}
/// Try to append all the given [Payload]s to a [Packet]
///
/// Fails if the final packet size would exceed 255 bytes (31 payloads).
/// On failure, the packet was left unmodified.
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(())
}
/// Serialize this Packet into a `&[u8]` which can be sent on-the-wire.
///
/// This can fail if buf is to small, in which case `None` is returned.
/// Otherwise, return the amount of bytes written into `buf`.
pub fn try_serialize_into(&self, buf: &mut [u8]) -> Option<usize> {
if buf.len() < 4 + self.payloads.len() as usize * 8 {
return None;
};
// the HEADER
buf[0] = self.version.major;
buf[1] = self.version.minor;
buf[2] = 4 + self.payloads.len() as u8 * 8;
buf[3] = self.payloads.len() as u8;
// the PAYLOAD
// now set each individual payload
for (index, payload) in self.payloads.iter().enumerate() {
payload.serialize_into(&mut buf[4 + index * 8..=11 + index * 8]);
}
Some(4 + self.payloads.len() as usize * 8)
}
}
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)
);
}
}