#[cfg(not(feature = "std"))]
use core::{
clone::Clone,
cmp::{Eq, PartialEq},
fmt::Debug,
prelude::rust_2021::derive,
result::Result::Ok,
};
use byteorder_cursor::Cursor;
use super::{
Result, SmaCmdWord, SmaEndpoint, SmaInvCounter, SmaInvHeader,
SmaPacketFooter, SmaPacketHeader, SmaSerde,
};
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct SmaInvIdentify {
pub dst: SmaEndpoint,
pub src: SmaEndpoint,
pub error_code: u16,
pub counters: SmaInvCounter,
pub identity: Option<[u8; Self::PAYLOAD_MAX]>,
}
impl SmaSerde for SmaInvIdentify {
fn serialize(&self, buffer: &mut Cursor<&mut [u8]>) -> Result<()> {
let data_len = if self.identity.is_some() {
buffer.check_remaining(Self::LENGTH_MAX)?;
Self::LENGTH_MAX - SmaPacketHeader::LENGTH - SmaPacketFooter::LENGTH
} else {
buffer.check_remaining(Self::LENGTH_MIN)?;
Self::LENGTH_MIN - SmaPacketHeader::LENGTH - SmaPacketFooter::LENGTH
};
let header = SmaPacketHeader {
data_len,
protocol: SmaPacketHeader::SMA_PROTOCOL_INV,
};
let (dst_ctrl, channel) = if self.identity.is_some() {
(0xC0, 1)
} else {
(0, 0)
};
let inv_header = SmaInvHeader {
wordcount: (data_len / 4) as u8,
class: 0xA0,
dst: self.dst.clone(),
dst_ctrl,
src: self.src.clone(),
error_code: self.error_code,
counters: self.counters.clone(),
cmd: SmaCmdWord {
channel,
opcode: Self::OPCODE,
},
..Default::default()
};
header.serialize(buffer)?;
inv_header.serialize(buffer)?;
if let Some(identity) = self.identity {
buffer.write_bytes(&identity);
} else {
buffer.write_bytes(&[0; Self::PAYLOAD_MIN]);
}
SmaPacketFooter::default().serialize(buffer)?;
Ok(())
}
fn deserialize(buffer: &mut Cursor<&[u8]>) -> Result<Self> {
buffer.check_remaining(Self::LENGTH_MIN)?;
let header = SmaPacketHeader::deserialize(buffer)?;
header.check_protocol(SmaPacketHeader::SMA_PROTOCOL_INV)?;
buffer.check_remaining(header.data_len)?;
let inv_header = SmaInvHeader::deserialize(buffer)?;
inv_header.check_wordcount(header.data_len)?;
inv_header.check_class(0xA0)?;
inv_header.check_opcode(Self::OPCODE)?;
let mut identity = [0; Self::PAYLOAD_MAX];
let identity =
if header.data_len - SmaInvHeader::LENGTH >= Self::PAYLOAD_MAX {
buffer.read_bytes(&mut identity);
Some(identity)
} else {
buffer.skip(Self::PAYLOAD_MIN);
None
};
SmaPacketFooter::deserialize(buffer)?;
Ok(Self {
dst: inv_header.dst,
src: inv_header.src,
error_code: inv_header.error_code,
counters: inv_header.counters,
identity,
})
}
}
impl SmaInvIdentify {
pub const OPCODE: u32 = 0x020000;
pub const LENGTH_MIN: usize = SmaPacketHeader::LENGTH
+ SmaInvHeader::LENGTH
+ Self::PAYLOAD_MIN
+ SmaPacketFooter::LENGTH;
pub const LENGTH_MAX: usize = SmaPacketHeader::LENGTH
+ SmaInvHeader::LENGTH
+ Self::PAYLOAD_MAX
+ SmaPacketFooter::LENGTH;
pub const PAYLOAD_MIN: usize = 8;
pub const PAYLOAD_MAX: usize = 48;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sma_inv_identify_serialization() {
let cmd = SmaInvIdentify {
dst: SmaEndpoint::broadcast(),
src: SmaEndpoint {
susy_id: 0xDEAD,
serial: 0xDEADBEEF,
},
error_code: 0,
counters: SmaInvCounter {
packet_id: 0,
..Default::default()
},
identity: None,
};
let mut buffer = [0u8; SmaInvIdentify::LENGTH_MIN];
let mut cursor = Cursor::new(&mut buffer[..]);
if let Err(e) = cmd.serialize(&mut cursor) {
panic!("SmaInvIdentify serialization failed: {e:?}");
}
#[rustfmt::skip]
let expected = [
0x53, 0x4D, 0x41, 0x00, 0x00, 0x04, 0x02, 0xA0,
0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, 0x10,
0x60, 0x65,
0x09, 0xA0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0xDE, 0xAD, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
assert_eq!(SmaInvIdentify::LENGTH_MIN, cursor.position());
assert_eq!(expected, buffer);
}
#[test]
fn test_sma_inv_identify_deserialization() {
#[rustfmt::skip]
let serialized = [
0x53, 0x4D, 0x41, 0x00, 0x00, 0x04, 0x02, 0xA0,
0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, 0x10,
0x60, 0x65,
0x09, 0xA0,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
0xDE, 0xAD, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let expected = SmaInvIdentify {
dst: SmaEndpoint::broadcast(),
src: SmaEndpoint {
susy_id: 0xDEAD,
serial: 0xDEADBEEF,
},
error_code: 0,
counters: SmaInvCounter {
packet_id: 0,
..Default::default()
},
identity: None,
};
let mut cursor = Cursor::new(&serialized[..]);
match SmaInvIdentify::deserialize(&mut cursor) {
Err(e) => panic!("SmaInvIdentify deserialization failed: {e:?}"),
Ok(cmd) => {
assert_eq!(expected, cmd);
assert_eq!(SmaInvIdentify::LENGTH_MIN, cursor.position());
}
}
}
#[test]
fn test_sma_inv_identify_response_deserialization() {
#[rustfmt::skip]
let serialized = [
0x53, 0x4D, 0x41, 0x00, 0x00, 0x04, 0x02, 0xA0,
0x00, 0x00, 0x00, 0x01, 0x00, 0x4E, 0x00, 0x10,
0x60, 0x65,
0x13, 0xA0,
0xDE, 0xAD, 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0xC0,
0x56, 0x78, 0xAB, 0xCD, 0xAB, 0xCE, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x80,
0x01, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x03, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x56, 0x78,
0xAB, 0xCD, 0xAB, 0xDE, 0x00, 0x00, 0x0A, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let expected = SmaInvIdentify {
dst: SmaEndpoint::dummy(),
src: SmaEndpoint {
susy_id: 0x5678,
serial: 0xABCDABCE,
},
error_code: 0,
counters: SmaInvCounter {
packet_id: 1,
..Default::default()
},
identity: Some([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x56, 0x78, 0xAB, 0xCD, 0xAB, 0xDE, 0x00, 0x00,
0x0A, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
]),
};
let mut cursor = Cursor::new(&serialized[..]);
match SmaInvIdentify::deserialize(&mut cursor) {
Err(e) => panic!("SmaInvIdentify deserialization failed: {e:?}"),
Ok(cmd) => {
assert_eq!(expected, cmd);
assert_eq!(SmaInvIdentify::LENGTH_MAX, cursor.position());
}
}
}
}