1use crate::error::{EtherNetIpError, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::Path;
6use std::time::Duration;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct ProductionConfig {
11 pub connection: ConnectionConfig,
13 pub performance: PerformanceConfig,
15 pub monitoring: MonitoringConfig,
17 pub security: SecurityConfig,
19 pub logging: LoggingConfig,
21 pub plc_settings: HashMap<String, PlcSpecificConfig>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ConnectionConfig {
27 pub connection_timeout: Duration,
29 pub read_timeout: Duration,
31 pub write_timeout: Duration,
33 pub max_connections: u32,
35 pub retry_attempts: u32,
37 pub retry_delay: Duration,
39 pub keep_alive_interval: Duration,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PerformanceConfig {
45 pub max_packet_size: usize,
47 pub batch_config: BatchConfig,
49 pub connection_pool: ConnectionPoolConfig,
51 pub memory_limits: MemoryLimits,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct BatchConfig {
57 pub max_operations_per_batch: usize,
59 pub batch_timeout: Duration,
61 pub continue_on_error: bool,
63 pub optimize_packet_packing: bool,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ConnectionPoolConfig {
69 pub initial_size: u32,
71 pub max_size: u32,
73 pub growth_increment: u32,
75 pub idle_timeout: Duration,
77 pub cleanup_interval: Duration,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MemoryLimits {
83 pub max_memory_mb: usize,
85 pub warning_threshold_mb: usize,
87 pub enable_monitoring: bool,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct MonitoringConfig {
93 pub enabled: bool,
95 pub collection_interval: Duration,
97 pub health_check_interval: Duration,
99 pub retention_period: Duration,
101 pub enable_profiling: bool,
103 pub alert_thresholds: AlertThresholds,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct AlertThresholds {
109 pub error_rate_threshold: f64,
111 pub latency_threshold_ms: f64,
113 pub memory_threshold_mb: usize,
115 pub connection_failure_threshold: u32,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SecurityConfig {
121 pub enable_encryption: bool,
123 pub validate_connections: bool,
125 pub validate_inputs: bool,
127 pub rate_limiting: RateLimitingConfig,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct RateLimitingConfig {
133 pub enabled: bool,
135 pub max_requests_per_second: u32,
137 pub burst_capacity: u32,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct LoggingConfig {
143 pub level: LogLevel,
145 pub format: LogFormat,
147 pub file_path: Option<String>,
149 pub enable_console: bool,
151 pub enable_structured: bool,
153 pub rotation: LogRotationConfig,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct LogRotationConfig {
159 pub enabled: bool,
161 pub max_file_size_mb: usize,
163 pub max_files: usize,
165 pub schedule: LogRotationSchedule,
167}
168
169#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
170#[serde(rename_all = "lowercase")]
171pub enum LogLevel {
172 Trace,
173 Debug,
174 Info,
175 Warn,
176 Error,
177}
178
179#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
180#[serde(rename_all = "lowercase")]
181pub enum LogFormat {
182 Json,
183 Text,
184}
185
186#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
187#[serde(rename_all = "lowercase")]
188pub enum LogRotationSchedule {
189 Daily,
190 Weekly,
191 Monthly,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
195pub struct PlcSpecificConfig {
196 pub model: String,
198 pub connection_settings: HashMap<String, String>,
200 pub tag_discovery: TagDiscoveryConfig,
202 pub performance_tuning: HashMap<String, String>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct TagDiscoveryConfig {
208 pub enabled: bool,
210 pub interval: Duration,
212 pub cache_tags: bool,
214 pub max_tags: usize,
216}
217
218impl Default for ProductionConfig {
219 fn default() -> Self {
220 Self {
221 connection: ConnectionConfig {
222 connection_timeout: Duration::from_secs(10),
223 read_timeout: Duration::from_secs(5),
224 write_timeout: Duration::from_secs(5),
225 max_connections: 10,
226 retry_attempts: 3,
227 retry_delay: Duration::from_secs(1),
228 keep_alive_interval: Duration::from_secs(30),
229 },
230 performance: PerformanceConfig {
231 max_packet_size: 4000,
232 batch_config: BatchConfig {
233 max_operations_per_batch: 50,
234 batch_timeout: Duration::from_secs(10),
235 continue_on_error: true,
236 optimize_packet_packing: true,
237 },
238 connection_pool: ConnectionPoolConfig {
239 initial_size: 2,
240 max_size: 10,
241 growth_increment: 2,
242 idle_timeout: Duration::from_secs(300),
243 cleanup_interval: Duration::from_secs(60),
244 },
245 memory_limits: MemoryLimits {
246 max_memory_mb: 100,
247 warning_threshold_mb: 80,
248 enable_monitoring: true,
249 },
250 },
251 monitoring: MonitoringConfig {
252 enabled: true,
253 collection_interval: Duration::from_secs(30),
254 health_check_interval: Duration::from_secs(60),
255 retention_period: Duration::from_secs(86400), enable_profiling: false,
257 alert_thresholds: AlertThresholds {
258 error_rate_threshold: 0.05,
259 latency_threshold_ms: 1000.0,
260 memory_threshold_mb: 80,
261 connection_failure_threshold: 5,
262 },
263 },
264 security: SecurityConfig {
265 enable_encryption: false,
266 validate_connections: true,
267 validate_inputs: true,
268 rate_limiting: RateLimitingConfig {
269 enabled: true,
270 max_requests_per_second: 100,
271 burst_capacity: 200,
272 },
273 },
274 logging: LoggingConfig {
275 level: LogLevel::Info,
276 format: LogFormat::Json,
277 file_path: Some("logs/ethernet_ip.log".to_string()),
278 enable_console: true,
279 enable_structured: true,
280 rotation: LogRotationConfig {
281 enabled: true,
282 max_file_size_mb: 100,
283 max_files: 10,
284 schedule: LogRotationSchedule::Daily,
285 },
286 },
287 plc_settings: HashMap::new(),
288 }
289 }
290}
291
292impl ProductionConfig {
293 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
295 let content = fs::read_to_string(path)?;
296 let config: ProductionConfig =
297 toml::from_str(&content).map_err(|e| EtherNetIpError::Other(e.to_string()))?;
298 Ok(config)
299 }
300
301 pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
303 let content =
304 toml::to_string_pretty(self).map_err(|e| EtherNetIpError::Other(e.to_string()))?;
305 fs::write(path, content)?;
306 Ok(())
307 }
308
309 pub fn validate(&self) -> std::result::Result<(), Vec<String>> {
311 let mut errors = Vec::new();
312
313 if self.connection.connection_timeout.as_secs() == 0 {
315 errors.push("Connection timeout must be greater than 0".to_string());
316 }
317
318 if self.connection.max_connections == 0 {
319 errors.push("Maximum connections must be greater than 0".to_string());
320 }
321
322 if self.performance.max_packet_size < 100 {
324 errors.push("Maximum packet size must be at least 100 bytes".to_string());
325 }
326
327 if self.performance.batch_config.max_operations_per_batch == 0 {
328 errors.push("Maximum operations per batch must be greater than 0".to_string());
329 }
330
331 if self.monitoring.collection_interval.as_secs() == 0 {
333 errors.push("Collection interval must be greater than 0".to_string());
334 }
335
336 if self.security.rate_limiting.enabled
338 && self.security.rate_limiting.max_requests_per_second == 0
339 {
340 errors.push(
341 "Max requests per second must be greater than 0 when rate limiting is enabled"
342 .to_string(),
343 );
344 }
345
346 if errors.is_empty() {
347 Ok(())
348 } else {
349 Err(errors)
350 }
351 }
352
353 pub fn get_plc_config(&self, plc_address: &str) -> Option<&PlcSpecificConfig> {
355 self.plc_settings.get(plc_address)
356 }
357
358 pub fn set_plc_config(&mut self, plc_address: String, config: PlcSpecificConfig) {
360 self.plc_settings.insert(plc_address, config);
361 }
362
363 pub fn development() -> Self {
365 let mut config = Self::default();
366 config.logging.level = LogLevel::Debug;
367 config.monitoring.enabled = false;
368 config.security.rate_limiting.enabled = false;
369 config.performance.memory_limits.enable_monitoring = false;
370 config
371 }
372
373 pub fn production() -> Self {
375 let mut config = Self::default();
376 config.logging.level = LogLevel::Info;
377 config.monitoring.enabled = true;
378 config.security.rate_limiting.enabled = true;
379 config.performance.memory_limits.enable_monitoring = true;
380 config.performance.memory_limits.max_memory_mb = 500;
381 config
382 }
383}