diff --git a/receivers/common/web/ConnectionMonitor.ts b/receivers/common/web/ConnectionMonitor.ts index e8a5cef..52f9a51 100644 --- a/receivers/common/web/ConnectionMonitor.ts +++ b/receivers/common/web/ConnectionMonitor.ts @@ -1,57 +1,73 @@ import { Opcode } from 'common/Packets'; - -const connectionPingTimeout = 2500; -const heartbeatRetries = {}; -let connections = []; -let uiUpdateCallbacks = { - onConnect: null, - onDisconnect: null, -} -const logger = window.targetAPI.logger; +import { Logger, LoggerType } from 'common/Logger'; // Window might be re-created while devices are still connected export function setUiUpdateCallbacks(callbacks: any) { - uiUpdateCallbacks = callbacks; + const logger = window.targetAPI.logger; + let frontendConnections = []; + + window.targetAPI.onConnect((_event, value: any) => { + frontendConnections.push(value.sessionId); + callbacks.onConnect(frontendConnections); + }); + window.targetAPI.onDisconnect((_event, value: any) => { + const index = frontendConnections.indexOf(value.sessionId); + if (index != -1) { + frontendConnections.splice(index, 1); + callbacks.onDisconnect(frontendConnections, value.sessionId); + } + }); window.targetAPI.getSessions().then((sessions: string[]) => { - connections = sessions; - if (connections.length > 0) { - uiUpdateCallbacks.onConnect(connections, true); + logger.info('Window created with current sessions:', sessions); + frontendConnections = sessions; + + if (frontendConnections.length > 0) { + callbacks.onConnect(frontendConnections, true); } }); } -function onPingPong(value: any) { - heartbeatRetries[value.sessionId] = 0; -} -window.targetAPI.onPing((_event, value: any) => onPingPong(value)); -window.targetAPI.onPong((_event, value: any) => onPingPong(value)); +export class ConnectionMonitor { + private static initialized = false; + private static connectionPingTimeout = 2500; + private static heartbeatRetries = new Map(); + private static backendConnections = new Map(); + private static logger; -window.targetAPI.onConnect((_event, value: any) => { - logger.info(`Device connected: ${JSON.stringify(value)}`); - connections.push(value.sessionId); - uiUpdateCallbacks.onConnect(connections); -}); -window.targetAPI.onDisconnect((_event, value: any) => { - logger.info(`Device disconnected: ${JSON.stringify(value)}`); - const index = connections.indexOf(value.sessionId); - if (index != -1) { - connections.splice(index, 1); - uiUpdateCallbacks.onDisconnect(connections, value.sessionId); - } -}); + constructor() { + if (!ConnectionMonitor.initialized) { + ConnectionMonitor.logger = new Logger('ConnectionMonitor', LoggerType.BACKEND); -setInterval(() => { - if (connections.length > 0) { - window.targetAPI.sendSessionMessage(Opcode.Ping, null); + setInterval(() => { + if (ConnectionMonitor.backendConnections.size > 0) { + for (const sessionId in ConnectionMonitor.backendConnections) { + if (ConnectionMonitor.heartbeatRetries.get(sessionId) > 3) { + ConnectionMonitor.logger.warn(`Could not ping device with connection id ${sessionId}. Disconnecting...`); + ConnectionMonitor.backendConnections.get(sessionId).disconnect(sessionId); + } - for (const sessionId of connections) { - if (heartbeatRetries[sessionId] > 3) { - logger.warn(`Could not ping device with connection id ${sessionId}. Disconnecting...`); - window.targetAPI.disconnectDevice(sessionId); - } + ConnectionMonitor.backendConnections.get(sessionId).send(Opcode.Ping, null); + ConnectionMonitor.heartbeatRetries.set(sessionId, ConnectionMonitor.heartbeatRetries.get(sessionId) === undefined ? 1 : ConnectionMonitor.heartbeatRetries.get(sessionId) + 1); + } + } + }, ConnectionMonitor.connectionPingTimeout); - heartbeatRetries[sessionId] = heartbeatRetries[sessionId] === undefined ? 1 : heartbeatRetries[sessionId] + 1; + ConnectionMonitor.initialized = true; } } -}, connectionPingTimeout); + + public static onPingPong(value: any) { + ConnectionMonitor.heartbeatRetries[value.sessionId] = 0; + } + + public static onConnect(listener: any, value: any) { + ConnectionMonitor.logger.info(`Device connected: ${JSON.stringify(value)}`); + ConnectionMonitor.backendConnections.set(value.sessionId, listener); + } + + public static onDisconnect(listener: any, value: any) { + ConnectionMonitor.logger.info(`Device disconnected: ${JSON.stringify(value)}`); + ConnectionMonitor.backendConnections.delete(value.sessionId); + } +} diff --git a/receivers/common/web/main/Preload.ts b/receivers/common/web/main/Preload.ts index 2b07ca0..001f8ed 100644 --- a/receivers/common/web/main/Preload.ts +++ b/receivers/common/web/main/Preload.ts @@ -38,12 +38,8 @@ if (TARGET === 'electron') { onDeviceInfo: (callback: any) => electronAPI.ipcRenderer.on('device-info', callback), getDeviceInfo: () => preloadData.deviceInfo, getSessions: () => electronAPI.ipcRenderer.invoke('get-sessions'), - sendSessionMessage: (opcode: Opcode, message: any) => electronAPI.ipcRenderer.send('send-session-message', { opcode: opcode, message: message }), - disconnectDevice: (session: string) => electronAPI.ipcRenderer.send('disconnect-device', session), onConnect: (callback: any) => electronAPI.ipcRenderer.on('connect', callback), onDisconnect: (callback: any) => electronAPI.ipcRenderer.on('disconnect', callback), - onPing: (callback: any) => electronAPI.ipcRenderer.on('ping', callback), - onPong: (callback: any) => electronAPI.ipcRenderer.on('pong', callback), logger: loggerInterface, }); @@ -51,17 +47,17 @@ if (TARGET === 'electron') { } else if (TARGET === 'webOS' || TARGET === 'tizenOS') { preloadData = { onDeviceInfoCb: () => { logger.error('Main: Callback not set while fetching device info'); }, + getSessionsCb: () => { logger.error('Main: Callback not set while calling getSessions'); }, onConnectCb: (_, value: any) => { logger.error('Main: Callback not set while calling onConnect'); }, onDisconnectCb: (_, value: any) => { logger.error('Main: Callback not set while calling onDisconnect'); }, - onPingCb: (_, value: any) => { logger.error('Main: Callback not set while calling onPing'); }, }; window.targetAPI = { onDeviceInfo: (callback: () => void) => preloadData.onDeviceInfoCb = callback, + getDeviceInfo: () => preloadData.deviceInfo, + getSessions: (callback: () => void) => preloadData.getSessionsCb = callback, onConnect: (callback: (_, value: any) => void) => preloadData.onConnectCb = callback, onDisconnect: (callback: (_, value: any) => void) => preloadData.onDisconnectCb = callback, - onPing: (callback: (_, value: any) => void) => preloadData.onPingCb = callback, - getDeviceInfo: () => preloadData.deviceInfo, logger: loggerInterface, }; } else { diff --git a/receivers/common/web/player/Preload.ts b/receivers/common/web/player/Preload.ts index 3135d31..b9faf9c 100644 --- a/receivers/common/web/player/Preload.ts +++ b/receivers/common/web/player/Preload.ts @@ -42,12 +42,8 @@ if (TARGET === 'electron') { onSetVolume: (callback: any) => electronAPI.ipcRenderer.on("setvolume", callback), onSetSpeed: (callback: any) => electronAPI.ipcRenderer.on("setspeed", callback), getSessions: () => electronAPI.ipcRenderer.invoke('get-sessions'), - sendSessionMessage: (opcode: Opcode, message: any) => electronAPI.ipcRenderer.send('send-session-message', { opcode: opcode, message: message }), - disconnectDevice: (session: string) => electronAPI.ipcRenderer.send('disconnect-device', session), onConnect: (callback: any) => electronAPI.ipcRenderer.on('connect', callback), onDisconnect: (callback: any) => electronAPI.ipcRenderer.on('disconnect', callback), - onPing: (callback: any) => electronAPI.ipcRenderer.on('ping', callback), - onPong: (callback: any) => electronAPI.ipcRenderer.on('pong', callback), logger: loggerInterface, }); @@ -64,6 +60,9 @@ if (TARGET === 'electron') { onSeekCb: () => { logger.error('Player: Callback "onseek" not set'); }, onSetVolumeCb: () => { logger.error('Player: Callback "setvolume" not set'); }, onSetSpeedCb: () => { logger.error('Player: Callback "setspeed" not set'); }, + getSessionsCb: () => { logger.error('Player: Callback "getSessions" not set'); }, + onConnectCb: () => { logger.error('Player: Callback "onConnect" not set'); }, + onDisconnectCb: () => { logger.error('Player: Callback "onDisconnect" not set'); }, }; window.targetAPI = { @@ -76,6 +75,9 @@ if (TARGET === 'electron') { onSeek: (callback: any) => { preloadData.onSeekCb = callback; }, onSetVolume: (callback: any) => { preloadData.onSetVolumeCb = callback; }, onSetSpeed: (callback: any) => { preloadData.onSetSpeedCb = callback; }, + getSessions: (callback: any) => { preloadData.getSessionsCb = callback; }, + onConnect: (callback: any) => { preloadData.onConnectCb = callback; }, + onDisconnect: (callback: any) => { preloadData.onDisconnectCb = callback; }, logger: loggerInterface, }; } else { diff --git a/receivers/electron/src/Main.ts b/receivers/electron/src/Main.ts index 0f6a6f1..67a5e69 100644 --- a/receivers/electron/src/Main.ts +++ b/receivers/electron/src/Main.ts @@ -4,6 +4,7 @@ import { DiscoveryService } from 'common/DiscoveryService'; import { TcpListenerService } from 'common/TcpListenerService'; import { WebSocketListenerService } from 'common/WebSocketListenerService'; import { NetworkService } from 'common/NetworkService'; +import { ConnectionMonitor } from 'common/ConnectionMonitor'; import { Logger, LoggerType } from 'common/Logger'; import { Updater } from './Updater'; import * as os from 'os'; @@ -22,6 +23,7 @@ export class Main { static tcpListenerService: TcpListenerService; static webSocketListenerService: WebSocketListenerService; static discoveryService: DiscoveryService; + static connectionMonitor: ConnectionMonitor; static tray: Tray; private static cachedInterfaces = null; @@ -143,6 +145,7 @@ export class Main { private static onReady() { Main.createTray(); + Main.connectionMonitor = new ConnectionMonitor(); Main.discoveryService = new DiscoveryService(); Main.discoveryService.start(); @@ -190,20 +193,20 @@ export class Main { l.emitter.on("setspeed", (message) => Main.playerWindow?.webContents?.send("setspeed", message)); l.emitter.on('connect', (message) => { + ConnectionMonitor.onConnect(l, message); Main.mainWindow?.webContents?.send('connect', message); Main.playerWindow?.webContents?.send('connect', message); }); l.emitter.on('disconnect', (message) => { + ConnectionMonitor.onDisconnect(l, message); Main.mainWindow?.webContents?.send('disconnect', message); Main.playerWindow?.webContents?.send('disconnect', message); }); l.emitter.on('ping', (message) => { - Main.mainWindow?.webContents?.send('ping', message); - Main.playerWindow?.webContents?.send('ping', message); + ConnectionMonitor.onPingPong(message); }); l.emitter.on('pong', (message) => { - Main.mainWindow?.webContents?.send('pong', message); - Main.playerWindow?.webContents?.send('pong', message); + ConnectionMonitor.onPingPong(message); }); l.start(); @@ -218,15 +221,6 @@ export class Main { ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => { l.send(Opcode.VolumeUpdate, value); }); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ipcMain.on('send-session-message', (event: IpcMainEvent, value: any) => { - l.send(value.opcode, value.message); - }); - - ipcMain.on('disconnect-device', (event: IpcMainEvent, value: string) => { - l.disconnect(value); - }); }); ipcMain.on('send-download-request', async () => {