update
This commit is contained in:
@@ -6,8 +6,18 @@ class ChildRegistrationUtils {
|
||||
}
|
||||
|
||||
async registerChild(child, positionVsParent, distance) {
|
||||
const { softwareType } = child.config.functionality;
|
||||
const { name, id } = child.config.general;
|
||||
if (!child || typeof child !== 'object') {
|
||||
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}`);
|
||||
|
||||
@@ -43,19 +53,21 @@ class ChildRegistrationUtils {
|
||||
}
|
||||
|
||||
this.logger.info(`✅ Child ${name} registered successfully`);
|
||||
return true;
|
||||
}
|
||||
|
||||
_storeChild(child, softwareType) {
|
||||
// Maintain your existing structure
|
||||
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 || {};
|
||||
if (!this.mainClass.child[softwareType][category]) {
|
||||
this.mainClass.child[softwareType][category] = [];
|
||||
if (!this.mainClass.child[typeKey][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
|
||||
@@ -95,4 +107,4 @@ class ChildRegistrationUtils {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ChildRegistrationUtils;
|
||||
module.exports = ChildRegistrationUtils;
|
||||
|
||||
@@ -73,17 +73,25 @@ class ConfigUtils {
|
||||
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
|
||||
mergeObjects(obj1, obj2) {
|
||||
for (let key in obj2) {
|
||||
if (obj2.hasOwnProperty(key)) {
|
||||
if (typeof obj2[key] === 'object') {
|
||||
if (!obj1[key]) {
|
||||
const nextValue = obj2[key];
|
||||
|
||||
if (Array.isArray(nextValue)) {
|
||||
obj1[key] = [...nextValue];
|
||||
} else if (this._isPlainObject(nextValue)) {
|
||||
if (!this._isPlainObject(obj1[key])) {
|
||||
obj1[key] = {};
|
||||
}
|
||||
this.mergeObjects(obj1[key], obj2[key]);
|
||||
this.mergeObjects(obj1[key], nextValue);
|
||||
} 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 {object} customHelpers additional helper functions to inject
|
||||
*/
|
||||
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}) {
|
||||
RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, (req, res) => {
|
||||
console.log(`Serving menuUtils.js for ${nodeName} node`);
|
||||
res.set('Content-Type', 'application/javascript');
|
||||
const browserCode = this.generateMenuUtilsCode(nodeName, customHelpers);
|
||||
res.send(browserCode);
|
||||
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}, options = {}) {
|
||||
const basePath = `/${nodeName}/resources`;
|
||||
|
||||
RED.httpAdmin.get(`${basePath}/menuUtilsData.json`, (req, res) => {
|
||||
res.json(this.generateMenuUtilsData(nodeName, customHelpers, options));
|
||||
});
|
||||
|
||||
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
|
||||
* @returns {string} a JS snippet to run in the browser
|
||||
*/
|
||||
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||
generateLegacyMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||
// Default helper implementations to expose alongside MenuUtils
|
||||
const defaultHelpers = {
|
||||
validateRequired: `function(value) {
|
||||
@@ -101,6 +190,11 @@ ${helpersCode}
|
||||
console.log('Loaded EVOLV.nodes.${nodeName}.utils.menuUtils');
|
||||
`;
|
||||
}
|
||||
|
||||
// Backward-compatible alias.
|
||||
generateMenuUtilsCode(nodeName, customHelpers = {}) {
|
||||
return this.generateLegacyMenuUtilsCode(nodeName, customHelpers);
|
||||
}
|
||||
}
|
||||
|
||||
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 = {}) {
|
||||
RED.httpAdmin.get(`/${nodeName}/resources/menuUtils.js`, function(req, res) {
|
||||
console.log(`Serving menuUtils.js for ${nodeName} node`);
|
||||
createMenuUtilsEndpoint(RED, nodeName, customHelpers = {}, options = {}) {
|
||||
const basePath = `/${nodeName}/resources`;
|
||||
|
||||
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.generateMenuUtilsCode(nodeName, customHelpers);
|
||||
const browserCode = this.generateLegacyMenuUtilsCode(nodeName, customHelpers);
|
||||
res.send(browserCode);
|
||||
}.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 = {
|
||||
validateRequired: `function(value) {
|
||||
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)
|
||||
.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…
|
||||
};
|
||||
|
||||
export default nodeTemplates;
|
||||
module.exports = nodeTemplates;
|
||||
|
||||
@@ -7,6 +7,9 @@ class OutputUtils {
|
||||
}
|
||||
|
||||
checkForChanges(output, format) {
|
||||
if (!output || typeof output !== 'object') {
|
||||
return {};
|
||||
}
|
||||
const changedFields = {};
|
||||
for (const key in output) {
|
||||
if (output.hasOwnProperty(key) && output[key] !== this.output[format][key]) {
|
||||
@@ -54,11 +57,11 @@ class OutputUtils {
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log('Unknown format in output utils');
|
||||
break;
|
||||
return null;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user