const mqtt = require('mqtt') const { v4: uuidv4 } = require('uuid'); // 顶部添加 const Framework = require('../../../framework/node-core-framework'); const logs = require('../logProxy'); // 获取logsService class MqttSyncClient { constructor(brokerUrl, options = {}) { this.client = mqtt.connect(brokerUrl, options) this.isConnected = false this.messageListeners = [] this.client.on('connect', () => { this.isConnected = true console.log('MQTT 服务端已连接') }) this.client.on('message', (topic, message) => { message = JSON.parse(message.toString()) console.log('MQTT 收到消息', topic, message) this.messageListeners.forEach(listener => listener(topic, message)) }) this.client.on('error', (err) => { console.warn('[MQTT] Error:', err.message) }) } waitForConnect(timeout = 5000) { return new Promise((resolve, reject) => { if (this.isConnected) return resolve() const timer = setTimeout(() => { reject(new Error('MQTT connect timeout')) }, timeout) const check = () => { if (this.isConnected) { clearTimeout(timer) resolve() } else { setTimeout(check, 100) } } check() }) } publish(topic, message, options = {}) { return new Promise((resolve, reject) => { this.client.publish(topic, message, options, (err) => { if (err) reject(err) else resolve() }) }) } subscribe(topic, callback = null, options = {}) { return new Promise((resolve, reject) => { this.client.subscribe(topic, options, (err, granted) => { if (err) { reject(err) } else { // 如果提供了回调函数,添加到消息监听器 if (callback && typeof callback === 'function') { const messageHandler = (responseTopic, message) => { if (responseTopic !== topic) return try { callback(topic, message) } catch (error) { console.warn(`[MQTT] 处理订阅消息失败: ${error.message}`) } } // 添加到消息监听器 this.addMessageListener(messageHandler) // 在订阅成功后返回订阅信息和消息处理器 resolve({ granted, messageHandler, topic }) } else { resolve(granted) } } 1 }) }) } /** * 发布指令并等待某个主题返回(模拟同步),通过uuid确定唯一项 * @param {*} requestTopic * @param {*} requestMessage * @param {*} responseTopic * @param {*} timeout */ async publishAndWait(sn_code, requestMessage, timeout = 60 * 3 * 1000) { return new Promise((resolve, reject) => { console.log(`[MQTT指令] 发送到主题: request_${sn_code}`, requestMessage); const uuid = uuidv4(); // 将uuid加入消息体,建议使用JSON格式 const msgObj = typeof requestMessage === 'object' ? { ...requestMessage, uuid } : { data: requestMessage, uuid }; const sendMsg = JSON.stringify(msgObj); const timer = setTimeout(() => { this.removeMessageListener(onMessage); reject(new Error('Timeout waiting for response')); }, timeout); const onMessage = (topic, message) => { let { uuid: responseUuid } = message if (topic === 'response' && responseUuid === uuid) { clearTimeout(timer); this.removeMessageListener(onMessage); resolve(message) } } this.addMessageListener(onMessage) this.publish(`request_${sn_code}`, sendMsg).catch(err => { clearTimeout(timer); this.removeMessageListener(onMessage); reject(err); }); }); } addMessageListener(fn) { this.messageListeners.push(fn) } removeMessageListener(fn) { this.messageListeners = this.messageListeners.filter(f => f !== fn) } /** * 取消订阅主题 * @param {string} topic 要取消订阅的主题 * @param {object} subscriptionInfo 订阅时返回的订阅信息 */ async unsubscribe(topic, subscriptionInfo = null) { return new Promise((resolve, reject) => { this.client.unsubscribe(topic, (err) => { if (err) { reject(err) } else { // 如果提供了订阅信息,移除对应的消息处理器 if (subscriptionInfo && subscriptionInfo.messageHandler) { this.removeMessageListener(subscriptionInfo.messageHandler) } resolve() } }) }) } end(force = false) { this.client.end(force) } } module.exports = MqttSyncClient