-
Apr 17th, 2014, 12:06 PM
#1
Thread Starter
Member
[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
-
Apr 17th, 2014, 12:27 PM
#2
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.
-
Apr 17th, 2014, 12:36 PM
#3
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
-
Apr 17th, 2014, 12:52 PM
#4
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
-
Apr 17th, 2014, 01:23 PM
#5
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
-
Apr 17th, 2014, 06:24 PM
#6
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by kaliman79912
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).
-
Apr 17th, 2014, 06:28 PM
#7
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by dbasnett
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?
-
Apr 17th, 2014, 06:29 PM
#8
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by ident
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.
-
Apr 17th, 2014, 06:39 PM
#9
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by passel
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.
-
Apr 17th, 2014, 07:03 PM
#10
Thread Starter
Member
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?
-
Apr 17th, 2014, 07:08 PM
#11
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by Solo44
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.
-
Apr 18th, 2014, 09:37 AM
#12
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by dbasnett
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.
-
Apr 18th, 2014, 12:17 PM
#13
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.
-
Apr 18th, 2014, 03:27 PM
#14
Re: VB 2010 Express - Serial port threading problem?
There is no need to write a custom delegate. Use MethodInvoker or Action(of,
vb Code:
Public Class Form1 Private Sub ReceivedText(ByVal text As String) Me.BeginInvoke(Sub() Me.TextBox1.Text = text) End Sub End Class
-
Apr 19th, 2014, 12:49 PM
#15
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
-
Apr 19th, 2014, 03:02 PM
#16
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by dbasnett
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.
-
Apr 26th, 2014, 10:29 PM
#17
Thread Starter
Member
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:
There's probably a very simple solution?
-
Apr 27th, 2014, 01:48 AM
#18
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.
-
Apr 27th, 2014, 08:15 AM
#19
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by passel
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.
-
Apr 27th, 2014, 03:58 PM
#20
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by dbasnett
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.
-
Apr 27th, 2014, 04:16 PM
#21
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.
-
Apr 27th, 2014, 05:34 PM
#22
Thread Starter
Member
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by dbasnett
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)
-
Apr 27th, 2014, 05:55 PM
#23
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.
-
Apr 27th, 2014, 07:03 PM
#24
Thread Starter
Member
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.
-
Apr 27th, 2014, 08:44 PM
#25
Re: VB 2010 Express - Serial port threading problem?
Originally Posted by Solo44
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.
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|