use crate::protocol::lookups::*;
use crate::protocol::management::address_claiming::AddressClaimStrategy;
use crate::protocol::messages::Pgn60928;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct IsoName(u64);
impl IsoName {
#[inline]
pub const fn from_raw(raw: u64) -> Self {
Self(raw)
}
#[inline]
pub const fn raw(&self) -> u64 {
self.0
}
#[inline]
pub const fn builder() -> IsoNameBuilder {
IsoNameBuilder::new()
}
#[inline]
pub const fn unique_number(&self) -> u32 {
(self.0 & 0x1F_FFFF) as u32
}
#[inline]
pub const fn manufacturer_code(&self) -> u16 {
((self.0 >> 21) & 0x7FF) as u16
}
#[inline]
pub const fn device_instance_lower(&self) -> u8 {
((self.0 >> 32) & 0x07) as u8
}
#[inline]
pub const fn device_instance_upper(&self) -> u8 {
((self.0 >> 35) & 0x1F) as u8
}
#[inline]
pub const fn device_instance(&self) -> u8 {
self.device_instance_lower() | (self.device_instance_upper() << 3)
}
#[inline]
pub const fn device_function(&self) -> u8 {
((self.0 >> 40) & 0xFF) as u8
}
#[inline]
pub const fn spare(&self) -> bool {
((self.0 >> 48) & 0x01) != 0
}
#[inline]
pub const fn device_class(&self) -> u8 {
((self.0 >> 49) & 0x7F) as u8
}
#[inline]
pub const fn system_instance(&self) -> u8 {
((self.0 >> 56) & 0x0F) as u8
}
#[inline]
pub const fn industry_group(&self) -> u8 {
((self.0 >> 60) & 0x07) as u8
}
#[inline]
pub const fn is_arbitrary_address_capable(&self) -> bool {
((self.0 >> 63) & 0x01) != 0
}
#[inline]
pub const fn is_marine(&self) -> bool {
self.industry_group() == 4
}
pub fn is_addr_capable_and_isoname_match(&self, strategy: AddressClaimStrategy<'_>) -> bool {
let is_addr_capable = self.is_arbitrary_address_capable();
match strategy {
AddressClaimStrategy::Fixed { preferred: _ } => !is_addr_capable,
AddressClaimStrategy::SelfConfigurable { addresses: _ } => !is_addr_capable,
AddressClaimStrategy::Arbitrary { preferred: _ } => is_addr_capable,
}
}
}
impl From<u64> for IsoName {
#[inline]
fn from(raw: u64) -> Self {
Self::from_raw(raw)
}
}
impl From<IsoName> for u64 {
#[inline]
fn from(name: IsoName) -> Self {
name.raw()
}
}
impl From<Pgn60928> for IsoName {
fn from(pgn: Pgn60928) -> Self {
Self::builder()
.unique_number(pgn.unique_number)
.manufacturer_code(u16::from(pgn.manufacturer_code))
.device_instance_lower(pgn.device_instance_lower)
.device_instance_upper(pgn.device_instance_upper)
.device_function(pgn.device_function) .spare(false) .device_class(u8::from(pgn.device_class))
.system_instance(pgn.system_instance)
.industry_group(u8::from(pgn.industry_group))
.arbitrary_address_capable(pgn.arbitrary_address_capable != YesNo::No)
.build()
}
}
impl From<IsoName> for Pgn60928 {
fn from(name: IsoName) -> Self {
let mut pgn = Pgn60928::new();
pgn.unique_number = name.unique_number();
pgn.manufacturer_code = ManufacturerCode::from(name.manufacturer_code());
pgn.device_instance_lower = name.device_instance_lower();
pgn.device_instance_upper = name.device_instance_upper();
pgn.device_function = name.device_function(); pgn.device_class = DeviceClass::from(name.device_class());
pgn.system_instance = name.system_instance();
pgn.industry_group = IndustryCode::from(name.industry_group());
pgn.arbitrary_address_capable = if name.is_arbitrary_address_capable() {
YesNo::Yes
} else {
YesNo::No
};
pgn
}
}
impl fmt::Display for IsoName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"IsoName {{ unique: {}, mfg: {}, func: {}, class: {}, inst: {}, aac: {} }}",
self.unique_number(),
self.manufacturer_code(),
self.device_function(),
self.device_class(),
self.device_instance(),
self.is_arbitrary_address_capable()
)
}
}
#[derive(Debug, Clone, Copy)]
pub struct IsoNameBuilder {
raw: u64,
}
impl IsoNameBuilder {
#[inline]
pub const fn new() -> Self {
Self { raw: 0 }
}
#[inline]
pub const fn unique_number(mut self, value: u32) -> Self {
assert!(value <= 0x1F_FFFF, "Unique number must fit in 21 bits");
self.raw = (self.raw & !0x1F_FFFF) | (value as u64 & 0x1F_FFFF);
self
}
#[inline]
pub const fn manufacturer_code(mut self, value: u16) -> Self {
assert!(value <= 0x7FF, "Manufacturer code must fit in 11 bits");
self.raw = (self.raw & !(0x7FF << 21)) | ((value as u64 & 0x7FF) << 21);
self
}
#[inline]
pub const fn device_instance_lower(mut self, value: u8) -> Self {
assert!(value <= 0x07, "Device instance lower must fit in 3 bits");
self.raw = (self.raw & !(0x07 << 32)) | ((value as u64 & 0x07) << 32);
self
}
#[inline]
pub const fn device_instance_upper(mut self, value: u8) -> Self {
assert!(value <= 0x1F, "Device instance upper must fit in 5 bits");
self.raw = (self.raw & !(0x1F << 35)) | ((value as u64 & 0x1F) << 35);
self
}
#[inline]
pub const fn device_instance(self, value: u8) -> Self {
self.device_instance_lower(value & 0x07)
.device_instance_upper((value >> 3) & 0x1F)
}
#[inline]
pub const fn device_function(mut self, value: u8) -> Self {
self.raw = (self.raw & !(0xFF << 40)) | ((value as u64) << 40);
self
}
#[inline]
pub const fn spare(mut self, value: bool) -> Self {
self.raw = (self.raw & !(0x01 << 48)) | ((value as u64) << 48);
self
}
#[inline]
pub const fn device_class(mut self, value: u8) -> Self {
assert!(value <= 0x7F, "Device class must fit in 7 bits");
self.raw = (self.raw & !(0x7F << 49)) | ((value as u64 & 0x7F) << 49);
self
}
#[inline]
pub const fn system_instance(mut self, value: u8) -> Self {
assert!(value <= 0x0F, "System instance must fit in 4 bits");
self.raw = (self.raw & !(0x0F << 56)) | ((value as u64 & 0x0F) << 56);
self
}
#[inline]
pub const fn industry_group(mut self, value: u8) -> Self {
assert!(value <= 0x07, "Industry group must fit in 3 bits");
self.raw = (self.raw & !(0x07 << 60)) | ((value as u64 & 0x07) << 60);
self
}
#[inline]
pub const fn arbitrary_address_capable(mut self, value: bool) -> Self {
self.raw = (self.raw & !(0x01 << 63)) | ((value as u64) << 63);
self
}
#[inline]
pub const fn build(self) -> IsoName {
IsoName(self.raw)
}
}
impl Default for IsoNameBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unique_number_extraction() {
let name = IsoName::builder().unique_number(0x1ABCDE).build();
assert_eq!(name.unique_number(), 0x1ABCDE);
}
#[test]
fn test_manufacturer_code_extraction() {
let name = IsoName::builder().manufacturer_code(275).build();
assert_eq!(name.manufacturer_code(), 275);
}
#[test]
fn test_arbitrary_address_capable() {
let name_aac = IsoName::builder().arbitrary_address_capable(true).build();
assert!(name_aac.is_arbitrary_address_capable());
let name_not_aac = IsoName::builder().arbitrary_address_capable(false).build();
assert!(!name_not_aac.is_arbitrary_address_capable());
}
#[test]
fn test_device_instance() {
let name = IsoName::builder().device_instance(0xAB).build();
assert_eq!(name.device_instance(), 0xAB);
}
#[test]
fn test_all_fields() {
let name = IsoName::builder()
.unique_number(123456)
.manufacturer_code(275)
.device_instance(42)
.device_function(130)
.device_class(25)
.system_instance(7)
.industry_group(4)
.arbitrary_address_capable(true)
.build();
assert_eq!(name.unique_number(), 123456);
assert_eq!(name.manufacturer_code(), 275);
assert_eq!(name.device_instance(), 42);
assert_eq!(name.device_function(), 130);
assert_eq!(name.device_class(), 25);
assert_eq!(name.system_instance(), 7);
assert_eq!(name.industry_group(), 4);
assert!(name.is_arbitrary_address_capable());
assert!(name.is_marine());
}
#[test]
fn test_raw_conversion() {
let raw_value = 0x8123456789ABCDEF;
let name = IsoName::from_raw(raw_value);
assert_eq!(name.raw(), raw_value);
let converted: u64 = name.into();
assert_eq!(converted, raw_value);
}
#[test]
fn test_round_trip() {
let original = IsoName::builder()
.unique_number(0x12345)
.manufacturer_code(0x2AB)
.device_instance(0x55)
.device_function(0xAA)
.device_class(0x33)
.system_instance(0x0C)
.industry_group(0x04)
.arbitrary_address_capable(true)
.build();
let raw = original.raw();
let restored = IsoName::from_raw(raw);
assert_eq!(original, restored);
assert_eq!(original.unique_number(), restored.unique_number());
assert_eq!(original.manufacturer_code(), restored.manufacturer_code());
assert_eq!(original.device_instance(), restored.device_instance());
assert_eq!(original.device_function(), restored.device_function());
assert_eq!(original.device_class(), restored.device_class());
assert_eq!(original.system_instance(), restored.system_instance());
assert_eq!(original.industry_group(), restored.industry_group());
assert_eq!(
original.is_arbitrary_address_capable(),
restored.is_arbitrary_address_capable()
);
}
#[test]
fn test_bit_63_aac() {
let name_aac = IsoName::builder().arbitrary_address_capable(true).build();
assert_eq!(name_aac.raw() & (1u64 << 63), 1u64 << 63);
let name_not_aac = IsoName::builder().arbitrary_address_capable(false).build();
assert_eq!(name_not_aac.raw() & (1u64 << 63), 0);
}
#[test]
fn test_address_claiming_compatibility() {
let my_name_raw = 0x8000_0000_0000_0000u64; let iso_name = IsoName::from_raw(my_name_raw);
let old_method = (my_name_raw >> 63) & 1 == 1;
let new_method = iso_name.is_arbitrary_address_capable();
assert_eq!(old_method, new_method);
assert!(new_method);
}
#[test]
fn test_pgn60928_to_isoname_conversion() {
let mut pgn = Pgn60928::new();
pgn.unique_number = 123456;
pgn.manufacturer_code = ManufacturerCode::from(69); pgn.device_instance_lower = 3;
pgn.device_instance_upper = 5;
pgn.set_device_function(DeviceFunction::Diagnostic); pgn.device_class = DeviceClass::from(25); pgn.system_instance = 7;
pgn.arbitrary_address_capable = YesNo::Yes;
let iso_name: IsoName = pgn.into();
assert_eq!(iso_name.unique_number(), 123456);
assert_eq!(iso_name.manufacturer_code(), 69);
assert_eq!(iso_name.device_instance_lower(), 3);
assert_eq!(iso_name.device_instance_upper(), 5);
assert_eq!(iso_name.device_function(), 0x82); assert!(!iso_name.spare()); assert_eq!(iso_name.device_class(), 25);
assert_eq!(iso_name.system_instance(), 7);
assert!(iso_name.is_arbitrary_address_capable());
}
#[test]
fn test_isoname_to_pgn60928_conversion() {
let iso_name = IsoName::builder()
.unique_number(654321)
.manufacturer_code(78) .device_instance_lower(7)
.device_instance_upper(15)
.device_function(200) .spare(true)
.device_class(20) .system_instance(10)
.industry_group(4) .arbitrary_address_capable(false)
.build();
let pgn: Pgn60928 = iso_name.into();
assert_eq!(pgn.unique_number, 654321);
assert_eq!(u16::from(pgn.manufacturer_code), 78);
assert_eq!(pgn.device_instance_lower, 7);
assert_eq!(pgn.device_instance_upper, 15);
assert_eq!(u16::from(pgn.device_function), 200);
assert_eq!(u8::from(pgn.device_class), 20);
assert_eq!(pgn.system_instance, 10);
assert_eq!(u8::from(pgn.industry_group), 4);
assert_eq!(pgn.arbitrary_address_capable, YesNo::No);
}
#[test]
fn test_pgn60928_isoname_round_trip() {
let mut original_pgn = Pgn60928::new();
original_pgn.unique_number = 999888;
original_pgn.manufacturer_code = ManufacturerCode::from(88); original_pgn.device_instance_lower = 1;
original_pgn.device_instance_upper = 31;
original_pgn.set_device_function(DeviceFunction::AlarmEnunciator5230); original_pgn.device_class = DeviceClass::from(30); original_pgn.system_instance = 15;
original_pgn.arbitrary_address_capable = YesNo::Yes;
let iso_name: IsoName = original_pgn.into();
let restored_pgn: Pgn60928 = iso_name.into();
assert_eq!(original_pgn.unique_number, restored_pgn.unique_number);
assert_eq!(
original_pgn.manufacturer_code,
restored_pgn.manufacturer_code
);
assert_eq!(
original_pgn.device_instance_lower,
restored_pgn.device_instance_lower
);
assert_eq!(
original_pgn.device_instance_upper,
restored_pgn.device_instance_upper
);
assert_eq!(original_pgn.device_function, restored_pgn.device_function);
assert_eq!(original_pgn.device_class, restored_pgn.device_class);
assert_eq!(original_pgn.system_instance, restored_pgn.system_instance);
assert_eq!(original_pgn.industry_group, restored_pgn.industry_group);
assert_eq!(
original_pgn.arbitrary_address_capable,
restored_pgn.arbitrary_address_capable
);
}
}