use super::maccommands::*;
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]
}
}
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[..]
}
}
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
}
}
};
}
pub struct LinkCheckReqCreator {}
impl_mac_cmd_creator_boilerplate!(LinkCheckReqCreator, 0x02);
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, gw_cnt: u8) -> &mut Self {
self.data[2] = gw_cnt;
self
}
}
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, &str> {
if data_rate > 0x0f {
return Err("data_rate out of range");
}
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, &str> {
if tx_power > 0x0f {
return Err("tx_power out of range");
}
self.data[1] &= 0xf0;
self.data[1] |= tx_power & 0x0f;
Ok(self)
}
pub fn set_channel_mask<T: Into<ChannelMask>>(&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
}
}
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
}
}
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, &str> {
self.data[1] = max_duty_cycle;
Ok(self)
}
}
pub struct DutyCycleAnsCreator {}
impl_mac_cmd_creator_boilerplate!(DutyCycleAnsCreator, 0x04);
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
}
}
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
}
}
pub struct DevStatusReqCreator {}
impl_mac_cmd_creator_boilerplate!(DevStatusReqCreator, 0x06);
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, &str> {
if margin < -32 || margin > 31 {
return Err("margin out of range");
}
self.data[2] = ((margin << 2) as u8) >> 2;
Ok(self)
}
}
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
}
}
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
}
}
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, &str> {
if delay > 0x0f {
return Err("delay out of range");
}
self.data[1] &= 0xf0;
self.data[1] |= delay;
Ok(self)
}
}
pub struct RXTimingSetupAnsCreator {}
impl_mac_cmd_creator_boilerplate!(RXTimingSetupAnsCreator, 0x08);
pub fn build_mac_commands<'a, 'b, 'c, T: AsMut<[u8]>>(
cmds: &'a [&'b dyn SerializableMacCommand],
mut out: T,
) -> Result<usize, &'c str> {
let res = out.as_mut();
if mac_commands_len(cmds) > res.len() {
return Err("failed to serialize mac commands in provided buffer: too small");
}
let mut i = 0;
for mc in cmds {
res[i] = mc.cid();
let l = mc.payload_len();
res[i + 1..i + l + 1].copy_from_slice(mc.payload_bytes());
i += l + 1;
}
Ok(i)
}