7

QQueue を介してシリアル ポートにデータを書き込み、スロットから読み取ることができるクラスを実装しました。これには QAsyncSerial を使用します。これは、boost::asio とコールバックを使用します。クラスはスレッドに移動され、QThread が「started()」を発行すると、その start() メソッドが実行されます。

問題は、forever {} と QWaitCondition を使用して start() メソッドで QQueue をデキューすることです。これが実行されている間 (明らかに永久に実行されます)、QAsyncSerial の dataReceived 信号に接続されたスロットを呼び出すことができないため、シリアル ポートから何も読み取れません。

この問題に対する通常のアプローチは何ですか?

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QObject(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    m_waitCondition = new QWaitCondition();
    serial.open(serialPort.deviceName(), 2400);
    connect(&serial, SIGNAL(dataReceived(QByteArray)), this, SLOT(serialSlotReceivedData(QByteArray)));
}

void SerialPortHandler::serialSlotReceivedData(QByteArray line)
{
    qDebug() << QString(line).toAscii();
}

void SerialPortHandler::sendTestPing()
{
    PingMessage *msg = new PingMessage();
    enqueueMessage(msg);
}

void SerialPortHandler::enqueueMessage(BaseMessage *msg)
{
    QMutexLocker locker(m_enqueueMessageMutex);
    m_messageQueue->enqueue(msg);
    m_waitCondition->wakeAll();
}

void SerialPortHandler::start()
{
    if (!serial.isOpen())
        return;

    forever {
        m_enqueueMessageMutex->lock();
        if (m_messageQueue->isEmpty())
            m_waitCondition->wait(m_enqueueMessageMutex);
        BaseMessage *msg = m_messageQueue->dequeue();
        serial.write(msg->encodeForWriting());
        m_enqueueMessageMutex->unlock();
    }
}

boost::asio で使用される変更された QAsyncSerial コールバック:

void QAsyncSerial::readCallback(const char *data, size_t size)
{
    emit dataReceived(QByteArray::fromRawData(data, (int) size));
}

編集:

この問題を別のアプローチで解決しました。私は QAsyncSerial を捨て、代わりに QAsyncSerial によって直接配布されている CallbackAsyncSerial を使用しました。現在、boost::asio が使用するコールバックは serialSlotReceivedData の「スロット」です。これにより、boost :: asio が実行されるスレッドでコールバックが呼び出されるため、問題が「解決」されます。独自のスレッドがあるため、SerialPortHandler が実行されるスレッドが永久ループによってブロックされても問題ありません。

新しいコード: (QAsyncSerial は CallbackAsyncSerial のラッパーのようなものであるため、変更されたのは些細なことだけです)

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QObject(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    m_waitCondition = new QWaitCondition();
    /* serial is now CallbackAsyncSerial and not QAsyncSerial */
    serial.open(QString(serialPort.deviceName()).toStdString(), 2400);
    serial.setCallback(bind(&SerialPortHandler::serialSlotReceivedData, this, _1, _2));

    m_messageProcessingState = MessageProcessingState::Inactive;
}

void SerialPortHandler::start()
{
    if (!serial.isOpen())
        return;

    forever {
        m_enqueueMessageMutex->lock();

        if (m_messageQueue->isEmpty())
            m_waitCondition->wait(m_enqueueMessageMutex);

        BaseMessage *msg = m_messageQueue->dequeue();
        QByteArray encodedMessage = msg->encodeForWriting();
        serial.write(encodedMessage.constData(), encodedMessage.length());

        m_enqueueMessageMutex->unlock();
    }
}
4

3 に答える 3

3

1) ジョブを実行する onMessageReady() などのスレッドにスロットを作成します。

2) 新しいメッセージの準備ができていることを示すシグナルを作成し、新しいメッセージを作成するたびに送信します。

3) QueuedConnection を使用してそれらを接続し、スレッドの exec 関数を呼び出します。

これは、WaitforObject のようにスレッドをブロックせず、すべての着信シグナルを処理します。

このようなもの:

SerialPortHandler: public QThread
{
  Q_OBJECT
...
signals:
    void sNewMessageReady();
slots:
    void onNewMessageReady();
    void serialSlotReceivedData(QByteArray);
};

SerialPortHandler::SerialPortHandler(SerialPort serialPort, QObject *parent) : QThread(parent), serialPort(serialPort)
{
    m_enqueueMessageMutex = new QMutex();
    m_messageQueue = new QQueue<BaseMessage*>();
    serial.open(serialPort.deviceName(), 2400);
    connect(&serial, SIGNAL(dataReceived(QByteArray)), this, SLOT(serialSlotReceivedData(QByteArray)));
    connect(this, SIGNAL(sNewMessageReady()), this, SLOT(onNewMessageReady()),Qt::QueuedConnection);
}

void SerialPortHandler::enqueueMessage(BaseMessage *msg)
{
    QMutexLocker locker(m_enqueueMessageMutex);
    m_messageQueue->enqueue(msg);
    emit sNewMessageReady();
}


void SerialPortHandler::onNewMessageReady()
{
    QMutexLocker locker(m_enqueueMessageMutex);
    BaseMessage *msg = m_messageQueue->dequeue();
    serial.write(msg->encodeForWriting());
}

結局、単にスレッドの exec() メソッドを呼び出すだけで、run() を再実装したり、QWaitCondotion を使用したりする必要はまったくありません。

于 2011-05-24T10:09:51.513 に答える
2

私は Qt を使用するのはかなり初めてで、このような問題に対する「通常の」アプローチがわからないため、これは暗闇の中でのショットのようなものですが、おそらくQCoreApplication::processEventsループ内での呼び出しが役立つでしょう。

于 2011-05-23T19:47:25.620 に答える
2

何らかの理由で厳密に必要でない限り、私は QWaitCondition を取り除きます。代わりに、 enqueueMessage() が新しいデータを QQueue に追加した後に (Qt) シグナルを発行し、ワーカー スレッドが通常の Qt の方法でそのシグナルを (受信する必要がある他のシグナルと共に) 受信するようにします。その後、タイムアウトやその他のハッカーは必要なく、問題は解決します。

(オプションの最適化: 新しいデータを追加する前に QQueue が空の場合にのみシリアル ポートに信号を送信させ、QQueue が空になるまでメイン スレッドの対応するスロットを QQueue から読み取らせます。送信する必要がある信号)

于 2011-05-24T01:58:38.877 に答える