Skip to main content

simple_doip/
logical_address.rs

1//! `DoIP` logical addressing ([`LogicalAddress`]), the identifier space used to
2//! address testers, ECUs, and gateways on a `DoIP` network, per ISO 13400-2.
3
4use core::fmt::{Debug, Display, LowerHex, UpperHex};
5#[cfg(feature = "std")]
6use tracing::info;
7
8#[derive(Clone, Copy, Eq)]
9/// Logical addressing is used to identify the ECU
10///
11/// A physical logical address uniquely represents a diagnostic application
12/// layer entity within any `DoIP` entity or on any server of the in-vehicle networks
13/// connected via `DoIP` gateways.
14pub struct LogicalAddress(
15    /// The 16-bit address value, as transmitted on the wire.
16    pub u16,
17);
18
19impl LogicalAddress {
20    /// Lower bound of the logical address range reserved for external test equipment
21    /// (testers). Addresses below this range are reserved
22    /// for other entity classes (e.g. `DoIP` gateways, ECUs).
23    pub const MIN_CLIENT_ADDRESS: LogicalAddress = LogicalAddress(0x0E00);
24    /// Upper bound of the logical address range reserved for external test equipment
25    /// (testers).
26    pub const MAX_CLIENT_ADDRESS: LogicalAddress = LogicalAddress(0x0FFF);
27
28    /// Sub-range of client addresses reserved for internal on-board diagnostics (OBD)
29    /// tooling rather than general external testers (0x0F00-0x0F7F).
30    /// A client address in this range is still valid, but
31    /// [`is_valid_client_address`](Self::is_valid_client_address) logs an
32    /// informational (`tracing::info!`) message
33    /// since this crate's use cases are external testers, not OBD tooling.
34    pub const OBD_ADDRESS_RANGE: (LogicalAddress, LogicalAddress) =
35        (LogicalAddress(0x0F00), LogicalAddress(0x0F7F));
36
37    /// Verify if the logical address is within the valid range for a client address
38    /// of 0x0E00 - 0x0FFF
39    #[must_use]
40    pub fn is_valid_client_address(&self) -> bool {
41        if *self >= Self::MIN_CLIENT_ADDRESS && *self <= Self::MAX_CLIENT_ADDRESS {
42            // Check if the logical address is in the OBD range
43            // For now we just log info to the user since this is a valid address,
44            // but it is not recommended to use this range for client addresses
45            // and is not in the use case of the crate at this time
46            if *self >= Self::OBD_ADDRESS_RANGE.0 && *self <= Self::OBD_ADDRESS_RANGE.1 {
47                #[cfg(feature = "std")]
48                info!(
49                    "Logical addresses in the 0x0F00-0x0F7F range are intended for internal \
50                data collection/on-board diagnostics only. Ensure that this is the intended use case."
51                );
52            }
53            true
54        } else {
55            false
56        }
57    }
58}
59
60impl Display for LogicalAddress {
61    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
62        write!(f, "{:#06X}", self.0)
63    }
64}
65impl Debug for LogicalAddress {
66    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
67        write!(f, "{:#06X}", self.0)
68    }
69}
70impl UpperHex for LogicalAddress {
71    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
72        UpperHex::fmt(&self.0, f)
73    }
74}
75impl LowerHex for LogicalAddress {
76    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
77        LowerHex::fmt(&self.0, f)
78    }
79}
80impl From<u16> for LogicalAddress {
81    fn from(addr: u16) -> Self {
82        LogicalAddress(addr)
83    }
84}
85impl From<LogicalAddress> for u16 {
86    fn from(addr: LogicalAddress) -> Self {
87        addr.0
88    }
89}
90impl PartialOrd<u16> for LogicalAddress {
91    fn partial_cmp(&self, other: &u16) -> Option<core::cmp::Ordering> {
92        self.0.partial_cmp(other)
93    }
94}
95impl PartialOrd<LogicalAddress> for LogicalAddress {
96    fn partial_cmp(&self, other: &LogicalAddress) -> Option<core::cmp::Ordering> {
97        self.0.partial_cmp(&other.0)
98    }
99}
100impl PartialEq<u16> for LogicalAddress {
101    fn eq(&self, other: &u16) -> bool {
102        self.0 == *other
103    }
104}
105impl PartialEq<LogicalAddress> for LogicalAddress {
106    fn eq(&self, other: &LogicalAddress) -> bool {
107        self.0 == other.0
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_logical_address() {
117        let addr = LogicalAddress(0x0E00);
118        assert!(addr.is_valid_client_address());
119        assert_eq!(addr.0, 0x0E00);
120        assert_eq!(addr, LogicalAddress(0x0E00));
121        assert_eq!(addr, 0x0E00);
122    }
123}