#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::{
fmt,
net::{IpAddr, SocketAddr},
time::Instant,
};
#[cfg(not(feature = "std"))]
use core::fmt;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(feature = "std")]
pub type Result<T> = std::result::Result<T, TransportError>;
#[cfg(not(feature = "std"))]
pub type Result<T> = core::result::Result<T, TransportError>;
#[derive(Debug)]
pub enum TransportError {
#[cfg(feature = "std")]
IoError(std::io::Error),
InvalidBvll(String),
RegistrationFailed,
NotConnected,
InvalidConfiguration(String),
}
impl fmt::Display for TransportError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "std")]
TransportError::IoError(e) => write!(f, "I/O error: {}", e),
TransportError::InvalidBvll(msg) => write!(f, "Invalid BVLL: {}", msg),
TransportError::RegistrationFailed => write!(f, "Foreign device registration failed"),
TransportError::NotConnected => write!(f, "Transport not connected"),
TransportError::InvalidConfiguration(msg) => {
write!(f, "Invalid configuration: {}", msg)
}
}
}
}
#[cfg(feature = "std")]
impl Error for TransportError {}
#[cfg(feature = "std")]
impl From<std::io::Error> for TransportError {
fn from(error: std::io::Error) -> Self {
TransportError::IoError(error)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BvllType {
BacnetIp = 0x81,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum BvllFunction {
OriginalUnicastNpdu = 0x0A,
OriginalBroadcastNpdu = 0x0B,
SecureBvll = 0x0C,
DistributeBroadcastToNetwork = 0x09,
RegisterForeignDevice = 0x05,
ReadBroadcastDistributionTable = 0x02,
ReadBroadcastDistributionTableAck = 0x03,
ForwardedNpdu = 0x04,
WriteBroadcastDistributionTable = 0x01,
ReadForeignDeviceTable = 0x06,
ReadForeignDeviceTableAck = 0x07,
DeleteForeignDeviceTableEntry = 0x08,
Result = 0x00,
}
#[derive(Debug, Clone)]
pub struct BvllHeader {
pub bvll_type: BvllType,
pub function: BvllFunction,
pub length: u16,
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct ForeignDeviceRegistration {
pub bbmd_address: SocketAddr,
pub ttl: u16,
pub last_registration: Instant,
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct BdtEntry {
pub address: IpAddr,
pub port: u16,
pub mask: IpAddr,
}
#[cfg(feature = "std")]
pub trait Transport: Send + Sync {
fn send(&mut self, data: &[u8], dest: &SocketAddr) -> Result<()>;
fn receive(&mut self) -> Result<(Vec<u8>, SocketAddr)>;
fn local_address(&self) -> Result<SocketAddr>;
fn is_connected(&self) -> bool;
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct BacnetIpConfig {
pub bind_address: SocketAddr,
pub broadcast_enabled: bool,
pub foreign_device: Option<ForeignDeviceRegistration>,
pub bdt: Vec<BdtEntry>,
pub buffer_size: usize,
}
#[cfg(feature = "std")]
impl Default for BacnetIpConfig {
fn default() -> Self {
Self {
bind_address: "0.0.0.0:47808".parse().unwrap(),
broadcast_enabled: true,
foreign_device: None,
bdt: Vec::new(),
buffer_size: 1500,
}
}
}
pub mod constants {
pub const BACNET_IP_PORT: u16 = 0xBAC0;
pub const MAX_BVLL_LENGTH: usize = 1497;
pub const BVLL_HEADER_SIZE: usize = 4;
pub const DEFAULT_FD_TTL: u16 = 900; }