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>, } impl<'a> Trace for TraceMap<'a> { fn signal_names(&self) -> Vec<&str> { self.traces.keys().cloned().collect() } fn get(&self, name: &str) -> Option<&Signal> { let sig: &dyn AnySignal = match self.traces.get(name) { Some(signal) => signal, None => return None, }; sig.as_any().downcast_ref::>() } } #[derive(Serialize, Deserialize)] pub struct StateBuddyTraceEntry { simtime: f64, state: HashMap, // inputEvent: String, // outputEvents: Vec, } #[derive(Serialize, Deserialize)] pub struct StateBuddyTrace { entries: Vec, } #[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>::new(); for entry in &trace.entries { for (prop, val) in &entry.state { let signal = traceMap.entry(prop.as_str()).or_insert_with(|| Signal::::Sampled { values: vec![], time_points: vec![], }); if let Signal::::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 = 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::(&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::::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, }