bacnet_services/
structured_error.rs1use crate::cov_multiple::SubscribeCOVPropertyMultipleError;
14use crate::list_manipulation::ChangeListError;
15use crate::object_mgmt::CreateObjectError;
16use crate::private_transfer::PrivateTransferError;
17use crate::virtual_terminal::VTCloseError;
18use crate::wpm::WritePropertyMultipleError;
19use bacnet_encoding::apdu::ErrorPdu;
20use bacnet_types::enums::ConfirmedServiceChoice;
21use bacnet_types::error::{Error, ErrorDetail};
22
23pub fn detail(pdu: &ErrorPdu) -> Option<ErrorDetail> {
28 let service = pdu.service_choice;
29 let error = if service == ConfirmedServiceChoice::ADD_LIST_ELEMENT
30 || service == ConfirmedServiceChoice::REMOVE_LIST_ELEMENT
31 {
32 ChangeListError::try_from(pdu).map(Error::from)
33 } else if service == ConfirmedServiceChoice::CREATE_OBJECT {
34 CreateObjectError::try_from(pdu).map(Error::from)
35 } else if service == ConfirmedServiceChoice::WRITE_PROPERTY_MULTIPLE {
36 WritePropertyMultipleError::try_from(pdu).map(Error::from)
37 } else if service == ConfirmedServiceChoice::SUBSCRIBE_COV_PROPERTY_MULTIPLE {
38 SubscribeCOVPropertyMultipleError::try_from(pdu).map(Error::from)
39 } else if service == ConfirmedServiceChoice::CONFIRMED_PRIVATE_TRANSFER {
40 PrivateTransferError::try_from(pdu).map(Error::from)
41 } else if service == ConfirmedServiceChoice::VT_CLOSE {
42 VTCloseError::try_from(pdu).map(Error::from)
43 } else {
44 return None;
45 };
46 match error {
47 Ok(Error::Structured { detail, .. }) => Some(*detail),
48 _ => None,
49 }
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55 use bacnet_types::constructed::BACnetObjectPropertyReference;
56 use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier};
57 use bacnet_types::primitives::ObjectIdentifier;
58 use bytes::Bytes;
59
60 #[test]
61 fn each_service_reads_its_own_body() {
62 let reference = BACnetObjectPropertyReference {
63 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_VALUE, 1).unwrap(),
64 property_identifier: PropertyIdentifier::PRESENT_VALUE.to_raw(),
65 property_array_index: None,
66 };
67 let (class, code) = (ErrorClass::PROPERTY, ErrorCode::WRITE_ACCESS_DENIED);
68 let cases = [
69 (
70 ChangeListError {
71 error_class: class,
72 error_code: code,
73 first_failed_element_number: 2,
74 }
75 .to_error_pdu(1, ConfirmedServiceChoice::REMOVE_LIST_ELEMENT),
76 Some(ErrorDetail::FirstFailedElementNumber(2)),
77 ),
78 (
79 CreateObjectError {
80 error_class: class,
81 error_code: code,
82 first_failed_element_number: 3,
83 }
84 .to_error_pdu(1),
85 Some(ErrorDetail::FirstFailedElementNumber(3)),
86 ),
87 (
88 WritePropertyMultipleError {
89 error_class: class,
90 error_code: code,
91 first_failed_write_attempt: reference.clone(),
92 }
93 .to_error_pdu(1),
94 Some(ErrorDetail::FirstFailedWriteAttempt(reference.clone())),
95 ),
96 (
97 SubscribeCOVPropertyMultipleError {
98 error_class: class,
99 error_code: code,
100 first_failed_subscription: Some(reference.clone()),
101 }
102 .to_error_pdu(1),
103 Some(ErrorDetail::FirstFailedSubscription(reference.clone())),
104 ),
105 (
106 SubscribeCOVPropertyMultipleError {
107 error_class: class,
108 error_code: code,
109 first_failed_subscription: None,
110 }
111 .to_error_pdu(1),
112 None,
113 ),
114 (
115 PrivateTransferError {
116 error_class: class,
117 error_code: code,
118 vendor_id: 9,
119 service_number: 1,
120 error_parameters: None,
121 }
122 .to_error_pdu(1),
123 Some(ErrorDetail::PrivateTransfer {
124 vendor_id: 9,
125 service_number: 1,
126 error_parameters: None,
127 }),
128 ),
129 (
130 VTCloseError {
131 error_class: class,
132 error_code: code,
133 list_of_vt_session_identifiers: Some(vec![3]),
134 }
135 .to_error_pdu(1),
136 Some(ErrorDetail::VtSessionIdentifiers(vec![3])),
137 ),
138 ];
139 for (pdu, expected) in cases {
140 assert_eq!(detail(&pdu), expected, "{:?}", pdu.service_choice);
141 let mut other = pdu.clone();
144 other.service_choice = ConfirmedServiceChoice::READ_PROPERTY;
145 assert_eq!(detail(&other), None);
146 let mut plain = pdu;
147 plain.error_data = Bytes::new();
148 assert_eq!(detail(&plain), None);
149 }
150 }
151}