简体   繁体   中英

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 on handleMessage(...) and never executes the signal/slot procedure (or refreshes the UI components). So, if the implementation of processMessage() 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?

Move the contents of your LCM constructor to another function:

LCMHandler::beginCommunication()
{
    lcm::LCM lcm;
    if(lcm.good())
    {
        //QObject base class call.
        moveToThread( &_myLocalQThread );

        _myLocalThread.start();

        lcm.subscribe("MyChannel", &LCMHandler::handleMessage, this);

        _isActive = true;

        // This is blocking, we don't want it to interfere with
        // the QApplication loop
        while(0 == lcm.handle());
    } 
    else 
    {
        std::cerr << "LCM Error" << std::endl;
    }

    _isActive = false;
}

Then something along these lines to allow your LCM loop to happen in another thread.

auto lcmHandler = LCMHandler::getInstance();

// I like to be explicit about the Qt::QueuedConnection. Default behavior should be thread safe, though.
connect( lcmHandler, &LCMHandler::messageReceived,
         this, &Consumer::processMessage, Qt::QueuedConnection );

// Add a std::atomic< bool > _isActive to LCMHandler 
if( not lcmHandler.isActive() )
{
    lcmHandler.beginCommunication();
}

And then make sure to properly close your QThread in the destructor.

LCMHandler::~LCMHandler()
{
    _myLocalQThread.quit();

    _myLocalQThread.wait();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM