use std::net::Ipv4Addr;
use bytes::{Buf, BufMut, BytesMut};
use super::ServiceFamily;
use crate::address::IndividualAddress;
use crate::error::{KnxError, KnxResult};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum DibType {
DeviceInfo = 0x01,
SupportedServiceFamilies = 0x02,
IpConfig = 0x03,
IpCurrentConfig = 0x04,
KnxAddresses = 0x05,
SecuredServiceFamilies = 0x06,
TunnellingInfo = 0x07,
ExtendedDeviceInfo = 0x08,
ManufacturerData = 0xFE,
}
impl DibType {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0x01 => Some(Self::DeviceInfo),
0x02 => Some(Self::SupportedServiceFamilies),
0x03 => Some(Self::IpConfig),
0x04 => Some(Self::IpCurrentConfig),
0x05 => Some(Self::KnxAddresses),
0x06 => Some(Self::SecuredServiceFamilies),
0x07 => Some(Self::TunnellingInfo),
0x08 => Some(Self::ExtendedDeviceInfo),
0xFE => Some(Self::ManufacturerData),
_ => None,
}
}
}
impl From<DibType> for u8 {
fn from(dt: DibType) -> Self {
dt as u8
}
}
#[derive(Debug, Clone)]
pub enum Dib {
DeviceInfo(DeviceInfo),
SupportedServiceFamilies(SupportedServiceFamilies),
Generic { dib_type: u8, data: Vec<u8> },
}
impl Dib {
pub fn dib_type(&self) -> u8 {
match self {
Self::DeviceInfo(_) => DibType::DeviceInfo as u8,
Self::SupportedServiceFamilies(_) => DibType::SupportedServiceFamilies as u8,
Self::Generic { dib_type, .. } => *dib_type,
}
}
pub fn encode(&self) -> Vec<u8> {
match self {
Self::DeviceInfo(info) => info.encode(),
Self::SupportedServiceFamilies(svc) => svc.encode(),
Self::Generic { dib_type, data } => {
let mut buf = BytesMut::with_capacity(2 + data.len());
buf.put_u8((2 + data.len()) as u8);
buf.put_u8(*dib_type);
buf.put_slice(data);
buf.to_vec()
}
}
}
pub fn decode(data: &[u8]) -> KnxResult<Self> {
if data.len() < 2 {
return Err(KnxError::frame_too_short(2, data.len()));
}
let length = data[0] as usize;
let dib_type = data[1];
if data.len() < length {
return Err(KnxError::frame_too_short(length, data.len()));
}
match DibType::from_u8(dib_type) {
Some(DibType::DeviceInfo) => Ok(Self::DeviceInfo(DeviceInfo::decode(&data[..length])?)),
Some(DibType::SupportedServiceFamilies) => Ok(Self::SupportedServiceFamilies(
SupportedServiceFamilies::decode(&data[..length])?,
)),
_ => Ok(Self::Generic {
dib_type,
data: data[2..length].to_vec(),
}),
}
}
pub fn decode_all(data: &[u8]) -> KnxResult<Vec<Self>> {
let mut dibs = Vec::new();
let mut remaining = data;
while remaining.len() >= 2 {
let length = remaining[0] as usize;
if length < 2 || remaining.len() < length {
break;
}
dibs.push(Self::decode(&remaining[..length])?);
remaining = &remaining[length..];
}
Ok(dibs)
}
}
#[derive(Debug, Clone)]
pub struct DeviceInfo {
pub knx_medium: KnxMedium,
pub device_status: u8,
pub individual_address: IndividualAddress,
pub project_installation_id: u16,
pub serial_number: [u8; 6],
pub multicast_address: Ipv4Addr,
pub mac_address: [u8; 6],
pub device_name: String,
}
impl DeviceInfo {
pub const LENGTH: u8 = 54;
pub fn new(name: impl Into<String>, individual_address: IndividualAddress) -> Self {
Self {
knx_medium: KnxMedium::Tp1,
device_status: 0x00,
individual_address,
project_installation_id: 0x0000,
serial_number: [0; 6],
multicast_address: Ipv4Addr::new(224, 0, 23, 12),
mac_address: [0; 6],
device_name: name.into(),
}
}
pub fn with_serial_number(mut self, serial: [u8; 6]) -> Self {
self.serial_number = serial;
self
}
pub fn with_mac_address(mut self, mac: [u8; 6]) -> Self {
self.mac_address = mac;
self
}
pub fn encode(&self) -> Vec<u8> {
let mut buf = BytesMut::with_capacity(Self::LENGTH as usize);
buf.put_u8(Self::LENGTH);
buf.put_u8(DibType::DeviceInfo as u8);
buf.put_u8(self.knx_medium as u8);
buf.put_u8(self.device_status);
buf.put_u16(self.individual_address.encode());
buf.put_u16(self.project_installation_id);
buf.put_slice(&self.serial_number);
buf.put_slice(&self.multicast_address.octets());
buf.put_slice(&self.mac_address);
let name_bytes = self.device_name.as_bytes();
let name_len = name_bytes.len().min(30);
buf.put_slice(&name_bytes[..name_len]);
for _ in name_len..30 {
buf.put_u8(0);
}
buf.to_vec()
}
pub fn decode(data: &[u8]) -> KnxResult<Self> {
if data.len() < Self::LENGTH as usize {
return Err(KnxError::frame_too_short(Self::LENGTH as usize, data.len()));
}
let mut buf = &data[2..];
let knx_medium = KnxMedium::from_u8(buf.get_u8()).unwrap_or(KnxMedium::Tp1);
let device_status = buf.get_u8();
let individual_address = IndividualAddress::decode(buf.get_u16());
let project_installation_id = buf.get_u16();
let mut serial_number = [0u8; 6];
serial_number.copy_from_slice(&buf[..6]);
buf = &buf[6..];
let multicast_address =
Ipv4Addr::new(buf.get_u8(), buf.get_u8(), buf.get_u8(), buf.get_u8());
let mut mac_address = [0u8; 6];
mac_address.copy_from_slice(&buf[..6]);
buf = &buf[6..];
let name_end = buf[..30].iter().position(|&b| b == 0).unwrap_or(30);
let device_name = String::from_utf8_lossy(&buf[..name_end]).to_string();
Ok(Self {
knx_medium,
device_status,
individual_address,
project_installation_id,
serial_number,
multicast_address,
mac_address,
device_name,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(u8)]
pub enum KnxMedium {
#[default]
Tp1 = 0x02,
Pl110 = 0x04,
Rf = 0x10,
Ip = 0x20,
}
impl KnxMedium {
pub fn from_u8(value: u8) -> Option<Self> {
match value {
0x02 => Some(Self::Tp1),
0x04 => Some(Self::Pl110),
0x10 => Some(Self::Rf),
0x20 => Some(Self::Ip),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct SupportedServiceFamilies {
pub families: Vec<ServiceFamilyEntry>,
}
#[derive(Debug, Clone, Copy)]
pub struct ServiceFamilyEntry {
pub family: ServiceFamily,
pub version: u8,
}
impl SupportedServiceFamilies {
pub fn default_families() -> Self {
Self {
families: vec![
ServiceFamilyEntry {
family: ServiceFamily::Core,
version: 1,
},
ServiceFamilyEntry {
family: ServiceFamily::DeviceManagement,
version: 1,
},
ServiceFamilyEntry {
family: ServiceFamily::Tunnelling,
version: 1,
},
],
}
}
pub fn with_family(mut self, family: ServiceFamily, version: u8) -> Self {
self.families.push(ServiceFamilyEntry { family, version });
self
}
pub fn encode(&self) -> Vec<u8> {
let length = 2 + self.families.len() * 2;
let mut buf = BytesMut::with_capacity(length);
buf.put_u8(length as u8);
buf.put_u8(DibType::SupportedServiceFamilies as u8);
for entry in &self.families {
buf.put_u8(entry.family.id());
buf.put_u8(entry.version);
}
buf.to_vec()
}
pub fn decode(data: &[u8]) -> KnxResult<Self> {
if data.len() < 2 {
return Err(KnxError::frame_too_short(2, data.len()));
}
let length = data[0] as usize;
if data.len() < length {
return Err(KnxError::frame_too_short(length, data.len()));
}
let mut families = Vec::new();
let mut buf = &data[2..length];
while buf.len() >= 2 {
let family_id = buf.get_u8();
let version = buf.get_u8();
families.push(ServiceFamilyEntry {
family: ServiceFamily::from_id(family_id),
version,
});
}
Ok(Self { families })
}
pub fn supports(&self, family: ServiceFamily) -> bool {
self.families.iter().any(|e| e.family == family)
}
pub fn version(&self, family: ServiceFamily) -> Option<u8> {
self.families
.iter()
.find(|e| e.family == family)
.map(|e| e.version)
}
}
impl Default for SupportedServiceFamilies {
fn default() -> Self {
Self::default_families()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_info_encode_decode() {
let info = DeviceInfo::new("Test Device", IndividualAddress::new(1, 2, 3))
.with_serial_number([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
.with_mac_address([0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF]);
let encoded = info.encode();
assert_eq!(encoded.len(), DeviceInfo::LENGTH as usize);
let decoded = DeviceInfo::decode(&encoded).unwrap();
assert_eq!(decoded.device_name, "Test Device");
assert_eq!(decoded.individual_address.to_string(), "1.2.3");
assert_eq!(decoded.serial_number, [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
}
#[test]
fn test_supported_service_families() {
let families = SupportedServiceFamilies::default_families();
assert!(families.supports(ServiceFamily::Core));
assert!(families.supports(ServiceFamily::Tunnelling));
assert!(!families.supports(ServiceFamily::Routing));
let encoded = families.encode();
let decoded = SupportedServiceFamilies::decode(&encoded).unwrap();
assert_eq!(decoded.families.len(), families.families.len());
}
#[test]
fn test_dib_decode_all() {
let device_info = DeviceInfo::new("Test", IndividualAddress::new(1, 1, 1));
let families = SupportedServiceFamilies::default_families();
let mut data = device_info.encode();
data.extend(families.encode());
let dibs = Dib::decode_all(&data).unwrap();
assert_eq!(dibs.len(), 2);
assert!(matches!(dibs[0], Dib::DeviceInfo(_)));
assert!(matches!(dibs[1], Dib::SupportedServiceFamilies(_)));
}
}