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