0728更新 添加公网服务器远程模式

main
zcwBit 3 months ago
parent 25c76a2961
commit 84e9eb0650

@ -70,8 +70,8 @@ class RemoteConnectSettingWidget(QWidget):
# 从站切换到主站,关闭客户端
from utils import Globals
protocolManage = Globals.getValue('protocolManage')
if protocolManage and hasattr(protocolManage, 'closeClent'):
protocolManage.closeClent()
if protocolManage and hasattr(protocolManage, 'closeClient'):
protocolManage.closeClient()
# 移除当前Widget
self.contentLayout.removeWidget(self.currentWidget)
self.currentWidget.hide()

@ -1,8 +1,13 @@
import sys
import socket
import threading
import requests
import json
import time
import uuid
from PyQt5.QtWidgets import QMainWindow, QVBoxLayout, QPushButton, \
QListWidget, QLabel, QWidget, QMessageBox, QTextEdit
QListWidget, QLabel, QWidget, QMessageBox, QTextEdit, \
QLineEdit, QHBoxLayout, QRadioButton, QButtonGroup, QGroupBox
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QObject, QTimer
from PyQt5.QtNetwork import QNetworkInterface,QAbstractSocket
from datetime import datetime
@ -14,6 +19,7 @@ class TcpClient(object):
# 配置信息
self.udpPort = 54321
self.broadcastAddr = '<broadcast>'
def discoverServers(self):
# print("[*] 正在扫描局域网设备...")
@ -106,14 +112,23 @@ class DeviceMasterWidget(QMainWindow):
self.statusLabel = None
self.infoLabel = None
self.disconnectList = None
self.publicServerInput = None
self.publicClientList = None
self.publicClients = []
self.autoRefreshEnabled = False
self.initUI()
# 定时器启动放到控件初始化后
self.statusTimer = QTimer(self)
self.statusTimer.timeout.connect(self.updateStatusInfo)
self.statusTimer.start(1000)
# 公网客户端自动刷新定时器
self.publicRefreshTimer = QTimer(self)
self.publicRefreshTimer.timeout.connect(self.autoRefreshPublicClients)
def initUI(self):
self.setWindowTitle('局域网设备发现')
self.setWindowTitle('设备连接管理')
# self.setGeometry(1000, 500, 400, 300)
# 主窗口部件
@ -125,6 +140,7 @@ class DeviceMasterWidget(QMainWindow):
layout.addWidget(self.statusLabel)
self.infoLabel = QLabel('连接信息: 无')
layout.addWidget(self.infoLabel)
# 新增:断开客户端区
self.disconnectList = QListWidget()
self.disconnectList.setFixedHeight(60)
@ -132,29 +148,87 @@ class DeviceMasterWidget(QMainWindow):
layout.addWidget(self.disconnectList)
self.disconnectList.itemClicked.connect(self.disconnectClient)
# 公网服务端配置区域
publicGroup = QGroupBox('公网服务端配置')
publicLayout = QVBoxLayout()
# 服务器地址输入
serverLayout = QHBoxLayout()
serverLayout.addWidget(QLabel('服务器地址:'))
self.publicServerInput = QLineEdit('43.138.48.181')
self.publicServerInput.setPlaceholderText('输入公网服务器IP地址')
serverLayout.addWidget(self.publicServerInput)
publicLayout.addLayout(serverLayout)
# 启动公网服务端按钮
self.publicConnectButton = QPushButton('启动公网服务端模式')
self.publicConnectButton.clicked.connect(self.startPublicServer)
self.publicConnectButton.setObjectName('setButton')
publicLayout.addWidget(self.publicConnectButton)
# 公网客户端扫描区域
publicLayout.addWidget(QLabel('公网服务器客户端列表:'))
self.publicClientList = QListWidget()
self.publicClientList.setFixedHeight(100)
self.publicClientList.itemDoubleClicked.connect(self.connectToPublicClient)
publicLayout.addWidget(self.publicClientList)
# 扫描和连接按钮
publicButtonLayout = QHBoxLayout()
self.scanPublicButton = QPushButton('扫描公网客户端')
self.scanPublicButton.clicked.connect(self.scanPublicClients)
self.scanPublicButton.setObjectName('setButton')
publicButtonLayout.addWidget(self.scanPublicButton)
self.connectPublicButton = QPushButton('连接选中客户端')
self.connectPublicButton.clicked.connect(self.connectToPublicClient)
self.connectPublicButton.setEnabled(False)
self.connectPublicButton.setObjectName('setButton')
publicButtonLayout.addWidget(self.connectPublicButton)
publicLayout.addLayout(publicButtonLayout)
# 自动刷新选项
from PyQt5.QtWidgets import QCheckBox
self.autoRefreshCheckBox = QCheckBox('自动刷新客户端列表 (5秒)')
self.autoRefreshCheckBox.stateChanged.connect(self.toggleAutoRefresh)
publicLayout.addWidget(self.autoRefreshCheckBox)
publicGroup.setLayout(publicLayout)
layout.addWidget(publicGroup)
self.ipAddresslabel = QLabel(self)
self.ipAddresslabel.setObjectName("setlabel")
self.ipAddresslabel.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
layout.addWidget(self.ipAddresslabel)
# 局域网设备发现区域
lanGroup = QGroupBox('局域网设备发现')
lanLayout = QVBoxLayout()
# 标题标签
self.titleLabel = QLabel('点击"扫描设备"按钮发现局域网中的设备')
layout.addWidget(self.titleLabel)
lanLayout.addWidget(self.titleLabel)
# 设备列表
self.deviceList = QListWidget()
layout.addWidget(self.deviceList)
lanLayout.addWidget(self.deviceList)
lanLayout2 = QHBoxLayout()
self.scanButton = QPushButton('扫描设备')
self.scanButton.clicked.connect(self.startDiscovery)
layout.addWidget(self.scanButton)
self.scanButton.setObjectName('setButton')
lanLayout2.addWidget(self.scanButton)
self.connectButton = QPushButton('连接设备')
self.connectButton.clicked.connect(self.connectToDevice)
self.connectButton.setEnabled(False)
self.connectButton.setObjectName('setButton')
layout.addWidget(self.connectButton)
lanLayout2.addWidget(self.connectButton)
lanLayout.addLayout(lanLayout2)
lanGroup.setLayout(lanLayout)
layout.addWidget(lanGroup)
self.ipAddresslabel.setText('当前设备IP地址: ' + self.getLocalIp())
@ -191,6 +265,176 @@ class DeviceMasterWidget(QMainWindow):
self.connectButton.setEnabled(True)
def startPublicServer(self):
"""启动公网服务端模式"""
serverIp = self.publicServerInput.text().strip()
if not serverIp:
QMessageBox.warning(self, '警告', '请输入服务器地址')
return
try:
# 设置为服务端模式
Globals.getValue('protocolManage').closeClient()
Globals.getValue('protocolManage').setServerMode(rabbitmqHost=serverIp)
QMessageBox.information(self, '成功', f'已启动公网服务端模式\n服务器地址: {serverIp}')
except Exception as e:
QMessageBox.critical(self, '失败', f'启动公网服务端模式失败: {str(e)}')
def scanPublicClients(self):
"""扫描公网服务器上的客户端"""
serverIp = self.publicServerInput.text().strip()
if not serverIp:
QMessageBox.warning(self, '警告', '请输入服务器地址')
return
try:
# 通过RabbitMQ管理API获取所有RPC队列
url = f"http://{serverIp}:15672/api/queues"
response = requests.get(url, auth=('dcs', '123456'), timeout=10)
queues = response.json()
# 筛选RPC客户端队列
rpc_queues = []
for queue in queues:
queue_name = queue['name']
if queue_name.startswith('rpc_queue_client'):
client_name = queue_name.replace('rpc_queue_', '')
rpc_queues.append({
'name': client_name,
'queue': queue_name,
'messages': queue.get('messages', 0),
'consumers': queue.get('consumers', 0),
'status': 'online' if queue.get('consumers', 0) > 0 else 'offline'
})
# 更新客户端列表
self.publicClients = rpc_queues
self.publicClientList.clear()
if not rpc_queues:
self.publicClientList.addItem('未发现任何客户端')
self.connectPublicButton.setEnabled(False)
else:
for client in rpc_queues:
status_text = "在线" if client['status'] == 'online' else "离线"
item_text = f"{client['name']} ({status_text}) - 消息:{client['messages']}"
self.publicClientList.addItem(item_text)
self.connectPublicButton.setEnabled(True)
QMessageBox.information(self, '扫描完成', f'发现 {len(rpc_queues)} 个客户端')
except Exception as e:
QMessageBox.critical(self, '扫描失败', f'扫描公网客户端失败: {str(e)}')
self.publicClientList.clear()
self.publicClientList.addItem('扫描失败')
self.connectPublicButton.setEnabled(False)
def connectToPublicClient(self):
"""连接到选中的公网客户端"""
selectedIndex = self.publicClientList.currentRow()
if selectedIndex == -1:
QMessageBox.warning(self, '警告', '请先选择一个客户端')
return
if selectedIndex >= len(self.publicClients):
QMessageBox.warning(self, '警告', '选择的客户端无效')
return
selectedClient = self.publicClients[selectedIndex]
clientName = selectedClient['name']
# 检查客户端状态
# if selectedClient['status'] == 'offline':
# reply = QMessageBox.question(self, '客户端离线',
# f'客户端 {clientName} 当前离线,是否仍要尝试连接?',
# QMessageBox.Yes | QMessageBox.No)
# if reply == QMessageBox.No:
# return
try:
# 检查是否已经连接
protocolManage = Globals.getValue('protocolManage')
if protocolManage and hasattr(protocolManage, 'RpcServer') and protocolManage.RpcServer:
existing_clients = protocolManage.RpcServer.getClientNames()
if clientName in existing_clients:
QMessageBox.information(self, '提示', f'客户端 {clientName} 已连接,无需重复连接')
return
# 尝试添加客户端
if protocolManage.RpcServer.addClient(clientName):
QMessageBox.information(self, '连接成功', f'已成功连接到客户端 {clientName}')
# 刷新状态信息
self.updateStatusInfo()
else:
QMessageBox.warning(self, '连接失败', f'无法连接到客户端 {clientName},客户端可能不在线')
else:
QMessageBox.warning(self, '错误', '请先启动公网服务端模式')
except Exception as e:
QMessageBox.critical(self, '连接失败', f'连接客户端失败: {str(e)}')
def autoRefreshPublicClients(self):
"""自动刷新公网客户端列表(静默模式)"""
if not self.autoRefreshEnabled:
return
serverIp = self.publicServerInput.text().strip()
if not serverIp:
return
try:
# 静默扫描,不显示消息框
url = f"http://{serverIp}:15672/api/queues"
response = requests.get(url, auth=('dcs', '123456'), timeout=5)
queues = response.json()
# 筛选RPC客户端队列
rpc_queues = []
for queue in queues:
queue_name = queue['name']
if queue_name.startswith('rpc_queue_client'):
client_name = queue_name.replace('rpc_queue_', '')
rpc_queues.append({
'name': client_name,
'queue': queue_name,
'messages': queue.get('messages', 0),
'consumers': queue.get('consumers', 0),
'status': 'online' if queue.get('consumers', 0) > 0 else 'offline'
})
# 更新客户端列表(保持选中状态)
current_selection = self.publicClientList.currentRow()
self.publicClients = rpc_queues
self.publicClientList.clear()
if not rpc_queues:
self.publicClientList.addItem('未发现任何客户端')
self.connectPublicButton.setEnabled(False)
else:
for client in rpc_queues:
status_text = "在线" if client['status'] == 'online' else "离线"
item_text = f"{client['name']} ({status_text}) - 消息:{client['messages']}"
self.publicClientList.addItem(item_text)
self.connectPublicButton.setEnabled(True)
# 恢复选中状态
if 0 <= current_selection < len(rpc_queues):
self.publicClientList.setCurrentRow(current_selection)
except Exception as e:
# 静默处理错误,不显示消息框
print(f"自动刷新公网客户端失败: {e}")
def toggleAutoRefresh(self, state):
"""切换自动刷新状态"""
self.autoRefreshEnabled = state == 2 # Qt.Checked = 2
if self.autoRefreshEnabled:
self.publicRefreshTimer.start(5000) # 每5秒刷新一次
print("已启用公网客户端自动刷新")
else:
self.publicRefreshTimer.stop()
print("已禁用公网客户端自动刷新")
def connectToDevice(self):
"""连接选中的设备"""
selectedIndex = self.deviceList.currentRow()
@ -418,13 +662,14 @@ class TcpServer(QObject):
class DeviceSlaveWidget(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("TCP/UDP 服务器控制")
self.setWindowTitle("设备从站控制")
# self.setGeometry(100, 100, 600, 400)
self.server = TcpServer()
self.server.updateSignal.connect(self.updateLog)
self.statusLabel = None
self.infoLabel = None
self.publicServerInput = None
self.initUI()
# 定时器启动放到控件初始化后
self.statusTimer = QTimer(self)
@ -440,20 +685,42 @@ class DeviceSlaveWidget(QMainWindow):
self.infoLabel = QLabel('连接信息: 无')
mainLayout.addWidget(self.infoLabel)
# 控制按钮
# self.tcpButton = QPushButton("启动 TCP 服务器")
# self.tcpButton.clicked.connect(self.toggleTcpServer)
# self.tcpButton.setObjectName('setButton')
# 公网客户端连接配置
publicGroup = QGroupBox('公网客户端连接')
publicLayout = QVBoxLayout()
# 服务器地址输入
serverLayout = QHBoxLayout()
serverLayout.addWidget(QLabel('服务器地址:'))
self.publicServerInput = QLineEdit('43.138.48.181')
self.publicServerInput.setPlaceholderText('输入公网服务器IP地址')
serverLayout.addWidget(self.publicServerInput)
publicLayout.addLayout(serverLayout)
# 公网客户端连接按钮
self.publicConnectButton = QPushButton('连接公网服务器')
self.publicConnectButton.clicked.connect(self.connectToPublicServer)
self.publicConnectButton.setObjectName('setButton')
publicLayout.addWidget(self.publicConnectButton)
publicGroup.setLayout(publicLayout)
mainLayout.addWidget(publicGroup)
# 局域网模式控制
lanGroup = QGroupBox('局域网通讯模式')
lanLayout = QVBoxLayout()
self.udpButton = QPushButton("开启远程通讯模式")
self.udpButton.clicked.connect(self.toggleUdpServer)
self.udpButton.setObjectName('setButton')
self.udpButton.setObjectName('setButton')
lanLayout.addWidget(self.udpButton)
lanGroup.setLayout(lanLayout)
mainLayout.addWidget(lanGroup)
# 日志显示
self.logDisplay = QTextEdit()
self.logDisplay.setReadOnly(True)
# 添加到布局
# mainLayout.addWidget(self.tcpButton)
mainLayout.addWidget(self.udpButton)
mainLayout.addWidget(QLabel("服务器日志:"))
mainLayout.addWidget(self.logDisplay)
@ -470,6 +737,27 @@ class DeviceSlaveWidget(QMainWindow):
self.server.startTcpServer()
# print("启动 TCP 服务器")
def connectToPublicServer(self):
"""连接到公网服务器作为客户端"""
serverIp = self.publicServerInput.text().strip()
if not serverIp:
QMessageBox.warning(self, '警告', '请输入服务器地址')
return
# try:
# 获取客户端名称
from protocol.RPC.RpcClient import RpcClient
clientName = RpcClient.getNextClientNameFromRabbitMQ(serverIp)
print(f"获取到客户端名称: {clientName}")
# 设置为客户端模式
Globals.getValue('protocolManage').closeServer()
Globals.getValue('protocolManage').setClentMode(clientName, rabbitmqHost=serverIp)
QMessageBox.information(self, '连接成功', f'已成功连接到公网服务器 {serverIp}\n客户端名称: {clientName}')
# except Exception as e:
# QMessageBox.critical(self, '连接失败', f'连接公网服务器失败: {str(e)}')
def toggleUdpServer(self):
if self.server.udpRunning:
self.server.stopUdpServer()

@ -99,7 +99,7 @@ class ProtocolManage(object):
# 使用非阻塞方式启动RPC客户端
self.RpcClient.startNonBlocking()
def closeClent(self):
def closeClient(self):
if self.RpcClient:
self.RpcClient.close()
self.RpcClient = None
@ -297,6 +297,8 @@ class ProtocolManage(object):
self.tcpVarManager.shutdown()
# 关闭所有Modbus通讯
self.modbusManager.stopAllModbus()
self.closeClient()
self.closeServer()
# 关闭后台读取线程
if hasattr(self, 'readThreadStop') and hasattr(self, 'readThread'):
self.readThreadStop.set()

@ -3,6 +3,7 @@ import uuid
import json
import threading
import socket
import requests
class RpcClient:
def __init__(self, clientName, rabbitHost='localhost', protocolManager=None):
@ -63,7 +64,7 @@ class RpcClient:
:param request: 请求数据
:return: 响应数据
"""
varName = request.get("varName")
varName = '.'.join(request.get("varName").split('.')[:-1])
value = request.get("value")
# 如果有协议管理器,使用它来处理写入
@ -95,7 +96,12 @@ class RpcClient:
min = 0
max = 1
variableName = variableName + '.' + self.clientName
self.variables[variableName]["value"] = value + '.' + self.clientName
# 确保变量条目存在,如果不存在则创建
if variableName not in self.variables:
self.variables[variableName] = {}
self.variables[variableName]["value"] = str(value)
self.variables[variableName]["min"] = min
self.variables[variableName]["max"] = max
self.variables[variableName]["type"] = type
@ -122,6 +128,7 @@ class RpcClient:
"""关闭RPC连接"""
try:
if self.channel and not self.channel.is_closed:
self.channel.queue_delete(queue=self.queueName)
self.channel.close()
if self.connection and not self.connection.is_closed:
self.connection.close()
@ -138,7 +145,46 @@ class RpcClient:
s.close()
return ip
except:
return '127.0.0.1'
return '127.0.0.1'\
@classmethod
def getNextClientNameFromRabbitMQ(cls, rabbitHost, username='dcs', password='123456'):
# try:
# 获取所有队列
url = f"http://{rabbitHost}:15672/api/queues"
response = requests.get(url, auth=(username, password), timeout=5)
queues = response.json()
print(queues)
# 筛选RPC队列
if not queues:
return "client1"
rpcQueues = [q['name'] for q in queues if q['name'].startswith('rpc_queue_client')]
# 提取客户端编号
clientNumbers = []
for queueName in rpcQueues:
# rpc_queue_client1 -> client1 -> 1
if queueName.startswith('rpc_queue_client'):
try:
numberStr = queueName.replace('rpc_queue_client', '')
if numberStr.isdigit():
clientNumbers.append(int(numberStr))
except:
continue
# 返回下一个可用编号
if not clientNumbers:
return "client1"
else:
nextNumber = max(clientNumbers) + 1
return f"client{nextNumber}"
# except Exception as e:
# print(f"从RabbitMQ获取客户端名称失败: {e}")
# # 降级方案:使用时间戳
# import time
# timestamp = int(time.time() % 10000)
# return f"client{timestamp}"
if __name__ == "__main__":
import sys

Loading…
Cancel
Save