SVG Graph, compressed statics ... and much more

This commit is contained in:
2025-01-23 23:20:54 +01:00
parent 6b54fb570a
commit ff60803f88
5 changed files with 82 additions and 17 deletions

View File

@@ -1,3 +1,4 @@
fn main() {
println!("cargo:rustc-link-arg-bins=-Tlinkall.x");
println!("cargo:rustc-env=CARGO_PKG_VERSION_SAFE={}", env!("CARGO_PKG_VERSION").replace(".", "_"));
}

View File

@@ -12,6 +12,9 @@ use include_file_compress::include_file_compress_deflate;
use crate::database::DATAPOINT_BUFFER;
const STYLES_CSS_FILENAME: &str = concat!("/styles-", env!("CARGO_PKG_VERSION_SAFE"), ".css");
const APP_JS_FILENAME: &str = concat!("/app-", env!("CARGO_PKG_VERSION_SAFE"), ".css");
static PICO_CONFIG : picoserve::Config<Duration> = picoserve::Config::new(
picoserve::Timeouts {
start_read_request: Some(Duration::from_secs(5)),
@@ -52,9 +55,9 @@ async fn web_task(
picoserve::Router::new()
.nest("/api/v1", api_router)
.route("/", get(|| async { index() }))
.route("/styles.css", get_service(File::with_content_type_and_headers(&CSS_CONTENT_TYPE,
.route(STYLES_CSS_FILENAME, get_service(File::with_content_type_and_headers(&CSS_CONTENT_TYPE,
include_file_compress_deflate!("src/static/styles.css", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
.route("/app.js", get_service(File::with_content_type_and_headers(&JS_CONTENT_TYPE,
.route(APP_JS_FILENAME, get_service(File::with_content_type_and_headers(&JS_CONTENT_TYPE,
include_file_compress_deflate!("src/static/app.js", 5), DEFLATE_CACHEABLE_CONTENT_HEADERS)))
.nest("/images", image_router);
@@ -81,8 +84,8 @@ fn page(heading: &str, content: Markup) -> Markup {
(DOCTYPE)
html {
head {
link rel="stylesheet" type="text/css" href="/styles.css";
script src="app.js" {};
link rel="stylesheet" type="text/css" href=(STYLES_CSS_FILENAME);
script src=(APP_JS_FILENAME) {};
title { (heading) }
}
body {
@@ -191,7 +194,7 @@ fn latest() -> impl IntoResponse {
fn index() -> impl IntoResponse {
page("Wallas 22GB Wifi Extension", html! {
p .intro { "Beta version of ESP32C3 based Wifi extension to the Wallas 361062 Control Panel for the DT/GB Heaters" }
p #graph { "Waiting for data ..." }
p #latest { "Waiting for latest reading ..." }
// p { "Time is now " (get_now()) }
})
}

View File

@@ -59,6 +59,10 @@ mod database;
use database::database_spawn;
/// CARGO_PKG_VERSION
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub const VERSION_SAFE: &str = env!("CARGO_PKG_VERSION_SAFE");
/// SSID for WiFi network
const WIFI_SSID: &str = env!("WIFI_SSID");
@@ -78,7 +82,7 @@ const MAX_CONCURRENT_SOCKETS: usize = HTTPD_SOCKETS + DHCP_SOCKETS + DNS_SOCKETS
#[main]
async fn main(spawner: Spawner) {
info!("booting firmware");
info!("booting firmware version {}", VERSION);
if let Err(error) = main_fallible(spawner).await {
error!("Error while running firmware: {error:?}");

View File

@@ -4,20 +4,69 @@ function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
document.addEventListener("DOMContentLoaded", () => DOMLoaded(), false);
document.addEventListener("DOMContentLoaded", () => LoadCurrent(), false);
document.addEventListener("DOMContentLoaded", () => LoadGraph(), false);
async function DOMLoaded() {
await sleep(1000);
async function LoadCurrent() {
await sleep(500);
while (true) {
fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
if (json.time) {
time = json.time;
temperature = json.temperature;
target = json.target;
document.getElementById("latest").innerHTML = `Temperature was ${temperature}&deg;C at ${time} UTC, target temperature was ${target}&deg;C`;
}
})
fetch("/api/v1/latest").then((response) => response.json()).then((json) => {
if (json.time) {
time = json.time;
temperature = json.temperature;
target = json.target;
document.getElementById("latest").innerHTML = `Temperature was ${temperature}&deg;C at ${time} UTC, target temperature was ${target}&deg;C`;
}
})
await sleep(10000);
}
};
const svg_xmlns = "http://www.w3.org/2000/svg";
function appendSVGElement(parent, name, attributes) {
var elem = document.createElementNS(svg_xmlns, name);
for (var aname in attributes) {
elem.setAttributeNS(null, aname, attributes[aname]);
}
parent.appendChild(elem);
return elem;
}
async function LoadGraph() {
await sleep(1000);
while (true) {
fetch("/api/v1/allreadings").then((response) => response.json()).then((json) => {
if (json instanceof Array && json.length > 0) {
var graph = document.createDocumentFragment();
var svg = appendSVGElement(graph, "svg", {"width": "100%", "height": "100%", "viewBox": "0 0 100 35"});
var x = 99;
var target_points = [];
var temperature_points = [];
for (var i = json.length - 1; i > 0; i--) {
var r = json[i];
var temperature_y = 25 - r.temperature;
temperature_points.push(`${x},${temperature_y}`);
var target_y = 25 - r.target;
target_points.push(`${x},${target_y}`);
x = x - 1;
}
temperature_points_str = temperature_points.join(" ");
target_points_str = target_points.join(" ");
// zero line
appendSVGElement(svg, "polyline", {"stroke-width": "0.1", "fill": "none", "stroke":"#000080", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,25 99,25"});
// ten line
appendSVGElement(svg, "polyline", {"stroke-width": "0.05", "fill": "none", "stroke":"#000070", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,15 99,15"});
// twenty line
appendSVGElement(svg, "polyline", {"stroke-width": "0.05", "fill": "none", "stroke":"#000080", "stroke-linecap": "round", "stroke-dasharray": "5,2", "points": "1,5 99,5"});
target_polyline = appendSVGElement(svg, "polyline", {"stroke-width": "1", "fill": "none", "stroke": "#007900", "points": target_points_str});
temperature_polyline = appendSVGElement(svg, "polyline", {"stroke-width": "1", "fill": "none", "stroke": "#0079d9", "points": temperature_points_str});
document.getElementById("graph").replaceChildren(graph);
}
})
await sleep(10000);
}
}

View File

@@ -20,6 +20,14 @@ p.intro {
padding: 0.2em;
margin: 0.3em 10vw 0.3em 10vw;
}
p#graph {
border: 1px solid #B15C1B;
padding: 0.2em;
margin: 0.3em 15vw 0.3em 15vw;
height: 40vw;
line-height: 100%;
text-align: center;
}
p#latest {
border: 1px solid #B15C1B;
padding: 0.2em;