bacnet_objects/device_identity/
mod.rs1mod network_port;
3use crate::prelude::*;
4use crate::{
5 database::ObjectDatabase,
6 device::{DeviceConfig, DeviceObject},
7 network_port::{Bip6PortConfig, BipPortConfig},
8 traits::BACnetObject,
9};
10use bacnet_types::{
11 enums::{NetworkType, ObjectType, PropertyIdentifier as P, Segmentation, ServiceSupported},
12 error::Error,
13 primitives::{ObjectIdentifier, PropertyValue},
14};
15use core::net::{Ipv4Addr, Ipv6Addr};
16pub use network_port::NetworkPortEntry;
17
18#[derive(Clone, Debug)]
35pub struct DeviceIdentity {
36 instance: u32,
37 vendor_id: u16,
38 max_apdu_length: u16,
39 segmentation_supported: Segmentation,
40 apdu_segment_timeout_ms: u64,
41 services: Vec<ServiceSupported>,
42 network_ports: Vec<NetworkPortEntry>,
43 device_uuid: [u8; 16],
44 name: String,
45}
46
47impl DeviceIdentity {
48 pub fn new(instance: u32, vendor_id: u16) -> Result<Self, Error> {
57 ObjectIdentifier::new(ObjectType::DEVICE, instance)?;
58 Ok(Self {
59 instance,
60 vendor_id,
61 max_apdu_length: 1476,
62 segmentation_supported: Segmentation::NONE,
63 apdu_segment_timeout_ms: 5000,
64 services: vec![ServiceSupported::READ_PROPERTY],
65 network_ports: Vec::new(),
66 device_uuid: [0; 16],
67 name: format!("device-{instance}"),
68 })
69 }
70
71 pub fn with_max_apdu(mut self, max_apdu: u16) -> Result<Self, Error> {
77 if max_apdu < 50 {
78 return Err(Error::Encoding(
79 "Device max-APDU must be at least 50".into(),
80 ));
81 }
82 self.max_apdu_length = max_apdu;
83 Ok(self)
84 }
85
86 pub fn with_segmentation(mut self, segmentation: Segmentation) -> Self {
92 self.segmentation_supported = segmentation;
93 self
94 }
95
96 pub fn with_apdu_segment_timeout_ms(mut self, timeout_ms: u64) -> Self {
99 self.apdu_segment_timeout_ms = timeout_ms;
100 self
101 }
102
103 pub fn with_services(mut self, services: &[ServiceSupported]) -> Self {
113 self.services = services.to_vec();
114 self
115 }
116
117 pub fn with_device_uuid(mut self, uuid: [u8; 16]) -> Self {
123 self.device_uuid = uuid;
124 self
125 }
126
127 pub fn with_name(mut self, name: impl Into<String>) -> Self {
129 self.name = name.into();
130 self
131 }
132
133 pub fn with_network_port(mut self, entry: NetworkPortEntry) -> Result<Self, Error> {
141 entry.build_object()?;
142 if self
143 .network_ports
144 .iter()
145 .any(|e| e.instance == entry.instance)
146 {
147 return Err(Error::Encoding(format!(
148 "duplicate Network-Port instance {}",
149 entry.instance
150 )));
151 }
152 self.network_ports.push(entry);
153 Ok(self)
154 }
155
156 pub fn with_bip_port(
158 self,
159 instance: u32,
160 network_number: u32,
161 ip: Ipv4Addr,
162 udp_port: u16,
163 ) -> Result<Self, Error> {
164 self.with_network_port(NetworkPortEntry::bip(
165 instance,
166 network_number,
167 ip,
168 udp_port,
169 ))
170 }
171
172 pub fn with_bip6_port(
175 self,
176 instance: u32,
177 network_number: u32,
178 ip: Ipv6Addr,
179 udp_port: u16,
180 ) -> Result<Self, Error> {
181 self.with_network_port(NetworkPortEntry::bip6(
182 instance,
183 network_number,
184 ip,
185 udp_port,
186 ))
187 }
188
189 pub fn with_sc_port(
191 self,
192 instance: u32,
193 network_number: u32,
194 vmac: [u8; 6],
195 ) -> Result<Self, Error> {
196 self.with_network_port(NetworkPortEntry::sc(instance, network_number, vmac))
197 }
198
199 fn registered_entry(&self, oid: ObjectIdentifier) -> Result<&NetworkPortEntry, Error> {
202 let entry = self
203 .network_ports
204 .iter()
205 .find(|entry| entry.instance == oid.instance_number())
206 .ok_or_else(|| Error::Encoding("selected Network Port missing from identity".into()))?;
207 entry.build_object()?;
208 Ok(entry)
209 }
210
211 #[doc(hidden)]
212 pub fn validate_registered_bip_internal(
213 &self,
214 oid: ObjectIdentifier,
215 config: &BipPortConfig,
216 ) -> Result<(), Error> {
217 let entry = self.registered_entry(oid)?;
218 if entry.network_type != NetworkType::IPV4.to_raw()
219 || entry.ip != config.ip_address
220 || entry.udp_port != config.udp_port
221 || entry.network_number != u32::from(config.network_number)
222 {
223 return Err(Error::Encoding(
224 "selected identity Network Port differs from database".into(),
225 ));
226 }
227 Ok(())
228 }
229
230 #[doc(hidden)]
235 pub fn validate_registered_bip6_internal(
236 &self,
237 oid: ObjectIdentifier,
238 config: &Bip6PortConfig,
239 ) -> Result<(), Error> {
240 let entry = self.registered_entry(oid)?;
241 if entry.network_type != NetworkType::IPV6.to_raw()
242 || entry.ipv6 != config.ip_address
243 || entry.udp_port != config.udp_port
244 || entry.network_number != u32::from(config.network_number)
245 {
246 return Err(Error::Encoding(
247 "selected identity Network Port differs from database".into(),
248 ));
249 }
250 Ok(())
251 }
252
253 #[doc(hidden)]
256 pub fn publish_registered_bip6_internal(
257 &mut self,
258 oid: ObjectIdentifier,
259 address: core::net::SocketAddrV6,
260 vmac: [u8; 3],
261 ) {
262 let entry = self
263 .network_ports
264 .iter_mut()
265 .find(|entry| entry.instance == oid.instance_number())
266 .expect("validated identity registration");
267 *entry = NetworkPortEntry::bip6_with_vmac(
268 entry.instance,
269 entry.network_number,
270 *address.ip(),
271 address.port(),
272 vmac,
273 );
274 }
275
276 #[doc(hidden)]
277 pub fn publish_registered_bip_internal(
278 &mut self,
279 oid: ObjectIdentifier,
280 address: core::net::SocketAddrV4,
281 ) {
282 let entry = self
283 .network_ports
284 .iter_mut()
285 .find(|entry| entry.instance == oid.instance_number())
286 .expect("validated identity registration");
287 *entry = NetworkPortEntry::bip(
288 entry.instance,
289 entry.network_number,
290 *address.ip(),
291 address.port(),
292 );
293 }
294
295 pub fn instance(&self) -> u32 {
297 self.instance
298 }
299
300 pub fn vendor_id(&self) -> u16 {
302 self.vendor_id
303 }
304
305 pub fn max_apdu_length(&self) -> u16 {
307 self.max_apdu_length
308 }
309
310 pub fn name(&self) -> &str {
312 &self.name
313 }
314
315 pub fn apdu_segment_timeout_ms(&self) -> u64 {
317 self.apdu_segment_timeout_ms
318 }
319
320 pub fn resolve(&self, local_receive_capacity: u16) -> Result<Self, Error> {
322 let mut identity = self.clone();
323 identity.max_apdu_length = identity.max_apdu_length.min(local_receive_capacity);
324 identity.validate()?;
325 Ok(identity)
326 }
327
328 pub fn validate(&self) -> Result<(), Error> {
330 ObjectIdentifier::new(ObjectType::DEVICE, self.instance)?;
331 if self.max_apdu_length < 50 {
332 return Err(Error::Encoding(
333 "Device max-APDU must be at least 50".into(),
334 ));
335 }
336 if self.segmentation_supported.to_raw() > Segmentation::NONE.to_raw() {
337 return Err(Error::Encoding(
338 "invalid Device segmentation support".into(),
339 ));
340 }
341 if self.segmentation_supported != Segmentation::NONE && self.apdu_segment_timeout_ms == 0 {
342 return Err(Error::Encoding(
343 "APDU_Segment_Timeout must be positive when segmentation is supported".into(),
344 ));
345 }
346 Ok(())
347 }
348
349 pub fn validate_device(&self, device: &dyn BACnetObject) -> Result<(), Error> {
351 self.validate()?;
352 if device.object_identifier() != self.device_oid() {
353 return Err(Error::Encoding(
354 "selected Device identifier must match identity".into(),
355 ));
356 }
357 for (property, expected) in [
358 (
359 P::OBJECT_IDENTIFIER,
360 PropertyValue::ObjectIdentifier(self.device_oid()),
361 ),
362 (
363 P::VENDOR_IDENTIFIER,
364 PropertyValue::Unsigned(u64::from(self.vendor_id)),
365 ),
366 (
367 P::MAX_APDU_LENGTH_ACCEPTED,
368 PropertyValue::Unsigned(u64::from(self.max_apdu_length)),
369 ),
370 (
371 P::SEGMENTATION_SUPPORTED,
372 PropertyValue::Enumerated(u32::from(self.segmentation_supported.to_raw())),
373 ),
374 ] {
375 if device.read_property(property, None)? != expected {
376 return Err(Error::Encoding(format!(
377 "selected Device {property:?} must match identity"
378 )));
379 }
380 }
381 if self.segmentation_supported != Segmentation::NONE
382 && device.read_property(P::APDU_SEGMENT_TIMEOUT, None)?
383 != PropertyValue::Unsigned(self.apdu_segment_timeout_ms)
384 {
385 return Err(Error::Encoding(
386 "selected Device APDU_Segment_Timeout must match identity".into(),
387 ));
388 }
389 Ok(())
390 }
391
392 pub fn segmentation(&self) -> Segmentation {
394 self.segmentation_supported
395 }
396
397 pub fn services(&self) -> &[ServiceSupported] {
399 &self.services
400 }
401
402 pub fn network_ports(&self) -> &[NetworkPortEntry] {
404 &self.network_ports
405 }
406
407 pub fn device_uuid(&self) -> [u8; 16] {
409 self.device_uuid
410 }
411
412 pub fn device_oid(&self) -> ObjectIdentifier {
414 ObjectIdentifier::new(ObjectType::DEVICE, self.instance)
415 .expect("identity instance was validated at construction")
416 }
417
418 pub fn build_database(&self) -> Result<ObjectDatabase, Error> {
424 let mut device = DeviceObject::new(DeviceConfig {
425 identity: self.clone(),
426 firmware_revision: env!("CARGO_PKG_VERSION").into(),
427 application_software_version: env!("CARGO_PKG_VERSION").into(),
428 ..DeviceConfig::default()
429 })?;
430 device.set_services_supported(&self.services);
431 device.set_device_uuid(self.device_uuid);
432 let device_oid = device.object_identifier();
433
434 let mut oids = vec![device_oid];
435 let mut ports = Vec::new();
436 for entry in &self.network_ports {
437 let port = entry.build_object()?;
438 oids.push(port.object_identifier());
439 ports.push(port);
440 }
441 device.set_object_list(oids);
442
443 let mut db = ObjectDatabase::new();
444 db.add(Box::new(device))?;
445 for port in ports {
446 db.add(Box::new(port))?;
447 }
448 Ok(db)
449 }
450}
451
452pub fn build_database_with_extra(
472 identity: &DeviceIdentity,
473 extra: Vec<Box<dyn crate::traits::BACnetObject>>,
474) -> Result<ObjectDatabase, Error> {
475 let mut device = DeviceObject::new(DeviceConfig {
476 identity: identity.clone(),
477 firmware_revision: env!("CARGO_PKG_VERSION").into(),
478 application_software_version: env!("CARGO_PKG_VERSION").into(),
479 ..DeviceConfig::default()
480 })?;
481 device.set_services_supported(&identity.services);
482 device.set_device_uuid(identity.device_uuid);
483 let device_oid = device.object_identifier();
484
485 let mut oids = vec![device_oid];
486 let mut ports = Vec::new();
487 for entry in &identity.network_ports {
488 let port = entry.build_object()?;
489 oids.push(port.object_identifier());
490 ports.push(port);
491 }
492 for obj in &extra {
493 oids.push(obj.object_identifier());
494 }
495 device.set_object_list(oids);
496
497 let mut db = ObjectDatabase::new();
498 db.add(Box::new(device))?;
499 for port in ports {
500 db.add(Box::new(port))?;
501 }
502 for obj in extra {
503 db.add(obj)?;
504 }
505 Ok(db)
506}
507
508impl Default for DeviceIdentity {
509 fn default() -> Self {
510 Self::new(1, 0)
511 .expect("default Device instance is valid")
512 .with_name("BACnet Device")
513 }
514}