yimultiscreenserver-sdk-web
Version:
YiMultiScreenServer SDK for Web
184 lines (166 loc) • 8.64 kB
text/typescript
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);
}
}