Spaces:
Sleeping
Sleeping
| /** | |
| * Copyright (c) 2017~2020, OBCon Inc. | |
| * All rights reserved. | |
| */ | |
| /** | |
| * @file | |
| * @copyright 2017~2020, OBCon Inc. | |
| * @author gye hyun james kim [pnuskgh@gmail.com] | |
| */ | |
| let fs = require('fs'); | |
| let path = require('path'); | |
| let crypto = require('crypto'); | |
| let moment = require('moment'); //--- format : YYYYMMDDHHmmss.SSS ZZ - 20191220172919.083 +0900 | |
| //--- Full Path를 반환 한다. | |
| function getPath() { | |
| return path.resolve(config.folder_root, path.join.apply(this, arguments)); | |
| } | |
| //--- Custom Path를 반환 한다. | |
| function getCustomPath() { | |
| let filePath = path.join.apply(this, arguments); | |
| let files = [ | |
| getPath('custom', config.service.name, filePath), | |
| getPath('custom', filePath), | |
| getPath(filePath) | |
| ]; | |
| let rtFile = getPath(filePath); | |
| for (let idx = 0; idx < files.length; idx++) { | |
| if (fs.existsSync(files[idx])) { | |
| rtFile = files[idx]; | |
| break; | |
| } | |
| } | |
| return path.resolve(rtFile); | |
| } | |
| function existFile() { | |
| let filePath = path.join.apply(this, arguments); | |
| let files = [ | |
| getPath('custom', config.service.name, filePath), | |
| getPath('custom', filePath), | |
| getPath(filePath) | |
| ]; | |
| let rtFlag = false; | |
| for (let idx = 0; idx < files.length; idx++) { | |
| if (fs.existsSync(files[idx])) { | |
| rtFlag = true; | |
| break; | |
| } | |
| } | |
| return rtFlag; | |
| } | |
| function getClass() { | |
| let filePath = getCustomPath.apply(this, arguments); | |
| return (fs.existsSync(filePath)) ? require(filePath):null; | |
| } | |
| function getModule() { | |
| let moduleClass = getClass.apply(this, arguments); | |
| return (moduleClass == null) ? null:new moduleClass(); | |
| } | |
| // function getSettingModule() { | |
| // let filePath = getCustomPath.apply(this, arguments); | |
| // let moduleClass = require(filePath); | |
| // return new moduleClass(); | |
| // } | |
| //--- 전체 URL을 반환 한다. | |
| function getFullUrl(url='') { | |
| // let rturl = config.http.baseUrl + config.http.path.substring(1); | |
| return (url == '') ? config.http.baseUrl:config.http.baseUrl + url.substring(1); | |
| } | |
| function getTheme(req) { | |
| let theme = JSON.parse(JSON.stringify(config.theme)); | |
| //--- Nginx에서 호출되는 경우 사용 | |
| // if (req.headers.hasOwnProperty('x-forwarded-for')) { | |
| // theme.templateBase = '/themes/' + config.theme.name + '/public'; | |
| // } else { | |
| // theme.templateBase = ''; | |
| // } | |
| theme['http'] = config.http; | |
| return theme; | |
| } | |
| //--- To-Do : 향후 site, user별 설정을 가져 간다. | |
| //--- Theme Customize를 위한 함수 : getTemplatePath() | |
| //--- modbus_ws_app.js의 app.set('views', getPath('themes'));와 app.use(express.static(getPath('themes'))); 참조 | |
| //--- app.use(express.static(getPath('themes'))); 설정에 의해 themes/ 폴더가 기본 폴더가 됨 | |
| function _getTemplateFile() { | |
| let filePath = path.join.apply(this, arguments); | |
| let files = [ | |
| getPath('themes', config.service.name, config.theme.name, filePath), | |
| getPath('themes', config.service.name, config.theme.defaultName, filePath), | |
| getPath('themes', config.theme.name, filePath), | |
| getPath('themes', config.theme.defaultName, filePath) | |
| ]; | |
| let urls = [ | |
| path.join(config.service.name, config.theme.name, filePath), | |
| path.join(config.service.name, config.theme.defaultName, filePath), | |
| path.join(config.theme.name, filePath), | |
| path.join(config.theme.defaultName, filePath) | |
| ]; | |
| // if (-1 < filePath.indexOf('spaAll')) { | |
| // console.log('filePath', filePath); | |
| // console.log('getTemplateFile', files); | |
| // } | |
| for (let idx = 0; idx < files.length; idx++) { | |
| if (fs.existsSync(files[idx])) { | |
| // if (-1 < filePath.indexOf('spaAll')) { | |
| // console.log(urls[idx]); | |
| // } | |
| return urls[idx].replace(/\\/g, '/'); | |
| } | |
| } | |
| return path.join('themes', config.theme.defaultName, filePath).replace(/\\/g, '/'); | |
| } | |
| function getTemplateFile() { | |
| return '/' + _getTemplateFile.apply(this, arguments); | |
| } | |
| //--- ejs에서 파일을 링크하기 위해서 사용 한다. | |
| function getTemplateFile2() { | |
| if ((arguments.length == 1) && (arguments[0].startsWith('http'))) { | |
| return arguments[0]; | |
| } | |
| return config.http.path + '/' + _getTemplateFile.apply(this, arguments); | |
| } | |
| //--- app.set('views', getPath('themes')); 설정에 의해 theme/ 폴더가 기본 폴더가 됨 | |
| function getTemplateEjs() { | |
| let file = _getTemplateFile.apply(this, arguments); | |
| return file; | |
| // return file.substr(0, file.length - 4); | |
| } | |
| function stringFormat(valStr, len, pad=' ') { | |
| let rtStr = valStr; | |
| while (rtStr.length < len) { | |
| rtStr = pad + rtStr; | |
| } | |
| return rtStr; | |
| } | |
| function stringLength(str) { | |
| let len = 0; | |
| for (let idx = 0; idx < str.length; idx++) { | |
| if (escape(str.charAt(idx)).length == 6) { | |
| len++; | |
| } | |
| len++; | |
| } | |
| return len; | |
| } | |
| function integerFormat(valInt, len, pad=' ', abs=true) { | |
| let rtStr = valInt.toString(); | |
| if (-1 < rtStr.indexOf('.')) { | |
| rtStr = rtStr.split('.')[0]; | |
| } | |
| while (rtStr.length < len) { | |
| rtStr = pad + rtStr; | |
| } | |
| if ((abs) && (len < rtStr.length)) { | |
| rtStr = rtStr.slice(rtStr.length - len, rtStr.length); | |
| } | |
| return rtStr; | |
| } | |
| //--- To-Do : 향후 개선 한다. | |
| //--- abs : true. 소수점 아래와 소수점 위의 길이를 지킨다. | |
| function floatFormat(valFloat, lenFirst=5, lenSecond=2, padFirst=' ', padSecond='0', abs=true) { | |
| let rtStr = valFloat.toString(); | |
| let tmpFirst = null; | |
| let tmpSecond = null; | |
| if (-1 < rtStr.indexOf('.')) { | |
| let items = rtStr.split('.'); | |
| tmpFirst = items[0]; | |
| tmpSecond = items[1]; | |
| if (abs) { | |
| if (lenSecond < tmpSecond.length) { | |
| tmpSecond = tmpSecond.slice(0, lenSecond); | |
| } | |
| } | |
| } else { | |
| tmpFirst = rtStr; | |
| tmpSecond = ''; | |
| } | |
| while (tmpSecond.length < lenSecond) { | |
| tmpSecond = tmpSecond + padSecond; | |
| } | |
| rtStr = tmpFirst + '.' + tmpSecond; | |
| while (rtStr.length < (lenFirst + 1 + lenSecond)) { | |
| rtStr = padFirst + rtStr; | |
| } | |
| if ((abs) && ((lenFirst + 1 + lenSecond) < rtStr.length)) { | |
| rtStr = rtStr.slice(rtStr.length - (lenFirst + 1 + lenSecond), rtStr.length); | |
| } | |
| return rtStr; | |
| } | |
| //--- 소수점 아래 자리만 확정 한다. | |
| function floatFormat2(valFloat, lenSecond=2, padSecond='0') { | |
| let rtStr = valFloat.toString(); | |
| let tmpFirst = null; | |
| let tmpSecond = null; | |
| if (-1 < rtStr.indexOf('.')) { | |
| let items = rtStr.split('.'); | |
| tmpFirst = items[0]; | |
| tmpSecond = items[1]; | |
| if (lenSecond < tmpSecond.length) { | |
| tmpSecond = tmpSecond.slice(0, lenSecond); | |
| } | |
| } else { | |
| tmpFirst = rtStr; | |
| tmpSecond = ''; | |
| } | |
| while (tmpSecond.length < lenSecond) { | |
| tmpSecond = tmpSecond + padSecond; | |
| } | |
| rtStr = tmpFirst + '.' + tmpSecond; | |
| return rtStr; | |
| } | |
| function getHash(plainText) { | |
| let hash = crypto.createHash('md5'); | |
| hash.update(plainText); | |
| return hash.digest('hex').toLowerCase(); | |
| } | |
| //--- string, number, bigint, boolean, function, undefined, symbol | |
| //--- object : null, object, array | |
| function obconTypeof(param) { | |
| return Object.prototype.toString.call(param).replace(/\[object /g, '').replace(/\]/g, '').toLowerCase(); | |
| } | |
| function Null(value) { | |
| if (isNone(value)) { | |
| return ''; | |
| } else if (value == null) { | |
| return ''; | |
| } | |
| return value; | |
| } | |
| //--- typeof : string (String), bigint (BigInt), number (Number), boolean (Boolean), object (Null), symbol (Symbol), function, undefined (Undefined) | |
| function isNone(param1) { | |
| return typeof(param1) === 'undefined'; | |
| } | |
| function isNull(param1) { | |
| return param1 == null; | |
| } | |
| function isString(param1) { | |
| return typeof(param1) == 'string'; | |
| } | |
| function isNumber(param1) { | |
| return typeof(param1) == 'number'; | |
| } | |
| function isArray(param1) { | |
| return obconTypeof(param1) == 'array'; | |
| } | |
| function existField(paramObject, paramField) { | |
| if ((paramObject == null) || (typeof(paramObject) == 'undefined')) { | |
| return false; | |
| } | |
| return typeof(paramObject[paramField]) != 'undefined'; | |
| // return Object.getOwnPropertyNames(paramObject).includes(paramField); | |
| // return -1 < Object.getOwnPropertyNames(paramObject).indexOf(paramField); | |
| } | |
| function existIdxField(paramObject, paramField) { | |
| if ((paramObject == null) || (typeof(paramObject) == 'undefined')) { | |
| return false; | |
| } | |
| return typeof(paramObject[`idx_${paramField}`]) != 'undefined'; | |
| // return Object.getOwnPropertyNames(paramObject).includes(paramField); | |
| // return -1 < Object.getOwnPropertyNames(paramObject).indexOf(paramField); | |
| } | |
| function getObjectValue(paramObject, paramField, paramDefault) { | |
| return existField(paramObject, paramField) ? paramObject[paramField]:paramDefault; | |
| } | |
| function deepCopy(obj) { | |
| return JSON.parse(JSON.stringify(obj)); | |
| } | |
| function randomInteger(min, max) { | |
| return Math.floor(Math.random() * (max - min + 1)) + min; | |
| } | |
| function randomFloat(min, max) { | |
| return Math.random() * (max - min) + min; | |
| } | |
| //--- pos : 1(첫번째 문자), 2(두번째 문자), ..., 32 | |
| let maxPos = 32; | |
| function initFlagString(source) { | |
| let tmpStr = source + '0000000000000000000000000000000000000000'; | |
| return tmpStr.substring(0, maxPos); | |
| } | |
| function getFlagString(source, pos) { | |
| let tmpStr = initFlagString(source); | |
| tmpStr = tmpStr.substring(pos - 1, pos); | |
| return tmpStr; | |
| } | |
| function setFlagString(source, pos, set) { | |
| let tmpStr = initFlagString(source); | |
| tmpStr = tmpStr.substring(0, pos - 1) + set + tmpStr.substring(pos, maxPos); | |
| return tmpStr; | |
| } | |
| //--- pos : 1(일자리), 2(십자리), ..., 32 | |
| function getFlagNumber(source, pos) { | |
| let tmpStr = '0000000000000000' + Math.abs(source); | |
| tmpStr = tmpStr.substring(tmpStr.length - 16); | |
| tmpStr = tmpStr.substring(16 - pos, 16 - pos + 1); | |
| return parseInt(tmpStr); | |
| } | |
| function setFlagNumber(source, pos, set) { | |
| let tmpStr = '0000000000000000' + Math.abs(source); | |
| tmpStr = tmpStr.substring(tmpStr.length - 16); | |
| tmpStr = tmpStr.substring(0, 16 - pos) + set + tmpStr.substring(16 - pos + 1); | |
| return parseInt(tmpStr); | |
| } | |
| function checkHacking(param) { | |
| if (!param) { | |
| return false; | |
| } | |
| if ((-1 < param.indexOf(';')) || (-1 < param.indexOf('\''))) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| //--- 박광서의 요청에 따라 현재 시간이 아니라, 전달되는 데이터의 시간으로 체크 한다. 2019.03.31 | |
| //--- 통신 이상 검사 : true. 통신 이상, false. 통신 정상 | |
| //--- | |
| //--- 한번도 데이터를 받은 적이 없으면 통신 이상 | |
| //--- 1. 정류기: 설정한 시간(Default 60초) 이상 데이터가 입력되지 않으면 통신 이상 | |
| //--- 2. 원격TB: 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 3. 배관링크: 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 4. 수위센서: 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 5. 관말압력 | |
| //--- 피크타임 모드인 경우 | |
| //--- 피크타임인 경우 - 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 피크타임이 아닌 경우 - 항상 통신 정상 | |
| //--- 피크타임 모드가 아닌 경우 - 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 6. 특정정압기: 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| //--- 7. 수신감도: 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 통신 이상 | |
| // function isNetworkError(deviceIndex, type, networkCycle, networkCycleUnit, statusDatetime) { | |
| function isNetworkError(siteKey, device, statusDatetime) { | |
| let deviceIndex = siteKey + '_' + device.type + '_' + device.deviceKey; | |
| let type = device.type; | |
| let networkCycle = device.networkCycle; | |
| let networkCycleUnit = device.networkCycleUnit; | |
| if (type == '5') { //--- 5. 관말압력 | |
| if (device.networkMode == '4') { //--- 4. 피크타임 모드 | |
| networkCycle = device.peakNetworkCycle; | |
| networkCycleUnit = '분'; | |
| //--- 설정된 피크타임 시간이 아니면, statusDatetime을 현재 시간으로 설정하여 항상 통신 이상이 발생하지 않도록 한다. | |
| let isPeak = false; | |
| let hour = parseInt(moment().format('HH')); //--- 현재 시간 (0 ~ 24) | |
| if ((device.peakTimeStart1 <= hour) && (hour < device.peakTimeEnd1)) { | |
| isPeak = true; | |
| } | |
| if ((device.peakTimeStart2 <= hour) && (hour < device.peakTimeEnd2)) { | |
| isPeak = true; | |
| } | |
| if (isPeak == false) { | |
| statusDatetime = moment(); | |
| } | |
| } | |
| } | |
| //--- 통신 이상 여부를 검사 한다. | |
| let rtcd = true; | |
| if (statusDatetime == null) { //--- 한번도 데이터를 받은 적이 없으면 통신 이상 처리 | |
| return true; | |
| } | |
| let datetimeFr = statusDatetime; //--- 최종 도착한 데이터의 시간 | |
| let datetimeTo = moment(); //--- 현재 시간 | |
| let duration = 0; | |
| let unit = '분'; | |
| switch (type) { | |
| case '1' : | |
| unit = '초'; | |
| duration = moment.duration(datetimeTo.diff(datetimeFr)).asSeconds(); | |
| //--- 정류기는 60초 이상 데이터가 입력되지 않으면 통신 이상 처리 -> 설정한 시간으로 비교 | |
| // rtcd = (60 < duration); | |
| rtcd = (device.networkCheckTime < duration); | |
| logger.info(`include/global_utils.js :: ${deviceIndex} : Duration - ${floatFormat(duration, 7, 2)} ${unit}, networkCycle : ${device.networkCheckTime} :: ${rtcd}`); | |
| break; | |
| case '2' : | |
| case '3' : | |
| case '4' : | |
| case '5' : | |
| case '6' : | |
| case '7' : | |
| unit = '분'; | |
| duration = moment.duration(datetimeTo.diff(datetimeFr)).asMinutes(); | |
| if (networkCycleUnit == '시') { | |
| networkCycle = networkCycle * 60; | |
| } | |
| //--- 박광서의 요청으로 임시로 통신 주기 * 3 보다 통신이 되지 않은 시간이 클 경우 오류 처리 (2019-03-11) | |
| rtcd = (3 * networkCycle < duration); | |
| logger.info(`include/global_utils.js :: ${deviceIndex} : Duration - ${floatFormat(duration, 7, 2)} ${unit}, 3 * networkCycle : ${3 * networkCycle} :: ${rtcd}`); | |
| break; | |
| case '8': | |
| case 'A': | |
| default: | |
| switch (networkCycleUnit) { | |
| case '초': | |
| unit = '초'; | |
| duration = moment.duration(datetimeTo.diff(datetimeFr)).asSeconds(); | |
| break; | |
| case '분': | |
| unit = '분'; | |
| duration = moment.duration(datetimeTo.diff(datetimeFr)).asMinutes(); | |
| break; | |
| case '시': | |
| unit = '분'; | |
| duration = moment.duration(datetimeTo.diff(datetimeFr)).asMinutes(); | |
| networkCycle = networkCycle * 60; | |
| break; | |
| } | |
| rtcd = (3 * networkCycle < duration); | |
| logger.info(`include/global_utils.js :: ${deviceIndex} : Duration - ${floatFormat(duration, 7, 2)} ${unit}, 3 * networkCycle : ${3 * networkCycle} :: ${rtcd}`); | |
| break; | |
| } | |
| return rtcd; | |
| } | |
| function saveNotificationNormal(device, datetimeTo) { | |
| let noti = {}; | |
| noti.name = '통신 정상'; | |
| noti.site = device.site; | |
| noti.device = device.id; | |
| noti.deviceKey = device.deviceKey; | |
| noti.deviceName = device.name; | |
| noti.branch = -1; | |
| noti.branchName = ''; | |
| noti.type = device.type; | |
| noti.eventDatetime = datetimeTo.format('YYYY-MM-DD HH:mm:ss'); | |
| noti.level = 'Info'; | |
| noti.description = '통신 정상'; | |
| //--- branch로 branch 이름을 가져 온다. | |
| let query = { where: { id: device.branch, deleted: false } }; | |
| modules.branches.model.table.findOne(query) | |
| .then(function(branch) { | |
| if (branch != null) { | |
| noti.branch = branch.id; | |
| noti.branchName = branch.name; | |
| } | |
| modules.notifications.model.table.create(noti) | |
| .then(function(results) { | |
| // logger.info(JSON.stringify(results)); | |
| }).catch(utils.obj.loggerError); | |
| }).catch(function(err) { | |
| logger.error(JSON.stringify(err)); | |
| modules.notifications.model.table.create(noti) | |
| .then(function(results) { | |
| // logger.info(JSON.stringify(results)); | |
| }).catch(utils.obj.loggerError); | |
| }); | |
| } | |
| function saveNotificationError(device, datetimeTo) { | |
| let noti = {}; | |
| noti.name = '통신 이상'; | |
| noti.site = device.site; | |
| noti.device = device.id; | |
| noti.deviceKey = device.deviceKey; | |
| noti.deviceName = device.name; | |
| noti.branch = -1; | |
| noti.branchName = ''; | |
| noti.type = device.type; | |
| noti.eventDatetime = datetimeTo.format('YYYY-MM-DD HH:mm:ss'); | |
| noti.level = 'Error'; | |
| noti.description = '통신 이상'; | |
| //--- branch로 branch 이름을 가져 온다. | |
| let query = { where: { id: device.branch, deleted: false } }; | |
| modules.branches.model.table.findOne(query) | |
| .then(function(branch) { | |
| if (branch != null) { | |
| noti.branch = branch.id; | |
| noti.branchName = branch.name; | |
| } | |
| modules.notifications.model.table.create(noti) | |
| .then(function(results) { | |
| // logger.info(JSON.stringify(results)); | |
| }).catch(utils.obj.loggerError); | |
| }).catch(function(err) { | |
| logger.error(JSON.stringify(err)); | |
| modules.notifications.model.table.create(noti) | |
| .then(function(results) { | |
| // logger.info(JSON.stringify(results)); | |
| }).catch(utils.obj.loggerError); | |
| }); | |
| } | |
| function stringToHex(str) { | |
| const buf = Buffer.from(str, 'utf8'); | |
| return buf.toString('hex'); | |
| } | |
| function hexToString(str) { | |
| const buf = new Buffer(str, 'hex'); | |
| return buf.toString('utf8'); | |
| } | |
| function isWindows() { | |
| return (process.env.HOMEDRIVE) ? true:false; | |
| } | |
| function isLinux() { | |
| return (process.env.HOME) ? true:false; | |
| } | |
| function stockUnitPrice(price) { | |
| if (price < 1000) { | |
| return 1; | |
| } else if (price < 5000) { | |
| return 5; | |
| } else if (price < 10000) { | |
| return 10; | |
| } else if (price < 50000) { | |
| return 50; | |
| } else if (price < 100000) { | |
| return 100; | |
| } else if (price < 500000) { | |
| return 500; | |
| } else { | |
| return 1000; | |
| } | |
| } | |
| //--- 주문가격으로 판매 가격 계산 : ROUNDUP(R350 * (1 + 1%) / H350, 0) * H350 | |
| function stockCalcSalePrice(stockCode, orderPrice, saleRate = 1.0) { //--- saleRate : 1% 상승시 판매 | |
| // let saleRate = self.getPolicy(stockCode, 'saleRate') | |
| let salePrice = orderPrice * (1 + saleRate / 100.0); | |
| let unit = stockUnitPrice(salePrice); | |
| return Math.ceil(salePrice / unit) * unit; | |
| } | |
| //--- 키움증권 매수 수수료 : ROUNDDOWN(매수 금액 * 0.015%, -1) | |
| function stockCalcPurchaseFee(purchaseTotal) { | |
| let fee = purchaseTotal * 0.015 / 100 | |
| return Math.floor(fee / 10) * 10; | |
| } | |
| //--- 키움증권 매도 수수료 : ROUNDDOWN(매도 금액 * 0.015%, -1) | |
| function stockCalcSaleFee(saleTotal) { | |
| let fee = saleTotal * 0.015 / 100; | |
| return Math.floor(fee / 10) * 10; | |
| } | |
| //--- 키움증권 매도 거래세 : ROUND((W1257 - J1257 - X1257) * (0.1% + 0.15%), -1) | |
| function stockCalcSaleTax(purchaseTotal, saleTotal) { | |
| let fee = (saleTotal - stockCalcPurchaseFee(purchaseTotal) - stockCalcSaleFee(saleTotal)) * (0.1 + 0.15) / 100; | |
| return Math.ceil(fee / 10) * 10; | |
| } | |
| //--- To-Do : LocalStorage, Cookie 등을 사용하여 설정 관리 | |
| function makeSetting(id='iconSetting') { | |
| // if ((!config.func) || (!config.func.page) || (!config.func.page.setting)) { | |
| // return ''; | |
| // } | |
| // if (config.func.page.setting == false) { | |
| // return ''; | |
| // } | |
| //--- https://icons.getbootstrap.com/ | |
| // <i class="bi-alarm" style="${config.theme.font.help} color: cornflowerblue; cursor: pointer;" | |
| // onclick="window.alert('Reserved');"></i> | |
| return `<i id="${id}" class="bi-gear" style="${config.theme.font.help} color: cornflowerblue; cursor: pointer; display: none;" | |
| onclick="nsOBCon.pageSettings.display();"></i>`; | |
| } | |
| //--- options.helpPage = 'url'; | |
| //--- options.helpPage = [ { name: '', url: '' } ]; //--- To-Do : 선택 목록을 보여준 후 선택시 도움말 화면 표시 | |
| function makeHelp(id='iconHelp') { | |
| // if ((!config.func) || (!config.func.page) || (!config.func.page.setting)) { | |
| // return ''; | |
| // } | |
| // if (config.func.page.setting == false) { | |
| // return ''; | |
| // } | |
| //--- https://icons.getbootstrap.com/ | |
| // return `<i class="bi-question-square" style="${config.theme.font.help} color: cornflowerblue; cursor: pointer;" | |
| // onclick="window.open('https://www.obcon.biz/cms/${options.helpPage}', '_blank');"></i>`; | |
| return `<i id="${id}" class="bi-question-square" style="${config.theme.font.help} color: cornflowerblue; cursor: pointer; display: none;" | |
| onclick="nsOBCon.pageHelps.display();"></i>`; | |
| } | |
| function getClientIp(req) { | |
| var clientIp = req.body.clientIp || | |
| req.headers['x-forwarded-for'] || | |
| req.connection.remoteAddress || | |
| req.socket.remoteAddress || | |
| req.connection.socket.remoteAddress || ''; | |
| clientIp = clientIp.split(',')[0]; | |
| clientIp = clientIp.split(':').slice(-1)[0]; | |
| return clientIp; | |
| } | |
| function datetimeFormatter(str, formatFr = 'YYYYMMDDHHmmss', formatTo = 'YYYY-MM-DD HH:mm:ss') { | |
| return (str == '') ? '': moment(str, formatFr).format(formatTo); | |
| } | |
| // async function sleep(miliseconds) { | |
| // let promise = new Promise(function(resolve, reject) { | |
| // setTimeout(function() { | |
| // resolve(); | |
| // }, miliseconds); | |
| // }); | |
| // await promise; | |
| // } | |
| module.exports = { | |
| getPath: getPath, | |
| getCustomPath: getCustomPath, | |
| existFile: existFile, | |
| getClass: getClass, | |
| getModule: getModule, | |
| // getSettingModule: getSettingModule, | |
| getFullUrl: getFullUrl, | |
| getTheme: getTheme, | |
| getTemplateFile: getTemplateFile, | |
| getTemplateFile2: getTemplateFile2, | |
| getTemplateEjs: getTemplateEjs, | |
| getHash: getHash, | |
| stringFormat: stringFormat, | |
| stringLength: stringLength, | |
| integerFormat: integerFormat, | |
| floatFormat: floatFormat, | |
| floatFormat2: floatFormat2, | |
| Null: Null, | |
| isNone: isNone, | |
| isNull: isNull, | |
| isString: isString, | |
| isNumber: isNumber, | |
| isArray: isArray, | |
| existField: existField, | |
| existIdxField: existIdxField, | |
| getObjectValue: getObjectValue, | |
| deepCopy: deepCopy, | |
| datetimeFormatter: datetimeFormatter, | |
| initFlagString: initFlagString, | |
| getFlagString: getFlagString, | |
| setFlagString: setFlagString, | |
| getFlagNumber: getFlagNumber, | |
| setFlagNumber: setFlagNumber, | |
| checkHacking: checkHacking, | |
| randomInteger: randomInteger, | |
| randomFloat: randomFloat, | |
| isNetworkError: isNetworkError, | |
| saveNotificationNormal: saveNotificationNormal, | |
| saveNotificationError: saveNotificationError, | |
| stringToHex: stringToHex, | |
| hexToString: hexToString, | |
| isWindows: isWindows, | |
| isLinux: isLinux, | |
| makeHelp: makeHelp, | |
| makeSetting: makeSetting, | |
| getClientIp: getClientIp, | |
| // sleep: sleep, | |
| stock: { | |
| unitPrice: stockUnitPrice, | |
| calcSalePrice : stockCalcSalePrice, | |
| calcPurchaseFee: stockCalcPurchaseFee, | |
| calcSaleFee: stockCalcSaleFee, | |
| calcSaleTax: stockCalcSaleTax | |
| }, | |
| obj: getModule('include', 'obconObject.js') | |
| }; | |