globalMqtt.js 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import mqtt from './mqtt.js';
  2. class MqttService {
  3. constructor() {
  4. if (!MqttService.instance) {
  5. this.cmdClient = null;
  6. this.dataClient = null;
  7. this.cmdConnected = false;
  8. this.dataConnected = false;
  9. this.connections = new Map(); // 内部管理多连接
  10. MqttService.instance = this;
  11. }
  12. return MqttService.instance;
  13. }
  14. // 连接 CMD
  15. connectCmd(userId) {
  16. if (this.cmdConnected && this.cmdClient) {
  17. console.log("CMD MQTT 已连接,复用现有实例");
  18. return Promise.resolve(this.cmdClient);
  19. }
  20. const brokerName = 'CMD';
  21. const url = 'wxs://cmd.radar-power.cn/mqtt/';
  22. const clientId = `xcx_mqtt_cmd1_${userId}_${Math.random().toString(16).substring(2, 8)}`;
  23. return this.connectToBroker(brokerName, { url, clientId, username: 'lnradar', password: 'lnradar' })
  24. .then(client => {
  25. this.cmdClient = client;
  26. this.cmdConnected = true;
  27. // 默认订阅 CMD 主题
  28. const topic = `/mps/wx_${userId}/notice`;
  29. this.subscribe('CMD', topic, (msg) => {
  30. console.log('CMD 消息:', msg);
  31. });
  32. // 打印订阅成功
  33. console.log(`✅ CMD MQTT 主题已订阅成功: ${topic}`);
  34. uni.$emit('mqtt-ready', this.cmdClient);
  35. return client;
  36. });
  37. }
  38. // 连接 DATA
  39. connectData(userId) {
  40. uni.showToast({
  41. title: "正在连接平台中...",
  42. icon: "loading",
  43. duration: 2000, //持续的时间
  44. });
  45. if (this.dataConnected && this.dataClient) {
  46. uni.hideToast()
  47. console.log("DATA MQTT 已连接,复用现有实例");
  48. return Promise.resolve(this.dataClient);
  49. }
  50. const brokerName = 'DATA';
  51. let url = ""
  52. // if (__wxConfig.envVersion == 'develop') {
  53. // url = 'wxs://data.radar-power.asia:8084/mqtt/'
  54. // }
  55. // if (__wxConfig.envVersion == 'trial') {
  56. // url = 'wxs://data.radar-power.cn/mqtt/';
  57. // }
  58. // if (__wxConfig.envVersion == 'release') {
  59. // url = 'wxs://data.radar-power.cn/mqtt/';
  60. // }
  61. url = 'wxs://data.radar-power.cn/mqtt/';
  62. const clientId = `xcx_mqtt_data1_${userId}_${Date.now()}`;
  63. return this.connectToBroker(brokerName, { url, clientId, username: 'lnradar', password: 'lnradar' })
  64. .then(client => {
  65. this.dataClient = client;
  66. this.dataConnected = true;
  67. uni.$emit('mqttData-ready', this.dataClient);
  68. return client;
  69. });
  70. }
  71. // 内部通用连接方法(多连接管理)
  72. connectToBroker(brokerName, config) {
  73. return new Promise((resolve, reject) => {
  74. if (this.connections.has(brokerName)) {
  75. const existingConn = this.connections.get(brokerName);
  76. if (existingConn.connected) {
  77. resolve(existingConn.client);
  78. return;
  79. }
  80. }
  81. const client = mqtt.connect(config.url, {
  82. clientId: config.clientId,
  83. username: config.username,
  84. password: config.password,
  85. reconnectPeriod: config.reconnectPeriod || 5000,
  86. wsOptions: {
  87. WebSocket: url => wx.connectSocket({
  88. url,
  89. header: { 'content-type': 'application/json' },
  90. protocols: ['mqtt'],
  91. })
  92. },
  93. });
  94. const connection = {
  95. client,
  96. connected: false,
  97. subscriptions: new Map(),
  98. };
  99. this.connections.set(brokerName, connection);
  100. client.on('connect', () => {
  101. uni.hideToast()
  102. console.log(`${brokerName} MQTT 连接成功`);
  103. connection.connected = true;
  104. uni.$emit('mqttDataReadyOnce', this.dataClient);
  105. resolve(client);
  106. });
  107. client.on('error', (err) => {
  108. uni.showModal({
  109. title: '提示',
  110. content: '连接平台失败,请重新登录',
  111. showCancel: false,
  112. success: (res) => {
  113. if (res.confirm) {
  114. uni.clearStorageSync();
  115. uni.reLaunch({
  116. url: "/pagesA/loginNew/loginNew"
  117. })
  118. }
  119. if (res.cancel) {
  120. }
  121. }
  122. })
  123. console.error(`${brokerName} MQTT 连接错误:`, err);
  124. connection.connected = false;
  125. reject(err);
  126. });
  127. client.on('close', () => {
  128. uni.showToast({
  129. title: `mqtt已经断开`,
  130. icon: "none",
  131. duration: 1500,
  132. });
  133. console.log(`${brokerName} MQTT 断开`);
  134. connection.connected = false;
  135. });
  136. client.on('message', (topic, message) => {
  137. const callbacks = connection.subscriptions.get(topic) || [];
  138. callbacks.forEach(cb => {
  139. try { cb(message.toString(), topic, brokerName); }
  140. catch (err) { console.error('Message callback error:', err); }
  141. });
  142. });
  143. // 连接超时
  144. setTimeout(() => {
  145. if (!connection.connected) reject(new Error(`${brokerName} 连接超时`));
  146. }, 10000);
  147. });
  148. }
  149. // 发布消息
  150. publish(clientType, topic, message) {
  151. const client = clientType === 'CMD' ? this.cmdClient : this.dataClient;
  152. if (client) client.publish(topic, message);
  153. else console.warn(`${clientType} MQTT 未连接,无法 publish`);
  154. }
  155. // 订阅
  156. subscribe(clientType, topic, callback, onSubscribeDone) {
  157. const connection = clientType === 'CMD' ? this.connections.get('CMD') : this.connections.get('DATA');
  158. if (!connection || !connection.connected) {
  159. console.warn(`${clientType} MQTT 未连接,无法 subscribe`);
  160. if (onSubscribeDone) onSubscribeDone(new Error('未连接'));
  161. return;
  162. }
  163. // 保存回调
  164. if (!connection.subscriptions.has(topic)) connection.subscriptions.set(topic, []);
  165. const callbacks = connection.subscriptions.get(topic);
  166. if (!callbacks.includes(callback)) callbacks.push(callback);
  167. // 执行订阅
  168. connection.client.subscribe(topic, (err) => {
  169. if (err) {
  170. console.error(`Subscribe error on ${clientType}:`, err);
  171. }
  172. // 订阅完成回调
  173. if (onSubscribeDone) onSubscribeDone(err);
  174. });
  175. // 返回取消订阅函数
  176. return () => this.unsubscribe(clientType, topic, callback);
  177. }
  178. // 取消订阅
  179. unsubscribe(clientType, topic, callback) {
  180. const connection = clientType === 'CMD' ? this.connections.get('CMD') : this.connections.get('DATA');
  181. if (!connection) return;
  182. const callbacks = connection.subscriptions.get(topic);
  183. if (callbacks) {
  184. const index = callbacks.indexOf(callback);
  185. if (index > -1) callbacks.splice(index, 1);
  186. if (callbacks.length === 0) {
  187. connection.subscriptions.delete(topic);
  188. connection.client.unsubscribe(topic);
  189. }
  190. }
  191. }
  192. // 断开所有连接
  193. disconnectAll() {
  194. if (this.cmdClient) { this.cmdClient.end(true); this.cmdClient = null; this.cmdConnected = false; }
  195. if (this.dataClient) { this.dataClient.end(true); this.dataClient = null; this.dataConnected = false; }
  196. this.connections.forEach(conn => conn.client.end());
  197. this.connections.clear();
  198. console.log('所有 MQTT 连接已关闭');
  199. }
  200. disconnectData() {
  201. if (this.dataClient) {
  202. this.dataClient.end(true);
  203. this.dataClient = null;
  204. this.dataConnected = false;
  205. this.connections.delete("DATA");
  206. console.log("DATA MQTT 连接已关闭");
  207. }
  208. }
  209. }
  210. // 单例导出
  211. const instance = new MqttService();
  212. export default instance;