P11.5 + B2.1/B2.2: per-command units + description (where applicable)

Adds  to scalar setters whose payloads are
plain numbers OR {value, unit}. Skipped where payload is compound or
mode-dependent (control-%, {F, C: [...]}, etc.) — documented inline.
Every command gains a description field for wikiGen consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-11 17:41:07 +02:00
parent ef81013e96
commit 5f1c9ae2ff
5 changed files with 107 additions and 88 deletions

View File

@@ -91,35 +91,29 @@ class PumpingStation extends BaseDomain {
this.measurements.type('volume').variant('predicted').position('atequipment')
.value(this.basin.minVol, Date.now(), 'm3').unit('m3');
// Plain id-keyed maps. Tests assign into them directly (legacy contract);
// ChildRouter onRegister handlers below also populate them.
this.machines = {};
this.stations = {};
this.machineGroups = {};
this.predictedFlowChildren = new Map();
// Registry-as-truth — `this.machines / machineGroups / stations` are
// read-only getters flattening `this.child[softwareType]` (BaseDomain
// helper). Mutations go through `childRegistrationUtils.registerChild`.
this.declareChildGetter('machines', 'machine');
this.declareChildGetter('machineGroups', 'machinegroup');
this.declareChildGetter('stations', 'pumpingstation');
// SafetyController constructed after child maps so its captured ctx
// references the live dicts rather than undefined.
// SafetyController's captured ctx exposes the same three names as live
// getters (installed in context()), so the registry remains the single
// source of truth long after configure() returns.
this.safety = new SafetyController(this.context());
this.router
.onRegister('measurement', (child) => this._subscribeMeasurement(child))
.onRegister('machine', (child) => {
this.machines[child.config.general.id] = child;
// Skip individual machines when a machineGroup parent is present —
// the group's flow.predicted already aggregates child machines.
if (Object.keys(this.machineGroups).length === 0) {
this._subscribePredictedFlow(child);
}
})
.onRegister('machinegroup', (child) => {
this.machineGroups[child.config.general.id] = child;
this._subscribePredictedFlow(child);
})
.onRegister('pumpingstation', (child) => {
this.stations[child.config.general.id] = child;
this._subscribePredictedFlow(child);
});
.onRegister('machinegroup', (child) => this._subscribePredictedFlow(child))
.onRegister('pumpingstation', (child) => this._subscribePredictedFlow(child));
this.logger.debug('PumpingStation initialized');
}
@@ -130,21 +124,28 @@ class PumpingStation extends BaseDomain {
// `_lastDirection`, `_stopHystRunning`) write straight to the live
// instance — Object.freeze on the view itself is fine because these
// flags live on the host, not in the view.
//
// machines / machineGroups / stations are installed as live getters
// that delegate to this.* getters (declareChildGetter). SafetyController
// captures this ctx once at construction; the getters keep it reading
// fresh from the registry after later child registrations.
context() {
return Object.freeze({
const host = this;
const ctx = {
...super.context(),
basin: this.basin,
flowAggregator: this.flowAggregator,
machines: this.machines,
machineGroups: this.machineGroups,
stations: this.stations,
mode: this.mode,
flowVariants: this.flowVariants,
levelVariants: this.levelVariants,
volVariants: this.volVariants,
flowThreshold: this.flowThreshold,
host: this,
});
};
Object.defineProperty(ctx, 'machines', { enumerable: true, get: () => host.machines });
Object.defineProperty(ctx, 'machineGroups', { enumerable: true, get: () => host.machineGroups });
Object.defineProperty(ctx, 'stations', { enumerable: true, get: () => host.stations });
return Object.freeze(ctx);
}
tick() {
@@ -301,9 +302,6 @@ class PumpingStation extends BaseDomain {
const [posKey, eventName] = mapped;
const childId = child.config.general.id ?? child.config.general.name;
if (!this.predictedFlowChildren.has(childId)) {
this.predictedFlowChildren.set(childId, { in: 0, out: 0 });
}
child.measurements.emitter.on(eventName, (eventData = {}) => {
const unit = eventData.unit || child.config?.general?.unit;
const ts = eventData.timestamp || Date.now();