FreeOpcUa / opcua-asyncio

OPC UA library for python >= 3.7
GNU Lesser General Public License v3.0
1.11k stars 358 forks source link

Question: How to filter events on subscription with where_clause #1568

Open HaraldBrueggemann opened 8 months ago

HaraldBrueggemann commented 8 months ago

Hi,

I have a server which publishes events derived by BaseEventType. Each event is also prioritized by the "Severity" Attribute of the event.

In the client application I currently want to react on events filtered by the eventtype and the severity.

Currently i try to subscribe to the event with the following code example:

def subscribe(client: object, type_name: str, node: ua.Node, handler: object) -> None:
        _type_node = client.nodes.base_event_type.get_child([f"0:{type_name}"])

        _where_clause = ua.ContentFilter()
        _where_clause.Elements.append(__get_filter_element_type(_type_node.nodeid))
        _where_clause.Elements.append(__get_filter_element_severity(200))

        _event_filter = ua.EventFilter()
        _event_filter.SelectClauses.append(__get_attribute_operand_valueNodeId(client))
        _event_filter.WhereClause = _where_clause

        params = ua.CreateSubscriptionParameters()
        params.RequestedPublishingInterval = 100
        params.RequestedLifetimeCount = 300
        params.RequestedMaxKeepAliveCount = 100

        subscription = client.create_subscription(params, handler)
        event_handle = subscription.subscribe_events(evtypes=_type_node.nodeid, evfilter=_event_filter)

def __get_filter_element_type(type_node_id: ua.NodeId) -> ua.ContentFilterElement:
        el = ua.ContentFilterElement()

        el.FilterOperator = ua.FilterOperator.OfType        
        op = ua.LiteralOperand(Value=ua.Variant(type_node_id))
        el.FilterOperands.append(op)

        return el

def __get_filter_element_severity(severity: int) -> ua.ContentFilterElement:
        el = ua.ContentFilterElement()
        el.FilterOperator = ua.FilterOperator.GreaterThanOrEqual

        op = ua.SimpleAttributeOperand()
        op.BrowsePath.append(ua.QualifiedName("Severity", 0))
        op.AttributeId = ua.AttributeIds.Value
        op.TypeDefinitionId = ua.NodeId(ua.ObjectIds.BaseEventType)

        op2 = ua.LiteralOperand(Value=ua.Variant(severity))

        el.FilterOperands.append(op)
        el.FilterOperands.append(op2)

        return el

The above code works with only the first element in the ContentFilter list. All following entries in the list are ignored. It looks like that an operator like and, or not... is missing in the contentfilter.

I can not find any example or code snippet for solving that problem. I only found examples with a single element in the contentfilter.

Am i totally doing it wrong? Any help is appreciated.

Thanks and Regards Harald

Python-Version: 3.10.12 opcua-asyncio Version: master-branch

schroeder- commented 7 months ago

First some server maynot support the complete eventfilter specification. So this could be an issue. Also we just send the Filter as you specify it to the server, so you need to read the OPC UA specification and follow it. From the spec see https://reference.opcfoundation.org/Core/Part4/v104/docs/7.4.1 :

The filter is evaluated by evaluating the first entry in the element array starting with the first operand in the operand array. The operands of an element may contain References to sub-elements resulting in the evaluation continuing to the referenced elements in the element array.

HaraldBrueggemann commented 7 months ago

Hi Schroeder,

thanks for your reply.

After reading the referenced link, my life is actually not easier. I still can`t figure out, how to use multiple filter element connected by logical operands. Therefore, I reworked my code to use only one filter element.

An example for filtering events by attributes, would be appreciated in the future.

Regards, Harald