bacnet-server 0.15.0

BACnet server: APDU dispatch and service handlers
Documentation
use super::*;
use bacnet_services::cov::COVNotificationRequest;
use bacnet_services::cov_multiple::COVNotificationMultipleRequest;

async fn selected_change(kind: CovNotificationKind, property: PropertyIdentifier) {
    for increment in [None, Some(2.0)] {
        let fixture = Fixture::new(false);
        fixture
            .db
            .write()
            .await
            .get_mut(&object())
            .unwrap()
            .write_property_from(
                PropertyIdentifier::PRESENT_VALUE,
                None,
                PropertyValue::Real(10.0),
                Some(8),
                &crate::command_source::test_origin(),
            )
            .unwrap();
        let mut sub = proposal(kind, false, property);
        sub.last_notified_observation = None;
        sub.cov_increment = increment;
        let accepted = fixture.table.write().await.admit_for_test(sub, 0).unwrap();
        fixture.fire(true, &[accepted]).await;
        assert_eq!(fixture.sent.lock().unwrap().len(), 1);
        fixture.sent.lock().unwrap().clear();
        {
            let mut db = fixture.db.write().await;
            let av = db.get_mut(&object()).unwrap();
            let (written, value) = if property == PropertyIdentifier::STATUS_FLAGS {
                (
                    PropertyIdentifier::OUT_OF_SERVICE,
                    PropertyValue::Boolean(true),
                )
            } else {
                (
                    PropertyIdentifier::RELINQUISH_DEFAULT,
                    PropertyValue::Real(5.0),
                )
            };
            av.write_property(written, None, value, None).unwrap();
            assert_eq!(
                av.read_property(PropertyIdentifier::PRESENT_VALUE, None)
                    .unwrap(),
                PropertyValue::Real(10.0)
            );
        }
        fixture.fire(false, &[]).await;
        let frames = fixture.sent.lock().unwrap().clone();
        assert_eq!(
            frames.len(),
            1,
            "selected {property:?} change kind={kind:?} increment={increment:?} with unchanged PV must report"
        );
        let Apdu::UnconfirmedRequest(request) =
            decode_apdu(decode_npdu(frames[0].clone()).unwrap().payload).unwrap()
        else {
            panic!("notification")
        };
        let (actual_property, value) = match kind {
            CovNotificationKind::Single => {
                let n = COVNotificationRequest::decode(&request.service_request).unwrap();
                assert_eq!(
                    n.list_of_values.len(),
                    if property == PropertyIdentifier::STATUS_FLAGS {
                        1
                    } else {
                        2
                    }
                );
                (
                    n.list_of_values[0].property_identifier,
                    n.list_of_values[0].value.clone(),
                )
            }
            CovNotificationKind::Multiple => {
                let n = COVNotificationMultipleRequest::decode(&request.service_request).unwrap();
                assert_eq!(n.list_of_cov_notifications.len(), 1);
                let values = &n.list_of_cov_notifications[0].list_of_values;
                assert_eq!(
                    values.len(),
                    if property == PropertyIdentifier::STATUS_FLAGS {
                        1
                    } else {
                        2
                    }
                );
                (values[0].property_identifier, values[0].value.clone())
            }
        };
        assert_eq!(actual_property, property);
        let expected = fixture
            .db
            .read()
            .await
            .get(&object())
            .unwrap()
            .read_property(property, None)
            .unwrap();
        let mut bytes = BytesMut::new();
        encode_property_value(&mut bytes, &expected).unwrap();
        assert_eq!(value, bytes);
        fixture.finish(false).await;
    }
}

#[tokio::test]
async fn cov_sample_single_status_selected_wire() {
    selected_change(
        CovNotificationKind::Single,
        PropertyIdentifier::STATUS_FLAGS,
    )
    .await;
}
#[tokio::test]
async fn cov_sample_multiple_status_selected_wire() {
    selected_change(
        CovNotificationKind::Multiple,
        PropertyIdentifier::STATUS_FLAGS,
    )
    .await;
}
#[tokio::test]
async fn cov_sample_single_relinquish_selected_wire() {
    selected_change(
        CovNotificationKind::Single,
        PropertyIdentifier::RELINQUISH_DEFAULT,
    )
    .await;
}
#[tokio::test]
async fn cov_sample_multiple_relinquish_selected_wire() {
    selected_change(
        CovNotificationKind::Multiple,
        PropertyIdentifier::RELINQUISH_DEFAULT,
    )
    .await;
}