use super::*;
use bacnet_services::write_property::WritePropertyRequest;
const ATTEMPTED: PropertyIdentifier = PropertyIdentifier::ATTEMPTED_SAMPLES;
const WINDOW_SAMPLES: PropertyIdentifier = PropertyIdentifier::WINDOW_SAMPLES;
async fn write(
h: &mut Harness,
property: PropertyIdentifier,
value: PropertyValue,
) -> Result<(), ErrorCode> {
let mut body = BytesMut::new();
WritePropertyRequest {
object_identifier: avg1(),
property_identifier: property,
property_array_index: None,
property_value: encoded(value),
priority: None,
}
.encode(&mut body)
.unwrap();
h.request(ConfirmedServiceChoice::WRITE_PROPERTY, body)
.await;
response(h).await
}
async fn missed(h: &Harness) {
h.server
.add_averaging_sample_local(&avg1(), None)
.await
.unwrap();
}
#[tokio::test(start_paused = true)]
async fn averaging_window_write_reports_nan_and_infinities_to_property_subscribers() {
let mut h = start().await;
subscribe_property(&mut h, 1, MIN, None).await;
assert_eq!(response(&h).await, Ok(()));
assert_eq!(
values(&h.cov_notification().await),
[(MIN, real(f32::INFINITY))]
);
subscribe_property(&mut h, 3, AVG, Some(1000.0)).await;
assert_eq!(response(&h).await, Ok(()));
assert_eq!(values(&h.cov_notification().await), [(AVG, real(f32::NAN))]);
sample(&h, 10.0).await;
assert_eq!(
notifications(&h, 2).await,
BTreeMap::from([(1, vec![(MIN, real(10.0))]), (3, vec![(AVG, real(10.0))])])
);
sample(&h, 30.0).await; h.no_notification().await;
assert_eq!(
write(&mut h, WINDOW_SAMPLES, PropertyValue::Unsigned(2)).await,
Ok(())
);
assert_eq!(
notifications(&h, 2).await,
BTreeMap::from([
(1, vec![(MIN, real(f32::INFINITY))]),
(3, vec![(AVG, real(f32::NAN))]),
])
);
assert_eq!(
write(&mut h, ATTEMPTED, PropertyValue::Unsigned(0)).await,
Ok(())
);
h.no_notification().await;
sample(&h, 10.0).await;
assert_eq!(notifications(&h, 2).await.len(), 2);
sample(&h, 20.0).await;
h.no_notification().await; sample(&h, 40.0).await;
assert_eq!(values(&h.cov_notification().await), [(MIN, real(20.0))]);
h.no_notification().await;
assert_eq!(avg_value(&h, AVG).await, PropertyValue::Real(30.0));
assert_eq!(avg_value(&h, ATTEMPTED).await, PropertyValue::Unsigned(2));
for (property, value) in [
(WINDOW_SAMPLES, PropertyValue::Unsigned(0)),
(WINDOW_SAMPLES, PropertyValue::Unsigned(1_441)),
(
PropertyIdentifier::WINDOW_INTERVAL,
PropertyValue::Unsigned(0),
),
(ATTEMPTED, PropertyValue::Unsigned(2)),
] {
assert_eq!(
write(&mut h, property, value).await,
Err(ErrorCode::VALUE_OUT_OF_RANGE),
"{property:?}"
);
}
h.no_notification().await;
assert_eq!(avg_value(&h, AVG).await, PropertyValue::Real(30.0));
assert_eq!(
avg_value(&h, WINDOW_SAMPLES).await,
PropertyValue::Unsigned(2)
);
h.server.stop().await.unwrap();
}
#[tokio::test(start_paused = true)]
async fn averaging_missed_sample_local_counts_an_attempt_and_not_a_value() {
let mut h = start().await;
subscribe_property(&mut h, 5, ATTEMPTED, None).await;
assert_eq!(response(&h).await, Ok(()));
h.cov_notification().await;
subscribe_property(&mut h, 6, VALID, None).await;
assert_eq!(response(&h).await, Ok(()));
h.cov_notification().await;
sample(&h, 8.0).await;
notifications(&h, 2).await;
missed(&h).await;
assert_eq!(
values(&h.cov_notification().await),
[(ATTEMPTED, encoded(PropertyValue::Unsigned(2)))]
);
h.no_notification().await;
assert_eq!(avg_value(&h, VALID).await, PropertyValue::Unsigned(1));
assert_eq!(avg_value(&h, AVG).await, PropertyValue::Real(8.0));
let unknown = ObjectIdentifier::new(ObjectType::AVERAGING, 9).unwrap();
let error = h
.server
.add_averaging_sample_local(&unknown, None)
.await
.unwrap_err();
assert_error(error, ErrorClass::OBJECT, ErrorCode::UNKNOWN_OBJECT);
let error = h
.server
.add_averaging_sample_local(&av1(), None)
.await
.unwrap_err();
assert_error(
error,
ErrorClass::OBJECT,
ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED,
);
h.no_notification().await;
h.server.stop().await.unwrap();
}