Compare commits
1 Commits
1cfb36f604
...
c60aa40666
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c60aa40666 |
24
index.js
24
index.js
@@ -8,25 +8,27 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Core helper modules
|
// Core helper modules
|
||||||
const outputUtils = require('./src/helper/outputUtils.js');
|
const helper = require('./src/helper/index.js');
|
||||||
const logger = require('./src/helper/logger.js');
|
const {
|
||||||
const validation = require('./src/helper/validationUtils.js');
|
outputUtils,
|
||||||
const configUtils = require('./src/helper/configUtils.js');
|
logger,
|
||||||
const assertions = require('./src/helper/assertionUtils.js')
|
validation,
|
||||||
|
configUtils,
|
||||||
|
assertions,
|
||||||
|
childRegistrationUtils,
|
||||||
|
gravity,
|
||||||
|
} = helper;
|
||||||
const coolprop = require('./src/coolprop-node/src/index.js');
|
const coolprop = require('./src/coolprop-node/src/index.js');
|
||||||
const gravity = require('./src/helper/gravity.js')
|
|
||||||
const assetApiConfig = require('./src/configs/assetApiConfig.js');
|
const assetApiConfig = require('./src/configs/assetApiConfig.js');
|
||||||
|
|
||||||
// Domain-specific modules
|
// Domain-specific modules
|
||||||
const { MeasurementContainer } = require('./src/measurements/index.js');
|
const { MeasurementContainer } = require('./src/measurements/index.js');
|
||||||
const configManager = require('./src/configs/index.js');
|
const configManager = require('./src/configs/index.js');
|
||||||
const nrmse = require('./src/nrmse/errorMetrics.js');
|
const { nrmse } = require('./src/nrmse/index.js');
|
||||||
const state = require('./src/state/state.js');
|
const { state } = require('./src/state/index.js');
|
||||||
const convert = require('./src/convert/index.js');
|
const convert = require('./src/convert/index.js');
|
||||||
const MenuManager = require('./src/menu/index.js');
|
const MenuManager = require('./src/menu/index.js');
|
||||||
const predict = require('./src/predict/predict_class.js');
|
const { predict, interpolation } = require('./src/predict/index.js');
|
||||||
const interpolation = require('./src/predict/interpolation.js');
|
|
||||||
const childRegistrationUtils = require('./src/helper/childRegistrationUtils.js');
|
|
||||||
const { loadCurve } = require('./datasets/assetData/curves/index.js'); //deprecated replace with load model data
|
const { loadCurve } = require('./datasets/assetData/curves/index.js'); //deprecated replace with load model data
|
||||||
const { loadModel } = require('./datasets/assetData/modelData/index.js');
|
const { loadModel } = require('./datasets/assetData/modelData/index.js');
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,16 @@
|
|||||||
"./menuUtils": "./src/helper/menuUtils.js",
|
"./menuUtils": "./src/helper/menuUtils.js",
|
||||||
"./mathUtils": "./src/helper/mathUtils.js",
|
"./mathUtils": "./src/helper/mathUtils.js",
|
||||||
"./assetUtils": "./src/helper/assetUtils.js",
|
"./assetUtils": "./src/helper/assetUtils.js",
|
||||||
"./outputUtils": "./src/helper/outputUtils.js"
|
"./outputUtils": "./src/helper/outputUtils.js",
|
||||||
|
"./helper": "./src/helper/index.js",
|
||||||
|
"./state": "./src/state/index.js",
|
||||||
|
"./predict": "./src/predict/index.js",
|
||||||
|
"./nrmse": "./src/nrmse/index.js",
|
||||||
|
"./outliers": "./src/outliers/index.js"
|
||||||
},
|
},
|
||||||
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node test.js"
|
"test": "node --test test/*.test.js src/nrmse/errorMetric.test.js"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -6,8 +6,18 @@ class ChildRegistrationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async registerChild(child, positionVsParent, distance) {
|
async registerChild(child, positionVsParent, distance) {
|
||||||
const { softwareType } = child.config.functionality;
|
if (!child || typeof child !== 'object') {
|
||||||
const { name, id } = child.config.general;
|
this.logger?.warn('registerChild skipped: invalid child payload');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!child.config?.functionality || !child.config?.general) {
|
||||||
|
this.logger?.warn('registerChild skipped: missing child config/functionality/general');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const softwareType = child.config.functionality.softwareType;
|
||||||
|
const name = child.config.general.name || child.config.general.id || 'unknown';
|
||||||
|
const id = child.config.general.id || name;
|
||||||
|
|
||||||
this.logger.debug(`Registering child: ${name} (${id}) as ${softwareType} at ${positionVsParent}`);
|
this.logger.debug(`Registering child: ${name} (${id}) as ${softwareType} at ${positionVsParent}`);
|
||||||
|
|
||||||
@@ -43,19 +53,21 @@ class ChildRegistrationUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`✅ Child ${name} registered successfully`);
|
this.logger.info(`✅ Child ${name} registered successfully`);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_storeChild(child, softwareType) {
|
_storeChild(child, softwareType) {
|
||||||
// Maintain your existing structure
|
// Maintain your existing structure
|
||||||
if (!this.mainClass.child) this.mainClass.child = {};
|
if (!this.mainClass.child) this.mainClass.child = {};
|
||||||
if (!this.mainClass.child[softwareType]) this.mainClass.child[softwareType] = {};
|
const typeKey = softwareType || 'unknown';
|
||||||
|
if (!this.mainClass.child[typeKey]) this.mainClass.child[typeKey] = {};
|
||||||
|
|
||||||
const { category = "sensor" } = child.config.asset || {};
|
const { category = "sensor" } = child.config.asset || {};
|
||||||
if (!this.mainClass.child[softwareType][category]) {
|
if (!this.mainClass.child[typeKey][category]) {
|
||||||
this.mainClass.child[softwareType][category] = [];
|
this.mainClass.child[typeKey][category] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.mainClass.child[softwareType][category].push(child);
|
this.mainClass.child[typeKey][category].push(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEW: Utility methods for parent to use
|
// NEW: Utility methods for parent to use
|
||||||
|
|||||||
@@ -73,17 +73,25 @@ class ConfigUtils {
|
|||||||
return updatedConfig;
|
return updatedConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_isPlainObject(value) {
|
||||||
|
return Object.prototype.toString.call(value) === '[object Object]';
|
||||||
|
}
|
||||||
|
|
||||||
// loop through objects and merge them obj1 will be updated with obj2 values
|
// loop through objects and merge them obj1 will be updated with obj2 values
|
||||||
mergeObjects(obj1, obj2) {
|
mergeObjects(obj1, obj2) {
|
||||||
for (let key in obj2) {
|
for (let key in obj2) {
|
||||||
if (obj2.hasOwnProperty(key)) {
|
if (obj2.hasOwnProperty(key)) {
|
||||||
if (typeof obj2[key] === 'object') {
|
const nextValue = obj2[key];
|
||||||
if (!obj1[key]) {
|
|
||||||
|
if (Array.isArray(nextValue)) {
|
||||||
|
obj1[key] = [...nextValue];
|
||||||
|
} else if (this._isPlainObject(nextValue)) {
|
||||||
|
if (!this._isPlainObject(obj1[key])) {
|
||||||
obj1[key] = {};
|
obj1[key] = {};
|
||||||
}
|
}
|
||||||
this.mergeObjects(obj1[key], obj2[key]);
|
this.mergeObjects(obj1[key], nextValue);
|
||||||
} else {
|
} else {
|
||||||
obj1[key] = obj2[key];
|
obj1[key] = nextValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,102 @@ class EndpointUtils {
|
|||||||
* @param {string} nodeName the name of the node (used in the URL)
|
* @param {string} nodeName the name of the node (used in the URL)
|
||||||
* @param {object} customHelpers additional helper functions to inject
|
* @param {object} customHelpers additional helper functions to inject
|
||||||
*/
|
*/
|
||||||
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}) {
|
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}, options = {}) {
|
||||||
RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, (req, res) => {
|
const basePath = `/${nodeName}/resources`;
|
||||||
console.log(`Serving menuUtils.js for ${nodeName} node`);
|
|
||||||
res.set('Content-Type', 'application/javascript');
|
RED.httpAdmin.get(`${basePath}/menuUtilsData.json`, (req, res) => {
|
||||||
const browserCode = this.generateMenuUtilsCode(nodeName, customHelpers);
|
res.json(this.generateMenuUtilsData(nodeName, customHelpers, options));
|
||||||
res.send(browserCode);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
RED.httpAdmin.get(`${basePath}/menuUtils.legacy.js`, (req, res) => {
|
||||||
|
res.set('Content-Type', 'application/javascript');
|
||||||
|
res.send(this.generateLegacyMenuUtilsCode(nodeName, customHelpers));
|
||||||
|
});
|
||||||
|
|
||||||
|
RED.httpAdmin.get(`${basePath}/menuUtils.js`, (req, res) => {
|
||||||
|
res.set('Content-Type', 'application/javascript');
|
||||||
|
res.send(this.generateMenuUtilsBootstrap(nodeName));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMenuUtilsData(nodeName, customHelpers = {}, options = {}) {
|
||||||
|
const defaultHelpers = {
|
||||||
|
validateRequired: `function(value) {
|
||||||
|
return value != null && value.toString().trim() !== '';
|
||||||
|
}`,
|
||||||
|
formatDisplayValue: `function(value, unit) {
|
||||||
|
return \`${'${'}value} ${'${'}unit || ''}\`.trim();
|
||||||
|
}`,
|
||||||
|
validateScaling: `function(min, max) {
|
||||||
|
return !isNaN(min) && !isNaN(max) && Number(min) < Number(max);
|
||||||
|
}`,
|
||||||
|
validateUnit: `function(unit) {
|
||||||
|
return typeof unit === 'string' && unit.trim() !== '';
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
nodeName,
|
||||||
|
helpers: { ...defaultHelpers, ...customHelpers },
|
||||||
|
options: {
|
||||||
|
autoLoadLegacy: options.autoLoadLegacy !== false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMenuUtilsBootstrap(nodeName) {
|
||||||
|
return `
|
||||||
|
// Stable bootstrap for EVOLV menu utils (${nodeName})
|
||||||
|
(function() {
|
||||||
|
const nodeName = ${JSON.stringify(nodeName)};
|
||||||
|
const basePath = '/' + nodeName + '/resources';
|
||||||
|
|
||||||
|
window.EVOLV = window.EVOLV || {};
|
||||||
|
window.EVOLV.nodes = window.EVOLV.nodes || {};
|
||||||
|
window.EVOLV.nodes[nodeName] = window.EVOLV.nodes[nodeName] || {};
|
||||||
|
window.EVOLV.nodes[nodeName].utils = window.EVOLV.nodes[nodeName].utils || {};
|
||||||
|
|
||||||
|
function parseHelper(fnBody) {
|
||||||
|
try {
|
||||||
|
return (new Function('return (' + fnBody + ')'))();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[menuUtils] helper parse failed:', error);
|
||||||
|
return function() { return null; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLegacyIfNeeded(autoLoadLegacy) {
|
||||||
|
if (!autoLoadLegacy || typeof window.MenuUtils === 'function') return Promise.resolve();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = basePath + '/menuUtils.legacy.js';
|
||||||
|
script.onload = resolve;
|
||||||
|
script.onerror = reject;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(basePath + '/menuUtilsData.json')
|
||||||
|
.then(function(res) { return res.json(); })
|
||||||
|
.then(function(payload) {
|
||||||
|
const helperFns = {};
|
||||||
|
Object.entries(payload.helpers || {}).forEach(function(entry) {
|
||||||
|
helperFns[entry[0]] = parseHelper(entry[1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.EVOLV.nodes[nodeName].utils.helpers = helperFns;
|
||||||
|
return loadLegacyIfNeeded(payload.options && payload.options.autoLoadLegacy);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
if (typeof window.MenuUtils === 'function' && !window.EVOLV.nodes[nodeName].utils.menuUtils) {
|
||||||
|
window.EVOLV.nodes[nodeName].utils.menuUtils = new window.MenuUtils();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('[menuUtils] bootstrap failed for ' + nodeName, error);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,7 +122,7 @@ class EndpointUtils {
|
|||||||
* @param {object} customHelpers map of name: functionString pairs
|
* @param {object} customHelpers map of name: functionString pairs
|
||||||
* @returns {string} a JS snippet to run in the browser
|
* @returns {string} a JS snippet to run in the browser
|
||||||
*/
|
*/
|
||||||
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
generateLegacyMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||||
// Default helper implementations to expose alongside MenuUtils
|
// Default helper implementations to expose alongside MenuUtils
|
||||||
const defaultHelpers = {
|
const defaultHelpers = {
|
||||||
validateRequired: `function(value) {
|
validateRequired: `function(value) {
|
||||||
@@ -101,6 +190,11 @@ ${helpersCode}
|
|||||||
console.log('Loaded EVOLV.nodes.${nodeName}.utils.menuUtils');
|
console.log('Loaded EVOLV.nodes.${nodeName}.utils.menuUtils');
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backward-compatible alias.
|
||||||
|
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||||
|
return this.generateLegacyMenuUtilsCode(nodeName, customHelpers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = EndpointUtils;
|
module.exports = EndpointUtils;
|
||||||
|
|||||||
25
src/helper/index.js
Normal file
25
src/helper/index.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
const assertions = require('./assertionUtils.js');
|
||||||
|
const assetUtils = require('./assetUtils.js');
|
||||||
|
const childRegistrationUtils = require('./childRegistrationUtils.js');
|
||||||
|
const configUtils = require('./configUtils.js');
|
||||||
|
const endpointUtils = require('./endpointUtils.js');
|
||||||
|
const gravity = require('./gravity.js');
|
||||||
|
const logger = require('./logger.js');
|
||||||
|
const menuUtils = require('./menuUtils.js');
|
||||||
|
const nodeTemplates = require('./nodeTemplates.js');
|
||||||
|
const outputUtils = require('./outputUtils.js');
|
||||||
|
const validation = require('./validationUtils.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
assertions,
|
||||||
|
assetUtils,
|
||||||
|
childRegistrationUtils,
|
||||||
|
configUtils,
|
||||||
|
endpointUtils,
|
||||||
|
gravity,
|
||||||
|
logger,
|
||||||
|
menuUtils,
|
||||||
|
nodeTemplates,
|
||||||
|
outputUtils,
|
||||||
|
validation,
|
||||||
|
};
|
||||||
@@ -480,17 +480,26 @@ generateHtml(htmlElement, options, savedValue) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}) {
|
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}, options = {}) {
|
||||||
RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, function(req, res) {
|
const basePath = `/${nodeName}/resources`;
|
||||||
console.log(`Serving menuUtils.js for ${nodeName} node`);
|
|
||||||
res.set('Content-Type', 'application/javascript');
|
|
||||||
|
|
||||||
const browserCode = this.generateMenuUtilsCode(nodeName, customHelpers);
|
RED.httpAdmin.get(`${basePath}/menuUtilsData.json`, function(req, res) {
|
||||||
|
res.json(this.generateMenuUtilsData(nodeName, customHelpers, options));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
RED.httpAdmin.get(`${basePath}/menuUtils.legacy.js`, function(req, res) {
|
||||||
|
res.set('Content-Type', 'application/javascript');
|
||||||
|
const browserCode = this.generateLegacyMenuUtilsCode(nodeName, customHelpers);
|
||||||
res.send(browserCode);
|
res.send(browserCode);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
|
RED.httpAdmin.get(`${basePath}/menuUtils.js`, function(req, res) {
|
||||||
|
res.set('Content-Type', 'application/javascript');
|
||||||
|
res.send(this.generateMenuUtilsBootstrap(nodeName));
|
||||||
|
}.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
generateMenuUtilsData(nodeName, customHelpers = {}, options = {}) {
|
||||||
const defaultHelpers = {
|
const defaultHelpers = {
|
||||||
validateRequired: `function(value) {
|
validateRequired: `function(value) {
|
||||||
return value && value.toString().trim() !== '';
|
return value && value.toString().trim() !== '';
|
||||||
@@ -500,7 +509,71 @@ generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
|||||||
}`
|
}`
|
||||||
};
|
};
|
||||||
|
|
||||||
const allHelpers = { ...defaultHelpers, ...customHelpers };
|
return {
|
||||||
|
nodeName,
|
||||||
|
helpers: { ...defaultHelpers, ...customHelpers },
|
||||||
|
options: {
|
||||||
|
autoLoadLegacy: options.autoLoadLegacy !== false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
generateMenuUtilsBootstrap(nodeName) {
|
||||||
|
return `
|
||||||
|
// Stable bootstrap for EVOLV menu utils (${nodeName})
|
||||||
|
(function() {
|
||||||
|
const nodeName = ${JSON.stringify(nodeName)};
|
||||||
|
const basePath = '/' + nodeName + '/resources';
|
||||||
|
|
||||||
|
window.EVOLV = window.EVOLV || {};
|
||||||
|
window.EVOLV.nodes = window.EVOLV.nodes || {};
|
||||||
|
window.EVOLV.nodes[nodeName] = window.EVOLV.nodes[nodeName] || {};
|
||||||
|
window.EVOLV.nodes[nodeName].utils = window.EVOLV.nodes[nodeName].utils || {};
|
||||||
|
|
||||||
|
function parseHelper(fnBody) {
|
||||||
|
try {
|
||||||
|
return (new Function('return (' + fnBody + ')'))();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[menuUtils] helper parse failed:', error);
|
||||||
|
return function() { return null; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLegacyIfNeeded(autoLoadLegacy) {
|
||||||
|
if (!autoLoadLegacy || typeof window.MenuUtils === 'function') return Promise.resolve();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = basePath + '/menuUtils.legacy.js';
|
||||||
|
script.onload = resolve;
|
||||||
|
script.onerror = reject;
|
||||||
|
document.head.appendChild(script);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(basePath + '/menuUtilsData.json')
|
||||||
|
.then(function(res) { return res.json(); })
|
||||||
|
.then(function(payload) {
|
||||||
|
const helperFns = {};
|
||||||
|
Object.entries(payload.helpers || {}).forEach(function(entry) {
|
||||||
|
helperFns[entry[0]] = parseHelper(entry[1]);
|
||||||
|
});
|
||||||
|
window.EVOLV.nodes[nodeName].utils.helpers = helperFns;
|
||||||
|
return loadLegacyIfNeeded(payload.options && payload.options.autoLoadLegacy);
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
if (typeof window.MenuUtils === 'function' && !window.EVOLV.nodes[nodeName].utils.menuUtils) {
|
||||||
|
window.EVOLV.nodes[nodeName].utils.menuUtils = new window.MenuUtils();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('[menuUtils] bootstrap failed for ' + nodeName, error);
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
generateLegacyMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||||
|
const allHelpers = { ...this.generateMenuUtilsData(nodeName).helpers, ...customHelpers };
|
||||||
|
|
||||||
const helpersCode = Object.entries(allHelpers)
|
const helpersCode = Object.entries(allHelpers)
|
||||||
.map(([name, func]) => ` ${name}: ${func}`)
|
.map(([name, func]) => ` ${name}: ${func}`)
|
||||||
@@ -533,6 +606,11 @@ ${helpersCode}
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backward-compatible alias
|
||||||
|
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||||
|
return this.generateLegacyMenuUtilsCode(nodeName, customHelpers);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = MenuUtils;
|
module.exports = MenuUtils;
|
||||||
@@ -53,4 +53,4 @@ const nodeTemplates = {
|
|||||||
// …add more node “templates” here…
|
// …add more node “templates” here…
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nodeTemplates;
|
module.exports = nodeTemplates;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ class OutputUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
checkForChanges(output, format) {
|
checkForChanges(output, format) {
|
||||||
|
if (!output || typeof output !== 'object') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
const changedFields = {};
|
const changedFields = {};
|
||||||
for (const key in output) {
|
for (const key in output) {
|
||||||
if (output.hasOwnProperty(key) && output[key] !== this.output[format][key]) {
|
if (output.hasOwnProperty(key) && output[key] !== this.output[format][key]) {
|
||||||
@@ -54,11 +57,11 @@ class OutputUtils {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log('Unknown format in output utils');
|
return null;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ const convertModule = require('../convert/index');
|
|||||||
|
|
||||||
class MeasurementContainer {
|
class MeasurementContainer {
|
||||||
constructor(options = {},logger) {
|
constructor(options = {},logger) {
|
||||||
|
this.logger = logger || null;
|
||||||
this.emitter = new EventEmitter();
|
this.emitter = new EventEmitter();
|
||||||
this.measurements = {};
|
this.measurements = {};
|
||||||
this.windowSize = options.windowSize || 10; // Default window size
|
this.windowSize = options.windowSize || 10; // Default window size
|
||||||
@@ -96,7 +97,10 @@ class MeasurementContainer {
|
|||||||
|
|
||||||
variant(variantName) {
|
variant(variantName) {
|
||||||
if (!this._currentType) {
|
if (!this._currentType) {
|
||||||
throw new Error('Type must be specified before variant');
|
if (this.logger) {
|
||||||
|
this.logger.warn('variant() ignored: type must be specified before variant');
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
this._currentVariant = variantName;
|
this._currentVariant = variantName;
|
||||||
this._currentPosition = null;
|
this._currentPosition = null;
|
||||||
@@ -106,11 +110,13 @@ class MeasurementContainer {
|
|||||||
|
|
||||||
position(positionValue) {
|
position(positionValue) {
|
||||||
if (!this._currentVariant) {
|
if (!this._currentVariant) {
|
||||||
throw new Error('Variant must be specified before position');
|
if (this.logger) {
|
||||||
|
this.logger.warn('position() ignored: variant must be specified before position');
|
||||||
|
}
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._currentPosition = positionValue.toString().toLowerCase();
|
||||||
this._currentPosition = positionValue.toString().toLowerCase();;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@@ -429,7 +435,10 @@ class MeasurementContainer {
|
|||||||
// Difference calculations between positions
|
// Difference calculations between positions
|
||||||
difference({ from = "downstream", to = "upstream", unit: requestedUnit } = {}) {
|
difference({ from = "downstream", to = "upstream", unit: requestedUnit } = {}) {
|
||||||
if (!this._currentType || !this._currentVariant) {
|
if (!this._currentType || !this._currentVariant) {
|
||||||
throw new Error("Type and variant must be specified for difference calculation");
|
if (this.logger) {
|
||||||
|
this.logger.warn('difference() ignored: type and variant must be specified');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const get = pos => {
|
const get = pos => {
|
||||||
@@ -510,7 +519,10 @@ class MeasurementContainer {
|
|||||||
|
|
||||||
getVariants() {
|
getVariants() {
|
||||||
if (!this._currentType) {
|
if (!this._currentType) {
|
||||||
throw new Error('Type must be specified before listing variants');
|
if (this.logger) {
|
||||||
|
this.logger.warn('getVariants() ignored: type must be specified first');
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
return this.measurements[this._currentType] ?
|
return this.measurements[this._currentType] ?
|
||||||
Object.keys(this.measurements[this._currentType]) : [];
|
Object.keys(this.measurements[this._currentType]) : [];
|
||||||
@@ -518,7 +530,10 @@ class MeasurementContainer {
|
|||||||
|
|
||||||
getPositions() {
|
getPositions() {
|
||||||
if (!this._currentType || !this._currentVariant) {
|
if (!this._currentType || !this._currentVariant) {
|
||||||
throw new Error('Type and variant must be specified before listing positions');
|
if (this.logger) {
|
||||||
|
this.logger.warn('getPositions() ignored: type and variant must be specified first');
|
||||||
|
}
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.measurements[this._currentType] ||
|
if (!this.measurements[this._currentType] ||
|
||||||
@@ -628,7 +643,10 @@ class MeasurementContainer {
|
|||||||
if (positionValue > 0) {
|
if (positionValue > 0) {
|
||||||
return "downstream";
|
return "downstream";
|
||||||
}
|
}
|
||||||
console.log(`Invalid position provided: ${positionValue}`);
|
if (this.logger) {
|
||||||
|
this.logger.warn(`Invalid position provided: ${positionValue}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/nrmse/index.js
Normal file
7
src/nrmse/index.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const nrmse = require('./errorMetrics.js');
|
||||||
|
const nrmseConfig = require('./nrmseConfig.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
nrmse,
|
||||||
|
nrmseConfig,
|
||||||
|
};
|
||||||
5
src/outliers/index.js
Normal file
5
src/outliers/index.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
const outlierDetection = require('./outlierDetection.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
outlierDetection,
|
||||||
|
};
|
||||||
@@ -61,6 +61,8 @@ class DynamicClusterDeviation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports = DynamicClusterDeviation;
|
||||||
|
|
||||||
// Rolling window simulation with outlier detection
|
// Rolling window simulation with outlier detection
|
||||||
/*
|
/*
|
||||||
const detector = new DynamicClusterDeviation();
|
const detector = new DynamicClusterDeviation();
|
||||||
|
|||||||
9
src/predict/index.js
Normal file
9
src/predict/index.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
const predict = require('./predict_class.js');
|
||||||
|
const interpolation = require('./interpolation.js');
|
||||||
|
const predictConfig = require('./predictConfig.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
predict,
|
||||||
|
interpolation,
|
||||||
|
predictConfig,
|
||||||
|
};
|
||||||
11
src/state/index.js
Normal file
11
src/state/index.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
const state = require('./state.js');
|
||||||
|
const stateManager = require('./stateManager.js');
|
||||||
|
const movementManager = require('./movementManager.js');
|
||||||
|
const stateConfig = require('./stateConfig.json');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
state,
|
||||||
|
stateManager,
|
||||||
|
movementManager,
|
||||||
|
stateConfig,
|
||||||
|
};
|
||||||
26
test/endpoint-utils.test.js
Normal file
26
test/endpoint-utils.test.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
|
||||||
|
const EndpointUtils = require('../src/helper/endpointUtils.js');
|
||||||
|
|
||||||
|
test('generateMenuUtilsData returns helpers and compatibility options', () => {
|
||||||
|
const endpointUtils = new EndpointUtils();
|
||||||
|
const data = endpointUtils.generateMenuUtilsData('measurement', {
|
||||||
|
customCheck: 'function(value) { return !!value; }',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(data.nodeName, 'measurement');
|
||||||
|
assert.equal(typeof data.helpers.validateRequired, 'string');
|
||||||
|
assert.equal(typeof data.helpers.customCheck, 'string');
|
||||||
|
assert.equal(data.options.autoLoadLegacy, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('generateMenuUtilsBootstrap points to data and legacy endpoints', () => {
|
||||||
|
const endpointUtils = new EndpointUtils();
|
||||||
|
const script = endpointUtils.generateMenuUtilsBootstrap('measurement');
|
||||||
|
|
||||||
|
assert.match(script, /menuUtilsData\.json/);
|
||||||
|
assert.match(script, /menuUtils\.legacy\.js/);
|
||||||
|
assert.match(script, /window\.EVOLV\.nodes/);
|
||||||
|
});
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ test('process format emits message with changed fields only', () => {
|
|||||||
assert.deepEqual(first.payload, { a: 1, b: 2 });
|
assert.deepEqual(first.payload, { a: 1, b: 2 });
|
||||||
|
|
||||||
const second = out.formatMsg({ a: 1, b: 2 }, config, 'process');
|
const second = out.formatMsg({ a: 1, b: 2 }, config, 'process');
|
||||||
assert.equal(second, undefined);
|
assert.equal(second, null);
|
||||||
|
|
||||||
const third = out.formatMsg({ a: 1, b: 3, c: { x: 1 } }, config, 'process');
|
const third = out.formatMsg({ a: 1, b: 3, c: { x: 1 } }, config, 'process');
|
||||||
assert.deepEqual(third.payload, { b: 3, c: JSON.stringify({ x: 1 }) });
|
assert.deepEqual(third.payload, { b: 3, c: JSON.stringify({ x: 1 }) });
|
||||||
|
|||||||
Reference in New Issue
Block a user