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_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
244                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = PercentageNeuronVoxelXYZPDecoder::new_box(
245                    cortical_id,
246                    z_neuron_resolution,
247                    number_channels,
248                    percentage_neuron_positioning,
249                    false,
250                    PercentageChannelDimensionality::D1
251                )?;
252
253                let io_props: serde_json::Map<String, serde_json::Value> = json!({
254                    "frame_change_handling": frame_change_handling,
255                    "percentage_neuron_positioning": percentage_neuron_positioning
256                }).as_object().unwrap().clone();
257
258                let initial_val: WrappedIOData = WrappedIOData::Percentage(Percentage::new_zero());
259                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
260                Ok(())
261            }
262        }
263
264        motor_unit_functions!(@generate_similar_functions $motor_unit, Percentage);
265    };
266
267    // Arm for WrappedIOType::Percentage3D
268    (@generate_functions
269        $motor_unit:ident,
270        Percentage_3D
271    ) => {
272        ::paste::paste! {
273            pub fn [<$motor_unit:snake _register>](
274                &mut self,
275                unit: CorticalUnitIndex,
276                number_channels: CorticalChannelCount,
277                frame_change_handling: FrameChangeHandling,
278                z_neuron_resolution: NeuronDepth,
279                percentage_neuron_positioning: PercentageNeuronPositioning
280                ) -> Result<(), FeagiDataError>
281            {
282                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
283
284                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = PercentageNeuronVoxelXYZPDecoder::new_box(
285                    cortical_id,
286                    z_neuron_resolution,
287                    number_channels,
288                    percentage_neuron_positioning,
289                    false,
290                    PercentageChannelDimensionality::D3
291                )?;
292
293                let io_props: serde_json::Map<String, serde_json::Value> = json!({
294                    "frame_change_handling": frame_change_handling,
295                    "percentage_neuron_positioning": percentage_neuron_positioning
296                }).as_object().unwrap().clone();
297
298                let initial_val: WrappedIOData = WrappedIOData::Percentage_3D(Percentage3D::new_zero());
299                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
300                Ok(())
301            }
302        }
303
304        motor_unit_functions!(@generate_similar_functions $motor_unit, Percentage3D);
305    };
306
307    // Arm for WrappedIOType::SignedPercentage
308    (@generate_functions
309        $motor_unit:ident,
310        SignedPercentage
311    ) => {
312        ::paste::paste! {
313            pub fn [<$motor_unit:snake _register>](
314                &mut self,
315                unit: CorticalUnitIndex,
316                number_channels: CorticalChannelCount,
317                frame_change_handling: FrameChangeHandling,
318                z_neuron_resolution: NeuronDepth,
319                percentage_neuron_positioning: PercentageNeuronPositioning
320                ) -> Result<(), FeagiDataError>
321            {
322                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
323                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = PercentageNeuronVoxelXYZPDecoder::new_box(
324                    cortical_id,
325                    z_neuron_resolution,
326                    number_channels,
327                    percentage_neuron_positioning,
328                    true,
329                    PercentageChannelDimensionality::D1
330                )?;
331
332                let io_props: serde_json::Map<String, serde_json::Value> = json!({
333                    "frame_change_handling": frame_change_handling,
334                    "percentage_neuron_positioning": percentage_neuron_positioning
335                }).as_object().unwrap().clone();
336
337                let initial_val: WrappedIOData = WrappedIOData::SignedPercentage(SignedPercentage::new_from_m1_1_unchecked(0.0));
338                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
339                Ok(())
340            }
341        }
342
343        motor_unit_functions!(@generate_similar_functions $motor_unit, SignedPercentage);
344    };
345
346    // Arm for WrappedIOType::MiscData
347    (@generate_functions
348        $motor_unit:ident,
349        MiscData
350    ) => {
351        ::paste::paste! {
352            pub fn [<$motor_unit:snake _register>](
353                &mut self,
354                unit: CorticalUnitIndex,
355                number_channels: CorticalChannelCount,
356                frame_change_handling: FrameChangeHandling,
357                misc_data_dimensions: MiscDataDimensions
358                ) -> Result<(), FeagiDataError>
359            {
360                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, unit)[0];
361                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = MiscDataNeuronVoxelXYZPDecoder::new_box(cortical_id, misc_data_dimensions, number_channels)?;
362
363                let io_props: serde_json::Map<String, serde_json::Value> = json!({
364                    "frame_change_handling": frame_change_handling
365                }).as_object().unwrap().clone();
366
367                let initial_val: WrappedIOData = WrappedIOType::MiscData(Some(misc_data_dimensions)).create_blank_data_of_type()?;
368                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
369                Ok(())
370            }
371        }
372
373        motor_unit_functions!(@generate_similar_functions $motor_unit, MiscData);
374    };
375
376    // Arm for WrappedIOType::ImageFrame
377    (@generate_functions
378        $motor_unit:ident,
379        ImageFrame
380    ) => {
381        ::paste::paste! {
382            pub fn [<$motor_unit:snake _register>](
383                &mut self,
384                unit: CorticalUnitIndex,
385                number_channels: CorticalChannelCount,
386                frame_change_handling: FrameChangeHandling,
387                image_properties: ImageFrameProperties,
388                ) -> Result<(), FeagiDataError>
389            {
390                let cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, unit)[0];
391                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = CartesianPlaneNeuronVoxelXYZPDecoder::new_box(cortical_id, &image_properties, number_channels)?;
392
393                let io_props: serde_json::Map<String, serde_json::Value> = json!({
394                    "frame_change_handling": frame_change_handling
395                }).as_object().unwrap().clone();
396
397                let initial_val: WrappedIOData = WrappedIOType::ImageFrame(Some(image_properties)).create_blank_data_of_type()?;
398                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
399                Ok(())
400            }
401        }
402
403        motor_unit_functions!(@generate_similar_functions $motor_unit, ImageFrame);
404    };
405
406    // Arm for WrappedIOType::ImageFilteringSettings
407    (@generate_functions
408        $motor_unit:ident,
409        ImageFilteringSettings
410    ) => {
411        ::paste::paste! {
412            pub fn [<$motor_unit:snake _register>](
413                &mut self,
414                unit: CorticalUnitIndex,
415                number_channels: CorticalChannelCount,
416                frame_change_handling: FrameChangeHandling,
417                z_neuron_resolution: NeuronDepth,
418                percentage_neuron_positioning: PercentageNeuronPositioning
419                ) -> Result<(), FeagiDataError>
420            {
421                let brightness_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
422                let contrast_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[1];
423                let diff_cortical_id: CorticalID = MotorCorticalUnit::[<get_cortical_ids_array_for_ $motor_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[2];
424
425                let io_props: serde_json::Map<String, serde_json::Value> = json!({
426                    "frame_change_handling": frame_change_handling,
427                    "percentage_neuron_positioning": percentage_neuron_positioning
428                }).as_object().unwrap().clone();
429
430                let decoder: Box<dyn NeuronVoxelXYZPDecoder + Sync + Send> = ImageFilteringSettingsNeuronVoxelXYZPDecoder::new_box(
431                    brightness_cortical_id,
432                    contrast_cortical_id,
433                    diff_cortical_id,
434                    z_neuron_resolution,
435                    z_neuron_resolution,
436                    z_neuron_resolution,
437                    number_channels,
438                    percentage_neuron_positioning)?;
439
440
441
442                let initial_val: WrappedIOData = WrappedIOData::ImageFilteringSettings(ImageFilteringSettings::default());
443
444                self.register(MotorCorticalUnit::$motor_unit, unit, decoder, io_props, number_channels, initial_val)?;
445                Ok(())
446            }
447        }
448
449        motor_unit_functions!(@generate_similar_functions $motor_unit, ImageFilteringSettings);
450    };
451}
452
453pub struct MotorDeviceCache {
454    motor_cortical_unit_caches:
455        HashMap<(MotorCorticalUnit, CorticalUnitIndex), MotorCorticalUnitCache>,
456    neuron_data: CorticalMappedXYZPNeuronVoxels,
457    byte_data: FeagiByteContainer,
458    previous_burst: Instant,
459    #[allow(dead_code)]
460    is_active: bool,
461}
462
463impl std::fmt::Debug for MotorDeviceCache {
464    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
465        f.debug_struct("MotorDeviceCache")
466            .field(
467                "stream_caches_count",
468                &self.motor_cortical_unit_caches.len(),
469            )
470            .field("neuron_data", &self.neuron_data)
471            .field("byte_data", &self.byte_data)
472            .field("previous_burst", &self.previous_burst)
473            .finish()
474    }
475}
476
477impl MotorDeviceCache {
478    pub fn new() -> Self {
479        MotorDeviceCache {
480            motor_cortical_unit_caches: HashMap::new(),
481            neuron_data: CorticalMappedXYZPNeuronVoxels::new(),
482            byte_data: FeagiByteContainer::new_empty(),
483            previous_burst: Instant::now(),
484            is_active: false,
485        }
486    }
487
488    /// Ingest already-decoded motor neuron data and run callbacks.
489    ///
490    /// This is a zero-copy convenience for callers that already have a decoded
491    /// `CorticalMappedXYZPNeuronVoxels` (e.g. from `feagi-agent`'s motor receive path),
492    /// avoiding a re-serialization into `FeagiByteContainer`.
493    ///
494    /// # Arguments
495    /// - `neuron_data`: decoded motor neuron voxels as published by FEAGI
496    /// - `time_of_decode`: timestamp used for callback timing logic
497    pub fn ingest_neuron_data_and_run_callbacks(
498        &mut self,
499        neuron_data: CorticalMappedXYZPNeuronVoxels,
500        time_of_decode: Instant,
501    ) -> Result<(), FeagiDataError> {
502        self.neuron_data = neuron_data;
503        self.try_decode_neural_data_into_cache(time_of_decode)
504    }
505
506    // Clears all registered devices and cache, to allow setting up again
507    pub fn reset(&mut self) {
508        self.motor_cortical_unit_caches.clear();
509        self.neuron_data = CorticalMappedXYZPNeuronVoxels::new();
510        self.byte_data = FeagiByteContainer::new_empty();
511        self.previous_burst = Instant::now();
512    }
513
514    pub fn verify_existence(
515        &self,
516        motor_cortical_unit: MotorCorticalUnit,
517        unit_index: CorticalUnitIndex,
518        cortical_channel_index: CorticalChannelIndex,
519    ) -> Result<(), FeagiDataError> {
520        let motor_stream_caches =
521            self.try_get_motor_channel_stream_caches(motor_cortical_unit, unit_index)?;
522        motor_stream_caches.verify_channel_exists(cortical_channel_index)
523    }
524
525    motor_cortical_units!(motor_unit_functions);
526
527    //region Data IO
528
529    pub fn get_feagi_byte_container(&self) -> &FeagiByteContainer {
530        &self.byte_data
531    }
532
533    pub fn get_feagi_byte_container_mut(&mut self) -> &mut FeagiByteContainer {
534        &mut self.byte_data
535    }
536
537    pub fn get_neurons(&self) -> &CorticalMappedXYZPNeuronVoxels {
538        &self.neuron_data
539    }
540
541    // Returns true if data was retrieved
542    pub fn try_decode_bytes_to_neural_data(&mut self) -> Result<bool, FeagiDataError> {
543        self.byte_data
544            .try_update_struct_from_first_found_struct_of_type(&mut self.neuron_data)
545    }
546
547    pub fn try_decode_neural_data_into_cache(
548        &mut self,
549        time_of_decode: Instant,
550    ) -> Result<(), FeagiDataError> {
551        for motor_channel_stream_cache in self.motor_cortical_unit_caches.values_mut() {
552            motor_channel_stream_cache.try_read_neuron_data_to_cache_and_do_callbacks(
553                &self.neuron_data,
554                time_of_decode,
555            )?;
556        }
557        Ok(())
558    }
559
560    //endregion
561
562    //region  JSON import / export
563
564    pub fn import_from_output_definition(
565        &mut self,
566        replacing_definition: &JSONInputOutputDefinition,
567    ) -> Result<(), FeagiDataError> {
568        self.reset();
569        let output_units_and_decoder_properties =
570            replacing_definition.get_output_units_and_decoder_properties();
571        for (motor_unit, unit_and_decoder_definitions) in output_units_and_decoder_properties {
572            for unit_and_decoder_definition in unit_and_decoder_definitions {
573                let unit_definition = &unit_and_decoder_definition.0;
574                let encoder_definition = &unit_and_decoder_definition.1;
575
576                if self
577                    .motor_cortical_unit_caches
578                    .contains_key(&(*motor_unit, unit_definition.cortical_unit_index))
579                {
580                    return Err(FeagiDataError::DeserializationError(format!(
581                        "Already registered motor {} of unit index {}!",
582                        *motor_unit, unit_definition.cortical_unit_index
583                    )));
584                }
585
586                let new_unit = MotorCorticalUnitCache::new_from_json(
587                    motor_unit,
588                    unit_definition,
589                    encoder_definition,
590                )?;
591                self.motor_cortical_unit_caches
592                    .insert((*motor_unit, unit_definition.cortical_unit_index), new_unit);
593            }
594        }
595        Ok(())
596    }
597
598    pub fn export_to_output_definition(
599        &self,
600        filling_definition: &mut JSONInputOutputDefinition,
601    ) -> Result<(), FeagiDataError> {
602        for ((motor_cortical_unit, cortical_unit_index), motor_channel_stream_caches) in
603            self.motor_cortical_unit_caches.iter()
604        {
605            let unit_and_encoder =
606                motor_channel_stream_caches.export_as_jsons(*cortical_unit_index);
607            filling_definition.insert_motor(
608                *motor_cortical_unit,
609                unit_and_encoder.0,
610                unit_and_encoder.1,
611            );
612        }
613        Ok(())
614    }
615
616    //endregion
617
618    //region Internal
619
620    //region Cache Abstractions
621
622    fn register(
623        &mut self,
624        motor_type: MotorCorticalUnit,
625        unit_index: CorticalUnitIndex,
626        neuron_decoder: Box<dyn NeuronVoxelXYZPDecoder>,
627        io_configuration_flags: serde_json::Map<String, serde_json::Value>,
628        number_channels: CorticalChannelCount,
629        initial_cached_value: WrappedIOData,
630    ) -> Result<(), FeagiDataError> {
631        // NOTE: The length of pipeline_stages_across_channels denotes the number of channels!
632
633        if self
634            .motor_cortical_unit_caches
635            .contains_key(&(motor_type, unit_index))
636        {
637            return Err(FeagiDataError::BadParameters(format!(
638                "Already registered motor {} of unit index {}!",
639                motor_type, unit_index
640            )));
641        }
642
643        self.motor_cortical_unit_caches.insert(
644            (motor_type, unit_index),
645            MotorCorticalUnitCache::new(
646                neuron_decoder,
647                io_configuration_flags,
648                number_channels,
649                initial_cached_value,
650            )?,
651        );
652
653        Ok(())
654    }
655
656    //region Data
657
658    fn try_read_preprocessed_cached_value(
659        &self,
660        motor_type: MotorCorticalUnit,
661        unit_index: CorticalUnitIndex,
662        channel_index: CorticalChannelIndex,
663    ) -> Result<&WrappedIOData, FeagiDataError> {
664        let motor_stream_caches =
665            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
666        motor_stream_caches.get_preprocessed_motor_value(channel_index)
667    }
668
669    fn try_read_postprocessed_cached_value(
670        &self,
671        motor_type: MotorCorticalUnit,
672        unit_index: CorticalUnitIndex,
673        channel_index: CorticalChannelIndex,
674    ) -> Result<&WrappedIOData, FeagiDataError> {
675        let motor_stream_caches =
676            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
677        motor_stream_caches.get_postprocessed_motor_value(channel_index)
678    }
679
680    fn try_register_motor_callback<F>(
681        &mut self,
682        motor_type: MotorCorticalUnit,
683        unit_index: CorticalUnitIndex,
684        channel_index: CorticalChannelIndex,
685        callback: F,
686    ) -> Result<FeagiSignalIndex, FeagiDataError>
687    where
688        F: Fn(&WrappedIOData) + Send + Sync + 'static,
689    {
690        let motor_stream_caches =
691            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
692        let index =
693            motor_stream_caches.try_connect_to_data_processed_signal(channel_index, callback)?;
694        Ok(index)
695    }
696
697    //endregion
698
699    //region Stages
700
701    fn try_get_single_stage_properties(
702        &self,
703        motor_type: MotorCorticalUnit,
704        unit_index: CorticalUnitIndex,
705        channel_index: CorticalChannelIndex,
706        stage_index: PipelineStagePropertyIndex,
707    ) -> Result<PipelineStageProperties, FeagiDataError> {
708        let motor_stream_caches =
709            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
710        motor_stream_caches.try_get_single_stage_properties(channel_index, stage_index)
711    }
712
713    fn try_get_all_stage_properties(
714        &self,
715        motor_type: MotorCorticalUnit,
716        unit_index: CorticalUnitIndex,
717        channel_index: CorticalChannelIndex,
718    ) -> Result<Vec<PipelineStageProperties>, FeagiDataError> {
719        let motor_stream_caches =
720            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
721        motor_stream_caches.get_all_stage_properties(channel_index)
722    }
723
724    fn try_update_single_stage_properties(
725        &mut self,
726        motor_type: MotorCorticalUnit,
727        unit_index: CorticalUnitIndex,
728        channel_index: CorticalChannelIndex,
729        pipeline_stage_property_index: PipelineStagePropertyIndex,
730        replacing_property: PipelineStageProperties,
731    ) -> Result<(), FeagiDataError> {
732        let motor_stream_caches =
733            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
734        motor_stream_caches.try_update_single_stage_properties(
735            channel_index,
736            pipeline_stage_property_index,
737            replacing_property,
738        )
739    }
740
741    fn try_update_all_stage_properties(
742        &mut self,
743        motor_type: MotorCorticalUnit,
744        unit_index: CorticalUnitIndex,
745        channel_index: CorticalChannelIndex,
746        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
747    ) -> Result<(), FeagiDataError> {
748        let motor_stream_caches =
749            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
750        motor_stream_caches
751            .try_update_all_stage_properties(channel_index, new_pipeline_stage_properties)
752    }
753
754    fn try_replace_single_stage(
755        &mut self,
756        motor_type: MotorCorticalUnit,
757        unit_index: CorticalUnitIndex,
758        channel_index: CorticalChannelIndex,
759        replacing_at_index: PipelineStagePropertyIndex,
760        new_pipeline_stage_properties: PipelineStageProperties,
761    ) -> Result<(), FeagiDataError> {
762        let motor_stream_caches =
763            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
764        motor_stream_caches.try_replace_single_stage(
765            channel_index,
766            replacing_at_index,
767            new_pipeline_stage_properties,
768        )
769    }
770
771    fn try_replace_all_stages(
772        &mut self,
773        motor_type: MotorCorticalUnit,
774        unit_index: CorticalUnitIndex,
775        channel_index: CorticalChannelIndex,
776        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
777    ) -> Result<(), FeagiDataError> {
778        let motor_stream_caches =
779            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
780        motor_stream_caches.try_replace_all_stages(channel_index, new_pipeline_stage_properties)
781    }
782
783    fn try_removing_all_stages(
784        &mut self,
785        sensor_type: MotorCorticalUnit,
786        unit_index: CorticalUnitIndex,
787        channel_index: CorticalChannelIndex,
788    ) -> Result<(), FeagiDataError> {
789        let motor_stream_cache =
790            self.try_get_motor_channel_stream_caches_mut(sensor_type, unit_index)?;
791        motor_stream_cache.try_removing_all_stages(channel_index)?;
792        Ok(())
793    }
794
795    //endregion
796
797    //endregion
798
799    //region Hashmap Interactions
800
801    fn try_get_motor_channel_stream_caches(
802        &self,
803        motor_type: MotorCorticalUnit,
804        unit_index: CorticalUnitIndex,
805    ) -> Result<&MotorCorticalUnitCache, FeagiDataError> {
806        let check = self
807            .motor_cortical_unit_caches
808            .get(&(motor_type, unit_index));
809        if check.is_none() {
810            return Err(FeagiDataError::BadParameters(format!(
811                "Unable to find {} of cortical unit index {} in registered motor's list!",
812                motor_type, unit_index
813            )));
814        }
815        let check = check.unwrap();
816        Ok(check)
817    }
818
819    fn try_get_motor_channel_stream_caches_mut(
820        &mut self,
821        motor_type: MotorCorticalUnit,
822        unit_index: CorticalUnitIndex,
823    ) -> Result<&mut MotorCorticalUnitCache, FeagiDataError> {
824        let check = self
825            .motor_cortical_unit_caches
826            .get_mut(&(motor_type, unit_index));
827        if check.is_none() {
828            return Err(FeagiDataError::BadParameters(format!(
829                "Unable to find {} of cortical unit index {} in registered motor's list!",
830                motor_type, unit_index
831            )));
832        }
833        let check = check.unwrap();
834        Ok(check)
835    }
836
837    //endregion
838
839    //endregion
840}
841
842impl Default for MotorDeviceCache {
843    fn default() -> Self {
844        Self::new()
845    }
846}
847
848impl Display for MotorDeviceCache {
849    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
850        Ok(writeln!(f, "Motor Device Cache:")?)
851    }
852}