feat(mgc): editor defaults, compact status badge, mode-case fix, real example flows + dashboard
Editor (mgc.html)
- Drag-in defaults now expose mode (optimalControl) and scaling (normalized)
via dropdowns in the edit dialog. Was: no control fields in the UI at all,
so users had to send set.mode/set.scaling after deploy or live with the
hidden schema defaults.
Wire-up (src/nodeClass.js)
- buildDomainConfig now bridges the flat editor fields (mode, scaling) into
the nested schema shape (mode.current, scaling.current). Was: returned {}
so the editor's mode/scaling never reached the runtime.
Mode-case bug fix (src/specificClass.js)
- Schema enum values are camelCase (optimalControl, priorityControl) but the
runtime switch in _runDispatch matched lowercase only. With the default
config, dispatch silently fell through to the warning branch and nothing
ran. Normalise via String(this.mode).toLowerCase() so both forms work.
Status badge (src/io/output.js)
- Compacted from ~80 chars (mode | Ⓝ: 💨=Q/Qmax | ⚡=P | N machine(s)) to
~50 chars (mode | norm | Q=Q/Qmax m³/h | P=P kW | active/total x).
Drops emoji glyphs that rendered inconsistently across themes; uses the
same dot+fill convention as pumpingStation.
Output extension (src/io/output.js)
- getOutput() now also emits flowCapacityMin/Max, machineCount,
machineCountActive. Was: only group-level totals + dist-from-peak +
mode/scaling, so dashboards couldn't show capacity / active count
without subscribing to each rotatingMachine individually.
Examples
- Drop pre-refactor stubs (basic.flow.json, integration.flow.json,
edge.flow.json). They had a single MGC + inject + debug, no children,
and never dispatched anything.
- 01-Basic.json: 1 MGC + 3 rotatingMachine pumps + Setup once-fires
virtualControl + cmd.startup on all pumps via fan-out function. Numbered
driver groups for Control mode / Scaling / Operator demand. Pumps
register with MGC via Port 2 (child.register, automatic).
- 02-Dashboard.json: same plumbing + FlowFuse Dashboard 2.0 page with
Controls (mode + scaling buttons, demand slider 0–100, stop + init
buttons), Status (7 ui-text rows), Trends (3 charts: flow + capacity,
power, BEP rel %), and a raw-output ui-template dumping every Port 0
field. Fan-out function caches last-known values so deltas don't blank.
Wiki + README
- examples/README.md rewritten for the two-file set with canonical command
surface table and "what to try" recipes.
- wiki/Home.md §11 (Examples) updated; §14 #4 (TODO flow item) replaced
with the actual current limitation (no per-pump fan-out on Port 0).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,6 +41,18 @@ function getOutput(mgc) {
|
||||
out.scaling = scaling;
|
||||
out.absDistFromPeak = absDistFromPeak;
|
||||
out.relDistFromPeak = relDistFromPeak;
|
||||
|
||||
// Group capacity + active-machine counts. Surfaced so dashboards can
|
||||
// show the same numbers the status badge does without subscribing to
|
||||
// every child node individually.
|
||||
out.flowCapacityMax = mgc.dynamicTotals?.flow?.max ?? 0;
|
||||
out.flowCapacityMin = mgc.dynamicTotals?.flow?.min ?? 0;
|
||||
out.machineCount = Object.keys(mgc.machines || {}).length;
|
||||
out.machineCountActive = Object.values(mgc.machines || {}).filter((m) => {
|
||||
const s = m?.state?.getCurrentState?.();
|
||||
const md = m?.currentMode;
|
||||
return s && s !== 'off' && s !== 'maintenance' && md !== 'maintenance';
|
||||
}).length;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -55,15 +67,16 @@ function getStatusBadge(mgc) {
|
||||
const md = m?.currentMode;
|
||||
return s && s !== 'off' && s !== 'maintenance' && md !== 'maintenance';
|
||||
});
|
||||
const status = available.length > 0 ? `${available.length} machine(s)` : 'No machines';
|
||||
let scalingSymbol;
|
||||
switch ((mgc.scaling || '').toLowerCase()) {
|
||||
case 'absolute': scalingSymbol = 'Ⓐ'; break;
|
||||
case 'normalized': scalingSymbol = 'Ⓝ'; break;
|
||||
default: scalingSymbol = mgc.mode || ''; break;
|
||||
}
|
||||
const text = ` ${mgc.mode || 'Unknown'} | ${scalingSymbol}: 💨=${Math.round(totalFlow)}/${Math.round(totalCapacity)} | ⚡=${Math.round(totalPower)} | ${status}`;
|
||||
return statusBadge.text(text, { fill: available.length > 0 ? 'green' : 'red', shape: 'dot' });
|
||||
const machineCount = Object.keys(mgc.machines || {}).length;
|
||||
const scaling = String(mgc.scaling || '').toLowerCase() === 'absolute' ? 'abs' : 'norm';
|
||||
const parts = [
|
||||
mgc.mode || '?',
|
||||
scaling,
|
||||
`Q=${Math.round(totalFlow)}/${Math.round(totalCapacity)} m³/h`,
|
||||
`P=${Math.round(totalPower)} kW`,
|
||||
`${available.length}/${machineCount}x`,
|
||||
];
|
||||
return statusBadge.compose(parts, { fill: available.length > 0 ? 'green' : (machineCount > 0 ? 'yellow' : 'grey'), shape: 'dot' });
|
||||
}
|
||||
|
||||
module.exports = { getOutput, getStatusBadge };
|
||||
|
||||
@@ -12,8 +12,14 @@ class nodeClass extends BaseNodeAdapter {
|
||||
static tickInterval = null;
|
||||
static statusInterval = 1000;
|
||||
|
||||
buildDomainConfig() {
|
||||
return {};
|
||||
buildDomainConfig(uiConfig = {}) {
|
||||
// Schema shape is mode.current / scaling.current (the schema nests
|
||||
// value + allowedActions/allowedSources under `current`). Editor field
|
||||
// names are flat — bridge here.
|
||||
const out = {};
|
||||
if (uiConfig.mode) out.mode = { current: uiConfig.mode };
|
||||
if (uiConfig.scaling) out.scaling = { current: uiConfig.scaling };
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -260,8 +260,11 @@ class MachineGroup extends BaseDomain {
|
||||
demandQout = this.interpolation.interpolate_lin_single_point(demandQ, 0, 100, dt.flow.min, dt.flow.max);
|
||||
}
|
||||
|
||||
// Normalize for the switch — schema enum values use camelCase
|
||||
// (optimalControl, priorityControl) while legacy callers send
|
||||
// lowercase. Accept both rather than silently falling through.
|
||||
const ctx = { mgc: this };
|
||||
switch (this.mode) {
|
||||
switch (String(this.mode || '').toLowerCase()) {
|
||||
case 'prioritycontrol': await control.equalFlowControl(ctx, demandQout, powerCap, priorityList); break;
|
||||
case 'prioritypercentagecontrol':
|
||||
if (this.scaling !== 'normalized') { this.logger.warn('Priority percentage control needs normalized scaling.'); return; }
|
||||
|
||||
Reference in New Issue
Block a user