Skip to main content

feagi_sensorimotor/caching/
sensor_device_cache.rs

1use crate::configuration::jsonable::JSONInputOutputDefinition;
2use crate::data_pipeline::per_channel_stream_caches::SensoryCorticalUnitCache;
3use crate::data_pipeline::{PipelineStageProperties, PipelineStagePropertyIndex};
4use crate::data_types::descriptors::PercentageChannelDimensionality;
5use crate::data_types::descriptors::{
6    ImageFrameProperties, MiscDataDimensions, SegmentedImageFrameProperties,
7};
8use crate::data_types::{
9    GazeProperties, ImageFrame, MiscData, Percentage, Percentage3D, SegmentedImageFrame,
10    SignedPercentage4D,
11};
12use crate::neuron_voxel_coding::xyzp::encoders::*;
13use crate::neuron_voxel_coding::xyzp::NeuronVoxelXYZPEncoder;
14use crate::wrapped_io_data::{WrappedIOData, WrappedIOType};
15use feagi_serialization::FeagiByteContainer;
16use feagi_structures::genomic::cortical_area::descriptors::{
17    CorticalChannelCount, CorticalChannelIndex, CorticalUnitIndex, NeuronDepth,
18};
19use feagi_structures::genomic::cortical_area::io_cortical_area_configuration_flag::{
20    FrameChangeHandling, PercentageNeuronPositioning,
21};
22use feagi_structures::genomic::cortical_area::CorticalID;
23use feagi_structures::genomic::SensoryCorticalUnit;
24use feagi_structures::neuron_voxels::xyzp::CorticalMappedXYZPNeuronVoxels;
25use feagi_structures::{sensor_cortical_units, FeagiDataError, FeagiSignal};
26use serde_json::json;
27use std::collections::HashMap;
28use std::fmt;
29use std::time::Instant;
30// InputOutputDefinition is used in commented-out code (lines 622, 645)
31// Uncomment when implementing set_from_input_definition and export_to_input_definition
32// use crate::configuration::jsonable::InputOutputDefinition;
33
34#[allow(unused_macros)] // Macro may be used in future
35macro_rules! sensor_unit_functions {
36    (
37        SensoryCorticalUnit {
38            $(
39                $(#[doc = $doc:expr])?
40                $cortical_type_key_name:ident => {
41                    friendly_name: $friendly_name:expr,
42                    accepted_wrapped_io_data_type: $accepted_wrapped_io_data_type:ident,
43                    cortical_id_unit_reference: $cortical_id_unit_reference:expr,
44                    number_cortical_areas: $number_cortical_areas:expr,
45                    cortical_type_parameters: {
46                        $($param_name:ident: $param_type:ty),* $(,)?
47                    },
48                    $(allowed_frame_change_handling: [$($allowed_frame:ident),* $(,)?],)?
49                    cortical_area_properties: {
50                        $($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])),* $(,)?
51                    }
52                }
53            ),* $(,)?
54        }
55    ) =>
56    {
57        $(
58            sensor_unit_functions!(@generate_functions
59            $cortical_type_key_name,
60            $accepted_wrapped_io_data_type
61            );
62        )*
63    };
64
65    //region Similar Functions
66    // Helper macro to generate stage and other similar functions
67    (@generate_similar_functions
68        $cortical_type_key_name:ident,
69        $wrapped_data_type:ident
70    ) => {
71        ::paste::paste! {
72
73            pub fn [<$cortical_type_key_name:snake _write>](
74                &mut self,
75                unit: CorticalUnitIndex,
76                channel: CorticalChannelIndex,
77                data: WrappedIOData,
78            ) -> Result<(), FeagiDataError>
79            {
80                const SENSOR_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
81                let instant = Instant::now();
82
83                self.try_update_value(SENSOR_TYPE, unit, channel, data, instant)?;
84                Ok(())
85            }
86
87            pub fn [<$cortical_type_key_name:snake _read_postprocessed_cache_value>](
88                &mut self,
89                unit: CorticalUnitIndex,
90                channel: CorticalChannelIndex,
91            ) -> Result< $wrapped_data_type, FeagiDataError> {
92
93                const SENSOR_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
94                let wrapped = self.try_read_postprocessed_cached_value(SENSOR_TYPE, unit, channel)?;
95                let val: $wrapped_data_type = wrapped.try_into()?;
96                Ok(val)
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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
107                let stage = self.try_get_single_stage_properties(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
118                let stages = self.try_get_all_stage_properties(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
131                self.try_update_single_stage_properties(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
143                self.try_update_all_stage_properties(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
156                self.try_replace_single_stage(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
168                self.try_replace_all_stages(SENSOR_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 SENSOR_UNIT_TYPE: SensoryCorticalUnit = SensoryCorticalUnit::$cortical_type_key_name;
179                self.try_removing_all_stages(SENSOR_UNIT_TYPE, unit, channel_index)?;
180                Ok(())
181            }
182        }
183    };
184    //endregion
185
186
187    // Arm for WrappedIOType::Boolean
188    (@generate_functions
189        $sensory_unit:ident,
190        Boolean
191    ) => {
192        ::paste::paste! {
193            pub fn [<$sensory_unit:snake _register>](
194                &mut self,
195                unit: CorticalUnitIndex,
196                number_channels: CorticalChannelCount,
197                ) -> Result<(), FeagiDataError>
198            {
199                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](unit)[0];
200                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = BooleanNeuronVoxelXYZPEncoder::new_box(cortical_id, number_channels)?;
201
202                let io_props: serde_json::Map<String, serde_json::Value> = json!({}).as_object().unwrap().clone();
203
204                let initial_val: WrappedIOData = false.into();
205                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
206                Ok(())
207            }
208        }
209
210        sensor_unit_functions!(@generate_similar_functions $sensory_unit, bool);
211    };
212
213    // Arm for WrappedIOType::Percentage
214    (@generate_functions
215        $sensory_unit:ident,
216        Percentage
217    ) => {
218        ::paste::paste! {
219            pub fn [<$sensory_unit:snake _register>](
220                &mut self,
221                unit: CorticalUnitIndex,
222                number_channels: CorticalChannelCount,
223                frame_change_handling: FrameChangeHandling,
224                z_neuron_resolution: NeuronDepth,
225                percentage_neuron_positioning: PercentageNeuronPositioning
226                ) -> Result<(), FeagiDataError>
227            {
228                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
229                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = PercentageNeuronVoxelXYZPEncoder::new_box(
230                    cortical_id,
231                    z_neuron_resolution,
232                    number_channels,
233                    percentage_neuron_positioning,
234                    false,
235                    PercentageChannelDimensionality::D1
236                )?;
237
238                let io_props: serde_json::Map<String, serde_json::Value> = json!({
239                    "frame_change_handling": frame_change_handling,
240                    "percentage_neuron_positioning": percentage_neuron_positioning
241                }).as_object().unwrap().clone();
242
243                let initial_val: WrappedIOData = WrappedIOData::Percentage(Percentage::new_zero());
244                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
245                Ok(())
246            }
247        }
248
249        sensor_unit_functions!(@generate_similar_functions $sensory_unit, Percentage);
250    };
251
252    // Arm for WrappedIOType::Percentage_3D
253    (@generate_functions
254        $sensory_unit:ident,
255        Percentage_3D
256    ) => {
257        ::paste::paste! {
258            pub fn [<$sensory_unit:snake _register>](
259                &mut self,
260                unit: CorticalUnitIndex,
261                number_channels: CorticalChannelCount,
262                frame_change_handling: FrameChangeHandling,
263                z_neuron_resolution: NeuronDepth,
264                percentage_neuron_positioning: PercentageNeuronPositioning
265                ) -> Result<(), FeagiDataError>
266            {
267                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
268                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = PercentageNeuronVoxelXYZPEncoder::new_box(
269                    cortical_id,
270                    z_neuron_resolution,
271                    number_channels,
272                    percentage_neuron_positioning,
273                    false,
274                    PercentageChannelDimensionality::D3
275                )?;
276
277                let io_props: serde_json::Map<String, serde_json::Value> = json!({
278                    "frame_change_handling": frame_change_handling,
279                    "percentage_neuron_positioning": percentage_neuron_positioning
280                }).as_object().unwrap().clone();
281
282                let initial_val: WrappedIOData = WrappedIOData::Percentage(Percentage::new_zero());
283                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
284                Ok(())
285            }
286        }
287
288        sensor_unit_functions!(@generate_similar_functions $sensory_unit, Percentage3D);
289    };
290
291    // Arm for WrappedIOType::SignedPercentage_4D
292    (@generate_functions
293        $sensory_unit:ident,
294        SignedPercentage_4D
295    ) => {
296        ::paste::paste! {
297            pub fn [<$sensory_unit:snake _register>](
298                &mut self,
299                unit: CorticalUnitIndex,
300                number_channels: CorticalChannelCount,
301                frame_change_handling: FrameChangeHandling,
302                z_neuron_resolution: NeuronDepth,
303                percentage_neuron_positioning: PercentageNeuronPositioning
304                ) -> Result<(), FeagiDataError>
305            {
306                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, percentage_neuron_positioning, unit)[0];
307                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = PercentageNeuronVoxelXYZPEncoder::new_box(
308                    cortical_id,
309                    z_neuron_resolution,
310                    number_channels,
311                    percentage_neuron_positioning,
312                    true,
313                    PercentageChannelDimensionality::D4
314                )?;
315
316                let io_props: serde_json::Map<String, serde_json::Value> = json!({
317                    "frame_change_handling": frame_change_handling,
318                    "percentage_neuron_positioning": percentage_neuron_positioning
319                }).as_object().unwrap().clone();
320
321                let initial_val: WrappedIOData = WrappedIOData::Percentage(Percentage::new_zero());
322                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
323                Ok(())
324            }
325        }
326
327        sensor_unit_functions!(@generate_similar_functions $sensory_unit, SignedPercentage4D);
328    };
329
330    // Arm for WrappedIOType::SegmentedImageFrame
331    (@generate_functions
332        $sensory_unit:ident,
333        SegmentedImageFrame
334    ) => {
335        ::paste::paste! {
336            pub fn [<$sensory_unit:snake _register>](
337                &mut self,
338                unit: CorticalUnitIndex,
339                number_channels: CorticalChannelCount,
340                frame_change_handling: FrameChangeHandling,
341                input_image_properties: ImageFrameProperties,
342                segmented_image_properties: SegmentedImageFrameProperties,
343                initial_gaze: GazeProperties
344                ) -> Result<(), FeagiDataError>
345            {
346                // Bit more unique, we define a custom stage for all channels for segmentation by default
347                let cortical_ids: [CorticalID; 9] = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, unit);
348                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = SegmentedImageFrameNeuronVoxelXYZPEncoder::new_box(cortical_ids, segmented_image_properties, number_channels)?;
349
350                let io_props: serde_json::Map<String, serde_json::Value> = json!({
351                    "frame_change_handling": frame_change_handling
352                }).as_object().unwrap().clone();
353
354                let initial_val: WrappedIOData = WrappedIOType::SegmentedImageFrame(Some(segmented_image_properties)).create_blank_data_of_type()?;
355                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
356
357                let stage_properties = PipelineStageProperties::new_image_frame_segmentator(input_image_properties.clone(), segmented_image_properties.clone(), initial_gaze.clone());
358
359                for channel_index in 0..*number_channels {
360                    let segmentator_pipeline = vec![stage_properties.clone()];
361                    self.[<$sensory_unit:snake _replace_all_stages>](unit, channel_index.into(), segmentator_pipeline);
362                }
363                Ok(())
364            }
365        }
366
367        sensor_unit_functions!(@generate_similar_functions $sensory_unit, SegmentedImageFrame);
368    };
369
370    // Arm for WrappedIOType::MiscData
371    (@generate_functions
372        $sensory_unit:ident,
373        MiscData
374    ) => {
375        ::paste::paste! {
376            pub fn [<$sensory_unit:snake _register>](
377                &mut self,
378                unit: CorticalUnitIndex,
379                number_channels: CorticalChannelCount,
380                frame_change_handling: FrameChangeHandling,
381                misc_data_dimensions: MiscDataDimensions
382                ) -> Result<(), FeagiDataError>
383            {
384                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, unit)[0];
385                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = MiscDataNeuronVoxelXYZPEncoder::new_box(cortical_id, misc_data_dimensions, number_channels)?;
386
387                let io_props: serde_json::Map<String, serde_json::Value> = json!({
388                    "frame_change_handling": frame_change_handling
389                }).as_object().unwrap().clone();
390
391                let initial_val: WrappedIOData = WrappedIOType::MiscData(Some(misc_data_dimensions)).create_blank_data_of_type()?;
392                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
393                Ok(())
394            }
395        }
396
397        sensor_unit_functions!(@generate_similar_functions $sensory_unit, MiscData);
398    };
399
400
401    // Arm for WrappedIOType::ImageFrame
402    (@generate_functions
403        $sensory_unit:ident,
404        ImageFrame
405    ) => {
406        ::paste::paste! {
407            pub fn [<$sensory_unit:snake _register>](
408                &mut self,
409                unit: CorticalUnitIndex,
410                number_channels: CorticalChannelCount,
411                frame_change_handling: FrameChangeHandling,
412                image_properties: ImageFrameProperties,
413                ) -> Result<(), FeagiDataError>
414            {
415                let cortical_id: CorticalID = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $sensory_unit:snake _with_parameters>](frame_change_handling, unit)[0];
416                let encoder: Box<dyn NeuronVoxelXYZPEncoder + Sync + Send> = CartesianPlaneNeuronVoxelXYZPEncoder::new_box(cortical_id, &image_properties, number_channels)?;
417
418                let io_props: serde_json::Map<String, serde_json::Value> = json!({
419                    "frame_change_handling": frame_change_handling
420                }).as_object().unwrap().clone();
421
422                let initial_val: WrappedIOData = WrappedIOType::ImageFrame(Some(image_properties)).create_blank_data_of_type()?;
423                self.register(SensoryCorticalUnit::$sensory_unit, unit, encoder, io_props, number_channels, initial_val)?;
424                Ok(())
425            }
426        }
427
428        sensor_unit_functions!(@generate_similar_functions $sensory_unit, ImageFrame);
429    };
430}
431
432pub struct SensorDeviceCache {
433    sensor_cortical_unit_caches:
434        HashMap<(SensoryCorticalUnit, CorticalUnitIndex), SensoryCorticalUnitCache>,
435    neuron_data: CorticalMappedXYZPNeuronVoxels,
436    byte_data: FeagiByteContainer,
437    previous_burst: Instant,
438    neurons_encoded_signal: FeagiSignal<CorticalMappedXYZPNeuronVoxels>,
439    bytes_encoded_signal: FeagiSignal<FeagiByteContainer>,
440}
441
442impl fmt::Debug for SensorDeviceCache {
443    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444        f.debug_struct("SensorDeviceCache")
445            .field(
446                "stream_caches_count",
447                &self.sensor_cortical_unit_caches.len(),
448            )
449            .field("neuron_data", &self.neuron_data)
450            .field("byte_data", &self.byte_data)
451            .field("previous_burst", &self.previous_burst)
452            .finish()
453    }
454}
455
456impl Default for SensorDeviceCache {
457    fn default() -> Self {
458        Self::new()
459    }
460}
461
462#[allow(unused_must_use)]
463impl SensorDeviceCache {
464    pub fn new() -> Self {
465        SensorDeviceCache {
466            sensor_cortical_unit_caches: HashMap::new(),
467            neuron_data: CorticalMappedXYZPNeuronVoxels::new(),
468            byte_data: FeagiByteContainer::new_empty(),
469            previous_burst: Instant::now(),
470            neurons_encoded_signal: FeagiSignal::new(),
471            bytes_encoded_signal: FeagiSignal::new(),
472        }
473    }
474
475    // Clears all registered devices and cache, to allow setting up again
476    pub fn reset(&mut self) {
477        self.sensor_cortical_unit_caches.clear();
478        self.neuron_data = CorticalMappedXYZPNeuronVoxels::new();
479        self.byte_data = FeagiByteContainer::new_empty();
480        self.previous_burst = Instant::now();
481        self.neurons_encoded_signal = FeagiSignal::new();
482        self.bytes_encoded_signal = FeagiSignal::new();
483    }
484    sensor_cortical_units!(sensor_unit_functions);
485
486    pub fn verify_existence(
487        &self,
488        sensory_cortical_unit: SensoryCorticalUnit,
489        unit_index: CorticalUnitIndex,
490        cortical_channel_index: CorticalChannelIndex,
491    ) -> Result<(), FeagiDataError> {
492        let sensor_stream_caches =
493            self.try_get_sensory_channel_stream_caches(sensory_cortical_unit, unit_index)?;
494        sensor_stream_caches.verify_channel_exists(cortical_channel_index)
495    }
496
497    pub fn try_get_index_of_first_stage_property_type_of(
498        &self,
499        sensory_cortical_unit: SensoryCorticalUnit,
500        unit_index: CorticalUnitIndex,
501        cortical_channel_index: CorticalChannelIndex,
502        property_example: &PipelineStageProperties,
503    ) -> Result<PipelineStagePropertyIndex, FeagiDataError> {
504        let sensor_stream_caches =
505            self.try_get_sensory_channel_stream_caches(sensory_cortical_unit, unit_index)?;
506        sensor_stream_caches
507            .try_get_first_index_of_stage_property_type(cortical_channel_index, property_example)
508    }
509
510    //region Data IO
511
512    pub fn get_feagi_byte_container(&self) -> &FeagiByteContainer {
513        &self.byte_data
514    }
515
516    pub fn get_feagi_byte_container_mut(&mut self) -> &mut FeagiByteContainer {
517        &mut self.byte_data
518    }
519
520    pub fn get_neurons(&self) -> &CorticalMappedXYZPNeuronVoxels {
521        &self.neuron_data
522    }
523
524    /// Encode all cached sensor data to neuron voxel format
525    ///
526    /// Iterates over all registered sensor stream caches and encodes their
527    /// processed data into neuron voxel representations. This populates
528    /// the internal neuron_data field.
529    ///
530    /// # Arguments
531    /// * `time_of_burst` - Timestamp for this encoding burst
532    ///
533    /// # Returns
534    /// * `Ok(())` - If encoding succeeded
535    /// * `Err(FeagiDataError)` - If encoding fails
536    pub fn encode_all_sensors_to_neurons(
537        &mut self,
538        time_of_burst: Instant,
539    ) -> Result<(), FeagiDataError> {
540        // Clear neuron data before encoding
541        self.neuron_data.clear_neurons_only();
542
543        let previous_burst = self.previous_burst;
544
545        // TODO see if we can parallelize this to work on multiple cortical areas at once
546        // Iterate over all registered sensor stream caches and encode them
547        // CRITICAL: Pass previous_burst (not time_of_burst) so encoder can check if channels were updated since last encoding
548        for ((_sensor_type, _unit_index), stream_cache) in
549            self.sensor_cortical_unit_caches.iter_mut()
550        {
551            stream_cache.update_neuron_data_with_recently_updated_cached_sensor_data(
552                &mut self.neuron_data,
553                previous_burst,
554            )?;
555        }
556
557        // Update previous_burst for next time
558        self.previous_burst = time_of_burst;
559
560        Ok(())
561    }
562
563    /// Encode neuron voxel data to byte container format
564    ///
565    /// Serializes the internal neuron_data into FeagiByteContainer format.
566    /// This populates the internal byte_data field.
567    ///
568    /// # Returns
569    /// * `Ok(())` - If encoding succeeded
570    /// * `Err(FeagiDataError)` - If encoding fails
571    pub fn encode_neurons_to_bytes(&mut self) -> Result<(), FeagiDataError> {
572        self.byte_data
573            .overwrite_byte_data_with_single_struct_data(&self.neuron_data, 0)
574            .map_err(|e| {
575                FeagiDataError::BadParameters(format!(
576                    "Failed to encode neuron data to bytes: {:?}",
577                    e
578                ))
579            })?;
580        Ok(())
581    }
582
583    //endregion
584
585    //region  JSON import / export
586
587    pub fn import_from_input_definition(
588        &mut self,
589        replacing_definition: &JSONInputOutputDefinition,
590    ) -> Result<(), FeagiDataError> {
591        self.reset();
592        let input_units_and_encoder_properties =
593            replacing_definition.get_input_units_and_encoder_properties();
594        for (sensory_unit, unit_and_encoder_definitions) in input_units_and_encoder_properties {
595            for unit_and_encoder_definition in unit_and_encoder_definitions {
596                let unit_definition = &unit_and_encoder_definition.0;
597                let encoder_definition = &unit_and_encoder_definition.1;
598
599                if self
600                    .sensor_cortical_unit_caches
601                    .contains_key(&(*sensory_unit, unit_definition.cortical_unit_index))
602                {
603                    return Err(FeagiDataError::DeserializationError(format!(
604                        "Already registered sensor {} of unit index {}!",
605                        *sensory_unit, unit_definition.cortical_unit_index
606                    )));
607                }
608
609                let new_unit = SensoryCorticalUnitCache::new_from_json(
610                    sensory_unit,
611                    unit_definition,
612                    encoder_definition,
613                )?;
614                self.sensor_cortical_unit_caches.insert(
615                    (*sensory_unit, unit_definition.cortical_unit_index),
616                    new_unit,
617                );
618            }
619        }
620        Ok(())
621    }
622
623    pub fn export_to_input_definition(
624        &self,
625        filling_definition: &mut JSONInputOutputDefinition,
626    ) -> Result<(), FeagiDataError> {
627        for ((sensory_cortical_unit, cortical_unit_index), sensory_channel_stream_caches) in
628            self.sensor_cortical_unit_caches.iter()
629        {
630            let unit_and_encoder =
631                sensory_channel_stream_caches.export_as_jsons(*cortical_unit_index);
632            filling_definition.insert_sensor(
633                *sensory_cortical_unit,
634                unit_and_encoder.0,
635                unit_and_encoder.1,
636            );
637        }
638        Ok(())
639    }
640
641    //endregion
642
643    //region Internal
644
645    //region Cache Abstractions
646
647    fn register(
648        &mut self,
649        sensor_type: SensoryCorticalUnit,
650        unit_index: CorticalUnitIndex,
651        neuron_encoder: Box<dyn NeuronVoxelXYZPEncoder>,
652        io_configuration_flags: serde_json::Map<String, serde_json::Value>,
653        number_channels: CorticalChannelCount,
654        initial_cached_value: WrappedIOData,
655    ) -> Result<(), FeagiDataError> {
656        if self
657            .sensor_cortical_unit_caches
658            .contains_key(&(sensor_type, unit_index))
659        {
660            return Err(FeagiDataError::BadParameters(format!(
661                "Already registered sensor {} of unit index {}!",
662                sensor_type, unit_index
663            )));
664        }
665
666        self.sensor_cortical_unit_caches.insert(
667            (sensor_type, unit_index),
668            SensoryCorticalUnitCache::new(
669                neuron_encoder,
670                io_configuration_flags,
671                number_channels,
672                initial_cached_value,
673            )?,
674        );
675
676        Ok(())
677    }
678
679    //region Data
680
681    fn try_update_value(
682        &mut self,
683        sensor_type: SensoryCorticalUnit,
684        unit_index: CorticalUnitIndex,
685        channel_index: CorticalChannelIndex,
686        value: WrappedIOData,
687        time_of_update: Instant,
688    ) -> Result<(), FeagiDataError> {
689        let sensor_stream_caches =
690            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
691        sensor_stream_caches.try_replace_input_channel_cache_value_and_run_pipeline(
692            channel_index,
693            value,
694            time_of_update,
695        )?; // Handles checking channel, value type
696        Ok(())
697    }
698
699    #[allow(dead_code)]
700    fn try_read_preprocessed_cached_value(
701        &self,
702        sensor_type: SensoryCorticalUnit,
703        unit_index: CorticalUnitIndex,
704        channel_index: CorticalChannelIndex,
705    ) -> Result<&WrappedIOData, FeagiDataError> {
706        let sensor_stream_caches =
707            self.try_get_sensory_channel_stream_caches(sensor_type, unit_index)?;
708        let value = sensor_stream_caches.try_get_channel_preprocessed_value(channel_index)?;
709        Ok(value)
710    }
711
712    fn try_read_postprocessed_cached_value(
713        &self,
714        sensor_type: SensoryCorticalUnit,
715        unit_index: CorticalUnitIndex,
716        channel_index: CorticalChannelIndex,
717    ) -> Result<&WrappedIOData, FeagiDataError> {
718        let sensor_stream_caches =
719            self.try_get_sensory_channel_stream_caches(sensor_type, unit_index)?;
720        let value =
721            sensor_stream_caches.try_get_channel_recent_postprocessed_value(channel_index)?;
722        Ok(value)
723    }
724
725    //endregion
726
727    //region Metadata
728
729    #[allow(dead_code)]
730    fn get_unit_friendly_name(
731        &self,
732        sensor_type: SensoryCorticalUnit,
733        unit_index: CorticalUnitIndex,
734    ) -> Result<&Option<String>, FeagiDataError> {
735        let sensor_stream_caches =
736            self.try_get_sensory_channel_stream_caches(sensor_type, unit_index)?;
737        Ok(sensor_stream_caches.get_friendly_name())
738    }
739
740    #[allow(dead_code)]
741    fn set_unit_friendly_name(
742        &mut self,
743        sensor_type: SensoryCorticalUnit,
744        unit_index: CorticalUnitIndex,
745        friendly_name: Option<String>,
746    ) -> Result<(), FeagiDataError> {
747        let sensor_stream_caches =
748            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
749        sensor_stream_caches.set_friendly_name(friendly_name);
750        Ok(())
751    }
752
753    //endregion
754
755    //region Stages
756
757    fn try_get_single_stage_properties(
758        &self,
759        sensor_type: SensoryCorticalUnit,
760        unit_index: CorticalUnitIndex,
761        channel_index: CorticalChannelIndex,
762        pipeline_stage_property_index: PipelineStagePropertyIndex,
763    ) -> Result<PipelineStageProperties, FeagiDataError> {
764        let sensor_stream_caches =
765            self.try_get_sensory_channel_stream_caches(sensor_type, unit_index)?;
766        sensor_stream_caches
767            .try_get_single_stage_properties(channel_index, pipeline_stage_property_index)
768    }
769
770    fn try_get_all_stage_properties(
771        &self,
772        sensor_type: SensoryCorticalUnit,
773        unit_index: CorticalUnitIndex,
774        channel_index: CorticalChannelIndex,
775    ) -> Result<Vec<PipelineStageProperties>, FeagiDataError> {
776        let sensor_stream_caches =
777            self.try_get_sensory_channel_stream_caches(sensor_type, unit_index)?;
778        sensor_stream_caches.get_all_stage_properties(channel_index)
779    }
780
781    fn try_update_single_stage_properties(
782        &mut self,
783        sensor_type: SensoryCorticalUnit,
784        unit_index: CorticalUnitIndex,
785        channel_index: CorticalChannelIndex,
786        pipeline_stage_property_index: PipelineStagePropertyIndex,
787        replacing_property: PipelineStageProperties,
788    ) -> Result<(), FeagiDataError> {
789        let sensor_stream_caches =
790            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
791        sensor_stream_caches.try_update_single_stage_properties(
792            channel_index,
793            pipeline_stage_property_index,
794            replacing_property,
795        )
796    }
797
798    fn try_update_all_stage_properties(
799        &mut self,
800        sensor_type: SensoryCorticalUnit,
801        unit_index: CorticalUnitIndex,
802        channel_index: CorticalChannelIndex,
803        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
804    ) -> Result<(), FeagiDataError> {
805        let sensor_stream_caches =
806            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
807        sensor_stream_caches
808            .try_update_all_stage_properties(channel_index, new_pipeline_stage_properties)
809    }
810
811    fn try_replace_single_stage(
812        &mut self,
813        sensor_type: SensoryCorticalUnit,
814        unit_index: CorticalUnitIndex,
815        channel_index: CorticalChannelIndex,
816        replacing_at_index: PipelineStagePropertyIndex,
817        new_pipeline_stage_properties: PipelineStageProperties,
818    ) -> Result<(), FeagiDataError> {
819        let sensor_stream_caches =
820            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
821        sensor_stream_caches.try_replace_single_stage(
822            channel_index,
823            replacing_at_index,
824            new_pipeline_stage_properties,
825        )
826    }
827
828    fn try_replace_all_stages(
829        &mut self,
830        sensor_type: SensoryCorticalUnit,
831        unit_index: CorticalUnitIndex,
832        channel_index: CorticalChannelIndex,
833        new_pipeline_stage_properties: Vec<PipelineStageProperties>,
834    ) -> Result<(), FeagiDataError> {
835        let sensor_stream_caches =
836            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
837        sensor_stream_caches.try_replace_all_stages(channel_index, new_pipeline_stage_properties)
838    }
839
840    fn try_removing_all_stages(
841        &mut self,
842        sensor_type: SensoryCorticalUnit,
843        unit_index: CorticalUnitIndex,
844        channel_index: CorticalChannelIndex,
845    ) -> Result<(), FeagiDataError> {
846        let sensor_stream_caches =
847            self.try_get_sensory_channel_stream_caches_mut(sensor_type, unit_index)?;
848        sensor_stream_caches.try_removing_all_stages(channel_index)?;
849        Ok(())
850    }
851
852    //endregion
853
854    //endregion
855
856    //region Hashmap Interactions
857
858    fn try_get_sensory_channel_stream_caches(
859        &self,
860        sensor_type: SensoryCorticalUnit,
861        unit_index: CorticalUnitIndex,
862    ) -> Result<&SensoryCorticalUnitCache, FeagiDataError> {
863        let check = self
864            .sensor_cortical_unit_caches
865            .get(&(sensor_type, unit_index));
866        if check.is_none() {
867            return Err(FeagiDataError::BadParameters(format!(
868                "Unable to find {} of cortical unit index {} in registered sensor's list!",
869                sensor_type, unit_index
870            )));
871        }
872        let check = check.unwrap();
873        Ok(check)
874    }
875
876    fn try_get_sensory_channel_stream_caches_mut(
877        &mut self,
878        sensor_type: SensoryCorticalUnit,
879        unit_index: CorticalUnitIndex,
880    ) -> Result<&mut SensoryCorticalUnitCache, FeagiDataError> {
881        let check = self
882            .sensor_cortical_unit_caches
883            .get_mut(&(sensor_type, unit_index));
884        if check.is_none() {
885            return Err(FeagiDataError::BadParameters(format!(
886                "Unable to find {} of cortical unit index {} in registered sensor's list!",
887                sensor_type, unit_index
888            )));
889        }
890        let check = check.unwrap();
891        Ok(check)
892    }
893
894    //endregion
895
896    //endregion
897}
898
899impl fmt::Display for SensorDeviceCache {
900    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
901        Ok(writeln!(f, "Motor Device Cache:")?)
902    }
903}