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    /// remove this `SocketConnectionIpduIdentifier`
391    pub fn remove(self, deep: bool) -> Result<(), AutosarAbstractionError> {
392        let opt_pdu_triggering = self.pdu_triggering();
393
394        AbstractionElement::remove(self, deep)?;
395
396        if let Some(pt) = opt_pdu_triggering {
397            pt.remove(deep)?;
398        }
399
400        Ok(())
401    }
402
403    /// get the `SocketConnection` containing this `SocketConnectionIpduIdentifier`
404    pub fn socket_connection(&self) -> Result<SocketConnection, AutosarAbstractionError> {
405        // SOCKET-CONNECTION > PDUS > SOCKET-CONNECTION-IPDU-IDENTIFIER
406        let socket_connection_elem = self.element().parent()?.unwrap().parent()?.unwrap();
407        SocketConnection::try_from(socket_connection_elem)
408    }
409
410    /// trigger a PDU in this `SocketConnectionIpduIdentifier`, creating a `PduTriggering`
411    pub fn trigger_pdu(&self, pdu: &Pdu) -> Result<PduTriggering, AutosarAbstractionError> {
412        if let Some(old_pt) = self.pdu_triggering() {
413            // there is already a PduTriggering in this SocketConnectionIpduIdentifier
414            // remove it first
415            old_pt.remove(false)?;
416        }
417        let channel = self
418            .socket_connection()?
419            .socket_connection_bundle()?
420            .physical_channel()?;
421        let pt = PduTriggering::new(pdu, &PhysicalChannel::Ethernet(channel))?;
422
423        self.element()
424            .get_or_create_sub_element(ElementName::PduTriggeringRef)?
425            .set_reference_target(pt.element())?;
426        Ok(pt)
427    }
428
429    /// get the `PduTriggering` associated with this `SocketConnectionIpduIdentifier`
430    #[must_use]
431    pub fn pdu_triggering(&self) -> Option<PduTriggering> {
432        self.element()
433            .get_sub_element(ElementName::PduTriggeringRef)
434            .and_then(|elem| elem.get_reference_target().ok())
435            .and_then(|pt| PduTriggering::try_from(pt).ok())
436    }
437
438    /// set the header id for this `SocketConnectionIpduIdentifier`
439    pub fn set_header_id(&self, header_id: u32) -> Result<(), AutosarAbstractionError> {
440        self.element()
441            .get_or_create_sub_element(ElementName::HeaderId)?
442            .set_character_data(header_id.to_string())?;
443        Ok(())
444    }
445
446    /// get the header id for this `SocketConnectionIpduIdentifier`
447    #[must_use]
448    pub fn header_id(&self) -> Option<u64> {
449        self.element()
450            .get_sub_element(ElementName::HeaderId)
451            .and_then(|elem| elem.character_data())
452            .and_then(|cdata| cdata.parse_integer())
453    }
454
455    /// set the timeout for this `SocketConnectionIpduIdentifier`
456    pub fn set_timeout(&self, timeout: Option<f64>) -> Result<(), AutosarAbstractionError> {
457        if let Some(timeout) = timeout {
458            self.element()
459                .get_or_create_sub_element(ElementName::PduCollectionPduTimeout)?
460                .set_character_data(timeout)?;
461        } else {
462            let _ = self
463                .element()
464                .remove_sub_element_kind(ElementName::PduCollectionPduTimeout);
465        }
466        Ok(())
467    }
468
469    /// get the timeout for this `SocketConnectionIpduIdentifier`
470    #[must_use]
471    pub fn timeout(&self) -> Option<f64> {
472        self.element()
473            .get_sub_element(ElementName::PduCollectionPduTimeout)
474            .and_then(|elem| elem.character_data())
475            .and_then(|cdata| cdata.float_value())
476    }
477
478    /// set the collection trigger for this `SocketConnectionIpduIdentifier`
479    pub fn set_collection_trigger(&self, trigger: Option<PduCollectionTrigger>) -> Result<(), AutosarAbstractionError> {
480        if let Some(trigger) = trigger {
481            self.element()
482                .get_or_create_sub_element(ElementName::PduCollectionTrigger)?
483                .set_character_data::<EnumItem>(trigger.into())?;
484        } else {
485            let _ = self
486                .element()
487                .remove_sub_element_kind(ElementName::PduCollectionTrigger);
488        }
489        Ok(())
490    }
491
492    /// get the collection trigger for this `SocketConnectionIpduIdentifier`
493    #[must_use]
494    pub fn collection_trigger(&self) -> Option<PduCollectionTrigger> {
495        self.element()
496            .get_sub_element(ElementName::PduCollectionTrigger)
497            .and_then(|elem| elem.character_data())
498            .and_then(|cdata| cdata.enum_value())
499            .and_then(|eval| eval.try_into().ok())
500    }
501
502    /// add a reference to a `SoAdRoutingGroup` to this `SocketConnectionIpduIdentifier`
503    pub fn add_routing_group(&self, routing_group: &SoAdRoutingGroup) -> Result<(), AutosarAbstractionError> {
504        self.element()
505            .get_or_create_sub_element(ElementName::RoutingGroupRefs)?
506            .create_sub_element(ElementName::RoutingGroupRef)?
507            .set_reference_target(routing_group.element())?;
508        Ok(())
509    }
510
511    /// create an iterator over all `SoAdRoutingGroups` referenced by this `SocketConnectionIpduIdentifier`
512    pub fn routing_groups(&self) -> impl Iterator<Item = SoAdRoutingGroup> + Send + use<> {
513        self.element()
514            .get_sub_element(ElementName::RoutingGroupRefs)
515            .into_iter()
516            .flat_map(|rgr| rgr.sub_elements())
517            .filter_map(|ref_elem| {
518                ref_elem
519                    .get_reference_target()
520                    .ok()
521                    .and_then(|elem| SoAdRoutingGroup::try_from(elem).ok())
522            })
523    }
524}
525
526//##################################################################
527
528/// A `SoAdRoutingGroup` is 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#[derive(Debug, Clone, PartialEq, Eq, Hash)]
532pub struct SoAdRoutingGroup(Element);
533abstraction_element!(SoAdRoutingGroup, SoAdRoutingGroup);
534impl IdentifiableAbstractionElement for SoAdRoutingGroup {}
535
536impl SoAdRoutingGroup {
537    /// create a new `SoAdRoutingGroup`
538    ///
539    /// `SoAdRoutingGroups` are used to link `SomeIp` settings in Consumed/ProvidedServiceInstances
540    /// to the `SocketConnectionBundles` used for transmission.
541    /// `SoAdRoutingGroups` are part of the old way of configuring Ethernet communication in AUTOSAR.
542    pub(crate) fn new(
543        name: &str,
544        package: &ArPackage,
545        control_type: Option<EventGroupControlType>,
546    ) -> Result<Self, AutosarAbstractionError> {
547        let srg_elem: Element = package
548            .element()
549            .get_or_create_sub_element(ElementName::Elements)?
550            .create_named_sub_element(ElementName::SoAdRoutingGroup, name)?;
551        let srg = Self(srg_elem);
552
553        if let Some(control_type) = control_type {
554            srg.set_control_type(control_type)?;
555        }
556
557        Ok(srg)
558    }
559
560    /// set the `EventGroupControlType` of this `SoAdRoutingGroup`
561    pub fn set_control_type(&self, control_type: EventGroupControlType) -> Result<(), AutosarAbstractionError> {
562        self.element()
563            .get_or_create_sub_element(ElementName::EventGroupControlType)?
564            .set_character_data::<EnumItem>(control_type.into())?;
565        Ok(())
566    }
567
568    /// get the `EventGroupControlType` of this `SoAdRoutingGroup`
569    #[must_use]
570    pub fn control_type(&self) -> Option<EventGroupControlType> {
571        self.element()
572            .get_sub_element(ElementName::EventGroupControlType)
573            .and_then(|elem| elem.character_data())
574            .and_then(|cdata| cdata.enum_value())
575            .and_then(|eval| eval.try_into().ok())
576    }
577}
578
579//##################################################################
580
581#[cfg(test)]
582mod test {
583    use super::*;
584    use crate::{
585        AutosarModelAbstraction, SystemCategory,
586        communication::{IPv4AddressSource, NetworkEndpointAddress, SocketAddressType},
587    };
588    use autosar_data::AutosarVersion;
589
590    #[test]
591    fn test_socket_connection_bundle() {
592        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
593        let package = model.get_or_create_package("/pkg1").unwrap();
594        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
595        let cluster = system.create_ethernet_cluster("Cluster", &package).unwrap();
596        let channel = cluster.create_physical_channel("Channel", None).unwrap();
597        let server_endpoint = channel
598            .create_network_endpoint(
599                "ServerAddress",
600                NetworkEndpointAddress::IPv4 {
601                    address: Some("192.168.0.1".to_string()),
602                    address_source: Some(IPv4AddressSource::Fixed),
603                    default_gateway: None,
604                    network_mask: None,
605                },
606                None,
607            )
608            .unwrap();
609        let server_socket = channel
610            .create_socket_address(
611                "ServerSocket",
612                &server_endpoint,
613                &TpConfig::TcpTp {
614                    port_number: Some(1234),
615                    port_dynamically_assigned: None,
616                },
617                SocketAddressType::Unicast(None),
618            )
619            .unwrap();
620        let bundle = channel
621            .create_socket_connection_bundle("Bundle", &server_socket)
622            .unwrap();
623        assert_eq!(channel.socket_connection_bundles().next(), Some(bundle.clone()));
624        assert_eq!(channel.socket_connection_bundles().count(), 1);
625        assert_eq!(channel, bundle.physical_channel().unwrap());
626        assert_eq!(Some(server_socket), bundle.server_port());
627
628        let client_endpoint = channel
629            .create_network_endpoint(
630                "ClientAddress",
631                NetworkEndpointAddress::IPv4 {
632                    address: Some("192.168.0.2".to_string()),
633                    address_source: Some(IPv4AddressSource::Fixed),
634                    default_gateway: None,
635                    network_mask: None,
636                },
637                None,
638            )
639            .unwrap();
640        let client_socket = channel
641            .create_socket_address(
642                "ClientSocket",
643                &client_endpoint,
644                &TpConfig::TcpTp {
645                    port_number: Some(1235),
646                    port_dynamically_assigned: None,
647                },
648                SocketAddressType::Unicast(None),
649            )
650            .unwrap();
651        let connection = bundle.create_bundled_connection(&client_socket).unwrap();
652
653        let pdu = system.create_isignal_ipdu("Pdu", &package, 8).unwrap();
654        let (scii, pt) = connection
655            .create_socket_connection_ipdu_identifier(&pdu, 0x1234, Some(0.5), Some(PduCollectionTrigger::Always))
656            .unwrap();
657        assert_eq!(connection.socket_connection_ipdu_identifiers().count(), 1);
658        assert_eq!(pt, scii.pdu_triggering().unwrap());
659        assert_eq!(Some(pt.clone()), connection.pdu_triggerings().next());
660        assert_eq!(Some(0x1234), scii.header_id());
661        assert_eq!(Some(0.5), scii.timeout());
662        assert_eq!(Some(PduCollectionTrigger::Always), scii.collection_trigger());
663
664        scii.set_header_id(0x5678).unwrap();
665        assert_eq!(Some(0x5678), scii.header_id());
666        scii.set_timeout(Some(1.5)).unwrap();
667        assert_eq!(Some(1.5), scii.timeout());
668        scii.set_collection_trigger(Some(PduCollectionTrigger::Never)).unwrap();
669        assert_eq!(Some(PduCollectionTrigger::Never), scii.collection_trigger());
670        connection
671            .set_client_ip_addr_from_connection_request(Some(true))
672            .unwrap();
673        assert_eq!(connection.client_ip_addr_from_connection_request(), Some(true));
674        connection.set_client_ip_addr_from_connection_request(None).unwrap();
675        assert_eq!(connection.client_ip_addr_from_connection_request(), None);
676        connection.set_client_port_from_connection_request(Some(false)).unwrap();
677        assert_eq!(connection.client_port_from_connection_request(), Some(false));
678        connection.set_client_port_from_connection_request(None).unwrap();
679        assert_eq!(connection.client_port_from_connection_request(), None);
680        connection.set_runtime_ip_address_configuration(true).unwrap();
681        assert!(connection.runtime_ip_address_configuration());
682        connection.set_runtime_port_configuration(true).unwrap();
683        assert!(connection.runtime_port_configuration());
684        connection.set_runtime_ip_address_configuration(false).unwrap();
685        assert!(!connection.runtime_ip_address_configuration());
686        connection.set_runtime_port_configuration(false).unwrap();
687        assert!(!connection.runtime_port_configuration());
688
689        let routing_group = system
690            .create_so_ad_routing_group("RoutingGroup", &package, None)
691            .unwrap();
692        scii.add_routing_group(&routing_group).unwrap();
693        assert_eq!(scii.routing_groups().next(), Some(routing_group.clone()));
694        assert_eq!(scii.routing_groups().count(), 1);
695
696        assert_eq!(routing_group.control_type(), None);
697        routing_group
698            .set_control_type(EventGroupControlType::TriggerUnicast)
699            .unwrap();
700        assert_eq!(
701            routing_group.control_type(),
702            Some(EventGroupControlType::TriggerUnicast)
703        );
704    }
705}