SOURCE

/**
 *  逻辑点
 *  1. 使用od班次时间
 *  2. 良品数和总数使用累加转变化值求和
 *  3. 输出多台设备总的稼动率, oee, 良率等
 *  4. 依赖最新的脚本引擎
 *  5. 输出多台设备总的计划运行时间, 实际运行时间, 总预期生产数, 总实际产出数, 总良品数,
 *
 *  改动点
 *  1. 计划运行时间扣除休息时
 */
const vs = require("../systemScripts/variable");
const http = require('http');
const moment = require("moment");

/********用于替代vs中对象没有这个函数TODO::*******/
const sdkClient = require('../modules/sdk-module');

// 获取上N个值
async function getLastNValues(id, n) {
    return new Promise(function (resolve, reject) {
        sdkClient.apis.getLastNVarValues(id, n, function (error, res) {
            if (error) {
                console.log(error);
                resolve([]);
            } else if (res && res.success && res.data) {
                resolve(res.data);
            } else {
                resolve([]);
            }
        });
    });
}

// 将moment对象format的函数
const fT = (m) => m.format('YYYY-MM-DD HH:mm:ss');
// 将YYYY-MM-DD HH:mm:ss的格式str转为moment对象
const mT = (s) => moment(s, 'YYYY-MM-DD HH:mm:ss');
// 将格式化str转为moment对象
const mHT = (s) => moment(s, 'HH:mm:ss');
// 数组求和函数
const sumFunc = (arr) => arr.reduce((prev, curr) => prev + curr, 0);
// 将YYYY-MM-DD HH:mm:ss字符串转为时间戳
const strToTimestamp = (str) => new Date(str).getTime();
// 一分钟的毫秒数
const ONE_MIN_MILLIONS = 60 * 1000;

/**
 * 给休息时间添加日期的前缀
 * @param obj
 * @returns {Array}
 */
function addDatePrefix(obj) {
    //  给休息时间添加前缀日期, 找到startTime的endOfday, 进行判断即可
    const sStr = obj['startTime'].format('YYYY-MM-DD');
    const eStr = obj['endTime'].format('YYYY-MM-DD');
    const hStr = obj['startTime'].format('HH:mm:ss');
    // 如果是同一天
    if (obj['startTime'].isSame(obj['endTime'], 'day')) {
        obj['restTimes'] = obj['restTimes'].map(i => {
            return {
                start: mT(`${sStr} ${i['start']}`),
                end: mT(`${sStr} ${i['end']}`)
            };
        }).sort((a, b) => a['start'].valueOf() - b['start'].valueOf())
    } else {
        // 如果是跨天,只需要判断时分秒和startTime的先后即可,先于startTime的肯定是第二天的。
        obj['restTimes'] = obj['restTimes'].map(i => {
            return {
                start: mHT(i['start']).isBefore(mHT(hStr)) ? mT(`${eStr} ${i['start']}`) : mT(`${sStr} ${i['start']}`),
                end: mHT(i['end']).isBefore(mHT(hStr)) ? mT(`${eStr} ${i['end']}`) : mT(`${sStr} ${i['end']}`)
            };
        }).sort((a, b) => a['start'].valueOf() - b['start'].valueOf())
    }
}

/**
 * 处理小数  确保大于等于0且小于等于1
 * @param {*} value
 */
const safeRate = value => {
    value = Number(value);
    if (Number.isNaN(value)) {
        value = 0;
    }

    if (value > 1) {
        value = 1;
    }

    if (value < 0) {
        value = 0;
    }

    return value;
}

/**
 * 封装post请求方法
 * @param host
 * @param port
 * @param url
 * @param data
 * @returns {Promise<*>}
 */
const postResponse = async (host, port, url, data) => {
    let option = {
        host: host,
        port: port,
        path: url,
        method: 'POST',
        headers: {
            'accept': '*/*',
            'content-type': "application/json"
        }
    };

    return new Promise((resolve, reject) => {
        let chunks = [];
        let size = 0;
        let req = http.request(option, res => {
            res.on('data', body => {
                chunks.push(body);
                size += body.length;
            });

            res.on('end', () => {
                let buffer = Buffer.concat(chunks, size);
                let data = JSON.parse(buffer.toString());
                resolve(data);
            });
        });

        req.on('error', error => {
            resolve(null);
        })

        req.write(JSON.stringify(data));
        req.end();
    });
};

/**
 * 将小数转化为百分比
 * @param {*} rate
 */
const convertRateString = rate => {
    return `${(rate * 100).toFixed(2)}%`;
};

/**
 * 获取设备的生产计划
 * @param deviceNos 这里是个拼接的字符串,所以要改写一下。
 * @param d
 * @param odIP
 * @param odPort
 * @returns {Promise<{}>}
 */
async function getDeviceProductionTime(deviceNos, d, odIP, odPort) {
    const reqData = {
        date: typeof (d) === 'string' ? d : d.format('YYYY-MM-DD'),
        deviceNos
    };
    let res = await postResponse(odIP, odPort, '/api/ol/pp', reqData);

    console.log('请求od拿到的生产计划');
    console.log(res);
    // 如果拿到了数据,进行处理
    if (res && Array.isArray(res['data'])) {
        const mapObj = {};
        for (let i of res['data']) {
            // 当前设备的生产计划list
            mapObj[i['deviceNo']] = i['dateVoList'] ? i['dateVoList'].map(item => ({
                startTime: item['startTime'],
                endTime: item['endTime'],
                restTimes: [...item['coffeeBreakDateVoList']]
            })) : [];
        }
        return mapObj
    }
    return {}
}

/**
 * 切分当前生产计划段,注意,这里休息时间可能是多端,以及跨天问题。
 * @param timeList
 * @returns {Array}
 */
function cutCurrentPeriodTime(timeList) {
    // 判断当前时间在哪一段
    const now = moment();
    // 找到当前时间在哪一段
    const arr = timeList.filter(i => {
        return now.isSameOrAfter(mT(i['startTime'])) && now.isSameOrBefore(mT(i['endTime']))
    });
    // 如果当前时间没有在任何一段内, 返回空数组
    if (arr.length <= 0) {
        return []
    }
    const currentPeriod = arr[0];
    currentPeriod['startTime'] = mT(currentPeriod['startTime']);
    currentPeriod['endTime'] = mT(currentPeriod['endTime']);
    // 得到最后的结果数组
    let result = [];
    // 没有休息时间时
    if (currentPeriod['restTimes'].length === 0) {
        result.push({s: fT(currentPeriod['startTime']), e: fT(now)})
        return result;
    }
    // 给休息时间添加日期前缀
    addDatePrefix(currentPeriod);
    // 判断now是在哪一段
    if (currentPeriod['restTimes'].length === 1) {
        // 一段休息时间
        let s = currentPeriod['startTime'];
        let rS = currentPeriod['restTimes'][0]['start'];
        let rE = currentPeriod['restTimes'][0]['end'];
        switch (true) {
            case now.isBefore(rS):
                result.push({s, e: now})
                break;
            case now.isSameOrAfter(rS) && now.isSameOrBefore(rE):
                result.push({s, e: rS})
                break;
            case now.isAfter(rE):
                result.push({s, e: rS})
                result.push({s: rE, e: now})
                break;
            default :
                ;
        }
    } else {
        // 多段休息时间
        const rests = currentPeriod['restTimes'];
        // 将切分后的时间段都推入slicedtimes,并用isRest作为区分
        const slicedTimes = [];
        // 这里处理多段休息时间
        for (let i = 0; i < rests.length; i++) {
            //   循环将每段生产时间和休息时间都推入slicedTimes
            if (i === 0) {
                slicedTimes.push({s: currentPeriod['startTime'], e: rests[i]['start'], isRest: false});
                slicedTimes.push({s: rests[i]['start'], e: rests[i]['end'], isRest: true});
            } else {
                slicedTimes.push({s: rests[i - 1]['end'], e: rests[i]['start'], isRest: false});
                slicedTimes.push({s: rests[i]['start'], e: rests[i]['end'], isRest: true});
            }
            //   在最后的时候要推入最后一段生产时间
            if (i === rests.length - 1) {
                slicedTimes.push({s: rests[i]['end'], e: currentPeriod['endTime'], isRest: false});
            }
        }
        //    在slicedTimes中查找当前时间是在哪一段
        for (let i = 0; i < slicedTimes.length; i++) {
            const curr = slicedTimes[i];
            if (now.isBetween(curr['s'], curr['e'], null, '[]')) {
                curr['isRest'] || result.push({s: curr['s'], e: now});
                break;
            } else {
                // 在找到当前区间之前,如果!isRest就推入result
                curr['isRest'] || result.push({s: curr['s'], e: curr['e']})
            }
        }
    }
    return result
}

/**
 * 判断状态是否处于运行状态
 * @param {*} value
 */
const isRunning = value => {
    return value === '2' || value === 2;
};

/**
 * 在单个时间段内计算设备实际运行时间
 * @param time
 */
async function getRealTimeBySection(device, time) {
    let statusVarId = device['deviceStatusVarId'];
    let startTime = typeof(time['s']) === 'string' ? time['s'] : fT(time['s']);
    // console.log(mT(startTime).valueOf());
    let endTime = typeof(time['e']) === 'string' ? time['e'] : fT(time['e']);
    // 最后的返回的结果值
    let result = 0;
    // 查看区间中有几个状态切换的变量
    let res = await vs.getVariablesInTimeRange(statusVarId, startTime, endTime);

    console.log('getVariablesInTimeRange');
    console.log(res);
    let len = res.length;
    let resultTimesArray = [];
    let lastNVal = [];
    // 如果区间中没有变量,这时候看last1Id是什么值就可以判断这一段是什么状态了
    if (len <= 0) {
        let last1Val = await getLastNValues(device['deviceStatusVarId'], 1);
        if (isRunning(last1Val[0]['value'])) {
            result = strToTimestamp(endTime) - strToTimestamp(startTime);
        }
    } else {
        //  如果区间中有m个变量,需要找last(m + 1)Id获取最后m+1个值来进行判断
        lastNVal = await getLastNValues(device['deviceStatusVarId'], len + 1);
        lastNVal = lastNVal.reverse(); // 这里获取到的是倒序的,进行一次reverse
        console.log('lastNVal');
        console.log(lastNVal);
        lastNVal.forEach((item, index) => {
            if (isRunning(item['value'])) {
                let currDiffValue = 0;
                if (index === 0) {
                    //  是第一个值的时候
                    currDiffValue = strToTimestamp(lastNVal[index + 1]['timestamp']) - strToTimestamp(startTime);
                } else if (index < len) {
                    //  不是最后一个值的时候
                    currDiffValue = strToTimestamp(lastNVal[index + 1]['timestamp']) - strToTimestamp(item['timestamp']);
                } else {
                    //  是最后一个值的时候
                    currDiffValue = strToTimestamp(endTime) - strToTimestamp(item['timestamp'])
                }
                resultTimesArray.push(currDiffValue)
            }
        });
        result = sumFunc(resultTimesArray);
    }
    console.log(result);
    return {
        timeRange: time,
        getVariablesInTimeRangeArray: res,
        len: len,
        lastNVal: lastNVal,
        getRealTimeBySectionValue: resultTimesArray,
        result: result
    }; // 返回实际运行的毫秒
}

/**
 * 计算稼动率
 * @param device
 * @param cuttedTimes
 * @returns {Promise<void>}
 */
async function calcActivation(device, cuttedTimes) {
    console.log('cuttedTimes');
    console.log(cuttedTimes);
    // 总工作时长毫秒
    let workTimeMills = 0;
    // 实际运行时长
    let realWorkTimeMills = 0;
    // cuttedTimes.forEach(async item => {
    //     workTimeMills += strToTimestamp(item['e']) - strToTimestamp(item['s']);
    //     const thisSectionRealTime = await getRealTimeBySection(device, item);
    //     realWorkTimeMills += thisSectionRealTime;
    // });
    let debugArray = [];
    for (let item of cuttedTimes) {
        workTimeMills += strToTimestamp(item['e']) - strToTimestamp(item['s']);
        const thisSectionRealTimeObj = await getRealTimeBySection(device, item);
        debugArray.push(thisSectionRealTimeObj);
        realWorkTimeMills += thisSectionRealTimeObj['result'];
    }

    let res = safeRate(realWorkTimeMills / workTimeMills);
    console.log('稼动率');
    console.log(res);
    return {
        debugArray,
        realWorkTimeMills,
        workTimeMills,
        activationRate: res
    }
}

/**
 * 计算良率
 * @param device
 */
async function calcOkRate(device) {
    let okRate = 0;
    const {qualifiedNumVarId, totalNumVarId} = device;
    const qualifiedNumObj = await vs.getCurrentValue(qualifiedNumVarId);
    const totalNumObj = await vs.getCurrentValue(totalNumVarId);

    const qualifiedNum = Number(qualifiedNumObj['value']);
    const totalNum = Number(totalNumObj['value']);

    if (qualifiedNum > 0 && totalNum > 0) {
        okRate = qualifiedNum / totalNum;
        okRate = safeRate(okRate);
    }
    return {
        qualifiedNum,
        totalNum,
        okRate
    };
}

/**
 * 计算性能指数
 * @returns {Promise<void>}
 */
async function calcPerformanceRate(device, realWorkTimeMills, totalNum, efficiency) {
    totalNum = Number(totalNum);
    efficiency = Number(efficiency);

    let performanceRate = 0;
    if (totalNum > 0 && efficiency > 0) {
        // 预期生产数
        let expectQualifiedNum = (realWorkTimeMills / ONE_MIN_MILLIONS) * efficiency;
        if (expectQualifiedNum) {
            performanceRate = safeRate(totalNum / expectQualifiedNum);
        }
    }
    return performanceRate;
}

/**
 * 计算oee
 * @param activateRate
 * @param okRate
 * @param performanceRate
 * @returns {Promise<number>}
 */
async function calcOeeRate(activateRate, okRate, performanceRate) {
    let oeeRate = okRate * activateRate * performanceRate;
    oeeRate = safeRate(oeeRate);

    return oeeRate;
}

/**
 * 计算每台设备的所有值
 */
async function calcEachDevice(device, cuttedTimes, variableMap) {
    const {deviceCode, efficiencyVarId, performanceRateId, cropRateId, okRateId, oeeRateId} = device;
    // 能效变量
    const efficiency = variableMap[efficiencyVarId];
    const resultPromises = [];

    // 稼动率计算
    const activationRateObject = await calcActivation(device, cuttedTimes);
    resultPromises.push(vs.setVariable(cropRateId, convertRateString(activationRateObject['activationRate'])));

    // 良率计算
    const okRateObject = await calcOkRate(device);
    resultPromises.push(vs.setVariable(okRateId, convertRateString(okRateObject['okRate'])));

    // 计算性能指数
    const {
        qualifiedNum,
        totalNum,
    } = okRateObject;
    const {
        realWorkTimeMills,
        workTimeMills,
    } = activationRateObject;
    const performanceRate = await calcPerformanceRate(device, realWorkTimeMills, totalNum, efficiency);
    resultPromises.push(vs.setVariable(performanceRateId, convertRateString(performanceRate)));

    // 计算oee
    const oeeRate = await calcOeeRate(activationRateObject['activationRate'], okRateObject['okRate'], performanceRate);
    resultPromises.push(vs.setVariable(oeeRateId, convertRateString(oeeRate)));

    await Promise.all(resultPromises);

    // 最后返回的数据
    return {
        activationRateObject,
        deviceCode,
        realWorkTimeMills,
        workTimeMills, // 总工作时长
        qualifiedNum,
        totalNum,
        efficiency
    }
}

/**
 * 主逻辑
 * @param {*} config 设备配置
 * @param {*} odIP od服务器IP
 * @param {*} odPort od服务器端口
 */
const mainLogic = async function (configStr, ip, port) {
    const d = moment();
    // 1. 校验入参
    let configObj = JSON.parse(configStr);
    if (!configObj || !Array.isArray(configObj.devices)) {
        throw new Error('无效的入参配置');
    }

    let devices = configObj.devices;
    let deviceCodeStr = devices.map(i => i.deviceCode).join(',');
    const deviceTimesMap = await getDeviceProductionTime(deviceCodeStr, d, ip, port);

    // 从ol中取出变量值
    let inputIds = [];
    devices.forEach(item => {
        let qualifiedNumVarId = Number(item['qualifiedNumVarId']);
        if (qualifiedNumVarId > 0) {
            inputIds.push(qualifiedNumVarId);
        }

        let totalNumVarId = Number(item['totalNumVarId']);
        if (totalNumVarId > 0) {
            inputIds.push(totalNumVarId);
        }

        let efficiencyVarId = Number(item['efficiencyVarId']);
        if (efficiencyVarId > 0) {
            inputIds.push(efficiencyVarId);
        }
    });
    let variables = await vs.getVariablesByIds(inputIds.join(','));
    let variableMap = {};
    for (let item of variables) {
        variableMap[item.id] = item.value;
    }

    let promises = []; // 将每个设备计算的promise推到这里面,一总执行
    // 遍历devices
    for (let device of devices) {
        //  拿到分割的时间段进行处理
        let deviceCuttedTimes = cutCurrentPeriodTime(deviceTimesMap[device['deviceCode']]);
        let deviceStatusVarId = Number(device['deviceStatusVarId']);
        //  如果没有设备状态变量或时间段,跳过
        if (Number.isNaN(deviceStatusVarId) || deviceStatusVarId <= 0 || !deviceCuttedTimes) {
            continue;
        }
        // let obj = await calcEachDevice(device, deviceCuttedTimes, variableMap);
        promises.push(calcEachDevice(device, deviceCuttedTimes, variableMap))
    }

    // 每个设备执行完毕
    let results = await Promise.all(promises);

    /*********** 计算总值 **********/
    let totalRunningMillions = 0; // 总实际运行时间
    let totalPlanWorkingMillions = 0; // 总计划运行时间
    let totalQualifiedNum = 0; // 总良品数
    let totalOutputNum = 0; // 总实际产量
    let totalExceptOutputNum = 0; // 总预期生产数
    results.forEach(i => {
        totalRunningMillions += i.realWorkTimeMills;
        totalPlanWorkingMillions += i.workTimeMills;
        totalQualifiedNum += i.qualifiedNum;
        totalOutputNum += i.totalNum;
        totalExceptOutputNum += (i.realWorkTimeMills / ONE_MIN_MILLIONS) * i.efficiency;
    });
    // 计划生产数保留整数
    totalExceptOutputNum = parseInt(totalExceptOutputNum);
    // 总稼动率
    let totalCropRate = 0;
    if (totalPlanWorkingMillions > 0) {
        totalCropRate = safeRate(totalRunningMillions / totalPlanWorkingMillions);
    }
    // 总良率
    let totalOkRate = 0;
    if (totalOutputNum > 0) {
        totalOkRate = safeRate(totalQualifiedNum / totalOutputNum);
    }
    // 总能效系数
    let totalPerformace = 0;
    if (totalExceptOutputNum > 0) {
        totalPerformace = safeRate(totalOutputNum / totalExceptOutputNum);
    }
    // 总OEE
    let totalOee = safeRate(totalCropRate * totalOkRate * totalPerformace);

    /*********** 设置总值 **********/
    let totalSetPromises = [];
    const {debugVarId, totalCropRateId, totalOkRateId, totalOeeRateId, totalPlanRunningId, totalRealRunningId, totalExpectOutputId} = configObj;
    // 设置总稼动率
    if (totalCropRateId) {
        totalSetPromises.push(vs.setVariable(totalCropRateId, convertRateString(totalCropRate)));
    }
    // 设置总良率
    if (totalOkRateId) {
        totalSetPromises.push(vs.setVariable(totalOkRateId, convertRateString(totalOkRate)));
    }
    // 设置总oee
    if (totalOeeRateId) {
        totalSetPromises.push(vs.setVariable(totalOeeRateId, convertRateString(totalOee)));
    }
    // 设置总计划运行时间
    if (totalPlanRunningId) {
        totalSetPromises.push(vs.setVariable(totalPlanRunningId, totalPlanWorkingMillions));
    }
    // 设置总实际运行时间
    if (totalRealRunningId) {
        totalSetPromises.push(vs.setVariable(totalRealRunningId, totalRunningMillions));
    }
    // 设置总预期生产数
    if (totalExpectOutputId) {
        totalSetPromises.push(vs.setVariable(totalExpectOutputId, totalExceptOutputNum));
    }

    await Promise.all(totalSetPromises);

    /*********** debug变量 **********/
    if (debugVarId) {
        let debugObj = {
            deviceTimesMap: deviceTimesMap,
            devices: results
        };
        await vs.setVariable(debugVarId, JSON.stringify(debugObj));
    }
};

// const config = {
//     "totalCropRateId": 4111,
//     "totalOkRateId": 4112,
//     "totalOeeRateId": 4113,
//     "totalPlanRunningId": 4114,
//     "totalRealRunningId": 4115,
//     "totalExpectOutputId": 4119,
//     "debugVarId": 4134,
//     "devices": [{
//         "deviceCode": "HDFA01",
//         "deviceStatusVarId": 3775,
//         "qualifiedNumVarId": 4278,
//         "totalNumVarId": 4277,
//         "efficiencyVarId": 3816,
//         "performanceRateId": 4037,
//         "cropRateId": 3737,
//         "okRateId": 3725,
//         "oeeRateId": 3749
//     }, {
//         "deviceCode": "HDFA02",
//         "deviceStatusVarId": 3776,
//         "qualifiedNumVarId": 4284,
//         "totalNumVarId": 4283,
//         "efficiencyVarId": 3817,
//         "performanceRateId": 4038,
//         "cropRateId": 3738,
//         "okRateId": 3726,
//         "oeeRateId": 3750
//     }, {
//         "deviceCode": "HDFA03",
//         "deviceStatusVarId": 3777,
//         "qualifiedNumVarId": 4290,
//         "totalNumVarId": 4289,
//         "efficiencyVarId": 3818,
//         "performanceRateId": 4039,
//         "cropRateId": 3739,
//         "okRateId": 3727,
//         "oeeRateId": 3751
//     }, {
//         "deviceCode": "HDFA04",
//         "deviceStatusVarId": 3778,
//         "qualifiedNumVarId": 4296,
//         "totalNumVarId": 4295,
//         "efficiencyVarId": 3819,
//         "performanceRateId": 4040,
//         "cropRateId": 3740,
//         "okRateId": 3728,
//         "oeeRateId": 3752
//     }, {
//         "deviceCode": "HDFA05",
//         "deviceStatusVarId": 3779,
//         "qualifiedNumVarId": 4302,
//         "totalNumVarId": 4301,
//         "efficiencyVarId": 3820,
//         "performanceRateId": 4041,
//         "cropRateId": 3741,
//         "okRateId": 3729,
//         "oeeRateId": 3753
//     }, {
//         "deviceCode": "HDFA06",
//         "deviceStatusVarId": 3780,
//         "qualifiedNumVarId": 4308,
//         "totalNumVarId": 4307,
//         "efficiencyVarId": 3821,
//         "performanceRateId": 4042,
//         "cropRateId": 3742,
//         "okRateId": 3730,
//         "oeeRateId": 3754
//     }, {
//         "deviceCode": "HDFA07",
//         "deviceStatusVarId": 3781,
//         "qualifiedNumVarId": 4314,
//         "totalNumVarId": 4313,
//         "efficiencyVarId": 3822,
//         "performanceRateId": 4043,
//         "cropRateId": 3743,
//         "okRateId": 3731,
//         "oeeRateId": 3755
//     }, {
//         "deviceCode": "HDFA08",
//         "deviceStatusVarId": 3782,
//         "qualifiedNumVarId": 4320,
//         "totalNumVarId": 4319,
//         "efficiencyVarId": 3823,
//         "performanceRateId": 4044,
//         "cropRateId": 3744,
//         "okRateId": 3732,
//         "oeeRateId": 3756
//     }, {
//         "deviceCode": "HDFA09",
//         "deviceStatusVarId": 3783,
//         "qualifiedNumVarId": 326,
//         "totalNumVarId": 4325,
//         "efficiencyVarId": 3824,
//         "performanceRateId": 4045,
//         "cropRateId": 3745,
//         "okRateId": 3733,
//         "oeeRateId": 3757
//     }, {
//         "deviceCode": "HDFA10",
//         "deviceStatusVarId": 3784,
//         "qualifiedNumVarId": 4332,
//         "totalNumVarId": 4331,
//         "efficiencyVarId": 3825,
//         "performanceRateId": 4046,
//         "cropRateId": 3746,
//         "okRateId": 3734,
//         "oeeRateId": 3758
//     }, {
//         "deviceCode": "HDFA11",
//         "deviceStatusVarId": 3785,
//         "qualifiedNumVarId": 4338,
//         "totalNumVarId": 4337,
//         "efficiencyVarId": 3840,
//         "performanceRateId": 4047,
//         "cropRateId": 3747,
//         "okRateId": 3735,
//         "oeeRateId": 3759
//     }, {
//         "deviceCode": "HDFA12",
//         "deviceStatusVarId": 3786,
//         "qualifiedNumVarId": 4344,
//         "totalNumVarId": 4343,
//         "efficiencyVarId": 3827,
//         "performanceRateId": 4048,
//         "cropRateId": 3748,
//         "okRateId": 3736,
//         "oeeRateId": 3760
//     }]
// }
// const ip = 'opendevice.liwinon.com';
// const port = '9090';
// mainLogic(JSON.stringify(config), ip, port);

module.exports = {
    main: mainLogic
};
console 命令行工具 X clear

                    
>
console