Files
telemetry_visualization/frontend/src/composables/websocket.ts

144 lines
3.8 KiB
TypeScript

import {
computed,
type MaybeRefOrGetter,
onMounted,
onUnmounted,
ref,
type Ref,
shallowRef,
toValue,
watch,
} from 'vue';
import type { TelemetryDefinition } from '@/composables/telemetry';
export interface TelemetryDataItem {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
timestamp: string;
}
interface TlmValue {
uuid: string;
value: TelemetryDataItem;
}
export class WebsocketHandle {
websocket: WebSocket | null;
should_be_connected: boolean;
connected: Ref<boolean>;
on_telem_value: Map<string, Array<(value: TelemetryDataItem) => void>>;
constructor() {
this.websocket = null;
this.should_be_connected = false;
this.connected = ref(false);
this.on_telem_value = new Map();
}
connect() {
this.should_be_connected = true;
if (this.websocket != null) {
return;
}
this.websocket = new WebSocket(
location.protocol.replace('http', 'ws') +
'//' +
location.host +
'/ws',
);
this.websocket.addEventListener('open', () => {
this.connected.value = true;
});
this.websocket.addEventListener('close', () => {
if (this.should_be_connected) {
this.disconnect();
setTimeout(() => {
this.connect();
}, 1000);
}
});
this.websocket.addEventListener('error', () => {
if (this.should_be_connected) {
this.disconnect();
setTimeout(() => {
this.connect();
}, 1000);
}
});
this.websocket.addEventListener('message', (event) => {
const message = JSON.parse(event.data);
if (message['TlmValue']) {
const tlm_value = message['TlmValue'] as TlmValue;
const listeners = this.on_telem_value.get(tlm_value.uuid);
if (listeners) {
listeners.forEach((listener) => {
listener(tlm_value.value);
});
}
}
});
}
disconnect() {
this.should_be_connected = false;
if (this.websocket == null) {
return;
}
this.connected.value = false;
this.websocket.close();
this.websocket = null;
this.on_telem_value.clear();
}
listen_to_telemetry(
telemetry: MaybeRefOrGetter<TelemetryDefinition | null>,
) {
const value_result = ref<TelemetryDataItem | null>(null);
const uuid = computed(() => {
const tlm = toValue(telemetry);
if (tlm) {
return tlm.uuid;
}
return null;
});
watch([uuid, this.connected], ([uuid_value, connected]) => {
if (connected && uuid_value) {
this.websocket?.send(
JSON.stringify({
RegisterTlmListener: {
uuid: uuid_value,
},
}),
);
if (!this.on_telem_value.has(uuid_value)) {
this.on_telem_value.set(uuid_value, []);
}
this.on_telem_value.get(uuid_value)?.push((value) => {
value_result.value = value;
});
}
});
return value_result;
}
}
export const WEBSOCKET_SYMBOL = Symbol();
export function useWebsocket() {
const handle = shallowRef<WebsocketHandle>(new WebsocketHandle());
onMounted(() => {
handle.value.connect();
});
onUnmounted(() => {
handle.value.disconnect();
});
return handle;
}