argus-wasm/src/lib.rs

108 lines
3.4 KiB
Rust

extern crate wasm_bindgen;
extern crate argus;
extern crate serde_json;
extern crate js_sys;
extern crate serde;
extern crate serde_wasm_bindgen;
use wasm_bindgen::prelude::*;
use argus::expr::{Expr};
use argus::{Trace, Signal, AnySignal, BooleanSemantics};
use argus::signals::interpolation;
use std::collections::HashMap;
use std::vec;
use serde::{Serialize, Deserialize};
use serde_wasm_bindgen::{from_value, to_value};
use std::time::Duration;
struct TraceMap<'a> {
traces: HashMap<&'a str, Signal<f64>>,
}
impl<'a> Trace for TraceMap<'a> {
fn signal_names(&self) -> Vec<&str> {
self.traces.keys().cloned().collect()
}
fn get<T: 'static>(&self, name: &str) -> Option<&Signal<T>> {
let sig: &dyn AnySignal = match self.traces.get(name) {
Some(signal) => signal,
None => return None,
};
sig.as_any().downcast_ref::<Signal<T>>()
}
}
#[derive(Serialize, Deserialize)]
pub struct StateBuddyTraceEntry {
simtime: f64,
state: HashMap<String, f64>,
// inputEvent: String,
// outputEvents: Vec<String>,
}
#[derive(Serialize, Deserialize)]
pub struct StateBuddyTrace {
entries: Vec<StateBuddyTraceEntry>,
}
#[wasm_bindgen]
pub fn eval_boolean(s: &str, js_trace: JsValue) -> JsValue {
// convert input (a sequence of state-objects) to a Trace that argus accepts:
let trace: StateBuddyTrace = match from_value(js_trace) {
Ok(trace) => trace,
Err(e) => return JsValue::from_str(("failed to parse JSON: ".to_owned() + &e.to_string()).as_str()),
};
let mut traceMap = HashMap::<&str, Signal<f64>>::new();
for entry in &trace.entries {
for (prop, val) in &entry.state {
let signal = traceMap.entry(prop.as_str()).or_insert_with(|| Signal::<f64>::Sampled {
values: vec![],
time_points: vec![],
});
if let Signal::<f64>::Sampled { values, time_points } = signal {
values.push(*val);
time_points.push(Duration::from_millis(entry.simtime as u64));
}
else {
return JsValue::from_str("this should never happen");
}
}
}
// parse property string
let parse_result = argus::parse_str(s);
let expr = match parse_result {
Ok(expr) => expr,
Err(e) => return JsValue::from_str("failed to parse expression"),
};
// evaluate property on trace
let eval_result = match expr {
Expr::Bool(bool_expr) => BooleanSemantics::eval::<interpolation::Constant>(&bool_expr, &TraceMap{traces: traceMap}),
_ => return JsValue::from_str("expected boolean expression (this should never happen)"),
};
let mut result = Vec::<StateBuddyEvalResultEntry>::new();
if let Ok(r) = eval_result {
r.iter().for_each(|(timestamp, satisfied), | {
result.push(StateBuddyEvalResultEntry{
timestamp: timestamp.as_millis() as f64,
satisfied: *satisfied,
});
});
}
else {
return JsValue::from_str("failed to evaluate expression");
}
to_value(&StateBuddyEvalResult { entries: result }).expect("fuuuck")
}
#[derive(Serialize, Deserialize)]
pub struct StateBuddyEvalResultEntry {
timestamp: f64,
satisfied: bool,
}
#[derive(Serialize, Deserialize)]
pub struct StateBuddyEvalResult {
entries: Vec<StateBuddyEvalResultEntry>,
}