Commit fcffcb04 authored by Christoph Ziebuhr's avatar Christoph Ziebuhr Committed by oroulet

Automatically add ConditionId (NodeId) to select_clauses

when subscribing to ConditionType
parent 3552cccf
...@@ -3,7 +3,7 @@ from typing import Dict, List, TYPE_CHECKING ...@@ -3,7 +3,7 @@ from typing import Dict, List, TYPE_CHECKING
from asyncua import ua from asyncua import ua
import asyncua import asyncua
from ..ua.uaerrors import UaError from ..ua.uaerrors import UaError
from .ua_utils import get_node_subtypes from .ua_utils import get_node_subtypes, is_subtype
if TYPE_CHECKING: if TYPE_CHECKING:
from asyncua.common.node import Node from asyncua.common.node import Node
...@@ -178,10 +178,19 @@ async def _select_clause_from_childs(child: "Node", refs: List[ua.ReferenceDescr ...@@ -178,10 +178,19 @@ async def _select_clause_from_childs(child: "Node", refs: List[ua.ReferenceDescr
async def select_clauses_from_evtype(evtypes: List["Node"]): async def select_clauses_from_evtype(evtypes: List["Node"]):
select_clauses = [] select_clauses = []
already_selected = {} already_selected = {}
add_condition_id = False
for evtype in evtypes: for evtype in evtypes:
if not add_condition_id and await is_subtype(evtype, ua.NodeId(ua.ObjectIds.ConditionType)):
add_condition_id = True
refs = await select_event_attributes_from_type_node(evtype, lambda n: n.get_references(ua.ObjectIds.Aggregates, ua.BrowseDirection.Forward, ua.NodeClass.Object | ua.NodeClass.Variable, True, _BROWSE_MASK)) refs = await select_event_attributes_from_type_node(evtype, lambda n: n.get_references(ua.ObjectIds.Aggregates, ua.BrowseDirection.Forward, ua.NodeClass.Object | ua.NodeClass.Variable, True, _BROWSE_MASK))
if refs: if refs:
await _select_clause_from_childs(evtype, refs, select_clauses, already_selected, []) await _select_clause_from_childs(evtype, refs, select_clauses, already_selected, [])
if add_condition_id:
# also request ConditionId, which is not modelled as a component of the ConditionType
op = ua.SimpleAttributeOperand()
op.AttributeId = ua.AttributeIds.NodeId
op.TypeDefinitionId = ua.NodeId(ua.ObjectIds.ConditionType)
select_clauses.append(op)
return select_clauses return select_clauses
......
...@@ -273,17 +273,7 @@ class Subscription: ...@@ -273,17 +273,7 @@ class Subscription:
MaxUInt32 for max queue size MaxUInt32 for max queue size
:return: Handle for changing/cancelling of the subscription :return: Handle for changing/cancelling of the subscription
""" """
sourcenode = Node(self.server, sourcenode) return await self.subscribe_events(sourcenode, evtypes, evfilter, queuesize)
if evfilter is None:
evfilter = await self._create_eventfilter(evtypes)
# Add SimpleAttribute for NodeId if missing.
matches = [a for a in evfilter.SelectClauses if a.AttributeId == ua.AttributeIds.NodeId]
if not matches:
conditionIdOperand = ua.SimpleAttributeOperand()
conditionIdOperand.TypeDefinitionId = ua.NodeId(ua.ObjectIds.ConditionType)
conditionIdOperand.AttributeId = ua.AttributeIds.NodeId
evfilter.SelectClauses.append(conditionIdOperand)
return await self._subscribe(sourcenode, ua.AttributeIds.EventNotifier, evfilter, queuesize=queuesize) # type: ignore
async def _subscribe( async def _subscribe(
self, self,
......
...@@ -211,6 +211,17 @@ async def get_node_supertype(node): ...@@ -211,6 +211,17 @@ async def get_node_supertype(node):
return None return None
async def is_subtype(node, supertype):
"""
return if a node is a subtype of a specified nodeid
"""
while node:
if node.nodeid == supertype:
return True
node = await get_node_supertype(node)
return False
async def is_child_present(node, browsename): async def is_child_present(node, browsename):
""" """
return if a browsename is present a child from the provide node return if a browsename is present a child from the provide node
......
...@@ -39,6 +39,9 @@ class EventGenerator: ...@@ -39,6 +39,9 @@ class EventGenerator:
if node: if node:
self.event = await events.get_event_obj_from_type_node(node) self.event = await events.get_event_obj_from_type_node(node)
if isinstance(self.event, event_objects.Condition):
# Add ConditionId, which is not modelled as a component of the ConditionType
self.event.add_property('NodeId', None, ua.VariantType.NodeId)
if isinstance(emitting_node, Node): if isinstance(emitting_node, Node):
pass pass
......
...@@ -601,6 +601,16 @@ async def test_get_event_attributes_from_type_node_AlarmConditionType(opc): ...@@ -601,6 +601,16 @@ async def test_get_event_attributes_from_type_node_AlarmConditionType(opc):
assert child in allVariables assert child in allVariables
async def test_get_filter(opc):
auditType = opc.opc.get_node(ua.ObjectIds.AuditEventType)
baseType = opc.opc.get_node(ua.ObjectIds.BaseEventType)
properties = await baseType.get_properties()
properties.extend(await auditType.get_properties())
evfilter = await asyncua.common.events.get_filter_from_event_type([auditType])
# Check number of elements in select clause
assert len(evfilter.SelectClauses) == len(properties)
async def test_get_filter_from_ConditionType(opc): async def test_get_filter_from_ConditionType(opc):
condType = opc.opc.get_node(ua.ObjectIds.ConditionType) condType = opc.opc.get_node(ua.ObjectIds.ConditionType)
baseType = opc.opc.get_node(ua.ObjectIds.BaseEventType) baseType = opc.opc.get_node(ua.ObjectIds.BaseEventType)
...@@ -613,13 +623,15 @@ async def test_get_filter_from_ConditionType(opc): ...@@ -613,13 +623,15 @@ async def test_get_filter_from_ConditionType(opc):
subproperties.extend(await var.get_properties()) subproperties.extend(await var.get_properties())
evfilter = await asyncua.common.events.get_filter_from_event_type([condType]) evfilter = await asyncua.common.events.get_filter_from_event_type([condType])
# Check number of elements in select clause # Check number of elements in select clause
assert len(evfilter.SelectClauses) == (len(properties) + len(variables) + len(subproperties)) assert len(evfilter.SelectClauses) == (len(properties) + len(variables) + len(subproperties) + 1)
# Check browse path variable with property # Check browse path variable with property
browsePathList = [o.BrowsePath for o in evfilter.SelectClauses if o.BrowsePath] browsePathList = [o.BrowsePath for o in evfilter.SelectClauses if o.BrowsePath]
browsePathEnabledState = [ua.uatypes.QualifiedName("EnabledState")] browsePathEnabledState = [ua.uatypes.QualifiedName("EnabledState")]
browsePathEnabledStateId = [ua.uatypes.QualifiedName("EnabledState"), ua.uatypes.QualifiedName("Id")] browsePathEnabledStateId = [ua.uatypes.QualifiedName("EnabledState"), ua.uatypes.QualifiedName("Id")]
assert browsePathEnabledState in browsePathList assert browsePathEnabledState in browsePathList
assert browsePathEnabledStateId in browsePathList assert browsePathEnabledStateId in browsePathList
# Check for additional NodeId attribute, which is not directly contained in ConditionType
assert len([o for o in evfilter.SelectClauses if o.AttributeId == ua.AttributeIds.NodeId]) == 1
# Check some subtypes in where clause # Check some subtypes in where clause
alarmType = opc.opc.get_node(ua.ObjectIds.AlarmConditionType) alarmType = opc.opc.get_node(ua.ObjectIds.AlarmConditionType)
systemType = opc.opc.get_node(ua.ObjectIds.SystemOffNormalAlarmType) systemType = opc.opc.get_node(ua.ObjectIds.SystemOffNormalAlarmType)
......
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