Skip to main content

feagi_structures/genomic/
sensory_cortical_unit.rs

1use crate::genomic::cortical_area::descriptors::CorticalSubUnitIndex;
2use crate::genomic::cortical_area::descriptors::CorticalUnitIndex;
3use crate::genomic::cortical_area::io_cortical_area_configuration_flag::{
4    FrameChangeHandling, PercentageNeuronPositioning,
5};
6use crate::genomic::cortical_area::{
7    CorticalAreaType, CorticalID, IOCorticalAreaConfigurationFlag,
8};
9use crate::sensor_cortical_units;
10use paste;
11use serde_json::{Map, Value};
12use std::collections::HashMap;
13use std::fmt::{Display, Formatter};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)] // TODO move me!
16pub struct UnitTopology {
17    pub relative_position: [i32; 3],
18    pub channel_dimensions_default: [u32; 3],
19    pub channel_dimensions_min: [u32; 3],
20    pub channel_dimensions_max: [u32; 3],
21}
22
23macro_rules! default_firing_threshold_impl {
24    () => {
25        None
26    };
27    ($value:expr) => {
28        Some($value)
29    };
30}
31
32macro_rules! default_mp_charge_accumulation_impl {
33    () => {
34        None
35    };
36    ($value:expr) => {
37        Some($value)
38    };
39}
40
41macro_rules! define_sensory_cortical_units_enum {
42    (
43        SensoryCorticalUnit {
44            $(
45                $(#[doc = $doc:expr])?
46                $variant_name:ident => {
47                    friendly_name: $friendly_name:expr,
48                    accepted_wrapped_io_data_type: $accepted_wrapped_io_data_type:expr,
49                    cortical_id_unit_reference: $cortical_id_unit_reference:expr,
50                    number_cortical_areas: $number_cortical_areas:expr,
51                    $(default_firing_threshold: $default_firing_threshold:expr,)?
52                    $(default_mp_charge_accumulation: $default_mp_charge_accumulation:expr,)?
53                    cortical_type_parameters: {
54                        $($param_name:ident: $param_type:ty),* $(,)?
55                    },
56                    $(allowed_frame_change_handling: [$($allowed_frame:ident),* $(,)?],)? // TODO delete this!
57                    cortical_area_properties: {
58                        $($cortical_sub_unit_index:tt => ($io_cortical_area_configuration_flag_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])),* $(,)?
59                    }
60                }
61            ),* $(,)?
62        }
63    ) => {
64        #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, serde::Serialize, serde::Deserialize)]
65        pub enum SensoryCorticalUnit {
66            $(
67                $(#[doc = $doc])?
68                $variant_name,
69            )*
70        }
71
72        impl SensoryCorticalUnit {
73            $(
74                paste::paste! {
75                    #[doc = "Get cortical area types array for " $friendly_name " using individual parameters."]
76                    pub const fn [<get_cortical_area_types_array_for_ $variant_name:snake _with_parameters >](
77                        $($param_name: $param_type),*) -> [CorticalAreaType; $number_cortical_areas] {
78                        [
79                            $(CorticalAreaType::BrainInput($io_cortical_area_configuration_flag_expr)),*
80                        ]
81                    }
82
83                    #[doc = "Get cortical IDs array for " $friendly_name " using individual parameters."]
84                    pub const fn [<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
85                        $($param_name: $param_type,)* cortical_unit_index: CorticalUnitIndex) -> [CorticalID; $number_cortical_areas] {
86                        let cortical_unit_identifier: [u8; 3] = $cortical_id_unit_reference;
87                        [
88                            $(
89                                $io_cortical_area_configuration_flag_expr .as_io_cortical_id(true, cortical_unit_identifier, cortical_unit_index, CorticalSubUnitIndex::from($cortical_sub_unit_index))
90                            ),*
91                        ]
92                    }
93                }
94            )*
95
96            pub const fn get_snake_case_name(&self) -> &'static str {
97                match self {
98                    $(
99                        SensoryCorticalUnit::$variant_name => paste::paste!{ stringify!([<$variant_name:snake>]) },
100                    )*
101                }
102            }
103
104            /// Parse a sensory cortical unit from its snake_case name
105            ///
106            /// # Arguments
107            /// * `name` - The snake_case name (e.g., "simple_vision", "accelerometer")
108            ///
109            /// # Returns
110            /// * `Some(SensoryCorticalUnit)` - If name matches a known type
111            /// * `None` - If name is not recognized
112            pub fn from_snake_case_name(name: &str) -> Option<SensoryCorticalUnit> {
113                match name {
114                    $(
115                        paste::paste!{ stringify!([<$variant_name:snake>]) } => Some(SensoryCorticalUnit::$variant_name),
116                    )*
117                    _ => None,
118                }
119            }
120
121            /// Returns all available sensory cortical unit types.
122            /// This is useful for enumerating all possible sensor types in the system.
123            pub const fn list_all() -> &'static [SensoryCorticalUnit] {
124                &[
125                    $(
126                        SensoryCorticalUnit::$variant_name,
127                    )*
128                ]
129            }
130
131            /// Returns the friendly (human-readable) name for this sensory cortical unit type.
132            pub const fn get_friendly_name(&self) -> &'static str {
133                match self {
134                    $(
135                        SensoryCorticalUnit::$variant_name => $friendly_name,
136                    )*
137                }
138            }
139
140            /// Returns the 3-byte cortical ID unit reference for this type. // TODO delete me!
141            pub const fn get_cortical_id_unit_reference(&self) -> [u8; 3] {
142                match self {
143                    $(
144                        SensoryCorticalUnit::$variant_name => $cortical_id_unit_reference,
145                    )*
146                }
147            }
148
149            /// Returns the number of cortical areas this type creates.
150            pub const fn get_number_cortical_areas(&self) -> usize {
151                match self {
152                    $(
153                        SensoryCorticalUnit::$variant_name => $number_cortical_areas,
154                    )*
155                }
156            }
157
158            /// Returns the template-defined default firing threshold for auto-created areas.
159            /// `None` means no template override is defined.
160            pub const fn get_default_firing_threshold(&self) -> Option<f64> {
161                match self {
162                    $(
163                        SensoryCorticalUnit::$variant_name => {
164                            default_firing_threshold_impl!($($default_firing_threshold)?)
165                        }
166                    )*
167                }
168            }
169
170            /// Returns the template-defined default mp_charge_accumulation for auto-created areas.
171            /// `None` means no template override is defined.
172            pub const fn get_default_mp_charge_accumulation(&self) -> Option<bool> {
173                match self {
174                    $(
175                        SensoryCorticalUnit::$variant_name => {
176                            default_mp_charge_accumulation_impl!($($default_mp_charge_accumulation)?)
177                        }
178                    )*
179                }
180            }
181
182            /// Returns the accepted wrapped IO data type name for this sensory unit type.
183            pub const fn get_accepted_wrapped_io_data_type(&self) -> &'static str { // TODO delete me!
184                match self {
185                    $(
186                        SensoryCorticalUnit::$variant_name => stringify!($accepted_wrapped_io_data_type),
187                    )*
188                }
189            }
190
191            /// Returns the default topology for all units of this cortical type.
192            pub fn get_unit_default_topology(&self) -> HashMap<CorticalSubUnitIndex, UnitTopology> {
193                match self {
194                    $(
195                        SensoryCorticalUnit::$variant_name => {
196                            let mut topology = HashMap::new();
197                            $(
198                                topology.insert(
199                                    CorticalSubUnitIndex::from($cortical_sub_unit_index),
200                                    UnitTopology {
201                                        relative_position: [$rel_x, $rel_y, $rel_z],
202                                        channel_dimensions_default: [$dim_default_x, $dim_default_y, $dim_default_z],
203                                        channel_dimensions_min: [$dim_min_x, $dim_min_y, $dim_min_z],
204                                        channel_dimensions_max: [$dim_max_x, $dim_max_y, $dim_max_z],
205                                    }
206                                );
207                            )*
208                            topology
209                        }
210                    )*
211                }
212            }
213
214
215            /// Returns the allowed frame change handling modes from the template, if restricted.
216            /// If None is returned, all frame change handling modes are allowed.
217            /// If Some is returned, only the specified modes are valid.
218            pub fn get_allowed_frame_change_handling(&self) -> Option<&'static [FrameChangeHandling]> { // TODO delete me!
219                match self {
220                    $(
221                        SensoryCorticalUnit::$variant_name => {
222                            $crate::get_allowed_frame_change_handling_impl!($($($allowed_frame),*)?)
223                        }
224                    )*
225                }
226            }
227
228            pub fn get_cortical_id_vector_from_index_and_serde_io_configuration_flags(&self, cortical_unit_index: CorticalUnitIndex, map: Map<String, Value>) -> Result<Vec<CorticalID>, crate::FeagiDataError> {
229                match self {
230                    $(
231                        SensoryCorticalUnit::$variant_name => {
232                            paste::paste! {
233                                let array = SensoryCorticalUnit::[<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
234                                    $($param_type::try_from_serde_map(&map)?,)*
235                                    cortical_unit_index);
236                                return Ok(array.to_vec());
237                            }
238                        }
239                    )*
240                }
241            }
242
243
244
245        }
246
247        impl Display for SensoryCorticalUnit {
248            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
249                match self {
250                    $(
251                        SensoryCorticalUnit::$variant_name => write!(f, $friendly_name),
252                    )*
253                }
254    }
255        }
256    };
257
258}
259// Generate the SensoryCorticalUnit enum and all helper methods from the template
260sensor_cortical_units!(define_sensory_cortical_units_enum);
261
262impl SensoryCorticalUnit {
263    /// Check if a legacy 3-char subtype matches a supported IPU type and return its default CorticalID.
264    /// Used by genome migrator to avoid converting unsupported legacy IPU areas to MiscData.
265    pub fn try_from_legacy_subtype(subtype: &str) -> Option<CorticalID> {
266        let subtype_bytes = subtype.as_bytes();
267        if subtype_bytes.len() != 3 {
268            return None;
269        }
270        let subtype_arr = [subtype_bytes[0], subtype_bytes[1], subtype_bytes[2]];
271        for unit in Self::list_all() {
272            if unit.get_cortical_id_unit_reference() == subtype_arr {
273                return Some(unit.get_default_cortical_id_for_group(CorticalUnitIndex::from(0u8)));
274            }
275        }
276        None
277    }
278
279    /// Get the default CorticalID for this unit with group index 0 (Absolute frame handling, Linear positioning).
280    pub fn get_default_cortical_id_for_group(&self, group_index: CorticalUnitIndex) -> CorticalID {
281        use crate::genomic::cortical_area::io_cortical_area_configuration_flag::{
282            FrameChangeHandling, PercentageNeuronPositioning,
283        };
284        let fh = FrameChangeHandling::Absolute;
285        let pos = PercentageNeuronPositioning::Linear;
286        match self {
287            SensoryCorticalUnit::Infrared => {
288                Self::get_cortical_ids_array_for_infrared_with_parameters(fh, pos, group_index)[0]
289            }
290            SensoryCorticalUnit::Proximity => {
291                Self::get_cortical_ids_array_for_proximity_with_parameters(fh, pos, group_index)[0]
292            }
293            SensoryCorticalUnit::Shock => {
294                Self::get_cortical_ids_array_for_shock_with_parameters(fh, pos, group_index)[0]
295            }
296            SensoryCorticalUnit::Battery => {
297                Self::get_cortical_ids_array_for_battery_with_parameters(fh, pos, group_index)[0]
298            }
299            SensoryCorticalUnit::Servo => {
300                Self::get_cortical_ids_array_for_servo_with_parameters(fh, pos, group_index)[0]
301            }
302            SensoryCorticalUnit::AnalogGPIO => {
303                Self::get_cortical_ids_array_for_analog_g_p_i_o_with_parameters(
304                    fh,
305                    pos,
306                    group_index,
307                )[0]
308            }
309            SensoryCorticalUnit::DigitalGPIO => {
310                Self::get_cortical_ids_array_for_digital_g_p_i_o_with_parameters(group_index)[0]
311            }
312            SensoryCorticalUnit::MiscData => {
313                Self::get_cortical_ids_array_for_misc_data_with_parameters(fh, group_index)[0]
314            }
315            SensoryCorticalUnit::TextEnglishInput => {
316                Self::get_cortical_ids_array_for_text_english_input_with_parameters(fh, group_index)
317                    [0]
318            }
319            SensoryCorticalUnit::CountInput => {
320                Self::get_cortical_ids_array_for_count_input_with_parameters(fh, pos, group_index)
321                    [0]
322            }
323            SensoryCorticalUnit::Vision => {
324                Self::get_cortical_ids_array_for_vision_with_parameters(fh, group_index)[0]
325            }
326            SensoryCorticalUnit::SegmentedVision => {
327                Self::get_cortical_ids_array_for_segmented_vision_with_parameters(fh, group_index)
328                    [0]
329            }
330            SensoryCorticalUnit::Accelerometer => {
331                Self::get_cortical_ids_array_for_accelerometer_with_parameters(fh, pos, group_index)
332                    [0]
333            }
334            SensoryCorticalUnit::Gyroscope => {
335                Self::get_cortical_ids_array_for_gyroscope_with_parameters(fh, pos, group_index)[0]
336            }
337        }
338    }
339}