diff --git a/frontend/App.vue b/frontend/App.vue index 9d7fc0a..144d8cd 100644 --- a/frontend/App.vue +++ b/frontend/App.vue @@ -57,7 +57,7 @@ async function onModelUpdateRequest(event: NetworkUpdateEvent) { } if (shutdownRequest !== null) { console.log("Shutting down the connection as requested by the server"); - event.disconnectForALittleBit(); + event.disconnect(); } sceneDocument.value = doc triggerRef(sceneDocument); // Why not triggered automatically? diff --git a/frontend/misc/network.ts b/frontend/misc/network.ts index fa14aa1..44c9b89 100644 --- a/frontend/misc/network.ts +++ b/frontend/misc/network.ts @@ -19,12 +19,12 @@ class NetworkUpdateEventModel { export class NetworkUpdateEvent extends Event { models: NetworkUpdateEventModel[]; - disconnectForALittleBit: () => void; + disconnect: () => void; - constructor(models: NetworkUpdateEventModel[], disconnectForALittleBit: () => void) { + constructor(models: NetworkUpdateEventModel[], disconnect: () => void) { super("update"); this.models = models; - this.disconnectForALittleBit = disconnectForALittleBit; + this.disconnect = disconnect; } } @@ -59,45 +59,36 @@ export class NetworkManager extends EventTarget { } } - private async monitorDevServer(url: URL, pendingTimeout: { id: number } = {id: -1}) { - try { - // WARNING: This will spam the console logs with failed requests when the server is down - const controller = new AbortController(); - let response = await fetch(url.toString(), {signal: controller.signal}); - // console.log("Monitoring", url.toString(), response); - if (response.status === 200) { - let lines = readLinesStreamings(response.body!.getReader()); - for await (let line of lines) { - if (!line || !line.startsWith("data:")) continue; - let data: { name: string, hash: string, is_remove: boolean | null } = JSON.parse(line.slice(5)); - // console.debug("WebSocket message", data); - let urlObj = new URL(url); - urlObj.searchParams.delete("api_updates"); - urlObj.searchParams.set("api_object", data.name); - this.foundModel(data.name, data.hash, urlObj.toString(), data.is_remove, async () => { - console.log("Disconnecting for a little bit"); - controller.abort(); - clearTimeout(pendingTimeout.id!); - pendingTimeout.id = -2; - setTimeout(() => { - console.log("Reconnecting after a little bit"); - this.monitorDevServer(url, pendingTimeout) - }, settings.monitorEveryMs * 50); - }); + private async monitorDevServer(url: URL, stop: () => boolean = () => false) { + while (!stop()) { + try { + // WARNING: This will spam the console logs with failed requests when the server is down + const controller = new AbortController(); + let response = await fetch(url.toString(), {signal: controller.signal}); + // console.log("Monitoring", url.toString(), response); + if (response.status === 200) { + let lines = readLinesStreamings(response.body!.getReader()); + for await (let line of lines) { + if (stop()) break; + if (!line || !line.startsWith("data:")) continue; + let data: { name: string, hash: string, is_remove: boolean | null } = JSON.parse(line.slice(5)); + // console.debug("WebSocket message", data); + let urlObj = new URL(url); + urlObj.searchParams.delete("api_updates"); + urlObj.searchParams.set("api_object", data.name); + this.foundModel(data.name, data.hash, urlObj.toString(), data.is_remove, async () => { + controller.abort(); // Notify the server that we are done + }); + } } + controller.abort(); + } catch (e) { // Ignore errors (retry very soon) } - } catch (e) { // Ignore errors (retry very soon) + await new Promise(resolve => setTimeout(resolve, settings.monitorEveryMs)); } - if (pendingTimeout.id >= -1) { - pendingTimeout.id = setTimeout(() => { - console.log("Reconnecting fast"); - this.monitorDevServer(url, pendingTimeout) - }, settings.monitorEveryMs); - } - return; } - private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnectForALittleBit: () => void = () => { + private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnect: () => void = () => { }) { let prevHash = this.knownObjectHashes[name]; // console.debug("Found model", name, "with hash", hash, "and previous hash", prevHash); @@ -109,7 +100,7 @@ export class NetworkManager extends EventTarget { if (!(name in this.knownObjectHashes)) return; // Nothing to remove... delete this.knownObjectHashes[name]; // Also update buffered updates if the model is removed - //this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name); + this.bufferedUpdates = this.bufferedUpdates.filter(m => m.name !== name); } let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove); this.bufferedUpdates.push(newModel); @@ -117,7 +108,7 @@ export class NetworkManager extends EventTarget { // Optimization: try to batch updates automatically for faster rendering if (this.batchTimeout !== null) clearTimeout(this.batchTimeout); this.batchTimeout = setTimeout(() => { - this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnectForALittleBit)); + this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnect)); this.bufferedUpdates = []; }, batchTimeout); } diff --git a/yacv_server/myhttp.py b/yacv_server/myhttp.py index 34ce981..672da44 100644 --- a/yacv_server/myhttp.py +++ b/yacv_server/myhttp.py @@ -75,7 +75,7 @@ class HTTPHandler(SimpleHTTPRequestHandler): with self.yacv.frontend_lock.r_locked(): # Avoid accepting new connections while shutting down - if self.yacv.shutting_down.is_set() and not self.yacv.at_least_one_client.is_set(): + if self.yacv.shutting_down.is_set() and self.yacv.at_least_one_client.is_set(): self.send_error(HTTPStatus.SERVICE_UNAVAILABLE, 'Server is shutting down') return self.yacv.at_least_one_client.set()