1use netrun_sim::graph::{
16 Edge, Graph, MaxSalvos, Node, PacketCount, Port, PortRef, PortSlotSpec, PortState, PortType,
17 SalvoCondition, SalvoConditionTerm,
18};
19use netrun_sim::net::{
20 NetAction, NetActionResponse, NetActionResponseData, NetSim, PacketLocation,
21};
22use indexmap::IndexMap;
23use std::collections::HashMap;
24
25fn main() {
26 let graph = create_diamond_graph();
28 println!("Created diamond graph: A -> B,C -> D");
29 println!("D requires inputs from BOTH B and C\n");
30
31 let mut net = NetSim::new(graph);
32
33 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 let edge_a_b = edge_location("A", "out1", "B", "in");
40 net.do_action(&NetAction::TransportPacketToLocation(
41 packet1.clone(),
42 edge_a_b,
43 ));
44 println!("Placed packet1 on edge A -> B");
45
46 let edge_a_c = edge_location("A", "out2", "C", "in");
48 net.do_action(&NetAction::TransportPacketToLocation(
49 packet2.clone(),
50 edge_a_c,
51 ));
52 println!("Placed packet2 on edge A -> C");
53
54 net.run_until_blocked();
56
57 let startable = net.get_startable_epochs();
58 println!(
59 "\nAfter first run: {} startable epochs (B and C)",
60 startable.len()
61 );
62
63 for epoch_id in startable {
65 let epoch = net.get_epoch(&epoch_id).unwrap();
66 let node_name = epoch.node_name.clone();
67 println!("\nProcessing node {}", node_name);
68
69 let started = match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
71 NetActionResponse::Success(NetActionResponseData::StartedEpoch(e), _) => e,
72 _ => panic!("Failed to start epoch"),
73 };
74
75 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 net.do_action(&NetAction::LoadPacketIntoOutputPort(
84 output,
85 "out".to_string(),
86 ));
87 net.do_action(&NetAction::SendOutputSalvo(
88 started.id.clone(),
89 "default".to_string(),
90 ));
91
92 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 net.run_until_blocked();
99
100 let d_in1 = PacketLocation::InputPort("D".to_string(), "in1".to_string());
102 let d_in2 = PacketLocation::InputPort("D".to_string(), "in2".to_string());
103 println!("\nD's input ports:");
104 println!(" in1 (from B): {} packets", net.packet_count_at(&d_in1));
105 println!(" in2 (from C): {} packets", net.packet_count_at(&d_in2));
106
107 let startable_d = net.get_startable_epochs();
109 println!("\nStartable epochs at D: {}", startable_d.len());
110
111 if let Some(d_epoch_id) = startable_d.first() {
112 let d_epoch = net.get_epoch(d_epoch_id).unwrap();
113 println!(
114 "D's epoch received {} packets from both branches!",
115 d_epoch.in_salvo.packets.len()
116 );
117 }
118
119 println!("\nDiamond flow example complete!");
120}
121
122fn create_diamond_graph() -> Graph {
123 let node_a = Node {
125 name: "A".to_string(),
126 in_ports: HashMap::new(),
127 out_ports: [
128 (
129 "out1".to_string(),
130 Port {
131 slots_spec: PortSlotSpec::Infinite,
132 },
133 ),
134 (
135 "out2".to_string(),
136 Port {
137 slots_spec: PortSlotSpec::Infinite,
138 },
139 ),
140 ]
141 .into(),
142 in_salvo_conditions: IndexMap::new(),
143 out_salvo_conditions: IndexMap::new(),
144 };
145
146 let node_b = create_simple_node("B");
148
149 let node_c = create_simple_node("C");
151
152 let node_d = Node {
154 name: "D".to_string(),
155 in_ports: [
156 (
157 "in1".to_string(),
158 Port {
159 slots_spec: PortSlotSpec::Infinite,
160 },
161 ),
162 (
163 "in2".to_string(),
164 Port {
165 slots_spec: PortSlotSpec::Infinite,
166 },
167 ),
168 ]
169 .into(),
170 out_ports: HashMap::new(),
171 in_salvo_conditions: IndexMap::from([(
172 "default".to_string(),
173 SalvoCondition {
174 max_salvos: MaxSalvos::Finite(1),
175 ports: [
176 ("in1".to_string(), PacketCount::All),
177 ("in2".to_string(), PacketCount::All),
178 ]
179 .into_iter()
180 .collect(),
181 term: SalvoConditionTerm::And(vec![
183 SalvoConditionTerm::Port {
184 port_name: "in1".to_string(),
185 state: PortState::NonEmpty,
186 },
187 SalvoConditionTerm::Port {
188 port_name: "in2".to_string(),
189 state: PortState::NonEmpty,
190 },
191 ]),
192 },
193 )]),
194 out_salvo_conditions: IndexMap::new(),
195 };
196
197 let edges = vec![
198 create_edge("A", "out1", "B", "in"),
199 create_edge("A", "out2", "C", "in"),
200 create_edge("B", "out", "D", "in1"),
201 create_edge("C", "out", "D", "in2"),
202 ];
203
204 let graph = Graph::new(vec![node_a, node_b, node_c, node_d], edges);
205 assert!(graph.validate().is_empty(), "Graph validation failed");
206 graph
207}
208
209fn create_simple_node(name: &str) -> Node {
210 Node {
211 name: name.to_string(),
212 in_ports: [(
213 "in".to_string(),
214 Port {
215 slots_spec: PortSlotSpec::Infinite,
216 },
217 )]
218 .into(),
219 out_ports: [(
220 "out".to_string(),
221 Port {
222 slots_spec: PortSlotSpec::Infinite,
223 },
224 )]
225 .into(),
226 in_salvo_conditions: IndexMap::from([(
227 "default".to_string(),
228 SalvoCondition {
229 max_salvos: MaxSalvos::Finite(1),
230 ports: [("in".to_string(), PacketCount::All)].into_iter().collect(),
231 term: SalvoConditionTerm::Port {
232 port_name: "in".to_string(),
233 state: PortState::NonEmpty,
234 },
235 },
236 )]),
237 out_salvo_conditions: IndexMap::from([(
238 "default".to_string(),
239 SalvoCondition {
240 max_salvos: MaxSalvos::Infinite,
241 ports: [("out".to_string(), PacketCount::All)]
242 .into_iter()
243 .collect(),
244 term: SalvoConditionTerm::Port {
245 port_name: "out".to_string(),
246 state: PortState::NonEmpty,
247 },
248 },
249 )]),
250 }
251}
252
253fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
254 Edge {
255 source: PortRef {
256 node_name: src_node.to_string(),
257 port_type: PortType::Output,
258 port_name: src_port.to_string(),
259 },
260 target: PortRef {
261 node_name: tgt_node.to_string(),
262 port_type: PortType::Input,
263 port_name: tgt_port.to_string(),
264 },
265 }
266}
267
268fn edge_location(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> PacketLocation {
269 PacketLocation::Edge(Edge {
270 source: PortRef {
271 node_name: src_node.to_string(),
272 port_type: PortType::Output,
273 port_name: src_port.to_string(),
274 },
275 target: PortRef {
276 node_name: tgt_node.to_string(),
277 port_type: PortType::Input,
278 port_name: tgt_port.to_string(),
279 },
280 })
281}
282
283fn create_packet(net: &mut NetSim) -> ulid::Ulid {
284 match net.do_action(&NetAction::CreatePacket(None)) {
285 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
286 _ => panic!("Failed to create packet"),
287 }
288}
289
290fn create_packet_in_epoch(net: &mut NetSim, epoch_id: &ulid::Ulid) -> ulid::Ulid {
291 match net.do_action(&NetAction::CreatePacket(Some(epoch_id.clone()))) {
292 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
293 _ => panic!("Failed to create packet in epoch"),
294 }
295}