2013-02-27 30 views
6

我正在通過串行端口向設備發送(寫入)字節。我正在使用QSerialPort(http://qt-project.org/wiki/QtSerialPort)模塊實例化設備IO支持。當我發送消息到我的INSTEON調制解調器(串行)時,在閱讀我的消息時,設備發送一條消息+ 0x06(ACK字節),然後發送一條狀態消息。Qt串行端口 - 始終讀取數據

我已經使用DockLight測試了我的消息(http://www.docklight.de/)。我將下列消息發送到查詢設備的狀態:

02 62 1D E9 4B 05 19 00 

使用Docklight,我收到了迴應:

02 62 1D E9 4B 05 19 00 06 02 50 20 CB CF 1E DA F7 21 00 FF 

返回的消息表明正是我所期望的,該設備是上。如果關閉,如果設備關閉,則調制解調器將在最後一個字節位置返回0x00。現在,我的問題 - 我不能正確地發送並接收響應字節。我已經嘗試了許多不同的實施例和配置,目前我使用以下:

設置信號槽連接:用於通過設備的QList迭代

QObject::connect(&thread, SIGNAL(sendResponse(QByteArray)), 
    this, SLOT(handleResponse(QByteArray))); 
QObject::connect(&thread, SIGNAL(error(QString)), 
    this, SLOT(processError(QString))); 
QObject::connect(&thread, SIGNAL(timeout(QString)), 
    this, SLOT(processTimeout(QString))); 

功能。如果設備是所需類型(「Light」),那麼我們將設備ID格式化爲預期的QByteArray消息結構。將消息傳遞給線程以進行發送。 (線程從QSerialPort BlockingMaster例如改性用於設置局部線程變量和執行(運行)螺紋

void Device::currentStatus(QList<Device *> * deviceList){ 
    QString devID, updateQry; 
    int devStatus, updateStatus; 
    updateStatus=0; 
    QSqlQuery query; 
    for(int i=0; i<deviceList->size(); i++){ 
     if(deviceList->at(i)->type == "Light"){ 
      devStatus = deviceList->at(i)->status; 
      devID = deviceList->at(i)->deviceID; 
      QByteArray msg; 
      bool msgStatus; 
      msg.resize(8); 

      msg[0] = 0x02; 
      msg[1] = 0x62; 
      msg[2] = 0x00; 
      msg[3] = 0x00; 
      msg[4] = 0x00; 
      msg[5] = 0x05; 
      msg[6] = 0x19; 
      msg[7] = 0x00; 
      msg.replace(2, 3, QByteArray::fromHex(devID.toLocal8Bit())); 
      qDebug() << "Has device " << deviceList->at(i)->name << "Changed?"; 
      //send(msg,&msgStatus, &updateStatus); 
      //msg.clear(); 
      thread.setupPort("COM3",500,msg); 
      if(devStatus!=updateStatus){ 
       qDebug() << deviceList->at(i)->name << " is now: " << updateStatus; 
       updateStatus = !updateStatus; 
      } 
     } 
    } 
} 

SetupThread功能

void serialThread::setupPort(const QString &portName, int waitTimeout, const QByteArray &msg){ 
    qDebug() << "Send Message " << msg.toHex(); 
    QMutexLocker locker(&mutex); 
    this->portName = portName; 
    this->waitTimeout = waitTimeout; 
    this->msg = msg; 
    if(!isRunning()) 
     start(); 
    else 
     cond.wakeOne(); 
} 

Run功能。 - 已處理髮送和接收

void serialThread::run(){ 
    bool currentPortNameChanged = false; 
    qDebug() << "Thread executed"; 
    mutex.lock(); 
    QString currentPortName; 
    if(currentPortName != portName){ 
     currentPortName = portName; 
     currentPortNameChanged = true; 
    } 

    int currentWaitTimeout = waitTimeout; 
    QByteArray sendMsg = msg; 
    mutex.unlock(); 
    QSerialPort serial; 

    while(!quit){ 
     if(currentPortNameChanged){ 
      serial.close(); 
      serial.setPortName("COM3"); 

      if (!serial.open(QIODevice::ReadWrite)) { 
       emit error(tr("Can't open %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 

      if (!serial.setBaudRate(QSerialPort::Baud19200)) { 
       emit error(tr("Can't set baud rate 9600 baud to port %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 

      if (!serial.setDataBits(QSerialPort::Data8)) { 
       emit error(tr("Can't set 8 data bits to port %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 

      if (!serial.setParity(QSerialPort::NoParity)) { 
       emit error(tr("Can't set no patity to port %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 

      if (!serial.setStopBits(QSerialPort::OneStop)) { 
       emit error(tr("Can't set 1 stop bit to port %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 

      if (!serial.setFlowControl(QSerialPort::NoFlowControl)) { 
       emit error(tr("Can't set no flow control to port %1, error code %2") 
          .arg(portName).arg(serial.error())); 
       return; 
      } 
     } 

     //write request 
     serial.write(msg); 
     if (serial.waitForBytesWritten(waitTimeout)) { 
      //! [8] //! [10] 
      // read response 
      if (serial.waitForReadyRead(currentWaitTimeout)) { 
       QByteArray responseData = serial.readAll(); 
       while (serial.waitForReadyRead(10)){ 
        responseData += serial.readAll(); 
       } 

       QByteArray response = responseData; 
       //! [12] 
       emit this->sendResponse(response); 
       //! [10] //! [11] //! [12] 
      } else { 
       emit this->timeout(tr("Wait read response timeout %1") 
          .arg(QTime::currentTime().toString())); 
      } 
      //! [9] //! [11] 
     } else { 
      emit timeout(tr("Wait write request timeout %1") 
         .arg(QTime::currentTime().toString())); 
     } 
     mutex.lock(); 
     cond.wait(&mutex); 
     if (currentPortName != portName) { 
      currentPortName = portName; 
      currentPortNameChanged = true; 
     } else { 
      currentPortNameChanged = false; 
     } 
     currentWaitTimeout = waitTimeout; 
     sendMsg = msg; 
     mutex.unlock(); 
    } 
    serial.close(); 
} 

handleResponse功能,接收響應信號的SLOT

void Device::handleResponse(const QByteArray &msg){ 
    qDebug() << "Read: " << msg.toHex(); 
} 

我收到以下輸出:這裏

Has device "Living Room Light" Changed? 
Send Message "02621de94b051900" 
Has device "Bedroom Light" Changed? 
Send Message "026220cbcf051900" 
Thread executed 
Read: "026220cbcf05190006" 
Polling for changes... 
Has device "Living Room Light" Changed? 
Send Message "02621de94b051900" 
Has device "Bedroom Light" Changed? 
Send Message "026220cbcf051900" 
Read: "025020cbcf1edaf721000002621de94b05190006" 
Polling for changes... 
Has device "Living Room Light" Changed? 
Send Message "02621de94b051900" 
Has device "Bedroom Light" Changed? 
Send Message "026220cbcf051900" 
Read: "02501de94b1edaf72100ff02621de94b05190006" 

兩個問題。

  1. 我從來沒有收到關於第二個設備(臥室燈)的任何迴應,這是發送第二個消息。似乎發送被阻止,你會如何建議我格式化我的發送,以便在收到第一次發送的響應後發送?只有1個COM端口可用於發送/接收。我相信我應該發送消息到設備1,接收設備1響應,發送到設備2,接收設備2.我可能會看到大量設備和使用等待條件的巨大交通堵塞,即。在執行設備2的通信過程之前,請等待設備1通信過程完成?

  2. 第一次閱讀包含適當的前半部分接收。Read: "026220cbcf05190006"第二接收包含第一響應接着所述第二響應的第一半部分的第二半:讀取2 - Read: "025020cbcf1edaf721000002621de94b05190006"適當全響應是02621DE94B05190006 025020CBCF1EDAF72100FF (注意20CBCF是設備2的在充分響應的示例ID)

對串口接收數據的方式應該做些什麼改正? 謝謝!

回答

3

我相信我的問題已經從這個問題的範圍轉移了。在Kuzulis的幫助下,我實現了Write/Read功能,可以一致地成功發送和讀取串行消息。 Kuzulis建議使用同步阻塞通信模式,但後來決定採用異步非阻塞方法最適合我的應用。

我的實現嚴格遵循QSerialPort源文件提供的「主」示例。

我使用CurrentStatus來遍歷Device對象的QList。對於Device列表中的每個Light,我格式化一個8 Byte消息來查詢設備的當前狀態(ON/OFF)。

void Device::currentStatus(QList<Device *> * deviceList){ 
    QString devID, updateQry; 
    int devStatus, updateStatus; 
    updateStatus=0; 
    QSqlQuery query; 
    for(int i=0; i<deviceList->size(); i++){ 
     if(deviceList->at(i)->type == "Light"){ 
      devStatus = deviceList->at(i)->status; 
      devID = deviceList->at(i)->deviceID; 
      QByteArray msg; 
      msg.resize(8); 

      msg[0] = 0x02; 
      msg[1] = 0x62; 
      msg[2] = 0x00; 
      msg[3] = 0x00; 
      msg[4] = 0x00; 
      msg[5] = 0x05; 
      msg[6] = 0x19; 
      msg[7] = 0x00; 
      msg.replace(2, 3, QByteArray::fromHex(devID.toLocal8Bit())); 
      qDebug() << "Has device " << deviceList->at(i)->name << "Changed?"; 

      emit writeRequest(msg); 

      if(devStatus!=updateStatus){ 
       qDebug() << deviceList->at(i)->name << " is now: " << updateStatus; 
       updateStatus = !updateStatus; 
      } 
     } 
    } 
} 

在設備類的構造,我的信號和槽連接:

Device::Device(){ 

    serialTimer.setSingleShot(true); 
    QObject::connect(&serial, SIGNAL(readyRead()), 
        this, SLOT(handleResponse())); 
    QObject::connect(&serialTimer, SIGNAL(timeout()), 
        this, SLOT(processTimeout())); 
    QObject::connect(this, SIGNAL(writeRequest(QByteArray)), 
        this, SLOT(writeSerial(QByteArray))); 
} 

該消息之後在currentStatus發送已經準備,emit writeRequest(msg);被調用。這將調度連接到插槽writeRequest的信號。 writeRequest用於設置並實際將消息寫入串行端口。

void Device::writeSerial(const QByteArray &msg){ 
    if (serial.portName() != "COM3") { 
     serial.close(); 
     serial.setPortName("COM3"); 

     if (!serial.open(QIODevice::ReadWrite)) { 
      processError(tr("Can't open %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 

     if (!serial.setBaudRate(QSerialPort::Baud19200)) { 
      processError(tr("Can't set rate 19200 baud to port %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 

     if (!serial.setDataBits(QSerialPort::Data8)) { 
      processError(tr("Can't set 8 data bits to port %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 

     if (!serial.setParity(QSerialPort::NoParity)) { 
      processError(tr("Can't set no patity to port %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 

     if (!serial.setStopBits(QSerialPort::OneStop)) { 
      processError(tr("Can't set 1 stop bit to port %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 

     if (!serial.setFlowControl(QSerialPort::NoFlowControl)) { 
      processError(tr("Can't set no flow control to port %1, error code %2") 
         .arg(serial.portName()).arg(serial.error())); 
      return; 
     } 
    } 
    qDebug() << "Message written"; 
    this->msgRequest = msg; 
    serial.write(msgRequest); 
    serialTimer.start(400); 
} 

設置串口後,我將當前消息保存到msgRequest。如果出現錯誤,則可能必須使用此重新發送消息。在調用serial.write()之後,我設置了一個400ms的計時器。一旦這個定時器到期,我檢查從串口讀取的內容。

handleResponse()是每次調用的插槽QSerialPort發出readyRead()信號。 readyRead()將任何可用數據追加到QByteArray response

void Device::handleResponse(){ 
    response.append(serial.readAll()); 
} 

經過400ms後,serialTimer(一次性定時器)將發出一個timeout()信號。在將我們要求的消息寫入串行端口之後立即啓動了serialTimerprocessTimeout()是我們在發送消息後最終檢查從PowerLinc調制解調器收到的響應的地方。當消息發送到INSTEON PowerLinc調制解調器(PLM)時,PLM回顯消息並追加0x06(肯定ACK)或0x15(NACK)。在processTimeout()我檢查以確保收到的最後一個字節是ACK字節,否則 - 重新發送我們最初請求的消息。

void Device::processTimeout(){ 
    qDebug() << "Read: " << response.toHex(); 
    int msgLength = this->msgRequest.length(); 
    if(response.at(msgLength)!=0x06){ 
     qDebug() << "Error, resend."; 
     emit writeRequest(msgRequest); 
    } 
    response.clear(); 
} 

我使用串行端口監視器4.0(Eltima軟件)來驗證串行端口上的寫入和讀取事務。在下面,您可以看到1個樣本交易的日誌打印輸出。

20:44:30:666 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00 <--- Send 
20:44:30:669 STATUS_SUCCESS 02 62 1d e9 4b 05 19 00 06 <--- Receive 
20:44:30:875 STATUS_SUCCESS 02 <--- Receive 
20:44:30:881 STATUS_SUCCESS 50 1d e9 4b 1e da f7 21 00 ff <--- Receive 

對於20次發送,我收到了相同的響應。因此,我可以放心地說我的數據到達不一致的問題已經解決。現在我正在努力處理多個寫請求,但我相信這是一個需要調查的單獨問題。我感謝大家的支持。

3
  1. 查看存儲庫中的BlockingMaster示例,並閱讀有關阻塞I/O的文檔。另外,請勿不必要地使用阻塞I/O。

  2. 使用bytesAvailable()獲取可供讀取的數據的數量,因爲不是您立即收到完整響應包的事實。

+1

謝謝您的意見。我相當大地調整了我的代碼。我現在收到了更多的「一致」迴應,但它仍然不符合我的意圖。 – 2013-03-04 05:12:17

+0

+1爲「難以想象的麪條代碼」...和一個有用的答案。 – cgmb 2013-03-08 03:05:36

+0

使用bytesAvailable()的問題是響應不是設定的字節數。根據發送到設備的消息的類型和格式,每個響應可以是可變的長度。 – 2013-10-18 08:07:39