autosar_data_abstraction/communication/signal/
mod.rs

1use crate::IdentifiableAbstractionElement;
2use crate::communication::{
3    AbstractPhysicalChannel, CommunicationDirection, DataTransformation, EndToEndTransformationISignalProps,
4    PhysicalChannel, SomeIpTransformationISignalProps, TransformationTechnology,
5};
6use crate::datatype::{CompuMethod, DataConstr, SwBaseType, Unit, ValueSpecification};
7use crate::{
8    AbstractionElement, ArPackage, AutosarAbstractionError, EcuInstance, abstraction_element,
9    communication::ISignalToIPduMapping, make_unique_name,
10};
11use autosar_data::{AutosarDataError, Element, ElementName, EnumItem, WeakElement};
12
13use super::TransformationISignalProps;
14
15/// Signal of the Interaction Layer
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct ISignal(Element);
18abstraction_element!(ISignal, ISignal);
19impl IdentifiableAbstractionElement for ISignal {}
20
21impl ISignal {
22    pub(crate) fn new(
23        name: &str,
24        package: &ArPackage,
25        bit_length: u64,
26        syssignal: &SystemSignal,
27        datatype: Option<&SwBaseType>,
28    ) -> Result<Self, AutosarAbstractionError> {
29        if bit_length > u64::from(u32::MAX) * 8 {
30            // max bit_length is 2^32 bytes
31            return Err(AutosarAbstractionError::InvalidParameter(format!(
32                "isignal {name}: bit length {bit_length} is too big"
33            )));
34        }
35
36        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
37        let elem_isignal = pkg_elements.create_named_sub_element(ElementName::ISignal, name)?;
38
39        elem_isignal
40            .create_sub_element(ElementName::DataTypePolicy)?
41            .set_character_data(EnumItem::Override)?;
42
43        let isignal = Self(elem_isignal);
44        isignal.set_length(bit_length)?;
45        isignal.set_system_signal(syssignal)?;
46
47        if let Some(datatype) = datatype {
48            isignal.set_datatype(datatype)?;
49        }
50
51        Ok(isignal)
52    }
53
54    /// set the data type for this signal
55    pub fn set_datatype(&self, datatype: &SwBaseType) -> Result<(), AutosarAbstractionError> {
56        self.element()
57            .get_or_create_sub_element(ElementName::NetworkRepresentationProps)?
58            .get_or_create_sub_element(ElementName::SwDataDefPropsVariants)?
59            .get_or_create_sub_element(ElementName::SwDataDefPropsConditional)?
60            .get_or_create_sub_element(ElementName::BaseTypeRef)?
61            .set_reference_target(datatype.element())?;
62        Ok(())
63    }
64
65    /// get the data type of this signal
66    #[must_use]
67    pub fn datatype(&self) -> Option<SwBaseType> {
68        self.element()
69            .get_sub_element(ElementName::NetworkRepresentationProps)?
70            .get_sub_element(ElementName::SwDataDefPropsVariants)?
71            .get_sub_element(ElementName::SwDataDefPropsConditional)?
72            .get_sub_element(ElementName::BaseTypeRef)?
73            .get_reference_target()
74            .ok()?
75            .try_into()
76            .ok()
77    }
78
79    /// set the length of this signal in bits
80    pub fn set_length(&self, bit_length: u64) -> Result<(), AutosarAbstractionError> {
81        self.element()
82            .get_or_create_sub_element(ElementName::Length)?
83            .set_character_data(bit_length)?;
84        Ok(())
85    }
86
87    /// get the length of this signal in bits
88    #[must_use]
89    pub fn length(&self) -> Option<u64> {
90        self.element()
91            .get_sub_element(ElementName::Length)?
92            .character_data()?
93            .parse_integer()
94    }
95
96    /// set the init value for this signal
97    ///
98    /// only `NumericalValueSpecification`, `TextValueSpecification` or `ArrayValueSpecification` are permitted here
99    pub fn set_init_value<T: Into<ValueSpecification>>(
100        &self,
101        value_spec: Option<T>,
102    ) -> Result<(), AutosarAbstractionError> {
103        if let Some(value_spec) = value_spec {
104            let value_spec: ValueSpecification = value_spec.into();
105            if !matches!(
106                value_spec,
107                ValueSpecification::Numerical(_) | ValueSpecification::Text(_) | ValueSpecification::Array(_)
108            ) {
109                return Err(AutosarAbstractionError::InvalidParameter(
110                "The init value must be a NumericalValueSpecification, TextValueSpecification or ArrayValueSpecification".to_string(),
111            ));
112            }
113            let init_value_elem = self.element().get_or_create_sub_element(ElementName::InitValue)?;
114            value_spec.store(&init_value_elem)?;
115        } else {
116            // remove the init value element if it exists
117            let _ = self.element().remove_sub_element_kind(ElementName::InitValue);
118        }
119        Ok(())
120    }
121
122    /// get the init value for this signal
123    #[must_use]
124    pub fn init_value(&self) -> Option<ValueSpecification> {
125        let init_value_elem = self
126            .element()
127            .get_sub_element(ElementName::InitValue)?
128            .get_sub_element_at(0)?;
129        ValueSpecification::load(&init_value_elem)
130    }
131
132    /// set the system signal that corresponds to this signal
133    pub fn set_system_signal(&self, syssignal: &SystemSignal) -> Result<(), AutosarAbstractionError> {
134        self.element()
135            .get_or_create_sub_element(ElementName::SystemSignalRef)?
136            .set_reference_target(syssignal.element())?;
137        Ok(())
138    }
139
140    /// get the system signal that corresponds to this isignal
141    #[must_use]
142    pub fn system_signal(&self) -> Option<SystemSignal> {
143        self.element()
144            .get_sub_element(ElementName::SystemSignalRef)?
145            .get_reference_target()
146            .ok()?
147            .try_into()
148            .ok()
149    }
150
151    /// an iterator over all `ISignalToIPduMapping` for this signal
152    ///
153    /// Usually a signal should only be mapped to a single PDU,
154    /// so this iterator is expected to return either zero or one item in ordinary cases.
155    #[must_use]
156    pub fn mappings(&self) -> Vec<ISignalToIPduMapping> {
157        let model_result = self.element().model();
158        let path_result = self.element().path();
159        if let (Ok(model), Ok(path)) = (model_result, path_result) {
160            model
161                .get_references_to(&path)
162                .iter()
163                .filter_map(|e| {
164                    e.upgrade()
165                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
166                        .and_then(|elem| ISignalToIPduMapping::try_from(elem).ok())
167                })
168                .collect()
169        } else {
170            vec![]
171        }
172    }
173
174    /// get the signal group that contains this signal, if any
175    #[must_use]
176    pub fn signal_group(&self) -> Option<ISignalGroup> {
177        let path = self.element().path().ok()?;
178        let referrers = self.element().model().ok()?.get_references_to(&path);
179
180        for elem in referrers
181            .iter()
182            .filter_map(|weak| weak.upgrade().and_then(|elem| elem.named_parent().ok().flatten()))
183        {
184            if let Ok(grp) = ISignalGroup::try_from(elem) {
185                return Some(grp);
186            }
187        }
188        None
189    }
190
191    /// list all `ISignalTriggering`s that trigger this signal
192    #[must_use]
193    pub fn signal_triggerings(&self) -> Vec<ISignalTriggering> {
194        let model_result = self.element().model();
195        let path_result = self.element().path();
196        if let (Ok(model), Ok(path)) = (model_result, path_result) {
197            model
198                .get_references_to(&path)
199                .iter()
200                .filter_map(|e| {
201                    e.upgrade()
202                        .and_then(|ref_elem| ref_elem.named_parent().ok().flatten())
203                        .and_then(|elem| ISignalTriggering::try_from(elem).ok())
204                })
205                .collect()
206        } else {
207            vec![]
208        }
209    }
210
211    /// add a data transformation to this signal
212    pub fn add_data_transformation(
213        &self,
214        data_transformation: &DataTransformation,
215    ) -> Result<(), AutosarAbstractionError> {
216        let transformations = self
217            .element()
218            .get_or_create_sub_element(ElementName::DataTransformations)?;
219        transformations
220            .create_sub_element(ElementName::DataTransformationRefConditional)?
221            .create_sub_element(ElementName::DataTransformationRef)?
222            .set_reference_target(data_transformation.element())?;
223
224        Ok(())
225    }
226
227    /// get all data transformations that are applied to this signal
228    pub fn data_transformations(&self) -> impl Iterator<Item = DataTransformation> + Send + 'static {
229        self.element()
230            .get_sub_element(ElementName::DataTransformations)
231            .into_iter()
232            .flat_map(|elem| elem.sub_elements())
233            .filter_map(|elem| elem.get_sub_element(ElementName::DataTransformationRef))
234            .filter_map(|elem| elem.get_reference_target().ok())
235            .filter_map(|elem| elem.try_into().ok())
236    }
237
238    /// create E2E transformation properties for this signal
239    pub fn create_e2e_transformation_isignal_props(
240        &self,
241        transformer: &TransformationTechnology,
242    ) -> Result<EndToEndTransformationISignalProps, AutosarAbstractionError> {
243        let tsp = self
244            .element()
245            .get_or_create_sub_element(ElementName::TransformationISignalPropss)?;
246        EndToEndTransformationISignalProps::new(tsp, transformer)
247    }
248
249    /// create SomeIp transformation properties for this signal
250    pub fn create_someip_transformation_isignal_props(
251        &self,
252        transformer: &TransformationTechnology,
253    ) -> Result<SomeIpTransformationISignalProps, AutosarAbstractionError> {
254        let tsp = self
255            .element()
256            .get_or_create_sub_element(ElementName::TransformationISignalPropss)?;
257        SomeIpTransformationISignalProps::new(tsp, transformer)
258    }
259
260    /// get all transformation properties that are applied to this signal
261    pub fn transformation_isignal_props(&self) -> impl Iterator<Item = TransformationISignalProps> + Send + 'static {
262        self.element()
263            .get_sub_element(ElementName::TransformationISignalPropss)
264            .into_iter()
265            .flat_map(|elem| elem.sub_elements())
266            .filter_map(|elem| TransformationISignalProps::try_from(elem).ok())
267    }
268}
269
270//##################################################################
271
272/// The system signal represents the communication system's view of data exchanged between SW components which reside on different ECUs
273///
274/// Use [`ArPackage::create_system_signal`] to create a new system signal
275#[derive(Debug, Clone, PartialEq, Eq, Hash)]
276pub struct SystemSignal(Element);
277abstraction_element!(SystemSignal, SystemSignal);
278impl IdentifiableAbstractionElement for SystemSignal {}
279
280impl SystemSignal {
281    /// Create a new system signal in the given package
282    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
283        let package_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
284        let elem_syssignal = package_elements.create_named_sub_element(ElementName::SystemSignal, name)?;
285
286        Ok(Self(elem_syssignal))
287    }
288
289    /// get the signal group that contains this signal
290    pub fn signal_group(&self) -> Option<SystemSignalGroup> {
291        let path = self.element().path().ok()?;
292        let referrers = self.element().model().ok()?.get_references_to(&path);
293        for elem in referrers
294            .iter()
295            .filter_map(WeakElement::upgrade)
296            .filter_map(|refelem| refelem.named_parent().ok().flatten())
297        {
298            if let Ok(grp) = SystemSignalGroup::try_from(elem) {
299                return Some(grp);
300            }
301        }
302        None
303    }
304
305    /// set the unit for this signal
306    pub fn set_unit(&self, unit: &Unit) -> Result<(), AutosarAbstractionError> {
307        self.element()
308            .get_or_create_sub_element(ElementName::PhysicalProps)?
309            .get_or_create_sub_element(ElementName::SwDataDefPropsVariants)?
310            .get_or_create_sub_element(ElementName::SwDataDefPropsConditional)?
311            .get_or_create_sub_element(ElementName::UnitRef)?
312            .set_reference_target(unit.element())?;
313        Ok(())
314    }
315
316    /// get the unit for this signal
317    #[must_use]
318    pub fn unit(&self) -> Option<Unit> {
319        self.element()
320            .get_sub_element(ElementName::PhysicalProps)?
321            .get_sub_element(ElementName::SwDataDefPropsVariants)?
322            .get_sub_element(ElementName::SwDataDefPropsConditional)?
323            .get_sub_element(ElementName::UnitRef)?
324            .get_reference_target()
325            .ok()?
326            .try_into()
327            .ok()
328    }
329
330    /// set the compu method for this signal
331    pub fn set_compu_method(&self, compu_method: &CompuMethod) -> Result<(), AutosarAbstractionError> {
332        self.element()
333            .get_or_create_sub_element(ElementName::PhysicalProps)?
334            .get_or_create_sub_element(ElementName::SwDataDefPropsVariants)?
335            .get_or_create_sub_element(ElementName::SwDataDefPropsConditional)?
336            .get_or_create_sub_element(ElementName::CompuMethodRef)?
337            .set_reference_target(compu_method.element())?;
338        Ok(())
339    }
340
341    /// get the compu method for this signal
342    #[must_use]
343    pub fn compu_method(&self) -> Option<CompuMethod> {
344        self.element()
345            .get_sub_element(ElementName::PhysicalProps)?
346            .get_sub_element(ElementName::SwDataDefPropsVariants)?
347            .get_sub_element(ElementName::SwDataDefPropsConditional)?
348            .get_sub_element(ElementName::CompuMethodRef)?
349            .get_reference_target()
350            .ok()?
351            .try_into()
352            .ok()
353    }
354
355    /// set the data constraint for this signal
356    pub fn set_data_constr(&self, data_constr: &DataConstr) -> Result<(), AutosarAbstractionError> {
357        self.element()
358            .get_or_create_sub_element(ElementName::PhysicalProps)?
359            .get_or_create_sub_element(ElementName::SwDataDefPropsVariants)?
360            .get_or_create_sub_element(ElementName::SwDataDefPropsConditional)?
361            .get_or_create_sub_element(ElementName::DataConstrRef)?
362            .set_reference_target(data_constr.element())?;
363        Ok(())
364    }
365
366    /// get the data constraint for this signal
367    #[must_use]
368    pub fn data_constr(&self) -> Option<DataConstr> {
369        self.element()
370            .get_sub_element(ElementName::PhysicalProps)?
371            .get_sub_element(ElementName::SwDataDefPropsVariants)?
372            .get_sub_element(ElementName::SwDataDefPropsConditional)?
373            .get_sub_element(ElementName::DataConstrRef)?
374            .get_reference_target()
375            .ok()?
376            .try_into()
377            .ok()
378    }
379}
380
381//##################################################################
382
383/// An `ISignalGroup` groups signals that should always be kept together
384#[derive(Debug, Clone, PartialEq, Eq, Hash)]
385pub struct ISignalGroup(Element);
386abstraction_element!(ISignalGroup, ISignalGroup);
387impl IdentifiableAbstractionElement for ISignalGroup {}
388
389impl ISignalGroup {
390    pub(crate) fn new(
391        name: &str,
392        package: &ArPackage,
393        system_signal_group: &SystemSignalGroup,
394    ) -> Result<Self, AutosarAbstractionError> {
395        let sig_pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
396        let elem_isiggrp = sig_pkg_elements.create_named_sub_element(ElementName::ISignalGroup, name)?;
397
398        elem_isiggrp
399            .create_sub_element(ElementName::SystemSignalGroupRef)?
400            .set_reference_target(system_signal_group.element())?;
401
402        Ok(Self(elem_isiggrp))
403    }
404
405    /// Add a signal to the signal group
406    pub fn add_signal(&self, signal: &ISignal) -> Result<(), AutosarAbstractionError> {
407        // make sure the relation of signal to signal group is maintained for the referenced system signal
408        let syssig_grp_of_signal = signal.system_signal().and_then(|ss| ss.signal_group());
409        let syssig_grp = self.system_signal_group();
410        if syssig_grp != syssig_grp_of_signal {
411            return Err(AutosarAbstractionError::InvalidParameter(
412                "The isignal and the system signal must both be part of corresponding signal groups".to_string(),
413            ));
414        }
415
416        let isrefs = self.element().get_or_create_sub_element(ElementName::ISignalRefs)?;
417
418        // check if the signal already exists in isrefs?
419
420        isrefs
421            .create_sub_element(ElementName::ISignalRef)?
422            .set_reference_target(signal.element())?;
423
424        Ok(())
425    }
426
427    /// get the system signal group that is associated with this signal group
428    #[must_use]
429    pub fn system_signal_group(&self) -> Option<SystemSignalGroup> {
430        self.element()
431            .get_sub_element(ElementName::SystemSignalGroupRef)?
432            .get_reference_target()
433            .ok()?
434            .try_into()
435            .ok()
436    }
437
438    /// Iterator over all [`ISignal`]s in this group
439    ///
440    /// # Example
441    pub fn signals(&self) -> impl Iterator<Item = ISignal> + Send + 'static {
442        self.element()
443            .get_sub_element(ElementName::ISignalRefs)
444            .into_iter()
445            .flat_map(|elem| elem.sub_elements())
446            .filter_map(|elem| {
447                elem.get_reference_target()
448                    .ok()
449                    .and_then(|elem| ISignal::try_from(elem).ok())
450            })
451    }
452
453    /// add a data transformation to this signal group
454    pub fn add_data_transformation(
455        &self,
456        data_transformation: &DataTransformation,
457    ) -> Result<(), AutosarAbstractionError> {
458        let cbst = self
459            .element()
460            .get_or_create_sub_element(ElementName::ComBasedSignalGroupTransformations)?;
461        cbst.create_sub_element(ElementName::DataTransformationRefConditional)?
462            .create_sub_element(ElementName::DataTransformationRef)?
463            .set_reference_target(data_transformation.element())?;
464        Ok(())
465    }
466
467    /// get all data transformations that are applied to this signal group
468    pub fn data_transformations(&self) -> impl Iterator<Item = DataTransformation> + Send + 'static {
469        self.element()
470            .get_sub_element(ElementName::ComBasedSignalGroupTransformations)
471            .into_iter()
472            .flat_map(|elem| elem.sub_elements())
473            .filter_map(|elem| elem.get_sub_element(ElementName::DataTransformationRef))
474            .filter_map(|elem| elem.get_reference_target().ok())
475            .filter_map(|elem| elem.try_into().ok())
476    }
477
478    /// create E2E transformation properties for this signal group
479    pub fn create_e2e_transformation_isignal_props(
480        &self,
481        transformer: &TransformationTechnology,
482    ) -> Result<EndToEndTransformationISignalProps, AutosarAbstractionError> {
483        let tsp = self
484            .element()
485            .get_or_create_sub_element(ElementName::TransformationISignalPropss)?;
486        EndToEndTransformationISignalProps::new(tsp, transformer)
487    }
488
489    /// create SomeIp transformation properties for this signal group
490    pub fn create_someip_transformation_isignal_props(
491        &self,
492        transformer: &TransformationTechnology,
493    ) -> Result<SomeIpTransformationISignalProps, AutosarAbstractionError> {
494        let tsp = self
495            .element()
496            .get_or_create_sub_element(ElementName::TransformationISignalPropss)?;
497        SomeIpTransformationISignalProps::new(tsp, transformer)
498    }
499
500    /// get all transformation properties that are applied to this signal group
501    pub fn transformation_isignal_props(&self) -> impl Iterator<Item = TransformationISignalProps> + Send + 'static {
502        self.element()
503            .get_sub_element(ElementName::TransformationISignalPropss)
504            .into_iter()
505            .flat_map(|elem| elem.sub_elements())
506            .filter_map(|elem| TransformationISignalProps::try_from(elem).ok())
507    }
508}
509
510//##################################################################
511
512/// A signal group refers to a set of signals that shall always be kept together. A signal group is used to
513/// guarantee the atomic transfer of AUTOSAR composite data types.
514///
515/// Use [`ArPackage::create_system_signal_group`] to create a new system signal group
516#[derive(Debug, Clone, PartialEq, Eq, Hash)]
517pub struct SystemSignalGroup(Element);
518abstraction_element!(SystemSignalGroup, SystemSignalGroup);
519impl IdentifiableAbstractionElement for SystemSignalGroup {}
520
521impl SystemSignalGroup {
522    /// Create a new system signal group
523    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
524        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
525        let signalgroup = pkg_elements.create_named_sub_element(ElementName::SystemSignalGroup, name)?;
526
527        Ok(Self(signalgroup))
528    }
529
530    /// Add a signal to the signal group
531    pub fn add_signal(&self, signal: &SystemSignal) -> Result<(), AutosarAbstractionError> {
532        let ssrefs = self
533            .element()
534            .get_or_create_sub_element(ElementName::SystemSignalRefs)?;
535
536        // check if the signal already exists in ssrefs?
537
538        ssrefs
539            .create_sub_element(ElementName::SystemSignalRef)?
540            .set_reference_target(signal.element())?;
541
542        Ok(())
543    }
544
545    /// Iterator over all [`SystemSignal`]s in this group
546    pub fn signals(&self) -> impl Iterator<Item = SystemSignal> + Send + 'static {
547        self.element()
548            .get_sub_element(ElementName::SystemSignalRefs)
549            .into_iter()
550            .flat_map(|elem| elem.sub_elements())
551            .filter_map(|elem| {
552                elem.get_reference_target()
553                    .ok()
554                    .and_then(|elem| SystemSignal::try_from(elem).ok())
555            })
556    }
557}
558
559//##################################################################
560
561/// an `ISignalTriggering` triggers a signal in a PDU
562#[derive(Debug, Clone, PartialEq, Eq, Hash)]
563pub struct ISignalTriggering(Element);
564abstraction_element!(ISignalTriggering, ISignalTriggering);
565impl IdentifiableAbstractionElement for ISignalTriggering {}
566
567impl ISignalTriggering {
568    pub(crate) fn new<T: AbstractPhysicalChannel>(
569        signal: &ISignal,
570        channel: &T,
571    ) -> Result<Self, AutosarAbstractionError> {
572        let model = channel.element().model()?;
573        let base_path = channel.element().path()?;
574        let signal_name = signal
575            .name()
576            .ok_or(AutosarAbstractionError::InvalidParameter("invalid signal".to_string()))?;
577        let pt_name = format!("ST_{signal_name}");
578        let pt_name = make_unique_name(&model, &base_path, &pt_name);
579
580        let triggerings = channel
581            .element()
582            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
583        let st_elem = triggerings.create_named_sub_element(ElementName::ISignalTriggering, &pt_name)?;
584        st_elem
585            .create_sub_element(ElementName::ISignalRef)?
586            .set_reference_target(signal.element())?;
587
588        let pt = Self(st_elem);
589
590        Ok(pt)
591    }
592
593    pub(crate) fn new_group(
594        signal_group: &ISignalGroup,
595        channel: &PhysicalChannel,
596    ) -> Result<Self, AutosarAbstractionError> {
597        let model = channel.element().model()?;
598        let base_path = channel.element().path()?;
599        let signal_name = signal_group.name().ok_or(AutosarAbstractionError::InvalidParameter(
600            "invalid signal group".to_string(),
601        ))?;
602        let pt_name = format!("ST_{signal_name}");
603        let pt_name = make_unique_name(&model, &base_path, &pt_name);
604
605        let triggerings = channel
606            .element()
607            .get_or_create_sub_element(ElementName::ISignalTriggerings)?;
608        let st_elem = triggerings.create_named_sub_element(ElementName::ISignalTriggering, &pt_name)?;
609        st_elem
610            .create_sub_element(ElementName::ISignalGroupRef)?
611            .set_reference_target(signal_group.element())?;
612
613        let pt = Self(st_elem);
614
615        Ok(pt)
616    }
617
618    /// get the physical channel that contains this signal triggering
619    pub fn physical_channel(&self) -> Result<PhysicalChannel, AutosarAbstractionError> {
620        let channel_elem = self.element().named_parent()?.ok_or(AutosarDataError::ItemDeleted)?;
621        PhysicalChannel::try_from(channel_elem)
622    }
623
624    /// connect this signal triggering to an ECU
625    pub fn connect_to_ecu(
626        &self,
627        ecu: &EcuInstance,
628        direction: CommunicationDirection,
629    ) -> Result<ISignalPort, AutosarAbstractionError> {
630        for signal_port in self.signal_ports() {
631            if let (Ok(existing_ecu), Some(existing_direction)) =
632                (signal_port.ecu(), signal_port.communication_direction())
633            {
634                if existing_ecu == *ecu && existing_direction == direction {
635                    return Ok(signal_port);
636                }
637            }
638        }
639
640        let channel = self.physical_channel()?;
641        let connector = channel
642            .ecu_connector(ecu)
643            .ok_or(AutosarAbstractionError::InvalidParameter(
644                "The ECU is not connected to the channel".to_string(),
645            ))?;
646
647        let name = self.name().ok_or(AutosarDataError::ItemDeleted)?;
648        let suffix = match direction {
649            CommunicationDirection::In => "Rx",
650            CommunicationDirection::Out => "Tx",
651        };
652        let port_name = format!("{name}_{suffix}",);
653        let sp_elem = connector
654            .element()
655            .get_or_create_sub_element(ElementName::EcuCommPortInstances)?
656            .create_named_sub_element(ElementName::ISignalPort, &port_name)?;
657        sp_elem
658            .create_sub_element(ElementName::CommunicationDirection)?
659            .set_character_data::<EnumItem>(direction.into())?;
660
661        self.element()
662            .get_or_create_sub_element(ElementName::ISignalPortRefs)?
663            .create_sub_element(ElementName::ISignalPortRef)?
664            .set_reference_target(&sp_elem)?;
665
666        Ok(ISignalPort(sp_elem))
667    }
668
669    /// create an iterator over all signal ports that are connected to this signal triggering
670    pub fn signal_ports(&self) -> impl Iterator<Item = ISignalPort> + Send + 'static {
671        self.element()
672            .get_sub_element(ElementName::ISignalPortRefs)
673            .into_iter()
674            .flat_map(|elem| elem.sub_elements())
675            .filter_map(|elem| {
676                elem.get_reference_target()
677                    .ok()
678                    .and_then(|elem| ISignalPort::try_from(elem).ok())
679            })
680    }
681}
682
683//##################################################################
684
685/// The `ISignalPort` allows an ECU to send or receive a Signal
686#[derive(Debug, Clone, PartialEq, Eq, Hash)]
687pub struct ISignalPort(Element);
688abstraction_element!(ISignalPort, ISignalPort);
689impl IdentifiableAbstractionElement for ISignalPort {}
690
691impl ISignalPort {
692    /// get the ECU that is connected to this signal port
693    pub fn ecu(&self) -> Result<EcuInstance, AutosarAbstractionError> {
694        let comm_connector_elem = self.element().named_parent()?.unwrap();
695        let ecu_elem = comm_connector_elem.named_parent()?.unwrap();
696        EcuInstance::try_from(ecu_elem)
697    }
698
699    /// set the communication direction of this port
700    pub fn set_communication_direction(
701        &self,
702        direction: CommunicationDirection,
703    ) -> Result<(), AutosarAbstractionError> {
704        self.element()
705            .get_or_create_sub_element(ElementName::CommunicationDirection)?
706            .set_character_data::<EnumItem>(direction.into())?;
707        Ok(())
708    }
709
710    /// get the communication direction of this port
711    #[must_use]
712    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
713        self.element()
714            .get_sub_element(ElementName::CommunicationDirection)?
715            .character_data()?
716            .enum_value()?
717            .try_into()
718            .ok()
719    }
720}
721
722//##################################################################
723
724/// The `TransferProperty` defines if or how the signal influences the transfer of the PDU
725#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
726pub enum TransferProperty {
727    /// The signal is pending; it does not trigger the transfer of the PDU
728    Pending,
729    /// The signal triggers the transfer of the PDU
730    Triggered,
731    /// The signal triggers the transfer of the PDU if the value changes
732    TriggeredOnChange,
733    /// The signal triggers the transfer of the PDU if the value changes without repetition
734    TriggeredOnChangeWithoutRepetition,
735    /// The signal triggers the transfer of the PDU without repetition
736    TriggeredWithoutRepetition,
737}
738
739impl From<TransferProperty> for EnumItem {
740    fn from(value: TransferProperty) -> Self {
741        match value {
742            TransferProperty::Pending => EnumItem::Pending,
743            TransferProperty::Triggered => EnumItem::Triggered,
744            TransferProperty::TriggeredOnChange => EnumItem::TriggeredOnChange,
745            TransferProperty::TriggeredOnChangeWithoutRepetition => EnumItem::TriggeredOnChangeWithoutRepetition,
746            TransferProperty::TriggeredWithoutRepetition => EnumItem::TriggeredWithoutRepetition,
747        }
748    }
749}
750
751impl TryFrom<EnumItem> for TransferProperty {
752    type Error = AutosarAbstractionError;
753
754    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
755        match value {
756            EnumItem::Pending => Ok(TransferProperty::Pending),
757            EnumItem::Triggered => Ok(TransferProperty::Triggered),
758            EnumItem::TriggeredOnChange => Ok(TransferProperty::TriggeredOnChange),
759            EnumItem::TriggeredOnChangeWithoutRepetition => Ok(TransferProperty::TriggeredOnChangeWithoutRepetition),
760            EnumItem::TriggeredWithoutRepetition => Ok(TransferProperty::TriggeredWithoutRepetition),
761            _ => Err(AutosarAbstractionError::ValueConversionError {
762                value: value.to_string(),
763                dest: "TransferProperty".to_string(),
764            }),
765        }
766    }
767}
768
769//##################################################################
770
771#[cfg(test)]
772mod tests {
773    use super::*;
774    use crate::{
775        AutosarModelAbstraction, ByteOrder, SystemCategory,
776        communication::{
777            AbstractFrame, AbstractPdu, CanAddressingMode, CanFrameType, DataTransformationSet, SomeIpMessageType,
778            SomeIpTransformationTechnologyConfig, TransformationTechnologyConfig,
779        },
780        datatype::{BaseTypeEncoding, CompuMethodContent, NumericalValueSpecification, SwBaseType, Unit},
781    };
782    use autosar_data::AutosarVersion;
783
784    #[test]
785    fn test_signal() {
786        let model = AutosarModelAbstraction::create("test.arxml", AutosarVersion::LATEST);
787        let package = model.get_or_create_package("/test").unwrap();
788        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
789        let unit = Unit::new("unit", &package, Some("Unit Name")).unwrap();
790        let compu_method = CompuMethod::new("compu_method", &package, CompuMethodContent::Identical).unwrap();
791        let data_constr = DataConstr::new("data_constr", &package).unwrap();
792        let sw_base_type =
793            SwBaseType::new("sw_base_type", &package, 8, BaseTypeEncoding::None, None, None, None).unwrap();
794
795        let sys_signal = package.create_system_signal("sys_signal").unwrap();
796        let signal = system
797            .create_isignal("signal", &package, 8, &sys_signal, Some(&sw_base_type))
798            .unwrap();
799
800        sys_signal.set_unit(&unit).unwrap();
801        sys_signal.set_compu_method(&compu_method).unwrap();
802        sys_signal.set_data_constr(&data_constr).unwrap();
803
804        assert_eq!(signal.length(), Some(8));
805        assert_eq!(signal.datatype(), Some(sw_base_type));
806        assert_eq!(signal.system_signal(), Some(sys_signal.clone()));
807        assert_eq!(sys_signal.unit(), Some(unit));
808        assert_eq!(sys_signal.compu_method(), Some(compu_method));
809        assert_eq!(sys_signal.data_constr(), Some(data_constr));
810
811        // mappings
812        assert_eq!(signal.mappings().len(), 0);
813        let ipdu = system.create_isignal_ipdu("ipdu", &package, 8).unwrap();
814        let mapping = ipdu
815            .map_signal(
816                &signal,
817                0,
818                ByteOrder::MostSignificantByteLast,
819                None,
820                TransferProperty::Triggered,
821            )
822            .unwrap();
823        assert_eq!(signal.mappings().len(), 1);
824        assert_eq!(signal.mappings()[0], mapping.clone());
825        assert_eq!(mapping.signal().unwrap(), signal);
826
827        // init value
828        let init_value = NumericalValueSpecification {
829            label: None,
830            value: 0.0,
831        };
832        signal.set_init_value(Some(init_value.clone())).unwrap();
833        assert_eq!(signal.init_value(), Some(init_value.into()));
834
835        signal.set_init_value::<ValueSpecification>(None).unwrap();
836        assert_eq!(signal.init_value(), None);
837    }
838
839    #[test]
840    fn test_signal_data_transformations() {
841        let model = AutosarModelAbstraction::create("test.arxml", AutosarVersion::LATEST);
842        let package = model.get_or_create_package("/test").unwrap();
843        let sw_base_type =
844            SwBaseType::new("sw_base_type", &package, 8, BaseTypeEncoding::None, None, None, None).unwrap();
845        let signal = ISignal::new(
846            "signal",
847            &package,
848            8,
849            &SystemSignal::new("sys_signal", &package).unwrap(),
850            Some(&sw_base_type),
851        )
852        .unwrap();
853
854        let dts = DataTransformationSet::new("data_transformation_set", &package).unwrap();
855        let transformer = dts
856            .create_transformation_technology(
857                "someip_xf",
858                &TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
859                    alignment: 8,
860                    byte_order: ByteOrder::MostSignificantByteFirst,
861                    interface_version: 1,
862                }),
863            )
864            .unwrap();
865        let data_transformation = dts
866            .create_data_transformation("someip_trans", &[&transformer], false)
867            .unwrap();
868
869        signal.add_data_transformation(&data_transformation).unwrap();
870
871        assert_eq!(signal.data_transformations().count(), 1);
872        assert_eq!(signal.data_transformations().next(), Some(data_transformation));
873
874        let someip_props = signal.create_someip_transformation_isignal_props(&transformer).unwrap();
875        someip_props.set_legacy_strings(Some(true)).unwrap();
876        someip_props.set_interface_version(Some(1)).unwrap();
877        someip_props.set_dynamic_length(Some(true)).unwrap();
878        someip_props.set_message_type(Some(SomeIpMessageType::Request)).unwrap();
879        someip_props.set_size_of_array_length(Some(8)).unwrap();
880        someip_props.set_size_of_string_length(Some(16)).unwrap();
881        someip_props.set_size_of_struct_length(Some(32)).unwrap();
882        someip_props.set_size_of_union_length(Some(64)).unwrap();
883
884        assert_eq!(signal.transformation_isignal_props().count(), 1);
885    }
886
887    #[test]
888    fn test_signal_group_data_transformations() {
889        let model = AutosarModelAbstraction::create("test.arxml", AutosarVersion::LATEST);
890        let package = model.get_or_create_package("/test").unwrap();
891
892        let signal_group = ISignalGroup::new(
893            "signal_group",
894            &package,
895            &SystemSignalGroup::new("sys_signal_group", &package).unwrap(),
896        )
897        .unwrap();
898
899        let dts = DataTransformationSet::new("data_transformation_set", &package).unwrap();
900        let transformer = dts
901            .create_transformation_technology(
902                "someip_xf",
903                &TransformationTechnologyConfig::SomeIp(SomeIpTransformationTechnologyConfig {
904                    alignment: 8,
905                    byte_order: ByteOrder::MostSignificantByteFirst,
906                    interface_version: 1,
907                }),
908            )
909            .unwrap();
910        let data_transformation = dts
911            .create_data_transformation("someip_trans", &[&transformer], false)
912            .unwrap();
913
914        signal_group.add_data_transformation(&data_transformation).unwrap();
915        assert_eq!(signal_group.data_transformations().count(), 1);
916        assert_eq!(signal_group.data_transformations().next(), Some(data_transformation));
917
918        let _someipxf_props = signal_group
919            .create_someip_transformation_isignal_props(&transformer)
920            .unwrap();
921        // the referenced transformer is not an E2E transformer, so no E2E properties can be created
922        let result = signal_group.create_e2e_transformation_isignal_props(&transformer);
923        assert!(result.is_err());
924
925        assert_eq!(signal_group.transformation_isignal_props().count(), 1);
926    }
927
928    #[test]
929    fn test_signal_group() {
930        let model = AutosarModelAbstraction::create("test.arxml", AutosarVersion::LATEST);
931        let package = model.get_or_create_package("/test").unwrap();
932        let sys_signal_group = SystemSignalGroup::new("sys_signal_group", &package).unwrap();
933        let signal_group = ISignalGroup::new("signal_group", &package, &sys_signal_group).unwrap();
934        assert_eq!(signal_group.system_signal_group(), Some(sys_signal_group.clone()));
935
936        let sys_signal = SystemSignal::new("sys_signal", &package).unwrap();
937        let signal = ISignal::new("signal", &package, 8, &sys_signal, None).unwrap();
938        assert_eq!(signal.system_signal(), Some(sys_signal.clone()));
939
940        sys_signal_group.add_signal(&sys_signal).unwrap();
941        assert_eq!(sys_signal.signal_group(), Some(sys_signal_group.clone()));
942        assert_eq!(sys_signal_group.signals().count(), 1);
943
944        signal_group.add_signal(&signal).unwrap();
945        assert_eq!(signal_group.signals().count(), 1);
946    }
947
948    #[test]
949    fn test_signal_triggering() {
950        let model = AutosarModelAbstraction::create("test.arxml", AutosarVersion::LATEST);
951        let package = model.get_or_create_package("/test").unwrap();
952        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
953        let cluster = system.create_can_cluster("cluster", &package, None).unwrap();
954        let channel = cluster.create_physical_channel("channel").unwrap();
955
956        let can_frame = system.create_can_frame("frame", &package, 8).unwrap();
957        channel
958            .trigger_frame(&can_frame, 0x100, CanAddressingMode::Standard, CanFrameType::Can20)
959            .unwrap();
960        let pdu = system.create_isignal_ipdu("pdu", &package, 8).unwrap();
961        can_frame
962            .map_pdu(&pdu, 0, ByteOrder::MostSignificantByteLast, None)
963            .unwrap();
964
965        let sw_base_type =
966            SwBaseType::new("sw_base_type", &package, 8, BaseTypeEncoding::None, None, None, None).unwrap();
967
968        let sys_signal = package.create_system_signal("sys_signal").unwrap();
969        let signal = system
970            .create_isignal("signal", &package, 8, &sys_signal, Some(&sw_base_type))
971            .unwrap();
972
973        pdu.map_signal(
974            &signal,
975            0,
976            ByteOrder::MostSignificantByteLast,
977            None,
978            TransferProperty::Pending,
979        )
980        .unwrap();
981        let pt = pdu.pdu_triggerings()[0].clone();
982
983        // signal triggering
984        let st = signal.signal_triggerings()[0].clone();
985        assert_eq!(st, pt.signal_triggerings().next().unwrap());
986
987        assert_eq!(st.physical_channel().unwrap(), PhysicalChannel::Can(channel.clone()));
988
989        let ecuinstance = system.create_ecu_instance("ecu", &package).unwrap();
990        let controller = ecuinstance.create_can_communication_controller("controller").unwrap();
991        controller.connect_physical_channel("connection", &channel).unwrap();
992
993        assert_eq!(st.signal_ports().count(), 0);
994        let signal_port = st.connect_to_ecu(&ecuinstance, CommunicationDirection::In).unwrap();
995        assert_eq!(st.signal_ports().count(), 1);
996        assert_eq!(signal_port.ecu().unwrap(), ecuinstance);
997        assert_eq!(signal_port.communication_direction(), Some(CommunicationDirection::In));
998        signal_port
999            .set_communication_direction(CommunicationDirection::Out)
1000            .unwrap();
1001        assert_eq!(signal_port.communication_direction(), Some(CommunicationDirection::Out));
1002        signal_port.set_name("new_name").unwrap();
1003        assert_eq!(signal_port.name().unwrap(), "new_name");
1004    }
1005}