UNPKG

node-red-contrib-freya-nodes

Version:

Custom nodes for Freya Vivarium Control System

90 lines (89 loc) 3.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemperatureController = void 0; const EventEmitter = require('events'); class TemperatureController extends EventEmitter { constructor() { super(); this.targetTemperature = 0; this.maximumTemperature = 0; this.maximumTemperature = 0; this.currentTemperature = 0; this.proportionalGain = 10; this.controlEffort = 0; this.isOpenLoop = true; this.controlLoopTimer = null; this.watchdogTimer = null; this.watchdogTimeout = 60; this._startWatchdog(); this.emitStatus('error', 'Open-loop mode', 'No sensor feedback — controller running without environmental feedback'); } _startWatchdog() { if (this.watchdogTimer) clearTimeout(this.watchdogTimer); this.setOpenLoopEnabled(false); this.watchdogTimer = setTimeout(() => { this.setOpenLoopEnabled(true); }, this.watchdogTimeout * 1000); } clear() { if (this.controlLoopTimer) clearInterval(this.controlLoopTimer); if (this.watchdogTimer) clearTimeout(this.watchdogTimer); this.controlEffort = 0; this.emitStatus('error', 'Open-loop mode', 'Controller inactive — running without environmental feedback'); } updateTemperature(temperature) { this.currentTemperature = temperature; this._startWatchdog(); } getCurrentReading() { return { targetTemperature: this.targetTemperature, currentTemperature: this.currentTemperature, timestamp: Math.floor(Date.now() / 1000) }; } configure(minimumTemperature, maximumTemperature, proportionalGain = 10) { this.proportionalGain = proportionalGain; this.minimumTemperature = minimumTemperature; this.maximumTemperature = maximumTemperature; this.emitStatus('ok', 'Controller configured', `Setpoint=${this.targetTemperature}°C, Kp=${this.proportionalGain}`); } setSetpoint(targetTemperature) { this.targetTemperature = targetTemperature; this.emitStatus('ok', 'Setpoint Updated', `Setpoint=${this.targetTemperature}°C)`); } setOpenLoopEnabled(enabled) { this.isOpenLoop = enabled; } emitStatus(level, message, details) { this.emit('status', { level, message, details }); } _executeControlLoop() { if (this.isOpenLoop) { this.controlEffort = 0; this.emitStatus('error', 'Open-loop mode', 'No sensor feedback — controller running without environmental feedback'); this.emit('controlOutput', this.controlEffort); return; } const error = this.targetTemperature - this.currentTemperature; this.controlEffort = this.proportionalGain * error; this.controlEffort = Math.max(-100, Math.min(100, this.controlEffort)); let statusMessage; if (this.controlEffort > 0) { statusMessage = `Heating at ${Math.round(this.controlEffort)}% effort`; } else if (this.controlEffort < 0) { statusMessage = `Cooling at ${Math.round(-this.controlEffort)}% effort`; } else { statusMessage = 'Idle (on target)'; } this.emitStatus('ok', statusMessage); this.emit('controlOutput', this.controlEffort); } } exports.TemperatureController = TemperatureController; module.exports = { TemperatureController };