Commit ef9e89f1 authored by oroulet's avatar oroulet Committed by oroulet

add sync/client-example.py and make it run

parent 1fb9d6bc
......@@ -5,6 +5,7 @@ import asyncio
from threading import Thread, Condition
import logging
from asyncua import ua
from asyncua import client
from asyncua import server
from asyncua.common import node
......@@ -79,32 +80,21 @@ def release_thread_loop():
stop_thread_loop()
def _get_super(func):
classname = func.__qualname__.split('.', 1)[0]
if hasattr(node, classname):
return getattr(node, classname)
if hasattr(client, classname):
return getattr(client, classname)
if hasattr(server, classname):
return getattr(server, classname)
if hasattr(subscription, classname):
return getattr(subscription, classname)
return AttributeError(f"Could not find super of parent class for method {func}")
def syncmethod(func):
def wrapper(self, *args, **kwargs):
#name = func.__name__
args = list(args) #FIXME: might be very inefficient...
for idx, arg in enumerate(args):
if isinstance(arg, Node):
args[idx] = arg.aio_obj
aio_func = getattr(self.aio_obj, func.__name__)
#sup = _get_super(func)
#super_func = getattr(sup, name)
global _tloop
print("CALLING", func, func.__name__, args)
result = _tloop.post(aio_func(*args, **kwargs))
if isinstance(result, node.Node):
return Node(result)
if isinstance(result, list) and len(result) > 0 and isinstance(result[0], node.Node):
return [Node(i) for i in result]
if isinstance(result, server.event_generator.EventGenerator):
return EventGenerator(result)
return result
return wrapper
......@@ -144,6 +134,12 @@ class Server:
def set_endpoint(self, url):
return self.aio_obj.set_endpoint(url)
def set_server_name(self, name):
return self.aio_obj.set_server_name(name)
def set_security_policy(self, security_policy):
return self.aio_obj.set_security_policy(security_policy)
@syncmethod
def register_namespace(self, url):
return self.aio_obj.register_namespace(url)
......@@ -156,14 +152,40 @@ class Server:
def stop(self):
pass
@syncmethod
async def get_event_generator(self, etype=None, emitting_node=ua.ObjectIds.Server):
pass
def get_node(self, nodeid):
return Node(server.Server.get_node(self, nodeid))
def import_xml(self, path=None, xmlstring=None):
return self.aio_obj.import_xml(path=None, xmlstring=None)
def set_attribute_value(self, nodeid, datavalue, attr=ua.AttributeIds.Value):
return self.aio_obj.set_attribute_value(nodeid, datavalue, attr)
class EventGenerator:
def __init__(self, aio_evgen):
self.aio_obj = aio_evgen
@property
def event(self):
return self.aio_obj.event
def trigger(self, time=None, message=None):
return self.aio_obj.trigger(time, message)
class Node:
def __init__(self, aio_node):
self.aio_obj = aio_node
global _tloop
@property
def nodeid(self):
return self.aio_obj.nodeid
@syncmethod
def get_browse_name(self):
......@@ -177,14 +199,34 @@ class Node:
def get_child(self, path):
pass
@syncmethod
def set_modelling_rule(self, mandatory: bool):
pass
@syncmethod
def add_variable(self, ns, name, val):
pass
@syncmethod
def add_property(self, ns, name, val):
pass
@syncmethod
def add_object(self, ns, name):
pass
@syncmethod
def add_object_type(self, ns, name):
pass
@syncmethod
def add_folder(self, ns, name):
pass
@syncmethod
def add_method(self, *args):
pass
@syncmethod
def set_writable(self, writable=True):
pass
......@@ -193,6 +235,10 @@ class Node:
def set_value(self, val):
pass
@syncmethod
def get_value(self, val):
pass
def __eq__(self, other):
return self.aio_obj == other.aio_obj
......
<?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>urn:freeopcua:example</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="Float">i=10</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasComponent">i=47</Alias>
</Aliases>
<UAObjectType BrowseName="1:MyXmlObj" NodeId="ns=1;i=2001">
<DisplayName>MyXmlObj</DisplayName>
<Description>MyXmlObj</Description>
<References>
<Reference IsForward="false" ReferenceType="HasSubtype">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=2002</Reference>
</References>
</UAObjectType>
<UAVariable BrowseName="1:MyXmlVar" DataType="Float" NodeId="ns=1;i=2002" ParentNodeId="ns=1;i=2001">
<DisplayName>MyXmlVar</DisplayName>
<Description>MyXmlVar</Description>
<References>
<Reference IsForward="false" ReferenceType="HasComponent">ns=1;i=2001</Reference>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
</References>
<Value>
<uax:Float>0.0</uax:Float>
</Value>
</UAVariable>
</UANodeSet>
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 asyncua import ua, uamethod
from asyncua.sync import Server, start_thread_loop, stop_thread_loop
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)
start_thread_loop()
# now setup our server
server = Server()
#server.disable_clock()
#server.set_endpoint("opc.tcp://localhost:4840/freeopcua/server/")
server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
server.set_server_name("FreeOpcUa Example 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://examples.freeopcua.github.io"
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(idx, "MyDevice")
dev.add_variable(idx, "sensor1", 1.0).set_modelling_rule(True)
dev.add_property(idx, "device_id", "0340").set_modelling_rule(True)
ctrl = dev.add_object(idx, "controller")
ctrl.set_modelling_rule(True)
ctrl.add_property(idx, "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(["{}:controller".format(idx), "{}:state".format(idx)]) # 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("custom_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()
stop_thread_loop()
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