313 lines
9.3 KiB
HTML
313 lines
9.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Pump Control</title>
|
|
<style>
|
|
body, html {
|
|
height: 100%;
|
|
margin: 0;
|
|
padding: 0;
|
|
background-color: lightgrey;
|
|
display: flex;
|
|
}
|
|
|
|
.svg-container {
|
|
flex: 2;
|
|
height: 100%;
|
|
position: relative;
|
|
display: flex;
|
|
}
|
|
|
|
#pump-svg {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.control-panel {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
padding: 10px;
|
|
background-color: #B8EBE5;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.control-panel p {
|
|
font-size: 20px;
|
|
font-family:verdana;
|
|
}
|
|
|
|
|
|
.control-panel h {
|
|
font-size: 32px;
|
|
font-family:verdana;
|
|
}
|
|
|
|
.button-container {
|
|
display: flex;
|
|
flex-direction: rows;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
font-size: 20px;
|
|
font-family:verdana;
|
|
}
|
|
|
|
.control-button {
|
|
padding: 10px;
|
|
background-color: #AFB0B1;
|
|
color: black;
|
|
border: none;
|
|
border-radius: 5px;
|
|
cursor: pointer;
|
|
width: 100px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.control-button:hover {
|
|
background-color: #DADBDC;
|
|
}
|
|
|
|
#outputtext {
|
|
width: 100%;
|
|
height: 200px;
|
|
resize: none;
|
|
border-radius: 12px;
|
|
}
|
|
|
|
.content-container {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 10px;
|
|
margin-bottom: 20px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="content-container">
|
|
<! object id="pump-svg" type="image/svg+xml" data="/static/UI5.svg"><! /object>
|
|
{% include svg_ui %}
|
|
<div class="control-panel">
|
|
<h1> DIL5 Control </h1>
|
|
<p> States </p>
|
|
<div class="button-container">
|
|
<button id = "test-btn" class="control-button" onclick="handleStateClick('TEST')">Test</button>
|
|
<button id = "condense-btn" class="control-button" onclick="handleStateClick('CONDENSE')">Condense</button>
|
|
<button id = "circulate-btn" class="control-button" onclick="handleStateClick('CIRCULATE')">Circulate</button>
|
|
<button id = "remove-btn" class="control-button" onclick="handleStateClick('REMOVE')">Remove</button>
|
|
<button id = "manual-btn" class="control-button" onclick="handleStateClick('MANUAL')">Manual</button>
|
|
</div>
|
|
<p> Status messages </p>
|
|
<textarea name="outputtext" id="outputtext" rows="10" cols="30" readonly></textarea>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
|
|
<script>
|
|
|
|
const svgElements = {};
|
|
|
|
var colors = {true: "#66ffba", false: "#ff3067", error: "#ffff00", clicked: "#ffffff", ok: "#ffffff"};
|
|
|
|
let active_state = "MANUAL";
|
|
|
|
var description;
|
|
var changeFuncs = {};
|
|
var updateValues = {};
|
|
|
|
function handle_describing(action, modpar, data) {
|
|
description = data;
|
|
for (const mod of Object.keys(data.modules)) {
|
|
const mdesc = data.modules[mod];
|
|
if (! mdesc) {
|
|
console.log('bad mod', mod)
|
|
continue;
|
|
}
|
|
const elems = document.querySelectorAll("[id^='" + mod + ":']");
|
|
let stateElement = null;
|
|
let clickElement = null;
|
|
elems.forEach(elem => {
|
|
let [parmod, ...func] = elem.id.split('-');
|
|
const [mod, param] = parmod.split(':');
|
|
// console.log(mod, param, func);
|
|
if (func.includes("click")) {
|
|
clickElement = elem;
|
|
addClickListener(elem, mod, toggleTarget);
|
|
}
|
|
if (func.includes("fault")) {
|
|
changeFuncs[mod + ":status"] = function (value) {
|
|
changeFill(elem, value[0] < 400 ? "ok" : "error");
|
|
}
|
|
}
|
|
if (func.includes("state")) {
|
|
stateElement = elem;
|
|
changeFuncs[mod + ":" + (param || 'value')] = function (value) {
|
|
changeFill(elem, value);
|
|
}
|
|
}
|
|
if (func.includes("text")) {
|
|
const pat = elem.textContent;
|
|
changeFuncs[mod + ":" + (param || 'value')] = function (value) {
|
|
elem.textContent = pat.replace('*', value);
|
|
elem.style.fill = "#d5d5d5";
|
|
}
|
|
}
|
|
if (func.includes("command")) {
|
|
console.log(func, mod, param);
|
|
addClickListener(elem, mod + ":" + param, sendCommand);
|
|
}
|
|
})
|
|
if (stateElement && clickElement) {
|
|
clickElement.stateElement = stateElement;
|
|
}
|
|
}
|
|
console.log('ACTIVATE', changeFuncs);
|
|
doSend("activate");
|
|
}
|
|
|
|
function handle_update(action, modpar, data) {
|
|
if (modpar.startsWith('compressor')) console.log(action, modpar, data);
|
|
if (modpar in changeFuncs) {
|
|
updateValues[modpar] = data[0];
|
|
}
|
|
}
|
|
|
|
handle_reply = handle_update;
|
|
handle_changed = handle_update;
|
|
|
|
function handle_error_update(action, modpar, data) {
|
|
}
|
|
|
|
function handleMSG(msg) {
|
|
const [action, modpar, ...tail] = msg.split(" ");
|
|
let data = tail.join(' ');
|
|
if (data) {
|
|
data = JSON.parse(data);
|
|
}
|
|
handler = window["handle_" + action];
|
|
if (handler) {
|
|
handler(action, modpar, data);
|
|
} else {
|
|
console.log(msg);
|
|
}
|
|
}
|
|
|
|
function processUpdates() {
|
|
let updates = updateValues;
|
|
updateValues = {};
|
|
for (const [modpar, value] of Object.entries(updates)) {
|
|
if (modpar.startsWith('compressor')) console.log('process', modpar, value);
|
|
changeFuncs[modpar](value);
|
|
}
|
|
requestAnimationFrame(processUpdates);
|
|
}
|
|
|
|
|
|
function handleStateClick(state) {
|
|
changeState("dil", state);
|
|
document.getElementById(state.toLowerCase() + "-btn").style.backgroundColor = "white";
|
|
document.getElementById(active_state.toLowerCase() + "-btn").style.backgroundColor = "rgb(175, 176, 177)";
|
|
active_state = state;
|
|
}
|
|
|
|
|
|
function updateState(state) {
|
|
document.getElementById(state.toLowerCase() + "-btn").style.backgroundColor = "white";
|
|
document.getElementById(active_state.toLowerCase() + "-btn").style.backgroundColor = "rgb(175, 176, 177)";
|
|
active_state = state;
|
|
}
|
|
|
|
function changeFill(element, state) {
|
|
stateElement = element.stateElement || element;
|
|
stateElement.state = state;
|
|
if (state in colors) {
|
|
stateElement.style.fill = colors[state];
|
|
} else {
|
|
stateElement.style.fill = "#eaeaea" ;
|
|
}
|
|
}
|
|
|
|
function writeToScreen(message) {
|
|
const output = document.getElementById("outputtext");
|
|
output.value += message;
|
|
output.scrollTop = output.scrollHeight;
|
|
}
|
|
|
|
function doConnect() {
|
|
websocket = new WebSocket("ws://linse-dil5:8010/");
|
|
websocket.onopen = evt => onOpen(evt);
|
|
websocket.onclose = evt => onClose(evt);
|
|
websocket.onmessage = evt => handleMSG(evt.data);
|
|
websocket.onerror = evt => onError(evt);
|
|
}
|
|
|
|
function onOpen(evt) {
|
|
writeToScreen("Connected to DIL5\n");
|
|
doSend("describe");
|
|
/*
|
|
for (var mod in ctr_state) {
|
|
readValue(mod);
|
|
}
|
|
startPolling();
|
|
*/
|
|
}
|
|
|
|
function changeState(mod, value) {
|
|
doSend(`change ${mod}:target "${value}"`);
|
|
//writeToScreen(`change ${mod}:target "${value}"\n`);
|
|
}
|
|
|
|
function onClose(evt) {
|
|
writeToScreen("disconnected\n");
|
|
stopPolling();
|
|
}
|
|
|
|
function onError(evt) {
|
|
if (evt.data === undefined) {
|
|
writeToScreen("Unable to connect to DIL5 as websocket is not connectable\n");
|
|
} else {
|
|
writeToScreen("error: " + evt.data + '\n');
|
|
stopPolling();
|
|
websocket.close();
|
|
}
|
|
|
|
}
|
|
|
|
function doSend(message) {
|
|
console.log('>', message);
|
|
websocket.send(message);
|
|
}
|
|
|
|
function toggleTarget(element, mod) {
|
|
stateElement = element.stateElement || element
|
|
const newState = stateElement.state === false;
|
|
console.log('click', element, mod, stateElement.state, newState);
|
|
changeFill(element, "clicked");
|
|
doSend(`change ${mod}:target ${newState}`);
|
|
}
|
|
|
|
function sendCommand(element, modcmd) {
|
|
console.log('command', modcmd);
|
|
doSend(`do ${modcmd}`);
|
|
}
|
|
|
|
function addClickListener(element, arg, func) {
|
|
element.addEventListener('click', () => { func(element, arg); });
|
|
element.classList.add('clickable');
|
|
}
|
|
|
|
window.onload = function () {
|
|
doConnect();
|
|
requestAnimationFrame(processUpdates);
|
|
}
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|