|
-
Oct 2nd, 2017, 03:26 AM
#1
Thread Starter
New Member
[RESOLVED] COM port and threading
Hallo everyone.
I'm a beginner at VB.NET but I got an task to resolve and I find it very interesting, therefore would like to learn more about this. I hope some of you guys can help me understand this.
Here is the situation...
(To make it simple, I'll get directly to my problem, and that is serialport communication.)
I have a program that reads UID from RFID Tags and if neccesary programs them (about 10 Tags per second - if only read performed / 2 Tags per second - if programming needed)
So you can assume that the main key here is fast and reliable communication.
Main problem is that serial port DataReceived event handler is fired (in my oppinion) uncontroled and every time differently. Sometimes I get 3 bytes and right after that another 13, sometimes I get 8 bytes and right after another 8 received and so on. ( I think that you all are familiar with this problem)
1. What would you recommend me, to declare COM port in another class or to keep it in the main Form class? I wanted to separate Main Class and SerialPort Class because of threading and invoking. i noticed that if DataReceived is in Main class, and I try to invoke something else from it, the Main class frezzes for a few ms. Or did I get it wrong. The main thing is, I want to have full control of everything what is happening at any time. As I said, when I need only to read UID from the Tags, there is about 10 Tags per second going through the reader. I can not afford that the SerialPort Data Received Event handler sends me partial UID (3 or 4 or 5 bytes and after that the rest)...
2. If I declare serialport in another class, how can I open that port from Main Class?`(I don't quite understand this Private Public Shared Protected.. Classes) All I know is that I am not able to declare serial port in shared class, but if I declare it in another type of class, I can not access it from Main class...
3. What's the best way to communicate with serialport? I need to send string to it ( I do it by sending an Byte Array ) and also to read bytes from it ( I read Byte by Byte ). But as I already mentioned, I need to receive the whole UID or the whole response from the tag. The good thing is that I know exactly at any moment how many bytes I'm expecting to receive (Maybe we can use that somehow - but not using Threshold )
I'll gladly provide my code here if you need it, but I wanted to build it again from Ground Zero, and to build it clean and correct.
Any advice is appreciated. If you have any links or tutorials about SerialPorts or asynchronous threads or something like that you can write it down in comments.
Thanks a lot in advance..
-
Oct 2nd, 2017, 07:21 AM
#2
Re: COM port and threading
This should get you started. Or his other answer (NOTE: there is good stuff further down from that particular post as well!)
-
Oct 2nd, 2017, 10:36 AM
#3
Re: COM port and threading
That's some pretty good code in those links.
The main thing to think about going in is "not getting all the data in one go is just how the COM port works". Or, more specifically, "this is how streaming I/O has always worked". It's not the "easiest" way to approach things, but here's why:
There's no "one true COM port protocol". Every program is free to send data a little differently than the others. Even when some standards are used, it's up to the program what constitutes "data". So since every program is free to do things differently, there's no one "done" message for the SerialPort class to listen for. That means it has to leave it up to you.
The API used by the SerialPort class is very similar to the API used by network APIs and file APIs for decades. It wants you to write "read" code like this:
Code:
Dim buffer(<big number>) As Byte
Dim bytesRead As Integer = 0
Dim bytesRemaining = ???
While Not bytesRemaining = 0
bytesRead += Read(buffer, bytesRead, bytesRemaining)
bytesRemaining -= bytesRead
End While
<do stuff>
This gets a little more complex when threads are involved, but not so much. Most of the time you just restructure your code like this:
Code:
Private _buffer(<big number>) As Byte
Private _bytesRead As Integer = 0
Private _bytesRemaining As Integer
Sub Start()
_bytesRemaining = ???
_bytesRead = 0
StarComPort()
End Sub
Sub WhenDataArrives(...)
bytesRead += _comPort.Read(buffer, _bytesRead, _bytesRemaining)
_bytesRemaining -= bytesRead
If _bytesRemaining = 0
WhenFinished()
End If
End Sub
Sub WhenFinished()
<do stuff>
End Sub
How do you know how to set up _bytesRemaining? It depends on your protocol. Some protocols send packets in fixed sizes. It sounds like you just want 8 bytes at a time, so that's probably you. Other protocols don't have a fixed size, but instead use terminators, or send a length along with each packet. Those involve reading some data, then interpreting what you have and are a bit more complicated to muck with.
I think dbasnett's code is good at introducing the concept, but I'm not sure it's the easiest to follow. Here's a 'lite' version, running with the idea that you want 8 bytes at a time.
First, let's make a "protocol" type to help us interpret odd amounts of bytes. It needs to take an array of bytes and give us back any groups of 8 that we received. If we have non-multiples of 8 like 3 or 11, it needs to hold on to those bytes until it gets more data. This is fidgety, and there's some concepts like circular buffers that can help immensely, but let's stick to the "simplest" idea and use arrays. This is going to be particularly difficult due to your choice of VB 2008, there are a lot of nice features you are missing I might end up accidentally using. Luckily, I'm pretty sure that version has LINQ and that's the biggest feature we need:
Code:
Public Class Protocol
Private _leftovers(-1) As Byte
Public Function InterpretBytes(ByVal input() As Byte) As List(Of Byte())
Dim output As New List(Of Byte())()
Dim fullByteStream As IEnumerable(Of Byte) = _leftovers.Concat(input)
Dim offset As Integer = 0
Dim finished As Boolean = False
Do
' Takes up to 8 bytes out of the full stream, skipping the elements we've already seen.
Dim chunk() As Byte = fullByteStream.Skip(offset).Take(8).ToArray()
If chunk.Length = 8 Then
' If it got all 8 bytes, store them
output.Add(chunk)
Else
' If it got less than 8 bytes, store this chunk as the "leftovers"
_leftovers = chunk
End If
offset += 8
finished = chunk.Length <> 8
Loop Until finished
Return output
End Function
End Class
When you have that, your DataReceived handler can be greatly simplified:
Code:
Private _protocol As New Protocol()
Private Sub SerialPort_DataReceived(...) Handles ...
Dim buffer(_yourPort.BytesToRead - 1) As Byte
_yourPort.Read(buffer, 0, buffer.Length)
Dim results As List(Of Byte()) = _protocol.InterpretBytes(buffer)
For Each result In results
DoSomethingWith(result)
Next
End Sub
Maybe that needs some thread synchronization, maybe not. The only way to find out sometimes is to test.
This answer is wrong. You should be using TableAdapter and Dictionaries instead.
-
Oct 9th, 2017, 12:24 AM
#4
Thread Starter
New Member
Re: COM port and threading
Thanks @topshot, I've already read those threads, but as Sitten Spynne told, for a beginner as I am, they are bit difficult to follow.. I don't quite understand everything.
-
Oct 9th, 2017, 12:25 AM
#5
Thread Starter
New Member
Re: COM port and threading
Thank you very much @Sitten Spynne, great and simple Idea, I will definetly try to use this in my code..
But as I allready mentioned, my problem is lack of "Threading" knowledge. How and more important when to start it, when to pause it, crossthreading, synchronization, reporting and so on...
Last edited by Edoliny; Oct 9th, 2017 at 12:35 AM.
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
|