feat(setDemand): surface specificClass.setDemand(value, unit='%') + slim npm pack
Why: - pumpingStation level-based control was calling MGC.handleInput(percent) directly. handleInput expects canonical m³/s; a 1 % keep-alive arrived as 1 m³/s ≈ 3600 m³/h, the dispatcher clamped to dt.flow.max and the group ran at 100 %. The unit math already existed inside the set.demand command handler — but only that handler could reach it. What: - New public method `async setDemand(value, unit='%')` on MachineGroup (specificClass.js). Resolves the unit (`%` → interpolate against the dynamic-totals envelope, absolute units → convert(value)) and calls handleInput with canonical m³/s. Negative value remains the operator stop-all signal. Single source of truth for the percent → m³/s rule. - Refactor handlers.setDemand to parse the payload + apply mode gating and then delegate to source.setDemand. Drops the local `convert` import (now reached via the source). - Update commands.basic.test.js mock with a setDemand shim that mirrors the real method, so existing handleInput assertions still hold. Packaging: - Add .npmignore mirroring .gitignore plus dev-only trees (test/, wiki/, CLAUDE.md, …) so the published tarball stays small. - Extend .gitignore with the standard dev-artifact deny list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -359,6 +359,39 @@ class MachineGroup extends BaseDomain {
|
||||
return this._demandDispatcher.fireAndWait({ source, demand, powerCap, priorityList });
|
||||
}
|
||||
|
||||
// Operator-style entry point: accepts a (value, unit) pair and resolves
|
||||
// to canonical m³/s before delegating to handleInput. Single source of
|
||||
// truth for the unit math shared by the set.demand command handler and
|
||||
// by parent nodes (e.g. pumpingStation level-based control) that hold a
|
||||
// direct reference to this specificClass and need to push a % demand
|
||||
// without re-implementing the interpolation. Negative value is the
|
||||
// stop-all signal regardless of unit.
|
||||
async setDemand(value, unit = '%') {
|
||||
const v = Number(value);
|
||||
if (!Number.isFinite(v)) {
|
||||
this.logger?.error?.(`setDemand: invalid value '${value}'`);
|
||||
return undefined;
|
||||
}
|
||||
if (v < 0) {
|
||||
await this.turnOffAllMachines();
|
||||
return undefined;
|
||||
}
|
||||
let canonical;
|
||||
if (unit === '%') {
|
||||
const dt = this.calcDynamicTotals();
|
||||
canonical = this.interpolation.interpolate_lin_single_point(
|
||||
v, 0, 100, dt.flow.min, dt.flow.max);
|
||||
} else {
|
||||
try {
|
||||
canonical = convert(v).from(unit).to('m3/s');
|
||||
} catch (err) {
|
||||
this.logger?.error?.(`setDemand: cannot convert ${v} ${unit} -> m3/s: ${err?.message || err}`);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return this.handleInput('parent', canonical);
|
||||
}
|
||||
|
||||
async _runDispatch(source, demand, powerCap, priorityList) {
|
||||
const demandQ = parseFloat(demand);
|
||||
if (!Number.isFinite(demandQ)) {
|
||||
|
||||
Reference in New Issue
Block a user