import NormalWindowRequestEntity from "src/entities/NormalWindowRequestEntity"; import { Protocol } from "src/entities/WSProtocol"; import EventBus, { EventNamesDefine } from "./EventBus"; export default class ClientConnection { ws: WebSocket | null = null; url = ""; user_name = ""; password = ""; _is_login = false; _rpc_id_counter = 0; rpc_map = new Map< number, (is_fail: boolean, packet: Protocol.Commands, data: string) => void >(); public login_callback: | ((this: ClientConnection, logined: boolean) => void) | null = null; constructor( url: string, user_name?: string | null, password?: string | null ) { this.reconnectTo(url, user_name, password); } get is_connected() { return this.ws && this.ws.readyState == WebSocket.OPEN; } get is_login() { return this._is_login; } public reconnectTo( url: string, user_name?: string | null, password?: string | null ) { if (this._destoryed) { this.ws?.close(); this.ws = null; return; } this.url = url; this.user_name = user_name ?? ""; this.password = password ?? ""; if (this.ws) { this.ws.close(); } this.ws = new WebSocket(url); this.initializeWs(); } initializeWs() { if (this.ws) { this.ws.onclose = (ev) => { this.onClose(ev); }; this.ws.onerror = (ev) => { this.onError(ev); }; this.ws.onopen = (ev) => { this.onOpen(ev); }; this.ws.onmessage = (ev) => { this.onMessage(ev); }; } } login() { if (this._destoryed) { this.ws?.close(); this.ws = null; return; } if (this.is_connected) { const request = new Protocol.LoginRequest(this.user_name, this.password); this.ws?.send(JSON.stringify(request)); } } onClose(ev: CloseEvent) { this._is_login = false; this._reconnect(); EventBus.getInstance().emit(EventNamesDefine.WebSocketClose, this); } onError(ev: Event) { this._is_login = false; this._reconnect(); EventBus.getInstance().emit(EventNamesDefine.WebSocketError, this); } private _reconnectTimer: ReturnType | null = null; private _reconnect() { if (this._destoryed) { this.ws?.close(); this.ws = null; return; } if ( !this.ws || (this.ws.readyState != WebSocket.CONNECTING && this.ws.readyState != WebSocket.OPEN) ) { if (this._reconnectTimer) { clearTimeout(this._reconnectTimer); this._reconnectTimer = null; } this._reconnectTimer = setTimeout(() => { this.reconnectTo(this.url, this.user_name, this.password); }, 3000); } } onOpen(ev: Event) { if (this._destoryed) { this.ws?.close(); this.ws = null; return; } this._is_login = false; this.login(); } onMessage(ev: MessageEvent) { if (this._destoryed) { this.ws?.close(); this.ws = null; return; } try { const packet = JSON.parse(ev.data) as Protocol.PacketEntity; if (packet) { if (packet.has_exception) { console.error(ev.data); } if (Protocol.Commands.AllCommands.has(packet.command)) { if ( packet.flag == Protocol.PacketEntity.FLAG_RESPONSE || packet.flag == Protocol.PacketEntity.FLAG_NOTIFY ) { if (packet.command == Protocol.Commands.kLogin) { const login_response = JSON.parse( ev.data ) as Protocol.LoginResponse; if (login_response) { this._is_login = !login_response.has_exception && login_response.success; if (this.is_login) { EventBus.getInstance().emit( EventNamesDefine.WebSocketConnected, this ); } if ( this.login_callback && typeof this.login_callback == "function" ) { this.login_callback(this._is_login); } } } else if (this.rpc_map.has(packet.rpc_id)) { const f = this.rpc_map.get(packet.rpc_id); if (f && typeof f == "function") { f(false, packet, ev.data); this.rpc_map.delete(packet.rpc_id); } } else { EventBus.getInstance().emit(EventNamesDefine.ResponseMessage, { packet: packet, data: ev.data, }); } } } else { console.error("unknow command: " + packet.command, packet); } } } catch (e) { console.error(e); } } public async doRpc<_ResponseType>( request: Protocol.PacketEntity ): Promise<_ResponseType | null> { return new Promise((resolve, reject) => { const rpc_id = ++this._rpc_id_counter; if (this.rpc_map.has(rpc_id)) { const f = this.rpc_map.get(rpc_id); if (f && typeof f == "function") { f(true, new Protocol.Commands(), ""); this.rpc_map.delete(rpc_id); } } request.rpc_id = rpc_id; this.ws?.send(JSON.stringify(request)); this.rpc_map.set( rpc_id, (is_fail: boolean, packet: Protocol.Commands, data: string) => { if (is_fail) { reject(); } else { try { const response = JSON.parse(data) as _ResponseType; if (response) { resolve(response); } else { reject(); } } catch { reject(); } } } ); }); } public async getSignalSources() { try { return await this.doRpc( new Protocol.GetSignalSourcesRequest() ); } catch (e) { console.error(e); } } public async getApplicationSettins() { try { return await this.doRpc( new Protocol.GetApplicationConfigRequestEntity() ); } catch (e) { console.error(e); } } public async getWindows() { try { return await this.doRpc( new Protocol.GetWindowsRequestEntity() ); } catch (e) { console.error(e); } } public moveWindow(window_id: number, x: number, y: number) { this.ws?.send( JSON.stringify(new Protocol.MoveWindowRequestEntity(window_id, x, y)) ); } public resizeWindow(window_id: number, width: number, height: number) { this.ws?.send( JSON.stringify( new Protocol.ResizeWindowRequestEntity(window_id, width, height) ) ); } public closeWindow(window_id: number) { this.ws?.send( JSON.stringify(new Protocol.CloseWindowRequestEntity(window_id)) ); } public openWindow(data: Protocol.OpenWindowRequestEntity) { this.ws?.send(JSON.stringify(data)); } public focusIn(window_id: number) { this.ws?.send( JSON.stringify( new NormalWindowRequestEntity( Protocol.Commands.kFocuseWindow, window_id ) ) ); } public async addSignalSourceGroup(parent_uuid: string, name: string) { try { return await this.doRpc( new Protocol.AddSignalSourcesGroupRequestEntity(0, parent_uuid, name) ); } catch (e) { console.error(e); } } private _destoryed = false; public destory() { this._destoryed = true; this.ws?.close(); this.ws = null; } }