1use std::net::SocketAddr;
4use std::time::Duration;
5
6use mabi_core::tags::Tags;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ModbusServerConfig {
12 #[serde(default = "default_bind_address")]
14 pub bind_address: SocketAddr,
15
16 #[serde(default = "default_max_connections")]
18 pub max_connections: usize,
19
20 #[serde(default = "default_timeout_secs")]
22 pub timeout_secs: u64,
23
24 #[serde(default = "default_true")]
26 pub keep_alive: bool,
27
28 #[serde(default = "default_true")]
30 pub tcp_nodelay: bool,
31
32 #[serde(default)]
34 pub rate_limit: u32,
35}
36
37fn default_bind_address() -> SocketAddr {
38 "0.0.0.0:502".parse().unwrap()
39}
40
41fn default_max_connections() -> usize {
42 1000
43}
44
45fn default_timeout_secs() -> u64 {
46 30
47}
48
49fn default_true() -> bool {
50 true
51}
52
53impl Default for ModbusServerConfig {
54 fn default() -> Self {
55 Self {
56 bind_address: default_bind_address(),
57 max_connections: default_max_connections(),
58 timeout_secs: default_timeout_secs(),
59 keep_alive: true,
60 tcp_nodelay: true,
61 rate_limit: 0,
62 }
63 }
64}
65
66impl ModbusServerConfig {
67 pub fn with_bind_address(mut self, addr: SocketAddr) -> Self {
69 self.bind_address = addr;
70 self
71 }
72
73 pub fn with_max_connections(mut self, max: usize) -> Self {
75 self.max_connections = max;
76 self
77 }
78
79 pub fn timeout(&self) -> Duration {
81 Duration::from_secs(self.timeout_secs)
82 }
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ModbusDeviceConfig {
88 pub unit_id: u8,
90
91 pub name: String,
93
94 #[serde(default = "default_coils")]
96 pub coils: u16,
97
98 #[serde(default = "default_discrete_inputs")]
100 pub discrete_inputs: u16,
101
102 #[serde(default = "default_holding_registers")]
104 pub holding_registers: u16,
105
106 #[serde(default = "default_input_registers")]
108 pub input_registers: u16,
109
110 #[serde(default)]
112 pub response_delay_ms: u64,
113
114 #[serde(default, skip_serializing_if = "Tags::is_empty")]
116 pub tags: Tags,
117}
118
119fn default_coils() -> u16 {
120 10000
121}
122
123fn default_discrete_inputs() -> u16 {
124 10000
125}
126
127fn default_holding_registers() -> u16 {
128 10000
129}
130
131fn default_input_registers() -> u16 {
132 10000
133}
134
135impl Default for ModbusDeviceConfig {
136 fn default() -> Self {
137 Self {
138 unit_id: 1,
139 name: "Modbus Device".to_string(),
140 coils: default_coils(),
141 discrete_inputs: default_discrete_inputs(),
142 holding_registers: default_holding_registers(),
143 input_registers: default_input_registers(),
144 response_delay_ms: 0,
145 tags: Tags::new(),
146 }
147 }
148}
149
150impl ModbusDeviceConfig {
151 pub fn new(unit_id: u8, name: impl Into<String>) -> Self {
153 Self {
154 unit_id,
155 name: name.into(),
156 ..Default::default()
157 }
158 }
159
160 pub fn with_response_delay(mut self, delay_ms: u64) -> Self {
162 self.response_delay_ms = delay_ms;
163 self
164 }
165
166 pub fn with_tags(mut self, tags: Tags) -> Self {
168 self.tags = tags;
169 self
170 }
171
172 pub fn with_tag(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
174 self.tags.insert(key.into(), value.into());
175 self
176 }
177
178 pub fn with_label(mut self, label: impl Into<String>) -> Self {
180 self.tags.add_label(label.into());
181 self
182 }
183
184 pub fn validate(&self) -> Result<(), String> {
186 if self.unit_id == 0 || self.unit_id > 247 {
187 return Err(format!("Invalid unit ID: {} (must be 1-247)", self.unit_id));
188 }
189 Ok(())
190 }
191}