autosar_data_abstraction/communication/physical_channel/ethernet/
networkendpoint.rs1use crate::communication::EthernetPhysicalChannel;
2use crate::{AbstractionElement, AutosarAbstractionError, IdentifiableAbstractionElement, abstraction_element};
3use autosar_data::{CharacterData, Element, ElementName, EnumItem};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct NetworkEndpoint(Element);
8abstraction_element!(NetworkEndpoint, NetworkEndpoint);
9impl IdentifiableAbstractionElement for NetworkEndpoint {}
10
11impl NetworkEndpoint {
12 pub(crate) fn new(
13 name: &str,
14 channel: &EthernetPhysicalChannel,
15 address: NetworkEndpointAddress,
16 ) -> Result<Self, AutosarAbstractionError> {
17 let el_network_endpoint = channel
18 .element()
19 .get_or_create_sub_element(ElementName::NetworkEndpoints)?
20 .create_named_sub_element(ElementName::NetworkEndpoint, name)?;
21
22 let network_endpoint = Self(el_network_endpoint);
23 let result = network_endpoint.add_network_endpoint_address(address);
24 if let Err(error) = result {
25 let _ = channel.element().remove_sub_element(network_endpoint.0);
26 return Err(error);
27 }
28
29 Ok(network_endpoint)
30 }
31
32 pub fn add_network_endpoint_address(&self, address: NetworkEndpointAddress) -> Result<(), AutosarAbstractionError> {
39 let mut fixedcount = 0;
40 if matches!(address, NetworkEndpointAddress::IPv4 { address_source, .. } if address_source == Some(IPv4AddressSource::Fixed))
41 || matches!(address, NetworkEndpointAddress::IPv6 { address_source, .. } if address_source == Some(IPv6AddressSource::Fixed))
42 {
43 fixedcount = 1;
44 }
45 for existing_address in self.addresses() {
46 if std::mem::discriminant(&existing_address) != std::mem::discriminant(&address) {
47 return Err(AutosarAbstractionError::InvalidParameter(
48 "you cannot mix IPv4 and IPv6 inside one NetworkEndpoint".to_string(),
49 ));
50 }
51 if matches!(existing_address, NetworkEndpointAddress::IPv4 { address_source, .. } if address_source == Some(IPv4AddressSource::Fixed))
52 || matches!(existing_address, NetworkEndpointAddress::IPv6 { address_source, .. } if address_source == Some(IPv6AddressSource::Fixed))
53 {
54 fixedcount += 1;
55 }
56 }
57 if fixedcount > 1 {
58 return Err(AutosarAbstractionError::InvalidParameter(
59 "Only one NetworkEndpointAddress can be a fixed address".to_string(),
60 ));
61 }
62
63 let addresses = self
64 .0
65 .get_or_create_sub_element(ElementName::NetworkEndpointAddresses)?;
66 match address {
67 NetworkEndpointAddress::IPv4 {
68 address,
69 address_source,
70 default_gateway,
71 network_mask,
72 } => {
73 let cfg = addresses.create_sub_element(ElementName::Ipv4Configuration)?;
74 if let Some(addr) = address {
75 cfg.create_sub_element(ElementName::Ipv4Address)?
76 .set_character_data(addr)?;
77 }
78 if let Some(addr_src) = address_source {
79 cfg.create_sub_element(ElementName::Ipv4AddressSource)?
80 .set_character_data::<EnumItem>(addr_src.into())?;
81 }
82 if let Some(defgw) = default_gateway {
83 cfg.create_sub_element(ElementName::DefaultGateway)?
84 .set_character_data(defgw)?;
85 }
86 if let Some(netmask) = network_mask {
87 cfg.create_sub_element(ElementName::NetworkMask)?
88 .set_character_data(netmask)?;
89 }
90 }
91 NetworkEndpointAddress::IPv6 {
92 address,
93 address_source,
94 default_router,
95 } => {
96 let cfg = addresses.create_sub_element(ElementName::Ipv6Configuration)?;
97 if let Some(addr) = address {
98 cfg.create_sub_element(ElementName::Ipv6Address)?
99 .set_character_data(addr)?;
100 }
101 if let Some(addr_src) = address_source {
102 cfg.create_sub_element(ElementName::Ipv6AddressSource)?
103 .set_character_data(addr_src.to_cdata())?;
104 }
105 if let Some(dr) = default_router {
106 cfg.create_sub_element(ElementName::DefaultRouter)?
107 .set_character_data(dr)?;
108 }
109 }
110 }
111 Ok(())
112 }
113
114 pub fn addresses(&self) -> impl Iterator<Item = NetworkEndpointAddress> + Send + use<> {
116 self.element()
117 .get_sub_element(ElementName::NetworkEndpointAddresses)
118 .into_iter()
119 .flat_map(|addresses| addresses.sub_elements())
120 .filter_map(|elem| NetworkEndpointAddress::try_from(elem).ok())
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
128pub enum NetworkEndpointAddress {
129 IPv4 {
131 address: Option<String>,
133 address_source: Option<IPv4AddressSource>,
135 default_gateway: Option<String>,
137 network_mask: Option<String>,
139 },
140 IPv6 {
142 address: Option<String>,
144 address_source: Option<IPv6AddressSource>,
146 default_router: Option<String>,
148 },
149}
150
151impl TryFrom<Element> for NetworkEndpointAddress {
152 type Error = AutosarAbstractionError;
153
154 fn try_from(element: Element) -> Result<Self, Self::Error> {
155 match element.element_name() {
156 ElementName::Ipv4Configuration => {
157 let address = element
158 .get_sub_element(ElementName::Ipv4Address)
159 .and_then(|i4a| i4a.character_data())
160 .and_then(|cdata| cdata.string_value());
161 let address_source = element
162 .get_sub_element(ElementName::Ipv4AddressSource)
163 .and_then(|i4as| i4as.character_data())
164 .and_then(IPv4AddressSource::from_cdata);
165 let default_gateway = element
166 .get_sub_element(ElementName::DefaultGateway)
167 .and_then(|dg| dg.character_data())
168 .and_then(|cdata| cdata.string_value());
169 let network_mask = element
170 .get_sub_element(ElementName::NetworkMask)
171 .and_then(|nm| nm.character_data())
172 .and_then(|cdata| cdata.string_value());
173
174 Ok(NetworkEndpointAddress::IPv4 {
175 address,
176 address_source,
177 default_gateway,
178 network_mask,
179 })
180 }
181 ElementName::Ipv6Configuration => {
182 let address = element
183 .get_sub_element(ElementName::Ipv6Address)
184 .and_then(|i6a| i6a.character_data())
185 .and_then(|cdata| cdata.string_value());
186 let address_source = element
187 .get_sub_element(ElementName::Ipv6AddressSource)
188 .and_then(|i6as| i6as.character_data())
189 .and_then(IPv6AddressSource::from_cdata);
190 let default_router = element
191 .get_sub_element(ElementName::DefaultRouter)
192 .and_then(|dr| dr.character_data())
193 .and_then(|cdata| cdata.string_value());
194
195 Ok(NetworkEndpointAddress::IPv6 {
196 address,
197 address_source,
198 default_router,
199 })
200 }
201 _ => Err(AutosarAbstractionError::ConversionError {
202 element,
203 dest: "NetwworkEndpointAddress".to_string(),
204 }),
205 }
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum IPv4AddressSource {
212 AutoIp,
214 AutoIpDoIp,
216 DHCPv4,
218 Fixed,
220}
221
222impl IPv4AddressSource {
223 fn from_cdata(cdata: CharacterData) -> Option<Self> {
224 match cdata {
225 CharacterData::Enum(EnumItem::AutoIp) => Some(Self::AutoIp),
226 CharacterData::Enum(EnumItem::AutoIpDoip) => Some(Self::AutoIpDoIp),
227 CharacterData::Enum(EnumItem::Dhcpv4) => Some(Self::DHCPv4),
228 CharacterData::Enum(EnumItem::Fixed) => Some(Self::Fixed),
229 _ => None,
230 }
231 }
232}
233
234impl From<IPv4AddressSource> for EnumItem {
235 fn from(value: IPv4AddressSource) -> Self {
236 match value {
237 IPv4AddressSource::AutoIp => EnumItem::AutoIp,
238 IPv4AddressSource::AutoIpDoIp => EnumItem::AutoIpDoip,
239 IPv4AddressSource::DHCPv4 => EnumItem::Dhcpv4,
240 IPv4AddressSource::Fixed => EnumItem::Fixed,
241 }
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum IPv6AddressSource {
248 DHCPv6,
250 Fixed,
252 LinkLocal,
254 LinkLocalDoIp,
256 RouterAdvertisement,
258}
259
260impl IPv6AddressSource {
261 fn from_cdata(cdata: CharacterData) -> Option<Self> {
262 match cdata {
263 CharacterData::Enum(EnumItem::Dhcpv6) => Some(Self::DHCPv6),
264 CharacterData::Enum(EnumItem::Fixed) => Some(Self::Fixed),
265 CharacterData::Enum(EnumItem::LinkLocal) => Some(Self::LinkLocal),
266 CharacterData::Enum(EnumItem::LinkLocalDoip) => Some(Self::LinkLocalDoIp),
267 CharacterData::Enum(EnumItem::RouterAdvertisement) => Some(Self::RouterAdvertisement),
268 _ => None,
269 }
270 }
271
272 fn to_cdata(self) -> CharacterData {
273 match self {
274 Self::DHCPv6 => CharacterData::Enum(EnumItem::Dhcpv6),
275 Self::Fixed => CharacterData::Enum(EnumItem::Fixed),
276 Self::LinkLocal => CharacterData::Enum(EnumItem::LinkLocal),
277 Self::LinkLocalDoIp => CharacterData::Enum(EnumItem::LinkLocalDoip),
278 Self::RouterAdvertisement => CharacterData::Enum(EnumItem::RouterAdvertisement),
279 }
280 }
281}
282
283#[cfg(test)]
286mod test {
287 use super::*;
288 use crate::{AutosarModelAbstraction, SystemCategory};
289 use autosar_data::AutosarVersion;
290
291 #[test]
292 fn test_network_endpoint_ipv4() {
293 let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
294 let pkg = model.get_or_create_package("/test").unwrap();
295 let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
296 let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
297 let channel = cluster.create_physical_channel("Channel", None).unwrap();
298
299 let address1 = NetworkEndpointAddress::IPv4 {
301 address: Some("192.168.0.1".to_string()),
302 address_source: Some(IPv4AddressSource::Fixed),
303 default_gateway: Some("192.168.0.2".to_string()),
304 network_mask: Some("255.255.0.0".to_string()),
305 };
306 let network_endpoint = channel
307 .create_network_endpoint("RemoteAddress", address1.clone(), None)
308 .unwrap();
309 assert_eq!(network_endpoint.addresses().count(), 1);
310 assert_eq!(network_endpoint.addresses().next().unwrap(), address1);
311
312 let address2 = NetworkEndpointAddress::IPv4 {
313 address: None,
314 address_source: Some(IPv4AddressSource::AutoIp),
315 default_gateway: None,
316 network_mask: None,
317 };
318 network_endpoint.add_network_endpoint_address(address2).unwrap();
319 assert_eq!(network_endpoint.addresses().count(), 2);
320 }
321
322 #[test]
323 fn test_network_endpoint_ipv6() {
324 let model = AutosarModelAbstraction::create("filename", AutosarVersion::LATEST);
325 let pkg = model.get_or_create_package("/test").unwrap();
326 let system = pkg.create_system("System", SystemCategory::SystemDescription).unwrap();
327 let cluster = system.create_ethernet_cluster("EthCluster", &pkg).unwrap();
328 let channel = cluster.create_physical_channel("Channel", None).unwrap();
329
330 let address1 = NetworkEndpointAddress::IPv6 {
332 address: Some("2001:0db8:0000:0000:0000:0000:0000:0001".to_string()),
333 address_source: Some(IPv6AddressSource::Fixed),
334 default_router: Some("2001:0db8:0000:0000:0000:0000:0000:0002".to_string()),
335 };
336 let network_endpoint = channel
337 .create_network_endpoint("RemoteAddress", address1.clone(), None)
338 .unwrap();
339 assert_eq!(network_endpoint.addresses().count(), 1);
340 assert_eq!(network_endpoint.addresses().next().unwrap(), address1);
341
342 let address2 = NetworkEndpointAddress::IPv6 {
343 address: None,
344 address_source: Some(IPv6AddressSource::LinkLocal),
345 default_router: None,
346 };
347 network_endpoint.add_network_endpoint_address(address2).unwrap();
348 assert_eq!(network_endpoint.addresses().count(), 2);
349 }
350}