simple/
simple.rs

1use ns3_parallel::{executor::ConfigFormat, BuildCmd, BuildParam, Executor, ExecutorBuilder};
2use serde::{Deserialize, Serialize};
3
4// This is what you want to read from your configuration file.
5// Each part of the configuration file will be formed into a struct of this.
6// Fields not specified in your config file will use the default value defined in the Default trait.
7#[derive(Debug, Serialize, Deserialize)]
8#[serde(default)]
9pub struct MyConfig {
10    pub sim_time: u32,
11    pub app_name: String,
12    pub policy: Vec<u32>,
13}
14
15// A set of parameters you need to execute ns3 program.
16// One param struct means one ns3 task.
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct MyParam {
19    pub sim_time: u32,
20    pub app_name: String,
21    pub policy: u32,
22}
23
24// This trait impl will give you the default value of your config struct.
25// When a field is not specified in the config file, the default value will be used.
26impl Default for MyConfig {
27    fn default() -> Self {
28        MyConfig {
29            sim_time: 100,
30            app_name: "ns3-tcp-bbr".to_string(),
31            policy: vec![1, 2, 3],
32        }
33    }
34}
35
36// From each of your config struct, you have to generate a vector of param struct.
37impl BuildParam<MyParam> for MyConfig {
38    fn build_param(&self) -> Vec<MyParam> {
39        let mut params: Vec<MyParam> = Vec::new();
40        for policy in &self.policy {
41            let param = MyParam {
42                sim_time: self.sim_time,
43                app_name: self.app_name.clone(),
44                policy: *policy,
45            };
46            params.push(param);
47        }
48        params
49    }
50}
51
52// From each of your param struct, you have to generate the command line passed to ns3 program.
53// The output of method build_cmd will be passed as the argument of "waf --run" in the command line.
54impl BuildCmd for MyParam {
55    fn build_cmd(&self) -> String {
56        format!(
57            "simple-ns3 --app-name={} --sim-time={} --policy={}",
58            self.app_name, self.sim_time, self.policy
59        )
60    }
61}
62
63#[tokio::main]
64async fn main() {
65    // ========== Use toml format as the config file ==========
66    // Use ExecutorBuilder to build your executor.
67    let mut exe: Executor<MyConfig, MyParam> = ExecutorBuilder::new()
68        .config_path("config.toml")
69        .config_format(ConfigFormat::Toml)
70        .ns3_path("ns-allinone-3.33/ns-3.33/")
71        .build()
72        .unwrap();
73
74    // Run your executor.
75    let _ = exe.execute().await.unwrap();
76
77    // Collect your results.
78    let outputs = exe.get_outputs().to_owned();
79
80    // Here I just print all the results, you can do whatever you want with them here.
81    for (_, output) in outputs {
82        for task in output {
83            println!("{}", task.stderr);
84        }
85    }
86
87    // ========== Use ron format as the config file ==========
88    // Use ExecutorBuilder to build your executor.
89    let mut exe: Executor<MyConfig, MyParam> = ExecutorBuilder::new()
90        .config_path("config.ron")
91        .config_format(ConfigFormat::Ron)
92        .ns3_path("ns-allinone-3.33/ns-3.33/")
93        .task_concurrent(4)
94        .retry_limit(2)
95        .build()
96        .unwrap();
97
98    // Run your executor.
99    let _ = exe.execute().await.unwrap();
100
101    // Collect your results.
102    let outputs = exe.get_outputs().to_owned();
103
104    // Here I just print all the results, you can do whatever you want with them here.
105    for (_, output) in outputs {
106        for task in output {
107            println!("{}", task.stderr);
108        }
109    }
110}