use super::super::*;
use super::metadata::assert_error;
use bacnet_types::enums::PropertyIdentifier as P;
use bacnet_types::enums::{
ErrorCode, EscalatorFault, EscalatorMode, EscalatorOperationDirection, LiftCarDirection,
LiftFault,
};
#[test]
fn property_metadata_elevator_group_writes_store_verbatim() {
let mut object = ElevatorGroupObject::new(1, "EG-1").unwrap();
for (p, value, expected) in [
(
P::GROUP_ID,
PropertyValue::Unsigned(47),
PropertyValue::Unsigned(47),
),
(
P::GROUP_MODE,
PropertyValue::Enumerated(2),
PropertyValue::Enumerated(2),
),
(
P::LANDING_CALL_CONTROL,
PropertyValue::ApplicationData(vec![0x09, 0x05, 0x19, 0x03]),
PropertyValue::ApplicationData(vec![0x09, 0x05, 0x19, 0x03]),
),
] {
object.write_property(p, None, value, None).unwrap();
assert_eq!(object.read_property(p, None).unwrap(), expected);
}
for (p, value) in [
(P::GROUP_ID, PropertyValue::Enumerated(47)),
(P::GROUP_MODE, PropertyValue::Unsigned(2)),
(P::LANDING_CALL_CONTROL, PropertyValue::Unsigned(1)),
(P::DESCRIPTION, PropertyValue::Unsigned(1)),
] {
assert_error(
object.write_property(p, None, value, None).unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
for p in [P::MACHINE_ROOM_ID, P::GROUP_MEMBERS, P::LANDING_CALLS] {
let value = object.read_property(p, None).unwrap();
assert_error(
object.write_property(p, None, value, None).unwrap_err(),
ErrorCode::WRITE_ACCESS_DENIED,
);
assert!(!object.is_writable_property(p));
}
}
#[test]
fn property_metadata_escalator_domain_validation_matches_dispatch() {
for out_of_service in [false, true] {
let mut object = EscalatorObject::new(1, "ESC-1").unwrap();
object
.write_property(
P::OUT_OF_SERVICE,
None,
PropertyValue::Boolean(out_of_service),
None,
)
.unwrap();
for (p, named, prior) in [
(
P::ESCALATOR_MODE,
EscalatorMode::UP.to_raw(),
EscalatorMode::STOP.to_raw(),
),
(
P::OPERATION_DIRECTION,
EscalatorOperationDirection::DOWN_REDUCED_SPEED.to_raw(),
EscalatorOperationDirection::UP_RATED_SPEED.to_raw(),
),
] {
for raw in [named, 1024, 65535] {
object
.write_property(p, None, PropertyValue::Enumerated(raw), None)
.unwrap_or_else(|e| panic!("{p:?} {raw} must be accepted: {e:?}"));
assert_eq!(
object.read_property(p, None).unwrap(),
PropertyValue::Enumerated(raw)
);
}
object
.write_property(p, None, PropertyValue::Enumerated(prior), None)
.unwrap();
for raw in [6u32, 1023, 65536, u32::MAX] {
assert_error(
object
.write_property(p, None, PropertyValue::Enumerated(raw), None)
.unwrap_err(),
ErrorCode::VALUE_OUT_OF_RANGE,
);
}
assert_eq!(
object.read_property(p, None).unwrap(),
PropertyValue::Enumerated(prior)
);
assert_error(
object
.write_property(p, None, PropertyValue::Unsigned(prior as u64), None)
.unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
object
.write_property(P::ENERGY_METER, None, PropertyValue::Real(42.0), None)
.unwrap();
for value in [f32::NAN, f32::INFINITY, f32::NEG_INFINITY] {
assert_error(
object
.write_property(P::ENERGY_METER, None, PropertyValue::Real(value), None)
.unwrap_err(),
ErrorCode::VALUE_OUT_OF_RANGE,
);
}
assert_eq!(
object.read_property(P::ENERGY_METER, None).unwrap(),
PropertyValue::Real(42.0)
);
let prior = PropertyValue::List(vec![
PropertyValue::Enumerated(EscalatorFault::CONTROLLER_FAULT.to_raw()),
PropertyValue::Enumerated(1024),
]);
object
.write_property(P::FAULT_SIGNALS, None, prior.clone(), None)
.unwrap();
for (values, expected) in [
(
vec![
PropertyValue::Enumerated(1024),
PropertyValue::Enumerated(1024),
],
ErrorCode::VALUE_OUT_OF_RANGE,
),
(
vec![PropertyValue::Enumerated(9)],
ErrorCode::VALUE_OUT_OF_RANGE,
),
(
vec![PropertyValue::Enumerated(1023)],
ErrorCode::VALUE_OUT_OF_RANGE,
),
(
vec![PropertyValue::Unsigned(8)],
ErrorCode::INVALID_DATA_TYPE,
),
] {
assert_error(
object
.write_property(P::FAULT_SIGNALS, None, PropertyValue::List(values), None)
.unwrap_err(),
expected,
);
assert_eq!(object.read_property(P::FAULT_SIGNALS, None).unwrap(), prior);
}
let energy_ref = object.read_property(P::ENERGY_METER_REF, None).unwrap();
assert_error(
object
.write_property(P::ENERGY_METER_REF, None, energy_ref, None)
.unwrap_err(),
ErrorCode::WRITE_ACCESS_DENIED,
);
for p in [P::POWER_MODE, P::PASSENGER_ALARM] {
object
.write_property(p, None, PropertyValue::Boolean(true), None)
.unwrap();
assert_eq!(
object.read_property(p, None).unwrap(),
PropertyValue::Boolean(true)
);
assert_error(
object
.write_property(p, None, PropertyValue::Enumerated(1), None)
.unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
}
}
}
#[test]
fn property_metadata_lift_writes_store_verbatim_with_range_gates() {
for out_of_service in [false, true] {
let mut object = LiftObject::new(1, "LIFT-1", 3).unwrap();
object
.write_property(
P::OUT_OF_SERVICE,
None,
PropertyValue::Boolean(out_of_service),
None,
)
.unwrap();
for (p, value) in [
(P::CAR_POSITION, PropertyValue::Unsigned(255)),
(
P::CAR_MOVING_DIRECTION,
PropertyValue::Enumerated(LiftCarDirection::DOWN.to_raw()),
),
(P::CAR_LOAD, PropertyValue::Real(312.5)),
(P::PASSENGER_ALARM, PropertyValue::Boolean(true)),
(P::ENERGY_METER, PropertyValue::Real(-1.5)),
(
P::FAULT_SIGNALS,
PropertyValue::List(vec![
PropertyValue::Enumerated(LiftFault::POSITION_LOST.to_raw()),
PropertyValue::Enumerated(1024),
]),
),
] {
object
.write_property(p, None, value.clone(), None)
.unwrap_or_else(|e| panic!("{p:?} must accept {value:?}: {e:?}"));
assert_eq!(object.read_property(p, None).unwrap(), value, "{p:?}");
}
for (p, value) in [
(P::CAR_POSITION, PropertyValue::Unsigned(256)),
(P::CAR_MOVING_DIRECTION, PropertyValue::Enumerated(6)),
(P::CAR_LOAD, PropertyValue::Real(f32::NAN)),
(P::ENERGY_METER, PropertyValue::Real(f32::INFINITY)),
(P::FAULT_SIGNALS, PropertyValue::Enumerated(17)),
] {
let before = object.read_property(p, None).unwrap();
assert_error(
object.write_property(p, None, value, None).unwrap_err(),
ErrorCode::VALUE_OUT_OF_RANGE,
);
assert_eq!(object.read_property(p, None).unwrap(), before, "{p:?}");
}
for (p, value) in [
(P::CAR_POSITION, PropertyValue::Enumerated(2)),
(P::CAR_MOVING_DIRECTION, PropertyValue::Unsigned(2)),
(P::CAR_LOAD, PropertyValue::Unsigned(50)),
(P::PASSENGER_ALARM, PropertyValue::Enumerated(1)),
(P::ENERGY_METER, PropertyValue::Unsigned(1)),
(P::FAULT_SIGNALS, PropertyValue::Unsigned(1)),
(P::DESCRIPTION, PropertyValue::Unsigned(1)),
(
P::OUT_OF_SERVICE,
PropertyValue::CharacterString("invalid".into()),
),
] {
let before = object.read_property(p, None).unwrap();
assert_error(
object.write_property(p, None, value, None).unwrap_err(),
ErrorCode::INVALID_DATA_TYPE,
);
assert_eq!(object.read_property(p, None).unwrap(), before, "{p:?}");
}
for p in [
P::ELEVATOR_GROUP,
P::GROUP_ID,
P::INSTALLATION_ID,
P::FLOOR_TEXT,
P::CAR_LOAD_UNITS,
P::ENERGY_METER_REF,
] {
let value = object.read_property(p, None).unwrap();
assert_error(
object.write_property(p, None, value, None).unwrap_err(),
ErrorCode::WRITE_ACCESS_DENIED,
);
assert!(!object.is_writable_property(p));
}
for p in [
P::ASSIGNED_LANDING_CALLS,
P::MAKING_CAR_CALL,
P::REGISTERED_CAR_CALL,
P::CAR_ASSIGNED_DIRECTION,
P::CAR_DOOR_STATUS,
P::CAR_DOOR_COMMAND,
P::CAR_DOOR_ZONE,
P::CAR_MODE,
P::NEXT_STOPPING_FLOOR,
P::CAR_DRIVE_STATUS,
P::LANDING_DOOR_STATUS,
] {
let value = object.read_property(p, None).unwrap();
let result = object.write_property(p, None, value, None);
if out_of_service {
result.unwrap();
} else {
assert_error(result.unwrap_err(), ErrorCode::WRITE_ACCESS_DENIED);
}
assert!(object.is_writable_property(p));
}
}
}