feat: 修改Modbus写入值,新增线径和重量警告信号及弹框逻辑,优化警告提示处理

This commit is contained in:
zhu-mengmeng 2025-07-23 13:22:53 +08:00
parent 23823d08f8
commit 5d2f0099ac
3 changed files with 188 additions and 142 deletions

Binary file not shown.

View File

@ -2,7 +2,7 @@ from pymodbus.client import ModbusTcpClient
import time import time
client = ModbusTcpClient('localhost', port=5020) client = ModbusTcpClient('localhost', port=5020)
client.connect() client.connect()
client.write_registers(address=11, values=[21962]) client.write_registers(address=11, values=[9562])
# client.write_registers(address=3, values=[0]) # client.write_registers(address=3, values=[0])
# time.sleep(2) # time.sleep(2)
# client.write_registers(address=0, values=[0]) # client.write_registers(address=0, values=[0])

View File

@ -59,6 +59,12 @@ class MainWindow(MainWindowUI):
emergency_stop_signal = Signal(int, str) # 用于在主线程中处理急停信号 emergency_stop_signal = Signal(int, str) # 用于在主线程中处理急停信号
diameter_warning_signal = Signal(float, str, str) # 参数final_value, bccd, tccd diameter_warning_signal = Signal(float, str, str) # 参数final_value, bccd, tccd
# 新增的线径警告弹框信号 - 参数:值, 最小值, 最大值
diameter_alert_signal = Signal(float, float, float)
# 新增的重量警告弹框信号 - 参数:值, 最小值, 最大值
weight_alert_signal = Signal(float, float, float)
def __init__(self, user_id=None, user_name=None, corp_name=None, corp_id=None): def __init__(self, user_id=None, user_name=None, corp_name=None, corp_id=None):
"""初始化主窗口""" """初始化主窗口"""
super().__init__(user_id) super().__init__(user_id)
@ -335,6 +341,12 @@ class MainWindow(MainWindowUI):
if hasattr(self, 'luno_query_button'): if hasattr(self, 'luno_query_button'):
self.luno_query_button.clicked.connect(self.handle_luno_query) self.luno_query_button.clicked.connect(self.handle_luno_query)
# 连接新的线径警告弹框信号
self.diameter_alert_signal.connect(self.show_diameter_alert)
# 连接重量警告弹框信号
self.weight_alert_signal.connect(self.show_weight_alert)
def update_inspection_columns(self): def update_inspection_columns(self):
"""更新检验列配置 - 使用检验配置管理器获取启用的列数和标题""" """更新检验列配置 - 使用检验配置管理器获取启用的列数和标题"""
try: try:
@ -866,16 +878,6 @@ class MainWindow(MainWindowUI):
self.serial_manager.stop_keyboard_listener() self.serial_manager.stop_keyboard_listener()
self.serial_manager.close_all_ports() self.serial_manager.close_all_ports()
def clear_operation_status(self, operation_type):
"""清除右上角的操作状态显示。"""
status_label_name = f"{operation_type}_status_label"
if hasattr(self, status_label_name):
try:
getattr(self, status_label_name).deleteLater()
delattr(self, status_label_name)
logging.info(f"已清除 '{operation_type}' 状态标签。")
except AttributeError:
pass # Failsafe
def handle_camera_status(self, is_connected, message): def handle_camera_status(self, is_connected, message):
"""相机状态处理的空方法,保留是为了兼容性""" """相机状态处理的空方法,保留是为了兼容性"""
@ -2437,48 +2439,23 @@ class MainWindow(MainWindowUI):
# 检查称重值是否在范围内 # 检查称重值是否在范围内
if net_weight_kg < min_weight or net_weight_kg > max_weight: if net_weight_kg < min_weight or net_weight_kg > max_weight:
# 写入寄存器D10 给值 2 表示重量超出范围 # 写入寄存器D10 给值 2 表示重量超出范围
try:
modbus = ModbusUtils() modbus = ModbusUtils()
client = modbus.get_client() client = modbus.get_client()
modbus.write_register_until_success(client, 10, 2) modbus.write_register_until_success(client, 10, 2)
modbus.close_client(client) modbus.close_client(client)
# 显示自动关闭的警告提示框
self.warning_msg = QMessageBox(self)
self.warning_msg.setIcon(QMessageBox.Warning)
self.warning_msg.setWindowTitle('重量超出范围')
self.warning_msg.setText(f"称重值 {net_weight_kg:.2f}kg 不在轴重要求范围内 ({min_weight:.1f} - {max_weight:.1f}kg)")
self.warning_msg.setStandardButtons(QMessageBox.Ok) # 添加确定按钮
self.warning_msg.setModal(False) # 确保非模态
self.warning_msg.setWindowFlags(self.warning_msg.windowFlags() | Qt.WindowStaysOnTopHint) # 置顶显示
# 连接按钮点击信号
self.warning_msg.buttonClicked.connect(lambda btn: self.warning_msg.close())
# 使用 show 而非 exec_ 保持非阻塞
self.warning_msg.show()
# 强制关闭方法1使用多种定时器策略
def force_close_weight_warning():
try:
if hasattr(self, 'warning_msg') and self.warning_msg:
if self.warning_msg.isVisible():
self.warning_msg.close()
self.warning_msg.deleteLater()
logging.debug("重量警告弹框已强制关闭")
else:
logging.debug("重量警告弹框已不可见,跳过关闭")
except Exception as e: except Exception as e:
logging.warning(f"强制关闭重量警告弹框时出错: {str(e)}") logging.error(f"写入Modbus寄存器失败: {str(e)}")
finally:
# 清理引用
if hasattr(self, 'warning_msg'):
delattr(self, 'warning_msg')
# 使用多个定时器确保关闭 # 使用信号触发弹框显示 - 避免主线程阻塞
QTimer.singleShot(5000, force_close_weight_warning) # 5秒后关闭 logging.warning(f"称重值 {net_weight_kg:.2f}kg 超出轴重要求范围 ({min_weight:.1f} - {max_weight:.1f}kg),发送信号显示警告")
QTimer.singleShot(6000, force_close_weight_warning) # 6秒后再次尝试 try:
self.weight_alert_signal.emit(net_weight_kg, min_weight, max_weight)
except Exception as e:
logging.error(f"发送重量警告信号失败: {str(e)}", exc_info=True)
# 阻止继续执行,等待用户处理 # 阻止继续执行,等待用户处理
logging.warning(f"称重值 {weight_kg:.3f}kg 超出轴重要求范围,已阻止保存,等待用户处理") logging.warning(f"称重值 {weight_kg:.3f}kg 超出轴重要求范围,已阻止保存")
return return
else: else:
logging.info(f"称重值 {weight_kg:.3f}kg 在轴重要求范围内 ({min_weight:.1f} - {max_weight:.1f}kg)") logging.info(f"称重值 {weight_kg:.3f}kg 在轴重要求范围内 ({min_weight:.1f} - {max_weight:.1f}kg)")
@ -3030,8 +3007,6 @@ class MainWindow(MainWindowUI):
if self._loading_info and self._current_stow_num > 0: if self._loading_info and self._current_stow_num > 0:
self.show_operation_status("拆垛层数", "input", str(self._current_stow_num)) self.show_operation_status("拆垛层数", "input", str(self._current_stow_num))
else: else:
# 上料任务完成,清除状态显示
self.clear_operation_status("input")
# 上料任务完成,恢复上料按钮样式 # 上料任务完成,恢复上料按钮样式
self.restore_input_button_style() self.restore_input_button_style()
logging.info("上料任务完成,恢复上料按钮样式") logging.info("上料任务完成,恢复上料按钮样式")
@ -3551,52 +3526,20 @@ class MainWindow(MainWindowUI):
# 检查线径值是否在范围内 # 检查线径值是否在范围内
if final_value < min_diameter or final_value > max_diameter: if final_value < min_diameter or final_value > max_diameter:
# 写入寄存器D10 给值 3 表示线径超出范围
modbus = ModbusUtils()
modbus_client = modbus.get_client()
modbus.write_register_until_success(modbus_client, 10, 3)
# 显示自动关闭的警告提示框
self.diameter_warning_msg = QMessageBox(self)
self.diameter_warning_msg.setIcon(QMessageBox.Warning)
self.diameter_warning_msg.setWindowTitle('线径超出范围')
self.diameter_warning_msg.setText(f"线径值 {final_value:.3f}mm 不在线径公差范围内 ({min_diameter:.3f} - {max_diameter:.3f}mm)")
self.diameter_warning_msg.setStandardButtons(QMessageBox.Ok) # 添加确定按钮
self.diameter_warning_msg.setModal(False) # 确保非模态
self.diameter_warning_msg.setWindowFlags(self.diameter_warning_msg.windowFlags() | Qt.WindowStaysOnTopHint) # 置顶显示
# 连接按钮点击信号 # 使用信号触发弹框显示 - 避免主线程阻塞
self.diameter_warning_msg.buttonClicked.connect(lambda btn: self.diameter_warning_msg.close()) logging.warning(f"线径值 {final_value:.3f}mm 超出线径公差范围 ({min_diameter:.3f} - {max_diameter:.3f}mm),发送信号显示警告")
# 使用 show 而非 exec_ 保持非阻塞
self.diameter_warning_msg.show()
# 强制关闭方法:使用多种定时器策略
def force_close_diameter_warning():
try: try:
if hasattr(self, 'diameter_warning_msg') and self.diameter_warning_msg: self.diameter_alert_signal.emit(final_value, min_diameter, max_diameter)
if self.diameter_warning_msg.isVisible():
self.diameter_warning_msg.close()
self.diameter_warning_msg.deleteLater()
logging.debug("线径警告弹框已强制关闭")
else:
logging.debug("线径警告弹框已不可见,跳过关闭")
except Exception as e: except Exception as e:
logging.warning(f"强制关闭线径警告弹框时出错: {str(e)}") logging.error(f"发送线径警告信号失败: {str(e)}", exc_info=True)
finally:
# 清理引用
if hasattr(self, 'diameter_warning_msg'):
delattr(self, 'diameter_warning_msg')
# 使用多个定时器确保关闭
QTimer.singleShot(2000, force_close_diameter_warning) # 2秒后关闭
QTimer.singleShot(3000, force_close_diameter_warning) # 3秒后再次尝试
# 重置测量列表,防止重复触发 # 重置测量列表,防止重复触发
self._diameter_measurements = [] self._diameter_measurements = []
# 阻止继续执行,等待用户处理 # 阻止继续执行,等待用户处理
logging.warning(f"线径值 {final_value:.3f}mm 超出线径公差范围,已阻止保存,等待用户处理") logging.warning(f"线径值 {final_value:.3f}mm 超出线径公差范围,已阻止保存")
return return
else: else:
logging.info(f"线径值 {final_value:.3f}mm 在线径公差范围内 ({min_diameter:.3f} - {max_diameter:.3f}mm)") logging.info(f"线径值 {final_value:.3f}mm 在线径公差范围内 ({min_diameter:.3f} - {max_diameter:.3f}mm)")
@ -3678,56 +3621,11 @@ class MainWindow(MainWindowUI):
@Slot(float, str, str) @Slot(float, str, str)
def show_diameter_warning(self, final_value, bccd, tccd): def show_diameter_warning(self, final_value, bccd, tccd):
"""显示线径超出范围警告(在主线程中执行)""" """显示线径超出范围警告(在主线程中执行)- 为避免冲突,此方法已弃用,仅保留接口"""
try: # 这个方法已经被新的线径处理逻辑取代,为避免冲突,此处不再显示弹框
# 显示自动关闭的警告提示框 # 仅记录日志,保持接口兼容性
self.diameter_warning_msg = QMessageBox(self) logging.info(f"线径警告信号已接收,但弹框显示已被新逻辑取代: 线径值 {final_value:.3f}mm, 范围 {bccd} - {tccd}")
self.diameter_warning_msg.setIcon(QMessageBox.Warning) return
self.diameter_warning_msg.setWindowTitle('线径超出范围')
self.diameter_warning_msg.setText(f"线径 {final_value:.3f} 不在公差范围内 ({bccd} - {tccd})")
self.diameter_warning_msg.setStandardButtons(QMessageBox.Ok) # 添加确定按钮
self.diameter_warning_msg.setModal(False) # 确保非模态
self.diameter_warning_msg.setWindowFlags(self.diameter_warning_msg.windowFlags() | Qt.WindowStaysOnTopHint) # 置顶显示
# 连接按钮点击信号
self.diameter_warning_msg.buttonClicked.connect(lambda btn: self.diameter_warning_msg.close())
# 显示提示框(非模态)
self.diameter_warning_msg.show()
# Windows平台强制关闭方法
def force_close_diameter_warning():
try:
if hasattr(self, 'diameter_warning_msg') and self.diameter_warning_msg:
if self.diameter_warning_msg.isVisible():
# 方法1正常关闭
self.diameter_warning_msg.close()
# 方法2强制删除
self.diameter_warning_msg.deleteLater()
# 方法3隐藏窗口
self.diameter_warning_msg.hide()
# 方法4设置为不可见
self.diameter_warning_msg.setVisible(False)
logging.debug("线径警告弹框已强制关闭")
else:
logging.debug("线径警告弹框已不可见,跳过关闭")
except Exception as e:
logging.warning(f"强制关闭线径警告弹框时出错: {str(e)}")
finally:
# 清理引用
try:
if hasattr(self, 'diameter_warning_msg'):
delattr(self, 'diameter_warning_msg')
except:
pass
# 使用多个定时器确保关闭Windows平台优化
QTimer.singleShot(2000, force_close_diameter_warning) # 1秒后关闭
QTimer.singleShot(3000, force_close_diameter_warning) # 2秒后最后尝试
logging.info(f"线径警告弹框已显示将在1-3秒后自动关闭")
except Exception as e:
logging.error(f"显示线径警告弹框时出错: {str(e)}")
def on_scanner_data_received(self, port_name, data): def on_scanner_data_received(self, port_name, data):
"""扫码器数据接收回调函数 """扫码器数据接收回调函数
@ -5111,6 +5009,154 @@ class MainWindow(MainWindowUI):
logging.warning(f"线径数据处理超时,强制释放锁。处理时间: {processing_time:.2f}") logging.warning(f"线径数据处理超时,强制释放锁。处理时间: {processing_time:.2f}")
self._processing_diameter_lock = False self._processing_diameter_lock = False
@Slot(float, float, float)
def show_diameter_alert(self, value, min_value, max_value):
"""显示线径超出范围警告 - 通过信号触发使用exec_强制显示"""
try:
# 使用更强制的方式显示警告对话框
from PySide6.QtWidgets import QApplication
# 记录当前时间,用于跟踪弹框显示时间
start_time = time.time()
logging.info(f"开始创建线径警告弹框,时间: {start_time}")
# 创建一个模态对话框 - 使用exec_方式显示
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical) # 使用Critical图标更明显
msg.setWindowTitle('警告:线径超出范围!')
msg.setText(f"<b>线径值 {value:.3f}mm 超出范围!</b><br><br>允许范围: {min_value:.3f} - {max_value:.3f}mm")
msg.setStandardButtons(QMessageBox.Ok)
# 设置样式,使其更显眼
msg.setStyleSheet("""
QMessageBox {
background-color: #ffeeee;
border: 3px solid #ff0000;
font-size: 14px;
}
QLabel {
color: #ff0000;
font-size: 16px;
font-weight: bold;
min-width: 250px;
min-height: 80px;
}
QPushButton {
background-color: #ff6666;
color: white;
font-weight: bold;
min-width: 80px;
min-height: 30px;
}
""")
# 确保对话框显示在所有窗口之上
msg.setWindowFlags(Qt.WindowStaysOnTopHint)
# 强制处理事件确保UI更新
QApplication.processEvents()
# 使用定时器在2秒后自动点击确定按钮
def auto_close():
try:
# 查找确定按钮并点击
for button in msg.buttons():
if msg.buttonRole(button) == QMessageBox.AcceptRole:
button.click()
logging.info(f"已自动点击确定按钮,弹框显示时长: {time.time() - start_time:.2f}")
return
# 如果没有找到确定按钮,直接关闭
msg.done(QMessageBox.Ok)
logging.info(f"已自动关闭弹框,弹框显示时长: {time.time() - start_time:.2f}")
except Exception as e:
logging.error(f"自动关闭弹框失败: {str(e)}")
# 设置自动关闭定时器
QTimer.singleShot(2000, auto_close)
# 显示弹框并阻塞直到用户关闭或自动关闭
logging.info(f"即将显示线径警告弹框...")
msg.exec_() # 使用exec_而不是show确保弹框显示
# 记录日志
logging.info(f"线径警告弹框已关闭,总显示时长: {time.time() - start_time:.2f}")
except Exception as e:
logging.error(f"显示线径警告弹框失败: {str(e)}", exc_info=True)
@Slot(float, float, float)
def show_weight_alert(self, value, min_value, max_value):
"""显示重量超出范围警告 - 通过信号触发使用exec_强制显示"""
try:
# 使用更强制的方式显示警告对话框
from PySide6.QtWidgets import QApplication
# 记录当前时间,用于跟踪弹框显示时间
start_time = time.time()
logging.info(f"开始创建重量警告弹框,时间: {start_time}")
# 创建一个模态对话框 - 使用exec_方式显示
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical) # 使用Critical图标更明显
msg.setWindowTitle('警告:重量超出范围!')
msg.setText(f"<b>称重值 {value:.2f}kg 超出范围!</b><br><br>允许范围: {min_value:.1f} - {max_value:.1f}kg")
msg.setStandardButtons(QMessageBox.Ok)
# 设置样式,使其更显眼
msg.setStyleSheet("""
QMessageBox {
background-color: #ffeeee;
border: 3px solid #ff0000;
font-size: 14px;
}
QLabel {
color: #ff0000;
font-size: 16px;
font-weight: bold;
min-width: 250px;
min-height: 80px;
}
QPushButton {
background-color: #ff6666;
color: white;
font-weight: bold;
min-width: 80px;
min-height: 30px;
}
""")
# 确保对话框显示在所有窗口之上
msg.setWindowFlags(Qt.WindowStaysOnTopHint)
# 强制处理事件确保UI更新
QApplication.processEvents()
# 使用定时器在2秒后自动点击确定按钮
def auto_close():
try:
# 查找确定按钮并点击
for button in msg.buttons():
if msg.buttonRole(button) == QMessageBox.AcceptRole:
button.click()
logging.info(f"已自动点击确定按钮,弹框显示时长: {time.time() - start_time:.2f}")
return
# 如果没有找到确定按钮,直接关闭
msg.done(QMessageBox.Ok)
logging.info(f"已自动关闭弹框,弹框显示时长: {time.time() - start_time:.2f}")
except Exception as e:
logging.error(f"自动关闭弹框失败: {str(e)}")
# 设置自动关闭定时器
QTimer.singleShot(2000, auto_close)
# 显示弹框并阻塞直到用户关闭或自动关闭
logging.info(f"即将显示重量警告弹框...")
msg.exec_() # 使用exec_而不是show确保弹框显示
# 记录日志
logging.info(f"重量警告弹框已关闭,总显示时长: {time.time() - start_time:.2f}")
except Exception as e:
logging.error(f"显示重量警告弹框失败: {str(e)}", exc_info=True)
def safe_str(val): def safe_str(val):