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