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 pending_port: Option<u8>,
18}
19
20impl RoutePath {
21 const DEFAULT_BACKPLANE_PORT: u8 = 1;
22 const DEFAULT_ETHERNET_PORT: u8 = 2;
23
24 #[must_use]
26 pub fn new() -> Self {
27 Self {
28 hops: Vec::new(),
29 pending_port: None,
30 }
31 }
32
33 #[must_use]
35 pub fn add_slot(mut self, slot: u8) -> Self {
36 self.hops.push(RouteHop::Backplane {
37 port: Self::DEFAULT_BACKPLANE_PORT,
38 slot,
39 });
40 self
41 }
42
43 #[must_use]
50 pub fn add_port(mut self, port: u8) -> Self {
51 if let Some(RouteHop::Ethernet { port: hop_port, .. }) = self
52 .hops
53 .iter_mut()
54 .rev()
55 .find(|hop| matches!(hop, RouteHop::Ethernet { .. }))
56 {
57 *hop_port = port;
58 } else {
59 self.pending_port = Some(port);
60 }
61 self
62 }
63
64 #[must_use]
66 pub fn add_address(mut self, address: String) -> Self {
67 let port = self
68 .pending_port
69 .take()
70 .or_else(|| self.pending_ethernet_port())
71 .unwrap_or(Self::DEFAULT_ETHERNET_PORT);
72 self.hops.push(RouteHop::Ethernet { port, address });
73 self
74 }
75
76 #[must_use]
78 pub fn add_backplane(mut self, port: u8, slot: u8) -> Self {
79 self.hops.push(RouteHop::Backplane { port, slot });
80 self
81 }
82
83 #[must_use]
85 pub fn add_ethernet(self, address: impl Into<String>) -> Self {
86 self.add_ethernet_with_port(Self::DEFAULT_ETHERNET_PORT, address)
87 }
88
89 #[must_use]
91 pub fn add_ethernet_with_port(mut self, port: u8, address: impl Into<String>) -> Self {
92 let address = address.into();
93 self.hops.push(RouteHop::Ethernet { port, address });
94 self
95 }
96
97 #[must_use]
99 pub fn hops(&self) -> &[RouteHop] {
100 &self.hops
101 }
102
103 #[must_use]
105 pub fn slots(&self) -> Vec<u8> {
106 self.hops
107 .iter()
108 .filter_map(|hop| match hop {
109 RouteHop::Backplane { slot, .. } => Some(*slot),
110 RouteHop::Ethernet { .. } => None,
111 })
112 .collect()
113 }
114
115 #[must_use]
117 pub fn ports(&self) -> Vec<u8> {
118 self.hops
119 .iter()
120 .filter_map(|hop| match hop {
121 RouteHop::Backplane { .. } => None,
122 RouteHop::Ethernet { port, .. } => Some(*port),
123 })
124 .collect()
125 }
126
127 #[must_use]
129 pub fn addresses(&self) -> Vec<String> {
130 self.hops
131 .iter()
132 .filter_map(|hop| match hop {
133 RouteHop::Backplane { .. } => None,
134 RouteHop::Ethernet { address, .. } => Some(address.clone()),
135 })
136 .collect()
137 }
138
139 #[must_use]
149 pub fn to_cip_bytes(&self) -> Vec<u8> {
150 let mut path = Vec::new();
151
152 for hop in &self.hops {
153 Self::append_hop(&mut path, hop);
154 }
155
156 path
157 }
158
159 fn append_hop(path: &mut Vec<u8>, hop: &RouteHop) {
160 match hop {
161 RouteHop::Backplane { port, slot } => {
162 path.push(*port);
163 path.push(*slot);
164 }
165 RouteHop::Ethernet { port, address } => {
166 Self::append_extended_link_address_segment(path, *port, address);
167 }
168 }
169 }
170
171 fn append_extended_link_address_segment(path: &mut Vec<u8>, port: u8, address: &str) {
172 path.push(0x10 | (port & 0x0F));
173 path.push(address.len().saturating_add(1) as u8);
174 path.extend_from_slice(address.as_bytes());
175 path.push(0x00);
176 if !(address.len() + 1).is_multiple_of(2) {
177 path.push(0x00);
178 }
179 }
180
181 fn pending_ethernet_port(&self) -> Option<u8> {
182 self.hops
183 .iter()
184 .filter_map(|hop| match hop {
185 RouteHop::Ethernet { port, .. } => Some(*port),
186 RouteHop::Backplane { .. } => None,
187 })
188 .next_back()
189 }
190}
191
192impl Default for RoutePath {
193 fn default() -> Self {
194 Self::new()
195 }
196}