mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
420 lines (379 loc) • 9.18 kB
JavaScript
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;