华荣三照明、合信、荣欣八组合馈电
This commit is contained in:
343
Test/test_modscan.py
Normal file
343
Test/test_modscan.py
Normal file
@ -0,0 +1,343 @@
|
||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
||||
QLineEdit, QMessageBox, QComboBox, QTableWidget, QTableWidgetItem, QSpinBox, QGroupBox, QFormLayout, QAction, QMenuBar)
|
||||
from PyQt5.QtCore import Qt, QTimer
|
||||
from PyQt5.QtGui import QFont, QIcon
|
||||
from modbus_tk import modbus_tcp, modbus_rtu
|
||||
import serial
|
||||
from modbus_tk.defines import *
|
||||
|
||||
class ModbusClient(QMainWindow):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.init_ui()
|
||||
self.master = None
|
||||
self.timer = QTimer()
|
||||
self.timer.timeout.connect(self.update_data)
|
||||
|
||||
def init_ui(self):
|
||||
self.setWindowTitle("Modbus 客户端")
|
||||
self.setGeometry(100, 100, 900, 700)
|
||||
self.setStyleSheet("""
|
||||
QWidget {
|
||||
background-color: #2e2e2e;
|
||||
color: #ffffff;
|
||||
font-family: Arial;
|
||||
font-size: 14px;
|
||||
}
|
||||
QGroupBox {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
border: 1px solid #555;
|
||||
border-radius: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left;
|
||||
padding: 10px;
|
||||
}
|
||||
QLabel {
|
||||
font-size: 14px;
|
||||
}
|
||||
QLineEdit, QComboBox, QSpinBox {
|
||||
padding: 5px;
|
||||
border: 1px solid #555;
|
||||
border-radius: 5px;
|
||||
background-color: #444;
|
||||
color: #fff;
|
||||
}
|
||||
QPushButton {
|
||||
background-color: #555;
|
||||
border: 1px solid #444;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background-color: #777;
|
||||
}
|
||||
QTableWidget {
|
||||
background-color: #444;
|
||||
alternate-background-color: #555;
|
||||
border: 1px solid #555;
|
||||
}
|
||||
QHeaderView::section {
|
||||
background-color: #333;
|
||||
color: #fff;
|
||||
padding: 5px;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
""")
|
||||
|
||||
# 创建菜单栏
|
||||
menu_bar = self.menuBar()
|
||||
file_menu = menu_bar.addMenu('文件')
|
||||
connection_menu = menu_bar.addMenu('连接')
|
||||
action_menu = menu_bar.addMenu('操作')
|
||||
|
||||
# 创建文件菜单项
|
||||
exit_action = QAction('退出', self)
|
||||
exit_action.triggered.connect(self.close)
|
||||
file_menu.addAction(exit_action)
|
||||
|
||||
# 创建连接菜单项
|
||||
connect_action = QAction('连接', self)
|
||||
connect_action.triggered.connect(self.connect_modbus)
|
||||
connection_menu.addAction(connect_action)
|
||||
|
||||
disconnect_action = QAction('断开连接', self)
|
||||
disconnect_action.triggered.connect(self.disconnect_modbus)
|
||||
connection_menu.addAction(disconnect_action)
|
||||
|
||||
# 创建操作菜单项
|
||||
execute_action = QAction('执行', self)
|
||||
execute_action.triggered.connect(self.execute_action)
|
||||
action_menu.addAction(execute_action)
|
||||
|
||||
monitor_action = QAction('开始监视', self)
|
||||
monitor_action.triggered.connect(self.toggle_monitoring)
|
||||
action_menu.addAction(monitor_action)
|
||||
|
||||
# 创建中心小部件
|
||||
central_widget = QWidget()
|
||||
self.setCentralWidget(central_widget)
|
||||
main_layout = QVBoxLayout(central_widget)
|
||||
|
||||
# 连接部分
|
||||
connection_group = QGroupBox("连接设置")
|
||||
connection_layout = QFormLayout()
|
||||
|
||||
self.mode_combo = QComboBox()
|
||||
self.mode_combo.addItem("TCP", "TCP")
|
||||
self.mode_combo.addItem("RTU", "RTU")
|
||||
self.mode_combo.currentIndexChanged.connect(self.toggle_mode)
|
||||
connection_layout.addRow(QLabel("模式选择:"), self.mode_combo)
|
||||
|
||||
self.ip_edit = QLineEdit()
|
||||
self.ip_edit.setPlaceholderText("输入 IP 地址")
|
||||
connection_layout.addRow(QLabel("IP 地址 / 串口端口:"), self.ip_edit)
|
||||
|
||||
self.port_edit = QLineEdit()
|
||||
self.port_edit.setPlaceholderText("输入端口号 / 波特率")
|
||||
connection_layout.addRow(QLabel("端口号 / 波特率:"), self.port_edit)
|
||||
|
||||
self.connect_button = QPushButton("连接")
|
||||
self.connect_button.clicked.connect(self.connect_modbus)
|
||||
connection_layout.addRow(self.connect_button)
|
||||
|
||||
self.disconnect_button = QPushButton("断开连接")
|
||||
self.disconnect_button.clicked.connect(self.disconnect_modbus)
|
||||
self.disconnect_button.setEnabled(False)
|
||||
connection_layout.addRow(self.disconnect_button)
|
||||
|
||||
connection_group.setLayout(connection_layout)
|
||||
main_layout.addWidget(connection_group)
|
||||
|
||||
# 功能部分
|
||||
function_group = QGroupBox("功能选择")
|
||||
function_layout = QFormLayout()
|
||||
|
||||
self.function_combo = QComboBox()
|
||||
self.function_combo.addItem("读线圈 (0)", READ_COILS)
|
||||
self.function_combo.addItem("读输入 (1)", READ_DISCRETE_INPUTS)
|
||||
self.function_combo.addItem("读保持寄存器 (3)", READ_HOLDING_REGISTERS)
|
||||
self.function_combo.addItem("读输入寄存器 (4)", READ_INPUT_REGISTERS)
|
||||
self.function_combo.addItem("写单个线圈 (5)", WRITE_SINGLE_COIL)
|
||||
self.function_combo.addItem("写单个寄存器 (6)", WRITE_SINGLE_REGISTER)
|
||||
self.function_combo.addItem("写多个寄存器 (16)", WRITE_MULTIPLE_REGISTERS)
|
||||
|
||||
self.function_combo.currentIndexChanged.connect(self.update_function_description)
|
||||
function_layout.addRow(QLabel("功能码:"), self.function_combo)
|
||||
|
||||
self.function_description = QLabel()
|
||||
function_layout.addRow(QLabel("功能描述:"), self.function_description)
|
||||
|
||||
self.device_edit = QLineEdit()
|
||||
self.device_edit.setPlaceholderText("输入设备地址")
|
||||
function_layout.addRow(QLabel("设备地址:"), self.device_edit)
|
||||
|
||||
self.address_edit = QLineEdit()
|
||||
self.address_edit.setPlaceholderText("输入寄存器地址")
|
||||
function_layout.addRow(QLabel("寄存器地址:"), self.address_edit)
|
||||
|
||||
self.value_edit = QLineEdit()
|
||||
self.value_edit.setPlaceholderText("输入写入值")
|
||||
function_layout.addRow(QLabel("写入值:"), self.value_edit)
|
||||
|
||||
self.execute_button = QPushButton("执行")
|
||||
self.execute_button.clicked.connect(self.execute_action)
|
||||
function_layout.addRow(self.execute_button)
|
||||
|
||||
function_group.setLayout(function_layout)
|
||||
main_layout.addWidget(function_group)
|
||||
|
||||
# 数据部分
|
||||
data_group = QGroupBox("数据")
|
||||
data_layout = QVBoxLayout()
|
||||
|
||||
self.data_table = QTableWidget()
|
||||
self.data_table.setColumnCount(2)
|
||||
self.data_table.setHorizontalHeaderLabels(["地址", "值"])
|
||||
data_layout.addWidget(self.data_table)
|
||||
|
||||
data_group.setLayout(data_layout)
|
||||
main_layout.addWidget(data_group)
|
||||
|
||||
# 实时监视部分
|
||||
monitor_group = QGroupBox("实时监视")
|
||||
monitor_layout = QFormLayout()
|
||||
self.monitor_interval_edit = QSpinBox()
|
||||
self.monitor_interval_edit.setRange(100, 5000) # 设置监视间隔范围
|
||||
self.monitor_interval_edit.setValue(1000) # 设置默认值
|
||||
monitor_layout.addRow(QLabel("监视间隔(ms):"), self.monitor_interval_edit)
|
||||
|
||||
self.start_address_edit = QLineEdit()
|
||||
self.start_address_edit.setPlaceholderText("输入开始地址")
|
||||
monitor_layout.addRow(QLabel("监视开始地址:"), self.start_address_edit)
|
||||
|
||||
self.length_edit = QSpinBox()
|
||||
self.length_edit.setRange(1, 100)
|
||||
monitor_layout.addRow(QLabel("长度:"), self.length_edit)
|
||||
|
||||
self.monitor_button = QPushButton("开始监视")
|
||||
self.monitor_button.clicked.connect(self.toggle_monitoring)
|
||||
monitor_layout.addRow(self.monitor_button)
|
||||
|
||||
monitor_group.setLayout(monitor_layout)
|
||||
main_layout.addWidget(monitor_group)
|
||||
|
||||
self.update_function_description()
|
||||
|
||||
def toggle_mode(self):
|
||||
mode = self.mode_combo.currentData()
|
||||
if mode == "TCP":
|
||||
self.ip_edit.setPlaceholderText("输入 IP 地址")
|
||||
self.port_edit.setPlaceholderText("输入端口号")
|
||||
elif mode == "RTU":
|
||||
self.ip_edit.setPlaceholderText("输入串口端口")
|
||||
self.port_edit.setPlaceholderText("输入波特率")
|
||||
|
||||
def connect_modbus(self):
|
||||
mode = self.mode_combo.currentData()
|
||||
try:
|
||||
if mode == "TCP":
|
||||
ip = self.ip_edit.text()
|
||||
port = int(self.port_edit.text())
|
||||
self.master = modbus_tcp.TcpMaster(ip, port)
|
||||
self.master.open()
|
||||
elif mode == "RTU":
|
||||
port = self.ip_edit.text()
|
||||
baudrate = int(self.port_edit.text())
|
||||
self.master = modbus_rtu.RtuMaster(serial.Serial(port, baudrate))
|
||||
self.master.open()
|
||||
QMessageBox.information(self, "连接", f"成功连接到 Modbus {mode} 设备。")
|
||||
self.connect_button.setEnabled(False)
|
||||
self.disconnect_button.setEnabled(True)
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "连接错误", f"无法连接到 Modbus 设备: {e}")
|
||||
|
||||
def disconnect_modbus(self):
|
||||
if self.master:
|
||||
self.master.close()
|
||||
self.master = None
|
||||
QMessageBox.information(self, "断开连接", "已断开与 Modbus 设备的连接。")
|
||||
self.connect_button.setEnabled(True)
|
||||
self.disconnect_button.setEnabled(False)
|
||||
|
||||
def update_function_description(self):
|
||||
descriptions = {
|
||||
READ_COILS: "读线圈状态",
|
||||
READ_DISCRETE_INPUTS: "读离散输入状态",
|
||||
READ_HOLDING_REGISTERS: "读保持寄存器值",
|
||||
READ_INPUT_REGISTERS: "读输入寄存器值",
|
||||
WRITE_SINGLE_COIL: "写单个位",
|
||||
WRITE_SINGLE_REGISTER: "写单个寄存器",
|
||||
WRITE_MULTIPLE_REGISTERS: "写多个寄存器",
|
||||
}
|
||||
function_code = self.function_combo.currentData()
|
||||
self.function_description.setText(descriptions.get(function_code, ""))
|
||||
|
||||
def execute_action(self):
|
||||
if not self.master:
|
||||
QMessageBox.warning(self, "连接", "请先连接到 Modbus 设备。")
|
||||
return
|
||||
|
||||
function_code = self.function_combo.currentData()
|
||||
device = int(self.device_edit.text())
|
||||
address = int(self.address_edit.text())
|
||||
|
||||
try:
|
||||
if function_code in [READ_COILS, READ_DISCRETE_INPUTS]:
|
||||
response = self.master.execute(device, function_code, address, 1)
|
||||
data = response[0]
|
||||
self.display_data({address: data})
|
||||
elif function_code in [READ_HOLDING_REGISTERS, READ_INPUT_REGISTERS]:
|
||||
response = self.master.execute(device, function_code, address, 1)
|
||||
data = response[0]
|
||||
self.display_data({address: data})
|
||||
elif function_code == WRITE_SINGLE_COIL:
|
||||
value = int(self.value_edit.text())
|
||||
if value not in [0, 1]:
|
||||
raise ValueError("写线圈的值必须为0或1")
|
||||
self.master.execute(device, function_code, address, output_value=value)
|
||||
QMessageBox.information(self, "写入", "写入操作成功完成。")
|
||||
elif function_code == WRITE_SINGLE_REGISTER:
|
||||
value = int(self.value_edit.text())
|
||||
self.master.execute(device, function_code, address, output_value=value)
|
||||
QMessageBox.information(self, "写入", "写入操作成功完成。")
|
||||
elif function_code == WRITE_MULTIPLE_REGISTERS:
|
||||
values_str = self.value_edit.text()
|
||||
values = [int(val) for val in values_str.split(",")]
|
||||
self.master.execute(device, function_code, address, output_value=values)
|
||||
QMessageBox.information(self, "写入", "写入操作成功完成。")
|
||||
except Exception as e:
|
||||
QMessageBox.critical(self, "错误", f"执行操作时出错: {e}")
|
||||
|
||||
def display_data(self, data):
|
||||
# 批量更新表格
|
||||
self.data_table.setRowCount(len(data))
|
||||
for i, (address, value) in enumerate(data.items()):
|
||||
self.data_table.setItem(i, 0, QTableWidgetItem(str(address)))
|
||||
self.data_table.setItem(i, 1, QTableWidgetItem(str(value)))
|
||||
|
||||
def toggle_monitoring(self):
|
||||
if self.timer.isActive():
|
||||
self.timer.stop()
|
||||
self.monitor_button.setText("开始监视")
|
||||
else:
|
||||
self.timer.start(self.monitor_interval_edit.value())
|
||||
self.monitor_button.setText("停止监视")
|
||||
|
||||
def update_data(self):
|
||||
if not self.master:
|
||||
self.timer.stop()
|
||||
self.monitor_button.setText("开始监视")
|
||||
QMessageBox.warning(self, "连接", "请先连接到 Modbus 设备。")
|
||||
return
|
||||
|
||||
try:
|
||||
device = int(self.device_edit.text())
|
||||
start_address = int(self.start_address_edit.text())
|
||||
length = self.length_edit.value()
|
||||
|
||||
response = self.master.execute(device, READ_HOLDING_REGISTERS, start_address, length)
|
||||
|
||||
# 将数据转换为地址-值对的字典
|
||||
data = {start_address + i: val for i, val in enumerate(response)}
|
||||
|
||||
# 更新表格
|
||||
self.display_data(data)
|
||||
except Exception as e:
|
||||
self.timer.stop()
|
||||
self.monitor_button.setText("开始监视")
|
||||
QMessageBox.critical(self, "错误", f"读取数据时出错: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
app.setFont(QFont("Arial", 10)) # 设置全局字体
|
||||
window = ModbusClient()
|
||||
window.show()
|
||||
app.exec_()
|
||||
Reference in New Issue
Block a user