update
Took 25 hours 49 minutes
BIN
public/images/Blue Lagoon.png
Normal file
After Width: | Height: | Size: 353 KiB |
BIN
public/images/Caipirinha.png
Normal file
After Width: | Height: | Size: 35 KiB |
BIN
public/images/Cosmopolitan.png
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
public/images/Cubralibre.png
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/Gin Tonic.png
Normal file
After Width: | Height: | Size: 462 KiB |
BIN
public/images/Malibu Beach.png
Normal file
After Width: | Height: | Size: 31 KiB |
BIN
public/images/Mezzo Mix.png
Normal file
After Width: | Height: | Size: 1020 KiB |
BIN
public/images/Mojito.png
Normal file
After Width: | Height: | Size: 611 KiB |
BIN
public/images/Moscow Mule.png
Normal file
After Width: | Height: | Size: 313 KiB |
BIN
public/images/Pina Colada.png
Normal file
After Width: | Height: | Size: 167 KiB |
BIN
public/images/Sex on the Beach.png
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
public/images/Swimming Pool.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
public/images/Tequila Sunrise.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
public/images/Wodka Bull.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
public/images/Wodka Cola.png
Normal file
After Width: | Height: | Size: 253 KiB |
BIN
public/images/Wodka Fanta.png
Normal file
After Width: | Height: | Size: 433 KiB |
BIN
public/images/Wodka Lemon.png
Normal file
After Width: | Height: | Size: 250 KiB |
BIN
public/images/Wodka O.png
Normal file
After Width: | Height: | Size: 794 KiB |
BIN
public/images/Zombie.png
Normal file
After Width: | Height: | Size: 116 KiB |
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
cursor: none;
|
cursor: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ body {
|
|||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
font-family: "Roboto", serif;
|
font-family: "Roboto", serif;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
cursor: none;
|
cursor: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,15 +40,35 @@ body {
|
|||||||
height: 9%;
|
height: 9%;
|
||||||
background-color: #1F5E5F;
|
background-color: #1F5E5F;
|
||||||
color: white;
|
color: white;
|
||||||
|
font-size: 2em;
|
||||||
|
padding-right: 3px;
|
||||||
|
padding-left: 3px;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#title {
|
#title {
|
||||||
float: right;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 1.9em;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
float: left;
|
||||||
|
width: calc(100% / 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#left {
|
||||||
|
font-size: 0.5em;
|
||||||
|
float: left;
|
||||||
|
width: calc(100% / 3);
|
||||||
|
text-align: left;
|
||||||
|
padding-top: 1.2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#right {
|
||||||
|
float: left;
|
||||||
|
width: calc(100% / 3);
|
||||||
|
text-align: right;
|
||||||
|
font-size: 0.5em;
|
||||||
|
padding-top: 1.2%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -108,7 +128,7 @@ body {
|
|||||||
#main {
|
#main {
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 1% 2%;
|
||||||
grid-template-columns: repeat(3, calc(90% / 3));
|
grid-template-columns: repeat(3, calc(90% / 3));
|
||||||
grid-template-rows: repeat(2, calc(90% / 2));
|
grid-template-rows: repeat(2, calc(90% / 2));
|
||||||
grid-gap: 10% 5%;
|
grid-gap: 10% 5%;
|
||||||
@ -122,26 +142,44 @@ body {
|
|||||||
#main .drink {
|
#main .drink {
|
||||||
grid-row: span 1;
|
grid-row: span 1;
|
||||||
grid-column: span 1;
|
grid-column: span 1;
|
||||||
background-color: black;
|
background-color: rgba(57, 57, 57, 0.6);
|
||||||
width: 100%;
|
width: 90%;
|
||||||
height: 100%;
|
height: 97%;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 100%;
|
grid-template-columns: 100%;
|
||||||
grid-template-rows: repeat(3, calc(100% / 3));
|
grid-template-rows: repeat(3, calc(100% / 3));
|
||||||
grid-row-gap: 5%;
|
grid-row-gap: 4%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 30px 10px 30px;
|
border-radius: 30px 10px 30px;
|
||||||
|
color: black;
|
||||||
|
/*box-shadow: 3px 3px 3px;*/
|
||||||
|
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.3), 0 6px 20px 0 rgba(0, 0, 0, 0.29);
|
||||||
|
transition: 0.4s;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-bottom: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#main .drink:hover {
|
||||||
|
background-color: rgba(57, 57, 57, 0.8);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.drink .thumbnail {
|
.drink .thumbnail {
|
||||||
grid-column: span 1;
|
grid-column: span 1;
|
||||||
grid-row: span 2;
|
grid-row: span 2;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
max-height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: 0.5s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.drink .name {
|
.drink .drinkName {
|
||||||
|
font-family: Ubuntu, sans-serif;
|
||||||
grid-column: span 1;
|
grid-column: span 1;
|
||||||
grid-row: span 1;
|
grid-row: span 1;
|
||||||
font-size: 140%;
|
font-size: 150%;
|
||||||
}
|
}
|
@ -50,7 +50,7 @@ export class App {
|
|||||||
|
|
||||||
public loadRoutes( ) : void
|
public loadRoutes( ) : void
|
||||||
{
|
{
|
||||||
this._app.use( "/", require("./routes/index") );
|
this._app.use( "/", require("./routes/indexRouter") );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
export enum Category {
|
export enum Category {
|
||||||
ALCOHOLIC,
|
ALCOHOLIC = "ALCOHOLIC",
|
||||||
ALCOHOL_FREE,
|
ALCOHOL_FREE = "ALCOHOL_FREE",
|
||||||
SYRUP,
|
SYRUP = "SYRUP",
|
||||||
JUICE
|
JUICE = "JUICE",
|
||||||
|
SOFTDRINK = "SOFTDRINK"
|
||||||
}
|
}
|
@ -12,4 +12,11 @@ export class Utils {
|
|||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static sleep(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
setTimeout(() => resolve(), ms);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,7 +1,5 @@
|
|||||||
export enum WebSocketEvent {
|
export enum WebSocketEvent {
|
||||||
STATUS_STARTING,
|
STATUS= "STATUS",
|
||||||
STATUS_READY,
|
DRINKS = "DRINKS",
|
||||||
STATUS_FILLING,
|
CONTAINERS = "CONTAINERS",
|
||||||
STATUS_REFRESHING,
|
|
||||||
STATUS_ERROR
|
|
||||||
}
|
}
|
@ -1,4 +1,6 @@
|
|||||||
import {WebSocketPayload} from "./WebSocketPayload";
|
import {WebSocketPayload} from "./WebSocketPayload";
|
||||||
|
import {WebSocketEvent} from "./WebSocketEvent";
|
||||||
|
import {iTender} from "./iTender";
|
||||||
|
|
||||||
export class WebSocketHandler {
|
export class WebSocketHandler {
|
||||||
private static _ws: WebSocket;
|
private static _ws: WebSocket;
|
||||||
@ -14,20 +16,21 @@ export class WebSocketHandler {
|
|||||||
public static send(payload: WebSocketPayload): Promise<void> {
|
public static send(payload: WebSocketPayload): Promise<void> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
if( this.ws && this.ws.readyState == 1 )
|
if (this.ws && this.ws.readyState == 1) {
|
||||||
{
|
|
||||||
await this.ws.send(payload.toString());
|
await this.ws.send(payload.toString());
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
reject("Websocket is not connected!");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
reject(e);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static sendStatus() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let payload = new WebSocketPayload(WebSocketEvent.STATUS, false, {status: iTender.status});
|
||||||
|
WebSocketHandler.send(payload).then(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -5,40 +5,40 @@ export class WebSocketPayload {
|
|||||||
this._event = value;
|
this._event = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set status(value: boolean) {
|
set error(value: boolean) {
|
||||||
this._status = value;
|
this._error = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set data(value: object | undefined) {
|
set data(value: any | undefined) {
|
||||||
this._data = value;
|
this._data = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _event: WebSocketEvent;
|
private _event: WebSocketEvent;
|
||||||
private _status: boolean;
|
private _error: boolean;
|
||||||
private _data: object | undefined;
|
private _data: any | undefined;
|
||||||
|
|
||||||
|
|
||||||
get event(): WebSocketEvent {
|
get event(): WebSocketEvent {
|
||||||
return this._event;
|
return this._event;
|
||||||
}
|
}
|
||||||
|
|
||||||
get status(): boolean {
|
get error(): boolean {
|
||||||
return this._status;
|
return this._error;
|
||||||
}
|
}
|
||||||
|
|
||||||
get data(): object | undefined {
|
get data(): any | undefined {
|
||||||
return this._data;
|
return this._data;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(event: WebSocketEvent, status: boolean, data?: object) {
|
constructor(event: WebSocketEvent, error: boolean = false, data?: any) {
|
||||||
this._event = event;
|
this._event = event;
|
||||||
this._status = status;
|
this._error = error;
|
||||||
this._data = data;
|
this._data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static parseFromJSON(json: string): WebSocketPayload | null {
|
public static parseFromBase64Json(json: string): WebSocketPayload | null {
|
||||||
json = (window) ? atob(json) : Buffer.from(json, "base64").toString();
|
json = (typeof window != 'undefined') ? atob(json) : Buffer.from(json, "base64").toString();
|
||||||
let rawPayload: { event: string, status: boolean, data: object };
|
let rawPayload: { event: string, error: boolean, data: any };
|
||||||
try {
|
try {
|
||||||
rawPayload = JSON.parse(json);
|
rawPayload = JSON.parse(json);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -47,15 +47,15 @@ export class WebSocketPayload {
|
|||||||
|
|
||||||
let wsEvent = WebSocketEvent[<keyof typeof WebSocketEvent>rawPayload.event];
|
let wsEvent = WebSocketEvent[<keyof typeof WebSocketEvent>rawPayload.event];
|
||||||
|
|
||||||
return new WebSocketPayload(wsEvent, rawPayload.status, rawPayload.data);
|
return new WebSocketPayload(wsEvent, rawPayload.error, rawPayload.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the payload as base64 encoded json string
|
* Returns the payload as base64 encoded json string
|
||||||
*/
|
*/
|
||||||
public toString(): string {
|
public toString(): string {
|
||||||
let json = JSON.stringify({"event": this._event, status: this._status, data: this._data});
|
let json = JSON.stringify({"event": this._event, status: this._error, data: this._data});
|
||||||
json = (window) ? btoa(json) : Buffer.from(json).toString("base64");
|
json = ((typeof window != 'undefined') ? btoa(json) : Buffer.from(json).toString("base64"));
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -38,7 +38,7 @@ export class WebsocketApp {
|
|||||||
|
|
||||||
public loadRoutes( ) : void
|
public loadRoutes( ) : void
|
||||||
{
|
{
|
||||||
this._app.use( "/", require("./routes/ws/websocket") );
|
this._app.use( "/", require("./routes/ws/websocketRoute") );
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen(): Promise<void> {
|
public listen(): Promise<void> {
|
||||||
|
@ -4,8 +4,7 @@ import * as mongoose from "mongoose";
|
|||||||
export const DrinkSchema = new mongoose.Schema<IDrink>({
|
export const DrinkSchema = new mongoose.Schema<IDrink>({
|
||||||
name: {type: String, required: true},
|
name: {type: String, required: true},
|
||||||
ingredients: [{type: {type: mongoose.Types.ObjectId, ref: "Ingredient", required: true}, amount: { type: Number } }],
|
ingredients: [{type: {type: mongoose.Types.ObjectId, ref: "Ingredient", required: true}, amount: { type: Number } }],
|
||||||
category: String,
|
category: String
|
||||||
recommendedQuantity: {type: Number, default: 200 }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Drink = mongoose.model<IDrink>('Drink', DrinkSchema);
|
const Drink = mongoose.model<IDrink>('Drink', DrinkSchema);
|
||||||
|
@ -12,7 +12,5 @@ export interface IDrink extends mongoose.Document {
|
|||||||
// Category of the drink
|
// Category of the drink
|
||||||
category: Category;
|
category: Category;
|
||||||
|
|
||||||
// Recommended amount in milliliters
|
|
||||||
recommendedQuantity: number;
|
|
||||||
|
|
||||||
}
|
}
|
10
src/database/IJob.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
import {IDrink} from "./IDrink";
|
||||||
|
|
||||||
|
export interface IJob extends mongoose.Document {
|
||||||
|
drink: IDrink;
|
||||||
|
amount: number;
|
||||||
|
startedAt: Date;
|
||||||
|
endAt: Date;
|
||||||
|
successful: boolean;
|
||||||
|
}
|
15
src/database/Job.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import mongoose from "mongoose";
|
||||||
|
import {IJob} from "./IJob";
|
||||||
|
|
||||||
|
|
||||||
|
export const JobSchema = new mongoose.Schema<IJob>({
|
||||||
|
drink: {type: mongoose.Types.ObjectId, ref: "Drink"},
|
||||||
|
amount: Number,
|
||||||
|
startedAt: Date,
|
||||||
|
endAt: Date,
|
||||||
|
successful: Boolean
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const Job = mongoose.model<IJob>('Job', JobSchema);
|
||||||
|
export default Job;
|
125
src/iTender.ts
@ -4,15 +4,35 @@ import {HCSR04} from "hc-sr04";
|
|||||||
import {IContainer} from "./database/IContainer";
|
import {IContainer} from "./database/IContainer";
|
||||||
import Drink from "./database/Drink";
|
import Drink from "./database/Drink";
|
||||||
import {IDrink} from "./database/IDrink";
|
import {IDrink} from "./database/IDrink";
|
||||||
|
import debug from "debug";
|
||||||
|
import {WebSocketHandler} from "./WebSocketHandler";
|
||||||
|
import {IJob} from "./database/IJob";
|
||||||
|
|
||||||
|
const log = debug("itender:station");
|
||||||
|
|
||||||
export class iTender {
|
export class iTender {
|
||||||
private static _status: iTenderStatus;
|
static get containers(): { container: IContainer; sensor: HCSR04; pump: null }[] {
|
||||||
|
return this._containers;
|
||||||
|
}
|
||||||
|
|
||||||
private static containers: { container: IContainer, sensor: HCSR04, pump: null }[];
|
static get drinks(): IDrink[] {
|
||||||
private static drinks: IDrink[];
|
return this._drinks;
|
||||||
|
}
|
||||||
|
|
||||||
static set status(value: iTenderStatus) {
|
static get currentJob(): IJob | null {
|
||||||
this._status = value;
|
return this._currentJob;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _status: iTenderStatus = iTenderStatus.STARTING;
|
||||||
|
private static _currentJob: IJob | null = null;
|
||||||
|
|
||||||
|
private static _containers: { container: IContainer, sensor: HCSR04, pump: null }[] = [];
|
||||||
|
private static _drinks: IDrink[];
|
||||||
|
|
||||||
|
static setStatus(status: iTenderStatus) {
|
||||||
|
this._status = status;
|
||||||
|
if (WebSocketHandler.ws && WebSocketHandler.ws.readyState == 1)
|
||||||
|
WebSocketHandler.sendStatus().then().catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get status(): iTenderStatus {
|
static get status(): iTenderStatus {
|
||||||
@ -21,32 +41,42 @@ export class iTender {
|
|||||||
|
|
||||||
static startFill() {
|
static startFill() {
|
||||||
// todo Fill method
|
// todo Fill method
|
||||||
|
|
||||||
|
|
||||||
|
// End
|
||||||
|
this.measureContainers().then();
|
||||||
}
|
}
|
||||||
|
|
||||||
static stopFill() {
|
static cancelFill() {
|
||||||
// todo Stop fill method
|
// todo Stop fill method
|
||||||
}
|
}
|
||||||
|
|
||||||
static measureContainers(): Promise<void> {
|
static measureContainers(): Promise<void> {
|
||||||
|
log("Measuring containers...");
|
||||||
|
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
let containers = await Container.find();
|
for (let c of this._containers) {
|
||||||
/*
|
try {
|
||||||
public measure() : Number {
|
let dist = c.sensor.distance();
|
||||||
let dist = this.sensor.distance();
|
c.container.filled = dist * 100 / (c.container.sensorFilledMax + c.container.sensorFilledMin);
|
||||||
return dist * 100 / (this.sensorFilledMax + this.sensorFilledMin);
|
} catch (e) {
|
||||||
|
c.container.filled = -1;
|
||||||
}
|
}
|
||||||
*/
|
await c.container.save();
|
||||||
})
|
}
|
||||||
|
log("Containers measured!");
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static refreshDrinks(): Promise<void> {
|
static refreshDrinks(): Promise<void> {
|
||||||
|
this.setStatus(iTenderStatus.REFRESHING);
|
||||||
|
log("Refreshing drinks...");
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
this.drinks = [];
|
this._drinks = [];
|
||||||
for (let d of (await Drink.find().populate("ingredients.type"))) {
|
for (let d of (await Drink.find().populate("ingredients.type"))) {
|
||||||
|
|
||||||
|
|
||||||
let drinkAccept = true;
|
let drinkAccept = true;
|
||||||
|
|
||||||
for (let i of d.ingredients) {
|
for (let i of d.ingredients) {
|
||||||
let c = await Container.findOne({content: i["type"]});
|
let c = await Container.findOne({content: i["type"]});
|
||||||
|
|
||||||
@ -54,26 +84,37 @@ export class iTender {
|
|||||||
drinkAccept = false;
|
drinkAccept = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (drinkAccept) {
|
||||||
|
this._drinks.push(d);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (drinkAccept)
|
log("Drinks refreshed!");
|
||||||
this.drinks.push(d);
|
|
||||||
}
|
resolve();
|
||||||
|
this.setStatus(iTenderStatus.READY);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static refreshContainers(): Promise<void> {
|
static refreshContainers(): Promise<void> {
|
||||||
|
log("Refreshing containers...");
|
||||||
|
this.setStatus(iTenderStatus.CALCULATING);
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
let containers = await Container.find();
|
let containers = await Container.find();
|
||||||
for (let c of containers) {
|
for (let c of containers) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
let found = false;
|
let found = false;
|
||||||
for (let c2 of this.containers) {
|
for (let c2 of this._containers) {
|
||||||
if (c2.container._id == c._id) {
|
if (c2.container._id == c._id) {
|
||||||
this.containers[i] = {
|
let sensor;
|
||||||
|
try {
|
||||||
|
sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
this._containers[i] = {
|
||||||
container: c,
|
container: c,
|
||||||
sensor: new HCSR04(c.sensorTrigger, c.sensorEcho),
|
sensor: sensor,
|
||||||
pump: null
|
pump: null
|
||||||
};
|
};
|
||||||
found = true;
|
found = true;
|
||||||
@ -81,10 +122,42 @@ export class iTender {
|
|||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
if (!found)
|
if (!found) {
|
||||||
this.containers.push({container: c, sensor: new HCSR04(c.sensorTrigger, c.sensorEcho), pump: null});
|
let sensor;
|
||||||
|
try {
|
||||||
|
sensor = new HCSR04(c.sensorTrigger, c.sensorEcho);
|
||||||
|
} catch (e) {
|
||||||
}
|
}
|
||||||
})
|
this._containers.push({
|
||||||
|
container: c,
|
||||||
|
sensor: sensor,
|
||||||
|
pump: null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
log("Containers refreshed!");
|
||||||
|
this.measureContainers().then().catch(console.error);
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
this.setStatus(iTenderStatus.READY);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async autoCheckup() {
|
||||||
|
setInterval(async () => {
|
||||||
|
if (!this._currentJob)
|
||||||
|
return;
|
||||||
|
// Check if startedTime plus 2 mins smaller than now
|
||||||
|
if (this._currentJob.startedAt.getTime() + 1000 * 60 * 2 <= Date.now()) {
|
||||||
|
// Job can be declared as stuck!
|
||||||
|
this._currentJob.successful = false;
|
||||||
|
this._currentJob.endAt = new Date();
|
||||||
|
await this._currentJob.save();
|
||||||
|
this._currentJob = null;
|
||||||
|
}
|
||||||
|
}, 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
export enum iTenderStatus {
|
export enum iTenderStatus {
|
||||||
// Machine is going to start
|
// Machine is going to start
|
||||||
STARTING,
|
STARTING= "STARTING",
|
||||||
// Machine is ready
|
// Machine is ready
|
||||||
READY,
|
READY = "READY",
|
||||||
// Machine is filling your drink and destroying your leberwurst
|
// Machine is filling your drink and destroying your leberwurst
|
||||||
FILLING,
|
FILLING = "FILLING",
|
||||||
// Drinks will be refreshed from global database (the internet neuland :O)
|
// Drinks will be refreshed from global database (the internet neuland :O)
|
||||||
REFRESHING,
|
REFRESHING = "REFRESHING",
|
||||||
// Drinks will be calculated (check containers and which drinks can be done)
|
// Drinks will be calculated (check containers and which drinks can be done)
|
||||||
CALCULATING,
|
CALCULATING = "CALCULATING",
|
||||||
// An error happened; Oh no :(
|
// An error happened; Oh no :(
|
||||||
ERROR
|
ERROR = "ERROR"
|
||||||
}
|
}
|
80
src/main.ts
@ -2,10 +2,13 @@ import {App} from "./App";
|
|||||||
import debug from "debug";
|
import debug from "debug";
|
||||||
import {WebsocketApp} from "./WebsocketApp";
|
import {WebsocketApp} from "./WebsocketApp";
|
||||||
import {Database} from "./database/Database";
|
import {Database} from "./database/Database";
|
||||||
import Drink from "./database/Drink";
|
|
||||||
import Ingredient from "./database/Ingredient";
|
import Ingredient from "./database/Ingredient";
|
||||||
import Container from "./database/Container";
|
|
||||||
import {iTender} from "./iTender";
|
import {iTender} from "./iTender";
|
||||||
|
import {iTenderStatus} from "./iTenderStatus";
|
||||||
|
import {Utils} from "./Utils";
|
||||||
|
import {Category} from "./Category";
|
||||||
|
import Drink from "./database/Drink";
|
||||||
|
import Container from "./database/Container";
|
||||||
|
|
||||||
const log = debug("itender:server");
|
const log = debug("itender:server");
|
||||||
|
|
||||||
@ -17,9 +20,18 @@ const wsApp = new WebsocketApp();
|
|||||||
try {
|
try {
|
||||||
log("Starting...");
|
log("Starting...");
|
||||||
await Database.connect();
|
await Database.connect();
|
||||||
|
//await test();
|
||||||
await app.listen();
|
await app.listen();
|
||||||
await wsApp.listen();
|
await wsApp.listen();
|
||||||
|
|
||||||
|
iTender.setStatus(iTenderStatus.STARTING);
|
||||||
|
await Utils.sleep(5000);
|
||||||
|
|
||||||
await init();
|
await init();
|
||||||
|
|
||||||
|
setInterval(refresh, 1000 * 60 * 10);
|
||||||
|
|
||||||
|
iTender.setStatus(iTenderStatus.READY);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("---- ERROR ----")
|
console.error("---- ERROR ----")
|
||||||
console.error(e);
|
console.error(e);
|
||||||
@ -27,19 +39,61 @@ const wsApp = new WebsocketApp();
|
|||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function init() : Promise<void> {
|
function init(): Promise<void> {
|
||||||
log("Initializing...");
|
log("Initializing...");
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
await iTender.refreshContainers();
|
await iTender.refreshContainers();
|
||||||
await iTender.refreshDrinks();
|
await iTender.refreshDrinks();
|
||||||
|
await iTender.autoCheckup();
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function refresh(): Promise<void> {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
// If there is a current job, DO NOT REFRESH!
|
||||||
|
if (iTender.currentJob)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//await iTender.refreshContainers(); Not needed because there is no change in containers?
|
||||||
|
await iTender.measureContainers();
|
||||||
|
//await iTender.refreshDrinks(); Not needed because there is no change in drinks?
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async function test() {
|
async function test() {
|
||||||
console.log("Testing fn");
|
console.log("Testing fn");
|
||||||
|
|
||||||
let ingredient = await Ingredient.findOne({name: "Cola"});
|
/*
|
||||||
|
let cola = new Ingredient();
|
||||||
|
cola.name = "Cola";
|
||||||
|
cola.category = Category.SOFTDRINK;
|
||||||
|
await cola.save();
|
||||||
|
|
||||||
|
let sprite = new Ingredient();
|
||||||
|
sprite.name = "Sprite";
|
||||||
|
sprite.category = Category.SOFTDRINK;
|
||||||
|
await sprite.save();
|
||||||
|
|
||||||
|
let fanta = new Ingredient();
|
||||||
|
fanta.name = "Fanta";
|
||||||
|
fanta.category = Category.SOFTDRINK;
|
||||||
|
await fanta.save();
|
||||||
|
|
||||||
|
let drink = new Drink();
|
||||||
|
drink.name = "Fanta";
|
||||||
|
drink.category = Category.ALCOHOL_FREE;
|
||||||
|
drink.ingredients = [{type: fanta, amount: 200}];
|
||||||
|
await drink.save();
|
||||||
|
|
||||||
|
drink = new Drink();
|
||||||
|
drink.name = "Mezzo Mix";
|
||||||
|
drink.category = Category.ALCOHOL_FREE;
|
||||||
|
drink.ingredients = [{type: fanta, amount: 100}, {type: cola, amount: 100}];
|
||||||
|
await drink.save();*/
|
||||||
|
|
||||||
|
let ingredient = await Ingredient.findOne({name: "Fanta"});
|
||||||
if (!ingredient)
|
if (!ingredient)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -49,26 +103,26 @@ async function test() {
|
|||||||
|
|
||||||
await drink.save();*/
|
await drink.save();*/
|
||||||
|
|
||||||
let drink = await Drink.findOne({name: "Cola"}).populate("ingredients.type");
|
/* let drink = await Drink.findOne({name: "Cola"}).populate("ingredients.type");
|
||||||
if (!drink) return;
|
if (!drink) return;
|
||||||
|
|
||||||
console.log(drink);
|
console.log(drink);*/
|
||||||
|
|
||||||
/*let container = new Container();
|
let container = new Container();
|
||||||
container.slot = 1;
|
container.slot = 2;
|
||||||
container.volume = 750;
|
container.volume = 750;
|
||||||
container.sensorEcho = 26;
|
container.sensorEcho = 28;
|
||||||
container.sensorTrigger = 27;
|
container.sensorTrigger = 29;
|
||||||
container.content = ingredient;
|
container.content = ingredient;
|
||||||
container.sensorFilledMax = 2;
|
container.sensorFilledMax = 2;
|
||||||
container.sensorFilledMin = 15;
|
container.sensorFilledMin = 15;
|
||||||
|
|
||||||
await container.save();*/
|
await container.save();
|
||||||
|
|
||||||
let container = await Container.findOne({slot: 1});
|
/* let container = await Container.findOne({slot: 1});
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
|
|
||||||
console.log(container);
|
console.log(container);*/
|
||||||
|
|
||||||
|
|
||||||
//console.log(drink.ingredients)
|
//console.log(drink.ingredients)
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
import {WebSocketPayload} from "../../WebSocketPayload";
|
|
||||||
import debug from "debug";
|
|
||||||
import {WebSocketHandler} from "../../WebSocketHandler";
|
|
||||||
|
|
||||||
const express = require('express');
|
|
||||||
const router = express.Router();
|
|
||||||
|
|
||||||
|
|
||||||
const log = debug("itender:websocket");
|
|
||||||
|
|
||||||
router.ws('/', (ws, req, next) => {
|
|
||||||
log("Incoming websocket connection")
|
|
||||||
ws.on('message', async (raw, bool) => {
|
|
||||||
let msg = WebSocketPayload.parseFromJSON(raw);
|
|
||||||
// If message is null, close the socket because it could not be decompiled
|
|
||||||
if (!msg) {
|
|
||||||
ws.close(1011);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (msg.event) {
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.on('open', (listener) => {
|
|
||||||
if (WebSocketHandler.ws)
|
|
||||||
WebSocketHandler.ws.close(1001);
|
|
||||||
WebSocketHandler.ws = ws;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = router;
|
|
59
src/routes/ws/websocketRoute.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import {WebSocketPayload} from "../../WebSocketPayload";
|
||||||
|
import debug from "debug";
|
||||||
|
import {WebSocketHandler} from "../../WebSocketHandler";
|
||||||
|
import {iTender} from "../../iTender";
|
||||||
|
import {iTenderStatus} from "../../iTenderStatus";
|
||||||
|
import {WebSocketEvent} from "../../WebSocketEvent";
|
||||||
|
|
||||||
|
const express = require('express');
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
|
||||||
|
const log = debug("itender:websocket");
|
||||||
|
|
||||||
|
router.ws('/', async (ws, req, next) => {
|
||||||
|
log("Incoming websocket connection...");
|
||||||
|
|
||||||
|
if (WebSocketHandler.ws)
|
||||||
|
WebSocketHandler.ws.close(1001);
|
||||||
|
WebSocketHandler.ws = ws;
|
||||||
|
|
||||||
|
|
||||||
|
await WebSocketHandler.sendStatus();
|
||||||
|
|
||||||
|
async function sendWhenReady() {
|
||||||
|
if (iTender.status != iTenderStatus.READY) {
|
||||||
|
setTimeout(sendWhenReady, 100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
iTender.setStatus(iTenderStatus.REFRESHING);
|
||||||
|
|
||||||
|
let payload = new WebSocketPayload(WebSocketEvent.DRINKS, false, iTender.drinks);
|
||||||
|
await WebSocketHandler.send(payload);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (iTender.status == iTenderStatus.REFRESHING)
|
||||||
|
iTender.setStatus(iTenderStatus.READY);
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sendWhenReady().then();
|
||||||
|
|
||||||
|
ws.on('message', async (raw, bool) => {
|
||||||
|
let msg = WebSocketPayload.parseFromBase64Json(raw);
|
||||||
|
// If message is null, close the socket because it could not be decompiled
|
||||||
|
if (!msg) {
|
||||||
|
ws.close(1011);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (msg.event) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = router;
|
0
src/test.ts
Normal file
@ -2,14 +2,19 @@ import {ButtonType} from "./ButtonType";
|
|||||||
|
|
||||||
export class Modal {
|
export class Modal {
|
||||||
|
|
||||||
private static currentModalId = "";
|
private static currentModalId: string | undefined = "";
|
||||||
|
|
||||||
private _title: string = "iTender";
|
private _title: string = "iTender";
|
||||||
private _content: string | undefined = "";
|
private _content: string | undefined = "";
|
||||||
private _id: string = "";
|
private _id: string = "";
|
||||||
private _loader: boolean = false;
|
private _loader: boolean = false;
|
||||||
private _buttons: { type: string, content: string, onclick: Function }[] = [];
|
private _buttons: { type: string, content: string, onclick: Function }[] = [];
|
||||||
|
private _leftCentered: boolean = false;
|
||||||
|
set leftCentered(value: boolean) {
|
||||||
|
this._leftCentered = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static modalInClose = false;
|
||||||
|
|
||||||
constructor(id, title: string, content?: string) {
|
constructor(id, title: string, content?: string) {
|
||||||
this._id = id;
|
this._id = id;
|
||||||
@ -17,6 +22,10 @@ export class Modal {
|
|||||||
this._content = content;
|
this._content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static isModalOpen(): boolean {
|
||||||
|
return !(!this.currentModalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
set title(value: string) {
|
set title(value: string) {
|
||||||
this._title = value;
|
this._title = value;
|
||||||
@ -34,8 +43,7 @@ export class Modal {
|
|||||||
this._loader = value;
|
this._loader = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addButton( type: ButtonType, content: string, onclick: Function )
|
public addButton(type: ButtonType, content: string, onclick: Function) {
|
||||||
{
|
|
||||||
this._buttons.push({type: type, content: content, onclick: onclick});
|
this._buttons.push({type: type, content: content, onclick: onclick});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,16 +51,39 @@ export class Modal {
|
|||||||
if (!this._content)
|
if (!this._content)
|
||||||
this._content = "";
|
this._content = "";
|
||||||
|
|
||||||
|
if( this._leftCentered )
|
||||||
|
{
|
||||||
|
this._content = "<div style='text-align: left; padding-left: 2%;'>" + this._content;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._loader)
|
if (this._loader)
|
||||||
this._content += "<br><div class=\"lds-ellipsis\">\n" +
|
this._content += "<br><div class=\"lds-ellipsis\">\n" +
|
||||||
" <div></div><div></div><div></div><div></div>\n" +
|
" <div></div><div></div><div></div><div></div>\n" +
|
||||||
"</div>";
|
"</div>";
|
||||||
|
|
||||||
for (let btn of this._buttons) {
|
for (let btn of this._buttons) {
|
||||||
this._content += `<button class="btn btn-${btn.type}" onclick="${btn.onclick}">${btn.content}</button>`;
|
this._content += `<button class="btn btn-${btn.type}" onclick="(${btn.onclick})();">${btn.content}</button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Modal.open(this._title, this._content, this._id);
|
if( this._leftCentered )
|
||||||
|
{
|
||||||
|
this._content+= "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
let title = this._title;
|
||||||
|
let content = this._content;
|
||||||
|
let id = this._id;
|
||||||
|
function tryOpen()
|
||||||
|
{
|
||||||
|
if( Modal.modalInClose )
|
||||||
|
{
|
||||||
|
setTimeout( tryOpen, 50 );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Modal.open(title, content, id );
|
||||||
|
}
|
||||||
|
|
||||||
|
tryOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,6 +92,7 @@ export class Modal {
|
|||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
public static open(title: string, content: string, id?: string): void {
|
public static open(title: string, content: string, id?: string): void {
|
||||||
|
|
||||||
const modal = document.getElementById("modal");
|
const modal = document.getElementById("modal");
|
||||||
const modalContent = document.getElementById("modalInnerContent");
|
const modalContent = document.getElementById("modalInnerContent");
|
||||||
|
|
||||||
@ -82,9 +114,11 @@ export class Modal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static close(id?: string): void {
|
public static close(id?: string): void {
|
||||||
if (this.currentModalId != id)
|
if (id && this.currentModalId != id)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Modal.modalInClose = true;
|
||||||
|
|
||||||
const modal = document.getElementById("modal");
|
const modal = document.getElementById("modal");
|
||||||
const modalContent = document.getElementById("modal-content");
|
const modalContent = document.getElementById("modal-content");
|
||||||
const modalInnerContent = document.getElementById("modalInnerContent");
|
const modalInnerContent = document.getElementById("modalInnerContent");
|
||||||
@ -100,6 +134,9 @@ export class Modal {
|
|||||||
modalInnerContent.innerHTML = "";
|
modalInnerContent.innerHTML = "";
|
||||||
modalContent.classList.remove("modalBlendOut");
|
modalContent.classList.remove("modalBlendOut");
|
||||||
modal.classList.remove("modalBlendOut");
|
modal.classList.remove("modalBlendOut");
|
||||||
|
this.modalInClose = false;
|
||||||
}, 800);
|
}, 800);
|
||||||
|
|
||||||
|
this.currentModalId = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
54
src/web/WebHandler.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {WebSocketPayload} from "../WebSocketPayload";
|
||||||
|
import {IDrink} from "../database/IDrink";
|
||||||
|
import {Modal} from "./Modal";
|
||||||
|
|
||||||
|
export class WebHandler {
|
||||||
|
public static onDrinkUpdate(payload: WebSocketPayload) {
|
||||||
|
if (!payload.data) return;
|
||||||
|
|
||||||
|
let drinks: IDrink[] = payload.data;
|
||||||
|
|
||||||
|
const main = document.getElementById("main");
|
||||||
|
if (!main) return;
|
||||||
|
|
||||||
|
main.style.gridTemplateRows = `repeat(${Math.round(drinks.length / 3)}, calc(90%/2))`;
|
||||||
|
|
||||||
|
for (let drink of drinks) {
|
||||||
|
let drinkEle = document.createElement("div");
|
||||||
|
drinkEle.classList.add("drink");
|
||||||
|
|
||||||
|
|
||||||
|
let drinkImg = document.createElement("img");
|
||||||
|
drinkImg.classList.add("thumbnail");
|
||||||
|
drinkEle.append(drinkImg);
|
||||||
|
|
||||||
|
let drinkName = document.createElement("p");
|
||||||
|
drinkName.classList.add("drinkName");
|
||||||
|
drinkEle.append(drinkName);
|
||||||
|
|
||||||
|
drinkImg.alt = "Foto von " + drink.name;
|
||||||
|
drinkImg.src = "/images/" + drink.name + ".png";
|
||||||
|
drinkName.innerText = drink.name;
|
||||||
|
|
||||||
|
let ingredients = "<ul style='list-style-type: disc;'>";
|
||||||
|
for( let i of drink.ingredients )
|
||||||
|
{
|
||||||
|
ingredients += "<li>" + i.amount + "ml " + i.type.name + "</li>";
|
||||||
|
}
|
||||||
|
ingredients+="</ul>"
|
||||||
|
|
||||||
|
drinkEle.onclick = () => {
|
||||||
|
let modal = new Modal("drink", drink.name );
|
||||||
|
modal.content = `<strong>Zutaten</strong><br>
|
||||||
|
${ingredients}`
|
||||||
|
modal.leftCentered = true;
|
||||||
|
modal.open();
|
||||||
|
};
|
||||||
|
|
||||||
|
main.append(drinkEle);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,28 +1,117 @@
|
|||||||
import {Modal} from "./Modal";
|
import {Modal} from "./Modal";
|
||||||
|
import {WebSocketEvent} from "../WebSocketEvent";
|
||||||
|
import {WebSocketPayload} from "../WebSocketPayload";
|
||||||
|
import {ButtonType} from "./ButtonType";
|
||||||
|
import {iTenderStatus} from "../iTenderStatus";
|
||||||
|
import {WebHandler} from "./WebHandler";
|
||||||
|
|
||||||
export class WebWebSocketHandler {
|
export class WebWebSocketHandler {
|
||||||
private socket : WebSocket;
|
private socket: WebSocket;
|
||||||
|
private static url = (window.location.protocol == "http:" ? "ws://" : "wss://") + window.location.hostname + ":3005";
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.socket = new WebSocket((window.location.protocol == "http:" ? "ws://" : "wss://") + window.location.hostname + ":3005");
|
this.socket = new WebSocket(WebWebSocketHandler.url);
|
||||||
this.socket.onopen = this.onOpen;
|
this.socket.onopen = this.onOpen;
|
||||||
this.socket.onclose = this.onClose;
|
this.socket.onclose = this.onClose;
|
||||||
this.socket.onerror = this.onError;
|
this.socket.onerror = this.onError;
|
||||||
|
this.socket.onmessage = this.onMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onOpen( event )
|
private onMessage(msgEvent: MessageEvent) {
|
||||||
{
|
console.log("[WS] Incoming message", msgEvent);
|
||||||
|
let payload = WebSocketPayload.parseFromBase64Json(msgEvent.data);
|
||||||
|
if (!payload) {
|
||||||
|
console.log("[WS] Could not parse message: ", msgEvent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.debug(payload)
|
||||||
|
|
||||||
|
console.debug(payload.event);
|
||||||
|
|
||||||
|
console.log("[WS] Received " + payload.event + " Event");
|
||||||
|
|
||||||
|
switch (payload.event) {
|
||||||
|
case WebSocketEvent.STATUS: {
|
||||||
|
let statusElement = document.getElementById("status");
|
||||||
|
if (statusElement)
|
||||||
|
statusElement.innerText = payload.data.status;
|
||||||
|
|
||||||
|
let status: iTenderStatus = payload.data.status;
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case iTenderStatus.READY: {
|
||||||
Modal.close("start");
|
Modal.close("start");
|
||||||
|
Modal.close("refreshing");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case iTenderStatus.STARTING: {
|
||||||
|
let modal = new Modal("start", "Willkommen!", `Einen Augenblick bitte<br>iTender startet...`);
|
||||||
|
modal.loader = true;
|
||||||
|
modal.open();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case iTenderStatus.REFRESHING: {
|
||||||
|
let modal = new Modal("refreshing", "Aktualisieren...", `Einen Augenblick bitte<br>iTender aktualisiert die Getränke...`);
|
||||||
|
modal.loader = true;
|
||||||
|
modal.open();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
private onClose( event ) {
|
case WebSocketEvent.DRINKS: {
|
||||||
console.error("WS Closed!", event );
|
WebHandler.onDrinkUpdate(payload);
|
||||||
//openModal("Einen Augenblick...", `Es wurde ein Verbindungsfehler erkannt.\nBitte warten Sie, während der Prozess neu gestartet wird...` );
|
break;
|
||||||
//window.location.reload();
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onError( event ) {
|
private onOpen(event) {
|
||||||
console.error("WS Error", event);
|
console.log("[WS] Connected", event);
|
||||||
|
|
||||||
|
let connectionElement = document.getElementById("right");
|
||||||
|
if (connectionElement)
|
||||||
|
{
|
||||||
|
connectionElement.innerText = "Verbunden";
|
||||||
|
connectionElement.style.color = "green";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private onClose(event) {
|
||||||
|
console.error("[WS] Closed!", event);
|
||||||
|
if (event.wasClean) {
|
||||||
|
let modal = new Modal("socketClosed", "Sitzung beendet!");
|
||||||
|
modal.content = `Diese Sitzung wurde beendet, da der iTender nun an einem anderen Gerät bzw. an dem Hauptgerät gesteuert wird.<br><br>`;
|
||||||
|
modal.addButton(ButtonType.SUCCESS, "Sitzung wiederherstellen", () => {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
modal.open();
|
||||||
|
} else {
|
||||||
|
let modal = new Modal("socketClosed", "Verbindungsproblem!");
|
||||||
|
modal.content = `Die Benutzeroberfläche hat die Verbindung mit dem Gerät verloren.<br>Die Verbindung wird wiederhergestellt...<br>`;
|
||||||
|
modal.loader = true;
|
||||||
|
modal.open();
|
||||||
|
setInterval(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
let connectionElement = document.getElementById("right");
|
||||||
|
if (connectionElement)
|
||||||
|
{
|
||||||
|
connectionElement.innerText = "Getrennt";
|
||||||
|
connectionElement.style.color = "red";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onError(event) {
|
||||||
|
console.error("[WS] Error", event);
|
||||||
|
let connectionElement = document.getElementById("right");
|
||||||
|
if (connectionElement)
|
||||||
|
connectionElement.innerText = "Fehler";
|
||||||
//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();
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,13 @@ document.addEventListener("DOMContentLoaded", () => {
|
|||||||
let modal = new Modal("start", "iTender");
|
let modal = new Modal("start", "iTender");
|
||||||
modal.content = "Willkommen";
|
modal.content = "Willkommen";
|
||||||
modal.loader = true;
|
modal.loader = true;
|
||||||
modal.open();
|
//modal.open();
|
||||||
connect();
|
connect();
|
||||||
|
|
||||||
setTimeout( load, 1000);
|
setTimeout( load, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
function load()
|
function load()
|
||||||
{
|
{
|
||||||
if(!main||!time)
|
if(!main||!time)
|
||||||
@ -25,35 +26,10 @@ function load()
|
|||||||
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 maxI = 20;
|
|
||||||
|
|
||||||
main.style.gridTemplateRows = `repeat(${Math.round(maxI/3)}, calc(90%/2))`;
|
|
||||||
|
|
||||||
|
|
||||||
for( let i = 0; i<maxI; i++ )
|
|
||||||
{
|
|
||||||
let testDrink = document.createElement("div");
|
|
||||||
testDrink.classList.add("drink");
|
|
||||||
|
|
||||||
let img = document.createElement("img");
|
|
||||||
img.classList.add("thumbnail");
|
|
||||||
testDrink.append(img);
|
|
||||||
|
|
||||||
let name = document.createElement("p");
|
|
||||||
name.classList.add("name");
|
|
||||||
testDrink.append(name);
|
|
||||||
|
|
||||||
img.alt="Thumbnail";
|
|
||||||
name.innerText = "Mixery"
|
|
||||||
|
|
||||||
main.append(testDrink);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let wsHandler;
|
let wsHandler;
|
||||||
function connect()
|
function connect()
|
||||||
{
|
{
|
||||||
wsHandler = new WebWebSocketHandler();
|
wsHandler = new WebWebSocketHandler();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
28
staticWeb/index.html
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><title>iTender</title>
|
||||||
|
<link rel="stylesheet" href="../public/stylesheets/reset.css">
|
||||||
|
<link rel="stylesheet" href="../public/stylesheets/style.css">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="modal" id="modal">
|
||||||
|
<div class="modal-content" id="modal-content">
|
||||||
|
<div class="modalInnerContent" id="modalInnerContent"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="overlay">
|
||||||
|
<div id="top"><span id="left"><strong>Status:</strong> <span id="status"></span></span><span
|
||||||
|
id="title">iTender</span><span id="right">Verbinden...</span></div>
|
||||||
|
<div id="bottom">
|
||||||
|
<button id="menuBtn">Menü</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="container">
|
||||||
|
<div id="menu"></div>
|
||||||
|
<div id="settings"></div>
|
||||||
|
<div id="main"></div>
|
||||||
|
</div>
|
||||||
|
<script src="./web.bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
346
staticWeb/web.bundle.js
Normal file
@ -4,6 +4,7 @@ html
|
|||||||
title iTender
|
title iTender
|
||||||
link(rel='stylesheet', href='/stylesheets/reset.css')
|
link(rel='stylesheet', href='/stylesheets/reset.css')
|
||||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||||
|
meta(charset="UTF-8")
|
||||||
body
|
body
|
||||||
div.modal#modal
|
div.modal#modal
|
||||||
div.modal-content#modal-content
|
div.modal-content#modal-content
|
||||||
@ -11,7 +12,9 @@ html
|
|||||||
|
|
||||||
div#overlay
|
div#overlay
|
||||||
div#top
|
div#top
|
||||||
|
span#left <strong>Status:</strong> <span id="status"></span>
|
||||||
span#title iTender
|
span#title iTender
|
||||||
|
span#right Verbinden...
|
||||||
div#bottom
|
div#bottom
|
||||||
button#menuBtn Menü
|
button#menuBtn Menü
|
||||||
div#container
|
div#container
|
||||||
@ -25,7 +28,7 @@ html
|
|||||||
block extra
|
block extra
|
||||||
script(src="/web.js")
|
script(src="/web.js")
|
||||||
script.
|
script.
|
||||||
setTimeout( () =>
|
// setTimeout( () =>
|
||||||
{
|
// {
|
||||||
window.location.reload();
|
// window.location.reload();
|
||||||
}, 120000 );
|
// }, 120000 );
|
||||||
|
@ -11,7 +11,7 @@ module.exports = {
|
|||||||
{
|
{
|
||||||
test: /\.tsx?$/,
|
test: /\.tsx?$/,
|
||||||
use: "ts-loader",
|
use: "ts-loader",
|
||||||
exclude: /node_modules/,
|
//exclude: /node_modules/,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|