use super::command::OpCode;
use super::error::{Error, ErrorKind, Status};
use crate::datalink::Transaction;
use core::convert::{TryFrom, TryInto};
use core::mem::size_of;
use core::ops::RangeTo;
use crc::{Algorithm, Crc};
const PACKET_OFFSET: usize = 1;
const PDU_OFFSET: usize = 6;
const CMD_SIZE_MIN: usize = 7;
const CUSTOM_ALG: Algorithm<u16> = Algorithm {
poly: 0x8005,
init: 0x0000,
refin: true,
refout: false,
xorout: 0x0000,
check: 0xbcdd,
residue: 0x0000,
};
pub const CRC16: Crc<u16> = Crc::<u16>::new(&CUSTOM_ALG);
#[derive(Debug)]
pub(crate) struct PacketBuilder<'a> {
buffer: &'a mut [u8],
pdu_length: Option<usize>,
opcode: Option<OpCode>,
mode: Option<u8>,
param2: Option<u16>,
}
impl<'a> PacketBuilder<'a> {
pub(crate) fn new(buffer: &'a mut [u8]) -> Self {
Self {
buffer,
pdu_length: None,
opcode: None,
mode: None,
param2: None,
}
}
pub(crate) fn opcode(&mut self, opcode: OpCode) -> &mut Self {
self.opcode.replace(opcode);
self
}
pub(crate) fn mode(&mut self, mode: u8) -> &mut Self {
self.mode.replace(mode);
self
}
pub(crate) fn param2(&mut self, param2: u16) -> &mut Self {
self.param2.replace(param2);
self
}
pub(crate) fn pdu_data(&mut self, data: impl AsRef<[u8]>) -> &mut Self {
let data_length = data.as_ref().len();
self.buffer[PDU_OFFSET..PDU_OFFSET + data_length]
.as_mut()
.copy_from_slice(data.as_ref());
self.pdu_length.replace(data_length);
self
}
pub(crate) fn pdu_length(&mut self, length: usize) -> &mut Self {
self.pdu_length.replace(length);
self
}
pub(crate) fn packet_buffer(&mut self) -> &mut [u8] {
self.buffer[PACKET_OFFSET..].as_mut()
}
pub(crate) fn pdu_buffer(&mut self) -> &mut [u8] {
self.buffer[PDU_OFFSET..].as_mut()
}
pub(crate) fn build(&mut self) -> Result<Packet, Error> {
let packet_length = self
.pdu_length
.iter()
.fold(CMD_SIZE_MIN, |min, pdu_len| min + pdu_len);
let opcode = self.opcode.ok_or(Error::from(ErrorKind::BadOpcode))?;
let mode = self.mode.unwrap_or_default();
let param2 = self.param2.unwrap_or_default();
self.buffer[0] = Transaction::Command as u8;
let packet = self.packet_buffer();
packet[0] = packet_length as u8;
packet[1] = opcode as u8;
packet[2] = mode;
packet[3..5]
.as_mut()
.copy_from_slice(param2.to_le_bytes().as_ref());
let crc_offset = packet_length - size_of::<u16>();
let crc = CRC16.checksum(&packet[..crc_offset]);
packet[crc_offset..packet_length]
.as_mut()
.copy_from_slice(crc.to_le_bytes().as_ref());
Ok(Packet {
opcode,
range: (..packet_length + PACKET_OFFSET),
})
}
}
impl<'a> From<&'a mut [u8]> for PacketBuilder<'a> {
fn from(buffer: &'a mut [u8]) -> Self {
Self::new(buffer)
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Packet {
opcode: OpCode,
range: RangeTo<usize>,
}
impl Packet {
pub(crate) fn opcode(&self) -> &OpCode {
&self.opcode
}
pub(crate) fn buffer(self, buffer: &[u8]) -> &[u8] {
buffer[self.range].as_ref()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Response<'a> {
pdu: &'a [u8],
}
impl<'a> Response<'a> {
pub(crate) fn new(buffer: &'a [u8]) -> Result<Self, Error> {
if buffer.len() < 0x04 {
return Err(ErrorKind::RxFail.into());
}
let (payload, crc_bytes) = buffer.split_at(buffer.len() - size_of::<u16>());
let crc = crc_bytes
.try_into()
.map(u16::from_le_bytes)
.unwrap_or_else(|_| unreachable!());
if crc != CRC16.checksum(&payload) {
return Err(ErrorKind::RxCrcError.into());
}
let (header, pdu) = payload.split_at(1);
if header[0] == 0x04 {
if let Ok(status) = Status::try_from(pdu[0]) {
return Err(status.into());
}
}
Ok(Self { pdu })
}
}
impl<'a> AsRef<[u8]> for Response<'a> {
fn as_ref(&self) -> &[u8] {
self.pdu
}
}