Compare commits

..

1 Commits

Author SHA1 Message Date
znetsixe
68ebe4ebce feat(valve): resolve supplier+type from asset registry, reject legacy asset fields
Mirrors the rotatingMachine cutover: assetResolver derives supplier/type/
units from the model id; nodeClass throws a clear "re-select model and
save" error if the saved node still carries denormalized supplier/
category/assetType strings. valve.html defaults trimmed accordingly.

14/14 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:12:47 +02:00
3 changed files with 36 additions and 7 deletions

View File

@@ -11,6 +11,8 @@ class nodeClass extends BaseNodeAdapter {
static statusInterval = 1000;
buildDomainConfig(uiConfig) {
_rejectLegacyAssetFields(uiConfig);
const flowUnit = _resolveUnit(uiConfig.unit, 'volumeFlowRate', 'm3/h');
const asNum = (v) => { const n = Number(v); return Number.isFinite(n) ? n : undefined; };
Valve._pendingExtras = {
@@ -29,7 +31,25 @@ class nodeClass extends BaseNodeAdapter {
gasChokedRatioLimit: asNum(uiConfig.gasChokedRatioLimit),
},
};
return { general: { unit: flowUnit }, asset: { unit: flowUnit } };
return {
general: { unit: flowUnit },
asset: { model: uiConfig.model || null, unit: flowUnit },
};
}
}
// See rotatingMachine/src/nodeClass.js for the rationale. Same cutover rule.
function _rejectLegacyAssetFields(uiConfig) {
const offenders = ['supplier', 'category', 'assetType'].filter((k) => {
const v = uiConfig[k];
return typeof v === 'string' && v.trim() !== '';
});
if (offenders.length > 0) {
throw new Error(
`valve: legacy asset field(s) [${offenders.join(', ')}] are saved on this node. ` +
`After the AssetResolver refactor these are derived from the model id. ` +
`Open the node in the editor, re-select the model, and save to migrate.`,
);
}
}

View File

@@ -5,7 +5,7 @@
// the logic; this file wires them together and preserves the public surface
// the test suite + parents (VGC, MGC, pumpingStation) depend on.
const { BaseDomain, UnitPolicy, state } = require('generalFunctions');
const { BaseDomain, UnitPolicy, state, assetResolver } = require('generalFunctions');
const { ValveHydraulicModel, normalizeServiceType } = require('./hydraulicModel');
const { FluidCompatibility, normalizeOptional } = require('./fluid/fluidCompatibility');
const { SupplierCurvePredictor } = require('./curve/supplierCurve');
@@ -67,6 +67,12 @@ class Valve extends BaseDomain {
});
this.model = this.config.asset?.model;
// Derived asset metadata (supplier, type, allowed units) — null if the
// model isn't in the registry. Valve tolerates a null model + inline
// configCurve, so we don't hard-fail here; the curve predictor logs.
this.assetMetadata = this.model
? assetResolver.resolveAssetMetadata('valve', this.model)
: null;
this.curvePredictor = new SupplierCurvePredictor({
logger: this.logger,
model: this.model,

View File

@@ -25,11 +25,11 @@
processOutputFormat: { value: "process" },
dbaseOutputFormat: { value: "influxdb" },
//define asset properties
// Asset identifier surface. supplier/category/assetType are derived
// at runtime via assetResolver.resolveAssetMetadata(model). Do NOT
// add them back here. See generalFunctions/src/registry/README.md.
uuid: { value: "" },
supplier: { value: "" },
category: { value: "" },
assetType: { value: "" },
assetTagNumber: { value: "" },
model: { value: "" },
unit: { value: "" },
@@ -54,7 +54,10 @@
icon: "font-awesome/fa-toggle-on",
label: function () {
return (this.positionIcon || "") + " " + (this.category ? this.category.slice(0, -1) : "Valve");
// No more `this.category` on the node — derive from the model if needed,
// else fall back to a generic name.
const stem = this.model ? this.model : "Valve";
return (this.positionIcon || "") + " " + stem;
},
oneditprepare: function() {