UNPKG

yimultiscreenserver-sdk-web

Version:

YiMultiScreenServer SDK for Web

184 lines (166 loc) 8.64 kB
import { MQTTLogic } from "../MQTTLogic"; import { CastSessionMessage } from "../message/CastSessionMessage"; import { DataMessage } from "../message/DataMessage"; import { DisplayReportMessage } from "../message/DisplayReportMessage"; import { MsgClientType } from "../message/MsgClientType"; import { MsgType } from "../message/MsgType"; import { WillMessage } from "../message/WillMessage"; import { YMSMessage } from "../message/YMSMessage"; import { IControllerMQTTCallback } from "./IControllerMQTTCallback"; import { Utils } from '../../../util/Utils'; import { AccountBindDeviceMessage } from "../message/AccountBindDeviceMessage"; import { UserHasBeenKickedMessage } from "../message/UserHasBeenKickedMessage"; import { SDKLog } from "../../../util/SDKLog"; import { SessionDestroyReason } from "../message/SessionDestroyReason"; export class ControllerMQTTImpl extends MQTTLogic { private readonly MYSELF_DISCONNECT_CALLBACK_GAP_MS: number = 500; // 0.5s private readonly callback: IControllerMQTTCallback; private accountId: number; private isSkipDisconnectCallbackOnce: boolean; private lastDisconnectTime: number; constructor(clientId: string, callback: IControllerMQTTCallback) { super(clientId); this.callback = callback; this.accountId = -1; this.isSkipDisconnectCallbackOnce = false; this.lastDisconnectTime = 0; } // Override // 控制端需要覆写实现下父类的connect方法 // 因为控制端需要处理两种场景:1.先登录账户,再连接display;2.先连接display,再登录账户 public async connect(displayCode: number): Promise<void> { if (this.isConnected) { // 此时就是场景1。用户先登录账户,再连接display // 必须先断开连接,因为登录账户时connect使用的option不包括will message // 所以要重新使用新的带有will message的option连接 this.isSkipDisconnectCallbackOnce = true; this.unsubscribeAccountTopic(this.accountId); await super.disconnect(displayCode) .then((result) => { this.isConnected = false; }) .catch((error) => { //不关注error }); } super.connect(displayCode); // 连接成功后,再次订阅上account相关的topic消息 if (-1 != this.accountId) { this.subscribeAccountTopic(this.accountId); } } // Override // 控制端需要覆写实现下父类的disconnect方法 // 因为控制端需要处理: 同时处于登录&连接display状态时,用户断开display后,如何处理账号topic消息的监听 public async disconnect(displayCode: number): Promise<string> { // 用户断开display时,原逻辑是彻底断开mqtt连接,但此时用户还处于登录状态 return new Promise((resolve, reject) => { super.disconnect(displayCode) .then((result) => { resolve(result); }) .catch(reject); // 有必要判断此时是否订阅了账号topic消息,如果订阅了,则断开后需要重新连接上mqtt服务 if (-1 != this.accountId) { // 说明此时用户还处于登录状态,重新连接上mqtt服务订阅上账号相关的topic消息 this.subscribeAccountTopic(this.accountId); } }); } /** * 控制端单独增加的公开方法,用于控制端成功完成账号登录后,监听该账号topic下消息 * @param accountId 登录后拿到的账号唯一ID */ public subscribeAccountTopic(accountId: number): void { this.accountId = accountId; if (!this.isConnected) { super.connectWithOptions(super.createMqttOptions()); } let account: string = Utils.getTopicPrefix(MsgType.ACCOUNT) + accountId; let bindDevice: string = Utils.getTopicPrefix(MsgType.BIND_DEVICE) + accountId; let topics: string[] = [account, bindDevice]; super.getClient()?.subscribe(topics); } /** * 控制端单独增加的公开方法,用于控制端成功退出账号或注销账号后,解除对指定账号的topic消息监听 * @param accountId 登录后拿到的账号唯一ID */ public unsubscribeAccountTopic(accountId: number): void { this.accountId = -1; if (!this.isConnected) { return; } let account: string = Utils.getTopicPrefix(MsgType.ACCOUNT) + accountId; let bindDevice: string = Utils.getTopicPrefix(MsgType.BIND_DEVICE) + accountId; let topics: string[] = [account, bindDevice]; super.getClient()?.unsubscribe(topics); } protected isMessageFromSelf(message: YMSMessage): boolean { return MsgClientType.CONTROLLER == message.sender; } protected isMessageForMe(message: YMSMessage): boolean { return (MsgClientType.ANY == message.receiver || MsgClientType.CONTROLLER == message.receiver); } protected createMySelfWill(displayCode: number, clientId: string): WillMessage { let ret: WillMessage = new WillMessage(MsgClientType.CONTROLLER, MsgClientType.ANY); ret.senderClientId = clientId; ret.timestamp = Date.now(); return ret; } protected subscribeInterestTopic(displayCode: number): void { let will: string = Utils.getTopicPrefix(MsgType.WILL) + displayCode; let report: string = Utils.getTopicPrefix(MsgType.REPORT) + displayCode; let cast: string = Utils.getTopicPrefix(MsgType.CAST) + displayCode; let topics: string[] = [will, report, cast]; super.getClient()?.subscribe(topics); } protected unsubscribeAllTopics(displayCode: number): void { let will: string = Utils.getTopicPrefix(MsgType.WILL) + displayCode; let report: string = Utils.getTopicPrefix(MsgType.REPORT) + displayCode; let cast: string = Utils.getTopicPrefix(MsgType.CAST) + displayCode; let topics: string[] = [will, report, cast]; super.getClient()?.unsubscribe(topics); } protected onWillMessageArrived(message: WillMessage): void { // must be display or server died if (MsgClientType.DISPLAY == message.sender) { this.callback.onDisplayDied(); } else if (MsgClientType.SERVER == message.sender) { this.callback.onServerDied(); } } protected onDisplayReportMessageArrived(message: DisplayReportMessage): void { this.callback.onDisplayReport(message.displayReport); } protected onCastSessionMessageArrived(message: CastSessionMessage): void { this.callback.onCastSession(message.castSession, message.action, message.mediaPayload, message.destroyReason?? SessionDestroyReason.NONE); } protected onDataMessageArrived(message: DataMessage): void { // we don't care about data msg } protected onUserHasBeenKickedMessageArrived(message: UserHasBeenKickedMessage): void { this.callback.onUserHasBeenKicked(message.user); } protected onAccountBindDeviceMessageArrived(message: AccountBindDeviceMessage): void { this.callback.onAccountBindDevice(message.accountBindDevice, message.bindDeviceAction); } protected onConnectionLost(cause: string): void { let now = Date.now(); if (now - this.lastDisconnectTime <= this.MYSELF_DISCONNECT_CALLBACK_GAP_MS) { // 自身断开连接回调做一个时间防抖,一定时长内连续触发我们都予以跳过 // 因为实操中发现前端使用的mtqq库会多次回调connectionLost SDKLog.d(this.TAG, "一定时长内连续触发connectionLost回调,跳过"); return; } this.lastDisconnectTime = now; if (this.isSkipDisconnectCallbackOnce) { // 此时,场景是用户先登录连接了mqtt,再connect display // 我们在connect时会先断开mqtt再重连,如果这次断开触发回调会导致上层不符合预期 SDKLog.d(this.TAG, "先登录再connect display,sdk主动触发一次重连,跳过这次回调"); this.isSkipDisconnectCallbackOnce = false; return; } this.callback.onMySelfDisconnected(cause); } }