edge_impulse_runner/backends/
mod.rs

1//! Backend abstraction for Edge Impulse inference
2//!
3//! This module provides a trait-based abstraction that allows switching between
4//! different inference backends:
5//!
6//! - **EIM Backend**: Binary communication with Edge Impulse model processes via Unix sockets
7//! - **FFI Backend**: Direct FFI calls to the Edge Impulse C++ SDK
8//!
9//! The backend system is designed to be extensible, allowing new inference engines
10//! to be added by implementing the `InferenceBackend` trait.
11
12use crate::error::EdgeImpulseError;
13use crate::inference::messages::InferenceResponse;
14use crate::types::ModelParameters;
15use std::path::PathBuf;
16
17/// Configuration for different backend types
18#[derive(Debug, Clone)]
19pub enum BackendConfig {
20    /// EIM binary communication mode
21    Eim {
22        /// Path to the .eim file
23        path: PathBuf,
24        /// Optional custom socket path
25        socket_path: Option<PathBuf>,
26    },
27    /// FFI direct mode
28    Ffi {
29        /// Enable debug logging
30        debug: bool,
31    },
32}
33
34/// Trait for inference backends
35pub trait InferenceBackend: Send + Sync {
36    /// Create a new backend instance
37    fn new(config: BackendConfig) -> Result<Self, EdgeImpulseError>
38    where
39        Self: Sized;
40
41    /// Run inference on features
42    fn infer(
43        &mut self,
44        features: Vec<f32>,
45        debug: Option<bool>,
46    ) -> Result<InferenceResponse, EdgeImpulseError>;
47
48    /// Get model parameters
49    fn parameters(&self) -> Result<&ModelParameters, EdgeImpulseError>;
50
51    /// Get sensor type
52    fn sensor_type(&self) -> Result<crate::types::SensorType, EdgeImpulseError>;
53
54    /// Get input size
55    fn input_size(&self) -> Result<usize, EdgeImpulseError>;
56
57    /// Set debug callback
58    fn set_debug_callback(&mut self, callback: Box<dyn Fn(&str) + Send + Sync>);
59
60    /// Normalize visual anomaly results
61    fn normalize_visual_anomaly(
62        &self,
63        anomaly: f32,
64        max: f32,
65        mean: f32,
66        regions: &[(f32, u32, u32, u32, u32)],
67    ) -> crate::types::VisualAnomalyResult;
68}
69
70#[cfg(feature = "eim")]
71pub mod eim;
72
73#[cfg(feature = "ffi")]
74pub mod ffi;
75
76/// Factory function to create the appropriate backend
77pub fn create_backend(
78    config: BackendConfig,
79) -> Result<Box<dyn InferenceBackend>, EdgeImpulseError> {
80    match config {
81        #[cfg(feature = "eim")]
82        BackendConfig::Eim { path, socket_path } => {
83            // Validate file extension for EIM backend
84            if let Some(ext) = path.extension() {
85                if ext != "eim" {
86                    return Err(EdgeImpulseError::InvalidPath);
87                }
88            } else {
89                return Err(EdgeImpulseError::InvalidPath);
90            }
91
92            use eim::EimBackend;
93            Ok(Box::new(EimBackend::new(BackendConfig::Eim {
94                path,
95                socket_path,
96            })?))
97        }
98        #[cfg(feature = "ffi")]
99        BackendConfig::Ffi { debug } => {
100            use ffi::FfiBackend;
101            Ok(Box::new(FfiBackend::new(BackendConfig::Ffi { debug })?))
102        }
103        #[cfg(not(feature = "eim"))]
104        BackendConfig::Eim { .. } => Err(EdgeImpulseError::InvalidOperation(
105            "EIM backend not enabled. Enable the 'eim' feature.".to_string(),
106        )),
107        #[cfg(not(feature = "ffi"))]
108        BackendConfig::Ffi { .. } => Err(EdgeImpulseError::InvalidOperation(
109            "FFI backend not enabled. Enable the 'ffi' feature.".to_string(),
110        )),
111    }
112}