@vscada/core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/a11y/contrast.d.ts +11 -0
- package/dist/a11y/contrast.d.ts.map +1 -0
- package/dist/a11y/live-region.d.ts +42 -0
- package/dist/a11y/live-region.d.ts.map +1 -0
- package/dist/a11y/reduced-motion.d.ts +55 -0
- package/dist/a11y/reduced-motion.d.ts.map +1 -0
- package/dist/a11y/static-representation-registry.d.ts +20 -0
- package/dist/a11y/static-representation-registry.d.ts.map +1 -0
- package/dist/bindings/binding-fixture.d.ts +3 -0
- package/dist/bindings/binding-fixture.d.ts.map +1 -0
- package/dist/bindings/binding-types.d.ts +52 -0
- package/dist/bindings/binding-types.d.ts.map +1 -0
- package/dist/bindings/color-ramp.d.ts +9 -0
- package/dist/bindings/color-ramp.d.ts.map +1 -0
- package/dist/bindings/transforms.d.ts +103 -0
- package/dist/bindings/transforms.d.ts.map +1 -0
- package/dist/constants-DMdpoOyH.js +4 -0
- package/dist/contracts/connection-renderer.d.ts +16 -0
- package/dist/contracts/connection-renderer.d.ts.map +1 -0
- package/dist/contracts/connection-state.d.ts +8 -0
- package/dist/contracts/connection-state.d.ts.map +1 -0
- package/dist/contracts/handles.d.ts +83 -0
- package/dist/contracts/handles.d.ts.map +1 -0
- package/dist/contracts/primitive.d.ts +37 -0
- package/dist/contracts/primitive.d.ts.map +1 -0
- package/dist/contracts/renderer.d.ts +34 -0
- package/dist/contracts/renderer.d.ts.map +1 -0
- package/dist/contracts/tag-value.d.ts +33 -0
- package/dist/contracts/tag-value.d.ts.map +1 -0
- package/dist/contracts/time-source.d.ts +14 -0
- package/dist/contracts/time-source.d.ts.map +1 -0
- package/dist/custom-elements.json +1068 -0
- package/dist/export/svg-export.d.ts +24 -0
- package/dist/export/svg-export.d.ts.map +1 -0
- package/dist/fsm/fsm.d.ts +45 -0
- package/dist/fsm/fsm.d.ts.map +1 -0
- package/dist/fsm/tween.d.ts +39 -0
- package/dist/fsm/tween.d.ts.map +1 -0
- package/dist/import/load-svg-import.d.ts +22 -0
- package/dist/import/load-svg-import.d.ts.map +1 -0
- package/dist/import/svg-source-registry.d.ts +4 -0
- package/dist/import/svg-source-registry.d.ts.map +1 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4549 -0
- package/dist/interaction/gesture-arbiter.d.ts +20 -0
- package/dist/interaction/gesture-arbiter.d.ts.map +1 -0
- package/dist/interaction/hit-area.d.ts +44 -0
- package/dist/interaction/hit-area.d.ts.map +1 -0
- package/dist/interaction/pan-zoom-gestures.d.ts +43 -0
- package/dist/interaction/pan-zoom-gestures.d.ts.map +1 -0
- package/dist/interaction/pointer-pipeline.d.ts +38 -0
- package/dist/interaction/pointer-pipeline.d.ts.map +1 -0
- package/dist/license/console-notice.d.ts +15 -0
- package/dist/license/console-notice.d.ts.map +1 -0
- package/dist/license/format.d.ts +29 -0
- package/dist/license/format.d.ts.map +1 -0
- package/dist/license/state.d.ts +43 -0
- package/dist/license/state.d.ts.map +1 -0
- package/dist/license/vendor-keys.d.ts +12 -0
- package/dist/license/vendor-keys.d.ts.map +1 -0
- package/dist/license/verify-fallback.d.ts +31 -0
- package/dist/license/verify-fallback.d.ts.map +1 -0
- package/dist/license/verify-webcrypto.d.ts +4 -0
- package/dist/license/verify-webcrypto.d.ts.map +1 -0
- package/dist/license/watermark.d.ts +20 -0
- package/dist/license/watermark.d.ts.map +1 -0
- package/dist/media/registry.d.ts +97 -0
- package/dist/media/registry.d.ts.map +1 -0
- package/dist/registry/connection-registry.d.ts +5 -0
- package/dist/registry/connection-registry.d.ts.map +1 -0
- package/dist/registry/element-catalog.d.ts +805 -0
- package/dist/registry/element-catalog.d.ts.map +1 -0
- package/dist/registry/primitive-registry.d.ts +7 -0
- package/dist/registry/primitive-registry.d.ts.map +1 -0
- package/dist/registry/theme-registry.d.ts +6 -0
- package/dist/registry/theme-registry.d.ts.map +1 -0
- package/dist/registry.d.ts +8 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/scada-view.d.ts +322 -0
- package/dist/scada-view.d.ts.map +1 -0
- package/dist/scene/connection-geometry.d.ts +95 -0
- package/dist/scene/connection-geometry.d.ts.map +1 -0
- package/dist/scene/constants.d.ts +10 -0
- package/dist/scene/constants.d.ts.map +1 -0
- package/dist/scene/crossing-jumps.d.ts +85 -0
- package/dist/scene/crossing-jumps.d.ts.map +1 -0
- package/dist/scene/dev-validate.d.ts +24 -0
- package/dist/scene/dev-validate.d.ts.map +1 -0
- package/dist/scene/errors.d.ts +15 -0
- package/dist/scene/errors.d.ts.map +1 -0
- package/dist/scene/groups.d.ts +90 -0
- package/dist/scene/groups.d.ts.map +1 -0
- package/dist/scene/lane-offset.d.ts +69 -0
- package/dist/scene/lane-offset.d.ts.map +1 -0
- package/dist/scene/orthogonal-router.d.ts +63 -0
- package/dist/scene/orthogonal-router.d.ts.map +1 -0
- package/dist/scene/scene-schema.d.ts +881 -0
- package/dist/scene/scene-schema.d.ts.map +1 -0
- package/dist/scene/structural-validator.d.ts +25 -0
- package/dist/scene/structural-validator.d.ts.map +1 -0
- package/dist/scene/validate-scene.d.ts +26 -0
- package/dist/scene/validate-scene.d.ts.map +1 -0
- package/dist/scene-authoring.d.ts +12 -0
- package/dist/scene-authoring.d.ts.map +1 -0
- package/dist/scene-authoring.js +952 -0
- package/dist/scheduler/alarm-band.d.ts +33 -0
- package/dist/scheduler/alarm-band.d.ts.map +1 -0
- package/dist/scheduler/animation-registry.d.ts +38 -0
- package/dist/scheduler/animation-registry.d.ts.map +1 -0
- package/dist/scheduler/lod.d.ts +25 -0
- package/dist/scheduler/lod.d.ts.map +1 -0
- package/dist/scheduler/quality-overlay.d.ts +19 -0
- package/dist/scheduler/quality-overlay.d.ts.map +1 -0
- package/dist/scheduler/quality.d.ts +15 -0
- package/dist/scheduler/quality.d.ts.map +1 -0
- package/dist/scheduler/scheduler.d.ts +243 -0
- package/dist/scheduler/scheduler.d.ts.map +1 -0
- package/dist/scheduler/visual-state-resolver.d.ts +31 -0
- package/dist/scheduler/visual-state-resolver.d.ts.map +1 -0
- package/dist/store/value-store.d.ts +65 -0
- package/dist/store/value-store.d.ts.map +1 -0
- package/dist/tokens.d.ts +703 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/topology/graph.d.ts +33 -0
- package/dist/topology/graph.d.ts.map +1 -0
- package/dist/topology/group-collapse.d.ts +15 -0
- package/dist/topology/group-collapse.d.ts.map +1 -0
- package/dist/topology/inference.d.ts +71 -0
- package/dist/topology/inference.d.ts.map +1 -0
- package/dist/topology/traversal.d.ts +29 -0
- package/dist/topology/traversal.d.ts.map +1 -0
- package/dist/topology/upstream-highlight.d.ts +75 -0
- package/dist/topology/upstream-highlight.d.ts.map +1 -0
- package/dist/units/formatter.d.ts +22 -0
- package/dist/units/formatter.d.ts.map +1 -0
- package/dist/units/registry.d.ts +21 -0
- package/dist/units/registry.d.ts.map +1 -0
- package/dist/view/group-view-state.d.ts +77 -0
- package/dist/view/group-view-state.d.ts.map +1 -0
- package/dist/view/viewport-state.d.ts +112 -0
- package/dist/view/viewport-state.d.ts.map +1 -0
- package/dist/vscada.umd.cjs +45 -0
- package/package.json +37 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Threshold } from '../scene/scene-schema';
|
|
2
|
+
import type { VesselAlarmTokens } from '../tokens';
|
|
3
|
+
/**
|
|
4
|
+
* Story 4.4 Task 3 — originally a core-owned, tag-agnostic MIRROR of what
|
|
5
|
+
* was then `@vscada/primitives`'s `vessels/alarm.ts` (Story 3.3), kept
|
|
6
|
+
* duplicated rather than shared because primitives depends on core, never
|
|
7
|
+
* the reverse, so primitives' copy couldn't be imported here.
|
|
8
|
+
*
|
|
9
|
+
* AD-12 fix (architecture-code-audit-2026-08-27) — collapsed the resulting
|
|
10
|
+
* 3-way duplication (this file, `vessels/alarm.ts`, `instruments/zone.ts`,
|
|
11
|
+
* all byte-identical `resolveAlarmBand` bodies) the other direction
|
|
12
|
+
* instead: THIS is now the single canonical implementation, exported
|
|
13
|
+
* publicly (mirroring the existing `resolveMediumStyle` precedent for a
|
|
14
|
+
* shared pure core utility primitives are meant to consume), and both
|
|
15
|
+
* primitive-side copies were deleted in favor of importing from here. The
|
|
16
|
+
* live-region controller (`scada-view.ts`) still uses it directly for ANY
|
|
17
|
+
* element with a numeric binding + declared `thresholds`, not just
|
|
18
|
+
* vessels/gauges.
|
|
19
|
+
*/
|
|
20
|
+
export type AlarmBand = 'normal' | 'low' | 'low-low' | 'high' | 'high-high';
|
|
21
|
+
export declare function resolveAlarmBand(value: number, thresholds: readonly Threshold[]): AlarmBand;
|
|
22
|
+
/**
|
|
23
|
+
* Band -> token color, with graceful degradation (AD-7: no alarm/zone
|
|
24
|
+
* tokens registered -> `fallback`, e.g. a plain surface/needle color,
|
|
25
|
+
* never a literal-color stand-in). `VesselAlarmTokens`'s shape (named
|
|
26
|
+
* fields, not a `Record`, so a theme can't accidentally omit a band) is
|
|
27
|
+
* reused as-is by every consumer of this function — `GaugeZoneTokens`
|
|
28
|
+
* (instruments) is a true type ALIAS of this type (`tokens.ts`), not just
|
|
29
|
+
* a structural coincidence, so no adapter is needed and the compiler
|
|
30
|
+
* enforces the two staying compatible.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveAlarmColor(tokens: VesselAlarmTokens | undefined, band: AlarmBand, fallback: string | undefined): string | undefined;
|
|
33
|
+
//# sourceMappingURL=alarm-band.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alarm-band.d.ts","sourceRoot":"","sources":["../../src/scheduler/alarm-band.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,GAAG,MAAM,GAAG,WAAW,CAAC;AAE5E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,SAAS,EAAE,GAAG,SAAS,CAW3F;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAc1I"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AnimationHandle } from '../contracts/handles';
|
|
2
|
+
/**
|
|
3
|
+
* Story 4.3 Task 2 — LOD's "concurrent animated elements are capped"
|
|
4
|
+
* priority order: state-lamps survive longest (safety indication),
|
|
5
|
+
* waves are cut first. `Scheduler` (the sanctioned home for ALL freeze
|
|
6
|
+
* POLICY, AD-5) reads `registeredEntries()`'s priority tag to decide what
|
|
7
|
+
* to cap; this registry only tracks the tag, never acts on it itself.
|
|
8
|
+
*/
|
|
9
|
+
export type AnimationPriority = 'state-lamp' | 'flow' | 'rotation' | 'wave';
|
|
10
|
+
/** Lower rank survives longer under a LOD cap. */
|
|
11
|
+
export declare const ANIMATION_PRIORITY_RANK: Record<AnimationPriority, number>;
|
|
12
|
+
export declare class AnimationRegistry {
|
|
13
|
+
private entries;
|
|
14
|
+
/** `priority` defaults to `'rotation'` — the middle tier — for any registration site that doesn't (yet) tag itself explicitly. */
|
|
15
|
+
register(elementId: string, handle: AnimationHandle, priority?: AnimationPriority): void;
|
|
16
|
+
/** Read-only view for `Scheduler`'s LOD cap policy — one row per registered animation, in registration order. */
|
|
17
|
+
registeredEntries(): {
|
|
18
|
+
elementId: string;
|
|
19
|
+
priority: AnimationPriority;
|
|
20
|
+
}[];
|
|
21
|
+
unregisterElement(elementId: string): void;
|
|
22
|
+
pauseAll(elementId: string): void;
|
|
23
|
+
resumeAll(elementId: string): void;
|
|
24
|
+
pauseScene(): void;
|
|
25
|
+
resumeScene(): void;
|
|
26
|
+
isPaused(elementId: string): boolean[];
|
|
27
|
+
/**
|
|
28
|
+
* Story 1.7: the sanctioned deterministic-frame mechanism for visual
|
|
29
|
+
* regression — seeks EVERY registered animation's WAAPI `currentTime`,
|
|
30
|
+
* paused or not. Works only because every continuous animation is
|
|
31
|
+
* registered (AD-5); an animation that doesn't move when sought is
|
|
32
|
+
* exactly as designed, and one that keeps moving after a seek is a real
|
|
33
|
+
* compliance bug (something bypassed the registry) — that's this
|
|
34
|
+
* method's whole reason for existing.
|
|
35
|
+
*/
|
|
36
|
+
seekAll(timeMs: number): void;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=animation-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"animation-registry.d.ts","sourceRoot":"","sources":["../../src/scheduler/animation-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;AAE5E,kDAAkD;AAClD,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,iBAAiB,EAAE,MAAM,CAKrE,CAAC;AAgBF,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,OAAO,CAAe;IAE9B,kIAAkI;IAClI,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,QAAQ,GAAE,iBAA8B,GAAG,IAAI;IAIpG,iHAAiH;IACjH,iBAAiB,IAAI;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,EAAE;IAIzE,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI1C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IASjC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IASlC,UAAU,IAAI,IAAI;IASlB,WAAW,IAAI,IAAI;IASnB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,EAAE;IAItC;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAK9B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 4.3 Task 2 — LOD (level-of-detail) zoom threshold, with hysteresis
|
|
3
|
+
* so a zoom hovering right at the boundary doesn't flap the simplified
|
|
4
|
+
* view on and off every frame. Below the threshold the scene is "a
|
|
5
|
+
* 40-tank overview" (§9.4): liquids simplify to static fills, concurrent
|
|
6
|
+
* animated elements are capped, and text labels below legibility hide —
|
|
7
|
+
* see `AnimationRegistry`'s priority tiers and `ScadaView`'s
|
|
8
|
+
* `vscada-lod-active` class toggle for the rest of the mechanism this
|
|
9
|
+
* threshold check drives.
|
|
10
|
+
*/
|
|
11
|
+
export declare const DEFAULT_LOD_ZOOM_THRESHOLD = 0.5;
|
|
12
|
+
/** Fraction of the threshold the zoom must cross BEYOND the boundary to flip state (~10%, Dev Notes). */
|
|
13
|
+
export declare const DEFAULT_LOD_HYSTERESIS_FRACTION = 0.1;
|
|
14
|
+
/** Provisional capacity-gate assumption (architecture-time, spine conventions) — the max concurrently-active registered animations once LOD is active. */
|
|
15
|
+
export declare const DEFAULT_LOD_MAX_ACTIVE_ANIMATIONS = 50;
|
|
16
|
+
/**
|
|
17
|
+
* Per-frame-cheap threshold check (a couple of comparisons, no allocation)
|
|
18
|
+
* — safe to call unconditionally every scheduler tick. `wasActive` is the
|
|
19
|
+
* PREVIOUS call's result: crossing INTO the simplified view requires zoom
|
|
20
|
+
* to fall below `threshold - band`; crossing OUT requires it to rise above
|
|
21
|
+
* `threshold + band`. A zoom sitting anywhere inside the band keeps
|
|
22
|
+
* whichever state it already had.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveLodActive(zoom: number, wasActive: boolean, threshold?: number, hysteresisFraction?: number): boolean;
|
|
25
|
+
//# sourceMappingURL=lod.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lod.d.ts","sourceRoot":"","sources":["../../src/scheduler/lod.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,yGAAyG;AACzG,eAAO,MAAM,+BAA+B,MAAM,CAAC;AACnD,0JAA0J;AAC1J,eAAO,MAAM,iCAAiC,KAAK,CAAC;AAEpD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,SAAS,GAAE,MAAmC,EAC9C,kBAAkB,GAAE,MAAwC,GAC3D,OAAO,CAIT"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Renderer } from '../contracts/renderer';
|
|
2
|
+
import type { ThemeContext } from '../contracts/primitive';
|
|
3
|
+
import type { Condition } from './quality';
|
|
4
|
+
/**
|
|
5
|
+
* The quality/staleness overlay (§4.3) — core-owned, cross-primitive
|
|
6
|
+
* (Project Structure Notes: "overlay visual helpers in core, not
|
|
7
|
+
* per-primitive"). Structure is built ONCE per element (AD-2); only
|
|
8
|
+
* visibility toggling happens per tick, through the renderer seam like any
|
|
9
|
+
* other scheduler-driven write. `<pattern>` for the bad-quality hatch is
|
|
10
|
+
* allowed (C7 bans filters, not patterns); desaturation is a grey overlay
|
|
11
|
+
* rect, never `filter: grayscale()`.
|
|
12
|
+
*/
|
|
13
|
+
export interface QualityOverlay {
|
|
14
|
+
group: SVGGElement;
|
|
15
|
+
setCondition(renderer: Renderer, condition: Condition): void;
|
|
16
|
+
setAgeBadge(renderer: Renderer, text: string | null): void;
|
|
17
|
+
}
|
|
18
|
+
export declare function buildQualityOverlay(w: number, h: number, theme: ThemeContext): QualityOverlay;
|
|
19
|
+
//# sourceMappingURL=quality-overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality-overlay.d.ts","sourceRoot":"","sources":["../../src/scheduler/quality-overlay.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAK3C;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,CAAC;IACnB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7D,WAAW,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CAC5D;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,cAAc,CAyF7F"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { BufferEntry } from '../store/value-store';
|
|
2
|
+
export type Condition = 'good' | 'uncertain' | 'stale' | 'bad';
|
|
3
|
+
export declare function worseCondition(a: Condition, b: Condition): Condition;
|
|
4
|
+
/**
|
|
5
|
+
* Per-tag condition (§4.3). `bad` quality wins outright — a value can be
|
|
6
|
+
* simultaneously old AND explicitly bad-quality, and bad quality is the
|
|
7
|
+
* worse of the two per the stated precedence. Age is computed against the
|
|
8
|
+
* declared `timestamp` when present, falling back to `arrivalTime` (§4.2).
|
|
9
|
+
*/
|
|
10
|
+
export declare function tagCondition(entry: Readonly<BufferEntry>, now: number, stalenessTimeoutMs: number): Condition;
|
|
11
|
+
/** Worst-of aggregation across every tag an element is bound to (design decision — one overlay tier per element). */
|
|
12
|
+
export declare function worstOf(conditions: Condition[]): Condition;
|
|
13
|
+
/** `47s` under a minute, `3m` at/above — deliberately coarse (§4.3's badge is a glance-level cue, not a stopwatch). */
|
|
14
|
+
export declare function formatAge(ms: number): string;
|
|
15
|
+
//# sourceMappingURL=quality.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quality.d.ts","sourceRoot":"","sources":["../../src/scheduler/quality.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,OAAO,GAAG,KAAK,CAAC;AAK/D,wBAAgB,cAAc,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,SAAS,CAEpE;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAM7G;AAED,qHAAqH;AACrH,wBAAgB,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,SAAS,CAE1D;AAED,uHAAuH;AACvH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAI5C"}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import type { Handle } from '../contracts/handles';
|
|
2
|
+
import type { Renderer } from '../contracts/renderer';
|
|
3
|
+
import type { TimeSource } from '../contracts/time-source';
|
|
4
|
+
import type { ConnectionState } from '../contracts/connection-state';
|
|
5
|
+
import type { ValueStore } from '../store/value-store';
|
|
6
|
+
import { type AnimationRegistry } from './animation-registry';
|
|
7
|
+
import type { QualityOverlay } from './quality-overlay';
|
|
8
|
+
import { type Condition } from './quality';
|
|
9
|
+
import type { FlowInferenceCache } from '../topology/inference';
|
|
10
|
+
/**
|
|
11
|
+
* Default numeric jump beyond which a value snaps instead of tweening
|
|
12
|
+
* (§9.2 "cap it"). Chosen so a typical 0–100% level/position range still
|
|
13
|
+
* tweens smoothly for normal process changes but a full-range jump (tank
|
|
14
|
+
* emptied a valve open→closed, a mode switch) snaps instead of visibly
|
|
15
|
+
* animating over the tween window. Configurable per component instance.
|
|
16
|
+
*/
|
|
17
|
+
export declare const DEFAULT_SNAP_THRESHOLD = 50;
|
|
18
|
+
/** §3.2 `mode` — see `Scheduler.mode`'s own doc for the static/live transition contract (Story 4.1). */
|
|
19
|
+
export type SchedulerMode = 'live' | 'static';
|
|
20
|
+
/** §3.2 default `stalenessTimeout`. */
|
|
21
|
+
export declare const DEFAULT_STALENESS_TIMEOUT_MS = 10000;
|
|
22
|
+
/** Story 4.3 Task 3 — how often staleness/flow evaluation keeps running (via `setInterval`, independent of rAF) while the scene is paused for off-screen/hidden-tab (rAF itself may be throttled to near-zero by the browser in a hidden tab — this is the explicit fallback). */
|
|
23
|
+
export declare const OFFSCREEN_QUALITY_POLL_MS = 1000;
|
|
24
|
+
/** §3.4 `render-stats` detail shape — emitted at 1Hz only while `debug` is enabled (Task 4). */
|
|
25
|
+
export interface RenderStats {
|
|
26
|
+
fps: number;
|
|
27
|
+
elementCount: number;
|
|
28
|
+
droppedFrames: number;
|
|
29
|
+
}
|
|
30
|
+
export interface SchedulerOptions {
|
|
31
|
+
/** Numeric jump beyond which a value snaps instead of tweening. */
|
|
32
|
+
snapThreshold?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The single coalescing rAF loop (§9.1, AD-1) — and, as of Story 1.6, the
|
|
36
|
+
* ONLY place staleness/quality is evaluated (AD-5: "this logic lives HERE
|
|
37
|
+
* and nowhere else"). One instance per `<scada-view>` — multi-instance
|
|
38
|
+
* sharing is explicitly Deferred (spine). Values land in the store
|
|
39
|
+
* synchronously via `ValueStore.setValues()`; DOM writes happen ONLY here,
|
|
40
|
+
* on ticks. Coalescing multiple pushes into one write per handle per frame
|
|
41
|
+
* falls out of the architecture by construction — no extra dedup
|
|
42
|
+
* bookkeeping is needed for that guarantee.
|
|
43
|
+
*/
|
|
44
|
+
export declare class Scheduler {
|
|
45
|
+
private timeSource;
|
|
46
|
+
private store;
|
|
47
|
+
private renderer;
|
|
48
|
+
private animationRegistry;
|
|
49
|
+
private registrations;
|
|
50
|
+
private tickables;
|
|
51
|
+
private overlays;
|
|
52
|
+
private elementQuality;
|
|
53
|
+
private stopTick;
|
|
54
|
+
private wasSceneFrozen;
|
|
55
|
+
/** Story 2.4 Task 2/3: topology-inference read seam — set via `registerFlowInference`. */
|
|
56
|
+
private flowInference;
|
|
57
|
+
/** Per-id (element or connection) "currently frozen for a flow reason" — independent bookkeeping from `elementQuality`'s own transition tracking, so the two tiers never fight over the SAME `pauseAll`/`resumeAll` call (AD-5, AD-12). */
|
|
58
|
+
private flowFrozen;
|
|
59
|
+
/** Per-id "currently showing the reused isolated/grey overlay" — only touched when quality is 'good' (AD-12: quality tier owns the overlay slot otherwise). */
|
|
60
|
+
private flowIsolatedOverlay;
|
|
61
|
+
/** §3.2 — Story 1.3 Task 4's "configurable component-level option" (mutable, like `stalenessTimeout`, not fixed at construction). */
|
|
62
|
+
snapThreshold: number;
|
|
63
|
+
/** Story 2.6 AC2: last `store.tagPrefixVersion` seen — a change is polled once per tick (never per-registration) and triggers `_resetInterpolationState`. */
|
|
64
|
+
private lastSeenTagPrefixVersion;
|
|
65
|
+
/** Story 4.3 Task 2 — per-id "currently paused for the LOD cap", independent bookkeeping from `flowFrozen`/quality's own tier (AD-5/AD-12: freeze tiers never fight over the same `pauseAll`/`resumeAll` call). */
|
|
66
|
+
private lodFrozen;
|
|
67
|
+
private lodActiveValue;
|
|
68
|
+
/** Cap on concurrently-active registered animations once LOD is active — a token (Dev Notes: "cap + priorities as tokens"), overridable per instance. */
|
|
69
|
+
lodMaxActiveAnimations: number;
|
|
70
|
+
/** Story 4.3 Task 3 — true while paused for off-screen/hidden-tab (`pauseForOffscreen`). */
|
|
71
|
+
private visibilityPaused;
|
|
72
|
+
private offscreenTimerId;
|
|
73
|
+
/** Story 4.3 Task 4 — render-stats accounting; all zero-cost when `debug` is off (never even read). */
|
|
74
|
+
private debugEnabled;
|
|
75
|
+
private statsFrameCount;
|
|
76
|
+
private statsDroppedFrames;
|
|
77
|
+
private statsWindowStart;
|
|
78
|
+
private lastTickTime;
|
|
79
|
+
private statsCallback;
|
|
80
|
+
/** Story 4.4 Task 3 — the ARIA live-region's quality/staleness seam; unconditional (unlike `statsCallback`, never gated behind `debug`). */
|
|
81
|
+
private conditionCallback;
|
|
82
|
+
/** §3.2 property — configuring the TIMEOUT is allowed; disabling the freeze BEHAVIOR is not (NFR4). */
|
|
83
|
+
stalenessTimeout: number;
|
|
84
|
+
/** §3.2 property. `'degraded'` is intentionally a documented no-op (see Dev Notes) — not yet given a visual treatment. */
|
|
85
|
+
connectionState: ConnectionState;
|
|
86
|
+
/** Story 4.1 §8.3 — "static mode is live mode with tween duration zero and timers off. One code path." Gates `start()` (no rAF subscription while static, AC1/FR16) and drives the snap-then-stop / restart transition below (AC2). */
|
|
87
|
+
private _mode;
|
|
88
|
+
/** Story 4.1 §3.2 `animate` — master switch for continuous (registry-driven) animation, ORTHOGONAL to `mode`: a live dashboard can pause animation, and a static export can keep pumps spinning (§8.1 default). */
|
|
89
|
+
private _animateEnabled;
|
|
90
|
+
constructor(timeSource: TimeSource, store: ValueStore, renderer: Renderer, animationRegistry: AnimationRegistry, options?: SchedulerOptions);
|
|
91
|
+
/**
|
|
92
|
+
* Animation-kind handles are not registered here — they go through the
|
|
93
|
+
* AnimationRegistry. Tween-kind handles go into a separate unconditional
|
|
94
|
+
* per-tick list (`tag` is unused for them — see `TweenHandle`'s doc).
|
|
95
|
+
*/
|
|
96
|
+
registerHandle(elementId: string, tag: string, handle: Handle): void;
|
|
97
|
+
/** Story 1.6: registers the pre-built quality overlay this element's structure carries (core-owned, cross-primitive). */
|
|
98
|
+
registerQualityOverlay(elementId: string, overlay: QualityOverlay): void;
|
|
99
|
+
/**
|
|
100
|
+
* Story 2.4 Task 2/3: wires a `FlowInferenceCache` (the topology module's
|
|
101
|
+
* OWN cache — recomputed only on source/valve FSM state-change events,
|
|
102
|
+
* never here) into the tick's read-only resolver pass. `ids` are every
|
|
103
|
+
* element/connection id this scheduler should apply inferred flow state
|
|
104
|
+
* to (node ids for equipment, edge/connection ids for pipes).
|
|
105
|
+
*/
|
|
106
|
+
registerFlowInference(cache: FlowInferenceCache, ids: Iterable<string>): void;
|
|
107
|
+
unregisterElement(elementId: string): void;
|
|
108
|
+
get registrationCount(): number;
|
|
109
|
+
/**
|
|
110
|
+
* §3.2 `mode` (Story 4.1). live→static: snaps every in-flight tween/
|
|
111
|
+
* interpolation to its target (reusing AC4's `snapToTargets()` verbatim —
|
|
112
|
+
* "one code path", §8.3) then `stop()`s the tick + offscreen-poll timer
|
|
113
|
+
* entirely (AC1: no rAF loop, no staleness timers while static — `tick()`
|
|
114
|
+
* itself is never reached, so it needs no static-aware branch of its
|
|
115
|
+
* own). static→live: `start()` resumes normal ticking; staleness simply
|
|
116
|
+
* starts being evaluated again on the next tick, computed from each tag's
|
|
117
|
+
* stored `timestamp`/`arrivalTime` exactly as if the gap had been live the
|
|
118
|
+
* whole time — data that aged past `stalenessTimeout` while static
|
|
119
|
+
* correctly (and intentionally) freezes on that very first live tick.
|
|
120
|
+
*/
|
|
121
|
+
get mode(): SchedulerMode;
|
|
122
|
+
set mode(value: SchedulerMode);
|
|
123
|
+
/**
|
|
124
|
+
* §3.2 `animate` (Story 4.1 AC1) — orthogonal to `mode`: pauses/resumes
|
|
125
|
+
* every registered continuous animation via the SAME `AnimationRegistry`
|
|
126
|
+
* pause/resume surface every other freeze tier uses. Resuming reuses the
|
|
127
|
+
* cross-tier-aware discipline `resumeFromOffscreen`/`applyLod` already
|
|
128
|
+
* establish: an id independently frozen by quality/flow/LOD/offscreen
|
|
129
|
+
* stays frozen — this switch only ever releases ITS OWN hold.
|
|
130
|
+
*/
|
|
131
|
+
get animate(): boolean;
|
|
132
|
+
set animate(value: boolean);
|
|
133
|
+
/** Story 4.1 AC1/FR16 — no-op while `mode === 'static'`: no rAF loop is ever scheduled (`TimeSource.onTick` is never called). */
|
|
134
|
+
start(): void;
|
|
135
|
+
stop(): void;
|
|
136
|
+
get isRunning(): boolean;
|
|
137
|
+
/** Story 4.3 Task 2 — the LOD zoom-threshold module's own output; a per-frame-cheap threshold check elsewhere feeds this, `applyLod()` (called every tick, cheap no-op when inactive) enforces the resulting cap. */
|
|
138
|
+
get lodActive(): boolean;
|
|
139
|
+
set lodActive(value: boolean);
|
|
140
|
+
/** §3.2 `debug` — gates render-stats accounting entirely (Task 4: "zero overhead when debug off"). */
|
|
141
|
+
get debug(): boolean;
|
|
142
|
+
set debug(value: boolean);
|
|
143
|
+
/** Registers the 1Hz `render-stats` consumer (`ScadaView` dispatches the CustomEvent from here). Returns an unsubscribe function. */
|
|
144
|
+
onRenderStats(cb: (stats: RenderStats) => void): () => void;
|
|
145
|
+
/**
|
|
146
|
+
* Story 4.4 Task 3 — fires exactly once per real per-element condition
|
|
147
|
+
* change (`evaluateQuality`'s own transition detection, the SAME edge
|
|
148
|
+
* `handleConditionTransition`'s pause/resume already reacts to) — never
|
|
149
|
+
* on every tick, never for an unchanged condition. The ARIA live-region
|
|
150
|
+
* controller is the one consumer; disambiguating "stale because
|
|
151
|
+
* disconnected" from "stale because this one tag timed out" is the
|
|
152
|
+
* CALLER's job (read `connectionState` at callback time), since by this
|
|
153
|
+
* point both causes have already collapsed into the same `Condition`
|
|
154
|
+
* value (§4.3's `worstOf`). Single-callback, same convention as
|
|
155
|
+
* `onRenderStats` — not a multi-listener event bus.
|
|
156
|
+
*/
|
|
157
|
+
onConditionTransition(cb: (elementId: string, previous: Condition, next: Condition) => void): () => void;
|
|
158
|
+
get isPausedForOffscreen(): boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Story 4.3 Task 3 — off-screen (`IntersectionObserver`) / hidden-tab
|
|
161
|
+
* (`visibilitychange`) pause: stops the main rAF-driven tick (DOM writes
|
|
162
|
+
* + animation playback) entirely, but keeps staleness/flow evaluation
|
|
163
|
+
* running on a `setInterval` fallback (`OFFSCREEN_QUALITY_POLL_MS`) —
|
|
164
|
+
* §9.4's "control room dashboard with six hidden tabs will melt a
|
|
165
|
+
* workstation" is about ANIMATION cost, not about the freeze rule
|
|
166
|
+
* silently going stale itself. `AnimationRegistry.pauseScene()` is the
|
|
167
|
+
* SAME blunt entering-edge call `tick()`'s own `sceneFrozen` transition
|
|
168
|
+
* already uses; resuming is deliberately selective (see
|
|
169
|
+
* `resumeFromOffscreen`), never a blind `resumeScene()`.
|
|
170
|
+
*/
|
|
171
|
+
pauseForOffscreen(): void;
|
|
172
|
+
/** Reverses `pauseForOffscreen()` — resumes only animations not ALSO independently frozen by quality/flow/LOD (the same cross-tier-aware pattern `applyFlowInference`/`applyLod` already use), then restarts the normal tick. */
|
|
173
|
+
resumeFromOffscreen(): void;
|
|
174
|
+
/** The fallback poll while paused-for-offscreen — staleness/flow only, no DOM writes, no tween/animation playback. */
|
|
175
|
+
private offscreenTick;
|
|
176
|
+
/** AC4: every in-flight tween AND data interpolation completes to its target immediately (Story 4.1's static-mode switch depends on this). */
|
|
177
|
+
snapToTargets(): void;
|
|
178
|
+
private tick;
|
|
179
|
+
/**
|
|
180
|
+
* Story 4.3 Task 2 — enforces the LOD-active animation cap by priority
|
|
181
|
+
* (`AnimationPriority`'s rank: state-lamps survive longest, waves are
|
|
182
|
+
* cut first). Cheap no-op when LOD has never engaged (`!lodActiveValue
|
|
183
|
+
* && lodFrozen.size===0`), so calling it unconditionally every tick is
|
|
184
|
+
* the "per-frame-cheap threshold check" Dev Notes calls for without
|
|
185
|
+
* real cost in the common (zoomed-in) case. Reuses the SAME
|
|
186
|
+
* cross-tier-aware discipline `applyFlowInference` established:
|
|
187
|
+
* pausing is always safe/idempotent; resuming checks that no OTHER
|
|
188
|
+
* freeze tier (quality/flow) still wants the element frozen first.
|
|
189
|
+
*/
|
|
190
|
+
private applyLod;
|
|
191
|
+
/** Story 4.3 Task 4 — cheap per-tick counters, flushed to `render-stats` at 1Hz. Only ever called while `debugEnabled` (zero overhead off). */
|
|
192
|
+
private accountFrame;
|
|
193
|
+
private countDistinctElements;
|
|
194
|
+
/**
|
|
195
|
+
* Story 2.6 AC2 — "changing tagPrefix at runtime re-resolves and clears
|
|
196
|
+
* interpolation state for affected tags (equivalent to a data reset)."
|
|
197
|
+
* `ValueStore.resolve()` is already always fresh (no caching), so the
|
|
198
|
+
* ONLY thing that can go stale across a prefix change is THIS scheduler's
|
|
199
|
+
* own per-registration bookkeeping (`lastWritten`/`lastArrivalTimeSeen`):
|
|
200
|
+
* left alone, the next tick would tween from the OLD tag's last value
|
|
201
|
+
* toward the NEW tag's value as if they were the same continuously-
|
|
202
|
+
* evolving quantity — a meaningless cross-tag blend. Polled once per
|
|
203
|
+
* tick (not per-registration lookup) via the store's own version
|
|
204
|
+
* counter — a pull, not a push, keeping the store ignorant of the
|
|
205
|
+
* scheduler (AD-1's unidirectional pipeline: store never reaches
|
|
206
|
+
* upstream into the scheduler).
|
|
207
|
+
*/
|
|
208
|
+
private syncTagPrefixIfChanged;
|
|
209
|
+
private isElementFrozen;
|
|
210
|
+
/**
|
|
211
|
+
* AD-5: the ONLY place staleness/quality is evaluated — the top
|
|
212
|
+
* precedence tier of the (future, 2.4) visual-state resolver. Covers
|
|
213
|
+
* every element OR connection with tag-bound handles, whether or not it
|
|
214
|
+
* has a visual overlay: a pipe's flow-dash animation must freeze on
|
|
215
|
+
* stale data exactly like a pump's impeller, even though a multi-segment
|
|
216
|
+
* pipe path has no well-defined bounding box for a dashed-border/hatch
|
|
217
|
+
* overlay (Story 1.4/1.6 don't give connections one).
|
|
218
|
+
*/
|
|
219
|
+
private evaluateQuality;
|
|
220
|
+
/**
|
|
221
|
+
* Story 2.4 Task 1/2/3 — the explicit⟩inferred tiers of the AD-12
|
|
222
|
+
* resolver, slotted beneath 1.6's quality tier (`evaluateQuality`, run
|
|
223
|
+
* just before this on the SAME tick). Per id: only an O(1) cache read
|
|
224
|
+
* (`FlowInferenceCache.getState`) — the expensive graph pass already
|
|
225
|
+
* happened, event-driven, elsewhere (AD-4). No `explicit` producer exists
|
|
226
|
+
* in the product yet (no primitive emits a tag-bound flow state), so it
|
|
227
|
+
* is always `undefined` here — the seam is real and tested via
|
|
228
|
+
* `resolveVisualState` directly, ready for a future story to feed it.
|
|
229
|
+
*
|
|
230
|
+
* Reuses the EXACT AD-5 pause/resume path (`animationRegistry.pauseAll` /
|
|
231
|
+
* `resumeAll`) `handleConditionTransition` above already uses for
|
|
232
|
+
* staleness — same idempotent per-entry `paused` flag, so this can run
|
|
233
|
+
* independently every tick without fighting the quality tier's own
|
|
234
|
+
* pause/resume calls (verified idempotent by Story 1.6, see its Debug Log).
|
|
235
|
+
* Isolation reuses 1.6's EXACT quality overlay (`QualityOverlay.setCondition`,
|
|
236
|
+
* `'stale'` = the grey desaturate treatment) — never a second desaturation
|
|
237
|
+
* path — but only while quality itself is `'good'` (AD-12: the quality
|
|
238
|
+
* tier owns the overlay slot outright whenever it isn't).
|
|
239
|
+
*/
|
|
240
|
+
private applyFlowInference;
|
|
241
|
+
private handleConditionTransition;
|
|
242
|
+
}
|
|
243
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/scheduler/scheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Condition } from './quality';
|
|
2
|
+
/**
|
|
3
|
+
* The AD-12 resolver (Story 2.4, Task 1) — completes the seam Story 1.6
|
|
4
|
+
* deliberately left open ("explicit-vs-inferred tiers arrive in 2.4").
|
|
5
|
+
* Fixed precedence, non-negotiable: quality/staleness overlay ⟩ explicit
|
|
6
|
+
* tag-bound state ⟩ topology-inferred state ⟩ defaults. ONE function, pure,
|
|
7
|
+
* no store/clock access — every caller (scheduler tick, tests) goes through
|
|
8
|
+
* this instead of hand-rolling its own if/else precedence chain.
|
|
9
|
+
*
|
|
10
|
+
* Quality wins the tier whenever it isn't `'good'` — this mirrors the
|
|
11
|
+
* spine's literal wording ("quality/staleness overlay" is ONE tier sitting
|
|
12
|
+
* above explicit/inferred, not split further here). Callers that need to
|
|
13
|
+
* distinguish "frozen" (stale/bad) from "merely uncertain" for animation
|
|
14
|
+
* pause/resume purposes do that with their OWN narrower check on the
|
|
15
|
+
* returned quality value (see `scheduler.ts`'s `isFlowFreezingCondition`) —
|
|
16
|
+
* this resolver only decides which TIER wins, not what each tier does.
|
|
17
|
+
*/
|
|
18
|
+
export type ResolverTier = 'quality' | 'explicit' | 'inferred' | 'default';
|
|
19
|
+
export interface ResolvedVisualState<S> {
|
|
20
|
+
tier: ResolverTier;
|
|
21
|
+
/** The winning value — a `Condition` when `tier === 'quality'`, otherwise an `S`. */
|
|
22
|
+
value: Condition | S;
|
|
23
|
+
}
|
|
24
|
+
export interface VisualStateResolverInput<S> {
|
|
25
|
+
quality: Condition;
|
|
26
|
+
explicit?: S;
|
|
27
|
+
inferred?: S;
|
|
28
|
+
defaultValue: S;
|
|
29
|
+
}
|
|
30
|
+
export declare function resolveVisualState<S>(input: VisualStateResolverInput<S>): ResolvedVisualState<S>;
|
|
31
|
+
//# sourceMappingURL=visual-state-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"visual-state-resolver.d.ts","sourceRoot":"","sources":["../../src/scheduler/visual-state-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAE3C;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAE3E,MAAM,WAAW,mBAAmB,CAAC,CAAC;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,qFAAqF;IACrF,KAAK,EAAE,SAAS,GAAG,CAAC,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB,CAAC,CAAC;IACzC,OAAO,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,CAAC;IACb,YAAY,EAAE,CAAC,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,wBAAwB,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAWhG"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { TagValue, TagValueQuality, ValueWarningDetail } from '../contracts/tag-value';
|
|
2
|
+
import type { TimeSource } from '../contracts/time-source';
|
|
3
|
+
/**
|
|
4
|
+
* The interpolation buffer (spec §9.3) — "the only unavoidable internal
|
|
5
|
+
* state," per AD-4. `quality`/`timestamp` beyond the spec's illustrative
|
|
6
|
+
* 3-field snippet are retained for two concrete, in-scope reasons, not
|
|
7
|
+
* speculative growth: (1) AD-13 idempotence needs to compare an incoming
|
|
8
|
+
* TagValue against what's already stored, and (2) Story 1.6's freeze rule
|
|
9
|
+
* will need per-tag quality without redesigning this store. Tween-progress
|
|
10
|
+
* bookkeeping (AD-4's separate "tween layer" state bucket) deliberately
|
|
11
|
+
* does NOT live here — that's the scheduler's job.
|
|
12
|
+
*/
|
|
13
|
+
export interface BufferEntry {
|
|
14
|
+
previousValue: TagValue['value'];
|
|
15
|
+
currentValue: TagValue['value'];
|
|
16
|
+
arrivalTime: number;
|
|
17
|
+
quality: TagValueQuality;
|
|
18
|
+
timestamp: number | undefined;
|
|
19
|
+
}
|
|
20
|
+
export declare class ValueStore {
|
|
21
|
+
private timeSource;
|
|
22
|
+
private onWarning?;
|
|
23
|
+
private buffer;
|
|
24
|
+
private _tagPrefix;
|
|
25
|
+
private _tagPrefixVersion;
|
|
26
|
+
constructor(timeSource: TimeSource, onWarning?: ((detail: ValueWarningDetail) => void) | undefined, tagPrefix?: string);
|
|
27
|
+
/**
|
|
28
|
+
* §3.2 — "one scene serves multiple identical units" (Story 2.6 AC2).
|
|
29
|
+
* The instance owns the mapping; the scene's bindings stay portable and
|
|
30
|
+
* unprefixed. Empty string (the default) is identity — every scene/test
|
|
31
|
+
* predating this story resolves byte-identical to before. Setting a NEW
|
|
32
|
+
* value bumps `tagPrefixVersion`, the seam the scheduler polls once per
|
|
33
|
+
* tick to detect a runtime change and reset its own per-registration
|
|
34
|
+
* interpolation bookkeeping (equivalent to a data reset for affected
|
|
35
|
+
* elements — see scheduler.ts).
|
|
36
|
+
*/
|
|
37
|
+
get tagPrefix(): string;
|
|
38
|
+
set tagPrefix(prefix: string);
|
|
39
|
+
/** Read-only change counter for `tagPrefix` — see the property doc above. */
|
|
40
|
+
get tagPrefixVersion(): number;
|
|
41
|
+
/** Partial update (§4.2): merges, never clears on absence. Empty array is a no-op. */
|
|
42
|
+
setValues(values: TagValue[]): void;
|
|
43
|
+
/** Resets the buffer — the ONLY way values are cleared (never an empty `setValues([])`). */
|
|
44
|
+
clearValues(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Raw, unprefixed buffer accessor — looks up exactly the tag name given,
|
|
47
|
+
* no mapping applied. This is what `ScadaView.values` enumerates through
|
|
48
|
+
* (it already has the real/full buffered tag from `tags()`); it must NOT
|
|
49
|
+
* also apply `tagPrefix`, or a non-empty prefix would double-prepend.
|
|
50
|
+
*/
|
|
51
|
+
get(tag: string): Readonly<BufferEntry> | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* THE single choke point for tag-lookup resolution (Story 2.6 AC2/Task 2):
|
|
54
|
+
* every binding/threshold/summaryTag/state-map tag a scene declares is
|
|
55
|
+
* portable and unprefixed; this is where `tagPrefix` gets applied, at
|
|
56
|
+
* lookup time, for ALL of them alike — none of those call sites need (or
|
|
57
|
+
* get) their own ad-hoc prefixing logic.
|
|
58
|
+
*/
|
|
59
|
+
resolve(tag: string): Readonly<BufferEntry> | undefined;
|
|
60
|
+
get size(): number;
|
|
61
|
+
tags(): IterableIterator<string>;
|
|
62
|
+
private ingestOne;
|
|
63
|
+
private checkTimestamp;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=value-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"value-store.d.ts","sourceRoot":"","sources":["../../src/store/value-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,eAAe,CAAC;IACzB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAID,qBAAa,UAAU;IAMnB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,SAAS,CAAC;IANpB,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAAK;gBAGpB,UAAU,EAAE,UAAU,EACtB,SAAS,CAAC,GAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,aAAA,EACxD,SAAS,SAAK;IAKhB;;;;;;;;;OASG;IACH,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,SAAS,CAAC,MAAM,EAAE,MAAM,EAI3B;IAED,6EAA6E;IAC7E,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED,sFAAsF;IACtF,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,IAAI;IAMnC,4FAA4F;IAC5F,WAAW,IAAI,IAAI;IAInB;;;;;OAKG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,SAAS;IAInD;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ,CAAC,WAAW,CAAC,GAAG,SAAS;IAIvD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,gBAAgB,CAAC,MAAM,CAAC;IAIhC,OAAO,CAAC,SAAS;IA+BjB,OAAO,CAAC,cAAc;CAoBvB"}
|