1use crate::{
2 ClientConfig, ClientError, ModbusClient, ReadDeviceIdentificationResponse,
3 ReportServerIdResponse, UnitId,
4};
5use rustmod_datalink::{DataLinkError, ModbusTcpTransport};
6use thiserror::Error;
7use tokio::runtime::Runtime;
8
9#[derive(Debug, Error)]
11#[non_exhaustive]
12pub enum SyncClientError {
13 #[error("runtime init error: {0}")]
15 RuntimeInit(std::io::Error),
16 #[error("datalink error: {0}")]
18 DataLink(#[from] DataLinkError),
19 #[error("client error: {0}")]
21 Client(#[from] ClientError),
22}
23
24pub struct SyncModbusTcpClient {
30 runtime: Runtime,
31 client: ModbusClient<ModbusTcpTransport>,
32}
33
34impl SyncModbusTcpClient {
35 pub fn connect(addr: &str) -> Result<Self, SyncClientError> {
37 Self::connect_with_config(addr, ClientConfig::default())
38 }
39
40 pub fn connect_with_config(addr: &str, config: ClientConfig) -> Result<Self, SyncClientError> {
42 let runtime = tokio::runtime::Builder::new_current_thread()
43 .enable_all()
44 .build()
45 .map_err(SyncClientError::RuntimeInit)?;
46 let link = runtime.block_on(ModbusTcpTransport::connect(addr))?;
47 let client = ModbusClient::with_config(link, config);
48 Ok(Self { runtime, client })
49 }
50
51 pub fn config(&self) -> ClientConfig {
53 self.client.config()
54 }
55
56 pub fn read_coils(
58 &self,
59 unit_id: UnitId,
60 start: u16,
61 quantity: u16,
62 ) -> Result<Vec<bool>, SyncClientError> {
63 self.runtime
64 .block_on(self.client.read_coils(unit_id, start, quantity))
65 .map_err(SyncClientError::Client)
66 }
67
68 pub fn read_discrete_inputs(
70 &self,
71 unit_id: UnitId,
72 start: u16,
73 quantity: u16,
74 ) -> Result<Vec<bool>, SyncClientError> {
75 self.runtime
76 .block_on(self.client.read_discrete_inputs(unit_id, start, quantity))
77 .map_err(SyncClientError::Client)
78 }
79
80 pub fn read_holding_registers(
82 &self,
83 unit_id: UnitId,
84 start: u16,
85 quantity: u16,
86 ) -> Result<Vec<u16>, SyncClientError> {
87 self.runtime
88 .block_on(self.client.read_holding_registers(unit_id, start, quantity))
89 .map_err(SyncClientError::Client)
90 }
91
92 pub fn read_input_registers(
94 &self,
95 unit_id: UnitId,
96 start: u16,
97 quantity: u16,
98 ) -> Result<Vec<u16>, SyncClientError> {
99 self.runtime
100 .block_on(self.client.read_input_registers(unit_id, start, quantity))
101 .map_err(SyncClientError::Client)
102 }
103
104 pub fn write_single_coil(
106 &self,
107 unit_id: UnitId,
108 address: u16,
109 value: bool,
110 ) -> Result<(), SyncClientError> {
111 self.runtime
112 .block_on(self.client.write_single_coil(unit_id, address, value))
113 .map_err(SyncClientError::Client)
114 }
115
116 pub fn write_single_register(
118 &self,
119 unit_id: UnitId,
120 address: u16,
121 value: u16,
122 ) -> Result<(), SyncClientError> {
123 self.runtime
124 .block_on(self.client.write_single_register(unit_id, address, value))
125 .map_err(SyncClientError::Client)
126 }
127
128 pub fn mask_write_register(
130 &self,
131 unit_id: UnitId,
132 address: u16,
133 and_mask: u16,
134 or_mask: u16,
135 ) -> Result<(), SyncClientError> {
136 self.runtime
137 .block_on(
138 self.client
139 .mask_write_register(unit_id, address, and_mask, or_mask),
140 )
141 .map_err(SyncClientError::Client)
142 }
143
144 pub fn write_multiple_coils(
146 &self,
147 unit_id: UnitId,
148 start: u16,
149 values: &[bool],
150 ) -> Result<(), SyncClientError> {
151 self.runtime
152 .block_on(self.client.write_multiple_coils(unit_id, start, values))
153 .map_err(SyncClientError::Client)
154 }
155
156 pub fn write_multiple_registers(
158 &self,
159 unit_id: UnitId,
160 start: u16,
161 values: &[u16],
162 ) -> Result<(), SyncClientError> {
163 self.runtime
164 .block_on(self.client.write_multiple_registers(unit_id, start, values))
165 .map_err(SyncClientError::Client)
166 }
167
168 pub fn read_write_multiple_registers(
170 &self,
171 unit_id: UnitId,
172 read_start: u16,
173 read_quantity: u16,
174 write_start: u16,
175 write_values: &[u16],
176 ) -> Result<Vec<u16>, SyncClientError> {
177 self.runtime
178 .block_on(self.client.read_write_multiple_registers(
179 unit_id,
180 read_start,
181 read_quantity,
182 write_start,
183 write_values,
184 ))
185 .map_err(SyncClientError::Client)
186 }
187
188 pub fn custom_request(
190 &self,
191 unit_id: UnitId,
192 function_code: u8,
193 payload: &[u8],
194 ) -> Result<Vec<u8>, SyncClientError> {
195 self.runtime
196 .block_on(self.client.custom_request(unit_id, function_code, payload))
197 .map_err(SyncClientError::Client)
198 }
199
200 pub fn report_server_id(&self, unit_id: UnitId) -> Result<ReportServerIdResponse, SyncClientError> {
202 self.runtime
203 .block_on(self.client.report_server_id(unit_id))
204 .map_err(SyncClientError::Client)
205 }
206
207 pub fn read_device_identification(
209 &self,
210 unit_id: UnitId,
211 read_device_id_code: u8,
212 object_id: u8,
213 ) -> Result<ReadDeviceIdentificationResponse, SyncClientError> {
214 self.runtime
215 .block_on(self.client.read_device_identification(
216 unit_id,
217 read_device_id_code,
218 object_id,
219 ))
220 .map_err(SyncClientError::Client)
221 }
222
223 pub fn read_coils_raw(
225 &self,
226 unit_id: UnitId,
227 start: u16,
228 quantity: u16,
229 ) -> Result<(Vec<u8>, u16), SyncClientError> {
230 self.runtime
231 .block_on(self.client.read_coils_raw(unit_id, start, quantity))
232 .map_err(SyncClientError::Client)
233 }
234
235 pub fn read_discrete_inputs_raw(
237 &self,
238 unit_id: UnitId,
239 start: u16,
240 quantity: u16,
241 ) -> Result<(Vec<u8>, u16), SyncClientError> {
242 self.runtime
243 .block_on(self.client.read_discrete_inputs_raw(unit_id, start, quantity))
244 .map_err(SyncClientError::Client)
245 }
246
247 pub fn read_exception_status(&self, unit_id: UnitId) -> Result<u8, SyncClientError> {
249 self.runtime
250 .block_on(self.client.read_exception_status(unit_id))
251 .map_err(SyncClientError::Client)
252 }
253
254 pub fn diagnostics(
256 &self,
257 unit_id: UnitId,
258 sub_function: u16,
259 data: u16,
260 ) -> Result<(u16, u16), SyncClientError> {
261 self.runtime
262 .block_on(self.client.diagnostics(unit_id, sub_function, data))
263 .map_err(SyncClientError::Client)
264 }
265
266 pub fn read_fifo_queue(
268 &self,
269 unit_id: UnitId,
270 address: u16,
271 ) -> Result<Vec<u16>, SyncClientError> {
272 self.runtime
273 .block_on(self.client.read_fifo_queue(unit_id, address))
274 .map_err(SyncClientError::Client)
275 }
276}