use petgraph::graphmap::DiGraphMap;
use petgraph::Direction::{Incoming, Outgoing};
use std::collections::BTreeMap;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use super::algorithms::floyd_warshall;
use super::interval::Interval;
pub type EventID = i32;
#[wasm_bindgen]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
pub struct Episode(pub EventID, pub EventID);
#[wasm_bindgen]
impl Episode {
#[wasm_bindgen(getter)]
pub fn start(&self) -> EventID {
self.0
}
#[wasm_bindgen(getter)]
pub fn end(&self) -> EventID {
self.1
}
}
#[wasm_bindgen]
#[derive(Debug, Default)]
pub struct Schedule {
stn: DiGraphMap<EventID, f64>,
dispatchable: DiGraphMap<EventID, f64>,
execution_windows: BTreeMap<EventID, Interval>,
committments: BTreeMap<EventID, f64>,
dirty: bool,
}
#[wasm_bindgen]
impl Schedule {
#[wasm_bindgen(constructor)]
pub fn new() -> Schedule {
Schedule {
dirty: true,
..Default::default()
}
}
#[wasm_bindgen(getter)]
pub fn root(&mut self) -> Option<EventID> {
match self.compile() {
Ok(_) => (),
Err(_e) => return None,
};
self.dispatchable.nodes().find(|s| {
self.dispatchable
.neighbors_directed(*s, petgraph::Incoming)
.all(|t| match self.dispatchable.edge_weight(t, *s) {
Some(w) => *w <= 0.,
None => false,
})
})
}
pub fn order(&self) -> Vec<EventID> {
vec![0]
}
#[wasm_bindgen(js_name = createEvent)]
pub fn create_event(&mut self) -> EventID {
let event_id = self.stn.node_count() as i32;
self.execution_windows
.insert(event_id, Interval(-std::f64::MAX, std::f64::MAX));
let n = self.stn.add_node(event_id);
self.dirty = true;
n
}
fn new_episode(&mut self) -> Episode {
let start_id = self.create_event();
let end_id = self.create_event();
Episode(start_id, end_id)
}
#[wasm_bindgen(catch, js_name = addEpisode)]
pub fn add_episode(&mut self, duration: Option<Vec<f64>>) -> Episode {
let d = duration.unwrap_or(vec![0., 0.]);
let i = Interval::from_vec(d);
let episode = self.new_episode();
self.stn.add_edge(episode.0, episode.1, i.upper());
self.stn.add_edge(episode.1, episode.0, -i.lower());
self.dirty = true;
episode
}
#[wasm_bindgen(js_name = getDuration)]
pub fn get_duration(&self, s: &Episode) -> Interval {
let lower = self.stn.edge_weight(s.1, s.0).unwrap_or(&0.);
let upper = self.stn.edge_weight(s.0, s.1).unwrap_or(&0.);
Interval::new(-*lower, *upper)
}
#[wasm_bindgen(catch)]
pub fn compile(&mut self) -> Result<(), JsValue> {
if !self.dirty {
return Ok(());
}
let mappings = match floyd_warshall(&self.stn) {
Ok(d) => d,
Err(e) => return Err(JsValue::from_str(&e)),
};
self.dispatchable = DiGraphMap::new();
for ((source, target), weight) in mappings.iter() {
self.dispatchable.add_edge(*source, *target, *weight);
}
self.dirty = false;
let c = self.committments.clone();
for (executed_event, time) in c.iter() {
self.commit_event(*executed_event, *time)?;
}
Ok(())
}
fn update_schedule(&mut self, event: EventID) -> Result<(), JsValue> {
self.compile()?;
let d = self.dispatchable.clone();
for neighbor in d.neighbors(event) {
if self.committments.contains_key(&neighbor) {
continue;
}
let time_to_neighbor = self.interval(event, neighbor)?;
let neighbor_window = match self.execution_windows.get(&neighbor) {
Some(i) => i,
None => return Err(JsValue::from_str(&format!("no such event {}", neighbor))),
};
let event_window = match self.execution_windows.get(&event) {
Some(i) => i,
None => return Err(JsValue::from_str(&format!("no such event {}", event))),
};
let new_neighbor_window = *neighbor_window & (*event_window + time_to_neighbor);
self.execution_windows.insert(neighbor, new_neighbor_window);
}
Ok(())
}
#[wasm_bindgen(catch, js_name = commitEvent)]
pub fn commit_event(&mut self, event: EventID, time: f64) -> Result<(), JsValue> {
self.committments.insert(event, time);
self.execution_windows
.insert(event, Interval::new(time, time));
self.update_schedule(event)?;
Ok(())
}
#[wasm_bindgen(catch, js_name = completeEpisode)]
pub fn complete_episode(&mut self, episode: &Episode, time: f64) -> Result<(), JsValue> {
self.commit_event(episode.end(), time)?;
Ok(())
}
#[wasm_bindgen(catch)]
pub fn window(&mut self, event: EventID) -> Result<Interval, JsValue> {
self.compile()?;
match self.execution_windows.get(&event) {
Some(i) => Ok(*i),
None => Err(JsValue::from(&format!("could not find event {}", event))),
}
}
#[wasm_bindgen(catch)]
pub fn interval(&mut self, source: EventID, target: EventID) -> Result<Interval, JsValue> {
self.compile()?;
let l = match self.dispatchable.edge_weight(target, source) {
Some(l) => l,
None => {
return Err(JsValue::from_str(&format!(
"missing lower edge: {} to {}",
target, source
)))
}
};
let upper = match self.dispatchable.edge_weight(source, target) {
Some(u) => u,
None => {
return Err(JsValue::from_str(&format!(
"missing upper edge: {} to {}",
source, target
)))
}
};
let lower = if *l == 0. { -0. } else { *l };
Ok(Interval::new(-lower, *upper))
}
#[wasm_bindgen(js_name = eventDistance)]
pub fn event_distance(&mut self, source: EventID, target: EventID) -> Result<JsValue, JsValue> {
if !self.stn.contains_node(source) {
return Err(JsValue::from_str(&format!(
"Source {} is not already in the Schedule. Have you added it with `addEpisode`?",
source
)));
}
if !self.stn.contains_node(target) {
return Err(JsValue::from_str(&format!(
"Target {} is not already in the Schedule. Have you added it with `addEpisode`?",
target
)));
}
match self.compile() {
Ok(_) => (),
Err(e) => return Err(e),
}
let t = match self.dispatchable.edge_weight(source, target) {
Some(t) => t,
None => return Err(JsValue::from_str(&"Cannot find path from start to target")),
};
Ok(JsValue::from_f64(*t))
}
pub fn update_interval(&mut self, source: EventID, target: EventID, interval: Vec<f64>) {
let i = Interval::from_vec(interval);
self.stn.add_edge(source, target, i.upper());
self.stn.add_edge(target, source, -i.lower());
self.dirty = true;
}
#[wasm_bindgen(js_name = addConstraint)]
pub fn add_constraint(
&mut self,
source: EventID,
target: EventID,
interval: Option<Vec<f64>>,
) -> Result<(), JsValue> {
if !self.stn.contains_node(source) {
return Err(JsValue::from_str(&format!(
"Source {} is not already in the Schedule. Have you added it with `addEpisode`?",
source
)));
}
if !self.stn.contains_node(target) {
return Err(JsValue::from_str(&format!(
"Target {} is not already in the Schedule. Have you added it with `addEpisode`?",
target
)));
}
let d = interval.unwrap_or(vec![0., 0.]);
let i = Interval::from_vec(d);
self.stn.add_edge(source, target, i.upper());
self.stn.add_edge(target, source, -i.lower());
self.dirty = true;
Ok(())
}
#[wasm_bindgen(catch, js_name = removeConstraint)]
pub fn remove_constraint(&mut self, source: EventID, target: EventID) -> Result<(), JsValue> {
if !self.stn.contains_node(source) {
return Err(JsValue::from_str(&format!(
"Source event {} is not in the Schedule. No constraints to remove",
source
)));
}
if !self.stn.contains_node(target) {
return Err(JsValue::from_str(&format!(
"Target event {} is not in the Schedule. No constraints to remove",
target
)));
}
self.stn.remove_edge(source, target);
Ok(())
}
#[wasm_bindgen(catch, js_name = removeConstraints)]
pub fn remove_constraints(
&mut self,
source: &Episode,
target: &Episode,
) -> Result<(), JsValue> {
self.remove_constraint(source.start(), target.start())?;
self.dirty = true;
self.remove_constraint(source.start(), target.start())?;
self.remove_constraint(source.start(), target.end())?;
self.remove_constraint(source.start(), target.end())?;
self.remove_constraint(target.end(), source.start())?;
self.remove_constraint(target.end(), source.start())?;
self.remove_constraint(target.end(), source.end())?;
self.remove_constraint(target.end(), source.end())?;
Ok(())
}
#[wasm_bindgen(catch, js_name = freeEpisode)]
pub fn free_episode(&mut self, episode: &Episode) -> Result<(), JsValue> {
let cloned_stn = self.stn.clone();
let incoming_edges = cloned_stn.neighbors_directed(episode.start(), Incoming);
let outgoing_edges = cloned_stn.neighbors_directed(episode.end(), Outgoing);
for e in incoming_edges {
self.stn.remove_edge(e, episode.start());
}
for e in outgoing_edges {
self.stn.remove_edge(episode.end(), e);
}
self.dirty = true;
Ok(())
}
}