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