/** * node class.js * * Encapsulates all node logic in a reusable class. In future updates we can split this into multiple generic classes and use the config to specifiy which ones to use. * This allows us to keep the Node-RED node clean and focused on wiring up the UI and event handlers. */ const Specific = require("./specificClass.js"); class nodeClass { /** * Create a Node. * @param {object} uiConfig - Node-RED node configuration. * @param {object} RED - Node-RED runtime API. */ constructor(uiConfig, RED, nodeInstance, nameOfNode) { // Preserve RED reference for HTTP endpoints if needed this.node = nodeInstance; // This is the Node-RED node instance, we can use this to send messages and update status this.RED = RED; // This is the Node-RED runtime API, we can use this to create endpoints if needed this.name = nameOfNode; // This is the name of the node, it should match the file name and the node type in Node-RED this.source = null; // Will hold the specific class instance this.config = null; // Will hold the merged configuration // Load default & UI config this._loadConfig(uiConfig); // Instantiate core class this._setupSpecificClass(); // Wire up event and lifecycle handlers this._startTickLoop(); this._attachInputHandler(); this._attachCloseHandler(); } /** * Load and merge default config with user-defined settings. * @param {object} uiConfig - Raw config from Node-RED UI. */ _loadConfig(uiConfig) { // Merge UI config over defaults this.config = { general: { name: uiConfig.name, id: this.node.id, // node.id is for the child registration process unit: null, logging: { enabled: uiConfig.enableLog, logLevel: uiConfig.logLevel } }, functionality: { positionVsParent: uiConfig.positionVsParent || 'atEquipment', // Default to 'atEquipment' if not specified } }; } /** * Instantiate the specific class and store as source. */ _setupSpecificClass() { this.source = new Specific(this.config); //store in node this.node.source = this.source; // Store the source in the node instance for easy access } /** * Start the periodic tick loop. */ _startTickLoop() { setTimeout(() => { this._tickInterval = setInterval(() => this._tick(), 1000); }, 1000); } /** * Execute a single tick and send outputs. */ _tick() { processMsg = null; influxMsg = null; // Send only updated outputs on ports 0 & 1 this.node.send([processMsg, influxMsg, null]); } /** * Attach the node's input handler, routing control messages to the class. */ _attachInputHandler() { this.node.on('input', (msg, send, done) => { /* Update to complete event based node by putting the tick function after an input event */ switch(msg.topic) { case 'registerChild': // Register this node as a child of the parent node const childId = msg.payload; const childObj = this.RED.nodes.getNode(childId); this.source.childRegistrationUtils.registerChild(childObj.source, msg.positionVsParent); break; } }); } /** * Clean up timers and intervals when Node-RED stops the node. */ _attachCloseHandler() { this.node.on('close', (done) => { clearInterval(this._tickInterval); done(); }); } } module.exports = nodeClass;