Unsolved Receiving LCM messages via signals/slots in C++ with Qt?
-
In my application, I am receiving messages from LCM (Lightweight Communications and Marshalling) that contain data for multiple consumers within the application. I imagined this working with the LCM handler as a singleton so that there is one instance that each class could use. For example, each consumer class would have:
QObject::connect(LCMHandler::getInstance(), SIGNAL(messageReceived()), this, SLOT(processMessage()));
Where
lcmhandler.h
is:class LCMHandler : public QObject { Q_OBJECT public: static LCMHandler* getInstance(); LCMHandler(); ~LCMHandler() {} void handleMessage(const lcm::ReceiveBuffer* rbuf, const std::string &chan, const example::example_t *msg); signals: void messageReceived(); private: static LCMReceiver* _instance; };
And
lcmhandler.cpp
is:LCMHandler* LCMHandler::_instance = 0; LCMHandler::LCMHandler() { lcm::LCM lcm; if(lcm.good()) { lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this); while(0 == lcm.handle()); } else { std::cerr << "LCM Error" << std::endl; } } LCMHandler* LCMHandler::getInstance() { if (!_instance) { _instance = new LCMHandler(); } return _instance; } void LCMHandler::handleMessage(const lcm::ReceiveBuffer *rbuf, const std::string &chan, const hlelcm::transponder_t *msg) { std::cout << "Received message on channel " << chan.c_str() << std::endl; emit messageReceived(); }
The application successfully prints "Received message on channel..." repeatedly; however, nothing else is executed, including code in the consumer class's
processMessage()
, presumably because the application gets stuck looping onhandleMessage(...)
and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation ofprocessMessage()
is:void Consumer::processMessage() { std::cout << "Message received" << std::endl; }
It never executes, while
handleMessage(...)
loops infinitely. Similarly, the Qt UI never loads because handleMessage is busy looping.What is the best way to handle the incoming messages? Should I refrain from using a singleton for
LCMHandler
? What do I need to change to make this implementation work? -
@marmoc said in Receiving LCM messages via signals/slots in C++ with Qt?:
Should I refrain from using a singleton for LCMHandler?
If you ask @kshegunov yes
the solution you do not want to implement is is calling
QApplication::processEvents()
inside your loop. What you really should do is makehandleMessage
work asynchronously instead of being an infinite loop. Not knowing what's inside it is difficult to be more specific -
@VRonin said in Receiving LCM messages via signals/slots in C++ with Qt?:
If you ask @kshegunov yes
Well, I'm not that fanatical ... okay, maybe a little bit, but I think we all would agree the singletonian approach promotes tight coupling.
@marmoc said in Receiving LCM messages via signals/slots in C++ with Qt?:
You shouldn't block the event loop, much less in the constructor. So I suggest something like this:class LCMHandler : public QObject { Q_OBJECT Q_DISABLE_COPY(LCMHandler) public: LCMHandler() { Q_ASSERT(!instance); instance = this; if(lcm.good()) lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this); else std::cerr << "LCM Error" << std::endl; // You can set also a flag for the class so you know at a later stage that the initialization failed. // Schedule the message polling through the event loop QMetaObject::invokeMethod(this, "pollMessage", Qt::QueuedConnection); } static LCMHandler * getInstance() { Q_ASSERT(instance); // We want to catch any (possible) nullptr dereferencing at runtime. This is usually an overkill though. return instance; } // ... // Implementation of handle message and other goodies ... private: Q_INVOKABLE void pollMessage() { // Check if there's a message if (lcm.handle()) { // Pull the data and emit the signal handleMessage( ... ); } QMetaObject::invokeMethod(this, "pollMessage", Qt::QueuedConnection); // Loop once more through the event loop } signals: void messageReceived(); private: lcm::LCM lcm; //< This has no place as a local in the constructor static LCMHandler * instance; }; LCMHandler * LCMHandler::instance = NULL; // This relies on the fact that trivial initialization are always done first
Then you can create the object in
main()
, as usual, on the stack.int main(int argc, char ** argv) { QApplication app(argc, argv); LCMHandler lcmHander; Consumer consumer; QObject::connect(&lcmHandler, &LCMHandler::messageReceived, &consumer, &Consumer::processMessage); return QApplication::exec(); }
-
@kshegunov's solution with
lcm::LCM::subscribe
I think he just forgot the void inQ_INVOKABLE pollMessage()
To give context to the people lost like me, they are using this library https://lcm-proj.github.io/group__LcmCpp.html
-
@VRonin said in Receiving LCM messages via signals/slots in C++ with Qt?:
I think he just forgot the void in
Yes, I did indeed.
To give context to the people lost like me, they are using this library https://lcm-proj.github.io/group__LcmCpp.html
Thanks, I just copied the OP's code blindly. :)
Seeing how the library is designed, I'd say one rather has to use something along the lines of:Q_INVOKABLE void pollMessage() { // Check if there's a message if (!lcm.handle()) ; // Just report the error or do nothing QMetaObject::invokeMethod(this, "pollMessage", Qt::QueuedConnection); // Loop once more through the event loop }
-
Looking at the documentation of the library looks like you are over-complicating it, you are using
while(0 == lcm.handle());
only to preventlcm
(awful thing calling a variable with the same name as a namespace btw) getting out of scope as the callback is managed already by the subscription -
@kshegunov @VRonin Thanks for the replies and the great suggestions! This helped a lot!