use super::channel_wire_tests::{
ch, channel, member, settled, slot, start, write_channel, write_status,
};
use super::command_action_wire_tests::ao;
use super::cov_wire_test_support::*;
use super::*;
use bacnet_objects::traits::BACnetObject;
use bacnet_types::enums::WriteStatus;
use std::borrow::Cow;
use std::sync::Mutex as StdMutex;
type Log = Arc<StdMutex<Vec<(PropertyIdentifier, PropertyValue)>>>;
struct Recorder {
oid: ObjectIdentifier,
log: Log,
}
fn rec() -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::from_raw(600), 1).unwrap()
}
impl BACnetObject for Recorder {
fn object_identifier(&self) -> ObjectIdentifier {
self.oid
}
fn object_name(&self) -> &str {
"REC-1"
}
fn read_property(
&self,
property: PropertyIdentifier,
_array_index: Option<u32>,
) -> Result<PropertyValue, Error> {
Ok(match property {
PropertyIdentifier::OBJECT_IDENTIFIER => PropertyValue::ObjectIdentifier(self.oid),
PropertyIdentifier::OBJECT_NAME => PropertyValue::CharacterString("REC-1".into()),
PropertyIdentifier::OBJECT_TYPE => {
PropertyValue::Enumerated(self.oid.object_type().to_raw())
}
_ => PropertyValue::Real(0.0),
})
}
fn write_property(
&mut self,
property: PropertyIdentifier,
_array_index: Option<u32>,
value: PropertyValue,
_priority: Option<u8>,
) -> Result<(), Error> {
self.log.lock().unwrap().push((property, value));
Ok(())
}
fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
Cow::Borrowed(&[])
}
}
async fn start_recording(members: Vec<(PropertyIdentifier, u32)>) -> (Harness, Log) {
let log = Log::default();
let recorder = Recorder {
oid: rec(),
log: Arc::clone(&log),
};
let h = Harness::start_with(ServerConfig::default(), move |db| {
db.add(Box::new(recorder)).unwrap();
db.add(Box::new(channel(
8,
1,
members
.into_iter()
.map(|(property, delay)| (member(rec(), property), delay))
.collect(),
)))
.unwrap();
})
.await;
(h, log)
}
#[tokio::test(start_paused = true)]
async fn channel_deleted_during_a_delay_leaves_the_rest_of_its_members_unwritten() {
let property = PropertyIdentifier::from_raw;
let (mut h, log) = start_recording(vec![(property(1000), 0), (property(1001), 100)]).await;
write_channel(&mut h, 8, &PropertyValue::Real(30.0), Some(10))
.await
.unwrap();
h.settle().await;
assert_eq!(log.lock().unwrap().len(), 1);
h.server
.database()
.write()
.await
.remove(&ch(8))
.unwrap()
.unwrap();
tokio::time::sleep(Duration::from_millis(200)).await;
let log = log.lock().unwrap();
assert_eq!(log.len(), 1, "{log:?}");
assert_eq!(log[0].0, property(1000));
}
#[tokio::test(start_paused = true)]
async fn channel_replaced_during_a_delay_leaves_the_new_object_to_its_own_run() {
let mut h = start().await;
write_channel(&mut h, 3, &PropertyValue::Real(30.0), Some(10))
.await
.unwrap();
h.settle().await;
h.server
.database()
.write()
.await
.add(Box::new(channel(3, 12, vec![(member(ao(1), PV), 1000)])))
.unwrap();
write_channel(&mut h, 3, &PropertyValue::Real(5.0), Some(10))
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(300)).await;
assert_eq!(slot(&h, ao(2), 10).await, PropertyValue::Null);
assert_eq!(write_status(&mut h, 3).await, WriteStatus::IN_PROGRESS);
tokio::time::sleep(Duration::from_millis(750)).await;
assert_eq!(settled(&mut h, 3).await, WriteStatus::SUCCESSFUL);
assert_eq!(slot(&h, ao(1), 10).await, PropertyValue::Real(5.0));
}
#[tokio::test(start_paused = true)]
async fn channel_lighting_command_reaches_its_member_without_the_context_0_framing() {
let (mut h, log) = start_recording(vec![(PropertyIdentifier::LIGHTING_COMMAND, 0)]).await;
let framed = [0x0E, 0x09, 0x01, 0x1C, 0x42, 0x48, 0x00, 0x00, 0x0F];
write_channel(
&mut h,
8,
&PropertyValue::ApplicationData(framed.to_vec()),
None,
)
.await
.unwrap();
assert_eq!(settled(&mut h, 8).await, WriteStatus::SUCCESSFUL);
let log = log.lock().unwrap();
assert_eq!(log.len(), 1);
let (property, value) = &log[0];
assert_eq!(*property, PropertyIdentifier::LIGHTING_COMMAND);
assert_eq!(
*value,
PropertyValue::ApplicationData(framed[1..8].to_vec())
);
}
#[tokio::test(start_paused = true)]
async fn channel_members_with_equal_delays_go_in_list_order() {
let property = PropertyIdentifier::from_raw;
let (mut h, log) = start_recording(vec![
(property(1000), 50),
(property(1001), 0),
(property(1002), 50),
(property(1003), 0),
])
.await;
write_channel(&mut h, 8, &PropertyValue::Real(2.5), Some(9))
.await
.unwrap();
tokio::time::sleep(Duration::from_millis(60)).await;
assert_eq!(settled(&mut h, 8).await, WriteStatus::SUCCESSFUL);
let order: Vec<_> = log
.lock()
.unwrap()
.iter()
.map(|(property, value)| {
assert_eq!(*value, PropertyValue::Real(2.5));
property.to_raw()
})
.collect();
assert_eq!(order, [1001, 1003, 1000, 1002]);
}