autosar_data_abstraction/communication/physical_channel/ethernet/
mod.rs

1use crate::communication::{
2    AbstractPdu, AbstractPhysicalChannel, CommunicationDirection, EthernetCluster, EthernetCommunicationConnector,
3    GeneralPurposePdu, Pdu, PduCollectionTrigger, PduTriggering,
4};
5use crate::{
6    AbstractionElement, ArPackage, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement,
7    abstraction_element,
8};
9use autosar_data::{AutosarVersion, Element, ElementName, EnumItem};
10
11mod networkendpoint;
12mod soad_old;
13mod socketaddress;
14mod someip;
15mod someip_old;
16
17pub use networkendpoint::*;
18pub use soad_old::*;
19pub use socketaddress::*;
20pub use someip::*;
21pub use someip_old::*;
22
23use super::PhysicalChannel;
24
25//##################################################################
26
27/// Provides information about the VLAN of an [`EthernetPhysicalChannel`]
28#[derive(Debug, Clone, PartialEq)]
29pub struct EthernetVlanInfo {
30    /// The name of the VLAN
31    pub vlan_name: String,
32    /// The VLAN ID which is transmitted in the ethernet frame
33    pub vlan_id: u16,
34}
35
36//##################################################################
37
38/// The `EthernetPhysicalChannel` represents a VLAN or untagged traffic
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub struct EthernetPhysicalChannel(Element);
41abstraction_element!(EthernetPhysicalChannel, EthernetPhysicalChannel);
42impl IdentifiableAbstractionElement for EthernetPhysicalChannel {}
43
44impl EthernetPhysicalChannel {
45    /// create a new physical channel for the cluster
46    pub(crate) fn new(
47        name: &str,
48        parent: &Element,
49        vlan_info: Option<&EthernetVlanInfo>,
50    ) -> Result<EthernetPhysicalChannel, AutosarAbstractionError> {
51        let physical_channel_elem = parent.create_named_sub_element(ElementName::EthernetPhysicalChannel, name)?;
52        let physical_channel = Self(physical_channel_elem);
53
54        // set the VLAN info and remove the physical_channel if the VLAN info is invalid
55        let result = physical_channel.set_vlan_info(vlan_info);
56        if let Err(error) = result {
57            // remove the created element if the VLAN info is invalid
58            let _ = parent.remove_sub_element(physical_channel.element().clone());
59            return Err(error);
60        }
61
62        // always set CATEGORY = WIRED, since this is the common case
63        let _ = physical_channel
64            .0
65            .create_sub_element(ElementName::Category)
66            .and_then(|cat| cat.set_character_data("WIRED"));
67
68        Ok(physical_channel)
69    }
70
71    /// set the VLAN information for this channel
72    ///
73    /// The supplied VLAN info must be unique - there cannot be two VLANs with the same vlan identifier.
74    /// One channel may be created without VLAN information; it carries untagged traffic.
75    ///
76    /// # Example
77    ///
78    /// ```
79    /// # use autosar_data::*;
80    /// # use autosar_data_abstraction::*;
81    /// # use autosar_data_abstraction::communication::*;
82    /// # fn main() -> Result<(), AutosarAbstractionError> {
83    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
84    /// # let package = model.get_or_create_package("/pkg1")?;
85    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
86    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
87    /// # let vlan_info_orig = EthernetVlanInfo {
88    /// #     vlan_name: "VLAN_1".to_string(),
89    /// #     vlan_id: 1,
90    /// # };
91    /// # let channel = cluster.create_physical_channel("Channel", Some(&vlan_info_orig))?;
92    /// let vlan_info = EthernetVlanInfo {
93    ///     vlan_name: "VLAN_2".to_string(),
94    ///     vlan_id: 2,
95    /// };
96    /// channel.set_vlan_info(Some(&vlan_info))?;
97    /// let info = channel.vlan_info().unwrap();
98    /// assert_eq!(info.vlan_id, 2);
99    /// # Ok(())}
100    /// ```
101    pub fn set_vlan_info(&self, vlan_info: Option<&EthernetVlanInfo>) -> Result<(), AutosarAbstractionError> {
102        let cluster = self.cluster()?;
103        // make sure there is no other channel with the same VLAN info
104        // If vlan_info is None, then there must be no existing channel without VLAN info
105        for channel in cluster.physical_channels() {
106            if &channel != self {
107                let other_vlan_info = channel.vlan_info();
108                if let (Some(v1), Some(v2)) = (&vlan_info, &other_vlan_info) {
109                    if v1.vlan_id == v2.vlan_id {
110                        // the vlan identifier of another channel matches the new vlan identifier
111                        return Err(AutosarAbstractionError::ItemAlreadyExists);
112                    }
113                } else if other_vlan_info.is_none() && vlan_info.is_none() {
114                    // there is already a channel for untagged traffic
115                    return Err(AutosarAbstractionError::ItemAlreadyExists);
116                }
117            }
118        }
119
120        // remove existing vlan info
121        let _ = self.element().remove_sub_element_kind(ElementName::Vlan);
122        // set the new vlan info
123        if let Some(vlan_info) = vlan_info {
124            let _ = self
125                .element()
126                .create_named_sub_element(ElementName::Vlan, &vlan_info.vlan_name)
127                .and_then(|vlan| vlan.create_sub_element(ElementName::VlanIdentifier))
128                .and_then(|vlan_id| vlan_id.set_character_data(vlan_info.vlan_id.to_string()));
129        }
130
131        Ok(())
132    }
133
134    /// Retrieves the VLAN information from a channel
135    ///
136    /// An ethernet physical channel that represents untagged traffic has no VLAN information and returns None.
137    ///
138    /// # Example
139    ///
140    /// ```
141    /// # use autosar_data::*;
142    /// # use autosar_data_abstraction::*;
143    /// # use autosar_data_abstraction::communication::*;
144    /// # fn main() -> Result<(), AutosarAbstractionError> {
145    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
146    /// # let package = model.get_or_create_package("/pkg1")?;
147    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
148    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
149    /// let vlan_info = EthernetVlanInfo {
150    ///     vlan_name: "VLAN_1".to_string(),
151    ///     vlan_id: 1,
152    /// };
153    /// let channel = cluster.create_physical_channel("Channel", Some(&vlan_info))?;
154    /// let info = channel.vlan_info().unwrap();
155    /// assert_eq!(info.vlan_id, 1);
156    /// # Ok(())}
157    /// ```
158    #[must_use]
159    pub fn vlan_info(&self) -> Option<EthernetVlanInfo> {
160        let vlan = self.0.get_sub_element(ElementName::Vlan)?;
161        let vlan_id = vlan
162            .get_sub_element(ElementName::VlanIdentifier)?
163            .character_data()?
164            .parse_integer::<u16>()?;
165        Some(EthernetVlanInfo {
166            vlan_name: vlan.item_name()?,
167            vlan_id,
168        })
169    }
170
171    /// get the cluster containing this physical channel
172    ///
173    /// # Example
174    ///
175    /// ```
176    /// # use autosar_data::*;
177    /// # use autosar_data_abstraction::*;
178    /// # fn main() -> Result<(), AutosarAbstractionError> {
179    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
180    /// # let package = model.get_or_create_package("/pkg1")?;
181    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
182    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
183    /// let channel = cluster.create_physical_channel("Channel", None)?;
184    /// let cluster_2 = channel.cluster()?;
185    /// assert_eq!(cluster, cluster_2);
186    /// # Ok(())}
187    /// ```
188    ///
189    /// # Errors
190    ///
191    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model
192    pub fn cluster(&self) -> Result<EthernetCluster, AutosarAbstractionError> {
193        let cluster_elem = self.0.named_parent()?.unwrap();
194        EthernetCluster::try_from(cluster_elem)
195    }
196
197    /// create a network endpoint - IPv4 or IPv6 address - for this channel
198    ///
199    /// In older versions of the Autosar standard, up to version 4.4.0, the `NetworkEndpoint` could be linked to an Ecu.
200    /// The parameter `ecu` specifies the target.
201    /// The link is obsoleted in newer versions, and will only be created if the file version allows it.
202    ///
203    /// # Example
204    ///
205    /// ```
206    /// # use autosar_data::*;
207    /// # use autosar_data_abstraction::*;
208    /// # use autosar_data_abstraction::communication::*;
209    /// # fn main() -> Result<(), AutosarAbstractionError> {
210    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
211    /// # let package = model.get_or_create_package("/pkg1")?;
212    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
213    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
214    /// # let channel = cluster.create_physical_channel("Channel", None)?;
215    /// let endpoint_address = NetworkEndpointAddress::IPv4 {
216    ///     address: Some("192.168.0.1".to_string()),
217    ///     address_source: Some(IPv4AddressSource::Fixed),
218    ///     default_gateway: Some("192.168.0.2".to_string()),
219    ///     network_mask: Some("255.255.255.0".to_string()),
220    /// };
221    /// let network_endpoint = channel.create_network_endpoint("Address1", endpoint_address, None)?;
222    /// # Ok(())}
223    /// ```
224    ///
225    /// # Errors
226    ///
227    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model
228    pub fn create_network_endpoint(
229        &self,
230        name: &str,
231        address: NetworkEndpointAddress,
232        ecu: Option<&EcuInstance>,
233    ) -> Result<NetworkEndpoint, AutosarAbstractionError> {
234        let network_endpoint = NetworkEndpoint::new(name, self, address)?;
235
236        if let Some(ecu_instance) = ecu {
237            let version = self.0.min_version()?;
238            if version <= AutosarVersion::Autosar_00046 {
239                let ne_element = network_endpoint.element();
240
241                // get a connector referenced by this physical channel which is contained in the ecu_instance
242                if let Some(connector) = self.ecu_connector(ecu_instance).map(|conn| conn.element().clone()) {
243                    let _ = connector
244                        .get_or_create_sub_element(ElementName::NetworkEndpointRefs)
245                        .and_then(|ner| ner.create_sub_element(ElementName::NetworkEndpointRef))
246                        .and_then(|ner| ner.set_reference_target(ne_element));
247                } else {
248                    // no connector between the ECU and this channel
249                    // -> abort after removing the network endpoint which was already created
250                    let _ = self
251                        .element()
252                        .get_sub_element(ElementName::NetworkEndpoints)
253                        .and_then(|nes| nes.remove_sub_element(ne_element.clone()).ok());
254                    return Err(AutosarAbstractionError::InvalidParameter(
255                        "The ECU must be connected to the channel".to_string(),
256                    ));
257                }
258            }
259        }
260
261        Ok(network_endpoint)
262    }
263
264    /// create an iterator over all [`NetworkEndpoint`]s in this channel
265    ///
266    /// # Example
267    ///
268    /// ```
269    /// # use autosar_data::*;
270    /// # use autosar_data_abstraction::*;
271    /// # use autosar_data_abstraction::communication::*;
272    /// # fn main() -> Result<(), AutosarAbstractionError> {
273    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
274    /// # let package = model.get_or_create_package("/pkg1")?;
275    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
276    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
277    /// # let channel = cluster.create_physical_channel("Channel", None)?;
278    /// # let endpoint_address = NetworkEndpointAddress::IPv4 {
279    /// #     address: Some("192.168.0.1".to_string()),
280    /// #     address_source: Some(IPv4AddressSource::Fixed),
281    /// #     default_gateway: Some("192.168.0.2".to_string()),
282    /// #     network_mask: Some("255.255.255.0".to_string()),
283    /// # };
284    /// # channel.create_network_endpoint("Address1", endpoint_address, None)?;
285    /// for network_endpoint in channel.network_endpoints() {
286    ///     // ...
287    /// }
288    /// # assert_eq!(channel.network_endpoints().count(), 1);
289    /// # Ok(())}
290    /// ```
291    pub fn network_endpoints(&self) -> impl Iterator<Item = NetworkEndpoint> + Send + use<> {
292        self.element()
293            .get_sub_element(ElementName::NetworkEndpoints)
294            .into_iter()
295            .flat_map(|ne| ne.sub_elements())
296            .filter_map(|ne_elem| NetworkEndpoint::try_from(ne_elem).ok())
297    }
298
299    /// create a socket address in the ethernet channel
300    ///
301    /// It contains the settings of the TCP/UDP port and links to a [`NetworkEndpoint`] which contains the IP address.
302    /// The socket address can either be a unicast adress which is associated with a single ECU, or a multicast address
303    ///
304    /// # Example
305    ///
306    /// ```
307    /// # use autosar_data::*;
308    /// # use autosar_data_abstraction::*;
309    /// # use autosar_data_abstraction::communication::*;
310    /// # fn main() -> Result<(), AutosarAbstractionError> {
311    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
312    /// # let package = model.get_or_create_package("/pkg1")?;
313    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
314    /// # let ecu_instance = system.create_ecu_instance("Ecu", &package)?;
315    /// # let controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
316    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
317    /// # let channel = cluster.create_physical_channel("Channel", None)?;
318    /// # controller.connect_physical_channel("connection", &channel)?;
319    /// # let endpoint_address = NetworkEndpointAddress::IPv4 {
320    /// #     address: Some("192.168.0.1".to_string()),
321    /// #     address_source: Some(IPv4AddressSource::Fixed),
322    /// #     default_gateway: Some("192.168.0.2".to_string()),
323    /// #     network_mask: Some("255.255.255.0".to_string()),
324    /// # };
325    /// # let network_endpoint = channel.create_network_endpoint("Address", endpoint_address, None)?;
326    /// let tcp_port = TpConfig::TcpTp {
327    ///     port_number: Some(1234),
328    ///     port_dynamically_assigned: None,
329    /// };
330    /// let socket_type = SocketAddressType::Unicast(Some(ecu_instance));
331    /// channel.create_socket_address("SocketName", &network_endpoint, &tcp_port, socket_type)?;
332    /// # Ok(())}
333    /// ```
334    ///
335    /// # Errors
336    ///
337    /// - [`AutosarAbstractionError::InvalidParameter`] `sa_type` contains a reference to an `EcuInstance` which is not connected to this channel
338    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model
339    pub fn create_socket_address(
340        &self,
341        name: &str,
342        network_endpoint: &NetworkEndpoint,
343        tp_config: &TpConfig,
344        sa_type: SocketAddressType,
345    ) -> Result<SocketAddress, AutosarAbstractionError> {
346        SocketAddress::new(name, self, network_endpoint, tp_config, sa_type)
347    }
348
349    /// create an iterator over all [`SocketAddress`]es in this channel
350    ///
351    /// # Example
352    ///
353    /// ```
354    /// # use autosar_data::*;
355    /// # use autosar_data_abstraction::*;
356    /// # use autosar_data_abstraction::communication::*;
357    /// # fn main() -> Result<(), AutosarAbstractionError> {
358    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
359    /// # let package = model.get_or_create_package("/pkg1")?;
360    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
361    /// # let ecu_instance = system.create_ecu_instance("Ecu", &package)?;
362    /// # let controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
363    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
364    /// # let channel = cluster.create_physical_channel("Channel", None)?;
365    /// # controller.connect_physical_channel("connection", &channel)?;
366    /// # let endpoint_address = NetworkEndpointAddress::IPv4 {
367    /// #     address: Some("192.168.0.1".to_string()),
368    /// #     address_source: Some(IPv4AddressSource::Fixed),
369    /// #     default_gateway: Some("192.168.0.2".to_string()),
370    /// #     network_mask: Some("255.255.255.0".to_string()),
371    /// # };
372    /// # let network_endpoint = channel.create_network_endpoint("Address", endpoint_address, None)?;
373    /// let tcp_port = TpConfig::TcpTp {
374    ///     port_number: Some(1234),
375    ///     port_dynamically_assigned: None,
376    /// };
377    /// let socket_type = SocketAddressType::Unicast(Some(ecu_instance));
378    /// channel.create_socket_address("SocketName", &network_endpoint, &tcp_port, socket_type)?;
379    /// assert_eq!(channel.socket_addresses().count(), 1);
380    /// # Ok(())}
381    /// ```
382    pub fn socket_addresses(&self) -> impl Iterator<Item = SocketAddress> + Send + use<> {
383        self.element()
384            .get_sub_element(ElementName::SoAdConfig)
385            .and_then(|sc| sc.get_sub_element(ElementName::SocketAddresss))
386            .into_iter()
387            .flat_map(|sa| sa.sub_elements())
388            .filter_map(|sa_elem| SocketAddress::try_from(sa_elem).ok())
389    }
390
391    /// create a socket connection bundle
392    ///
393    /// The `SocketConnectionBundle` is the "old" way to establish a connection between two sockets.
394    /// It is deprecated in newer versions of the Autosar standard, but remains available for compatibility.
395    ///
396    /// # Example
397    ///
398    /// ```
399    /// # use autosar_data::*;
400    /// # use autosar_data_abstraction::*;
401    /// # use autosar_data_abstraction::communication::*;
402    /// # fn main() -> Result<(), AutosarAbstractionError> {
403    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00046);
404    /// # let package = model.get_or_create_package("/pkg1")?;
405    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
406    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
407    /// # let channel = cluster.create_physical_channel("Channel", None)?;
408    /// # let server_endpoint = channel.create_network_endpoint("ServerAddress", NetworkEndpointAddress::IPv4 {
409    /// #    address: Some("192.16.0.1".to_string()),
410    /// #    address_source: Some(IPv4AddressSource::Fixed),
411    /// #    default_gateway: None,
412    /// #    network_mask: None
413    /// # }, None)?;
414    /// # let server_socket = channel.create_socket_address("ServerSocket", &server_endpoint, &TpConfig::TcpTp {
415    /// #    port_number: Some(1234),
416    /// #    port_dynamically_assigned: None
417    /// # }, SocketAddressType::Unicast(None))?;
418    /// let bundle = channel.create_socket_connection_bundle("Bundle", &server_socket)?;
419    /// # Ok(())}
420    /// ```
421    pub fn create_socket_connection_bundle(
422        &self,
423        name: &str,
424        server_port: &SocketAddress,
425    ) -> Result<SocketConnectionBundle, AutosarAbstractionError> {
426        let soadcfg = self.0.get_or_create_sub_element(ElementName::SoAdConfig)?;
427        let connections = soadcfg.get_or_create_sub_element(ElementName::ConnectionBundles)?;
428
429        SocketConnectionBundle::new(name, server_port, &connections)
430    }
431
432    /// iterate over all socket connection bundles in this channel
433    ///
434    /// The `SocketConnectionBundle` is the "old" way to establish a connection between two sockets.
435    /// It is deprecated in newer versions of the Autosar standard, but remains available for compatibility.
436    pub fn socket_connection_bundles(&self) -> impl Iterator<Item = SocketConnectionBundle> + Send + use<> {
437        self.element()
438            .get_sub_element(ElementName::SoAdConfig)
439            .and_then(|sc| sc.get_sub_element(ElementName::ConnectionBundles))
440            .into_iter()
441            .flat_map(|cb| cb.sub_elements())
442            .filter_map(|cb_elem| SocketConnectionBundle::try_from(cb_elem).ok())
443    }
444
445    /// create a pair of static socket connections
446    ///
447    /// Static socket connections are usually created as a pair, one on each socket involved on the connection.
448    /// This helper function creates both at once. To create a single connection, use [`SocketAddress::create_static_socket_connection`].
449    ///
450    /// If the connection is a TCP connection, the first port connects to the second port, and the second port listens for incoming connection.
451    /// The ordering of `port_1` and `port_2` has no impact on the direction of the transported PDUs. This is defined in the `PduTriggering`.
452    ///
453    /// `StaticSocketConnection`s are the "new" way to establish a connection between two sockets.
454    /// It was introduced in Autosar 4.5.0 (`AUTOSAR_00048`) and is the recommended way to create connections.
455    ///
456    /// `SocketConnectionBundles` (old) and `StaticSocketConnections` (new) may never be used in the same file.
457    ///
458    /// # Example
459    ///
460    /// ```
461    /// # use autosar_data::*;
462    /// # use autosar_data_abstraction::*;
463    /// # use autosar_data_abstraction::communication::*;
464    /// # fn main() -> Result<(), AutosarAbstractionError> {
465    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
466    /// # let package = model.get_or_create_package("/pkg1")?;
467    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
468    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
469    /// # let channel = cluster.create_physical_channel("Channel", None)?;
470    /// # let endpoint = channel.create_network_endpoint("ServerAddress", NetworkEndpointAddress::IPv4 {
471    /// #    address: Some("192.168.0.1".to_string()),
472    /// #    address_source: Some(IPv4AddressSource::Fixed),
473    /// #    default_gateway: None,
474    /// #    network_mask: None
475    /// # }, None)?;
476    /// # let server_socket = channel.create_socket_address("ServerSocket", &endpoint, &TpConfig::TcpTp {
477    /// #    port_number: Some(1234),
478    /// #    port_dynamically_assigned: None
479    /// # }, SocketAddressType::Unicast(None))?;
480    /// # let client_socket = channel.create_socket_address("ClientSocket", &endpoint, &TpConfig::TcpTp {
481    /// #    port_number: Some(1235),
482    /// #    port_dynamically_assigned: None
483    /// # }, SocketAddressType::Unicast(None))?;
484    /// let (connection_a, connection_b) = channel.create_static_socket_connection_pair("Connection", &server_socket, &client_socket, None)?;
485    /// # Ok(())}
486    /// ```
487    pub fn create_static_socket_connection_pair(
488        &self,
489        name: &str,
490        port_1: &SocketAddress,
491        port_2: &SocketAddress,
492        tcp_connect_timeout: Option<f64>,
493    ) -> Result<(StaticSocketConnection, StaticSocketConnection), AutosarAbstractionError> {
494        let ssc1 = port_1.create_static_socket_connection(name, port_2, Some(TcpRole::Connect), tcp_connect_timeout)?;
495        let ssc2 = port_2.create_static_socket_connection(name, port_1, Some(TcpRole::Listen), tcp_connect_timeout)?;
496        Ok((ssc1, ssc2))
497    }
498
499    /// configure SOME/IP service discovery (SD) for an ECU connected to this channel
500    ///
501    /// SD is used to broadcast service offers on the network and subscribe to services offered by other ECUs.
502    /// This function configures the ECU to use the SOME/IP SD protocol.
503    ///
504    /// SD uses either socket connection bundles or static socket connections to communicate.
505    ///
506    /// `ecu` is the ECU that should be configured for SD.
507    /// `unicast_socket` is the socket address used for unicast rx/tx communication by the ECU.
508    /// `unicast_rx_pdu` and `unicast_tx_pdu` are the `GeneralPurposePdus` used for the unicast communication.
509    /// `common_config` contains common configuration settings that can be used for all SD ECUs.
510    ///  - `multicast_rx_socket` is the socket address used for multicast communication by all SD ECUs.
511    ///  - `remote_socket` is a socket whose IP is set to ANY with UDP port 0, acting as the remote address in the SD communication.
512    ///  - `name_prefix` is an optional prefix for the names of the created elements.
513    ///  - `prefer_static_socket_connections` is a flag that determines if `SocketConnectionBundles` should be used instead of `StaticSocketConnections`.
514    ///    This is only relevant if the type can't be detected automatically.
515    ///  - `ipdu_identifier_set` is contains the `IPduIdentifiers` that are used in `StaticSocketConnections`.
516    ///
517    /// Note:
518    /// Usually `SomeIP` SD is expected to use port 30490, but this is not mandatory.
519    /// The port number is set in the sockets, and must be the same for all SD sockets.
520    ///
521    /// # Example
522    /// ```
523    /// # use autosar_data::*;
524    /// # use autosar_data_abstraction::*;
525    /// # use autosar_data_abstraction::communication::*;
526    /// # fn main() -> Result<(), AutosarAbstractionError> {
527    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
528    /// # let package = model.get_or_create_package("/pkg1")?;
529    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
530    /// # let ecu_instance = system.create_ecu_instance("Ecu", &package)?;
531    /// # let controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
532    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
533    /// # let channel = cluster.create_physical_channel("Channel", None)?;
534    /// # controller.connect_physical_channel("connection", &channel)?;
535    ///
536    /// let unicast_endpoint = channel.create_network_endpoint("UnicastEndpoint", NetworkEndpointAddress::IPv4 {
537    ///    address: Some("192.168.0.1".to_string()),
538    ///    address_source: Some(IPv4AddressSource::Fixed),
539    ///    default_gateway: None,
540    ///    network_mask: None
541    /// }, None)?;
542    /// let unicast_socket = channel.create_socket_address("UnicastSocket", &unicast_endpoint, &TpConfig::UdpTp {
543    ///    port_number: Some(30490),
544    ///    port_dynamically_assigned: None
545    /// }, SocketAddressType::Unicast(Some(ecu_instance.clone())))?;
546    /// let multicast_rx_endpoint = channel.create_network_endpoint("MulticastEndpoint", NetworkEndpointAddress::IPv4 {
547    ///    address: Some("239.0.0.1".to_string()),
548    ///    address_source: Some(IPv4AddressSource::Fixed),
549    ///    default_gateway: None,
550    ///    network_mask: None
551    /// }, None)?;
552    /// let multicast_rx_socket = channel.create_socket_address("MulticastSocket", &multicast_rx_endpoint, &TpConfig::UdpTp {
553    ///    port_number: Some(30490),
554    ///    port_dynamically_assigned: None
555    /// }, SocketAddressType::Multicast(vec![ecu_instance.clone()]))?;
556    /// let remote_endpoint = channel.create_network_endpoint("RemoteEndpoint", NetworkEndpointAddress::IPv4 {
557    ///    address: Some("ANY".to_string()),
558    ///    address_source: None,
559    ///    default_gateway: None,
560    ///    network_mask: None
561    /// }, None)?;
562    /// let remote_socket = channel.create_socket_address("RemoteSocket", &remote_endpoint, &TpConfig::UdpTp {
563    ///   port_number: Some(0),
564    ///   port_dynamically_assigned: None
565    /// }, SocketAddressType::Unicast(None))?;
566    /// let unicast_rx_pdu = system.create_general_purpose_pdu("UnicastRxPdu", &package, 0, GeneralPurposePduCategory::Sd)?;
567    /// let unicast_tx_pdu = system.create_general_purpose_pdu("UnicastTxPdu", &package, 0, GeneralPurposePduCategory::Sd)?;
568    /// let multicast_rx_pdu = system.create_general_purpose_pdu("MulticastRxPdu", &package, 0, GeneralPurposePduCategory::Sd)?;
569    /// let common_config = CommonServiceDiscoveryConfig {
570    ///   multicast_rx_socket: &multicast_rx_socket,
571    ///   multicast_rx_pdu: &multicast_rx_pdu,
572    ///   remote_socket: &remote_socket,
573    ///   name_prefix: None,
574    ///   prefer_static_socket_connections: false,
575    ///   ipdu_identifier_set: None,
576    /// };
577    ///
578    /// channel.configure_service_discovery_for_ecu(&ecu_instance, &unicast_socket, &unicast_rx_pdu, &unicast_tx_pdu, &common_config)?;
579    /// # Ok(())}
580    /// ```
581    pub fn configure_service_discovery_for_ecu(
582        &self,
583        ecu: &EcuInstance,
584        unicast_socket: &SocketAddress,
585        unicast_rx_pdu: &GeneralPurposePdu,
586        unicast_tx_pdu: &GeneralPurposePdu,
587        common_config: &CommonServiceDiscoveryConfig,
588    ) -> Result<(), AutosarAbstractionError> {
589        let version = self.0.min_version()?;
590
591        // check: the ECU must be connected to this channel
592        if self.ecu_connector(ecu).is_none() {
593            return Err(AutosarAbstractionError::InvalidParameter(
594                "The ECU must be connected to the channel".to_string(),
595            ));
596        };
597
598        // build the SD configuration using SocketConnectionBundles if the file verion is old,
599        // or if SocketConnectionBundles already exist, or if the user prefers it
600        let use_scb = version < AutosarVersion::Autosar_00048
601            || self.has_socket_connections()
602            || !common_config.prefer_static_socket_connections;
603
604        // check: each socket must be part of the channel
605        if unicast_socket.physical_channel()? != *self
606            || common_config.multicast_rx_socket.physical_channel()? != *self
607            || common_config.remote_socket.physical_channel()? != *self
608        {
609            return Err(AutosarAbstractionError::InvalidParameter(
610                "All sockets must be part of the channel".to_string(),
611            ));
612        }
613
614        // the "unicast socket" must be configured as Unicast
615        match unicast_socket.socket_address_type() {
616            Some(SocketAddressType::Unicast(opt_socket_ecu)) => {
617                if let Some(socket_ecu) = opt_socket_ecu {
618                    if &socket_ecu != ecu {
619                        return Err(AutosarAbstractionError::InvalidParameter(
620                            "The unicast socket belongs to a different ECU".to_string(),
621                        ));
622                    }
623                }
624            }
625            None => {
626                // set the ECU for the unicast socket
627                unicast_socket.set_unicast_ecu(ecu)?;
628            }
629            _ => {
630                return Err(AutosarAbstractionError::InvalidParameter(
631                    "The unicast socket is not configured as Unicast".to_string(),
632                ));
633            }
634        }
635
636        // the "multicast rx socket" must be configured as Multicast
637        // the ecu will be added to the list of multicast ECUs in the socket
638        match common_config.multicast_rx_socket.socket_address_type() {
639            Some(SocketAddressType::Multicast(_)) | None => {
640                common_config.multicast_rx_socket.add_multicast_ecu(ecu)?;
641            }
642            _ => {
643                return Err(AutosarAbstractionError::InvalidParameter(
644                    "The multicast rx socket is not configured as Multicast".to_string(),
645                ));
646            }
647        }
648
649        // each socket must use UDP
650        let Some(TpConfig::UdpTp {
651            port_number: unicast_port,
652            ..
653        }) = unicast_socket.tp_config()
654        else {
655            return Err(AutosarAbstractionError::InvalidParameter(
656                "The unicast port must use UDP".to_string(),
657            ));
658        };
659        let Some(TpConfig::UdpTp {
660            port_number: multicast_rx_port,
661            ..
662        }) = common_config.multicast_rx_socket.tp_config()
663        else {
664            return Err(AutosarAbstractionError::InvalidParameter(
665                "The multicast rx port must use UDP".to_string(),
666            ));
667        };
668        let Some(TpConfig::UdpTp {
669            port_number: remote_port,
670            port_dynamically_assigned: remote_dynamically_assigned,
671        }) = common_config.remote_socket.tp_config()
672        else {
673            return Err(AutosarAbstractionError::InvalidParameter(
674                "The remote port must use UDP".to_string(),
675            ));
676        };
677        if unicast_port.is_none() || unicast_port != multicast_rx_port {
678            return Err(AutosarAbstractionError::InvalidParameter(
679                "All local UDP ports must use the same port number".to_string(),
680            ));
681        }
682        // required the remote port to either be 0 or dynamically_assigned = true
683        // the attribute dynamically_assigned is obsolete in AUTOSAR 4.5.0 and newer
684        if remote_port != Some(0) && remote_dynamically_assigned != Some(true) {
685            return Err(AutosarAbstractionError::InvalidParameter(
686                "The remote UDP port must be 0 / dynamically assigned".to_string(),
687            ));
688        }
689
690        // the IP address (ipv4 or ipv6) of the remote socket must be set to ANY
691        let Some(remote_network_endpoint) = common_config.remote_socket.network_endpoint() else {
692            return Err(AutosarAbstractionError::InvalidParameter(
693                "The remote socket must have a network endpoint".to_string(),
694            ));
695        };
696        if !remote_network_endpoint.addresses().all(|neaddr| match neaddr {
697            NetworkEndpointAddress::IPv4 { address, .. } => address == Some("ANY".to_string()),
698            NetworkEndpointAddress::IPv6 { address, .. } => address == Some("ANY".to_string()),
699        }) {
700            return Err(AutosarAbstractionError::InvalidParameter(
701                "The IP (v4/v6) address of the remote socket must be set to ANY".to_string(),
702            ));
703        }
704
705        // create the SD configuration
706        if use_scb {
707            self.configure_sd_socket_connection_bundle(
708                ecu,
709                unicast_socket,
710                unicast_tx_pdu,
711                unicast_rx_pdu,
712                common_config,
713            )?;
714        } else {
715            self.configure_sd_static_socket_connection(
716                common_config,
717                unicast_socket,
718                unicast_rx_pdu,
719                ecu,
720                unicast_tx_pdu,
721            )?;
722        }
723
724        Ok(())
725    }
726
727    /// configure SOME/IP service discovery (SD) using `SocketConnectionBundles`
728    fn configure_sd_socket_connection_bundle(
729        &self,
730        ecu: &EcuInstance,
731        unicast_socket: &SocketAddress,
732        unicast_tx_pdu: &GeneralPurposePdu,
733        unicast_rx_pdu: &GeneralPurposePdu,
734        common_config: &CommonServiceDiscoveryConfig<'_>,
735    ) -> Result<(), AutosarAbstractionError> {
736        let name_prefix = common_config.name_prefix.unwrap_or("");
737        let ecu_name = ecu.name().unwrap_or("unnamed".to_string());
738
739        let connection_bundles = self
740            .element()
741            .get_or_create_sub_element(ElementName::SoAdConfig)?
742            .get_or_create_sub_element(ElementName::ConnectionBundles)?;
743
744        // check if the unicast connection already exists
745        let scb_unicast = self.socket_connection_bundles().find(|scb| {
746            scb.server_port().is_some_and(|sp| &sp == unicast_socket)
747                && scb.bundled_connections().any(|sc| {
748                    sc.client_ip_addr_from_connection_request() == Some(true)
749                        && sc.client_port().is_some_and(|cp| &cp == common_config.remote_socket)
750                        && sc.pdu_triggerings().count() == 2
751                })
752        });
753
754        if scb_unicast.is_none() {
755            // create a new SocketConnectionBundle for the unicast connection
756            let scb_name = format!("{name_prefix}SD_Unicast_{ecu_name}");
757            let scb = SocketConnectionBundle::new(&scb_name, unicast_socket, &connection_bundles)?;
758            let conn = scb.create_bundled_connection(common_config.remote_socket)?;
759            conn.set_client_ip_addr_from_connection_request(Some(true))?;
760            conn.set_client_port_from_connection_request(Some(true))?;
761            let (_, pt_tx) = conn.create_socket_connection_ipdu_identifier(
762                unicast_tx_pdu,
763                SocketConnection::SD_HEADER_ID,
764                None,
765                Some(PduCollectionTrigger::Always),
766            )?;
767            let (_, pt_rx) = conn.create_socket_connection_ipdu_identifier(
768                unicast_rx_pdu,
769                SocketConnection::SD_HEADER_ID,
770                None,
771                Some(PduCollectionTrigger::Always),
772            )?;
773            pt_tx.create_pdu_port(ecu, CommunicationDirection::Out)?;
774            pt_rx.create_pdu_port(ecu, CommunicationDirection::In)?;
775        }
776
777        // check if the multicast connection already exists
778        let scb_multicast_opt = self.socket_connection_bundles().find(|scb| {
779            scb.server_port()
780                .is_some_and(|sp| &sp == common_config.multicast_rx_socket)
781                && scb.bundled_connections().any(|sc| {
782                    sc.client_ip_addr_from_connection_request() == Some(true)
783                        && sc.client_port().is_some_and(|cp| &cp == common_config.remote_socket)
784                        && sc.pdu_triggerings().count() == 1
785                })
786        });
787
788        let scb_multicast_pt = if let Some(pt) = scb_multicast_opt
789            .and_then(|scb| scb.bundled_connections().next())
790            .and_then(|sc| sc.pdu_triggerings().next())
791        {
792            // the PduTriggering in the multicast connection already exists, return it
793            pt
794        } else {
795            // create a new SocketConnectionBundle for the multicast connection
796            let scb_name = format!("{name_prefix}SD_Multicast_Rx");
797            let scb = SocketConnectionBundle::new(&scb_name, common_config.multicast_rx_socket, &connection_bundles)?;
798            let conn = scb.create_bundled_connection(common_config.remote_socket)?;
799            conn.set_client_ip_addr_from_connection_request(Some(true))?;
800            conn.set_client_port_from_connection_request(Some(true))?;
801            // trigger the multicast PDU in the connection, which creates a PduTriggering
802            let (_, pt) = conn.create_socket_connection_ipdu_identifier(
803                common_config.multicast_rx_pdu,
804                SocketConnection::SD_HEADER_ID,
805                None,
806                Some(PduCollectionTrigger::Always),
807            )?;
808            pt
809        };
810        // add a PduPort for the ecu to the PduTriggering
811        scb_multicast_pt.create_pdu_port(ecu, CommunicationDirection::In)?;
812
813        Ok(())
814    }
815
816    /// configure SOME/IP service discovery (SD) using `StaticSocketConnections`
817    fn configure_sd_static_socket_connection(
818        &self,
819        common_config: &CommonServiceDiscoveryConfig<'_>,
820        unicast_socket: &SocketAddress,
821        unicast_rx_pdu: &GeneralPurposePdu,
822        ecu: &EcuInstance,
823        unicast_tx_pdu: &GeneralPurposePdu,
824    ) -> Result<(), AutosarAbstractionError> {
825        let name_prefix = common_config.name_prefix.unwrap_or("");
826        let ecu_name = ecu.name().unwrap_or("unnamed".to_string());
827
828        let Some(ipdu_identifier_set) = common_config.ipdu_identifier_set else {
829            return Err(AutosarAbstractionError::InvalidParameter(
830                "An IPduIdentifierSet is required for StaticSocketConnections".to_string(),
831            ));
832        };
833        let ssc_unicast = unicast_socket.static_socket_connections().find(|ssc| {
834            ssc.remote_socket().is_some_and(|rs| &rs == common_config.remote_socket)
835                && ssc.ipdu_identifiers().count() == 2
836        });
837
838        if ssc_unicast.is_none() {
839            // create a new StaticSocketConnection for the unicast connection
840            let name = format!("{name_prefix}SD_Unicast_{ecu_name}");
841            let ssc = unicast_socket.create_static_socket_connection(&name, common_config.remote_socket, None, None)?;
842            // create the IPduIdentifier for the unicast rx PDU
843            let name = format!("{name_prefix}SD_Unicast_Rx_{ecu_name}");
844            let idpu_identifier_rx = ipdu_identifier_set.create_socon_ipdu_identifier(
845                &name,
846                unicast_rx_pdu,
847                self,
848                Some(SoConIPduIdentifier::SD_HEADER_ID),
849                None,
850                Some(PduCollectionTrigger::Always),
851            )?;
852            // create a PduPort for the ecu in the new PduTriggering
853            idpu_identifier_rx
854                .pdu_triggering()
855                .unwrap()
856                .create_pdu_port(ecu, CommunicationDirection::In)?;
857            // create the IPduIdentifier for the unicast tx PDU
858            let name = format!("{name_prefix}SD_Unicast_Tx_{ecu_name}");
859            let idpu_identifier_tx = ipdu_identifier_set.create_socon_ipdu_identifier(
860                &name,
861                unicast_tx_pdu,
862                self,
863                Some(SoConIPduIdentifier::SD_HEADER_ID),
864                None,
865                Some(PduCollectionTrigger::Always),
866            )?;
867            // create a PduPort for the ecu in the new PduTriggering
868            idpu_identifier_tx
869                .pdu_triggering()
870                .unwrap()
871                .create_pdu_port(ecu, CommunicationDirection::Out)?;
872            ssc.add_ipdu_identifier(&idpu_identifier_rx)?;
873            ssc.add_ipdu_identifier(&idpu_identifier_tx)?;
874        }
875
876        // create or extend the shared multicast connection
877        let ssc_multicast = common_config
878            .multicast_rx_socket
879            .static_socket_connections()
880            .find(|ssc| {
881                ssc.remote_socket().is_some_and(|rs| &rs == common_config.remote_socket)
882                    && ssc.ipdu_identifiers().count() == 1
883            });
884
885        let pt_multicast_rx = if let Some(pt) = ssc_multicast
886            .and_then(|ssc| ssc.ipdu_identifiers().next())
887            .and_then(|ipi| ipi.pdu_triggering())
888        {
889            // the PduTriggering already exists, return it
890            pt
891        } else {
892            // create a new StaticSocketConnection for the multicast connection
893            let name = format!("{name_prefix}SD_Multicast_Rx");
894            let ssc = common_config.multicast_rx_socket.create_static_socket_connection(
895                &name,
896                common_config.remote_socket,
897                None,
898                None,
899            )?;
900            let idpu_identifier_mc_rx = ipdu_identifier_set.create_socon_ipdu_identifier(
901                &name,
902                common_config.multicast_rx_pdu,
903                self,
904                Some(SoConIPduIdentifier::SD_HEADER_ID),
905                None,
906                Some(PduCollectionTrigger::Always),
907            )?;
908            let pt = idpu_identifier_mc_rx.pdu_triggering().unwrap();
909            ssc.add_ipdu_identifier(&idpu_identifier_mc_rx)?;
910            pt
911        };
912        // add a PduPort for the ecu to the PduTriggering
913        pt_multicast_rx.create_pdu_port(ecu, CommunicationDirection::In)?;
914        Ok(())
915    }
916
917    /// check if the channel contains any `SocketConnectionBundles` (old) or `SocketConnections` (very old)
918    #[must_use]
919    pub fn has_socket_connections(&self) -> bool {
920        if let Some(soad_config) = self.element().get_sub_element(ElementName::SoAdConfig) {
921            if let Some(connection_bundles) = soad_config.get_sub_element(ElementName::ConnectionBundles) {
922                // does at least one SocketConnectionBundle exist?
923                if connection_bundles.sub_elements().count() > 0 {
924                    return true;
925                }
926            }
927            if let Some(connections) = soad_config.get_sub_element(ElementName::Connections) {
928                // does at least one SocketConnection exist?
929                return connections.sub_elements().count() > 0;
930            }
931        }
932        false
933    }
934}
935
936impl From<EthernetPhysicalChannel> for PhysicalChannel {
937    fn from(channel: EthernetPhysicalChannel) -> Self {
938        PhysicalChannel::Ethernet(channel)
939    }
940}
941
942impl AbstractPhysicalChannel for EthernetPhysicalChannel {
943    type CommunicationConnectorType = EthernetCommunicationConnector;
944}
945
946//##################################################################
947
948/// A `CommonServiceDiscoveryConfig` contains common configuration settings for `System::configure_service_discovery_for_ecu`.
949///
950/// This struct contains ECU-independent settings that should be re-used for all ECUs that are configured for SD.
951pub struct CommonServiceDiscoveryConfig<'a> {
952    /// the socket address used for multicast rx by all SD ECUs
953    pub multicast_rx_socket: &'a SocketAddress,
954    /// the multicast rx PDU used by all SD ECUs
955    pub multicast_rx_pdu: &'a GeneralPurposePdu,
956    /// the remote socket used for SD communication. This socket must have an IP address (v4 or v6) set to ANY.
957    pub remote_socket: &'a SocketAddress,
958    /// `configure_service_discovery_for_ecu` checks if any `SocketConnectionBundles` exist. If so, the old configuration method must be used.
959    /// If none are found and the version is new enough, both methods are possible, and this flag determines which one to use.
960    pub prefer_static_socket_connections: bool,
961    /// an ipdu identifier set in which `PduTriggerings` are created. Only needed for `StaticSocketConnections`.
962    pub ipdu_identifier_set: Option<&'a SocketConnectionIpduIdentifierSet>,
963    /// an optional prefix for the names of the created elements
964    pub name_prefix: Option<&'a str>,
965}
966
967//##################################################################
968
969/// A static socket connection is a connection between two sockets.
970///
971/// This is the new way to establish a connection. It was introduced in Autosar 4.5.0 (`AUTOSAR_00048`).
972#[derive(Debug, Clone, PartialEq, Eq, Hash)]
973pub struct StaticSocketConnection(Element);
974abstraction_element!(StaticSocketConnection, StaticSocketConnection);
975impl IdentifiableAbstractionElement for StaticSocketConnection {}
976
977impl StaticSocketConnection {
978    pub(crate) fn new(
979        name: &str,
980        parent: &Element,
981        remote_address: &SocketAddress,
982        tcp_role: Option<TcpRole>,
983        tcp_connect_timeout: Option<f64>,
984    ) -> Result<Self, AutosarAbstractionError> {
985        let connections = parent.get_or_create_sub_element(ElementName::StaticSocketConnections)?;
986        let ssc_elem = connections.create_named_sub_element(ElementName::StaticSocketConnection, name)?;
987
988        let ssc = Self(ssc_elem);
989
990        ssc.set_remote_socket(remote_address)?;
991        ssc.set_tcp_role(tcp_role)?;
992        ssc.set_tcp_connect_timeout(tcp_connect_timeout)?;
993
994        Ok(ssc)
995    }
996
997    /// get the socket address containing this static socket connection
998    pub fn socket_address(&self) -> Result<SocketAddress, AutosarAbstractionError> {
999        let sa = self.element().named_parent()?.unwrap();
1000        SocketAddress::try_from(sa)
1001    }
1002
1003    /// set the remote socket of this connection
1004    pub fn set_remote_socket(&self, remote_socket: &SocketAddress) -> Result<(), AutosarAbstractionError> {
1005        self.element()
1006            .get_or_create_sub_element(ElementName::RemoteAddresss)?
1007            .get_or_create_sub_element(ElementName::SocketAddressRefConditional)?
1008            .get_or_create_sub_element(ElementName::SocketAddressRef)?
1009            .set_reference_target(remote_socket.element())?;
1010        Ok(())
1011    }
1012
1013    /// get the remote socket of this connection
1014    #[must_use]
1015    pub fn remote_socket(&self) -> Option<SocketAddress> {
1016        let remote_socket = self
1017            .element()
1018            .get_sub_element(ElementName::RemoteAddresss)?
1019            .get_sub_element(ElementName::SocketAddressRefConditional)?
1020            .get_sub_element(ElementName::SocketAddressRef)?
1021            .get_reference_target()
1022            .ok()?;
1023        SocketAddress::try_from(remote_socket).ok()
1024    }
1025
1026    /// add a `SoConIPduIdentifier` to this static socket connection
1027    pub fn add_ipdu_identifier(&self, identifier: &SoConIPduIdentifier) -> Result<(), AutosarAbstractionError> {
1028        let ipdu_identifiers = self.element().get_or_create_sub_element(ElementName::IPduIdentifiers)?;
1029        let scii = ipdu_identifiers
1030            .create_sub_element(ElementName::SoConIPduIdentifierRefConditional)?
1031            .create_sub_element(ElementName::SoConIPduIdentifierRef)?;
1032        scii.set_reference_target(identifier.element())?;
1033        Ok(())
1034    }
1035
1036    /// create an iterator over all `SoConIPduIdentifiers` in this static socket connection
1037    pub fn ipdu_identifiers(&self) -> impl Iterator<Item = SoConIPduIdentifier> + Send + use<> {
1038        self.element()
1039            .get_sub_element(ElementName::IPduIdentifiers)
1040            .into_iter()
1041            .flat_map(|elem| elem.sub_elements())
1042            .filter_map(|scirc: Element| {
1043                scirc
1044                    .get_sub_element(ElementName::SoConIPduIdentifierRef)
1045                    .and_then(|sciir| sciir.get_reference_target().ok())
1046                    .and_then(|scii| SoConIPduIdentifier::try_from(scii).ok())
1047            })
1048    }
1049
1050    /// set the TCP role of this static socket connection
1051    pub fn set_tcp_role(&self, role: Option<TcpRole>) -> Result<(), AutosarAbstractionError> {
1052        if let Some(role) = role {
1053            self.element()
1054                .get_or_create_sub_element(ElementName::TcpRole)?
1055                .set_character_data::<EnumItem>(role.into())?;
1056        } else {
1057            let _ = self.element().remove_sub_element_kind(ElementName::TcpRole);
1058        }
1059        Ok(())
1060    }
1061
1062    /// get the TCP role of this static socket connection
1063    #[must_use]
1064    pub fn tcp_role(&self) -> Option<TcpRole> {
1065        self.element()
1066            .get_sub_element(ElementName::TcpRole)
1067            .and_then(|elem| elem.character_data())
1068            .and_then(|cdata| cdata.enum_value())
1069            .and_then(|enumitem| enumitem.try_into().ok())
1070    }
1071
1072    /// set the TCP connect timeout of this static socket connection
1073    pub fn set_tcp_connect_timeout(&self, timeout: Option<f64>) -> Result<(), AutosarAbstractionError> {
1074        if let Some(timeout) = timeout {
1075            self.element()
1076                .get_or_create_sub_element(ElementName::TcpConnectTimeout)?
1077                .set_character_data(timeout)?;
1078        } else {
1079            let _ = self.element().remove_sub_element_kind(ElementName::TcpConnectTimeout);
1080        }
1081        Ok(())
1082    }
1083
1084    /// get the TCP connect timeout of this static socket connection
1085    #[must_use]
1086    pub fn tcp_connect_timeout(&self) -> Option<f64> {
1087        self.element()
1088            .get_sub_element(ElementName::TcpConnectTimeout)
1089            .and_then(|elem| elem.character_data())
1090            .and_then(|cdata| cdata.parse_float())
1091    }
1092}
1093
1094//##################################################################
1095
1096/// A `SocketConnectionIpduIdentifierSet` contains a set of `SoConIPduIdentifiers`, which are used in static socket connections and in `SomeIp` events.
1097#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1098pub struct SocketConnectionIpduIdentifierSet(Element);
1099abstraction_element!(SocketConnectionIpduIdentifierSet, SocketConnectionIpduIdentifierSet);
1100impl IdentifiableAbstractionElement for SocketConnectionIpduIdentifierSet {}
1101
1102impl SocketConnectionIpduIdentifierSet {
1103    /// create a new `SocketConnectionIpduIdentifierSet`
1104    ///
1105    /// The `SocketConnectionIpduIdentifierSet` is a Fibex element, so this function is not exported in the API.
1106    /// Users should call `System::create_socket_connection_ipdu_identifier_set` instead, which also creates the required `FibexElementRef`.
1107    pub(crate) fn new(name: &str, package: &ArPackage) -> Result<Self, AutosarAbstractionError> {
1108        let sci = package
1109            .element()
1110            .get_or_create_sub_element(ElementName::Elements)?
1111            .create_named_sub_element(ElementName::SocketConnectionIpduIdentifierSet, name)?;
1112        Ok(Self(sci))
1113    }
1114
1115    /// create a new `SoConIPduIdentifier` in this set
1116    pub fn create_socon_ipdu_identifier<T: AbstractPdu>(
1117        &self,
1118        name: &str,
1119        pdu: &T,
1120        channel: &EthernetPhysicalChannel,
1121        header_id: Option<u64>,
1122        timeout: Option<f64>,
1123        collection_trigger: Option<PduCollectionTrigger>,
1124    ) -> Result<SoConIPduIdentifier, AutosarAbstractionError> {
1125        let ipdu_identifiers = self.element().get_or_create_sub_element(ElementName::IPduIdentifiers)?;
1126        SoConIPduIdentifier::new(
1127            name,
1128            &ipdu_identifiers,
1129            &pdu.clone().into(),
1130            channel,
1131            header_id,
1132            timeout,
1133            collection_trigger,
1134        )
1135    }
1136
1137    /// create an iterator over all `SoConIPduIdentifiers` in this set
1138    pub fn socon_ipdu_identifiers(&self) -> impl Iterator<Item = SoConIPduIdentifier> + Send + use<> {
1139        self.element()
1140            .get_sub_element(ElementName::IPduIdentifiers)
1141            .into_iter()
1142            .flat_map(|elem| elem.sub_elements())
1143            .filter_map(|elem| SoConIPduIdentifier::try_from(elem).ok())
1144    }
1145}
1146
1147//##################################################################
1148
1149/// A `SoConIPduIdentifier` describes a PDU that is transported over a static socket connection.
1150#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1151pub struct SoConIPduIdentifier(Element);
1152abstraction_element!(SoConIPduIdentifier, SoConIPduIdentifier);
1153impl IdentifiableAbstractionElement for SoConIPduIdentifier {}
1154
1155impl SoConIPduIdentifier {
1156    /// The PDU header id for SD messages must always be set to `0xFFFF_8100`
1157    pub const SD_HEADER_ID: u64 = 0xFFFF_8100;
1158
1159    // create a new SoConIPduIdentifier (internal)
1160    pub(crate) fn new(
1161        name: &str,
1162        parent: &Element,
1163        pdu: &Pdu,
1164        channel: &EthernetPhysicalChannel,
1165        header_id: Option<u64>,
1166        timeout: Option<f64>,
1167        collection_trigger: Option<PduCollectionTrigger>,
1168    ) -> Result<Self, AutosarAbstractionError> {
1169        let scii = Self(parent.create_named_sub_element(ElementName::SoConIPduIdentifier, name)?);
1170        scii.set_pdu_internal(pdu, channel)?;
1171
1172        if let Some(header_id) = header_id {
1173            scii.set_header_id(header_id)?;
1174        }
1175        if let Some(timeout) = timeout {
1176            scii.set_timeout(timeout)?;
1177        }
1178        if let Some(collection_trigger) = collection_trigger {
1179            scii.set_collection_trigger(collection_trigger)?;
1180        }
1181        Ok(scii)
1182    }
1183
1184    /// create a new `PduTriggering` for the pdu and reference it in this `SoConIPduIdentifier`
1185    pub fn set_pdu<T: AbstractPdu>(
1186        &self,
1187        pdu: &T,
1188        channel: &EthernetPhysicalChannel,
1189    ) -> Result<(), AutosarAbstractionError> {
1190        let pdu: Pdu = pdu.clone().into();
1191        self.set_pdu_internal(&pdu, channel)
1192    }
1193
1194    fn set_pdu_internal(&self, pdu: &Pdu, channel: &EthernetPhysicalChannel) -> Result<(), AutosarAbstractionError> {
1195        if let Some(pt_old) = self
1196            .element()
1197            .get_sub_element(ElementName::PduTriggeringRef)
1198            .and_then(|pt| pt.get_reference_target().ok())
1199        {
1200            let pt_old = PduTriggering::try_from(pt_old)?;
1201            if let Some(old_pdu) = pt_old.pdu() {
1202                if old_pdu == *pdu {
1203                    return Ok(());
1204                }
1205                // remove old pdu_triggering - ideally this should use a remove() method on the
1206                // PduTriggering, but for now there is a "manual" implementation
1207                channel
1208                    .element()
1209                    .get_sub_element(ElementName::PduTriggerings)
1210                    .and_then(|pts| pts.remove_sub_element(pt_old.element().clone()).ok());
1211            }
1212        }
1213        let pt_new = PduTriggering::new(pdu, &PhysicalChannel::Ethernet(channel.clone()))?;
1214        self.element()
1215            .get_or_create_sub_element(ElementName::PduTriggeringRef)?
1216            .set_reference_target(pt_new.element())?;
1217        Ok(())
1218    }
1219
1220    /// set the header id for this `SoConIPduIdentifier`
1221    pub fn set_header_id(&self, header_id: u64) -> Result<(), AutosarAbstractionError> {
1222        self.element()
1223            .get_or_create_sub_element(ElementName::HeaderId)?
1224            .set_character_data(header_id)?;
1225        Ok(())
1226    }
1227
1228    /// set the timeout for this `SoConIPduIdentifier`
1229    pub fn set_timeout(&self, timeout: f64) -> Result<(), AutosarAbstractionError> {
1230        self.element()
1231            .get_or_create_sub_element(ElementName::PduCollectionPduTimeout)?
1232            .set_character_data(timeout)?;
1233        Ok(())
1234    }
1235
1236    /// set the collection trigger for this `SoConIPduIdentifier`
1237    pub fn set_collection_trigger(&self, trigger: PduCollectionTrigger) -> Result<(), AutosarAbstractionError> {
1238        self.element()
1239            .get_or_create_sub_element(ElementName::PduCollectionTrigger)?
1240            .set_character_data::<EnumItem>(trigger.into())?;
1241        Ok(())
1242    }
1243
1244    /// get the `PduTriggering` referenced by this `SoConIPduIdentifier`
1245    #[must_use]
1246    pub fn pdu_triggering(&self) -> Option<PduTriggering> {
1247        let pt = self
1248            .element()
1249            .get_sub_element(ElementName::PduTriggeringRef)?
1250            .get_reference_target()
1251            .ok()?;
1252        PduTriggering::try_from(pt).ok()
1253    }
1254
1255    /// get the header id for this `SoConIPduIdentifier`
1256    #[must_use]
1257    pub fn header_id(&self) -> Option<u64> {
1258        self.element()
1259            .get_sub_element(ElementName::HeaderId)?
1260            .character_data()?
1261            .parse_integer()
1262    }
1263
1264    /// get the timeout for this `SoConIPduIdentifier`
1265    #[must_use]
1266    pub fn timeout(&self) -> Option<f64> {
1267        self.element()
1268            .get_sub_element(ElementName::PduCollectionPduTimeout)?
1269            .character_data()?
1270            .float_value()
1271    }
1272
1273    /// get the collection trigger for this `SoConIPduIdentifier`
1274    #[must_use]
1275    pub fn collection_trigger(&self) -> Option<PduCollectionTrigger> {
1276        self.element()
1277            .get_sub_element(ElementName::PduCollectionTrigger)?
1278            .character_data()?
1279            .enum_value()?
1280            .try_into()
1281            .ok()
1282    }
1283}
1284
1285//##################################################################
1286
1287/// The role of a TCP connection in a static socket connection can either be `Connect` (=client) or `Listen` (=server).
1288#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1289pub enum TcpRole {
1290    /// The TCP socket is a client which connects to a server
1291    Connect,
1292    /// The TCP socket is a server which listens for incoming connections
1293    Listen,
1294}
1295
1296impl TryFrom<EnumItem> for TcpRole {
1297    type Error = AutosarAbstractionError;
1298
1299    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
1300        match value {
1301            EnumItem::Listen => Ok(Self::Listen),
1302            EnumItem::Connect => Ok(Self::Connect),
1303
1304            _ => Err(AutosarAbstractionError::ValueConversionError {
1305                value: value.to_string(),
1306                dest: "TcpRole".to_string(),
1307            }),
1308        }
1309    }
1310}
1311
1312impl From<TcpRole> for EnumItem {
1313    fn from(value: TcpRole) -> Self {
1314        match value {
1315            TcpRole::Listen => EnumItem::Listen,
1316            TcpRole::Connect => EnumItem::Connect,
1317        }
1318    }
1319}
1320
1321//##################################################################
1322
1323/// control types used in routing groups for SOME/IP events
1324#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1325pub enum EventGroupControlType {
1326    /// Activate the data path for unicast events and triggered unicast events that are sent out after a client got subscribed
1327    ActivationAndTriggerUnicast,
1328    /// Activate the data path for multicast events of an `EventGroup`
1329    ActivationMulticast,
1330    /// Activate the data path for unicast events
1331    ActivationUnicast,
1332    /// Activate the data path for triggered unicast events that are sent out after a client got subscribed
1333    TriggerUnicast,
1334}
1335
1336impl From<EventGroupControlType> for EnumItem {
1337    fn from(value: EventGroupControlType) -> Self {
1338        match value {
1339            EventGroupControlType::ActivationAndTriggerUnicast => EnumItem::ActivationAndTriggerUnicast,
1340            EventGroupControlType::ActivationMulticast => EnumItem::ActivationMulticast,
1341            EventGroupControlType::ActivationUnicast => EnumItem::ActivationUnicast,
1342            EventGroupControlType::TriggerUnicast => EnumItem::TriggerUnicast,
1343        }
1344    }
1345}
1346
1347impl TryFrom<EnumItem> for EventGroupControlType {
1348    type Error = AutosarAbstractionError;
1349
1350    fn try_from(value: EnumItem) -> Result<Self, Self::Error> {
1351        match value {
1352            EnumItem::ActivationAndTriggerUnicast => Ok(Self::ActivationAndTriggerUnicast),
1353            EnumItem::ActivationMulticast => Ok(Self::ActivationMulticast),
1354            EnumItem::ActivationUnicast => Ok(Self::ActivationUnicast),
1355            EnumItem::TriggerUnicast => Ok(Self::TriggerUnicast),
1356
1357            _ => Err(AutosarAbstractionError::ValueConversionError {
1358                value: value.to_string(),
1359                dest: "EventGroupControlType".to_string(),
1360            }),
1361        }
1362    }
1363}
1364
1365//##################################################################
1366
1367#[cfg(test)]
1368mod test {
1369    use super::*;
1370    use crate::{ArPackage, AutosarModelAbstraction, System, SystemCategory, communication::GeneralPurposePduCategory};
1371    use autosar_data::AutosarVersion;
1372
1373    #[test]
1374    fn channel_basic() {
1375        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00044);
1376        let pkg = model.get_or_create_package("/test").unwrap();
1377        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1378        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1379        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1380
1381        let wrapped_channel: PhysicalChannel = channel.clone().into();
1382        assert_eq!(wrapped_channel, PhysicalChannel::Ethernet(channel));
1383    }
1384
1385    #[test]
1386    fn channel_network_endpoint() {
1387        // note: for this test, the version should be < AUTOSAR_00046
1388        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00044);
1389        let pkg = model.get_or_create_package("/test").unwrap();
1390        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1391        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1392        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1393
1394        // create a network endpoint that is not referenced by an ECU
1395        let endpoint = channel
1396            .create_network_endpoint(
1397                "Endpoint",
1398                NetworkEndpointAddress::IPv4 {
1399                    address: Some("192.168.0.1".to_string()),
1400                    address_source: None,
1401                    default_gateway: None,
1402                    network_mask: None,
1403                },
1404                None,
1405            )
1406            .unwrap();
1407        assert_eq!(channel.network_endpoints().next().unwrap(), endpoint);
1408
1409        // create a network endpoint that is referenced by an ECU
1410        // references from the ECU to the network endpoint are only used when the version is <= AUTOSAR_00046
1411        // variant 1 - the ECU is not connected to the channel, so it is not allowed to reference the endpoint
1412        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
1413        let result = channel.create_network_endpoint(
1414            "Endpoint1",
1415            NetworkEndpointAddress::IPv4 {
1416                address: Some("192.168.0.2".to_string()),
1417                address_source: None,
1418                default_gateway: None,
1419                network_mask: None,
1420            },
1421            Some(&ecu),
1422        );
1423        assert!(result.is_err());
1424        assert_eq!(channel.network_endpoints().next().unwrap(), endpoint);
1425
1426        // variant 2 - the ECU is connected to the channel, so it is allowed to reference the endpoint
1427        let ethernet_controller = ecu
1428            .create_ethernet_communication_controller("Controller", Some("01:23:45:ab:cd:ef".to_string()))
1429            .unwrap();
1430        ethernet_controller
1431            .connect_physical_channel("connection", &channel)
1432            .unwrap();
1433        let endpoint = channel
1434            .create_network_endpoint(
1435                "Endpoint2",
1436                NetworkEndpointAddress::IPv4 {
1437                    address: Some("192.168.0.2".to_string()),
1438                    address_source: None,
1439                    default_gateway: None,
1440                    network_mask: None,
1441                },
1442                Some(&ecu),
1443            )
1444            .unwrap();
1445        assert_eq!(channel.network_endpoints().last().unwrap(), endpoint);
1446        assert_eq!(channel.network_endpoints().count(), 2);
1447    }
1448
1449    #[test]
1450    fn channel_vlan() {
1451        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
1452        let pkg = model.get_or_create_package("/test").unwrap();
1453        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1454        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1455
1456        let channel = cluster.create_physical_channel("channel_name", None).unwrap();
1457        let c2 = channel.cluster().unwrap();
1458        assert_eq!(cluster, c2);
1459
1460        let vi = channel.vlan_info();
1461        assert!(vi.is_none());
1462
1463        let elem_vlan = channel
1464            .element()
1465            .create_named_sub_element(ElementName::Vlan, "VLAN_1")
1466            .unwrap();
1467        let vi = channel.vlan_info();
1468        assert!(vi.is_none());
1469
1470        let elem_vlanid = elem_vlan.create_sub_element(ElementName::VlanIdentifier).unwrap();
1471        let vi = channel.vlan_info();
1472        assert!(vi.is_none());
1473
1474        elem_vlanid.set_character_data(1).unwrap();
1475        let vi = channel.vlan_info().unwrap();
1476        assert_eq!(vi.vlan_id, 1);
1477    }
1478
1479    #[test]
1480    fn sd_configuration_old() {
1481        // note: for this test, the version should be < AUTOSAR_00046
1482        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00044);
1483        let pkg = model.get_or_create_package("/test").unwrap();
1484        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1485        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1486        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1487
1488        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
1489        let controller = ecu
1490            .create_ethernet_communication_controller("EthController", None)
1491            .unwrap();
1492        controller.connect_physical_channel("connection", &channel).unwrap();
1493
1494        let (
1495            unicast_socket,
1496            multicast_rx_socket,
1497            remote_anyaddr_socket,
1498            unicast_rx_pdu,
1499            unicast_tx_pdu,
1500            multicast_rx_pdu,
1501        ) = prepare_sd_config_items(&pkg, &system, &channel, &ecu);
1502
1503        let common_config = CommonServiceDiscoveryConfig {
1504            multicast_rx_socket: &multicast_rx_socket,
1505            multicast_rx_pdu: &multicast_rx_pdu,
1506            remote_socket: &remote_anyaddr_socket,
1507            name_prefix: None,
1508            prefer_static_socket_connections: false,
1509            ipdu_identifier_set: None,
1510        };
1511
1512        let result = channel.configure_service_discovery_for_ecu(
1513            &ecu,
1514            &unicast_socket,
1515            &unicast_rx_pdu,
1516            &unicast_tx_pdu,
1517            &common_config,
1518        );
1519        assert!(result.is_ok());
1520
1521        assert_eq!(channel.socket_connection_bundles().count(), 2);
1522        assert!(channel.socket_connection_bundles().any(|scb| {
1523            scb.server_port().is_some_and(|sp| sp == unicast_socket)
1524                && scb.bundled_connections().any(|sc| {
1525                    sc.client_ip_addr_from_connection_request() == Some(true)
1526                        && sc.client_port().is_some_and(|cp| &cp == common_config.remote_socket)
1527                        && sc.pdu_triggerings().count() == 2
1528                })
1529        }));
1530        assert!(channel.socket_connection_bundles().any(|scb| {
1531            scb.server_port()
1532                .is_some_and(|sp| &sp == common_config.multicast_rx_socket)
1533                && scb.bundled_connections().any(|sc| {
1534                    sc.client_ip_addr_from_connection_request() == Some(true)
1535                        && sc.client_port().is_some_and(|cp| &cp == common_config.remote_socket)
1536                        && sc.pdu_triggerings().count() == 1
1537                })
1538        }));
1539
1540        // run the function again. This should not create new elements
1541        let result = channel.configure_service_discovery_for_ecu(
1542            &ecu,
1543            &unicast_socket,
1544            &unicast_rx_pdu,
1545            &unicast_tx_pdu,
1546            &common_config,
1547        );
1548        assert!(result.is_ok());
1549        assert_eq!(channel.socket_connection_bundles().count(), 2);
1550    }
1551
1552    #[test]
1553    fn sd_configuration_new() {
1554        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00053);
1555        let pkg = model.get_or_create_package("/test").unwrap();
1556        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1557        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1558        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1559
1560        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
1561        let controller = ecu
1562            .create_ethernet_communication_controller("EthController", None)
1563            .unwrap();
1564        controller.connect_physical_channel("connection", &channel).unwrap();
1565
1566        let (
1567            unicast_socket,
1568            multicast_rx_socket,
1569            remote_anyaddr_socket,
1570            unicast_rx_pdu,
1571            unicast_tx_pdu,
1572            multicast_rx_pdu,
1573        ) = prepare_sd_config_items(&pkg, &system, &channel, &ecu);
1574        let ipdu_identifier_set = system
1575            .create_socket_connection_ipdu_identifier_set("IpduIdentifierSet", &pkg)
1576            .unwrap();
1577
1578        let common_config = CommonServiceDiscoveryConfig {
1579            multicast_rx_socket: &multicast_rx_socket,
1580            multicast_rx_pdu: &multicast_rx_pdu,
1581            remote_socket: &remote_anyaddr_socket,
1582            name_prefix: None,
1583            prefer_static_socket_connections: true,
1584            ipdu_identifier_set: Some(&ipdu_identifier_set),
1585        };
1586
1587        let result = channel.configure_service_discovery_for_ecu(
1588            &ecu,
1589            &unicast_socket,
1590            &unicast_rx_pdu,
1591            &unicast_tx_pdu,
1592            &common_config,
1593        );
1594        assert!(result.is_ok());
1595
1596        // check if the static socket connections were created
1597        assert!(unicast_socket.static_socket_connections().count() == 1);
1598        assert!(unicast_socket.static_socket_connections().any(|ssc| {
1599            ssc.remote_socket().is_some_and(|rs| &rs == common_config.remote_socket)
1600                && ssc.ipdu_identifiers().count() == 2
1601        }));
1602
1603        assert!(multicast_rx_socket.static_socket_connections().count() == 1);
1604        assert!(multicast_rx_socket.static_socket_connections().any(|ssc| {
1605            ssc.remote_socket().is_some_and(|rs| &rs == common_config.remote_socket)
1606                && ssc.ipdu_identifiers().count() == 1
1607        }));
1608
1609        // run the function again. This should not create new elements
1610        let result = channel.configure_service_discovery_for_ecu(
1611            &ecu,
1612            &unicast_socket,
1613            &unicast_rx_pdu,
1614            &unicast_tx_pdu,
1615            &common_config,
1616        );
1617        assert!(result.is_ok());
1618
1619        assert!(unicast_socket.static_socket_connections().count() == 1);
1620        assert!(multicast_rx_socket.static_socket_connections().count() == 1);
1621    }
1622
1623    fn prepare_sd_config_items(
1624        pkg: &ArPackage,
1625        system: &System,
1626        channel: &EthernetPhysicalChannel,
1627        ecu: &EcuInstance,
1628    ) -> (
1629        SocketAddress,
1630        SocketAddress,
1631        SocketAddress,
1632        GeneralPurposePdu,
1633        GeneralPurposePdu,
1634        GeneralPurposePdu,
1635    ) {
1636        let network_address = NetworkEndpointAddress::IPv4 {
1637            address: Some("192.168.0.1".to_string()),
1638            address_source: Some(IPv4AddressSource::Fixed),
1639            default_gateway: Some("192.168.0.200".to_string()),
1640            network_mask: Some("255.255.255.0".to_string()),
1641        };
1642        let network_endpoint = channel
1643            .create_network_endpoint("local_endpoint", network_address, None)
1644            .unwrap();
1645        let unicast_socket = channel
1646            .create_socket_address(
1647                "UnicastSocket",
1648                &network_endpoint,
1649                &TpConfig::UdpTp {
1650                    port_number: Some(30490),
1651                    port_dynamically_assigned: None,
1652                },
1653                SocketAddressType::Unicast(Some(ecu.clone())),
1654            )
1655            .unwrap();
1656        let multicast_rx_endpoint = channel
1657            .create_network_endpoint(
1658                "MulticastEndpoint",
1659                NetworkEndpointAddress::IPv4 {
1660                    address: Some("239.0.0.1".to_string()),
1661                    address_source: Some(IPv4AddressSource::Fixed),
1662                    default_gateway: None,
1663                    network_mask: None,
1664                },
1665                None,
1666            )
1667            .unwrap();
1668        let multicast_rx_socket = channel
1669            .create_socket_address(
1670                "MulticastSocket",
1671                &multicast_rx_endpoint,
1672                &TpConfig::UdpTp {
1673                    port_number: Some(30490),
1674                    port_dynamically_assigned: None,
1675                },
1676                SocketAddressType::Multicast(vec![ecu.clone()]),
1677            )
1678            .unwrap();
1679        let remote_anyaddr_endpoint = channel
1680            .create_network_endpoint(
1681                "RemoteEndpoint",
1682                NetworkEndpointAddress::IPv4 {
1683                    address: Some("ANY".to_string()),
1684                    address_source: None,
1685                    default_gateway: None,
1686                    network_mask: None,
1687                },
1688                None,
1689            )
1690            .unwrap();
1691        let remote_anyaddr_socket = channel
1692            .create_socket_address(
1693                "RemoteSocket",
1694                &remote_anyaddr_endpoint,
1695                &TpConfig::UdpTp {
1696                    port_number: None,
1697                    port_dynamically_assigned: Some(true),
1698                },
1699                SocketAddressType::Unicast(None),
1700            )
1701            .unwrap();
1702        let unicast_rx_pdu = system
1703            .create_general_purpose_pdu("UnicastRxPdu", pkg, 0, GeneralPurposePduCategory::Sd)
1704            .unwrap();
1705        let unicast_tx_pdu = system
1706            .create_general_purpose_pdu("UnicastTxPdu", pkg, 0, GeneralPurposePduCategory::Sd)
1707            .unwrap();
1708        let multicast_rx_pdu = system
1709            .create_general_purpose_pdu("MulticastRxPdu", pkg, 0, GeneralPurposePduCategory::Sd)
1710            .unwrap();
1711        (
1712            unicast_socket,
1713            multicast_rx_socket,
1714            remote_anyaddr_socket,
1715            unicast_rx_pdu,
1716            unicast_tx_pdu,
1717            multicast_rx_pdu,
1718        )
1719    }
1720
1721    #[test]
1722    fn socon_ipdu_identifier() {
1723        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
1724        let pkg = model.get_or_create_package("/test").unwrap();
1725        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1726        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1727        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1728        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
1729        let ipdu_identifier_set = system
1730            .create_socket_connection_ipdu_identifier_set("IpduIdentifierSet", &pkg)
1731            .unwrap();
1732        let pdu = system
1733            .create_general_purpose_pdu("Pdu", &pkg, 1, GeneralPurposePduCategory::Sd)
1734            .unwrap();
1735        let pdu2 = system
1736            .create_general_purpose_pdu("Pdu2", &pkg, 2, GeneralPurposePduCategory::Sd)
1737            .unwrap();
1738        let controller = ecu
1739            .create_ethernet_communication_controller("Controller", Some("01:23:45:ab:cd:ef".to_string()))
1740            .unwrap();
1741        controller.connect_physical_channel("connection", &channel).unwrap();
1742
1743        let socon_ipdu_identifier = ipdu_identifier_set
1744            .create_socon_ipdu_identifier(
1745                "SoConIPduIdentifier",
1746                &pdu,
1747                &channel,
1748                Some(SoConIPduIdentifier::SD_HEADER_ID),
1749                Some(0.1),
1750                Some(PduCollectionTrigger::Always),
1751            )
1752            .unwrap();
1753        assert_eq!(
1754            socon_ipdu_identifier.pdu_triggering().unwrap().pdu().unwrap(),
1755            pdu.into()
1756        );
1757        assert_eq!(
1758            socon_ipdu_identifier.header_id().unwrap(),
1759            SoConIPduIdentifier::SD_HEADER_ID
1760        );
1761        assert_eq!(socon_ipdu_identifier.timeout().unwrap(), 0.1);
1762        assert_eq!(
1763            socon_ipdu_identifier.collection_trigger().unwrap(),
1764            PduCollectionTrigger::Always
1765        );
1766
1767        socon_ipdu_identifier.set_pdu(&pdu2, &channel).unwrap();
1768        assert_eq!(
1769            socon_ipdu_identifier.pdu_triggering().unwrap().pdu().unwrap(),
1770            pdu2.into()
1771        );
1772        socon_ipdu_identifier.set_timeout(0.2).unwrap();
1773        assert_eq!(socon_ipdu_identifier.timeout().unwrap(), 0.2);
1774        socon_ipdu_identifier
1775            .set_collection_trigger(PduCollectionTrigger::Never)
1776            .unwrap();
1777        assert_eq!(
1778            socon_ipdu_identifier.collection_trigger().unwrap(),
1779            PduCollectionTrigger::Never
1780        );
1781        socon_ipdu_identifier.set_header_id(0x1234).unwrap();
1782        assert_eq!(socon_ipdu_identifier.header_id().unwrap(), 0x1234);
1783    }
1784
1785    #[test]
1786    pub fn static_socket_connection() {
1787        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
1788        let pkg = model.get_or_create_package("/test").unwrap();
1789        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
1790        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
1791        let channel = cluster.create_physical_channel("Channel", None).unwrap();
1792
1793        // create a static socket connection between the local_socket and the remote_socket
1794        let remote_address = NetworkEndpointAddress::IPv4 {
1795            address: Some("192.168.0.1".to_string()),
1796            address_source: Some(IPv4AddressSource::Fixed),
1797            default_gateway: None,
1798            network_mask: None,
1799        };
1800        let remote_endpoint = channel
1801            .create_network_endpoint("RemoteAddress", remote_address, None)
1802            .unwrap();
1803        let remote_socket = channel
1804            .create_socket_address(
1805                "RemoteSocket",
1806                &remote_endpoint,
1807                &TpConfig::UdpTp {
1808                    port_number: Some(12345),
1809                    port_dynamically_assigned: None,
1810                },
1811                SocketAddressType::Unicast(None),
1812            )
1813            .unwrap();
1814        let local_address = NetworkEndpointAddress::IPv4 {
1815            address: Some("192.168.0.2".to_string()),
1816            address_source: Some(IPv4AddressSource::Fixed),
1817            default_gateway: None,
1818            network_mask: None,
1819        };
1820        let local_endpoint = channel
1821            .create_network_endpoint("LocalAddress", local_address, None)
1822            .unwrap();
1823        let local_socket = channel
1824            .create_socket_address(
1825                "LocalSocket",
1826                &local_endpoint,
1827                &TpConfig::UdpTp {
1828                    port_number: Some(12346),
1829                    port_dynamically_assigned: None,
1830                },
1831                SocketAddressType::Unicast(None),
1832            )
1833            .unwrap();
1834        let ssc = local_socket
1835            .create_static_socket_connection("ssc", &remote_socket, None, None)
1836            .unwrap();
1837        assert_eq!(ssc.remote_socket().unwrap(), remote_socket);
1838        assert_eq!(ssc.tcp_role(), None);
1839        ssc.set_tcp_role(Some(TcpRole::Connect)).unwrap();
1840        assert_eq!(ssc.tcp_role().unwrap(), TcpRole::Connect);
1841        ssc.set_tcp_connect_timeout(Some(0.3333)).unwrap();
1842        assert_eq!(ssc.tcp_connect_timeout().unwrap(), 0.3333);
1843
1844        // add an IPduIdentifier to the static socket connection
1845        assert_eq!(ssc.ipdu_identifiers().count(), 0);
1846        let ipdu_identifier_set = system
1847            .create_socket_connection_ipdu_identifier_set("IpduIdentifierSet", &pkg)
1848            .unwrap();
1849        let pdu = GeneralPurposePdu::new("Pdu", &pkg, 0, GeneralPurposePduCategory::Sd).unwrap();
1850        let socon_ipdu_identifier = ipdu_identifier_set
1851            .create_socon_ipdu_identifier(
1852                "SoConIPduIdentifier",
1853                &pdu,
1854                &channel,
1855                Some(SoConIPduIdentifier::SD_HEADER_ID),
1856                Some(0.1),
1857                Some(PduCollectionTrigger::Always),
1858            )
1859            .unwrap();
1860        ssc.add_ipdu_identifier(&socon_ipdu_identifier).unwrap();
1861        assert_eq!(ssc.ipdu_identifiers().count(), 1);
1862        assert_eq!(ssc.ipdu_identifiers().next().unwrap(), socon_ipdu_identifier);
1863        assert_eq!(ssc.socket_address().unwrap(), local_socket);
1864    }
1865}