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