use super::maccommands::*;
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
InvalidDataRate,
InvalidTxPower,
MarginOutOfRange,
DelayOutOfRange,
MaxEirpOutOfRange,
NanoSecondsOutOfRange,
BufferTooShort,
}
macro_rules! impl_mac_cmd_creator_boilerplate {
($type:ident, $cid:expr) => {
impl Default for $type {
fn default() -> Self {
Self {}
}
}
impl $type {
pub fn new() -> Self {
Default::default()
}
pub fn build(&self) -> &[u8] {
&[$cid]
}
pub const fn cid(&self) -> u8 {
$cid
}
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
1
}
}
impl_mac_cmd_payload!($type);
};
($type:ident, $cid:expr, $len:expr) => {
impl Default for $type {
fn default() -> Self {
let data = [0; $len];
Self { data }
}
}
impl $type {
pub fn new() -> Self {
let mut data = [0; $len];
data[0] = $cid;
Self { data }
}
pub fn build(&self) -> &[u8] {
&self.data[..]
}
pub const fn cid(&self) -> u8 {
$cid
}
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
$len
}
}
impl_mac_cmd_payload!($type);
};
}
macro_rules! impl_mac_cmd_payload {
($type:ident) => {
impl SerializableMacCommand for $type {
fn payload_bytes(&self) -> &[u8] {
&self.build()[1..]
}
fn cid(&self) -> u8 {
self.build()[0]
}
fn payload_len(&self) -> usize {
self.build().len() - 1
}
}
};
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkCheckReqCreator {}
impl_mac_cmd_creator_boilerplate!(LinkCheckReqCreator, 0x02);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkCheckAnsCreator {
data: [u8; 3],
}
impl_mac_cmd_creator_boilerplate!(LinkCheckAnsCreator, 0x02, 3);
impl LinkCheckAnsCreator {
pub fn set_margin(&mut self, margin: u8) -> &mut Self {
self.data[1] = margin;
self
}
pub fn set_gateway_count(&mut self, gateway_count: u8) -> &mut Self {
self.data[2] = gateway_count;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkADRReqCreator {
data: [u8; 5],
}
impl_mac_cmd_creator_boilerplate!(LinkADRReqCreator, 0x03, 5);
impl LinkADRReqCreator {
pub fn set_data_rate(&mut self, data_rate: u8) -> Result<&mut Self, Error> {
if data_rate > 0x0f {
return Err(Error::InvalidDataRate);
}
self.data[1] &= 0x0f;
self.data[1] |= data_rate << 4;
Ok(self)
}
pub fn set_tx_power(&mut self, tx_power: u8) -> Result<&mut Self, Error> {
if tx_power > 0x0f {
return Err(Error::InvalidTxPower);
}
self.data[1] &= 0xf0;
self.data[1] |= tx_power & 0x0f;
Ok(self)
}
pub fn set_channel_mask<T: Into<ChannelMask<2>>>(&mut self, channel_mask: T) -> &mut Self {
let converted = channel_mask.into();
self.data[2] = converted.as_ref()[0];
self.data[3] = converted.as_ref()[1];
self
}
pub fn set_redundancy<T: Into<Redundancy>>(&mut self, redundancy: T) -> &mut Self {
let converted = redundancy.into();
self.data[4] = converted.raw_value();
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LinkADRAnsCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(LinkADRAnsCreator, 0x03, 2);
impl LinkADRAnsCreator {
pub fn set_channel_mask_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= ack as u8;
self
}
pub fn set_data_rate_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfd;
self.data[1] |= (ack as u8) << 1;
self
}
pub fn set_tx_power_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfb;
self.data[1] |= (ack as u8) << 2;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DutyCycleReqCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(DutyCycleReqCreator, 0x04, 2);
impl DutyCycleReqCreator {
pub fn set_max_duty_cycle(&mut self, max_duty_cycle: u8) -> Result<&mut Self, Error> {
self.data[1] = max_duty_cycle;
Ok(self)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DutyCycleAnsCreator {}
impl_mac_cmd_creator_boilerplate!(DutyCycleAnsCreator, 0x04);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RXParamSetupReqCreator {
data: [u8; 5],
}
impl_mac_cmd_creator_boilerplate!(RXParamSetupReqCreator, 0x05, 5);
impl RXParamSetupReqCreator {
pub fn set_dl_settings<T: Into<DLSettings>>(&mut self, dl_settings: T) -> &mut Self {
let converted = dl_settings.into();
self.data[1] = converted.raw_value();
self
}
pub fn set_frequency<'a, T: Into<Frequency<'a>>>(&mut self, frequency: T) -> &mut Self {
let converted = frequency.into();
self.data[2..5].copy_from_slice(converted.as_ref());
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RXParamSetupAnsCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(RXParamSetupAnsCreator, 0x05, 2);
impl RXParamSetupAnsCreator {
pub fn set_channel_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= ack as u8;
self
}
pub fn set_rx2_data_rate_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfd;
self.data[1] |= (ack as u8) << 1;
self
}
pub fn set_rx1_data_rate_offset_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfb;
self.data[1] |= (ack as u8) << 2;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DevStatusReqCreator {}
impl_mac_cmd_creator_boilerplate!(DevStatusReqCreator, 0x06);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DevStatusAnsCreator {
data: [u8; 3],
}
impl_mac_cmd_creator_boilerplate!(DevStatusAnsCreator, 0x06, 3);
impl DevStatusAnsCreator {
pub fn set_battery(&mut self, battery: u8) -> &mut Self {
self.data[1] = battery;
self
}
pub fn set_margin(&mut self, margin: i8) -> Result<&mut Self, Error> {
if !(-32..=31).contains(&margin) {
return Err(Error::MarginOutOfRange);
}
self.data[2] = ((margin << 2) as u8) >> 2;
Ok(self)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NewChannelReqCreator {
data: [u8; 6],
}
impl_mac_cmd_creator_boilerplate!(NewChannelReqCreator, 0x07, 6);
impl NewChannelReqCreator {
pub fn set_channel_index(&mut self, channel_index: u8) -> &mut Self {
self.data[1] = channel_index;
self
}
pub fn set_frequency<'a, T: Into<Frequency<'a>>>(&mut self, frequency: T) -> &mut Self {
let converted = frequency.into();
self.data[2..5].copy_from_slice(converted.as_ref());
self
}
pub fn set_data_rate_range<T: Into<DataRateRange>>(&mut self, data_rate_range: T) -> &mut Self {
self.data[5] = data_rate_range.into().raw_value();
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NewChannelAnsCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(NewChannelAnsCreator, 0x07, 2);
impl NewChannelAnsCreator {
pub fn set_channel_frequency_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= ack as u8;
self
}
pub fn set_data_rate_range_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfd;
self.data[1] |= (ack as u8) << 1;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RXTimingSetupReqCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(RXTimingSetupReqCreator, 0x08, 2);
impl RXTimingSetupReqCreator {
pub fn set_delay(&mut self, delay: u8) -> Result<&mut Self, Error> {
if delay > 0x0f {
return Err(Error::DelayOutOfRange);
}
self.data[1] &= 0xf0;
self.data[1] |= delay;
Ok(self)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RXTimingSetupAnsCreator {}
impl_mac_cmd_creator_boilerplate!(RXTimingSetupAnsCreator, 0x08);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TXParamSetupReqCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(TXParamSetupReqCreator, 0x09, 2);
impl TXParamSetupReqCreator {
pub fn set_downlink_dwell_time(&mut self) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= (1 << 5) as u8;
self
}
pub fn set_uplink_dwell_time(&mut self) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= (1 << 4) as u8;
self
}
pub fn set_max_eirp(&mut self, max_eirp: u8) -> Result<&mut Self, Error> {
if max_eirp > 0x0F {
return Err(Error::MaxEirpOutOfRange);
}
self.data[1] &= 0xf0;
self.data[1] |= max_eirp;
Ok(self)
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TXParamSetupAnsCreator;
impl_mac_cmd_creator_boilerplate!(TXParamSetupAnsCreator, 0x09);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DlChannelReqCreator {
data: [u8; 5],
}
impl_mac_cmd_creator_boilerplate!(DlChannelReqCreator, 0x0A, 5);
impl DlChannelReqCreator {
pub fn set_channel_index(&mut self, index: u8) -> &mut Self {
self.data[1] = index;
self
}
pub fn set_frequency<'a, T: Into<Frequency<'a>>>(&mut self, frequency: T) -> &mut Self {
let converted = frequency.into();
self.data[2..5].copy_from_slice(converted.as_ref());
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DlChannelAnsCreator {
data: [u8; 2],
}
impl_mac_cmd_creator_boilerplate!(DlChannelAnsCreator, 0x0A, 2);
impl DlChannelAnsCreator {
pub fn set_channel_frequency_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfe;
self.data[1] |= ack as u8;
self
}
pub fn set_uplink_frequency_exists_ack(&mut self, ack: bool) -> &mut Self {
self.data[1] &= 0xfd;
self.data[1] |= (ack as u8) << 1;
self
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DeviceTimeReqCreator;
impl_mac_cmd_creator_boilerplate!(DeviceTimeReqCreator, 0x0D);
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DeviceTimeAnsCreator {
data: [u8; 6],
}
impl_mac_cmd_creator_boilerplate!(DeviceTimeAnsCreator, 0x0D, 6);
impl DeviceTimeAnsCreator {
pub fn set_seconds(&mut self, seconds: u32) -> &mut Self {
self.data[1..5].copy_from_slice(&seconds.to_le_bytes());
self
}
pub fn set_nano_seconds(&mut self, nano_seconds: u32) -> Result<&mut Self, Error> {
if nano_seconds > 1000000000 {
return Err(Error::NanoSecondsOutOfRange);
}
self.data[5] = (nano_seconds / 3906250) as u8;
Ok(self)
}
}
pub fn build_mac_commands<T: AsMut<[u8]>>(
cmds: &[&dyn SerializableMacCommand],
mut out: T,
) -> Result<usize, Error> {
let res = out.as_mut();
if mac_commands_len(cmds) > res.len() {
return Err(Error::BufferTooShort);
}
let mut i = 0;
for mc in cmds {
res[i] = mc.cid();
let start = i + 1;
let end = start + mc.payload_len();
res[start..end].copy_from_slice(mc.payload_bytes());
i = end;
}
Ok(i)
}
macro_rules! mac_cmds_creator_enum {
(
$outer_vis:vis enum $outer_type:ident$(<$outer_lifetime:lifetime>),* {
$(
$name:ident, $creator:ident
)*
}
) => {
#[allow(dead_code, missing_docs)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
$outer_vis enum $outer_type$(<$outer_lifetime>)* {
$(
$name($creator),
)*
}
#[allow(clippy::len_without_is_empty)]
impl$(<$outer_lifetime>)* $outer_type$(<$outer_lifetime>)* {
pub fn len(&self) -> usize {
match self {
$(
Self::$name(creator) => creator.len(),
)*
}
}
pub fn build(&self) -> &[u8] {
match *self {
$(
Self::$name(ref v) => v.build(),
)*
}
}
}
impl SerializableMacCommand for $outer_type$(<$outer_lifetime>)* {
fn payload_bytes(&self) -> &[u8] {
&self.build()[1..]
}
fn cid(&self) -> u8 {
match self {
$(
Self::$name(creator) => creator.cid(),
)*
}
}
fn payload_len(&self) -> usize {
self.len() - 1
}
}
}
}
mac_cmds_creator_enum! {
pub enum UplinkMacCommandCreator {
LinkCheckReq, LinkCheckReqCreator
LinkADRAns, LinkADRAnsCreator
DutyCycleAns, DutyCycleAnsCreator
RXParamSetupAns, RXParamSetupAnsCreator
DevStatusAns, DevStatusAnsCreator
NewChannelAns, NewChannelAnsCreator
RXTimingSetupAns, RXTimingSetupAnsCreator
TXParamSetupAns, TXParamSetupAnsCreator
DlChannelAns, DlChannelAnsCreator
DeviceTimeReq, DeviceTimeReqCreator
}
}
mac_cmds_creator_enum! {
pub enum DownlinkMacCommandCreator {
LinkCheckAns, LinkCheckAnsCreator
LinkADRReq, LinkADRReqCreator
DutyCycleReq, DutyCycleReqCreator
RXParamSetupReq, RXParamSetupReqCreator
DevStatusReq, DevStatusReqCreator
NewChannelReq, NewChannelReqCreator
RXTimingSetupReq, RXTimingSetupReqCreator
TXParamSetupReq, TXParamSetupReqCreator
DlChannelReq, DlChannelReqCreator
DeviceTimeAns, DeviceTimeAnsCreator
}
}