mikakaraila / node-red-contrib-opcua

A Node-RED node to communicate OPC UA. Uses node-opcua library.
Other
212 stars 196 forks source link

Server don`t update subscription variable with Python opcua-asyncio when subscription sampling interval is set to 0 #526

Closed BBSGotling closed 1 year ago

BBSGotling commented 1 year ago

Hello!

After updating a client with the Python opcua-asyncio library to a version > 1.0.0 the node-red-contrib-opcua server doesn`t update the subscription variables at a value change. They have changed the default subscription sampling interval to 0 in the opcua-asyncio library. In the opc-ua documentation indicates 0 that the server should use the fastest practical rate.

It can be fixed in the 104-opcuaserver.js when the minimumSamplingInterval is set to value for example 500ms:

addressSpace.getOwnNamespace().addVariable({
                componentOf: vendorName,
                nodeId: "ns=1;s=FreeMemory",
                browseName: "FreeMemory",
                dataType: "Double",
                minimumSamplingInterval: 500,
                value: {
                    get: function () {
                        return new opcua.Variant({
                            dataType: opcua.DataType.Double,
                            value: available_memory()
                        });
                    }
                }
            });

Reproduce with Python script: https://github.com/FreeOpcUa/opcua-asyncio/blob/master/examples/client-subscription.py sampling_interval=0

node_handles = await subscription.subscribe_data_change(
        nodes=nodes, # a list of nodes i want to subscribe to
        attr=ua.AttributeIds.Value, # the attribute i am interested in
        queuesize=50, # the queuesize should be bigger then the number of changes within a publishinterval, in this case 50 valuechanges per 1000 ms
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=0 # <-------!
    )

The Python client have to be the only one with subscription. When a other client have a subscription with a sampling interval 1000ms or so the error could not reproduced.

mikakaraila commented 1 year ago

I expect @AndreasHeine should look this one...

AndreasHeine commented 1 year ago

subscription = await client.create_subscription(0, handler) -> RequestedPublishingInterval=0 for the Subscription

https://reference.opcfoundation.org/v104/Core/docs/Part4/5.13.2/

This interval defines the cyclic rate that the Subscriptionis being requested to return Notificationsto the Client. This interval is expressed in milliseconds. This interval is represented by the publishing timer in the Subscriptionstate table (see 5.13.1.2).

The negotiated value for this parameter returned in the response is used as the default sampling interval for MonitoredItemsassigned to this Subscription.

If the requested value is 0 or negative, the Servershall revise with the fastest supported publishing interval.


minimumSamplingInterval: 500,

https://reference.opcfoundation.org/v104/Core/docs/Part4/5.12.2/ https://reference.opcfoundation.org/Core/Part4/v104/docs/7.16

referes to the "samplingInterval" of a monitored item

The interval that defines the fastest rate at which the MonitoredItem(s) should be accessed and evaluated. This interval is defined in milliseconds.

The value 0 indicates that the Servershould use the fastest practical rate.

The value -1 indicates that the default sampling interval defined by the publishing interval of the Subscriptionis requested. A different sampling interval is used if the publishing interval is not a supported sampling interval. Any negative number is interpreted as -1. The sampling interval is not changed if the publishing interval is changed by a subsequent call to the ModifySubscription Service.

The Serveruses this parameter to assign the MonitoredItemsto a sampling interval that it supports.

The assigned interval is provided in the revisedSamplingIntervalparameter. The Server shall always return a revisedSamplingIntervalthat is equal or higher than the requestedsamplingInterval. If the requestedsamplingIntervalis higher than the maximum sampling interval supported by the Server, the maximum sampling interval is returned.


thats kind of confusing because your question refers to a mix of servicesets...

AndreasHeine commented 1 year ago

if you want to use tha sampling interval of the monitored item the 0 must be here

    subscription = await client.create_subscription(
                    period=1000, # the client will send each 1000 ms a publishrequest and the server responds with the changes since last publishrequest
                    handler=handler, # SubscriptionHandler which will be used for processing the notifications in the publishresponse
                    publishing=True
    )

    node_handles = await subscription.subscribe_data_change(
        nodes=nodes, # a list of nodes i want to subscribe to
        attr=ua.AttributeIds.Value, # the attribute i am interested in
        queuesize=50, # the queuesize should be bigger then the number of changes within a publishinterval, in this case 50 valuechanges per 1000 ms
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=0 # <-------!
    )

subscription = await client.create_subscription(0, handler) this just referes to the subscription polling/publishing interval for changes

so there is a difference between transport (subscription) and server checking the variable for changes (monitored item)

AndreasHeine commented 1 year ago

@mikakaraila after some reviewing of sourcecode

Subscription-PublishingInterval: https://github.com/node-opcua/node-opcua/blob/master/packages/node-opcua-server/source/server_subscription.ts#L64-L72

MonitoredItem-SamplingInterval: https://github.com/node-opcua/node-opcua/blob/master/packages/node-opcua-server/source/server_subscription.ts#L943-L986

but i cant spot a error... look correct

so if i do:

    subscription = await client.create_subscription(
                    period=0,
                    handler=handler,
                    publishing=True
    )

i expect to get revised from server to 50ms https://github.com/node-opcua/node-opcua/blob/e1ed70a684fc69130696e001a9d43280df1466f4/packages/node-opcua-server/source/server_subscription.ts#L473

and if i do:

    node_handles = await subscription.subscribe_data_change(
        nodes=nodes,
        attr=ua.AttributeIds.Value,
        queuesize=50,
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=0
    )

i expect to get revised to "MinimumSamplingInterval" of the UaVariable if value is 0 of the UaVariable it should revise to 50ms https://github.com/node-opcua/node-opcua/blob/e1ed70a684fc69130696e001a9d43280df1466f4/packages/node-opcua-server/source/monitored_item.ts#L364

BBSGotling commented 1 year ago

The error will show when the variable nodeId: "ns=1;s=FreeMemory" from the default Address space at the node-red Server is used. I will write a detailed descripton tomorrow.

BBSGotling commented 1 year ago

Sorry for the confusing first post. I hope, I will clear it a little bit with this.

To reproduce the error:

Then I use this script:

subscription = await client.create_subscription(
                    period=1000,
                    handler=handler,
                    publishing=True
    )

and

node_handles = await subscription.subscribe_data_change(
        nodes=nodes,
        attr=ua.AttributeIds.Value,
        queuesize=50,
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=0
    )

I get back the revisedPublishingIntervall=1000 and only one value. No actualized value when the data change.

With this:

subscription = await client.create_subscription(
                    period=0,
                    handler=handler,
                    publishing=True
    )

I get back the revisedPublishingIntervall=50 and also only one value. No actualized value when the data change.

With sampling_interval greater than 0 and period=1000:

node_handles = await subscription.subscribe_data_change(
        nodes=nodes,
        attr=ua.AttributeIds.Value,
        queuesize=50,
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=500
    )

I get back the revisedPublishingIntervall=50 and actualized value when the data change.

When I change the period to 1000ms it also works.

subscription = await client.create_subscription(
                    period=1000,
                    handler=handler,
                    publishing=True
    )

Get back revisedPublishingIntervall=1000 and actualized value when the data change.

When I add minimumSamplingInterval: 500 in the node-red-contrib-opcua server:

addressSpace.getOwnNamespace().addVariable({
                componentOf: vendorName,
                nodeId: "ns=1;s=FreeMemory",
                browseName: "FreeMemory",
                dataType: "Double",
                minimumSamplingInterval: 500,
                value: {
                    get: function () {
                        return new opcua.Variant({
                            dataType: opcua.DataType.Double,
                            value: available_memory()
                        });
                    }
                }
            });

and

subscription = await client.create_subscription(
                    period=0,
                    handler=handler,
                    publishing=True
    )

and

node_handles = await subscription.subscribe_data_change(
        nodes=nodes,
        attr=ua.AttributeIds.Value,
        queuesize=50,
        monitoring=ua.MonitoringMode.Reporting,
        sampling_interval=0
    )

It works with no problem, get back revisedPublishingIntervall=500 and actualized value when the data change.

mikakaraila commented 1 year ago

Seems a bit strange as in the node-opcua you don´t have to define minimumSamplingInterval as it is optional. Only if you want to limit it with some special variables then it should be used, not in normal cases. @erossignon any comments?

AndreasHeine commented 1 year ago

@mikakaraila it is not really optional! i guess the defaults just refer to some limitations of the sampling interval implementation. below 50ms it does not make sense to sample then the exception based reporting is more efficient!

mikakaraila commented 1 year ago

@AndreasHeine Take a look how Etienne has used addVariable, I found only one case that uses minimumSamplingInterval. I expect here is something more... or I have misunderstood how it should be used.

AndreasHeine commented 1 year ago

@AndreasHeine Take a look how Etienne has used addVariable, I found only one case that uses minimumSamplingInterval. I expect here is something more... or I have misunderstood how it should be used.

you define the sampling interval for analog values or those where the datasource cant be querried that hard like in eventdriven... every time the getter gets called to not overwhelm the source!

BBSGotling commented 1 year ago

I have found a closed issue in the node-opcua repository with the same problem: https://github.com/node-opcua/node-opcua/issues/1133

They have add the minimumSamplingInterval in the example Server.

mikakaraila commented 1 year ago

Seems that @erossignon should update also other examples... I will add minimumSamplingInterval to those variables that are not meant to be event based. Boolean variables are normally event based.

mikakaraila commented 1 year ago

Server code addVariable etc. added needed minimumSamplingInterval, please test ad reopen if not working.