obcon-scada / app /include /Application.js
chanmin0723
HF: checkAlive 비활성화 (스냅샷 데모 - 실장치 통신 없음으로 인한 전체 통신이상 오염 방지)
9f40753
Raw
History Blame Contribute Delete
33.6 kB
'use strict'
/**
* Copyright (c) 2017~2024, OBCon Inc.
* All rights reserved.
*/
/**
* @file
* @copyright 2017~2024, OBCon Inc.
* @author gye hyun james kim [pnuskgh@gmail.com]
*/
const fs = require('fs');
const path = require('path');
const arg = require('arg');
const moment = require('moment');
const cluster = require('cluster');
const appl = (global.appl = global.appl || {});
global.type = (appl && appl.type) ? appl.type : (global.type || '');
class Application {
constructor(options) {
// logger가 아직 초기화 전이라도 안전하게 콘솔로 시작
global.logger = console;
Object.assign(appl, {
isECMAScriptModules: typeof (require.main) == 'undefined',
datetime: moment().format('YYYY-MM-DD HH:mm:ss'), // Application 시작 일시
...options
});
// ✅ PATCH: name/type 기본값 보정 (PM2에서 "*** undefined Application ..." 방지)
if (!appl.name) appl.name = (process.env.APP_NAME || 'OBCon SCADA');
if (!appl.type && global.type) appl.type = global.type;
if (process.platform == 'darwin') {
// 오류 발생 :: MaxListenersExceededWarning: Possible EventEmitter memory leak detected.
process.setMaxListeners(50);
process.on('unhandledRejection', error => {
console.error('Unhandled promise rejection:', error);
});
}
this._programName = 'bin/scada.js';
global.funcShutdowns = []; // 종료시 실행할 함수 { name: string, func: async function }
this._intervalDeviceIndex = null;
}
_using() {
console.log(`Using : node ${this._programName} --conf CONFFILE`);
console.log(' CONFFILE : 설정 파일');
console.log('');
}
_copyFile(source, target) {
const content = fs.readFileSync(path.join(appl.root, source));
fs.writeFileSync(path.join(appl.root, target), content);
}
/**
* logger.js가 여러 형태로 export될 수 있어서 안전하게 초기화:
* 1) { createLogger() } 형태
* 2) module.exports = class Logger 형태
* 3) module.exports = function(...) 형태
* 실패 시 console로 fallback
*/
_initGlobalLogger() {
try {
const loggerMod = utils.getModule('include', 'logger.js');
// case 1) createLogger 제공
if (loggerMod && typeof loggerMod.createLogger === 'function') {
global.logger = loggerMod.createLogger();
return;
}
// case 2) 클래스/함수 자체 export (module.exports = class Logger)
if (typeof loggerMod === 'function') {
try {
// class로 new 가능한 경우
global.logger = new loggerMod();
return;
} catch (e) {
// new가 안되면 함수 호출 시도
try {
global.logger = loggerMod();
if (!global.logger) global.logger = console;
return;
} catch (e2) {
global.logger = console;
return;
}
}
}
// case 3) 객체인데 createLogger가 없으면 console fallback
global.logger = console;
} catch (e) {
global.logger = console;
}
}
async initApplication() {
try {
//--- 1. 환경 변수 등 설정
process.env.NODE_ENV = 'production';
// ✅ PATCH: PM2 감지 강화
appl.isPM2 = !!(
process.env.PM2_HOME ||
process.env.pm_id ||
process.env.PM2 ||
process.env.PM2_UUID ||
process.env.NODE_APP_INSTANCE ||
process.env.INSTANCE_ID
);
appl.platform = process.platform; // win32, linux, darwin
process.chdir(path.join(__dirname, '..'));
appl.root = process.cwd().replace(/\\/g, '/');
if (['scada_batch'].includes(appl.type)) {
appl.worker = {
id: 1,
type: 'master',
seq: 1
};
} else {
// https://pm2.keymetrics.io/docs/usage/quick-start/
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
// INSTANCE_ID / NODE_APP_INSTANCE / pm_id -> 0,1,2... 형태이므로 +1 해서 1-based로 사용
const rawInstance = (process.env.INSTANCE_ID ?? process.env.NODE_APP_INSTANCE ?? process.env.pm_id ?? '0');
const parsed = parseInt(rawInstance, 10);
const instanceId = (Number.isFinite(parsed) ? parsed : 0) + 1;
appl.worker = {
id: instanceId,
type: 'pm2',
seq: instanceId
};
} else {
appl.worker = {
id: (cluster.isMaster) ? 1 : -1,
type: 'master',
seq: 1,
};
}
}
this._programName = process.argv[1].replace(/\\/g, '/').replace(`${appl.root}/`, '');
//--- 2. 설정 정보 로딩
if (process.argv.length <= 2) {
this._using();
process.exit(1);
}
const args = arg(
{
'--conf': String,
'-c': '--conf'
},
{ argv: process.argv.slice(2) }
);
const configFile = args['--conf'];
if (!configFile) {
console.error('Error: 설정 파일을 지정 하세요.');
this._using();
process.exit(2);
}
if (!fs.existsSync(path.join(appl.root, configFile))) {
console.error(`Error: 설정 파일이 존재하지 않습니다 - ${configFile}`);
this._using();
process.exit(3);
}
global.config = require(path.join(appl.root, configFile));
global.conf = global.config;
// ✅ PATCH: config에 서비스 이름/타입이 있으면 appl 기본값 보정
try {
if (!appl.name) appl.name = (config?.service?.name || config?.service?.title || 'OBCon SCADA');
if (!appl.type) appl.type = (config?.service?.type || appl.type);
} catch (e) { }
if (config && typeof config.funcAfter === 'function') {
config.funcAfter();
}
this._copyFile('version.json', 'version_local.json');
//--- 3. Global Utility 함수 로드
const UtilityPrev = require(path.join(appl.root, 'include', 'UtilityPrev.js'));
const utilityPrev = new UtilityPrev();
global.utils = utilityPrev.getModule('include', 'Utility.js');
// [hotfix] utilityPrev.getModule() 결과가 wrapper일 수 있어 Utility 인스턴스로 보정
try {
const utilMod = require(path.join(__dirname, 'Utility.js'));
if (!global.utils ||
(typeof global.utils.existField !== 'function') ||
(typeof global.utils.isNetworkError !== 'function') ||
(typeof global.utils.getModule !== 'function')) {
global.utils = (typeof utilMod === 'function') ? new utilMod() : utilMod;
}
global.utils.obj = global.utils.obj || {};
global.utils.obj.loggerError = global.utils.obj.loggerError || function (e) {
try { logger.error(e && e.stack ? e.stack : e); } catch (_) { }
};
} catch (e) {
global.utils = global.utils || {};
global.utils.obj = global.utils.obj || {};
global.utils.obj.loggerError = global.utils.obj.loggerError || function (err) {
try { logger.error(err && err.stack ? err.stack : err); } catch (_) { }
};
}
//--- 4. Cluster 설정
if (appl.isPM2 == false) {
switch (appl.type) {
case 'obcon_scada':
case 'obcon_proxy':
case 'obcon_cms':
case 'obcon_iot':
global.clusters = utils.getModule('include', 'cluster.js');
if (global.clusters && typeof global.clusters.forks === 'function') {
global.clusters.forks();
} else {
try { console.error('[WARN] cluster.js has no forks(), continue without clustering'); } catch (e) { }
}
if (cluster.isMaster == false) {
// cluster에서 worker에게 메시지를 보내는 시간보다 5초간 대기 한다.
if (utils && typeof utils.wait !== "function") {
utils.wait = async (condFn, timeoutMs = 5000, intervalMs = 50) => {
const start = Date.now();
while (true) {
let ok = false;
try { ok = !!condFn(); } catch (e) { ok = false; }
if (ok) return true;
if (Date.now() - start > timeoutMs) throw new Error(`utils.wait timeout after ${timeoutMs}ms`);
await new Promise(r => setTimeout(r, intervalMs));
}
};
}
await utils.wait(() => ((appl.worker && (appl.worker && appl.worker.id != -1))), 5 * 1000);
}
break;
}
}
//--- 5. Global Logger 설정 : logger.info(), logger.warn(), logger.error()
this._initGlobalLogger();
const dbg = (msg) => {
try {
if (global.logger && typeof global.logger.debug === 'function') global.logger.debug(msg);
else console.log(msg);
} catch (e) { }
};
dbg(`1. 환경 변수 등 설정`);
dbg(`2. 설정 정보 로딩`);
dbg(`3. Global Utility 함수 로드`);
dbg(`4. Cluster 설정`);
dbg(`5. Global Logger 설정`);
//--- 6. Global Security 설정
global.security = utils.getModule('include', 'Security.js');
dbg(`6. Global Security 설정`);
//--- 7. Global Cache 설정
global.caches = utils.getModule('include', 'caches.js'); // Cluster에서 사용
dbg(`7. Global Cache 설정`);
//--- Global WatchDogTimer 설정
global.watchDogTimer = utils.getModule('include', 'watch_dog_timer.js');
dbg(`Global WatchDogTimer 설정`);
//--- 8. Global Emails 설정
global.emails = utils.getModule('include', 'Emails.js');
dbg(`8. Global Emails 설정`);
//--- 9. Global Telegram 설정
global.telegram = (config.telegram && config.telegram.use) ? await utils.getModule('include', 'Telegram.js') : null;
dbg(`9. Global Telegram 설정`);
//--- 10. Global SMS 설정
global.sms = await utils.getModule('include', 'sms.js');
dbg(`10. Global SMS 설정- ${this._programName}`);
//--- 11. Global Database 설정
global.databases = utils.getModule('include', 'databases.js');
global.db = {
dataTypes: databases.dataTypes,
db_scada: databases.scada,
db_data: databases.data
};
dbg(`11. Global Database 설정`);
//--- 보안: 비밀번호는 마스킹해서 로그에 남김. HF Logs 가 공개될 수 있음.
try {
const _sc = global.conf?.databases?.scada || {};
const _safe = { ...(_sc), password: _sc.password ? `<len=${String(_sc.password).length}>` : null };
console.log("[DB-CONF scada]", _safe);
} catch (e) { console.log("[DB-CONF scada] <print failed:", e.message, ">"); }
console.log("[CONF keys]", global.conf ? Object.keys(global.conf) : null);
console.log("[CONF.databases keys]", global.conf?.databases ? Object.keys(global.conf.databases) : null);
console.log("[CONF file hint]", global.conf?.__filename || global.conf?.configFile || null);
//--- 12. Fields 정의
global.fields = utils.getModule('include', 'fields.js');
dbg(`12. Global Fields 설정`);
//--- 13. Module별로 Model과 View 정의
require(path.join(appl.root, 'modules', 'modules.js'));
dbg(`13. Module별로 Model과 View 정의`);
//--- 14. Settings 정의
global.settingSync = utils.getModule('include', 'settings.js');
dbg(`14. Global Settings 정의`);
//--- 15. Global Interfaces 설정
switch (appl.type) {
case 'obcon_scada':
global.internalProxyClient = utils.getModule('modules', 'proxies', 'proxy_client.js');
dbg(`15. Global Proxy Client 설정`);
// intentional fallthrough
case 'obcon_scada_device':
global.interfaces = utils.getModule('interfaces', 'interfaces.js');
dbg(`15. Global Interfaces 설정`);
break;
}
//--- MQTT 설정
require(path.join(appl.root, 'modules', 'mqtt_app.js'));
dbg(`Global MQTT Client (mqtt_client) 설정`);
// ---- HOTFIX: utils.wait polyfill
if (typeof utils.wait !== 'function') {
utils.wait = async (condFn, timeoutMs = 5000, intervalMs = 50) => {
const start = Date.now();
while (true) {
try {
if (typeof condFn === 'function' ? !!condFn() : !!condFn) return true;
} catch (e) { }
if ((Date.now() - start) >= timeoutMs) {
throw new Error(`utils.wait timeout after ${timeoutMs}ms`);
}
await new Promise(r => setTimeout(r, intervalMs));
}
};
}
//--- 16. Redis 설정
if (config.redis && config.redis.isUse) {
global.redis = utils.getModule('include', 'Redis.js');
await utils.wait(() => global.redis.ready, 5 * 60 * 1000);
setTimeout(async function () {
try {
await modules.devices.view.getDevicesInfo(true);
} catch (e) { }
}, 1000);
}
dbg(`16. Redis 설정`);
//--- IECP 설정
global.iecp = utils.getModule('interfaces', 'IECP.js');
iecp.startGarbageCollection();
dbg(`IECP 설정`);
} catch (e) {
if (typeof (logger) == 'undefined') {
console.error(e.message);
console.error(e.stack);
this._using();
} else {
try { logger.error(e.message); } catch (ex) { }
console.error(e.stack);
}
process.exit(3);
}
}
async run() {
global.app_http = null;
global.app_io = null;
global.app_tcp = null;
global.app_udp = null;
global.app_modbus = null;
global.app_proxy = null;
const workType = () => {
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
const wid = appl.worker?.id ?? (parseInt(process.env.NODE_APP_INSTANCE ?? process.env.INSTANCE_ID ?? process.env.pm_id ?? '0', 10) + 1);
return `PM2 worker ${wid}`;
} else {
return (cluster.isMaster) ? 'Master' : `Worker ${(appl.worker && appl.worker.id)}`;
}
}
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
process.on('SIGINT', function () {
watchDogTimer.stop(); // WatchDogTimer를 종료 한다.
if (global.app_io != null) {
try { global.app_io.close(function () { }.bind(this)); } catch (e) { utils.obj.loggerError(e); }
}
if ((global.app_tcp != null) && (global.app_tcp.server != null)) {
try { global.app_tcp.server.close(function () { }.bind(this)); } catch (e) { utils.obj.loggerError(e); }
}
if ((global.app_udp != null) && (global.app_udp._serverSocket != null)) {
try { global.app_udp._serverSocket.close(function () { }.bind(this)); } catch (e) { utils.obj.loggerError(e); }
}
if ((global.app_modbus != null) && (global.app_modbus.server != null)) {
try { global.app_modbus.server.close(function () { }.bind(this)); } catch (e) { utils.obj.loggerError(e); }
}
if ((typeof (global.app_proxy) != 'undefined') && (global.app_proxy != null)) {
try { global.app_proxy.close(); } catch (e) { utils.obj.loggerError(e); }
}
try { logger.debug('Bye bye'); } catch (e) { }
try {
logger.info('------------------------------------------------------------');
logger.info('SIGINT ------------------------------------------------------------');
logger.info('------------------------------------------------------------');
} catch (e) { }
process.exit(0);
}.bind(this));
}
try {
logger.info(`*** ${appl.name || 'OBCon SCADA'} Application - ${workType()} ***`);
switch (appl.type) {
case 'obcon_scada': this._scada_app(); break;
case 'scada_schedule': this._schedule_app(); break;
case 'obcon_proxy': this._proxy_app(); break;
case 'obcon_scada_device': await this._device_app(); break;
case 'obcon_iot': this._http_app(); break;
case 'scada_batch': this._batch_app(); break;
case 'obcon_cms': this._http_app(); break; // Deprecated
case 'cms_batch': this._batch_app(); break; // Deprecated
default:
logger.error('정의되지 않은 application type 입니다.');
break;
}
} catch (e) {
console.error('error', e);
utils.obj.loggerError(e);
watchDogTimer.stop();
if ((appl.isPM2 == false) && (cluster.isMaster)) {
for (let idx in cluster.workers) {
cluster.workers[idx].kill();
}
}
process.exit(4);
}
}
// PM2에게 ready 신호를 보내 application이 실행 준비가 되었음을 알린다.
_pm2_ready() {
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
setTimeout(function () {
process.send('ready');
}, 500);
}
}
async _scada_app() {
try {
global.idxDevices = {}; // Device별 Interface Version 관리
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
// ✅ PM2에서는 cluster.worker가 없을 수 있으니 appl.worker.id 사용
const pm2WorkerId = ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1);
this._mqtt_client_init('pm2', pm2WorkerId);
setTimeout(async function () {
await this._mqtt_client_makeIndex('pm2');
let appTypes = [];
if (0 < config.cluster.worker_count_http) appTypes.push('http');
if (0 < config.cluster.worker_count_tcp) appTypes.push('tcp');
if (0 < config.cluster.worker_count_udp) appTypes.push('udp');
if (0 < config.cluster.worker_count_modbus) appTypes.push('modbus');
if (0 < config.cluster.worker_count_proxy) appTypes.push('proxy');
for (let type of appTypes) {
if (type == 'modbus') {
require(path.resolve(utils.getCustomPath('modules', 'ModbusApp.js')));
} else if (type == 'proxy') {
global.app_proxy = utils.getModule('modules', 'proxies', 'proxy_app.js');
global.app_proxy.run();
} else {
require(path.resolve(utils.getCustomPath('modules', `${type}_app.js`)));
}
logger.info(`start ${type} service`);
}
mqtt_client.publish(
'publish_command',
'{ "cmd": "stop checkAlive", "clientId": "' + mqtt_client.clientId + '" }'
);
setTimeout(function () {
if (process.env.OBCON_DISABLE_CHECKALIVE === '1') {
console.log('[HF] checkAlive disabled (OBCON_DISABLE_CHECKALIVE=1) - snapshot demo mode');
} else {
watchDogTimer.set_wdt('checkAlive', 10, modules.devices.view.checkAlive.bind(modules.devices.view), moment());
}
this._pm2_ready();
}.bind(this), 1000);
}.bind(this), 1000);
} else {
// ✅ PATCH: cluster.worker 안전 접근
if (cluster.isWorker && cluster.worker && cluster.worker.id != null) {
let type = 'proxy';
if (cluster.worker.id <= config.cluster.worker_count_http) type = 'http';
else if (cluster.worker.id <= config.cluster.worker_count_http + config.cluster.worker_count_tcp) type = 'tcp';
else if (cluster.worker.id <= config.cluster.worker_count_http + config.cluster.worker_count_tcp + config.cluster.worker_count_udp) type = 'udp';
else if (cluster.worker.id <= config.cluster.worker_count_http + config.cluster.worker_count_tcp + config.cluster.worker_count_udp + config.cluster.worker_count_modbus) type = 'modbus';
this._mqtt_client_init(type, cluster.worker.id);
setTimeout(async function () {
await this._mqtt_client_makeIndex(type);
if (type == 'modbus') {
require(path.resolve(utils.getCustomPath('modules', 'ModbusApp.js')));
} else if (type == 'proxy') {
global.app_proxy = utils.getModule('modules', 'proxies', 'proxy_app.js');
global.app_proxy.run();
} else {
require(path.resolve(utils.getCustomPath('modules', `${type}_app.js`)));
}
logger.info(`start ${type} service`);
}.bind(this), 1000);
} else {
if (process.env.OBCON_DISABLE_CHECKALIVE === '1') {
console.log('[HF] checkAlive disabled (OBCON_DISABLE_CHECKALIVE=1) - snapshot demo mode');
} else {
watchDogTimer.set_wdt('checkAlive', 10, modules.devices.view.checkAlive.bind(modules.devices.view), moment());
}
logger.debug(`master start checkAlive`);
this._mqtt_client_init('master', 'master');
setTimeout(async function () {
await this._mqtt_client_makeIndex('master');
}.bind(this), 1000);
}
}
} catch (ex) {
console.error(ex);
}
}
async _proxy_app() {
if (appl.isPM2) {
if (!appl.worker) appl.worker = { id: ((parseInt(process.env.INSTANCE_ID || process.env.NODE_APP_INSTANCE || process.env.pm_id || "0", 10) || 0) + 1) };
setTimeout(async function () {
global.app_proxy = utils.getModule('modules', 'proxies', 'proxy_app.js');
await global.app_proxy.run(appl.type);
logger.info(`start ${appl.type} service`);
}.bind(this), 1000);
} else {
if (cluster.isWorker) {
setTimeout(async function () {
global.app_proxy = utils.getModule('modules', 'proxies', 'proxy_app.js');
await global.app_proxy.run(appl.type);
logger.info(`start proxy service`);
}.bind(this), 1000);
}
}
}
_getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; // 최댓값은 제외, 최솟값은 포함
}
/**
* http_app.js 로딩 실패 시에도 로그 남기고,
* PM2 ready는 finally에서 항상 실행
*/
_http_app() {
if ((appl.isPM2) || (cluster.isWorker)) {
setTimeout(function () {
try {
const p = path.resolve(utils.getCustomPath('modules', 'http_app.js'));
try { logger.info(`[HTTP] require: ${p}`); } catch (e) { console.log(`[HTTP] require: ${p}`); }
require(p);
try { logger.info(`woker start http service : ${config.http.host}:${config.http.port}`); } catch (e) { }
} catch (e) {
try { logger.error(`[HTTP] http_app.js load failed: ${e && (e.stack || e)}`); }
catch (ex) { console.error(`[HTTP] http_app.js load failed: ${e && (e.stack || e)}`); }
} finally {
this._pm2_ready();
}
}.bind(this), 1000);
}
}
_batch_app() {
setTimeout(function () {
let jobBatch = null;
switch (config.batch.path.length) {
case 1: jobBatch = utils.getModule(config.batch.path[0]); break;
case 2: jobBatch = utils.getModule(config.batch.path[0], config.batch.path[1]); break;
case 3: jobBatch = utils.getModule(config.batch.path[0], config.batch.path[1], config.batch.path[2]); break;
case 4: jobBatch = utils.getModule(config.batch.path[0], config.batch.path[1], config.batch.path[2], config.batch.path[3]); break;
case 5: jobBatch = utils.getModule(config.batch.path[0], config.batch.path[1], config.batch.path[2], config.batch.path[3], config.batch.path[4]); break;
default:
logger.error(`실행할 batch 파일을 올바르게 지정 하세요 - ${config.batch.path.join('/')}`);
process.exit(5);
}
logger.info(`Start batch - ${config.batch.path.join('/')}`);
this._pm2_ready();
jobBatch.batchJob(config.batch.options, function (code, message) {
logger.info(`Batch job complete : code - ${code}, message - [${message}]`);
setTimeout(function () {
process.exit(0);
}.bind(this), config.batch.waitTime * 1000);
});
}.bind(this), 1000);
}
async _device_app() {
if (config.emulator.isUse == false) {
console.error('장비 애뮬레이터를 지원하지 않습니다.');
return;
}
console.log('30초 sleep . . . . . .');
await utils.sleep(30 * 1000);
global.idxDevices = {};
await modules.devices.view.makeDeviceIndexForInterface();
console.log('30초 sleep . . . . . . again');
await utils.sleep(30 * 1000);
await modules.devices.view.makeDeviceIndexForInterface();
setTimeout(function () {
let query = { where: { deleted: false } };
modules.sites.model.table.findAll(query)
.then(function (sites) {
let idxSites = modules.sites.view.makeIdx(sites, 'id');
query = { where: { statusUse: 'Active', deleted: false } };
modules.devices.model.table.findAll(query)
.then(function (devices) {
let idxDevices = modules.devices.view.makeIdx(devices, 'deviceKey');
for (let idx = 0; idx < config.emulator.devices.length; idx++) {
let client = config.emulator.devices[idx];
let site = idxSites[`idx_${client.site}`];
let device = idxDevices[`idx_${client.deviceKey}`];
logger.info(`${client.clientType} Client : ${site.siteKey}_${device.type}_${device.deviceKey}`);
let emulator = null;
if (client.clientType == '') {
emulator = utils.getModule('modules', 'clients', `ModbusClient_${device.type}.js`);
} else {
emulator = utils.getModule('modules', 'clients', `${client.clientType}_client_${device.type}.js`);
}
emulator.init(device, site);
setTimeout(function () {
if (emulator._site != null) {
switch (client.clientType) {
case 'modbus':
emulator.startConnect(config.modbus.server.host, config.modbus.server.port);
break;
case 'udp':
emulator.startConnect(config.udp.server.host, config.udp.server.port);
break;
default:
emulator.startConnect(config.tcp.server.host, config.tcp.server.port);
break;
}
setTimeout(function () {
emulator.startAction();
}, 500);
}
}.bind(this), 500);
}
this._pm2_ready();
}.bind(this)).catch(utils.obj.loggerError);
}.bind(this)).catch(utils.obj.loggerError);
}.bind(this), 1000);
}
_schedule_app() {
global.jobs = { jobFiles: [] };
logger.info(`registerAll()`);
modules.jobManagers.view.registerAll();
setTimeout(function () {
logger.info(`startAll()`);
modules.jobManagers.view.startAll();
}.bind(this), 2000);
this._pm2_ready();
}
_mqtt_client_init(type, clientId) {
mqtt_client.type = type;
mqtt_client.clientId = config.mqtt.clientIdPrefix + clientId;
mqtt_client.init();
}
async _mqtt_client_makeIndex(type) {
mqtt_client.subscribe('publish_command');
mqtt_client.subscribe('iecp'); // IECP 통신을 위한 topic 등록
if (this._intervalDeviceIndex == null) {
this._intervalDeviceIndex = setInterval(async function () {
try {
await modules.devices.view.makeDeviceIndexForInterface();
} catch (ex) { }
}.bind(this), 60 * 60 * 1000);
}
}
}
module.exports = Application;