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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use super::*;
// AccessUserObject (type 35)
// ---------------------------------------------------------------------------
/// BACnet Access User object (type 35).
///
/// Stands for whoever or whatever is granted access (someone, a team, a
/// tracked item) and the credentials it holds. Its kind lives in User_Type;
/// Table 12-38 has no Present_Value, Assigned_Access_Rights or Out_Of_Service
/// row, so the object serves none of them (#1064).
///
/// Credentials, Members and Member_Of are lists of
/// `BACnetDeviceObjectReference` (Clauses 12.33.12 to 12.33.14; #1394), so an
/// entry may name an object in another device. Credentials names the user's
/// Access Credential objects. Members and Member_Of name other Access Users,
/// one level down and one level up a hierarchy of users (a department and
/// the people in it, say). The application sets all three; they are
/// read-only over the network.
///
/// Global_Identifier names the user across devices: every Access User
/// standing for the same person, team or item carries the same nonzero
/// value, and 0 means none is assigned (Clause 12.33.5). It is the W row of
/// Table 12-38, so clients write it as they write an Access Credential's
/// (#1463).
pub struct AccessUserObject {
oid: ObjectIdentifier,
name: String,
description: String,
global_identifier: u32,
user_type: AccessUserType,
credentials: Vec<BACnetDeviceObjectReference>,
members: Vec<BACnetDeviceObjectReference>,
member_of: Vec<BACnetDeviceObjectReference>,
status_flags: StatusFlags,
reliability: Reliability,
}
impl AccessUserObject {
/// Create a new Access User object.
pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
let oid = ObjectIdentifier::new(ObjectType::ACCESS_USER, instance)?;
Ok(Self {
oid,
name: name.into(),
description: String::new(),
global_identifier: 0,
user_type: AccessUserType::ASSET,
credentials: Vec::new(),
members: Vec::new(),
member_of: Vec::new(),
status_flags: StatusFlags::empty(),
reliability: Reliability::NO_FAULT_DETECTED,
})
}
/// Set Global_Identifier; 0 means none is assigned (Clause 12.33.5).
pub fn set_global_identifier(&mut self, value: u32) {
self.global_identifier = value;
}
/// Set Credentials, the Access Credential objects the user holds
/// (Clause 12.33.14). A reference with no device identifier names an
/// object in this device.
///
/// Each reference has to name an Access Credential object, and its
/// device identifier, when given, a Device object; a list breaking either
/// rule is refused with VALUE_OUT_OF_RANGE and the credentials set before
/// are kept. The list is read-only over the network.
pub fn set_credentials(
&mut self,
credentials: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
) -> Result<(), Error> {
self.credentials = references_to(ObjectType::ACCESS_CREDENTIAL, credentials)?;
Ok(())
}
/// Set Members, the Access Users one level below this one
/// (Clause 12.33.12), with the checks [`Self::set_credentials`] makes,
/// for Access User objects.
pub fn set_members(
&mut self,
members: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
) -> Result<(), Error> {
self.members = references_to(ObjectType::ACCESS_USER, members)?;
Ok(())
}
/// Set Member_Of, the Access Users one level above this one
/// (Clause 12.33.13), with the checks [`Self::set_members`] makes.
pub fn set_member_of(
&mut self,
groups: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
) -> Result<(), Error> {
self.member_of = references_to(ObjectType::ACCESS_USER, groups)?;
Ok(())
}
}
impl BACnetObject for AccessUserObject {
fn object_identifier(&self) -> ObjectIdentifier {
self.oid
}
fn object_name(&self) -> &str {
&self.name
}
fn read_property(
&self,
property: PropertyIdentifier,
array_index: Option<u32>,
) -> Result<PropertyValue, Error> {
// Clause 12.33 holds the OUT_OF_SERVICE flag FALSE.
if let Some(result) =
read_common_properties!(self, property, array_index, no_out_of_service)
{
return result;
}
match property {
p if p == PropertyIdentifier::OBJECT_TYPE => {
Ok(PropertyValue::Enumerated(ObjectType::ACCESS_USER.to_raw()))
}
p if p == PropertyIdentifier::USER_TYPE => {
Ok(PropertyValue::Enumerated(self.user_type.to_raw()))
}
p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
Ok(PropertyValue::Unsigned(self.global_identifier.into()))
}
p if p == PropertyIdentifier::CREDENTIALS => {
Ok(crate::device_reference::reference_list(&self.credentials))
}
p if p == PropertyIdentifier::MEMBERS => {
Ok(crate::device_reference::reference_list(&self.members))
}
p if p == PropertyIdentifier::MEMBER_OF => {
Ok(crate::device_reference::reference_list(&self.member_of))
}
_ => Err(common::unknown_property_error()),
}
}
fn write_property(
&mut self,
property: PropertyIdentifier,
_array_index: Option<u32>,
value: PropertyValue,
_priority: Option<u8>,
) -> Result<(), Error> {
if let Some(result) = common::write_description(&mut self.description, property, &value) {
return result;
}
match property {
p if p == PropertyIdentifier::GLOBAL_IDENTIFIER => {
let PropertyValue::Unsigned(raw) = value else {
return Err(common::invalid_data_type_error());
};
self.global_identifier = common::u64_to_u32(raw)?;
Ok(())
}
p if p == PropertyIdentifier::USER_TYPE => {
if let PropertyValue::Enumerated(v) = value {
self.user_type = AccessUserType::from_raw(v);
Ok(())
} else {
Err(common::invalid_data_type_error())
}
}
_ => Err(crate::common::unhandled_write_error(
self.property_metadata().as_ref(),
property,
_array_index,
)),
}
}
fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
super::metadata_identity::for_access_user_object(self)
}
fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
}
}
// ---------------------------------------------------------------------------