PCM_Report/work_order_query.py

322 lines
12 KiB
Python
Raw Normal View History

2025-12-11 14:32:31 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
工单查询模块 - 独立的SQL Server查询功能
"""
import json
import os
from typing import Dict, Optional
from pathlib import Path
from logger import get_logger
logger = get_logger()
# 配置文件路径
CONFIG_FILE = Path(__file__).parent / 'work_order_db_config.json'
# 默认配置
DEFAULT_CONFIG = {
'debug_mode': False, # Debug模式True时使用假数据不连接数据库
'host': 'localhost', # 数据库服务器地址
'port': 1433, # SQL Server端口通常是1433
'database': 'MES_DB', # 数据库名
'username': 'sa', # 用户名
'password': 'password', # 密码
2026-01-16 14:47:53 +08:00
'charset': 'utf8mb4',
'target_process_name': '泵空跑合' # 目标工序名称,用于精确查询工单
2025-12-11 14:32:31 +08:00
}
def load_db_config() -> Dict[str, any]:
"""
从配置文件加载数据库配置
如果配置文件不存在则创建默认配置文件
"""
if CONFIG_FILE.exists():
try:
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
config = json.load(f)
# 只提取需要的配置项
return {
'debug_mode': config.get('debug_mode', DEFAULT_CONFIG['debug_mode']),
'host': config.get('host', DEFAULT_CONFIG['host']),
'port': config.get('port', DEFAULT_CONFIG['port']),
'database': config.get('database', DEFAULT_CONFIG['database']),
'username': config.get('username', DEFAULT_CONFIG['username']),
'password': config.get('password', DEFAULT_CONFIG['password']),
2026-01-16 14:47:53 +08:00
'charset': config.get('charset', DEFAULT_CONFIG['charset']),
'target_process_name': config.get('target_process_name', DEFAULT_CONFIG['target_process_name']) or DEFAULT_CONFIG['target_process_name']
2025-12-11 14:32:31 +08:00
}
except Exception as e:
logger.error(f"读取配置文件失败: {e}")
return DEFAULT_CONFIG.copy()
else:
# 创建默认配置文件
try:
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
config_with_desc = DEFAULT_CONFIG.copy()
config_with_desc['description'] = '工单数据库连接配置'
config_with_desc['comment'] = '请根据实际环境修改以上配置'
json.dump(config_with_desc, f, ensure_ascii=False, indent=2)
logger.info(f"已创建默认配置文件: {CONFIG_FILE}")
except Exception as e:
logger.error(f"创建配置文件失败: {e}")
return DEFAULT_CONFIG.copy()
# 工单数据库配置(从文件加载)
WORK_ORDER_DB_CONFIG = load_db_config()
def reload_config():
"""
重新加载配置文件
"""
global WORK_ORDER_DB_CONFIG
WORK_ORDER_DB_CONFIG = load_db_config()
logger.info("工单数据库配置已重新加载")
def query_work_order(work_order_no: str) -> Optional[Dict[str, str]]:
"""
查询工单信息 - SQL Server版本
Args:
work_order_no: 工单号
Returns:
包含工单信息的字典如果查询失败则返回None
字典包含
- work_order_no: 工单号
- process_no: 工序号 (CSYMBOL)
2026-01-16 14:47:53 +08:00
- process_name: 工序名称 (CNEXTPROCNAME)
2025-12-11 14:32:31 +08:00
- part_no: 零件号 (CMATERIALSYMBOL)
- executor: 执行人 (CEXCUTOR)
"""
if not work_order_no:
logger.warning("工单号为空")
return None
# Debug模式返回假数据
if WORK_ORDER_DB_CONFIG.get('debug_mode', False):
logger.info(f"[DEBUG模式] 返回工单号 {work_order_no} 的假数据")
return {
'work_order_no': work_order_no,
'process_no': 'DEBUG-001',
2026-01-16 14:47:53 +08:00
'process_name': WORK_ORDER_DB_CONFIG.get('target_process_name', '泵空跑合'),
2025-12-11 14:32:31 +08:00
'part_no': 'PART-DEBUG-12345',
'executor': '测试人员'
}
conn = None
cursor = None
try:
import pyodbc
# 尝试不同的ODBC驱动
drivers = [
"ODBC Driver 17 for SQL Server",
"ODBC Driver 18 for SQL Server",
"ODBC Driver 13 for SQL Server",
"ODBC Driver 11 for SQL Server",
"SQL Server Native Client 11.0",
"SQL Server Native Client 10.0",
"SQL Server"
]
# 查找可用的驱动
available_drivers = pyodbc.drivers()
driver_to_use = None
for driver in drivers:
if driver in available_drivers:
driver_to_use = driver
logger.info(f"使用ODBC驱动: {driver}")
break
if not driver_to_use:
logger.warning(f"未找到合适的SQL Server ODBC驱动可用驱动: {available_drivers}")
# 尝试使用第一个包含SQL的驱动
for available in available_drivers:
if 'SQL' in available.upper():
driver_to_use = available
logger.info(f"尝试使用驱动: {driver_to_use}")
break
if not driver_to_use:
raise Exception("未找到SQL Server ODBC驱动将尝试pymssql")
# 构建SQL Server连接字符串
conn_str = (
f"DRIVER={{{driver_to_use}}};"
f"SERVER={WORK_ORDER_DB_CONFIG['host']},{WORK_ORDER_DB_CONFIG['port']};"
f"DATABASE={WORK_ORDER_DB_CONFIG['database']};"
f"UID={WORK_ORDER_DB_CONFIG['username']};"
f"PWD={WORK_ORDER_DB_CONFIG['password']}"
)
logger.info(f"连接到SQL Server: {WORK_ORDER_DB_CONFIG['host']}:{WORK_ORDER_DB_CONFIG['port']}/{WORK_ORDER_DB_CONFIG['database']}")
# 连接数据库
conn = pyodbc.connect(conn_str)
cursor = conn.cursor()
2026-01-16 14:47:53 +08:00
# 获取目标工序名称并去除前后空格
target_process_name = WORK_ORDER_DB_CONFIG.get('target_process_name', '泵空跑合').strip()
2025-12-11 14:32:31 +08:00
# 构建SQL查询语句SQL Server使用?作为参数占位符)
2026-01-16 14:47:53 +08:00
# 使用LIKE模糊匹配更容错
2025-12-11 14:32:31 +08:00
sql_query = """
SELECT
mte.CSYMBOL,
2026-01-16 14:47:53 +08:00
mte.CNEXTPROCNAME,
2025-12-11 14:32:31 +08:00
mpe.CMATERIALSYMBOL,
mte.CEXCUTOR
FROM MES_TASK_EXTEND mte
INNER JOIN MBP_PROJECT_EXTEND mpe ON mte.CPGDID = mpe.CID
2026-01-16 14:47:53 +08:00
WHERE mte.CSYMBOL LIKE ?
AND RTRIM(LTRIM(ISNULL(mte.CNEXTPROCNAME, ''))) LIKE ?
2025-12-11 14:32:31 +08:00
"""
# 添加通配符进行模糊匹配
2026-01-16 14:47:53 +08:00
work_order_pattern = f"{work_order_no}%"
process_name_pattern = f"%{target_process_name}%"
logger.debug(f"执行查询: {sql_query} with parameters: '{work_order_pattern}', '{process_name_pattern}'")
cursor.execute(sql_query, work_order_pattern, process_name_pattern)
2025-12-11 14:32:31 +08:00
row = cursor.fetchone()
if not row:
2026-01-16 14:47:53 +08:00
logger.warning(f"未找到工单号 '{work_order_no}' 且工序名称为 '{target_process_name}' 的相关信息")
2025-12-11 14:32:31 +08:00
return None
# 提取查询结果 - pyodbc返回Row对象可以通过索引或属性访问
work_order_info = {
'work_order_no': work_order_no,
'process_no': row.CSYMBOL if row.CSYMBOL else '', # 工序号
2026-01-16 14:47:53 +08:00
'process_name': row.CNEXTPROCNAME if row.CNEXTPROCNAME else '', # 工序名称
2025-12-11 14:32:31 +08:00
'part_no': row.CMATERIALSYMBOL if row.CMATERIALSYMBOL else '', # 零件号
'executor': row.CEXCUTOR if row.CEXCUTOR else '' # 执行人
}
logger.info(f"工单查询成功: {work_order_info}")
return work_order_info
except ImportError:
logger.error("pyodbc未安装请运行: pip install pyodbc")
# 尝试使用pymssql作为备选
try:
import pymssql
logger.info("尝试使用pymssql连接SQL Server")
conn = pymssql.connect(
server=WORK_ORDER_DB_CONFIG['host'],
port=WORK_ORDER_DB_CONFIG['port'],
user=WORK_ORDER_DB_CONFIG['username'],
password=WORK_ORDER_DB_CONFIG['password'],
database=WORK_ORDER_DB_CONFIG['database']
)
cursor = conn.cursor(as_dict=True)
2026-01-16 14:47:53 +08:00
# 获取目标工序名称并去除前后空格
target_process_name = WORK_ORDER_DB_CONFIG.get('target_process_name', '泵空跑合').strip()
# 使用LIKE模糊匹配更容错
2025-12-11 14:32:31 +08:00
sql_query = """
SELECT
mte.CSYMBOL,
2026-01-16 14:47:53 +08:00
mte.CNEXTPROCNAME,
2025-12-11 14:32:31 +08:00
mpe.CMATERIALSYMBOL,
mte.CEXCUTOR
FROM MES_TASK_EXTEND mte
INNER JOIN MBP_PROJECT_EXTEND mpe ON mte.CPGDID = mpe.CID
2026-01-16 14:47:53 +08:00
WHERE mte.CSYMBOL LIKE %s
AND RTRIM(LTRIM(ISNULL(mte.CNEXTPROCNAME, ''))) LIKE %s
2025-12-11 14:32:31 +08:00
"""
2026-01-16 14:47:53 +08:00
# 添加通配符进行模糊匹配
work_order_pattern = f"{work_order_no}%"
process_name_pattern = f"%{target_process_name}%"
logger.debug(f"执行查询(pymssql): {sql_query} with parameters: '{work_order_pattern}', '{process_name_pattern}'")
cursor.execute(sql_query, (work_order_pattern, process_name_pattern))
2025-12-11 14:32:31 +08:00
result = cursor.fetchone()
if not result:
2026-01-16 14:47:53 +08:00
logger.warning(f"未找到工单号 '{work_order_no}' 且工序名称为 '{target_process_name}' 的相关信息")
2025-12-11 14:32:31 +08:00
return None
work_order_info = {
'work_order_no': work_order_no,
'process_no': result.get('CSYMBOL', ''),
2026-01-16 14:47:53 +08:00
'process_name': result.get('CNEXTPROCNAME', ''),
2025-12-11 14:32:31 +08:00
'part_no': result.get('CMATERIALSYMBOL', ''),
'executor': result.get('CEXCUTOR', '')
}
logger.info(f"工单查询成功(pymssql): {work_order_info}")
return work_order_info
except ImportError:
error_msg = "SQL Server驱动未安装请安装以下任一库\n1. pip install pyodbc\n2. pip install pymssql"
logger.error(error_msg)
raise Exception(error_msg)
except Exception as e:
logger.error(f"pymssql查询错误: {e}")
raise Exception(f"数据库查询错误: {e}")
except Exception as e:
logger.error(f"SQL Server查询错误: {e}")
raise Exception(f"数据库查询错误: {e}")
finally:
if cursor:
cursor.close()
if conn:
conn.close()
logger.debug("数据库连接已关闭")
def update_work_order_db_config(host: str = None, port: int = None,
database: str = None, username: str = None,
password: str = None, save_to_file: bool = True):
"""
更新工单数据库连接配置
Args:
host: 数据库服务器地址
port: 数据库端口
database: 数据库名
username: 用户名
password: 密码
save_to_file: 是否保存到配置文件
"""
global WORK_ORDER_DB_CONFIG
if host is not None:
WORK_ORDER_DB_CONFIG['host'] = host
if port is not None:
WORK_ORDER_DB_CONFIG['port'] = port
if database is not None:
WORK_ORDER_DB_CONFIG['database'] = database
if username is not None:
WORK_ORDER_DB_CONFIG['username'] = username
if password is not None:
WORK_ORDER_DB_CONFIG['password'] = password
logger.info(f"工单数据库配置已更新: host={WORK_ORDER_DB_CONFIG['host']}, "
f"port={WORK_ORDER_DB_CONFIG['port']}, database={WORK_ORDER_DB_CONFIG['database']}")
# 保存到配置文件
if save_to_file:
try:
config_to_save = WORK_ORDER_DB_CONFIG.copy()
config_to_save['description'] = '工单数据库连接配置'
config_to_save['comment'] = '请根据实际环境修改以上配置'
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
json.dump(config_to_save, f, ensure_ascii=False, indent=2)
logger.info(f"配置已保存到文件: {CONFIG_FILE}")
except Exception as e:
logger.error(f"保存配置文件失败: {e}")