autosar_data_abstraction/communication/physical_channel/ethernet/
socketaddress.rs

1use crate::communication::{
2    AbstractPhysicalChannel, ConsumedServiceInstanceV1, EthernetPhysicalChannel, NetworkEndpoint,
3    ProvidedServiceInstanceV1, StaticSocketConnection, TcpRole,
4};
5use crate::{
6    AbstractionElement, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement, abstraction_element,
7};
8use autosar_data::{Element, ElementName};
9
10//##################################################################
11
12/// A socket address establishes the link between one or more ECUs and a `NetworkEndpoint`.
13/// It contains all settings that are relevant for this combination.
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct SocketAddress(Element);
16abstraction_element!(SocketAddress, SocketAddress);
17impl IdentifiableAbstractionElement for SocketAddress {}
18
19impl SocketAddress {
20    pub(crate) fn new(
21        name: &str,
22        channel: &EthernetPhysicalChannel,
23        network_endpoint: &NetworkEndpoint,
24        tp_config: &TpConfig,
25        sa_type: SocketAddressType,
26    ) -> Result<Self, AutosarAbstractionError> {
27        let channel_elem = channel.element();
28        let (unicast, ecu_instances) = match sa_type {
29            SocketAddressType::Unicast(Some(ecu_instance)) => (true, vec![ecu_instance]),
30            SocketAddressType::Unicast(None) => (true, vec![]),
31            SocketAddressType::Multicast(ecu_instances) => (false, ecu_instances),
32        };
33
34        // TCP connections don't work using multicast IP addresses
35        if !unicast && matches!(tp_config, TpConfig::TcpTp { .. }) {
36            return Err(AutosarAbstractionError::InvalidParameter(
37                "TCP is incomptible with multicasting".to_string(),
38            ));
39        }
40        // extension: check if the address is valid for multicasting?
41        // IPv4: 224.0.0.0 - 239.255.255.255
42        // IPv6: FFxx:/12
43
44        // get the connector for each ECU in advance, so that nothing needs to be cleaned up if there is a problem here
45        let connectors = ecu_instances
46            .iter()
47            .filter_map(|ecu_instance| channel.ecu_connector(ecu_instance).map(|conn| conn.element().clone()))
48            .collect::<Vec<_>>();
49        if connectors.len() != ecu_instances.len() {
50            return Err(AutosarAbstractionError::InvalidParameter(
51                "All EcuInstances must be connected to the EthernetPhysicalChannel".to_string(),
52            ));
53        }
54
55        let elem = channel_elem
56            .get_or_create_sub_element(ElementName::SoAdConfig)?
57            .get_or_create_sub_element(ElementName::SocketAddresss)?
58            .create_named_sub_element(ElementName::SocketAddress, name)?;
59
60        if unicast {
61            if !connectors.is_empty() {
62                elem.create_sub_element(ElementName::ConnectorRef)
63                    .unwrap()
64                    .set_reference_target(&connectors[0])
65                    .unwrap();
66            }
67        } else {
68            let mc_connectors = elem.create_sub_element(ElementName::MulticastConnectorRefs)?;
69            for conn in &connectors {
70                mc_connectors
71                    .create_sub_element(ElementName::MulticastConnectorRef)?
72                    .set_reference_target(conn)?;
73            }
74        }
75
76        let ae_name = format!("{name}_AE");
77        let ae = elem.create_named_sub_element(ElementName::ApplicationEndpoint, &ae_name)?;
78        ae.create_sub_element(ElementName::NetworkEndpointRef)?
79            .set_reference_target(network_endpoint.element())?;
80        let tp_configuration = ae.create_sub_element(ElementName::TpConfiguration)?;
81        match tp_config {
82            TpConfig::TcpTp {
83                port_number,
84                port_dynamically_assigned,
85            } => {
86                let tcptp = tp_configuration.create_sub_element(ElementName::TcpTp)?;
87                let tcptp_port = tcptp.create_sub_element(ElementName::TcpTpPort)?;
88                // PortNumber and DynamicallyAssigned are mutually exclusive.
89                // The attribute DynamicallyAssigned is deprecated starting in Autosar 4.5.0
90                if let Some(portnum) = port_number {
91                    tcptp_port
92                        .create_sub_element(ElementName::PortNumber)?
93                        .set_character_data(portnum.to_string())?;
94                } else if let Some(dyn_assign) = port_dynamically_assigned {
95                    tcptp_port
96                        .create_sub_element(ElementName::DynamicallyAssigned)?
97                        .set_character_data(*dyn_assign)?;
98                }
99            }
100            TpConfig::UdpTp {
101                port_number,
102                port_dynamically_assigned,
103            } => {
104                let udptp_port = tp_configuration
105                    .create_sub_element(ElementName::UdpTp)?
106                    .create_sub_element(ElementName::UdpTpPort)?;
107                // PortNumber and DynamicallyAssigned are mutually exclusive.
108                // The attribute DynamicallyAssigned is deprecated starting in Autosar 4.5.0
109                if let Some(portnum) = port_number {
110                    udptp_port
111                        .create_sub_element(ElementName::PortNumber)?
112                        .set_character_data(portnum.to_string())?;
113                } else if let Some(dyn_assign) = port_dynamically_assigned {
114                    let boolstr = if *dyn_assign { "true" } else { "false" };
115                    udptp_port
116                        .create_sub_element(ElementName::DynamicallyAssigned)?
117                        .set_character_data(boolstr)?;
118                }
119            }
120        }
121
122        Ok(Self(elem))
123    }
124
125    /// get the network endpoint of this `SocketAddress`
126    #[must_use]
127    pub fn network_endpoint(&self) -> Option<NetworkEndpoint> {
128        let ne = self
129            .element()
130            .get_sub_element(ElementName::ApplicationEndpoint)?
131            .get_sub_element(ElementName::NetworkEndpointRef)?
132            .get_reference_target()
133            .ok()?;
134        ne.try_into().ok()
135    }
136
137    /// get the socket address type: unicast / multicast, as well as the connected ecus
138    #[must_use]
139    pub fn socket_address_type(&self) -> Option<SocketAddressType> {
140        if let Some(connector_ref) = self.0.get_sub_element(ElementName::ConnectorRef) {
141            let ecu = EcuInstance::try_from(connector_ref.get_reference_target().ok()?.named_parent().ok()??).ok()?;
142            Some(SocketAddressType::Unicast(Some(ecu)))
143        } else if let Some(mcr) = self.0.get_sub_element(ElementName::MulticastConnectorRefs) {
144            let ecus = mcr
145                .sub_elements()
146                .filter_map(|cr| {
147                    cr.get_reference_target()
148                        .ok()
149                        .and_then(|conn| conn.named_parent().ok().flatten())
150                })
151                .filter_map(|ecu_elem| EcuInstance::try_from(ecu_elem).ok())
152                .collect::<Vec<_>>();
153            Some(SocketAddressType::Multicast(ecus))
154        } else {
155            None
156        }
157    }
158
159    /// add an `EcuInstance` to this multicast `SocketAddress`
160    pub fn add_multicast_ecu(&self, ecu: &EcuInstance) -> Result<(), AutosarAbstractionError> {
161        let socket_type = self.socket_address_type();
162        match socket_type {
163            Some(SocketAddressType::Multicast(multicast_ecus)) => {
164                // extend the list of multicast EcuInstances if needed
165                if !multicast_ecus.contains(ecu) {
166                    let Some(connector) = self.physical_channel()?.ecu_connector(ecu) else {
167                        return Err(AutosarAbstractionError::InvalidParameter(
168                            "EcuInstance is not connected to the EthernetPhysicalChannel".to_string(),
169                        ));
170                    };
171                    let mcr = self.0.get_or_create_sub_element(ElementName::MulticastConnectorRefs)?;
172                    let mc_ref = mcr.create_sub_element(ElementName::MulticastConnectorRef)?;
173                    mc_ref.set_reference_target(connector.element())?;
174                }
175            }
176            None => {
177                // add the first EcuInstance to this multicast SocketAddress
178                let Some(connector) = self.physical_channel()?.ecu_connector(ecu) else {
179                    return Err(AutosarAbstractionError::InvalidParameter(
180                        "EcuInstance is not connected to the EthernetPhysicalChannel".to_string(),
181                    ));
182                };
183                let mcr = self.0.get_or_create_sub_element(ElementName::MulticastConnectorRefs)?;
184                let mc_ref = mcr.create_sub_element(ElementName::MulticastConnectorRef)?;
185                mc_ref.set_reference_target(connector.element())?;
186            }
187            Some(SocketAddressType::Unicast(_)) => {
188                return Err(AutosarAbstractionError::InvalidParameter(
189                    "This SocketAddress is not a multicast socket".to_string(),
190                ));
191            }
192        }
193
194        Ok(())
195    }
196
197    /// set the `EcuInstance` for this unicast `SocketAddress`
198    pub fn set_unicast_ecu(&self, ecu: &EcuInstance) -> Result<(), AutosarAbstractionError> {
199        let socket_type = self.socket_address_type();
200        match socket_type {
201            None | Some(SocketAddressType::Unicast(_)) => {
202                let channel = self.physical_channel()?;
203                let Some(connector) = channel.ecu_connector(ecu) else {
204                    return Err(AutosarAbstractionError::InvalidParameter(
205                        "EcuInstance is not connected to the EthernetPhysicalChannel".to_string(),
206                    ));
207                };
208                self.0
209                    .get_or_create_sub_element(ElementName::ConnectorRef)?
210                    .set_reference_target(connector.element())?;
211            }
212            Some(SocketAddressType::Multicast(_)) => {
213                return Err(AutosarAbstractionError::InvalidParameter(
214                    "This SocketAddress is not a unicast socket".to_string(),
215                ));
216            }
217        }
218
219        Ok(())
220    }
221
222    /// get the transport protocol settings for this `SocketAddress`
223    #[must_use]
224    pub fn tp_config(&self) -> Option<TpConfig> {
225        let tp = self
226            .0
227            .get_sub_element(ElementName::ApplicationEndpoint)?
228            .get_sub_element(ElementName::TpConfiguration)?;
229
230        if let Some(tcp_tp) = tp.get_sub_element(ElementName::TcpTp) {
231            let port = tcp_tp.get_sub_element(ElementName::TcpTpPort)?;
232            let (port_number, port_dynamically_assigned) = Self::port_config(&port);
233            Some(TpConfig::TcpTp {
234                port_number,
235                port_dynamically_assigned,
236            })
237        } else if let Some(udp_tp) = tp.get_sub_element(ElementName::UdpTp) {
238            let port = udp_tp.get_sub_element(ElementName::UdpTpPort)?;
239            let (port_number, port_dynamically_assigned) = Self::port_config(&port);
240            Some(TpConfig::UdpTp {
241                port_number,
242                port_dynamically_assigned,
243            })
244        } else {
245            None
246        }
247    }
248
249    // get the port number and dynamic assignment setting from a port element
250    fn port_config(port_element: &Element) -> (Option<u16>, Option<bool>) {
251        let port_number = port_element
252            .get_sub_element(ElementName::PortNumber)
253            .and_then(|pn| pn.character_data())
254            .and_then(|cdata| cdata.parse_integer());
255        let port_dynamically_assigned = port_element
256            .get_sub_element(ElementName::DynamicallyAssigned)
257            .and_then(|da| da.character_data())
258            .and_then(|cdata| cdata.string_value())
259            .map(|val| val == "true" || val == "1");
260        (port_number, port_dynamically_assigned)
261    }
262
263    /// create a new `StaticSocketConnection` from this `SocketAddress` to a remote `SocketAddress`
264    pub fn create_static_socket_connection(
265        &self,
266        name: &str,
267        remote_address: &SocketAddress,
268        tcp_role: Option<TcpRole>,
269        tcp_connect_timeout: Option<f64>,
270    ) -> Result<StaticSocketConnection, AutosarAbstractionError> {
271        let own_tp_config = self.tp_config();
272        let remote_tp_config = remote_address.tp_config();
273        match (own_tp_config, remote_tp_config) {
274            (Some(TpConfig::TcpTp { .. }), Some(TpConfig::TcpTp { .. })) => {
275                StaticSocketConnection::new(name, self.element(), remote_address, tcp_role, tcp_connect_timeout)
276            }
277            (Some(TpConfig::UdpTp { .. }), Some(TpConfig::UdpTp { .. })) | (None, None) => {
278                StaticSocketConnection::new(name, self.element(), remote_address, None, None)
279            }
280            _ => Err(AutosarAbstractionError::InvalidParameter(
281                "Both SocketAddresses must use the same transport protocol".to_string(),
282            )),
283        }
284    }
285
286    /// get the `PhysicalChannel` containing this `SocketAddress`
287    pub fn physical_channel(&self) -> Result<EthernetPhysicalChannel, AutosarAbstractionError> {
288        let named_parent = self.0.named_parent()?.unwrap();
289        named_parent.try_into()
290    }
291
292    /// iterate over all `StaticSocketConnection`s in this `SocketAddress`
293    pub fn static_socket_connections(&self) -> impl Iterator<Item = StaticSocketConnection> + Send + use<> {
294        self.0
295            .get_sub_element(ElementName::StaticSocketConnections)
296            .into_iter()
297            .flat_map(|ssc| ssc.sub_elements())
298            .filter_map(|ssc| StaticSocketConnection::try_from(ssc).ok())
299    }
300
301    /// create a `ProvidedServiceInstanceV1` in this `SocketAddress`
302    ///
303    /// Creating a `ProvidedServiceInstanceV1` in a `SocketAddress` is part of the old way of defining services (<= Autosar 4.5.0).
304    /// It is obsolete in newer versions of the standard.
305    ///
306    /// When using the new way of defining services, a `ProvidedServiceInstance` should be created in a `ServiceInstanceCollectionSet` instead.
307    pub fn create_provided_service_instance(
308        &self,
309        name: &str,
310        service_identifier: u16,
311        instance_identifier: u16,
312    ) -> Result<ProvidedServiceInstanceV1, AutosarAbstractionError> {
313        let socket_name = self.name().unwrap_or_default();
314        let ae_name = format!("{socket_name}_AE");
315        let ae = self
316            .element()
317            .get_or_create_named_sub_element(ElementName::ApplicationEndpoint, &ae_name)?;
318        let psis = ae.get_or_create_sub_element(ElementName::ProvidedServiceInstances)?;
319
320        ProvidedServiceInstanceV1::new(name, &psis, service_identifier, instance_identifier)
321    }
322
323    /// get the `ProvidedServiceInstanceV1`s in this `SocketAddress`
324    pub fn provided_service_instances(&self) -> impl Iterator<Item = ProvidedServiceInstanceV1> + Send + use<> {
325        self.element()
326            .get_sub_element(ElementName::ApplicationEndpoint)
327            .and_then(|ae| ae.get_sub_element(ElementName::ProvidedServiceInstances))
328            .into_iter()
329            .flat_map(|psis| psis.sub_elements())
330            .filter_map(|psi| ProvidedServiceInstanceV1::try_from(psi).ok())
331    }
332
333    /// create a `ConsumedServiceInstanceV1` in this `SocketAddress`
334    ///
335    /// Creating a `ConsumedServiceInstanceV1` in a `SocketAddress` is part of the old way of defining services (<= Autosar 4.5.0).
336    /// It is obsolete in newer versions of the standard.
337    ///
338    /// When using the new way of defining services, a `ConsumedServiceInstance` should be created in a `ServiceInstanceCollectionSet` instead.
339    pub fn create_consumed_service_instance(
340        &self,
341        name: &str,
342        provided_service_instance: &ProvidedServiceInstanceV1,
343    ) -> Result<ConsumedServiceInstanceV1, AutosarAbstractionError> {
344        let socket_name = self.name().unwrap_or_default();
345        let ae_name = format!("{socket_name}_AE");
346        let ae = self
347            .element()
348            .get_or_create_named_sub_element(ElementName::ApplicationEndpoint, &ae_name)?;
349        let csis = ae.get_or_create_sub_element(ElementName::ConsumedServiceInstances)?;
350        ConsumedServiceInstanceV1::new(name, &csis, provided_service_instance)
351    }
352
353    /// get the `ConsumedServiceInstance`s in this `SocketAddress`
354    pub fn consumed_service_instances(&self) -> impl Iterator<Item = ConsumedServiceInstanceV1> + Send + use<> {
355        self.element()
356            .get_sub_element(ElementName::ApplicationEndpoint)
357            .and_then(|ae| ae.get_sub_element(ElementName::ConsumedServiceInstances))
358            .into_iter()
359            .flat_map(|csis| csis.sub_elements())
360            .filter_map(|csi| ConsumedServiceInstanceV1::try_from(csi).ok())
361    }
362}
363
364//##################################################################
365
366/// transport protocol settings of a [`SocketAddress`]
367#[derive(Debug, Clone, PartialEq, Eq)]
368pub enum TpConfig {
369    /// The socket uses TCP
370    TcpTp {
371        /// The port number used by the socket
372        port_number: Option<u16>,
373        /// If the port number is dynamically assigned. Obsolete; set the port number to None instead
374        port_dynamically_assigned: Option<bool>,
375        // additional TCP options: currently not supported
376    },
377    /// The socket uses UDP
378    UdpTp {
379        /// The port number used by the socket
380        port_number: Option<u16>,
381        /// If the port number is dynamically assigned. Obsolete; set the port number to None instead
382        port_dynamically_assigned: Option<bool>,
383    },
384    // RtpTp, Ieee1722Tp, HttpTp: currently not supported
385}
386
387//##################################################################
388
389/// Describes if a [`SocketAddress`] is used for unicast or multicast
390#[derive(Debug, Clone, PartialEq)]
391pub enum SocketAddressType {
392    /// The socket is used for unicast communication with a single ECU
393    Unicast(Option<EcuInstance>),
394    /// The socket is used for multicast communication with multiple ECUs
395    Multicast(Vec<EcuInstance>),
396}
397
398//##################################################################
399
400#[cfg(test)]
401mod test {
402    use super::*;
403    use crate::communication::{IPv4AddressSource, NetworkEndpointAddress};
404    use crate::{AutosarModelAbstraction, SystemCategory};
405    use autosar_data::AutosarVersion;
406
407    #[test]
408    fn socket_address() {
409        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_4_3_0);
410        let package = model.get_or_create_package("/pkg1").unwrap();
411        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
412        let cluster = system.create_ethernet_cluster("Cluster", &package).unwrap();
413        let channel = cluster.create_physical_channel("Channel", None).unwrap();
414
415        let ecu_instance = system.create_ecu_instance("Ecu", &package).unwrap();
416        let controller = ecu_instance
417            .create_ethernet_communication_controller("EthCtrl", None)
418            .unwrap();
419        controller.connect_physical_channel("connection", &channel).unwrap();
420
421        let ecu_instance2 = system.create_ecu_instance("Ecu2", &package).unwrap();
422        let controller2 = ecu_instance2
423            .create_ethernet_communication_controller("EthCtrl", None)
424            .unwrap();
425        controller2.connect_physical_channel("connection", &channel).unwrap();
426
427        let ecu_instance3 = system.create_ecu_instance("Ecu3", &package).unwrap();
428        let controller3 = ecu_instance3
429            .create_ethernet_communication_controller("EthCtrl", None)
430            .unwrap();
431        controller3.connect_physical_channel("connection", &channel).unwrap();
432
433        let endpoint_address = NetworkEndpointAddress::IPv4 {
434            address: Some("192.168.0.1".to_string()),
435            address_source: Some(IPv4AddressSource::Fixed),
436            default_gateway: Some("192.168.0.2".to_string()),
437            network_mask: Some("255.255.255.0".to_string()),
438        };
439        let network_endpoint = channel
440            .create_network_endpoint("Address", endpoint_address, Some(&ecu_instance))
441            .unwrap();
442        let tcp_port = TpConfig::UdpTp {
443            port_number: Some(1234),
444            port_dynamically_assigned: None,
445        };
446
447        // create a unicast socket with an EcuInstance
448        let socket_type: SocketAddressType = SocketAddressType::Unicast(Some(ecu_instance.clone()));
449        let unicast_socket_address = channel
450            .create_socket_address("Socket", &network_endpoint, &tcp_port, socket_type.clone())
451            .unwrap();
452        assert_eq!(channel.socket_addresses().count(), 1);
453        assert_eq!(unicast_socket_address.network_endpoint().unwrap(), network_endpoint);
454        assert_eq!(unicast_socket_address.socket_address_type().unwrap(), socket_type);
455        // replace the EcuInstance in the socket
456        unicast_socket_address.set_unicast_ecu(&ecu_instance2).unwrap();
457        assert_eq!(
458            unicast_socket_address.socket_address_type().unwrap(),
459            SocketAddressType::Unicast(Some(ecu_instance2.clone()))
460        );
461
462        // create a unicast socket without an EcuInstance
463        let socket_type: SocketAddressType = SocketAddressType::Unicast(None);
464        let unicast_socket_address2 = channel
465            .create_socket_address("Socket2", &network_endpoint, &tcp_port, socket_type.clone())
466            .unwrap();
467        // set the EcuInstance and verify that it is set
468        unicast_socket_address2.set_unicast_ecu(&ecu_instance).unwrap();
469        assert_eq!(
470            unicast_socket_address2.socket_address_type().unwrap(),
471            SocketAddressType::Unicast(Some(ecu_instance.clone()))
472        );
473
474        // create a multicast socket with multiple EcuInstances
475        let socket_type: SocketAddressType =
476            SocketAddressType::Multicast(vec![ecu_instance.clone(), ecu_instance2.clone()]);
477        let multicast_socket_address = channel
478            .create_socket_address("Socket3", &network_endpoint, &tcp_port, socket_type.clone())
479            .unwrap();
480        assert_eq!(multicast_socket_address.socket_address_type().unwrap(), socket_type);
481        // add an EcuInstance to the multicast socket
482        multicast_socket_address.add_multicast_ecu(&ecu_instance3).unwrap();
483        assert_eq!(
484            multicast_socket_address.socket_address_type().unwrap(),
485            SocketAddressType::Multicast(vec![ecu_instance.clone(), ecu_instance2.clone(), ecu_instance3.clone()])
486        );
487    }
488
489    #[test]
490    fn socket_sd_config() {
491        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_4_3_0);
492        let package = model.get_or_create_package("/pkg1").unwrap();
493        let system = package.create_system("System", SystemCategory::SystemExtract).unwrap();
494        let cluster = system.create_ethernet_cluster("Cluster", &package).unwrap();
495        let channel = cluster.create_physical_channel("Channel", None).unwrap();
496
497        // let ecu_instance = system.create_ecu_instance("Ecu", &package).unwrap();
498        // let controller = ecu_instance
499        //     .create_ethernet_communication_controller("EthCtrl", None)
500        //     .unwrap();
501        // controller.connect_physical_channel("connection", &channel).unwrap();
502
503        let endpoint_address = NetworkEndpointAddress::IPv4 {
504            address: Some("192.168.0.1".to_string()),
505            address_source: Some(IPv4AddressSource::Fixed),
506            default_gateway: None,
507            network_mask: None,
508        };
509        let network_endpoint = channel
510            .create_network_endpoint("Address", endpoint_address, None)
511            .unwrap();
512        let tcp_port = TpConfig::TcpTp {
513            port_number: Some(1234),
514            port_dynamically_assigned: None,
515        };
516        let socket_type: SocketAddressType = SocketAddressType::Unicast(None);
517        let socket = channel
518            .create_socket_address("Socket", &network_endpoint, &tcp_port, socket_type.clone())
519            .unwrap();
520
521        let provided_service_instance = socket.create_provided_service_instance("psi", 1, 2).unwrap();
522        let consumed_service_instance = socket
523            .create_consumed_service_instance("csi", &provided_service_instance)
524            .unwrap();
525
526        assert_eq!(socket.provided_service_instances().count(), 1);
527        assert_eq!(
528            socket.provided_service_instances().next().unwrap(),
529            provided_service_instance
530        );
531        assert_eq!(socket.consumed_service_instances().count(), 1);
532        assert_eq!(
533            socket.consumed_service_instances().next().unwrap(),
534            consumed_service_instance
535        );
536    }
537}