Use QIODevice Asynchronous Interface with Synchronous API
I am communicating with a device in Qt using a QIODevice (specifically a QSerialPort). Normally it is easy to use the asynchronous signals/slots mechanism of QIODevice to process replies from the device and perform actions. However, the particular device I am controlling has an API which is not suited to asynchronous communication (I have no way to determine which replies correspond to which command other than the order they occur in). That being said, I need to send a command to the device then wait for a reply to that immediate command before processing more commands.
I can think of two was to go about doing this in Qt. The first method is to have a sendCommand() slot in my object which queues the command in an outgoing queue. A timer (or some other mechanism) then pulls these commands one at a time from the queue, sends the command to the device, then puts the command into an incoming queue. I then use the readyRead() signal from the QIODevice to get responses from the device. I associate a given response to a given command by the order in which it is received and the next command in the incoming queue. I will assume that the response corresponds to the next item in the queue. This method seems a little bad because I have no real way of determining timeout from a particular command or other error conditions.
My second idea is to create a new thread which then uses the synchonous (waitForReadyRead) API of the QIODevice to communicate. I would have a sendCommand() slot and commandResponse() signal (that also sends back a pointer or some method to identify the originally send command). The sendCommand() method would add commands to a queue which would then be processed in a loop by the thread. The thread would read the command from the queue, send the data to the QIODevice, block until a reply is receive, process the reply, then emit a signal. I am not entirely sure how to create a thread with the ability to use signals and slots however.
What I would like to know is if there is one method that would be better and if there is a better method I have not considered.
Do you really need a queue of command ? How often are you going to send them ?
I will probably only send commands every few hundred milliseconds at most. However, the device sometimes can take up to a few seconds to respond to certain commands which means I need some was to buffer commands sent to the device within my program.
hand-shake based communication can perfectly be do asynchronous. you just need a send buffer (a QQueue, e.g.) once you got an answer, you parse it and send the next command from the queue. a QTimer restarts the communication when an answer does not come in (completely).
I've programmed different apps that way, both ASCII or binary protocols. You can use the same approach for sockets, too.
Ok. Would a good method in Qt be to do the following.
- I add a command to the send queue using a slot
- If there is only one item in the queue, I send this command to the device
- When the readyRead() slot is called by the QIODevice, I attempt to parse the command from the device
- Once a command is successfully received, I emit a signal and pull the next command from the queue
The only problem I can see is how to correlate different replies to different commands. For example, my device has a series of GET commands which all return data with exactly the same format (only the values differ) so I have no way of knowing which type of GET reply it is. I need to somehow have a good method of keeping track of which signal to emit when I process the command based upon which command was just sent.
how to correlate different replies to different commands
If you only send one command and wait until you get the reply before sending next command then you always know to which command the current reply belongs. Or are you going to send several commands at the same time?
Yes, you are correct that I would only send a single command thus I would know which one I am receiving a reply for. I suppose I am just confused as to the exact/proper way to go about this in the signals/slots world where my reply comes in a different function. Perhaps I send the command, place it into some sort of buffer, then when the reply slot is called, I look into that buffer to see what command caused the reply and act accordingly. I could also use a QTimer to implement some sort of timeout which discards the command buffer and emits an error/timeout signal.