use super::*;
use bacnet_types::error::RemoteError;
pub(in crate::server) struct ReadAuditIntent(PendingWrite);
impl<T: TransportPort + 'static> WriteAudit<'_, T> {
pub(in crate::server) fn read_intent(
&self,
db: &ObjectDatabase,
target: ObjectIdentifier,
property: PropertyIdentifier,
index: Option<u32>,
result: Option<(ErrorClass, ErrorCode)>,
) -> Option<ReadAuditIntent> {
self.read_target_intent(db, target, Some((property, index)), result)
}
pub(in crate::server) fn completed_read_intent<R>(
&self,
db: &ObjectDatabase,
target: ObjectIdentifier,
property: Option<(PropertyIdentifier, Option<u32>)>,
result: &Result<R, Error>,
) -> Option<ReadAuditIntent> {
let result = match result {
Ok(_) => None,
Err(
Error::Timeout(_)
| Error::Remote(RemoteError::Reject { .. })
| Error::Remote(RemoteError::Abort { .. }),
) => return None,
Err(error) => Some(super::super::requests::confirmed_response::error_fields(
error,
)),
};
self.read_target_intent(db, target, property, result)
}
fn read_target_intent(
&self,
db: &ObjectDatabase,
target: ObjectIdentifier,
property: Option<(PropertyIdentifier, Option<u32>)>,
result: Option<(ErrorClass, ErrorCode)>,
) -> Option<ReadAuditIntent> {
let selected_reporter = self.select(Some(target), target.object_type())?;
let reporter = &selected_reporter.configuration;
let device = db.local_device().identifier();
let route = device
.and_then(|device| recipient(db, device))
.and_then(|value| self.transactions.audit_routes.get()?.resolve(&value));
let status = Arc::clone(&selected_reporter.status);
status.set_configured(device.is_some() && route.is_some());
let device = device?;
let policy = db
.get(&target)
.map(|object| object.audit_object_policy_internal())
.unwrap_or_default()
.effective_internal(reporter);
if !policy.reports(AuditOperation::READ, property.map(|(id, _)| id), None)
|| !reporter.monitors(target)
{
return None;
}
Some(ReadAuditIntent(PendingWrite {
delay: reporter.maximum_send_delay,
selection: None,
failure: Some(self.failure_ticket(
&status,
reporter.confirmed,
device,
route.clone(),
)?),
route,
completion: status.begin_delivery(),
status,
confirmed: reporter.confirmed,
notification: BACnetAuditNotification {
source_timestamp: None,
target_timestamp: None,
source_device: self.source.clone(),
source_object: None,
operation: AuditOperation::READ,
source_comment: None,
target_comment: None,
invoke_id: self.invoke_id,
source_user_id: None,
source_user_role: None,
target_device: BACnetRecipient::Device(device),
target_object: Some(target),
target_property: property.map(|(property, index)| AuditPropertyReference {
property_identifier: property,
property_array_index: index.map(u64::from),
}),
target_priority: None,
target_value: None,
current_value: None,
result,
},
}))
}
pub(in crate::server) async fn admit_reads(
&self,
db: &RwLock<ObjectDatabase>,
mut intents: Vec<ReadAuditIntent>,
) {
intents.retain(|intent| intent.0.route.is_some());
if intents.is_empty() {
return;
}
{
let mut db = db.write().await;
for intent in &mut intents {
intent.0.notification.target_timestamp =
Some(super::super::event_timestamp::sample_event_timestamp(&mut db).timestamp);
}
}
for intent in intents {
self.admit(intent.0);
}
}
}