Bläddra i källkod

实现没有线程的读线圈,偶尔成功版

main
email 2 timmar sedan
förälder
incheckning
f5b76a702c
8 ändrade filer med 328 tillägg och 20 borttagningar
  1. +108
    -9
      untitled/hmidocument.cpp
  2. +14
    -1
      untitled/hmidocument.h
  3. +37
    -4
      untitled/mainwindow.cpp
  4. +10
    -2
      untitled/mainwindow.h
  5. +114
    -0
      untitled/modbussimulator.cpp
  6. +39
    -0
      untitled/modbussimulator.h
  7. +3
    -0
      untitled/untitled.pro
  8. +3
    -4
      untitled/untitled.pro.user

+ 108
- 9
untitled/hmidocument.cpp Visa fil

@@ -22,6 +22,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include<QSpinBox>
#include<QMessageBox>
HMIDocument::HMIDocument(QWidget *parent)
: BaseDocument(HMI, parent),
m_title("未命名HMI"),
@@ -66,8 +67,95 @@ HMIDocument::HMIDocument(QWidget *parent)

HMIDocument::~HMIDocument()
{
stopSimulation();
}
void HMIDocument::startSimulation()
{
qDebug() <<"123";
if (m_modbusSimulator && m_modbusSimulator->isRunning()) return;
// 收集所有线圈地址
QSet<int> addresses;

foreach (QGraphicsItem *item, m_scene->items()) {
if (auto namedItem = dynamic_cast<NamedItem*>(item)) {
bool ok;
int address = namedItem->name().toInt(&ok);

if (ok && address >= 0 && address <= 4000) { // 限制有效范围
// 只收集指示灯(ResizableEllipse)的地址
if (auto ellipseItem = dynamic_cast<ResizableEllipse*>(item)) {
addresses.insert(address);
// 遍历代码结束后
qDebug() << "收集到的指示灯地址:" << addresses; // 检查是否正确包含地址(如123)
} else {
// 对按钮(ResizableRectangle)进行处理,但不收集地址
qDebug() << "按钮不收集地址: " << item->type();
}
} else {
// 记录无效地址,方便调试
qDebug() << "无效的线圈地址:" << namedItem->name()
<< "(图形项类型:" << item->type() << ")";
}
}
}

if (addresses.isEmpty()) {
QMessageBox::warning(nullptr, "仿真启动", "没有找到有效的线圈地址绑定");
return;
}

if (!m_modbusSimulator) {
m_modbusSimulator = new ModbusSimulator(this);
connect(m_modbusSimulator, &ModbusSimulator::coilStatusRead,
this, &HMIDocument::onCoilStatusRead);
connect(m_modbusSimulator, &ModbusSimulator::errorOccurred,
this, &HMIDocument::onSimulationError);
}
// 遍历代码结束后
qDebug() << "zhxing";
m_modbusSimulator->setCoilAddresses(addresses);
m_modbusSimulator->startSimulation();
}

void HMIDocument::stopSimulation()
{
if (m_modbusSimulator) {
m_modbusSimulator->stopSimulation();
}
}

bool HMIDocument::isSimulationRunning() const
{
return m_modbusSimulator && m_modbusSimulator->isRunning();
}

void HMIDocument::onCoilStatusRead(int address, bool state)
{
foreach (QGraphicsItem *item, m_scene->items()) {
if (auto namedItem = dynamic_cast<NamedItem*>(item)) {
bool ok;
int itemAddress = namedItem->name().toInt(&ok);
if (ok && itemAddress == address) {
if (auto ellipse = dynamic_cast<ResizableEllipse*>(item)) {
ellipse->setBrush(state ? ellipse->onColor() : ellipse->offColor());
ellipse->update();
}
}
}
}
}

void HMIDocument::onSimulationError(const QString &message)
{
QMessageBox::critical(nullptr, "仿真错误", message);
}

void HMIDocument::writeCoil(int address, bool state)
{
if (m_modbusSimulator) {
m_modbusSimulator->writeCoil(address, state);
}
}
// 事件过滤器(处理绘图、拖拽等事件)
bool HMIDocument::eventFilter(QObject *obj, QEvent *event)
{
@@ -191,6 +279,8 @@ void HMIDocument::showContextMenu(QPoint globalPos)
QAction *pasteAction = menu.addAction("粘贴");
QAction *deleteAction = menu.addAction("删除");
pasteAction->setEnabled(!m_copiedItemsData.isEmpty());

ResizableRectangle *rectItem = dynamic_cast<ResizableRectangle*>(clickedItem);
connect(propAction, &QAction::triggered, this, &HMIDocument::showItemProperties);
connect(copyAction, &QAction::triggered, this, &HMIDocument::copySelectedItems);
connect(pasteAction, &QAction::triggered, this, &HMIDocument::pasteItems);
@@ -202,15 +292,24 @@ void HMIDocument::showContextMenu(QPoint globalPos)
QAction *offAction = menu.addAction("置为OFF");

connect(onAction, &QAction::triggered, [=]() {
if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
rect->setBrush(rect->pressedColor());
}
});
connect(offAction, &QAction::triggered, [=]() {
if (auto rect = dynamic_cast<ResizableRectangle*>(clickedItem)) {
rect->setBrush(rect->releasedColor());
}
});
rectItem->setBrush(rectItem->pressedColor()); // 使用 rectItem
// 获取线圈地址并写入
bool ok;
int address = rectItem->name().toInt(&ok); // 使用 rectItem
if (ok && address > 0) {
writeCoil(address, true); // 写入ON状态
}
});

connect(offAction, &QAction::triggered, [=]() {
rectItem->setBrush(rectItem->releasedColor()); // 使用 rectItem
// 获取线圈地址并写入
bool ok;
int address = rectItem->name().toInt(&ok); // 使用 rectItem
if (ok && address > 0) {
writeCoil(address, false); // 写入OFF状态
}
});
}
}
menu.exec(globalPos + QPoint(10, 10));


+ 14
- 1
untitled/hmidocument.h Visa fil

@@ -5,7 +5,7 @@
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QJsonObject>
#include"modbussimulator.h"
// 前向声明
class ResizableRectangle;
class ResizableEllipse;
@@ -40,12 +40,25 @@ public:
bool loadFromFile(const QString &filePath) override;

void markModified();


// 仿真控制
void startSimulation();
void stopSimulation();
bool isSimulationRunning() const;

// 写线圈方法
void writeCoil(int address, bool state);
signals:
void titleChanged(const QString &title);

protected:
bool eventFilter(QObject *obj, QEvent *event) override;
private slots:
void onCoilStatusRead(int address, bool state);
void onSimulationError(const QString &message);
private:
ModbusSimulator *m_modbusSimulator;
// 辅助方法
void createShape(const QString& type, const QPointF &pos);
void resetDrawFlags();


+ 37
- 4
untitled/mainwindow.cpp Visa fil

@@ -205,10 +205,43 @@ void MainWindow::createMenus()
simulationMenu->setFont(itemFont);

// 运行动作 (暂时留空)
QAction *runAction = new QAction("运行", this);
runAction->setFont(itemFont);
runAction->setEnabled(false);
simulationMenu->addAction(runAction);
// QAction *runAction = new QAction("运行", this);
// runAction->setFont(itemFont);
// simulationMenu->addAction(runAction);
// 创建仿真运行动作并连接到槽函数
m_runSimulationAction = new QAction("运行", this); // 使用成员变量
m_runSimulationAction->setFont(itemFont);
simulationMenu->addAction(m_runSimulationAction);

// 添加连接:将仿真运行动作连接到 onRunSimulation 槽
connect(m_runSimulationAction, &QAction::triggered, this, &MainWindow::onRunSimulation);
}

void MainWindow::onRunSimulation()
{
if (auto hmiDoc = dynamic_cast<HMIDocument*>(m_tabWidget->currentWidget())) {
if (hmiDoc->isSimulationRunning()) {
hmiDoc->stopSimulation();
} else {
hmiDoc->startSimulation();
}
onSimulationStatusChanged();
}
}

void MainWindow::onSimulationStatusChanged()
{
if (auto hmiDoc = dynamic_cast<HMIDocument*>(m_tabWidget->currentWidget())) {
if (hmiDoc->isSimulationRunning()) {
m_runSimulationAction->setText("停止");
m_runSimulationAction->setIcon(QIcon(":/icons/stop.png"));
statusBar()->showMessage("仿真运行中...", 2000);
} else {
m_runSimulationAction->setText("运行");
m_runSimulationAction->setIcon(QIcon(":/icons/run.png"));
statusBar()->showMessage("仿真已停止", 2000);
}
}
}
// 创建左侧工具栏
void MainWindow::createToolbars()


+ 10
- 2
untitled/mainwindow.h Visa fil

@@ -5,9 +5,11 @@
#include <QTabWidget>
#include <QToolBar>
#include <QAction>
#include<QTextEdit>
#include <QTextEdit>
#include "basedocument.h"
#include<QPushButton>
#include <QPushButton>
#include <QMenu>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
@@ -33,6 +35,11 @@ private slots:
void onCloseTab(int index); // 关闭标签页
void onClearLogButtonClicked();
void showLogContextMenu(const QPoint &pos);

// 新增的仿真相关槽函数
void onRunSimulation(); // 运行/停止仿真
void onSimulationStatusChanged(); // 更新仿真状态显示

private:
void createMenus(); // 创建菜单栏
QWidget* m_logPanelContainer;
@@ -53,6 +60,7 @@ private:
QAction *m_saveAction;
QAction *m_saveAsAction;
QAction *m_openAction;
QAction *m_runSimulationAction; // 新增:仿真运行动作
};

#endif // MAINWINDOW_H

+ 114
- 0
untitled/modbussimulator.cpp Visa fil

@@ -0,0 +1,114 @@
#include "modbussimulator.h"
#include <QSerialPort>
#include <QDebug>
#include <algorithm>
#include<QThread>
ModbusSimulator::ModbusSimulator(QObject *parent)
: QObject(parent), m_coilAddresses(),m_running(false), m_mutex()
{
m_modbusClient = new QModbusRtuSerialMaster(this);
m_readTimer = new QTimer(this);
connect(m_readTimer, &QTimer::timeout, this, &ModbusSimulator::readCoils);
}

ModbusSimulator::~ModbusSimulator()
{
stopSimulation();
}

bool ModbusSimulator::isRunning() const
{
return m_running;
}

void ModbusSimulator::startSimulation()
{
qDebug() << "进入 startSimulation() 函数";
if (m_running) return;
qDebug() << "启动Modbus仿真555...";
// 配置Modbus连接
m_modbusClient->setConnectionParameter(QModbusDevice::SerialPortNameParameter, "COM1");
m_modbusClient->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
m_modbusClient->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
m_modbusClient->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
m_modbusClient->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);

if (!m_modbusClient->connectDevice())
{
emit errorOccurred("无法连接Modbus设备");
return;
}
qDebug() << "Modbus设备连接成功";
m_running = true;
m_readTimer->start(1000); // 每秒读取一次
}

void ModbusSimulator::stopSimulation()
{
if (!m_running) return;

m_readTimer->stop();
m_modbusClient->disconnectDevice();
m_running = false;
}

void ModbusSimulator::setCoilAddresses(const QSet<int> &addresses)
{
qDebug() <<"执行到此";
qDebug() << "setCoilAddresses called, this:" << this; // 打印对象地址
QMutexLocker locker(&m_mutex); // 必须加锁
m_coilAddresses = QSet<int>(addresses);
}

void ModbusSimulator::writeCoil(int address, bool state)
{
if (!m_running) return;

if (auto reply = m_modbusClient->sendWriteRequest(
QModbusDataUnit(QModbusDataUnit::Coils, address, {state}), 1)) {

connect(reply, &QModbusReply::finished, [reply, address]() {
if (reply->error() != QModbusDevice::NoError) {
qWarning() << "写线圈失败:" << reply->errorString() << "地址:" << address;
}
reply->deleteLater();
});
}
}

void ModbusSimulator::readCoils()
{
QSet<int> addressesCopy;
{
QMutexLocker locker(&m_mutex);
if (!m_running || m_coilAddresses.isEmpty()) return;
addressesCopy = m_coilAddresses; // 在锁保护下复制
}
int minAddress = *std::min_element(addressesCopy.begin(), addressesCopy.end());
int maxAddress = *std::max_element(addressesCopy.begin(), addressesCopy.end());
int count = maxAddress - minAddress + 1;

// 创建读请求
QModbusDataUnit readUnit(QModbusDataUnit::Coils, minAddress, count);
if (auto *reply = m_modbusClient->sendReadRequest(readUnit, 1)) {
if (!reply->isFinished()) {
// 使用智能指针确保地址集合在回调中有效
auto safeAddresses = QSharedPointer<QSet<int>>::create(addressesCopy);
connect(reply, &QModbusReply::finished, this, [this, reply, safeAddresses]() {
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
for (int i = 0; i < unit.valueCount(); ++i) {
int address = unit.startAddress() + i;
bool state = unit.value(i);
if (safeAddresses->contains(address)) {
emit coilStatusRead(address, state);
}
}
}
reply->deleteLater();
});
} else {
delete reply;
}
}
}

+ 39
- 0
untitled/modbussimulator.h Visa fil

@@ -0,0 +1,39 @@
#ifndef MODBUSSIMULATOR_H
#define MODBUSSIMULATOR_H

#include <QObject>
#include <QModbusRtuSerialMaster>
#include <QTimer>
#include <QSet>
#include <QMutex> // 新增:互斥锁头文件
class ModbusSimulator : public QObject
{
Q_OBJECT

public:
explicit ModbusSimulator(QObject *parent = nullptr);
~ModbusSimulator();

void startSimulation();
void stopSimulation();
bool isRunning() const;

void setCoilAddresses(const QSet<int> &addresses);
void writeCoil(int address, bool state);

signals:
void coilStatusRead(int address, bool state);
void errorOccurred(const QString &message);

private slots:
void readCoils();

private:
QModbusRtuSerialMaster *m_modbusClient;
QTimer *m_readTimer;
QSet<int> m_coilAddresses;
bool m_running;
QMutex m_mutex; // 新增:保护m_coilAddresses的互斥锁
};

#endif // MODBUSSIMULATOR_H

+ 3
- 0
untitled/untitled.pro Visa fil

@@ -1,5 +1,6 @@
QT += core gui
QT += core gui widgets serialport
QT+=serialbus
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
@@ -21,6 +22,7 @@ SOURCES += \
hmidocument.cpp \
main.cpp \
mainwindow.cpp \
modbussimulator.cpp \
plcdocument.cpp \
plcitems.cpp

@@ -29,6 +31,7 @@ HEADERS += \
graphicsitems.h \
hmidocument.h \
mainwindow.h \
modbussimulator.h \
plcdocument.h \
plcitems.h



+ 3
- 4
untitled/untitled.pro.user Visa fil

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.11.1, 2025-08-12T21:46:20. -->
<!-- Written by QtCreator 4.11.1, 2025-08-14T12:27:31. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@@ -287,9 +287,8 @@
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">untitled2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:C:/Users/admin/Desktop/two/untitled/untitled.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Users/admin/Desktop/two/untitled/untitled.pro</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:C:/Users/admin/Desktop/seven/untitled/untitled.pro</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">C:/Users/admin/Desktop/seven/untitled/untitled.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="bool" key="RunConfiguration.Arguments.multi">false</value>
<value type="QString" key="RunConfiguration.OverrideDebuggerStartup"></value>


Laddar…
Avbryt
Spara