autosar_data_abstraction/communication/
data_transformation.rs

1use crate::{
2    AbstractionElement, ArPackage, AutosarAbstractionError, ByteOrder, IdentifiableAbstractionElement,
3    abstraction_element,
4};
5use autosar_data::{AutosarVersion, Element, ElementName, EnumItem};
6
7/// A [`DataTransformationSet`] contains `DataTransformation`s and `TransformationTechnology`s used in communication
8///
9/// Use [`ArPackage::create_data_transformation_set`] to create a new `DataTransformationSet`
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct DataTransformationSet(Element);
12abstraction_element!(DataTransformationSet, DataTransformationSet);
13impl IdentifiableAbstractionElement for DataTransformationSet {}
14
15impl DataTransformationSet {
16    /// Create a new `DataTransformationSet`
17    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
18        let elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
19        let transformation_set = elements.create_named_sub_element(ElementName::DataTransformationSet, name)?;
20
21        Ok(Self(transformation_set))
22    }
23
24    /// Create a new `DataTransformation` in the `DataTransformationSet`
25    pub fn create_data_transformation(
26        &self,
27        name: &str,
28        transformations: &[&TransformationTechnology],
29        execute_despite_data_unavailability: bool,
30    ) -> Result<DataTransformation, AutosarAbstractionError> {
31        let data_transformations = self
32            .element()
33            .get_or_create_sub_element(ElementName::DataTransformations)?;
34        let transformation = DataTransformation::new(
35            &data_transformations,
36            name,
37            transformations,
38            execute_despite_data_unavailability,
39        )?;
40        Ok(transformation)
41    }
42
43    /// Iterate over all `DataTransformation`s in the `DataTransformationSet`
44    pub fn data_transformations(&self) -> impl Iterator<Item = DataTransformation> + Send + 'static {
45        self.element()
46            .get_sub_element(ElementName::DataTransformations)
47            .into_iter()
48            .flat_map(|container| container.sub_elements())
49            .filter_map(|elem| elem.try_into().ok())
50    }
51
52    /// Create a new `TransformationTechnology` in the `DataTransformationSet`
53    pub fn create_transformation_technology(
54        &self,
55        name: &str,
56        config: &TransformationTechnologyConfig,
57    ) -> Result<TransformationTechnology, AutosarAbstractionError> {
58        let transtechs = self
59            .element()
60            .get_or_create_sub_element(ElementName::TransformationTechnologys)?;
61        TransformationTechnology::new(&transtechs, name, config)
62    }
63
64    /// Iterate over all `TransformationTechnology`s in the `DataTransformationSet`
65    pub fn transformation_technologies(&self) -> impl Iterator<Item = TransformationTechnology> + Send + 'static {
66        self.element()
67            .get_sub_element(ElementName::TransformationTechnologys)
68            .into_iter()
69            .flat_map(|container| container.sub_elements())
70            .filter_map(|elem| elem.try_into().ok())
71    }
72}
73
74//#########################################################
75
76/// A `DataTransformation` is a chain of `TransformationTechnology`s that are used to transform data
77#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub struct DataTransformation(Element);
79abstraction_element!(DataTransformation, DataTransformation);
80impl IdentifiableAbstractionElement for DataTransformation {}
81
82impl DataTransformation {
83    /// Create a new `DataTransformation`
84    fn new(
85        parent: &Element,
86        name: &str,
87        transformations: &[&TransformationTechnology],
88        execute_despite_data_unavailability: bool,
89    ) -> Result<Self, AutosarAbstractionError> {
90        // an empty chain is not allowed
91        if transformations.is_empty() {
92            return Err(AutosarAbstractionError::InvalidParameter(
93                "A DataTransformation must contain at least one TransformationTechnology".to_string(),
94            ));
95        }
96
97        // only the first transformation in a chain may have TransformerClass 'Serializer'
98        for transformation in &transformations[1..] {
99            if transformation.transformer_class() == Some(EnumItem::Serializer) {
100                return Err(AutosarAbstractionError::InvalidParameter(
101                    "A DataTransformation may only contain a TransformationTechnology with TransformerClass 'Serializer' at the start of the chain".to_string(),
102                ));
103            }
104        }
105
106        // every transformation in the chain must be part of the same DataTransformationSet as the DataTransformation
107        let dts = parent
108            .named_parent()?
109            .and_then(|dts| DataTransformationSet::try_from(dts).ok());
110        if !transformations
111            .iter()
112            .all(|ttech| ttech.data_transformation_set() == dts)
113        {
114            return Err(AutosarAbstractionError::InvalidParameter(
115                "All TransformationTechnologies in a DataTransformation must be part of the same DataTransformationSet"
116                    .to_string(),
117            ));
118        }
119
120        // if any of the transformations is an E2E transformation, then executeDespiteDataUnavailability must be true
121        if transformations
122            .iter()
123            .any(|ttech| ttech.protocol().as_deref() == Some("E2E"))
124            && !execute_despite_data_unavailability
125        {
126            return Err(AutosarAbstractionError::InvalidParameter(
127                "If a DataTransformation contains an E2E transformation, executeDespiteDataUnavailability must be true"
128                    .to_string(),
129            ));
130        }
131
132        let transformation = parent.create_named_sub_element(ElementName::DataTransformation, name)?;
133        transformation
134            .create_sub_element(ElementName::ExecuteDespiteDataUnavailability)?
135            .set_character_data(execute_despite_data_unavailability)?;
136        let chain_refs = transformation.create_sub_element(ElementName::TransformerChainRefs)?;
137        for transformation in transformations {
138            chain_refs
139                .create_sub_element(ElementName::TransformerChainRef)?
140                .set_reference_target(transformation.element())?;
141        }
142
143        Ok(Self(transformation))
144    }
145
146    /// get the `DataTransformationSet` that contains this `DataTransformation`
147    #[must_use]
148    pub fn data_transformation_set(&self) -> Option<DataTransformationSet> {
149        self.element()
150            .named_parent()
151            .ok()?
152            .and_then(|dts| DataTransformationSet::try_from(dts).ok())
153    }
154
155    /// Create an iterator over the `TransformationTechnologies` in the `DataTransformation`
156    ///
157    /// # Example
158    ///
159    /// ```
160    /// # use autosar_data::*;
161    /// # use autosar_data_abstraction::{*, communication::*};
162    /// # fn main() -> Result<(), AutosarAbstractionError> {
163    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
164    /// # let package = model.get_or_create_package("/pkg")?;
165    /// let dts = package.create_data_transformation_set("dts")?;
166    /// let config = TransformationTechnologyConfig::Com(ComTransformationTechnologyConfig { isignal_ipdu_length: 8 });
167    /// let ttech = dts.create_transformation_technology("ttech1", &config)?;
168    /// let dt = dts.create_data_transformation("dt", &[&ttech], true)?;
169    /// let mut ttech_iter = dt.transformation_technologies();
170    /// assert_eq!(ttech_iter.next(), Some(ttech));
171    /// # Ok(())}
172    /// ```
173    pub fn transformation_technologies(&self) -> impl Iterator<Item = TransformationTechnology> + Send + 'static {
174        self.0
175            .get_sub_element(ElementName::TransformerChainRefs)
176            .into_iter()
177            .flat_map(|container| container.sub_elements())
178            .filter_map(|elem| {
179                elem.get_reference_target()
180                    .ok()
181                    .and_then(|ttech| TransformationTechnology::try_from(ttech).ok())
182            })
183    }
184}
185
186//#########################################################
187
188/// A `TransformationTechnology` describes how to transform signal or PDU data
189#[derive(Debug, Clone, PartialEq, Eq, Hash)]
190pub struct TransformationTechnology(Element);
191abstraction_element!(TransformationTechnology, TransformationTechnology);
192impl IdentifiableAbstractionElement for TransformationTechnology {}
193
194impl TransformationTechnology {
195    /// Create a new `TransformationTechnology`
196    fn new(
197        parent: &Element,
198        name: &str,
199        config: &TransformationTechnologyConfig,
200    ) -> Result<Self, AutosarAbstractionError> {
201        let ttech_elem = parent.create_named_sub_element(ElementName::TransformationTechnology, name)?;
202        let ttech = Self(ttech_elem);
203        ttech.set_config(config)?;
204
205        Ok(ttech)
206    }
207
208    /// Set the configuration of the `TransformationTechnology`
209    pub fn set_config(&self, config: &TransformationTechnologyConfig) -> Result<(), AutosarAbstractionError> {
210        let ttech = self.element();
211        let version = ttech.min_version()?;
212        let buffer_props = ttech.get_or_create_sub_element(ElementName::BufferProperties)?;
213
214        match config {
215            TransformationTechnologyConfig::Generic(generic_config) => {
216                ttech
217                    .get_or_create_sub_element(ElementName::Protocol)?
218                    .set_character_data(generic_config.protocol_name.as_str())?;
219                ttech
220                    .get_or_create_sub_element(ElementName::Version)?
221                    .set_character_data(generic_config.protocol_version.as_str())?;
222                ttech
223                    .get_or_create_sub_element(ElementName::TransformerClass)?
224                    .set_character_data(EnumItem::Custom)?;
225                buffer_props
226                    .get_or_create_sub_element(ElementName::HeaderLength)?
227                    .set_character_data(u64::from(generic_config.header_length))?;
228                buffer_props
229                    .get_or_create_sub_element(ElementName::InPlace)?
230                    .set_character_data(generic_config.in_place)?;
231
232                // remove the transformation descriptions, which are only used by E2E and SOMEIP
233                let _ = ttech.remove_sub_element_kind(ElementName::TransformationDescriptions);
234                // remove the buffer computation, which is only used by COM
235                let _ = buffer_props.remove_sub_element_kind(ElementName::BufferComputation);
236                // remove the NeedsOriginalData setting, which is only used by E2E
237                let _ = ttech.remove_sub_element_kind(ElementName::NeedsOriginalData);
238            }
239            TransformationTechnologyConfig::Com(com_config) => {
240                ttech
241                    .get_or_create_sub_element(ElementName::Protocol)?
242                    .set_character_data("COMBased")?;
243                ttech
244                    .get_or_create_sub_element(ElementName::Version)?
245                    .set_character_data("1")?;
246                ttech
247                    .get_or_create_sub_element(ElementName::TransformerClass)?
248                    .set_character_data(EnumItem::Serializer)?;
249
250                // comxf does not have a header
251                buffer_props
252                    .get_or_create_sub_element(ElementName::HeaderLength)?
253                    .set_character_data(0)?;
254                // comxf is always the first transformer in a chain, and the first transformer is not allowed to be in place
255                buffer_props
256                    .get_or_create_sub_element(ElementName::InPlace)?
257                    .set_character_data("false")?;
258
259                if version <= AutosarVersion::Autosar_00049 {
260                    let _ = buffer_props.remove_sub_element_kind(ElementName::BufferComputation);
261                    // only in versions up to AUTOSAR R20-11 (AUTOSAR_00049): a COM transformer must have a BUFFER-COMPUTATION
262                    let bufcomp_compu = buffer_props
263                        .create_sub_element(ElementName::BufferComputation)?
264                        .create_sub_element(ElementName::CompuRationalCoeffs)?;
265                    let numerator = bufcomp_compu.create_sub_element(ElementName::CompuNumerator)?;
266                    numerator
267                        .create_sub_element(ElementName::V)?
268                        .set_character_data(u64::from(com_config.isignal_ipdu_length))?;
269                    numerator.create_sub_element(ElementName::V)?.set_character_data(1)?;
270                    bufcomp_compu
271                        .create_sub_element(ElementName::CompuDenominator)?
272                        .create_sub_element(ElementName::V)?
273                        .set_character_data(1)?;
274                }
275
276                // remove the transformation descriptions, which are only used by E2E and SOMEIP
277                let _ = ttech.remove_sub_element_kind(ElementName::TransformationDescriptions);
278                // remove the NeedsOriginalData setting, which is only used by E2E
279                let _ = ttech.remove_sub_element_kind(ElementName::NeedsOriginalData);
280            }
281            TransformationTechnologyConfig::E2E(e2e_config) => {
282                ttech
283                    .get_or_create_sub_element(ElementName::Protocol)?
284                    .set_character_data("E2E")?;
285                ttech
286                    .get_or_create_sub_element(ElementName::Version)?
287                    .set_character_data("1.0.0")?;
288                ttech
289                    .get_or_create_sub_element(ElementName::TransformerClass)?
290                    .set_character_data(EnumItem::Safety)?;
291                if version >= AutosarVersion::Autosar_4_3_0 {
292                    ttech
293                        .get_or_create_sub_element(ElementName::HasInternalState)?
294                        .set_character_data("true")?;
295                }
296                ttech
297                    .get_or_create_sub_element(ElementName::NeedsOriginalData)?
298                    .set_character_data("false")?;
299
300                // select the profile name and header length based on the chosen E2E profile
301                let (profile_name, header_length) = match e2e_config.profile {
302                    E2EProfile::P01 => ("PROFILE_01", 16),
303                    E2EProfile::P02 => ("PROFILE_02", 16),
304                    E2EProfile::P04 => ("PROFILE_04", 96),
305                    E2EProfile::P04m => ("PROFILE_04m", 128),
306                    E2EProfile::P05 => ("PROFILE_05", 24),
307                    E2EProfile::P06 => ("PROFILE_06", 40),
308                    E2EProfile::P07 => ("PROFILE_07", 160),
309                    E2EProfile::P07m => ("PROFILE_07m", 192),
310                    E2EProfile::P08 => ("PROFILE_08", 128),
311                    E2EProfile::P08m => ("PROFILE_08m", 160),
312                    E2EProfile::P11 => ("PROFILE_11", 16),
313                    E2EProfile::P22 => ("PROFILE_22", 16),
314                    E2EProfile::P44 => ("PROFILE_44", 96),
315                    E2EProfile::P44m => ("PROFILE_44m", 128),
316                };
317
318                // when E2E is used in a transformer chain after COM, the header length must be zero
319                // since we don#t know how this transformer will be used, the user must set zero_header_length to true if the header length should be zero
320                let real_header_length = if e2e_config.zero_header_length {
321                    0u32
322                } else {
323                    header_length
324                };
325
326                buffer_props
327                    .get_or_create_sub_element(ElementName::HeaderLength)?
328                    .set_character_data(u64::from(real_header_length))?;
329                buffer_props
330                    .get_or_create_sub_element(ElementName::InPlace)?
331                    .set_character_data(e2e_config.transform_in_place)?;
332
333                let trans_desc = ttech.get_or_create_sub_element(ElementName::TransformationDescriptions)?;
334                let _ = trans_desc.remove_sub_element_kind(ElementName::SomeipTransformationDescription);
335                let e2e_desc = trans_desc.get_or_create_sub_element(ElementName::EndToEndTransformationDescription)?;
336
337                // create the E2E profile description, with the mandatory fields
338                e2e_desc
339                    .get_or_create_sub_element(ElementName::ProfileName)?
340                    .set_character_data(profile_name)?;
341                e2e_desc
342                    .get_or_create_sub_element(ElementName::UpperHeaderBitsToShift)?
343                    .set_character_data(u64::from(e2e_config.offset))?;
344                e2e_desc
345                    .get_or_create_sub_element(ElementName::MaxDeltaCounter)?
346                    .set_character_data(u64::from(e2e_config.max_delta_counter))?;
347                e2e_desc
348                    .get_or_create_sub_element(ElementName::MaxErrorStateInit)?
349                    .set_character_data(u64::from(e2e_config.max_error_state_init))?;
350                e2e_desc
351                    .get_or_create_sub_element(ElementName::MaxErrorStateInvalid)?
352                    .set_character_data(u64::from(e2e_config.max_error_state_invalid))?;
353                e2e_desc
354                    .get_or_create_sub_element(ElementName::MaxErrorStateValid)?
355                    .set_character_data(u64::from(e2e_config.max_error_state_valid))?;
356                e2e_desc
357                    .get_or_create_sub_element(ElementName::MaxNoNewOrRepeatedData)?
358                    .set_character_data(u64::from(e2e_config.max_no_new_or_repeated_data))?;
359                e2e_desc
360                    .get_or_create_sub_element(ElementName::MinOkStateInit)?
361                    .set_character_data(u64::from(e2e_config.min_ok_state_init))?;
362                e2e_desc
363                    .get_or_create_sub_element(ElementName::MinOkStateInvalid)?
364                    .set_character_data(u64::from(e2e_config.min_ok_state_invalid))?;
365                e2e_desc
366                    .get_or_create_sub_element(ElementName::MinOkStateValid)?
367                    .set_character_data(u64::from(e2e_config.min_ok_state_valid))?;
368
369                // window size is one value in AUTOSAR 4.4.0 (AUTOSAR_00047) and older, and three values in AUTOSAR 4.5.0 (AUTOSAR_00048) and newer
370                if version <= AutosarVersion::Autosar_00047 {
371                    // window size is only valid in AUTOSAR 4.4.0 (AUTOSAR_00047) and older
372                    e2e_desc
373                        .get_or_create_sub_element(ElementName::WindowSize)?
374                        .set_character_data(u64::from(e2e_config.window_size))?;
375                } else {
376                    // new (Autosar 4.5.0+): window size can be set for each state
377                    e2e_desc
378                        .get_or_create_sub_element(ElementName::WindowSizeInit)?
379                        .set_character_data(u64::from(e2e_config.window_size_init.unwrap_or(e2e_config.window_size)))?;
380                    e2e_desc
381                        .get_or_create_sub_element(ElementName::WindowSizeInvalid)?
382                        .set_character_data(u64::from(
383                            e2e_config.window_size_invalid.unwrap_or(e2e_config.window_size),
384                        ))?;
385                    e2e_desc
386                        .get_or_create_sub_element(ElementName::WindowSizeValid)?
387                        .set_character_data(u64::from(
388                            e2e_config.window_size_valid.unwrap_or(e2e_config.window_size),
389                        ))?;
390                }
391
392                // special handling for E2E profiles 01 and 11
393                if matches!(e2e_config.profile, E2EProfile::P01 | E2EProfile::P11) {
394                    // data id mode
395                    let Some(data_id_mode) = e2e_config.data_id_mode else {
396                        return Err(AutosarAbstractionError::InvalidParameter(
397                            "Data ID mode is required for E2E profiles 01 and 11".to_string(),
398                        ));
399                    };
400                    e2e_desc
401                        .get_or_create_sub_element(ElementName::DataIdMode)?
402                        .set_character_data::<EnumItem>(data_id_mode.into())?;
403
404                    // counter offset
405                    let Some(counter_offset) = e2e_config.counter_offset else {
406                        return Err(AutosarAbstractionError::InvalidParameter(
407                            "Counter offset is required for E2E profiles 01 and 11".to_string(),
408                        ));
409                    };
410                    e2e_desc
411                        .get_or_create_sub_element(ElementName::CounterOffset)?
412                        .set_character_data(u64::from(counter_offset))?;
413
414                    // crc offset
415                    let Some(crc_offset) = e2e_config.crc_offset else {
416                        return Err(AutosarAbstractionError::InvalidParameter(
417                            "CRC offset is required for E2E profiles 01 and 11".to_string(),
418                        ));
419                    };
420                    e2e_desc
421                        .get_or_create_sub_element(ElementName::CrcOffset)?
422                        .set_character_data(u64::from(crc_offset))?;
423
424                    // data id nibble offset
425                    if data_id_mode == DataIdMode::Lower12Bit {
426                        let Some(data_id_nibble_offset) = e2e_config.data_id_nibble_offset else {
427                            return Err(AutosarAbstractionError::InvalidParameter(
428                                "Data ID nibble offset is required for E2E profiles 01 and 11 with DataIdMode::Lower12Bit".to_string(),
429                            ));
430                        };
431                        e2e_desc
432                            .get_or_create_sub_element(ElementName::DataIdNibbleOffset)?
433                            .set_character_data(u64::from(data_id_nibble_offset))?;
434                    }
435
436                    // offset may only be set if the profile is not 01 or 11
437                    let _ = e2e_desc.remove_sub_element_kind(ElementName::Offset);
438                } else {
439                    // offset may only be set if the profile is not 01 or 11
440                    e2e_desc
441                        .get_or_create_sub_element(ElementName::Offset)?
442                        .set_character_data(u64::from(e2e_config.offset))?;
443
444                    // remove the data id mode, counter offset, crc offset, and data id nibble offset, which are only used by profiles 01 and 11
445                    let _ = e2e_desc.remove_sub_element_kind(ElementName::DataIdMode);
446                    let _ = e2e_desc.remove_sub_element_kind(ElementName::CounterOffset);
447                    let _ = e2e_desc.remove_sub_element_kind(ElementName::CrcOffset);
448                    let _ = e2e_desc.remove_sub_element_kind(ElementName::DataIdNibbleOffset);
449                }
450
451                // optional fields
452                // profile behavior
453                if let Some(profile_behavior) = e2e_config.profile_behavior {
454                    e2e_desc
455                        .get_or_create_sub_element(ElementName::ProfileBehavior)?
456                        .set_character_data::<EnumItem>(profile_behavior.into())?;
457                }
458
459                // sync counter init
460                if let Some(sync_counter_init) = e2e_config.sync_counter_init {
461                    e2e_desc
462                        .get_or_create_sub_element(ElementName::SyncCounterInit)?
463                        .set_character_data(u64::from(sync_counter_init))?;
464                }
465
466                // remove the buffer computation, which is only used by COM
467                let _ = buffer_props.remove_sub_element_kind(ElementName::BufferComputation);
468            }
469            TransformationTechnologyConfig::SomeIp(someip_config) => {
470                ttech
471                    .get_or_create_sub_element(ElementName::Protocol)?
472                    .set_character_data("SOMEIP")?;
473                ttech
474                    .get_or_create_sub_element(ElementName::TransformerClass)?
475                    .set_character_data(EnumItem::Serializer)?;
476
477                // someip header length is always 64
478                buffer_props
479                    .get_or_create_sub_element(ElementName::HeaderLength)?
480                    .set_character_data(64)?;
481                // someip is always the first transformer in a chain, and the first transformer is not allowed to be in place
482                buffer_props
483                    .get_or_create_sub_element(ElementName::InPlace)?
484                    .set_character_data("false")?;
485
486                let trans_desc = ttech.get_or_create_sub_element(ElementName::TransformationDescriptions)?;
487                let _ = trans_desc.remove_sub_element_kind(ElementName::EndToEndTransformationDescription);
488                let someip_desc = trans_desc.get_or_create_sub_element(ElementName::SomeipTransformationDescription)?;
489                someip_desc
490                    .get_or_create_sub_element(ElementName::Alignment)?
491                    .set_character_data(u64::from(someip_config.alignment))?;
492                someip_desc
493                    .get_or_create_sub_element(ElementName::ByteOrder)?
494                    .set_character_data::<EnumItem>(someip_config.byte_order.into())?;
495                someip_desc
496                    .get_or_create_sub_element(ElementName::InterfaceVersion)?
497                    .set_character_data(u64::from(someip_config.interface_version))?;
498
499                // the someip transformer must currently always use version "1.0.0"
500                ttech
501                    .get_or_create_sub_element(ElementName::Version)?
502                    .set_character_data("1.0.0")?;
503
504                // remove the buffer computation, which is only used by COM
505                let _ = buffer_props.remove_sub_element_kind(ElementName::BufferComputation);
506                // remove the needs original data setting, which is only used by E2E
507                let _ = ttech.remove_sub_element_kind(ElementName::NeedsOriginalData);
508            }
509        }
510
511        Ok(())
512    }
513
514    /// Get the protocol of the `TransformationTechnology`
515    #[must_use]
516    pub fn protocol(&self) -> Option<String> {
517        self.element()
518            .get_sub_element(ElementName::Protocol)?
519            .character_data()?
520            .string_value()
521    }
522
523    /// Get the transformer class of the `TransformationTechnology`
524    #[must_use]
525    pub fn transformer_class(&self) -> Option<EnumItem> {
526        self.element()
527            .get_sub_element(ElementName::TransformerClass)?
528            .character_data()?
529            .enum_value()
530    }
531
532    /// get the `DataTransformationSet` that contains this `TransformationTechnology`
533    #[must_use]
534    pub fn data_transformation_set(&self) -> Option<DataTransformationSet> {
535        self.element()
536            .named_parent()
537            .ok()?
538            .and_then(|dts| DataTransformationSet::try_from(dts).ok())
539    }
540
541    /// Get the configuration of the `TransformationTechnology`
542    #[must_use]
543    pub fn config(&self) -> Option<TransformationTechnologyConfig> {
544        let protocol = self
545            .element()
546            .get_sub_element(ElementName::Protocol)?
547            .character_data()?
548            .string_value()?;
549
550        let opt_e2e_desc = self
551            .element()
552            .get_sub_element(ElementName::TransformationDescriptions)
553            .and_then(|tdesc| tdesc.get_sub_element(ElementName::EndToEndTransformationDescription));
554        let opt_someip_desc = self
555            .element()
556            .get_sub_element(ElementName::TransformationDescriptions)
557            .and_then(|tdesc| tdesc.get_sub_element(ElementName::SomeipTransformationDescription));
558
559        if let Some(e2e_desc) = opt_e2e_desc {
560            // E2E transformation
561            let profile_name = e2e_desc
562                .get_sub_element(ElementName::ProfileName)?
563                .character_data()?
564                .string_value()?;
565            let profile = match profile_name.as_str() {
566                "PROFILE_01" => E2EProfile::P01,
567                "PROFILE_02" => E2EProfile::P02,
568                "PROFILE_04" => E2EProfile::P04,
569                "PROFILE_04m" => E2EProfile::P04m,
570                "PROFILE_05" => E2EProfile::P05,
571                "PROFILE_06" => E2EProfile::P06,
572                "PROFILE_07" => E2EProfile::P07,
573                "PROFILE_07m" => E2EProfile::P07m,
574                "PROFILE_08" => E2EProfile::P08,
575                "PROFILE_08m" => E2EProfile::P08m,
576                "PROFILE_11" => E2EProfile::P11,
577                "PROFILE_22" => E2EProfile::P22,
578                "PROFILE_44" => E2EProfile::P44,
579                "PROFILE_44m" => E2EProfile::P44m,
580                _ => return None,
581            };
582
583            let buffer_props = self.element().get_sub_element(ElementName::BufferProperties)?;
584            let in_place = buffer_props
585                .get_sub_element(ElementName::InPlace)?
586                .character_data()?
587                .parse_bool()?;
588            let buffer_header_length = buffer_props
589                .get_sub_element(ElementName::HeaderLength)?
590                .character_data()?
591                .parse_integer::<u32>()?;
592
593            let opt_window_size_init = e2e_desc
594                .get_sub_element(ElementName::WindowSizeInit)
595                .and_then(|elem| elem.character_data())
596                .and_then(|cdata| cdata.parse_integer::<u32>());
597            let opt_window_size_invalid = e2e_desc
598                .get_sub_element(ElementName::WindowSizeInvalid)
599                .and_then(|elem| elem.character_data())
600                .and_then(|cdata| cdata.parse_integer::<u32>());
601            let opt_window_size_valid = e2e_desc
602                .get_sub_element(ElementName::WindowSizeValid)
603                .and_then(|elem| elem.character_data())
604                .and_then(|cdata| cdata.parse_integer::<u32>());
605            // window size is one value in AUTOSAR 4.4.0 (AUTOSAR_00047) and older, and three values in AUTOSAR 4.5.0 (AUTOSAR_00048) and newer
606            // when getting the config, first try to use the WINDOW-SIZE, then fall back to the three separate values
607            let opt_window_size = e2e_desc
608                .get_sub_element(ElementName::WindowSize)
609                .and_then(|elem| elem.character_data())
610                .and_then(|cdata| cdata.parse_integer())
611                .or(opt_window_size_init)
612                .or(opt_window_size_invalid)
613                .or(opt_window_size_valid);
614
615            let config = E2ETransformationTechnologyConfig {
616                profile,
617                zero_header_length: buffer_header_length == 0,
618                transform_in_place: in_place,
619                offset: 0,
620                max_delta_counter: e2e_desc
621                    .get_sub_element(ElementName::MaxDeltaCounter)?
622                    .character_data()?
623                    .parse_integer()?,
624                max_error_state_init: e2e_desc
625                    .get_sub_element(ElementName::MaxErrorStateInit)?
626                    .character_data()?
627                    .parse_integer()?,
628                max_error_state_invalid: e2e_desc
629                    .get_sub_element(ElementName::MaxErrorStateInvalid)?
630                    .character_data()?
631                    .parse_integer()?,
632                max_error_state_valid: e2e_desc
633                    .get_sub_element(ElementName::MaxErrorStateValid)?
634                    .character_data()?
635                    .parse_integer()?,
636                max_no_new_or_repeated_data: e2e_desc
637                    .get_sub_element(ElementName::MaxNoNewOrRepeatedData)?
638                    .character_data()?
639                    .parse_integer()?,
640                min_ok_state_init: e2e_desc
641                    .get_sub_element(ElementName::MinOkStateInit)?
642                    .character_data()?
643                    .parse_integer()?,
644                min_ok_state_invalid: e2e_desc
645                    .get_sub_element(ElementName::MinOkStateInvalid)?
646                    .character_data()?
647                    .parse_integer()?,
648                min_ok_state_valid: e2e_desc
649                    .get_sub_element(ElementName::MinOkStateValid)?
650                    .character_data()?
651                    .parse_integer()?,
652                window_size: opt_window_size?,
653                window_size_init: e2e_desc
654                    .get_sub_element(ElementName::WindowSizeInit)
655                    .and_then(|elem| elem.character_data())
656                    .and_then(|cd| cd.parse_integer()),
657                window_size_invalid: e2e_desc
658                    .get_sub_element(ElementName::WindowSizeInvalid)
659                    .and_then(|elem| elem.character_data())
660                    .and_then(|cd| cd.parse_integer()),
661                window_size_valid: e2e_desc
662                    .get_sub_element(ElementName::WindowSizeValid)
663                    .and_then(|elem| elem.character_data())
664                    .and_then(|cd| cd.parse_integer()),
665                profile_behavior: e2e_desc
666                    .get_sub_element(ElementName::ProfileBehavior)
667                    .and_then(|elem| elem.character_data())
668                    .and_then(|cd| cd.enum_value())
669                    .and_then(|enumitem| enumitem.try_into().ok()),
670                sync_counter_init: e2e_desc
671                    .get_sub_element(ElementName::SyncCounterInit)
672                    .and_then(|elem| elem.character_data())
673                    .and_then(|cd| cd.parse_integer()),
674                data_id_mode: e2e_desc
675                    .get_sub_element(ElementName::DataIdMode)
676                    .and_then(|elem| elem.character_data())
677                    .and_then(|cd| cd.enum_value())
678                    .and_then(|enumitem| enumitem.try_into().ok()),
679                data_id_nibble_offset: e2e_desc
680                    .get_sub_element(ElementName::DataIdNibbleOffset)
681                    .and_then(|elem| elem.character_data())
682                    .and_then(|cd| cd.parse_integer()),
683                crc_offset: e2e_desc
684                    .get_sub_element(ElementName::CrcOffset)
685                    .and_then(|elem| elem.character_data())
686                    .and_then(|cd| cd.parse_integer()),
687                counter_offset: e2e_desc
688                    .get_sub_element(ElementName::CounterOffset)
689                    .and_then(|elem| elem.character_data())
690                    .and_then(|cd| cd.parse_integer()),
691            };
692
693            Some(TransformationTechnologyConfig::E2E(config))
694        } else if let Some(someip_desc) = opt_someip_desc {
695            // SOMEIP transformation
696            let someip_config = SomeIpTransformationTechnologyConfig {
697                alignment: someip_desc
698                    .get_sub_element(ElementName::Alignment)?
699                    .character_data()?
700                    .parse_integer()?,
701                byte_order: someip_desc
702                    .get_sub_element(ElementName::ByteOrder)?
703                    .character_data()?
704                    .enum_value()
705                    .and_then(|enumitem| enumitem.try_into().ok())?,
706                interface_version: someip_desc
707                    .get_sub_element(ElementName::InterfaceVersion)?
708                    .character_data()?
709                    .parse_integer()?,
710            };
711            Some(TransformationTechnologyConfig::SomeIp(someip_config))
712        } else if protocol == "COMBased" || protocol == "ComBased" {
713            // COM transformation
714            let com_config = ComTransformationTechnologyConfig {
715                isignal_ipdu_length: self
716                    .element()
717                    .get_sub_element(ElementName::BufferProperties)?
718                    .get_sub_element(ElementName::BufferComputation)?
719                    .get_sub_element(ElementName::CompuRationalCoeffs)?
720                    .get_sub_element(ElementName::CompuNumerator)?
721                    .get_sub_element(ElementName::V)?
722                    .character_data()?
723                    .parse_integer()?,
724            };
725            Some(TransformationTechnologyConfig::Com(com_config))
726        } else {
727            // generic transformation
728            let buffer_props = self.element().get_sub_element(ElementName::BufferProperties)?;
729            let in_place = buffer_props
730                .get_sub_element(ElementName::InPlace)?
731                .character_data()?
732                .parse_bool()?;
733
734            let generic_config = GenericTransformationTechnologyConfig {
735                protocol_name: self
736                    .element()
737                    .get_sub_element(ElementName::Protocol)?
738                    .character_data()?
739                    .string_value()?,
740                protocol_version: self
741                    .element()
742                    .get_sub_element(ElementName::Version)?
743                    .character_data()?
744                    .string_value()?,
745                header_length: buffer_props
746                    .get_sub_element(ElementName::HeaderLength)?
747                    .character_data()?
748                    .parse_integer()?,
749                in_place,
750            };
751            Some(TransformationTechnologyConfig::Generic(generic_config))
752        }
753    }
754}
755
756//#########################################################
757
758/// The configuration of any kind of `TransformationTechnology`
759#[derive(Debug, Clone, PartialEq, Eq)]
760pub enum TransformationTechnologyConfig {
761    /// Configuration for a generic transformation technology
762    Generic(GenericTransformationTechnologyConfig),
763    /// Configuration for a COM transformation technology
764    Com(ComTransformationTechnologyConfig),
765    /// Configuration for an E2E transformation technology
766    E2E(E2ETransformationTechnologyConfig),
767    /// Configuration for a SOMEIP transformation technology
768    SomeIp(SomeIpTransformationTechnologyConfig),
769}
770
771//#########################################################
772
773/// Configuration for a generic transformation technology
774/// For a generic trasformation, the mandatory values must be chosen by the user
775#[derive(Debug, Clone, PartialEq, Eq)]
776pub struct GenericTransformationTechnologyConfig {
777    /// The name of the custom protocol
778    pub protocol_name: String,
779    /// The version of the custom protocol
780    pub protocol_version: String,
781    /// The length of the header in bits
782    pub header_length: u32,
783    /// Should the transformation take place in the existing buffer or in a separate buffer?
784    pub in_place: bool,
785}
786
787//#########################################################
788
789/// Configuration for a COM transformation
790#[derive(Debug, Clone, PartialEq, Eq)]
791pub struct ComTransformationTechnologyConfig {
792    /// The length of the `ISignalIpdu` tha will be transformed by this Com transformer.
793    /// The value is only used up to AUTOSAR R20-11 (`AUTOSAR_00049`), where it is needed to calculate the buffer size.
794    pub isignal_ipdu_length: u32,
795}
796
797//#########################################################
798
799/// Configuration for an E2E transformation
800#[derive(Debug, Clone, PartialEq, Eq)]
801pub struct E2ETransformationTechnologyConfig {
802    /// E2E profile to use
803    pub profile: E2EProfile,
804    /// When E2E is used in a transformer chain after COM, the header length must be zero.
805    /// In this configuration you are expected to provide space for the E2E data inside the signal group layout, and `zero_header_length` should be set to true.
806    /// If `zero_header_length` is set to false, the appropriate header length for the chosen E2E profile will be used (e.g. 24 bits for `PROFILE_05`)
807    pub zero_header_length: bool,
808    /// Should the E2E transformation take place in the existing buffer or in a separate buffer?
809    pub transform_in_place: bool,
810    /// The offset in bits from the start of the buffer where the E2E data should be placed
811    /// If E2E is used after COM, the offset should be 0; if E2E is used after SOMEIP, the offset should be 64
812    pub offset: u32,
813    /// Maximum jump in the counter value between two consecutive messages
814    pub max_delta_counter: u32,
815    /// The maximum allowed number of consecutive failed counter checks in the init state
816    pub max_error_state_init: u32,
817    /// The maximum allowed number of consecutive failed counter checks in the invalid state
818    pub max_error_state_invalid: u32,
819    /// The maximum allowed number of consecutive failed counter checks in the valid state
820    pub max_error_state_valid: u32,
821    /// The maximum allowed number of consecutive failed counter checks
822    pub max_no_new_or_repeated_data: u32,
823    /// The minimum allowed number of consecutive successful counter checks in the init state
824    pub min_ok_state_init: u32,
825    /// The minimum allowed number of consecutive successful counter checks in the invalid state
826    pub min_ok_state_invalid: u32,
827    /// The minimum allowed number of consecutive successful counter checks in the valid state
828    pub min_ok_state_valid: u32,
829    /// window size: Size of the monitoring window for the E2E state machine.
830    /// This can be directly set up to AUTOSAR 4.4.0 (`AUTOSAR_00047`).
831    /// For newer files this only provides the default if `window_size_init`, `window_size_invalid` and `window_size_valid` are not set
832    pub window_size: u32,
833    /// window size in the init state - only valid in AUTOSAR 4.5.0 (`AUTOSAR_00048`) and newer. if it is not set, this will default to `window_size`
834    pub window_size_init: Option<u32>,
835    /// window size in the invalid state - only valid in AUTOSAR 4.5.0 (`AUTOSAR_00048`) and newer. if it is not set, this will default to `window_size`
836    pub window_size_invalid: Option<u32>,
837    /// window size in the valid state - only valid in AUTOSAR 4.5.0 (`AUTOSAR_00048`) and newer. if it is not set, this will default to `window_size`
838    pub window_size_valid: Option<u32>,
839    /// Behavior of the check functionality
840    pub profile_behavior: Option<E2EProfileBehavior>,
841    /// Number of successful checks required for validating the consistency of the counter
842    pub sync_counter_init: Option<u32>,
843    /// The data ID mode to use; required for E2E profiles 01 and 11, unused otherwise
844    pub data_id_mode: Option<DataIdMode>,
845    /// Offset of the data ID in the Data[] array in bits. Required for E2E profiles 01 and 11 when `data_id_mode` is `Lower12Bit`, unused otherwise
846    pub data_id_nibble_offset: Option<u32>,
847    /// Offset of the crc in the Data[] array in bits. Required for E2E profiles 01 and 11, unused otherwise
848    pub crc_offset: Option<u32>,
849    /// Offset of the counter in the Data[] array in bits. Required for E2E profiles 01 and 11, unused otherwise
850    pub counter_offset: Option<u32>,
851}
852
853//#########################################################
854
855/// Configuration for a SOMEIP transformation
856#[derive(Debug, Clone, PartialEq, Eq)]
857pub struct SomeIpTransformationTechnologyConfig {
858    /// The alignment of the data in bits
859    pub alignment: u32,
860    /// The byte order of the data
861    pub byte_order: ByteOrder,
862    /// The interface version the SOME/IP transformer shall use.
863    pub interface_version: u32,
864}
865
866//#########################################################
867
868/// enumeration of the possible E2E profiles
869#[derive(Debug, Clone, Copy, PartialEq, Eq)]
870pub enum E2EProfile {
871    /// E2E Profile 01: Legacy profile, uses a 4-bit counter, 16-bit data id and an 8-bit CRC. New projects should use P11 instead.
872    P01,
873    /// E2E Profile 02: Legacy profile, uses a 8-bit counter, 8-bit data id and a 8-bit CRC. New projects should use P22 instead.
874    P02,
875    /// E2E Profile 04: Uses an 16-bit length, 16-bit counter, 32-bit data id and a 32-bit CRC
876    P04,
877    /// E2E Profile 04m: Uses an 16-bit length, 16-bit counter, 32-bit data id and a 32-bit CRC, as well as source ID, message type and message result
878    P04m,
879    /// E2E Profile 05: Uses an 8-bit counter, 16-bit data id and a 16-bit CRC
880    P05,
881    /// E2E Profile 06: Uses a 16-bit length, 8-bit counter, 16-bit data id and a 16-bit CRC
882    P06,
883    /// E2E Profile 07: Uses an 32-bit length, 32-bit counter, 32-bit data id and a 64-bit CRC
884    P07,
885    /// E2E Profile 07: Uses an 32-bit length, 32-bit counter, 32-bit data id and a 64-bit CRC, as well as source ID, message type and message result
886    P07m,
887    /// E2E Profile 08: Uses an 32-bit length, 32-bit counter, 32-bit data id and a 32-bit CRC
888    P08,
889    /// E2E Profile 08m: Uses an 32-bit length, 32-bit counter, 32-bit data id and a 32-bit CRC, as well as source ID, message type and message result
890    P08m,
891    /// E2E Profile 11: Uses an 4-bit counter, 16-bit or 12-bit data id and a 8-bit CRC
892    P11,
893    /// E2E Profile 22: Uses a 4-bit counter, 8-bit data id and a 8-bit CRC
894    P22,
895    /// E2E Profile 44: Uses a 16-bit length, 16-bit counter, 32-bit data id and a 32-bit CRC
896    P44,
897    /// E2E Profile 44m: Uses a 16-bit length, 16-bit counter, 32-bit data id and a 32-bit CRC, as well as source ID, message type and message result
898    P44m,
899}
900
901//#########################################################
902
903/// there are two standardized behaviors for E2E profiles, which can be selected for each E2E transformation
904#[derive(Debug, Clone, Copy, PartialEq, Eq)]
905pub enum E2EProfileBehavior {
906    /// Pre Autosar-R4.2 behavior
907    PreR4_2,
908    /// behavior according to Autosar-R4.2 and newer
909    R4_2,
910}
911
912impl From<E2EProfileBehavior> for EnumItem {
913    fn from(e2e_profile_behavior: E2EProfileBehavior) -> EnumItem {
914        match e2e_profile_behavior {
915            E2EProfileBehavior::PreR4_2 => EnumItem::PreR4_2,
916            E2EProfileBehavior::R4_2 => EnumItem::R4_2,
917        }
918    }
919}
920
921impl TryFrom<EnumItem> for E2EProfileBehavior {
922    type Error = AutosarAbstractionError;
923
924    fn try_from(value: EnumItem) -> Result<E2EProfileBehavior, AutosarAbstractionError> {
925        match value {
926            EnumItem::PreR4_2 => Ok(E2EProfileBehavior::PreR4_2),
927            EnumItem::R4_2 => Ok(E2EProfileBehavior::R4_2),
928            _ => Err(AutosarAbstractionError::ValueConversionError {
929                value: value.to_string(),
930                dest: "E2EProfileBehavior".to_string(),
931            }),
932        }
933    }
934}
935
936//#########################################################
937
938/// data ID modes for E2E profiles 01 and 11
939#[derive(Debug, Clone, Copy, PartialEq, Eq)]
940pub enum DataIdMode {
941    /// Two bytes of the data id are included in the CRC (double ID configuration).
942    All16Bit,
943    /// The data id is split into two 8-bit parts, which are included in the CRC in an alternating manner.
944    Alternating8Bit,
945    /// The low byte is included in the implicit CRC calculation, the low nibble of the high byte is transmitted along with the data
946    Lower12Bit,
947    /// Only the low byte is included, the high byte is never used
948    Lower8Bit,
949}
950
951impl From<DataIdMode> for EnumItem {
952    fn from(data_id_mode: DataIdMode) -> EnumItem {
953        match data_id_mode {
954            DataIdMode::All16Bit => EnumItem::All16Bit,
955            DataIdMode::Alternating8Bit => EnumItem::Alternating8Bit,
956            DataIdMode::Lower12Bit => EnumItem::Lower12Bit,
957            DataIdMode::Lower8Bit => EnumItem::Lower8Bit,
958        }
959    }
960}
961
962impl TryFrom<EnumItem> for DataIdMode {
963    type Error = AutosarAbstractionError;
964
965    fn try_from(value: EnumItem) -> Result<DataIdMode, AutosarAbstractionError> {
966        match value {
967            EnumItem::All16Bit => Ok(DataIdMode::All16Bit),
968            EnumItem::Alternating8Bit => Ok(DataIdMode::Alternating8Bit),
969            EnumItem::Lower12Bit => Ok(DataIdMode::Lower12Bit),
970            EnumItem::Lower8Bit => Ok(DataIdMode::Lower8Bit),
971            _ => Err(AutosarAbstractionError::ValueConversionError {
972                value: value.to_string(),
973                dest: "DataIdMode".to_string(),
974            }),
975        }
976    }
977}
978
979//#########################################################
980
981/// message types that can be used in a SOME/IP message header, depending on the type of communication
982#[derive(Debug, Clone, Copy, PartialEq, Eq)]
983pub enum SomeIpMessageType {
984    /// Notification message
985    Notification,
986    /// Request message, which expects a response
987    Request,
988    /// Request without return message - a fire and forget message
989    RequestNoReturn,
990    /// Response message
991    Response,
992}
993
994impl From<SomeIpMessageType> for EnumItem {
995    fn from(someip_msg_type: SomeIpMessageType) -> EnumItem {
996        match someip_msg_type {
997            SomeIpMessageType::Notification => EnumItem::Notification,
998            SomeIpMessageType::Request => EnumItem::Request,
999            SomeIpMessageType::RequestNoReturn => EnumItem::RequestNoReturn,
1000            SomeIpMessageType::Response => EnumItem::Response,
1001        }
1002    }
1003}
1004
1005impl TryFrom<EnumItem> for SomeIpMessageType {
1006    type Error = AutosarAbstractionError;
1007
1008    fn try_from(value: EnumItem) -> Result<SomeIpMessageType, AutosarAbstractionError> {
1009        match value {
1010            EnumItem::Notification => Ok(SomeIpMessageType::Notification),
1011            EnumItem::Request => Ok(SomeIpMessageType::Request),
1012            EnumItem::RequestNoReturn => Ok(SomeIpMessageType::RequestNoReturn),
1013            EnumItem::Response => Ok(SomeIpMessageType::Response),
1014            _ => Err(AutosarAbstractionError::ValueConversionError {
1015                value: value.to_string(),
1016                dest: "SomeIpMessageType".to_string(),
1017            }),
1018        }
1019    }
1020}
1021
1022//#########################################################
1023
1024/// Properties for the End to End transformation of an ISignal(Group)
1025#[derive(Debug, Clone, PartialEq, Eq)]
1026pub struct EndToEndTransformationISignalProps(Element);
1027abstraction_element!(EndToEndTransformationISignalProps, EndToEndTransformationISignalProps);
1028
1029impl EndToEndTransformationISignalProps {
1030    pub(crate) fn new(
1031        parent_element: Element,
1032        transformer: &TransformationTechnology,
1033    ) -> Result<Self, AutosarAbstractionError> {
1034        if transformer.protocol().as_deref() != Some("E2E") {
1035            return Err(AutosarAbstractionError::InvalidParameter(
1036                "EndToEndTransformationISignalProps must reference a E2E transformer".to_string(),
1037            ));
1038        }
1039        let e2e_props_elem = parent_element.create_sub_element(ElementName::EndToEndTransformationISignalProps)?;
1040
1041        let e2e_props = Self(e2e_props_elem);
1042        e2e_props.set_transformer(transformer)?;
1043
1044        Ok(e2e_props)
1045    }
1046
1047    fn inner_element(&self) -> Option<Element> {
1048        self.0
1049            .get_sub_element(ElementName::EndToEndTransformationISignalPropsVariants)?
1050            .get_sub_element(ElementName::EndToEndTransformationISignalPropsConditional)
1051    }
1052
1053    fn create_inner_element(&self) -> Result<Element, AutosarAbstractionError> {
1054        let e2e_props_elem = self
1055            .element()
1056            .get_or_create_sub_element(ElementName::EndToEndTransformationISignalPropsVariants)?
1057            .get_or_create_sub_element(ElementName::EndToEndTransformationISignalPropsConditional)?;
1058        Ok(e2e_props_elem)
1059    }
1060
1061    /// set the transformer reference of the E2E transformation properties
1062    pub fn set_transformer(&self, transformer: &TransformationTechnology) -> Result<(), AutosarAbstractionError> {
1063        if transformer.protocol().as_deref() != Some("E2E") {
1064            return Err(AutosarAbstractionError::InvalidParameter(
1065                "EndToEndTransformationISignalProps must reference a E2E transformer".to_string(),
1066            ));
1067        }
1068        self.create_inner_element()?
1069            .get_or_create_sub_element(ElementName::TransformerRef)?
1070            .set_reference_target(transformer.element())?;
1071        Ok(())
1072    }
1073
1074    /// get the transformer reference of the E2E transformation properties
1075    #[must_use]
1076    pub fn transformer(&self) -> Option<TransformationTechnology> {
1077        let t_elem = self
1078            .inner_element()?
1079            .get_sub_element(ElementName::TransformerRef)?
1080            .get_reference_target()
1081            .ok()?;
1082        TransformationTechnology::try_from(t_elem).ok()
1083    }
1084
1085    /// set the data IDs that are used for the E2E transformation
1086    pub fn set_data_ids(&self, data_ids: &[u32]) -> Result<(), AutosarAbstractionError> {
1087        if data_ids.is_empty() {
1088            let _ = self
1089                .inner_element()
1090                .and_then(|inner| inner.remove_sub_element_kind(ElementName::DataIds).ok());
1091        } else {
1092            let data_ids_elem = self.create_inner_element()?.create_sub_element(ElementName::DataIds)?;
1093            for data_id in data_ids {
1094                data_ids_elem
1095                    .create_sub_element(ElementName::DataId)?
1096                    .set_character_data(u64::from(*data_id))?;
1097            }
1098        }
1099        Ok(())
1100    }
1101
1102    /// get the data IDs that are used for the E2E transformation
1103    #[must_use]
1104    pub fn data_ids(&self) -> Vec<u32> {
1105        self.inner_element()
1106            .and_then(|inner_elem| inner_elem.get_sub_element(ElementName::DataIds))
1107            .map(|elem| {
1108                elem.sub_elements()
1109                    .filter_map(|elem| elem.character_data().and_then(|cdata| cdata.parse_integer()))
1110                    .collect()
1111            })
1112            .unwrap_or_default()
1113    }
1114
1115    /// set the length of payload and E2E header in bits
1116    pub fn set_data_length(&self, data_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1117        if let Some(data_length) = data_length {
1118            self.create_inner_element()?
1119                .get_or_create_sub_element(ElementName::DataLength)?
1120                .set_character_data(data_length.to_string())?;
1121        } else {
1122            let _ = self
1123                .inner_element()
1124                .and_then(|inner| inner.remove_sub_element_kind(ElementName::DataLength).ok());
1125        }
1126        Ok(())
1127    }
1128
1129    /// get the length of payload and E2E header in bits
1130    #[must_use]
1131    pub fn data_length(&self) -> Option<u32> {
1132        self.inner_element()?
1133            .get_sub_element(ElementName::DataLength)?
1134            .character_data()?
1135            .parse_integer()
1136    }
1137
1138    /// set the maximum data length
1139    pub fn set_max_data_length(&self, max_data_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1140        if let Some(max_data_length) = max_data_length {
1141            self.create_inner_element()?
1142                .get_or_create_sub_element(ElementName::MaxDataLength)?
1143                .set_character_data(max_data_length.to_string())?;
1144        } else {
1145            let _ = self
1146                .inner_element()
1147                .and_then(|inner| inner.remove_sub_element_kind(ElementName::MaxDataLength).ok());
1148        }
1149        Ok(())
1150    }
1151
1152    /// get the maximum data length
1153    #[must_use]
1154    pub fn max_data_length(&self) -> Option<u32> {
1155        self.inner_element()?
1156            .get_sub_element(ElementName::MaxDataLength)
1157            .and_then(|elem| elem.character_data())
1158            .and_then(|cdata| cdata.parse_integer())
1159    }
1160
1161    /// set the minimum data length
1162    pub fn set_min_data_length(&self, min_data_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1163        if let Some(min_data_length) = min_data_length {
1164            self.create_inner_element()?
1165                .get_or_create_sub_element(ElementName::MinDataLength)?
1166                .set_character_data(min_data_length.to_string())?;
1167        } else {
1168            let _ = self
1169                .inner_element()
1170                .and_then(|inner| inner.remove_sub_element_kind(ElementName::MinDataLength).ok());
1171        }
1172        Ok(())
1173    }
1174
1175    /// get the minimum data length
1176    #[must_use]
1177    pub fn min_data_length(&self) -> Option<u32> {
1178        self.inner_element()?
1179            .get_sub_element(ElementName::MinDataLength)?
1180            .character_data()?
1181            .parse_integer()
1182    }
1183
1184    /// set the source ID
1185    pub fn set_source_id(&self, source_id: Option<u32>) -> Result<(), AutosarAbstractionError> {
1186        if let Some(source_id) = source_id {
1187            self.create_inner_element()?
1188                .get_or_create_sub_element(ElementName::SourceId)?
1189                .set_character_data(source_id.to_string())?;
1190        } else {
1191            let _ = self
1192                .inner_element()
1193                .and_then(|inner| inner.remove_sub_element_kind(ElementName::SourceId).ok());
1194        }
1195        Ok(())
1196    }
1197
1198    /// get the source ID
1199    #[must_use]
1200    pub fn source_id(&self) -> Option<u32> {
1201        self.inner_element()?
1202            .get_sub_element(ElementName::SourceId)?
1203            .character_data()?
1204            .parse_integer()
1205    }
1206}
1207
1208//#########################################################
1209
1210/// Properties for the SOMEIP transformation of an ISignal(Group)
1211#[derive(Debug, Clone, PartialEq, Eq)]
1212pub struct SomeIpTransformationISignalProps(Element);
1213abstraction_element!(SomeIpTransformationISignalProps, SomeipTransformationISignalProps);
1214
1215impl SomeIpTransformationISignalProps {
1216    pub(crate) fn new(
1217        parent_element: Element,
1218        transformer: &TransformationTechnology,
1219    ) -> Result<Self, AutosarAbstractionError> {
1220        if transformer.protocol().as_deref() != Some("SOMEIP") {
1221            return Err(AutosarAbstractionError::InvalidParameter(
1222                "SOMEIPTransformationISignalProps must reference a SOMEIP transformer".to_string(),
1223            ));
1224        }
1225        let someip_props_elem = parent_element.create_sub_element(ElementName::SomeipTransformationISignalProps)?;
1226        let someip_props = Self(someip_props_elem);
1227        someip_props.set_transformer(transformer)?;
1228
1229        Ok(someip_props)
1230    }
1231
1232    fn inner_element(&self) -> Option<Element> {
1233        self.0
1234            .get_sub_element(ElementName::SomeipTransformationISignalPropsVariants)?
1235            .get_sub_element(ElementName::SomeipTransformationISignalPropsConditional)
1236    }
1237
1238    fn create_inner_element(&self) -> Result<Element, AutosarAbstractionError> {
1239        let e2e_props_elem = self
1240            .element()
1241            .get_or_create_sub_element(ElementName::SomeipTransformationISignalPropsVariants)?
1242            .get_or_create_sub_element(ElementName::SomeipTransformationISignalPropsConditional)?;
1243        Ok(e2e_props_elem)
1244    }
1245
1246    /// set the transformer reference of the SOMEIP transformation properties
1247    pub fn set_transformer(&self, transformer: &TransformationTechnology) -> Result<(), AutosarAbstractionError> {
1248        if transformer.protocol().as_deref() != Some("SOMEIP") {
1249            return Err(AutosarAbstractionError::InvalidParameter(
1250                "SOMEIPTransformationISignalProps must reference a SOMEIP transformer".to_string(),
1251            ));
1252        }
1253        self.create_inner_element()?
1254            .get_or_create_sub_element(ElementName::TransformerRef)?
1255            .set_reference_target(transformer.element())?;
1256        Ok(())
1257    }
1258
1259    /// get the transformer reference of the SOMEIP transformation properties
1260    #[must_use]
1261    pub fn transformer(&self) -> Option<TransformationTechnology> {
1262        let t_elem = self
1263            .inner_element()?
1264            .get_sub_element(ElementName::TransformerRef)?
1265            .get_reference_target()
1266            .ok()?;
1267        TransformationTechnology::try_from(t_elem).ok()
1268    }
1269
1270    /// set the legacy strings property
1271    pub fn set_legacy_strings(&self, legacy_strings: Option<bool>) -> Result<(), AutosarAbstractionError> {
1272        if let Some(legacy_strings) = legacy_strings {
1273            self.create_inner_element()?
1274                .get_or_create_sub_element(ElementName::ImplementsLegacyStringSerialization)?
1275                .set_character_data(legacy_strings.to_string())?;
1276        } else {
1277            let _ = self.inner_element().and_then(|inner| {
1278                inner
1279                    .remove_sub_element_kind(ElementName::ImplementsLegacyStringSerialization)
1280                    .ok()
1281            });
1282        }
1283        Ok(())
1284    }
1285
1286    /// get the legacy strings property
1287    #[must_use]
1288    pub fn legacy_strings(&self) -> Option<bool> {
1289        self.inner_element()?
1290            .get_sub_element(ElementName::ImplementsLegacyStringSerialization)?
1291            .character_data()?
1292            .parse_bool()
1293    }
1294
1295    /// set the interface version property
1296    pub fn set_interface_version(&self, interface_version: Option<u32>) -> Result<(), AutosarAbstractionError> {
1297        if let Some(interface_version) = interface_version {
1298            self.create_inner_element()?
1299                .get_or_create_sub_element(ElementName::InterfaceVersion)?
1300                .set_character_data(interface_version.to_string())?;
1301        } else {
1302            let _ = self
1303                .inner_element()
1304                .and_then(|inner| inner.remove_sub_element_kind(ElementName::InterfaceVersion).ok());
1305        }
1306        Ok(())
1307    }
1308
1309    /// get the interface version property
1310    #[must_use]
1311    pub fn interface_version(&self) -> Option<u32> {
1312        self.inner_element()?
1313            .get_sub_element(ElementName::InterfaceVersion)?
1314            .character_data()?
1315            .parse_integer()
1316    }
1317
1318    /// set the dynamic length property
1319    pub fn set_dynamic_length(&self, dynamic_length: Option<bool>) -> Result<(), AutosarAbstractionError> {
1320        if let Some(dynamic_length) = dynamic_length {
1321            self.create_inner_element()?
1322                .get_or_create_sub_element(ElementName::IsDynamicLengthFieldSize)?
1323                .set_character_data(dynamic_length.to_string())?;
1324        } else {
1325            let _ = self.inner_element().and_then(|inner| {
1326                inner
1327                    .remove_sub_element_kind(ElementName::IsDynamicLengthFieldSize)
1328                    .ok()
1329            });
1330        }
1331        Ok(())
1332    }
1333
1334    /// get the dynamic length property
1335    #[must_use]
1336    pub fn dynamic_length(&self) -> Option<bool> {
1337        self.inner_element()?
1338            .get_sub_element(ElementName::IsDynamicLengthFieldSize)?
1339            .character_data()?
1340            .parse_bool()
1341    }
1342
1343    /// set the message type property
1344    pub fn set_message_type(&self, message_type: Option<SomeIpMessageType>) -> Result<(), AutosarAbstractionError> {
1345        if let Some(message_type) = message_type {
1346            self.create_inner_element()?
1347                .get_or_create_sub_element(ElementName::MessageType)?
1348                .set_character_data::<EnumItem>(message_type.into())?;
1349        } else {
1350            let _ = self
1351                .inner_element()
1352                .and_then(|inner| inner.remove_sub_element_kind(ElementName::MessageType).ok());
1353        }
1354        Ok(())
1355    }
1356
1357    /// get the message type property
1358    #[must_use]
1359    pub fn message_type(&self) -> Option<SomeIpMessageType> {
1360        self.inner_element()?
1361            .get_sub_element(ElementName::MessageType)?
1362            .character_data()?
1363            .enum_value()?
1364            .try_into()
1365            .ok()
1366    }
1367
1368    /// set the size of array length property
1369    pub fn set_size_of_array_length(&self, size_of_array_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1370        if let Some(size_of_array_length) = size_of_array_length {
1371            self.create_inner_element()?
1372                .get_or_create_sub_element(ElementName::SizeOfArrayLengthFields)?
1373                .set_character_data(size_of_array_length.to_string())?;
1374        } else {
1375            let _ = self
1376                .inner_element()
1377                .and_then(|inner| inner.remove_sub_element_kind(ElementName::SizeOfArrayLengthFields).ok());
1378        }
1379        Ok(())
1380    }
1381
1382    /// get the size of array length property
1383    #[must_use]
1384    pub fn size_of_array_length(&self) -> Option<u32> {
1385        self.inner_element()?
1386            .get_sub_element(ElementName::SizeOfArrayLengthFields)?
1387            .character_data()?
1388            .parse_integer()
1389    }
1390
1391    /// set the size of string length property
1392    pub fn set_size_of_string_length(&self, size_of_string_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1393        if let Some(size_of_string_length) = size_of_string_length {
1394            self.create_inner_element()?
1395                .get_or_create_sub_element(ElementName::SizeOfStringLengthFields)?
1396                .set_character_data(size_of_string_length.to_string())?;
1397        } else {
1398            let _ = self.inner_element().and_then(|inner| {
1399                inner
1400                    .remove_sub_element_kind(ElementName::SizeOfStringLengthFields)
1401                    .ok()
1402            });
1403        }
1404        Ok(())
1405    }
1406
1407    /// get the size of string length property
1408    #[must_use]
1409    pub fn size_of_string_length(&self) -> Option<u32> {
1410        self.inner_element()?
1411            .get_sub_element(ElementName::SizeOfStringLengthFields)?
1412            .character_data()?
1413            .parse_integer()
1414    }
1415
1416    /// set the size of struct length property
1417    pub fn set_size_of_struct_length(&self, size_of_struct_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1418        if let Some(size_of_struct_length) = size_of_struct_length {
1419            self.create_inner_element()?
1420                .get_or_create_sub_element(ElementName::SizeOfStructLengthFields)?
1421                .set_character_data(size_of_struct_length.to_string())?;
1422        } else {
1423            let _ = self.inner_element().and_then(|inner| {
1424                inner
1425                    .remove_sub_element_kind(ElementName::SizeOfStructLengthFields)
1426                    .ok()
1427            });
1428        }
1429        Ok(())
1430    }
1431
1432    /// get the size of struct length property
1433    #[must_use]
1434    pub fn size_of_struct_length(&self) -> Option<u32> {
1435        self.inner_element()?
1436            .get_sub_element(ElementName::SizeOfStructLengthFields)?
1437            .character_data()?
1438            .parse_integer()
1439    }
1440
1441    /// set the size of union length property
1442    pub fn set_size_of_union_length(&self, size_of_union_length: Option<u32>) -> Result<(), AutosarAbstractionError> {
1443        if let Some(size_of_union_length) = size_of_union_length {
1444            self.create_inner_element()?
1445                .get_or_create_sub_element(ElementName::SizeOfUnionLengthFields)?
1446                .set_character_data(size_of_union_length.to_string())?;
1447        } else {
1448            let _ = self
1449                .inner_element()
1450                .and_then(|inner| inner.remove_sub_element_kind(ElementName::SizeOfUnionLengthFields).ok());
1451        }
1452        Ok(())
1453    }
1454
1455    /// get the size of union length property
1456    #[must_use]
1457    pub fn size_of_union_length(&self) -> Option<u32> {
1458        self.inner_element()?
1459            .get_sub_element(ElementName::SizeOfUnionLengthFields)?
1460            .character_data()?
1461            .parse_integer()
1462    }
1463}
1464
1465//#########################################################
1466
1467/// Wrapper enum for the properties for the transformation of an ISignal(Group)
1468#[derive(Debug, Clone, PartialEq, Eq)]
1469pub enum TransformationISignalProps {
1470    /// Properties for the End to End transformation of an ISignal(Group)
1471    E2E(EndToEndTransformationISignalProps),
1472    /// Properties for the SOMEIP transformation of an ISignal(Group)
1473    SomeIp(SomeIpTransformationISignalProps),
1474}
1475
1476impl AbstractionElement for TransformationISignalProps {
1477    fn element(&self) -> &Element {
1478        match self {
1479            TransformationISignalProps::E2E(e2e_props) => e2e_props.element(),
1480            TransformationISignalProps::SomeIp(someip_props) => someip_props.element(),
1481        }
1482    }
1483}
1484
1485impl TryFrom<Element> for TransformationISignalProps {
1486    type Error = AutosarAbstractionError;
1487
1488    fn try_from(element: Element) -> Result<Self, Self::Error> {
1489        match element.element_name() {
1490            ElementName::EndToEndTransformationISignalProps => {
1491                EndToEndTransformationISignalProps::try_from(element).map(TransformationISignalProps::E2E)
1492            }
1493            ElementName::SomeipTransformationISignalProps => {
1494                SomeIpTransformationISignalProps::try_from(element).map(TransformationISignalProps::SomeIp)
1495            }
1496            _ => Err(AutosarAbstractionError::ConversionError {
1497                element,
1498                dest: "TransformationISignalProps".to_string(),
1499            }),
1500        }
1501    }
1502}
1503
1504//#########################################################
1505
1506#[cfg(test)]
1507mod test {
1508    use super::*;
1509    use crate::{
1510        AutosarModelAbstraction,
1511        communication::{ISignal, SystemSignal},
1512        datatype::{BaseTypeEncoding, SwBaseType},
1513    };
1514
1515    #[test]
1516    fn transformation_technologies() {
1517        // Data Transformation Sets and transformation technologies were introduced in AUTOSAR 4.2.1
1518        // There have been several changes over time, so it makes sense to test with multiple versions
1519        let versions = vec![
1520            AutosarVersion::Autosar_4_2_1,
1521            AutosarVersion::Autosar_4_3_0,
1522            AutosarVersion::Autosar_00044,
1523            AutosarVersion::Autosar_00046,
1524            AutosarVersion::Autosar_00047,
1525            AutosarVersion::Autosar_00048,
1526            AutosarVersion::Autosar_00049,
1527            AutosarVersion::Autosar_00050,
1528            AutosarVersion::Autosar_00051,
1529            AutosarVersion::Autosar_00052,
1530        ];
1531        for version in versions {
1532            create_transformation_technologies(version);
1533        }
1534    }
1535
1536    fn create_transformation_technologies(file_version: AutosarVersion) {
1537        let model = AutosarModelAbstraction::create("test", file_version);
1538        let package = model.get_or_create_package("/package").unwrap();
1539        let dts = DataTransformationSet::new("test", &package).unwrap();
1540
1541        // create a generic transformation technology
1542        let config = TransformationTechnologyConfig::Generic(GenericTransformationTechnologyConfig {
1543            protocol_name: "test".to_string(),
1544            protocol_version: "1.0.0".to_string(),
1545            header_length: 16,
1546            in_place: true,
1547        });
1548        let ttech = dts.create_transformation_technology("generic", &config).unwrap();
1549        let config2 = ttech.config().unwrap();
1550        assert_eq!(config, config2);
1551
1552        // create a COM transformation technology
1553        let config = TransformationTechnologyConfig::Com(ComTransformationTechnologyConfig { isignal_ipdu_length: 8 });
1554        dts.create_transformation_technology("com", &config).unwrap();
1555
1556        // create an E2E transformation technology for each profile
1557        for profile in &[
1558            E2EProfile::P01,
1559            E2EProfile::P02,
1560            E2EProfile::P04,
1561            E2EProfile::P04m,
1562            E2EProfile::P05,
1563            E2EProfile::P06,
1564            E2EProfile::P07,
1565            E2EProfile::P07m,
1566            E2EProfile::P08,
1567            E2EProfile::P08m,
1568            E2EProfile::P11,
1569            E2EProfile::P22,
1570            E2EProfile::P44,
1571            E2EProfile::P44m,
1572        ] {
1573            let e2e_config = E2ETransformationTechnologyConfig {
1574                profile: *profile,
1575                zero_header_length: false,
1576                transform_in_place: true,
1577                offset: 0,
1578                max_delta_counter: 0,
1579                max_error_state_init: 0,
1580                max_error_state_invalid: 0,
1581                max_error_state_valid: 0,
1582                max_no_new_or_repeated_data: 0,
1583                min_ok_state_init: 0,
1584                min_ok_state_invalid: 0,
1585                min_ok_state_valid: 0,
1586                window_size: 10,
1587                window_size_init: Some(11),
1588                window_size_invalid: Some(12),
1589                window_size_valid: Some(13),
1590                profile_behavior: Some(E2EProfileBehavior::R4_2),
1591                sync_counter_init: Some(0),
1592                data_id_mode: Some(DataIdMode::Lower12Bit),
1593                data_id_nibble_offset: Some(1),
1594                crc_offset: Some(2),
1595                counter_offset: Some(3),
1596            };
1597            let config = TransformationTechnologyConfig::E2E(e2e_config.clone());
1598            let ttech = dts
1599                .create_transformation_technology(&format!("{profile:?}"), &config)
1600                .unwrap();
1601            let config2 = ttech.config().unwrap();
1602            let TransformationTechnologyConfig::E2E(e2e_config2) = config2 else {
1603                panic!("Expected E2E transformation technology");
1604            };
1605            assert_eq!(e2e_config.profile, e2e_config2.profile);
1606            assert_eq!(e2e_config.zero_header_length, e2e_config2.zero_header_length);
1607            assert_eq!(e2e_config.transform_in_place, e2e_config2.transform_in_place);
1608            assert_eq!(e2e_config.offset, e2e_config2.offset);
1609            assert_eq!(e2e_config.max_delta_counter, e2e_config2.max_delta_counter);
1610            assert_eq!(e2e_config.max_error_state_init, e2e_config2.max_error_state_init);
1611            assert_eq!(e2e_config.max_error_state_invalid, e2e_config2.max_error_state_invalid);
1612            assert_eq!(e2e_config.max_error_state_valid, e2e_config2.max_error_state_valid);
1613            assert_eq!(
1614                e2e_config.max_no_new_or_repeated_data,
1615                e2e_config2.max_no_new_or_repeated_data
1616            );
1617            assert_eq!(e2e_config.min_ok_state_init, e2e_config2.min_ok_state_init);
1618            assert_eq!(e2e_config.min_ok_state_invalid, e2e_config2.min_ok_state_invalid);
1619            assert_eq!(e2e_config.min_ok_state_valid, e2e_config2.min_ok_state_valid);
1620            if *profile == E2EProfile::P01 || *profile == E2EProfile::P11 {
1621                assert_eq!(e2e_config.data_id_mode, e2e_config2.data_id_mode);
1622                assert_eq!(e2e_config.data_id_nibble_offset, e2e_config2.data_id_nibble_offset);
1623            }
1624        }
1625
1626        // create a SOMEIP transformation technology
1627        let config = TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
1628            alignment: 8,
1629            byte_order: ByteOrder::MostSignificantByteFirst,
1630            interface_version: 1,
1631        });
1632        let ttech = dts.create_transformation_technology("someip", &config).unwrap();
1633        let config2 = ttech.config().unwrap();
1634        assert_eq!(config, config2);
1635
1636        // verify the transformation technologies in the data transformation set
1637        let mut ttechs_iter = dts.transformation_technologies();
1638        assert_eq!(ttechs_iter.next().unwrap().name().unwrap(), "generic");
1639        assert_eq!(ttechs_iter.next().unwrap().name().unwrap(), "com");
1640    }
1641
1642    #[test]
1643    fn data_transformation() {
1644        let model = AutosarModelAbstraction::create("test", AutosarVersion::Autosar_00049);
1645        let package = model.get_or_create_package("/package").unwrap();
1646        let dts = DataTransformationSet::new("test_dts", &package).unwrap();
1647
1648        let e2e_transformation_config = TransformationTechnologyConfig::E2E(E2ETransformationTechnologyConfig {
1649            profile: E2EProfile::P01,
1650            zero_header_length: false,
1651            transform_in_place: true,
1652            offset: 0,
1653            max_delta_counter: 0,
1654            max_error_state_init: 0,
1655            max_error_state_invalid: 0,
1656            max_error_state_valid: 0,
1657            max_no_new_or_repeated_data: 0,
1658            min_ok_state_init: 0,
1659            min_ok_state_invalid: 0,
1660            min_ok_state_valid: 0,
1661            window_size: 11,
1662            window_size_init: Some(11),
1663            window_size_invalid: Some(12),
1664            window_size_valid: Some(13),
1665            profile_behavior: Some(E2EProfileBehavior::R4_2),
1666            sync_counter_init: Some(0),
1667            data_id_mode: Some(DataIdMode::All16Bit),
1668            data_id_nibble_offset: None,
1669            crc_offset: Some(2),
1670            counter_offset: Some(3),
1671        });
1672        let e2e_transformation = dts
1673            .create_transformation_technology("e2e", &e2e_transformation_config)
1674            .unwrap();
1675        assert_eq!(e2e_transformation.config().unwrap(), e2e_transformation_config);
1676
1677        let com_transformation_config =
1678            TransformationTechnologyConfig::Com(ComTransformationTechnologyConfig { isignal_ipdu_length: 8 });
1679        let com_transformation = dts
1680            .create_transformation_technology("com", &com_transformation_config)
1681            .unwrap();
1682        assert_eq!(com_transformation.config().unwrap(), com_transformation_config);
1683
1684        let someip_transformation_config =
1685            TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
1686                alignment: 8,
1687                byte_order: ByteOrder::MostSignificantByteFirst,
1688                interface_version: 1,
1689            });
1690        let someip_transformation = dts
1691            .create_transformation_technology("someip", &someip_transformation_config)
1692            .unwrap();
1693        assert_eq!(someip_transformation.config().unwrap(), someip_transformation_config);
1694
1695        let generic_transformation_config =
1696            TransformationTechnologyConfig::Generic(GenericTransformationTechnologyConfig {
1697                protocol_name: "test".to_string(),
1698                protocol_version: "1.0.0".to_string(),
1699                header_length: 16,
1700                in_place: true,
1701            });
1702        let generic_transformation = dts
1703            .create_transformation_technology("generic", &generic_transformation_config)
1704            .unwrap();
1705        assert_eq!(someip_transformation.config().unwrap(), someip_transformation_config);
1706
1707        // not allowed: empty transformation chain
1708        let result = dts.create_data_transformation("test1", &[], true);
1709        assert!(result.is_err());
1710
1711        // not allowed: multiple serializers Com + SomeIp
1712        let result = dts.create_data_transformation("test2", &[&com_transformation, &someip_transformation], true);
1713        assert!(result.is_err());
1714
1715        // Ok: Com only
1716        let result = dts.create_data_transformation("test3", &[&com_transformation], true);
1717        assert!(result.is_ok());
1718
1719        // Ok: E2E only
1720        let result = dts.create_data_transformation("test4", &[&e2e_transformation], true);
1721        assert!(result.is_ok());
1722
1723        // Ok: SomeIp only
1724        let result = dts.create_data_transformation("test5", &[&someip_transformation], true);
1725        assert!(result.is_ok());
1726
1727        // Ok: generic only
1728        let result = dts.create_data_transformation("test6", &[&generic_transformation], true);
1729        assert!(result.is_ok());
1730
1731        // Ok: Com + E2E
1732        let result = dts.create_data_transformation("test7", &[&com_transformation, &e2e_transformation], true);
1733        assert!(result.is_ok());
1734
1735        // Ok: SomeIp + E2E
1736        let result = dts.create_data_transformation("test8", &[&someip_transformation, &e2e_transformation], true);
1737        assert!(result.is_ok());
1738
1739        // Ok: generic + E2E
1740        let result = dts.create_data_transformation("test9", &[&generic_transformation, &e2e_transformation], true);
1741        assert!(result.is_ok());
1742        let dt = result.unwrap();
1743        assert_eq!(dt.data_transformation_set().unwrap(), dts);
1744
1745        let dts2 = package.create_data_transformation_set("test_dts2").unwrap();
1746
1747        // not allowed: dts2 using transformation from dts
1748        let result = dts2.create_data_transformation("test10", &[&e2e_transformation], true);
1749        assert!(result.is_err());
1750
1751        // iterate over the data transformations
1752        let mut dts_iter = dts.data_transformations();
1753        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test3");
1754        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test4");
1755        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test5");
1756        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test6");
1757        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test7");
1758        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test8");
1759        assert_eq!(dts_iter.next().unwrap().name().unwrap(), "test9");
1760        assert_eq!(dts_iter.next(), None);
1761    }
1762
1763    #[test]
1764    fn transformation_technology() {
1765        let model = AutosarModelAbstraction::create("test", AutosarVersion::Autosar_4_2_1);
1766        let package = model.get_or_create_package("/package").unwrap();
1767        let dts = DataTransformationSet::new("test_dts", &package).unwrap();
1768
1769        let e2e_config_p01 = TransformationTechnologyConfig::E2E(E2ETransformationTechnologyConfig {
1770            profile: E2EProfile::P01,
1771            zero_header_length: false,
1772            transform_in_place: true,
1773            offset: 0,
1774            max_delta_counter: 0,
1775            max_error_state_init: 0,
1776            max_error_state_invalid: 0,
1777            max_error_state_valid: 0,
1778            max_no_new_or_repeated_data: 0,
1779            min_ok_state_init: 0,
1780            min_ok_state_invalid: 0,
1781            min_ok_state_valid: 0,
1782            window_size: 10,
1783            window_size_init: Some(11),
1784            window_size_invalid: Some(12),
1785            window_size_valid: Some(13),
1786            profile_behavior: Some(E2EProfileBehavior::R4_2),
1787            sync_counter_init: Some(0),
1788            data_id_mode: Some(DataIdMode::Lower12Bit),
1789            data_id_nibble_offset: Some(1),
1790            crc_offset: Some(2),
1791            counter_offset: Some(3),
1792        });
1793        let e2e_config_p04 = TransformationTechnologyConfig::E2E(E2ETransformationTechnologyConfig {
1794            profile: E2EProfile::P04,
1795            zero_header_length: false,
1796            transform_in_place: true,
1797            offset: 0,
1798            max_delta_counter: 0,
1799            max_error_state_init: 0,
1800            max_error_state_invalid: 0,
1801            max_error_state_valid: 0,
1802            max_no_new_or_repeated_data: 0,
1803            min_ok_state_init: 0,
1804            min_ok_state_invalid: 0,
1805            min_ok_state_valid: 0,
1806            window_size: 10,
1807            window_size_init: Some(11),
1808            window_size_invalid: Some(12),
1809            window_size_valid: Some(13),
1810            profile_behavior: Some(E2EProfileBehavior::R4_2),
1811            sync_counter_init: Some(0),
1812            data_id_mode: None,
1813            data_id_nibble_offset: None,
1814            crc_offset: None,
1815            counter_offset: None,
1816        });
1817        let com_config =
1818            TransformationTechnologyConfig::Com(ComTransformationTechnologyConfig { isignal_ipdu_length: 8 });
1819        let someip_config = TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
1820            alignment: 8,
1821            byte_order: ByteOrder::MostSignificantByteFirst,
1822            interface_version: 1,
1823        });
1824        let generic_config = TransformationTechnologyConfig::Generic(GenericTransformationTechnologyConfig {
1825            protocol_name: "test".to_string(),
1826            protocol_version: "1.0.0".to_string(),
1827            header_length: 16,
1828            in_place: true,
1829        });
1830
1831        // create a "clean" transformation technology for each type
1832        let transformation = dts.create_transformation_technology("t", &e2e_config_p01).unwrap();
1833        let e2e_p01_transformation_orig = transformation.element().serialize();
1834        dts.element()
1835            .remove_sub_element_kind(ElementName::TransformationTechnologys)
1836            .unwrap();
1837
1838        let transformation = dts.create_transformation_technology("t", &e2e_config_p04).unwrap();
1839        let e2e_p04_transformation_orig = transformation.element().serialize();
1840        dts.element()
1841            .remove_sub_element_kind(ElementName::TransformationTechnologys)
1842            .unwrap();
1843
1844        let transformation = dts.create_transformation_technology("t", &com_config).unwrap();
1845        let com_transformation_orig = transformation.element().serialize();
1846        dts.element()
1847            .remove_sub_element_kind(ElementName::TransformationTechnologys)
1848            .unwrap();
1849
1850        let transformation = dts.create_transformation_technology("t", &someip_config).unwrap();
1851        let someip_transformation_orig = transformation.element().serialize();
1852        dts.element()
1853            .remove_sub_element_kind(ElementName::TransformationTechnologys)
1854            .unwrap();
1855
1856        let transformation = dts.create_transformation_technology("t", &generic_config).unwrap();
1857        let generic_transformation_orig = transformation.element().serialize();
1858        dts.element()
1859            .remove_sub_element_kind(ElementName::TransformationTechnologys)
1860            .unwrap();
1861
1862        // overwrite the transformation technology with another configuration, and check if the resulting xml is identical
1863        let transformation = dts.create_transformation_technology("t", &generic_config).unwrap();
1864        transformation.set_config(&e2e_config_p01).unwrap();
1865        assert_eq!(transformation.element().serialize(), e2e_p01_transformation_orig);
1866        transformation.set_config(&e2e_config_p04).unwrap();
1867        assert_eq!(transformation.element().serialize(), e2e_p04_transformation_orig);
1868        transformation.set_config(&com_config).unwrap();
1869        assert_eq!(transformation.element().serialize(), com_transformation_orig);
1870        transformation.set_config(&someip_config).unwrap();
1871        assert_eq!(transformation.element().serialize(), someip_transformation_orig);
1872        transformation.set_config(&generic_config).unwrap();
1873        assert_eq!(transformation.element().serialize(), generic_transformation_orig);
1874    }
1875
1876    #[test]
1877    fn data_transformation_chain_iter() {
1878        let model = AutosarModelAbstraction::create("test", AutosarVersion::Autosar_4_2_1);
1879        let package = model.get_or_create_package("/package").unwrap();
1880        let dts = DataTransformationSet::new("test_dts", &package).unwrap();
1881
1882        let com_transformation = dts
1883            .create_transformation_technology(
1884                "com",
1885                &TransformationTechnologyConfig::Com(ComTransformationTechnologyConfig { isignal_ipdu_length: 8 }),
1886            )
1887            .unwrap();
1888
1889        let dt = dts
1890            .create_data_transformation("test", &[&com_transformation], false)
1891            .unwrap();
1892        assert_eq!(dt.transformation_technologies().count(), 1);
1893        let com_transformation2 = dt.transformation_technologies().next().unwrap();
1894        assert_eq!(com_transformation, com_transformation2);
1895    }
1896
1897    #[test]
1898    fn transformation_isignal_props() {
1899        let model = AutosarModelAbstraction::create("test", AutosarVersion::Autosar_00049);
1900        let package = model.get_or_create_package("/package").unwrap();
1901        let dts = DataTransformationSet::new("test_dts", &package).unwrap();
1902
1903        let e2e_transformation = dts
1904            .create_transformation_technology(
1905                "e2e",
1906                &TransformationTechnologyConfig::E2E(E2ETransformationTechnologyConfig {
1907                    profile: E2EProfile::P01,
1908                    zero_header_length: false,
1909                    transform_in_place: true,
1910                    offset: 0,
1911                    max_delta_counter: 0,
1912                    max_error_state_init: 0,
1913                    max_error_state_invalid: 0,
1914                    max_error_state_valid: 0,
1915                    max_no_new_or_repeated_data: 0,
1916                    min_ok_state_init: 0,
1917                    min_ok_state_invalid: 0,
1918                    min_ok_state_valid: 0,
1919                    window_size: 10,
1920                    window_size_init: Some(11),
1921                    window_size_invalid: Some(12),
1922                    window_size_valid: Some(13),
1923                    profile_behavior: Some(E2EProfileBehavior::R4_2),
1924                    sync_counter_init: Some(0),
1925                    data_id_mode: Some(DataIdMode::Lower12Bit),
1926                    data_id_nibble_offset: Some(1),
1927                    crc_offset: Some(2),
1928                    counter_offset: Some(3),
1929                }),
1930            )
1931            .unwrap();
1932        let someip_transformation = dts
1933            .create_transformation_technology(
1934                "someip",
1935                &TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
1936                    alignment: 8,
1937                    byte_order: ByteOrder::MostSignificantByteFirst,
1938                    interface_version: 1,
1939                }),
1940            )
1941            .unwrap();
1942
1943        let sw_base_type =
1944            SwBaseType::new("sw_base_type", &package, 8, BaseTypeEncoding::None, None, None, None).unwrap();
1945        let signal = ISignal::new(
1946            "signal",
1947            &package,
1948            8,
1949            &SystemSignal::new("sys_signal", &package).unwrap(),
1950            Some(&sw_base_type),
1951        )
1952        .unwrap();
1953
1954        let e2e_props = signal
1955            .create_e2e_transformation_isignal_props(&e2e_transformation)
1956            .unwrap();
1957        assert_eq!(e2e_props.transformer().unwrap(), e2e_transformation);
1958        e2e_props.set_data_ids(&[1, 2, 3]).unwrap();
1959        e2e_props.set_data_length(Some(8)).unwrap();
1960        e2e_props.set_max_data_length(Some(16)).unwrap();
1961        e2e_props.set_min_data_length(Some(4)).unwrap();
1962        e2e_props.set_source_id(Some(0)).unwrap();
1963        assert_eq!(e2e_props.data_ids(), vec![1, 2, 3]);
1964        assert_eq!(e2e_props.data_length().unwrap(), 8);
1965        assert_eq!(e2e_props.max_data_length().unwrap(), 16);
1966        assert_eq!(e2e_props.min_data_length().unwrap(), 4);
1967        assert_eq!(e2e_props.source_id().unwrap(), 0);
1968        e2e_props.set_data_ids(&[]).unwrap();
1969        e2e_props.set_data_length(None).unwrap();
1970        e2e_props.set_max_data_length(None).unwrap();
1971        e2e_props.set_min_data_length(None).unwrap();
1972        e2e_props.set_source_id(None).unwrap();
1973        assert_eq!(e2e_props.data_ids(), vec![]);
1974        assert_eq!(e2e_props.data_length(), None);
1975        assert_eq!(e2e_props.max_data_length(), None);
1976        assert_eq!(e2e_props.min_data_length(), None);
1977        assert_eq!(e2e_props.source_id(), None);
1978
1979        assert!(EndToEndTransformationISignalProps::try_from(e2e_props.element().clone()).is_ok());
1980
1981        let someip_props = signal
1982            .create_someip_transformation_isignal_props(&someip_transformation)
1983            .unwrap();
1984        assert_eq!(someip_props.transformer().unwrap(), someip_transformation);
1985        someip_props.set_legacy_strings(Some(true)).unwrap();
1986        someip_props.set_interface_version(Some(1)).unwrap();
1987        someip_props.set_dynamic_length(Some(true)).unwrap();
1988        someip_props.set_message_type(Some(SomeIpMessageType::Request)).unwrap();
1989        someip_props.set_size_of_array_length(Some(8)).unwrap();
1990        someip_props.set_size_of_string_length(Some(16)).unwrap();
1991        someip_props.set_size_of_struct_length(Some(32)).unwrap();
1992        someip_props.set_size_of_union_length(Some(64)).unwrap();
1993        assert!(someip_props.legacy_strings().unwrap());
1994        assert_eq!(someip_props.interface_version().unwrap(), 1);
1995        assert!(someip_props.dynamic_length().unwrap());
1996        assert_eq!(someip_props.message_type().unwrap(), SomeIpMessageType::Request);
1997        assert_eq!(someip_props.size_of_array_length().unwrap(), 8);
1998        assert_eq!(someip_props.size_of_string_length().unwrap(), 16);
1999        assert_eq!(someip_props.size_of_struct_length().unwrap(), 32);
2000        assert_eq!(someip_props.size_of_union_length().unwrap(), 64);
2001        someip_props.set_legacy_strings(None).unwrap();
2002        someip_props.set_interface_version(None).unwrap();
2003        someip_props.set_dynamic_length(None).unwrap();
2004        someip_props.set_message_type(None).unwrap();
2005        someip_props.set_size_of_array_length(None).unwrap();
2006        someip_props.set_size_of_string_length(None).unwrap();
2007        someip_props.set_size_of_struct_length(None).unwrap();
2008        someip_props.set_size_of_union_length(None).unwrap();
2009        assert_eq!(someip_props.legacy_strings(), None);
2010        assert_eq!(someip_props.interface_version(), None);
2011        assert_eq!(someip_props.dynamic_length(), None);
2012        assert_eq!(someip_props.message_type(), None);
2013        assert_eq!(someip_props.size_of_array_length(), None);
2014        assert_eq!(someip_props.size_of_string_length(), None);
2015        assert_eq!(someip_props.size_of_struct_length(), None);
2016        assert_eq!(someip_props.size_of_union_length(), None);
2017
2018        assert!(SomeIpTransformationISignalProps::try_from(someip_props.element().clone()).is_ok());
2019
2020        assert_eq!(signal.transformation_isignal_props().count(), 2);
2021        let mut props_iter = signal.transformation_isignal_props();
2022        assert_eq!(props_iter.next().unwrap().element(), e2e_props.element());
2023        assert_eq!(props_iter.next().unwrap().element(), someip_props.element());
2024    }
2025}