diff --git a/customgraphics.cpp b/customgraphics.cpp index 167f8ca..050aab8 100644 --- a/customgraphics.cpp +++ b/customgraphics.cpp @@ -402,6 +402,7 @@ void HmiButton::setOn(bool on) setColor(m_onColor); else setColor(m_offColor); + emit stateChanged(m_isOn); // 通知 HMIModule 更新 update(); } } @@ -414,10 +415,16 @@ QRectF HmiButton::boundingRect() const void HmiButton::paintShape(QPainter *painter) { painter->setPen(Qt::NoPen); - painter->setBrush(m_color); + painter->setBrush(m_isOn ? m_onColor : m_offColor); painter->drawRect(boundingRect()); } +void HmiButton::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + // 让基类处理其他情况(选择、拖动等) + HmiComponent::mousePressEvent(event); +} + HmiIndicator::HmiIndicator(QGraphicsItem *parent) : HmiComponent(parent) { // 指示灯初始为圆形 diff --git a/customgraphics.h b/customgraphics.h index e5fd22f..672fdf7 100644 --- a/customgraphics.h +++ b/customgraphics.h @@ -87,6 +87,7 @@ protected: // 按钮类 class HmiButton : public HmiComponent { + Q_OBJECT public: HmiButton(QGraphicsItem *parent = nullptr); QRectF boundingRect() const override; @@ -96,9 +97,12 @@ public: bool isOn() const; void setOn(bool on); +signals: + void stateChanged(bool on); + protected: void paintShape(QPainter *painter) override; - + void mousePressEvent(QGraphicsSceneMouseEvent *event) override; private: bool m_isOn = false; // 默认OFF }; diff --git a/customgraphicsscene.cpp b/customgraphicsscene.cpp index 41e7448..bc8250e 100644 --- a/customgraphicsscene.cpp +++ b/customgraphicsscene.cpp @@ -15,16 +15,7 @@ CustomGraphicsScene::CustomGraphicsScene(QGraphicsView* view, QObject *parent) void CustomGraphicsScene::setMode(Mode mode) { - if (m_mode == mode) return; - m_mode = mode; - - // 根据模式设置光标 - if (m_mode == CreateItem) { - m_view->setCursor(Qt::CrossCursor); - } else { - m_view->setCursor(Qt::ArrowCursor); // 恢复为标准箭头光标 - } } void CustomGraphicsScene::setComponentTypeToCreate(ComponentType type) diff --git a/hmimodule.cpp b/hmimodule.cpp index b74e501..a7b86fe 100644 --- a/hmimodule.cpp +++ b/hmimodule.cpp @@ -14,10 +14,18 @@ #include HMIModule::HMIModule(Ui::MainWindow* ui, QObject *parent) - : QObject{parent}, ui_(ui), m_scene(nullptr) + : QObject{parent}, ui_(ui), m_scene(nullptr), m_modbusManager(new ModbusManager(this)) { // 初始化 m_pageOrder m_pageOrder.append(1); // 初始页面 + + // 接收 Modbus 更新 + connect(m_modbusManager, &ModbusManager::coilUpdated, this, [this](int address, bool value){ + updateComponentsByAddress(address, value); + }); + + // 接收日志 + connect(m_modbusManager, &ModbusManager::logMessage, this, &HMIModule::logMessageGenerated); } void HMIModule::setButtonIcon(QAbstractButton *button, const QString &iconPath) @@ -391,6 +399,11 @@ void HMIModule::setModified(bool modified) m_isModified = modified; } +ModbusManager *HMIModule::getModbusManager() const +{ + return m_modbusManager; +} + void HMIModule::setupNewComponent(HmiComponent* item) { if (!item) return; @@ -401,12 +414,44 @@ void HMIModule::setupNewComponent(HmiComponent* item) connect(item, &HmiComponent::changeOnColorRequested, this, &HMIModule::onChangeOnColorRequested); connect(item, &HmiComponent::changeOffColorRequested, this, &HMIModule::onChangeOffColorRequested); + if (auto *btn = dynamic_cast(item)) { + connect(btn, &HmiButton::stateChanged, this, [this, btn](bool on){ + int addr = btn->address(); + updateComponentsByAddress(addr, on); + if (m_modbusManager && m_modbusManager->isRunning()) { + m_modbusManager->writeCoil(addr, on); + } + }); + } QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); QString log = QString("[%1] 创建 %2 组件").arg(currentTime).arg(item->componentName()); emit logMessageGenerated(log); } +void HMIModule::updateComponentsByAddress(int address, bool on) +{ + QTabWidget* tabWidget = ui_->tabWidget_2; + for (int i = 0; i < tabWidget->count(); ++i) { + QGraphicsView* view = tabWidget->widget(i)->findChild(); + if (!view) continue; + + auto scene = view->scene(); + for (auto *item : scene->items()) { + if (auto hmi = dynamic_cast(item)) { + if (hmi->address() == address) { + if (auto btn = dynamic_cast(hmi)) { + btn->setOn(on); // 按钮联动 + } else { + // 非按钮类就按颜色更新 + hmi->setColor(on ? hmi->onColor() : hmi->offColor()); + } + } + } + } + } +} + void HMIModule::prepareToCreateButton() { // 获取当前 TabWidget 中选中的 GraphicsView diff --git a/hmimodule.h b/hmimodule.h index e1ccc99..3cd5a84 100644 --- a/hmimodule.h +++ b/hmimodule.h @@ -1,9 +1,10 @@ #ifndef HMIMODULE_H #define HMIMODULE_H -#include #include "ui_MainWindow.h" #include "customgraphicsscene.h" +#include "modbusmanager.h" +#include #include #include #include @@ -21,6 +22,7 @@ public: void resetPages(); bool isModified() const; // 判断是否有未保存的修改 void setModified(bool modified); // 设置修改状态 + ModbusManager* getModbusManager() const; signals: void pageAdded(int pageNumber, int index); @@ -46,6 +48,7 @@ private slots: private: void setupNewComponent(HmiComponent* item); + void updateComponentsByAddress(int address, bool on); private: Ui::MainWindow* ui_; @@ -66,6 +69,8 @@ private: QList m_availablePageNumbers; // 存储可用的页面编号 (有序列表) QList m_pageOrder; // 维护页面顺序 bool m_isModified = false; // 添加一个修改标志变量 + + ModbusManager *m_modbusManager; }; #endif // HMIMODULE_H diff --git a/mainwindow.cpp b/mainwindow.cpp index f0d5cf7..0c6b43a 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -23,6 +23,9 @@ MainWindow::MainWindow(QWidget *parent) connect(ui_->actionNew, &QAction::triggered, this, &MainWindow::onNewFile); connect(ui_->actionOpen, &QAction::triggered, this, &MainWindow::onOpenFile); connect(ui_->actionSave, &QAction::triggered, this, &MainWindow::onSaveFile); + + connect(ui_->actionStart, &QAction::triggered, this, &MainWindow::startSimulation); + connect(ui_->actionStop, &QAction::triggered, this, &MainWindow::stopSimulation); } MainWindow::~MainWindow() @@ -109,3 +112,18 @@ void MainWindow::onSaveFile() } } +void MainWindow::startSimulation() +{ + if (hmi_ && hmi_->getModbusManager()) { + // 自己的串口参数,如 COM2 / 9600 + hmi_->getModbusManager()->startSimulation("COM2", 9600, QSerialPort::NoParity, QSerialPort::Data8, QSerialPort::OneStop); + } +} + +void MainWindow::stopSimulation() +{ + if (hmi_ && hmi_->getModbusManager()) { + hmi_->getModbusManager()->stopSimulation(); + } +} + diff --git a/mainwindow.h b/mainwindow.h index a298546..0c4986a 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -27,6 +27,9 @@ private slots: void onNewFile(); void onOpenFile(); void onSaveFile(); + void startSimulation(); + void stopSimulation(); + private: Ui::MainWindow *ui_; diff --git a/modbusmanager.cpp b/modbusmanager.cpp new file mode 100644 index 0000000..66a7c28 --- /dev/null +++ b/modbusmanager.cpp @@ -0,0 +1,124 @@ +#include "ModbusManager.h" +#include +#include +#include +#include + +ModbusManager::ModbusManager(QObject *parent) + : QObject(parent) +{ +} + +ModbusManager::~ModbusManager() +{ + stopSimulation(); +} + +bool ModbusManager::startSimulation(const QString &serialPortName, + int baudRate, + int parity, + int dataBits, + int stopBits) +{ + if (m_running) { + emit logMessage("仿真已经在运行"); + return true; + } + + m_modbusDevice = new QModbusRtuSerialClient(this); + if (!m_modbusDevice) + return false; + + m_modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPortName); + m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, parity); + m_modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudRate); + m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits); + m_modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits); + m_modbusDevice->setTimeout(1000); + m_modbusDevice->setNumberOfRetries(3); + + if (!m_modbusDevice->connectDevice()) { + emit logMessage("无法连接到 Modbus RTU 从站"); + delete m_modbusDevice; + m_modbusDevice = nullptr; + return false; + } + + // 定时轮询 + m_pollingTimer = new QTimer(this); + connect(m_pollingTimer, &QTimer::timeout, this, &ModbusManager::pollModbusCoils); + m_pollingTimer->start(200); // 200毫秒轮询 + + m_running = true; + emit logMessage(QString("开始仿真:串口 ") + serialPortName); + return true; +} + +void ModbusManager::stopSimulation() +{ + if (!m_running) return; + + if (m_pollingTimer) { + m_pollingTimer->stop(); + m_pollingTimer->deleteLater(); + m_pollingTimer = nullptr; + } + if (m_modbusDevice) { + m_modbusDevice->disconnectDevice(); + m_modbusDevice->deleteLater(); + m_modbusDevice = nullptr; + } + + m_running = false; + emit logMessage("停止仿真"); +} + +void ModbusManager::pollModbusCoils() +{ + if (!m_modbusDevice) return; + + int startAddress = 0; + int numberOfCoils = 9; // 根据需要修改 + + QModbusDataUnit readUnit(QModbusDataUnit::Coils, startAddress, numberOfCoils); + + if (auto *reply = m_modbusDevice->sendReadRequest(readUnit, 1)) { // 1 = 从站ID + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, this, [this, reply]() { + if (reply->error() == QModbusDevice::NoError) { + auto result = reply->result(); + for (uint i = 0; i < result.valueCount(); ++i) { + int addr = result.startAddress() + i; + bool val = result.value(i); + if (m_coilValues.value(addr) != val) { + m_coilValues[addr] = val; + emit coilUpdated(addr, val); + } + } + } else { + emit logMessage("读取线圈失败: " + reply->errorString()); + } + reply->deleteLater(); + }); + } else { + reply->deleteLater(); + } + } +} + +void ModbusManager::writeCoil(int address, bool value) +{ + if (!m_running || !m_modbusDevice) return; + + QModbusDataUnit writeUnit(QModbusDataUnit::Coils, address, 1); + writeUnit.setValue(0, value); + + if (auto *reply = m_modbusDevice->sendWriteRequest(writeUnit, 1)) { + connect(reply, &QModbusReply::finished, reply, &QObject::deleteLater); + } +} + +bool ModbusManager::isRunning() const +{ + return m_running; +} diff --git a/modbusmanager.h b/modbusmanager.h new file mode 100644 index 0000000..6a5d51d --- /dev/null +++ b/modbusmanager.h @@ -0,0 +1,41 @@ +#ifndef MODBUSMANAGER_H +#define MODBUSMANAGER_H + +#include +#include +#include +#include +#include + +class ModbusManager : public QObject +{ + Q_OBJECT +public: + explicit ModbusManager(QObject *parent = nullptr); + ~ModbusManager(); + + bool startSimulation(const QString &serialPortName, + int baudRate = 9600, + int parity = QSerialPort::NoParity, + int dataBits = QSerialPort::Data8, + int stopBits = QSerialPort::OneStop); + void stopSimulation(); + + void writeCoil(int address, bool value); + bool isRunning() const; + +signals: + void coilUpdated(int address, bool value); // 从站数据更新 + void logMessage(const QString &msg); // 日志 + +private slots: + void pollModbusCoils(); + +private: + QModbusClient *m_modbusDevice = nullptr; + QTimer *m_pollingTimer = nullptr; + bool m_running = false; + QMap m_coilValues; +}; + +#endif // MODBUSMANAGER_H