feat(追踪系统): 修改钢卷追踪逻辑为按钢卷号升序处理
- 前端调整位置数量从5个改为4个并更新提示信息 - 后端修改SQL查询按COILID升序获取钢卷 - 新增按钢卷号范围查询功能 - 实现追踪状态管理,支持追踪结束检测和恢复 - 优化信号处理逻辑,支持末卷重复信号处理
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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 (?, ?, ?, ?)
|
||||
|
||||
Reference in New Issue
Block a user