netrun-sim 0.1.0__tar.gz → 0.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/Cargo.lock +0 -64
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/PKG-INFO +1 -1
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/Cargo.toml +3 -4
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/examples/diamond_flow.rs +14 -17
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/examples/linear_flow.rs +14 -17
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/graph.rs +71 -116
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/net.rs +14 -14
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/test_fixtures.rs +4 -5
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/common/mod.rs +4 -5
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/graph_api.rs +1 -54
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/net_api.rs +2 -2
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/tests/workflow.rs +13 -13
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/pyproject.toml +1 -1
- netrun_sim-0.1.1/python/CHANGELOG.md +19 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/Cargo.toml +0 -1
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/README.md +6 -10
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/diamond_flow.ipynb +29 -32
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/diamond_flow.py +5 -9
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/linear_flow.ipynb +10 -13
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/examples/linear_flow.py +5 -9
- {netrun_sim-0.1.0/python → netrun_sim-0.1.1}/python/netrun_sim/__init__.pyi +4 -15
- {netrun_sim-0.1.0 → netrun_sim-0.1.1/python}/python/netrun_sim/__init__.pyi +4 -15
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/errors.rs +2 -2
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/graph.rs +24 -62
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/net.rs +3 -3
- netrun_sim-0.1.0/python/CHANGELOG.md +0 -6
- netrun_sim-0.1.0/python/examples/graph_serialization.ipynb +0 -542
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/Cargo.toml +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/_utils.rs +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/core/src/lib.rs +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/.envrc +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/netrun_sim/__init__.py +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/netrun_sim/py.typed +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/publish_new_version.sh +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/python/netrun_sim/__init__.py +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/python/netrun_sim/py.typed +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/src/lib.rs +0 -0
- {netrun_sim-0.1.0 → netrun_sim-0.1.1}/python/uv.lock +0 -0
|
@@ -90,12 +90,6 @@ dependencies = [
|
|
|
90
90
|
"rustversion",
|
|
91
91
|
]
|
|
92
92
|
|
|
93
|
-
[[package]]
|
|
94
|
-
name = "itoa"
|
|
95
|
-
version = "1.0.16"
|
|
96
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
97
|
-
checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010"
|
|
98
|
-
|
|
99
93
|
[[package]]
|
|
100
94
|
name = "js-sys"
|
|
101
95
|
version = "0.3.83"
|
|
@@ -118,12 +112,6 @@ version = "0.4.15"
|
|
|
118
112
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
119
113
|
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
|
|
120
114
|
|
|
121
|
-
[[package]]
|
|
122
|
-
name = "memchr"
|
|
123
|
-
version = "2.7.6"
|
|
124
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
-
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
|
126
|
-
|
|
127
115
|
[[package]]
|
|
128
116
|
name = "memoffset"
|
|
129
117
|
version = "0.9.1"
|
|
@@ -138,8 +126,6 @@ name = "netrun-sim"
|
|
|
138
126
|
version = "0.1.0"
|
|
139
127
|
dependencies = [
|
|
140
128
|
"indexmap",
|
|
141
|
-
"serde",
|
|
142
|
-
"serde_json",
|
|
143
129
|
"thiserror",
|
|
144
130
|
"ulid",
|
|
145
131
|
"utcnow",
|
|
@@ -151,7 +137,6 @@ version = "0.1.0"
|
|
|
151
137
|
dependencies = [
|
|
152
138
|
"netrun-sim",
|
|
153
139
|
"pyo3",
|
|
154
|
-
"serde_json",
|
|
155
140
|
"ulid",
|
|
156
141
|
]
|
|
157
142
|
|
|
@@ -306,49 +291,6 @@ version = "1.0.22"
|
|
|
306
291
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
307
292
|
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
|
|
308
293
|
|
|
309
|
-
[[package]]
|
|
310
|
-
name = "serde"
|
|
311
|
-
version = "1.0.228"
|
|
312
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
313
|
-
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
314
|
-
dependencies = [
|
|
315
|
-
"serde_core",
|
|
316
|
-
"serde_derive",
|
|
317
|
-
]
|
|
318
|
-
|
|
319
|
-
[[package]]
|
|
320
|
-
name = "serde_core"
|
|
321
|
-
version = "1.0.228"
|
|
322
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
323
|
-
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
324
|
-
dependencies = [
|
|
325
|
-
"serde_derive",
|
|
326
|
-
]
|
|
327
|
-
|
|
328
|
-
[[package]]
|
|
329
|
-
name = "serde_derive"
|
|
330
|
-
version = "1.0.228"
|
|
331
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
332
|
-
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
333
|
-
dependencies = [
|
|
334
|
-
"proc-macro2",
|
|
335
|
-
"quote",
|
|
336
|
-
"syn",
|
|
337
|
-
]
|
|
338
|
-
|
|
339
|
-
[[package]]
|
|
340
|
-
name = "serde_json"
|
|
341
|
-
version = "1.0.147"
|
|
342
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
343
|
-
checksum = "6af14725505314343e673e9ecb7cd7e8a36aa9791eb936235a3567cc31447ae4"
|
|
344
|
-
dependencies = [
|
|
345
|
-
"itoa",
|
|
346
|
-
"memchr",
|
|
347
|
-
"serde",
|
|
348
|
-
"serde_core",
|
|
349
|
-
"zmij",
|
|
350
|
-
]
|
|
351
|
-
|
|
352
294
|
[[package]]
|
|
353
295
|
name = "syn"
|
|
354
296
|
version = "2.0.111"
|
|
@@ -604,9 +546,3 @@ dependencies = [
|
|
|
604
546
|
"quote",
|
|
605
547
|
"syn",
|
|
606
548
|
]
|
|
607
|
-
|
|
608
|
-
[[package]]
|
|
609
|
-
name = "zmij"
|
|
610
|
-
version = "0.1.8"
|
|
611
|
-
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
612
|
-
checksum = "f1dccf46b25b205e4bebe1d5258a991df1cc17801017a845cb5b3fe0269781aa"
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "netrun-sim"
|
|
3
|
+
description = "A flow-based development (FBD) simulation engine."
|
|
3
4
|
version = "0.1.0"
|
|
4
5
|
edition = "2024"
|
|
6
|
+
license-file = "../../LICENSE"
|
|
7
|
+
repository = "https://github.com/lukastk/netrun/"
|
|
5
8
|
|
|
6
9
|
[dependencies]
|
|
7
10
|
indexmap = "2.12.1"
|
|
8
|
-
serde = { version = "1.0", features = ["derive"] }
|
|
9
11
|
thiserror = "2.0"
|
|
10
12
|
ulid = "=1.1.0"
|
|
11
13
|
utcnow = "0.2.7"
|
|
12
|
-
|
|
13
|
-
[dev-dependencies]
|
|
14
|
-
serde_json = "1.0"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//! - Synchronization: D's epoch only triggers when both inputs are present
|
|
14
14
|
|
|
15
15
|
use netrun_sim::graph::{
|
|
16
|
-
Edge,
|
|
16
|
+
Edge, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
|
|
17
17
|
SalvoCondition, SalvoConditionTerm,
|
|
18
18
|
};
|
|
19
19
|
use netrun_sim::net::{
|
|
@@ -190,26 +190,23 @@ fn create_simple_node(name: &str) -> Node {
|
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
192
|
|
|
193
|
-
fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) ->
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
port_name: src_port.to_string(),
|
|
200
|
-
},
|
|
201
|
-
target: PortRef {
|
|
202
|
-
node_name: tgt_node.to_string(),
|
|
203
|
-
port_type: PortType::Input,
|
|
204
|
-
port_name: tgt_port.to_string(),
|
|
205
|
-
},
|
|
193
|
+
fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
|
|
194
|
+
Edge {
|
|
195
|
+
source: PortRef {
|
|
196
|
+
node_name: src_node.to_string(),
|
|
197
|
+
port_type: PortType::Output,
|
|
198
|
+
port_name: src_port.to_string(),
|
|
206
199
|
},
|
|
207
|
-
|
|
208
|
-
|
|
200
|
+
target: PortRef {
|
|
201
|
+
node_name: tgt_node.to_string(),
|
|
202
|
+
port_type: PortType::Input,
|
|
203
|
+
port_name: tgt_port.to_string(),
|
|
204
|
+
},
|
|
205
|
+
}
|
|
209
206
|
}
|
|
210
207
|
|
|
211
208
|
fn edge_location(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> PacketLocation {
|
|
212
|
-
PacketLocation::Edge(
|
|
209
|
+
PacketLocation::Edge(Edge {
|
|
213
210
|
source: PortRef {
|
|
214
211
|
node_name: src_node.to_string(),
|
|
215
212
|
port_type: PortType::Output,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
//! - Sending output salvos to continue flow
|
|
9
9
|
|
|
10
10
|
use netrun_sim::graph::{
|
|
11
|
-
Edge,
|
|
11
|
+
Edge, Graph, Node, Port, PortRef, PortSlotSpec, PortState, PortType,
|
|
12
12
|
SalvoCondition, SalvoConditionTerm,
|
|
13
13
|
};
|
|
14
14
|
use netrun_sim::net::{
|
|
@@ -34,7 +34,7 @@ fn main() {
|
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
// Transport packet to the edge A -> B
|
|
37
|
-
let edge_a_b = PacketLocation::Edge(
|
|
37
|
+
let edge_a_b = PacketLocation::Edge(Edge {
|
|
38
38
|
source: PortRef {
|
|
39
39
|
node_name: "A".to_string(),
|
|
40
40
|
port_type: PortType::Output,
|
|
@@ -174,20 +174,17 @@ fn create_node(name: &str, in_ports: Vec<&str>, out_ports: Vec<&str>) -> Node {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) ->
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
port_name: src_port.to_string(),
|
|
184
|
-
},
|
|
185
|
-
target: PortRef {
|
|
186
|
-
node_name: tgt_node.to_string(),
|
|
187
|
-
port_type: PortType::Input,
|
|
188
|
-
port_name: tgt_port.to_string(),
|
|
189
|
-
},
|
|
177
|
+
fn create_edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
|
|
178
|
+
Edge {
|
|
179
|
+
source: PortRef {
|
|
180
|
+
node_name: src_node.to_string(),
|
|
181
|
+
port_type: PortType::Output,
|
|
182
|
+
port_name: src_port.to_string(),
|
|
190
183
|
},
|
|
191
|
-
|
|
192
|
-
|
|
184
|
+
target: PortRef {
|
|
185
|
+
node_name: tgt_node.to_string(),
|
|
186
|
+
port_type: PortType::Input,
|
|
187
|
+
port_name: tgt_port.to_string(),
|
|
188
|
+
},
|
|
189
|
+
}
|
|
193
190
|
}
|
|
@@ -3,14 +3,13 @@
|
|
|
3
3
|
//! This module defines the static structure of a network: nodes, ports, edges,
|
|
4
4
|
//! and the conditions that govern packet flow (salvo conditions).
|
|
5
5
|
|
|
6
|
-
use serde::{Deserialize, Serialize};
|
|
7
6
|
use std::collections::{HashMap, HashSet};
|
|
8
7
|
|
|
9
8
|
/// The name of a port on a node.
|
|
10
9
|
pub type PortName = String;
|
|
11
10
|
|
|
12
11
|
/// Specifies the capacity of a port (how many packets it can hold).
|
|
13
|
-
#[derive(Debug, Clone
|
|
12
|
+
#[derive(Debug, Clone)]
|
|
14
13
|
pub enum PortSlotSpec {
|
|
15
14
|
/// Port can hold unlimited packets.
|
|
16
15
|
Infinite,
|
|
@@ -19,7 +18,7 @@ pub enum PortSlotSpec {
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
/// A port on a node where packets can enter or exit.
|
|
22
|
-
#[derive(Debug, Clone
|
|
21
|
+
#[derive(Debug, Clone)]
|
|
23
22
|
pub struct Port {
|
|
24
23
|
/// The capacity specification for this port.
|
|
25
24
|
pub slots_spec: PortSlotSpec,
|
|
@@ -29,7 +28,7 @@ pub struct Port {
|
|
|
29
28
|
///
|
|
30
29
|
/// These predicates test the current packet count at a port against
|
|
31
30
|
/// various conditions like empty, full, or numeric comparisons.
|
|
32
|
-
#[derive(Debug, Clone
|
|
31
|
+
#[derive(Debug, Clone)]
|
|
33
32
|
pub enum PortState {
|
|
34
33
|
/// Port has zero packets.
|
|
35
34
|
Empty,
|
|
@@ -55,7 +54,7 @@ pub enum PortState {
|
|
|
55
54
|
///
|
|
56
55
|
/// This forms a simple expression tree that can combine port state checks
|
|
57
56
|
/// with logical operators (And, Or, Not).
|
|
58
|
-
#[derive(Debug, Clone
|
|
57
|
+
#[derive(Debug, Clone)]
|
|
59
58
|
pub enum SalvoConditionTerm {
|
|
60
59
|
/// Check if a specific port matches a state predicate.
|
|
61
60
|
Port { port_name: String, state: PortState },
|
|
@@ -130,7 +129,7 @@ pub type SalvoConditionName = String;
|
|
|
130
129
|
/// Salvo conditions are attached to nodes and control the flow of packets:
|
|
131
130
|
/// - **Input salvo conditions**: Define when packets at input ports can trigger a new epoch
|
|
132
131
|
/// - **Output salvo conditions**: Define when packets at output ports can be sent out
|
|
133
|
-
#[derive(Debug, Clone
|
|
132
|
+
#[derive(Debug, Clone)]
|
|
134
133
|
pub struct SalvoCondition {
|
|
135
134
|
/// Maximum number of times this condition can trigger per epoch.
|
|
136
135
|
/// For input salvo conditions, this must be 1.
|
|
@@ -160,7 +159,7 @@ fn collect_ports_from_term(term: &SalvoConditionTerm, ports: &mut HashSet<PortNa
|
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
/// Errors that can occur during graph validation
|
|
163
|
-
#[derive(Debug, Clone, PartialEq,
|
|
162
|
+
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
|
|
164
163
|
pub enum GraphValidationError {
|
|
165
164
|
/// Edge references a node that doesn't exist
|
|
166
165
|
#[error("edge {edge_source} -> {edge_target} references non-existent node '{missing_node}'")]
|
|
@@ -230,7 +229,7 @@ pub type NodeName = String;
|
|
|
230
229
|
/// - Output ports where packets are sent
|
|
231
230
|
/// - Input salvo conditions that define when arriving packets trigger an epoch
|
|
232
231
|
/// - Output salvo conditions that define when output packets can be sent
|
|
233
|
-
#[derive(Debug, Clone
|
|
232
|
+
#[derive(Debug, Clone)]
|
|
234
233
|
pub struct Node {
|
|
235
234
|
/// The unique name of this node.
|
|
236
235
|
pub name: NodeName,
|
|
@@ -245,7 +244,7 @@ pub struct Node {
|
|
|
245
244
|
}
|
|
246
245
|
|
|
247
246
|
/// Whether a port is for input or output.
|
|
248
|
-
#[derive(Debug, Clone, PartialEq, Eq, Hash
|
|
247
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
249
248
|
pub enum PortType {
|
|
250
249
|
/// An input port (packets flow into the node).
|
|
251
250
|
Input,
|
|
@@ -254,7 +253,7 @@ pub enum PortType {
|
|
|
254
253
|
}
|
|
255
254
|
|
|
256
255
|
/// A reference to a specific port on a specific node.
|
|
257
|
-
#[derive(Debug, Clone, PartialEq, Eq, Hash
|
|
256
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
258
257
|
pub struct PortRef {
|
|
259
258
|
/// The name of the node containing this port.
|
|
260
259
|
pub node_name: NodeName,
|
|
@@ -277,22 +276,16 @@ impl std::fmt::Display for PortRef {
|
|
|
277
276
|
/// A connection between two ports in the graph.
|
|
278
277
|
///
|
|
279
278
|
/// Edges connect output ports to input ports, allowing packets to flow
|
|
280
|
-
/// between nodes.
|
|
281
|
-
|
|
282
|
-
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
279
|
+
/// between nodes.
|
|
280
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
283
281
|
pub struct Edge {
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/// A reference to an edge, identified by its source and target ports.
|
|
287
|
-
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
|
288
|
-
pub struct EdgeRef {
|
|
289
282
|
/// The output port where this edge originates.
|
|
290
283
|
pub source: PortRef,
|
|
291
284
|
/// The input port where this edge terminates.
|
|
292
285
|
pub target: PortRef,
|
|
293
286
|
}
|
|
294
287
|
|
|
295
|
-
impl std::fmt::Display for
|
|
288
|
+
impl std::fmt::Display for Edge {
|
|
296
289
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
297
290
|
write!(f, "{} -> {}", self.source, self.target)
|
|
298
291
|
}
|
|
@@ -307,7 +300,7 @@ impl std::fmt::Display for EdgeRef {
|
|
|
307
300
|
/// # Example
|
|
308
301
|
///
|
|
309
302
|
/// ```
|
|
310
|
-
/// use netrun_sim::graph::{Graph, Node, Edge,
|
|
303
|
+
/// use netrun_sim::graph::{Graph, Node, Edge, PortRef, PortType, Port, PortSlotSpec};
|
|
311
304
|
/// use std::collections::HashMap;
|
|
312
305
|
///
|
|
313
306
|
/// // Create a simple A -> B graph
|
|
@@ -326,74 +319,45 @@ impl std::fmt::Display for EdgeRef {
|
|
|
326
319
|
/// out_salvo_conditions: HashMap::new(),
|
|
327
320
|
/// };
|
|
328
321
|
///
|
|
329
|
-
/// let edge =
|
|
330
|
-
///
|
|
331
|
-
///
|
|
332
|
-
///
|
|
333
|
-
/// },
|
|
334
|
-
/// Edge {},
|
|
335
|
-
/// );
|
|
322
|
+
/// let edge = Edge {
|
|
323
|
+
/// source: PortRef { node_name: "A".to_string(), port_type: PortType::Output, port_name: "out".to_string() },
|
|
324
|
+
/// target: PortRef { node_name: "B".to_string(), port_type: PortType::Input, port_name: "in".to_string() },
|
|
325
|
+
/// };
|
|
336
326
|
///
|
|
337
327
|
/// let graph = Graph::new(vec![node_a, node_b], vec![edge]);
|
|
338
328
|
/// assert!(graph.validate().is_empty());
|
|
339
329
|
/// ```
|
|
340
|
-
#[derive(Debug, Clone
|
|
341
|
-
#[serde(from = "GraphData", into = "GraphData")]
|
|
330
|
+
#[derive(Debug, Clone)]
|
|
342
331
|
pub struct Graph {
|
|
343
332
|
nodes: HashMap<NodeName, Node>,
|
|
344
|
-
edges:
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
edges_by_tail: HashMap<PortRef, EdgeRef>,
|
|
348
|
-
#[serde(skip)]
|
|
349
|
-
edges_by_head: HashMap<PortRef, EdgeRef>,
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/// Helper struct for serializing/deserializing Graph without the indexes
|
|
353
|
-
#[derive(Serialize, Deserialize)]
|
|
354
|
-
struct GraphData {
|
|
355
|
-
nodes: Vec<Node>,
|
|
356
|
-
edges: Vec<(EdgeRef, Edge)>,
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
impl From<GraphData> for Graph {
|
|
360
|
-
fn from(data: GraphData) -> Self {
|
|
361
|
-
Graph::new(data.nodes, data.edges)
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
impl From<Graph> for GraphData {
|
|
366
|
-
fn from(graph: Graph) -> Self {
|
|
367
|
-
GraphData {
|
|
368
|
-
nodes: graph.nodes.into_values().collect(),
|
|
369
|
-
edges: graph.edges.into_iter().collect(),
|
|
370
|
-
}
|
|
371
|
-
}
|
|
333
|
+
edges: HashSet<Edge>,
|
|
334
|
+
edges_by_tail: HashMap<PortRef, Edge>,
|
|
335
|
+
edges_by_head: HashMap<PortRef, Edge>,
|
|
372
336
|
}
|
|
373
337
|
|
|
374
338
|
impl Graph {
|
|
375
339
|
/// Creates a new Graph from a list of nodes and edges.
|
|
376
340
|
///
|
|
377
341
|
/// Builds internal indexes for efficient edge lookups by source (tail) and target (head) ports.
|
|
378
|
-
pub fn new(nodes: Vec<Node>, edges: Vec<
|
|
342
|
+
pub fn new(nodes: Vec<Node>, edges: Vec<Edge>) -> Self {
|
|
379
343
|
let nodes_map: HashMap<NodeName, Node> = nodes
|
|
380
344
|
.into_iter()
|
|
381
345
|
.map(|node| (node.name.clone(), node))
|
|
382
346
|
.collect();
|
|
383
347
|
|
|
384
|
-
let mut
|
|
385
|
-
let mut edges_by_tail: HashMap<PortRef,
|
|
386
|
-
let mut edges_by_head: HashMap<PortRef,
|
|
348
|
+
let mut edges_set: HashSet<Edge> = HashSet::new();
|
|
349
|
+
let mut edges_by_tail: HashMap<PortRef, Edge> = HashMap::new();
|
|
350
|
+
let mut edges_by_head: HashMap<PortRef, Edge> = HashMap::new();
|
|
387
351
|
|
|
388
|
-
for
|
|
389
|
-
edges_by_tail.insert(
|
|
390
|
-
edges_by_head.insert(
|
|
391
|
-
|
|
352
|
+
for edge in edges {
|
|
353
|
+
edges_by_tail.insert(edge.source.clone(), edge.clone());
|
|
354
|
+
edges_by_head.insert(edge.target.clone(), edge.clone());
|
|
355
|
+
edges_set.insert(edge);
|
|
392
356
|
}
|
|
393
357
|
|
|
394
358
|
Graph {
|
|
395
359
|
nodes: nodes_map,
|
|
396
|
-
edges:
|
|
360
|
+
edges: edges_set,
|
|
397
361
|
edges_by_tail,
|
|
398
362
|
edges_by_head,
|
|
399
363
|
}
|
|
@@ -402,16 +366,16 @@ impl Graph {
|
|
|
402
366
|
/// Returns a reference to all nodes in the graph, keyed by name.
|
|
403
367
|
pub fn nodes(&self) -> &HashMap<NodeName, Node> { &self.nodes }
|
|
404
368
|
|
|
405
|
-
/// Returns a reference to all edges in the graph
|
|
406
|
-
pub fn edges(&self) -> &
|
|
369
|
+
/// Returns a reference to all edges in the graph.
|
|
370
|
+
pub fn edges(&self) -> &HashSet<Edge> { &self.edges }
|
|
407
371
|
|
|
408
372
|
/// Returns the edge that has the given output port as its source (tail).
|
|
409
|
-
pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&
|
|
373
|
+
pub fn get_edge_by_tail(&self, output_port_ref: &PortRef) -> Option<&Edge> {
|
|
410
374
|
self.edges_by_tail.get(output_port_ref)
|
|
411
375
|
}
|
|
412
376
|
|
|
413
377
|
/// Returns the edge that has the given input port as its target (head).
|
|
414
|
-
pub fn get_edge_by_head(&self, input_port_ref: &PortRef) -> Option<&
|
|
378
|
+
pub fn get_edge_by_head(&self, input_port_ref: &PortRef) -> Option<&Edge> {
|
|
415
379
|
self.edges_by_head.get(input_port_ref)
|
|
416
380
|
}
|
|
417
381
|
|
|
@@ -425,9 +389,9 @@ impl Graph {
|
|
|
425
389
|
let mut seen_edges: HashSet<(&PortRef, &PortRef)> = HashSet::new();
|
|
426
390
|
|
|
427
391
|
// Validate edges
|
|
428
|
-
for
|
|
429
|
-
let source = &
|
|
430
|
-
let target = &
|
|
392
|
+
for edge in &self.edges {
|
|
393
|
+
let source = &edge.source;
|
|
394
|
+
let target = &edge.target;
|
|
431
395
|
|
|
432
396
|
// Check for duplicate edges
|
|
433
397
|
if !seen_edges.insert((source, target)) {
|
|
@@ -611,22 +575,19 @@ mod tests {
|
|
|
611
575
|
}
|
|
612
576
|
}
|
|
613
577
|
|
|
614
|
-
fn edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) ->
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
port_name: tgt_port.to_string(),
|
|
626
|
-
},
|
|
578
|
+
fn edge(src_node: &str, src_port: &str, tgt_node: &str, tgt_port: &str) -> Edge {
|
|
579
|
+
Edge {
|
|
580
|
+
source: PortRef {
|
|
581
|
+
node_name: src_node.to_string(),
|
|
582
|
+
port_type: PortType::Output,
|
|
583
|
+
port_name: src_port.to_string(),
|
|
584
|
+
},
|
|
585
|
+
target: PortRef {
|
|
586
|
+
node_name: tgt_node.to_string(),
|
|
587
|
+
port_type: PortType::Input,
|
|
588
|
+
port_name: tgt_port.to_string(),
|
|
627
589
|
},
|
|
628
|
-
|
|
629
|
-
)
|
|
590
|
+
}
|
|
630
591
|
}
|
|
631
592
|
|
|
632
593
|
#[test]
|
|
@@ -727,21 +688,18 @@ mod tests {
|
|
|
727
688
|
simple_node("B", vec!["in"], vec![]),
|
|
728
689
|
];
|
|
729
690
|
// Edge from input port (wrong type)
|
|
730
|
-
let edges = vec![
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
port_name: "in".to_string(),
|
|
736
|
-
},
|
|
737
|
-
target: PortRef {
|
|
738
|
-
node_name: "B".to_string(),
|
|
739
|
-
port_type: PortType::Input,
|
|
740
|
-
port_name: "in".to_string(),
|
|
741
|
-
},
|
|
691
|
+
let edges = vec![Edge {
|
|
692
|
+
source: PortRef {
|
|
693
|
+
node_name: "A".to_string(),
|
|
694
|
+
port_type: PortType::Input, // Wrong!
|
|
695
|
+
port_name: "in".to_string(),
|
|
742
696
|
},
|
|
743
|
-
|
|
744
|
-
|
|
697
|
+
target: PortRef {
|
|
698
|
+
node_name: "B".to_string(),
|
|
699
|
+
port_type: PortType::Input,
|
|
700
|
+
port_name: "in".to_string(),
|
|
701
|
+
},
|
|
702
|
+
}];
|
|
745
703
|
let graph = Graph::new(nodes, edges);
|
|
746
704
|
|
|
747
705
|
let errors = graph.validate();
|
|
@@ -755,21 +713,18 @@ mod tests {
|
|
|
755
713
|
simple_node("B", vec!["in"], vec!["out"]),
|
|
756
714
|
];
|
|
757
715
|
// Edge to output port (wrong type)
|
|
758
|
-
let edges = vec![
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
port_name: "out".to_string(),
|
|
769
|
-
},
|
|
716
|
+
let edges = vec![Edge {
|
|
717
|
+
source: PortRef {
|
|
718
|
+
node_name: "A".to_string(),
|
|
719
|
+
port_type: PortType::Output,
|
|
720
|
+
port_name: "out".to_string(),
|
|
721
|
+
},
|
|
722
|
+
target: PortRef {
|
|
723
|
+
node_name: "B".to_string(),
|
|
724
|
+
port_type: PortType::Output, // Wrong!
|
|
725
|
+
port_name: "out".to_string(),
|
|
770
726
|
},
|
|
771
|
-
|
|
772
|
-
)];
|
|
727
|
+
}];
|
|
773
728
|
let graph = Graph::new(nodes, edges);
|
|
774
729
|
|
|
775
730
|
let errors = graph.validate();
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
//! [`NetAction`] and returns a [`NetActionResponse`] containing any events that occurred.
|
|
8
8
|
|
|
9
9
|
use crate::_utils::get_utc_now;
|
|
10
|
-
use crate::graph::{
|
|
10
|
+
use crate::graph::{Edge, Graph, NodeName, Port, PortSlotSpec, PortName, PortType, PortRef, SalvoConditionName, SalvoConditionTerm, evaluate_salvo_condition};
|
|
11
11
|
use indexmap::IndexSet;
|
|
12
12
|
use std::collections::{HashMap, HashSet};
|
|
13
13
|
use ulid::Ulid;
|
|
@@ -34,7 +34,7 @@ pub enum PacketLocation {
|
|
|
34
34
|
/// Loaded into an epoch's output port, ready to be sent.
|
|
35
35
|
OutputPort(EpochID, PortName),
|
|
36
36
|
/// In transit on an edge between nodes.
|
|
37
|
-
Edge(
|
|
37
|
+
Edge(Edge),
|
|
38
38
|
/// External to the network (not yet injected or already extracted).
|
|
39
39
|
OutsideNet,
|
|
40
40
|
}
|
|
@@ -211,8 +211,8 @@ pub enum NetActionError {
|
|
|
211
211
|
CannotMovePacketIntoRunningEpoch { packet_id: PacketID, epoch_id: EpochID },
|
|
212
212
|
|
|
213
213
|
/// Edge does not exist in the graph
|
|
214
|
-
#[error("edge not found: {
|
|
215
|
-
EdgeNotFound {
|
|
214
|
+
#[error("edge not found: {edge}")]
|
|
215
|
+
EdgeNotFound { edge: Edge },
|
|
216
216
|
}
|
|
217
217
|
|
|
218
218
|
/// An event that occurred during a network action.
|
|
@@ -292,8 +292,8 @@ impl Net {
|
|
|
292
292
|
let mut packets_by_location: HashMap<PacketLocation, IndexSet<PacketID>> = HashMap::new();
|
|
293
293
|
|
|
294
294
|
// Initialize empty packet sets for all edges
|
|
295
|
-
for
|
|
296
|
-
packets_by_location.insert(PacketLocation::Edge(
|
|
295
|
+
for edge in graph.edges() {
|
|
296
|
+
packets_by_location.insert(PacketLocation::Edge(edge.clone()), IndexSet::new());
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
// Initialize empty packet sets for all input ports
|
|
@@ -1051,11 +1051,11 @@ impl Net {
|
|
|
1051
1051
|
});
|
|
1052
1052
|
}
|
|
1053
1053
|
}
|
|
1054
|
-
PacketLocation::Edge(
|
|
1054
|
+
PacketLocation::Edge(edge) => {
|
|
1055
1055
|
// Check edge exists in graph
|
|
1056
|
-
if !self.graph.edges().
|
|
1056
|
+
if !self.graph.edges().contains(edge) {
|
|
1057
1057
|
return NetActionResponse::Error(NetActionError::EdgeNotFound {
|
|
1058
|
-
|
|
1058
|
+
edge: edge.clone(),
|
|
1059
1059
|
});
|
|
1060
1060
|
}
|
|
1061
1061
|
}
|
|
@@ -1224,7 +1224,7 @@ mod tests {
|
|
|
1224
1224
|
let packet_id = get_packet_id(&response);
|
|
1225
1225
|
|
|
1226
1226
|
// Manually place packet on edge (simulating it came from node A)
|
|
1227
|
-
let edge_location = PacketLocation::Edge(
|
|
1227
|
+
let edge_location = PacketLocation::Edge(Edge {
|
|
1228
1228
|
source: PortRef {
|
|
1229
1229
|
node_name: "A".to_string(),
|
|
1230
1230
|
port_type: PortType::Output,
|
|
@@ -1341,7 +1341,7 @@ mod tests {
|
|
|
1341
1341
|
|
|
1342
1342
|
// Create packet on edge A->B
|
|
1343
1343
|
let packet_id = get_packet_id(&net.do_action(&NetAction::CreatePacket(None)));
|
|
1344
|
-
let edge_location = PacketLocation::Edge(
|
|
1344
|
+
let edge_location = PacketLocation::Edge(Edge {
|
|
1345
1345
|
source: PortRef {
|
|
1346
1346
|
node_name: "A".to_string(),
|
|
1347
1347
|
port_type: PortType::Output,
|
|
@@ -1391,7 +1391,7 @@ mod tests {
|
|
|
1391
1391
|
let mut net = Net::new(graph);
|
|
1392
1392
|
|
|
1393
1393
|
// Create two packets on edge A->B
|
|
1394
|
-
let edge_location = PacketLocation::Edge(
|
|
1394
|
+
let edge_location = PacketLocation::Edge(Edge {
|
|
1395
1395
|
source: PortRef {
|
|
1396
1396
|
node_name: "A".to_string(),
|
|
1397
1397
|
port_type: PortType::Output,
|
|
@@ -1431,7 +1431,7 @@ mod tests {
|
|
|
1431
1431
|
let graph = linear_graph_3();
|
|
1432
1432
|
let mut net = Net::new(graph);
|
|
1433
1433
|
|
|
1434
|
-
let edge_location = PacketLocation::Edge(
|
|
1434
|
+
let edge_location = PacketLocation::Edge(Edge {
|
|
1435
1435
|
source: PortRef {
|
|
1436
1436
|
node_name: "A".to_string(),
|
|
1437
1437
|
port_type: PortType::Output,
|
|
@@ -1541,7 +1541,7 @@ mod tests {
|
|
|
1541
1541
|
assert!(matches!(response, NetActionResponse::Success(NetActionResponseData::None, _)));
|
|
1542
1542
|
|
|
1543
1543
|
// Packet should now be on edge B->C
|
|
1544
|
-
let edge_loc = PacketLocation::Edge(
|
|
1544
|
+
let edge_loc = PacketLocation::Edge(Edge {
|
|
1545
1545
|
source: PortRef {
|
|
1546
1546
|
node_name: "B".to_string(),
|
|
1547
1547
|
port_type: PortType::Output,
|