The development of OPC UA applications takes currently a lot of effort. This is caused by the large possibilities of the OPC UA specification. With this implemtation we want to define some conventions, which shoud make the technology more useable.
The idea of the opcua-smart library is to simplify the OPC UA application generation. Since OPC UA has more than 1500 pages of basic specifications, and the number is still growing, we decided to make some simplification.
This is done by some constraints regarding the modeling functionality of OPC UA. This library deliberately does not offer all functions of OPC UA to simplify the creation of applications.
Copyright (C) 2019-* Jürgen "eTM" Mangler [email protected]. opcua-smart is freely distributable according to the terms of the GNU Lesser General Public License 3.0 (see the file 'COPYING'). This code is distributed without any warranty. See the file 'COPYING' for details.
# Debian/Ubuntu
apt install build-essential cmake-curses-gui libmbedtls-dev libxml2-dev libxslt-dev libz-dev libssl-dev libicu-dev
# Fedora/Redhat
dnf install @buildsys-build @development-tools cmake libxml2-devel libxslt-devel zlib-devel libicu-devel mbedtls-devel
Dependency: https://github.com/open62541/open62541 > 0.4 (master branch as of 2019-04-26)
git clone https://github.com/open62541/open62541.git
cd open62541
mkdir build
cd build
cmake ..
ccmake ..
# Configuration, see picture below
make
sudo make install
gem install opcua
If the installation works correctly, but examples are still complaining about missing lib62541.so, try this:
sudo echo "/usr/local/lib" > /etc/ld.so.conf.d/local.conf # add to libs path
sudo ldconfig # update libs
sudo ldconfig -p | grep libopen62541 # check if its there
or on 64-bit systems:
sudo echo "/usr/local/lib64" > /etc/ld.so.conf.d/local.conf # add to libs path
sudo ldconfig # update libs
sudo ldconfig -p | grep libopen62541 # check if its there
Use rake to build c bindings:
rake
Run server with verbose:
ruby example/server_import_nodeset.rb -v restart
Use relative paths to ruby libraries:
#require 'opcua/server'
require_relative '../lib/opcua/server'
The server has following functions:
- Create the server and add_namespace
- Create ObjectTypes
- Manifest ObjectTypes
- Delete Objects
- Find nodes in the adress space
- Loop for getting real life data
Every server application uses the Demonite gem, which allows to run the server as service.
Daemonite.new do
on startup do |opts|
...
end
run do |opts|
...
end
on exit do
...
end
end.loop!
Each server has 3 sections the startup, run, and exit. In the startup we create the server and the namespace, define all nodes and typically manifest the adress space. The run section loops and therefore updates the values of the nodes in the server. On exit we can d additionally things e.g. close the connection to another interface.
server = OPCUA::Server.new
server.add_namespace "https://yourdomain/testserver"
Basically all new created types are subtypes of the BaseObjectType. With server.types.add_object_type(:TestObjectType)
a new type is defined in the information model. All nodes of the new created type are defined in the tap{}
region.
to = server.types.add_object_type(:TestObjectType).tap{ |t|
t.add_variable :TestVariable
t.add_object(:TestObject, server.types.folder).tap{ |u|
u.add_object :M, mt, OPCUA::OPTIONAL
}
t.add_method :TestMethod, inputarg1: OPCUA::TYPES::STRING, inputarg2: OPCUA::TYPES::DATETIME do |node, inputarg1, inputarg2|
#do some stuff here
end
}
In this example the TestObjectType is defined. It consits of TestVariable of the BaseVariableType an TestObject of the FolderType and a TestMethod.
The .add_variable :TestVariable
command adds a variable with the name TestVariable.
Multible variables can be defined at once with the .add_variables
command.
t.add_variables :TestVar1, :TestVar2
By default variables are read-only.
If you want to add a variable with read/write support you must use the .add_Varable_rw
method.
t.add_variable_rw :TestVar1
With .add_object(:TestObject)
a new object named TestObject is added. The second parameter is optional and definies of which type the new object is. Default the object is from BaseObjectType. In this example the created object is from FolderType. All child nodes of the object can be definded in the tap{}
area.
Methods are added with the .add_method(:TestMethod)
function. Per default the method has no input and output arguments. By adding additional arguments you can define input arguments. The code for defining a method with input arguments looks like
t.add_method :TestMethod, inputarg1: OPCUA::TYPES::STRING, inputarg2: OPCUA::TYPES::DATETIME do |node, inputarg1, inputarg2|
#do some stuff here
end
Input arguments can have a name and a type.
in the do...end
section you write the code which should be executed by calling the method.
ObjectTypes can be instiantiated with the .manifest
method.
testobject =server.objects.manifest(:TestObjectType, to)
Objects can be deleted witch the .delete!
function.
testobject =server.objects.manifest(:TestObjectType, to)
testobject.delete!
To get a specific node you should use the .find
method.
tv = to.find :TestVariable
tv is now the TestVariable node.
You can also find several nodes at the same time.
tva = to.find :TestVariable1, :TestVariable2
tva is now a array containing the requested nodes.
tv1, tv2 = to.find :TestVariable1, :TestVariable2
You can also request several nodes with one find statement.
To get the value of a specific node use the .value
method.
tv.value = 10
tv.value = 'ten'
puts tv.value
You can assign vlaues without definig a datatype. The correct DataType will be used. Default we use _UA::STRING, UA::DOUBLE and UA::INT. Additional Datatypes can be added by request.
The server loop looks like follows:
run do
sleep server.run
to.value = 'Testvariable1'
p to.value
rescue => e
puts e.message
end
The loop starts with sleep server.run
. This is recommended by the open62541 developer. With the .value
function you can write or get the value of a node.
TBD. See examples subdirectory.