428 lines
13 KiB
HTML
428 lines
13 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>
|
|
|
|
<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>
|
|
|
|
//var ctr_ids = ["MV9", "turbopump", "MV10", "MV11", "MV12", "MV13", "MV8", "MVB", "MV2", "MV1", "MV3a", "MV3b", "GV1", "GV2", "MV14", "V1", "V2", "V9", "V5", "V4", "pump", "compressor"];
|
|
//var ctr_mod = ["MV9", "turbopump", "MV10", "MV11", "MV12", "MV13", "MV8", "MVB", "MV2", "MV1", "MV3a", "MV3b", "GV1", "GV2", "MV14", "V1", "V2", "V9", "V5", "V4", "pump", "compressor"];
|
|
var ctr_state = {
|
|
V1: 0, V2: 0, V4: 0, V5: 0, V9: 0,
|
|
MV1: 0, MV2: 0, MV3a: 0, MV3b: 0, MV9: 0, MV10: 0, MV11: 0, MV12: 0, MV13: 0,
|
|
GV1: 0, GV2: 0,
|
|
turbopump: 0, pump: 0, compressor: 0
|
|
}
|
|
// var ctr_state = new Array(ctr_mod.length).fill("init");
|
|
|
|
const svgElements = {};
|
|
let updateQueue = [];
|
|
|
|
var state_ids = ["Airpressure"];
|
|
var state_mod = ["Druckluft"];
|
|
|
|
var param_ids = ["P1txt", "P2txt", "P3txt", "P4txt", "P5txt", "speedtxt", "currenttxt"];
|
|
var param_unit = [" mbar", " mbar", " mbar", " mbar", " mbar", " Hz", " %"];
|
|
var param_mod = ["p1", "p2", "p3", "p4", "p5", "turbopumpspeed", "turbopumpcurrent"];
|
|
|
|
var colors = {"true": "#66ffba", "false": "#ff3067", "error": "#ff8000", "clicked": "#ffffff"};
|
|
|
|
var controlpanelObject = document.getElementById("pump-svg");
|
|
var svgDoc;
|
|
let active_state = "MANUAL";
|
|
|
|
let pollTimer;
|
|
const POLL_INTERVAL = 500;
|
|
|
|
function handleStateClick(state) {
|
|
changeState("stateMachine", 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 changeControlledValve(mod, state) {
|
|
if (mod in ctr_state) {
|
|
ctr_state[mod] = state;
|
|
if (state in colors) {
|
|
svgElements[mod].style.fill = colors[state];
|
|
} else {
|
|
svgElements[mod].style.fill = "#eaeaea" ;
|
|
}
|
|
}
|
|
}
|
|
|
|
function fillstateID(mod, state) {
|
|
const index = state_mod.indexOf(mod);
|
|
const element = svgDoc.querySelector("#" + state_ids[index]);
|
|
if (index !== -1 && element) {
|
|
element.style.fill = state === "true" ? "#66ffbaff" :
|
|
state === "false" ? "#ff3067ff" : "#eaeaea";
|
|
}
|
|
}
|
|
|
|
function editText(mod, text) {
|
|
const index = param_mod.indexOf(mod);
|
|
if (index !== -1) {
|
|
const textElement = svgDoc.querySelector("#" + param_ids[index]);
|
|
if (textElement) {
|
|
textElement.textContent = text + param_unit[index];
|
|
textElement.style.fill = "#d5d5d5";
|
|
}
|
|
}
|
|
}
|
|
|
|
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 => updateQueue.push(evt.data);
|
|
websocket.onerror = evt => onError(evt);
|
|
}
|
|
|
|
function onOpen(evt) {
|
|
writeToScreen("Connected to DIL5\n");
|
|
//doSend("activate");
|
|
for (var mod in ctr_state) {
|
|
readValue(mod);
|
|
}
|
|
startPolling();
|
|
}
|
|
|
|
function changeTarget(mod, value) {
|
|
doSend(`change ${mod}:target ${value}`);
|
|
|
|
}
|
|
|
|
function changeState(mod, value) {
|
|
doSend(`change ${mod}:target "${value}"`);
|
|
//writeToScreen(`change ${mod}:target "${value}"\n`);
|
|
}
|
|
|
|
function readValue(mod) {
|
|
doSend("read " + mod + ":value");
|
|
}
|
|
|
|
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) {
|
|
websocket.send(message);
|
|
}
|
|
|
|
function handleUpdate(msg) {
|
|
const teile = msg.split(":");
|
|
const mod = teile[0].split(" ")[1];
|
|
const param = teile[1].split(" ")[0];
|
|
const value = teile[1].split(",")[0].split('[')[1];
|
|
|
|
if (param === "value") {
|
|
if (param_mod.includes(mod)) {
|
|
editText(mod, value);
|
|
} else if (ctr_mod.includes(mod)) {
|
|
console.log(mod, value);
|
|
changeControlledValve(mod, value);
|
|
} else if (state_mod.includes(mod)) {
|
|
fillstateID(mod, value);
|
|
} else if (mod == "stateMachine"){
|
|
console.log("StateMaschine")
|
|
}
|
|
} else if (param === "_speed") {
|
|
editText("turbopumpspeed", value);
|
|
} else if (param === "_current") {
|
|
editText("turbopumpcurrent", value);
|
|
}
|
|
}
|
|
|
|
function handleChange(msg) {
|
|
const teile = msg.split(":");
|
|
const mod = teile[0].split(" ")[1];
|
|
const value = teile[1].split(",")[0].split('[')[1];
|
|
changeControlledValve(mod, value);
|
|
}
|
|
|
|
function handleReply(msg) {
|
|
const teile = msg.split(" ");
|
|
const modt = teile[1].split(':')
|
|
const mod = modt[0]
|
|
const param = modt[1]
|
|
let value = JSON.parse(teile.slice(2).join(' '))[0]
|
|
|
|
if (param === "status") {
|
|
console.log('S', mod, value)
|
|
}
|
|
if (param === "status" && value[0] >= 400) {
|
|
value = 'error';
|
|
param = 'value';
|
|
}
|
|
if (param === "value") {
|
|
value = value.toString();
|
|
if (mod in ctr_state) {
|
|
changeControlledValve(mod, value);
|
|
} else if (param_mod.includes(mod)) {
|
|
editText(mod, value);
|
|
} else if (state_mod.includes(mod)) {
|
|
fillstateID(mod, value);
|
|
} else if (mod == "stateMachine"){
|
|
if (value == "5"){
|
|
if (active_state != "TEST"){
|
|
updateState("TEST")
|
|
}
|
|
} else if (value == "4"){
|
|
if (active_state != "MANUAL"){
|
|
updateState("MANUAL")
|
|
}
|
|
|
|
}
|
|
}
|
|
} else if (param === "_speed") {
|
|
editText("turbopumpspeed", value);
|
|
} else if (param === "_current") {
|
|
editText("turbopumpcurrent", value);
|
|
} else if (param === "stateMachine") {
|
|
console.log(msg)
|
|
updateState(value)
|
|
}
|
|
}
|
|
|
|
function handleMSG(msg) {
|
|
if (msg.startsWith("changed")) {
|
|
handleChange(msg);
|
|
} else if (msg.startsWith("update")) {
|
|
handleUpdate(msg);
|
|
} else if (msg.startsWith("reply")) {
|
|
handleReply(msg);
|
|
} else if (msg.startsWith("error_update")) {
|
|
// writeToScreen("Error in update: " + msg + '\n');
|
|
console.log(msg);
|
|
}
|
|
}
|
|
|
|
function processUpdateQueue() {
|
|
if (updateQueue.length > 0) {
|
|
const queueCopy = updateQueue.slice();
|
|
updateQueue = [];
|
|
queueCopy.forEach(msg => handleMSG(msg));
|
|
}
|
|
requestAnimationFrame(processUpdateQueue);
|
|
}
|
|
|
|
function addClickListener(element, id) {
|
|
if (! element) {
|
|
console.log(id, 'does not exist');
|
|
return;
|
|
}
|
|
element.addEventListener('click', () => {
|
|
console.log("click");
|
|
const state = ctr_state[id];
|
|
let newState = state === "false";
|
|
console.log(id, state, newState);
|
|
changeControlledValve(id, "clicked");
|
|
changeTarget(id, newState);
|
|
});
|
|
element.classList.add('clickable');
|
|
}
|
|
|
|
function startPolling() {
|
|
pollTimer = setInterval(() => {
|
|
pollAllStates();
|
|
}, POLL_INTERVAL);
|
|
}
|
|
|
|
|
|
function stopPolling() {
|
|
clearInterval(pollTimer);
|
|
}
|
|
|
|
function sleep(ms) {
|
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
}
|
|
|
|
async function pollAllStates() {
|
|
for (const mod in ctr_state) {
|
|
doSend(`read ${mod}:value`);
|
|
doSend(`read ${mod}:status`);
|
|
await sleep(50);
|
|
}
|
|
await sleep(50);
|
|
doSend('read turbopump:_speed');
|
|
await sleep(50);
|
|
doSend('read turbopump:_current');
|
|
await sleep(50);
|
|
doSend('read stateMachine:value');
|
|
|
|
for (const mod of state_mod) {
|
|
doSend(`read ${mod}:value`);
|
|
await sleep(50);
|
|
}
|
|
|
|
for (const mod of param_mod) {
|
|
doSend(`read ${mod}:value`);
|
|
await sleep(50);
|
|
}
|
|
|
|
}
|
|
|
|
function pollSingle(mod) {
|
|
doSend(`read ${mod}:value`);
|
|
}
|
|
|
|
var winLoaded = false;
|
|
var svgLoaded = false;
|
|
|
|
function afterLoad() {
|
|
doConnect();
|
|
svgDoc = controlpanelObject.contentDocument;
|
|
for (var id in ctr_state) {
|
|
element = svgDoc.querySelector("#" + id);
|
|
svgElements[id] = element;
|
|
addClickListener(element, id);
|
|
element = svgDoc.querySelector("#" + id + "fill");
|
|
if (element) {
|
|
svgElements[id] = element;
|
|
// addClickListener(element, id);
|
|
}
|
|
};
|
|
requestAnimationFrame(processUpdateQueue);
|
|
}
|
|
|
|
window.onload = function () { winLoaded = true; if (svgLoaded) afterLoad(); }
|
|
controlpanelObject.onload = function () { svgLoaded = true; if (winLoaded) afterLoad(); }
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|