Qt - получить данные и переслать их по последовательному соединению

Я пытаюсь разработать простое приложение Qt. После того, как я нажму кнопку «СТАРТ», приложение должно непрерывно извлекать данные с устройства (используя сторонние библиотеки) и пересылать их как можно скорее по последовательному соединению.

(Уродливое) программное обеспечение, которое я использовал до сих пор, было консольным приложением, которое запускалось последовательно и получало кадры данных, как только они были доступны хостом, используя следующий цикл:

    while(1)
    {
          [...]
        while( MyClient.GetFrame().Result != Result::Success )
        {
            Sleep( 200 );
            std::cout << ".";
        }

          [... pack and send on serial]
    }

Мне было интересно, какой более удобный способ реализовать это в Qt, чтобы поддерживать отзывчивость графического интерфейса, а также с минимально возможной задержкой между getFrame и функцией последовательной записи.

Должен ли я использовать срабатывающий по таймеру SLOT? Пространство имен QtConcurrent? QRunnable? Каковы основные преимущества и недостатки каждого из этих подходов?

Спасибо за вашу помощь!


person blackistheanswer    schedule 27.03.2014    source источник


Ответы (1)


Поскольку существующая библиотека заставляет вас запрашивать данные, единственное, что нужно сделать, это запустить ее по таймеру. Вам решать, будет ли объект, выполняющий эту работу, выполняться в основном потоке или в рабочем потоке. Нет необходимости использовать Qt Concurrent или QRunnable — использование QObject несколько упрощает жизнь, поскольку вы можете легко предоставить обратную связь графическому интерфейсу.

Например, сделав некоторые предположения об API вашего клиента:

class Worker : public QObject {
  Client m_client;
  QSerialPort m_port;
  QBasicTimer m_timer;

  void processFrame() {
    if (m_client.GetFrame().Result != Result::Success) return;
    QByteArray frame = QByteArray::fromRawData(
      m_client.GetFrame().Data, m_client.GetFrame().Size);
    ... process the frame
    if (m_port.write(frame) != frame.size()) {
      ... process the error
    }
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() == m_timer.timerId()) processFrame();
  }
public:
  Worker(QObject * parent = 0) : QObject(parent) {}
  Q_SLOT bool open(const QString & name) {
    m_port.close();
    m_port.setPortName(name);
    if (!m_port.open(name)) return false;
    if (!m_port.setBaudRate(9600)) return false;
    if (!m_port.setDataBits(QSerialPort::Data8)) return false;
    ... other settings go here
    return true;
  }
  Q_SLOT void start() { m_timer.start(200, this); }
  Q_SLOT void stop() { m_timer.stop(); }
  ...
}

/// A thread that's always safe to destruct
class Thread : public QThread {
  using QThread::run; // lock the default implementation
public:
  Thread(QObject * parent = 0) : QThread(parent) {}
  ~Thread() { quit(); wait(); }
};

int main(int argc, char ** argv) {
  QApplication app(argc, argv);
  Worker worker;
  Thread thread; // worker must be declared before thread!
  if (true) {
    // this code is optional, disabling it should not change things
    // too much unless the client implementation blocks too much
    thread.start();
    worker.moveToThread(&thread);
  }

  QPushButton button("Start");
  QObject::connect(&button, &QPushButton::clicked, [&worker]{
    // Those are cross-thread calls, they can't be done directly
    QMetaObject::invoke(&worker, "open", Q_ARG(QString, "COM1");
    QMetaObject::invoke(&worker, "start");
  });
  button.show();

  return app.exec(argc, argv);
}
person Kuba hasn't forgotten Monica    schedule 27.03.2014