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                        [
63                            $(CorticalAreaType::BrainOutput($io_cortical_area_configuration_flag_expr)),*
64                        ]
65                    }
66
67                    #[doc = "Get cortical IDs array for " $friendly_name "."]
68                    pub const fn [<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
69                        $($param_name: $param_type,)* cortical_unit_index: CorticalUnitIndex) -> [CorticalID; $number_cortical_areas] {
70                        let cortical_unit_identifier: [u8; 3] = $cortical_id_unit_reference;
71                        [
72                            $(
73                                $io_cortical_area_configuration_flag_expr .as_io_cortical_id(false, cortical_unit_identifier, cortical_unit_index, CorticalSubUnitIndex::from($cortical_sub_unit_index))
74                            ),*
75                        ]
76                    }
77                }
78            )*
79
80            pub const fn get_snake_case_name(&self) -> &'static str {
81                match self {
82                    $(
83                        MotorCorticalUnit::$variant_name => paste::paste!{ stringify!([<$variant_name:snake>]) },
84                    )*
85                }
86            }
87
88            /// Get the accepted wrapped IO data type for this motor cortical unit.
89            /// Returns the string name of the data type (e.g., "MiscData", "SignedPercentageData").
90            pub const fn get_accepted_wrapped_io_data_type(&self) -> &'static str {
91                match self {
92                    $(
93                        MotorCorticalUnit::$variant_name => stringify!($variant_name:snake),
94                    )*
95                }
96            }
97
98            /// Parse a motor cortical unit from its snake_case name
99            ///
100            /// # Arguments
101            /// * `name` - The snake_case name (e.g., "positional_servo", "led_matrix")
102            ///
103            /// # Returns
104            /// * `Some(MotorCorticalUnit)` - If name matches a known type
105            /// * `None` - If name is not recognized
106            pub fn from_snake_case_name(name: &str) -> Option<MotorCorticalUnit> {
107                match name {
108                    $(
109                        paste::paste!{ stringify!([<$variant_name:snake>]) } => Some(MotorCorticalUnit::$variant_name),
110                    )*
111                    _ => None,
112                }
113            }
114
115            // TODO from_snake_case_name_const
116
117            /// Returns all available motor cortical unit types.
118            /// This is useful for enumerating all possible motor types in the system.
119            pub const fn list_all() -> &'static [MotorCorticalUnit] {
120                &[
121                    $(
122                        MotorCorticalUnit::$variant_name,
123                    )*
124                ]
125            }
126
127            /// Returns the friendly (human-readable) name for this motor cortical unit type.
128            pub const fn get_friendly_name(&self) -> &'static str {
129                match self {
130                    $(
131                        MotorCorticalUnit::$variant_name => $friendly_name,
132                    )*
133                }
134            }
135
136            /// Returns the 3-byte cortical ID unit reference for this type.
137            pub const fn get_cortical_id_unit_reference(&self) -> [u8; 3] {
138                match self {
139                    $(
140                        MotorCorticalUnit::$variant_name => $cortical_id_unit_reference,
141                    )*
142                }
143            }
144
145            /// Returns the number of cortical areas this type creates.
146            pub const fn get_number_cortical_areas(&self) -> usize {
147                match self {
148                    $(
149                        MotorCorticalUnit::$variant_name => $number_cortical_areas,
150                    )*
151                }
152            }
153
154            /// Returns the default topology for all units of this cortical type.
155            pub fn get_unit_default_topology(&self) -> HashMap<CorticalSubUnitIndex, UnitTopology> {
156                match self {
157                    $(
158                        MotorCorticalUnit::$variant_name => {
159                            let mut topology = HashMap::new();
160                            $(
161                                topology.insert(
162                                    CorticalSubUnitIndex::from($cortical_sub_unit_index),
163                                    UnitTopology {
164                                        relative_position: [$rel_x, $rel_y, $rel_z],
165                                        channel_dimensions_default: [$dim_default_x, $dim_default_y, $dim_default_z],
166                                        channel_dimensions_min: [$dim_min_x, $dim_min_y, $dim_min_z],
167                                        channel_dimensions_max: [$dim_max_x, $dim_max_y, $dim_max_z],
168                                    }
169                                );
170                            )*
171                            topology
172                        }
173                    )*
174                }
175            }
176
177            /// Returns the allowed FrameChangeHandling values for this motor cortical unit.
178            /// If None, all FrameChangeHandling values are allowed.
179            /// If Some, only the specified values are allowed.
180            pub fn get_allowed_frame_change_handling(&self) -> Option<&'static [FrameChangeHandling]> {
181                match self {
182                    $(
183                        MotorCorticalUnit::$variant_name => {
184                            $crate::get_allowed_frame_change_handling_impl!($($($allowed_frame),*)?)
185                        }
186                    )*
187                }
188            }
189
190            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> {
191                match self {
192                    $(
193                        MotorCorticalUnit::$variant_name => {
194                            paste::paste! {
195                                let array = MotorCorticalUnit::[<get_cortical_ids_array_for_ $variant_name:snake _with_parameters >](
196                                    $($param_type::try_from_serde_map(&map)?,)*
197                                    cortical_unit_index);
198                                return Ok(array.to_vec());
199                            }
200                        }
201                    )*
202                }
203            }
204
205        }
206
207        impl Display for MotorCorticalUnit {
208            fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
209                match self {
210                    $(
211                        MotorCorticalUnit::$variant_name => write!(f, $friendly_name),
212                    )*
213                }
214            }
215        }
216    };
217
218}
219// Generate the MotorCorticalUnit enum and all helper methods from the template
220motor_cortical_units!(define_motor_cortical_units_enum);
221
222impl MotorCorticalUnit {
223    /// Check if a legacy 3-char subtype matches a supported OPU type and return its default CorticalID.
224    /// Used by genome migrator to avoid converting unsupported legacy OPU areas to MiscData.
225    pub fn try_from_legacy_subtype(subtype: &str) -> Option<CorticalID> {
226        let subtype_bytes = subtype.as_bytes();
227        if subtype_bytes.len() != 3 {
228            return None;
229        }
230        let subtype_arr = [subtype_bytes[0], subtype_bytes[1], subtype_bytes[2]];
231        for unit in Self::list_all() {
232            if unit.get_cortical_id_unit_reference() == subtype_arr {
233                return Some(unit.get_default_cortical_id_for_group(CorticalUnitIndex::from(0u8)));
234            }
235        }
236        None
237    }
238
239    /// Get the default CorticalID for this unit with group index 0 (Absolute frame handling, Linear positioning).
240    pub fn get_default_cortical_id_for_group(&self, group_index: CorticalUnitIndex) -> CorticalID {
241        use crate::genomic::cortical_area::io_cortical_area_configuration_flag::{
242            FrameChangeHandling, PercentageNeuronPositioning,
243        };
244        let fh = FrameChangeHandling::Absolute;
245        let pos = PercentageNeuronPositioning::Linear;
246        match self {
247            MotorCorticalUnit::RotaryMotor => {
248                Self::get_cortical_ids_array_for_rotary_motor_with_parameters(fh, pos, group_index)
249                    [0]
250            }
251            MotorCorticalUnit::PositionalServo => {
252                Self::get_cortical_ids_array_for_positional_servo_with_parameters(
253                    FrameChangeHandling::Absolute,
254                    pos,
255                    group_index,
256                )[0]
257            }
258            MotorCorticalUnit::Gaze => {
259                Self::get_cortical_ids_array_for_gaze_with_parameters(fh, pos, group_index)[0]
260            }
261            MotorCorticalUnit::MiscData => {
262                Self::get_cortical_ids_array_for_misc_data_with_parameters(fh, group_index)[0]
263            }
264            MotorCorticalUnit::TextEnglishOutput => {
265                Self::get_cortical_ids_array_for_text_english_output_with_parameters(
266                    fh,
267                    group_index,
268                )[0]
269            }
270            MotorCorticalUnit::CountOutput => {
271                Self::get_cortical_ids_array_for_count_output_with_parameters(fh, pos, group_index)
272                    [0]
273            }
274            MotorCorticalUnit::ObjectSegmentation => {
275                Self::get_cortical_ids_array_for_object_segmentation_with_parameters(
276                    fh,
277                    group_index,
278                )[0]
279            }
280            MotorCorticalUnit::SimpleVisionOutput => {
281                Self::get_cortical_ids_array_for_simple_vision_output_with_parameters(
282                    fh,
283                    group_index,
284                )[0]
285            }
286            MotorCorticalUnit::DynamicImageProcessing => {
287                Self::get_cortical_ids_array_for_dynamic_image_processing_with_parameters(
288                    fh,
289                    pos,
290                    group_index,
291                )[0]
292            }
293        }
294    }
295}