Browse Source

添加寄存器映射类和modbus管理类

master
鹏鹏 李 1 day ago
parent
commit
ead0b485ac
17 changed files with 385 additions and 15 deletions
  1. +14
    -2
      coil.cpp
  2. +1
    -0
      coil.h
  3. +17
    -2
      contact.cpp
  4. +1
    -0
      contact.h
  5. +8
    -2
      editor.pro
  6. +6
    -0
      item.cpp
  7. +6
    -0
      item.h
  8. +1
    -0
      mainwindow.cpp
  9. +4
    -2
      mainwindow.h
  10. +146
    -0
      modbusmanager.cpp
  11. +55
    -0
      modbusmanager.h
  12. +38
    -7
      mygraphicsview.cpp
  13. +3
    -0
      mygraphicsview.h
  14. +10
    -0
      plc.cpp
  15. +3
    -0
      plc.h
  16. +44
    -0
      registermanager.cpp
  17. +28
    -0
      registermanager.h

+ 14
- 2
coil.cpp View File

@@ -11,6 +11,11 @@ void Coil::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid
{
painter->setRenderHint(QPainter::Antialiasing);

if (registerValue_ > 0) {
painter->setBrush(Qt::green); // 激活状态
} else {
painter->setBrush(Qt::black); // 未激活状态
}
if (type_ == "线圈") {
// 绘制线圈样式: 两边线段+中间椭圆
painter->drawLine(-12, 0, -5, 0);
@@ -28,8 +33,10 @@ void Coil::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid
painter->setFont(QFont("Arial", 8));
painter->setPen(Qt::black);
// 在元件底部居中绘制寄存器ID
painter->drawText(boundingRect().adjusted(0, 20, 0, 0),
Qt::AlignCenter, registerId_);
// painter->drawText(boundingRect().adjusted(0, 20, 0, 0),
// Qt::AlignCenter, registerId_);
QString text = QString("%1: %2").arg(registerId()).arg(registerValue_);
painter->drawText(boundingRect(), Qt::AlignBottom | Qt::AlignHCenter, text);
painter->restore();
}
if (option->state & QStyle::State_Selected) {
@@ -41,3 +48,8 @@ void Coil::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWid
painter->drawRect(boundingRect());
}
}

bool Coil::state() const
{
return registerValue_ > 0;
}

+ 1
- 0
coil.h View File

@@ -9,6 +9,7 @@ public:
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *) override;
bool state() const override;
};

#endif // COIL_H

+ 17
- 2
contact.cpp View File

@@ -11,6 +11,11 @@ void Contact::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
{
painter->setRenderHint(QPainter::Antialiasing);
if (type_ == "常开") {
if (state()) {
painter->setPen(QPen(Qt::green, 2)); // 激活状态
} else {
painter->setPen(QPen(Qt::black, 1)); // 未激活状态
}
painter->drawLine(-12, 0, -4, 0);
painter->drawLine(-4, -8, -4, 8);
painter->drawLine(4, -8, 4, 8);
@@ -23,6 +28,11 @@ void Contact::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
painter->drawEllipse(QPointF(18, 0), 4, 4); // 右
}
else if (type_ == "常闭") {
if (!state()) {
painter->setPen(QPen(Qt::green, 2)); // 激活状态
} else {
painter->setPen(QPen(Qt::black, 1)); // 未激活状态
}
painter->drawLine(-15, -10, 15, 10); // 对角线
painter->drawLine(-12, 0, -4, 0);
painter->drawLine(-4, -8, -4, 8);
@@ -41,8 +51,8 @@ void Contact::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
painter->setFont(QFont("Arial", 8));
painter->setPen(Qt::black);
// 在元件底部居中绘制寄存器ID
painter->drawText(boundingRect().adjusted(0, 20, 0, 0),
Qt::AlignCenter, registerId_);
QString text = QString("%1: %2").arg(registerId()).arg(registerValue_);
painter->drawText(boundingRect(), Qt::AlignBottom | Qt::AlignHCenter, text);
painter->restore();
}
if (option->state & QStyle::State_Selected) {
@@ -54,3 +64,8 @@ void Contact::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, Q
painter->drawRect(boundingRect());
}
}

bool Contact::state() const
{
return registerValue_ > 0;
}

+ 1
- 0
contact.h View File

@@ -9,6 +9,7 @@ public:
void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *) override;
bool state() const override;
};

#endif // CONTACT_H

+ 8
- 2
editor.pro View File

@@ -1,4 +1,6 @@
QT += core gui
QT += serialport
QT += serialbus

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

@@ -27,9 +29,11 @@ SOURCES += \
light.cpp \
main.cpp \
mainwindow.cpp \
modbusmanager.cpp \
mygraphicsview.cpp \
plc.cpp \
project.cpp
project.cpp \
registermanager.cpp

HEADERS += \
button.h \
@@ -42,9 +46,11 @@ HEADERS += \
item.h \
light.h \
mainwindow.h \
modbusmanager.h \
mygraphicsview.h \
plc.h \
project.h
project.h \
registermanager.h

FORMS += \
hmi.ui \


+ 6
- 0
item.cpp View File

@@ -58,6 +58,12 @@ QString Item::itemType()
return type_;
}

void Item::setRegisterValue(quint16 value)
{
registerValue_ = value;
update(); // 触发重绘
}

void Item::MenuActions(QMenu *menu)
{
menu->addAction("复制");


+ 6
- 0
item.h View File

@@ -23,6 +23,11 @@ public:
QString itemType();
void setRegisterId(const QString& id) { registerId_ = id; }
QString registerId() const { return registerId_; }
void setRegisterValue(quint16 value);
quint16 registerValue() const { return registerValue_; }

// 添加状态属性(用于线圈、触点等)
virtual bool state() const { return false; }


signals:
@@ -39,6 +44,7 @@ protected:
QString type_;
QList<Connection*> connections_;
QString registerId_;
quint16 registerValue_ = 0;
};

#endif // ITEM_H

+ 1
- 0
mainwindow.cpp View File

@@ -30,6 +30,7 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui->action_save, &QAction::triggered, this, &MainWindow::saveProject);
connect(ui->action_open, &QAction::triggered, this, &MainWindow::openProject);


plc_->applyProjectToScene(plcProject_); // 初始空
hmi_->applyProjectToScene(hmiProject_);
}


+ 4
- 2
mainwindow.h View File

@@ -2,8 +2,10 @@
#define MAINWINDOW_H

#include <QMainWindow>
#include <plc.h>
#include <hmi.h>
#include "plc.h"
#include "hmi.h"
#include "modbusmanager.h"
#include "registermanager.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }


+ 146
- 0
modbusmanager.cpp View File

@@ -0,0 +1,146 @@
#include "modbusmanager.h"
#include <QModbusDataUnit>
#include <QDebug>

ModbusManager::ModbusManager(RegisterManager* regManager, QObject *parent)
: QObject(parent), registerManager(regManager)
{
modbusDevice = new QModbusRtuSerialMaster(this);
pollTimer = new QTimer(this);

connect(modbusDevice, &QModbusClient::errorOccurred, this,
[this](QModbusDevice::Error) {
emit errorOccurred(modbusDevice->errorString());
});

connect(pollTimer, &QTimer::timeout, this, &ModbusManager::readRegisters);
}

ModbusManager::~ModbusManager()
{
stopSimulation();
disconnectDevice();
}

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

void ModbusManager::disconnectDevice()
{
if (modbusDevice->state() != QModbusDevice::UnconnectedState) {
modbusDevice->disconnectDevice();
emit connectionStatusChanged(false);
}
}

void ModbusManager::startSimulation(int interval)
{
pollTimer->start(interval);
}

void ModbusManager::stopSimulation()
{
pollTimer->stop();
}

void ModbusManager::readRegisters()
{
if (!modbusDevice || modbusDevice->state() != QModbusDevice::ConnectedState)
return;

// 读取所有D寄存器 (保持寄存器)
readRegisterGroup(QModbusDataUnit::HoldingRegisters, 0, 4000);

// 读取所有M寄存器 (线圈)
readRegisterGroup(QModbusDataUnit::Coils, 0, 4000);
}

void ModbusManager::readRegisterGroup(QModbusDataUnit::RegisterType type, int startAddr, int count)
{
const int MAX_REG_PER_REQUEST = 125; // Modbus RTU限制

for (int addr = startAddr; addr < count; addr += MAX_REG_PER_REQUEST) {
int readCount = qMin(MAX_REG_PER_REQUEST, count - addr);

QModbusDataUnit unit(type, addr, 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()
{
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();
}

void ModbusManager::writeRegister(const QString& registerId, quint16 value)
{
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();
});
}
}

+ 55
- 0
modbusmanager.h View File

@@ -0,0 +1,55 @@
#ifndef MODBUSMANAGER_H
#define MODBUSMANAGER_H

#include <QObject>
#include <QModbusRtuSerialMaster>
#include <QSerialPort>
#include <QTimer>
#include <QMap>
#include "registermanager.h"

class ModbusManager : public QObject
{
Q_OBJECT
public:
explicit ModbusManager(RegisterManager* regManager, QObject *parent = nullptr);
~ModbusManager();

// 串口连接管理
bool connectToDevice(const QString& portName,
QSerialPort::BaudRate baudRate = QSerialPort::Baud9600,
QSerialPort::DataBits dataBits = QSerialPort::Data8,
QSerialPort::Parity parity = QSerialPort::NoParity,
QSerialPort::StopBits stopBits = QSerialPort::OneStop);

void disconnectDevice();

// 仿真控制
void startSimulation(int interval = 500); // 毫秒
void stopSimulation();

// 寄存器读写
void writeRegister(const QString& registerId, quint16 value);

// 设置从站地址
void setSlaveAddress(int address) { slaveAddress = address; }

signals:
void connectionStatusChanged(bool connected);
void errorOccurred(const QString& error);

private slots:
void readRegisters();
void processModbusReply();

private:
QModbusRtuSerialMaster *modbusDevice;
RegisterManager* registerManager;
QTimer* pollTimer;
int slaveAddress = 1; // 默认从站地址
QMap<QModbusReply*, QString> pendingRequests;

void readRegisterGroup(QModbusDataUnit::RegisterType type, int startAddr, int count);
};

#endif // MODBUSMANAGER_H

+ 38
- 7
mygraphicsview.cpp View File

@@ -5,6 +5,8 @@
#include <QDebug>
#include <QMenu>
#include <QInputDialog>
#include <QMessageBox>
#include <QRegularExpression>

MyGraphicsView::ClipInfo MyGraphicsView::clipboard_ = {};

@@ -165,16 +167,45 @@ void MyGraphicsView::onItemRequestDelete(Item *item)

void MyGraphicsView::onItemRequestBindRegister(Item *item)
{
if (!item) return;

bool ok = false;
QString reg = QInputDialog::getText(this,
"寄存器",
"编号:",
QLineEdit::Normal,
item->registerId(),
&ok);
QString reg = QInputDialog::getText(
this,
"绑定寄存器",
"格式: <类型><地址>\n"
"类型: D(保持寄存器), M(线圈寄存器)\n"
"示例: D100, M200\n"
"地址范围: 0-4000",
QLineEdit::Normal,
item->registerId(),
&ok
);

if (ok && !reg.isEmpty()) {
item->setRegisterId(reg);
// 验证寄存器格式
QRegularExpression regex("^[DMdm]\\d{1,4}$");
QRegularExpressionMatch match = regex.match(reg);

if (!match.hasMatch()) {
QMessageBox::warning(this, "格式错误", "请输入有效的寄存器格式\n示例: D100, M200");
return;
}

int address = reg.mid(1).toInt();
if (address > 4000) {
QMessageBox::warning(this, "范围错误", "寄存器地址必须在0-4000范围内");
return;
}

// 标准化格式 (D100)
QString newReg = QString("%1%2").arg(reg[0].toUpper()).arg(address);

item->setRegisterId(newReg);
item->update();

// 发出绑定信号
emit itemBoundToRegister(item, newReg);
}
}



+ 3
- 0
mygraphicsview.h View File

@@ -28,6 +28,9 @@ protected:
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;

signals:
void itemBoundToRegister(Item*, QString);

private:
Item* anchorItemAt(const QPoint& viewPos, Item::AnchorType& anchorType);



+ 10
- 0
plc.cpp View File

@@ -17,6 +17,8 @@ PLC::PLC(QWidget *parent) :
ui(new Ui::PLC)
{
ui->setupUi(this);
registerManager = new RegisterManager(this);
modbusManager = new ModbusManager(registerManager, this);
/* 1. 场景 */
plc_scene_ = new QGraphicsScene(this);
ui->graphicsView->setScene(plc_scene_);
@@ -33,6 +35,14 @@ PLC::PLC(QWidget *parent) :
createComponents();
connect(ui->listWidget,&QListWidget::currentTextChanged,this,&PLC::onListwidgetCurrenttextchanged);
connect(ui->btn_insert,&QPushButton::clicked,this,&PLC::btnInsertClicked);
// connect(modbusManager, &ModbusManager::connectionStatusChanged,
// this, &PLC::updateConnectionStatus);
// connect(modbusManager, &ModbusManager::errorOccurred,
// this, &PLC::handleModbusError);
connect(ui->graphicsView, &MyGraphicsView::itemBoundToRegister,
registerManager, &RegisterManager::bindItem);
// connect(this, &PLC::requestWriteRegister,
// modbusManager, &ModbusManager::writeRegister);
}

PLC::~PLC()


+ 3
- 0
plc.h View File

@@ -3,6 +3,7 @@

#include <QGraphicsScene>
#include <QWidget>
#include "modbusmanager.h"
#include "project.h"

namespace Ui {
@@ -32,6 +33,8 @@ private slots:
private:
Ui::PLC *ui;
QGraphicsScene *plc_scene_;
ModbusManager* modbusManager;
RegisterManager* registerManager;
QString selectedComponentType;
};



+ 44
- 0
registermanager.cpp View File

@@ -0,0 +1,44 @@
// registermanager.cpp
#include "registermanager.h"
#include "item.h"

RegisterManager::RegisterManager(QObject *parent) : QObject(parent) {}

void RegisterManager::bindItem(Item* item, const QString& registerId)
{
if (registerId.isEmpty() || !item) return;

// 添加到映射表
registerMap[registerId].append(item);

// 连接图元销毁信号
connect(item, &Item::destroyed, this, [this, item, registerId](){
if (registerMap.contains(registerId)) {
registerMap[registerId].removeAll(item);
if (registerMap[registerId].isEmpty()) {
registerMap.remove(registerId);
}
}
});
}

void RegisterManager::updateRegisterValue(const QString& registerId, quint16 value)
{
if (!registerMap.contains(registerId)) return;

// 更新所有绑定该寄存器的图元
for (Item* item : registerMap[registerId]) {
item->setRegisterValue(value);
item->update(); // 刷新显示
}
}

QStringList RegisterManager::getAllRegisteredRegisters() const
{
return registerMap.keys();
}

QList<Item*> RegisterManager::getItemsForRegister(const QString& registerId) const
{
return registerMap.value(registerId, QList<Item*>());
}

+ 28
- 0
registermanager.h View File

@@ -0,0 +1,28 @@
#ifndef REGISTERMANAGER_H
#define REGISTERMANAGER_H

#include <QObject>
#include "item.h"

class RegisterManager : public QObject
{
Q_OBJECT
public:
explicit RegisterManager(QObject *parent = nullptr);

// 注册图元与寄存器绑定关系
void bindItem(Item* item, const QString& registerId);

// 更新寄存器值
void updateRegisterValue(const QString& registerId, quint16 value);
QStringList getAllRegisteredRegisters() const;

// 获取绑定特定寄存器的所有图元
QList<Item*> getItemsForRegister(const QString& registerId) const;

private:
// 寄存器到图元的映射 (寄存器ID -> 图元列表)
QMap<QString, QList<Item*>> registerMap;
};

#endif // REGISTERMANAGER_H

Loading…
Cancel
Save