Solved

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

Posted on 2011-03-24
3
1,121 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

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create and use a custom WaterMark textbox class.  The custom WaterMark textbox class allows you to set the WaterMark Background Color and WaterMark text at design time.   IMAGE OF WATERMARKS STEPS Create VB …
It was really hard time for me to get the understanding of Delegates in C#. I went through many websites and articles but I found them very clumsy. After going through those sites, I noted down the points in a easy way so here I am sharing that unde…
This video shows how to quickly and easily add an email signature for all users on Exchange 2016. The resulting signature is applied on a server level by Exchange Online. The email signature template has been downloaded from: www.mail-signatures…
With Secure Portal Encryption, the recipient is sent a link to their email address directing them to the email laundry delivery page. From there, the recipient will be required to enter a user name and password to enter the page. Once the recipient …

856 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