further generalize and add 'view only'
This commit is contained in:
+26
-8
@@ -1,27 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from flask import Flask, render_template, request, redirect, jsonify
|
||||
import sys
|
||||
import time
|
||||
import threading
|
||||
import socket
|
||||
from pathlib import Path
|
||||
from flask import Flask, render_template, request, redirect, jsonify
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
class Args:
|
||||
def __init__(self, webpage='dil5', port='80'):
|
||||
self.webpage = webpage
|
||||
def __init__(self, name, port='8050', webport='8051'):
|
||||
args = dict(
|
||||
html = 'common.html',
|
||||
svg = 'common.svg',
|
||||
css = 'common.css',
|
||||
js = 'secop.js',
|
||||
)
|
||||
common, _, variant = name.partition('_')
|
||||
names = [common, name] if variant else [name]
|
||||
for name in names:
|
||||
for key in list(args):
|
||||
print(f'{name}.{key}')
|
||||
if (Path('templates') / f'{name}.{key}').is_file():
|
||||
print(f'--- {name}.{key}')
|
||||
args[key] = f'{name}.{key}'
|
||||
hostname = socket.gethostname()
|
||||
if '.' not in hostname:
|
||||
hostname += '.psi.ch'
|
||||
args['wsaddr'] = f'{hostname}:{webport}'
|
||||
self.port = int(port)
|
||||
print(self.webpage, self.port)
|
||||
self.html = args.pop('html')
|
||||
self.args = args
|
||||
print(self.html, self.port, self.args)
|
||||
|
||||
args = Args(*sys.argv[1:])
|
||||
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template(f'{args.webpage}.html',
|
||||
svg_ui=f'{args.webpage}.svg',
|
||||
css=f'{args.webpage}.css',
|
||||
secop_js='secop.js')
|
||||
return render_template(f'{args.html}', **args.args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host="0.0.0.0", port=args.port, debug=True)
|
||||
|
||||
@@ -28,7 +28,6 @@ body, html {
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
background-color: #B8EBE5;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.control-panel p {
|
||||
@@ -70,7 +69,6 @@ body, html {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
resize: none;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.content-container {
|
||||
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Control Panel</title>
|
||||
<style>
|
||||
{% include css %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-container">
|
||||
{% include svg %}
|
||||
<div class="control-panel">
|
||||
<div>
|
||||
<input type=radio id="readonly", onClick="location.href='?'" checked>view only
|
||||
<input type=radio id="writable", onClick="location.href='?writable=1'">ACTIVE
|
||||
</div>
|
||||
<p> Status messages </p>
|
||||
<textarea name="outputtext" id="outputtext" rows="10" cols="30" readonly></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
var wsaddr = "ws://{{ wsaddr }}";
|
||||
var writable = urlParams.get('writable');
|
||||
console.log(writable);
|
||||
if (writable) {
|
||||
document.getElementById('readonly').checked = false;
|
||||
document.getElementById('writable').checked = true;
|
||||
}
|
||||
{% include js %}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,217 +0,0 @@
|
||||
<!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>
|
||||
{% include css %}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content-container">
|
||||
{% include svg_ui %}
|
||||
</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;
|
||||
}
|
||||
console.log(mdesc);
|
||||
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");
|
||||
}
|
||||
|
||||
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');
|
||||
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>
|
||||
@@ -2771,7 +2771,7 @@
|
||||
id="p2_text"
|
||||
x="77.788719"
|
||||
y="102.05273"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><text
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:2.82223px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
x="54.806484"
|
||||
@@ -2781,7 +2781,7 @@
|
||||
id="p1_text"
|
||||
x="54.806484"
|
||||
y="101.86591"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><text
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:2.82223px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
x="100.03925"
|
||||
@@ -2791,7 +2791,7 @@
|
||||
id="p3_text"
|
||||
x="100.03925"
|
||||
y="101.91296"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><text
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:2.82223px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
x="178.63339"
|
||||
@@ -2801,7 +2801,7 @@
|
||||
id="p6_text"
|
||||
x="178.63339"
|
||||
y="149.38992"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><text
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:2.82223px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
x="178.63339"
|
||||
@@ -2811,7 +2811,7 @@
|
||||
id="p7_-text"
|
||||
x="178.63339"
|
||||
y="166.88943"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><g
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><g
|
||||
id="p4"
|
||||
transform="matrix(0.38916925,0,0,0.3996349,143.66415,99.238056)"><circle
|
||||
style="fill:#eaeaea;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
@@ -2863,7 +2863,7 @@
|
||||
id="tspan99"
|
||||
x="150.38907"
|
||||
y="103.28608"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text><text
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text><text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:2.82223px;line-height:1.25;font-family:sans-serif;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583"
|
||||
x="217.12985"
|
||||
@@ -2873,7 +2873,7 @@
|
||||
id="tspan99-1"
|
||||
x="217.12985"
|
||||
y="103.00496"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x </tspan></text><g
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* ln/min</tspan></text><g
|
||||
id="p5"
|
||||
transform="matrix(0.38916925,0,0,0.3996349,214.90402,191.81556)"><circle
|
||||
style="fill:#eaeaea;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
@@ -2936,7 +2936,7 @@
|
||||
id="p8_text"
|
||||
x="151.83731"
|
||||
y="25.169991"
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">x mbar</tspan></text></g><g
|
||||
style="font-size:2.82223px;fill:#d5d5d5;fill-opacity:1;stroke-width:0.264583">* mbar</tspan></text></g><g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Divers"
|
||||
|
Before Width: | Height: | Size: 204 KiB After Width: | Height: | Size: 204 KiB |
+12
-7
@@ -21,11 +21,14 @@ function handle_describing(action, modpar, data) {
|
||||
let clickElement = null;
|
||||
elems.forEach(elem => {
|
||||
let [parmod, ...func] = elem.id.split('-');
|
||||
console.log(parmod, func);
|
||||
const [mod, param] = parmod.split(':');
|
||||
// console.log(mod, param, func);
|
||||
if (func.includes("click")) {
|
||||
clickElement = elem;
|
||||
addClickListener(elem, mod, toggleTarget);
|
||||
if (writable) {
|
||||
addClickListener(elem, mod, toggleTarget);
|
||||
}
|
||||
}
|
||||
if (func.includes("fault")) {
|
||||
changeFuncs[mod + ":status"] = function (value) {
|
||||
@@ -41,13 +44,15 @@ function handle_describing(action, modpar, data) {
|
||||
if (func.includes("text")) {
|
||||
const pat = elem.textContent;
|
||||
changeFuncs[mod + ":" + (param || 'value')] = function (value) {
|
||||
elem.textContent = pat.replace('*', value);
|
||||
elem.textContent = pat.replace('*', value.toFixed(1));
|
||||
elem.style.fill = "#d5d5d5";
|
||||
}
|
||||
}
|
||||
if (func.includes("command")) {
|
||||
console.log(func, mod, param);
|
||||
addClickListener(elem, mod + ":" + param, sendCommand);
|
||||
if (writable) {
|
||||
addClickListener(elem, mod + ":" + param, sendCommand);
|
||||
}
|
||||
}
|
||||
})
|
||||
if (stateElement && clickElement) {
|
||||
@@ -59,7 +64,7 @@ function handle_describing(action, modpar, data) {
|
||||
}
|
||||
|
||||
function handle_update(action, modpar, data) {
|
||||
if (modpar.startsWith('compressor')) console.log(action, modpar, data);
|
||||
// console.log(action, modpar, data);
|
||||
if (modpar in changeFuncs) {
|
||||
updateValues[modpar] = data[0];
|
||||
}
|
||||
@@ -89,7 +94,7 @@ function processUpdates() {
|
||||
let updates = updateValues;
|
||||
updateValues = {};
|
||||
for (const [modpar, value] of Object.entries(updates)) {
|
||||
if (modpar.startsWith('compressor')) console.log('process', modpar, value);
|
||||
// console.log('process', modpar, value);
|
||||
changeFuncs[modpar](value);
|
||||
}
|
||||
requestAnimationFrame(processUpdates);
|
||||
@@ -127,7 +132,7 @@ function writeToScreen(message) {
|
||||
}
|
||||
|
||||
function doConnect() {
|
||||
websocket = new WebSocket("ws://linse-dil5:8010/");
|
||||
websocket = new WebSocket(wsaddr);
|
||||
websocket.onopen = evt => onOpen(evt);
|
||||
websocket.onclose = evt => onClose(evt);
|
||||
websocket.onmessage = evt => handleMSG(evt.data);
|
||||
@@ -190,4 +195,4 @@ function addClickListener(element, arg, func) {
|
||||
window.onload = function () {
|
||||
doConnect();
|
||||
requestAnimationFrame(processUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user