use std::any::Any;
use std::sync::Arc;
use super::property::{BACnetValue, PropertyError, PropertyId, StatusFlags};
use super::types::{ObjectId, ObjectType};
pub trait BACnetObject: Send + Sync + 'static {
fn object_identifier(&self) -> ObjectId;
fn object_name(&self) -> &str;
fn object_type(&self) -> ObjectType {
self.object_identifier().object_type
}
fn description(&self) -> Option<&str> {
None
}
fn read_property(&self, property_id: PropertyId) -> Result<BACnetValue, PropertyError>;
fn read_property_at(
&self,
property_id: PropertyId,
array_index: Option<u32>,
) -> Result<BACnetValue, PropertyError> {
let value = self.read_property(property_id)?;
if let Some(index) = array_index {
match value {
BACnetValue::Array(arr) => arr
.get(index as usize)
.cloned()
.ok_or(PropertyError::InvalidArrayIndex(index)),
_ => Err(PropertyError::InvalidArrayIndex(index)),
}
} else {
Ok(value)
}
}
fn write_property(
&self,
property_id: PropertyId,
value: BACnetValue,
) -> Result<(), PropertyError>;
fn write_property_at(
&self,
property_id: PropertyId,
value: BACnetValue,
_array_index: Option<u32>,
_priority: Option<u8>,
) -> Result<(), PropertyError> {
self.write_property(property_id, value)
}
fn list_properties(&self) -> Vec<PropertyId>;
fn has_property(&self, property_id: PropertyId) -> bool {
self.list_properties().contains(&property_id)
}
fn status_flags(&self) -> StatusFlags {
StatusFlags::default()
}
fn is_out_of_service(&self) -> bool {
self.status_flags().out_of_service
}
fn present_value(&self) -> Result<BACnetValue, PropertyError> {
self.read_property(PropertyId::PresentValue)
}
fn as_any(&self) -> &dyn Any;
}
pub trait WritableObject: BACnetObject {
fn set_present_value(&self, value: BACnetValue) -> Result<(), PropertyError>;
fn set_present_value_with_priority(
&self,
value: BACnetValue,
_priority: u8,
) -> Result<(), PropertyError> {
self.set_present_value(value)
}
fn relinquish_default(&self) -> Option<BACnetValue> {
None
}
fn priority_array(&self) -> Option<[Option<BACnetValue>; 16]> {
None
}
fn relinquish(&self, _priority: u8) -> Result<(), PropertyError> {
Ok(())
}
}
pub trait CovSupport: BACnetObject {
fn cov_increment(&self) -> Option<f32> {
None
}
fn set_cov_increment(&self, _increment: f32) -> Result<(), PropertyError> {
Err(PropertyError::WriteAccessDenied(PropertyId::CovIncrement))
}
fn check_cov(&self) -> bool;
fn cov_values(&self) -> Vec<(PropertyId, BACnetValue)> {
let mut values = Vec::new();
if let Ok(pv) = self.present_value() {
values.push((PropertyId::PresentValue, pv));
}
values.push((
PropertyId::StatusFlags,
BACnetValue::BitString(self.status_flags().to_bits()),
));
values
}
fn reset_cov(&self);
}
pub trait IntrinsicReporting: BACnetObject {
fn event_state(&self) -> super::property::EventState {
super::property::EventState::Normal
}
fn check_event_state(&self) -> Option<super::property::EventState> {
None
}
fn high_limit(&self) -> Option<f32> {
None
}
fn low_limit(&self) -> Option<f32> {
None
}
fn deadband(&self) -> Option<f32> {
None
}
}
pub trait ObjectBuilder<T: BACnetObject> {
fn name(self, name: impl Into<String>) -> Self;
fn description(self, desc: impl Into<String>) -> Self;
fn out_of_service(self, oos: bool) -> Self;
fn build(self) -> T;
}
pub type BoxedObject = Box<dyn BACnetObject>;
pub type ArcObject = Arc<dyn BACnetObject>;
#[cfg(test)]
mod tests {
use super::*;
struct MockObject {
id: ObjectId,
name: String,
}
impl BACnetObject for MockObject {
fn object_identifier(&self) -> ObjectId {
self.id
}
fn object_name(&self) -> &str {
&self.name
}
fn read_property(&self, property_id: PropertyId) -> Result<BACnetValue, PropertyError> {
match property_id {
PropertyId::ObjectIdentifier => Ok(BACnetValue::ObjectIdentifier(self.id)),
PropertyId::ObjectName => Ok(BACnetValue::CharacterString(self.name.clone())),
PropertyId::ObjectType => Ok(BACnetValue::Enumerated(self.id.object_type as u32)),
PropertyId::PresentValue => Ok(BACnetValue::Real(0.0)),
_ => Err(PropertyError::NotFound(property_id)),
}
}
fn write_property(
&self,
property_id: PropertyId,
_value: BACnetValue,
) -> Result<(), PropertyError> {
Err(PropertyError::WriteAccessDenied(property_id))
}
fn list_properties(&self) -> Vec<PropertyId> {
vec![
PropertyId::ObjectIdentifier,
PropertyId::ObjectName,
PropertyId::ObjectType,
PropertyId::PresentValue,
]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[test]
fn test_mock_object() {
let obj = MockObject {
id: ObjectId::new(ObjectType::AnalogInput, 1),
name: "Test AI".to_string(),
};
assert_eq!(obj.object_type(), ObjectType::AnalogInput);
assert_eq!(obj.object_name(), "Test AI");
assert!(obj.has_property(PropertyId::PresentValue));
assert!(!obj.has_property(PropertyId::HighLimit));
}
}