Warning: do NOT use SerialPort class!!!

G

Guest

The SerialPort class in .NET 2.o is buggy, has misleading and incomplete
documentation and should by no means used for LOB / mission critical /
factory automation projects!

For example: mixing ReadByte() and Read(byte_buffer,offset,length) creates
phantom data!

Microsoft: scrap it! Redesign and rewrite a serialport class that
transparently reads and write bytes!!! Sometime s... happens!

herbert
 
C

Christopher Reed

Please elaborate. Personally, I wouldn't use the ReadByte and Read methods
together. I would, considering the datastream that would be read, determine
which would be appropriate for the task at hand.

So, why do you believe that there is something wrong with the SerialPort
class and not your own code and logic. Please show code.
 
G

Guest

Ok, below is the complete code:
look at the comment "phantom data".

please teach me the difference between

myPort.Read(readBuffer, 0, 5) 'this creates phantom data

and

For i As Integer = 0 To readBuffer.Length - 1
readBuffer(i) = CType(myPort.ReadByte, Byte)
Next

thanks herbert
------------
Imports System.IO.Ports
Imports System.Threading
Imports System.IO
Imports System.Text

Module Dauertest
Const PortName As String = "COM4" 'Com4 is USB/RS-232 Adapter
Dim myPort As SerialPort

Sub Main()

'get the list of all ports known on this computer
'if parameter port is not included then throw exception
If Not My.Computer.Ports.SerialPortNames.Contains(PortName.ToUpper)
Then
'you must connect the USB Adapter!
Throw New Exception("Port " & PortName & " does not exist.")
End If

myPort = New SerialPort(PortName.ToUpper) 'convert
portname to upper case before accessing it
myPort.BaudRate = 9600
myPort.DataBits = 8
myPort.Parity = IO.Ports.Parity.None
myPort.StopBits = IO.Ports.StopBits.One
myPort.Handshake = Handshake.None
myPort.DtrEnable = True
myPort.Encoding = System.Text.Encoding.UTF8 'support 8 bits
per byte transparent!

'open the port
Try
AddHandler myPort.ErrorReceived, AddressOf ErrRcvd
myPort.Open() 'open the port
Catch ex As UnauthorizedAccessException
Console.WriteLine("Cannot access port " & PortName & ". Maybe in
use by other application.")
Console.WriteLine("Hit ENTER to exit.")
Console.ReadLine()
Exit Sub
End Try
myPort.DiscardOutBuffer()
myPort.DiscardInBuffer()

Try
Console.WriteLine("Send x88.")
Dim wb(0) As Byte
wb(0) = &H88
myPort.Write(wb, 0, 1)

Dim myByte As Byte 'buffer to read a single byte
Dim readBuffer(4) As Byte '5 byte buffer for rest of TP message

While True 'read all bytes from line and output
them in hex to console
myByte = CType(myPort.ReadByte, Byte) 'readbyte returns
integer!!!
Console.Write("." & String.Format("{0:X2} ", myByte))
Select Case myByte
Case &H70 '
myByte = CType(myPort.ReadByte, Byte) 'readbyte
returns integer!!!
If myByte <> 1 Then Console.WriteLine("invalid
reader nr. or out of synch")

Case &H80 '
myByte = CType(myPort.ReadByte, Byte) 'readbyte
returns integer!!!

If myByte <> 1 Then Console.WriteLine("invalid
reader nr. or out of synch")

Case &H1 'Lesegerät Nr. 1
'myPort.Read(readBuffer, 0, 5) 'this creates
phantom data!!! ********
For i As Integer = 0 To readBuffer.Length - 1
'this works fine
readBuffer(i) = CType(myPort.ReadByte, Byte)
Next
Console.Write("Reader 1: ")
For i As Integer = 0 To readBuffer.Length - 1
Console.Write(String.Format("{0:X2}",
readBuffer(i)) & ".")
Next
Console.WriteLine()
Case &H2 'Lesegerät Nr. 2
myPort.Read(readBuffer, 0, 5)
Console.Write("Reader 2: ")
For i As Integer = 0 To readBuffer.Length - 1
Console.Write(String.Format("{0:X2}",
readBuffer(i)) & ".")
Next
Console.WriteLine()
Case Else
Console.WriteLine("************************* Invalid
start byte. ************************* ")
End Select

End While
Thread.Sleep(Threading.Timeout.Infinite)
Catch ex As Exception
Console.WriteLine("Error: " & ex.Message)
Finally
myPort.Dispose()
End Try
Console.WriteLine("Hit enter to exit.")
Console.ReadLine()
End Sub

Private Sub ErrRcvd(ByVal sender As Object, ByVal e As
System.IO.Ports.SerialErrorReceivedEventArgs)
Console.WriteLine("Error received")

End Sub

End Module

herbert
 
G

Guest

Hi Herbert,
Just having a quick look at your problem. From what I've read on the MSDN
site the functions are operating correctly, you've just made an incorrect
assumption.

From the documentation ReadByte reads a single byte synchronosly from the
input buffer... Fair Enough....

However, the read method reads the AVAILABLE data to the array, however not
all bytes may have been moved to the buffer by the time you make the call.

Read returns the number of bytes actually READ from the input buffer and you
should use this to determine the actual number of bytes that are valid in the
output array.

BTW this is all standard stuff when dealing with streams of buffered data

Regards,
Myles.
 
G

Guest

Thanks,

"available data" ... available where?
"moved to the buffer" ... which buffer?

imho it is standard stream behaviour to receive messages fragmented. It is
NOT standard stream behaviour to create phantom data.
And when the documentation talks about input buffer: is this the UART
buffer, the serial port driver buffer, the SerialPort class' buffer...?

I'm doing serial comm for more than 40 years. This is the first time I run
into phantom data. Why not simply have methods like
ReadSingleByteTransparently()
ReadArrayOfBytesTransparently()
ReadSingleCharacter()
ReadArrayOfCharacters()
and the like for Write
?

and then have a stream on top of it for those who like to chain composable
streams.
regards
herbert
 
G

Guest

I can't comment on the actual implementation of the .net framework (because I
don't know) and as already stated this appears to be the problem from reading
the documentation... I have NOT attempted to recreate your problem.

However normally when talking about a buffer in high level abstracted
languages like .net the implication is that it is a software buffer belonging
to the class not the UART buffer (after all how many UARTS on the market
support 4096 byte buffers.. what's the norm 16 to 128 bytes?).

From the point of view of the phantom data can I assume from your response
that even with checking the length of the data read that the problem still
exists?

Regards,
Myles.
 
G

Guest

If ReadByte()is low level access and Read(byte_array) is high-level stream,
than I cannot explain why ReadByte() following Read() returns bytes already
returned by Read().

regards
herbert
 
G

Guest

because it matters: full-duplex operation

is the SerialPort class Thread-safe in that Thread Nr.1 can execute any kind
of Write() method whereas at the same time Thread Nr. 2 executes any kind of
Read() method ?

thanks
herbert
 
J

Jon Skeet [C# MVP]

herbert said:
The SerialPort class in .NET 2.o is buggy, has misleading and incomplete
documentation and should by no means used for LOB / mission critical /
factory automation projects!

For example: mixing ReadByte() and Read(byte_buffer,offset,length) creates
phantom data!

You haven't explained what *exactly* you mean by "phantom data". What
are you expecting to see, and what are you seeing?

What *is* clear, as has been said before, is that you're not looking at
the return value of Read, and you should be.

(I'm also somewhat concerned by the code:

myPort.Encoding = System.Text.Encoding.UTF8 'support 8 bits
per byte transparent!

but that's a different matter.)
 
G

Guest

Jon,

1) let me first thank you for your excellent article about thread
synchronization
http://www.yoda.arachsys.com/csharp/threads
a must read! It is stuff like this that drives .NET foreward into production
environments.

2) you are concerned about
myPort.Encoding = System.Text.Encoding.UTF8 'support 8 bits
per byte transparent!

well the problem with SerialPort class is that it is not intended to handle
raw bytes - or it is not documented at all.
*** the line above is the official answer by Microsoft given on their
feedback pages for VS.2005 as the only way to read bytes transparently!!! ***

another hint from the german newsgroups was:
use
Encoding.GetEncoding(28591) which is equivalent to
Encoding.GetEncoding(„ISO-8859-1“)

to read bytes transparently.
my comment: it's a mess!

3) you want to know what "phantom data" means.
my app reads a single byte, which is the message ID, then upon this decides
how many more bytes to read because of the message length being constant
depending on the message ID.
Therefore the remaining message was originally read in using Read(bytearray,
offset, length) , which failed then converted to a FOR NEXT loop using
ReadByte.

as it turned out, Read(bytearray, offset, length) reads the data correctly.
In my case this is 5 bytes.

yet afterwards, the next ReadByte() returns one or more bytes from the end
of the previous message, which the app already received.
It's like read(bytearraym,offset, length) does not remove the data from the
input-buffer (whatever and wherever it is).

The funny thing is: the number of phantom bytes varies. It happens however
very often.
I can send you a screenshot showing both my application's output as well as
the output of a RS.232 tracer.

Note: I have this problem both with physical COM ports as well as virtual
COM ports (USB adapter). And I only run at 9600 baud, as a higher speed is
not recommended by USB adapter manufacturers. Why by the way?

thanks
herbert
 
G

Guest

Warning. Do not use the SerialPort class if you don't know how to. ;)

The Read method returns the number of bytes that was read. If you ignore
the return value, you don't know how many of the bytes in the array that
actually is data.

If you treat the entire contents of the array as data when it isn't, you
will of course get what you call "phantom data".

The error is not in the SerialPort class, but in how you use it.
 
G

Guest

Göran,

the MSDN documentation does not specify this explicitely:
http://msdn2.microsoft.com/en-us/library/ms143549.aspx

The 3rd parameter count is "number of bytes to read".
It does not say "maximum length of buffer".
This is how I interpret the documentation.

In case of a transmission error I would look at the actual number of bytes
received.

OK, Microsoft can always document a bug and declare it a feature.

for me it doesn't make a difference if the code is buggy or the
documentation is misleading or the samples are missing. I waste my time and
money on SerialPort.

thanks
herbert
 
G

Guest

Steven

I admit I made wrong assumptions about the missing samples and documentation
of the SerialPort class.

best regards
herbert
 
R

r norman

Göran,

the MSDN documentation does not specify this explicitely:
http://msdn2.microsoft.com/en-us/library/ms143549.aspx

The 3rd parameter count is "number of bytes to read".
It does not say "maximum length of buffer".
This is how I interpret the documentation.

In case of a transmission error I would look at the actual number of bytes
received.

OK, Microsoft can always document a bug and declare it a feature.

for me it doesn't make a difference if the code is buggy or the
documentation is misleading or the samples are missing. I waste my time and
money on SerialPort.

"Number of bytes to read" is the maximum number. If fewer are
available than you don't get that number! If you fail to use the
return value with the actual number of bytes read, then that is your
problem.

And if this is the first time that you have wasted time and money
chasing a wrong assumption about how things actually work, then you
have not been programming for very long!
 
J

Jon Skeet [C# MVP]

herbert said:
1) let me first thank you for your excellent article about thread
synchronization
http://www.yoda.arachsys.com/csharp/threads
a must read! It is stuff like this that drives .NET foreward into production
environments.

Thanks very much.
2) you are concerned about

well the problem with SerialPort class is that it is not intended to handle
raw bytes - or it is not documented at all.
*** the line above is the official answer by Microsoft given on their
feedback pages for VS.2005 as the only way to read bytes transparently!!! ***

That sounds very unlikely, to be honest. Could you direct me to the
page in question? It would be interesting to try to clear that up.
another hint from the german newsgroups was:
use
Encoding.GetEncoding(28591) which is equivalent to
Encoding.GetEncoding(?ISO-8859-1?)

to read bytes transparently.
my comment: it's a mess!

If that's truly the case, it is indeed a mess. I find it hard to
believe though - it would be a very strange way of coding it to start
with.
3) you want to know what "phantom data" means.
my app reads a single byte, which is the message ID, then upon this decides
how many more bytes to read because of the message length being constant
depending on the message ID.
Therefore the remaining message was originally read in using Read(bytearray,
offset, length) , which failed then converted to a FOR NEXT loop using
ReadByte.

as it turned out, Read(bytearray, offset, length) reads the data correctly.
In my case this is 5 bytes.

yet afterwards, the next ReadByte() returns one or more bytes from the end
of the previous message, which the app already received.
It's like read(bytearraym,offset, length) does not remove the data from the
input-buffer (whatever and wherever it is).

The funny thing is: the number of phantom bytes varies. It happens however
very often.
I can send you a screenshot showing both my application's output as well as
the output of a RS.232 tracer.

That would be very useful. It's unfortunate that a problem like this
can't be easily reproduced without the device in question, but the
screenshots sound like they'd give some clues.
 
G

Guest

herbert said:
Jon,

1) let me first thank you for your excellent article about thread
synchronization
http://www.yoda.arachsys.com/csharp/threads
a must read! It is stuff like this that drives .NET foreward into production
environments.

2) you are concerned about

well the problem with SerialPort class is that it is not intended to handle
raw bytes - or it is not documented at all.
*** the line above is the official answer by Microsoft given on their
feedback pages for VS.2005 as the only way to read bytes transparently!!! ***

The encoding is only used if you read characters, not if you read bytes.
another hint from the german newsgroups was:
use
Encoding.GetEncoding(28591) which is equivalent to
Encoding.GetEncoding(„ISO-8859-1“)

to read bytes transparently.
my comment: it's a mess!

Yes, if you try to read bytes by reading characters, it gets messy.
3) you want to know what "phantom data" means.
my app reads a single byte, which is the message ID, then upon this decides
how many more bytes to read because of the message length being constant
depending on the message ID.
Therefore the remaining message was originally read in using Read(bytearray,
offset, length) , which failed then converted to a FOR NEXT loop using
ReadByte.

as it turned out, Read(bytearray, offset, length) reads the data correctly.
In my case this is 5 bytes.

yet afterwards, the next ReadByte() returns one or more bytes from the end
of the previous message, which the app already received.
It's like read(bytearraym,offset, length) does not remove the data from the
input-buffer (whatever and wherever it is).

It sounds like the call to the Read method didn't in fact read five
bytes at all. The values that happened to be in the part of the array
that was not used just happened to look like they were ok.

You have to get the return value from the call to know how many bytes
that was actually read.
 
M

me

If you believe SerialPort is any good, tr

SerialPort serialPort = new SerialPort()
serialPort.StopBits = StopBits.Zero

It throws an exception ;-)

C#/.NET is typical Microsoft rubbish... I only use it for my job, never for my own code
 
?

=?ISO-8859-1?Q?G=F6ran_Andersson?=

No, it doesn't throw an exception. It won't even compile.

The StopBits enumeration doesn't have any value named "Zero".
 
Top