use super::*;
use bacnet_objects::analog::AnalogInputObject;
use bacnet_objects::traits::BACnetObject;
use bacnet_services::wpm::WritePropertyMultipleRequest;
use bacnet_types::enums::EngineeringUnits;
use bacnet_types::error::RemoteError;
fn make_db_with_ai() -> ObjectDatabase {
let mut db = ObjectDatabase::new();
let mut ai = AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap();
ai.set_present_value(72.5);
db.add(Box::new(ai)).unwrap();
db
}
fn make_db_with_msi() -> ObjectDatabase {
let mut db = ObjectDatabase::new();
db.add(Box::new(
bacnet_objects::multistate::MultiStateInputObject::new(1, "MSI-1", 3).unwrap(),
))
.unwrap();
db
}
fn make_db_with_device_and_ai() -> ObjectDatabase {
let mut db = crate::server::clocked_test_database();
let device = bacnet_objects::device::DeviceObject::new(bacnet_objects::device::DeviceConfig {
identity: bacnet_objects::DeviceIdentity::default().with_name("TestDevice"),
..Default::default()
})
.unwrap();
db.add(Box::new(device)).unwrap();
db.add(Box::new(
AnalogInputObject::new(1, "AI-1", EngineeringUnits::DEGREES_CELSIUS).unwrap(),
))
.unwrap();
db
}
fn group_member_rows(
db: &ObjectDatabase,
oid: ObjectIdentifier,
references: impl IntoIterator<Item = (PropertyIdentifier, Option<u32>)>,
) -> usize {
if oid.object_type() != ObjectType::GROUP {
return 0;
}
let Ok(PropertyValue::List(members)) = db
.get(&oid)
.unwrap()
.read_property(PropertyIdentifier::LIST_OF_GROUP_MEMBERS, None)
else {
panic!("List_Of_Group_Members is a list");
};
let per_read: usize = members
.iter()
.map(|member| {
let PropertyValue::ApplicationData(bytes) = member else {
panic!("{member:?}");
};
let (spec, _) =
bacnet_encoding::constructed::decode_read_access_specification(bytes, 0).unwrap();
spec.list_of_property_references.len()
})
.sum();
let reads = references
.into_iter()
.filter(|&(property, index)| {
property == PropertyIdentifier::PRESENT_VALUE && index.is_none()
})
.count();
reads * per_read
}
fn list_refusal(result: Result<(), Error>) -> (ErrorClass, ErrorCode, u32) {
let (class, code, element) = match result {
Err(Error::Remote(RemoteError::Error { class, code })) => (class, code, 0),
Err(Error::Structured {
class,
code,
detail,
}) => match *detail {
ErrorDetail::FirstFailedElementNumber(element) => (class, code, element),
other => panic!("expected an element number, got {other:?}"),
},
other => panic!("expected a protocol refusal, got {other:?}"),
};
(class, code, element)
}
mod access_control_arrays;
mod access_door_oos_writes;
mod access_point_authorization_writes;
mod access_required_rows;
mod access_rights_accompaniment;
mod access_rights_rule_writes;
mod access_rights_rules;
mod access_typed_values;
mod access_zone_oos_writes;
mod acknowledge_alarm;
mod acknowledge_alarm_ee;
mod alarm_summary_projection;
mod alert_enrollment;
mod array_index_gating;
mod atomic_read_file_budget;
mod atomic_write_file_budget;
mod audit_log_query;
mod audit_recipient_writes;
mod averaging_window_writes;
mod binary_lighting_operations;
mod binary_lighting_relinquish_default;
mod calendar_date_list;
mod channel_present_value_writes;
mod color_command_writes;
mod command_present_value_writes;
mod cov_multiple_admission;
mod cov_multiple_parameters;
mod cov_property_parameters;
mod cov_request_parameters;
mod create_object_creation_only;
mod create_object_default_name;
mod create_object_initial_values;
mod create_object_state_count;
mod credential_data_input_oos_writes;
mod detection_enable_summary;
mod device_description_writes;
mod device_event;
mod device_reference_reads;
mod device_reference_writes;
mod elevator_landing_calls;
mod elevator_properties;
mod enrollment_summary_budget;
mod enrollment_summary_filters;
mod enrollment_summary_recipients;
mod enrollment_summary_strict;
mod enrollment_summary_support;
mod escalator_writes;
mod file_access_method;
mod file_empty_eof;
mod file_metadata;
mod file_persistence;
mod file_storage_hook;
mod framed_properties;
mod get_event_information_projection;
mod indexed_write_presence;
mod life_safety_cov;
mod life_safety_mode_writes;
mod life_safety_oos_writes;
mod life_safety_operation;
mod life_safety_reset;
mod lighting_command_writes;
mod lighting_present_value_writes;
mod lighting_required_rows;
mod list_element_edits;
mod list_element_recipients;
mod list_element_subscriptions;
mod list_element_targets;
mod list_value_writes;
mod log_reference_writes;
mod loop_properties;
mod loop_reference_follow;
mod multi_element_writes;
mod noncommandable_null_writes;
mod noncommandable_value_source;
mod none_enrollment_fault;
mod passwords;
mod property_metadata;
mod pulse_converter_input_reference;
mod pulse_converter_writes;
mod read_event_arrays;
mod read_range;
mod read_range_audit_log;
mod read_range_time;
mod read_rpm;
mod reference_writes;
mod reporting_options_writes;
mod scalar_null_writes;
mod staging_writes;
mod state_text_count;
mod tags_profile_rows;
mod trend_log_multiple_options;
mod trend_log_options;
mod undefined_property_rows;
mod value_required_rows;
mod wpm_create_alarm;
mod wpm_prefix_commit;
mod write_cov_who;
mod write_property_name;
mod write_validation;
fn sourced_wp(db: &mut ObjectDatabase, data: &[u8]) -> Result<ObjectIdentifier, Error> {
handle_write_property_observed(
db,
data,
None,
None,
Some(&crate::command_source::test_origin()),
)
.map(|(oid, _)| oid)
}
fn sourced_wpm(db: &mut ObjectDatabase, data: &[u8]) -> Result<Vec<ObjectIdentifier>, Error> {
let mut snapshots = crate::life_safety_cov::LifeSafetyCovSnapshots::default();
match handle_write_property_multiple_observed(
db,
data,
&mut snapshots,
None,
None,
None,
Some(&crate::command_source::test_origin()),
) {
WritePropertyMultipleOutcome::Success { committed_oids } => Ok(committed_oids),
WritePropertyMultipleOutcome::Error { error, .. } => Err(error),
WritePropertyMultipleOutcome::Reject { reason } => Err(Error::reject(reason)),
}
}