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
// SPDX-License-Identifier: MIT OR Apache-2.0
//! Modbus server (slave) service trait.
use async_trait;
use crate;
/// A Modbus server service — handles incoming requests and produces responses.
///
/// Implement this trait to create a custom Modbus server. The transport loops
/// (TCP, RTU, ASCII) call [`call`](Service::call) for each decoded request.
///
/// For hooking into the request/response lifecycle without reimplementing the
/// entire trait, see [`ServerHook`] and [`HookedService`].
///
/// # Example
///
/// ```no_run
/// use async_trait::async_trait;
/// use oms_modbus::frame::{Request, Response, Exception};
/// use oms_modbus::server::Service;
///
/// /// A fixed-value service — always returns the same register value.
/// struct FixedService { value: u16 }
///
/// #[async_trait]
/// impl Service for FixedService {
/// async fn call(&self, request: Request<'_>) -> Result<Response, Exception> {
/// match request {
/// Request::ReadHoldingRegisters(_addr, qty) =>
/// Ok(Response::ReadHoldingRegisters(vec![self.value; qty as usize])),
/// _ => Err(Exception::IllegalFunction),
/// }
/// }
/// }
/// ```
// ── Server hook support ─────────────────────────────────────────────────────
/// Thread-local request context — set by `process_server_request` before
/// calling [`Service::call`]. Hooks use this to see which slave the request
/// is addressed to without changing the `Service` trait signature.
/// A hook that intercepts server requests — compose with [`HookedService`].
///
/// All methods have default no-op implementations. Implement only the hooks
/// you need.
///
/// # Examples
///
/// ```no_run
/// use oms_modbus::*;
/// use async_trait::async_trait;
///
/// // Log every request/response pair
/// struct LogHook;
/// #[async_trait]
/// impl ServerHook for LogHook {
/// async fn after_call(
/// &self, slave: u8,
/// result: Result<Response, Exception>,
/// ) -> Result<Response, Exception> {
/// println!("[slave={slave}] {:?}", result);
/// result
/// }
/// }
/// ```
/// A [`Service`] wrapper that applies a [`ServerHook`] around another service.
///
/// # Examples
///
/// ```no_run
/// use oms_modbus::*;
/// use std::sync::Arc;
///
/// # async fn example() {
/// let store = Arc::new(SlaveStore::new());
/// # struct DummyHook; #[async_trait::async_trait] impl ServerHook for DummyHook {}
/// let hook = DummyHook;
/// let hooked = HookedService::new(store, hook);
/// // Pass `hooked` to any serve_forever() — it implements Service.
/// # }
/// ```