#[derive(Debug, PartialEq)]
pub enum MacCommand<'a> {
LinkCheckReq(LinkCheckReqPayload),
LinkCheckAns(LinkCheckAnsPayload<'a>),
LinkADRReq(LinkADRReqPayload<'a>),
LinkADRAns(LinkADRAnsPayload<'a>),
DutyCycleReq(DutyCycleReqPayload<'a>),
DutyCycleAns(DutyCycleAnsPayload),
RXParamSetupReq(RXParamSetupReqPayload<'a>),
RXParamSetupAns(RXParamSetupAnsPayload<'a>),
DevStatusReq(DevStatusReqPayload),
DevStatusAns(DevStatusAnsPayload<'a>),
NewChannelReq(NewChannelReqPayload<'a>),
NewChannelAns(NewChannelAnsPayload<'a>),
RXTimingSetupReq(RXTimingSetupReqPayload<'a>),
RXTimingSetupAns(RXTimingSetupAnsPayload),
}
impl<'a> MacCommand<'a> {
#![allow(clippy::clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
match *self {
MacCommand::LinkCheckReq(_) => LinkCheckReqPayload::len(),
MacCommand::LinkCheckAns(_) => LinkCheckAnsPayload::len(),
MacCommand::LinkADRReq(_) => LinkADRReqPayload::len(),
MacCommand::LinkADRAns(_) => LinkADRAnsPayload::len(),
MacCommand::DutyCycleReq(_) => DutyCycleReqPayload::len(),
MacCommand::DutyCycleAns(_) => DutyCycleAnsPayload::len(),
MacCommand::RXParamSetupReq(_) => RXParamSetupReqPayload::len(),
MacCommand::RXParamSetupAns(_) => RXParamSetupAnsPayload::len(),
MacCommand::DevStatusReq(_) => DevStatusReqPayload::len(),
MacCommand::DevStatusAns(_) => DevStatusAnsPayload::len(),
MacCommand::NewChannelReq(_) => NewChannelReqPayload::len(),
MacCommand::NewChannelAns(_) => NewChannelAnsPayload::len(),
MacCommand::RXTimingSetupReq(_) => RXTimingSetupReqPayload::len(),
MacCommand::RXTimingSetupAns(_) => RXTimingSetupAnsPayload::len(),
}
}
pub fn bytes(&self) -> &[u8] {
match *self {
MacCommand::LinkCheckReq(_) => &[],
MacCommand::LinkCheckAns(ref v) => &v.0[..],
MacCommand::LinkADRReq(ref v) => &v.0[..],
MacCommand::LinkADRAns(ref v) => &v.0[..],
MacCommand::DutyCycleReq(ref v) => &v.0[..],
MacCommand::DutyCycleAns(_) => &[],
MacCommand::RXParamSetupReq(ref v) => &v.0[..],
MacCommand::RXParamSetupAns(ref v) => &v.0[..],
MacCommand::DevStatusReq(_) => &[],
MacCommand::DevStatusAns(ref v) => &v.0[..],
MacCommand::NewChannelReq(ref v) => &v.0[..],
MacCommand::NewChannelAns(ref v) => &v.0[..],
MacCommand::RXTimingSetupReq(ref v) => &v.0[..],
MacCommand::RXTimingSetupAns(_) => &[],
}
}
}
pub trait SerializableMacCommand {
fn payload_bytes(&self) -> &[u8];
fn cid(&self) -> u8;
fn payload_len(&self) -> usize;
}
impl<'a> SerializableMacCommand for MacCommand<'a> {
fn payload_bytes(&self) -> &[u8] {
self.bytes()
}
fn cid(&self) -> u8 {
match *self {
MacCommand::LinkCheckReq(_) => LinkCheckReqPayload::cid(),
MacCommand::LinkCheckAns(_) => LinkCheckAnsPayload::cid(),
MacCommand::LinkADRReq(_) => LinkADRReqPayload::cid(),
MacCommand::LinkADRAns(_) => LinkADRAnsPayload::cid(),
MacCommand::DutyCycleReq(_) => DutyCycleReqPayload::cid(),
MacCommand::DutyCycleAns(_) => DutyCycleAnsPayload::cid(),
MacCommand::RXParamSetupReq(_) => RXParamSetupReqPayload::cid(),
MacCommand::RXParamSetupAns(_) => RXParamSetupAnsPayload::cid(),
MacCommand::DevStatusReq(_) => DevStatusReqPayload::cid(),
MacCommand::DevStatusAns(_) => DevStatusAnsPayload::cid(),
MacCommand::NewChannelReq(_) => NewChannelReqPayload::cid(),
MacCommand::NewChannelAns(_) => NewChannelAnsPayload::cid(),
MacCommand::RXTimingSetupReq(_) => RXTimingSetupReqPayload::cid(),
MacCommand::RXTimingSetupAns(_) => RXTimingSetupAnsPayload::cid(),
}
}
fn payload_len(&self) -> usize {
self.len()
}
}
pub fn mac_commands_len(cmds: &[&dyn SerializableMacCommand]) -> usize {
cmds.iter().map(|mc| mc.payload_len() + 1).sum()
}
macro_rules! mac_cmd_zero_len {
(
$(
$(#[$outer:meta])*
struct $type:ident[cmd=$name:ident, cid=$cid:expr, uplink=$uplink:expr]
)*
) => {
$(
$(#[$outer])*
pub struct $type();
impl $type {
pub fn new(_: &[u8]) -> Result<$type, &str> {
Ok($type())
}
pub fn new_as_mac_cmd<'a>(data: &[u8]) -> Result<(MacCommand<'a>, usize), &str> {
Ok((MacCommand::$name($type::new(data)?), 0))
}
pub const fn cid() -> u8 {
$cid
}
pub const fn uplink() -> bool {
$uplink
}
pub const fn len() -> usize {
0
}
}
)*
fn parse_zero_len_mac_cmd<'a, 'b>(data: &'a [u8], uplink: bool) -> Result<(usize, MacCommand<'a>), &'b str> {
match (data[0], uplink) {
$(
($cid, $uplink) => Ok((0, MacCommand::$name($type::new(&[])?))),
)*
_ => Err("uknown mac command")
}
}
}
}
macro_rules! mac_cmds {
(
$(
$(#[$outer:meta])*
struct $type:ident[cmd=$name:ident, cid=$cid:expr, uplink=$uplink:expr, size=$size:expr]
)*
) => {
$(
$(#[$outer])*
pub struct $type<'a>(&'a [u8]);
impl<'a> $type<'a> {
pub fn new<'b>(data: &'a [u8]) -> Result<$type<'a>, &'b str> {
if data.len() < $size {
Err("incorrect size for")
} else {
Ok($type(&data[..]))
}
}
pub fn new_as_mac_cmd<'b>(data: &'a [u8]) -> Result<(MacCommand<'a>, usize), &'b str> {
Ok((MacCommand::$name($type::new(data)?), $size))
}
pub const fn cid() -> u8 {
$cid
}
pub const fn uplink() -> bool {
$uplink
}
pub const fn len() -> usize {
$size
}
}
)*
fn parse_one_mac_cmd<'a, 'b>(data: &'a [u8], uplink: bool) -> Result<(usize, MacCommand<'a>), &'b str> {
match (data[0], uplink) {
$(
($cid, $uplink) if data.len() > $size => Ok(($size, MacCommand::$name($type::new(&data[1.. 1 + $size])?))),
)*
_ => parse_zero_len_mac_cmd(data, uplink)
}
}
}
}
mac_cmd_zero_len! {
#[derive(Debug, PartialEq)]
struct LinkCheckReqPayload[cmd=LinkCheckReq, cid=0x02, uplink=true]
#[derive(Debug, PartialEq)]
struct DutyCycleAnsPayload[cmd=DutyCycleAns, cid=0x04, uplink=true]
#[derive(Debug, PartialEq)]
struct DevStatusReqPayload[cmd=DevStatusReq, cid=0x06, uplink=false]
#[derive(Debug, PartialEq)]
struct RXTimingSetupAnsPayload[cmd=RXTimingSetupAns, cid=0x08, uplink=true]
}
mac_cmds! {
#[derive(Debug, PartialEq)]
struct LinkCheckAnsPayload[cmd=LinkCheckAns, cid=0x02, uplink=false, size=2]
#[derive(Debug, PartialEq)]
struct LinkADRReqPayload[cmd=LinkADRReq, cid=0x03, uplink=false, size=4]
#[derive(Debug, PartialEq)]
struct LinkADRAnsPayload[cmd=LinkADRAns, cid=0x03, uplink=true, size=1]
#[derive(Debug, PartialEq)]
struct DutyCycleReqPayload[cmd=DutyCycleReq, cid=0x04, uplink=false, size=1]
#[derive(Debug, PartialEq)]
struct RXParamSetupReqPayload[cmd=RXParamSetupReq, cid=0x05, uplink=false, size=4]
#[derive(Debug, PartialEq)]
struct RXParamSetupAnsPayload[cmd=RXParamSetupAns, cid=0x05, uplink=true, size=1]
#[derive(Debug, PartialEq)]
struct DevStatusAnsPayload[cmd=DevStatusAns, cid=0x06, uplink=false, size=2]
#[derive(Debug, PartialEq)]
struct NewChannelReqPayload[cmd=NewChannelReq, cid=0x07, uplink=false, size=5]
#[derive(Debug, PartialEq)]
struct NewChannelAnsPayload[cmd=NewChannelAns, cid=0x07, uplink=true, size=1]
#[derive(Debug, PartialEq)]
struct RXTimingSetupReqPayload[cmd=RXTimingSetupReq, cid=0x08, uplink=false, size=1]
}
macro_rules! create_ack_fn {
(
$(#[$outer:meta])*
$fn_name:ident, $offset:expr
) => (
$(#[$outer])*
pub fn $fn_name(&self) -> bool {
self.0[0] & (0x01 << $offset) != 0
}
)
}
macro_rules! create_value_reader_fn {
(
$(#[$outer:meta])*
$fn_name:ident, $index:expr
) => (
$(#[$outer])*
pub fn $fn_name(&self) -> u8 {
self.0[$index]
}
)
}
pub fn parse_mac_commands(data: &[u8], uplink: bool) -> MacCommandIterator {
MacCommandIterator {
index: 0,
data,
uplink,
}
}
pub struct MacCommandIterator<'a> {
data: &'a [u8],
index: usize,
uplink: bool,
}
impl<'a> Iterator for MacCommandIterator<'a> {
type Item = MacCommand<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.index < self.data.len() {
if let Ok((l, v)) = parse_one_mac_cmd(&self.data[self.index..], self.uplink) {
self.index += 1 + l;
return Some(v);
}
}
None
}
}
impl<'a> LinkCheckAnsPayload<'a> {
create_value_reader_fn!(
margin,
0
);
create_value_reader_fn!(
gateway_count,
1
);
}
impl<'a> From<&'a [u8; 2]> for LinkCheckAnsPayload<'a> {
fn from(v: &'a [u8; 2]) -> Self {
LinkCheckAnsPayload(v)
}
}
impl<'a> LinkADRReqPayload<'a> {
pub fn data_rate(&self) -> u8 {
self.0[0] >> 4
}
pub fn tx_power(&self) -> u8 {
self.0[0] & 0x0f
}
pub fn channel_mask(&self) -> ChannelMask {
ChannelMask::new_from_raw(&self.0[1..3])
}
pub fn redundancy(&self) -> Redundancy {
Redundancy::new(self.0[3])
}
}
#[derive(Debug, PartialEq)]
pub struct ChannelMask([u8; 2]);
impl ChannelMask {
pub fn new(data: &[u8]) -> Result<Self, &str> {
if data.len() < 2 {
return Err("not enough bytes to read");
}
Ok(Self::new_from_raw(data))
}
pub fn new_from_raw(data: &[u8]) -> Self {
let payload = [data[0], data[1]];
ChannelMask(payload)
}
fn channel_enabled(&self, index: usize) -> bool {
self.0[index >> 3] & (1 << (index & 0x07)) != 0
}
pub fn is_enabled(&self, index: usize) -> Result<bool, &str> {
if index > 15 {
return Err("index should be between 0 and 15");
}
Ok(self.channel_enabled(index))
}
pub fn statuses(&self) -> [bool; 16] {
let mut res = [false; 16];
for (i, c) in res.iter_mut().enumerate() {
*c = self.channel_enabled(i);
}
res
}
}
impl From<[u8; 2]> for ChannelMask {
fn from(v: [u8; 2]) -> Self {
ChannelMask(v)
}
}
impl AsRef<[u8]> for ChannelMask {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
#[derive(Debug, PartialEq)]
pub struct Redundancy(u8);
impl Redundancy {
pub fn new(data: u8) -> Self {
Redundancy(data)
}
pub fn channel_mask_control(&self) -> u8 {
(self.0 >> 4) & 0x07
}
pub fn number_of_transmissions(&self) -> u8 {
self.0 & 0x0f
}
pub fn raw_value(&self) -> u8 {
self.0
}
}
impl From<u8> for Redundancy {
fn from(v: u8) -> Self {
Redundancy(v)
}
}
impl<'a> LinkADRAnsPayload<'a> {
create_ack_fn!(
channel_mask_ack,
0
);
create_ack_fn!(
data_rate_ack,
1
);
create_ack_fn!(
powert_ack,
2
);
pub fn ack(&self) -> bool {
self.0[0] == 0x07
}
}
impl<'a> DutyCycleReqPayload<'a> {
pub fn max_duty_cycle_raw(&self) -> u8 {
self.0[0] & 0x0f
}
pub fn max_duty_cycle(&self) -> f32 {
let divisor = 1 << self.max_duty_cycle_raw();
1.0 / (divisor as f32)
}
}
impl<'a> RXParamSetupReqPayload<'a> {
pub fn dl_settings(&self) -> DLSettings {
DLSettings::new(self.0[0])
}
pub fn frequency(&self) -> Frequency {
Frequency::new_from_raw(&self.0[1..])
}
}
#[derive(Debug, PartialEq)]
pub struct DLSettings(u8);
impl DLSettings {
pub fn new(byte: u8) -> DLSettings {
DLSettings(byte)
}
pub fn rx1_dr_offset(&self) -> u8 {
self.0 >> 4 & 0x07
}
pub fn rx2_data_rate(&self) -> u8 {
self.0 & 0x0f
}
pub fn raw_value(&self) -> u8 {
self.0
}
}
impl From<u8> for DLSettings {
fn from(v: u8) -> Self {
DLSettings(v)
}
}
#[derive(Debug, PartialEq)]
pub struct Frequency<'a>(&'a [u8]);
impl<'a> Frequency<'a> {
pub fn new_from_raw(bytes: &'a [u8]) -> Self {
Frequency(bytes)
}
pub fn new(bytes: &'a [u8]) -> Option<Self> {
if bytes.len() != 3 {
return None;
}
Some(Frequency(bytes))
}
pub fn value(&self) -> u32 {
((u32::from(self.0[2]) << 16) + (u32::from(self.0[1]) << 8) + u32::from(self.0[0])) * 100
}
}
impl<'a> From<&'a [u8; 3]> for Frequency<'a> {
fn from(v: &'a [u8; 3]) -> Self {
Frequency(&v[..])
}
}
impl<'a> AsRef<[u8]> for Frequency<'a> {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl<'a> RXParamSetupAnsPayload<'a> {
create_ack_fn!(
channel_ack,
0
);
create_ack_fn!(
rx2_data_rate_ack,
1
);
create_ack_fn!(
rx1_dr_offset_ack,
2
);
pub fn ack(&self) -> bool {
self.0[0] == 0x07
}
}
impl<'a> DevStatusAnsPayload<'a> {
create_value_reader_fn!(
battery,
0
);
pub fn margin(&self) -> i8 {
((self.0[1] << 2) as i8) >> 2
}
}
impl<'a> NewChannelReqPayload<'a> {
create_value_reader_fn!(
channel_index,
0
);
pub fn frequency(&self) -> Frequency {
Frequency::new_from_raw(&self.0[1..4])
}
pub fn data_rate_range(&self) -> DataRateRange {
DataRateRange::new_from_raw(self.0[4])
}
}
#[derive(Debug, PartialEq)]
pub struct DataRateRange(u8);
impl DataRateRange {
pub fn new_from_raw(byte: u8) -> DataRateRange {
DataRateRange(byte)
}
pub fn new(byte: u8) -> Result<DataRateRange, &'static str> {
if let Err(err) = Self::can_build_from(byte) {
return Err(err);
}
Ok(Self::new_from_raw(byte))
}
pub fn can_build_from(byte: u8) -> Result<(), &'static str> {
if (byte >> 4) < (byte & 0x0f) {
return Err("data rate range can not have max data rate smaller than min data rate");
}
Ok(())
}
pub fn max_data_rate(&self) -> u8 {
self.0 >> 4
}
pub fn min_data_range(&self) -> u8 {
self.0 & 0x0f
}
pub fn raw_value(&self) -> u8 {
self.0
}
}
impl From<u8> for DataRateRange {
fn from(v: u8) -> Self {
DataRateRange(v)
}
}
impl<'a> NewChannelAnsPayload<'a> {
create_ack_fn!(
channel_freq_ack,
0
);
create_ack_fn!(
data_rate_range_ack,
1
);
pub fn ack(&self) -> bool {
self.0[0] == 0x03
}
}
impl<'a> RXTimingSetupReqPayload<'a> {
pub fn delay(&self) -> u8 {
self.0[0] & 0x0f
}
}