autosar_data_abstraction/communication/
data_transformation.rs

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