autosar_data_abstraction/communication/physical_channel/ethernet/
soad_old.rs

1use crate::communication::{
2    AbstractPdu, EthernetPhysicalChannel, EventGroupControlType, Pdu, PduCollectionTrigger, PduTriggering,
3    PhysicalChannel, SocketAddress, TpConfig,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
7};
8use autosar_data::{Element, ElementName, EnumItem};
9
10//##################################################################
11
12/// A `SocketConnectionBundle` describes a connection between a server port and multiple client ports.
13/// It contains multiple bundled connections, each transporting one or more PDUs.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct SocketConnectionBundle(Element);
16abstraction_element!(SocketConnectionBundle, SocketConnectionBundle);
17impl IdentifiableAbstractionElement for SocketConnectionBundle {}
18
19impl SocketConnectionBundle {
20    pub(crate) fn new(
21        name: &str,
22        server_port: &SocketAddress,
23        connections_elem: &Element,
24    ) -> Result<Self, AutosarAbstractionError> {
25        let scb_elem = connections_elem.create_named_sub_element(ElementName::SocketConnectionBundle, name)?;
26        let scb = Self(scb_elem);
27
28        scb.set_server_port(server_port)?;
29
30        Ok(scb)
31    }
32
33    /// get the physical channel containing this socket connection bundle
34    ///
35    /// # Example
36    ///
37    /// ```
38    /// # use autosar_data::*;
39    /// # use autosar_data_abstraction::*;
40    /// # use autosar_data_abstraction::communication::*;
41    /// # fn main() -> Result<(), AutosarAbstractionError> {
42    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
43    /// # let package = model.get_or_create_package("/pkg1")?;
44    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
45    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
46    /// # let channel = cluster.create_physical_channel("Channel", None)?;
47    /// # let server_endpoint = channel.create_network_endpoint("ServerAddress", NetworkEndpointAddress::IPv4 {
48    /// #    address: Some("192.168.0.1".to_string()),
49    /// #    address_source: Some(IPv4AddressSource::Fixed),
50    /// #    default_gateway: None,
51    /// #    network_mask: None
52    /// # }, None)?;
53    /// # let server_socket = channel.create_socket_address("ServerSocket", &server_endpoint, &TpConfig::TcpTp { port_number: Some(1234), port_dynamically_assigned: None }, SocketAddressType::Unicast(None))?;
54    /// # let client_endpoint = channel.create_network_endpoint("ClientAddress", NetworkEndpointAddress::IPv4 {
55    /// #    address: Some("192.168.0.2".to_string()),
56    /// #    address_source: Some(IPv4AddressSource::Fixed),
57    /// #    default_gateway: None,
58    /// #    network_mask: None
59    /// # }, None)?;
60    /// # let client_socket = channel.create_socket_address("ClientSocket", &client_endpoint, &TpConfig::TcpTp { port_number: Some(1235), port_dynamically_assigned: None }, SocketAddressType::Unicast(None))?;
61    /// let bundle = channel.create_socket_connection_bundle("Bundle", &server_socket)?;
62    /// assert_eq!(channel, bundle.physical_channel()?);
63    /// # Ok(())}
64    /// ```
65    pub fn physical_channel(&self) -> Result<EthernetPhysicalChannel, AutosarAbstractionError> {
66        let channel = self.element().named_parent()?.unwrap();
67        EthernetPhysicalChannel::try_from(channel)
68    }
69
70    ///set the server port of this socket connection bundle
71    pub fn set_server_port(&self, server_port: &SocketAddress) -> Result<(), AutosarAbstractionError> {
72        self.element()
73            .get_or_create_sub_element(ElementName::ServerPortRef)?
74            .set_reference_target(server_port.element())?;
75        Ok(())
76    }
77
78    /// get the server port of this socket connection bundle
79    #[must_use]
80    pub fn server_port(&self) -> Option<SocketAddress> {
81        self.element()
82            .get_sub_element(ElementName::ServerPortRef)
83            .and_then(|spr| spr.get_reference_target().ok())
84            .and_then(|sp| SocketAddress::try_from(sp).ok())
85    }
86
87    /// create a bundled `SocketConnection` between the server port and a client port
88    pub fn create_bundled_connection(
89        &self,
90        client_port: &SocketAddress,
91    ) -> Result<SocketConnection, AutosarAbstractionError> {
92        let Some(server_port) = self.server_port() else {
93            return Err(AutosarAbstractionError::InvalidParameter(
94                "SocketConnectionBundle has no server port".to_string(),
95            ));
96        };
97        let own_tp_config = server_port.tp_config();
98        let remote_tp_config = client_port.tp_config();
99        match (own_tp_config, remote_tp_config) {
100            (Some(TpConfig::TcpTp { .. }), Some(TpConfig::TcpTp { .. }))
101            | (Some(TpConfig::UdpTp { .. }), Some(TpConfig::UdpTp { .. }))
102            | (None, None) => { /* ok */ }
103            _ => {
104                return Err(AutosarAbstractionError::InvalidParameter(
105                    "Both SocketAddresses must use the same transport protocol".to_string(),
106                ));
107            }
108        }
109        let conn = self
110            .0
111            .get_or_create_sub_element(ElementName::BundledConnections)?
112            .create_sub_element(ElementName::SocketConnection)?;
113
114        SocketConnection::new(conn, client_port)
115    }
116
117    /// create an iterator over all bundled connections in this socket connection bundle
118    pub fn bundled_connections(&self) -> impl Iterator<Item = SocketConnection> + Send + use<> {
119        self.element()
120            .get_sub_element(ElementName::BundledConnections)
121            .into_iter()
122            .flat_map(|bc| bc.sub_elements())
123            .filter_map(|elem| SocketConnection::try_from(elem).ok())
124    }
125}
126
127//##################################################################
128
129/// A socketConnection inside a `SocketConnectionBundle` describes a single connection to a specific client port.
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131pub struct SocketConnection(Element);
132abstraction_element!(SocketConnection, SocketConnection);
133
134impl SocketConnection {
135    /// The PDU header id for SD messages must always be set to `0xFFFF_8100`
136    pub const SD_HEADER_ID: u32 = 0xFFFF_8100;
137
138    // create a new SocketConnection (internal)
139    pub(crate) fn new(conn: Element, client_port: &SocketAddress) -> Result<Self, AutosarAbstractionError> {
140        let conn = Self(conn);
141        conn.set_client_port(client_port)?;
142
143        Ok(conn)
144    }
145
146    /// get the socket connection bundle containing this socket connection
147    ///
148    /// # Example
149    ///
150    /// ```
151    /// # use autosar_data::*;
152    /// # use autosar_data_abstraction::*;
153    /// # use autosar_data_abstraction::communication::*;
154    /// # fn main() -> Result<(), AutosarAbstractionError> {
155    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
156    /// # let package = model.get_or_create_package("/pkg1")?;
157    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
158    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
159    /// # let channel = cluster.create_physical_channel("Channel", None)?;
160    /// # let server_endpoint = channel.create_network_endpoint("ServerAddress", NetworkEndpointAddress::IPv4 {
161    /// #    address: Some("192.168.0.1".to_string()),
162    /// #    address_source: Some(IPv4AddressSource::Fixed),
163    /// #    default_gateway: None,
164    /// #    network_mask: None
165    /// # }, None)?;
166    /// # let server_socket = channel.create_socket_address("ServerSocket", &server_endpoint, &TpConfig::TcpTp { port_number: Some(1234), port_dynamically_assigned: None }, SocketAddressType::Unicast(None))?;
167    /// # let client_endpoint = channel.create_network_endpoint("ClientAddress", NetworkEndpointAddress::IPv4 {
168    /// #    address: Some("192.168.0.2".to_string()),
169    /// #    address_source: Some(IPv4AddressSource::Fixed),
170    /// #    default_gateway: None,
171    /// #    network_mask: None
172    /// # }, None)?;
173    /// # let client_socket = channel.create_socket_address("ClientSocket", &client_endpoint, &TpConfig::TcpTp { port_number: Some(1235), port_dynamically_assigned: None }, SocketAddressType::Unicast(None))?;
174    /// let bundle = channel.create_socket_connection_bundle("Bundle", &server_socket)?;
175    /// let connection = bundle.create_bundled_connection(&client_socket)?;
176    /// assert_eq!(bundle, connection.socket_connection_bundle()?);
177    /// # Ok(())}
178    /// ```
179    pub fn socket_connection_bundle(&self) -> Result<SocketConnectionBundle, AutosarAbstractionError> {
180        let bundle = self.element().named_parent()?.unwrap();
181        SocketConnectionBundle::try_from(bundle)
182    }
183
184    /// set the client port of this socket connection
185    pub fn set_client_port(&self, client_port: &SocketAddress) -> Result<(), AutosarAbstractionError> {
186        self.element()
187            .get_or_create_sub_element(ElementName::ClientPortRef)?
188            .set_reference_target(client_port.element())?;
189        Ok(())
190    }
191
192    /// get the client port of this socket connection
193    #[must_use]
194    pub fn client_port(&self) -> Option<SocketAddress> {
195        self.element()
196            .get_sub_element(ElementName::ClientPortRef)
197            .and_then(|cpr| cpr.get_reference_target().ok())
198            .and_then(|cp| SocketAddress::try_from(cp).ok())
199    }
200
201    /// Create a new `SocketConnectionIpduIdentifier` in this socket connection
202    ///
203    /// The `SocketConnectionIpduIdentifier` is used to trigger a PDU, and contains associated settings
204    /// The function returns a tuple of the new `SocketConnectionIpduIdentifier` and the associated `PduTriggering`
205    /// since most callers only need the `PduTriggering`.
206    pub fn create_socket_connection_ipdu_identifier<T: AbstractPdu>(
207        &self,
208        pdu: &T,
209        header_id: u32,
210        timeout: Option<f64>,
211        collection_trigger: Option<PduCollectionTrigger>,
212    ) -> Result<(SocketConnectionIpduIdentifier, PduTriggering), AutosarAbstractionError> {
213        SocketConnectionIpduIdentifier::new(
214            self.element(),
215            &pdu.clone().into(),
216            header_id,
217            timeout,
218            collection_trigger,
219        )
220    }
221
222    /// create an iterator over all `SocketConnectionIpduIdentifiers` in this socket connection
223    pub fn socket_connection_ipdu_identifiers(
224        &self,
225    ) -> impl Iterator<Item = SocketConnectionIpduIdentifier> + Send + use<> {
226        self.element()
227            .get_sub_element(ElementName::Pdus)
228            .into_iter()
229            .flat_map(|pdus| pdus.sub_elements())
230            .filter_map(|elem| SocketConnectionIpduIdentifier::try_from(elem).ok())
231    }
232
233    /// create an iterator over all PDU triggerings in this socket connection
234    pub fn pdu_triggerings(&self) -> impl Iterator<Item = PduTriggering> + Send + use<> {
235        self.element()
236            .get_sub_element(ElementName::Pdus)
237            .into_iter()
238            .flat_map(|pdus| pdus.sub_elements())
239            .filter_map(|scii: Element| {
240                scii.get_sub_element(ElementName::PduTriggeringRef)
241                    .and_then(|pt| pt.get_reference_target().ok())
242                    .and_then(|pt| PduTriggering::try_from(pt).ok())
243            })
244    }
245
246    /// set or remove the `client_ip_addr_from_connection_request` attribute for this socket connection
247    ///
248    /// if the value is Some(true), the attribute is set to "true"
249    /// if the value is Some(false), the attribute is set to "false"
250    /// if the value is None, the attribute is removed
251    pub fn set_client_ip_addr_from_connection_request(
252        &self,
253        value: Option<bool>,
254    ) -> Result<(), AutosarAbstractionError> {
255        if let Some(value) = value {
256            self.element()
257                .get_or_create_sub_element(ElementName::ClientIpAddrFromConnectionRequest)?
258                .set_character_data(value.to_string())?;
259        } else {
260            let _ = self
261                .element()
262                .remove_sub_element_kind(ElementName::ClientIpAddrFromConnectionRequest);
263        }
264        Ok(())
265    }
266
267    /// get the value of the `client_ip_addr_from_connection_request` attribute for this socket connection
268    #[must_use]
269    pub fn client_ip_addr_from_connection_request(&self) -> Option<bool> {
270        self.element()
271            .get_sub_element(ElementName::ClientIpAddrFromConnectionRequest)
272            .and_then(|elem| elem.character_data())
273            .and_then(|cdata| cdata.parse_bool())
274    }
275
276    /// set or remove the `client_port_from_connection_request` attribute for this socket connection
277    ///
278    /// if the value is Some(true), the attribute is set to "true"
279    /// if the value is Some(false), the attribute is set to "false"
280    /// if the value is None, the attribute is removed
281    pub fn set_client_port_from_connection_request(&self, value: Option<bool>) -> Result<(), AutosarAbstractionError> {
282        if let Some(value) = value {
283            self.element()
284                .get_or_create_sub_element(ElementName::ClientPortFromConnectionRequest)?
285                .set_character_data(value.to_string())?;
286        } else {
287            let _ = self
288                .element()
289                .remove_sub_element_kind(ElementName::ClientPortFromConnectionRequest);
290        }
291        Ok(())
292    }
293
294    /// get the value of the `client_port_from_connection_request` attribute for this socket connection
295    #[must_use]
296    pub fn client_port_from_connection_request(&self) -> Option<bool> {
297        self.element()
298            .get_sub_element(ElementName::ClientPortFromConnectionRequest)
299            .and_then(|elem| elem.character_data())
300            .and_then(|cdata| cdata.parse_bool())
301    }
302
303    /// set the `RuntimeIpAddressConfiguration` attribute for this socket connection
304    ///
305    /// If `state` is true, the attribute is set to "Sd"
306    /// If `state` is false, the attribute is removed
307    pub fn set_runtime_ip_address_configuration(&self, state: bool) -> Result<(), AutosarAbstractionError> {
308        if state {
309            self.element()
310                .get_or_create_sub_element(ElementName::RuntimeIpAddressConfiguration)?
311                .set_character_data(EnumItem::Sd)?;
312        } else {
313            let _ = self
314                .element()
315                .remove_sub_element_kind(ElementName::RuntimeIpAddressConfiguration);
316        }
317        Ok(())
318    }
319
320    /// check if the value of the `RuntimeIpAddressConfiguration` attribute is "SD"
321    #[must_use]
322    pub fn runtime_ip_address_configuration(&self) -> bool {
323        let enum_value = self
324            .element()
325            .get_sub_element(ElementName::RuntimeIpAddressConfiguration)
326            .and_then(|elem| elem.character_data())
327            .and_then(|cdata| cdata.enum_value());
328        enum_value == Some(EnumItem::Sd)
329    }
330
331    /// set the `RuntimePortConfiguration` attributes for this socket connection
332    ///
333    /// If `state` is true, the attribute is set to "Sd"
334    /// If `state` is false, the attributes is removed
335    pub fn set_runtime_port_configuration(&self, state: bool) -> Result<(), AutosarAbstractionError> {
336        if state {
337            self.element()
338                .get_or_create_sub_element(ElementName::RuntimePortConfiguration)?
339                .set_character_data(EnumItem::Sd)?;
340        } else {
341            let _ = self
342                .element()
343                .remove_sub_element_kind(ElementName::RuntimePortConfiguration);
344        }
345        Ok(())
346    }
347
348    /// check if the value of the `RuntimePortConfiguration` attribute is "SD"
349    #[must_use]
350    pub fn runtime_port_configuration(&self) -> bool {
351        let enum_value = self
352            .element()
353            .get_sub_element(ElementName::RuntimePortConfiguration)
354            .and_then(|elem| elem.character_data())
355            .and_then(|cdata| cdata.enum_value());
356        enum_value == Some(EnumItem::Sd)
357    }
358}
359
360//##################################################################
361
362/// A `SocketConnectionIpduIdentifier` is used to trigger a PDU in a `SocketConnection`.
363///
364/// In addition to the Pdu Triggering, it also contains associated settings like the
365/// header id, timeout and collection trigger.
366#[derive(Debug, Clone, PartialEq, Eq, Hash)]
367pub struct SocketConnectionIpduIdentifier(Element);
368abstraction_element!(SocketConnectionIpduIdentifier, SocketConnectionIpduIdentifier);
369
370impl SocketConnectionIpduIdentifier {
371    pub(crate) fn new<T: AbstractPdu>(
372        parent: &Element,
373        pdu: &T,
374        header_id: u32,
375        timeout: Option<f64>,
376        collection_trigger: Option<PduCollectionTrigger>,
377    ) -> Result<(Self, PduTriggering), AutosarAbstractionError> {
378        let scii_elem = parent
379            .get_or_create_sub_element(ElementName::Pdus)?
380            .create_sub_element(ElementName::SocketConnectionIpduIdentifier)?;
381        let scii = Self(scii_elem);
382        let pt = scii.trigger_pdu(&pdu.clone().into())?;
383        scii.set_header_id(header_id)?;
384        scii.set_timeout(timeout)?;
385        scii.set_collection_trigger(collection_trigger)?;
386
387        Ok((scii, pt))
388    }
389
390    /// get the `SocketConnection` containing this `SocketConnectionIpduIdentifier`
391    pub fn socket_connection(&self) -> Result<SocketConnection, AutosarAbstractionError> {
392        // SOCKET-CONNECTION > PDUS > SOCKET-CONNECTION-IPDU-IDENTIFIER
393        let socket_connection_elem = self.element().parent()?.unwrap().parent()?.unwrap();
394        SocketConnection::try_from(socket_connection_elem)
395    }
396
397    /// trigger a PDU in this `SocketConnectionIpduIdentifier`, creating a `PduTriggering`
398    pub fn trigger_pdu(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
399        if let Some(old_pt) = self.pdu_triggering() {
400            // there is already a PduTriggering in this SocketConnectionIpduIdentifier
401            // remove it first -- ideally this should be old_pt.remove() but that doesn't exist yet
402            if let Ok(Some(parent)) = old_pt.element().parent() {
403                parent.remove_sub_element(old_pt.element().clone())?;
404            }
405        }
406        let channel = self
407            .socket_connection()?
408            .socket_connection_bundle()?
409            .physical_channel()?;
410        let pt = PduTriggering::new(pdu, &PhysicalChannel::Ethernet(channel))?;
411
412        self.element()
413            .get_or_create_sub_element(ElementName::PduTriggeringRef)?
414            .set_reference_target(pt.element())?;
415        Ok(pt)
416    }
417
418    /// get the `PduTriggering` associated with this `SocketConnectionIpduIdentifier`
419    #[must_use]
420    pub fn pdu_triggering(&self) -> Option<PduTriggering> {
421        self.element()
422            .get_sub_element(ElementName::PduTriggeringRef)
423            .and_then(|elem| elem.get_reference_target().ok())
424            .and_then(|pt| PduTriggering::try_from(pt).ok())
425    }
426
427    /// set the header id for this `SocketConnectionIpduIdentifier`
428    pub fn set_header_id(&self, header_id: u32) -> Result<(), AutosarAbstractionError> {
429        self.element()
430            .get_or_create_sub_element(ElementName::HeaderId)?
431            .set_character_data(header_id.to_string())?;
432        Ok(())
433    }
434
435    /// get the header id for this `SocketConnectionIpduIdentifier`
436    #[must_use]
437    pub fn header_id(&self) -> Option<u64> {
438        self.element()
439            .get_sub_element(ElementName::HeaderId)
440            .and_then(|elem| elem.character_data())
441            .and_then(|cdata| cdata.parse_integer())
442    }
443
444    /// set the timeout for this `SocketConnectionIpduIdentifier`
445    pub fn set_timeout(&self, timeout: Option<f64>) -> Result<(), AutosarAbstractionError> {
446        if let Some(timeout) = timeout {
447            self.element()
448                .get_or_create_sub_element(ElementName::PduCollectionPduTimeout)?
449                .set_character_data(timeout)?;
450        } else {
451            let _ = self
452                .element()
453                .remove_sub_element_kind(ElementName::PduCollectionPduTimeout);
454        }
455        Ok(())
456    }
457
458    /// get the timeout for this `SocketConnectionIpduIdentifier`
459    #[must_use]
460    pub fn timeout(&self) -> Option<f64> {
461        self.element()
462            .get_sub_element(ElementName::PduCollectionPduTimeout)
463            .and_then(|elem| elem.character_data())
464            .and_then(|cdata| cdata.float_value())
465    }
466
467    /// set the collection trigger for this `SocketConnectionIpduIdentifier`
468    pub fn set_collection_trigger(&self, trigger: Option<PduCollectionTrigger>) -> Result<(), AutosarAbstractionError> {
469        if let Some(trigger) = trigger {
470            self.element()
471                .get_or_create_sub_element(ElementName::PduCollectionTrigger)?
472                .set_character_data::<EnumItem>(trigger.into())?;
473        } else {
474            let _ = self
475                .element()
476                .remove_sub_element_kind(ElementName::PduCollectionTrigger);
477        }
478        Ok(())
479    }
480
481    /// get the collection trigger for this `SocketConnectionIpduIdentifier`
482    #[must_use]
483    pub fn collection_trigger(&self) -> Option<PduCollectionTrigger> {
484        self.element()
485            .get_sub_element(ElementName::PduCollectionTrigger)
486            .and_then(|elem| elem.character_data())
487            .and_then(|cdata| cdata.enum_value())
488            .and_then(|eval| eval.try_into().ok())
489    }
490
491    /// add a reference to a `SoAdRoutingGroup` to this `SocketConnectionIpduIdentifier`
492    pub fn add_routing_group(&self, routing_group: &SoAdRoutingGroup) -> Result<(), AutosarAbstractionError> {
493        self.element()
494            .get_or_create_sub_element(ElementName::RoutingGroupRefs)?
495            .create_sub_element(ElementName::RoutingGroupRef)?
496            .set_reference_target(routing_group.element())?;
497        Ok(())
498    }
499
500    /// create an iterator over all `SoAdRoutingGroups` referenced by this `SocketConnectionIpduIdentifier`
501    pub fn routing_groups(&self) -> impl Iterator<Item = SoAdRoutingGroup> + Send + use<> {
502        self.element()
503            .get_sub_element(ElementName::RoutingGroupRefs)
504            .into_iter()
505            .flat_map(|rgr| rgr.sub_elements())
506            .filter_map(|ref_elem| {
507                ref_elem
508                    .get_reference_target()
509                    .ok()
510                    .and_then(|elem| SoAdRoutingGroup::try_from(elem).ok())
511            })
512    }
513}
514
515//##################################################################
516
517/// A `SoAdRoutingGroup` is used to link `SomeIp` settings in Consumed/ProvidedServiceInstances
518/// to the `SocketConnectionBundles` used for transmission.
519/// `SoAdRoutingGroups` are part of the old way of configuring Ethernet communication in AUTOSAR.
520#[derive(Debug, Clone, PartialEq, Eq, Hash)]
521pub struct SoAdRoutingGroup(Element);
522abstraction_element!(SoAdRoutingGroup, SoAdRoutingGroup);
523impl IdentifiableAbstractionElement for SoAdRoutingGroup {}
524
525impl SoAdRoutingGroup {
526    /// create a new `SoAdRoutingGroup`
527    ///
528    /// `SoAdRoutingGroups` are used to link `SomeIp` settings in Consumed/ProvidedServiceInstances
529    /// to the `SocketConnectionBundles` used for transmission.
530    /// `SoAdRoutingGroups` are part of the old way of configuring Ethernet communication in AUTOSAR.
531    pub(crate) fn new(
532        name: &str,
533        package: &ArPackage,
534        control_type: Option<EventGroupControlType>,
535    ) -> Result<Self, AutosarAbstractionError> {
536        let srg_elem: Element = package
537            .element()
538            .get_or_create_sub_element(ElementName::Elements)?
539            .create_named_sub_element(ElementName::SoAdRoutingGroup, name)?;
540        let srg = Self(srg_elem);
541
542        if let Some(control_type) = control_type {
543            srg.set_control_type(control_type)?;
544        }
545
546        Ok(srg)
547    }
548
549    /// set the `EventGroupControlType` of this `SoAdRoutingGroup`
550    pub fn set_control_type(&self, control_type: EventGroupControlType) -> Result<(), AutosarAbstractionError> {
551        self.element()
552            .get_or_create_sub_element(ElementName::EventGroupControlType)?
553            .set_character_data::<EnumItem>(control_type.into())?;
554        Ok(())
555    }
556
557    /// get the `EventGroupControlType` of this `SoAdRoutingGroup`
558    #[must_use]
559    pub fn control_type(&self) -> Option<EventGroupControlType> {
560        self.element()
561            .get_sub_element(ElementName::EventGroupControlType)
562            .and_then(|elem| elem.character_data())
563            .and_then(|cdata| cdata.enum_value())
564            .and_then(|eval| eval.try_into().ok())
565    }
566}
567
568//##################################################################
569
570#[cfg(test)]
571mod test {
572    use super::*;
573    use crate::{
574        AutosarModelAbstraction, SystemCategory,
575        communication::{IPv4AddressSource, NetworkEndpointAddress, SocketAddressType},
576    };
577    use autosar_data::AutosarVersion;
578
579    #[test]
580    fn test_socket_connection_bundle() {
581        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
582        let package = model.get_or_create_package("/pkg1").unwrap();
583        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
584        let cluster = system.create_ethernet_cluster("Cluster", &package).unwrap();
585        let channel = cluster.create_physical_channel("Channel", None).unwrap();
586        let server_endpoint = channel
587            .create_network_endpoint(
588                "ServerAddress",
589                NetworkEndpointAddress::IPv4 {
590                    address: Some("192.168.0.1".to_string()),
591                    address_source: Some(IPv4AddressSource::Fixed),
592                    default_gateway: None,
593                    network_mask: None,
594                },
595                None,
596            )
597            .unwrap();
598        let server_socket = channel
599            .create_socket_address(
600                "ServerSocket",
601                &server_endpoint,
602                &TpConfig::TcpTp {
603                    port_number: Some(1234),
604                    port_dynamically_assigned: None,
605                },
606                SocketAddressType::Unicast(None),
607            )
608            .unwrap();
609        let bundle = channel
610            .create_socket_connection_bundle("Bundle", &server_socket)
611            .unwrap();
612        assert_eq!(channel.socket_connection_bundles().next(), Some(bundle.clone()));
613        assert_eq!(channel.socket_connection_bundles().count(), 1);
614        assert_eq!(channel, bundle.physical_channel().unwrap());
615        assert_eq!(Some(server_socket), bundle.server_port());
616
617        let client_endpoint = channel
618            .create_network_endpoint(
619                "ClientAddress",
620                NetworkEndpointAddress::IPv4 {
621                    address: Some("192.168.0.2".to_string()),
622                    address_source: Some(IPv4AddressSource::Fixed),
623                    default_gateway: None,
624                    network_mask: None,
625                },
626                None,
627            )
628            .unwrap();
629        let client_socket = channel
630            .create_socket_address(
631                "ClientSocket",
632                &client_endpoint,
633                &TpConfig::TcpTp {
634                    port_number: Some(1235),
635                    port_dynamically_assigned: None,
636                },
637                SocketAddressType::Unicast(None),
638            )
639            .unwrap();
640        let connection = bundle.create_bundled_connection(&client_socket).unwrap();
641
642        let pdu = system.create_isignal_ipdu("Pdu", &package, 8).unwrap();
643        let (scii, pt) = connection
644            .create_socket_connection_ipdu_identifier(&pdu, 0x1234, Some(0.5), Some(PduCollectionTrigger::Always))
645            .unwrap();
646        assert_eq!(connection.socket_connection_ipdu_identifiers().count(), 1);
647        assert_eq!(pt, scii.pdu_triggering().unwrap());
648        assert_eq!(Some(pt.clone()), connection.pdu_triggerings().next());
649        assert_eq!(Some(0x1234), scii.header_id());
650        assert_eq!(Some(0.5), scii.timeout());
651        assert_eq!(Some(PduCollectionTrigger::Always), scii.collection_trigger());
652
653        scii.set_header_id(0x5678).unwrap();
654        assert_eq!(Some(0x5678), scii.header_id());
655        scii.set_timeout(Some(1.5)).unwrap();
656        assert_eq!(Some(1.5), scii.timeout());
657        scii.set_collection_trigger(Some(PduCollectionTrigger::Never)).unwrap();
658        assert_eq!(Some(PduCollectionTrigger::Never), scii.collection_trigger());
659        connection
660            .set_client_ip_addr_from_connection_request(Some(true))
661            .unwrap();
662        assert_eq!(connection.client_ip_addr_from_connection_request(), Some(true));
663        connection.set_client_ip_addr_from_connection_request(None).unwrap();
664        assert_eq!(connection.client_ip_addr_from_connection_request(), None);
665        connection.set_client_port_from_connection_request(Some(false)).unwrap();
666        assert_eq!(connection.client_port_from_connection_request(), Some(false));
667        connection.set_client_port_from_connection_request(None).unwrap();
668        assert_eq!(connection.client_port_from_connection_request(), None);
669        connection.set_runtime_ip_address_configuration(true).unwrap();
670        assert!(connection.runtime_ip_address_configuration());
671        connection.set_runtime_port_configuration(true).unwrap();
672        assert!(connection.runtime_port_configuration());
673        connection.set_runtime_ip_address_configuration(false).unwrap();
674        assert!(!connection.runtime_ip_address_configuration());
675        connection.set_runtime_port_configuration(false).unwrap();
676        assert!(!connection.runtime_port_configuration());
677
678        let routing_group = system
679            .create_so_ad_routing_group("RoutingGroup", &package, None)
680            .unwrap();
681        scii.add_routing_group(&routing_group).unwrap();
682        assert_eq!(scii.routing_groups().next(), Some(routing_group.clone()));
683        assert_eq!(scii.routing_groups().count(), 1);
684
685        assert_eq!(routing_group.control_type(), None);
686        routing_group
687            .set_control_type(EventGroupControlType::TriggerUnicast)
688            .unwrap();
689        assert_eq!(
690            routing_group.control_type(),
691            Some(EventGroupControlType::TriggerUnicast)
692        );
693    }
694}