aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-19 23:33:17 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2024-09-19 23:33:17 +0200
commit7208269be13d0ee27fd696fcae31330e2a78b969 (patch)
tree97ceed90bb472fbeab9b0dbc135fb418d64f253d
parenteec01c9b72feff9533477014881b982124ca7b6d (diff)
downloadodr-dabmux-gui-7208269be13d0ee27fd696fcae31330e2a78b969.tar.gz
odr-dabmux-gui-7208269be13d0ee27fd696fcae31330e2a78b969.tar.bz2
odr-dabmux-gui-7208269be13d0ee27fd696fcae31330e2a78b969.zip
Add content to settings page
-rw-r--r--src/config.rs63
-rw-r--r--src/ui.rs79
-rw-r--r--templates/head.html2
-rw-r--r--templates/settings.html76
4 files changed, 156 insertions, 64 deletions
diff --git a/src/config.rs b/src/config.rs
index 3e0571d..54f94dd 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,16 +2,75 @@ use std::fs;
use anyhow::Context;
use serde::{Deserialize, Serialize};
+type Protection = u8;
+
+#[derive(Debug, Serialize, Deserialize, Clone)]
+pub struct Service {
+ pub sid: u32,
+ pub ecc: u8,
+ pub label: String,
+ pub shortlabel: String,
+ pub input_port: u16,
+ pub bitrate: u32,
+ pub protection: Protection,
+}
+
+impl Service {
+ pub fn sid_hex(&self) -> String {
+ format!("{:04X}", self.sid)
+ }
+
+ pub fn ecc_hex(&self) -> String {
+ format!("{:02X}", self.ecc)
+ }
+}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Config {
- pub name: String,
+ pub instance_name: String,
+ pub tist: bool,
+ pub tist_offset: i32,
+ // TODO tai_clock_bulletins
+ pub ensemble_id: u16,
+ pub ensemble_ecc: u8,
+ pub ensemble_label: String,
+ pub ensemble_shortlabel: String,
+ pub output_edi_port: u16,
+ pub services: Vec<Service>,
+}
+
+impl Config {
+ pub fn ensemble_id_hex(&self) -> String {
+ format!("{:04X}", self.ensemble_id)
+ }
+
+ pub fn ensemble_ecc_hex(&self) -> String {
+ format!("{:02X}", self.ensemble_ecc)
+ }
}
impl Default for Config {
fn default() -> Self {
Config {
- name: "CHANGEME".to_owned(),
+ instance_name: "CHANGEME".to_owned(),
+ tist: true,
+ tist_offset: 0,
+ ensemble_id: 0x4FFF,
+ ensemble_ecc: 0xE1,
+ ensemble_label: "OpenDigitalRadio".to_owned(),
+ ensemble_shortlabel: "ODR".to_owned(),
+ output_edi_port: 8951,
+ services: vec![
+ Service {
+ sid: 0x4DAA,
+ ecc: 0xE1,
+ label: "nothing".to_owned(),
+ shortlabel: "no".to_owned(),
+ input_port: 9001,
+ bitrate: 128,
+ protection: 2
+ }
+ ],
}
}
}
diff --git a/src/ui.rs b/src/ui.rs
index 529dfa6..914d289 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -22,7 +22,8 @@ use crate::SharedState;
pub async fn serve(port: u16, shared_state: SharedState) {
let app = Router::new()
.route("/", get(dashboard))
- .route("/settings", get(show_settings).post(post_settings))
+ .route("/settings", get(show_settings))
+ .route("/api/settings", post(post_settings))
.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 {
fn styles(&self) -> Vec<&'static str> {
match self {
ActivePage::Dashboard => vec![],
- ActivePage::Settings => vec![],
+ ActivePage::Settings => vec!["settings.js", "main.js"],
ActivePage::None => vec![],
}
}
@@ -99,60 +100,32 @@ struct SettingsAppliedTemplate<'a> {
error_reason: String,
}
-#[derive(Deserialize, Debug)]
-struct FormConfig {
- name: String,
-}
-
-impl TryFrom<FormConfig> for config::Config {
- type Error = anyhow::Error;
-
- fn try_from(value: FormConfig) -> Result<Self, Self::Error> {
- Ok(config::Config {
- name: value.name,
- })
- }
-}
-
async fn post_settings(
State(state): State<SharedState>,
- Form(input): Form<FormConfig>) -> (StatusCode, SettingsAppliedTemplate<'static>) {
- match config::Config::try_from(input) {
- Ok(c) => {
- match c.store() {
- Ok(()) => {
- state.lock().unwrap().conf.clone_from(&c);
-
- (StatusCode::OK, SettingsAppliedTemplate {
- title: "Settings",
- conf: c,
- page: ActivePage::None,
- ok: true,
- error_message: "",
- error_reason: "".to_owned(),
- })
- }
- Err(e) => {
- (StatusCode::INTERNAL_SERVER_ERROR, SettingsAppliedTemplate {
- title: "Settings",
- conf : c,
- page: ActivePage::None,
- ok: false,
- error_message: "Failed to store config",
- error_reason: e.to_string(),
- })
- },
- }
- },
+ Json(conf): Json<config::Config>) -> (StatusCode, SettingsAppliedTemplate<'static>) {
+
+ match conf.store() {
+ Ok(()) => {
+ state.lock().unwrap().conf.clone_from(&conf);
+
+ (StatusCode::OK, SettingsAppliedTemplate {
+ title: "Settings",
+ conf,
+ page: ActivePage::None,
+ ok: true,
+ error_message: "",
+ error_reason: "".to_owned(),
+ })
+ }
Err(e) => {
- (StatusCode::BAD_REQUEST, SettingsAppliedTemplate {
- title: "Settings",
- conf: state.lock().unwrap().conf.clone(),
- page: ActivePage::None,
- ok: false,
- error_message: "Error interpreting POST data",
- error_reason: e.to_string(),
- })
+ (StatusCode::INTERNAL_SERVER_ERROR, SettingsAppliedTemplate {
+ title: "Settings",
+ conf,
+ page: ActivePage::None,
+ ok: false,
+ error_message: "Failed to store config",
+ error_reason: e.to_string(),
+ })
},
}
}
diff --git a/templates/head.html b/templates/head.html
index 47b314f..7a2e5bb 100644
--- a/templates/head.html
+++ b/templates/head.html
@@ -16,7 +16,7 @@
<div class="head-nav-topdiv">
<div class="nav-title">
<p class="text-lg">ODR-DabMux</p>
- <p class="text-lg"><b>{{ conf.name }}</b></p>
+ <p class="text-lg"><b>{{ conf.instance_name }}</b></p>
</div>
<div class="div-menu">
diff --git a/templates/settings.html b/templates/settings.html
index 8a7d56e..dfe71cf 100644
--- a/templates/settings.html
+++ b/templates/settings.html
@@ -1,15 +1,75 @@
{% include "head.html" %}
<div class="content">
<h1>ODR-DabMux Settings</h1>
-
<div class="section">
- <form action="/settings" method="post">
- <fieldset>
- <legend>General</legend>
- <div><label for="name">Name of this instance:</label><input class="textinput" type="text" name="name" value="{{ conf.name }}"></div>
- </fieldset>
- <div><input class="btn" type="submit" value="Update"></div>
- </form>
+ <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>
+ <label for="tist">Enable TIST:</label>
+ <input type="checkbox" id="tist" value="Enable TIST"
+ {% if conf.tist %} checked {% endif %} >
+ </div>
+ <div>
+ <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>
+ <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>
+ <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>
+ <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>
+ <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">
+ <template id="service_template">
+ <p class="service">
+ <input class="textinput srv_sid" type="text" placeholder="Service ID in hex">
+ <input class="textinput srv_ecc" type="text" placeholder="Service ECC in hex">
+ <input class="textinput srv_label" type="text" placeholder="Service Label">
+ <input class="textinput srv_shortlabel" type="text" placeholder="Service Short Label">
+ <input class="textinput srv_input_port" type="text" placeholder="EDI TCP Input Port">
+ <input class="textinput srv_bitrate" type="text" placeholder="Bitrate in kbps">
+ <input class="textinput srv_protection" type="text" placeholder="Protection 1 to 4">
+ <button class="btn" type="button" onclick="btn_settings_remove_service(this)">Remove</button>
+ </p>
+ </template>
+ <div id="services">
+ {% for srv in conf.services %}
+ <p class="service">
+ <input class="textinput srv_sid" type="text" placeholder="Service ID in hex"
+ value="{{ srv.sid_hex() }}">
+ <input class="textinput srv_ecc" type="text" placeholder="Service ECC in hex"
+ value="{{ srv.ecc_hex() }}">
+ <input class="textinput srv_label" type="text" placeholder="Service Label"
+ value="{{ srv.label }}">
+ <input class="textinput srv_shortlabel" type="text" placeholder="Service Short Label"
+ value="{{ srv.shortlabel }}">
+ <input class="textinput srv_input_port" type="text" placeholder="EDI TCP Input Port"
+ value="{{ srv.input_port }}">
+ <input class="textinput srv_bitrate" type="text" placeholder="Bitrate in kbps"
+ value="{{ srv.bitrate }}">
+ <input class="textinput srv_protection" type="text" placeholder="Protection 1 to 4"
+ value="{{ srv.protection }}">
+ <button class="btn" type="button" onclick="btn_settings_remove_service(this)">Remove</button>
+ </p>
+ {% endfor %}
+ </div>
+ <button class="btn" type="button" onclick="btn_settings_add_service()">Add service</button>
+ </div>
+ <div class="section">
+ <button class="btn" type="button" onclick="btn_settings_send()">Send</button>
+ </div>
</div>
{% include "foot.html" %}
{# vi:set et sw=2 ts=2: #}