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 Feedbacks
563
564    //endregion
565
566    //region  JSON import / export
567
568    pub fn import_from_output_definition(
569        &mut self,
570        replacing_definition: &JSONInputOutputDefinition,
571    ) -> Result<(), FeagiDataError> {
572        self.reset();
573        let output_units_and_decoder_properties =
574            replacing_definition.get_output_units_and_decoder_properties();
575        for (motor_unit, unit_and_decoder_definitions) in output_units_and_decoder_properties {
576            for unit_and_decoder_definition in unit_and_decoder_definitions {
577                let unit_definition = &unit_and_decoder_definition.0;
578                let encoder_definition = &unit_and_decoder_definition.1;
579
580                if self
581                    .motor_cortical_unit_caches
582                    .contains_key(&(*motor_unit, unit_definition.cortical_unit_index))
583                {
584                    return Err(FeagiDataError::DeserializationError(format!(
585                        "Already registered motor {} of unit index {}!",
586                        *motor_unit, unit_definition.cortical_unit_index
587                    )));
588                }
589
590                let new_unit = MotorCorticalUnitCache::new_from_json(
591                    motor_unit,
592                    unit_definition,
593                    encoder_definition,
594                )?;
595                self.motor_cortical_unit_caches
596                    .insert((*motor_unit, unit_definition.cortical_unit_index), new_unit);
597            }
598        }
599        Ok(())
600    }
601
602    pub fn export_to_output_definition(
603        &self,
604        filling_definition: &mut JSONInputOutputDefinition,
605    ) -> Result<(), FeagiDataError> {
606        for ((motor_cortical_unit, cortical_unit_index), motor_channel_stream_caches) in
607            self.motor_cortical_unit_caches.iter()
608        {
609            let unit_and_encoder =
610                motor_channel_stream_caches.export_as_jsons(*cortical_unit_index);
611            filling_definition.insert_motor(
612                *motor_cortical_unit,
613                unit_and_encoder.0,
614                unit_and_encoder.1,
615            );
616        }
617        Ok(())
618    }
619
620    //endregion
621
622    //region Internal
623
624    //region Cache Abstractions
625
626    fn register(
627        &mut self,
628        motor_type: MotorCorticalUnit,
629        unit_index: CorticalUnitIndex,
630        neuron_decoder: Box<dyn NeuronVoxelXYZPDecoder>,
631        io_configuration_flags: serde_json::Map<String, serde_json::Value>,
632        number_channels: CorticalChannelCount,
633        initial_cached_value: WrappedIOData,
634    ) -> Result<(), FeagiDataError> {
635        // NOTE: The length of pipeline_stages_across_channels denotes the number of channels!
636
637        if self
638            .motor_cortical_unit_caches
639            .contains_key(&(motor_type, unit_index))
640        {
641            return Err(FeagiDataError::BadParameters(format!(
642                "Already registered motor {} of unit index {}!",
643                motor_type, unit_index
644            )));
645        }
646
647        self.motor_cortical_unit_caches.insert(
648            (motor_type, unit_index),
649            MotorCorticalUnitCache::new(
650                neuron_decoder,
651                io_configuration_flags,
652                number_channels,
653                initial_cached_value,
654            )?,
655        );
656
657        Ok(())
658    }
659
660    //region Data
661
662    fn try_read_preprocessed_cached_value(
663        &self,
664        motor_type: MotorCorticalUnit,
665        unit_index: CorticalUnitIndex,
666        channel_index: CorticalChannelIndex,
667    ) -> Result<&WrappedIOData, FeagiDataError> {
668        let motor_stream_caches =
669            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
670        motor_stream_caches.get_preprocessed_motor_value(channel_index)
671    }
672
673    fn try_read_postprocessed_cached_value(
674        &self,
675        motor_type: MotorCorticalUnit,
676        unit_index: CorticalUnitIndex,
677        channel_index: CorticalChannelIndex,
678    ) -> Result<&WrappedIOData, FeagiDataError> {
679        let motor_stream_caches =
680            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
681        motor_stream_caches.get_postprocessed_motor_value(channel_index)
682    }
683
684    fn try_register_motor_callback<F>(
685        &mut self,
686        motor_type: MotorCorticalUnit,
687        unit_index: CorticalUnitIndex,
688        channel_index: CorticalChannelIndex,
689        callback: F,
690    ) -> Result<FeagiSignalIndex, FeagiDataError>
691    where
692        F: Fn(&WrappedIOData) + Send + Sync + 'static,
693    {
694        let motor_stream_caches =
695            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
696        let index =
697            motor_stream_caches.try_connect_to_data_processed_signal(channel_index, callback)?;
698        Ok(index)
699    }
700
701    //endregion
702
703    //region Stages
704
705    fn try_get_single_stage_properties(
706        &self,
707        motor_type: MotorCorticalUnit,
708        unit_index: CorticalUnitIndex,
709        channel_index: CorticalChannelIndex,
710        stage_index: PipelineStagePropertyIndex,
711    ) -> Result<PipelineStageProperties, FeagiDataError> {
712        let motor_stream_caches =
713            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
714        motor_stream_caches.try_get_single_stage_properties(channel_index, stage_index)
715    }
716
717    fn try_get_all_stage_properties(
718        &self,
719        motor_type: MotorCorticalUnit,
720        unit_index: CorticalUnitIndex,
721        channel_index: CorticalChannelIndex,
722    ) -> Result<Vec<PipelineStageProperties>, FeagiDataError> {
723        let motor_stream_caches =
724            self.try_get_motor_channel_stream_caches(motor_type, unit_index)?;
725        motor_stream_caches.get_all_stage_properties(channel_index)
726    }
727
728    fn try_update_single_stage_properties(
729        &mut self,
730        motor_type: MotorCorticalUnit,
731        unit_index: CorticalUnitIndex,
732        channel_index: CorticalChannelIndex,
733        pipeline_stage_property_index: PipelineStagePropertyIndex,
734        replacing_property: PipelineStageProperties,
735    ) -> Result<(), FeagiDataError> {
736        let motor_stream_caches =
737            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
738        motor_stream_caches.try_update_single_stage_properties(
739            channel_index,
740            pipeline_stage_property_index,
741            replacing_property,
742        )
743    }
744
745    fn try_update_all_stage_properties(
746        &mut self,
747        motor_type: MotorCorticalUnit,
748        unit_index: CorticalUnitIndex,
749        channel_index: CorticalChannelIndex,
750        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
751    ) -> Result<(), FeagiDataError> {
752        let motor_stream_caches =
753            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
754        motor_stream_caches
755            .try_update_all_stage_properties(channel_index, new_pipeline_stage_properties)
756    }
757
758    fn try_replace_single_stage(
759        &mut self,
760        motor_type: MotorCorticalUnit,
761        unit_index: CorticalUnitIndex,
762        channel_index: CorticalChannelIndex,
763        replacing_at_index: PipelineStagePropertyIndex,
764        new_pipeline_stage_properties: PipelineStageProperties,
765    ) -> Result<(), FeagiDataError> {
766        let motor_stream_caches =
767            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
768        motor_stream_caches.try_replace_single_stage(
769            channel_index,
770            replacing_at_index,
771            new_pipeline_stage_properties,
772        )
773    }
774
775    fn try_replace_all_stages(
776        &mut self,
777        motor_type: MotorCorticalUnit,
778        unit_index: CorticalUnitIndex,
779        channel_index: CorticalChannelIndex,
780        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
781    ) -> Result<(), FeagiDataError> {
782        let motor_stream_caches =
783            self.try_get_motor_channel_stream_caches_mut(motor_type, unit_index)?;
784        motor_stream_caches.try_replace_all_stages(channel_index, new_pipeline_stage_properties)
785    }
786
787    fn try_removing_all_stages(
788        &mut self,
789        sensor_type: MotorCorticalUnit,
790        unit_index: CorticalUnitIndex,
791        channel_index: CorticalChannelIndex,
792    ) -> Result<(), FeagiDataError> {
793        let motor_stream_cache =
794            self.try_get_motor_channel_stream_caches_mut(sensor_type, unit_index)?;
795        motor_stream_cache.try_removing_all_stages(channel_index)?;
796        Ok(())
797    }
798
799    //endregion
800
801    //endregion
802
803    //region Hashmap Interactions
804
805    fn try_get_motor_channel_stream_caches(
806        &self,
807        motor_type: MotorCorticalUnit,
808        unit_index: CorticalUnitIndex,
809    ) -> Result<&MotorCorticalUnitCache, FeagiDataError> {
810        let check = self
811            .motor_cortical_unit_caches
812            .get(&(motor_type, unit_index));
813        if check.is_none() {
814            return Err(FeagiDataError::BadParameters(format!(
815                "Unable to find {} of cortical unit index {} in registered motor's list!",
816                motor_type, unit_index
817            )));
818        }
819        let check = check.unwrap();
820        Ok(check)
821    }
822
823    fn try_get_motor_channel_stream_caches_mut(
824        &mut self,
825        motor_type: MotorCorticalUnit,
826        unit_index: CorticalUnitIndex,
827    ) -> Result<&mut MotorCorticalUnitCache, FeagiDataError> {
828        let check = self
829            .motor_cortical_unit_caches
830            .get_mut(&(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    //endregion
842
843    //endregion
844}
845
846impl Default for MotorDeviceCache {
847    fn default() -> Self {
848        Self::new()
849    }
850}
851
852impl Display for MotorDeviceCache {
853    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
854        Ok(writeln!(f, "Motor Device Cache:")?)
855    }
856}