@metz-connect/node-red-ewio2
Version:
Provides nodes to access EWIO2 interfaces like IOs and metering - from Node-RED
350 lines (342 loc) • 18.5 kB
HTML
<script type="text/javascript">
/**
* Stores IO information of connected EWIO2, that the current value is available if IO port in config menu is changed.
* Object is deleted when EWIO2 is changed (changed by selecting another element in dropdown or opening configuration menu).
* @memberof analogInNode
*/
var analogIns = {};
/**
* Flag that is set if EWIO2 in dropdown has changed. Required to enable / disable analog input configureation dropdown.
* @memberof analogInNode
*/
var ewio2Changed = false;
/**
* Receives an object with all values of an analog IN pin. If it is the currently selected analog IN pin, the settings in analog IN configuration menu are updated.
* @memberof analogInNode
*/
RED.comms.subscribe("publish/ewio2/config/ai/#", function (topic, msg) {
const recvUser = topic.split("/")[6];
const recvPrevIoPort = topic.split("/")[5];
const recvNodeId = topic.split("/")[4];
const ioType = topic.split("/")[3];
// get ewio2 id of disabled select
var ewio2Id =$("#node-input-ewio2 option").eq($("#node-input-ewio2").prop("selectedIndex")).val();
// message was received for the currently selected EWIO2
if(ewio2Id == recvNodeId) {
// only add options for the choosen type
if(msg.ioType == ioType) {
// the msg must contain at least 7 elemtens (7 for e.g. setValue response; 8 for livedata (contains also moduleName))
if(Object.keys(msg).length >= 7) {
const dropdownValue = JSON.stringify({"nodeId": recvNodeId, "host": msg.host, "addr": msg.address});
if(!optionExists(dropdownValue, "#node-input-ioportAi")) {
const moduleName = ((msg.moduleName) && (msg.moduleName != "")) ? " @ " + msg.moduleName : "";
$("#node-input-ioportAi").append($('<option>', {
value: dropdownValue,
text: msg.name + moduleName
}));
}
// temporarilly store received msgs (only for this EWIO2), to have the current value and pulse counter value and flag
analogIns[dropdownValue] = msg;
// set value and pulse counter radio button (true / false) of the choosen and received IO port
let selectedIoPort = $('#node-input-ioportAi').val();
log("selected: " + selectedIoPort + "; recvPrevIoPort: " + recvPrevIoPort);
if(recvPrevIoPort != "undefined") {
const recvPrevIoPortObj = JSON.parse(recvPrevIoPort);
const configNode = RED.nodes.node(ewio2Id);
if (configNode && recvPrevIoPortObj.nodeId === ewio2Id && recvPrevIoPortObj.host == configNode.host) {
selectedIoPort = recvPrevIoPort;
}
}
// set "last" value as selected value in dropdown
$('#node-input-ioportAi').val(selectedIoPort);
// trigger ioport change, to set correct prevIoPort
$('#node-input-ioportAi').trigger("change");
// enable all html elements on valid analog IN values
enableHtmlElements(true, "ioportAi");
let enableConfigFlag = false;
// values of EWIO2 (e.g. analog input configuration) can only be set by Administrator or Operator (handled by websocket-manager). This disables config dropdown for user Standard.
if (recvUser === "Administrator" || recvUser === "Operator") {
enableConfigFlag = true;
}
// enable analog in specific html elements
enableAiSpecificElements(true, enableConfigFlag);
// enable topic html element
enableTopicElement(true);
}
}
}
});
/**
* Frontend part of analog IN node.
* @memberof analogInNode
*/
RED.nodes.registerType('EWIO2 - Analog IN', {
category: 'METZ CONNECT',
color: '#4682B4',
defaults: {
ewio2: { value: "", type: "EWIO2" },
name: { value: "" },
ioportAi: { value: "", required: true },
valueConfig: { value: "" },
value: { value: "" },
inputSignal: { value: "" },
valueUpdate: { value: "" },
outputTopic: { value: "" }
},
// set pulse counter value input
inputs:1,
outputs:1,
icon: "ewio2-analog-input.svg",
label: function() {
return this.name||"EWIO2 - Analog IN";
},
oneditprepare: function() {
let prevIoPort = this.ioportAi;
const nodeId = this.id;
let prevEwio2Data = getConfigNodeDataStructure(this.ewio2, nodeId);
let prevConfig;
initSensorAI(this);
const node = this;
// analog input sensor configuration info
$('#btn-info-aiConfig').tooltip({
//use 'of' to link the tooltip to your specified input
position: { of: '#node-input-valueConfig', my: 'left top', at: 'left bottom' },
content: "No info!"
});
function showAiInfoTooltip(aiInfoBtn) {
let aiConfigInfo;
aiConfigInfo = node._("@metz-connect/node-red-ewio2/ewio2:status.aiConfigInfo");
aiInfoBtn.tooltip({ items: "#btn-info-aiConfig", content: aiConfigInfo} );
aiInfoBtn.tooltip("open");
}
// show analog input info
$('#btn-info-aiConfig').on({
"click": function (e) {
e.preventDefault();
showAiInfoTooltip($(this));
},
"mouseover": function (e) {
if ($("#btn-info-aiConfig").is(":enabled")) {
e.preventDefault();
showAiInfoTooltip($(this));
}
},
"mouseout": function () {
$(this).tooltip("disable");
}
});
// analog input sensor configuration current exclampation mark tooltip
$('#lbl-aiConfig-exclamationMark').tooltip({
//use 'of' to link the tooltip to your specified input
position: { of: '#node-input-valueConfig', my: 'left top', at: 'left bottom' },
content: "No info!"
});
function showAiCurrentTooltip(aiCurrentLabel) {
let aiConfigWarning;
aiConfigWarning = node._("@metz-connect/node-red-ewio2/ewio2:status.aiConfigWarning");
aiCurrentLabel.tooltip({ items: "#lbl-aiConfig-exclamationMark", content: aiConfigWarning} );
aiCurrentLabel.tooltip("open");
}
// show analog input current warning
$('#lbl-aiConfig-exclamationMark').on({
"click": function (e) {
e.preventDefault();
showAiCurrentTooltip($(this));
},
"mouseover": function (e) {
e.preventDefault();
showAiCurrentTooltip($(this));
},
"mouseout": function () {
$(this).tooltip("disable");
}
});
// save the current selected EWIO2, when configuration menu item "ewio2" gets focus. Forward to Node-RED runtime, to be able to publish value again to Node-RED editor.
$("#node-input-ewio2").focus(function () {
prevEwio2Data = getConfigNodeDataStructure(this.value, nodeId);
})
// whenever EWIO2 configuration is changed, async function is called (to "await" for connection)
$("#node-input-ewio2").change(async function () {
ewio2Changed = true;
log("onchange ewio2: prev: " + prevEwio2Data.data.configNodeId + "; current: " + this.value);
// clear IO-Port dropdown, configuration dropdown and locally stored data
$("#node-input-ioportAi option").remove();
$("#node-input-valueConfig option").remove();
analogIns = {};
// clear value
$("#node-input-value").val("");
// clear and hide error msg
$('#label-error-status').text("");
$('#section-error-status').hide();
// disable IO port for every selected EWIO2 configuration node
enableIoPortElement("ioportAi", false);
// disable analog in specific html elements
enableAiSpecificElements(false, false);
// disable topic html element
enableTopicElement(false);
let configNodeId = this.value;
if (!configNodeId || configNodeId == "_ADD_") {
return;
}
// disable all html elements only when a valid EWIO2 configuration node is available
enableHtmlElements(false, "ioportAi");
try {
// one EWIO2 is selected in configuration menu and customer changes to another EWIO2 > close websocket connection of "unselected" EWIO2
if (prevEwio2Data.data.configNodeId != "" && prevEwio2Data.data.configNodeId != "_ADD_") {
const selectedEwio2Data = getConfigNodeDataStructure(this.value, nodeId);
if (JSON.stringify(prevEwio2Data.data) !== JSON.stringify(selectedEwio2Data.data)) {
log(prevEwio2Data.data.configNodeId + " deselected -> close websocket connection");
await closeDeleteWebsocket(prevEwio2Data.data, nodeId);
log("close websocket done");
}
}
// reset prevIoPort, if another EWIO2 is choosen
log(prevIoPort + " typeof: " + typeof prevIoPort);
if (prevIoPort) {
log("prevIoPort-nodeId: " + JSON.parse(prevIoPort).nodeId + "; current EWIO2 id: " + this.value);
if (JSON.parse(prevIoPort).nodeId != this.value) {
prevIoPort = "";
log("reset prevIoPort to ''");
}
}
log("prevIoPort to send: " + prevIoPort);
await getEwio2ConfigData(getConfigNodeDataStructure(configNodeId, nodeId), "ai", prevIoPort, nodeId, undefined);
prevEwio2Data = getConfigNodeDataStructure(this.value, nodeId);
}
catch (error) {
log("analog in onchange EWIO2 error");
log(error);
}
});
$("#node-input-ioportAi").change(async function () {
let portUpdated = false;
if (prevIoPort != this.value) {
portUpdated = true;
}
prevIoPort = this.value;
log("onchange ioport: " + prevIoPort);
// only save value, if a value is choosen. Nothing choosen not possible, due to dropdown
if(this.value != "") {
// set value and manual mode radio button (true / false) of the newly choosen IO port
const receivedValue = analogIns[this.value].value;
$("#node-input-value").val(receivedValue);
// only clear and reload sensor config dropdown, when config has changed or EWIO2 changed (in dropdown) or config has changed. Otherwise the dropdown options were flickering
if (portUpdated || ewio2Changed || (analogIns[this.value].updatedInput & 0x02)) {
ewio2Changed = false;
// clear configuration dropdown
$("#node-input-valueConfig option").remove();
const receivedModuleName = analogIns[this.value].moduleName
const receivedConfig = analogIns[this.value].config;
let sensorEntries = sensorAI.entries();
if (receivedModuleName == "MR-CI4") {
sensorEntries = sensorAI_mrCi4.entries();
}
// show sensor configuration values in configuration dropdowns
for (const sensor of sensorEntries){
if(!optionExists(sensor[0], "#node-input-valueConfig")) {
$("#node-input-valueConfig").append($('<option>', {
value: sensor[0],
text: sensor[1]
}));
}
}
// select received sensor configuration
$("#node-input-valueConfig").val(receivedConfig);
// show the exclamation mark for current (Ampere) configurations
if (receivedConfig >= "26" && receivedConfig <= "29") {
$("#lbl-aiConfig-exclamationMark").show();
}
else {
$("#lbl-aiConfig-exclamationMark").hide();
}
}
}
});
// save the current selected sensor configuration, when configuration menu item "configuration" gets focus.
// This is needes to detect a change of sensor configuration.
$("#node-input-valueConfig").focus(function () {
prevConfig = this.value;
})
$("#node-input-valueConfig").change(async function () {
if (this.value && prevConfig) {
if (this.value != prevConfig) {
await setAiConfig(getConfigNodeDataStructure($("#node-input-ewio2").val(), nodeId), this.value, $("#node-input-ioportAi").val(), nodeId);
// show the exclamation mark for current (Ampere) configurations
if (this.value >= "26" && this.value <= "29") {
$("#lbl-aiConfig-exclamationMark").show();
}
else {
$("#lbl-aiConfig-exclamationMark").hide();
}
prevConfig = this.value;
}
}
});
},
oneditsave: async function() {
log("analog IN: oneditsave");
let configNodeId = $("#node-input-ewio2").val() || this.ewio2;
await prepareCloseWebsocket(configNodeId, this.id);
},
oneditcancel: async function() {
log("analog IN: oneditcancel");
let configNodeId = $("#node-input-ewio2").val() || this.ewio2;
await prepareCloseWebsocket(configNodeId, this.id);
},
oneditdelete: async function() {
log("analog IN: oneditdelete; node-ID: " + this.id);
let configNodeId = $("#node-input-ewio2").val() || this.ewio2;
await prepareCloseWebsocket(configNodeId, this.id);
}
});
</script>
<script type="text/html" data-template-name="EWIO2 - Analog IN">
<div id="section-error-status" class="form-row" hidden>
<label id="label-error-status" style="color:red; width:inherit"><i class="fa fa-warning"></i> Error</label>
</div>
<div class="form-row">
<label for="node-input-ewio2"><i class="fa fa-microchip"></i> EWIO2</label>
<input type="text" id="node-input-ewio2">
</div>
<div class="form-row">
<label for="node-input-ioportAi"><i class="fa fa-wrench"></i> <span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.port"></label>
<select id="node-input-ioportAi" style="width:70%"></select>
</div>
<div class="form-row">
<label for="node-input-valueConfig"><i class="fa fa-cogs"></i> <span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.valueConfig"></label>
<select id="node-input-valueConfig"></select>
<button id="btn-info-aiConfig" class="editor-button" >
<i class="fa fa-fw fa-info"></i>
</button>
<label id="lbl-aiConfig-exclamationMark" style="width: auto"><i class="fa fa-exclamation-triangle"></i></label>
</div>
<div id="section-value" class="form-row">
<label for="node-input-value"><i class="fa fa-bullseye"></i> <span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.value"></label>
<input type="text" id="node-input-value" disabled>
</div>
<hr>
<div class="form-row">
<h4><span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.trigger"></h4>
</div>
<div class="form-row">
<input type="checkbox" id="node-input-inputSignal" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-inputSignal" style="width: auto" data-i18n="@metz-connect/node-red-ewio2/ewio2:label.inputSignal"></label>
</div>
<div class="form-row">
<input type="checkbox" id="node-input-valueUpdate" style="display: inline-block; width: auto; vertical-align: top;">
<label for="node-input-valueUpdate" style="width: auto" data-i18n="@metz-connect/node-red-ewio2/ewio2:label.valueUpdate"></label>
</div>
<hr>
<div class="form-row">
<h4><span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.nodeOutput"></h4>
</div>
<div class="form-row">
<label for="node-input-outputTopic"><i class="fa fa-map-signs"></i> Topic</label>
<input type="text" id="node-input-outputTopic">
</div>
<hr>
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="@metz-connect/node-red-ewio2/ewio2:label.name"></label>
<input type="text" id="node-input-name"/>
</div>
</script>