Skip to content

Commit 8bf3889

Browse files
committed
feat: begin setting up global websocket instance
- setup websocket instance in store -setup connection, error, and close logic - used for connecting to python backend
1 parent 2292c0a commit 8bf3889

File tree

8 files changed

+215
-52
lines changed

8 files changed

+215
-52
lines changed
+2-43
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,10 @@
11
import { onMount } from 'solid-js'
2-
import { sendToRTCServer } from '@utils/hooks/websocket'
3-
4-
const PORT = 7856
5-
6-
interface IWebSocket {
7-
ws: WebSocket
8-
pc: RTCPeerConnection
9-
dc: RTCDataChannel
10-
camStream: any
11-
}
2+
import { initWebSocket } from '@utils/hooks/websocket'
123

134
const webSocketHandler = () => {
145
onMount(async () => {
15-
initwebSocket()
6+
initWebSocket()
167
})
178
}
189

19-
/********************************* connect *************************************/
20-
21-
/**
22-
* @description initialize connection to the server
23-
* we use websocket heartbeat to the server
24-
* every 10 seconds
25-
*/
26-
const initwebSocket = () => {
27-
const socket: IWebSocket['ws'] = new WebSocket(`ws://127.0.0.1:${PORT}/camera/`)
28-
29-
socket.onopen = () => {
30-
setInterval(() => {
31-
sendToRTCServer(
32-
{
33-
msg: {
34-
msg_type: 'heartbeat',
35-
receiver: '',
36-
sender: '',
37-
msg: '',
38-
},
39-
},
40-
socket,
41-
)
42-
}, 1000 * 10)
43-
}
44-
45-
//* TODO: Add notification to the user
46-
socket.onerror = (error) => {
47-
console.log('WebSocket error: ' + error)
48-
}
49-
}
50-
5110
export default webSocketHandler

GUI/ETVR/src/store/api/selectors.ts

+36
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
11
import { createMemo } from 'solid-js'
22
import { restState, endpointsMap } from './restAPI'
3+
import { rtcState } from './websocket'
34

45
export const restStatus = createMemo(() => restState().status)
56
export const restDevice = createMemo(() => restState().device)
67
export const restResponse = createMemo(() => restState().response)
78
export const endpoints = createMemo(() => endpointsMap)
9+
10+
/**
11+
* @description
12+
* @returns {RTCState}
13+
* @example
14+
* const status = rtcStatus()
15+
* if (status === RTCState.CONNECTED) {
16+
* // do something
17+
* }
18+
*/
19+
export const rtcStatus = createMemo(() => rtcState().status)
20+
21+
export const rtcMessageType = createMemo(() => rtcState().messageType)
22+
23+
/**
24+
* @description get the current websocket instance
25+
* @returns {WebSocket} the current websocket instance
26+
* @example
27+
* const ws = rtcWebSocket()
28+
* ws.send(JSON.stringify({ msg: 'hello' }))
29+
*
30+
* Note: must use a try catch block to catch any errors that may occur do to possible null values
31+
*
32+
* try {
33+
* const ws = rtcWebSocket()
34+
* ws.send(JSON.stringify({ msg: 'hello' }))
35+
* } catch (e) {
36+
* console.error(e)
37+
* }
38+
*/
39+
export const rtcWebSocket = createMemo(() => rtcState().ws)
40+
41+
export const rtcConnectedPeers = createMemo(() => rtcState().connectedPeers)
42+
export const rtcConnectInterval = createMemo(() => rtcState().connectInterval)
43+
export const rtcTimeout = createMemo(() => rtcState().timeout)

GUI/ETVR/src/store/api/websocket.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { createMemo } from 'solid-js'
2+
import { createStore, produce } from 'solid-js/store'
3+
import { RTCMessageType, RTCState } from '@utils/enums'
4+
import { sendToRTCServer } from '@utils/hooks/websocket'
5+
6+
const PORT = 7856
7+
8+
export interface IWebSocket {
9+
ws: WebSocket
10+
pc?: RTCPeerConnection
11+
dc?: RTCDataChannel
12+
status: RTCState
13+
messageType: RTCMessageType
14+
camStream?: any
15+
connectedPeers?: string[]
16+
connectInterval?: NodeJS.Timeout
17+
timeout?: number
18+
}
19+
20+
export const defaultState: IWebSocket = {
21+
ws: new WebSocket(`ws://127.0.0.1:${PORT}/camera/`),
22+
status: RTCState.DISCONNECTED,
23+
messageType: RTCMessageType.VIDEO_OFFER,
24+
camStream: null,
25+
connectedPeers: [],
26+
connectInterval: undefined,
27+
timeout: 250,
28+
}
29+
30+
const [state, setState] = createStore<IWebSocket>(defaultState)
31+
32+
export const setRTCStatus = (status: RTCState) => {
33+
setState(
34+
produce((s) => {
35+
s.status = status
36+
}),
37+
)
38+
}
39+
40+
export const setRTCMessageType = (messageType: RTCMessageType) => {
41+
setState(
42+
produce((s) => {
43+
s.messageType = messageType
44+
}),
45+
)
46+
}
47+
48+
export const setRTCWebSocket = (ws: WebSocket) => {
49+
setState(
50+
produce((s) => {
51+
s.ws = ws
52+
}),
53+
)
54+
}
55+
56+
export const setConnectInterval = (interval: NodeJS.Timeout) => {
57+
setState(
58+
produce((s) => {
59+
s.connectInterval = interval
60+
}),
61+
)
62+
}
63+
64+
export const setRTCTimeout = (time: number) => {
65+
setState(
66+
produce((s) => {
67+
s.timeout = time
68+
}),
69+
)
70+
}
71+
72+
export const rtcState = createMemo(() => state)

GUI/ETVR/src/styles/index.css

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
transition: background-image 300ms ease;
7373
}
7474

75+
/* This style is REQUIRED due to how kobalte handles focus-visible state */
7576
* {
7677
@apply outline-none [&[data-focus-visible]]:outline-white;
7778
}

GUI/ETVR/src/utils/enums.ts

+42-5
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,26 @@ export enum loaderType {
2626

2727
/**
2828
* @description Enum for the different types of notifications
29+
* @enum {string}
30+
* @property {string} ERROR - An Error notification
31+
* @property {string} SUCCESS - A Success notification
32+
* @property {string} INFO - An Info notification
33+
* @property {string} WARNING - A Warning notification
34+
* @property {string} DEFAULT - A Default notification
2935
*/
3036
export enum ENotificationType {
3137
ERROR = 'ERROR',
3238
SUCCESS = 'SUCCESS',
3339
INFO = 'INFO',
3440
WARNING = 'WARNING',
41+
DEFAULT = 'DEFAULT',
3542
}
3643

3744
/**
3845
* @description Enum for the different types of notifications actions
46+
* @enum {string}
47+
* @property {string} OS - The notification will be handled by the OS
48+
* @property {string} APP - The notification will be handled by the APP
3949
*/
4050
export enum ENotificationAction {
4151
OS = 'OS',
@@ -44,11 +54,38 @@ export enum ENotificationAction {
4454

4555
/**
4656
* @description Enum for the different types of RTC message types
57+
* @enum {string}
58+
* @property {string} VIDEO_OFFER - The connection is not yet open.
59+
* @property {string} VIDEO_ANSWER - The connection is open and ready to communicate.
60+
* @property {string} NEW_ICE_CANDIDATE - A new camera is wanting to connect.
61+
* @property {string} CAMERA_ERROR - The connection is closed or couldn't be opened.
62+
* @property {string} CLOSE_CAMERA_STREAM - The connection is requesting to close.
4763
*/
4864
export enum RTCMessageType {
49-
VIDEO_OFFER = 'video-offer',
50-
VIDEO_ANSWER = 'video-answer',
51-
NEW_ICE_CANDIDATE = 'new-ice-candidate',
52-
REMOTE_DESKTOP = 'remote-desktop',
53-
CLOSE_REMOTE_DESKTOP = 'close-remote-desktop',
65+
VIDEO_OFFER = 'VIDEO_OFFER',
66+
VIDEO_ANSWER = 'VIDEO_ANSWER',
67+
NEW_ICE_CANDIDATE = 'NEW_ICE_CANDIDATE',
68+
CAMERA_ERROR = 'CAMERA_ERROR',
69+
CLOSE_CAMERA_STREAM = 'CLOSE_CAMERA_STREAM',
70+
}
71+
72+
/**
73+
* @description Enum for the different States of the RTC connection
74+
* @enum {string}
75+
* @property {string} CONNECTING - The connection is not yet open.
76+
* @property {string} OPEN - The connection is open and ready to communicate.
77+
* @property {string} CLOSING - The connection is in the process of closing.
78+
* @property {string} CLOSED - The connection is closed or couldn't be opened.
79+
* @property {string} ERROR - The connection is in an error state.
80+
* @property {string} DISCONNECTED - The connection is disconnected.
81+
* @property {string} CONNECTED - The connection is connected.
82+
*/
83+
export enum RTCState {
84+
CONNECTING = 'CONNECTING',
85+
OPEN = 'OPEN',
86+
CLOSING = 'CLOSING',
87+
CLOSED = 'CLOSED',
88+
ERROR = 'ERROR',
89+
DISCONNECTED = 'DISCONNECTED',
90+
CONNECTED = 'CONNECTED',
5491
}
+59-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,69 @@
1+
import { rtcWebSocket, rtcTimeout } from '@store/api/selectors'
2+
import { setRTCStatus, setConnectInterval, setRTCTimeout } from '@store/api/websocket'
3+
import { RTCState } from '@utils/enums'
4+
15
interface IWebRTCMessage {
26
msg: string | object | null | undefined
37
}
48

5-
const sendToRTCServer = (msg: IWebRTCMessage, ws: WebSocket) => {
9+
const sendToRTCServer = (msg: IWebRTCMessage) => {
610
if (!msg) {
711
console.error('[sendToRTCServer]: Message is null or undefined')
812
return
913
}
10-
ws.send(JSON.stringify(msg))
14+
rtcWebSocket().send(JSON.stringify(msg))
15+
}
16+
17+
export const check = () => {
18+
const ws = rtcWebSocket()
19+
if (!ws || ws.readyState == WebSocket.CLOSED) {
20+
//check if websocket instance is closed, if so call `init` function.
21+
setRTCStatus(RTCState.DISCONNECTED)
22+
initWebSocket()
23+
}
24+
}
25+
26+
/********************************* connect *************************************/
27+
/**
28+
* @description initialize connection to the server
29+
* we use websocket heartbeat to the server
30+
* every 10 seconds
31+
*/
32+
const initWebSocket = () => {
33+
setRTCStatus(RTCState.CONNECTING)
34+
rtcWebSocket().onopen = () => {
35+
setRTCStatus(RTCState.CONNECTED)
36+
setInterval(() => {
37+
sendToRTCServer({
38+
msg: {
39+
msg_type: 'heartbeat',
40+
receiver: '',
41+
sender: '',
42+
msg: '',
43+
},
44+
})
45+
}, 1000 * 10)
46+
}
47+
//* TODO: Add notification to the user
48+
rtcWebSocket().onerror = (e) => {
49+
setRTCStatus(RTCState.ERROR)
50+
console.error('Socket encountered error: ', e, 'Closing socket')
51+
rtcWebSocket().close()
52+
}
53+
//* TODO: Add notification to the user
54+
rtcWebSocket().onclose = (e) => {
55+
//increment retry interval
56+
setRTCTimeout((rtcTimeout() as number) + (rtcTimeout() as number))
57+
//call check function after timeout
58+
setConnectInterval(setTimeout(check, Math.min(10000, rtcTimeout() as number)))
59+
console.log(
60+
`Socket is closed. Reconnect will be attempted in ${Math.min(
61+
10000 / 1000,
62+
((rtcTimeout() as number) + (rtcTimeout() as number)) / 1000,
63+
)} second.`,
64+
e.reason,
65+
)
66+
}
1167
}
1268

13-
export { sendToRTCServer }
69+
export { sendToRTCServer, initWebSocket }

GUI/ETVR/tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@src/*": ["src/*"],
3030
"@assets/*": ["./assets/*"],
3131
"@hooks/*": ["src/utils/hooks/*"],
32+
"@store/*": ["src/store/*"],
3233
"@static/*": ["src/static/*"],
3334
"@utils/*": ["src/utils/*"]
3435
}

GUI/ETVR/vite.config.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default defineConfig({
1616
'@src': path.resolve(__dirname, './src'),
1717
'@assets': path.resolve(__dirname, './assets'),
1818
'@hooks': path.resolve(__dirname, './src/utils/hooks'),
19+
'@store': path.resolve(__dirname, './src/store'),
1920
'@static': path.resolve(__dirname, './src/static'),
2021
'@utils': path.resolve(__dirname, './src/utils'),
2122
},
@@ -33,5 +34,5 @@ export default defineConfig({
3334
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
3435
// produce sourcemaps for debug builds
3536
sourcemap: !!process.env.TAURI_DEBUG,
36-
}
37+
},
3738
})

0 commit comments

Comments
 (0)