Commit 5106825a authored by Dario Ernst's avatar Dario Ernst Committed by oroulet

Separate events’ emitting nodes from their SourceNodes

!! Warning, API-breaking changes!

When closely reading the spec, events are emitted by one node, and refer
to their SourceNode in an attribute — the SourceNode can and often will
be different from the emitting node.

Until now, python-opcua assumed these two to be identical. This commit
tries to straighten this situation.

To that end, we introduce new parameters in the eventgenerator,
Event-class and all children thereof. The EventGenerator will now try to
honor emitting nodes given to it, but still handle and add SourceNodes.
The InternalSubscription finally serializing and emitting the event is
adapted to this new behavior.
parent a2fbf4b7
This diff is collapsed.
......@@ -21,17 +21,18 @@ class Event:
add properties using the add_property method!!!
"""
def __init__(self):
def __init__(self, emitting_node=ua.ObjectIds.Server):
self.server_handle = None
self.select_clauses = None
self.event_fields = None
self.data_types = {}
self.emitting_node = emitting_node
# save current attributes
self.internal_properties = list(self.__dict__.keys())[:] + ["internal_properties"]
def __str__(self):
return "{0}({1})".format(
self.__class__.__name__,
self.__class__.__name__,
[str(k) + ":" + str(v) for k, v in self.__dict__.items() if k not in self.internal_properties])
__repr__ = __str__
......
......@@ -4,7 +4,7 @@ import uuid
from opcua import ua
from opcua import Node
from ..common import get_event_obj_from_type_node, BaseEvent
from opcua.common import events, event_object
__all__ = ["EventGenerator"]
......@@ -27,11 +27,9 @@ class EventGenerator:
self.isession = isession
self.event = None
async def init(self, etype=None):
if not etype:
etype = BaseEvent()
node = None
if isinstance(etype, BaseEvent):
async def init(self, etype=None, emitting_node=ua.ObjectIds.Server):
if isinstance(etype, event_objects.BaseEvent):
self.event = etype
elif isinstance(etype, Node):
node = etype
......@@ -40,41 +38,36 @@ class EventGenerator:
else:
node = Node(self.isession, ua.NodeId(etype))
if node:
self.event = await get_event_obj_from_type_node(node)
self.event = await events.get_event_obj_from_type_node(node)
async def set_source(self, source=ua.ObjectIds.Server):
if isinstance(source, Node):
if isinstance(emitting_node, Node):
pass
elif isinstance(source, ua.NodeId):
source = Node(self.isession, source)
elif isinstance(emitting_node, ua.NodeId):
emitting_node = Node(isession, emitting_node)
else:
source = Node(self.isession, ua.NodeId(source))
if self.event.SourceNode:
if source.nodeid != self.event.SourceNode:
self.logger.warning(
"Source NodeId: '%s' and event SourceNode: '%s' are not the same. Using '%s' as SourceNode",
str(source.nodeid), str(self.event.SourceNode), str(self.event.SourceNode))
source = Node(self.isession, self.event.SourceNode)
emitting_node = Node(isession, ua.NodeId(emitting_node))
self.event.SourceNode = source.nodeid
self.event.SourceName = (await source.get_browse_name()).Name
if not self.event.SourceNode:
self.event.SourceNode = emitting_node.nodeid
self.event.SourceName = (await emitting_node.get_browse_name()).Name
await source.set_event_notifier([ua.EventNotifier.SubscribeToEvents])
await emitting_node.set_event_notifier([ua.EventNotifier.SubscribeToEvents])
refs = []
ref = ua.AddReferencesItem()
ref.IsForward = True
ref.ReferenceTypeId = ua.NodeId(ua.ObjectIds.GeneratesEvent)
ref.SourceNodeId = source.nodeid
ref.SourceNodeId = emitting_node.nodeid
ref.TargetNodeClass = ua.NodeClass.ObjectType
ref.TargetNodeId = self.event.EventType
refs.append(ref)
results = await self.isession.add_references(refs)
# result.StatusCode.check()
self.emitting_node = emitting_node
def __str__(self):
return "EventGenerator(Type:{0}, Source:{1}, Time:{2}, Message: {3})".format(self.event.EventType,
self.event.SourceNode,
return "EventGenerator(Type:{0}, Emitting Node:{1}, Time:{2}, Message: {3})".format(self.event.EventType,
self.emitting_node,
self.event.Time,
self.event.Message)
__repr__ = __str__
......
......@@ -213,14 +213,13 @@ class MonitoredItemService:
return False
def trigger_event(self, event):
# with self._lock:
if event.SourceNode not in self._monitored_events:
if event.emitting_node not in self._monitored_events:
self.logger.debug("%s has no subscription for events %s from node: %s",
self, event, event.SourceNode)
self, event, event.emitting_node)
return False
self.logger.debug("%s has subscription for events %s from node: %s",
self, event, event.SourceNode)
mids = self._monitored_events[event.SourceNode]
self, event, event.emitting_node)
mids = self._monitored_events[event.emitting_node]
for mid in mids:
self._trigger_event(event, mid)
......
......@@ -403,16 +403,14 @@ class Server:
uries = await self.get_namespace_array()
return uries.index(uri)
async def get_event_generator(self, etype=None, source=ua.ObjectIds.Server):
async def get_event_generator(self, etype=None, emitting_node=ua.ObjectIds.Server):
"""
Returns an event object using an event type from address space.
Use this object to fire events
"""
if not etype:
etype = BaseEvent()
ev_gen = EventGenerator(self.iserver.isession)
await ev_gen.init(etype)
await ev_gen.set_source(source)
await ev_gen.init(etype, emitting_node=emitting_node)
return ev_gen
def create_custom_data_type(self, idx, name, basetype=ua.ObjectIds.BaseDataType, properties=None) -> Coroutine:
......
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