@zigbang/honeyfarm-node
Version:
## Raspberry Pi HoneyFarm Node ### Setup && Run
536 lines (535 loc) • 28.8 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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.node = void 0;
var shelljs_1 = __importDefault(require("shelljs"));
var child_process_1 = __importDefault(require("child_process"));
var commander_1 = __importDefault(require("commander"));
var logger_1 = __importDefault(require("./logger"));
var api_1 = __importDefault(require("./api"));
var bms_1 = __importDefault(require("./bms"));
var types_1 = require("./util/types");
var upnp_1 = __importDefault(require("./upnp"));
var node = /** @class */ (function () {
function node() {
this.resources = {};
this.maxDevices = 16;
this.portMap = {};
this.isMacMachine = false;
this.appiumDefaultPort = 4724;
this.useUPnP = false;
this.endpoint = "http://localhost:4723";
this.wdaDefaultPort = 8101;
this.mjpegServerDefaultPort = 9101;
this.iosDevice = undefined;
this.CHECK_BATTERY_TERM = 5 * 60 * 1000; // 5min
}
node.prototype.run = function () {
return __awaiter(this, void 0, void 0, function () {
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
shelljs_1.default.config.silent = true;
commander_1.default
.option('--endpoint <endpoint>', 'Setting endpoint')
.option('--appiumBeginPort <appiumBeginPort>', 'Setting appium Begin port')
.option('--upnp', 'Enable upnp mode')
.action(function () {
_this.endpoint = commander_1.default.endpoint || _this.endpoint;
_this.appiumDefaultPort = Number.parseInt(commander_1.default.appiumBeginPort) || _this.appiumDefaultPort;
_this.useUPnP = commander_1.default.upnp || _this.useUPnP;
})
.parse(process.argv);
this.api = new api_1.default(this.endpoint);
this.bms = new bms_1.default();
return [4 /*yield*/, this.init()];
case 1:
_a.sent();
return [4 /*yield*/, this.updateDeviceStatus()];
case 2:
_a.sent();
this.checkBatteryStatus();
setInterval(function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
this.checkBatteryStatus();
this.updateBmsTimerStates();
return [2 /*return*/];
});
}); }, this.CHECK_BATTERY_TERM);
return [2 /*return*/];
}
});
});
};
node.prototype.checkBatteryStatus = function () {
return __awaiter(this, void 0, void 0, function () {
var rule;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.api.getBatteryChargeRule()];
case 1:
rule = _a.sent();
if (rule && true === rule.hasOwnProperty("threshold")) {
this.bms.setBatteryChargeLevelRange(rule.threshold.min, rule.threshold.max);
}
else {
logger_1.default.warn("Invalid battery threshold value: ".concat(rule));
}
this.bms.checkBatteryStatus(this.resources);
return [2 /*return*/];
}
});
});
};
node.prototype.init = function () {
return __awaiter(this, void 0, void 0, function () {
var i, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
for (i = 0; i < this.maxDevices; i++) {
this.portMap[this.appiumDefaultPort + i] = "FREE";
}
if (!(process.platform === "darwin")) return [3 /*break*/, 2];
this.isMacMachine = true;
_a = this;
return [4 /*yield*/, this.getIosDeviceList()];
case 1:
_a.iosDevice = _b.sent();
_b.label = 2;
case 2: return [2 /*return*/];
}
});
});
};
node.prototype.getIosDeviceList = function () {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, new Promise(function (resolve, reject) {
var nid = require("node-ios-device");
nid.devices(function (err, devices) {
if (err) {
reject(err);
}
else {
resolve(devices);
}
});
})];
});
});
};
node.prototype.updateDeviceStatus = function () {
return __awaiter(this, void 0, void 0, function () {
var onlineSerials, unregisteredDevices;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.useUPnP) return [3 /*break*/, 3];
return [4 /*yield*/, upnp_1.default.getExternalIp()];
case 1:
_a.sent();
return [4 /*yield*/, upnp_1.default.getMappedPort()];
case 2:
_a.sent();
_a.label = 3;
case 3: return [4 /*yield*/, this.getOnlineSerials()];
case 4:
onlineSerials = _a.sent();
logger_1.default.info("Discovered Devices: ".concat(JSON.stringify(onlineSerials)));
// 현재 등록된 디바이스 중 연결되지 않는 디바이스 제거
return [4 /*yield*/, Promise.all(Object.keys(this.resources)
.filter(function (serial) { return !Object.keys(onlineSerials).includes(serial); })
.map(function (serial) { return __awaiter(_this, void 0, void 0, function () { return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.removeDevice(serial)];
case 1:
_a.sent();
return [2 /*return*/];
}
}); }); }))
// 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우
];
case 5:
// 현재 등록된 디바이스 중 연결되지 않는 디바이스 제거
_a.sent();
// 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우
return [4 /*yield*/, Promise.all(Object.entries(onlineSerials).map(function (_a) {
var key = _a[0], value = _a[1];
return __awaiter(_this, void 0, void 0, function () {
var resource, checkAppiumServer;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!!this.resources[key]) return [3 /*break*/, 2];
return [4 /*yield*/, this.addDeviceToLocal(key, value)];
case 1:
_b.sent();
return [3 /*break*/, 3];
case 2:
resource = this.resources[key];
checkAppiumServer = shelljs_1.default.exec("lsof -i :".concat(resource.port, " -t")).stdout;
if (!checkAppiumServer)
this.startAppiumServer(key, resource.port, resource.wdaPort);
_b.label = 3;
case 3:
if (value === "android") {
this.updateBatteryLevel(key);
}
return [2 /*return*/];
}
});
});
}))
// 클라이언트에 등록된 디바이스 중 서버에 등록되지 않은 경우
];
case 6:
// 현재 연결된 디바이스 중 클라이언트에 등록되지 않았을 경우
_a.sent();
return [4 /*yield*/, Promise.all(Object.entries(this.resources).map(function (_a) {
var serial = _a[0], device = _a[1];
return __awaiter(_this, void 0, void 0, function () {
var exist;
return __generator(this, function (_b) {
switch (_b.label) {
case 0: return [4 /*yield*/, this.api.getDevice(device.port, device.platform, device.version, serial)];
case 1:
exist = _b.sent();
if (!exist) {
return [2 /*return*/, serial];
}
else {
return [2 /*return*/, undefined];
}
return [2 /*return*/];
}
});
});
}))];
case 7:
unregisteredDevices = _a.sent();
return [4 /*yield*/, Promise.all(unregisteredDevices.map(function (serial) { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!serial) return [3 /*break*/, 2];
return [4 /*yield*/, this.addDeviceToServer(serial)];
case 1:
_a.sent();
_a.label = 2;
case 2: return [2 /*return*/];
}
});
}); }))];
case 8:
_a.sent();
setTimeout(function () { return __awaiter(_this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.updateDeviceStatus()];
case 1:
_a.sent();
return [2 /*return*/];
}
});
}); }, 10000);
return [2 /*return*/];
}
});
});
};
node.prototype.updateBmsTimerStates = function () {
return __awaiter(this, void 0, void 0, function () {
var rst;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.api.postBmsTimerState(this.bms.timer.get_enabled_timers_())];
case 1:
rst = _a.sent();
if (rst && 'ok' === rst.result_code) {
//success
// Logger.info(`BMS timer status is updated.`)
}
else {
logger_1.default.warn("Fail to update BMS timer status: ".concat(JSON.stringify(rst)));
}
return [2 /*return*/];
}
});
});
};
node.prototype.updateBatteryLevel = function (udid) {
return __awaiter(this, void 0, void 0, function () {
var remaining, batteryLevel, vals, port;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
remaining = -1;
batteryLevel = shelljs_1.default.exec("adb -s ".concat(udid, " shell dumpsys battery | grep level")).stdout;
if (batteryLevel.includes("level")) {
vals = batteryLevel.split(":");
if (vals.length === 2) {
remaining = parseInt(vals[1]);
}
}
this.resources[udid].batteryLevel = remaining;
port = this.resources[udid].port;
console.log("batteryLevel : ", this.resources[udid]);
return [4 /*yield*/, this.api.updateDeviceStatus(port, {
batteryLevel: remaining
})];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
};
node.prototype.getOnlineSerials = function () {
return __awaiter(this, void 0, void 0, function () {
var serials, _a, devices, onlineDevices;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
serials = {};
if (!this.isMacMachine) return [3 /*break*/, 2];
_a = this;
return [4 /*yield*/, this.getIosDeviceList()];
case 1:
_a.iosDevice = _b.sent();
this.iosDevice.map(function (device) { serials[device.udid] = "ios"; });
this.getOnlineSimulator().map(function (device) { serials[device.udid] = "ios"; });
_b.label = 2;
case 2:
devices = shelljs_1.default.exec("adb devices | grep -v devices").stdout;
onlineDevices = devices.match(/(\w+-?\w+?)\tdevice$/gm) || [];
onlineDevices.map(function (device) { serials[device.replace("\tdevice", "")] = "android"; });
return [2 /*return*/, serials];
}
});
});
};
node.prototype.getOnlineSimulator = function () {
var result = [];
var data = shelljs_1.default.exec("xcrun simctl list devices --json");
var deviceList = JSON.parse(data.toString());
for (var _i = 0, _a = Object.entries(deviceList.devices); _i < _a.length; _i++) {
var _b = _a[_i], version = _b[0], devices = _b[1];
if (!version.includes("iOS")) {
continue;
}
for (var _c = 0, _d = devices; _c < _d.length; _c++) {
var device = _d[_c];
if (device.state === "Booted") {
var versionRex = version === null || version === void 0 ? void 0 : version.match(/\w+-\d+-\d+/g);
result.push({
udid: device.udid,
name: device.name,
productVersion: versionRex ? versionRex[0].replace("iOS-", "").replace("-", ".") : "",
type: "simulator"
});
}
}
}
return result;
};
node.prototype.addDeviceToLocal = function (serial, platform) {
var _a;
return __awaiter(this, void 0, void 0, function () {
var _b, result, port, _, wdaPort, mjpegServerPort, devicelist, deviceInfo, name_1, platformVersion, webviewVersion;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!(platform === "ios")) return [3 /*break*/, 2];
_b = this;
return [4 /*yield*/, this.getIosDeviceList()];
case 1:
_b.iosDevice = _c.sent();
_c.label = 2;
case 2:
result = Object.entries(this.portMap).find(function (_a) {
var port = _a[0], status = _a[1];
return status === "FREE";
});
if (!result) {
logger_1.default.error("All Ports Already in USED");
return [2 /*return*/];
}
port = result[0], _ = result[1];
wdaPort = this.wdaDefaultPort + Number(port) - this.appiumDefaultPort;
mjpegServerPort = this.mjpegServerDefaultPort + Number(port) - this.appiumDefaultPort;
try {
this.startAppiumServer(serial, port, wdaPort.toString());
if (platform === "ios") {
devicelist = this.iosDevice.concat(this.getOnlineSimulator());
deviceInfo = devicelist.filter(function (device) { return device.udid === serial; });
name_1 = ((_a = deviceInfo[0]) === null || _a === void 0 ? void 0 : _a.productType) ? types_1.iPhone_TYPE[deviceInfo[0].productType] : deviceInfo[0].name;
this.resources[serial] = {
platform: platform,
version: deviceInfo[0].productVersion,
port: port,
name: name_1,
wdaPort: "".concat(wdaPort),
mjpegServerPort: "".concat(mjpegServerPort),
type: deviceInfo[0].type
};
}
else {
platformVersion = shelljs_1.default.exec("adb -s ".concat(serial, " shell getprop ro.build.version.release")).stdout.replace("\n", "").trim();
webviewVersion = shelljs_1.default.exec("adb -s ".concat(serial, " shell dumpsys package com.android.chrome | grep versionName"))
.stdout.split("\n")[0]
.replace("versionName=", "")
.match(/\d+\.\d+\.\d+/)[0];
this.resources[serial] = {
platform: platform,
version: platformVersion,
port: port,
webviewVersion: webviewVersion
};
}
this.portMap[port] = "USED";
logger_1.default.info("Add Device(Local) ".concat(serial, " to Port ").concat(port));
}
catch (e) {
logger_1.default.error("Error while Add Device to Local. Removing ".concat(serial));
logger_1.default.error(e);
this.removeDevice(serial);
}
return [2 /*return*/];
}
});
});
};
node.prototype.startAppiumServer = function (serial, port, wdaPort) {
return __awaiter(this, void 0, void 0, function () {
var appiumOptions, appium, e_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 3, , 4]);
logger_1.default.info("Starting Appium Server with Port:".concat(port));
appiumOptions = ["-p", "".concat(port), "--webdriveragent-port", "".concat(wdaPort), "--relaxed-security"];
appium = child_process_1.default.spawn("appium", appiumOptions, { detached: true });
appium.stdout.on("data", function (data) {
logger_1.default.info(data);
});
if (!this.useUPnP) return [3 /*break*/, 2];
return [4 /*yield*/, upnp_1.default.addPortMapping(Number(port), 60 * 60 * 24 * 30)];
case 1:
_a.sent();
_a.label = 2;
case 2: return [3 /*break*/, 4];
case 3:
e_1 = _a.sent();
logger_1.default.error("Error while Add Device to Local. Removing ".concat(serial));
logger_1.default.error(e_1);
this.removeDevice(serial);
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
};
node.prototype.addDeviceToServer = function (serial) {
return __awaiter(this, void 0, void 0, function () {
var device, e_2;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
device = this.resources[serial];
if (!device) {
logger_1.default.error("Trying to Add Device that are not registerd to local ".concat(serial));
return [2 /*return*/];
}
_a.label = 1;
case 1:
_a.trys.push([1, 3, , 4]);
logger_1.default.info("Adding Device(Server) ".concat(serial, " to Port ").concat(device.port));
return [4 /*yield*/, this.api.registerDeviceStatus(device.port, device.platform, device.version, serial, device.name, device.wdaPort, device.mjpegServerPort, device.type, device.batteryLevel)];
case 2:
_a.sent();
logger_1.default.info("Added Device(Server) ".concat(serial, " to Port ").concat(device.port));
return [3 /*break*/, 4];
case 3:
e_2 = _a.sent();
logger_1.default.error("Error while Add Device to Server");
logger_1.default.error(e_2);
return [3 /*break*/, 4];
case 4: return [2 /*return*/];
}
});
});
};
node.prototype.removeDevice = function (serial) {
return __awaiter(this, void 0, void 0, function () {
var port, appiumPID, e_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
port = this.resources[serial].port;
logger_1.default.warn("Remove Device: ".concat(serial, " from port: ").concat(port));
return [4 /*yield*/, this.api.deregisterDeviceStatus(port)];
case 1:
_a.sent();
delete this.resources[serial];
this.portMap[port] = "FREE";
appiumPID = shelljs_1.default.exec("lsof -n -i4TCP:".concat(port, " | grep node | awk '{print $2}'"));
child_process_1.default.exec("kill -9 ".concat(appiumPID));
if (this.useUPnP) {
upnp_1.default.removePortMapping(Number(port));
}
return [3 /*break*/, 3];
case 2:
e_3 = _a.sent();
logger_1.default.error(e_3);
return [3 /*break*/, 3];
case 3: return [2 /*return*/];
}
});
});
};
return node;
}());
exports.node = node;
new node().run();