0

マルチスレッド アプリケーションを開発しています。modbus で n 個のデバイスをインスタンス化する必要があります。そこで、N 個のスレッド (ServiceSlots) をインスタンス化するコントローラー (ServiceSM) を作成しました。

デバイスはさまざまなので、デバイスの種類ごとに「ドライバー」を作成する必要がありました。ドライバーの 1 つが QModbusClient クラスを使用するため、デバイスの種類を管理するコントローラーを作成しました。

スキーマ

ステート マシンの動作とデバイスへの接続をテストするために、グラフィカル インターフェイスで実行するサンプル コードを作成しました。

分かりやすくするために重要でないコードのスニペットをいくつか削除しました


MD4040driver クラスのコードでこのセクションを実行すると、次のメッセージが表示されます。グラフィカル インターフェイスで DeviceDriver クラスをインスタンス化すると、完全に機能します。スレッド内でインスタンス化すると問題が発生します。

通話時

modbusDevice->connectDevice()

MD4040drive::sm_conn() - 接続してみてください - これは私のメッセージです エラー:

QObject::connect: タイプ 'QModbusDevice::State' の引数をキューに入れることができません (qRegisterMetaType() を使用して 'QModbusDevice::State' が登録されていることを確認してください)。

QObject: 別のスレッドにある親の子を作成できません。(親は QTcpSocket(0x24a6ce8)、親のスレッドは ServiceSlots(0xea66488)、現在のスレッドは QThread(0x2418a78)

QObject: 別のスレッドにある親の子を作成できません。(親は QTcpSocket(0x24a6ce8)、親のスレッドは ServiceSlots(0xea66488)、現在のスレッドは QThread(0x2418a78)

void MD4040drive::sm_conn()
{

    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
        modbusDevice->setTimeout( this->cfg.modbus.timeout );
        modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
        qDebug() << "MD4040drive::sm_conn() - try connect";
        if (!modbusDevice->connectDevice()) {
            qDebug()  << "Erro: " << modbusDevice->errorString();
        } else {
            qDebug()  << "Aguardando conexão...";
        }
    }
    else{
        //already connected...
        this->getDados_NO_TH();
    }
}

私のコード(部分)を休ませる


devicedriverviewgui.h devicedriverviewgui.cpp

class DeviceDriverViewGUI : public QDialog
{
    Q_OBJECT
public:
    explicit DeviceDriverViewGUI(QWidget *parent = 0);
    ~DeviceDriverViewGUI();
private slots:
    void on_pbTry_clicked();

private:
    Ui::DeviceDriverViewGUI *ui;
    ServiceSlots *serviceSlot;

};

void DeviceDriverViewGUI::on_pbTry_clicked()
{
    Equip equip_try = Equip();

    serviceSlot = new ServiceSlots();        
    serviceSlot->setEquipamento(equip_try);
    serviceSlot->start();
}

serviceslots.h serviceslots.cpp

class ServiceSlots : public QThread
{
     Q_OBJECT
public:
    ServiceSlots();
    void run();
private:
    QTimer *timer;
    DeviceDriver *device;

private slots:
    void sm_getData();
    void device_response(bool boEnd);
};

void ServiceSlots::run()
{
    int e;
    eventLoop = new QEventLoop();
    timer = new QTimer();
    connect(timer, SIGNAL(timeout()),this, SLOT(sm_controler()));
    timer->start(TICK_SM_SLOT);
    this->device = new DeviceDriver();
    e = eventLoop->exec();
    qDebug() << "Exit loop"<< e;
}

void ServiceSlots::sm_controler()
{
    if(this->idleState){;}
    else{
        this->sm_getData();
        this->idleState = true;
    }
}

void ServiceSlots::sm_getData()
{
    connect(device,SIGNAL(end(bool)),this,SLOT(device_response(bool)));
    device->requestDeviceDriver(&this->equipamento,&this->next_date);
}

devicedriver.h devicedriver.cpp

class DeviceDriver : public QObject
{
    Q_OBJECT
public:
    DeviceDriver();
    void requestDeviceDriver(Equip *equip,QDateTime *date);
private:
    //Drivers de dispositivos..
    MD4040drive *md4040;
private slots:
    //Request data to driver...
    void request();
signals:
    void end(bool boEnd);
};

void DeviceDriver::request()
{
    connect(md4040,SIGNAL(end(bool)),this,SLOT(md4040_end(bool)));
    this->md4040->requestMD4040drive(&this->equip,&this->date);
}

DeviceDriver::DeviceDriver(){
    ----
    md4040 = new MD4040drive();
    ---
}

void DeviceDriver::requestDeviceDriver(Equip *equip, QDateTime *date){
    this->equip = *equip;
    this->date = *date;
    this->request();
}

md4040drive.h md4040drive.cpp

class MD4040drive : public QObject // QObject//public QObject  QRunnable  QThread
{
    Q_OBJECT
public:
    explicit MD4040drive(QObject *parent = 0);
    ~MD4040drive();
    void requestMD4040drive(Equip *equip,QDateTime *date);
private:
    void run();
    QModbusClient *modbusDevice;

private slots:
    void m_conn();
signals:
    void end(bool boRun);

};

MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
    modbusDevice = new QModbusTcpClient();
    connect(modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}

void MD4040drive::requestMD4040drive(Equip *equip, QDateTime *date)
{
    this->equip = *equip;
    this->date = *date;
    this->run();
}


void MD4040drive::run()
{
    this->sm_conn();
}

void MD4040drive::sm_conn()
{

    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, this->cfg.modbus.porta );
        modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, this->cfg.modbus.ip);
        modbusDevice->setTimeout( this->cfg.modbus.timeout );
        modbusDevice->setNumberOfRetries(this->cfg.modbus.retries);
        qDebug() << "MD4040drive::sm_conn() - try connect";
        if (!modbusDevice->connectDevice()) {
            qDebug()  << "Erro: " << modbusDevice->errorString();
        } else {
            qDebug()  << "Aguardando conexão...";
        }
    }
    else{
        //already connected...
        this->getDados_NO_TH();
    }
}
4

2 に答える 2

2

いくつかの問題があります:

  1. qRegisterMetaType<QModbusDevice::State>()電話する必要がありますmain()

  2. グループとして他のスレッドに移動される可能性があるすべてのオブジェクト間の親子関係を維持する必要があります。

  3. ServiceSlotsandDeviceDriverクラスは不要のようです。

  4. どこにでもあるのthis->は非慣用的な C++ です。this->ローカル変数からメンバーを明確にする必要がない限り、記述しないでください。

  5. 親オブジェクトと同じ存続期間を持つ場合は、オブジェクトを値で保持することを好みます。コンパイラにメモリ管理コードを生成させましょう!

  6. C++11 を活用します。

まず第一にSafeThread、いつでも安全に破棄できるスレッドを提供するヘルパー クラスを作成しましょう。

class SafeThread : public QThread {
   Q_OBJECT
   using QThread::run;
public:
   using QThread::QThread;
   ~SafeThread() { quit(); wait(); }
};

クラスは、ドライブとそのDeviceDriverViewGUIスレッドを値で保持できます。

class DeviceDriverViewGUI : public QDialog
{
   Q_OBJECT
public:
   explicit DeviceDriverViewGUI(QWidget *parent = nullptr);

private:
   Ui::DeviceDriverViewGUI ui;
   MD4040drive drive;
   SafeThread driveThread{this};
   Equip equipamento;
   QDateTime nextDate;

   Q_SLOT void on_pbTry_clicked();
};

次に、プッシュボタンをドライブのスレッド コンテキストに直接接続requestMD4040driveし、適切なスレッドで実行できます。

DeviceDriverViewGUI::DeviceDriverViewGUI(QWidget *parent) : QDialog(parent)
{
   ui.setupUi(this);
   //                                       vvvvvv -- gives the thread context
   connect(ui.pbTry, &QPushButton::clicked, &drive, [this]{
      Q_ASSERT(QThread::currentThread() == drive.thread()); // ensured by the thread context
      drive.requestMD4040drive(&equipamento, nextDate);
   });
   connect(&drive, &MD4040drive::end, this, [this](bool end){
      //...
   });
   drive.moveToThread(&driveThread);
   driveThread.start();
}

このようにすると、要求をキューに入れるための余分なヘルパー オブジェクトやタイマーは必要ありません。Qt がすべてを処理します。

Qt 値クラスを関数に渡すときは、ポインターではなく const 参照で渡します。は、MD4040driveおおよそ次のようになります。

class MD4040drive : public QObject
{
   Q_OBJECT
public:
   explicit MD4040drive(QObject *parent = nullptr);
   void requestMD4040drive(Equip *equip, const QDateTime &date);
      Q_SIGNAL void end(bool boRun);
private:
   Equip *equip = nullptr;
   QDateTime date;
   QModbusTcpClient modbusDevice{this};
   Cfg cfg;

   Q_SLOT void onStateChanged();
   Q_SLOT void m_conn();
   void sm_conn();
   void getDados_NO_TH() {}
};

実装:

MD4040drive::MD4040drive(QObject *parent): QObject(parent)
{
   connect(&modbusDevice, &QModbusClient::stateChanged,this, &MD4040drive::onStateChanged);
}

void MD4040drive::requestMD4040drive(Equip *equip, const QDateTime &date)
{
   this->equip = equip;
   this->date = date;
   sm_conn();
}

void MD4040drive::sm_conn()
{

   if (modbusDevice.state() != QModbusDevice::ConnectedState) {
      modbusDevice.setConnectionParameter(QModbusDevice::NetworkPortParameter, cfg.modbus.porta );
      modbusDevice.setConnectionParameter(QModbusDevice::NetworkAddressParameter, cfg.modbus.ip);
      modbusDevice.setTimeout( this->cfg.modbus.timeout );
      modbusDevice.setNumberOfRetries(this->cfg.modbus.retries);
      qDebug() << "MD4040drive::sm_conn() - try connect";
      if (!modbusDevice.connectDevice()) {
         qDebug()  << "Erro: " << modbusDevice.errorString();
      } else {
         qDebug()  << "Aguardando conexão...";
      }
   }
   else{
      //already connected...
      getDados_NO_TH();
   }
}

構成クラスは次のようになります。コンパイラが必要なコンストラクタとデストラクタを生成することに注意してください。

struct Cfg {
   struct Modbus {
      int porta = 0;
      QString ip = QStringLiteral("127.0.0.1");
      int timeout = 1000;
      int retries = 2;
   } modbus;
};
于 2016-11-29T22:12:17.450 に答える
0

qRegisterMetaType() を使用して「QModbusDevice::State」が登録されていることを確認してください

qRegisterMetaType<QModbusDevice::State>();スレッド間でこのタイプのパラメーターを渡すシグナル/スロットを接続する前に呼び出す必要があることを意味します。

および/またはグローバルスコープでマクロを追加Q_DECLARE_METATYPE(QModbusDevice::State)します(どちらが実際に必要であるかを明確に理解していないため、両方が確実に機能します...)。

詳細については、この投稿を参照してください:カスタム タイプのシグナルの発行は機能しません

于 2016-11-29T21:41:02.893 に答える