脚本优化 工序名称查询条件优化

main
risingLee 2026-02-25 14:03:05 +08:00
parent c73d347e01
commit da0e41f475
2 changed files with 211 additions and 13 deletions

View File

@ -1196,6 +1196,30 @@ class MainWindow(QMainWindow):
self.start_work_order_btn.clicked.connect(self._start_work_order)
toolbar_layout.addWidget(self.start_work_order_btn)
# 实验台上电按钮
self.power_on_btn = QPushButton("实验台上电")
self.power_on_btn.setStyleSheet(
"QPushButton { font-weight:700; font-size:13px; color:white;"
"padding:8px 20px; border-radius:6px; background-color:#4caf50; }"
"QPushButton:hover { background-color:#388e3c; }"
)
self.power_on_btn.setCursor(Qt.PointingHandCursor)
self.power_on_btn.setFixedHeight(36)
self.power_on_btn.clicked.connect(self._power_on_experiment_table)
toolbar_layout.addWidget(self.power_on_btn)
# 实验台断电按钮
self.power_off_btn = QPushButton("实验台断电")
self.power_off_btn.setStyleSheet(
"QPushButton { font-weight:700; font-size:13px; color:white;"
"padding:8px 20px; border-radius:6px; background-color:#f44336; }"
"QPushButton:hover { background-color:#d32f2f; }"
)
self.power_off_btn.setCursor(Qt.PointingHandCursor)
self.power_off_btn.setFixedHeight(36)
self.power_off_btn.clicked.connect(self._power_off_experiment_table)
toolbar_layout.addWidget(self.power_off_btn)
# 开始记录按钮仅Debug模式显示
self.start_exp_btn = QPushButton("开始记录")
self.start_exp_btn.setStyleSheet(
@ -4441,21 +4465,42 @@ class MainWindow(QMainWindow):
self.logger.error(f"Failed to delete abandoned experiment record: {e}")
# 只有当最新的记录未结束时才提示
elif end_ts is None:
# 若用户此时关闭程序,视为本次实验“非正常终止”,应标记为作废
reply = QMessageBox.question(
self,
"实验记录未结束",
f"检测到最新的实验记录未结束(开始时间: {start_ts}\n\n是否结束当前实验记录并关闭程序?",
f"检测到最新的实验记录未结束(开始时间: {start_ts}\n\n"
"关闭程序将把该实验记录标记为【作废】。\n\n"
"是否继续并作废该实验记录?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply == QMessageBox.Yes:
# 用户确认结束实验
# 用户确认关闭:将该实验标记为作废,而不是已完成
try:
self._end_experiment()
self.logger.info("Experiment ended before closing application")
db = sqlite3.connect(str(APP_DIR / "experiments.db"))
cur = db.cursor()
cur.execute(
"""
UPDATE experiments
SET
end_ts = COALESCE(end_ts, datetime('now')),
is_terminated = 1,
remark = CASE
WHEN remark IS NULL OR remark = '' THEN '作废-手动关闭程序'
WHEN remark LIKE '%作废-手动关闭程序%' THEN remark
ELSE remark || ' [作废-手动关闭程序]'
END
WHERE id = ?
""",
(eid,),
)
db.commit()
db.close()
self.logger.info(f"[关闭程序] 实验 {eid} 在窗口关闭时被标记为作废")
except Exception as e:
self.logger.error(f"Failed to end experiment: {e}")
self.logger.error(f"[关闭程序] 标记实验为作废失败: {e}")
event.accept()
else:
# 用户取消关闭
@ -5356,12 +5401,94 @@ class MainWindow(QMainWindow):
except Exception as e:
self.logger.error(f"[UI更新] 处理连接状态变化失败: {e}", exc_info=True)
def _power_on_experiment_table(self) -> None:
"""实验台上电按钮点击事件"""
try:
# 确认对话框
reply = QMessageBox.question(
self,
"确认上电",
"确定要给实验台上电吗?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply != QMessageBox.Yes:
return
self.logger.info("[实验台上电] 开始执行上电操作")
self.statusBar().showMessage("正在执行实验台上电...", 2000)
# 写入 Modbus 寄存器0x5555 = 上电(合闸)
# 参考 pcm-influxdb-debug.py0x5555 会触发 closeBreaker()(合闸/上电)
success = self._write_modbus_control_register(0x5555)
if success:
self.logger.info("[实验台上电] ✅ 上电指令发送成功")
self.statusBar().showMessage("✅ 实验台上电成功", 3000)
else:
self.logger.error("[实验台上电] ❌ 上电指令发送失败")
self.statusBar().showMessage("❌ 实验台上电失败请检查Modbus连接", 5000)
QMessageBox.warning(
self,
"上电失败",
"实验台上电失败,请检查:\n"
"1. Modbus连接配置是否正确\n"
"2. 设备是否在线\n"
"3. 指令值是否正确"
)
except Exception as e:
self.logger.error(f"[实验台上电] 异常: {e}", exc_info=True)
self.statusBar().showMessage(f"❌ 实验台上电异常: {e}", 5000)
QMessageBox.critical(self, "错误", f"实验台上电时发生异常: {str(e)}")
def _power_off_experiment_table(self) -> None:
"""实验台断电按钮点击事件"""
try:
# 确认对话框
reply = QMessageBox.question(
self,
"确认断电",
"确定要给实验台断电吗?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No
)
if reply != QMessageBox.Yes:
return
self.logger.info("[实验台断电] 开始执行断电操作")
self.statusBar().showMessage("正在执行实验台断电...", 2000)
# 写入 Modbus 寄存器0x0000 = 断电(开闸)
# 参考 pcm-influxdb-debug.py0x0000 会触发 openBreaker()(开闸/断电)
success = self._write_modbus_control_register(0x0000)
if success:
self.logger.info("[实验台断电] ✅ 断电指令发送成功")
self.statusBar().showMessage("✅ 实验台断电成功", 3000)
else:
self.logger.error("[实验台断电] ❌ 断电指令发送失败")
self.statusBar().showMessage("❌ 实验台断电失败请检查Modbus连接", 5000)
QMessageBox.warning(
self,
"断电失败",
"实验台断电失败,请检查:\n"
"1. Modbus连接配置是否正确\n"
"2. 设备是否在线\n"
"3. 指令值是否正确"
)
except Exception as e:
self.logger.error(f"[实验台断电] 异常: {e}", exc_info=True)
self.statusBar().showMessage(f"❌ 实验台断电异常: {e}", 5000)
QMessageBox.critical(self, "错误", f"实验台断电时发生异常: {str(e)}")
def _write_modbus_control_register(self, value: int) -> bool:
"""通过原始Socket直接发送Modbus TCP报文写入控制寄存器1200
完全抛弃pymodbus使用最底层的socket通信模拟Modbus Poll的行为
Args:
value: 控制值 (0x5555=继续/开始, 0xAAAA=暂停, 0xFFFF=作废, 1=开始记录, 0=停止记录)
value: 控制值 (0x5555=继续/开始/上电合闸, 0xAAAA=暂停, 0xFFFF=作废, 0x0000=停止/断电开闸, 1=开始记录, 0=停止记录)
Returns:
bool: 写入是否成功
@ -6124,6 +6251,74 @@ class MainWindow(QMainWindow):
return ts
def _cleanup_incomplete_experiments_on_startup() -> None:
"""
应用启动时清理上次异常退出遗留的实验记录
- 对于已经开始(start_ts 不为空)但没有结束时间(end_ts 为空)且未标记作废(is_terminated=0)的记录
自动标记为作废并补充 end_ts remark
"""
try:
import sqlite3
from pathlib import Path
from logger import get_logger
logger = get_logger()
db_path = Path(__file__).parent / "experiments.db"
if not db_path.exists():
return
db = sqlite3.connect(str(db_path))
cur = db.cursor()
# 查找未正常结束且未标记作废的实验
cur.execute(
"""
SELECT id, start_ts, end_ts, is_terminated, remark
FROM experiments
WHERE start_ts IS NOT NULL
AND (end_ts IS NULL OR end_ts = '')
AND (is_terminated IS NULL OR is_terminated = 0)
"""
)
rows = cur.fetchall()
if not rows:
db.close()
return
logger.info("[启动清理] 检测到 %d 条未正常结束的实验记录,将标记为作废", len(rows))
for eid, start_ts, end_ts, is_terminated, remark in rows:
cur.execute(
"""
UPDATE experiments
SET
end_ts = COALESCE(end_ts, datetime('now')),
is_terminated = 1,
remark = CASE
WHEN remark IS NULL OR remark = '' THEN '作废-程序异常退出'
WHEN remark LIKE '%作废-程序异常退出%' THEN remark
ELSE remark || ' [作废-程序异常退出]'
END
WHERE id = ?
""",
(eid,),
)
logger.info("[启动清理] 实验 %s 已标记为作废(程序异常退出)", eid)
db.commit()
db.close()
except Exception as e:
try:
from logger import get_logger
logger = get_logger()
logger.error("[启动清理] 清理未完成实验记录失败: %s", e, exc_info=True)
except Exception:
pass
def run_app() -> None:
import sys
@ -6174,6 +6369,9 @@ def run_app() -> None:
logger = get_logger()
logger.error("数据库初始化失败,程序可能无法正常运行")
# 继续运行,但记录错误
else:
# 数据库结构准备好后,清理上次异常退出遗留的实验记录
_cleanup_incomplete_experiments_on_startup()
except Exception as e:
logger = get_logger()
logger.error(f"数据库初始化异常: {e}", exc_info=True)

View File

@ -89,7 +89,7 @@ def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
字典包含
- work_order_no: 工单号
- process_no: 工序号 (CSYMBOL)
- process_name: 工序名称 (CNEXTPROCNAME)
- process_name: 工序名称 (CPROCNAME)
- part_no: 零件号 (CMATERIALSYMBOL)
- executor: 执行人 (CEXCUTOR)
"""
@ -169,13 +169,13 @@ def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
sql_query = """
SELECT
mte.CSYMBOL,
mte.CNEXTPROCNAME,
mte.CPROCNAME,
mpe.CMATERIALSYMBOL,
mte.CEXCUTOR
FROM MES_TASK_EXTEND mte
INNER JOIN MBP_PROJECT_EXTEND mpe ON mte.CPGDID = mpe.CID
WHERE mte.CSYMBOL LIKE ?
AND RTRIM(LTRIM(ISNULL(mte.CNEXTPROCNAME, ''))) LIKE ?
AND RTRIM(LTRIM(ISNULL(mte.CPROCNAME, ''))) LIKE ?
"""
# 添加通配符进行模糊匹配
@ -193,7 +193,7 @@ def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
work_order_info = {
'work_order_no': work_order_no,
'process_no': row.CSYMBOL if row.CSYMBOL else '', # 工序号
'process_name': row.CNEXTPROCNAME if row.CNEXTPROCNAME else '', # 工序名称
'process_name': row.CPROCNAME if row.CPROCNAME else '', # 工序名称
'part_no': row.CMATERIALSYMBOL if row.CMATERIALSYMBOL else '', # 零件号
'executor': row.CEXCUTOR if row.CEXCUTOR else '' # 执行人
}
@ -226,13 +226,13 @@ def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
sql_query = """
SELECT
mte.CSYMBOL,
mte.CNEXTPROCNAME,
mte.CPROCNAME,
mpe.CMATERIALSYMBOL,
mte.CEXCUTOR
FROM MES_TASK_EXTEND mte
INNER JOIN MBP_PROJECT_EXTEND mpe ON mte.CPGDID = mpe.CID
WHERE mte.CSYMBOL LIKE %s
AND RTRIM(LTRIM(ISNULL(mte.CNEXTPROCNAME, ''))) LIKE %s
AND RTRIM(LTRIM(ISNULL(mte.CPROCNAME, ''))) LIKE %s
"""
# 添加通配符进行模糊匹配
@ -249,7 +249,7 @@ def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
work_order_info = {
'work_order_no': work_order_no,
'process_no': result.get('CSYMBOL', ''),
'process_name': result.get('CNEXTPROCNAME', ''),
'process_name': result.get('CPROCNAME', ''),
'part_no': result.get('CMATERIALSYMBOL', ''),
'executor': result.get('CEXCUTOR', '')
}