rcrf-wifi/commander.html
2025-08-14 20:51:28 +02:00

342 lines
14 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MBC rc boatjes - commander</title>
<style>
* {
--background: #000000;
--background-second: #222222;
--foreground: #CCCCCC;
}
body {
background-color: var(--background);
color: var(--foreground);
}
.login {
position: fixed;
top: 50px;
right: 50px;
bottom: 50px;
left: 50px;
background-color: var(--background-second);
text-align: center;
padding: calc(50vh - 150px) 50px;
height: 200px;
}
.btn {
padding: 5px 10px;
cursor: pointer;
display: inline-block;
}
#edit_config {
position: absolute;
background-color: var(--background-second);
top: 0;
left: 0;
right: 0;
/* height: 100%; */
}
#edit_config form {
margin: 40px auto;
}
</style>
</head>
<body>
<div class="login">
<input id="pass" type="password" value="1234" />
<button id="login">login</button>
</div>
<main>
<div id="refresh" class="btn">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-clockwise" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 3a5 5 0 1 0 4.546 2.914.5.5 0 0 1 .908-.417A6 6 0 1 1 8 2z"/>
<path d="M8 4.466V.534a.25.25 0 0 1 .41-.192l2.36 1.966c.12.1.12.284 0 .384L8.41 4.658A.25.25 0 0 1 8 4.466"/>
</svg>
</div>
<table>
<thead>
<caption>bootjes</caption>
<tr>
<th>id</th>
<th>naam</th>
<th>status</th>
</tr>
</thead>
<tbody id="boatjes"></tbody>
</table>
<table>
<thead>
<caption>clients</caption>
<tr>
<th>id</th>
<th>boat</th>
<th>status</th>
</tr>
</thead>
<tbody id="clients"></tbody>
</table>
<div id="edit_config" style="display:none">
<form>
<input type="hidden" name="boart" />
name: <input type="text" name="name" id="edit_name" /><br/>
stuur trim max: <input type="range" name="ch0_max" id="edit_ch0_max" max="8192" min="4096" /><br/>
stuur trim mid: <input type="range" name="ch0_mid" id="edit_ch0_mid" max="8192" min="0" /><br/>
stuur trim min: <input type="range" name="ch0_min" id="edit_ch0_min" max="4096" min="0" /><br/>
motor trim max: <input type="range" name="ch1_max" id="edit_ch1_max" max="8192" min="4096" /><br/>
motor trim mid: <input type="range" name="ch1_mid" id="edit_ch1_mid" max="8192" min="0" /><br/>
motor trim min: <input type="range" name="ch1_min" id="edit_ch1_min" max="4096" min="0" /><br/>
<button class="btn" onclick="updateConfig()">toepassen</button> <button class="btn" onclick="document.getElementById('edit_config').style.display = 'none'">anulleren</button>
</form>
</div>
</main>
<script>
const passEl = document.getElementById('pass');
const loginEl = document.getElementById('login');
const boatjesEl = document.getElementById('boatjes');
const clientsEl = document.getElementById('clients');
const kickIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-hand-thumbs-down\" viewBox=\"0 0 16 16\"><path d=\"M8.864 15.674c-.956.24-1.843-.484-1.908-1.42-.072-1.05-.23-2.015-.428-2.59-.125-.36-.479-1.012-1.04-1.638-.557-.624-1.282-1.179-2.131-1.41C2.685 8.432 2 7.85 2 7V3c0-.845.682-1.464 1.448-1.546 1.07-.113 1.564-.415 2.068-.723l.048-.029c.272-.166.578-.349.97-.484C6.931.08 7.395 0 8 0h3.5c.937 0 1.599.478 1.934 1.064.164.287.254.607.254.913 0 .152-.023.312-.077.464.201.262.38.577.488.9.11.33.172.762.004 1.15.069.13.12.268.159.403.077.27.113.567.113.856s-.036.586-.113.856c-.035.12-.08.244-.138.363.394.571.418 1.2.234 1.733-.206.592-.682 1.1-1.2 1.272-.847.283-1.803.276-2.516.211a10 10 0 0 1-.443-.05 9.36 9.36 0 0 1-.062 4.51c-.138.508-.55.848-1.012.964zM11.5 1H8c-.51 0-.863.068-1.14.163-.281.097-.506.229-.776.393l-.04.025c-.555.338-1.198.73-2.49.868-.333.035-.554.29-.554.55V7c0 .255.226.543.62.65 1.095.3 1.977.997 2.614 1.709.635.71 1.064 1.475 1.238 1.977.243.7.407 1.768.482 2.85.025.362.36.595.667.518l.262-.065c.16-.04.258-.144.288-.255a8.34 8.34 0 0 0-.145-4.726.5.5 0 0 1 .595-.643h.003l.014.004.058.013a9 9 0 0 0 1.036.157c.663.06 1.457.054 2.11-.163.175-.059.45-.301.57-.651.107-.308.087-.67-.266-1.021L12.793 7l.353-.354c.043-.042.105-.14.154-.315.048-.167.075-.37.075-.581s-.027-.414-.075-.581c-.05-.174-.111-.273-.154-.315l-.353-.354.353-.354c.047-.047.109-.176.005-.488a2.2 2.2 0 0 0-.505-.804l-.353-.354.353-.354c.006-.005.041-.05.041-.17a.9.9 0 0 0-.121-.415C12.4 1.272 12.063 1 11.5 1\"/></svg>"
const banIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-slash-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14m0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16\"/><path d=\"M11.354 4.646a.5.5 0 0 0-.708 0l-6 6a.5.5 0 0 0 .708.708l6-6a.5.5 0 0 0 0-.708\"/></svg>";
const openIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-key\" viewBox=\"0 0 16 16\"><path d=\"M0 8a4 4 0 0 1 7.465-2H14a.5.5 0 0 1 .354.146l1.5 1.5a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0L13 9.207l-.646.647a.5.5 0 0 1-.708 0L11 9.207l-.646.647a.5.5 0 0 1-.708 0L9 9.207l-.646.647A.5.5 0 0 1 8 10h-.535A4 4 0 0 1 0 8m4-3a3 3 0 1 0 2.712 4.285A.5.5 0 0 1 7.163 9h.63l.853-.854a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.646-.647a.5.5 0 0 1 .708 0l.646.647.793-.793-1-1h-6.63a.5.5 0 0 1-.451-.285A3 3 0 0 0 4 5\"/><path d=\"M4 8a1 1 0 1 1-2 0 1 1 0 0 1 2 0\"/></svg>";
const lockIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-lock\" viewBox=\"0 0 16 16\"><path d=\"M8 1a2 2 0 0 1 2 2v4H6V3a2 2 0 0 1 2-2m3 6V3a3 3 0 0 0-6 0v4a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2M5 8h6a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1\"/></svg>";
const freeIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-calendar-event\" viewBox=\"0 0 16 16\"><path d=\"M11 6.5a.5.5 0 0 1 .5-.5h1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-1a.5.5 0 0 1-.5-.5z\"/><path d=\"M3.5 0a.5.5 0 0 1 .5.5V1h8V.5a.5.5 0 0 1 1 0V1h1a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h1V.5a.5.5 0 0 1 .5-.5M1 4v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V4z\"/></svg>"
const logIcon = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-list-check\" viewBox=\"0 0 16 16\"> <path fill-rule=\"evenodd\" d=\"M5 11.5a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h9a.5.5 0 0 1 0 1h-9a.5.5 0 0 1-.5-.5M3.854 2.146a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 3.293l1.146-1.147a.5.5 0 0 1 .708 0m0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 1 1 .708-.708L2 7.293l1.146-1.147a.5.5 0 0 1 .708 0m0 4a.5.5 0 0 1 0 .708l-1.5 1.5a.5.5 0 0 1-.708 0l-.5-.5a.5.5 0 0 1 .708-.708l.146.147 1.146-1.147a.5.5 0 0 1 .708 0\"/></svg>"
const serverURL = 'ws://10.254.0.1:8080/';
var conn = false;
var boats = [];
var boatsLocked = [];
loginEl.addEventListener('click', login);
document.getElementById('refresh').addEventListener('click', refreshAll);
function login(e)
{
if (conn) conn.close();
conn = new WebSocket(serverURL, ['mbcRcRf']);
conn.addEventListener('open', onOpen);
conn.addEventListener('message', onMessage);
}
function onOpen(e)
{
conn.send(passEl.value + ";4675;" + Math.floor(new Date().getTime()/1000.0));
refreshAll()
}
function refreshAll()
{
reqBoats();
reqLockedBoats();
reqClients();
}
function onMessage(e)
{
console.log("recive: ", e.data);
let msg = e.data.split(':')
let data = msg[0];
msg.splice(0, 1);
msg = msg.join(':');
switch (data)
{
case 'boats':
boatjesEl.innerHTML = "";
addBoats(msg);
addBoats(boatsLocked);
boats = msg;
break;
case 'lockedBoats':
boatjesEl.innerHTML = "";
addBoats(boats);
addBoats(msg);
boatsLocked = msg;
passEl.parentElement.style.display = 'none';
break;
case 'clients':
addClients(msg);
break;
default:
console.warn('onMessage(): invlid data type (' + data + ')');
break;
}
}
function getDataFomEl(el)
{
while (el.tagName != "BODY" && el.dataset['id'] == null)
{
el = el.parentElement;
}
return el.dataset;
}
function kickClient(e)
{
let client = getDataFomEl(e.target);
console.log("kickClient", client);
conn.send(passEl.value + ";kick;" + client['id']);
}
function freeBoat(e)
{
let boat = getDataFomEl(e.target);
console.log("unlockBoat", boat);
conn.send(passEl.value + ";free;" + boat['id']);
}
function lockBoat(e)
{
let boat = getDataFomEl(e.target);
console.log("lockBoat", boat);
conn.send(passEl.value + ";lock;" + boat['id']);
}
function getBoatLog(e)
{
// let cmd = prompt("enter command");
let boat = getDataFomEl(e.target);
// console.log("send cmd", boat, cmd);
// conn.send(passEl.value + ";sendcmd;" + boat['id'] + ";" + cmd);
conn.send(passEl.value + ";sendcmd;" + boat['id'] + ";servotrim:0");
conn.send(passEl.value + ";sendcmd;" + boat['id'] + ";servotrim:1");
}
function reqBoats()
{
conn.send(passEl.value + ";boats");
}
function reqLockedBoats()
{
conn.send(passEl.value + ";locked");
}
function reqClients()
{
conn.send(passEl.value + ";clients");
}
function addBoats(boats)
{
if (boats == "")
{
return;
}
boats = boats.split(':');
for (let boat in boats)
{
if (boats[boat] != "")
{
addBoat(boats[boat]);
}
}
}
function addBoat(boat)
{
boat = boat.split(';')
if (boat.length == 3)
{
let boatRow = document.createElement("tr");
boatRow.className = "boat";
boatRow.dataset['id'] = boat[0];
boatRow.dataset['name'] = boat[1];
boatRow.dataset['state'] = boat[2];
let boatCells = [
document.createElement("td"),
document.createElement("td"),
document.createElement("td"),
document.createElement("td")
]
boatCells[0].innerText = boat[0];
boatCells[1].innerText = boat[1];
boatCells[2].innerText = boat[2];
let lockBtn = document.createElement("div");
lockBtn.className = "btn";
lockBtn.innerHTML = lockIcon;
lockBtn.ariaLabel = "lock";
lockBtn.addEventListener('click', lockBoat);
boatCells[3].append(lockBtn);
let freeBtn = document.createElement("div");
freeBtn.className = "btn";
freeBtn.innerHTML = freeIcon;
lockBtn.ariaLabel = "free";
freeBtn.addEventListener('click', freeBoat);
boatCells[3].append(freeBtn);
let logBtn = document.createElement("div");
logBtn.className = "btn";
logBtn.innerHTML = logIcon;
lockBtn.ariaLabel = "view log";
logBtn.addEventListener('click', getBoatLog);
boatCells[3].append(logBtn);
for (cell in boatCells)
{
boatRow.append(boatCells[cell]);
}
boatjesEl.append(boatRow);
}
else
{
console.warn("addBoat(): incorect number of args (" + boat.length.toString() + "; " + boat.toString() + ")");
}
}
function addClients(clients)
{
clientsEl.innerHTML = "";
clients = clients.split(':');
for (let client in clients)
{
if (clients[client] != "")
{
addClient(clients[client]);
}
}
}
function addClient(client)
{
client = client.split(';')
if (client.length == 3)
{
let clientRow = document.createElement("tr");
clientRow.className = "client";
clientRow.dataset['id'] = client[0];
clientRow.dataset['boat'] = client[1];
clientRow.dataset['state'] = client[2];
let clientCells = [
document.createElement("td"),
document.createElement("td"),
document.createElement("td"),
document.createElement("td")
]
clientCells[0].innerText = client[0];
clientCells[1].innerText = client[1];
clientCells[2].innerText = client[2];
let kickBtn = document.createElement("div");
kickBtn.className = "btn";
kickBtn.innerHTML = kickIcon;
kickBtn.addEventListener('click', kickClient);
clientCells[3].append(kickBtn);
for (cell in clientCells)
{
clientRow.append(clientCells[cell]);
}
clientsEl.append(clientRow);
}
else
{
console.warn("addClient(): incorect number of args (" + client.length.toString() + "; " + client.toString() + ")");
}
}
</script>
</body>
</html>