Update v1.0.0
Took 1 hour 1 minute
Before Width: | Height: | Size: 353 KiB |
Before Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 162 KiB |
Before Width: | Height: | Size: 1.7 MiB |
Before Width: | Height: | Size: 448 KiB |
Before Width: | Height: | Size: 462 KiB |
Before Width: | Height: | Size: 3.7 MiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 1020 KiB |
Before Width: | Height: | Size: 611 KiB |
Before Width: | Height: | Size: 313 KiB |
Before Width: | Height: | Size: 167 KiB |
Before Width: | Height: | Size: 136 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 253 KiB |
Before Width: | Height: | Size: 433 KiB |
Before Width: | Height: | Size: 250 KiB |
Before Width: | Height: | Size: 794 KiB |
Before Width: | Height: | Size: 116 KiB |
25
src/Utils.ts
@ -1,5 +1,6 @@
|
|||||||
import * as dns from "dns";
|
import * as dns from "dns";
|
||||||
import * as ping from "net-ping";
|
import * as fs from "fs";
|
||||||
|
import * as https from 'https';
|
||||||
|
|
||||||
export class Utils {
|
export class Utils {
|
||||||
public static checkInternet(): Promise<boolean> {
|
public static checkInternet(): Promise<boolean> {
|
||||||
@ -20,4 +21,26 @@ export class Utils {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static downloadImage(url, filepath) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
https.get(url, (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
res.pipe(fs.createWriteStream(filepath))
|
||||||
|
.on('error', reject)
|
||||||
|
.once('close', () => resolve(filepath));
|
||||||
|
} else {
|
||||||
|
// Consume response data to free up memory
|
||||||
|
res.resume();
|
||||||
|
reject(new Error(`Request Failed With a Status Code: ${res.statusCode}`));
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static deleteImage( id )
|
||||||
|
{
|
||||||
|
fs.unlinkSync("./public/images/" + id + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -3,8 +3,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
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const Drink = mongoose.model<IDrink>('Drink', DrinkSchema);
|
const Drink = mongoose.model<IDrink>('Drink', DrinkSchema);
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {IIngredient} from "./IIngredient";
|
import {IIngredient} from "./IIngredient";
|
||||||
import {Category} from "../Category";
|
|
||||||
import * as mongoose from "mongoose";
|
import * as mongoose from "mongoose";
|
||||||
|
|
||||||
export interface IDrink extends mongoose.Document {
|
export interface IDrink extends mongoose.Document {
|
||||||
@ -9,8 +8,5 @@ export interface IDrink extends mongoose.Document {
|
|||||||
// Ingredients
|
// Ingredients
|
||||||
ingredients: { type: IIngredient, amount: Number }[];
|
ingredients: { type: IIngredient, amount: Number }[];
|
||||||
|
|
||||||
// Category of the drink
|
|
||||||
category: Category;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -19,6 +19,7 @@ import {clearInterval} from "timers";
|
|||||||
import {RejectReason} from "./RejectReason";
|
import {RejectReason} from "./RejectReason";
|
||||||
import {Settings} from "./Settings";
|
import {Settings} from "./Settings";
|
||||||
import GPIO from "rpi-gpio";
|
import GPIO from "rpi-gpio";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const log = debug("itender:station");
|
const log = debug("itender:station");
|
||||||
const mixLog = debug("itender:mix");
|
const mixLog = debug("itender:mix");
|
||||||
@ -56,6 +57,7 @@ export class iTender {
|
|||||||
this._status = status;
|
this._status = status;
|
||||||
if (WebSocketHandler.ws && WebSocketHandler.ws.readyState == 1)
|
if (WebSocketHandler.ws && WebSocketHandler.ws.readyState == 1)
|
||||||
WebSocketHandler.sendStatus().then().catch(console.error);
|
WebSocketHandler.sendStatus().then().catch(console.error);
|
||||||
|
log("Status is now " + status);
|
||||||
}
|
}
|
||||||
|
|
||||||
static get status(): iTenderStatus {
|
static get status(): iTenderStatus {
|
||||||
@ -138,11 +140,11 @@ export class iTender {
|
|||||||
let timers: NodeJS.Timeout[] = [];
|
let timers: NodeJS.Timeout[] = [];
|
||||||
for (let x of job.amounts) {
|
for (let x of job.amounts) {
|
||||||
// Start pump here
|
// Start pump here
|
||||||
await GPIO.setup(x.container.pumpPin,GPIO.DIR_OUT);
|
await GPIO.setup(x.container.pumpPin, GPIO.DIR_OUT);
|
||||||
await GPIO.write(x.container.pumpPin, true);
|
await GPIO.write(x.container.pumpPin, true);
|
||||||
|
|
||||||
let waitTime = (Settings.get("secondsPer100ml") as number) / 100 * x.amount * 1000;
|
let waitTime = (Settings.get("secondsPer100ml") as number) / 100 * x.amount * 1000;
|
||||||
mixLog( `Starting output of pump ${x.container.pumpPin}` );
|
mixLog(`Starting output of pump ${x.container.pumpPin}`);
|
||||||
//mixLog(x.ingredient + " takes " + (waitTime / 1000) + "s for " + x.amount + "ml");
|
//mixLog(x.ingredient + " takes " + (waitTime / 1000) + "s for " + x.amount + "ml");
|
||||||
let timer = setTimeout(() => {
|
let timer = setTimeout(() => {
|
||||||
// Stop pump here
|
// Stop pump here
|
||||||
@ -154,7 +156,7 @@ export class iTender {
|
|||||||
if (timers[i] != timer)
|
if (timers[i] != timer)
|
||||||
arr.push(timers[i]);
|
arr.push(timers[i]);
|
||||||
}
|
}
|
||||||
mixLog( `Stopping output of pump ${x.container.pumpPin}` )
|
mixLog(`Stopping output of pump ${x.container.pumpPin}`)
|
||||||
timers = arr;
|
timers = arr;
|
||||||
|
|
||||||
}, waitTime);
|
}, waitTime);
|
||||||
@ -287,11 +289,97 @@ export class iTender {
|
|||||||
static refreshFromServer(): Promise<void> {
|
static refreshFromServer(): Promise<void> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
iTender.setStatus(iTenderStatus.DOWNLOADING)
|
iTender.setStatus(iTenderStatus.DOWNLOADING)
|
||||||
|
log("Refreshing drinks from server...");
|
||||||
|
try {
|
||||||
|
const requestIngredients = await axios.get("https://itender.iif.li/api/ingredients");
|
||||||
|
let serverIngredients = requestIngredients.data as IIngredient[];
|
||||||
|
log("Got " + serverIngredients.length + " ingredients from server");
|
||||||
|
|
||||||
|
let localIngredients = await Ingredient.find();
|
||||||
|
for (let local of localIngredients) {
|
||||||
|
let found = false;
|
||||||
|
for (let remote of serverIngredients) {
|
||||||
|
if (local.name == remote.name) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
await Ingredient.deleteOne({"_id": local._id});
|
||||||
|
for( let c of (await Container.find( {content: local._id } )) )
|
||||||
|
{
|
||||||
|
c.content = undefined;
|
||||||
|
c.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (let remote of serverIngredients) {
|
||||||
|
let ingredient = await Ingredient.findOne({name: remote.name});
|
||||||
|
if (!ingredient)
|
||||||
|
ingredient = new Ingredient();
|
||||||
|
|
||||||
|
ingredient.name = remote.name;
|
||||||
|
await ingredient.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const requestDrinks = await axios.get("https://itender.iif.li/api/drinks");
|
||||||
|
let serverDrinks = requestDrinks.data as IDrink[];
|
||||||
|
log("Got " + serverDrinks.length + " drinks from server");
|
||||||
|
|
||||||
|
|
||||||
|
let localDrinks = await Drink.find();
|
||||||
|
for (let local of localDrinks) {
|
||||||
|
let found = false;
|
||||||
|
for (let remote of serverDrinks) {
|
||||||
|
if (local.name == remote.name) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
Utils.deleteImage(local._id);
|
||||||
|
await Drink.deleteOne({"_id": local._id});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (let remote of serverDrinks) {
|
||||||
|
let drink = await Drink.findOne({name: remote.name});
|
||||||
|
if (!drink)
|
||||||
|
drink = new Drink();
|
||||||
|
|
||||||
|
drink.name = remote.name;
|
||||||
|
drink.ingredients = remote.ingredients;
|
||||||
|
|
||||||
|
await drink.save();
|
||||||
|
|
||||||
|
|
||||||
|
// Download thumbnail
|
||||||
|
Utils.downloadImage("https://itender.iif.li/images/" + remote._id + ".png", "./public/images/" + drink._id + ".png").then(filepath => {
|
||||||
|
log("Drink " + remote.name + "'s Thumbnail downloaded to " + filepath);
|
||||||
|
}).catch(e => {
|
||||||
|
log("Drink " + remote.name + " failed to download thumbnail!\n" + e);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
iTender.setStatus(iTenderStatus.READY);
|
iTender.setStatus(iTenderStatus.READY);
|
||||||
resolve();
|
resolve();
|
||||||
|
iTender.refreshDrinks();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ router.ws('/', async (ws, req, next) => {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.CONTAINERS: {
|
case RequestType.CONTAINERS: {
|
||||||
WebSocketHandler.answerRequest(msg.data["type"] as RequestType, (await Container.find().sort({"slot": 1})));
|
WebSocketHandler.answerRequest(msg.data["type"] as RequestType, (await Container.find().sort({"slot": 1}).populate("content")));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RequestType.INGREDIENTS: {
|
case RequestType.INGREDIENTS: {
|
||||||
|