//this class will handle the output events for the node red node class OutputUtils { constructor() { this.output ={}; this.output['influxdb'] = {}; this.output['process'] = {}; } checkForChanges(output, format) { const changedFields = {}; for (const key in output) { if (output.hasOwnProperty(key) && output[key] !== this.output[format][key]) { let value = output[key]; // For fields: if the value is an object (and not a Date), stringify it. if (value !== null && typeof value === 'object' && !(value instanceof Date)) { changedFields[key] = JSON.stringify(value); } else { changedFields[key] = value; } } } // Update the saved output state. this.output[format] = { ...this.output[format], ...changedFields }; return changedFields; } formatMsg(output, config, format) { //define emtpy message let msg = {}; // Compare output with last output and only include changed values const changedFields = this.checkForChanges(output,format); if (Object.keys(changedFields).length > 0) { switch (format) { case 'influxdb': // Extract the relevant config properties. const relevantConfig = this.extractRelevantConfig(config); // Flatten the tags so that no nested objects are passed on. const flatTags = this.flattenTags(relevantConfig); msg = this.influxDBFormat(changedFields, config, flatTags); break; case 'process': // Compare output with last output and only include changed values msg = this.processFormat(changedFields,config); //console.log(msg); break; default: console.log('Unknown format in output utils'); break; } return msg; } } influxDBFormat(changedFields, config , flatTags) { // Create the measurement and topic using softwareType and name config.functionality.softwareType + . const measurement = config.general.name; const payload = { measurement: measurement, fields: changedFields, tags: flatTags, timestamp: new Date(), }; const topic = measurement; const msg = { topic: topic, payload: payload }; return msg; } flattenTags(obj) { const result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { const value = obj[key]; if (value !== null && typeof value === 'object' && !(value instanceof Date)) { // Recursively flatten the nested object. const flatChild = this.flattenTags(value); for (const childKey in flatChild) { if (flatChild.hasOwnProperty(childKey)) { result[`${key}_${childKey}`] = String(flatChild[childKey]); } } } else { // InfluxDB tags must be strings. result[key] = String(value); } } } return result; } extractRelevantConfig(config) { return { // general properties id: config.general?.id, name: config.general?.name, unit: config.general?.unit, // functionality properties softwareType: config.functionality?.softwareType, role: config.functionality?.role, // asset properties (exclude machineCurve) uuid: config.asset?.uuid, geoLocation: config.asset?.geoLocation, supplier: config.asset?.supplier, type: config.asset?.type, subType: config.asset?.subType, model: config.asset?.model, }; } processFormat(changedFields,config) { // Create the measurement and topic using softwareType and name config.functionality.softwareType + . const measurement = config.general.name; const payload = changedFields; const topic = measurement; const msg = { topic: topic, payload: payload }; return msg; } } module.exports = OutputUtils;