use deku::{
bitvec::{BitSlice, BitVec, Msb0},
ctx::{BitSize, Endian},
prelude::*,
};
use crate::{data::FileHeader, file::File, session::InterfaceStatus};
use super::interface::{InterfaceConfiguration, InterfaceType};
pub use super::query::Query;
#[derive(DekuRead, DekuWrite, Default, Debug, Clone, PartialEq, Copy)]
pub struct Length(
#[deku(
reader = "Length::read(deku::rest)",
writer = "Length::write(deku::output, &self.0)"
)]
u32,
);
impl Into<u32> for Length {
fn into(self) -> u32 {
self.0 as u32
}
}
impl From<u32> for Length {
fn from(value: u32) -> Self {
Self(value)
}
}
impl Into<usize> for Length {
fn into(self) -> usize {
self.0 as usize
}
}
impl From<usize> for Length {
fn from(value: usize) -> Self {
Self(value as u32)
}
}
impl Length {
fn required_bits(value: u32) -> u32 {
value.checked_ilog2().unwrap_or(0) + 1
}
fn read(rest: &BitSlice<u8, Msb0>) -> Result<(&BitSlice<u8, Msb0>, u32), DekuError> {
let (rest, size) = <u8 as DekuRead<'_, _>>::read(rest, (Endian::Big, BitSize(2)))?;
let (rest, value) = <u32 as DekuRead<'_, _>>::read(
rest,
(Endian::Big, BitSize((6 + (size * u8::BITS as u8)) as usize)),
)?;
Ok((rest, value))
}
fn write(output: &mut BitVec<u8, Msb0>, value: &u32) -> Result<(), DekuError> {
let num_extra_bits = Length::required_bits(*value).checked_sub(6).unwrap_or(0);
let mut num_extra_bytes = num_extra_bits.checked_div(u8::BITS).unwrap_or(0);
if (num_extra_bits % u8::BITS) > 0 {
num_extra_bytes += 1;
}
DekuWrite::write(&num_extra_bytes, output, (Endian::Big, BitSize(2)))?;
DekuWrite::write(
value,
output,
(
Endian::Big,
BitSize((6 + num_extra_bytes * u8::BITS) as usize),
),
)?;
Ok(())
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct FileOffset {
pub file_id: u8,
pub offset: Length,
}
impl FileOffset {
pub fn no_offset(file_id: u8) -> Self {
Self {
file_id,
offset: 0u32.into(),
}
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(type = "u8")]
pub enum StatusCode {
#[deku(id = "0x00")]
Ok,
#[deku(id = "0x01")]
Received,
#[deku(id = "0x02")]
ItfFull,
#[deku(id = "0xFF")]
FileIdMissing,
#[deku(id = "0xFE")]
CreateFileIdAlreadyExist,
#[deku(id = "0xFD")]
FileIsNotRestorable,
#[deku(id = "0xFC")]
InsufficientPermission,
#[deku(id = "0xFB")]
CreateFileLengthOverflow,
#[deku(id = "0xFA")]
CreateFileAllocationOverflow, #[deku(id = "0xF9")]
WriteOffsetOverflow,
#[deku(id = "0xF8")]
WriteDataOverflow,
#[deku(id = "0xF7")]
WriteStorageUnavailable,
#[deku(id = "0xF6")]
UnknownOperation,
#[deku(id = "0xF5")]
OperandIncomplete,
#[deku(id = "0xF4")]
OperandWrongFormat,
#[deku(id = "0x80")]
UnknownError,
}
impl StatusCode {
pub fn is_err(&self) -> bool {
self.deku_id().unwrap() > 0x80
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct ActionStatus {
pub action_id: u8,
pub status: StatusCode,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(type = "u8")]
pub enum Permission {
#[deku(id = "0x42")] Dash7([u8; 8]),
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(type = "u8")]
pub enum PermissionLevel {
#[deku(id = "0")]
User,
#[deku(id = "1")]
Root,
}
#[derive(DekuRead, DekuWrite, Default, Debug, Clone, PartialEq)]
pub struct ActionHeader {
#[deku(bits = 1)]
pub group: bool,
#[deku(bits = 1, pad_bits_after = "6")]
pub response: bool,
}
impl ActionHeader {
pub fn new(group: bool, response: bool) -> Self {
Self { group, response }
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct Nop {
pub header: ActionHeader,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct FileId {
pub header: ActionHeader,
pub file_id: u8,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct FileData {
pub header: ActionHeader,
pub offset: FileOffset,
#[deku(
reader = "FileData::read(deku::rest, offset)",
writer = "FileData::write(deku::output, &self.data, &self.offset)"
)]
data: File,
}
impl FileData {
pub fn new(header: ActionHeader, offset: FileOffset, data: File) -> Self {
Self {
header,
offset,
data,
}
}
fn read<'a>(
rest: &'a BitSlice<u8, Msb0>,
offset: &FileOffset,
) -> Result<(&'a BitSlice<u8, Msb0>, File), DekuError> {
let (rest, length) = <Length as DekuRead<'_, _>>::read(rest, ())?;
let file_id = offset.file_id.try_into()?;
File::read(rest, (file_id, Into::<u32>::into(length)))
}
fn write(
output: &mut BitVec<u8, Msb0>,
data: &File,
offset: &FileOffset,
) -> Result<(), DekuError> {
let file_id = offset.file_id.try_into()?;
let vec_size = match data {
File::Other(val) => val.len() as u32,
_ => 0,
};
let length_offset = output.len();
DekuWrite::write(&0u8, output, ())?;
let output_offset = output.len();
DekuWrite::write(data, output, (file_id, vec_size))?;
let data_length = Length((output.len() - output_offset) as u32 / u8::BITS);
output[length_offset..length_offset + 8].clone_from_bitslice(&data_length.to_bits()?);
Ok(())
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct FileProperties {
pub header: ActionHeader,
pub file_id: u8,
pub file_header: FileHeader,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct ReadFileData {
pub header: ActionHeader,
pub offset: FileOffset,
pub length: Length,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct ActionQuery {
pub header: ActionHeader,
pub query: Query,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct PermissionRequest {
pub header: ActionHeader,
pub level: PermissionLevel,
pub permission: Permission,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct CopyFile {
pub header: ActionHeader,
pub src_file_id: u8,
pub dst_file_id: u8,
}
#[derive(DekuRead, DekuWrite, Clone, Copy, Debug, PartialEq)]
#[deku(bits = 2, type = "u8")]
pub enum StatusType {
#[deku(id = "0")]
Action,
#[deku(id = "1")]
Interface,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct StatusOperand {
#[deku(update = "self.status.deku_id().unwrap()", pad_bits_after = "6")]
status_type: StatusType,
#[deku(ctx = "*status_type")]
pub status: Status,
}
impl Into<StatusOperand> for Status {
fn into(self) -> StatusOperand {
StatusOperand {
status_type: self.deku_id().unwrap(),
status: self,
}
}
}
impl Into<Status> for StatusOperand {
fn into(self) -> Status {
self.status
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(ctx = "status_type: StatusType", id = "status_type")]
pub enum Status {
#[deku(id = "StatusType::Action")]
Action(ActionStatus),
#[deku(id = "StatusType::Interface")]
Interface(InterfaceStatusOperand),
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct InterfaceStatusOperand {
pub interface_id: u8,
#[deku(
reader = "InterfaceStatusOperand::read(deku::rest, *interface_id)",
writer = "InterfaceStatusOperand::write(deku::output, &self.status, self.interface_id)"
)]
pub status: InterfaceStatus,
}
impl From<InterfaceStatus> for InterfaceStatusOperand {
fn from(status: InterfaceStatus) -> Self {
Self {
interface_id: status.deku_id().unwrap().deku_id().unwrap(),
status,
}
}
}
impl Into<StatusOperand> for InterfaceStatusOperand {
fn into(self) -> StatusOperand {
Status::Interface(self).into()
}
}
impl InterfaceStatusOperand {
pub fn read<'a>(
rest: &'a BitSlice<u8, Msb0>,
interface_id: u8,
) -> Result<(&'a BitSlice<u8, Msb0>, InterfaceStatus), DekuError> {
let (rest, length) = <Length as DekuRead<'_, _>>::read(rest, ())?;
let interface_id = interface_id.try_into()?;
InterfaceStatus::read(rest, (interface_id, Into::<u32>::into(length)))
}
#[cfg(not(feature = "subiot_v0_0"))]
pub fn write(
output: &mut BitVec<u8, Msb0>,
status: &InterfaceStatus,
interface_id: u8,
) -> Result<(), DekuError> {
let interface_id = interface_id.try_into()?;
let vec_size = match status {
InterfaceStatus::Other(val) => val.len() as u32,
_ => 0,
};
let length_offset = output.len();
DekuWrite::write(&0u8, output, ())?;
let output_offset = output.len();
DekuWrite::write(status, output, (interface_id, vec_size))?;
let data_length = Length((output.len() - output_offset) as u32 / u8::BITS);
output[length_offset..length_offset + 8].clone_from_bitslice(&data_length.to_bits()?);
Ok(())
}
#[cfg(feature = "subiot_v0_0")]
pub fn read<'a>(
rest: &'a BitSlice<u8, Msb0>,
interface_id: u8,
) -> Result<(&'a BitSlice<u8, Msb0>, InterfaceStatus), DekuError> {
let interface_id = interface_id.try_into()?;
InterfaceStatus::read(rest, (interface_id, 0))
}
#[cfg(feature = "subiot_v0_0")]
pub fn write(
output: &mut BitVec<u8, Msb0>,
status: &InterfaceStatus,
interface_id: u8,
) -> Result<(), DekuError> {
let interface_id = interface_id.try_into()?;
let output_offset = output.len();
DekuWrite::write(status, output, (interface_id, 0))
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct ResponseTag {
#[deku(bits = 1)]
pub eop: bool,
#[deku(bits = 1, pad_bits_after = "6")]
pub error: bool,
pub id: u8,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(bits = 2, type = "u8")]
pub enum ChunkStep {
#[deku(id = "0")]
Continue,
#[deku(id = "1")]
Start,
#[deku(id = "2")]
End,
#[deku(id = "3")]
StartEnd,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct Chunk {
#[deku(pad_bits_after = "6")]
pub step: ChunkStep,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
#[deku(bits = 2, type = "u8")]
pub enum LogicOp {
#[deku(id = "0")]
Or,
#[deku(id = "1")]
Xor,
#[deku(id = "2")]
Nor,
#[deku(id = "3")]
Nand,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct Logic {
#[deku(pad_bits_after = "6")]
pub logic: LogicOp,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct Forward {
#[deku(bits = 1, pad_bits_before = "1", pad_bits_after = "6")]
pub response: bool,
#[deku(update = "self.configuration.deku_id().unwrap()")]
interface_type: InterfaceType,
#[deku(ctx = "*interface_type")]
pub configuration: InterfaceConfiguration,
}
impl Forward {
pub fn new(response: bool, configuration: InterfaceConfiguration) -> Self {
Self {
response,
interface_type: configuration.deku_id().unwrap(),
configuration,
}
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct IndirectForward {
#[deku(bits = 1, update = "self.configuration.is_some()")]
overloaded: bool,
#[deku(bits = 1, pad_bits_after = "6")]
pub response: bool,
pub interface_file_id: u8,
#[deku(
reader = "IndirectForward::read(deku::rest, *overloaded)",
writer = "IndirectForward::write(deku::output, &self.configuration)"
)]
pub configuration: Option<InterfaceConfiguration>,
}
impl IndirectForward {
pub fn new(
response: bool,
interface_file_id: u8,
configuration: Option<InterfaceConfiguration>,
) -> Self {
Self {
overloaded: configuration.is_some(),
response,
interface_file_id,
configuration,
}
}
fn read(
rest: &BitSlice<u8, Msb0>,
overloaded: bool,
) -> Result<(&BitSlice<u8, Msb0>, Option<InterfaceConfiguration>), DekuError> {
let config = if !overloaded {
None
} else {
Some(InterfaceConfiguration::Unknown)
};
Ok((rest, config))
}
fn write(
output: &mut BitVec<u8, Msb0>,
configuration: &Option<InterfaceConfiguration>,
) -> Result<(), DekuError> {
if let Some(config) = configuration.as_ref() {
DekuWrite::write(config, output, config.deku_id().unwrap())?;
}
Ok(())
}
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct RequestTag {
#[deku(bits = 1, pad_bits_after = "7")]
pub eop: bool,
pub id: u8,
}
#[derive(DekuRead, DekuWrite, Debug, Clone, PartialEq)]
pub struct Extension {
pub header: ActionHeader,
}
#[cfg(test)]
mod test {
use hex_literal::hex;
use super::*;
use crate::{
network::{Address, Addressee, NlsState},
physical::{Channel, ChannelBand, ChannelClass, ChannelCoding, ChannelHeader},
session::Dash7InterfaceStatus,
test_tools::test_item,
transport::GroupCondition,
};
#[test]
fn test_length() {
test_item(Length(1), &[0x01]);
test_item(Length(65), &[0x40, 0x41]);
test_item(Length(4263936), &[0xC0, 0x41, 0x10, 0x00]);
}
#[test]
fn test_file_offset() {
test_item(
FileOffset {
file_id: 2,
offset: 0x3F_FFu32.into(),
},
&hex!("02 7F FF"),
)
}
#[test]
fn test_action_status() {
test_item(
ActionStatus {
action_id: 2,
status: StatusCode::UnknownOperation,
},
&hex!("02 F6"),
)
}
#[test]
fn test_interface_status() {
let data = &hex!("D7 14 32 00 32 2D 3E 50 80 00 00 58 20 01 39 38 38 37 00 39 00 2E");
let item: InterfaceStatusOperand = InterfaceStatus::Dash7(Dash7InterfaceStatus {
channel: Channel {
header: ChannelHeader::new(
ChannelBand::Band868,
ChannelClass::LoRate,
ChannelCoding::FecPn9,
),
index: 50,
},
rx_level: 45,
link_budget: 62,
target_rx_level: 80,
nls: true,
missed: false,
retry: false,
unicast: false,
fifo_token: 0,
sequence_number: 0,
response_timeout: 384.into(),
addressee: Addressee::new(
false,
GroupCondition::Any,
Address::Uid(4123107267735781422u64),
NlsState::None,
1,
),
})
.into();
test_item::<InterfaceStatusOperand>(item, data);
}
}