Skip to main content

rust_ethernet_ip/
config.rs

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/// Production configuration for EtherNet/IP library
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[deprecated(
11    since = "1.2.0",
12    note = "ProductionConfig is not consumed by EipClient and implies unsupported enforcement; configure EipClient/Client/Fleet directly. The type will be removed in 2.0."
13)]
14pub struct ProductionConfig {
15    /// Connection settings
16    pub connection: ConnectionConfig,
17    /// Performance settings
18    pub performance: PerformanceConfig,
19    /// Monitoring settings
20    pub monitoring: MonitoringConfig,
21    /// Security settings
22    pub security: SecurityConfig,
23    /// Logging settings
24    pub logging: LoggingConfig,
25    /// PLC-specific settings
26    pub plc_settings: HashMap<String, PlcSpecificConfig>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct ConnectionConfig {
31    /// Default connection timeout
32    pub connection_timeout: Duration,
33    /// Default read timeout
34    pub read_timeout: Duration,
35    /// Default write timeout
36    pub write_timeout: Duration,
37    /// Maximum number of concurrent connections
38    pub max_connections: u32,
39    /// Connection retry attempts
40    pub retry_attempts: u32,
41    /// Retry delay between attempts
42    pub retry_delay: Duration,
43    /// Keep-alive interval
44    pub keep_alive_interval: Duration,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct PerformanceConfig {
49    /// Maximum packet size
50    pub max_packet_size: usize,
51    /// Batch operation configuration
52    pub batch_config: BatchConfig,
53    /// Connection pool settings
54    pub connection_pool: ConnectionPoolConfig,
55    /// Memory limits
56    pub memory_limits: MemoryLimits,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct BatchConfig {
61    /// Maximum operations per batch
62    pub max_operations_per_batch: usize,
63    /// Batch timeout
64    pub batch_timeout: Duration,
65    /// Continue on error
66    pub continue_on_error: bool,
67    /// Optimize packet packing
68    pub optimize_packet_packing: bool,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ConnectionPoolConfig {
73    /// Initial pool size
74    pub initial_size: u32,
75    /// Maximum pool size
76    pub max_size: u32,
77    /// Pool growth increment
78    pub growth_increment: u32,
79    /// Connection idle timeout
80    pub idle_timeout: Duration,
81    /// Pool cleanup interval
82    pub cleanup_interval: Duration,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct MemoryLimits {
87    /// Maximum memory usage in MB
88    pub max_memory_mb: usize,
89    /// Memory warning threshold in MB
90    pub warning_threshold_mb: usize,
91    /// Enable memory monitoring
92    pub enable_monitoring: bool,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub struct MonitoringConfig {
97    /// Enable production monitoring
98    pub enabled: bool,
99    /// Metrics collection interval
100    pub collection_interval: Duration,
101    /// Health check interval
102    pub health_check_interval: Duration,
103    /// Metrics retention period
104    pub retention_period: Duration,
105    /// Enable performance profiling
106    pub enable_profiling: bool,
107    /// Alert thresholds
108    pub alert_thresholds: AlertThresholds,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct AlertThresholds {
113    /// Error rate threshold (0.0 to 1.0)
114    pub error_rate_threshold: f64,
115    /// Latency threshold in milliseconds
116    pub latency_threshold_ms: f64,
117    /// Memory usage threshold in MB
118    pub memory_threshold_mb: usize,
119    /// Connection failure threshold
120    pub connection_failure_threshold: u32,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct SecurityConfig {
125    /// Enable connection encryption (if supported by PLC)
126    pub enable_encryption: bool,
127    /// Connection validation
128    pub validate_connections: bool,
129    /// Input validation
130    pub validate_inputs: bool,
131    /// Rate limiting
132    pub rate_limiting: RateLimitingConfig,
133}
134
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct RateLimitingConfig {
137    /// Enable rate limiting
138    pub enabled: bool,
139    /// Maximum requests per second
140    pub max_requests_per_second: u32,
141    /// Burst capacity
142    pub burst_capacity: u32,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct LoggingConfig {
147    /// Log level (trace, debug, info, warn, error)
148    pub level: LogLevel,
149    /// Log format (json, text)
150    pub format: LogFormat,
151    /// Log file path
152    pub file_path: Option<String>,
153    /// Enable console logging
154    pub enable_console: bool,
155    /// Enable structured logging
156    pub enable_structured: bool,
157    /// Log rotation settings
158    pub rotation: LogRotationConfig,
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct LogRotationConfig {
163    /// Enable log rotation
164    pub enabled: bool,
165    /// Maximum file size in MB
166    pub max_file_size_mb: usize,
167    /// Maximum number of files
168    pub max_files: usize,
169    /// Rotation schedule (daily, weekly, monthly)
170    pub schedule: LogRotationSchedule,
171}
172
173#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
174#[serde(rename_all = "lowercase")]
175pub enum LogLevel {
176    Trace,
177    Debug,
178    Info,
179    Warn,
180    Error,
181}
182
183#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
184#[serde(rename_all = "lowercase")]
185pub enum LogFormat {
186    Json,
187    Text,
188}
189
190#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
191#[serde(rename_all = "lowercase")]
192pub enum LogRotationSchedule {
193    Daily,
194    Weekly,
195    Monthly,
196}
197
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct PlcSpecificConfig {
200    /// PLC model/type
201    pub model: String,
202    /// Specific connection settings
203    pub connection_settings: HashMap<String, String>,
204    /// Tag discovery settings
205    pub tag_discovery: TagDiscoveryConfig,
206    /// Performance tuning
207    pub performance_tuning: HashMap<String, String>,
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct TagDiscoveryConfig {
212    /// Enable automatic tag discovery
213    pub enabled: bool,
214    /// Discovery interval
215    pub interval: Duration,
216    /// Cache discovered tags
217    pub cache_tags: bool,
218    /// Maximum tags to discover
219    pub max_tags: usize,
220}
221
222#[expect(
223    deprecated,
224    reason = "CODEX-AQ keeps ProductionConfig compatibility until 2.0 removal"
225)]
226impl Default for ProductionConfig {
227    fn default() -> Self {
228        Self {
229            connection: ConnectionConfig {
230                connection_timeout: Duration::from_secs(10),
231                read_timeout: Duration::from_secs(5),
232                write_timeout: Duration::from_secs(5),
233                max_connections: 10,
234                retry_attempts: 3,
235                retry_delay: Duration::from_secs(1),
236                keep_alive_interval: Duration::from_secs(30),
237            },
238            performance: PerformanceConfig {
239                max_packet_size: 4000,
240                batch_config: BatchConfig {
241                    max_operations_per_batch: 50,
242                    batch_timeout: Duration::from_secs(10),
243                    continue_on_error: true,
244                    optimize_packet_packing: true,
245                },
246                connection_pool: ConnectionPoolConfig {
247                    initial_size: 2,
248                    max_size: 10,
249                    growth_increment: 2,
250                    idle_timeout: Duration::from_secs(300),
251                    cleanup_interval: Duration::from_secs(60),
252                },
253                memory_limits: MemoryLimits {
254                    max_memory_mb: 100,
255                    warning_threshold_mb: 80,
256                    enable_monitoring: true,
257                },
258            },
259            monitoring: MonitoringConfig {
260                enabled: true,
261                collection_interval: Duration::from_secs(30),
262                health_check_interval: Duration::from_secs(60),
263                retention_period: Duration::from_secs(86400), // 24 hours
264                enable_profiling: false,
265                alert_thresholds: AlertThresholds {
266                    error_rate_threshold: 0.05,
267                    latency_threshold_ms: 1000.0,
268                    memory_threshold_mb: 80,
269                    connection_failure_threshold: 5,
270                },
271            },
272            security: SecurityConfig {
273                enable_encryption: false,
274                validate_connections: true,
275                validate_inputs: true,
276                rate_limiting: RateLimitingConfig {
277                    enabled: true,
278                    max_requests_per_second: 100,
279                    burst_capacity: 200,
280                },
281            },
282            logging: LoggingConfig {
283                level: LogLevel::Info,
284                format: LogFormat::Json,
285                file_path: Some("logs/ethernet_ip.log".to_string()),
286                enable_console: true,
287                enable_structured: true,
288                rotation: LogRotationConfig {
289                    enabled: true,
290                    max_file_size_mb: 100,
291                    max_files: 10,
292                    schedule: LogRotationSchedule::Daily,
293                },
294            },
295            plc_settings: HashMap::new(),
296        }
297    }
298}
299
300#[expect(
301    deprecated,
302    reason = "CODEX-AQ keeps ProductionConfig compatibility until 2.0 removal"
303)]
304impl ProductionConfig {
305    /// Load configuration from file
306    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
307        let content = fs::read_to_string(path)?;
308        let config: ProductionConfig =
309            toml::from_str(&content).map_err(|e| EtherNetIpError::Other(e.to_string()))?;
310        Ok(config)
311    }
312
313    /// Save configuration to file
314    pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
315        let content =
316            toml::to_string_pretty(self).map_err(|e| EtherNetIpError::Other(e.to_string()))?;
317        fs::write(path, content)?;
318        Ok(())
319    }
320
321    /// Validate configuration
322    pub fn validate(&self) -> std::result::Result<(), Vec<String>> {
323        let mut errors = Vec::new();
324
325        // Validate connection settings
326        if self.connection.connection_timeout.as_secs() == 0 {
327            errors.push("Connection timeout must be greater than 0".to_string());
328        }
329
330        if self.connection.max_connections == 0 {
331            errors.push("Maximum connections must be greater than 0".to_string());
332        }
333
334        // Validate performance settings
335        if self.performance.max_packet_size < 100 {
336            errors.push("Maximum packet size must be at least 100 bytes".to_string());
337        }
338
339        if self.performance.batch_config.max_operations_per_batch == 0 {
340            errors.push("Maximum operations per batch must be greater than 0".to_string());
341        }
342
343        // Validate monitoring settings
344        if self.monitoring.collection_interval.as_secs() == 0 {
345            errors.push("Collection interval must be greater than 0".to_string());
346        }
347
348        // Validate security settings
349        if self.security.rate_limiting.enabled
350            && self.security.rate_limiting.max_requests_per_second == 0
351        {
352            errors.push(
353                "Max requests per second must be greater than 0 when rate limiting is enabled"
354                    .to_string(),
355            );
356        }
357
358        if errors.is_empty() {
359            Ok(())
360        } else {
361            Err(errors)
362        }
363    }
364
365    /// Get PLC-specific configuration
366    pub fn get_plc_config(&self, plc_address: &str) -> Option<&PlcSpecificConfig> {
367        self.plc_settings.get(plc_address)
368    }
369
370    /// Add or update PLC-specific configuration
371    pub fn set_plc_config(&mut self, plc_address: String, config: PlcSpecificConfig) {
372        self.plc_settings.insert(plc_address, config);
373    }
374
375    /// Create a development configuration
376    pub fn development() -> Self {
377        let mut config = Self::default();
378        config.logging.level = LogLevel::Debug;
379        config.monitoring.enabled = false;
380        config.security.rate_limiting.enabled = false;
381        config.performance.memory_limits.enable_monitoring = false;
382        config
383    }
384
385    /// Create a production configuration
386    pub fn production() -> Self {
387        let mut config = Self::default();
388        config.logging.level = LogLevel::Info;
389        config.monitoring.enabled = true;
390        config.security.rate_limiting.enabled = true;
391        config.performance.memory_limits.enable_monitoring = true;
392        config.performance.memory_limits.max_memory_mb = 500;
393        config
394    }
395}