Took 7 hours 48 minutes
This commit is contained in:
Tobias Hopp 2022-11-17 23:29:33 +01:00
parent c81713cd23
commit 03dd0e0fb6
20 changed files with 642 additions and 211 deletions

View File

@ -1,10 +1,12 @@
.btn { .btn {
padding: 12px 16px; padding: 11px 15px;
border: none; border: none;
color: white; color: white;
font-size: 0.95em; font-size: 0.96em;
border-radius: 2px; border-radius: 2px;
cursor: none !important; cursor: none !important;
font-weight: 500;
margin-right: 2%;
} }
@ -110,10 +112,10 @@ label {
.input[type=checkbox] { .input[type=checkbox] {
margin-bottom: 2px; margin-bottom: 2px;
/* Double-sized Checkboxes */ /* Double-sized Checkboxes */
-ms-transform: scale(1.5); /* IE */ -ms-transform: scale(1.6); /* IE */
-moz-transform: scale(1.5); /* FF */ -moz-transform: scale(1.6); /* FF */
-webkit-transform: scale(1.5); /* Safari and Chrome */ -webkit-transform: scale(1.6); /* Safari and Chrome */
-o-transform: scale(1.5); /* Opera */ -o-transform: scale(1.6); /* Opera */
transform: scale(1.5); transform: scale(1.6);
padding: 10px; padding: 12px;
} }

View File

@ -4,34 +4,28 @@
padding-top: 1%; padding-top: 1%;
display: grid; display: grid;
grid-template-columns: repeat(2, calc(95% / 2)); grid-template-columns: repeat(2, calc(95% / 2));
grid-template-rows: repeat(7, calc(100% / 7)); grid-template-rows: repeat(9, calc(100% / 9));
grid-gap: 2% 5%; grid-gap: 2% 2%;
color: white;
} }
#setup_slots {
min-width: 55px;
width: 10%;
margin-left: 3%;
border: 1px solid aliceblue;
}
#setup #setupContainersDiv { #setup #setupContainersDiv {
grid-row: span 5; grid-row: span 18;
grid-column: span 2; grid-column: span 2;
width: 100%; width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
border: 1px solid cadetblue; border: 1px solid white;
border-radius: 5px; border-radius: 5px;
padding: 1% 10px; padding: 0.45% 10px;
overflow: auto; overflow: auto;
} }
#setup #setupContainersDiv #containerAddBtn { #setup #setupContainersDiv #containerAddBtn {
position: relative; position: relative;
left: 43%; left: 46%;
bottom: 10%; bottom: 8%;
} }
#setup #setupLEDDiv { #setup #setupLEDDiv {
@ -39,7 +33,7 @@
grid-column: span 1; grid-column: span 1;
height: 100%; height: 100%;
text-align: center; text-align: center;
border: 1px solid cadetblue; border: 1px solid white;
border-radius: 5px; border-radius: 5px;
padding: 1% 10px; padding: 1% 10px;
margin-right: 10%; margin-right: 10%;
@ -52,7 +46,7 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
text-align: center; text-align: center;
border: 1px solid cadetblue; border: 1px solid white;
border-radius: 5px; border-radius: 5px;
padding: 1% 10px; padding: 1% 10px;
@ -73,7 +67,7 @@
border-radius: 10px; border-radius: 10px;
padding: 3% 10px; padding: 3% 10px;
transition: 0.5s; transition: 0.5s;
background-color: rgb(111, 109, 109); background-color: rgb(135, 133, 133);
overflow: hidden; overflow: hidden;
} }

View File

@ -34,10 +34,10 @@ body {
} }
h1 { h1 {
font-size: 1.7em; font-size: 1.74em;
font-weight: 500; font-weight: 500;
margin-bottom: 2%; margin-bottom: 2%;
color: black; color: white;
} }
@ -53,12 +53,11 @@ h1 {
left: 0; left: 0;
right: 0; right: 0;
height: 9%; height: 9%;
background-color: #1F5E5F;
color: white; color: white;
font-size: 2em; font-size: 2em;
padding-right: 3px; padding-right: 3px;
padding-left: 3px; padding-left: 3px;
background-color: #167fcc;
} }
@ -94,7 +93,7 @@ h1 {
left: 0; left: 0;
right: 0; right: 0;
height: 9%; height: 9%;
background-color: #1F5E5F; background-color: #167fcc;
} }
@ -121,10 +120,12 @@ h1 {
#container { #container {
position: absolute; position: absolute;
top: 10%; top: 9%;
left: 1%; left: 0;
right: 1%; right: 0;
height: 80%; height: 82%;
color: white;
background-color: #0e1f31;
} }
.pane { .pane {
@ -134,7 +135,7 @@ h1 {
ms-overflow-style: none; /* IE and Edge */ ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */ scrollbar-width: none; /* Firefox */
scroll-behavior: smooth; scroll-behavior: smooth;
color: black; color: white;
} }
.hiddenPane { .hiddenPane {

View File

@ -1,3 +1,14 @@
export class HX711 { export class HX711 {
private clockPin: number;
private dataPin: number;
constructor(clockPin: number, dataPin: number) {
this.clockPin = clockPin;
this.dataPin = dataPin;
}
public measure(): number {
return 0;
}
} }

5
src/SensorType.ts Normal file
View File

@ -0,0 +1,5 @@
export enum SensorType {
NONE="NONE",
ULTRASOUND="ULTRASOUND",
LOADCELL="LOADCELL"
}

View File

@ -2,12 +2,15 @@ import * as fs from "fs";
import path from "path"; import path from "path";
export class Settings { export class Settings {
static get json(): {} {
return this._json;
}
static get setupDone(): boolean { static get setupDone(): boolean {
return this._setupDone; return this._setupDone;
} }
private static _setupDone: boolean; private static _setupDone: boolean;
private static json: {}; private static _json: {};
public static loadSettings() { public static loadSettings() {
@ -17,22 +20,22 @@ export class Settings {
} }
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"];
this._setupDone = false; this._setupDone = false;
} }
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 {
return this.json[key]; return this._json[key];
} }
public static set(key: string, value: any) { public static set(key: string, value: any) {
this.json[key] = value; this._json[key] = value;
} }
} }

View File

@ -2,4 +2,6 @@ export enum WebSocketEvent {
STATUS= "STATUS", STATUS= "STATUS",
DRINKS = "DRINKS", DRINKS = "DRINKS",
CONTAINERS = "CONTAINERS", CONTAINERS = "CONTAINERS",
CONFIG = "CONFIG",
TARE = "TARE",
} }

View File

@ -1,6 +1,7 @@
import {WebSocketPayload} from "./WebSocketPayload"; import {WebSocketPayload} from "./WebSocketPayload";
import {WebSocketEvent} from "./WebSocketEvent"; import {WebSocketEvent} from "./WebSocketEvent";
import {iTender} from "./iTender"; import {iTender} from "./iTender";
import {Settings} from "./Settings";
export class WebSocketHandler { export class WebSocketHandler {
private static _ws: WebSocket; private static _ws: WebSocket;
@ -33,4 +34,10 @@ export class WebSocketHandler {
}); });
} }
static sendRunningConfig() {
return new Promise(resolve => {
let payload = new WebSocketPayload(WebSocketEvent.CONFIG, false, Settings.json);
WebSocketHandler.send(payload).then(resolve);
})
}
} }

View File

@ -8,6 +8,7 @@ export const ContainerSchema = new Mongoose.Schema<IContainer>({
sensorType: String, sensorType: String,
sensorPin1: Number, sensorPin1: Number,
sensorPin2: Number, sensorPin2: Number,
rawData: Number,
pumpPin: {type: Number, required: true}, pumpPin: {type: Number, required: true},
content: {type: mongoose.Types.ObjectId, ref: "Ingredient"}, content: {type: mongoose.Types.ObjectId, ref: "Ingredient"},
sensorFilledMax: Number, sensorFilledMax: Number,

View File

@ -1,5 +1,6 @@
import {IIngredient} from "./IIngredient"; import {IIngredient} from "./IIngredient";
import * as mongoose from "mongoose"; import * as mongoose from "mongoose";
import {SensorType} from "../SensorType";
export interface IContainer extends mongoose.Document { export interface IContainer extends mongoose.Document {
slot: number; slot: number;
@ -7,11 +8,13 @@ export interface IContainer extends mongoose.Document {
volume: number; volume: number;
sensorFilledMin: number; sensorFilledMin: number;
sensorFilledMax: number; sensorFilledMax: number;
sensorType: string; // Sensor Type
sensorType: SensorType;
sensorPin1: number; sensorPin1: number;
sensorPin2: number; sensorPin2: number;
rawData: number;
pumpPin: number; pumpPin: number;
filled: Number; filled: number;
enabled: boolean; enabled: boolean;
autoDisabled: boolean; autoDisabled: boolean;
} }

View File

@ -11,6 +11,7 @@ import {Utils} from "./Utils";
import {WebSocketPayload} from "./WebSocketPayload"; import {WebSocketPayload} from "./WebSocketPayload";
import {WebSocketEvent} from "./WebSocketEvent"; import {WebSocketEvent} from "./WebSocketEvent";
import {HX711} from "./HX711"; import {HX711} from "./HX711";
import {SensorType} from "./SensorType";
const log = debug("itender:station"); const log = debug("itender:station");
@ -35,6 +36,10 @@ export class iTender {
return this._internetConnection; return this._internetConnection;
} }
/**
* @Deprecated
* @private
*/
private static _containers: { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] = []; private static _containers: { container: IContainer, sensor: HCSR04 | HX711, pump: null }[] = [];
private static _drinks: IDrink[]; private static _drinks: IDrink[];
@ -64,28 +69,34 @@ export class iTender {
log("Measuring containers..."); log("Measuring containers...");
return new Promise(async resolve => { return new Promise(async resolve => {
for (let c of this._containers) { for (let c of (await Container.find({enabled: true}))) {
try { try {
if (!(c.sensor instanceof HX711)) { let rand = Math.random() * 5;
let dist = c.sensor.distance(); if (c.sensorType == SensorType.ULTRASOUND) {
c.container.filled = dist * 100 / (c.container.sensorFilledMax + c.container.sensorFilledMin); let sensor = new HCSR04(c.sensorPin1, c.sensorPin2);
}
} catch (e) { c.rawData = sensor.distance();
c.container.filled = -1; c.rawData = rand;
c.filled = c.rawData * c.sensorFilledMin / 100;
} else if (c.sensorType == SensorType.LOADCELL) {
let sensor = new HX711(c.sensorPin1, c.sensorPin2);
c.rawData = sensor.measure();
c.rawData = rand;
c.filled = c.rawData * c.sensorFilledMax / 100;
} }
await c.container.save(); } catch (e) {
c.filled = -1;
c.rawData = 0;
}
await c.save();
} }
log("Containers measured!"); log("Containers measured!");
resolve(); resolve();
let cons: IContainer[] = []; let payload = new WebSocketPayload(WebSocketEvent.CONTAINERS, false, (await Container.find()));
for (let c of this._containers) { await WebSocketHandler.send(payload);
cons.push(c.container);
}
let payload = new WebSocketPayload(WebSocketEvent.CONTAINERS, false, cons);
}); });
} }
@ -117,6 +128,9 @@ export class iTender {
}); });
} }
/**
* @Deprecated
*/
static refreshContainers(): Promise<void> { static refreshContainers(): Promise<void> {
log("Refreshing containers..."); log("Refreshing containers...");
this.setStatus(iTenderStatus.CALCULATING); this.setStatus(iTenderStatus.CALCULATING);
@ -176,6 +190,7 @@ export class iTender {
this._currentJob.endAt = new Date(); this._currentJob.endAt = new Date();
await this._currentJob.save(); await this._currentJob.save();
this._currentJob = null; this._currentJob = null;
this.setStatus(iTenderStatus.READY);
} }
}, 30000); }, 30000);
} }
@ -196,4 +211,14 @@ export class iTender {
}); });
} }
private static interval;
public static toggleTare(state: boolean) {
clearInterval(iTender.interval);
if ( state )
this.interval = setInterval(async () => {
await this.measureContainers();
}, 500);
}
} }

View File

@ -69,7 +69,8 @@ function init(): Promise<void> {
} }
// Containers // Containers
await iTender.refreshContainers(); //await iTender.refreshContainers();
await iTender.measureContainers();
// Drinks // Drinks
await iTender.refreshDrinks(); await iTender.refreshDrinks();
// Start auto checkup for stuck jobs // Start auto checkup for stuck jobs

View File

@ -4,6 +4,8 @@ import {WebSocketHandler} from "../../WebSocketHandler";
import {iTender} from "../../iTender"; import {iTender} from "../../iTender";
import {iTenderStatus} from "../../iTenderStatus"; import {iTenderStatus} from "../../iTenderStatus";
import {WebSocketEvent} from "../../WebSocketEvent"; import {WebSocketEvent} from "../../WebSocketEvent";
import Container from "../../database/Container";
import {SensorType} from "../../SensorType";
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
@ -14,11 +16,13 @@ const log = debug("itender:websocket");
router.ws('/', async (ws, req, next) => { router.ws('/', async (ws, req, next) => {
log("Incoming websocket connection..."); log("Incoming websocket connection...");
if (WebSocketHandler.ws) if (WebSocketHandler.ws) {
iTender.toggleTare(false);
WebSocketHandler.ws.close(1001); WebSocketHandler.ws.close(1001);
}
WebSocketHandler.ws = ws; WebSocketHandler.ws = ws;
await WebSocketHandler.sendRunningConfig();
await WebSocketHandler.sendStatus(); await WebSocketHandler.sendStatus();
async function sendWhenReady() { async function sendWhenReady() {
@ -50,7 +54,31 @@ router.ws('/', async (ws, req, next) => {
} }
switch (msg.event) { switch (msg.event) {
case WebSocketEvent.TARE: {
if (msg.data["state"] == true) {
iTender.toggleTare(true);
} else {
iTender.toggleTare(false);
}
break;
}
case WebSocketEvent.CONTAINERS: {
let data = msg.data as { pumpPin: number; sensorType: SensorType; sensor1: number; sensor2: number; volume: number; }[];
await Container.remove({});
let i = 0;
for (let c of data) {
let container = new Container();
container.slot = i;
container.volume = c.volume;
container.sensorType = c.sensorType;
container.sensorPin1 = c.sensor1;
container.sensorPin2 = c.sensor2;
container.enabled = true;
container.autoDisabled = true;
i++;
}
}
} }
}); });

View File

@ -1,4 +1,5 @@
export enum ButtonType { export enum ButtonType {
PRIMARY = "primary",
SUCCESS = "success", SUCCESS = "success",
ERROR = "error", ERROR = "error",

View File

@ -44,7 +44,7 @@ export class Modal {
this._loader = value; this._loader = value;
} }
public addButton(type: ButtonType, content: string, onclick: Function): HTMLButtonElement { public addButton(type: ButtonType, content: string, onclick: Function = () => {}): HTMLButtonElement {
let btn = document.createElement("button"); let btn = document.createElement("button");
btn.classList.add("btn", "btn-" + type); btn.classList.add("btn", "btn-" + type);
btn.onclick = () => onclick(btn); btn.onclick = () => onclick(btn);
@ -55,6 +55,8 @@ export class Modal {
return btn; return btn;
} }
public open(): Promise<void> { public open(): Promise<void> {
return new Promise(async (resolve) => { return new Promise(async (resolve) => {
/* if (this._leftCentered) { /* if (this._leftCentered) {
@ -119,6 +121,10 @@ export class Modal {
} }
public close() : void {
Modal.close(this._id);
}
public static close(id?: string): void { public static close(id?: string): void {
if (id && this.currentModalId != id) if (id && this.currentModalId != id)
return; return;

View File

@ -1,7 +1,428 @@
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";
export class Setup { export class Setup {
public static onSetupUpdate() public static onConfigUpdate(payload: WebSocketPayload) {
{
// Setup containers updated // 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"];
}
}
static async openSetup() {
// new
WebHandler.openPane(Pane.SETUP);
let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
menuBtn.disabled = true;
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;
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.<br>
Während der Einmessung müssen die Behälter je nachdem geleert, gefüllt oder komplett entnommen 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 payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: true});
let modal = new Modal("tare", "Einmessung");
let txt = document.createElement("p");
txt.innerHTML = `Messung Teil 1<br>
Bitte alle <strong>Behälter entfernen</strong> und Sensoren freilegen.<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 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 ohne Inhalt</strong> 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);
txt.innerHTML = `Messung Teil 3<br>
Bitte nun alle <strong>Behälter mit vollständigem Inhalt</strong> 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: 2});
WebWebSocketHandler.send(payload);
payload = new WebSocketPayload(WebSocketEvent.TARE, false, {state: false});
WebWebSocketHandler.send(payload);
txt.innerHTML = `Alle Werte wurden erfolgreich gespeichert.<br>Die Einmessung kann jederzeit über die Speichern-Navigation wiederholt werden.<br><br>Das Einmessen sollte nach mehrfachem Benutzen wiederholt werden.<br>`;
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);
}
}
}, 1000);
}).catch(() => {
setupSaveBtn.disabled = false;
txt.innerHTML = `Fehler beim Speichern.<br>iTender hat nicht reagiert.`;
setTimeout(() => saveModal.close(), 2500);
});
} }
} }
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;
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];
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 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;
}
}

View File

@ -3,7 +3,6 @@ import {IDrink} from "../database/IDrink";
import {Modal} from "./Modal"; import {Modal} from "./Modal";
import {ButtonType} from "./ButtonType"; import {ButtonType} from "./ButtonType";
import {Pane} from "./Pane"; import {Pane} from "./Pane";
import {setup} from "rpi-gpio";
export class WebHandler { export class WebHandler {
private static containers = []; private static containers = [];
@ -63,110 +62,8 @@ ${ingredients}`*/ //todo
} }
static async openSetup() { static async openSetup() {
// new
this.openPane(Pane.SETUP);
let menuBtn = document.getElementById("menuBtn") as HTMLButtonElement;
let setupContainers = document.getElementById("setupContainers") as HTMLDivElement;
menuBtn.disabled = true;
const containerAddBtn = document.getElementById("containerAddBtn") as HTMLButtonElement;
containerAddBtn.onclick = () => {
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 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);
con.append(document.createElement("br"));
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;
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;
selectPin.append(pinEle);
}
let pumpLabel = document.createElement("label");
pumpLabel.innerText = "Pumpen Pin";
con.append(pumpLabel);
con.append(selectPin.cloneNode(true));
con.append(document.createElement("br"));
let sensor1Label = document.createElement("label");
sensor1Label.innerText = "Sensor 1 Pin";
con.append(sensor1Label);
con.append(selectPin.cloneNode(true));
con.append(document.createElement("br"));
let sensor2Label = document.createElement("label");
sensor2Label.innerText = "Sensor 2 Pin";
con.append(sensor2Label);
con.append(selectPin.cloneNode(true));
con.append(document.createElement("br"));
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);
};
return; return;
// old // old

View File

@ -4,17 +4,21 @@ import {WebSocketPayload} from "../WebSocketPayload";
import {ButtonType} from "./ButtonType"; import {ButtonType} from "./ButtonType";
import {iTenderStatus} from "../iTenderStatus"; import {iTenderStatus} from "../iTenderStatus";
import {WebHandler} from "./WebHandler"; import {WebHandler} from "./WebHandler";
import {Setup} from "./Setup";
export class WebWebSocketHandler { export class WebWebSocketHandler {
private socket: WebSocket; private static socket: WebSocket;
private static url = (window.location.protocol == "http:" ? "ws://" : "wss://") + window.location.hostname + ":3005"; private static url = (window.location.protocol == "http:" ? "ws://" : "wss://") + window.location.hostname + ":3005";
public static tareContainerUpdates: (payload: WebSocketPayload) => void;
constructor() { constructor() {
this.socket = new WebSocket(WebWebSocketHandler.url); WebWebSocketHandler.socket = new WebSocket(WebWebSocketHandler.url);
this.socket.onopen = this.onOpen; WebWebSocketHandler.socket.onopen = this.onOpen;
this.socket.onclose = this.onClose; WebWebSocketHandler.socket.onclose = this.onClose;
this.socket.onerror = this.onError; WebWebSocketHandler.socket.onerror = this.onError;
this.socket.onmessage = this.onMessage; WebWebSocketHandler.socket.onmessage = this.onMessage;
} }
private onMessage(msgEvent: MessageEvent) { private onMessage(msgEvent: MessageEvent) {
@ -24,13 +28,15 @@ export class WebWebSocketHandler {
console.log("[WS] Could not parse message: ", msgEvent); console.log("[WS] Could not parse message: ", msgEvent);
return; return;
} }
console.debug(payload)
console.debug(payload.event);
console.log("[WS] Received " + payload.event + " Event"); console.log("[WS] Received " + payload.event + " Event");
switch (payload.event) { switch (payload.event) {
case WebSocketEvent.CONFIG: {
// Incoming WebSocketStatus
Setup.onConfigUpdate(payload);
break;
}
case WebSocketEvent.STATUS: { case WebSocketEvent.STATUS: {
let statusElement = document.getElementById("status"); let statusElement = document.getElementById("status");
if (statusElement) if (statusElement)
@ -64,7 +70,7 @@ export class WebWebSocketHandler {
break; break;
} }
case iTenderStatus.SETUP: { case iTenderStatus.SETUP: {
WebHandler.openSetup(); Setup.openSetup();
} }
} }
break; break;
@ -100,7 +106,7 @@ export class WebWebSocketHandler {
let txt = document.createElement("p"); let txt = document.createElement("p");
txt.innerHTML = `Diese Sitzung wurde beendet, da der iTender nun an einem anderen Gerät bzw. an dem Hauptgerät gesteuert wird.<br><br>`; txt.innerHTML = `Diese Sitzung wurde beendet, da der iTender nun an einem anderen Gerät bzw. an dem Hauptgerät gesteuert wird.<br><br>`;
modal.addContent(txt); modal.addContent(txt);
modal.addButton(ButtonType.SUCCESS, "Sitzung wiederherstellen", () => { modal.addButton(ButtonType.PRIMARY, "Sitzung wiederherstellen", () => {
window.location.reload(); window.location.reload();
}); });
modal.open(); modal.open();
@ -131,4 +137,24 @@ export class WebWebSocketHandler {
//openModal("Einen Augenblick...", `Es wurde ein kritischer Fehler festgestellt.\nBitte warten Sie, während der Prozess neu gestartet wird...` ); //openModal("Einen Augenblick...", `Es wurde ein kritischer Fehler festgestellt.\nBitte warten Sie, während der Prozess neu gestartet wird...` );
//window.location.reload(); //window.location.reload();
} }
public static send(payload: WebSocketPayload): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
if (this.socket && this.socket.readyState == 1) {
await this.socket.send(payload.toString());
resolve();
}
} catch (e) {
/*let modal = new Modal("error", "Verbindungsfehler");
let txt = document.createElement("p");
txt.innerHTML = `Beim Austausch von Informationen ist ein Problem aufgetreten.<br>Falls dies öfter passieren sollte, ist der Support zu kontaktieren.`;
modal.addContent(txt);
await modal.open();*/
reject();
}
});
}
} }

View File

@ -1,6 +1,28 @@
extends layout extends layout
block setup block setup
div#setupLEDDiv
h1 LED-Einstellungen
div.inputGroup
label(onclick="document.getElementById('ledCheckbox').checked = !document.getElementById('ledCheckbox').checked;") Aktivieren
input#ledCheckbox.input(type="checkbox")
div.inputGroup
label GPIO-Pin
input#ledGPIO.input(type="number" value="22" style="width:15%" disabled="disabled")
div.inputGroup
label Ambiente Farbe
input#ambientColor.input(type="color" value="#05445E" style="width:15%" disabled="disabled")
div#setupExtraDiv
h1 Erweiterte Einstellungen
div.inputGroup
label(onclick="document.getElementById('allowRemoteCheckbox').checked = !document.getElementById('allowRemoteCheckbox').checked;") Remote-Verbindungen erlauben
input#allowRemoteCheckbox.input(type="checkbox")
div.inputGroup
label(onclick="document.getElementById('hotspotCheckbox').checked = !document.getElementById('hotspotCheckbox').checked;") Ohne WiFi Hotspot aktivieren
input#hotspotCheckbox.input(type="checkbox")
div#setupContainersDiv div#setupContainersDiv
h1 Behälter h1 Behälter
// Setup // Setup
@ -9,32 +31,7 @@ block setup
div#setupContainers div#setupContainers
div#setupLEDDiv button.btn.btn-success#setup_saveBtn(style="grid-row: span 1; grid-column: span 2; border-radius: 15px; font-size: 1.2em;") Speichern
h1 LED-Einstellungen
div.inputGroup
label Aktivieren
input.input(type="checkbox")
div.inputGroup
label GPIO-Pin
input.input(type="number" value="22" style="width:15%" disabled="disabled")
div.inputGroup
label Ambiente Farbe
input.input(type="color" value="#05445e" style="width:15%" disabled="disabled")
div#setupExtraDiv
h1 Erweiterte Einstellungen
div.inputGroup
label Remote-Verbindungen erlauben
input.input(type="checkbox" id="setup_remoteCheckbox")
div.inputGroup
label Ohne WiFi Hotspot aktivieren
input.input(type="checkbox" id="setup_hotspotCheckbox")
div.inputGroup
label Ohne WiFi Hotspot aktivieren
input.input(type="checkbox" id="setup_hotspotCheckbox")
button.btn.btn-success(style="grid-row: span 1; grid-column: span 2; border-radius: 15px; font-size: 1.2em;") Speichern
block menu block menu
// Menu // Menu

View File

@ -14,7 +14,7 @@ html
div#top div#top
span#left <strong>Status:</strong> <span id="status">...</span> span#left <strong>Status:</strong> <span id="status">...</span>
span#title iTender span#title iTender
span#right Verbinden... span#right 00:00
div#bottom div#bottom
button.btn#menuBtn Menü button.btn#menuBtn Menü
div#containers div#containers