Results 1 to 25 of 25

Thread: [RESOLVED] VB 2010 Express - Serial port threading problem?

  1. #1

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Resolved [RESOLVED] VB 2010 Express - Serial port threading problem?

    Running on Win 7. The situation is: I have a hardware device sitting on a serial port and it accepts a set of string commands and returns a response string to each command. As a base, I used a set of code I found on the Web that uses a delegate and separate thread and it works just fine up to a point. The requirement is to be able to poll the device up to every 200 ms. with two commands and send other commands less frequently. The device runs at 57,600 bps.

    I can send one of the commands every 200ms and receive the responses perfectly and that will run OK for many minutes. If I then add a second command to the sequence and read its response, responses to the first command are received OK for a short while but responses to the second command are never received. These commands are sent and responses read in a timer tick event. So, the sequence would be: send command 1, read response, send command 2, read response. The code includes the ability to send the commands individually under button control and both commands work just fine.

    When running in the timer mode, the commands are being sent and responses to both commands are being received OK but the received data isn't there for the second command. I think the problem lies with possible misunderstanding of the delegate and threading on my part. Any suggestions greatly appreciated. Here's the code:

    Code:
    Imports System
    Imports System.ComponentModel
    Imports System.Threading
    Imports System.IO.Ports
    
    Public Class frmMain
        Dim myPort As Array  'COM Ports detected on the system will be stored here
        Delegate Sub SetTextCallback(ByVal [text] As String) 'Added to prevent threading errors during receiving of data
        Dim interval As String
    
        ' The user can send a single ?AF or ?BF command with the SEND button.  Or, he can send the commands repeatedly
        ' by pressing the TIMED SEND button.  In that case, the tick event sends the ?AF, reads the response, and then
        ' sends the ?BF command and reads that response.  The responses to ?AF and ?BF should update the text boxes 
        ' labeled VFO A and VFO B with the values returned. 
    
        Private Sub frmMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    
            'When our form loads, auto detect all serial ports in the system and populate the cmbPort Combo box.
            myPort = IO.Ports.SerialPort.GetPortNames() 'Get all com ports available
    
            For i = 0 To UBound(myPort)
                cmbPort.Items.Add(myPort(i))
            Next
            cmbPort.Text = cmbPort.Items.Item(0)    'Set cmbPort text to the first COM port detected
    
            btnDisconnect.Enabled = False           'Initially Disconnect Button is Disabled
    
        End Sub
    
        Private Sub btnConnect_Click(sender As System.Object, e As System.EventArgs) Handles btnConnect.Click
    
            SerialPort1.PortName = cmbPort.Text         'Set SerialPort1 to the selected COM port at startup
            SerialPort1.Open()
    
            btnConnect.Enabled = False          'Disable Connect button
            btnDisconnect.Enabled = True        'and Enable Disconnect button
    
        End Sub
    
        Private Sub btnDisconnect_Click(sender As System.Object, e As System.EventArgs) Handles btnDisconnect.Click
    
            SerialPort1.Close()             'Close our Serial Port
            btnConnect.Enabled = True
            btnDisconnect.Enabled = False
            Me.Close()
    
        End Sub
    
        Private Sub btnSend_Click(sender As System.Object, e As System.EventArgs) Handles btnSend.Click
    
            SerialPort1.Write(txtTransmit.Text & vbCr) 'The text contained in the txtText will be sent to the serial port as ascii
            'plus the carriage return (Enter Key) 
    
        End Sub
    
        Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
    
            ReceivedText(SerialPort1.ReadExisting())    'Automatically called every time a data is received at the serialPort
    
        End Sub
    
        Private Sub ReceivedText(ByVal [text] As String)
    
            'compares the ID of the creating Thread to the ID of the calling Thread
            If Me.rtbReceived.InvokeRequired Then
                Dim x As New SetTextCallback(AddressOf ReceivedText)
                Me.Invoke(x, New Object() {(text)})
            Else
                Me.rtbReceived.Text &= [text]
            End If
    
        End Sub
    
        Private Sub cmbPort_SelectedIndexChanged(sender As System.Object, e As System.EventArgs) Handles cmbPort.SelectedIndexChanged
            If SerialPort1.IsOpen = False Then
                SerialPort1.PortName = cmbPort.Text         'pop a message box to user if he is changing ports
            Else                                                                               'without disconnecting first.
                MsgBox("Valid only if port is Closed", vbCritical)
            End If
        End Sub
    
        Private Sub Timer1_Tick(sender As System.Object, e As System.EventArgs) Handles tmrTimer1.Tick
    
            Dim freq As String
    
            'Each time tick occurs, we send a request to the Orion
            SerialPort1.Write("?AF" & vbCr) 'Requests current value of VFO-A along with carriage return
    
            'Response to the ?AF is a string like "@AF14236789 which will be displayed as: "14.236.789"
            If Me.rtbReceived.Text.StartsWith("@AF") Then
                freq = Mid(rtbReceived.Text, 4, 2) & "." & Mid(rtbReceived.Text, 6, 3) & "." & Mid(rtbReceived.Text, 9, 3)
                freq = freq.TrimStart("0")
                If freq.Length = 9 Then
                    txtVFOA.Text = "  " & freq
                Else
                    txtVFOA.Text = freq
                End If
                rtbReceived.Text = ""
            End If
    
            SerialPort1.Write("?BF" & vbCr) 'Requests current value of VFO-B along with carriage return
    
            'Response to the ?BF is a string like "@BF14236789 which will be displayed as: "14.236.789"
            If Me.rtbReceived.Text.StartsWith("@BF") Then
                freq = Mid(rtbReceived.Text, 4, 2) & "." & Mid(rtbReceived.Text, 6, 3) & "." & Mid(rtbReceived.Text, 9, 3)
                freq = freq.TrimStart("0")
                If freq.Length = 9 Then
                    txtVFOB.Text = "  " & freq
                Else
                    txtVFOB.Text = freq
                End If
                rtbReceived.Text = ""
            End If
    
    
        End Sub
    
    
        Private Sub btnTimerOn_Click(sender As System.Object, e As System.EventArgs) Handles btnTimerOn.Click
    
            tmrTimer1.Enabled = True
            tmrTimer1.Interval = CInt(cmbTimerInterval.Text)
    
        End Sub
    
        Private Sub btnStopTimedSend_Click(sender As System.Object, e As System.EventArgs) Handles btnStopTimedSend.Click
    
            tmrTimer1.Enabled = False
    
        End Sub
    
    End Class

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: VB 2010 Express - Serial port threading problem?

    Well, I'm unfortunately busy today so can't fully test or respond to posts as I would like, but an issue that most of these serial port questions have in common is that the user is not treating the serial data as a stream.
    When you do ReadExisting, you will read however many bytes (or characters) have been received so far, which may very well be less than you are expecting. so if you're expecting 11 characters, and you've only received the first 3 to 10 characters at the time you read, you need to wait until the next read to receive the rest of the characters so you have a full response.
    I mentioned, 3 as the lower value because if it was less than 3, you woundn't match so may end up loosing that response, but hopefully sync up again later. If if was 3 or more, you would probably match your "header", and then try to parse the "fields", without having them all, so would have other issues.

    That is about all I can say for now, my free time is lacking.
    Once possible, not robust "fix", might be to change the logic so that your timer runs at 100ms, and issues a command on one tick, then looks for the response in the next tick. That 100ms delay might allow for the response to be fully receieved before you try to look at it.
    Last edited by passel; Apr 17th, 2014 at 12:31 PM.

  3. #3
    PowerPoster kaliman79912's Avatar
    Join Date
    Jan 2009
    Location
    Ciudad Juarez, Chihuahua. Mexico
    Posts
    2,593

    Re: VB 2010 Express - Serial port threading problem?

    As we do not have the same equipment as you do and it is impossible to test it. I would just recommend a few things. On the Timer1_Tick sub, you write to the serial port and then check the value of rtbReceived. Instead you should wait until there is something there before checking (put a time limit for that) and then do the same after the other write to the port.
    More important than the will to succeed, is the will to prepare for success.

    Please rate the posts, your comments are the fuel to keep helping people

  4. #4
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: VB 2010 Express - Serial port threading problem?

    Your delegation is very out dated. Take a look at JMC article here http://www.vbforums.com/showthread.p...Worker-Threads

  5. #5
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Here is a pattern to deal with data reception. Notice how little the DataReceived event handler does. The work of decoding the protocol is moved to another thread. You should be able to accomplish what you want in 'ProcessProto'

    Code:
        Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
            pthrd = New Threading.Thread(AddressOf ProcessProto)
            pthrd.IsBackground = True
            pthrd.Start()
        End Sub
    
        Dim pthrd As Threading.Thread
    
        Dim buf As New System.Text.StringBuilder
        Dim bufLock As New Object
    
        Dim chkBuf As New Threading.AutoResetEvent(False)
    
        Private Sub ProcessProto()
            Do
                'process received message
                'make certain to remove messages from the buffer
                'and when doing so lock it
                If buf.Length > 0 Then
    
                End If
                chkBuf.WaitOne(5000)
            Loop
        End Sub
    
        Private Sub SerialPort1_DataReceived(sender As Object, _
                                             e As IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
            If SerialPort1.BytesToRead > 0 Then
                Threading.Monitor.Enter(bufLock) 'lock the buffer while changing
                buf.Append(SerialPort1.ReadExisting)
                Threading.Monitor.Exit(bufLock) 'inlock the buffer
                chkBuf.Set()
            End If
        End Sub
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  6. #6

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by kaliman79912 View Post
    As we do not have the same equipment as you do and it is impossible to test it. I would just recommend a few things. On the Timer1_Tick sub, you write to the serial port and then check the value of rtbReceived. Instead you should wait until there is something there before checking (put a time limit for that) and then do the same after the other write to the port.
    I'm doing it that way because the documentation and explanations I've read so far leave many things hanging. For example, I would think that if I used the Readline method that it would not complete until the entire string ending with CR is in the buffer. In fact, I've never gotten a Readline to complete ever, even though the data has clearly been sent. Another thing very unclear is just when and how often is the DataReceived event triggered. Does that happen when the first character is received? Does it happen again for every character (realizing that maybe two or more characters may have arrived in the meantime). If the goal is to receive the character string of known length, do I have to count the bytes until I know they're all here or is there a method that will pass me just the completed string (like I thought Readline was supposed to do).

  7. #7

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by dbasnett View Post
    Here is a pattern to deal with data reception. Notice how little the DataReceived event handler does. The work of decoding the protocol is moved to another thread. You should be able to accomplish what you want in 'ProcessProto'
    I still have some basic gaps in my understanding of just what VB is doing in some cases. For example, how often does the DataReceived event handler get triggered when receiving a 12 byte string at 56.7 Kbps? If VB knows what the BytesToRead value is when the handler is entered, doesn't that mean that the entire string has been received already?

  8. #8

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by ident View Post
    Your delegation is very out dated. Take a look at JMC article here http://www.vbforums.com/showthread.p...Worker-Threads
    The example you pointed to had a date of 2007. The one I used was dated 2010 and written specifically for VB 2010 - so not sure how I can tell when something is outdated or not.

  9. #9

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by passel View Post
    When you do ReadExisting, you will read however many bytes (or characters) have been received so far, which may very well be less than you are expecting.
    OK - I understand that. I used the ReadExisting approach because that's what was in the working example. Is there an existing method that would handle the waiting for me until the end of the message is received (in this case, every message either way ends with CR). The purpose of this exercise is to allow programs running through two other serial ports to share information obtained from this device on this serial port. The VB program would constantly poll the device and store the info in variables and would then forward that to the other two ports if and when requested. All of these links are running at 57.6 kbps and if I have to introduce delays on all three, I'm concerned about the ability to sustain the desired rate of data flow.

  10. #10

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Here's more information on my situation and what I think may be happening. I removed the statements in the Tick routine that set rtbReceived.Text to "". As a result, I can now see every response from the device and every expected response string is there and complete. I can set a much longer timer interval and see that every response is received.
    The code in the timer tick event sets the formatted received response into a text box (two - one for each of the two types of command responses). The text for VFOA is always displayed in its text box and in timely fashion. The text for VFOB is NEVER displayed in its text box. Based on the little I know about thread interaction, it seems its not possible to set a variable in one thread from another thread. So, I'm assuming that (for reasons I don't understand) that may be the cause of the problem. Is there some way that I can obtain the thread ID or whatever its called so I can log it and see?

  11. #11
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by Solo44 View Post
    I still have some basic gaps in my understanding of just what VB is doing in some cases. For example, how often does the DataReceived event handler get triggered when receiving a 12 byte string at 56.7 Kbps? If VB knows what the BytesToRead value is when the handler is entered, doesn't that mean that the entire string has been received already?
    My advice is to read what is there when it is there and to worry about the protocol elsewhere. There are several layers of software at play here, and trying to do too much in the handler is problematic.

    It looks like the protocol is an @followed by two known characters, some variable amount of characters, and then a CR.

    Running several ports at 57.6 Kbps, 7,200 bytes per second, is not an issue unless you cause one. Normally the problem is stalling the handler while trying to update the UI, which is why I take the approach I presented.

    edit:
    What the code to process received data might look like
    Code:
        Private Sub ProcessProto() 'from post #5
            Dim rcvdMessage As New System.Text.StringBuilder
            Dim messCmplt As Boolean = False
            Do
                'process received message
                'make certain to remove messages from the buffer
                'and when doing so lock it
                Do While buf.Length > 0
                    Threading.Monitor.Enter(bufLock)
                    Dim rcvdCH As String = buf(0)
                    buf.Remove(0, 1)
                    Threading.Monitor.Exit(bufLock)
                    If rcvdCH = "@" AndAlso rcvdMessage.Length = 0 Then
                        rcvdMessage.Append(rcvdCH)
                    ElseIf rcvdCH = "@" Then
                        'error?
                    ElseIf rcvdCH = ControlChars.Cr Then
                        messCmplt = True
                        Exit Do
                    Else
                        rcvdMessage.Append(rcvdCH)
                    End If
                Loop
                If messCmplt Then
                    'a complete message
                    Dim theMessage As String = rcvdMessage.ToString
                    'reset for next message
                    messCmplt = False
                    rcvdMessage.Length = 0
                    'process message just rcvd
                    Me.Invoke(Sub()
                                  Label1.Text = theMessage
                              End Sub)
                End If
                chkBuf.WaitOne(5000) 'wait for more data
            Loop
        End Sub
    Last edited by dbasnett; Apr 18th, 2014 at 07:33 AM.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  12. #12

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by dbasnett View Post
    My advice is to read what is there when it is there and to worry about the protocol elsewhere. There are several layers of software at play here, and trying to do too much in the handler is problematic.
    Well, I implemented your approach and now have the basic code working. I have a better understanding of how the threads function now. I didn't realize that I could define a new thread to run continuously in the background and that was the part that was missing. The VB course book I used never mentioned things like StringBuilder, locking and in fact didn't even mention threads at all. Not new concepts to me, but had no idea they existed or how to implement them in VB.

    Your code example was very clear and understandable, after reading about several of the things new to me. The one fault I've found with all the documentation I've seen on VB (VB help, books and forum responses) is the tendency of people answering questions or explaining things to jump directly to long and complex code examples without adequately explaining the concepts. The entire threading subject could be easily explained with a couple of nice diagrams that included notes on how threads can trigger or respond to events in other threads, how threads are initiated and terminated, etc. Anyway, thanks for the advice and I'll probably be back this way if I get into a jam again.

  13. #13
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    There are some threading threads in the forum. Don't know if they are any good or not.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  14. #14
    Bad man! ident's Avatar
    Join Date
    Mar 2009
    Location
    Cambridge
    Posts
    5,398

    Re: VB 2010 Express - Serial port threading problem?

    There is no need to write a custom delegate. Use MethodInvoker or Action(of,

    vb Code:
    1. Public Class Form1
    2.  
    3.     Private Sub ReceivedText(ByVal text As String)
    4.         Me.BeginInvoke(Sub() Me.TextBox1.Text = text)
    5.     End Sub
    6.  
    7. End Class

  15. #15
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Normally (I'd like to say always) the code I use looks like this, in which I read bytes and only convert to strings if required. The form has one label, two comboboxes, and one button.

    Code:
    Imports System.IO.Ports
    
    Public Class Form1
        Dim WithEvents sp As New SerialPort
        Dim isRun As New Threading.ManualResetEvent(False)
    
        Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
            Try
                isRun.Reset() 'closing
                'wake the threads
                rcvd.Set()
                procproto.Set()
                'wait on them
                rcvdThrd.Join()
                procprotoThrd.Join()
                If sp.IsOpen Then sp.Close()
            Catch ex As Exception
    
            End Try
        End Sub
    
        Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles Me.Shown
            'add port names to combobox
            cboxPN.Items.AddRange(SerialPort.GetPortNames)
            'and speeds
            cboxSPD.Items.AddRange([Enum].GetNames(GetType(PortSpeeds)))
            'other setting could be done...
    
            'this thread reads bytes and adds them to the buffer
            rcvdThrd = New Threading.Thread(AddressOf Receive)
            rcvdThrd.IsBackground = True
    
            'this thread examines the buffer to see if a 
            'complete message, as defined by the protocol, is available
            procprotoThrd = New Threading.Thread(AddressOf Protocol)
            procprotoThrd.IsBackground = True
    
            isRun.Set() 'we are running
            procprotoThrd.Start()
            rcvdThrd.Start()
        End Sub
    
        Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
            'open
            If Not sp.IsOpen AndAlso cboxPN.SelectedIndex >= 0 AndAlso cboxSPD.SelectedIndex >= 0 Then
                'port name and speed selected
                With sp
                    .PortName = cboxPN.SelectedItem.ToString
                    .BaudRate = CInt([Enum].Parse(GetType(PortSpeeds), cboxSPD.SelectedItem.ToString))
                    'other settings
                    .DataBits = 8
                    .Parity = Parity.None
                    .StopBits = StopBits.One
                    'if using strings this encoding decodes all 8 bit values
                    .Encoding = System.Text.Encoding.GetEncoding(28591)
                End With
                Try
                    sp.Open()
                    sp.DtrEnable = True 'turn DTR on
                    Debug.WriteLine("is open")
                    sp.WriteLine("This is a test")
                Catch ex As Exception
                    'open failed code here
                    Debug.WriteLine(ex.Message)
                End Try
            End If
        End Sub
    
        Private Sub sp_DataReceived(sender As Object, _
                                    e As SerialDataReceivedEventArgs) _
                                Handles sp.DataReceived
            rcvd.Set() 'read the bytes
        End Sub
    
        Dim rcvd As New Threading.AutoResetEvent(False)
        Dim rcvdThrd As Threading.Thread
        Private Sub Receive()
            Do
                If sp.IsOpen Then 'is port open
                    Dim numb As Integer = sp.BytesToRead 'number of bytes to read
                    If numb > 0 Then
                        Dim temp(numb - 1) As Byte 'create a temporary buffer
                        Try
                            numb = sp.Read(temp, 0, numb) 'read the bytes
                            If numb <> temp.Length Then
                                Array.Resize(temp, numb)
                            End If
                            'add temp buffer to public buffer
                            Threading.Monitor.Enter(bufLock)
                            buf.AddRange(temp)
                            Threading.Monitor.Exit(bufLock)
                            procproto.Set() 'check for possible message
                        Catch ex As Exception
                            'fix error handler
                        End Try
                    End If
                End If
                rcvd.WaitOne() 'wait for event handler to fire
            Loop While isRun.WaitOne(0)
        End Sub
    
        Dim buf As New List(Of Byte)
        Dim bufLock As New Object
    
        Dim procproto As New Threading.AutoResetEvent(False)
        Dim procprotoThrd As Threading.Thread
        'in this example the protocol is
        'some number of chars followed by 'eol'
        Private Sub Protocol()
            Const eol As Byte = 10 'EOL = LF
            Do
                If buf.Count > 0 Then 'some data present
                    'is it a complete message as defined by our protocol?
                    Dim idx As Integer
                    Do
                        idx = buf.IndexOf(eol)
                        If idx >= 0 Then 'do I have a EOL?
                            'yes, extract message
                            Dim mess As New System.Text.StringBuilder
                            'get message without EOL and
                            'get rid of processed data in buffer
                            Threading.Monitor.Enter(bufLock)
                            mess.Append(sp.Encoding.GetChars(buf.ToArray, 0, idx))
                            buf.RemoveRange(0, idx + 1)
                            Threading.Monitor.Exit(bufLock)
                            '
                            'process the message, in this case...
                            'show message to user
    
                            Me.BeginInvoke(Sub()
                                               Label1.Text = mess.ToString
                                               Label1.Refresh()
                                           End Sub)
                        End If
                    Loop While idx >= 0
                End If
                procproto.WaitOne() 'wait for signal to proccess next potential message
            Loop While isRun.WaitOne(0)
        End Sub
    
        Private Sub sp_ErrorReceived(sender As Object, _
                                     e As SerialErrorReceivedEventArgs) Handles sp.ErrorReceived
            Debug.WriteLine(e.EventType.ToString)
        End Sub
    
        Private Sub sp_PinChanged(sender As Object, _
                                  e As SerialPinChangedEventArgs) Handles sp.PinChanged
            Debug.WriteLine(e.EventType.ToString)
        End Sub
    End Class
    
    ''' <summary>
    ''' enumerates 'standard' port speeds
    ''' </summary>
    ''' <remarks>derived from many sources</remarks>
    Public Enum PortSpeeds
        b110 = 110
        b300 = 300
        b600 = 600
        b1200 = 1200
        b2400 = 2400
        b4800 = 4800
        b9600 = 9600
        b14400 = 14400
        b19200 = 19200
        b28800 = 28800
        b38400 = 38400
        b56000 = 56000
        b57600 = 57600
        b115200 = 115200
        b128000 = 128000
        b153600 = 153600
        b230400 = 230400
        b256000 = 256000
        b460800 = 460800
        b921600 = 921600
    End Enum
    What changes is what is inside of Protocol. The threading shown here is about as simple as four threads can interact with each other.
    Last edited by dbasnett; Jun 11th, 2014 at 11:17 AM. Reason: change receive to protocol
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  16. #16

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by dbasnett View Post
    Normally (I'd like to say always) the code I use looks like this, in which I read bytes and only convert to strings if required.
    What changes is what is inside of Protocol. The threading shown here is about as simple as four threads can interact with each other.
    That's great and very timely. I've determined that my current approach doesn't work very well with a high rate of polling the device. Right now, I'm blasting 12 writes out of the timer tick routine with no delays between and then handling the responses as a stream. What I really want to do is to send the commands one at a time and process the response and not send another command until the previous response is received (no need to wait for the processing of the response to be complete). The device has no way to notify my end that commands are being received faster than they can be processed. I understand the threading mechanism now pretty well, but need to dig into the signaling so I can wake up one thread when something occurs in another. Thanks for your help on this.

  17. #17

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Well, I'm getting very close with this. The program is now working with three serial ports and 7 threads in all with a very high message rate and all works well, except.... Every now and then, I get an error on the statement marked "ERROR HERE" in this code segment:

    Code:
     'some number of chars followed by a LF
        Private Sub Protocol()
            Const eol As Byte = 10 'LF
            Do
                If buf.Count > 0 Then 'some data present
                    'is it a complete message as defined by our protocol?
                    Dim idx As Integer
                    Do
                        idx = buf.IndexOf(eol)
                        If idx >= 0 Then 'do I have a LF?
                            'yes, extract message
                            Dim mess As New System.Text.StringBuilder
                            'get message without LF
                            mess.Append(sp.Encoding.GetChars(buf.ToArray, 0, idx))   [ERROR HERE]
                            '
                            'process the message, in this case...
                            'show message to user
                            Me.BeginInvoke(Sub()
                                               Label1.Text = mess.ToString
                                           End Sub)
                            'get rid of processed data in buffer
                            Threading.Monitor.Enter(bufLock)
                            buf.RemoveRange(0, idx + 1)
                            Threading.Monitor.Exit(bufLock)
                        End If
                    Loop While idx >= 0
                End If
                procproto.WaitOne() 'wait for signal to proccess next potential message
    I've tried just using your original code and I have the same problem. I'm afraid I just don't know enough about VB yet to understand what's going on. Here's an image showing the error:

    Name:  SP2 processing error.jpg
Views: 5474
Size:  18.4 KB

    There's probably a very simple solution?

  18. #18
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: VB 2010 Express - Serial port threading problem?

    What was the value of idx when you got the error?
    You're checking for greater than or equal to 0, but if it was 0 would a string with only a linefeed in it convert correctly?
    Perhaps you want > 0, not >= 0.

  19. #19
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by passel View Post
    What was the value of idx when you got the error?
    You're checking for greater than or equal to 0, but if it was 0 would a string with only a linefeed in it convert correctly?
    Perhaps you want > 0, not >= 0.
    An index of 0 is fine. Make this change to the inner most Do and try it.
    Code:
                        idx = buf.IndexOf(eol)
                        If idx >= 0 Then 'do I have a LF?
                            'yes, extract message
                            Dim mess As New System.Text.StringBuilder
                            'get message without LF and
                            'get rid of processed data in buffer
                            Threading.Monitor.Enter(bufLock)
                            mess.Append(sp.Encoding.GetChars(buf.ToArray, 0, idx))
                            buf.RemoveRange(0, idx + 1)
                            Threading.Monitor.Exit(bufLock)
                            '
                            'process the message, in this case...
                            'show message to user
    
                            Me.BeginInvoke(Sub()
                                               Label1.Text = mess.ToString
                                               Label1.Refresh()
                                           End Sub)
                        End If
    If you aren't sure where it goes let me know. When I looked at what I posted it seemed clear that I had made a mistake. The list of bytes (buf) was being manipulated outside of the lock.
    Last edited by dbasnett; Apr 27th, 2014 at 08:20 AM.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  20. #20

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by dbasnett View Post
    An index of 0 is fine. Make this change to the inner most Do and try it.
    Code:
                        idx = buf.IndexOf(eol)
                        If idx >= 0 Then 'do I have a LF?
                            'yes, extract message
                            Dim mess As New System.Text.StringBuilder
                            'get message without LF and
                            'get rid of processed data in buffer
                            Threading.Monitor.Enter(bufLock)
                            mess.Append(sp.Encoding.GetChars(buf.ToArray, 0, idx))
                            buf.RemoveRange(0, idx + 1)
                            Threading.Monitor.Exit(bufLock)
                            '
                            'process the message, in this case...
                            'show message to user
    
                            Me.BeginInvoke(Sub()
                                               Label1.Text = mess.ToString
                                               Label1.Refresh()
                                           End Sub)
                        End If
    If you aren't sure where it goes let me know. When I looked at what I posted it seemed clear that I had made a mistake. The list of bytes (buf) was being manipulated outside of the lock.
    OK - I understand manipulating buf outside the lock. But when I put my message processing where your comments say to put it, nothing gets processed ever. For one thing, I have no idea what this statement is trying to do:

    mess.Append(sp.Encoding.GetChars(buf.ToArray, 0, idx))

    Assuming that the intent of it is that the message to be processed is in buf, I don't see how that can be as the statement " buf.RemoveRange(0, idx + 1)" seems to have just removed that message before we got to the processing. Some other puzzles to me: Why did we define buf as a byte list to begin with? Everything in this application is pure character - there's no data that isn't character. And, I don't understand why we're working with an array at this moment.

    I was blindly using your code without understand those items from the beginning, but since it was working (sort of), I elected not to question those things in the beginning. Thanks for spending the time on this and I think I'm just a small step away from everything being right.

  21. #21
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    The code was giving you an error on a certain line. Because of the location of the line we know there is a LF. You saying that nothing gets processed doesn't make sense. It may be that all there is are LF's in the buffer, but they are getting processed. Put a breakpoint on the Threading.Monitor.Enter line and step through the code, looking at the buffer as you go. You may have to post your actual code.

    The line in question converts the bytes to chars and adds them to the string builder. Why? Because this code comes from a Class that I have that handles a variety of SerialPort input. The bytes are added to the stringbuilder, then deleted from the buffer.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  22. #22

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by dbasnett View Post
    The code was giving you an error on a certain line. Because of the location of the line we know there is a LF. You saying that nothing gets processed doesn't make sense.
    OK - now all is well. I posted the code below just so you could see where I picked off the message for processing. It ran for over an hour with no problem and previously would fail in 1-5 minutes. I happened to mouse on the link in your signature line, the entry about your first computer. You must be almost as old as I am! On my first assignment, that was the new machine on the raised floor.

    Code:
      Do
    
                If SP2buf.Count > 0 Then   'we have a message to process
                    Dim idx As Integer
                    Do
                        idx = SP2buf.IndexOf(13)
                        If idx > 0 Then   'Is the CR there?
                            Dim mess As New System.Text.StringBuilder
                            Threading.Monitor.Enter(SP2buflock)
                            mess.Append(SP2.Encoding.GetChars(SP2buf.ToArray, 0, idx))      'get the message without the vbcr
                            Threading.Monitor.Exit(SP2buflock)
                            SP2Request = mess.ToString
                            Threading.Monitor.Enter(SP2buflock)
                            SP2buf.RemoveRange(0, idx + 1)      'get rid of the processed data in the buffer
                            Threading.Monitor.Exit(SP2buflock)
                            If Microsoft.VisualBasic.Left(SP2Request, 1) = "*" Then
                                If rbSP2TraceOn.Checked = True Then
                                    'Write the message to the trace box
                                    Me.Invoke(Sub()
                                                  Threading.Monitor.Enter(SP2buflock)
                                                  rtbSP2Traffic.Text &= SP2Request & vbCr
                                                  Threading.Monitor.Exit(SP2buflock)
                                              End Sub)
    
                                    'Now process the message
                                    Select Case Mid(SP2Request, 1, 3)
    
                                        Case "*AF"
                                            freq = Mid(SP2Request, 4, 2) & "." & Mid(SP2Request, 7, 3) & "." & Mid(SP2Request, 10, 3)
                                            freq = freq.TrimStart("0")   'kill the leading zero
                                            If freq.Length = 9 Then   'Indicates a single digit MHz and need padding to maintain display position
                                                Me.Invoke(Sub()
                                                              txtVFOA.Text = "  " & freq
                                                          End Sub)
                                            Else
                                                Me.Invoke(Sub()
                                                              txtVFOA.Text = freq
                                                          End Sub)
                                            End If
    
                                        Case "*BF"
                                            freq = Mid(SP2Request, 4, 2) & "." & Mid(SP2Request, 7, 3) & "." & Mid(SP2Request, 10, 3)
                                            freq = freq.TrimStart("0")   'kill the leading zero
                                            If freq.Length = 9 Then   'Indicates a single digit MHz and need padding to maintain display position
                                                Me.Invoke(Sub()
                                                              txtVFOB.Text = "  " & freq
                                                          End Sub)
                                            Else
                                                Me.Invoke(Sub()
                                                              txtVFOB.Text = freq
                                                          End Sub)
                                            End If
                                    End Select
                                End If
    
                                Select Case Mid(SP2Request, 1, 4)
    
                                    Case "*RMF"
                                        Me.Invoke(Sub()
                                                      txbMainFilter.Text = Mid(SP2Request, 5, SP2Request.Length - 4)
                                                  End Sub)
    
                                End Select
    
                                SP1.Write(SP2Request & vbCr)    'Send the command to the Orion immediately
                                If rbOrionTraceOn.Checked = True Then
    
                                    'Write the command to the Orion trace box
                                    Me.Invoke(Sub()
                                                  rtbOrionTraffic.Text &= SP2Request & vbCr
                                              End Sub)
                                End If
    
                            End If
    
                            'Log to SP2 trace 
    
                            If rbSP2TraceOn.Checked = True Then
                                Me.Invoke(Sub()
                                              Threading.Monitor.Enter(SP2buflock)
                                              rtbSP2Traffic.Text &= SP2Request & vbCr  'Log to SP2 trace
                                              Threading.Monitor.Exit(SP2buflock)
                                          End Sub)
                            End If
                        End If
                    Loop While idx >= 0
                End If
    
                SP2respReceived.WaitOne()       'Wait for signal to process next message
    
            Loop While isRun.WaitOne(0)

  23. #23
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Glad it works. I did notice a few things. The only time it is required to lock the buffer is if you are manipulating it. When you lock it the other thread could be blocked. Once the string is in SP2Request I don't see the need to lock the buffer, so remove the locking in the Invokes. Also,

    Code:
                            'replace this
                            Dim mess As New System.Text.StringBuilder
                            Threading.Monitor.Enter(SP2buflock)
                            mess.Append(SP2.Encoding.GetChars(SP2buf.ToArray, 0, idx))      'get the message without the vbcr
                            Threading.Monitor.Exit(SP2buflock)
                            SP2Request = mess.ToString
                            Threading.Monitor.Enter(SP2buflock)
                            SP2buf.RemoveRange(0, idx + 1)      'get rid of the processed data in the buffer
                            Threading.Monitor.Exit(SP2buflock)
    
                            'with this
                            Threading.Monitor.Enter(SP2buflock)
                            SP2Request = SP2.Encoding.GetChars(SP2buf.ToArray, 0, idx)     'get the message without the vbcr
                            SP2buf.RemoveRange(0, idx + 1)      'get rid of the processed data in the buffer
                            Threading.Monitor.Exit(SP2buflock)
    I will be 60 in July.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

  24. #24

    Thread Starter
    Member
    Join Date
    Apr 2014
    Posts
    62

    Re: VB 2010 Express - Serial port threading problem?

    Will make that change, thanks. I'm going to mark this one resolved. Well, you're still a youngster. I turned 70 last month. Almost all of my development work in the past has been in assembler, so the object stuff is new to me. Never worked on the 1620, but the install in my customer location was in the spring of 1966.

  25. #25
    Powered By Medtronic dbasnett's Avatar
    Join Date
    Dec 2007
    Location
    Jefferson City, MO
    Posts
    9,754

    Re: VB 2010 Express - Serial port threading problem?

    Quote Originally Posted by Solo44 View Post
    Will make that change, thanks. I'm going to mark this one resolved. Well, you're still a youngster. I turned 70 last month. Almost all of my development work in the past has been in assembler, so the object stuff is new to me. Never worked on the 1620, but the install in my customer location was in the spring of 1966.
    I wrote a lot of assembler on the 1620. It was an odd machine. It didn't have fixed length words, was BCD based, and did addition with a look up table.

    Glad you resolved your issue.
    My First Computer -- Documentation Link (RT?M) -- Using the Debugger -- Prime Number Sieve
    Counting Bits -- Subnet Calculator -- UI Guidelines -- >> SerialPort Answer <<

    "Those who use Application.DoEvents have no idea what it does and those who know what it does never use it." John Wein

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width