rust_ethernet_ip/
route.rs1#[derive(Debug, Clone, PartialEq, Eq)]
3#[non_exhaustive]
4pub enum RouteHop {
5 Backplane { port: u8, slot: u8 },
7 Ethernet { port: u8, address: String },
9}
10
11#[derive(Debug, Clone)]
13pub struct RoutePath {
14 hops: Vec<RouteHop>,
15}
16
17impl RoutePath {
18 const DEFAULT_BACKPLANE_PORT: u8 = 1;
19 const DEFAULT_ETHERNET_PORT: u8 = 2;
20
21 #[must_use]
23 pub fn new() -> Self {
24 Self { hops: Vec::new() }
25 }
26
27 #[must_use]
29 pub fn add_slot(mut self, slot: u8) -> Self {
30 self.hops.push(RouteHop::Backplane {
31 port: Self::DEFAULT_BACKPLANE_PORT,
32 slot,
33 });
34 self
35 }
36
37 #[must_use]
39 pub fn add_port(mut self, port: u8) -> Self {
40 let port_index = self
41 .hops
42 .iter()
43 .filter(|hop| matches!(hop, RouteHop::Ethernet { .. }))
44 .count()
45 .saturating_sub(1);
46 self.update_ethernet_hop_port(port_index, port);
47 self
48 }
49
50 #[must_use]
52 pub fn add_address(mut self, address: String) -> Self {
53 let port = self
54 .pending_ethernet_port()
55 .unwrap_or(Self::DEFAULT_ETHERNET_PORT);
56 self.hops.push(RouteHop::Ethernet { port, address });
57 self
58 }
59
60 #[must_use]
62 pub fn add_backplane(mut self, port: u8, slot: u8) -> Self {
63 self.hops.push(RouteHop::Backplane { port, slot });
64 self
65 }
66
67 #[must_use]
69 pub fn add_ethernet(self, address: impl Into<String>) -> Self {
70 self.add_ethernet_with_port(Self::DEFAULT_ETHERNET_PORT, address)
71 }
72
73 #[must_use]
75 pub fn add_ethernet_with_port(mut self, port: u8, address: impl Into<String>) -> Self {
76 let address = address.into();
77 self.hops.push(RouteHop::Ethernet { port, address });
78 self
79 }
80
81 #[must_use]
83 pub fn hops(&self) -> &[RouteHop] {
84 &self.hops
85 }
86
87 #[must_use]
89 pub fn slots(&self) -> Vec<u8> {
90 self.hops
91 .iter()
92 .filter_map(|hop| match hop {
93 RouteHop::Backplane { slot, .. } => Some(*slot),
94 RouteHop::Ethernet { .. } => None,
95 })
96 .collect()
97 }
98
99 #[must_use]
101 pub fn ports(&self) -> Vec<u8> {
102 self.hops
103 .iter()
104 .filter_map(|hop| match hop {
105 RouteHop::Backplane { .. } => None,
106 RouteHop::Ethernet { port, .. } => Some(*port),
107 })
108 .collect()
109 }
110
111 #[must_use]
113 pub fn addresses(&self) -> Vec<String> {
114 self.hops
115 .iter()
116 .filter_map(|hop| match hop {
117 RouteHop::Backplane { .. } => None,
118 RouteHop::Ethernet { address, .. } => Some(address.clone()),
119 })
120 .collect()
121 }
122
123 #[must_use]
133 pub fn to_cip_bytes(&self) -> Vec<u8> {
134 let mut path = Vec::new();
135
136 for hop in &self.hops {
137 Self::append_hop(&mut path, hop);
138 }
139
140 path
141 }
142
143 fn append_hop(path: &mut Vec<u8>, hop: &RouteHop) {
144 match hop {
145 RouteHop::Backplane { port, slot } => {
146 path.push(*port);
147 path.push(*slot);
148 }
149 RouteHop::Ethernet { port, address } => {
150 Self::append_extended_link_address_segment(path, *port, address);
151 }
152 }
153 }
154
155 fn append_extended_link_address_segment(path: &mut Vec<u8>, port: u8, address: &str) {
156 path.push(0x10 | (port & 0x0F));
157 path.push(address.len().saturating_add(1) as u8);
158 path.extend_from_slice(address.as_bytes());
159 path.push(0x00);
160 if !(address.len() + 1).is_multiple_of(2) {
161 path.push(0x00);
162 }
163 }
164
165 fn update_ethernet_hop_port(&mut self, port_index: usize, port: u8) -> bool {
166 if let Some(RouteHop::Ethernet { port: hop_port, .. }) = self
167 .hops
168 .iter_mut()
169 .filter(|hop| matches!(hop, RouteHop::Ethernet { .. }))
170 .nth(port_index)
171 {
172 *hop_port = port;
173 true
174 } else {
175 false
176 }
177 }
178
179 fn pending_ethernet_port(&self) -> Option<u8> {
180 self.hops
181 .iter()
182 .filter_map(|hop| match hop {
183 RouteHop::Ethernet { port, .. } => Some(*port),
184 RouteHop::Backplane { .. } => None,
185 })
186 .next_back()
187 }
188}
189
190impl Default for RoutePath {
191 fn default() -> Self {
192 Self::new()
193 }
194}