diff --git a/srp_analysis/src/aux.rs b/srp_analysis/src/aux.rs index c6bd07425e521cddd0dda6ba7c427aced089af35..e701c35674a1ef2b0af2f76abf5aaabf3d9d9e01 100644 --- a/srp_analysis/src/aux.rs +++ b/srp_analysis/src/aux.rs @@ -11,6 +11,10 @@ pub struct TaskTimes { pub i: u32 // Preumtion time } +pub fn task_load(task: &Task) -> f32 { + return task.get_wcet() as f32 / task.inter_arrival as f32; +} + /// Returns the total load factor, L(t) = C(t) / A(t) (Where C(t) is WCET and A(t) is the inter-arrival time of the task). /// /// See: https://gitlab.henriktjader.com/pln/klee_tutorial/-/blob/master/HOME_EXAM.md#L85 @@ -22,7 +26,7 @@ pub struct TaskTimes { pub fn total_cpu_load(tasks: &Vec<Task>) -> f32 { let mut total_l: f32 = 0.0; for task in tasks { - total_l += task.get_wcet() as f32 / task.inter_arrival as f32; + total_l += task_load(&task); } return total_l; } @@ -150,7 +154,7 @@ pub fn preemption_time(task: &Task, task_set: &Tasks, bp_approx: bool) -> Result let wcet = task.get_wcet(); let block_t = blocking_time(&task, &task_set); - // Give us the first estimate + // Give us the first estimate (initial condition) let mut r_estimate = wcet + block_t; loop { @@ -159,7 +163,7 @@ pub fn preemption_time(task: &Task, task_set: &Tasks, bp_approx: bool) -> Result if prev_r_est > task.deadline { // Our busy period is deviating, break it of - return Err("The busy period deviated....") + return Err("The task is not scheduable given the current task set.") } let mut preemp_t_est = 0; diff --git a/srp_analysis/src/main.rs b/srp_analysis/src/main.rs index b62a48019b5d857ece6902386134af9535c0fb38..9282f2a298fc465a3293c4d9f645f420163e2487 100644 --- a/srp_analysis/src/main.rs +++ b/srp_analysis/src/main.rs @@ -1,15 +1,40 @@ pub mod common; pub mod aux; +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + use std::path::PathBuf; use structopt::StructOpt; +use structopt::clap::arg_enum; use common::*; use aux::*; +/* +enum Example { + POSSIBLE, + IMPOSSIBLE +}*/ + +arg_enum! { + #[derive(Debug)] + enum Example { + POSSIBLE, + IMPOSSIBLE + } +} + /// A basic example #[derive(StructOpt, Debug)] -#[structopt(name = "srp_analysis", about ="asdasdasdasd")] +#[structopt(name = "srp_analysis", about =" +Running +cargo run -- --generate_example -o example.json +cargo run -- --approximate -i example.json +cargo run -- --help +")] struct Opt { // A flag, true if used in the command line. Note doc comment will // be used for the help message of the flag. The name of the @@ -18,49 +43,175 @@ struct Opt { #[structopt(name = "approximate", long)] approx_bp: bool, + // The number of occurrences of the `v/verbose` flag + /// Verbose mode (-v, -vv, -vvv, etc.) + #[structopt(short, long, parse(from_occurrences))] + verbose: u8, + /// Generate an example file #[structopt(long)] // generate: bool, - - + #[structopt(short = "ex", long = "example", possible_values = &Example::variants(), case_insensitive = true, default_value = "possible")] + example: Example, /// Output file for generated example #[structopt(short, long, parse(from_os_str))] output: Option<PathBuf>, - /// Output file for generated example + /// Input file for for task definition #[structopt(short, long, parse(from_os_str), required_unless = "generate")] input: Option<PathBuf>, } - + /* + // Genarate a certain example (schedulable, non-schedulable) + #[structopt(short = "ex", long = "example", default_value = "schedulable")] + example: Example, + */ - // The number of occurrences of the `v/verbose` flag - /// Verbose mode (-v, -vv, -vvv, etc.) - //#[structopt(short, long, parse(from_occurrences))] - //verbose: u8, +fn main() { + let exit_code = real_main(); + std::process::exit(exit_code); +} + +fn real_main() -> i32 { + let opt = Opt::from_args(); + macro_rules! verbose_p { + ($format:expr, $s: expr, $verbose_lvl: expr) => { if opt.verbose >= $verbose_lvl { println!($format,$s) } } + } + + verbose_p!("Parameters: {:?}", opt, 1); + if opt.generate { + let tasks = match opt.example { + Example::POSSIBLE => { + verbose_p!("{}", "Generating a schedulable task set", 1); + schedulable_task_set() + }, + Example::IMPOSSIBLE => { + verbose_p!("{}", "Generating a non-schedulable task set", 1); + non_schedulable_task_set() + } + }; + + // Convert the task vector to a JSON string. + let serialized = serde_json::to_string(&tasks).unwrap(); + + if let Some(output_file) = opt.output { + let path = Path::new(&output_file); + let display = path.display(); + + // Code taken from: https://doc.rust-lang.org/rust-by-example/std_misc/file/create.html + // Open a file in write-only mode, returns `io::Result<File>` + let mut file = match File::create(&path) { + Err(why) => panic!("couldn't create {}: {}", display, why), + Ok(file) => file, + }; + + // Write the serialized data to `file`, returns `io::Result<()>` + match file.write_all(serialized.as_bytes()) { + Err(why) => panic!("couldn't write to {}: {}", display, why), + Ok(_) => println!("successfully wrote example data wrote to: {}", display), + } + + } else { + println!("{}", serialized); + } + + return 0; + } + + let input_file = opt.input.unwrap(); // Let it panic, if it has reached this stage and panics we've fucked up our StructOpt config - /// Set speed - //#[structopt(short, long, default_value = "42")] - //speed: f64, + let serialized_data = fs::read_to_string(input_file).unwrap(); + let tasks: Tasks = serde_json::from_str(&serialized_data).unwrap(); + verbose_p!("Data from file: {}", serialized_data, 2); - // the long option will be translated by default to kebab case, - // i.e. `--nb-cars`. - /// Number of cars - //#[structopt(short = "c", long)] - //nb_cars: Option<i32>, + verbose_p!("Parsed data: {:?}", &tasks, 3); - /// admin_level to consider - //#[structopt(short, long)] - //level: Vec<String>, + let (ip, tr) = pre_analysis(&tasks); - /// Files to process - //#[structopt(name = "FILE", parse(from_os_str))] - //files: Vec<PathBuf>, + verbose_p!("ip: {:?}", ip, 1); + verbose_p!("tr: {:?}", tr, 1); -fn main() { + match opt.approx_bp { + false => { + println!("=== Running analysis (realistic) ==="); + println!("Note: While running without approximation, busy period (Bp(t)) will be computed in relation to all other tasks.\n"); + }, + true => { + println!("=== Running analysis (approximation) ==="); + println!("Note: While running with approximation, busy period (Bp(t)) will be defined as the deadline time (D(t)) of a task.\n"); + } + } + println!("=== Task analysis: results ==="); + for t in tasks.iter() { + println!("-> {:?}", &t.id); + match response_time(&t, &tasks, opt.approx_bp) { + Ok(re) => println!(":: Response time = {:?}", re), + Err(_) => println!(":: Response time = INCONC") + } + match preemption_time(&t, &tasks, opt.approx_bp) { + Ok(re) => println!(":: Preemption time = {:?}", re), + Err(_) => println!(":: Preemption time = INCONC") + } + /* + println!(":: Response time (safe approx) = {:?}", response_time(&t, &tasks, true).unwrap()); + println!(":: Preemption time (safe approx) = {:?}", preemption_time(&t, &tasks, true).unwrap()); + match response_time(&t, &tasks, false) { + Ok(re) => println!(":: Response time (realistic) = {:?}", re), + Err(_) => println!(":: Response time (realistic) = INCONC") + } + match preemption_time(&t, &tasks, false) { + Ok(re) => println!(":: Preemption time (realistic) = {:?}", re), + Err(_) => println!(":: Preemption time (realistic) = INCONC") + } + */ + println!(":: Blocking time = {:?}", blocking_time(&t, &tasks)); + println!(":: WCET = {:?}", t.get_wcet()); + println!(""); + } + + verbose_p!("Combined result = {:?}", times_as_vec(&tasks, false), 1); + + println!("=== Schedulability analysis: results ==="); + println!("-> Loadfactor"); + for task in &tasks { + let t_load = task_load(&task); + if t_load <= 1.0 { + println!("{:?} : {:?} - OK", task.id, t_load); + } else { + println!("{:?} : {:?} - TO HIGH", task.id, t_load); + } + } + let l = total_cpu_load(&tasks); + if l <= 1.0 { + println!("Total loadfactor: {:?} - OK", l) + } else { + println!("Total loadfactor: {:?} - Task set not schedulable", l) + } + println!("-> response time, R(t) < D(t) ?"); + let mut inconc: Vec<String> = vec![]; + for task in &tasks { + match response_time(task, &tasks, opt.approx_bp) { + Ok(re) => println!("{:?} : {:?} - OK", task.id, re), + Err(_) => { + println!("{:?} : INCONC - R(t) >= {:?}", task.id, task.deadline); + inconc.push(task.id.clone().to_string()); + } + } + } + if inconc.len() > 0 || l > 1.0 { + println!("--> Analysis result: Task set NOT schedulable <--"); + return 1; + } else { + println!("--> Analysis result: Task set schedulable <--"); + return 0; + } +} + +fn schedulable_task_set() -> Tasks { let t1 = Task { id: "T1".to_string(), prio: 1, @@ -123,75 +274,71 @@ fn main() { }, }; - // builds a vector of tasks t1, t2, t3 - let tasks: Tasks = vec![t1, t2, t3]; - - // Example for running - // cargo run -- --generate_example -o hej.json - // cargo run -- -i hej.json --approximate - // cargo run -- --help - - let opt = Opt::from_args(); - //println!("{:?}", opt); - if opt.generate { - - // Convert the Point to a JSON string. - let serialized = serde_json::to_string(&tasks).unwrap(); - - // Prints serialized = {"x":1,"y":2} - - - if let Some(output_file) = opt.output { - println!("Saving example file to: {:?}", output_file); - } else { - println!("{}", serialized); - } - // Convert the JSON string back to a Point. - //let deserialized: [Task; 3] = serde_json::from_str(&serialized).unwrap(); - - // Prints deserialized = Point { x: 1, y: 2 } - //println!("deserialized = {:?}", deserialized); - - return; - } - - //let input_file = opt.input.unwrap(); // Let it panic, if it has reached this stage we've fucked up our StructOpt config - - println!("tasks {:?}", &tasks); - // println!("tot_util {}", tot_util(&tasks)); - - let (ip, tr) = pre_analysis(&tasks); - - println!("ip: {:?}", ip); - println!("tr: {:?}", tr); - - let _approximate_bp = false; + return vec![t1,t2,t3]; +} - println!("=== Running analysis ==="); - for t in tasks.iter() { - println!("-> {:?}", &t.id); +fn non_schedulable_task_set() -> Tasks { + let t1 = Task { + id: "T1".to_string(), + prio: 1, + deadline: 100, + inter_arrival: 100, + trace: Trace { + id: "T1".to_string(), + start: 0, + end: 10, + inner: vec![], + }, + }; - println!(":: Response time (safe approx) = {:?}", response_time(&t, &tasks, true).unwrap()); - println!(":: Preemption time (safe approx) = {:?}", preemption_time(&t, &tasks, true).unwrap()); - match response_time(&t, &tasks, false) { - Ok(re) => println!(":: Response time (realistic) = {:?}", re), - Err(_) => println!(":: Response time (realistic) = INCONC") - } - match preemption_time(&t, &tasks, false) { - Ok(re) => println!(":: Preemption time (realistic) = {:?}", re), - Err(_) => println!(":: Preemption time (realistic) = INCONC") - } - println!(":: Blocking time = {:?}", blocking_time(&t, &tasks)); - println!(":: WCET = {:?}", t.get_wcet()); - println!(""); - } + let t2 = Task { + id: "T2".to_string(), + prio: 2, + deadline: 40, + inter_arrival: 100, + trace: Trace { + id: "T2".to_string(), + start: 0, + end: 30, + inner: vec![ + Trace { + id: "R1".to_string(), + start: 10, + end: 20, + inner: vec![Trace { + id: "R2".to_string(), + start: 12, + end: 16, + inner: vec![], + }], + }, + Trace { + id: "R1".to_string(), + start: 22, + end: 28, + inner: vec![], + }, + ], + }, + }; - println!("{:?}", times_as_vec(&tasks, false)); - let l = total_cpu_load(&tasks); - if l <= 1.0 { - println!("Loadfactor: {:?} - OK", l) - } else { - println!("Loadfactor: {:?} - FAILED", l) - } + let t3 = Task { + id: "T3".to_string(), + prio: 3, + deadline: 50, + inter_arrival: 50, + trace: Trace { + id: "T3".to_string(), + start: 0, + end: 30, + inner: vec![Trace { + id: "R2".to_string(), + start: 10, + end: 20, + inner: vec![], + }], + }, + }; -} + return vec![t1,t2,t3]; +} \ No newline at end of file