agri-app/utils/mqtt.js

290 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* 智能农业小程序 - MQTT全局工具类uniapp+小程序适配版)
* 核心设计:
* 1. 配置暂时写死全局,后续可替换为登录后从数据字典获取
* 2. clientId仅随机生成无需关联userId保证唯一性即可
* 3. 同步方法为主,全局统一管理连接/订阅/发布,多页面复用
*/
// 引入小程序版MQTT核心库确保mqtt.min.js在utils目录
// 微信小程序端:引入适配小程序的 mqtt 版本
import mqtt from 'mqtt/dist/mqtt'
// ===================== MQTT配置暂时写死TODO后续从数据字典获取=====================
const MQTT_CONFIG = {
server: 'wxs://mq.xiaoces.com:443/mqtt', // 替换为你的MQTT服务器地址
username: 'admin', // 替换为通用账号
password: 'Admin#12345678', // 替换为通用密码
clean: true,
host: 'mq.xiaoces.com',
port: 443,
reconnectPeriod: 5000, // 重连间隔
connectTimeout: 10000, // 连接超时
keepalive: 60, // 心跳时间
}
// ===================== MQTT全局状态管理 =====================
const mqttState = {
client: null, // MQTT客户端实例
isConnected: false, // 连接状态(同步标记)
subscribeList: [], // 全局订阅列表(登录后赋值)
options: { // MQTT连接选项
clientId: '', // 仅随机生成无需关联userId
...MQTT_CONFIG // 合并基础配置
},
onMessageCallback: null // 全局消息回调(各页面自定义)
}
/**
* 初始化MQTT配置生成随机clientId无需传入userId
* @returns {Boolean} - 是否配置成功
*/
export function initMqttConfig() {
if (mqttState.client) {
console.info("重连前强制断开",mqttState.options.clientId)
// 加try-catch避免断开时客户端已异常导致报错
try {
mqttState.client.end(true)
} catch (err) {
console.warn('旧连接断开失败:', err)
}
}
try {
// 仅随机生成clientId保证唯一性避免连接冲突
mqttState.options.clientId = `wx_mqtt_${Math.random().toString(16).substr(2, 10)}`
// 重置旧状态,避免干扰
mqttState.isConnected = false
mqttState.client = null
console.log('MQTT配置初始化成功clientId', mqttState.options.clientId)
return true
} catch (err) {
uni.showToast({ title: '设备连接异常-设备初始化失败', icon: 'none', duration: 2000 })
console.error('MQTT配置初始化失败', err)
return false
}
}
/**
* 建立MQTT连接初始化后调用同步返回触发状态
* @returns {Boolean} - 是否成功触发连接
*/
export function connectMqtt() {
// 前置校验:是否已初始化配置
if (!mqttState.options.clientId) {
uni.showToast({ title: '设备连接失败', icon: 'none' })
console.error('MQTT连接失败请先调用initMqttConfig初始化配置')
return false
}
console.info(mqttState.client!=null,mqttState.client)
// 避免重复连接
if (mqttState.client!=null) {
console.log('MQTT已连接无需重复操作')
return true
}
try {
// 创建客户端实例(同步操作)
// #ifndef MP-WEIXIN
MQTT_CONFIG.server = 'wss://mq.xiaoces.com:443/mqtt'
// #endif
console.info("mqttState.connect",mqttState)
mqttState.client = mqtt.connect(MQTT_CONFIG.server, mqttState.options)
console.info("重连中。。")
// 监听核心事件(异步,同步更新状态)
mqttState.client.on('connect', () => {
console.log('MQTT连接成功',mqttState.options.clientId)
mqttState.isConnected = true
// 连接成功后自动订阅全局列表
subscribeAllTopics()
})
mqttState.client.on('close', () => {
console.log('MQTT连接断开')
mqttState.isConnected = false
})
mqttState.client.on('error', (err) => {
uni.showToast({ title: '设备连接异常', icon: 'none' })
console.error('MQTT连接错误', err)
mqttState.isConnected = false
})
// 全局接收消息,转发给页面自定义回调
mqttState.client.on('message', (topic, payload) => {
const message = payload.toString()
// console.log(`收到MQTT消息topic=${topic}message=${message}`)
if (typeof mqttState.onMessageCallback === 'function') {
mqttState.onMessageCallback(topic, message)
}
})
return true
} catch (err) {
uni.showToast({ title: '设备连接异常', icon: 'none' })
console.error('MQTT连接创建失败', err)
mqttState.isConnected = false
return false
}
}
/**
* 设置页面专属消息回调(各页面独立处理消息)
* @param {Function} callback - (topic, message) => {}
*/
export function setOnMessageCallback(callback) {
if (typeof callback === 'function') {
mqttState.onMessageCallback = callback
console.log('MQTT消息回调已注册')
} else {
console.error('MQTT回调必须是函数类型')
}
}
/**
* 移除消息回调(页面卸载时调用,避免内存泄漏)
*/
export function removeOnMessageCallback() {
mqttState.onMessageCallback = null
console.log('MQTT消息回调已移除')
}
/**
* 更新全局订阅列表(登录后调用,自动订阅)
* @param {Array} list - 订阅主题列表 ['topic1', 'topic2']
* @returns {Boolean} - 是否更新成功
*/
export function updateSubscribeList(list) {
if (!Array.isArray(list)) {
uni.showToast({ title: '设备订阅更新异常', icon: 'none' })
console.error('订阅列表必须是数组')
return false
}
mqttState.subscribeList = [...list]
console.log('MQTT订阅列表已更新', list)
// 已连接则立即订阅
if (mqttState.isConnected) {
subscribeAllTopics()
}
return true
}
/**
* 内部方法:订阅所有全局主题
*/
function subscribeAllTopics() {
const { isConnected, client, subscribeList } = mqttState
if (!isConnected || !client || subscribeList.length === 0) {
console.warn('MQTT订阅跳过未连接或列表为空')
return
}
client.subscribe(subscribeList, { qos: 0 }, (err) => {
if (err) {
uni.showToast({ title: '设备订阅异常', icon: 'none' })
console.error('MQTT订阅失败', err)
} else {
console.log(`MQTT成功订阅${subscribeList.join(', ')}`)
}
})
}
/**
* 发布MQTT消息同步调用
* @param {String} topic - 发布主题
* @param {String/Object} message - 发布内容
* @returns {Boolean} - 是否触发发布成功
*/
export function publishMqtt(topic, message) {
if (process.env.NODE_ENV === "production") {
const { isConnected, client } = mqttState
if (!isConnected || !client) {
uni.showToast({ title: '控制异常', icon: 'none' })
console.error('MQTT发布失败未连接')
return false
}
if (!topic) {
uni.showToast({ title: '控制异常', icon: 'none' })
console.error('MQTT发布失败主题为空')
return false
}
try {
const msg = typeof message === 'object' ? JSON.stringify(message) : String(message)
client.publish(topic, msg, (err) => {
if (err) {
uni.showToast({ title: '控制异常', icon: 'none' })
console.error(`MQTT发布失败topic=${topic}err=${err}`)
} else {
console.log(`MQTT发布成功topic=${topic}message=${msg}`)
}
})
return true
} catch (err) {
uni.showToast({ title: '控制异常', icon: 'none' })
console.error('MQTT发布异常', err)
return false
}
}
}
/**
* 断开MQTT连接登出/小程序切后台时调用)
* @returns {Boolean} - 是否断开成功
*/
export function disconnectMqtt() {
if (!mqttState.client) {
console.log('MQTT无客户端实例无需断开')
resetMqttState()
return true
}
try {
mqttState.client.end(true) // 强制断开
resetMqttState()
console.log('MQTT连接已断开')
return true
} catch (err) {
uni.showToast({ title: '设备通信异常', icon: 'none' })
console.error('MQTT断开失败', err)
return false
}
}
/**
* 重置MQTT状态内部方法
*/
function resetMqttState() {
mqttState.isConnected = false
mqttState.client = null
mqttState.subscribeList = []
mqttState.onMessageCallback = null
// 保留clientId直到下一次初始化覆盖
}
/**
* 获取当前MQTT状态同步
* @returns {Object} - 状态副本
*/
export function getMqttState() {
return {
isConnected: mqttState.isConnected,
subscribeList: [...mqttState.subscribeList],
clientId: mqttState.options.clientId,
client: mqttState.client
}
}
// 导出所有方法(全局调用)
export default {
initMqttConfig,
connectMqtt,
setOnMessageCallback,
removeOnMessageCallback,
updateSubscribeList,
publishMqtt,
disconnectMqtt,
getMqttState
}