use bacnet_encoding::constructed::encode_log_record;
use bacnet_services::read_range::{
RangeSpec, ReadRangeAck, ReadRangeRequest, ReadRangeValidation, ReadRangeViolation,
};
use bacnet_types::constructed::{BACnetLogRecord, LogDatum};
use bacnet_types::enums::{ConfirmedServiceChoice, ObjectType, PropertyIdentifier};
use bacnet_types::error::Error;
use bacnet_types::primitives::{Date, ObjectIdentifier, Time};
use bytes::BytesMut;
use super::fake_device::{DEVICE_MAC, client_with_device};
fn trend_log() -> ObjectIdentifier {
ObjectIdentifier::new(ObjectType::TREND_LOG, 1).unwrap()
}
fn records(count: u8) -> Vec<BACnetLogRecord> {
(0..count)
.map(|second| BACnetLogRecord {
date: Date {
year: 126,
month: 10,
day: 5,
day_of_week: 1,
},
time: Time {
hour: 9,
minute: 0,
second,
hundredths: 0,
},
log_datum: LogDatum::RealValue(f32::from(second)),
status_flags: None,
})
.collect()
}
fn wrapped_page() -> Vec<u8> {
let mut item_data = BytesMut::new();
for record in records(10) {
encode_log_record(&record, &mut item_data).unwrap();
}
let ack = ReadRangeAck {
object_identifier: trend_log(),
property_identifier: PropertyIdentifier::LOG_BUFFER,
property_array_index: None,
result_flags: (false, false, true),
item_count: 10,
item_data: item_data.to_vec(),
first_sequence_number: Some(0),
};
let mut buf = BytesMut::new();
ack.encode(&mut buf);
buf.to_vec()
}
fn request() -> ReadRangeRequest {
ReadRangeRequest {
object_identifier: trend_log(),
property_identifier: PropertyIdentifier::LOG_BUFFER,
property_array_index: None,
range: Some(RangeSpec::BySequenceNumber {
reference_seq: 1,
count: 10,
}),
}
}
#[tokio::test]
async fn strict_refuses_a_zero_first_sequence_page_with_the_rule_it_broke() {
let (mut client, device) = client_with_device(|_, _| Ok(wrapped_page())).await;
let request = request();
let error = client
.read_range(
&DEVICE_MAC,
request.object_identifier,
request.property_identifier,
None,
request.range.clone(),
)
.await
.unwrap_err();
assert!(
matches!(
error,
Error::ReadRangeViolation(ReadRangeViolation::ZeroFirstSequenceNumber)
),
"{error:?}"
);
let error = client
.read_range_with(&DEVICE_MAC, &request, ReadRangeValidation::Strict)
.await
.unwrap_err();
assert!(matches!(error, Error::ReadRangeViolation(_)));
assert_eq!(device.count(ConfirmedServiceChoice::READ_RANGE), 2);
client.stop().await.unwrap();
}
#[tokio::test]
async fn lenient_keeps_the_page_and_names_the_rule_in_one_request() {
let (mut client, device) = client_with_device(|_, _| Ok(wrapped_page())).await;
let reply = client
.read_range_with(&DEVICE_MAC, &request(), ReadRangeValidation::Lenient)
.await
.unwrap();
assert_eq!(
reply.violations,
[ReadRangeViolation::ZeroFirstSequenceNumber]
);
assert_eq!(reply.ack.first_sequence_number, Some(0));
assert_eq!(reply.ack.trend_log_records().unwrap(), records(10));
assert_eq!(device.count(ConfirmedServiceChoice::READ_RANGE), 1);
client.stop().await.unwrap();
}
#[tokio::test]
async fn lenient_still_refuses_an_answer_that_does_not_decode() {
let (mut client, _device) = client_with_device(|_, _| Ok(vec![0x0C, 0x00])).await;
let error = client
.read_range_with(&DEVICE_MAC, &request(), ReadRangeValidation::Lenient)
.await
.unwrap_err();
assert!(
matches!(error, Error::Decoding(_) | Error::BufferTooShort { .. }),
"{error:?}"
);
client.stop().await.unwrap();
}