Author Topic: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)  (Read 12426 times)

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
There is a problem with EthernetIPforCLXCom.vb ComErrorHandler
Responded(e.TransactionNumber) (2nd line)

e.TransactionNumber is a UShort but Responded is only dimensioned to 255
Several times now my program has stopped (in the VB2010 Express IDE with standard error handler settings) at Responded(e.TransactionNumber) because e.TransactionNumber > 255.  So there may be 2 issues, one with the out of bounds and the other with the failure of error handling to kick this error back to a real error handler.

Some other less important issues:

With a 1756-L61 and 1756-ENBT/A I keep getting many Open Errors (after last night 45000) as reported by the PLC web page Diagnostic overview.

This is what I am doing:


 PLC.REAL_N = 1560
 PLC.DINT_N = 1000)

             frmLoad.EthernetIPforCLXCom1.Write("HMI.TRIGGER", 1)
            Thread.Sleep(150)

            sHMI_REAL = frmLoad.EthernetIPforCLXCom1.Read("HMI.REAL", PLC.REAL_N)

            sHMI_DINT = frmLoad.EthernetIPforCLXCom1.Read("HMI.DINT", PLC.DINT_N)



It seems to work OK, but the Open Errors don't give me a lot of confidence.

I am running this once a second.  I am a bit surprised at the high PC CPU usage this requires during the read, but it does work.
It takes about 200 msec for the reads.

I have also found that a Read of a Bool array doesn't work.  I don't know if this hasn't been implemented or is a bug.

When you put out the next version, it would make me more confident if it didn't have any compiler warnings.

Keep up the great work.  This is a really cool project.









Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #1 on: August 16, 2013, 12:38:43 PM »
In the ComError, this is how the line is supposed to read:

       Responded(e.TransactionNumber And 255) = True
 

Is the 200ms you come up with including the sleep(150)? A typical single read should take less than 10ms. If you Wireshark the packets, you should be able to see the time in between the 2 reads that will give you an accurate read time.

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #2 on: August 16, 2013, 06:28:37 PM »
The 200 msec is just for the reads, not including the sleep.  Those are arrays of 1560 and 1000 elements.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #3 on: August 16, 2013, 07:22:54 PM »
With a 1756-L61 and 1756-ENBT/A I keep getting many Open Errors (after last night 45000) as reported by the PLC web page Diagnostic overview.

This is what I am doing:

 PLC.REAL_N = 1560
 PLC.DINT_N = 1000)

             frmLoad.EthernetIPforCLXCom1.Write("HMI.TRIGGER", 1)
            Thread.Sleep(150)

            sHMI_REAL = frmLoad.EthernetIPforCLXCom1.Read("HMI.REAL", PLC.REAL_N)

            sHMI_DINT = frmLoad.EthernetIPforCLXCom1.Read("HMI.DINT", PLC.DINT_N)

It seems to work OK, but the Open Errors don't give me a lot of confidence.

I am running this once a second.  I am a bit surprised at the high PC CPU usage this requires during the read, but it does work.
It takes about 200 msec for the reads.
I tried to replicate this using the following code with a 1 second timer. It ran for 3 hours and reported over 43000 messages sent and received without any Open or Close errors:
Code: [Select]
    Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles Timer1.Tick
        EthernetIPforCLXCom1.Write("HMITag", 1)
        Threading.Thread.Sleep(150)
        Me.Text = EthernetIPforCLXCom1.Read("RealTag")
        Me.Text &= " " & EthernetIPforCLXCom1.Read("DINTTag")
    End Sub

Are you maybe running through a remote connection or something that would cause delays in the network? The latest release only allows 150ms to get a response from each packet. After 2 tries, it times out and closes the connection. I will increase that to 250ms for the next release.


The reading of large arrays has to receive the information in partial packets. If you are reading 1500 elements, it may require 5 or 6 packets to get all of the information, therefore 50ms+ can be expected on a very lightly loaded network.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #4 on: August 16, 2013, 07:32:10 PM »
I also tried the Boolean array and it is working for me. How many bits are you reading?

This is the code I tested with:

Code: [Select]
        Dim d() As String = EthernetIPforCLXCom1.Read("BoolArray[0]", 5)

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #5 on: August 19, 2013, 12:19:53 PM »
I used your example and now I can read BOOL arrays too!   Originally I couldn't find any examples of reading arrays and found that giving the name of the array worked for DINT and REAL.  I never understood that I needed to specify the first element with "Bracket 0 Bracket".  So what I was doing for DINT and REAL worked, but not for BOOL.  The Open Errors I was seeing was just a coincidence and not a problem with AHMI.  The PLC I was using had some produced/consumed tags and its partner PLC was disconnected.  I mistakenly thought the errors were coming from AHMI since I had never seen them before then.   My 200 msec read times are for 22 CIP blocks, if I did the math right  (480 bytes / block)  In the ethernet world that seems like an eternity for such little data (10.5 kbytes), but in the PLC CIP world, maybe that is normal.   I have also found that my thread.sleeps are causing high cpu usage.  I don't understand that yet, but AHMI seems to be working fine.  I plan on using a lot  of coding for PLC communications instead of the built in components.  If there are some code examples of reading/writing tags & arrays and other coding tips and tricks I would appreciate it if you can point me in that direction.   Thanks for your help.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #6 on: August 19, 2013, 05:46:23 PM »
The fastest time I have been able to get from one packet to reading another is 6ms. That was on a test PLC with essentially no program. I would guess that time can increase as the PLC program grows in size and has more to process. I have also seen cases where Ethernet IO was used on compactLogix and the HMI was on the same network. This drastically affected the speed.

As for tips, the most common question asked is how to read program scope tags. This is the format for that:

PROGRAM:ProgramName.MyTag

Subscriptions are the most efficient method to get data because the driver has built in logic to combine multiple subscriptions into single reads and they also update on a background thread, so they do not tie up the UI thread like a Read does. The latest version now has a DataSubscriber component that makes subscriptions much easier.
« Last Edit: August 19, 2013, 05:49:37 PM by Archie »

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #7 on: August 19, 2013, 07:16:37 PM »
I am using another thread to do the array reads.  It appears to work OK.  There is no theoretical problem with doing that is there?

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #8 on: August 19, 2013, 07:43:55 PM »
The latest release of the ControlLogix driver was restructured to handle reading from multiple threads. Versions prior to 3.5 will run into problems, but 3.51 and later should work fine.

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #9 on: August 19, 2013, 08:00:51 PM »
I found a new problem in ExtractData

This is another case where the program just stopped at that line and didn't kick back to an errorhandler.

Case &HD3 '* BOOL Array
                Dim x, i, l As UInt32
                Dim CurrentBitPos As UInt16 = BitIndex
                For j As Integer = 0 To ((returnedData.Length - startIndex) / 4) - 1
                    x = BitConverter.ToUInt32(returnedData, (j * 4 + startIndex))
                    While CurrentBitPos < BitsPerElement
                        l = (2 ^ (CurrentBitPos))
                        result(i) = (x And l) > 0
                        i += 1
                        CurrentBitPos += 1
                    End While
                    CurrentBitPos = 0
                Next


IndexOutofRangeException
i = 31

in line:

result(i) = (x And l) > 0 

in above code

Dim result(ElementsToReturn) As String

ElementsToReturn = 30



I got there with this:

 For i = 0 To 10
            Dim dT() As String = frmLoad.EthernetIPforCLXCom1.Read("HMI.BOOL[" & i & "]", 1)
 Next

BOOL is an array in a UDT called HMI


Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #10 on: August 20, 2013, 04:19:00 PM »
I have not found a way to set a single element of a multielement array.  For example:

 Dim BI() As Integer = {1}
        Dim BSng() As Single = {1}
        Dim BStr() As String = {"1"}

        Dim k As Integer

        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BI)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BSng)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BStr)

None of these methods work.  k always returns 0.
Actually the first method worked a time or two, but not reliably, and once the bit was set, I could never set it back to 0.

I have also experienced some weirdness when single stepping through the program.  Sometimes it works OK, but then will often stop at a end synclock or at
 DLL(MyDLLInstance).WriteTagValue(tag, StringVals, numberOfElements, SequenceNumber)

with a NullReferenceException was unhandled.

it looks like DLL.EIPEncap.SessionHandle = 0


Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #11 on: August 20, 2013, 04:49:23 PM »
I have also experienced some weirdness when single stepping through the program.  Sometimes it works OK, but then will often stop at a end synclock or at
 DLL(MyDLLInstance).WriteTagValue(tag, StringVals, numberOfElements, SequenceNumber)

with a NullReferenceException was unhandled.

it looks like DLL.EIPEncap.SessionHandle = 0
It is tricky to single step with an Ethernet/IP driver because it times out fairly quickly and the PLC closes the connection. So if you take more than about 5 seconds stepping through, the read/write will fail.

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #12 on: August 20, 2013, 07:28:23 PM »
I found a new problem in ExtractData

This is another case where the program just stopped at that line and didn't kick back to an errorhandler.

Case &HD3 '* BOOL Array
                Dim x, i, l As UInt32
                Dim CurrentBitPos As UInt16 = BitIndex
                For j As Integer = 0 To ((returnedData.Length - startIndex) / 4) - 1
                    x = BitConverter.ToUInt32(returnedData, (j * 4 + startIndex))
                    While CurrentBitPos < BitsPerElement
                        l = (2 ^ (CurrentBitPos))
                        result(i) = (x And l) > 0
                        i += 1
                        CurrentBitPos += 1
                    End While
                    CurrentBitPos = 0
                Next


IndexOutofRangeException
i = 31

in line:

result(i) = (x And l) > 0 

in above code

Dim result(ElementsToReturn) As String

ElementsToReturn = 30



I got there with this:

 For i = 0 To 10
            Dim dT() As String = frmLoad.EthernetIPforCLXCom1.Read("HMI.BOOL[" & i & "]", 1)
 Next

BOOL is an array in a UDT called HMI
I tried 2 different scenarios with reading BOOL arrays and both worked correctly. Is there maybe other reads occurring at the same time?

Code: [Select]
        For i = 0 To 10
            Dim dT() As String = EthernetIPforCLXCom1.Read("BOOLArray[" & i & "]", 1)
            Me.Text &= dT(0)
        Next

        Dim x() As String
        x = EthernetIPforCLXCom1.Read("BoolArray[0]", 50)

Archie

  • Administrator
  • Hero Member
  • *****
  • Posts: 5322
    • View Profile
    • AdvancedHMI
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #13 on: August 20, 2013, 08:01:44 PM »
I have not found a way to set a single element of a multielement array.  For example:

 Dim BI() As Integer = {1}
        Dim BSng() As Single = {1}
        Dim BStr() As String = {"1"}

        Dim k As Integer

        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BI)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BSng)
        k = frmLoad.EthernetIPforCLXCom1.Write("HMI.BOOL[0]", 1, BStr)

None of these methods work.  k always returns 0.
I have been able to replicate this and working on a fix

Randy

  • Newbie
  • *
  • Posts: 23
    • View Profile
Re: EthernetIPforCLXCom.vb ComErrorHandler Responded(e.TransactionNumber)
« Reply #14 on: August 21, 2013, 02:32:19 PM »
Here are a few comments:

The Default Buttton ForeColor is Active Caption Text instead of ControlText.

By Default the label on a new button is almost impossible to read.  I am not sure how the default gets set in AHMI, But it may be a good idea to have the default more readable.
On Your machine Active Caption Text may be set to a different color.  On mine (windows default) it is white.


I have been struggling with PC CPU usage issues.  My goal is to have a background thread read some arrays a couple of times a second.  I can do this but so far all my attempts have resulted in high CPU usage.  Perhaps

you have some insight on this.  Consider the following code:

    Dim PLC_Run As Boolean

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Read_PLC()
    End Sub

    Private Sub Read_PLC()
        On Error GoTo Err_Sub
        Dim sw As New Stopwatch
        sw.Start()
        Dim dB() As String = Me.EthernetIPforCLXCom1.Read("HMI.BOOL[0]", 2048)
        Dim dR() As String = Me.EthernetIPforCLXCom1.Read("HMI.REAL[0]", 1560)
        sw.Stop()
        Me.lblTime.Text = sw.ElapsedMilliseconds
Exit_Sub:
        Exit Sub
Err_Sub:
        Debug.Print("Read_PLC " & Err.Description)
        Resume Next
        Resume
    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        PLC_Run = True
        Do While PLC_Run
            Read_PLC()
            Application.DoEvents()
        Loop
    End Sub

    Private Sub btnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStop.Click
        PLC_Run = False
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        PLC_Run = True
        Do While PLC_Run
            Read_PLC()
            Threading.Thread.Sleep(100)
            Application.DoEvents()
        Loop
    End Sub

I have found that a single click of Button1 will cause the PC CPU usuage to jump to 50-60% on a dual core cpu and stay that way for 20 seconds.

A button2 click puts it in a loop where the reads happen continuously.  In that case the PC cpu stays at about 10% until I click Stop at which time the CPU goes up to 50% for 10 seconds.  During this continuous poll

the PLC CPU goes to 40%.

A button3_click also cause high CPU.  If I comment out the Read_PLC the CPU drops to < 10%.
The important point is that a thread.sleep by itself causes the cpu to drop as you would expect, but if Read_PLC is involved with a thread.sleep the cpu jumps up.

This has the same behavior if I run this in a different thread.



I am surprised you can't replicate the problems with:

For i = 0 To 10
            Dim dT() As String = frmLoad.EthernetIPforCLXCom1.Read("HMI.BOOL[" & i & "]", 1)
 Next


Maybe it is because it is part of a UDT


Try this in a .L5X:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RSLogix5000Content SchemaRevision="1.0" SoftwareRevision="19.01" TargetName="HMI" TargetType="DataType" ContainsContext="true" Owner="., ." ExportDate="Tue Aug 20 09:05:56 2013" ExportOptions="References

DecoratedData Context Dependencies ForceProtectedEncoding AllProjDocTrans">
<Controller Use="Context" Name="HMI">
<DataTypes Use="Context">
<DataType Use="Target" Name="HMI" Family="NoFamily" Class="User">
<Members>
<Member Name="REAL" DataType="REAL" Dimension="1560" Radix="Float" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="BOOL" DataType="BOOL" Dimension="2048" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="SINT" DataType="SINT" Dimension="1" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="INT" DataType="INT" Dimension="1" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="DINT" DataType="DINT" Dimension="1" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="LINT" DataType="LINT" Dimension="1" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="STRG" DataType="STRING" Dimension="1" Radix="NullType" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_REAL" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_BOOL" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_SINT" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_INT" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_DINT" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_LINT" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="N_STRG" DataType="DINT" Dimension="0" Radix="Decimal" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="Z_TIMER" DataType="TIMER" Dimension="0" Radix="NullType" Hidden="false" ExternalAccess="Read/Write"/>
<Member Name="ZZZZZZZZZZHMI15" DataType="SINT" Dimension="0" Radix="Decimal" Hidden="true" ExternalAccess="Read/Write"/>
<Member Name="Z_TRIGGER" DataType="BIT" Dimension="0" Radix="Decimal" Hidden="false" Target="ZZZZZZZZZZHMI15" BitNumber="0" ExternalAccess="Read/Write"/>
</Members>
</DataType>
</DataTypes>
</Controller>
</RSLogix5000Content>

Import as UDT.
Then create a tag called HMI of that UDT type and try the code again.  it always breaks for me.



As I said earlier I also have a lot of concerns with the unhandled exceptions I have experienced.  As you know an unhandled exception is death to production code.

It appears that

Private Sub DataLinkLayer_DataReceived(ByVal sender As Object, ByVal e As MfgControl.AdvancedHMI.Drivers.Common.PlcComEventArgs)

could benefit from an error handler.  You may know of other code entry points (especially from other threads) that need error handlers.

Any error in a driver should kick an error back to the driver entry point.


Personally I put an errorhandler on every code entry point.  I still use the old unstructured code because Try Catch doesn't have a Resume Next.

Practically every block of code I write starts with:

Friend Sub Example()
        On Error GoTo Err_Sub

Exit_Sub:
        Exit Sub
Err_Sub:
        ErrorHandler("Example " & Err.Description)
        Resume Next
        Resume
    End Sub

I change Example in the name and ErrorHandler to the new name of the Sub or Function.

I have a global error handler:


Friend Sub ErrorHandler(ByVal S As String, Optional ByVal FlashRed As Boolean = False)
   fHMI.ErrorHandler(S, FlashRed)
End Sub


the fHMI.ErrorHandler puts the error in a listbox and logs it to a text file.

If I start to get a lot of errors I can attach to the process and put a breakpoint on the ErrorHandler.
I can step over the real errorHandler and then once I am back in the erring routine move the execution point to resume to take me back to the offending line of code.

This approach has saved me a lot of time.

on the fHMI form (which is a container that can't close) I have a listbox docked to the botton of the form and a Clear and ClearAll button on the Same line just left of the listbox scrollbar.
The buttons are anchored to bottom right.  Note that fHMI cannot be the default instance of the form, otherwise it can't display errors from multiple threads.



Here is the code.

 Friend HMIErrors As Error_Struct
    Friend Structure Error_Struct
        Friend NumErrors As Integer
        Friend FlashRed As Boolean
        Friend LastFlashChange As Integer
    End Structure

Friend Sub HMIUpdate()
        On Error GoTo Err_Sub
        Dim S As String
        S = "Sec Change: " & Timing.Sec_Change & vbCrLf _
          & "Sec TaskTime: " & Timing.Sec_TaskTime & vbCrLf _
          & "PLC Trigger Wait: " & Timing.PLC_Trigger_Wait & vbCrLf _
          & "PLC Read Wait: " & Timing.PLC_Read_Wait
        Me.lblms.Text = S



        If HMIErrors.FlashRed Then
            If Now.Second <> HMIErrors.LastFlashChange Then
                HMIErrors.LastFlashChange = Now.Second
                If Me.ListError.BackColor = System.Drawing.Color.Red Then
                    Me.ListError.BackColor = Color.FromKnownColor(KnownColor.Window)
                Else
                    Me.ListError.BackColor = System.Drawing.Color.Red
                End If
            End If
        End If
Exit_Sub:
            Exit Sub
Err_Sub:
            ErrorHandler("frmHMI_HMIUpdate " & Err.Description)
            Resume Next
            Resume
    End Sub



    Private Sub btnClearAll_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClearAll.Click
        On Error GoTo Err_Sub
        Me.ListError.Items.Clear()
        HMIErrors.NumErrors = 0
        HMIErrors.FlashRed = False
        Me.ListError.BackColor = Color.FromKnownColor(KnownColor.Window)
Exit_Sub:
        Exit Sub
Err_Sub:
        ErrorHandler("btnClearAll_Click " & Err.Description)
        Resume Next
        Resume
    End Sub

    Private Sub btnClear_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClear.Click
        On Error GoTo Err_Sub

        If Me.ListError.Items.Count = 0 Then
            btnClearAll.PerformClick()
            Exit Sub
        End If

        Dim LastItem As Integer = Me.ListError.TopIndex
        Me.ListError.Items.RemoveAt(LastItem)

        HMIErrors.NumErrors = Me.ListError.Items.Count

        If HMIErrors.NumErrors = 0 Then
            btnClearAll.PerformClick()
        Else
            LastItem = LastItem - 1
            If LastItem < 0 Then LastItem = 0
            Me.ListError.TopIndex = LastItem
        End If


Exit_Sub:
        Exit Sub
Err_Sub:
        ErrorHandler("btnClear_Click " & Err.Description)
        Resume Next
        Resume
    End Sub

    Private Delegate Sub ErrorHandlerCallback(ByVal S As String, ByVal FlashRed As Boolean)

    Friend Sub ErrorHandler(ByVal S As String, Optional ByVal FlashRed As Boolean = False)
        On Error Resume Next
        If Me.ListError.InvokeRequired Then
            'The Callback is necessary to display errors from other threads
            Dim d As New ErrorHandlerCallback(AddressOf ErrorHandler)
            Me.Invoke(d, New Object() {S, FlashRed})
        Else
            Dim DT As Date
            Dim DTFile As String
            On Error Resume Next

            HMIErrors.NumErrors += 1
            DT = Now
            S = DT.ToString & ": Alarm " & HMIErrors.NumErrors.ToString & ": " & S
            DTFile = PathDLog & "\Log\ERR" & Format(DT, "yy") & Format(DT, "MM") & Format(DT, "dd")

            If FlashRed Then
                HMIErrors.FlashRed = True
                fHMI.ListError.BackColor = System.Drawing.Color.Red
            End If

            If HMIErrors.NumErrors < 1000 Then
                My.Computer.FileSystem.WriteAllText(DTFile & ".txt", S & vbCrLf, True)
                Me.ListError.Items.Add(S)
            ElseIf HMIErrors.NumErrors = 1000 Then
                Me.ListError.Items.Add(S)
                S = DT.ToString & ": Alarm " & HMIErrors.NumErrors.ToString & ": " & "Alarm Limit exceeded, Clear All to continue Error recording"
                Me.ListError.Items.Add(S)
                HMIErrors.FlashRed = True
                fHMI.ListError.BackColor = System.Drawing.Color.Red
            End If
            Me.ListError.TopIndex = Me.ListError.Items.Count - 1
        End If

    End Sub



It is best to have a UnhandledException handler in MyApplication Events:

        Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException
            ErrorHandler(e.Exception.ToString)
            Application.DoEvents()
            e.ExitApplication = False
        End Sub


Also need a declaration:

Public Event UnhandledException(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)

This will let your program continue to run in the event of an unhandled exception.

Note that it doesn't work with the debugger.  It only works if started from an .exe

Test with:

Dim v(1) As Integer
v(10) = v(10)