Commit f68eb8ab authored by Thomas Jürges's avatar Thomas Jürges

Add server & .gitignore files

parent 123547f4
open62541_build
#! /usr/bin/env python3
from threading import Thread
import copy
import logging
from datetime import datetime
import time
from math import sin
import sys
sys.path.insert(0, "..")
try:
from IPython import embed
except ImportError:
import code
def embed():
myvars = globals()
myvars.update(locals())
shell = code.InteractiveConsole(myvars)
shell.interact()
from opcua import ua, uamethod, Server
class SubHandler(object):
"""
Subscription Handler. To receive events from server for a subscription
"""
def datachange_notification(self, node, val, data):
print("Python: New data change event", node, val)
def event_notification(self, event):
print("Python: New event", event)
# method to be exposed through server
def func(parent, variant):
ret = False
if variant.Value % 2 == 0:
ret = True
return [ua.Variant(ret, ua.VariantType.Boolean)]
# method to be exposed through server
# uses a decorator to automatically convert to and from variants
@uamethod
def multiply(parent, x, y):
print("multiply method call with parameters: ", x, y)
return x * y
class VarUpdater(Thread):
def __init__(self, var):
Thread.__init__(self)
self._stopev = False
self.var = var
def stop(self):
self._stopev = True
def run(self):
while not self._stopev:
v = sin(time.time() / 10)
self.var.set_value(v)
time.sleep(0.1)
if __name__ == "__main__":
# optional: setup logging
logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("opcua.address_space")
# logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.internal_server")
# logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.binary_server_asyncio")
# logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.uaprocessor")
# logger.setLevel(logging.DEBUG)
# now setup our server
server = Server()
#server.disable_clock()
#server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/")
server.set_endpoint("opc.tcp://dop407:4840/LOFAR2/CS000/server/")
server.set_server_name("LOFAR2 OPC UA station server")
# set all possible endpoint policies for clients to connect through
server.set_security_policy([
ua.SecurityPolicyType.NoSecurity,
ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt,
ua.SecurityPolicyType.Basic256Sha256_Sign])
# setup our own namespace
uri = "http://lofar.eu"
idx = server.register_namespace(uri)
# create a new node type we can instantiate in our address space
dev = server.nodes.base_object_type.add_object_type(0, "Uniboard")
dev.add_variable(0, "Temperature #1", 1.0).set_modelling_rule(True)
dev.add_property(0, "boardId", "0340").set_modelling_rule(True)
ctrl = dev.add_object(0, "controller")
ctrl.set_modelling_rule(True)
ctrl.add_property(0, "state", "Idle").set_modelling_rule(True)
# populating our address space
# First a folder to organise our nodes
myfolder = server.nodes.objects.add_folder(idx, "myEmptyFolder")
# instanciate one instance of our device
mydevice = server.nodes.objects.add_object(idx, "Device0001", dev)
mydevice_var = mydevice.get_child(["0:controller", "0:state"]) # get proxy to our device state variable
# create directly some objects and variables
myobj = server.nodes.objects.add_object(idx, "MyObject")
myvar = myobj.add_variable(idx, "MyVariable", 6.7)
mysin = myobj.add_variable(idx, "MySin", 0, ua.VariantType.Float)
myvar.set_writable() # Set MyVariable to be writable by clients
mystringvar = myobj.add_variable(idx, "MyStringVariable", "Really nice string")
mystringvar.set_writable() # Set MyVariable to be writable by clients
mydtvar = myobj.add_variable(idx, "MyDateTimeVar", datetime.utcnow())
mydtvar.set_writable() # Set MyVariable to be writable by clients
myarrayvar = myobj.add_variable(idx, "myarrayvar", [6.7, 7.9])
myarrayvar = myobj.add_variable(idx, "myStronglytTypedVariable", ua.Variant([], ua.VariantType.UInt32))
myprop = myobj.add_property(idx, "myproperty", "I am a property")
mymethod = myobj.add_method(idx, "mymethod", func, [ua.VariantType.Int64], [ua.VariantType.Boolean])
multiply_node = myobj.add_method(idx, "multiply", multiply, [ua.VariantType.Int64, ua.VariantType.Int64], [ua.VariantType.Int64])
# import some nodes from xml
server.import_xml("LOFAR2_CS000_nodes.xml")
# creating a default event object
# The event object automatically will have members for all events properties
# you probably want to create a custom event type, see other examples
myevgen = server.get_event_generator()
myevgen.event.Severity = 300
# starting!
server.start()
print("Available loggers are: ", logging.Logger.manager.loggerDict.keys())
vup = VarUpdater(mysin) # just a stupide class update a variable
vup.start()
try:
# enable following if you want to subscribe to nodes on server side
#handler = SubHandler()
#sub = server.create_subscription(500, handler)
#handle = sub.subscribe_data_change(myvar)
# trigger event, all subscribed clients wil receive it
var = myarrayvar.get_value() # return a ref to value in db server side! not a copy!
var = copy.copy(var) # WARNING: we need to copy before writting again otherwise no data change event will be generated
var.append(9.3)
myarrayvar.set_value(var)
mydevice_var.set_value("Running")
myevgen.trigger(message="This is BaseEvent")
server.set_attribute_value(myvar.nodeid, ua.DataValue(9.9)) # Server side write method which is a but faster than using set_value
embed()
finally:
vup.stop()
server.stop()
<?xml version='1.0' encoding='utf-8'?>
<UAModel><Model current_node="i=85" path="LOFAR2_CS000_nodes.xml" /></UAModel>
\ No newline at end of file
<?xml version='1.0' encoding='utf-8'?>
<UANodeSet xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NamespaceUris>
<Uri>https://lofar.eu//LOFAR2/Station/</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasComponent">i=47</Alias>
</Aliases>
<UAObjectType BrowseName="1:MyObjectType" NodeId="ns=1;i=1002">
<DisplayName>MyObjectType</DisplayName>
<References>
<Reference IsForward="false" ReferenceType="HasSubtype">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
</References>
</UAObjectType>
<UAVariable AccessLevel="3" BrowseName="1:MyVariable" DataType="Double" NodeId="ns=1;i=6001" ParentNodeId="ns=1;i=1002" UserAccessLevel="3">
<DisplayName>MyVariable</DisplayName>
<References>
<Reference IsForward="false" ReferenceType="HasComponent">ns=1;i=1002</Reference>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
</References>
<Value>
<uax:Double>0.0</uax:Double>
</Value>
</UAVariable>
</UANodeSet>
<?xml version="1.0" encoding="UTF-8"?>
<project version="21.1">
<meta>
<filters directoryPatterns="" filePatterns="" positiveFilePatterns="" showHiddenFiles="false"/>
<options/>
</meta>
<projectTree name="LOFAR2_Station.xpr">
<folder path="."/>
</projectTree>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<ModelDesign xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://opcfoundation.org/UA/ModelDesign.xsd https://raw.githubusercontent.com/OPCFoundation/UA-ModelCompiler/v1.04/ModelCompiler/UA%20Model%20Design.xsd"
xmlns="http://opcfoundation.org/UA/ModelDesign.xsd"
xmlns:ANIMAL="https://opcua.rocks/UA/animal/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd"
TargetNamespace="https://opcua.rocks/UA/animal/"
TargetXmlNamespace="https://opcua.rocks/UA/animal/" TargetVersion="0.9.0"
TargetPublicationDate="2019-04-01T00:00:00Z">
<Namespaces>
<Namespace Name="animal" Prefix="animal" XmlNamespace="https://opcua.rocks/UA/animal/Types.xsd" XmlPrefix="animal">https://opcua.rocks/UA/animal/</Namespace>
<Namespace Name="OpcUa" Version="1.03" PublicationDate="2013-12-02T00:00:00Z" Prefix="Opc.Ua" InternalPrefix="Opc.Ua.Server" XmlNamespace="http://opcfoundation.org/UA/2008/02/Types.xsd" XmlPrefix="OpcUa">http://opcfoundation.org/UA/</Namespace>
</Namespaces>
<!-- ### Reference Types ###-->
<!-- ### Object Types ###-->
<!-- ### Variable Types ###-->
<!-- ### Data Types ###-->
<!-- ### Objects ###-->
</ModelDesign>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd http://opcfoundation.org/UA/2011/03/UANodeSet.xsd"
xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:uax="http://opcfoundation.org/UA/11/03/Types.xsd"
xmlns:s1="https://github.com/FreeOpcUa/python-opcua/customobject/Types.xsd">
<NamespaceUris>
<Uri>https://github.com/FreeOpcUa/python-opcua/customobject</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasComponent">i=47</Alias>
</Aliases>
<Extensions>
<Extension>
<ModelInfo Tool="UaModeler" Hash="qnoF+0+hDsL4GCM37vuINw==" Version="1.4.3"/>
</Extension>
</Extensions>
<UAObjectType NodeId="ns=1;i=1002" BrowseName="1:MyObjectType">
<DisplayName>MyObjectType</DisplayName>
<References>
<Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAVariable DataType="Double" ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=6001" BrowseName="1:MyVariable" UserAccessLevel="3" AccessLevel="3">
<DisplayName>MyVariable</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1002</Reference>
</References>
<Value>
<uax:Double>0</uax:Double>
</Value>
</UAVariable>
</UANodeSet>
#! /usr/bin/env python3
import math
import random
import time
import datetime
from opcua import ua, uamethod, Server
# Change values every sleepTime seconds.
sleepTime = 2
# Store the variables and methods in these dicts
variables = {}
methods = {}
# Disable (True) or enable (False) modifying RW_sine
modify_RW_sine = True
@uamethod
def set_RW_sine(nodeId = None, value = 0.0):
global modify_RW_sine
global variables
global sleepTime
modify_RW_sine = False
variables["RW_sine"]["variable"].set_value(value)
time.sleep(2.0 * sleepTime)
modify_RW_sine = True
return modify_RW_sine
if __name__ == "__main__":
# setup our server
server = Server()
server.set_endpoint("opc.tcp://dop407:4840/LOFAR2/CS000")
server.set_server_name("LOFAR2 OPC UA station server")
# setup our own namespace, not really necessary but should as spec
uri = "http://lofar.eu"
idx = server.register_namespace(uri)
# Get Objects node.
objects = server.get_objects_node()
# Now add some folders.
folder = objects.add_folder(idx, "Uniboard #1")
subfolder = folder.add_folder(idx, "FPGA #1")
# Add variables and methods to the subfolder.
obj = subfolder.add_object(idx, "Parent_object_for_RW_sine_variable")
var = obj.add_variable(idx, "RW_sine", 0.0)
# Set RW_time to be writable by clients
var.set_writable()
# Enable value history.
# Keep values not older than "period" and
# keep a maximum of "count" values.
#server.historize_node_data_change(var, period = datetime.timedelta(hours = 1), count = 10)
# Add the variable to my bookkeeping dict.
variables["RW_sine"] = {"object": obj, "variable": var}
obj = subfolder.add_object(idx, "Parent_object_for_RO_cosine_variable")
var = obj.add_variable(idx, "RO_cosine", 0.0)
# Set RO_time to be read-only
var.set_read_only()
# Enable value history.
# Keep values not older than "period" and
# keep a maximum of "count" values.
#server.historize_node_data_change(var, period = datetime.timedelta(hours = 1), count = 10)
# Add the variable to my bookkeeping dict.
variables["RO_cosine"] = {"object": obj, "variable": var}
# Create a method that can modify "RW_sine".
# args are
# nodeid, browsename, method_to_be_called, [input argument types], [output argument types]
# or
# idx, name, method_to_be_called, [input argument types], [output argument types]
#
# If [input|output argument types] is specified, child nodes advertising
# what arguments the method uses and returns will be created.
#
# A callback is a method accepting the nodeid of the parent as first
# argument and variants after that. It returns a list of variants.
#
obj = subfolder.add_object_type(idx, "Parent_object_for_set_RW_sine")
method = obj.add_method(idx, "set_RW_sine", set_RW_sine,
[ua.VariantType.Double], [ua.VariantType.Boolean])
# Add the method to my bookkeeping dict.
methods["set_RW_sine"] = {"object": obj, "function": set_RW_sine}
# Try to import some stuff from an XML file.
server.import_xml("LOFAR2_CS000_nodes.xml")
# Create a "Value out of range" event, a.k.a. alarm
# Start the server.
server.start()
value = 0.0
increment = random.TWOPI / 10.0 * sleepTime
try:
while True:
# Sleep for sleepTime seconds.
time.sleep(sleepTime)
# Now continue with the sine/cosine but add a random noise.
#
# Keep a copy of the counter in order to add different random values
# to the variables.
value += increment
value_copy = value
value_copy += (random.random() - 0.5)
value_copy %= random.TWOPI
variables["RO_cosine"]["variable"].set_value(math.cos(value_copy))
# if abs(value_copy) > 0.5:
# event.message = "Warning! The value of abs(RO_cosine) > 0.5."
# event.source_node = variables["RO_cosine"]["variable"].get_id()
# event.time = datetime.datetime.now()
# server.trigger_event(event)
if modify_RW_sine is True:
value_copy = value
value_copy += (random.random() - 0.5)
value_copy %= random.TWOPI
variables["RW_sine"]["variable"].set_value(math.sin(value_copy))
# if abs(value_copy) > 0.5:
# event.message = "Warning! The value of abs(RW_sine) > 0.5."
# event.source_node = variables["RW_sine"]["variable"].get_id()
# event.time = datetime.datetime.now()
# server.trigger_event(event)
finally:
#close connection, remove subcsriptions, etc
server.stop()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment