I'm implementing a class that talks to a motor controller over a USB device. I have everything working except for a way to indicate whether a parameter fetched over the comm link is "fresh" or not. What I have so far:
class MyCommClass
{
public:
bool getSpeed( double *speed );
private:
void rxThread();
struct MsgBase
{ /* .. */ };
struct Msg1 : public MsgBase
{ /* .. */ };
struct Msg2 : public MsgBase
{ /* .. */ };
/* .. */
struct MsgN : public MsgBase
{ /* .. */ };
Msg1 msg1;
Msg2 msg2;
/* .. */
MsgN msgn;
std::map< unsigned long id, MsgBase *msg > messages;
};
rxThead()
is an infinite loop running in a separate thread checking the USB device for available messages. Each message has a unique identifier which rxThread()
uses to stick it into the right msgx
object. What I need is when the user calls the getSpeed()
function it needs to be able to tell whether the current speed value is "fresh" or "stale" i.e. whether the msgx
object that contains the speed value was updated within a specified timeout period. So each message object needs to implement its own timeout (since they vary per message).
All messages are transmitted periodically by the motor controller, but there are also some that get transmitted as soon as their contents change (but they will also be transmitted periodically if the contents do not change). This means that receiving a message at more than the nominal rate is OK, but it should appear at least once within the maximum timeout period.
The USB device provides timestamps along with the messages so I have access to that information. The timestamp does not reflect the current time, it is an unsigned long
number with microsecond resolution that the device updates every time a message is received. I suspect the device just starts incrementing this from 0
from the time I call its initialization functions. A couple of different ways I can think of implementing this are:
Each message object launches a thread that runs infinitely waiting (WaitForSingleObject) for the timeout period. After the timeout it checks whether a counter variable (that was cached before the wait) has been incremented. If not it sets a flag marking the message as stale. The counter would be incremented every time
rxThread()
updates开发者_开发知识库 that message object.rxThread()
, in addition to stuffing messages, also iterates through the list of messages and checks the timestamp that each was last updated. If the timestamp exceeds the timeout it flags the message as stale. This method might have a problem with the amount of processing required. It probably wouldn't be a problem on most machines but this code needs to run on a piggishly slow 'industrial computer'.
I'd really appreciate your thoughts and suggestions on how to implement this. I'm open to ideas other than the two I've mentioned. I'm using Visual Studio 2005 and cross-platform portability is not a big concern as the USB device drivers are Windows only. There are currently about 8 messages I'm monitoring, but it would be nice if the solution were lightweight enough that I could add several (maybe another 8) more without running into processing horsepower limitations.
Thanks in advance, Ashish.
If you don't need to do something "right away" when a message becomes stale, I think you can skip using timers if you store both the computer's time and the device's timestamp with each message:
#include <ctime>
#include <climits>
class TimeStamps {
public:
std::time_t sys_time() const; // in seconds
unsigned long dev_time() const; // in ms
/* .. */
};
class MyCommClass {
/* .. */
private:
struct MsgBase {
TimeStamps time;
/* .. */
};
TimeStamps most_recent_time;
bool msg_stale(MsgBase const& msg, unsigned long ms_timeout) const {
if (most_recent_time.sys_time() - msg.time.sys_time() > ULONG_MAX/1000)
return true; // device timestamps have wrapped around
// Note the subtraction may "wrap".
return most_recent_time.dev_time() - msg.time.dev_time() >= ms_timeout;
}
/* .. */
};
Of course, TimeStamps can be another nested class in MyCommClass if you prefer.
Finally, rxThread() should set the appropriate message's TimeStamps object and the most_recent_time member each time a message is received. All this won't detect a message as stale if it became stale after the last message of any other type was received, but your second possible solution in the question would have the same issue, so maybe that doesn't matter. If it does matter, something like this could still work, if msg_stale() also compares the current time.
How about storing the timestamp in the message, and having getSpeed()
check the timestamp?
精彩评论