2013-02-27 26 views
6

Tôi đang gửi (ghi) byte vào thiết bị qua cổng nối tiếp của mình. Tôi đang sử dụng mô-đun QSerialPort (http://qt-project.org/wiki/QtSerialPort) để nhanh chóng hỗ trợ IO thiết bị. Khi tôi gửi tin nhắn đến modem INSTEON của tôi (nối tiếp), khi đọc tin nhắn của tôi, thiết bị sẽ gửi lại một bản sao của tin nhắn của tôi + 0x06 (ACK Byte), sau đó là thông báo trạng thái.Cổng nối tiếp Qt - Đọc dữ liệu một cách nhất quán

Tôi đã kiểm tra thư của mình bằng DockLight (http://www.docklight.de/). Tôi gửi thông điệp sau đây để truy vấn trạng thái của thiết bị:

02 62 1D E9 4B 05 19 00 

Sử dụng Docklight, tôi nhận được phản hồi:

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

Thông điệp trở chỉ ra chính xác những gì tôi mong đợi, rằng thiết bị trên . Nếu tắt, modem sẽ gửi lại 0x00 ở vị trí byte cuối cùng nếu thiết bị tắt. Bây giờ, vấn đề của tôi - Tôi không phải thiết lập chức năng của tôi đúng cách để gửi và sau đó nhận các byte phản hồi. Tôi đã cố gắng rất nhiều ví dụ và cấu hình khác nhau, hiện nay tôi đang sử dụng như sau: kết nối tín hiệu-khe

Setup:

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))); 

Chức năng sử dụng để lặp qua QList của thiết bị. Nếu thiết bị là kiểu mong muốn ("Light"), thì chúng ta định dạng ID thiết bị thành cấu trúc tin nhắn QByteArray dự định. Chuyển tin nhắn tới chủ đề để gửi. (Chủ đề được sửa đổi từ QSerialPort BlockingMaster dụ

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 chức năng sử dụng để thiết lập các biến chủ đề địa phương và thực thi (chạy) chủ đề

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 Chức năng -.. Cầm gửi và nhận

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 chức năng, SLOT nhận tín hiệu phản hồi

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

tôi nhận được kết quả như sau:

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" 

Hai vấn đề ở đây.

  1. Tôi chưa bao giờ nhận được bất kỳ phản hồi nào về thiết bị thứ hai (Đèn ngủ), đây là tin nhắn được gửi thứ hai. Dường như việc gửi đang bị chặn, bạn sẽ đề xuất định dạng gửi của tôi như thế nào để tôi gửi sau khi nhận được phản hồi cho lần gửi đầu tiên? Chỉ có 1 cổng COM có thể được sử dụng để gửi/nhận. Tôi tin rằng tôi nên gửi tin nhắn đến thiết bị 1, nhận được thiết bị 1 phản ứng, gửi đến thiết bị 2, nhận thiết bị 2. Tôi có thể kết thúc nhìn thấy một mứt giao thông rất lớn với rất nhiều thiết bị và sử dụng điều kiện chờ đợi, tức là. chờ cho quá trình liên lạc thiết bị 1 kết thúc trước khi thực hiện quá trình kết nối cho thiết bị 2?

  2. Lần đọc đầu tiên chứa nửa đầu thích hợp của lần nhận.Read: "026220cbcf05190006" Thứ hai nhận chứa nửa thứ 2 của phản ứng 1 tiếp theo là nửa ngày 1 câu trả lời thứ hai: Đọc 2 - Read: "025020cbcf1edaf721000002621de94b05190006" Câu trả lời đầy đủ thích hợp là 02621DE94B05190006 025020CBCF1EDAF72100FF (lưu ý 20CBCF là ID thiết bị 2 trong ví dụ phản ứng đầy đủ)

Điều chỉnh nào được thực hiện theo cách tôi nhận dữ liệu từ cổng nối tiếp? Cảm ơn bạn!

Trả lời

3

Tôi tin rằng các sự cố của tôi đã chuyển từ phạm vi của câu hỏi này. Với sự trợ giúp của Kuzulis, tôi đã thực hiện các chức năng Write/Read để gửi và đọc thành công các thông điệp nối tiếp một cách nhất quán. Kuzulis khuyến nghị sử dụng mẫu truyền thông chặn Đồng bộ, tuy nhiên sau đó nó đã quyết định rằng phương pháp Non-Blocking Không đồng bộ sẽ phù hợp nhất cho ứng dụng của tôi.

Việc triển khai của tôi chặt chẽ theo ví dụ "Chính" được cung cấp cùng với tệp nguồn QSerialPort.

Tôi sử dụng CurrentStatus để lặp qua Danh sách các đối tượng Device. Đối với mỗi Ánh sáng trong danh sách Thiết bị, tôi định dạng một tin nhắn 8 Byte để truy vấn trạng thái hiện tại của thiết bị (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; 
      } 
     } 
    } 
} 

Trong constructor lớp Device, tôi kết nối các tín hiệu và khe cắm:

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))); 
} 

Sau khi tin nhắn cần gửi trong currentStatus đã được chuẩn bị, emit writeRequest(msg); được gọi. Điều này gửi tín hiệu được kết nối với khe writeRequest. writeRequest được sử dụng để thiết lập và thực sự viết tin nhắn đến cổng nối tiếp.

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); 
} 

Sau khi thiết lập cổng nối tiếp, tôi lưu thông báo hiện tại vào msgRequest. Điều này có thể phải được sử dụng để gửi lại tin nhắn nếu có lỗi. Sau khi gọi serial.write(), tôi thiết lập bộ hẹn giờ 400ms. Khi bộ hẹn giờ này hết hạn, tôi kiểm tra những gì đã đọc từ cổng nối tiếp.

handleResponse() là một khe được gọi là mọi thời gian QSerialPort phát ra tín hiệu readyRead(). readyRead() gắn thêm bất kỳ dữ liệu nào có sẵn vào QByteArray response.

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

Sau 400ms, bộ đếm nối tiếp (một lần chụp) sẽ phát ra tín hiệu timeout(). serialTimer được bắt đầu ngay sau khi viết tin nhắn yêu cầu của chúng tôi đến cổng nối tiếp. processTimeout() là nơi cuối cùng chúng tôi kiểm tra phản hồi nhận được từ PowerLinc Modem sau khi gửi tin nhắn của chúng tôi. Khi các tin nhắn được gửi đến Modem PowerLinc INSTEON (PLM), PLM sẽ trả về thông điệp và nối thêm 0x06 (Positive ACK) hoặc 0x15 (NACK). Trong processTimeout() Tôi kiểm tra để đảm bảo byte cuối cùng nhận được là byte ACK, nếu không - gửi lại tin nhắn được yêu cầu ban đầu của chúng tôi.

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(); 
} 

Tôi đã sử dụng Cổng giám sát cổng nối tiếp 4.0 (Phần mềm Eltima) để xác minh ghi và đọc giao dịch trên cổng nối tiếp. Dưới đây, bạn có thể xem bản in nhật ký cho 1 giao dịch mẫu.

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 

Trong 20 lần gửi, tôi nhận được phản hồi tương tự. Vì vậy, tôi có thể nói một cách an toàn các vấn đề của tôi với sự xuất hiện dữ liệu không phù hợp đã được giải quyết. Bây giờ tôi đang đấu tranh với nhiều yêu cầu viết, nhưng tôi tin rằng đó là một câu hỏi riêng biệt để được điều tra. Tôi đánh giá cao sự hỗ trợ của mọi người.

3
  1. Xem ví dụ BlockingMaster trong kho và đọc tài liệu về chặn I/O. Ngoài ra, không sử dụng chặn I/O không cần thiết.

  2. Sử dụng byteCó sẵn() để nhận số lượng dữ liệu sẵn có để đọc, vì không phải là bạn ngay lập tức nhận được gói phản hồi hoàn chỉnh.

+1

Cảm ơn bạn đã nhận xét. Tôi đã điều chỉnh mã của tôi khá đáng kể. Tôi hiện đang nhận được phản hồi "nhất quán" hơn, tuy nhiên nó vẫn không phù hợp với ý định của tôi. –

+0

+1 cho "mì không thể tưởng tượng được mã" ... và một câu trả lời hữu ích. – cgmb

+0

Sự cố với việc sử dụng byteTiện ích() là phản hồi không phải là số byte được đặt. Mỗi câu trả lời có thể có độ dài thay đổi tùy thuộc vào loại và định dạng của tin nhắn được gửi đến thiết bị. –

Các vấn đề liên quan