smithtek-node-red-meter
Version:
Adds record function to node-red
224 lines (201 loc) • 8.41 kB
JavaScript
module.exports = function (RED) {
function SmithtekRecord(config) {
RED.nodes.createNode(this, config);
// this.ThresholdType = config.ThresholdType || 'upper';
this.recordType = config.recordType || 'time';
this.startType = config.startType;
this.stopType = config.stopType;
this.savetoType = config.savetoType;
// record time
this.start = config.start;
this.stop = config.stop;
this.saveto = config.saveto;
// record unit
this.multiplier = config.multiplier;
var node = this;
var isRecording = false;
var currentMultiplier = Number.NaN;
// caculate multiplier
if ('unit' === node.recordType) {
if (!Number.isNaN(parseFloat(node.multiplier)))
currentMultiplier = parseFloat(node.multiplier);
}
// print all data for debug
// node.log("start type: " + node.startType);
// node.log("stop type: " + node.stopType);
// node.log("Saveto type: " + node.savetoType);
// node.log("Start: " + node.start);
// node.log("Stop: " + node.stop);
// node.log("Saveto: " + node.saveto);
// node.log("recordType: " + node.recordType);
// node.log("multiplier: " + node.multiplier);
const defautUpdateTimer = 1000;
var timerUpdate = null;
// init variable to save records
var nodeRecord = null;
if ('global' === node.savetoType) {
nodeRecord = this.context().global;
} else if ('flow' === node.savetoType) {
nodeRecord = this.context().flow;
} else {
// should never go to this loop
}
// get value or default 0
// asum that node.saveto is a valid String
nodeRecord.get(node.saveto) || 0;
// Set initial status
showStatus(node.recordType, isRecording);
node.on('input', function (msg) {
// this.log(JSON.stringify(msg));
if (
Object.prototype.hasOwnProperty.call(msg, 'payload') &&
Object.prototype.hasOwnProperty.call(msg.payload, 'reset')
) {
resetRecorder();
} else if (
Object.prototype.hasOwnProperty.call(msg, 'payload') &&
Object.prototype.hasOwnProperty.call(msg.payload, 'multiplier')
) {
// update multiplier
updateMultiplier(msg);
} else {
if ('time' === node.recordType) {
timeRecorder(msg);
} else if ('unit' === node.recordType) {
// record every for every imcoming msg with multiplier
unitRecorder(msg);
}
}
});
node.on('close', function (done) {
if (timerUpdate) {
clearInterval(timerUpdate);
timerUpdate = null;
}
done();
});
function resetRecorder() {
// reset record if msg.payload.reset is existed
nodeRecord.set(node.saveto, 0);
showStatus(node.recordType, isRecording);
}
function timeRecorder(msg) {
// format start signal from config
if (
((node.startType === 'str' && msg.payload === node.start) ||
(node.startType === 'num' &&
msg.payload === parseInt(node.start)) ||
(node.startType === 'bool' &&
msg.payload === parseBool(node.start))) &&
!isRecording
) {
isRecording = true;
// node.log(`Start time record: ${isRecording}`);
showStatus(node.recordType, true);
if (null === timerUpdate)
timerUpdate = setInterval(function () {
nodeRecord.set(
node.saveto,
(nodeRecord.get(node.saveto) || 0) + 1
);
// send to output
msg.payload = nodeRecord.get(node.saveto);
msg._msgid = RED.util.generateId();
// node.log(`interval time record: ${isRecording} ${timerUpdate}`);
node.send(msg);
showStatus(node.recordType, true);
}, defautUpdateTimer);
} else if (
((node.startType === 'str' && msg.payload === node.stop) ||
(node.startType === 'num' &&
msg.payload === parseInt(node.stop)) ||
(node.startType === 'bool' &&
msg.payload === parseBool(node.stop))) &&
isRecording
) {
isRecording = false;
// node.log(`Stop time record: ${isRecording}`);
if (null != timerUpdate) clearInterval(timerUpdate);
timerUpdate = null;
showStatus(node.recordType, false);
} else {}
}
function unitRecorder(msg) {
if (!Number.isNaN(currentMultiplier)) {
nodeRecord.set(
node.saveto,
(nodeRecord.get(node.saveto) || 0) + 1 * currentMultiplier
);
// bypass msg
msg.payload = parseFloat(nodeRecord.get(node.saveto));
node.send(msg);
showStatus(node.recordType, false);
}
}
function updateMultiplier(msg) {
// caculate multiplier
if ('unit' === node.recordType) {
if (!Number.isNaN(parseFloat(msg.payload.multiplier)))
currentMultiplier = parseFloat(msg.payload.multiplier);
showStatus(node.recordType, false);
}
}
function showStatus(type, status) {
if ('time' === type) {
if (status) {
node.status({
fill: 'green',
shape: 'ring',
text: `TIME RECORDING: ${node.savetoType}.${
node.saveto
}: ${nodeRecord.get(node.saveto) || 0}(s)`,
});
} else {
node.status({
fill: 'red',
shape: 'ring',
text: `STOPPED: ${node.savetoType}.${node.saveto}: ${
nodeRecord.get(node.saveto) || 0
}(s)`,
});
}
} else if ('unit' === type) {
if (!Number.isNaN(currentMultiplier)) {
node.status({
fill: 'green',
shape: 'ring',
text: `UNIT RECORDING - Multi:${currentMultiplier.toFixed(
2
)} - ${node.savetoType}.${node.saveto}: ${(
nodeRecord.get(node.saveto) || 0
).toFixed(2)}`,
});
} else {
node.status({
fill: 'red',
shape: 'ring',
text: `STOPPED: invalid multiplier input`,
});
}
}
}
function parseBool(val) {
if (
(typeof val === 'string' &&
(val.toLowerCase() === 'true' ||
val.toLowerCase() === 'yes')) ||
val === 1
)
return true;
else if (
(typeof val === 'string' &&
(val.toLowerCase() === 'false' ||
val.toLowerCase() === 'no')) ||
val === 0
)
return false;
return null;
}
}
RED.nodes.registerType('Smithtek-Metering', SmithtekRecord);
};