From 37d60ba63e3a50fd40366f6798e20d3b961d2879 Mon Sep 17 00:00:00 2001 From: yaj <1229314433@qq.com> Date: Sat, 28 Feb 2026 11:45:25 +0800 Subject: [PATCH] =?UTF-8?q?add(=E6=B4=BE=E5=8D=95=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=88=B7=E6=96=B0)=E3=80=81modify(=E6=B4=BE=E5=8D=95=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=92=8C=E7=AE=A1=E7=89=87=E4=BB=BB=E5=8A=A1=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E7=95=8C=E9=9D=A2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/opc_config.ini | 4 +- controller/main_controller.py | 67 ++++++------ service/pdrecord_query_thread.py | 27 +++++ view/main_window.py | 131 +++++++++++++++++------- view/widgets/dispatch_details_dialog.py | 16 ++- view/widgets/segment_details_dialog.py | 18 ++-- 6 files changed, 178 insertions(+), 85 deletions(-) create mode 100644 service/pdrecord_query_thread.py diff --git a/config/opc_config.ini b/config/opc_config.ini index 78af530..3a5d824 100644 --- a/config/opc_config.ini +++ b/config/opc_config.ini @@ -14,9 +14,9 @@ sub_interval = 500 [OPC_NODE_LIST] upper_weight = 2:upper,2:upper_weight lower_weight = 2:lower,2:lower_weight -pd_data = 2:pd,2:pd_data upper_hopper_position = 2:upper,2:upper_hopper_position upper_clamp_status = 2:upper,2:upper_clamp_status vibration_frequency=2:vibration_frequency production_progress=2:production_progress -segment_tasks=2:segment_tasks \ No newline at end of file +segment_tasks=2:segment_tasks +dispatch_tasks=2:dispatch_tasks \ No newline at end of file diff --git a/controller/main_controller.py b/controller/main_controller.py index 2b9fa38..707e481 100644 --- a/controller/main_controller.py +++ b/controller/main_controller.py @@ -11,7 +11,9 @@ from service.msg_recorder import MessageRecorder from service.opcua_ui_client import OpcuaUiClient from service.artifact_query_thread import ArtifactInfoQueryThread # 管片任务查询 -from busisness.models import ArtifactInfoModel +from service.pdrecord_query_thread import PDRecordQueryThread # 派单任务查询 +from busisness.models import ArtifactInfoModel +from busisness.models import PDRecordModel from typing import List @@ -42,8 +44,8 @@ class MainController: self.__connectSignals() def showMainWindow(self): - # self.main_window.showFullScreen() - self.main_window.show() + self.main_window.showFullScreen() + # self.main_window.show() def _initSubControllers(self): # 右侧视频显示控制模块 @@ -158,34 +160,18 @@ class MainController: import traceback traceback.print_exc() - def convert_to_ampm(self, time_str: str) -> str: - """时间格式转换: 转换为AM/PM形式""" - from datetime import datetime - time_formats = [ - "%Y-%m-%d %H:%M:%S", - "%Y-%m-%d %H:%M:%S.%f" - ] - for fmt in time_formats: - try: - dt = datetime.strptime(time_str, fmt) - return dt.strftime("%I:%M%p") - except ValueError: - continue - return "--:--" - def onUpdateUiByArtifactInfo(self, artifact_list:List[ArtifactInfoModel]): - for index, artifact in enumerate(artifact_list, 1): - if artifact.MouldCode: - self.main_window.segment_task_widget.set_task_id(f"task{index}", artifact.MouldCode) # 模具号 - if artifact.BetonVolume: - self.main_window.segment_task_widget.set_task_volume(f"task{index}", artifact.BetonVolume) # 浇筑方量 - if artifact.BeginTime: - time_str = self.convert_to_ampm(artifact.BeginTime) - self.main_window.segment_task_widget.set_task_time(f"task{index}", time_str) # 开始时间 - self.main_window.SetSegmentTaskDetails(f"task{index}", artifact) # 更新管片任务详情 + # 更新管片任务 + self.main_window.update_segment_tasks(artifact_list) # 将opc服务中的 segment_tasks的值复原为 0,以便下次触发管片更新 self.opc_client.write_value_by_name("segment_tasks", 0) + def onUpdateUiByPDRecord(self, pdrecord_list:List[PDRecordModel]): + # 更新派单任务 + self.main_window.update_dispatch_tasks(pdrecord_list) + # 将opc服务中的 dispatch_tasks的值复原为 0,以便下次触发派单更新 + self.opc_client.write_value_by_name("dispatch_tasks", 0) + # ======================== OPCUA值更新界面方法 ====================== def _update_upper_weight(self, val): # 更新上料斗重量 @@ -224,18 +210,31 @@ class MainController: def _update_segment_tasks(self, val): if val: # 需要更新管片任务 """更新左侧的管片任务""" - if hasattr(self, "query_thread") and self.query_thread.isRunning(): + if hasattr(self, "segment_query_thread") and self.segment_query_thread.isRunning(): return # 1. 管片信息查询线程 - self.query_thread = ArtifactInfoQueryThread() + self.segment_query_thread = ArtifactInfoQueryThread() # 2. 主线程更新管片任务UI - self.query_thread.query_finished.connect(self.onUpdateUiByArtifactInfo) + self.segment_query_thread.query_finished.connect(self.onUpdateUiByArtifactInfo) # 3. 查询管片信息错误 - self.query_thread.query_error.connect(self.onQueryArtifactInfoError) - self.query_thread.start() + self.segment_query_thread.query_error.connect(self.onQueryInfoError) + self.segment_query_thread.start() - def onQueryArtifactInfoError(self, error_msg:str): - # 查询管片信息失败预警 + def _update_dispatch_tasks(self, val): + if val: # 需要更新派单任务 + """更新右侧的派单任务""" + if hasattr(self, "dispatch_query_thread") and self.dispatch_query_thread.isRunning(): + return + # 1. 派单信息查询线程 + self.dispatch_query_thread = PDRecordQueryThread() + # 2. 主线程更新派单任务UI + self.dispatch_query_thread.query_finished.connect(self.onUpdateUiByPDRecord) + # 3. 查询派单信息错误 + self.dispatch_query_thread.query_error.connect(self.onQueryInfoError) + self.dispatch_query_thread.start() + + def onQueryInfoError(self, error_msg:str): + # 查询信息失败预警 self.msg_recorder.warning_record(error_msg) def _update_vibration_frequency(self, val): diff --git a/service/pdrecord_query_thread.py b/service/pdrecord_query_thread.py new file mode 100644 index 0000000..799a861 --- /dev/null +++ b/service/pdrecord_query_thread.py @@ -0,0 +1,27 @@ +from PySide6.QtWidgets import QWidget +from PySide6.QtCore import QThread, Signal +from busisness.blls import PDRecordBll +from busisness.models import PDRecordModel + +class PDRecordQueryThread(QThread): + # 定义信号:子线程查询完成 + query_finished = Signal(list) + # 定义信号:发送错误信息 + query_error = Signal(str) + + def __init__(self): + super().__init__() + + def run(self): + try: + pdrecord_dal = PDRecordBll() + pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据 + + if pdrecords: + # 查询完成,发射信号 + self.query_finished.emit(pdrecords) + else: + raise ValueError("未查询到有效数据") + except Exception as e: + print(f"更新派单任务数据失败: {str(e)}") + self.query_error.emit(f"更新派单任务数据失败: {str(e)}") \ No newline at end of file diff --git a/view/main_window.py b/view/main_window.py index cf46dfc..9dee8f8 100644 --- a/view/main_window.py +++ b/view/main_window.py @@ -26,6 +26,7 @@ from .widgets.segment_details_dialog import SegmentDetailsDialog from .widgets.dispatch_details_dialog import DispatchDetailsDialog from busisness.models import ArtifactInfoModel +from busisness.models import PDRecordModel class MainWindow(QWidget): @@ -35,17 +36,12 @@ class MainWindow(QWidget): def __init__(self): super().__init__() self.initWindow() - # 保存管片任务信息的字典 task1: ArtifactInfoModel1.... (用于显示管片任务详情) - self.artifact_dict = {} - # 当前点击/选中的 管片任务详情对应的任务名(task1\task2\task3) (用于刷新选中的管片任务详情) - self.current_selected_segment_detail_name = None + self.init_business_attributes() # 初始化业务属性 self.createSubWidgets() # 创建子部件 self.initSubWidgets() # 初始化子部件 self.setupLayout() # 设置布局 self.connectSignalToSlot() - - # 安装事件过滤器,处理计划方量的 QLineEdit的失去焦点事件 self.installEventFilter(self) @@ -103,6 +99,16 @@ class MainWindow(QWidget): self.update_background() + def init_business_attributes(self): + # 保存管片任务信息的字典 task1: ArtifactInfoModel1.... (用于显示管片任务详情) + self.artifact_dict = {} + # 当前点击/选中的 管片任务详情对应的任务名(task1\task2\task3) (用于刷新选中的管片任务详情) + self.current_selected_segment_detail_name = None + + # 保存派单任务信息的字典 task1: PDRecordModel1.... (用于显示派单任务详情) + self.dispatch_dict = {} + # 当前点击/选中的 派单任务详情对应的任务名(task1\task2\task3) (用于刷新选中的派单任务详情) + self.current_selected_dispatch_detail_name = None def createSubWidgets(self): """创建所有子部件实例""" @@ -157,44 +163,54 @@ class MainWindow(QWidget): return "--:--" def _init_segment_tasks(self): - """从数据库中读取管片任务数据并更新到UI""" + """初始化管片任务, 从数据库中读取管片任务数据并更新到UI""" try: from busisness.blls import ArtifactBll artifact_dal = ArtifactBll() artifacts = artifact_dal.get_artifact_task() # 获取管片任务数据 - # 遍历数据并更新UI - for i, artifact in enumerate(artifacts, 1): - if artifact.MouldCode: # 更新模具号 - self.segment_task_widget.set_task_id(f"task{i}", artifact.MouldCode) - if artifact.BetonVolume: # 更新方量 - self.segment_task_widget.set_task_volume(f"task{i}", artifact.BetonVolume) - if artifact.BeginTime: # 更新时间 (管片任务的开始时间) - # print("artifact.BeginTime: ", artifact.BeginTime) - self.segment_task_widget.set_task_time(f"task{i}", self.convert_to_ampm(artifact.BeginTime)) - self.SetSegmentTaskDetails(f"task{i}", artifact) # 设置管片任务详情信息 + # 更新管片任务 + self.update_segment_tasks(artifacts) except Exception as e: print(f"更新管片任务数据失败: {e}") + def update_segment_tasks(self, artifact_list:List[ArtifactInfoModel]): + # 更新管片任务相关的信息 + # 遍历数据并更新UI (包括左侧的管片任务 以及 管片任务详情) + for index, artifact in enumerate(artifact_list, 1): + if artifact.MouldCode: # 更新模具号 + self.segment_task_widget.set_task_id(f"task{index}", artifact.MouldCode) + if artifact.BetonVolume: # 更新浇筑方量 + self.segment_task_widget.set_task_volume(f"task{index}", artifact.BetonVolume) + if artifact.BeginTime: # 更新时间 (管片任务的开始时间) + # print("artifact.BeginTime: ", artifact.BeginTime) + self.segment_task_widget.set_task_time(f"task{index}", self.convert_to_ampm(artifact.BeginTime)) + self.SetSegmentTaskDetails(f"task{index}", artifact) # 设置管片任务详情信息 + def _init_dispatch_tasks(self): - """从数据库中读取派单任务数据并更新到UI""" + """初始化派单任务, 从数据库中读取派单任务数据并更新到UI""" try: from busisness.blls import PDRecordBll pdrecord_dal = PDRecordBll() pdrecords = pdrecord_dal.get_PD_record() # 获取派单任务数据 - # 遍历数据并更新UI - for i, record in enumerate(pdrecords, 1): - if record.MouldCode: # 更新模具号 - self.dispatch_task_widget.set_task_id(f"task{i}", record.MouldCode) - if record.BetonVolume: # 更新方量 - self.dispatch_task_widget.set_task_volume(f"task{i}", record.BetonVolume) - if record.CreateTime: # 更新时间 (派单任务的创建时间) - self.dispatch_task_widget.set_task_time(f"task{i}", self.convert_to_ampm(record.CreateTime)) - + # 更新派单任务 + self.update_dispatch_tasks(pdrecords) except Exception as e: print(f"更新派单任务数据失败: {e}") + def update_dispatch_tasks(self, pdrecord_list:List[PDRecordModel]): + # 更新派单任务相关的信息 + # 遍历数据并更新UI + for index, record in enumerate(pdrecord_list, 1): + if record.MouldCode: # 更新模具号 + self.dispatch_task_widget.set_task_id(f"task{index}", record.MouldCode) + if record.BetonVolume: # 更新方量(派单方量[最终计算出来的派单方量]) + self.dispatch_task_widget.set_task_volume(f"task{index}", record.FBetonVolume) + if record.CreateTime: # 更新时间 (派单任务的创建时间) + self.dispatch_task_widget.set_task_time(f"task{index}", self.convert_to_ampm(record.CreateTime)) + self.SetPDRecordTaskDetails(f"task{index}", record) + def setupLayout(self): """设置垂直布局,从上到下排列部件""" main_layout = QVBoxLayout(self) # 主布局:垂直布局 @@ -323,21 +339,28 @@ class MainWindow(QWidget): def handleDispatchTaskDetails(self, dispatch_task_name:str): # 派单任务名 task1、task2、task3 (分别对应第一条派单任务、 第二条派单任务...) - print("main_window: handleDispatchTaskDetails", dispatch_task_name) + # print("main_window: handleDispatchTaskDetails", dispatch_task_name) # 显示派单任务详情对话框 - dispatch_details_dialog = DispatchDetailsDialog(dispatch_task_name, self) - + if not hasattr(self, "dispatch_details_dialog"): + self.dispatch_details_dialog = DispatchDetailsDialog(dispatch_task_name, self) + pd_record:PDRecordModel = self.dispatch_dict.get(dispatch_task_name) + # 这里可以设置对话框显示的内容 如 set_segment_id # dispatch_details_dialog.set_segment_id("9999999999") # 设置派单任务详情中的方量的值 - current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name) - dispatch_details_dialog.set_row_value(4, str(current_volume)) # 派单方量的值的行号为4,第五行 + # current_volume = self.dispatch_task_widget.get_task_volume(dispatch_task_name) + # self.dispatch_details_dialog.set_row_value(4, str(current_volume)) # 派单方量的值的行号为4,第五行 + + # 更新派单任务详情按钮弹窗的显示 + self.updateDispatchTaskDetailsDialog(pd_record) # 派单任务详情页面中确定修改了派单任务的方量 # 备注:褚工说管片任务和派单任务中的方量都只有一位小数,料斗上的方量显示两位 2025/11/8 - dispatch_details_dialog.confirm_modify_volume.connect(self.handleModifyDispatchTaskVolume) - dispatch_details_dialog.show() + self.dispatch_details_dialog.confirm_modify_volume.connect(self.handleModifyDispatchTaskVolume) + self.dispatch_details_dialog.show() + # 更新选中的派单任务详情对应的任务名 + self.current_selected_dispatch_detail_name = dispatch_task_name def handleModifyDispatchTaskVolume(self, dispatch_task_name:str, modifyed_volume:float): """派单任务详情页面中, 修改了派单任务的方量""" @@ -345,6 +368,33 @@ class MainWindow(QWidget): self.dispatch_task_widget.set_task_volume(dispatch_task_name, modifyed_volume) # 其他操作,可能需要修改数据库的派单任务方量 + + def updateDispatchTaskDetailsDialog(self, pd_record:PDRecordModel): + if pd_record and hasattr(self, "dispatch_details_dialog"): + self.dispatch_details_dialog.set_segment_id(pd_record.ArtifactActionID) # 管片ID + self.dispatch_details_dialog.set_row_value(0, pd_record.CreateTime.split('.')[0] if pd_record.CreateTime else None) # 创建时间 (去掉毫秒) + self.dispatch_details_dialog.set_row_value(1, pd_record.OptTime.split('.')[0] if pd_record.OptTime else None) # 派单时间 (去掉毫秒) + self.dispatch_details_dialog.set_row_value(2, pd_record.TaskID) # 任务编号 + self.dispatch_details_dialog.set_row_value(3, pd_record.ProduceMixID) # 生产配合比编号(配比编号) + self.dispatch_details_dialog.set_row_value(4, pd_record.FBetonVolume) # 派单方量(最终计算出来的实际派单方量) + + # 派单状态 (1计划中2已下发0未知),默认1 + status_mapping = { + 1: "计划中", + 2: "已下发", + 0: None # 未知 + } + status_text = status_mapping.get(pd_record.Status) + self.dispatch_details_dialog.set_row_value(5, status_text) + + # 派单模式(1自动派单 2手动派单 0未知 )Mode: int = 0 + mode_mapping = { + 1: "自动派单", + 2: "手动派单", + 0: None # 未知 + } + mode_text = mode_mapping.get(pd_record.Mode) + self.dispatch_details_dialog.set_row_value(6, mode_text) # 更新 派单任务widget的坐标 @@ -426,8 +476,17 @@ class MainWindow(QWidget): # self.artifact_dict 管片信息的字典 def SetSegmentTaskDetails(self, task_name:str, artifact_info:ArtifactInfoModel): self.artifact_dict[task_name] = artifact_info - if task_name == self.current_selected_segment_detail_name: - self.updateSegmentTaskDetailsDialog(artifact_info) # 刷新管片任务详情按钮弹窗 + if hasattr(self, "segment_details_dialog") and self.segment_details_dialog and self.segment_details_dialog.isVisible(): + if task_name == self.current_selected_segment_detail_name: + self.updateSegmentTaskDetailsDialog(artifact_info) # 刷新管片任务详情按钮弹窗 + + # ======= 设置派单任务详情接口 ========== + # self.dispatch_dict 派单信息的字典 + def SetPDRecordTaskDetails(self, task_name:str, pd_record:PDRecordModel): + self.dispatch_dict[task_name] = pd_record + if hasattr(self, "dispatch_details_dialog") and self.dispatch_details_dialog and self.dispatch_details_dialog.isVisible(): + if task_name == self.current_selected_dispatch_detail_name: + self.updateDispatchTaskDetailsDialog(pd_record) # 刷新派单任务详情按钮弹窗 if __name__ == "__main__": import sys diff --git a/view/widgets/dispatch_details_dialog.py b/view/widgets/dispatch_details_dialog.py index 7a535a4..77ddedd 100644 --- a/view/widgets/dispatch_details_dialog.py +++ b/view/widgets/dispatch_details_dialog.py @@ -171,11 +171,18 @@ class DispatchDetailsDialog(QDialog): } """ ) - self.close_btn.clicked.connect(self.close) + self.close_btn.clicked.connect(self._on_close_button_clicked) parent_layout.addStretch() parent_layout.addWidget(self.close_btn) + def _on_close_button_clicked(self): + """关闭按钮点击后的处理逻辑""" + # 1. 执行取消修改方量的逻辑 + self.onCancelModifyVolume() + # 2. 执行关闭窗口的逻辑 + self.close() + def _add_segment_id_area(self, parent_layout): id_layout = QHBoxLayout() @@ -349,7 +356,8 @@ class DispatchDetailsDialog(QDialog): self.modify_btn.show() # 2、关闭派单方量调整控件 - self.volume_value_adjuster.close() + if hasattr(self, "volume_value_adjuster") and self.volume_value_adjuster: + self.volume_value_adjuster.close() def paintEvent(self, event): if not self.bg_pixmap.isNull(): @@ -368,12 +376,12 @@ class DispatchDetailsDialog(QDialog): def set_row_label(self, row, new_label_text: str): """修改左侧的显示的标签的文本,如: 创建时间、派单时间等""" if 0 <= row < len(self.rows): - self.rows[row].label.setText(new_label_text) + self.rows[row].label.setText(str(new_label_text) if new_label_text is not None else "") def set_row_value(self, row, new_value_text: str): """修改右侧的显示的值, 如: 2025年9月9日 9:9:9""" if 0 <= row < len(self.rows): - self.rows[row].value.setText(new_value_text) + self.rows[row].value.setText(str(new_value_text) if new_value_text is not None else "") # 测试代码 diff --git a/view/widgets/segment_details_dialog.py b/view/widgets/segment_details_dialog.py index b0bba0f..d27c370 100644 --- a/view/widgets/segment_details_dialog.py +++ b/view/widgets/segment_details_dialog.py @@ -257,9 +257,10 @@ class SegmentDetailsDialog(QDialog): row: 左列网格行号(0-6,共7行) new_label_text: 新的标签文字(如“管片编号”) """ - if new_label_text and 0 <= row < len(self.left_cells): + + if 0 <= row < len(self.left_cells): cell = self.left_cells[row] - cell.label.setText(str(new_label_text)) + cell.label.setText(str(new_label_text) if new_label_text else "") def set_left_value(self, row, new_value_text:str): """ @@ -268,9 +269,9 @@ class SegmentDetailsDialog(QDialog): row: 左列网格行号(0-6,共7行) new_value_text: 新的值(如“FB789”) """ - if new_value_text and 0 <= row < len(self.left_cells): + if 0 <= row < len(self.left_cells): cell = self.left_cells[row] - cell.value.setText(str(new_value_text)) + cell.value.setText(str(new_value_text) if new_value_text else "") def set_right_label(self, row, new_label_text:str): """ @@ -279,9 +280,9 @@ class SegmentDetailsDialog(QDialog): row: 右列网格行号(0-6,共7行) new_label_text: 新的标签文字(如“分块号”) """ - if new_label_text and 0 <= row < len(self.right_cells): + if 0 <= row < len(self.right_cells): cell = self.right_cells[row] - cell.label.setText(str(new_label_text)) + cell.label.setText(str(new_label_text) if new_label_text else "") def set_right_value(self, row, new_value_text:str): """ @@ -290,10 +291,9 @@ class SegmentDetailsDialog(QDialog): row: 右列网格行号(0-6,共7行) new_value_text: 新的值(如“FB789”) """ - if new_value_text and 0 <= row < len(self.left_cells): + if 0 <= row < len(self.left_cells): cell = self.right_cells[row] - cell.value.setText(str(new_value_text)) - + cell.value.setText(str(new_value_text) if new_value_text else "") # 测试代码 if __name__ == "__main__":