aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-20 11:23:53 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-20 11:23:53 +0200
commite42edddd33382855202ff2f17e6b77d65f6ad152 (patch)
treec26c0f5b48e66a3f36380bcdfb1ecd60c7453234 /src
parent7208269be13d0ee27fd696fcae31330e2a78b969 (diff)
downloadodr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.tar.gz
odr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.tar.bz2
odr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.zip
Show RC params in dashboard
Diffstat (limited to 'src')
-rw-r--r--src/dabmux.rs112
-rw-r--r--src/main.rs3
-rw-r--r--src/ui.rs50
3 files changed, 161 insertions, 4 deletions
diff --git a/src/dabmux.rs b/src/dabmux.rs
new file mode 100644
index 0000000..fcdd864
--- /dev/null
+++ b/src/dabmux.rs
@@ -0,0 +1,112 @@
+use anyhow::anyhow;
+use serde_json::Value;
+
+const ZMQ_TIMEOUT : i64 = 2000;
+
+pub struct DabMux {
+ ctx : zmq::Context,
+ rc_endpoint : String,
+}
+
+pub struct Param {
+ pub module : String,
+ pub param : String,
+ pub value : String,
+}
+
+
+impl DabMux {
+ pub fn new() -> Self {
+ let ctx = zmq::Context::new();
+ Self {
+ ctx,
+ rc_endpoint : "tcp://127.0.0.1:12722".to_owned()
+ }
+ }
+
+ fn value_to_params(v: Value) -> anyhow::Result<Vec<Param>> {
+ let root = v.as_object().ok_or(anyhow!("RC data is not a JSON object"))?;
+
+ let mut all_params = Vec::new();
+
+ for (module_name, params_value) in root {
+ let params = params_value.as_object().ok_or(anyhow!("RC module {} is not a JSON object", module_name))?;
+
+ for (param_name, value_json) in params {
+
+ let value = match value_json {
+ Value::Null => "null".to_owned(),
+ Value::Bool(b) => b.to_string(),
+ Value::Number(n) => n.to_string(),
+ Value::String(s) => s.clone(),
+ Value::Array(_) => return Err(anyhow!(format!("Unexpected array in {}.{}", module_name, param_name))),
+ Value::Object(_) => return Err(anyhow!(format!("Unexpected object in {}.{}", module_name, param_name))),
+ };
+
+ all_params.push(
+ Param {
+ module: module_name.to_owned(),
+ param: param_name.to_owned(),
+ value
+ });
+ }
+ }
+
+ Ok(all_params)
+ }
+
+ pub fn get_rc_parameters(&mut self) -> anyhow::Result<Vec<Param>> {
+ let sock = self.ctx.socket(zmq::REQ)?;
+ sock.connect(&self.rc_endpoint)?;
+ sock.send("showjson", 0)?;
+
+ let mut msg = zmq::Message::new();
+ let mut items = [
+ sock.as_poll_item(zmq::POLLIN),
+ ];
+ zmq::poll(&mut items, ZMQ_TIMEOUT).unwrap();
+ if items[0].is_readable() {
+ sock.recv(&mut msg, 0)?;
+ let msg = msg.as_str().ok_or(anyhow!("RC response is not a str"))?;
+
+ // JSON structure:
+ // { "module1": { "param1": "value", "param2": "value" }, "module2": { ... } }
+ let v: Value = serde_json::from_str(msg)?;
+ Self::value_to_params(v)
+ }
+ else {
+ Err(anyhow!("Timeout reading RC"))
+ }
+ }
+
+ pub fn set_rc_parameter(&mut self, module: &str, param: &str, value: &str) -> anyhow::Result<Value> {
+ let sock = self.ctx.socket(zmq::REQ)?;
+ sock.connect(&self.rc_endpoint)?;
+ sock.send_multipart(["set", module, param, value], 0)?;
+
+ let mut items = [
+ sock.as_poll_item(zmq::POLLIN),
+ ];
+ zmq::poll(&mut items, ZMQ_TIMEOUT).unwrap();
+ if items[0].is_readable() {
+ let mut parts = sock.recv_multipart(0)?;
+
+ let j : String = parts.drain(..)
+ .map(|p| match String::from_utf8(p)
+ {
+ Ok(s) => s,
+ Err(_) => "???".to_owned(),
+ })
+ .collect::<Vec<String>>()
+ .join(",");
+
+ eprintln!("SET_RC: {}", j);
+
+ let v: Value = serde_json::Value::String(j);
+ Ok(v)
+ }
+ else {
+ Err(anyhow!("Timeout reading RC"))
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index e64570a..66fbe23 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,9 +4,11 @@ use log::{debug, info, warn, error};
mod ui;
mod config;
+mod dabmux;
struct AppState {
conf : config::Config,
+ dabmux : dabmux::DabMux,
}
type SharedState = Arc<Mutex<AppState>>;
@@ -22,6 +24,7 @@ async fn main() -> std::io::Result<()> {
let shared_state = Arc::new(Mutex::new(AppState {
conf : conf.clone(),
+ dabmux : dabmux::DabMux::new(),
}));
let port = 3000;
diff --git a/src/ui.rs b/src/ui.rs
index 914d289..6bcb04d 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -24,6 +24,7 @@ pub async fn serve(port: u16, shared_state: SharedState) {
.route("/", get(dashboard))
.route("/settings", get(show_settings))
.route("/api/settings", post(post_settings))
+ .route("/api/set_rc", post(post_rc))
.nest_service("/static", ServeDir::new("static"))
/* For an example for timeouts and tracing, have a look at the git history */
.with_state(shared_state);
@@ -45,7 +46,7 @@ impl ActivePage {
// Used by templates/head.html to include the correct js files in <head>
fn styles(&self) -> Vec<&'static str> {
match self {
- ActivePage::Dashboard => vec![],
+ ActivePage::Dashboard => vec!["dashboard.js", "main.js"],
ActivePage::Settings => vec!["settings.js", "main.js"],
ActivePage::None => vec![],
}
@@ -58,18 +59,59 @@ struct DashboardTemplate<'a> {
title: &'a str,
page: ActivePage,
conf: config::Config,
+ errors: Option<String>,
+ params: Vec<crate::dabmux::Param>,
}
async fn dashboard(State(state): State<SharedState>) -> DashboardTemplate<'static> {
- let conf = {
- let st = state.lock().unwrap();
- st.conf.clone()
+ let (conf, params_result) = {
+ let mut st = state.lock().unwrap();
+
+ let params_result = st.dabmux.get_rc_parameters();
+
+ (st.conf.clone(), params_result)
+ };
+
+ let (params, errors) = match params_result {
+ Ok(v) => {
+ (v, None)
+ },
+ Err(e) => {
+ (Vec::new(), Some(format!("{}", e)))
+ },
};
DashboardTemplate {
title: "Dashboard",
conf,
page: ActivePage::Dashboard,
+ params,
+ errors,
+ }
+}
+
+#[derive(Deserialize)]
+struct SetRc {
+ pub module : String,
+ pub param : String,
+ pub value : String,
+}
+
+async fn post_rc(
+ State(state): State<SharedState>,
+ Json(set_rc): Json<SetRc>) -> (StatusCode, Json<serde_json::Value>) {
+
+ let set_rc_result = {
+ let mut st = state.lock().unwrap();
+ st.dabmux.set_rc_parameter(&set_rc.module, &set_rc.param, &set_rc.value)
+ };
+
+ match set_rc_result {
+ Ok(v) => (StatusCode::OK, Json(v)),
+ Err(e) => {
+ let e_str = serde_json::Value::String(e.to_string());
+ (StatusCode::BAD_REQUEST, Json(e_str))
+ },
}
}