C# – issue/response serial port processing in C#


Okay here's the problem (this is related to a previous post of mine)

I need to be able to have an issue/response system for serial comms that works something like this:

issue: hello
response: world?
issues: no, hello nurse
reponse: well you're no fun.

this would mean I say "hello" the remote unit is expected to send back "world?" within some timeframe, and if it doesn't i should have a way to access that buffer, so here's what i'm thinking, please give me feedback

a ReaderWriterLock'd 'readBuffer'
a Issue Method that will write to the stream
a Response Method that will watch the readBuffer until it contains what i'm expecting or until the timeout expires.

first, how would the stackoverflow community design this class, second, how would they write the datarecieved event? Third how would they make this code more robust so that multiple instances of the class can exist in parallel threads for simultaneous communications?

Best Solution

This is basically a producer-consumer problem, so that should be the basis for the general design.

Here are some thoughts on that:

a) FIFO buffer (Queue)

First of all, you should have an instance of a thread-safe Queue (a FIFO buffer) for each instance of your class. One thread would receive the data and fill it, while the other one would read the data in a thread-safe manner. This only means you would have to use a lock on each enqueue/dequeue operation.

FIFO Queue would enable you to simultaneously process the data in the worker thread, while filling it from the communication thread. If you need to receive lots of data, you could dequeue some data in the worker thread and parse it before all of it has been received. Otherwise you would need to wait until all data has been received to parse it all at once. In most cases, you don't know how much data you are supposed to get, until you start to parse it.

b) Worker thread waiting for data

I would create a worker thread which would wait for a signal that new data has been received. You could use ManualResetEvent.WaitOne(timeOut) to have a timeout in case nothing happens for a while. When the data is received, you would have to parse it, based on your current state -- so that would be an implementation of a state machine.

c) Port abstraction

To handle different types of ports, you could wrap your serial port inside an interface, which could have at least these methods (I might have forgotten something):

 interface IPort
      void Open();
      void Close();
      event EventHandler<DataEventArgs> DataReceived;
      void Write(Data data);

This would help you separate the specific communication code from the state machine.

NOTE: (according to Microsoft) The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer. So you could create your own implementation of IPort which would poll SerialPort in regular intervals to ensure that you don't miss a byte (there is a question of SO which already addresses this).

d) Receiving data

To receive the data, you would have to attach a handler for the IPort.DataReceived event (or SerialPort.DataReceived, if you're not wrapping it), and enqueue the received data to the Queue inside the handler. In that handler you would also set the mentionel ManualResetEvent to notify the worker thread that new data has been received.

Related Question