348 lines
9.4 KiB
JavaScript
348 lines
9.4 KiB
JavaScript
let lampContainer = document.getElementById("leuchte");
|
|
let fillContainer = document.getElementById("fill");
|
|
let configInput = document.getElementById("config");
|
|
let configLoadError = document.getElementById("config_error");
|
|
let configOutput = document.getElementById("config_out");
|
|
let ctrlPane = document.getElementById("ctrlPane");
|
|
let serialErrorState = document.getElementById("serialState");
|
|
|
|
const warn = document.getElementById("configWarn");
|
|
|
|
// Inputs
|
|
let input_setup_top = document.getElementById("setup_top");
|
|
|
|
// Elements
|
|
const topElement = document.getElementById("module_top");
|
|
|
|
const config_ring1 = document.getElementById("ring1Confg");
|
|
const config_ring2 = document.getElementById("ring2Confg");
|
|
const config_ring3 = document.getElementById("ring3Confg");
|
|
const config_ring4 = document.getElementById("ring4Confg");
|
|
const config_ring5 = document.getElementById("ring5Confg");
|
|
|
|
const config_ring_list = [config_ring1, config_ring2, config_ring3, config_ring4, config_ring5];
|
|
|
|
// Serial port
|
|
let port;
|
|
const usbVendorId = 0x0403;
|
|
|
|
// URLs
|
|
|
|
const urls = {
|
|
rings: {
|
|
red: "res/images/licht_rot.jpg",
|
|
green: "res/images/licht_gruen.jpg",
|
|
blue: "res/images/licht_blau.jpg",
|
|
yellow: "res/images/licht_gelb.jpg",
|
|
white: "res/images/licht_weiss.jpg",
|
|
empty: "",
|
|
},
|
|
extras: {
|
|
top: {
|
|
normal: "res/images/top.jpg",
|
|
buzzer: "res/images/licht_summer.jpg"
|
|
}
|
|
},
|
|
selector: {
|
|
rings: {
|
|
red: "res/images/module_red.jpg",
|
|
green: "res/images/module_green.jpg",
|
|
blue: "res/images/module_blue.jpg",
|
|
yellow: "res/images/module_yellow.jpg",
|
|
white: "res/images/module_white.jpg",
|
|
empty: "res/images/no.png",
|
|
},
|
|
extras: {
|
|
top: {
|
|
normal: "res/images/no.png",
|
|
buzzer: "res/images/module_sounder.jpg"
|
|
}
|
|
}
|
|
},
|
|
ctrls: {
|
|
off: "res/images/buttons/Off.png",
|
|
on: "res/images/buttons/On.png",
|
|
alternate1: "res/images/buttons/Alternate1.png",
|
|
alternate2: "res/images/buttons/Alternate2.png"
|
|
}
|
|
}
|
|
|
|
// Attach event listeners
|
|
input_setup_top.addEventListener("change", (event) => {
|
|
myConfig.extras.top = event.target.value;
|
|
applyConfig();
|
|
});
|
|
|
|
let myConfig = {
|
|
rings: {
|
|
0: "green",
|
|
1: "yellow",
|
|
2: "red",
|
|
3: "empty",
|
|
4: "empty",
|
|
},
|
|
extras: {
|
|
top: "buzzer"
|
|
},
|
|
states: {
|
|
0: 0,
|
|
1: 0,
|
|
2: 0,
|
|
3: 0,
|
|
4: 0,
|
|
}
|
|
}
|
|
|
|
function validateConfig() {
|
|
// Check if there is a "hole" (empty) in the rings
|
|
// We want to detect ELEMENT EMPTY ELEMENT
|
|
// If the stack is "openended", thats fine
|
|
// Iterate over keys
|
|
let wasEmpty = false;
|
|
for (let i = 0; i < Object.keys(myConfig.rings).length; i++) {
|
|
console.log("Checking ring ", i);
|
|
isNowEmpty = myConfig.rings[i] === "empty";
|
|
if (wasEmpty && !isNowEmpty) {
|
|
warn.innerText = "Invalid config: Empty ring in the middle of the stack!";
|
|
return false;
|
|
}
|
|
console.log("Was empty: ", wasEmpty, " is empty: ", isNowEmpty);
|
|
wasEmpty = myConfig.rings[i] === "empty";
|
|
|
|
}
|
|
|
|
let amountOfPopulatedRings = 0;
|
|
for (let i = 0; i < Object.keys(myConfig.rings).length; i++) {
|
|
if (myConfig.rings[i] !== "empty") {
|
|
amountOfPopulatedRings++;
|
|
}
|
|
}
|
|
if(amountOfPopulatedRings === 0) {
|
|
warn.innerText = "Invalid config: No rings selected!";
|
|
return false;
|
|
}
|
|
|
|
if(amountOfPopulatedRings == 5 && myConfig.extras.top === "buzzer") {
|
|
warn.innerText = "Invalid config: Buzzer selected with all rings!";
|
|
return false;
|
|
}
|
|
|
|
warn.innerText = "";
|
|
return true;
|
|
}
|
|
|
|
function applyConfig() {
|
|
validateConfig();
|
|
// Remove state from output
|
|
let configCopy = JSON.parse(JSON.stringify(myConfig));
|
|
delete configCopy.states;
|
|
configOutput.innerText = JSON.stringify(configCopy);
|
|
topElement.src = urls.extras.top[myConfig.extras.top];
|
|
|
|
// Update radio buttons
|
|
for (let i = config_ring_list.length-1; i >= 0; i--) {
|
|
let currElm = config_ring_list[i];
|
|
for (let j = currElm.children.length-1; j >= 0; j--) {
|
|
const child = currElm.children[j];
|
|
if (child.type === "radio") {
|
|
if (child.value === myConfig.rings[i]) {
|
|
child.checked = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Populate lamp tower
|
|
// Iterate over rings keys
|
|
for (let i = Object.keys(myConfig.rings).length - 1; i >= 0; i--) {
|
|
const ring = myConfig.rings[i];
|
|
let ringElm = document.getElementById("ring" + (i + 1));
|
|
if (!ringElm) {
|
|
ringElm = document.createElement("img");
|
|
ringElm.id = "ring" + (i + 1);
|
|
fillContainer.appendChild(ringElm);
|
|
} else {
|
|
ringElm.src = urls.rings[ring];
|
|
}
|
|
|
|
if(myConfig.states[i] == 0) {
|
|
ringElm.classList.remove("is-lit");
|
|
ringElm.classList.remove("is-alternating-1");
|
|
ringElm.classList.remove("is-alternating-2");
|
|
} else if(myConfig.states[i] == 2) {
|
|
ringElm.classList.add("is-alternating-1");
|
|
ringElm.classList.remove("is-alternating-2");
|
|
ringElm.classList.remove("is-lit");
|
|
}else{
|
|
ringElm.classList.add("is-lit");
|
|
ringElm.classList.remove("is-alternating-1");
|
|
ringElm.classList.remove("is-alternating-2");
|
|
}
|
|
}
|
|
setTimeout(() => {
|
|
// Add divs for the controll elements in the same height as the rings
|
|
// But only show for the visible rings
|
|
for (let i = 0; i < Object.keys(myConfig.rings).length; i++) {
|
|
// Get top and bottom y position of the ring from html element
|
|
|
|
const ring = myConfig.rings[i];
|
|
let ringElm = document.getElementById("ring" + (i + 1));
|
|
if (ring === "empty") {
|
|
continue;
|
|
}
|
|
console.log("Ring ", i, " is ", ring);
|
|
// output coords
|
|
console.log("Ring ", i, " coords: ", ringElm.getBoundingClientRect());
|
|
console.log(ringElm)
|
|
let ctrlElm = document.getElementById("ctrl" + (i + 1));
|
|
if (!ctrlElm) {
|
|
ctrlElm = document.createElement("div");
|
|
ctrlElm.id = "ctrl" + (i + 1);
|
|
ctrlElm.classList.add("ctrl");
|
|
ctrlPane.appendChild(ctrlElm);
|
|
}
|
|
// Set position by top and bottom of the ring (ignore left and right)
|
|
ctrlElm.style.position = "absolute";
|
|
ctrlElm.style.top = ringElm.getBoundingClientRect().top + "px";
|
|
ctrlElm.style.bottom = ringElm.getBoundingClientRect().bottom + "px";
|
|
ctrlElm.style.width = "250px";
|
|
|
|
// Add buttons for the modes (off, on, alternate1, alternate2)
|
|
let actionList = ["off", "on", "alternate1", "alternate2"];
|
|
for (let j = 0; j < actionList.length; j++) {
|
|
let action = actionList[j];
|
|
let actionElm = document.getElementById("action" + (i + 1) + action);
|
|
if (!actionElm) {
|
|
actionElm = document.createElement("button");
|
|
actionElm.id = "action" + (i + 1) + action;
|
|
// actionElm.innerText = action;
|
|
actionElm.style.backgroundImage = `url(${urls.ctrls[action]})`;
|
|
actionElm.style.backgroundSize = "contain";
|
|
actionElm.style.backgroundRepeat = "no-repeat";
|
|
actionElm.classList.add("ctrl-action");
|
|
actionElm.addEventListener("click", () => {
|
|
writeAction(i, action);
|
|
});
|
|
ctrlElm.appendChild(actionElm);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// ctrlElm.style.left = "0px";
|
|
// ctrlElm.style.right = "0px";
|
|
}
|
|
}, 200);
|
|
|
|
}
|
|
|
|
function writeAction(ring, state){
|
|
actionToState = {
|
|
"off": 0,
|
|
"on": 1,
|
|
"alternate1": 2,
|
|
"alternate2": 3
|
|
}
|
|
// Check if the ring is empty and check if buzzer is enabled
|
|
if(myConfig.rings[ring] === "empty" && myConfig.extras.top === "buzzer") {
|
|
// Write to buzzer
|
|
|
|
return;
|
|
}
|
|
myConfig.states[ring] = actionToState[state];
|
|
applyConfig();
|
|
serial_send();
|
|
}
|
|
|
|
function loadConfig() {
|
|
try {
|
|
let config = JSON.parse(configInput.value);
|
|
// Merge configs, as states is not part of the input
|
|
config.states = myConfig.states;
|
|
myConfig = config;
|
|
applyConfig();
|
|
} catch (error) {
|
|
console.error("Error loading config: ", error);
|
|
configLoadError.innerText = "Error loading config: " + error;
|
|
} finally {
|
|
configLoadError.innerText = "Config loaded!";
|
|
}
|
|
|
|
input_setup_top.value = myConfig.extras.top;
|
|
|
|
}
|
|
|
|
function serial_check_env() {
|
|
if (!navigator.serial) {
|
|
console.error("Serial not supported!");
|
|
serialErrorState.innerText = "Serial not supported in this browser! Try using something chromium based!";
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
async function serial_connect() {
|
|
if (!serial_check_env()) {
|
|
return;
|
|
}
|
|
navigator.serial.requestPort({ filters: [{ usbVendorId }] }).then((serialPort) => {
|
|
serialPort.open({ baudRate: 9600 });
|
|
port = serialPort;
|
|
serialErrorState.innerText = "Connected to serial port!";
|
|
}).catch((error) => {
|
|
console.error("Error connecting to serial port: ", error);
|
|
serialErrorState.innerText = "Error connecting to serial port: " + error;
|
|
});
|
|
}
|
|
|
|
async function serial_send() {
|
|
if(port && port.writable) {
|
|
let data = `WR${myConfig.states[0]}${myConfig.states[1]}${myConfig.states[2]}${myConfig.states[3]}${myConfig.states[4]}\r`;
|
|
const encoder = new TextEncoder();
|
|
const writer = port.writable.getWriter();
|
|
await writer.write(encoder.encode(data));
|
|
writer.releaseLock();
|
|
}
|
|
}
|
|
|
|
// // Populate config section
|
|
// // From urls->selector->rings
|
|
// // Show as radio buttons
|
|
for (let i = config_ring_list.length-1; i >= 0; i--) {
|
|
let currElm = config_ring_list[i];
|
|
for (const key in urls.selector.rings) {
|
|
if (urls.selector.rings.hasOwnProperty(key)) {
|
|
const url = urls.selector.rings[key];
|
|
let radio = document.createElement("input");
|
|
radio.type = "radio";
|
|
radio.name = "ring" + (i + 1);
|
|
radio.value = key;
|
|
radio.id = "ring" + (i + 1) + key;
|
|
radio.addEventListener("change", (event) => {
|
|
myConfig.rings[i] = event.target.value;
|
|
applyConfig();
|
|
});
|
|
currElm.appendChild(radio);
|
|
|
|
let img = document.createElement("img");
|
|
img.src = url;
|
|
img.addEventListener("click", () => {
|
|
radio.checked = true;
|
|
myConfig.rings[i] = key;
|
|
applyConfig();
|
|
}
|
|
);
|
|
currElm.appendChild(img);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
applyConfig();
|
|
setTimeout(() => {
|
|
applyConfig();
|
|
}
|
|
, 200);
|