-
Problems with data exchange beatween forms
Hello all,
I am trying to make an application, that gets data from OPC server to form1 and displays some pieces in form2. The app works that way: on start - form1 starts. Form2 must be started manually after form form1 is statrted (i mad a buttton for that). If some values in OPC server changes i need to see some of them in form2. Here is a sub which shows how do i get data from OPC server.
Code:
Public mObject As List (Of Object)
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
Dim i As Integer = 0
Dim j As Integer = 1
Dim k As Integer = 0
If dwCount > 0 Then
mObject = New List(Of Object)
For i = 0 To jk - 1
For j = 1 To jk
If phClientItems(i) = j Then
mObject.Add(pvValues(k))
k += 1
End If
Next j
Next i
End If
dwCount = 0
End Sub
I have tried to write line before sub end
Code:
Dim c As Integer
For c=0 To mObject.Count - 1
Form2.mObject.Add(Object(c))
Next c
but nothing happened when i opened form2, nor on change of data in OPC server (i wrote the code that on form2 load all values are shown in msgbox, message box did not show up).
dwCounts - how many values were changed in PLC.
phClientItems - variable index.
pvValues - variable value.
All the ideas are wellcome.
Thank you in advance.
-
Re: Problems with data exchange beatween forms
That may do things you completely do not want, and there are several possible issues.
1) Since you say Form2.whatever, that strongly suggests that you are using the default instance there. I thoroughly detest the default instance, as it causes more confusion than any possible benefit. I'm not quite sure what behavior you saw, but what the default instances may trip you up. Are you displaying the default instance of Form2, or are you displaying a different instance.
2) You are passing around lots of reference types, such as when you add the object to the list from the array of object argument. That will copy the address of the item at the array index, but not the item itself. This could be correct, depending on how you are creating that array of object, but is that what you want to do? You then copy that address across to Form2. In that case, it probably will work, as the List is re-created each time the sub is run.
-
Re: Problems with data exchange beatween forms
Well, i am trying to do so that data from OPC server are populated in datagrid on form2. I do not have any problems with displaying data in datagrid on form1 :
Code:
Public mObject As List (Of Object)
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
Dim i As Integer = 0
Dim j As Integer = 1
Dim k As Integer = 0
If dwCount > 0 Then
mObject = New List(Of Object)
For i = 0 To jk - 1
For j = 1 To jk
If phClientItems(i) = j Then
DataGridView1.Rows(j-1).Cells(1).Value=Convert.ToString(pvValues(k))
mObject.Add(pvValues(k))
k += 1
End If
Next j
Next i
End If
dwCount = 0
But i need to do so that those values are displayed in form2 datagrid. I was thinking to make a public sub in form2, something like:
Code:
Public Sub xxx()
Dim a As Integer
For a=0 to jk
DataGridView1.Rows(a).Cells(1).Value=Convert.TosString(mObject(i))
Next a
End Sub
But then what? Or maybe the way i am trying to do it is not correct, i have no ideas.
-
Re: Problems with data exchange beatween forms
Well, just to be sure, that data comes i put a button on form2 and wrote code in its click event
Code:
Dim i As Integer
For i=0 To Form1.mObject.Count - 1
DataGridView1.Rows(i).Cells(1).Value=Form1.mObject(i)
Next i
and datagrid in form2 was filled with values from OPC server. How to make this code working without clicking of button? I can do it on form2 load, but it will work only on form2 loading.
-
Re: Problems with data exchange beatween forms
You dont want it to happen when the user clicks a button and you dont want it to happen when the form loads, so when do you want it to happen?
-
Re: Problems with data exchange beatween forms
Is that OnDataChange running in a background thread?
-
Re: Problems with data exchange beatween forms
@chris128, this is a real time monitoring, i need to run that code for form2 when changes happens in OPC server.
@Shaggy Hiker, i am not sure what means "running in background", but if data of an active item in active group changes, the server calls that sub OnDataChange().
-
Re: Problems with data exchange beatween forms
Quote:
i need to run that code for form2 when changes happens in OPC server.
Well then you need some way of knowing when changed happen in OPC server - which I assume is what this OnDataChange event that you mentioned is for. So stick the code in the event handler for that OnDataChange event.
-
Re: Problems with data exchange beatween forms
Well chris, i might be do not express myself clear enough. When some changes in OPC server occours then OPC server calls that sub OnDataChange(). This sub by default looks like that:
Code:
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
End Sub
So, inside that sub i put this code to take values from OPC server and populate them in forms1 datagrid.
Code:
Dim i As Integer = 0
Dim j As Integer = 1
Dim k As Integer = 0
If dwCount > 0 Then
mObject = New List(Of Object)
For i = 0 To jk - 1
For j = 1 To jk
If phClientItems(i) = j Then
DataGridView1.Rows(j-1).Cells(1).Value=Convert.ToString(pvValues(k))
k += 1
End If
Next j
Next i
End If
And each time some changes happens - values in forms1 datagrid changes as well (this sub is in form1). But i also want to see same chaneges in forms2 datagrid.
This sub (OnDataChange) is a part of IOPCDataCallBack which i implement in form1 class.
-
Re: Problems with data exchange beatween forms
Then you need to tell form2 to do it as well - you could create a new method on Form2 called something like DataChanged and have that do the code that is required to fill Form2's datagrid, then in your OnDataChange sub on Form1 you call that DataChanged method on Form2.
Personally I would try and implement this outside of your forms (ie in a dedicated class) but depending on how this OPC component works that may not be possible in this situation
-
Re: Problems with data exchange beatween forms
Well, i am not good in VB so maybe this will help you more to undrstand what i am trying to do, and at that time i will try to create a new method and put its call into OnDataChange sub.
Code:
The basic sequence of the individual program steps corresponds to the previous sample
OPC custom interface, asynchronous communication . These include establishing the
connection to the OPC server, creating a group with variables, and reading and writing
values for an item. The following table shows the steps to be executed by the program:
Step Description
1 Select .NET components
2 Convert the ProgID to a CLSID
3 Establish connection to the OPC server
4 Create an OPC group
5 Adding Items
6 Request the interface IConnectionPointContainer
7 Request the interface IOPCAsyncIO2
8 Create callback object
9 Connect OPC server and callback object of the client
10 Execute required write and read operations
11 Receive notifications of the OPC server
12 Delete objects and release memory
Step 1: Select .NET components
To use the Custom Interface for OPC Data Access with .NET, you must use the Runtime
Callable Wrapper (RCW) described above in your Visual Basic project. In the sample project,
the components OpcRcw.Da and OpcRcw.Comn have already been added.
If these do not appear in the selection, you can also select them using the Browse button
from the following folder
<installationpath>\SIEMENS\SIMATIC.NET\opc2\bin .
Sample programs
5.4 OPC Custom interface (asynchronous communication) in VB.NET
Industrial Communication with PG/PC Volume 2 - Interfaces
Programming Manual, Release 06/2008, C79000-G8976-C197-08 421
Figure 5-25 Selecting the Runtime Callable Wrapper (RCW) in the Visual Basic project
With the commands
Imports OpcRcw.Comn
Imports OpcRcw.Da
it is simple to use the name space and methods of the OPC Custom interface with .NET
Framework.
Step 2: Convert the ProgID to a type
To identify it, each COM server has a ProgID that is assigned to a worldwide unique type.
This is obtained with the GetTypeFromProgID() function. The ProgID of the OPC server of
SIMATIC NET is L"OPC.SimaticNET ":
Dim typeofOPCserver As Type =
Type.GetTypeFromProgID(L"OPC.SimaticNET")
Step 3: Establish connection to the OPC server
The CreateInstance() function of the Activator class generates an instance of the class with a
previously specified type:
m_server = Activator.CreateInstance(typeofOPCserver)
The result of this section of program is an interface variable m_server of the type
IOPCServer.
Sample programs
5.4 OPC Custom interface (asynchronous communication) in VB.NET
Industrial Communication with PG/PC Volume 2 - Interfaces
422 Programming Manual, Release 06/2008, C79000-G8976-C197-08
Step 4: Create an OPC group
The IOPCServer interface has the AddGroup() method for creating groups:
m_server.AddGroup("MyOPCGroup",
0,
250,
1,
pTimeBias,
pDeadband,
LOCALE_ID,
ServerGroup,
RevisedUpdateRate,
GetType(IOPCGroupStateMgt).GUID,
m_group)
The result of this program section is a group with the specified name and the required
properties. AddGroup() also returns a variable m_group as return parameter, an interface to
a group object, in this case IOPCGroupStateMgt. The IOPCGroupStateMgt interface is
necessary to allow use of the SetState() methods. The SetState() method is required later to
activate and deactivate a group.
By means of a simple assignment, the m_item variable of the type IOPCItemMgt can be
derived from the m_group interface variable.
m_item = m_group
Step 5: Adding Items
The IOPCItemMgt interface has the AddItems() method for creating OPC items:
m_item.AddItems(2, itemdefs, Results, pErrors)
The result of this program section is that the server adds two items with the properties
specified in the itemdefs parameter. The variables of the result structure Results (server
handle, data type of the item on the target system etc.) are also initialized.
Since the results are written to unmanaged memory by the underlying COM interface, the
access from managed .NET code must make use of the marshaling functions:
result = Marshal.PtrToStructure(pos, GetType(OPCITEMRESULT))
ServerHandle1 = result.hServer
pos = New IntPtr(pos.ToInt32() +
Marshal.SizeOf(GetType(OPCITEMRESULT)))
result = Marshal.PtrToStructure(pos, GetType(OPCITEMRESULT))
ServerHandle2 = result.hServer
The client must also make sure that the unmanaged memory is released:
Marshal.FreeCoTaskMem(Results)
Marshal.FreeCoTaskMem(pErrors)
Step 6: Request the interface IConnectionPointContainer
The m_ConnectionContainer variable of the type IConnectionPointContainer can be derived
from the m_group variable.
m_ConnectionContainer = m_group
The interface is required to locate the IConnectionPoint interface.
Sample programs
5.4 OPC Custom interface (asynchronous communication) in VB.NET
Industrial Communication with PG/PC Volume 2 - Interfaces
Programming Manual, Release 06/2008, C79000-G8976-C197-08 423
Step 7: Request the IOPCAsyncIO2 interfaces
The m_asyncIO2 variable of the type IOPCAsyncIO2 can be derived from the m_group
variable.
m_asyncIO2 = m_group
This interface provides methods for asynchronous reading and writing of values.
Step 8: Create callback object
To allow the OPC server to return a result of asynchronous operations, the
IOPCDataCallback interface must be implemented on the client.
Implements IOPCDataCallback
The connection of the IConnectionPoint interface and IOPCDataCallback of the OPC server
is established using the FindConnectionPoint() method of the IConnectionPointContainer
interface. This generates a callback object for asynchronous calls.
m_ConnectionContainer.FindConnectionPoint(
GetType(IOPCDataCallback).GUID,
m_ConnectionPoint)
Step 9: Connect OPC server and callback object of the client
The connection between the callback object of the OPC server and the client application is
established by the Advise() method. The return variable m_dwCookie is an ID for the
connection.
m_ConnectionPoint.Advise(Me, m_dwCookie)
Step 10: Execute required write or read operation
The Read() and Write() methods that were generated in Step 7 can be accessed over the
IOPCAsyncIO2 interface:
m_asyncIO2.Read(1, pServer, Transaction, CancelID, pErrors)
Step 11: Receive notifications of the OPC server
If the data of an active item in an active group changes, the server calls the OnDataChange()
method of the callback object. On completion of a read operation, the server calls the
OnReadComplete() method, on completion of a write operation, the OnWriteComplete()
method is called. The server passes the return values as parameters to the method.
Overridable Sub OnDataChange(
ByVal dwTransid As Integer,
ByVal hGroup As Integer,
ByVal hrMasterquality As Integer,
ByVal hrMastererror As Integer,
ByVal dwCount As Integer,
ByVal phClientItems() As Integer,
ByVal pvValues() As Object,
ByVal pwQualities() As Short,
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME,
ByVal pErrors() As Integer) Implements
IOPCDataCallback.OnDataChange
Sample programs
5.5 OPC Custom interface (synchronous communication) in C#
Industrial Communication with PG/PC Volume 2 - Interfaces
424 Programming Manual, Release 06/2008, C79000-G8976-C197-08
Step 12: Delete objects and release memory
Before exiting the program, the OPC objects that have been created must be deleted and
the memory reserved for them must be released. The relevant functions are integral parts of
the previously used interfaces. Unadvise() terminates the connection between the OPC
server and the callback object:
m_ConnectionPoint.Unadvise(m_dwCookie)
Marshal.ReleaseComObject(m_ConnectionPoint)
m_item.RemoveItems(2, pItems, pErrors)
....
Marshal.FreeCoTaskMem(pErrors)
m_server.RemoveGroup(ServerGroup, True)
Releasing memory
When using COM, it is sometimes necessary for the client to release memory that was
requested by the server. The rules for this are as follows:
● [out]: The memory for a parameter with this attribute will be requested from the server.
● [in, out]: The memory for such a parameter will be requested from the client. The server
can release the memory again and reallocate it.
● [in]: The client will request the memory. The server is not responsible for releasing the
memory.
In all three cases, the reserved memory of unmanaged COM objects should be released by
the client.
Thank you for your patience :)
-
Re: Problems with data exchange beatween forms
OK, i made sub in form2:
Code:
Public sub xxx ( ByVal example as object)
ListBox1.Items.Add(example)
MsgBox(example)
End Sub
And changed code in forms1 OnDataChange sub:
Code:
Public mObject As List(Of Objects) = New List(Of Objects)
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
Try
Dim i As Integer = 0
Dim j As Integer = 1
Dim k As Integer = 0
If dwCount > 0 Then
mObject = New List(Of Object)
For i = 0 To jk - 1
For j = 1 To jk
If phClientItems(i) = j Then
DataGridView1.Rows(j-1).Cells(1).Value=Convert.ToString(pvValues(k))
mObject.Add(pvValues(k))
k += 1
End If
Next j
Next i
End If
Finally
Form2.xxx(mObject(0))
End Try
End Sub
Each time when data changes in OPC server - forms1 datagrid gets new data, message box shows correct value, but my forms2 ListBox remains empty. Now i am really out of imagination.
-
Re: Problems with data exchange beatween forms
I notice you are using the default form instance there (as you just used Form2.xxx instead of creating a new instance of Form2 then calling xxx on that instance). Are you sure that the Form2 you are viewing on the screen is the default instance as well?
Also, have you placed that call to Form2.xxx in the Finally block of your Try/Catch code just to test with? because I dont think it is going to give you the results you expect when it is only getting called once
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
chris128
I notice you are using the default form instance there (as you just used Form2.xxx instead of creating a new instance of Form2 then calling xxx on that instance). Are you sure that the Form2 you are viewing on the screen is the default instance as well?
Yes, i think so, code of form2
Code:
Option Strict Off
Option Explicit On
Imports System.IO
Public Class Form2
Public mObject As List(Of Object) = New List(Of Object)
Private Sub Form2_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
DataGridView1.Columns(0).HeaderText = "Parameter"
DataGridView1.Columns(1).HeaderText = "Value"
DataGridView1.Columns(2).HeaderText = "Unit"
Dim sr As StreamReader = New StreamReader("txt\viens.txt")
Dim line As String = Nothing
Do
line = sr.ReadLine()
If Not line = Nothing Then
Dim mArray(2) As String
mArray(0) = Split(line, " ")(0)
mArray(1) = ""
mArray(2) = Split(line, " ")(1)
DataGridView1.Rows.Add(mArray)
End If
Loop Until line Is Nothing
Dim i As Integer
For i = 0 To Form1.mObject.Count - 1
DataGridView1.Rows(Form1.mObject1(i) - 1).Cells(1).Value = (Form1.mObject(i))
ListBox1.Items.Add(Form1.mObject(i))
Next i
End Sub
Public Sub xxx(ByVal example As String)
ListBox1.Items.Add(example)
End Sub
End Class
Quote:
Originally Posted by
chris128
Also, have you placed that call to Form2.xxx in the Finally block of your Try/Catch code just to test with? because I dont think it is going to give you the results you expect when it is only getting called once
It looks silly, but i put it there as i do not know where else i could put it :blush:
-
Re: Problems with data exchange beatween forms
How are you displaying form 2? That would show us whether you are using the default instance or not.
-
Re: Problems with data exchange beatween forms
If you mean how do i open form2 then here is a code in form1:
Code:
Private Sub Form2ToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Form2ToolStripMenuItem.Click
Form2.Show()
End Sub
-
Re: Problems with data exchange beatween forms
Well, that's the default instance, so the sub call should be correct the way you have it.
The problem would be if you had declared an instance of Form2 with something like:
Private myFrm2 as New Form2
I notice that you are putting together a list of objects in the data arrival method, but only passing over the first object, but then, you are also just adding one item, so it is probably nearly what you want to pass.
However, since you are passing an object to a sub that is expecting a String, you probably have Option Strict Off. You may find that what is coming across is not at all what you expect to see.
The next step would be to put a breakpoint in the sub on Form2 where you add the item to the listbox. If the breakpoint is reached, execution will pause, and you can look to see what is in the argument to the sub.
After doing that, you will have these possibilities:
1) The breakpoint is never reached.
2) The breakpoint is reached, but the argument is an empty string.
3) The breakpoint is reached, and the argument is not an empty string, but it is not what you wanted.
4) The breakpoint is reached, and the argument is what you wanted.
What to do next is dependent on which of those four is the right one.
-
Re: Problems with data exchange beatween forms
Well, i hope i did correctly what you said and when this line in form1
Code:
Form2.xxx(mObject(0))
becomes highlighted (yellow) and the value of mObject(0) is the one that should be.
EDITED:
After that line becomes yellow and i press F10 form2 sub xxx becomes yellow and the value of variable example is the same as mObject(0), and after that it returns back to form1 line Form2.xxx(mObject(0)) that line becomes gray, but nothing is added to listbox.
-
Re: Problems with data exchange beatween forms
That wasn't the line I wanted the breakpoint on. Put it on this line from the sub in Form2:
ListBox1.Items.Add(example)
Have no other breakpoints.
From what you have said, it sounds like you will end up with situation 4. If that is the case, the one other thing that might be an easy answer would be to add the line:
Listbox1.Refresh
after the item is added.
However, give that a try, and see whether example is, in fact, what you expect it to be.
-
Re: Problems with data exchange beatween forms
I did that but listbox is empty. After line ListBox1.Items.Add(exmple) i put line MsgBox(example) and the message box with correct value showed up.
-
Re: Problems with data exchange beatween forms
Ok, I think you have completely covered the fact that the listbox should be getting something, yet it is not displaying anything. This leads to one of a variety of possibilities.
First off, try this:
You have shown the Load event for Form2. Put something into the listbox during the load event. It doesn't matter what, just some random string will be fine. Does that show up?
If that string does not show up, then you are seeing the wrong form. Why that is I can't say, as I see no reason for it from what you have shown. The most typical reason for that is because you are changing the default instance, but not showing the default instance. However, you showed how you are showing the form, and you are showing the default instance, as well, so that should not be the answer.
If the string does show up, then I suspect that your data arrival is running in a background thread. I have never tried messing with that, but I seem to remember JM mentioning something about a control in a background thread not displaying in the UI thread. On the other hand, that doesn't seem right, either, as you are not creating the control in the background thread, so adding to it from a background thread should cause an IllegalCrossThread exception.
Either way, I must say that I am stumped. From what you have shown so far, I see no good reason why the item is not showing up.
-
Re: Problems with data exchange beatween forms
OK, in form2 load event i put line ListBox1.Items.Add(Form1.mObjetc(0)) and i got into listbox first value from OPC server, and that is good.
-
Re: Problems with data exchange beatween forms
I think you may not be on the thread you think you are on. I don't have enough experience with this to be sure whether or not what you are seeing is a result of your call to the sub happening on a background thread, but I think it is reasonably likely.
One alternative you might try would be something like what I do in post #2 of this thread using a SynchronizationContext:
http://www.vbforums.com/showthread.p...zation+context
Save the SynchronizationContext early on, when you are clearly in the UI thread, though it doesn't really matter where you save it, though it should probably be on Form1.
Then, look at Posting a call to the SynchronizationContext that you saved. It's a pretty simple means of ensuring that your call is happening on the UI thread. It might solve your problem, or it might miss the mark.
-
Re: Problems with data exchange beatween forms
OK, thank you for being patience with me, Shaggy Hiker, +karma :)
I will check the alternative you suggest.
-
Re: Problems with data exchange beatween forms
Well Shaggy Hiker, i have read your post, but as i am not good enough in visual basic i hardly can understand things there.
Maybe somebody has more solutions what is wrong here?
-
Re: Problems with data exchange beatween forms
OK, i think i found what is wrong, but don't know how to fix.
In form 2 i made a public sub
Code:
Public Sub xxx(ByRef example As Object)
TextBox1.Text = Convert.ToString(example)
End Sub
And put a breake point at it.
In form1, sub OnDataChange, after Try i put code
Code:
Form2.xxx(mObject(0))
and put breake point at line Try.
After debugger started program stopped on line Try in form1. On next line
Code:
Form2.xxx(mObject(0))
i put mouse cursor on mOject(0) and saw that it contains value:
Code:
mObject Count=1
(0) 45 {UInteger}
After that line
Code:
Public Sub xxx(ByRef example As Object)
in form2 became highlighted, and value of example is
Code:
example 45 {UInteger}
Jump on line
Code:
TextBox1.Text = Convert.ToString(example)
gave me
Code:
TextBox1.Text ""
exmple: 45 {UInteger}
After jump to line End Sub, the values are
Code:
TextBox1.Text "45"
exmple: 45 {UInteger}
Then it returned to form1 line
Code:
Form2.xxx(mObject(0))
and the value of mObject(0) there is
Code:
mObject Count=1
(0) 45 {UInteger}
But i decided to check what value contains TextBox1 in form2, so went back to form2, put mouse pointer on the TextBox1.Text in line
Code:
TextBox1.Text = Convert.ToString(example)
and saw:
Code:
TextBox.Text1 { "An exception 'Microsoft.VisualStudio.Debugger.Runtime.CrossThreadMessagingException' occurred"}
StackTrace
Code:
StackTrace = " at Microsoft.VisualStudio.Debugger.Runtime.Main.ThrowCrossThreadMessageException(String formatString) at System.Windows.Forms.SafeNativeMethods.GetWindowTextLength(HandleRef hWnd) at System.Windows.Forms.Control.get_WindowText() at System.Windo...
I believe that is the problem why textbox remains empty in form2. Does anybody has any clue how can i fix it?
-
Re: Problems with data exchange beatween forms
It means you are accessing that control from a thread that does not 'own' it. Basically you should not modify any controls from a background thread, it should be done from the UI thread. You can use JMC's tutorial here to learn how to work around this: http://www.vbforums.com/showthread.php?t=498387
-
Re: Problems with data exchange beatween forms
Well, i have tried that as well, but with no luck
Form2
Code:
Public Delegate Sub SetTextBoxTextInvoker(ByVal text As String)
Public Sub SetTextBoxText(ByVal text As String)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(New SetTextBoxTextInvoker(AddressOf SetTextBoxText), text)
Else
TextBox1.Text = text
End If
End Sub
Form1
Code:
Finally
Form2.SetTextBoxText(mObject(0))
End Try
Is that code correct?
-
Re: Problems with data exchange beatween forms
I think that should work yes, but the way I have always done it in the past is to use two separate methods, so for example:
vb Code:
Public Sub SetTextBoxText(ByVal text As String)
TextBox1.Invoke(New SetTextBoxTextInvoker(AddressOf InvokedSetTextBoxText), text)
End Sub
Private Sub InvokedSetTextBoxText(ByVal text As String)
TextBox1.Text = text
End Sub
Like I said though, I think the way you are currently doing it should still work...
-
Re: Problems with data exchange beatween forms
Well, result is the same - empty textbox in form2. I even do not understand, why do i need this code
Code:
Public Delegate Sub SetTextBoxTextInvoker(ByVal text As String)
Public Sub SetTextBoxText(ByVal text As String)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(New SetTextBoxTextInvoker(AddressOf SetTextBoxText), text)
Else
TextBox1.Text = text
End If
End Sub
i put a break point on this sub and saw that program comes to if and jumps to else, so it makes same as mine sub
Code:
Public Sub xxx(ByVal example As Object)
TextBox1.Text = Convert.ToString(example)
End Sub
-
Re: Problems with data exchange beatween forms
After long experiments i came up to the dead end. I decided to clean up that OnDataChange sub and simply put code that each time when something changes in OPC server i will get an message box, with a first value.
Code:
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
If dwCount > 0 Then
MsgBox(pvValues(0))
End If
dwCount = 0
End Sub
So each time OPC server calls that sub i get a message box with the first value. After that i decided to make so that after each change i get value in the texbox that is on form1, but textbox remains empty all the time.
Code:
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
If dwCount > 0 Then
MsgBox(pvValues(0))
TextBox1.Text = Convert.ToString(pvValues(0))
End If
dwCount = 0
End Sub
I have checked what is wrong, and as Chris128 said it was
Quote:
Originally Posted by
chris128
It means you are accessing that control from a thread that does not 'own' it.
Fine, i put those two lines in my code and textbox on form1 started to work (same thing with ListBox, RichTextBox and so on).
Code:
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
If dwCount > 0 Then
MsgBox(pvValues(0))
ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = False
TextBox1.Text = Convert.ToString(pvValues(0))
ThreadExceptionDialog.CheckForIllegalCrossThreadCalls = True
End If
dwCount = 0
End Sub
It would be good, but how then i can make to work textbox on form2?
Beg for ideas, or my head will explode :eek2:
-
Re: Problems with data exchange beatween forms
All you are doing there is just hiding the exceptions by setting CheckForIllegalCrossThreadCalls to false... Those exceptions are thrown for a reason, just hiding them instead of actually fixing the problem is going to cause you more problems in the future
-
Re: Problems with data exchange beatween forms
OK, i made things like that
Code:
Form1
Private Delegate Sub yyyInvoker(ByVal mString As String)
Private Sub yyy(ByVal mString As String)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(New yyyInvoker(AddressOf yyy), mString)
Else
TextBox1.Text = mString
End If
End Sub
Overridable Sub OnDataChange(...........................)
If dwCount > 0 Then
yyy(pvValues(0))
End If
dwCount = 0
End Sub
When the program comes to line
Code:
If TextBox1.InvokeRequired Then
I see
Code:
TextBox1.InvokeRequired True
and textbox in form1 is filled with a value from OPC server, HYRRAY!
So i made similar sub in form2
Code:
Form2
Public Delegate Sub xxxInvoker(ByVal qString As String)
Public Sub xxx(ByVal qString As String)
If TextBox1.InvokeRequired Then
TextBox1.Invoke(New xxxInvoker(AddressOf xxx), qString)
Else
TextBox1.Text = qString
End If
End Sub
and put a call into form1
Code:
Form1
Overridable Sub OnDataChange(...........................)
If dwCount > 0 Then
yyy(pvValues(0))
Form2.xxx(pvValues(0))
End If
dwCount = 0
End Sub
when program comes to line in form2
Code:
If TextBox1.InvokeRequired Then
i see that
Code:
TextBox1.InvokeRequired False
and textbox in form2 remains empty.
Any comments on that?
-
Re: Problems with data exchange beatween forms
Hmm that does sound odd - is there any chance you could post your full solution here as an attachment to your post?
-
Re: Problems with data exchange beatween forms
Yes sure, here it is, the strange thing is that it says, that invokerequired False.
Maybe it is not possible to do what i want.
-
Re: Problems with data exchange beatween forms
Firstly, turn Option Strict on!
Anyway - I'll take a look through your code now and see if I can see anything
-
Re: Problems with data exchange beatween forms
In the SIEMENS sample it is shown as Strict off.
-
Re: Problems with data exchange beatween forms
Then they are not very good VB.NET programmers... There is no good reason to have Option Strict off, it just means you have a little bit less to type and can be less careful with casting and converting, which then results in problems a lot of the time. Turn it on and fix all of the problems that it highlights and then see if you are still having problems.
I looked at your code but at the moment it is so messy (presumably from you trying out lots of different things) that it is quite hard to see what is going on. Also, I do not know what this OPC thing does or how the OnDataChanged method gets called so it is hard for me to test it. I tested just calling that OnDataChanged method from a button click and passing in some random parameters but it all worked fine and the textbox on both Form1 and Form2 was filled in.
-
Re: Problems with data exchange beatween forms
Well chris128, you did a great job, i am really appreciate that!
Quote:
Originally Posted by
chris128
Then they are not very good VB.NET programmers...
Unfortunetaly i am much worser then they are, that is fact :)
Here is a piece of information how this OnDataChange works, maybe it will be usefull for you in some future
Code:
To allow the OPC server to return a result of asynchronous operations, the
IOPCDataCallback interface must be implemented on the client.
Implements IOPCDataCallback
The connection of the IConnectionPoint interface and IOPCDataCallback of the OPC server
is established using the FindConnectionPoint() method of the IConnectionPointContainer
interface. This generates a callback object for asynchronous calls.
m_ConnectionContainer.FindConnectionPoint(
GetType(IOPCDataCallback).GUID,
m_ConnectionPoint)
The connection between the callback object of the OPC server and the client application is
established by the Advise() method. The return variable m_dwCookie is an ID for the
connection.
m_ConnectionPoint.Advise(Me, m_dwCookie)
method of the callback object. On completion of a read operation, the server calls the
OnReadComplete() method, on completion of a write operation, the OnWriteComplete()
method is called. The server passes the return values as parameters to the method.
Overridable Sub OnDataChange(
ByVal dwTransid As Integer,
ByVal hGroup As Integer,
ByVal hrMasterquality As Integer,
ByVal hrMastererror As Integer,
ByVal dwCount As Integer,
ByVal phClientItems() As Integer,
ByVal pvValues() As Object,
ByVal pwQualities() As Short,
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME,
ByVal pErrors() As Integer) Implements
IOPCDataCallback.OnDataChange
I will try to start it all fom the empty list, if i succeed i will post the result, if not - well, then i will be in need to give up.
Here is a small gift from me :)
http://upload.wikimedia.org/wikipedi...sberg_beer.jpg
Once more time thank you very much for all you did.
-
Re: Problems with data exchange beatween forms
haha thank you for the massively oversized beer :D
What exactly does this OPC server thing do? I mean is there any way I could test it in the same way you are testing it? Does it require any special equipment or anything like that?
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
chris128
...I mean is there any way I could test it in the same way you are testing it? Does it require any special equipment or anything like that?
You can get an OPC-DA DLL from OPC Foundation, Kepware, or RSLinx. The file you need on your system is something like OPCDAAuto.DLL, kepopcdaauto.dll, or RSopcdaauto.dll.
Once installed on your system, you can instantiate the appropriate objects with which you can examine actual OPC-DA Servers, if you have any installed on your system.
Iconics has a free OPC Simulator and TopServer has a free OPC Simulator.
Quote:
Originally Posted by
chris128
...What exactly does this OPC server thing do?
Think of an OPC-DA server like a printer driver, only an industry standard printer driver. In fact, it is exactly an industry standard device driver. If you make a hardware device, you can also make a software OPC-DA driver to go along with it.
It is widely adopted because many vendors make useful OPC-DA clients, which all know how to talk to any OPC-DA server.
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
Fact
Hello all,
I am trying to make an application, that gets data from OPC server to form1 and displays some pieces in form2. The app works that way: on start - form1 starts. Form2 must be started manually after form form1 is statrted (i mad a buttton for that). If some values in OPC server changes i need to see some of them in form2. Here is a sub which shows how do i get data from OPC server.
Code:
Public mObject As List (Of Object)
Overridable Sub OnDataChange( _
ByVal dwTransid As Integer, _
ByVal hGroup As Integer, _
ByVal hrMasterquality As Integer, _
ByVal hrMastererror As Integer, _
ByVal dwCount As Integer, _
ByVal phClientItems() As Integer, _
ByVal pvValues() As Object, _
ByVal pwQualities() As Short, _
ByVal pftTimeStamps() As OpcRcw.Da.FILETIME, _
ByVal pErrors() As Integer) Implements IOPCDataCallback.OnDataChange
Dim i As Integer = 0
Dim j As Integer = 1
Dim k As Integer = 0
If dwCount > 0 Then
mObject = New List(Of Object)
For i = 0 To jk - 1
For j = 1 To jk
If phClientItems(i) = j Then
mObject.Add(pvValues(k))
k += 1
End If
Next j
Next i
End If
dwCount = 0
End Sub
I have tried to write line before sub end
Code:
Dim c As Integer
For c=0 To mObject.Count - 1
Form2.mObject.Add(Object(c))
Next c
but nothing happened when i opened form2, nor on change of data in OPC server (i wrote the code that on form2 load all values are shown in msgbox, message box did not show up).
dwCounts - how many values were changed in PLC.
phClientItems - variable index.
pvValues - variable value.
All the ideas are wellcome.
Thank you in advance.
I did not study your entire thread, and did not really see if this problem is solved, but a workaround I have used is to place this in the Form2_Load Event:
VB Code:
Me.CheckForIllegalCrossThreadCalls = False
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
Dave Sell
Iconics has a free OPC Simulator and TopServer has a free OPC Simulator.
Ok cool thanks for the information, I'm downloading that Iconics OPC Simulator now :)
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
chris128
Ok cool thanks for the information, I'm downloading that Iconics OPC Simulator now :)
You should start by examining the OPC Simulator using an OPC Browser.
Iconics has one for free I think, called OPC Data Spy. Use the OPC Data Spy to examine and set values in your OPC Simulator.
Then you can attempt to graduate to doing this in code.
-
Re: Problems with data exchange beatween forms
Well at the moment I am just wanting to run the code that the original creator of this thread is using and see what happens. The trouble is at the moment when I test it I do not actually have an OPC server so all I can do is manually call the OnDataChanged sub that he is having problems with, which does not produce the same problems he is having.
EDIT: OK I've got it installed (and it came with that OPC Spy thing) so now how do I go about sending some data to my project or do I not have to do anything like that?
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
chris128
Well at the moment I am just wanting to run the code that the original creator of this thread is using and see what happens. The trouble is at the moment when I test it I do not actually have an OPC server so all I can do is manually call the OnDataChanged sub that he is having problems with, which does not produce the same problems he is having.
EDIT: OK I've got it installed (and it came with that OPC Spy thing) so now how do I go about sending some data to my project or do I not have to do anything like that?
The only way to properly execute his code will be to configure the OPC Simulator to be exactly the OPC Topic and exactly the OPC Tags in their exact heirarchical arrangement. The original poster is the only one who knows this.
As far as the DataChanged event, once you are truly "listening" to an OPC Item in code, then go to the OPC Data Spy, and do a SyncWrite, which allows you to "Set" the value, thus triggering the event.
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
Dave Sell
I did not study your entire thread, and did not really see if this problem is solved, but a workaround I have used is to place this in the Form2_Load Event:
VB Code:
Me.CheckForIllegalCrossThreadCalls = False
Hey Dave, thanks for the solution, unfortunately it did not make my form 2 to work :)
Quote:
Originally Posted by
Dave Sell
You can get an OPC-DA DLL from OPC Foundation, Kepware, or RSLinx. The file you need on your system is something like OPCDAAuto.DLL, kepopcdaauto.dll, or RSopcdaauto.dll.
Both dll's - opcrcw.da.dll and opcrcw.comn.dll are in bin\Release directory.
For testing purpose i am using: Simatic Step7 to load the project to the station, SimaticNET for OPC server, CP5512 with HW adapter to communicate with PLC and S7-300 PLC CPU 315-2DP :)
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
Fact
Hey Dave, thanks for the solution, unfortunately it did not make my form 2 to work :)
Both dll's - opcrcw.da.dll and opcrcw.comn.dll are in bin\Release directory.
For testing purpose i am using: Simatic Step7 to load the project to the station, SimaticNET for OPC server, CP5512 with HW adapter to communicate with PLC and S7-300 PLC CPU 315-2DP :)
Have you tried that code in the Form1_Load Event?
Can you email me those DLLs?
-
Re: Problems with data exchange beatween forms
I made an ActiveX wrapper which I use to easily communicate with OPC Servers from VS2005/2008. It is compiled as a binary package which needs to be installed. Once installed, any VS2005/2008 app (VB+C#) can add a reference to the library.
On the coding side, the number of lines to code is very minimal:
VB Code:
Imports OPC_Process
Public Class frmMain
'
Private WithEvents m_OPC_Process As OPC_Process.cls_OPC_Group
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
'
' This allows me to update my Form's GUI Objects
Me.CheckForIllegalCrossThreadCalls = False
'
m_OPC_Process = New OPC_Process.cls_OPC_Group
'
' I can listen to "any" OPC Server installed on the same OS as the app is running on
' (not all servers in the world tested...)
m_OPC_Process.Initialize_OPC(Me.txt_OPCServer.Text, Me.txt_Context.Text, "10")
'
End Sub
Private Sub AddOPC_Item()
m_OPC_Process.AddListener(Me.txt_Context.Text, "Location/goes/here/" & "TagName")
End Sub
Private Sub WriteOPC_Item()
m_OPC_Process.WriteToTag(Me.txt_Context.Text, "Location/goes/here/" & "TagName", "Value!")
End Sub
Private Sub m_OPC_Process_DataChanged(ByRef Context As String, ByRef TagName As String, ByRef TagValue As String) Handles m_OPC_Process.DataChanged
'
' Any change to any OPCItem that occurs on the device will trigger this Event
' Do anything you want with TagName/TagValues here...
'
End Sub
End Class
This is not an official product of our company, but let me know if you'd like to try it, I will see how I can start distributing this.
-
Re: Problems with data exchange beatween forms
Quote:
Originally Posted by
Dave Sell
Can you email me those DLLs?
If you go back to the first page of this thread you will see near the end of the page the OP attached his entire project file to a post so you can just get them out of there.
-
1 Attachment(s)
Re: Problems with data exchange beatween forms
Well, i do not have problems with connection to OPC server and all data changes are displayed correct, but only in form where the main code is. The connection to OPC server, creation of group, filling of it with variables is made in form1 load event. What i am after is to be able to view some data in form 1 some data in form2 some data in form 3 and so on. I believe if i put that main code in all forms i will see the result i am seeking, but then the program will be something like 3x, 4x bigger, because i will repeat same code in all forms.
Those dll's are Runtime Callable Wraper components.
I have attached a pure code, with some explanations how it works.