autosar_data_abstraction/communication/controller/
ethernet.rs

1use crate::communication::{
2    AbstractCommunicationConnector, AbstractCommunicationController, EthernetPhysicalChannel, EthernetVlanInfo,
3};
4use crate::{
5    AbstractionElement, AutosarAbstractionError, EcuInstance, IdentifiableAbstractionElement, abstraction_element,
6};
7use autosar_data::{AutosarDataError, AutosarModel, Element, ElementName, ElementsIterator, WeakElement};
8
9/// An `EcuInstance` needs an `EthernetCommunicationController` in order to connect to an ethernet cluster.
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub struct EthernetCommunicationController(Element);
12abstraction_element!(EthernetCommunicationController, EthernetCommunicationController);
13impl IdentifiableAbstractionElement for EthernetCommunicationController {}
14
15impl EthernetCommunicationController {
16    // create an EthernetCommunicationController
17    pub(crate) fn new(
18        name: &str,
19        ecu: &EcuInstance,
20        mac_address: Option<String>,
21    ) -> Result<Self, AutosarAbstractionError> {
22        let commcontrollers = ecu.element().get_or_create_sub_element(ElementName::CommControllers)?;
23        let ctrl = commcontrollers.create_named_sub_element(ElementName::EthernetCommunicationController, name)?;
24        let ethccc = ctrl
25            .create_sub_element(ElementName::EthernetCommunicationControllerVariants)?
26            .create_sub_element(ElementName::EthernetCommunicationControllerConditional)?;
27        if let Some(mac_address) = mac_address {
28            // creating the mac address element fails if the supplied string has an invalid format
29            let result = ethccc
30                .create_sub_element(ElementName::MacUnicastAddress)
31                .and_then(|mua| mua.set_character_data(mac_address));
32            if let Err(mac_address_error) = result {
33                let _ = commcontrollers.remove_sub_element(ctrl);
34                return Err(mac_address_error.into());
35            }
36        }
37        let coupling_port_name = format!("{name}_CouplingPort");
38        let _ = ethccc
39            .create_sub_element(ElementName::CouplingPorts)
40            .and_then(|cps| cps.create_named_sub_element(ElementName::CouplingPort, &coupling_port_name));
41
42        Ok(Self(ctrl))
43    }
44
45    /// return an iterator over the [`EthernetPhysicalChannel`]s connected to this controller
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// # use autosar_data::*;
51    /// # use autosar_data_abstraction::*;
52    /// # fn main() -> Result<(), AutosarAbstractionError> {
53    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
54    /// # let package = model.get_or_create_package("/pkg1")?;
55    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
56    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
57    /// let ethernet_controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
58    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
59    /// # let physical_channel = cluster.create_physical_channel("Channel", None)?;
60    /// ethernet_controller.connect_physical_channel("connection", &physical_channel)?;
61    /// for channel in ethernet_controller.connected_channels() {
62    ///     // ...
63    /// }
64    /// # Ok(())}
65    /// ```
66    ///
67    /// # Errors
68    ///
69    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
70    pub fn connected_channels(&self) -> impl Iterator<Item = EthernetPhysicalChannel> + Send + 'static {
71        if let Ok(ecu) = self.ecu_instance().map(|ecuinstance| ecuinstance.element().clone()) {
72            EthernetCtrlChannelsIterator::new(self, &ecu)
73        } else {
74            EthernetCtrlChannelsIterator {
75                connector_iter: None,
76                comm_controller: self.0.clone(),
77                model: None,
78            }
79        }
80    }
81
82    /// Connect this [`EthernetCommunicationController`] inside an [`EcuInstance`] to an [`EthernetPhysicalChannel`] in the [`crate::System`]
83    ///
84    /// Creates an `EthernetCommunicationConnector` in the [`EcuInstance`] that contains this [`EthernetCommunicationController`].
85    ///
86    /// This function establishes the relationships:
87    ///  - [`EthernetPhysicalChannel`] -> `EthernetCommunicationConnector`
88    ///  - `EthernetCommunicationConnector` -> [`EthernetCommunicationController`]
89    ///
90    /// # Example
91    ///
92    /// ```
93    /// # use autosar_data::*;
94    /// # use autosar_data_abstraction::*;
95    /// # fn main() -> Result<(), AutosarAbstractionError> {
96    /// # let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
97    /// # let package = model.get_or_create_package("/pkg1")?;
98    /// # let system = package.create_system("System", SystemCategory::SystemExtract)?;
99    /// # let ecu_instance = system.create_ecu_instance("ecu_name", &package)?;
100    /// let ethernet_controller = ecu_instance.create_ethernet_communication_controller("EthCtrl", None)?;
101    /// # let cluster = system.create_ethernet_cluster("Cluster", &package)?;
102    /// # let physical_channel = cluster.create_physical_channel("Channel", None)?;
103    /// ethernet_controller.connect_physical_channel("connection", &physical_channel)?;
104    /// # Ok(()) }
105    /// ```
106    ///
107    /// # Errors
108    ///
109    /// - [`AutosarAbstractionError::ModelError`] An error occurred in the Autosar model while trying to create the ECU-INSTANCE
110    pub fn connect_physical_channel(
111        &self,
112        connection_name: &str,
113        eth_channel: &EthernetPhysicalChannel,
114    ) -> Result<EthernetCommunicationConnector, AutosarAbstractionError> {
115        let ecu: Element = self.0.named_parent()?.unwrap();
116        let cluster_of_channel = eth_channel.cluster()?;
117
118        // There can be multiple connectors referring to a single EthernetCommunicationController,
119        // but all of these connectors must refer to different PhysicalChannels
120        // (= VLANs) of the same EthernetCluster.
121        for phys_channel in self.connected_channels() {
122            if phys_channel == *eth_channel {
123                return Err(AutosarAbstractionError::ItemAlreadyExists);
124            }
125
126            if phys_channel.cluster()? != cluster_of_channel {
127                return Err(AutosarAbstractionError::InvalidParameter(
128                    "The EthernetCommunicationController may only refer to different channels within the same cluster"
129                        .to_string(),
130                ));
131            }
132        }
133
134        // create a new connector
135        let connectors = ecu.get_or_create_sub_element(ElementName::Connectors)?;
136        let connector = EthernetCommunicationConnector::new(connection_name, &connectors, self)?;
137
138        // if the ethernet physical channel has a category (WIRED / WIRELESS / CANXL) then
139        // set the category of the connector to the same value
140        if let Some(category) = eth_channel
141            .element()
142            .get_sub_element(ElementName::Category)
143            .and_then(|cat| cat.character_data())
144            .and_then(|cdata| cdata.string_value())
145        {
146            let _ = connector
147                .element()
148                .create_sub_element(ElementName::Category)
149                .and_then(|cat| cat.set_character_data(category));
150        }
151
152        // create a communication connector ref in the ethernet channel that refers to this connector
153        let channel_connctor_refs = eth_channel
154            .element()
155            .get_or_create_sub_element(ElementName::CommConnectors)?;
156        channel_connctor_refs
157            .create_sub_element(ElementName::CommunicationConnectorRefConditional)
158            .and_then(|ccrc| ccrc.create_sub_element(ElementName::CommunicationConnectorRef))
159            .and_then(|ccr| ccr.set_reference_target(connector.element()))?;
160
161        // if the PhysicalChannel has VLAN info AND if there is a coupling port in this CommunicationController
162        // then the coupling port should link to the PhysicalChannel / VLAN
163        if let Some(EthernetVlanInfo { .. }) = eth_channel.vlan_info() {
164            if let Some(coupling_port) = self
165                .0
166                .get_sub_element(ElementName::EthernetCommunicationControllerVariants)
167                .and_then(|eccv| eccv.get_sub_element(ElementName::EthernetCommunicationControllerConditional))
168                .and_then(|eccc| eccc.get_sub_element(ElementName::CouplingPorts))
169                .and_then(|cps| cps.get_sub_element(ElementName::CouplingPort))
170            {
171                coupling_port
172                    .get_or_create_sub_element(ElementName::VlanMemberships)
173                    .and_then(|vms| vms.create_sub_element(ElementName::VlanMembership))
174                    .and_then(|vm| vm.create_sub_element(ElementName::VlanRef))
175                    .and_then(|vr| vr.set_reference_target(eth_channel.element()))?;
176            }
177        }
178
179        Ok(connector)
180    }
181}
182
183impl AbstractCommunicationController for EthernetCommunicationController {}
184
185//##################################################################
186
187/// A connector between an [`EthernetCommunicationController`] in an ECU and an [`EthernetPhysicalChannel`]
188#[derive(Debug, Clone, PartialEq, Eq, Hash)]
189pub struct EthernetCommunicationConnector(Element);
190abstraction_element!(EthernetCommunicationConnector, EthernetCommunicationConnector);
191impl IdentifiableAbstractionElement for EthernetCommunicationConnector {}
192
193impl EthernetCommunicationConnector {
194    pub(crate) fn new(
195        name: &str,
196        parent: &Element,
197        controller: &EthernetCommunicationController,
198    ) -> Result<Self, AutosarAbstractionError> {
199        let connector = parent.create_named_sub_element(ElementName::EthernetCommunicationConnector, name)?;
200        connector
201            .create_sub_element(ElementName::CommControllerRef)
202            .and_then(|refelem| refelem.set_reference_target(&controller.0))?;
203        Ok(Self(connector))
204    }
205}
206
207impl AbstractCommunicationConnector for EthernetCommunicationConnector {
208    type CommunicationControllerType = EthernetCommunicationController;
209
210    fn controller(&self) -> Result<Self::CommunicationControllerType, AutosarAbstractionError> {
211        let controller = self
212            .element()
213            .get_sub_element(ElementName::CommControllerRef)
214            .ok_or_else(|| {
215                AutosarAbstractionError::ModelError(AutosarDataError::ElementNotFound {
216                    target: ElementName::CommControllerRef,
217                    parent: self.element().element_name(),
218                })
219            })?
220            .get_reference_target()?;
221        EthernetCommunicationController::try_from(controller)
222    }
223}
224
225//##################################################################
226
227#[doc(hidden)]
228pub struct EthernetCtrlChannelsIterator {
229    connector_iter: Option<ElementsIterator>,
230    comm_controller: Element,
231    model: Option<AutosarModel>,
232}
233
234impl EthernetCtrlChannelsIterator {
235    fn new(controller: &EthernetCommunicationController, ecu: &Element) -> Self {
236        let iter = ecu.get_sub_element(ElementName::Connectors).map(|c| c.sub_elements());
237        let comm_controller = controller.element().clone();
238        let model = comm_controller.model().ok();
239        Self {
240            connector_iter: iter,
241            comm_controller,
242            model,
243        }
244    }
245}
246
247impl Iterator for EthernetCtrlChannelsIterator {
248    type Item = EthernetPhysicalChannel;
249
250    fn next(&mut self) -> Option<Self::Item> {
251        let model = self.model.as_ref()?;
252        let connector_iter = self.connector_iter.as_mut()?;
253        for connector in connector_iter.by_ref() {
254            if connector.element_name() == ElementName::EthernetCommunicationConnector {
255                if let Some(commcontroller_of_connector) = connector
256                    .get_sub_element(ElementName::CommControllerRef)
257                    .and_then(|ccr| ccr.get_reference_target().ok())
258                {
259                    if commcontroller_of_connector == self.comm_controller {
260                        for ref_origin in model
261                            .get_references_to(&connector.path().ok()?)
262                            .iter()
263                            .filter_map(WeakElement::upgrade)
264                            .filter_map(|elem| elem.named_parent().ok().flatten())
265                        {
266                            // This assumes that each connector will only ever be referenced by at most one
267                            // PhysicalChannel, which is true for well-formed files.
268                            if ref_origin.element_name() == ElementName::EthernetPhysicalChannel {
269                                return EthernetPhysicalChannel::try_from(ref_origin).ok();
270                            }
271                        }
272                    }
273                }
274            }
275        }
276        None
277    }
278}
279
280//##################################################################
281
282#[cfg(test)]
283mod test {
284    use super::*;
285    use crate::{AutosarModelAbstraction, SystemCategory, communication::EthernetVlanInfo};
286    use autosar_data::AutosarVersion;
287
288    #[test]
289    fn controller() {
290        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
291        let pkg = model.get_or_create_package("/test").unwrap();
292        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
293        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
294
295        // can't create a controller with an invalid MAC address
296        let result = ecu.create_ethernet_communication_controller("Controller", Some("abcdef".to_string()));
297        assert!(result.is_err());
298
299        // create a controller
300        let result = ecu.create_ethernet_communication_controller("Controller", Some("01:02:03:04:05:06".to_string()));
301        let controller = result.unwrap();
302
303        // create some physical channels
304        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
305        let channel1 = cluster.create_physical_channel("C1", None).unwrap();
306        let vlan_info = EthernetVlanInfo {
307            vlan_name: "VLAN_1".to_string(),
308            vlan_id: 1,
309        };
310        let channel2 = cluster.create_physical_channel("C2", Some(&vlan_info)).unwrap();
311
312        // connect the controller to channel1
313        let connector = controller
314            .connect_physical_channel("connection_name1", &channel1)
315            .unwrap();
316        assert_eq!(connector.controller().unwrap(), controller);
317        // can't connect to the same channel again
318        let result = controller.connect_physical_channel("connection_name2", &channel1);
319        assert!(result.is_err());
320        // connect the controller to channel2
321        let result = controller.connect_physical_channel("connection_name2", &channel2);
322        assert!(result.is_ok());
323
324        // create a different cluster and channel, then try to connect the controller to it
325        let cluster2 = system.create_ethernet_cluster("EthCluster2", &pkg).unwrap();
326        let channel3 = cluster2.create_physical_channel("C3", None).unwrap();
327        let result = controller.connect_physical_channel("connection_name3", &channel3);
328        // can't connect one ethernet controller to channels from different clusters
329        assert!(result.is_err());
330
331        let count = controller.connected_channels().count();
332        assert_eq!(count, 2);
333
334        // remove the controller and try to list its connected channels again
335        let ctrl_parent = controller.element().parent().unwrap().unwrap();
336        ctrl_parent.remove_sub_element(controller.element().clone()).unwrap();
337        let count = controller.connected_channels().count();
338        assert_eq!(count, 0);
339    }
340
341    #[test]
342    fn connector() {
343        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
344        let pkg = model.get_or_create_package("/test").unwrap();
345        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
346        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
347
348        let controller = ecu
349            .create_ethernet_communication_controller("Controller", None)
350            .unwrap();
351        assert_eq!(controller.ecu_instance().unwrap(), ecu);
352
353        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
354        let channel = cluster.create_physical_channel("C1", None).unwrap();
355
356        // create a connector
357        let connector = controller
358            .connect_physical_channel("connection_name", &channel)
359            .unwrap();
360        assert_eq!(connector.controller().unwrap(), controller);
361        assert_eq!(connector.ecu_instance().unwrap(), ecu);
362
363        // remove the connector and try to get the controller from it
364        let conn_parent = connector.element().parent().unwrap().unwrap();
365        conn_parent.remove_sub_element(connector.element().clone()).unwrap();
366        let result = connector.controller();
367        assert!(result.is_err());
368    }
369}