Skip to main content

feagi_sensorimotor/caching/
motor_device_cache.rs

1use crate::configuration::jsonable::JSONInputOutputDefinition;
2use crate::data_pipeline::per_channel_stream_caches::MotorCorticalUnitCache;
3use crate::data_pipeline::{PipelineStageProperties, PipelineStagePropertyIndex};
4use crate::data_types::descriptors::*;
5use crate::data_types::*;
6use crate::neuron_voxel_coding::xyzp::decoders::*;
7use crate::neuron_voxel_coding::xyzp::NeuronVoxelXYZPDecoder;
8use crate::wrapped_io_data::{WrappedIOData, WrappedIOType};
9use feagi_serialization::FeagiByteContainer;
10use feagi_structures::genomic::cortical_area::descriptors::{
11    CorticalChannelCount, CorticalChannelIndex, CorticalUnitIndex, NeuronDepth,
12};
13use feagi_structures::genomic::cortical_area::io_cortical_area_configuration_flag::{
14    FrameChangeHandling, PercentageNeuronPositioning,
15};
16use feagi_structures::genomic::cortical_area::CorticalID;
17use feagi_structures::genomic::MotorCorticalUnit;
18use feagi_structures::neuron_voxels::xyzp::CorticalMappedXYZPNeuronVoxels;
19use feagi_structures::{motor_cortical_units, FeagiDataError, FeagiSignalIndex};
20use serde_json::json;
21use std::collections::HashMap;
22use std::fmt::{Display, Formatter};
23use std::time::Instant;
24
25macro_rules! motor_unit_functions {
26    (
27        MotorCorticalUnit {
28            $(
29                $(#[doc = $doc:expr])?
30                $cortical_type_key_name:ident => {
31                    friendly_name: $friendly_name:expr,
32                    accepted_wrapped_io_data_type: $accepted_wrapped_io_data_type:ident,
33                    cortical_id_unit_reference: $cortical_id_unit_reference:expr,
34                    number_cortical_areas: $number_cortical_areas:expr,
35                    cortical_type_parameters: {
36                        $($param_name:ident: $param_type:ty),* $(,)?
37                    },
38                    $(allowed_frame_change_handling: [$($allowed_frame:ident),* $(,)?],)?
39                    cortical_area_properties: {
40                        $($area_index:tt => ($cortical_area_type_expr:expr, relative_position: [$rel_x:expr, $rel_y:expr, $rel_z:expr], channel_dimensions_default: [$dim_default_x:expr, $dim_default_y:expr, $dim_default_z:expr], channel_dimensions_min: [$dim_min_x:expr, $dim_min_y:expr, $dim_min_z:expr], channel_dimensions_max: [$dim_max_x:expr, $dim_max_y:expr, $dim_max_z:expr])),* $(,)?
41                    }
42                }
43            ),* $(,)?
44        }
45    ) =>
46    {
47        $(
48            motor_unit_functions!(@generate_functions
49            $cortical_type_key_name,
50            $accepted_wrapped_io_data_type
51            );
52        )*
53    };
54
55    //region Similar Functions
56    // Helper macro to generate stage and other similar functions
57    (@generate_similar_functions
58        $cortical_type_key_name:ident,
59        $wrapped_data_type:ident
60    ) => {
61        ::paste::paste! {
62            pub fn [<$cortical_type_key_name:snake _read_preprocessed_cache_value>](
63                &self,
64                unit: CorticalUnitIndex,
65                channel: CorticalChannelIndex,
66            ) -> Result< $wrapped_data_type, FeagiDataError> {
67
68                const MOTOR_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
69                let wrapped = self.try_read_preprocessed_cached_value(MOTOR_TYPE, unit, channel)?;
70                let val: $wrapped_data_type = wrapped.try_into()?;
71                Ok(val)
72            }
73
74            pub fn [<$cortical_type_key_name:snake _read_postprocessed_cache_value>](
75                &self,
76                unit: CorticalUnitIndex,
77                channel: CorticalChannelIndex,
78            ) -> Result< $wrapped_data_type, FeagiDataError> {
79
80                const MOTOR_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
81                let wrapped = self.try_read_postprocessed_cached_value(MOTOR_TYPE, unit, channel)?;
82                let val: $wrapped_data_type = wrapped.try_into()?;
83                Ok(val)
84            }
85
86            pub fn [<$cortical_type_key_name:snake _try_register_motor_callback>]<F>(
87                &mut self,
88                unit: CorticalUnitIndex,
89                channel_index: CorticalChannelIndex,
90                callback: F
91            ) -> Result<FeagiSignalIndex, FeagiDataError>
92            where F: Fn(&WrappedIOData) + Send + Sync + 'static,
93            {
94                const MOTOR_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
95                let signal_index = self.try_register_motor_callback(MOTOR_TYPE, unit, channel_index, callback)?;
96                Ok(signal_index)
97            }
98
99            pub fn [<$cortical_type_key_name:snake _get_single_stage_properties>](
100                &mut self,
101                unit: CorticalUnitIndex,
102                channel_index: CorticalChannelIndex,
103                stage_index: PipelineStagePropertyIndex
104            ) -> Result<PipelineStageProperties, FeagiDataError>
105            {
106                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
107                let stage = self.try_get_single_stage_properties(MOTOR_UNIT_TYPE, unit, channel_index, stage_index)?;
108                Ok(stage)
109            }
110
111            pub fn [<$cortical_type_key_name:snake _get_all_stage_properties>](
112                &mut self,
113                unit: CorticalUnitIndex,
114                channel_index: CorticalChannelIndex
115            ) -> Result<Vec<PipelineStageProperties>, FeagiDataError>
116            {
117                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
118                let stages = self.try_get_all_stage_properties(MOTOR_UNIT_TYPE, unit, channel_index)?;
119                Ok(stages)
120            }
121
122            pub fn [<$cortical_type_key_name:snake _update_single_stage_properties>](
123                &mut self,
124                unit: CorticalUnitIndex,
125                channel_index: CorticalChannelIndex,
126                pipeline_stage_property_index: PipelineStagePropertyIndex,
127                updating_property: PipelineStageProperties
128            ) -> Result<(), FeagiDataError>
129            {
130                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
131                self.try_update_single_stage_properties(MOTOR_UNIT_TYPE, unit, channel_index, pipeline_stage_property_index, updating_property)?;
132                Ok(())
133            }
134
135            pub fn [<$cortical_type_key_name:snake _update_all_stage_properties>](
136                &mut self,
137                unit: CorticalUnitIndex,
138                channel_index: CorticalChannelIndex,
139                updated_pipeline_stage_properties: Vec<PipelineStageProperties>
140            ) -> Result<(), FeagiDataError>
141            {
142                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
143                self.try_update_all_stage_properties(MOTOR_UNIT_TYPE, unit, channel_index, updated_pipeline_stage_properties)?;
144                Ok(())
145            }
146
147            pub fn [<$cortical_type_key_name:snake _replace_single_stage>](
148                &mut self,
149                unit: CorticalUnitIndex,
150                channel_index: CorticalChannelIndex,
151                pipeline_stage_property_index: PipelineStagePropertyIndex,
152                replacing_property: PipelineStageProperties
153            ) -> Result<(), FeagiDataError>
154            {
155                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
156                self.try_replace_single_stage(MOTOR_UNIT_TYPE, unit, channel_index, pipeline_stage_property_index, replacing_property)?;
157                Ok(())
158            }
159
160            pub fn [<$cortical_type_key_name:snake _replace_all_stages>](
161                &mut self,
162                unit: CorticalUnitIndex,
163                channel_index: CorticalChannelIndex,
164                new_pipeline_stage_properties: Vec<PipelineStageProperties>
165            ) -> Result<(), FeagiDataError>
166            {
167                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
168                self.try_replace_all_stages(MOTOR_UNIT_TYPE, unit, channel_index, new_pipeline_stage_properties)?;
169                Ok(())
170            }
171
172            pub fn [<$cortical_type_key_name:snake _removing_all_stages>](
173                &mut self,
174                unit: CorticalUnitIndex,
175                channel_index: CorticalChannelIndex
176            ) -> Result<(), FeagiDataError>
177            {
178                const MOTOR_UNIT_TYPE: MotorCorticalUnit = MotorCorticalUnit::$cortical_type_key_name;
179                self.try_removing_all_stages(MOTOR_UNIT_TYPE, unit, channel_index)?;
180                Ok(())
181            }
182        }
183    };
184    //endregion
185
186    // Arm for WrappedIOType::GazeProperties
187    (@generate_functions
188        $motor_unit:ident,
189        GazeProperties
190    ) => {
191        ::paste::paste! {
192            pub fn [<$motor_unit:snake _register>](
193                &mut self,
194                unit: CorticalUnitIndex,
195                number_channels: CorticalChannelCount,
196                frame_change_handling: FrameChangeHandling,
197                eccentricity_z_neuron_resolution: NeuronDepth,
198                modulation_z_neuron_resolution: NeuronDepth,
199                percentage_neuron_positioning: PercentageNeuronPositioning
200                ) -> Result<(), FeagiDataError>
201            {
202                let eccentricity_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
203                let modularity_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[1];
204
205                let io_props: serde_json::Map<String, serde_json::Value> = json!({
206                    "frame_change_handling": frame_change_handling,
207                    "percentage_neuron_positioning": percentage_neuron_positioning
208                }).as_object().unwrap().clone();
209
210                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = GazePropertiesNeuronVoxelXYZPDecoder::new_box(
211                    eccentricity_cortical_id,
212                    modularity_cortical_id,
213                    eccentricity_z_neuron_resolution,
214                    modulation_z_neuron_resolution,
215                    number_channels,
216                    percentage_neuron_positioning,
217                )?;
218
219                let initial_val: WrappedIOData = WrappedIOData::GazeProperties(GazeProperties::create_default_centered());
220                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
221                Ok(())
222            }
223        }
224
225        motor_unit_functions!(@generate_similar_functions $motor_unit, GazeProperties);
226    };
227
228    // Arm for WrappedIOType::Percentage
229    (@generate_functions
230        $motor_unit:ident,
231        Percentage
232    ) => {
233        ::paste::paste! {
234            pub fn [<$motor_unit:snake _register>](
235                &mut self,
236                unit: CorticalUnitIndex,
237                number_channels: CorticalChannelCount,
238                frame_change_handling: FrameChangeHandling,
239                z_neuron_resolution: NeuronDepth,
240                percentage_neuron_positioning: PercentageNeuronPositioning
241                ) -> Result<(), FeagiDataError>
242            {
243                let cortical_ids = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit);
244
245                // Handle both single-area and dual-area percentage types
246                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = match (cortical_ids.get(0), cortical_ids.get(1)) {
247                    (Some(&id0), Some(&id1)) => {
248                        // Dual-area type (e.g., PositionalServo with absolute + incremental)
249                        PositionalServoNeuronVoxelXYZPDecoder::new_box(
250                            id0,
251                            id1,
252                            z_neuron_resolution,
253                            number_channels,
254                            percentage_neuron_positioning,
255                        )?
256                    }
257                    (Some(&id0), None) => {
258                        // Single-area percentage type (e.g., CountOutput)
259                        PercentageNeuronVoxelXYZPDecoder::new_box(
260                            id0,
261                            z_neuron_resolution,
262                            number_channels,
263                            percentage_neuron_positioning,
264                            false,
265                            PercentageChannelDimensionality::D1
266                        )?
267                    }
268                    _ => {
269                        return Err(FeagiDataError::InternalError(
270                            "Expected at least one cortical ID for Percentage motor unit".to_string()
271                        ));
272                    }
273                };
274
275                let io_props: serde_json::Map<String, serde_json::Value> = json!({
276                    "frame_change_handling": frame_change_handling,
277                    "percentage_neuron_positioning": percentage_neuron_positioning
278                }).as_object().unwrap().clone();
279
280                let initial_val: WrappedIOData = WrappedIOData::Percentage(Percentage::new_zero());
281                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
282                Ok(())
283            }
284        }
285
286        motor_unit_functions!(@generate_similar_functions $motor_unit, Percentage);
287    };
288
289    // Arm for WrappedIOType::Percentage3D
290    (@generate_functions
291        $motor_unit:ident,
292        Percentage_3D
293    ) => {
294        ::paste::paste! {
295            pub fn [<$motor_unit:snake _register>](
296                &mut self,
297                unit: CorticalUnitIndex,
298                number_channels: CorticalChannelCount,
299                frame_change_handling: FrameChangeHandling,
300                z_neuron_resolution: NeuronDepth,
301                percentage_neuron_positioning: PercentageNeuronPositioning
302                ) -> Result<(), FeagiDataError>
303            {
304                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
305
306                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = PercentageNeuronVoxelXYZPDecoder::new_box(
307                    cortical_id,
308                    z_neuron_resolution,
309                    number_channels,
310                    percentage_neuron_positioning,
311                    false,
312                    PercentageChannelDimensionality::D3
313                )?;
314
315                let io_props: serde_json::Map<String, serde_json::Value> = json!({
316                    "frame_change_handling": frame_change_handling,
317                    "percentage_neuron_positioning": percentage_neuron_positioning
318                }).as_object().unwrap().clone();
319
320                let initial_val: WrappedIOData = WrappedIOData::Percentage_3D(Percentage3D::new_zero());
321                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
322                Ok(())
323            }
324        }
325
326        motor_unit_functions!(@generate_similar_functions $motor_unit, Percentage3D);
327    };
328
329    // Arm for WrappedIOType::SignedPercentage
330    (@generate_functions
331        $motor_unit:ident,
332        SignedPercentage
333    ) => {
334        ::paste::paste! {
335            pub fn [<$motor_unit:snake _register>](
336                &mut self,
337                unit: CorticalUnitIndex,
338                number_channels: CorticalChannelCount,
339                frame_change_handling: FrameChangeHandling,
340                z_neuron_resolution: NeuronDepth,
341                percentage_neuron_positioning: PercentageNeuronPositioning
342                ) -> Result<(), FeagiDataError>
343            {
344                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
345                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = PercentageNeuronVoxelXYZPDecoder::new_box(
346                    cortical_id,
347                    z_neuron_resolution,
348                    number_channels,
349                    percentage_neuron_positioning,
350                    true,
351                    PercentageChannelDimensionality::D1
352                )?;
353
354                let io_props: serde_json::Map<String, serde_json::Value> = json!({
355                    "frame_change_handling": frame_change_handling,
356                    "percentage_neuron_positioning": percentage_neuron_positioning
357                }).as_object().unwrap().clone();
358
359                let initial_val: WrappedIOData = WrappedIOData::SignedPercentage(SignedPercentage::new_from_m1_1_unchecked(0.0));
360                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
361                Ok(())
362            }
363        }
364
365        motor_unit_functions!(@generate_similar_functions $motor_unit, SignedPercentage);
366    };
367
368    // Arm for WrappedIOType::MiscData
369    (@generate_functions
370        $motor_unit:ident,
371        MiscData
372    ) => {
373        ::paste::paste! {
374            pub fn [<$motor_unit:snake _register>](
375                &mut self,
376                unit: CorticalUnitIndex,
377                number_channels: CorticalChannelCount,
378                frame_change_handling: FrameChangeHandling,
379                misc_data_dimensions: MiscDataDimensions
380                ) -> Result<(), FeagiDataError>
381            {
382                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, unit)[0];
383                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = MiscDataNeuronVoxelXYZPDecoder::new_box(cortical_id, misc_data_dimensions, number_channels)?;
384
385                let io_props: serde_json::Map<String, serde_json::Value> = json!({
386                    "frame_change_handling": frame_change_handling
387                }).as_object().unwrap().clone();
388
389                let initial_val: WrappedIOData = WrappedIOType::MiscData(Some(misc_data_dimensions)).create_blank_data_of_type()?;
390                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
391                Ok(())
392            }
393        }
394
395        motor_unit_functions!(@generate_similar_functions $motor_unit, MiscData);
396    };
397
398    // Arm for WrappedIOType::ImageFrame
399    (@generate_functions
400        $motor_unit:ident,
401        ImageFrame
402    ) => {
403        ::paste::paste! {
404            pub fn [<$motor_unit:snake _register>](
405                &mut self,
406                unit: CorticalUnitIndex,
407                number_channels: CorticalChannelCount,
408                frame_change_handling: FrameChangeHandling,
409                image_properties: ImageFrameProperties,
410                ) -> Result<(), FeagiDataError>
411            {
412                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, unit)[0];
413                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = CartesianPlaneNeuronVoxelXYZPDecoder::new_box(cortical_id, &image_properties, number_channels)?;
414
415                let io_props: serde_json::Map<String, serde_json::Value> = json!({
416                    "frame_change_handling": frame_change_handling
417                }).as_object().unwrap().clone();
418
419                let initial_val: WrappedIOData = WrappedIOType::ImageFrame(Some(image_properties)).create_blank_data_of_type()?;
420                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
421                Ok(())
422            }
423        }
424
425        motor_unit_functions!(@generate_similar_functions $motor_unit, ImageFrame);
426    };
427
428    // Arm for WrappedIOType::ImageFilteringSettings
429    (@generate_functions
430        $motor_unit:ident,
431        ImageFilteringSettings
432    ) => {
433        ::paste::paste! {
434            pub fn [<$motor_unit:snake _register>](
435                &mut self,
436                unit: CorticalUnitIndex,
437                number_channels: CorticalChannelCount,
438                frame_change_handling: FrameChangeHandling,
439                z_neuron_resolution: NeuronDepth,
440                percentage_neuron_positioning: PercentageNeuronPositioning
441                ) -> Result<(), FeagiDataError>
442            {
443                let brightness_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
444                let contrast_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[1];
445                let diff_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[2];
446
447                let io_props: serde_json::Map<String, serde_json::Value> = json!({
448                    "frame_change_handling": frame_change_handling,
449                    "percentage_neuron_positioning": percentage_neuron_positioning
450                }).as_object().unwrap().clone();
451
452                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = ImageFilteringSettingsNeuronVoxelXYZPDecoder::new_box(
453                    brightness_cortical_id,
454                    contrast_cortical_id,
455                    diff_cortical_id,
456                    z_neuron_resolution,
457                    z_neuron_resolution,
458                    z_neuron_resolution,
459                    number_channels,
460                    percentage_neuron_positioning)?;
461
462
463
464                let initial_val: WrappedIOData = WrappedIOData::ImageFilteringSettings(ImageFilteringSettings::default());
465
466                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
467                Ok(())
468            }
469        }
470
471        motor_unit_functions!(@generate_similar_functions $motor_unit, ImageFilteringSettings);
472    };
473}
474
475pub struct MotorDeviceCache {
476    motor_cortical_unit_caches:
477        HashMap<(MotorCorticalUnit, CorticalUnitIndex), MotorCorticalUnitCache>,
478    neuron_data: CorticalMappedXYZPNeuronVoxels,
479    byte_data: FeagiByteContainer,
480    previous_burst: Instant,
481    #[allow(dead_code)]
482    is_active: bool,
483}
484
485impl std::fmt::Debug for MotorDeviceCache {
486    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487        f.debug_struct("MotorDeviceCache")
488            .field(
489                "stream_caches_count",
490                &self.motor_cortical_unit_caches.len(),
491            )
492            .field("neuron_data", &self.neuron_data)
493            .field("byte_data", &self.byte_data)
494            .field("previous_burst", &self.previous_burst)
495            .finish()
496    }
497}
498
499impl MotorDeviceCache {
500    pub fn new() -> Self {
501        MotorDeviceCache {
502            motor_cortical_unit_caches: HashMap::new(),
503            neuron_data: CorticalMappedXYZPNeuronVoxels::new(),
504            byte_data: FeagiByteContainer::new_empty(),
505            previous_burst: Instant::now(),
506            is_active: false,
507        }
508    }
509
510    /// Ingest already-decoded motor neuron data and run callbacks.
511    ///
512    /// This is a zero-copy convenience for callers that already have a decoded
513    /// `CorticalMappedXYZPNeuronVoxels` (e.g. from `feagi-agent`'s motor receive path),
514    /// avoiding a re-serialization into `FeagiByteContainer`.
515    ///
516    /// # Arguments
517    /// - `neuron_data`: decoded motor neuron voxels as published by FEAGI
518    /// - `time_of_decode`: timestamp used for callback timing logic
519    pub fn ingest_neuron_data_and_run_callbacks(
520        &mut self,
521        neuron_data: CorticalMappedXYZPNeuronVoxels,
522        time_of_decode: Instant,
523    ) -> Result<(), FeagiDataError> {
524        self.neuron_data = neuron_data;
525        self.try_decode_neural_data_into_cache(time_of_decode)
526    }
527
528    // Clears all registered devices and cache, to allow setting up again
529    pub fn reset(&mut self) {
530        self.motor_cortical_unit_caches.clear();
531        self.neuron_data = CorticalMappedXYZPNeuronVoxels::new();
532        self.byte_data = FeagiByteContainer::new_empty();
533        self.previous_burst = Instant::now();
534    }
535
536    pub fn verify_existence(
537        &self,
538        motor_cortical_unit: MotorCorticalUnit,
539        unit_index: CorticalUnitIndex,
540        cortical_channel_index: CorticalChannelIndex,
541    ) -> Result<(), FeagiDataError> {
542        let motor_stream_caches =
543            self.try_get_motor_channel_stream_caches(motor_cortical_unit, unit_index)?;
544        motor_stream_caches.verify_channel_exists(cortical_channel_index)
545    }
546
547    motor_cortical_units!(motor_unit_functions);
548
549    //region Data IO
550
551    pub fn get_feagi_byte_container(&self) -> &FeagiByteContainer {
552        &self.byte_data
553    }
554
555    pub fn get_feagi_byte_container_mut(&mut self) -> &mut FeagiByteContainer {
556        &mut self.byte_data
557    }
558
559    pub fn get_neurons(&self) -> &CorticalMappedXYZPNeuronVoxels {
560        &self.neuron_data
561    }
562
563    // Returns true if data was retrieved
564    pub fn try_decode_bytes_to_neural_data(&mut self) -> Result<bool, FeagiDataError> {
565        self.byte_data
566            .try_update_struct_from_first_found_struct_of_type(&mut self.neuron_data)
567    }
568
569    pub fn try_decode_neural_data_into_cache(
570        &mut self,
571        time_of_decode: Instant,
572    ) -> Result<(), FeagiDataError> {
573        for motor_channel_stream_cache in self.motor_cortical_unit_caches.values_mut() {
574            motor_channel_stream_cache.try_read_neuron_data_to_cache_and_do_callbacks(
575                &self.neuron_data,
576                time_of_decode,
577            )?;
578        }
579        Ok(())
580    }
581
582    //endregion
583
584    //region  JSON import / export
585
586    pub fn import_from_output_definition(
587        &mut self,
588        replacing_definition: &JSONInputOutputDefinition,
589    ) -> Result<(), FeagiDataError> {
590        self.reset();
591        let output_units_and_decoder_properties =
592            replacing_definition.get_output_units_and_decoder_properties();
593        for (motor_unit, unit_and_decoder_definitions) in output_units_and_decoder_properties {
594            for unit_and_decoder_definition in unit_and_decoder_definitions {
595                let unit_definition = &unit_and_decoder_definition.0;
596                let encoder_definition = &unit_and_decoder_definition.1;
597
598                if self
599                    .motor_cortical_unit_caches
600                    .contains_key(&(*motor_unit, unit_definition.cortical_unit_index))
601                {
602                    return Err(FeagiDataError::DeserializationError(format!(
603                        "Already registered motor {} of unit index {}!",
604                        *motor_unit, unit_definition.cortical_unit_index
605                    )));
606                }
607
608                let new_unit = MotorCorticalUnitCache::new_from_json(
609                    motor_unit,
610                    unit_definition,
611                    encoder_definition,
612                )?;
613                self.motor_cortical_unit_caches
614                    .insert((*motor_unit, unit_definition.cortical_unit_index), new_unit);
615            }
616        }
617        Ok(())
618    }
619
620    pub fn export_to_output_definition(
621        &self,
622        filling_definition: &mut JSONInputOutputDefinition,
623    ) -> Result<(), FeagiDataError> {
624        for ((motor_cortical_unit, cortical_unit_index), motor_channel_stream_caches) in
625            self.motor_cortical_unit_caches.iter()
626        {
627            let unit_and_encoder =
628                motor_channel_stream_caches.export_as_jsons(*cortical_unit_index);
629            filling_definition.insert_motor(
630                *motor_cortical_unit,
631                unit_and_encoder.0,
632                unit_and_encoder.1,
633            );
634        }
635        Ok(())
636    }
637
638    //endregion
639
640    //region Internal
641
642    //region Cache Abstractions
643
644    fn register(
645        &mut self,
646        motor_type: MotorCorticalUnit,
647        unit_index: CorticalUnitIndex,
648        neuron_decoder: Box<dyn NeuronVoxelXYZPDecoder>,
649        io_configuration_flags: serde_json::Map<String, serde_json::Value>,
650        number_channels: CorticalChannelCount,
651        initial_cached_value: WrappedIOData,
652    ) -> Result<(), FeagiDataError> {
653        // NOTE: The length of pipeline_stages_across_channels denotes the number of channels!
654
655        if self
656            .motor_cortical_unit_caches
657            .contains_key(&(motor_type, unit_index))
658        {
659            return Err(FeagiDataError::BadParameters(format!(
660                "Already registered motor {} of unit index {}!",
661                motor_type, unit_index
662            )));
663        }
664
665        self.motor_cortical_unit_caches.insert(
666            (motor_type, unit_index),
667            MotorCorticalUnitCache::new(
668                neuron_decoder,
669                io_configuration_flags,
670                number_channels,
671                initial_cached_value,
672            )?,
673        );
674
675        Ok(())
676    }
677
678    //region Data
679
680    fn try_read_preprocessed_cached_value(
681        &self,
682        motor_type: MotorCorticalUnit,
683        unit_index: CorticalUnitIndex,
684        channel_index: CorticalChannelIndex,
685    ) -> Result<&WrappedIOData, FeagiDataError> {
686        let motor_stream_caches =
687            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
688        motor_stream_caches.get_preprocessed_motor_value(channel_index)
689    }
690
691    fn try_read_postprocessed_cached_value(
692        &self,
693        motor_type: MotorCorticalUnit,
694        unit_index: CorticalUnitIndex,
695        channel_index: CorticalChannelIndex,
696    ) -> Result<&WrappedIOData, FeagiDataError> {
697        let motor_stream_caches =
698            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
699        motor_stream_caches.get_postprocessed_motor_value(channel_index)
700    }
701
702    fn try_register_motor_callback<F>(
703        &mut self,
704        motor_type: MotorCorticalUnit,
705        unit_index: CorticalUnitIndex,
706        channel_index: CorticalChannelIndex,
707        callback: F,
708    ) -> Result<FeagiSignalIndex, FeagiDataError>
709    where
710        F: Fn(&WrappedIOData) + Send + Sync + 'static,
711    {
712        let motor_stream_caches =
713            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
714        let index =
715            motor_stream_caches.try_connect_to_data_processed_signal(channel_index, callback)?;
716        Ok(index)
717    }
718
719    //endregion
720
721    //region Stages
722
723    fn try_get_single_stage_properties(
724        &self,
725        motor_type: MotorCorticalUnit,
726        unit_index: CorticalUnitIndex,
727        channel_index: CorticalChannelIndex,
728        stage_index: PipelineStagePropertyIndex,
729    ) -> Result<PipelineStageProperties, FeagiDataError> {
730        let motor_stream_caches =
731            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
732        motor_stream_caches.try_get_single_stage_properties(channel_index, stage_index)
733    }
734
735    fn try_get_all_stage_properties(
736        &self,
737        motor_type: MotorCorticalUnit,
738        unit_index: CorticalUnitIndex,
739        channel_index: CorticalChannelIndex,
740    ) -> Result<Vec<PipelineStageProperties>, FeagiDataError> {
741        let motor_stream_caches =
742            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
743        motor_stream_caches.get_all_stage_properties(channel_index)
744    }
745
746    fn try_update_single_stage_properties(
747        &mut self,
748        motor_type: MotorCorticalUnit,
749        unit_index: CorticalUnitIndex,
750        channel_index: CorticalChannelIndex,
751        pipeline_stage_property_index: PipelineStagePropertyIndex,
752        replacing_property: PipelineStageProperties,
753    ) -> Result<(), FeagiDataError> {
754        let motor_stream_caches =
755            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
756        motor_stream_caches.try_update_single_stage_properties(
757            channel_index,
758            pipeline_stage_property_index,
759            replacing_property,
760        )
761    }
762
763    fn try_update_all_stage_properties(
764        &mut self,
765        motor_type: MotorCorticalUnit,
766        unit_index: CorticalUnitIndex,
767        channel_index: CorticalChannelIndex,
768        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
769    ) -> Result<(), FeagiDataError> {
770        let motor_stream_caches =
771            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
772        motor_stream_caches
773            .try_update_all_stage_properties(channel_index, new_pipeline_stage_properties)
774    }
775
776    fn try_replace_single_stage(
777        &mut self,
778        motor_type: MotorCorticalUnit,
779        unit_index: CorticalUnitIndex,
780        channel_index: CorticalChannelIndex,
781        replacing_at_index: PipelineStagePropertyIndex,
782        new_pipeline_stage_properties: PipelineStageProperties,
783    ) -> Result<(), FeagiDataError> {
784        let motor_stream_caches =
785            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
786        motor_stream_caches.try_replace_single_stage(
787            channel_index,
788            replacing_at_index,
789            new_pipeline_stage_properties,
790        )
791    }
792
793    fn try_replace_all_stages(
794        &mut self,
795        motor_type: MotorCorticalUnit,
796        unit_index: CorticalUnitIndex,
797        channel_index: CorticalChannelIndex,
798        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
799    ) -> Result<(), FeagiDataError> {
800        let motor_stream_caches =
801            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
802        motor_stream_caches.try_replace_all_stages(channel_index, new_pipeline_stage_properties)
803    }
804
805    fn try_removing_all_stages(
806        &mut self,
807        sensor_type: MotorCorticalUnit,
808        unit_index: CorticalUnitIndex,
809        channel_index: CorticalChannelIndex,
810    ) -> Result<(), FeagiDataError> {
811        let motor_stream_cache =
812            self.try_get_motor_channel_stream_caches_mut(sensor_type, unit_index)?;
813        motor_stream_cache.try_removing_all_stages(channel_index)?;
814        Ok(())
815    }
816
817    //endregion
818
819    //endregion
820
821    //region Hashmap Interactions
822
823    fn try_get_motor_channel_stream_caches(
824        &self,
825        motor_type: MotorCorticalUnit,
826        unit_index: CorticalUnitIndex,
827    ) -> Result<&MotorCorticalUnitCache, FeagiDataError> {
828        let check = self
829            .motor_cortical_unit_caches
830            .get(&(motor_type, unit_index));
831        if check.is_none() {
832            return Err(FeagiDataError::BadParameters(format!(
833                "Unable to find {} of cortical unit index {} in registered motor's list!",
834                motor_type, unit_index
835            )));
836        }
837        let check = check.unwrap();
838        Ok(check)
839    }
840
841    fn try_get_motor_channel_stream_caches_mut(
842        &mut self,
843        motor_type: MotorCorticalUnit,
844        unit_index: CorticalUnitIndex,
845    ) -> Result<&mut MotorCorticalUnitCache, FeagiDataError> {
846        let check = self
847            .motor_cortical_unit_caches
848            .get_mut(&(motor_type, unit_index));
849        if check.is_none() {
850            return Err(FeagiDataError::BadParameters(format!(
851                "Unable to find {} of cortical unit index {} in registered motor's list!",
852                motor_type, unit_index
853            )));
854        }
855        let check = check.unwrap();
856        Ok(check)
857    }
858
859    //endregion
860
861    //endregion
862}
863
864impl Default for MotorDeviceCache {
865    fn default() -> Self {
866        Self::new()
867    }
868}
869
870impl Display for MotorDeviceCache {
871    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
872        Ok(writeln!(f, "Motor Device Cache:")?)
873    }
874}