health.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. from datetime import datetime
  2. from typing import List
  3. from fastapi import HTTPException, APIRouter
  4. from app.logger import logger
  5. from app.models.HealthEvaluationReqAndResp import AssessmentResult, AssessmentRequest, SubsystemResult
  6. from app.services.HealthAssessor import HealthAssessor
  7. from app.services.HealthDataFetcher import DataFetcher
  8. router = APIRouter()
  9. @router.post("/health_assess", response_model=List[AssessmentResult])
  10. async def assess_windfarm(request: AssessmentRequest):
  11. try:
  12. datetime.strptime(request.month, "%Y-%m")
  13. except ValueError:
  14. raise HTTPException(status_code=400, detail="无效时间格式,使用YYYY-MM格式")
  15. logger.info(f"开始处理风场评估请求 - 风场编码: {request.windcode}, 月份: {request.month}")
  16. fetcher = DataFetcher()
  17. assessor = HealthAssessor()
  18. turbines = fetcher.get_turbines(request.windcode)
  19. print(turbines)
  20. if turbines.empty:
  21. raise HTTPException(status_code=404, detail="未找到风场数据")
  22. results: List[AssessmentResult] = [] # 显式声明列表类型并初始化
  23. all_turbines = len(turbines)
  24. processed_count = 0
  25. for idx, row in turbines.iterrows():
  26. try:
  27. engine_code = row['engine_code']
  28. mill_type_code = row['mill_type_code']
  29. wind_turbine_name = row['engine_name']
  30. # 获取机型类型
  31. mill_type_num = fetcher.get_mill_type(mill_type_code)
  32. mill_type = {1: 'dfig', 2: 'direct', 3: 'semi_direct'}.get(mill_type_num, 'unknown')
  33. if mill_type == 'unknown':
  34. logger.warning(f"{engine_code}机型未知,跳过处理")
  35. continue
  36. # 获取特征与数据
  37. # 先获取该风场所有可用列名
  38. available_columns = fetcher.get_turbine_columns(request.windcode)
  39. if not available_columns:
  40. continue
  41. # 获取有效特征列(基于实际列名)
  42. valid_features = assessor._get_all_possible_features(assessor, mill_type, available_columns)
  43. # 获取数据
  44. data = fetcher.fetch_turbine_data(request.windcode, engine_code, request.month, valid_features)
  45. print(data)
  46. if data is None or data.empty: # 增加对None的检查
  47. logger.warning(f"{engine_code}在{request.month}无有效数据")
  48. # 创建空评估结果,所有评分为-1
  49. empty_assessment = {
  50. 'engine_code': engine_code,
  51. 'wind_turbine_name': wind_turbine_name,
  52. 'mill_type': mill_type,
  53. 'total_health_score': -1,
  54. 'subsystems': {
  55. 'generator': {
  56. 'health_score': -1,
  57. 'weights': {},
  58. 'message': None
  59. },
  60. 'nacelle': {
  61. 'health_score': -1,
  62. 'weights': {},
  63. 'message': None
  64. },
  65. 'grid': {
  66. 'health_score': -1,
  67. 'weights': {},
  68. 'message': None
  69. },
  70. 'drive_train': {
  71. 'health_score': -1,
  72. 'weights': {},
  73. 'message': None
  74. }
  75. },
  76. 'assessed_subsystems': ['generator', 'nacelle', 'grid', 'drive_train']
  77. }
  78. formatted_result = _format_result(empty_assessment)
  79. results.append(formatted_result)
  80. processed_count += 1
  81. logger.info(f"处理无数据风机{engine_code}(已处理{processed_count}/{all_turbines}台)")
  82. continue
  83. # 执行评估并格式化结果
  84. assessment = assessor.assess_turbine(engine_code, data, mill_type, wind_turbine_name)
  85. if not assessment: # 检查评估结果有效性
  86. logger.warning(f"{engine_code}评估结果为空")
  87. continue
  88. formatted_result = _format_result(assessment)
  89. if isinstance(formatted_result, AssessmentResult): # 严格类型检查
  90. results.append(formatted_result)
  91. processed_count += 1
  92. logger.info(f"成功处理{engine_code}(已处理{processed_count}/{all_turbines}台)")
  93. else:
  94. logger.error(f"{engine_code}结果格式化失败,类型错误: {type(formatted_result)}")
  95. except Exception as e:
  96. logger.error(f"处理{engine_code}时发生异常: {str(e)}", exc_info=True)
  97. continue
  98. # 最终返回处理 - 强制转换为列表并确保类型正确
  99. if not results:
  100. logger.warning(f"风场{request.windcode}在{request.month}无有效评估结果,返回空列表")
  101. return []
  102. # 防御性类型检查(生产环境可移除)
  103. if not isinstance(results, list):
  104. logger.error(f"结果类型错误,期望list但得到{type(results)},强制转换为列表")
  105. results = [results] if results is not None else []
  106. return results
  107. def _format_result(assessment):
  108. """格式化评估结果"""
  109. return AssessmentResult(
  110. engine_code=assessment['engine_code'],
  111. wind_turbine_name=assessment['wind_turbine_name'],
  112. mill_type=assessment['mill_type'],
  113. total_health_score=assessment.get('total_health_score'),
  114. subsystems={
  115. k: SubsystemResult(**v)
  116. for k, v in assessment['subsystems'].items()
  117. },
  118. assessed_subsystems=assessment.get('assessed_subsystems', [])
  119. )