Took 3 hours 30 minutes
This commit is contained in:
Tobias Hopp 2022-11-20 16:32:54 +01:00
parent 016f4bc3fe
commit 9d839328f8
15 changed files with 347 additions and 564 deletions

1
config.json Normal file
View File

@ -0,0 +1 @@
{"setupDone":true,"led_enabled":true,"remote_enabled":true,"hotspot_enabled":false,"led_gpio":22,"ambient_color":"#05445e"}

View File

@ -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;
}

View File

@ -64,6 +64,7 @@ h1 {
#title { #title {
text-align: center; text-align: center;
float: left; float: left;
margin-top: 0.6%;
width: calc(100% / 3); width: calc(100% / 3);
} }
@ -73,7 +74,8 @@ h1 {
float: left; float: left;
width: calc(100% / 3); width: calc(100% / 3);
text-align: left; text-align: left;
padding-top: 1.2%; padding-top: 1.6%;
padding-left: 10px;
} }
@ -81,8 +83,9 @@ h1 {
float: left; float: left;
width: calc(100% / 3); width: calc(100% / 3);
text-align: right; text-align: right;
font-size: 0.5em; font-size: 0.8em;
padding-top: 1.2%; padding-top: 1.2%;
padding-right: 10px;
} }
@ -145,11 +148,40 @@ h1 {
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
scroll-behavior: smooth; scroll-behavior: smooth;
color: white; color: white;
/*animation: showPane 0.3s forwards;*/
}
@keyframes showPane {
0% {
display: none;
opacity: 0;
}
1% {
display: block;
}
100% {
opacity: 1;
}
} }
.hiddenPane { .hiddenPane {
transition: 0.4s;
display: none !important; 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 { #settings {

View File

@ -2,6 +2,10 @@ import * as fs from "fs";
import path from "path"; import path from "path";
export class Settings { export class Settings {
static set setupDone(value: boolean) {
this._setupDone = value;
this.set("setupDone", value);
}
static get json(): {} { static get json(): {} {
return this._json; return this._json;
} }
@ -14,12 +18,12 @@ export class Settings {
public static loadSettings() { public static loadSettings() {
if (!fs.existsSync(path.join(__dirname, "/config.json"))) { if (!fs.existsSync(path.join(__dirname, "../config.json"))) {
this._setupDone = false; 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._json = JSON.parse(file.toString("utf8"));
this._setupDone = this._json["setupDone"]; this._setupDone = this._json["setupDone"];
@ -28,7 +32,7 @@ export class Settings {
} }
public static saveSettings() { 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 { public static get(key: string): any {
@ -37,5 +41,6 @@ export class Settings {
public static set(key: string, value: any) { public static set(key: string, value: any) {
this._json[key] = value; this._json[key] = value;
this.saveSettings();
} }
} }

View File

@ -4,4 +4,5 @@ export enum WebSocketEvent {
CONTAINERS = "CONTAINERS", CONTAINERS = "CONTAINERS",
CONFIG = "CONFIG", CONFIG = "CONFIG",
TARE = "TARE", TARE = "TARE",
SETUP = "SETUP"
} }

View File

@ -71,8 +71,7 @@ export class iTender {
return new Promise(async resolve => { return new Promise(async resolve => {
for (let c of (await Container.find({enabled: true}))) { for (let c of (await Container.find({enabled: true}))) {
try { try {
if( !c.sensorFilledMax || c.sensorFilledMin ) if (!c.sensorFilledMax || c.sensorFilledMin) {
{
c.enabled = false; c.enabled = false;
await c.save(); await c.save();
continue; continue;
@ -225,7 +224,7 @@ export class iTender {
public static toggleTare(state: boolean) { public static toggleTare(state: boolean) {
clearInterval(iTender.interval); clearInterval(iTender.interval);
if ( state ) if (state)
this.interval = setInterval(async () => { this.interval = setInterval(async () => {
await this.measureContainers(); await this.measureContainers();
}, 500); }, 500);

View File

@ -7,4 +7,8 @@ router.get('/', function (req, res, next) {
res.render('index', {title: 'Express'}); res.render('index', {title: 'Express'});
}); });
router.get('/status', (req, res) => {
res.status(200).send("ok");
})
module.exports = router; module.exports = router;

View File

@ -6,6 +6,7 @@ import {iTenderStatus} from "../../iTenderStatus";
import {WebSocketEvent} from "../../WebSocketEvent"; import {WebSocketEvent} from "../../WebSocketEvent";
import Container from "../../database/Container"; import Container from "../../database/Container";
import {SensorType} from "../../SensorType"; import {SensorType} from "../../SensorType";
import {Settings} from "../../Settings";
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
@ -26,25 +27,6 @@ router.ws('/', async (ws, req, next) => {
await WebSocketHandler.sendContainers(); await WebSocketHandler.sendContainers();
await WebSocketHandler.sendStatus(); 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) => { ws.on('message', async (raw, bool) => {
let msg = WebSocketPayload.parseFromBase64Json(raw); let msg = WebSocketPayload.parseFromBase64Json(raw);
@ -86,7 +68,27 @@ router.ws('/', async (ws, req, next) => {
case WebSocketEvent.CONFIG: { case WebSocketEvent.CONFIG: {
// ToDo // ToDo
console.log("New Settings:", msg.data);
// Danach setup modus aus // 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();
}
}
} }
} }
}); });

View File

@ -1,6 +1,7 @@
export enum Pane { export enum Pane {
MAIN= "Main", MAIN= "MAIN",
MENU = "Menü", MENU = "MENU",
SETTINGS = "Einstellungen", SETTINGS = "SETTINGS",
SETUP = "Setup" SETUP = "SETUP",
NONE = "NONE"
} }

View File

@ -29,6 +29,30 @@ export class Setup {
ambientColor.value = 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() { static async openSetup() {
@ -37,6 +61,12 @@ export class Setup {
let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement; let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
menuBtn.disabled = true; 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; const containerAddBtn = document.getElementById("containerAddBtn") as HTMLButtonElement;
containerAddBtn.onclick = Setup.addSetupContainer; containerAddBtn.onclick = Setup.addSetupContainer;
@ -110,9 +140,9 @@ export class Setup {
for (let c of (document.getElementById("setupContainers") as HTMLDivElement).getElementsByTagName("div")) { for (let c of (document.getElementById("setupContainers") as HTMLDivElement).getElementsByTagName("div")) {
let sensorType = c.getElementsByTagName("select")[1].value; let sensorType = c.getElementsByTagName("select")[1].value;
let type; let type;
if( sensorType == "-1" ) if (sensorType == "-1")
type = SensorType.NONE; type = SensorType.NONE;
else if ( sensorType == "0" ) else if (sensorType == "0")
type = SensorType.ULTRASOUND; type = SensorType.ULTRASOUND;
else else
type = SensorType.LOADCELL; type = SensorType.LOADCELL;
@ -142,7 +172,21 @@ export class Setup {
setTimeout(() => { setTimeout(() => {
saveModal.close(); saveModal.close();
setupSaveBtn.disabled = false; setupSaveBtn.disabled = false;
let tareModal = new Modal("setup", "Einmessung Sensoren"); 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"); let txt = document.createElement("p");
txt.innerHTML = `Damit alle Sensoren korrekte Werte liefern, sollte eine Einmessung durchgeführt werden.<br> txt.innerHTML = `Damit alle Sensoren korrekte Werte liefern, sollte eine Einmessung durchgeführt werden.<br>
Während der Einmessung müssen die Behälter je nachdem geleert, gefüllt oder komplett entnommen werden.<br><br>`; Während der Einmessung müssen die Behälter je nachdem geleert, gefüllt oder komplett entnommen werden.<br><br>`;
@ -207,7 +251,7 @@ Die Gewichtssensoren werden beim Bestätigen austariert.<br><br>Zum fortfahren T
}); });
tareModal.open(); tareModal.open();
// irgendwie müssen jz die container updates abgegriffen werden
WebWebSocketHandler.tareContainerUpdates = (payload: WebSocketPayload) => { WebWebSocketHandler.tareContainerUpdates = (payload: WebSocketPayload) => {
if (!ul) return; if (!ul) return;
@ -220,16 +264,6 @@ Die Gewichtssensoren werden beim Bestätigen austariert.<br><br>Zum fortfahren T
li.innerText = `Behälter ${c.slot}: ${c.rawData} [${c.sensorType}]`; li.innerText = `Behälter ${c.slot}: ${c.rawData} [${c.sensorType}]`;
ul.append(li); ul.append(li);
} }
}
}, 1000);
}).catch(() => {
setupSaveBtn.disabled = false;
txt.innerHTML = `Fehler beim Speichern.<br>iTender hat nicht reagiert.`;
setTimeout(() => saveModal.close(), 2500);
});
} }
} }
@ -255,8 +289,8 @@ Die Gewichtssensoren werden beim Bestätigen austariert.<br><br>Zum fortfahren T
selectPin.append(noSel.cloneNode(true)); selectPin.append(noSel.cloneNode(true));
selectPin.selectedIndex = 0; 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, 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]; 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) { for (let pin of pins) {
let pinEle = document.createElement("option") as HTMLOptionElement; let pinEle = document.createElement("option") as HTMLOptionElement;
pinEle.innerText = "" + pin; pinEle.innerText = "" + pin;

View File

@ -1,14 +1,14 @@
import {WebSocketPayload} from "../WebSocketPayload"; import {WebSocketPayload} from "../WebSocketPayload";
import {IDrink} from "../database/IDrink"; import {IDrink} from "../database/IDrink";
import {Modal} from "./Modal";
import {ButtonType} from "./ButtonType";
import {Pane} from "./Pane"; import {Pane} from "./Pane";
import {IContainer} from "../database/IContainer"; import {IContainer} from "../database/IContainer";
import {Setup} from "./Setup"; import {Setup} from "./Setup";
import * as Events from "events";
import {SensorType} from "../SensorType"; import {SensorType} from "../SensorType";
export class WebHandler { export class WebHandler {
static get currentPane(): Pane {
return this._currentPane;
}
private static containers = []; private static containers = [];
public static onDrinkUpdate(payload: WebSocketPayload) { public static onDrinkUpdate(payload: WebSocketPayload) {
@ -78,9 +78,9 @@ ${ingredients}`*/ //todo
(selects[0] as HTMLSelectElement).value = c.pumpPin.toString(); (selects[0] as HTMLSelectElement).value = c.pumpPin.toString();
let type; let type;
if( c.sensorType == SensorType.NONE ) if (c.sensorType == SensorType.NONE)
type = "-1"; type = "-1";
else if( c.sensorType == SensorType.ULTRASOUND ) else if (c.sensorType == SensorType.ULTRASOUND)
type = "0"; type = "0";
else else
type = "1"; type = "1";
@ -88,440 +88,74 @@ ${ingredients}`*/ //todo
(selects[2] as HTMLSelectElement).value = c.sensorPin1.toString(); (selects[2] as HTMLSelectElement).value = c.sensorPin1.toString();
(selects[3] as HTMLSelectElement).value = c.sensorPin2.toString(); (selects[3] as HTMLSelectElement).value = c.sensorPin2.toString();
(selects[4] as HTMLSelectElement).value = c.volume.toString(); (selects[4] as HTMLSelectElement).value = c.volume.toString();
let event = new Event('change', { bubbles: true }); let event = new Event('change', {bubbles: true});
selects[1].dispatchEvent( event ); selects[1].dispatchEvent(event);
i++; i++;
} }
} }
static async openSetup() { private static _currentPane: Pane;
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<br>Bitte überprüfen, ob alle benötigten Felder gesetzt sind<br>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.<br>
Bitte zunächst jegliche Behälter von/unter den Sensoren entfernen.<br>Zum fortfahren "Messen"-Schaltfläche berühren.<br>`;
tareModal.addContent(txt);
let btn = tareModal.addButton(ButtonType.SUCCESS, "Messen", () => {
txt.innerHTML = "<strong>Messung läuft...</strong><br><span style='text-decoration: underline'>Gerät nicht berühren!</span><br><div class=\"lds-ellipsis\">\n" +
" <div></div><div></div><div></div><div></div>\n" +
"</div>";
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();
}
public static openPane(pane: Pane): void { public static openPane(pane: Pane): void {
let mainPanel = document.getElementById("main") as HTMLDivElement; let mainPanel = document.getElementById("main") as HTMLDivElement;
let setupPanel = document.getElementById("setup") as HTMLDivElement; let setupPanel = document.getElementById("setup") as HTMLDivElement;
let menuPanel = document.getElementById("menu") as HTMLDivElement; let menuPanel = document.getElementById("menu") as HTMLDivElement;
let settingsPanel = document.getElementById("settings") as HTMLDivElement; let settingsPanel = document.getElementById("settings") as HTMLDivElement;
console.log("Opening pane " + pane);
if (!this._currentPane) {
mainPanel.classList.add("hiddenPane"); mainPanel.classList.add("hiddenPane");
setupPanel.classList.add("hiddenPane"); setupPanel.classList.add("hiddenPane");
menuPanel.classList.add("hiddenPane"); menuPanel.classList.add("hiddenPane");
settingsPanel.classList.add("hiddenPane"); settingsPanel.classList.add("hiddenPane");
}
switch (this._currentPane) {
case Pane.MAIN: {
mainPanel.classList.add("hiddenPane");
break;
}
case Pane.MENU: {
menuPanel.classList.add("hiddenPane");
break;
}
case Pane.SETUP: {
setupPanel.classList.add("hiddenPane");
break;
}
case Pane.SETTINGS: {
settingsPanel.classList.add("hiddenPane");
break;
}
}
let title = document.getElementById("title") as HTMLTitleElement;
switch (pane) { switch (pane) {
case Pane.MAIN: { case Pane.MAIN: {
mainPanel.classList.remove("hiddenPane"); mainPanel.classList.remove("hiddenPane");
title.innerText = "iTender";
break; break;
} }
case Pane.MENU: { case Pane.MENU: {
menuPanel.classList.remove("hiddenPane"); menuPanel.classList.remove("hiddenPane");
title.innerText = "Menü";
break; break;
} }
case Pane.SETUP: { case Pane.SETUP: {
setupPanel.classList.remove("hiddenPane"); setupPanel.classList.remove("hiddenPane");
title.innerText = "Setup";
break; break;
} }
case Pane.SETTINGS: { case Pane.SETTINGS: {
settingsPanel.classList.remove("hiddenPane"); settingsPanel.classList.remove("hiddenPane");
title.innerText = "Einstellungen";
break; break;
} }
} }
let title = document.getElementById("title") as HTMLTitleElement; this._currentPane = pane;
title.innerText = pane.toString();
} }
} }

View File

@ -5,6 +5,7 @@ import {ButtonType} from "./ButtonType";
import {iTenderStatus} from "../iTenderStatus"; import {iTenderStatus} from "../iTenderStatus";
import {WebHandler} from "./WebHandler"; import {WebHandler} from "./WebHandler";
import {Setup} from "./Setup"; import {Setup} from "./Setup";
import {Pane} from "./Pane";
export class WebWebSocketHandler { export class WebWebSocketHandler {
private static socket: WebSocket; private static socket: WebSocket;
@ -12,7 +13,6 @@ export class WebWebSocketHandler {
public static tareContainerUpdates: (payload: WebSocketPayload) => void; public static tareContainerUpdates: (payload: WebSocketPayload) => void;
constructor() { constructor() {
WebWebSocketHandler.socket = new WebSocket(WebWebSocketHandler.url); WebWebSocketHandler.socket = new WebSocket(WebWebSocketHandler.url);
WebWebSocketHandler.socket.onopen = this.onOpen; WebWebSocketHandler.socket.onopen = this.onOpen;
@ -22,13 +22,12 @@ export class WebWebSocketHandler {
} }
private onMessage(msgEvent: MessageEvent) { private onMessage(msgEvent: MessageEvent) {
console.log("[WS] Incoming message", msgEvent);
let payload = WebSocketPayload.parseFromBase64Json(msgEvent.data); let payload = WebSocketPayload.parseFromBase64Json(msgEvent.data);
if (!payload) { if (!payload) {
console.log("[WS] Could not parse message: ", msgEvent); console.log("[WS] Could not parse message: ", msgEvent);
return; return;
} }
console.log("[WS] Received " + payload.event + " Event"); console.log("[WS] Received " + payload.event + " Event", payload);
switch (payload.event) { switch (payload.event) {
case WebSocketEvent.CONFIG: { case WebSocketEvent.CONFIG: {
@ -49,6 +48,8 @@ export class WebWebSocketHandler {
Modal.close("start"); Modal.close("start");
Modal.close("refreshing"); Modal.close("refreshing");
Modal.close("setup"); Modal.close("setup");
WebHandler.openPane(Pane.MAIN);
(document.getElementById("menuBtn") as HTMLButtonElement).disabled = false;
break; break;
} }
case iTenderStatus.STARTING: { case iTenderStatus.STARTING: {
@ -61,15 +62,16 @@ export class WebWebSocketHandler {
break; break;
} }
case iTenderStatus.REFRESHING: { case iTenderStatus.REFRESHING: {
let modal = new Modal("refreshing", "Aktualisieren..."); /* let modal = new Modal("refreshing", "Aktualisieren...");
let txt = document.createElement("p"); let txt = document.createElement("p");
txt.innerHTML = `Einen Augenblick bitte<br>iTender aktualisiert die Getränke...`; txt.innerHTML = `Einen Augenblick bitte<br>iTender aktualisiert die Getränke...`;
modal.addContent(txt); modal.addContent(txt);
modal.loader = true; modal.loader = true;
modal.open(); modal.open();*/
break; break;
} }
case iTenderStatus.SETUP: { case iTenderStatus.SETUP: {
Modal.close("start");
Setup.openSetup(); Setup.openSetup();
} }
} }
@ -83,6 +85,9 @@ export class WebWebSocketHandler {
case WebSocketEvent.CONTAINERS: { case WebSocketEvent.CONTAINERS: {
WebHandler.onContainerUpdate(payload); WebHandler.onContainerUpdate(payload);
if (WebWebSocketHandler.tareContainerUpdates != undefined) {
WebWebSocketHandler.tareContainerUpdates(payload);
}
break; break;
} }
} }
@ -117,9 +122,10 @@ export class WebWebSocketHandler {
modal.addContent(txt); modal.addContent(txt);
modal.loader = true; modal.loader = true;
modal.open(); modal.open();
setInterval(() => { setInterval(async () => {
if ((await WebWebSocketHandler.checkConnection()))
window.location.reload(); window.location.reload();
}, 5000); }, 2000);
} }
/* let connectionElement = document.getElementById("right"); /* let connectionElement = document.getElementById("right");
@ -129,6 +135,31 @@ export class WebWebSocketHandler {
}*/ }*/
} }
private static checkConnection(): Promise<boolean> {
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) { private onError(event) {
console.error("[WS] Error", event); console.error("[WS] Error", event);
/*let connectionElement = document.getElementById("right"); /*let connectionElement = document.getElementById("right");
@ -139,6 +170,7 @@ export class WebWebSocketHandler {
} }
public static send(payload: WebSocketPayload): Promise<void> { public static send(payload: WebSocketPayload): Promise<void> {
console.log("[WS] Sending " + payload.event + " Event",payload);
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
if (this.socket && this.socket.readyState == 1) { if (this.socket && this.socket.readyState == 1) {

View File

@ -8,7 +8,8 @@ const time = document.getElementById("right");
document.addEventListener("DOMContentLoaded", () => { document.addEventListener("DOMContentLoaded", () => {
console.log("DOM Loaded"); console.log("DOM Loaded");
WebHandler.openPane(Pane.MAIN); setupOnClickEvents();
WebHandler.openPane(Pane.NONE);
let modal = new Modal("start", "iTender"); let modal = new Modal("start", "iTender");
let txt = document.createElement("p"); let txt = document.createElement("p");
@ -18,23 +19,38 @@ document.addEventListener("DOMContentLoaded", () => {
//modal.open(); //modal.open();
connect(); connect();
setTimeout( load, 1000); setTimeout(load, 100);
}); });
function setupOnClickEvents() {
const menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
menuBtn.disabled = true;
function load() function doMenu() {
{ if (WebHandler.currentPane != Pane.MENU) {
if(!main||!time) WebHandler.openPane(Pane.MENU);
menuBtn.innerText = "Start";
} else {
WebHandler.openPane(Pane.MAIN);
menuBtn.innerText = "Menü";
}
}
menuBtn.onclick = doMenu;
}
function load() {
if (!main || !time)
return; return;
setInterval( () => { setInterval(() => {
let currentDate = new Date(); let currentDate = new Date();
time.innerText = "" + ( currentDate.getHours() < 10 ? "0" + currentDate.getHours() : currentDate.getHours() ) + ":" + ( currentDate.getMinutes() < 10 ? "0" + currentDate.getMinutes() : currentDate.getMinutes() ); time.innerText = "" + (currentDate.getHours() < 10 ? "0" + currentDate.getHours() : currentDate.getHours()) + ":" + (currentDate.getMinutes() < 10 ? "0" + currentDate.getMinutes() : currentDate.getMinutes());
}, 1000 ); }, 1000);
} }
let wsHandler; let wsHandler;
function connect()
{ function connect() {
wsHandler = new WebWebSocketHandler(); wsHandler = new WebWebSocketHandler();
} }

View File

@ -8,7 +8,7 @@ block setup
input#ledCheckbox.input(type="checkbox") input#ledCheckbox.input(type="checkbox")
div.inputGroup div.inputGroup
label GPIO-Pin 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 div.inputGroup
label Ambiente Farbe label Ambiente Farbe
input#ambientColor.input(type="color" value="#05445E" style="width:15%" disabled="disabled") input#ambientColor.input(type="color" value="#05445E" style="width:15%" disabled="disabled")
@ -31,11 +31,23 @@ block setup
div#setupContainers 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 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 block menu
// 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 block settings
// Settings // Settings

View File

@ -20,14 +20,14 @@ html
div#containers div#containers
div#container div#container
div.pane#main
block main
div.pane#setup div.pane#setup
block setup block setup
div.pane#menu div.pane#menu
block menu block menu
div.pane#settings div.pane#settings
block settings block settings
div.pane#main
block main
block extra block extra
script(src="/web.js") script(src="/web.js")