PLC寄存器写入验证工具

发布于:2025-08-19 ⋅ 阅读:(21) ⋅ 点赞:(0)

PLC寄存器写入验证工具

在这里插入图片描述

概述

PLC寄存器写入验证工具是一个基于PyQt5和modbus_tk库开发的图形界面应用程序,用于通过Modbus TCP协议与PLC设备进行通信,实现对PLC寄存器的写入操作及结果验证。

该工具提供了简洁直观的用户界面,支持连接到PLC设备、写入数据到指定寄存器,并自动验证写入结果,适用于工业自动化领域的设备调试和数据验证工作。

功能特点

  • 通过Modbus TCP协议与PLC设备建立连接
  • 支持指定寄存器地址和写入值
  • 写入数据后自动读取验证,确保数据正确写入
  • 所有网络操作在后台线程执行,避免UI界面卡顿
  • 实时显示连接状态和操作结果
  • 友好的错误提示和状态反馈

依赖环境

运行本工具需要以下依赖库:

  • Python 3.x
  • PyQt5:用于构建图形用户界面
  • modbus_tk:用于实现Modbus通信功能

可通过以下命令安装所需依赖:

pip install PyQt5 modbus-tk

使用方法

1. 连接PLC设备

  1. 在"PLC IP地址"输入框中填写目标PLC的IP地址(默认值:192.168.1.1)
  2. 在"端口"输入框中填写Modbus端口号(默认值:502,通常无需修改)
  3. 点击"连接"按钮建立与PLC的连接
  4. 连接成功后,状态会显示"成功连接到[IP地址:端口]",“连接"按钮变为"断开”

2. 写入数据到寄存器

  1. 确保已成功连接到PLC
  2. 在"寄存器地址"输入框中填写目标寄存器的地址(整数)
  3. 在"写入值"输入框中填写要写入的值(整数)
  4. 点击"写入数据"按钮执行写入操作
  5. 系统会自动完成写入并验证结果,操作结果会通过弹窗和状态标签显示

3. 断开连接

  • 点击"断开"按钮可断开与PLC的连接
  • 关闭应用程序时会自动断开连接

代码结构说明

主要类和功能

  1. Communicate类:继承自QObject,用于在非UI线程和UI线程之间传递信号,避免线程安全问题

  2. PLCModbusWriter类:主窗口类,继承自QMainWindow,包含以下核心方法:

    • __init__():初始化窗口界面和变量
    • toggle_connection():切换连接状态(连接/断开)
    • connect_to_plc():建立与PLC的连接(在后台线程执行)
    • disconnect_from_plc():断开与PLC的连接
    • write_data():处理写入数据的用户请求
    • _write_data_thread():实际执行写入和验证操作(在后台线程执行)
    • show_message():显示消息提示并更新状态标签
    • update_ui_after_connection():根据连接状态更新UI元素
    • closeEvent():窗口关闭时确保断开连接

线程处理

为避免网络操作导致UI界面卡顿,所有Modbus通信操作(连接、写入、读取)均在独立线程中执行:

  • 连接操作通过threading.Thread在后台执行
  • 写入和验证操作同样在单独线程中执行
  • 使用信号槽机制(Communicate类)在后台线程和UI线程之间传递消息

注意事项

  1. 确保PLC设备已正确配置Modbus TCP服务器功能
  2. 确保PLC的IP地址和端口号输入正确,且与工具在同一网络中
  3. 寄存器地址和写入值必须为整数,否则会收到错误提示
  4. 操作超时时间设置为5秒,若网络环境较差可适当调整
  5. 连接状态会实时显示在窗口底部的状态标签中

错误处理

程序会捕获并显示以下常见错误:

  • 无效的IP地址或端口号
  • 无法连接到PLC设备(网络问题或设备未响应)
  • 无效的寄存器地址或写入值
  • 写入操作失败(权限问题或设备限制)
  • 读取验证失败

所有错误信息都会通过弹窗和状态标签显示,便于问题诊断和排查。

import sys
import threading
import warnings

# 忽略特定警告
warnings.filterwarnings("ignore", message="sipPyTypeDict() is deprecated")

from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QLabel, QLineEdit, QPushButton, QMessageBox)
from PyQt5.QtCore import Qt, pyqtSignal, QObject
import modbus_tk
import modbus_tk.defines as cst
from modbus_tk import modbus_tcp


# 用于在非UI线程中发送信号到UI线程
class Communicate(QObject):
    signal = pyqtSignal(str)


class PLCModbusWriter(QMainWindow):
    def __init__(self):
        super().__init__()

        # 初始化变量
        self.master = None
        self.is_connected = False
        self.com = Communicate()
        self.com.signal.connect(self.show_message)

        # 设置窗口
        self.setWindowTitle("PLC寄存器写入验证工具")
        self.setGeometry(100, 100, 1200, 800)

        # 创建中心部件和布局
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)

        # IP地址和端口设置
        ip_port_layout = QHBoxLayout()
        ip_port_layout.addWidget(QLabel("PLC IP地址:"))
        self.ip_edit = QLineEdit("192.168.1.1")
        ip_port_layout.addWidget(self.ip_edit)

        ip_port_layout.addWidget(QLabel("端口:"))
        self.port_edit = QLineEdit("502")  # 添加端口编辑框,默认502
        self.port_edit.setMaximumWidth(80)  # 限制端口输入框宽度
        ip_port_layout.addWidget(self.port_edit)

        main_layout.addLayout(ip_port_layout)

        # 寄存器地址设置
        addr_layout = QHBoxLayout()
        addr_layout.addWidget(QLabel("寄存器地址:"))
        self.addr_edit = QLineEdit("0")
        addr_layout.addWidget(self.addr_edit)
        main_layout.addLayout(addr_layout)

        # 写入值设置
        value_layout = QHBoxLayout()
        value_layout.addWidget(QLabel("写入值:"))
        self.value_edit = QLineEdit("0")
        value_layout.addWidget(self.value_edit)
        main_layout.addLayout(value_layout)

        # 按钮布局
        btn_layout = QHBoxLayout()
        self.connect_btn = QPushButton("连接")
        self.connect_btn.clicked.connect(self.toggle_connection)
        btn_layout.addWidget(self.connect_btn)

        self.write_btn = QPushButton("写入数据")
        self.write_btn.clicked.connect(self.write_data)
        self.write_btn.setEnabled(False)  # 初始禁用
        btn_layout.addWidget(self.write_btn)

        main_layout.addLayout(btn_layout)

        # 状态标签
        self.status_label = QLabel("状态: 未连接")
        self.status_label.setAlignment(Qt.AlignCenter)
        main_layout.addWidget(self.status_label)

    def toggle_connection(self):
        if not self.is_connected:
            # 连接到PLC
            threading.Thread(target=self.connect_to_plc, daemon=True).start()
        else:
            # 断开连接
            self.disconnect_from_plc()

    def connect_to_plc(self):
        try:
            ip_address = self.ip_edit.text()
            # 从输入框获取端口号,默认为502
            try:
                port = int(self.port_edit.text())
            except ValueError:
                self.com.signal.emit("端口号必须是整数")
                return

            # 创建Modbus TCP主站
            self.master = modbus_tcp.TcpMaster(host=ip_address, port=port)
            self.master.set_timeout(5.0)

            # 尝试读取一个寄存器来验证连接
            self.master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)

            # 连接成功
            self.is_connected = True
            self.com.signal.emit(f"成功连接到 {ip_address}:{port}")
            self.update_ui_after_connection(True)

        except Exception as e:
            self.com.signal.emit(f"连接失败: {str(e)}")
            self.is_connected = False
            self.master = None
            self.update_ui_after_connection(False)

    def disconnect_from_plc(self):
        if self.master:
            self.master.close()
            self.master = None

        self.is_connected = False
        self.show_message("已断开连接")
        self.update_ui_after_connection(False)

    def write_data(self):
        if not self.is_connected or not self.master:
            self.show_message("请先连接到PLC")
            return

        try:
            # 获取输入值
            addr = int(self.addr_edit.text())
            value = int(self.value_edit.text())

            # 在新线程中执行写入操作,避免UI卡顿
            threading.Thread(target=self._write_data_thread, args=(addr, value), daemon=True).start()

        except ValueError:
            self.show_message("寄存器地址和值必须是整数")
        except Exception as e:
            self.show_message(f"写入错误: {str(e)}")

    def _write_data_thread(self, addr, value):
        try:
            # 写入数据到保持寄存器
            self.master.execute(1, cst.WRITE_SINGLE_REGISTER, addr, output_value=value)
            self.com.signal.emit(f"成功写入: 地址 {addr} = {value}")

            # 验证写入结果
            result = self.master.execute(1, cst.READ_HOLDING_REGISTERS, addr, 1)
            self.com.signal.emit(f"验证结果: 地址 {addr} = {result[0]}")

        except Exception as e:
            self.com.signal.emit(f"写入失败: {str(e)}")

    def show_message(self, text):
        QMessageBox.information(self, "信息", text)
        self.status_label.setText(f"状态: {text}")

    def update_ui_after_connection(self, connected):
        if connected:
            self.connect_btn.setText("断开")
            self.write_btn.setEnabled(True)
            self.ip_edit.setEnabled(False)  # 连接后禁用IP编辑
            self.port_edit.setEnabled(False)  # 连接后禁用端口编辑
        else:
            self.connect_btn.setText("连接")
            self.write_btn.setEnabled(False)
            self.ip_edit.setEnabled(True)  # 断开后启用IP编辑
            self.port_edit.setEnabled(True)  # 断开后启用端口编辑

    def closeEvent(self, event):
        # 关闭窗口时断开连接
        if self.is_connected:
            self.disconnect_from_plc()
        event.accept()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = PLCModbusWriter()
    window.show()
    sys.exit(app.exec_())


网站公告

今日签到

点亮在社区的每一天
去签到