From 09ee50601311c802e67e1f7b0a5278c334d2e406 Mon Sep 17 00:00:00 2001 From: metamuffin Date: Tue, 14 Jun 2022 13:58:09 +0200 Subject: dynamic scheduling --- karlc/src/main.rs | 25 +++++++++++- karlcommon/src/protocol.rs | 4 +- karld/src/condition.rs | 44 +++++++++++++++++++--- karld/src/main.rs | 17 ++++++++- karld/src/schedule.rs | 94 +++++++++++++++++++++++++++++++++++----------- 5 files changed, 153 insertions(+), 31 deletions(-) diff --git a/karlc/src/main.rs b/karlc/src/main.rs index 501ef3c..4501c56 100644 --- a/karlc/src/main.rs +++ b/karlc/src/main.rs @@ -96,7 +96,30 @@ fn main() { print!(" \x1b[38;2;100;255;100mSchedule: \x1b[0m"); match t.schedule { Schedule::Never => println!("\x1b[3m\x1b[2m(never)\x1b[0m"), - Schedule::Dynamic { .. } => todo!(), + Schedule::Dynamic { + duration, + priority, + scheduled, + .. + } => { + // TODO dont ignore condition + println!("dynamicly scheduled",); + println!(" \x1b[38;2;100;255;100mPriority:\x1b[0m {priority}"); + println!( + " \x1b[38;2;100;255;100mDuration:\x1b[0m {:?}", + std::time::Duration::from_secs(duration as u64) + ); + println!( + " \x1b[38;2;100;255;100mScheduled for:\x1b[0m {}", + scheduled + .map(|r| format!( + "{} - {}", + NaiveDateTime::from_timestamp(r.start, 0), + NaiveDateTime::from_timestamp(r.end, 0) + )) + .unwrap_or("...".to_string()) + ); + } Schedule::Static(t) => { println!( "from {} to {}", diff --git a/karlcommon/src/protocol.rs b/karlcommon/src/protocol.rs index d0aad41..02a9db1 100644 --- a/karlcommon/src/protocol.rs +++ b/karlcommon/src/protocol.rs @@ -51,7 +51,7 @@ pub enum Schedule { Never, Dynamic { priority: f64, - scheduled: Option, + scheduled: Option>, duration: i64, condition: Condition, // duration, during which the task should be scheduled }, @@ -91,7 +91,7 @@ pub enum Condition { }, } -#[derive(Debug, Clone, Copy, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum Property { Year, diff --git a/karld/src/condition.rs b/karld/src/condition.rs index 1a9abab..643ca5e 100644 --- a/karld/src/condition.rs +++ b/karld/src/condition.rs @@ -1,6 +1,9 @@ use chrono::{Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike}; use karlcommon::{Condition, Property}; -use std::cmp::{max, min}; +use std::{ + cmp::{max, min}, + ops::Range, +}; use Direction::*; use Edge::*; @@ -40,6 +43,12 @@ pub trait ConditionFind { (None, None) => None, } } + + fn find_instance(&self, dir: Direction, from: NaiveDateTime) -> Range> { + let start = self.find(Edge::Start, dir, from); + let end = self.find(Edge::End, dir, start.unwrap_or(from)); + start..end + } } impl ConditionFind for Condition { @@ -240,11 +249,34 @@ impl ConditionFind for Condition { } } Condition::Range { - prop: _, - min: _, - max: _, - modulus: _, - } => todo!(), + prop, + min, + max, + modulus, + } => { + // TODO + assert_eq!(*modulus, None); + assert_eq!(*prop, Property::Unix); + assert_eq!(dir, Direction::Forward); + let min = NaiveDateTime::from_timestamp(*min, 0); + let max = NaiveDateTime::from_timestamp(*max, 0); + match edge { + Start => { + if min < from { + None + } else { + Some(min) + } + } + End => { + if max < from { + None + } else { + Some(max) + } + } + } + } } } } diff --git a/karld/src/main.rs b/karld/src/main.rs index 24f70a3..414b1e6 100644 --- a/karld/src/main.rs +++ b/karld/src/main.rs @@ -88,7 +88,7 @@ fn main() { schedule: Schedule::Dynamic { scheduled: None, duration: 15 * 60, - priority: 1.0, + priority: 2.0, condition: Condition::Equal { prop: Property::Monthofyear, value: 6, @@ -97,6 +97,21 @@ fn main() { }, }, ); + TASKS.write().unwrap().insert( + 4, + Task { + id: 4, + description: Some("sollte ich wirklich mal machen".to_string()), + name: "Geschirrspüler ausräumen".to_string(), + tags: vec!["Unwichtig".to_string()], + schedule: Schedule::Dynamic { + scheduled: None, + duration: 15 * 60, + priority: 5.0, + condition: Condition::Never, + }, + }, + ); std::thread::spawn(move || { std::thread::sleep(std::time::Duration::from_secs_f64(0.1)); diff --git a/karld/src/schedule.rs b/karld/src/schedule.rs index 971c63a..c30fd5c 100644 --- a/karld/src/schedule.rs +++ b/karld/src/schedule.rs @@ -1,8 +1,14 @@ +use chrono::Utc; use karlcommon::{Condition, Schedule}; +use log::{info, warn}; -use crate::TASKS; +use crate::{ + condition::{ConditionFind, Direction}, + TASKS, +}; pub fn schedule_dynamic() { + info!("starting dynamic scheduling"); let mut tasks = TASKS.write().unwrap(); let mut colliders = vec![]; @@ -24,25 +30,71 @@ pub fn schedule_dynamic() { } }); - let cond = Condition::Invert(box Condition::Or( - colliders - .iter() - .map(|c| match &c.schedule { - Schedule::Never => Condition::Never, - Schedule::Condition(c) => c.clone(), - Schedule::Static(r) => Condition::Range { - min: r.start, - max: r.end, - modulus: None, - prop: karlcommon::Property::Unix, - }, - Schedule::Dynamic { .. } => unreachable!(), - }) - .collect(), - )); - println!("{:?}", cond); - - while dynamic.len() != 0 { - todo!() + let now = Utc::now().naive_local(); + while let Some(t) = dynamic.pop() { + let props = match &t.schedule { + Schedule::Dynamic { + duration, + condition, + priority, + scheduled, + } => (duration, condition, priority, scheduled), + _ => unreachable!(), + }; + + let duration = + chrono::Duration::from_std(std::time::Duration::from_secs(*props.0 as u64)).unwrap(); + + let cond = Condition::Invert(box Condition::Or( + colliders + .iter() + .map(|c| match &c.schedule { + Schedule::Never => Condition::Never, + Schedule::Condition(c) => c.clone(), + Schedule::Static(r) => Condition::Range { + min: r.start, + max: r.end, + modulus: None, + prop: karlcommon::Property::Unix, + }, + Schedule::Dynamic { scheduled, .. } => match scheduled { + Some(r) => Condition::Range { + min: r.start, + max: r.end, + modulus: None, + prop: karlcommon::Property::Unix, + }, + None => Condition::Never, + }, + }) + .collect(), + )); + + let mut cursor = now; + + let mut found_spot = false; + for _ in 0..256 { + // TODO actually respect the condition and repeat + let free = cond.find_instance(Direction::Forward, cursor); + let p_start = free.start.unwrap_or(now); + let p_end = p_start + duration; + if free.end.map(|e| e >= p_end).unwrap_or(true) { + t.schedule = Schedule::Dynamic { + condition: props.1.clone(), + duration: *props.0, + priority: *props.2, + scheduled: Some(p_start.timestamp()..p_end.timestamp()), + }; + colliders.push(t); + found_spot = true; + break; + } + cursor = free.end.unwrap() + } + if !found_spot { + warn!("could not find a spot where the task fits in"); + } } + drop(tasks); + info!("done"); } -- cgit v1.2.3-70-g09d2