|
@@ -1,164 +1,182 @@
|
|
-// mqttService.js
|
|
|
|
-import mqtt from './mqtt'
|
|
|
|
|
|
+import mqtt from './mqtt.js';
|
|
|
|
|
|
class MqttService {
|
|
class MqttService {
|
|
constructor() {
|
|
constructor() {
|
|
if (!MqttService.instance) {
|
|
if (!MqttService.instance) {
|
|
- this.cmdClient = null
|
|
|
|
- this.dataClient = null
|
|
|
|
- this.cmdConnected = false
|
|
|
|
- this.dataConnected = false
|
|
|
|
- MqttService.instance = this
|
|
|
|
|
|
+ this.cmdClient = null;
|
|
|
|
+ this.dataClient = null;
|
|
|
|
+ this.cmdConnected = false;
|
|
|
|
+ this.dataConnected = false;
|
|
|
|
+ this.connections = new Map(); // 内部管理多连接
|
|
|
|
+ MqttService.instance = this;
|
|
}
|
|
}
|
|
- return MqttService.instance
|
|
|
|
|
|
+ return MqttService.instance;
|
|
}
|
|
}
|
|
|
|
|
|
// 连接 CMD
|
|
// 连接 CMD
|
|
connectCmd(userId) {
|
|
connectCmd(userId) {
|
|
if (this.cmdConnected && this.cmdClient) {
|
|
if (this.cmdConnected && this.cmdClient) {
|
|
- console.log("CMD MQTT 已连接,复用现有实例")
|
|
|
|
- return this.cmdClient
|
|
|
|
- }
|
|
|
|
- console.log("CMD MQTT 连接中...")
|
|
|
|
- const params = {
|
|
|
|
- clientId: "xcx_mqtt_cmd1_" + userId + "_" + Math.random().toString(16).substring(2, 8),
|
|
|
|
- username: "lnradar",
|
|
|
|
- password: "lnradar",
|
|
|
|
- wsOptions: {
|
|
|
|
- WebSocket: function (url) {
|
|
|
|
- return wx.connectSocket({
|
|
|
|
- url,
|
|
|
|
- header: { "content-type": "application/json" },
|
|
|
|
- protocols: ["mqtt"],
|
|
|
|
- })
|
|
|
|
- },
|
|
|
|
- },
|
|
|
|
|
|
+ console.log("CMD MQTT 已连接,复用现有实例");
|
|
|
|
+ return Promise.resolve(this.cmdClient);
|
|
}
|
|
}
|
|
|
|
|
|
- this.cmdClient = mqtt.connect("wxs://cmd.radar-power.cn/mqtt/", params)
|
|
|
|
-
|
|
|
|
- this.cmdClient.on("connect", () => {
|
|
|
|
- this.cmdConnected = true
|
|
|
|
- console.log("CMD MQTT 连接成功")
|
|
|
|
- uni.$emit("mqtt-ready", this.cmdClient)
|
|
|
|
- this.cmdClient.subscribe(`/mps/wx_${userId}/notice`, (err) => {
|
|
|
|
- if (err) {
|
|
|
|
- console.error("CMD 订阅失败", err)
|
|
|
|
- } else {
|
|
|
|
- console.log(`成功订阅设备主题: /mps/wx_${userId}/notice`)
|
|
|
|
- }
|
|
|
|
- })
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- this.cmdClient.on("message", (topic, message) => {
|
|
|
|
- console.log("CMD 接收到消息:", topic, JSON.parse(message.toString()))
|
|
|
|
- })
|
|
|
|
|
|
+ const brokerName = 'CMD';
|
|
|
|
+ const url = 'wxs://cmd.radar-power.cn/mqtt/';
|
|
|
|
+ const clientId = `xcx_mqtt_cmd1_${userId}_${Math.random().toString(16).substring(2, 8)}`;
|
|
|
|
|
|
- this.cmdClient.on("error", (err) => {
|
|
|
|
- this.cmdConnected = false
|
|
|
|
- console.error("CMD MQTT 连接错误:", err)
|
|
|
|
- })
|
|
|
|
|
|
+ return this.connectToBroker(brokerName, { url, clientId, username: 'lnradar', password: 'lnradar' })
|
|
|
|
+ .then(client => {
|
|
|
|
+ this.cmdClient = client;
|
|
|
|
+ this.cmdConnected = true;
|
|
|
|
|
|
- this.cmdClient.on("disconnect", () => {
|
|
|
|
- this.cmdConnected = false
|
|
|
|
- console.log("CMD MQTT 断开")
|
|
|
|
- })
|
|
|
|
|
|
+ // 默认订阅 CMD 主题
|
|
|
|
+ this.subscribe('CMD', `/mps/wx_${userId}/notice`, (msg) => {
|
|
|
|
+ console.log('CMD 消息:', msg);
|
|
|
|
+ });
|
|
|
|
|
|
- return this.cmdClient
|
|
|
|
|
|
+ uni.$emit('mqtt-ready', this.cmdClient);
|
|
|
|
+ return client;
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
// 连接 DATA
|
|
// 连接 DATA
|
|
connectData(userId) {
|
|
connectData(userId) {
|
|
if (this.dataConnected && this.dataClient) {
|
|
if (this.dataConnected && this.dataClient) {
|
|
- console.log("DATA MQTT 已连接,复用现有实例")
|
|
|
|
- return this.dataClient
|
|
|
|
- }
|
|
|
|
- console.log("DATA MQTT 连接中...")
|
|
|
|
- const params = {
|
|
|
|
- clientId: "xcx_mqtt_data1_" + userId + "_" + Date.now(),
|
|
|
|
- username: "lnradar",
|
|
|
|
- password: "lnradar",
|
|
|
|
- WebSocket: function (url) {
|
|
|
|
- return wx.connectSocket({
|
|
|
|
- url,
|
|
|
|
- header: { "content-type": "application/json" },
|
|
|
|
- protocols: ["mqtt"],
|
|
|
|
- })
|
|
|
|
- },
|
|
|
|
|
|
+ console.log("DATA MQTT 已连接,复用现有实例");
|
|
|
|
+ return Promise.resolve(this.dataClient);
|
|
}
|
|
}
|
|
|
|
|
|
- this.dataClient = mqtt.connect("wxs://data.radar-power.cn/mqtt/", params)
|
|
|
|
-
|
|
|
|
- this.dataClient.on("connect", () => {
|
|
|
|
- this.dataConnected = true
|
|
|
|
- uni.$emit("mqttData-ready", this.dataClient)
|
|
|
|
- console.log("DATA MQTT 连接成功")
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- this.dataClient.on("message", (topic, message) => {
|
|
|
|
- console.log("DATA 接收到消息:", topic, JSON.parse(message.toString()))
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- this.dataClient.on("error", (err) => {
|
|
|
|
- this.dataConnected = false
|
|
|
|
- console.error("DATA MQTT 连接错误:", err)
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- this.dataClient.on("disconnect", () => {
|
|
|
|
- this.dataConnected = false
|
|
|
|
- console.log("DATA MQTT 断开")
|
|
|
|
- })
|
|
|
|
-
|
|
|
|
- // 给 DATA 加一个关闭方法
|
|
|
|
- this.dataClient.close = (force = true) => {
|
|
|
|
- this.dataClient.end(force, () => {
|
|
|
|
- this.dataConnected = false
|
|
|
|
- this.dataClient = null
|
|
|
|
- console.log("DATA MQTT 已关闭")
|
|
|
|
- })
|
|
|
|
- }
|
|
|
|
|
|
+ const brokerName = 'DATA';
|
|
|
|
+ const url = 'wxs://data.radar-power.cn/mqtt/';
|
|
|
|
+ const clientId = `xcx_mqtt_data1_${userId}_${Date.now()}`;
|
|
|
|
+
|
|
|
|
+ return this.connectToBroker(brokerName, { url, clientId, username: 'lnradar', password: 'lnradar' })
|
|
|
|
+ .then(client => {
|
|
|
|
+ this.dataClient = client;
|
|
|
|
+ this.dataConnected = true;
|
|
|
|
|
|
- return this.dataClient
|
|
|
|
|
|
+ uni.$emit('mqttData-ready', this.dataClient);
|
|
|
|
+ return client;
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
- // 发布
|
|
|
|
|
|
+ // 内部通用连接方法(多连接管理)
|
|
|
|
+ connectToBroker(brokerName, config) {
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
+ if (this.connections.has(brokerName)) {
|
|
|
|
+ const existingConn = this.connections.get(brokerName);
|
|
|
|
+ if (existingConn.connected) {
|
|
|
|
+ resolve(existingConn.client);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const client = mqtt.connect(config.url, {
|
|
|
|
+ clientId: config.clientId,
|
|
|
|
+ username: config.username,
|
|
|
|
+ password: config.password,
|
|
|
|
+ reconnectPeriod: config.reconnectPeriod || 5000,
|
|
|
|
+ wsOptions: {
|
|
|
|
+ WebSocket: url => wx.connectSocket({
|
|
|
|
+ url,
|
|
|
|
+ header: { 'content-type': 'application/json' },
|
|
|
|
+ protocols: ['mqtt'],
|
|
|
|
+ })
|
|
|
|
+ },
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const connection = {
|
|
|
|
+ client,
|
|
|
|
+ connected: false,
|
|
|
|
+ subscriptions: new Map(),
|
|
|
|
+ };
|
|
|
|
+ this.connections.set(brokerName, connection);
|
|
|
|
+
|
|
|
|
+ client.on('connect', () => {
|
|
|
|
+ console.log(`${brokerName} MQTT 连接成功`);
|
|
|
|
+ connection.connected = true;
|
|
|
|
+ resolve(client);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ client.on('error', (err) => {
|
|
|
|
+ console.error(`${brokerName} MQTT 连接错误:`, err);
|
|
|
|
+ connection.connected = false;
|
|
|
|
+ reject(err);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ client.on('close', () => {
|
|
|
|
+ console.log(`${brokerName} MQTT 断开`);
|
|
|
|
+ connection.connected = false;
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ client.on('message', (topic, message) => {
|
|
|
|
+ const callbacks = connection.subscriptions.get(topic) || [];
|
|
|
|
+ callbacks.forEach(cb => {
|
|
|
|
+ try { cb(message.toString(), topic, brokerName); }
|
|
|
|
+ catch (err) { console.error('Message callback error:', err); }
|
|
|
|
+ });
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 连接超时
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ if (!connection.connected) reject(new Error(`${brokerName} 连接超时`));
|
|
|
|
+ }, 10000);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 发布消息
|
|
publish(clientType, topic, message) {
|
|
publish(clientType, topic, message) {
|
|
- const client = clientType === "CMD" ? this.cmdClient : this.dataClient
|
|
|
|
- if (client) {
|
|
|
|
- client.publish(topic, message)
|
|
|
|
- } else {
|
|
|
|
- console.warn(`${clientType} MQTT 未连接,无法 publish`)
|
|
|
|
- }
|
|
|
|
|
|
+ const client = clientType === 'CMD' ? this.cmdClient : this.dataClient;
|
|
|
|
+ if (client) client.publish(topic, message);
|
|
|
|
+ else console.warn(`${clientType} MQTT 未连接,无法 publish`);
|
|
}
|
|
}
|
|
|
|
|
|
// 订阅
|
|
// 订阅
|
|
- subscribe(clientType, topic) {
|
|
|
|
- const client = clientType === "CMD" ? this.cmdClient : this.dataClient
|
|
|
|
- if (client) {
|
|
|
|
- client.subscribe(topic)
|
|
|
|
- } else {
|
|
|
|
- console.warn(`${clientType} MQTT 未连接,无法 subscribe`)
|
|
|
|
|
|
+ subscribe(clientType, topic, callback) {
|
|
|
|
+ const connection = clientType === 'CMD' ? this.connections.get('CMD') : this.connections.get('DATA');
|
|
|
|
+ if (!connection || !connection.connected) {
|
|
|
|
+ console.warn(`${clientType} MQTT 未连接,无法 subscribe`);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!connection.subscriptions.has(topic)) connection.subscriptions.set(topic, []);
|
|
|
|
+ const callbacks = connection.subscriptions.get(topic);
|
|
|
|
+ if (!callbacks.includes(callback)) callbacks.push(callback);
|
|
|
|
+
|
|
|
|
+ connection.client.subscribe(topic, err => {
|
|
|
|
+ if (err) console.error(`Subscribe error on ${clientType}:`, err);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 返回取消订阅函数
|
|
|
|
+ return () => this.unsubscribe(clientType, topic, callback);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 取消订阅
|
|
|
|
+ unsubscribe(clientType, topic, callback) {
|
|
|
|
+ const connection = clientType === 'CMD' ? this.connections.get('CMD') : this.connections.get('DATA');
|
|
|
|
+ if (!connection) return;
|
|
|
|
+
|
|
|
|
+ const callbacks = connection.subscriptions.get(topic);
|
|
|
|
+ if (callbacks) {
|
|
|
|
+ const index = callbacks.indexOf(callback);
|
|
|
|
+ if (index > -1) callbacks.splice(index, 1);
|
|
|
|
+ if (callbacks.length === 0) {
|
|
|
|
+ connection.subscriptions.delete(topic);
|
|
|
|
+ connection.client.unsubscribe(topic);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 断开所有连接
|
|
// 断开所有连接
|
|
disconnectAll() {
|
|
disconnectAll() {
|
|
- if (this.cmdClient) {
|
|
|
|
- this.cmdClient.end(true)
|
|
|
|
- this.cmdClient = null
|
|
|
|
- this.cmdConnected = false
|
|
|
|
- console.log("CMD MQTT 已关闭")
|
|
|
|
- }
|
|
|
|
- if (this.dataClient) {
|
|
|
|
- this.dataClient.end(true)
|
|
|
|
- this.dataClient = null
|
|
|
|
- this.dataConnected = false
|
|
|
|
- console.log("DATA MQTT 已关闭")
|
|
|
|
- }
|
|
|
|
|
|
+ if (this.cmdClient) { this.cmdClient.end(true); this.cmdClient = null; this.cmdConnected = false; }
|
|
|
|
+ if (this.dataClient) { this.dataClient.end(true); this.dataClient = null; this.dataConnected = false; }
|
|
|
|
+ this.connections.forEach(conn => conn.client.end());
|
|
|
|
+ this.connections.clear();
|
|
|
|
+ console.log('所有 MQTT 连接已关闭');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-const instance = new MqttService()
|
|
|
|
-// Object.freeze(instance)
|
|
|
|
-
|
|
|
|
-export default instance
|
|
|
|
|
|
+// 单例导出
|
|
|
|
+const instance = new MqttService();
|
|
|
|
+export default instance;
|