我正在开发一个多线程应用程序,我需要通过modbus实例化n个设备。 所以我创建了一个实例化N个线程(ServiceSlots)的控制器(ServiceSM)。
设备各不相同所以我必须创建"驱动程序"对于每种类型的设备,其中一个驱动程序使用QModbusClient类,因此我创建了一个控制器来管理设备类型。
为了测试状态机的操作和与设备的连接,我制作了一个示例代码,以便在图形界面中运行。
我删除了一些无关紧要的代码片段,以便于理解
在MD4040driver类中 当我的代码运行此部分时,将显示以下消息。 如果我在图形界面中实例化DeviceDriver类,它可以很好地工作,当我在一个线程中实例化它时会出现问题。
通话时
modbusDevice->connectDevice()
MD4040drive :: sm_conn() - 尝试连接 - 这是我的消息 错误:
QObject :: connect:无法对类型的参数进行排队 ' QModbusDevice ::国家' (确保' QModbusDevice :: State'已注册 使用qRegisterMetaType()。)
QObject:无法为不同的父级创建子级 线。 (Parent是QTcpSocket(0x24a6ce8),父亲的线程是 ServiceSlots(0xea66488),当前线程是QThread(0x2418a78)
QObject:无法为不同的父级创建子级 线。 (Parent是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();
}
}
答案 0 :(得分:2)
有一些问题:
您需要在state 3
中致电qRegisterMetaType<QModbusDevice::State>()
。
您需要维护可能作为一个组移动到其他线程的所有对象之间的父子关系。
main()
和ServiceSlots
类似乎没必要。
无处不在的DeviceDriver
是非惯用的C ++。除非您需要从局部变量中消除成员的歧义,否则不要写this->
。
如果对象的生命周期与父对象相同,则倾向于按值保存对象。让编译器为您生成内存管理代码!
利用C ++ 11。
所有的拳头,让我们有一个帮助器this->
类,它为我们提供了一个可以随时安全地破坏的线程:
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
以这种方式完成后,您不需要任何无关的辅助对象,也不需要定时器来对请求进行排队。 Qt处理所有这些。
将Qt值类传递给函数时,通过const引用传递它们,而不是通过指针传递它们。 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();
}
应该大致如下:
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();
}
}
答案 1 :(得分:0)
确保&#39; QModbusDevice :: State&#39;使用qRegisterMetaType()
注册
意味着在连接将在线程之间传递此类参数的signal / slot之前,需要调用qRegisterMetaType<QModbusDevice::State>();
。
和/或在全局范围内添加Q_DECLARE_METATYPE(QModbusDevice::State)
宏(永远不清楚实际需要哪一个,确保两个工作......)。
有关详情,请参阅此帖子:Emitting signals with custom types does not work