yimultiscreenserver-sdk-web
Version:
YiMultiScreenServer SDK for Web
217 lines (190 loc) • 9.41 kB
text/typescript
import { MQTTConfig } from "../../config/MQTTConfig";
import { SDKLog } from "../../util/SDKLog";
import { Utils } from "../../util/Utils";
import { IMQTTCallback } from "./IMQTTCallback";
import { IMQTTClient } from './IMQTTClient';
import { MQTTConnectOption } from "./MQTTConnectOption";
import { AccountBindDeviceMessage } from "./message/AccountBindDeviceMessage";
import { CastSessionMessage } from "./message/CastSessionMessage";
import { DataMessage } from "./message/DataMessage";
import { DisplayReportMessage } from "./message/DisplayReportMessage";
import { MsgType } from "./message/MsgType";
import { UserHasBeenKickedMessage } from "./message/UserHasBeenKickedMessage";
import { WillMessage } from "./message/WillMessage";
import { YMSMessage } from "./message/YMSMessage";
import { MessageParser } from "./parser/MessageParser";
export abstract class MQTTLogic implements IMQTTCallback {
readonly TAG: string = "MQTTLogic";
private parser: MessageParser;
private clientId: string;
private messageTimestampOfSenderUnderTopics: Map<string, Map<string, number>>;
private client: IMQTTClient | undefined;
private config: MQTTConfig | null;
private displayCode: number;
protected isConnected: boolean;
constructor(clientId: string) {
this.messageTimestampOfSenderUnderTopics = new Map();
this.parser = new MessageParser();
this.clientId = clientId;
this.config = null;
this.displayCode = -1;
this.isConnected = false;
}
public setMQTTClientImpl(impl: IMQTTClient, config: MQTTConfig): void {
this.client = impl;
this.config = config;
this.client?.init(this.getConfig().getBroker(), this.clientId);
}
/**
* 连接MQTT服务器
* @param displayCode 传入值<=0时,代表需要通配(#)
*/
public connect(displayCode: number): void {
this.displayCode = displayCode;
let options: MQTTConnectOption = this.createMqttOptions();
let willMessage: WillMessage = this.createMySelfWill(displayCode, this.clientId);
let topic: string;
if (displayCode <= 0) {
// 遗嘱消息不能使用通配符
topic = Utils.getTopicPrefix(MsgType.WILL);
} else {
topic = Utils.getTopicPrefix(MsgType.WILL) + displayCode;
}
// set will message
options.willTopic = topic;
options.willMessage = this.parser.toJson(willMessage);
options.willQoS = willMessage.getQoS();
options.isWillRetained = willMessage.isRetained();
// connect
this.connectWithOptions(options);
this.subscribeInterestTopic(displayCode);
}
protected connectWithOptions(options: MQTTConnectOption): void {
this.client?.connect(options);
this.client?.setCallback(this);
this.isConnected = true;
}
protected createMqttOptions(): MQTTConnectOption {
let options: MQTTConnectOption = new MQTTConnectOption();
options.userName = this.getConfig().getUserName();
options.password = this.getConfig().getPassWord();
options.isCleanSession = this.getConfig().isCleanSession();
options.keepAliveInterval = this.getConfig().getKeepAlive();
options.connectionTimeout = this.getConfig().getConnectTimeout();
return options;
}
public publish(displayCode: number, ymsMessage: YMSMessage): void {
ymsMessage.timestamp = Date.now();
ymsMessage.senderClientId = this.clientId;
this.client?.publish(Utils.getTopicPrefix(ymsMessage.msgType) + displayCode,
this.parser.toJson(ymsMessage), ymsMessage.getQoS(), ymsMessage.isRetained());
}
public disconnect(displayCode: number): Promise<string> {
return new Promise((resolve, reject) => {
this.unsubscribeAllTopics(displayCode);
this.client?.disconnect()
.then(() => {
this.isConnected = false;
resolve("MQTT断连成功");
})
.catch(reject);
});
}
// override
connectionLost(cause: string): void {
SDKLog.e(this.TAG, cause);
this.isConnected = false;
this.onConnectionLost(cause);
}
// override
connectComplete(reconnect: boolean, serverURI: string): void {
this.isConnected = true;
// 如果MQTT实现库内部会尝试自动重连,则在此响应自动重连成功后的回调,并再次进行主题订阅
if (reconnect) {
this.subscribeInterestTopic(this.displayCode);
}
}
// override
messageArrived(topic: string, message: string): void {
SDKLog.i(this.TAG, "message arriavd, topic = " + topic);
let ymsMessage: YMSMessage = this.parser.parse(message);
if (null === ymsMessage || undefined === ymsMessage) return;
if (this.isMessageFromSelf(ymsMessage)) {
return;
}
if (!this.isMessageForMe(ymsMessage)) {
return;
}
if (this.isMessageExpired(topic, ymsMessage)) {
return;
}
switch (ymsMessage.msgType) {
case MsgType.WILL:
this.onWillMessageArrived(ymsMessage as WillMessage);
break;
case MsgType.REPORT:
this.onDisplayReportMessageArrived(ymsMessage as DisplayReportMessage);
break;
case MsgType.CAST:
this.onCastSessionMessageArrived(ymsMessage as CastSessionMessage);
break;
case MsgType.DATA:
this.onDataMessageArrived(ymsMessage as DataMessage);
break;
case MsgType.ACCOUNT:
this.onUserHasBeenKickedMessageArrived(ymsMessage as UserHasBeenKickedMessage);
break;
case MsgType.BIND_DEVICE:
this.onAccountBindDeviceMessageArrived(ymsMessage as AccountBindDeviceMessage);
break;
}
}
private isMessageExpired(topic: string, message: YMSMessage): boolean {
// 如果是will遗嘱消息,则不做过期判断,直接返回未过期
if (MsgType.WILL === message.msgType) {
return false;
}
// 判断message的topic是否已经缓存在map,没有则进行首次添加
if (0 === this.messageTimestampOfSenderUnderTopics.size ||
!this.messageTimestampOfSenderUnderTopics.has(topic)) {
let firstTimeOfTopic: Map<string, number> = new Map();
this.messageTimestampOfSenderUnderTopics.set(topic, firstTimeOfTopic);
}
// 取对应topic的value,就是这个topic下所有发送端设备上一次发送消息的时间戳map
let messageTimestampOfSender: Map<string, number> | undefined = this.messageTimestampOfSenderUnderTopics.get(topic);
// 判断此clientId设备是否已经缓存上次时间戳,没有则进行首次添加
if (0 === messageTimestampOfSender!.size ||
!messageTimestampOfSender!.has(message.senderClientId)) {
// 首次添加,则将时间戳设置为0,表示第一次收到此clientId设备的消息
messageTimestampOfSender!.set(message.senderClientId, 0);
}
// 取此clientId设备对应的上次消息时间戳,进行判断本次时间戳是否比上一次新,不是则标识为过期消息
let lastMessageTimestamp: number | undefined = messageTimestampOfSender!.get(message.senderClientId);
let expired: boolean = (undefined === lastMessageTimestamp) ? true : (message.timestamp <= lastMessageTimestamp);
// 如果不是过期消息,则刷新时间戳缓存
if (!expired) {
lastMessageTimestamp = message.timestamp;
messageTimestampOfSender!.set(message.senderClientId, lastMessageTimestamp);
this.messageTimestampOfSenderUnderTopics.set(topic, messageTimestampOfSender!);
}
return expired;
}
protected getClient(): IMQTTClient | undefined {
return this.client;
}
protected getConfig(): MQTTConfig {
return this.config!;
}
protected abstract isMessageFromSelf(message: YMSMessage): boolean;
protected abstract isMessageForMe(message: YMSMessage): boolean;
protected abstract createMySelfWill(displayCode: number, clientId: string): WillMessage;
protected abstract subscribeInterestTopic(displayCode: number): void;
protected abstract unsubscribeAllTopics(displayCode: number): void;
protected abstract onWillMessageArrived(message: WillMessage): void;
protected abstract onDisplayReportMessageArrived(message: DisplayReportMessage): void;
protected abstract onCastSessionMessageArrived(message: CastSessionMessage): void;
protected abstract onDataMessageArrived(message: DataMessage): void;
protected abstract onUserHasBeenKickedMessageArrived(message: UserHasBeenKickedMessage): void;
protected abstract onAccountBindDeviceMessageArrived(message: AccountBindDeviceMessage): void;
protected abstract onConnectionLost(cause: string): void;
}