Skip to content

Commit

Permalink
First steps in adding a full viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
keesverruijt committed Sep 16, 2024
1 parent 4798d79 commit 345e5a9
Show file tree
Hide file tree
Showing 9 changed files with 744 additions and 8 deletions.
518 changes: 518 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ network-interface = "2.0.0"
num-derive = "0.4.2"
num-traits = "0.2.19"
protobuf = "3.5.1"
rust-embed = { version = "8.5.0", features = ["axum"] }
rust-embed = { version = "8.5.0", features = ["axum","interpolate-folder-path"] }
serde = { version = "1.0.206", features = ["derive", "serde_derive"] }
serde_json = "1.0.125"
serde_repr = "0.1.19"
Expand All @@ -38,3 +38,5 @@ tokio-shutdown = "0.1.4"

[build-dependencies]
protobuf-codegen = "3.5.1"
reqwest = { version = "0.12.7", features = ["rustls-tls", "blocking"] }
rust-embed = { version = "8.5.0", features = ["axum","interpolate-folder-path"] }
38 changes: 38 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{env, fs, path::PathBuf};

fn main() {
// Use this in build.rs
protobuf_codegen::Codegen::new()
Expand All @@ -9,4 +11,40 @@ fn main() {
// Specify output directory relative to Cargo output directory.
.cargo_out_dir("protos")
.run_from_script();

let body = reqwest::blocking::get(
"https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.0/dist/protobuf.min.js",
)
.unwrap()
.text()
.unwrap();
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut dest_path = PathBuf::from(&out_dir);
dest_path.push("web");
fs::create_dir_all(&dest_path).unwrap();
dest_path.push("protobuf.min.js");
fs::write(&dest_path, body).unwrap();

let body = reqwest::blocking::get(
"https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.0/dist/protobuf.js",
)
.unwrap()
.text()
.unwrap();
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut dest_path = PathBuf::from(&out_dir);
dest_path.push("web");
dest_path.push("protobuf.js");
fs::write(&dest_path, body).unwrap();

let out_dir = env::var_os("OUT_DIR").unwrap();
let mut src_path = PathBuf::from("src");
src_path.push("protos");
src_path.push("RadarMessage.proto");
let mut dest_path = PathBuf::from(&out_dir);
dest_path.push("web");
dest_path.push("RadarMessage.proto");
fs::copy(&src_path, &dest_path).unwrap();

println!("cargo::rerun-if-changed=build.rs");
}
2 changes: 1 addition & 1 deletion src/navico/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use bincode::deserialize;
use log::{debug, trace, warn};
use protobuf::Message;
use serde::Deserialize;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use std::time::{SystemTime, UNIX_EPOCH};
use std::{io, time::Duration};
use tokio::net::UdpSocket;
use tokio::sync::mpsc::Receiver;
Expand Down
8 changes: 7 additions & 1 deletion src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,16 @@ use crate::{

const RADAR_URI: &str = "/v1/api/radars";
const SPOKES_URI: &str = "/v1/api/spokes/";
const CONTROL_URI: &str = "/v1/api/control";
const CONTROL_URI: &str = "/v1/api/control/";

#[derive(RustEmbed, Clone)]
#[folder = "web/"]
struct Assets;

#[derive(RustEmbed, Clone)]
#[folder = "$OUT_DIR/web/"]
struct ProtoAssets;

#[derive(Error, Debug)]
pub enum WebError {
#[error("Socket operation failed")]
Expand Down Expand Up @@ -70,13 +74,15 @@ impl Web {
.unwrap();

let serve_assets = ServeEmbed::<Assets>::new();
let proto_assets = ServeEmbed::<ProtoAssets>::new();
let mut shutdown_rx = self.shutdown_tx.subscribe();
let shutdown_tx = self.shutdown_tx.clone(); // Clone as self used in with_state() and with_graceful_shutdown() below

let app = Router::new()
.route(RADAR_URI, get(get_radars))
.route(&format!("{}{}", SPOKES_URI, ":key"), get(spokes_handler))
.route(&format!("{}{}", CONTROL_URI, ":key"), get(control_handler))
.nest_service("/proto", proto_assets)
.nest_service("/", serve_assets)
.with_state(self)
.into_make_service_with_connect_info::<SocketAddr>();
Expand Down
11 changes: 7 additions & 4 deletions web/mayara.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import van from "./van-1.5.2.debug.js";

const {a, li} = van.tags
const {a, tr, td} = van.tags

const RadarEntry = (id, name) => li(a({href: "/control.html?id=" + id}, name))
const RadarEntry = (id, name) => tr(
td({ class: 'myr' }, a({ href: "/control.html?id=" + id }, name + " controller")),
td({ class: 'myr' }, a({ href: "/viewer.html?id=" + id }, name + " PPI viewer")),
)

function radarsLoaded(d) {
let c = Object.keys(d).length;
if (c > 0) {
let r = document.getElementById("radars");
r.innerHTML = "<div>" + c + " radars detected</div><ul></ul>";
r = r.getElementsByTagName('ul')[0];
r.innerHTML = "<div>" + c + " radars detected</div><table></table>";
r = r.getElementsByTagName('table')[0];
Object.keys(d).sort().forEach(function(v, i) { van.add(r, RadarEntry(v, d[v].name)); });
}

Expand Down
25 changes: 24 additions & 1 deletion web/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ html {
color: rgb(152, 217, 204);
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
background: #000000;
background-color: #000000;
}

td.myr {
background-color: lightgray;
}

div.myr_controller {
Expand Down Expand Up @@ -46,3 +50,22 @@ input:read-only {
background-color: #000000;
color: rgb(152, 217, 204);
}

/* PPI viewer */


div.myr_container {
width: 100%;
height: 600px;
}

div.myr_controller_left {
float: left;
height: auto;
overflow-y:auto;
}

div.myr_ppi {
float: left;
background-color: lightgreen;
}
24 changes: 24 additions & 0 deletions web/viewer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Mayara - Marine Yacht Radar Client Control</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link type="text/css" rel="stylesheet" href="style.css" />
<script type="module" src="control.js"></script>
<script type="module" src="viewer.js"></script>
<script src="//cdn.rawgit.com/dcodeIO/protobuf.js/7.4.0/dist/protobuf.min.js"></script>
</head>
<body>
<div class="myr_container">
<div id="myr_controller" class="myr_controller myr_controller_left">
<div id="myr_title">Radar Controls</div>
<div id="myr_error" class="myr_error" style="visibility: hidden;"></div>
<div id="myr_controls" class="myr_control"></div>
</div>
<div class="myr_ppi">
<canvas id="myr_canvas" width="600" height="600"></canvas>
</div>
</div>
</body>
</html>
122 changes: 122 additions & 0 deletions web/viewer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@

const prefix = 'myrv_';

window.onload = function () {
const urlParams = new URLSearchParams(window.location.search);
const id = urlParams.get('id');

loadRadars(id);
}

function loadRadars(id) {
fetch('/v1/api/radars')
.then(res => res.json())
.then(out => radarsLoaded(id, out))
.catch(err => restart(id));
}

function restart(id) {
setTimeout(loadRadars(id), 15000);
}
function radarsLoaded(id, d) {
myrv_radar = d[id];

if (myrv_radar === undefined || myrv_radar.controls === undefined) {
restart(id);
return;
}
myr_webSocket = new WebSocket(myr_radar.controlUrl);

myr_webSocket.onopen = (e) => {
console.log("websocket open: " + JSON.stringify(e));
}
myr_webSocket.onclose = (e) => {
console.log("websocket close: " + e);
setControl({ id: '0', value: '0' });
restart(id);
}
myr_webSocket.onmessage = (e) => {
let v = JSON.parse(e.data);
setControl(v);
myr_no_response_timeout.setTimeout();
}
}

function setControl(v) {
let i = get_element(v.id);
let control = myr_controls[v.id];
if (i && control) {
i.value = v.value;
console.log("<- " + control.name + " = " + v.value);
let n = i.parentNode.querySelector('.myr_numeric');
if (n) n.innerHTML = v.value;
let d = i.parentNode.querySelector('.myr_description');
if (d) {
let description = (control.descriptions) ? control.descriptions[v.value] : undefined;
if (!description) description = v.value;
d.innerHTML = description;
}
if (v.error) {
myr_error_message.raise(v.error);
}
}
}

function buildControls() {
let c = get_element('title');
c.innerHTML = "";
van.add(c, div(myr_radar.name + " Controls"));

c = get_element('controls');
c.innerHTML = "";
for (const [k, v] of Object.entries(myr_controls)) {
van.add(c, (v['isStringValue'])
? StringValue(k, v.name)
: ('validValues' in v)
? SelectValue(k, v.name, v['validValues'], v['descriptions'])
: ('maxValue' in v && v.maxValue <= 100)
? RangeValue(k, v.name, v.minValue, v.maxValue, 0, 'descriptions' in v)
: NumericValue(k, v.name));
if (v['isReadOnly']) {
get_element(k).setAttribute('readonly', 'true');
} else if (v['isStringValue']) {
van.add(get_element(k).parentNode, SetButton());
}
}
}

function do_change(e) {
let v = e.target;
let id = strip_prefix(v.id);
console.log("change " + e + " " + id + "=" + v.value);
let cv = JSON.stringify({ id: id, value: v.value });
myr_webSocket.send(cv);
console.log(myr_controls[id].name + "-> " + cv);
}

function do_button(e) {
let v = e.target.previousElementSibling;
let id = strip_prefix(v.id);
console.log("set_button " + e + " " + id + "=" + v.value);
let cv = JSON.stringify({ id: id, value: v.value });
myr_webSocket.send(cv);
console.log(myr_controls[id].name + "-> " + cv);
}

function do_input(e) {
let v = e.target;
console.log("input " + e + " " + v.id + "=" + v.value);
}

function get_element(id) {
let did = prefix + id;
let r = document.getElementById(did);
return r;
}

function strip_prefix(id) {
if (id.startsWith(prefix)) {
return id.substr(prefix.length);
}
return id;
}

0 comments on commit 345e5a9

Please sign in to comment.