2013-02-27 19 views
6

Seri bağlantı noktam aracılığıyla bir cihaza (yazma) bayt gönderiyorum. Aygıt IO desteğini başlatmak için QSerialPort (http://qt-project.org/wiki/QtSerialPort) modülünü kullanıyorum. INSTEON modemime (seri) mesaj gönderdiğimde mesajım okunduğunda cihaz + 0x06 (ACK Byte) mesajımın bir kopyasını ve ardından bir durum mesajı gönderir. Qt Seri Bağlantı Noktası - Sürekli olarak veri okumak

Mesajımı DockLight (http://www.docklight.de/) kullanarak test ettim. Ben cihazın durumunu sorgulamak için aşağıdaki iletiyi göndermek:

02 62 1D E9 4B 05 19 00 

Docklight kullanarak, yanıt alırsınız:

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

döndürülen mesajı Cihaz üzerinde olduğunu, ne beklediğiniz tam gösterir . Kapalıysa, modem kapalıysa, son bayt konumuna 0x00 geri gönderir. Şimdi, benim sorunum - Yanıtlama baytlarını göndermek ve almak için doğru şekilde ayarlamam gerekir.

Kur sinyal yuvası bağlantıları: Şu anda aşağıdaki kullanıyorum, pek çok farklı örnekler ve yapılandırmaları çalıştık cihazların QList arasında yineleme için kullanılan

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

İşlevini. Cihazın türü ("Işık") isteniyorsa, cihaz kimliğini amaçlanan QByteArray mesaj yapısına göre biçimlendiririz. İleti göndermek için iletiye geçirin. (QSerialPort BlockingMaster örneğin modifiye parçacığı yerel iplik değişkenleri ve yürütür (çalışır) iplik ayarlamak için kullanılan

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 fonksiyonu

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 Fonksiyonu -.. Gönderme ve

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(); 
} 
alıcı Saplı handleResponse işlevi, yanıt sinyali alan SLOT

Burada

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" 

İki sorunları:

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

aşağıdaki çıktıyı alırsınız.

  1. İkinci aygıtla ilgili hiçbir yanıt almadım (Bedroom Light), ikinci gönderilen mesaj budur. Gönderme engelleniyormuş gibi görünüyor, ilk gönderim için yanıt alındıktan sonra gönderim yapmak için gönderimi biçimlendirmeyi nasıl öneriyorsunuz? Göndermek/göndermek için kullanılabilecek sadece 1 COM bağlantı noktası vardır. Cihaz 1'e mesaj göndermem, Cihaz 1 yanıtı al, Cihaz 2'ye gönder, Cihaz 2'yi almam gerektiğine inanıyorum. Çok fazla cihazla büyük bir trafik sıkışıklığı ve bekletme koşulları kullanarak sonuçlanabilir miyim? Cihaz 1 için işlemi başlatmadan önce cihaz 1 iletişim sürecini bitirmek için bekleyiniz mi?

  2. İlk okuma, almanın uygun 1. yarısını içerir.İkinci almak Read: "026220cbcf05190006" ikinci tepkinin 1. yarıda ardından 1 tepkinin 2 yarısını içerir: 2 - Oku

uygun tam yanıt 02621DE94B05190006 025020CBCF1EDAF72100FF olduğunu Read: "025020cbcf1edaf721000002621de94b05190006" (tam yanıt örnekte Cihaz 2'nin kimliği 20CBCF not ise)

Seri bağlantı noktasından veri alma şeklim için hangi düzeltmeler yapılmalıdır? Teşekkür ederiz!

cevap

3

Sorunlarımın bu sorunun kapsamından ayrıldığına inanıyorum. Kuzulis'in yardımıyla, seri mesajları tutarlı bir şekilde göndermek ve okumak için Yaz/Oku fonksiyonlarını uyguladım. Kuzulis, Eşzamanlı engelleme iletişim kalıbını kullanmasını önerdi, ancak daha sonra, Eşzamansız Engelleme olmayan yöntemin uygulamam için en uygun olduğuna karar verildi.

Uygulamam, QSerialPort kaynak dosyalarıyla sağlanan "Master" örneğini yakından takip ediyor.

Device nesnelerini içeren bir QList boyunca yinelemek için CurrentStatus kullanıyorum. Cihaz listesindeki her bir Işık için, cihazın mevcut durumunu (ON/OFF) sorgulamak için 8 Byte mesaj formatlıyorum. emit writeRequest(msg); denir hazırlanmıştır currentStatus göndermek için mesajdan sonra

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

: Aygıt sınıfı yapıcısında

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

, bir sinyal ve yuvaları bağlayın. Bu, writeRequest yuvasına bağlı bir sinyal gönderir. writeRequest, mesajın seri bağlantı noktasına ayarlanması ve yazılması için kullanılır.

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

seri port kurduktan sonra msgRequest geçerli mesajı kaydedin. Bu, bir hata olduğunda iletiyi yeniden göndermek için kullanılabilir. serial.write() den sonra, 400ms için bir zamanlayıcı ayarlıyorum. Bu zamanlayıcının süresi dolduğunda, seri bağlantı noktasından neyin okunduğunu kontrol ederim.

handleResponse(), her zaman QSerialPort'un readyRead() sinyalini yayar adlı bir yuvadır. readyRead(), kullanılabilir verileri QByteArray response'a ekler. 400 ms sonra

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

, serialTimer bir timeout() sinyal yayacaktır (bir Timer çekim). İstenen iletiyi seri bağlantı noktasına yazdıktan hemen sonra serialTimer başlatıldı. processTimeout(), nihayet mesajımızı gönderdikten sonra PowerLinc Modem'den alınan yanıtı kontrol ettiğimiz yerdir. Mesajlar INSTEON PowerLinc Modem'e (PLM) gönderildiğinde, PLM mesajı tekrar ekler ve 0x06'yı (Pozitif ACK) veya 0x15'i (NACK) ekler. processTimeout()'da, en son gönderilen iletinin ACK bayt olduğundan emin olmak için işaretliyim.

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

ben yazma doğrulamak ve seri port üzerinde işlem okumak için Seri Port Monitor 4,0 (Eltima Software) kullanılır. Aşağıda, 1 örnek işlem için günlük çıktısını görebilirsiniz.

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 gönderim için aynı yanıtı aldım. Bu nedenle, tutarsız veri gelişiyle ilgili sorunlarımı çözdüğümü rahatlıkla söyleyebilirim. Şimdi çoklu yazma talepleriyle uğraşıyorum ama bunun araştırılması gereken ayrı bir soru olduğuna inanıyorum. Herkesin desteğini takdir ediyorum.

3
  1. Depodaki BlockingMaster örneğine bakın ve engelleme G/Ç ile ilgili belgeleri okuyun. Ayrıca, gereksiz yere G/Ç engelleme kullanmayın.

  2. Okunacak veri sayısını almak için bytesAvailable() öğesini kullanın; çünkü tam bir yanıt paketi aldığınızdan değil.

+1

Yorumlarınız için teşekkür ederiz. Kodumu oldukça önemli ölçüde ayarladık. Şimdi daha fazla "tutarlı" yanıt alıyorum, ancak yine de niyetimle uyumlu değil. –

+0

+1 "düşünülemez erişte kodu" ... ve yararlı bir cevap. – cgmb

+0

bytesAvailable() kullanılarak elde edilen sorun, yanıtın belirli sayıda bayt olmamasıdır. Her cevap, cihaza gönderilen mesajın türüne ve formatına bağlı olarak değişken uzunluk olabilir. –