views:

1413

answers:

2

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?

A: 

I had a similar design issue writing an asynchronous socket listener for formatted data. A translation of what I came up with into the SerialPort / DataReceived model would be something like this:

The main class encapsulates the issue/response system - it will contain the logic for generating the response based on input. Each instance of the class will be bound to a single serial port that can be set during or after construction. There will be a StartCommunications type method - it will wire up the DataReceived event to another method in the class. This method is responsible for grabbing the data from the port, and determining if a full message has arrived. If so, it raises its own event (defined on the class), which will have an appropriate method wired to it. You could also have it call a predefined method on your class instead of raising an event - I defined an event to improve flexibility.

That basic design is working just fine in a production system, and can handle more input than the rest of the systems connected to it can.

Harper Shelby
+3  A: 

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.

Groo
This is the kind of feedback I wanted, tho i'm not certain why the fifo buffer is needed i definately understand it's use. In my experiements i'm using a stringbuilder.
Firoso
I just edited the post regarding FIFO buffer.
Groo
For example, if you needed to parse [length][item(1)][item(2)]...[item(n)], where [length] is the number of items, then you could dequeue the length information as soon as you receive it, and prepare and allocate your data for the items, while the queue is being filled.
Groo