122 lines
3.9 KiB
Rust
122 lines
3.9 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;
|
|
extern crate web_sys;
|
|
|
|
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;
|
|
use web_sys::console;
|
|
|
|
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");
|
|
}
|
|
}
|
|
}
|
|
let m = TraceMap{traces: traceMap};
|
|
for name in m.signal_names() {
|
|
console::log_2(&JsValue::from_str("signal name:"), &JsValue::from_str(name));
|
|
}
|
|
// parse property string
|
|
let parse_result = argus::parse_str(s);
|
|
let expr = match parse_result {
|
|
Ok(expr) => expr,
|
|
Err(e) => {
|
|
let messages: Vec<String> = e.iter().map(|e| e.to_string()).collect();
|
|
let joined = messages.join(",");
|
|
return JsValue::from_str(format!("failed to parse expression: {}", joined).as_str());
|
|
},
|
|
};
|
|
// evaluate property on trace
|
|
let eval_result = match expr {
|
|
Expr::Bool(bool_expr) => BooleanSemantics::eval::<interpolation::Constant>(&bool_expr, &m),
|
|
_ => return JsValue::from_str("expected boolean expression (this should never happen)"),
|
|
};
|
|
console::log_1(&JsValue::from_str("evaluated property"));
|
|
let mut result = Vec::<StateBuddyEvalResultEntry>::new();
|
|
match eval_result {
|
|
Ok(r) => {
|
|
r.iter().for_each(|(timestamp, satisfied), | {
|
|
result.push(StateBuddyEvalResultEntry{
|
|
timestamp: timestamp.as_millis() as f64,
|
|
satisfied: *satisfied,
|
|
});
|
|
});
|
|
}
|
|
Err(e) => {
|
|
return JsValue::from_str(format!("failed to evaluate expression: {}", e).as_str());
|
|
}
|
|
}
|
|
console::log_1(&JsValue::from_str("now converting to JS value again"));
|
|
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>,
|
|
}
|