@@ -30,6 +30,7 @@ SOURCES += \ | |||
main.cpp \ | |||
mainwindow.cpp \ | |||
modbusmanager.cpp \ | |||
modbusworker.cpp \ | |||
mygraphicsview.cpp \ | |||
myscene.cpp \ | |||
plc.cpp \ | |||
@@ -48,6 +49,7 @@ HEADERS += \ | |||
light.h \ | |||
mainwindow.h \ | |||
modbusmanager.h \ | |||
modbusworker.h \ | |||
mygraphicsview.h \ | |||
myscene.h \ | |||
plc.h \ | |||
@@ -1,6 +1,6 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<!DOCTYPE QtCreatorProject> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-13T11:12:10. --> | |||
<!-- Written by QtCreator 4.11.1, 2025-08-13T17:04:04. --> | |||
<qtcreator> | |||
<data> | |||
<variable>EnvironmentId</variable> | |||
@@ -8,6 +8,10 @@ int main(int argc, char *argv[]) | |||
// 设置应用程序信息 | |||
QApplication::setApplicationName("PLC梯形图编辑器"); | |||
QApplication::setApplicationVersion("1.0"); | |||
qRegisterMetaType<QSerialPort::BaudRate>("QSerialPort::BaudRate"); | |||
qRegisterMetaType<QSerialPort::DataBits>("QSerialPort::DataBits"); | |||
qRegisterMetaType<QSerialPort::Parity>("QSerialPort::Parity"); | |||
qRegisterMetaType<QSerialPort::StopBits>("QSerialPort::StopBits"); | |||
MainWindow w; | |||
w.resize(1000, 600); | |||
@@ -13,6 +13,7 @@ | |||
#include <QAction> | |||
#include <QFileDialog> | |||
#include "creatitem.h" | |||
#include <QDebug> | |||
MainWindow::MainWindow(QWidget *parent) | |||
: QMainWindow(parent) | |||
@@ -56,6 +57,12 @@ MainWindow::MainWindow(QWidget *parent) | |||
connect(modbusManager, &ModbusManager::connectionStatusChanged, | |||
this, &MainWindow::updateConnectionStatus); | |||
connect(modbusManager, &ModbusManager::registerValueUpdated, | |||
registerManager, &RegisterManager::updateRegisterValue); | |||
connect(modbusManager, &ModbusManager::errorOccurred, | |||
this, [this](const QString& error) { | |||
ui->textEdit->append("错误: " + error); | |||
}); | |||
// connect(modbusManager, &ModbusManager::connectionStatusChanged, | |||
// this, &PLC::updateConnectionStatus); | |||
// connect(modbusManager, &ModbusManager::errorOccurred, | |||
@@ -1,58 +1,53 @@ | |||
#include "modbusmanager.h" | |||
#include <QModbusDataUnit> | |||
#include <QDebug> | |||
#include <QThread> | |||
ModbusManager::ModbusManager(RegisterManager* regManager, QObject *parent) | |||
: QObject(parent), registerManager(regManager) | |||
: QObject(parent) | |||
{ | |||
modbusDevice = new QModbusRtuSerialMaster(this); | |||
modbusThread = new QThread(this); | |||
worker = new ModbusWorker(regManager); | |||
pollTimer = new QTimer(this); | |||
worker->moveToThread(modbusThread); | |||
connect(modbusDevice, &QModbusClient::errorOccurred, this, | |||
[this](QModbusDevice::Error) { | |||
emit errorOccurred(modbusDevice->errorString()); | |||
}); | |||
// 连接工作线程信号 | |||
connect(worker, &ModbusWorker::connectionStatusChanged, | |||
this, &ModbusManager::connectionStatusChanged); | |||
connect(worker, &ModbusWorker::errorOccurred, | |||
this, &ModbusManager::errorOccurred); | |||
connect(worker, &ModbusWorker::registerValueUpdated, | |||
this, &ModbusManager::registerValueUpdated); | |||
connect(pollTimer, &QTimer::timeout, this, &ModbusManager::readRegisters); | |||
connect(modbusThread, &QThread::started, worker, &ModbusWorker::initModbusDevice); | |||
modbusThread->start(); | |||
} | |||
ModbusManager::~ModbusManager() | |||
{ | |||
stopSimulation(); | |||
disconnectDevice(); | |||
modbusThread->quit(); | |||
modbusThread->wait(); | |||
delete worker; | |||
pollTimer->stop(); | |||
} | |||
bool ModbusManager::connectToDevice(const QString& portName, | |||
QSerialPort::BaudRate baudRate, | |||
QSerialPort::DataBits dataBits, | |||
QSerialPort::Parity parity, | |||
QSerialPort::StopBits stopBits) | |||
void ModbusManager::connectToDevice(const QString& portName, | |||
QSerialPort::BaudRate baudRate, | |||
QSerialPort::DataBits dataBits, | |||
QSerialPort::Parity parity, | |||
QSerialPort::StopBits stopBits) | |||
{ | |||
if (modbusDevice->state() != QModbusDevice::UnconnectedState) { | |||
disconnectDevice(); | |||
} | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, portName); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudRate); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, parity); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits); | |||
if (!modbusDevice->connectDevice()) { | |||
emit errorOccurred(tr("连接失败: %1").arg(modbusDevice->errorString())); | |||
return false; | |||
} | |||
emit connectionStatusChanged(true); | |||
return true; | |||
QMetaObject::invokeMethod(worker, "connectToDevice", Qt::QueuedConnection, | |||
Q_ARG(QString, portName), | |||
Q_ARG(QSerialPort::BaudRate, baudRate), | |||
Q_ARG(QSerialPort::DataBits, dataBits), | |||
Q_ARG(QSerialPort::Parity, parity), | |||
Q_ARG(QSerialPort::StopBits, stopBits)); | |||
} | |||
void ModbusManager::disconnectDevice() | |||
{ | |||
if (modbusDevice->state() != QModbusDevice::UnconnectedState) { | |||
modbusDevice->disconnectDevice(); | |||
emit connectionStatusChanged(false); | |||
} | |||
QMetaObject::invokeMethod(worker, "disconnectDevice", Qt::QueuedConnection); | |||
} | |||
void ModbusManager::startSimulation(int interval) | |||
@@ -65,123 +60,14 @@ void ModbusManager::stopSimulation() | |||
pollTimer->stop(); | |||
} | |||
void ModbusManager::readRegisters() | |||
{ | |||
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState) | |||
return; | |||
// 获取所有已注册的寄存器ID | |||
QStringList regIds = registerManager->getAllRegisteredRegisters(); | |||
QMap<QString, QSet<int>> regMap; // 按类型分组存储地址 | |||
// 分组寄存器地址 | |||
for (const QString &id : regIds) { | |||
if (id.startsWith("D") || id.startsWith("M")) { | |||
bool ok; | |||
int addr = id.mid(1).toInt(&ok); | |||
if (ok && addr >= 0 && addr <= 4000) { | |||
regMap[id.left(1)].insert(addr); | |||
} | |||
} | |||
} | |||
// 读取D寄存器 (保持寄存器) | |||
if (regMap.contains("D")) { | |||
readRegisterGroup(QModbusDataUnit::HoldingRegisters, regMap["D"].values()); | |||
} | |||
// 读取M寄存器 (线圈) | |||
if (regMap.contains("M")) { | |||
readRegisterGroup(QModbusDataUnit::Coils, regMap["M"].values()); | |||
} | |||
} | |||
void ModbusManager::readRegisterGroup(QModbusDataUnit::RegisterType type, const QList<int> &addresses) | |||
{ | |||
if (addresses.isEmpty()) return; | |||
QList<int> sortedAddrs = addresses; | |||
std::sort(sortedAddrs.begin(), sortedAddrs.end()); | |||
// 合并连续地址范围 | |||
int start = sortedAddrs.first(); | |||
int end = start; | |||
for (int i = 1; i < sortedAddrs.size(); i++) { | |||
if (sortedAddrs[i] == end + 1) { | |||
end = sortedAddrs[i]; | |||
} else { | |||
readAddressRange(type, start, end - start + 1); | |||
start = sortedAddrs[i]; | |||
end = start; | |||
} | |||
} | |||
readAddressRange(type, start, end - start + 1); | |||
} | |||
void ModbusManager::readAddressRange(QModbusDataUnit::RegisterType type, int startAddr, int count) | |||
{ | |||
const int MAX_REG_PER_REQUEST = 120; | |||
for (int offset = 0; offset < count; offset += MAX_REG_PER_REQUEST) { | |||
int readCount = qMin(MAX_REG_PER_REQUEST, count - offset); | |||
QModbusDataUnit unit(type, startAddr + offset, readCount); | |||
if (auto *reply = modbusDevice->sendReadRequest(unit, slaveAddress)) { | |||
QString regType = (type == QModbusDataUnit::HoldingRegisters) ? "D" : "M"; | |||
pendingRequests[reply] = regType; | |||
connect(reply, &QModbusReply::finished, this, &ModbusManager::processModbusReply); | |||
} | |||
} | |||
} | |||
void ModbusManager::processModbusReply() | |||
void ModbusManager::writeRegister(const QString& registerId, quint16 value) | |||
{ | |||
auto *reply = qobject_cast<QModbusReply*>(sender()); | |||
if (!reply) return; | |||
if (reply->error() == QModbusDevice::NoError) { | |||
const QModbusDataUnit unit = reply->result(); | |||
QString regType = pendingRequests.value(reply); | |||
int startAddr = unit.startAddress(); | |||
for (uint i = 0; i < unit.valueCount(); i++) { | |||
int regAddr = startAddr + i; | |||
quint16 value = unit.value(i); | |||
QString regId = QString("%1%2").arg(regType).arg(regAddr); | |||
// 更新图元状态 | |||
registerManager->updateRegisterValue(regId, value); | |||
} | |||
} else { | |||
emit errorOccurred(tr("Modbus错误: %1").arg(reply->errorString())); | |||
} | |||
pendingRequests.remove(reply); | |||
reply->deleteLater(); | |||
QMetaObject::invokeMethod(worker, "writeRegister", Qt::QueuedConnection, | |||
Q_ARG(QString, registerId), | |||
Q_ARG(quint16, value)); | |||
} | |||
void ModbusManager::writeRegister(const QString& registerId, quint16 value) | |||
void ModbusManager::readRegisters() | |||
{ | |||
if (registerId.isEmpty()) return; | |||
QModbusDataUnit::RegisterType regType = registerId.startsWith("D") | |||
? QModbusDataUnit::HoldingRegisters | |||
: QModbusDataUnit::Coils; | |||
int regAddr = registerId.mid(1).toInt(); | |||
QModbusDataUnit writeUnit(regType, regAddr, 1); | |||
writeUnit.setValue(0, value); | |||
if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, slaveAddress)) { | |||
connect(reply, &QModbusReply::finished, this, [this, reply]() { | |||
if (reply->error() != QModbusDevice::NoError) { | |||
emit errorOccurred(tr("写入失败: %1").arg(reply->errorString())); | |||
} | |||
reply->deleteLater(); | |||
}); | |||
} | |||
QMetaObject::invokeMethod(worker, "readRegisters", Qt::QueuedConnection); | |||
} |
@@ -2,11 +2,10 @@ | |||
#define MODBUSMANAGER_H | |||
#include <QObject> | |||
#include <QModbusRtuSerialMaster> | |||
#include <QSerialPort> | |||
#include <QTimer> | |||
#include <QMap> | |||
#include "registermanager.h" | |||
#include "modbusworker.h" | |||
class RegisterManager; | |||
class QThread; | |||
class ModbusManager : public QObject | |||
{ | |||
@@ -16,7 +15,7 @@ public: | |||
~ModbusManager(); | |||
// 串口连接管理 | |||
bool connectToDevice(const QString& portName, | |||
void connectToDevice(const QString& portName, | |||
QSerialPort::BaudRate baudRate = QSerialPort::Baud9600, | |||
QSerialPort::DataBits dataBits = QSerialPort::Data8, | |||
QSerialPort::Parity parity = QSerialPort::NoParity, | |||
@@ -32,24 +31,20 @@ public: | |||
void writeRegister(const QString& registerId, quint16 value); | |||
// 设置从站地址 | |||
void setSlaveAddress(int address) { slaveAddress = address; } | |||
// void setSlaveAddress(int address) { worker->setSlaveAddress(address); } | |||
signals: | |||
void connectionStatusChanged(bool connected); | |||
void errorOccurred(const QString& error); | |||
void registerValueUpdated(const QString& registerId, quint16 value); | |||
private slots: | |||
public slots: | |||
void readRegisters(); | |||
void processModbusReply(); | |||
void readRegisterGroup(QModbusDataUnit::RegisterType type, const QList<int> &addresses); | |||
void readAddressRange(QModbusDataUnit::RegisterType type, int startAddr, int count); | |||
private: | |||
QModbusRtuSerialMaster *modbusDevice; | |||
RegisterManager* registerManager; | |||
ModbusWorker* worker; | |||
QTimer* pollTimer; | |||
int slaveAddress = 1; // 默认从站地址 | |||
QMap<QModbusReply*, QString> pendingRequests; | |||
QThread* modbusThread; | |||
}; | |||
#endif // MODBUSMANAGER_H |
@@ -0,0 +1,184 @@ | |||
#include "modbusworker.h" | |||
#include <QModbusDataUnit> | |||
#include <QDebug> | |||
#include <QSerialPort> | |||
ModbusWorker::ModbusWorker(RegisterManager* regManager, QObject *parent) | |||
: QObject(parent), registerManager(regManager) | |||
{ | |||
// modbusDevice = new QModbusRtuSerialMaster(this); | |||
// connect(modbusDevice, &QModbusClient::errorOccurred, this, | |||
// [this](QModbusDevice::Error) { | |||
// emit errorOccurred(modbusDevice->errorString()); | |||
// }); | |||
} | |||
ModbusWorker::~ModbusWorker() | |||
{ | |||
disconnectDevice(); | |||
} | |||
bool ModbusWorker::connectToDevice(const QString& portName, | |||
QSerialPort::BaudRate baudRate, | |||
QSerialPort::DataBits dataBits, | |||
QSerialPort::Parity parity, | |||
QSerialPort::StopBits stopBits) | |||
{ | |||
if (!modbusDevice) { // 新增检查 | |||
emit errorOccurred("Modbus设备未初始化"); | |||
return false; | |||
} | |||
if (modbusDevice->state() != QModbusDevice::UnconnectedState) { | |||
disconnectDevice(); | |||
} | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, portName); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudRate); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, parity); | |||
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits); | |||
if (!modbusDevice->connectDevice()) { | |||
emit errorOccurred(tr("连接失败: %1").arg(modbusDevice->errorString())); | |||
return false; | |||
} | |||
emit connectionStatusChanged(true); | |||
return true; | |||
} | |||
void ModbusWorker::disconnectDevice() | |||
{ | |||
if (modbusDevice->state() != QModbusDevice::UnconnectedState) { | |||
modbusDevice->disconnectDevice(); | |||
emit connectionStatusChanged(false); | |||
} | |||
} | |||
void ModbusWorker::initModbusDevice() | |||
{ | |||
modbusDevice = new QModbusRtuSerialMaster(this); | |||
connect(modbusDevice, &QModbusClient::errorOccurred, this, | |||
[this](QModbusDevice::Error) { | |||
emit errorOccurred(modbusDevice->errorString()); | |||
}); | |||
} | |||
void ModbusWorker::readRegisters() | |||
{ | |||
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState) | |||
return; | |||
// 获取所有已注册的寄存器ID | |||
QStringList regIds = registerManager->getAllRegisteredRegisters(); | |||
QMap<QString, QSet<int>> regMap; | |||
for (const QString &id : regIds) { | |||
if (id.startsWith("D") || id.startsWith("M")) { | |||
bool ok; | |||
int addr = id.mid(1).toInt(&ok); | |||
if (ok && addr >= 0 && addr <= 4000) { | |||
regMap[id.left(1)].insert(addr); | |||
} | |||
} | |||
} | |||
if (regMap.contains("D")) { | |||
readRegisterGroup(QModbusDataUnit::HoldingRegisters, regMap["D"].values()); | |||
} | |||
if (regMap.contains("M")) { | |||
readRegisterGroup(QModbusDataUnit::Coils, regMap["M"].values()); | |||
} | |||
} | |||
void ModbusWorker::readRegisterGroup(QModbusDataUnit::RegisterType type, const QList<int> &addresses) | |||
{ | |||
if (addresses.isEmpty()) return; | |||
QList<int> sortedAddrs = addresses; | |||
std::sort(sortedAddrs.begin(), sortedAddrs.end()); | |||
int start = sortedAddrs.first(); | |||
int end = start; | |||
for (int i = 1; i < sortedAddrs.size(); i++) { | |||
if (sortedAddrs[i] == end + 1) { | |||
end = sortedAddrs[i]; | |||
} else { | |||
readAddressRange(type, start, end - start + 1); | |||
start = sortedAddrs[i]; | |||
end = start; | |||
} | |||
} | |||
readAddressRange(type, start, end - start + 1); | |||
} | |||
void ModbusWorker::readAddressRange(QModbusDataUnit::RegisterType type, int startAddr, int count) | |||
{ | |||
const int MAX_REG_PER_REQUEST = 120; | |||
for (int offset = 0; offset < count; offset += MAX_REG_PER_REQUEST) { | |||
int readCount = qMin(MAX_REG_PER_REQUEST, count - offset); | |||
QModbusDataUnit unit(type, startAddr + offset, readCount); | |||
if (auto *reply = modbusDevice->sendReadRequest(unit, slaveAddress)) { | |||
QString regType = (type == QModbusDataUnit::HoldingRegisters) ? "D" : "M"; | |||
pendingRequests[reply] = regType; | |||
connect(reply, &QModbusReply::finished, this, &ModbusWorker::processModbusReply); | |||
} | |||
} | |||
} | |||
void ModbusWorker::processModbusReply() | |||
{ | |||
auto *reply = qobject_cast<QModbusReply*>(sender()); | |||
if (!reply) return; | |||
if (reply->error() == QModbusDevice::NoError) { | |||
const QModbusDataUnit unit = reply->result(); | |||
QString regType = pendingRequests.value(reply); | |||
int startAddr = unit.startAddress(); | |||
for (uint i = 0; i < unit.valueCount(); i++) { | |||
int regAddr = startAddr + i; | |||
quint16 value = unit.value(i); | |||
QString regId = QString("%1%2").arg(regType).arg(regAddr); | |||
emit registerValueUpdated(regId, value); | |||
} | |||
} else { | |||
emit errorOccurred(tr("Modbus错误: %1").arg(reply->errorString())); | |||
} | |||
pendingRequests.remove(reply); | |||
reply->deleteLater(); | |||
} | |||
void ModbusWorker::writeRegister(const QString& registerId, quint16 value) | |||
{ | |||
if (!modbusDevice) { | |||
emit errorOccurred("Modbus设备未初始化"); | |||
return; | |||
} | |||
if (registerId.isEmpty()) return; | |||
QModbusDataUnit::RegisterType regType = registerId.startsWith("D") | |||
? QModbusDataUnit::HoldingRegisters | |||
: QModbusDataUnit::Coils; | |||
int regAddr = registerId.mid(1).toInt(); | |||
QModbusDataUnit writeUnit(regType, regAddr, 1); | |||
writeUnit.setValue(0, value); | |||
if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, slaveAddress)) { | |||
connect(reply, &QModbusReply::finished, this, [this, reply]() { | |||
if (reply->error() != QModbusDevice::NoError) { | |||
emit errorOccurred(tr("写入失败: %1").arg(reply->errorString())); | |||
} | |||
reply->deleteLater(); | |||
}); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
#ifndef MODBUSWORKER_H | |||
#define MODBUSWORKER_H | |||
#include <QObject> | |||
#include <QTimer> | |||
#include <QModbusRtuSerialMaster> | |||
#include <QSerialPort> | |||
#include "registermanager.h" | |||
class ModbusWorker : public QObject | |||
{ | |||
Q_OBJECT | |||
public: | |||
explicit ModbusWorker(RegisterManager* regManager, QObject *parent = nullptr); | |||
~ModbusWorker(); | |||
public slots: | |||
// void startSimulation(int interval = 500); | |||
// void stopSimulation(); | |||
void writeRegister(const QString& registerId, quint16 value); | |||
bool connectToDevice(const QString& portName, | |||
QSerialPort::BaudRate baudRate, | |||
QSerialPort::DataBits dataBits, | |||
QSerialPort::Parity parity, | |||
QSerialPort::StopBits stopBits); | |||
void disconnectDevice(); | |||
void initModbusDevice(); | |||
private slots: | |||
void readRegisters(); | |||
void processModbusReply(); | |||
void readRegisterGroup(QModbusDataUnit::RegisterType type, const QList<int> &addresses); | |||
void readAddressRange(QModbusDataUnit::RegisterType type, int startAddr, int count); | |||
signals: | |||
void connectionStatusChanged(bool connected); | |||
void errorOccurred(const QString& error); | |||
void registerValueUpdated(const QString& registerId, quint16 value); | |||
private: | |||
QModbusRtuSerialMaster *modbusDevice = nullptr; | |||
RegisterManager* registerManager; | |||
int slaveAddress = 1; | |||
QMap<QModbusReply*, QString> pendingRequests; | |||
}; | |||
#endif // MODBUSWORKER_H |