Commit ea8eeaf2 authored by olivier R-D's avatar olivier R-D

add first test for WhereClause, make Event.SourceName a string as in spec

parent ff3bda51
......@@ -56,7 +56,7 @@ class EventGenerator(object):
source = Node(self.isession, self.event.SourceNode)
self.event.SourceNode = source.nodeid
self.event.SourceName = source.get_browse_name()
self.event.SourceName = source.get_browse_name().Name
source.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(1, ua.VariantType.Byte)))
refs = []
......
......@@ -112,10 +112,12 @@ class MonitoredItemService(object):
result.FilterResult = ua.EventFilterResult()
for _ in params.RequestedParameters.Filter.SelectClauses:
result.FilterResult.SelectClauseResults.append(ua.StatusCode())
# FIXME: where clause result
# TODO: spec says we should check WhereClause here
mdata.where_clause_evaluator = WhereClauseEvaluator(self.logger, self.aspace, mdata.mfilter.WhereClause)
self._commit_monitored_item(result, mdata)
self._monitored_events[params.ItemToMonitor.NodeId] = result.MonitoredItemId
if params.ItemToMonitor.NodeId not in self._monitored_events:
self._monitored_events[params.ItemToMonitor.NodeId] = []
self._monitored_events[params.ItemToMonitor.NodeId].append(result.MonitoredItemId)
return result
def _create_data_change_monitored_item(self, params):
......@@ -147,8 +149,10 @@ class MonitoredItemService(object):
if mid not in self._monitored_items:
return ua.StatusCode(ua.StatusCodes.BadMonitoredItemIdInvalid)
for k, v in self._monitored_events.items():
if v == mid:
self._monitored_events.pop(k)
if mid in v:
v.remove(mid)
if not v:
self._monitored_events.pop(k)
break
for k, v in self._monitored_datachange.items():
if v == mid:
......@@ -182,20 +186,22 @@ class MonitoredItemService(object):
return False
self.logger.debug("%s has subscription for events %s from node: %s",
self, event, event.SourceNode)
mid = self._monitored_events[event.SourceNode]
if mid not in self._monitored_items:
self.logger.debug("Could not find monitored items for id %s for event %s in subscription %s",
mid, event, self)
return False
mdata = self._monitored_items[mid]
if not mdata.where_clause_evaluator.eval(event):
self.logger.debug("Event does not fit WhereClause, not generating event", mid, event, self)
return
fieldlist = ua.EventFieldList()
fieldlist.ClientHandle = mdata.client_handle
fieldlist.EventFields = self._get_event_fields(mdata.mfilter, event)
self.isub.enqueue_event(mid, fieldlist, mdata.parameters.RevisedQueueSize)
return True
mids = self._monitored_events[event.SourceNode]
for mid in mids:
self._trigger_event(event, mid)
def _trigger_event(self, event, mid):
if mid not in self._monitored_items:
self.logger.debug("Could not find monitored items for id %s for event %s in subscription %s", mid, event, self)
return
mdata = self._monitored_items[mid]
if not mdata.where_clause_evaluator.eval(event):
self.logger.debug("Event does not fit WhereClause, not generating event", mid, event, self)
return
fieldlist = ua.EventFieldList()
fieldlist.ClientHandle = mdata.client_handle
fieldlist.EventFields = self._get_event_fields(mdata.mfilter, event)
self.isub.enqueue_event(mid, fieldlist, mdata.parameters.RevisedQueueSize)
def _get_event_fields(self, evfilter, event):
fields = []
......@@ -381,24 +387,43 @@ class WhereClauseEvaluator(object):
def _eval_el(self, index, event):
el = self.elements[index]
if el.FilterOperator == ua.FilterOperator.And:
self.elements(el.FilterOperands[0].Index)
return self._eval_op(el.FilterOperands[0], event) and self._eval_op(el.FilterOperands[1], event)
elif el.FilterOperator == ua.FilterOperator.Or:
return self._eval_op(el.FilterOperands[0], event) or self._eval_el(el.FilterOperands[1], event)
elif el.FilterOperator == ua.FilterOperator.InList:
return self._eval_op(el.FilterOperands[0], event) in [self._eval_op(op, event) for op in el.FilterOperands[1:]]
elif el.FilterOperator == ua.FilterOperator.Equals:
return self._eval_op(el.FilterOperands[0], event) == self._eval_el(el.FilterOperands[1], event)
#ops = [self._eval_op(op, event) for op in el.FilterOperands]
ops = el.FilterOperands # just to make code more readable
if el.FilterOperator == ua.FilterOperator.Equals:
return self._eval_op(ops[0], event) == self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.IsNull:
return self._eval_op(el.FilterOperands[0], event) is None # FIXME: might be too strict
return self._eval_op(ops[0], event) is None # FIXME: might be too strict
elif el.FilterOperator == ua.FilterOperator.GreaterThan:
return self._eval_op(ops[0], event) > self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.LessThan:
return self._eval_op(el.FilterOperands[0], event) < self._eval_el(el.FilterOperands[1], event)
return self._eval_op(ops[0], event) < self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.GreaterThanOrEqual:
return self._eval_op(ops[0], event) >= self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.LessThanOrEqual:
return self._eval_op(el.FilterOperands[0], event) < self._eval_el(el.FilterOperands[1], event)
return self._eval_op(ops[0], event) <= self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.Like:
return self._likeoperator(self._eval_op(ops[0], event), self._eval_el(ops[1], event))
elif el.FilterOperator == ua.FilterOperator.Not:
return not self._eval_op(ops[0], event)
elif el.FilterOperator == ua.FilterOperator.Between:
return self._eval_el(ops[2], event) >= self._eval_op(ops[0], event) >= self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.InList:
return self._eval_op(ops[0], event) in [self._eval_op(op, event) for op in ops[1:]]
elif el.FilterOperator == ua.FilterOperator.And:
self.elements(ops[0].Index)
return self._eval_op(ops[0], event) and self._eval_op(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.Or:
return self._eval_op(ops[0], event) or self._eval_el(ops[1], event)
elif el.FilterOperator == ua.FilterOperator.Cast:
self.logger("Cast operand not implemented")
raise NotImplementError
else:
# TODO: implement missing operators
print("WhereClause not implemented for element: %s", el)
raise NotImplementError
def _like_operator(self, string, pattern):
raise NotImplementError
def _eval_op(self, op, event):
# seems spec says we should return Null if issues
......@@ -414,7 +439,7 @@ class WhereClauseEvaluator(object):
elif type(op) is ua.SimpleAttributeOperand:
if op.BrowsePath:
# we only support depth of 1
return getattr(event, op.BrowsePath[0].BrowseName.Name)
return getattr(event, op.BrowsePath[0].Name)
else:
# TODO: write code for index range.... but doe it make any sense
return self._aspace.get_attribute_value(event.EventType, op.AttributeId).Value.Value
......@@ -422,6 +447,7 @@ class WhereClauseEvaluator(object):
return op.Value.Value
else:
self.logger.warning("Where clause element % is not of a known type", el)
raise NotImplementError
......@@ -324,7 +324,7 @@ class CommonTests(object):
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.EventType, ua.NodeId(ua.ObjectIds.BaseEventType))
self.assertEqual(ev.Severity, 1)
self.assertEqual(ev.SourceName, self.opc.get_server_node().get_display_name().Text)
self.assertEqual(ev.SourceName, self.opc.get_server_node().get_browse_name().Name)
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
self.assertEqual(ev.Message.Text, msg)
self.assertEqual(ev.Time, tid)
......@@ -350,7 +350,7 @@ class CommonTests(object):
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.EventType, ua.NodeId(ua.ObjectIds.BaseEventType))
self.assertEqual(ev.Severity, 1)
self.assertEqual(ev.SourceName, b'MyObject')
self.assertEqual(ev.SourceName, 'MyObject')
self.assertEqual(ev.SourceNode, o.nodeid)
self.assertEqual(ev.Message.Text, msg)
self.assertEqual(ev.Time, tid)
......@@ -373,7 +373,7 @@ class CommonTests(object):
evgen.trigger(tid, msg)
with self.assertRaises(TimeoutError): # we should not receive event
ev = msclt.future.result(10)
ev = msclt.future.result(2)
# time.sleep(0.1)
sub.unsubscribe(handle)
......@@ -401,7 +401,7 @@ class CommonTests(object):
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.EventType, etype.nodeid)
self.assertEqual(ev.Severity, serverity)
self.assertEqual(ev.SourceName, self.opc.get_server_node().get_display_name().Text)
self.assertEqual(ev.SourceName, self.opc.get_server_node().get_browse_name().Name)
self.assertEqual(ev.SourceNode, self.opc.get_server_node().nodeid)
self.assertEqual(ev.Message.Text, msg)
self.assertEqual(ev.Time, tid)
......@@ -434,7 +434,7 @@ class CommonTests(object):
self.assertIsNot(ev, None) # we did not receive event
self.assertEqual(ev.EventType, etype.nodeid)
self.assertEqual(ev.Severity, 1)
self.assertEqual(ev.SourceName, b'MyObject')
self.assertEqual(ev.SourceName, 'MyObject')
self.assertEqual(ev.SourceNode, o.nodeid)
self.assertEqual(ev.Message.Text, msg)
self.assertEqual(ev.Time, tid)
......
......@@ -327,7 +327,7 @@ class TestServer(unittest.TestCase, CommonTests):
def check_eventgenerator_SourceServer(test, evgen):
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(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)
......@@ -335,7 +335,7 @@ def check_eventgenerator_SourceServer(test, evgen):
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(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)
......
This diff is collapsed.
......@@ -9,6 +9,7 @@ from opcua import ua
from opcua.ua import extensionobject_from_binary
from opcua.ua import extensionobject_to_binary
from opcua.ua.uatypes import flatten, get_shape, reshape
from opcua.server.internal_subscription import WhereClauseEvaluator
......@@ -358,7 +359,31 @@ class TestUnit(unittest.TestCase):
n = ua.NodeId(0, 3)
self.assertFalse(n.is_null())
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__':
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