1use crate::{
2 ClientConfig, ClientError, ModbusClient, ReadDeviceIdentificationResponse,
3 ReportServerIdResponse,
4};
5use rustmod_datalink::{DataLinkError, ModbusTcpTransport};
6use thiserror::Error;
7use tokio::runtime::Runtime;
8
9#[derive(Debug, Error)]
10pub enum SyncClientError {
11 #[error("runtime init error: {0}")]
12 RuntimeInit(std::io::Error),
13 #[error("datalink error: {0}")]
14 DataLink(#[from] DataLinkError),
15 #[error("client error: {0}")]
16 Client(#[from] ClientError),
17}
18
19pub struct SyncModbusTcpClient {
20 runtime: Runtime,
21 client: ModbusClient<ModbusTcpTransport>,
22}
23
24impl SyncModbusTcpClient {
25 pub fn connect(addr: &str) -> Result<Self, SyncClientError> {
26 Self::connect_with_config(addr, ClientConfig::default())
27 }
28
29 pub fn connect_with_config(addr: &str, config: ClientConfig) -> Result<Self, SyncClientError> {
30 let runtime = tokio::runtime::Builder::new_multi_thread()
31 .enable_all()
32 .build()
33 .map_err(SyncClientError::RuntimeInit)?;
34 let link = runtime.block_on(ModbusTcpTransport::connect(addr))?;
35 let client = ModbusClient::with_config(link, config);
36 Ok(Self { runtime, client })
37 }
38
39 pub fn config(&self) -> ClientConfig {
40 self.client.config()
41 }
42
43 pub fn read_coils(
44 &self,
45 unit_id: u8,
46 start: u16,
47 quantity: u16,
48 ) -> Result<Vec<bool>, SyncClientError> {
49 self.runtime
50 .block_on(self.client.read_coils(unit_id, start, quantity))
51 .map_err(SyncClientError::Client)
52 }
53
54 pub fn read_discrete_inputs(
55 &self,
56 unit_id: u8,
57 start: u16,
58 quantity: u16,
59 ) -> Result<Vec<bool>, SyncClientError> {
60 self.runtime
61 .block_on(self.client.read_discrete_inputs(unit_id, start, quantity))
62 .map_err(SyncClientError::Client)
63 }
64
65 pub fn read_holding_registers(
66 &self,
67 unit_id: u8,
68 start: u16,
69 quantity: u16,
70 ) -> Result<Vec<u16>, SyncClientError> {
71 self.runtime
72 .block_on(self.client.read_holding_registers(unit_id, start, quantity))
73 .map_err(SyncClientError::Client)
74 }
75
76 pub fn read_input_registers(
77 &self,
78 unit_id: u8,
79 start: u16,
80 quantity: u16,
81 ) -> Result<Vec<u16>, SyncClientError> {
82 self.runtime
83 .block_on(self.client.read_input_registers(unit_id, start, quantity))
84 .map_err(SyncClientError::Client)
85 }
86
87 pub fn write_single_coil(
88 &self,
89 unit_id: u8,
90 address: u16,
91 value: bool,
92 ) -> Result<(), SyncClientError> {
93 self.runtime
94 .block_on(self.client.write_single_coil(unit_id, address, value))
95 .map_err(SyncClientError::Client)
96 }
97
98 pub fn write_single_register(
99 &self,
100 unit_id: u8,
101 address: u16,
102 value: u16,
103 ) -> Result<(), SyncClientError> {
104 self.runtime
105 .block_on(self.client.write_single_register(unit_id, address, value))
106 .map_err(SyncClientError::Client)
107 }
108
109 pub fn mask_write_register(
110 &self,
111 unit_id: u8,
112 address: u16,
113 and_mask: u16,
114 or_mask: u16,
115 ) -> Result<(), SyncClientError> {
116 self.runtime
117 .block_on(
118 self.client
119 .mask_write_register(unit_id, address, and_mask, or_mask),
120 )
121 .map_err(SyncClientError::Client)
122 }
123
124 pub fn write_multiple_coils(
125 &self,
126 unit_id: u8,
127 start: u16,
128 values: &[bool],
129 ) -> Result<(), SyncClientError> {
130 self.runtime
131 .block_on(self.client.write_multiple_coils(unit_id, start, values))
132 .map_err(SyncClientError::Client)
133 }
134
135 pub fn write_multiple_registers(
136 &self,
137 unit_id: u8,
138 start: u16,
139 values: &[u16],
140 ) -> Result<(), SyncClientError> {
141 self.runtime
142 .block_on(self.client.write_multiple_registers(unit_id, start, values))
143 .map_err(SyncClientError::Client)
144 }
145
146 pub fn read_write_multiple_registers(
147 &self,
148 unit_id: u8,
149 read_start: u16,
150 read_quantity: u16,
151 write_start: u16,
152 write_values: &[u16],
153 ) -> Result<Vec<u16>, SyncClientError> {
154 self.runtime
155 .block_on(self.client.read_write_multiple_registers(
156 unit_id,
157 read_start,
158 read_quantity,
159 write_start,
160 write_values,
161 ))
162 .map_err(SyncClientError::Client)
163 }
164
165 pub fn custom_request(
166 &self,
167 unit_id: u8,
168 function_code: u8,
169 payload: &[u8],
170 ) -> Result<Vec<u8>, SyncClientError> {
171 self.runtime
172 .block_on(self.client.custom_request(unit_id, function_code, payload))
173 .map_err(SyncClientError::Client)
174 }
175
176 pub fn report_server_id(&self, unit_id: u8) -> Result<ReportServerIdResponse, SyncClientError> {
177 self.runtime
178 .block_on(self.client.report_server_id(unit_id))
179 .map_err(SyncClientError::Client)
180 }
181
182 pub fn read_device_identification(
183 &self,
184 unit_id: u8,
185 read_device_id_code: u8,
186 object_id: u8,
187 ) -> Result<ReadDeviceIdentificationResponse, SyncClientError> {
188 self.runtime
189 .block_on(self.client.read_device_identification(
190 unit_id,
191 read_device_id_code,
192 object_id,
193 ))
194 .map_err(SyncClientError::Client)
195 }
196}