From 57f6ec9cde0eed06b65a9f0652cdd85e6cc73d8e Mon Sep 17 00:00:00 2001 From: znetsixe <73483679+znetsixe@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:45:32 +0200 Subject: [PATCH] fix for childregistration --- .../measurement/measurementConfig.json | 357 ------------------ measurement.html | 2 +- measurementOLD.js | 188 --------- src/nodeClass.js | 15 +- .../measurement.js => src/specificClass.js | 0 5 files changed, 10 insertions(+), 552 deletions(-) delete mode 100644 dependencies/measurement/measurementConfig.json delete mode 100644 measurementOLD.js rename dependencies/measurement/measurement.js => src/specificClass.js (100%) diff --git a/dependencies/measurement/measurementConfig.json b/dependencies/measurement/measurementConfig.json deleted file mode 100644 index 05b36bc..0000000 --- a/dependencies/measurement/measurementConfig.json +++ /dev/null @@ -1,357 +0,0 @@ -{ - "general": { - "name": { - "default": "Measurement Configuration", - "rules": { - "type": "string", - "description": "A human-readable name or label for this measurement configuration." - } - }, - "id": { - "default": null, - "rules": { - "type": "string", - "nullable": true, - "description": "A unique identifier for this configuration. If not provided, defaults to null." - } - }, - "unit": { - "default": "unitless", - "rules": { - "type": "string", - "description": "The unit of measurement 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": "measurement", - "rules": { - "type": "string", - "description": "Specified software type for this configuration." - } - }, - "role": { - "default": "Sensor", - "rules": { - "type": "string", - "description": "Indicates the role this configuration plays (e.g., sensor, controller, etc.)." - } - } - }, - "asset": { - "uuid": { - "default": null, - "rules": { - "type": "string", - "nullable": true, - "description": "Asset tag number which is 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": { - "x": 0, - "y": 0, - "z": 0 - }, - "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." - } - }, - "type": { - "default": "sensor", - "rules": { - "type": "enum", - "values": [ - { - "value": "sensor", - "description": "A device that detects or measures a physical property and responds to it (e.g. temperature sensor)." - } - ] - } - }, - "subType": { - "default": "pressure", - "rules": { - "type": "string", - "description": "A more specific classification within 'type'. For example, 'pressure' for a pressure sensor." - } - }, - "model": { - "default": "Unknown", - "rules": { - "type": "string", - "description": "A user-defined or manufacturer-defined model identifier for the asset." - } - }, - "accuracy": { - "default": null, - "rules": { - "type": "number", - "nullable": true, - "description": "The accuracy of the sensor, typically represented as a percentage or absolute value." - } - }, - "repeatability": { - "default": null, - "rules": { - "type": "number", - "nullable": true, - "description": "The repeatability of the sensor, typically represented as a percentage or absolute value." - } - } - }, - "scaling": { - "enabled": { - "default": false, - "rules": { - "type": "boolean", - "description": "Indicates whether input scaling is active. If true, input values will be scaled according to the parameters below." - } - }, - "inputMin": { - "default": 0, - "rules": { - "type": "number", - "description": "The minimum expected input value before scaling." - } - }, - "inputMax": { - "default": 1, - "rules": { - "type": "number", - "description": "The maximum expected input value before scaling." - } - }, - "absMin": { - "default": 50, - "rules": { - "type": "number", - "description": "The absolute minimum value that can be read or displayed after scaling." - } - }, - "absMax": { - "default": 100, - "rules": { - "type": "number", - "description": "The absolute maximum value that can be read or displayed after scaling." - } - }, - "offset": { - "default": 0, - "rules": { - "type": "number", - "description": "A constant offset to apply to the scaled output (e.g., to calibrate zero-points)." - } - } - }, - "smoothing": { - "smoothWindow": { - "default": 10, - "rules": { - "type": "number", - "min": 1, - "description": "Determines the size of the data window (number of samples) used for smoothing operations." - } - }, - "smoothMethod": { - "default": "mean", - "rules": { - "type": "enum", - "values": [ - { - "value": "none", - "description": "No smoothing is applied; raw data is passed through." - }, - { - "value": "mean", - "description": "Calculates the simple arithmetic mean (average) of the data points in a window." - }, - { - "value": "min", - "description": "Selects the smallest (minimum) value among the data points in a window." - }, - { - "value": "max", - "description": "Selects the largest (maximum) value among the data points in a window." - }, - { - "value": "sd", - "description": "Computes the standard deviation to measure the variation or spread of the data." - }, - { - "value": "lowPass", - "description": "Filters out high-frequency components, allowing only lower frequencies to pass." - }, - { - "value": "highPass", - "description": "Filters out low-frequency components, allowing only higher frequencies to pass." - }, - { - "value": "weightedMovingAverage", - "description": "Applies varying weights to each data point in a window before averaging." - }, - { - "value": "bandPass", - "description": "Filters the signal to allow only frequencies within a specific range to pass." - }, - { - "value": "median", - "description": "Selects the median (middle) value in a window, minimizing the effect of outliers." - }, - { - "value": "kalman", - "description": "Applies a Kalman filter to combine noisy measurements over time for more accurate estimates." - }, - { - "value": "savitzkyGolay", - "description": "Uses a polynomial smoothing filter on a moving window, which can also provide derivative estimates." - } - ] - } - } - }, - "simulation": { - "enabled": { - "default": false, - "rules": { - "type": "boolean", - "description": "If true, the system operates in simulation mode, generating simulated values instead of using real inputs." - } - }, - "safeCalibrationTime": { - "default": 100, - "rules": { - "type": "number", - "min": 100, - "description": "Time to wait before finalizing calibration in simulation mode (in milliseconds or appropriate unit)." - } - } - }, - "interpolation": { - "percentMin": { - "default": 0, - "rules": { - "type": "number", - "min": 0, - "description": "Minimum percentage for interpolation or data scaling operations." - } - }, - "percentMax": { - "default": 100, - "rules": { - "type": "number", - "max": 100, - "description": "Maximum percentage for interpolation or data scaling operations." - } - } - }, - "outlierDetection": { - "enabled": { - "default": false, - "rules": { - "type": "boolean", - "description": "Indicates whether outlier detection is enabled. If true, outliers will be identified and handled according to the method specified." - } - }, - "method": { - "default": "zScore", - "rules": { - "type": "enum", - "values": [ - { - "value": "zScore", - "description": "Uses the Z-score method to identify outliers based on standard deviations from the mean." - }, - { - "value": "iqr", - "description": "Uses the Interquartile Range (IQR) method to identify outliers based on the spread of the middle 50% of the data." - }, - { - "value": "modifiedZScore", - "description": "Uses a modified Z-score method that is more robust to small sample sizes." - } - ] - } - }, - "threshold": { - "default": 3, - "rules": { - "type": "number", - "description": "The threshold value used by the selected outlier detection method. For example, a Z-score threshold of 3.0." - } - } - } -} \ No newline at end of file diff --git a/measurement.html b/measurement.html index 1f0ed73..3050bb6 100644 --- a/measurement.html +++ b/measurement.html @@ -34,7 +34,7 @@ logLevel: { value: "error" }, //physicalAspect - physicalAspect: { value: "" }, + positionVsParent: { value: "" }, }, diff --git a/measurementOLD.js b/measurementOLD.js deleted file mode 100644 index eef57e3..0000000 --- a/measurementOLD.js +++ /dev/null @@ -1,188 +0,0 @@ -console.log('Loading measurement.js module...'); -// load helper modules from the generalFunctions folder -const { outputUtils, MenuManager, configManager } = require('generalFunctions'); - -//internal node-red node dependencies this is the class that will handle the measurement -const Measurement = require("./dependencies/measurement/measurement"); -console.log('All dependencies loaded successfully'); -const nameOfNode = "measurement"; - -module.exports = function (RED) { - //load the config manager to get the default configs - this.configManager = new configManager(); - this.defaultConfig = this.configManager.getConfig('measurement'); - - function measurement(config) { - //create node - RED.nodes.createNode(this, config); - //call this => node so whenver you want to call a node function type node and the function behind it - var node = this; - - try{ - //load user defined config in the node-red UI - const mConfig={ - general: { - name: config.name, - id: node.id, - unit: config.unit, - logging:{ - logLevel: config.logLevel, - enabled: config.enableLog, - }, - }, - asset: { - tagCode: config.assetTagCode, - supplier: config.supplier, - subType: config.subType, - model: config.model, - }, - scaling:{ - enabled: config.scaling, - inputMin: config.i_min, - inputMax: config.i_max, - absMin: config.o_min, - absMax: config.o_max, - offset: config.i_offset - }, - smoothing: { - smoothWindow: config.count, - smoothMethod: config.smooth_method, - }, - simulation: { - enabled: config.simulator, - }, - } - - //make new measurement on creation to work with. - const m = new Measurement(mConfig); - - // put m on node memory as source - node.source = m; - - //load output utils - const output = new outputUtils(); - - function updateNodeStatus(val) { - //display status - node.status({ fill: "green", shape: "dot", text: val + " " + mConfig.general.unit }); - } - - //Update status only on event change - m.emitter.on('mAbs', (val) => { - updateNodeStatus(val); - }); - - //never ending functions - function tick(){ - - //kick class ticks for time move - m.tick(); - - //get output - const classOutput = m.getOutput(); - const dbOutput = output.formatMsg(classOutput, m.config, "influxdb"); - const pOutput = output.formatMsg(classOutput, m.config, "process"); - - //only send output on values that changed - let msgs = []; - msgs[0] = pOutput; - msgs[1] = dbOutput; - - node.send(msgs); - - } - - // register child on first output this timeout is needed because of node - red stuff - setTimeout( - () => { - - /*---execute code on first start----*/ - let msgs = []; - - msgs[2] = { topic : "registerChild" , payload: node.id, positionVsParent: "upstream" }; - msgs[3] = { topic : "registerChild" , payload: node.id, positionVsParent: "downstream" }; - - //send msg - this.send(msgs); - }, - 100 - ); - - //declare refresh interval internal node - setTimeout( - () => { - /*---execute code on first start----*/ - this.intervalId = setInterval(function(){ tick() },1000) - }, - 1000 - ); - - //-------------------------------------------------------------------->>what to do on input - node.on("input", function (msg,send,done) { - - if(msg.topic == "simulator" ){ - m.toggleSimulation(); - } - - if(msg.topic == "outlierDetection" ){ - m.toggleOutlierDetection(); - } - - if(msg.topic == "calibrate" ){ - m.calibrate(); - } - - if(msg.topic == "measurement" && typeof msg.payload == "number"){ - //feed input into the measurement node and calculate output - m.inputValue = parseFloat(msg.payload); - } - - done(); - - }); - - // tidy up any async code here - shutdown connections and so on. - node.on('close', function(done) { - if (node.intervalId) clearTimeout(node.intervalId); - if (node.tickInterval) clearInterval(node.tickInterval); - if (done) done(); - }); - - }catch(e){ - console.log(e); - } - } - - RED.nodes.registerType("measurement", measurement); - - - const menuManager = new MenuManager(); - - // Register the measurement menu handler - RED.httpAdmin.get("/measurement/menu.js", (req, res) => { - try { - const script = menuManager.createEndpoint('measurement', ['asset']); - res.set('Content-Type', 'application/javascript').send(script); - } catch (error) { - console.error("Error creating measurement menu script:", error); - res.status(500).send(`// Failed to generate menu data: ${error.message}`); - } - }); - - //register the measurement specific configs so the browser can access them - RED.httpAdmin.get("/measurement/configData", (req, res) => { - // load the config data from the JSON file and send it - try { - const configData = require('./dependencies/measurement/measurementConfig.json'); - res.json(configData); - } catch (error) { - console.error("Error loading measurement config data:", error); - res.status(500).send(`// Failed to load config data: ${error.message}`); - } - }); - - // Register the measurement node and its menu data endpoint - console.log('Measurement node and its menu data endpoint are registered.'); - - -}; \ No newline at end of file diff --git a/src/nodeClass.js b/src/nodeClass.js index cfb6205..f048065 100644 --- a/src/nodeClass.js +++ b/src/nodeClass.js @@ -5,7 +5,7 @@ * This allows us to keep the Node-RED node clean and focused on wiring up the UI and event handlers. */ const { outputUtils, configManager } = require('generalFunctions'); -const Measurement = require("../dependencies/measurement/measurement"); +const Measurement = require("./specificClass"); /** * Class representing a Measurement Node-RED node. @@ -23,7 +23,7 @@ class MeasurementNode { this.RED = RED; // Load default & UI config - this._loadConfig(uiConfig); + this._loadConfig(uiConfig,this.node); // Instantiate core Measurement class this._setupMeasurementClass(); @@ -40,15 +40,16 @@ class MeasurementNode { * Load and merge default config with user-defined settings. * @param {object} uiConfig - Raw config from Node-RED UI. */ - _loadConfig(uiConfig) { + _loadConfig(uiConfig,node) { const cfgMgr = new configManager(); this.defaultConfig = cfgMgr.getConfig("measurement"); + console.log( uiConfig.positionVsParent); // Merge UI config over defaults this.config = { general: { name: uiConfig.name, - id: uiConfig.id, //need to add this later use node.uuid from a single project file thats unique per location + node-red environment + node + id: node.id, //need to add this later use node.uuid from a single project file thats unique per location + node-red environment + node unit: uiConfig.unit, // add converter options later to convert to default units (need like a model that defines this which units we are going to use and then conver to those standards) logging: { enabled: uiConfig.enableLog, @@ -78,7 +79,9 @@ class MeasurementNode { simulation: { enabled: uiConfig.simulator }, - positionVsParent: uiConfig.position || 'atEquipment', // default to 'atEquipment' if not set + functionality: { + positionVsParent: uiConfig.positionVsParent || 'atEquipment', // Default to 'atEquipment' if not specified + } }; // Utility for formatting outputs @@ -110,7 +113,7 @@ class MeasurementNode { this.node.send([ null, null, - { topic: 'registerChild', payload: this.id, positionVsParent: this.config.functionality.positionVsParent } + { topic: 'registerChild', payload: this.config.general.id, positionVsParent: this.config?.functionality?.positionVsParent || 'atEquipment' }, ]); }, 100); } diff --git a/dependencies/measurement/measurement.js b/src/specificClass.js similarity index 100% rename from dependencies/measurement/measurement.js rename to src/specificClass.js