autosar_data_abstraction/communication/transport_layer/
can_tp.rs

1use crate::communication::{AbstractIpdu, CanCluster, CanCommunicationConnector, IPdu, NPdu};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement,
4    abstraction_element,
5};
6use autosar_data::{Element, ElementName, EnumItem};
7
8//#########################################################
9
10/// Container for `CanTp` configuration
11///
12/// There should be one `CanTpConfig` for each CAN network in the system
13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct CanTpConfig(Element);
15abstraction_element!(CanTpConfig, CanTpConfig);
16impl IdentifiableAbstractionElement for CanTpConfig {}
17
18impl CanTpConfig {
19    pub(crate) fn new(name: &str, package: &ArPackage, cluster: &CanCluster) -> Result<Self, AutosarAbstractionError> {
20        let pkg_elem = package.element().get_or_create_sub_element(ElementName::Elements)?;
21        let tp_config_elem = pkg_elem.create_named_sub_element(ElementName::CanTpConfig, name)?;
22        let tp_config = Self(tp_config_elem);
23        tp_config.set_cluster(cluster)?;
24
25        Ok(tp_config)
26    }
27
28    /// set the `CanCluster` associated with this configuration
29    pub fn set_cluster(&self, cluster: &CanCluster) -> Result<(), AutosarAbstractionError> {
30        self.element()
31            .get_or_create_sub_element(ElementName::CommunicationClusterRef)?
32            .set_reference_target(cluster.element())?;
33        Ok(())
34    }
35
36    /// get the `CanCluster` associated with this configuration
37    #[must_use]
38    pub fn cluster(&self) -> Option<CanCluster> {
39        self.element()
40            .get_sub_element(ElementName::CommunicationClusterRef)
41            .and_then(|elem| elem.get_reference_target().ok())
42            .and_then(|target| CanCluster::try_from(target).ok())
43    }
44
45    /// create a `CanTp` ECU in the configuration
46    pub fn create_can_tp_ecu(
47        &self,
48        ecu_instance: &EcuInstance,
49        cycle_time_main_function: Option<f64>,
50    ) -> Result<CanTpEcu, AutosarAbstractionError> {
51        let ecu_collection = self.element().get_or_create_sub_element(ElementName::TpEcus)?;
52
53        CanTpEcu::new(&ecu_collection, ecu_instance, cycle_time_main_function)
54    }
55
56    /// get all of the ECUs in the configuration
57    pub fn can_tp_ecus(&self) -> impl Iterator<Item = CanTpEcu> + Send + use<> {
58        self.element()
59            .get_sub_element(ElementName::TpEcus)
60            .into_iter()
61            .flat_map(|ecu_collection| ecu_collection.sub_elements())
62            .filter_map(|tp_ecu_elem| CanTpEcu::try_from(tp_ecu_elem).ok())
63    }
64
65    /// create a new `CanTpAddress` in the configuration
66    pub fn create_can_tp_address(&self, name: &str, address: u32) -> Result<CanTpAddress, AutosarAbstractionError> {
67        let addresses_container = self.element().get_or_create_sub_element(ElementName::TpAddresss)?;
68        CanTpAddress::new(name, &addresses_container, address)
69    }
70
71    /// get all of the Can Tp addresses in the configuration
72    pub fn can_tp_addresses(&self) -> impl Iterator<Item = CanTpAddress> + Send + use<> {
73        self.element()
74            .get_sub_element(ElementName::TpAddresss)
75            .into_iter()
76            .flat_map(|addresses_container| {
77                addresses_container
78                    .sub_elements()
79                    .filter_map(|address_elem| CanTpAddress::try_from(address_elem).ok())
80            })
81    }
82
83    /// create a new `CanTpChannel` in the configuration
84    pub fn create_can_tp_channel(
85        &self,
86        name: &str,
87        channel_id: u32,
88        mode: CanTpChannelMode,
89    ) -> Result<CanTpChannel, AutosarAbstractionError> {
90        let channels_container = self.element().get_or_create_sub_element(ElementName::TpChannels)?;
91        CanTpChannel::new(name, &channels_container, channel_id, mode)
92    }
93
94    /// iterate over all `CanTpChannel`s in the configuration
95    pub fn can_tp_channels(&self) -> impl Iterator<Item = CanTpChannel> + Send + use<> {
96        self.element()
97            .get_sub_element(ElementName::TpChannels)
98            .into_iter()
99            .flat_map(|channels_container| {
100                channels_container
101                    .sub_elements()
102                    .filter_map(|channel_elem| CanTpChannel::try_from(channel_elem).ok())
103            })
104    }
105
106    /// create a new `CanTpConnection` in the configuration
107    pub fn create_can_tp_connection<T: AbstractIpdu>(
108        &self,
109        name: Option<&str>,
110        addressing_format: CanTpAddressingFormat,
111        can_tp_channel: &CanTpChannel,
112        data_pdu: &NPdu,
113        tp_sdu: &T,
114        padding_activation: bool,
115    ) -> Result<CanTpConnection, AutosarAbstractionError> {
116        let connections_container = self.element().get_or_create_sub_element(ElementName::TpConnections)?;
117        CanTpConnection::new(
118            name,
119            &connections_container,
120            addressing_format,
121            can_tp_channel,
122            data_pdu,
123            &tp_sdu.clone().into(),
124            padding_activation,
125        )
126    }
127
128    /// iterate over all `CanTpConnections` in the configuration
129    pub fn can_tp_connections(&self) -> impl Iterator<Item = CanTpConnection> + Send + use<> {
130        self.element()
131            .get_sub_element(ElementName::TpConnections)
132            .into_iter()
133            .flat_map(|connections_container| {
134                connections_container
135                    .sub_elements()
136                    .filter_map(|connection_elem| CanTpConnection::try_from(connection_elem).ok())
137            })
138    }
139
140    /// create a new `CanTpNode` in the configuration
141    pub fn create_can_tp_node(&self, name: &str) -> Result<CanTpNode, AutosarAbstractionError> {
142        let nodes_container = self.element().get_or_create_sub_element(ElementName::TpNodes)?;
143        CanTpNode::new(name, &nodes_container)
144    }
145
146    /// get all of the `CanTpNodes` in the configuration
147    pub fn can_tp_nodes(&self) -> impl Iterator<Item = CanTpNode> + Send + use<> {
148        self.element()
149            .get_sub_element(ElementName::TpNodes)
150            .into_iter()
151            .flat_map(|nodes_container| {
152                nodes_container
153                    .sub_elements()
154                    .filter_map(|node_elem| CanTpNode::try_from(node_elem).ok())
155            })
156    }
157}
158
159//#########################################################
160
161/// A `CanTpEcu` represents an ECU that is using the `CanTp` module
162#[derive(Debug, Clone, PartialEq)]
163pub struct CanTpEcu(Element);
164abstraction_element!(CanTpEcu, CanTpEcu);
165
166impl CanTpEcu {
167    pub(crate) fn new(
168        parent: &Element,
169        ecu_instance: &EcuInstance,
170        cycle_time_main_function: Option<f64>,
171    ) -> Result<Self, AutosarAbstractionError> {
172        let tp_ecu_elem = parent.create_sub_element(ElementName::CanTpEcu)?;
173        let tp_ecu = Self(tp_ecu_elem);
174
175        tp_ecu.set_ecu_instance(ecu_instance)?;
176        tp_ecu.set_cycle_time_main_function(cycle_time_main_function)?;
177
178        Ok(tp_ecu)
179    }
180
181    /// set the ECU instance of the `CanTpEcu`
182    pub fn set_ecu_instance(&self, ecu_instance: &EcuInstance) -> Result<(), AutosarAbstractionError> {
183        self.element()
184            .get_or_create_sub_element(ElementName::EcuInstanceRef)?
185            .set_reference_target(ecu_instance.element())?;
186        Ok(())
187    }
188
189    /// get the ECU instance of the `CanTpEcu`
190    #[must_use]
191    pub fn ecu_instance(&self) -> Option<EcuInstance> {
192        self.element()
193            .get_sub_element(ElementName::EcuInstanceRef)
194            .and_then(|elem| elem.get_reference_target().ok())
195            .and_then(|target| EcuInstance::try_from(target).ok())
196    }
197
198    /// set the cycle time of the `CanTp` main function of the ECU
199    pub fn set_cycle_time_main_function(&self, cycle_time: Option<f64>) -> Result<(), AutosarAbstractionError> {
200        if let Some(cycle_time) = cycle_time {
201            self.element()
202                .get_or_create_sub_element(ElementName::CycleTimeMainFunction)?
203                .set_character_data(cycle_time)?;
204        } else {
205            let _ = self
206                .element()
207                .remove_sub_element_kind(ElementName::CycleTimeMainFunction);
208        }
209        Ok(())
210    }
211
212    /// get the cycle time of the `CanTp` main function of the ECU
213    #[must_use]
214    pub fn cycle_time_main_function(&self) -> Option<f64> {
215        self.element()
216            .get_sub_element(ElementName::CycleTimeMainFunction)
217            .and_then(|elem| elem.character_data())
218            .and_then(|cdata| cdata.parse_float())
219    }
220}
221
222//#########################################################
223
224/// A `CanTpAddress` represents a logical address in the `CanTp` module
225#[derive(Debug, Clone, PartialEq, Eq, Hash)]
226pub struct CanTpAddress(Element);
227abstraction_element!(CanTpAddress, CanTpAddress);
228impl IdentifiableAbstractionElement for CanTpAddress {}
229
230impl CanTpAddress {
231    pub(crate) fn new(name: &str, parent: &Element, tp_address: u32) -> Result<Self, AutosarAbstractionError> {
232        let address_elem = parent.create_named_sub_element(ElementName::CanTpAddress, name)?;
233        let address = Self(address_elem);
234        address.set_tp_address(tp_address)?;
235
236        Ok(address)
237    }
238
239    /// set the address value of the `CanTpAddress`
240    pub fn set_tp_address(&self, tp_address: u32) -> Result<(), AutosarAbstractionError> {
241        self.element()
242            .get_or_create_sub_element(ElementName::TpAddress)?
243            .set_character_data(u64::from(tp_address))?;
244        Ok(())
245    }
246
247    /// get the address value of the `CanTpAddress`
248    #[must_use]
249    pub fn tp_address(&self) -> Option<u32> {
250        self.element()
251            .get_sub_element(ElementName::TpAddress)
252            .and_then(|elem| elem.character_data())
253            .and_then(|cdata| cdata.parse_integer())
254    }
255}
256
257//#########################################################
258
259/// A `CanTpChannel` represents a channel in the `CanTp` module
260#[derive(Debug, Clone, PartialEq, Eq, Hash)]
261pub struct CanTpChannel(Element);
262abstraction_element!(CanTpChannel, CanTpChannel);
263impl IdentifiableAbstractionElement for CanTpChannel {}
264
265impl CanTpChannel {
266    pub(crate) fn new(
267        name: &str,
268        parent: &Element,
269        channel_id: u32,
270        mode: CanTpChannelMode,
271    ) -> Result<Self, AutosarAbstractionError> {
272        let channel_elem = parent.create_named_sub_element(ElementName::CanTpChannel, name)?;
273        let channel = Self(channel_elem);
274
275        channel.set_channel_id(channel_id)?;
276        channel.set_channel_mode(mode)?;
277
278        Ok(channel)
279    }
280
281    /// set the channel id of the channel
282    pub fn set_channel_id(&self, channel_id: u32) -> Result<(), AutosarAbstractionError> {
283        self.element()
284            .get_or_create_sub_element(ElementName::ChannelId)?
285            .set_character_data(u64::from(channel_id))?;
286        Ok(())
287    }
288
289    /// get the channel id of the channel
290    #[must_use]
291    pub fn channel_id(&self) -> Option<u32> {
292        self.element()
293            .get_sub_element(ElementName::ChannelId)
294            .and_then(|elem| elem.character_data())
295            .and_then(|cdata| cdata.parse_integer())
296    }
297
298    /// set the channel mode of the channel
299    pub fn set_channel_mode(&self, mode: CanTpChannelMode) -> Result<(), AutosarAbstractionError> {
300        self.element()
301            .get_or_create_sub_element(ElementName::ChannelMode)?
302            .set_character_data::<EnumItem>(mode.into())?;
303        Ok(())
304    }
305
306    /// get the channel mode of the channel
307    #[must_use]
308    pub fn channel_mode(&self) -> Option<CanTpChannelMode> {
309        self.element()
310            .get_sub_element(ElementName::ChannelMode)
311            .and_then(|elem| elem.character_data())
312            .and_then(|cdata| cdata.enum_value())
313            .and_then(|enumitem| enumitem.try_into().ok())
314    }
315}
316
317//#########################################################
318
319/// The mode of a `CanTpChannel`
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
321pub enum CanTpChannelMode {
322    /// Full duplex mode
323    FullDuplex,
324    /// Half duplex mode
325    HalfDuplex,
326}
327
328impl From<CanTpChannelMode> for EnumItem {
329    fn from(mode: CanTpChannelMode) -> Self {
330        match mode {
331            CanTpChannelMode::FullDuplex => EnumItem::FullDuplexMode,
332            CanTpChannelMode::HalfDuplex => EnumItem::HalfDuplexMode,
333        }
334    }
335}
336
337impl TryFrom<EnumItem> for CanTpChannelMode {
338    type Error = AutosarAbstractionError;
339
340    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
341        match value {
342            EnumItem::FullDuplexMode => Ok(CanTpChannelMode::FullDuplex),
343            EnumItem::HalfDuplexMode => Ok(CanTpChannelMode::HalfDuplex),
344            _ => Err(AutosarAbstractionError::ValueConversionError {
345                value: value.to_string(),
346                dest: "CanTpChannelMode".to_string(),
347            }),
348        }
349    }
350}
351
352//#########################################################
353
354/// A connection identifies the sender and the receiver of this particular communication.
355/// The `CanTp` module routes a Pdu through this connection.
356#[derive(Debug, Clone, PartialEq, Eq, Hash)]
357pub struct CanTpConnection(Element);
358abstraction_element!(CanTpConnection, CanTpConnection);
359
360impl IdentifiableAbstractionElement for CanTpConnection {
361    /// get the name of the connection
362    ///
363    /// In early versions of the Autosar standard, `CanTpConnection` was not identifiable.
364    /// This was fixed later by adding the Ident sub-element. This method returns the name
365    /// provied in the Ident element, if it exists.
366    fn name(&self) -> Option<String> {
367        self.element()
368            .get_sub_element(ElementName::Ident)
369            .and_then(|elem| elem.item_name())
370    }
371
372    /// set the name of the connection
373    fn set_name(&self, name: &str) -> Result<(), AutosarAbstractionError> {
374        // rename an existing Ident element or create a new one
375        if let Some(ident_elem) = self.element().get_sub_element(ElementName::Ident) {
376            ident_elem.set_item_name(name)?;
377        } else {
378            self.element().create_named_sub_element(ElementName::Ident, name)?;
379        }
380        Ok(())
381    }
382}
383
384impl CanTpConnection {
385    pub(crate) fn new(
386        name: Option<&str>,
387        parent: &Element,
388        addressing_format: CanTpAddressingFormat,
389        can_tp_channel: &CanTpChannel,
390        data_pdu: &NPdu,
391        tp_sdu: &IPdu,
392        padding_activation: bool,
393    ) -> Result<Self, AutosarAbstractionError> {
394        let connection_elem = parent.create_sub_element(ElementName::CanTpConnection)?;
395        let connection = Self(connection_elem);
396
397        if let Some(name) = name {
398            connection.set_name(name)?;
399        }
400        connection.set_addressing_format(addressing_format)?;
401        connection.set_channel(can_tp_channel)?;
402        connection.set_data_pdu(data_pdu)?;
403        connection.set_tp_sdu(tp_sdu)?;
404        connection.set_padding_activation(padding_activation)?;
405
406        Ok(connection)
407    }
408
409    /// set the `CanTpChannel` associated with this connection
410    pub fn set_channel(&self, channel: &CanTpChannel) -> Result<(), AutosarAbstractionError> {
411        self.element()
412            .get_or_create_sub_element(ElementName::CanTpChannelRef)?
413            .set_reference_target(channel.element())?;
414        Ok(())
415    }
416
417    /// get the `CanTpChannel` associated with this connection
418    #[must_use]
419    pub fn channel(&self) -> Option<CanTpChannel> {
420        self.element()
421            .get_sub_element(ElementName::CanTpChannelRef)
422            .and_then(|elem| elem.get_reference_target().ok())
423            .and_then(|target| CanTpChannel::try_from(target).ok())
424    }
425
426    /// set the `NPdu` associated with this connection
427    pub fn set_data_pdu(&self, data_pdu: &NPdu) -> Result<(), AutosarAbstractionError> {
428        self.element()
429            .get_or_create_sub_element(ElementName::DataPduRef)?
430            .set_reference_target(data_pdu.element())?;
431        Ok(())
432    }
433
434    /// get the `NPdu` associated with this connection
435    ///
436    /// This is the Pdu that is sent over the CAN network
437    #[must_use]
438    pub fn data_pdu(&self) -> Option<NPdu> {
439        self.element()
440            .get_sub_element(ElementName::DataPduRef)
441            .and_then(|elem| elem.get_reference_target().ok())
442            .and_then(|target| NPdu::try_from(target).ok())
443    }
444
445    /// set the `IPdu` associated with this connection
446    pub fn set_tp_sdu<T: AbstractIpdu>(&self, tp_sdu: &T) -> Result<(), AutosarAbstractionError> {
447        self.element()
448            .get_or_create_sub_element(ElementName::TpSduRef)?
449            .set_reference_target(tp_sdu.element())?;
450        Ok(())
451    }
452
453    /// get the `IPdu` associated with this connection
454    ///
455    /// This is the Pdu that is sent over the transport protocol
456    #[must_use]
457    pub fn tp_sdu(&self) -> Option<IPdu> {
458        self.element()
459            .get_sub_element(ElementName::TpSduRef)
460            .and_then(|elem| elem.get_reference_target().ok())
461            .and_then(|target| IPdu::try_from(target).ok())
462    }
463
464    /// set the addressing format of the connection
465    pub fn set_addressing_format(
466        &self,
467        addressing_format: CanTpAddressingFormat,
468    ) -> Result<(), AutosarAbstractionError> {
469        self.element()
470            .get_or_create_sub_element(ElementName::AddressingFormat)?
471            .set_character_data::<EnumItem>(addressing_format.into())?;
472        Ok(())
473    }
474
475    /// get the addressing format of the connection
476    #[must_use]
477    pub fn addressing_format(&self) -> Option<CanTpAddressingFormat> {
478        self.element()
479            .get_sub_element(ElementName::AddressingFormat)
480            .and_then(|elem| elem.character_data())
481            .and_then(|cdata| cdata.enum_value())
482            .and_then(|enumitem| enumitem.try_into().ok())
483    }
484
485    /// set the padding activation of the connection
486    pub fn set_padding_activation(&self, padding_activation: bool) -> Result<(), AutosarAbstractionError> {
487        self.element()
488            .get_or_create_sub_element(ElementName::PaddingActivation)?
489            .set_character_data(padding_activation)?;
490        Ok(())
491    }
492
493    /// get the padding activation of the connection
494    #[must_use]
495    pub fn padding_activation(&self) -> Option<bool> {
496        self.element()
497            .get_sub_element(ElementName::PaddingActivation)
498            .and_then(|elem| elem.character_data())
499            .and_then(|cdata| cdata.parse_bool())
500    }
501
502    /// set the transmitter of the connection
503    ///
504    /// This is a `CanTpNode` representing an ECU that will send the data
505    pub fn set_transmitter(&self, transmitter: &CanTpNode) -> Result<(), AutosarAbstractionError> {
506        self.element()
507            .get_or_create_sub_element(ElementName::TransmitterRef)?
508            .set_reference_target(transmitter.element())?;
509        Ok(())
510    }
511
512    /// get the transmitter of the connection
513    #[must_use]
514    pub fn transmitter(&self) -> Option<CanTpNode> {
515        self.element()
516            .get_sub_element(ElementName::TransmitterRef)
517            .and_then(|elem| elem.get_reference_target().ok())
518            .and_then(|target| CanTpNode::try_from(target).ok())
519    }
520
521    /// add a receiver to the connection
522    ///
523    /// This is a `CanTpNode` representing an ECU that will receive the data
524    pub fn add_receiver(&self, receiver: &CanTpNode) -> Result<(), AutosarAbstractionError> {
525        let receivers = self.element().get_or_create_sub_element(ElementName::ReceiverRefs)?;
526        let receiver_ref_elem = receivers.create_sub_element(ElementName::ReceiverRef)?;
527        receiver_ref_elem.set_reference_target(receiver.element())?;
528        Ok(())
529    }
530
531    /// get all of the receivers of the connection
532    pub fn receivers(&self) -> impl Iterator<Item = CanTpNode> + Send + use<> {
533        self.element()
534            .get_sub_element(ElementName::ReceiverRefs)
535            .into_iter()
536            .flat_map(|receivers| {
537                receivers.sub_elements().filter_map(|receiver_ref_elem| {
538                    receiver_ref_elem
539                        .get_reference_target()
540                        .ok()
541                        .and_then(|target| CanTpNode::try_from(target).ok())
542                })
543            })
544    }
545}
546
547//#########################################################
548
549/// The addressing format of a `CanTpConnection`
550#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
551pub enum CanTpAddressingFormat {
552    /// Extended addressing format
553    Extended,
554    /// Mixed 11-bit addressing format
555    Mixed,
556    /// Mixed 29-bit addressing format
557    Mixed29Bit,
558    /// Normal fixed addressing format
559    NormalFixed,
560    /// Standard addressing format
561    Standard,
562}
563
564impl From<CanTpAddressingFormat> for EnumItem {
565    fn from(format: CanTpAddressingFormat) -> Self {
566        match format {
567            CanTpAddressingFormat::Extended => EnumItem::Extended,
568            CanTpAddressingFormat::Mixed => EnumItem::Mixed,
569            CanTpAddressingFormat::Mixed29Bit => EnumItem::Mixed29Bit,
570            CanTpAddressingFormat::NormalFixed => EnumItem::Normalfixed,
571            CanTpAddressingFormat::Standard => EnumItem::Standard,
572        }
573    }
574}
575
576impl TryFrom<EnumItem> for CanTpAddressingFormat {
577    type Error = AutosarAbstractionError;
578
579    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
580        match value {
581            EnumItem::Extended => Ok(CanTpAddressingFormat::Extended),
582            EnumItem::Mixed => Ok(CanTpAddressingFormat::Mixed),
583            EnumItem::Mixed29Bit => Ok(CanTpAddressingFormat::Mixed29Bit),
584            EnumItem::Normalfixed => Ok(CanTpAddressingFormat::NormalFixed),
585            EnumItem::Standard => Ok(CanTpAddressingFormat::Standard),
586            _ => Err(AutosarAbstractionError::ValueConversionError {
587                value: value.to_string(),
588                dest: "CanTpAddressingFormat".to_string(),
589            }),
590        }
591    }
592}
593
594//#########################################################
595
596/// A `CanTpNode` provides the TP address and the connection to the topology description in a `CanTpConfig`
597#[derive(Debug, Clone, PartialEq, Eq, Hash)]
598pub struct CanTpNode(Element);
599abstraction_element!(CanTpNode, CanTpNode);
600
601impl IdentifiableAbstractionElement for CanTpNode {}
602
603impl CanTpNode {
604    pub(crate) fn new(name: &str, parent: &Element) -> Result<Self, AutosarAbstractionError> {
605        let node_elem = parent.create_named_sub_element(ElementName::CanTpNode, name)?;
606        Ok(Self(node_elem))
607    }
608
609    /// set the `CanTpAddress` of this Node
610    pub fn set_address(&self, address: &CanTpAddress) -> Result<(), AutosarAbstractionError> {
611        self.element()
612            .get_or_create_sub_element(ElementName::TpAddressRef)?
613            .set_reference_target(address.element())?;
614        Ok(())
615    }
616
617    /// get the `CanTpAddress` of this Node
618    #[must_use]
619    pub fn address(&self) -> Option<CanTpAddress> {
620        self.element()
621            .get_sub_element(ElementName::TpAddressRef)
622            .and_then(|elem| elem.get_reference_target().ok())
623            .and_then(|target| CanTpAddress::try_from(target).ok())
624    }
625
626    /// set the reference to a `CanCommunicationConnector` of an ECU and a `CanPhysicalChannel`
627    ///
628    /// The connector connects the ECU to the physical channel, so by setting this reference, the
629    /// ECU is also connected to the `CanTpNode`
630    pub fn set_connector(&self, connector: &CanCommunicationConnector) -> Result<(), AutosarAbstractionError> {
631        self.element()
632            .get_or_create_sub_element(ElementName::ConnectorRef)?
633            .set_reference_target(connector.element())?;
634        Ok(())
635    }
636
637    /// get the `CanCommunicationConnector` of this Node
638    #[must_use]
639    pub fn connector(&self) -> Option<CanCommunicationConnector> {
640        self.element()
641            .get_sub_element(ElementName::ConnectorRef)
642            .and_then(|elem| elem.get_reference_target().ok())
643            .and_then(|target| CanCommunicationConnector::try_from(target).ok())
644    }
645}
646
647//#########################################################
648
649#[cfg(test)]
650mod test {
651    use super::*;
652    use crate::{AutosarModelAbstraction, SystemCategory, communication::DiagPduType};
653    use autosar_data::AutosarVersion;
654
655    #[test]
656    fn can_transport_protocol() {
657        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
658        let package = model.get_or_create_package("/pkg1").unwrap();
659
660        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
661        let can_cluster = system.create_can_cluster("can_cluster", &package, None).unwrap();
662        let can_channel = can_cluster.create_physical_channel("can_channel").unwrap();
663        let ecu_instance = system.create_ecu_instance("ecu_instance", &package).unwrap();
664        let communication_controller = ecu_instance.create_can_communication_controller("can_ctrl").unwrap();
665        let connector = communication_controller
666            .connect_physical_channel("name", &can_channel)
667            .unwrap();
668
669        let can_tp_config = system
670            .create_can_tp_config("can_tp_config", &package, &can_cluster)
671            .unwrap();
672        assert_eq!(can_tp_config.cluster().unwrap(), can_cluster);
673
674        let tp_ecu = can_tp_config.create_can_tp_ecu(&ecu_instance, Some(1.0)).unwrap();
675        assert_eq!(can_tp_config.can_tp_ecus().count(), 1);
676        assert_eq!(can_tp_config.can_tp_ecus().next().unwrap(), tp_ecu);
677        assert_eq!(tp_ecu.ecu_instance().unwrap().name().unwrap(), "ecu_instance");
678        assert_eq!(tp_ecu.cycle_time_main_function().unwrap(), 1.0);
679
680        let address = can_tp_config.create_can_tp_address("address", 0x1234).unwrap();
681        assert_eq!(address.tp_address().unwrap(), 0x1234);
682        assert_eq!(can_tp_config.can_tp_addresses().count(), 1);
683        assert_eq!(can_tp_config.can_tp_addresses().next().unwrap(), address);
684
685        let channel = can_tp_config
686            .create_can_tp_channel("channel", 1, CanTpChannelMode::FullDuplex)
687            .unwrap();
688        let channel2 = can_tp_config
689            .create_can_tp_channel("channel2", 2, CanTpChannelMode::FullDuplex)
690            .unwrap();
691        assert_eq!(can_tp_config.can_tp_channels().count(), 2);
692        assert_eq!(channel.channel_id().unwrap(), 1);
693        assert_eq!(channel.channel_mode().unwrap(), CanTpChannelMode::FullDuplex);
694
695        let data_pdu = system.create_n_pdu("data_pdu", &package, 8).unwrap();
696        let data_pdu2 = system.create_n_pdu("data_pdu2", &package, 8).unwrap();
697        let tp_sdu = system
698            .create_dcm_ipdu("ipdu", &package, 4096, DiagPduType::DiagRequest)
699            .unwrap();
700        let tp_sdu2 = system
701            .create_dcm_ipdu("ipdu2", &package, 4096, DiagPduType::DiagResponse)
702            .unwrap();
703
704        let connection = can_tp_config
705            .create_can_tp_connection(
706                Some("connection"),
707                CanTpAddressingFormat::Standard,
708                &channel,
709                &data_pdu,
710                &tp_sdu,
711                false,
712            )
713            .unwrap();
714        assert_eq!(can_tp_config.can_tp_connections().count(), 1);
715        assert_eq!(can_tp_config.can_tp_connections().next().unwrap(), connection);
716
717        assert_eq!(connection.name().unwrap(), "connection");
718        // in a CanTpConnection, the name is provided by the optional IDENT sub-element
719        connection
720            .element()
721            .remove_sub_element_kind(ElementName::Ident)
722            .unwrap();
723        assert_eq!(connection.name(), None);
724        connection.set_name("new_name").unwrap();
725        assert_eq!(connection.name().unwrap(), "new_name");
726
727        assert_eq!(connection.channel().unwrap(), channel);
728        connection.set_channel(&channel2).unwrap();
729        assert_eq!(connection.channel().unwrap(), channel2);
730
731        assert_eq!(connection.data_pdu().unwrap(), data_pdu);
732        connection.set_data_pdu(&data_pdu2).unwrap();
733        assert_eq!(connection.data_pdu().unwrap(), data_pdu2);
734
735        assert_eq!(connection.tp_sdu().unwrap(), tp_sdu.into());
736        connection.set_tp_sdu(&tp_sdu2).unwrap();
737        assert_eq!(connection.tp_sdu().unwrap(), tp_sdu2.into());
738
739        assert_eq!(connection.addressing_format().unwrap(), CanTpAddressingFormat::Standard);
740        connection
741            .set_addressing_format(CanTpAddressingFormat::Extended)
742            .unwrap();
743        assert_eq!(connection.addressing_format().unwrap(), CanTpAddressingFormat::Extended);
744
745        assert!(!connection.padding_activation().unwrap());
746        connection.set_padding_activation(true).unwrap();
747        assert!(connection.padding_activation().unwrap());
748
749        let node = can_tp_config.create_can_tp_node("node").unwrap();
750        assert_eq!(can_tp_config.can_tp_nodes().count(), 1);
751        assert_eq!(can_tp_config.can_tp_nodes().next().unwrap(), node);
752
753        node.set_address(&address).unwrap();
754        assert_eq!(node.address().unwrap(), address);
755        node.set_connector(&connector).unwrap();
756        assert_eq!(node.connector().unwrap(), connector);
757        connection.set_transmitter(&node).unwrap();
758        assert_eq!(connection.transmitter().unwrap(), node);
759
760        connection.add_receiver(&node).unwrap();
761        assert_eq!(connection.receivers().count(), 1);
762    }
763}