feat: 修复一些已知问题

This commit is contained in:
zhu-mengmeng 2025-06-30 09:58:16 +08:00
parent 11c6b15d3e
commit 6d60e77743
10 changed files with 636 additions and 205 deletions

View File

@ -3,7 +3,7 @@
"name": "腾智微丝产线包装系统", "name": "腾智微丝产线包装系统",
"version": "1.0.0", "version": "1.0.0",
"features": { "features": {
"enable_serial_ports": false, "enable_serial_ports": true,
"enable_keyboard_listener": false, "enable_keyboard_listener": false,
"enable_camera": false "enable_camera": false
}, },
@ -49,8 +49,8 @@
"default_framerate": 30 "default_framerate": 30
}, },
"modbus": { "modbus": {
"host": "192.168.2.88", "host": "localhost",
"port": "502" "port": "5020"
}, },
"serial": { "serial": {
"keyboard": { "keyboard": {
@ -78,6 +78,15 @@
"stable_threshold": 10, "stable_threshold": 10,
"stop_bits": 1, "stop_bits": 1,
"timeout": 1 "timeout": 1
},
"scanner": {
"code": "scanner",
"data_bits": 8,
"parity": "N",
"port": "9600",
"ser": "COM3",
"stop_bits": 1,
"timeout": 1
} }
}, },
"electricity": { "electricity": {

Binary file not shown.

View File

@ -3,19 +3,19 @@ import time
client = ModbusTcpClient('localhost', port=5020) client = ModbusTcpClient('localhost', port=5020)
client.connect() client.connect()
client.write_registers(address=11, values=[2248]) client.write_registers(address=11, values=[2248])
client.write_registers(address=13, values=[0]) client.write_registers(address=3, values=[0])
client.write_registers(address=21, values=[0]) # client.write_registers(address=20, values=[0])
time.sleep(2) # time.sleep(2)
# client.write_registers(address=21, values=[1]) # client.write_registers(address=20, values=[1])
# client.write_registers(address=30, values=[25]) # client.write_registers(address=30, values=[25])
# client.write_registers(address=5, values=[16]) # client.write_registers(address=5, values=[16])
# 贴标完成 # 贴标完成
# client.write_registers(address=24, values=[1])s # client.write_registers(address=24, values=[1])s
client.write_registers(address=13, values=[1]) # client.write_registers(address=13, values=[1])
result = client.read_holding_registers(address=1, count=1) result = client.read_holding_registers(address=3, count=1)
print(result.registers[0],"123===") print(result.registers[0],"123===")
client.close() client.close()

View File

@ -28,7 +28,7 @@ class SerialSettingsUI(QWidget):
enable_layout.addStretch() enable_layout.addStretch()
main_layout.addLayout(enable_layout) main_layout.addLayout(enable_layout)
# 创建串口设置组 # # 创建串口设置组
serial_group = QGroupBox("串口设置") serial_group = QGroupBox("串口设置")
serial_layout = QGridLayout(serial_group) serial_layout = QGridLayout(serial_group)
@ -39,6 +39,7 @@ class SerialSettingsUI(QWidget):
# 串口选择 # 串口选择
mdz_port_layout = QHBoxLayout() mdz_port_layout = QHBoxLayout()
self.mdz_port_combo = QComboBox() self.mdz_port_combo = QComboBox()
self.mdz_port_combo.addItem("不使用", "") # 添加空选项
self.mdz_refresh_btn = QPushButton("刷新") self.mdz_refresh_btn = QPushButton("刷新")
mdz_port_layout.addWidget(self.mdz_port_combo) mdz_port_layout.addWidget(self.mdz_port_combo)
mdz_port_layout.addWidget(self.mdz_refresh_btn) mdz_port_layout.addWidget(self.mdz_refresh_btn)
@ -85,6 +86,7 @@ class SerialSettingsUI(QWidget):
# 串口选择 # 串口选择
xj_port_layout = QHBoxLayout() xj_port_layout = QHBoxLayout()
self.xj_port_combo = QComboBox() self.xj_port_combo = QComboBox()
self.xj_port_combo.addItem("不使用", "") # 添加空选项
self.xj_refresh_btn = QPushButton("刷新") self.xj_refresh_btn = QPushButton("刷新")
xj_port_layout.addWidget(self.xj_port_combo) xj_port_layout.addWidget(self.xj_port_combo)
xj_port_layout.addWidget(self.xj_refresh_btn) xj_port_layout.addWidget(self.xj_refresh_btn)
@ -114,10 +116,47 @@ class SerialSettingsUI(QWidget):
self.xj_parity_combo.addItem(parity[0], parity[1]) self.xj_parity_combo.addItem(parity[0], parity[1])
xj_layout.addRow("校验位:", self.xj_parity_combo) xj_layout.addRow("校验位:", self.xj_parity_combo)
# 扫码器串口设置
scanner_group = QGroupBox("扫码器串口")
scanner_layout = QFormLayout(scanner_group)
# 串口选择
scanner_port_layout = QHBoxLayout()
self.scanner_port_combo = QComboBox()
self.scanner_port_combo.addItem("不使用", "") # 添加空选项
self.scanner_refresh_btn = QPushButton("刷新")
scanner_port_layout.addWidget(self.scanner_port_combo)
scanner_port_layout.addWidget(self.scanner_refresh_btn)
scanner_layout.addRow("串口:", scanner_port_layout)
# 波特率
self.scanner_baud_combo = QComboBox()
for baud in ["1200", "2400", "4800", "9600", "19200", "38400", "57600", "115200"]:
self.scanner_baud_combo.addItem(baud)
scanner_layout.addRow("波特率:", self.scanner_baud_combo)
# 数据位
self.scanner_data_bits_combo = QComboBox()
for bits in ["5", "6", "7", "8"]:
self.scanner_data_bits_combo.addItem(bits)
scanner_layout.addRow("数据位:", self.scanner_data_bits_combo)
# 停止位
self.scanner_stop_bits_combo = QComboBox()
for bits in ["1", "1.5", "2"]:
self.scanner_stop_bits_combo.addItem(bits)
scanner_layout.addRow("停止位:", self.scanner_stop_bits_combo)
# 校验位
self.scanner_parity_combo = QComboBox()
for parity in [("无校验", "N"), ("奇校验", "O"), ("偶校验", "E")]:
self.scanner_parity_combo.addItem(parity[0], parity[1])
scanner_layout.addRow("校验位:", self.scanner_parity_combo)
# 将三个组添加到布局 # 将三个组添加到布局
serial_layout.addWidget(mdz_group, 0, 0) serial_layout.addWidget(mdz_group, 0, 0)
serial_layout.addWidget(xj_group, 0, 1) serial_layout.addWidget(xj_group, 0, 1)
serial_layout.addWidget(scanner_group, 1, 0) # 添加扫码器串口设置,放在第二行第一列
# 设置列伸缩因子使两列等宽比例1:1 # 设置列伸缩因子使两列等宽比例1:1
serial_layout.setColumnStretch(0, 1) serial_layout.setColumnStretch(0, 1)
@ -129,8 +168,10 @@ class SerialSettingsUI(QWidget):
test_layout = QHBoxLayout() test_layout = QHBoxLayout()
self.test_mdz_btn = QPushButton("测试米电阻串口") self.test_mdz_btn = QPushButton("测试米电阻串口")
self.test_xj_btn = QPushButton("测试线径串口") self.test_xj_btn = QPushButton("测试线径串口")
self.test_scanner_btn = QPushButton("测试扫码器串口") # 添加测试扫码器串口按钮
test_layout.addWidget(self.test_mdz_btn) test_layout.addWidget(self.test_mdz_btn)
test_layout.addWidget(self.test_xj_btn) test_layout.addWidget(self.test_xj_btn)
test_layout.addWidget(self.test_scanner_btn) # 添加到布局
test_layout.addStretch() test_layout.addStretch()
main_layout.addLayout(test_layout) main_layout.addLayout(test_layout)

View File

@ -486,22 +486,22 @@ class SettingsUI(QWidget):
self.tab_widget.addTab(self.param_tab, "参数配置") self.tab_widget.addTab(self.param_tab, "参数配置")
def create_electricity_tab(self): def create_electricity_tab(self):
"""创建电监控选项卡""" """创建电监控选项卡"""
# 电监控选项卡 # 电监控选项卡
self.electricity_tab = QWidget() self.electricity_tab = QWidget()
self.electricity_layout = QVBoxLayout(self.electricity_tab) self.electricity_layout = QVBoxLayout(self.electricity_tab)
self.electricity_layout.setContentsMargins(0, 0, 0, 0) self.electricity_layout.setContentsMargins(0, 0, 0, 0)
self.electricity_layout.setSpacing(0) self.electricity_layout.setSpacing(0)
# 添加一个临时提示标签表示此处将由ElectricitySettingsUI替换 # 添加一个临时提示标签表示此处将由ElectricitySettingsUI替换
self.electricity_placeholder = QLabel("正在加载电监控设置...") self.electricity_placeholder = QLabel("正在加载电监控设置...")
self.electricity_placeholder.setFont(self.normal_font) self.electricity_placeholder.setFont(self.normal_font)
self.electricity_placeholder.setAlignment(Qt.AlignCenter) self.electricity_placeholder.setAlignment(Qt.AlignCenter)
self.electricity_placeholder.setStyleSheet("color: #888888; padding: 20px;") self.electricity_placeholder.setStyleSheet("color: #888888; padding: 20px;")
self.electricity_layout.addWidget(self.electricity_placeholder) self.electricity_layout.addWidget(self.electricity_placeholder)
# 添加到选项卡 # 添加到选项卡
self.tab_widget.addTab(self.electricity_tab, "监控") self.tab_widget.addTab(self.electricity_tab, "监控")
def create_pallet_type_tab(self): def create_pallet_type_tab(self):
# 托盘类型设置选项卡 # 托盘类型设置选项卡

View File

@ -54,7 +54,8 @@ class SerialManager:
self.data = { self.data = {
'mdz': 0, 'mdz': 0,
'xj': 0, # 添加线径数据 'xj': 0, # 添加线径数据
'cz': 0 'cz': 0,
'scanner': '' # 添加扫码器数据
} }
# 是否自动查询米电阻数据默认为False只通过PageUp键触发 # 是否自动查询米电阻数据默认为False只通过PageUp键触发
@ -102,6 +103,7 @@ class SerialManager:
self.mdz_config = self.config.get_config('mdz') self.mdz_config = self.config.get_config('mdz')
self.cz_config = self.config.get_config('cz') self.cz_config = self.config.get_config('cz')
self.xj_config = self.config.get_config('xj') # 添加线径配置 self.xj_config = self.config.get_config('xj') # 添加线径配置
self.scanner_config = self.config.get_config('scanner') # 添加扫码器配置
# 检查操作系统类型在macOS上处理COM端口名称问题 # 检查操作系统类型在macOS上处理COM端口名称问题
os_type = platform.system() os_type = platform.system()
@ -118,7 +120,7 @@ class SerialManager:
# 检查是否自动查询米电阻数据 # 检查是否自动查询米电阻数据
self.auto_query_mdz = self.config.get_value('serial.keyboard.auto_query', False) self.auto_query_mdz = self.config.get_value('serial.keyboard.auto_query', False)
logging.info(f"已加载串口配置mdz={self.mdz_config}, xj={self.xj_config}, cz={self.cz_config}, data_file={self.data_file}") logging.info(f"已加载串口配置mdz={self.mdz_config}, xj={self.xj_config}, cz={self.cz_config}, scanner={self.scanner_config}, data_file={self.data_file}")
logging.info(f"米电阻自动查询: {'开启' if self.auto_query_mdz else '关闭'}") logging.info(f"米电阻自动查询: {'开启' if self.auto_query_mdz else '关闭'}")
except Exception as e: except Exception as e:
logging.error(f"加载配置出错: {e}") logging.error(f"加载配置出错: {e}")
@ -126,6 +128,8 @@ class SerialManager:
self.data_file = os.path.abspath('data.txt') self.data_file = os.path.abspath('data.txt')
self.mdz_config = {'port': 9600, 'ser': 'COM5'} self.mdz_config = {'port': 9600, 'ser': 'COM5'}
self.cz_config = {'port': 9600, 'ser': 'COM2', 'stable_threshold': 10} self.cz_config = {'port': 9600, 'ser': 'COM2', 'stable_threshold': 10}
self.xj_config = {'port': 9600, 'ser': 'COM3'}
self.scanner_config = {'port': 9600, 'ser': 'COM4'}
self.stable_threshold = 10 self.stable_threshold = 10
self.auto_query_mdz = False self.auto_query_mdz = False
logging.info(f"使用默认配置,数据文件: {self.data_file}") logging.info(f"使用默认配置,数据文件: {self.data_file}")
@ -194,88 +198,105 @@ class SerialManager:
Args: Args:
port_name: 串口名称如COM1 port_name: 串口名称如COM1
port_type: 串口类型'cz'表示称重'mdz'表示米电阻, 'xj'表示线径 port_type: 串口类型'cz'表示称重'mdz'表示米电阻, 'xj'表示线径, 'scanner'表示扫码器
baud_rate: 波特率如果为None则从配置文件读取 baud_rate: 波特率如果为None则从配置文件读取
data_bits: 数据位 data_bits: 数据位
stop_bits: 停止位 stop_bits: 停止位
parity: 校验位N-无校验E-偶校验O-校验 parity: 校验位'N'表示无校验'O'表示奇校验'E'表示偶校验
timeout: 超时时间单位秒 timeout: 超时时间单位秒
callback: 数据回调函数接收参数为(port_name, data) callback: 回调函数接收(port_name, data)作为参数
Returns: Returns:
是否成功打开 bool: 成功返回True失败返回False
""" """
# 如果串口已经打开,先关闭
if port_name in self.serial_ports and self.serial_ports[port_name]:
try:
self.close_port(port_name)
except Exception as e:
logging.error(f"关闭已打开的串口失败: {e}")
# 配置串口参数
try: try:
# 如果波特率为None从配置文件读取 # 从配置读取波特率(如果未提供)
if baud_rate is None: if baud_rate is None:
if port_type == 'cz' and self.cz_config: if port_type == 'cz' and self.cz_config:
baud_rate = self.cz_config.get('port', 9600) baud_rate = self.cz_config.get('port', 9600)
elif port_type == 'mdz' and self.mdz_config: elif port_type == 'mdz' and self.mdz_config:
baud_rate = self.mdz_config.get('port', 9600) baud_rate = self.mdz_config.get('port', 9600)
elif port_type == 'xj' and self.xj_config: # 添加线径配置 elif port_type == 'xj' and self.xj_config:
baud_rate = self.xj_config.get('port', 9600) baud_rate = self.xj_config.get('port', 9600)
elif port_type == 'scanner' and self.scanner_config:
baud_rate = self.scanner_config.get('port', 9600)
else: else:
baud_rate = 9600 # 默认波特率 baud_rate = 9600 # 默认波特率
# 如果串口已经打开,先关闭 # 转换校验位为PySerial常量
if port_name in self.serial_ports: if parity.upper() == 'N':
self.close_port(port_name) parity_constant = serial.PARITY_NONE
elif parity.upper() == 'O':
parity_constant = serial.PARITY_ODD
elif parity.upper() == 'E':
parity_constant = serial.PARITY_EVEN
else:
parity_constant = serial.PARITY_NONE
# 打开串口 # 打开串口
ser = serial.Serial( self.serial_ports[port_name] = serial.Serial(
port=port_name, port=port_name,
baudrate=baud_rate, baudrate=baud_rate,
bytesize=data_bits, bytesize=data_bits,
stopbits=stop_bits, stopbits=stop_bits,
parity=parity, parity=parity_constant,
timeout=timeout timeout=timeout
) )
if not ser.is_open: logging.info(f"打开串口成功: {port_name}, 类型: {port_type}, 波特率: {baud_rate}")
ser.open()
# 存储串口对象
self.serial_ports[port_name] = ser
logging.info(f"串行对象 for {port_name} 存储在 self.serial_ports 中. 当前活跃端口: {list(self.serial_ports.keys())}")
# 设置回调 # 设置回调
if callback: if callback:
self.callbacks[port_name] = callback self.callbacks[port_name] = callback
# 启动读取线程 # 创建并启动读取线程
self.running_flags[port_name] = True self.running_flags[port_name] = True
# 根据串口类型选择不同的读取线程
if port_type == 'cz': if port_type == 'cz':
# 称重数据需要特殊处理
thread = threading.Thread(target=self._read_weight_thread, args=(port_name, self.stable_threshold)) thread = threading.Thread(target=self._read_weight_thread, args=(port_name, self.stable_threshold))
thread.daemon = True
thread.start()
self.read_threads[port_name] = thread
elif port_type == 'mdz': elif port_type == 'mdz':
# 米电阻数据需要特殊处理
thread = threading.Thread(target=self._read_resistance_thread, args=(port_name,)) thread = threading.Thread(target=self._read_resistance_thread, args=(port_name,))
thread.daemon = True elif port_type == 'xj':
thread.start() # 线径数据需要特殊处理
self.read_threads[port_name] = thread
elif port_type == 'xj': # 添加线径读取线程
thread = threading.Thread(target=self._read_diameter_thread, args=(port_name,)) thread = threading.Thread(target=self._read_diameter_thread, args=(port_name,))
thread.daemon = True elif port_type == 'scanner':
thread.start() # 扫码器数据需要特殊处理
self.read_threads[port_name] = thread thread = threading.Thread(target=self._read_scanner_thread, args=(port_name,))
else: else:
# 默认读取线程 # 其他类型使用通用处理
thread = threading.Thread(target=self._read_thread, args=(port_name,)) thread = threading.Thread(target=self._read_thread, args=(port_name,))
thread.daemon = True
thread.start()
self.read_threads[port_name] = thread
logging.info(f"串口 {port_name} ({port_type}) 已打开,波特率={baud_rate}") thread.daemon = True
thread.start()
self.read_threads[port_name] = thread
return True return True
except Exception as e: except Exception as e:
logging.error(f"打开串口 {port_name} 失败: {str(e)}") logging.error(f"打开串口失败: {port_name}, 错误: {e}")
if port_name in self.serial_ports: # 清理,以防部分成功 # 确保清理好资源
del self.serial_ports[port_name] if port_name in self.serial_ports:
logging.info(f"打开 {port_name} 失败后, 当前活跃端口: {list(self.serial_ports.keys())}") try:
self.serial_ports[port_name].close()
except:
pass
self.serial_ports.pop(port_name, None)
# 停止相关线程
self.running_flags[port_name] = False
if port_name in self.read_threads:
self.read_threads.pop(port_name, None)
return False return False
def close_port(self, port_name: str) -> bool: def close_port(self, port_name: str) -> bool:
@ -351,6 +372,35 @@ class SerialManager:
logging.error(f"向串口 {port_name} 写入数据失败: {str(e)}") logging.error(f"向串口 {port_name} 写入数据失败: {str(e)}")
return False return False
def read_data(self, port_name: str, size: int = None) -> bytes:
"""
从串口读取数据
Args:
port_name: 串口名称
size: 要读取的字节数如果为None则读取所有可用数据
Returns:
读取的数据如果失败则返回空字节
"""
try:
if not self.is_port_open(port_name):
logging.error(f"尝试从未打开的串口 {port_name} 读取数据")
return b''
if size is None:
# 读取所有可用数据
if self.serial_ports[port_name].in_waiting > 0:
return self.serial_ports[port_name].read(self.serial_ports[port_name].in_waiting)
return b''
else:
# 读取指定数量的字节
return self.serial_ports[port_name].read(size)
except Exception as e:
logging.error(f"从串口 {port_name} 读取数据失败: {str(e)}")
return b''
def _read_thread(self, port_name: str): def _read_thread(self, port_name: str):
""" """
串口读取线程 串口读取线程
@ -534,7 +584,7 @@ class SerialManager:
return return
# 构建数据字符串 # 构建数据字符串
data_str = f"mdz:{self.data['mdz']}|cz:{self.data['cz']}|" data_str = f"mdz:{self.data['mdz']}|cz:{self.data['cz']}|scanner:{self.data['scanner']}|"
# 确保目录存在 # 确保目录存在
data_dir = os.path.dirname(self.data_file) data_dir = os.path.dirname(self.data_file)
@ -792,7 +842,7 @@ class SerialManager:
"""通知所有相关回调函数""" """通知所有相关回调函数"""
try: try:
# 端口特定回调 (通常用于原始串口数据) # 端口特定回调 (通常用于原始串口数据)
if port_name in self.callbacks and port_name not in ['mdz_data', 'xj_data']: # 避免重复处理 if port_name in self.callbacks and port_name not in ['mdz_data', 'xj_data', 'scanner_data']: # 避免重复处理
try: try:
# 假设这种回调期望原始的 value (可能是字节串,也可能是其他类型) # 假设这种回调期望原始的 value (可能是字节串,也可能是其他类型)
self.callbacks[port_name](port_name, value) self.callbacks[port_name](port_name, value)
@ -874,6 +924,37 @@ class SerialManager:
else: else:
logging.warning(f"回调失败: xj_data 中实际值为None. 初始 value: {value}") logging.warning(f"回调失败: xj_data 中实际值为None. 初始 value: {value}")
# 全局回调, 特别处理 'scanner_data'
if 'scanner_data' in self.callbacks and port_name == 'scanner_data':
actual_scanner_value = None
source_info = "unknown"
if isinstance(value, dict):
actual_scanner_value = value.get('value')
source_info = value.get('source', source_info)
elif isinstance(value, (str, bytes)):
if isinstance(value, bytes):
try:
actual_scanner_value = value.decode('utf-8').strip()
except:
actual_scanner_value = str(value)
else:
actual_scanner_value = value
if actual_scanner_value is not None:
callback_data_str = f"扫码数据: {actual_scanner_value}"
try:
triggering_port = port_name if port_name not in ['scanner_data', 'scanner'] else self.scanner_config.get('ser', 'N/A') if self.scanner_config else 'N/A'
if source_info.startswith("mock"):
triggering_port = f"mock_{port_name}"
self.callbacks['scanner_data'](triggering_port, callback_data_str.encode('utf-8'))
logging.info(f"通知 'scanner_data' 回调. 值: {actual_scanner_value}, 源: {source_info}, 触发源端口: {triggering_port}")
except Exception as e:
logging.error(f"调用全局回调 'scanner_data' 失败: {e}", exc_info=True)
else:
logging.warning(f"回调失败: scanner_data 中实际值为None. 初始 value: {value}")
except Exception as e: except Exception as e:
logging.error(f"通知回调失败: {e}", exc_info=True) logging.error(f"通知回调失败: {e}", exc_info=True)
@ -893,14 +974,15 @@ class SerialManager:
os_type = platform.system() os_type = platform.system()
if os_type == "Darwin" and ( if os_type == "Darwin" and (
(self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser'] and self.mdz_config['ser'].startswith('COM')) or (self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser'] and self.mdz_config['ser'].startswith('COM')) or
(self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].startswith('COM')) (self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].startswith('COM')) or
(self.scanner_config and 'ser' in self.scanner_config and self.scanner_config['ser'] and self.scanner_config['ser'].startswith('COM'))
): ):
logging.warning("检测到在macOS系统上配置了Windows格式的COM端口这些端口将无法正常打开") logging.warning("检测到在macOS系统上配置了Windows格式的COM端口这些端口将无法正常打开")
logging.warning("macOS上的串口通常是/dev/tty.*或/dev/cu.*格式") logging.warning("macOS上的串口通常是/dev/tty.*或/dev/cu.*格式")
# 继续尝试打开,但不影响程序流程 # 继续尝试打开,但不影响程序流程
# 尝试打开线径串口 # 尝试打开线径串口
if self.cz_config and 'ser' in self.cz_config and self.cz_config['ser']: if self.cz_config and 'ser' in self.cz_config and self.cz_config['ser'] and self.cz_config['ser'].strip():
port_name = self.cz_config['ser'] port_name = self.cz_config['ser']
baud_rate = self.cz_config.get('port', 2400) baud_rate = self.cz_config.get('port', 2400)
@ -917,10 +999,10 @@ class SerialManager:
else: else:
logging.info(f"线径串口 {port_name} 已经打开,无需重新打开") logging.info(f"线径串口 {port_name} 已经打开,无需重新打开")
else: else:
logging.warning("线径串口未配置,跳过自动打开") logging.warning("线径串口未配置或设置为不使用,跳过自动打开")
# 尝试打开米电阻串口 # 尝试打开米电阻串口
if self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser']: if self.mdz_config and 'ser' in self.mdz_config and self.mdz_config['ser'] and self.mdz_config['ser'].strip():
port_name = self.mdz_config['ser'] port_name = self.mdz_config['ser']
baud_rate = self.mdz_config.get('port', 9600) baud_rate = self.mdz_config.get('port', 9600)
@ -937,7 +1019,27 @@ class SerialManager:
else: else:
logging.info(f"米电阻串口 {port_name} 已经打开,无需重新打开") logging.info(f"米电阻串口 {port_name} 已经打开,无需重新打开")
else: else:
logging.warning("米电阻串口未配置,跳过自动打开") logging.warning("米电阻串口未配置或设置为不使用,跳过自动打开")
# 尝试打开扫码器串口
if self.scanner_config and 'ser' in self.scanner_config and self.scanner_config['ser'] and self.scanner_config['ser'].strip():
port_name = self.scanner_config['ser']
baud_rate = self.scanner_config.get('port', 9600)
if not self.is_port_open(port_name):
try:
if self.open_port(port_name, 'scanner', baud_rate):
logging.info(f"自动打开扫码器串口 {port_name} 成功")
else:
logging.error(f"自动打开扫码器串口 {port_name} 失败")
success = False
except Exception as e:
logging.error(f"自动打开扫码器串口 {port_name} 时发生异常: {e}")
success = False
else:
logging.info(f"扫码器串口 {port_name} 已经打开,无需重新打开")
else:
logging.warning("扫码器串口未配置或设置为不使用,跳过自动打开")
# 注意不在这里启动键盘监听器而是在MainWindow的handle_start方法中显式调用start_keyboard_listener # 注意不在这里启动键盘监听器而是在MainWindow的handle_start方法中显式调用start_keyboard_listener
@ -1023,3 +1125,73 @@ class SerialManager:
except Exception as e: except Exception as e:
logging.error(f"处理线径数据总体异常: {e}") logging.error(f"处理线径数据总体异常: {e}")
return False return False
def _read_scanner_thread(self, port_name: str):
"""
扫码器串口读取线程
Args:
port_name: 串口名称
"""
try:
while self.running_flags.get(port_name, False):
if not self.is_port_open(port_name):
time.sleep(0.1)
continue
# 检查是否有数据可读
if self.serial_ports[port_name].in_waiting > 0:
response = self.serial_ports[port_name].read(self.serial_ports[port_name].in_waiting)
self._process_scanner_response(port_name, response)
time.sleep(0.1)
except Exception as e:
logging.error(f"扫码器串口 {port_name} 读取线程异常: {e}")
def _process_scanner_response(self, port_name, response_bytes: bytes):
"""处理扫码器响应数据"""
try:
if response_bytes: # 确保有响应数据
try:
# 尝试解码为字符串
scanner_value = response_bytes.decode('utf-8').strip()
# 记录日志
logging.info(f"[{port_name}] 扫码数据: {scanner_value}")
# 更新数据
self.data['scanner'] = scanner_value
# 写入文件并通知回调
self._write_data_to_file()
# 使用"扫码数据: xxx"格式通知回调
callback_data = f"扫码数据: {scanner_value}".encode('utf-8')
if 'scanner_data' in self.callbacks:
self.callbacks['scanner_data'](port_name, callback_data)
return True
except Exception as e:
logging.error(f"处理扫码数据异常: {e}")
# 解码失败,尝试直接使用字节数据
# 记录日志(十六进制字符串)
hex_str = ' '.join(f'{b:02X}' for b in response_bytes)
logging.warning(f"[{port_name}] 扫码数据(十六进制): {hex_str}")
# 更新数据(使用十六进制字符串)
self.data['scanner'] = hex_str
# 写入文件并通知回调
self._write_data_to_file()
# 使用"扫码数据: xxx"格式通知回调
callback_data = f"扫码数据: {hex_str}".encode('utf-8')
if 'scanner_data' in self.callbacks:
self.callbacks['scanner_data'](port_name, callback_data)
return True
else:
logging.warning("扫码响应数据为空")
# 如果无法解析,则直接返回失败
return False
except Exception as e:
logging.error(f"处理扫码数据总体异常: {e}")
return False

View File

@ -205,6 +205,10 @@ class MainWindow(MainWindowUI):
# 加载托盘号列表 # 加载托盘号列表
self.load_pallet_codes() self.load_pallet_codes()
# 恢复开始按钮原始样式
self.restore_start_button_style()
def get_axios_num(self,tray_id): def get_axios_num(self,tray_id):
"""获取托盘号对应的轴号""" """获取托盘号对应的轴号"""
from dao.inspection_dao import InspectionDAO from dao.inspection_dao import InspectionDAO
@ -369,10 +373,13 @@ class MainWindow(MainWindowUI):
# 更新串口管理器配置 # 更新串口管理器配置
self.serial_manager.reload_config() self.serial_manager.reload_config()
# 重新打开已配置的串口
self.serial_manager.auto_open_configured_ports()
# 重新加载托盘号 # 重新加载托盘号
self.load_pallet_codes() self.load_pallet_codes()
logging.info("设置已更新,重新加载配置") logging.info("设置已更新,重新加载配置并重新打开串口")
def handle_input(self): def handle_input(self):
"""处理上料按钮点击事件""" """处理上料按钮点击事件"""
@ -508,6 +515,49 @@ class MainWindow(MainWindowUI):
logging.error(f"处理下料操作失败: {str(e)}") logging.error(f"处理下料操作失败: {str(e)}")
QMessageBox.critical(self, "错误", f"处理下料操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"处理下料操作失败: {str(e)}")
def restore_start_button_style(self):
"""恢复开始按钮的原始样式"""
try:
self.start_button.setStyleSheet("""
QPushButton {
background-color: transparent;
color: black;
border: 1px solid #4caf50;
border-radius: 4px;
padding: 2px 10px;
min-height: 29px;
max-height: 29px;
}
QPushButton:hover {
background-color: #f0f0f0;
}
""")
except Exception as e:
logging.error(f"{str(e)}")
def fill_start_button_style(self):
"""填充开始按钮样式 - 绿色背景,白色字体"""
try:
# 填充按钮样式 - 绿色背景,白色字体,高度与下料按钮一致
self.start_button.setStyleSheet("""
QPushButton {
background-color: #4caf50;
color: white;
border: 1px solid #4caf50;
border-radius: 4px;
padding: 2px 10px;
min-height: 24px;
max-height: 24px;
}
QPushButton:hover {
background-color: #4caf50;
color: white;
}
""")
logging.info("已填充开始按钮样式")
except Exception as e:
logging.error(f"填充开始按钮样式失败: {str(e)}")
def handle_start(self): def handle_start(self):
""" """
处理开始按钮点击事件 处理开始按钮点击事件
@ -522,11 +572,14 @@ class MainWindow(MainWindowUI):
if self._current_unload_info and self._current_unload_num > 0: if self._current_unload_info and self._current_unload_num > 0:
# 下料模式 - 开始下料操作 # 下料模式 - 开始下料操作
# 确保寄存器3(下料启动)设为1寄存器4已在handle_output中设置了当前层数 # 确保寄存器3(下料启动)设为1寄存器4已在handle_output中设置了当前层数
success2 = modbus.write_register_until_success(client, 2, 1)
success3 = modbus.write_register_until_success(client, 3, 1) success3 = modbus.write_register_until_success(client, 3, 1)
if success3: if success2 and success3:
logging.info(f"开始下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}") logging.info(f"开始下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}")
QMessageBox.information(self, "操作提示", f"开始下料操作:当前第{self._current_unload_num}") QMessageBox.information(self, "操作提示", f"开始下料操作:当前第{self._current_unload_num}")
# 填充按钮样式
self.fill_start_button_style()
else: else:
QMessageBox.warning(self, "错误", "开始下料操作失败") QMessageBox.warning(self, "错误", "开始下料操作失败")
else: else:
@ -538,8 +591,11 @@ class MainWindow(MainWindowUI):
if success0 and success2: if success0 and success2:
self._is_loading_active = True # 标记上料任务已开始 self._is_loading_active = True # 标记上料任务已开始
logging.info(f"开始上料操作:当前层数 {self._current_stow_num}") logging.info(f"开始上料操作:当前层数 {self._current_stow_num}")
# 填充按钮样式
self.fill_start_button_style()
else: else:
QMessageBox.warning(self, "错误", "开始上料操作失败") QMessageBox.warning(self, "错误", "开始上料操作失败")
except Exception as e: except Exception as e:
logging.error(f"开始操作失败: {str(e)}") logging.error(f"开始操作失败: {str(e)}")
QMessageBox.critical(self, "错误", f"开始操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"开始操作失败: {str(e)}")
@ -559,6 +615,8 @@ class MainWindow(MainWindowUI):
if success3: if success3:
logging.info(f"停止下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}") logging.info(f"停止下料操作:当前层数 {self._current_unload_num}/{self._total_unload_num}")
QMessageBox.information(self, "操作提示", "已停止下料操作") QMessageBox.information(self, "操作提示", "已停止下料操作")
# 恢复按钮原始样式
self.restore_start_button_style()
else: else:
QMessageBox.warning(self, "错误", "停止下料操作失败") QMessageBox.warning(self, "错误", "停止下料操作失败")
else: else:
@ -569,8 +627,11 @@ class MainWindow(MainWindowUI):
self._is_loading_active = False # 标记上料任务已停止 self._is_loading_active = False # 标记上料任务已停止
logging.info("停止上料操作") logging.info("停止上料操作")
QMessageBox.information(self, "操作提示", "已停止上料操作") QMessageBox.information(self, "操作提示", "已停止上料操作")
# 恢复按钮原始样式
self.restore_start_button_style()
else: else:
QMessageBox.warning(self, "错误", "停止上料操作失败") QMessageBox.warning(self, "错误", "停止上料操作失败")
except Exception as e: except Exception as e:
logging.error(f"停止操作失败: {str(e)}") logging.error(f"停止操作失败: {str(e)}")
QMessageBox.critical(self, "错误", f"停止操作失败: {str(e)}") QMessageBox.critical(self, "错误", f"停止操作失败: {str(e)}")
@ -1943,35 +2004,46 @@ class MainWindow(MainWindowUI):
def handle_register_change(self, address, value): def handle_register_change(self, address, value):
"""处理寄存器变化""" """处理寄存器变化"""
logging.info(f"[处理] 寄存器D{address}变化: {value}") logging.info(f"[处理] 寄存器D{address}变化: {value}")
# 在这里可以添加通用寄存器变化处理逻辑 # 这里可以添加通用寄存器变化处理逻辑
pass
@Slot(int, str) @Slot(int, str)
def handle_loading_feedback(self, status, desc): def handle_loading_feedback(self, status, desc):
"""处理上料信息反馈""" """处理上料信息反馈"""
message = desc # Default message message = desc
if status == 1: try:
modbus = ModbusUtils() if status == 1:
client = modbus.get_client() modbus = ModbusUtils()
# 睡 0.5 秒用于延缓modbus 监听 client = modbus.get_client()
time.sleep(0.5) # 睡 0.5 秒用于延缓modbus 监听
modbus.write_register_until_success(client, 2, 0) time.sleep(0.5)
modbus.write_register_until_success(client, 2, 0)
if self._current_stow_num > 0:
completed_layer_num = self._current_stow_num
self._current_stow_num -= 1
if self._current_stow_num == 0:
self._is_loading_active = False # 任务完成,标记为非活动
self._loading_info = None
logging.info("所有层拆垛完成,清空上料信息")
message = f"{completed_layer_num} 层(最后一层)拆垛完成!"
# 重置寄存器 0 和 2 为 0
modbus.write_register_until_success(client, 0, 0)
modbus.write_register_until_success(client, 2, 0)
self.loading_feedback_signal.emit("input", message)
# 恢复开始按钮原始样式
self.restore_start_button_style()
else:
logging.info(f"当前层拆垛完成,剩余层数: {self._current_stow_num}")
message = f"{completed_layer_num} 层拆垛完成。"
self.loading_feedback_signal.emit("input", message)
#通知寄存器,进行第几层拆垛
modbus.write_register_until_success(client,0 ,self._current_stow_num)
except Exception as e:
logging.error(f"处理上料信息反馈失败: {str(e)}")
# 不在这里显示对话框,而是通过信号传递错误信息
self.loading_feedback_signal.emit("error", f"处理上料信息反馈失败: {str(e)}")
finally:
modbus.close_client(client) modbus.close_client(client)
if self._current_stow_num > 0:
completed_layer_num = self._current_stow_num
self._current_stow_num -= 1
if self._current_stow_num == 0:
self._is_loading_active = False # 任务完成,标记为非活动
self._loading_info = None
logging.info("所有层拆垛完成,清空上料信息")
message = f"{completed_layer_num} 层(最后一层)拆垛完成!"
else:
logging.info(f"当前层拆垛完成,剩余层数: {self._current_stow_num}")
message = f"{completed_layer_num} 层拆垛完成。"
self.loading_feedback_signal.emit("input", message)
def _handle_loading_feedback_ui(self, status_type, desc): def _handle_loading_feedback_ui(self, status_type, desc):
"""在主线程中处理上料UI更新""" """在主线程中处理上料UI更新"""
try: try:
@ -1996,6 +2068,8 @@ class MainWindow(MainWindowUI):
client = modbus.get_client() client = modbus.get_client()
try: try:
# 睡 0.5 秒用于延缓modbus 监听
time.sleep(0.5)
# 临时重置寄存器3(下料启动)为0等待用户下一次启动 # 临时重置寄存器3(下料启动)为0等待用户下一次启动
modbus.write_register_until_success(client, 3, 0) modbus.write_register_until_success(client, 3, 0)
@ -2013,6 +2087,9 @@ class MainWindow(MainWindowUI):
# 通过信号触发UI更新 - 显示前一层完成的消息 # 通过信号触发UI更新 - 显示前一层完成的消息
message = f"{self._current_unload_num-1}层下料完成,请启动第{self._current_unload_num}层下料" message = f"{self._current_unload_num-1}层下料完成,请启动第{self._current_unload_num}层下料"
self.unloading_feedback_signal.emit("output", message) self.unloading_feedback_signal.emit("output", message)
# 恢复开始按钮原始样式
self.restore_start_button_style()
else: else:
# 所有层都下料完成,重置寄存器和计数器 # 所有层都下料完成,重置寄存器和计数器
modbus.write_register_until_success(client, 3, 0) # 确保下料启动寄存器为0 modbus.write_register_until_success(client, 3, 0) # 确保下料启动寄存器为0
@ -2034,6 +2111,9 @@ class MainWindow(MainWindowUI):
# 通过信号触发UI更新而不是直接操作UI # 通过信号触发UI更新而不是直接操作UI
message = f"托盘 {tray_code} 的所有 {total_tier} 层下料已全部完成" message = f"托盘 {tray_code} 的所有 {total_tier} 层下料已全部完成"
self.unloading_feedback_signal.emit("output", message) self.unloading_feedback_signal.emit("output", message)
# 恢复开始按钮原始样式
self.restore_start_button_style()
except Exception as e: except Exception as e:
logging.error(f"处理下料反馈时发生错误: {str(e)}") logging.error(f"处理下料反馈时发生错误: {str(e)}")
# 不在这里显示对话框,而是通过信号传递错误信息 # 不在这里显示对话框,而是通过信号传递错误信息
@ -2292,6 +2372,9 @@ class MainWindow(MainWindowUI):
# 注册线径数据回调 # 注册线径数据回调
self.serial_manager.callbacks['xj_data'] = self.on_diameter_data_received self.serial_manager.callbacks['xj_data'] = self.on_diameter_data_received
# 注册扫码器数据回调
self.serial_manager.callbacks['scanner_data'] = self.on_scanner_data_received
# 自动打开已配置的串口 # 自动打开已配置的串口
self.serial_manager.auto_open_configured_ports() self.serial_manager.auto_open_configured_ports()
@ -2402,6 +2485,33 @@ class MainWindow(MainWindowUI):
except Exception as e: except Exception as e:
logging.error(f"处理线径数据失败: {str(e)}") logging.error(f"处理线径数据失败: {str(e)}")
def on_scanner_data_received(self, port_name, data):
"""扫码器数据接收回调函数
Args:
port_name: 串口名称
data: 接收到的数据
"""
try:
# 解析数据
data_str = data.decode('utf-8') if isinstance(data, bytes) else str(data)
logging.info(f"收到扫码器数据: {data_str} 来自 {port_name}")
# 提取扫码数据,格式为"扫码数据: xxx"
if "扫码数据:" in data_str:
gc_note = data_str.split("扫码数据:")[1].strip()
logging.info(f"提取到工程号: {gc_note}")
# 设置工程号到输入框
self.order_edit.setText(gc_note)
# 模拟按下回车键触发handle_order_enter方法
self.handle_order_enter()
else:
logging.warning(f"收到的数据不包含扫码数据标记: {data_str}")
except Exception as e:
logging.error(f"处理扫码器数据失败: {str(e)}")
def set_inspection_value(self, data_type, config, value): def set_inspection_value(self, data_type, config, value):
"""设置检验项目值到表格中 """设置检验项目值到表格中

View File

@ -32,6 +32,10 @@ class SerialSettingsWidget(SerialSettingsUI):
self.xj_refresh_btn.clicked.connect(self.refresh_ports) self.xj_refresh_btn.clicked.connect(self.refresh_ports)
self.test_xj_btn.clicked.connect(self.test_xj_port) self.test_xj_btn.clicked.connect(self.test_xj_port)
# 扫码器串口
self.scanner_refresh_btn.clicked.connect(self.refresh_ports)
self.test_scanner_btn.clicked.connect(self.test_scanner_port)
# 保存按钮 # 保存按钮
self.save_btn.clicked.connect(self.save_settings) self.save_btn.clicked.connect(self.save_settings)
@ -46,9 +50,18 @@ class SerialSettingsWidget(SerialSettingsUI):
try: try:
# 保存当前选择 # 保存当前选择
current_mdz_port = self.mdz_port_combo.currentData() current_mdz_port = self.mdz_port_combo.currentData()
current_xj_port = self.xj_port_combo.currentData()
current_scanner_port = self.scanner_port_combo.currentData()
# 清空列表 # 清空列表
self.mdz_port_combo.clear() self.mdz_port_combo.clear()
self.xj_port_combo.clear()
self.scanner_port_combo.clear()
# 添加"不使用"选项
self.mdz_port_combo.addItem("不使用", "")
self.xj_port_combo.addItem("不使用", "")
self.scanner_port_combo.addItem("不使用", "")
# 获取可用串口 # 获取可用串口
ports = list(serial.tools.list_ports.comports()) ports = list(serial.tools.list_ports.comports())
@ -56,15 +69,40 @@ class SerialSettingsWidget(SerialSettingsUI):
for port in ports: for port in ports:
port_name = port.device port_name = port.device
port_desc = f"{port_name} ({port.description})" port_desc = f"{port_name} ({port.description})"
# 添加到米电阻下拉框
self.mdz_port_combo.addItem(port_desc, port_name) self.mdz_port_combo.addItem(port_desc, port_name)
# 添加到线径下拉框
self.xj_port_combo.addItem(port_desc, port_name)
# 添加到扫码器下拉框
self.scanner_port_combo.addItem(port_desc, port_name)
# 恢复之前的选择 # 恢复之前的选择
if current_mdz_port: if current_mdz_port:
index = self.mdz_port_combo.findData(current_mdz_port) index = self.mdz_port_combo.findData(current_mdz_port)
if index >= 0: if index >= 0:
self.mdz_port_combo.setCurrentIndex(index) self.mdz_port_combo.setCurrentIndex(index)
else:
# 如果之前没有选择,则设为"不使用"
self.mdz_port_combo.setCurrentIndex(0)
if current_xj_port:
index = self.xj_port_combo.findData(current_xj_port)
if index >= 0:
self.xj_port_combo.setCurrentIndex(index)
else:
# 如果之前没有选择,则设为"不使用"
self.xj_port_combo.setCurrentIndex(0)
if current_scanner_port:
index = self.scanner_port_combo.findData(current_scanner_port)
if index >= 0:
self.scanner_port_combo.setCurrentIndex(index)
else:
# 如果之前没有选择,则设为"不使用"
self.scanner_port_combo.setCurrentIndex(0)
logging.info(f"已刷新串口列表,找到 {len(ports)} 个串口") logging.info(f"已刷新串口列表,找到 {len(ports)} 个串口")
except Exception as e: except Exception as e:
@ -155,6 +193,39 @@ class SerialSettingsWidget(SerialSettingsUI):
if index >= 0: if index >= 0:
self.xj_parity_combo.setCurrentIndex(index) self.xj_parity_combo.setCurrentIndex(index)
# 加载扫码器设置
scanner_config = self.config.get_config('scanner')
if scanner_config:
# 设置串口
scanner_port = scanner_config.get('ser', '')
index = self.scanner_port_combo.findData(scanner_port)
if index >= 0:
self.scanner_port_combo.setCurrentIndex(index)
# 设置波特率
scanner_baud = str(scanner_config.get('port', '9600'))
index = self.scanner_baud_combo.findText(scanner_baud)
if index >= 0:
self.scanner_baud_combo.setCurrentIndex(index)
# 设置数据位
scanner_data_bits = str(scanner_config.get('data_bits', '8'))
index = self.scanner_data_bits_combo.findText(scanner_data_bits)
if index >= 0:
self.scanner_data_bits_combo.setCurrentIndex(index)
# 设置停止位
scanner_stop_bits = str(scanner_config.get('stop_bits', '1'))
index = self.scanner_stop_bits_combo.findText(scanner_stop_bits)
if index >= 0:
self.scanner_stop_bits_combo.setCurrentIndex(index)
# 设置校验位
scanner_parity = scanner_config.get('parity', 'N')
index = self.scanner_parity_combo.findData(scanner_parity)
if index >= 0:
self.scanner_parity_combo.setCurrentIndex(index)
logging.info("已加载串口设置") logging.info("已加载串口设置")
except Exception as e: except Exception as e:
logging.error(f"加载串口设置失败: {e}") logging.error(f"加载串口设置失败: {e}")
@ -180,7 +251,6 @@ class SerialSettingsWidget(SerialSettingsUI):
mdz_query_interval = self.mdz_query_interval.value() mdz_query_interval = self.mdz_query_interval.value()
mdz_config = { mdz_config = {
'ser': mdz_port,
'port': mdz_baud, 'port': mdz_baud,
'data_bits': mdz_data_bits, 'data_bits': mdz_data_bits,
'stop_bits': mdz_stop_bits, 'stop_bits': mdz_stop_bits,
@ -189,6 +259,10 @@ class SerialSettingsWidget(SerialSettingsUI):
'query_interval': mdz_query_interval 'query_interval': mdz_query_interval
} }
# 只有当用户选择了串口时才保存串口配置
if mdz_port:
mdz_config['ser'] = mdz_port
self.config.set_config('mdz', mdz_config) self.config.set_config('mdz', mdz_config)
# 保存线径设置 # 保存线径设置
@ -199,17 +273,37 @@ class SerialSettingsWidget(SerialSettingsUI):
xj_parity = self.xj_parity_combo.currentData() xj_parity = self.xj_parity_combo.currentData()
xj_config = { xj_config = {
'ser': xj_port,
'port': xj_baud, 'port': xj_baud,
'data_bits': xj_data_bits, 'data_bits': xj_data_bits,
'stop_bits': xj_stop_bits, 'stop_bits': xj_stop_bits,
'parity': xj_parity 'parity': xj_parity
} }
# 只有当用户选择了串口时才保存串口配置
if xj_port:
xj_config['ser'] = xj_port
self.config.set_config('xj', xj_config) self.config.set_config('xj', xj_config)
# 保存扫码器设置
scanner_port = self.scanner_port_combo.currentData()
scanner_baud = int(self.scanner_baud_combo.currentText())
scanner_data_bits = int(self.scanner_data_bits_combo.currentText())
scanner_stop_bits = float(self.scanner_stop_bits_combo.currentText())
scanner_parity = self.scanner_parity_combo.currentData()
scanner_config = {
'port': scanner_baud,
'data_bits': scanner_data_bits,
'stop_bits': scanner_stop_bits,
'parity': scanner_parity
}
# 只有当用户选择了串口时才保存串口配置
if scanner_port:
scanner_config['ser'] = scanner_port
self.config.set_config('scanner', scanner_config)
# 发送设置变更信号 # 发送设置变更信号
self.settings_changed.emit() self.settings_changed.emit()
@ -224,15 +318,16 @@ class SerialSettingsWidget(SerialSettingsUI):
"""测试米电阻串口""" """测试米电阻串口"""
try: try:
port = self.mdz_port_combo.currentData() port = self.mdz_port_combo.currentData()
if not port:
QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"")
return
baud = int(self.mdz_baud_combo.currentText()) baud = int(self.mdz_baud_combo.currentText())
data_bits = int(self.mdz_data_bits_combo.currentText()) data_bits = int(self.mdz_data_bits_combo.currentText())
stop_bits = float(self.mdz_stop_bits_combo.currentText()) stop_bits = float(self.mdz_stop_bits_combo.currentText())
parity = self.mdz_parity_combo.currentData() parity = self.mdz_parity_combo.currentData()
if not port:
QMessageBox.warning(self, "测试失败", "请选择串口")
return
# 关闭可能已经打开的串口 # 关闭可能已经打开的串口
if self.serial_manager.is_port_open(port): if self.serial_manager.is_port_open(port):
self.serial_manager.close_port(port) self.serial_manager.close_port(port)
@ -248,44 +343,46 @@ class SerialSettingsWidget(SerialSettingsUI):
if query_cmd: if query_cmd:
try: try:
# 转换查询指令为字节 # 转换查询指令为字节
query_bytes = bytes.fromhex(query_cmd.replace(' ', '')) cmd_bytes = bytes.fromhex(query_cmd.replace(' ', ''))
self.serial_manager.write_data(port, cmd_bytes)
time.sleep(0.1) # 等待响应
# 发送查询指令 # 读取响应
self.serial_manager.write_data(port, query_bytes) response = self.serial_manager.read_data(port)
if response:
# 等待一段时间 # 将字节转换为十六进制字符串
time.sleep(0.5) hex_str = ' '.join(f'{b:02X}' for b in response)
QMessageBox.information(self, "测试成功", f"串口打开成功,收到响应:\n{hex_str}")
# 关闭串口 else:
self.serial_manager.close_port(port) QMessageBox.information(self, "测试成功", "串口打开成功,但未收到响应")
QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 测试成功,已发送查询指令")
except Exception as e: except Exception as e:
self.serial_manager.close_port(port) QMessageBox.warning(self, "测试结果", f"串口打开成功,但发送指令失败: {e}")
QMessageBox.warning(self, "测试失败", f"发送查询指令失败: {e}")
else: else:
self.serial_manager.close_port(port) QMessageBox.information(self, "测试成功", "串口打开成功")
QMessageBox.information(self, "测试成功", f"米电阻串口 {port} 打开成功,但未发送查询指令")
# 关闭串口
self.serial_manager.close_port(port)
else: else:
QMessageBox.warning(self, "测试失败", f"无法打开米电阻串口 {port}") QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}")
except Exception as e: except Exception as e:
logging.error(f"测试米电阻串口失败: {e}") logging.error(f"测试米电阻串口失败: {e}")
QMessageBox.warning(self, "测试失败", f"测试米电阻串口失败: {e}") QMessageBox.critical(self, "测试失败", f"测试米电阻串口失败: {e}")
def test_xj_port(self): def test_xj_port(self):
"""测试线径串口""" """测试线径串口"""
try: try:
port = self.xj_port_combo.currentData() port = self.xj_port_combo.currentData()
if not port:
QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"")
return
baud = int(self.xj_baud_combo.currentText()) baud = int(self.xj_baud_combo.currentText())
data_bits = int(self.xj_data_bits_combo.currentText()) data_bits = int(self.xj_data_bits_combo.currentText())
stop_bits = float(self.xj_stop_bits_combo.currentText()) stop_bits = float(self.xj_stop_bits_combo.currentText())
parity = self.xj_parity_combo.currentData() parity = self.xj_parity_combo.currentData()
if not port:
QMessageBox.warning(self, "测试失败", "请选择串口")
return
# 关闭可能已经打开的串口 # 关闭可能已经打开的串口
if self.serial_manager.is_port_open(port): if self.serial_manager.is_port_open(port):
self.serial_manager.close_port(port) self.serial_manager.close_port(port)
@ -296,15 +393,68 @@ class SerialSettingsWidget(SerialSettingsUI):
) )
if success: if success:
# 等待一段时间 QMessageBox.information(self, "测试成功", f"串口 {port} 打开成功")
time.sleep(2)
# 关闭串口 # 关闭串口
self.serial_manager.close_port(port) self.serial_manager.close_port(port)
QMessageBox.information(self, "测试成功", f"线径串口 {port} 测试成功")
else: else:
QMessageBox.warning(self, "测试失败", f"无法打开线径串口 {port}") QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}")
except Exception as e: except Exception as e:
logging.error(f"测试线径串口失败: {e}") logging.error(f"测试线径串口失败: {e}")
QMessageBox.warning(self, "测试失败", f"测试线径串口失败: {e}") QMessageBox.critical(self, "测试失败", f"测试线径串口失败: {e}")
def test_scanner_port(self):
"""测试扫码器串口"""
try:
port = self.scanner_port_combo.currentData()
if not port:
QMessageBox.warning(self, "测试失败", "请先选择串口,当前设置为\"不使用\"")
return
baud = int(self.scanner_baud_combo.currentText())
data_bits = int(self.scanner_data_bits_combo.currentText())
stop_bits = float(self.scanner_stop_bits_combo.currentText())
parity = self.scanner_parity_combo.currentData()
# 关闭可能已经打开的串口
if self.serial_manager.is_port_open(port):
self.serial_manager.close_port(port)
# 尝试打开串口
success = self.serial_manager.open_port(
port, 'scanner', baud, data_bits, stop_bits, parity, 1.0
)
if success:
QMessageBox.information(self, "测试成功", f"串口 {port} 打开成功\n请触发扫码器进行扫描测试")
# 尝试读取数据(短暂等待扫码器输入)
start_time = time.time()
timeout = 5.0 # 5秒超时
while time.time() - start_time < timeout:
response = self.serial_manager.read_data(port)
if response:
# 尝试将字节解码为字符串
try:
text = response.decode('utf-8').strip()
QMessageBox.information(self, "测试成功", f"收到扫码数据:\n{text}")
break
except:
# 如果解码失败,显示十六进制
hex_str = ' '.join(f'{b:02X}' for b in response)
QMessageBox.information(self, "测试成功", f"收到扫码数据 (十六进制):\n{hex_str}")
break
time.sleep(0.1) # 短暂休眠减少CPU占用
# 关闭串口
self.serial_manager.close_port(port)
else:
QMessageBox.critical(self, "测试失败", f"无法打开串口 {port}")
except Exception as e:
logging.error(f"测试扫码器串口失败: {e}")
QMessageBox.critical(self, "测试失败", f"测试扫码器串口失败: {e}")

View File

@ -85,28 +85,28 @@ class SettingsWidget(SettingsUI):
self.pallet_type_settings = PalletTypeSettingsWidget(self) self.pallet_type_settings = PalletTypeSettingsWidget(self)
self.plc_settings = PLCSettingsWidget(self) self.plc_settings = PLCSettingsWidget(self)
# 创建电监控设置组件 # 创建电监控设置组件
try: try:
self.electricity_settings = ElectricitySettingsUI(self) self.electricity_settings = ElectricitySettingsUI(self)
logging.info("监控设置组件创建成功") logging.info("监控设置组件创建成功")
# 移除临时占位符标签并添加电监控设置部件 # 移除临时占位符标签并添加电监控设置部件
if hasattr(self, 'electricity_placeholder'): if hasattr(self, 'electricity_placeholder'):
logging.info("移除电监控临时占位符") logging.info("移除电监控临时占位符")
self.electricity_layout.removeWidget(self.electricity_placeholder) self.electricity_layout.removeWidget(self.electricity_placeholder)
self.electricity_placeholder.hide() self.electricity_placeholder.hide()
self.electricity_placeholder.deleteLater() self.electricity_placeholder.deleteLater()
else: else:
logging.warning("未找到电监控临时占位符标签") logging.warning("未找到电监控临时占位符标签")
# 检查布局是否可用 # 检查布局是否可用
if hasattr(self, 'electricity_layout'): if hasattr(self, 'electricity_layout'):
logging.info("添加电监控设置部件到布局") logging.info("添加电监控设置部件到布局")
self.electricity_layout.addWidget(self.electricity_settings) self.electricity_layout.addWidget(self.electricity_settings)
else: else:
logging.error("无法找到electricity_layout布局") logging.error("无法找到electricity_layout布局")
except Exception as e: except Exception as e:
logging.error(f"初始化电监控设置组件失败: {e}") logging.error(f"初始化电监控设置组件失败: {e}")
# 移除临时占位符标签并添加检验设置部件 # 移除临时占位符标签并添加检验设置部件
if hasattr(self, 'inspection_placeholder'): if hasattr(self, 'inspection_placeholder'):
@ -172,13 +172,13 @@ class SettingsWidget(SettingsUI):
self.pallet_type_settings.settings_changed.connect(self.on_settings_changed) self.pallet_type_settings.settings_changed.connect(self.on_settings_changed)
self.plc_settings.settings_changed.connect(self.on_settings_changed) self.plc_settings.settings_changed.connect(self.on_settings_changed)
# 连接电监控设置信号 # 连接电监控设置信号
if hasattr(self, 'electricity_settings'): if hasattr(self, 'electricity_settings'):
try: try:
self.electricity_settings.settings_changed.connect(self.on_settings_changed) self.electricity_settings.settings_changed.connect(self.on_settings_changed)
logging.info("已连接电监控设置信号") logging.info("已连接电监控设置信号")
except Exception as e: except Exception as e:
logging.error(f"连接电监控设置信号时出错: {e}") logging.error(f"连接电监控设置信号时出错: {e}")
# 不再在这里连接刷新按钮,避免重复连接 # 不再在这里连接刷新按钮,避免重复连接
logging.info("刷新按钮已在初始化时连接,不再重复连接") logging.info("刷新按钮已在初始化时连接,不再重复连接")

View File

@ -28,54 +28,23 @@ class SettingsWindow(QDialog):
self.settings_widget = SettingsWidget(self) self.settings_widget = SettingsWidget(self)
main_layout.addWidget(self.settings_widget) main_layout.addWidget(self.settings_widget)
# 添加串口设置到标签页
self.serial_settings = SerialSettingsWidget(self)
self.settings_widget.tab_widget.addTab(self.serial_settings, "串口设置")
# 应用相机刷新按钮修复 # 应用相机刷新按钮修复
try: self._fix_camera_refresh_button()
# 先尝试直接找到刷新按钮并添加事件处理
logging.info("尝试直接查找刷新按钮并添加事件处理...")
from PySide6.QtWidgets import QPushButton
# 在整个UI层次中查找刷新设备按钮 # 添加按钮
refresh_buttons = [] self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
for widget in self.findChildren(QPushButton): self.button_box.accepted.connect(self.accept)
if widget.text() == "刷新设备": self.button_box.rejected.connect(self.reject)
refresh_buttons.append(widget) main_layout.addWidget(self.button_box)
if refresh_buttons: # 连接信号
logging.info(f"找到 {len(refresh_buttons)} 个刷新设备按钮") self.serial_settings.settings_changed.connect(self.settings_changed.emit)
for i, button in enumerate(refresh_buttons):
try:
# 断开现有连接
button.clicked.disconnect()
except Exception:
pass
# 连接到相机设置的刷新方法 logging.info("SettingsWindow初始化完成")
if hasattr(self.settings_widget, 'camera_settings') and hasattr(self.settings_widget.camera_settings, 'refresh_devices'):
button.clicked.connect(self.settings_widget.camera_settings.refresh_devices)
logging.info(f"已将刷新按钮 #{i+1} 连接到refresh_devices方法")
# 尝试调用一次
self.settings_widget.camera_settings.refresh_devices()
logging.info("已手动调用refresh_devices方法初始化设备列表")
# 使用导入的修复模块作为备选方案
try:
# 使用相对导入,更可靠
from . import refresh_devices_fix
refresh_devices_fix.fix_camera_refresh_button(self.settings_widget)
logging.info("已应用相机刷新按钮修复(通过相对导入)")
except ImportError:
# 如果相对导入失败,尝试绝对导入
try:
import refresh_devices_fix
refresh_devices_fix.fix_camera_refresh_button(self.settings_widget)
logging.info("已应用相机刷新按钮修复(通过绝对导入)")
except ImportError:
# 如果绝对导入也失败,直接内联实现修复逻辑
logging.warning("导入refresh_devices_fix失败使用内联修复")
self._fix_camera_refresh_button()
except Exception as e:
logging.error(f"应用相机刷新按钮修复失败: {str(e)}")
def _fix_camera_refresh_button(self): def _fix_camera_refresh_button(self):
"""内联实现的刷新按钮修复逻辑,用于导入模块失败的情况""" """内联实现的刷新按钮修复逻辑,用于导入模块失败的情况"""
@ -135,26 +104,6 @@ class SettingsWindow(QDialog):
except Exception as e: except Exception as e:
logging.error(f"内联修复相机刷新按钮时发生错误: {str(e)}") logging.error(f"内联修复相机刷新按钮时发生错误: {str(e)}")
# 添加串口设置到标签页
self.serial_settings = SerialSettingsWidget(self)
self.settings_widget.tab_widget.addTab(self.serial_settings, "串口设置")
# 添加电力监控设置到标签页
self.electricity_settings = ElectricitySettingsUI(self)
self.settings_widget.tab_widget.addTab(self.electricity_settings, "电力监控")
# 添加按钮
self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.accept)
self.button_box.rejected.connect(self.reject)
main_layout.addWidget(self.button_box)
# 连接信号
self.serial_settings.settings_changed.connect(self.settings_changed.emit)
self.electricity_settings.settings_changed.connect(self.settings_changed.emit)
logging.info("SettingsWindow初始化完成")
def accept(self): def accept(self):
"""确认按钮处理,保存所有设置并发送设置变更信号""" """确认按钮处理,保存所有设置并发送设置变更信号"""
# 通知设置已变更 # 通知设置已变更