#[cfg(not(feature = "std"))]
use alloc::{string::String, vec::Vec};
use crate::object::{ObjectIdentifier, ObjectType, PropertyIdentifier, PropertyValue, Result};
#[derive(Clone)]
pub struct ObjectFunctions {
pub object_type: ObjectType,
pub count: fn() -> usize,
pub index_to_instance: fn(usize) -> Option<u32>,
pub valid_instance: fn(u32) -> bool,
pub object_name: fn(u32) -> Option<String>,
pub read_property: fn(u32, PropertyIdentifier) -> Result<PropertyValue>,
pub write_property: fn(u32, PropertyIdentifier, PropertyValue) -> Result<()>,
pub is_property_writable: fn(u32, PropertyIdentifier) -> bool,
pub property_list: fn(u32) -> Vec<PropertyIdentifier>,
}
impl core::fmt::Debug for ObjectFunctions {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ObjectFunctions")
.field("object_type", &self.object_type)
.field("count", &"fn()")
.field("index_to_instance", &"fn(usize) -> Option<u32>")
.field("valid_instance", &"fn(u32) -> bool")
.field("object_name", &"fn(u32) -> Option<String>")
.field(
"read_property",
&"fn(u32, PropertyIdentifier) -> Result<PropertyValue>",
)
.field(
"write_property",
&"fn(u32, PropertyIdentifier, PropertyValue) -> Result<()>",
)
.field(
"is_property_writable",
&"fn(u32, PropertyIdentifier) -> bool",
)
.field("property_list", &"fn(u32) -> Vec<PropertyIdentifier>")
.finish()
}
}
#[derive(Debug, Clone)]
pub struct DeviceObject {
object_table: Vec<ObjectFunctions>,
device_instance: u32,
device_identifier: String,
device_name: String,
device_description: String,
vendor_identifier: u16,
vendor_name: String,
model_name: String,
firmware_revision: String,
application_software_version: String,
protocol_version: u8,
protocol_revision: u8,
}
impl DeviceObject {
pub fn new(device_instance: u32, device_name: String) -> Self {
Self {
object_table: Vec::new(),
device_instance,
device_identifier: format!("Device-{}", device_instance),
device_name,
device_description: String::new(),
vendor_identifier: 0,
vendor_name: String::from("Unknown"),
model_name: String::from("BACnet-RS Device"),
firmware_revision: String::from("1.0"),
application_software_version: String::from("1.0"),
protocol_version: 1,
protocol_revision: 30, }
}
pub fn find_object_functions(&self, object_type: ObjectType) -> Option<&ObjectFunctions> {
self.object_table
.iter()
.find(|f| f.object_type == object_type)
}
pub fn object_functions(&self) -> &[ObjectFunctions] {
&self.object_table
}
pub fn register_object_functions(&mut self, functions: ObjectFunctions) {
self.object_table
.retain(|f| f.object_type != functions.object_type);
self.object_table.push(functions);
}
pub fn device_instance(&self) -> u32 {
self.device_instance
}
pub fn device_name(&self) -> &str {
&self.device_name
}
pub fn device_identifier(&self) -> &str {
&self.device_identifier
}
pub fn application_software_version(&self) -> &str {
&self.application_software_version
}
pub fn protocol_version(&self) -> u8 {
self.protocol_version
}
pub fn protocol_revision(&self) -> u8 {
self.protocol_revision
}
pub fn set_device_description(&mut self, description: String) {
self.device_description = description;
}
pub fn set_vendor_info(&mut self, vendor_id: u16, vendor_name: String) {
self.vendor_identifier = vendor_id;
self.vendor_name = vendor_name;
}
pub fn set_model_info(&mut self, model_name: String, firmware_revision: String) {
self.model_name = model_name;
self.firmware_revision = firmware_revision;
}
pub fn read_object_property(
&self,
object_id: ObjectIdentifier,
property: PropertyIdentifier,
) -> Result<PropertyValue> {
if let Some(funcs) = self.find_object_functions(object_id.object_type) {
if !(funcs.valid_instance)(object_id.instance) {
return Err(crate::object::ObjectError::InstanceNotFound);
}
(funcs.read_property)(object_id.instance, property)
} else {
Err(crate::object::ObjectError::TypeNotSupported)
}
}
pub fn write_object_property(
&self,
object_id: ObjectIdentifier,
property: PropertyIdentifier,
value: PropertyValue,
) -> Result<()> {
if let Some(funcs) = self.find_object_functions(object_id.object_type) {
if !(funcs.valid_instance)(object_id.instance) {
return Err(crate::object::ObjectError::InstanceNotFound);
}
if !(funcs.is_property_writable)(object_id.instance, property) {
return Err(crate::object::ObjectError::PropertyNotWritable);
}
(funcs.write_property)(object_id.instance, property, value)
} else {
Err(crate::object::ObjectError::TypeNotSupported)
}
}
pub fn total_object_count(&self) -> usize {
self.object_table.iter().map(|funcs| (funcs.count)()).sum()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn mock_count() -> usize {
2
}
fn mock_index_to_instance(index: usize) -> Option<u32> {
match index {
0 => Some(1),
1 => Some(2),
_ => None,
}
}
fn mock_valid_instance(instance: u32) -> bool {
instance == 1 || instance == 2
}
fn mock_object_name(instance: u32) -> Option<String> {
if mock_valid_instance(instance) {
Some(format!("Test Object {}", instance))
} else {
None
}
}
fn mock_read_property(_instance: u32, property: PropertyIdentifier) -> Result<PropertyValue> {
match property {
PropertyIdentifier::PresentValue => Ok(PropertyValue::Real(42.0)),
_ => Err(crate::object::ObjectError::UnknownProperty),
}
}
fn mock_write_property(
_instance: u32,
_property: PropertyIdentifier,
_value: PropertyValue,
) -> Result<()> {
Ok(())
}
fn mock_is_writable(_instance: u32, property: PropertyIdentifier) -> bool {
property == PropertyIdentifier::PresentValue
}
fn mock_property_list(_instance: u32) -> Vec<PropertyIdentifier> {
vec![PropertyIdentifier::PresentValue]
}
#[test]
fn test_device_object_creation() {
let device = DeviceObject::new(123, "Test Device".to_string());
assert_eq!(device.device_instance(), 123);
assert_eq!(device.device_name(), "Test Device");
assert_eq!(device.total_object_count(), 0);
}
#[test]
fn test_register_object_functions() {
let mut device = DeviceObject::new(123, "Test Device".to_string());
let functions = ObjectFunctions {
object_type: ObjectType::AnalogInput,
count: mock_count,
index_to_instance: mock_index_to_instance,
valid_instance: mock_valid_instance,
object_name: mock_object_name,
read_property: mock_read_property,
write_property: mock_write_property,
is_property_writable: mock_is_writable,
property_list: mock_property_list,
};
device.register_object_functions(functions);
assert_eq!(device.total_object_count(), 2);
assert!(device
.find_object_functions(ObjectType::AnalogInput)
.is_some());
}
#[test]
fn test_find_object_functions() {
let mut device = DeviceObject::new(123, "Test Device".to_string());
let functions = ObjectFunctions {
object_type: ObjectType::AnalogInput,
count: mock_count,
index_to_instance: mock_index_to_instance,
valid_instance: mock_valid_instance,
object_name: mock_object_name,
read_property: mock_read_property,
write_property: mock_write_property,
is_property_writable: mock_is_writable,
property_list: mock_property_list,
};
device.register_object_functions(functions);
assert!(device
.find_object_functions(ObjectType::AnalogInput)
.is_some());
assert!(device
.find_object_functions(ObjectType::AnalogOutput)
.is_none());
}
#[test]
fn test_read_object_property() {
let mut device = DeviceObject::new(123, "Test Device".to_string());
let functions = ObjectFunctions {
object_type: ObjectType::AnalogInput,
count: mock_count,
index_to_instance: mock_index_to_instance,
valid_instance: mock_valid_instance,
object_name: mock_object_name,
read_property: mock_read_property,
write_property: mock_write_property,
is_property_writable: mock_is_writable,
property_list: mock_property_list,
};
device.register_object_functions(functions);
let object_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
let result = device.read_object_property(object_id, PropertyIdentifier::PresentValue);
assert!(result.is_ok());
if let Ok(PropertyValue::Real(val)) = result {
assert_eq!(val, 42.0);
} else {
panic!("Expected Real property value");
}
}
}