diff --git a/config.json b/config.json new file mode 100644 index 0000000..c440a2d --- /dev/null +++ b/config.json @@ -0,0 +1 @@ +{"setupDone":true,"led_enabled":true,"remote_enabled":true,"hotspot_enabled":false,"led_gpio":22,"ambient_color":"#05445e"} \ No newline at end of file diff --git a/public/stylesheets/menu.css b/public/stylesheets/menu.css new file mode 100644 index 0000000..02a95d9 --- /dev/null +++ b/public/stylesheets/menu.css @@ -0,0 +1,10 @@ +#menu { + padding-left: 5%; + padding-right: 5%; + padding-top: 1%; + display: grid; + grid-template-columns: repeat(2, calc(90% / 2)); + grid-template-rows: repeat(2, calc(90% / 2)); + grid-gap: 2% 2%; + color: white; +} \ No newline at end of file diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index aba88af..5e7cb06 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -64,6 +64,7 @@ h1 { #title { text-align: center; float: left; + margin-top: 0.6%; width: calc(100% / 3); } @@ -73,7 +74,8 @@ h1 { float: left; width: calc(100% / 3); text-align: left; - padding-top: 1.2%; + padding-top: 1.6%; + padding-left: 10px; } @@ -81,8 +83,9 @@ h1 { float: left; width: calc(100% / 3); text-align: right; - font-size: 0.5em; + font-size: 0.8em; padding-top: 1.2%; + padding-right: 10px; } @@ -145,11 +148,40 @@ h1 { scrollbar-width: none; /* Firefox */ scroll-behavior: smooth; color: white; + /*animation: showPane 0.3s forwards;*/ + +} + +@keyframes showPane { + 0% { + display: none; + opacity: 0; + } + 1% { + display: block; + } + 100% { + opacity: 1; + } } .hiddenPane { + transition: 0.4s; display: none !important; - transition: 0.2s; + /*animation: hidePane 0.4s forwards;*/ +} +@keyframes hidePane { + 0% { + opacity: 1; + display: block !important; + } + 99% { + display: block !important; + } + 100% { + display: none !important; + opacity: 0; + } } #settings { diff --git a/src/Settings.ts b/src/Settings.ts index 1751c54..3ef08c1 100644 --- a/src/Settings.ts +++ b/src/Settings.ts @@ -2,6 +2,10 @@ import * as fs from "fs"; import path from "path"; export class Settings { + static set setupDone(value: boolean) { + this._setupDone = value; + this.set("setupDone", value); + } static get json(): {} { return this._json; } @@ -14,12 +18,12 @@ export class Settings { public static loadSettings() { - if (!fs.existsSync(path.join(__dirname, "/config.json"))) { + if (!fs.existsSync(path.join(__dirname, "../config.json"))) { this._setupDone = false; - fs.writeFileSync(path.join(__dirname, "/config.json"), `{"setupDone":false}`); + fs.writeFileSync(path.join(__dirname, "../config.json"), `{"setupDone":false}`); } - let file = fs.readFileSync(path.join(__dirname, "/config.json")); + let file = fs.readFileSync(path.join(__dirname, "../config.json")); this._json = JSON.parse(file.toString("utf8")); this._setupDone = this._json["setupDone"]; @@ -28,7 +32,7 @@ export class Settings { } public static saveSettings() { - fs.writeFileSync(path.join(__dirname, "/config.json"), JSON.stringify(this._json)); + fs.writeFileSync(path.join(__dirname, "../config.json"), JSON.stringify(this._json)); } public static get(key: string): any { @@ -37,5 +41,6 @@ export class Settings { public static set(key: string, value: any) { this._json[key] = value; + this.saveSettings(); } } \ No newline at end of file diff --git a/src/WebSocketEvent.ts b/src/WebSocketEvent.ts index 4f2faab..6124e5a 100644 --- a/src/WebSocketEvent.ts +++ b/src/WebSocketEvent.ts @@ -4,4 +4,5 @@ export enum WebSocketEvent { CONTAINERS = "CONTAINERS", CONFIG = "CONFIG", TARE = "TARE", + SETUP = "SETUP" } \ No newline at end of file diff --git a/src/iTender.ts b/src/iTender.ts index f387520..973d6ad 100644 --- a/src/iTender.ts +++ b/src/iTender.ts @@ -71,8 +71,7 @@ export class iTender { return new Promise(async resolve => { for (let c of (await Container.find({enabled: true}))) { try { - if( !c.sensorFilledMax || c.sensorFilledMin ) - { + if (!c.sensorFilledMax || c.sensorFilledMin) { c.enabled = false; await c.save(); continue; @@ -225,7 +224,7 @@ export class iTender { public static toggleTare(state: boolean) { clearInterval(iTender.interval); - if ( state ) + if (state) this.interval = setInterval(async () => { await this.measureContainers(); }, 500); diff --git a/src/routes/indexRouter.ts b/src/routes/indexRouter.ts index 19d8f8d..b0785de 100644 --- a/src/routes/indexRouter.ts +++ b/src/routes/indexRouter.ts @@ -7,4 +7,8 @@ router.get('/', function (req, res, next) { res.render('index', {title: 'Express'}); }); +router.get('/status', (req, res) => { + res.status(200).send("ok"); +}) + module.exports = router; diff --git a/src/routes/ws/websocketRoute.ts b/src/routes/ws/websocketRoute.ts index 52e7352..0f49bc8 100644 --- a/src/routes/ws/websocketRoute.ts +++ b/src/routes/ws/websocketRoute.ts @@ -6,6 +6,7 @@ import {iTenderStatus} from "../../iTenderStatus"; import {WebSocketEvent} from "../../WebSocketEvent"; import Container from "../../database/Container"; import {SensorType} from "../../SensorType"; +import {Settings} from "../../Settings"; const express = require('express'); const router = express.Router(); @@ -26,25 +27,6 @@ router.ws('/', async (ws, req, next) => { await WebSocketHandler.sendContainers(); await WebSocketHandler.sendStatus(); - async function sendWhenReady() { - if (iTender.status != iTenderStatus.READY) { - setTimeout(sendWhenReady, 100); - return; - } - - iTender.setStatus(iTenderStatus.REFRESHING); - - let payload = new WebSocketPayload(WebSocketEvent.DRINKS, false, iTender.drinks); - await WebSocketHandler.send(payload); - - setTimeout(() => { - if (iTender.status == iTenderStatus.REFRESHING) - iTender.setStatus(iTenderStatus.READY); - }, 2000); - - } - - sendWhenReady().then(); ws.on('message', async (raw, bool) => { let msg = WebSocketPayload.parseFromBase64Json(raw); @@ -86,7 +68,27 @@ router.ws('/', async (ws, req, next) => { case WebSocketEvent.CONFIG: { // ToDo + console.log("New Settings:", msg.data); // Danach setup modus aus + for (const [key, value] of Object.entries(msg.data)) { + Settings.set(key, value); + } + + Settings.setupDone = true; + break; + } + + case WebSocketEvent.SETUP: { + if ((msg.data as boolean)) { + iTender.setStatus(iTenderStatus.SETUP); + } else { + if (Settings.setupDone) { + iTender.setStatus(iTenderStatus.READY); + await WebSocketHandler.sendContainers(); + await WebSocketHandler.sendRunningConfig(); + + } + } } } }); diff --git a/src/web/Pane.ts b/src/web/Pane.ts index 3167080..1702d61 100644 --- a/src/web/Pane.ts +++ b/src/web/Pane.ts @@ -1,6 +1,7 @@ export enum Pane { - MAIN= "Main", - MENU = "Menü", - SETTINGS = "Einstellungen", - SETUP = "Setup" + MAIN= "MAIN", + MENU = "MENU", + SETTINGS = "SETTINGS", + SETUP = "SETUP", + NONE = "NONE" } \ No newline at end of file diff --git a/src/web/Setup.ts b/src/web/Setup.ts index 404c906..b1e45f9 100644 --- a/src/web/Setup.ts +++ b/src/web/Setup.ts @@ -29,6 +29,30 @@ export class Setup { 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.
Um ihn zu nutzen, ist eine Grund-Konfiguration (Setup) nötig.

Das Setup lässt sich später auch jederzeit aus dem Menü erneut aufrufen.
Um die Einrichtung des Gerätes abzuschließen, muss mindestens ein Behälter mit Pumpe (optional auch Sensor) hinzugefügt werden.

`; + 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.

+LED-Modul
+In diesem Modul kann die Ambiente-Farbe sowie der LED-Streifen aktiviert werden.

+Erweiterte Einstellungen
+Hier lässt sich konfigurieren, ob die Nutzung der Remote-Bedienung erlaubt ist, oder ein Hotspot aktiviert werden soll, falls keine WiFI-Verbindung vorliegt.

+Behälter-Modul
+Dort werden die Behälter definiert, welche in den iTender gestellt werden.
Dort müssen GPIO-Pins der Pumpe, etwaige Sensoren-Typen und Pins definiert werden.
Außerdem wird das Volumen eingestellt.

`; + btn.innerText = "Einrichtung starten"; + btn.onclick= () => {modal.close();} + } + modal.addContent(btn); + modal.open(); + } } static async openSetup() { @@ -37,6 +61,12 @@ export class 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; @@ -110,9 +140,9 @@ export class Setup { for (let c of (document.getElementById("setupContainers") as HTMLDivElement).getElementsByTagName("div")) { let sensorType = c.getElementsByTagName("select")[1].value; let type; - if( sensorType == "-1" ) + if (sensorType == "-1") type = SensorType.NONE; - else if ( sensorType == "0" ) + else if (sensorType == "0") type = SensorType.ULTRASOUND; else type = SensorType.LOADCELL; @@ -142,85 +172,7 @@ export class Setup { setTimeout(() => { saveModal.close(); setupSaveBtn.disabled = false; - let tareModal = new Modal("setup", "Einmessung Sensoren"); - let txt = document.createElement("p"); - txt.innerHTML = `Damit alle Sensoren korrekte Werte liefern, sollte eine Einmessung durchgeführt werden.
-Während der Einmessung müssen die Behälter je nachdem geleert, gefüllt oder komplett entnommen werden.

`; - tareModal.addContent(txt); - - tareModal.addButton(ButtonType.PRIMARY, "Später", () => { - tareModal.close(); - }); - - let ul; - tareModal.addButton(ButtonType.PRIMARY, "Starten", async () => { - tareModal.close(); - let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: true}); - - let modal = new Modal("tare", "Einmessung"); - - let txt = document.createElement("p"); - txt.innerHTML = `Messung Teil 1
-Bitte alle Behälter entfernen und Sensoren freilegen.
-Die Gewichtssensoren werden beim Bestätigen austariert

Zum fortfahren Tarieren drücken.
`; - modal.addContent(txt); - - ul = document.createElement("ul"); - modal.addContent(ul); - - 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
-Bitte nun alle Behälter ohne Inhalt einsetzen.
-Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren Tarieren drücken.
`; - btn.onclick = () => { - let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 1}); - WebWebSocketHandler.send(payload); - - txt.innerHTML = `Messung Teil 3
-Bitte nun alle Behälter mit vollständigem Inhalt einsetzen.
-Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren Tarieren drücken.
`; - - btn.onclick = () => { - let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 2}); - WebWebSocketHandler.send(payload); - payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: false}); - WebWebSocketHandler.send(payload); - - txt.innerHTML = `Alle Werte wurden erfolgreich gespeichert.
Die Einmessung kann jederzeit über die Speichern-Navigation wiederholt werden.

Das Einmessen sollte nach mehrfachem Benutzen wiederholt werden.
`; - btn.innerText = "Schließen"; - btn.onclick = () => modal.close(); - } - - }; - }; - - modal.addContent(btn); - await modal.open(); - await WebWebSocketHandler.send(payload); - }); - - tareModal.open(); - // irgendwie müssen jz die container updates abgegriffen werden - WebWebSocketHandler.tareContainerUpdates = (payload: WebSocketPayload) => { - 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} [${c.sensorType}]`; - ul.append(li); - } - } + this.startTare(); }, 1000); }).catch(() => { @@ -233,6 +185,88 @@ Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren T } } + 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.
+Während der Einmessung müssen die Behälter je nachdem geleert, gefüllt oder komplett entnommen werden.

`; + tareModal.addContent(txt); + + tareModal.addButton(ButtonType.PRIMARY, "Später", () => { + tareModal.close(); + }); + + let ul; + tareModal.addButton(ButtonType.PRIMARY, "Starten", async () => { + tareModal.close(); + let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: true}); + + let modal = new Modal("tare", "Einmessung"); + + let txt = document.createElement("p"); + txt.innerHTML = `Messung Teil 1
+Bitte alle Behälter entfernen und Sensoren freilegen.
+Die Gewichtssensoren werden beim Bestätigen austariert

Zum fortfahren Tarieren drücken.
`; + modal.addContent(txt); + + ul = document.createElement("ul"); + modal.addContent(ul); + + 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
+Bitte nun alle Behälter ohne Inhalt einsetzen.
+Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren Tarieren drücken.
`; + btn.onclick = () => { + let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 1}); + WebWebSocketHandler.send(payload); + + txt.innerHTML = `Messung Teil 3
+Bitte nun alle Behälter mit vollständigem Inhalt einsetzen.
+Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren Tarieren drücken.
`; + + btn.onclick = () => { + let payload = new WebSocketPayload(WebSocketEvent.TARE, false, {tare: 2}); + WebWebSocketHandler.send(payload); + payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: false}); + WebWebSocketHandler.send(payload); + + txt.innerHTML = `Alle Werte wurden erfolgreich gespeichert.
Die Einmessung kann jederzeit über die Speichern-Navigation wiederholt werden.

Das Einmessen sollte nach mehrfachem Benutzen wiederholt werden.
`; + btn.innerText = "Schließen"; + btn.onclick = () => modal.close(); + } + + }; + }; + + modal.addContent(btn); + await modal.open(); + await WebWebSocketHandler.send(payload); + }); + + tareModal.open(); + + WebWebSocketHandler.tareContainerUpdates = (payload: WebSocketPayload) => { + 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} [${c.sensorType}]`; + ul.append(li); + } + } + } + public static addSetupContainer() { let setupContainers = document.getElementById("setupContainers") as HTMLDivElement; @@ -255,8 +289,8 @@ Die Gewichtssensoren werden beim Bestätigen austariert.

Zum fortfahren T selectPin.append(noSel.cloneNode(true)); selectPin.selectedIndex = 0; - - const pins = [3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 21, 23, 24, 26, 29, 31, 32, 33, 34, 35, 36, 37, 38, 40]; + // 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; diff --git a/src/web/WebHandler.ts b/src/web/WebHandler.ts index 2e221a7..256cec0 100644 --- a/src/web/WebHandler.ts +++ b/src/web/WebHandler.ts @@ -1,14 +1,14 @@ import {WebSocketPayload} from "../WebSocketPayload"; import {IDrink} from "../database/IDrink"; -import {Modal} from "./Modal"; -import {ButtonType} from "./ButtonType"; import {Pane} from "./Pane"; import {IContainer} from "../database/IContainer"; import {Setup} from "./Setup"; -import * as Events from "events"; import {SensorType} from "../SensorType"; export class WebHandler { + static get currentPane(): Pane { + return this._currentPane; + } private static containers = []; public static onDrinkUpdate(payload: WebSocketPayload) { @@ -78,9 +78,9 @@ ${ingredients}`*/ //todo (selects[0] as HTMLSelectElement).value = c.pumpPin.toString(); let type; - if( c.sensorType == SensorType.NONE ) + if (c.sensorType == SensorType.NONE) type = "-1"; - else if( c.sensorType == SensorType.ULTRASOUND ) + else if (c.sensorType == SensorType.ULTRASOUND) type = "0"; else type = "1"; @@ -88,440 +88,74 @@ ${ingredients}`*/ //todo (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 ); + let event = new Event('change', {bubbles: true}); + selects[1].dispatchEvent(event); i++; } } - static async openSetup() { - - - return; - - // old - Modal.close(); - let modal = new Modal("setup", "Setup 1/2"); - - let containers: { container: HTMLDivElement, slot: number, pumpPin: HTMLSelectElement, sensorType: HTMLSelectElement, sensorPin1: HTMLSelectElement, sensorPin2: HTMLSelectElement, volume: HTMLSelectElement }[] = []; - - let onchange = () => { - let containerEle = document.getElementById("setup_containers") as HTMLDivElement; - let containerNumber = document.getElementById("setup_slots") as HTMLInputElement; - if (!containerEle || !containerNumber) return; - - let i = 1; - let oldElements = containerEle.getElementsByTagName("div"); - for (let e of oldElements) { - if (i > containerNumber.valueAsNumber) { - e.remove(); - containers.pop(); - } - i++; - } - - for (let i = containerEle.getElementsByTagName("div").length; i < containerNumber.valueAsNumber; i++) { - let con = document.createElement("div"); - let thisContainer = {}; - thisContainer["container"] = con; - con.classList.add("setupContainer"); - - let containerName = document.createElement("p"); - containerName.innerText = "Container " + (i + 1); - thisContainer["slot"] = i + 1; - - con.append(containerName); - - let sensorTypeLabel = document.createElement("label"); - sensorTypeLabel.innerText = "Art des Sensors "; - con.append(sensorTypeLabel); - - let sensorType = document.createElement("select"); - sensorType.classList.add("input"); - let sensorTypeNone = document.createElement("option") as HTMLOptionElement; - sensorTypeNone.innerText = "Keiner"; - sensorTypeNone.value = "0"; - sensorType.append(sensorTypeNone); - let sensorTypeUltrasound = document.createElement("option") as HTMLOptionElement; - sensorTypeUltrasound.innerText = "Ultraschall"; - sensorTypeUltrasound.value = "1"; - sensorType.append(sensorTypeUltrasound); - let sensorTypeScale = document.createElement("option") as HTMLOptionElement; - sensorTypeScale.innerText = "Wägezelle"; - sensorTypeScale.value = "2"; - sensorType.append(sensorTypeScale); - - - con.append(sensorType); - thisContainer["sensorType"] = sensorType; - - con.append(document.createElement("br")); - - - let changeWhenTypeOfSensor: HTMLElement[] = []; - let list = ["Pumpen Port ", "Sensor 1 ", "Sensor 2 "]; - for (let t of list) { - let labelElement = document.createElement("label"); - labelElement.innerText = t; - changeWhenTypeOfSensor.push(labelElement); - con.append(labelElement); - - let selectElement = document.createElement("select"); - labelElement.style.display = "none"; - selectElement.style.display = "none"; - selectElement.classList.add("input"); - if (t == "Sensor 1 ") { - thisContainer["sensorPin1"] = selectElement; - } else if (t == "Sensor 2 ") { - thisContainer["sensorPin2"] = selectElement; - } else { - thisContainer["pumpPin"] = selectElement; - labelElement.style.display = "inline"; - selectElement.style.display = "inline"; - } - - let noSel = document.createElement("option") as HTMLOptionElement; - noSel.innerText = "Bitte wählen"; - noSel.value = "-1"; - noSel.disabled = true; - - selectElement.append(noSel); //loveyou - selectElement.selectedIndex = 0; - - const pins = [3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 21, 22, 23, 24, 26, 29, 31, 32, 33, 34, 35, 36, 37, 38, 40]; - for (let pin of pins) { - let pinEle = document.createElement("option") as HTMLOptionElement; - pinEle.innerText = "" + pin; - pinEle.value = "" + pin; - selectElement.append(pinEle); - } - con.append(selectElement); - - let br = document.createElement("br"); - con.append(br); - } - - sensorType.onchange = () => { - // 0 => ultrasound; 1 => scale - if (sensorType.value == "1") { - thisContainer["sensorPin1"].style.display = "inline"; - thisContainer["sensorPin2"].style.display = "inline"; - changeWhenTypeOfSensor[1].style.display = "inline"; - changeWhenTypeOfSensor[2].style.display = "inline"; - changeWhenTypeOfSensor[1].innerText = "Trigger Pin "; - changeWhenTypeOfSensor[2].innerText = "Echo Pin "; - } else if (sensorType.value == "2") { - thisContainer["sensorPin1"].style.display = "inline"; - thisContainer["sensorPin2"].style.display = "inline"; - changeWhenTypeOfSensor[1].style.display = "inline"; - changeWhenTypeOfSensor[2].style.display = "inline"; - changeWhenTypeOfSensor[1].innerText = "Clock Pin "; - changeWhenTypeOfSensor[2].innerText = "Data Pin "; - } else { - thisContainer["sensorPin1"].style.display = "none"; - thisContainer["sensorPin2"].style.display = "none"; - changeWhenTypeOfSensor[1].style.display = "none"; - changeWhenTypeOfSensor[2].style.display = "none"; - changeWhenTypeOfSensor[1].innerText = "Sensor 1 "; - changeWhenTypeOfSensor[2].innerText = "Sensor 2 "; - } - }; - - let labelElement = document.createElement("label"); - labelElement.innerText = "Gesamtvolumen (ml) "; - con.append(labelElement); - - let selectElement = document.createElement("select"); - selectElement.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; - selectElement.append(pinEle); - thisContainer["volume"] = selectElement; - } - selectElement.selectedIndex = 7; - - con.append(selectElement); - - let br = document.createElement("br"); - con.append(br); - - containerEle.append(con); - let cast = thisContainer as { container: HTMLDivElement, slot: number, pumpPin: HTMLSelectElement, sensorType: HTMLSelectElement, sensorPin1: HTMLSelectElement, sensorPin2: HTMLSelectElement, volume: HTMLSelectElement }; - containers.push(cast); - console.log(cast); - } - }; - - let div1 = document.createElement("div"); - div1.style.marginBottom = "2%"; - modal.addContent(div1); - let labelElement = document.createElement("label"); - labelElement.innerText = "Anzahl Slots"; - div1.append(labelElement); - - let numberInputElement = document.createElement("input") as HTMLInputElement; - numberInputElement.type = "number"; - numberInputElement.id = "setup_slots"; - numberInputElement.classList.add("input"); - numberInputElement.style.width = "20%;" - numberInputElement.value = "0"; - numberInputElement.min = "0"; - numberInputElement.max = "30"; - numberInputElement.onchange = () => { - onchange(); - }; - div1.append(numberInputElement); - - let divContainers = document.createElement("div"); - divContainers.id = "setup_containers"; - modal.addContent(divContainers); - - let status = document.createElement("p"); - status.style.color = "black"; - status.innerHTML = "Drücke 'Weiter' zum Speichern"; - modal.addContent(status); - - modal.addButton(ButtonType.SUCCESS, "Weiter", () => { - status.innerHTML = "..."; - status.style.color = "gray"; - if (numberInputElement.valueAsNumber < 0 || numberInputElement.valueAsNumber > 30) { - numberInputElement.value = "0"; - status.style.color = "black"; - status.innerHTML = "Automatische Änderungen überprüfen!"; - return; - } - - // Check - if (numberInputElement.valueAsNumber != containers.length) { - onchange(); - status.style.color = "black"; - status.innerHTML = "Automatische Änderungen überprüfen!"; - return; - } - - if (containers.length == 0) { - status.style.color = "red"; - status.innerHTML = "Es muss mindestens ein Container hinzugefügt werden!"; - return; - } - - let newContainers: { slot: number, pumpPin: number, sensorPin1: number, sensorPin2: number, volume: number }[] = []; - let ok = true; - for (let c of containers) { - for (let c2 of containers) { - if (c.slot == c.slot) continue; - - let pins = [c.sensorPin1.value, c.sensorPin2.value, c.pumpPin.value, c2.sensorPin1.value, c2.sensorPin2.value, c2.pumpPin.value]; - - let i = 0; - for (let pin of pins) { - let j = 0; - if (pin == "-1") { - console.log("Skip ", pin); - continue; - } - - for (let pin2 of pins) { - // Wenn index derselbe ist, ignorieren - if (j == i) { - console.log("Index skip ", pin); - continue; - } - - if (pin2 == "-1") { - console.log("Skip ", pin); - continue; - } - - if (pin == pin2) { - console.log("Pin same error", pin, pin2, c, c2) - ok = false; - c.container.classList.add("error"); - c2.container.classList.add("error"); - setTimeout(() => { - c.container.classList.remove("error"); - c2.container.classList.remove("error"); - }, 2000); - break; - } - - j++; - } - i++; - if (!ok) break; - } - if (!ok) break; - - /*console.log(c, c2); - console.log(c.sensorPin2.value == c2.sensorPin2.value); - if ( - c.pumpPin.value == c2.pumpPin.value || - (c.sensorType.value != "0" && ( - (c.sensorPin1.value == c2.sensorPin1.value) - || (c.sensorPin2.value == c2.sensorPin2.value) - || (c.sensorPin1.value == c2.sensorPin2.value) - || (c.sensorPin2.value == c2.sensorPin1.value))) - ) { - console.log("Check not ok") - ok = false; - c.container.classList.add("error"); - c2.container.classList.add("error"); - setTimeout(() => { - c.container.classList.remove("error"); - c2.container.classList.remove("error"); - }, 2000); - break; - }*/ - } - if (!ok) break; - - if (c.pumpPin.value == "-1" || (c.sensorType.value != "0" && (c.sensorPin1.value == "-1" || c.sensorPin2.value == "-1"))) { - console.log("Local check invalid", c); - ok = false; - c.container.classList.add("error"); - - setTimeout(() => { - c.container.classList.remove("error"); - - }, 1000); - } - - - let pins = [c.sensorPin1.value, c.sensorPin2.value, c.pumpPin.value]; - - let i = 0; - for (let pin of pins) { - let j = 0; - if (pin == "-1") { - console.log("Skip ", pin); - continue; - } - for (let pin2 of pins) { - // Wenn index derselbe ist, ignorieren - if (j == i) { - console.log("Index skip ", pin); - continue; - } - - if (pin2 == "-1") { - console.log("Skip ", pin); - continue; - } - - if (pin == pin2) { - console.log("Pin same error", pin, pin2, c) - ok = false; - c.container.classList.add("error"); - setTimeout(() => { - c.container.classList.remove("error"); - }, 2000); - break; - } - - j++; - } - i++; - } - - /*if (c.pumpPin.value == c.sensorPin1.value || c.pumpPin.value == c.sensorPin2.value || c.sensorPin1.value == c.sensorPin2.value) { - ok = false; - c.container.classList.add("error"); - - setTimeout(() => { - c.container.classList.remove("error"); - - }, 1000); - break; - }*/ - - newContainers.push({ - slot: c.slot, - sensorPin1: parseInt(c.sensorPin1.value), - sensorPin2: parseInt(c.sensorPin2.value), - pumpPin: parseInt(c.pumpPin.value), - volume: parseInt(c.volume.value) - }); - } - if (!ok) { - status.style.color = "red"; - status.innerHTML = "Problem erkannt
Bitte überprüfen, ob alle benötigten Felder gesetzt sind
und ob Pins nicht mehrfach belegt sind!" - return; - } - - // todo Hier konfiguration an Server senden und speichern - - // zurück und hinsenden mittels function die hier in der WebHandler.ts gespeichert wird maybe? - - status.style.color = "green"; - status.innerHTML = "Konfiguration gespeichert!"; - - let tareModal = new Modal("setup", "Setup 2/2"); - - let container = document.createElement("div"); - tareModal.addContent(container); - - let txt = document.createElement("p"); - txt.innerHTML = `Um das Setup abzuschließen, müssen die Sensoren eingestellt werden.
-Bitte zunächst jegliche Behälter von/unter den Sensoren entfernen.
Zum fortfahren "Messen"-Schaltfläche berühren.
`; - tareModal.addContent(txt); - - - let btn = tareModal.addButton(ButtonType.SUCCESS, "Messen", () => { - txt.innerHTML = "Messung läuft...
Gerät nicht berühren!
\n" + - "
\n" + - "
"; - btn.disabled = true; - btn.innerText = "Bitte warten"; - }); - // todo send messung to server to start - - tareModal.open(); - }); - - //await modal.open(); - - let setupNumber = document.getElementById("setup_slots") as HTMLInputElement; - setupNumber.focus(); - } + private static _currentPane: Pane; public static openPane(pane: Pane): void { let mainPanel = document.getElementById("main") as HTMLDivElement; let setupPanel = document.getElementById("setup") as HTMLDivElement; let menuPanel = document.getElementById("menu") as HTMLDivElement; let settingsPanel = document.getElementById("settings") as HTMLDivElement; + console.log("Opening pane " + pane); + if (!this._currentPane) { + mainPanel.classList.add("hiddenPane"); + setupPanel.classList.add("hiddenPane"); + menuPanel.classList.add("hiddenPane"); + settingsPanel.classList.add("hiddenPane"); + } - mainPanel.classList.add("hiddenPane"); - setupPanel.classList.add("hiddenPane"); - menuPanel.classList.add("hiddenPane"); - settingsPanel.classList.add("hiddenPane"); - - - switch (pane) { + switch (this._currentPane) { case Pane.MAIN: { - mainPanel.classList.remove("hiddenPane"); + mainPanel.classList.add("hiddenPane"); break; } case Pane.MENU: { - menuPanel.classList.remove("hiddenPane"); + menuPanel.classList.add("hiddenPane"); break; } case Pane.SETUP: { - setupPanel.classList.remove("hiddenPane"); + setupPanel.classList.add("hiddenPane"); break; } case Pane.SETTINGS: { - settingsPanel.classList.remove("hiddenPane"); + settingsPanel.classList.add("hiddenPane"); break; } } + let title = document.getElementById("title") as HTMLTitleElement; - title.innerText = pane.toString(); + switch (pane) { + case Pane.MAIN: { + mainPanel.classList.remove("hiddenPane"); + title.innerText = "iTender"; + break; + } + case Pane.MENU: { + menuPanel.classList.remove("hiddenPane"); + title.innerText = "Menü"; + break; + } + case Pane.SETUP: { + setupPanel.classList.remove("hiddenPane"); + title.innerText = "Setup"; + break; + } + case Pane.SETTINGS: { + settingsPanel.classList.remove("hiddenPane"); + title.innerText = "Einstellungen"; + break; + } + } + + this._currentPane = pane; } } \ No newline at end of file diff --git a/src/web/WebWebSocketHandler.ts b/src/web/WebWebSocketHandler.ts index 7455f20..27115ca 100644 --- a/src/web/WebWebSocketHandler.ts +++ b/src/web/WebWebSocketHandler.ts @@ -5,6 +5,7 @@ import {ButtonType} from "./ButtonType"; import {iTenderStatus} from "../iTenderStatus"; import {WebHandler} from "./WebHandler"; import {Setup} from "./Setup"; +import {Pane} from "./Pane"; export class WebWebSocketHandler { private static socket: WebSocket; @@ -12,7 +13,6 @@ export class WebWebSocketHandler { public static tareContainerUpdates: (payload: WebSocketPayload) => void; - constructor() { WebWebSocketHandler.socket = new WebSocket(WebWebSocketHandler.url); WebWebSocketHandler.socket.onopen = this.onOpen; @@ -22,13 +22,12 @@ export class WebWebSocketHandler { } private onMessage(msgEvent: MessageEvent) { - console.log("[WS] Incoming message", msgEvent); let payload = WebSocketPayload.parseFromBase64Json(msgEvent.data); if (!payload) { console.log("[WS] Could not parse message: ", msgEvent); return; } - console.log("[WS] Received " + payload.event + " Event"); + console.log("[WS] Received " + payload.event + " Event", payload); switch (payload.event) { case WebSocketEvent.CONFIG: { @@ -49,6 +48,8 @@ export class WebWebSocketHandler { Modal.close("start"); Modal.close("refreshing"); Modal.close("setup"); + WebHandler.openPane(Pane.MAIN); + (document.getElementById("menuBtn") as HTMLButtonElement).disabled = false; break; } case iTenderStatus.STARTING: { @@ -61,15 +62,16 @@ export class WebWebSocketHandler { break; } case iTenderStatus.REFRESHING: { - let modal = new Modal("refreshing", "Aktualisieren..."); - let txt = document.createElement("p"); - txt.innerHTML = `Einen Augenblick bitte
iTender aktualisiert die Getränke...`; - modal.addContent(txt); - modal.loader = true; - modal.open(); + /* let modal = new Modal("refreshing", "Aktualisieren..."); + let txt = document.createElement("p"); + txt.innerHTML = `Einen Augenblick bitte
iTender aktualisiert die Getränke...`; + modal.addContent(txt); + modal.loader = true; + modal.open();*/ break; } case iTenderStatus.SETUP: { + Modal.close("start"); Setup.openSetup(); } } @@ -83,6 +85,9 @@ export class WebWebSocketHandler { case WebSocketEvent.CONTAINERS: { WebHandler.onContainerUpdate(payload); + if (WebWebSocketHandler.tareContainerUpdates != undefined) { + WebWebSocketHandler.tareContainerUpdates(payload); + } break; } } @@ -117,9 +122,10 @@ export class WebWebSocketHandler { modal.addContent(txt); modal.loader = true; modal.open(); - setInterval(() => { - window.location.reload(); - }, 5000); + setInterval(async () => { + if ((await WebWebSocketHandler.checkConnection())) + window.location.reload(); + }, 2000); } /* let connectionElement = document.getElementById("right"); @@ -129,6 +135,31 @@ export class WebWebSocketHandler { }*/ } + private static checkConnection(): Promise { + return new Promise(async resolve => { + const xhr = new XMLHttpRequest(); + xhr.open("GET", '/status', true); + + //Send the proper header information along with the request + xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); + + xhr.onreadystatechange = () => { // Call a function when the state changes. + if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) { + resolve(true); + } else if (xhr.readyState == XMLHttpRequest.DONE) { + resolve(false); + } + } + + try { + await xhr.send(); + } catch (e) { + resolve(false); + } + + }) + } + private onError(event) { console.error("[WS] Error", event); /*let connectionElement = document.getElementById("right"); @@ -139,6 +170,7 @@ export class WebWebSocketHandler { } public static send(payload: WebSocketPayload): Promise { + console.log("[WS] Sending " + payload.event + " Event",payload); return new Promise(async (resolve, reject) => { try { if (this.socket && this.socket.readyState == 1) { diff --git a/src/web/main.ts b/src/web/main.ts index 0f68d3f..a14d325 100644 --- a/src/web/main.ts +++ b/src/web/main.ts @@ -7,34 +7,50 @@ const main = document.getElementById("main"); const time = document.getElementById("right"); document.addEventListener("DOMContentLoaded", () => { - console.log("DOM Loaded"); - WebHandler.openPane(Pane.MAIN); + console.log("DOM Loaded"); + setupOnClickEvents(); + WebHandler.openPane(Pane.NONE); - let modal = new Modal("start", "iTender"); - let txt = document.createElement("p"); - txt.innerText = "Willkommen" - modal.addContent(txt); - modal.loader = true; - //modal.open(); - connect(); + let modal = new Modal("start", "iTender"); + let txt = document.createElement("p"); + txt.innerText = "Willkommen" + modal.addContent(txt); + modal.loader = true; + //modal.open(); + connect(); - setTimeout( load, 1000); + setTimeout(load, 100); }); +function setupOnClickEvents() { + const menuBtn = document.getElementById("menuBtn") as HTMLButtonElement; + menuBtn.disabled = true; -function load() -{ - if(!main||!time) - return; + function doMenu() { + if (WebHandler.currentPane != Pane.MENU) { + WebHandler.openPane(Pane.MENU); + menuBtn.innerText = "Start"; + } else { + WebHandler.openPane(Pane.MAIN); + menuBtn.innerText = "Menü"; + } + } - setInterval( () => { - let currentDate = new Date(); - time.innerText = "" + ( currentDate.getHours() < 10 ? "0" + currentDate.getHours() : currentDate.getHours() ) + ":" + ( currentDate.getMinutes() < 10 ? "0" + currentDate.getMinutes() : currentDate.getMinutes() ); - }, 1000 ); + menuBtn.onclick = doMenu; +} + +function load() { + if (!main || !time) + return; + + setInterval(() => { + let currentDate = new Date(); + time.innerText = "" + (currentDate.getHours() < 10 ? "0" + currentDate.getHours() : currentDate.getHours()) + ":" + (currentDate.getMinutes() < 10 ? "0" + currentDate.getMinutes() : currentDate.getMinutes()); + }, 1000); } let wsHandler; -function connect() -{ - wsHandler = new WebWebSocketHandler(); + +function connect() { + wsHandler = new WebWebSocketHandler(); } diff --git a/views/index.pug b/views/index.pug index b823307..737318d 100644 --- a/views/index.pug +++ b/views/index.pug @@ -8,7 +8,7 @@ block setup input#ledCheckbox.input(type="checkbox") div.inputGroup label GPIO-Pin - input#ledGPIO.input(type="number" value="22" style="width:15%" disabled="disabled") + input#ledGPIO.input(type="number" value="40" style="width:15%" disabled="disabled") div.inputGroup label Ambiente Farbe input#ambientColor.input(type="color" value="#05445E" style="width:15%" disabled="disabled") @@ -31,11 +31,23 @@ block setup div#setupContainers - button.btn.btn-danger#setup_cancelBtn(style="grid-row: span 1; grid-column: span 1; border-radius: 15px; font-size: 1.2em;" onclick="window.location.reload()") Abbrechen + button.btn.btn-danger#setup_cancelBtn(style="grid-row: span 1; grid-column: span 1; border-radius: 15px; font-size: 1.2em;") Abbrechen button.btn.btn-success#setup_saveBtn(style="grid-row: span 1; grid-column: span 1; border-radius: 15px; font-size: 1.2em;") Speichern block menu // Menu + // gridrow span 1 + setupcontainers + butto + button.btn.btn-primary#menu_containers Behälter + button.btn.btn-primary#menu_stats Statistiken + button.btn.btn-primary#menu_settings Einstellungen + button.btn.btn-primary#menu_setup Setup + + + + + block settings // Settings diff --git a/views/layout.pug b/views/layout.pug index 3b00db5..4f9cb82 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -20,14 +20,14 @@ html div#containers div#container + div.pane#main + block main div.pane#setup block setup div.pane#menu block menu div.pane#settings block settings - div.pane#main - block main block extra script(src="/web.js") \ No newline at end of file