import ReconnectingWebSocket from "reconnecting-websocket"; import NormalWindowRequestEntity from "src/entities/NormalWindowRequestEntity"; import { PlanEntity } from "src/entities/PlanEntity"; import { SignalSourceEntity } from "src/entities/SignalSourceEntity"; import SubtitleEntity from "src/entities/SubtitleEntity"; import { Protocol } from "src/entities/WSProtocol"; import EventBus, { EventNamesDefine } from "./EventBus"; class _RpcInfo { send_timestamp: number; timeout_timestamp: number; callback: (is_fail: boolean, packet: Protocol.Commands, data: string) => void; constructor( timeout_timestamp: number, callback: ( is_fail: boolean, packet: Protocol.Commands, data: string ) => void ) { this.timeout_timestamp = timeout_timestamp; this.send_timestamp = new Date().getTime(); this.callback = callback; } public reject() { this.callback(true, new Protocol.Commands(), ""); } public reslove(is_fail: boolean, packet: Protocol.Commands, data: string) { this.callback(is_fail, packet, data); } } export default class ClientConnection { ws: ReconnectingWebSocket | null = null; url = ""; user_name = ""; password = ""; _is_login = false; _rpc_id_counter = 0; rpc_map = new Map(); public login_callback: | ((this: ClientConnection, logined: boolean) => void) | null = null; constructor( url: string, user_name?: string | null, password?: string | null ) { this.url = url; this.user_name = user_name ?? ""; this.password = password ?? ""; if (this.ws) { this.ws.close(); } this.ws = new ReconnectingWebSocket(url); this.initializeWs(); setInterval(() => { this.checkRpcTimeout(); }, 1000); } get is_connected() { return this.ws && this.ws.readyState == WebSocket.OPEN; } get is_login() { return this._is_login; } checkRpcTimeout() { const current_datetime = new Date().getTime(); this.rpc_map.forEach((v, k, m) => { if (current_datetime - v.send_timestamp > v.timeout_timestamp) { v.reject(); m.delete(k); } }); } 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.is_connected) { const request = new Protocol.LoginRequest(this.user_name, this.password); this.ws?.send(JSON.stringify(request)); } } onClose(ev: any) { this._is_login = false; EventBus.getInstance().emit(EventNamesDefine.WebSocketClose, this); } onError(ev: any) { this._is_login = false; EventBus.getInstance().emit(EventNamesDefine.WebSocketError, this); } onOpen(ev: any) { this._is_login = false; this.login(); } onMessage(ev: MessageEvent) { try { const packet = JSON.parse(ev.data) as Protocol.PacketEntity; if (packet) { if (packet.has_exception) { console.error(ev.data); this.rpc_map.get(packet.rpc_id)?.reject(); this.rpc_map.delete(packet.rpc_id); } 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)) { this.rpc_map.get(packet.rpc_id)?.reslove(false, packet, ev.data); this.rpc_map.delete(packet.rpc_id); } else { EventBus.getInstance().emit( packet.flag == Protocol.PacketEntity.FLAG_NOTIFY ? EventNamesDefine.NotifyMessage : packet.flag == Protocol.PacketEntity.FLAG_RESPONSE ? EventNamesDefine.ResponseMessage : EventNamesDefine.UnKnow, { 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)) { this.rpc_map.get(rpc_id)?.reject(); this.rpc_map.delete(rpc_id); } request.rpc_id = rpc_id; this.rpc_map.set( rpc_id, new _RpcInfo( request.timeout, (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(); } } } ) ); if (request) { request.timeout; } this.ws?.send(JSON.stringify(request)); }); } public async getSignalSources() { try { return await this.doRpc( new Protocol.GetSignalSourcesRequest() ); } catch (e) { console.error(e); } } public async addSignalSourceGroup(parent_uuid: string, name: string) { try { return await this.doRpc( new Protocol.AddSignalSourceGroupRequestEntity(0, parent_uuid, name) ); } catch (e) { console.error(e); } } public async editSignalSourceGroup(uuid: string, name: string) { try { return await this.doRpc( new Protocol.EditSignalSourceGroupRequestEntity(0, uuid, name) ); } catch (e) { console.error(e); } } public async deleteSignalSourceGroup(uuid: string) { try { return await this.doRpc( new Protocol.DeleteSignalSourceGroupRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public async addSignalSource(item: SignalSourceEntity) { try { return await this.doRpc( new Protocol.AddSignalSourceRequestEntity(0, item) ); } catch (e) { console.error(e); } } public async editSignalSource(item: SignalSourceEntity) { try { return await this.doRpc( new Protocol.EditSignalSourceRequestEntity(0, item) ); } catch (e) { console.error(e); } } public async deleteSignalSource(uuid: string) { try { return await this.doRpc( new Protocol.DeleteSignalSourceRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public async getModes() { try { return await this.doRpc( new Protocol.GetModesRequestEntity() ); } catch (e) { console.error(e); } } public async addPlanGroup(group_uuid: string, name: string) { try { return await this.doRpc( new Protocol.AddPlanGroupRequestEntity(0, group_uuid, name) ); } catch (e) { console.error(e); } } public async editPlanGroup(uuid: string, name: string) { try { return await this.doRpc( new Protocol.EditPlanGroupRequestEntity(0, uuid, name) ); } catch (e) { console.error(e); } } public async deletePlanGroup(uuid: string) { try { return await this.doRpc( new Protocol.DeletePlanGroupRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public async addPlan(entity?: PlanEntity) { try { return await this.doRpc( new Protocol.AddPlanRequestEntity(0, entity) ); } catch (e) { console.error(e); } } public async editPlan(entity?: PlanEntity) { try { return await this.doRpc( new Protocol.EditPlanRequestEntity(0, entity) ); } catch (e) { console.error(e); } } public async deletePlan(uuid: string) { try { return await this.doRpc( new Protocol.DeletePlanRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public async getCurrentRunningPlan() { try { return await this.doRpc( new Protocol.GetCurrentRunningPlanRequestEntity(0) ); } catch (e) { console.error(e); } } public runPlan(uuid: string) { this.ws?.send(JSON.stringify(new Protocol.RunPlanRequestEntity(uuid))); } public stopCurrentRunningPlan() { this.ws?.send( JSON.stringify(new Protocol.StopCurrentRunningPlanRequestEntity()) ); } public async getPlans() { try { return await this.doRpc( new Protocol.GetPlansRequestEntity() ); } catch (e) { console.error(e); } } public async addModeGroup(group_uuid: string, name: string) { try { return await this.doRpc( new Protocol.AddModeGroupRequestEntity(0, group_uuid, name) ); } catch (e) { console.error(e); } } public async editModeGroup(uuid: string, name: string) { try { return await this.doRpc( new Protocol.EditModeGroupRequestEntity(0, uuid, name) ); } catch (e) { console.error(e); } } public async deleteModeGroup(uuid: string) { try { return await this.doRpc( new Protocol.DeleteModeGroupRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public async addMode(group_uuid?: string, name?: string, index?: number) { try { console.log(index); return await this.doRpc( new Protocol.AddModeRequestEntity(0, name, group_uuid, index) ); } catch (e) { console.error(e); } } public async editMode(uuid?: string, name?: string, index?: number) { try { return await this.doRpc( new Protocol.EditModeRequestEntity(0, name, uuid, index) ); } catch (e) { console.error(e); } } public async deleteMode(uuid: string) { try { return await this.doRpc( new Protocol.DeleteModeRequestEntity(0, uuid) ); } catch (e) { console.error(e); } } public callMode(uuid: string) { this.ws?.send(JSON.stringify(new Protocol.CallModeRequestEntity(uuid))); } 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 setWallRowCol(wall_row: number, wall_col: number) { this.ws?.send( JSON.stringify( new Protocol.SetApplicationConfigRequestEntity( 0, "wall_row", wall_row.toString() ) ) ); this.ws?.send( JSON.stringify( new Protocol.SetApplicationConfigRequestEntity( 0, "wall_col", wall_col.toString() ) ) ); } public setPowerOnPlan(uuid: string) { this.ws?.send( JSON.stringify( new Protocol.SetApplicationConfigRequestEntity(0, "power_on_plan", uuid) ) ); } public windowFitGrid(window_id: number) { this.ws?.send( JSON.stringify( new NormalWindowRequestEntity( Protocol.Commands.kWindowFitGrid, window_id ) ) ); } public async getSubtitle() { try { return await this.doRpc( new Protocol.GetSubtitleRequestEntity(0) ); } catch (e) { console.error(e); } } public async setSubtitle(subtitle: SubtitleEntity) { try { return await this.doRpc( new Protocol.SetSubtitleRequestEntity(0, subtitle) ); } catch (e) { console.error(e); } } public async setSystemNetwork( request: Protocol.SetSystemNetworkRequestEntity ) { return await this.doRpc(request); } public async setSystemGraphics( request: Protocol.SetSystemGraphicsRequestEntity ) { return await this.doRpc(request); } public async setSystemOther(request: Protocol.SetSystemOtherRequestEntity) { return await this.doRpc(request); } public async getSupportResolutions() { try { return await this.doRpc( new Protocol.GetSupportResolutionsRequestEntity(0) ); } catch (e) { console.error(e); } } public async getOutputBoardSetting() { try { return await this.doRpc( new Protocol.GetOutputBoardSettingRequestEntity(0) ); } catch (e) { console.error(e); } } public async setOutputBoardSetting( request: Protocol.SetOutputBoardSettingRequestEntity ) { try { return await this.doRpc( request ); } catch (e) { console.error(e); } } public async restoreOutputBoard() { try { return await this.doRpc( new Protocol.RestoreOutputBoardRequestEntity(0) ); } catch (e) { console.error(e); } } public restartDevice(delay_ms?: number) { this.ws?.send( JSON.stringify(new Protocol.RestartDeviceRequestEntity(delay_ms)) ); } public destory() { if (this.ws) { this.ws.onclose = null; this.ws.onerror = null; this.ws.onopen = null; this.ws.onmessage = null; } this.ws?.close(); this.ws = null; } } export interface NotifyMessage { packet: Protocol.PacketEntity; data: string; }