Commit f1e055b3 authored by Denis Štogl's avatar Denis Štogl

Events implementation proposal

parent 33dd4565
......@@ -14,7 +14,7 @@ except ImportError:
shell.interact()
from opcua import ua, uamethod, Server, Event
from opcua import ua, uamethod, Server, EventGenerator
class SubHandler(object):
......
......@@ -10,7 +10,7 @@ Pure Python OPC-UA library
from opcua.common.node import Node
from opcua.common.methods import uamethod
from opcua.common.event import Event
from opcua.common.event import EventGenerator
from opcua.common.subscription import Subscription
from opcua.client.client import Client
from opcua.server.server import Server
......
......@@ -6,7 +6,7 @@ from opcua import Node
import uuid
class Event(object):
class EventGenerator(object):
"""
Create an event based on an event type. Per default is BaseEventType used.
......@@ -24,51 +24,78 @@ class Event(object):
def __init__(self, isession, etype=ua.ObjectIds.BaseEventType, source=ua.ObjectIds.Server):
self.isession = isession
self.node = None
self.event = etype
if isinstance(etype, Node):
if isinstance(etype, ua.BaseEvent):
pass
elif isinstance(etype, Node):
self.node = etype
elif isinstance(etype, ua.NodeId):
self.node = Node(self.isession, etype)
else:
self.node = Node(self.isession, ua.NodeId(etype))
self._set_members_from_node(self.node)
if self.node:
event = CustomEvent()
references = node.get_children_descriptions(refs=ua.ObjectIds.HasProperty)
for desc in references:
node = Node(self.isession, desc.NodeId)
setattr(self, desc.BrowseName.Name, node.get_value())
if isinstance(source, Node):
self.SourceNode = source.NodeId
elif isinstance(etype, ua.NodeId):
self.SourceNode = source.NodeId
pass
elif isinstance(source, NodeId):
source = ua.Node(isession, source)
else:
self.SourceNode = ua.NodeId(source)
# set some default values for attributes from BaseEventType, thus that all event must have
self.EventId = uuid.uuid4().bytes
self.EventType = self.node.nodeid
self.LocaleTime = datetime.utcnow()
self.ReceiveTime = datetime.utcnow()
self.Time = datetime.utcnow()
self.Message = ua.LocalizedText()
self.Severity = ua.Variant(1, ua.VariantType.UInt16)
self.SourceName = "Server"
# og set some node attributed we also are expected to have
self.BrowseName = self.node.get_browse_name()
self.DisplayName = self.node.get_display_name()
self.NodeId = self.node.nodeid
self.NodeClass = self.node.get_node_class()
self.Description = self.node.get_description()
source = Node(isession, ua.NodeId(source))
if self.event.SourceNode.Identifier:
source = Node(self.iserver.isession, self.event.SourceNode)
self.event.SourceName = source.get_display_name().Text
source.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(1, ua.VariantType.Byte)))
def __str__(self):
return "Event(Type:{}, Source:{}, Time:{}, Message: {})".format(self.EventType, self.SourceNode, self.Time, self.Message)
return "EventGenerator(Type:{}, Source:{}, Time:{}, Message: {})".format(self.EventType, self.SourceNode, self.Time, self.Message)
__repr__ = __str__
def trigger(self):
def trigger(self, time=None, message=None):
"""
Trigger the event. This will send a notification to all subscribed clients
"""
self.isession.subscription_service.trigger_event(self)
self.event.EventId = ua.Variant(uuid.uuid4().hex, ua.VariantType.ByteString)
if time:
self.event.Time = time
else:
self.event.Time = datetime.utcnow()
self.event.ReciveTime = datetime.utcnow()
#FIXME: LocalTime is wrong but currently know better. For description s. Part 5 page 18
self.event.LocaleTime = datetime.utcnow()
if message:
self.Message = ua.LocalizedText(message)
elif not self.event.Message:
self.event.Message = ua.LocalizedText(self.source.get_browse_name().Text)
self.isession.subscription_service.trigger_event(self.event)
def _set_members_from_node(self, node):
references = node.get_children_descriptions(refs=ua.ObjectIds.HasProperty)
for desc in references:
node = Node(self.isession, desc.NodeId)
setattr(self, desc.BrowseName.Name, node.get_value())
class CustomEvent(ua.BaseEvent):
def __init__(self, etype=ua.ObjectIds.BaseEventType, sourcenode=ua.NodeId(ua.ObjectIds.Server), message=None, severity=1):
super(ua.BaseEvent, self).__init__(sourcenode, message, severity, True)
#TODO: Add fileds
#TODO: Extend to show all fields of CustomEvent
def __str__(self):
s = 'CustomEvent(EventId:{}'.format(self.EventId)
s += ', EventType:{}'.format(self.EventType)
s += ', SourceNode:{}'.format(self.SourceNode)
s += ', SourceName:{}'.format(self.SourceName)
s += ', Time:{}'.format(self.Time)
s += ', RecieveTime:{}'.format(self.RecieveTime)
s += ', LocalTime:{}'.format(self.LocalTime)
s += ', Message:{}'.format(self.Message)
s += ', Severity:{}'.format(self.Severity)
s += ')'
return s
__repr__ = __str__
......@@ -39,12 +39,6 @@ class SubHandler(object):
"""
pass
def event(self, handle, event):
"""
Deprecated use event_notification
"""
pass
class EventResult():
"""
......
......@@ -459,7 +459,7 @@ class AddressSpace(object):
def get_attribute_value(self, nodeid, attr):
with self._lock:
#self.logger.debug("get attr val: %s %s", nodeid, attr)
self.logger.debug("get attr val: %s %s", nodeid, attr)
if nodeid not in self._nodes:
dv = ua.DataValue()
dv.StatusCode = ua.StatusCode(ua.StatusCodes.BadNodeIdUnknown)
......
......@@ -14,7 +14,7 @@ from opcua import ua
from opcua.server.binary_server_asyncio import BinaryServer
from opcua.server.internal_server import InternalServer
from opcua.common.node import Node
from opcua.common.event import Event
from opcua.common.event import EventGenerator
from opcua.common.subscription import Subscription
from opcua.common import xmlimporter
from opcua.common.manage_nodes import delete_nodes
......@@ -322,12 +322,19 @@ class Server(object):
uries = self.get_namespace_array()
return uries.index(uri)
def get_event_object(self, etype=ua.ObjectIds.BaseEventType, source=ua.ObjectIds.Server):
def get_event_generator(self, etype=ua.ObjectIds.BaseEventType, source=ua.ObjectIds.Server):
"""
Returns an event object using an event type from address space.
Use this object to fire events
"""
return Event(self.iserver.isession, etype, source)
return EventGenerator(self.iserver.isession, etype, source)
def create_custom_event(self, name, baseetype=ua.ObjectIds.BaseEventType, properties=[]):
base_event = self.get_node(baseetype)
custom_event = base_event.add_object(name)
for property in properties:
custom_event.add_property(property[0], property[1])
def import_xml(self, path):
"""
......@@ -338,9 +345,9 @@ class Server(object):
def delete_nodes(self, nodes, recursive=False):
return delete_nodes(self.iserver.isession, nodes, recursive)
def historize_node(self, node):
self.iserver.enable_history(node)
def dehistorize_node(self, node):
self.iserver.disable_history(node)
......@@ -4,4 +4,6 @@ from opcua.ua.object_ids import ObjectIds
from opcua.ua.status_codes import StatusCodes
from opcua.ua.uaprotocol_auto import *
from opcua.ua.uaprotocol_hand import *
from opcua.ua.uatypes_auto import *
from opcua.ua.uatypes import * #TODO: This should be renamed to uatypes_hand
......@@ -1068,8 +1068,6 @@ class XmlElement(FrozenClass):
__repr__ = __str__
class DataValue(FrozenClass):
'''
......
'''
Example auto_generated file with UA Types
For now only events!
'''
from opcua.ua import *
# TODO: This should be autogeneratd form XML description of EventTypes
class BaseEvent(FrozenClass):
'''
BaseEvent implements BaseEventType from which inherit all other events and it is used per default.
'''
def __init__(self, sourcenode=NodeId(ObjectIds.Server), message=None, severity=1, extended=False):
self.EventId = bytes()
self.EventType = NodeId(ObjectIds.BaseEventType)
self.SourceNode = sourcenode
self.SourceName = None
self.Time = None
self.RecieveTime = None
self.LocalTime = None
self.Message = LocalizedText(message)
self.Severity = Variant(severity, VariantType.UInt16)
if not extended:
self._freeze = True
def __str__(self):
s = 'BaseEventType(EventId:{}'.format(self.EventId)
s += ', EventType:{}'.format(self.EventType)
s += ', SourceNode:{}'.format(self.SourceNode)
s += ', SourceName:{}'.format(self.SourceName)
s += ', Time:{}'.format(self.Time)
s += ', RecieveTime:{}'.format(self.RecieveTime)
s += ', LocalTime:{}'.format(self.LocalTime)
s += ', Message:{}'.format(self.Message)
s += ', Severity:{}'.format(self.Severity)
s += ')'
return s
__repr__ = __str__
......@@ -23,7 +23,7 @@ def add_server_methods(srv):
def func2(parent, methodname, value):
return math.sin(value)
o = srv.get_objects_node()
o = srv.get_objects_node()š
v = o.add_method(ua.NodeId("ServerMethodArray", 2), ua.QualifiedName('ServerMethodArray', 2), func2, [ua.VariantType.String, ua.VariantType.Int64], [ua.VariantType.Int64])
@uamethod
......@@ -86,7 +86,7 @@ class MySubHandler():
class MySubHandler2():
def __init__(self):
self.results = []
self.results = []
def datachange_notification(self, node, val, data):
self.results.append((node, val))
......@@ -97,8 +97,8 @@ class MySubHandler2():
class MySubHandlerCounter():
def __init__(self):
self.datachange_count = 0
self.event_count = 0
self.datachange_count = 0
self.event_count = 0
def datachange_notification(self, node, val, data):
self.datachange_count += 1
......@@ -263,30 +263,6 @@ class CommonTests(object):
handle = sub.subscribe_events(v)
sub.delete()
def test_events_deprecated(self):
msclt = MySubHandlerDeprecated()
sub = self.opc.create_subscription(100, msclt)
handle = sub.subscribe_events()
ev = Event(self.srv.iserver.isession)
msg = b"this is my msg "
ev.Message.Text = msg
tid = datetime.utcnow()
ev.Time = tid
ev.Severity = 500
ev.trigger()
clthandle, ev = msclt.future.result()
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.Message.Text, msg)
#self.assertEqual(msclt.ev.Time, tid)
self.assertEqual(ev.Severity, 500)
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
# time.sleep(0.1)
sub.unsubscribe(handle)
sub.delete()
def test_events(self):
msclt = MySubHandler()
sub = self.opc.create_subscription(100, msclt)
......@@ -302,10 +278,10 @@ class CommonTests(object):
ev = msclt.future.result()
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
self.assertEqual(ev.Message.Text, msg)
#self.assertEqual(msclt.ev.Time, tid)
self.assertEqual(ev.Severity, 500)
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
# time.sleep(0.1)
sub.unsubscribe(handle)
......@@ -387,7 +363,7 @@ class CommonTests(object):
def test_utf8(self):
objects = self.opc.get_objects_node()
utf_string = "æøå@%&"
bn = ua.QualifiedName(utf_string, 3)
bn = ua.QualifiedName(utf_string, 3)
nid = ua.NodeId("æølå", 3)
val = "æøå"
v = objects.add_variable(nid, bn, val)
......@@ -747,7 +723,7 @@ class CommonTests(object):
o = self.opc.get_objects_node()
# subscribe to a variable
startv1 = True
startv1 = True
v1 = o.add_variable(3, 'SubscriptionVariableBool', startv1)
sub = self.opc.create_subscription(100, msclt)
handle1 = sub.subscribe_data_change(v1)
......@@ -777,9 +753,9 @@ class CommonTests(object):
msclt = MySubHandler2()
o = self.opc.get_objects_node()
startv1 = True
startv1 = True
v1 = o.add_variable(3, 'SubscriptionVariableMany1', startv1)
startv2 = [1.22, 1.65]
startv2 = [1.22, 1.65]
v2 = o.add_variable(3, 'SubscriptionVariableMany2', startv2)
sub = self.opc.create_subscription(100, msclt)
......@@ -787,7 +763,7 @@ class CommonTests(object):
# Now check we get the start values
nodes = [v1, v2]
count = 0
while not len(msclt.results) > 1:
count += 1
......@@ -804,7 +780,7 @@ class CommonTests(object):
else:
self.fail("Error node {} is neither {} nor {}".format(node, v1, v2))
sub.delete()
sub.delete()
def test_subscribe_server_time(self):
msclt = MySubHandler()
......
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