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.

Reading / writting values via QuickOPC causes RAM usage (GE iFix, VBA)

More
24 Mar 2021 09:39 #9547 by support
Thank you for update. Yes, it is worth trying with the changes you have made, although, frankly, they do not directly address the problem.

QuickOPC (COM) will always be loaded into the process that instantiates its objects - in your case, it will be whatever process is running the VBA interpreter (iFix Workspace according to what you wrote). I guess you can already see that by the fact that it is the memory consumption of that process that is growing (and not some other, separate process). .NET objects exposed to COM via the "interop" mechanism are always like this (in-process servers).

Regards

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

More
23 Mar 2021 10:37 #9541 by bvader
Hi,

thank you for your answers.

Regarding iFix Workspace application, it is a 32-bit application which starts to perform badly / starts crashing when the application itself is using arround 1,2 GB of RAM. These are the words from official GE representative.

Regarding point b), is there any way to find out if QuickOPC gets loaded to the same process as Workspace / VBA is using?

Regarding the notes:

1) Understood. I just made this implementation with "With...End With" for test. Before this change I was creating a new EasyUAClient for every function call (every Sub)
Dim QuickOPCCLient As New EasyUAClient
I have chaged this now and have implemented it as you suggested.
Public QuickOPCCLient As New EasyUAClient
QuickOPCCLient is now a public object which is used by all of the functions and there is no need to create a new EasyUAClient every time it is used. There is only one instance which is "alive" all the time.

2) I have tried clearing these two objects as well but was receiving a VBA error message.

In the Do...While loop I added a Sleep(1000), to wait 1 second before reading the value from the OPC-UA server. This should eliminate the stress from the component. Now we will see how the code performs long term at the customer site.

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

More
22 Mar 2021 16:12 #9532 by support
Hello.

QuickOPC-UA is written in .NET; the COM interface sits on top of it. The memory is managed by .NET. The code is responsible for not leaking memory by keeping references to unused objects, but besides that, there is practically no control over the memory usage. It is not uncommon to see high memory usage, and that - by itself - is not a problem. If there *are* problems, though, they can be caused by

a) A true memory leak in QuickOPC or user code. In this case, there would be a demonstrable, ever-increasing trend, that would end up in consuming all available memory. If this is the case, please demonstrate it by a PerfMon chart showing that ("Private Bytes" of the process). The fact that 1 GB is consumed is not, by itself, a proof of the leak. But you might have mentioned that just as a transient number that would keep growing anyway.

b) A high memory consumption by the .NET part that somehow hampers the operations of the "other" part (QuickOPC gets loaded into the same process as the hosting VBA is using). I suspect that this might be the case here; unfortunately, if so, I cannot think of a solution.

Some notes:

1. I understand your approach with "With/End With" around the EasyUAClient, and there is nothing wrong in principle with it. But as a developer who knows what is going on inside, I would recommend against it. Instead, I would just create one EasyUAClient and keep it around all the time. The repeated creation and (maybe even more) destruction of the EasyUAClient demands computer resources unnecessarily.

2. It is nice that you "clear" the objects/variants by setting them back to Nothing, although (at least when the code is written in this way, i.e. as a function), VBA should do it anyway. But, if you really wanted to be consistent with it, you forgot to clear Arguments(0) and Result(0) where objects are also "sitting".

Regarding the second part of your post: I think you are doing it right, in principle. You could use a subscription for it, but I think it that it would be an overkill for this purpose. I would add some little waiting into the loop, because currently (I hope I am reading it well) it is a "tight" loop where the new read would immediately follow the old one, which is potentially dangerous for CPU and other resource usage.

Best regards

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

More
22 Mar 2021 09:00 #9526 by bvader
Hi,

I am facing issues with high memory usage while executing Read and Write values operations with QuickOPC-UA Client. We are using the application in connection with GE iFix. iFix scripts are written in Visual Basic for Applications 7.1.

We noticed that after a certain amount of time we started getting the "Network transaction in progress" error in iFix. We checked numerous possible reasons for this, because our client was facing poor performance from virtual machines. We first suspected that poor performance of virtual machines was the reason for this error. We conducted additional test on a test sistem in our company where the performance of the computers is not an issued and found out that we can replicated the problem. Above mentioned error message is really a masked Windows system error "Server is Busy....Switch / retry". You can read more about this error here: GE Support

We design SCADA systems and as a part of the system, each command performed on the P&ID screen is recorder to Audit Trail. So if the operator opens a valve on the system an Audi Trail record is generated and stored on the SQL database. The program flow is as follows:

1. User clicks "Activate" on the user form
2. VBA function is called which uses QuickOPC-UA COM component to pass the data to the OPC-UA server.
3. OPC-UA server receives the request and responds (request finished or error message).
4. If there was no error Audit Trail record is stored on the SQL.

We noticed that prior to receiving the "Network transaction in progress" error message RAM usage of the iFix Workspace application (UI environment which hold the VBA code) is very high ad was arrong 1.04 GB at some point. I started isolating the issue and found out if I comment out the call of the AuditTrail function, which usses only QuickOPC-UA functions, I didn't detect any change in RAM usage when the code was executed. If I left the AuditTrail function active, every call of the function caused bump of the RAM usage. After 100, 1000 clicks on the valve and corresponding calls of the AuditTrail function the RAM usage was again significant. If I continued in that matter I started receiving the "Network transaction in progress" error. The OPC-UA server is performing normaly because other clients were working normally.

I thought that we were not handling the QuickOPC-UA component correctly and that something was staying active in the background but as far as I can tell this is not the case. I have changed the usage of the EasyUAClient component by always using the "With" and "End with" sentence. This should clean up any activifty after the use of the component.

For reading of values we are using the code below because we were facing issues with reading old parameter values when the new value was already present on the OPC-UA server. This approach helped (it was response from your support, thanks again).

Public Function API_ReadValue(API_Server As String, API_Parameter_Address As String) As Variant
Dim ReadArguments As New UAReadArguments
Dim Arguments(0) As Variant
Dim Results() As Variant
Dim ReadResult As UAAttributeDataResult
 
    ReadArguments.EndpointDescriptor.UrlString = API_Server
    ReadArguments.NodeDescriptor.NodeId.expandedText = API_Parameter_Address
    ReadArguments.ReadParameters.MaximumAge = 0
 
    Set Arguments(0) = ReadArguments
 
    With New EasyUAClient
        Results = .ReadMultiple(Arguments)
    End With
 
    Set ReadResult = Results(0)
 
    API_ReadValue = ReadResult.AttributeData.Value
 
    Set ReadArguments = Nothing
    Set ReadResult = Nothing
 
End Function


The function below is a validation function. When we send a trigger to the OPC-UA server we are expecting a response in a form of value 2 or value >=10 if there is an error present. if I execute the code below without the "Do While" sentence I don't get the response quickly enough and am always reading value 1 (trigger set) but this is not the issue of the QuickOPC-UA client rather application specifit because the request hasn't been handled yet by the OPA-UA server. Do you have any other suggestions on how to delay the check of the response?

Public Function API_CheckResponse(API_Module As String, API_Parameter As String) As Integer
Dim StartTime As Date
Dim TriggerStatus As Integer
 
    API_CheckResponse = 0
 
    TriggerStatus = API_ReadValue(TRAC_User.TRAC_OPC_UA_Server.CurrentValue, TRAC_User.TRAC_API_Server.CurrentValue & TRAC_User.TRAC_API_Instance.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Module & TRAC_User.TRAC_API_StringEscape.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Parameter & TRAC_User.TRAC_API_StringEscape.CurrentValue)
 
   StartTime = Now
 
    Do While TriggerStatus = 0 Or TriggerStatus = 1
 
    If APITimeout(StartTime) = 1 Then
        MsgBox GetTextBox(485), vbOKOnly + vbExclamation, GetTextBox(113) & "API_CheckResponse"
 
        With New EasyUAClient
            Call .WriteValue(TRAC_User.TRAC_OPC_UA_Server.CurrentValue, TRAC_User.TRAC_API_Server.CurrentValue & TRAC_User.TRAC_API_Instance.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Module & TRAC_User.TRAC_API_StringEscape.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Parameter & TRAC_User.TRAC_API_StringEscape.CurrentValue, 0)
        End With
 
        Exit Function
 
    End If
 
    TriggerStatus = API_ReadValue(TRAC_User.TRAC_OPC_UA_Server.CurrentValue, TRAC_User.TRAC_API_Server.CurrentValue & TRAC_User.TRAC_API_Instance.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Module & TRAC_User.TRAC_API_StringEscape.CurrentValue & TRAC_User.TRAC_API_Delimiter.CurrentValue & TRAC_User.TRAC_API_StringEscape.CurrentValue & API_Parameter & TRAC_User.TRAC_API_StringEscape.CurrentValue)
 
    Loop
 
    API_CheckResponse = TriggerStatus
 
End Function

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

Moderators: support
Time to create page: 0.056 seconds