1use bacnet_types::enums::{ObjectType, PropertyIdentifier};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
10use bacnet_types::MacAddr;
11use std::borrow::Cow;
12
13use crate::common::{self, read_common_properties};
14use crate::traits::{BACnetObject, WritePropertyRollback};
15
16pub struct NetworkPortObject {
22 oid: ObjectIdentifier,
23 name: String,
24 description: String,
25 status_flags: StatusFlags,
26 out_of_service: bool,
27 reliability: u32,
28 network_type: u32,
30 network_number: u32,
32 mac_address: MacAddr,
34 max_apdu_length_accepted: u32,
36 link_speed: f32,
38 changes_pending: bool,
40 command: u32,
42 ip_address: Vec<u8>,
44 ip_default_gateway: Vec<u8>,
46 ip_subnet_mask: Vec<u8>,
48 ip_udp_port: u16,
50}
51
52struct NetworkPortWriteRollback {
53 changes_pending: bool,
54}
55
56impl NetworkPortObject {
57 pub fn new(instance: u32, name: impl Into<String>, network_type: u32) -> Result<Self, Error> {
61 let oid = ObjectIdentifier::new(ObjectType::NETWORK_PORT, instance)?;
62 Ok(Self {
63 oid,
64 name: name.into(),
65 description: String::new(),
66 status_flags: StatusFlags::empty(),
67 out_of_service: false,
68 reliability: 0,
69 network_type,
70 network_number: 0,
71 mac_address: MacAddr::new(),
72 max_apdu_length_accepted: 1476,
73 link_speed: 0.0,
74 changes_pending: false,
75 command: 0,
76 ip_address: vec![0, 0, 0, 0],
77 ip_default_gateway: vec![0, 0, 0, 0],
78 ip_subnet_mask: vec![255, 255, 255, 0],
79 ip_udp_port: 0xBAC0,
80 })
81 }
82
83 pub fn set_description(&mut self, desc: impl Into<String>) {
85 self.description = desc.into();
86 }
87
88 pub fn set_ip_address(&mut self, addr: Vec<u8>) {
90 self.ip_address = addr;
91 }
92
93 pub fn set_ip_default_gateway(&mut self, gw: Vec<u8>) {
95 self.ip_default_gateway = gw;
96 }
97
98 pub fn set_ip_subnet_mask(&mut self, mask: Vec<u8>) {
100 self.ip_subnet_mask = mask;
101 }
102
103 pub fn set_mac_address(&mut self, mac: MacAddr) {
105 self.mac_address = mac;
106 }
107
108 pub fn set_network_number(&mut self, num: u32) {
110 self.network_number = num;
111 }
112
113 pub fn set_link_speed(&mut self, speed: f32) {
115 self.link_speed = speed;
116 }
117
118 pub fn set_udp_port(&mut self, port: u16) {
120 self.ip_udp_port = port;
121 }
122}
123
124impl BACnetObject for NetworkPortObject {
125 fn object_identifier(&self) -> ObjectIdentifier {
126 self.oid
127 }
128
129 fn object_name(&self) -> &str {
130 &self.name
131 }
132
133 fn read_property(
134 &self,
135 property: PropertyIdentifier,
136 array_index: Option<u32>,
137 ) -> Result<PropertyValue, Error> {
138 if let Some(result) = read_common_properties!(self, property, array_index) {
139 return result;
140 }
141 match property {
142 p if p == PropertyIdentifier::OBJECT_TYPE => {
143 Ok(PropertyValue::Enumerated(ObjectType::NETWORK_PORT.to_raw()))
144 }
145 p if p == PropertyIdentifier::NETWORK_TYPE => {
146 Ok(PropertyValue::Enumerated(self.network_type))
147 }
148 p if p == PropertyIdentifier::NETWORK_NUMBER => {
149 Ok(PropertyValue::Unsigned(self.network_number as u64))
150 }
151 p if p == PropertyIdentifier::MAC_ADDRESS => {
152 Ok(PropertyValue::OctetString(self.mac_address.to_vec()))
153 }
154 p if p == PropertyIdentifier::MAX_APDU_LENGTH_ACCEPTED => Ok(PropertyValue::Unsigned(
155 self.max_apdu_length_accepted as u64,
156 )),
157 p if p == PropertyIdentifier::LINK_SPEED => Ok(PropertyValue::Real(self.link_speed)),
158 p if p == PropertyIdentifier::CHANGES_PENDING => {
159 Ok(PropertyValue::Boolean(self.changes_pending))
160 }
161 p if p == PropertyIdentifier::COMMAND_NP => Ok(PropertyValue::Enumerated(self.command)),
162 p if p == PropertyIdentifier::IP_ADDRESS => {
163 Ok(PropertyValue::OctetString(self.ip_address.clone()))
164 }
165 p if p == PropertyIdentifier::IP_DEFAULT_GATEWAY => {
166 Ok(PropertyValue::OctetString(self.ip_default_gateway.clone()))
167 }
168 p if p == PropertyIdentifier::IP_SUBNET_MASK => {
169 Ok(PropertyValue::OctetString(self.ip_subnet_mask.clone()))
170 }
171 p if p == PropertyIdentifier::BACNET_IP_UDP_PORT => {
172 Ok(PropertyValue::Unsigned(self.ip_udp_port as u64))
173 }
174 _ => Err(common::unknown_property_error()),
175 }
176 }
177
178 fn write_property(
179 &mut self,
180 property: PropertyIdentifier,
181 _array_index: Option<u32>,
182 value: PropertyValue,
183 _priority: Option<u8>,
184 ) -> Result<(), Error> {
185 if let Some(result) =
186 common::write_out_of_service(&mut self.out_of_service, property, &value)
187 {
188 return result;
189 }
190 if let Some(result) = common::write_description(&mut self.description, property, &value) {
191 return result;
192 }
193 if property == PropertyIdentifier::COMMAND_NP {
194 if let PropertyValue::Enumerated(v) = value {
195 self.command = v;
196 return Ok(());
197 }
198 return Err(common::invalid_data_type_error());
199 }
200 if property == PropertyIdentifier::IP_ADDRESS {
201 if let PropertyValue::OctetString(v) = value {
202 self.ip_address = v;
203 self.changes_pending = true;
204 return Ok(());
205 }
206 return Err(common::invalid_data_type_error());
207 }
208 if property == PropertyIdentifier::IP_DEFAULT_GATEWAY {
209 if let PropertyValue::OctetString(v) = value {
210 self.ip_default_gateway = v;
211 self.changes_pending = true;
212 return Ok(());
213 }
214 return Err(common::invalid_data_type_error());
215 }
216 if property == PropertyIdentifier::IP_SUBNET_MASK {
217 if let PropertyValue::OctetString(v) = value {
218 self.ip_subnet_mask = v;
219 self.changes_pending = true;
220 return Ok(());
221 }
222 return Err(common::invalid_data_type_error());
223 }
224 if property == PropertyIdentifier::BACNET_IP_UDP_PORT {
225 if let PropertyValue::Unsigned(v) = value {
226 if v > u16::MAX as u64 {
227 return Err(common::value_out_of_range_error());
228 }
229 self.ip_udp_port = v as u16;
230 self.changes_pending = true;
231 return Ok(());
232 }
233 return Err(common::invalid_data_type_error());
234 }
235 if property == PropertyIdentifier::NETWORK_NUMBER {
236 if let PropertyValue::Unsigned(v) = value {
237 self.network_number = common::u64_to_u32(v)?;
238 return Ok(());
239 }
240 return Err(common::invalid_data_type_error());
241 }
242 if property == PropertyIdentifier::MAC_ADDRESS {
243 if let PropertyValue::OctetString(v) = value {
244 self.mac_address = v.into();
245 return Ok(());
246 }
247 return Err(common::invalid_data_type_error());
248 }
249 Err(common::write_access_denied_error())
250 }
251
252 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
253 static PROPS: &[PropertyIdentifier] = &[
254 PropertyIdentifier::OBJECT_IDENTIFIER,
255 PropertyIdentifier::OBJECT_NAME,
256 PropertyIdentifier::DESCRIPTION,
257 PropertyIdentifier::OBJECT_TYPE,
258 PropertyIdentifier::STATUS_FLAGS,
259 PropertyIdentifier::OUT_OF_SERVICE,
260 PropertyIdentifier::RELIABILITY,
261 PropertyIdentifier::NETWORK_TYPE,
262 PropertyIdentifier::NETWORK_NUMBER,
263 PropertyIdentifier::MAC_ADDRESS,
264 PropertyIdentifier::MAX_APDU_LENGTH_ACCEPTED,
265 PropertyIdentifier::LINK_SPEED,
266 PropertyIdentifier::CHANGES_PENDING,
267 PropertyIdentifier::COMMAND_NP,
268 PropertyIdentifier::IP_ADDRESS,
269 PropertyIdentifier::IP_DEFAULT_GATEWAY,
270 PropertyIdentifier::IP_SUBNET_MASK,
271 PropertyIdentifier::BACNET_IP_UDP_PORT,
272 ];
273 Cow::Borrowed(PROPS)
274 }
275
276 fn capture_write_property_rollback(
277 &mut self,
278 property: PropertyIdentifier,
279 _value: &PropertyValue,
280 ) -> Option<WritePropertyRollback> {
281 matches!(
282 property,
283 PropertyIdentifier::IP_ADDRESS
284 | PropertyIdentifier::IP_DEFAULT_GATEWAY
285 | PropertyIdentifier::IP_SUBNET_MASK
286 | PropertyIdentifier::BACNET_IP_UDP_PORT
287 )
288 .then(|| {
289 WritePropertyRollback::new(NetworkPortWriteRollback {
290 changes_pending: self.changes_pending,
291 })
292 })
293 }
294
295 fn restore_write_property_rollback(
296 &mut self,
297 rollback: WritePropertyRollback,
298 ) -> Result<(), Error> {
299 self.changes_pending = rollback
300 .downcast::<NetworkPortWriteRollback>()?
301 .changes_pending;
302 Ok(())
303 }
304
305 fn is_createable(&self) -> bool {
307 false
308 }
309 fn is_deleteable(&self) -> bool {
310 false
311 }
312}
313
314#[cfg(test)]
315mod tests;