Professional OPC
Development Tools

logos

Online Forums

Technical support is provided through Support Forums below. Anybody can view them; you need to Register/Login to our site (see links in upper right corner) in order to Post questions. You do not have to be a licensed user of our product.

Please read Rules for forum posts before reporting your issue or asking a question. OPC Labs team is actively monitoring the forums, and replies as soon as possible. Various technical information can also be found in our Knowledge Base. For your convenience, we have also assembled a Frequently Asked Questions page.

Do not use the Contact page for technical issues.

"SubscribeDataChange" using the read function

More
16 Dec 2019 14:05 #8082 by Moien
Thanks again for your response.
I was on a business trip for two weeks and now I am back in the office. We want to put an order for your OPC Client license, but first we should clarify this issue completely.
Would you please define some new variables on your OPC Server using the string format as the Node-ID instead of integer?
We need the following variables:
1. 32 bit Interger
2. 64 bit Integer
3. a double variable
4. a float variable
5. a sting variable
6. a boolean variable

Then we can test those variables on your OPC Server and see if it is working correctly using string Node-IDs.

Best regards,
Moien

Please Log in or Create an account to join the conversation.

More
30 Nov 2019 08:11 #8054 by support
Hello.

thanks for update. Regarding the type of Node IDs: I do not think the differences in type (integer versus string) are responsible for any significant performance differences in our tests. QuickOPC supports all 4 types (integer, string, GUID and opaque). But the client must use whatever the uses chose for its node IDs. The demo server we have on Web is built from OPC Foundation sample code, and it happened to have all Node IDs as inetgers, so we are using that as it is. If the string node IDs are what your Softing server has, then you need to use them in your client - that's clear, and fine.

In the tests I described, my client was on 1Gbit local network, but connected through the Internet to a server that is in Microsoft Azure (hosted in Netherlands I think). The measured speed of the Internet connection is around 6Mbps.

Best regards

Please Log in or Create an account to join the conversation.

More
29 Nov 2019 10:12 #8053 by Moien
Thank you so much for your detailed response and sending the example code.

We have used your Demo server (opc.tcp://opcua.demo-this.com:51210/UA/SampleServer) and ran a code of ours. We took a variable and wrote continuously into that variable every second. Using the read function we tried to read this variable out. We got the runtine values every second. We used the following OPC variable at your Demo server:
Object.Data.Static.Scalar.Int16Value. We used your node defintion as Node ID = 'ns=2; i=10219'. The value of this variable has been incremented every second. This test has been done successfully. This means the read function delivers new values every second without using any extra functions like subscription.

In our case (Softing OPC UA Server) and I believe in most of industrial cases instead of item number of node ID (nsu=namespaceUri;i=integer), item name as string is used (nsu=namespaceUri;s=string). Numbers for this defintion are not optimal because if you change the number of OPC items, you have to change all the numbering order. Moreover, numbers are not the best way to describe and represent the variables. We haven't find any example on your OPC Server which is using (nsu=namespaceUri;s=string) format.
In your website (opclabs.doc-that.com/files/onlinedocs/QuickOpc/Latest/User%2...OPC/OPC%20UA%20Node%20IDs.html) it is mentioned that the expanded node ID string (nsu=namespaceUri;s=string) is supported by OPCLabs.

The server for testing our application is nearly the same as yours. We have one CPU with two virtual machines. We are using 100 Mbit/s network. I am wondering if you are using a faster network compared to ours (1Gbit/s?).

Best regards,
Moien

Please Log in or Create an account to join the conversation.

More
28 Nov 2019 11:12 #8052 by support
Dear Sir,

I have made two test programs in Delphi with QuickOPC.
The first one subscribes to 300 variables from Softing OPC UA .NET Demo Server, with sampling interval 1000 ms. The vast majority of variables has a change for each sampling period. The other program reads the same 300 variables from the same server repeatedly, and sleeps 1000 ms between the reads.

I have then measured the CPU usage. It was done on a quite poor virtual machine, with just 2 virtual CPUs. The server was on a network (on a remote machine).

The program with subscriptions took between 4-9% CPU, with average around 5%.
The program with repeated reads took between 0-2% CPU, with average below 1%.

To me, the numbers are satisfactory and I do not see any problem. It was a surprise to see that the subscriptions took more CPU than the reads, as I have expected it the other way round. It is something we may have look in the future.

Out of curiosity, I have also looked at the server machine. The server's process always stayed almost always below 1% CPU.

I am copying my two example below. If you are seeing very different results with the CPU usage, then you should determine what is different from the tests that I have done.
// This example shows how to subscribe to 300 monitored items from Softing OPC UA .NET Demo Server.
 
type
  TClientEventHandlers1 = class
    FailureCount: Cardinal;
    SuccessCount: Cardinal;
    procedure OnDataChangeNotification(
      ASender: TObject;
      sender: OleVariant;
      const eventArgs: _EasyUADataChangeNotificationEventArgs);
  end;
 
procedure TClientEventHandlers1.OnDataChangeNotification(
  ASender: TObject;
  sender: OleVariant;
  const eventArgs: _EasyUADataChangeNotificationEventArgs);
begin
  // Process the data.
  if eventArgs.Succeeded then
    SuccessCount := SuccessCount + 1
  else
    FailureCount := FailureCount + 1;
end;
 
class procedure Softing_OpcUaNetDemoServer.SubscribeMany;
const
  EndpointDescriptorUrlString = 'opc.tcp://TEST-OPC-3:51510/UA/DemoServer';
  NamespaceUriString = 'nsu=http://opcfoundation.org/Quickstarts/ReferenceApplications';
var
  Arguments: OleVariant;
  Client: TEasyUAClient;
  ClientEventHandlers: TClientEventHandlers1;
  HandleArray: OleVariant;
  I: Cardinal;
  MonitoredItemArguments1, MonitoredItemArguments2, MonitoredItemArguments3: _EasyUAMonitoredItemArguments;
  MonitoringParameters: _UAMonitoringParameters;
begin
  // Instantiate the client object and hook events
  Client := TEasyUAClient.Create(nil);
  ClientEventHandlers := TClientEventHandlers1.Create;
  Client.OnDataChangeNotification := ClientEventHandlers.OnDataChangeNotification;
 
  //
  MonitoringParameters := CoUAMonitoringParameters.Create;
  MonitoringParameters.SamplingInterval := 1000;
  Arguments := VarArrayCreate([0, 299], varVariant);
  for I := 0 to 99 do
  begin
    MonitoredItemArguments1 := CoEasyUAMonitoredItemArguments.Create;
    MonitoredItemArguments1.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    MonitoredItemArguments1.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;s=/Dynamic/All Profiles/Scalar Mass/Boolean/Boolean%.3d', [I]);
    MonitoredItemArguments1.MonitoringParameters := MonitoringParameters;
 
    MonitoredItemArguments2 := CoEasyUAMonitoredItemArguments.Create;
    MonitoredItemArguments2.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    MonitoredItemArguments2.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;ns=2;s=/Dynamic/All Profiles/Scalar Mass/Int16/Int16%.3d', [I]);
    MonitoredItemArguments2.MonitoringParameters := MonitoringParameters;
 
    MonitoredItemArguments3 := CoEasyUAMonitoredItemArguments.Create;
    MonitoredItemArguments3.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    MonitoredItemArguments3.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;ns=2;s=/Dynamic/All Profiles/Scalar Mass/Float/Float%.3d', [I]);
    MonitoredItemArguments3.MonitoringParameters := MonitoringParameters;
 
    Arguments[I] := MonitoredItemArguments1;
    Arguments[100 + I] := MonitoredItemArguments2;
    Arguments[200 + I] := MonitoredItemArguments3;
  end;
 
  WriteLn('Subscribing...');
  TVarData(HandleArray).VType := varArray or varVariant;
  TVarData(HandleArray).VArray := PVarArray(Client.SubscribeMultipleMonitoredItems(Arguments));
 
  WriteLn('Processing monitored item changed events for 60 seconds...');
  PumpSleep(60*1000);
 
  WriteLn('Unsubscribing...');
  Client.UnsubscribeAllMonitoredItems;
 
  WriteLn('Waiting for 5 seconds...');
  Sleep(5*1000);
 
  WriteLn;
  WriteLn(Format('Success count: %d', [ClientEventHandlers.SuccessCount]));
  WriteLn(Format('Failure count: %d', [ClientEventHandlers.FailureCount]));
  WriteLn(Format('Total count: %d', [ClientEventHandlers.SuccessCount + ClientEventHandlers.FailureCount]));
 
  WriteLn;
  WriteLn('Finished.');
  FreeAndNil(Client);
  FreeAndNil(ClientEventHandlers);
// This example shows how to repeatedly read 300 variables from Softing OPC UA .NET Demo Server.
 
class procedure Softing_OpcUaNetDemoServer.RepeatedReadMany;
const
  EndpointDescriptorUrlString = 'opc.tcp://TEST-OPC-3:51510/UA/DemoServer';
  NamespaceUriString = 'nsu=http://opcfoundation.org/Quickstarts/ReferenceApplications';
var
  Arguments: OleVariant;
  Client: TEasyUAClient;
  FailureCount: Cardinal;
  SuccessCount: Cardinal;
  I, J: Cardinal;
  ReadArguments1, ReadArguments2, ReadArguments3, ReadArguments4: _UAReadArguments;
  Result: _UAAttributeDataResult;
  Results: OleVariant;
begin
  // Instantiate the client object
  Client := TEasyUAClient.Create(nil);
 
  //
  Arguments := VarArrayCreate([0, 299], varVariant);
  for I := 0 to 99 do
  begin
    ReadArguments1 := CoUAReadArguments.Create;
    ReadArguments1.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    ReadArguments1.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;s=/Dynamic/All Profiles/Scalar Mass/Boolean/Boolean%.3d', [I]);
 
    ReadArguments2 := CoUAReadArguments.Create;
    ReadArguments2.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    ReadArguments2.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;ns=2;s=/Dynamic/All Profiles/Scalar Mass/Int16/Int16%.3d', [I]);
 
    ReadArguments3 := CoUAReadArguments.Create;
    ReadArguments3.EndpointDescriptor.UrlString := EndpointDescriptorUrlString;
    ReadArguments3.NodeDescriptor.NodeId.ExpandedText := NamespaceUriString +
      Format(' ;ns=2;s=/Dynamic/All Profiles/Scalar Mass/Float/Float%.3d', [I]);
 
    Arguments[I] := ReadArguments1;
    Arguments[100 + I] := ReadArguments2;
    Arguments[200 + I] := ReadArguments3;
  end;
 
  WriteLn;
  WriteLn('Read the variables and wait 1 second, 60 times...');
  for J := 1 to 60 do
  begin
    TVarData(Results).VType := varArray or varVariant;
    TVarData(Results).VArray := PVarArray(Client.ReadMultiple(Arguments));
 
    SuccessCount := 0;
    FailureCount := 0;
    for I := 0 to 299 do
       if Results[I].Succeeded then
         SuccessCount := SuccessCount + 1
       else
         FailureCount := FailureCount + 1;
    WriteLn(Format('Success count: %d, failure count: %d', [SuccessCount, FailureCount]));
 
    PumpSleep(1000);
  end;
 
  WriteLn;
  WriteLn('Finished.');
  FreeAndNil(Client);

Note: I have used the recent version of QuickOPC (2019.2) which has been released just this Monday. There should be no considerable difference to earlier versions with performance, though. But there is a slight change in the way array arguments are passed to COM, so you may have to use the old syntax if you are using older version. For example, what in my example is:
TVarData(HandleArray).VArray := PVarArray(Client.SubscribeMultipleMonitoredItems(Arguments));
was earlier
TVarData(HandleArray).VArray := PVarArray(
    Client.SubscribeMultipleMonitoredItems(PSafeArray(TVarData(Arguments).VArray)));
(the newer version requires no PSafeArray).

Best regards

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 15:34 #8049 by support
I want to ask one more thing:

Weren't your tests (for the problem with the Read) with some kind of simulated tags, or something similar? Or were they reflecting some real, persistent values such as the PLC variable, or actuator value?

I can imagine that some servers may keep the simulated values only for a duration of a session (= connection). And QuickOPC disconnects automatically after some time (I think the default is 5 seconds) if the server is not in use for other operations.

Thank you

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 14:42 #8048 by support
Hello.

If I get a reproducible scenario where the Read does not work as it should, I would be able to investigate in details and find the cause. I think it is most likely either in a mistake in the app code, or in the UA server, but of course I cannot completely rule out a bug in QuickOPC.

The recommended approach to get continuous value updates over some period of time is to use subscriptions (and no reads).
The second possible approach is to Read periodically (and no subscription should be necessary). This is *much* less efficient, but in principle should work as well.

So we have two things to solve:
- Why the Read does not return the right value. This has been tested so thoroughly that I do not see any point in us trying again. You need to some me your code, or precise procedure in which that happens.
- High CPU consumption. As I said, I will do some experiment here and reply here then.

Best regards

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 13:59 #8047 by Moien
A colleague of mine and I we are both using your OPC UA Client and we both faced into this problem (Read function does not deliver the run time values on the OPC UA Server properly) independent of each other. We are using the OPC UA Server from "Softing". I suggest that you also test it once more time using a third party OPC UA Server to see if it really works for you. I am going to test it one more time using the examples from your sides to assure that I am doing every step right. And then we will communicate regarding the results!
Moreover, you said at the very first beginning that I have to use the subscription instead of read in order to get the right values in a continuous period of time. Now you are saying that Read function should deliver the run time values! To be honest I am a little bit confused now!
Anyway, we are trying to find a solution and I hope we get some at the end of the day.

Regards,
Moien

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 13:16 #8046 by support
I did not know the Read does not work for you well, until now. In your post that started this forum thread, you wrote "I used the "SubscribeDataChange" function (every 1000 milliseconds) to get the new values every second, otherwise the values won't be updated in my Delphi application.", and you complained about the CPU usage, but have not mentioned that the "otherwise" refers to the situation in which you are using repeated Reads without a subscription.

There is no need to get upset. If you think you spend too much time explaining things, it is because before understanding you well, there is no chance to help you.

The fact that the subsequent Reads do not return the correct values would be, of course, wrong. And that is where we need to look first. I can assure that this *should* work well, without having to set up a subscription "on a side". Many users are using it reliably, and I have just tested it and it works for me. I therefore do not understand when you write "you can test it on your own test OPC UA Server. Please do that and you get it what I mean! Just define a node ID and try to write different values into that variable. At the same time you can try to read this value and print it out. You will see no changes in this variable. The variable value will be the first value that you wrote into that NodeID". That is not what I am observing. If this is happening to you, please send a full code example that shows the problem (with one variable, and to our test server, as you wrote). I am afraid that there is something else in play, possibly some overlooked fact on your side/in the code.

I still plan to do the CPU usage test with 300/400 variables as you suggested.

Regards

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 12:35 #8045 by Moien
And if you have tired for one variable please try to increase the number of variables (something like 300 or 400 variables). Please follow the changes in CPU usage and you will see what I mean. That's the whole idea of all of my messages here!

Best regards,
Moien

Please Log in or Create an account to join the conversation.

More
27 Nov 2019 12:16 #8043 by Moien
As a very simple test you can test it on your own test OPC UA Server. Please do that and you get it what I mean! Just define a node ID and try to write different values into that variable. At the same time you can try to read this value and print it out. You will see no changes in this variable. The variable value will be the first value that you wrote into that NodeID!!!
If you wanna see the changes you have to use Subscription instead of read function. This is not the read function that we know since 12 years from our OPC DA Client.

Regards,
Moien

Please Log in or Create an account to join the conversation.

Moderators: support
Time to create page: 0.089 seconds