use bacnet_types::MacAddr;
use bacnet_types::enums::{NetworkType, PropertyIdentifier as P};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::{Bip6Bind, Bip6PortConfig, BipPortConfig};
use crate::common;
#[derive(Clone, Debug, PartialEq, Eq)]
pub(super) enum IpConfig {
V4(BipPortConfig),
V6(Bip6PortConfig),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PortAddress {
V4([u8; 4], u16),
V6([u8; 16], u16),
}
impl PortAddress {
pub(crate) fn udp(self) -> u16 {
match self {
Self::V4(_, udp) | Self::V6(_, udp) => udp,
}
}
pub(crate) fn same_family(self, other: Self) -> bool {
matches!(
(self, other),
(Self::V4(..), Self::V4(..)) | (Self::V6(..), Self::V6(..))
)
}
pub(crate) fn same_ip(self, other: Self) -> bool {
match (self, other) {
(Self::V4(a, _), Self::V4(b, _)) => a == b,
(Self::V6(a, _), Self::V6(b, _)) => a == b,
_ => false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PortBind {
V4([u8; 4], u16),
V6(Bip6Bind),
}
impl PortBind {
pub(crate) fn address(self) -> PortAddress {
match self {
Self::V4(ip, udp) => PortAddress::V4(ip, udp),
Self::V6(bind) => PortAddress::V6(bind.ip, bind.udp),
}
}
}
impl IpConfig {
pub(super) fn network_type(&self) -> NetworkType {
match self {
Self::V4(_) => NetworkType::IPV4,
Self::V6(_) => NetworkType::IPV6,
}
}
pub(super) fn network_number(&self) -> u16 {
match self {
Self::V4(config) => config.network_number,
Self::V6(config) => config.network_number,
}
}
pub(super) fn apdu_length(&self) -> u32 {
match self {
Self::V4(config) => config.apdu_length,
Self::V6(config) => config.apdu_length,
}
}
pub(super) fn validate(&self, instance: u32) -> Result<(), Error> {
match self {
Self::V4(config) => config.validate(instance),
Self::V6(config) => config.validate(instance),
}
}
pub(super) fn mac(&self) -> MacAddr {
match self {
Self::V4(config) => {
let mut mac = MacAddr::from_slice(&config.ip_address);
mac.extend_from_slice(&config.udp_port.to_be_bytes());
mac
}
Self::V6(config) => MacAddr::from_slice(&config.vmac),
}
}
pub(super) fn address(&self) -> PortAddress {
match self {
Self::V4(config) => PortAddress::V4(config.ip_address, config.udp_port),
Self::V6(config) => PortAddress::V6(config.ip_address, config.udp_port),
}
}
pub(super) fn with_bind(&self, bind: PortBind, capacity: u32) -> Result<Self, Error> {
Ok(match (self, bind) {
(Self::V4(config), PortBind::V4(ip, udp)) => Self::V4(BipPortConfig {
ip_address: ip,
udp_port: udp,
apdu_length: capacity,
..config.clone()
}),
(Self::V6(config), PortBind::V6(bind)) => Self::V6(Bip6PortConfig {
ip_address: bind.ip,
udp_port: bind.udp,
vmac: bind.vmac,
multicast_address: bind.multicast,
apdu_length: capacity,
..config.clone()
}),
_ => {
return Err(Error::Encoding(
"Network Port bind is for the other IP version".into(),
));
}
})
}
pub(super) fn is_ipv4(&self) -> bool {
matches!(self, Self::V4(_))
}
pub(super) fn mode_property(&self) -> P {
match self {
Self::V4(_) => P::BACNET_IP_MODE,
Self::V6(_) => P::BACNET_IPV6_MODE,
}
}
pub(super) fn read(
&self,
property: P,
array_index: Option<u32>,
) -> Option<Result<PropertyValue, Error>> {
let octets = |value: &[u8]| Some(Ok(PropertyValue::OctetString(value.to_vec())));
match self {
Self::V4(config) => match property {
P::IP_ADDRESS => octets(&config.ip_address),
P::IP_SUBNET_MASK => octets(&config.subnet_mask),
P::IP_DEFAULT_GATEWAY => octets(&config.default_gateway),
P::BACNET_IP_UDP_PORT => Some(Ok(PropertyValue::Unsigned(config.udp_port.into()))),
P::IP_DNS_SERVER => Some(address_array(&config.dns_servers, array_index)),
_ => None,
},
Self::V6(config) => match property {
P::IPV6_ADDRESS => octets(&config.ip_address),
P::IPV6_PREFIX_LENGTH => {
Some(Ok(PropertyValue::Unsigned(config.prefix_length.into())))
}
P::BACNET_IPV6_UDP_PORT => {
Some(Ok(PropertyValue::Unsigned(config.udp_port.into())))
}
P::IPV6_DEFAULT_GATEWAY => octets(&config.default_gateway),
P::BACNET_IPV6_MULTICAST_ADDRESS => octets(&config.multicast_with_port()),
P::IPV6_DNS_SERVER => Some(address_array(&config.dns_servers, array_index)),
_ => None,
},
}
}
}
fn address_array<const N: usize>(
addresses: &[[u8; N]],
array_index: Option<u32>,
) -> Result<PropertyValue, Error> {
common::read_array(
addresses
.iter()
.map(|address| PropertyValue::OctetString(address.to_vec()))
.collect(),
array_index,
)
}