UNPKG

yimultiscreenserver-sdk-web

Version:

YiMultiScreenServer SDK for Web

217 lines (190 loc) 9.41 kB
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; }