@zigbang/honeyfarm-node
Version:
## Raspberry Pi HoneyFarm Node ### Setup && Run
350 lines (349 loc) • 18.6 kB
JavaScript
"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;