Two related changes bundled together because the diffuser curve files
only make sense once the registry namespace they live in exists.
src/registry — new asset-metadata resolver:
- AssetResolver with synchronous resolve(namespace, id) + lazy cache,
async refresh() for future remote pulls.
- FileBackend (per-id or single-file layouts, case-insensitive) and a
stub HttpBackend (disabled unless EVOLV_ASSET_REMOTE=1).
- Namespaces: curves, menu, monsterSamples, monsterSpecs, units. Menu
namespace re-keys by inner softwareType + filename so editors that
pass either string resolve to the same tree.
- README explains how to add a namespace.
- AssetCategoryManager (datasets/assetData/index.js) becomes a thin
facade over the resolver so existing consumers don't move.
- 246/246 tests pass — including the 39-test registry suite.
datasets/assetData — file moves + new diffuser data:
- modelData/*.json deleted; curves/*.json is the canonical home.
- New diffuser.json menu tree with GVA, Jäger, Aquaconsult/Entec,
PIK/PRK suppliers.
- gva-elastox-r.json migrated from the inline _loadSpecs hardcode,
re-tagged coverageBasis="bottom-coverage-pct" (the legacy 2.4
elements/m² was a prior mis-conversion; we can't recover the
original % so it's a single-point curve under key "0").
- jaeger-jetflex-td-65-2-g-epdm-1000.json — extracted from the Jäger
EPDM-1000mm SSOTE/DWP chart on the data sheet (vector-PDF read).
SSOTE 8.20→6.40 %/m, DWP 25→48 mbar across Q 2-12 Nm³/h. Single
coverage (vendor doesn't state test conditions).
- aerostrip-phoenix.json — 4-coverage SOTE family at 4.75 m water
depth (DD 5/10/15/20 %, flux 10-70 Nm³/h·m²) from the Entec/de
Winter 2023-11-22 dataset; DWP curve from the 21 % @ 4.05 m chart.
- pik300.json / prk300.json — 5-coverage SOTE + SSOTR (DD 5-25 %)
with split DWP per model variant, water depth ≈ 4.0 m inferred from
the SOTE↔SSOTR ratio in the source spreadsheet.
src/configs/diffuser.json:
- New asset.{model, assetTagNumber} block so the editor's selected
model id survives validation.
- diffuser.density description corrected to "Bottom coverage [%]";
default 2.4 → 15 (typical fine-bubble install).
src/configs/{rotatingMachine,valve}.json: small alignment edits that
came with the registry phase.
src/menu/asset.js + src/menu/aquonSamples.js: rewritten as facades
over assetResolver, keeping the editor-side cascade behaviour intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
493 lines
16 KiB
JSON
493 lines
16 KiB
JSON
{
|
|
"general": {
|
|
"name": {
|
|
"default": "Rotating Machine",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "A human-readable name or label for this machine configuration."
|
|
}
|
|
},
|
|
"id": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "A unique identifier for this configuration. If not provided, defaults to null."
|
|
}
|
|
},
|
|
"unit": {
|
|
"default": "l/s",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "The default measurement unit for this configuration (e.g., 'meters', 'seconds', 'unitless')."
|
|
}
|
|
},
|
|
"logging": {
|
|
"logLevel": {
|
|
"default": "info",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{
|
|
"value": "debug",
|
|
"description": "Log messages are printed for debugging purposes."
|
|
},
|
|
{
|
|
"value": "info",
|
|
"description": "Informational messages are printed."
|
|
},
|
|
{
|
|
"value": "warn",
|
|
"description": "Warning messages are printed."
|
|
},
|
|
{
|
|
"value": "error",
|
|
"description": "Error messages are printed."
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"enabled": {
|
|
"default": true,
|
|
"rules": {
|
|
"type": "boolean",
|
|
"description": "Indicates whether logging is active. If true, log messages will be generated."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"functionality": {
|
|
"softwareType": {
|
|
"default": "rotatingmachine",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Specified software type for this configuration."
|
|
}
|
|
},
|
|
"role": {
|
|
"default": "RotationalDeviceController",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Indicates the role this configuration plays within the system."
|
|
}
|
|
},
|
|
"positionVsParent":{
|
|
"default":"atEquipment",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{
|
|
"value": "atEquipment",
|
|
"description": "The node is connected at the equipment level and is responsible for controlling or monitoring the equipment as a whole."
|
|
},
|
|
{
|
|
"value": "upstream",
|
|
"description": "The node is connected in a downstream position, indicating it is responsible for monitoring or controlling processes that occur after the equipment's operation, such as product flow or output."
|
|
},
|
|
{
|
|
"value": "downstream",
|
|
"description": "The node is connected in an upstream position, indicating it is responsible for monitoring or controlling processes that occur before the equipment's operation, such as input flow or supply."
|
|
}
|
|
],
|
|
"description": "Defines the position of the measurement relative to its parent equipment or system."
|
|
}
|
|
},
|
|
"distance": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "number",
|
|
"nullable": true,
|
|
"description": "Optional spatial offset from the parent equipment reference. Populated from the editor when hasDistance is enabled; null otherwise."
|
|
}
|
|
},
|
|
"distanceUnit": {
|
|
"default": "m",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Unit for the functionality.distance offset (e.g. 'm', 'cm')."
|
|
}
|
|
},
|
|
"distanceDescription": {
|
|
"default": "",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Free-text description of what the distance offset represents (e.g. 'cable length from control panel to motor')."
|
|
}
|
|
}
|
|
},
|
|
"output": {
|
|
"process": {
|
|
"default": "process",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{ "value": "process", "description": "Delta-compressed process message (default)." },
|
|
{ "value": "json", "description": "Raw JSON payload." },
|
|
{ "value": "csv", "description": "CSV-formatted payload." }
|
|
],
|
|
"description": "Format of the process payload emitted on output port 0."
|
|
}
|
|
},
|
|
"dbase": {
|
|
"default": "influxdb",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{ "value": "influxdb", "description": "InfluxDB line-protocol payload (default)." },
|
|
{ "value": "json", "description": "Raw JSON payload." },
|
|
{ "value": "csv", "description": "CSV-formatted payload." }
|
|
],
|
|
"description": "Format of the telemetry payload emitted on output port 1."
|
|
}
|
|
}
|
|
},
|
|
"asset": {
|
|
"uuid": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "A universally unique identifier for this asset. May be null if not assigned."
|
|
}
|
|
},
|
|
"tagCode":{
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Asset tag code which is a unique identifier for this asset. May be null if not assigned."
|
|
}
|
|
},
|
|
"tagNumber": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Optional asset tag number for legacy integrations."
|
|
}
|
|
},
|
|
"geoLocation": {
|
|
"default": {},
|
|
"rules": {
|
|
"type": "object",
|
|
"description": "An object representing the asset's physical coordinates or location.",
|
|
"schema": {
|
|
"x": {
|
|
"default": 0,
|
|
"rules": {
|
|
"type": "number",
|
|
"description": "X coordinate of the asset's location."
|
|
}
|
|
},
|
|
"y": {
|
|
"default": 0,
|
|
"rules": {
|
|
"type": "number",
|
|
"description": "Y coordinate of the asset's location."
|
|
}
|
|
},
|
|
"z": {
|
|
"default": 0,
|
|
"rules": {
|
|
"type": "number",
|
|
"description": "Z coordinate of the asset's location."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"model": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Product model id (e.g. 'hidrostal-H05K-S03R'). Required at startup: the node looks the curve up via assetResolver.resolve('curves', model). Supplier/type/units are derived from the asset registry (assetResolver.resolveAssetMetadata) — do NOT save them on the node."
|
|
}
|
|
},
|
|
"unit": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "string",
|
|
"nullable": true,
|
|
"description": "Deployment unit chosen by the user (e.g. 'm3/h'). Must appear in the registry's model.units list for this model. Validated at startup."
|
|
}
|
|
},
|
|
"curveUnits": {
|
|
"default": {
|
|
"pressure": "mbar",
|
|
"flow": "m3/h",
|
|
"power": "kW",
|
|
"control": "%"
|
|
},
|
|
"rules": {
|
|
"type": "object",
|
|
"schema": {
|
|
"pressure": {
|
|
"default": "mbar",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Pressure unit used on the machine curve dimension axis."
|
|
}
|
|
},
|
|
"flow": {
|
|
"default": "m3/h",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Flow unit used in the machine curve output (nq.y)."
|
|
}
|
|
},
|
|
"power": {
|
|
"default": "kW",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Power unit used in the machine curve output (np.y)."
|
|
}
|
|
},
|
|
"control": {
|
|
"default": "%",
|
|
"rules": {
|
|
"type": "string",
|
|
"description": "Control axis unit used in the curve x-dimension."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"accuracy": {
|
|
"default": null,
|
|
"rules": {
|
|
"type": "number",
|
|
"nullable": true,
|
|
"description": "The accuracy of the machine or sensor, typically as a percentage or absolute value."
|
|
}
|
|
},
|
|
"machineCurve": {
|
|
"default": {
|
|
"nq": {},
|
|
"np": {}
|
|
},
|
|
"rules": {
|
|
"type": "machineCurve",
|
|
"description": "All machine curves must have a 'nq' and 'np' curve. nq stands for the flow curve, np stands for the power curve. Together they form the efficiency curve."
|
|
}
|
|
}
|
|
},
|
|
"mode": {
|
|
"current": {
|
|
"default": "auto",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{
|
|
"value": "auto",
|
|
"description": "Machine accepts setpoints from a parent controller and runs autonomously."
|
|
},
|
|
{
|
|
"value": "virtualControl",
|
|
"description": "Controlled via GUI setpoints; ignores parent commands."
|
|
},
|
|
{
|
|
"value": "fysicalControl",
|
|
"description": "Controlled via physical buttons or switches; ignores external automated commands."
|
|
}
|
|
],
|
|
"description": "The operational mode of the machine."
|
|
}
|
|
},
|
|
"allowedActions":{
|
|
"default":{},
|
|
"rules": {
|
|
"type": "object",
|
|
"schema":{
|
|
"auto": {
|
|
"default": [
|
|
"statuscheck",
|
|
"execmovement",
|
|
"execsequence",
|
|
"flowmovement",
|
|
"emergencystop",
|
|
"entermaintenance"
|
|
],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Actions allowed in auto mode."
|
|
}
|
|
},
|
|
"virtualControl": {
|
|
"default": [
|
|
"statuscheck",
|
|
"execmovement",
|
|
"flowmovement",
|
|
"execsequence",
|
|
"emergencystop",
|
|
"exitmaintenance"
|
|
],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Actions allowed in virtualControl mode."
|
|
}
|
|
},
|
|
"fysicalControl": {
|
|
"default": [
|
|
"statuscheck",
|
|
"emergencystop",
|
|
"entermaintenance",
|
|
"exitmaintenance"
|
|
],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Actions allowed in fysicalControl mode."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"description": "Information about valid command sources recognized by the machine."
|
|
},
|
|
"allowedSources":{
|
|
"default": {},
|
|
"rules": {
|
|
"type": "object",
|
|
"schema":{
|
|
"auto": {
|
|
"default": ["parent", "GUI", "fysical"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sources allowed in auto mode."
|
|
}
|
|
},
|
|
"virtualControl": {
|
|
"default": ["GUI", "fysical"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sources allowed in virtualControl mode."
|
|
}
|
|
},
|
|
"fysicalControl": {
|
|
"default": ["fysical"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sources allowed in fysicalControl mode."
|
|
}
|
|
}
|
|
},
|
|
"description": "Information about valid command sources recognized by the machine."
|
|
}
|
|
}
|
|
},
|
|
"source": {
|
|
"default": "parent",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{
|
|
"value": "parent",
|
|
"description": "Commands are received from a parent controller."
|
|
},
|
|
{
|
|
"value": "GUI",
|
|
"description": "Commands are received from a graphical user interface."
|
|
},
|
|
{
|
|
"value": "fysical",
|
|
"description": "Commands are received from physical buttons or switches."
|
|
}
|
|
],
|
|
"description": "Information about valid command sources recognized by the machine."
|
|
}
|
|
},
|
|
"sequences":{
|
|
"default":{},
|
|
"rules": {
|
|
"type": "object",
|
|
"schema": {
|
|
"startup": {
|
|
"default": ["starting","warmingup","operational"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states for starting up the machine."
|
|
}
|
|
},
|
|
"shutdown": {
|
|
"default": ["stopping","coolingdown","idle"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states for shutting down the machine."
|
|
}
|
|
},
|
|
"emergencystop": {
|
|
"default": ["emergencystop","off"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states for an emergency stop."
|
|
}
|
|
},
|
|
"boot": {
|
|
"default": ["idle","starting","warmingup","operational"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states for booting up the machine."
|
|
}
|
|
},
|
|
"entermaintenance":{
|
|
"default": ["stopping","coolingdown","idle","maintenance"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states if the machine is running to put it in maintenance state"
|
|
}
|
|
},
|
|
"exitmaintenance":{
|
|
"default": ["off","idle"],
|
|
"rules": {
|
|
"type": "set",
|
|
"itemType": "string",
|
|
"description": "Sequence of states if the machine is running to put it in maintenance state"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"description": "Predefined sequences of states for the machine."
|
|
|
|
},
|
|
"calculationMode": {
|
|
"default": "medium",
|
|
"rules": {
|
|
"type": "enum",
|
|
"values": [
|
|
{
|
|
"value": "low",
|
|
"description": "Calculations run at fixed intervals (time-based)."
|
|
},
|
|
{
|
|
"value": "medium",
|
|
"description": "Calculations run when new setpoints arrive or measured changes occur (event-driven)."
|
|
},
|
|
{
|
|
"value": "high",
|
|
"description": "Calculations run on all event-driven info, including every movement."
|
|
}
|
|
],
|
|
"description": "The frequency at which calculations are performed."
|
|
}
|
|
},
|
|
"flowNumber": {
|
|
"default": 1,
|
|
"rules": {
|
|
"type": "number",
|
|
"nullable": false,
|
|
"description": "Defines which effluent flow of the parent node to handle."
|
|
}
|
|
}
|
|
}
|
|
|