Solved

VB.NET - SerialPort - Trouble with thread delegation with controls

Posted on 2011-03-24
3
1,090 Views
Last Modified: 2012-05-11
I have been trying to program an interface to a serial controller.  This program will send a series of commands to the controller, waiting for each response and execute code based on the response of the controller.  I am getting the following error then the program received the second of four responses from the controller.  I think it has to do with the thread delegation but I am not sure how to fix it.  PLEASE HELP!

System.IndexOutOfRangeException was unhandled
  Message="Index was outside the bounds of the array."
  Source="System.Windows.Forms"
  StackTrace:
       at System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
       at System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
       at WindowsApplication2.Form1.SerialPort1_DataReceived(Object sender, SerialDataReceivedEventArgs e) in C:\Users\tnowacoski\Documents\My Projects\WindowsApplication2\WindowsApplication2\Form1.vb:line 50
       at System.IO.Ports.SerialPort.CatchReceivedEvents(Object src, SerialDataReceivedEventArgs e)
       at System.IO.Ports.SerialStream.EventLoopRunner.CallReceiveEvents(Object state)
       at System.Threading._ThreadPoolWaitCallback.WaitCallback_Context(Object state)
       at System.Threading.ExecutionContext.runTryCode(Object userData)
       at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
       at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading._ThreadPoolWaitCallback.PerformWaitCallbackInternal(_ThreadPoolWaitCallback tpWaitCallBack)
       at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback(Object state)

Here is the code:
Public Class Form1
    Dim tTicks As Integer
    Public Delegate Sub SerialDelegate()


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim SawPrintText() As String = txtScan.Text.Split(",")
        lblTopRail.Text = SawPrintText(9)
        lblBottomRail.Text = SawPrintText(10)
        btnPrint.Focus()
    End Sub

    Private Sub btnPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click
        If SerialPort1.IsOpen Then
            SerialPort1.Close()
        End If
        Try
            With SerialPort1
                .PortName = "COM3"
                .BaudRate = 115000
                .Parity = IO.Ports.Parity.None
                .DataBits = 8
                .StopBits = IO.Ports.StopBits.One
            End With
            SerialPort1.Open()
            lblReady.Text = "Setting Up Printer - Wait!"
            Timer1.Interval = 1000
            Timer1.Enabled = True
        Catch ex As Exception
        End Try
        SerialSend("01")
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        tTicks = 0
    End Sub

    Private Sub Timer1_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer1.Tick
        If tTicks < 10 Then
            tTicks = tTicks + 1
            txtTimer.Text = tTicks
        Else
            Timer1.Stop()
            Timer1.Enabled = False
        End If

    End Sub

    Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
        chkStatus.Invoke(New SerialDelegate(AddressOf ProcessResponse), New Object() {})
    End Sub

    Private Sub ProcessResponse()
        Dim strResponse() As String = SerialPort1.ReadExisting.Split(",")
        If strResponse(1) = "0" Then
            Select Case strResponse(0)
                Case "01"
                    chkStatus.SetItemChecked(0, True)
                    SerialSend("30,1,0," & lblTopRail.Text)
                Case "30"
                    If strResponse(2) = "1" Then
                        chkStatus.SetItemChecked(1, True)
                        SerialSend("30,2,0," & lblBottomRail.Text)
                    ElseIf strResponse(2) = "2" Then
                        chkStatus.SetItemChecked(2, True)
                        SerialSend("07,1")
                    End If
                Case "07"
                    chkStatus.SetItemChecked(3, True)
                    SerialPort1.Close()
            End Select
        End If
    End Sub

    Private Sub SerialSend(ByVal SStr As String)
        Try
            SerialPort1.Write(SStr & vbCr)
        Catch ex As Exception

        End Try
    End Sub
End Class

Open in new window

0
Comment
Question by:tnowacoski
  • 2
3 Comments
 
LVL 85

Expert Comment

by:Mike Tomlinson
ID: 35207065
Most likely the response was not what you expected.

At this line, you split, producing an array:

    Dim strResponse() As String = SerialPort1.ReadExisting.Split(",")

Down below you are assuming that it actually has certain indexes...attempting to access both index 1 and 2.

Use code like this to see if you can narrow down the problem:
Private Sub ProcessResponse()
        Dim response As String = SerialPort1.ReadExisting
        Dim strResponse() As String = response.Split(",")
        If strResponse.GetUpperBound(0) >= 1 Then
            If strResponse(1) = "0" Then
                Select Case strResponse(0)
                    Case "01"
                        chkStatus.SetItemChecked(0, True)
                        SerialSend("30,1,0," & lblTopRail.Text)

                    Case "30"
                        If strResponse.GetUpperBound(0) >= 2 Then
                            If strResponse(2) = "1" Then
                                chkStatus.SetItemChecked(1, True)
                                SerialSend("30,2,0," & lblBottomRail.Text)
                            ElseIf strResponse(2) = "2" Then
                                chkStatus.SetItemChecked(2, True)
                                SerialSend("07,1")
                            End If
                        Else
                            MessageBox.Show("No strResponse(2): " & response, "Unexpected input")
                        End If

                    Case "07"
                        chkStatus.SetItemChecked(3, True)
                        SerialPort1.Close()

                End Select
            End If
        Else
            MessageBox.Show("No strResponse(1): " & response, "Unexpected input")
        End If
    End Sub

Open in new window

0
 

Author Comment

by:tnowacoski
ID: 35209542
Thanks!  Was looking at that with a blind eye!
I will bump the points up to 500 if you can answer this?  Looking at the following code, everything works as expected but the program hangs on SerialPort1.Close(), no exceptions are caught.   Any ideas?

Imports System.Threading
Imports System
Imports System.IO
Imports System.Data

Public Class Form1
    Dim tTicks As Integer
    Private _flash As Boolean = False
    Private filePath As String = "C:\SawPrint.Log"
    Private fileStream As FileStream
    Private streamWriter As StreamWriter

    Public Delegate Sub SerialDelegate()


    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnParse.Click
        Dim SawPrintText() As String = txtScan.Text.Split(",")
        lblTopRail.Text = SawPrintText(9)
        lblBottomRail.Text = SawPrintText(10)
        btnParse.Enabled = False
        btnPrint.Enabled = True
        btnPrint.Focus()
    End Sub

    Private Sub btnPrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnPrint.Click
        Me.Cursor = Cursors.WaitCursor
        If SerialPort1.IsOpen Then
            SerialPort1.Close()
        End If
        Try
            With SerialPort1
                .PortName = "COM3"
                .BaudRate = 115000
                .Parity = IO.Ports.Parity.None
                .DataBits = 8
                .StopBits = IO.Ports.StopBits.One
            End With
            SerialPort1.Open()
            lblReady.Text = "Setting Up Printer - Wait!"
            Me.Refresh()
            Timer1.Interval = 1000
            btnPrint.Enabled = False
            SerialSend("01")
        Catch ex As Exception
            MsgBox("Error Opening Port: " & ex.Message)
        End Try
    End Sub

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        tTicks = 0
        _flash = True
        lblReady.ForeColor = Color.Red
    End Sub


    Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
        chkStatus.Invoke(New SerialDelegate(AddressOf ProcessResponse), New Object() {})
    End Sub

    Private Sub ProcessResponse()
        Dim sndstring As String = String.Empty
        Dim rcvstring As String = SerialPort1.ReadExisting
        Dim strResponse() As String = rcvstring.Split(",")
        If strResponse(0) = "01" Then
            If strResponse(1) = "0" Then
                chkStatus.SetItemChecked(0, True)
                sndstring = "30,1,0," & Chr(34) & lblTopRail.Text & Chr(34)
                SerialSend(sndstring)
            End If
        ElseIf strResponse(0) = "30" Then
            If strResponse(1) = "0" Then
                chkStatus.SetItemChecked(1, True)
                If strResponse(2) = "1" Then
                    chkStatus.SetItemChecked(2, True)
                    sndstring = "30,2,0," & Chr(34) & lblBottomRail.Text & Chr(34)
                Else
                    sndstring = "07,1"
                End If
                SerialSend(sndstring)
            End If
        ElseIf strResponse(0) = "07" Then
            If RTrim(rcvstring.Substring(3, 1)) = "0" Then
                chkStatus.SetItemChecked(3, True)
                lblReady.ForeColor = Color.Green
                lblReady.Text = "Printer Ready!"
                Me.Cursor = Cursors.Default
                btnReset.Focus()
                ClosePort()
            End If
        Else
        End If
        Me.Refresh()
    End Sub
    Private Sub ClosePort()
        Try
            SerialPort1.Close()
        Catch ex As Exception
            MsgBox("Error Closing Port: " & ex.Message)
        End Try

    End Sub
    Private Sub SerialSend(ByVal SStr As String)
        Try
            SerialPort1.Write(SStr + vbCr)
        Catch ex As Exception
            MsgBox("Error Sending to Serial: " & ex.Message)
        End Try
    End Sub

    Private Sub btnReset_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnReset.Click
        Dim i As Integer
        lblReady.Text = "Printer Not Ready!"
        lblReady.ForeColor = Color.Red
        btnParse.Enabled = True
        btnPrint.Enabled = False
        lblTopRail.Text = String.Empty
        lblBottomRail.Text = String.Empty
        For i = 0 To 3
            chkStatus.SetItemChecked(i, False)
        Next
        txtScan.Text = String.Empty
        txtScan.Focus()
    End Sub

End Class

Open in new window

0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 250 total points
ID: 35209598
Glad that was helpful.   =)

To be honest, I have ZERO experience with the SerialPort class.  =\

Looking at the docs, though:
http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.close.aspx

It has this ominous sounding warning:

    "The best practice for any application is to wait for some amount of time after calling the Close method before attempting to call the Open method, as the port may not be closed instantly."

Maybe that has something to do with it?...
0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Since .Net 2.0, Visual Basic has made it easy to create a splash screen and set it via the "Splash Screen" drop down in the Project Properties.  A splash screen set in this manner is automatically created, displayed and closed by the framework itsel…
Introduction As chip makers focus on adding processor cores over increasing clock speed, developers need to utilize the features of modern CPUs.  One of the ways we can do this is by implementing parallel algorithms in our software.   One recent…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
Polish reports in Access so they look terrific. Take yourself to another level. Equations, Back Color, Alternate Back Color. Write easy VBA Code. Tighten space to use less pages. Launch report from a menu, considering criteria only when it is filled…

705 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now