diff --git a/package.json b/package.json index 08069f0..1b93a19 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@types/mongoose": "^5.11.97", "@types/morgan": "^1.9.3", "@types/node": "^18.11.9", + "@types/rpi-gpio": "^2.1.1", "cookie-parser": "^1.4.6", "debug": "^4.3.4", "express": "~4.16.1", @@ -27,7 +28,8 @@ "http-errors": "~1.6.3", "mongoose": "^6.7.2", "morgan": "^1.10.0", - "pug": "2.0.0-beta11" + "pug": "2.0.0-beta11", + "rpi-gpio": "^2.1.7" }, "devDependencies": { "@types/electron": "^1.6.10", diff --git a/public/stylesheets/buttons.css b/public/stylesheets/buttons.css index f8c4ab8..f96da48 100644 --- a/public/stylesheets/buttons.css +++ b/public/stylesheets/buttons.css @@ -1,11 +1,10 @@ .btn { padding: 11px 15px; border: none; - cursor: pointer; color: white; font-size: 0.8em; border-radius: 2px; - + cursor:none !important; } diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 55c59bb..6845fe6 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -3,11 +3,26 @@ @import url("/stylesheets/buttons.css"); @import url("/stylesheets/modal.css"); + +:root { + cursor: none; +} + + +html * {cursor: none !important} + + +body::-webkit-scrollbar { + display: none; +} + + body { color: white; scroll-behavior: smooth; font-family: "Roboto", serif; font-style: normal; + cursor: none; } @@ -118,10 +133,13 @@ body { border-radius: 30px 10px 30px; } + .drink .thumbnail { grid-column: span 1; grid-row: span 2; } + + .drink .name { grid-column: span 1; grid-row: span 1; diff --git a/src/WebSocketHandler.ts b/src/WebSocketHandler.ts new file mode 100644 index 0000000..b319489 --- /dev/null +++ b/src/WebSocketHandler.ts @@ -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 { + 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); + } + }); + } + +} \ No newline at end of file diff --git a/src/WebSocketPayload.ts b/src/WebSocketPayload.ts index f487b06..147b6da 100644 --- a/src/WebSocketPayload.ts +++ b/src/WebSocketPayload.ts @@ -45,8 +45,17 @@ export class WebSocketPayload { return null; } - let wsEvent = WebSocketEvent[ rawPayload.event]; + let wsEvent = WebSocketEvent[rawPayload.event]; 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; + } } \ No newline at end of file diff --git a/src/database/AbstractContainer.ts b/src/database/AbstractContainer.ts deleted file mode 100644 index 32e9898..0000000 --- a/src/database/AbstractContainer.ts +++ /dev/null @@ -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); - } - - - -} \ No newline at end of file diff --git a/src/database/Container.ts b/src/database/Container.ts index 60720de..ceb9b8f 100644 --- a/src/database/Container.ts +++ b/src/database/Container.ts @@ -1,17 +1,18 @@ import * as Mongoose from "mongoose"; -import {AbstractContainer} from "./AbstractContainer"; import mongoose from "mongoose"; +import {IContainer} from "./IContainer"; -export const ContainerSchema = new Mongoose.Schema({ +export const ContainerSchema = new Mongoose.Schema({ slot: {type: Number}, volume: {type: Number, required: true, default: 1000}, sensorEcho: Number, sensorTrigger: Number, - content: {type: String}, + content: {type: mongoose.Types.ObjectId}, sensorFilledMax: Number, - sensorFilledMin: Number + sensorFilledMin: Number, + filled: Number, }); -const Container = mongoose.model('Container', ContainerSchema); +const Container = mongoose.model('Container', ContainerSchema); export default Container; diff --git a/src/database/Drink.ts b/src/database/Drink.ts index a04fbfa..a57ec0d 100644 --- a/src/database/Drink.ts +++ b/src/database/Drink.ts @@ -4,7 +4,8 @@ import * as mongoose from "mongoose"; export const DrinkSchema = new mongoose.Schema({ name: {type: String, required: true}, 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('Drink', DrinkSchema); diff --git a/src/database/IContainer.ts b/src/database/IContainer.ts index 04cdef1..89bd656 100644 --- a/src/database/IContainer.ts +++ b/src/database/IContainer.ts @@ -1,7 +1,7 @@ import {IIngredient} from "./IIngredient"; import * as mongoose from "mongoose"; -export interface IContainer { +export interface IContainer extends mongoose.Document{ slot: number; content: IIngredient|undefined; volume: number; @@ -9,4 +9,5 @@ export interface IContainer { sensorFilledMax: number; sensorTrigger: number; sensorEcho: number; + filled: Number; } \ No newline at end of file diff --git a/src/database/IDrink.ts b/src/database/IDrink.ts index 7ab5bb5..c19367a 100644 --- a/src/database/IDrink.ts +++ b/src/database/IDrink.ts @@ -12,4 +12,7 @@ export interface IDrink extends mongoose.Document { // Category of the drink category: Category; + // Recommended amount in milliliters + recommendedQuantity: number; + } \ No newline at end of file diff --git a/src/iTender.ts b/src/iTender.ts index 5d21c33..90a7ef4 100644 --- a/src/iTender.ts +++ b/src/iTender.ts @@ -1,10 +1,15 @@ 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 { 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) { this._status = value; @@ -22,15 +27,65 @@ export class iTender { // todo Stop fill method } - static measureContainers() { - for( let container of this.containers ) - { - container.measure(); - } + static measureContainers(): Promise { + return new Promise(async resolve => { + let containers = await Container.find(); + /* + public measure() : Number { + let dist = this.sensor.distance(); + return dist * 100 / (this.sensorFilledMax + this.sensorFilledMin); + } + */ + }) } - static loadContainers() { + static refreshDrinks(): Promise { + 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 { + + 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}); + } + }) + } + + } \ No newline at end of file diff --git a/src/iTenderStatus.ts b/src/iTenderStatus.ts index 637bc22..e2b1821 100644 --- a/src/iTenderStatus.ts +++ b/src/iTenderStatus.ts @@ -1,7 +1,14 @@ export enum iTenderStatus { + // Machine is going to start STARTING, + // Machine is ready READY, + // Machine is filling your drink and destroying your leberwurst FILLING, + // Drinks will be refreshed from global database (the internet neuland :O) REFRESHING, + // Drinks will be calculated (check containers and which drinks can be done) + CALCULATING, + // An error happened; Oh no :( ERROR } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 442df5e..b5d8346 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,7 +4,8 @@ import {WebsocketApp} from "./WebsocketApp"; import {Database} from "./database/Database"; import Drink from "./database/Drink"; import Ingredient from "./database/Ingredient"; -import {Category} from "./Category"; +import Container from "./database/Container"; +import {iTender} from "./iTender"; const log = debug("itender:server"); @@ -12,31 +13,64 @@ const app = new App(); const wsApp = new WebsocketApp(); - -(async( ) => { +(async () => { try { log("Starting..."); await Database.connect(); await app.listen(); await wsApp.listen(); - await test(); - } catch( e ) - { + await init(); + } catch (e) { console.error("---- ERROR ----") console.error(e); process.exit(-1); } })(); +function init() : Promise { + log("Initializing..."); + return new Promise(async resolve => { + await iTender.refreshContainers(); + await iTender.refreshDrinks(); + resolve(); + }); +} async function test() { 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) /*let ingredient = new Ingredient(); ingredient.name = "Cola"; diff --git a/src/routes/ws/websocket.ts b/src/routes/ws/websocket.ts index 4244940..1517f7d 100644 --- a/src/routes/ws/websocket.ts +++ b/src/routes/ws/websocket.ts @@ -1,10 +1,10 @@ import {WebSocketPayload} from "../../WebSocketPayload"; import debug from "debug"; +import {WebSocketHandler} from "../../WebSocketHandler"; const express = require('express'); const router = express.Router(); -let currentWS: WebSocket; const log = debug("itender:websocket"); @@ -24,9 +24,9 @@ router.ws('/', (ws, req, next) => { }); ws.on('open', (listener) => { - if (currentWS) - currentWS.close(1001); - currentWS = ws; + if (WebSocketHandler.ws) + WebSocketHandler.ws.close(1001); + WebSocketHandler.ws = ws; }); }); diff --git a/src/web/WebSocketHandler.ts b/src/web/WebWebSocketHandler.ts similarity index 96% rename from src/web/WebSocketHandler.ts rename to src/web/WebWebSocketHandler.ts index 56442a9..3af1523 100644 --- a/src/web/WebSocketHandler.ts +++ b/src/web/WebWebSocketHandler.ts @@ -1,6 +1,6 @@ import {Modal} from "./Modal"; -export class WebSocketHandler { +export class WebWebSocketHandler { private socket : WebSocket; constructor() { diff --git a/src/web/main.ts b/src/web/main.ts index e3e0dbd..5444e4b 100644 --- a/src/web/main.ts +++ b/src/web/main.ts @@ -1,4 +1,4 @@ -import {WebSocketHandler} from "./WebSocketHandler"; +import {WebWebSocketHandler} from "./WebWebSocketHandler"; import {Modal} from "./Modal"; const main = document.getElementById("main"); @@ -54,6 +54,6 @@ function load() let wsHandler; function connect() { - wsHandler = new WebSocketHandler(); + wsHandler = new WebWebSocketHandler(); } diff --git a/yarn.lock b/yarn.lock index 9269a79..c4965b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -991,6 +991,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" 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@*": version "1.15.0" 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" 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: version "6.26.0" 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: ms "2.1.2" -debug@^3.2.7: +debug@^3.1.0, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -1762,6 +1776,14 @@ envinfo@^7.7.3: resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475" 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: version "4.0.1" 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" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nan@^2.14.2: +nan@^2.14.0, nan@^2.14.2: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== @@ -2927,6 +2949,11 @@ responselike@^1.0.2: dependencies: 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: version "0.1.3" 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" 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: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"