UNPKG

node-red-contrib-iiot-opcua

Version:

An Industrial IoT OPC UA toolbox contribution package for Node-RED based on node-opcua.

318 lines (298 loc) 19 kB
<!-- The BSD 3-Clause License Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) Copyright 2015,2016 - Mika Karaila, Valmet Automation Inc. (node-red-contrib-opcua) All rights reserved. node-red-contrib-iiot-opcua --> <script type='text/javascript'> RED.nodes.registerType('OPCUA-IIoT-Connector', { category: 'config', defaults: { discoveryUrl: {value: ''}, endpoint: {value: "opc.tcp://localhost:55388/", required: true}, endpointMustExist: {value: false}, keepSessionAlive: {value: true}, loginEnabled: {value: false}, name: {value: "LOCAL SERVER"}, showErrors: {value: false}, securityPolicy: {value: 'None', required: true}, securityMode: {value: 'None', required: true}, individualCerts: {value: false}, publicCertificateFile: {value: ''}, privateKeyFile: {value: ''}, defaultSecureTokenLifetime: {value: ''}, autoSelectRightEndpoint: {value: false}, strategyMaxRetry: {value: ''}, strategyInitialDelay: {value: ''}, strategyMaxDelay: {value: ''}, strategyRandomisationFactor: {value: ''}, requestedSessionTimeout: {value: ''}, connectionStartDelay: {value: ''}, //connectionStopDelay: {value: ''}, //Todo: check missing default assignment for constants. reconnectDelay: {value: ''}, maxBadSessionRequests: {value: 10} }, credentials: { user: {type: "text"}, password: {type: "password"} }, label: function () { return this.name || this.endpoint; }, oneditprepare: function () { let node = this node.lookupItems = [] let tabs = RED.tabs.create({ id: "node-input-connector-tabs", onchange: function (tab) { $("#node-input-connector-tabs-content").children().hide() $("#" + tab.id).show() } }) tabs.addTab({ id: "opcuaiiot-server-tab-settings", label: this._("opcua-iiot-contrib.tabs-label.settings") }) tabs.addTab({ id: "opcuaiiot-server-tab-security", label: this._("opcua-iiot-contrib.tabs-label.security") }) tabs.addTab({ id: "opcuaiiot-server-tab-strategy", label: this._("opcua-iiot-contrib.tabs-label.strategy") }) // Security Management try { let loginCheckbox = $("#node-config-input-loginEnabled"); let loginFields = $("#node-config-login"); if (this.loginEnabled) { loginCheckbox.prop('checked', true); loginFields.show(); } else { loginCheckbox.prop('checked', false); loginFields.hide(); } loginCheckbox.change(function () { if ($(this).is(":checked")) { loginFields.show(); } else { loginFields.hide(); $('#node-config-input-user').val(''); $('#node-config-input-password').val(''); } }); } catch (err) { this.error(err); } // Certificate Management try { let certsCheckbox = $("#node-config-input-individualCerts"); let configCertFields = $("#node-config-certFiles"); if (this.individualCerts) { certsCheckbox.prop('checked', true); configCertFields.show(); } else { certsCheckbox.prop('checked', false); configCertFields.hide(); } certsCheckbox.change(function () { if ($(this).is(":checked")) { configCertFields.show(); } else { configCertFields.hide(); $('#node-config-input-publicCertificateFile').val(''); $('#node-config-input-privateKeyFile').val(''); } }); } catch (err) { this.error(err); } let discoveryLookupButton = $("#node-config-lookup-discovery") let discoveryUrlField = $("#node-config-input-discoveryUrl") discoveryLookupButton.on("click", function () { discoveryLookupButton.addClass('disabled') $.getJSON('/opcuaIIoT/client/discover/' + node.id + '/' + encodeURIComponent(discoveryUrlField.val()), function (data) { discoveryLookupButton.removeClass('disabled') node.lookupItems = [] let security = null $.each(data, function (i, entry) { node.lookupItems.push({ value: entry, label: entry }) }) endpointField.autocomplete({ source: node.lookupItems, minLength: 0, focus: function (event, ui) { endpointField.val(ui.item.value); return false; }, select: function (event, ui) { endpointField.val(ui.item.value) return false; }, close: function (event, ui) { endpointField.autocomplete("destroy") } }).autocomplete("search", "") }) }) let endpointsLookupButton = $("#node-config-lookup-endpoint") let endpointField = $("#node-config-input-endpoint") let securityPolicyField = $("#node-config-input-securityPolicy") let securityModeField = $("#node-config-input-securityMode") endpointsLookupButton.on("click", function () { endpointsLookupButton.addClass('disabled') $.getJSON('/opcuaIIoT/client/endpoints/' + node.id + '/' + encodeURIComponent(endpointField.val()), function (data) { endpointsLookupButton.removeClass('disabled') node.lookupItems = [] let security = null $.each(data, function (i, entry) { security = entry.securityPolicyUri.split('#') node.lookupItems.push({ value: entry.endpointUrl, label: entry.endpointUrl + ' (' + entry.securityMode + ' ' + security[1] + ')', securityPolicy: security[1], securityMode: entry.securityMode }) }) endpointField.autocomplete({ source: node.lookupItems, minLength: 0, focus: function (event, ui) { endpointField.val(ui.item.value); securityPolicyField.val(ui.item.securityPolicy); securityModeField.val(ui.item.securityMode); return false; }, select: function (event, ui) { endpointField.val(ui.item.value) securityPolicyField.val(ui.item.securityPolicy); securityModeField.val(ui.item.securityMode); return false; }, close: function (event, ui) { endpointField.autocomplete("destroy") } }).autocomplete("search", "") }) }) } }); </script> <script type='text/x-red' data-template-name='OPCUA-IIoT-Connector'> <div class='form-row'> <ul style='background:#fff;min-width:600px;margin-bottom:20px' id='node-input-connector-tabs'> </ul> </div> <div id='node-input-connector-tabs-content' style='min-height:170px'> <div id='opcuaiiot-server-tab-settings' style='display:none'> <div class='form-row'> <label for='node-config-input-discoveryUrl'><i class='fa fa-external-link'></i> <span data-i18n='opcua-iiot-contrib.label.discoveryUrl'></span></label> <input type='text' id='node-config-input-discoveryUrl' , palceholder='opc.tcp://server.local:4840/'> <a id='node-config-lookup-discovery' class='btn'><i id='node-lookup-topic-icon' class='fa fa-search'></i></a> </div> <div class='form-row'> <label for='node-config-input-endpoint'><i class='fa fa-external-link'></i> <span data-i18n='opcua-iiot-contrib.label.endpoint'></span></label> <input type='text' id='node-config-input-endpoint'> <a id='node-config-lookup-endpoint' class='btn'><i id='node-lookup-topic-icon' class='fa fa-search'></i></a> </div> <div class='form-row'> <label style='min-width:160px' for='node-config-input-endpointMustExist'><i class='fa fa-cc-discover'> </i> <span data-i18n='opcua-iiot-contrib.label.endpointMustExist'></span></label> <input type='checkbox' id='node-config-input-endpointMustExist' style='max-width:30px'> </div> <div class='form-row'> <label style='min-width:160px' for='node-config-input-autoSelectRightEndpoint'><i class='fa fa-magic'></i> <span data-i18n='opcua-iiot-contrib.label.autoSelectRightEndpoint'></span></label> <input type='checkbox' id='node-config-input-autoSelectRightEndpoint' style='max-width:30px'> </div> <div class='form-row'> <label style='min-width:160px' for='node-config-input-keepSessionAlive'><i class='fa fa-heart-o'></i> <span data-i18n='opcua-iiot-contrib.label.keepSessionAlive'></span></label> <input type='checkbox' id='node-config-input-keepSessionAlive' style='max-width:30px'> </div> <div class='form-row'> <label for='node-config-input-name'><i class='icon-tag'></i> <span data-i18n='node-red:common.label.name'></span></label> <input type='text' id='node-config-input-name' placeholder='Name'> </div> <hr> <div class='form-row'> <label style='min-width:160px' for='node-config-input-showErrors'><i class='fa fa-exclamation-circle'></i> <span data-i18n='opcua-iiot-contrib.label.showErrors'></span></label> <input type='checkbox' id='node-config-input-showErrors' style='max-width:30px'> </div> </div> <div id='opcuaiiot-server-tab-security' style='display:none'> <div class='form-row'> <!-- SecurityPolicy enum via REST --> <label for='node-config-input-securityPolicy' style='min-width:140px'><i class='fa fa-certificate'></i> <span data-i18n='opcua-iiot-contrib.label.securityPolicy'></span></label> <select type='text' id='node-config-input-securityPolicy'> <option value='None'>None</option> <option value='Basic128'>Basic128</option> <option value='Basic192'>Basic192</option> <option value='Basic192Rsa15'>Basic192Rsa15</option> <option value='Basic256Rsa15'>Basic256Rsa15</option> <option value='Basic256Sha256'>Basic256Sha256</option> <option value='Aes128_Sha256_RsaOaep'>Aes128_Sha256_RsaOaep</option> <option value='Aes256_Sha256_RsaPss'>Aes256_Sha256_RsaPss </option> <option value='PubSub_Aes128_CTR'>PubSub_Aes128_CTR</option> <option value='PubSub_Aes256_CTR'> PubSub_Aes256_CTR</option> </select> </div> <div class='form-row'> <!-- MessageSecurityMode enum via REST --> <label for='node-config-input-securityMode' style='min-width:140px'><i class='fa fa-key'></i> <span data-i18n='opcua-iiot-contrib.label.securityMode'></span></label> <select type='text' id='node-config-input-securityMode'> <option value='None'>None</option> <option value='Sign'>Sign</option> <option value='SignAndEncrypt'>Sign&Encrypt</option> </select> </div> <div class='form-row'> <label style='min-width:140px'> <i class='fa fa-certificate'></i> <span data-i18n='opcua-iiot-contrib.label.certificateFiles'></span> </label> </div> <div class='form-row'> <label style='min-width:240px'> <i class='fa fa-file'></i> <span data-i18n='opcua-iiot-contrib.label.individualCerts'></span> <input type='checkbox' id='node-config-input-individualCerts' style='max-width:30px'> </label> </div> <div id='node-config-certFiles'> <hr> <div class='form-row'> <label for='node-config-input-publicCertificateFile'><i class='fa fa-file'></i> <span data-i18n='opcua-iiot-contrib.label.publicCertificateFile'></span></label> <input type='text' id='node-config-input-publicCertificateFile' placeholder='./certificates/client_selfsigned_cert_1024.pem' style='min-width:480px'> </div> <div class='form-row'> <label for='node-config-input-privateKeyFile'><i class='fa fa-file'></i> <span data-i18n='opcua-iiot-contrib.label.privateKeyFile'></span></label> <input type='text' id='node-config-input-privateKeyFile' placeholder='./certificates/PKI/own/private/private_key.pem' style='min-width:480px'> </div> <hr> </div> <div class='form-row'> <label for='node-config-input-defaultSecureTokenLifetime'><i class='fa fa-file'></i> <span data-i18n='opcua-iiot-contrib.label.defaultSecureTokenLifetime'></span></label> <input type='text' id='node-config-input-defaultSecureTokenLifetime' placeholder='120000'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-loginEnabled'><i class='icon-lock'></i> <span data-i18n='opcua-iiot-contrib.label.credentials'></span></label> <input type='checkbox' id='node-config-input-loginEnabled' style='max-width:30px'> </div> <div id='node-config-login'> <div class='form-row'> <label for='node-config-input-user'><i class='icon-user'></i> <span data-i18n='opcua-iiot-contrib.label.user'></span> </label> <input type='text' id='node-config-input-user'> </div> <div class='form-row'> <label for='node-config-input-password'><i class='icon-asterisk'></i> <span data-i18n='opcua-iiot-contrib.label.password'> </span></label> <input type='password' id='node-config-input-password'> </div> </div> </div> <div id='opcuaiiot-server-tab-strategy' style='display:none'> <div class='form-row'> <label for='node-config-input-strategyMaxRetry'><i class='fa fa-repeat'></i> <span data-i18n='opcua-iiot-contrib.label.strategyMaxRetry'></span></label> <input type='text' id='node-config-input-strategyMaxRetry' placeholder='10000' style='width:80px'> </div> <div class='form-row'> <label for='node-config-input-strategyInitialDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.strategyInitialDelay'></span></label> <input type='text' id='node-config-input-strategyInitialDelay' placeholder='500' style='width:80px'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-strategyMaxDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.strategyMaxDelay'></span></label> <input type='text' id='node-config-input-strategyMaxDelay' placeholder='30000' style='width:80px'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-strategyRandomisationFactor'><i class='fa fa-random'></i> <span data-i18n='opcua-iiot-contrib.label.strategyRandomisationFactor'></span></label> <input type='text' id='node-config-input-strategyRandomisationFactor' placeholder='0.2' style='width:80px'> </div> <hr> <h4>Session</h4> <div class='form-row'> <label for='node-config-input-requestedSessionTimeout'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.requestedSessionTimeout'></span></label> <input type='text' id='node-config-input-requestedSessionTimeout' placeholder='60000' style='width:80px'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-maxBadSessionRequests'><i class='fa fa-power-off'></i> <span data-i18n='opcua-iiot-contrib.label.maxBadSessionRequests'></span></label> <input type='text' id='node-config-input-maxBadSessionRequests' placeholder='10' style='width:80px'> &nbsp; times </div> <hr> <h4><span data-i18n='opcua-iiot-contrib.label.serverConnectionDelays'></span></h4> <div class='form-row'> <label for='node-config-input-connectionStartDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.connectionStartDelay'></span></label> <input type='text' id='node-config-input-connectionStartDelay' placeholder='2000' style='width:80px'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-connectionStopDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.connectionStopDelay'></span></label> <input type='text' id='node-config-input-connectionStopDelay' placeholder='2000' style='width:80px'> &nbsp; msec. </div> <div class='form-row'> <label for='node-config-input-reconnectDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcua-iiot-contrib.label.reconnectDelay'></span></label> <input type='text' id='node-config-input-reconnectDelay' placeholder='1000' style='width:80px'> &nbsp; msec. </div> </div> </div> </script> <script type='text/x-red' data-help-name='OPCUA-IIoT-Connector'> <h2>OPC UA IIoT Connector</h2> <p> The Connector is a pure configuration node and starts connecting to the OPC UA server with an OPC UA client instantly after 'Deploy' or start/restart. </p> <p>The default for config nodes is 'On all flows' - set it to the flow to get all deleted if you delete the flow. </p> <p> The Connector is one connection to your OPC UA server, which handles the session. If you need multiple connections use as many Connector nodes as you need. With that you can build your idea of OPC UA in IIoT or IoT connectivity. </p> <h3>Configuration</h3> <strong>Discovery (optional lookup)</strong> <p> Some servers providing a discovery port, which is different from the endpoint port. To discover that port set it to opc.tcp://myopcuaserver.local:4840/ </p> <p><a href='https://opcfoundation.org/wp-content/uploads/2014/08/12_UA_Discovery.pdf' target='_blank'> OPC UA Discovery and Endpoints</a></p> <p> Discovery is just to provide some lookup. The connector uses just the endpoint to open the connection to server.</p> <strong>Endpoint</strong> <p> The endpoint address (URI) to the OPC UA server like opc.tcp://localhost:55388/ or opc.tcp://mypcname.local:55388/</p> <p>The endpoint must exist as named if that option is activated. That gives you more integrity in connecting devices. </p> <p> Auto selecting for the right endpoint is optional and tries to find the correct endpoint to your security settings.</p> <strong>Keep Session Alive</strong> <p> If the sessions should stay alive, then set that option to checked. Otherwise, it will close at session timeout and the first request will restart the session, but you will not succeed with the first request. With the keep alive option the session will survive and every request succeeds. </p> <h3>Security</h3> <p>The Security tab provides some security settings and the user login option.</p> <p> Protect your data with the OPC UA included security options!</p> <div> <ul style='margin-left:80px'> <li>None </li><li> Basic256Sha256 </li><li>Basic256Rsa15 </li><li>Basic256 </li><li>Basic192Rsa15 </li><li>Basic128Rsa15 </li><li> Aes256_Sha256_RsaPss </li><li>Aes128_Sha256_RsaOaep </li></ul> </div> <div> <ul style='margin-left:80px'> <li>None </li> <li>Sign </li><li>Sign&Encrypt </li></ul> </div> <h3>Certificates</h3> <p> Use the node-opcua-pki package or other tools to build your individual key pair and set the path to file here. Otherwise, you get pre-installed keys from node-opcua for secured communication. </p> <p>You could set up your own certificate and private key files. This is optional.</p> <p> With None/NONE mode the keys are not in use.</p> <h3>User Login</h3> <p> The user login is for user authentication to the server. It is stored with Node-RED credentials best practice.</p> <strong>Name</strong> <p>Name in the selection of the endpoint configuration.</p> <p> Set showErrors to get errors from node-opcua on browse.</p> <h3>Strategy</h3> <p> The strategy is based on the node-opcua API strategy parameters.</p> </script>