费森4008s 网口通讯 ,原生串口透传网口
chenyc
7 天以前 11e1b9d3ed909b28ef8e3330d152730c4e6d67c6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
// aliyun-iot.js
const IotDevice = require('alibabacloud-iot-device-sdk');
const logger = require('./logger');
const { getAliyunDeviceSecret, getAliyunConfig } = require('./api');
 
// 固定设备信息(可被配置覆盖)
const PRODUCT_KEY = 'k08fzZOZdxN';
// 可选的默认设备密钥(仅用于兼容单设备调试),实际应按设备序列号获取专属密钥
const DEFAULT_DEVICE_SECRET = null;
 
// 存储设备实例(长连接复用)
const devices = new Map();
 
/**
 * 获取或创建设备实例(长连接)
 * @param {string} deviceId
 * @return {Object|null} 设备实例 或 null(创建失败)
 */
// 统一封装:按设备号从你的服务获取三元组
const aliyunCfg = getAliyunConfig();
let configLogged = false;
 
async function fetchDeviceTriple(deviceId, log) {
    try {
        (log || logger).info(`${deviceId} 请求三元组,设备号: ${deviceId}`);
        const resp = await getAliyunDeviceSecret(aliyunCfg.secretApiPath || 'device/info/getAliyunDeviceSecret', deviceId);
        const body = resp && resp.data ? resp.data : resp;
        // 兼容不同返回结构:{code, data:{productKey, deviceName, deviceSecret}} 或 {productKey, deviceName, deviceSecret}
        const payload = body && body.data ? body.data : body;
        const pk = payload?.productKey || aliyunCfg.productKeyOverride || PRODUCT_KEY;
        const dn = payload?.deviceName || deviceId;
        const ds = payload?.deviceSecret || null;
        if (!ds) {
            throw new Error('未获取到 deviceSecret');
        }
        return { productKey: pk, deviceName: dn, deviceSecret: ds };
    } catch (err) {
        (log || logger).error(`${deviceId} 获取三元组失败: ${err.message}`);
        return null;
    }
}
 
async function getOrCreateDevice(deviceId, ctx) {
    const log = logger.forDevice(deviceId, ctx?.ip, ctx?.clientId);
    if (!deviceId || typeof deviceId !== 'string') {
        log.error('无效的 deviceId', { deviceId });
        return null;
    }
 
    const existingDevice = devices.get(deviceId);
    if (existingDevice) {
        return existingDevice;
    }
 
    try {
        // 尝试为该设备ID获取独立的 deviceSecret
        const triple = await fetchDeviceTriple(deviceId, log);
        if (!triple) return null;
 
        const { productKey, deviceName, deviceSecret } = triple;
        const region = aliyunCfg.region || 'cn-shanghai';
        const host = `${productKey}.iot-as-mqtt.${region}.aliyuncs.com`;
 
        if (!configLogged) {
            configLogged = true;
            log.info('aliyun.config', {
                source: aliyunCfg.__meta?.source,
                path: aliyunCfg.__meta?.path,
                region,
                productKeyOverride: aliyunCfg.productKeyOverride || '',
                apiBaseUrl: aliyunCfg.apiBaseUrl,
                secretApiPath: aliyunCfg.secretApiPath,
            });
        }
 
        const device = IotDevice.device({
            productKey,
            deviceName,
            deviceSecret,
            host,
            port: 1883,
            keepalive: 60,
            clean: true,
            reconnectPeriod: 5000 // 自动重连间隔
        });
 
        device.on('connect', () => {
            log.info(`✅ 设备 ${deviceId} 已连接到阿里云 IoT`);
        });
 
        device.on('reconnect', () => {
            log.warn(`🔁 设备 ${deviceId} 正在重连...`);
        });
 
        device.on('error', (err) => {
            log.error(`❌ 设备 ${deviceId} 发生错误`, {
                错误: err.message,
                堆栈: err.stack
            });
        });
 
        device.on('offline', () => {
            log.warn(`⚠️ 设备 ${deviceId} 已离线`);
        });
 
        // 缓存实例
        devices.set(deviceId, device);
 
        return device;
 
    } catch (err) {
        log.error('创建设备实例失败', {
            错误: err.message,
            堆栈: err.stack,
            deviceId
        });
        return null;
    }
}
 
/**
 * 发送数据到阿里云 IoT(高频上报,长连接)
 * @param {string} deviceId
 * @param {Object} data
 */
async function sendDataToAliyun(deviceId, data, ctx) {
    const log = logger.forDevice(deviceId, ctx?.ip, ctx?.clientId);
    // 参数校验
    if (!deviceId || typeof deviceId !== 'string' || !data || typeof data !== 'object') {
        log.error('sendDataToAliyun 参数错误', { deviceId, data: typeof data });
        return;
    }
 
    let device;
    try {
        device = await getOrCreateDevice(deviceId, ctx);
        if (!device) {
            log.error('无法创建设备实例', { deviceId });
            return;
        }
 
        // 如果未连接,不阻塞,直接返回(可选:记录日志)
        if (!device.connected) {
            log.warn(`设备 ${deviceId} 当前未连接,跳过发送`);
            return;
        }
 
        // 发送数据(可配置是否打印 payload)
        if (aliyunCfg.printPayload) {
            console.log(`\n🚀 Aliyun IoT postProps 发送 [${deviceId}] JSON:`);
            try {
                console.log(JSON.stringify(data, null, 2));
            } catch (_) {
                console.log(data);
            }
        }
        device.postProps(data, (res) => {
            console.log('Aliyun IoT postProps 回调:', res);
            if (res?.message === 'success') {
                // 成功:无需日志(太频繁),可注释
                log.debug(`📡 数据已发送`, { deviceId });
            } else {
                const errorMsg = res?.message || '未知错误';
                log.error(`❌ 发送失败`, { deviceId, 错误: errorMsg, data });
            }
        });
 
    } catch (err) {
        log.error('sendDataToAliyun 执行异常', {
            错误: err.message,
            堆栈: err.stack,
            deviceId,
            data
        });
    }
}
 
/**
 * 关闭所有设备连接(用于进程退出)
 */
function closeAllConnections() {
    devices.forEach((device, id) => {
        try {
            device.end();
            logger.forDevice(id).info(`已关闭设备连接: ${id}`);
        } catch (err) {
            logger.forDevice(id).error(`关闭设备 ${id} 失败`, { 错误: err.message });
        }
    });
    devices.clear();
}
 
// 优雅退出
process.on('SIGINT', closeAllConnections);
process.on('SIGTERM', closeAllConnections);
 
module.exports = {
    sendDataToAliyun,
    closeAllConnections // 可选导出,用于主程序调用
};