phase 10
This commit is contained in:
+63
-2
@@ -1,5 +1,5 @@
|
||||
import { readable, writable, type Readable } from './store';
|
||||
import type { SignalRef, SignalValue, SignalMeta } from './types';
|
||||
import { writable, type Readable } from './store';
|
||||
import type { SignalRef, SignalValue, SignalMeta, HistoryPoint } from './types';
|
||||
|
||||
// ── Types ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -31,6 +31,8 @@ class WsClient {
|
||||
private refCounts = new Map<string, number>();
|
||||
// subscriber callbacks per signal
|
||||
private subscribers = new Map<string, Set<SubscriberEntry>>();
|
||||
// pending history request callbacks keyed by "ds\0name"
|
||||
private histCallbacks = new Map<string, Array<(pts: HistoryPoint[]) => void>>();
|
||||
|
||||
connect(url: string): void {
|
||||
this.url = url;
|
||||
@@ -146,7 +148,25 @@ class WsClient {
|
||||
for (const s of subs) s.onMeta(meta);
|
||||
break;
|
||||
}
|
||||
case 'history': {
|
||||
const hcbs = this.histCallbacks.get(key);
|
||||
if (hcbs) {
|
||||
const pts: HistoryPoint[] = (msg.points ?? []).map((p: any) => ({
|
||||
ts: p.ts ?? p.TS,
|
||||
value: p.value ?? p.Value,
|
||||
}));
|
||||
this.histCallbacks.delete(key);
|
||||
for (const cb of hcbs) cb(pts);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'error':
|
||||
// Resolve any pending history callbacks with empty data on error
|
||||
if (key && this.histCallbacks.has(key)) {
|
||||
const hcbs = this.histCallbacks.get(key)!;
|
||||
this.histCallbacks.delete(key);
|
||||
for (const cb of hcbs) cb([]);
|
||||
}
|
||||
console.warn('[ws] server error', msg.code, msg.message);
|
||||
break;
|
||||
default:
|
||||
@@ -197,6 +217,47 @@ class WsClient {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Request historical data for a signal.
|
||||
* Resolves with an array of HistoryPoint once the server responds.
|
||||
* Resolves with [] if the datasource doesn't support history or times out.
|
||||
*/
|
||||
history(
|
||||
ref: SignalRef,
|
||||
start: string,
|
||||
end: string,
|
||||
maxPoints = 5000,
|
||||
): Promise<HistoryPoint[]> {
|
||||
return new Promise((resolve) => {
|
||||
const key = `${ref.ds}\0${ref.name}`;
|
||||
|
||||
// Timeout after 15 s — resolves with empty array
|
||||
const timer = setTimeout(() => {
|
||||
const cbs = this.histCallbacks.get(key);
|
||||
if (cbs) {
|
||||
const idx = cbs.indexOf(wrappedResolve);
|
||||
if (idx >= 0) cbs.splice(idx, 1);
|
||||
if (cbs.length === 0) this.histCallbacks.delete(key);
|
||||
}
|
||||
resolve([]);
|
||||
}, 15_000);
|
||||
|
||||
const wrappedResolve = (pts: HistoryPoint[]) => {
|
||||
clearTimeout(timer);
|
||||
resolve(pts);
|
||||
};
|
||||
|
||||
let cbs = this.histCallbacks.get(key);
|
||||
if (!cbs) {
|
||||
cbs = [];
|
||||
this.histCallbacks.set(key, cbs);
|
||||
}
|
||||
cbs.push(wrappedResolve);
|
||||
|
||||
this._send({ type: 'history', ds: ref.ds, name: ref.name, start, end, maxPoints });
|
||||
});
|
||||
}
|
||||
|
||||
write(ref: SignalRef, value: any): void {
|
||||
this._send({ type: 'write', ds: ref.ds, name: ref.name, value });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user