use bacnet_objects::analog::{AnalogInputObject, AnalogOutputObject, AnalogValueObject};
use bacnet_objects::binary::{BinaryInputObject, BinaryOutputObject, BinaryValueObject};
use bacnet_objects::command_source::CommandOrigin;
use bacnet_objects::database::ObjectDatabase;
use bacnet_objects::device::{DeviceConfig, DeviceObject};
use bacnet_objects::multistate::MultiStateValueObject;
use bacnet_objects::notification_class::NotificationClass;
use bacnet_objects::traits::BACnetObject;
use bacnet_objects::trend::TrendLogObject;
use bacnet_objects::DeviceIdentity;
use bacnet_types::enums::{EngineeringUnits, ObjectType, PropertyIdentifier, Reliability};
use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
pub const LAB_VENDOR: u16 = 260;
pub fn lab_identity(instance: u32) -> DeviceIdentity {
DeviceIdentity::new(instance, LAB_VENDOR)
.expect("valid Device identity")
.with_name(format!("Lab device {instance}"))
}
fn database(instance: u32, objects: Vec<Box<dyn BACnetObject>>) -> ObjectDatabase {
let mut device = DeviceObject::new(DeviceConfig {
identity: lab_identity(instance),
vendor_name: "Rusty Lab".into(),
model_name: "Bench-1".into(),
firmware_revision: "1.2.3".into(),
application_software_version: "4.5".into(),
..Default::default()
})
.expect("lab Device object");
let mut list = vec![device.object_identifier()];
list.extend(objects.iter().map(|object| object.object_identifier()));
device.set_object_list(list);
let mut db = ObjectDatabase::new();
db.add(Box::new(device)).unwrap();
for object in objects {
db.add(object).unwrap();
}
db
}
fn set(object: &mut dyn BACnetObject, property: PropertyIdentifier, value: PropertyValue) {
object
.write_property(property, None, value, None)
.unwrap_or_else(|error| panic!("lab {property}: {error}"));
}
pub fn lab_database(instance: u32) -> ObjectDatabase {
let mut ai1 =
AnalogInputObject::new(1, "Zone Temp", EngineeringUnits::DEGREES_FAHRENHEIT).unwrap();
ai1.set_present_value(72.5);
set(
&mut ai1,
PropertyIdentifier::OUT_OF_SERVICE,
PropertyValue::Boolean(true),
);
set(
&mut ai1,
PropertyIdentifier::RELIABILITY,
PropertyValue::Enumerated(Reliability::OPEN_LOOP.to_raw()),
);
let mut ai2 =
AnalogInputObject::new(2, "Outside Air", EngineeringUnits::DEGREES_FAHRENHEIT).unwrap();
ai2.set_present_value(51.0);
let mut ao1 = AnalogOutputObject::new(1, "Damper", EngineeringUnits::PERCENT).unwrap();
let operator = CommandOrigin::Local {
owner_device: ObjectIdentifier::new(ObjectType::DEVICE, instance).unwrap(),
initiating_object: None,
};
ao1.write_property_from(
PropertyIdentifier::PRESENT_VALUE,
None,
PropertyValue::Real(55.0),
Some(10),
&operator,
)
.expect("command AO:1 at priority 10");
database(
instance,
vec![
Box::new(ai1),
Box::new(ai2),
Box::new(ao1),
Box::new(
AnalogValueObject::new(1, "Setpoint", EngineeringUnits::DEGREES_FAHRENHEIT)
.unwrap(),
),
Box::new(BinaryInputObject::new(1, "Fan Status").unwrap()),
Box::new(BinaryOutputObject::new(1, "Fan Command").unwrap()),
Box::new(BinaryValueObject::new(1, "Occupied").unwrap()),
Box::new(MultiStateValueObject::new(1, "Mode", 3).unwrap()),
Box::new(NotificationClass::new(1, "Alarms").unwrap()),
Box::new(TrendLogObject::new(1, "Zone Temp Log", 100).unwrap()),
],
)
}
pub fn large_database(instance: u32, count: u32) -> ObjectDatabase {
let points = (1..=count)
.map(|n| {
Box::new(
AnalogValueObject::new(n, format!("Point {n}"), EngineeringUnits::NO_UNITS)
.unwrap(),
) as Box<dyn BACnetObject>
})
.collect();
database(instance, points)
}