node-red-contrib-iiot-opcua
Version:
An Industrial IoT OPC UA toolbox contribution package for Node-RED based on node-opcua.
233 lines (204 loc) • 11.4 kB
HTML
<!--
The BSD 3-Clause License
Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com)
Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/)
All rights reserved.
node-red-contrib-iiot-opcua
--> <script type='text/javascript'> RED.nodes.registerType('OPCUA-IIoT-Method-Caller', {
category: 'IIoT',
color: '#ABCDEF',
defaults: {
connector: {type: "OPCUA-IIoT-Connector", required: true},
objectId: {value: ""},
methodId: {value: ""},
methodType: {value: "basic", required: true},
value: {value: null},
justValue: {value: true},
name: {value: ""},
showStatusActivities: {value: false},
showErrors: {value: false},
inputArguments: {value: []}
},
inputs: 1,
outputs: 1,
align: "left",
icon: "icon.png",
label: function () {
return this.name || "Method Caller";
},
labelStyle: function () {
return this.name ? "node_label_italic" : "";
},
oneditprepare: function () {
let node = this
let tabs = RED.tabs.create({
id: "node-input-method-tabs",
onchange: function (tab) {
$("#node-input-method-tabs-content").children().hide()
$("#" + tab.id).show()
}
})
tabs.addTab({
id: "opcuaiiot-method-tab-settings",
label: this._("opcua-iiot-contrib.tabs-label.settings")
})
tabs.addTab({
id: "opcuaiiot-method-tab-argument",
label: this._("opcua-iiot-contrib.tabs-label.argument")
})
// Argument Management
node.inputArguments.forEach(function (element, index, array) {
generateArgumentEntry(element, index);
})
function generateArgumentEntry (argument, id) {
let container = $('<li/>', {
style:
"background: #fefefe; margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;"
})
let row = $('<div id="row' + id + '"/>').appendTo(container)
$('<i style="color: #eee; cursor: move;" class="node-input-method-argument-handle fa fa-bars"></i>').appendTo(row)
let nameField = $('<input/>', {
id: "node-input-method-argument-name" + id,
class: 'methodArgumentName',
type: "text",
style: "margin-left:5px;width:20%;",
placeholder: 'name'
}).appendTo(row)
let datatypeField = $('<select/>', {
id: "node-input-method-argument-dataType" + id,
class: 'methodArgumentDatatype',
type: "text",
style: "margin-left:5px;width:18%;",
}).appendTo(row);
$.getJSON('/opcuaIIoT/plain/DataTypeIds', function (data) {
$(data).each(function () {
datatypeField.append($("<option>").attr('value', this).text(this));
});
datatypeField.val(argument.dataType)
})
let valueField = $('<input/>', {
id: "node-input-method-argument-value" + id,
class: 'methodArgumentValue',
type: "text",
style: "margin: 0 auto;width:43%;min-width:60px;margin-left:5px",
placeholder: 'value'
}).appendTo(row)
nameField.val(argument.name)
datatypeField.val(argument.dataType)
valueField.val(argument.value)
let finalspan = $('<span/>', {style: "float: right;margin-right: 10px;"}).appendTo(row)
let removeArgumentButton = $('<a/>', {
href: "#",
class: "editor-button editor-button-small",
style: "margin-top: 7px; margin-left: 5px;"
}).appendTo(finalspan)
$('<i/>', {class: "fa fa-remove"}).appendTo(removeArgumentButton)
removeArgumentButton.click(function () {
container.css({"background": "#fee"})
container.fadeOut(300, function () {
$(this).remove()
});
});
$("#node-input-method-argument-container").append(container)
}
$("#node-input-method-argument-container").sortable({
axis: "y",
handle: ".node-input-method-argument-handle",
cursor: "move"
});
$("#node-input-method-argument-container .node-input-method-argument-handle").disableSelection()
let cacheItemCount = 0
$("#node-input-method-argument-add").click(function () {
cacheItemCount++
generateArgumentEntry({name: '', dataType: '', value: ''}, cacheItemCount)
// length is every time one more than index
$("#node-input-method-argument-container-div").scrollTop($("#node-input-method-argument-container-div").get(0).scrollHeight)
})
// dialog handling
function switchDialogResize () {
let rows = $("#dialog-form>div:not(.node-input-method-argument-container-row)")
let height = $("#dialog-form").height()
rows.each(function (index, row) {
height -= row.outerHeight(true)
})
let editorRow = $("#dialog-form>div.node-input-method-argument-container-row")
height -= (parseInt(editorRow.css("marginTop")) + parseInt(editorRow.css("marginBottom")))
$("#node-input-method-argument-container-div").css("height", height + "px")
}
$("#dialog").on("dialogresize", switchDialogResize)
$("#dialog").on("dialogopen", function (ev) {
let size = $("#dialog").dialog('option', 'sizeCache-switch')
if (size) {
$("#dialog").dialog('option', 'width', size.width)
$("#dialog").dialog('option', 'height', size.height)
switchDialogResize()
} else {
setTimeout(switchDialogResize, 10)
}
})
$("#dialog").on("dialogclose", function (ev, ui) {
$("#dialog").off("dialogresize", switchDialogResize)
})
},
oneditsave: function () {
let node = this
let cacheArguments = $("#node-input-method-argument-container").children()
node.inputArguments = []
cacheArguments.each(function () {
node.inputArguments.push({
name: $(this).find(".methodArgumentName").val(),
dataType: $(this).find(".methodArgumentDatatype").val(),
value: $(this).find(".methodArgumentValue").val()
})
})
// console.log('well done editsave ...')
}
}); </script> <script type='text/x-red' data-template-name='OPCUA-IIoT-Method-Caller'>
<div class='form-row'> <ul style='background:#fff;min-width:600px;margin-bottom:20px' id='node-input-method-tabs'></ul>
</div> <div id='node-input-method-tabs-content' style='min-height:170px'> <div id='opcuaiiot-method-tab-settings'
style='display:none'> <div class='form-row' style='min-width:640px'> <label for='node-input-connector'><i
class='icon-globe'></i> <span data-i18n='opcua-iiot-contrib.label.connector'></span></label> <input type='text'
id='node-input-connector'> </div> <div class='form-row'> <label for='node-input-objectId'><i class='icon-tasks'></i>
<span data-i18n='opcua-iiot-contrib.label.objectId'></span></label> <input type='text' id='node-input-objectId'
placeholder='ns=0;s=Server or ns=0;i=2253'> </div> <div class='form-row'> <label for='node-input-methodId'><i
class='icon-tasks'></i> <span data-i18n='opcua-iiot-contrib.label.methodId'></span></label> <input type='text'
id='node-input-methodId' placeholder='ns=1;s=Bark or ns=1;i=VendorName-Bark'> </div> <div class='form-row'> <label
for='node-input-methodType'><i class='icon-tasks'></i> <span data-i18n='opcua-iiot-contrib.label.methodType'></span>
</label> <select type='text' id='node-input-methodType'> <option value='basic'>Basic</option> <option value='complex'>
Complex</option> </select> </div> <div class='form-row'> <label for='node-input-value'><i class='icon-tag'></i> <span
data-i18n='opcua-iiot-contrib.label.value'></span></label> <input type='text' id='node-input-value' placeholder=''>
</div> <div class='form-row'> <label for='node-input-name'><i class='fa fa-tag'></i> <span
data-i18n='node-red:common.label.name'></span></label> <input type='text' id='node-input-name' placeholder=''> </div>
<hr> <div class='form-row'> <label style='min-width:160px' for='node-input-justValue'><i class='fa fa-simplybuilt'></i>
<span data-i18n='opcua-iiot-contrib.label.justValue'></span></label> <input type='checkbox' id='node-input-justValue'
style='max-width:30px'> </div> <hr> <div class='form-row'> <label style='min-width:160px'
for='node-input-showStatusActivities'><i class='fa fa-bolt'></i> <span
data-i18n='opcua-iiot-contrib.label.showActivities'></span></label> <input type='checkbox'
id='node-input-showStatusActivities' style='max-width:30px'> </div> <div class='form-row'> <label
style='min-width:160px' for='node-input-showErrors'><i class='fa fa-exclamation-circle'></i> <span
data-i18n='opcua-iiot-contrib.label.showErrors'></span></label> <input type='checkbox' id='node-input-showErrors'
style='max-width:30px'> </div> </div> <div id='opcuaiiot-method-tab-argument' style='display:none'> <div
class='form-row node-input-method-argument-container-row' style='margin-bottom:0'> <div
id='node-input-method-argument-container-div'
style='box-sizing:border-box;border-radius:5px;height:340px;padding:5px;border:1px solid #ccc;overflow-y:scroll'> <ol
id='node-input-method-argument-container' style='list-style-type:none;margin:0'></ol> </div> </div> <div
class='form-row'> <a href='#' class='editor-button editor-button-small' id='node-input-method-argument-add'
style='margin-top:4px'><i class='fa fa-plus'></i> <span data-i18n='opcua-iiot-contrib.label.addButton'></span></a>
</div> </div> </div> </script> <script type='text/x-red' data-help-name='OPCUA-IIoT-Method-Caller'>
<h2>OPC UA IIoT Method Caller</h2> <p>The Node is to call a basic or complex method on the OPC UA server.</p> <h3>Input
</h3> <p>
If the settings of the node are filled, then a simple inject will work, otherwise the incoming has to receive a complex message with all parameters.
</p> <p>You need to add your XML definition of types and attributes to the server.</p> <div> <strong>
Simple event message payload could be a simple inject if the node parameters are filled or you have to send the parameters via msg object like:
</strong> <pre>msg.payload.</pre> <ul> <li>objectId (Object-Id) </li><li>methodId (Method-Id) </li><li>
inputArguments (Array) <ul> <li>name (just to name the method parameter - has no effect to later use for now) </li><li>
dataType (all node-opcua DataTypes) </li><li>value (value or Variant as expected from node-opcua) </li></ul> </li><li>
methodType (basic, complex) </li></ul> </div> <p>
The settings in that node will override incoming data if the msg property is empty or undefined.</p> <div> <strong>
Complex event message:</strong> <ul> <li>objectId (Object-Id) </li><li>methodId (Method-Id) </li><li>
inputArguments (Array) <ul> <li>dataType </li><li>value </li></ul> </li><li>methodType </li><li>nodetype </li></ul>
</div> <h3>Output</h3> <div> <strong>Event message:</strong> <ul> <li>payload (Array) <ul> <li>statusCode </li><li>
inputArgumentResults (Array) </li><li>inputArgumentDiagnosticInfos (Array) </li><li>outputArguments (Array) </li></ul>
</li><li>definitionResults <ul> <li>methodId </li><li>methodDefinition </li></ul> </li><li>nodetype (method) </li><li>
methodType </li></ul> </div> <strong>Name</strong> <p>Name in the flow of Node-RED.</p> <p>
Set showErrors to get errors from node-opcua on browse.</p> </script>