UNPKG

mm_os

Version:

MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。

420 lines (379 loc) 9.18 kB
const MQTT = require('mqtt'); /** * @module MQTT客户端 */ class Mqtt { static config = { hostname: '127.0.0.1', port: '', protocol: 'ws', client_id, subs_qos: 0, publish_qos: 1, username: 'demo', password: 'asd123', clean: false, topic_receive: 'client/message/', longtime: 15000, // maxInflight: 20, // 重连间隔时间,单位为毫秒, 默认6秒 reco_period: 6000, // 连接超时时间,单位为毫秒, 默认30秒, 即尝试连接5次 connect_timeout: 30000, ...config }; /** * 构造函数 * @param {object} config 配置参数 */ constructor(config) { this.config = {...Mqtt.config, ...config}; // mqtt客户端服务器 this.client = null; /** * message queue */ this.list_msg = [ /* { // information ID id: "", // request method method: "", // message parameters params: {}, // Callback func: function(res){} } */ ]; /** * 订阅集合 (主题 => 函数列表) */ this.dict_subscribe = {}; /** * method collection */ this.methods = { /** * 获取所有方法 * @returns {Array} 方法列表 */ getMethod: function () { return Object.keys(_this.methods); } }; this.retry_times = 0; this.connecting = false; } } /** * 重新连接 */ Mqtt.prototype.reconnect = function () { this.retry_times += 1; if (this.retryTimes > 5) { try { this.client.end(); setTimeout(() => { this.init(); }, this.config.reconnectPeriod); } catch (error) { this.$message.error(error.toString()); } } }; /** * 初始化 * @param {object} config 配置参数 */ Mqtt.prototype.init = function (config) { this.config = Object.assign(this.config, config); this.client = { connected: false, }; this.retry_times = 0; this.connecting = false; }; /** * 运行MQTT * @param {object} config 配置参数 * @returns {Promise} 连接结果 */ Mqtt.prototype.run = function (config) { this.connecting = true; try { var cg = Object.assign(this.config, config); this._createClient(cg); if (this.client.on) { return this._setupClientEvents(); } } catch (error) { this.connecting = false; return this._rejectPromise(error); } return this._rejectPromise(); }; /** * 创建客户端 * @param {object} config 配置参数 * @private */ Mqtt.prototype._createClient = function (config) { // 创建配置副本以避免修改原始参数 var cg = { ...config }; if (cg.host) { delete cg.hostname; delete cg.port; this.client = MQTT.connect(cg.host, cg); } else { this.client = MQTT.connect(cg); } }; /** * 设置客户端事件 * @returns {Promise} 连接结果 * @private */ Mqtt.prototype._setupClientEvents = function () { return new Promise((resolve, reject) => { this.client.on('connect', (packet, err) => { this.connecting = false; if (err) { resolve(null); reject(err); } else { this.client.on('message', async (topic, message) => { await this.receive(topic, message.toString()); }); resolve(packet); } }); this.client.on('reconnect', this.reconnect); this.client.on('error', (error) => { console.error('Connection failed', error); }); }); }; /** * 创建被拒绝的Promise * @param {Error} error 错误对象 * @returns {Promise} 被拒绝的Promise * @private */ Mqtt.prototype._rejectPromise = function (error) { return new Promise((resolve, reject) => { resolve(null); reject(error); }); }; /** * 监听事件 * @param {string} event_name 事件名称 * @param {Function} func 事件处理函数 */ Mqtt.prototype.on = function (event_name, func) { this.client.on(event_name, func); }; /** * 接收消息 * @param {string} topic 订阅板块 * @param {object} msg 消息主体 */ Mqtt.prototype.message = async function (topic, msg) { if (this.dict_subscribe[topic]) { var list = this.dict_subscribe[topic]; try { for (var key in list) { list[key](msg); } } catch (err) { console.error(err); } } }; /** * 接收消息 * @param {string} topic 订阅 * @param {string} body_str 消息主体字符串 * @returns {object} 解析后的消息主体 */ Mqtt.prototype.receive = async function (topic, body_str) { var json = this._parseMessage(body_str); if (json) { if (this._handleMessage(json)) { return; } if (this._handleMethodMessage(json)) { return; } await this.message(topic, json); return; } await this.message(topic, body_str); }; /** * 解析消息 * @param {string} body_str 消息主体字符串 * @returns {object|null} 解析后的消息 * @private */ Mqtt.prototype._parseMessage = function (body_str) { if (body_str && (body_str.indexOf('[') === 0 || body_str.indexOf('{') === 0)) { try { return JSON.parse(body_str); } catch { } } return null; }; /** * 处理消息 * @param {object} json 消息对象 * @returns {boolean} 是否处理成功 * @private */ Mqtt.prototype._handleMessage = function (json) { var id = json.id; if (json.result && id) { var lt = this.list_msg; var len = lt.length; for (var i = 0; i < len; i++) { var o = lt[i]; if (id === o.id) { o.func(json.result); lt.splice(i, 1); return true; } } } return false; }; /** * 处理方法消息 * @param {object} json 消息对象 * @returns {boolean} 是否处理成功 * @private */ Mqtt.prototype._handleMethodMessage = function (json) { if (json.method) { var func = this._getMethod(json.method); if (func) { this._execMethod(func, json); return true; } } return false; }; /** * 获取方法 * @param {string} methodStr 方法字符串 * @returns {Function|null} 方法函数 * @private */ Mqtt.prototype._getMethod = function (methodStr) { var methods = this.methods; var arr = methodStr.split('.'); for (var i = 0; i < arr.length; i++) { var m = methods[arr[i]]; if (m) { methods = m; } else { return null; } } return typeof (methods) == 'function' ? methods : null; }; /** * 执行方法 * @param {Function} func 方法函数 * @param {object} json 消息对象 * @private */ Mqtt.prototype._execMethod = function (func, json) { var params = json.params; var from = params.from; var id = json.id; var ret = func(params); if (ret) { var obj = {}; if (id) { obj.id = id; } obj.result = ret; if (from) { var topic_receive = this.config.topic_receive + from; this.send(topic_receive, obj); } } }; Mqtt.prototype.subscribe = function (topic, func, qos = null, retain = false) { // console.log("订阅", qos); this.client.subscribe(topic, { // 订阅消息方式,0为保留 , 1为确认收到1次 qos: qos || 0, retain }); if (func) { if (!this.dict_subscribe[topic]) { this.dict_subscribe[topic] = {}; } var key = this.key_num + 1; this.dict_subscribe[topic][key] = func; return key; } return ''; }; Mqtt.prototype.unsubscribe = function (topic, key) { this.client.unsubscribe(topic); if (this.dict_subscribe[topic]) { delete this.dict_subscribe[topic][key]; } }; Mqtt.prototype.end = function () { this.client.end(); }; Mqtt.prototype.publish = function (topic, message, qos = null, retain = false) { this.client.publish(topic, message, { qos: qos || 0, retain }); }; Mqtt.prototype.send = function (topic, msg_obj) { this.publish(topic, JSON.stringify(msg_obj)); }; Mqtt.prototype.req = function (topic, method, params, func) { var data = { id: this.client.options.clientId + '_' + Date.parse(new Date()), method, params }; if (func) { data.func = func; this.list_msg.push(data); } this.send(topic, data); }; /** * 同步请求 - 可及时取回消息 * @param {string} topic 订阅板块 * @param {string} method 请求方法 * @param {object} params 传递参数 * @returns {object} 返回响应结果 */ Mqtt.prototype.reqASync = function (topic, method, params) { var _this = this; return new Promise((resolve, reject) => { var has_msg; _this.req(topic, method, params, (res) => { has_msg = true; resolve(res); }); setTimeout(() => { if (!has_msg) { resolve(null); var err = new Error('request timeout!'); reject(err); } }, 3000); }); }; module.exports = Mqtt;