diff --git a/Cargo.lock b/Cargo.lock index 981172d..f6bcf4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -685,6 +685,17 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -704,9 +715,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", + "futures-macro", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1491,6 +1504,7 @@ dependencies = [ "chrono", "derive_more 1.0.0", "fern", + "futures-util", "hex", "log", "prost", diff --git a/frontend/src/composables/websocket.ts b/frontend/src/composables/websocket.ts new file mode 100644 index 0000000..603bec9 --- /dev/null +++ b/frontend/src/composables/websocket.ts @@ -0,0 +1,112 @@ +import { computed, onMounted, onUnmounted, ref, type Ref, shallowRef, watch } from 'vue' + +class WebsocketHandle { + websocket: WebSocket | null; + should_be_connected: boolean; + connected: Ref; + on_telem_value: Mapvoid>>; + + 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", (event) => { + this.connected.value = true; + }); + this.websocket.addEventListener("close", (event) => { + if (this.should_be_connected) { + this.disconnect(); + setTimeout(() => { + this.connect(); + }, 1000); + } + }); + this.websocket.addEventListener("error", (event) => { + 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 uuid = message["TlmValue"]["uuid"]; + if (uuid) { + const listeners = this.on_telem_value.get(uuid); + if (listeners) { + listeners.forEach((listener) => { + listener(message["TlmValue"]["value"]); + }); + } + } + } + }); + } + + disconnect() { + this.should_be_connected = false; + if (this.websocket == null) { + return; + } + + this.websocket.close(); + this.websocket = null; + this.connected.value = false; + this.on_telem_value.clear(); + } + + listen_to_telemetry(telemetry: Ref) { + const value_result = ref(null); + + const uuid = computed(() => { + if (telemetry.value) { + return telemetry.value.uuid; + } + return null; + }); + + watch([uuid, this.connected], () => { + if (this.connected) { + let uuid_value = 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 function useWebsocket() { + const handle = shallowRef(new WebsocketHandle()); + + onMounted(() => { + handle.value.connect(); + }); + onUnmounted(() => { + handle.value.disconnect(); + }) + + return handle; +} diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 18a387a..355d647 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -1,20 +1,27 @@