diff --git a/ToDo.md b/ToDo.md
index 4d320af..bac33b5 100644
--- a/ToDo.md
+++ b/ToDo.md
@@ -5,4 +5,7 @@
- [ ] Fix Fehler, wenn keine Getränke hinzugefügt worden sind
- [ ] Schriftarten Lokal machen
- [x] Probleme beim Laden der Container im Setup
--
\ No newline at end of file
+- [ ] Container option "Auto tare" hinzufügen
+ - Heißt, sobald ein Inhalt in einen Container eingestellt wird und das Volumen angegeben ist, werden die Sensoren auf den aktuellen Füllstand als 100% gesetzt
+- [ ] Nach Speichern des Setups und bei Einmessen auf Schließen, Setup verlassen
+- [ ]
\ No newline at end of file
diff --git a/doc/Notes.md b/doc/Notes.md
index 10a0cd8..5397ec9 100644
--- a/doc/Notes.md
+++ b/doc/Notes.md
@@ -1,4 +1,5 @@
# Notes und kleine Dokumentation
+
Was haben wir bereits am iTender Projekt gemacht?
@@ -6,26 +7,30 @@ Was haben wir bereits am iTender Projekt gemacht?
## Konzept-Erstellung
#### Ideen
+
- Grund-Ideen
- - Smarten Cocktail-Mischer
- - 4 Getränke Behälter (mit Saft, Sirup oder Likör bzw. Schnapps)
- - 4 Pumpen (Peristaltik Pumpe)
- - Raspberry Pi als Prozessoreinheit
- - Display in der Front mit Benutzeroberfläche
- - Automatisches filtern von Getränken, je nachdem welche "Zutaten" in den Behältern sind
- - Messung der aktuellen Füllmenge der Behälter, basierend auf Gewicht (mittels Wägezelle) oder Abstand zur Wasseroberfläche (mittels Ultraschall-Sensor)
-
+ - Smarten Cocktail-Mischer
+ - 4 Getränke Behälter (mit Saft, Sirup oder Likör bzw. Schnapps)
+ - 4 Pumpen (Peristaltik Pumpe)
+ - Raspberry Pi als Prozessoreinheit
+ - Display in der Front mit Benutzeroberfläche
+ - Automatisches filtern von Getränken, je nachdem welche "Zutaten" in den Behältern sind
+ - Messung der aktuellen Füllmenge der Behälter, basierend auf Gewicht (mittels Wägezelle) oder Abstand zur
+ Wasseroberfläche (mittels Ultraschall-Sensor)
+
- Nice to have
- - LED-Stripes für schöne Beleuchtung, basierend auf dem aktuellen Status der Maschine
- - Extra Schlauch für weitere außenstehende Getränke
- - Mit Bier-Fass Adapter?
- - Kühlung der Container mittels Peltierelement und Lüftern
+ - LED-Stripes für schöne Beleuchtung, basierend auf dem aktuellen Status der Maschine
+ - Extra Schlauch für weitere außenstehende Getränke
+ - Mit Bier-Fass Adapter?
+ - Kühlung der Container mittels Peltierelement und Lüftern
#### Erstes 3D-Modell
+
#### Neues 3D-Modell
+
@@ -35,12 +40,23 @@ Was haben wir bereits am iTender Projekt gemacht?
## Das Programm
#### Aufbau
+
- Das Programm ist aufgebaut in eine Client-Seite und eine Server-Seite
- Diese sind zur Sicherheit des Geräts voneinander getrennt
- Server und Client kommunizieren über einen WebSocket, welchen man sich als eine Art Chat-Kanal vorstellen kann
- Client und Server haben bestimmte Status, ein Status ist beispielsweise READY oder FILLING
- Die Oberfläche ist sowohl über das Display, aber auch über ein Tablet steuerbar
-- Oberfläche sendet Befehle an den Server → Server verarbeitet und gibt ggfs. eine Antwort
+- Oberfläche sendet Befehle an den Server → Server verarbeitet und gibt ggf. eine Antwort
+- Bei jedem start wird der Status des Netzwerkes erfasst
+ - Sollte Netzwerk zu verfügung stehen, versucht iTender die Getränke vom Hauptserver (im Internet) zu aktualisieren
+ - Somit kommen auch beim bestehenden Produkt immer neue Getränke und mögliche Zutaten dazu
+- Nach jedem mischen und alle 5 minuten werden die Füllstände der Behälter erfasst
+- Danach werden auch die verfügbaren Cocktails berechnet, diese werden dann im Webinterface angezeigt
+
+#### Code fakten
+
+- Inzwischen hat der Programmiercode 14492 Zeilen
+- In Stunden wurde das Projekt (Stand 29.11) ~80 Stunden programmiert
#### Fotos des Webinterfaces (Stand 21.11)
@@ -48,27 +64,55 @@ Was haben wir bereits am iTender Projekt gemacht?
Die Main Pane ist der Hauptteil und direkt die Einstiegsseite des iTenders
Hier können Getränke ausgewählt werden, welche dann "gemacht" werden
-
+
+
Menu
Das Menü ist das Navigationsherz, von hier aus können alle anderen Panels erreicht werden
-
+
Containers
Hier können die Behälter inhalte aktualisiert werden
Man wählt die "Zutat" aus und danach wie voll der Behälter nun ist
In der Regel kann das auch automatisch eingemessen werden, wenn alle Sensoren eingestellt sind
-
Trotzdem sollte das hier eingestellt werden
-
+
+
+
Fill
Einfach ein "Popup" welches anzeigt dass das Getränk gefüllt wird
-
+
+
+
Setup
Das Setup ist das erste menü was nach dem ersten einrichten erscheint
es dient zur Grundkonfiguration
-
+
+
+
+
+
+
+
+### Erklärung der Dateien und Modulen
+
+| Context | Datei | Beschreibung |
+|-----------|---------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| iTender | main | Die Hauptdatei, sie ist der Einstiegspunkt des Programms, von hier aus wird die Datenbank verbunden und der Webserver gestartet |
+| iTender | MyGPIO | Die eigene GPIO Library von Tobias Hopp. Sie dient zum Steuern von GPIO-Pins am Raspberry Pi |
+| iTender | Utils | Hier befinden sich einige schnelle Funktionen, welche des öfteren von mehreren Methoden im Programm genutzt werden |
+| iTender | RejectReason, SensorType, RequestType | Ein paar Klassen welche Enums (sogenannte feste Platzhalter für Variablen) bereitstellen |
+| iTender | SensorType | Die Art des Sensors, aktuell sind Ultraschallsensoren und Wäge sensoren unterstützt |
+| Webserver | App | Die App ist die Instanz des Webservers, welcher für die Oberfläche genutzt wird, dieser wird vom iTender selbst, sowie etwaigen anderen Geräten aufgerufen |
+| Webserver | WebsocketApp | Ähnlich wie die normale App, nur spezifisch für die Direktverbindung zwischen Oberfläche und Server |
+| iTender | LEDHandler | Übernimmt die Steuerung der WS2812b LEDs |
+| iTender | Category | Kategorie des Getränks |
+| iTender | Settings | Die Einstellungen des iTenders, werden gespeichert in der config.json |
+| Webseite | error.pug, index.pug, layout.pug | Die statische Webseite für den iTender, diese Oberfläche liest die JavaScript Dateien ein und verbindet sich dann mit dem WebSocket (mit dem iTender) |
+| Compiler | dist/ Ordner | Hier sind alle kompilierten Dateien zu finden |
+| Webserver | web/main | Der Einstiegspunkt für die Weboberfläche, von hier aus wird mit dem WebSocket verbunden |
+| | | |
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index 71fdf59..c12a56c 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -48,6 +48,32 @@ body {
user-select: none; /* Standard syntax */
}
+#blockPanel {
+ z-index: 999;
+ position: fixed;
+ top:0;
+ left:0;
+ background-color: rgba(0,0,0,0.9);
+ width: 100vw;
+ height: 100vh;
+;
+ transition: opacity 0.4s;;
+}
+
+.opacityOutDisplayNone {
+ animation: opacityOutDisplayNone 0.4s linear forwards;
+
+}
+
+@keyframes opacityOutDisplayNone {
+ 0% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ height: 0;
+ }
+}
h1 {
font-size: 1.74em;
diff --git a/src/Utils.ts b/src/Utils.ts
index d5dce86..cb76107 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -42,6 +42,8 @@ export class Utils {
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}`));
}
+ }).on("error", (e) => {
+ reject(new Error("Request failed " + e))
});
});
}
diff --git a/src/WebSocketEvent.ts b/src/WebSocketEvent.ts
index 502dc66..713a988 100644
--- a/src/WebSocketEvent.ts
+++ b/src/WebSocketEvent.ts
@@ -8,6 +8,6 @@ export enum WebSocketEvent {
SETUP = "SETUP",
REQUEST = "REQUEST",
RESPONSE = "RESPONSE",
- FILL = "FILL",
- CANCEL = "CANCEL"
+ CANCEL = "CANCEL",
+ ERROR = "ERROR",
}
\ No newline at end of file
diff --git a/src/iTender.ts b/src/iTender.ts
index 6f0831a..f1f1d19 100644
--- a/src/iTender.ts
+++ b/src/iTender.ts
@@ -17,7 +17,6 @@ import {IIngredient} from "./database/IIngredient";
import Ingredient from "./database/Ingredient";
import {clearInterval} from "timers";
import {RejectReason} from "./RejectReason";
-import {Settings} from "./Settings";
import axios from "axios";
import GPIO from "rpi-gpio";
import {MyGPIO} from "./MyGPIO";
@@ -27,6 +26,10 @@ const isPI = require("detect-rpi");
const log = debug("itender:station");
const mixLog = debug("itender:mix");
+
+/**
+ * The main class of the itender, here a located all main features of the system, like starting pumps, firing events and stuff
+ */
export class iTender {
private static secondsPer100ml: number = 35.3335;
@@ -34,6 +37,9 @@ export class iTender {
return this._drinks;
}
+ /**
+ * The current job of the itender
+ */
static get currentJob(): IJob | null {
return this._currentJob;
}
@@ -43,6 +49,9 @@ export class iTender {
private static _jobCheckInterval: NodeJS.Timer;
private static _internetConnection: boolean = false;
+ /**
+ * Returns true if internet connection is active
+ */
static get internetConnection(): boolean {
return this._internetConnection;
}
@@ -60,6 +69,11 @@ export class iTender {
return this._status;
}
+
+ /**
+ * This method is fired if the user likes to mix a drink
+ * @param data
+ */
static onReceiveFill(data: { drink: IDrink, amounts?: { ingredient: String, amount: number }[], amount?: number }): Promise {
return new Promise(async (resolve, reject) => {
mixLog("Receiving fill");
@@ -71,6 +85,7 @@ export class iTender {
const job = new Job();
+
let amounts: { ingredient: IIngredient, amount: number, container?: IContainer }[] = [];
job.completeAmount = 0;
if (data.amounts) {
@@ -121,8 +136,7 @@ export class iTender {
console.log(amounts);
job.drink = drink
job.amounts = amounts as { ingredient: IIngredient, amount: number, container: IContainer }[];
- if( job.estimatedTime < 0.5 )
- {
+ if (job.estimatedTime < 0.5) {
job.estimatedTime = 1;
}
await job.save()
@@ -134,6 +148,11 @@ export class iTender {
}
+
+ /**
+ * Start the internal fill method
+ * @param job
+ */
static async startFill(job: IJob) {
job.startedAt = new Date();
await job.populate([{path: "amounts.ingredient"}, {path: "amounts.container"}, {path: "drink"}]);
@@ -149,7 +168,7 @@ export class iTender {
try {
await MyGPIO.setup(x.container.pumpPin, GPIO.DIR_OUT)
- await MyGPIO.write(x.container.pumpPin, true );
+ await MyGPIO.write(x.container.pumpPin, true);
} catch (e) {
if (isPI()) {
log("[ERROR] GPIO I/O Error " + e);
@@ -177,7 +196,7 @@ export class iTender {
mixLog(`Stopping output of pump ${x.container.pumpPin}`);
// Stop pump here
try {
- await MyGPIO.write(x.container.pumpPin, false );
+ await MyGPIO.write(x.container.pumpPin, false);
} catch (e) {
if (isPI()) {
log("[ERROR] GPIO I/O Error " + e);
@@ -204,11 +223,15 @@ export class iTender {
job.successful = true;
await job.save();
mixLog("Job successful");
- setTimeout( () => iTender.setStatus(iTenderStatus.READY), 3000 )
+ setTimeout(() => iTender.setStatus(iTenderStatus.READY), 3000)
}, 500);
}
+
+ /**
+ * Cancel the fill
+ */
static async cancelFill() {
if (!this._currentJob || this.status != iTenderStatus.FILLING)
return;
@@ -221,7 +244,7 @@ export class iTender {
for (let x of this._currentJob.amounts) {
// stop pump pin
try {
- await MyGPIO.write(x.container.pumpPin, false );
+ await MyGPIO.write(x.container.pumpPin, false);
} catch (e) {
}
@@ -231,6 +254,10 @@ export class iTender {
}
+
+ /**
+ * Measure all containers based on their sensor values
+ */
static measureContainers(): Promise {
log("Measuring containers...");
@@ -269,6 +296,11 @@ export class iTender {
});
}
+
+ /**
+ * Refresh the drinks to the local variable
+ * Check which drinks can be done, based on the current container ingredients
+ */
static refreshDrinks(): Promise {
log("Refreshing drinks...");
return new Promise(async resolve => {
@@ -326,6 +358,10 @@ export class iTender {
try {
const requestIngredients = await axios.get("https://itender.iif.li/api/ingredients");
let serverIngredients = requestIngredients.data as IIngredient[];
+ if (serverIngredients.length == 0) {
+ log("Got 0 ingredients from the server... aborting.");
+ throw new Error("Got 0 ingredients from the server, invalid");
+ }
log("Got " + serverIngredients.length + " ingredients from server");
let localIngredients = await Ingredient.find();
@@ -362,6 +398,10 @@ export class iTender {
const requestDrinks = await axios.get("https://itender.iif.li/api/drinks");
let serverDrinks = requestDrinks.data as IDrink[];
+ if (serverDrinks.length == 0) {
+ log("Got 0 drinks from the server... aborting.");
+ throw new Error("Got 0 drinks from the server, invalid");
+ }
log("Got " + serverDrinks.length + " drinks from server");
@@ -405,16 +445,16 @@ export class iTender {
log("Drink " + remote.name + " failed to download thumbnail! (" + url + ") | " + e);
}
}
-
}
} catch (e) {
- console.error(e);
+ console.error("Could not refresh drinks " + e);
+ await WebSocketHandler.send(new WebSocketPayload(WebSocketEvent.ERROR, false, "Beim aktualisieren der Getränke ist ein Netzwerk-Fehler aufgetreten.
Bitte später erneut versuchen!"));
}
iTender.setStatus(iTenderStatus.READY);
resolve();
- iTender.refreshDrinks();
+ await iTender.refreshDrinks();
});
}
diff --git a/src/web/Containers.ts b/src/web/Containers.ts
index 1e46815..a2e1d9b 100644
--- a/src/web/Containers.ts
+++ b/src/web/Containers.ts
@@ -8,6 +8,10 @@ import {WebSocketPayload} from "../WebSocketPayload";
import {WebSocketEvent} from "../WebSocketEvent";
export class Containers {
+
+ /**
+ * Open the menu for the container ingredient setup
+ */
static openMenu() {
let modal = new Modal("containers", "Behälter aktualisieren");
let txt = document.createElement("p");
@@ -24,6 +28,7 @@ export class Containers {
btnSave.disabled = true;
let containerVolumes: Record = {};
+ let containers: Record = {};
let volume = document.createElement("span");
volume.innerText = "";
@@ -35,6 +40,7 @@ export class Containers {
volumeSlider.style.visibility = "hidden";
volumeSlider.id = "containers_volumeSlider"
+ // When volume slider is changed
function onChange() {
volume.innerText = volumeSlider.value + " ml ";
txt.innerText = "Speichern zum abschließen"
@@ -52,6 +58,8 @@ export class Containers {
let selectIngredient = document.createElement("select");
selectIngredient.style.visibility = "hidden";
selectIngredient.classList.add("input");
+
+ // When ingredient is changed
selectIngredient.onchange = () => {
if (selectIngredient.value == "null") {
volumeSlider.value = "0";
@@ -78,27 +86,40 @@ export class Containers {
let selectContainer = document.createElement("select");
selectContainer.classList.add("input");
+ //let containers : IContainer[] = [];
selectContainer.onchange = () => {
+ // Enable select ingredient field and set max and min to the slider
selectIngredient.style.visibility = "visible";
volumeSlider.max = String(containerVolumes[selectContainer.value]);
volumeSlider.min = String(0);
volumeSlider.value = String(containerVolumes[selectContainer.value] / 2);
txt.innerText = "Ingredient des Behälters auswählen";
+
+ // When content of container is filled, preselect the ingredient selector
+ if (containers[selectContainer.value].content) {
+ selectIngredient.value = containers[selectContainer.value].content?._id;
+ let event = new Event('change', {bubbles: true});
+ selectIngredient.dispatchEvent(event);
+ }
+
+
}
selectContainer.append(nonSelect.cloneNode(true));
selectContainer.selectedIndex = 0;
+
WebWebSocketHandler.request(RequestType.CONTAINERS).then((payload) => {
for (let container of (payload.data["content"] as IContainer[])) {
containerVolumes[container._id] = container.volume;
let option = document.createElement("option");
option.value = container._id;
- option.innerText = "Behälter Slot " + container.slot + "[" + (container.content && container.content.name ? container.content.name : "Kein Inhalt") + "]";
+ option.innerText = "Behälter Slot " + (container.slot+1) + "[" + (container.content && container.content.name ? container.content.name : "Kein Inhalt") + "]";
selectContainer.append(option);
-
+ containers[container._id] = container;
}
+ //containers = payload.data["content"] as IContainer[];
});
WebWebSocketHandler.request(RequestType.INGREDIENTS).then((payload) => {
for (let ingredient of (payload.data["content"] as IIngredient[])) {
@@ -134,7 +155,14 @@ export class Containers {
ingredient: (selectIngredient.value == "null") ? null : selectIngredient.value,
filled: volumeSlider.value
});
- WebWebSocketHandler.send(payload).then(() => modal.close());
+
+ WebWebSocketHandler.send(payload).then(() => {
+ selectContainer.value = "-1";
+ selectIngredient.value = "-1";
+ let event = new Event('change', {bubbles: true});
+ selectContainer.dispatchEvent(event);
+ selectIngredient.dispatchEvent(event);
+ });
};
modal.open();
diff --git a/src/web/Fill.ts b/src/web/Fill.ts
new file mode 100644
index 0000000..e750927
--- /dev/null
+++ b/src/web/Fill.ts
@@ -0,0 +1,101 @@
+import {WebSocketPayload} from "../WebSocketPayload";
+import {Modal} from "./Modal";
+import {WebSocketEvent} from "../WebSocketEvent";
+import {RequestType} from "../RequestType";
+import {IJob} from "../database/IJob";
+import {WebWebSocketHandler} from "./WebWebSocketHandler";
+
+export class Fill {
+ static onFillEvent(payload: WebSocketPayload) {
+ let modal = new Modal("fill", "Cocktail wird zubereitet");
+ let header = document.createElement("h2");
+ header.innerText = "";
+
+ modal.addContent(header);
+
+ let txt = document.createElement("p");
+ txt.innerHTML = `Der Cocktail wird gerade zubereitet`;
+ txt.id = "main_fillTxt";
+
+ let waterAnimDiv = document.createElement("div");
+ waterAnimDiv.classList.add("water");
+ modal.addContent(txt);
+
+ modal.addContent(waterAnimDiv);
+
+ let seconds = document.createElement("span");
+ seconds.innerText = "60s";
+ seconds.style.marginRight = "3%";
+
+ modal.addContent(seconds);
+
+ let ml = document.createElement("span");
+ ml.innerText = "200ml";
+ modal.addContent(ml);
+
+ modal.addContent(document.createElement("br"));
+ modal.addContent(document.createElement("br"));
+
+
+ let cancelBtn = document.createElement("button");
+ cancelBtn.classList.add("btn", "btn-danger");
+ cancelBtn.innerText = "Abbrechen";
+ cancelBtn.disabled = true;
+ setTimeout(() => {
+ cancelBtn.disabled = false;
+ }, 1000);
+ cancelBtn.onclick = () => {
+ cancelBtn.disabled = true;
+ txt.innerHTML = "Der Vorgang wird abgebrochen...";
+ waterAnimDiv.classList.add("waterCancel");
+
+ WebWebSocketHandler.send(new WebSocketPayload(WebSocketEvent.CANCEL));
+ };
+ modal.addContent(cancelBtn);
+
+ function riseSlowlyUp(lastNumber: number, number: number) {
+ for (let i = lastNumber; i < number; i++) {
+ setTimeout(() => {
+ ml.innerText = Math.floor(i) + "ml";
+ }, (number - lastNumber / 1000) + i * 4);
+ }
+ }
+
+ modal.open().then(() => {
+ WebWebSocketHandler.request(RequestType.JOB).then((payload) => {
+ let minus = 0;
+ let job = payload.data.content as IJob;
+ ml.innerText = Math.floor((job.completeAmount / job.estimatedTime) * minus) + "ml";
+ waterAnimDiv.style.setProperty("--fillTime", job.estimatedTime + "s");
+ waterAnimDiv.style.backgroundImage = `url("/images/${job.drink._id}.png")`;
+ header.innerText = job.drink.name;
+ seconds.innerText = Math.floor(job.estimatedTime) + "s";
+
+ let last = 0;
+ let interval = setInterval(() => {
+ minus++;
+ if (minus + 1 > (job.estimatedTime as number)) {
+ clearInterval(interval);
+ }
+
+ seconds.innerText = (Math.floor(job.estimatedTime as number - minus)) + "s";
+ let calc = Math.floor((job.completeAmount / job.estimatedTime) * minus);
+ riseSlowlyUp(last, calc)
+ last = calc;
+ }, 1000);
+
+
+ setTimeout(() => {
+ txt.innerHTML = "Bitte entnehme den Cocktail";
+ modal.title.innerHTML = "Cocktail fertig gestellt"
+
+ cancelBtn.classList.add("btn-blendout");
+ waterAnimDiv.classList.add("waterFinished");
+ cancelBtn.onclick = () => {
+ modal.close();
+ }
+ }, job.estimatedTime * 1000);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/src/web/Modal.ts b/src/web/Modal.ts
index fc0f398..8921e6a 100644
--- a/src/web/Modal.ts
+++ b/src/web/Modal.ts
@@ -1,10 +1,13 @@
import {ButtonType} from "./ButtonType";
export class Modal {
+ get title(): HTMLHeadingElement {
+ return this._title;
+ }
private static currentModalId: string | undefined = "";
- private _title: string = "iTender";
+ private _title: HTMLHeadingElement;
private _id: string = "";
private _loader: boolean = false;
private _buttons: { type: string, content: string, onclick: Function }[] = [];
@@ -19,11 +22,10 @@ export class Modal {
constructor(id, title: string) {
this._id = id;
- this._title = title;
- let t = document.createElement("h1");
- t.innerText = title;
- this._elements.push(t);
+ this._title = document.createElement("h1") as HTMLHeadingElement;
+ this._title.innerText = title;
+ this._elements.push(this._title);
}
public static isModalOpen(): boolean {
@@ -91,6 +93,10 @@ export class Modal {
});
}
+ public setTitle(title) {
+
+ }
+
/**
* @param elements
* @param id
diff --git a/src/web/WebWebSocketHandler.ts b/src/web/WebWebSocketHandler.ts
index 1fe09e7..b3c3c53 100644
--- a/src/web/WebWebSocketHandler.ts
+++ b/src/web/WebWebSocketHandler.ts
@@ -7,7 +7,7 @@ import {WebHandler} from "./WebHandler";
import {Setup} from "./Setup";
import {Pane} from "./Pane";
import {RequestType} from "../RequestType";
-import {IJob} from "../database/IJob";
+import {Fill} from "./Fill";
export class WebWebSocketHandler {
private static socket: WebSocket;
@@ -29,8 +29,7 @@ export class WebWebSocketHandler {
}
public static registerForEvent(event: WebSocketEvent, fn: (payload: WebSocketPayload) => void) {
- for( let e of WebWebSocketHandler.eventRegister )
- {
+ for (let e of WebWebSocketHandler.eventRegister) {
if (e.fn == fn) {
console.log("Event fn already registered");
return;
@@ -55,11 +54,27 @@ export class WebWebSocketHandler {
switch (payload.event) {
case WebSocketEvent.CONFIG: {
- // Incoming WebSocketStatus
Setup.onConfigUpdate(payload);
break;
}
+ case WebSocketEvent.DRINKS: {
+ WebHandler.onDrinkUpdate(payload);
+ break;
+ }
+
+ case WebSocketEvent.ERROR: {
+ let modal = new Modal("error", "Aww crap!");
+ let txt = document.createElement("p");
+ txt.innerHTML = payload.data;
+ modal.addContent(txt);
+ modal.addContent(document.createElement("br"));
+ modal.addButton(ButtonType.PRIMARY, "Schließen", () => modal.close() );
+ modal.open();
+ break;
+ }
+
+ // Incoming WebSocketStatus
case WebSocketEvent.STATUS: {
let statusElement = document.getElementById("status");
if (statusElement)
@@ -90,19 +105,16 @@ export class WebWebSocketHandler {
case iTenderStatus.DOWNLOADING: {
let modal = new Modal("download", "Aktualisieren");
let txt = document.createElement("p");
- txt.innerHTML = `Einen Augenblick bitte
iTender aktualisiert die Getränke vom Server.`;
+ txt.innerHTML = `Einen Augenblick bitte
iTender aktualisiert die Getränke vom Server...`;
modal.addContent(txt);
modal.loader = true;
modal.open();
- 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();*/
+ setTimeout( () => {
+ if( txt )
+ {
+ txt.innerHTML = txt.innerHTML + "
Der Vorgang dauert länger als gewöhnlich.
Überprüfe deine Internetverbindung!"
+ }
+ }, 1000 * 15 )
break;
}
case iTenderStatus.SETUP: {
@@ -111,111 +123,16 @@ export class WebWebSocketHandler {
break;
}
case iTenderStatus.FILLING: {
-
- let modal = new Modal("fill", "Getränk wird ausgegeben");
- let header = document.createElement("h2");
- header.innerText = "";
-
- modal.addContent(header);
-
- let txt = document.createElement("p");
- txt.innerHTML = `Dein Cocktail wird gerade zubereitet`;
- txt.id = "main_fillTxt";
-
- let waterAnimDiv = document.createElement("div");
- waterAnimDiv.classList.add("water");
- modal.addContent(txt);
-
- modal.addContent(waterAnimDiv);
-
- let seconds = document.createElement("span");
- seconds.innerText = "60s";
- seconds.style.marginRight = "3%";
-
- modal.addContent(seconds);
-
- let ml = document.createElement("span");
- ml.innerText = "200ml";
- modal.addContent(ml);
-
- modal.addContent(document.createElement("br"));
- modal.addContent(document.createElement("br"));
-
-
- let cancelBtn = document.createElement("button");
- cancelBtn.classList.add("btn", "btn-danger");
- cancelBtn.innerText = "Abbrechen";
- cancelBtn.disabled = true;
- setTimeout(() => {
- cancelBtn.disabled = false;
- }, 1000);
- cancelBtn.onclick = () => {
- cancelBtn.disabled = true;
- txt.innerHTML = "Der Vorgang wird abgebrochen...";
- waterAnimDiv.classList.add("waterCancel");
-
- WebWebSocketHandler.send(new WebSocketPayload(WebSocketEvent.CANCEL));
- };
- modal.addContent(cancelBtn);
-
- function riseSlowlyUp(lastNumber:number, number: number) {
- for (let i = lastNumber; i < number; i++) {
- setTimeout(() => {
- ml.innerText = i + "ml";
- }, (number-lastNumber/1000)+i*4);
- }
- }
-
- modal.open().then(() => {
- WebWebSocketHandler.request(RequestType.JOB).then((payload) => {
- let minus = 0;
- let job = payload.data.content as IJob;
- ml.innerText = Math.floor((job.completeAmount / job.estimatedTime) * minus) + "ml";
- waterAnimDiv.style.setProperty("--fillTime", job.estimatedTime + "s");
- waterAnimDiv.style.backgroundImage = `url("/images/${job.drink._id}.png")`;
- header.innerText = job.drink.name;
- seconds.innerText = job.estimatedTime + "s";
-
- let last = 0;
- let interval = setInterval(() => {
- minus++;
- if (minus + 1 > (job.estimatedTime as number)) {
- clearInterval(interval);
- }
-
- seconds.innerText = (Math.floor(job.estimatedTime as number - minus)) + "s";
- let calc = Math.floor((job.completeAmount / job.estimatedTime) * minus);
- riseSlowlyUp(last, calc)
- last = calc;
- //ml.innerText = + "ml";
- }, 1000);
-
-
- setTimeout(() => {
- txt.innerHTML = "Bitte entnehme den Cocktail!";
- /*cancelBtn.classList.add("btn-primary");
- cancelBtn.classList.remove("btn-danger");
- cancelBtn.innerText = "Schließen";*/
- cancelBtn.classList.add("btn-blendout");
- waterAnimDiv.classList.add("waterFinished");
- cancelBtn.onclick = () => {
- modal.close();
- }
- //setTimeout(() => modal.close(), 1000 * 4.5);
- }, job.estimatedTime * 1000);
- });
- });
+ Fill.onFillEvent(payload);
break;
}
-
+ default: {
+ console.log("Unknown to handle " + status);
+ }
}
break;
}
- case WebSocketEvent.DRINKS: {
- WebHandler.onDrinkUpdate(payload);
- break;
- }
}
}
@@ -223,12 +140,8 @@ export class WebWebSocketHandler {
private onOpen(event) {
console.log("[WS] Connected", event);
- /*let connectionElement = document.getElementById("right");
- if (connectionElement) {
- connectionElement.innerText = "Verbunden";
- connectionElement.style.color = "green";
- }*/
-
+ const blockPanel = document.getElementById("blockPanel") as HTMLDivElement;
+ blockPanel.classList.add("opacityOutDisplayNone");
}
private onClose(event) {
diff --git a/views/layout.pug b/views/layout.pug
index 034af97..083a5bf 100644
--- a/views/layout.pug
+++ b/views/layout.pug
@@ -7,6 +7,7 @@ html
link(rel='stylesheet', href='/stylesheets/style.css')
meta(charset="UTF-8")
body
+ div#blockPanel
div.modal#modal
div.modal-content#modal-content
div.modalInnerContent#modalInnerContent