wireless-esp32-tools-web-host/src/composables/websocket/websocketWrapper.ts

223 lines
6.0 KiB
TypeScript

import type {ApiJsonMsg, ControlMsg, ServerMsg} from "@/api";
import {ControlEvent, ControlMsgType} from "@/api";
import {isDevMode} from "@/composables/buildMode";
interface IWebsocket {
close(): void;
send(msg: ServerMsg): void;
}
class WebsocketDummy implements IWebsocket {
close(): void {
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
send(msg: ServerMsg) {
}
}
class OneTimeWebsocket implements IWebsocket {
private readonly heartBeatTimeout: number = 1;
private readonly host: string;
private readonly intervalId: number;
private readonly msgCallback: (ev: ServerMsg) => any;
private readonly ctrlCallback: (msg: ControlMsg) => any;
private readonly closeCallback: () => void;
private socket: WebSocket;
private heartBeatTimeCount: number;
private stoped: boolean;
private cleared: boolean;
private hasBeenConnected: boolean;
constructor(host: string,
msgCallback: (ev: ServerMsg) => any,
ctrlCallback: (msg: ControlMsg) => any,
closeCallback: () => void
) {
this.host = host;
this.stoped = false;
this.cleared = false;
this.hasBeenConnected = false;
this.msgCallback = msgCallback;
this.ctrlCallback = ctrlCallback;
this.closeCallback = closeCallback;
this.heartBeatTimeCount = 0;
this.intervalId = 0;
this.socket = new WebSocket(`ws://${this.host}/ws`);
this.setSocketProp();
this.intervalId = setInterval(() => {
if (this.heartBeatTimeCount > this.heartBeatTimeout) {
/* did not receive packet "heartBeatTimeout" times,
* connection may be lost: close the socket */
if (this.socket.readyState === this.socket.OPEN) {
console.log("No heart beat, break connection");
this.close();
this.clear();
}
if (isDevMode()) {
console.log("interval: ", this.heartBeatTimeCount, "state: ", this.socket.readyState);
}
}
this.heartBeatTimeCount++;
}, 2000);
}
private setSocketProp() {
this.socket.binaryType = 'arraybuffer'
this.heartBeatTimeCount = 0;
this.socket.onmessage = (ev) => {
this.heartBeatTimeCount = 0;
if (!ev.data)
return
const msg: ServerMsg = {
data: ev.data,
type: "json",
}
if (typeof ev.data === "string") {
msg.type = "json"
} else {
msg.type = "binary";
}
this.msgCallback(msg);
}
this.socket.onclose = (ev) => {
if (isDevMode()) {
console.log("ws closed", ev.reason, ev.code);
}
this.socket.onclose = null
this.socket.onopen = null
this.socket.onerror = null
this.socket.onmessage = null;
this.clear();
};
this.socket.onerror = (error) => {
this.close();
};
this.socket.onopen = ev => {
// console.log('WebSocket Connected');
if (this.stoped) {
this.close();
return;
}
this.heartBeatTimeCount = 0;
this.hasBeenConnected = true;
const msg: ControlMsg = {
type: ControlMsgType.WS_EVENT,
data: ControlEvent.CONNECTED,
}
this.ctrlCallback(msg);
};
const msg: ControlMsg = {
type: ControlMsgType.WS_EVENT,
data: ControlEvent.CONNECTING,
}
this.ctrlCallback(msg);
}
close() {
this.stoped = true;
this.socket.close();
}
send(msg: ServerMsg) {
if (this.socket.readyState !== WebSocket.OPEN) {
return;
}
if (isDevMode()) {
console.log('WebSocket proxies data ', msg);
}
this.socket.send(msg.data);
}
clear() {
if (this.cleared) {
return;
}
this.cleared = true;
clearInterval(this.intervalId);
const msg: ControlMsg = {
type: ControlMsgType.WS_EVENT,
data: ControlEvent.DISCONNECTED,
}
this.ctrlCallback(msg);
this.closeCallback();
}
}
export class WebsocketWrapper {
private socket: IWebsocket;
private msgCallback: (ev: ServerMsg) => any;
private ctrlCallback: (msg: ControlMsg) => any;
private timeoutId: number;
private host: string;
private stoped: boolean;
constructor() {
this.socket = new WebsocketDummy();
this.msgCallback = () => {};
this.ctrlCallback = () => {};
this.timeoutId = 0;
this.host = "";
this.stoped = false;
this.closeCallback = this.closeCallback.bind(this);
}
init(host: string,
msgCallback: (ev: ServerMsg) => any,
ctrlCallback: (msg: ControlMsg) => any)
{
if (this.host === host) {
return;
}
this.newConnection(host, msgCallback, ctrlCallback);
}
private newConnection(host: string,
msgCallback: (ev: ServerMsg) => any,
ctrlCallback: (msg: ControlMsg) => any)
{
this.socket.close();
this.host = host;
this.msgCallback = msgCallback;
this.ctrlCallback = ctrlCallback;
this.socket = new OneTimeWebsocket(host, this.msgCallback, this.ctrlCallback, this.closeCallback);
}
private closeCallback() {
if (this.stoped) {
return;
}
this.timeoutId = setTimeout(() =>
this.newConnection(this.host, this.msgCallback, this.ctrlCallback),
1000);
}
deinit() {
this.socket.close();
this.stoped = true;
clearTimeout(this.timeoutId);
}
send(msg: ServerMsg) {
this.socket.send(msg)
}
}