From fc8b38d44d930e3a6130ebeca54c066d74f76e63 Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Mon, 13 Apr 2026 14:57:44 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=BF=BD=E8=B8=AA=E7=B3=BB=E7=BB=9F):=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=92=A2=E5=8D=B7=E8=BF=BD=E8=B8=AA=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E4=B8=BA=E6=8C=89=E9=92=A2=E5=8D=B7=E5=8F=B7=E5=8D=87?= =?UTF-8?q?=E5=BA=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 前端调整位置数量从5个改为4个并更新提示信息 - 后端修改SQL查询按COILID升序获取钢卷 - 新增按钢卷号范围查询功能 - 实现追踪状态管理,支持追踪结束检测和恢复 - 优化信号处理逻辑,支持末卷重复信号处理 --- backend/opc_service.py | 192 ++++++++++++++++++++++++++----- backend/sqlite_sync.py | 36 +++++- frontend/src/views/TrackCoil.vue | 6 +- 3 files changed, 201 insertions(+), 33 deletions(-) diff --git a/backend/opc_service.py b/backend/opc_service.py index 0d2cd37..d172300 100644 --- a/backend/opc_service.py +++ b/backend/opc_service.py @@ -64,7 +64,10 @@ class OpcService: self.signal2_last: Optional[int] = None self.signal2_rise_time: Optional[datetime] = None self.signal1_coils: List[Dict[str, Any]] = [] - self.current_seq_start: int = 1 + self.first_coilid: str = "" + self.last_tracked_coilid: str = "" + self.end_coilid: str = "" + self.tracking_ended: bool = False # 状态机: WAIT_S1=等待信号1, WAIT_S2=等待信号2 self.track_state: str = "WAIT_S1" self.last_counter_at_state_change: Optional[Any] = None @@ -274,58 +277,138 @@ class OpcService: self.signal2_last = signal2_value async def _handle_signal1(self): - """Handle signal1: fetch next 5 coils from PDI and save to SQLite temp table.""" - from sqlite_sync import ( - sqlite_get_max_sequencenb, - sqlite_get_coils_by_sequencenb_range, - sqlite_save_coils_to_track - ) + """Handle signal1: fetch next 4 coils from Oracle PDI table and save to SQLite temp table.""" + from sqlite_sync import sqlite_save_coils_to_track, sqlite_clear_coil_track def _do_fetch(): - max_seq = sqlite_get_max_sequencenb() - if max_seq is None or max_seq < 1: - self._log("Signal1: No PDI data available") + # 如果追踪已结束,检查是否有新钢卷需要恢复追踪 + self._log(f"Signal1: Called, first_coilid={repr(self.first_coilid)}, tracking_ended={self.tracking_ended}") + if self.tracking_ended: + try: + from database import get_connection + conn = get_connection() + cursor = conn.cursor() + try: + cursor.execute("SELECT COILID FROM PLTM.PDI_PLTM ORDER BY COILID ASC") + all_coil_ids = [r[0] for r in cursor.fetchall()] + # 使用 end_coilid 判断是否有新钢卷 + check_coilid = self.end_coilid or self.last_tracked_coilid + if all_coil_ids and check_coilid: + has_new = any(cid > check_coilid for cid in all_coil_ids) + if has_new: + self._log("Signal1: New coils detected, resuming tracking") + self.tracking_ended = False + # 从最后一个追踪的钢卷之后继续 + self.first_coilid = check_coilid + else: + self._log("Signal1: Tracking ended, no new coils, skip") + return + elif all_coil_ids and not check_coilid: + # 没有结束点记录但有计划,按首卷重新启动追踪 + self._log("Signal1: Tracking ended without checkpoint, restarting from first plan") + self.tracking_ended = False + self.first_coilid = "" + else: + # 没有计划,保持结束状态 + self._log("Signal1: Tracking ended, PDI empty, skip") + return + finally: + cursor.close() + conn.close() + except Exception as exc: + self._log(f"Signal1: Check new coils failed: {exc}") + return + + try: + from database import get_connection + conn = get_connection() + cursor = conn.cursor() + try: + if self.first_coilid: + cursor.execute(""" + SELECT COILID, SEQUENCENB, ROLLPROGRAMNB + FROM PLTM.PDI_PLTM + WHERE COILID >= :start_coilid + ORDER BY COILID ASC + """, {"start_coilid": self.first_coilid}) + else: + cursor.execute(""" + SELECT COILID, SEQUENCENB, ROLLPROGRAMNB + FROM PLTM.PDI_PLTM + ORDER BY COILID ASC + """) + rows = cursor.fetchmany(4) + coils = [{"coilid": r[0], "sequencenb": r[1], "rollprogramnb": r[2]} for r in rows] + finally: + cursor.close() + conn.close() + except Exception as exc: + self._log(f"Signal1: Failed to fetch from Oracle: {exc}") return - start_seq = self.current_seq_start - end_seq = min(start_seq + 4, max_seq) - - if end_seq < start_seq: - self._log(f"Signal1: Insufficient PDI data (max_seq={max_seq}, start={start_seq})") - return - - coils = sqlite_get_coils_by_sequencenb_range(start_seq, end_seq) if len(coils) == 0: - self._log(f"Signal1: No coils found") + self._log("Signal1: No more coils in PDI, ending tracking") + sqlite_clear_coil_track() + self.tracking_ended = True + return + + if len(coils) == 1 and coils[0]["coilid"] == self.first_coilid: + self._log("Signal1: Only the same coil exists, saving it and setting for next query") + self.signal1_coils = coils + sqlite_save_coils_to_track(coils) + # 保持 first_coilid 不变,下次查询会从同一个位置继续 + # Signal2 触发后,检查是否还有更多钢卷 return self.signal1_coils = coils sqlite_save_coils_to_track(coils) - self._log(f"Signal1: Saved {len(coils)} coils (seq {start_seq}-{end_seq}) to temp table") + if len(coils) >= 2: + self.first_coilid = coils[1]["coilid"] + else: + self.first_coilid = coils[0]["coilid"] - self.current_seq_start = start_seq + 1 + self.last_tracked_coilid = coils[0]["coilid"] + + self._log(f"Signal1: Saved {len(coils)} coils, next_start: {self.first_coilid}, last_tracked: {self.last_tracked_coilid}") loop = asyncio.get_event_loop() await loop.run_in_executor(None, _do_fetch) async def _handle_signal2(self): """Handle signal2: update CMPT_PL_TRACKMAP with temp table data.""" - from sqlite_sync import sqlite_get_coil_track + from sqlite_sync import sqlite_clear_coil_track, sqlite_get_coil_track from database import get_connection def _do_update(): coils = sqlite_get_coil_track() - if not coils: - self._log("Signal2: No coils in temp track table") - return try: conn = get_connection() cursor = conn.cursor() try: + if not coils: + self._log("Signal2: No coils in temp track table, clearing all positions") + for pos in range(1, 11): + cursor.execute("SELECT COUNT(*) FROM PLTM.CMPT_PL_TRACKMAP WHERE POSITION = :pos", {"pos": pos}) + if cursor.fetchone()[0] > 0: + cursor.execute("UPDATE PLTM.CMPT_PL_TRACKMAP SET COILID = NULL, TOM = SYSDATE WHERE POSITION = :pos", {"pos": pos}) + conn.commit() + # 标记追踪结束 + if self.signal1_coils: + self.end_coilid = self.signal1_coils[0]["coilid"] + self.tracking_ended = True + # 清空 first_coilid,下次 Signal1 触发时从头查询 + self.first_coilid = "" + self._log(f"Signal2: Tracking ended (no coils), end_coilid={self.end_coilid}") + return + + cursor.execute("SELECT COILID FROM PLTM.CMPT_PL_TRACKMAP WHERE POSITION = 1") + prev_pos1_coil = cursor.fetchone() + prev_pos1_coilid = prev_pos1_coil[0] if prev_pos1_coil else None + coil_count = len(coils) - for i in range(5): + for i in range(4): target_position = i + 1 coil_index = target_position - 1 if coil_index >= 0 and coil_index < coil_count: @@ -357,8 +440,63 @@ class OpcService: INSERT INTO PLTM.CMPT_PL_TRACKMAP (POSITION, COILID, TOM) VALUES (:position, NULL, SYSDATE) """, {"position": target_position}) + + for pos in range(5, 11): + cursor.execute("SELECT COUNT(*) FROM PLTM.CMPT_PL_TRACKMAP WHERE POSITION = :pos", {"pos": pos}) + if cursor.fetchone()[0] > 0: + cursor.execute("UPDATE PLTM.CMPT_PL_TRACKMAP SET COILID = NULL, TOM = SYSDATE WHERE POSITION = :pos", {"pos": pos}) + + new_pos1_coil = coils[0]["coilid"] if coils else None + pos1_unchanged = prev_pos1_coilid and new_pos1_coil and prev_pos1_coilid == new_pos1_coil + if pos1_unchanged: + # 只在“末卷(单卷)重复信号”时清空,避免 6-7 阶段提前进入清空/重写循环 + if coil_count == 1: + for pos in range(1, 11): + cursor.execute("SELECT COUNT(*) FROM PLTM.CMPT_PL_TRACKMAP WHERE POSITION = :pos", {"pos": pos}) + if cursor.fetchone()[0] > 0: + cursor.execute("UPDATE PLTM.CMPT_PL_TRACKMAP SET COILID = NULL, TOM = SYSDATE WHERE POSITION = :pos", {"pos": pos}) + sqlite_clear_coil_track() + self.end_coilid = coils[0]["coilid"] + self.last_tracked_coilid = self.end_coilid + self.tracking_ended = True + self.first_coilid = "" + self._log("Signal2: Final single coil repeated, cleared all positions and ended tracking") + else: + self._log("Signal2: Coil at position 1 unchanged but not final single-coil stage, keep tracking") + conn.commit() - self._log(f"Signal2: Updated 5 positions (coils: {coil_count})") + self._log(f"Signal2: Updated 4 positions (coils: {coil_count})") + + # 检查是否还有更多钢卷可以追踪 + self._log(f"Signal2: Checking remaining coils, first_coilid={repr(self.first_coilid)}") + + if self.first_coilid: + try: + cursor.execute(""" + SELECT COILID FROM PLTM.PDI_PLTM + WHERE COILID >= :start_coilid + ORDER BY COILID ASC + """, {"start_coilid": self.first_coilid}) + all_remaining = cursor.fetchall() + remaining_count = len(all_remaining) + self._log(f"Signal2: Check remaining after {self.first_coilid}: count={remaining_count}, coils={[r[0] for r in all_remaining]}") + if remaining_count == 0: + # 没有更多钢卷了,标记追踪结束 + self.end_coilid = self.first_coilid + self.last_tracked_coilid = self.first_coilid + self.tracking_ended = True + self._log(f"Signal2: No more coils, tracking ended, end_coilid={self.end_coilid}") + elif remaining_count >= 2: + # 有2个以上钢卷,保持 first_coilid 不变,让 Signal1 处理 + self._log(f"Signal2: >=2 coils remain, first_coilid unchanged, waiting for Signal1") + else: + # 只有一个钢卷时先展示为“7(1个)”,下一次重复信号再清空到 NULL + self._log( + f"Signal2: One coil remains ({all_remaining[0][0]}), " + "waiting one more cycle before final clear" + ) + except Exception as exc: + self._log(f"Signal2: Check next coils failed: {exc}") finally: cursor.close() conn.close() diff --git a/backend/sqlite_sync.py b/backend/sqlite_sync.py index 8c6e765..a60e79d 100644 --- a/backend/sqlite_sync.py +++ b/backend/sqlite_sync.py @@ -337,7 +337,7 @@ def sqlite_get_coils_by_sequencenb_range(start_seq: int, end_seq: int) -> List[D SELECT COILID, SEQUENCENB, ROLLPROGRAMNB FROM PDI_PLTM WHERE SEQUENCENB >= ? AND SEQUENCENB <= ? - ORDER BY SEQUENCENB DESC + ORDER BY COILID ASC """, (start_seq, end_seq)) rows = cursor.fetchall() return [{"coilid": r[0], "sequencenb": r[1], "rollprogramnb": r[2]} for r in rows] @@ -345,12 +345,42 @@ def sqlite_get_coils_by_sequencenb_range(start_seq: int, end_seq: int) -> List[D sc.close() +def sqlite_get_coils_by_coilid(start_coilid: str, count: int = 4) -> List[Dict[str, Any]]: + sc = get_sqlite() + try: + cursor = sc.execute(""" + SELECT COILID, SEQUENCENB, ROLLPROGRAMNB + FROM PDI_PLTM + WHERE COILID >= ? + ORDER BY COILID ASC + LIMIT ? + """, (start_coilid, count)) + rows = cursor.fetchall() + return [{"coilid": r[0], "sequencenb": r[1], "rollprogramnb": r[2]} for r in rows] + finally: + sc.close() + + +def sqlite_get_first_coils(count: int = 4) -> List[Dict[str, Any]]: + sc = get_sqlite() + try: + cursor = sc.execute(""" + SELECT COILID, SEQUENCENB, ROLLPROGRAMNB + FROM PDI_PLTM + ORDER BY COILID ASC + LIMIT ? + """, (count,)) + rows = cursor.fetchall() + return [{"coilid": r[0], "sequencenb": r[1], "rollprogramnb": r[2]} for r in rows] + finally: + sc.close() + + def sqlite_save_coils_to_track(coils: List[Dict[str, Any]]): sc = get_sqlite() try: sc.execute("DELETE FROM COIL_TRACK_TEMP") - reversed_coils = list(reversed(coils)) - for i, coil in enumerate(reversed_coils): + for i, coil in enumerate(coils): sc.execute(""" INSERT INTO COIL_TRACK_TEMP (COILID, SEQUENCENB, ROLLPROGRAMNB, POSITION) VALUES (?, ?, ?, ?) diff --git a/frontend/src/views/TrackCoil.vue b/frontend/src/views/TrackCoil.vue index 5bee232..d59e7b1 100644 --- a/frontend/src/views/TrackCoil.vue +++ b/frontend/src/views/TrackCoil.vue @@ -7,7 +7,7 @@ 模拟信号1(入口) 模拟信号2(焊接完成) - 提示: Position 1对应产线入口(顺序号1), Position 5对应产线出口(顺序号5) + 提示: 追踪4个位置,按钢卷号升序(最小的先进产线)
@@ -21,7 +21,7 @@ @@ -40,7 +40,7 @@ - +