diff --git a/recipe/py/.vscode/launch.json b/recipe/py/.vscode/launch.json new file mode 100644 index 0000000..d5b59c4 --- /dev/null +++ b/recipe/py/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/recipe/py/.vscode/settings.json b/recipe/py/.vscode/settings.json index 21f07a0..f1dc2e8 100644 --- a/recipe/py/.vscode/settings.json +++ b/recipe/py/.vscode/settings.json @@ -1,5 +1,4 @@ { - "python.pythonPath": "/usr/bin/python2.7", "python.linting.pylintEnabled": true, "python.linting.enabled": true } \ No newline at end of file diff --git a/recipe/py/dummy.py b/recipe/py/dummy.py index 0039361..4619c74 100644 --- a/recipe/py/dummy.py +++ b/recipe/py/dummy.py @@ -31,6 +31,10 @@ class machineStatus: strArmPos = "0" strCvtPos = "0" ovenOpMode = "manual" + bucketOpMode = "manual" + robotOpMode = "manual" + allModuleGood2Go = False + modeRecheckCnt = 0 macst = machineStatus() @@ -602,7 +606,19 @@ def ctrl_latch(): mqttc.connect("localhost", 1883, 60) mqttc.loop_start() -if macst.ovenOpMode == "remote": +while True: + if macst.ovenOpMode == "remote" and macst.bucketOpMode == "remote" and macst.robotOpMode == "remote": + macst.allModuleGood2Go = True + break + else: + macst.allModuleGood2Go = False + if macst.modeRecheckCnt >= 3: + break + else: + macst.modeRecheckCnt = macst.modeRecheckCnt + 1 + time.sleep(1) + +if macst.allModuleGood2Go == True: thread1 = threading.Thread(target=ctrl_oven_and_robot) thread2 = threading.Thread(target=ctrl_latch) @@ -614,7 +630,13 @@ def ctrl_latch(): mqttc.publish("gate/cmd/open", "true") else: - logger.fatal("the mode of oven is not remote") + msg = ("recipe: the modules are not all in the remote mode" + + ", modeRecheckCnt: " + str(macst.modeRecheckCnt) + + ", ovenOpMode: " + str(macst.ovenOpMode) + + ", bucketOpMode: " + str(macst.bucketOpMode) + + ", robotOpMode: " + str(macst.robotOpMode)) + logger.fatal(msg) + mqttc.publish("bucket/status/alarm", msg) mqttc.disconnect() mqttc.loop_stop() diff --git a/sh/test/ui2/backend/index.js b/sh/test/ui2/backend/index.js deleted file mode 100644 index 3874c3c..0000000 --- a/sh/test/ui2/backend/index.js +++ /dev/null @@ -1,330 +0,0 @@ -require("dotenv").config(); - -const version = "cakeVendingBackend v1.3"; - -const log4js = require("log4js"); -log4js.configure({ - appenders: { - file: { - type: "dateFile", - filename: "log/cakeBackend.log", - maxLogSize: 1000000, // 1 MB - backups: 5, - category: "normal", - }, - out: { - type: "stdout", - }, - }, - categories: { - default: { appenders: ["file", "out"], level: "trace" }, - }, -}); -const logger = log4js.getLogger("cake"); - -const express = require("express"); -const app = express(); -const http = require("http"); -const https = require("https"); -const cors = require("cors"); -const bodyParser = require("body-parser"); -const fs = require("fs"); -const exec = require("child_process").exec; -const mqtt = require("mqtt"); -const jwt = require("express-jwt"); -const sqlite3 = require("sqlite3").verbose(); -const os = require("os"); -const axios = require("axios"); - -const mqttOpt = { - port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, - clientId: version, -}; - -const httpsOptions = { - key: fs.readFileSync("./ssl_files/server_private_key.pem"), - ca: [fs.readFileSync("./ssl_files/cert.pem")], - cert: fs.readFileSync("./ssl_files/server_cert.pem"), -}; - -const checkModuleAliveInterval = 10 * 60 * 1000; //ms -const maxModuleDeadCnt = 0; -const maxMachTemp = 70; - -let bucketAliveMsg = "bucketAliveMsg"; -let lastBucketAliveMsg = "lastBucketAliveMsg"; -let bucketDeadCnt = 0; - -let ovenAliveMsg = "ovenAliveMsg"; -let lastOvenAliveMsg = "lastOvenAliveMsg"; -let ovenDeadCnt = 0; - -let robotAliveMsg = "robotAliveMsg"; -let lastRobotAliveMsg = "lastRobotAliveMsg"; -let robotDeadCnt = 0; - -let latchAliveMsg = "latchAliveMsg"; -let lastLatchAliveMsg = "lastLatchAliveMsg"; -let latchDeadCnt = 0; - -const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); - -const iNameList = os.networkInterfaces(); - -logger.info(version + " started"); - -let db = new sqlite3.Database("mydatebase.db", function (err) { - if (err) throw err; -}); - -// db.serialize(function () { -// //db.run 如果 Staff 資料表不存在,那就建立 Staff 資料表 -// db.run("CREATE TABLE IF NOT EXISTS Stuff (thing TEXT)"); -// let stmt = db.prepare("INSERT INTO Stuff VALUES (?)"); - -// //寫進10筆資料 -// for (var i = 0; i < 10; i++) { -// stmt.run("staff_number" + i); -// } - -// stmt.finalize(); - -// db.each("SELECT rowid AS id, thing FROM Stuff", function (err, row) { -// //log 出所有的資料 -// logger.trace(row.id + ": " + row.thing); -// }); -// }); - -db.close(); - -app.use(cors()); -app.use(bodyParser.urlencoded({ extended: false })); -app.use(bodyParser.text()); -app.use(log4js.connectLogger(logger, { level: "info" })); - -app.get( - "/version", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.send(version); - } -); - -app.post( - "/recipe/start/original", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - // exec("node /home/pi/recipe/original.js", function (err, stdout, stderr) { - exec("python /home/pi/recipe/test.py", function (err, stdout, stderr) { - if (err !== null) { - res.sendStatus(500); - logger.error(stderr); - } else { - res.sendStatus(200); - logger.trace(stdout); - } - }); - } -); - -app.get( - "/ad/playList", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - let readDir = fs.readdirSync("/home/pi/ad"); - res.send(readDir); - } -); - -app.post( - "/stop/oven/heating", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.sendStatus(200); - } -); - -app.post( - "/machine/alive", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.send(res.body); - } -); - -app.post( - "/coin/enable", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - mqttClient.publish("coin/cmd/enable", "true"); - res.sendStatus(200); - } -); - -app.post( - "/coin/disable", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - mqttClient.publish("coin/cmd/enable", "false"); - res.sendStatus(200); - } -); - -// http.createServer(app).listen(process.env.MACHINE_BACKEND_PORT, () => { -// logger.info( -// version + " listening on port " + process.env.MACHINE_BACKEND_PORT -// ); -// }); - -https - .createServer(httpsOptions, app) - .listen(process.env.MACHINE_BACKEND_PORT, () => { - logger.info( - version + " listening on port " + process.env.MACHINE_BACKEND_PORT - ); - }); - -// http -// .createServer(app) -// .listen(process.env.MACHINE_BACKEND_PORT, "localhost", () => { -// logger.info( -// "localhost " + -// version + -// " listening on port " + -// process.env.MACHINE_BACKEND_PORT -// ); -// }); - -// https -// .createServer(httpsOptions, app) -// .listen(process.env.MACHINE_BACKEND_PORT, iNameList.tun0[0].address, () => { -// logger.info( -// iNameList.tun0[0].address + -// " " + -// version + -// " listening on port " + -// process.env.MACHINE_BACKEND_PORT -// ); -// }); - -const postWebAPI = (url, payload) => { - axios({ - method: "post", - baseURL: process.env.TELEGRAM_BOT_IP + url, - headers: { - Authorization: "Bearer " + process.env.CAKE_ACCESS_TOKEN, - "content-type": "text/plain", - }, - data: payload, - }) - .then((res) => { - logger.trace("POST " + url + " " + payload + " " + res.status); - }) - .catch((err) => { - logger.error(err.message); - }); -}; - -mqttClient.on("message", function (topic, message) { - if (topic === "bucket/status/alive") { - bucketAliveMsg = message.toString(); - } else if (topic === "oven/status/alive") { - ovenAliveMsg = message.toString(); - } else if (topic === "robot/status/alive") { - robotAliveMsg = message.toString(); - } else if (topic === "latch/status/alive") { - latchAliveMsg = message.toString(); - } else if (topic === "bucket/status/machTemp") { - const macTempStr = message.toString(); - if (parseFloat(macTempStr) >= maxMachTemp) { - postWebAPI("/machine/alarm", "machine temperature: " + macTempStr); - } - } else if (topic === "bucket/status/alarm") { - postWebAPI("/machine/alarm", message.toString()); - } -}); - -const checkModuleAlive = () => { - if (bucketAliveMsg === lastBucketAliveMsg) { - bucketDeadCnt = bucketDeadCnt + 1; - } else { - bucketDeadCnt = 0; - } - lastBucketAliveMsg = bucketAliveMsg; - if (bucketDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "bucket is dead"); - } - - if (ovenAliveMsg === lastOvenAliveMsg) { - ovenDeadCnt = ovenDeadCnt + 1; - } else { - ovenDeadCnt = 0; - } - lastOvenAliveMsg = ovenAliveMsg; - if (ovenDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "oven is dead"); - } - - if (robotAliveMsg === lastRobotAliveMsg) { - robotDeadCnt = robotDeadCnt + 1; - } else { - robotDeadCnt = 0; - } - lastRobotAliveMsg = robotAliveMsg; - if (robotDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "robot is dead"); - } - - if (latchAliveMsg === lastLatchAliveMsg) { - latchDeadCnt = latchDeadCnt + 1; - } else { - latchDeadCnt = 0; - } - lastLatchAliveMsg = latchAliveMsg; - if (latchDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "latch is dead"); - } -}; -setInterval(checkModuleAlive, checkModuleAliveInterval); - -const mqttSubsTopis = [ - "bucket/status/#", - "oven/status/#", - "robot/status/#", - "latch/status/#", -]; - -mqttClient.on("connect", function () { - logger.info(version + " connect to broker OK"); - mqttSubsTopis.forEach(function (topic, index, array) { - mqttClient.subscribe(topic); - }); -}); diff --git a/sh/test/ui2/backend/package.json b/sh/test/ui2/backend/package.json deleted file mode 100644 index c5f2316..0000000 --- a/sh/test/ui2/backend/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "cake_vending_backend", - "version": "1.0.0", - "description": "the backend of cake vending machine", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node index.js", - "build": "webpack --config webpack.config.js" - }, - "author": "", - "license": "ISC", - "dependencies": { - "axios": "^0.19.0", - "body-parser": "^1.19.0", - "cors": "^2.8.5", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "express-jwt": "^5.3.1", - "log4js": "^6.1.2", - "mqtt": "^3.0.0", - "sqlite3": "^4.1.1" - }, - "devDependencies": { - "webpack": "^4.43.0", - "webpack-node-externals": "^1.7.2", - "file-loader": "^6.0.0", - "webpack-cli": "^3.3.11" - } -} diff --git a/sh/test/ui2/backendgpio/A4988.js b/sh/test/ui2/backendgpio/A4988.js deleted file mode 100644 index cba9e3a..0000000 --- a/sh/test/ui2/backendgpio/A4988.js +++ /dev/null @@ -1,178 +0,0 @@ -const Gpio = require("pigpio").Gpio; - -class A4988 { - constructor({ - step = 15, - dir = 14, - ms1 = 24, - ms2 = 23, - ms3 = 18, - enable = 25 - }) { - this._abort = false; - this._delay = 1; - this._direction = false; - this._steps = 0; - this._step_size = "FULL"; - this._enabled = true; - this._turning = false; - - this._step = new Gpio(step, { mode: Gpio.OUTPUT }); - this._dir = new Gpio(dir, { mode: Gpio.OUTPUT }); - this._step.digitalWrite(false); - this._dir.digitalWrite(false); - - if (ms1 && ms2 && ms3) { - this._ms1 = new Gpio(ms1, { mode: Gpio.OUTPUT }); - this._ms2 = new Gpio(ms2, { mode: Gpio.OUTPUT }); - this._ms3 = new Gpio(ms3, { mode: Gpio.OUTPUT }); - - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - } - - if (enable) { - this._enable = new Gpio(enable, { mode: Gpio.OUTPUT }); - this._enable.digitalWrite(false); - } - } - - get delay() { - return this._delay; - } - - set delay(d) { - if (typeof d != "number") throw `'delay' must be a number (${d})`; - if (d <= 0) throw `'delay' must be >= 0 (${d})`; - this._delay = d; - } - - get direction() { - return this._direction; - } - - set direction(d) { - if (typeof d != "boolean") throw `'direction' must be boolean (${d})`; - this._direction = d; - this._dir.digitalWrite(d); - } - - get step_size() { - return this._step_size; - } - - set step_size(ss) { - if (!this._ms1) return; - if (typeof ss != "string") throw `'step_size' must be a string (${ss})`; - switch (ss.toUpperCase()) { - case "FULL": - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - this._step_size = "FULL"; - break; - case "HALF": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - this._step_size = "HALF"; - break; - case "QUARTER": - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(false); - this._step_size = "QUARTER"; - break; - case "EIGHTH": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(false); - this._step_size = "EIGHTH"; - break; - case "SIXTEENTH": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(true); - this._step_size = "SIXTEENTH"; - break; - default: - break; - } - } - - get enabled() { - return this._enabled; - } - - set enabled(e) { - if (e) { - this._enable.digitalWrite(false); - this._enabled = true; - } else { - this._enable.digitalWrite(true); - this._enabled = false; - } - } - - get turning() { - return this._turning; - } - - enable() { - this._enable.digitalWrite(false); - this._enabled = true; - return new Promise(res => setTimeout(res, 5)); - } - - disable() { - this._enable.digitalWrite(true); - this._enabled = false; - return new Promise(res => setTimeout(res, 5)); - } - - _turn(steps, res) { - if (this._abort) { - this._turning = false; - res(this._steps); - return; - } - if (this._steps < steps) { - this._steps++; - } else { - this._steps--; - } - this._step.digitalWrite(true); - this._step.digitalWrite(false); - if (this._steps == steps) { - this._turning = false; - res(this._steps); - return; - } - setTimeout(() => this._turn(steps, res), this._delay); - } - - turn(steps = 1, callback) { - if (this._turning) - return Promise.reject(new Error("Motor already running")); - this._steps = 0; - this._abort = false; - this._turning = true; - if (this._steps < steps) { - this._dir.digitalWrite(true); - } else { - this._dir.digitalWrite(false); - } - if (typeof callback == "function") { - this._turn(steps, callback); - } else { - return new Promise(res => this._turn(steps, res)); - } - } - - stop() { - this._abort = true; - } -} - -module.exports = A4988; diff --git a/sh/test/ui2/backendgpio/index.js b/sh/test/ui2/backendgpio/index.js deleted file mode 100644 index b555459..0000000 --- a/sh/test/ui2/backendgpio/index.js +++ /dev/null @@ -1,184 +0,0 @@ -require("dotenv").config(); - -const version = "cakeVendingBackendGPIO v1.0"; - -const log4js = require("log4js"); -log4js.configure({ - appenders: { - file: { - type: "dateFile", - filename: "log/cakeBackendGPIO.log", - maxLogSize: 1000000, // 1 MB - backups: 5, - category: "normal", - }, - out: { - type: "stdout", - }, - }, - categories: { - default: { appenders: ["file", "out"], level: "trace" }, - }, -}); -const logger = log4js.getLogger("cake"); - -const mqtt = require("mqtt"); -const gpio = require("rpi-gpio"); -const A4988 = require("./A4988"); - -//todo: idle的時候每五分鐘回抽 - -const mqttOpt = { - port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, - clientId: version, -}; - -const coinPinIdx = 7; //GPIO 4, pin 7 -const coinEnablePinIdx = 11; //GPIO 17, pin 11 -const kanbanEnablePinIdx = 13; //GPIO 27, pin 13 -const gateLimitPinIdx = 15; //GPIO 22, pin 15 - -const gateMotor = new A4988({ - step: 24, //GPIO 24, pin 18 - dir: 25, //GPIO 25, pin 22 - ms1: 15, //GPIO 15, pin 10 - ms2: 18, //GPIO 18, pin 12 - ms3: 23, //GPIO 23, pin 16 - enable: 14, //GPIO 14, pin 8 -}); -gateMotor.step_size = "sixteenth"; - -const gateOpen = -4000; -const gateClose = 4000; - -let coinCnt = 0; -let coinEnable = false; -let coinValue = false; -let coinLastValue = false; -const coinValueDebounceLimit = 0; -let coinValueDebounceCnt = 0; - -const gateLimitDebounceLimit = 5; -let gateLimitDebounceCnt = 0; -let gateLimitValue = false; -let gateLimitLastValue = false; - -const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); - -logger.info(version + " started"); - -mqttClient.on("message", function (topic, message) { - if (topic === "coin/cmd/enable") { - if (message.toString() === "true") { - coinEnable = true; - gpio.write(coinEnablePinIdx, true); - logger.trace("coin/cmd/enable true"); - } else { - coinCnt = 0; - coinEnable = false; - gpio.write(coinEnablePinIdx, false); - logger.trace("coin/cmd/enable false"); - } - } else if (topic === "kanban/cmd/enable") { - if (message.toString() === "true") { - gpio.write(kanbanEnablePinIdx, true); - logger.trace("kanban/cmd/enable true"); - } else { - gpio.write(kanbanEnablePinIdx, false); - logger.trace("kanban/cmd/enable false"); - } - } else if (topic === "gate/cmd/open") { - if (message.toString() === "true") { - gateMotor.enable().then( - gateMotor.turn(gateOpen).then((steps) => { - logger.trace(`gate turned ${steps} steps`); - gateMotor.disable(); - }) - ); - logger.trace("gate/cmd/open true"); - } else { - gateMotor.enable().then( - gateMotor.turn(gateClose).then((steps) => { - logger.trace(`gate turned ${steps} steps`); - gateMotor.disable(); - }) - ); - logger.trace("gate/cmd/open false"); - } - } else if (topic === "gate/cmd/stop") { - if (message.toString() === "true") { - gateMotor.stop(); - gateMotor.disable(); - logger.trace("gate/cmd/stop true"); - } - } -}); - -const mqttSubsTopis = ["coin/cmd/#", "kanban/cmd/#", "gate/cmd/#"]; - -mqttClient.on("connect", function () { - gateMotor.turn(gateClose); - logger.info("connect to broker OK"); - mqttSubsTopis.forEach(function (topic, index, array) { - mqttClient.subscribe(topic); - }); -}); - -gpio.on("change", function (channel, value) { - logger.trace("pin " + channel + " is " + value); - if (coinEnable) { - if (channel === coinPinIdx) { - if (value === true) { - if (coinValueDebounceCnt < coinValueDebounceLimit) { - coinValueDebounceCnt = coinValueDebounceCnt + 1; - } else { - coinValue = true; - coinValueDebounceCnt = 0; - } - } else { - coinValue = false; - coinValueDebounceCnt = 0; - } - //if (coinValue === true && coinLastValue === false) { - if (value === true) { - coinCnt = coinCnt + 1; - logger.trace(coinCnt); - mqttClient.publish("coin/status/inc", "1"); - if (coinCnt >= 5) { - coinEnable = false; - coinCnt = 0; - gpio.write(coinEnablePinIdx, false); - logger.trace("coin disable"); - } - } - coinLastValue = coinValue; - } - } - if (channel === gateLimitPinIdx) { - if (value === true) { - if (gateLimitDebounceCnt < gateLimitDebounceLimit) { - gateLimitDebounceCnt = gateLimitDebounceCnt + 1; - } else { - gateLimitValue = true; - gateLimitDebounceCnt = 0; - } - } else { - gateLimitValue = false; - gateLimitDebounceCnt = 0; - } - if (gateLimitValue === true && gateLimitLastValue === false) { - gateMotor.stop(); - logger.trace("gate stoped"); - } - gateLimitLastValue = gateLimitValue; - } -}); - -gpio.setup(coinEnablePinIdx, gpio.DIR_OUT, function (err) { - gpio.write(coinEnablePinIdx, false); -}); -gpio.setup(kanbanEnablePinIdx, gpio.DIR_OUT, function (err) { - gpio.write(kanbanEnablePinIdx, false); -}); -gpio.setup(coinPinIdx, gpio.DIR_IN, gpio.EDGE_RISING); -gpio.setup(gateLimitPinIdx, gpio.DIR_IN, gpio.EDGE_RISING); diff --git a/sh/ui2/backend/index.js b/sh/ui2/backend/index.js deleted file mode 100644 index 3874c3c..0000000 --- a/sh/ui2/backend/index.js +++ /dev/null @@ -1,330 +0,0 @@ -require("dotenv").config(); - -const version = "cakeVendingBackend v1.3"; - -const log4js = require("log4js"); -log4js.configure({ - appenders: { - file: { - type: "dateFile", - filename: "log/cakeBackend.log", - maxLogSize: 1000000, // 1 MB - backups: 5, - category: "normal", - }, - out: { - type: "stdout", - }, - }, - categories: { - default: { appenders: ["file", "out"], level: "trace" }, - }, -}); -const logger = log4js.getLogger("cake"); - -const express = require("express"); -const app = express(); -const http = require("http"); -const https = require("https"); -const cors = require("cors"); -const bodyParser = require("body-parser"); -const fs = require("fs"); -const exec = require("child_process").exec; -const mqtt = require("mqtt"); -const jwt = require("express-jwt"); -const sqlite3 = require("sqlite3").verbose(); -const os = require("os"); -const axios = require("axios"); - -const mqttOpt = { - port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, - clientId: version, -}; - -const httpsOptions = { - key: fs.readFileSync("./ssl_files/server_private_key.pem"), - ca: [fs.readFileSync("./ssl_files/cert.pem")], - cert: fs.readFileSync("./ssl_files/server_cert.pem"), -}; - -const checkModuleAliveInterval = 10 * 60 * 1000; //ms -const maxModuleDeadCnt = 0; -const maxMachTemp = 70; - -let bucketAliveMsg = "bucketAliveMsg"; -let lastBucketAliveMsg = "lastBucketAliveMsg"; -let bucketDeadCnt = 0; - -let ovenAliveMsg = "ovenAliveMsg"; -let lastOvenAliveMsg = "lastOvenAliveMsg"; -let ovenDeadCnt = 0; - -let robotAliveMsg = "robotAliveMsg"; -let lastRobotAliveMsg = "lastRobotAliveMsg"; -let robotDeadCnt = 0; - -let latchAliveMsg = "latchAliveMsg"; -let lastLatchAliveMsg = "lastLatchAliveMsg"; -let latchDeadCnt = 0; - -const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); - -const iNameList = os.networkInterfaces(); - -logger.info(version + " started"); - -let db = new sqlite3.Database("mydatebase.db", function (err) { - if (err) throw err; -}); - -// db.serialize(function () { -// //db.run 如果 Staff 資料表不存在,那就建立 Staff 資料表 -// db.run("CREATE TABLE IF NOT EXISTS Stuff (thing TEXT)"); -// let stmt = db.prepare("INSERT INTO Stuff VALUES (?)"); - -// //寫進10筆資料 -// for (var i = 0; i < 10; i++) { -// stmt.run("staff_number" + i); -// } - -// stmt.finalize(); - -// db.each("SELECT rowid AS id, thing FROM Stuff", function (err, row) { -// //log 出所有的資料 -// logger.trace(row.id + ": " + row.thing); -// }); -// }); - -db.close(); - -app.use(cors()); -app.use(bodyParser.urlencoded({ extended: false })); -app.use(bodyParser.text()); -app.use(log4js.connectLogger(logger, { level: "info" })); - -app.get( - "/version", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.send(version); - } -); - -app.post( - "/recipe/start/original", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - // exec("node /home/pi/recipe/original.js", function (err, stdout, stderr) { - exec("python /home/pi/recipe/test.py", function (err, stdout, stderr) { - if (err !== null) { - res.sendStatus(500); - logger.error(stderr); - } else { - res.sendStatus(200); - logger.trace(stdout); - } - }); - } -); - -app.get( - "/ad/playList", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - let readDir = fs.readdirSync("/home/pi/ad"); - res.send(readDir); - } -); - -app.post( - "/stop/oven/heating", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.sendStatus(200); - } -); - -app.post( - "/machine/alive", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - res.send(res.body); - } -); - -app.post( - "/coin/enable", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - mqttClient.publish("coin/cmd/enable", "true"); - res.sendStatus(200); - } -); - -app.post( - "/coin/disable", - jwt({ - subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - name: process.env.CAKE_ACCESS_TOKEN_NAME, - secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - }), - (req, res) => { - mqttClient.publish("coin/cmd/enable", "false"); - res.sendStatus(200); - } -); - -// http.createServer(app).listen(process.env.MACHINE_BACKEND_PORT, () => { -// logger.info( -// version + " listening on port " + process.env.MACHINE_BACKEND_PORT -// ); -// }); - -https - .createServer(httpsOptions, app) - .listen(process.env.MACHINE_BACKEND_PORT, () => { - logger.info( - version + " listening on port " + process.env.MACHINE_BACKEND_PORT - ); - }); - -// http -// .createServer(app) -// .listen(process.env.MACHINE_BACKEND_PORT, "localhost", () => { -// logger.info( -// "localhost " + -// version + -// " listening on port " + -// process.env.MACHINE_BACKEND_PORT -// ); -// }); - -// https -// .createServer(httpsOptions, app) -// .listen(process.env.MACHINE_BACKEND_PORT, iNameList.tun0[0].address, () => { -// logger.info( -// iNameList.tun0[0].address + -// " " + -// version + -// " listening on port " + -// process.env.MACHINE_BACKEND_PORT -// ); -// }); - -const postWebAPI = (url, payload) => { - axios({ - method: "post", - baseURL: process.env.TELEGRAM_BOT_IP + url, - headers: { - Authorization: "Bearer " + process.env.CAKE_ACCESS_TOKEN, - "content-type": "text/plain", - }, - data: payload, - }) - .then((res) => { - logger.trace("POST " + url + " " + payload + " " + res.status); - }) - .catch((err) => { - logger.error(err.message); - }); -}; - -mqttClient.on("message", function (topic, message) { - if (topic === "bucket/status/alive") { - bucketAliveMsg = message.toString(); - } else if (topic === "oven/status/alive") { - ovenAliveMsg = message.toString(); - } else if (topic === "robot/status/alive") { - robotAliveMsg = message.toString(); - } else if (topic === "latch/status/alive") { - latchAliveMsg = message.toString(); - } else if (topic === "bucket/status/machTemp") { - const macTempStr = message.toString(); - if (parseFloat(macTempStr) >= maxMachTemp) { - postWebAPI("/machine/alarm", "machine temperature: " + macTempStr); - } - } else if (topic === "bucket/status/alarm") { - postWebAPI("/machine/alarm", message.toString()); - } -}); - -const checkModuleAlive = () => { - if (bucketAliveMsg === lastBucketAliveMsg) { - bucketDeadCnt = bucketDeadCnt + 1; - } else { - bucketDeadCnt = 0; - } - lastBucketAliveMsg = bucketAliveMsg; - if (bucketDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "bucket is dead"); - } - - if (ovenAliveMsg === lastOvenAliveMsg) { - ovenDeadCnt = ovenDeadCnt + 1; - } else { - ovenDeadCnt = 0; - } - lastOvenAliveMsg = ovenAliveMsg; - if (ovenDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "oven is dead"); - } - - if (robotAliveMsg === lastRobotAliveMsg) { - robotDeadCnt = robotDeadCnt + 1; - } else { - robotDeadCnt = 0; - } - lastRobotAliveMsg = robotAliveMsg; - if (robotDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "robot is dead"); - } - - if (latchAliveMsg === lastLatchAliveMsg) { - latchDeadCnt = latchDeadCnt + 1; - } else { - latchDeadCnt = 0; - } - lastLatchAliveMsg = latchAliveMsg; - if (latchDeadCnt > maxModuleDeadCnt) { - postWebAPI("/machine/alarm", "latch is dead"); - } -}; -setInterval(checkModuleAlive, checkModuleAliveInterval); - -const mqttSubsTopis = [ - "bucket/status/#", - "oven/status/#", - "robot/status/#", - "latch/status/#", -]; - -mqttClient.on("connect", function () { - logger.info(version + " connect to broker OK"); - mqttSubsTopis.forEach(function (topic, index, array) { - mqttClient.subscribe(topic); - }); -}); diff --git a/sh/ui2/backend/package.json b/sh/ui2/backend/package.json deleted file mode 100644 index c5f2316..0000000 --- a/sh/ui2/backend/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "cake_vending_backend", - "version": "1.0.0", - "description": "the backend of cake vending machine", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", - "start": "node index.js", - "build": "webpack --config webpack.config.js" - }, - "author": "", - "license": "ISC", - "dependencies": { - "axios": "^0.19.0", - "body-parser": "^1.19.0", - "cors": "^2.8.5", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "express-jwt": "^5.3.1", - "log4js": "^6.1.2", - "mqtt": "^3.0.0", - "sqlite3": "^4.1.1" - }, - "devDependencies": { - "webpack": "^4.43.0", - "webpack-node-externals": "^1.7.2", - "file-loader": "^6.0.0", - "webpack-cli": "^3.3.11" - } -} diff --git a/sh/ui2/backendgpio/A4988.js b/sh/ui2/backendgpio/A4988.js deleted file mode 100644 index cba9e3a..0000000 --- a/sh/ui2/backendgpio/A4988.js +++ /dev/null @@ -1,178 +0,0 @@ -const Gpio = require("pigpio").Gpio; - -class A4988 { - constructor({ - step = 15, - dir = 14, - ms1 = 24, - ms2 = 23, - ms3 = 18, - enable = 25 - }) { - this._abort = false; - this._delay = 1; - this._direction = false; - this._steps = 0; - this._step_size = "FULL"; - this._enabled = true; - this._turning = false; - - this._step = new Gpio(step, { mode: Gpio.OUTPUT }); - this._dir = new Gpio(dir, { mode: Gpio.OUTPUT }); - this._step.digitalWrite(false); - this._dir.digitalWrite(false); - - if (ms1 && ms2 && ms3) { - this._ms1 = new Gpio(ms1, { mode: Gpio.OUTPUT }); - this._ms2 = new Gpio(ms2, { mode: Gpio.OUTPUT }); - this._ms3 = new Gpio(ms3, { mode: Gpio.OUTPUT }); - - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - } - - if (enable) { - this._enable = new Gpio(enable, { mode: Gpio.OUTPUT }); - this._enable.digitalWrite(false); - } - } - - get delay() { - return this._delay; - } - - set delay(d) { - if (typeof d != "number") throw `'delay' must be a number (${d})`; - if (d <= 0) throw `'delay' must be >= 0 (${d})`; - this._delay = d; - } - - get direction() { - return this._direction; - } - - set direction(d) { - if (typeof d != "boolean") throw `'direction' must be boolean (${d})`; - this._direction = d; - this._dir.digitalWrite(d); - } - - get step_size() { - return this._step_size; - } - - set step_size(ss) { - if (!this._ms1) return; - if (typeof ss != "string") throw `'step_size' must be a string (${ss})`; - switch (ss.toUpperCase()) { - case "FULL": - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - this._step_size = "FULL"; - break; - case "HALF": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(false); - this._ms3.digitalWrite(false); - this._step_size = "HALF"; - break; - case "QUARTER": - this._ms1.digitalWrite(false); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(false); - this._step_size = "QUARTER"; - break; - case "EIGHTH": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(false); - this._step_size = "EIGHTH"; - break; - case "SIXTEENTH": - this._ms1.digitalWrite(true); - this._ms2.digitalWrite(true); - this._ms3.digitalWrite(true); - this._step_size = "SIXTEENTH"; - break; - default: - break; - } - } - - get enabled() { - return this._enabled; - } - - set enabled(e) { - if (e) { - this._enable.digitalWrite(false); - this._enabled = true; - } else { - this._enable.digitalWrite(true); - this._enabled = false; - } - } - - get turning() { - return this._turning; - } - - enable() { - this._enable.digitalWrite(false); - this._enabled = true; - return new Promise(res => setTimeout(res, 5)); - } - - disable() { - this._enable.digitalWrite(true); - this._enabled = false; - return new Promise(res => setTimeout(res, 5)); - } - - _turn(steps, res) { - if (this._abort) { - this._turning = false; - res(this._steps); - return; - } - if (this._steps < steps) { - this._steps++; - } else { - this._steps--; - } - this._step.digitalWrite(true); - this._step.digitalWrite(false); - if (this._steps == steps) { - this._turning = false; - res(this._steps); - return; - } - setTimeout(() => this._turn(steps, res), this._delay); - } - - turn(steps = 1, callback) { - if (this._turning) - return Promise.reject(new Error("Motor already running")); - this._steps = 0; - this._abort = false; - this._turning = true; - if (this._steps < steps) { - this._dir.digitalWrite(true); - } else { - this._dir.digitalWrite(false); - } - if (typeof callback == "function") { - this._turn(steps, callback); - } else { - return new Promise(res => this._turn(steps, res)); - } - } - - stop() { - this._abort = true; - } -} - -module.exports = A4988; diff --git a/sh/ui2/backendgpio/index.js b/sh/ui2/backendgpio/index.js deleted file mode 100644 index b555459..0000000 --- a/sh/ui2/backendgpio/index.js +++ /dev/null @@ -1,184 +0,0 @@ -require("dotenv").config(); - -const version = "cakeVendingBackendGPIO v1.0"; - -const log4js = require("log4js"); -log4js.configure({ - appenders: { - file: { - type: "dateFile", - filename: "log/cakeBackendGPIO.log", - maxLogSize: 1000000, // 1 MB - backups: 5, - category: "normal", - }, - out: { - type: "stdout", - }, - }, - categories: { - default: { appenders: ["file", "out"], level: "trace" }, - }, -}); -const logger = log4js.getLogger("cake"); - -const mqtt = require("mqtt"); -const gpio = require("rpi-gpio"); -const A4988 = require("./A4988"); - -//todo: idle的時候每五分鐘回抽 - -const mqttOpt = { - port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, - clientId: version, -}; - -const coinPinIdx = 7; //GPIO 4, pin 7 -const coinEnablePinIdx = 11; //GPIO 17, pin 11 -const kanbanEnablePinIdx = 13; //GPIO 27, pin 13 -const gateLimitPinIdx = 15; //GPIO 22, pin 15 - -const gateMotor = new A4988({ - step: 24, //GPIO 24, pin 18 - dir: 25, //GPIO 25, pin 22 - ms1: 15, //GPIO 15, pin 10 - ms2: 18, //GPIO 18, pin 12 - ms3: 23, //GPIO 23, pin 16 - enable: 14, //GPIO 14, pin 8 -}); -gateMotor.step_size = "sixteenth"; - -const gateOpen = -4000; -const gateClose = 4000; - -let coinCnt = 0; -let coinEnable = false; -let coinValue = false; -let coinLastValue = false; -const coinValueDebounceLimit = 0; -let coinValueDebounceCnt = 0; - -const gateLimitDebounceLimit = 5; -let gateLimitDebounceCnt = 0; -let gateLimitValue = false; -let gateLimitLastValue = false; - -const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); - -logger.info(version + " started"); - -mqttClient.on("message", function (topic, message) { - if (topic === "coin/cmd/enable") { - if (message.toString() === "true") { - coinEnable = true; - gpio.write(coinEnablePinIdx, true); - logger.trace("coin/cmd/enable true"); - } else { - coinCnt = 0; - coinEnable = false; - gpio.write(coinEnablePinIdx, false); - logger.trace("coin/cmd/enable false"); - } - } else if (topic === "kanban/cmd/enable") { - if (message.toString() === "true") { - gpio.write(kanbanEnablePinIdx, true); - logger.trace("kanban/cmd/enable true"); - } else { - gpio.write(kanbanEnablePinIdx, false); - logger.trace("kanban/cmd/enable false"); - } - } else if (topic === "gate/cmd/open") { - if (message.toString() === "true") { - gateMotor.enable().then( - gateMotor.turn(gateOpen).then((steps) => { - logger.trace(`gate turned ${steps} steps`); - gateMotor.disable(); - }) - ); - logger.trace("gate/cmd/open true"); - } else { - gateMotor.enable().then( - gateMotor.turn(gateClose).then((steps) => { - logger.trace(`gate turned ${steps} steps`); - gateMotor.disable(); - }) - ); - logger.trace("gate/cmd/open false"); - } - } else if (topic === "gate/cmd/stop") { - if (message.toString() === "true") { - gateMotor.stop(); - gateMotor.disable(); - logger.trace("gate/cmd/stop true"); - } - } -}); - -const mqttSubsTopis = ["coin/cmd/#", "kanban/cmd/#", "gate/cmd/#"]; - -mqttClient.on("connect", function () { - gateMotor.turn(gateClose); - logger.info("connect to broker OK"); - mqttSubsTopis.forEach(function (topic, index, array) { - mqttClient.subscribe(topic); - }); -}); - -gpio.on("change", function (channel, value) { - logger.trace("pin " + channel + " is " + value); - if (coinEnable) { - if (channel === coinPinIdx) { - if (value === true) { - if (coinValueDebounceCnt < coinValueDebounceLimit) { - coinValueDebounceCnt = coinValueDebounceCnt + 1; - } else { - coinValue = true; - coinValueDebounceCnt = 0; - } - } else { - coinValue = false; - coinValueDebounceCnt = 0; - } - //if (coinValue === true && coinLastValue === false) { - if (value === true) { - coinCnt = coinCnt + 1; - logger.trace(coinCnt); - mqttClient.publish("coin/status/inc", "1"); - if (coinCnt >= 5) { - coinEnable = false; - coinCnt = 0; - gpio.write(coinEnablePinIdx, false); - logger.trace("coin disable"); - } - } - coinLastValue = coinValue; - } - } - if (channel === gateLimitPinIdx) { - if (value === true) { - if (gateLimitDebounceCnt < gateLimitDebounceLimit) { - gateLimitDebounceCnt = gateLimitDebounceCnt + 1; - } else { - gateLimitValue = true; - gateLimitDebounceCnt = 0; - } - } else { - gateLimitValue = false; - gateLimitDebounceCnt = 0; - } - if (gateLimitValue === true && gateLimitLastValue === false) { - gateMotor.stop(); - logger.trace("gate stoped"); - } - gateLimitLastValue = gateLimitValue; - } -}); - -gpio.setup(coinEnablePinIdx, gpio.DIR_OUT, function (err) { - gpio.write(coinEnablePinIdx, false); -}); -gpio.setup(kanbanEnablePinIdx, gpio.DIR_OUT, function (err) { - gpio.write(kanbanEnablePinIdx, false); -}); -gpio.setup(coinPinIdx, gpio.DIR_IN, gpio.EDGE_RISING); -gpio.setup(gateLimitPinIdx, gpio.DIR_IN, gpio.EDGE_RISING); diff --git a/sh/ui2/backendgpio/package.json b/sh/ui2/backendgpio/package.json deleted file mode 100644 index ecfda79..0000000 --- a/sh/ui2/backendgpio/package.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "backendgpio", - "version": "1.0.0", - "description": "the gpio backend of cake vending machine", - "main": "index.js", - "scripts": { - "test": "node test.js", - "start": "node index.js", - "build": "webpack" - }, - "author": "", - "license": "ISC", - "dependencies": { - "mqtt": "^3.0.0", - "rpi-gpio": "^2.1.5", - "dotenv": "^8.2.0", - "log4js": "^6.1.2", - "pigpio": "^3.1.1" - }, - "devDependencies": { - "webpack": "^4.43.0", - "webpack-cli": "^3.3.11" - } -} diff --git a/ui2/backend/index.js b/ui2/backend/index.js index 4e7b7c5..77db4d7 100644 --- a/ui2/backend/index.js +++ b/ui2/backend/index.js @@ -1,8 +1,4 @@ -require("dotenv").config({ path: "../.env" }); - -const version = "cakeVendingBackend v1.23"; -//不會跟telegram bot相連 -//腳本路徑是/home/pi/recipe/dummy.py +require("dotenv").config({ path: "../frontend/.env" }); const log4js = require("log4js"); log4js.configure({ @@ -40,28 +36,40 @@ const axios = require("axios"); const util = require("util"); const { stringify } = require("comment-json"); -const mqttOpt = { - port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, - clientId: version, -}; +//min to ms +const checkModuleAliveInterval = + process.env.CHECK_MODULE_ALIVE_INTERVAL * 60 * 1000; -const httpsOptions = { - key: fs.readFileSync("./ssl_files/server_private_key.pem"), - ca: [fs.readFileSync("./ssl_files/cert.pem")], - cert: fs.readFileSync("./ssl_files/server_cert.pem"), -}; +const maxModuleDeadCnt = process.env.MAX_MODULE_DEAD_CNT; + +//Celsius +const maxMachTemp = process.env.MAX_NACH_TEMP; + +//distance, greater means lower +const bowlCntWarningLevel = process.env.BOWL_CNT_WARNING_LEVEL; + +//distance, greater means lower +const bowlCntAlarmLevel = process.env.BOWL_CNT_ALARM_LEVEL; + +//distance, greater means lower +const batterVolWarningLevel = process.env.BATTER_VOL_WARNING_LEVEL; + +//distance, greater means lower +const batterVolAlarmLevel = process.env.BATTER_ALARM_WARNING_LEVEL; + +//Celsius +let maxFridgeTemp = process.env.MAX_FRIDGE_TEMP; + +//min to ms +const checkGateCmdDelay = process.env.CHECK_GATE_CMD_DELAY * 60 * 1000; + +//NTD +const unitPrice = process.env.UNIT_PRICE; + +//set to ms +const coinEnableDelayToResponse = + process.env.COIN_ENABLE_DELAY_TO_RESPONSE * 1000; -const checkModuleAliveInterval = 10 * 60 * 1000; //ms -const maxModuleDeadCnt = 0; -const maxMachTemp = 70; //Celsius -const bowlCntWarningLevel = 60; //distance, greater means lower -const bowlCntAlarmLevel = 64; //distance, greater means lower -const batterVolWarningLevel = 43; //distance, greater means lower -const batterVolAlarmLevel = 45; //distance, greater means lower -let maxFridgeTemp = 20; //Celsius -const checkGateCmdDelay = 10 * 60 * 1000; //ms -const unitPrice = 50; //NTD -const coinEnableDelayToResponse = 5000; const dbPath = "mydatebase.db"; let bucketAliveMsg = "bucketAliveMsg"; @@ -88,42 +96,53 @@ let macTempStr = "1"; let canPost = true; let checkGateCmdDelayObj; -const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); - -// const iNameList = os.networkInterfaces(); -// const tun0IP = iNameList.tun0[0].address; -const tun0IP = "192.168.1.99"; -// const localIP = "localhost"; -// const localIP = "172.27.240.59"; - const machineInfo = { name: process.env.LOCALNAME, - ver: version, - ip: tun0IP, + ver: process.env.REACT_APP_VERSION, + isDevMode: process.env.DEV_MODE === "true" ? true : false, + connect2Bot: process.env.CONNECT_2_BOT === "true" ? true : false, + ip: this.connect2Bot ? tun0IP : null, +}; + +if (machineInfo.connect2Bot) { + const iNameList = os.networkInterfaces(); + const tun0IP = iNameList.tun0[0].address; +} + +const mqttOpt = { + port: process.env.MACHINE_LOCAL_MQTT_BROKER_PORT, + clientId: machineInfo.ver, +}; + +const httpsOptions = { + key: fs.readFileSync("./ssl_files/server_private_key.pem"), + ca: [fs.readFileSync("./ssl_files/cert.pem")], + cert: fs.readFileSync("./ssl_files/server_cert.pem"), }; const postWebAPI = (url, payload) => { if (canPost) { - // axios({ - // method: "post", - // baseURL: - // process.env.TELEGRAM_BOT_IP + ":" + process.env.SERVER_PORT + url, - // // baseURL: "https://localhost:10010" + url, - // headers: { - // Authorization: "Bearer " + process.env.CAKE_ACCESS_TOKEN, - // "content-type": "text/plain", - // }, - // httpsAgent: new https.Agent({ - // rejectUnauthorized: false, - // }), - // data: payload, - // }) - // .then((res) => { - // logger.trace("POST " + url + " " + payload + " " + res.status); - // }) - // .catch((err) => { - // logger.error(err.message); - // }); + if (machineInfo.connect2Bot) { + axios({ + method: "post", + baseURL: + process.env.TELEGRAM_BOT_IP + ":" + process.env.SERVER_PORT + url, + headers: { + Authorization: "Bearer " + process.env.CAKE_ACCESS_TOKEN, + "content-type": "text/plain", + }, + httpsAgent: new https.Agent({ + rejectUnauthorized: false, + }), + data: payload, + }) + .then((res) => { + logger.trace("POST " + url + " " + payload + " " + res.status); + }) + .catch((err) => { + logger.error(err.message); + }); + } logger.trace("POST " + url + " " + payload); } }; @@ -132,9 +151,6 @@ const postWebAPI2 = (url, payload) => { return postWebAPI(url, process.env.LOCALNAME + " " + payload); }; -logger.info(version + " started"); -// postWebAPI("/machine/online", stringify(machineInfo)); - const today = new Date(); const tableName = "[" + @@ -186,13 +202,13 @@ app.use(log4js.connectLogger(logger, { level: "info" })); app.get( "/version", - // jwt({ - // subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, - // name: process.env.CAKE_ACCESS_TOKEN_NAME, - // secret: process.env.CAKE_ACCESS_TOKEN_SECRET, - // }), + jwt({ + subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, + name: process.env.CAKE_ACCESS_TOKEN_NAME, + secret: process.env.CAKE_ACCESS_TOKEN_SECRET, + }), (req, res) => { - res.send(version); + res.send(machineInfo.ver); } ); @@ -213,9 +229,7 @@ app.post( fridgeTempStr, macTempStr ); - // exec("node /home/pi/recipe/original.js", function (err, stdout, stderr) { exec("python /home/pi/recipe/dummy.py", function (err, stdout, stderr) { - // exec("python ../../recipe/py/test2.py", function (err, stdout, stderr) { if (err !== null) { res.status(500).send(stderr); logger.error(stderr); @@ -235,11 +249,8 @@ app.get( secret: process.env.CAKE_ACCESS_TOKEN_SECRET, }), (req, res) => { - // const root = "C:\\codes\\cakeVending\\ui2\\frontend\\public\\video"; const root = "/home/pi/ui2/frontend/build/video"; let ret = fs.readdirSync(root).map(function (file, index, array) { - // return { src: root + "/" + file, type: "video/mp4" }; - // return { src: ".\\video\\" + file, type: "video/mp4" }; return ".\\video\\" + file; }); res.send(ret); @@ -395,50 +406,52 @@ app.post( } ); -// http.createServer(app).listen(process.env.MACHINE_BACKEND_PORT, () => { -// logger.info( -// version + " listening on port " + process.env.MACHINE_BACKEND_PORT -// ); -// }); +app.get( + "/devMode", + jwt({ + subject: process.env.CAKE_ACCESS_TOKEN_SUBJECT, + name: process.env.CAKE_ACCESS_TOKEN_NAME, + secret: process.env.CAKE_ACCESS_TOKEN_SECRET, + }), + (req, res) => { + res.status(200).send(process.env.DEV_MODE); + } +); -// for test purpose so listening on all interface -// for production should change to http <--> localhost, https <--> tun0 +logger.info(stringify(machineInfo) + " started"); +postWebAPI("/machine/online", stringify(machineInfo)); -// https -// .createServer(httpsOptions, app) -// .listen(process.env.MACHINE_BACKEND_PORT, () => { -// logger.info( -// version + " listening on port " + process.env.MACHINE_BACKEND_PORT -// ); -// }); +const mqttClient = mqtt.connect("mqtt://localhost", mqttOpt); http .createServer(app) .listen(process.env.MACHINE_BACKEND_PORT, "localhost", () => { logger.info( - "localhost " + - version + - " listening on port " + + machineInfo.ver + + " listening on " + + "localhost:" + process.env.MACHINE_BACKEND_PORT ); }); -// https -// .createServer(httpsOptions, app) -// .listen(process.env.MACHINE_BACKEND_PORT, tun0IP, () => { -// logger.info( -// tun0IP + -// " " + -// version + -// " listening on port " + -// process.env.MACHINE_BACKEND_PORT -// ); -// }); +if (machineInfo.connect2Bot) { + https + .createServer(httpsOptions, app) + .listen(process.env.MACHINE_BACKEND_PORT, machineInfo.ip, () => { + logger.info( + stringify(machineInfo) + + " listening on port " + + process.env.MACHINE_BACKEND_PORT + ); + }); +} const postAlarm = (payload) => { logger.error(payload); postWebAPI2("/machine/alarm", payload); - //machineDisable(); + if (machineInfo.isDevMode === false) { + machineDisable(); + } }; const postWarning = (payload) => { @@ -557,7 +570,7 @@ const mqttSubsTopis = [ ]; mqttClient.on("connect", function () { - logger.info(version + " connect to broker OK"); + logger.info(machineInfo.ver + " connect to broker OK"); mqttSubsTopis.forEach(function (topic, index, array) { mqttClient.subscribe(topic); }); diff --git a/ui2/frontend/src/components/AboutPage/AboutPage.js b/ui2/frontend/src/components/AboutPage/AboutPage.js index 19d2443..bb7b8f3 100644 --- a/ui2/frontend/src/components/AboutPage/AboutPage.js +++ b/ui2/frontend/src/components/AboutPage/AboutPage.js @@ -6,6 +6,8 @@ import { Translate } from "react-redux-i18n"; import { withStyles } from "@material-ui/core/styles"; import Typography from "@material-ui/core/Typography"; +import "./aboutpage.css"; + import {} from "../../store/reducers/pageStatus"; const styles = (theme) => ({ @@ -26,10 +28,12 @@ class AboutPage extends Component { return (
- - {/* */} - {process.env.REACT_APP_VERSION} - +
+ + {/* */} + {process.env.REACT_APP_VERSION} + +
); } diff --git a/ui2/frontend/src/components/AboutPage/aboutpage.css b/ui2/frontend/src/components/AboutPage/aboutpage.css new file mode 100644 index 0000000..5652f35 --- /dev/null +++ b/ui2/frontend/src/components/AboutPage/aboutpage.css @@ -0,0 +1,10 @@ +.text { + margin: 10px; + border-radius: 10px; + /* background: #fff; */ + background-color: rgba(125, 125, 125, 0.3); + padding: 20px; + /* display: inline-block; */ + backdrop-filter: blur(10px); + -moz-backdrop-filter: blur(10px); +} diff --git a/ui2/frontend/src/components/MainPage/MainPage.js b/ui2/frontend/src/components/MainPage/MainPage.js index e99de15..e8d7ee0 100644 --- a/ui2/frontend/src/components/MainPage/MainPage.js +++ b/ui2/frontend/src/components/MainPage/MainPage.js @@ -24,6 +24,8 @@ import { import AboutPage from "../AboutPage"; import AutoCloseBtnDlg from "../AutoCloseBtnDlg"; +import logo from "../../imgs/logo.svg"; + import UIfx from "uifx"; import pop from "../../sounds/pop.flac"; import ratchet from "../../sounds/ratchet.wav"; @@ -34,6 +36,9 @@ const ratchetSfx = new UIfx(ratchet); const styles = (theme) => ({ root: { flexGrow: 1, + backgroundImage: `url(${logo})`, + backgroundPosition: "center", + backgroundRepeat: "no-repeat", }, tabs: { borderRight: `1px solid ${theme.palette.divider}`, @@ -107,16 +112,7 @@ class MainPage extends Component { )} - { - // this.props.setOriginalRecipeStart(); - // this.props.setADPageTitle("makingText"); - // this.props.coinValueDec(items[0].priceNum); - // this.props.setPageSelected("ad"); - // this.props.setMakingProgress(0); - // }} - /> + ({ root: { flexGrow: 1, + backgroundImage: `url(${logo})`, + backgroundPosition: "center", + backgroundRepeat: "no-repeat", }, box: { height: "90vh", diff --git a/ui2/frontend/src/components/RootPage/RootPage.js b/ui2/frontend/src/components/RootPage/RootPage.js index 677817a..711ff35 100644 --- a/ui2/frontend/src/components/RootPage/RootPage.js +++ b/ui2/frontend/src/components/RootPage/RootPage.js @@ -19,6 +19,7 @@ import { setPageSelected, setCheckoutDlgClose, getVideoPlayList, + getDevMode, } from "../../store/reducers/pageStatus"; import store from "../../store"; @@ -36,6 +37,7 @@ class RootPage extends Component { constructor(props) { super(props); this.props.getVideoPlayList(); + this.props.getDevMode(); this.idleTimer = null; this.onIdle = this.onIdle.bind(this); } @@ -131,6 +133,7 @@ const mapDispatchToProps = (dispatch) => { setPageSelected: (data) => setPageSelected(data), setCheckoutDlgClose: () => setCheckoutDlgClose(), getVideoPlayList: () => getVideoPlayList(), + getDevMode: () => getDevMode(), }, dispatch ); diff --git a/ui2/frontend/src/layouts/BNTa.svg b/ui2/frontend/src/imgs/logo.svg similarity index 100% rename from ui2/frontend/src/layouts/BNTa.svg rename to ui2/frontend/src/imgs/logo.svg diff --git a/ui2/frontend/src/imgs/logo_gray_45.svg b/ui2/frontend/src/imgs/logo_gray_45.svg new file mode 100644 index 0000000..cb05a19 --- /dev/null +++ b/ui2/frontend/src/imgs/logo_gray_45.svg @@ -0,0 +1,44 @@ + + + + background + + + + Layer 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui2/frontend/src/layouts/Home.js b/ui2/frontend/src/layouts/Home.js index 1f04868..a55ffa9 100644 --- a/ui2/frontend/src/layouts/Home.js +++ b/ui2/frontend/src/layouts/Home.js @@ -2,25 +2,12 @@ import React, { Fragment, Component } from "react"; import { connect } from "react-redux"; import { bindActionCreators } from "redux"; -// import logo from "../logo.svg"; -import logo from "./BNTa.svg"; - import { withStyles } from "@material-ui/core/styles"; const styles = (theme) => ({ root: { flexGrow: 1, backgroundColor: process.env.REACT_APP_LIGHT_YELLOW, - backgroundImage: `url(${logo})`, - // backgroundPosition: "60vh 20vh", - backgroundPosition: "center", - backgroundRepeat: "no-repeat", - // opacity: 0.5, - }, - content: { - // backgroundColor: "rgba(0, 0, 0, 0.3)", - // paddingTop: "5vh", - // paddingBottom: "15vh" }, }); @@ -29,11 +16,8 @@ class Home extends Component { const { classes, children } = this.props; return ( -
-
{children}
+
+
{children}
); diff --git a/ui2/frontend/src/store/reducers/pageStatus.js b/ui2/frontend/src/store/reducers/pageStatus.js index db48e34..7b070ec 100644 --- a/ui2/frontend/src/store/reducers/pageStatus.js +++ b/ui2/frontend/src/store/reducers/pageStatus.js @@ -18,6 +18,7 @@ const SET_CHECKOUTDLG_TITLE = "set/checkoutDlg/title"; const SET_RECIPE_PROGRESS_VISABLE = "set/recipe/progress/visable"; const GET_NEXT_VIDEO_URL = "get/next/video/url"; const SET_PRESS_TO_BAKE_DLG = "set/pressToBake"; +const GET_DEV_MODE = "get/devMode"; const backend = "http://localhost:8081"; @@ -37,11 +38,16 @@ const initState = { checkoutDlgTitle: "plsInsertCoin", showRecipeProgress: false, pressToBakeDlgOpen: false, + isDevMode: false, }; -function checkOvenIsReady(tempature) { - const parsed = parseInt(tempature, 10); - return parsed >= process.env.REACT_APP_OVEN_GOOD_TEMPERATURE ? true : false; +function checkOvenIsReady(isDevMode, tempature) { + if (isDevMode) { + return true; + } else { + const parsed = parseInt(tempature, 10); + return parsed >= process.env.REACT_APP_OVEN_GOOD_TEMPERATURE ? true : false; + } } function decTheCoinValue(coinValue, data) { @@ -136,7 +142,7 @@ export default function reducer(state = initState, action) { case "oven/status/temperature": return { ...state, - ovenIsReady: checkOvenIsReady(action.payload), + ovenIsReady: checkOvenIsReady(state.isDevMode, action.payload), }; case "oven/cmd/temperature": return { @@ -201,6 +207,11 @@ export default function reducer(state = initState, action) { ...state, pressToBakeDlgOpen: action.payload, }; + case GET_DEV_MODE: + return { + ...state, + isDevMode: action.payload, + }; } } @@ -317,3 +328,24 @@ export function setPressToBakeDlgClose() { payload: false, }; } + +export function getDevMode() { + return (dispatch) => { + axios({ + method: "get", + baseURL: backend + "/devMode", + headers: { + Authorization: "Bearer " + process.env.REACT_APP_CAKE_ACCESS_TOKEN, + }, + }) + .then((res) => { + dispatch({ + type: GET_DEV_MODE, + payload: res.data, + }); + }) + .catch((err) => { + console.log(err.message); + }); + }; +}