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