bacnet_rs/datalink/mod.rs
1//! BACnet Data Link Layer Module
2//!
3//! This module implements the data link layer functionality for BACnet communication protocol,
4//! providing support for various data link protocols used in building automation networks.
5//! The data link layer sits between the physical layer and the network layer in the BACnet
6//! protocol stack, handling frame-level communication.
7//!
8//! # Overview
9//!
10//! The data link layer is responsible for:
11//! - **Frame Assembly/Disassembly**: Constructing and parsing protocol-specific frames
12//! - **Address Management**: Handling different address formats (IP, MAC, MS/TP station)
13//! - **Error Detection**: CRC calculation and verification for data integrity
14//! - **Media Access Control**: Managing access to shared communication media
15//! - **Multi-Protocol Support**: Abstracting differences between various data link types
16//!
17//! # Supported Data Link Types
18//!
19//! ## BACnet/IP (Annex J)
20//! - UDP/IP based communication on port 47808 (0xBAC0)
21//! - BVLC (BACnet Virtual Link Control) for broadcast management
22//! - Foreign device registration and BBMD support
23//! - Most common in modern installations
24//!
25//! ## BACnet/Ethernet (ISO 8802-3)
26//! - Direct Ethernet frame communication
27//! - Uses Ethernet type 0x82DC for BACnet
28//! - LLC header for protocol identification
29//! - Suitable for high-speed local networks
30//!
31//! ## MS/TP (Master-Slave/Token-Passing)
32//! - RS-485 based serial communication
33//! - Token-passing for media access control
34//! - Supports up to 128 masters and 127 slaves
35//! - Common in field-level devices
36//!
37//! ## PTP (Point-to-Point)
38//! - Direct serial connection between two devices
39//! - Simplified protocol without token passing
40//! - Used for device configuration and testing
41//!
42//! ## ARCnet
43//! - Legacy token-passing network
44//! - Less common in modern installations
45//!
46//! # Architecture
47//!
48//! The module uses a trait-based design with the [`DataLink`] trait providing a common
49//! interface for all data link implementations. This allows upper layers to work with
50//! any data link type transparently.
51//!
52//! # Examples
53//!
54//! ## Creating a BACnet/IP Data Link
55//!
56//! ```no_run
57//! use bacnet_rs::datalink::{BacnetIpDataLink, DataLink, DataLinkAddress};
58//!
59//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
60//! // Create a BACnet/IP data link on the default port
61//! let mut data_link = BacnetIpDataLink::new("0.0.0.0:47808")?;
62//!
63//! // Send a frame to a specific IP address
64//! let frame_data = vec![0x01, 0x02, 0x03, 0x04];
65//! let dest_addr = "192.168.1.100:47808".parse()?;
66//! data_link.send_frame(&frame_data, &DataLinkAddress::Ip(dest_addr))?;
67//!
68//! // Send a broadcast frame
69//! data_link.send_frame(&frame_data, &DataLinkAddress::Broadcast)?;
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! ## Working with Different Data Link Types
75//!
76//! ```no_run
77//! use bacnet_rs::datalink::{DataLink, DataLinkType, DataLinkAddress};
78//!
79//! fn process_frame(data_link: &mut dyn DataLink) -> Result<(), Box<dyn std::error::Error>> {
80//! // The function works with any data link type
81//! match data_link.link_type() {
82//! DataLinkType::BacnetIp => println!("Using BACnet/IP"),
83//! DataLinkType::Ethernet => println!("Using Ethernet"),
84//! DataLinkType::MsTP => println!("Using MS/TP"),
85//! _ => println!("Using other data link type"),
86//! }
87//!
88//! // Receive a frame
89//! match data_link.receive_frame() {
90//! Ok((frame_data, source_addr)) => {
91//! println!("Received {} bytes from {:?}", frame_data.len(), source_addr);
92//! }
93//! Err(e) => println!("No frame received: {:?}", e),
94//! }
95//!
96//! Ok(())
97//! }
98//! ```
99//!
100//! # Feature Flags
101//!
102//! - `std`: Enables standard library features and network implementations (enabled by default)
103//! - Without `std`: Provides no_std compatible core functionality
104
105#[cfg(feature = "std")]
106use std::error::Error;
107
108#[cfg(feature = "std")]
109use std::fmt;
110
111#[cfg(not(feature = "std"))]
112use core::fmt;
113
114#[cfg(feature = "std")]
115use std::net::SocketAddr;
116
117#[cfg(not(feature = "std"))]
118use alloc::{string::String, vec::Vec};
119
120/// Result type for data link operations.
121///
122/// This type alias provides a convenient way to work with data link operation results,
123/// automatically using the appropriate [`DataLinkError`] type for the error case.
124///
125/// # Examples
126///
127/// ```
128/// use bacnet_rs::datalink::Result;
129///
130/// fn parse_frame(data: &[u8]) -> Result<Vec<u8>> {
131/// if data.is_empty() {
132/// return Err(bacnet_rs::datalink::DataLinkError::InvalidFrame);
133/// }
134/// Ok(data.to_vec())
135/// }
136/// ```
137#[cfg(feature = "std")]
138pub type Result<T> = std::result::Result<T, DataLinkError>;
139
140#[cfg(not(feature = "std"))]
141pub type Result<T> = core::result::Result<T, DataLinkError>;
142
143/// Errors that can occur during data link layer operations.
144///
145/// This enum represents all possible error conditions that can arise when working
146/// with the data link layer, including I/O errors, protocol violations, and
147/// validation failures.
148///
149/// # Examples
150///
151/// ```
152/// use bacnet_rs::datalink::{DataLinkError, Result};
153///
154/// fn validate_frame_size(size: usize) -> Result<()> {
155/// if size > 1500 {
156/// return Err(DataLinkError::InvalidFrame);
157/// }
158/// Ok(())
159/// }
160/// ```
161#[derive(Debug)]
162pub enum DataLinkError {
163 /// Network I/O error occurred.
164 ///
165 /// This variant wraps standard I/O errors that occur during network operations,
166 /// such as socket errors, timeouts, or connection failures.
167 #[cfg(feature = "std")]
168 IoError(std::io::Error),
169
170 /// Invalid frame format detected.
171 ///
172 /// This error indicates that a received frame does not conform to the expected
173 /// format for the data link type, such as invalid headers, incorrect frame
174 /// structure, or protocol violations.
175 InvalidFrame,
176
177 /// CRC check failed during frame validation.
178 ///
179 /// This error occurs when the calculated CRC/checksum does not match the
180 /// expected value, indicating data corruption during transmission.
181 CrcError,
182
183 /// Address resolution or validation failed.
184 ///
185 /// This error includes various address-related issues such as invalid address
186 /// formats, unreachable destinations, or address conflicts. The string provides
187 /// additional context about the specific issue.
188 AddressError(String),
189
190 /// Unsupported data link type for the requested operation.
191 ///
192 /// This error occurs when attempting to use a data link type that is not
193 /// supported by the current implementation or when mixing incompatible
194 /// address types with data link types.
195 UnsupportedType,
196}
197
198impl fmt::Display for DataLinkError {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 match self {
201 #[cfg(feature = "std")]
202 DataLinkError::IoError(e) => write!(f, "I/O error: {}", e),
203 DataLinkError::InvalidFrame => write!(f, "Invalid frame format"),
204 DataLinkError::CrcError => write!(f, "CRC check failed"),
205 DataLinkError::AddressError(msg) => write!(f, "Address error: {}", msg),
206 DataLinkError::UnsupportedType => write!(f, "Unsupported data link type"),
207 }
208 }
209}
210
211#[cfg(feature = "std")]
212impl Error for DataLinkError {}
213
214/// BACnet data link layer types supported by this implementation.
215///
216/// This enum identifies the different data link technologies that can be used
217/// for BACnet communication. Each type has different characteristics in terms
218/// of speed, cost, wiring requirements, and typical use cases.
219///
220/// # Examples
221///
222/// ```
223/// use bacnet_rs::datalink::DataLinkType;
224///
225/// fn get_default_port(link_type: DataLinkType) -> Option<u16> {
226/// match link_type {
227/// DataLinkType::BacnetIp => Some(47808),
228/// _ => None,
229/// }
230/// }
231/// ```
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub enum DataLinkType {
234 /// BACnet/IP (Annex J).
235 ///
236 /// Uses UDP/IP for communication, typically on port 47808. This is the most
237 /// common data link type in modern BACnet installations, providing good
238 /// performance and easy integration with existing IP networks.
239 BacnetIp,
240
241 /// BACnet/Ethernet (ISO 8802-3).
242 ///
243 /// Direct Ethernet frame communication using Ethernet type 0x82DC. Provides
244 /// high performance on local networks but requires Ethernet infrastructure
245 /// and may need special permissions for raw socket access.
246 Ethernet,
247
248 /// MS/TP (Master-Slave/Token-Passing).
249 ///
250 /// Serial communication over RS-485, using a token-passing protocol for
251 /// media access control. Common in field-level devices due to low cost
252 /// and long cable runs. Supports data rates from 9600 to 115200 bps.
253 MsTP,
254
255 /// PTP (Point-to-Point).
256 ///
257 /// Direct serial connection between two devices, typically used for device
258 /// configuration, testing, or isolated connections. Simpler than MS/TP as
259 /// it doesn't require token passing.
260 PointToPoint,
261
262 /// ARCnet.
263 ///
264 /// Legacy token-passing network technology. While still supported by the
265 /// BACnet standard, it is rarely used in new installations. Included for
266 /// compatibility with older systems.
267 Arcnet,
268}
269
270/// Common trait for all data link layer implementations.
271///
272/// This trait provides a unified interface for different data link technologies,
273/// allowing upper protocol layers to work with any data link type transparently.
274/// All data link implementations must provide frame sending/receiving capabilities,
275/// type identification, and local address information.
276///
277/// # Thread Safety
278///
279/// Implementations must be `Send + Sync` to allow use in multi-threaded contexts.
280/// Internal synchronization may be required for shared resources.
281///
282/// # Examples
283///
284/// ## Implementing a Custom Data Link
285///
286/// ```
287/// use bacnet_rs::datalink::{DataLink, DataLinkType, DataLinkAddress, Result};
288///
289/// struct CustomDataLink {
290/// // Implementation details
291/// }
292///
293/// impl DataLink for CustomDataLink {
294/// fn send_frame(&mut self, frame: &[u8], dest: &DataLinkAddress) -> Result<()> {
295/// // Send frame to destination
296/// Ok(())
297/// }
298///
299/// fn receive_frame(&mut self) -> Result<(Vec<u8>, DataLinkAddress)> {
300/// // Receive and return frame with source address
301/// Ok((vec![0x01, 0x02], DataLinkAddress::Broadcast))
302/// }
303///
304/// fn link_type(&self) -> DataLinkType {
305/// DataLinkType::PointToPoint
306/// }
307///
308/// fn local_address(&self) -> DataLinkAddress {
309/// DataLinkAddress::Broadcast
310/// }
311/// }
312/// ```
313///
314/// ## Using the Trait
315///
316/// ```
317/// use bacnet_rs::datalink::{DataLink, DataLinkAddress};
318///
319/// fn send_broadcast(data_link: &mut dyn DataLink, data: &[u8]) -> Result<(), Box<dyn std::error::Error>> {
320/// data_link.send_frame(data, &DataLinkAddress::Broadcast)?;
321/// Ok(())
322/// }
323/// ```
324pub trait DataLink: Send + Sync {
325 /// Send a frame to the specified destination address.
326 ///
327 /// This method takes the frame data (typically an NPDU) and sends it to the
328 /// specified destination using the appropriate data link protocol. The frame
329 /// data should not include data link headers, as these will be added by the
330 /// implementation.
331 ///
332 /// # Arguments
333 ///
334 /// * `frame` - The frame data to send (NPDU layer and above)
335 /// * `dest` - The destination address in the appropriate format for this data link type
336 ///
337 /// # Errors
338 ///
339 /// Returns an error if:
340 /// - The destination address type is incompatible with this data link type
341 /// - Network I/O errors occur
342 /// - The frame is too large for the data link type
343 fn send_frame(&mut self, frame: &[u8], dest: &DataLinkAddress) -> Result<()>;
344
345 /// Receive a frame from the data link.
346 ///
347 /// This method blocks until a frame is received or an error occurs. The returned
348 /// frame data excludes data link headers, containing only the NPDU and above.
349 /// The source address is provided to identify the sender.
350 ///
351 /// # Returns
352 ///
353 /// Returns a tuple containing:
354 /// - The received frame data (NPDU layer and above)
355 /// - The source address of the frame
356 ///
357 /// # Errors
358 ///
359 /// Returns an error if:
360 /// - No frame is available (timeout)
361 /// - Network I/O errors occur
362 /// - The received frame is invalid or corrupted
363 ///
364 /// # Note
365 ///
366 /// Implementations may use timeouts to prevent indefinite blocking.
367 fn receive_frame(&mut self) -> Result<(Vec<u8>, DataLinkAddress)>;
368
369 /// Get the data link type of this implementation.
370 ///
371 /// This method returns the specific type of data link technology used by
372 /// this implementation, allowing upper layers to make data link-specific
373 /// decisions if necessary.
374 ///
375 /// # Examples
376 ///
377 /// ```
378 /// use bacnet_rs::datalink::{DataLink, DataLinkType};
379 ///
380 /// fn is_ip_based(data_link: &dyn DataLink) -> bool {
381 /// data_link.link_type() == DataLinkType::BacnetIp
382 /// }
383 /// ```
384 fn link_type(&self) -> DataLinkType;
385
386 /// Get the local address of this data link.
387 ///
388 /// Returns the address that identifies this device on the data link network.
389 /// The format depends on the data link type:
390 /// - BACnet/IP: IP address and port
391 /// - Ethernet: MAC address
392 /// - MS/TP: Station address (0-254)
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// use bacnet_rs::datalink::{DataLink, DataLinkAddress};
398 ///
399 /// fn print_local_address(data_link: &dyn DataLink) {
400 /// match data_link.local_address() {
401 /// DataLinkAddress::Ip(addr) => println!("IP: {}", addr),
402 /// DataLinkAddress::Ethernet(mac) => println!("MAC: {:02X?}", mac),
403 /// DataLinkAddress::MsTP(station) => println!("MS/TP Station: {}", station),
404 /// _ => println!("Other address type"),
405 /// }
406 /// }
407 /// ```
408 fn local_address(&self) -> DataLinkAddress;
409}
410
411/// Data link layer address representation.
412///
413/// This enum provides a unified way to represent addresses across different
414/// data link types. Each variant corresponds to the addressing scheme used
415/// by a specific data link technology.
416///
417/// # Examples
418///
419/// ```
420/// use bacnet_rs::datalink::DataLinkAddress;
421/// # #[cfg(feature = "std")]
422/// # {
423/// use std::net::SocketAddr;
424///
425/// // IP address for BACnet/IP
426/// let ip_addr: SocketAddr = "192.168.1.100:47808".parse().unwrap();
427/// let addr = DataLinkAddress::Ip(ip_addr);
428///
429/// // MAC address for Ethernet
430/// let mac_addr = DataLinkAddress::Ethernet([0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
431///
432/// // MS/TP station address
433/// let mstp_addr = DataLinkAddress::MsTP(42);
434///
435/// // Broadcast to all devices
436/// let broadcast = DataLinkAddress::Broadcast;
437/// # }
438/// ```
439#[derive(Debug, Clone, PartialEq, Eq)]
440pub enum DataLinkAddress {
441 /// IP address and port for BACnet/IP communication.
442 ///
443 /// Used with BACnet/IP data links. The port is typically 47808 (0xBAC0)
444 /// but can be different for non-standard configurations or when multiple
445 /// BACnet networks share the same IP network.
446 #[cfg(feature = "std")]
447 Ip(SocketAddr),
448
449 /// Ethernet MAC address for direct Ethernet communication.
450 ///
451 /// Used with BACnet/Ethernet data links. The 6-byte array represents
452 /// a standard 48-bit MAC address. Special addresses include:
453 /// - `FF:FF:FF:FF:FF:FF` - Ethernet broadcast
454 /// - `01:00:5E:xx:xx:xx` - IPv4 multicast range
455 Ethernet([u8; 6]),
456
457 /// MS/TP station address.
458 ///
459 /// Used with MS/TP data links. Valid ranges:
460 /// - 0-127: Master nodes (can initiate communication)
461 /// - 128-254: Slave nodes (only respond to requests)
462 /// - 255: Broadcast address
463 MsTP(u8),
464
465 /// Broadcast address for sending to all devices.
466 ///
467 /// This is a logical broadcast that is translated to the appropriate
468 /// physical broadcast address for each data link type:
469 /// - BACnet/IP: UDP broadcast or multicast
470 /// - Ethernet: FF:FF:FF:FF:FF:FF
471 /// - MS/TP: Station address 255
472 Broadcast,
473}
474
475/// BACnet/IP (Annex J) implementation.
476///
477/// This module provides BACnet communication over IP networks using UDP port 47808.
478/// It includes BVLC (BACnet Virtual Link Control) for broadcast management, foreign
479/// device registration, and BBMD (BACnet Broadcast Management Device) support.
480pub mod bip;
481
482/// BACnet/Ethernet (ISO 8802-3) implementation.
483///
484/// This module provides direct Ethernet frame communication for BACnet, using
485/// Ethernet type 0x82DC and LLC headers for protocol identification. It offers
486/// high performance on local networks.
487pub mod ethernet;
488
489/// MS/TP (Master-Slave/Token-Passing) implementation.
490///
491/// This module provides BACnet communication over RS-485 serial links using a
492/// token-passing protocol. It's commonly used for field-level devices due to
493/// its low cost and ability to support long cable runs.
494pub mod mstp;
495
496/// Frame validation and analysis utilities.
497///
498/// This module provides comprehensive validation functions for all supported
499/// data link types, including structure validation, CRC verification, and
500/// pattern detection for troubleshooting.
501pub mod validation;
502
503#[cfg(feature = "std")]
504pub use bip::BacnetIpDataLink;
505
506#[cfg(feature = "std")]
507pub use ethernet::EthernetDataLink;
508
509#[cfg(feature = "std")]
510pub use mstp::MstpDataLink;