bacnet-objects 0.13.0

BACnet object model: traits, database, and standard object types
Documentation
use super::super::*;
use crate::event::{
    EventStateChange, EventTransition, EventTransitionCommit, EventTransitionCommitError,
};
use bacnet_encoding::primitives::decode_timestamp_choice;
use bacnet_types::primitives::BACnetTimeStamp;

fn timestamp_at(object: &EventEnrollmentObject, index: u32) -> BACnetTimeStamp {
    let PropertyValue::ApplicationData(bytes) = object
        .read_property(PropertyIdentifier::EVENT_TIME_STAMPS, Some(index))
        .unwrap()
    else {
        panic!("Event_Time_Stamps slot must be encoded application data");
    };
    let (timestamp, end) = decode_timestamp_choice(&bytes, 0).unwrap();
    assert_eq!(end, bytes.len());
    timestamp
}

fn snapshot(object: &EventEnrollmentObject) -> (PropertyValue, PropertyValue, PropertyValue) {
    (
        object
            .read_property(PropertyIdentifier::EVENT_STATE, None)
            .unwrap(),
        object
            .read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
            .unwrap(),
        object
            .read_property(PropertyIdentifier::EVENT_TIME_STAMPS, None)
            .unwrap(),
    )
}

#[test]
fn stock_enrollment_atomically_commits_state_ack_and_exact_history_coordinate() {
    let mut object =
        EventEnrollmentObject::new(1, "EE-atomic", EventType::CHANGE_OF_BITSTRING).unwrap();

    object
        .commit_event_transition_internal(EventTransitionCommit {
            change: EventStateChange {
                from: EventState::NORMAL,
                to: EventState::HIGH_LIMIT,
            },
            coordinate: EventTransition::ToOffnormal,
            ack_required: true,
            timestamp: BACnetTimeStamp::SequenceNumber(41),
            message_text: None,
        })
        .unwrap();
    assert_eq!(
        object
            .read_property(PropertyIdentifier::EVENT_STATE, None)
            .unwrap(),
        PropertyValue::Enumerated(EventState::HIGH_LIMIT.to_raw())
    );
    assert_eq!(
        object
            .read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
            .unwrap(),
        PropertyValue::BitString {
            unused_bits: 5,
            data: vec![0x60],
        }
    );
    assert_eq!(
        timestamp_at(&object, 1),
        BACnetTimeStamp::SequenceNumber(41)
    );
    assert_eq!(timestamp_at(&object, 2), BACnetTimeStamp::SequenceNumber(0));
    assert_eq!(timestamp_at(&object, 3), BACnetTimeStamp::SequenceNumber(0));
    assert_eq!(
        object.event_history.original_from_states,
        [Some(EventState::NORMAL), None, None]
    );
    assert_eq!(
        object.event_history.original_to_states,
        [Some(EventState::HIGH_LIMIT), None, None]
    );

    object
        .commit_event_transition_internal(EventTransitionCommit {
            change: EventStateChange {
                from: EventState::HIGH_LIMIT,
                to: EventState::HIGH_LIMIT,
            },
            coordinate: EventTransition::ToOffnormal,
            ack_required: false,
            timestamp: BACnetTimeStamp::SequenceNumber(42),
            message_text: None,
        })
        .unwrap();
    assert_eq!(
        timestamp_at(&object, 1),
        BACnetTimeStamp::SequenceNumber(42)
    );
    assert_eq!(
        object.event_history.original_from_states,
        [Some(EventState::HIGH_LIMIT), None, None]
    );
    assert_eq!(
        object.event_history.original_to_states,
        [Some(EventState::HIGH_LIMIT), None, None]
    );
    assert_eq!(
        object
            .read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
            .unwrap(),
        PropertyValue::BitString {
            unused_bits: 5,
            data: vec![0xE0],
        }
    );
}

#[test]
fn rejected_stock_commit_changes_none_of_the_three_properties() {
    let mut object =
        EventEnrollmentObject::new(2, "EE-reject", EventType::CHANGE_OF_BITSTRING).unwrap();
    let before = snapshot(&object);

    assert_eq!(
        object.commit_event_transition_internal(EventTransitionCommit {
            change: EventStateChange {
                from: EventState::NORMAL,
                to: EventState::HIGH_LIMIT,
            },
            coordinate: EventTransition::ToNormal,
            ack_required: true,
            timestamp: BACnetTimeStamp::SequenceNumber(99),
            message_text: None,
        }),
        Err(EventTransitionCommitError::CoordinateTargetMismatch {
            coordinate: EventTransition::ToNormal,
            target: EventState::HIGH_LIMIT,
        })
    );
    assert_eq!(snapshot(&object), before);
}