diff options
-rw-r--r-- | README.md | 15 | ||||
-rw-r--r-- | src/config.rs | 18 | ||||
-rw-r--r-- | src/ui.rs | 51 | ||||
-rw-r--r-- | static/settings.js | 1 | ||||
-rw-r--r-- | static/style.css | 18 | ||||
-rw-r--r-- | templates/dashboard.html | 6 | ||||
-rw-r--r-- | templates/head.html | 3 | ||||
-rw-r--r-- | templates/settings.html | 7 | ||||
-rw-r--r-- | templates/settings_applied.html | 12 |
9 files changed, 53 insertions, 78 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..923a33e --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +## A control UI for ODR-DabMux + +Goals of this Web User Interface: + + * Simplify creating basic ODR-DabMux DAB Ensemble configurations + * Interact with the ODR-DabMux Remote Control through a web UI + +Complilation prerequisites + + * Install Rust, most probably through [rustup](https://rustup.rs/) + * Type `cargo run` + * Navigate to http://localhost:3000 + * Create a new Ensemble configuration in the Settings page, and specify where to write the odr-dabmux json config file + * Execute `odr-dabmux` with one argument: the configuration file + * Check in the Dashboard page that you see RC values diff --git a/src/config.rs b/src/config.rs index 64f3c09..68b5f44 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,5 +1,6 @@ use std::{collections::HashMap, fs}; use anyhow::Context; +use log::error; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -54,6 +55,7 @@ impl Service { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Config { pub instance_name: String, + pub dabmux_config_location: String, pub tist: bool, pub tist_offset: i32, // TODO tai_clock_bulletins @@ -79,6 +81,7 @@ impl Default for Config { fn default() -> Self { Config { instance_name: "CHANGEME".to_owned(), + dabmux_config_location: "/etc/odr-dabmux.json".to_owned(), tist: true, tist_offset: 0, ensemble_id: 0x4FFF, @@ -108,7 +111,11 @@ impl Config { pub fn load() -> anyhow::Result<Self> { if std::path::Path::new(CONFIGFILE).exists() { let file_contents = fs::read_to_string(CONFIGFILE)?; - toml::from_str(&file_contents).context("parsing config file") + toml::from_str(&file_contents) + .or_else(|e| { + error!("Failed to read existing config file: {}", e); + Ok(Default::default()) + }) } else { Ok(Default::default()) @@ -120,7 +127,7 @@ impl Config { .context("writing config file") } - pub fn dump_to_json(&self) -> serde_json::Value { + pub fn write_dabmux_json(&self) -> anyhow::Result<()> { let now = chrono::Utc::now().to_rfc3339(); let mut services = HashMap::new(); @@ -150,7 +157,7 @@ impl Config { })); } - json!({ + let new_conf = json!({ "_comment": format!("Generated at {} by odr-dabmux-gui", now), "general": { "dabmode": 1, @@ -186,6 +193,9 @@ impl Config { } } } - }) + }); + + fs::write(&self.dabmux_config_location, serde_json::to_string_pretty(&new_conf)?) + .context("writing dabmux config file") } } @@ -9,7 +9,6 @@ use axum::{ }; use serde::Deserialize; -use log::info; use tower_http::services::ServeDir; use crate::config; @@ -35,7 +34,6 @@ pub async fn serve(port: u16, shared_state: SharedState) { enum ActivePage { Dashboard, Settings, - None, } impl ActivePage { @@ -44,7 +42,6 @@ impl ActivePage { match self { ActivePage::Dashboard => vec!["dashboard.js", "main.js"], ActivePage::Settings => vec!["settings.js", "main.js"], - ActivePage::None => vec![], } } } @@ -95,7 +92,7 @@ struct SetRc { async fn post_rc( State(state): State<SharedState>, - Json(set_rc): Json<SetRc>) -> (StatusCode, Json<serde_json::Value>) { + Json(set_rc): Json<SetRc>) -> (StatusCode, String) { let set_rc_result = { let mut st = state.lock().unwrap(); @@ -103,11 +100,8 @@ async fn post_rc( }; 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)) - }, + Ok(v) => (StatusCode::OK, v.as_str().or(Some("")).unwrap().to_owned()), + Err(e) => (StatusCode::BAD_REQUEST, e.to_string()), } } @@ -127,45 +121,20 @@ async fn show_settings(State(state): State<SharedState>) -> SettingsTemplate<'st } } -#[derive(Template)] -#[template(path = "settings_applied.html")] -struct SettingsAppliedTemplate<'a> { - title: &'a str, - page: ActivePage, - conf: config::Config, - ok: bool, - error_message: &'a str, - error_reason: String, -} - async fn post_settings( State(state): State<SharedState>, - Json(conf): Json<config::Config>) -> (StatusCode, SettingsAppliedTemplate<'static>) { + Json(conf): Json<config::Config>) -> (StatusCode, String) { match conf.store() { Ok(()) => { state.lock().unwrap().conf.clone_from(&conf); - info!("{}", conf.dump_to_json()); - - (StatusCode::OK, SettingsAppliedTemplate { - title: "Settings", - conf, - page: ActivePage::None, - ok: true, - error_message: "", - error_reason: "".to_owned(), - }) + match conf.write_dabmux_json() { + Ok(()) => (StatusCode::OK, "".to_owned()), + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, + format!("Failed to write odr-dabmux config: {}", e.to_string())) + } } - Err(e) => { - (StatusCode::INTERNAL_SERVER_ERROR, SettingsAppliedTemplate { - title: "Settings", - conf, - page: ActivePage::None, - ok: false, - error_message: "Failed to store config", - error_reason: e.to_string(), - }) - }, + Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, format!("Failed to write UI config: {}", e.to_string())) } } diff --git a/static/settings.js b/static/settings.js index 7f5a067..4b917ae 100644 --- a/static/settings.js +++ b/static/settings.js @@ -12,6 +12,7 @@ async function btn_settings_remove_service(element_clicked) { async function btn_settings_send() { let data = { 'instance_name': document.getElementById('instance_name').value, + 'dabmux_config_location': document.getElementById('dabmux_config_location').value, 'tist': document.getElementById('tist').checked, 'tist_offset': parseInt(document.getElementById('tist_offset').value, 10), 'ensemble_id': parseInt(document.getElementById('ensemble_id').value, 16), diff --git a/static/style.css b/static/style.css index 39ca359..c7ab14f 100644 --- a/static/style.css +++ b/static/style.css @@ -1,7 +1,6 @@ :root { --main-color:rgb(7 89 133); --bg-color:rgb(224 242 254); - --title-color:rgb(224 242 254); --title-bg-color:rgb(125 211 252); --divide-color:rgb(125 211 252); --active-bg-color:rgb(12 74 110); @@ -95,8 +94,9 @@ ul { } .text-lg { - font-size: 1.125rem; - line-height: 1.75rem; + font-size: 1.4rem; + line-height: 2rem; + font-weight: bold; } .head-nav-topdiv { @@ -114,7 +114,7 @@ ul { border-radius: 0.5rem; margin-top: 0.5rem; background-color: var(--title-bg-color); - color: var(--title-color); + color: var(--main-color); } main { @@ -122,16 +122,6 @@ main { flex: 1 1 auto; } -h1 { - font-size: 1.2rem; - line-height: 1.75rem; - font-weight: 700; -} - -h2 { - font-weight: 700; -} - a { color: inherit; text-decoration: none; diff --git a/templates/dashboard.html b/templates/dashboard.html index 040538c..a60d6a1 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -1,10 +1,6 @@ {% include "head.html" %} <div class="content"> - <h1>Dashboard</h1> - <div class="section"> - <h2>ODR-DabMux</h2> - <p>www.opendigitalradio.org</p> - </div> + <h1>ODR-DabMux Dashboard</h1> <div class="section"> <h2>Remote control</h2> diff --git a/templates/head.html b/templates/head.html index 7a2e5bb..c835d6e 100644 --- a/templates/head.html +++ b/templates/head.html @@ -16,7 +16,8 @@ <div class="head-nav-topdiv"> <div class="nav-title"> <p class="text-lg">ODR-DabMux</p> - <p class="text-lg"><b>{{ conf.instance_name }}</b></p> + <p><a href="https://www.opendigitalradio.org">www.opendigitalradio.org</a></p> + <p class="text-lg">Instance name:<br>{{ conf.instance_name }}</p> </div> <div class="div-menu"> diff --git a/templates/settings.html b/templates/settings.html index 74843fb..713947f 100644 --- a/templates/settings.html +++ b/templates/settings.html @@ -4,7 +4,12 @@ <div class="section"> <h2>General</h2> <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 }}"> + <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="dabmux_config_location">ODR-DabMux JSON config to write:</label> + <input class="textinput" type="text" id="dabmux_config_location" value="{{ conf.dabmux_config_location }}"> </div> <div class="setting-entry"> <label for="tist">Enable TIST:</label> diff --git a/templates/settings_applied.html b/templates/settings_applied.html deleted file mode 100644 index 214b8fd..0000000 --- a/templates/settings_applied.html +++ /dev/null @@ -1,12 +0,0 @@ -{% include "head.html" %} -<div class="content"> - {% if ok %} - <h1>Configuration updated</h1> - {% else %} - <h1>Configuration update failed</h1> - <p>{{ error_message }}:</p> - <p>{{ error_reason }}:</p> - {% endif %} -</div> -{% include "foot.html" %} -{# vi:set et sw=2 ts=2: #} |