193 lines
6.6 KiB
TypeScript
193 lines
6.6 KiB
TypeScript
import {IJob} from "./database/IJob";
|
|
import {iTenderStatus} from "./iTenderStatus";
|
|
import {MyGPIO} from "./MyGPIO";
|
|
import GPIO from "rpi-gpio";
|
|
import {SensorType} from "./SensorType";
|
|
import {clearInterval} from "timers";
|
|
import {IContainer} from "./database/IContainer";
|
|
import {iTender} from "./iTender";
|
|
import debug from "debug";
|
|
import {ArduinoProxyPayload} from "./ArduinoProxyPayload";
|
|
import {ArduinoProxyPayloadType} from "./ArduinoProxyPayloadType";
|
|
import {ArduinoProxy} from "./ArduinoProxy";
|
|
|
|
const isPI = require("detect-rpi");
|
|
|
|
const log = debug("itender:mix");
|
|
|
|
export class Mixer {
|
|
static get currentJob(): IJob {
|
|
return this._currentJob;
|
|
}
|
|
|
|
/**
|
|
* Timers for the job, for the pumps etc.
|
|
* @private
|
|
*/
|
|
private static _jobTimers: NodeJS.Timeout[] = [];
|
|
|
|
/**
|
|
* The current itender job
|
|
* @private
|
|
*/
|
|
private static _currentJob: IJob;
|
|
|
|
/**
|
|
* Checks if the job has finished every 500ms
|
|
* @private
|
|
*/
|
|
private static _jobEndCheckInterval: NodeJS.Timer;
|
|
|
|
/**
|
|
* Start the internal fill method, a sub-method of the onReceiveFill method
|
|
* This method only gets executed if REALLY all is okay, it is the internal function
|
|
* @param job
|
|
*/
|
|
|
|
static async startFill(job: IJob) {
|
|
job.startedAt = new Date();
|
|
await job.populate([{path: "amounts.ingredient"}, {path: "amounts.container"}, {path: "drink"}]);
|
|
log("New fill job " + job.drink.name + " will take " + job.estimatedTime + "s");
|
|
|
|
this._currentJob = job;
|
|
iTender.setStatus(iTenderStatus.FILLING);
|
|
|
|
|
|
for (let x of job.amounts) {
|
|
|
|
// Start pump here
|
|
try {
|
|
if (x.container.useProxy) {
|
|
let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
|
|
pin: x.container.pumpPin,
|
|
mode: "DIGITAL",
|
|
value: 255
|
|
});
|
|
await ArduinoProxy.sendRequest(payload);
|
|
} else {
|
|
await MyGPIO.setup(x.container.pumpPin, GPIO.DIR_OUT)
|
|
await MyGPIO.write(x.container.pumpPin, true);
|
|
}
|
|
|
|
} catch (e) {
|
|
if (isPI()) {
|
|
log("[ERROR] GPIO I/O Error " + e);
|
|
// Todo error handling to user
|
|
await this.cancelFill();
|
|
return;
|
|
} else {
|
|
log("[WARNING] GPIO I/O Error, but it's normal cause you are not on raspberry");
|
|
}
|
|
}
|
|
|
|
|
|
let waitTime = (iTender.secondsPer100ml as number) / 100 * x.amount * 1000;
|
|
log(`Starting output of pump ${x.container.pumpPin}`);
|
|
//mixLog(x.ingredient + " takes " + (waitTime / 1000) + "s for " + x.amount + "ml");
|
|
let timer = setTimeout(async () => {
|
|
// Remove from list of timers
|
|
let arr: NodeJS.Timer[] = [];
|
|
for (let i = 0; i < this._jobTimers.length; i++) {
|
|
if (this._jobTimers[i] != timer)
|
|
arr.push(this._jobTimers[i]);
|
|
}
|
|
|
|
log(`Stopping output of pump ${x.container.pumpPin}`);
|
|
// Stop pump here
|
|
try {
|
|
if (x.container.useProxy) {
|
|
let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
|
|
pin: x.container.pumpPin,
|
|
mode: "DIGITAL",
|
|
"value": 0
|
|
});
|
|
await ArduinoProxy.sendRequest(payload);
|
|
} else {
|
|
await MyGPIO.write(x.container.pumpPin, false);
|
|
}
|
|
|
|
} catch (e) {
|
|
if (isPI()) {
|
|
log("[ERROR] GPIO I/O Error " + e);
|
|
await this.cancelFill();
|
|
return;
|
|
} else {
|
|
log("[WARNING] GPIO I/O Error, but it's normal cause you are not on raspberry");
|
|
}
|
|
}
|
|
|
|
if (x.container.sensorType == SensorType.NONE) {
|
|
// V2: Manual measuring
|
|
x.container.filled = x.container.filled - x.amount;
|
|
await x.container.save();
|
|
}
|
|
|
|
this._jobTimers = arr;
|
|
|
|
}, waitTime);
|
|
this._jobTimers.push(timer);
|
|
}
|
|
|
|
this._jobEndCheckInterval = setInterval(async () => {
|
|
|
|
if (this._jobTimers.length != 0)
|
|
return;
|
|
|
|
clearInterval(this._jobEndCheckInterval);
|
|
job.endAt = new Date();
|
|
job.successful = true;
|
|
|
|
await job.save();
|
|
log("Job successful");
|
|
setTimeout(() => iTender.setStatus(iTenderStatus.READY), 3000)
|
|
|
|
}, 500);
|
|
}
|
|
|
|
|
|
/**
|
|
* Cancel the fill instantly
|
|
*/
|
|
static async cancelFill() {
|
|
if (!this._currentJob || iTender.status != iTenderStatus.FILLING)
|
|
return;
|
|
|
|
clearInterval(this._jobEndCheckInterval);
|
|
this._currentJob.successful = false;
|
|
this._currentJob.endAt = new Date();
|
|
await this._currentJob.save();
|
|
|
|
for (let timer of this._jobTimers) {
|
|
// Clears all the ongoing stop timers
|
|
clearTimeout(timer);
|
|
}
|
|
|
|
for (let jobIngredient of this._currentJob.amounts) {
|
|
// stop pump pin
|
|
try {
|
|
if (jobIngredient.container.useProxy) {
|
|
let payload = new ArduinoProxyPayload(ArduinoProxyPayloadType.SET_PIN, {
|
|
pin: jobIngredient.container.pumpPin,
|
|
mode: "DIGITAL",
|
|
"value": 0
|
|
});
|
|
await ArduinoProxy.sendRequest(payload);
|
|
} else {
|
|
await MyGPIO.write(jobIngredient.container.pumpPin, false);
|
|
}
|
|
} catch (e) {
|
|
|
|
}
|
|
|
|
// ToDo v2 calc
|
|
let container: IContainer = jobIngredient.container;
|
|
let deltaStartStop = (this._currentJob.endAt.getTime() - this._currentJob.startedAt.getTime()) / 1000;
|
|
|
|
// füllmenge - ( ( (stopp-start) / 1000 ) * ( sekunden100ml / 100 ) )
|
|
container.filled = container.filled - (deltaStartStop * (iTender.secondsPer100ml / 100)) // V2: Near the current fill value based on time values from delta start stop
|
|
container.save().then();
|
|
}
|
|
|
|
iTender.setStatus(iTenderStatus.READY);
|
|
}
|
|
} |