// Panel-local state variables (ds === 'local'). // // Definitions live in the interface XML and travel with the panel, but the live // value is instantiated per browser/panel instance starting from the variable's // initial value — nothing is persisted server-side and no WebSocket traffic is // involved. The panel logic (a future feature) and write-capable widgets can // update these values locally. // // This module deliberately depends only on the store primitives and types so it // can be imported by both stores.ts and ws.ts without creating an import cycle. import { writable, type Readable, type Writable } from './store'; import type { SignalValue, SignalMeta, StateVar } from './types'; import { parseInitialArray, applySizing, type ArrVal } from './arraypolicy'; const valueStores = new Map>(); const metaStores = new Map>(); const decls = new Map(); export function declaredVar(name: string): StateVar | undefined { return decls.get(name); } const DEFAULT_VALUE: SignalValue = { value: null, quality: 'unknown', ts: null }; function valueW(name: string): Writable { let s = valueStores.get(name); if (!s) { s = writable(DEFAULT_VALUE); valueStores.set(name, s); } return s; } function metaW(name: string): Writable { let s = metaStores.get(name); if (!s) { s = writable(null); metaStores.set(name, s); } return s; } // coerce parses a state variable's stored string into a live value. function coerce(v: StateVar): any { switch (v.type) { case 'bool': return v.initial === 'true' || v.initial === '1'; case 'string': return v.initial; case 'array': return parseInitialArray(v); default: { const n = parseFloat(v.initial); return isNaN(n) ? 0 : n; } } } // initLocalState (re)instantiates every state variable to its initial value and // publishes metadata. Call this whenever a panel is loaded. export function initLocalState(vars: StateVar[] | undefined): void { for (const v of vars ?? []) { decls.set(v.name, v); valueW(v.name).set({ value: coerce(v), quality: 'good', ts: new Date().toISOString(), }); metaW(v.name).set({ type: v.type ?? 'number', unit: v.unit, displayLow: v.low ?? 0, displayHigh: v.high ?? 100, writable: true, elem: v.elem, sizing: v.sizing, capacity: v.capacity, }); } } export function getLocalValueStore(name: string): Readable { return valueW(name); } export function getLocalMetaStore(name: string): Readable { return metaW(name); } // writeLocalState updates a local variable's live value in place. // When the declared variable is an array type, the written value is passed // through applySizing to enforce the declared sizing policy. export function writeLocalState(name: string, value: any): void { const sv = decls.get(name); let v = value; if (sv?.type === 'array' && Array.isArray(value)) v = applySizing(value as ArrVal[], sv); valueW(name).set({ value: v, quality: 'good', ts: new Date().toISOString() }); }