Skip to content

Commit

Permalink
refactor: migrate entire websocket stack to solid-use
Browse files Browse the repository at this point in the history
-  migrate to useWebSocket hook
- simplify code
- each camera object now has useWebSocket accessors
  • Loading branch information
ZanzyTHEbar committed Apr 1, 2023
1 parent 861d7d6 commit a1827a1
Show file tree
Hide file tree
Showing 8 changed files with 43 additions and 252 deletions.
2 changes: 1 addition & 1 deletion GUI/ETVR/src/components/Settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const Settings = (props: IProps) => {
placeholder="Setup camera address"
id="camera-address"
required={true}
type='text'
type="text"
onChange={(value) => {
props.onChangeCameraAddress(value)
}}
Expand Down
9 changes: 3 additions & 6 deletions GUI/ETVR/src/components/WebSocket/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import { onMount, Show } from 'solid-js'
import { Show } from 'solid-js'
import { OrangeLoader } from '@components/Loader'
import { showCameraView } from '@store/ui/selectors'
import { initWebSocket } from '@utils/hooks/websocket'

const WebSocketHandler = () => {
onMount(async () => {
initWebSocket()
})
// TODO: Grab selected camera from store, connect if not connected, and display video stream on component mounted

const WebSocketHandler = () => {
return (
<div class={'w-full h-full'}>
<Show
Expand Down
20 changes: 1 addition & 19 deletions GUI/ETVR/src/static/types/enums/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,27 +34,9 @@ export enum ENotificationAction {
APP = 'APP',
}

export enum RTCMessageType {
VIDEO_OFFER = 'VIDEO_OFFER',
VIDEO_ANSWER = 'VIDEO_ANSWER',
NEW_ICE_CANDIDATE = 'NEW_ICE_CANDIDATE',
CAMERA_ERROR = 'CAMERA_ERROR',
CLOSE_CAMERA_STREAM = 'CLOSE_CAMERA_STREAM',
}

export enum RTCState {
CONNECTING = 'CONNECTING',
OPEN = 'OPEN',
CLOSING = 'CLOSING',
CLOSED = 'CLOSED',
ERROR = 'ERROR',
DISCONNECTED = 'DISCONNECTED',
CONNECTED = 'CONNECTED',
}

export enum RANGE_INPUT_FORMAT {
EYE_POSITION_SCALAR = 'Eye position scalar',
THRESHOLD = 'Thresshold',
THRESHOLD = 'Threshold',
ROTATION = 'Rotation',
}

Expand Down
6 changes: 0 additions & 6 deletions GUI/ETVR/src/store/api/selectors.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { createMemo } from 'solid-js'
import { ghRestState, ghEndpoint } from './ghAPI'
import { restState, endpointsMap } from './restAPI'
import { rtcState } from './websocket'

/********************************* rest *************************************/
export const restStatus = createMemo(() => restState().status)
Expand All @@ -13,8 +12,3 @@ export const ghRestStatus = createMemo(() => ghRestState().status)
export const firmwareAssets = createMemo(() => ghRestState().assets)
export const firmwareVersion = createMemo(() => ghRestState().version)
export const ghRESTEndpoint = createMemo(() => ghEndpoint)
/********************************* websockets *************************************/
export const rtcStatus = createMemo(() => rtcState().status)
export const rtcMessageType = createMemo(() => rtcState().messageType)
export const rtcConnectInterval = createMemo(() => rtcState().connectInterval)
export const rtcTimeout = createMemo(() => rtcState().timeout)
56 changes: 0 additions & 56 deletions GUI/ETVR/src/store/api/websocket.ts

This file was deleted.

69 changes: 8 additions & 61 deletions GUI/ETVR/src/store/camera/camera.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { createMemo } from 'solid-js'
import { createStore, produce } from 'solid-js/store'
import { RTCMessageType, RTCState } from '@src/static/types/enums'
import { type IWebSocket } from '@store/api/websocket'

export enum CameraStatus {
ACTIVE = 'ACTIVE',
Expand All @@ -21,7 +19,7 @@ export interface ICamera {
type: CameraType
address: string
activeCameraSection: string
ws: IWebSocket
ws: object
}

const tempCameraComponents: ICamera[] = [
Expand All @@ -30,78 +28,42 @@ const tempCameraComponents: ICamera[] = [
type: CameraType.WIRELESS,
address: '192.168.0.204',
activeCameraSection: 'Left Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
{
status: CameraStatus.LOADING,
type: CameraType.WIRELESS,
address: '192.168.0.232',
activeCameraSection: 'Right Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
{
status: CameraStatus.LOADING,
type: CameraType.WIRELESS,
address: '192.168.0.234',
activeCameraSection: 'Right Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
{
status: CameraStatus.LOADING,
type: CameraType.WIRELESS,
address: '192.168.0.204',
activeCameraSection: 'Left Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
{
status: CameraStatus.LOADING,
type: CameraType.WIRELESS,
address: '192.168.0.232',
activeCameraSection: 'Right Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
{
status: CameraStatus.LOADING,
type: CameraType.WIRELESS,
address: '192.168.0.234',
activeCameraSection: 'Right Eye',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
]

Expand All @@ -117,13 +79,7 @@ export const defaultState: ICameraStore = {
type: CameraType.NONE,
address: ' ',
activeCameraSection: ' ',
ws: {
status: RTCState.DISCONNECTED,
messageType: RTCMessageType.VIDEO_OFFER,
camStream: null,
connectInterval: undefined,
timeout: 250,
},
ws: {},
},
}

Expand Down Expand Up @@ -168,13 +124,4 @@ export const resetSelectedCamera = () => {
)
}

export const setCameraSocket = (camera: ICamera, ws: IWebSocket) => {
setState(
produce((s) => {
s.cameras = s.cameras.filter((c: { address: string }) => c.address !== camera.address)
s.cameras.push({ ...camera, ws })
}),
)
}

export const cameraState = createMemo(() => state)
129 changes: 26 additions & 103 deletions GUI/ETVR/src/utils/hooks/websocket/index.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,34 @@
// TODO: Switch to tauri websocket plugin - https://github.com/tauri-apps/tauri-plugin-websocket
import { RTCState } from '@src/static/types/enums'
import { rtcTimeout, rtcConnectInterval } from '@store/api/selectors'
import { setRTCStatus, setConnectInterval, setRTCTimeout } from '@store/api/websocket'
import { useWebSocket } from 'solidjs-use'
import { addNotification, ENotificationType } from '@hooks/notifications'
import { getGlobalNotificationsType } from '@store/app/settings/selectors'
import { cameras } from '@store/camera/selectors'

import { isEmpty } from '@utils/index'
const PORT = 7856
const LOCAL_HOST = 'wss://127.0.0.1'

interface IWebRTCMessage {
msg: string | object | null | undefined
}

export const sendToRTCServer = (msg: IWebRTCMessage) => {
if (!msg) {
console.error('[sendToRTCServer]: Message is null or undefined')
return
}
cameras().forEach((camera) => {
if (camera.ws.ws) camera.ws.ws.send(JSON.stringify(msg))
})
}

export const check = () => {
cameras().forEach((camera) => {
if (!camera.ws.ws || camera.ws.ws.readyState == WebSocket.CLOSED) {
//check if websocket instance is closed, if so call `init` function.
setRTCStatus(RTCState.DISCONNECTED)
initWebSocket()
}
})
}

export const generateWebsocketClients = () => {
const clients = cameras().map((camera, i) => {
if (!camera.ws.ws) {
camera.ws.ws = new WebSocket(`${LOCAL_HOST}:${PORT + i}`)
return camera.ws
}
})

clients.forEach((client) => {
console.log('[WebSocket Handler]: Websocket Clients - ', client)
})
}

/********************************* connect *************************************/
/**
* @description initialize connection to the server
* we use websocket heartbeat to the server
* every 10 seconds
*/
export const initWebSocket = () => {
setRTCStatus(RTCState.CONNECTING)
cameras().forEach((camera) => {
if (camera.ws.ws) {
camera.ws.ws.onopen = () => {
setRTCTimeout(250) // reset timer to 250 on open of websocket connection
clearTimeout(rtcConnectInterval()) // clear Interval on open of websocket connection

setRTCStatus(RTCState.CONNECTED)
setInterval(() => {
sendToRTCServer({
msg: {
msg_type: 'heartbeat',
receiver: '',
sender: '',
msg: '',
},
})
}, 1000 * 10)

console.log('[WebSocket Client]: Connection Opened')
}
}
})
//* TODO: Add notification to the user
cameras().forEach((camera) => {
if (camera.ws.ws) {
camera.ws.ws.onerror = (e) => {
setRTCStatus(RTCState.ERROR)
console.error('[WebSocket Client]: Socket encountered error: ', e, 'Closing socket')
cameras().forEach((camera) => {
if (camera.ws.ws) {
if (camera.ws.ws.readyState != WebSocket.CONNECTING) {
if (camera.ws.ws.bufferedAmount <= 0) camera.ws.ws.close()
}
}
})
}
}
})
//* TODO: Add notification to the user
cameras().forEach((camera) => {
if (camera.ws.ws) {
camera.ws.ws.onclose = (e) => {
console.log(
`[WebSocket Client]: Socket is closed. Reconnect will be attempted in ${Math.min(
10000 / 1000,
((rtcTimeout() as number) + (rtcTimeout() as number)) / 1000,
)} second.`,
e.reason,
)
//increment retry interval
setRTCTimeout((rtcTimeout() as number) + (rtcTimeout() as number))
//call check function after timeout
setConnectInterval(setTimeout(check, Math.min(10000, rtcTimeout() as number)))
}
cameras().map((camera, i) => {
if (isEmpty(camera.ws)) {
const { status, data, send, open, close } = useWebSocket(`${LOCAL_HOST}:${PORT + i}`, {
autoReconnect: {
retries: 3,
delay: 1000,
onFailed() {
addNotification({
type: ENotificationType.ERROR,
action: getGlobalNotificationsType(),
title: 'Websocket Connection Failed',
message: `[WebSocket Handler]: Failed to connect to ${camera.address}`,
})
},
},
heartbeat: {
message: 'ping',
interval: 1000,
pongTimeout: 1000,
},
})
camera.ws = { status, data, send, open, close }
}
})
}
Loading

0 comments on commit a1827a1

Please sign in to comment.