autosar_data_abstraction/ecu_configuration/values/
reference.rs

1use crate::ecu_configuration::{AbstractEcucReferenceDef, EcucAnyReferenceDef, EcucInstanceReferenceDef};
2use crate::{AbstractionElement, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element};
3use autosar_data::{Element, ElementName};
4
5//#########################################################
6
7/// An `EcucInstanceReferenceValue` provides the mechanism to reference an instance of a prototype
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct EcucInstanceReferenceValue(Element);
10abstraction_element!(EcucInstanceReferenceValue, EcucInstanceReferenceValue);
11
12impl EcucInstanceReferenceValue {
13    pub(crate) fn new(
14        parent: &Element,
15        definition: &EcucInstanceReferenceDef,
16        target_context: &[&Element],
17        target: &Element,
18    ) -> Result<Self, AutosarAbstractionError> {
19        let instance_ref_elem = parent.create_sub_element(ElementName::EcucInstanceReferenceValue)?;
20        let instance_ref = Self(instance_ref_elem);
21
22        instance_ref.set_definition(definition)?;
23        instance_ref.set_target(target_context, target)?;
24
25        Ok(instance_ref)
26    }
27
28    /// set the parameter definition reference
29    pub fn set_definition(&self, definition: &EcucInstanceReferenceDef) -> Result<(), AutosarAbstractionError> {
30        self.element()
31            .get_or_create_sub_element(ElementName::DefinitionRef)?
32            .set_reference_target(definition.element())?;
33
34        Ok(())
35    }
36
37    /// get the parameter definition
38    ///
39    /// This function returns the definition as an `EcucParameterDef` enum, which
40    /// could contain either an `EcucFloatParamDef` or an `EcucIntegerParamDef`.
41    /// If the definition is not loaded, use `definition_ref()` instead.
42    #[must_use]
43    pub fn definition(&self) -> Option<EcucInstanceReferenceDef> {
44        let definition_elem = self
45            .element()
46            .get_sub_element(ElementName::DefinitionRef)?
47            .get_reference_target()
48            .ok()?;
49        EcucInstanceReferenceDef::try_from(definition_elem).ok()
50    }
51
52    /// get the parameter definition reference as a string
53    ///
54    /// This function is an alternative to `definition()`; it is useful when the
55    /// referenced definition is not loaded and can't be resolved.
56    #[must_use]
57    pub fn definition_ref(&self) -> Option<String> {
58        self.element()
59            .get_sub_element(ElementName::DefinitionRef)?
60            .character_data()?
61            .string_value()
62    }
63
64    /// Set the target of the reference
65    ///
66    /// An instance reference targets a specific instance of a prototype. In order to uniquely identify the target,
67    /// the target context is required. The target context is a list of elements that are the parent elements of the
68    /// target element. The instance reference definition specifies which context elements are required.
69    pub fn set_target(&self, taget_context: &[&Element], target: &Element) -> Result<(), AutosarAbstractionError> {
70        // remove existing target elements
71        let _ = self.element().remove_sub_element_kind(ElementName::ValueIref);
72        // create the target context elements
73
74        let value_iref_elem = self.element().create_sub_element(ElementName::ValueIref)?;
75        for context_elem in taget_context {
76            value_iref_elem
77                .create_sub_element(ElementName::ContextElementRef)?
78                .set_reference_target(context_elem)?;
79        }
80        // create the target element
81        value_iref_elem
82            .create_sub_element(ElementName::TargetRef)?
83            .set_reference_target(target)?;
84
85        Ok(())
86    }
87
88    /// Get the target of the reference
89    ///
90    /// Returns the targt element of the instance reference, as well as the context elements that are needed to uniquely
91    /// identify the target.
92    #[must_use]
93    pub fn target(&self) -> Option<(Vec<Element>, Element)> {
94        let value_iref_elem = self.element().get_sub_element(ElementName::ValueIref)?;
95        let target = value_iref_elem
96            .get_sub_element(ElementName::TargetRef)?
97            .get_reference_target()
98            .ok()?;
99
100        let context_elements: Vec<_> = value_iref_elem
101            .sub_elements()
102            .filter(|elem| elem.element_name() == ElementName::ContextElementRef)
103            .filter_map(|context_ref| context_ref.get_reference_target().ok())
104            .collect();
105
106        Some((context_elements, target))
107    }
108
109    /// set the index of the reference
110    ///
111    /// If the reference definition has `requiresIndex` set to `true`, then the reference
112    /// must have an index. Otherwise the index is meaningless.
113    pub fn set_index(&self, index: Option<u64>) -> Result<(), AutosarAbstractionError> {
114        if let Some(index) = index {
115            self.element()
116                .get_or_create_sub_element(ElementName::Index)?
117                .set_character_data(index)?;
118        } else {
119            let _ = self.element().remove_sub_element_kind(ElementName::Index);
120        }
121
122        Ok(())
123    }
124
125    /// get the index of the reference
126    ///
127    /// If the reference definition has `requiresIndex` set to `true`, then the reference
128    /// must have an index. Otherwise the index is meaningless.
129    #[must_use]
130    pub fn index(&self) -> Option<u64> {
131        self.element()
132            .get_sub_element(ElementName::Index)?
133            .character_data()?
134            .parse_integer()
135    }
136
137    /// set the isAutoValue flag
138    ///
139    /// If the reference definition has `withAuto` set to `true`, then the reference is allowed to have an auto value.
140    pub fn set_is_auto_value(&self, is_auto_value: Option<bool>) -> Result<(), AutosarAbstractionError> {
141        if let Some(is_auto_value) = is_auto_value {
142            self.element()
143                .get_or_create_sub_element(ElementName::IsAutoValue)?
144                .set_character_data(is_auto_value)?;
145        } else {
146            let _ = self.element().remove_sub_element_kind(ElementName::IsAutoValue);
147        }
148
149        Ok(())
150    }
151
152    /// get the isAutoValue flag
153    #[must_use]
154    pub fn is_auto_value(&self) -> Option<bool> {
155        self.element()
156            .get_sub_element(ElementName::IsAutoValue)?
157            .character_data()?
158            .parse_bool()
159    }
160}
161
162//#########################################################
163
164/// An `EcucReferenceValue` allows the ecu tonfiguration to refer to any identifiable element in the Autosar model
165#[derive(Debug, Clone, PartialEq, Eq, Hash)]
166pub struct EcucReferenceValue(Element);
167abstraction_element!(EcucReferenceValue, EcucReferenceValue);
168
169impl EcucReferenceValue {
170    pub(crate) fn new<T: AbstractEcucReferenceDef>(
171        parent: &Element,
172        definition: &T,
173        target: &Element,
174    ) -> Result<Self, AutosarAbstractionError> {
175        let reference_elem = parent.create_sub_element(ElementName::EcucReferenceValue)?;
176        let reference = Self(reference_elem);
177
178        reference.set_definition(definition)?;
179        reference.set_target(target)?;
180
181        Ok(reference)
182    }
183
184    /// set the parameter definition reference
185    pub fn set_definition<T: AbstractEcucReferenceDef>(&self, definition: &T) -> Result<(), AutosarAbstractionError> {
186        self.element()
187            .get_or_create_sub_element(ElementName::DefinitionRef)?
188            .set_reference_target(definition.element())?;
189
190        Ok(())
191    }
192
193    /// get the reference definition
194    ///
195    /// This function returns the definition as an `EcucParameterDef` enum, which
196    /// could contain either an `EcucFloatParamDef` or an `EcucIntegerParamDef`.
197    /// If the definition is not loaded, use `definition_ref()` instead.
198    #[must_use]
199    pub fn definition(&self) -> Option<EcucAnyReferenceDef> {
200        let definition_elem = self
201            .element()
202            .get_sub_element(ElementName::DefinitionRef)?
203            .get_reference_target()
204            .ok()?;
205        EcucAnyReferenceDef::try_from(definition_elem).ok()
206    }
207
208    /// get the referenced definition ref as a string
209    ///
210    /// This function is an alternative to `definition()`; it is useful when the
211    /// referenced definition is not loaded and can't be resolved.
212    #[must_use]
213    pub fn definition_ref(&self) -> Option<String> {
214        self.element()
215            .get_sub_element(ElementName::DefinitionRef)?
216            .character_data()?
217            .string_value()
218    }
219
220    /// Set the target of the reference
221    pub fn set_target(&self, target: &Element) -> Result<(), AutosarAbstractionError> {
222        self.element()
223            .get_or_create_sub_element(ElementName::ValueRef)?
224            .set_reference_target(target)?;
225
226        Ok(())
227    }
228
229    /// Get the target of the reference
230    #[must_use]
231    pub fn target(&self) -> Option<Element> {
232        self.element()
233            .get_sub_element(ElementName::ValueRef)?
234            .get_reference_target()
235            .ok()
236    }
237
238    /// set the index of the reference
239    ///
240    /// If the reference definition has `requiresIndex` set to `true`, then the reference
241    /// must have an index. Otherwise the index is meaningless.
242    pub fn set_index(&self, index: Option<u64>) -> Result<(), AutosarAbstractionError> {
243        if let Some(index) = index {
244            self.element()
245                .get_or_create_sub_element(ElementName::Index)?
246                .set_character_data(index)?;
247        } else {
248            let _ = self.element().remove_sub_element_kind(ElementName::Index);
249        }
250
251        Ok(())
252    }
253
254    /// get the index of the reference
255    ///
256    /// If the reference definition has `requiresIndex` set to `true`, then the reference
257    /// must have an index. Otherwise the index is meaningless.
258    #[must_use]
259    pub fn index(&self) -> Option<u64> {
260        self.element()
261            .get_sub_element(ElementName::Index)?
262            .character_data()?
263            .parse_integer()
264    }
265
266    /// set the isAutoValue flag
267    ///
268    /// If the reference definition has `withAuto` set to `true`, then the reference is allowed to have an auto value.
269    pub fn set_is_auto_value(&self, is_auto_value: Option<bool>) -> Result<(), AutosarAbstractionError> {
270        if let Some(is_auto_value) = is_auto_value {
271            self.element()
272                .get_or_create_sub_element(ElementName::IsAutoValue)?
273                .set_character_data(is_auto_value)?;
274        } else {
275            let _ = self.element().remove_sub_element_kind(ElementName::IsAutoValue);
276        }
277
278        Ok(())
279    }
280
281    /// get the isAutoValue flag
282    #[must_use]
283    pub fn is_auto_value(&self) -> Option<bool> {
284        self.element()
285            .get_sub_element(ElementName::IsAutoValue)?
286            .character_data()?
287            .parse_bool()
288    }
289}
290
291//#########################################################
292
293/// The `EcucAnyReferenceValue` is an enum that can hold either of the reference value types
294/// It is used as a return type for the iterator of reference values
295#[derive(Debug, Clone, PartialEq, Eq, Hash)]
296pub enum EcucAnyReferenceValue {
297    /// An instance reference value
298    Instance(EcucInstanceReferenceValue),
299    /// A normal reference value
300    Reference(EcucReferenceValue),
301}
302
303impl TryFrom<Element> for EcucAnyReferenceValue {
304    type Error = AutosarAbstractionError;
305
306    fn try_from(element: Element) -> Result<Self, Self::Error> {
307        match element.element_name() {
308            ElementName::EcucInstanceReferenceValue => {
309                Ok(EcucAnyReferenceValue::Instance(EcucInstanceReferenceValue(element)))
310            }
311            ElementName::EcucReferenceValue => Ok(EcucAnyReferenceValue::Reference(EcucReferenceValue(element))),
312            _ => Err(AutosarAbstractionError::ConversionError {
313                element,
314                dest: "EcucAnyReferenceValue".to_string(),
315            }),
316        }
317    }
318}
319
320impl AbstractionElement for EcucAnyReferenceValue {
321    fn element(&self) -> &Element {
322        match self {
323            EcucAnyReferenceValue::Instance(instance) => instance.element(),
324            EcucAnyReferenceValue::Reference(reference) => reference.element(),
325        }
326    }
327}
328
329impl IdentifiableAbstractionElement for EcucAnyReferenceValue {}
330
331//#########################################################
332
333#[cfg(test)]
334mod test {
335    use crate::{
336        AbstractionElement, AutosarModelAbstraction, ecu_configuration::EcucAnyReferenceValue,
337        software_component::AbstractSwComponentType,
338    };
339    use autosar_data::AutosarVersion;
340
341    #[test]
342    fn test_ecu_configuration_values() {
343        let definition_model = AutosarModelAbstraction::create("definition.arxml", AutosarVersion::LATEST);
344        let def_package = definition_model.get_or_create_package("/def_package").unwrap();
345
346        let values_model = AutosarModelAbstraction::create("values.arxml", AutosarVersion::LATEST);
347        let val_package = values_model.get_or_create_package("/val_package").unwrap();
348
349        // create a definition for the ECU configuration
350        let module_def = def_package.create_ecuc_module_def("ModuleDef").unwrap();
351        let container_def = module_def.create_param_conf_container_def("ContainerDef").unwrap();
352        let instance_ref_def = container_def
353            .create_instance_reference_def("InstanceRefDef", "origin")
354            .unwrap();
355        let foreign_reference_def = container_def
356            .create_foreign_reference_def("ForeignRefDef", "origin")
357            .unwrap();
358        let choice_reference_def = container_def
359            .create_choice_reference_def("ChoiceRefDef", "origin")
360            .unwrap();
361        let reference_def = container_def.create_reference_def("RefDef", "origin").unwrap();
362        let uri_reference_def = container_def.create_uri_reference_def("UriRefDef", "origin").unwrap();
363
364        // create an ecu configuration based on the definition model
365        let ecuc_config_values = val_package
366            .create_ecuc_module_configuration_values("Module", &module_def)
367            .unwrap();
368        let container_values = ecuc_config_values
369            .create_container_value("Container", &container_def)
370            .unwrap();
371
372        // create an instance reference value
373        // an InstanceReferenceValue can only refer to certain elements; in oder to be able to set up a valid
374        // instance reference we need to create the "infrastructure" in the model
375        let comp = val_package.create_composition_sw_component_type("comp").unwrap();
376        let port_interface = val_package.create_sender_receiver_interface("sr_interface").unwrap();
377        let r_port_prototype = comp.create_r_port("sr_r_port", &port_interface).unwrap();
378        // create the instance reference value
379        let instance_ref = container_values
380            .create_instance_reference(
381                &instance_ref_def,
382                &[r_port_prototype.element()],
383                r_port_prototype.element(),
384            )
385            .unwrap();
386        // the definition is in a different model, so it can't be resolved
387        assert!(instance_ref.definition().is_none());
388        assert_eq!(instance_ref.definition_ref(), instance_ref_def.element().path().ok());
389        let (target_context, target) = instance_ref.target().unwrap();
390        assert_eq!(&target, r_port_prototype.element());
391        assert_eq!(target_context, &[r_port_prototype.element().clone()]);
392        assert_eq!(instance_ref.index(), None);
393        instance_ref.set_index(Some(42)).unwrap();
394        assert_eq!(instance_ref.index(), Some(42));
395        assert_eq!(instance_ref.is_auto_value(), None);
396        instance_ref.set_is_auto_value(Some(true)).unwrap();
397        assert_eq!(instance_ref.is_auto_value(), Some(true));
398
399        let foreign_ref = container_values
400            .create_reference_value(&foreign_reference_def, val_package.element())
401            .unwrap();
402        assert!(foreign_ref.definition().is_none());
403        assert_eq!(
404            foreign_ref.definition_ref(),
405            foreign_reference_def.element().path().ok()
406        );
407        assert_eq!(&foreign_ref.target().unwrap(), val_package.element());
408        assert_eq!(foreign_ref.index(), None);
409        foreign_ref.set_index(Some(42)).unwrap();
410        assert_eq!(foreign_ref.index(), Some(42));
411        assert_eq!(foreign_ref.is_auto_value(), None);
412        foreign_ref.set_is_auto_value(Some(true)).unwrap();
413        assert_eq!(foreign_ref.is_auto_value(), Some(true));
414
415        let choice_ref = container_values
416            .create_reference_value(&choice_reference_def, val_package.element())
417            .unwrap();
418        assert!(choice_ref.definition().is_none());
419        assert_eq!(choice_ref.definition_ref(), choice_reference_def.element().path().ok());
420        assert_eq!(&choice_ref.target().unwrap(), val_package.element());
421        assert_eq!(choice_ref.index(), None);
422        choice_ref.set_index(Some(42)).unwrap();
423        assert_eq!(choice_ref.index(), Some(42));
424        assert_eq!(choice_ref.is_auto_value(), None);
425        choice_ref.set_is_auto_value(Some(true)).unwrap();
426        assert_eq!(choice_ref.is_auto_value(), Some(true));
427
428        let ref_ref = container_values
429            .create_reference_value(&reference_def, val_package.element())
430            .unwrap();
431        assert!(ref_ref.definition().is_none());
432        assert_eq!(ref_ref.definition_ref(), reference_def.element().path().ok());
433        assert_eq!(&ref_ref.target().unwrap(), val_package.element());
434        assert_eq!(ref_ref.index(), None);
435        ref_ref.set_index(Some(42)).unwrap();
436        assert_eq!(ref_ref.index(), Some(42));
437        assert_eq!(ref_ref.is_auto_value(), None);
438        ref_ref.set_is_auto_value(Some(true)).unwrap();
439        assert_eq!(ref_ref.is_auto_value(), Some(true));
440
441        let uri_ref = container_values
442            .create_reference_value(&uri_reference_def, val_package.element())
443            .unwrap();
444        assert!(uri_ref.definition().is_none());
445        assert_eq!(uri_ref.definition_ref(), uri_reference_def.element().path().ok());
446        assert_eq!(&uri_ref.target().unwrap(), val_package.element());
447        assert_eq!(uri_ref.index(), None);
448        uri_ref.set_index(Some(42)).unwrap();
449        assert_eq!(uri_ref.index(), Some(42));
450        assert_eq!(uri_ref.is_auto_value(), None);
451        uri_ref.set_is_auto_value(Some(true)).unwrap();
452        assert_eq!(uri_ref.is_auto_value(), Some(true));
453
454        assert_eq!(container_values.reference_values().count(), 5);
455
456        let any_ref = EcucAnyReferenceValue::try_from(instance_ref.element().clone()).unwrap();
457        assert!(matches!(any_ref, EcucAnyReferenceValue::Instance(_)));
458        assert_eq!(any_ref.element(), instance_ref.element());
459        let any_ref = EcucAnyReferenceValue::try_from(foreign_ref.element().clone()).unwrap();
460        assert!(matches!(any_ref, EcucAnyReferenceValue::Reference(_)));
461        assert_eq!(any_ref.element(), foreign_ref.element());
462        let err = EcucAnyReferenceValue::try_from(val_package.element().clone());
463        assert!(err.is_err());
464    }
465}