bacnet_server/server/confirmed_answer.rs
1//! The answer a device gives a confirmed request this server sent: a
2//! confirmed notification, a WriteProperty a run makes in another device, or
3//! the ReadProperty a Channel sends there to learn a member's datatype
4//! (#1342).
5//!
6//! An answer that turns the request down keeps what it said (#1323): the
7//! Error PDU's class and code, or the Reject or Abort reason. Notification
8//! senders only ask whether the request was taken; a Channel writing a member
9//! in another device needs the code too, since a NULL refused as an invalid
10//! datatype still counts as written (Clause 12.53.7).
11
12use bacnet_types::enums::{AbortReason, ErrorClass, ErrorCode, RejectReason};
13use bacnet_types::error::Error;
14use bytes::Bytes;
15
16/// Result of a confirmed request from the receiving device's side.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum CovAckResult {
19 /// SimpleAck received: the device took the request.
20 Ack,
21 /// An unsegmented ComplexAck received, with its service data: the device
22 /// answered a read.
23 Data(Bytes),
24 /// Error, Reject or Abort received: the device turned the request down.
25 Error(Refusal),
26}
27
28/// How a device turned down a confirmed request.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Refusal {
31 /// An Error PDU.
32 Error {
33 /// Its error class.
34 class: ErrorClass,
35 /// Its error code.
36 code: ErrorCode,
37 },
38 /// A Reject PDU, with its reason.
39 Reject(RejectReason),
40 /// An Abort PDU, with its reason.
41 Abort(AbortReason),
42}
43
44/// The error a client's WriteProperty answered this way reports:
45/// [`Error::Protocol`], [`Error::Reject`] or [`Error::Abort`]. A `Refusal`
46/// keeps only an Error's class and code, so it never becomes
47/// [`Error::Structured`], which a client reports for the services whose
48/// Error carries more.
49impl From<Refusal> for Error {
50 fn from(refusal: Refusal) -> Self {
51 match refusal {
52 Refusal::Error { class, code } => Self::Protocol {
53 class: class.to_raw().into(),
54 code: code.to_raw().into(),
55 },
56 Refusal::Reject(reason) => Self::Reject {
57 reason: reason.to_raw(),
58 },
59 Refusal::Abort(reason) => Self::Abort {
60 reason: reason.to_raw(),
61 },
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn refusal_converts_to_the_error_a_client_request_reports() {
72 let error = Refusal::Error {
73 class: ErrorClass::PROPERTY,
74 code: ErrorCode::INVALID_DATA_TYPE,
75 };
76 assert!(matches!(
77 Error::from(error),
78 Error::Protocol { class: 2, code: 9 }
79 ));
80 let reject = Refusal::Reject(RejectReason::INVALID_PARAMETER_DATA_TYPE);
81 assert!(matches!(Error::from(reject), Error::Reject { reason: 3 }));
82 let abort = Refusal::Abort(AbortReason::SEGMENTATION_NOT_SUPPORTED);
83 assert!(matches!(Error::from(abort), Error::Abort { reason: 4 }));
84 }
85}