#[cfg(feature = "std")]
use std::{
collections::HashMap,
sync::{Arc, RwLock},
time::Instant,
};
#[cfg(not(feature = "std"))]
use alloc::{
collections::BTreeMap as HashMap,
sync::Arc,
vec::Vec,
string::String,
boxed::Box,
};
use super::{
BacnetObject, ObjectIdentifier, ObjectType, PropertyIdentifier, PropertyValue,
ObjectError, Result, Device,
};
#[cfg(feature = "std")]
pub struct ObjectDatabase {
objects: Arc<RwLock<HashMap<ObjectIdentifier, Box<dyn BacnetObject>>>>,
type_index: Arc<RwLock<HashMap<ObjectType, Vec<ObjectIdentifier>>>>,
name_index: Arc<RwLock<HashMap<String, ObjectIdentifier>>>,
revision: Arc<RwLock<u32>>,
last_modified: Arc<RwLock<Instant>>,
device_id: ObjectIdentifier,
}
#[cfg(feature = "std")]
impl ObjectDatabase {
pub fn new(device: Device) -> Self {
let device_id = device.identifier();
let mut objects = HashMap::new();
let mut type_index = HashMap::new();
let mut name_index = HashMap::new();
type_index.entry(ObjectType::Device)
.or_insert_with(Vec::new)
.push(device_id);
name_index.insert(device.object_name.clone(), device_id);
objects.insert(device_id, Box::new(device) as Box<dyn BacnetObject>);
Self {
objects: Arc::new(RwLock::new(objects)),
type_index: Arc::new(RwLock::new(type_index)),
name_index: Arc::new(RwLock::new(name_index)),
revision: Arc::new(RwLock::new(1)),
last_modified: Arc::new(RwLock::new(Instant::now())),
device_id,
}
}
pub fn add_object(&self, object: Box<dyn BacnetObject>) -> Result<()> {
let identifier = object.identifier();
{
let objects = self.objects.read().unwrap();
if objects.contains_key(&identifier) {
return Err(ObjectError::InvalidConfiguration(
format!("Object {} already exists", identifier.instance)
));
}
}
let object_name = match object.get_property(PropertyIdentifier::ObjectName)? {
PropertyValue::CharacterString(name) => name,
_ => return Err(ObjectError::InvalidPropertyType),
};
{
let mut objects = self.objects.write().unwrap();
let mut type_index = self.type_index.write().unwrap();
let mut name_index = self.name_index.write().unwrap();
type_index.entry(identifier.object_type)
.or_insert_with(Vec::new)
.push(identifier);
name_index.insert(object_name, identifier);
objects.insert(identifier, object);
self.increment_revision();
}
Ok(())
}
pub fn remove_object(&self, identifier: ObjectIdentifier) -> Result<()> {
if identifier == self.device_id {
return Err(ObjectError::WriteAccessDenied);
}
let object_name = {
let objects = self.objects.read().unwrap();
match objects.get(&identifier) {
Some(obj) => match obj.get_property(PropertyIdentifier::ObjectName)? {
PropertyValue::CharacterString(name) => name,
_ => return Err(ObjectError::InvalidPropertyType),
},
None => return Err(ObjectError::NotFound),
}
};
{
let mut objects = self.objects.write().unwrap();
let mut type_index = self.type_index.write().unwrap();
let mut name_index = self.name_index.write().unwrap();
objects.remove(&identifier);
if let Some(type_list) = type_index.get_mut(&identifier.object_type) {
type_list.retain(|&id| id != identifier);
}
name_index.remove(&object_name);
self.increment_revision();
}
Ok(())
}
pub fn get_property(&self, identifier: ObjectIdentifier, property: PropertyIdentifier) -> Result<PropertyValue> {
let objects = self.objects.read().unwrap();
match objects.get(&identifier) {
Some(obj) => obj.get_property(property),
None => Err(ObjectError::NotFound),
}
}
pub fn set_property(&self, identifier: ObjectIdentifier, property: PropertyIdentifier, value: PropertyValue) -> Result<()> {
let mut objects = self.objects.write().unwrap();
match objects.get_mut(&identifier) {
Some(obj) => {
let result = obj.set_property(property, value);
if result.is_ok() {
self.increment_revision();
}
result
}
None => Err(ObjectError::NotFound),
}
}
pub fn get_object_by_name(&self, name: &str) -> Result<ObjectIdentifier> {
let name_index = self.name_index.read().unwrap();
match name_index.get(name) {
Some(&identifier) => Ok(identifier),
None => Err(ObjectError::NotFound),
}
}
pub fn get_objects_by_type(&self, object_type: ObjectType) -> Vec<ObjectIdentifier> {
let type_index = self.type_index.read().unwrap();
type_index.get(&object_type)
.cloned()
.unwrap_or_default()
}
pub fn get_all_objects(&self) -> Vec<ObjectIdentifier> {
let objects = self.objects.read().unwrap();
objects.keys().cloned().collect()
}
pub fn object_count(&self) -> usize {
let objects = self.objects.read().unwrap();
objects.len()
}
pub fn object_count_by_type(&self, object_type: ObjectType) -> usize {
let type_index = self.type_index.read().unwrap();
type_index.get(&object_type)
.map(|list| list.len())
.unwrap_or(0)
}
pub fn get_device_id(&self) -> ObjectIdentifier {
self.device_id
}
pub fn revision(&self) -> u32 {
*self.revision.read().unwrap()
}
pub fn last_modified(&self) -> Instant {
*self.last_modified.read().unwrap()
}
pub fn contains(&self, identifier: ObjectIdentifier) -> bool {
let objects = self.objects.read().unwrap();
objects.contains_key(&identifier)
}
pub fn contains_name(&self, name: &str) -> bool {
let name_index = self.name_index.read().unwrap();
name_index.contains_key(name)
}
pub fn next_instance(&self, object_type: ObjectType) -> u32 {
let type_index = self.type_index.read().unwrap();
if let Some(objects) = type_index.get(&object_type) {
let max_instance = objects.iter()
.map(|id| id.instance)
.max()
.unwrap_or(0);
max_instance.saturating_add(1)
} else {
0
}
}
pub fn search_by_property(
&self,
property: PropertyIdentifier,
value: &PropertyValue,
) -> Vec<ObjectIdentifier> {
let objects = self.objects.read().unwrap();
let mut results = Vec::new();
for (&id, obj) in objects.iter() {
if let Ok(prop_value) = obj.get_property(property) {
if Self::property_values_equal(&prop_value, value) {
results.push(id);
}
}
}
results
}
fn property_values_equal(a: &PropertyValue, b: &PropertyValue) -> bool {
match (a, b) {
(PropertyValue::Null, PropertyValue::Null) => true,
(PropertyValue::Boolean(a), PropertyValue::Boolean(b)) => a == b,
(PropertyValue::UnsignedInteger(a), PropertyValue::UnsignedInteger(b)) => a == b,
(PropertyValue::SignedInt(a), PropertyValue::SignedInt(b)) => a == b,
(PropertyValue::Real(a), PropertyValue::Real(b)) => (a - b).abs() < f32::EPSILON,
(PropertyValue::Double(a), PropertyValue::Double(b)) => (a - b).abs() < f64::EPSILON,
(PropertyValue::CharacterString(a), PropertyValue::CharacterString(b)) => a == b,
(PropertyValue::Enumerated(a), PropertyValue::Enumerated(b)) => a == b,
(PropertyValue::ObjectIdentifier(a), PropertyValue::ObjectIdentifier(b)) => a == b,
_ => false,
}
}
fn increment_revision(&self) {
let mut revision = self.revision.write().unwrap();
*revision = revision.wrapping_add(1);
let mut last_modified = self.last_modified.write().unwrap();
*last_modified = Instant::now();
}
pub fn statistics(&self) -> DatabaseStatistics {
let objects = self.objects.read().unwrap();
let type_index = self.type_index.read().unwrap();
let mut type_counts = HashMap::new();
for (object_type, identifiers) in type_index.iter() {
type_counts.insert(*object_type, identifiers.len());
}
DatabaseStatistics {
total_objects: objects.len(),
object_types: type_index.len(),
type_counts,
revision: self.revision(),
last_modified: self.last_modified(),
}
}
}
#[cfg(feature = "std")]
#[derive(Debug, Clone)]
pub struct DatabaseStatistics {
pub total_objects: usize,
pub object_types: usize,
pub type_counts: HashMap<ObjectType, usize>,
pub revision: u32,
pub last_modified: Instant,
}
#[cfg(feature = "std")]
pub struct DatabaseBuilder {
device: Option<Device>,
objects: Vec<Box<dyn BacnetObject>>,
}
#[cfg(feature = "std")]
impl DatabaseBuilder {
pub fn new() -> Self {
Self {
device: None,
objects: Vec::new(),
}
}
pub fn with_device(mut self, device: Device) -> Self {
self.device = Some(device);
self
}
pub fn add_object(mut self, object: Box<dyn BacnetObject>) -> Self {
self.objects.push(object);
self
}
pub fn build(self) -> Result<ObjectDatabase> {
let device = self.device.ok_or_else(|| {
ObjectError::InvalidConfiguration("Device object is required".to_string())
})?;
let database = ObjectDatabase::new(device);
for object in self.objects {
database.add_object(object)?;
}
Ok(database)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::object::{
analog::{AnalogInput, AnalogValue},
binary::BinaryInput,
};
#[test]
fn test_database_creation() {
let device = Device::new(1234, "Test Device".to_string());
let db = ObjectDatabase::new(device);
assert_eq!(db.object_count(), 1);
assert_eq!(db.revision(), 1);
assert!(db.contains(ObjectIdentifier::new(ObjectType::Device, 1234)));
}
#[test]
fn test_add_remove_objects() {
let device = Device::new(1234, "Test Device".to_string());
let db = ObjectDatabase::new(device);
let ai = AnalogInput::new(1, "Temperature".to_string());
db.add_object(Box::new(ai)).unwrap();
assert_eq!(db.object_count(), 2);
assert_eq!(db.object_count_by_type(ObjectType::AnalogInput), 1);
let bi = BinaryInput::new(1, "Door Sensor".to_string());
db.add_object(Box::new(bi)).unwrap();
assert_eq!(db.object_count(), 3);
let ai_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
db.remove_object(ai_id).unwrap();
assert_eq!(db.object_count(), 2);
assert_eq!(db.object_count_by_type(ObjectType::AnalogInput), 0);
}
#[test]
fn test_object_lookup() {
let device = Device::new(1234, "Test Device".to_string());
let db = ObjectDatabase::new(device);
let av = AnalogValue::new(100, "Setpoint".to_string());
db.add_object(Box::new(av)).unwrap();
let av_id = ObjectIdentifier::new(ObjectType::AnalogValue, 100);
assert!(db.contains(av_id));
let found_id = db.get_object_by_name("Setpoint").unwrap();
assert_eq!(found_id, av_id);
let objects = db.get_objects_by_type(ObjectType::AnalogValue);
assert_eq!(objects.len(), 1);
assert_eq!(objects[0], av_id);
}
#[test]
fn test_property_search() {
let device = Device::new(1234, "Test Device".to_string());
let db = ObjectDatabase::new(device);
for i in 0..5 {
let mut av = AnalogValue::new(i, format!("AV{}", i));
av.present_value = 20.0 + i as f32;
db.add_object(Box::new(av)).unwrap();
}
let results = db.search_by_property(
PropertyIdentifier::PresentValue,
&PropertyValue::Real(22.0),
);
assert_eq!(results.len(), 1);
assert_eq!(results[0].instance, 2);
}
#[test]
fn test_database_builder() {
let db = DatabaseBuilder::new()
.with_device(Device::new(5000, "Built Device".to_string()))
.add_object(Box::new(AnalogInput::new(1, "AI1".to_string())))
.add_object(Box::new(AnalogInput::new(2, "AI2".to_string())))
.add_object(Box::new(BinaryInput::new(1, "BI1".to_string())))
.build()
.unwrap();
assert_eq!(db.object_count(), 4); assert_eq!(db.object_count_by_type(ObjectType::AnalogInput), 2);
assert_eq!(db.object_count_by_type(ObjectType::BinaryInput), 1);
}
#[test]
fn test_next_instance() {
let device = Device::new(1234, "Test Device".to_string());
let db = ObjectDatabase::new(device);
assert_eq!(db.next_instance(ObjectType::AnalogInput), 0);
db.add_object(Box::new(AnalogInput::new(5, "AI5".to_string()))).unwrap();
db.add_object(Box::new(AnalogInput::new(10, "AI10".to_string()))).unwrap();
db.add_object(Box::new(AnalogInput::new(3, "AI3".to_string()))).unwrap();
assert_eq!(db.next_instance(ObjectType::AnalogInput), 11);
}
}