# HG changeset patch
# User drewp@bigasterisk.com
# Date 2022-05-24 06:32:37
# Node ID 1a96f8647126de3c3ac5fc120324755e9b7d0675
# Parent 17b268d2b7f3d38448fd1a0630a3591475d20cc8
big graph & autodep porting to make collector display labels from a syncedgraph
diff --git a/light9/collector/web/Light9CollectorUi.ts b/light9/collector/web/Light9CollectorUi.ts
--- a/light9/collector/web/Light9CollectorUi.ts
+++ b/light9/collector/web/Light9CollectorUi.ts
@@ -1,75 +1,63 @@
import debug from "debug";
import { html, LitElement } from "lit";
-import { customElement, property } from "lit/decorators.js";
+import { customElement, property, state } from "lit/decorators.js";
import ReconnectingWebSocket from "reconnectingwebsocket";
import { sortBy, uniq } from "underscore";
+import { SyncedGraph } from "../../web/SyncedGraph";
+import { GraphAwarePage } from "../../web/GraphAwarePage";
+import { getTopGraph, GraphChangedEvent } from "../../web/RdfdbSyncedGraph";
+import { NamedNode } from "n3";
+import { Patch } from "../../web/patch";
+import { linkHorizontal } from "d3";
-debug.enable('*');
+export { RdfdbSyncedGraph } from "../../web/RdfdbSyncedGraph";
+export { Light9CollectorDevice } from "../../web/collector/Light9CollectorDevice";
+
+debug.enable("*");
const log = debug("collector");
-class Updates {
- constructor() {
- this.listeners = [];
- }
- addListener(cb) {
- this.listeners.push(cb);
- }
- onMessage(msg) {
- this.listeners.forEach(function (lis) {
- lis(msg);
- });
- }
-}
-
@customElement("light9-collector-ui")
-export class Light9CollectorUi extends LitElement {
+export class Light9CollectorUi extends GraphAwarePage {
+ graph?: SyncedGraph;
static styles = [];
render() {
- return html`
-
-
+ return html`${super.render()}
Devices
-
-
- ${this.devices.map((d)=>html``)}
-
-
+ `;
+ }
+}
+
+@customElement("light9-collector-device-list")
+export class Light9CollectorDeviceList extends LitElement {
+ graph!: SyncedGraph;
+ @property() devices: NamedNode[] = [];
+
+ render() {
+ return html`
+ Devices
+
+ ${this.devices.map((d) => html``)}
`;
}
-
- @property() graph: Object = {};
- @property() updates: Updates;
- @property() devices: Array = [];
- // observers: [
- // 'onGraph(graph)',
- // ],
-
+
constructor() {
super();
- this.updates = new Updates();
- const ws = new ReconnectingWebSocket(location.href.replace("http", "ws") + "api/updates");
- ws.addEventListener("message", (ev: any) => {
- log("ws msg", ev);
- this.updates.onMessage(ev.data);
+ getTopGraph().then((g) => {
+ this.graph = g;
+ this.graph.runHandler(this.findDevices.bind(this), "findDevices");
});
}
-
- onGraph(graph) {
- this.graph.runHandler(this.findDevices.bind(this), "findDevices");
- }
-
- findDevices() {
- var U = function (x) {
- return this.graph.Uri(x);
- };
- this.set("devices", []);
-
+
+ findDevices(patch?: Patch) {
+ const U = this.graph.U();
+
+ this.devices = [];
let classes = this.graph.subjects(U("rdf:type"), U(":DeviceClass"));
uniq(sortBy(classes, "value"), true).forEach((dc) => {
sortBy(this.graph.subjects(U("rdf:type"), dc), "value").forEach((dev) => {
- this.push("devices", dev);
+ this.devices.push(dev as NamedNode);
});
});
}
diff --git a/light9/collector/web/index.html b/light9/collector/web/index.html
--- a/light9/collector/web/index.html
+++ b/light9/collector/web/index.html
@@ -5,7 +5,7 @@
-
+
- graph: [[status]]
-
-
-
-
-
+ constructor() {
+ super();
+ this.status = "startup";
+ this.graph = new SyncedGraph(
+ this.testGraph ? null : "/rdfdb/api/syncedGraph",
+ {
+ "": "http://light9.bigasterisk.com/",
+ dev: "http://light9.bigasterisk.com/device/",
+ rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
+ rdfs: "http://www.w3.org/2000/01/rdf-schema#",
+ xsd: "http://www.w3.org/2001/XMLSchema#",
+ },
+ (s: string) => {
+ this.status = s;
+ },
+ this.onClear.bind(this),
+ this.onGraphChanged.bind(this)
+ );
+ // (window as any).topSyncedGraph = this.graph;
+ res(this.graph);
+ }
+
+ private onGraphChanged(graph: SyncedGraph, patch: Patch) {
+ this.dispatchEvent(
+ new GraphChangedEvent("changed", {
+ detail: { graph, patch },
+ bubbles: true,
+ composed: true,
+ })
+ );
+ }
+ });
+});
+
+
+async function getTopGraph(): Promise {
+ const s = (window as any).topSyncedGraph;
+ return await s;
+}
+
+export { RdfdbSyncedGraph, getTopGraph };
\ No newline at end of file
diff --git a/light9/web/resource-display.html b/light9/web/ResourceDisplay.ts
rename from light9/web/resource-display.html
rename to light9/web/ResourceDisplay.ts
--- a/light9/web/resource-display.html
+++ b/light9/web/ResourceDisplay.ts
@@ -1,158 +1,165 @@
-
-
-
+import debug from "debug";
+import { css, html, LitElement, PropertyValues } from "lit";
+import { customElement, property, state } from "lit/decorators.js";
+import { NamedNode } from "n3";
+// import { GraphChangedEvent } from "../../web/RdfdbSyncedGraph";
+// import { runHandler } from "./GraphAwarePage";
+import { Patch, patchContainsPreds, patchSizeSummary } from "./patch";
+import { getTopGraph } from "./RdfdbSyncedGraph";
+import { SyncedGraph } from "./SyncedGraph";
+debug.enable("*");
+const log = debug("device-el");
+const RDFS_LABEL = new NamedNode("http://www.w3.org/2000/01/rdf-schema#label");
-
-
-
+ .resource {
+ border: 1px solid #545454;
+ border-radius: 5px;
+ padding: 1px;
+ margin: 2px;
+ background: rgb(49, 49, 49);
+ display: inline-block;
+ text-shadow: 1px 1px 2px black;
+ }
+ .resource.minor {
+ background: none;
+ border: none;
+ }
+ .resource a {
+ color: rgb(150, 150, 255);
+ padding: 1px;
+ display: inline-block;
+ }
+ .resource.minor a {
+ text-decoration: none;
+ color: rgb(155, 155, 193);
+ padding: 0;
+ }
+ `,
+ ];
-
-
- {{label}}
-
-
-
+ render() {
+ return html`
+ ${this.label}
+ `;
+ //
+ //
+
+ //
+ //
+ // New label:
+ //
+ //
+ //
+ //
+ //
+ // `;
+ }
+ // callers might set this as string or pass a NamedNode.
+ @property() uri?: NamedNode | string;
+
+ @state() label: string = "";
+ @property() rename: boolean = false;
+ @property() minor: boolean = false;
+ // @state() renameTo: String; notify: true };
-
-
- New label:
-
-
-
-
-
-
-
-
-
+ constructor() {
+ super();
+ getTopGraph().then((g) => {
+ this.graph = g;
+ this.runUriHandler();
+ });
+ }
+
+ realUri(): NamedNode {
+ if (!this.uri) {
+ return new NamedNode("");
+ }
+ return typeof this.uri === "string" ? new NamedNode(this.uri) : this.uri;
+ }
+
+ href() {
+ if (!this.uri) {
+ return "javascript:;";
+ }
+ return typeof this.uri === "string" ? this.uri : this.uri.value;
+ }
+
+ updated(changedProperties: PropertyValues) {
+ if (changedProperties.has("uri")) {
+ if (!this.graph) {
+ return; /*too soon*/
+ }
+ this.runUriHandler();
+ }
+ }
+
+ resClasses() {
+ return this.minor ? "resource minor" : "resource";
+ }
+
+ runUriHandler() {
+ this.graph.runHandler(this.onUri.bind(this), `rdisplay ${this.href()}` /*needs uniqueness?*/);
+ }
+
+ onUri(patch?: Patch) {
+ if (!this.uri) {
+ this.label = "";
+ return;
+ }
+
+ const uri = this.realUri();
+ this.graph.runHandler(this.setLabel.bind(this), `label ${uri.value}`);
+ }
+
+ setLabel(patch?: Patch) {
+ if (patch && !patchContainsPreds(patch, [RDFS_LABEL])) {
+ return;
+ }
+ const uri = this.realUri();
+ this.label = this.graph.labelOrTail(uri);
+ }
+
+ onRename() {
+ this.renameTo = this.label;
+ this.shadowRoot.querySelector("#renameDialog").open();
+ this.shadowRoot.querySelector("#renameTo").setSelectionRange(0, -1);
+ }
+
+ onRenameKey(ev) {
+ if (ev.key == "Enter") {
+ this.shadowRoot.querySelector("[dialog-confirm]").click();
+ }
+ if (ev.key == "Escape") {
+ this.shadowRoot.querySelector("[dialog-dismiss]").click();
+ }
+ }
+
+ onRenameClosed() {
+ var dialog = this.shadowRoot.querySelector("#renameDialog");
+ if (dialog.closingReason.confirmed) {
+ var label = this.graph.Uri("rdfs:label");
+ var ctxs = this.graph.contextsWithPattern(this.uri, label, null);
+ if (ctxs.length != 1) {
+ throw new Error(`${ctxs.length} label stmts for ${this.uri.label}`);
+ }
+ this.graph.patchObject(typeof this.uri === "string" ? this.graph.Uri(this.uri) : this.uri, label, this.graph.Literal(this.renameTo), ctxs[0]);
+ }
+ }
+}
diff --git a/light9/web/graph.ts b/light9/web/SyncedGraph.ts
rename from light9/web/graph.ts
rename to light9/web/SyncedGraph.ts
--- a/light9/web/graph.ts
+++ b/light9/web/SyncedGraph.ts
@@ -1,196 +1,64 @@
import * as d3 from "d3";
import debug from "debug";
import * as N3 from "n3";
-import { Quad, Quad_Subject, Quad_Predicate, Quad_Object, Quad_Graph } from "n3";
-import { filter, sortBy, unique } from "underscore";
-import { allPatchSubjs, Patch } from "./patch";
+import { Quad, Quad_Object, Quad_Predicate, Quad_Subject } from "n3";
+import { sortBy, unique } from "underscore";
+import { AutoDependencies, HandlerFunc } from "./AutoDependencies";
+import { Patch, patchSizeSummary } from "./patch";
import { RdfDbClient } from "./rdfdbclient";
const log = debug("graph");
const RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
-interface QuadPattern {
- subject: Quad_Subject | null;
- predicate: Quad_Predicate | null;
- object: Quad_Object | null;
- graph: Quad_Graph | null;
-}
-
-class Handler {
- patterns: QuadPattern[];
- innerHandlers: Handler[];
- // a function and the quad patterns it cared about
- constructor(public func: ((p: Patch) => void) | null, public label: string) {
- this.patterns = []; // s,p,o,g quads that should trigger the next run
- this.innerHandlers = []; // Handlers requested while this one was running
- }
-}
-
-class AutoDependencies {
- handlers: Handler;
- handlerStack: Handler[];
- constructor() {
- // tree of all known Handlers (at least those with non-empty
- // patterns). Top node is not a handler.
- this.handlers = new Handler(null, "root");
- this.handlerStack = [this.handlers]; // currently running
- }
-
- runHandler(func: any, label: any) {
- // what if we have this func already? duplicate is safe?
-
- if (label == null) {
- throw new Error("missing label");
- }
-
- const h = new Handler(func, label);
- const tailChildren = this.handlerStack[this.handlerStack.length - 1].innerHandlers;
- const matchingLabel = filter(tailChildren, (c: { label: any }) => c.label === label).length;
- // ohno, something depends on some handlers getting run twice :(
- if (matchingLabel < 2) {
- tailChildren.push(h);
- }
- //console.time("handler #{label}")
- return this._rerunHandler(h, null);
- }
- //console.timeEnd("handler #{label}")
- //@_logHandlerTree()
-
- _rerunHandler(handler: Handler, patch: any) {
- handler.patterns = [];
- this.handlerStack.push(handler);
- try {
- if (handler.func === null) {
- throw new Error("tried to rerun root");
- }
- return handler.func(patch);
- } catch (e) {
- return log("error running handler: ", e);
- } finally {
- // assuming here it didn't get to do all its queries, we could
- // add a *,*,*,* handler to call for sure the next time?
- //log('done. got: ', handler.patterns)
- this.handlerStack.pop();
- }
- }
- // handler might have no watches, in which case we could forget about it
-
- _logHandlerTree() {
- log("handler tree:");
- var prn = function (h: Handler, depth: number) {
- let indent = "";
- for (let i = 0, end = depth, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) {
- indent += " ";
- }
- log(`${indent} \"${h.label}\" ${h.patterns.length} pats`);
- return Array.from(h.innerHandlers).map((c: any) => prn(c, depth + 1));
- };
- return prn(this.handlers, 0);
- }
-
- _handlerIsAffected(child: Handler, patchSubjs: Set) {
- if (patchSubjs === null) {
- return true;
- }
- if (!child.patterns.length) {
- return false;
- }
-
- for (let stmt of Array.from(child.patterns)) {
- if (stmt.subject === null) {
- // wildcard on subject
- return true;
- }
- if (patchSubjs.has(stmt.subject.value)) {
- return true;
- }
- }
-
- return false;
- }
-
- graphChanged(patch: Patch) {
- // SyncedGraph is telling us this patch just got applied to the graph.
-
- const subjs = allPatchSubjs(patch);
-
- var rerunInners = (cur: Handler) => {
- const toRun = cur.innerHandlers.slice();
- for (let child of Array.from(toRun)) {
- //match = @_handlerIsAffected(child, subjs)
- //continue if not match
- //log('match', child.label, match)
- //child.innerHandlers = [] # let all children get called again
-
- this._rerunHandler(child, patch);
- rerunInners(child);
- }
- };
- return rerunInners(this.handlers);
- }
-
- askedFor(s: Quad_Subject | null, p: Quad_Predicate | null, o: Quad_Object | null, g: Quad_Graph | null) {
- // SyncedGraph is telling us someone did a query that depended on
- // quads in the given pattern.
- const current = this.handlerStack[this.handlerStack.length - 1];
- if (current != null && current !== this.handlers) {
- return current.patterns.push({ subject: s, predicate: p, object: o, graph: g } as QuadPattern);
- }
- }
-}
-
export class SyncedGraph {
- _autoDeps: AutoDependencies;
- _client: any;
- graph: N3.Store;
+ private _autoDeps: AutoDependencies;
+ private _client: RdfDbClient;
+ private graph: N3.Store;
cachedFloatValues: any;
cachedUriValues: any;
prefixFuncs: (x: string) => string = (x) => x;
serial: any;
_nextNumber: any;
- // Main graph object for a browser to use. Syncs both ways with
- // rdfdb. Meant to hide the choice of RDF lib, so we can change it
+ // Main graph object for a browser to use. Consider using RdfdbSyncedGraph element to create & own
+ // one of these. Syncs both ways with rdfdb. Meant to hide the choice of RDF lib, so we can change it
// later.
//
// Note that _applyPatch is the only method to write to the graph, so
// it can fire subscriptions.
constructor(
- // patchSenderUrl is the /syncedGraph path of an rdfdb server.
- public patchSenderUrl: any,
+ // url is the /syncedGraph path of an rdfdb server.
+ public url: any,
// prefixes can be used in Uri(curie) calls.
public prefixes: { [short: string]: string },
private setStatus: any,
// called if we clear the graph
- private clearCb: any
+ private clearCb: any,
+ private onGraphChanged: (graph: SyncedGraph, newPatch: Patch)=>void
) {
this.graph = new N3.Store();
- this._autoDeps = new AutoDependencies(); // replaces GraphWatchers
+ this._autoDeps = new AutoDependencies();
this.clearGraph();
- if (this.patchSenderUrl) {
- this._client = new RdfDbClient(this.patchSenderUrl, this._clearGraphOnNewConnection.bind(this), this._applyPatch.bind(this), this.setStatus);
- }
+ this._client = new RdfDbClient(this.url, this._clearGraphOnNewConnection.bind(this), this._applyPatch.bind(this), this.setStatus);
}
clearGraph() {
// just deletes the statements; watchers are unaffected.
- if (this.graph != null) {
- this._applyPatch({ adds: [], dels: this.graph.getQuads(null, null, null, null) });
- }
+ this.cachedFloatValues = new Map(); // s + '|' + p -> number
+ this.cachedUriValues = new Map(); // s + '|' + p -> Uri
+ this._applyPatch({ adds: [], dels: this.graph.getQuads(null, null, null, null) });
// if we had a Store already, this lets N3.Store free all its indices/etc
this.graph = new N3.Store();
this._addPrefixes(this.prefixes);
- this.cachedFloatValues = new Map(); // s + '|' + p -> number
- return (this.cachedUriValues = new Map()); // s + '|' + p -> Uri
}
_clearGraphOnNewConnection() {
// must not send a patch to the server!
- log("graph: clearGraphOnNewConnection");
+ log("clearGraphOnNewConnection");
this.clearGraph();
- log("graph: clearGraphOnNewConnection done");
+ log("clearGraphOnNewConnection done");
if (this.clearCb != null) {
return this.clearCb();
}
@@ -203,6 +71,10 @@ export class SyncedGraph {
this.prefixFuncs = N3.Util.prefixes(this.prefixes);
}
+ U() { // just a shorthand
+ return this.Uri.bind(this);
+ }
+
Uri(curie: string) {
if (curie == null) {
throw new Error("no uri");
@@ -299,20 +171,21 @@ export class SyncedGraph {
_applyPatch(patch: Patch) {
// In most cases you want applyAndSendPatch.
//
- // This is the only method that writes to @graph!
- let quad: any;
+ // This is the only method that writes to this.graph!
+ log("patch from server [1]")
this.cachedFloatValues.clear();
this.cachedUriValues.clear();
- for (quad of Array.from(patch.dels)) {
+ for (let quad of Array.from(patch.dels)) {
//log("remove #{JSON.stringify(quad)}")
const did = this.graph.removeQuad(quad);
}
//log("removed: #{did}")
- for (quad of Array.from(patch.adds)) {
+ for (let quad of Array.from(patch.adds)) {
this.graph.addQuad(quad);
}
- //log('applied patch locally', patchSizeSummary(patch))
- return this._autoDeps.graphChanged(patch);
+ log("applied patch locally", patchSizeSummary(patch));
+ this._autoDeps.graphChanged(patch);
+ this.onGraphChanged(this, patch);
}
getObjectPatch(s: N3.NamedNode, p: N3.NamedNode, newObject: N3.Quad_Object, g: N3.NamedNode): Patch {
@@ -326,7 +199,7 @@ export class SyncedGraph {
}
patchObject(s: N3.NamedNode, p: N3.NamedNode, newObject: N3.Quad_Object, g: N3.NamedNode) {
- return this.applyAndSendPatch(this.getObjectPatch(s, p, newObject, g));
+ this.applyAndSendPatch(this.getObjectPatch(s, p, newObject, g));
}
clearObjects(s: N3.NamedNode, p: N3.NamedNode, g: N3.NamedNode) {
@@ -336,7 +209,7 @@ export class SyncedGraph {
});
}
- runHandler(func: any, label: any) {
+ runHandler(func: HandlerFunc, label: string) {
// runs your func once, tracking graph calls. if a future patch
// matches what you queried, we runHandler your func again (and
// forget your queries from the first time).
@@ -350,7 +223,7 @@ export class SyncedGraph {
this.serial += 1;
//label = label + @serial
- return this._autoDeps.runHandler(func, label);
+ this._autoDeps.runHandler(func, label);
}
_singleValue(s: Quad_Subject, p: Quad_Predicate) {
@@ -416,13 +289,13 @@ export class SyncedGraph {
return ret;
}
- objects(s: any, p: any) {
+ objects(s: any, p: any): Quad_Object[] {
this._autoDeps.askedFor(s, p, null, null);
const quads = this.graph.getQuads(s, p, null, null);
return Array.from(quads).map((q: { object: any }) => q.object);
}
- subjects(p: any, o: any) {
+ subjects(p: any, o: any): Quad_Subject[] {
this._autoDeps.askedFor(null, p, o, null);
const quads = this.graph.getQuads(null, p, o, null);
return Array.from(quads).map((q: { subject: any }) => q.subject);
@@ -454,7 +327,7 @@ export class SyncedGraph {
return out;
}
- contains(s: any, p: any, o: any) {
+ contains(s: any, p: any, o: any): boolean {
this._autoDeps.askedFor(s, p, o, null);
log("contains calling getQuads when graph has ", this.graph.size);
return this.graph.getQuads(s, p, o, null).length > 0;
diff --git a/light9/collector/web/Light9CollectorDevice.ts b/light9/web/collector/Light9CollectorDevice.ts
rename from light9/collector/web/Light9CollectorDevice.ts
rename to light9/web/collector/Light9CollectorDevice.ts
--- a/light9/collector/web/Light9CollectorDevice.ts
+++ b/light9/web/collector/Light9CollectorDevice.ts
@@ -1,7 +1,11 @@
-import * as debug from "debug";
+import debug from "debug";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators.js";
+import { NamedNode } from "n3";
+import { GraphChangedEvent } from "../RdfdbSyncedGraph";
+export {ResourceDisplay} from "../ResourceDisplay"
debug.enable("*");
+const log = debug("device-el");
@customElement("light9-collector-device")
export class Light9CollectorDevice extends LitElement {
@@ -34,38 +38,50 @@ export class Light9CollectorDevice exten
render() {
return html`
-
+
out attr |
value |
chan |
-
-
- {{item.attr}} |
- {{item.val}} → |
- {{item.chan}} |
-
-
+ ${this.attrs.map(
+ (item) => html`
+
+ ${item.attr} |
+ ${item.val} → |
+ ${item.chan} |
+
+ `
+ )}
`;
}
- @property() graph: Object = {};
- @property() uri: Object = {};
- @property() attrs: Array = [];
+ @property({
+ // todo don't rebuild uri; pass it right
+ converter: (s: string | null) => new NamedNode(s || ""),
+ })
+ uri: NamedNode = new NamedNode("");
+ @property() attrs: Array<{ attr: string; valClass: string; val: string; chan: string }> = [];
+ constructor() {
+ super();
+ // addGraphChangeListener(this.onGraphChanged.bind(this));
+ }
+ onChanged(ev: GraphChangedEvent) {
+ log("patch from server [5]");
+ }
// observers: [
// "initUpdates(updates)",
// ],
- initUpdates(updates) {
- updates.addListener(function (msg) {
- if (msg.outputAttrsSet && msg.outputAttrsSet.dev == this.uri.value) {
- this.set("attrs", msg.outputAttrsSet.attrs);
- this.attrs.forEach(function (row) {
- row.valClass = row.val == 255 ? "full" : row.val ? "nonzero" : "";
- });
- }
- });
- }
+ // initUpdates(updates) {
+ // updates.addListener(function (msg) {
+ // if (msg.outputAttrsSet && msg.outputAttrsSet.dev == this.uri.value) {
+ // this.set("attrs", msg.outputAttrsSet.attrs);
+ // this.attrs.forEach(function (row) {
+ // row.valClass = row.val == 255 ? "full" : row.val ? "nonzero" : "";
+ // });
+ // }
+ // });
+ // }
}
diff --git a/light9/web/collector/README.md b/light9/web/collector/README.md
new file mode 100644
--- /dev/null
+++ b/light9/web/collector/README.md
@@ -0,0 +1,1 @@
+this is meant to be at light9/collector/web but I couldn't figure out the vite paths
\ No newline at end of file
diff --git a/light9/web/rdfdbclient.ts b/light9/web/rdfdbclient.ts
--- a/light9/web/rdfdbclient.ts
+++ b/light9/web/rdfdbclient.ts
@@ -9,10 +9,10 @@ export class RdfDbClient {
_patchesReceived: number;
_patchesSent: number;
_connectionId: string;
- _reconnectionTimeout: number | null;
- ws: WebSocket | undefined;
- _pingLoopTimeout: any;
- // Send and receive patches from rdfdb
+ _reconnectionTimeout?: number;
+ ws?: WebSocket;
+ _pingLoopTimeout?: number;
+ // Send and receive patches from rdfdb. Primarily used in SyncedGraph.
//
// What this should do, and does not yet, is keep the graph
// 'coasting' over a reconnect, applying only the diffs from the old
@@ -30,7 +30,6 @@ export class RdfDbClient {
this._patchesReceived = 0;
this._patchesSent = 0;
this._connectionId = "??";
- this._reconnectionTimeout = null;
this.ws = undefined;
this._newConnection();
@@ -63,7 +62,7 @@ export class RdfDbClient {
}
sendPatch(patch: Patch) {
- log("rdfdbclient: queue patch to server ", patchSizeSummary(patch));
+ log("queue patch to server ", patchSizeSummary(patch));
this._patchesToSend.push(patch);
this._updateStatus();
this._continueSending();
@@ -76,35 +75,37 @@ export class RdfDbClient {
this.ws.close();
}
this.ws = new WebSocket(fullUrl);
+ this.ws.onopen = this.onWsOpen.bind(this);
+ this.ws.onerror = this.onWsError.bind(this);
+ this.ws.onclose = this.onWsClose.bind(this);
+ this.ws.onmessage = this._onMessage.bind(this);
+ }
- this.ws.onopen = () => {
- log("rdfdbclient: new connection to", fullUrl);
- this._updateStatus();
- this.clearGraphOnNewConnection();
- return this._pingLoop();
- };
+ private onWsOpen() {
+ log("new connection to", this.patchSenderUrl);
+ this._updateStatus();
+ this.clearGraphOnNewConnection();
+ return this._pingLoop();
+ }
- this.ws.onerror = (e: Event) => {
- log("rdfdbclient: ws error " + e);
- if (this.ws !== undefined) {
- const closeHandler = this.ws.onclose?.bind(this.ws);
- if (!closeHandler) {
- throw new Error();
- }
- closeHandler(new CloseEvent("forced"));
+ private onWsError(e: Event) {
+ log("ws error", e);
+ if (this.ws !== undefined) {
+ const closeHandler = this.ws.onclose?.bind(this.ws);
+ if (!closeHandler) {
+ throw new Error();
}
- };
+ closeHandler(new CloseEvent("forced"));
+ }
+ }
- this.ws.onclose = (ev: CloseEvent) => {
- log("rdfdbclient: ws close");
- this._updateStatus();
- if (this._reconnectionTimeout != null) {
- clearTimeout(this._reconnectionTimeout);
- }
- this._reconnectionTimeout = (setTimeout(this._newConnection.bind(this), 1000) as unknown) as number;
- };
-
- this.ws.onmessage = this._onMessage.bind(this);
+ private onWsClose(ev: CloseEvent) {
+ log("ws close");
+ this._updateStatus();
+ if (this._reconnectionTimeout !== undefined) {
+ clearTimeout(this._reconnectionTimeout);
+ }
+ this._reconnectionTimeout = (setTimeout(this._newConnection.bind(this), 1000) as unknown) as number;
}
_pingLoop() {
@@ -115,7 +116,7 @@ export class RdfDbClient {
if (this._pingLoopTimeout != null) {
clearTimeout(this._pingLoopTimeout);
}
- this._pingLoopTimeout = setTimeout(this._pingLoop.bind(this), 10000);
+ this._pingLoopTimeout = (setTimeout(this._pingLoop.bind(this), 10000) as unknown) as number;
}
}
@@ -131,6 +132,7 @@ export class RdfDbClient {
if (input.connectedAs) {
this._connectionId = input.connectedAs;
} else {
+ log("patch from server [0]")
parseJsonPatch(input, this.applyPatch.bind(this));
this._patchesReceived++;
}
@@ -148,7 +150,7 @@ export class RdfDbClient {
const sendOne = (patch: any, cb: (arg0: any) => any) => {
return toJsonPatch(patch, (json: string) => {
- log("rdfdbclient: send patch to server, " + json.length + " bytes");
+ log("send patch to server, " + json.length + " bytes");
if (!this.ws) {
throw new Error("can't send");
}