Files
generalFunctions/src/configs/rotatingMachine.json
znetsixe 0a4b52f517 feat(registry): AssetResolver + diffuser supplier curves (Jäger / Aerostrip / PIK / PRK)
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>
2026-05-12 17:12:13 +02:00

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."
}
}
}