1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::fs;
4use std::path::Path;
5use std::time::Duration;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ProductionConfig {
10 pub connection: ConnectionConfig,
12 pub performance: PerformanceConfig,
14 pub monitoring: MonitoringConfig,
16 pub security: SecurityConfig,
18 pub logging: LoggingConfig,
20 pub plc_settings: HashMap<String, PlcSpecificConfig>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ConnectionConfig {
26 pub connection_timeout: Duration,
28 pub read_timeout: Duration,
30 pub write_timeout: Duration,
32 pub max_connections: u32,
34 pub retry_attempts: u32,
36 pub retry_delay: Duration,
38 pub keep_alive_interval: Duration,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct PerformanceConfig {
44 pub max_packet_size: usize,
46 pub batch_config: BatchConfig,
48 pub connection_pool: ConnectionPoolConfig,
50 pub memory_limits: MemoryLimits,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct BatchConfig {
56 pub max_operations_per_batch: usize,
58 pub batch_timeout: Duration,
60 pub continue_on_error: bool,
62 pub optimize_packet_packing: bool,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct ConnectionPoolConfig {
68 pub initial_size: u32,
70 pub max_size: u32,
72 pub growth_increment: u32,
74 pub idle_timeout: Duration,
76 pub cleanup_interval: Duration,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct MemoryLimits {
82 pub max_memory_mb: usize,
84 pub warning_threshold_mb: usize,
86 pub enable_monitoring: bool,
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct MonitoringConfig {
92 pub enabled: bool,
94 pub collection_interval: Duration,
96 pub health_check_interval: Duration,
98 pub retention_period: Duration,
100 pub enable_profiling: bool,
102 pub alert_thresholds: AlertThresholds,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct AlertThresholds {
108 pub error_rate_threshold: f64,
110 pub latency_threshold_ms: f64,
112 pub memory_threshold_mb: usize,
114 pub connection_failure_threshold: u32,
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct SecurityConfig {
120 pub enable_encryption: bool,
122 pub validate_connections: bool,
124 pub validate_inputs: bool,
126 pub rate_limiting: RateLimitingConfig,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct RateLimitingConfig {
132 pub enabled: bool,
134 pub max_requests_per_second: u32,
136 pub burst_capacity: u32,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct LoggingConfig {
142 pub level: String,
144 pub format: String,
146 pub file_path: Option<String>,
148 pub enable_console: bool,
150 pub enable_structured: bool,
152 pub rotation: LogRotationConfig,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct LogRotationConfig {
158 pub enabled: bool,
160 pub max_file_size_mb: usize,
162 pub max_files: usize,
164 pub schedule: String,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct PlcSpecificConfig {
170 pub model: String,
172 pub connection_settings: HashMap<String, String>,
174 pub tag_discovery: TagDiscoveryConfig,
176 pub performance_tuning: HashMap<String, String>,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct TagDiscoveryConfig {
182 pub enabled: bool,
184 pub interval: Duration,
186 pub cache_tags: bool,
188 pub max_tags: usize,
190}
191
192impl Default for ProductionConfig {
193 fn default() -> Self {
194 Self {
195 connection: ConnectionConfig {
196 connection_timeout: Duration::from_secs(10),
197 read_timeout: Duration::from_secs(5),
198 write_timeout: Duration::from_secs(5),
199 max_connections: 10,
200 retry_attempts: 3,
201 retry_delay: Duration::from_secs(1),
202 keep_alive_interval: Duration::from_secs(30),
203 },
204 performance: PerformanceConfig {
205 max_packet_size: 4000,
206 batch_config: BatchConfig {
207 max_operations_per_batch: 50,
208 batch_timeout: Duration::from_secs(10),
209 continue_on_error: true,
210 optimize_packet_packing: true,
211 },
212 connection_pool: ConnectionPoolConfig {
213 initial_size: 2,
214 max_size: 10,
215 growth_increment: 2,
216 idle_timeout: Duration::from_secs(300),
217 cleanup_interval: Duration::from_secs(60),
218 },
219 memory_limits: MemoryLimits {
220 max_memory_mb: 100,
221 warning_threshold_mb: 80,
222 enable_monitoring: true,
223 },
224 },
225 monitoring: MonitoringConfig {
226 enabled: true,
227 collection_interval: Duration::from_secs(30),
228 health_check_interval: Duration::from_secs(60),
229 retention_period: Duration::from_secs(86400), enable_profiling: false,
231 alert_thresholds: AlertThresholds {
232 error_rate_threshold: 0.05,
233 latency_threshold_ms: 1000.0,
234 memory_threshold_mb: 80,
235 connection_failure_threshold: 5,
236 },
237 },
238 security: SecurityConfig {
239 enable_encryption: false,
240 validate_connections: true,
241 validate_inputs: true,
242 rate_limiting: RateLimitingConfig {
243 enabled: true,
244 max_requests_per_second: 100,
245 burst_capacity: 200,
246 },
247 },
248 logging: LoggingConfig {
249 level: "info".to_string(),
250 format: "json".to_string(),
251 file_path: Some("logs/ethernet_ip.log".to_string()),
252 enable_console: true,
253 enable_structured: true,
254 rotation: LogRotationConfig {
255 enabled: true,
256 max_file_size_mb: 100,
257 max_files: 10,
258 schedule: "daily".to_string(),
259 },
260 },
261 plc_settings: HashMap::new(),
262 }
263 }
264}
265
266impl ProductionConfig {
267 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, Box<dyn std::error::Error>> {
269 let content = fs::read_to_string(path)?;
270 let config: ProductionConfig = toml::from_str(&content)?;
271 Ok(config)
272 }
273
274 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<(), Box<dyn std::error::Error>> {
276 let content = toml::to_string_pretty(self)?;
277 fs::write(path, content)?;
278 Ok(())
279 }
280
281 pub fn validate(&self) -> Result<(), Vec<String>> {
283 let mut errors = Vec::new();
284
285 if self.connection.connection_timeout.as_secs() == 0 {
287 errors.push("Connection timeout must be greater than 0".to_string());
288 }
289
290 if self.connection.max_connections == 0 {
291 errors.push("Maximum connections must be greater than 0".to_string());
292 }
293
294 if self.performance.max_packet_size < 100 {
296 errors.push("Maximum packet size must be at least 100 bytes".to_string());
297 }
298
299 if self.performance.batch_config.max_operations_per_batch == 0 {
300 errors.push("Maximum operations per batch must be greater than 0".to_string());
301 }
302
303 if self.monitoring.collection_interval.as_secs() == 0 {
305 errors.push("Collection interval must be greater than 0".to_string());
306 }
307
308 if self.security.rate_limiting.enabled
310 && self.security.rate_limiting.max_requests_per_second == 0
311 {
312 errors.push(
313 "Max requests per second must be greater than 0 when rate limiting is enabled"
314 .to_string(),
315 );
316 }
317
318 let valid_levels = ["trace", "debug", "info", "warn", "error"];
320 if !valid_levels.contains(&self.logging.level.as_str()) {
321 errors.push(format!(
322 "Invalid log level: {}. Must be one of: {:?}",
323 self.logging.level, valid_levels
324 ));
325 }
326
327 if errors.is_empty() {
328 Ok(())
329 } else {
330 Err(errors)
331 }
332 }
333
334 pub fn get_plc_config(&self, plc_address: &str) -> Option<&PlcSpecificConfig> {
336 self.plc_settings.get(plc_address)
337 }
338
339 pub fn set_plc_config(&mut self, plc_address: String, config: PlcSpecificConfig) {
341 self.plc_settings.insert(plc_address, config);
342 }
343
344 pub fn development() -> Self {
346 let mut config = Self::default();
347 config.logging.level = "debug".to_string();
348 config.monitoring.enabled = false;
349 config.security.rate_limiting.enabled = false;
350 config.performance.memory_limits.enable_monitoring = false;
351 config
352 }
353
354 pub fn production() -> Self {
356 let mut config = Self::default();
357 config.logging.level = "info".to_string();
358 config.monitoring.enabled = true;
359 config.security.rate_limiting.enabled = true;
360 config.performance.memory_limits.enable_monitoring = true;
361 config.performance.memory_limits.max_memory_mb = 500;
362 config
363 }
364}