I’ve been asked to write a method that will allow a caller to send a command string to a hardware device via the serial port. After sending the command the method must wait for a response from the device, which it then returns to the caller.
To complicate things the hardware device periodically sends unsolicited packets of data to the PC (data that the app must store for reporting). So when I send a serial command, I may receive one or more data packets before receiving the command response.
Other considerations: there may be multiple clients sending serial commands potentially at the same time as this method will form the basis of a WCF service. Also, the method needs to be synchronous (for reasons I won’t go into here), so that rules out using a callback to return the response to the client.
Regarding the “multiple clients”, I was planning to use a BlockingCollection<> to queue the incoming commands, with a background thread that executes the tasks one at a time, thus avoiding serial port contention.
However I’m not sure how to deal with the incoming serial data. My initial thoughts were to have another background thread that continually reads the serial port, storing data analysis packets, but also looking for command responses. When one is received the thread would somehow return the response data to the method that originally sent the serial command (which has been waiting ever since doing so – remember I have a stipulation that the method is synchronous).
It’s this last bit I’m unsure of – how can I get my method to wait until the background thread has received the command’s response? And how can I pass the response from the background thread to my waiting method, so it can return it to the caller? I’m new to threading so am I going about this the wrong way?
Thanks in advance
Andy
First of all: When you use the
SerialPortclass that comes with the framework, the data received event is asynchronous already. When you send something, data is coming in asynchronously.What I’d try is: queue all requests that need to wait for an answer. In the overall receive handler, check whether the incoming data is the answer for one of the requests. If so, store the reply along with the request information (create some kind of state class for that). All other incoming data is handled normally.
So, how to make the requests wait for an answer? The call that is to send the command and return the reply would create the state object, queue it and also monitor the object to see whether an answer was received. If an answer was received, the call returns the result.
A possible outline could be:
What’s
SerialPortHandler? I’d make this a singleton class which contains anInstanceproperty to access the singleton instance. This class does all the serial port stuff. It should also contain an event that is raised when “out of band” information comes in (data that is not a reply to a command).It also contains the
SendRequestmethod which sends the command to the serial device, stores the state object in an internal list, waits for the command’s reply to come in and updates the state object with the reply.The state object contains a wait handle called
ReplyReceivedwhich is set by theSerialPortHandlerafter it has changed the state object’sReplyproperty. That way you don’t need a loop andThread.Sleep. Also, instead of callingWaitOne()you could callWaitOne(timeout)withtimeoutbeing a number of milliseconds to wait for the reply to come in. This way you could implement some kind of timeout-feature.This is how it could look in
SerialPortHandler:Please note: This is what I’d try to start with. I’m sure this can be very much optimized, but as you see there’s not much “multithreading” involved where – only the
SendAndWaitmethod should be called in a way so that multiple clients can issue commands while another client is still waiting for its response.EDIT
Another note: You’re saying that the method should form the basis for a WCF service. This makes things easier, as if you configure the service right, a instance of the service class will be created for every call to the service, so the
SendAndWaitmethod would “live” in its own instance of the service and doesn’t even need to be re-entrant at all. In that case, you just need to make sure that theSerialPortHandleris always active (=> is created and running independently from the actual WCF service), no matter whether there’s currently an instance of your service class at all.EDIT 2
I changed my sample code to not loop and sleep as suggested in the comments.