const { Settler } = require('./specificClass.js'); const { configManager } = require('generalFunctions'); class nodeClass { /** * Node-RED node class for settler. * @param {object} uiConfig - Node-RED node configuration * @param {object} RED - Node-RED runtime API * @param {object} nodeInstance - Node-RED node instance * @param {string} nameOfNode - Name of the node */ constructor(uiConfig, RED, nodeInstance, nameOfNode) { // Preserve RED reference for HTTP endpoints if needed this.node = nodeInstance; this.RED = RED; this.name = nameOfNode; this.source = null; this._loadConfig(uiConfig) this._setupClass(); this._attachInputHandler(); this._registerChild(); this._startTickLoop(); this._attachCloseHandler(); } /** * Handle node-red input messages */ _attachInputHandler() { this.node.on('input', (msg, send, done) => { try { switch (msg.topic) { case 'registerChild': { const childId = msg.payload; const childObj = this.RED.nodes.getNode(childId); if (!childObj || !childObj.source) { this.source?.logger?.warn(`registerChild skipped: missing child/source for id=${childId}`); break; } this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent); break; } default: this.source?.logger?.warn(`Unknown topic: ${msg.topic}`); } } catch (error) { this.source?.logger?.error(`Input handler failure: ${error.message}`); } if (typeof done === 'function') { done(); } }); } /** * Parse node configuration * @param {object} uiConfig Config set in UI in node-red */ _loadConfig(uiConfig) { const cfgMgr = new configManager(); this.config = cfgMgr.buildConfig('settler', uiConfig, this.node.id); } /** * Register this node as a child upstream and downstream. * Delayed to avoid Node-RED startup race conditions. */ _registerChild() { setTimeout(() => { this.node.send([ null, null, { topic: 'registerChild', payload: this.node.id, positionVsParent: this.config?.functionality?.positionVsParent || 'atEquipment' } ]); }, 100); } /** * Setup settler class */ _setupClass() { this.source = new Settler(this.config); // protect from reassignment this.node.source = this.source; } _startTickLoop() { setTimeout(() => { this._tickInterval = setInterval(() => this._tick(), 1000); }, 1000); } _tick(){ this.node.send([this.source.getEffluent, null, null]); } _attachCloseHandler() { this.node.on('close', (done) => { clearInterval(this._tickInterval); if (typeof done === 'function') done(); }); } } module.exports = nodeClass;