1#[cfg(feature = "std")]
164use std::error::Error;
165
166#[cfg(feature = "std")]
167use std::fmt;
168
169#[cfg(not(feature = "std"))]
170use core::fmt;
171
172#[cfg(not(feature = "std"))]
173use alloc::{format, string::String, vec::Vec};
174
175#[cfg(feature = "std")]
177pub type Result<T> = std::result::Result<T, ServiceError>;
178
179#[cfg(not(feature = "std"))]
180pub type Result<T> = core::result::Result<T, ServiceError>;
181
182#[derive(Debug)]
184pub enum ServiceError {
185 UnsupportedService,
187 InvalidParameters(String),
189 Timeout,
191 Rejected(RejectReason),
193 Aborted(AbortReason),
195 EncodingError(String),
197 UnsupportedServiceChoice(u8),
199}
200
201impl fmt::Display for ServiceError {
202 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203 match self {
204 ServiceError::UnsupportedService => write!(f, "Service not supported"),
205 ServiceError::InvalidParameters(msg) => write!(f, "Invalid parameters: {}", msg),
206 ServiceError::Timeout => write!(f, "Service timeout"),
207 ServiceError::Rejected(reason) => write!(f, "Service rejected: {:?}", reason),
208 ServiceError::Aborted(reason) => write!(f, "Service aborted: {:?}", reason),
209 ServiceError::EncodingError(msg) => write!(f, "Encoding error: {}", msg),
210 ServiceError::UnsupportedServiceChoice(choice) => {
211 write!(f, "Unsupported service choice: {}", choice)
212 }
213 }
214 }
215}
216
217#[cfg(feature = "std")]
218impl Error for ServiceError {}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222#[repr(u8)]
223pub enum ConfirmedServiceChoice {
224 AcknowledgeAlarm = 0,
226 ConfirmedEventNotification = 2,
227 GetAlarmSummary = 3,
228 GetEnrollmentSummary = 4,
229 GetEventInformation = 29,
230
231 AtomicReadFile = 6,
233 AtomicWriteFile = 7,
234
235 AddListElement = 8,
237 RemoveListElement = 9,
238 CreateObject = 10,
239 DeleteObject = 11,
240 ReadProperty = 12,
241 ReadPropertyMultiple = 14,
242 WriteProperty = 15,
243 WritePropertyMultiple = 16,
244
245 DeviceCommunicationControl = 17,
247 ReinitializeDevice = 20,
248
249 VtOpen = 21,
251 VtClose = 22,
252 VtData = 23,
253
254 Authenticate = 24,
256 RequestKey = 25,
257
258 ReadRange = 26,
260 SubscribeCOV = 5,
261 SubscribeCOVProperty = 28,
262
263 AuthRequest = 34,
265}
266
267impl TryFrom<u8> for ConfirmedServiceChoice {
268 type Error = ServiceError;
269
270 fn try_from(value: u8) -> Result<Self> {
271 match value {
272 0 => Ok(Self::AcknowledgeAlarm),
273 2 => Ok(Self::ConfirmedEventNotification),
274 3 => Ok(Self::GetAlarmSummary),
275 4 => Ok(Self::GetEnrollmentSummary),
276 29 => Ok(Self::GetEventInformation),
277 6 => Ok(Self::AtomicReadFile),
278 7 => Ok(Self::AtomicWriteFile),
279 8 => Ok(Self::AddListElement),
280 9 => Ok(Self::RemoveListElement),
281 10 => Ok(Self::CreateObject),
282 11 => Ok(Self::DeleteObject),
283 12 => Ok(Self::ReadProperty),
284 14 => Ok(Self::ReadPropertyMultiple),
285 15 => Ok(Self::WriteProperty),
286 16 => Ok(Self::WritePropertyMultiple),
287 17 => Ok(Self::DeviceCommunicationControl),
288 20 => Ok(Self::ReinitializeDevice),
289 21 => Ok(Self::VtOpen),
290 22 => Ok(Self::VtClose),
291 23 => Ok(Self::VtData),
292 24 => Ok(Self::Authenticate),
293 25 => Ok(Self::RequestKey),
294 26 => Ok(Self::ReadRange),
295 5 => Ok(Self::SubscribeCOV),
296 28 => Ok(Self::SubscribeCOVProperty),
297 34 => Ok(Self::AuthRequest),
298 _ => Err(ServiceError::UnsupportedServiceChoice(value)),
299 }
300 }
301}
302
303#[derive(Debug, Clone, Copy, PartialEq, Eq)]
304#[repr(u8)]
305pub enum UnconfirmedServiceChoice {
306 IAm = 0,
307 IHave = 1,
308 UnconfirmedCOVNotification = 2,
309 UnconfirmedEventNotification = 3,
310 UnconfirmedPrivateTransfer = 4,
311 UnconfirmedTextMessage = 5,
312 TimeSynchronization = 6,
313 WhoHas = 7,
314 WhoIs = 8,
315 UtcTimeSynchronization = 9,
316 WriteGroup = 10,
317 UnconfirmedCOVNotificationMultiple = 11,
318 UnconfirmedAuditNotification = 12,
319 WhoAmI = 13,
320 YouAre = 14,
321}
322
323impl TryFrom<u8> for UnconfirmedServiceChoice {
324 type Error = ServiceError;
325
326 fn try_from(value: u8) -> Result<Self> {
327 match value {
328 0 => Ok(Self::IAm),
329 1 => Ok(Self::IHave),
330 2 => Ok(Self::UnconfirmedCOVNotification),
331 3 => Ok(Self::UnconfirmedEventNotification),
332 4 => Ok(Self::UnconfirmedPrivateTransfer),
333 5 => Ok(Self::UnconfirmedTextMessage),
334 6 => Ok(Self::TimeSynchronization),
335 7 => Ok(Self::WhoHas),
336 8 => Ok(Self::WhoIs),
337 9 => Ok(Self::UtcTimeSynchronization),
338 10 => Ok(Self::WriteGroup),
339 11 => Ok(Self::UnconfirmedCOVNotificationMultiple),
340 12 => Ok(Self::UnconfirmedAuditNotification),
341 13 => Ok(Self::WhoAmI),
342 14 => Ok(Self::YouAre),
343 _ => Err(ServiceError::UnsupportedServiceChoice(value)),
344 }
345 }
346}
347
348generate_custom_enum!(
349 RejectReason{
351 Other = 0,
352 BufferOverflow = 1,
353 InconsistentParameters = 2,
354 InvalidParameterDataType = 3,
355 InvalidTag = 4,
356 MissingRequiredParameter = 5,
357 ParameterOutOfRange = 6,
358 TooManyArguments = 7,
359 UndefinedEnumeration = 8,
360 UnrecognizedService = 9,
361 InvalidDataEncoding = 10,
362}, u8, 64..=255);
363
364generate_custom_enum!(
365 AbortReason{
371 Other = 0,
372 BufferOverflow = 1,
373 InvalidApduInThisState = 2,
374 PreemptedByHigherPriorityTask = 3,
375 SegmentationNotSupported = 4,
376 SecurityError = 5,
377 InsufficientSecurity = 6,
378 WindowSizeOutOfRange = 7,
379 ApplicationExceededReplyTime = 8,
380 OutOfResources = 9,
381 TsmTimeout = 10,
382 ApduTooLong = 11,
383}, u8, 64..=255);
384
385use crate::encoding::{
386 decode_context_enumerated, decode_context_object_id, decode_context_tag,
387 decode_context_unsigned, decode_enumerated, decode_object_identifier, decode_tag,
388 decode_unsigned, encode_context_enumerated, encode_context_object_id, encode_context_unsigned,
389 encode_enumerated, encode_object_identifier, encode_unsigned, BACnetTag,
390 Result as EncodingResult,
391};
392use crate::object::{
393 ObjectError, ObjectIdentifier, PropertyIdentifier, PropertyValue, Segmentation,
394};
395use crate::property::{self, decode_property_value, encode_property_value};
396use crate::{generate_custom_enum, EncodingError};
397
398pub const BACNET_ARRAY_ALL: u32 = 0xFFFFFFFF;
400
401#[derive(Debug, Clone, PartialEq, Eq, Default)]
403pub struct WhoIsRequest {
404 pub device_instance_range_low_limit: Option<u32>,
406 pub device_instance_range_high_limit: Option<u32>,
408}
409
410impl WhoIsRequest {
411 pub fn new() -> Self {
413 Self {
414 device_instance_range_low_limit: None,
415 device_instance_range_high_limit: None,
416 }
417 }
418
419 pub fn for_device(device_instance: u32) -> Self {
421 Self {
422 device_instance_range_low_limit: Some(device_instance),
423 device_instance_range_high_limit: Some(device_instance),
424 }
425 }
426
427 pub fn for_range(low: u32, high: u32) -> Self {
429 Self {
430 device_instance_range_low_limit: Some(low),
431 device_instance_range_high_limit: Some(high),
432 }
433 }
434
435 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
437 if let (Some(low), Some(high)) = (
440 self.device_instance_range_low_limit,
441 self.device_instance_range_high_limit,
442 ) {
443 let low_bytes = encode_context_unsigned(low, 0)?;
445 buffer.extend_from_slice(&low_bytes);
446
447 let high_bytes = encode_context_unsigned(high, 1)?;
449 buffer.extend_from_slice(&high_bytes);
450 }
451 Ok(())
454 }
455
456 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
458 let mut request = WhoIsRequest::new();
459 let mut pos = 0;
460
461 if pos < data.len() {
463 match decode_context_unsigned(&data[pos..], 0) {
464 Ok((low, consumed)) => {
465 request.device_instance_range_low_limit = Some(low);
466 pos += consumed;
467
468 if pos < data.len() {
470 match decode_context_unsigned(&data[pos..], 1) {
471 Ok((high, _consumed)) => {
472 request.device_instance_range_high_limit = Some(high);
473 }
474 Err(_) => {
475 return Err(crate::encoding::EncodingError::InvalidFormat(
477 "Who-Is request has low limit without high limit".to_string(),
478 ));
479 }
480 }
481 }
482 }
483 Err(_) => {
484 }
486 }
487 }
488
489 Ok(request)
490 }
491
492 pub fn matches(&self, device_instance: u32) -> bool {
494 match (
495 self.device_instance_range_low_limit,
496 self.device_instance_range_high_limit,
497 ) {
498 (None, None) => true, (Some(low), Some(high)) => device_instance >= low && device_instance <= high,
500 (Some(low), None) => device_instance >= low,
501 (None, Some(high)) => device_instance <= high,
502 }
503 }
504}
505
506#[derive(Debug, Clone, PartialEq, Eq)]
508pub struct IAmRequest {
509 pub device_identifier: ObjectIdentifier,
511 pub max_apdu_length_accepted: u32,
513 pub segmentation_supported: Segmentation,
515 pub vendor_identifier: u16,
517}
518
519impl IAmRequest {
520 pub fn new(
522 device_identifier: ObjectIdentifier,
523 max_apdu_length_accepted: u32,
524 segmentation_supported: Segmentation,
525 vendor_identifier: u16,
526 ) -> Self {
527 Self {
528 device_identifier,
529 max_apdu_length_accepted,
530 segmentation_supported,
531 vendor_identifier,
532 }
533 }
534
535 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
537 encode_object_identifier(buffer, self.device_identifier)?;
539
540 encode_unsigned(buffer, self.max_apdu_length_accepted)?;
542
543 encode_enumerated(buffer, self.segmentation_supported as u32);
545
546 encode_unsigned(buffer, self.vendor_identifier as u32)?;
548
549 Ok(())
550 }
551
552 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
554 let mut pos = 0;
555
556 let (device_identifier, consumed) = decode_object_identifier(&data[pos..])?;
558 pos += consumed;
559
560 let (max_apdu_length_accepted, consumed) = decode_unsigned(&data[pos..])?;
562 pos += consumed;
563
564 let (segmentation_supported, consumed) = decode_enumerated(&data[pos..])?;
566 pos += consumed;
567
568 let (vendor_identifier, _consumed) = decode_unsigned(&data[pos..])?;
570
571 Ok(IAmRequest::new(
572 device_identifier,
573 max_apdu_length_accepted,
574 segmentation_supported
575 .try_into()
576 .map_err(|e: ObjectError| EncodingError::InvalidFormat(e.to_string()))?,
577 vendor_identifier as u16,
578 ))
579 }
580}
581
582#[derive(Debug, Clone, PartialEq, Eq)]
584pub struct ReadPropertyRequest {
585 pub object_identifier: ObjectIdentifier,
587 pub property_identifier: PropertyIdentifier,
589 pub property_array_index: Option<u32>,
591}
592
593impl ReadPropertyRequest {
594 pub fn new(
596 object_identifier: ObjectIdentifier,
597 property_identifier: PropertyIdentifier,
598 ) -> Self {
599 Self {
600 object_identifier,
601 property_identifier,
602 property_array_index: None,
603 }
604 }
605
606 pub fn with_array_index(
608 object_identifier: ObjectIdentifier,
609 property_identifier: PropertyIdentifier,
610 array_index: u32,
611 ) -> Self {
612 Self {
613 object_identifier,
614 property_identifier,
615 property_array_index: Some(array_index),
616 }
617 }
618
619 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
621 let obj_id_bytes = encode_context_object_id(self.object_identifier, 0)?;
623 buffer.extend_from_slice(&obj_id_bytes);
624
625 let prop_id_bytes = encode_context_enumerated(self.property_identifier.into(), 1)?;
627 buffer.extend_from_slice(&prop_id_bytes);
628
629 if let Some(array_index) = self.property_array_index {
631 let array_bytes = encode_context_unsigned(array_index, 2)?;
632 buffer.extend_from_slice(&array_bytes);
633 }
634
635 Ok(())
636 }
637
638 pub fn decode(buffer: &[u8]) -> EncodingResult<Self> {
639 let mut offset = 0;
640 let (object_identifier, consumed) = decode_context_object_id(&buffer[offset..], 0)?;
641 offset += consumed;
642 let (property_identifier, consumed) = decode_context_enumerated(&buffer[offset..], 1)?;
643 offset += consumed;
644
645 let property_array_index = if offset < buffer.len() {
646 let (array_index, _) = decode_context_unsigned(&buffer[offset..], 2)?;
647 Some(array_index)
648 } else {
649 None
650 };
651
652 Ok(Self {
653 object_identifier,
654 property_identifier: property_identifier.into(),
655 property_array_index,
656 })
657 }
658}
659
660#[derive(Debug, Clone)]
662pub struct ReadPropertyResponse {
663 pub object_identifier: ObjectIdentifier,
665 pub property_identifier: PropertyIdentifier,
667 pub property_array_index: Option<u32>,
669 pub property_values: Vec<property::PropertyValue>, }
672
673impl ReadPropertyResponse {
674 pub fn new(
676 object_identifier: ObjectIdentifier,
677 property_identifier: PropertyIdentifier,
678 property_values: Vec<property::PropertyValue>,
679 ) -> Self {
680 Self {
681 object_identifier,
682 property_identifier,
683 property_array_index: None,
684 property_values,
685 }
686 }
687
688 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
690 let mut pos = 0;
691
692 let (object_identifier, consumed) = decode_context_object_id(&data[pos..], 0)?;
694 pos += consumed;
695
696 let (property_identifier, consumed) = decode_context_enumerated(&data[pos..], 1)?;
698 pos += consumed;
699
700 let property_array_index = match decode_context_unsigned(&data[pos..], 2) {
702 Ok((array_index, consumed)) => {
703 pos += consumed;
704 if array_index == BACNET_ARRAY_ALL {
705 None
706 } else {
707 Some(array_index)
708 }
709 }
710 Err(_) => None,
711 };
712
713 let (tag, _, consumed) = decode_tag(&data[pos..])?;
714 pos += consumed;
715
716 let property_values = if let BACnetTag::Context(3) = tag {
717 let (tag, _, _) = decode_tag(&data[pos..])?;
718 let mut current_tag = tag;
719 let mut values = Vec::new();
720
721 while let BACnetTag::Application(_) = current_tag {
722 let (value, consumed) = decode_property_value(&data[pos..])?;
723 values.push(value);
724 pos += consumed;
725 let (tag, _, _) = decode_tag(&data[pos..])?;
726 current_tag = tag;
727 }
728 values
729 } else {
730 return Err(EncodingError::InvalidTag);
731 };
732
733 let (tag, _, _) = decode_tag(&data[pos..])?;
734
735 if let BACnetTag::Context(tag) = tag {
736 if tag != 3 {
737 return Err(EncodingError::InvalidTag);
738 }
739 }
740
741 Ok(ReadPropertyResponse {
742 object_identifier,
743 property_identifier: property_identifier.into(),
744 property_array_index,
745 property_values,
746 })
747 }
748
749 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
750 let object_id = encode_context_object_id(self.object_identifier, 0)?;
751 buffer.extend_from_slice(&object_id);
752
753 let prop_id = encode_context_enumerated(self.property_identifier.into(), 1)?;
754 buffer.extend_from_slice(&prop_id);
755
756 if let Some(array_index) = self.property_array_index {
758 let array_bytes = encode_context_unsigned(array_index, 2)?;
759 buffer.extend_from_slice(&array_bytes);
760 }
761
762 buffer.push(0x3E); for property_value in &self.property_values {
765 encode_property_value(property_value, buffer)?;
766 }
767 buffer.push(0x3F); Ok(())
770 }
771}
772
773#[derive(Debug, Clone)]
775pub struct WritePropertyRequest {
776 pub object_identifier: ObjectIdentifier,
778 pub property_identifier: u32,
780 pub property_array_index: Option<u32>,
782 pub property_value: Vec<u8>, pub priority: Option<u8>,
786}
787
788impl WritePropertyRequest {
789 pub fn new(
791 object_identifier: ObjectIdentifier,
792 property_identifier: u32,
793 property_value: Vec<u8>,
794 ) -> Self {
795 Self {
796 object_identifier,
797 property_identifier,
798 property_array_index: None,
799 property_value,
800 priority: None,
801 }
802 }
803
804 pub fn with_priority(
806 object_identifier: ObjectIdentifier,
807 property_identifier: u32,
808 property_value: Vec<u8>,
809 priority: u8,
810 ) -> Self {
811 Self {
812 object_identifier,
813 property_identifier,
814 property_array_index: None,
815 property_value,
816 priority: Some(priority),
817 }
818 }
819
820 pub fn with_array_index(
822 object_identifier: ObjectIdentifier,
823 property_identifier: u32,
824 array_index: u32,
825 property_value: Vec<u8>,
826 ) -> Self {
827 Self {
828 object_identifier,
829 property_identifier,
830 property_array_index: Some(array_index),
831 property_value,
832 priority: None,
833 }
834 }
835
836 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
838 let object_id: u32 = self.object_identifier.try_into()?;
840 buffer.push(0x0C); buffer.extend_from_slice(&object_id.to_be_bytes());
842
843 buffer.push(0x19); buffer.push(self.property_identifier as u8);
846
847 if let Some(array_index) = self.property_array_index {
849 buffer.push(0x29); buffer.push(array_index as u8);
851 }
852
853 buffer.push(0x3E); buffer.extend_from_slice(&self.property_value);
856 buffer.push(0x3F); if let Some(priority) = self.priority {
860 buffer.push(0x49); buffer.push(priority);
862 }
863
864 Ok(())
865 }
866
867 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
869 let mut pos = 0;
870
871 if pos + 5 > data.len() || data[pos] != 0x0C {
873 return Err(crate::encoding::EncodingError::InvalidTag);
874 }
875 pos += 1;
876
877 let object_id_bytes = [data[pos], data[pos + 1], data[pos + 2], data[pos + 3]];
878 let object_id = u32::from_be_bytes(object_id_bytes);
879 let object_identifier = object_id.into();
880 pos += 4;
881
882 if pos + 2 > data.len() || data[pos] != 0x19 {
884 return Err(crate::encoding::EncodingError::InvalidTag);
885 }
886 pos += 1;
887 let property_identifier = data[pos] as u32;
888 pos += 1;
889
890 let property_array_index = if pos < data.len() && data[pos] == 0x29 {
892 pos += 1;
893 let array_index = data[pos] as u32;
894 pos += 1;
895 Some(array_index)
896 } else {
897 None
898 };
899
900 if pos >= data.len() || data[pos] != 0x3E {
902 return Err(crate::encoding::EncodingError::InvalidTag);
903 }
904 pos += 1;
905
906 let value_start = pos;
908 let mut value_end = pos;
909 while value_end < data.len() {
910 if data[value_end] == 0x3F {
911 break;
912 }
913 value_end += 1;
914 }
915
916 if value_end >= data.len() {
917 return Err(crate::encoding::EncodingError::InvalidTag);
918 }
919
920 let property_value = data[value_start..value_end].to_vec();
921 pos = value_end + 1;
922
923 let priority = if pos < data.len() && data[pos] == 0x49 {
925 pos += 1;
926 if pos < data.len() {
927 Some(data[pos])
928 } else {
929 None
930 }
931 } else {
932 None
933 };
934
935 Ok(WritePropertyRequest {
936 object_identifier,
937 property_identifier,
938 property_array_index,
939 property_value,
940 priority,
941 })
942 }
943}
944
945#[derive(Debug, Clone)]
947pub struct ReadPropertyMultipleRequest {
948 pub read_access_specifications: Vec<ReadAccessSpecification>,
950}
951
952#[derive(Debug, Clone)]
953pub struct ReadAccessSpecification {
954 pub object_identifier: ObjectIdentifier,
956 pub property_references: Vec<PropertyReference>,
958}
959
960#[derive(Debug, Clone)]
961pub struct PropertyReference {
962 pub property_identifier: PropertyIdentifier,
964 pub property_array_index: Option<u32>,
966}
967
968impl ReadPropertyMultipleRequest {
969 pub fn new(read_access_specifications: Vec<ReadAccessSpecification>) -> Self {
971 Self {
972 read_access_specifications,
973 }
974 }
975
976 pub fn add_specification(&mut self, spec: ReadAccessSpecification) {
978 self.read_access_specifications.push(spec);
979 }
980
981 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
982 for spec in &self.read_access_specifications {
983 spec.encode(buffer)?;
984 }
985
986 Ok(())
987 }
988}
989
990impl ReadAccessSpecification {
991 pub fn new(
993 object_identifier: ObjectIdentifier,
994 property_references: Vec<PropertyReference>,
995 ) -> Self {
996 Self {
997 object_identifier,
998 property_references,
999 }
1000 }
1001
1002 pub fn add_property(&mut self, property_reference: PropertyReference) {
1004 self.property_references.push(property_reference);
1005 }
1006
1007 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1008 let object_id_bytes = encode_context_object_id(self.object_identifier, 0)?;
1010 buffer.extend_from_slice(&object_id_bytes);
1011
1012 buffer.push(0x1E); for property_ref in &self.property_references {
1016 property_ref.encode(buffer)?;
1017 }
1018
1019 buffer.push(0x1F); Ok(())
1022 }
1023}
1024
1025impl PropertyReference {
1026 pub fn new(property_identifier: PropertyIdentifier) -> Self {
1028 Self {
1029 property_identifier,
1030 property_array_index: None,
1031 }
1032 }
1033
1034 pub fn with_array_index(property_identifier: PropertyIdentifier, array_index: u32) -> Self {
1036 Self {
1037 property_identifier,
1038 property_array_index: Some(array_index),
1039 }
1040 }
1041
1042 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1043 let prop_id_bytes = encode_context_enumerated(self.property_identifier.into(), 0)?;
1044 buffer.extend_from_slice(&prop_id_bytes);
1045
1046 if let Some(array_index) = self.property_array_index {
1047 let array_bytes = encode_context_unsigned(array_index, 1)?;
1048 buffer.extend_from_slice(&array_bytes);
1049 }
1050
1051 Ok(())
1052 }
1053}
1054
1055#[derive(Debug, Clone)]
1056pub struct ReadPropertyMultipleResponse {
1057 pub read_access_results: Vec<ReadAccessResult>,
1058}
1059
1060impl ReadPropertyMultipleResponse {
1061 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
1062 let mut read_access_results = Vec::new();
1063
1064 let mut total_consumed = 0;
1065
1066 while total_consumed < data.len() {
1067 let (result, consumed) = ReadAccessResult::decode(&data[total_consumed..])?;
1068 total_consumed += consumed;
1069 read_access_results.push(result);
1070 }
1071
1072 Ok(Self {
1073 read_access_results,
1074 })
1075 }
1076}
1077
1078#[derive(Debug, Clone)]
1079pub struct ReadAccessResult {
1080 pub object_identifier: ObjectIdentifier,
1081 pub results: Vec<PropertyResult>,
1082}
1083
1084impl ReadAccessResult {
1085 pub fn decode(data: &[u8]) -> EncodingResult<(Self, usize)> {
1086 let mut total_consumed = 0;
1087 let mut results = Vec::new();
1088 let (object_id, consumed) = decode_context_object_id(data, 0)?;
1089 total_consumed += consumed;
1090
1091 let (context_id, context_size, consumed) = decode_context_tag(&data[total_consumed..])?;
1092 total_consumed += consumed;
1093
1094 if context_id == 1 && context_size == 6 {
1095 let (mut context_id, _, _) = decode_context_tag(&data[total_consumed..])?;
1096
1097 while context_id != 1 {
1098 let (result, consumed) = PropertyResult::decode(&data[total_consumed..])?;
1099 total_consumed += consumed;
1100 results.push(result);
1101
1102 let (id, _, _) = decode_context_tag(&data[total_consumed..])?;
1103 context_id = id;
1104 }
1105
1106 let (_, _, consumed) = decode_context_tag(&data[total_consumed..])?;
1107 total_consumed += consumed;
1108
1109 if context_id != 1 {
1110 return Err(EncodingError::InvalidTag);
1111 }
1112 } else {
1113 return Err(EncodingError::InvalidTag);
1114 }
1115
1116 Ok((
1117 Self {
1118 object_identifier: object_id,
1119 results,
1120 },
1121 total_consumed,
1122 ))
1123 }
1124}
1125
1126#[derive(Debug, Clone)]
1127pub struct PropertyResult {
1128 pub property_identifier: PropertyIdentifier,
1129 pub array_index: Option<u32>,
1130 pub value: PropertyResultValue,
1131}
1132
1133impl PropertyResult {
1134 pub fn decode(bytes: &[u8]) -> EncodingResult<(Self, usize)> {
1135 let (property_identifier, consumed) = decode_context_enumerated(bytes, 2)?;
1136 let mut total_consumed = consumed;
1137
1138 let (tag, _, _) = decode_tag(&bytes[total_consumed..])?;
1139
1140 let array_index = if let BACnetTag::Context(3) = tag {
1141 let (index, consumed) = decode_context_unsigned(&bytes[total_consumed..], 3)?;
1142 total_consumed += consumed;
1143 Some(index)
1144 } else {
1145 None
1146 };
1147
1148 let (tag, _, consumed) = decode_tag(&bytes[total_consumed..])?;
1149 total_consumed += consumed;
1150
1151 let value = if let BACnetTag::Context(4) = tag {
1152 let (tag, _, _) = decode_tag(&bytes[total_consumed..])?;
1153 let mut current_tag = tag;
1154 let mut values = Vec::new();
1155
1156 while let BACnetTag::Application(_) = current_tag {
1157 let (value, consumed) = decode_property_value(&bytes[total_consumed..])?;
1158 values.push(value);
1159 total_consumed += consumed;
1160 let (tag, _, _) = decode_tag(&bytes[total_consumed..])?;
1161 current_tag = tag;
1162 }
1163 PropertyResultValue::Value(values)
1164 } else if let BACnetTag::Context(5) = tag {
1165 let (error_class, consumed) = decode_enumerated(&bytes[total_consumed..])?;
1166 total_consumed += consumed;
1167 let (error_code, consumed) = decode_enumerated(&bytes[total_consumed..])?;
1168 total_consumed += consumed;
1169 PropertyResultValue::Error(error_class, error_code)
1170 } else {
1171 return Err(EncodingError::InvalidTag);
1172 };
1173
1174 let (tag, _, consumed) = decode_tag(&bytes[total_consumed..])?;
1175 total_consumed += consumed;
1176
1177 if let BACnetTag::Context(tag) = tag {
1178 if tag != 4 && tag != 5 {
1179 return Err(EncodingError::InvalidTag);
1180 }
1181 } else {
1182 return Err(EncodingError::InvalidTag);
1183 }
1184
1185 Ok((
1186 Self {
1187 property_identifier: property_identifier.into(),
1188 array_index,
1189 value,
1190 },
1191 total_consumed,
1192 ))
1193 }
1194}
1195
1196#[derive(Debug, Clone, PartialEq)]
1197pub enum PropertyResultValue {
1198 Value(Vec<property::PropertyValue>),
1199 Error(u32, u32),
1200}
1201
1202#[derive(Debug, Clone)]
1204pub struct SubscribeCovRequest {
1205 pub subscriber_process_identifier: u32,
1207 pub monitored_object_identifier: ObjectIdentifier,
1209 pub issue_confirmed_notifications: Option<bool>,
1211 pub lifetime: Option<u32>,
1213}
1214
1215impl SubscribeCovRequest {
1216 pub fn new(
1218 subscriber_process_identifier: u32,
1219 monitored_object_identifier: ObjectIdentifier,
1220 ) -> Self {
1221 Self {
1222 subscriber_process_identifier,
1223 monitored_object_identifier,
1224 issue_confirmed_notifications: None,
1225 lifetime: None,
1226 }
1227 }
1228
1229 pub fn with_confirmation(
1231 subscriber_process_identifier: u32,
1232 monitored_object_identifier: ObjectIdentifier,
1233 issue_confirmed_notifications: bool,
1234 ) -> Self {
1235 Self {
1236 subscriber_process_identifier,
1237 monitored_object_identifier,
1238 issue_confirmed_notifications: Some(issue_confirmed_notifications),
1239 lifetime: None,
1240 }
1241 }
1242
1243 pub fn with_lifetime(
1245 subscriber_process_identifier: u32,
1246 monitored_object_identifier: ObjectIdentifier,
1247 lifetime: u32,
1248 ) -> Self {
1249 Self {
1250 subscriber_process_identifier,
1251 monitored_object_identifier,
1252 issue_confirmed_notifications: None,
1253 lifetime: Some(lifetime),
1254 }
1255 }
1256
1257 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1259 buffer.push(0x09); buffer.push(self.subscriber_process_identifier as u8);
1262
1263 let object_id: u32 = self.monitored_object_identifier.try_into()?;
1265 buffer.push(0x1C); buffer.extend_from_slice(&object_id.to_be_bytes());
1267
1268 if let Some(confirmed) = self.issue_confirmed_notifications {
1270 buffer.push(0x22); buffer.push(if confirmed { 1 } else { 0 });
1272 }
1273
1274 if let Some(lifetime) = self.lifetime {
1276 buffer.push(0x39); buffer.push(lifetime as u8);
1278 }
1279
1280 Ok(())
1281 }
1282}
1283
1284#[derive(Debug, Clone)]
1286pub struct SubscribeCovPropertyRequest {
1287 pub subscriber_process_identifier: u32,
1289 pub monitored_object_identifier: ObjectIdentifier,
1291 pub issue_confirmed_notifications: Option<bool>,
1293 pub lifetime: Option<u32>,
1295 pub monitored_property: PropertyReference,
1297 pub cov_increment: Option<f32>,
1299}
1300
1301impl SubscribeCovPropertyRequest {
1302 pub fn new(
1304 subscriber_process_identifier: u32,
1305 monitored_object_identifier: ObjectIdentifier,
1306 monitored_property: PropertyReference,
1307 ) -> Self {
1308 Self {
1309 subscriber_process_identifier,
1310 monitored_object_identifier,
1311 issue_confirmed_notifications: None,
1312 lifetime: None,
1313 monitored_property,
1314 cov_increment: None,
1315 }
1316 }
1317
1318 pub fn with_cov_increment(mut self, increment: f32) -> Self {
1320 self.cov_increment = Some(increment);
1321 self
1322 }
1323}
1324
1325#[derive(Debug, Clone)]
1327pub struct CovNotificationRequest {
1328 pub subscriber_process_identifier: u32,
1330 pub initiating_device_identifier: ObjectIdentifier,
1332 pub monitored_object_identifier: ObjectIdentifier,
1334 pub time_remaining: u32,
1336 pub list_of_values: Vec<PropertyValue>,
1338}
1339
1340impl CovNotificationRequest {
1341 pub fn new(
1343 subscriber_process_identifier: u32,
1344 initiating_device_identifier: ObjectIdentifier,
1345 monitored_object_identifier: ObjectIdentifier,
1346 time_remaining: u32,
1347 list_of_values: Vec<PropertyValue>,
1348 ) -> Self {
1349 Self {
1350 subscriber_process_identifier,
1351 initiating_device_identifier,
1352 monitored_object_identifier,
1353 time_remaining,
1354 list_of_values,
1355 }
1356 }
1357
1358 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1360 buffer.push(0x09); buffer.push(self.subscriber_process_identifier as u8);
1363
1364 let device_id: u32 = self.initiating_device_identifier.try_into()?;
1366 buffer.push(0x1C); buffer.extend_from_slice(&device_id.to_be_bytes());
1368
1369 let object_id: u32 = self.monitored_object_identifier.try_into()?;
1371 buffer.push(0x2C); buffer.extend_from_slice(&object_id.to_be_bytes());
1373
1374 buffer.push(0x39); buffer.push(self.time_remaining as u8);
1377
1378 Ok(())
1382 }
1383}
1384
1385#[derive(Debug, Clone)]
1387pub struct CovSubscription {
1388 pub subscriber_process_identifier: u32,
1390 pub subscriber_device_identifier: ObjectIdentifier,
1392 pub monitored_object_identifier: ObjectIdentifier,
1394 pub monitored_property: Option<PropertyReference>,
1396 pub issue_confirmed_notifications: bool,
1398 pub lifetime: u32,
1400 pub time_remaining: u32,
1402 pub cov_increment: Option<f32>,
1404}
1405
1406impl CovSubscription {
1407 pub fn new(
1409 subscriber_process_identifier: u32,
1410 subscriber_device_identifier: ObjectIdentifier,
1411 monitored_object_identifier: ObjectIdentifier,
1412 lifetime: u32,
1413 ) -> Self {
1414 Self {
1415 subscriber_process_identifier,
1416 subscriber_device_identifier,
1417 monitored_object_identifier,
1418 monitored_property: None,
1419 issue_confirmed_notifications: false,
1420 lifetime,
1421 time_remaining: lifetime,
1422 cov_increment: None,
1423 }
1424 }
1425
1426 pub fn is_expired(&self) -> bool {
1428 self.lifetime > 0 && self.time_remaining == 0
1429 }
1430
1431 pub fn update_time(&mut self, elapsed_seconds: u32) {
1433 if self.lifetime > 0 {
1434 self.time_remaining = self.time_remaining.saturating_sub(elapsed_seconds);
1435 }
1436 }
1437}
1438
1439#[derive(Debug, Default)]
1441pub struct CovSubscriptionManager {
1442 subscriptions: Vec<CovSubscription>,
1444}
1445
1446impl CovSubscriptionManager {
1447 pub fn new() -> Self {
1449 Self {
1450 subscriptions: Vec::new(),
1451 }
1452 }
1453
1454 pub fn add_subscription(&mut self, subscription: CovSubscription) {
1456 self.subscriptions.retain(|s| {
1458 !(s.subscriber_device_identifier == subscription.subscriber_device_identifier
1459 && s.subscriber_process_identifier == subscription.subscriber_process_identifier
1460 && s.monitored_object_identifier == subscription.monitored_object_identifier)
1461 });
1462
1463 self.subscriptions.push(subscription);
1464 }
1465
1466 pub fn remove_subscription(
1468 &mut self,
1469 subscriber_device: ObjectIdentifier,
1470 subscriber_process: u32,
1471 monitored_object: ObjectIdentifier,
1472 ) {
1473 self.subscriptions.retain(|s| {
1474 !(s.subscriber_device_identifier == subscriber_device
1475 && s.subscriber_process_identifier == subscriber_process
1476 && s.monitored_object_identifier == monitored_object)
1477 });
1478 }
1479
1480 pub fn get_subscriptions_for_object(
1482 &self,
1483 object_id: ObjectIdentifier,
1484 ) -> Vec<&CovSubscription> {
1485 self.subscriptions
1486 .iter()
1487 .filter(|s| s.monitored_object_identifier == object_id && !s.is_expired())
1488 .collect()
1489 }
1490
1491 pub fn cleanup_expired(&mut self) {
1493 self.subscriptions.retain(|s| !s.is_expired());
1494 }
1495
1496 pub fn update_timers(&mut self, elapsed_seconds: u32) {
1498 for subscription in &mut self.subscriptions {
1499 subscription.update_time(elapsed_seconds);
1500 }
1501 }
1502
1503 pub fn active_count(&self) -> usize {
1505 self.subscriptions
1506 .iter()
1507 .filter(|s| !s.is_expired())
1508 .count()
1509 }
1510}
1511
1512#[derive(Debug, Clone)]
1514pub struct AtomicReadFileRequest {
1515 pub file_identifier: ObjectIdentifier,
1517 pub access_method: FileAccessMethod,
1519}
1520
1521#[derive(Debug, Clone)]
1523pub enum FileAccessMethod {
1524 StreamAccess {
1526 file_start_position: i32,
1528 requested_octet_count: u32,
1530 },
1531 RecordAccess {
1533 file_start_record: i32,
1535 requested_record_count: u32,
1537 },
1538}
1539
1540impl AtomicReadFileRequest {
1541 pub fn new_stream_access(
1543 file_identifier: ObjectIdentifier,
1544 start_position: i32,
1545 octet_count: u32,
1546 ) -> Self {
1547 Self {
1548 file_identifier,
1549 access_method: FileAccessMethod::StreamAccess {
1550 file_start_position: start_position,
1551 requested_octet_count: octet_count,
1552 },
1553 }
1554 }
1555
1556 pub fn new_record_access(
1558 file_identifier: ObjectIdentifier,
1559 start_record: i32,
1560 record_count: u32,
1561 ) -> Self {
1562 Self {
1563 file_identifier,
1564 access_method: FileAccessMethod::RecordAccess {
1565 file_start_record: start_record,
1566 requested_record_count: record_count,
1567 },
1568 }
1569 }
1570
1571 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1573 let file_id: u32 = self.file_identifier.try_into()?;
1575 buffer.push(0x0C); buffer.extend_from_slice(&file_id.to_be_bytes());
1577
1578 buffer.push(0x1E); match &self.access_method {
1582 FileAccessMethod::StreamAccess {
1583 file_start_position,
1584 requested_octet_count,
1585 } => {
1586 buffer.push(0x0E); buffer.push(0x09); buffer.extend_from_slice(&file_start_position.to_be_bytes());
1592
1593 buffer.push(0x19); buffer.extend_from_slice(&requested_octet_count.to_be_bytes());
1596
1597 buffer.push(0x0F); }
1599 FileAccessMethod::RecordAccess {
1600 file_start_record,
1601 requested_record_count,
1602 } => {
1603 buffer.push(0x1E); buffer.push(0x09); buffer.extend_from_slice(&file_start_record.to_be_bytes());
1609
1610 buffer.push(0x19); buffer.extend_from_slice(&requested_record_count.to_be_bytes());
1613
1614 buffer.push(0x1F); }
1616 }
1617
1618 buffer.push(0x1F); Ok(())
1621 }
1622}
1623
1624#[derive(Debug, Clone)]
1626pub struct AtomicReadFileResponse {
1627 pub end_of_file: bool,
1629 pub access_method_result: FileAccessMethodResult,
1631}
1632
1633#[derive(Debug, Clone)]
1635pub enum FileAccessMethodResult {
1636 StreamAccess {
1638 file_start_position: i32,
1640 file_data: Vec<u8>,
1642 },
1643 RecordAccess {
1645 file_start_record: i32,
1647 record_count: u32,
1649 file_record_data: Vec<Vec<u8>>,
1651 },
1652}
1653
1654impl AtomicReadFileResponse {
1655 pub fn new_stream_access(end_of_file: bool, start_position: i32, data: Vec<u8>) -> Self {
1657 Self {
1658 end_of_file,
1659 access_method_result: FileAccessMethodResult::StreamAccess {
1660 file_start_position: start_position,
1661 file_data: data,
1662 },
1663 }
1664 }
1665
1666 pub fn new_record_access(end_of_file: bool, start_record: i32, records: Vec<Vec<u8>>) -> Self {
1668 let record_count = records.len() as u32;
1669 Self {
1670 end_of_file,
1671 access_method_result: FileAccessMethodResult::RecordAccess {
1672 file_start_record: start_record,
1673 record_count,
1674 file_record_data: records,
1675 },
1676 }
1677 }
1678}
1679
1680#[derive(Debug, Clone)]
1682pub struct AtomicWriteFileRequest {
1683 pub file_identifier: ObjectIdentifier,
1685 pub access_method: FileWriteAccessMethod,
1687}
1688
1689#[derive(Debug, Clone)]
1691pub enum FileWriteAccessMethod {
1692 StreamAccess {
1694 file_start_position: i32,
1696 file_data: Vec<u8>,
1698 },
1699 RecordAccess {
1701 file_start_record: i32,
1703 record_count: u32,
1705 file_record_data: Vec<Vec<u8>>,
1707 },
1708}
1709
1710impl AtomicWriteFileRequest {
1711 pub fn new_stream_access(
1713 file_identifier: ObjectIdentifier,
1714 start_position: i32,
1715 data: Vec<u8>,
1716 ) -> Self {
1717 Self {
1718 file_identifier,
1719 access_method: FileWriteAccessMethod::StreamAccess {
1720 file_start_position: start_position,
1721 file_data: data,
1722 },
1723 }
1724 }
1725
1726 pub fn new_record_access(
1728 file_identifier: ObjectIdentifier,
1729 start_record: i32,
1730 records: Vec<Vec<u8>>,
1731 ) -> Self {
1732 let record_count = records.len() as u32;
1733 Self {
1734 file_identifier,
1735 access_method: FileWriteAccessMethod::RecordAccess {
1736 file_start_record: start_record,
1737 record_count,
1738 file_record_data: records,
1739 },
1740 }
1741 }
1742
1743 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1745 let file_id: u32 = self.file_identifier.try_into()?;
1747 buffer.push(0x0C); buffer.extend_from_slice(&file_id.to_be_bytes());
1749
1750 buffer.push(0x1E); match &self.access_method {
1754 FileWriteAccessMethod::StreamAccess {
1755 file_start_position,
1756 file_data,
1757 } => {
1758 buffer.push(0x0E); buffer.push(0x09); buffer.extend_from_slice(&file_start_position.to_be_bytes());
1764
1765 buffer.push(0x1E); buffer.extend_from_slice(file_data);
1768 buffer.push(0x1F); buffer.push(0x0F); }
1772 FileWriteAccessMethod::RecordAccess {
1773 file_start_record,
1774 record_count: _,
1775 file_record_data,
1776 } => {
1777 buffer.push(0x1E); buffer.push(0x09); buffer.extend_from_slice(&file_start_record.to_be_bytes());
1783
1784 let record_count = file_record_data.len() as u32;
1786 buffer.push(0x19); buffer.extend_from_slice(&record_count.to_be_bytes());
1788
1789 buffer.push(0x2E); for record in file_record_data {
1792 buffer.push(0x65); buffer.push(record.len() as u8);
1795 buffer.extend_from_slice(record);
1796 }
1797 buffer.push(0x2F); buffer.push(0x1F); }
1801 }
1802
1803 buffer.push(0x1F); Ok(())
1806 }
1807}
1808
1809#[derive(Debug, Clone)]
1811pub struct AtomicWriteFileResponse {
1812 pub file_start_position: i32,
1814}
1815
1816#[derive(Debug, Clone)]
1818pub struct TimeSynchronizationRequest {
1819 pub date_time: BacnetDateTime,
1821}
1822
1823#[derive(Debug, Clone)]
1825pub struct UtcTimeSynchronizationRequest {
1826 pub utc_date_time: BacnetDateTime,
1828}
1829
1830#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1832pub struct BacnetDateTime {
1833 pub date: crate::object::Date,
1835 pub time: crate::object::Time,
1837}
1838
1839impl BacnetDateTime {
1840 pub fn new(date: crate::object::Date, time: crate::object::Time) -> Self {
1842 Self { date, time }
1843 }
1844
1845 #[cfg(feature = "std")]
1847 pub fn now() -> Self {
1848 use chrono::{Datelike, Local, Timelike};
1849
1850 let now = Local::now();
1851 let year = now.year() as u16;
1852 let month = now.month() as u8;
1853 let day = now.day() as u8;
1854 let weekday = now.weekday().number_from_monday() as u8; let hour = now.hour() as u8;
1856 let minute = now.minute() as u8;
1857 let second = now.second() as u8;
1858 let hundredths = (now.nanosecond() / 10_000_000) as u8;
1859
1860 let date = crate::object::Date {
1861 year,
1862 month,
1863 day,
1864 weekday,
1865 };
1866 let time = crate::object::Time {
1867 hour,
1868 minute,
1869 second,
1870 hundredths,
1871 };
1872 Self::new(date, time)
1873 }
1874
1875 pub fn unspecified() -> Self {
1877 Self {
1878 date: crate::object::Date {
1879 year: 255,
1880 month: 255,
1881 day: 255,
1882 weekday: 255,
1883 },
1884 time: crate::object::Time {
1885 hour: 255,
1886 minute: 255,
1887 second: 255,
1888 hundredths: 255,
1889 },
1890 }
1891 }
1892
1893 pub fn is_unspecified(&self) -> bool {
1895 self.date.year == 255
1896 && self.date.month == 255
1897 && self.date.day == 255
1898 && self.date.weekday == 255
1899 && self.time.hour == 255
1900 && self.time.minute == 255
1901 && self.time.second == 255
1902 && self.time.hundredths == 255
1903 }
1904
1905 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1907 use crate::encoding::{encode_date, encode_time};
1908
1909 encode_date(
1911 buffer,
1912 self.date.year,
1913 self.date.month,
1914 self.date.day,
1915 self.date.weekday,
1916 )?;
1917
1918 encode_time(
1920 buffer,
1921 self.time.hour,
1922 self.time.minute,
1923 self.time.second,
1924 self.time.hundredths,
1925 )?;
1926
1927 Ok(())
1928 }
1929
1930 pub fn decode(data: &[u8]) -> EncodingResult<(Self, usize)> {
1932 use crate::encoding::{decode_date, decode_time};
1933
1934 let ((year, month, day, weekday), consumed_date) = decode_date(data)?;
1936
1937 let ((hour, minute, second, hundredths), consumed_time) =
1939 decode_time(&data[consumed_date..])?;
1940
1941 let datetime = BacnetDateTime {
1942 date: crate::object::Date {
1943 year,
1944 month,
1945 day,
1946 weekday,
1947 },
1948 time: crate::object::Time {
1949 hour,
1950 minute,
1951 second,
1952 hundredths,
1953 },
1954 };
1955
1956 Ok((datetime, consumed_date + consumed_time))
1957 }
1958}
1959
1960impl TimeSynchronizationRequest {
1961 pub fn new(date_time: BacnetDateTime) -> Self {
1963 Self { date_time }
1964 }
1965
1966 #[cfg(feature = "std")]
1968 pub fn now() -> Self {
1969 Self::new(BacnetDateTime::now())
1970 }
1971
1972 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
1974 self.date_time.encode(buffer)
1975 }
1976
1977 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
1979 let (date_time, _consumed) = BacnetDateTime::decode(data)?;
1980 Ok(Self::new(date_time))
1981 }
1982}
1983
1984impl UtcTimeSynchronizationRequest {
1985 pub fn new(utc_date_time: BacnetDateTime) -> Self {
1987 Self { utc_date_time }
1988 }
1989
1990 #[cfg(feature = "std")]
1992 pub fn now() -> Self {
1993 use chrono::{Datelike, Timelike, Utc};
1994
1995 let now = Utc::now();
1996 let year = now.year() as u16;
1997 let month = now.month() as u8;
1998 let day = now.day() as u8;
1999 let weekday = now.weekday().number_from_monday() as u8;
2000 let hour = now.hour() as u8;
2001 let minute = now.minute() as u8;
2002 let second = now.second() as u8;
2003 let hundredths = (now.nanosecond() / 10_000_000) as u8;
2004
2005 let date = crate::object::Date {
2006 year,
2007 month,
2008 day,
2009 weekday,
2010 };
2011 let time = crate::object::Time {
2012 hour,
2013 minute,
2014 second,
2015 hundredths,
2016 };
2017 let utc_date_time = BacnetDateTime::new(date, time);
2018 Self::new(utc_date_time)
2019 }
2020
2021 pub fn encode(&self, buffer: &mut Vec<u8>) -> EncodingResult<()> {
2023 self.utc_date_time.encode(buffer)
2024 }
2025
2026 pub fn decode(data: &[u8]) -> EncodingResult<Self> {
2028 let (utc_date_time, _consumed) = BacnetDateTime::decode(data)?;
2029 Ok(Self::new(utc_date_time))
2030 }
2031}
2032
2033#[cfg(test)]
2034mod tests {
2035 use super::*;
2036 use crate::object::{ObjectIdentifier, ObjectType};
2037
2038 #[test]
2039 fn test_whois_request() {
2040 let whois_all = WhoIsRequest::new();
2042 assert!(whois_all.matches(123));
2043 assert!(whois_all.matches(456));
2044
2045 let whois_specific = WhoIsRequest::for_device(123);
2047 assert!(whois_specific.matches(123));
2048 assert!(!whois_specific.matches(124));
2049
2050 let whois_range = WhoIsRequest::for_range(100, 200);
2052 assert!(whois_range.matches(150));
2053 assert!(!whois_range.matches(50));
2054 assert!(!whois_range.matches(250));
2055 }
2056
2057 #[test]
2058 fn test_whois_encoding() {
2059 let mut buffer = Vec::new();
2060
2061 let whois_all = WhoIsRequest::new();
2063 whois_all.encode(&mut buffer).unwrap();
2064 assert_eq!(buffer.len(), 0); buffer.clear();
2068 let whois_specific = WhoIsRequest::for_device(123);
2069 whois_specific.encode(&mut buffer).unwrap();
2070 assert!(!buffer.is_empty());
2071
2072 let decoded = WhoIsRequest::decode(&buffer).unwrap();
2074 assert_eq!(decoded, whois_specific);
2075 }
2076
2077 #[test]
2078 fn test_iam_request() {
2079 let device_id = ObjectIdentifier::new(ObjectType::Device, 123);
2080 let iam = IAmRequest::new(device_id, 1476, Segmentation::Both, 999);
2081
2082 assert_eq!(iam.device_identifier.instance, 123);
2083 assert_eq!(iam.max_apdu_length_accepted, 1476);
2084 assert_eq!(iam.vendor_identifier, 999);
2085 }
2086
2087 #[test]
2088 fn test_read_property_request() {
2089 let object_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
2090 let read_prop = ReadPropertyRequest::new(object_id, PropertyIdentifier::PresentValue); assert_eq!(read_prop.object_identifier.instance, 1);
2093 assert_eq!(
2094 read_prop.property_identifier,
2095 PropertyIdentifier::PresentValue
2096 );
2097 assert_eq!(read_prop.property_array_index, None);
2098
2099 let read_prop_array =
2100 ReadPropertyRequest::with_array_index(object_id, PropertyIdentifier::PresentValue, 0);
2101 assert_eq!(read_prop_array.property_array_index, Some(0));
2102 }
2103
2104 #[test]
2105 fn test_write_property_request() {
2106 let object_id = ObjectIdentifier::new(ObjectType::AnalogOutput, 1);
2107 let property_value = vec![0x44, 0x42, 0x20, 0x00, 0x00]; let write_prop = WritePropertyRequest::new(object_id, 85, property_value.clone());
2109
2110 assert_eq!(write_prop.object_identifier.instance, 1);
2111 assert_eq!(write_prop.property_identifier, 85);
2112 assert_eq!(write_prop.property_value, property_value);
2113 assert_eq!(write_prop.priority, None);
2114
2115 let write_prop_priority =
2117 WritePropertyRequest::with_priority(object_id, 85, property_value.clone(), 8);
2118 assert_eq!(write_prop_priority.priority, Some(8));
2119
2120 let mut buffer = Vec::new();
2122 write_prop.encode(&mut buffer).unwrap();
2123 assert!(!buffer.is_empty());
2124
2125 let decoded = WritePropertyRequest::decode(&buffer).unwrap();
2126 assert_eq!(decoded.object_identifier.instance, 1);
2127 assert_eq!(decoded.property_identifier, 85);
2128 assert_eq!(decoded.property_value, property_value);
2129 }
2130
2131 #[test]
2132 fn test_read_property_multiple_request() {
2133 let object_id1 = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
2134 let object_id2 = ObjectIdentifier::new(ObjectType::BinaryInput, 2);
2135
2136 let prop_ref1 = PropertyReference::new(PropertyIdentifier::PresentValue); let prop_ref2 = PropertyReference::new(PropertyIdentifier::ObjectName); let prop_ref3 = PropertyReference::with_array_index(PropertyIdentifier::PriorityArray, 8); let spec1 = ReadAccessSpecification::new(object_id1, vec![prop_ref1, prop_ref2]);
2141 let spec2 = ReadAccessSpecification::new(object_id2, vec![prop_ref3]);
2142
2143 let rpm_request = ReadPropertyMultipleRequest::new(vec![spec1, spec2]);
2144
2145 assert_eq!(rpm_request.read_access_specifications.len(), 2);
2146 assert_eq!(
2147 rpm_request.read_access_specifications[0]
2148 .property_references
2149 .len(),
2150 2
2151 );
2152 assert_eq!(
2153 rpm_request.read_access_specifications[1]
2154 .property_references
2155 .len(),
2156 1
2157 );
2158 assert_eq!(
2159 rpm_request.read_access_specifications[1].property_references[0].property_array_index,
2160 Some(8)
2161 );
2162 }
2163
2164 #[test]
2165 fn test_subscribe_cov_request() {
2166 let object_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
2167 let cov_req = SubscribeCovRequest::new(123, object_id);
2168
2169 assert_eq!(cov_req.subscriber_process_identifier, 123);
2170 assert_eq!(cov_req.monitored_object_identifier.instance, 1);
2171 assert_eq!(cov_req.issue_confirmed_notifications, None);
2172 assert_eq!(cov_req.lifetime, None);
2173
2174 let cov_confirmed = SubscribeCovRequest::with_confirmation(123, object_id, true);
2176 assert_eq!(cov_confirmed.issue_confirmed_notifications, Some(true));
2177
2178 let cov_lifetime = SubscribeCovRequest::with_lifetime(123, object_id, 3600);
2180 assert_eq!(cov_lifetime.lifetime, Some(3600));
2181
2182 let mut buffer = Vec::new();
2184 cov_req.encode(&mut buffer).unwrap();
2185 assert!(!buffer.is_empty());
2186 }
2187
2188 #[test]
2189 fn test_cov_subscription_manager() {
2190 let mut manager = CovSubscriptionManager::new();
2191
2192 let device_id = ObjectIdentifier::new(ObjectType::Device, 1);
2193 let object_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
2194
2195 let subscription = CovSubscription::new(123, device_id, object_id, 3600);
2196 manager.add_subscription(subscription);
2197
2198 assert_eq!(manager.active_count(), 1);
2199
2200 let subscriptions = manager.get_subscriptions_for_object(object_id);
2201 assert_eq!(subscriptions.len(), 1);
2202 assert_eq!(subscriptions[0].subscriber_process_identifier, 123);
2203
2204 manager.update_timers(1800); let subscriptions = manager.get_subscriptions_for_object(object_id);
2207 assert_eq!(subscriptions[0].time_remaining, 1800);
2208
2209 manager.update_timers(1800); assert_eq!(manager.active_count(), 0);
2212
2213 manager.cleanup_expired();
2214 assert_eq!(manager.subscriptions.len(), 0);
2215 }
2216
2217 #[test]
2218 fn test_cov_notification_request() {
2219 let device_id = ObjectIdentifier::new(ObjectType::Device, 1);
2220 let object_id = ObjectIdentifier::new(ObjectType::AnalogInput, 1);
2221 let values = vec![
2222 crate::object::PropertyValue::Real(25.5), crate::object::PropertyValue::Boolean(false), ];
2225
2226 let notification = CovNotificationRequest::new(123, device_id, object_id, 3600, values);
2227
2228 assert_eq!(notification.subscriber_process_identifier, 123);
2229 assert_eq!(notification.initiating_device_identifier, device_id);
2230 assert_eq!(notification.monitored_object_identifier, object_id);
2231 assert_eq!(notification.time_remaining, 3600);
2232 assert_eq!(notification.list_of_values.len(), 2);
2233
2234 let mut buffer = Vec::new();
2236 notification.encode(&mut buffer).unwrap();
2237 assert!(!buffer.is_empty());
2238 }
2239
2240 #[test]
2241 fn test_atomic_read_file_request() {
2242 let file_id = ObjectIdentifier::new(ObjectType::File, 1);
2243
2244 let read_stream = AtomicReadFileRequest::new_stream_access(file_id, 0, 1024);
2246 match &read_stream.access_method {
2247 FileAccessMethod::StreamAccess {
2248 file_start_position,
2249 requested_octet_count,
2250 } => {
2251 assert_eq!(*file_start_position, 0);
2252 assert_eq!(*requested_octet_count, 1024);
2253 }
2254 _ => panic!("Expected StreamAccess"),
2255 }
2256
2257 let read_record = AtomicReadFileRequest::new_record_access(file_id, 5, 10);
2259 match &read_record.access_method {
2260 FileAccessMethod::RecordAccess {
2261 file_start_record,
2262 requested_record_count,
2263 } => {
2264 assert_eq!(*file_start_record, 5);
2265 assert_eq!(*requested_record_count, 10);
2266 }
2267 _ => panic!("Expected RecordAccess"),
2268 }
2269
2270 let mut buffer = Vec::new();
2272 read_stream.encode(&mut buffer).unwrap();
2273 assert!(!buffer.is_empty());
2274 }
2275
2276 #[test]
2277 fn test_atomic_read_file_response() {
2278 let data = vec![1, 2, 3, 4, 5];
2280 let response_stream = AtomicReadFileResponse::new_stream_access(false, 0, data.clone());
2281 assert!(!response_stream.end_of_file);
2282
2283 match &response_stream.access_method_result {
2284 FileAccessMethodResult::StreamAccess {
2285 file_start_position,
2286 file_data,
2287 } => {
2288 assert_eq!(*file_start_position, 0);
2289 assert_eq!(*file_data, data);
2290 }
2291 _ => panic!("Expected StreamAccess result"),
2292 }
2293
2294 let records = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
2296 let response_record = AtomicReadFileResponse::new_record_access(true, 10, records.clone());
2297 assert!(response_record.end_of_file);
2298
2299 match &response_record.access_method_result {
2300 FileAccessMethodResult::RecordAccess {
2301 file_start_record,
2302 record_count,
2303 file_record_data,
2304 } => {
2305 assert_eq!(*file_start_record, 10);
2306 assert_eq!(*record_count, 3);
2307 assert_eq!(*file_record_data, records);
2308 }
2309 _ => panic!("Expected RecordAccess result"),
2310 }
2311 }
2312
2313 #[test]
2314 fn test_atomic_write_file_request() {
2315 let file_id = ObjectIdentifier::new(ObjectType::File, 1);
2316
2317 let data = vec![65, 66, 67, 68]; let write_stream = AtomicWriteFileRequest::new_stream_access(file_id, 100, data.clone());
2320 match &write_stream.access_method {
2321 FileWriteAccessMethod::StreamAccess {
2322 file_start_position,
2323 file_data,
2324 } => {
2325 assert_eq!(*file_start_position, 100);
2326 assert_eq!(*file_data, data);
2327 }
2328 _ => panic!("Expected StreamAccess"),
2329 }
2330
2331 let records = vec![
2333 b"Record 1".to_vec(),
2334 b"Record 2".to_vec(),
2335 b"Record 3".to_vec(),
2336 ];
2337 let write_record = AtomicWriteFileRequest::new_record_access(file_id, 5, records.clone());
2338 match &write_record.access_method {
2339 FileWriteAccessMethod::RecordAccess {
2340 file_start_record,
2341 record_count,
2342 file_record_data,
2343 } => {
2344 assert_eq!(*file_start_record, 5);
2345 assert_eq!(*record_count, 3);
2346 assert_eq!(*file_record_data, records);
2347 }
2348 _ => panic!("Expected RecordAccess"),
2349 }
2350
2351 let mut buffer = Vec::new();
2353 write_stream.encode(&mut buffer).unwrap();
2354 assert!(!buffer.is_empty());
2355 }
2356
2357 #[test]
2358 fn test_atomic_write_file_response() {
2359 let response = AtomicWriteFileResponse {
2360 file_start_position: 150,
2361 };
2362 assert_eq!(response.file_start_position, 150);
2363 }
2364
2365 #[test]
2366 fn test_bacnet_datetime() {
2367 let date = crate::object::Date {
2369 year: 2024,
2370 month: 3,
2371 day: 15,
2372 weekday: 5,
2373 };
2374 let time = crate::object::Time {
2375 hour: 14,
2376 minute: 30,
2377 second: 45,
2378 hundredths: 50,
2379 };
2380 let datetime = BacnetDateTime::new(date, time);
2381 assert_eq!(datetime.date.year, 2024);
2382 assert_eq!(datetime.date.month, 3);
2383 assert_eq!(datetime.date.day, 15);
2384 assert_eq!(datetime.date.weekday, 5); assert_eq!(datetime.time.hour, 14);
2386 assert_eq!(datetime.time.minute, 30);
2387 assert_eq!(datetime.time.second, 45);
2388 assert_eq!(datetime.time.hundredths, 50);
2389
2390 let unspecified = BacnetDateTime::unspecified();
2392 assert!(unspecified.is_unspecified());
2393 assert_eq!(unspecified.date.year, 255);
2394 assert_eq!(unspecified.time.hour, 255);
2395
2396 let mut buffer = Vec::new();
2398 datetime.encode(&mut buffer).unwrap();
2399 assert_eq!(buffer.len(), 10); let (decoded, consumed) = BacnetDateTime::decode(&buffer).unwrap();
2402 assert_eq!(consumed, 10);
2403 assert_eq!(decoded, datetime);
2404 }
2405
2406 #[test]
2407 fn test_time_synchronization_request() {
2408 let date = crate::object::Date {
2409 year: 2024,
2410 month: 6,
2411 day: 20,
2412 weekday: 4,
2413 };
2414 let time = crate::object::Time {
2415 hour: 10,
2416 minute: 15,
2417 second: 30,
2418 hundredths: 25,
2419 };
2420 let datetime = BacnetDateTime::new(date, time);
2421 let time_sync = TimeSynchronizationRequest::new(datetime);
2422
2423 assert_eq!(time_sync.date_time, datetime);
2424
2425 let mut buffer = Vec::new();
2427 time_sync.encode(&mut buffer).unwrap();
2428 assert_eq!(buffer.len(), 10);
2429
2430 let decoded = TimeSynchronizationRequest::decode(&buffer).unwrap();
2431 assert_eq!(decoded.date_time, datetime);
2432 }
2433
2434 #[test]
2435 fn test_utc_time_synchronization_request() {
2436 let date = crate::object::Date {
2437 year: 2024,
2438 month: 6,
2439 day: 20,
2440 weekday: 4,
2441 };
2442 let time = crate::object::Time {
2443 hour: 18,
2444 minute: 45,
2445 second: 15,
2446 hundredths: 75,
2447 };
2448 let utc_datetime = BacnetDateTime::new(date, time);
2449 let utc_sync = UtcTimeSynchronizationRequest::new(utc_datetime);
2450
2451 assert_eq!(utc_sync.utc_date_time, utc_datetime);
2452
2453 let mut buffer = Vec::new();
2455 utc_sync.encode(&mut buffer).unwrap();
2456 assert_eq!(buffer.len(), 10);
2457
2458 let decoded = UtcTimeSynchronizationRequest::decode(&buffer).unwrap();
2459 assert_eq!(decoded.utc_date_time, utc_datetime);
2460 }
2461
2462 #[cfg(feature = "std")]
2463 #[test]
2464 fn test_time_synchronization_now() {
2465 let now_sync = TimeSynchronizationRequest::now();
2467 assert!(!now_sync.date_time.is_unspecified());
2468
2469 let utc_now_sync = UtcTimeSynchronizationRequest::now();
2470 assert!(!utc_now_sync.utc_date_time.is_unspecified());
2471
2472 assert!(now_sync.date_time.date.year >= 2024);
2474 assert!(now_sync.date_time.date.month >= 1 && now_sync.date_time.date.month <= 12);
2475 assert!(now_sync.date_time.date.day >= 1 && now_sync.date_time.date.day <= 31);
2476 assert!(now_sync.date_time.time.hour <= 23);
2477 assert!(now_sync.date_time.time.minute <= 59);
2478 assert!(now_sync.date_time.time.second <= 59);
2479 assert!(now_sync.date_time.time.hundredths <= 99);
2480 }
2481
2482 #[test]
2483 fn test_read_property_multiple_response() {
2484 let data = [
2485 0xc, 0x0, 0x80, 0x75, 0x3b, 0x1e, 0x29, 0x4b, 0x4e, 0xc4, 0x0, 0x80, 0x75, 0x3b, 0x4f,
2486 0x29, 0x4d, 0x4e, 0x75, 0xc, 0x0, 0x48, 0x53, 0x31, 0x43, 0x75, 0x72, 0x76, 0x65, 0x5f,
2487 0x59, 0x33, 0x4f, 0x29, 0x4f, 0x4e, 0x91, 0x2, 0x4f, 0x29, 0x55, 0x4e, 0x44, 0x42,
2488 0x6c, 0x0, 0x0, 0x4f, 0x29, 0x6f, 0x4e, 0x82, 0x4, 0x0, 0x4f, 0x29, 0x24, 0x4e, 0x91,
2489 0x0, 0x4f, 0x29, 0x51, 0x4e, 0x10, 0x4f, 0x29, 0x75, 0x4e, 0x91, 0x3e, 0x4f, 0x29,
2490 0x1c, 0x4e, 0x75, 0x43, 0x0, 0x53, 0x65, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20,
2491 0x66, 0x6f, 0x72, 0x20, 0x74, 0x68, 0x69, 0x72, 0x64, 0x20, 0x63, 0x75, 0x72, 0x76,
2492 0x65, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6f, 0x75, 0x74,
2493 0x64, 0x6f, 0x6f, 0x72, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x65, 0x6e, 0x73, 0x61, 0x74,
2494 0x65, 0x64, 0x20, 0x73, 0x65, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x20, 0x48, 0x53,
2495 0x31, 0x4f, 0x29, 0x67, 0x4e, 0x91, 0x0, 0x4f, 0x1f,
2496 ];
2497
2498 let response = ReadPropertyMultipleResponse::decode(&data).unwrap();
2499 assert_eq!(response.read_access_results.len(), 1);
2500
2501 let result = &response.read_access_results[0];
2502 assert_eq!(
2503 result.object_identifier,
2504 ObjectIdentifier::new(ObjectType::AnalogValue, 30011)
2505 );
2506 assert_eq!(result.results.len(), 10);
2507 assert_eq!(
2508 result.results[0].property_identifier,
2509 PropertyIdentifier::ObjectIdentifier
2510 );
2511 assert_eq!(
2512 result.results[0].value,
2513 PropertyResultValue::Value(vec![property::PropertyValue::ObjectIdentifier(
2514 ObjectIdentifier::new(ObjectType::AnalogValue, 30011)
2515 )])
2516 );
2517 assert_eq!(
2518 result.results[1].property_identifier,
2519 PropertyIdentifier::ObjectName
2520 );
2521 assert_eq!(
2522 result.results[1].value,
2523 PropertyResultValue::Value(vec![property::PropertyValue::CharacterString(
2524 "HS1Curve_Y3".to_string()
2525 )])
2526 );
2527 assert_eq!(
2528 result.results[2].property_identifier,
2529 PropertyIdentifier::ObjectType
2530 );
2531 assert_eq!(
2532 result.results[2].value,
2533 PropertyResultValue::Value(vec![property::PropertyValue::Enumerated(
2534 ObjectType::AnalogValue.into()
2535 )])
2536 );
2537 assert_eq!(
2538 result.results[3].property_identifier,
2539 PropertyIdentifier::PresentValue
2540 );
2541 assert_eq!(
2542 result.results[3].value,
2543 PropertyResultValue::Value(vec![property::PropertyValue::Real(59.0)])
2544 );
2545 assert_eq!(
2546 result.results[4].property_identifier,
2547 PropertyIdentifier::StatusFlags
2548 );
2549 assert_eq!(
2550 result.results[4].value,
2551 PropertyResultValue::Value(vec![property::PropertyValue::BitString(vec![
2552 false, false, false, false
2553 ])])
2554 );
2555 assert_eq!(
2556 result.results[5].property_identifier,
2557 PropertyIdentifier::EventState
2558 );
2559 assert_eq!(
2560 result.results[5].value,
2561 PropertyResultValue::Value(vec![property::PropertyValue::Enumerated(0)])
2562 );
2563 assert_eq!(
2564 result.results[6].property_identifier,
2565 PropertyIdentifier::OutOfService
2566 );
2567 assert_eq!(
2568 result.results[6].value,
2569 PropertyResultValue::Value(vec![property::PropertyValue::Boolean(false)])
2570 );
2571 assert_eq!(
2572 result.results[7].property_identifier,
2573 PropertyIdentifier::Units
2574 );
2575 assert_eq!(
2576 result.results[7].value,
2577 PropertyResultValue::Value(vec![property::PropertyValue::Enumerated(62)])
2578 );
2579 assert_eq!(
2580 result.results[8].property_identifier,
2581 PropertyIdentifier::Description
2582 );
2583 assert_eq!(
2584 result.results[8].value,
2585 PropertyResultValue::Value(vec![property::PropertyValue::CharacterString(
2586 "Setpoint for third curvepoint for outdoor compensated setpoint HS1".to_string()
2587 )])
2588 );
2589 assert_eq!(
2590 result.results[9].property_identifier,
2591 PropertyIdentifier::Reliability
2592 );
2593 assert_eq!(
2594 result.results[9].value,
2595 PropertyResultValue::Value(vec![property::PropertyValue::Enumerated(0)])
2596 );
2597 }
2598
2599 #[test]
2600 fn test_read_property_response() {
2601 let data = [
2602 0xc, 0x2, 0x0, 0xa, 0x50, 0x19, 0x4d, 0x3e, 0x75, 0xf, 0x0, 0x43, 0x6f, 0x72, 0x72,
2603 0x69, 0x67, 0x6f, 0x48, 0x65, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x3f,
2604 ];
2605
2606 let response = ReadPropertyResponse::decode(&data).unwrap();
2607
2608 assert_eq!(
2609 response.object_identifier,
2610 ObjectIdentifier::new(ObjectType::Device, 2640)
2611 );
2612 assert_eq!(response.property_identifier, PropertyIdentifier::ObjectName);
2613 assert_eq!(response.property_array_index, None);
2614 assert_eq!(response.property_values.len(), 1);
2615 assert_eq!(
2616 response.property_values[0],
2617 property::PropertyValue::CharacterString("CorrigoHeating".to_string())
2618 );
2619
2620 let mut encoded = Vec::new();
2621 response.encode(&mut encoded).unwrap();
2622 assert_eq!(encoded.len(), data.len());
2623 assert_eq!(encoded, data);
2624 }
2625}