1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
//! The core `Transport`, `AsyncTransport`, and `TimeKeeper` traits.
use crate::;
use Vec;
use Future;
use ;
/// A unified trait defining the interface for any Modbus physical or network transport layer.
///
/// This trait abstracts the underlying communication medium (e.g., TCP socket, Serial COM port,
/// or a mocked in-memory buffer) so that the higher-level Modbus Client Services can orchestrate
/// transactions without needing to know the specifics of the hardware layer.
///
/// # Implementor Responsibilities
/// Implementors of this trait must ensure:
/// - **Connection Management**: Handling the initialization and teardown of the physical link.
/// - **Framing**: Reading exactly one complete Modbus Application Data Unit (ADU) at a time for TCP.
/// For TCP, this means parsing the MBAP header to determine the length. For Serial (RTU), this
/// involves managing inter-frame timing silences or LRC/CRCs. In other words, just provide the available bytes;
/// the protocol stack is intelligent enough to construct the full frame. If a timeout occurs, the stack will clear the buffer.
/// A trait for abstracting time-related operations, primarily for mocking in tests
/// and providing a consistent interface for `no_std` environments.
/// Async transport abstraction for Modbus communication.
///
/// This trait is the async parallel of the sync [`Transport`] trait.
/// Implementations live in `mbus-network` (`TokioTcpTransport`) and `mbus-serial`
/// (`TokioRtuTransport`, `TokioAsciiTransport`) behind their respective `async` feature flags.
///
/// # Compile-time metadata
///
/// Unlike the sync trait, async transport metadata is exposed as associated constants
/// instead of runtime methods. This eliminates instance borrows when the protocol stack
/// needs to know transport family for framing or broadcast decisions.
///
/// - [`SUPPORTS_BROADCAST_WRITES`](AsyncTransport::SUPPORTS_BROADCAST_WRITES) — set to
/// `true` only for transports that can safely apply Modbus broadcast writes
/// (address `0`) with no response (typically Serial RTU/ASCII).
/// - [`TRANSPORT_TYPE`](AsyncTransport::TRANSPORT_TYPE) — declares the transport family
/// for framing decisions (TCP vs RTU vs ASCII ADU layout).
///
/// Concrete implementations bind their transport family at the type level (often via
/// const generics for serial RTU/ASCII), so per-instance variance is intentionally
/// not supported.
///
/// # Framing contract
///
/// Unlike the sync `Transport::recv()` which returns whatever bytes are available,
/// `AsyncTransport::recv()` **must not return until exactly one complete ADU is ready**:
///
/// - **TCP**: reads the 6-byte MBAP prefix, parses the length field, then reads exactly that
/// many remaining bytes. Always returns a complete, valid-length frame.
/// - **Serial RTU**: accumulates bytes and returns when the inter-frame silence timer fires
/// (3.5 character times). The timer resets on every received byte.
/// The timer is only started after the first byte arrives — silence before any data
/// is not treated as a frame boundary.
/// - **Serial ASCII**: accumulates bytes until the `\r\n` terminator is found.
///
/// # Send bounds
///
/// Both `send` and `recv` return futures that are `Send`, enabling their use with
/// `tokio::spawn` without boxing. Implementations using `async fn` syntax are accepted
/// by the compiler as long as all captured state is `Send`.