autosar_data_abstraction/communication/transport_layer/
doip_tp.rs

1use crate::communication::{EthernetCluster, PduTriggering};
2use crate::{
3    AbstractionElement, ArPackage, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element,
4};
5use autosar_data::{Element, ElementName};
6
7/// Container for `DoIp` TP configuration
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub struct DoIpTpConfig(Element);
10abstraction_element!(DoIpTpConfig, DoIpTpConfig);
11impl IdentifiableAbstractionElement for DoIpTpConfig {}
12
13impl DoIpTpConfig {
14    pub(crate) fn new(
15        name: &str,
16        package: &ArPackage,
17        cluster: &EthernetCluster,
18    ) -> Result<Self, AutosarAbstractionError> {
19        let pkg_elem = package.element().get_or_create_sub_element(ElementName::Elements)?;
20
21        let tp_config_elem = pkg_elem.create_named_sub_element(ElementName::DoIpTpConfig, name)?;
22        let tp_config = Self(tp_config_elem);
23
24        tp_config.set_cluster(cluster)?;
25
26        Ok(tp_config)
27    }
28
29    /// set the reference to the `EthernetCluster` for this `DoIpTpConfig`
30    pub fn set_cluster(&self, cluster: &EthernetCluster) -> Result<(), AutosarAbstractionError> {
31        self.element()
32            .get_or_create_sub_element(ElementName::CommunicationClusterRef)?
33            .set_reference_target(cluster.element())?;
34        Ok(())
35    }
36
37    /// get the `EthernetCluster` for this `DoIpTpConfig`
38    #[must_use]
39    pub fn cluster(&self) -> Option<EthernetCluster> {
40        self.element()
41            .get_sub_element(ElementName::CommunicationClusterRef)
42            .and_then(|elem| elem.get_reference_target().ok())
43            .and_then(|elem| EthernetCluster::try_from(elem).ok())
44    }
45
46    /// create a new `DoIpLogicAddress`
47    pub fn create_doip_logic_address(
48        &self,
49        name: &str,
50        address: u32,
51    ) -> Result<DoIpLogicAddress, AutosarAbstractionError> {
52        let logic_addresses_elem = self
53            .element()
54            .get_or_create_sub_element(ElementName::DoIpLogicAddresss)?;
55        DoIpLogicAddress::new(name, &logic_addresses_elem, address)
56    }
57
58    /// iterate over all `DoIpLogicAddresss`
59    pub fn doip_logic_addresses(&self) -> impl Iterator<Item = DoIpLogicAddress> + Send + use<> {
60        self.element()
61            .get_sub_element(ElementName::DoIpLogicAddresss)
62            .into_iter()
63            .flat_map(|elem| elem.sub_elements())
64            .map(DoIpLogicAddress)
65    }
66
67    /// create a new `DoIpTpConnection`
68    pub fn create_doip_tp_connection(
69        &self,
70        name: Option<&str>,
71        source: &DoIpLogicAddress,
72        target: &DoIpLogicAddress,
73        tp_sdu_triggering: &PduTriggering,
74    ) -> Result<DoIpTpConnection, AutosarAbstractionError> {
75        let tp_connections_elem = self.element().get_or_create_sub_element(ElementName::TpConnections)?;
76        DoIpTpConnection::new(name, &tp_connections_elem, source, target, tp_sdu_triggering)
77    }
78
79    /// iterate over all `DoIpTpConnections`
80    pub fn doip_tp_connections(&self) -> impl Iterator<Item = DoIpTpConnection> + Send + use<> {
81        self.element()
82            .get_sub_element(ElementName::TpConnections)
83            .into_iter()
84            .flat_map(|elem| elem.sub_elements())
85            .map(DoIpTpConnection)
86    }
87}
88
89//##################################################################
90
91/// This element defines the logical address of a `DoIp` connection
92#[derive(Debug, Clone, PartialEq, Eq, Hash)]
93pub struct DoIpLogicAddress(Element);
94abstraction_element!(DoIpLogicAddress, DoIpLogicAddress);
95impl IdentifiableAbstractionElement for DoIpLogicAddress {}
96
97impl DoIpLogicAddress {
98    pub(crate) fn new(name: &str, parent: &Element, address: u32) -> Result<Self, AutosarAbstractionError> {
99        let logic_address_elem = parent.create_named_sub_element(ElementName::DoIpLogicAddress, name)?;
100        let logic_address = Self(logic_address_elem);
101        logic_address.set_address(address)?;
102
103        Ok(logic_address)
104    }
105
106    /// set the address of this `DoIpLogicAddress`
107    pub fn set_address(&self, address: u32) -> Result<(), AutosarAbstractionError> {
108        self.element()
109            .get_or_create_sub_element(ElementName::Address)?
110            .set_character_data(u64::from(address))?;
111        Ok(())
112    }
113
114    /// get the address of this `DoIpLogicAddress`
115    #[must_use]
116    pub fn address(&self) -> Option<u32> {
117        self.element()
118            .get_sub_element(ElementName::Address)
119            .and_then(|elem| elem.character_data())
120            .and_then(|data| data.parse_integer())
121    }
122}
123
124//##################################################################
125
126/// The `DoIpTpConnection` defines a `DoIp` transport protocol connection
127#[derive(Debug, Clone, PartialEq, Eq, Hash)]
128pub struct DoIpTpConnection(Element);
129abstraction_element!(DoIpTpConnection, DoIpTpConnection);
130
131impl IdentifiableAbstractionElement for DoIpTpConnection {
132    /// get the name of the connection
133    ///
134    /// In early versions of the Autosar standard, `TpConnections` were not identifiable.
135    /// This was fixed later by adding the `Ident` sub-element. This method returns the name
136    /// provied in the Ident element, if it exists.
137    fn name(&self) -> Option<String> {
138        self.element()
139            .get_sub_element(ElementName::Ident)
140            .and_then(|elem| elem.item_name())
141    }
142
143    /// set the name of the connection
144    fn set_name(&self, name: &str) -> Result<(), AutosarAbstractionError> {
145        if let Some(ident_elem) = self.element().get_sub_element(ElementName::Ident) {
146            ident_elem.set_item_name(name)?;
147        } else {
148            self.element().create_named_sub_element(ElementName::Ident, name)?;
149        }
150        Ok(())
151    }
152}
153
154impl DoIpTpConnection {
155    pub(crate) fn new(
156        name: Option<&str>,
157        parent: &Element,
158        source: &DoIpLogicAddress,
159        target: &DoIpLogicAddress,
160        tp_sdu_triggering: &PduTriggering,
161    ) -> Result<Self, AutosarAbstractionError> {
162        let tp_connection_elem = parent.create_sub_element(ElementName::DoIpTpConnection)?;
163        if let Some(name) = name {
164            tp_connection_elem.create_named_sub_element(ElementName::Ident, name)?;
165        }
166        let tp_connection = Self(tp_connection_elem);
167        tp_connection.set_source(source)?;
168        tp_connection.set_target(target)?;
169        tp_connection.set_tp_sdu_triggering(tp_sdu_triggering)?;
170
171        Ok(tp_connection)
172    }
173
174    /// set the source `DoIpLogicAddress`
175    pub fn set_source(&self, source: &DoIpLogicAddress) -> Result<(), AutosarAbstractionError> {
176        self.element()
177            .get_or_create_sub_element(ElementName::DoIpSourceAddressRef)?
178            .set_reference_target(source.element())?;
179        Ok(())
180    }
181
182    /// get the source `DoIpLogicAddress`
183    #[must_use]
184    pub fn source(&self) -> Option<DoIpLogicAddress> {
185        self.element()
186            .get_sub_element(ElementName::DoIpSourceAddressRef)
187            .and_then(|elem| elem.get_reference_target().ok())
188            .and_then(|elem| DoIpLogicAddress::try_from(elem).ok())
189    }
190
191    /// set the target `DoIpLogicAddress`
192    pub fn set_target(&self, target: &DoIpLogicAddress) -> Result<(), AutosarAbstractionError> {
193        self.element()
194            .get_or_create_sub_element(ElementName::DoIpTargetAddressRef)?
195            .set_reference_target(target.element())?;
196        Ok(())
197    }
198
199    /// get the target `DoIpLogicAddress`
200    #[must_use]
201    pub fn target(&self) -> Option<DoIpLogicAddress> {
202        self.element()
203            .get_sub_element(ElementName::DoIpTargetAddressRef)
204            .and_then(|elem| elem.get_reference_target().ok())
205            .and_then(|elem| DoIpLogicAddress::try_from(elem).ok())
206    }
207
208    /// set the `PduTriggering` for this connection
209    pub fn set_tp_sdu_triggering(&self, tp_sdu_triggering: &PduTriggering) -> Result<(), AutosarAbstractionError> {
210        self.element()
211            .get_or_create_sub_element(ElementName::TpSduRef)?
212            .set_reference_target(tp_sdu_triggering.element())?;
213        Ok(())
214    }
215
216    /// get the `PduTriggering` for this connection
217    #[must_use]
218    pub fn tp_sdu_triggering(&self) -> Option<PduTriggering> {
219        self.element()
220            .get_sub_element(ElementName::TpSduRef)
221            .and_then(|elem| elem.get_reference_target().ok())
222            .and_then(|elem| PduTriggering::try_from(elem).ok())
223    }
224}
225
226//##################################################################
227
228/// This element defines the `DoIp` configuration for a specific Ecu
229///
230/// Only available in `Autosar_00048` and later
231#[derive(Debug, Clone, PartialEq, Eq, Hash)]
232pub struct DoIpConfig(Element);
233abstraction_element!(DoIpConfig, DoIpConfig);
234
235//##################################################################
236
237#[cfg(test)]
238mod test {
239    use super::*;
240    use crate::{
241        AutosarModelAbstraction, SystemCategory,
242        communication::{
243            CommunicationDirection, DiagPduType, IPv4AddressSource, NetworkEndpointAddress, SocketAddressType, TpConfig,
244        },
245    };
246    use autosar_data::AutosarVersion;
247
248    #[test]
249    fn test_doip_transport_protocol() {
250        let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
251        let package = model.get_or_create_package("/pkg1").unwrap();
252
253        let system = package.create_system("system", SystemCategory::EcuExtract).unwrap();
254        let eth_cluster = system.create_ethernet_cluster("can_cluster", &package).unwrap();
255        let eth_channel = eth_cluster.create_physical_channel("can_channel", None).unwrap();
256        let ecu_instance = system.create_ecu_instance("ecu_instance", &package).unwrap();
257        let communication_controller = ecu_instance
258            .create_ethernet_communication_controller("can_ctrl", Some("ab:cd:ef:01:02:03".to_string()))
259            .unwrap();
260        let _connector = communication_controller
261            .connect_physical_channel("name", &eth_channel)
262            .unwrap();
263
264        // create socket #1
265        let network_address_1 = NetworkEndpointAddress::IPv4 {
266            address: Some("192.168.0.1".to_string()),
267            address_source: Some(IPv4AddressSource::Fixed),
268            default_gateway: Some("192.168.0.200".to_string()),
269            network_mask: Some("255.255.255.0".to_string()),
270        };
271        let network_endpoint_1 = eth_channel
272            .create_network_endpoint("local_endpoint", network_address_1, None)
273            .unwrap();
274        let udp_port_1 = TpConfig::UdpTp {
275            port_number: Some(1234),
276            port_dynamically_assigned: None,
277        };
278        let socket_type_1 = SocketAddressType::Unicast(Some(ecu_instance.clone()));
279        let socket_address_tcp_1 = eth_channel
280            .create_socket_address("ServerSocket", &network_endpoint_1, &udp_port_1, socket_type_1)
281            .unwrap();
282
283        // ceate socket #2
284        let network_address_2 = NetworkEndpointAddress::IPv4 {
285            address: Some("192.168.0.2".to_string()),
286            address_source: Some(IPv4AddressSource::Fixed),
287            default_gateway: Some("192.168.0.200".to_string()),
288            network_mask: Some("255.255.255.0".to_string()),
289        };
290        let network_endpoint_2 = eth_channel
291            .create_network_endpoint("remote_endpoint", network_address_2, None)
292            .unwrap();
293        let udp_port_2 = TpConfig::UdpTp {
294            port_number: Some(5678),
295            port_dynamically_assigned: None,
296        };
297        let socket_type_2 = SocketAddressType::Unicast(None);
298        let socket_address_tcp_2 = eth_channel
299            .create_socket_address("ClientSocket", &network_endpoint_2, &udp_port_2, socket_type_2)
300            .unwrap();
301
302        // create a connection (V2)
303        let (static_socket_connection_a, static_socket_connection_b) = eth_channel
304            .create_static_socket_connection_pair(
305                "StaticSocketConnection",
306                &socket_address_tcp_1,
307                &socket_address_tcp_2,
308                None,
309            )
310            .unwrap();
311
312        // create a DCM_I_Pdu
313        let dcm_i_pdu = system
314            .create_dcm_ipdu("Diag", &package, 1024, DiagPduType::DiagRequest)
315            .unwrap();
316
317        // create an IPduIdentifier, which is used to map the PDU to both sides of the socket connection
318        let ipdu_identifier_set_package = model.get_or_create_package("/Network/IpduIdentifierSets").unwrap();
319        let socon_ipdu_identifier_set = system
320            .create_socket_connection_ipdu_identifier_set("IpduIdentifierSet", &ipdu_identifier_set_package)
321            .unwrap();
322        let ipdu_identifier = socon_ipdu_identifier_set
323            .create_socon_ipdu_identifier("IpduIdentifier", &dcm_i_pdu, &eth_channel, Some(0x1000), None, None)
324            .unwrap();
325
326        // trigger the DCM_I_Pdu on the connection
327        static_socket_connection_a
328            .add_ipdu_identifier(&ipdu_identifier)
329            .unwrap();
330        static_socket_connection_b
331            .add_ipdu_identifier(&ipdu_identifier)
332            .unwrap();
333        let pdu_triggering = ipdu_identifier.pdu_triggering().unwrap();
334        pdu_triggering
335            .create_pdu_port(&ecu_instance, CommunicationDirection::Out)
336            .unwrap();
337
338        let doip_tp_config = system
339            .create_doip_tp_config("doip_tp_config", &package, &eth_cluster)
340            .unwrap();
341        assert_eq!(doip_tp_config.cluster(), Some(eth_cluster.clone()));
342
343        let doip_logic_address_source = doip_tp_config.create_doip_logic_address("addr_source", 1).unwrap();
344        assert_eq!(doip_logic_address_source.address(), Some(1));
345        let doip_logic_address_target = doip_tp_config.create_doip_logic_address("addr_target", 2).unwrap();
346        assert_eq!(doip_logic_address_target.address(), Some(2));
347
348        let doip_tp_connection = doip_tp_config
349            .create_doip_tp_connection(
350                Some("connection_name"),
351                &doip_logic_address_source,
352                &doip_logic_address_target,
353                &pdu_triggering,
354            )
355            .unwrap();
356        assert_eq!(doip_tp_connection.source(), Some(doip_logic_address_source.clone()));
357        assert_eq!(doip_tp_connection.target(), Some(doip_logic_address_target.clone()));
358        assert_eq!(doip_tp_connection.tp_sdu_triggering(), Some(pdu_triggering.clone()));
359
360        assert_eq!(doip_tp_connection.name().unwrap(), "connection_name");
361        doip_tp_connection.set_name("other_name").unwrap();
362        assert_eq!(doip_tp_connection.name().unwrap(), "other_name");
363        doip_tp_connection
364            .element()
365            .remove_sub_element_kind(ElementName::Ident)
366            .unwrap();
367        assert_eq!(doip_tp_connection.name(), None);
368
369        let doip_tp_connections: Vec<DoIpTpConnection> = doip_tp_config.doip_tp_connections().collect();
370        assert_eq!(doip_tp_connections.len(), 1);
371        assert_eq!(doip_tp_connections[0], doip_tp_connection);
372
373        let doip_logic_addresses: Vec<DoIpLogicAddress> = doip_tp_config.doip_logic_addresses().collect();
374        assert_eq!(doip_logic_addresses.len(), 2);
375        assert_eq!(doip_logic_addresses[0], doip_logic_address_source);
376    }
377}