autosar_data_abstraction/system/
mapping.rs

1use crate::{
2    AbstractionElement, AutosarAbstractionError, EcuInstance, Element, IdentifiableAbstractionElement, System,
3    abstraction_element, communication, software_component,
4};
5use autosar_data::ElementName;
6use communication::SystemSignal;
7use software_component::{
8    AbstractSwComponentType, ComponentPrototype, PortInterface, PortPrototype, RootSwCompositionPrototype,
9    SwComponentPrototype, VariableDataPrototype,
10};
11
12//##################################################################
13
14/// A `SystemMapping` contains mappings in the `System`
15///
16/// it contains mappings between SWCs and ECUs, as well as between ports and signals
17#[derive(Debug, Clone, PartialEq, Eq, Hash)]
18pub struct SystemMapping(Element);
19abstraction_element!(SystemMapping, SystemMapping);
20impl IdentifiableAbstractionElement for SystemMapping {}
21
22impl SystemMapping {
23    pub(crate) fn new(name: &str, system: &System) -> Result<Self, AutosarAbstractionError> {
24        let element = system
25            .element()
26            .get_or_create_sub_element(ElementName::Mappings)?
27            .create_named_sub_element(ElementName::SystemMapping, name)?;
28
29        Ok(Self(element))
30    }
31
32    /// get the system that contains this mapping
33    pub fn system(&self) -> Result<System, AutosarAbstractionError> {
34        let sys_elem = self.element().named_parent()?.unwrap();
35        System::try_from(sys_elem)
36    }
37
38    /// create a new mapping between a SWC and an ECU
39    pub fn map_swc_to_ecu(
40        &self,
41        name: &str,
42        component_prototype: &SwComponentPrototype,
43        ecu: &EcuInstance,
44    ) -> Result<SwcToEcuMapping, AutosarAbstractionError> {
45        let root_composition_prototype =
46            self.system()?
47                .root_sw_composition()
48                .ok_or(AutosarAbstractionError::InvalidParameter(
49                    "The root compositon must be set before mapping any swc".to_string(),
50                ))?;
51        let root_composition_type =
52            root_composition_prototype
53                .composition()
54                .ok_or(AutosarAbstractionError::InvalidParameter(
55                    "Incomplete root composition prototype".to_string(),
56                ))?;
57
58        let mut context_composition_prototypes = vec![];
59        let mut current_composition = component_prototype.parent_composition()?;
60
61        // check if the composition is a child of the root composition; this is needed to ensure that the loop can terminate
62        if root_composition_type != current_composition && !root_composition_type.is_parent_of(&current_composition) {
63            return Err(AutosarAbstractionError::InvalidParameter(
64                "The composition is not a child of the root composition".to_string(),
65            ));
66        }
67
68        // find all compositions between the root composition and the current composition
69        while current_composition != root_composition_type {
70            // typical case is that each component is only in one composition, so the for loop should only run once
71            for comp_proto in current_composition.instances() {
72                // this condition should never fail - it only returns none if comp_proto is the root
73                // composition, which we already know is not true
74                if let Ok(Some(comp_type)) = comp_proto.parent_composition()
75                    && (root_composition_type == comp_type || root_composition_type.is_parent_of(&comp_type))
76                {
77                    context_composition_prototypes.push(comp_proto.clone());
78                    current_composition = comp_type;
79                    break;
80                }
81            }
82        }
83
84        // the items were collected in reverse order, so we need to reverse them again
85        context_composition_prototypes.reverse();
86
87        SwcToEcuMapping::new(
88            name,
89            component_prototype,
90            &context_composition_prototypes,
91            &root_composition_prototype,
92            ecu,
93            self,
94        )
95    }
96
97    /// create a new mapping between a sender/receiver port and a signal
98    ///
99    /// `signal`: the system signal that the port is mapped to
100    ///
101    /// `data_element`: the data element that is mapped to the signal
102    ///
103    /// `port_prototype`: the port prototype that contains the data element
104    ///
105    /// `context_components`: a list of component prototypes from the root up to the component that directly contains the port.
106    /// This list may be empty, or it could only contain the final application component prototype containing the port.
107    ///
108    /// `root_composition_prototype`: the root composition prototype that contains the `swc_prototype`.
109    /// Rarely required, but may be needed if multiple root compositions use the same composition/component hierarchy.
110    pub fn map_sender_receiver_to_signal<T: Into<PortPrototype> + Clone>(
111        &self,
112        signal: &SystemSignal,
113        data_element: &VariableDataPrototype,
114        port_prototype: &T,
115        context_components: &[&SwComponentPrototype],
116        root_composition_prototype: Option<&RootSwCompositionPrototype>,
117    ) -> Result<SenderReceiverToSignalMapping, AutosarAbstractionError> {
118        self.map_sender_receiver_to_signal_internal(
119            signal,
120            data_element,
121            &port_prototype.clone().into(),
122            context_components,
123            root_composition_prototype,
124        )
125    }
126
127    fn map_sender_receiver_to_signal_internal(
128        &self,
129        signal: &SystemSignal,
130        data_element: &VariableDataPrototype,
131        port_prototype: &PortPrototype,
132        context_components: &[&SwComponentPrototype],
133        root_composition_prototype: Option<&RootSwCompositionPrototype>,
134    ) -> Result<SenderReceiverToSignalMapping, AutosarAbstractionError> {
135        // sanity checks
136        // the port must be a sender/receiver port
137        let Some(PortInterface::SenderReceiverInterface(interface)) = port_prototype.port_interface() else {
138            return Err(AutosarAbstractionError::InvalidParameter(
139                "The port prototype must be a sender/receiver port".to_string(),
140            ));
141        };
142
143        // the data element must be part of the sender/receiver interface
144        if data_element.interface()? != interface {
145            return Err(AutosarAbstractionError::InvalidParameter(
146                "The data element must be part of the sender/receiver interface".to_string(),
147            ));
148        }
149
150        // the last context component in the list contains the port prototype
151        if let Some(swc_prototype) = context_components.last() {
152            let swc_type = port_prototype.component_type()?;
153            let swc_prototype_type =
154                swc_prototype
155                    .component_type()
156                    .ok_or(AutosarAbstractionError::InvalidParameter(
157                        "invalid SWC prototype: component type ref is missing".to_string(),
158                    ))?;
159            if swc_type != swc_prototype_type {
160                return Err(AutosarAbstractionError::InvalidParameter(
161                    "The port must be part of the component prototype".to_string(),
162                ));
163            }
164        }
165
166        // create the mapping
167        let data_mappings = self.element().get_or_create_sub_element(ElementName::DataMappings)?;
168
169        SenderReceiverToSignalMapping::new(
170            &data_mappings,
171            signal,
172            data_element,
173            port_prototype,
174            context_components,
175            root_composition_prototype,
176        )
177    }
178}
179
180//#########################################################
181
182/// A `SwcToEcuMapping` contains a mapping between a `SwComponentPrototype` and an `EcuInstance`
183#[derive(Debug, Clone, PartialEq, Eq, Hash)]
184pub struct SwcToEcuMapping(Element);
185abstraction_element!(SwcToEcuMapping, SwcToEcuMapping);
186impl IdentifiableAbstractionElement for SwcToEcuMapping {}
187
188impl SwcToEcuMapping {
189    pub(crate) fn new(
190        name: &str,
191        component_prototype: &SwComponentPrototype,
192        context_composition_prototypes: &[ComponentPrototype],
193        root_composition_prototype: &RootSwCompositionPrototype,
194        ecu: &EcuInstance,
195        mapping: &SystemMapping,
196    ) -> Result<Self, AutosarAbstractionError> {
197        let sw_mappings_elem = mapping.element().get_or_create_sub_element(ElementName::SwMappings)?;
198        let swc_to_ecu_mapping = sw_mappings_elem.create_named_sub_element(ElementName::SwcToEcuMapping, name)?;
199
200        let iref = swc_to_ecu_mapping
201            .create_sub_element(ElementName::ComponentIrefs)?
202            .create_sub_element(ElementName::ComponentIref)?;
203
204        // create the references to root composition and context compositions
205        iref.create_sub_element(ElementName::ContextCompositionRef)?
206            .set_reference_target(root_composition_prototype.element())?;
207        for context_comp in context_composition_prototypes {
208            iref.create_sub_element(ElementName::ContextComponentRef)?
209                .set_reference_target(context_comp.element())?;
210        }
211        // create the reference to the target component prototype
212        iref.create_sub_element(ElementName::TargetComponentRef)?
213            .set_reference_target(component_prototype.element())?;
214
215        swc_to_ecu_mapping
216            .create_sub_element(ElementName::EcuInstanceRef)?
217            .set_reference_target(ecu.element())?;
218
219        Ok(Self(swc_to_ecu_mapping))
220    }
221
222    /// get the component prototype that is mapped here
223    #[must_use]
224    pub fn target_component(&self) -> Option<SwComponentPrototype> {
225        self.element()
226            .get_sub_element(ElementName::ComponentIrefs)
227            .and_then(|irefs| irefs.get_sub_element(ElementName::ComponentIref))
228            .and_then(|iref| iref.get_sub_element(ElementName::TargetComponentRef))
229            .and_then(|target| target.get_reference_target().ok())
230            .and_then(|target| SwComponentPrototype::try_from(target).ok())
231    }
232
233    /// get the ECU instance which is the target of this mapping
234    #[must_use]
235    pub fn ecu_instance(&self) -> Option<EcuInstance> {
236        self.element()
237            .get_sub_element(ElementName::EcuInstanceRef)
238            .and_then(|r| r.get_reference_target().ok())
239            .and_then(|target| EcuInstance::try_from(target).ok())
240    }
241}
242
243//#########################################################
244
245/// A `SenderReceiverToSignalMapping` contains a mapping between a sender/receiver port and a system signal
246#[derive(Debug, Clone, PartialEq, Eq, Hash)]
247pub struct SenderReceiverToSignalMapping(Element);
248abstraction_element!(SenderReceiverToSignalMapping, SenderReceiverToSignalMapping);
249
250impl SenderReceiverToSignalMapping {
251    pub(crate) fn new(
252        parent: &Element,
253        signal: &SystemSignal,
254        data_element: &VariableDataPrototype,
255        port_prototype: &PortPrototype,
256        context_components: &[&SwComponentPrototype],
257        root_composition_prototype: Option<&RootSwCompositionPrototype>,
258    ) -> Result<Self, AutosarAbstractionError> {
259        let sr_mapping = parent.create_sub_element(ElementName::SenderReceiverToSignalMapping)?;
260        let iref = sr_mapping.create_sub_element(ElementName::DataElementIref)?;
261        iref.create_sub_element(ElementName::ContextPortRef)?
262            .set_reference_target(port_prototype.element())?;
263        iref.create_sub_element(ElementName::TargetDataPrototypeRef)?
264            .set_reference_target(data_element.element())?;
265
266        // the list of context components is ordered, with the root composition prototype at the beginning
267        for comp_proto in context_components {
268            iref.create_sub_element(ElementName::ContextComponentRef)?
269                .set_reference_target(comp_proto.element())?;
270        }
271
272        if let Some(root_composition_prototype) = root_composition_prototype {
273            iref.create_sub_element(ElementName::ContextCompositionRef)?
274                .set_reference_target(root_composition_prototype.element())?;
275        }
276
277        sr_mapping
278            .create_sub_element(ElementName::SystemSignalRef)?
279            .set_reference_target(signal.element())?;
280
281        Ok(Self(sr_mapping))
282    }
283
284    /// Get the system signal that is the target of this mapping
285    #[must_use]
286    pub fn system_signal(&self) -> Option<SystemSignal> {
287        let element = self
288            .element()
289            .get_sub_element(ElementName::SystemSignalRef)
290            .and_then(|r| r.get_reference_target().ok())?;
291        SystemSignal::try_from(element).ok()
292    }
293
294    /// Get the data element that is mapped to the signal
295    #[must_use]
296    pub fn data_element(&self) -> Option<VariableDataPrototype> {
297        let element = self
298            .element()
299            .get_sub_element(ElementName::DataElementIref)
300            .and_then(|iref| iref.get_sub_element(ElementName::TargetDataPrototypeRef))
301            .and_then(|r| r.get_reference_target().ok())?;
302        VariableDataPrototype::try_from(element).ok()
303    }
304}
305
306//#########################################################
307
308#[cfg(test)]
309mod test {
310    use super::*;
311    use crate::{
312        AutosarModelAbstraction, SystemCategory,
313        datatype::{ApplicationPrimitiveCategory, ApplicationPrimitiveDataType},
314    };
315
316    #[test]
317    fn mappings() {
318        let model = AutosarModelAbstraction::create("filename", autosar_data::AutosarVersion::LATEST);
319        let package = model.get_or_create_package("/package").unwrap();
320        let system = package
321            .create_system("test_system", SystemCategory::EcuExtract)
322            .unwrap();
323        let mapping = system.get_or_create_mapping("test_mapping").unwrap();
324
325        let ecu = system.create_ecu_instance("test_ecu", &package).unwrap();
326        let root_composition_type = package.create_composition_sw_component_type("test_swc").unwrap();
327        let _root_composition = system
328            .set_root_sw_composition("test_root_composition", &root_composition_type)
329            .unwrap();
330
331        let ecu_composition_type = package
332            .create_composition_sw_component_type("Ecu_A_Composition")
333            .unwrap();
334        let ecu_composition_prototype = root_composition_type
335            .create_component("Ecu_A_Composition_Prototype", &ecu_composition_type)
336            .unwrap();
337
338        // map ecu_composition_prototype to the ecu
339        let swc_to_ecu = mapping
340            .map_swc_to_ecu("test_swc_to_ecu", &ecu_composition_prototype, &ecu)
341            .unwrap();
342
343        assert_eq!(swc_to_ecu.target_component().unwrap(), ecu_composition_prototype);
344        assert_eq!(swc_to_ecu.ecu_instance().unwrap(), ecu);
345
346        // map a signal to a port
347        let sys_signal = package.create_system_signal("test_signal").unwrap();
348
349        let sender_receiver_interface = package
350            .create_sender_receiver_interface("SenderReceiverInterface")
351            .unwrap();
352        let data_type = ApplicationPrimitiveDataType::new(
353            "Primitive",
354            &package,
355            ApplicationPrimitiveCategory::Value,
356            None,
357            None,
358            None,
359        )
360        .unwrap();
361        let data_element = sender_receiver_interface
362            .create_data_element("element", &data_type)
363            .unwrap();
364        let sr_port = ecu_composition_type
365            .create_r_port("test_port", &sender_receiver_interface)
366            .unwrap();
367
368        mapping
369            .map_sender_receiver_to_signal(&sys_signal, &data_element, &sr_port, &[], None)
370            .unwrap();
371    }
372}