Files
2026-07-10 18:55:55 +08:00

333 lines
13 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime, timedelta, timezone
from queue import Queue
import threading
from flask import Blueprint, json, jsonify, current_app, request
from app.blueprints.incident_gen6.service import aggregate_and_sort_incidents_gen6, aggregate_line_list_gen6, convert_to_histro_data_v2, get_logging_list_gen6, process_incident_data_gen6
from app.link_wedata_utils import sync_query_tencent_cloud_data
from app.utils import decrypt_code
incident_gen6_bp = Blueprint("incident_gen6_bp", __name__)
@incident_gen6_bp.route("/list1", methods=["POST"])
def acc_list1():
encrypted = request.headers.get('X-Encrypted-Timestamp')
if not encrypted:
return jsonify({"msg": "Parameters are Incorrect"}), 400
encrypt_str=decrypt_code(encrypted)
if not encrypt_str:
return jsonify({"msg": "Invalid request"}), 400
data = request.get_json()
incident_type = data.get("incident_type", "").strip() or None
singal_name = data.get("singal_name", []) or None
# print("singal_name",singal_name)
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
json_path = os.path.join(current_dir, "incident_sample.json")
with open(json_path, "r", encoding="utf-8") as f:
events_data = json.load(f)
histo_path = os.path.join(current_dir, "histro.json")
with open(histo_path, "r", encoding="utf-8") as f_histro:
histo_data = json.load(f_histro)
histo_data_new = []
for item in histo_data:
new_item = {
'signal': item['signal'],
'time': item['time'],
'value': item['value'],
'valueExplanation': item['valueExplanation'],
'pre_value':item['pre_value'],
'pre_valueExplanation':item['pre_valueExplanation'],
'is_pre_value_needed':item['is_pre_value_needed']
}
histo_data_new.append(new_item)
histo_list=convert_to_histro_data_v2(histo_data_new)
events_data_new=[]
for item in events_data:
new_item = {
'oneid': item['oneid'],
'incident_name': item['incident_name'],
'incident_time': item['incident_time'],
'key': item['key'],
'signal_value': item['signal_value'],
'latitude': item['latitude'],
'longitude': item['longitude'],
'odometer': item['odometer'],
'speed': item['speed']
}
events_data_new.append(new_item)
if incident_type == "Parking":
# 过滤出 incident_name 等于 Test_I_Park_Trip 的数据
events_data_new = [
item for item in events_data_new
if item.get("incident_name") in ["Test_I_Park_Trip", "Test_I_RMA"]
]
elif incident_type == "Driving":
# 过滤出 incident_name 不等于 Test_I_Park_Trip 的数据
events_data_new = [
item for item in events_data_new
if item.get("incident_name") not in ["Test_I_Park_Trip", "Test_I_RMA"]
]
# event数据
events_list=process_incident_data_gen6(events_data_new) # 初始的event list数据,但是没有15分钟处理
map_list = aggregate_and_sort_incidents_gen6(events_data_new)
# print(f"共 {len(map_list)} 条:")
line_list = aggregate_line_list_gen6(map_list)
logging_list=get_logging_list_gen6(map_list)
response = {
"code": 200,
"data": {
# "events_data":events_data,
"events_list": events_list,
"map_list": map_list,
"line_list": line_list,
"logging_list": logging_list,
"histo_list":histo_list
},
"msg": "success",
}
return jsonify(response)
@incident_gen6_bp.route("/list", methods=["POST"])
def acc_list():
# 验证请求头的信息,通过才继续
encrypted = request.headers.get('X-Encrypted-Timestamp')
if not encrypted:
return jsonify({"msg": "Parameters are Incorrect"}), 400
encrypt_str=decrypt_code(encrypted)
if not encrypt_str:
return jsonify({"msg": "Invalid request"}), 400
data = request.get_json()
incident_type = data.get("incident_type", "").strip() or None
signal_name = data.get("signal_name", [])
if not data:
return jsonify({"code": 400, "data": None, "msg": "请填写有效参数"}), 400
required_fields = ["incident_time", "oneid"]
for field in required_fields:
if field not in data:
return (
jsonify({"code": 400, "data": None, "msg": f"缺少必需字段: {field}"}),
400,
)
incident_time = data["incident_time"]
# 检查是否为非空列表
if not isinstance(incident_time, list) or len(incident_time) != 2:
return (
jsonify(
{
"code": 400,
"data": [],
"msg": "incident_time必须是包含两个时间的数组",
}
),
400,
)
oneid = data["oneid"]
if not isinstance(oneid, str) or not oneid.strip():
return jsonify({"code": 400, "data": None, "msg": "oneid必须是非空字符串"}), 400
config = {
'region': 'ap-shanghai', # 引擎所在地域
'secret_id': current_app.config['WEDATA_SECRET_ID'],
'secret_key': current_app.config['WEDATA_SECRET_KEY'],
'engine': current_app.config['WEDATA_ENGINE'], # 引擎名称
"database":current_app.config['WEDATA_DATABASE']
}
# 生成sql
try:
incid_sql, incid_params = generate_incidents_sql(incident_time, oneid, signal_name, table_name='gen6_incidents')
histo_sql, histo_params = generate_incidents_sql(incident_time, oneid, signal_name, table_name='gen6_histograms')
except ValueError as e:
return jsonify({"code": 400, "data": None, "msg": f"参数错误:{str(e)}"}), 400
print("incid_sql", incid_sql)
print("histo_sql", histo_sql)
result_queue = Queue(maxsize=2) # 最多存放2个结果
# ========== 仅修改这里:给原函数加极简异常防护(不新增任何函数) ==========
def safe_query(*args):
"""极简包装:仅捕获异常,保证队列必有结果"""
try:
# 直接调用原函数,参数完全传透(args就是config, sql, params, queue, task_id
sync_query_tencent_cloud_data(*args)
except Exception as e: # except Exception as e:
# 异常时手动向队列写错误结果(args[3]是queueargs[4]是task_id
args[3].put({"task_id": args[4], "success": False, "data": None, "error": str(e)})
# try:
# error_msg=(
# f'数据库连接失败:{str(e)}'
# if str(e)
# else f'底层系统错误:{type(e).__name__}'
# )
# args[3].put({"task_id": args[4], "success": False, "data": None, "error":error_msg})
# except Exception as query_err:
# print(f'任务失败,无法写入队列:{query_err},原始错误是:{e}')
# 5. 创建并启动两个查询线程
thread1 = threading.Thread(
target=safe_query,
args=(config, incid_sql, incid_params, result_queue, "incidents") # task_id 标记为 incidents
)
thread2 = threading.Thread(
target=safe_query,
args=(config, histo_sql, histo_params, result_queue, "histo") # task_id 标记为 signals
)
# 启动线程
thread1.start()
thread2.start()
thread1.join()
thread2.join()
# 从队列中提取结果
results_dict = {}
for _ in range(2):
task_result = result_queue.get()
results_dict[task_result["task_id"]] = task_result
# 8. 检查是否有查询失败
has_error = False
error_msg = ""
for task_id, res in results_dict.items():
if not res["success"]:
has_error = True
error_msg = f"查询异常:{res['error'][:80]}" # 错误信息保留前80
if has_error:
return jsonify({
"code": 500,
"msg": f"查询失败:{error_msg}",
"data": None
}), 500
# 9. 获取两个表的查询结果(业务处理核心)
events_data = results_dict["incidents"]["data"]
histo_data = results_dict["histo"]["data"]
histo_list=convert_to_histro_data_v2(histo_data)
if incident_type == "Parking":
# 过滤出 incident_name 等于 Test_I_Park_Trip 的数据
events_data = [
item for item in events_data
if item.get("incident_name") in ["Test_I_Park_Trip", "Test_I_RMA"]
]
elif incident_type == "Driving":
# 过滤出 incident_name 不等于 Test_I_Park_Trip 的数据
events_data = [
item for item in events_data
if item.get("incident_name") not in ["Test_I_Park_Trip", "Test_I_RMA"]
]
# event数据
events_list=process_incident_data_gen6(events_data) # 初始的event list数据,但是没有15分钟处理
map_list = aggregate_and_sort_incidents_gen6(events_data)
line_list = aggregate_line_list_gen6(map_list)
logging_list=get_logging_list_gen6(map_list)
response = {
"code": 200,
"data": {
# "events_data":events_data,
"events_list": events_list,
"map_list": [],
"line_list": line_list,
"logging_list": logging_list,
"histo_list":histo_list
},
"msg": "success",
}
return jsonify(response)
def generate_incidents_sql(incident_time: list, oneid: str, signal_list: list, table_name: str) -> tuple:
"""
简化版:仅处理用到的两个表,字段名映射极简,去掉冗余扩展
- gen6_incidents:时间=incident_timeID字段=oneid
- gen6_histograms:时间=timeID字段=vin
"""
# 处理UTC时间(带T/Z)转本地时间(东八区)
def convert_utc_to_cst(utc_time_str):
try:
# 解析UTC时间(支持带T/Z的格式)
utc_dt = datetime.fromisoformat(utc_time_str.replace('Z', '+00:00'))
# 转东八区(UTC+8
cst_tz = timezone(timedelta(hours=8))
cst_dt = utc_dt.astimezone(cst_tz)
# 转为数据库兼容的格式(YYYY-MM-DD HH:mm:ss.fff
return cst_dt.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] # 保留3位毫秒
except Exception as e:
# 解析失败则返回原时间(兼容测试参数)
return utc_time_str
# 转换时间范围
incident_time = [convert_utc_to_cst(t) for t in incident_time]
# 1. 极简字段映射(只保留用到的表,避免KeyError)
FIELD_MAP = {
"gen6_incidents": {"time": "incident_time",
"id": "oneid",
# "select_fields": "oneid, incident_name,incident_time,`key`,signal_value,latitude,longitude,odometer,speed" },
"select_fields": "*" },
"gen6_histograms": {"time": "time",
"id": "vin",
"select_fields": "signal, time, value, valueExplanation"},
}
# 2. 基础校验(只校验用到的表,去掉冗余)
if table_name not in FIELD_MAP:
raise ValueError(f"仅支持查询表:{list(FIELD_MAP.keys())}")
if len(incident_time) != 2 or not all(incident_time):
raise ValueError("incident_time必须是包含两个非空时间的列表")
if not isinstance(oneid, str) or not oneid.strip():
raise ValueError("oneid必须是非空字符串")
# 3. 提取当前表的字段名(核心:只用两行完成字段替换)
time_field = FIELD_MAP[table_name]["time"]
id_field = FIELD_MAP[table_name]["id"]
select_fields = FIELD_MAP[table_name]["select_fields"]
# 4. 拼接SQL条件(极简逻辑,去掉冗余注释)
conditions = [
f"{time_field} BETWEEN %s AND %s",
f"{id_field} = %s"
]
params = [incident_time[0], incident_time[1], oneid]
# 仅gen6_histograms处理signal条件
if table_name == "gen6_histograms":
if signal_list:
signal_placeholders = ",".join(["%s"] * len(signal_list))
conditions.append(f"signal IN ({signal_placeholders})")
params += signal_list
else:
conditions.append("signal = %s")
params.append("DAS_DTR_UI_Stat_ST3")
# 5. 生成最终SQL(简化格式化)
sql = f"SELECT {select_fields} FROM {table_name} WHERE {' AND '.join(conditions)}"
return sql, params