I have an application that receives data from a wireless radio using RS-232. These radios use an API for communicating with multiple clients. To use the radios I created a library for communicate with them that other software can utilize with minimal changes from a normal SerialPort connection. The library reads from a SerialPort object and inserts incoming data into different buffers depending on the radio it receives from. Each packet that is received contains a header indicating its length, source, etc.
I start by reading the header, which is fixed-length, from the port and parsing it. In the header, the length of the data is defined before the data payload itself, so once I know the length of the data, I then wait for that much data to be available, then read in that many bytes.
Example (the other elements from the header are omitted):
// Read header
byte[] header = new byte[RCV_HEADER_LENGTH];
this.Port.Read(header, 0, RCV_HEADER_LENGTH);
// Get length of data in packet
short dataLength = header[1];
byte[] payload = new byte[dataLength];
// Make sure all the payload of this packet is ready to read
while (this.Port.BytesToRead < dataLength) { }
this.Port.Read(payload, 0, dataLength);
Obviously the empty while port is bad. If for some reason the data never arrives the thread will lock. I haven't encountered this problem yet, but I'm looking for an elegant way to do this. My first thought is to add a short timer that starts just before the while-loop, and sets an abortRead flag when it elapses that would break the while loop, like this:
// Make sure all the payload of this packet is ready to read
abortRead = false;
readTimer.Start();
while (this.Port.BytesToRead < dataLength && !abortRead) {}
This code needs to handle a constant stream of incoming data as quickly as it can, so keeping overhead to a minimum is a concern, and am wondering if I am doing this properly.
You don't have to run this while loop, the method Read would either fill the buffer for you or would throw a TimeoutException if buffer wasn't filled within the SerialPort.ReadTimeout time (which you can adjust to your needs).
But some general remark - your while loop would cause intensive CPU work for nothing, in the few milliseconds it would take the data to arrive you would have thousends of this while loop iterations, you should've add some Thread.Sleep inside.
If you want to truly adress this problem, you need to run the code in the background. There are different options to do that; you can start a thread, you start a Task or you can use async await.
To fully cover all options, the answer would be endless. If you use threads or tasks with the default scheduler and your wait time is expected to be rather short, you can use SpinWait.SpinUntil instead of your while loop. This will perform better than your solution:
SpinWait.SpinUntil(() => this.Port.BytesToRead >= dataLength);
If you are free to use async await, I would recommend this solution, since you need only a few changes to your code. You can use Task.Delay and in the best case you pass a CancellationToken to be able to cancel your operation:
try {
while (this.Port.BytesToRead < dataLength) {
await Task.Delay(100, cancellationToken);
}
}
catch(OperationCancelledException) {
//Cancellation logic
}
I think I would do this asynchronously with the SerialPort DataReceived event.
// Class fields
private const int RCV_HEADER_LENGTH = 8;
private const int MAX_DATA_LENGTH = 255;
private SerialPort Port;
private byte[] PacketBuffer = new byte[RCV_HEADER_LENGTH + MAX_DATA_LENGTH];
private int Readi = 0;
private int DataLength = 0;
// In your constructor
this.Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
if (e.EventType != SerialData.Chars)
{
return;
}
// Read all available bytes.
int len = Port.BytesToRead;
byte[] data = new byte[len];
Port.Read(data, 0, len);
// Go through each byte.
for (int i = 0; i < len; i++)
{
// Add the next byte to the packet buffer.
PacketBuffer[Readi++] = data[i];
// Check if we've received the complete header.
if (Readi == RCV_HEADER_LENGTH)
{
DataLength = PacketBuffer[1];
}
// Check if we've received the complete data.
if (Readi == RCV_HEADER_LENGTH + DataLength)
{
// The packet is complete add it to the appropriate buffer.
Readi = 0;
}
}
}
Related
I am stumped. Searched myself blue in the face - no go.
I am trying to establish serial comms with a device that sends 2 different blocks of data (one after the other) every 1 second continuously. The first block starts with "PID" and the second block ends with "H18".
I only need to read once every 5 seconds.
My problem is two fold:
I have no idea/control when the read starts and often starts mid - block.
I have no control over the start and end cycle to ensure I get a full two blocks as I need both.
Both blocks are about 200 characters long in total, has no /r at the beginning and has /r/n in between various items.
I have tried doing two subsequent reads but no success. Tried playing with StartsWith and EndsWith but they are not recognized? The code has been all over the show, but here is the base I am working from currently:
static void DataReceivedHandlerbat(object sender, SerialDataReceivedEventArgs e)
{
var batm = sender as SerialPort;
if ((batm != null) && (!_gotResponse))
{
while (stringb.Length < 200)
{
byte[] buffer = new byte[batm.BytesToRead];
int numRead = batm.Read(buffer, 0, buffer.Length);
stringb.Append(System.Text.Encoding.ASCII.GetString(buffer));
// if (stringb.S == 0)
//{
// _gotResponse = true;
// break;
//}
}
}
}
and
/// Obtain Battery string
SerialPort batm = new SerialPort();
batm.PortName = "com4";
batm.BaudRate = 19200;
batm.DataBits = 8;
batm.Parity = Parity.None;
batm.StopBits = StopBits.One;
batm.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandlerbat);
batm.Open();
//batm.ReadExisting();
int timeoutMsb;
timeoutMsb = 1000;
var startTimeb = DateTime.Now;
while (!_gotResponse && ((DateTime.Now - startTimeb).TotalMilliseconds < timeoutMsb))
{
Thread.Sleep(20);
}
batm.Close();
_gotResponse = false;
//Build Battery String
String bat = stringb.ToString();
Please help me - I am fairly new to C# and have struggled for 4 days with this?
Is this a GUI application or a background service? Either way, ditch DataReceived event and use ReadAsync, as I showed in my blog post. Then, buffer all incoming data into a List<byte> (this easily deals with messages which arrive split into two), and implement some synchronization logic.
Here's an outline of how synchronization works:
loop through the List<byte>, find the beginning of a message
determine whether the entire message has been received
copy the message payload and fire off an event so the business logic that acts on the message can be separate from the buffering/parsing logic
remove bytes from the list up to the end of the detected message
search the remainder of the buffer for more valid messages (repeat 1-4)
when the buffer List<byte> doesn't contain a complete message, call ReadAsync again
I am trying to read from several serial ports from sensors through microcontrollers. Each serial port will receive more than 2000 measurements (each measurement is 7 bytes, all in hex). And they are firing at the same time. Right now I am polling from 4 serial ports. Also, I translate each measurement into String and append it to a Stringbuilder. When I finish receiving data, they will be ouput in to a file. The problem is the CPU consumption is very high, ranging from 80% to 100%.
I went though some articles and put Thread.Sleep(100) at the end. It reduces CPU time when there is no data coming. I also put Thread.Sleep at the end of each polling when the BytesToRead is smaller than 100. It only helps to a certain extent.
Can someone suggest a solution to poll from serial port and handle data that I get? Maybe appending every time I get something causes the problem?
//I use separate threads for all sensors
private void SensorThread(SerialPort mySerialPort, int bytesPerMeasurement, TextBox textBox, StringBuilder data)
{
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = ""; }));
int bytesRead;
int t;
Byte[] dataIn;
while (mySerialPort.IsOpen)
{
try
{
if (mySerialPort.BytesToRead != 0)
{
//trying to read a fix number of bytes
bytesRead = 0;
t = 0;
dataIn = new Byte[bytesPerMeasurement];
t = mySerialPort.Read(dataIn, 0, bytesPerMeasurement);
bytesRead += t;
while (bytesRead != bytesPerMeasurement)
{
t = mySerialPort.Read(dataIn, bytesRead, bytesPerMeasurement - bytesRead);
bytesRead += t;
}
//convert them into hex string
StringBuilder s = new StringBuilder();
foreach (Byte b in dataIn) { s.Append(b.ToString("X") + ","); }
var line = s.ToString();
var lineString = string.Format("{0} ---- {2}",
line,
mySerialPort.BytesToRead);
data.Append(lineString + "\r\n");//append a measurement to a huge Stringbuilder...Need a solution for this.
////use delegate to change UI thread...
textBox.BeginInvoke(new MethodInvoker(delegate() { textBox.Text = line; }));
if (mySerialPort.BytesToRead <= 100) { Thread.Sleep(100); }
}
else{Thread.Sleep(100);}
}
catch (Exception ex)
{
//MessageBox.Show(ex.ToString());
}
}
}
this is not a good way to do it, it far better to work on the DataReceived event.
basically with serial ports there's a 3 stage process that works well.
Receiving the Data from the serial port
Waiting till you have a relevant chunk of data
Interpreting the data
so something like
class DataCollector
{
private readonly Action<List<byte>> _processMeasurement;
private readonly string _port;
private SerialPort _serialPort;
private const int SizeOfMeasurement = 4;
List<byte> Data = new List<byte>();
public DataCollector(string port, Action<List<byte>> processMeasurement)
{
_processMeasurement = processMeasurement;
_serialPort = new SerialPort(port);
_serialPort.DataReceived +=SerialPortDataReceived;
}
private void SerialPortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
while(_serialPort.BytesToRead > 0)
{
var count = _serialPort.BytesToRead;
var bytes = new byte[count];
_serialPort.Read(bytes, 0, count);
AddBytes(bytes);
}
}
private void AddBytes(byte[] bytes)
{
Data.AddRange(bytes);
while(Data.Count > SizeOfMeasurement)
{
var measurementData = Data.GetRange(0, SizeOfMeasurement);
Data.RemoveRange(0, SizeOfMeasurement);
if (_processMeasurement != null) _processMeasurement(measurementData);
}
}
}
Note: Add Bytes keeps collecting data till you have enough to count as a measurement, or if you get a burst of data, splits it up into seperate measurements.... so you can get 1 byte one time, 2 the next, and 1 more the next, and it will then take that an turn it into a measurement. Most of the time if your micro sends it in a burst, it will come in as one, but sometimes it will get split into 2.
then somewhere you can do
var collector = new DataCollector("COM1", ProcessMeasurement);
and
private void ProcessMeasurement(List<byte> bytes)
{
// this will get called for every measurement, so then
// put stuff into a text box.... or do whatever
}
First of all consider reading Using Stopwatches and Timers in .NET. You can break down any performance issue with this and tell exactly which part of Your code is causing the problem.
Use SerialPort.DataReceived Event to trigger data receiving process.
Separate receiving process and data manipulation process. Store Your data first then process.
Do not edit UI from reading loop.
I guess what you should be doing is adding an event handler to process incoming data:
mySerialPort.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
This eliminates the need to run a separate thread for each serial port you listen to. Also, each DataReceived handler will be called precisely when there is data available and will consume only as much CPU time as is necessary to process the data, then yield to the application/OS.
If that doesn't solve the CPU usage problem, it means you're doing too much processing. But unless you've got some very fast serial ports I can't imagine the code you've got there will pose a problem.
I have a client that will send a lots of data to server from different threads.
The packet uses the following format:
PACKET_ID
CONTENT
END_OF_PACKET_INDICATOR
I have the following onDataRecieved function:
public void OnDataReceived(IAsyncResult asyn)
{
SocketPacket socketData = (SocketPacket)asyn.AsyncState;
int iRx = 0;
iRx = socketData.m_currentSocket.EndReceive(asyn);
char[] chars = new char[iRx + 1];
System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
int charLen = d.GetChars(socketData.dataBuffer, 0, iRx, chars, 0);
MessageBox.Show("Incoming data: " + socketData.dataBuffer.Length.ToString() + " from socket(" + socketData.socket_id + ")");
char[] PACKET_END_IDENTIFIER = { (char)2, (char)1, (char)2, (char)1 };
for (int i = 0; i < iRx; i++)
{
GLOBAL_BUFFER.Add(chars[iRx]);
}
if (PacketEndReached(chars, PACKET_END_IDENTIFIER))
{
// done reading the data
PROCESS_PACKET(GLOBAL_BUFFER);
}
WaitForData(socketData.m_currentSocket, socketData.socket_id);
}
My socket buffer size is set to 100. If I send 1000 bytes, they would be split up in 10 chunks and onDataRecieved would get triggered 10 times.
All I need to do is keep reading the data into buffer for each individual packet sent my client until PacketEndReached gets triggered
then pass the buffer to another function that will process the data.
If I define a GLOBAL_BUFFER for storing incoming data, then if client sends data from multiple threads, wouldn't the data get mixed up? I need a way to read all the data for each individual packet sent my client.
Thanks!
UPDATE:
This is my current class:
public partial class TCP_SERVER
{
const int MAX_CLIENTS = 3000;
const int MAX_SOCKET_BUFFER_SIZE = 10;
public AsyncCallback pfnWorkerCallBack;
private Socket m_mainSocket;
private Socket[] m_workerSocket = new Socket[MAX_CLIENTS];
private int m_clientCount = 0;
public GLOBAL_BUFFER;
public void StartServer(int listen_port)
public void OnClientConnect(IAsyncResult asyn)
public void ProcessIncomingData(char[] INCOMING_DATA, int CLIENT_ID)
public void OnDataReceived(IAsyncResult asyn)
}
As you can see GLOBAL_BUFFER is defined 'globally'. If client sends packet_1 that takes 10 seconds to send and at the same time packet_2 that takes 2 secs to send data would get mixed up. I need to collect data for each packet individually.
If at all possible, I would recommend allowing each client thread to have their own connection to the server. Doing so will help the Winsock stack differentiate the messages from each thread and avoid any bleeding of packets between messages. This will effectively allow you to benefit from the stack's ability to decipher which messages (and message segements) are intended to be grouped together before passing them to your application as a complete message.
The message design you describe while very primitive can only work (reliably) if you separate your threads to different connections (or otherwise provide a guarantee that only a single message will be sent from the client at a time). You employee a very primitive message framing technique to your communication which will aide in your effort to determining message boundries but the reason it is failing is because socketData.m_currentSocket.EndReceive(asyn); will only tell you the number of bytes received when the event is raised (not necessarily the total number of bytes in the message). Rather than relying on it to tell you how many bytes have been read, I'd suggest reading the incoming message incrementally from a loop within your async handler reading very small message segments until it discovers your end of message byte sequence. Doing so will tell your event when to quit reading and to pass the data on to something else to process it.
The way I typically approach message framing is to have a before message eye-catcher (some value that will rarely if ever be seen in the messaging), followed by the length of the message (encoded to taste, I personally use binary encoding for it's efficiency), , the message content, and finally a second eye-catcher at the end of your message. The eye-catchers serve as logical queues for message breaks in the protocol and the message length tells your server explicitly how many bytes to wait for. Doing it this way, you are guaranteed to receive the number of bytes necessary (if you don't it is a problem so discard and/or throw exception) and it provides a very explicit boundary between messages that you can code to, which allows intermittent spot checking and validation.
Simply use Dictionary<String,List<Char>> to replace your current GLOBAL_BUFFER,
store different PACKET_ID data into different List.
I strongly recommend you a perfect Socket Framework SuperSocket, you needn't write any socket code, it will significantly improve your development efficiency.
I am new to Visual C#. I have to receive a packet of 468 bytes every second from a embedded device serially. The header of the packet is 0xbf, 0x13, 0x97, 0x74. After check validating the packet header i am saving this packet , process it, and display it graphically.
The problem is that i start losing packets after few hours. (Other software was logging the same data for the whole week and is working well).
The code is here...
private void DataRec(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
rtTotBytes = comport.BytesToRead;
rtTotBytesRead = comport.Read(rtSerBuff, 0, rtTotBytes);
this.Invoke(new ComportDelegate(ComportDlgtCallback), rtSerBuff, rtTotBytesRead);
}
//Delegate
delegate void ComportDelegate(byte[] sBuff, int sByte);
//Callback Function to Delegate
private void ComportDlgtCallback(byte[] SerBuff, int TotBytes)
{
for (int k = 0; k < TotBytes; k++)
{
switch (rtState)
{
case 0:
if (SerBuff[k] == 0xbf) { rtState = 1; TempBuff[0] = 0xbf; }
else rtState = 0;
break;
case 1:
if (SerBuff[k] == 0x13) { rtState = 2; TempBuff[1] = 0x13; }
else rtState = 0;
break;
case 2:
if (SerBuff[k] == 0x97) { rtState = 3; TempBuff[2] = 0x97; }
else rtState = 0;
break;
case 3:
if (SerBuff[k] == 0x74) { rtState = 4; TempBuff[3] = 0x74; rtCnt = 4; }
else rtState = 0;
break;
case 4:
if (rtCnt == 467)
{
TempBuff[rtCnt] = SerBuff[k];
TempBuff.CopyTo(PlotBuff, 0);
ProcessPacket(PlotBuff);
rtState = 0; rtCnt = 0;
}
else
TempBuff[rtCnt++] = SerBuff[k];
break;
}
}
}
Another question: can the BytesToRead be zero if a DataReceivedEvent had occured? Do you have to check (BytesToRead>0) in DataRecievedEvent?
Serial port input data must be treated as a stream, and not series of packets. For example, when device sends 0xbf, 0x13, 0x97, 0x74 packet, DataRec function may be called once with the whole packet, or twice with 0xbf, 0x13 and 0x97, 0x74 packets, or 4 times with one byte, etc. The program must be flexible enough to handle input stream using some parser. Your current program doesn't do this, it can miss logical packets which are received in a several function calls. Another situation is possible, when several packets are received in one DataRec function call - your program is not ready also for such situation.
Edit.
Typical serial port input stream handling algorithm should look like this:
DataRec function adds received data to input queue and calls parser.
Input queue is some byte array, which contains the data already received, but not parsed yet. New data is added to the end, and parsed packets are removed from the beginning of this queue.
Parser reads the input queue, handles all recognized packets and removes them from the queue, leaving all unrecognized data for the next call.
I think a problem could be that you can't be sure that you receive a full package within the DataReceived event. It is possible that you just got the first half of the packet and half a second later the second half.
So you should implement another layer where you put the data into a buffer. The further proceeding depends on the data format.
If you receive additionally informations like an end mark or the length of the data you could check if the buffer already contains these informations. If yes advance this full package to your routine.
If you don't have this information you have to wait till you receive the next header and forward the data within your buffer till this new header.
Have you checked the memory usage of the program?
Maybe you have a small interop class, memory or something which is not properly freed, adds up after a few hours and make the program run sluggish, causing it to lose data.
I'd use process explorer to check how memory and cpu use change after a few hours. Maybe check for hdd activity, too.
If this does not lead to results, use a full blown profiler like ANTS and try to run the program under the profiler to check for problems.
As Alex Farber points out, there's no guarantee that when your DataReceived handler is invoked, all the bytes are there.
If your buffers are always a fixed size, and at a low rate, you can use the Read function directly, rather than relying on the DataReceived event. Conceptually:
packetSize = 468;
...initialization...
comport.ReadTimeout = 2000; //packets expected every 1000 milliseconds, so give it some slack
while (captureFlag) {
comport.Read(rtSerBuff, 0, packetSize);
...do stuff...
}
This can be put into its own worker thread if you want.
Another approach would be to use the ReadLine method. You mention that the packets have a known starting signature. Do they also have a known ending signature that is guaranteed to not be repeated in the packet? If so, you can set the NewLine property to this ending signature and use ReadLine. Again, you can put this in a worker thread,
I have an ugly piece of Serial Port code which is very unstable.
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
while (port.BytesToRead > 0)
{
var count = port.BytesToRead;
byte[] buffer = new byte[count];
var read = port.Read(buffer, 0, count);
if (DataEncapsulator != null)
buffer = DataEncapsulator.UnWrap(buffer);
var response = dataCollector.Collect(buffer);
if (response != null)
{
this.OnDataReceived(response);
}
Thread.Sleep(100);
}
}
If I remove either Thread.Sleep(100) calls the code stops working.
Of course this really slows things down and if lots of data streams in,
it stops working as well unless I make the sleep even bigger.
(Stops working as in pure deadlock)
Please note the DataEncapsulator and DataCollector are components
provided by MEF, but their performance is quite good.
The class has a Listen() method which starts a background worker to
receive data.
public void Listen(IDataCollector dataCollector)
{
this.dataCollector = dataCollector;
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
}
void worker_DoWork(object sender, DoWorkEventArgs e)
{
port = new SerialPort();
//Event handlers
port.ReceivedBytesThreshold = 15;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
..... remainder of code ...
Suggestions are welcome!
Update:
*Just a quick note about what the IDataCollector classes do.
There is no way to know if all bytes of the data that has been sent
are read in a single read operation. So everytime data is read it is
passed to the DataColllector which returns true when a complete and
valid protocol message has been received. In this case here it just
checks for a sync byte, length , crc and tail byte. The real work
is done later by other classes.
*
Update 2:
I replaced the code now as suggested, but still there is something wrong:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var count = port.BytesToRead;
byte[] buffer = new byte[count];
var read = port.Read(buffer, 0, count);
if (DataEncapsulator != null)
buffer = DataEncapsulator.UnWrap(buffer);
var response = dataCollector.Collect(buffer);
if (response != null)
{
this.OnDataReceived(response);
}
}
You see this works fine with a fast and stable connection.
But OnDataReceived is NOT called every time data is received.
(See the MSDN docs for more). So if the data gets fragmented
and you only read once within the event data gets lost.
And now I remember why I had the loop in the first place, because
it actually does have to read multiple times if the connection is slow or unstable.
Obviously I can't go back to the while loop solution, so what can I do?
My first concern with the original while-based code fragment is the constant allocation of memory for the byte buffer. Putting a "new" statement here specifically going to the .NET memory manager to allocate memory for the buffer, while taking the memory allocated in the last iteration and sending it back into the unused pool for eventual garbage collection. That seems like an awful lot of work to do in a relatively tight loop.
I am curious as to the performance improvement you would gain by creating this buffer at design-time with a reasonable size, say 8K, so you don't have all of this memory allocation and deallocation and fragmentation. Would that help?
private byte[] buffer = new byte[8192];
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
Thread.Sleep(100);
while (port.BytesToRead > 0)
{
var count = port.BytesToRead;
var read = port.Read(buffer, 0, count);
// ... more code
}
}
My other concern with re-allocating this buffer on every iteration of the loop is that the reallocation may be unnecessary if the buffer is already large enough. Consider the following:
Loop Iteration 1: 100 bytes received; allocate buffer of 100 bytes
Loop Iteration 2: 75 bytes received; allocate buffer of 75 bytes
In this scenario, you don't really need to re-allocate the buffer, because the buffer of 100 bytes allocated in Loop Iteration 1 is more than enough to handle the 75 bytes received in Loop Iteration 2. There is no need to destroy the 100 byte buffer and create a 75 byte buffer. (This is moot, of course, if you just statically create the buffer and move it out of the loop altogether.)
On another tangent, I might suggest that the DataReceived loop concern itself only with the reception of the data. I am not sure what those MEF components are doing, but I question if their work has to be done in the data reception loop. Is it possible for the received data to be put on some sort of queue and the MEF components can pick them up there? I am interested in keeping the DataReceived loop as speedy as possible. Perhaps the received data can be put on a queue so that it can go right back to work receiving more data. You can set up another thread, perhaps, to watch for data arriving on the queue and have the MEF components pick up the data from there and do their work from there. That may be more coding, but it may help the data reception loop be as responsive as possible.
And it can be so simple...
Either you use DataReceived handler but without a loop and certainly without Sleep(), read what data is ready and push it somewhere (to a Queue or MemoryStream),
or
Start a Thread (BgWorker) and do a (blocking) serialPort1.Read(...), and again, push or assemble the data you get.
Edit:
From what you posted I would say: drop the eventhandler and just Read the bytes inside Dowork(). That has the benefit you can specify how much data you want, as long as it is (a lot) smaller than the ReadBufferSize.
Edit2, regarding Update2:
You will still be much better of with a while loop inside a BgWorker, not using the event at all. The simple way:
byte[] buffer = new byte[128]; // 128 = (average) size of a record
while(port.IsOpen && ! worker.CancelationPending)
{
int count = port.Read(buffer, 0, 128);
// proccess count bytes
}
Now maybe your records are variable-sized and you don't don't want to wait for the next 126 bytes to come in to complete one. You can tune this by reducing the buffer size or set a ReadTimeOut. To get very fine-grained you could use port.ReadByte(). Since that reads from the ReadBuffer it's not really any slower.
If you want to write the data to a file and the serial port stops every so often this is a simple way to do it. If possible make your buffer large enough to hold all the bytes that you plan to put in a single file. Then write the code in your datareceived event handler as shown below. Then when you get an oportunity write the whole buffer to a file as shown below that. If you must read FROM your buffer while the serial port is reading TO your buffer then try using a buffered stream object to avoid deadlocks and race conditions.
private byte[] buffer = new byte[8192];
var index = 0;
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
index += port.Read(buffer, index, port.BytesToRead);
}
void WriteDataToFile()
{
binaryWriter.Write(buffer, 0, index);
index = 0;
}