use super::cov_wire_test_support::*;
use super::*;
use crate::cov::CovSubscriptionTable;
use bacnet_objects::device::{DeviceConfig, DeviceObject};
use bacnet_objects::multistate::MultiStateValueObject;
use bacnet_objects::schedule::ScheduleObject;
use bacnet_services::object_mgmt::{CreateObjectRequest, ObjectSpecifier};
use bacnet_types::constructed::BACnetObjectPropertyReference;
use bacnet_types::enums::{ObjectType, Reliability};
fn sch1() -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::SCHEDULE, 1).unwrap()
}
fn msv(instance: u32) -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::MULTI_STATE_VALUE, instance).unwrap()
}
fn multi_state_value(instance: u32) -> Box<MultiStateValueObject> {
Box::new(MultiStateValueObject::new(instance, format!("MSV-{instance}"), 3).unwrap())
}
fn with_schedule(db: &mut ObjectDatabase) {
db.add(multi_state_value(1)).unwrap();
let mut schedule = ScheduleObject::new(1, "SCH-1", PropertyValue::Unsigned(2)).unwrap();
schedule
.set_object_property_references(
[msv(1), msv(9)]
.map(|object| BACnetObjectPropertyReference::new(object, PV.to_raw()))
.to_vec(),
)
.unwrap();
db.add(Box::new(schedule)).unwrap();
}
async fn read_from(
db: &RwLock<ObjectDatabase>,
object: ObjectIdentifier,
property: PropertyIdentifier,
) -> PropertyValue {
db.read()
.await
.get(&object)
.unwrap()
.read_property(property, None)
.unwrap()
}
async fn schedule_reliability(db: &RwLock<ObjectDatabase>) -> Reliability {
match read_from(db, sch1(), PropertyIdentifier::RELIABILITY).await {
PropertyValue::Enumerated(raw) => Reliability::from_raw(raw),
other => panic!("Reliability read {other:?}"),
}
}
async fn settle() {
tokio::time::sleep(Duration::from_millis(10)).await;
}
#[tokio::test(start_paused = true)]
async fn the_retry_writes_the_created_object_and_owes_cov_for_it() {
let db = {
let mut db = crate::server::clocked_test_database();
db.add(Box::new(
DeviceObject::new(DeviceConfig::default()).unwrap(),
))
.unwrap();
with_schedule(&mut db);
Arc::new(RwLock::new(db))
};
crate::schedule::tick_schedules(&db).await;
assert_eq!(
schedule_reliability(&db).await,
Reliability::CONFIGURATION_ERROR
);
let cov_table = RwLock::new(CovSubscriptionTable::new());
let committed = {
let mut db_w = db.write().await;
db_w.add(multi_state_value(9)).unwrap();
crate::membership::settle(&db, &mut db_w, &cov_table).await
};
assert_eq!(read_from(&db, msv(9), PV).await, PropertyValue::Unsigned(2));
assert_eq!(
schedule_reliability(&db).await,
Reliability::NO_FAULT_DETECTED
);
assert_eq!(committed.coarse, [msv(9), sch1()]);
assert!(db.write().await.take_membership_work_internal().is_empty());
}
#[tokio::test(start_paused = true)]
async fn create_object_retries_the_refusal_before_its_answer() {
let mut h = Harness::start_with(ServerConfig::default(), with_schedule).await;
settle().await;
let db = Arc::clone(h.server.database());
assert_eq!(
schedule_reliability(&db).await,
Reliability::CONFIGURATION_ERROR
);
db.write().await.set_membership_waker_internal(None);
let mut body = BytesMut::new();
CreateObjectRequest {
object_specifier: ObjectSpecifier::Identifier(msv(9)),
list_of_initial_values: Vec::new(),
}
.encode(&mut body);
h.request(ConfirmedServiceChoice::CREATE_OBJECT, body).await;
settle().await;
assert_eq!(read_from(&db, msv(9), PV).await, PropertyValue::Unsigned(2));
assert_eq!(
schedule_reliability(&db).await,
Reliability::NO_FAULT_DETECTED
);
}
#[tokio::test(start_paused = true)]
async fn an_applications_own_add_is_retried_by_the_server_task() {
let h = Harness::start_with(ServerConfig::default(), with_schedule).await;
settle().await;
let db = Arc::clone(h.server.database());
assert_eq!(
schedule_reliability(&db).await,
Reliability::CONFIGURATION_ERROR
);
db.write().await.add(multi_state_value(9)).unwrap();
settle().await;
assert_eq!(read_from(&db, msv(9), PV).await, PropertyValue::Unsigned(2));
assert_eq!(
schedule_reliability(&db).await,
Reliability::NO_FAULT_DETECTED
);
}