aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-20 21:24:32 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-20 21:24:32 +0200
commitad30bb7aedf6b2e959a0ed90e90e2b991a69f6dc (patch)
tree72ade1a402d3e2e53cf9d976da3ca9a2b66b9a3b
parent3189dc23f2abf2060f591219e8256301e7c41aed (diff)
downloadodr-dabmux-gui-ad30bb7aedf6b2e959a0ed90e90e2b991a69f6dc.tar.gz
odr-dabmux-gui-ad30bb7aedf6b2e959a0ed90e90e2b991a69f6dc.tar.bz2
odr-dabmux-gui-ad30bb7aedf6b2e959a0ed90e90e2b991a69f6dc.zip
Write dabmux config, add README
-rw-r--r--README.md15
-rw-r--r--src/config.rs18
-rw-r--r--src/ui.rs51
-rw-r--r--static/settings.js1
-rw-r--r--static/style.css18
-rw-r--r--templates/dashboard.html6
-rw-r--r--templates/head.html3
-rw-r--r--templates/settings.html7
-rw-r--r--templates/settings_applied.html12
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")
}
}
diff --git a/src/ui.rs b/src/ui.rs
index a5be6a5..4a73ba4 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -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: #}