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                    if root_composition_type == comp_type || root_composition_type.is_parent_of(&comp_type) {
76                        context_composition_prototypes.push(comp_proto.clone());
77                        current_composition = comp_type;
78                        break;
79                    }
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<(), 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<(), AutosarAbstractionError> {
135        // sanity checks
136        // the port must be a sender/receiver port
137        let 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        let sr_mapping = data_mappings.create_sub_element(ElementName::SenderReceiverToSignalMapping)?;
169
170        let iref = sr_mapping.create_sub_element(ElementName::DataElementIref)?;
171        iref.create_sub_element(ElementName::ContextPortRef)?
172            .set_reference_target(port_prototype.element())?;
173        iref.create_sub_element(ElementName::TargetDataPrototypeRef)?
174            .set_reference_target(data_element.element())?;
175
176        // the list of context components is ordered, with the root composition prototype at the beginning
177        for comp_proto in context_components {
178            iref.create_sub_element(ElementName::ContextComponentRef)?
179                .set_reference_target(comp_proto.element())?;
180        }
181
182        if let Some(root_composition_prototype) = root_composition_prototype {
183            iref.create_sub_element(ElementName::ContextCompositionRef)?
184                .set_reference_target(root_composition_prototype.element())?;
185        }
186
187        sr_mapping
188            .create_sub_element(ElementName::SystemSignalRef)?
189            .set_reference_target(signal.element())?;
190
191        Ok(())
192    }
193}
194
195//#########################################################
196
197/// A `SwcToEcuMapping` contains a mapping between a `SwComponentPrototype` and an `EcuInstance`
198#[derive(Debug, Clone, PartialEq, Eq, Hash)]
199pub struct SwcToEcuMapping(Element);
200abstraction_element!(SwcToEcuMapping, SwcToEcuMapping);
201impl IdentifiableAbstractionElement for SwcToEcuMapping {}
202
203impl SwcToEcuMapping {
204    pub(crate) fn new(
205        name: &str,
206        component_prototype: &SwComponentPrototype,
207        context_composition_prototypes: &[ComponentPrototype],
208        root_composition_prototype: &RootSwCompositionPrototype,
209        ecu: &EcuInstance,
210        mapping: &SystemMapping,
211    ) -> Result<Self, AutosarAbstractionError> {
212        let sw_mappings_elem = mapping.element().get_or_create_sub_element(ElementName::SwMappings)?;
213        let swc_to_ecu_mapping = sw_mappings_elem.create_named_sub_element(ElementName::SwcToEcuMapping, name)?;
214
215        let iref = swc_to_ecu_mapping
216            .create_sub_element(ElementName::ComponentIrefs)?
217            .create_sub_element(ElementName::ComponentIref)?;
218
219        // create the references to root composition and context compositions
220        iref.create_sub_element(ElementName::ContextCompositionRef)?
221            .set_reference_target(root_composition_prototype.element())?;
222        for context_comp in context_composition_prototypes {
223            iref.create_sub_element(ElementName::ContextComponentRef)?
224                .set_reference_target(context_comp.element())?;
225        }
226        // create the reference to the target component prototype
227        iref.create_sub_element(ElementName::TargetComponentRef)?
228            .set_reference_target(component_prototype.element())?;
229
230        swc_to_ecu_mapping
231            .create_sub_element(ElementName::EcuInstanceRef)?
232            .set_reference_target(ecu.element())?;
233
234        Ok(Self(swc_to_ecu_mapping))
235    }
236
237    /// get the component prototype that is mapped here
238    #[must_use]
239    pub fn target_component(&self) -> Option<SwComponentPrototype> {
240        self.element()
241            .get_sub_element(ElementName::ComponentIrefs)
242            .and_then(|irefs| irefs.get_sub_element(ElementName::ComponentIref))
243            .and_then(|iref| iref.get_sub_element(ElementName::TargetComponentRef))
244            .and_then(|target| target.get_reference_target().ok())
245            .and_then(|target| SwComponentPrototype::try_from(target).ok())
246    }
247
248    /// get the ECU instance which is the target of this mapping
249    #[must_use]
250    pub fn ecu_instance(&self) -> Option<EcuInstance> {
251        self.element()
252            .get_sub_element(ElementName::EcuInstanceRef)
253            .and_then(|r| r.get_reference_target().ok())
254            .and_then(|target| EcuInstance::try_from(target).ok())
255    }
256}
257
258//#########################################################
259
260#[cfg(test)]
261mod test {
262    use super::*;
263    use crate::{
264        AutosarModelAbstraction, SystemCategory,
265        datatype::{ApplicationPrimitiveCategory, ApplicationPrimitiveDataType},
266    };
267
268    #[test]
269    fn mappings() {
270        let model = AutosarModelAbstraction::create("filename", autosar_data::AutosarVersion::LATEST);
271        let package = model.get_or_create_package("/package").unwrap();
272        let system = package
273            .create_system("test_system", SystemCategory::EcuExtract)
274            .unwrap();
275        let mapping = system.get_or_create_mapping("test_mapping").unwrap();
276
277        let ecu = system.create_ecu_instance("test_ecu", &package).unwrap();
278        let root_composition_type = package.create_composition_sw_component_type("test_swc").unwrap();
279        let _root_composition = system
280            .set_root_sw_composition("test_root_composition", &root_composition_type)
281            .unwrap();
282
283        let ecu_composition_type = package
284            .create_composition_sw_component_type("Ecu_A_Composition")
285            .unwrap();
286        let ecu_composition_prototype = root_composition_type
287            .create_component("Ecu_A_Composition_Prototype", &ecu_composition_type)
288            .unwrap();
289
290        // map ecu_composition_prototype to the ecu
291        let swc_to_ecu = mapping
292            .map_swc_to_ecu("test_swc_to_ecu", &ecu_composition_prototype, &ecu)
293            .unwrap();
294
295        assert_eq!(swc_to_ecu.target_component().unwrap(), ecu_composition_prototype);
296        assert_eq!(swc_to_ecu.ecu_instance().unwrap(), ecu);
297
298        // map a signal to a port
299        let sys_signal = package.create_system_signal("test_signal").unwrap();
300
301        let sender_receiver_interface = package
302            .create_sender_receiver_interface("SenderReceiverInterface")
303            .unwrap();
304        let data_type = ApplicationPrimitiveDataType::new(
305            "Primitive",
306            &package,
307            ApplicationPrimitiveCategory::Value,
308            None,
309            None,
310            None,
311        )
312        .unwrap();
313        let data_element = sender_receiver_interface
314            .create_data_element("element", &data_type)
315            .unwrap();
316        let sr_port = ecu_composition_type
317            .create_r_port("test_port", &sender_receiver_interface)
318            .unwrap();
319
320        mapping
321            .map_sender_receiver_to_signal(&sys_signal, &data_element, &sr_port, &[], None)
322            .unwrap();
323    }
324}