Skip to main content

meterbus_wired_datalink/frame/field/
address.rs

1//! Wired M-Bus configured-slave and special-purpose data-link addresses.
2//!
3//! Every structured wired M-Bus frame contains a one-byte address field. Most
4//! values identify one configured slave, while values near the ends of the
5//! address space select protocol-defined modes such as unconfigured-slave
6//! access, selected-slave addressing, diagnosis, and broadcast.
7//!
8//! [`Address`] preserves the byte read from the wire. [`AddressKind`] explains
9//! how that value is used. Keeping both lets decoders and diagnostic tools
10//! report reserved or unexpected addresses clearly.
11//!
12//! # Address space
13//!
14//! | Value | [`AddressKind`] | Meaning | Response generally expected |
15//! | --- | --- | --- | --- |
16//! | `0` | [`AddressKind::Unconfigured`] | All unconfigured slaves | Yes |
17//! | `1..=250` | [`AddressKind::Primary`] | One configured slave | Yes |
18//! | `251` | [`AddressKind::PrimaryMasterRepeater`] | Primary-master repeater management | Yes |
19//! | `252` | [`AddressKind::Reserved`] | Reserved | No |
20//! | `253` | [`AddressKind::Secondary`] | Previously selected slave or slaves | Yes |
21//! | `254` | [`AddressKind::Test`] | Test and diagnosis | Yes |
22//! | `255` | [`AddressKind::Broadcast`] | All slaves | No |
23//!
24//! [`Address::expects_response`] implements the final column: it returns
25//! `false` only for the reserved and broadcast classes. It describes the
26//! address-level rule, not a guarantee that a particular request has a reply.
27//! The communication type, device capabilities, bus state, and transmission
28//! success can all affect whether a response is actually observed.
29//!
30//! Address 253 refers to slaves selected by an earlier application-layer
31//! operation; it does not contain their identity. Address 255 is broadcast, so
32//! slaves do not reply. Reserved values remain representable for diagnostics.
33//!
34//! # Examples
35//!
36//! Classify an ordinary configured-slave address:
37//!
38//! ```
39//! use meterbus_wired_datalink::{Address, AddressKind};
40//!
41//! let address = Address::new(42);
42//! assert_eq!(address.value(), 42);
43//! assert_eq!(address.kind(), AddressKind::Primary);
44//! assert!(address.expects_response());
45//! ```
46//!
47//! Detect a broadcast before waiting for a reply:
48//!
49//! ```
50//! use meterbus_wired_datalink::{Address, AddressKind};
51//!
52//! let address = Address::from(0xff);
53//! assert_eq!(address.kind(), AddressKind::Broadcast);
54//! assert!(!address.expects_response());
55//! ```
56//!
57//! Frame constructors do not reject reserved or special-purpose addresses. The
58//! caller decides whether an address is valid for the current operation.
59
60/// A data-link address.
61#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
62pub struct Address(u8);
63
64impl Address {
65    /// Creates an address from its wire value.
66    #[must_use]
67    pub const fn new(value: u8) -> Self {
68        Self(value)
69    }
70
71    /// Returns the wire value.
72    #[must_use]
73    pub const fn value(self) -> u8 {
74        self.0
75    }
76
77    /// Classifies the address according to EN 13757-2.
78    #[must_use]
79    pub const fn kind(self) -> AddressKind {
80        match self.0 {
81            0 => AddressKind::Unconfigured,
82            1..=250 => AddressKind::Primary,
83            251 => AddressKind::PrimaryMasterRepeater,
84            252 => AddressKind::Reserved,
85            253 => AddressKind::Secondary,
86            254 => AddressKind::Test,
87            255 => AddressKind::Broadcast,
88        }
89    }
90
91    /// Returns whether a slave response may be expected.
92    #[must_use]
93    pub const fn expects_response(self) -> bool {
94        !matches!(self.kind(), AddressKind::Reserved | AddressKind::Broadcast)
95    }
96}
97
98impl From<u8> for Address {
99    fn from(value: u8) -> Self {
100        Self::new(value)
101    }
102}
103
104/// The protocol-defined use of a data-link address.
105#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
106pub enum AddressKind {
107    /// Address 0, used by unconfigured slaves.
108    Unconfigured,
109    /// Configured slave address in the range 1 through 250.
110    Primary,
111    /// Address 251, used for primary-master repeater management.
112    PrimaryMasterRepeater,
113    /// Reserved address 252.
114    Reserved,
115    /// Address 253, used to address a previously selected slave.
116    Secondary,
117    /// Address 254, used for testing and diagnosis.
118    Test,
119    /// Broadcast address 255.
120    Broadcast,
121}
122
123#[cfg(test)]
124#[cfg_attr(coverage_nightly, coverage(off))]
125mod tests {
126    use super::*;
127
128    #[test]
129    fn classifies_every_address_range() {
130        let cases = [
131            (0, AddressKind::Unconfigured, true),
132            (1, AddressKind::Primary, true),
133            (250, AddressKind::Primary, true),
134            (251, AddressKind::PrimaryMasterRepeater, true),
135            (252, AddressKind::Reserved, false),
136            (253, AddressKind::Secondary, true),
137            (254, AddressKind::Test, true),
138            (255, AddressKind::Broadcast, false),
139        ];
140
141        for (value, kind, expects_response) in cases {
142            let address = Address::from(value);
143            assert_eq!(address.value(), value);
144            assert_eq!(address.kind(), kind);
145            assert_eq!(address.expects_response(), expects_response);
146        }
147    }
148
149    #[test]
150    fn reserved_address_remains_representable() {
151        assert_eq!(Address::new(252).value(), 252);
152        assert_eq!(Address::new(252).kind(), AddressKind::Reserved);
153    }
154}