Compare commits
1 Commits
12904b4902
...
d641d2248d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d641d2248d |
@@ -173,31 +173,93 @@
|
||||
setNumberField('node-input-flowSetpoint', this.flowSetpoint);
|
||||
setNumberField('node-input-flowDeadband', this.flowDeadband);
|
||||
|
||||
// Live-compute derived safety levels so the operator can see
|
||||
// what the % will actually trip at. Mirrors the code formula
|
||||
// in specificClass._validateThresholdOrdering.
|
||||
const fNum = (id) => parseFloat(document.getElementById(`node-input-${id}`)?.value);
|
||||
const updateDerivedLevels = () => {
|
||||
// Interactive diagram: place every threshold line/input at its
|
||||
// proportional y on the tank, plus compute derived safety levels
|
||||
// (dryRunLevel, overfillLevel) that are shown both in the diagram
|
||||
// and next to the safety-% fields. Same formulas as
|
||||
// specificClass._validateThresholdOrdering.
|
||||
const DIAG = { topY: 40, botY: 380 };
|
||||
const fNum = (id) => {
|
||||
const v = parseFloat(document.getElementById(`node-input-${id}`)?.value);
|
||||
return Number.isFinite(v) ? v : null;
|
||||
};
|
||||
const yForLevel = (val, basinH) => {
|
||||
if (val == null || !basinH) return null;
|
||||
const y = DIAG.botY - (val / basinH) * (DIAG.botY - DIAG.topY);
|
||||
return Math.max(DIAG.topY - 8, Math.min(DIAG.botY + 8, y));
|
||||
};
|
||||
const placeRow = (id, y) => {
|
||||
if (y == null) return;
|
||||
const line = document.getElementById(`ps-line-${id}`);
|
||||
const label = document.getElementById(`ps-label-${id}`);
|
||||
const unit = document.getElementById(`ps-unit-${id}`);
|
||||
const fo = document.getElementById(`ps-fo-${id}`);
|
||||
const sub = document.getElementById(`ps-sub-${id}`);
|
||||
if (line) { line.setAttribute('y1', y); line.setAttribute('y2', y); }
|
||||
if (label) label.setAttribute('y', y + 4);
|
||||
if (unit) unit.setAttribute('y', y + 4);
|
||||
if (fo) fo.setAttribute('y', y - 11);
|
||||
if (sub) sub.setAttribute('y', y + 15);
|
||||
};
|
||||
const redraw = () => {
|
||||
const basinH = fNum('basinHeight') || 5;
|
||||
placeRow('overflowLevel', yForLevel(fNum('overflowLevel'), basinH));
|
||||
placeRow('maxLevel', yForLevel(fNum('maxLevel'), basinH));
|
||||
placeRow('startLevel', yForLevel(fNum('startLevel'), basinH));
|
||||
placeRow('minLevel', yForLevel(fNum('minLevel'), basinH));
|
||||
placeRow('inflowLevel', yForLevel(fNum('inflowLevel'), basinH));
|
||||
const outflowY = yForLevel(fNum('outflowLevel'), basinH);
|
||||
placeRow('outflowLevel', outflowY);
|
||||
// Dead-volume band fills from outflowLevel down to the floor
|
||||
const deadvol = document.getElementById('ps-deadvol');
|
||||
if (deadvol && outflowY != null) {
|
||||
deadvol.setAttribute('y', outflowY);
|
||||
deadvol.setAttribute('height', Math.max(0, DIAG.botY - outflowY));
|
||||
}
|
||||
// Derived dryRunLevel (safety, from %)
|
||||
const basedOn = document.getElementById('node-input-minHeightBasedOn')?.value || 'outlet';
|
||||
const refLow = basedOn === 'inlet' ? fNum('inflowLevel') : fNum('outflowLevel');
|
||||
const dryRunPct = fNum('dryRunThresholdPercent');
|
||||
const overfillPct = fNum('overfillThresholdPercent');
|
||||
const overflow = fNum('overflowLevel');
|
||||
const dryRunLvl = Number.isFinite(refLow) && Number.isFinite(dryRunPct)
|
||||
? refLow * (1 + dryRunPct / 100) : null;
|
||||
const overfillLvl = Number.isFinite(overflow) && Number.isFinite(overfillPct)
|
||||
? overflow * (overfillPct / 100) : null;
|
||||
const dryEl = document.getElementById('derived-dryRunLevel');
|
||||
const ovfEl = document.getElementById('derived-overfillLevel');
|
||||
if (dryEl) dryEl.textContent = dryRunLvl != null ? `→ dryRunLevel ≈ ${dryRunLvl.toFixed(2)} m` : '→ dryRunLevel ≈ — m';
|
||||
if (ovfEl) ovfEl.textContent = overfillLvl != null ? `→ overfillLevel ≈ ${overfillLvl.toFixed(2)} m` : '→ overfillLevel ≈ — m';
|
||||
const dryPct = fNum('dryRunThresholdPercent');
|
||||
const ovfPct = fNum('overfillThresholdPercent');
|
||||
const ovf = fNum('overflowLevel');
|
||||
const dryLvl = (refLow != null && dryPct != null) ? refLow * (1 + dryPct / 100) : null;
|
||||
const ovfLvl = (ovf != null && ovfPct != null) ? ovf * (ovfPct / 100) : null;
|
||||
placeRow('dryRunLevel', yForLevel(dryLvl, basinH));
|
||||
const dryLbl = document.getElementById('ps-label-dryRunLevel');
|
||||
if (dryLbl) dryLbl.textContent = dryLvl != null
|
||||
? `dryRunLevel ≈ ${dryLvl.toFixed(2)} m (safety — from %)`
|
||||
: 'dryRunLevel ≈ — m (safety — from %)';
|
||||
// Safety-section readouts (same values, second view)
|
||||
const d1 = document.getElementById('derived-dryRunLevel');
|
||||
if (d1) d1.textContent = dryLvl != null ? `→ dryRunLevel ≈ ${dryLvl.toFixed(2)} m` : '→ dryRunLevel ≈ — m';
|
||||
const d2 = document.getElementById('derived-overfillLevel');
|
||||
if (d2) d2.textContent = ovfLvl != null ? `→ overfillLevel ≈ ${ovfLvl.toFixed(2)} m` : '→ overfillLevel ≈ — m';
|
||||
// Ordering warning ribbon
|
||||
const warn = document.getElementById('ps-warning');
|
||||
const issues = [];
|
||||
const pairs = [
|
||||
['outflowLevel', 'inflowLevel', '<'],
|
||||
['inflowLevel', 'overflowLevel', '<'],
|
||||
['minLevel', 'startLevel', '<='],
|
||||
['startLevel', 'maxLevel', '<'],
|
||||
['maxLevel', 'overflowLevel', '<='],
|
||||
];
|
||||
for (const [a, b, op] of pairs) {
|
||||
const av = fNum(a), bv = fNum(b);
|
||||
if (av == null || bv == null) continue;
|
||||
if (op === '<' ? !(av < bv) : !(av <= bv)) issues.push(`${a} ${op} ${b}`);
|
||||
}
|
||||
if (warn) {
|
||||
if (issues.length) { warn.setAttribute('visibility', 'visible'); warn.textContent = `⚠ Check ordering: ${issues.join(', ')}`; }
|
||||
else { warn.setAttribute('visibility', 'hidden'); }
|
||||
}
|
||||
};
|
||||
['inflowLevel','outflowLevel','overflowLevel','minHeightBasedOn','dryRunThresholdPercent','overfillThresholdPercent']
|
||||
.forEach((id) => {
|
||||
const el = document.getElementById(`node-input-${id}`);
|
||||
if (el) { el.addEventListener('input', updateDerivedLevels); el.addEventListener('change', updateDerivedLevels); }
|
||||
});
|
||||
setTimeout(updateDerivedLevels, 50);
|
||||
['basinHeight','overflowLevel','maxLevel','startLevel','minLevel','inflowLevel','outflowLevel',
|
||||
'dryRunThresholdPercent','overfillThresholdPercent','minHeightBasedOn'].forEach((id) => {
|
||||
const el = document.getElementById(`node-input-${id}`);
|
||||
if (el) { el.addEventListener('input', redraw); el.addEventListener('change', redraw); }
|
||||
});
|
||||
setTimeout(redraw, 60);
|
||||
|
||||
//------------------- END OF CUSTOM config UI ELEMENTS ------------------- //
|
||||
},
|
||||
@@ -250,76 +312,106 @@
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>Basin Geometry</h4>
|
||||
<p style="font-size:12px;color:#777;margin:0 0 8px 0;">All heights measured from the basin floor (0 m).</p>
|
||||
<h4>Basin parameters</h4>
|
||||
<p style="font-size:12px;color:#777;margin:0 0 8px 0;">Heights are measured from the basin floor (0 m). Enter values next to each line — the diagram scales to whatever you enter.</p>
|
||||
|
||||
<details open style="margin:0 0 12px 0;padding:6px;background:#fafafa;border:1px solid #e5e5e5;border-radius:4px;">
|
||||
<summary style="cursor:pointer;font-size:12px;color:#0c99d9;user-select:none;font-weight:600;">📐 Parameters diagram</summary>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 310" style="display:block;width:100%;max-width:440px;margin-top:6px;" font-family="Arial,sans-serif" font-size="11">
|
||||
<defs>
|
||||
<marker id="ps-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#1F4E79" />
|
||||
</marker>
|
||||
</defs>
|
||||
<!-- Tank body -->
|
||||
<rect x="170" y="30" width="100" height="260" fill="#F0F8FF" stroke="#333" stroke-width="1.5" />
|
||||
<!-- Dead-volume band at bottom -->
|
||||
<rect x="171" y="270" width="98" height="19" fill="#AACCE0" />
|
||||
<!-- basinHeight (rim) -->
|
||||
<line x1="165" y1="30" x2="275" y2="30" stroke="#333" stroke-width="1.5" />
|
||||
<text x="280" y="34" fill="#333" font-size="10">basinHeight</text>
|
||||
<!-- overflowLevel -->
|
||||
<line x1="165" y1="55" x2="275" y2="55" stroke="#C0392B" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text x="280" y="58" fill="#C0392B" font-size="10">overflowLevel</text>
|
||||
<!-- maxLevel -->
|
||||
<line x1="165" y1="90" x2="275" y2="90" stroke="#D68910" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text x="280" y="93" fill="#D68910" font-size="10">maxLevel</text>
|
||||
<!-- startLevel -->
|
||||
<line x1="165" y1="150" x2="275" y2="150" stroke="#1E8449" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text x="280" y="153" fill="#1E8449" font-size="10">startLevel</text>
|
||||
<!-- Inlet arrow -->
|
||||
<line x1="130" y1="180" x2="170" y2="180" stroke="#1F4E79" stroke-width="2" marker-end="url(#ps-arrow)" />
|
||||
<text x="125" y="177" text-anchor="end" fill="#1F4E79" font-size="10" font-weight="bold">Inlet</text>
|
||||
<text x="125" y="189" text-anchor="end" fill="#777" font-size="9">bottom of pipe</text>
|
||||
<!-- minLevel -->
|
||||
<line x1="165" y1="215" x2="275" y2="215" stroke="#6C3483" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text x="280" y="218" fill="#6C3483" font-size="10">minLevel</text>
|
||||
<!-- dryRunLevel -->
|
||||
<line x1="165" y1="245" x2="275" y2="245" stroke="#C0392B" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text x="280" y="248" fill="#C0392B" font-size="10">dryRunLevel</text>
|
||||
<text x="280" y="259" fill="#999" font-style="italic" font-size="9">safety — from %</text>
|
||||
<!-- Outlet arrow -->
|
||||
<line x1="270" y1="278" x2="310" y2="278" stroke="#1F4E79" stroke-width="2" marker-end="url(#ps-arrow)" />
|
||||
<text x="315" y="275" fill="#1F4E79" font-size="10" font-weight="bold">Outlet</text>
|
||||
<text x="315" y="287" fill="#777" font-size="9">top of pipe</text>
|
||||
<!-- Floor / datum -->
|
||||
<line x1="165" y1="290" x2="275" y2="290" stroke="#000" stroke-width="2" />
|
||||
<text x="155" y="294" text-anchor="end" fill="#000" font-size="10">0 m (datum)</text>
|
||||
</svg>
|
||||
</details>
|
||||
<style>
|
||||
#ps-basin-diagram input[type=number] {
|
||||
width: 100%; height: 20px; box-sizing: border-box;
|
||||
font-size: 11px; padding: 1px 4px; margin: 0;
|
||||
border: 1px solid #ccc; border-radius: 3px; background: #fff;
|
||||
}
|
||||
#ps-basin-diagram input[type=number]:focus { outline: 1px solid #0c99d9; border-color: #0c99d9; }
|
||||
</style>
|
||||
|
||||
<svg id="ps-basin-diagram" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 430"
|
||||
style="display:block;width:100%;max-width:540px;margin:0 0 12px 0;background:#fff;border:1px solid #e5e5e5;border-radius:4px;"
|
||||
font-family="Arial,sans-serif" font-size="11">
|
||||
<defs>
|
||||
<marker id="ps-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" fill="#1F4E79" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Tank body -->
|
||||
<rect x="200" y="40" width="120" height="340" fill="#F0F8FF" stroke="#333" stroke-width="1.5" />
|
||||
<!-- Dead-volume band (y + height updated dynamically below outflowLevel) -->
|
||||
<rect id="ps-deadvol" x="201" width="118" fill="#AACCE0" />
|
||||
|
||||
<!-- basinHeight — always at tank rim (y=40 in viewBox coords) -->
|
||||
<line id="ps-line-basinHeight" x1="195" y1="40" x2="325" y2="40" stroke="#333" stroke-width="1.5" />
|
||||
<text id="ps-label-basinHeight" x="330" y="44" fill="#333">basinHeight</text>
|
||||
<foreignObject id="ps-fo-basinHeight" x="425" y="29" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-basinHeight" min="0" step="0.1" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-basinHeight" x="500" y="44" fill="#555">m</text>
|
||||
|
||||
<!-- overflowLevel -->
|
||||
<line id="ps-line-overflowLevel" x1="195" x2="325" stroke="#C0392B" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text id="ps-label-overflowLevel" x="330" fill="#C0392B">overflowLevel</text>
|
||||
<foreignObject id="ps-fo-overflowLevel" x="425" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-overflowLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-overflowLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- maxLevel -->
|
||||
<line id="ps-line-maxLevel" x1="195" x2="325" stroke="#D68910" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text id="ps-label-maxLevel" x="330" fill="#D68910">maxLevel</text>
|
||||
<foreignObject id="ps-fo-maxLevel" x="425" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-maxLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-maxLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- startLevel -->
|
||||
<line id="ps-line-startLevel" x1="195" x2="325" stroke="#1E8449" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text id="ps-label-startLevel" x="330" fill="#1E8449">startLevel</text>
|
||||
<foreignObject id="ps-fo-startLevel" x="425" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-startLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-startLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- Inlet — arrow + input on the left -->
|
||||
<line id="ps-line-inflowLevel" x1="140" x2="200" stroke="#1F4E79" stroke-width="2" marker-end="url(#ps-arrow)" />
|
||||
<text id="ps-label-inflowLevel" x="135" text-anchor="end" fill="#1F4E79" font-weight="bold">Inlet</text>
|
||||
<text id="ps-sub-inflowLevel" x="135" text-anchor="end" fill="#777" font-size="9">bottom of pipe</text>
|
||||
<foreignObject id="ps-fo-inflowLevel" x="5" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-inflowLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-inflowLevel" x="80" fill="#555">m</text>
|
||||
|
||||
<!-- minLevel -->
|
||||
<line id="ps-line-minLevel" x1="195" x2="325" stroke="#6C3483" stroke-dasharray="4 2" stroke-width="1.5" />
|
||||
<text id="ps-label-minLevel" x="330" fill="#6C3483">minLevel</text>
|
||||
<foreignObject id="ps-fo-minLevel" x="425" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-minLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-minLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- dryRunLevel (derived, read-only) -->
|
||||
<line id="ps-line-dryRunLevel" x1="195" x2="325" stroke="#C0392B" stroke-dasharray="1 2" stroke-width="1" opacity="0.6" />
|
||||
<text id="ps-label-dryRunLevel" x="330" fill="#C0392B" font-size="10" font-style="italic">dryRunLevel ≈ — m (safety — from %)</text>
|
||||
|
||||
<!-- Outlet — arrow on right, input below the threshold column -->
|
||||
<line id="ps-line-outflowLevel" x1="320" x2="360" stroke="#1F4E79" stroke-width="2" marker-end="url(#ps-arrow)" />
|
||||
<text id="ps-label-outflowLevel" x="365" fill="#1F4E79" font-weight="bold">Outlet</text>
|
||||
<text id="ps-sub-outflowLevel" x="365" fill="#777" font-size="9">top of pipe</text>
|
||||
<foreignObject id="ps-fo-outflowLevel" x="425" width="70" height="22">
|
||||
<input xmlns="http://www.w3.org/1999/xhtml" type="number" id="node-input-outflowLevel" min="0" step="0.01" />
|
||||
</foreignObject>
|
||||
<text id="ps-unit-outflowLevel" x="500" fill="#555">m</text>
|
||||
|
||||
<!-- Floor / datum -->
|
||||
<line x1="195" y1="380" x2="325" y2="380" stroke="#000" stroke-width="2" />
|
||||
<text x="330" y="384" fill="#000">0 m (datum)</text>
|
||||
|
||||
<!-- Ordering-warning ribbon -->
|
||||
<text id="ps-warning" x="260" y="410" text-anchor="middle" fill="#C0392B" font-size="10" font-style="italic" visibility="hidden"></text>
|
||||
</svg>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="node-input-basinVolume"><i class="fa fa-cube"></i> Basin Volume (m³)</label>
|
||||
<input type="number" id="node-input-basinVolume" min="0" step="0.1" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-basinHeight"><i class="fa fa-arrows-v"></i> Basin Height (m)</label>
|
||||
<input type="number" id="node-input-basinHeight" min="0" step="0.1" />
|
||||
</div>
|
||||
|
||||
<!-- Inlet/Outlet elevations -->
|
||||
<div class="form-row">
|
||||
<label for="node-input-inflowLevel"><i class="fa fa-long-arrow-up"></i> Inlet (bottom of pipe, m)</label>
|
||||
<input type="number" id="node-input-inflowLevel" min="0" step="0.01" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-outflowLevel"><i class="fa fa-long-arrow-down"></i> Outlet (top of pipe, m)</label>
|
||||
<input type="number" id="node-input-outflowLevel" min="0" step="0.01" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-overflowLevel"><i class="fa fa-tint"></i> Overflow (weir crest, m)</label>
|
||||
<input type="number" id="node-input-overflowLevel" min="0" step="0.01" />
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -334,18 +426,7 @@
|
||||
</div>
|
||||
|
||||
<div id="ps-mode-levelbased" class="ps-mode-section">
|
||||
<div class="form-row">
|
||||
<label for="node-input-minLevel">minLevel (m)</label>
|
||||
<input type="number" id="node-input-minLevel" placeholder="m" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-startLevel">startLevel (m)</label>
|
||||
<input type="number" id="node-input-startLevel" placeholder="m" />
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-maxLevel">maxLevel (m)</label>
|
||||
<input type="number" id="node-input-maxLevel" placeholder="m" />
|
||||
</div>
|
||||
<p style="font-size:12px;color:#777;margin:0;">Level-based uses <code>minLevel</code> / <code>startLevel</code> / <code>maxLevel</code> from the diagram above.</p>
|
||||
</div>
|
||||
|
||||
<div id="ps-mode-flowbased" class="ps-mode-section" style="display:none">
|
||||
|
||||
Reference in New Issue
Block a user