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