- Posts: 46
- Thank you received: 0
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.
- Forum
- Discussions
- QuickOPC-UA in .NET
- Reading, Writing, Subscriptions
- "SubscribeDataChange" using the read function
"SubscribeDataChange" using the read function
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.
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.
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.
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));
TVarData(HandleArray).VArray := PVarArray(
Client.SubscribeMultipleMonitoredItems(PSafeArray(TVarData(Arguments).VArray)));
Best regards
Please Log in or Create an account to join the conversation.
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.
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.
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.
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.
Best regards,
Moien
Please Log in or Create an account to join the conversation.
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.
- Forum
- Discussions
- QuickOPC-UA in .NET
- Reading, Writing, Subscriptions
- "SubscribeDataChange" using the read function