This commit is contained in:
Martino Ferrari
2026-04-26 11:01:55 +02:00
parent 91b42027c9
commit e83e183673
17 changed files with 1009 additions and 182 deletions
+63 -2
View File
@@ -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 });
}