From 2540d19b7635d8a34ec927800bf739f492862bfc Mon Sep 17 00:00:00 2001 From: znetsixe <73483679+znetsixe@users.noreply.github.com> Date: Thu, 24 Jul 2025 13:17:10 +0200 Subject: [PATCH] updates to accomodate valve node --- datasets/assetData/assetData.json | 56 ++++ datasets/assetData/curves/ECDV.json | 16 ++ index.js | 6 +- src/configs/valve.json | 387 +++++++++++++++++++++++++++ src/helper/childRegistrationUtils.js | 25 ++ src/state/movementManager.js | 1 + 6 files changed, 487 insertions(+), 4 deletions(-) create mode 100644 datasets/assetData/curves/ECDV.json create mode 100644 src/configs/valve.json diff --git a/datasets/assetData/assetData.json b/datasets/assetData/assetData.json index acd7d01..6df8a84 100644 --- a/datasets/assetData/assetData.json +++ b/datasets/assetData/assetData.json @@ -32,6 +32,32 @@ } ] }, + { + "name": "Flow", + "models": [ + { + "name": "VegaFlow 10", + "units": ["m³/h", "gpm", "l/min"] + }, + { + "name": "VegaFlow 20", + "units": ["m³/h", "gpm", "l/min"] + } + ] + }, + { + "name": "Level", + "models": [ + { + "name": "VegaLevel 10", + "units": ["m", "ft", "mm"] + }, + { + "name": "VegaLevel 20", + "units": ["m", "ft", "mm"] + } + ] + }, { "name": "Level", "models": [ @@ -68,6 +94,36 @@ ] } ] + }, + { + "name": "Binder Engineering", + "categories": [ + { + "name": "Valves", + "types": [ + { + "name": "Gate", + "models": [ + { + "id": "binder-valve-001", + "name": "ECDV", + "units": ["m³/h", "gpm", "l/min"] + } + ] + }, + { + "name": "Jet", + "models": [ + { + "id": "binder-valve-002", + "name": "JCV", + "units": ["m³/h", "gpm", "l/min"] + } + ] + } + ] + } + ] } ] } \ No newline at end of file diff --git a/datasets/assetData/curves/ECDV.json b/datasets/assetData/curves/ECDV.json new file mode 100644 index 0000000..895c38e --- /dev/null +++ b/datasets/assetData/curves/ECDV.json @@ -0,0 +1,16 @@ +{ + "1.204": { + "125": { + "x": [0,10,20,30,40,50,60,70,80,90,100], + "y": [0,18,50,95,150,216,337,564,882,1398,1870] + }, + "150": { + "x": [0,10,20,30,40,50,60,70,80,90,100], + "y": [0,25,73,138,217,314,490,818,1281,2029,2715] + }, + "400": { + "x": [0,10,20,30,40,50,60,70,80,90,100], + "y": [0,155,443,839,1322,1911,2982,4980,7795,12349,16524] + } + } +} \ No newline at end of file diff --git a/index.js b/index.js index 54bc8de..d542a17 100644 --- a/index.js +++ b/index.js @@ -12,12 +12,11 @@ const outputUtils = require('./src/helper/outputUtils.js'); const logger = require('./src/helper/logger.js'); const validation = require('./src/helper/validationUtils.js'); const configUtils = require('./src/helper/configUtils.js'); -const assertions = require('./src/helper/assertionUtils.js') // Domain-specific modules const { MeasurementContainer } = require('./src/measurements/index.js'); const configManager = require('./src/configs/index.js'); -const nrmse = require('./src/nrmse/errorMetrics.js'); +const nrmse = require('./src/nrmse/ErrorMetrics.js'); const state = require('./src/state/state.js'); const convert = require('./src/convert/index.js'); const MenuManager = require('./src/menu/index.js'); @@ -35,7 +34,6 @@ module.exports = { configUtils, logger, validation, - assertions, MeasurementContainer, nrmse, state, @@ -43,4 +41,4 @@ module.exports = { MenuManager, childRegistrationUtils, loadCurve -}; +}; \ No newline at end of file diff --git a/src/configs/valve.json b/src/configs/valve.json new file mode 100644 index 0000000..5ad3e0a --- /dev/null +++ b/src/configs/valve.json @@ -0,0 +1,387 @@ +{ + "general": { + "name": { + "default": "valve", + "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": "m3/h", + "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": "valve", + "rules": { + "type": "string", + "description": "Specified software type for this configuration." + } + }, + "role": { + "default": "controller", + "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." + } + } + }, + "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." + } + }, + "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." + } + } + } + } + }, + "supplier": { + "default": "Unknown", + "rules": { + "type": "string", + "description": "The supplier or manufacturer of the asset." + } + }, + "category": { + "default": "valve", + "rules": { + "type": "string", + "description": "A general classification of the asset tied to the specific software. This is not chosen from the asset dropdown menu." + } + }, + "type": { + "default": "gate", + "rules": { + "type": "string", + "description": "A more specific classification within 'type'. For example, 'centrifugal' for a centrifugal pump." + } + }, + "model": { + "default": "Unknown", + "rules": { + "type": "string", + "description": "A user-defined or manufacturer-defined model identifier for the asset." + } + }, + "unit": { + "default": "unitless", + "rules": { + "type": "string", + "description": "The unit of measurement for this asset (e.g., 'meters', 'seconds', 'unitless')." + } + }, + "accuracy": { + "default": null, + "rules": { + "type": "number", + "nullable": true, + "description": "The accuracy of the machine or sensor, typically as a percentage or absolute value." + } + }, + "valveCurve": { + "default": { + "1.204": { + "1": { + "x": [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100], + "y": [0, 18, 50, 95, 150, 216, 337, 564, 882, 1398, 1870] + } + } + }, + "rules": { + "type": "valveCurve", + "description": "the first parameter is kg (usually according to 1 normal cubic meter per hour acc. to din norm ) and the second parameter is the diameter in mm. The x values are the opening of the valve in percent and the y values are the KV values in m3/h. The KV value is the flow rate of water at a temperature of 20 degrees Celsius through the valve when it is fully open." + } + } + }, + "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." + }, + { + "value": "maintenance", + "description": "No active control from auto, virtual, or fysical sources." + } + ], + "description": "The operational mode of the machine." + } + }, + "allowedActions":{ + "default":{}, + "rules": { + "type": "object", + "schema":{ + "auto": { + "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in auto mode." + } + }, + "virtualControl": { + "default": ["statusCheck", "execMovement", "execSequence", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in virtualControl mode." + } + }, + "fysicalControl": { + "default": ["statusCheck", "emergencyStop"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in fysicalControl mode." + } + }, + "maintenance": { + "default": ["statusCheck"], + "rules": { + "type": "set", + "itemType": "string", + "description": "Actions allowed in maintenance 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." + } + } + } + }, + "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." + } + } + } + \ No newline at end of file diff --git a/src/helper/childRegistrationUtils.js b/src/helper/childRegistrationUtils.js index f9b9cef..bb6db1e 100644 --- a/src/helper/childRegistrationUtils.js +++ b/src/helper/childRegistrationUtils.js @@ -83,6 +83,11 @@ class ChildRegistrationUtils { this.connectValve(child); break; + case "machineGroup": + this.logger.debug(`Registering complete machineGroup child: ${id}`); + this.connectMachineGroup(child); + break; + case "actuator": this.logger.debug(`Registering linear actuator child: ${id}`); this.connectActuator(child,positionVsParent); @@ -184,6 +189,26 @@ class ChildRegistrationUtils { this.logger.info(`Valve ${valveId} registered successfully.`); } + connectMachineGroup(machineGroup) { + if (!machineGroup) { + this.logger.warn("Invalid machineGroup provided."); + return; + } + + try { + const machineGroupId = Object.keys(this.mainClass.machineGroups).length + 1; + this.mainClass.machineGroups[machineGroupId] = machineGroup; + } catch (error) { + this.logger.warn(`Skip machinegroup connnection: ${error.message}`); + } + + machineGroup.emitter.on("totalFlowChange", (data) => { + this.mainClass.logger.debug('Total flow change of machineGroup detected'); + this.mainClass.handleInput("parent", "totalFlowChange", data)}); //Geef nieuwe totale flow door aan valveGrouControl + + this.logger.info(`MachineGroup ${machineGroup.config.general.name} registered successfully.`); + } + connectActuator(actuator, positionVsParent) { if (!actuator) { this.logger.warn("Invalid actuator provided."); diff --git a/src/state/movementManager.js b/src/state/movementManager.js index 8688cd4..424c628 100644 --- a/src/state/movementManager.js +++ b/src/state/movementManager.js @@ -13,6 +13,7 @@ class movementManager { this.speed = speed; this.maxSpeed = maxSpeed; + console.log(`MovementManager: Initial speed=${this.speed}, maxSpeed=${maxSpeed}`); this.interval = interval; this.timeleft = 0; // timeleft of current movement