Files
pumpingStation/test/integration/basic-dashboard-flow.test.js

104 lines
3.9 KiB
JavaScript
Raw Normal View History

Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
function loadDashboardFlow() {
const flowPath = path.join(__dirname, '../../examples/02-Dashboard.json');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
return JSON.parse(fs.readFileSync(flowPath, 'utf8'));
}
function makeContextStub() {
const store = {};
return {
get(key) {
return store[key];
},
set(key, value) {
store[key] = value;
},
};
}
test('basic dashboard flow contains the pumpingStation node and trend widgets', () => {
const flow = loadDashboardFlow();
const ps = flow.find((n) => n.type === 'pumpingStation');
const parser = flow.find((n) => n.id === 'fn_status_split');
const levelChart = flow.find((n) => n.id === 'ui_chart_level');
const volumeChart = flow.find((n) => n.id === 'ui_chart_volume');
const flowChart = flow.find((n) => n.id === 'ui_chart_flow');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
assert.ok(ps, 'pumpingStation node should exist');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
assert.equal(ps.type, 'pumpingStation');
assert.equal(ps.controlMode, 'levelbased');
assert.equal(ps.levelCurveType, 'linear');
assert.equal(ps.inletPipeDiameter, 0.3);
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
assert.equal(ps.outletPipeDiameter, 0.3);
assert.ok(parser, 'fn_status_split should exist');
assert.equal(parser.outputs, 14);
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
assert.equal(levelChart.type, 'ui-chart');
assert.equal(volumeChart.type, 'ui-chart');
assert.equal(flowChart.type, 'ui-chart');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
});
test('basic dashboard parser routes process fields to charts and state text', () => {
const flow = loadDashboardFlow();
const parser = flow.find((n) => n.id === 'fn_status_split');
assert.ok(parser, 'fn_status_split should exist');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const func = new Function('msg', 'context', 'node', parser.func);
const context = makeContextStub();
const node = { send() {} };
// Flatten format is `${type}.${variant}.${position}.${childId}`. When the
// runtime writes without an explicit .child(), childId='default'. Mirror
// the real shape here. (See generalFunctions/src/measurements/
// MeasurementContainer.js getFlattenedOutput.)
const out = func({
payload: {
'level.predicted.atequipment.default': 3.25,
'volume.predicted.atequipment.default': 32.5,
'volumePercent.predicted.atequipment.default': 65,
'flow.predicted.in.default': 0.005,
'flow.predicted.out.default': 0.002,
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
'netFlowRate.predicted.atequipment.default': 0.003,
percControl: 25,
mode: 'levelbased',
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
direction: 'filling',
safetyState: 'normal',
isOverflowing: false,
timeleft: 400,
},
}, context, node);
assert.ok(Array.isArray(out));
assert.equal(out.length, 14);
assert.equal(out[0].payload, 'levelbased');
assert.equal(out[1].payload, 'filling');
assert.equal(out[2].payload, '3.25 m');
assert.equal(out[3].payload, '32.50 m³');
assert.equal(out[4].payload, '65.00 %');
assert.equal(out[5].payload, '25.0 %');
assert.deepEqual(out[7], { topic: 'Level', payload: 3.25 });
assert.deepEqual(out[8], { topic: 'Volume', payload: 32.5 });
assert.deepEqual(out[9], { topic: 'Volume %', payload: 65 });
assert.deepEqual(out[10], { topic: 'Inflow', payload: 18 });
assert.deepEqual(out[11], { topic: 'Outflow', payload: 7.2 });
assert.deepEqual(out[12], { topic: 'Net', payload: 10.8 });
assert.ok(Array.isArray(out[13].payload));
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
});
test('basic dashboard parser keeps previous values when process output sends only changed fields', () => {
const flow = loadDashboardFlow();
const parser = flow.find((n) => n.id === 'fn_status_split');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
const func = new Function('msg', 'context', 'node', parser.func);
const context = makeContextStub();
const node = { send() {} };
func({ payload: { 'level.predicted.atequipment.default': 3.1, percControl: 10 } }, context, node);
const out = func({ payload: { percControl: 20 } }, context, node);
assert.equal(out[2].payload, '3.10 m');
assert.equal(out[5].payload, '20.0 %');
Hold-then-ramp shift semantics + shiftArmPercent + e2e tests Runtime (specificClass.js): - Replace the "shift left both ramp ends" geometry with a true hold-then-ramp hysteresis driven by output %, not level: • Up-curve % crosses shiftArmPercent on the way up → ARM. • Filling→draining transition while armed → capture the up-curve % at that moment as _shiftHoldValue. • Draining + level ≥ shiftLevel → output stays at _shiftHoldValue (horizontal hold, matching the dashed segment in the SVG). • Draining + level in [start, shift] → output ramps holdValue → 0 % along the same curve shape (linear or log) as the up curve. • Draining + level < startLevel → 0 % AND disarm. • Returning to filling clears holdValue, stays armed; next drain transition captures a fresh hold so bouncing fills rearm cleanly. • Disarm only when level ≤ startLevel. - New _curveShape(x) helper for shared linear/log shaping. - Removed legacy _levelBasedRampStart / _levelBasedRampTop / _updateShiftArmed in favour of the inline state machine. Adapter (nodeClass.js): - Pipe shiftArmPercent through to control.levelbased. Editor (pumpingStation.html + src/editor/): - Add shiftArmPercent input row (% with unit) to the mode side panel (only shown when shifted ramp is enabled). Default 95 %. - Add the horizontal arming-% line + label inside the mode SVG — this is the "% Threshold triggering shifted ramp down" line from the original drawing that had been missing. - Redraw the shifted-down curve to match the SVG geometry literally: 100 % flat from maxLevel → shiftLevel, then ramp shiftLevel → startLevel down to 0 %, OFF below startLevel. Preview shows the worst-case envelope (hold = 100 %); runtime hold is captured live. - Validation extended: 0 < shiftArmPercent ≤ 100; ordering rules preserved (start < shift ≤ max etc.). - Auto-default shiftArmPercent to 95 when shift is enabled and the current value is missing or out of range. Dashboard example (examples/basic-dashboard.flow.json): - Parser now reads `level.predicted.atequipment.default` etc. The MeasurementContainer flatten format includes the implicit 'default' childId; consumers must include it. Comment in the parser points at the documenting source in generalFunctions. Tests: - test/basic: replace old level-armed-shift tests with two new ones that exercise the hold-then-ramp arming, capture, hold, ramp-down, disarm, and the bounce case (filling→draining→filling→draining captures a fresh hold each time). - test/integration/shifted-ramp-end-to-end.test.js: new file. Drives Q_IN/Q_OUT through the full runtime tick with a controllable clock, asserting the same hysteresis path the dashboard exercises. - test/integration/basic-dashboard-flow.test.js: fixture keys updated to the .default-suffixed form so they match the real flatten output. 56/56 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 11:46:46 +02:00
});