use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned};
use pv::application::PacketType;
use zerocopy::byteorder::big_endian::U16;
mod receiver;
use crate::gateway::link::{Address, GatewayID};
use crate::pv;
use crate::pv::link::SlotCounter;
pub use receiver::{Counters, Receiver, Sink};
#[derive(
Debug,
Copy,
Clone,
Eq,
PartialEq,
Ord,
PartialOrd,
FromBytes,
IntoBytes,
Unaligned,
KnownLayout,
Immutable,
Serialize,
Deserialize,
JsonSchema,
)]
#[repr(transparent)]
#[serde(transparent)]
pub struct CommandSequenceNumber(pub u8);
#[derive(
Debug, Copy, Clone, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable,
)]
#[repr(C)]
pub struct CommandRequest {
pub unknown: [u8; 3],
pub packet_type: PacketType,
pub sequence_number: CommandSequenceNumber,
}
#[derive(
Debug, Copy, Clone, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable,
)]
#[repr(C)]
pub struct CommandResponse {
pub unknown_1: u8,
pub tx_buffers_free: u8,
pub unknown_2: u8,
pub packet_type: PacketType,
pub command_sequence_number: CommandSequenceNumber,
}
#[derive(
Debug, Copy, Clone, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable,
)]
#[repr(C)]
pub struct ReceiveRequest {
pub unknown_1: [u8; 2],
pub packet_number: U16,
pub unknown_2: u8,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct ReceiveResponse {
pub rx_buffers_used: Option<u8>,
pub tx_buffers_free: Option<u8>,
pub unknown_a: Option<[u8; 2]>,
pub unknown_b: Option<[u8; 2]>,
pub packet_number: u16,
pub slot_counter: SlotCounter,
}
fn interpret_packet_number_lo(new_lo: u8, old: u16) -> u16 {
let [old_hi, old_lo] = old.to_be_bytes();
let new_hi = if new_lo >= old_lo {
old_hi
} else {
old_hi + 1
};
u16::from_be_bytes([new_hi, new_lo])
}
#[derive(thiserror::Error, Debug, Copy, Clone, Eq, PartialEq)]
pub enum InvalidReceiveResponse {
#[error("too short: expected at least {0} bytes")]
TooShort(usize),
#[error("invalid status type: {0:#06x}")]
UnknownStatusType(u16),
}
impl ReceiveResponse {
pub fn read_from_bytes(
bytes: &[u8],
packet_number: u16,
) -> Result<(Self, pv::network::ReceivedPackets), InvalidReceiveResponse> {
if bytes.len() < 2 {
return Err(InvalidReceiveResponse::TooShort(5));
};
let status_type = U16::ref_from_bytes(&bytes[0..2]).unwrap().get();
if status_type & 0xffe0 != 0x00e0 {
return Err(InvalidReceiveResponse::UnknownStatusType(status_type));
}
let (_, mut rest) = bytes.split_at(2);
let expected_length = if status_type & 0x0001 == 0 { 1 } else { 0 }
+ if status_type & 0x0002 == 0 { 1 } else { 0 }
+ if status_type & 0x0004 == 0 { 2 } else { 0 }
+ if status_type & 0x0008 == 0 { 2 } else { 0 }
+ if status_type & 0x0010 == 0 { 2 } else { 1 }
+ 2;
if rest.len() < expected_length {
return Err(InvalidReceiveResponse::TooShort(expected_length + 2));
}
let rx_buffers_used = if status_type & 0x0001 == 0 {
let (value, new_rest) = rest.split_at(1);
rest = new_rest;
Some(value[0])
} else {
None
};
let tx_buffers_free = if status_type & 0x0002 == 0 {
let (value, new_rest) = rest.split_at(1);
rest = new_rest;
Some(value[0])
} else {
None
};
let unknown_a = if status_type & 0x0004 == 0 {
let (value, new_rest) = rest.split_at(2);
rest = new_rest;
Some([value[0], value[1]])
} else {
None
};
let unknown_b = if status_type & 0x0008 == 0 {
let (value, new_rest) = rest.split_at(2);
rest = new_rest;
Some([value[0], value[1]])
} else {
None
};
let packet_number = if status_type & 0x0010 == 0 {
let (value, new_rest) = rest.split_at(2);
rest = new_rest;
u16::from_be_bytes([value[0], value[1]])
} else {
let (value, new_rest) = rest.split_at(1);
rest = new_rest;
interpret_packet_number_lo(value[0], packet_number)
};
let (slot_counter, new_rest) = rest.split_at(2);
rest = new_rest;
let slot_counter = SlotCounter::read_from_bytes(slot_counter).unwrap();
Ok((
Self {
rx_buffers_used,
tx_buffers_free,
unknown_a,
unknown_b,
packet_number,
slot_counter,
},
pv::network::ReceivedPackets(rest),
))
}
}
#[derive(
Debug, Copy, Clone, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable,
)]
#[repr(C)]
pub struct IdentifyResponse {
pub pv_long_address: pv::LongAddress,
pub gateway_address: [u8; 2],
}
impl IdentifyResponse {
pub fn gateway_id(&self) -> Option<GatewayID> {
match Address::from(self.gateway_address) {
Address::From(_) => None,
Address::To(id) => Some(id),
}
}
}
#[derive(
Debug, Copy, Clone, Eq, PartialEq, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable,
)]
#[repr(C)]
pub struct EnumerationStartRequest {
pub unknown: [u8; 4],
pub enumeration_address: [u8; 2],
}
impl EnumerationStartRequest {
pub fn enumeration_gateway_id(&self) -> Option<GatewayID> {
match Address::from(self.enumeration_address) {
Address::From(_) => None,
Address::To(id) => Some(id),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pv::network::ReceivedPackets;
#[test]
fn rx_request_from_bytes() {
assert_eq!(
ReceiveRequest::read_from_bytes(&[0x00, 0x01, 0x18, 0x83, 0x04]),
Ok(ReceiveRequest {
unknown_1: [0x00, 0x01],
packet_number: 0x1883.into(),
unknown_2: 0x04,
})
);
}
#[test]
fn rx_response_from_bytes() {
assert_eq!(
ReceiveResponse::read_from_bytes(
&[0x00, 0xE0, 0x04, 0x0E, 0x00, 0x01, 0x02, 0x00, 0x40, 0xFB, 0x21, 0x1B, 1, 2, 3],
0x40FB,
),
Ok((
ReceiveResponse {
rx_buffers_used: Some(0x04),
tx_buffers_free: Some(0x0E),
unknown_a: Some([0x00, 0x01]),
unknown_b: Some([0x02, 0x00]),
packet_number: 0x40FB,
slot_counter: 0x211B.into(),
},
ReceivedPackets(&[1, 2, 3])
))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xFE, 0x02, 0xFF, 0x21, 0x22, 4], 0x40FB),
Ok((
ReceiveResponse {
rx_buffers_used: Some(0x02),
tx_buffers_free: None,
unknown_a: None,
unknown_b: None,
packet_number: 0x40FF,
slot_counter: 0x2122.into(),
},
ReceivedPackets(&[4])
))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE, 0x00, 0x41, 0x01, 0x21, 0x27], 0x40FB),
Ok((
ReceiveResponse {
rx_buffers_used: Some(0x00),
tx_buffers_free: None,
unknown_a: None,
unknown_b: None,
packet_number: 0x4101,
slot_counter: 0x2127.into(),
},
ReceivedPackets(&[])
))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE, 0x00, 0x41, 0x01, 0x21], 0x40FB),
Err(InvalidReceiveResponse::TooShort(7))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE, 0x00, 0x41, 0x01], 0x40FB),
Err(InvalidReceiveResponse::TooShort(7))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE, 0x00, 0x41], 0x40FB),
Err(InvalidReceiveResponse::TooShort(7))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE, 0x00], 0x40FB),
Err(InvalidReceiveResponse::TooShort(7))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xEE], 0x40FB),
Err(InvalidReceiveResponse::TooShort(7))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xFF, 0x03, 0x21, 0x31], 0x40FB),
Ok((
ReceiveResponse {
rx_buffers_used: None,
tx_buffers_free: None,
unknown_a: None,
unknown_b: None,
packet_number: 0x4103,
slot_counter: 0x2131.into(),
},
ReceivedPackets(&[])
))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xFF, 0x03, 0x21], 0x40FB),
Err(InvalidReceiveResponse::TooShort(5))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xFF, 0x03], 0x40FB),
Err(InvalidReceiveResponse::TooShort(5))
);
assert_eq!(
ReceiveResponse::read_from_bytes(&[0x00, 0xFF], 0x40FB),
Err(InvalidReceiveResponse::TooShort(5))
);
}
#[test]
fn identify_response_payload() {
let expected = IdentifyResponse {
pv_long_address: pv::LongAddress([0x04, 0xC0, 0x5B, 0x30, 0x00, 0x02, 0xBE, 0x16]),
gateway_address: [0x12, 0x01],
};
assert_eq!(
IdentifyResponse::read_from_bytes(&[
0x04, 0xC0, 0x5B, 0x30, 0x00, 0x02, 0xBE, 0x16, 0x12, 0x01
]),
Ok(expected)
);
assert_eq!(expected.gateway_id(), Some(0x1201.try_into().unwrap()));
}
}