1use crate::error::ModbusError;
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::Arc;
5
6#[derive(Debug, Clone)]
7pub struct ApplicationDataUnit {
8 pub transaction: Option<u16>,
9 pub unit: u8,
10 pub fc: u8,
11 pub data: Vec<u8>,
12}
13
14impl ApplicationDataUnit {
15 pub fn new(unit: u8, fc: u8, data: Vec<u8>) -> Self {
16 Self {
17 transaction: None,
18 unit,
19 fc,
20 data,
21 }
22 }
23
24 pub fn with_transaction(mut self, transaction: u16) -> Self {
25 self.transaction = Some(transaction);
26 self
27 }
28}
29
30#[derive(Debug, Clone)]
31pub struct FramedDataUnit {
32 pub adu: ApplicationDataUnit,
33 pub raw: Vec<u8>,
34}
35
36#[derive(Debug, Clone)]
40pub struct ServerId {
41 pub server_id: Vec<u8>,
43 pub run_indicator_status: bool,
44 pub additional_data: Vec<u8>,
45}
46
47impl ServerId {
48 pub fn single(id: u8, run_indicator: bool, additional_data: Vec<u8>) -> Self {
50 Self {
51 server_id: vec![id],
52 run_indicator_status: run_indicator,
53 additional_data,
54 }
55 }
56}
57
58#[derive(Debug, Clone)]
59pub struct DeviceIdentification {
60 pub read_device_id_code: u8,
61 pub conformity_level: u8,
62 pub more_follows: bool,
63 pub next_object_id: u8,
64 pub objects: Vec<DeviceObject>,
65}
66
67#[derive(Debug, Clone)]
68pub struct DeviceObject {
69 pub id: u8,
70 pub value: String,
71}
72
73#[derive(Debug, Clone, Default)]
74pub struct AddressRange {
75 pub discrete_inputs: Vec<(u16, u16)>,
76 pub coils: Vec<(u16, u16)>,
77 pub input_registers: Vec<(u16, u16)>,
78 pub holding_registers: Vec<(u16, u16)>,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum CustomFcPredict {
87 Length(usize),
88 NeedMore,
89}
90
91pub type CustomFcHandleResult =
93 Pin<Box<dyn Future<Output = Result<Vec<u8>, ModbusError>> + Send + 'static>>;
94
95pub type CustomFcHandler = Arc<dyn Fn(Vec<u8>, u8) -> CustomFcHandleResult + Send + Sync + 'static>;
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct MasterResponse<T> {
102 pub transaction: Option<u16>,
103 pub unit: u8,
104 pub fc: u8,
105 pub data: T,
106 pub raw: Vec<u8>,
107}
108
109impl<T: PartialEq> PartialEq<T> for MasterResponse<T> {
110 fn eq(&self, other: &T) -> bool {
111 self.data == *other
112 }
113}
114
115#[derive(Clone)]
127#[allow(clippy::type_complexity)]
128pub struct CustomFunctionCode {
129 pub fc: u8,
131 pub predict_request_length: Arc<dyn Fn(&[u8]) -> CustomFcPredict + Send + Sync + 'static>,
133 pub predict_response_length: Arc<dyn Fn(&[u8]) -> CustomFcPredict + Send + Sync + 'static>,
135 pub handle: Option<CustomFcHandler>,
139}
140
141impl std::fmt::Debug for CustomFunctionCode {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 f.debug_struct("CustomFunctionCode")
144 .field("fc", &self.fc)
145 .field("handle", &self.handle.is_some())
146 .finish()
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_adu_new() {
156 let adu = ApplicationDataUnit::new(1, 0x03, vec![0x00, 0x00, 0x00, 0x0a]);
157 assert_eq!(adu.unit, 1);
158 assert_eq!(adu.fc, 0x03);
159 assert_eq!(adu.data, vec![0x00, 0x00, 0x00, 0x0a]);
160 assert_eq!(adu.transaction, None);
161 }
162
163 #[test]
164 fn test_adu_with_transaction() {
165 let adu = ApplicationDataUnit::new(1, 0x03, vec![]).with_transaction(42);
166 assert_eq!(adu.transaction, Some(42));
167 }
168
169 #[test]
170 fn test_framed_data_unit() {
171 let adu = ApplicationDataUnit::new(1, 0x03, vec![0x00, 0x01]);
172 let raw = vec![0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x01, 0x03, 0x00, 0x01];
173 let frame = FramedDataUnit {
174 adu,
175 raw: raw.clone(),
176 };
177 assert_eq!(frame.raw, raw);
178 }
179
180 #[test]
181 fn test_server_id_single() {
182 let sid = ServerId::single(1, true, vec![1, 2, 3]);
183 assert_eq!(sid.server_id, vec![1]);
184 assert!(sid.run_indicator_status);
185 assert_eq!(sid.additional_data, vec![1, 2, 3]);
186 }
187
188 #[test]
189 fn test_server_id_multi() {
190 let sid = ServerId {
191 server_id: vec![0x01, 0x02, 0x03],
192 run_indicator_status: false,
193 additional_data: vec![0xab, 0xcd],
194 };
195 assert_eq!(sid.server_id.len(), 3);
196 assert!(!sid.run_indicator_status);
197 }
198
199 #[test]
200 fn test_device_object() {
201 let obj = DeviceObject {
202 id: 0x01,
203 value: "ProductCode".to_string(),
204 };
205 assert_eq!(obj.id, 0x01);
206 assert_eq!(obj.value, "ProductCode");
207 }
208
209 #[test]
210 fn test_device_identification() {
211 let di = DeviceIdentification {
212 read_device_id_code: 0x01,
213 conformity_level: 0x81,
214 more_follows: false,
215 next_object_id: 0x00,
216 objects: vec![
217 DeviceObject {
218 id: 0x00,
219 value: "VendorName".to_string(),
220 },
221 DeviceObject {
222 id: 0x01,
223 value: "ProductCode".to_string(),
224 },
225 ],
226 };
227 assert_eq!(di.read_device_id_code, 0x01);
228 assert_eq!(di.conformity_level, 0x81);
229 assert!(!di.more_follows);
230 assert_eq!(di.objects.len(), 2);
231 }
232
233 #[test]
234 fn test_address_range_default() {
235 let range = AddressRange::default();
236 assert!(range.coils.is_empty());
237 assert!(range.discrete_inputs.is_empty());
238 assert!(range.input_registers.is_empty());
239 assert!(range.holding_registers.is_empty());
240 }
241
242 #[test]
243 fn test_custom_function_code_construction() {
244 let cfc = CustomFunctionCode {
245 fc: 0x65,
246 predict_request_length: Arc::new(|_| CustomFcPredict::Length(8)),
247 predict_response_length: Arc::new(|_| CustomFcPredict::Length(8)),
248 handle: None,
249 };
250 assert_eq!(cfc.fc, 0x65);
251 assert!(cfc.handle.is_none());
252 }
253}