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 formatValue(value, prec) { return Number(value.toPrecision(prec)).toString(); } 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; console.log(mod, param, func, 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; console.log('X', elem) changeFuncs[mod + ":" + (param || 'value')] = function (value) { let [repl, dot, fix] = pat.match(/\*(\.)?(\d)?/); if (dot) { formatted = value.toFixed(fix || 0); } else { formatted = formatValue(value, fix || 5); } elem.textContent = pat.replace(repl, formatted); console.log('Y', elem) } } if (func.includes("command")) { console.log(func, mod, param); addClickListener(elem, mod + ":" + param, sendCommand); } }) if (stateElement && clickElement) { // the clickElement needs a reference to the state for toggleTarget() clickElement.stateElement = stateElement; } } console.log('ACTIVATE', changeFuncs); websocket.send("activate"); } function handle_update(action, modpar, data) { // 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)) { // 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; element.state = state; if (state in colors) { element.style.fill = colors[state]; } else { element.style.fill = "#eaeaea" ; } } function writeToScreen(message) { const output = document.getElementById("outputtext"); output.value += message; output.scrollTop = output.scrollHeight; } function doConnect() { websocket = new WebSocket(wsaddr); 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 "+ wsaddr + "\n"); websocket.send("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 "+ wsaddr + "\n"); } else { writeToScreen("error: " + evt.data + '\n'); websocket.close(); } } function doSend(message) { // send a change or do message console.log('doSend'); if (! writable) { console.log('unprotected doSend') return; } console.log('>', message); websocket.send(message); } function warnViewOnly() { if (writable) return false; alert('please set the page ACTIVE to change things'); return true; } function toggleTarget(element, mod) { if (warnViewOnly()) return; 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) { if (warnViewOnly()) return; 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); }