Bounds (new src/editor/bounds.js):
- Sets HTML5 min/max on every level + percent input each redraw,
derived from the current values of related inputs so the spinner
stops at the basin hierarchy:
0 < outflowLevel < dryRunLevel < startLevel ≤ inflowLevel
≤ shiftLevel ≤ maxLevel ≤ overflowLevel ≤ basinHeight
- dryRunPercent capped so dryRunLevel ≤ startLevel given current outflow.
- shiftArmPercent ∈ [1, 100]; highVolumeSafety% ∈ [1, 100].
Validation:
- New visible ribbon above the basin diagram (#ps-basin-validation)
listing every hierarchy violation. The in-SVG warning text is now a
small reminder ("⚠ N ordering issues").
- basin-diagram.js owns hierarchy issues; mode-preview.js trimmed to
only own shift-specific issues (shift > start, shift ≤ max,
shiftArmPercent range, shiftLevel required-when-enabled).
- oneditsave blocks Deploy on the union of _psBasinValidationIssues
and _psModeValidationIssues with a RED.notify listing all problems.
Layout polish:
- Side panel widened to 220 px with minmax(0, 1fr) first column so long
labels can no longer push the rows past the panel edge.
- Basin SVG max-width 380 → 360, gap between side panel and SVG bumped
14 → 28 px. Tank shifted right (x=145 width=110) so the inlet
"bottom of pipe" sub-label is no longer clipped on the left edge.
- "0 m (datum)" moved below the tank (y=395, centred) so it can't
collide with "Outlet / top of pipe" when outflowLevel is near floor.
- Zone labels shortened (Spare / Sewage + buffer / Buffer / Dead vol)
and only show when the bracketing thresholds are ≥ 28 px apart, so
they never sit on a threshold label.
- Mode preview axis labels under the chart removed — line colour +
side-panel labels + hover-couple already identify each line. Stub
<text> elements left hidden to keep the redraw loop simple. Arm-%
line + label trimmed in 10 px on the right so they're not clipped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
115 lines
5.1 KiB
JavaScript
115 lines
5.1 KiB
JavaScript
// PumpingStation editor — oneditprepare entry. Wires up form-field
|
|
// initialization, control-mode toggle, safety toggles, and binds
|
|
// redraws for the basin diagram + level-based mode preview.
|
|
|
|
(function () {
|
|
const ns = window.PSEditor = window.PSEditor || {};
|
|
|
|
ns.oneditprepare = function () {
|
|
const node = this;
|
|
|
|
// Wait for menu data (asset/logger/position dropdowns) before init.
|
|
const waitForMenuData = () => {
|
|
if (window.EVOLV?.nodes?.pumpingStation?.initEditor) {
|
|
window.EVOLV.nodes.pumpingStation.initEditor(node);
|
|
} else {
|
|
setTimeout(waitForMenuData, 50);
|
|
}
|
|
};
|
|
waitForMenuData();
|
|
|
|
const refHeightEl = document.getElementById('node-input-refHeight');
|
|
if (refHeightEl) refHeightEl.value = node.refHeight || 'NAP';
|
|
|
|
// Safety toggle pairs — each toggle enables/disables its threshold input.
|
|
const dryRunToggle = document.getElementById('node-input-enableDryRunProtection');
|
|
const dryRunPercent = document.getElementById('node-input-dryRunThresholdPercent');
|
|
const highVolumeToggle = document.getElementById('node-input-enableHighVolumeSafety');
|
|
const highVolumePercent = document.getElementById('node-input-highVolumeSafetyThresholdPercent');
|
|
|
|
const toggleInput = (toggleEl, inputEl) => {
|
|
if (!toggleEl || !inputEl) return;
|
|
inputEl.disabled = !toggleEl.checked;
|
|
inputEl.parentElement.classList.toggle('disabled', inputEl.disabled);
|
|
};
|
|
|
|
if (dryRunToggle && dryRunPercent) {
|
|
dryRunToggle.checked = !!node.enableDryRunProtection;
|
|
dryRunPercent.value = Number.isFinite(node.dryRunThresholdPercent) ? node.dryRunThresholdPercent : 2;
|
|
dryRunToggle.addEventListener('change', () => toggleInput(dryRunToggle, dryRunPercent));
|
|
toggleInput(dryRunToggle, dryRunPercent);
|
|
}
|
|
|
|
if (highVolumeToggle && highVolumePercent) {
|
|
highVolumeToggle.checked = node.enableHighVolumeSafety !== undefined
|
|
? !!node.enableHighVolumeSafety
|
|
: !!node.enableOverfillProtection;
|
|
const highVolumePct = node.highVolumeSafetyThresholdPercent ?? node.overfillThresholdPercent;
|
|
highVolumePercent.value = Number.isFinite(highVolumePct) ? highVolumePct : 98;
|
|
highVolumeToggle.addEventListener('change', () => toggleInput(highVolumeToggle, highVolumePercent));
|
|
toggleInput(highVolumeToggle, highVolumePercent);
|
|
}
|
|
|
|
// Control-mode section toggle (levelbased / manual).
|
|
const toggleModeSections = (val) => {
|
|
document.querySelectorAll('.ps-mode-section').forEach((el) => el.style.display = 'none');
|
|
const active = document.getElementById(`ps-mode-${val}`);
|
|
if (active) active.style.display = '';
|
|
};
|
|
const modeSelect = document.getElementById('node-input-controlMode');
|
|
if (modeSelect) {
|
|
modeSelect.value = node.controlMode === 'manual' ? 'manual' : 'levelbased';
|
|
toggleModeSections(modeSelect.value);
|
|
modeSelect.addEventListener('change', (e) => toggleModeSections(e.target.value));
|
|
}
|
|
|
|
// Numeric field defaults.
|
|
ns.setNumberField('node-input-startLevel', node.startLevel);
|
|
ns.setNumberField('node-input-maxLevel', node.maxLevel);
|
|
ns.setNumberField('node-input-logCurveFactor', node.logCurveFactor);
|
|
ns.setNumberField('node-input-shiftLevel', node.shiftLevel);
|
|
ns.setNumberField('node-input-shiftArmPercent', Number.isFinite(node.shiftArmPercent) ? node.shiftArmPercent : 95);
|
|
ns.setNumberField('node-input-flowSetpoint', node.flowSetpoint);
|
|
ns.setNumberField('node-input-flowDeadband', node.flowDeadband);
|
|
|
|
const curveSelect = document.getElementById('node-input-levelCurveType');
|
|
if (curveSelect) curveSelect.value = node.levelCurveType || node.curveType || 'linear';
|
|
const shiftCheckbox = document.getElementById('node-input-enableShiftedRamp');
|
|
if (shiftCheckbox) shiftCheckbox.checked = !!node.enableShiftedRamp;
|
|
|
|
// Bind redraws to the inputs each diagram cares about.
|
|
ns.bindRedraw(
|
|
['basinHeight', 'overflowLevel', 'inflowLevel', 'outflowLevel',
|
|
'dryRunThresholdPercent', 'highVolumeSafetyThresholdPercent'],
|
|
ns.basinDiagram.redraw
|
|
);
|
|
ns.bindRedraw(
|
|
// dryRunLevel is derived (outflowLevel + dryRunThresholdPercent),
|
|
// so the mode preview must redraw when either of those change.
|
|
['startLevel', 'maxLevel', 'inflowLevel', 'outflowLevel', 'overflowLevel',
|
|
'dryRunThresholdPercent',
|
|
'levelCurveType', 'logCurveFactor', 'enableShiftedRamp', 'shiftLevel',
|
|
'shiftArmPercent'],
|
|
ns.modePreview.redraw
|
|
);
|
|
|
|
// Whenever any level/percent input changes, refresh the bounds first
|
|
// so the next redraw + validation sees the correct min/max attrs.
|
|
ns.bindRedraw(
|
|
['basinHeight', 'basinVolume', 'overflowLevel', 'maxLevel',
|
|
'inflowLevel', 'startLevel', 'outflowLevel',
|
|
'dryRunThresholdPercent', 'highVolumeSafetyThresholdPercent',
|
|
'enableShiftedRamp', 'shiftLevel', 'shiftArmPercent'],
|
|
() => ns.bounds?.apply()
|
|
);
|
|
|
|
// Initial render + hover-couple wiring once the DOM is settled.
|
|
setTimeout(() => {
|
|
ns.bounds?.apply();
|
|
ns.basinDiagram.redraw();
|
|
ns.modePreview.redraw();
|
|
ns.hoverCouple?.init();
|
|
}, 60);
|
|
};
|
|
})();
|