1use bacnet_types::enums::{NetworkType, ObjectType, PropertyIdentifier, Reliability};
12use bacnet_types::error::Error;
13use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
14use bacnet_types::MacAddr;
15use std::borrow::Cow;
16
17use crate::common::{self, read_common_properties};
18use crate::traits::BACnetObject;
19
20mod bip_config;
21mod metadata;
22mod registration;
23use bacnet_types::network_number::NetworkNumber;
24pub use bip_config::BipPortConfig;
25
26pub struct NetworkPortObject {
28 oid: ObjectIdentifier,
29 name: String,
30 description: String,
31 status_flags: StatusFlags,
32 out_of_service: bool,
33 reliability: Reliability,
34 network_type: NetworkType,
35 network_number: NetworkNumber,
36 mac_address: MacAddr,
37 apdu_length: u32,
38 bip: Option<BipPortConfig>,
39 binding: std::sync::Weak<()>,
40}
41
42impl NetworkPortObject {
43 pub fn new_bip(
46 instance: u32,
47 name: impl Into<String>,
48 config: BipPortConfig,
49 ) -> Result<Self, Error> {
50 config.validate(instance)?;
51 let mut mac = MacAddr::from_slice(&config.ip_address);
52 mac.extend_from_slice(&config.udp_port.to_be_bytes());
53 Ok(Self {
54 oid: ObjectIdentifier::new(ObjectType::NETWORK_PORT, instance)?,
55 name: name.into(),
56 description: String::new(),
57 status_flags: StatusFlags::empty(),
58 out_of_service: false,
59 reliability: Reliability::NO_FAULT_DETECTED,
60 network_type: NetworkType::IPV4,
61 network_number: NetworkNumber::configured(config.network_number)
62 .expect("validated Network Number"),
63 mac_address: mac,
64 apdu_length: config.apdu_length,
65 bip: Some(config),
66 binding: std::sync::Weak::new(),
67 })
68 }
69
70 pub fn new_non_bip(
74 instance: u32,
75 name: impl Into<String>,
76 network_type: NetworkType,
77 network_number: u16,
78 mac_address: MacAddr,
79 apdu_length: u32,
80 ) -> Result<Self, Error> {
81 if !matches!(network_type, NetworkType::ETHERNET | NetworkType::VIRTUAL)
82 || network_number == u16::MAX
83 || apdu_length < 50
84 {
85 return Err(common::value_out_of_range_error());
86 }
87 Ok(Self {
88 oid: ObjectIdentifier::new(ObjectType::NETWORK_PORT, instance)?,
89 name: name.into(),
90 description: String::new(),
91 status_flags: StatusFlags::empty(),
92 out_of_service: false,
93 reliability: Reliability::NO_FAULT_DETECTED,
94 network_type,
95 network_number: NetworkNumber::configured(network_number)
96 .expect("validated Network Number"),
97 mac_address,
98 apdu_length,
99 bip: None,
100 binding: std::sync::Weak::new(),
101 })
102 }
103
104 pub fn set_description(&mut self, desc: impl Into<String>) {
106 self.description = desc.into();
107 }
108}
109
110impl BACnetObject for NetworkPortObject {
111 fn object_identifier(&self) -> ObjectIdentifier {
112 self.oid
113 }
114 fn object_name(&self) -> &str {
115 &self.name
116 }
117
118 fn read_property(
119 &self,
120 property: PropertyIdentifier,
121 array_index: Option<u32>,
122 ) -> Result<PropertyValue, Error> {
123 use PropertyIdentifier as P;
124 if let Some(result) = read_common_properties!(self, property, array_index) {
125 return result;
126 }
127 match property {
128 P::OBJECT_TYPE => Ok(PropertyValue::Enumerated(ObjectType::NETWORK_PORT.to_raw())),
129 P::NETWORK_TYPE => Ok(PropertyValue::Enumerated(self.network_type.to_raw())),
130 P::PROTOCOL_LEVEL => Ok(PropertyValue::Enumerated(2)),
131 P::NETWORK_NUMBER => Ok(PropertyValue::Unsigned(
132 self.network_number.snapshot().0.into(),
133 )),
134 P::NETWORK_NUMBER_QUALITY => Ok(PropertyValue::Enumerated(
135 self.network_number.snapshot().1.into(),
136 )),
137 P::MAC_ADDRESS => Ok(PropertyValue::OctetString(self.mac_address.to_vec())),
138 P::APDU_LENGTH => Ok(PropertyValue::Unsigned(self.apdu_length.into())),
139 P::LINK_SPEED => Ok(PropertyValue::Real(0.0)),
141 P::CHANGES_PENDING => Ok(PropertyValue::Boolean(false)),
142 _ => {
143 let config = self
144 .bip
145 .as_ref()
146 .ok_or_else(common::unknown_property_error)?;
147 match property {
148 P::BACNET_IP_MODE => Ok(PropertyValue::Enumerated(0)),
149 P::IP_ADDRESS => Ok(PropertyValue::OctetString(config.ip_address.to_vec())),
150 P::IP_SUBNET_MASK => {
151 Ok(PropertyValue::OctetString(config.subnet_mask.to_vec()))
152 }
153 P::IP_DEFAULT_GATEWAY => {
154 Ok(PropertyValue::OctetString(config.default_gateway.to_vec()))
155 }
156 P::BACNET_IP_UDP_PORT => Ok(PropertyValue::Unsigned(config.udp_port.into())),
157 P::IP_DNS_SERVER => match array_index {
158 None => Ok(PropertyValue::List(
159 config
160 .dns_servers
161 .iter()
162 .map(|ip| PropertyValue::OctetString(ip.to_vec()))
163 .collect(),
164 )),
165 Some(0) => Ok(PropertyValue::Unsigned(config.dns_servers.len() as u64)),
166 Some(index) => config
167 .dns_servers
168 .get(index as usize - 1)
169 .map(|ip| PropertyValue::OctetString(ip.to_vec()))
170 .ok_or_else(common::invalid_array_index_error),
171 },
172 _ => Err(common::unknown_property_error()),
173 }
174 }
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 property == PropertyIdentifier::OUT_OF_SERVICE && self.is_bound() {
186 return Err(common::write_access_denied_error());
187 }
188 if let Some(result) =
189 common::write_out_of_service(&mut self.out_of_service, property, &value)
190 {
191 return result;
192 }
193 if let Some(result) = common::write_description(&mut self.description, property, &value) {
194 return result;
195 }
196 if self
199 .property_metadata()
200 .iter()
201 .any(|row| row.property_identifier == property)
202 {
203 Err(common::write_access_denied_error())
204 } else {
205 Err(common::unknown_property_error())
206 }
207 }
208
209 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
210 metadata::for_object(self)
211 }
212 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
213 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
214 }
215 fn is_createable(&self) -> bool {
216 false
217 }
218 fn is_deleteable(&self) -> bool {
219 false
220 }
221}
222
223#[cfg(test)]
224mod tests;