PCM_Report/sqlserver_writer.py

549 lines
24 KiB
Python
Raw Normal View History

2025-12-11 14:32:31 +08:00
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SQL Server 数据写入模块
将脚本返回的数据写入到 SQL Server 数据库
"""
import pyodbc
from typing import Dict, Any, Optional
from datetime import datetime
from logger import get_logger
logger = get_logger()
class SQLServerWriter:
"""SQL Server 数据写入器"""
def __init__(self, connection_config: Dict[str, Any]):
"""
初始化 SQL Server 连接
Args:
connection_config: 连接配置
- host: 服务器地址
- port: 端口号默认 1433
- database: 数据库名
- username: 用户名
- password: 密码
"""
self.config = connection_config
self.connection = None
def connect(self) -> bool:
"""
建立数据库连接
Returns:
连接是否成功
"""
try:
host = self.config.get('host', 'localhost')
port = self.config.get('port', 1433)
database = self.config.get('database', '')
username = self.config.get('username', '')
password = self.config.get('password', '')
if not database:
logger.error("SQL Server 数据库名未配置")
return False
# 尝试多个驱动
driver_candidates = [
"ODBC Driver 18 for SQL Server",
"ODBC Driver 17 for SQL Server",
"ODBC Driver 13 for SQL Server",
"SQL Server",
]
last_error = None
for driver in driver_candidates:
conn_str = (
f"DRIVER={{{driver}}};"
f"SERVER={host},{port};"
f"DATABASE={database};"
f"UID={username};"
f"PWD={password};"
f"TrustServerCertificate=yes"
)
try:
self.connection = pyodbc.connect(conn_str, timeout=10)
logger.info(f"SQL Server 连接成功 (驱动: {driver})")
return True
except Exception as e:
last_error = e
continue
logger.error(f"SQL Server 连接失败: {last_error}")
return False
except Exception as e:
logger.error(f"SQL Server 连接异常: {e}", exc_info=True)
return False
def disconnect(self):
"""关闭数据库连接"""
if self.connection:
try:
self.connection.close()
logger.info("SQL Server 连接已关闭")
except Exception as e:
logger.error(f"关闭 SQL Server 连接失败: {e}")
def write_pump_600_data(self, data: Dict[str, Any]) -> bool:
"""
写入 600 泵跑合数据
Args:
data: 脚本返回的数据字典包含
- order_no: 工单号必填
- runin_date: 跑合日期
- operator_name: 操作员
- power_end_part_no: 动力端零件号
- motor_speed_rpm: 电机转速
- ambient_temp_c: 环境温度
- start_time: 开始时间
- end_time: 结束时间
- reviewer_name: 审核人
- is_normal: 是否正常1=正常0=异常
- abnormal_desc: 异常描述
- remarks: 备注
- temp_main_1_t05 ~ temp_main_1_t35: 主轴承1温度数据
- temp_main_2_t05 ~ temp_main_2_t35: 主轴承2温度数据
- temp_main_3_t05 ~ temp_main_3_t35: 主轴承3温度数据
- temp_main_4_t05 ~ temp_main_4_t35: 主轴承4温度数据
- temp_crosshead_1_t05 ~ temp_crosshead_1_t35: 十字头1温度数据
- temp_crosshead_2_t05 ~ temp_crosshead_2_t35: 十字头2温度数据
- temp_crosshead_3_t05 ~ temp_crosshead_3_t35: 十字头3温度数据
- temp_gbox_small_1_t05 ~ temp_gbox_small_1_t35: 小齿轮箱1温度数据
- temp_gbox_small_2_t05 ~ temp_gbox_small_2_t35: 小齿轮箱2温度数据
- temp_gbox_big_3_t05 ~ temp_gbox_big_3_t35: 大齿轮箱3温度数据
- temp_gbox_big_4_t05 ~ temp_gbox_big_4_t35: 大齿轮箱4温度数据
Returns:
写入是否成功
"""
if not self.connection:
logger.error("SQL Server 未连接")
return False
try:
# 验证必填字段
order_no = data.get('order_no')
if not order_no:
logger.error("工单号order_no为必填字段")
return False
start_time = data.get('start_time')
end_time = data.get('end_time')
# 检查记录是否已存在(基于 order_no, start_time, end_time 组合)
cursor = self.connection.cursor()
# 构建查询条件
if start_time and end_time:
# 如果有开始和结束时间,使用三个字段组合查询
cursor.execute(
"""SELECT id FROM pump_600_no_load_run_in
WHERE order_no = ? AND start_time = ? AND end_time = ?""",
(order_no, start_time, end_time)
)
elif start_time:
# 只有开始时间
cursor.execute(
"""SELECT id FROM pump_600_no_load_run_in
WHERE order_no = ? AND start_time = ?""",
(order_no, start_time)
)
else:
# 只有工单号(向后兼容)
cursor.execute(
"SELECT id FROM pump_600_no_load_run_in WHERE order_no = ?",
(order_no,)
)
existing = cursor.fetchone()
if existing:
existing_id = existing[0]
logger.info(
f"找到已存在的记录 (id={existing_id}, order_no={order_no}, "
f"start_time={start_time}, end_time={end_time}),将更新数据"
)
return self._update_pump_600_data_by_id(existing_id, data)
else:
logger.info(
f"未找到匹配记录 (order_no={order_no}, start_time={start_time}, "
f"end_time={end_time}),将插入新数据"
)
return self._insert_pump_600_data(data)
except Exception as e:
logger.error(f"写入 SQL Server 数据失败: {e}", exc_info=True)
return False
def _insert_pump_600_data(self, data: Dict[str, Any]) -> bool:
"""插入新记录"""
try:
cursor = self.connection.cursor()
# 构建 SQL 插入语句
sql = """
INSERT INTO pump_600_no_load_run_in (
order_no, runin_date, operator_name, power_end_part_no,
motor_speed_rpm, ambient_temp_c, start_time, end_time,
reviewer_name, is_normal, abnormal_desc, remarks,
temp_main_1_t05, temp_main_1_t10, temp_main_1_t15, temp_main_1_t20,
temp_main_1_t25, temp_main_1_t30, temp_main_1_t35,
temp_main_2_t05, temp_main_2_t10, temp_main_2_t15, temp_main_2_t20,
temp_main_2_t25, temp_main_2_t30, temp_main_2_t35,
temp_main_3_t05, temp_main_3_t10, temp_main_3_t15, temp_main_3_t20,
temp_main_3_t25, temp_main_3_t30, temp_main_3_t35,
temp_main_4_t05, temp_main_4_t10, temp_main_4_t15, temp_main_4_t20,
temp_main_4_t25, temp_main_4_t30, temp_main_4_t35,
temp_crosshead_1_t05, temp_crosshead_1_t10, temp_crosshead_1_t15, temp_crosshead_1_t20,
temp_crosshead_1_t25, temp_crosshead_1_t30, temp_crosshead_1_t35,
temp_crosshead_2_t05, temp_crosshead_2_t10, temp_crosshead_2_t15, temp_crosshead_2_t20,
temp_crosshead_2_t25, temp_crosshead_2_t30, temp_crosshead_2_t35,
temp_crosshead_3_t05, temp_crosshead_3_t10, temp_crosshead_3_t15, temp_crosshead_3_t20,
temp_crosshead_3_t25, temp_crosshead_3_t30, temp_crosshead_3_t35,
temp_gbox_small_1_t05, temp_gbox_small_1_t10, temp_gbox_small_1_t15, temp_gbox_small_1_t20,
temp_gbox_small_1_t25, temp_gbox_small_1_t30, temp_gbox_small_1_t35,
temp_gbox_small_2_t05, temp_gbox_small_2_t10, temp_gbox_small_2_t15, temp_gbox_small_2_t20,
temp_gbox_small_2_t25, temp_gbox_small_2_t30, temp_gbox_small_2_t35,
temp_gbox_big_3_t05, temp_gbox_big_3_t10, temp_gbox_big_3_t15, temp_gbox_big_3_t20,
temp_gbox_big_3_t25, temp_gbox_big_3_t30, temp_gbox_big_3_t35,
temp_gbox_big_4_t05, temp_gbox_big_4_t10, temp_gbox_big_4_t15, temp_gbox_big_4_t20,
temp_gbox_big_4_t25, temp_gbox_big_4_t30, temp_gbox_big_4_t35,
created_at, updated_at
) VALUES (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?, ?, ?, ?, ?, ?,
?, ?
)
"""
# 准备参数
now = datetime.now()
params = [
data.get('order_no'),
data.get('runin_date'),
data.get('operator_name'),
data.get('power_end_part_no'),
data.get('motor_speed_rpm'),
data.get('ambient_temp_c'),
data.get('start_time'),
data.get('end_time'),
data.get('reviewer_name'),
data.get('is_normal', 1), # 默认正常
data.get('abnormal_desc'),
data.get('remarks'),
]
# 添加所有温度字段
temp_fields = [
'temp_main_1', 'temp_main_2', 'temp_main_3', 'temp_main_4',
'temp_crosshead_1', 'temp_crosshead_2', 'temp_crosshead_3',
'temp_gbox_small_1', 'temp_gbox_small_2',
'temp_gbox_big_3', 'temp_gbox_big_4'
]
time_points = ['t05', 't10', 't15', 't20', 't25', 't30', 't35']
for field in temp_fields:
for time_point in time_points:
key = f"{field}_{time_point}"
params.append(data.get(key))
# 添加时间戳
params.extend([now, now])
# 执行插入
cursor.execute(sql, params)
self.connection.commit()
logger.info(f"✅ 成功插入工单 {data.get('order_no')} 的数据到 SQL Server")
return True
except Exception as e:
logger.error(f"插入 SQL Server 数据失败: {e}", exc_info=True)
try:
self.connection.rollback()
except:
pass
return False
def _update_pump_600_data_by_id(self, record_id: int, data: Dict[str, Any]) -> bool:
"""根据记录ID更新已存在的记录"""
try:
cursor = self.connection.cursor()
# 构建 SQL 更新语句
sql = """
UPDATE pump_600_no_load_run_in SET
order_no = ?, runin_date = ?, operator_name = ?, power_end_part_no = ?,
motor_speed_rpm = ?, ambient_temp_c = ?, start_time = ?, end_time = ?,
reviewer_name = ?, is_normal = ?, abnormal_desc = ?, remarks = ?,
temp_main_1_t05 = ?, temp_main_1_t10 = ?, temp_main_1_t15 = ?, temp_main_1_t20 = ?,
temp_main_1_t25 = ?, temp_main_1_t30 = ?, temp_main_1_t35 = ?,
temp_main_2_t05 = ?, temp_main_2_t10 = ?, temp_main_2_t15 = ?, temp_main_2_t20 = ?,
temp_main_2_t25 = ?, temp_main_2_t30 = ?, temp_main_2_t35 = ?,
temp_main_3_t05 = ?, temp_main_3_t10 = ?, temp_main_3_t15 = ?, temp_main_3_t20 = ?,
temp_main_3_t25 = ?, temp_main_3_t30 = ?, temp_main_3_t35 = ?,
temp_main_4_t05 = ?, temp_main_4_t10 = ?, temp_main_4_t15 = ?, temp_main_4_t20 = ?,
temp_main_4_t25 = ?, temp_main_4_t30 = ?, temp_main_4_t35 = ?,
temp_crosshead_1_t05 = ?, temp_crosshead_1_t10 = ?, temp_crosshead_1_t15 = ?, temp_crosshead_1_t20 = ?,
temp_crosshead_1_t25 = ?, temp_crosshead_1_t30 = ?, temp_crosshead_1_t35 = ?,
temp_crosshead_2_t05 = ?, temp_crosshead_2_t10 = ?, temp_crosshead_2_t15 = ?, temp_crosshead_2_t20 = ?,
temp_crosshead_2_t25 = ?, temp_crosshead_2_t30 = ?, temp_crosshead_2_t35 = ?,
temp_crosshead_3_t05 = ?, temp_crosshead_3_t10 = ?, temp_crosshead_3_t15 = ?, temp_crosshead_3_t20 = ?,
temp_crosshead_3_t25 = ?, temp_crosshead_3_t30 = ?, temp_crosshead_3_t35 = ?,
temp_gbox_small_1_t05 = ?, temp_gbox_small_1_t10 = ?, temp_gbox_small_1_t15 = ?, temp_gbox_small_1_t20 = ?,
temp_gbox_small_1_t25 = ?, temp_gbox_small_1_t30 = ?, temp_gbox_small_1_t35 = ?,
temp_gbox_small_2_t05 = ?, temp_gbox_small_2_t10 = ?, temp_gbox_small_2_t15 = ?, temp_gbox_small_2_t20 = ?,
temp_gbox_small_2_t25 = ?, temp_gbox_small_2_t30 = ?, temp_gbox_small_2_t35 = ?,
temp_gbox_big_3_t05 = ?, temp_gbox_big_3_t10 = ?, temp_gbox_big_3_t15 = ?, temp_gbox_big_3_t20 = ?,
temp_gbox_big_3_t25 = ?, temp_gbox_big_3_t30 = ?, temp_gbox_big_3_t35 = ?,
temp_gbox_big_4_t05 = ?, temp_gbox_big_4_t10 = ?, temp_gbox_big_4_t15 = ?, temp_gbox_big_4_t20 = ?,
temp_gbox_big_4_t25 = ?, temp_gbox_big_4_t30 = ?, temp_gbox_big_4_t35 = ?,
updated_at = ?
WHERE id = ?
"""
# 准备参数
now = datetime.now()
params = [
data.get('order_no'),
data.get('runin_date'),
data.get('operator_name'),
data.get('power_end_part_no'),
data.get('motor_speed_rpm'),
data.get('ambient_temp_c'),
data.get('start_time'),
data.get('end_time'),
data.get('reviewer_name'),
data.get('is_normal', 1),
data.get('abnormal_desc'),
data.get('remarks'),
]
# 添加所有温度字段
temp_fields = [
'temp_main_1', 'temp_main_2', 'temp_main_3', 'temp_main_4',
'temp_crosshead_1', 'temp_crosshead_2', 'temp_crosshead_3',
'temp_gbox_small_1', 'temp_gbox_small_2',
'temp_gbox_big_3', 'temp_gbox_big_4'
]
time_points = ['t05', 't10', 't15', 't20', 't25', 't30', 't35']
for field in temp_fields:
for time_point in time_points:
key = f"{field}_{time_point}"
params.append(data.get(key))
# 添加更新时间和记录ID
params.extend([now, record_id])
# 执行更新
cursor.execute(sql, params)
self.connection.commit()
logger.info(f"✅ 成功更新记录 (id={record_id}, order_no={data.get('order_no')}) 到 SQL Server")
return True
except Exception as e:
logger.error(f"更新 SQL Server 数据失败: {e}", exc_info=True)
try:
self.connection.rollback()
except:
pass
return False
def _update_pump_600_data(self, order_no: str, data: Dict[str, Any]) -> bool:
"""更新已存在的记录(向后兼容,已废弃)"""
try:
cursor = self.connection.cursor()
# 构建 SQL 更新语句
sql = """
UPDATE pump_600_no_load_run_in SET
runin_date = ?, operator_name = ?, power_end_part_no = ?,
motor_speed_rpm = ?, ambient_temp_c = ?, start_time = ?, end_time = ?,
reviewer_name = ?, is_normal = ?, abnormal_desc = ?, remarks = ?,
temp_main_1_t05 = ?, temp_main_1_t10 = ?, temp_main_1_t15 = ?, temp_main_1_t20 = ?,
temp_main_1_t25 = ?, temp_main_1_t30 = ?, temp_main_1_t35 = ?,
temp_main_2_t05 = ?, temp_main_2_t10 = ?, temp_main_2_t15 = ?, temp_main_2_t20 = ?,
temp_main_2_t25 = ?, temp_main_2_t30 = ?, temp_main_2_t35 = ?,
temp_main_3_t05 = ?, temp_main_3_t10 = ?, temp_main_3_t15 = ?, temp_main_3_t20 = ?,
temp_main_3_t25 = ?, temp_main_3_t30 = ?, temp_main_3_t35 = ?,
temp_main_4_t05 = ?, temp_main_4_t10 = ?, temp_main_4_t15 = ?, temp_main_4_t20 = ?,
temp_main_4_t25 = ?, temp_main_4_t30 = ?, temp_main_4_t35 = ?,
temp_crosshead_1_t05 = ?, temp_crosshead_1_t10 = ?, temp_crosshead_1_t15 = ?, temp_crosshead_1_t20 = ?,
temp_crosshead_1_t25 = ?, temp_crosshead_1_t30 = ?, temp_crosshead_1_t35 = ?,
temp_crosshead_2_t05 = ?, temp_crosshead_2_t10 = ?, temp_crosshead_2_t15 = ?, temp_crosshead_2_t20 = ?,
temp_crosshead_2_t25 = ?, temp_crosshead_2_t30 = ?, temp_crosshead_2_t35 = ?,
temp_crosshead_3_t05 = ?, temp_crosshead_3_t10 = ?, temp_crosshead_3_t15 = ?, temp_crosshead_3_t20 = ?,
temp_crosshead_3_t25 = ?, temp_crosshead_3_t30 = ?, temp_crosshead_3_t35 = ?,
temp_gbox_small_1_t05 = ?, temp_gbox_small_1_t10 = ?, temp_gbox_small_1_t15 = ?, temp_gbox_small_1_t20 = ?,
temp_gbox_small_1_t25 = ?, temp_gbox_small_1_t30 = ?, temp_gbox_small_1_t35 = ?,
temp_gbox_small_2_t05 = ?, temp_gbox_small_2_t10 = ?, temp_gbox_small_2_t15 = ?, temp_gbox_small_2_t20 = ?,
temp_gbox_small_2_t25 = ?, temp_gbox_small_2_t30 = ?, temp_gbox_small_2_t35 = ?,
temp_gbox_big_3_t05 = ?, temp_gbox_big_3_t10 = ?, temp_gbox_big_3_t15 = ?, temp_gbox_big_3_t20 = ?,
temp_gbox_big_3_t25 = ?, temp_gbox_big_3_t30 = ?, temp_gbox_big_3_t35 = ?,
temp_gbox_big_4_t05 = ?, temp_gbox_big_4_t10 = ?, temp_gbox_big_4_t15 = ?, temp_gbox_big_4_t20 = ?,
temp_gbox_big_4_t25 = ?, temp_gbox_big_4_t30 = ?, temp_gbox_big_4_t35 = ?,
updated_at = ?
WHERE order_no = ?
"""
# 准备参数
now = datetime.now()
params = [
data.get('runin_date'),
data.get('operator_name'),
data.get('power_end_part_no'),
data.get('motor_speed_rpm'),
data.get('ambient_temp_c'),
data.get('start_time'),
data.get('end_time'),
data.get('reviewer_name'),
data.get('is_normal', 1),
data.get('abnormal_desc'),
data.get('remarks'),
]
# 添加所有温度字段
temp_fields = [
'temp_main_1', 'temp_main_2', 'temp_main_3', 'temp_main_4',
'temp_crosshead_1', 'temp_crosshead_2', 'temp_crosshead_3',
'temp_gbox_small_1', 'temp_gbox_small_2',
'temp_gbox_big_3', 'temp_gbox_big_4'
]
time_points = ['t05', 't10', 't15', 't20', 't25', 't30', 't35']
for field in temp_fields:
for time_point in time_points:
key = f"{field}_{time_point}"
params.append(data.get(key))
# 添加更新时间和工单号
params.extend([now, order_no])
# 执行更新
cursor.execute(sql, params)
self.connection.commit()
logger.info(f"✅ 成功更新工单 {order_no} 的数据到 SQL Server")
return True
except Exception as e:
logger.error(f"更新 SQL Server 数据失败: {e}", exc_info=True)
try:
self.connection.rollback()
except:
pass
return False
def write_script_data_to_sqlserver(script_data: Dict[str, Any], sqlserver_config: Dict[str, Any]) -> bool:
"""
将脚本数据写入 SQL Server
Args:
script_data: 脚本返回的数据
sqlserver_config: SQL Server 连接配置
Returns:
写入是否成功
"""
writer = SQLServerWriter(sqlserver_config)
try:
# 连接数据库
if not writer.connect():
logger.error("无法连接到 SQL Server")
return False
# 写入数据
success = writer.write_pump_600_data(script_data)
return success
except Exception as e:
logger.error(f"写入 SQL Server 失败: {e}", exc_info=True)
return False
finally:
writer.disconnect()
2026-01-16 14:47:53 +08:00
def verify_data_in_sqlserver(order_no: str, start_time: str, end_time: str, sqlserver_config: Dict[str, Any]) -> bool:
"""
验证数据是否已写入 SQL Server
Args:
order_no: 工单号
start_time: 开始时间
end_time: 结束时间
sqlserver_config: SQL Server 连接配置
Returns:
数据是否存在
"""
writer = SQLServerWriter(sqlserver_config)
try:
# 连接数据库
if not writer.connect():
logger.error("无法连接到 SQL Server 进行验证")
return False
cursor = writer.connection.cursor()
# 查询数据是否存在
if start_time and end_time:
cursor.execute(
"""SELECT COUNT(*) FROM pump_600_no_load_run_in
WHERE order_no = ? AND start_time = ? AND end_time = ?""",
(order_no, start_time, end_time)
)
elif start_time:
cursor.execute(
"""SELECT COUNT(*) FROM pump_600_no_load_run_in
WHERE order_no = ? AND start_time = ?""",
(order_no, start_time)
)
else:
cursor.execute(
"SELECT COUNT(*) FROM pump_600_no_load_run_in WHERE order_no = ?",
(order_no,)
)
count = cursor.fetchone()[0]
exists = count > 0
if exists:
logger.info(f"✅ 验证成功:数据已存在于 SQL Server (order_no={order_no})")
else:
logger.warning(f"⚠ 验证失败:数据不存在于 SQL Server (order_no={order_no})")
return exists
except Exception as e:
logger.error(f"验证 SQL Server 数据失败: {e}", exc_info=True)
return False
finally:
writer.disconnect()