pub struct NetSim {
pub graph: Graph,
/* private fields */
}Expand description
The runtime state of a flow-based network.
A NetSim is created from a Graph and tracks:
- All packets and their locations
- All epochs and their states
- Which epochs are startable
All mutations must go through NetSim::do_action to ensure proper event tracking.
Fields§
§graph: GraphThe graph topology this network is running on.
Implementations§
Source§impl NetSim
impl NetSim
Sourcepub fn new(graph: Graph) -> Self
pub fn new(graph: Graph) -> Self
Creates a new net simulation from a Graph.
Initializes packet location tracking for all edges and input ports.
Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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}More examples
20fn main() {
21 // Create a linear graph: A -> B -> C
22 let graph = create_linear_graph();
23 println!("Created graph with {} nodes", graph.nodes().len());
24
25 // Create a network from the graph
26 let mut net = NetSim::new(graph);
27
28 // Create a packet outside the network
29 let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
30 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
31 println!("Created packet: {}", id);
32 id
33 }
34 _ => panic!("Failed to create packet"),
35 };
36
37 // Transport packet to the edge A -> B
38 let edge_a_b = PacketLocation::Edge(Edge {
39 source: PortRef {
40 node_name: "A".to_string(),
41 port_type: PortType::Output,
42 port_name: "out".to_string(),
43 },
44 target: PortRef {
45 node_name: "B".to_string(),
46 port_type: PortType::Input,
47 port_name: "in".to_string(),
48 },
49 });
50 net.do_action(&NetAction::TransportPacketToLocation(
51 packet_id.clone(),
52 edge_a_b,
53 ));
54 println!("Placed packet on edge A -> B");
55
56 // Run the network - packet moves to B's input port and triggers an epoch
57 net.run_until_blocked();
58 println!("Ran network until blocked");
59
60 // Check for startable epochs
61 let startable = net.get_startable_epochs();
62 println!("Startable epochs: {}", startable.len());
63
64 if let Some(epoch_id) = startable.first() {
65 // Start the epoch
66 match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
67 NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
68 println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
69
70 // In a real scenario, external code would process the packet here
71 // For this example, we'll just consume it and create an output
72
73 // Consume the input packet
74 net.do_action(&NetAction::ConsumePacket(packet_id));
75 println!("Consumed input packet");
76
77 // Create an output packet
78 let output_packet =
79 match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
80 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
81 _ => panic!("Failed to create output packet"),
82 };
83 println!("Created output packet: {}", output_packet);
84
85 // Load it into the output port
86 net.do_action(&NetAction::LoadPacketIntoOutputPort(
87 output_packet.clone(),
88 "out".to_string(),
89 ));
90 println!("Loaded packet into output port");
91
92 // Send the output salvo
93 net.do_action(&NetAction::SendOutputSalvo(
94 epoch.id.clone(),
95 "default".to_string(),
96 ));
97 println!("Sent output salvo - packet is now on edge B -> C");
98
99 // Finish the epoch
100 net.do_action(&NetAction::FinishEpoch(epoch.id));
101 println!("Finished epoch");
102
103 // Run the network again - packet moves to C
104 net.run_until_blocked();
105 println!("Ran network until blocked again");
106
107 // Check for new startable epochs at C
108 let startable_c = net.get_startable_epochs();
109 println!(
110 "New startable epochs (should be at C): {}",
111 startable_c.len()
112 );
113 }
114 _ => panic!("Failed to start epoch"),
115 }
116 }
117
118 println!("\nLinear flow example complete!");
119}Sourcepub fn do_action(&mut self, action: &NetAction) -> NetActionResponse
pub fn do_action(&mut self, action: &NetAction) -> NetActionResponse
Perform an action on the network.
This is the primary way to mutate the network state. All actions produce a response containing either success data and events, or an error.
§Example
use netrun_sim::net::{NetSim, NetAction, NetActionResponse, NetActionResponseData};
use netrun_sim::graph::{Graph, Node, Port, PortSlotSpec};
use indexmap::IndexMap;
use std::collections::HashMap;
let node = Node {
name: "A".to_string(),
in_ports: HashMap::new(),
out_ports: HashMap::new(),
in_salvo_conditions: IndexMap::new(),
out_salvo_conditions: IndexMap::new(),
};
let graph = Graph::new(vec![node], vec![]);
let mut net = NetSim::new(graph);
// Create a packet outside the network
let response = net.do_action(&NetAction::CreatePacket(None));
match response {
NetActionResponse::Success(NetActionResponseData::Packet(id), events) => {
println!("Created packet {}", id);
}
_ => panic!("Expected success"),
}Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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 // Node A: source with two outputs
124 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 // Node B: one input, one output
147 let node_b = create_simple_node("B");
148
149 // Node C: one input, one output
150 let node_c = create_simple_node("C");
151
152 // Node D: TWO inputs (requires both), no outputs
153 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 // Require BOTH inputs to be non-empty
182 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}More examples
20fn main() {
21 // Create a linear graph: A -> B -> C
22 let graph = create_linear_graph();
23 println!("Created graph with {} nodes", graph.nodes().len());
24
25 // Create a network from the graph
26 let mut net = NetSim::new(graph);
27
28 // Create a packet outside the network
29 let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
30 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
31 println!("Created packet: {}", id);
32 id
33 }
34 _ => panic!("Failed to create packet"),
35 };
36
37 // Transport packet to the edge A -> B
38 let edge_a_b = PacketLocation::Edge(Edge {
39 source: PortRef {
40 node_name: "A".to_string(),
41 port_type: PortType::Output,
42 port_name: "out".to_string(),
43 },
44 target: PortRef {
45 node_name: "B".to_string(),
46 port_type: PortType::Input,
47 port_name: "in".to_string(),
48 },
49 });
50 net.do_action(&NetAction::TransportPacketToLocation(
51 packet_id.clone(),
52 edge_a_b,
53 ));
54 println!("Placed packet on edge A -> B");
55
56 // Run the network - packet moves to B's input port and triggers an epoch
57 net.run_until_blocked();
58 println!("Ran network until blocked");
59
60 // Check for startable epochs
61 let startable = net.get_startable_epochs();
62 println!("Startable epochs: {}", startable.len());
63
64 if let Some(epoch_id) = startable.first() {
65 // Start the epoch
66 match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
67 NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
68 println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
69
70 // In a real scenario, external code would process the packet here
71 // For this example, we'll just consume it and create an output
72
73 // Consume the input packet
74 net.do_action(&NetAction::ConsumePacket(packet_id));
75 println!("Consumed input packet");
76
77 // Create an output packet
78 let output_packet =
79 match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
80 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
81 _ => panic!("Failed to create output packet"),
82 };
83 println!("Created output packet: {}", output_packet);
84
85 // Load it into the output port
86 net.do_action(&NetAction::LoadPacketIntoOutputPort(
87 output_packet.clone(),
88 "out".to_string(),
89 ));
90 println!("Loaded packet into output port");
91
92 // Send the output salvo
93 net.do_action(&NetAction::SendOutputSalvo(
94 epoch.id.clone(),
95 "default".to_string(),
96 ));
97 println!("Sent output salvo - packet is now on edge B -> C");
98
99 // Finish the epoch
100 net.do_action(&NetAction::FinishEpoch(epoch.id));
101 println!("Finished epoch");
102
103 // Run the network again - packet moves to C
104 net.run_until_blocked();
105 println!("Ran network until blocked again");
106
107 // Check for new startable epochs at C
108 let startable_c = net.get_startable_epochs();
109 println!(
110 "New startable epochs (should be at C): {}",
111 startable_c.len()
112 );
113 }
114 _ => panic!("Failed to start epoch"),
115 }
116 }
117
118 println!("\nLinear flow example complete!");
119}Sourcepub fn packet_count_at(&self, location: &PacketLocation) -> usize
pub fn packet_count_at(&self, location: &PacketLocation) -> usize
Get the number of packets at a given location.
Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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}Sourcepub fn get_packets_at_location(
&self,
location: &PacketLocation,
) -> Vec<PacketID> ⓘ
pub fn get_packets_at_location( &self, location: &PacketLocation, ) -> Vec<PacketID> ⓘ
Get all packets at a given location.
Sourcepub fn get_epoch(&self, epoch_id: &EpochID) -> Option<&Epoch>
pub fn get_epoch(&self, epoch_id: &EpochID) -> Option<&Epoch>
Get an epoch by ID.
Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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}Sourcepub fn get_startable_epochs(&self) -> Vec<EpochID> ⓘ
pub fn get_startable_epochs(&self) -> Vec<EpochID> ⓘ
Get all startable epoch IDs.
Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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}More examples
20fn main() {
21 // Create a linear graph: A -> B -> C
22 let graph = create_linear_graph();
23 println!("Created graph with {} nodes", graph.nodes().len());
24
25 // Create a network from the graph
26 let mut net = NetSim::new(graph);
27
28 // Create a packet outside the network
29 let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
30 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
31 println!("Created packet: {}", id);
32 id
33 }
34 _ => panic!("Failed to create packet"),
35 };
36
37 // Transport packet to the edge A -> B
38 let edge_a_b = PacketLocation::Edge(Edge {
39 source: PortRef {
40 node_name: "A".to_string(),
41 port_type: PortType::Output,
42 port_name: "out".to_string(),
43 },
44 target: PortRef {
45 node_name: "B".to_string(),
46 port_type: PortType::Input,
47 port_name: "in".to_string(),
48 },
49 });
50 net.do_action(&NetAction::TransportPacketToLocation(
51 packet_id.clone(),
52 edge_a_b,
53 ));
54 println!("Placed packet on edge A -> B");
55
56 // Run the network - packet moves to B's input port and triggers an epoch
57 net.run_until_blocked();
58 println!("Ran network until blocked");
59
60 // Check for startable epochs
61 let startable = net.get_startable_epochs();
62 println!("Startable epochs: {}", startable.len());
63
64 if let Some(epoch_id) = startable.first() {
65 // Start the epoch
66 match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
67 NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
68 println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
69
70 // In a real scenario, external code would process the packet here
71 // For this example, we'll just consume it and create an output
72
73 // Consume the input packet
74 net.do_action(&NetAction::ConsumePacket(packet_id));
75 println!("Consumed input packet");
76
77 // Create an output packet
78 let output_packet =
79 match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
80 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
81 _ => panic!("Failed to create output packet"),
82 };
83 println!("Created output packet: {}", output_packet);
84
85 // Load it into the output port
86 net.do_action(&NetAction::LoadPacketIntoOutputPort(
87 output_packet.clone(),
88 "out".to_string(),
89 ));
90 println!("Loaded packet into output port");
91
92 // Send the output salvo
93 net.do_action(&NetAction::SendOutputSalvo(
94 epoch.id.clone(),
95 "default".to_string(),
96 ));
97 println!("Sent output salvo - packet is now on edge B -> C");
98
99 // Finish the epoch
100 net.do_action(&NetAction::FinishEpoch(epoch.id));
101 println!("Finished epoch");
102
103 // Run the network again - packet moves to C
104 net.run_until_blocked();
105 println!("Ran network until blocked again");
106
107 // Check for new startable epochs at C
108 let startable_c = net.get_startable_epochs();
109 println!(
110 "New startable epochs (should be at C): {}",
111 startable_c.len()
112 );
113 }
114 _ => panic!("Failed to start epoch"),
115 }
116 }
117
118 println!("\nLinear flow example complete!");
119}Sourcepub fn get_packet(&self, packet_id: &PacketID) -> Option<&Packet>
pub fn get_packet(&self, packet_id: &PacketID) -> Option<&Packet>
Get a packet by ID.
Sourcepub fn run_until_blocked(&mut self) -> Vec<NetEvent>
pub fn run_until_blocked(&mut self) -> Vec<NetEvent>
Run the network until blocked, returning all events that occurred.
This is a convenience method that repeatedly calls RunStep until no more
progress can be made. Equivalent to:
while !net.is_blocked() {
net.do_action(&NetAction::RunStep);
}Examples found in repository?
25fn main() {
26 // Create a diamond graph
27 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 // Create two packets and place them on edges from A
34 let packet1 = create_packet(&mut net);
35 let packet2 = create_packet(&mut net);
36 println!("Created packets: {} and {}", packet1, packet2);
37
38 // Place packet1 on edge A -> B
39 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 // Place packet2 on edge A -> C
47 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 // Run network - packets move to B and C, triggering epochs
55 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 // Process B and C, sending outputs to D
64 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 // Start the epoch
70 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 // Find and consume the input packet
76 let input_packet = started.in_salvo.packets[0].1.clone();
77 net.do_action(&NetAction::ConsumePacket(input_packet));
78
79 // Create output packet
80 let output = create_packet_in_epoch(&mut net, &started.id);
81
82 // Load into output port and send
83 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 // Finish epoch
93 net.do_action(&NetAction::FinishEpoch(started.id));
94 println!(" Finished {} - sent packet to D", node_name);
95 }
96
97 // Run network - packets move from B->D and C->D edges to D's input ports
98 net.run_until_blocked();
99
100 // Check D's input ports
101 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 // D should now have a startable epoch (both inputs present)
108 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}More examples
20fn main() {
21 // Create a linear graph: A -> B -> C
22 let graph = create_linear_graph();
23 println!("Created graph with {} nodes", graph.nodes().len());
24
25 // Create a network from the graph
26 let mut net = NetSim::new(graph);
27
28 // Create a packet outside the network
29 let packet_id = match net.do_action(&NetAction::CreatePacket(None)) {
30 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => {
31 println!("Created packet: {}", id);
32 id
33 }
34 _ => panic!("Failed to create packet"),
35 };
36
37 // Transport packet to the edge A -> B
38 let edge_a_b = PacketLocation::Edge(Edge {
39 source: PortRef {
40 node_name: "A".to_string(),
41 port_type: PortType::Output,
42 port_name: "out".to_string(),
43 },
44 target: PortRef {
45 node_name: "B".to_string(),
46 port_type: PortType::Input,
47 port_name: "in".to_string(),
48 },
49 });
50 net.do_action(&NetAction::TransportPacketToLocation(
51 packet_id.clone(),
52 edge_a_b,
53 ));
54 println!("Placed packet on edge A -> B");
55
56 // Run the network - packet moves to B's input port and triggers an epoch
57 net.run_until_blocked();
58 println!("Ran network until blocked");
59
60 // Check for startable epochs
61 let startable = net.get_startable_epochs();
62 println!("Startable epochs: {}", startable.len());
63
64 if let Some(epoch_id) = startable.first() {
65 // Start the epoch
66 match net.do_action(&NetAction::StartEpoch(epoch_id.clone())) {
67 NetActionResponse::Success(NetActionResponseData::StartedEpoch(epoch), _) => {
68 println!("Started epoch {} on node {}", epoch.id, epoch.node_name);
69
70 // In a real scenario, external code would process the packet here
71 // For this example, we'll just consume it and create an output
72
73 // Consume the input packet
74 net.do_action(&NetAction::ConsumePacket(packet_id));
75 println!("Consumed input packet");
76
77 // Create an output packet
78 let output_packet =
79 match net.do_action(&NetAction::CreatePacket(Some(epoch.id.clone()))) {
80 NetActionResponse::Success(NetActionResponseData::Packet(id), _) => id,
81 _ => panic!("Failed to create output packet"),
82 };
83 println!("Created output packet: {}", output_packet);
84
85 // Load it into the output port
86 net.do_action(&NetAction::LoadPacketIntoOutputPort(
87 output_packet.clone(),
88 "out".to_string(),
89 ));
90 println!("Loaded packet into output port");
91
92 // Send the output salvo
93 net.do_action(&NetAction::SendOutputSalvo(
94 epoch.id.clone(),
95 "default".to_string(),
96 ));
97 println!("Sent output salvo - packet is now on edge B -> C");
98
99 // Finish the epoch
100 net.do_action(&NetAction::FinishEpoch(epoch.id));
101 println!("Finished epoch");
102
103 // Run the network again - packet moves to C
104 net.run_until_blocked();
105 println!("Ran network until blocked again");
106
107 // Check for new startable epochs at C
108 let startable_c = net.get_startable_epochs();
109 println!(
110 "New startable epochs (should be at C): {}",
111 startable_c.len()
112 );
113 }
114 _ => panic!("Failed to start epoch"),
115 }
116 }
117
118 println!("\nLinear flow example complete!");
119}Sourcepub fn is_blocked(&self) -> bool
pub fn is_blocked(&self) -> bool
Check if the network is blocked (no progress can be made by RunStep).
Returns true if:
- No packets can move from edges to input ports (all destinations full or no packets on edges)
- No input salvo conditions can be triggered
Sourcepub fn undo_action(
&mut self,
action: &NetAction,
events: &[NetEvent],
) -> Result<(), UndoError>
pub fn undo_action( &mut self, action: &NetAction, events: &[NetEvent], ) -> Result<(), UndoError>
Undo a previously executed action.
Takes the original action and the events it produced.
Returns Ok(()) on success, or an error if undo is not possible.
§Restrictions
- Actions must be undone in reverse order (LIFO)
- State may have changed since the action (undo may fail)
§Example
let action = NetAction::CreatePacket(None);
let response = net.do_action(&action);
if let NetActionResponse::Success(_, events) = response {
// Later, to undo:
net.undo_action(&action, &events)?;
}