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>
This commit is contained in:
@@ -11,6 +11,8 @@ class nodeClass extends BaseNodeAdapter {
|
|||||||
static statusInterval = 1000;
|
static statusInterval = 1000;
|
||||||
|
|
||||||
buildDomainConfig(uiConfig) {
|
buildDomainConfig(uiConfig) {
|
||||||
|
_rejectLegacyAssetFields(uiConfig);
|
||||||
|
|
||||||
const flowUnit = _resolveUnit(uiConfig.unit, 'volumeFlowRate', 'm3/h');
|
const flowUnit = _resolveUnit(uiConfig.unit, 'volumeFlowRate', 'm3/h');
|
||||||
const asNum = (v) => { const n = Number(v); return Number.isFinite(n) ? n : undefined; };
|
const asNum = (v) => { const n = Number(v); return Number.isFinite(n) ? n : undefined; };
|
||||||
Valve._pendingExtras = {
|
Valve._pendingExtras = {
|
||||||
@@ -29,7 +31,25 @@ class nodeClass extends BaseNodeAdapter {
|
|||||||
gasChokedRatioLimit: asNum(uiConfig.gasChokedRatioLimit),
|
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.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
// the logic; this file wires them together and preserves the public surface
|
// the logic; this file wires them together and preserves the public surface
|
||||||
// the test suite + parents (VGC, MGC, pumpingStation) depend on.
|
// 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 { ValveHydraulicModel, normalizeServiceType } = require('./hydraulicModel');
|
||||||
const { FluidCompatibility, normalizeOptional } = require('./fluid/fluidCompatibility');
|
const { FluidCompatibility, normalizeOptional } = require('./fluid/fluidCompatibility');
|
||||||
const { SupplierCurvePredictor } = require('./curve/supplierCurve');
|
const { SupplierCurvePredictor } = require('./curve/supplierCurve');
|
||||||
@@ -67,6 +67,12 @@ class Valve extends BaseDomain {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.model = this.config.asset?.model;
|
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({
|
this.curvePredictor = new SupplierCurvePredictor({
|
||||||
logger: this.logger,
|
logger: this.logger,
|
||||||
model: this.model,
|
model: this.model,
|
||||||
|
|||||||
13
valve.html
13
valve.html
@@ -25,11 +25,11 @@
|
|||||||
processOutputFormat: { value: "process" },
|
processOutputFormat: { value: "process" },
|
||||||
dbaseOutputFormat: { value: "influxdb" },
|
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: "" },
|
uuid: { value: "" },
|
||||||
supplier: { value: "" },
|
assetTagNumber: { value: "" },
|
||||||
category: { value: "" },
|
|
||||||
assetType: { value: "" },
|
|
||||||
model: { value: "" },
|
model: { value: "" },
|
||||||
unit: { value: "" },
|
unit: { value: "" },
|
||||||
|
|
||||||
@@ -54,7 +54,10 @@
|
|||||||
icon: "font-awesome/fa-toggle-on",
|
icon: "font-awesome/fa-toggle-on",
|
||||||
|
|
||||||
label: function () {
|
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() {
|
oneditprepare: function() {
|
||||||
|
|||||||
Reference in New Issue
Block a user