Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
O
opcua-asyncio
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Nikola Balog
opcua-asyncio
Commits
ea8eeaf2
Commit
ea8eeaf2
authored
May 08, 2016
by
olivier R-D
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add first test for WhereClause, make Event.SourceName a string as in spec
parent
ff3bda51
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
630 additions
and
39 deletions
+630
-39
opcua/server/event.py
opcua/server/event.py
+1
-1
opcua/server/internal_subscription.py
opcua/server/internal_subscription.py
+57
-31
tests/tests_common.py
tests/tests_common.py
+5
-5
tests/tests_server.py
tests/tests_server.py
+2
-2
tests/tests_subscriptions.py
tests/tests_subscriptions.py
+540
-0
tests/tests_unit.py
tests/tests_unit.py
+25
-0
No files found.
opcua/server/event.py
View file @
ea8eeaf2
...
...
@@ -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
=
[]
...
...
opcua/server/internal_subscription.py
View file @
ea8eeaf2
...
...
@@ -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
tests/tests_common.py
View file @
ea8eeaf2
...
...
@@ -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
)
...
...
tests/tests_server.py
View file @
ea8eeaf2
...
...
@@ -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
)
...
...
tests/tests_subscriptions.py
0 → 100644
View file @
ea8eeaf2
This diff is collapsed.
Click to expand it.
tests/tests_unit.py
View file @
ea8eeaf2
...
...
@@ -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
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment