itender/src/web/Setup.ts
Tobias Hopp e181b42c6c fix
Took 11 minutes
2022-11-28 14:16:09 +01:00

491 lines
20 KiB
TypeScript

import {Pane} from "./Pane";
import {Modal} from "./Modal";
import {ButtonType} from "./ButtonType";
import {WebHandler} from "./WebHandler";
import {WebSocketPayload} from "../WebSocketPayload";
import {WebSocketEvent} from "../WebSocketEvent";
import {WebWebSocketHandler} from "./WebWebSocketHandler";
import {IContainer} from "../database/IContainer";
import {SensorType} from "../SensorType";
import {RequestType} from "../RequestType";
export class Setup {
public static onConfigUpdate(payload: WebSocketPayload) {
// Setup containers updated
const ledCheckbox = document.getElementById("ledCheckbox") as HTMLInputElement;
const ledGPIO = document.getElementById("ledGPIO") as HTMLInputElement;
const ambientColor = document.getElementById("ambientColor") as HTMLInputElement;
const allowRemoteCheckbox = document.getElementById("allowRemoteCheckbox") as HTMLInputElement;
const hotspotCheckbox = document.getElementById("hotspotCheckbox") as HTMLInputElement;
ledCheckbox.checked = !!payload.data["led_enabled"];
allowRemoteCheckbox.checked = !!payload.data["remote_enabled"];
hotspotCheckbox.checked = !!payload.data["hotspot_enabled"];
if (payload.data["led_gpio"]) {
ledGPIO.value = payload.data["led_gpio"];
}
if (payload.data["ambient_color"]) {
ambientColor.value = payload.data["ambient_color"];
}
(document.getElementById("setup_cancelBtn") as HTMLButtonElement).disabled = !payload.data["setupDone"];
if (!payload.data["setupDone"]) {
let modal = new Modal("setup", "Willkommen!");
let txt = document.createElement("p");
txt.innerHTML = `Dieser iTender ist noch nicht eingerichtet.<br>Um ihn zu nutzen, ist eine Grund-Konfiguration (Setup) nötig.<br><br>Das Setup lässt sich später auch jederzeit aus dem Menü erneut aufrufen.<br>Um die Einrichtung des Gerätes abzuschließen, muss mindestens ein Behälter mit Pumpe (optional auch Sensor) hinzugefügt werden.<br><br>`;
modal.addContent(txt);
let btn = document.createElement("button");
btn.classList.add("btn", "btn-primary");
btn.innerText = "Weiter";
btn.onclick = () => {
txt.innerHTML = `Auf der folgenden Seite können nun verschiedene Module eingerichtet werden.<br><br>
<strong>LED-Modul</strong><br>
In diesem Modul kann die Ambiente-Farbe sowie der LED-Streifen aktiviert werden.<br><br>
<strong>Erweiterte Einstellungen</strong><br>
Hier lässt sich konfigurieren, ob die Nutzung der Remote-Bedienung erlaubt ist, oder ein Hotspot aktiviert werden soll, falls keine WiFI-Verbindung vorliegt.<br><br>
<strong>Behälter-Modul</strong><br>
Dort werden die Behälter definiert, welche in den iTender gestellt werden.<br>Dort müssen GPIO-Pins der Pumpe, etwaige Sensoren-Typen und Pins definiert werden.<br>Außerdem wird das Volumen eingestellt.<br><br>`;
btn.innerText = "Einrichtung starten";
btn.onclick = () => {
modal.close();
}
}
modal.addContent(btn);
modal.open();
}
}
static async openSetup() {
// new
WebHandler.openPane(Pane.SETUP);
let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
menuBtn.disabled = true;
const cancelBtn = document.getElementById("setup_cancelBtn") as HTMLButtonElement;
cancelBtn.onclick = () => {
let payload = new WebSocketPayload(WebSocketEvent.SETUP, false, false);
WebWebSocketHandler.send(payload);
}
const containerAddBtn = document.getElementById("containerAddBtn") as HTMLButtonElement;
containerAddBtn.onclick = Setup.addSetupContainer;
const setupSaveBtn = document.getElementById("setup_saveBtn") as HTMLButtonElement;
setupSaveBtn.onclick = () => {
const containers = document.getElementById("setupContainers") as HTMLDivElement;
let errorModal = new Modal("setup", "Fehler!");
let ele = document.createElement("p");
ele.innerHTML = `Das Setup konnte nicht abgeschlossen werden.<br>`;
errorModal.addContent(ele);
errorModal.addContent(document.createElement("br"));
errorModal.addButton(ButtonType.PRIMARY, "Schließen", () => {
errorModal.close();
});
if (containers.childNodes.length == 0) {
ele.innerHTML += `Es muss mindestens ein Behälter hinzugefügt worden sein.`;
errorModal.open();
return;
}
for (let c of containers.getElementsByTagName("div")) {
let selects = c.getElementsByTagName("select");
if (selects[0].value == "-1") {
ele.innerHTML += `Es müssen alle Pumpen-Pins gesetzt sein.`;
errorModal.open();
c.classList.add("error");
setTimeout(() => {
c.classList.remove("error");
}, 2500);
return;
}
if (selects[1].value != "-1" && (selects[2].value == "-1" || selects[3].value == "-1")) {
ele.innerHTML += `Wenn ein Sensor-Typ definiert ist, müssen alle Sensor-Pins gesetzt sein.`;
errorModal.open();
c.classList.add("error");
setTimeout(() => {
c.classList.remove("error");
}, 2500);
return;
}
}
if (!Setup.checkContainers()) {
ele.innerHTML = `Einige Pins sind doppelt belegt.<br>Jeder GPIO-Pin kann nur einmal belegt werden!`;
errorModal.open();
return;
}
setupSaveBtn.disabled = true;
let saveModal = new Modal("setup", "Setup");
let txt = document.createElement("p");
txt.innerHTML = `Die Einstellungen werden gespeichert...<br>
<div class="lds-ellipsis"><div></div><div></div><div></div><div></div></div>`;
saveModal.addContent(txt);
saveModal.open();
const ledCheckbox = document.getElementById("ledCheckbox") as HTMLInputElement;
const ledGPIO = document.getElementById("ledGPIO") as HTMLInputElement;
const ambientColor = document.getElementById("ambientColor") as HTMLInputElement;
const allowRemoteCheckbox = document.getElementById("allowRemoteCheckbox") as HTMLInputElement;
const hotspotCheckbox = document.getElementById("hotspotCheckbox") as HTMLInputElement;
let cons: { pumpPin: number; sensorType: SensorType; sensor1: number; sensor2: number; volume: number; }[] = [];
for (let c of (document.getElementById("setupContainers") as HTMLDivElement).getElementsByTagName("div")) {
let sensorType = c.getElementsByTagName("select")[1].value;
let type;
if (sensorType == "-1")
type = SensorType.NONE;
else if (sensorType == "0")
type = SensorType.ULTRASOUND;
else
type = SensorType.LOADCELL;
cons.push({
"pumpPin": parseInt(c.getElementsByTagName("select")[0].value),
"sensorType": type,
"sensor1": parseInt(c.getElementsByTagName("select")[2].value),
"sensor2": parseInt(c.getElementsByTagName("select")[3].value),
"volume": parseInt(c.getElementsByTagName("select")[4].value)
});
}
let payload = new WebSocketPayload(WebSocketEvent.CONTAINERS, false, cons);
WebWebSocketHandler.send(payload);
payload = new WebSocketPayload(WebSocketEvent.CONFIG, false, {
"led_enabled": ledCheckbox.checked,
"remote_enabled": allowRemoteCheckbox.checked,
"hotspot_enabled": hotspotCheckbox.checked,
"led_gpio": parseInt(ledGPIO.value),
"ambient_color": ambientColor.value
});
menuBtn.disabled = false;
WebWebSocketHandler.send(payload).then(() => {
setTimeout(() => {
saveModal.close();
setupSaveBtn.disabled = false;
this.startTare();
}, 1000);
}).catch(() => {
setupSaveBtn.disabled = false;
txt.innerHTML = `Fehler beim Speichern.<br>iTender hat nicht reagiert.`;
setTimeout(() => saveModal.close(), 2500);
});
}
}
public static startTare() {
let tareModal = new Modal("tare", "Einmessung Sensoren");
let txt = document.createElement("p");
txt.innerHTML = `Damit alle Sensoren korrekte Werte liefern, sollte eine Einmessung durchgeführt werden.<br>
Während der Einmessung müssen die Behälter einmal geleert und gefüllt werden.<br><br>`;
tareModal.addContent(txt);
tareModal.addButton(ButtonType.PRIMARY, "Später", () => {
tareModal.close();
});
let ul;
tareModal.addButton(ButtonType.PRIMARY, "Starten", async () => {
tareModal.close();
let modal = new Modal("tare", "Einmessung");
let txt = document.createElement("p");
txt.innerHTML = `Messung Teil 1<br>
Bitte den <strong>Inhalt der Behälter entfernen</strong><br>
Die Gewichtssensoren werden beim Bestätigen austariert<br><br>Zum fortfahren Tarieren drücken.<br>`;
modal.addContent(txt);
ul = document.createElement("ul");
modal.addContent(ul);
let tareInterval: NodeJS.Timer;
let btn = document.createElement("button");
btn.classList.add("btn", "btn-primary");
btn.innerText = "Tarieren";
btn.style.marginTop = "3%";
btn.onclick = () => {
let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 0});
WebWebSocketHandler.send(payload);
txt.innerHTML = `Messung Teil 2<br>
Bitte nun alle <strong>Behälter mit Inhalt füllen</strong> und wieder einsetzen.<br>
Die Gewichtssensoren werden beim Bestätigen austariert.<br><br>Zum fortfahren Tarieren drücken.<br>`;
btn.onclick = () => {
let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 1});
WebWebSocketHandler.send(payload);
btn.onclick = () => {
modal.close();
clearInterval(tareInterval);
};
};
};
modal.addContent(btn);
await modal.open();
tareInterval = setInterval(() => WebWebSocketHandler.request(RequestType.CONTAINERS).then((payload) => {
if (!ul) return;
ul.innerHTML = "";
let containers = payload.data as IContainer[];
for (let c of containers) {
if (c.sensorType == SensorType.NONE) continue;
let li = document.createElement("li");
li.innerText = `Behälter ${c.slot}: ${c.rawData.toFixed(3)} [${c.sensorType}]`;
ul.append(li);
}
}), 250);
});
tareModal.open();
}
public static addSetupContainer() {
let setupContainers = document.getElementById("setupContainers") as HTMLDivElement;
let con = document.createElement("div");
let containerName = document.createElement("p");
containerName.innerText = "Behälter " + (setupContainers.getElementsByTagName("div").length + 1);
con.classList.add("setupContainer");
con.append(containerName);
let selectPin = document.createElement("select");
selectPin.style.display = "none";
selectPin.classList.add("input");
selectPin.style.display = "inline";
let noSel = document.createElement("option") as HTMLOptionElement;
noSel.innerText = "Bitte wählen";
noSel.value = "-1";
noSel.disabled = true;
selectPin.append(noSel.cloneNode(true));
selectPin.selectedIndex = 0;
// 3,5,7,8,10,11,12,13,15,16,18,19,21,22,23,24,26,29,31,32,33,35,36,37,38,40
const pins = [3, 7, 8, 10, 11, 12, 13, 15, 16, 18, 19, 21, 22, 23, 24, 26, 29, 31, 32, 33, 35, 36, 37, 38];
for (let pin of pins) {
let pinEle = document.createElement("option") as HTMLOptionElement;
pinEle.innerText = "" + pin;
pinEle.value = "" + pin;
selectPin.append(pinEle);
}
let nSelect;
let pumpLabel = document.createElement("label");
pumpLabel.innerText = "Pumpen Pin";
con.append(pumpLabel);
nSelect = selectPin.cloneNode(true);
nSelect.onchange = () => Setup.checkContainers();
nSelect.selectedIndex = 0;
con.append(nSelect);
con.append(document.createElement("br"));
// Sensor Art
let sensorTypeLabel = document.createElement("label");
sensorTypeLabel.innerText = "Sensor Art ";
con.append(sensorTypeLabel);
let sensorType = document.createElement("select");
sensorType.classList.add("noCheckup");
sensorType.classList.add("input");
let sensorTypeNone = document.createElement("option") as HTMLOptionElement;
sensorTypeNone.innerText = "Kein Sensor";
sensorTypeNone.value = "-1";
sensorType.append(sensorTypeNone);
let sensorTypeUltrasound = document.createElement("option") as HTMLOptionElement;
sensorTypeUltrasound.innerText = "Ultraschall";
sensorTypeUltrasound.value = "0";
sensorType.append(sensorTypeUltrasound);
let sensorTypeScale = document.createElement("option") as HTMLOptionElement;
sensorTypeScale.innerText = "Wägezelle";
sensorTypeScale.value = "1";
sensorType.append(sensorTypeScale);
con.append(sensorType);
con.append(document.createElement("br"));
// Sensor 1
let sensor1Label = document.createElement("label");
sensor1Label.innerText = "Sensor 1 Pin";
con.append(sensor1Label);
let sensor1Select = selectPin.cloneNode(true) as HTMLSelectElement;
sensor1Select.selectedIndex = 0;
sensor1Select.disabled = true;
sensor1Select.onchange = () => Setup.checkContainers();
con.append(sensor1Select);
con.append(document.createElement("br"));
// Sensor 2
let sensor2Label = document.createElement("label");
sensor2Label.innerText = "Sensor 2 Pin";
con.append(sensor2Label);
let sensor2Select = selectPin.cloneNode(true) as HTMLSelectElement;
sensor2Select.selectedIndex = 0;
sensor2Select.disabled = true;
sensor2Select.onchange = () => Setup.checkContainers();
con.append(sensor2Select);
sensorType.onchange = () => {
if (sensorType.value == "0") {
sensor1Label.innerText = "Trigger Pin";
sensor2Label.innerText = "Trigger Pin";
sensor1Select.disabled = false;
sensor2Select.disabled = false;
} else if (sensorType.value == "1") {
sensor1Label.innerText = "Clock Pin";
sensor2Label.innerText = "Data Pin";
sensor1Select.disabled = false;
sensor2Select.disabled = false;
} else {
sensor1Label.innerText = "Sensor 1 Pin";
sensor2Label.innerText = "Sensor 2 Pin";
sensor1Select.disabled = true;
sensor2Select.disabled = true;
}
};
con.append(document.createElement("br"));
// Volume
let volumeLabel = document.createElement("label");
volumeLabel.innerText = "Volumen (ml) ";
con.append(volumeLabel);
let volumeSelect = document.createElement("select");
volumeSelect.classList.add("noCheckup");
volumeSelect.classList.add("input");
const mls = [50, 100, 200, 250, 300, 330, 500, 750, 1000, 1250, 1500, 2000, 2500, 5000, 10000];
for (let ml of mls) {
let pinEle = document.createElement("option") as HTMLOptionElement;
pinEle.innerText = "" + ml;
pinEle.value = "" + ml;
volumeSelect.append(pinEle);
volumeSelect["volume"] = volumeSelect;
}
volumeSelect.selectedIndex = 7;
con.append(volumeSelect);
let removeBtn = document.createElement("button");
removeBtn.classList.add("btn", "btn-danger");
removeBtn.onclick = () => {
con.classList.add("removeSlowly");
setTimeout(() => {
con.remove();
let i = 1;
for (let elementsByTagNameElement of setupContainers.getElementsByTagName("div")) {
let e = elementsByTagNameElement.getElementsByTagName("p")[0] as HTMLParagraphElement;
e.innerText = "Behälter " + i;
i++;
}
}, 750);
}
removeBtn.style.float = "right";
removeBtn.innerText = "Entfernen";
con.append(removeBtn);
setupContainers.append(con);
}
public static onContainerUpdate(payload: WebSocketPayload) {
let containerDiv = document.getElementById("setupContainers") as HTMLDivElement;
containerDiv.innerHTML = "";
let containers = payload.data["content"] as IContainer[];
for (let c of containers) {
Setup.addSetupContainer();
}
let i = 0;
let list = containerDiv.getElementsByTagName("div");
for (let c of containers) {
let current = list[i] as HTMLDivElement;
let selects = current.getElementsByTagName("select");
(selects[0] as HTMLSelectElement).value = c.pumpPin.toString();
let type;
if (c.sensorType == SensorType.NONE)
type = "-1";
else if (c.sensorType == SensorType.ULTRASOUND)
type = "0";
else
type = "1";
(selects[1] as HTMLSelectElement).value = type;
(selects[2] as HTMLSelectElement).value = c.sensorPin1.toString();
(selects[3] as HTMLSelectElement).value = c.sensorPin2.toString();
(selects[4] as HTMLSelectElement).value = c.volume.toString();
let event = new Event('change', {bubbles: true});
selects[1].dispatchEvent(event);
i++;
}
}
public static checkContainers(): boolean {
console.log("Checking containers...")
let returner = true;
const containers = document.getElementById("setupContainers") as HTMLDivElement;
let setupContainers = containers.getElementsByTagName("div");
for (let c of setupContainers) {
for (let c2 of setupContainers) {
for (let sel of c.getElementsByTagName("select")) {
if (sel.value == "-1") continue;
if (sel.classList.contains("noCheckup")) continue;
if (sel.disabled) continue;
for (let sel2 of c2.getElementsByTagName("select")) {
if (sel2.value == "-1") continue;
if (sel2.disabled) continue;
if (sel == sel2) continue;
if (sel2.classList.contains("noCheckup")) continue;
if (sel.value == sel2.value) {
c.classList.add("error");
c2.classList.add("error");
sel.classList.add("error");
sel2.classList.add("error");
setTimeout(() => {
c.classList.remove("error");
c2.classList.remove("error");
sel.classList.remove("error");
sel2.classList.remove("error");
}, 2200);
returner = false;
}
}
}
}
}
return returner;
}
}