aboutsummaryrefslogtreecommitdiffstats
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
parent7208269be13d0ee27fd696fcae31330e2a78b969 (diff)
downloadodr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.tar.gz
odr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.tar.bz2
odr-dabmux-gui-e42edddd33382855202ff2f17e6b77d65f6ad152.zip
Show RC params in dashboard
-rw-r--r--Cargo.lock232
-rw-r--r--Cargo.toml1
-rw-r--r--src/dabmux.rs112
-rw-r--r--src/main.rs3
-rw-r--r--src/ui.rs50
-rw-r--r--static/dashboard.js5
-rw-r--r--static/settings.js41
-rw-r--r--static/style.css13
-rw-r--r--templates/dashboard.html15
-rw-r--r--templates/settings.html17
10 files changed, 476 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 0880ac8..0e47997 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -200,6 +200,12 @@ dependencies = [
[[package]]
name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
@@ -257,10 +263,22 @@ version = "1.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07b1695e2c7e8fc85310cde85aeaab7e3097f593c91d209d3f9df76c928100f0"
dependencies = [
+ "jobserver",
+ "libc",
"shlex",
]
[[package]]
+name = "cfg-expr"
+version = "0.15.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+dependencies = [
+ "smallvec",
+ "target-lexicon",
+]
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -307,6 +325,62 @@ dependencies = [
]
[[package]]
+name = "crossbeam"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-deque"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
+dependencies = [
+ "crossbeam-epoch",
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-epoch"
+version = "0.9.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
+
+[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -342,6 +416,23 @@ dependencies = [
]
[[package]]
+name = "dircpy"
+version = "0.3.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a88521b0517f5f9d51d11925d8ab4523497dcf947073fa3231a311b63941131c"
+dependencies = [
+ "jwalk",
+ "log",
+ "walkdir",
+]
+
+[[package]]
+name = "either"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
+
+[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -485,6 +576,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
name = "hermit-abi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -641,6 +738,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
+name = "jobserver"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "js-sys"
version = "0.3.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -650,6 +756,16 @@ dependencies = [
]
[[package]]
+name = "jwalk"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2735847566356cd2179a2a38264839308f7079fa96e6bd5a42d740460e003c56"
+dependencies = [
+ "crossbeam",
+ "rayon",
+]
+
+[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -811,6 +927,7 @@ dependencies = [
"toml",
"tower-http",
"tun",
+ "zmq",
]
[[package]]
@@ -881,6 +998,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
+name = "pkg-config"
+version = "0.3.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
+
+[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -944,12 +1067,32 @@ dependencies = [
]
[[package]]
+name = "rayon"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
+dependencies = [
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
+dependencies = [
+ "crossbeam-deque",
+ "crossbeam-utils",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0884ad60e090bf1345b93da0a5de8923c93884cd03f40dfcfddd3b4bee661853"
dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
]
[[package]]
@@ -971,6 +1114,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1137,6 +1289,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394"
[[package]]
+name = "system-deps"
+version = "6.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "target-lexicon"
+version = "0.12.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+
+[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1314,7 +1485,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5"
dependencies = [
- "bitflags",
+ "bitflags 2.6.0",
"bytes",
"futures-util",
"http",
@@ -1456,12 +1627,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
+name = "version-compare"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b"
+
+[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
+[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1523,6 +1710,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
[[package]]
+name = "winapi-util"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
+dependencies = [
+ "windows-sys 0.52.0",
+]
+
+[[package]]
name = "windows"
version = "0.51.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1731,3 +1927,35 @@ dependencies = [
"quote",
"syn 2.0.77",
]
+
+[[package]]
+name = "zeromq-src"
+version = "0.2.6+4.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc120b771270365d5ed0dfb4baf1005f2243ae1ae83703265cb3504070f4160b"
+dependencies = [
+ "cc",
+ "dircpy",
+]
+
+[[package]]
+name = "zmq"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd3091dd571fb84a9b3e5e5c6a807d186c411c812c8618786c3c30e5349234e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "libc",
+ "zmq-sys",
+]
+
+[[package]]
+name = "zmq-sys"
+version = "0.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e8351dc72494b4d7f5652a681c33634063bbad58046c1689e75270908fdc864"
+dependencies = [
+ "libc",
+ "system-deps",
+ "zeromq-src",
+]
diff --git a/Cargo.toml b/Cargo.toml
index 2c3809c..a85e890 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ toml = "0.8"
tokio = { version = "1", features = ["full"] }
tokio-tungstenite = "0.21"
tower-http = { version = "0.5.0", features = ["fs"] }
+zmq = "0.10"
futures-core = "0.3"
futures= "0.3"
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))
+ },
}
}
diff --git a/static/dashboard.js b/static/dashboard.js
new file mode 100644
index 0000000..6eb8274
--- /dev/null
+++ b/static/dashboard.js
@@ -0,0 +1,5 @@
+async function btn_dash_update(element_clicked, module, param) {
+ let value = element_clicked.parentElement.children[0].value;
+ let data = {'module': module, 'param': param, 'value': value};
+ await post('/api/set_rc', data);
+}
diff --git a/static/settings.js b/static/settings.js
new file mode 100644
index 0000000..287f6f6
--- /dev/null
+++ b/static/settings.js
@@ -0,0 +1,41 @@
+async function btn_settings_add_service() {
+ const template = document.getElementById('service_template');
+
+ let clon = template.content.cloneNode(true);
+ document.getElementById('services').appendChild(clon);
+}
+
+async function btn_settings_remove_service(element_clicked) {
+ element_clicked.parentElement.remove()
+}
+
+async function btn_settings_send() {
+ let data = {
+ 'instance_name': document.getElementById('instance_name').value,
+ 'tist': document.getElementById('tist').checked,
+ 'tist_offset': parseInt(document.getElementById('tist_offset').value, 10),
+ 'ensemble_id': parseInt(document.getElementById('ensemble_id').value, 16),
+ 'ensemble_ecc': parseInt(document.getElementById('ensemble_ecc').value, 16),
+ 'ensemble_label': document.getElementById('ensemble_label').value,
+ 'ensemble_shortlabel': document.getElementById('ensemble_shortlabel').value,
+ 'output_edi_port': parseInt(document.getElementById('output_edi_port').value, 10),
+ 'services': [],
+ };
+
+ const services = document.getElementById('services');
+ const destList = services.querySelectorAll("p.service");
+ for (let i = 0; i < destList.length; i++) {
+ data.services.push({
+ 'sid': parseInt(destList[i].querySelector("input.srv_sid").value, 16),
+ 'ecc': parseInt(destList[i].querySelector("input.srv_ecc").value, 16),
+ 'label': destList[i].querySelector("input.srv_label").value,
+ 'shortlabel': destList[i].querySelector("input.srv_shortlabel").value,
+ 'input_port': parseInt(destList[i].querySelector("input.srv_input_port").value, 10),
+ 'bitrate': parseInt(destList[i].querySelector("input.srv_bitrate").value, 10),
+ 'protection': parseInt(destList[i].querySelector("input.srv_protection").value, 10),
+ });
+ }
+
+ await post('/api/settings', data);
+}
+
diff --git a/static/style.css b/static/style.css
index dd43c7a..39ca359 100644
--- a/static/style.css
+++ b/static/style.css
@@ -29,6 +29,19 @@ nav {
flex: none;
}
+.section {
+ padding-left: 0.25rem;
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+}
+
+.setting-entry {
+ padding-left: 0.25rem;
+ padding-top: 0.25rem;
+ padding-bottom: 0.25rem;
+}
+
+
.div-menu {
border-top-width: 1px;
border-bottom-width: 0px;
diff --git a/templates/dashboard.html b/templates/dashboard.html
index c8a1693..040538c 100644
--- a/templates/dashboard.html
+++ b/templates/dashboard.html
@@ -7,6 +7,21 @@
</div>
<div class="section">
<h2>Remote control</h2>
+
+ {% if let Some(e) = errors %}
+ <p>Error!: {{ e }}</p>
+ {% endif %}
+
+ <table>
+ <tr><th>Module</th><th>Parameter</th><th>Value</th></tr>
+ {% for p in params %}
+ <tr><td>{{ p.module }}</td><td>{{ p.param }}</td>
+ <td>
+ <input class="textinput" type="text" value="{{ p.value }}">
+ <button class="btn" type="button" onclick="btn_dash_update(this, '{{ p.module }}', '{{ p.param }}')">Update</button>
+ </td></tr>
+ {% endfor %}
+ </table>
</div>
</div>
{% include "foot.html" %}
diff --git a/templates/settings.html b/templates/settings.html
index dfe71cf..ca9a77a 100644
--- a/templates/settings.html
+++ b/templates/settings.html
@@ -3,34 +3,37 @@
<h1>ODR-DabMux Settings</h1>
<div class="section">
<h2>General</h2>
- <div><label for="instance_name">Name of this instance:</label><input class="textinput" type="text" id="instance_name" value="{{ conf.instance_name }}"></div>
- <div>
+ <div class="setting-entry">
+ <label for="instance_name">Name of this instance:</label><input class="textinput" type="text" id="instance_name" value="{{ conf.instance_name }}">
+ </div>
+ <div class="setting-entry">
<label for="tist">Enable TIST:</label>
<input type="checkbox" id="tist" value="Enable TIST"
{% if conf.tist %} checked {% endif %} >
</div>
- <div>
+ <div class="setting-entry">
<label for="tist_offset">TIST offset:</label>
<input class="textinput" type="text" id="tist_offset" placeholder="TIST offset in seconds" value="{{ conf.tist_offset }}">
</div>
- <div>
+ <div class="setting-entry">
<label for="ensemble_id">EId:</label>
<input class="textinput" type="text" id="ensemble_id" placeholder="Ensemble ID in hex" value="{{ conf.ensemble_id_hex() }}">
</div>
- <div>
+ <div class="setting-entry">
<label for="ensemble_ecc">ECC:</label>
<input class="textinput" type="text" id="ensemble_ecc" placeholder="Ensemble ECC in hex" value="{{ conf.ensemble_ecc_hex() }}">
</div>
- <div>
+ <div class="setting-entry">
<label for="ensemble_label">Label and shortlabel:</label>
<input class="textinput" type="text" id="ensemble_label" placeholder="Ensemble Label" value="{{ conf.ensemble_label }}">
<input class="textinput" type="text" id="ensemble_shortlabel" placeholder="Ensemble Short Label" value="{{ conf.ensemble_shortlabel }}">
</div>
- <div>
+ <div class="setting-entry">
<label for="output_edi_port">EDI TCP Listen Port</label>
<input class="textinput" type="text" id="output_edi_port" placeholder="TCP Listen Port for EDI Output" value="{{ conf.output_edi_port }}">
</div>
</div>
+ <div class="section"><h2>Services:</h2></div>
<div class="section">
<template id="service_template">
<p class="service">