pbootcms网站模板|日韩1区2区|织梦模板||网站源码|日韩1区2区|jquery建站特效-html5模板网

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

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 ()?環形?)
主站蜘蛛池模板: 热风机_工业热风机生产厂家上海冠顶公司提供专业热风机图片价格实惠 | 杭州公司变更法人-代理记账收费价格-公司注销代办_杭州福道财务管理咨询有限公司 | 深圳富泰鑫五金_五金冲压件加工_五金配件加工_精密零件加工厂 | 变压器配件,变压器吸湿器,武强县吉口变压器配件有限公司 | 宝鸡市人民医院| 环氧乙烷灭菌器_压力蒸汽灭菌器_低温等离子过氧化氢灭菌器 _低温蒸汽甲醛灭菌器_清洗工作站_医用干燥柜_灭菌耗材-环氧乙烷灭菌器_脉动真空压力蒸汽灭菌器_低温等离子灭菌设备_河南省三强医疗器械有限责任公司 | 纯化水设备-纯水设备-超纯水设备-[大鹏水处理]纯水设备一站式服务商-东莞市大鹏水处理科技有限公司 | 招商帮-一站式网络营销服务|搜索营销推广|信息流推广|短视视频营销推广|互联网整合营销|网络推广代运营|招商帮企业招商好帮手 | 北京模型公司-工业模型-地产模型-施工模型-北京渝峰时代沙盘模型制作公司 | 100国际学校招生 - 专业国际学校择校升学规划 | 玻纤土工格栅_钢塑格栅_PP焊接_单双向塑料土工格栅_复合防裂布厂家_山东大庚工程材料科技有限公司 | 本安接线盒-本安电路用接线盒-本安分线盒-矿用电话接线盒-JHH生产厂家-宁波龙亿电子科技有限公司 | 棉柔巾代加工_洗脸巾oem_一次性毛巾_浴巾生产厂家-杭州禾壹卫品科技有限公司 | 储气罐,真空罐,缓冲罐,隔膜气压罐厂家批发价格,空压机储气罐规格型号-上海申容压力容器集团有限公司 | 别墅图纸超市|别墅设计图纸|农村房屋设计图|农村自建房|别墅设计图纸及效果图大全 | 柴油发电机组_柴油发电机_发电机组价格-江苏凯晨电力设备有限公司 | 运动木地板厂家,篮球场木地板品牌,体育场馆木地板安装 - 欧氏运动地板 | SRRC认证_电磁兼容_EMC测试整改_FCC认证_SDOC认证-深圳市环测威检测技术有限公司 | 高压贴片电容|贴片安规电容|三端滤波器|风华电容代理南京南山 | 旋转/数显粘度计-运动粘度测定仪-上海平轩科学仪器 | 石膏基自流平砂浆厂家-高强石膏基保温隔声自流平-轻质抹灰石膏粉砂浆批发-永康市汇利建设有限公司 | 武汉高温老化房,恒温恒湿试验箱,冷热冲击试验箱-武汉安德信检测设备有限公司 | 细胞染色-流式双标-试剂盒免费代做-上海研谨生物科技有限公司 | 回转支承-转盘轴承-回转驱动生产厂家-洛阳隆达轴承有限公司 | 智能风向风速仪,风速告警仪,数字温湿仪,综合气象仪(气象五要素)-上海风云气象仪器有限公司 | wika威卡压力表-wika压力变送器-德国wika代理-威卡总代-北京博朗宁科技 | 智能垃圾箱|垃圾房|垃圾分类亭|垃圾分类箱专业生产厂家定做-宿迁市传宇环保设备有限公司 | 通风天窗,通风气楼,屋顶通风天窗,屋顶通风天窗公司 | 废气处理_废气处理设备_工业废气处理_江苏龙泰环保设备制造有限公司 | 楼承板-开口楼承板-闭口楼承板-无锡海逵 | 杭州用友|用友软件|用友财务软件|用友ERP系统--杭州协友软件官网 | 警用|治安|保安|不锈钢岗亭-售货亭价格-垃圾分类亭-移动厕所厂家-苏州灿宇建材 | 菏泽商标注册_菏泽版权登记_商标申请代理_菏泽商标注册去哪里 | 杭州双螺杆挤出机-百科| 北京租车公司_汽车/客车/班车/大巴车租赁_商务会议/展会用车/旅游大巴出租_北京桐顺创业租车公司 | 加热制冷恒温循环器-加热制冷循环油浴-杭州庚雨仪器有限公司 | 全自动包装机_灌装机生产厂家-迈驰包装设备有限公司 | 登车桥动力单元-非标液压泵站-非标液压系统-深圳市三好科技有限公司 | 高效节能电机_伺服主轴电机_铜转子电机_交流感应伺服电机_图片_型号_江苏智马科技有限公司 | 低压载波电能表-单相导轨式电能表-华邦电力科技股份有限公司-智能物联网综合管理平台 | 定制液氮罐_小型气相液氮罐_自增压液氮罐_班德液氮罐厂家 |