Skip to main content

NetSim

Struct NetSim 

Source
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: Graph

The graph topology this network is running on.

Implementations§

Source§

impl NetSim

Source

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?
examples/diamond_flow.rs (line 31)
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
Hide additional examples
examples/linear_flow.rs (line 26)
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}
Source

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?
examples/diamond_flow.rs (lines 40-43)
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
Hide additional examples
examples/linear_flow.rs (line 29)
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}
Source

pub fn packet_count_at(&self, location: &PacketLocation) -> usize

Get the number of packets at a given location.

Examples found in repository?
examples/diamond_flow.rs (line 104)
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}
Source

pub fn get_packets_at_location( &self, location: &PacketLocation, ) -> Vec<PacketID>

Get all packets at a given location.

Source

pub fn get_epoch(&self, epoch_id: &EpochID) -> Option<&Epoch>

Get an epoch by ID.

Examples found in repository?
examples/diamond_flow.rs (line 65)
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}
Source

pub fn get_startable_epochs(&self) -> Vec<EpochID>

Get all startable epoch IDs.

Examples found in repository?
examples/diamond_flow.rs (line 57)
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
Hide additional examples
examples/linear_flow.rs (line 61)
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}
Source

pub fn get_packet(&self, packet_id: &PacketID) -> Option<&Packet>

Get a packet by ID.

Source

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?
examples/diamond_flow.rs (line 55)
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
Hide additional examples
examples/linear_flow.rs (line 57)
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}
Source

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
Source

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)?;
}

Trait Implementations§

Source§

impl Debug for NetSim

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for NetSim

§

impl RefUnwindSafe for NetSim

§

impl Send for NetSim

§

impl Sync for NetSim

§

impl Unpin for NetSim

§

impl UnwindSafe for NetSim

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V