1use std::error::Error;
7use std::fmt;
8
9impl fmt::Display for ClassificationResult {
10 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11 write!(f, "{}: {:.4}", self.label, self.value)
12 }
13}
14
15impl fmt::Display for BoundingBox {
16 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17 write!(
18 f,
19 "{}: {:.4} (x={}, y={}, w={}, h={})",
20 self.label, self.value, self.x, self.y, self.width, self.height
21 )
22 }
23}
24
25impl fmt::Display for TimingResult {
26 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27 write!(
28 f,
29 "Timing: dsp={} ms, classification={} ms, anomaly={} ms",
30 self.dsp, self.classification, self.anomaly
31 )
32 }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum EdgeImpulseError {
38 Ok,
39 ShapesDontMatch,
40 Canceled,
41 MemoryAllocationFailed,
42 OutOfMemory,
43 InputTensorWasNull,
44 OutputTensorWasNull,
45 AllocatedTensorWasNull,
46 TfliteError,
47 TfliteArenaAllocFailed,
48 ReadSensor,
49 MinSizeRatio,
50 MaxSizeRatio,
51 OnlySupportImages,
52 ModelInputTensorWasNull,
53 ModelOutputTensorWasNull,
54 UnsupportedInferencingEngine,
55 AllocWhileCacheLocked,
56 NoValidImpulse,
57 Other,
58}
59
60#[cfg(feature = "ffi")]
61impl From<edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR> for EdgeImpulseError {
62 fn from(error: edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR) -> Self {
63 match error {
64 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OK => EdgeImpulseError::Ok,
65 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_ERROR_SHAPES_DONT_MATCH => {
66 EdgeImpulseError::ShapesDontMatch
67 }
68 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_CANCELED => EdgeImpulseError::Canceled,
69 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_ALLOC_FAILED => EdgeImpulseError::MemoryAllocationFailed,
70 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OUT_OF_MEMORY => EdgeImpulseError::OutOfMemory,
71 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_INPUT_TENSOR_WAS_NULL => {
72 EdgeImpulseError::InputTensorWasNull
73 }
74 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OUTPUT_TENSOR_WAS_NULL => {
75 EdgeImpulseError::OutputTensorWasNull
76 }
77 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_TFLITE_ERROR => EdgeImpulseError::TfliteError,
78 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_TFLITE_ARENA_ALLOC_FAILED => {
79 EdgeImpulseError::TfliteArenaAllocFailed
80 }
81 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_DSP_ERROR => EdgeImpulseError::ReadSensor,
82 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_INVALID_SIZE => EdgeImpulseError::MinSizeRatio,
83 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_ONLY_SUPPORTED_FOR_IMAGES => {
84 EdgeImpulseError::OnlySupportImages
85 }
86 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_UNSUPPORTED_INFERENCING_ENGINE => {
87 EdgeImpulseError::UnsupportedInferencingEngine
88 }
89 edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_INFERENCE_ERROR => EdgeImpulseError::NoValidImpulse,
90 _ => EdgeImpulseError::Other,
91 }
92 }
93}
94
95impl fmt::Display for EdgeImpulseError {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 match self {
98 EdgeImpulseError::Ok => write!(f, "Operation completed successfully"),
99 EdgeImpulseError::ShapesDontMatch => {
100 write!(f, "Input shapes don't match expected dimensions")
101 }
102 EdgeImpulseError::Canceled => write!(f, "Operation was canceled"),
103 EdgeImpulseError::MemoryAllocationFailed => write!(f, "Memory allocation failed"),
104 EdgeImpulseError::OutOfMemory => write!(f, "Out of memory"),
105 EdgeImpulseError::InputTensorWasNull => write!(f, "Input tensor was null"),
106 EdgeImpulseError::OutputTensorWasNull => write!(f, "Output tensor was null"),
107 EdgeImpulseError::AllocatedTensorWasNull => write!(f, "Allocated tensor was null"),
108 EdgeImpulseError::TfliteError => write!(f, "TensorFlow Lite error"),
109 EdgeImpulseError::TfliteArenaAllocFailed => {
110 write!(f, "TensorFlow Lite arena allocation failed")
111 }
112 EdgeImpulseError::ReadSensor => write!(f, "Error reading sensor data"),
113 EdgeImpulseError::MinSizeRatio => write!(f, "Minimum size ratio not met"),
114 EdgeImpulseError::MaxSizeRatio => write!(f, "Maximum size ratio exceeded"),
115 EdgeImpulseError::OnlySupportImages => write!(f, "Only image input is supported"),
116 EdgeImpulseError::ModelInputTensorWasNull => write!(f, "Model input tensor was null"),
117 EdgeImpulseError::ModelOutputTensorWasNull => write!(f, "Model output tensor was null"),
118 EdgeImpulseError::UnsupportedInferencingEngine => {
119 write!(f, "Unsupported inferencing engine")
120 }
121 EdgeImpulseError::AllocWhileCacheLocked => {
122 write!(f, "Allocation attempted while cache is locked")
123 }
124 EdgeImpulseError::NoValidImpulse => write!(f, "No valid impulse found"),
125 EdgeImpulseError::Other => write!(f, "Unknown error occurred"),
126 }
127 }
128}
129
130impl Error for EdgeImpulseError {}
131
132pub type EdgeImpulseResult<T> = Result<T, EdgeImpulseError>;
134
135pub struct EdgeImpulseHandle {
137 #[cfg(feature = "ffi")]
138 handle: *mut edge_impulse_ffi_rs::bindings::ei_impulse_handle_t,
139}
140
141impl EdgeImpulseHandle {
142 pub fn new() -> EdgeImpulseResult<Self> {
144 #[cfg(feature = "ffi")]
145 {
146 let handle_ptr =
147 std::ptr::null_mut::<edge_impulse_ffi_rs::bindings::ei_impulse_handle_t>();
148 let result = unsafe { edge_impulse_ffi_rs::bindings::ei_ffi_init_impulse(handle_ptr) };
149 let error = EdgeImpulseError::from(result);
150 if error != EdgeImpulseError::Ok {
151 return Err(error);
152 }
153
154 Ok(Self { handle: handle_ptr })
155 }
156
157 #[cfg(not(feature = "ffi"))]
158 {
159 Err(EdgeImpulseError::Other)
160 }
161 }
162}
163
164impl Drop for EdgeImpulseHandle {
165 fn drop(&mut self) {
166 }
168}
169
170pub struct Signal {
172 #[cfg(feature = "ffi")]
173 c_signal: Box<edge_impulse_ffi_rs::bindings::ei_signal_t>,
174}
175
176impl Signal {
177 pub fn from_raw_data(_data: &[f32]) -> EdgeImpulseResult<Self> {
179 #[cfg(feature = "ffi")]
180 {
181 let mut c_signal = Box::new(edge_impulse_ffi_rs::bindings::ei_signal_t {
182 get_data: [0u64; 4], total_length: 0,
184 });
185 let result = unsafe {
186 edge_impulse_ffi_rs::bindings::ei_ffi_signal_from_buffer(
187 _data.as_ptr(),
188 _data.len(),
189 &mut *c_signal,
190 )
191 };
192
193 if result == edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OK {
194 Ok(Self { c_signal })
195 } else {
196 Err(EdgeImpulseError::from(result))
197 }
198 }
199
200 #[cfg(not(feature = "ffi"))]
201 {
202 Err(EdgeImpulseError::Other)
203 }
204 }
205
206 #[cfg(feature = "ffi")]
207 pub fn as_ptr(&self) -> *mut edge_impulse_ffi_rs::bindings::ei_signal_t {
208 Box::as_ref(&self.c_signal) as *const edge_impulse_ffi_rs::bindings::ei_signal_t
209 as *mut edge_impulse_ffi_rs::bindings::ei_signal_t
210 }
211}
212
213pub struct InferenceResult {
215 #[cfg(feature = "ffi")]
216 result: *mut edge_impulse_ffi_rs::bindings::ei_impulse_result_t,
217}
218
219impl Default for InferenceResult {
220 fn default() -> Self {
221 Self::new()
222 }
223}
224
225impl InferenceResult {
226 pub fn new() -> Self {
228 #[cfg(feature = "ffi")]
229 {
230 let result = unsafe {
231 let ptr = std::alloc::alloc_zeroed(std::alloc::Layout::new::<
232 edge_impulse_ffi_rs::bindings::ei_impulse_result_t,
233 >())
234 as *mut edge_impulse_ffi_rs::bindings::ei_impulse_result_t;
235 ptr
236 };
237 Self { result }
238 }
239
240 #[cfg(not(feature = "ffi"))]
241 {
242 Self {}
243 }
244 }
245
246 #[cfg(feature = "ffi")]
248 pub fn as_ptr(&self) -> *const edge_impulse_ffi_rs::bindings::ei_impulse_result_t {
249 self.result as *const edge_impulse_ffi_rs::bindings::ei_impulse_result_t
250 }
251
252 #[cfg(feature = "ffi")]
254 pub fn as_mut_ptr(&mut self) -> *mut edge_impulse_ffi_rs::bindings::ei_impulse_result_t {
255 self.result
256 }
257
258 pub fn classifications(&self, _label_count: usize) -> Vec<ClassificationResult> {
260 #[cfg(feature = "ffi")]
261 {
262 unsafe {
263 let result = &*self.result;
264 (0.._label_count)
265 .map(|i| {
266 let c = result.classification[i];
267 let label = if !c.label.is_null() {
268 std::ffi::CStr::from_ptr(c.label)
269 .to_string_lossy()
270 .into_owned()
271 } else {
272 String::new()
273 };
274 ClassificationResult {
275 label,
276 value: c.value,
277 }
278 })
279 .collect()
280 }
281 }
282
283 #[cfg(not(feature = "ffi"))]
284 {
285 vec![]
286 }
287 }
288
289 pub fn bounding_boxes(&self) -> Vec<BoundingBox> {
291 #[cfg(feature = "ffi")]
292 {
293 unsafe {
294 let result = &*self.result;
295 if result.bounding_boxes_count == 0 || result.bounding_boxes.is_null() {
296 return vec![];
297 }
298 let bbs = std::slice::from_raw_parts(
299 result.bounding_boxes,
300 result.bounding_boxes_count as usize,
301 );
302 bbs.iter()
303 .filter_map(|bb| {
304 if bb.value == 0.0 {
305 return None;
306 }
307 let label = if !bb.label.is_null() {
308 std::ffi::CStr::from_ptr(bb.label)
309 .to_string_lossy()
310 .into_owned()
311 } else {
312 String::new()
313 };
314 Some(BoundingBox {
315 label,
316 value: bb.value,
317 x: bb.x,
318 y: bb.y,
319 width: bb.width,
320 height: bb.height,
321 })
322 })
323 .collect()
324 }
325 }
326
327 #[cfg(not(feature = "ffi"))]
328 {
329 vec![]
330 }
331 }
332
333 pub fn timing(&self) -> TimingResult {
335 #[cfg(feature = "ffi")]
336 {
337 unsafe {
338 let result = &*self.result;
339 let t = &result.timing;
340 TimingResult {
341 dsp: t.dsp as i32,
342 classification: t.classification as i32,
343 anomaly: t.anomaly as i32,
344 }
345 }
346 }
347
348 #[cfg(not(feature = "ffi"))]
349 {
350 TimingResult {
351 dsp: 0,
352 classification: 0,
353 anomaly: 0,
354 }
355 }
356 }
357}
358
359impl Drop for InferenceResult {
360 fn drop(&mut self) {
361 #[cfg(feature = "ffi")]
362 {
363 if !self.result.is_null() {
364 unsafe {
365 std::alloc::dealloc(
366 self.result as *mut u8,
367 std::alloc::Layout::new::<edge_impulse_ffi_rs::bindings::ei_impulse_result_t>(
368 ),
369 );
370 }
371 }
372 }
373 }
374}
375
376pub struct EdgeImpulseClassifier {
378 #[cfg(feature = "ffi")]
379 initialized: bool,
380}
381
382impl Default for EdgeImpulseClassifier {
383 fn default() -> Self {
384 Self::new()
385 }
386}
387
388impl EdgeImpulseClassifier {
389 pub fn new() -> Self {
391 #[cfg(feature = "ffi")]
392 {
393 Self { initialized: false }
394 }
395
396 #[cfg(not(feature = "ffi"))]
397 {
398 Self {}
399 }
400 }
401
402 pub fn init(&mut self) -> EdgeImpulseResult<()> {
404 #[cfg(feature = "ffi")]
405 {
406 unsafe { edge_impulse_ffi_rs::bindings::ei_ffi_run_classifier_init() };
407 self.initialized = true;
408 Ok(())
409 }
410
411 #[cfg(not(feature = "ffi"))]
412 {
413 Err(EdgeImpulseError::Other)
414 }
415 }
416
417 pub fn deinit(&mut self) -> EdgeImpulseResult<()> {
419 #[cfg(feature = "ffi")]
420 {
421 if self.initialized {
422 unsafe { edge_impulse_ffi_rs::bindings::ei_ffi_run_classifier_deinit() };
423 self.initialized = false;
424 }
425 Ok(())
426 }
427
428 #[cfg(not(feature = "ffi"))]
429 {
430 Ok(())
431 }
432 }
433
434 pub fn run_classifier(
436 &self,
437 _signal: &Signal,
438 _debug: bool,
439 ) -> EdgeImpulseResult<InferenceResult> {
440 #[cfg(feature = "ffi")]
441 {
442 if !self.initialized {
443 return Err(EdgeImpulseError::Other);
444 }
445 let result = InferenceResult::new();
446 let result_code = unsafe {
447 edge_impulse_ffi_rs::bindings::ei_ffi_run_classifier(
448 _signal.as_ptr(),
449 result.result,
450 if _debug { 1 } else { 0 },
451 )
452 };
453 if result_code == edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OK {
454 Ok(result)
455 } else {
456 Err(EdgeImpulseError::from(result_code))
457 }
458 }
459
460 #[cfg(not(feature = "ffi"))]
461 {
462 Err(EdgeImpulseError::Other)
463 }
464 }
465
466 pub fn run_classifier_continuous(
468 &self,
469 _signal: &Signal,
470 _debug: bool,
471 _enable_maf: bool,
472 ) -> EdgeImpulseResult<InferenceResult> {
473 #[cfg(feature = "ffi")]
474 {
475 if !self.initialized {
476 return Err(EdgeImpulseError::Other);
477 }
478 let result = InferenceResult::new();
479 let result_code = unsafe {
480 edge_impulse_ffi_rs::bindings::ei_ffi_run_classifier_continuous(
481 _signal.as_ptr(),
482 result.result,
483 if _debug { 1 } else { 0 },
484 if _enable_maf { 1 } else { 0 },
485 )
486 };
487 if result_code == edge_impulse_ffi_rs::bindings::EI_IMPULSE_ERROR::EI_IMPULSE_OK {
488 Ok(result)
489 } else {
490 Err(EdgeImpulseError::from(result_code))
491 }
492 }
493
494 #[cfg(not(feature = "ffi"))]
495 {
496 Err(EdgeImpulseError::Other)
497 }
498 }
499
500 pub fn run_inference(
502 &self,
503 _handle: &mut EdgeImpulseHandle,
504 #[cfg(feature = "ffi")] fmatrix: *mut edge_impulse_ffi_rs::bindings::ei_feature_t,
505 #[cfg(not(feature = "ffi"))] _fmatrix: *mut std::ffi::c_void,
506 _debug: bool,
507 ) -> EdgeImpulseResult<InferenceResult> {
508 #[cfg(feature = "ffi")]
509 {
510 if !self.initialized {
511 return Err(EdgeImpulseError::Other);
512 }
513 let result = InferenceResult::new();
514 let result_code = unsafe {
515 edge_impulse_ffi_rs::bindings::ei_ffi_run_inference(
516 _handle.handle,
517 fmatrix,
518 result.result,
519 if _debug { 1 } else { 0 },
520 )
521 };
522 let error = EdgeImpulseError::from(result_code);
523 if error == EdgeImpulseError::Ok {
524 Ok(result)
525 } else {
526 Err(error)
527 }
528 }
529
530 #[cfg(not(feature = "ffi"))]
531 {
532 Err(EdgeImpulseError::Other)
533 }
534 }
535}
536
537impl Drop for EdgeImpulseClassifier {
538 fn drop(&mut self) {
539 let _ = self.deinit();
540 }
541}
542
543pub struct ModelMetadata;
547
548#[derive(Debug, Clone)]
549pub struct ModelMetadataInfo {
550 pub input_width: usize,
551 pub input_height: usize,
552 pub input_frames: usize,
553 pub label_count: usize,
554 pub project_name: &'static str,
555 pub project_owner: &'static str,
556 pub project_id: usize,
557 pub deploy_version: usize,
558 pub sensor: i32,
559 pub inferencing_engine: usize,
560 pub interval_ms: usize,
561 pub frequency: usize,
562 pub slice_size: usize,
563 pub has_anomaly: bool,
564 pub has_object_detection: bool,
565 pub has_object_tracking: bool,
566 pub raw_sample_count: usize,
567 pub raw_samples_per_frame: usize,
568 pub input_features_count: usize,
569}
570
571#[derive(Debug, Clone)]
572pub struct ClassificationResult {
573 pub label: String,
574 pub value: f32,
575}
576
577#[derive(Debug, Clone)]
578pub struct BoundingBox {
579 pub label: String,
580 pub value: f32,
581 pub x: u32,
582 pub y: u32,
583 pub width: u32,
584 pub height: u32,
585}
586
587#[derive(Debug, Clone)]
588pub struct TimingResult {
589 pub dsp: i32,
590 pub classification: i32,
591 pub anomaly: i32,
592}
593
594impl fmt::Display for ModelMetadataInfo {
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 writeln!(f, "Model Metadata:")?;
597 writeln!(
598 f,
599 " Project: {} (ID: {})",
600 self.project_name, self.project_id
601 )?;
602 writeln!(f, " Owner: {}", self.project_owner)?;
603 writeln!(f, " Deploy version: {}", self.deploy_version)?;
604 writeln!(
605 f,
606 " Input: {}x{} frames: {}",
607 self.input_width, self.input_height, self.input_frames
608 )?;
609 writeln!(f, " Label count: {}", self.label_count)?;
610 writeln!(f, " Sensor: {}", self.sensor)?;
611 writeln!(f, " Inferencing engine: {}", self.inferencing_engine)?;
612 writeln!(f, " Interval (ms): {}", self.interval_ms)?;
613 writeln!(f, " Frequency: {}", self.frequency)?;
614 writeln!(f, " Slice size: {}", self.slice_size)?;
615 writeln!(f, " Has anomaly: {}", self.has_anomaly)?;
616 writeln!(f, " Has object detection: {}", self.has_object_detection)?;
617 writeln!(f, " Has object tracking: {}", self.has_object_tracking)?;
618 writeln!(f, " Raw sample count: {}", self.raw_sample_count)?;
619 writeln!(f, " Raw samples per frame: {}", self.raw_samples_per_frame)?;
620 writeln!(f, " Input features count: {}", self.input_features_count)?;
621 Ok(())
622 }
623}
624
625impl ModelMetadata {
626 pub fn input_width() -> usize {
628 #[cfg(feature = "ffi")]
629 {
630 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_INPUT_WIDTH
631 }
632 #[cfg(not(feature = "ffi"))]
633 {
634 0
635 }
636 }
637
638 pub fn input_height() -> usize {
640 #[cfg(feature = "ffi")]
641 {
642 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_INPUT_HEIGHT
643 }
644 #[cfg(not(feature = "ffi"))]
645 {
646 0
647 }
648 }
649
650 pub fn input_frame_size() -> usize {
652 Self::input_width() * Self::input_height()
653 }
654
655 pub fn input_frames() -> usize {
657 #[cfg(feature = "ffi")]
658 {
659 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_INPUT_FRAMES
660 }
661 #[cfg(not(feature = "ffi"))]
662 {
663 0
664 }
665 }
666
667 pub fn label_count() -> usize {
669 #[cfg(feature = "ffi")]
670 {
671 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_LABEL_COUNT
672 }
673 #[cfg(not(feature = "ffi"))]
674 {
675 0
676 }
677 }
678
679 pub fn project_name() -> &'static str {
681 #[cfg(feature = "ffi")]
682 {
683 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_PROJECT_NAME
684 }
685 #[cfg(not(feature = "ffi"))]
686 {
687 ""
688 }
689 }
690
691 pub fn project_owner() -> &'static str {
693 #[cfg(feature = "ffi")]
694 {
695 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_PROJECT_OWNER
696 }
697 #[cfg(not(feature = "ffi"))]
698 {
699 ""
700 }
701 }
702
703 pub fn project_id() -> usize {
705 #[cfg(feature = "ffi")]
706 {
707 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_PROJECT_ID
708 }
709 #[cfg(not(feature = "ffi"))]
710 {
711 0
712 }
713 }
714
715 pub fn deploy_version() -> usize {
717 #[cfg(feature = "ffi")]
718 {
719 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_PROJECT_DEPLOY_VERSION
720 }
721 #[cfg(not(feature = "ffi"))]
722 {
723 0
724 }
725 }
726
727 pub fn sensor() -> i32 {
729 #[cfg(feature = "ffi")]
730 {
731 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_SENSOR
732 }
733 #[cfg(not(feature = "ffi"))]
734 {
735 0
736 }
737 }
738
739 pub fn inferencing_engine() -> usize {
741 #[cfg(feature = "ffi")]
742 {
743 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_INFERENCING_ENGINE
744 }
745 #[cfg(not(feature = "ffi"))]
746 {
747 0
748 }
749 }
750
751 pub fn interval_ms() -> usize {
753 #[cfg(feature = "ffi")]
754 {
755 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_INTERVAL_MS
756 }
757 #[cfg(not(feature = "ffi"))]
758 {
759 0
760 }
761 }
762
763 pub fn frequency() -> usize {
765 #[cfg(feature = "ffi")]
766 {
767 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_FREQUENCY
768 }
769 #[cfg(not(feature = "ffi"))]
770 {
771 0
772 }
773 }
774
775 pub fn slice_size() -> usize {
777 #[cfg(feature = "ffi")]
778 {
779 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_SLICE_SIZE
780 }
781 #[cfg(not(feature = "ffi"))]
782 {
783 0
784 }
785 }
786
787 pub fn has_anomaly() -> bool {
789 #[cfg(feature = "ffi")]
790 {
791 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_HAS_ANOMALY != 0
792 }
793 #[cfg(not(feature = "ffi"))]
794 {
795 false
796 }
797 }
798
799 pub fn has_object_detection() -> bool {
801 #[cfg(feature = "ffi")]
802 {
803 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_OBJECT_DETECTION != 0
804 }
805 #[cfg(not(feature = "ffi"))]
806 {
807 false
808 }
809 }
810
811 pub fn has_object_tracking() -> bool {
813 #[cfg(feature = "ffi")]
814 {
815 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_OBJECT_TRACKING_ENABLED != 0
816 }
817 #[cfg(not(feature = "ffi"))]
818 {
819 false
820 }
821 }
822
823 pub fn raw_sample_count() -> usize {
825 #[cfg(feature = "ffi")]
826 {
827 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_RAW_SAMPLE_COUNT
828 }
829 #[cfg(not(feature = "ffi"))]
830 {
831 0
832 }
833 }
834
835 pub fn raw_samples_per_frame() -> usize {
837 #[cfg(feature = "ffi")]
838 {
839 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_RAW_SAMPLES_PER_FRAME
840 }
841 #[cfg(not(feature = "ffi"))]
842 {
843 0
844 }
845 }
846
847 pub fn input_features_count() -> usize {
849 #[cfg(feature = "ffi")]
850 {
851 edge_impulse_ffi_rs::model_metadata::EI_CLASSIFIER_NN_INPUT_FRAME_SIZE
852 }
853 #[cfg(not(feature = "ffi"))]
854 {
855 0
856 }
857 }
858
859 pub fn get() -> ModelMetadataInfo {
860 ModelMetadataInfo {
861 input_width: Self::input_width(),
862 input_height: Self::input_height(),
863 input_frames: Self::input_frames(),
864 label_count: Self::label_count(),
865 project_name: Self::project_name(),
866 project_owner: Self::project_owner(),
867 project_id: Self::project_id(),
868 deploy_version: Self::deploy_version(),
869 sensor: Self::sensor(),
870 inferencing_engine: Self::inferencing_engine(),
871 interval_ms: Self::interval_ms(),
872 frequency: Self::frequency(),
873 slice_size: Self::slice_size(),
874 has_anomaly: Self::has_anomaly(),
875 has_object_detection: Self::has_object_detection(),
876 has_object_tracking: Self::has_object_tracking(),
877 raw_sample_count: Self::raw_sample_count(),
878 raw_samples_per_frame: Self::raw_samples_per_frame(),
879 input_features_count: Self::input_features_count(),
880 }
881 }
882}
883
884#[cfg(test)]
885mod tests {
886 use super::*;
887
888 #[test]
889 fn test_ffi_initialization() {
890 let mut classifier = EdgeImpulseClassifier::new();
891 let _ = classifier.init();
893 let _ = classifier.deinit();
894 }
895}