node-red-contrib-opcua
Version:
A Node-RED node to communicate via OPC UA based on node-opcua library.
262 lines (250 loc) • 15.8 kB
HTML
<!--
Copyright 2015 Valmet Automation Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<script type="text/javascript">
RED.nodes.registerType('OpcUa-Server',{
category: 'opcua',
color:"#3FADB5",
defaults: {
port: {value: "53880", required: true},
name: {value:""},
endpoint: {value: ""},
users: {value: "users.json"},
nodesetDir: {value: ""},
autoAcceptUnknownCertificate: {value: true},
registerToDiscovery: {value: false},
constructDefaultAddressSpace: {value: true},
allowAnonymous: {value: true, required: true},
endpointNone: {value: true},
endpointSign: {value: true},
endpointSignEncrypt: {value: true},
endpointBasic128Rsa15: {value: true},
endpointBasic256: {value: true},
endpointBasic256Sha256: {value: true},
maxNodesPerBrowse: {value: 0, required: false},
maxNodesPerHistoryReadData: {value: 0, required: false},
maxNodesPerHistoryReadEvents: {value: 0, required: false},
maxNodesPerHistoryUpdateData: {value: 0, required: false},
maxNodesPerRead: {value: 0, required: false},
maxNodesPerWrite: {value: 0, required: false},
maxNodesPerMethodCall: {value: 0, required: false},
maxNodesPerRegisterNodes: {value: 0, required: false},
maxNodesPerNodeManagement: {value: 0, required: false},
maxMonitoredItemsPerCall: {value: 0, required: false},
maxNodesPerHistoryUpdateEvents: {value: 0, required: false},
maxNodesPerTranslateBrowsePathsToNodeIds: {value: 0, required: false},
maxConnectionsPerEndpoint: {value: 20, required: false},
maxMessageSize: {value: 4096, required: false},
maxBufferSize: {value: 4096, required: false},
maxSessions: {value: 20 , required: false}
},
inputs:1,
outputs:1,
align: "right",
icon: "opcuanodeLogo.png",
label: function() {
return this.name || "OPC UA server";
},
labelStyle: function() {
return this.name?"node_label_italic":"";
}
});
</script>
<script type="text/x-red" data-template-name="OpcUa-Server">
<div class="form-row">
<label for="node-input-port"><i class="icon-tasks"></i> Port</label>
<!-- dynamic Ports from 49152 to 65535 (c000hex bis FFFFhex) -->
<input type="text" id="node-input-port" placeholder="Ports 49152 bis 65535 (c000hex bis FFFFhex)">
</div>
<div class="form-row">
<label for="node-input-name"><i class="icon-tasks"></i> Name</label>
<input type="text" id="node-input-name" placeholder="">
</div>
<div class="form-row">
<label for="node-input-endpoint"><i class="icon-tasks"></i> ResourcePath</label>
<input type="text" id="node-input-endpoint" placeholder="UA/SimpleNodeRedServer">
</div>
<div class="form-row">
<label for="node-input-users"><i class="icon-tasks"></i> Users file</label>
<input type="text" id="node-input-users" placeholder="users.json">
</div>
<div class="form-row">
<label for="node-input-nodesetDir"><i class="icon-tasks"></i> Custom nodeset directory</label>
<input type="text" id="node-input-nodesetDir" placeholder="">
</div>
<div class="form-row">
<label for="node-input-autoAcceptUnknownCertificate" style="width:72%;"><i class="icon-tasks"></i> Auto Accept Unknown Certificates</label>
<input type="checkbox" id="node-input-autoAcceptUnknownCertificate" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-registerToDiscovery" style="width:72%;"><i class="icon-tasks"></i> Register to Local Discovery</label>
<input type="checkbox" id="node-input-registerToDiscovery" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-constructDefaultAddressSpace" style="width:72%;"><i class="icon-tasks"></i> Build default AddressSpace</label>
<input type="checkbox" id="node-input-constructDefaultAddressSpace" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-allowAnonymous" style="width:72%;"><i class="icon-tasks"></i> Allow anonymous user</label>
<input type="checkbox" id="node-input-allowAnonymous" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointNone" style="width:72%;"><i class="icon-tasks"></i> Security mode: None</label>
<input type="checkbox" id="node-input-endpointNone" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointSign" style="width:72%;"><i class="icon-tasks"></i> Security mode: Sign</label>
<input type="checkbox" id="node-input-endpointSign" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointSignEncrypt" style="width:72%;"><i class="icon-tasks"></i> Security mode: Sign & Encrypt</label>
<input type="checkbox" id="node-input-endpointSignEncrypt" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointBasic128Rsa15" style="width:72%;"><i class="icon-tasks"></i> Security policy: Basic128-Rsa15</label>
<input type="checkbox" id="node-input-endpointBasic128Rsa15" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointBasic256" style="width:72%;"><i class="icon-tasks"></i> Security policy: Basic256</label>
<input type="checkbox" id="node-input-endpointBasic256" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-endpointBasic256Sha256" style="width:72%;"><i class="icon-tasks"></i> Security policy: Basic256-Sha256</label>
<input type="checkbox" id="node-input-endpointBasic256Sha256" style="max-width:20px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerBrowse" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerBrowse</label>
<input type="number" id="node-input-maxNodesPerBrowse" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerHistoryReadData" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerHistoryReadData</label>
<input type="number" id="node-input-maxNodesPerHistoryReadData" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerHistoryReadEvents" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerHistoryReadEvents</label>
<input type="number" id="node-input-maxNodesPerHistoryReadEvents" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerHistoryUpdateData" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerHistoryUpdateData</label>
<input type="number" id="node-input-maxNodesPerHistoryUpdateData" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerRead" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerRead</label>
<input type="number" id="node-input-maxNodesPerRead" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerWrite" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerWrite</label>
<input type="number" id="node-input-maxNodesPerWrite" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerMethodCall" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerMethodCall</label>
<input type="number" id="node-input-maxNodesPerMethodCall" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerRegisterNodes" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerRegisterNodes</label>
<input type="number" id="node-input-maxNodesPerRegisterNodes" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerNodeManagement" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerNodeManagement</label>
<input type="number" id="node-input-maxNodesPerNodeManagement" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxMonitoredItemsPerCall" style="width:72%;"><i class="icon-tasks"></i> maxMonitoredItemsPerCall</label>
<input type="number" id="node-input-maxMonitoredItemsPerCall" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerHistoryUpdateEvents" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerHistoryUpdateEvents</label>
<input type="number" id="node-input-maxNodesPerHistoryUpdateEvents" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxNodesPerTranslateBrowsePathsToNodeIds" style="width:72%;"><i class="icon-tasks"></i> maxNodesPerTranslateBrowsePathsToNodeIds</label>
<input type="number" id="node-input-maxNodesPerTranslateBrowsePathsToNodeIds" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxConnectionsPerEndpoint" style="width:72%;"><i class="icon-tasks"></i> maxConnectionsPerEndpoint</label>
<input type="number" id="node-input-maxConnectionsPerEndpoint" placeholder="number" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxMessageSize" style="width:72%;"><i class="icon-tasks"></i> maxMessageSize</label>
<input type="number" id="node-input-maxMessageSize" placeholder="4096" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxBufferSize" style="width:72%;"><i class="icon-tasks"></i> maxBufferSize</label>
<input type="number" id="node-input-maxBufferSize" placeholder="4096" style="max-width:100px">
</div>
<div class="form-row">
<label for="node-input-maxSessions" style="width:72%;"><i class="icon-tasks"></i> maxSessions</label>
<input type="number" id="node-input-maxSessions" style="max-width:100px">
</div>
</script>
<script type="text/x-red" data-help-name="OpcUa-Server">
<p>Creates OPC UA server with own variables, object structures and methods to endpoint: opc.tcp://localhost:PORT/ResourcePath/</p>
<p>Parameters PORT and ResourcePath can be given for the server.</p>
<p>File that contains username, password and role.</p>
<p>Register to local Discover Server, if true => OPC UA Server will register itself to opc.tcp://localhost:4840</p>
<p>ConstructDefaultAddressSpace, if false then Server AddressSpace will be empty (no VendorName and sub objects)<p>
<p>Server active endpoints from security mode and policy:</p>
<p>Security mode: None Sign SignAndEncrypt<p>
<p>Security policy: Basic128-Rsa15 | Basic256 | Basic128-Sha256<p>
<p>maxSessions: Maximum number of sessions allowed by the server, default 20. Changing might impact performance.<p>
<p>
OPC UA server commands:
<ul>
<li><b>restartOPCUAServer</b></li>
<li><b>addEquipment</b></li>
<li><b>addPhysicalAsset</b></li>
<li><b>setFolder</b> msg.topic = "folderName" // Example topic format ns=1;s=FolderName (note: uses this as parentFolder)</li>
<li><b>addFolder</b> msg.topic = "ns=1;s=NewFolder" // Note parent folder is objects if not set by setFolder, newFolder is set to parentFolder</li>
<li><b>addVariable</b> msg.topic = "ns=1;s=VariableName;datatype=Double" // parent folder is set above</li>
<li><b>installHistorian</b> msg.topic = "ns=1;s=VariableName;datatype=Double</li>
<li><b>installDiscreteAlarm</b> msg.topic = "ns=1;s=VariableName;datatype=Double" parentObject msg.alarmText for the alarm, msg.priority for alarm severity</li>
<li><b>installLimitAlarm</b> msg.topic = "ns=1;s=VariableName;datatype=Double" parentObject msg.hh=highhighLimit, msg.h=highLimit, msg.l=lowLimit, msg.ll=lowlowLimit</li>
<li><b>addExtensionObject</b> msg.topic = "ns=1;s=VariableName;datatype=ExtensionObject;TypeId=ns=3;i=1001</li>
<li><b>deleteNode</b> msg.payload = "ns=1;s=VariableName"</li>
<li><b>registerNamespace</b> msg.topic = "www.acme.com" output msg.payload = ns=index </li>
<li><b>getNamespaceIndex</b> msg.topic = "www.acme.com" output msg.payload = ns=index </li>
<li><b>getNamespaces</b> output msg.payload = { nsuri1: 1, nsuri2: 2, ... }</li>
<li><b>setUsers</b> msg.topic = "" msg.users JSON structure of users (username, password role)</li>
<li><b>addFile</b> msg.topic = "ns=1;s=test.txt" // Note parent folder is objects if not set by setFolder/addFolder</li>
<li><b>saveAddressSpace</b> msg.filename ="./test.xml" // Saves current AddressSpace into the given filename. If msg.topic used, it should contain namespace index that will be saved. </li>
<li><b>loadAddressSpace</b> msg.filename ="./test.xml" // Load AddressSpace from the given filename.</li>
<li><b>bindVariables</b> Bind same node.send(msg) callbacks to get/set methods as addVariable</li>
<li><b>bindMethod</b> msg.topic = nodeId, msg.code can be given in function node that contains method function</li>
</ul>
Examples JSON Injects: <br />
<pre>
msg.payload = { "opcuaCommand": "addEquipment", "nodeName": "Machine" }
</pre>
<pre>
{ "opcuaCommand": "restartOPCUAServer" }
{ "opcuaCommand": "addEquipment", "nodeName": "Machine" }
{ "opcuaCommand": "addPhysicalAsset", "nodeName": "PhysicalAsset" }
{ "opcuaCommand": "deleteNode", "nodeId": "ns=4;s=PhysicalAsset2" }
</pre>
<pre>
msg.payload = { "opcuaCommand": "addFile", "fileName": "c:/temp/test.txt" }
</pre>
</p>
<p>
OPC UA message type to set variable Counter by JSON Inject
<pre>
{ payload : { "messageType" : "Variable", "namespace" : "1", "variableName": "Counter", "variableValue": msg.payload }};
</pre>
</p>
<p>
Server will send message as client will connect or disconnect.<br>
Topic: Client-connected or Client-disconnected, payload: session name (depends on client implementation)
</p>
<p>
Server operation limits can be set by node UI dialog.
</p>
</script>