use std::fmt;
use std::io::{Cursor, Read};
use std::ops::{Deref, DerefMut};
use std::time::Duration;
use anyhow::Result;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::cflist::ChMask;
use crate::dl_settings::DLSettings;
use crate::helpers::{decode_freq, encode_freq};
pub trait PayloadCodec<Struct = Self> {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Struct>;
fn encode(&self) -> Result<Vec<u8>>;
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum CID {
ResetInd,
ResetConf,
LinkCheckReq,
LinkCheckAns,
LinkADRReq,
LinkADRAns,
DutyCycleReq,
DutyCycleAns,
RxParamSetupReq,
RxParamSetupAns,
DevStatusReq,
DevStatusAns,
NewChannelReq,
NewChannelAns,
RxTimingSetupReq,
RxTimingSetupAns,
TxParamSetupReq,
TxParamSetupAns,
DlChannelReq,
DlChannelAns,
RekeyConf,
RekeyInd,
ADRParamSetupReq,
ADRParamSetupAns,
DeviceTimeReq,
DeviceTimeAns,
ForceRejoinReq,
RejoinParamSetupReq,
RejoinParamSetupAns,
PingSlotInfoReq,
PingSlotInfoAns,
PingSlotChannelReq,
PingSlotChannelAns,
BeaconFreqReq,
BeaconFreqAns,
DeviceModeInd,
DeviceModeConf,
RelayConfReq,
RelayConfAns,
EndDeviceConfReq,
EndDeviceConfAns,
FilterListReq,
FilterListAns,
UpdateUplinkListReq,
UpdateUplinkListAns,
CtrlUplinkListReq,
CtrlUplinkListAns,
ConfigureFwdLimitReq,
ConfigureFwdLimitAns,
NotifyNewEndDeviceReq,
Raw,
}
impl fmt::Display for CID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl CID {
pub fn to_u8(&self) -> u8 {
match self {
CID::ResetInd | CID::ResetConf => 0x01,
CID::LinkCheckReq | CID::LinkCheckAns => 0x02,
CID::LinkADRReq | CID::LinkADRAns => 0x03,
CID::DutyCycleReq | CID::DutyCycleAns => 0x04,
CID::RxParamSetupReq | CID::RxParamSetupAns => 0x05,
CID::DevStatusReq | CID::DevStatusAns => 0x06,
CID::NewChannelReq | CID::NewChannelAns => 0x07,
CID::RxTimingSetupReq | CID::RxTimingSetupAns => 0x08,
CID::TxParamSetupReq | CID::TxParamSetupAns => 0x09,
CID::DlChannelReq | CID::DlChannelAns => 0x0a,
CID::RekeyConf | CID::RekeyInd => 0x0b,
CID::ADRParamSetupReq | CID::ADRParamSetupAns => 0x0c,
CID::DeviceTimeReq | CID::DeviceTimeAns => 0x0d,
CID::ForceRejoinReq => 0x0e,
CID::RejoinParamSetupReq | CID::RejoinParamSetupAns => 0x0f,
CID::PingSlotInfoReq | CID::PingSlotInfoAns => 0x10,
CID::PingSlotChannelReq | CID::PingSlotChannelAns => 0x11,
CID::BeaconFreqReq | CID::BeaconFreqAns => 0x13, CID::DeviceModeInd | CID::DeviceModeConf => 0x20,
CID::RelayConfReq | CID::RelayConfAns => 0x40,
CID::EndDeviceConfReq | CID::EndDeviceConfAns => 0x41,
CID::FilterListReq | CID::FilterListAns => 0x42,
CID::UpdateUplinkListReq | CID::UpdateUplinkListAns => 0x43,
CID::CtrlUplinkListReq | CID::CtrlUplinkListAns => 0x44,
CID::ConfigureFwdLimitReq | CID::ConfigureFwdLimitAns => 0x45,
CID::NotifyNewEndDeviceReq => 0x46,
CID::Raw => 0xff,
}
}
pub fn from_u8(uplink: bool, v: u8) -> Result<Self> {
Ok(if uplink {
match v {
0x01 => CID::ResetInd,
0x02 => CID::LinkCheckReq,
0x03 => CID::LinkADRAns,
0x04 => CID::DutyCycleAns,
0x05 => CID::RxParamSetupAns,
0x06 => CID::DevStatusAns,
0x07 => CID::NewChannelAns,
0x08 => CID::RxTimingSetupAns,
0x09 => CID::TxParamSetupAns,
0x0a => CID::DlChannelAns,
0x0b => CID::RekeyInd,
0x0c => CID::ADRParamSetupAns,
0x0d => CID::DeviceTimeReq,
0x0f => CID::RejoinParamSetupAns,
0x10 => CID::PingSlotInfoReq,
0x11 => CID::PingSlotChannelAns,
0x13 => CID::BeaconFreqAns,
0x20 => CID::DeviceModeInd,
0x40 => CID::RelayConfAns,
0x41 => CID::EndDeviceConfAns,
0x42 => CID::FilterListAns,
0x43 => CID::UpdateUplinkListAns,
0x44 => CID::CtrlUplinkListAns,
0x45 => CID::ConfigureFwdLimitAns,
0x46 => CID::NotifyNewEndDeviceReq,
_ => {
return Err(anyhow!("Invalid CID: {}", v));
}
}
} else {
match v {
0x01 => CID::ResetConf,
0x02 => CID::LinkCheckAns,
0x03 => CID::LinkADRReq,
0x04 => CID::DutyCycleReq,
0x05 => CID::RxParamSetupReq,
0x06 => CID::DevStatusReq,
0x07 => CID::NewChannelReq,
0x08 => CID::RxTimingSetupReq,
0x09 => CID::TxParamSetupReq,
0x0a => CID::DlChannelReq,
0x0b => CID::RekeyConf,
0x0c => CID::ADRParamSetupReq,
0x0d => CID::DeviceTimeAns,
0x0e => CID::ForceRejoinReq,
0x0f => CID::RejoinParamSetupReq,
0x10 => CID::PingSlotInfoAns,
0x11 => CID::PingSlotChannelReq,
0x13 => CID::BeaconFreqReq,
0x20 => CID::DeviceModeConf,
0x40 => CID::RelayConfReq,
0x41 => CID::EndDeviceConfReq,
0x42 => CID::FilterListReq,
0x43 => CID::UpdateUplinkListReq,
0x44 => CID::CtrlUplinkListReq,
0x45 => CID::ConfigureFwdLimitReq,
_ => {
return Err(anyhow!("Invalid CID: {}", v));
}
}
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum MACCommand {
ResetInd(ResetIndPayload),
ResetConf(ResetConfPayload),
LinkCheckReq,
LinkCheckAns(LinkCheckAnsPayload),
LinkADRReq(LinkADRReqPayload),
LinkADRAns(LinkADRAnsPayload),
DutyCycleReq(DutyCycleReqPayload),
DutyCycleAns,
RxParamSetupReq(RxParamSetupReqPayload),
RxParamSetupAns(RxParamSetupAnsPayload),
DevStatusReq,
DevStatusAns(DevStatusAnsPayload),
NewChannelReq(NewChannelReqPayload),
NewChannelAns(NewChannelAnsPayload),
RxTimingSetupReq(RxTimingSetupReqPayload),
RxTimingSetupAns,
TxParamSetupReq(TxParamSetupReqPayload),
TxParamSetupAns,
DlChannelReq(DlChannelReqPayload),
DlChannelAns(DlChannelAnsPayload),
RekeyConf(RekeyConfPayload),
RekeyInd(RekeyIndPayload),
ADRParamSetupReq(ADRParamSetupReqPayload),
ADRParamSetupAns,
DeviceTimeReq,
DeviceTimeAns(DeviceTimeAnsPayload),
ForceRejoinReq(ForceRejoinReqPayload),
RejoinParamSetupReq(RejoinParamSetupReqPayload),
RejoinParamSetupAns(RejoinParamSetupAnsPayload),
PingSlotInfoReq(PingSlotInfoReqPayload),
PingSlotInfoAns,
PingSlotChannelReq(PingSlotChannelReqPayload),
PingSlotChannelAns(PingSlotChannelAnsPayload),
BeaconFreqReq(BeaconFreqReqPayload),
BeaconFreqAns(BeaconFreqAnsPayload),
DeviceModeInd(DeviceModeIndPayload),
DeviceModeConf(DeviceModeConfPayload),
RelayConfReq(RelayConfReqPayload),
RelayConfAns(RelayConfAnsPayload),
EndDeviceConfReq(EndDeviceConfReqPayload),
EndDeviceConfAns(EndDeviceConfAnsPayload),
FilterListReq(FilterListReqPayload),
FilterListAns(FilterListAnsPayload),
UpdateUplinkListReq(UpdateUplinkListReqPayload),
UpdateUplinkListAns,
CtrlUplinkListReq(CtrlUplinkListReqPayload),
CtrlUplinkListAns(CtrlUplinkListAnsPayload),
ConfigureFwdLimitReq(ConfigureFwdLimitReqPayload),
ConfigureFwdLimitAns,
NotifyNewEndDeviceReq(NotifyNewEndDeviceReqPayload),
Raw(Vec<u8>),
}
impl MACCommand {
pub fn cid(&self) -> CID {
match self {
MACCommand::ResetInd(_) => CID::ResetInd,
MACCommand::ResetConf(_) => CID::ResetConf,
MACCommand::LinkCheckReq => CID::LinkCheckReq,
MACCommand::LinkCheckAns(_) => CID::LinkCheckAns,
MACCommand::LinkADRReq(_) => CID::LinkADRReq,
MACCommand::LinkADRAns(_) => CID::LinkADRAns,
MACCommand::DutyCycleReq(_) => CID::DutyCycleReq,
MACCommand::DutyCycleAns => CID::DutyCycleAns,
MACCommand::RxParamSetupReq(_) => CID::RxParamSetupReq,
MACCommand::RxParamSetupAns(_) => CID::RxParamSetupAns,
MACCommand::DevStatusReq => CID::DevStatusReq,
MACCommand::DevStatusAns(_) => CID::DevStatusAns,
MACCommand::NewChannelReq(_) => CID::NewChannelReq,
MACCommand::NewChannelAns(_) => CID::NewChannelAns,
MACCommand::RxTimingSetupReq(_) => CID::RxTimingSetupReq,
MACCommand::RxTimingSetupAns => CID::RxTimingSetupAns,
MACCommand::TxParamSetupReq(_) => CID::TxParamSetupReq,
MACCommand::TxParamSetupAns => CID::TxParamSetupAns,
MACCommand::DlChannelReq(_) => CID::DlChannelReq,
MACCommand::DlChannelAns(_) => CID::DlChannelAns,
MACCommand::RekeyConf(_) => CID::RekeyConf,
MACCommand::RekeyInd(_) => CID::RekeyInd,
MACCommand::ADRParamSetupReq(_) => CID::ADRParamSetupReq,
MACCommand::ADRParamSetupAns => CID::ADRParamSetupAns,
MACCommand::DeviceTimeReq => CID::DeviceTimeReq,
MACCommand::DeviceTimeAns(_) => CID::DeviceTimeAns,
MACCommand::ForceRejoinReq(_) => CID::ForceRejoinReq,
MACCommand::RejoinParamSetupReq(_) => CID::RejoinParamSetupReq,
MACCommand::RejoinParamSetupAns(_) => CID::RejoinParamSetupAns,
MACCommand::PingSlotInfoReq(_) => CID::PingSlotInfoReq,
MACCommand::PingSlotInfoAns => CID::PingSlotInfoAns,
MACCommand::PingSlotChannelReq(_) => CID::PingSlotChannelReq,
MACCommand::PingSlotChannelAns(_) => CID::PingSlotChannelAns,
MACCommand::BeaconFreqReq(_) => CID::BeaconFreqReq,
MACCommand::BeaconFreqAns(_) => CID::BeaconFreqAns, MACCommand::DeviceModeInd(_) => CID::DeviceModeInd,
MACCommand::DeviceModeConf(_) => CID::DeviceModeConf,
MACCommand::RelayConfReq(_) => CID::RelayConfReq,
MACCommand::RelayConfAns(_) => CID::RelayConfAns,
MACCommand::EndDeviceConfReq(_) => CID::EndDeviceConfReq,
MACCommand::EndDeviceConfAns(_) => CID::EndDeviceConfAns,
MACCommand::FilterListReq(_) => CID::FilterListReq,
MACCommand::FilterListAns(_) => CID::FilterListAns,
MACCommand::UpdateUplinkListReq(_) => CID::UpdateUplinkListReq,
MACCommand::UpdateUplinkListAns => CID::UpdateUplinkListAns,
MACCommand::CtrlUplinkListReq(_) => CID::CtrlUplinkListReq,
MACCommand::CtrlUplinkListAns(_) => CID::CtrlUplinkListAns,
MACCommand::ConfigureFwdLimitReq(_) => CID::ConfigureFwdLimitReq,
MACCommand::ConfigureFwdLimitAns => CID::ConfigureFwdLimitAns,
MACCommand::NotifyNewEndDeviceReq(_) => CID::NotifyNewEndDeviceReq,
MACCommand::Raw(_) => CID::Raw,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum Version {
LoRaWAN1_1,
}
impl Version {
pub fn to_u8(&self) -> u8 {
match self {
Version::LoRaWAN1_1 => 0x01,
}
}
pub fn from_u8(v: u8) -> Result<Self> {
Ok(match v {
0x01 => Version::LoRaWAN1_1,
_ => {
return Err(anyhow!("invalid version"));
}
})
}
}
impl fmt::Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum DwellTime {
NoLimit,
Limit400ms,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum DeviceModeClass {
ClassA,
ClassC,
}
impl DeviceModeClass {
pub fn to_u8(&self) -> u8 {
match self {
DeviceModeClass::ClassA => 0x00,
DeviceModeClass::ClassC => 0x02,
}
}
pub fn from_u8(v: u8) -> Result<Self> {
Ok(match v {
0x00 => DeviceModeClass::ClassA,
0x02 => DeviceModeClass::ClassC,
_ => {
return Err(anyhow!("invalid device mode"));
}
})
}
}
impl fmt::Display for DeviceModeClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
DeviceModeClass::ClassA => "A",
DeviceModeClass::ClassC => "C",
}
)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct MACCommandSet(Vec<MACCommand>);
impl Deref for MACCommandSet {
type Target = Vec<MACCommand>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for MACCommandSet {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl MACCommandSet {
pub fn new(macs: Vec<MACCommand>) -> Self {
MACCommandSet(macs)
}
pub fn cid(&self) -> Result<CID> {
self.0
.first()
.map(|v| v.cid())
.ok_or_else(|| anyhow!("Set is empty"))
}
pub fn size(&self) -> Result<usize> {
let b = self.to_vec()?;
Ok(b.len())
}
pub fn from_slice(b: &[u8]) -> Self {
MACCommandSet(vec![MACCommand::Raw(b.to_vec())])
}
pub fn push(&mut self, m: MACCommand) {
self.0.push(m);
}
pub fn to_vec(&self) -> Result<Vec<u8>> {
let mut out = Vec::new();
for mac in &self.0 {
match mac {
MACCommand::ResetInd(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ResetConf(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::LinkCheckReq => {
out.push(mac.cid().to_u8());
}
MACCommand::LinkCheckAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::LinkADRReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::LinkADRAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DutyCycleReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DutyCycleAns => {
out.push(mac.cid().to_u8());
}
MACCommand::RxParamSetupReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RxParamSetupAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DevStatusReq => {
out.push(mac.cid().to_u8());
}
MACCommand::DevStatusAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::NewChannelReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::NewChannelAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RxTimingSetupReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RxTimingSetupAns => {
out.push(mac.cid().to_u8());
}
MACCommand::TxParamSetupReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::TxParamSetupAns => {
out.push(mac.cid().to_u8());
}
MACCommand::DlChannelReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DlChannelAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RekeyConf(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RekeyInd(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ADRParamSetupReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ADRParamSetupAns => {
out.push(mac.cid().to_u8());
}
MACCommand::DeviceTimeReq => {
out.push(mac.cid().to_u8());
}
MACCommand::DeviceTimeAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ForceRejoinReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RejoinParamSetupReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RejoinParamSetupAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::PingSlotInfoReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::PingSlotInfoAns => {
out.push(mac.cid().to_u8());
}
MACCommand::PingSlotChannelReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::PingSlotChannelAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::BeaconFreqReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::BeaconFreqAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DeviceModeInd(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::DeviceModeConf(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RelayConfReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::RelayConfAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::EndDeviceConfReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::EndDeviceConfAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::FilterListReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::FilterListAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::UpdateUplinkListReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::UpdateUplinkListAns => out.push(mac.cid().to_u8()),
MACCommand::CtrlUplinkListReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::CtrlUplinkListAns(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ConfigureFwdLimitReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::ConfigureFwdLimitAns => out.push(mac.cid().to_u8()),
MACCommand::NotifyNewEndDeviceReq(pl) => {
out.push(mac.cid().to_u8());
out.extend_from_slice(&pl.encode()?);
}
MACCommand::Raw(v) => out.extend_from_slice(v),
};
}
Ok(out)
}
pub fn decode_from_raw(&mut self, uplink: bool) -> Result<()> {
if self.0.is_empty() {
return Ok(());
}
if self.0.len() == 1 {
if let MACCommand::Raw(b) = &self.0[0] {
let mut cur = Cursor::new(b.clone());
let mut commands = vec![];
let mut b = [0; 1];
loop {
if cur.read_exact(&mut b).is_err() {
break;
}
let cid = match CID::from_u8(uplink, b[0]) {
Ok(v) => v,
Err(_) => {
let mut b = b.to_vec();
cur.read_to_end(&mut b)?;
commands.push(MACCommand::Raw(b));
break;
}
};
match cid {
CID::ResetInd => {
commands.push(MACCommand::ResetInd(ResetIndPayload::decode(&mut cur)?))
}
CID::ResetConf => commands
.push(MACCommand::ResetConf(ResetConfPayload::decode(&mut cur)?)),
CID::LinkCheckReq => commands.push(MACCommand::LinkCheckReq),
CID::LinkCheckAns => commands.push(MACCommand::LinkCheckAns(
LinkCheckAnsPayload::decode(&mut cur)?,
)),
CID::LinkADRReq => commands
.push(MACCommand::LinkADRReq(LinkADRReqPayload::decode(&mut cur)?)),
CID::LinkADRAns => commands
.push(MACCommand::LinkADRAns(LinkADRAnsPayload::decode(&mut cur)?)),
CID::DutyCycleReq => commands.push(MACCommand::DutyCycleReq(
DutyCycleReqPayload::decode(&mut cur)?,
)),
CID::DutyCycleAns => commands.push(MACCommand::DutyCycleAns),
CID::RxParamSetupReq => commands.push(MACCommand::RxParamSetupReq(
RxParamSetupReqPayload::decode(&mut cur)?,
)),
CID::RxParamSetupAns => commands.push(MACCommand::RxParamSetupAns(
RxParamSetupAnsPayload::decode(&mut cur)?,
)),
CID::DevStatusReq => commands.push(MACCommand::DevStatusReq),
CID::DevStatusAns => commands.push(MACCommand::DevStatusAns(
DevStatusAnsPayload::decode(&mut cur)?,
)),
CID::NewChannelReq => commands.push(MACCommand::NewChannelReq(
NewChannelReqPayload::decode(&mut cur)?,
)),
CID::NewChannelAns => commands.push(MACCommand::NewChannelAns(
NewChannelAnsPayload::decode(&mut cur)?,
)),
CID::RxTimingSetupReq => commands.push(MACCommand::RxTimingSetupReq(
RxTimingSetupReqPayload::decode(&mut cur)?,
)),
CID::RxTimingSetupAns => commands.push(MACCommand::RxTimingSetupAns),
CID::TxParamSetupReq => commands.push(MACCommand::TxParamSetupReq(
TxParamSetupReqPayload::decode(&mut cur)?,
)),
CID::TxParamSetupAns => commands.push(MACCommand::TxParamSetupAns),
CID::DlChannelReq => commands.push(MACCommand::DlChannelReq(
DlChannelReqPayload::decode(&mut cur)?,
)),
CID::DlChannelAns => commands.push(MACCommand::DlChannelAns(
DlChannelAnsPayload::decode(&mut cur)?,
)),
CID::RekeyConf => commands
.push(MACCommand::RekeyConf(RekeyConfPayload::decode(&mut cur)?)),
CID::RekeyInd => {
commands.push(MACCommand::RekeyInd(RekeyIndPayload::decode(&mut cur)?))
}
CID::ADRParamSetupReq => commands.push(MACCommand::ADRParamSetupReq(
ADRParamSetupReqPayload::decode(&mut cur)?,
)),
CID::ADRParamSetupAns => commands.push(MACCommand::ADRParamSetupAns),
CID::DeviceTimeReq => commands.push(MACCommand::DeviceTimeReq),
CID::DeviceTimeAns => commands.push(MACCommand::DeviceTimeAns(
DeviceTimeAnsPayload::decode(&mut cur)?,
)),
CID::ForceRejoinReq => commands.push(MACCommand::ForceRejoinReq(
ForceRejoinReqPayload::decode(&mut cur)?,
)),
CID::RejoinParamSetupReq => commands.push(MACCommand::RejoinParamSetupReq(
RejoinParamSetupReqPayload::decode(&mut cur)?,
)),
CID::RejoinParamSetupAns => commands.push(MACCommand::RejoinParamSetupAns(
RejoinParamSetupAnsPayload::decode(&mut cur)?,
)),
CID::PingSlotInfoReq => commands.push(MACCommand::PingSlotInfoReq(
PingSlotInfoReqPayload::decode(&mut cur)?,
)),
CID::PingSlotInfoAns => commands.push(MACCommand::PingSlotInfoAns),
CID::PingSlotChannelReq => commands.push(MACCommand::PingSlotChannelReq(
PingSlotChannelReqPayload::decode(&mut cur)?,
)),
CID::PingSlotChannelAns => commands.push(MACCommand::PingSlotChannelAns(
PingSlotChannelAnsPayload::decode(&mut cur)?,
)),
CID::BeaconFreqReq => commands.push(MACCommand::BeaconFreqReq(
BeaconFreqReqPayload::decode(&mut cur)?,
)),
CID::BeaconFreqAns => commands.push(MACCommand::BeaconFreqAns(
BeaconFreqAnsPayload::decode(&mut cur)?,
)),
CID::DeviceModeInd => commands.push(MACCommand::DeviceModeInd(
DeviceModeIndPayload::decode(&mut cur)?,
)),
CID::DeviceModeConf => commands.push(MACCommand::DeviceModeConf(
DeviceModeConfPayload::decode(&mut cur)?,
)),
CID::RelayConfReq => commands.push(MACCommand::RelayConfReq(
RelayConfReqPayload::decode(&mut cur)?,
)),
CID::RelayConfAns => commands.push(MACCommand::RelayConfAns(
RelayConfAnsPayload::decode(&mut cur)?,
)),
CID::EndDeviceConfReq => commands.push(MACCommand::EndDeviceConfReq(
EndDeviceConfReqPayload::decode(&mut cur)?,
)),
CID::EndDeviceConfAns => commands.push(MACCommand::EndDeviceConfAns(
EndDeviceConfAnsPayload::decode(&mut cur)?,
)),
CID::FilterListReq => commands.push(MACCommand::FilterListReq(
FilterListReqPayload::decode(&mut cur)?,
)),
CID::FilterListAns => commands.push(MACCommand::FilterListAns(
FilterListAnsPayload::decode(&mut cur)?,
)),
CID::UpdateUplinkListReq => commands.push(MACCommand::UpdateUplinkListReq(
UpdateUplinkListReqPayload::decode(&mut cur)?,
)),
CID::UpdateUplinkListAns => commands.push(MACCommand::UpdateUplinkListAns),
CID::CtrlUplinkListReq => commands.push(MACCommand::CtrlUplinkListReq(
CtrlUplinkListReqPayload::decode(&mut cur)?,
)),
CID::CtrlUplinkListAns => commands.push(MACCommand::CtrlUplinkListAns(
CtrlUplinkListAnsPayload::decode(&mut cur)?,
)),
CID::ConfigureFwdLimitReq => {
commands.push(MACCommand::ConfigureFwdLimitReq(
ConfigureFwdLimitReqPayload::decode(&mut cur)?,
))
}
CID::ConfigureFwdLimitAns => {
commands.push(MACCommand::ConfigureFwdLimitAns)
}
CID::NotifyNewEndDeviceReq => {
commands.push(MACCommand::NotifyNewEndDeviceReq(
NotifyNewEndDeviceReqPayload::decode(&mut cur)?,
))
}
CID::Raw => {}
}
}
self.0 = commands;
}
}
Ok(())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ResetIndPayload {
pub dev_lorawan_version: Version,
}
impl PayloadCodec for ResetIndPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(ResetIndPayload {
dev_lorawan_version: Version::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.dev_lorawan_version.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ResetConfPayload {
pub serv_lorawan_version: Version,
}
impl PayloadCodec for ResetConfPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(ResetConfPayload {
serv_lorawan_version: Version::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.serv_lorawan_version.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LinkCheckAnsPayload {
pub margin: u8,
pub gw_cnt: u8,
}
impl PayloadCodec for LinkCheckAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 2];
cur.read_exact(&mut b)?;
Ok(LinkCheckAnsPayload {
margin: b[0],
gw_cnt: b[1],
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.margin, self.gw_cnt])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LinkADRReqPayload {
pub dr: u8,
pub tx_power: u8,
pub ch_mask: ChMask,
pub redundancy: Redundancy,
}
impl PayloadCodec for LinkADRReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 4];
cur.read_exact(&mut b)?;
Ok(LinkADRReqPayload {
dr: (b[0] & 0xf0) >> 4,
tx_power: b[0] & 0x0f,
ch_mask: ChMask::from_bytes([b[1], b[2]]),
redundancy: Redundancy::from_u8(b[3]),
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 4];
if self.dr > 15 {
return Err(anyhow!("max value of dr is 15"));
}
if self.tx_power > 15 {
return Err(anyhow!("max value of tx_power is 15"));
}
b[0] = self.tx_power | (self.dr << 4);
b[1..3].clone_from_slice(&self.ch_mask.to_bytes());
b[3] = self.redundancy.to_u8()?;
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct Redundancy {
pub ch_mask_cntl: u8,
pub nb_rep: u8,
}
impl Redundancy {
pub fn to_u8(&self) -> Result<u8> {
if self.nb_rep > 15 {
return Err(anyhow!("max value of nb_rep is 15"));
}
if self.ch_mask_cntl > 7 {
return Err(anyhow!("max value of ch_mask_cntl is 7"));
}
Ok(self.nb_rep | (self.ch_mask_cntl << 4))
}
pub fn from_u8(b: u8) -> Self {
Redundancy {
nb_rep: b & 0x0f,
ch_mask_cntl: (b & 0x70) >> 4,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct LinkADRAnsPayload {
pub ch_mask_ack: bool,
pub dr_ack: bool,
pub tx_power_ack: bool,
}
impl PayloadCodec for LinkADRAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(LinkADRAnsPayload {
ch_mask_ack: b[0] & 0x01 != 0,
dr_ack: b[0] & 0x02 != 0,
tx_power_ack: b[0] & 0x04 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.ch_mask_ack {
b |= 0x01;
}
if self.dr_ack {
b |= 0x02;
}
if self.tx_power_ack {
b |= 0x04;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DutyCycleReqPayload {
pub max_duty_cycle: u8,
}
impl PayloadCodec for DutyCycleReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(DutyCycleReqPayload {
max_duty_cycle: b[0],
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.max_duty_cycle > 15 && self.max_duty_cycle != 255 {
return Err(anyhow!("max_duty_cycle must have value 0 - 15 or 255"));
}
Ok(vec![self.max_duty_cycle])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RxParamSetupReqPayload {
pub frequency: u32,
pub dl_settings: DLSettings,
}
impl PayloadCodec for RxParamSetupReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 4];
cur.read_exact(&mut b)?;
Ok(RxParamSetupReqPayload {
dl_settings: DLSettings::from_le_bytes([b[0]]),
frequency: decode_freq(&b[1..])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 4];
b[0..1].copy_from_slice(&self.dl_settings.to_le_bytes()?);
b[1..4].copy_from_slice(&encode_freq(self.frequency)?);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RxParamSetupAnsPayload {
pub channel_ack: bool,
pub rx2_dr_ack: bool,
pub rx1_dr_offset_ack: bool,
}
impl PayloadCodec for RxParamSetupAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RxParamSetupAnsPayload {
channel_ack: b[0] & 0x01 != 0,
rx2_dr_ack: b[0] & 0x02 != 0,
rx1_dr_offset_ack: b[0] & 0x04 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.channel_ack {
b |= 0x01;
}
if self.rx2_dr_ack {
b |= 0x02;
}
if self.rx1_dr_offset_ack {
b |= 0x04;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DevStatusAnsPayload {
pub battery: u8,
pub margin: i8,
}
impl PayloadCodec for DevStatusAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 2];
cur.read_exact(&mut b)?;
Ok(DevStatusAnsPayload {
battery: b[0],
margin: {
if b[1] > 31 {
(b[1] as i8) - 64
} else {
b[1] as i8
}
},
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.margin < -32 {
return Err(anyhow!("min margin value is -32"));
}
if self.margin > 31 {
return Err(anyhow!("max margin value is 31"));
}
Ok(vec![self.battery, {
if self.margin < 0 {
(self.margin + 64) as u8
} else {
self.margin as u8
}
}])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NewChannelReqPayload {
pub ch_index: u8,
pub freq: u32,
pub min_dr: u8,
pub max_dr: u8,
}
impl PayloadCodec for NewChannelReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 5];
cur.read_exact(&mut b)?;
Ok(NewChannelReqPayload {
ch_index: b[0],
freq: decode_freq(&b[1..4])?,
min_dr: b[4] & 0x0f,
max_dr: (b[4] & 0xf0) >> 4,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 5];
b[0] = self.ch_index;
b[1..4].copy_from_slice(&encode_freq(self.freq)?);
b[4] = self.min_dr | (self.max_dr << 4);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NewChannelAnsPayload {
pub channel_freq_ok: bool,
pub dr_range_ok: bool,
}
impl PayloadCodec for NewChannelAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(NewChannelAnsPayload {
channel_freq_ok: b[0] & 0x01 != 0,
dr_range_ok: b[0] & 0x02 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.channel_freq_ok {
b = 0x01;
}
if self.dr_range_ok {
b |= 0x02;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RxTimingSetupReqPayload {
pub delay: u8,
}
impl PayloadCodec for RxTimingSetupReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RxTimingSetupReqPayload { delay: b[0] })
}
fn encode(&self) -> Result<Vec<u8>> {
if self.delay > 15 {
return Err(anyhow!("max delay value is 15"));
}
Ok(vec![self.delay])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct TxParamSetupReqPayload {
pub uplink_dwell_time: DwellTime,
pub downlink_dwell_time: DwellTime,
pub max_eirp: u8,
}
impl PayloadCodec for TxParamSetupReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(TxParamSetupReqPayload {
uplink_dwell_time: {
if b[0] & 0x10 != 0 {
DwellTime::Limit400ms
} else {
DwellTime::NoLimit
}
},
downlink_dwell_time: {
if b[0] & 0x20 != 0 {
DwellTime::Limit400ms
} else {
DwellTime::NoLimit
}
},
max_eirp: b[0] & 0x0f,
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.max_eirp > 15 {
return Err(anyhow!("max max_eirp value is 15"));
}
let mut b = vec![self.max_eirp];
if self.uplink_dwell_time == DwellTime::Limit400ms {
b[0] |= 0x10;
}
if self.downlink_dwell_time == DwellTime::Limit400ms {
b[0] |= 0x20;
}
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DlChannelReqPayload {
pub ch_index: u8,
pub freq: u32,
}
impl PayloadCodec for DlChannelReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 4];
cur.read_exact(&mut b)?;
Ok(DlChannelReqPayload {
ch_index: b[0],
freq: decode_freq(&b[1..4])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 4];
b[0] = self.ch_index;
b[1..4].copy_from_slice(&encode_freq(self.freq)?);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DlChannelAnsPayload {
pub uplink_freq_exists: bool,
pub channel_freq_ok: bool,
}
impl PayloadCodec for DlChannelAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(DlChannelAnsPayload {
channel_freq_ok: b[0] & 0x01 != 0,
uplink_freq_exists: b[0] & 0x02 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.channel_freq_ok {
b |= 0x01;
}
if self.uplink_freq_exists {
b |= 0x02;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RekeyConfPayload {
pub serv_lorawan_version: Version,
}
impl PayloadCodec for RekeyConfPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RekeyConfPayload {
serv_lorawan_version: Version::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.serv_lorawan_version.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RekeyIndPayload {
pub dev_lorawan_version: Version,
}
impl PayloadCodec for RekeyIndPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RekeyIndPayload {
dev_lorawan_version: Version::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.dev_lorawan_version.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ADRParamSetupReqPayload {
pub adr_param: ADRParam,
}
impl PayloadCodec for ADRParamSetupReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(ADRParamSetupReqPayload {
adr_param: ADRParam::from_u8(b[0]),
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.adr_param.to_u8()?])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ADRParam {
pub limit_exp: u8,
pub delay_exp: u8,
}
impl ADRParam {
pub fn from_u8(b: u8) -> Self {
ADRParam {
delay_exp: b & 0x0f,
limit_exp: b >> 4,
}
}
pub fn to_u8(&self) -> Result<u8> {
if self.limit_exp > 15 {
return Err(anyhow!("max limit_exp value is 15"));
}
if self.delay_exp > 15 {
return Err(anyhow!("max delay_exp value is 15"));
}
Ok(self.delay_exp | (self.limit_exp << 4))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DeviceTimeAnsPayload {
pub time_since_gps_epoch: Duration,
}
impl PayloadCodec for DeviceTimeAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 5];
cur.read_exact(&mut b)?;
let secs = {
let mut secs_b: [u8; 4] = [0; 4];
secs_b.copy_from_slice(&b[0..4]);
u32::from_le_bytes(secs_b)
} as u64;
let nanos = (b[4] as u32) * 3906250;
Ok(DeviceTimeAnsPayload {
time_since_gps_epoch: Duration::new(secs, nanos),
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 5];
b[0..4].copy_from_slice(&(self.time_since_gps_epoch.as_secs() as u32).to_le_bytes());
b[4] = ((self.time_since_gps_epoch.as_nanos() % 1_000_000_000) / 3906250) as u8;
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ForceRejoinReqPayload {
pub period: u8,
pub max_retries: u8,
pub rejoin_type: u8,
pub dr: u8,
}
impl PayloadCodec for ForceRejoinReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 2];
cur.read_exact(&mut b)?;
Ok(ForceRejoinReqPayload {
dr: b[0] & 0x0f,
rejoin_type: (b[0] & 0x70) >> 4,
max_retries: b[1] & 0x07,
period: (b[1] & 0x38) >> 3,
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.period > 7 {
return Err(anyhow!("max period value is 7"));
}
if self.max_retries > 7 {
return Err(anyhow!("max max_retries value is 7"));
}
if self.rejoin_type != 0 && self.rejoin_type != 2 {
return Err(anyhow!("rejoin_type must be 0 or 2"));
}
if self.dr > 15 {
return Err(anyhow!("max dr value is 15"));
}
Ok(vec![
self.dr | (self.rejoin_type << 4),
self.max_retries | (self.period << 3),
])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RejoinParamSetupReqPayload {
pub max_time_n: u8,
pub max_count_n: u8,
}
impl PayloadCodec for RejoinParamSetupReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RejoinParamSetupReqPayload {
max_count_n: b[0] & 0x0f,
max_time_n: (b[0] & 0xf0) >> 4,
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.max_time_n > 15 {
return Err(anyhow!("max max_time_n value is 15"));
}
if self.max_count_n > 15 {
return Err(anyhow!("max max_count_n value is 15"));
}
Ok(vec![self.max_count_n | (self.max_time_n << 4)])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RejoinParamSetupAnsPayload {
pub time_ok: bool,
}
impl PayloadCodec for RejoinParamSetupAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RejoinParamSetupAnsPayload {
time_ok: b[0] & 0x01 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.time_ok {
b = 0x01;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct PingSlotInfoReqPayload {
pub periodicity: u8,
}
impl PayloadCodec for PingSlotInfoReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(PingSlotInfoReqPayload {
periodicity: b[0] & 0x07,
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.periodicity > 7 {
return Err(anyhow!("max periodicity value is 7"));
}
Ok(vec![self.periodicity])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct PingSlotChannelReqPayload {
pub freq: u32,
pub dr: u8,
}
impl PayloadCodec for PingSlotChannelReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 4];
cur.read_exact(&mut b)?;
Ok(PingSlotChannelReqPayload {
freq: decode_freq(&b[0..3])?,
dr: b[3] & 0x0f,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 4];
b[0..3].copy_from_slice(&encode_freq(self.freq)?);
b[3] = self.dr;
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct PingSlotChannelAnsPayload {
pub dr_ok: bool,
pub channel_freq_ok: bool,
}
impl PayloadCodec for PingSlotChannelAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(PingSlotChannelAnsPayload {
channel_freq_ok: b[0] & 0x01 != 0,
dr_ok: b[0] & 0x02 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = 0;
if self.channel_freq_ok {
b = 0x01;
}
if self.dr_ok {
b |= 0x02;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct BeaconFreqReqPayload {
pub freq: u32,
}
impl PayloadCodec for BeaconFreqReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 3];
cur.read_exact(&mut b)?;
Ok(BeaconFreqReqPayload {
freq: decode_freq(&b)?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(encode_freq(self.freq)?.to_vec())
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct BeaconFreqAnsPayload {
beacon_freq_ok: bool,
}
impl PayloadCodec for BeaconFreqAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(BeaconFreqAnsPayload {
beacon_freq_ok: b[0] & 0x01 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.beacon_freq_ok {
b = 0x01;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DeviceModeIndPayload {
pub class: DeviceModeClass,
}
impl PayloadCodec for DeviceModeIndPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(DeviceModeIndPayload {
class: DeviceModeClass::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.class.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct DeviceModeConfPayload {
pub class: DeviceModeClass,
}
impl PayloadCodec for DeviceModeConfPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(DeviceModeConfPayload {
class: DeviceModeClass::from_u8(b[0])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.class.to_u8()])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ChannelSettingsRelay {
pub start_stop: u8,
pub cad_periodicity: u8,
pub default_ch_idx: u8,
pub second_ch_idx: u8,
pub second_ch_dr: u8,
pub second_ch_ack_offset: u8,
}
impl ChannelSettingsRelay {
pub fn to_bytes(&self) -> Result<[u8; 2]> {
if self.start_stop > 1 {
return Err(anyhow!("max value of start_stop is 1"));
}
if self.cad_periodicity > 7 {
return Err(anyhow!("max value of cad_periodicity is 7"));
}
if self.default_ch_idx > 1 {
return Err(anyhow!("max value of default_ch_idx is 1"));
}
if self.second_ch_idx > 1 {
return Err(anyhow!("max value of second_ch_idx is 1"));
}
if self.second_ch_dr > 15 {
return Err(anyhow!("max value of second_ch_dr is 15"));
}
if self.second_ch_ack_offset > 7 {
return Err(anyhow!("max value of second_ch_ack_offset is 7"));
}
Ok([
self.second_ch_ack_offset | (self.second_ch_dr << 3) | (self.second_ch_idx << 7),
(self.second_ch_idx >> 1)
| (self.default_ch_idx << 1)
| (self.cad_periodicity << 2)
| (self.start_stop << 5),
])
}
pub fn from_bytes(b: [u8; 2]) -> Self {
ChannelSettingsRelay {
second_ch_ack_offset: b[0] & 0x07,
second_ch_dr: (b[0] & 0x78) >> 3,
second_ch_idx: ((b[0] & 0x80) >> 7) | ((b[1] & 0x01) << 1),
default_ch_idx: (b[1] & 0x02) >> 1,
cad_periodicity: (b[1] & 0x1c) >> 2,
start_stop: (b[1] & 0x20) >> 5,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RelayConfReqPayload {
pub channel_settings_relay: ChannelSettingsRelay,
pub second_ch_freq: u32,
}
impl PayloadCodec for RelayConfReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 5];
cur.read_exact(&mut b)?;
Ok(RelayConfReqPayload {
channel_settings_relay: ChannelSettingsRelay::from_bytes([b[0], b[1]]),
second_ch_freq: decode_freq(&b[2..5])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 5];
b[0..2].copy_from_slice(&self.channel_settings_relay.to_bytes()?);
b[2..5].copy_from_slice(&encode_freq(self.second_ch_freq)?);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct RelayConfAnsPayload {
pub second_ch_freq_ack: bool,
pub second_ch_ack_offset_ack: bool,
pub second_ch_dr_ack: bool,
pub second_ch_idx_ack: bool,
pub default_ch_idx_ack: bool,
pub cad_periodicity_ack: bool,
}
impl PayloadCodec for RelayConfAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(RelayConfAnsPayload {
second_ch_freq_ack: b[0] & 0x01 != 0,
second_ch_ack_offset_ack: b[0] & 0x02 != 0,
second_ch_dr_ack: b[0] & 0x04 != 0,
second_ch_idx_ack: b[0] & 0x08 != 0,
default_ch_idx_ack: b[0] & 0x10 != 0,
cad_periodicity_ack: b[0] & 0x20 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.second_ch_freq_ack {
b |= 0x01;
}
if self.second_ch_ack_offset_ack {
b |= 0x02;
}
if self.second_ch_dr_ack {
b |= 0x04;
}
if self.second_ch_idx_ack {
b |= 0x08;
}
if self.default_ch_idx_ack {
b |= 0x10;
}
if self.cad_periodicity_ack {
b |= 0x20;
}
Ok(vec![b])
}
}
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum RelayModeActivation {
#[default]
DisableRelayMode,
EnableRelayMode,
Dynamic,
EndDeviceControlled,
}
impl RelayModeActivation {
pub fn to_u8(&self) -> u8 {
match self {
RelayModeActivation::DisableRelayMode => 0x00,
RelayModeActivation::EnableRelayMode => 0x01,
RelayModeActivation::Dynamic => 0x02,
RelayModeActivation::EndDeviceControlled => 0x03,
}
}
pub fn from_u8(v: u8) -> Result<Self> {
Ok(match v {
0x00 => RelayModeActivation::DisableRelayMode,
0x01 => RelayModeActivation::EnableRelayMode,
0x02 => RelayModeActivation::Dynamic,
0x03 => RelayModeActivation::EndDeviceControlled,
_ => {
return Err(anyhow!("invalid RelayModeActivation: {}", v));
}
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum ResetLimitCounter {
TokenCounterToZero,
TokenCounterToReloadRate,
TokenCounterToMaxValue,
NoChange,
}
impl ResetLimitCounter {
pub fn to_u8(&self) -> u8 {
match self {
ResetLimitCounter::TokenCounterToZero => 0x00,
ResetLimitCounter::TokenCounterToReloadRate => 0x01,
ResetLimitCounter::TokenCounterToMaxValue => 0x02,
ResetLimitCounter::NoChange => 0x03,
}
}
pub fn from_u8(v: u8) -> Result<Self> {
Ok(match v {
0x00 => ResetLimitCounter::TokenCounterToZero,
0x01 => ResetLimitCounter::TokenCounterToReloadRate,
0x02 => ResetLimitCounter::TokenCounterToMaxValue,
0x03 => ResetLimitCounter::NoChange,
_ => {
return Err(anyhow!("Invalid ResetLimitCounter value: {}", v));
}
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ActivationRelayMode {
pub relay_mode_activation: RelayModeActivation,
pub smart_enable_level: u8,
}
impl ActivationRelayMode {
pub fn from_u8(b: u8) -> Result<Self> {
Ok(ActivationRelayMode {
relay_mode_activation: RelayModeActivation::from_u8((b & 0x0c) >> 2)?,
smart_enable_level: b & 0x03,
})
}
pub fn to_u8(&self) -> Result<u8> {
if self.smart_enable_level > 3 {
return Err(anyhow!("max value of smart_enable_level is 3"));
}
Ok((self.relay_mode_activation.to_u8() << 2) | self.smart_enable_level)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ChannelSettingsED {
pub second_ch_ack_offset: u8,
pub second_ch_dr: u8,
pub second_ch_idx: u8,
pub backoff: u8,
}
impl ChannelSettingsED {
pub fn from_bytes(b: [u8; 2]) -> Self {
ChannelSettingsED {
second_ch_ack_offset: b[0] & 0x07,
second_ch_dr: (b[0] & 0x78) >> 3,
second_ch_idx: ((b[0] & 0x80) >> 7) | ((b[1] & 0x01) << 1),
backoff: (b[1] & 0x7e) >> 1,
}
}
pub fn to_bytes(&self) -> Result<[u8; 2]> {
if self.second_ch_ack_offset > 7 {
return Err(anyhow!("max value of second_ch_ack_offset is 7"));
}
if self.second_ch_dr > 15 {
return Err(anyhow!("max value of second_ch_dr is 15"));
}
if self.second_ch_idx > 3 {
return Err(anyhow!("max value of second_ch_idx is 3"));
}
if self.backoff > 63 {
return Err(anyhow!("max value of backoff is 63"));
}
Ok([
self.second_ch_ack_offset | (self.second_ch_dr << 3) | (self.second_ch_idx << 7),
(self.second_ch_idx >> 1) | (self.backoff << 1),
])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct EndDeviceConfReqPayload {
pub activation_relay_mode: ActivationRelayMode,
pub channel_settings_ed: ChannelSettingsED,
pub second_ch_freq: u32,
}
impl PayloadCodec for EndDeviceConfReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 6];
cur.read_exact(&mut b)?;
Ok(EndDeviceConfReqPayload {
activation_relay_mode: ActivationRelayMode::from_u8(b[0])?,
channel_settings_ed: ChannelSettingsED::from_bytes([b[1], b[2]]),
second_ch_freq: decode_freq(&b[3..6])?,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 6];
b[0] = self.activation_relay_mode.to_u8()?;
b[1..3].copy_from_slice(&self.channel_settings_ed.to_bytes()?);
b[3..6].copy_from_slice(&encode_freq(self.second_ch_freq)?);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct EndDeviceConfAnsPayload {
pub second_ch_freq_ack: bool,
pub second_ch_dr_ack: bool,
pub second_ch_idx_ack: bool,
pub backoff_ack: bool,
}
impl PayloadCodec for EndDeviceConfAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(EndDeviceConfAnsPayload {
second_ch_freq_ack: b[0] & 0x01 != 0,
second_ch_dr_ack: b[0] & 0x02 != 0,
second_ch_idx_ack: b[0] & 0x04 != 0,
backoff_ack: b[0] & 0x08 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.second_ch_freq_ack {
b |= 0x01;
}
if self.second_ch_dr_ack {
b |= 0x02;
}
if self.second_ch_idx_ack {
b |= 0x04;
}
if self.backoff_ack {
b |= 0x08;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub enum FilterListAction {
NoRule,
Forward,
Filter,
}
impl FilterListAction {
pub fn to_u8(&self) -> u8 {
match self {
FilterListAction::NoRule => 0x00,
FilterListAction::Forward => 0x01,
FilterListAction::Filter => 0x02,
}
}
pub fn from_u8(v: u8) -> Result<Self> {
Ok(match v {
0x00 => FilterListAction::NoRule,
0x01 => FilterListAction::Forward,
0x02 => FilterListAction::Filter,
_ => {
return Err(anyhow!("invalid FilterListAction: {}", v));
}
})
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FilterListReqPayload {
pub filter_list_idx: u8,
pub filter_list_action: FilterListAction,
pub filter_list_eui: Vec<u8>,
}
impl PayloadCodec for FilterListReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = vec![0; 2];
cur.read_exact(&mut b)?;
let len = b[0] & 0x17;
let mut eui = vec![0; len as usize];
cur.read_exact(&mut eui)?;
eui.reverse();
Ok(FilterListReqPayload {
filter_list_action: FilterListAction::from_u8((b[0] & 0x60) >> 5)?,
filter_list_idx: ((b[0] & 0x80) >> 7) | ((b[1] & 0x07) << 1),
filter_list_eui: eui,
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.filter_list_idx > 15 {
return Err(anyhow!("max filter_list_idx value is 15"));
}
if self.filter_list_eui.len() > 16 {
return Err(anyhow!("max filter_list_eui length is 16"));
}
let mut b = vec![
self.filter_list_eui.len() as u8
| (self.filter_list_action.to_u8() << 5)
| (self.filter_list_idx << 7),
(self.filter_list_idx >> 1),
];
let mut filter_list_eui = self.filter_list_eui.clone();
filter_list_eui.reverse();
b.extend_from_slice(&filter_list_eui);
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FilterListAnsPayload {
pub filter_list_action_ack: bool,
pub filter_list_len_ack: bool,
pub combined_rules_ack: bool,
}
impl PayloadCodec for FilterListAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(FilterListAnsPayload {
filter_list_action_ack: b[0] & 0x01 != 0,
filter_list_len_ack: b[0] & 0x02 != 0,
combined_rules_ack: b[0] & 0x04 != 0,
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b: u8 = 0;
if self.filter_list_action_ack {
b |= 0x01;
}
if self.filter_list_len_ack {
b |= 0x02;
}
if self.combined_rules_ack {
b |= 0x04;
}
Ok(vec![b])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct UplinkLimitPL {
pub reload_rate: u8,
pub bucket_size: u8,
}
impl UplinkLimitPL {
pub fn from_u8(v: u8) -> Self {
UplinkLimitPL {
reload_rate: v & 0x3f,
bucket_size: (v & 0xc0) >> 6,
}
}
pub fn to_u8(&self) -> Result<u8> {
if self.reload_rate > 63 {
return Err(anyhow!("max reload_rate value is 63"));
}
if self.bucket_size > 3 {
return Err(anyhow!("max bucket_size value is 3"));
}
Ok(self.reload_rate | (self.bucket_size << 6))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct UpdateUplinkListReqPayload {
pub uplink_list_idx: u8,
pub uplink_limit: UplinkLimitPL,
pub dev_addr: crate::DevAddr,
pub w_fcnt: u32,
pub root_wor_s_key: crate::AES128Key,
}
impl PayloadCodec for UpdateUplinkListReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 26];
cur.read_exact(&mut b)?;
Ok(UpdateUplinkListReqPayload {
uplink_list_idx: b[0] & 0x0f,
uplink_limit: UplinkLimitPL::from_u8(b[1]),
dev_addr: crate::DevAddr::from_le_bytes({
let mut bb = [0; 4];
bb.copy_from_slice(&b[2..6]);
bb
}),
w_fcnt: u32::from_le_bytes({
let mut bb = [0; 4];
bb.copy_from_slice(&b[6..10]);
bb
}),
root_wor_s_key: crate::AES128Key::from_bytes({
let mut bb = [0; 16];
bb.copy_from_slice(&b[10..26]);
bb
}),
})
}
fn encode(&self) -> Result<Vec<u8>> {
if self.uplink_list_idx > 15 {
return Err(anyhow!("max uplink_list_idx value is 15"));
}
let mut b = vec![0; 26];
b[0] = self.uplink_list_idx;
b[1] = self.uplink_limit.to_u8()?;
b[2..6].copy_from_slice(&self.dev_addr.to_le_bytes());
b[6..10].copy_from_slice(&self.w_fcnt.to_le_bytes());
b[10..26].copy_from_slice(&self.root_wor_s_key.to_bytes());
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CtrlUplinkActionPL {
pub uplink_list_idx: u8,
pub ctrl_uplink_action: u8,
}
impl CtrlUplinkActionPL {
pub fn to_u8(&self) -> Result<u8> {
if self.uplink_list_idx > 15 {
return Err(anyhow!("max uplink_list_idx value is 15"));
}
if self.ctrl_uplink_action > 1 {
return Err(anyhow!("max ctrl_uplink_action is 1"));
}
Ok(self.uplink_list_idx | (self.ctrl_uplink_action << 4))
}
pub fn from_u8(v: u8) -> Self {
CtrlUplinkActionPL {
uplink_list_idx: v & 0x0f,
ctrl_uplink_action: (v & 0x10) >> 4,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CtrlUplinkListReqPayload {
pub ctrl_uplink_action: CtrlUplinkActionPL,
}
impl PayloadCodec for CtrlUplinkListReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 1];
cur.read_exact(&mut b)?;
Ok(CtrlUplinkListReqPayload {
ctrl_uplink_action: CtrlUplinkActionPL::from_u8(b[0]),
})
}
fn encode(&self) -> Result<Vec<u8>> {
Ok(vec![self.ctrl_uplink_action.to_u8()?])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct CtrlUplinkListAnsPayload {
pub uplink_list_idx_ack: bool,
pub w_fcnt: u32,
}
impl PayloadCodec for CtrlUplinkListAnsPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 5];
cur.read_exact(&mut b)?;
Ok(CtrlUplinkListAnsPayload {
uplink_list_idx_ack: b[0] & 0x01 > 0,
w_fcnt: u32::from_le_bytes({
let mut bb = [0; 4];
bb.copy_from_slice(&b[1..5]);
bb
}),
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 5];
if self.uplink_list_idx_ack {
b[0] |= 0x01;
}
b[1..5].copy_from_slice(&self.w_fcnt.to_le_bytes());
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FwdLimitReloadRatePL {
pub overall_reload_rate: u8,
pub global_uplink_reload_rate: u8,
pub notify_reload_rate: u8,
pub join_req_reload_rate: u8,
pub reset_limit_counter: ResetLimitCounter,
}
impl FwdLimitReloadRatePL {
pub fn from_bytes(b: [u8; 4]) -> Result<Self> {
Ok(FwdLimitReloadRatePL {
overall_reload_rate: b[0] & 0x7f,
global_uplink_reload_rate: (b[0] >> 7) | ((b[1] & 0x3f) << 1),
notify_reload_rate: (b[1] >> 6) | ((b[2] & 0x1f) << 2),
join_req_reload_rate: (b[2] >> 5) | ((b[3] & 0x0f) << 3),
reset_limit_counter: ResetLimitCounter::from_u8((b[3] & 0x30) >> 4)?,
})
}
pub fn to_bytes(&self) -> Result<[u8; 4]> {
if self.join_req_reload_rate > 127 {
return Err(anyhow!("max join_req_reload_rate is 127"));
}
if self.notify_reload_rate > 127 {
return Err(anyhow!("max notify_reload_rate is 127"));
}
if self.global_uplink_reload_rate > 127 {
return Err(anyhow!("max global_uplink_reload_rate is 127"));
}
if self.overall_reload_rate > 127 {
return Err(anyhow!("max overall_reload_rate is 127"));
}
Ok([
self.overall_reload_rate | (self.global_uplink_reload_rate << 7),
(self.global_uplink_reload_rate >> 1) | (self.notify_reload_rate << 6),
(self.notify_reload_rate >> 2) | (self.join_req_reload_rate << 5),
(self.join_req_reload_rate >> 3) | (self.reset_limit_counter.to_u8() << 4),
])
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct FwdLimitLoadCapacityPL {
pub overall_limit_size: u8,
pub global_uplink_limit_size: u8,
pub notify_limit_size: u8,
pub join_req_limit_size: u8,
}
impl FwdLimitLoadCapacityPL {
pub fn from_u8(v: u8) -> Self {
FwdLimitLoadCapacityPL {
overall_limit_size: v & 0x03,
global_uplink_limit_size: (v & 0x0c) >> 2,
notify_limit_size: (v & 0x30) >> 4,
join_req_limit_size: (v & 0xc0) >> 6,
}
}
pub fn to_u8(&self) -> Result<u8> {
if self.overall_limit_size > 3 {
return Err(anyhow!("max overall_limit_size is 3"));
}
if self.global_uplink_limit_size > 3 {
return Err(anyhow!("max global_uplink_limit_size is 3"));
}
if self.notify_limit_size > 3 {
return Err(anyhow!("max notify_limit_size is 3"));
}
if self.join_req_limit_size > 3 {
return Err(anyhow!("max join_req_limit_size is 3"));
}
Ok(self.overall_limit_size
| (self.global_uplink_limit_size << 2)
| (self.notify_limit_size << 4)
| (self.join_req_limit_size << 6))
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct ConfigureFwdLimitReqPayload {
pub reload_rate: FwdLimitReloadRatePL,
pub load_capacity: FwdLimitLoadCapacityPL,
}
impl PayloadCodec for ConfigureFwdLimitReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 5];
cur.read_exact(&mut b)?;
Ok(ConfigureFwdLimitReqPayload {
reload_rate: FwdLimitReloadRatePL::from_bytes({
let mut bb = [0; 4];
bb.copy_from_slice(&b[0..4]);
bb
})?,
load_capacity: FwdLimitLoadCapacityPL::from_u8(b[4]),
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 5];
b[0..4].copy_from_slice(&self.reload_rate.to_bytes()?);
b[4] = self.load_capacity.to_u8()?;
Ok(b)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct PowerLevel {
pub wor_snr: isize,
pub wor_rssi: isize,
}
impl PowerLevel {
pub fn from_bytes(b: [u8; 2]) -> Self {
PowerLevel {
wor_snr: (b[0] & 0x1f) as isize - 20,
wor_rssi: -(((b[0] >> 5) | ((b[1] & 0x0f) << 3)) as isize) - 15,
}
}
pub fn to_bytes(&self) -> [u8; 2] {
let wor_snr = self.wor_snr.clamp(-20, 11);
let wor_rssi = self.wor_rssi.clamp(-142, -15);
let wor_snr = (wor_snr + 20) as u8;
let wor_rssi = -(wor_rssi + 15) as u8;
[wor_snr | (wor_rssi << 5), wor_rssi >> 3]
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize))]
pub struct NotifyNewEndDeviceReqPayload {
pub dev_addr: crate::DevAddr,
pub power_level: PowerLevel,
}
impl PayloadCodec for NotifyNewEndDeviceReqPayload {
fn decode(cur: &mut Cursor<Vec<u8>>) -> Result<Self> {
let mut b = [0; 6];
cur.read_exact(&mut b)?;
Ok(NotifyNewEndDeviceReqPayload {
dev_addr: crate::DevAddr::from_le_bytes([b[0], b[1], b[2], b[3]]),
power_level: PowerLevel::from_bytes([b[4], b[5]]),
})
}
fn encode(&self) -> Result<Vec<u8>> {
let mut b = vec![0; 6];
b[0..4].copy_from_slice(&self.dev_addr.to_le_bytes());
b[4..6].copy_from_slice(&self.power_level.to_bytes());
Ok(b)
}
}
#[cfg(test)]
mod test {
use super::*;
struct MacTest {
uplink: bool,
command: MACCommand,
bytes: Vec<u8>,
}
#[test]
fn test_command() {
let tests = vec![
MacTest {
uplink: true,
command: MACCommand::ResetInd(ResetIndPayload {
dev_lorawan_version: Version::LoRaWAN1_1,
}),
bytes: vec![0x01, 0x01],
},
MacTest {
uplink: false,
command: MACCommand::ResetConf(ResetConfPayload {
serv_lorawan_version: Version::LoRaWAN1_1,
}),
bytes: vec![0x01, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::LinkCheckReq,
bytes: vec![0x02],
},
MacTest {
uplink: false,
command: MACCommand::LinkCheckAns(LinkCheckAnsPayload {
margin: 10,
gw_cnt: 15,
}),
bytes: vec![0x02, 0x0a, 0x0f],
},
MacTest {
uplink: false,
command: MACCommand::LinkADRReq(LinkADRReqPayload {
dr: 1,
tx_power: 2,
ch_mask: ChMask::new({
let mut mask: [bool; 16] = [false; 16];
mask[2] = true;
mask
}),
redundancy: Redundancy {
ch_mask_cntl: 4,
nb_rep: 5,
},
}),
bytes: vec![0x03, 0x12, 0x04, 0x00, 0x45],
},
MacTest {
uplink: true,
command: MACCommand::LinkADRAns(LinkADRAnsPayload {
ch_mask_ack: true,
dr_ack: false,
tx_power_ack: false,
}),
bytes: vec![0x03, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::LinkADRAns(LinkADRAnsPayload {
ch_mask_ack: false,
dr_ack: true,
tx_power_ack: false,
}),
bytes: vec![0x03, 0x02],
},
MacTest {
uplink: true,
command: MACCommand::LinkADRAns(LinkADRAnsPayload {
ch_mask_ack: false,
dr_ack: false,
tx_power_ack: true,
}),
bytes: vec![0x03, 0x04],
},
MacTest {
uplink: true,
command: MACCommand::LinkADRAns(LinkADRAnsPayload {
ch_mask_ack: true,
dr_ack: true,
tx_power_ack: true,
}),
bytes: vec![0x03, 0x07],
},
MacTest {
uplink: false,
command: MACCommand::DutyCycleReq(DutyCycleReqPayload { max_duty_cycle: 13 }),
bytes: vec![0x04, 0x0d],
},
MacTest {
uplink: true,
command: MACCommand::DutyCycleAns,
bytes: vec![0x04],
},
MacTest {
uplink: false,
command: MACCommand::RxParamSetupReq(RxParamSetupReqPayload {
frequency: 26265700,
dl_settings: DLSettings {
rx2_dr: 11,
rx1_dr_offset: 3,
opt_neg: false,
},
}),
bytes: vec![0x05, 0x3b, 0x01, 0x02, 0x04],
},
MacTest {
uplink: true,
command: MACCommand::RxParamSetupAns(RxParamSetupAnsPayload {
channel_ack: true,
rx2_dr_ack: false,
rx1_dr_offset_ack: true,
}),
bytes: vec![0x05, 0x05],
},
MacTest {
uplink: false,
command: MACCommand::DevStatusReq,
bytes: vec![0x06],
},
MacTest {
uplink: true,
command: MACCommand::DevStatusAns(DevStatusAnsPayload {
battery: 0,
margin: -30,
}),
bytes: vec![0x06, 0x00, 0x22],
},
MacTest {
uplink: true,
command: MACCommand::DevStatusAns(DevStatusAnsPayload {
battery: 255,
margin: 30,
}),
bytes: vec![0x06, 0xff, 0x1e],
},
MacTest {
uplink: true,
command: MACCommand::DevStatusAns(DevStatusAnsPayload {
battery: 127,
margin: -1,
}),
bytes: vec![0x06, 0x7f, 0x3f],
},
MacTest {
uplink: true,
command: MACCommand::DevStatusAns(DevStatusAnsPayload {
battery: 127,
margin: 0,
}),
bytes: vec![0x06, 0x7f, 0x00],
},
MacTest {
uplink: false,
command: MACCommand::NewChannelReq(NewChannelReqPayload {
ch_index: 3,
freq: 26265700,
max_dr: 5,
min_dr: 10,
}),
bytes: vec![0x07, 0x03, 0x01, 0x02, 0x04, 0x5a],
},
MacTest {
uplink: false,
command: MACCommand::NewChannelReq(NewChannelReqPayload {
ch_index: 3,
freq: 2_410_000_000,
max_dr: 5,
min_dr: 0,
}),
bytes: vec![7, 3, 80, 222, 183, 80],
},
MacTest {
uplink: true,
command: MACCommand::NewChannelAns(NewChannelAnsPayload {
channel_freq_ok: false,
dr_range_ok: false,
}),
bytes: vec![0x07, 0x00],
},
MacTest {
uplink: true,
command: MACCommand::NewChannelAns(NewChannelAnsPayload {
channel_freq_ok: true,
dr_range_ok: false,
}),
bytes: vec![0x07, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::NewChannelAns(NewChannelAnsPayload {
channel_freq_ok: false,
dr_range_ok: true,
}),
bytes: vec![0x07, 0x02],
},
MacTest {
uplink: true,
command: MACCommand::NewChannelAns(NewChannelAnsPayload {
channel_freq_ok: true,
dr_range_ok: true,
}),
bytes: vec![0x07, 0x03],
},
MacTest {
uplink: false,
command: MACCommand::RxTimingSetupReq(RxTimingSetupReqPayload { delay: 15 }),
bytes: vec![0x08, 0x0f],
},
MacTest {
uplink: true,
command: MACCommand::RxTimingSetupAns,
bytes: vec![0x08],
},
MacTest {
uplink: false,
command: MACCommand::TxParamSetupReq(TxParamSetupReqPayload {
uplink_dwell_time: DwellTime::NoLimit,
downlink_dwell_time: DwellTime::NoLimit,
max_eirp: 15,
}),
bytes: vec![0x09, 0x0f],
},
MacTest {
uplink: false,
command: MACCommand::TxParamSetupReq(TxParamSetupReqPayload {
uplink_dwell_time: DwellTime::Limit400ms,
downlink_dwell_time: DwellTime::NoLimit,
max_eirp: 15,
}),
bytes: vec![0x09, 0x1f],
},
MacTest {
uplink: false,
command: MACCommand::TxParamSetupReq(TxParamSetupReqPayload {
uplink_dwell_time: DwellTime::NoLimit,
downlink_dwell_time: DwellTime::Limit400ms,
max_eirp: 15,
}),
bytes: vec![0x09, 0x2f],
},
MacTest {
uplink: true,
command: MACCommand::TxParamSetupAns,
bytes: vec![0x09],
},
MacTest {
uplink: false,
command: MACCommand::DlChannelReq(DlChannelReqPayload {
ch_index: 0,
freq: 868100000,
}),
bytes: vec![0x0a, 0x00, 0x28, 0x76, 0x84],
},
MacTest {
uplink: false,
command: MACCommand::DlChannelReq(DlChannelReqPayload {
ch_index: 1,
freq: 868200000,
}),
bytes: vec![0x0a, 0x01, 0x10, 0x7a, 0x84],
},
MacTest {
uplink: true,
command: MACCommand::DlChannelAns(DlChannelAnsPayload {
uplink_freq_exists: false,
channel_freq_ok: false,
}),
bytes: vec![0x0a, 0x00],
},
MacTest {
uplink: true,
command: MACCommand::DlChannelAns(DlChannelAnsPayload {
uplink_freq_exists: false,
channel_freq_ok: true,
}),
bytes: vec![0x0a, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::DlChannelAns(DlChannelAnsPayload {
uplink_freq_exists: true,
channel_freq_ok: false,
}),
bytes: vec![0x0a, 0x02],
},
MacTest {
uplink: true,
command: MACCommand::DlChannelAns(DlChannelAnsPayload {
uplink_freq_exists: true,
channel_freq_ok: true,
}),
bytes: vec![0x0a, 0x03],
},
MacTest {
uplink: false,
command: MACCommand::RekeyConf(RekeyConfPayload {
serv_lorawan_version: Version::LoRaWAN1_1,
}),
bytes: vec![0x0b, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::RekeyInd(RekeyIndPayload {
dev_lorawan_version: Version::LoRaWAN1_1,
}),
bytes: vec![0x0b, 0x01],
},
MacTest {
uplink: false,
command: MACCommand::ADRParamSetupReq(ADRParamSetupReqPayload {
adr_param: ADRParam {
limit_exp: 10,
delay_exp: 15,
},
}),
bytes: vec![0x0c, 0xaf],
},
MacTest {
uplink: true,
command: MACCommand::ADRParamSetupAns,
bytes: vec![0x0c],
},
MacTest {
uplink: true,
command: MACCommand::DeviceTimeReq,
bytes: vec![0x0d],
},
MacTest {
uplink: false,
command: MACCommand::DeviceTimeAns(DeviceTimeAnsPayload {
time_since_gps_epoch: Duration::from_secs(1),
}),
bytes: vec![0x0d, 0x01, 0x00, 0x00, 0x00, 0x00],
},
MacTest {
uplink: false,
command: MACCommand::DeviceTimeAns(DeviceTimeAnsPayload {
time_since_gps_epoch: Duration::new(1, 2 * 3906250),
}),
bytes: vec![0x0d, 0x01, 0x00, 0x00, 0x00, 0x02],
},
MacTest {
uplink: false,
command: MACCommand::ForceRejoinReq(ForceRejoinReqPayload {
period: 3,
max_retries: 4,
rejoin_type: 2,
dr: 5,
}),
bytes: vec![0x0e, 0x25, 0x1c],
},
MacTest {
uplink: false,
command: MACCommand::RejoinParamSetupReq(RejoinParamSetupReqPayload {
max_time_n: 14,
max_count_n: 15,
}),
bytes: vec![0x0f, 0xef],
},
MacTest {
uplink: true,
command: MACCommand::RejoinParamSetupAns(RejoinParamSetupAnsPayload {
time_ok: true,
}),
bytes: vec![0x0f, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::PingSlotInfoReq(PingSlotInfoReqPayload { periodicity: 3 }),
bytes: vec![0x10, 0x03],
},
MacTest {
uplink: false,
command: MACCommand::PingSlotInfoAns,
bytes: vec![0x10],
},
MacTest {
uplink: false,
command: MACCommand::PingSlotChannelReq(PingSlotChannelReqPayload {
freq: 868100000,
dr: 5,
}),
bytes: vec![0x11, 0x28, 0x76, 0x84, 0x05],
},
MacTest {
uplink: true,
command: MACCommand::PingSlotChannelAns(PingSlotChannelAnsPayload {
dr_ok: false,
channel_freq_ok: false,
}),
bytes: vec![0x011, 0x00],
},
MacTest {
uplink: true,
command: MACCommand::PingSlotChannelAns(PingSlotChannelAnsPayload {
dr_ok: false,
channel_freq_ok: true,
}),
bytes: vec![0x011, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::PingSlotChannelAns(PingSlotChannelAnsPayload {
dr_ok: true,
channel_freq_ok: false,
}),
bytes: vec![0x011, 0x02],
},
MacTest {
uplink: true,
command: MACCommand::PingSlotChannelAns(PingSlotChannelAnsPayload {
dr_ok: true,
channel_freq_ok: true,
}),
bytes: vec![0x11, 0x03],
},
MacTest {
uplink: false,
command: MACCommand::BeaconFreqReq(BeaconFreqReqPayload { freq: 868100000 }),
bytes: vec![0x13, 0x28, 0x76, 0x84],
},
MacTest {
uplink: true,
command: MACCommand::BeaconFreqAns(BeaconFreqAnsPayload {
beacon_freq_ok: false,
}),
bytes: vec![0x13, 0x00],
},
MacTest {
uplink: true,
command: MACCommand::BeaconFreqAns(BeaconFreqAnsPayload {
beacon_freq_ok: true,
}),
bytes: vec![0x13, 0x01],
},
MacTest {
uplink: true,
command: MACCommand::DeviceModeInd(DeviceModeIndPayload {
class: DeviceModeClass::ClassA,
}),
bytes: vec![0x20, 0x00],
},
MacTest {
uplink: true,
command: MACCommand::DeviceModeInd(DeviceModeIndPayload {
class: DeviceModeClass::ClassC,
}),
bytes: vec![0x20, 0x02],
},
MacTest {
uplink: false,
command: MACCommand::DeviceModeConf(DeviceModeConfPayload {
class: DeviceModeClass::ClassA,
}),
bytes: vec![0x20, 0x00],
},
MacTest {
uplink: false,
command: MACCommand::DeviceModeConf(DeviceModeConfPayload {
class: DeviceModeClass::ClassC,
}),
bytes: vec![0x20, 0x02],
},
MacTest {
uplink: false,
command: MACCommand::RelayConfReq(RelayConfReqPayload {
channel_settings_relay: ChannelSettingsRelay {
start_stop: 1,
cad_periodicity: 3,
default_ch_idx: 0,
second_ch_idx: 1,
second_ch_dr: 4,
second_ch_ack_offset: 5,
},
second_ch_freq: 868100000,
}),
bytes: vec![64, 165, 44, 40, 118, 132],
},
MacTest {
uplink: true,
command: MACCommand::RelayConfAns(RelayConfAnsPayload {
second_ch_freq_ack: true,
second_ch_ack_offset_ack: true,
second_ch_dr_ack: true,
second_ch_idx_ack: true,
default_ch_idx_ack: true,
cad_periodicity_ack: true,
}),
bytes: vec![64, 63],
},
MacTest {
uplink: true,
command: MACCommand::RelayConfAns(RelayConfAnsPayload {
second_ch_freq_ack: true,
second_ch_ack_offset_ack: false,
second_ch_dr_ack: true,
second_ch_idx_ack: false,
default_ch_idx_ack: true,
cad_periodicity_ack: false,
}),
bytes: vec![64, 21],
},
MacTest {
uplink: false,
command: MACCommand::EndDeviceConfReq(EndDeviceConfReqPayload {
second_ch_freq: 868100000,
channel_settings_ed: ChannelSettingsED {
second_ch_ack_offset: 5,
second_ch_dr: 4,
second_ch_idx: 1,
backoff: 63,
},
activation_relay_mode: ActivationRelayMode {
relay_mode_activation: RelayModeActivation::Dynamic,
smart_enable_level: 3,
},
}),
bytes: vec![65, 11, 165, 126, 40, 118, 132],
},
MacTest {
uplink: true,
command: MACCommand::EndDeviceConfAns(EndDeviceConfAnsPayload {
second_ch_freq_ack: true,
second_ch_dr_ack: false,
second_ch_idx_ack: true,
backoff_ack: true,
}),
bytes: vec![65, 13],
},
MacTest {
uplink: false,
command: MACCommand::FilterListReq(FilterListReqPayload {
filter_list_idx: 3,
filter_list_action: FilterListAction::Forward,
filter_list_eui: vec![1, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 2],
}),
bytes: vec![66, 176, 1, 2, 2, 3, 4, 5, 6, 7, 8, 8, 7, 6, 5, 4, 3, 2, 1],
},
MacTest {
uplink: true,
command: MACCommand::FilterListAns(FilterListAnsPayload {
filter_list_action_ack: false,
filter_list_len_ack: true,
combined_rules_ack: false,
}),
bytes: vec![66, 2],
},
MacTest {
uplink: false,
command: MACCommand::UpdateUplinkListReq(UpdateUplinkListReqPayload {
uplink_list_idx: 3,
uplink_limit: UplinkLimitPL {
reload_rate: 60,
bucket_size: 2,
},
dev_addr: crate::DevAddr::from_be_bytes([1, 2, 3, 4]),
w_fcnt: 128,
root_wor_s_key: crate::AES128Key::from_bytes([
1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8,
]),
}),
bytes: vec![
67, 3, 188, 4, 3, 2, 1, 128, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6,
7, 8,
],
},
MacTest {
uplink: true,
command: MACCommand::UpdateUplinkListAns,
bytes: vec![67],
},
MacTest {
uplink: false,
command: MACCommand::CtrlUplinkListReq(CtrlUplinkListReqPayload {
ctrl_uplink_action: CtrlUplinkActionPL {
uplink_list_idx: 3,
ctrl_uplink_action: 1,
},
}),
bytes: vec![68, 19],
},
MacTest {
uplink: true,
command: MACCommand::CtrlUplinkListAns(CtrlUplinkListAnsPayload {
uplink_list_idx_ack: true,
w_fcnt: 128,
}),
bytes: vec![68, 1, 128, 0, 0, 0],
},
MacTest {
uplink: false,
command: MACCommand::ConfigureFwdLimitReq(ConfigureFwdLimitReqPayload {
reload_rate: FwdLimitReloadRatePL {
overall_reload_rate: 100,
global_uplink_reload_rate: 90,
notify_reload_rate: 80,
join_req_reload_rate: 70,
reset_limit_counter: ResetLimitCounter::NoChange,
},
load_capacity: FwdLimitLoadCapacityPL {
overall_limit_size: 2,
global_uplink_limit_size: 2,
notify_limit_size: 1,
join_req_limit_size: 3,
},
}),
bytes: vec![69, 100, 45, 212, 56, 218],
},
MacTest {
uplink: true,
command: MACCommand::ConfigureFwdLimitAns,
bytes: vec![69],
},
MacTest {
uplink: true,
command: MACCommand::NotifyNewEndDeviceReq(NotifyNewEndDeviceReqPayload {
dev_addr: crate::DevAddr::from_be_bytes([1, 2, 3, 4]),
power_level: PowerLevel {
wor_snr: -10,
wor_rssi: -120,
},
}),
bytes: vec![70, 4, 3, 2, 1, 42, 13],
},
];
for tst in tests {
assert_eq!(
tst.bytes,
MACCommandSet::new(vec![tst.command.clone()])
.to_vec()
.unwrap()
);
let mut command = MACCommandSet::from_slice(&tst.bytes);
command.decode_from_raw(tst.uplink).unwrap();
assert_eq!(MACCommandSet::new(vec![tst.command.clone()]), command);
}
}
}