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)]
10pub struct ProductionConfig {
11    /// Connection settings
12    pub connection: ConnectionConfig,
13    /// Performance settings
14    pub performance: PerformanceConfig,
15    /// Monitoring settings
16    pub monitoring: MonitoringConfig,
17    /// Security settings
18    pub security: SecurityConfig,
19    /// Logging settings
20    pub logging: LoggingConfig,
21    /// PLC-specific settings
22    pub plc_settings: HashMap<String, PlcSpecificConfig>,
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct ConnectionConfig {
27    /// Default connection timeout
28    pub connection_timeout: Duration,
29    /// Default read timeout
30    pub read_timeout: Duration,
31    /// Default write timeout
32    pub write_timeout: Duration,
33    /// Maximum number of concurrent connections
34    pub max_connections: u32,
35    /// Connection retry attempts
36    pub retry_attempts: u32,
37    /// Retry delay between attempts
38    pub retry_delay: Duration,
39    /// Keep-alive interval
40    pub keep_alive_interval: Duration,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct PerformanceConfig {
45    /// Maximum packet size
46    pub max_packet_size: usize,
47    /// Batch operation configuration
48    pub batch_config: BatchConfig,
49    /// Connection pool settings
50    pub connection_pool: ConnectionPoolConfig,
51    /// Memory limits
52    pub memory_limits: MemoryLimits,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct BatchConfig {
57    /// Maximum operations per batch
58    pub max_operations_per_batch: usize,
59    /// Batch timeout
60    pub batch_timeout: Duration,
61    /// Continue on error
62    pub continue_on_error: bool,
63    /// Optimize packet packing
64    pub optimize_packet_packing: bool,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ConnectionPoolConfig {
69    /// Initial pool size
70    pub initial_size: u32,
71    /// Maximum pool size
72    pub max_size: u32,
73    /// Pool growth increment
74    pub growth_increment: u32,
75    /// Connection idle timeout
76    pub idle_timeout: Duration,
77    /// Pool cleanup interval
78    pub cleanup_interval: Duration,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct MemoryLimits {
83    /// Maximum memory usage in MB
84    pub max_memory_mb: usize,
85    /// Memory warning threshold in MB
86    pub warning_threshold_mb: usize,
87    /// Enable memory monitoring
88    pub enable_monitoring: bool,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
92pub struct MonitoringConfig {
93    /// Enable production monitoring
94    pub enabled: bool,
95    /// Metrics collection interval
96    pub collection_interval: Duration,
97    /// Health check interval
98    pub health_check_interval: Duration,
99    /// Metrics retention period
100    pub retention_period: Duration,
101    /// Enable performance profiling
102    pub enable_profiling: bool,
103    /// Alert thresholds
104    pub alert_thresholds: AlertThresholds,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct AlertThresholds {
109    /// Error rate threshold (0.0 to 1.0)
110    pub error_rate_threshold: f64,
111    /// Latency threshold in milliseconds
112    pub latency_threshold_ms: f64,
113    /// Memory usage threshold in MB
114    pub memory_threshold_mb: usize,
115    /// Connection failure threshold
116    pub connection_failure_threshold: u32,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct SecurityConfig {
121    /// Enable connection encryption (if supported by PLC)
122    pub enable_encryption: bool,
123    /// Connection validation
124    pub validate_connections: bool,
125    /// Input validation
126    pub validate_inputs: bool,
127    /// Rate limiting
128    pub rate_limiting: RateLimitingConfig,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132pub struct RateLimitingConfig {
133    /// Enable rate limiting
134    pub enabled: bool,
135    /// Maximum requests per second
136    pub max_requests_per_second: u32,
137    /// Burst capacity
138    pub burst_capacity: u32,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct LoggingConfig {
143    /// Log level (trace, debug, info, warn, error)
144    pub level: LogLevel,
145    /// Log format (json, text)
146    pub format: LogFormat,
147    /// Log file path
148    pub file_path: Option<String>,
149    /// Enable console logging
150    pub enable_console: bool,
151    /// Enable structured logging
152    pub enable_structured: bool,
153    /// Log rotation settings
154    pub rotation: LogRotationConfig,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct LogRotationConfig {
159    /// Enable log rotation
160    pub enabled: bool,
161    /// Maximum file size in MB
162    pub max_file_size_mb: usize,
163    /// Maximum number of files
164    pub max_files: usize,
165    /// Rotation schedule (daily, weekly, monthly)
166    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    /// PLC model/type
197    pub model: String,
198    /// Specific connection settings
199    pub connection_settings: HashMap<String, String>,
200    /// Tag discovery settings
201    pub tag_discovery: TagDiscoveryConfig,
202    /// Performance tuning
203    pub performance_tuning: HashMap<String, String>,
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize)]
207pub struct TagDiscoveryConfig {
208    /// Enable automatic tag discovery
209    pub enabled: bool,
210    /// Discovery interval
211    pub interval: Duration,
212    /// Cache discovered tags
213    pub cache_tags: bool,
214    /// Maximum tags to discover
215    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), // 24 hours
256                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    /// Load configuration from file
294    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    /// Save configuration to file
302    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    /// Validate configuration
310    pub fn validate(&self) -> std::result::Result<(), Vec<String>> {
311        let mut errors = Vec::new();
312
313        // Validate connection settings
314        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        // Validate performance settings
323        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        // Validate monitoring settings
332        if self.monitoring.collection_interval.as_secs() == 0 {
333            errors.push("Collection interval must be greater than 0".to_string());
334        }
335
336        // Validate security settings
337        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    /// Get PLC-specific configuration
354    pub fn get_plc_config(&self, plc_address: &str) -> Option<&PlcSpecificConfig> {
355        self.plc_settings.get(plc_address)
356    }
357
358    /// Add or update PLC-specific configuration
359    pub fn set_plc_config(&mut self, plc_address: String, config: PlcSpecificConfig) {
360        self.plc_settings.insert(plc_address, config);
361    }
362
363    /// Create a development configuration
364    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    /// Create a production configuration
374    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}