feat(diffuser): resolve supplier curves via assetResolver + wire asset menu

_loadSpecs() now calls loadCurve(model) instead of returning a hardcoded
literal. Default model 'gva-elastox-r' keeps the legacy GVA numbers; the
editor cascade (supplier → type → model → unit) lets users pick Jäger,
Aerostrip, or PIK/PRK once those curve files land in generalFunctions.

Editor changes:
- diffuser.js serves /diffuser/menu.js + /diffuser/configData.js
- diffuser.html loads the shared MenuManager scripts, includes
  asset-fields-placeholder + logger-fields-placeholder, and runs the
  shared init/save lifecycle.
- Density field re-labelled "Bottom coverage [%]" — semantics were
  always meant to be % surface-area coverage; "elements per m²" was a
  prior mis-conversion. Default flipped 2.4 → 15 (typical fine-bubble).
- New defaults: model, unit, assetTagNumber.

specificClass:
- buildDomainConfig now forwards uiConfig.model/unit/assetTagNumber
  under config.asset.* so _loadSpecs can resolve it.
- _loadSpecs walks config.asset.model || config.model || DEFAULT, falls
  through to GVA on a missing curve file (with a clear error if neither
  resolves to a usable otr_curve + p_curve).

All 8 unit + structure tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
znetsixe
2026-05-12 17:11:50 +02:00
parent 6372bdc926
commit 2c5704b5c0
4 changed files with 115 additions and 25 deletions

View File

@@ -14,11 +14,19 @@ class nodeClass extends BaseNodeAdapter {
buildDomainConfig(uiConfig) {
const n = (v, fb) => (Number.isFinite(Number(v)) ? Number(v) : fb);
const s = (v, fb) => (typeof v === 'string' && v.length ? v : fb);
return {
asset: {
model: s(uiConfig.model, 'gva-elastox-r'),
assetTagNumber: s(uiConfig.assetTagNumber, ''),
},
general: {
unit: s(uiConfig.unit, 'Nm3/h'),
},
diffuser: {
number: n(uiConfig.number, 1),
elements: n(uiConfig.i_elements, 1),
density: n(uiConfig.i_diff_density, 2.4),
density: n(uiConfig.i_diff_density, 15),
waterHeight: n(uiConfig.i_m_water, 0),
alfaFactor: n(uiConfig.alfaf, 0.7),
headerPressure: n(uiConfig.i_pressure, 0),

View File

@@ -1,6 +1,12 @@
'use strict';
const { BaseDomain, statusBadge, interpolation, gravity, convert } = require('generalFunctions');
const { BaseDomain, statusBadge, interpolation, gravity, convert, loadCurve } = require('generalFunctions');
// Default curve used when the node's asset model field is not set. Preserves
// the historical behaviour of the hardcoded _loadSpecs() (GVA ELASTOX-R at
// density 2.4 elements/m²) — the existing test suite calibrates against
// these numbers.
const DEFAULT_DIFFUSER_MODEL = 'gva-elastox-r';
class Diffuser extends BaseDomain {
static name = 'diffuser';
@@ -19,7 +25,7 @@ class Diffuser extends BaseDomain {
this.i_water_density = _num(d.waterDensity, 997);
this.i_alfa_factor = _num(d.alfaFactor, 0.7);
this.i_n_elements = _posInt(d.elements, 1);
this.i_diff_density = _num(d.density, 2.4);
this.i_diff_density = _num(d.density, 15);
this.i_m_water = _num(d.waterHeight, 0);
this.i_flow = 0;
this.zoneVolume = _num(d.zoneVolume, 0);
@@ -219,11 +225,27 @@ class Diffuser extends BaseDomain {
}
_loadSpecs() {
// Curve lookup id: prefer the asset-menu-saved field, fall back to the
// legacy GVA ELASTOX-R reference (same numbers as the previous inline
// _loadSpecs). If a configured id misses the registry, fall back too —
// a missing curve would otherwise crash the constructor in production.
const cfgModel =
this.config?.asset?.model ||
this.config?.model ||
DEFAULT_DIFFUSER_MODEL;
const raw = loadCurve(cfgModel) || loadCurve(DEFAULT_DIFFUSER_MODEL);
if (!raw || !raw.otr_curve || !raw.p_curve) {
throw new Error(
`diffuser: curve '${cfgModel}' is missing otr_curve/p_curve (registry has: ${Object.keys(raw || {}).join(',') || 'nothing'})`,
);
}
return {
supplier: 'GVA', type: 'ELASTOX-R',
supplier: raw._meta?.supplier || null,
type: raw._meta?.type || null,
model: raw._meta?.model || cfgModel,
units: { Nm3: { temp: 20, pressure: 1.01325, RH: 0 } },
otr_curve: { 2.4: { x: [2, 3, 4, 5, 6, 7, 8, 9, 10], y: [26, 25, 24, 23.5, 23, 22.75, 22.5, 22.25, 22] } },
p_curve: { 0: { x: [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], y: [40, 42.5, 45, 47.5, 50, 51.5, 53, 54.5, 56, 57.5, 59] } },
otr_curve: raw.otr_curve,
p_curve: raw.p_curve,
};
}
}