rust_ethernet_ip/
route.rs1#[derive(Debug, Clone, PartialEq, Eq)]
3#[non_exhaustive]
4pub enum RouteHop {
5 Backplane {
7 port: u8,
9 slot: u8,
11 },
12 Ethernet {
14 port: u8,
16 address: String,
18 },
19}
20
21#[derive(Debug, Clone)]
23pub struct RoutePath {
24 hops: Vec<RouteHop>,
25 pending_port: Option<u8>,
28}
29
30impl RoutePath {
31 const DEFAULT_BACKPLANE_PORT: u8 = 1;
32 const DEFAULT_ETHERNET_PORT: u8 = 2;
33
34 #[must_use]
36 pub fn new() -> Self {
37 Self {
38 hops: Vec::new(),
39 pending_port: None,
40 }
41 }
42
43 #[must_use]
45 pub fn add_slot(mut self, slot: u8) -> Self {
46 self.hops.push(RouteHop::Backplane {
47 port: Self::DEFAULT_BACKPLANE_PORT,
48 slot,
49 });
50 self
51 }
52
53 #[must_use]
60 pub fn add_port(mut self, port: u8) -> Self {
61 if let Some(RouteHop::Ethernet { port: hop_port, .. }) = self
62 .hops
63 .iter_mut()
64 .rev()
65 .find(|hop| matches!(hop, RouteHop::Ethernet { .. }))
66 {
67 *hop_port = port;
68 } else {
69 self.pending_port = Some(port);
70 }
71 self
72 }
73
74 #[must_use]
76 pub fn add_address(mut self, address: String) -> Self {
77 let port = self
78 .pending_port
79 .take()
80 .or_else(|| self.pending_ethernet_port())
81 .unwrap_or(Self::DEFAULT_ETHERNET_PORT);
82 self.hops.push(RouteHop::Ethernet { port, address });
83 self
84 }
85
86 #[must_use]
88 pub fn add_backplane(mut self, port: u8, slot: u8) -> Self {
89 self.hops.push(RouteHop::Backplane { port, slot });
90 self
91 }
92
93 #[must_use]
95 pub fn add_ethernet(self, address: impl Into<String>) -> Self {
96 self.add_ethernet_with_port(Self::DEFAULT_ETHERNET_PORT, address)
97 }
98
99 #[must_use]
101 pub fn add_ethernet_with_port(mut self, port: u8, address: impl Into<String>) -> Self {
102 let address = address.into();
103 self.hops.push(RouteHop::Ethernet { port, address });
104 self
105 }
106
107 #[must_use]
109 pub fn hops(&self) -> &[RouteHop] {
110 &self.hops
111 }
112
113 #[must_use]
115 pub fn slots(&self) -> Vec<u8> {
116 self.hops
117 .iter()
118 .filter_map(|hop| match hop {
119 RouteHop::Backplane { slot, .. } => Some(*slot),
120 RouteHop::Ethernet { .. } => None,
121 })
122 .collect()
123 }
124
125 #[must_use]
127 pub fn ports(&self) -> Vec<u8> {
128 self.hops
129 .iter()
130 .filter_map(|hop| match hop {
131 RouteHop::Backplane { .. } => None,
132 RouteHop::Ethernet { port, .. } => Some(*port),
133 })
134 .collect()
135 }
136
137 #[must_use]
139 pub fn addresses(&self) -> Vec<String> {
140 self.hops
141 .iter()
142 .filter_map(|hop| match hop {
143 RouteHop::Backplane { .. } => None,
144 RouteHop::Ethernet { address, .. } => Some(address.clone()),
145 })
146 .collect()
147 }
148
149 #[must_use]
159 pub fn to_cip_bytes(&self) -> Vec<u8> {
160 let mut path = Vec::new();
161
162 for hop in &self.hops {
163 Self::append_hop(&mut path, hop);
164 }
165
166 path
167 }
168
169 fn append_hop(path: &mut Vec<u8>, hop: &RouteHop) {
170 match hop {
171 RouteHop::Backplane { port, slot } => {
172 path.push(*port);
173 path.push(*slot);
174 }
175 RouteHop::Ethernet { port, address } => {
176 Self::append_extended_link_address_segment(path, *port, address);
177 }
178 }
179 }
180
181 fn append_extended_link_address_segment(path: &mut Vec<u8>, port: u8, address: &str) {
182 path.push(0x10 | (port & 0x0F));
183 path.push(address.len().saturating_add(1) as u8);
184 path.extend_from_slice(address.as_bytes());
185 path.push(0x00);
186 if !(address.len() + 1).is_multiple_of(2) {
187 path.push(0x00);
188 }
189 }
190
191 fn pending_ethernet_port(&self) -> Option<u8> {
192 self.hops
193 .iter()
194 .filter_map(|hop| match hop {
195 RouteHop::Ethernet { port, .. } => Some(*port),
196 RouteHop::Backplane { .. } => None,
197 })
198 .next_back()
199 }
200}
201
202impl Default for RoutePath {
203 fn default() -> Self {
204 Self::new()
205 }
206}