autosar_data_abstraction/communication/pdu/
isignal_ipdu.rs

1use crate::communication::{
2    AbstractIpdu, AbstractPdu, CommunicationDirection, DynamicPartAlternative, IPdu, ISignal, ISignalGroup,
3    MultiplexedIPdu, Pdu, PduToFrameMapping, TransferProperty,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, ByteOrder, IdentifiableAbstractionElement,
7    abstraction_element, get_reference_parents, is_used_system_element, make_unique_name,
8};
9use autosar_data::{Element, ElementName, EnumItem};
10
11//##################################################################
12
13/// Trait for PDUs that can contain signals, i.e., ISignalIPdu and NmPdu
14pub trait SignalPdu: AbstractPdu {
15    /// returns an iterator over all signals and signal groups mapped to the PDU
16    fn mapped_signals(&self) -> impl Iterator<Item = ISignalToIPduMapping> + Send + use<Self>;
17
18    /// map a signal to the `ISignalIPdu` or `NmPdu`
19    ///
20    /// If this signal is part of a signal group, then the group must be mapped first
21    fn map_signal(
22        &self,
23        signal: &ISignal,
24        start_position: u32,
25        byte_order: ByteOrder,
26        update_bit: Option<u32>,
27        transfer_property: TransferProperty,
28    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError>;
29
30    /// map a signal group to the PDU
31    fn map_signal_group(&self, signal_group: &ISignalGroup) -> Result<ISignalToIPduMapping, AutosarAbstractionError>;
32}
33
34// helper to verify signal placement for SignalPdus
35pub(crate) fn verify_signal_mapping(
36    pdu: &impl SignalPdu,
37    signal: &ISignal,
38    start_position: u32,
39    byte_order: ByteOrder,
40    update_bit: Option<u32>,
41    signal_name: &String,
42) -> Result<(), AutosarAbstractionError> {
43    let length = pdu.length().unwrap_or(0);
44    let mut validator = SignalMappingValidator::new(length);
45    for mapping in pdu.mapped_signals() {
46        if let (Some(m_signal), Some(m_start_pos), Some(m_byte_order)) =
47            (mapping.signal(), mapping.start_position(), mapping.byte_order())
48        {
49            let len = m_signal.length().unwrap_or(0);
50            validator.add_signal(m_start_pos, len, m_byte_order, mapping.update_bit());
51        }
52    }
53    if !validator.add_signal(start_position, signal.length().unwrap_or(0), byte_order, update_bit) {
54        return Err(AutosarAbstractionError::InvalidParameter(format!(
55            "Cannot map signal {signal_name} to an overlapping position in the pdu"
56        )));
57    }
58
59    // build a bitmap of all signals that are already mapped in this pdu
60    // add the new signal to the validator bitmap to see if it overlaps any existing signals
61    if let Some(signal_group) = signal.signal_group()
62        && !pdu
63            .mapped_signals()
64            .filter_map(|mapping| mapping.signal_group())
65            .any(|grp| grp == signal_group)
66    {
67        return Err(AutosarAbstractionError::InvalidParameter(
68            "Cannot map signal to pdu, because it is part of an unmapped signal group.".to_string(),
69        ));
70    }
71    Ok(())
72}
73
74//##################################################################
75
76/// Represents the `IPdus` handled by Com
77#[derive(Debug, Clone, PartialEq, Eq, Hash)]
78pub struct ISignalIPdu(Element);
79abstraction_element!(ISignalIPdu, ISignalIPdu);
80impl IdentifiableAbstractionElement for ISignalIPdu {}
81
82impl ISignalIPdu {
83    pub(crate) fn new(name: &str, package: &ArPackage, length: u32) -> Result<Self, AutosarAbstractionError> {
84        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
85        let elem_pdu = pkg_elements.create_named_sub_element(ElementName::ISignalIPdu, name)?;
86        elem_pdu
87            .create_sub_element(ElementName::Length)?
88            .set_character_data(length.to_string())?;
89
90        Ok(Self(elem_pdu))
91    }
92
93    /// remove this `ISignalIPdu` from the model
94    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
95        // remove all triggerings of this PDU
96        for pdu_triggering in self.pdu_triggerings() {
97            let _ = pdu_triggering.element().remove_sub_element_kind(ElementName::IPduRef);
98            let _ = pdu_triggering.remove(deep);
99        }
100
101        // remove all signal mappings of this PDU
102        for signal_mapping in self.mapped_signals() {
103            let _ = signal_mapping.remove(deep);
104        }
105
106        let ref_parents = get_reference_parents(self.element())?;
107
108        AbstractionElement::remove(self, deep)?;
109
110        for (named_parent, parent) in ref_parents {
111            match parent.element_name() {
112                ElementName::PduToFrameMapping => {
113                    if let Ok(pdu_to_frame_mapping) = PduToFrameMapping::try_from(parent) {
114                        pdu_to_frame_mapping.remove(deep)?;
115                    }
116                }
117                // Multiplexed IPDUs
118                ElementName::DynamicPartAlternative => {
119                    if let Ok(dynamic_part_alternative) = DynamicPartAlternative::try_from(parent) {
120                        dynamic_part_alternative.remove(deep)?;
121                    }
122                }
123                ElementName::StaticPart => {
124                    if let Ok(multiplexed_ipdu) = MultiplexedIPdu::try_from(named_parent) {
125                        multiplexed_ipdu.remove(deep)?;
126                    }
127                }
128                ElementName::ISignalIPduRefConditional => {
129                    if let Ok(Some(parent_parent)) = parent.parent() {
130                        parent_parent.remove_sub_element(parent)?;
131                    }
132                }
133                _ => {}
134            }
135        }
136
137        Ok(())
138    }
139
140    /// map a signal to the `ISignalIPdu`
141    ///
142    /// If this signal is part of a signal group, then the group must be mapped first
143    pub fn map_signal(
144        &self,
145        signal: &ISignal,
146        start_position: u32,
147        byte_order: ByteOrder,
148        update_bit: Option<u32>,
149        transfer_property: TransferProperty,
150    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
151        let signal_name = signal
152            .name()
153            .ok_or(AutosarAbstractionError::InvalidParameter("invalid signal".to_string()))?;
154
155        verify_signal_mapping(self, signal, start_position, byte_order, update_bit, &signal_name)?;
156
157        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
158        for pt in self.pdu_triggerings() {
159            let st = pt.create_signal_triggering(signal)?;
160            for pdu_port in pt.pdu_ports() {
161                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
162                    st.connect_to_ecu(&ecu, direction)?;
163                }
164            }
165        }
166
167        // create and return the new mapping
168        let model = self.element().model()?;
169        let base_path = self.element().path()?;
170        let name = make_unique_name(&model, &base_path, &signal_name);
171
172        let mappings = self
173            .element()
174            .get_or_create_sub_element(ElementName::ISignalToPduMappings)?;
175
176        ISignalToIPduMapping::new_with_signal(
177            &name,
178            &mappings,
179            signal,
180            start_position,
181            byte_order,
182            update_bit,
183            transfer_property,
184        )
185    }
186
187    /// map a signal group to the PDU
188    pub fn map_signal_group(
189        &self,
190        signal_group: &ISignalGroup,
191    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
192        let signal_group_name = signal_group.name().ok_or(AutosarAbstractionError::InvalidParameter(
193            "invalid signal group".to_string(),
194        ))?;
195
196        // add a pdu triggering for the newly mapped PDU to each frame triggering of this frame
197        for pt in self.pdu_triggerings() {
198            let st = pt.create_signal_group_triggering(signal_group)?;
199            for pdu_port in pt.pdu_ports() {
200                if let (Ok(ecu), Some(direction)) = (pdu_port.ecu(), pdu_port.communication_direction()) {
201                    st.connect_to_ecu(&ecu, direction)?;
202                }
203            }
204        }
205
206        // create and return the new mapping
207        let model = self.element().model()?;
208        let base_path = self.element().path()?;
209        let name = make_unique_name(&model, &base_path, &signal_group_name);
210
211        let mappings = self
212            .element()
213            .get_or_create_sub_element(ElementName::ISignalToPduMappings)?;
214
215        ISignalToIPduMapping::new_with_group(&name, &mappings, signal_group)
216    }
217
218    /// set the transmission timing of the PDU
219    pub fn set_timing(&self, timing_spec: &IpduTiming) -> Result<(), AutosarAbstractionError> {
220        let _ = self
221            .element()
222            .remove_sub_element_kind(ElementName::IPduTimingSpecifications);
223
224        let timing_elem = self
225            .element()
226            .create_sub_element(ElementName::IPduTimingSpecifications)?
227            .create_sub_element(ElementName::IPduTiming)?;
228        if let Some(min_delay) = timing_spec.minimum_delay {
229            timing_elem
230                .create_sub_element(ElementName::MinimumDelay)?
231                .set_character_data(min_delay)?;
232        }
233        if let Some(transmission_mode_true_timing) = &timing_spec.transmission_mode_true_timing {
234            let tmtt_elem = timing_elem
235                .get_or_create_sub_element(ElementName::TransmissionModeDeclaration)?
236                .create_sub_element(ElementName::TransmissionModeTrueTiming)?;
237            Self::set_transmission_mode_timinig(tmtt_elem, transmission_mode_true_timing)?;
238        }
239        if let Some(transmission_mode_false_timing) = &timing_spec.transmission_mode_false_timing {
240            let tmtf_elem = timing_elem
241                .get_or_create_sub_element(ElementName::TransmissionModeDeclaration)?
242                .create_sub_element(ElementName::TransmissionModeFalseTiming)?;
243            Self::set_transmission_mode_timinig(tmtf_elem, transmission_mode_false_timing)?;
244        }
245
246        Ok(())
247    }
248
249    /// Helper function to set the transmission mode timing, used by `ISignalIPdu::set_timing` for both true and false timing
250    fn set_transmission_mode_timinig(
251        timing_element: Element,
252        transmission_mode_timing: &TransmissionModeTiming,
253    ) -> Result<(), AutosarAbstractionError> {
254        if let Some(cyclic_timing) = &transmission_mode_timing.cyclic_timing {
255            let ct_elem = timing_element.create_sub_element(ElementName::CyclicTiming)?;
256            ct_elem
257                .create_sub_element(ElementName::TimePeriod)?
258                .create_sub_element(ElementName::Value)?
259                .set_character_data(cyclic_timing.time_period)?;
260            if let Some(time_offset) = cyclic_timing.time_offset {
261                ct_elem
262                    .create_sub_element(ElementName::TimeOffset)?
263                    .create_sub_element(ElementName::Value)?
264                    .set_character_data(time_offset)?;
265            }
266        }
267        if let Some(event_controlled_timing) = &transmission_mode_timing.event_controlled_timing {
268            let ect_elem = timing_element.create_sub_element(ElementName::EventControlledTiming)?;
269            ect_elem
270                .create_sub_element(ElementName::NumberOfRepetitions)?
271                .set_character_data(u64::from(event_controlled_timing.number_of_repetitions))?;
272            if let Some(repetition_period) = event_controlled_timing.repetition_period {
273                ect_elem
274                    .create_sub_element(ElementName::RepetitionPeriod)?
275                    .create_sub_element(ElementName::Value)?
276                    .set_character_data(repetition_period)?;
277            }
278        }
279
280        Ok(())
281    }
282
283    /// get the transmission timing of the PDU
284    #[must_use]
285    pub fn timing(&self) -> Option<IpduTiming> {
286        let timing_elem = self
287            .element()
288            .get_sub_element(ElementName::IPduTimingSpecifications)?
289            .get_sub_element(ElementName::IPduTiming)?;
290        let minimum_delay = timing_elem
291            .get_sub_element(ElementName::MinimumDelay)
292            .and_then(|md| md.character_data())
293            .and_then(|cdata| cdata.parse_float());
294        let transmission_mode_true_timing = timing_elem
295            .get_sub_element(ElementName::TransmissionModeDeclaration)
296            .and_then(|tmd| tmd.get_sub_element(ElementName::TransmissionModeTrueTiming))
297            .and_then(|tmtt| Self::transmission_mode_timing(&tmtt));
298        let transmission_mode_false_timing = timing_elem
299            .get_sub_element(ElementName::TransmissionModeDeclaration)
300            .and_then(|tmd| tmd.get_sub_element(ElementName::TransmissionModeFalseTiming))
301            .and_then(|tmtf| Self::transmission_mode_timing(&tmtf));
302
303        Some(IpduTiming {
304            minimum_delay,
305            transmission_mode_true_timing,
306            transmission_mode_false_timing,
307        })
308    }
309
310    /// Helper function to get the transmission mode timing, used by `ISignalIPdu::timing` for both true and false modes
311    fn transmission_mode_timing(timing_elem: &Element) -> Option<TransmissionModeTiming> {
312        let cyclic_timing = timing_elem.get_sub_element(ElementName::CyclicTiming).and_then(|ct| {
313            let time_period = ct
314                .get_sub_element(ElementName::TimePeriod)
315                .and_then(|tp| tp.get_sub_element(ElementName::Value))
316                .and_then(|val| val.character_data())
317                .and_then(|cdata| cdata.parse_float());
318            let time_offset = ct
319                .get_sub_element(ElementName::TimeOffset)
320                .and_then(|to| to.get_sub_element(ElementName::Value))
321                .and_then(|val| val.character_data())
322                .and_then(|cdata| cdata.parse_float());
323            time_period.map(|tp| CyclicTiming {
324                time_period: tp,
325                time_offset,
326            })
327        });
328        let event_controlled_timing = timing_elem
329            .get_sub_element(ElementName::EventControlledTiming)
330            .and_then(|ect| {
331                let number_of_repetitions = ect
332                    .get_sub_element(ElementName::NumberOfRepetitions)
333                    .and_then(|nr| nr.character_data())
334                    .and_then(|cdata| cdata.parse_integer());
335                let repetition_period = ect
336                    .get_sub_element(ElementName::RepetitionPeriod)
337                    .and_then(|rp| rp.get_sub_element(ElementName::Value))
338                    .and_then(|val| val.character_data())
339                    .and_then(|cdata| cdata.parse_float());
340                number_of_repetitions.map(|nr| EventControlledTiming {
341                    number_of_repetitions: nr,
342                    repetition_period,
343                })
344            });
345
346        Some(TransmissionModeTiming {
347            cyclic_timing,
348            event_controlled_timing,
349        })
350    }
351}
352
353impl SignalPdu for ISignalIPdu {
354    /// returns an iterator over all signals and signal groups mapped to the PDU
355    fn mapped_signals(&self) -> impl Iterator<Item = ISignalToIPduMapping> + Send + use<> {
356        self.element()
357            .get_sub_element(ElementName::ISignalToPduMappings)
358            .into_iter()
359            .flat_map(|mappings| mappings.sub_elements())
360            .filter_map(|elem| ISignalToIPduMapping::try_from(elem).ok())
361    }
362
363    fn map_signal(
364        &self,
365        signal: &ISignal,
366        start_position: u32,
367        byte_order: ByteOrder,
368        update_bit: Option<u32>,
369        transfer_property: TransferProperty,
370    ) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
371        ISignalIPdu::map_signal(self, signal, start_position, byte_order, update_bit, transfer_property)
372    }
373
374    fn map_signal_group(&self, signal_group: &ISignalGroup) -> Result<ISignalToIPduMapping, AutosarAbstractionError> {
375        ISignalIPdu::map_signal_group(self, signal_group)
376    }
377}
378
379impl AbstractPdu for ISignalIPdu {}
380
381impl AbstractIpdu for ISignalIPdu {}
382
383impl From<ISignalIPdu> for Pdu {
384    fn from(value: ISignalIPdu) -> Self {
385        Pdu::ISignalIPdu(value)
386    }
387}
388
389impl From<ISignalIPdu> for IPdu {
390    fn from(value: ISignalIPdu) -> Self {
391        IPdu::ISignalIPdu(value)
392    }
393}
394
395//##################################################################
396
397/// `ISignalToIPduMapping` connects an `ISignal` or `ISignalGroup` to an `ISignalToIPdu`
398#[derive(Debug, Clone, PartialEq, Eq, Hash)]
399pub struct ISignalToIPduMapping(Element);
400abstraction_element!(ISignalToIPduMapping, ISignalToIPduMapping);
401impl IdentifiableAbstractionElement for ISignalToIPduMapping {}
402
403impl ISignalToIPduMapping {
404    pub(crate) fn new_with_signal(
405        name: &str,
406        mappings: &Element,
407        signal: &ISignal,
408        start_position: u32,
409        byte_order: ByteOrder,
410        update_bit: Option<u32>,
411        transfer_property: TransferProperty,
412    ) -> Result<Self, AutosarAbstractionError> {
413        let signal_mapping = mappings.create_named_sub_element(ElementName::ISignalToIPduMapping, name)?;
414        signal_mapping
415            .create_sub_element(ElementName::ISignalRef)?
416            .set_reference_target(signal.element())?;
417        signal_mapping
418            .create_sub_element(ElementName::PackingByteOrder)?
419            .set_character_data::<EnumItem>(byte_order.into())?;
420        signal_mapping
421            .create_sub_element(ElementName::StartPosition)?
422            .set_character_data(u64::from(start_position))?;
423        signal_mapping
424            .create_sub_element(ElementName::TransferProperty)?
425            .set_character_data::<EnumItem>(transfer_property.into())?;
426        if let Some(update_bit_pos) = update_bit {
427            signal_mapping
428                .create_sub_element(ElementName::UpdateIndicationBitPosition)?
429                .set_character_data(u64::from(update_bit_pos))?;
430        }
431
432        Ok(Self(signal_mapping))
433    }
434
435    /// remove this `ISignalToIPduMapping` from the model
436    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
437        let opt_signal = self.signal();
438        let opt_signal_group = self.signal_group();
439
440        AbstractionElement::remove(self, false)?;
441
442        if deep {
443            // check if the signal is still used
444            if let Some(signal) = opt_signal
445                && !is_used_system_element(signal.element())
446            {
447                signal.remove(true)?;
448            }
449
450            // check if the signal group is still used
451            if let Some(signal_group) = opt_signal_group
452                && !is_used_system_element(signal_group.element())
453            {
454                signal_group.remove(true)?;
455            }
456        }
457
458        Ok(())
459    }
460
461    pub(crate) fn new_with_group(
462        name: &str,
463        mappings: &Element,
464        signal_group: &ISignalGroup,
465    ) -> Result<Self, AutosarAbstractionError> {
466        let signal_mapping = mappings.create_named_sub_element(ElementName::ISignalToIPduMapping, name)?;
467        signal_mapping
468            .create_sub_element(ElementName::ISignalGroupRef)?
469            .set_reference_target(signal_group.element())?;
470
471        Ok(Self(signal_mapping))
472    }
473
474    /// Reference to the signal that is mapped to the PDU.
475    /// Every mapping contains either a signal or a signal group.
476    #[must_use]
477    pub fn signal(&self) -> Option<ISignal> {
478        self.element()
479            .get_sub_element(ElementName::ISignalRef)
480            .and_then(|sigref| sigref.get_reference_target().ok())
481            .and_then(|signal_elem| ISignal::try_from(signal_elem).ok())
482    }
483
484    /// Set the byte order of the data in the mapped signal.
485    pub fn set_byte_order(&self, byte_order: ByteOrder) -> Result<(), AutosarAbstractionError> {
486        self.element()
487            .get_or_create_sub_element(ElementName::PackingByteOrder)?
488            .set_character_data::<EnumItem>(byte_order.into())?;
489        Ok(())
490    }
491
492    /// Byte order of the data in the signal.
493    #[must_use]
494    pub fn byte_order(&self) -> Option<ByteOrder> {
495        self.element()
496            .get_sub_element(ElementName::PackingByteOrder)
497            .and_then(|pbo| pbo.character_data())
498            .and_then(|cdata| cdata.enum_value())
499            .and_then(|enumval| enumval.try_into().ok())
500    }
501
502    /// Start position of the signal data within the PDU (bit position).
503    /// The start position is mandatory if the mapping describes a signal.
504    #[must_use]
505    pub fn start_position(&self) -> Option<u32> {
506        self.element()
507            .get_sub_element(ElementName::StartPosition)
508            .and_then(|sp_elem| sp_elem.character_data())
509            .and_then(|cdata| cdata.parse_integer())
510    }
511
512    /// Bit position of the update bit for the mapped signal. Not all signals use an update bit.
513    /// This is never used for signal groups
514    #[must_use]
515    pub fn update_bit(&self) -> Option<u32> {
516        self.element()
517            .get_sub_element(ElementName::UpdateIndicationBitPosition)
518            .and_then(|uibp| uibp.character_data())
519            .and_then(|cdata| cdata.parse_integer())
520    }
521
522    /// Set the transfer property of the mapped signal
523    pub fn set_transfer_property(&self, transfer_property: TransferProperty) -> Result<(), AutosarAbstractionError> {
524        self.element()
525            .get_or_create_sub_element(ElementName::TransferProperty)?
526            .set_character_data::<EnumItem>(transfer_property.into())?;
527        Ok(())
528    }
529
530    /// Get the transfer property of the mapped signal
531    #[must_use]
532    pub fn transfer_property(&self) -> Option<TransferProperty> {
533        self.element()
534            .get_sub_element(ElementName::TransferProperty)
535            .and_then(|pbo| pbo.character_data())
536            .and_then(|cdata| cdata.enum_value())
537            .and_then(|enumval| enumval.try_into().ok())
538    }
539
540    /// Reference to the signal group that is mapped to the PDU.
541    /// Every mapping contains either a signal or a signal group.
542    #[must_use]
543    pub fn signal_group(&self) -> Option<ISignalGroup> {
544        self.element()
545            .get_sub_element(ElementName::ISignalGroupRef)
546            .and_then(|sgref| sgref.get_reference_target().ok())
547            .and_then(|siggrp_elem| ISignalGroup::try_from(siggrp_elem).ok())
548    }
549}
550
551//##################################################################
552
553/// Timing specification for an IPDU
554#[derive(Debug, Clone, PartialEq)]
555pub struct IpduTiming {
556    /// minimum delay in seconds between two transmissions of the PDU
557    pub minimum_delay: Option<f64>,
558    /// timing specification if the COM transmission mode is true
559    pub transmission_mode_true_timing: Option<TransmissionModeTiming>,
560    /// timing specification if the COM transmission mode is false
561    pub transmission_mode_false_timing: Option<TransmissionModeTiming>,
562}
563
564/// Cyclic and event controlled timing parameters for an IPDU
565#[derive(Debug, Clone, PartialEq)]
566pub struct TransmissionModeTiming {
567    /// cyclic timing parameters
568    pub cyclic_timing: Option<CyclicTiming>,
569    /// event controlled timing parameters
570    pub event_controlled_timing: Option<EventControlledTiming>,
571}
572
573/// Cyclic timing parameters for an IPDU
574#[derive(Debug, Clone, PartialEq)]
575pub struct CyclicTiming {
576    /// period of repetition in seconds
577    pub time_period: f64,
578    /// delay until the first transmission of the PDU in seconds
579    pub time_offset: Option<f64>,
580}
581
582/// Event controlled timing parameters for an IPDU
583#[derive(Debug, Clone, PartialEq)]
584pub struct EventControlledTiming {
585    /// The PDU will be sent (number of repetitions + 1) times. If number of repetitions is 0, then the PDU is sent exactly once.
586    pub number_of_repetitions: u32,
587    /// time in seconds between two transmissions of the PDU
588    pub repetition_period: Option<f64>,
589}
590
591//##################################################################
592
593/// A group of ISignalIPdus that is handled together
594#[derive(Debug, Clone, PartialEq, Eq, Hash)]
595pub struct ISignalIPduGroup(Element);
596abstraction_element!(ISignalIPduGroup, ISignalIPduGroup);
597impl IdentifiableAbstractionElement for ISignalIPduGroup {}
598
599impl ISignalIPduGroup {
600    pub(crate) fn new(
601        name: &str,
602        package: &ArPackage,
603        communication_direction: CommunicationDirection,
604    ) -> Result<Self, AutosarAbstractionError> {
605        let pkg_elements = package.element().get_or_create_sub_element(ElementName::Elements)?;
606        let elem_pdu_group = pkg_elements.create_named_sub_element(ElementName::ISignalIPduGroup, name)?;
607        let pdu_group = Self(elem_pdu_group);
608        pdu_group.set_communication_direction(communication_direction)?;
609
610        Ok(pdu_group)
611    }
612
613    /// set the communication direction of the PDU group
614    pub fn set_communication_direction(
615        &self,
616        communication_direction: CommunicationDirection,
617    ) -> Result<(), AutosarAbstractionError> {
618        self.element()
619            .get_or_create_sub_element(ElementName::CommunicationDirection)
620            .and_then(|cd| cd.set_character_data::<EnumItem>(communication_direction.into()))?;
621        Ok(())
622    }
623
624    /// get the communication direction of the PDU group
625    #[must_use]
626    pub fn communication_direction(&self) -> Option<CommunicationDirection> {
627        self.element()
628            .get_sub_element(ElementName::CommunicationDirection)
629            .and_then(|cd| cd.character_data())
630            .and_then(|cdata| cdata.enum_value())
631            .and_then(|enumval| enumval.try_into().ok())
632    }
633
634    /// add a PDU to the PDU group
635    pub fn add_pdu(&self, pdu: &ISignalIPdu) -> Result<(), AutosarAbstractionError> {
636        self.element()
637            .get_or_create_sub_element(ElementName::ISignalIPdus)?
638            .create_sub_element(ElementName::ISignalIPduRefConditional)?
639            .create_sub_element(ElementName::ISignalIPduRef)?
640            .set_reference_target(pdu.element())?;
641        Ok(())
642    }
643
644    /// get an iterator over all PDUs in the PDU group
645    pub fn pdus(&self) -> impl Iterator<Item = ISignalIPdu> + Send + use<> {
646        self.element()
647            .get_sub_element(ElementName::ISignalIPdus)
648            .into_iter()
649            .flat_map(|pdus_elem| pdus_elem.sub_elements())
650            .filter_map(|pdu_ref_cond| {
651                pdu_ref_cond
652                    .get_sub_element(ElementName::ISignalIPduRef)
653                    .and_then(|pdu_ref| pdu_ref.get_reference_target().ok())
654                    .and_then(|pdu_elem| ISignalIPdu::try_from(pdu_elem).ok())
655            })
656    }
657}
658
659//##################################################################
660
661/// Helper struct to validate signal mappings
662pub struct SignalMappingValidator {
663    bitmap: Vec<u8>,
664}
665
666impl SignalMappingValidator {
667    /// Create a new validator for a PDU with the given length
668    #[must_use]
669    pub fn new(length: u32) -> Self {
670        Self {
671            bitmap: vec![0; length as usize],
672        }
673    }
674
675    /// add a signal to the validator
676    ///
677    /// This will mark the bits in the bitmap that are used by the signal.
678    /// If the signal overlaps with any previously added signal, then the method will return false.
679    pub fn add_signal(
680        &mut self,
681        bit_position: u32,
682        bit_length: u64,
683        byte_order: ByteOrder,
684        update_bit: Option<u32>,
685    ) -> bool {
686        let bit_position = u64::from(bit_position);
687        let first_byte = (bit_position / 8) as usize;
688        let bit_offset = bit_position % 8; // bit position inside the first byte
689        let first_byte_bits; // number of bits in the first byte
690        let mut first_mask;
691
692        if byte_order == ByteOrder::MostSignificantByteFirst {
693            // MostSignificantByteFirst / big endian
694            // bit-position: 5, length: 10
695            // byte   |               0               |               1               |
696            // bit    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
697            // signal | 4   5   6   7   8   9                           0   1   2   3
698            first_byte_bits = (bit_offset + 1).min(bit_length);
699            first_mask = ((1u16 << (bit_offset + 1)) - 1) as u8;
700            if bit_offset + 1 != first_byte_bits {
701                let pos2 = bit_offset - first_byte_bits;
702                let subtract_mask = (1u8 << pos2) - 1;
703                first_mask -= subtract_mask;
704            }
705        } else {
706            // MostSignificantByteLast / little endian
707            // bit-position: 5, length: 10
708            // byte   |               0               |               1               |
709            // bit    | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
710            // signal |                     0   1   2   3   4   5   6   7   8   9
711            first_byte_bits = (8 - bit_offset).min(bit_length); // value range 1 - 8
712            first_mask = !((1u16 << bit_offset) - 1) as u8; // 0b1111_1110, 0b1111_1100, 0b1111_1000, ..., 0b1000_0000
713            if bit_offset + first_byte_bits < 8 {
714                let pos2 = bit_offset + first_byte_bits;
715                let subtract_mask = !((1u8 << pos2) - 1);
716                first_mask -= subtract_mask;
717            }
718        }
719        let full_bytes = (bit_length - first_byte_bits) as usize / 8;
720        let end_bits = (bit_length - first_byte_bits) % 8;
721
722        let mut result = self.apply_mask(first_mask, first_byte);
723        result &= self.apply_full_bytes(first_byte + 1, full_bytes);
724
725        // handle any bits in a partial trailing byte
726        if end_bits > 0 {
727            let end_mask = if byte_order == ByteOrder::MostSignificantByteFirst {
728                !((1u8 << end_bits) - 1)
729            } else {
730                (1u8 << end_bits) - 1
731            };
732            result &= self.apply_mask(end_mask, first_byte + full_bytes + 1);
733        }
734
735        // handle the update bit, if any
736        if let Some(update_bit) = update_bit {
737            let position = (update_bit / 8) as usize;
738            let bit_pos = update_bit % 8;
739            let mask = 1 << bit_pos;
740            result &= self.apply_mask(mask, position);
741        }
742
743        result
744    }
745
746    fn apply_mask(&mut self, mask: u8, position: usize) -> bool {
747        if position < self.bitmap.len() {
748            // check if any of the masked bits were already set
749            let result = self.bitmap[position] & mask == 0;
750            // set the masked bits
751            self.bitmap[position] |= mask;
752            result
753        } else {
754            false
755        }
756    }
757
758    fn apply_full_bytes(&mut self, position: usize, count: usize) -> bool {
759        let mut result = true;
760        if count > 0 {
761            let limit = self.bitmap.len().min(position + count);
762            for idx in position..limit {
763                result &= self.apply_mask(0xff, idx);
764            }
765            // make sure all of the signal bytes are inside the pdu length
766            result &= limit == position + count;
767        }
768        result
769    }
770}
771
772//##################################################################
773
774#[cfg(test)]
775mod test {
776    use super::*;
777    use crate::{AutosarModelAbstraction, ByteOrder, SystemCategory};
778    use autosar_data::AutosarVersion;
779
780    #[test]
781    fn isignal_ipdu() {
782        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
783        let package = model.get_or_create_package("/pkg").unwrap();
784        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
785
786        let pdu = system.create_isignal_ipdu("isignal_ipdu", &package, 8).unwrap();
787        assert_eq!(pdu.name().unwrap(), "isignal_ipdu");
788        assert_eq!(pdu.length().unwrap(), 8);
789
790        // create a signal and map it to the PDU
791        let syssignal = package.create_system_signal("syssignal").unwrap();
792        let isignal = system.create_isignal("isignal", &package, 4, &syssignal, None).unwrap();
793        let mapping = pdu
794            .map_signal(
795                &isignal,
796                0,
797                ByteOrder::MostSignificantByteFirst,
798                Some(5),
799                TransferProperty::Triggered,
800            )
801            .unwrap();
802        assert_eq!(mapping.signal().unwrap(), isignal);
803        assert_eq!(mapping.start_position().unwrap(), 0);
804        assert_eq!(mapping.update_bit(), Some(5));
805        assert_eq!(mapping.byte_order().unwrap(), ByteOrder::MostSignificantByteFirst);
806        mapping.set_byte_order(ByteOrder::MostSignificantByteLast).unwrap();
807        assert_eq!(mapping.byte_order().unwrap(), ByteOrder::MostSignificantByteLast);
808        assert_eq!(mapping.transfer_property().unwrap(), TransferProperty::Triggered);
809        mapping.set_transfer_property(TransferProperty::Pending).unwrap();
810        assert_eq!(mapping.transfer_property().unwrap(), TransferProperty::Pending);
811
812        // create a signal group which contains a signal
813        let syssignal_group = package.create_system_signal_group("syssignal_group").unwrap();
814        let signal_group = system
815            .create_isignal_group("signal_group", &package, &syssignal_group)
816            .unwrap();
817        let grouped_syssignal = package.create_system_signal("groups_syssignal").unwrap();
818        syssignal_group.add_signal(&grouped_syssignal).unwrap();
819        let grouped_isignal = system
820            .create_isignal("grouped_isignal", &package, 4, &grouped_syssignal, None)
821            .unwrap();
822        signal_group.add_signal(&grouped_isignal).unwrap();
823        assert_eq!(grouped_isignal.signal_group().unwrap(), signal_group);
824
825        // map the signal to the PDU - this should fail, because the signal is part of an unmapped signal group
826        let result = pdu.map_signal(
827            &grouped_isignal,
828            9,
829            ByteOrder::MostSignificantByteFirst,
830            None,
831            TransferProperty::Triggered,
832        );
833        assert!(result.is_err());
834
835        // map the signal group to the PDU
836        let mapping = pdu.map_signal_group(&signal_group).unwrap();
837        assert_eq!(mapping.signal_group().unwrap(), signal_group);
838
839        // map the grouped signal to the PDU - this should now work
840        let _mapping = pdu
841            .map_signal(
842                &grouped_isignal,
843                9,
844                ByteOrder::MostSignificantByteFirst,
845                None,
846                TransferProperty::Triggered,
847            )
848            .unwrap();
849    }
850
851    #[test]
852    fn validate_signal_mapping() {
853        // create a validator and add a 2-bit signal
854        let mut validator = SignalMappingValidator::new(4);
855        let result = validator.add_signal(0, 2, ByteOrder::MostSignificantByteLast, None);
856        assert!(result);
857        assert_eq!(validator.bitmap[0], 0x03);
858
859        // create a validator and add a little-endian 9-bit signal
860        let mut validator = SignalMappingValidator::new(4);
861        let result = validator.add_signal(5, 10, ByteOrder::MostSignificantByteLast, None);
862        assert!(result);
863        assert_eq!(validator.bitmap[0], 0xE0);
864        assert_eq!(validator.bitmap[1], 0x7F);
865
866        // create a validator and add a big-endian 9-bit signal
867        let mut validator = SignalMappingValidator::new(4);
868        let result = validator.add_signal(5, 10, ByteOrder::MostSignificantByteFirst, None);
869        assert!(result);
870        assert_eq!(validator.bitmap[0], 0x3F);
871        assert_eq!(validator.bitmap[1], 0xF0);
872
873        // add another signal to the existing validator
874        let result = validator.add_signal(5, 10, ByteOrder::MostSignificantByteLast, None);
875        // it returns false (the signals overlap), but the bitmap is still updated
876        assert!(!result);
877        assert_eq!(validator.bitmap[0], 0xFF);
878        assert_eq!(validator.bitmap[1], 0xFF);
879
880        // create a validator and add a 32-bit signal
881        let mut validator = SignalMappingValidator::new(4);
882        let result = validator.add_signal(0, 32, ByteOrder::MostSignificantByteLast, None);
883        assert!(result);
884        assert_eq!(validator.bitmap[0], 0xFF);
885        assert_eq!(validator.bitmap[1], 0xFF);
886        assert_eq!(validator.bitmap[2], 0xFF);
887        assert_eq!(validator.bitmap[3], 0xFF);
888
889        // create a validator and add a big-endian 32-bit signal
890        let mut validator = SignalMappingValidator::new(4);
891        let result = validator.add_signal(7, 32, ByteOrder::MostSignificantByteFirst, None);
892        assert!(result);
893        assert_eq!(validator.bitmap[0], 0xFF);
894        assert_eq!(validator.bitmap[1], 0xFF);
895        assert_eq!(validator.bitmap[2], 0xFF);
896        assert_eq!(validator.bitmap[3], 0xFF);
897
898        // multiple mixed signals
899        let mut validator = SignalMappingValidator::new(8);
900        let result = validator.add_signal(7, 16, ByteOrder::MostSignificantByteFirst, Some(60));
901        assert!(result);
902        let result = validator.add_signal(16, 3, ByteOrder::MostSignificantByteLast, Some(61));
903        assert!(result);
904        let result = validator.add_signal(19, 7, ByteOrder::MostSignificantByteLast, Some(62));
905        assert!(result);
906        let result = validator.add_signal(26, 30, ByteOrder::MostSignificantByteLast, Some(63));
907        assert!(result);
908        let result = validator.add_signal(59, 4, ByteOrder::MostSignificantByteFirst, None);
909        assert!(result);
910        assert_eq!(validator.bitmap, [0xFF; 8]);
911    }
912
913    #[test]
914    fn ipdu_timing() {
915        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
916        let package = model.get_or_create_package("/pkg").unwrap();
917        let pdu = ISignalIPdu::new("pdu_name", &package, 8).unwrap();
918
919        let timing_spec = IpduTiming {
920            minimum_delay: Some(0.1),
921            transmission_mode_true_timing: Some(TransmissionModeTiming {
922                cyclic_timing: Some(CyclicTiming {
923                    time_period: 0.2,
924                    time_offset: Some(0.3),
925                }),
926                event_controlled_timing: Some(EventControlledTiming {
927                    number_of_repetitions: 4,
928                    repetition_period: Some(0.5),
929                }),
930            }),
931            transmission_mode_false_timing: Some(TransmissionModeTiming {
932                cyclic_timing: Some(CyclicTiming {
933                    time_period: 0.6,
934                    time_offset: Some(0.7),
935                }),
936                event_controlled_timing: Some(EventControlledTiming {
937                    number_of_repetitions: 8,
938                    repetition_period: Some(0.9),
939                }),
940            }),
941        };
942        pdu.set_timing(&timing_spec).unwrap();
943        let timing_spec2 = pdu.timing().unwrap();
944        assert_eq!(timing_spec, timing_spec2);
945    }
946
947    #[test]
948    fn isignal_ipdu_group() {
949        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
950        let package = model.get_or_create_package("/pkg").unwrap();
951        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
952        let pdu_group = system
953            .create_isignal_ipdu_group("PduGroup", &package, CommunicationDirection::In)
954            .unwrap();
955
956        assert_eq!(system.isignal_ipdu_groups().count(), 1);
957
958        assert_eq!(pdu_group.name().unwrap(), "PduGroup");
959        assert_eq!(pdu_group.communication_direction().unwrap(), CommunicationDirection::In);
960
961        let isignal_ipdu1 = system.create_isignal_ipdu("Pdu1", &package, 8).unwrap();
962        let isignal_ipdu2 = system.create_isignal_ipdu("Pdu2", &package, 16).unwrap();
963
964        pdu_group.add_pdu(&isignal_ipdu1).unwrap();
965        pdu_group.add_pdu(&isignal_ipdu2).unwrap();
966
967        assert_eq!(pdu_group.pdus().count(), 2);
968    }
969}