bacnet_endpoint/
source_profile.rs1use super::*;
3impl<T: TransportPort + 'static> EndpointSession<T> {
4 pub async fn write_audit_recipient(
21 &self,
22 recipient: Option<bacnet_types::constructed::BACnetRecipient>,
23 ) -> Result<(), Error> {
24 let unavailable = || Error::Encoding("endpoint source Audit owner is not running".into());
25 if !self.is_running() {
26 return Err(unavailable());
27 }
28 let database = self.database.as_ref().ok_or_else(unavailable)?;
29 let mut db = database.write().await;
30 let runtime = self
31 .source_recipient
32 .as_ref()
33 .filter(|runtime| self.is_running() && runtime.owner.is_active())
34 .ok_or_else(unavailable)?;
35 runtime.write_local(&mut db, recipient)
36 }
37
38 pub(super) fn prepare_source_audit_reporter(
39 &mut self,
40 ) -> Result<Option<crate::source_audit::recipient::SourceRoutes>, Error> {
41 let Some(selected) = self.source_audit_reporter else {
42 return Ok(None);
43 };
44 if self.role == SessionRole::ServerOnly {
45 return Err(Error::Encoding(
46 "source Audit Reporter requires a client role".into(),
47 ));
48 }
49 if self.role == SessionRole::Both && self.device_write_authorizer.is_none() {
50 return Err(Error::Encoding(
51 "source Audit with Both roles requires an explicit Device write authorizer".into(),
52 ));
53 }
54 let broadcast = self
55 .ingress
56 .as_ref()
57 .and_then(|ingress| ingress.bip_broadcast_endpoint())
58 .ok_or_else(|| Error::Encoding("source Audit requires IPv4 B/IP".into()))?;
59 let routes = crate::source_audit::recipient::SourceRoutes::new(
60 &self.source_audit_bindings,
61 broadcast,
62 )?;
63 let db = self.database.as_mut().ok_or_else(|| {
64 Error::Encoding("source Audit Reporter requires an attached local database".into())
65 })?;
66 let db = Arc::get_mut(db)
69 .expect("database is unshared before startup")
70 .get_mut();
71 let device = db.local_device().identifier().ok_or_else(|| {
74 Error::Encoding("source Audit Reporter requires a concrete local Device".into())
75 })?;
76 if self
77 .identity
78 .as_ref()
79 .is_some_and(|identity| device.instance_number() != identity.instance())
80 {
81 return Err(Error::Encoding(
82 "source Audit Reporter local Device does not match session identity".into(),
83 ));
84 }
85 let recipient = db
86 .get_mut(&device)
87 .and_then(|object| object.device_authority_internal())
88 .filter(|authority| authority.object_identifier() == device)
89 .and_then(|authority| authority.provisioned_audit_recipient().cloned())
90 .ok_or_else(|| {
91 Error::Encoding(
92 "source Audit requires a provisioned built-in Device recipient".into(),
93 )
94 })?;
95 routes.validate_initial(&recipient)?;
96 if selected.object_type() != ObjectType::AUDIT_REPORTER {
97 return Err(Error::Encoding(
98 "source selection must be an Audit Reporter".into(),
99 ));
100 }
101 let object = db.get(&selected).ok_or_else(|| {
102 Error::Encoding("selected source Audit Reporter is absent from the database".into())
103 })?;
104 if !object
105 .audit_reporter_internal()
106 .is_some_and(|reporter| reporter.object_identifier() == selected)
107 {
108 return Err(Error::Encoding(
109 "selected object lacks the Audit Reporter capability".into(),
110 ));
111 }
112 if object.audit_reporter_internal().is_some_and(|reporter| {
113 reporter
114 .property_list()
115 .contains(&PropertyIdentifier::MONITORED_OBJECTS)
116 || reporter
117 .configuration_internal()
118 .maximum_send_delay
119 .is_some()
120 }) {
121 return Err(Error::Encoding(
122 "source Audit does not support Monitored_Objects or delayed notifications".into(),
123 ));
124 }
125 for (oid, object) in db.iter_objects() {
126 if oid != selected && oid.object_type() == ObjectType::AUDIT_REPORTER {
127 match object.read_property(PropertyIdentifier::AUDIT_SOURCE_REPORTER, None) {
128 Ok(PropertyValue::Boolean(false)) => {}
129 Ok(PropertyValue::Boolean(true)) => {
130 return Err(Error::Encoding(
131 "database already contains a conflicting source Audit Reporter".into(),
132 ));
133 }
134 _ => {
135 return Err(Error::Encoding(
136 "cannot determine another Audit Reporter's source ownership".into(),
137 ));
138 }
139 }
140 }
141 }
142 Ok(Some(routes))
143 }
144}