This commit is contained in:
Tobias Hopp 2022-11-14 15:41:11 +01:00
parent 1380575845
commit a7fac21442
17 changed files with 236 additions and 63 deletions

View File

@ -19,6 +19,7 @@
"@types/mongoose": "^5.11.97", "@types/mongoose": "^5.11.97",
"@types/morgan": "^1.9.3", "@types/morgan": "^1.9.3",
"@types/node": "^18.11.9", "@types/node": "^18.11.9",
"@types/rpi-gpio": "^2.1.1",
"cookie-parser": "^1.4.6", "cookie-parser": "^1.4.6",
"debug": "^4.3.4", "debug": "^4.3.4",
"express": "~4.16.1", "express": "~4.16.1",
@ -27,7 +28,8 @@
"http-errors": "~1.6.3", "http-errors": "~1.6.3",
"mongoose": "^6.7.2", "mongoose": "^6.7.2",
"morgan": "^1.10.0", "morgan": "^1.10.0",
"pug": "2.0.0-beta11" "pug": "2.0.0-beta11",
"rpi-gpio": "^2.1.7"
}, },
"devDependencies": { "devDependencies": {
"@types/electron": "^1.6.10", "@types/electron": "^1.6.10",

View File

@ -1,11 +1,10 @@
.btn { .btn {
padding: 11px 15px; padding: 11px 15px;
border: none; border: none;
cursor: pointer;
color: white; color: white;
font-size: 0.8em; font-size: 0.8em;
border-radius: 2px; border-radius: 2px;
cursor:none !important;
} }

View File

@ -3,11 +3,26 @@
@import url("/stylesheets/buttons.css"); @import url("/stylesheets/buttons.css");
@import url("/stylesheets/modal.css"); @import url("/stylesheets/modal.css");
:root {
cursor: none;
}
html * {cursor: none !important}
body::-webkit-scrollbar {
display: none;
}
body { body {
color: white; color: white;
scroll-behavior: smooth; scroll-behavior: smooth;
font-family: "Roboto", serif; font-family: "Roboto", serif;
font-style: normal; font-style: normal;
cursor: none;
} }
@ -118,10 +133,13 @@ body {
border-radius: 30px 10px 30px; border-radius: 30px 10px 30px;
} }
.drink .thumbnail { .drink .thumbnail {
grid-column: span 1; grid-column: span 1;
grid-row: span 2; grid-row: span 2;
} }
.drink .name { .drink .name {
grid-column: span 1; grid-column: span 1;
grid-row: span 1; grid-row: span 1;

33
src/WebSocketHandler.ts Normal file
View File

@ -0,0 +1,33 @@
import {WebSocketPayload} from "./WebSocketPayload";
export class WebSocketHandler {
private static _ws: WebSocket;
static get ws(): WebSocket {
return this._ws;
}
static set ws(value: WebSocket) {
this._ws = value;
}
public static send(payload: WebSocketPayload): Promise<void> {
return new Promise(async (resolve, reject) => {
try {
if( this.ws && this.ws.readyState == 1 )
{
await this.ws.send(payload.toString());
resolve();
}
else
{
reject("Websocket is not connected!");
}
} catch (e) {
reject(e);
}
});
}
}

View File

@ -45,8 +45,17 @@ export class WebSocketPayload {
return null; return null;
} }
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.status, rawPayload.data);
} }
/**
* Returns the payload as base64 encoded json string
*/
public toString(): string {
let json = JSON.stringify({"event": this._event, status: this._status, data: this._data});
json = (window) ? btoa(json) : Buffer.from(json).toString("base64");
return json;
}
} }

View File

@ -1,26 +0,0 @@
import {IContainer} from "./IContainer";
import {IIngredient} from "./IIngredient";
import {HCSR04} from "hc-sr04";
export abstract class AbstractContainer implements IContainer {
slot: number = 1;
content: IIngredient | undefined;
sensorEcho: number = 26;
sensorFilledMax: number = 0;
sensorFilledMin: number = 0;
sensorTrigger: number = 27;
volume: number = 1000;
sensor: HCSR04;
protected constructor() {
this.sensor = new HCSR04(this.sensorTrigger, this.sensorEcho);
}
public measure() : Number {
let dist = this.sensor.distance();
return dist * 100 / (this.sensorFilledMax + this.sensorFilledMin);
}
}

View File

@ -1,17 +1,18 @@
import * as Mongoose from "mongoose"; import * as Mongoose from "mongoose";
import {AbstractContainer} from "./AbstractContainer";
import mongoose from "mongoose"; import mongoose from "mongoose";
import {IContainer} from "./IContainer";
export const ContainerSchema = new Mongoose.Schema<AbstractContainer>({ export const ContainerSchema = new Mongoose.Schema<IContainer>({
slot: {type: Number}, slot: {type: Number},
volume: {type: Number, required: true, default: 1000}, volume: {type: Number, required: true, default: 1000},
sensorEcho: Number, sensorEcho: Number,
sensorTrigger: Number, sensorTrigger: Number,
content: {type: String}, content: {type: mongoose.Types.ObjectId},
sensorFilledMax: Number, sensorFilledMax: Number,
sensorFilledMin: Number sensorFilledMin: Number,
filled: Number,
}); });
const Container = mongoose.model<AbstractContainer>('Container', ContainerSchema); const Container = mongoose.model<IContainer>('Container', ContainerSchema);
export default Container; export default Container;

View File

@ -4,7 +4,8 @@ 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);

View File

@ -1,7 +1,7 @@
import {IIngredient} from "./IIngredient"; import {IIngredient} from "./IIngredient";
import * as mongoose from "mongoose"; import * as mongoose from "mongoose";
export interface IContainer { export interface IContainer extends mongoose.Document{
slot: number; slot: number;
content: IIngredient|undefined; content: IIngredient|undefined;
volume: number; volume: number;
@ -9,4 +9,5 @@ export interface IContainer {
sensorFilledMax: number; sensorFilledMax: number;
sensorTrigger: number; sensorTrigger: number;
sensorEcho: number; sensorEcho: number;
filled: Number;
} }

View File

@ -12,4 +12,7 @@ export interface IDrink extends mongoose.Document {
// Category of the drink // Category of the drink
category: Category; category: Category;
// Recommended amount in milliliters
recommendedQuantity: number;
} }

View File

@ -1,10 +1,15 @@
import {iTenderStatus} from "./iTenderStatus"; import {iTenderStatus} from "./iTenderStatus";
import {AbstractContainer} from "./database/AbstractContainer"; import Container from "./database/Container";
import {HCSR04} from "hc-sr04";
import {IContainer} from "./database/IContainer";
import Drink from "./database/Drink";
import {IDrink} from "./database/IDrink";
export class iTender { export class iTender {
private static _status: iTenderStatus; private static _status: iTenderStatus;
private static containers: AbstractContainer[]; private static containers: { container: IContainer, sensor: HCSR04, pump: null }[];
private static drinks: IDrink[];
static set status(value: iTenderStatus) { static set status(value: iTenderStatus) {
this._status = value; this._status = value;
@ -22,15 +27,65 @@ export class iTender {
// todo Stop fill method // todo Stop fill method
} }
static measureContainers() { static measureContainers(): Promise<void> {
for( let container of this.containers ) return new Promise(async resolve => {
{ let containers = await Container.find();
container.measure(); /*
} public measure() : Number {
let dist = this.sensor.distance();
return dist * 100 / (this.sensorFilledMax + this.sensorFilledMin);
}
*/
})
} }
static loadContainers() { static refreshDrinks(): Promise<void> {
return new Promise(async resolve => {
this.drinks = [];
for (let d of (await Drink.find().populate("ingredients.type"))) {
let drinkAccept = true;
for (let i of d.ingredients) {
let c = await Container.findOne({content: i["type"]});
if (!c) {
drinkAccept = false;
break;
}
}
if (drinkAccept)
this.drinks.push(d);
}
});
} }
static refreshContainers(): Promise<void> {
return new Promise(async resolve => {
let containers = await Container.find();
for (let c of containers) {
let i = 0;
let found = false;
for (let c2 of this.containers) {
if (c2.container._id == c._id) {
this.containers[i] = {
container: c,
sensor: new HCSR04(c.sensorTrigger, c.sensorEcho),
pump: null
};
found = true;
break;
}
i++;
}
if (!found)
this.containers.push({container: c, sensor: new HCSR04(c.sensorTrigger, c.sensorEcho), pump: null});
}
})
}
} }

View File

@ -1,7 +1,14 @@
export enum iTenderStatus { export enum iTenderStatus {
// Machine is going to start
STARTING, STARTING,
// Machine is ready
READY, READY,
// Machine is filling your drink and destroying your leberwurst
FILLING, FILLING,
// Drinks will be refreshed from global database (the internet neuland :O)
REFRESHING, REFRESHING,
// Drinks will be calculated (check containers and which drinks can be done)
CALCULATING,
// An error happened; Oh no :(
ERROR ERROR
} }

View File

@ -4,7 +4,8 @@ import {WebsocketApp} from "./WebsocketApp";
import {Database} from "./database/Database"; import {Database} from "./database/Database";
import Drink from "./database/Drink"; import Drink from "./database/Drink";
import Ingredient from "./database/Ingredient"; import Ingredient from "./database/Ingredient";
import {Category} from "./Category"; import Container from "./database/Container";
import {iTender} from "./iTender";
const log = debug("itender:server"); const log = debug("itender:server");
@ -12,31 +13,64 @@ const app = new App();
const wsApp = new WebsocketApp(); const wsApp = new WebsocketApp();
(async () => {
(async( ) => {
try { try {
log("Starting..."); log("Starting...");
await Database.connect(); await Database.connect();
await app.listen(); await app.listen();
await wsApp.listen(); await wsApp.listen();
await test(); await init();
} catch( e ) } catch (e) {
{
console.error("---- ERROR ----") console.error("---- ERROR ----")
console.error(e); console.error(e);
process.exit(-1); process.exit(-1);
} }
})(); })();
function init() : Promise<void> {
log("Initializing...");
return new Promise(async resolve => {
await iTender.refreshContainers();
await iTender.refreshDrinks();
resolve();
});
}
async function test() { async function test() {
console.log("Testing fn"); console.log("Testing fn");
let ingredient = await Ingredient.findOne({name: "Cola"});
if (!ingredient)
return;
/*let drink = new Drink();
drink.name = "Cola";
drink.ingredients = [{type: ingredient, amount: 200}];
await drink.save();*/
let drink = await Drink.findOne({name: "Cola"}).populate("ingredients.type");
if (!drink) return;
console.log(drink);
/*let container = new Container();
container.slot = 1;
container.volume = 750;
container.sensorEcho = 26;
container.sensorTrigger = 27;
container.content = ingredient;
container.sensorFilledMax = 2;
container.sensorFilledMin = 15;
await container.save();*/
let container = await Container.findOne({slot: 1});
if (!container) return;
console.log(container);
let drink = await Drink.findOne( { name: "Mezzo Mix" } ).populate("ingredients.type");
if(!drink)return;
console.log(drink.ingredients);
//console.log(drink.ingredients) //console.log(drink.ingredients)
/*let ingredient = new Ingredient(); /*let ingredient = new Ingredient();
ingredient.name = "Cola"; ingredient.name = "Cola";

View File

@ -1,10 +1,10 @@
import {WebSocketPayload} from "../../WebSocketPayload"; import {WebSocketPayload} from "../../WebSocketPayload";
import debug from "debug"; import debug from "debug";
import {WebSocketHandler} from "../../WebSocketHandler";
const express = require('express'); const express = require('express');
const router = express.Router(); const router = express.Router();
let currentWS: WebSocket;
const log = debug("itender:websocket"); const log = debug("itender:websocket");
@ -24,9 +24,9 @@ router.ws('/', (ws, req, next) => {
}); });
ws.on('open', (listener) => { ws.on('open', (listener) => {
if (currentWS) if (WebSocketHandler.ws)
currentWS.close(1001); WebSocketHandler.ws.close(1001);
currentWS = ws; WebSocketHandler.ws = ws;
}); });
}); });

View File

@ -1,6 +1,6 @@
import {Modal} from "./Modal"; import {Modal} from "./Modal";
export class WebSocketHandler { export class WebWebSocketHandler {
private socket : WebSocket; private socket : WebSocket;
constructor() { constructor() {

View File

@ -1,4 +1,4 @@
import {WebSocketHandler} from "./WebSocketHandler"; import {WebWebSocketHandler} from "./WebWebSocketHandler";
import {Modal} from "./Modal"; import {Modal} from "./Modal";
const main = document.getElementById("main"); const main = document.getElementById("main");
@ -54,6 +54,6 @@ function load()
let wsHandler; let wsHandler;
function connect() function connect()
{ {
wsHandler = new WebSocketHandler(); wsHandler = new WebWebSocketHandler();
} }

View File

@ -991,6 +991,13 @@
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
"@types/rpi-gpio@^2.1.1":
version "2.1.1"
resolved "https://registry.yarnpkg.com/@types/rpi-gpio/-/rpi-gpio-2.1.1.tgz#1f9c04926bae9bc95461c428b0397cf9f7ddbc4a"
integrity sha512-GGKVP2BatDgGEnxqBe6PbE2Wu2ZqYXpW5o6GIkOsbuj5hKS4W7DtP3gz3Vq+fNQ6JIDkv9qR0YAyDR4w9He3Bw==
dependencies:
"@types/node" "*"
"@types/serve-static@*": "@types/serve-static@*":
version "1.15.0" version "1.15.0"
resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.0.tgz#c7930ff61afb334e121a9da780aac0d9b8f34155"
@ -1278,6 +1285,13 @@ asap@~2.0.3:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
async-retry@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
integrity sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==
dependencies:
retry "0.13.1"
babel-runtime@^6.26.0: babel-runtime@^6.26.0:
version "6.26.0" version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
@ -1641,7 +1655,7 @@ debug@4.x, debug@^4.1.0, debug@^4.1.1, debug@^4.3.4:
dependencies: dependencies:
ms "2.1.2" ms "2.1.2"
debug@^3.2.7: debug@^3.1.0, debug@^3.2.7:
version "3.2.7" version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@ -1762,6 +1776,14 @@ envinfo@^7.7.3:
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw== integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
epoll@^2.0.10:
version "2.0.10"
resolved "https://registry.yarnpkg.com/epoll/-/epoll-2.0.10.tgz#aae54a267d7fb8108074b62646d658b267f73915"
integrity sha512-kx5y1SxivN99HjXDZpE/A73FHJV/dzRQt+qoF88CEza3RcEKGqNfkXPPY/oqVBV5w6G2N6b8xd5s5zprgrUVnQ==
dependencies:
bindings "^1.5.0"
nan "^2.14.0"
epoll@^4.0.1: epoll@^4.0.1:
version "4.0.1" version "4.0.1"
resolved "https://registry.yarnpkg.com/epoll/-/epoll-4.0.1.tgz#62b787d29980394798fa7b9a45fbefea518fc732" resolved "https://registry.yarnpkg.com/epoll/-/epoll-4.0.1.tgz#62b787d29980394798fa7b9a45fbefea518fc732"
@ -2521,7 +2543,7 @@ ms@2.1.3, ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
nan@^2.14.2: nan@^2.14.0, nan@^2.14.2:
version "2.17.0" version "2.17.0"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb"
integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==
@ -2927,6 +2949,11 @@ responselike@^1.0.2:
dependencies: dependencies:
lowercase-keys "^1.0.0" lowercase-keys "^1.0.0"
retry@0.13.1:
version "0.13.1"
resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658"
integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==
right-align@^0.1.1: right-align@^0.1.1:
version "0.1.3" version "0.1.3"
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
@ -2946,6 +2973,15 @@ roarr@^2.15.3:
semver-compare "^1.0.0" semver-compare "^1.0.0"
sprintf-js "^1.1.2" sprintf-js "^1.1.2"
rpi-gpio@^2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/rpi-gpio/-/rpi-gpio-2.1.7.tgz#0a478d77133733830b4eb62075fddf5d6bb7ec82"
integrity sha512-u6McqKR6QuHP5zq/3UuXkJfY2fJHhvNaJMP58jz7pIf4OFyhBI3hh0vyYn5OGMwFbFdcBfOMC1RAPeRXLKF6YA==
dependencies:
async-retry "^1.2.1"
debug "^3.1.0"
epoll "^2.0.10"
safe-buffer@5.1.2: safe-buffer@5.1.2:
version "5.1.2" version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"