UNPKG

node-red-contrib-iiot-opcua

Version:

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

759 lines (661 loc) 31.7 kB
<!-- The BSD 3-Clause License Copyright 2022 - DATATRONiQ GmbH (https://datatroniq.com) Copyright (c) 2018-2022 Klaus Landsdorf (http://node-red.plus/) Copyright 2013, 2016 IBM Corp. (node-red) All rights reserved. node-red-contrib-iiot-opcua --> <style> .opcuaiiotinject-time-row{padding-left:110px}.opcuaiiotinject-time-row select{margin:3px 0}.opcuaiiotinject-time-days label{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;vertical-align:top;width:100px}.opcuaiiotinject-time-days input{width:auto}.opcuaiiotinject-time-times{width:90px}#opcuaiiotinject-time-time{width:75px}.opcuaiiotinject-time-count{width:40px!important} </style> <script type='text/javascript'> RED.nodes.registerType('OPCUA-IIoT-Inject', { category: 'IIoT', color: '#ABCDEF', defaults: { injectType: {value: 'inject'}, payload: { value: '', validate: function (v) { let ptype = $('#node-input-payloadType').val() || this.payloadType if (ptype === 'json') { try { JSON.parse(v) return true } catch (err) { return false } } else if (ptype === 'flow' || ptype === 'global') { return /^[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]+)*/i.test(v) } else if (ptype === 'num') { return /^[+-]?[0-9]*\.?[0-9]*([eE][-+]?[0-9]+)?$/.test(v) } return true } }, payloadType: {value: 'date'}, topic: {value: ''}, repeat: {value: ''}, crontab: {value: ''}, once: {value: false}, startDelay: {value: ''}, name: {value: ''}, addressSpaceItems: {value: []} }, inputs: 0, outputs: 1, align: 'left', icon: 'icon.png' }) let injectNode = RED.nodes.registry.getNodeType('OPCUA-IIoT-Inject') injectNode.button = { onclick: function () { let label = (this.name || this.payload).replace(/&/g, '&amp').replace(/</g, '&lt').replace(/>/g, '&gt') if (this.payloadType === 'date') { label = this._('opcuaiiotinject.timestamp') } if (this.payloadType === 'none') { label = this._('opcuaiiotinject.blank') } let node = this $.ajax({ url: 'opcuaIIoT/inject/' + this.id, type: 'POST', success: function (resp) { RED.notify(node._('opcuaiiotinject.success', {label: label}), 'success') }, error: function (jqXHR, textStatus, errorThrown) { if (jqXHR.status === 404) { RED.notify(node._('common.notification.error', {message: node._('common.notification.errors.not-deployed')}), 'error') } else if (jqXHR.status === 500) { RED.notify(node._('common.notification.error', {message: node._('opcuaiiotinject.errors.failed')}), 'error') } else if (jqXHR.status === 0) { RED.notify(node._('common.notification.error', {message: node._('common.notification.errors.no-response')}), 'error') } else { RED.notify(node._('common.notification.error', { message: node._('common.notification.errors.unexpected', { status: jqXHR.status, message: textStatus }) }), 'error') } } }) } } injectNode.oneditprepare = function () { let node = this let fieldPayloadType = $('#node-input-payloadType') if (this.payloadType === null) { if (this.payload === '') { this.payloadType = 'date' } else { this.payloadType = 'str' } } else if (this.payloadType === 'string' || this.payloadType === 'none') { this.payloadType = 'str' } fieldPayloadType.val(this.payloadType) $('#node-input-payload').typedInput({ default: 'str', typeField: fieldPayloadType, types: ['flow', 'global', 'str', 'num', 'bool', 'json', 'date'] }) $('#opcuaiiotinject-time-type-select').change(function () { $('#node-input-crontab').val('') let id = $('#opcuaiiotinject-time-type-select').val() $('.opcuaiiotinject-time-row').hide() $('#opcuaiiotinject-time-row-' + id).show() if ((id === 'none') || (id === 'interval')) { $('#node-once').show() $('#node-delay').show() } else { $('#node-once').hide() $('#node-delay').hide() $('#node-input-once').prop('checked', false) } }) $('.opcuaiiotinject-time-times').each(function () { for (let i = 0; i < 24; i++) { let l = (i < 10 ? '0' : '') + i + ':00' $(this).append($('<option></option>').val(i).text(l)) } }) $('<option></option>').val(24).text('00:00').appendTo('#opcuaiiotinject-time-interval-time-end') $('#opcuaiiotinject-time-interval-time-start').change(function () { let start = Number($('#opcuaiiotinject-time-interval-time-start').val()) let end = Number($('#opcuaiiotinject-time-interval-time-end').val()) $('#opcuaiiotinject-time-interval-time-end option').remove() for (let i = start + 1; i < 25; i++) { let l = (i < 10 ? '0' : '') + i + ':00' if (i === 24) { l = '00:00' } let opt = $('<option></option>').val(i).text(l).appendTo('#opcuaiiotinject-time-interval-time-end') if (i === end) { opt.attr('selected', 'selected') } } }) $('.opcuaiiotinject-time-count').spinner({ min: 1 }) $.widget('ui.injecttimespinner', $.ui.spinner, { options: { // seconds step: 60 * 1000, // hours page: 60 }, _parse: function (value) { if (typeof value === 'string') { // already a timestamp if (Number(value) === value) { return Number(value) } let p = value.split(':') let offset = new Date().getTimezoneOffset() return ((Number(p[0]) * 60) + Number(p[1]) + offset) * 60 * 1000 } return value }, _format: function (value) { let d = new Date(value) let h = d.getHours() let m = d.getMinutes() return ((h < 10) ? '0' : '') + h + ':' + ((m < 10) ? '0' : '') + m } }) $('#opcuaiiotinject-time-time').injecttimespinner() let repeattype = 'none' if (this.repeat !== '') { repeattype = 'interval' let units = 's' let timeRange = this.repeat if (this.repeat % 60 === 0) { units = 'm' timeRange = timeRange / 60 } if (this.repeat % 1440 === 0) { units = 'h' timeRange = timeRange / 60 } $('#opcuaiiotinject-time-interval-count').val(timeRange) $('#opcuaiiotinject-time-interval-units').val(units) $('#opcuaiiotinject-time-interval-days').prop('disabled', 'disabled') } else if (this.crontab !== '') { let cronparts = this.crontab.split(' ') let days = cronparts[4] if (!isNaN(cronparts[0]) && !isNaN(cronparts[1])) { repeattype = 'time' // Fixed time let time = cronparts[1] + ':' + cronparts[0] $('#opcuaiiotinject-time-time').val(time) $('#opcuaiiotinject-time-type-select').val('s') if (days === '*') { $('#opcuaiiotinject-time-time-days input[type=checkbox]').prop('checked', true) } else { $('#opcuaiiotinject-time-time-days input[type=checkbox]').removeAttr('checked') days.split(',').forEach(function (v) { $('#opcuaiiotinject-time-time-days [value=' + v + ']').prop('checked', true) }) } } else { repeattype = 'interval-time' // interval - time period let minutes = cronparts[0].slice(2) if (minutes === '') { minutes = '0' } $('#opcuaiiotinject-time-interval-time-units').val(minutes) if (days === '*') { $('#opcuaiiotinject-time-interval-time-days input[type=checkbox]').prop('checked', true) } else { $('#opcuaiiotinject-time-interval-time-days input[type=checkbox]').removeAttr('checked') days.split(',').forEach(function (v) { $('#opcuaiiotinject-time-interval-time-days [value=' + v + ']').prop('checked', true) }) } let time = cronparts[1] let timeparts = time.split(',') let start let end if (timeparts.length === 1) { // 0 or 0-10 let hours = timeparts[0].split('-') if (hours.length === 1) { if (hours[0] === '') { start = '0' end = '0' } else { start = hours[0] end = Number(hours[0]) + 1 } } else { start = hours[0] end = Number(hours[1]) + 1 } } else { // 23,0 or 17-23,0-10 or 23,0-2 or 17-23,0 let startparts = timeparts[0].split('-') start = startparts[0] let endparts = timeparts[1].split('-') if (endparts.length === 1) { end = Number(endparts[0]) + 1 } else { end = Number(endparts[1]) + 1 } } $('#opcuaiiotinject-time-interval-time-end').val(end) $('#opcuaiiotinject-time-interval-time-start').val(start) } } else { repeattype = 'none' } $('.opcuaiiotinject-time-row').hide() $('#opcuaiiotinject-time-type-select').val(repeattype) $('#opcuaiiotinject-time-row-' + repeattype).show() $('#node-input-payload').typedInput('type', this.payloadType) $('#opcuaiiotinject-time-type-select').change() $('#opcuaiiotinject-time-interval-time-start').change() $('#node-input-injectType').change(function () { if ($(this).val() === 'listen') { $('#input-label-listener').show() $('#input-label-payload').hide() if(fieldPayloadType.val() !== 'num' && fieldPayloadType.val() !== 'json') { $('#node-input-payload').typedInput('type', 'num') $('#node-input-payload').val('1000') } } else { $('#input-label-payload').show() $('#input-label-listener').hide() } }) $('#node-input-once').change(function () { if ($(this).is(':checked') || $('#opcuaiiotinject-time-type-select').val() !== 'none') { $('#node-delay').show() } else { $('#node-delay').hide() } }) let tabs = RED.tabs.create({ id: 'node-input-inject-tabs', onchange: function (tab) { $('#node-input-inject-tabs-content').children().hide() $('#' + tab.id).show() } }) tabs.addTab({ id: 'opcuaiiotinject-tab-settings', label: this._('opcuaiiotinject.tabs-label.settings') }) tabs.addTab({ id: 'opcuaiiotinject-tab-addressSpaceItems', label: this._('opcuaiiotinject.tabs-label.addressSpaceItems') }) let cacheItemCount = 0 if (node.addressSpaceItems && node.addressSpaceItems.length > 0) { cacheItemCount = node.addressSpaceItems.length node.addressSpaceItems.forEach(function (element, index, array) { generateItemsEntry(element, index) }) } function generateItemsEntry (opcuaItem, id) { let container = $('<li/>', { style: 'background: #fefefe; margin:0; padding:8px 0px; border-bottom: 1px solid #ccc;' }) let row = $('<div class="row" id="row' + id + '" />').appendTo(container) $('<i style="color: #eee; cursor: move;" class="node-input-addressSpaceItems-handle fa fa-bars"></i>').appendTo(row) let itemNameField = $('<input/>', { class: 'addressSpaceItemName', id: 'node-input-addressSpaceItems-name' + id, type: 'text', style: 'margin: 0 auto;width:20%;min-width:60px;margin-left:5px', placeholder: 'Name' }).appendTo(row) let itemNodIdField = $('<input/>', { class: 'addressSpaceItemNodeId', id: 'node-input-addressSpaceItems-nodeId' + id, type: 'text', style: 'margin: 0 auto;width:43%;min-width:60px;margin-left:5px', placeholder: 'ns=0;i=85' }).appendTo(row) let itemDatatypeField = $('<input/>', { class: 'addressSpaceItemDatatype', id: 'node-input-addressSpaceItems-datatype' + id, type: 'text', style: 'margin: 0 auto;width:18%;min-width:60px;margin-left:5px', placeholder: 'optional' }).appendTo(row) itemNameField.val(opcuaItem.name) itemNodIdField.val(opcuaItem.nodeId) itemDatatypeField.val(opcuaItem.datatypeName) let finalspan = $('<span/>', {style: 'float: right;margin-right: 10px;'}).appendTo(row) let lookupItemButton = $('<a/>', { href: '#', id: 'node-button-inject-datatypes' + id, class: 'editor-button editor-button-small listButton', style: 'margin-top: 7px; margin-left: 5px;' }).appendTo(finalspan) lookupItemButton.click(function () { let datatypesLookupButton = $('#node-button-inject-datatypes' + id) let datatypeLookupField = $('#row' + id + ' #node-input-addressSpaceItems-datatype' + id) datatypesLookupButton.addClass('disabled') $.getJSON('/opcuaIIoT/plain/DataTypeIds', function (data) { datatypesLookupButton.removeClass('disabled') node.datatypeNames = [] $.each(data, function (i, datatypeName) { node.datatypeNames.push(datatypeName) }) datatypeLookupField.autocomplete({ source: node.datatypeNames, minLength: 0, close: function (event, ui) { datatypeLookupField.autocomplete('destroy') } }).autocomplete('search', '') }) }) $('<i/>', {class: 'fa fa-search'}).appendTo(lookupItemButton) let removeItemButton = $('<a/>', { href: '#', id: 'node-button-inject-remove' + id, class: 'editor-button editor-button-small listButton', style: 'margin-top: 7px; margin-left: 5px;' }).appendTo(finalspan) $('<i/>', {class: 'fa fa-remove'}).appendTo(removeItemButton) removeItemButton.click(function () { container.fadeOut(300, function () { $(this).remove() }) }) $('#node-input-addressSpaceItems-container').append(container) } $('#node-input-addressSpaceItems-container').sortable({ axis: 'y', handle: '.node-input-addressSpaceItems-handle', cursor: 'move' }) $('#node-input-addressSpaceItems-container .node-input-addressSpaceItems-handle').disableSelection() $('#node-input-addressSpaceItems-add').click(function () { if(!cacheItemCount || cacheItemCount < 0) { cacheItemCount = 0 } generateItemsEntry({name: '', nodeId: '', datatypeName: ''}, cacheItemCount++) // length is every time one more than index $('#node-input-addressSpaceItems-container-div').scrollTop($('#node-input-addressSpaceItems-container-div').get(0).scrollHeight) }) function switchDialogResize () { let rows = $('#dialog-form>div:not(.node-input-addressSpaceItems-container-row)') let height = $('#dialog-form').height() rows.each(function (index, row) { height -= row.outerHeight(true) }) let editorRow = $('#dialog-form>div.node-input-addressSpaceItems-container-row') height -= (parseInt(editorRow.css('marginTop')) + parseInt(editorRow.css('marginBottom'))) $('#node-input-addressSpaceItems-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) }) // console.log('well done editprepare ...') } injectNode.oneditsave = function () { let node = this let repeat = '' let crontab = '' let type = $('#opcuaiiotinject-time-type-select').val() if (type === 'interval') { crontab = '' let count = $('#opcuaiiotinject-time-interval-count').val() let units = $('#opcuaiiotinject-time-interval-units').val() if (units === 's') { repeat = count } else { if (units === 'n') { repeat = count / 1000 } else if (units === 'm') { repeat = count * 60 } else if (units === 'h') { repeat = count * 60 * 60 } } } else if (type === 'interval-time') { repeat = '' let count = $('#opcuaiiotinject-time-interval-time-units').val() let startTime = Number($('#opcuaiiotinject-time-interval-time-start').val()) let endTime = Number($('#opcuaiiotinject-time-interval-time-end').val()) let days = $('#opcuaiiotinject-time-interval-time-days input[type=checkbox]:checked').map(function (_, el) { return $(el).val() }).get() if (days.length === 0) { crontab = '' } else { if (days.length === 7) { days = '*' } else { days = days.join(',') } let timerange = '' if (endTime === 0) { timerange = startTime + '-23' } else if (startTime + 1 < endTime) { timerange = startTime + '-' + (endTime - 1) } else if (startTime + 1 === endTime) { timerange = startTime } else { let startpart = '' let endpart = '' if (startTime === 23) { startpart = '23' } else { startpart = startTime + '-23' } if (endTime === 1) { endpart = '0' } else { endpart = '0-' + (endTime - 1) } timerange = startpart + ',' + endpart } if (count === '0') { crontab = count + ' ' + timerange + ' * * ' + days } else { crontab = '*/' + count + ' ' + timerange + ' * * ' + days } } } else if (type === 'time') { let time = $('#opcuaiiotinject-time-time').val() let days = $('#opcuaiiotinject-time-time-days input[type=checkbox]:checked').map(function (_, el) { return $(el).val() }).get() if (days.length === 0) { crontab = '' } else { if (days.length === 7) { days = '*' } else { days = days.join(',') } let parts = time.split(':') repeat = '' crontab = parts[1] + ' ' + parts[0] + ' * * ' + days } } $('#node-input-repeat').val(repeat) $('#node-input-crontab').val(crontab) let cacheItems = $('#node-input-addressSpaceItems-container').children() node.addressSpaceItems = [] cacheItems.each(function () { node.addressSpaceItems.push({ name: $(this).find('.addressSpaceItemName').val(), nodeId: $(this).find('.addressSpaceItemNodeId').val(), datatypeName: $(this).find('.addressSpaceItemDatatype').val() }) }) // console.log('well done editsave ...') } injectNode.label = function () { let suffix = '' if (this.once) { suffix = ' ¹' } let hasRepeat = ( this.repeat && this.repeat !== 'none' ) if (hasRepeat || this.crontab) { suffix = suffix + ' ↻' } if (this.name) { return this.name + suffix } switch (this.payloadType) { case 'string': case 'str': case 'num': case 'bool': case 'json': if (this.topic !== '' && (this.topic.length + this.payload.length) <= 32) { return this.topic + ':' + this.payload + suffix } else if (this.payload.length > 0 && this.payload.length < 24) { return this.payload + suffix } return this._('opcuaiiotinject.inject') + suffix case 'date': if (this.topic !== '' && this.topic.length <= 16) { return this.topic + ':' + this._('opcuaiiotinject.timestamp') + suffix } return this._('opcuaiiotinject.timestamp') + suffix default: if (this.payloadType === 'flow' && this.payload.length < 19) { return 'flow.' + this.payload + suffix } else if (this.payloadType === 'global' && this.payload.length < 17) { return 'global.' + this.payload + suffix } return this._('opcuaiiotinject.inject') + suffix } } injectNode.labelStyle = function () { return this.name ? 'node_label_italic' : '' } </script> <script type='text/x-red' data-template-name='OPCUA-IIoT-Inject'> <div class='form-row'> <ul style='background:#fff;min-width:600px;margin-bottom:20px' id='node-input-inject-tabs'></ul> </div> <div id='node-input-inject-tabs-content' style='min-height:170px'> <div id='opcuaiiotinject-tab-settings' style='display:none'> <div class='form-row' id='node-inject-type'> <label for=''><i class='fa fa-list'></i> <span data-i18n='opcuaiiotinject.label.injectType'></span></label> <select id='node-input-injectType' type='text' style='width:140px'> <option value='inject' data-i18n='opcuaiiotinject.type-label.inject'></option> <option value='read' data-i18n='opcuaiiotinject.type-label.read'></option> <option value='write' data-i18n='opcuaiiotinject.type-label.write'></option> <option value='listen' data-i18n='opcuaiiotinject.type-label.listen'></option> </select> </div> <div class='form-row' id='input-payload-row'> <label for='node-input-payload'><i class='fa fa-envelope'></i> <span id='input-label-payload' data-i18n='opcuaiiotinject.label.payload'></span> <span id='input-label-listener' data-i18n='opcuaiiotinject.label.interval'></span> </label> <input type='text' id='node-input-payload' style='width:70%'> <input type='hidden' id='node-input-payloadType'> </div> <div class='form-row'> <label for='node-input-topic'><i class='icon-tag'></i> <span data-i18n='node-red:common.label.topic'></span></label> <input type='text' id='node-input-topic'> </div> <div class='form-row' id='node-delay'> <label for='node-input-startDelay'><i class='fa fa-clock-o'></i> <span data-i18n='opcuaiiotinject.startDelay'></span></label> <input type='text' id='node-input-startDelay' placeholder='10' style='width:60px'> <span data-i18n='opcuaiiotinject.startDelayAddon'> </span> </div> <div class='form-row' id='node-once'> <label for='node-input-once' style='width:160px'> <i class='fa fa-power-off'></i> <span data-i18n='opcuaiiotinject.onstart'></span></label> <input type='checkbox' id='node-input-once' style='width:40px'> </div> <div class='form-row'> <label for=''><i class='fa fa-repeat'></i> <span data-i18n='opcuaiiotinject.label.repeat'></span></label> <select id='opcuaiiotinject-time-type-select'> <option value='none' data-i18n='opcuaiiotinject.none'></option> <option value='interval' data-i18n='opcuaiiotinject.interval'> </option> <option value='interval-time' data-i18n='opcuaiiotinject.interval-time'></option> <option value='time' data-i18n='opcuaiiotinject.time'></option> </select> <input type='hidden' id='node-input-repeat'> <input type='hidden' id='node-input-crontab'> </div> <div class='form-row opcuaiiotinject-time-row hidden' id='opcuaiiotinject-time-row-interval'> <span data-i18n='opcuaiiotinject.every'></span> <input id='opcuaiiotinject-time-interval-count' class='opcuaiiotinject-time-count' value='500'> <select style='width:140px' id='opcuaiiotinject-time-interval-units'> <option value='n' data-i18n='opcuaiiotinject.milliseconds'></option> <option value='s' data-i18n='opcuaiiotinject.seconds'></option> <option value='m' data-i18n='opcuaiiotinject.minutes'></option> <option value='h' data-i18n='opcuaiiotinject.hours'></option> </select><br> </div> <div class='form-row opcuaiiotinject-time-row hidden' id='opcuaiiotinject-time-row-interval-time'> <span data-i18n='opcuaiiotinject.every'></span> <select style='width:90px' id='opcuaiiotinject-time-interval-time-units' class='opcuaiiotinject-time-int-count' value='1'> <option value='1'>1</option> <option value='2'>2</option> <option value='3'>3</option> <option value='4'>4</option> <option value='5'>5</option> <option value='6'>6</option> <option value='10'>10</option> <option value='12'>12</option> <option value='15'>15</option> <option value='20'>20</option> <option value='30'>30</option> <option value='0'>60</option> </select> <span data-i18n='opcuaiiotinject.minutes'></span> <br> <span data-i18n='opcuaiiotinject.between'></span> <select id='opcuaiiotinject-time-interval-time-start' class='opcuaiiotinject-time-times'></select> <span data-i18n='opcuaiiotinject.and'></span> <select id='opcuaiiotinject-time-interval-time-end' class='opcuaiiotinject-time-times'></select><br> <div id='opcuaiiotinject-time-interval-time-days' class='opcuaiiotinject-time-days'> <div style='display:inline-block vertical-align: topmargin-right:5px' data-i18n='opcuaiiotinject.on'>on</div> <div style='display:inline-block'> <div> <label><input type='checkbox' checked='checked' value='1'> <span data-i18n='opcuaiiotinject.days.0'></span></label> <label><input type='checkbox' checked='checked' value='2'> <span data-i18n='opcuaiiotinject.days.1'></span></label> <label><input type='checkbox' checked='checked' value='3'> <span data-i18n='opcuaiiotinject.days.2'></span></label> </div> <div> <label><input type='checkbox' checked='checked' value='4'> <span data-i18n='opcuaiiotinject.days.3'></span></label> <label><input type='checkbox' checked='checked' value='5'> <span data-i18n='opcuaiiotinject.days.4'></span></label> <label><input type='checkbox' checked='checked' value='6'> <span data-i18n='opcuaiiotinject.days.5'></span></label> </div> <div> <label><input type='checkbox' checked='checked' value='0'> <span data-i18n='opcuaiiotinject.days.6'></span></label> </div> </div> </div> </div> <div class='form-row opcuaiiotinject-time-row hidden' id='opcuaiiotinject-time-row-time'> <span data-i18n='opcuaiiotinject.at'></span> <input id='opcuaiiotinject-time-time' value='12:00'><br> <div id='opcuaiiotinject-time-time-days' class='opcuaiiotinject-time-days'> <div style='display:inline-block vertical-align: topmargin-right: 5px'>on </div> <div style='display:inline-block'> <div> <label><input type='checkbox' checked='checked' value='1'> <span data-i18n='opcuaiiotinject.days.0'></span></label> <label><input type='checkbox' checked='checked' value='2'> <span data-i18n='opcuaiiotinject.days.1'></span></label> <label><input type='checkbox' checked='checked' value='3'> <span data-i18n='opcuaiiotinject.days.2'></span></label> </div> <div> <label><input type='checkbox' checked='checked' value='4'> <span data-i18n='opcuaiiotinject.days.3'></span> </label> <label><input type='checkbox' checked='checked' value='5'> <span data-i18n='opcuaiiotinject.days.4'></span> </label> <label><input type='checkbox' checked='checked' value='6'> <span data-i18n='opcuaiiotinject.days.5'></span> </label> </div> <div> <label><input type='checkbox' checked='checked' value='0'> <span data-i18n='opcuaiiotinject.days.6'></span></label> </div> </div> </div> </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' data-i18n='[placeholder]node-red:common.label.name'> </div> <div class='form-tips' data-i18n='[html]opcuaiiotinject.tip'></div> </div> <div id='opcuaiiotinject-tab-addressSpaceItems' style='display:none'> <div class='form-row node-input-addressSpaceItems-container-row' style='margin-bottom:0'> <div id='node-input-addressSpaceItems-container-div' style='box-sizing:border-box;border-radius:5px;height:480px;padding:5px;border:1px solid #ccc;overflow-y:scroll'> <ol id='node-input-addressSpaceItems-container' style='list-style-type:none;margin:0 auto;width:95%;margin-left:20px'></ol> </div> </div> <div class='form-row'> <a href='#' class='editor-button editor-button-small' id='node-input-addressSpaceItems-add' style='margin-top:4px'><i class='fa fa-plus'></i> <span data-i18n='opcuaiiotinject.label.addButton'></span></a> </div> </div> </div> </script> <script type='text/x-red' data-help-name='OPCUA-IIoT-Inject'> <h2>OPC UA IIoT Inject</h2> <p> The modified Inject node is designed to provide some unique features you need to create beautiful flows.</p> <p> Pressing the button on the left side of the node builds an inject object to read, browse, listen or write by the OPC UA IIoT nodes. </p> <h2>Configuration</h2> <h3>Tab Settings</h3> <strong>Type</strong> <p> Set that type to prepare the correct value for injects.</p> <strong>Payload/Interval</strong> <p> There is an option to send a JSON payload inject into the Listener.</p> <p>Example:</p> <p><pre> { "interval": 500, "queueSize": 10 }</pre></p> <div> <h5>msg.payload.options<h5> <strong> event subscription parameters default: <pre> msg.payload.options = { requestedPublishingInterval: 100, requestedLifetimeCount: 60, requestedMaxKeepAliveCount: 10, maxNotificationsPerPublish: 4, publishingEnabled: true, priority: 3 } </pre> <strong>subscription parameters default: <pre> msg.payload.options = { requestedPublishingInterval: 100, requestedLifetimeCount: 1000, requestedMaxKeepAliveCount: 12, maxNotificationsPerPublish: 100, publishingEnabled: true, priority: 6 } </pre> </strong></strong></h5></h5></div> <p> If the Inject-Type is set to read/write, then the value is a standard payload to transport your data.</p> <strong>Topic </strong> <p>The topic is to group for queues or protocols like MQTT.</p> <strong>Repeat</strong> <p> With the repeat settings you can set a cron configuration.</p> <p> The interval settings needs a short delay on start. This is equal to the once option but without any inject at to start working. </p> <strong>Inject once at start</strong> <p> The 'Delay sec. at start' has to be set to a minimum for starting injects toward the OPC UA server.</p> <p> All activities will start ever <strong>after</strong> the start inject is done.</p> <p> I did a contribute to Node-RED to provide that delay at start from Node-RED 0.18 inject too.</p> <strong>Name</strong> <p>Name in the flow of Node-RED.</p> <h3>Tab OPC UA Items</h3> <strong>Name (optional)</strong> <p> A short human readable name for long or numeric nodeId's.</p> <strong>Node-Id (mandatory)</strong> <p> The Node-Id has to be an OPC UA valid Node-Id like ns=0;i=85 (root->Objects)</p> <p> You can use our Browser node for OPC UA to find the correct Node-Id.</p> <strong>Data Type (mandatory/optional)</strong> <p>The Data Type setting is needed to do the converts of the node-opcua Variant type result(s).</p> <p> It is mandatory to write data otherwise it is optional!</p> <p> In best practice do transformations with the Result Filter node.</p> <div> <strong>Content of an inject:</strong> <ul> <li>payload (value or options for listener) </li><li>topic </li><li>nodetype (inject, read, write, listen) </li><li> injectType (inject) </li><li>addressSpaceItems (Array of nodeId's) </li></ul> </div> </script>