refactor(units): use UnitPolicy.convert instead of hardcoded m3/h<->m3/s scalars
Replace the M3H_TO_M3S constant in control/manual.js and the `* 3600` inline conversion in the status badge with this.unitPolicy.convert calls. Expose unitPolicy on the frozen control context so manual strategies pick it up without reaching into host. Matches the contract direction in .claude/refactor/CONTRACTS.md §6. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,14 @@ async function run() {
|
||||
}
|
||||
|
||||
async function forwardDemand(ctx, demand) {
|
||||
const { machineGroups, machines, logger } = ctx;
|
||||
const { machineGroups, machines, unitPolicy, logger } = ctx;
|
||||
logger?.info?.(`Manual demand forwarded: ${demand}`);
|
||||
|
||||
if (machineGroups && Object.keys(machineGroups).length > 0) {
|
||||
const groupDemand = unitPolicy.convert(demand, 'm3/h', 'm3/s', 'manual demand to machineGroups');
|
||||
await Promise.all(
|
||||
Object.values(machineGroups).map((group) =>
|
||||
group.handleInput('parent', demand).catch((err) => {
|
||||
group.handleInput('parent', groupDemand).catch((err) => {
|
||||
logger?.error?.(`Failed to forward demand to group: ${err.message}`);
|
||||
})
|
||||
)
|
||||
|
||||
@@ -146,6 +146,7 @@ class PumpingStation extends BaseDomain {
|
||||
levelVariants: this.levelVariants,
|
||||
volVariants: this.volVariants,
|
||||
flowThreshold: this.flowThreshold,
|
||||
unitPolicy: this.unitPolicy,
|
||||
host: this,
|
||||
};
|
||||
Object.defineProperty(ctx, 'machines', { enumerable: true, get: () => host.machines });
|
||||
@@ -262,7 +263,7 @@ class PumpingStation extends BaseDomain {
|
||||
};
|
||||
const { arrow = '❔', fill = 'grey' } = STYLES[this.state?.direction] || {};
|
||||
const pct = this.measurements.type('volumePercent').variant('predicted').position('atequipment').getCurrentValue() ?? 0;
|
||||
const netFlowM3h = (this.state?.netFlow ?? 0) * 3600;
|
||||
const netFlowM3h = this.unitPolicy.convert(this.state?.netFlow ?? 0, 'm3/s', 'm3/h', 'status badge netFlow');
|
||||
const mode = this.mode || '?';
|
||||
const manualPart = this.mode === 'manual' && Number.isFinite(this._manualDemand)
|
||||
? `Qd=${this._manualDemand.toFixed(0)} m³/h` : null;
|
||||
@@ -289,6 +290,10 @@ class PumpingStation extends BaseDomain {
|
||||
this.logger.debug(
|
||||
`Measurement update ${eventName} <- ${eventData.childName || child.config.general.name}: ${eventData.value} ${eventData.unit}`
|
||||
);
|
||||
if (measurementType === 'level') {
|
||||
this.measurementRouter.route(measurementType, eventData.value, position, eventData);
|
||||
return;
|
||||
}
|
||||
this.measurements.type(measurementType).variant('measured').position(position)
|
||||
.value(eventData.value, eventData.timestamp, eventData.unit);
|
||||
this.measurementRouter.route(measurementType, eventData.value, position, eventData);
|
||||
|
||||
@@ -4,8 +4,15 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
const { UnitPolicy } = require('generalFunctions');
|
||||
const manual = require('../../src/control/manual');
|
||||
|
||||
const unitPolicy = UnitPolicy.declare({
|
||||
canonical: { flow: 'm3/s' },
|
||||
output: { flow: 'm3/s' },
|
||||
requireUnitForTypes: [],
|
||||
});
|
||||
|
||||
function makeGroup(name) {
|
||||
const calls = { handleInput: [] };
|
||||
return {
|
||||
@@ -28,15 +35,15 @@ function makeLogger() {
|
||||
return { info: () => {}, debug: () => {}, warn: () => {}, error: () => {} };
|
||||
}
|
||||
|
||||
test('forwardDemand calls handleInput("parent", demand) on every machine group', async () => {
|
||||
test('forwardDemand calls handleInput("parent", canonical m3/s demand) on every machine group', async () => {
|
||||
const groups = { a: makeGroup('A'), b: makeGroup('B'), c: makeGroup('C') };
|
||||
const ctx = { machineGroups: groups, machines: {}, logger: makeLogger() };
|
||||
const ctx = { machineGroups: groups, machines: {}, unitPolicy, logger: makeLogger() };
|
||||
|
||||
await manual.forwardDemand(ctx, 50);
|
||||
await manual.forwardDemand(ctx, 360);
|
||||
|
||||
for (const g of Object.values(groups)) {
|
||||
assert.equal(g._calls.handleInput.length, 1);
|
||||
assert.deepEqual(g._calls.handleInput[0], ['parent', 50]);
|
||||
assert.deepEqual(g._calls.handleInput[0], ['parent', 0.1]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -54,7 +61,7 @@ test('forwardDemand with no machineGroups but direct machines splits demand even
|
||||
|
||||
test('run() is a no-op (manual mode is event-driven)', async () => {
|
||||
const groups = { a: makeGroup('A') };
|
||||
const ctx = { machineGroups: groups, machines: {}, logger: makeLogger() };
|
||||
const ctx = { machineGroups: groups, machines: {}, unitPolicy, logger: makeLogger() };
|
||||
await manual.run(ctx, { percControl: 0 });
|
||||
assert.equal(groups.a._calls.handleInput.length, 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user