东丽网口版透析机 socket- server 通讯
chenyc
22 小时以前 55c8181bbe4c198f9bda5520ea0d8ba148933f9e
index.js
@@ -1,10 +1,40 @@
// ✅ 正确写法(适用于 pkg 打包环境)
const fs = require('fs');
const path = require('path');
// 获取 exe 所在目录(兼容 pkg 打包后的 __dirname)
const appPath = process.pkg ? path.dirname(process.execPath) : __dirname;
const mqttConfigPath = path.join(appPath, 'mqtt.json');
const aliyunConfigPath = path.join(appPath, 'aliyun.json');
const httpConfigPath = path.join(appPath, 'httpConfig.json');
const homeConfigPath = path.join(appPath, 'homeConfig.json');
const mqttConfig = JSON.parse(fs.readFileSync(mqttConfigPath, 'utf8'));
const aliyunConfig = JSON.parse(fs.readFileSync(aliyunConfigPath, 'utf8'));
const httpConfig = JSON.parse(fs.readFileSync(httpConfigPath, 'utf8'));
const homeConfig = JSON.parse(fs.readFileSync(homeConfigPath, 'utf8'));
console.log(aliyunConfig)
const { initMqtt, publishMessage } = require('./mqttClient');
const net = require('net');
const logger = require('./logger');
const EventEmitter = require('events');
const aliyunIot = require('aliyun-iot-device-sdk');
const { getAliyunDeviceSecret } = require('./api');
const toModel = require('./Strholp');
const { publishMessage } = require('./mqttClient');
const dataCache = require('./dataCache');
const HttpServer = require('./httpServer');
// 初始化 MQTT(独立于阿里云)
initMqtt(mqttConfig);
// ========== 自定义错误类(可选)==========
class CustomError extends Error {
@@ -16,7 +46,7 @@
// ========== 常量配置 ==========
const MAX_BUFFER_SIZE = 500;      // 缓冲区最大长度
const RETRY_INTERVAL_MS = 10000;  // 重试间隔 10s
const RETRY_INTERVAL_MS = 30000;  // 重试间隔 30s
const KEEP_ALIVE_INTERVAL_MS = 60000; // 保活间隔 60s
const DEVICE_TIMEOUT_MS = 120000; // 设备无响应超时 2分钟
@@ -39,7 +69,12 @@
    }
    handleDevice(socket) {
        const deviceId = socket.remoteAddress + ':' + socket.remotePort;
        // 处理 IPv6 映射 IPv4 地址 (::ffff:127.0.0.1 -> 127.0.0.1)
        let remoteAddress = socket.remoteAddress;
        if (remoteAddress.startsWith('::ffff:')) {
            remoteAddress = remoteAddress.slice(7); // 移除 ::ffff: 前缀
        }
        const deviceId = remoteAddress + ':' + socket.remotePort;
        logger.info(`建立新连接: ${deviceId}`);
        const deviceInfo = {
@@ -85,7 +120,7 @@
                const message = buffer.substring(startIdx, endIdx).trim();
                buffer = buffer.substring(endIdx + 2); // 移除已处理部分(含 \r\n)
                logger.info(`${deviceId} 接收到完整消息: ${message}`);
                logger.info(`${deviceId} 接收到完整消息: ${randomLetters(20)}${message}${randomLetters(20)}`);
                this.handleData(deviceId, message);
            }
        });
@@ -110,7 +145,7 @@
            if (deviceInfo.status === 'pending' || deviceInfo.status === 'invalid') {
                // 握手阶段:在 'K' 和 'K0000' 之间切换(你的原始逻辑)
                deviceInfo.lastSignal = deviceInfo.lastSignal === 'K' ? 'K0000' : 'K';
                logger.info(`重试发送 '${deviceInfo.lastSignal}' 给设备 ${deviceId}`);
                logger.info(`重试发送 '${randomLetters(10)}${deviceInfo.lastSignal==='K'?'a':'b'}${randomLetters(10)}' 给设备 ${deviceId}`);
                this.sendKeepAliveToDevice(deviceId);
            }
        }, RETRY_INTERVAL_MS);
@@ -149,7 +184,7 @@
        try {
            deviceInfo.socket.write(`${deviceInfo.lastSignal}\r\n`);
            logger.info(`发送信号 '${deviceInfo.lastSignal}' 给设备 ${deviceId}`);
            logger.info(`发送信号 '${randomLetters(10)}${deviceInfo.lastSignal==='K'?'a':'b'}${randomLetters(10)}' 给设备 ${deviceId}`);
        } catch (err) {
            logger.error(`发送信号失败 ${deviceId}:`, err.message);
            this.removeDevice(deviceId);
@@ -159,33 +194,58 @@
    async handleData(deviceId, message) {
        const deviceInfo = this.devices.get(deviceId);
        if (!deviceInfo) return;
        deviceInfo.lastAck = Date.now();
        try {
            const masData = toModel(message);
            const ipAddress = deviceId
            const masData = toModel(message, ipAddress);
            deviceInfo.iotDeviceNo = masData.n;
            deviceInfo.masData = masData;
            // ✅【新增】缓存数据到内存(按设备序号)
            dataCache.setDeviceData(masData.n, masData);
            // ✅【核心改动】收到数据立即发 MQTT(不管阿里云)
            if (mqttConfig.enabled) {
                const topic = `${mqttConfig.defaultTopicPrefix}/${masData.n}`;
                const payload = JSON.stringify({
                    ...masData,
                    deviceId: deviceId,
                    timestamp: new Date().toISOString()
                });
                publishMessage(topic, payload);
                logger.info(`📡 已通过 MQTT 发送数据到 ${topic}`);
            }
            if (deviceInfo.status !== 'valid') {
                deviceInfo.status = 'valid';
                this.emit('validResponse', deviceId);
                logger.info(`${deviceId} 首次有效响应,停止重试`);
                this.stopRetryMechanism(deviceId);
                // 成功后固定使用 'K0000' 保活(建议)
                this.startKeepAlive(deviceId, deviceInfo.lastSignal);
                await this.registerDevice(deviceId);
            // 【可选】仅当阿里云启用时才注册并上报
            if (aliyunConfig.enabled) {
                if (deviceInfo.status !== 'valid') {
                    logger.info(`${deviceId} 阿里云设备状态变为有效`);
                    deviceInfo.status = 'valid';
                    this.stopRetryMechanism(deviceId);
                    logger.info(`${deviceId} 停止重试机制`);
                    this.startKeepAlive(deviceId, deviceInfo.lastSignal);
                    logger.info(`${deviceId} 启动保持连接机制`);
                    await this.registerAliyunDevice(deviceId); // 改名避免混淆
                } else {
                    this.postPropsToAliyun(deviceId);
                }
            } else {
                logger.info(`${deviceId} 已注册,直接上报数据到阿里云`);
                this.postPropsToDevice(deviceId);
                // this.onDeviceDataReceived(deviceId, masData);
                // 阿里云未启用,但已通过 MQTT 发送,无需其他操作
                if (deviceInfo.status !== 'valid') {
                    deviceInfo.status = 'valid';
                    this.stopRetryMechanism(deviceId);
                    this.startKeepAlive(deviceId, deviceInfo.lastSignal);
                    logger.info(`${deviceId} 已完成握手(阿里云未启用)`);
                }
            }
        } catch (err) {
            logger.error(`${deviceId} 处理消息出错:`, err.message);
        }
    }
    async registerDevice(deviceId) {
    async registerAliyunDevice(deviceId) {
        const deviceInfo = this.devices.get(deviceId);
        if (!deviceInfo || deviceInfo.iotDevice) return;
@@ -193,7 +253,7 @@
            logger.info(`${deviceId} 请求三元组,设备号: ${deviceInfo.iotDeviceNo}`);
            const { data } = await getDeviceSYZ(deviceInfo.iotDeviceNo);
            logger.info(`${deviceId} 三元组返回: ${JSON.stringify(data)}`);
            const model=data.data
            const model = data.data
            if (model?.productKey && model?.deviceName && model?.deviceSecret) {
                deviceInfo.iotDevice = aliyunIot.device({
                    ProductKey: model.productKey,
@@ -214,33 +274,18 @@
        }
    }
    postPropsToDevice(deviceId) {
    postPropsToAliyun(deviceId) {
        const deviceInfo = this.devices.get(deviceId);
        if (!deviceInfo?.iotDevice || !deviceInfo.masData) return;
        logger.info(`${deviceId} 阿里云上报属性: ${JSON.stringify(deviceInfo.masData)}`);
        const props = deviceInfo.masData;
        deviceInfo.iotDevice.postProps(props, (res) => {
            if (res?.message === 'success') {
                logger.info(`${deviceId} 上报属性成功`);
                logger.info(`${deviceId} 阿里云上报属性成功`);
            } else {
                logger.error(`${deviceId} 上报属性失败:`, res?.message || 'unknown');
                logger.error(`${deviceId} 阿里云上报属性失败:`, res?.message || 'unknown');
            }
        });
    }
    onDeviceDataReceived(deviceId, data) {
        const topic = `touxiji/${data.n}`;
        const payload = JSON.stringify({
            ...data,
            timestamp: new Date().toISOString()
        });
        try {
            logger.info(`发布 MQTT 消息到主题 ${topic}: ${payload}`);
            publishMessage(topic, payload);
        } catch (error) {
            logger.error(`MQTT 发布失败 ${topic}:`, error.message);
        }
    }
    removeDevice(deviceId) {
@@ -273,6 +318,17 @@
        throw new CustomError("获取三元组失败", err);
    }
}
// 生成随机字母字符串
function randomLetters(length) {
  const chars = 'abcdefghijklmnopqrstuvwxyz';
  let result = '';
  for (let i = 0; i < length; i++) {
    result += chars.charAt(Math.floor(Math.random() * chars.length));
  }
  return result;
}
// ========== 启动服务器 ==========
const manager = new DeviceManager(); // ✅ 单例!
@@ -289,7 +345,12 @@
    manager.handleDevice(socket);
});
const PORT = process.env.PORT || 10961;
const PORT =  homeConfig.socketPort || 10961;
server.listen(PORT, () => {
    logger.info(`Socket 服务已启动,监听端口: ${PORT}`);
});
    logger.info(`Socket 服务已启动,监听超级端口: ${PORT}`);
});
// ========== 启动 HTTP 服务 ==========
const HTTP_PORT = process.env.HTTP_PORT || httpConfig.port || 8080;
const httpServer = new HttpServer(HTTP_PORT, httpConfig);
httpServer.start();