@@ -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) | |||
{ | |||
// 指示灯初始为圆形 | |||
@@ -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 | |||
}; | |||
@@ -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) | |||
@@ -14,10 +14,18 @@ | |||
#include <QDebug> | |||
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<HmiButton*>(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<QGraphicsView*>(); | |||
if (!view) continue; | |||
auto scene = view->scene(); | |||
for (auto *item : scene->items()) { | |||
if (auto hmi = dynamic_cast<HmiComponent*>(item)) { | |||
if (hmi->address() == address) { | |||
if (auto btn = dynamic_cast<HmiButton*>(hmi)) { | |||
btn->setOn(on); // 按钮联动 | |||
} else { | |||
// 非按钮类就按颜色更新 | |||
hmi->setColor(on ? hmi->onColor() : hmi->offColor()); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
void HMIModule::prepareToCreateButton() | |||
{ | |||
// 获取当前 TabWidget 中选中的 GraphicsView | |||
@@ -1,9 +1,10 @@ | |||
#ifndef HMIMODULE_H | |||
#define HMIMODULE_H | |||
#include <QObject> | |||
#include "ui_MainWindow.h" | |||
#include "customgraphicsscene.h" | |||
#include "modbusmanager.h" | |||
#include <QObject> | |||
#include <QList> | |||
#include <QMap> | |||
#include <QVector> | |||
@@ -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<int> m_availablePageNumbers; // 存储可用的页面编号 (有序列表) | |||
QList<int> m_pageOrder; // 维护页面顺序 | |||
bool m_isModified = false; // 添加一个修改标志变量 | |||
ModbusManager *m_modbusManager; | |||
}; | |||
#endif // HMIMODULE_H |
@@ -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(); | |||
} | |||
} | |||
@@ -27,6 +27,9 @@ private slots: | |||
void onNewFile(); | |||
void onOpenFile(); | |||
void onSaveFile(); | |||
void startSimulation(); | |||
void stopSimulation(); | |||
private: | |||
Ui::MainWindow *ui_; | |||
@@ -0,0 +1,124 @@ | |||
#include "ModbusManager.h" | |||
#include <QModbusDataUnit> | |||
#include <QModbusReply> | |||
#include <QSerialPort> | |||
#include <QVariant> | |||
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; | |||
} |
@@ -0,0 +1,41 @@ | |||
#ifndef MODBUSMANAGER_H | |||
#define MODBUSMANAGER_H | |||
#include <QObject> | |||
#include <qmodbusrtuserialclient.h> | |||
#include <QTimer> | |||
#include <QMap> | |||
#include <QSerialPort> | |||
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<int, bool> m_coilValues; | |||
}; | |||
#endif // MODBUSMANAGER_H |