Skip to main content

mabi_core/
factory.rs

1//! Device factory and plugin system for extensibility.
2//!
3//! This module provides abstractions for creating devices dynamically
4//! and registering protocol plugins at runtime.
5
6use std::collections::HashMap;
7use std::sync::Arc;
8
9use parking_lot::RwLock;
10
11use crate::config::DeviceConfig;
12use crate::device::BoxedDevice;
13use crate::error::{Error, Result};
14use crate::protocol::Protocol;
15use crate::tags::Tags;
16
17/// A factory trait for creating devices.
18///
19/// Implement this trait to add support for new protocols or device types.
20///
21/// # Example
22///
23/// ```ignore
24/// struct ModbusDeviceFactory;
25///
26/// impl DeviceFactory for ModbusDeviceFactory {
27///     fn protocol(&self) -> Protocol {
28///         Protocol::ModbusTcp
29///     }
30///
31///     fn create(&self, config: DeviceConfig) -> Result<BoxedDevice> {
32///         Ok(Box::new(ModbusDevice::new(config)?))
33///     }
34/// }
35/// ```
36pub trait DeviceFactory: Send + Sync {
37    /// Get the protocol this factory handles.
38    fn protocol(&self) -> Protocol;
39
40    /// Create a device from configuration.
41    fn create(&self, config: DeviceConfig) -> Result<BoxedDevice>;
42
43    /// Create multiple devices from configurations.
44    fn create_batch(&self, configs: Vec<DeviceConfig>) -> Result<Vec<BoxedDevice>> {
45        configs.into_iter().map(|c| self.create(c)).collect()
46    }
47
48    /// Validate device configuration without creating the device.
49    fn validate(&self, config: &DeviceConfig) -> Result<()> {
50        if config.id.is_empty() {
51            return Err(Error::Config("Device ID cannot be empty".into()));
52        }
53        if config.name.is_empty() {
54            return Err(Error::Config("Device name cannot be empty".into()));
55        }
56        if config.protocol != self.protocol() {
57            return Err(Error::Config(format!(
58                "Protocol mismatch: expected {:?}, got {:?}",
59                self.protocol(),
60                config.protocol
61            )));
62        }
63        Ok(())
64    }
65
66    /// Get factory metadata.
67    fn metadata(&self) -> FactoryMetadata {
68        FactoryMetadata {
69            protocol: self.protocol(),
70            version: "1.0.0".to_string(),
71            description: format!("{:?} device factory", self.protocol()),
72            capabilities: Vec::new(),
73        }
74    }
75}
76
77/// Metadata about a device factory.
78#[derive(Debug, Clone)]
79pub struct FactoryMetadata {
80    /// Protocol this factory handles.
81    pub protocol: Protocol,
82    /// Factory version.
83    pub version: String,
84    /// Description.
85    pub description: String,
86    /// Supported capabilities.
87    pub capabilities: Vec<String>,
88}
89
90/// A type-erased device factory.
91pub type BoxedFactory = Box<dyn DeviceFactory>;
92
93/// Registry for device factories.
94///
95/// This allows registering factories for different protocols at runtime,
96/// enabling a plugin-like architecture.
97///
98/// # Example
99///
100/// ```ignore
101/// let mut registry = FactoryRegistry::new();
102/// registry.register(ModbusDeviceFactory)?;
103///
104/// let config = DeviceConfig { /* ... */ };
105/// let device = registry.create_device(config)?;
106/// ```
107pub struct FactoryRegistry {
108    factories: RwLock<HashMap<Protocol, Arc<BoxedFactory>>>,
109}
110
111impl FactoryRegistry {
112    /// Create a new empty registry.
113    pub fn new() -> Self {
114        Self {
115            factories: RwLock::new(HashMap::new()),
116        }
117    }
118
119    /// Register a device factory.
120    pub fn register<F: DeviceFactory + 'static>(&self, factory: F) -> Result<()> {
121        let protocol = factory.protocol();
122        let mut factories = self.factories.write();
123
124        if factories.contains_key(&protocol) {
125            return Err(Error::Config(format!(
126                "Factory already registered for protocol {:?}",
127                protocol
128            )));
129        }
130
131        factories.insert(protocol, Arc::new(Box::new(factory)));
132        Ok(())
133    }
134
135    /// Register a boxed factory.
136    pub fn register_boxed(&self, factory: BoxedFactory) -> Result<()> {
137        let protocol = factory.protocol();
138        let mut factories = self.factories.write();
139
140        if factories.contains_key(&protocol) {
141            return Err(Error::Config(format!(
142                "Factory already registered for protocol {:?}",
143                protocol
144            )));
145        }
146
147        factories.insert(protocol, Arc::new(factory));
148        Ok(())
149    }
150
151    /// Unregister a factory for a protocol.
152    pub fn unregister(&self, protocol: Protocol) -> bool {
153        self.factories.write().remove(&protocol).is_some()
154    }
155
156    /// Get a factory for a protocol.
157    pub fn get(&self, protocol: Protocol) -> Option<Arc<BoxedFactory>> {
158        self.factories.read().get(&protocol).cloned()
159    }
160
161    /// Check if a factory is registered for a protocol.
162    pub fn has(&self, protocol: Protocol) -> bool {
163        self.factories.read().contains_key(&protocol)
164    }
165
166    /// Get all registered protocols.
167    pub fn protocols(&self) -> Vec<Protocol> {
168        self.factories.read().keys().copied().collect()
169    }
170
171    /// Get metadata for all registered factories.
172    pub fn all_metadata(&self) -> Vec<FactoryMetadata> {
173        self.factories
174            .read()
175            .values()
176            .map(|f| f.metadata())
177            .collect()
178    }
179
180    /// Create a device using the appropriate factory.
181    pub fn create_device(&self, config: DeviceConfig) -> Result<BoxedDevice> {
182        let factory = self
183            .get(config.protocol)
184            .ok_or_else(|| Error::NotSupported(format!("Protocol {:?}", config.protocol)))?;
185
186        factory.validate(&config)?;
187        factory.create(config)
188    }
189
190    /// Create multiple devices.
191    pub fn create_devices(&self, configs: Vec<DeviceConfig>) -> Result<Vec<BoxedDevice>> {
192        configs
193            .into_iter()
194            .map(|c| self.create_device(c))
195            .collect()
196    }
197}
198
199impl Default for FactoryRegistry {
200    fn default() -> Self {
201        Self::new()
202    }
203}
204
205/// Plugin trait for extending the simulator.
206///
207/// Plugins can register factories, add event handlers, and customize behavior.
208pub trait Plugin: Send + Sync {
209    /// Get plugin name.
210    fn name(&self) -> &str;
211
212    /// Get plugin version.
213    fn version(&self) -> &str {
214        "1.0.0"
215    }
216
217    /// Get plugin description.
218    fn description(&self) -> &str {
219        ""
220    }
221
222    /// Initialize the plugin (called when loaded).
223    fn initialize(&mut self) -> Result<()> {
224        Ok(())
225    }
226
227    /// Register device factories with the registry.
228    fn register_factories(&self, _registry: &FactoryRegistry) -> Result<()> {
229        Ok(())
230    }
231
232    /// Shutdown the plugin (called when unloaded).
233    fn shutdown(&mut self) -> Result<()> {
234        Ok(())
235    }
236}
237
238/// A boxed plugin.
239pub type BoxedPlugin = Box<dyn Plugin>;
240
241/// Plugin manager for loading and managing plugins.
242pub struct PluginManager {
243    plugins: RwLock<Vec<BoxedPlugin>>,
244    registry: Arc<FactoryRegistry>,
245}
246
247impl PluginManager {
248    /// Create a new plugin manager.
249    pub fn new(registry: Arc<FactoryRegistry>) -> Self {
250        Self {
251            plugins: RwLock::new(Vec::new()),
252            registry,
253        }
254    }
255
256    /// Load a plugin.
257    pub fn load<P: Plugin + 'static>(&self, mut plugin: P) -> Result<()> {
258        plugin.initialize()?;
259        plugin.register_factories(&self.registry)?;
260
261        self.plugins.write().push(Box::new(plugin));
262        Ok(())
263    }
264
265    /// Load a boxed plugin.
266    pub fn load_boxed(&self, mut plugin: BoxedPlugin) -> Result<()> {
267        plugin.initialize()?;
268        plugin.register_factories(&self.registry)?;
269
270        self.plugins.write().push(plugin);
271        Ok(())
272    }
273
274    /// Get the number of loaded plugins.
275    pub fn plugin_count(&self) -> usize {
276        self.plugins.read().len()
277    }
278
279    /// Get information about loaded plugins.
280    pub fn plugin_info(&self) -> Vec<PluginInfo> {
281        self.plugins
282            .read()
283            .iter()
284            .map(|p| PluginInfo {
285                name: p.name().to_string(),
286                version: p.version().to_string(),
287                description: p.description().to_string(),
288            })
289            .collect()
290    }
291
292    /// Get the factory registry.
293    pub fn registry(&self) -> &Arc<FactoryRegistry> {
294        &self.registry
295    }
296
297    /// Shutdown all plugins.
298    pub fn shutdown_all(&self) -> Result<()> {
299        for plugin in self.plugins.write().iter_mut() {
300            plugin.shutdown()?;
301        }
302        Ok(())
303    }
304}
305
306/// Information about a loaded plugin.
307#[derive(Debug, Clone)]
308pub struct PluginInfo {
309    /// Plugin name.
310    pub name: String,
311    /// Plugin version.
312    pub version: String,
313    /// Plugin description.
314    pub description: String,
315}
316
317#[cfg(test)]
318mod tests {
319    use super::*;
320    use crate::device::{Device, DeviceInfo, DeviceStatistics};
321    use crate::types::{DataPoint, DataPointDef, DataPointId};
322    use crate::value::Value;
323    use async_trait::async_trait;
324
325    // Mock device for testing
326    struct MockDevice {
327        info: DeviceInfo,
328    }
329
330    impl MockDevice {
331        fn new(id: &str, name: &str) -> Self {
332            Self {
333                info: DeviceInfo::new(id, name, Protocol::ModbusTcp),
334            }
335        }
336    }
337
338    #[async_trait]
339    impl Device for MockDevice {
340        fn info(&self) -> &DeviceInfo {
341            &self.info
342        }
343
344        async fn initialize(&mut self) -> Result<()> {
345            Ok(())
346        }
347
348        async fn start(&mut self) -> Result<()> {
349            Ok(())
350        }
351
352        async fn stop(&mut self) -> Result<()> {
353            Ok(())
354        }
355
356        async fn tick(&mut self) -> Result<()> {
357            Ok(())
358        }
359
360        fn point_definitions(&self) -> Vec<&DataPointDef> {
361            vec![]
362        }
363
364        fn point_definition(&self, _point_id: &str) -> Option<&DataPointDef> {
365            None
366        }
367
368        async fn read(&self, point_id: &str) -> Result<DataPoint> {
369            Ok(DataPoint::new(
370                DataPointId::new(&self.info.id, point_id),
371                Value::F64(0.0),
372            ))
373        }
374
375        async fn write(&mut self, _point_id: &str, _value: Value) -> Result<()> {
376            Ok(())
377        }
378
379        fn statistics(&self) -> DeviceStatistics {
380            DeviceStatistics::default()
381        }
382    }
383
384    // Mock factory for testing
385    struct MockFactory;
386
387    impl DeviceFactory for MockFactory {
388        fn protocol(&self) -> Protocol {
389            Protocol::ModbusTcp
390        }
391
392        fn create(&self, config: DeviceConfig) -> Result<BoxedDevice> {
393            Ok(Box::new(MockDevice::new(&config.id, &config.name)))
394        }
395    }
396
397    // Mock plugin for testing
398    struct MockPlugin {
399        name: String,
400    }
401
402    impl MockPlugin {
403        fn new(name: &str) -> Self {
404            Self {
405                name: name.to_string(),
406            }
407        }
408    }
409
410    impl Plugin for MockPlugin {
411        fn name(&self) -> &str {
412            &self.name
413        }
414
415        fn description(&self) -> &str {
416            "Mock plugin for testing"
417        }
418
419        fn register_factories(&self, registry: &FactoryRegistry) -> Result<()> {
420            registry.register(MockFactory)
421        }
422    }
423
424    #[test]
425    fn test_factory_registry() {
426        let registry = FactoryRegistry::new();
427
428        assert!(!registry.has(Protocol::ModbusTcp));
429
430        registry.register(MockFactory).unwrap();
431
432        assert!(registry.has(Protocol::ModbusTcp));
433        assert!(registry.protocols().contains(&Protocol::ModbusTcp));
434    }
435
436    #[test]
437    fn test_factory_create_device() {
438        let registry = FactoryRegistry::new();
439        registry.register(MockFactory).unwrap();
440
441        let config = DeviceConfig {
442            id: "test-001".to_string(),
443            name: "Test Device".to_string(),
444            description: String::new(),
445            protocol: Protocol::ModbusTcp,
446            address: None,
447            points: vec![],
448            metadata: Default::default(),
449            tags: Tags::new(),
450        };
451
452        let device = registry.create_device(config).unwrap();
453        assert_eq!(device.id(), "test-001");
454    }
455
456    #[test]
457    fn test_factory_validation() {
458        let factory = MockFactory;
459
460        // Empty ID should fail
461        let config = DeviceConfig {
462            id: String::new(),
463            name: "Test".to_string(),
464            description: String::new(),
465            protocol: Protocol::ModbusTcp,
466            address: None,
467            points: vec![],
468            metadata: Default::default(),
469            tags: Tags::new(),
470        };
471        assert!(factory.validate(&config).is_err());
472
473        // Wrong protocol should fail
474        let config = DeviceConfig {
475            id: "test".to_string(),
476            name: "Test".to_string(),
477            description: String::new(),
478            protocol: Protocol::OpcUa,
479            address: None,
480            points: vec![],
481            metadata: Default::default(),
482            tags: Tags::new(),
483        };
484        assert!(factory.validate(&config).is_err());
485    }
486
487    #[test]
488    fn test_plugin_manager() {
489        let registry = Arc::new(FactoryRegistry::new());
490        let manager = PluginManager::new(registry.clone());
491
492        assert_eq!(manager.plugin_count(), 0);
493
494        manager.load(MockPlugin::new("test-plugin")).unwrap();
495
496        assert_eq!(manager.plugin_count(), 1);
497        assert!(registry.has(Protocol::ModbusTcp));
498
499        let info = manager.plugin_info();
500        assert_eq!(info[0].name, "test-plugin");
501    }
502
503    #[test]
504    fn test_duplicate_factory_registration() {
505        let registry = FactoryRegistry::new();
506
507        registry.register(MockFactory).unwrap();
508        let result = registry.register(MockFactory);
509
510        assert!(result.is_err());
511    }
512}