0

我正在开发一个多线程应用程序,我需要通过 modbus 实例化 n 个设备。所以我创建了一个控制器(ServiceSM)来实例化 N 个线程(ServiceSlots)。

设备多种多样,因此我必须为每种类型的设备创建“驱动程序”,其中一个驱动程序使用 QModbusClient 类,因此我创建了一个控制器来管理设备类型。

图式

为了测试状态机的操作和与设备的连接,我制作了一个示例代码以在图形界面中运行。

我删除了一些无关紧要的代码片段,以便更容易理解


在 MD4040driver 类中,当我的代码运行此部分时,会出现以下消息。如果我在图形界面中实例化 DeviceDriver 类,它可以完美运行,当我在线程中实例化它时会出现问题。

打电话时

modbusDevice->connectDevice()

MD4040drive::sm_conn() - 尝试连接 - 这是我的消息错误:

QObject::connect: 无法对“QModbusDevice::State”类型的参数进行排队(确保使用 qRegisterMetaType() 注册了“QModbusDevice::State”。)

QObject:无法为不同线程中的父级创建子级。(Parent是QTcpSocket(0x24a6ce8),parent的线程是ServiceSlots(0xea66488),当前线程是QThread(0x2418a78)

QObject:无法为不同线程中的父级创建子级。(Parent是QTcpSocket(0x24a6ce8),parent的线程是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();
}

服务槽.h 服务槽.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);
}

设备驱动程序.h 设备驱动程序.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. ServiceSlotsDeviceDriver类似乎是不必要的。

  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 回答