bacnet-objects 0.12.0

BACnet object model: traits, database, and standard object types
Documentation
//! #270 — writable Relinquish_Default on the commandable multi-state
//! types (MultiState Output and MultiState Value).

use super::super::*;
// ---------------------------------------------------------------------------
// #270 — writable Relinquish_Default (MSO + MSV)
// ---------------------------------------------------------------------------

/// A MultiState Relinquish_Default write is validated like a commanded
/// Present_Value (Unsigned 1..=Number_Of_States) and — with an all-NULL
/// priority array — Present_Value immediately resolves to the written
/// default.
#[test]
fn mso_msv_relinquish_default_write_recaptures_present_value() {
    for object in [
        &mut MultiStateOutputObject::new(1, "MSO-1", 3).unwrap() as &mut dyn BACnetObject,
        &mut MultiStateValueObject::new(1, "MSV-1", 3).unwrap() as &mut dyn BACnetObject,
    ] {
        assert!(object.is_writable_property(PropertyIdentifier::RELINQUISH_DEFAULT));

        object
            .write_property(
                PropertyIdentifier::RELINQUISH_DEFAULT,
                None,
                PropertyValue::Unsigned(2),
                None,
            )
            .unwrap();
        assert_eq!(
            object
                .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
                .unwrap(),
            PropertyValue::Unsigned(2)
        );
        assert_eq!(
            object
                .read_property(PropertyIdentifier::PRESENT_VALUE, None)
                .unwrap(),
            PropertyValue::Unsigned(2),
            "with an empty priority array, PV must resolve to the written default"
        );

        // 0 and Number_Of_States+1 are out of range; u32-overflow refuses at
        // the same pairing as an out-of-range state. None touch the default.
        for value in [
            PropertyValue::Unsigned(0),
            PropertyValue::Unsigned(4),
            PropertyValue::Unsigned(u64::MAX),
            PropertyValue::Real(2.0),
        ] {
            assert!(
                object
                    .write_property(PropertyIdentifier::RELINQUISH_DEFAULT, None, value, None)
                    .is_err(),
                "invalid Relinquish_Default write must refuse"
            );
            assert_eq!(
                object
                    .read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
                    .unwrap(),
                PropertyValue::Unsigned(2),
                "refused writes must leave Relinquish_Default untouched"
            );
        }
    }

    // The local setters share the validation.
    let mut mso = MultiStateOutputObject::new(2, "MSO-2", 5).unwrap();
    assert!(mso.set_relinquish_default(6).is_err());
    assert!(mso.set_relinquish_default(0).is_err());
    mso.set_relinquish_default(5).unwrap();
    assert_eq!(
        mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
            .unwrap(),
        PropertyValue::Unsigned(5)
    );
}

/// Number_Of_States-shrink interplay, pinned: Clause 12.19 / Table 12-22 leave any
/// adjustment of Priority_Array / Relinquish_Default / Present_Value /
/// Feedback_Value to local policy when the state count shrinks below stored
/// values, so the local count setter does not auto-adjust them — a stale
/// Relinquish_Default keeps driving Present_Value until the application
/// resolves the configuration, with CONFIGURATION_ERROR reported meanwhile.
#[test]
fn mso_relinquish_default_is_not_range_locked_after_store() {
    let mut mso = MultiStateOutputObject::new(1, "MSO-1", 3).unwrap();
    mso.set_relinquish_default(3).unwrap();
    assert_eq!(
        mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
            .unwrap(),
        PropertyValue::Unsigned(3)
    );
    mso.set_number_of_states(2).unwrap();
    assert_eq!(
        mso.read_property(PropertyIdentifier::RELINQUISH_DEFAULT, None)
            .unwrap(),
        PropertyValue::Unsigned(3)
    );
    assert_eq!(
        mso.read_property(PropertyIdentifier::PRESENT_VALUE, None)
            .unwrap(),
        PropertyValue::Unsigned(3)
    );
    assert_eq!(
        mso.read_property(PropertyIdentifier::RELIABILITY, None)
            .unwrap(),
        PropertyValue::Enumerated(Reliability::CONFIGURATION_ERROR.to_raw())
    );
    // The local API adds no Number_Of_States write arm of its own; the row
    // is writable only through a whole State_Text write (#1443).
    assert!(mso
        .write_property(
            PropertyIdentifier::NUMBER_OF_STATES,
            None,
            PropertyValue::Unsigned(3),
            None
        )
        .is_err());
}