久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

發送一系列命令并等待響應

Sending a sequence of commands and wait for response(發送一系列命令并等待響應)
本文介紹了發送一系列命令并等待響應的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!

問題描述

我必須更新連接到串行端口的設備上的固件和設置.由于這是由一系列命令完成的,因此我發送一個命令并等待我收到答復.在 answere(多行)中,我搜索指示操作是否成功完成的字符串.

I have to update firmware and settings on a device connected to a serial port. Since this is done by a sequence of commands, I send a command and wait until I recive an answer. Inside the answere (many lines) I search for a string that indicates if the operation is finished successfully.

Serial->write("boot", 1000);
Serial->waitForKeyword("boot successful");
Serial->sendFile("image.dat");
…

所以我為這個阻塞讀/寫方法創建了一個新線程.在線程內部,我使用了 waitForX() 函數.如果我調用 watiForKeyword() 它將調用 readLines() 直到它檢測到關鍵字或超時

So I’ve created a new Thread for this blocking read/write method. Inside the thread I make use of the waitForX() functions. If I call watiForKeyword() it will call readLines() until it detects the keyword or timesout

bool waitForKeyword(const QString &keyword)
{
    QString str;

    // read all lines
    while(serial->readLines(10000))
    {
        // check each line
        while((str = serial->getLine()) != "")
        {
            // found!
            if(str.contains(keyword))
                return true;
        }
    }
    // timeout
    return false;
}

readLines() 讀取所有可用的內容并將其分成幾行,每一行都放在一個 QStringList 中,為了得到一個字符串,我調用 getLine(),它返回列表中的第一個字符串并刪除它.

readLines() reads everything available and separates it into lines , each line is placed inside a QStringList and to get a string I call getLine() which returns the first string in the list and deletes it.

bool SerialPort::readLines(int waitTimeout)
{
if(!waitForReadyRead(waitTimeout))
{
    qDebug() << "Timeout reading" << endl;
    return false;
}

QByteArray data = readAll();
while (waitForReadyRead(100))
    data += readAll();

char* begin = data.data();
char* ptr = strstr(data, "
");

while(ptr != NULL)
{
    ptr+=2;
    buffer.append(begin, ptr - begin);
    emit readyReadLine(buffer);
    lineBuffer.append(QString(buffer)); // store line in Qstringlist
    buffer.clear();

    begin = ptr;
    ptr = strstr(begin, "
");
}
// rest
buffer.append(begin, -1);
return true;
}

問題是,如果我通過終端發送文件來測試應用程序 readLines() 只會讀取文件的一小部分(5 行左右).由于這些行不包含關鍵字.該函數將再次運行,但這次它不等待超時,readLines 立即返回 false.怎么了 ?另外,我不確定這是否是正確的方法......有誰知道如何發送一系列命令并每次都等待響應?

The problem is if I send a file via terminal to test the app readLines() will only read a smale part of the file ( 5 Lines or so). Since these lines do not contain the keyword. the function will run once again, but this time it dosnt wait for timeout, readLines just return false immediately. Whats wrong ? Also I'm not shure if this is the right approach... Does anyone know how to send a sequenze of commands and wait for a response each time?

推薦答案

讓我們使用 QStateMachine 來簡化這個問題.讓我們回想一下您希望這樣的代碼看起來如何:

Let's use QStateMachine to make this simple. Let's recall how you wished such code would look:

Serial->write("boot", 1000);
Serial->waitForKeyword("boot successful");
Serial->sendFile("image.dat");

讓我們將它放在一個類中,該類具有程序員可能處于的每個狀態的顯式狀態成員.我們還將擁有動作生成器 sendexpect 等.將給定的動作附加到狀態.

Let's put it in a class that has explicit state members for each state the programmer could be in. We'll also have action generators send, expect, etc. that attach given actions to states.

// https://github.com/KubaO/stackoverflown/tree/master/questions/comm-commands-32486198
#include <QtWidgets>
#include <private/qringbuffer_p.h>
#include <type_traits>

[...]

class Programmer : public StatefulObject {
   Q_OBJECT
   AppPipe m_port { nullptr, QIODevice::ReadWrite, this };
   State      s_boot   { &m_mach, "s_boot" },
              s_send   { &m_mach, "s_send" };
   FinalState s_ok     { &m_mach, "s_ok" },
              s_failed { &m_mach, "s_failed" };
public:
   Programmer(QObject * parent = 0) : StatefulObject(parent) {
      connectSignals();
      m_mach.setInitialState(&s_boot);
      send  (&s_boot, &m_port, "boot
");
      expect(&s_boot, &m_port, "boot successful", &s_send, 1000, &s_failed);
      send  (&s_send, &m_port, ":HULLOTHERE
:00000001FF
");
      expect(&s_send, &m_port, "load successful", &s_ok, 1000, &s_failed);
   }
   AppPipe & pipe() { return m_port; }
};

這是一個功能齊全、完整的程序員代碼!完全異步、非阻塞,也能處理超時.

This is fully functional, complete code for the programmer! Completely asynchronous, non-blocking, and it handles timeouts, too.

可以擁有動態生成狀態的基礎設施,這樣您就不必手動創建所有狀態.如果您有明確的狀態,代碼要小得多,恕我直言更容易理解.只有對于具有 50-100 多個狀態的復雜通信協議,擺脫顯式命名狀態才有意義.

It's possible to have infrastructure that generates the states on-the-fly, so that you don't have to manually create all the states. The code is much smaller and IMHO easier to comperehend if you have explicit states. Only for complex communication protocols with 50-100+ states would it make sense to get rid of explicit named states.

AppPipe 是一個簡單的進程內雙向管道,可用作真正串行端口的替代品:

The AppPipe is a simple intra-process bidirectional pipe that can be used as a stand-in for a real serial port:

// See http://stackoverflow.com/a/32317276/1329652
/// A simple point-to-point intra-process pipe. The other endpoint can live in any
/// thread.
class AppPipe : public QIODevice {
  [...]
};

StatefulObject 包含一個狀態機、一些用于監控狀態機進度的基本信號,以及用于將信號與狀態連接起來的 connectSignals 方法:

The StatefulObject holds a state machine, some basic signals useful for monitoring the state machine's progress, and the connectSignals method used to connect the signals with the states:

class StatefulObject : public QObject {
   Q_OBJECT
   Q_PROPERTY (bool running READ isRunning NOTIFY runningChanged)
protected:
   QStateMachine m_mach  { this };
   StatefulObject(QObject * parent = 0) : QObject(parent) {}
   void connectSignals() {
      connect(&m_mach, &QStateMachine::runningChanged, this, &StatefulObject::runningChanged);
      for (auto state : m_mach.findChildren<QAbstractState*>())
         QObject::connect(state, &QState::entered, this, [this, state]{
            emit stateChanged(state->objectName());
         });
   }
public:
   Q_SLOT void start() { m_mach.start(); }
   Q_SIGNAL void runningChanged(bool);
   Q_SIGNAL void stateChanged(const QString &);
   bool isRunning() const { return m_mach.isRunning(); }
};

StateFinalState 是 Qt 3 風格的簡單命名狀態包裝器.它們允許我們一次性聲明狀態并為其命名.

The State and FinalState are simple named state wrappers in the style of Qt 3. They allow us to declare the state and give it a name in one go.

template <class S> struct NamedState : S {
   NamedState(QState * parent, const char * name) : S(parent) {
      this->setObjectName(QLatin1String(name));
   }
};
typedef NamedState<QState> State;
typedef NamedState<QFinalState> FinalState;

動作生成器也很簡單.動作生成器的意思是在進入給定狀態時做某事".要執行的狀態始終作為第一個參數給出.第二個和后續參數特定于給定的操作.有時,一個動作也可能需要一個目標狀態,例如如果成功或失敗.

The action generators are quite simple, too. The meaning of an action generator is "do something when a given state is entered". The state to act on is always given as the first argument. The second and subsequent arguments are specific to the given action. Sometimes, an action might need a target state as well, e.g. if it succeeds or fails.

void send(QAbstractState * src, QIODevice * dev, const QByteArray & data) {
   QObject::connect(src, &QState::entered, dev, [dev, data]{
      dev->write(data);
   });
}

QTimer * delay(QState * src, int ms, QAbstractState * dst) {
   auto timer = new QTimer(src);
   timer->setSingleShot(true);
   timer->setInterval(ms);
   QObject::connect(src, &QState::entered, timer, static_cast<void (QTimer::*)()>(&QTimer::start));
   QObject::connect(src, &QState::exited,  timer, &QTimer::stop);
   src->addTransition(timer, SIGNAL(timeout()), dst);
   return timer;
}

void expect(QState * src, QIODevice * dev, const QByteArray & data, QAbstractState * dst,
            int timeout = 0, QAbstractState * dstTimeout = nullptr)
{
   addTransition(src, dst, dev, SIGNAL(readyRead()), [dev, data]{
      return hasLine(dev, data);
   });
   if (timeout) delay(src, timeout, dstTimeout);
}

hasLine 測試只是檢查可以從設備讀取的所有行以獲取給定的針.這適用于這個簡單的通信協議.如果您的通信涉及更多,您將需要更復雜的機器.有必要閱讀所有的行,即使你找到了你的針.這是因為這個測試是從 readyRead 信號中調用的,并且在該信號中您必須讀取滿足所選標準的所有數據.這里的標準是數據形成整行.

The hasLine test simply checks all lines that can be read from the device for a given needle. This works fine for this simple communications protocol. You'd need more complex machinery if your communications were more involved. It is necessary to read all the lines, even if you find your needle. That's because this test is invoked from the readyRead signal, and in that signal you must read all the data that fulfills a chosen criterion. Here, the criterion is that the data forms a full line.

static bool hasLine(QIODevice * dev, const QByteArray & needle) {
   auto result = false;
   while (dev->canReadLine()) {
      auto line = dev->readLine();
      if (line.contains(needle)) result = true;
   }
   return result;
}

使用默認 API 向狀態添加受保護的轉換有點麻煩,因此我們將對其進行包裝以使其更易于使用,并保持上面的動作生成器的可讀性:

Adding guarded transitions to states is a bit cumbersome with the default API, so we will wrap it to make it easier to use, and to keep the action generators above readable:

template <typename F>
class GuardedSignalTransition : public QSignalTransition {
   F m_guard;
protected:
   bool eventTest(QEvent * ev) Q_DECL_OVERRIDE {
      return QSignalTransition::eventTest(ev) && m_guard();
   }
public:
   GuardedSignalTransition(const QObject * sender, const char * signal, F && guard) :
      QSignalTransition(sender, signal), m_guard(std::move(guard)) {}
   GuardedSignalTransition(const QObject * sender, const char * signal, const F & guard) :
      QSignalTransition(sender, signal), m_guard(guard) {}
};

template <typename F> static GuardedSignalTransition<F> *
addTransition(QState * src, QAbstractState *target,
              const QObject * sender, const char * signal, F && guard) {
   auto t = new GuardedSignalTransition<typename std::decay<F>::type>
         (sender, signal, std::forward<F>(guard));
   t->setTargetState(target);
   src->addTransition(t);
   return t;
}

僅此而已 - 如果您擁有真正的設備,這就是您所需要的.由于我沒有你的設備,我將創建另一個 StatefulObject 來模擬假定的設備行為:

That's about it - if you had a real device, that's all you need. Since I don't have your device, I'll create another StatefulObject to emulate the presumed device behavior:

class Device : public StatefulObject {
   Q_OBJECT
   AppPipe m_dev { nullptr, QIODevice::ReadWrite, this };
   State      s_init     { &m_mach, "s_init" },
              s_booting  { &m_mach, "s_booting" },
              s_firmware { &m_mach, "s_firmware" };
   FinalState s_loaded   { &m_mach, "s_loaded" };
public:
   Device(QObject * parent = 0) : StatefulObject(parent) {
      connectSignals();
      m_mach.setInitialState(&s_init);
      expect(&s_init, &m_dev, "boot", &s_booting);
      delay (&s_booting, 500, &s_firmware);
      send  (&s_firmware, &m_dev, "boot successful
");
      expect(&s_firmware, &m_dev, ":00000001FF", &s_loaded);
      send  (&s_loaded,   &m_dev, "load successful
");
   }
   Q_SLOT void stop() { m_mach.stop(); }
   AppPipe & pipe() { return m_dev; }
};

現在讓我們很好地可視化.我們將有一個帶有文本瀏覽器的窗口,顯示通信內容.下面是啟動/停止編程器或設備的按鈕,以及指示仿真設備和編程器狀態的標簽:

Now let's make it all nicely visualized. We'll have a window with a text browser showing the contents of the communications. Below it will be buttons to start/stop the programmer or the device, and labels indicating the state of the emulated device and the programmer:

int main(int argc, char ** argv) {
   using Q = QObject;
   QApplication app{argc, argv};
   Device dev;
   Programmer prog;

   QWidget w;
   QGridLayout grid{&w};
   QTextBrowser comms;
   QPushButton devStart{"Start Device"}, devStop{"Stop Device"},
               progStart{"Start Programmer"};
   QLabel devState, progState;
   grid.addWidget(&comms, 0, 0, 1, 3);
   grid.addWidget(&devState, 1, 0, 1, 2);
   grid.addWidget(&progState, 1, 2);
   grid.addWidget(&devStart, 2, 0);
   grid.addWidget(&devStop, 2, 1);
   grid.addWidget(&progStart, 2, 2);
   devStop.setDisabled(true);
   w.show();

我們將連接設備和程序員的AppPipe.我們還將可視化程序員發送和接收的內容:

We'll connect the device's and programmer's AppPipes. We'll also visualize what the programmer is sending and receiving:

   dev.pipe().addOther(&prog.pipe());
   prog.pipe().addOther(&dev.pipe());
   Q::connect(&prog.pipe(), &AppPipe::hasOutgoing, &comms, [&](const QByteArray & data){
      comms.append(formatData("&gt;", "blue", data));
   });
   Q::connect(&prog.pipe(), &AppPipe::hasIncoming, &comms, [&](const QByteArray & data){
      comms.append(formatData("&lt;", "green", data));
   });

最后,我們將連接按鈕和標簽:

Finally, we'll connect the buttons and labels:

   Q::connect(&devStart, &QPushButton::clicked, &dev, &Device::start);
   Q::connect(&devStop, &QPushButton::clicked, &dev, &Device::stop);
   Q::connect(&dev, &Device::runningChanged, &devStart, &QPushButton::setDisabled);
   Q::connect(&dev, &Device::runningChanged, &devStop, &QPushButton::setEnabled);
   Q::connect(&dev, &Device::stateChanged, &devState, &QLabel::setText);
   Q::connect(&progStart, &QPushButton::clicked, &prog, &Programmer::start);
   Q::connect(&prog, &Programmer::runningChanged, &progStart, &QPushButton::setDisabled);
   Q::connect(&prog, &Programmer::stateChanged, &progState, &QLabel::setText);
   return app.exec();
}

#include "main.moc"

ProgrammerDevice 可以存在于任何線程中.我將它們留在主線程中,因為沒有理由將它們移出,但是您可以將它們都放入專用線程中,或者將每個放入自己的線程中,或者放入與其他對象共享的線程中,等等.它是完全透明的,因為 AppPipe 支持跨線程通信.如果使用 QSerialPort 而不是 AppPipe,情況也會如此.重要的是 QIODevice 的每個實例僅在一個線程中使用.其他一切都通過信號/插槽連接發生.

The Programmer and Device could live in any thread. I've left them in the main thread since there's no reason to move them out, but you could put both into a dedicated thread, or each into its own thread, or into threads shared with other objects, etc. It's completely transparent since AppPipe supports communications across the threads. This would also be the case if QSerialPort was used instead of AppPipe. All that matters is that each instance of a QIODevice is used from one thread only. Everything else happens via signal/slot connections.

例如如果您希望 Programmer 位于專用線程中,您可以在 main 的某處添加以下內容:

E.g. if you wanted the Programmer to live in a dedicated thread, you'd add the following somewhere in main:

  // fix QThread brokenness
  struct Thread : QThread { ~Thread() { quit(); wait(); } };

  Thread progThread;
  prog.moveToThread(&progThread);
  progThread.start();

一個小助手格式化數據以使其更易于閱讀:

A little helper formats the data to make it easier to read:

static QString formatData(const char * prefix, const char * color, const QByteArray & data) {
   auto text = QString::fromLatin1(data).toHtmlEscaped();
   if (text.endsWith('
')) text.truncate(text.size() - 1);
   text.replace(QLatin1Char('
'), QString::fromLatin1("<br/>%1 ").arg(QLatin1String(prefix)));
   return QString::fromLatin1("<font color="%1">%2 %3</font><br/>")
         .arg(QLatin1String(color)).arg(QLatin1String(prefix)).arg(text);
}

這篇關于發送一系列命令并等待響應的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!

【網站聲明】本站部分內容來源于互聯網,旨在幫助大家更快的解決問題,如果有圖片或者內容侵犯了您的權益,請聯系我們刪除處理,感謝您的支持!

相關文檔推薦

How can I read and manipulate CSV file data in C++?(如何在 C++ 中讀取和操作 CSV 文件數據?)
In C++ why can#39;t I write a for() loop like this: for( int i = 1, double i2 = 0; (在 C++ 中,為什么我不能像這樣編寫 for() 循環: for( int i = 1, double i2 = 0;)
How does OpenMP handle nested loops?(OpenMP 如何處理嵌套循環?)
Reusing thread in loop c++(在循環 C++ 中重用線程)
Precise thread sleep needed. Max 1ms error(需要精確的線程睡眠.最大 1ms 誤差)
Is there ever a need for a quot;do {...} while ( )quot; loop?(是否需要“do {...} while ()?環形?)
主站蜘蛛池模板: 国产高清在线精品一区二区三区 | 中文字幕加勒比 | 色婷婷综合在线观看 | 久久久久久国产 | 国产精品欧美一区二区 | 国产精品日韩 | 91中文字幕在线观看 | av一二三四 | 精品一区二区三区中文字幕 | 亚洲国产精品va在线看黑人 | 在线视频成人 | 欧美一区二区三区在线播放 | 亚洲麻豆| 天天干天天玩天天操 | 欧美理论| 狠狠干天天干 | 久久久久国产精品午夜一区 | 在线观看国产 | 国产在线精品一区二区 | av在线免费不卡 | 日韩国产在线观看 | 一区二区三区视频在线观看 | 99精品视频在线 | 国产一区三区视频 | 亚洲精品久久久久中文字幕欢迎你 | 日韩欧美在线视频一区 | 干干干日日日 | 欧美成人一区二区三区 | 一区二区三区视频在线观看 | 欧美色综合天天久久综合精品 | 久久国产婷婷国产香蕉 | 日本激情一区二区 | 国产精品爱久久久久久久 | 91视频在线 | 天天搞天天操 | 天堂在线91 | 超碰美女在线 | 日韩免费视频 | 在线国产视频 | 久久精品国产一区二区三区 | 欧美三区在线观看 |