use super::*;
use PropertyIdentifier as P;
use bacnet_encoding::constructed::encode_object_property_reference;
use bacnet_objects::binary::BinaryValueObject;
use bacnet_types::constructed::BACnetObjectPropertyReference;
use bacnet_types::enums::EngineeringUnits;
use bacnet_types::error::RemoteError;
fn ai() -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap()
}
fn switch(instance: u32) -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::BINARY_VALUE, instance).unwrap()
}
fn application(value: &PropertyValue) -> Vec<u8> {
let mut bytes = BytesMut::new();
encode_property_value(&mut bytes, value).unwrap();
bytes.to_vec()
}
fn reference_to(target: ObjectIdentifier) -> Vec<u8> {
let mut bytes = BytesMut::new();
encode_object_property_reference(
&mut bytes,
&BACnetObjectPropertyReference::new(target, P::PRESENT_VALUE.to_raw()),
);
bytes.to_vec()
}
fn wp(
db: &mut ObjectDatabase,
oid: ObjectIdentifier,
property: P,
index: Option<u32>,
value: Vec<u8>,
) -> Result<(), Error> {
let mut request = BytesMut::new();
WritePropertyRequest {
object_identifier: oid,
property_identifier: property,
property_array_index: index,
property_value: value,
priority: None,
}
.encode(&mut request)
.unwrap();
handle_write_property(db, &request).map(|_| ())
}
fn read(
db: &ObjectDatabase,
oid: ObjectIdentifier,
property: P,
index: Option<u32>,
) -> PropertyValue {
db.get(&oid)
.unwrap()
.read_property(property, index)
.unwrap()
}
fn refused(result: Result<(), Error>, expected: ErrorCode) {
assert!(
matches!(result, Err(Error::Remote(RemoteError::Error { class, code }))
if class == ErrorClass::PROPERTY && code == expected),
"expected {expected:?}, got {result:?}"
);
}
fn db() -> ObjectDatabase {
let mut db = ObjectDatabase::new();
db.add(Box::new(
AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap(),
))
.unwrap();
let mut active = BinaryValueObject::new(1, "BV-1").unwrap();
active
.write_property_from(
P::PRESENT_VALUE,
None,
PropertyValue::Enumerated(1),
Some(8),
&crate::command_source::test_origin(),
)
.unwrap();
db.add(Box::new(active)).unwrap();
db
}
#[test]
fn a_reference_written_over_the_wire_makes_the_inhibit_follow_it() {
let mut db = db();
wp(
&mut db,
ai(),
P::EVENT_ALGORITHM_INHIBIT_REF,
None,
reference_to(switch(1)),
)
.unwrap();
assert_eq!(
read(&db, ai(), P::EVENT_ALGORITHM_INHIBIT_REF, None),
PropertyValue::ApplicationData(reference_to(switch(1)))
);
assert!(db.follow_event_algorithm_inhibit(&ai()));
assert_eq!(
read(&db, ai(), P::EVENT_ALGORITHM_INHIBIT, None),
PropertyValue::Boolean(true)
);
let off = application(&PropertyValue::Boolean(false));
refused(
wp(&mut db, ai(), P::EVENT_ALGORITHM_INHIBIT, None, off.clone()),
ErrorCode::WRITE_ACCESS_DENIED,
);
wp(
&mut db,
ai(),
P::EVENT_ALGORITHM_INHIBIT_REF,
None,
reference_to(switch(4_194_303)),
)
.unwrap();
assert_eq!(
read(&db, ai(), P::EVENT_ALGORITHM_INHIBIT, None),
PropertyValue::Boolean(false)
);
wp(&mut db, ai(), P::EVENT_ALGORITHM_INHIBIT, None, off).unwrap();
}
#[test]
fn message_texts_config_is_written_by_index_and_no_size_is_written() {
let mut db = db();
let text = |text: &str| PropertyValue::CharacterString(text.into());
wp(
&mut db,
ai(),
P::EVENT_MESSAGE_TEXTS_CONFIG,
Some(2),
application(&text("Sensor broken")),
)
.unwrap();
assert_eq!(
read(&db, ai(), P::EVENT_MESSAGE_TEXTS_CONFIG, None),
PropertyValue::List(vec![text(""), text("Sensor broken"), text("")])
);
refused(
wp(
&mut db,
ai(),
P::EVENT_MESSAGE_TEXTS_CONFIG,
Some(4),
application(&text("x")),
),
ErrorCode::INVALID_ARRAY_INDEX,
);
let size = application(&PropertyValue::Unsigned(2));
refused(
wp(&mut db, ai(), P::EVENT_MESSAGE_TEXTS_CONFIG, Some(0), size),
ErrorCode::WRITE_ACCESS_DENIED,
);
assert_eq!(
read(&db, ai(), P::EVENT_MESSAGE_TEXTS_CONFIG, Some(0)),
PropertyValue::Unsigned(3)
);
}