use std::sync::Arc;
use bacnet_types::bip_port::IpMulticast;
use bacnet_types::enums::PropertyIdentifier as P;
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::NetworkPortObject;
use crate::common;
#[derive(Clone, Default)]
pub(super) struct Multicast {
view: Option<Arc<dyn IpMulticast>>,
pending: Option<[u8; 4]>,
}
impl Multicast {
pub(super) fn lent(view: Option<Arc<dyn IpMulticast>>) -> Self {
Self {
view,
pending: None,
}
}
pub(super) fn is_pending(&self) -> bool {
self.pending.is_some()
}
pub(super) fn clear_pending(&mut self) {
self.pending = None;
}
}
impl NetworkPortObject {
pub(super) fn serves_multicast(&self) -> bool {
self.multicast.view.is_some()
&& self
.ip
.as_ref()
.is_some_and(super::ip_config::IpConfig::is_ipv4)
}
pub(super) fn activates_multicast(&self) -> bool {
self.activation
&& self
.multicast
.view
.as_ref()
.is_some_and(|view| view.can_change())
}
pub(super) fn read_multicast(&self, property: P) -> Option<Result<PropertyValue, Error>> {
if property != P::BACNET_IP_MULTICAST_ADDRESS {
return None;
}
let view = self
.multicast
.view
.as_ref()
.filter(|_| self.serves_multicast());
Some(match view {
Some(view) => {
let group = self.multicast.pending.unwrap_or_else(|| view.group());
Ok(PropertyValue::OctetString(group.to_vec()))
}
None => Err(common::unknown_property_error()),
})
}
pub(super) fn write_multicast(
&mut self,
property: P,
array_index: Option<u32>,
value: &PropertyValue,
) -> Option<Result<(), Error>> {
if property != P::BACNET_IP_MULTICAST_ADDRESS || !self.serves_multicast() {
return None;
}
if !self.activates_multicast() {
return Some(Err(common::write_access_denied_error()));
}
if array_index.is_some() {
return Some(Err(common::property_is_not_an_array_error()));
}
let PropertyValue::OctetString(octets) = value else {
return Some(Err(common::invalid_data_type_error()));
};
let Ok(group) = <[u8; 4]>::try_from(octets.as_slice()) else {
return Some(Err(common::value_out_of_range_error()));
};
let view = self.multicast.view.as_ref()?;
Some(
view.check(group)
.map(|()| self.multicast.pending = Some(group)),
)
}
pub(super) fn check_multicast(&self) -> Result<(), Error> {
match (self.multicast.pending, &self.multicast.view) {
(None, _) => Ok(()),
(Some(group), Some(view)) => view.check(group),
(Some(_), None) => Err(common::write_access_denied_error()),
}
}
pub(super) fn activate_multicast(&mut self) -> Result<(), Error> {
let Some(group) = self.multicast.pending else {
return Ok(());
};
let view = self
.multicast
.view
.as_ref()
.ok_or_else(common::write_access_denied_error)?;
view.activate(group)?;
self.multicast.pending = None;
Ok(())
}
}