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 + use<> {
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            && 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        Ok(connector)
179    }
180}
181
182impl AbstractCommunicationController for EthernetCommunicationController {}
183
184//##################################################################
185
186/// A connector between an [`EthernetCommunicationController`] in an ECU and an [`EthernetPhysicalChannel`]
187#[derive(Debug, Clone, PartialEq, Eq, Hash)]
188pub struct EthernetCommunicationConnector(Element);
189abstraction_element!(EthernetCommunicationConnector, EthernetCommunicationConnector);
190impl IdentifiableAbstractionElement for EthernetCommunicationConnector {}
191
192impl EthernetCommunicationConnector {
193    pub(crate) fn new(
194        name: &str,
195        parent: &Element,
196        controller: &EthernetCommunicationController,
197    ) -> Result<Self, AutosarAbstractionError> {
198        let connector = parent.create_named_sub_element(ElementName::EthernetCommunicationConnector, name)?;
199        connector
200            .create_sub_element(ElementName::CommControllerRef)
201            .and_then(|refelem| refelem.set_reference_target(&controller.0))?;
202        Ok(Self(connector))
203    }
204}
205
206impl AbstractCommunicationConnector for EthernetCommunicationConnector {
207    type CommunicationControllerType = EthernetCommunicationController;
208
209    fn controller(&self) -> Result<Self::CommunicationControllerType, AutosarAbstractionError> {
210        let controller = self
211            .element()
212            .get_sub_element(ElementName::CommControllerRef)
213            .ok_or_else(|| {
214                AutosarAbstractionError::ModelError(AutosarDataError::ElementNotFound {
215                    target: ElementName::CommControllerRef,
216                    parent: self.element().element_name(),
217                })
218            })?
219            .get_reference_target()?;
220        EthernetCommunicationController::try_from(controller)
221    }
222}
223
224//##################################################################
225
226#[doc(hidden)]
227pub struct EthernetCtrlChannelsIterator {
228    connector_iter: Option<ElementsIterator>,
229    comm_controller: Element,
230    model: Option<AutosarModel>,
231}
232
233impl EthernetCtrlChannelsIterator {
234    fn new(controller: &EthernetCommunicationController, ecu: &Element) -> Self {
235        let iter = ecu.get_sub_element(ElementName::Connectors).map(|c| c.sub_elements());
236        let comm_controller = controller.element().clone();
237        let model = comm_controller.model().ok();
238        Self {
239            connector_iter: iter,
240            comm_controller,
241            model,
242        }
243    }
244}
245
246impl Iterator for EthernetCtrlChannelsIterator {
247    type Item = EthernetPhysicalChannel;
248
249    fn next(&mut self) -> Option<Self::Item> {
250        let model = self.model.as_ref()?;
251        let connector_iter = self.connector_iter.as_mut()?;
252        for connector in connector_iter.by_ref() {
253            if connector.element_name() == ElementName::EthernetCommunicationConnector
254                && let Some(commcontroller_of_connector) = connector
255                    .get_sub_element(ElementName::CommControllerRef)
256                    .and_then(|ccr| ccr.get_reference_target().ok())
257                && commcontroller_of_connector == self.comm_controller
258            {
259                for ref_origin in model
260                    .get_references_to(&connector.path().ok()?)
261                    .iter()
262                    .filter_map(WeakElement::upgrade)
263                    .filter_map(|elem| elem.named_parent().ok().flatten())
264                {
265                    // This assumes that each connector will only ever be referenced by at most one
266                    // PhysicalChannel, which is true for well-formed files.
267                    if ref_origin.element_name() == ElementName::EthernetPhysicalChannel {
268                        return EthernetPhysicalChannel::try_from(ref_origin).ok();
269                    }
270                }
271            }
272        }
273        None
274    }
275}
276
277//##################################################################
278
279#[cfg(test)]
280mod test {
281    use super::*;
282    use crate::{AutosarModelAbstraction, SystemCategory, communication::EthernetVlanInfo};
283    use autosar_data::AutosarVersion;
284
285    #[test]
286    fn controller() {
287        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
288        let pkg = model.get_or_create_package("/test").unwrap();
289        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
290        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
291
292        // can't create a controller with an invalid MAC address
293        let result = ecu.create_ethernet_communication_controller("Controller", Some("abcdef".to_string()));
294        assert!(result.is_err());
295
296        // create a controller
297        let result = ecu.create_ethernet_communication_controller("Controller", Some("01:02:03:04:05:06".to_string()));
298        let controller = result.unwrap();
299
300        // create some physical channels
301        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
302        let channel1 = cluster.create_physical_channel("C1", None).unwrap();
303        let vlan_info = EthernetVlanInfo {
304            vlan_name: "VLAN_1".to_string(),
305            vlan_id: 1,
306        };
307        let channel2 = cluster.create_physical_channel("C2", Some(&vlan_info)).unwrap();
308
309        // connect the controller to channel1
310        let connector = controller
311            .connect_physical_channel("connection_name1", &channel1)
312            .unwrap();
313        assert_eq!(connector.controller().unwrap(), controller);
314        // can't connect to the same channel again
315        let result = controller.connect_physical_channel("connection_name2", &channel1);
316        assert!(result.is_err());
317        // connect the controller to channel2
318        let result = controller.connect_physical_channel("connection_name2", &channel2);
319        assert!(result.is_ok());
320
321        // create a different cluster and channel, then try to connect the controller to it
322        let cluster2 = system.create_ethernet_cluster("EthCluster2", &pkg).unwrap();
323        let channel3 = cluster2.create_physical_channel("C3", None).unwrap();
324        let result = controller.connect_physical_channel("connection_name3", &channel3);
325        // can't connect one ethernet controller to channels from different clusters
326        assert!(result.is_err());
327
328        let count = controller.connected_channels().count();
329        assert_eq!(count, 2);
330
331        // remove the controller and try to list its connected channels again
332        let ctrl_parent = controller.element().parent().unwrap().unwrap();
333        ctrl_parent.remove_sub_element(controller.element().clone()).unwrap();
334        let count = controller.connected_channels().count();
335        assert_eq!(count, 0);
336    }
337
338    #[test]
339    fn connector() {
340        let model = AutosarModelAbstraction::create("filename", AutosarVersion::Autosar_00048);
341        let pkg = model.get_or_create_package("/test").unwrap();
342        let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
343        let ecu = system.create_ecu_instance("ECU", &pkg).unwrap();
344
345        let controller = ecu
346            .create_ethernet_communication_controller("Controller", None)
347            .unwrap();
348        assert_eq!(controller.ecu_instance().unwrap(), ecu);
349
350        let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
351        let channel = cluster.create_physical_channel("C1", None).unwrap();
352
353        // create a connector
354        let connector = controller
355            .connect_physical_channel("connection_name", &channel)
356            .unwrap();
357        assert_eq!(connector.controller().unwrap(), controller);
358        assert_eq!(connector.ecu_instance().unwrap(), ecu);
359
360        // remove the connector and try to get the controller from it
361        let conn_parent = connector.element().parent().unwrap().unwrap();
362        conn_parent.remove_sub_element(connector.element().clone()).unwrap();
363        let result = connector.controller();
364        assert!(result.is_err());
365    }
366}