Commit 92999182 authored by ORD's avatar ORD

Merge pull request #179 from FreeOpcUa/evfilter

WhereClause support
parents 94ecade8 8b3323a7
...@@ -23,9 +23,9 @@ class SubHandler(object): ...@@ -23,9 +23,9 @@ class SubHandler(object):
if __name__ == "__main__": if __name__ == "__main__":
#from IPython import embed #from IPython import embed
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
#client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/") client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
client = Client("opc.tcp://olivier:olivierpass@localhost:53530/OPCUA/SimulationServer/") #client = Client("opc.tcp://olivier:olivierpass@localhost:53530/OPCUA/SimulationServer/")
client.set_security_string("Basic256,SignAndEncrypt,certificate-example.der,private-key-example.pem") #client.set_security_string("Basic256,SignAndEncrypt,certificate-example.der,private-key-example.pem")
try: try:
client.connect() client.connect()
root = client.get_root_node() root = client.get_root_node()
......
import sys
sys.path.insert(0, "..")
import logging
from opcua import Client
from opcua import ua
from IPython import embed
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.EventType)
if __name__ == "__main__":
#from IPython import embed
logging.basicConfig(level=logging.WARN)
client = Client("opc.tcp://localhost:53530/OPCUA/SimulationServer/")
#client = Client("opc.tcp://olivier:olivierpass@localhost:53530/OPCUA/SimulationServer/")
try:
client.connect()
root = client.get_root_node()
print("Root is", root)
handler = SubHandler()
sub = client.create_subscription(500, handler)
handle = sub.subscribe_events(evtype=2788)
# refresh server condition to force generation of events
cond = root.get_child(["0:Types", "0:EventTypes", "0:BaseEventType", "0:ConditionType"])
cond.call_method("0:ConditionRefresh", ua.Variant(sub.subscription_id, ua.VariantType.UInt32))
embed()
finally:
client.disconnect()
...@@ -53,10 +53,12 @@ if __name__ == "__main__": ...@@ -53,10 +53,12 @@ if __name__ == "__main__":
try: try:
# time.sleep is here just because we want to see events in UaExpert # time.sleep is here just because we want to see events in UaExpert
import time import time
for i in range(1, 10): count = 0
time.sleep(10) while True:
myevgen.trigger(message="This is MyFirstEvent with MyNumericProperty and MyStringProperty.") time.sleep(2)
mysecondevgen.trigger(message="This is MySecondEvent with MyIntProperty and MyBoolProperty.") myevgen.trigger(message="MyFirstEvent " + str(count))
mysecondevgen.trigger(message="MySecondEvent " + str(count))
count += 1
embed() embed()
finally: finally:
......
...@@ -211,22 +211,56 @@ class Subscription(object): ...@@ -211,22 +211,56 @@ class Subscription(object):
def _get_filter_from_event_type(self, eventtype): def _get_filter_from_event_type(self, eventtype):
eventtype = self._get_node(eventtype) eventtype = self._get_node(eventtype)
evfilter = ua.EventFilter() evfilter = ua.EventFilter()
for property in get_event_properties_from_type_node(eventtype): evfilter.SelectClauses = self._select_clauses_from_evtype(eventtype)
op = ua.SimpleAttributeOperand() evfilter.WhereClause = self._where_clause_from_evtype(eventtype)
op.TypeDefinitionId = eventtype.nodeid
op.AttributeId = ua.AttributeIds.Value
op.BrowsePath = [property.get_browse_name()]
evfilter.SelectClauses.append(op)
return evfilter return evfilter
def subscribe_events(self, sourcenode=ua.ObjectIds.Server, evtype=ua.ObjectIds.BaseEventType): def _select_clauses_from_evtype(self, evtype):
clauses = []
for prop in get_event_properties_from_type_node(evtype):
op = ua.SimpleAttributeOperand()
op.TypeDefinitionId = evtype.nodeid
op.AttributeId = ua.AttributeIds.Value
op.BrowsePath = [prop.get_browse_name()]
clauses.append(op)
return clauses
def _where_clause_from_evtype(self, evtype):
cf = ua.ContentFilter()
el = ua.ContentFilterElement()
# operands can be ElementOperand, LiteralOperand, AttributeOperand, SimpleAttribute
op = ua.SimpleAttributeOperand()
op.TypeDefinitionId = evtype.nodeid
op.BrowsePath.append(ua.QualifiedName("EventType", 0))
op.AttributeId = ua.AttributeIds.Value
el.FilterOperands.append(op)
for subtypeid in [st.nodeid for st in self._get_subtypes(evtype)]:
op = ua.LiteralOperand()
op.Value = ua.Variant(subtypeid)
el.FilterOperands.append(op)
el.FilterOperator = ua.FilterOperator.InList
cf.Elements.append(el)
return cf
def _get_subtypes(self, parent, nodes=None):
if nodes is None:
nodes = [parent]
for child in parent.get_children(refs=ua.ObjectIds.HasSubtype):
nodes.append(child)
self._get_subtypes(child, nodes)
return nodes
def subscribe_events(self, sourcenode=ua.ObjectIds.Server, evtype=ua.ObjectIds.BaseEventType, evfilter=None):
""" """
Subscribe to events from a node. Default node is Server node. Subscribe to events from a node. Default node is Server node.
In most servers the server node is the only one you can subscribe to. In most servers the server node is the only one you can subscribe to.
if evfilter is provided, evtype is ignored
Return a handle which can be used to unsubscribe Return a handle which can be used to unsubscribe
""" """
sourcenode = self._get_node(sourcenode) sourcenode = self._get_node(sourcenode)
evfilter = self._get_filter_from_event_type(evtype) if evfilter is None:
evfilter = self._get_filter_from_event_type(evtype)
return self._subscribe(sourcenode, ua.AttributeIds.EventNotifier, evfilter) return self._subscribe(sourcenode, ua.AttributeIds.EventNotifier, evfilter)
def _subscribe(self, nodes, attr, mfilter=None, queuesize=0): def _subscribe(self, nodes, attr, mfilter=None, queuesize=0):
......
...@@ -56,7 +56,7 @@ class EventGenerator(object): ...@@ -56,7 +56,7 @@ class EventGenerator(object):
source = Node(self.isession, self.event.SourceNode) source = Node(self.isession, self.event.SourceNode)
self.event.SourceNode = source.nodeid self.event.SourceNode = source.nodeid
self.event.SourceName = source.get_display_name().Text self.event.SourceName = source.get_browse_name().Name
source.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(1, ua.VariantType.Byte))) source.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(1, ua.VariantType.Byte)))
refs = [] refs = []
......
This diff is collapsed.
...@@ -4,12 +4,13 @@ from opcua import Client ...@@ -4,12 +4,13 @@ from opcua import Client
from opcua import Server from opcua import Server
from opcua import ua from opcua import ua
from tests_subscriptions import SubscriptionTests
from tests_common import CommonTests, add_server_methods from tests_common import CommonTests, add_server_methods
port_num1 = 48510 port_num1 = 48510
class TestClient(unittest.TestCase, CommonTests): class TestClient(unittest.TestCase, CommonTests, SubscriptionTests):
''' '''
Run common tests on client side Run common tests on client side
......
This diff is collapsed.
import unittest import unittest
from tests_common import CommonTests, add_server_methods, MySubHandler
import os import os
import shelve import shelve
import time import time
from tests_common import CommonTests, add_server_methods
from tests_subscriptions import SubscriptionTests
from datetime import timedelta, datetime from datetime import timedelta, datetime
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
...@@ -17,7 +19,7 @@ port_num = 485140 ...@@ -17,7 +19,7 @@ port_num = 485140
port_discovery = 48550 port_discovery = 48550
class TestServer(unittest.TestCase, CommonTests): class TestServer(unittest.TestCase, CommonTests, SubscriptionTests):
''' '''
Run common tests on server side Run common tests on server side
...@@ -327,7 +329,7 @@ class TestServer(unittest.TestCase, CommonTests): ...@@ -327,7 +329,7 @@ class TestServer(unittest.TestCase, CommonTests):
def check_eventgenerator_SourceServer(test, evgen): def check_eventgenerator_SourceServer(test, evgen):
server = test.opc.get_server_node() server = test.opc.get_server_node()
test.assertEqual(evgen.event.SourceName, server.get_display_name().Text) test.assertEqual(evgen.event.SourceName, server.get_browse_name().Name)
test.assertEqual(evgen.event.SourceNode, ua.NodeId(ua.ObjectIds.Server)) test.assertEqual(evgen.event.SourceNode, ua.NodeId(ua.ObjectIds.Server))
test.assertEqual(server.get_attribute(ua.AttributeIds.EventNotifier).Value, ua.Variant(1, ua.VariantType.Byte)) test.assertEqual(server.get_attribute(ua.AttributeIds.EventNotifier).Value, ua.Variant(1, ua.VariantType.Byte))
refs = server.get_referenced_nodes(ua.ObjectIds.GeneratesEvent, ua.BrowseDirection.Forward, ua.NodeClass.ObjectType, False) refs = server.get_referenced_nodes(ua.ObjectIds.GeneratesEvent, ua.BrowseDirection.Forward, ua.NodeClass.ObjectType, False)
...@@ -335,7 +337,7 @@ def check_eventgenerator_SourceServer(test, evgen): ...@@ -335,7 +337,7 @@ def check_eventgenerator_SourceServer(test, evgen):
def check_event_generator_object(test, evgen, obj): def check_event_generator_object(test, evgen, obj):
test.assertEqual(evgen.event.SourceName, obj.get_display_name().Text) test.assertEqual(evgen.event.SourceName, obj.get_browse_name().Name)
test.assertEqual(evgen.event.SourceNode, obj.nodeid) test.assertEqual(evgen.event.SourceNode, obj.nodeid)
test.assertEqual(obj.get_attribute(ua.AttributeIds.EventNotifier).Value, ua.Variant(1, ua.VariantType.Byte)) test.assertEqual(obj.get_attribute(ua.AttributeIds.EventNotifier).Value, ua.Variant(1, ua.VariantType.Byte))
refs = obj.get_referenced_nodes(ua.ObjectIds.GeneratesEvent, ua.BrowseDirection.Forward, ua.NodeClass.ObjectType, False) refs = obj.get_referenced_nodes(ua.ObjectIds.GeneratesEvent, ua.BrowseDirection.Forward, ua.NodeClass.ObjectType, False)
......
This diff is collapsed.
...@@ -9,6 +9,7 @@ from opcua import ua ...@@ -9,6 +9,7 @@ from opcua import ua
from opcua.ua import extensionobject_from_binary from opcua.ua import extensionobject_from_binary
from opcua.ua import extensionobject_to_binary from opcua.ua import extensionobject_to_binary
from opcua.ua.uatypes import flatten, get_shape, reshape from opcua.ua.uatypes import flatten, get_shape, reshape
from opcua.server.internal_subscription import WhereClauseEvaluator
...@@ -358,7 +359,31 @@ class TestUnit(unittest.TestCase): ...@@ -358,7 +359,31 @@ class TestUnit(unittest.TestCase):
n = ua.NodeId(0, 3) n = ua.NodeId(0, 3)
self.assertFalse(n.is_null()) self.assertFalse(n.is_null())
self.assertTrue(n.has_null_identifier()) self.assertTrue(n.has_null_identifier())
def test_where_clause(self):
cf = ua.ContentFilter()
el = ua.ContentFilterElement()
op = ua.SimpleAttributeOperand()
op.BrowsePath.append(ua.QualifiedName("property", 2))
el.FilterOperands.append(op)
for i in range(10):
op = ua.LiteralOperand()
op.Value = ua.Variant(i)
el.FilterOperands.append(op)
el.FilterOperator = ua.FilterOperator.InList
cf.Elements.append(el)
wce = WhereClauseEvaluator(logging.getLogger(__name__), None, cf)
ev = ua.BaseEvent()
ev._freeze = False
ev.property = 3
self.assertTrue(wce.eval(ev))
if __name__ == '__main__': if __name__ == '__main__':
logging.basicConfig(level=logging.WARN) logging.basicConfig(level=logging.WARN)
......
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