Implement DevAddrPrefix type.

This type defines the (NetID derived) prefix that must be used when
generating device addresses. This can be retrieved from the NetID, but
it is also possible to define a prefix that defines a smaller address
range within the NetID.

See also #49.
This commit is contained in:
Orne Brocaar 2022-10-19 12:03:55 +01:00
parent e04f991e76
commit 6cc813aed1
3 changed files with 166 additions and 46 deletions

View File

@ -2,11 +2,97 @@ use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use anyhow::Result; use anyhow::Result;
use serde::{Serialize, Serializer}; use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::netid::NetID; use super::netid::NetID;
use crate::Error; use crate::Error;
#[derive(PartialEq, Eq, Copy, Clone, Default)]
pub struct DevAddrPrefix([u8; 4], u32);
impl DevAddrPrefix {
pub fn new(prefix: [u8; 4], size: u32) -> Self {
DevAddrPrefix(prefix, size)
}
fn prefix(&self) -> [u8; 4] {
self.0
}
fn size(&self) -> u32 {
self.1
}
}
impl fmt::Display for DevAddrPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", hex::encode(&self.0), self.1)
}
}
impl fmt::Debug for DevAddrPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}/{}", hex::encode(&self.0), self.1)
}
}
impl FromStr for DevAddrPrefix {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_string();
let parts: Vec<&str> = s.split("/").collect();
if parts.len() != 2 {
return Err(Error::DevAddrPrefixFormat);
}
if parts[0].len() != 8 {
return Err(Error::DevAddrPrefixFormat);
}
let mut mask: [u8; 4] = [0; 4];
hex::decode_to_slice(&parts[0], &mut mask)?;
let size: u32 = parts[1].parse().map_err(|_| Error::DevAddrPrefixFormat)?;
Ok(DevAddrPrefix(mask, size))
}
}
impl Serialize for DevAddrPrefix {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for DevAddrPrefix {
fn deserialize<D>(deserialize: D) -> Result<DevAddrPrefix, D::Error>
where
D: Deserializer<'de>,
{
deserialize.deserialize_str(DevAddrPrefixVisitor)
}
}
struct DevAddrPrefixVisitor;
impl<'de> Visitor<'de> for DevAddrPrefixVisitor {
type Value = DevAddrPrefix;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("A DevAddrPrefix in the format 00000000/0 is expected")
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
DevAddrPrefix::from_str(value).map_err(|e| E::custom(format!("{}", e)))
}
}
#[derive(PartialEq, Copy, Clone, AsExpression, FromSqlRow, Default)] #[derive(PartialEq, Copy, Clone, AsExpression, FromSqlRow, Default)]
#[diesel(sql_type = diesel::sql_types::Binary)] #[diesel(sql_type = diesel::sql_types::Binary)]
pub struct DevAddr([u8; 4]); pub struct DevAddr([u8; 4]);
@ -54,7 +140,7 @@ impl DevAddr {
pub fn is_net_id(&self, net_id: NetID) -> bool { pub fn is_net_id(&self, net_id: NetID) -> bool {
let mut dev_addr = *self; let mut dev_addr = *self;
dev_addr.set_addr_prefix(&net_id); dev_addr.set_dev_addr_prefix(net_id.dev_addr_prefix());
*self == dev_addr *self == dev_addr
} }
@ -83,18 +169,18 @@ impl DevAddr {
} }
} }
pub fn set_addr_prefix(&mut self, netid: &NetID) { pub fn set_dev_addr_prefix(&mut self, prefix: DevAddrPrefix) {
match netid.netid_type() { // convert devaddr to u32
0 => self.set_set_addr_prefix(1, 6, netid), let mut devaddr = u32::from_be_bytes(self.0);
1 => self.set_set_addr_prefix(2, 6, netid),
2 => self.set_set_addr_prefix(3, 9, netid), // clean the prefix bits
3 => self.set_set_addr_prefix(4, 11, netid), let mask = u32::MAX; // all u32 bits to 1
4 => self.set_set_addr_prefix(5, 12, netid), devaddr &= !(mask << (32 - prefix.size()));
5 => self.set_set_addr_prefix(6, 13, netid),
6 => self.set_set_addr_prefix(7, 15, netid), // set the prefix
7 => self.set_set_addr_prefix(8, 17, netid), devaddr |= u32::from_be_bytes(prefix.prefix());
_ => {}
} self.0 = devaddr.to_be_bytes();
} }
fn get_nwkid(&self, prefix_length: u32, nwkid_bits: u32) -> Vec<u8> { fn get_nwkid(&self, prefix_length: u32, nwkid_bits: u32) -> Vec<u8> {
@ -117,31 +203,6 @@ impl DevAddr {
out[4 - blen..].to_vec() out[4 - blen..].to_vec()
} }
fn set_set_addr_prefix(&mut self, prefix_length: u32, nwkid_bits: u32, netid: &NetID) {
// convert devaddr to u32
let mut devaddr = u32::from_be_bytes(self.0);
// clear the bits for the prefix and NwkID
let mask = u32::MAX; // all u32 bits to 1
devaddr &= !(mask << (32 - prefix_length - nwkid_bits));
// set the type prefix
let prefix: u32 = 254 << (32 - prefix_length);
let mut devaddr = devaddr | prefix;
// set the NwkID
let mut netid_bytes: [u8; 4] = [0; 4];
let netid_id = netid.id();
netid_bytes[4 - netid_id.len()..].clone_from_slice(&netid_id);
let mut nwkid = u32::from_be_bytes(netid_bytes);
nwkid <<= 32 - nwkid_bits; // truncate the MSB of the NetID ID field
nwkid >>= prefix_length; // shift base for the prefix MSB
devaddr |= nwkid;
self.0 = devaddr.to_be_bytes();
}
} }
impl fmt::Display for DevAddr { impl fmt::Display for DevAddr {
@ -237,42 +298,49 @@ mod tests {
} }
#[test] #[test]
fn test_to_le_bytes() { fn test_dev_addr_prefix() {
let p = DevAddrPrefix::from_str("01000000/8").unwrap();
assert_eq!(DevAddrPrefix::new([1, 0, 0, 0], 8), p);
assert_eq!("01000000/8", p.to_string());
}
#[test]
fn test_dev_addr_to_le_bytes() {
for tst in tests() { for tst in tests() {
assert_eq!(tst.bytes, tst.devaddr.to_le_bytes()); assert_eq!(tst.bytes, tst.devaddr.to_le_bytes());
} }
} }
#[test] #[test]
fn test_from_le_bytes() { fn test_dev_addr_from_le_bytes() {
for tst in tests() { for tst in tests() {
assert_eq!(tst.devaddr, DevAddr::from_le_bytes(tst.bytes)); assert_eq!(tst.devaddr, DevAddr::from_le_bytes(tst.bytes));
} }
} }
#[test] #[test]
fn test_netid_type() { fn test_dev_addr_netid_type() {
for tst in tests() { for tst in tests() {
assert_eq!(tst.netid_type, tst.devaddr.netid_type()); assert_eq!(tst.netid_type, tst.devaddr.netid_type());
} }
} }
#[test] #[test]
fn test_nwkid() { fn test_dev_addr_nwkid() {
for tst in tests() { for tst in tests() {
assert_eq!(tst.nwkid, tst.devaddr.nwkid()); assert_eq!(tst.nwkid, tst.devaddr.nwkid());
} }
} }
#[test] #[test]
fn test_string() { fn test_dev_addr_string() {
for tst in tests() { for tst in tests() {
assert_eq!(tst.string, tst.devaddr.to_string()); assert_eq!(tst.string, tst.devaddr.to_string());
} }
} }
#[test] #[test]
fn test_set_addr_prefix() { fn test_dev_addr_set_addr_prefix() {
let tests = vec![ let tests = vec![
SetAddrPrefixTest { SetAddrPrefixTest {
devaddr: DevAddr::from_be_bytes([0xff, 0xff, 0xff, 0xff]), devaddr: DevAddr::from_be_bytes([0xff, 0xff, 0xff, 0xff]),
@ -288,7 +356,7 @@ mod tests {
for tst in tests { for tst in tests {
let mut devaddr = tst.devaddr.clone(); let mut devaddr = tst.devaddr.clone();
devaddr.set_addr_prefix(&tst.netid); devaddr.set_dev_addr_prefix(tst.netid.dev_addr_prefix());
assert_eq!(tst.expected_devaddr, devaddr); assert_eq!(tst.expected_devaddr, devaddr);
} }
} }

View File

@ -14,6 +14,9 @@ pub enum Error {
#[error("DevAddr expects exactly 4 bytes")] #[error("DevAddr expects exactly 4 bytes")]
DevAddrLength, DevAddrLength,
#[error("DevAddrPrefix must be in the form 00000000/0")]
DevAddrPrefixFormat,
#[error(transparent)] #[error(transparent)]
FromHexError(#[from] hex::FromHexError), FromHexError(#[from] hex::FromHexError),
} }

View File

@ -5,6 +5,7 @@ use anyhow::Result;
use serde::de::{self, Visitor}; use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::devaddr::DevAddrPrefix;
use crate::Error; use crate::Error;
#[derive(Default, PartialEq, Clone, Copy, Hash, Eq)] #[derive(Default, PartialEq, Clone, Copy, Hash, Eq)]
@ -74,6 +75,38 @@ impl NetID {
out[4 - blen..].to_vec() out[4 - blen..].to_vec()
} }
pub fn dev_addr_prefix(&self) -> DevAddrPrefix {
match self.netid_type() {
0 => self.get_dev_addr_prefix(1, 6),
1 => self.get_dev_addr_prefix(2, 6),
2 => self.get_dev_addr_prefix(3, 9),
3 => self.get_dev_addr_prefix(4, 11),
4 => self.get_dev_addr_prefix(5, 12),
5 => self.get_dev_addr_prefix(6, 13),
6 => self.get_dev_addr_prefix(7, 15),
7 => self.get_dev_addr_prefix(8, 17),
_ => panic!("netid_type bug"),
}
}
fn get_dev_addr_prefix(&self, prefix_length: u32, nwkid_bits: u32) -> DevAddrPrefix {
// type prefix
let mut prefix: u32 = 254 << (32 - prefix_length);
// NwkID prefix
let mut netid_bytes: [u8; 4] = [0; 4];
let netid_id = self.id();
netid_bytes[4 - netid_id.len()..].clone_from_slice(&netid_id);
let mut nwkid = u32::from_be_bytes(netid_bytes);
nwkid <<= 32 - nwkid_bits; // truncate the MSB of the NetID ID field
nwkid >>= prefix_length; // shift base for the prefix MSB
// merge type prefix with nwkid.
prefix |= nwkid;
DevAddrPrefix::new(prefix.to_be_bytes(), prefix_length + nwkid_bits)
}
} }
impl fmt::Display for NetID { impl fmt::Display for NetID {
@ -144,6 +177,7 @@ mod tests {
id: Vec<u8>, id: Vec<u8>,
bytes: [u8; 3], bytes: [u8; 3],
string: String, string: String,
dev_addr_prefix: DevAddrPrefix,
} }
fn tests() -> Vec<Test> { fn tests() -> Vec<Test> {
@ -154,6 +188,7 @@ mod tests {
id: vec![0x2d], id: vec![0x2d],
bytes: [0x6d, 0x00, 0x00], bytes: [0x6d, 0x00, 0x00],
string: "00006d".into(), string: "00006d".into(),
dev_addr_prefix: DevAddrPrefix::new([0x5a, 0x00, 0x00, 0x00], 7),
}, },
Test { Test {
netid: NetID::from_be_bytes([0x20, 0x00, 0x6d]), netid: NetID::from_be_bytes([0x20, 0x00, 0x6d]),
@ -161,6 +196,7 @@ mod tests {
id: vec![0x2d], id: vec![0x2d],
bytes: [0x6d, 0x00, 0x20], bytes: [0x6d, 0x00, 0x20],
string: "20006d".into(), string: "20006d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xad, 0x00, 0x00, 0x00], 8),
}, },
Test { Test {
netid: NetID::from_be_bytes([0x40, 0x03, 0x6d]), netid: NetID::from_be_bytes([0x40, 0x03, 0x6d]),
@ -168,6 +204,7 @@ mod tests {
id: vec![0x01, 0x6d], id: vec![0x01, 0x6d],
bytes: [0x6d, 0x03, 0x40], bytes: [0x6d, 0x03, 0x40],
string: "40036d".into(), string: "40036d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xd6, 0xd0, 0x00, 0x00], 12),
}, },
Test { Test {
netid: NetID::from_be_bytes([0x76, 0xdb, 0x6d]), netid: NetID::from_be_bytes([0x76, 0xdb, 0x6d]),
@ -175,6 +212,7 @@ mod tests {
id: vec![0x16, 0xdb, 0x6d], id: vec![0x16, 0xdb, 0x6d],
bytes: [0x6d, 0xdb, 0x76], bytes: [0x6d, 0xdb, 0x76],
string: "76db6d".into(), string: "76db6d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xe6, 0xda, 0x00, 0x00], 15),
}, },
Test { Test {
netid: NetID::from_be_bytes([0x96, 0xdb, 0x6d]), netid: NetID::from_be_bytes([0x96, 0xdb, 0x6d]),
@ -182,6 +220,7 @@ mod tests {
id: vec![0x16, 0xdb, 0x6d], id: vec![0x16, 0xdb, 0x6d],
bytes: [0x6d, 0xdb, 0x96], bytes: [0x6d, 0xdb, 0x96],
string: "96db6d".into(), string: "96db6d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xf5, 0xb6, 0x80, 0x00], 17),
}, },
Test { Test {
netid: NetID::from_be_bytes([0xb6, 0xdb, 0x6d]), netid: NetID::from_be_bytes([0xb6, 0xdb, 0x6d]),
@ -189,6 +228,7 @@ mod tests {
id: vec![0x16, 0xdb, 0x6d], id: vec![0x16, 0xdb, 0x6d],
bytes: [0x6d, 0xdb, 0xb6], bytes: [0x6d, 0xdb, 0xb6],
string: "b6db6d".into(), string: "b6db6d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xfb, 0x6d, 0xa0, 0x00], 19),
}, },
Test { Test {
netid: NetID::from_be_bytes([0xd6, 0xdb, 0x6d]), netid: NetID::from_be_bytes([0xd6, 0xdb, 0x6d]),
@ -196,6 +236,7 @@ mod tests {
id: vec![0x16, 0xdb, 0x6d], id: vec![0x16, 0xdb, 0x6d],
bytes: [0x6d, 0xdb, 0xd6], bytes: [0x6d, 0xdb, 0xd6],
string: "d6db6d".into(), string: "d6db6d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xfd, 0x6d, 0xb4, 00], 22),
}, },
Test { Test {
netid: NetID::from_be_bytes([0xf6, 0xdb, 0x6d]), netid: NetID::from_be_bytes([0xf6, 0xdb, 0x6d]),
@ -203,6 +244,7 @@ mod tests {
id: vec![0x16, 0xdb, 0x6d], id: vec![0x16, 0xdb, 0x6d],
bytes: [0x6d, 0xdb, 0xf6], bytes: [0x6d, 0xdb, 0xf6],
string: "f6db6d".into(), string: "f6db6d".into(),
dev_addr_prefix: DevAddrPrefix::new([0xfe, 0x6d, 0xb6, 0x80], 25),
}, },
] ]
} }
@ -248,4 +290,11 @@ mod tests {
assert_eq!(tst.netid, NetID::from_str(&tst.string).unwrap()); assert_eq!(tst.netid, NetID::from_str(&tst.string).unwrap());
} }
} }
#[test]
fn test_dev_addr_prefix() {
for tst in tests() {
assert_eq!(tst.dev_addr_prefix, tst.netid.dev_addr_prefix());
}
}
} }