Skip to main content

feagi_structures/genomic/
motor_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::genomic::sensory_cortical_unit::UnitTopology;
10use crate::motor_cortical_units;
11use paste;
12use serde_json::{Map, Value};
13use std::collections::HashMap;
14use std::fmt::{Display, Formatter};
15
16// Helper macro to handle optional allowed_frame_change_handling
17#[macro_export]
18macro_rules! get_allowed_frame_change_handling_impl { // TODO delete this!
19    () => {
20        None
21    };
22    ($($allowed:ident),+) => {
23        Some(&[$(FrameChangeHandling::$allowed),+] as &'static [FrameChangeHandling])
24    };
25}
26
27macro_rules! define_motor_cortical_units_enum {
28    (
29        MotorCorticalUnit {
30            $(
31                $(#[doc = $doc:expr])?
32                $variant_name:ident => {
33                    friendly_name: $friendly_name:expr,
34                    accepted_wrapped_io_data_type: $accepted_wrapped_io_data_type:expr,
35                    cortical_id_unit_reference: $cortical_id_unit_reference:expr,
36                    number_cortical_areas: $number_cortical_areas:expr,
37                    cortical_type_parameters: {
38                        $($param_name:ident: $param_type:ty),* $(,)?
39                    },
40                    $(allowed_frame_change_handling: [$($allowed_frame:ident),* $(,)?],)? // TODO delete this!
41                    cortical_area_properties: {
42                        $($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])),* $(,)?
43                    }
44                }
45            ),* $(,)?
46        }
47    ) => {
48        #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, serde::Deserialize, serde::Serialize)]
49        pub enum MotorCorticalUnit {
50            $(
51                $(#[doc = $doc])?
52                $variant_name,
53            )*
54        }
55
56        impl MotorCorticalUnit {
57            $(
58                paste::paste! {
59                    #[doc = "Get cortical area types array for " $friendly_name "."]
60                    pub const fn [<get_cortical_area_types_array_for_ $variant_name:snake _with_parameters >](
61                        $($param_name: $param_type),*) -> [CorticalAreaType; $number_cortical_areas] {
62                        // Keep parameterized API stable even for unit templates that don't consume every parameter.
63                        $(let _ = &$param_name;)*
64                        [
65                            $(CorticalAreaType::BrainOutput($io_cortical_area_configuration_flag_expr)),*
66                        ]
67                    }
68
69                    #[doc = "Get cortical IDs array for " $friendly_name "."]
70                    pub const fn [<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
71                        $($param_name: $param_type,)* cortical_unit_index: CorticalUnitIndex) -> [CorticalID; $number_cortical_areas] {
72                        // Keep parameterized API stable even for unit templates that don't consume every parameter.
73                        $(let _ = &$param_name;)*
74                        let cortical_unit_identifier: [u8; 3] = $cortical_id_unit_reference;
75                        [
76                            $(
77                                $io_cortical_area_configuration_flag_expr .as_io_cortical_id(false, cortical_unit_identifier, cortical_unit_index, CorticalSubUnitIndex::from($cortical_sub_unit_index))
78                            ),*
79                        ]
80                    }
81                }
82            )*
83
84            pub const fn get_snake_case_name(&self) -> &'static str {
85                match self {
86                    $(
87                        MotorCorticalUnit::$variant_name => paste::paste!{ stringify!([<$variant_name:snake>]) },
88                    )*
89                }
90            }
91
92            /// Get the accepted wrapped IO data type for this motor cortical unit.
93            /// Returns the string name of the data type (e.g., "MiscData", "SignedPercentageData").
94            pub const fn get_accepted_wrapped_io_data_type(&self) -> &'static str {
95                match self {
96                    $(
97                        MotorCorticalUnit::$variant_name => stringify!($variant_name:snake),
98                    )*
99                }
100            }
101
102            /// Parse a motor cortical unit from its snake_case name
103            ///
104            /// # Arguments
105            /// * `name` - The snake_case name (e.g., "positional_servo", "led_matrix")
106            ///
107            /// # Returns
108            /// * `Some(MotorCorticalUnit)` - If name matches a known type
109            /// * `None` - If name is not recognized
110            pub fn from_snake_case_name(name: &str) -> Option<MotorCorticalUnit> {
111                match name {
112                    $(
113                        paste::paste!{ stringify!([<$variant_name:snake>]) } => Some(MotorCorticalUnit::$variant_name),
114                    )*
115                    _ => None,
116                }
117            }
118
119            // TODO from_snake_case_name_const
120
121            /// Returns all available motor cortical unit types.
122            /// This is useful for enumerating all possible motor types in the system.
123            pub const fn list_all() -> &'static [MotorCorticalUnit] {
124                &[
125                    $(
126                        MotorCorticalUnit::$variant_name,
127                    )*
128                ]
129            }
130
131            /// Returns the friendly (human-readable) name for this motor cortical unit type.
132            pub const fn get_friendly_name(&self) -> &'static str {
133                match self {
134                    $(
135                        MotorCorticalUnit::$variant_name => $friendly_name,
136                    )*
137                }
138            }
139
140            /// Returns the 3-byte cortical ID unit reference for this type.
141            pub const fn get_cortical_id_unit_reference(&self) -> [u8; 3] {
142                match self {
143                    $(
144                        MotorCorticalUnit::$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                        MotorCorticalUnit::$variant_name => $number_cortical_areas,
154                    )*
155                }
156            }
157
158            /// Returns the default topology for all units of this cortical type.
159            pub fn get_unit_default_topology(&self) -> HashMap<CorticalSubUnitIndex, UnitTopology> {
160                match self {
161                    $(
162                        MotorCorticalUnit::$variant_name => {
163                            let mut topology = HashMap::new();
164                            $(
165                                topology.insert(
166                                    CorticalSubUnitIndex::from($cortical_sub_unit_index),
167                                    UnitTopology {
168                                        relative_position: [$rel_x, $rel_y, $rel_z],
169                                        channel_dimensions_default: [$dim_default_x, $dim_default_y, $dim_default_z],
170                                        channel_dimensions_min: [$dim_min_x, $dim_min_y, $dim_min_z],
171                                        channel_dimensions_max: [$dim_max_x, $dim_max_y, $dim_max_z],
172                                    }
173                                );
174                            )*
175                            topology
176                        }
177                    )*
178                }
179            }
180
181            /// Returns the allowed FrameChangeHandling values for this motor cortical unit.
182            /// If None, all FrameChangeHandling values are allowed.
183            /// If Some, only the specified values are allowed.
184            pub fn get_allowed_frame_change_handling(&self) -> Option<&'static [FrameChangeHandling]> {
185                match self {
186                    $(
187                        MotorCorticalUnit::$variant_name => {
188                            $crate::get_allowed_frame_change_handling_impl!($($($allowed_frame),*)?)
189                        }
190                    )*
191                }
192            }
193
194            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> {
195                match self {
196                    $(
197                        MotorCorticalUnit::$variant_name => {
198                            paste::paste! {
199                                let array = MotorCorticalUnit::[<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
200                                    $($param_type::try_from_serde_map(&map)?,)*
201                                    cortical_unit_index);
202                                return Ok(array.to_vec());
203                            }
204                        }
205                    )*
206                }
207            }
208
209        }
210
211        impl Display for MotorCorticalUnit {
212            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
213                match self {
214                    $(
215                        MotorCorticalUnit::$variant_name => write!(f, $friendly_name),
216                    )*
217                }
218            }
219        }
220    };
221
222}
223// Generate the MotorCorticalUnit enum and all helper methods from the template
224motor_cortical_units!(define_motor_cortical_units_enum);
225
226impl MotorCorticalUnit {
227    /// Check if a legacy 3-char subtype matches a supported OPU type and return its default CorticalID.
228    /// Used by genome migrator to avoid converting unsupported legacy OPU areas to MiscData.
229    pub fn try_from_legacy_subtype(subtype: &str) -> Option<CorticalID> {
230        let subtype_bytes = subtype.as_bytes();
231        if subtype_bytes.len() != 3 {
232            return None;
233        }
234        let subtype_arr = [subtype_bytes[0], subtype_bytes[1], subtype_bytes[2]];
235        for unit in Self::list_all() {
236            if unit.get_cortical_id_unit_reference() == subtype_arr {
237                return Some(unit.get_default_cortical_id_for_group(CorticalUnitIndex::from(0u8)));
238            }
239        }
240        None
241    }
242
243    /// Get the default CorticalID for this unit with group index 0 (Absolute frame handling, Linear positioning).
244    pub fn get_default_cortical_id_for_group(&self, group_index: CorticalUnitIndex) -> CorticalID {
245        use crate::genomic::cortical_area::io_cortical_area_configuration_flag::{
246            FrameChangeHandling, PercentageNeuronPositioning,
247        };
248        let fh = FrameChangeHandling::Absolute;
249        let pos = PercentageNeuronPositioning::Linear;
250        match self {
251            MotorCorticalUnit::RotaryMotor => {
252                Self::get_cortical_ids_array_for_rotary_motor_with_parameters(fh, pos, group_index)
253                    [0]
254            }
255            MotorCorticalUnit::PositionalServo => {
256                Self::get_cortical_ids_array_for_positional_servo_with_parameters(
257                    FrameChangeHandling::Absolute,
258                    pos,
259                    group_index,
260                )[0]
261            }
262            MotorCorticalUnit::Gaze => {
263                Self::get_cortical_ids_array_for_gaze_with_parameters(fh, pos, group_index)[0]
264            }
265            MotorCorticalUnit::MiscData => {
266                Self::get_cortical_ids_array_for_misc_data_with_parameters(fh, group_index)[0]
267            }
268            MotorCorticalUnit::TextEnglishOutput => {
269                Self::get_cortical_ids_array_for_text_english_output_with_parameters(
270                    fh,
271                    group_index,
272                )[0]
273            }
274            MotorCorticalUnit::CountOutput => {
275                Self::get_cortical_ids_array_for_count_output_with_parameters(fh, pos, group_index)
276                    [0]
277            }
278            MotorCorticalUnit::ObjectSegmentation => {
279                Self::get_cortical_ids_array_for_object_segmentation_with_parameters(
280                    fh,
281                    group_index,
282                )[0]
283            }
284            MotorCorticalUnit::SimpleVisionOutput => {
285                Self::get_cortical_ids_array_for_simple_vision_output_with_parameters(
286                    fh,
287                    group_index,
288                )[0]
289            }
290            MotorCorticalUnit::DynamicImageProcessing => {
291                Self::get_cortical_ids_array_for_dynamic_image_processing_with_parameters(
292                    fh,
293                    pos,
294                    group_index,
295                )[0]
296            }
297        }
298    }
299}