aboutsummaryrefslogtreecommitdiff
path: root/karld/src/schedule.rs
blob: c30fd5c2b1e4288f8975d523425696d66285e963 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
use chrono::Utc;
use karlcommon::{Condition, Schedule};
use log::{info, warn};

use crate::{
    condition::{ConditionFind, Direction},
    TASKS,
};

pub fn schedule_dynamic() {
    info!("starting dynamic scheduling");
    let mut tasks = TASKS.write().unwrap();

    let mut colliders = vec![];
    let mut dynamic = vec![];
    for t in tasks.values_mut() {
        if t.schedule.is_dynamic() {
            dynamic.push(t)
        } else {
            colliders.push(t)
        }
    }

    dynamic.sort_by_key(|t| {
        if let Schedule::Dynamic { priority, .. } = t.schedule {
            // TODO increase precision by using floats
            (priority * 1000.0) as i64
        } else {
            0
        }
    });

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