mirror of
https://github.com/yeicor-3d/yet-another-cad-viewer.git
synced 2025-12-19 22:24:17 +01:00
clean frontend disconnection protocol
This commit is contained in:
@@ -12,9 +12,10 @@ export let extrasNameValueHelpers = "__helpers";
|
||||
*
|
||||
* Remember to call mergeFinalize after all models have been merged (slower required operations).
|
||||
*/
|
||||
export async function mergePartial(url: string, name: string, document: Document): Promise<Document> {
|
||||
export async function mergePartial(url: string, name: string, document: Document, networkFinished: () => void = () => {}): Promise<Document> {
|
||||
// Load the new document
|
||||
let newDoc = await io.read(url);
|
||||
networkFinished()
|
||||
|
||||
// Remove any previous model with the same name
|
||||
await document.transform(dropByName(name));
|
||||
|
||||
@@ -7,22 +7,24 @@ class NetworkUpdateEventModel {
|
||||
url: string;
|
||||
// TODO: Detect and manage instances of the same object (same hash, different name)
|
||||
hash: string | null;
|
||||
isRemove: boolean;
|
||||
isRemove: boolean | null; // This is null for a shutdown event
|
||||
|
||||
constructor(name: string, url: string, hash: string | null, isDelete: boolean) {
|
||||
constructor(name: string, url: string, hash: string | null, isRemove: boolean | null) {
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.hash = hash;
|
||||
this.isRemove = isDelete;
|
||||
this.isRemove = isRemove;
|
||||
}
|
||||
}
|
||||
|
||||
export class NetworkUpdateEvent extends Event {
|
||||
models: NetworkUpdateEventModel[];
|
||||
disconnectForALittleBit: () => void;
|
||||
|
||||
constructor(models: NetworkUpdateEventModel[]) {
|
||||
constructor(models: NetworkUpdateEventModel[], disconnectForALittleBit: () => void) {
|
||||
super("update");
|
||||
this.models = models;
|
||||
this.disconnectForALittleBit = disconnectForALittleBit;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,37 +59,57 @@ export class NetworkManager extends EventTarget {
|
||||
}
|
||||
}
|
||||
|
||||
private async monitorDevServer(url: URL) {
|
||||
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
|
||||
let response = await fetch(url.toString());
|
||||
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 = JSON.parse(line.slice(5));
|
||||
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);
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) { // Ignore errors (retry very soon)
|
||||
}
|
||||
setTimeout(() => this.monitorDevServer(url), 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) {
|
||||
private foundModel(name: string, hash: string | null, url: string, isRemove: boolean | null, disconnectForALittleBit: () => void = () => {
|
||||
}) {
|
||||
let prevHash = this.knownObjectHashes[name];
|
||||
// console.debug("Found model", name, "with hash", hash, "and previous hash", prevHash);
|
||||
if (!hash || hash !== prevHash || isRemove) {
|
||||
if (!isRemove) {
|
||||
// Update known hashes
|
||||
if (isRemove == false) {
|
||||
this.knownObjectHashes[name] = hash;
|
||||
} else {
|
||||
} else if (isRemove == true) {
|
||||
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);
|
||||
}
|
||||
let newModel = new NetworkUpdateEventModel(name, url, hash, isRemove);
|
||||
this.bufferedUpdates.push(newModel);
|
||||
@@ -95,7 +117,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));
|
||||
this.dispatchEvent(new NetworkUpdateEvent(this.bufferedUpdates, disconnectForALittleBit));
|
||||
this.bufferedUpdates = [];
|
||||
}, batchTimeout);
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import {Matrix4} from "three/src/math/Matrix4.js"
|
||||
/** This class helps manage SceneManagerData. All methods are static to support reactivity... */
|
||||
export class SceneMgr {
|
||||
/** Loads a GLB model from a URL and adds it to the viewer or replaces it if the names match */
|
||||
static async loadModel(sceneUrl: Ref<string>, document: Document, name: string, url: string, updateHelpers: boolean = true, reloadScene: boolean = true): Promise<Document> {
|
||||
static async loadModel(sceneUrl: Ref<string>, document: Document, name: string, url: string, updateHelpers: boolean = true, reloadScene: boolean = true, networkFinished: () => void = () => {}): Promise<Document> {
|
||||
let loadStart = performance.now();
|
||||
|
||||
// Start merging into the current document, replacing or adding as needed
|
||||
document = await mergePartial(url, name, document);
|
||||
document = await mergePartial(url, name, document, networkFinished);
|
||||
|
||||
console.log("Model", name, "loaded in", performance.now() - loadStart, "ms");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user