.NET Equivalent for VB6 DoEvents + Sleep - vb.net

I have a program that I'm updating from VB6 to VB.NET that is used for making optical measurements. The hardware runs Win XP or Win 2000 Embedded. In the original code, there is a section that is triggered after a measurement is started that uses DoEvents and Sleep:
While instDefItf.InstrumentState = Busy
Sleep 500
DoEvents
Wend
I've tried replacing the middle two lines in my code with a simple Threading.Thread.Sleep(500) (I realize that this is probably poor practice, but not having a ton of experience with VB, I was trying to preserve the VB6 program as closely as possible). This makes the code work correctly - on the second try. On the first try, the measurements that are supposed to be taken simply aren't, but no errors are thrown and the subsequent code executes correctly.
Based on this, I have two primary questions. First, is there something I could simply replace the VB6 code with to get the desired functionality? Second, is there a better way to periodically query the instrument state that doesn't make use of the Sleep function?
EDIT:
The function I referred to above is a helper function (I think that's the right term) that checks to see if the instrument has completed its measurement. The measurement routine is contained within a DLL, and the code that executes it is given below.
If cbMeasLength.Value = vbUnchecked Then
Call I12001Ref.StartMtj1IlAcquisition2(CLng(lChannel), ConnectorA, m_moduleCollection.ReflectanceModule(lRm).SerialNumber)
End If
' Wait the measurement completion
If cbMeasLength.Value = vbUnchecked Then
If WaitForOperationCompleted = False Then
Exit Sub
End If
End If

Related

VBA execution flow interrupted unexpectedly

Context of the problem
I'm developing a new feature for an HMI using Factory Talk View Studio 7.00.00 (CPR 9 SR 6) and VBA 6.5.
I have two displays: ma1_header and ma2_header and, as many of you know, in Factory Talk View Studio (I'm going to call it as FTV for brevity from now on) each display has a dedicated DisplayCode.
A Display code can be seen as the VBA code behind an Excel file that stays open as long as its excel file. From this point of view, the VBA code bounded to a display in FTV has the same behaviour of a VBA project in excel, so it's closed when the graphic display it's closed by the user or from code.
Another important point in order to understand the problem is that when a generic display in FTV called A is opened in Replace mode with at least one pixel that overlaps another display B, the display B is closed with its VBA code. Take in mind that ma1_header and ma2_header are always opened in Replace mode.
Problem description
That said, now I'm going to describe the problem that I found.
The VBA code bounded to ma1_header and ma2_header is mostly the same (the differences are pointed out in the following schema) and it performs some init actions on display start and after those it runs a procedure called ScheduleCheck. This procedure updates some UI components and it evaluates some conditions in order to determine if it's time to show ma2_header (ma1_header if the code it's executed behind ma2_header), then it recalls itself.
The command that opens a display is not executed directly from VBA, its executed asynchronously outside VBA. In fact VBA for some actions like: "Show a display", "Set tags values", etc. can uses a library that enables it to tell to a FTV service (that also can be used with a command line tool) to perform a list of these actions (1 command or more transmitted at time).
When a user starts a FTV client the ma1_header is shown first with some others displays which compose the user interface.
In ma1_header, when the opening conditions for ma2_header are satisfied and it's opened, the problem comes out. Let's proceed now with a step by step description of the VBA state, in order to be as clear as possible on the problem:
In ma1_header the opening conditions are satisfied and the asynchronous command that shows ma2_header is excuted. Note that for the moment ma1_header vba is still open.
When ma2_header start opening the code it's executed without any problem till the wait procedure. The wait procedure is written as follow:
Public Declare Function GetTickCount Lib "kernel32" () As Long
Public Sub wait(lMillSec As Long)
Dim lT2 As Long
Dim lT1 As Long
On Error GoTo errHandle
Const strMethod = "wait"
MsgBox "wait - 1"
lT1 = GetTickCount
lT2 = lT1
While lT2 - lT1 < lMillSec
lT2 = GetTickCount
DoEvents
Wend
errHandle:
If Err.Number Then
LogDiagnosticsMessage "VBA: Display " & Me.Name & " in Method " & strMethod
Err.Clear
End If
End Sub
The execution of the wait procedure proceed without any problems until the DoEvents command on the 18th rows. When DoEvents it's executed the ma1_header has the time to close itself definitively (even its vba code) then the vba flow in ma2_header seems to stops here without any error. Due to this the ScheduleCheck can't recall itself.
Ugly solution 1
Currently I found what I call an ugly solution that let code works.
When ma1_header is open, the ma2_header opening will force the close of ma1_header that , as I explained before, is completed only when DoEvents is executed.
I tried to transmit this new list of commands to the FTW tool when the ma2_header need to be shown:
##New##
Abort MA2_HEADER;
Pause 1;
Display MA1_HEADER /TRRU;
##Old##
Display MA1_HEADER /TRRU;
With the new approach I close the ma2_header, then I wait (in the FTV tool, not in VBA) for 1 second with the command "Pause 1;" then I open ma1_header when I'm reasonable sure that ma1_heder is closed thanks to the pause command.
In this way the ma2_heder periodically executes the procedure ScheduleCheck without strange execution interruption.
I don't know why this solves my problem, so I'd like to understand why it works and which is the cause of this problem in order to find a better solution.
Ugly solution 2
I found another ugly solution, but as before I'm not satisfied because I would like to know way my current code has this problem (for me solving a problem without knowing what is the cause it's a defeat as a programmer).
I've created the following new tag on the Tag server of FTV
I've created the following new event on FTV:
I've added a string display containing the new tag Tick in ma1_header and in ma2_header
Now, in VBA, I can use the Change event of this string display in order to execute the same code contained in ScheduleCheck (obviously without the wait with DoEvents loop) each time this string display changes (each second).
Any clarification on the problem or a better solution would be really appreciated.

VBA Macro running too fast

It's weird that I'm finding ways to slow down my macro. Apart from Doevents and other time delay techniques, which are basically a workaround, is there a way through which we can get around the asynchronous execution. As in, I want the VBA code to behave like this:
start executing line 1>finish executing line 1>move to line 2;
Forgive if I'm wrong but currently it seems to follow:
Start executing line 1>without caring whether line 1 finished or not start executing line2
If you are calling external programs (vbs, exe) then the vba isn't getting any feedback on the process at all. It calls the programs and moves on to the next line of code (the vba doesn't know if/when the external programs finishes).
One way to slow this process down would be to put a application.wait or application.sleep between the calls, but that is also a workaround. Please post your actual code and perhaps we can troubleshoot further.
if the code is about refreshing data, use Refresh method with backgroundquery=False in a For Loop instead of RefreshAll.
For Each con In Me.Connections
con.ODBCConnection.BackgroundQuery = False
con.Refresh
Next

Code Execution Sequence

I am a PLC programmer who is currently using a variant of VB to control a motor.
I want to call a function that will execute moves and not return to the main code until the move has been completed. Currently here is what I have:
Program 'Main Program
While 1
If move_req = 1
Function MoveMotor
End If
Wend
End Program
Function MoveMotor
MoveABS 10 ' Move to encoder position 10mm
move_complete = 1
While move_req = 1
'Do Nothing
Wend
End Function
For some reason this code isn't working and the move command is being sent over and over again. Could this be because the main program continues to run when the function is running? Is that how VB works? I am used to thinking of code sequence in terms of PLC's where they scan through everything repeatedly at a certain frequency.
Whenever the move is complete, there must be a way for it to be detected by the program. It looks like you want move_req to be set to zero when this happens, but I cannot see what would cause that. How does the machine signal the program that it's finished moving?
A second point is that when you have a loop that waits while it checks for variable change, it can cause a CPU spike. You can put a pause in the loop with some thing like System.Threading.Thread.Sleep(100) where 100 is milliseconds to pause.

Async event handler - flycapture from PointGrey

I am using Point Grey's FlyCapture API to drive some cameras.
In a public class, I implemented all the starting and initializing code ; in the following _cam refers to a ManagedGigECamera.
Because I have 16 cameras, I want the code to be as fast as possible, so I wanted to use tasks.
Here is the code I use:
_cam.StartCapture(AddressOf OnImageGrabbed)
.../...
Public Sub OnImageGrabbed(ByVal raw_image As ManagedImage)
Dim t As Task = Task.Run(Sub()
'save image to disk or whatever
End Sub)
t.Wait()
End Sub
The above gives -sort of- satisfaction. By viewing image timestamps, I can see that some images are saved seconds after they are grabbed, and even some images are skipped altogether...
I wanted to make sure each call to OnImageGrabbed would start a new task, and tried the following, but it crashes right away with 'object not set to an instance of an object' (can't really debug, the code is running on a remote machine)
_cam.StartCapture(AddressOf OnImageGrabbed)
.../...
Public Async Sub OnImageGrabbed(ByVal raw_image As ManagedImage)
Await Task.Run(Sub()
'save image to disk or whatever
End Sub)
End Sub
All in all, my questions are:
how can I run an event handler asynchronously ?
why, using the first code, do I get (what appears to be) random delays between each call
to OnImageGrabbed ? I mean the differences in time between image timestamps is never the same, and tend to increase on the long run (first few images are almost synchronized, but after 1 minute or so, each image is separated by more and more time). Memory leak ? GC ?
Thanks in advance for any hint !
EDIT:
In the end I changed the way the system works: I fire a software trigger on each camera using a timer, and each trigger is fired 'in parallel':
Parallel.ForEach(Of ListOfCameras)(SingleCamera,
Sub(aCamera, loopstate, num)
aCamera.FireTrigger()
End Sub)
Starting a task and then immediately blocking on it (via Wait) nets you nothing. You may as well just run the saving-image code directly.
The second example is actually asynchronous. You're probably getting an exception because the ManagedImage argument or one of its child objects is being disposed. Remember that the code raising the event has no idea that your code is asynchronous; it's up to you to copy out what you need from the event arguments if you're going to use it asynchronously.

How to use the same class accross multiple threads and return a variable

My apologies in advance if this has already been answered, but every search I have done does not come close to what I need. Also, this is all pseudo code.
Here is the situation: I created a form (targeting DOT NET 3.5) that does a loop on a gridview recreating a class and runs the code. After the code runs, there is a local variable on the class that gets updated and allows me to use it and the process repeats. Something like this:
For x as Integer = 0 to Me.txtTextBox.Lines.Count - 1 'Can be in the hundreds
Dim objMyClass as MyClass = New MyClass(Me.DatagridView1.Rows(x).Cells(0).Value)
if objMyClass.Start() = True then
'Do my thing with objMyClass.LocalLongVariable
End If
Next
This works just fine, but takes literally days to complete. The last time I ran this it took like 6 days, 7 hours and 40 something minutes to complete and barely bumped the CPU usage.
So, now I want to use MulitThreading to run several of these instances at the same time. I have not been able to get this to work. Everything I try returns different values every time I run it (and it should not). I believe that the threads are accessing the local variable across other threads and are incrementing at will. And SyncLock locks up the entire program. I have also tried adding a custom event that fires when the process is completed and executes a delegate on the Main form, but that has not worked either.
Now, my question is simple: How can I run multiple threads using the same base class (passing a unique string variable) and have the local class variable produce the correct results back to the UI? (And, from what I have been reading, the BackgroundWorker class in not suitable for this many threads (like hundreds); correct me if I read it incorrectly please)
I am looking for something like:
Dim thrd(Me.txtTextBox.Lines.Count) as Thread
Dim objMyClass(Me.txtTextBox.Lines.Count) as MyClass
For x as Integer = 0 to Me.txtTextBox.Lines.Count - 1
thrd(x) = new Thread (Sub()
objMyClass(x) = New MyClass(Me.GridView1.Rows(x).Cells(0).Value
If objMyClass.Start() = True Then
'Do my stuff here (maybe call a delegate??)
End If
End)
thrd(x).IsBackground = True
thrd(x).Start()
Next
Any help/advice on how to proceed will be greatly appreciated. And, if you know of any examples of your suggestion, please post the code/link.
The solution was, in fact, Synclock. My issue was that I was locking the wrong object, objMyClass, instead of the current Me AND I was failing to use Monitor.PulseAll(). Also, I switched to using the ThreadPool.QueueUserWorkItem(AddressOf objMyClass, args) and also used SyncLock on my custom event raised when the thread completes. It's a whole lot easier!! Thanks!!