UNPKG

@zigbang/honeyfarm-node

Version:

## Raspberry Pi HoneyFarm Node ### Setup && Run

350 lines (349 loc) 18.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var logger_1 = __importDefault(require("./logger")); var types_1 = require("./util/types"); var bms_1 = __importDefault(require("./bms")); var STATE; (function (STATE) { STATE["IDLE"] = "IDLE"; STATE["RUNNING"] = "RUNNING"; STATE["STOP"] = "STOP"; STATE["TERMINATE"] = "TERMINATE"; STATE["WAIT"] = "WAIT"; STATE["ERROR"] = "ERROR"; STATE["SHUTTING_DOWN"] = "SHUTTING_DOWN"; })(STATE || (STATE = {})); var Timer = /** @class */ (function () { function Timer(device_groups) { var _this = this; this.flag_timers = false; this.enabled_timers_ = {}; this.device_groups_ = []; this.device_groups_buffer = []; this.CLOCK_TERM = 15 * 60 * 1000; // 15m this.DEFAULT_CHARGE_TERM = 120 * 60 * 1000; // 120m, 2h this.ERROR_WAIT_TERM = 30 * 60 * 1000; // 30m this.set_device_groups_(device_groups); this.clockTimers(); var time_handle_ = setInterval(function () { _this.clockTimers(); }, this.CLOCK_TERM); } Timer.prototype.findDeviceGroup = function (groupName) { var filteredList = this.get_device_groups_().filter(function (dg) { return dg.name === groupName; }); return filteredList.length < 1 ? null : filteredList[0]; }; Timer.prototype.getTimerTerm = function (groupName) { var dg = this.findDeviceGroup(groupName); if (null === dg || false === dg.hasOwnProperty("mode")) { logger_1.default.warn("getTimerTerm(): mode is undefined in device_group.json. timer for ".concat(groupName, " would be end.")); return null; } else if (types_1.MODE_NAME.TIMER !== dg.mode.name) { logger_1.default.warn("getTimerTerm(): mode.name is not \"timer\" in device_group.json. timer for ".concat(groupName, " would be end.")); return null; } else if (false === dg.mode.hasOwnProperty("option")) { logger_1.default.warn("getTimerTerm(): mode.option is undefined. Instead, default term:".concat(this.DEFAULT_CHARGE_TERM, "(s) would be used.")); return this.DEFAULT_CHARGE_TERM; } else if (false === dg.mode.option.hasOwnProperty("timer_term")) { logger_1.default.warn("getTimerTerm(): mode.option.timer_term is undefined. Instead, default term:".concat(this.DEFAULT_CHARGE_TERM, "(s) would be used.")); return this.DEFAULT_CHARGE_TERM; } else if (dg.mode.option.hasOwnProperty("timer_term")) { var rawTerm = dg.mode.option.timer_term; if (Number.isSafeInteger(rawTerm) && rawTerm >= 1 && rawTerm <= 10) { //Logger.info(`timer.ts - getTimerTerm(): Timer term is set as ${rawTerm}(min) for "${groupName}".`) return rawTerm * 60 * 60 * 1000; //(ms -> hour) } else { logger_1.default.warn("getTimerTerm(): ".concat(rawTerm, " is not safe integer(Float, too large or zero). Instead, default term:").concat(this.DEFAULT_CHARGE_TERM, "(s) would be used for \"").concat(groupName, "\".")); return this.DEFAULT_CHARGE_TERM; } } else { logger_1.default.warn("getTimerTerm(): Invalid action - stop the timer for ".concat(groupName)); return null; } }; Timer.prototype.watchEnableTimers = function (groups) { var _this = this; if (groups) { this.set_device_groups_(groups); } this.get_device_groups_().forEach(function (group) { if (false === group.hasOwnProperty("name")) { return; } else if (!group.hasOwnProperty("mode") || !group.mode.hasOwnProperty("name") || group.mode.name !== "timer") { //timer 있으면 -> 제거 / 없으면 -> don`t care if (true === _this.enabled_timers_.hasOwnProperty(group.name)) { //stop -> terminate loop 태우기. var timer = _this.enabled_timers_[group.name]; if (false === [STATE.TERMINATE, STATE.SHUTTING_DOWN, undefined].includes(timer.state)) { timer.state = STATE.STOP; } } return; } else if ("timer" === group.mode.name) { //enabled_timers_에 없으면 -> 초기화 / 있으면 -> don`t care if (false === _this.enabled_timers_.hasOwnProperty(group.name)) { _this.enabled_timers_[group.name] = { "state": STATE.IDLE }; } } }); if (0 < Object.keys(this.enabled_timers_).length) { this.flag_timers = true; } else { //None of controllers needs a timer. Put down the flag. this.flag_timers = false; } }; Timer.prototype.clockTimers = function () { var _this = this; if (false === this.flag_timers) { return; } var timerEnabledGroupNames = Object.keys(this.enabled_timers_); timerEnabledGroupNames.forEach(function (gn) { var deviceGroup = _this.findDeviceGroup(gn); if (null === deviceGroup) { logger_1.default.error("clockTimers() - error: device group \"".concat(gn, "\" is not found.")); delete _this.enabled_timers_[gn]; return; } var state = _this.enabled_timers_[gn].state; if (state === undefined) { logger_1.default.error("clockTimers() - error: State of timer for ".concat(gn, " is undefined, so this timer would be removed from enabled_timers_")); delete _this.enabled_timers_[gn]; return; } logger_1.default.info("clockTimers() - timer \"".concat(gn, "\": ").concat(state)); if (STATE.RUNNING === state || STATE.WAIT === state || STATE.SHUTTING_DOWN === state) { // don`t add any task while running, waiting for error resolving } else if (STATE.IDLE === state) { // run timer _this.runTimer(deviceGroup); } else if (STATE.ERROR === state) { _this.handleErrorTimer(deviceGroup); } else if (STATE.STOP === state) { //start to clear clocks _this.stopTimer(gn); } else if (STATE.TERMINATE === state) { // start terminating //remove this groupname from enabled_timers_ _this.terminateTimer(deviceGroup); } }); }; Timer.prototype.runTimer = function (group) { return __awaiter(this, void 0, void 0, function () { var term, status, timerInfo, res; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: timerInfo = this.enabled_timers_[group.name]; if (!timerInfo || !timerInfo.state) { logger_1.default.error("Timer Initialization Error: Timer for ".concat(group.name, " is undefined. This timer will be put into error cycle.\n")); timerInfo = { "state": STATE.ERROR }; return [2 /*return*/]; } timerInfo.state = STATE.WAIT; return [4 /*yield*/, bms_1.default.operateBatteryCommand(group.controller_endpoint, types_1.BMS_CMD.POWER_STATUS)]; case 1: res = _a.sent(); if (res && res.data && res.data.hasOwnProperty("POWER")) { status = res.data.POWER; term = this.getTimerTerm(group.name); if (null === term && false === [STATE.STOP, STATE.TERMINATE, STATE.SHUTTING_DOWN].includes(timerInfo.state)) { logger_1.default.error("runTimer() - Timer mode or option arguments are missing. This timer(".concat(group.name, ") will be stopped.\n")); timerInfo.state = STATE.STOP; return [2 /*return*/]; } //'OFF' === status ? this.DISCHARGE_TERM : this.CHARGE_TERM } else { if (false === [STATE.STOP, STATE.TERMINATE, STATE.SHUTTING_DOWN].includes(timerInfo.state)) { logger_1.default.error("runTimer() - Timer error during checking the power status for ".concat(group.name, ". This timer will be put into error cycle.\n")); timerInfo.state = STATE.ERROR; } return [2 /*return*/]; } timerInfo.timeout = setTimeout(function () { return __awaiter(_this, void 0, void 0, function () { var res; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, bms_1.default.operateBatteryCommand(group.controller_endpoint, types_1.BMS_CMD.POWER_TOGGLE)]; case 1: res = _a.sent(); if (res && res.data && res.data.hasOwnProperty("POWER")) { this.idleTimer(group.name); timerInfo.state = STATE.IDLE; } else { if (false === [STATE.STOP, STATE.TERMINATE, STATE.SHUTTING_DOWN].includes(timerInfo.state)) { logger_1.default.error("runTimer() - Timer error during power toggling for ".concat(group.name, ". This timer will be put into error cycle.\n")); timerInfo.state = STATE.ERROR; } } return [2 /*return*/]; } }); }); }, term); logger_1.default.info("runTimer() - Timer for ".concat(group.name, " is set up.(").concat(timerInfo.timeout, ")\n")); timerInfo.state = STATE.RUNNING; return [2 /*return*/]; } }); }); }; Timer.prototype.idleTimer = function (groupName) { var timer = this.enabled_timers_[groupName]; if (undefined === timer) { logger_1.default.info("idleTimer() - The timer for ".concat(groupName, " have already deleted from the list \"enabled_timers_\".")); return; } clearTimeout(timer.timeout); delete this.enabled_timers_[groupName].timeout; return; }; Timer.prototype.stopTimer = function (groupName) { var timerInfo = this.enabled_timers_[groupName]; if (undefined === timerInfo) { logger_1.default.info("stopTimer() - The timer for ".concat(groupName, " is undefined in the object \"enabled_timers_\".")); return; } logger_1.default.info("stopTimer() - STOPPING: ".concat(timerInfo.timeout)); clearTimeout(timerInfo.timeout); delete timerInfo.timeout; timerInfo.state = STATE.TERMINATE; return; }; Timer.prototype.terminateTimer = function (group) { return __awaiter(this, void 0, void 0, function () { var gn, timerInfo, res; return __generator(this, function (_a) { switch (_a.label) { case 0: gn = group.name; timerInfo = this.enabled_timers_[gn]; timerInfo.state = STATE.SHUTTING_DOWN; return [4 /*yield*/, bms_1.default.operateBatteryCommand(group.controller_endpoint, types_1.BMS_CMD.POWER_ON)]; case 1: res = _a.sent(); if (res && res.data && res.data.hasOwnProperty("POWER")) { logger_1.default.info("terminateTimer() - Successfully turned power on before timer for ".concat(gn, " terminated\n")); delete this.enabled_timers_[gn]; } else { logger_1.default.error("terminateTimer() - Failed to turn power on for \"".concat(gn, "\". It may need to be turned on manually, or the devices can be discharged. \n")); // anyway, terminate this timer without any state change. delete this.enabled_timers_[gn]; } return [2 /*return*/]; } }); }); }; Timer.prototype.handleErrorTimer = function (group) { var _this = this; //에러가 발생한 controller는 일정 clock 동안 bms 관련 어떠한 동작도 시키지 않는다. //그렇다고 아예 대상에서 제거하면 단순 에러가 발생할 때마다 허니팜 노드를 재실행해야 하므로, ERROR_WAIT_TERM이 지나면 재동작의 기회를 부여한다. var gn = group.name; var timerInfo = this.enabled_timers_[gn]; timerInfo.state = STATE.WAIT; logger_1.default.warn("handleErrorTimer() - After ".concat(this.ERROR_WAIT_TERM, ", timer for \"").concat(gn, "\" controller would be started. \n")); if (timerInfo.timeout) { clearTimeout(timerInfo.timeout); delete timerInfo.timeout; } timerInfo.timeout = setTimeout(function () { logger_1.default.warn("handleErrorTimer() - Error wait time is done, and \"".concat(gn, "\" controller would be started. \n")); clearTimeout(timerInfo.timeout); delete timerInfo.timeout; _this.enabled_timers_[gn].state = STATE.IDLE; }, this.ERROR_WAIT_TERM); }; Timer.prototype.set_device_groups_ = function (groups) { this.device_groups_ = __spreadArray([], groups, true); }; Timer.prototype.get_device_groups_ = function () { this.device_groups_buffer = __spreadArray([], this.device_groups_, true); return this.device_groups_buffer; }; Timer.prototype.get_enabled_timers_ = function () { var _this = this; var rst = []; Object.keys(this.enabled_timers_).forEach(function (groupName) { var dg = _this.findDeviceGroup(groupName); rst.push({ controller_name: groupName, state: _this.enabled_timers_[groupName].state, timeout: _this.enabled_timers_[groupName].timeout ? { _idleTimeout: _this.enabled_timers_[groupName].timeout._idleTimeout, _idleStart: _this.enabled_timers_[groupName].timeout._idleStart } : null, }); }); return rst; }; return Timer; }()); exports.default = Timer;