Skip to main content

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;