From 1a5fa1a95b0b69bae5410807dd097777919cd43b Mon Sep 17 00:00:00 2001 From: zhu-mengmeng <15588200382@163.com> Date: Tue, 17 Jun 2025 09:31:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E6=94=B9=E6=89=98=E7=9B=98?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E8=8E=B7=E5=8F=96=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=8B=E6=96=99=E6=93=8D=E4=BD=9C=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=AF=84=E5=AD=98=E5=99=A8=E5=86=99=E5=85=A5=E5=8F=8A?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dao/pallet_type_dao.py | 11 +- db/jtDB.db | Bin 57344 -> 65536 bytes from pymodbus.py | 4 +- ui/unloading_dialog_ui.py | 237 +++++++++++++++++++++++++++++ utils/pallet_type_manager.py | 6 +- widgets/main_window.py | 92 +++++------ widgets/unloading_dialog_widget.py | 112 ++++++++++++++ 7 files changed, 405 insertions(+), 57 deletions(-) create mode 100644 ui/unloading_dialog_ui.py create mode 100644 widgets/unloading_dialog_widget.py diff --git a/dao/pallet_type_dao.py b/dao/pallet_type_dao.py index ac03425..e4cc523 100644 --- a/dao/pallet_type_dao.py +++ b/dao/pallet_type_dao.py @@ -61,23 +61,22 @@ class PalletTypeDAO: logging.error(f"获取托盘类型失败: {str(e)}") return [] - def get_pallet_type_by_pallet_id(self, pallet_id): + def get_pallet_type_by_pallet_id(self, pallet_code): """根据托盘号获取托盘类型 Args: - pallet_id: 托盘号 + pallet_code: 托盘号 Returns: dict: 托盘类型信息,未找到则返回None """ try: sql = """ - SELECT DISTINCT sort_order + SELECT DISTINCT tier FROM wsbz_pallet_archives t1 - LEFT JOIN wsbz_pallet_types t2 ON t1.type_id = t2.id - WHERE pallet_id = ? AND t1.is_deleted = FALSE + WHERE pallet_code = ? AND t1.is_deleted = FALSE """ - params = (pallet_id,) + params = (pallet_code,) self.db.cursor.execute(sql, params) row = self.db.cursor.fetchone() if row: diff --git a/db/jtDB.db b/db/jtDB.db index 3c0d65439b710ba9ac95e73ea01b645c07acd9ac..137181bb343eb2075e2ff8fba3fd501c70a7652e 100644 GIT binary patch delta 2759 zcmeHJPmB{)7=Ld&w9~@to29^7H%lLCh3&5HEOnNx9%?DoU8t?8U1F4^%x*`lw$nPD z5^F+j%o^ZeB2EHCVHXZ?SON*H2`3|l?O{z!Jh>OI2pTV3ST7{LndxrZtq@JT7@71n z^XB{Y`}3Rk{k~RjTB=p%^Zwpt002i9ri-C#=c!@8XBj+y^dzD?7M=r6+4&Os)qd4} z*qXEa2G7BT>y-h>&CVV`wmw0CCjzQCot3nUi(9nH;uh%8?loN6DJFNBI+IGNz zO0pg+ZTQ_Ak4Gb+7)eX-h^4Gbl8LE^>9Ckr)Kp$cO9~;Av1H`cNW%EVszWUV`!~`j zYx`hhz~0(DxHY65Z#?_)n&($S(Kni|#Sb(2VoEI+Btnjg%FMf>qUUPHHH}6c-Nt5> zGx@aCZB#RQuI81IRq2SNI5rQHV&`~ucEhHwnd0ek}=$3Np7 zj^cQ|mm9~7$8HI09|X5LQt2ZpHCJGtuxt6C02|t+AYt$d=+l;hflWNHqALKc;4->W zcfNccJ+G| zg%_x!(*T{uN#_ISWoI1yLG$m>qerAfrV5i$o!y0e^Mk&^(e;N4v`M~xm+ruFOuPx~i>v{-v-i%5iPtQ@v zhKECFV#nqJ14+h6y_HKVb?W3Nil7a0u&p@MRUBk$Rmi#U9Azg9P!>-(?>WD8hSBeo zoj1|;iiK;j)9>}d9jzE9JrnyTV|wYgZ(O>4^~~LGuF?Ar#wa&BKfY1nKgZp48+Y9Y z19g;HyTFibR%>p(8r9&!sme5Ea}45HVUsCHs9lf}|8E&;ib;pi{(T7={1n*gwMMK> zbABAMIrb1f@FM*Pfx58#H9OMU7t2Le%KdWk?47U9j?(_nFAH7zFww^hi#|gm^mX+i z&morU=yi8@vwQO;wNO$I*&cy3dwvyZ?Lw|^{Z-_KM<7iFc*czWHe)pDnhv-@t4U2E zXae7A?`B$SJJ9Ure$!*DhXXEGOXd0i?P{$uKlWrsrsc+9mzl=-etyhlm~|>G?0tZa zZH%z@DVKLyJR7C6I2?v2JZnea)>M9dE^lq#JHm-bC>bHi(B5c-=$7|ZExU~InlUG< fF^8Ii^YIXGpYZ>9h;xIFW$7>e9sMh^RPFf_EL7f{ delta 3574 zcmdUyS!^3c7{_9Fu;cva) zK$90(5FdhNb_23K7{bQW|sjugjUOlt?=9%RSmltP_FP=QH{N8zw-ZC7I zj`Sx7hKGz1xZRPimYtEVYLTzuIy$?#j>p>D>x}N;#KPxqncY=xU?@JCtX{bE+S2Q9 z)YNgqqe=7nxPEPLpN=M;8X1Tu!OLH|T^x6sDo3ZMZ(ad6J=K8r`oLA{0q`63F<7R% z`N;v8@RgOpM-{{IXd*hIe_b|*`W>#?V1r^$L+V$ChKZUjF5K5t1|rNxuP*2l^j~DI z2;~@zjX~_E>@oHi_8J>Y6&LMeT^lhl+^oPW;GJd;Eklt;aJ@N9U4d|V1)^8j!z`a- zw!TdRuBbh=@<9B=6B{iGfsU?#i&y6y!Qz& z;(6Dz%YDl|?XDw#AVkY*F6A6(Q zm3lr<&xbidRwZ6l0*S+kWHjbz5LHnz8_cN`dMC!6y0F{{_3@Z>M$PQ4c z2|`i15NldQY0nvJB_;?#qyj;bBccsDs4eJni3Ngq8|d+gVg{;myZxZ@gVODbC#RNX zKPv<%s#3@X?46(ui-8c#jAX} zq#bGJwUz0ZKiuHtSbSPMc{rkT`VfPYqtf$p1n1MhrE_-X&Uv1AR_iRi^!6UoQVzqk zR_7%ODC&Qme(5F2s}Oo``*er4qYBWK zJtPHf@%!Dd9f`agM_9O3zw~7?8bMQatzEhEk)DUwT22G6Acx1nJ z;%IDyr=BtP-_IuwywGPoP&jUYAK?njIFA6@BmQ4b8kDDI@=-V8Qxy0}>*>Gz;0j!N l>f9~s;p=;1?#6=zxG{-g+Nu`$l=9O>Qor0_T-x4j{{bRlBaZ+8 diff --git a/from pymodbus.py b/from pymodbus.py index 257e237..fa3471e 100644 --- a/from pymodbus.py +++ b/from pymodbus.py @@ -6,10 +6,10 @@ client.connect() # client.write_registers(address=6, values=[1]) # client.write_registers(address=5, values=[16]) # 贴标完成 -client.write_registers(address=13, values=[1]) +# client.write_registers(address=13, values=[1]) # client.write_registers(address=24, values=[1]) -result = client.read_holding_registers(address=24, count=1) +result = client.read_holding_registers(address=4, count=1) print(result.registers[0],"123===") client.close() \ No newline at end of file diff --git a/ui/unloading_dialog_ui.py b/ui/unloading_dialog_ui.py new file mode 100644 index 0000000..e367f40 --- /dev/null +++ b/ui/unloading_dialog_ui.py @@ -0,0 +1,237 @@ +from PySide6.QtWidgets import ( + QDialog, QLabel, QLineEdit, QComboBox, QPushButton, + QVBoxLayout, QHBoxLayout, QFrame +) +from PySide6.QtCore import Qt +from PySide6.QtGui import QFont + +class UnloadingDialogUI(QDialog): + def __init__(self): + super().__init__() + self.setWindowTitle("下料操作") + self.setFixedSize(600, 250) # 减小高度,因为移除了第一行 + + # 设置字体 + self.normal_font = QFont("微软雅黑", 12) + + # 初始化UI + self.init_ui() + + def init_ui(self): + """初始化UI""" + # 主布局 + self.main_layout = QVBoxLayout(self) + self.main_layout.setContentsMargins(20, 20, 20, 20) + self.main_layout.setSpacing(0) # 移除布局间距 + + # 创建内容区域 + self.create_content_frame() + + # 创建按钮 + self.create_buttons() + + def create_content_frame(self): + """创建内容区域""" + # 创建一个带边框的容器 + container = QFrame() + container.setStyleSheet(""" + QFrame { + border: 1px solid #e0e0e0; + background-color: white; + } + """) + + # 容器的垂直布局 + container_layout = QVBoxLayout(container) + container_layout.setContentsMargins(0, 0, 0, 0) + container_layout.setSpacing(0) + + # 通用样式 + label_style = """ + QLabel { + background-color: #f5f5f5; + color: #333333; + font-weight: bold; + border: none; + border-right: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; + padding: 0 8px; + } + """ + + input_style = """ + QLineEdit { + border: none; + border-right: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; + background-color: white; + selection-background-color: #0078d4; + padding: 0 8px; + } + QLineEdit:focus { + background-color: #f8f8f8; + } + """ + + value_style = """ + QLabel { + background-color: white; + border: none; + border-right: 1px solid #e0e0e0; + border-bottom: 1px solid #e0e0e0; + padding: 0 8px; + } + """ + + # 第一行:托盘号 + row1 = QHBoxLayout() + row1.setSpacing(0) + + self.tray_label = QLabel("托盘号") + self.tray_label.setFont(self.normal_font) + self.tray_label.setStyleSheet(label_style) + self.tray_label.setFixedWidth(100) + self.tray_label.setFixedHeight(45) + + self.tray_input = QLineEdit() + self.tray_input.setFont(self.normal_font) + self.tray_input.setPlaceholderText("请扫描托盘号") + self.tray_input.setStyleSheet(input_style) + self.tray_input.setFixedHeight(45) + + row1.addWidget(self.tray_label) + row1.addWidget(self.tray_input, 1) + container_layout.addLayout(row1) + + # 第二行:轴型和托盘料 + row2 = QHBoxLayout() + row2.setSpacing(0) + + axis_layout = QHBoxLayout() + axis_layout.setSpacing(0) + self.axis_label = QLabel("轴型") + self.axis_label.setFont(self.normal_font) + self.axis_label.setStyleSheet(label_style) + self.axis_label.setFixedWidth(100) + self.axis_label.setFixedHeight(40) + + self.axis_value = QLabel("--") + self.axis_value.setFont(self.normal_font) + self.axis_value.setStyleSheet(value_style) + self.axis_value.setFixedHeight(40) + + axis_layout.addWidget(self.axis_label) + axis_layout.addWidget(self.axis_value, 1) + + material_layout = QHBoxLayout() + material_layout.setSpacing(0) + self.pallet_material_label = QLabel("托盘料") + self.pallet_material_label.setFont(self.normal_font) + self.pallet_material_label.setStyleSheet(label_style) + self.pallet_material_label.setFixedWidth(100) + self.pallet_material_label.setFixedHeight(40) + + self.pallet_material_value = QLabel("--") + self.pallet_material_value.setFont(self.normal_font) + self.pallet_material_value.setStyleSheet(value_style) + self.pallet_material_value.setFixedHeight(40) + + material_layout.addWidget(self.pallet_material_label) + material_layout.addWidget(self.pallet_material_value, 1) + + row2.addLayout(axis_layout, 1) + row2.addLayout(material_layout, 1) + container_layout.addLayout(row2) + + # 第三行:数量和重量 + row3 = QHBoxLayout() + row3.setSpacing(0) + + quantity_layout = QHBoxLayout() + quantity_layout.setSpacing(0) + self.quantity_label = QLabel("数量") + self.quantity_label.setFont(self.normal_font) + self.quantity_label.setStyleSheet(label_style) + self.quantity_label.setFixedWidth(100) + self.quantity_label.setFixedHeight(40) + + self.quantity_value = QLabel("--") + self.quantity_value.setFont(self.normal_font) + self.quantity_value.setStyleSheet(value_style) + self.quantity_value.setFixedHeight(40) + + quantity_layout.addWidget(self.quantity_label) + quantity_layout.addWidget(self.quantity_value, 1) + + weight_layout = QHBoxLayout() + weight_layout.setSpacing(0) + self.weight_label = QLabel("重量") + self.weight_label.setFont(self.normal_font) + self.weight_label.setStyleSheet(label_style) + self.weight_label.setFixedWidth(100) + self.weight_label.setFixedHeight(40) + + self.weight_value = QLabel("--") + self.weight_value.setFont(self.normal_font) + self.weight_value.setStyleSheet(value_style) + self.weight_value.setFixedHeight(40) + + weight_layout.addWidget(self.weight_label) + weight_layout.addWidget(self.weight_value, 1) + + row3.addLayout(quantity_layout, 1) + row3.addLayout(weight_layout, 1) + container_layout.addLayout(row3) + + # 添加弹性空间 + container_layout.addStretch() + + # 将容器添加到主布局 + self.main_layout.addWidget(container) + + def create_buttons(self): + """创建按钮""" + # 按钮布局 + button_layout = QHBoxLayout() + button_layout.setContentsMargins(0, 20, 0, 0) + button_layout.setSpacing(10) + + # 确认按钮 + self.confirm_button = QPushButton("确认") + self.confirm_button.setFont(self.normal_font) + self.confirm_button.setStyleSheet(""" + QPushButton { + background-color: #fff8e1; + border: 1px solid #ffc107; + padding: 8px 16px; + font-weight: bold; + border-radius: 4px; + } + QPushButton:hover { + background-color: #fff3e0; + } + """) + + # 取消按钮 + self.cancel_button = QPushButton("取消") + self.cancel_button.setFont(self.normal_font) + self.cancel_button.setStyleSheet(""" + QPushButton { + background-color: #f5f5f5; + border: 1px solid #e0e0e0; + padding: 8px 16px; + font-weight: bold; + border-radius: 4px; + } + QPushButton:hover { + background-color: #eeeeee; + } + """) + + # 添加按钮到布局 + button_layout.addStretch() + button_layout.addWidget(self.confirm_button) + button_layout.addWidget(self.cancel_button) + + # 将按钮布局添加到主布局 + self.main_layout.addLayout(button_layout) \ No newline at end of file diff --git a/utils/pallet_type_manager.py b/utils/pallet_type_manager.py index ad2bd23..aa4269e 100644 --- a/utils/pallet_type_manager.py +++ b/utils/pallet_type_manager.py @@ -199,13 +199,13 @@ class PalletTypeManager: else: return None - def get_pallet_type_by_pallet_id(self, pallet_id): + def get_pallet_type_by_pallet_id(self, pallet_code): """根据托盘号获取托盘类型 Args: - pallet_id: 托盘号 + pallet_code: 托盘号 """ - result = self.dao.get_pallet_type_by_pallet_id(pallet_id) + result = self.dao.get_pallet_type_by_pallet_id(pallet_code) if result: return result else: diff --git a/widgets/main_window.py b/widgets/main_window.py index 16e3766..91d1a68 100644 --- a/widgets/main_window.py +++ b/widgets/main_window.py @@ -396,67 +396,67 @@ class MainWindow(MainWindowUI): def handle_output(self): """处理下料按钮点击事件""" - # 创建对话框 - dialog = QDialog(self) - dialog.setWindowTitle("下料操作") - dialog.setFixedSize(300, 200) + # 获取托盘号 + tray_id = self.tray_edit.currentText() + if not tray_id: + QMessageBox.warning(self, "提示", "请先选择或输入托盘号") + return + + # 启动监听(不论后续是否确认下料) + # 启动Modbus监控 + if not hasattr(self, 'modbus_monitor') or not self.modbus_monitor.is_running(): + self.setup_modbus_monitor() + logging.info("已在下料操作前启动Modbus监控") - # 对话框布局 - layout = QVBoxLayout(dialog) + # 启动串口监听 + self.serial_manager.auto_open_configured_ports() - # 添加提示信息 - info_label = QLabel("请选择下料托盘类型:") - info_label.setFont(self.normal_font) - layout.addWidget(info_label) + # 启动键盘监听器 + self.serial_manager.start_keyboard_listener() + logging.info("已在下料操作前启动键盘监听器") - # 添加托盘类型选择 - pallet_combo = QComboBox() - pallet_combo.setFont(self.normal_font) - # 复制当前托盘类型选择器的内容 - for i in range(self.output_pallet_type_combo.count()): - pallet_combo.addItem(self.output_pallet_type_combo.itemText(i)) - layout.addWidget(pallet_combo) - - # 添加按钮 - button_layout = QHBoxLayout() - confirm_button = QPushButton("确认") - confirm_button.setFont(self.normal_font) - confirm_button.setStyleSheet("background-color: #fff8e1; border: 1px solid #ffc107; padding: 8px 16px; font-weight: bold; border-radius: 4px;") - - cancel_button = QPushButton("取消") - cancel_button.setFont(self.normal_font) - cancel_button.setStyleSheet("padding: 8px 16px; font-weight: bold; border-radius: 4px;") - - button_layout.addStretch() - button_layout.addWidget(confirm_button) - button_layout.addWidget(cancel_button) - layout.addLayout(button_layout) - - # 连接按钮信号 - confirm_button.clicked.connect(dialog.accept) - cancel_button.clicked.connect(dialog.reject) + # 创建下料对话框 + from widgets.unloading_dialog_widget import UnloadingDialog + dialog = UnloadingDialog(parent=self) # 显示对话框 result = dialog.exec() - + # 如果用户确认,则执行下料操作 if result == QDialog.Accepted: - selected_type = pallet_combo.currentText() - # 获取托盘的排序,该顺序影响着下料寄存器的写入值 切记,需要和 PLC 确认沟通完成后才能修改排序值 - pallets_dict = self.pallet_type_manager.get_pallet_type_by_type(selected_type) + # 获取托盘料值作为下料层数 + stow_num = dialog.pallet_material_value.text() + if stow_num == "--" or not stow_num: + QMessageBox.warning(self, "错误", "未获取到托盘料信息,请重试") + return + + # 获取托盘号对应的托盘类型 + pallet_type = self.pallet_type_manager.get_pallet_type_by_pallet_id(tray_id) + # 初始化托盘号对应的序号 + if tray_id not in self.init_seq: + self.init_seq[tray_id] = 1 + + if not pallet_type: + QMessageBox.warning(self, "错误", "未查到对应下料托盘类型") + return + # 执行Modbus操作 modbus = ModbusUtils() client = modbus.get_client() try: - #TODO: 下料 D3 寄存器写入 1 D1 寄存器写入托盘类型 - if modbus.write_register_until_success(client, 3, 1) and modbus.write_register_until_success(client, 1, pallets_dict.get(selected_type)): + # 下料 D3 寄存器写入 1 D1 寄存器写入托盘类型 + success0 = modbus.write_register_until_success(client, 0, int(stow_num)) + success1 = modbus.write_register_until_success(client, 1, int(pallet_type)) + success3 = modbus.write_register_until_success(client, 3, 1) + + if success0 and success1 and success3: # 创建状态标签并显示在右上角 - self.show_operation_status("下料托盘", "output", selected_type) + self.show_operation_status("码垛层数", "output", stow_num) else: - QMessageBox.information(self, "操作提示", "下料失败") + QMessageBox.information(self, "操作提示", "码垛层数") except Exception as e: - logging.error(f"下料操作失败: {str(e)}") - QMessageBox.critical(self, "错误", f"下料操作失败: {str(e)}") + logging.error(f"码垛层数操作失败: {str(e)}") + QMessageBox.critical(self, "错误", f"码垛失败: {str(e)}") finally: modbus.close_client(client) diff --git a/widgets/unloading_dialog_widget.py b/widgets/unloading_dialog_widget.py new file mode 100644 index 0000000..c880c1d --- /dev/null +++ b/widgets/unloading_dialog_widget.py @@ -0,0 +1,112 @@ +from ui.unloading_dialog_ui import UnloadingDialogUI +from apis.tary_api import TaryApi +from PySide6.QtCore import Qt, Signal +from PySide6.QtWidgets import QMessageBox, QDialog +import logging + +class UnloadingDialog(UnloadingDialogUI): + # 定义一个信号,用于向主窗口传递托盘号 + tray_code_signal = Signal(str, str, str, str) + + def __init__(self, parent=None): + """初始化下料对话框""" + super().__init__() + self.parent = parent + + # 初始化API + self.tary_api = TaryApi() + + # 彻底禁用对话框的回车键关闭功能 + self.setModal(True) + # 禁用所有按钮的默认行为 + self.confirm_button.setAutoDefault(False) + self.confirm_button.setDefault(False) + self.cancel_button.setAutoDefault(False) + self.cancel_button.setDefault(False) + + # 设置对话框特性,按下Escape键才能关闭 + self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) + + # 绑定事件 + self.setup_connections() + + def setup_connections(self): + """设置事件连接""" + # 托盘号输入框回车事件触发查询 + self.tray_input.returnPressed.connect(self.handle_tray_return_pressed) + self.tray_input.editingFinished.connect(self.on_tray_query) + + # 确认按钮点击事件 + self.confirm_button.clicked.connect(self.accept) + + # 取消按钮点击事件 + self.cancel_button.clicked.connect(self.reject) + + def handle_tray_return_pressed(self): + """处理托盘输入框的回车事件""" + # 阻止事件传播 + logging.info("托盘输入框回车事件触发") + self.on_tray_query() + + # 阻止事件继续传播 + return True + + def on_tray_query(self): + """查询托盘信息""" + try: + tray_code = self.tray_input.text().strip() + if not tray_code: + return + + logging.info(f"查询托盘号: {tray_code}") + + # 调用API获取托盘信息 + response = self.tary_api.get_tary_info(tray_code) + + logging.info(f"托盘信息响应: {response}") + logging.info(f"response.success={response.get('success')}, response.data存在={response.get('data') is not None}") + + if response.get("success", False) and response.get("data"): + tray_data = response.get("data", {}) + logging.info(f"托盘数据: {tray_data}") + + # 显示托盘相关信息 + axis_type = str(tray_data.get("axis_type", "--")) + material = str(tray_data.get("material", "--")) + weight = str(tray_data.get("weight", "--")) + + logging.info(f"显示托盘信息: 轴型={axis_type}, 托盘料={material}, 重量={weight}") + + # 设置显示值 + self.axis_value.setText(axis_type) + self.pallet_material_value.setText(material) + self.quantity_value.setText("") # 数量为空 + self.weight_value.setText(f"{weight} kg") + + # 发送托盘号到主窗口 + from widgets.main_window import MainWindow + main_window = self.parent + if main_window and isinstance(main_window, MainWindow): + # 检查托盘号是否已存在 + existed = False + for i in range(main_window.tray_edit.count()): + if main_window.tray_edit.itemText(i) == tray_code: + existed = True + break + + # 如果不存在,则添加 + if not existed: + logging.info(f"添加托盘号到主窗口: {tray_code}") + main_window.tray_edit.addItem(tray_code) + + # 设置当前选中的托盘号 + main_window.tray_edit.setCurrentText(tray_code) + logging.info(f"设置主窗口当前托盘号: {tray_code}") + else: + # 获取托盘信息失败 + error_msg = response.get("message", "获取托盘信息失败") + logging.warning(f"查询失败: {error_msg}") + QMessageBox.warning(self, "查询失败", error_msg) + except Exception as e: + logging.error(f"查询托盘信息异常: {str(e)}") + QMessageBox.critical(self, "查询异常", f"查询托盘信息时发生异常: {str(e)}") \ No newline at end of file