node-red-contrib-freya-nodes
Version:
Custom nodes for Freya Vivarium Control System
90 lines (89 loc) • 3.57 kB
JavaScript
"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 };