temperatureLargeComponentsAnalyst.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  1. import os
  2. import pandas as pd
  3. import plotly.express as px
  4. import plotly.graph_objects as go
  5. from algorithmContract.confBusiness import *
  6. from algorithmContract.contract import Contract
  7. from behavior.analystWithGoodBadLimitPoint import AnalystWithGoodBadLimitPoint
  8. from plotly.subplots import make_subplots
  9. from utils.directoryUtil import DirectoryUtil as dir
  10. class Generator:
  11. def __init__(self) -> None:
  12. self.fieldTemperatorOfDEBearing = None
  13. self.fieldTemperatorOfNDEBearing = None
  14. TemperatureColumns = {Field_MainBearTemp: "主轴承温度",
  15. Field_GbMsBearTemp: "齿轮箱中速轴温度",
  16. Field_GbLsBearTemp: "齿轮箱低速轴温度",
  17. Field_GbHsBearTemp: "齿轮箱高速轴温度",
  18. Field_GeneratorDE: "发电机驱动端轴承温度",
  19. Field_GeneratorNDE: "发电机非驱动端轴承温度",
  20. Field_GenWiTemp1: "发电机绕组温度"}
  21. GeneratorTemperatureAnslysisColumns = [
  22. Field_GeneratorDE, Field_GeneratorNDE, Field_NacTemp]
  23. class TemperatureLargeComponentsAnalyst(AnalystWithGoodBadLimitPoint):
  24. """
  25. 风电机组大部件温升分析
  26. """
  27. def typeAnalyst(self):
  28. return "temperature_large_components"
  29. def getUseColumns(self, dataFrame: pd.DataFrame, temperatureColumns: list[dict]):
  30. # 获取非全为空的列名
  31. non_empty_cols = self.getNoneEmptyFields(dataFrame, temperatureColumns)
  32. useCols = []
  33. # useCols.append(Field_Time)
  34. useCols.append(Field_ActiverPower)
  35. if not self.common.isNone(Field_EnvTemp) and Field_EnvTemp in dataFrame.columns:
  36. useCols.append(Field_EnvTemp)
  37. if not self.common.isNone(Field_NacTemp) and Field_NacTemp in dataFrame.columns:
  38. useCols.append(Field_NacTemp)
  39. useCols.extend(non_empty_cols)
  40. return useCols
  41. def getNoneEmptyFields(self, dataFrame: pd.DataFrame, temperatureColumns: dict) -> list:
  42. # 使用set和列表推导式来获取在DataFrame中存在的字段
  43. existing_fields = [
  44. key for key in TemperatureColumns.keys() if key in dataFrame.columns
  45. ]
  46. # 检查指定列中非全为空的列
  47. non_empty_columns = dataFrame[existing_fields].apply(
  48. lambda x: x.notnull().any(), axis=0)
  49. # 获取非全为空的列名
  50. noneEmptyFields = non_empty_columns[non_empty_columns].index.tolist()
  51. return noneEmptyFields
  52. def dataReprocess(self, dataFrame: pd.DataFrame, non_empty_cols: list):
  53. # Initialize an empty df for aggregation
  54. agg_dict = {col: 'median' for col in non_empty_cols}
  55. # Group by 'power_floor' and aggregate
  56. grouped = dataFrame.groupby([Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine]).agg(agg_dict).reset_index()
  57. # Sort by 'power_floor'
  58. grouped.sort_values(
  59. [Field_PowerFloor, Field_CodeOfTurbine, Field_NameOfTurbine], inplace=True)
  60. return grouped
  61. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  62. select=[Field_DeviceCode, Field_Time, Field_WindSpeed, Field_ActiverPower]
  63. select=select+[Field_EnvTemp,Field_NacTemp]+list(TemperatureColumns.keys())
  64. dictionary = self.processTurbineData(turbineCodes, conf,select )
  65. dataFrameOfTurbines = self.userDataFrame(
  66. dictionary, conf.dataContract.configAnalysis, self)
  67. turbrineInfos = self.common.getTurbineInfos(
  68. conf.dataContract.dataFilter.powerFarmID, turbineCodes, self.turbineInfo)
  69. groupedOfTurbineModel = turbrineInfos.groupby(Field_MillTypeCode)
  70. returnDatas = []
  71. for turbineModelCode, group in groupedOfTurbineModel:
  72. currTurbineCodes = group[Field_CodeOfTurbine].unique().tolist()
  73. currTurbineModeInfo = self.common.getTurbineModelByCode(
  74. turbineModelCode, self.turbineModelInfo)
  75. currDataFrameOfTurbines = dataFrameOfTurbines[dataFrameOfTurbines[Field_CodeOfTurbine].isin(
  76. currTurbineCodes)]
  77. # 将 currTurbineInfos 转换为字典
  78. currTurbineInfos_dict = turbrineInfos.set_index(Field_CodeOfTurbine)[Field_NameOfTurbine].to_dict()
  79. # 使用 map 函数来填充 Field_NameOfTurbine 列
  80. currDataFrameOfTurbines[Field_NameOfTurbine] = currDataFrameOfTurbines[Field_CodeOfTurbine].map(currTurbineInfos_dict).fillna("")
  81. # 获取非全为空的列名
  82. non_empty_cols = self.getUseColumns(currDataFrameOfTurbines, TemperatureColumns)
  83. dataFrame = self.dataReprocess(currDataFrameOfTurbines, non_empty_cols)
  84. # non_empty_cols.remove(Field_Time)
  85. non_empty_cols.remove(Field_ActiverPower)
  86. non_empty_cols.remove(Field_EnvTemp)
  87. non_empty_cols.remove(Field_NacTemp)
  88. returnData= self.drawTemperatureGraph(dataFrame, outputAnalysisDir, conf, non_empty_cols,currTurbineModeInfo)
  89. returnDatas.append(returnData)
  90. returnResult = pd.concat(returnDatas, ignore_index=True)
  91. return returnResult
  92. def drawTemperatureGraph(self, dataFrameMerge: pd.DataFrame, outputAnalysisDir, conf: Contract, temperatureCols: list, turbineModelInfo: pd.Series):
  93. """
  94. 大部件温度传感器分析
  95. """
  96. y_name = '温差(℃)'
  97. outputDir = os.path.join(outputAnalysisDir, "GeneratorTemperature")
  98. dir.create_directory(outputDir)
  99. # 按设备名分组数据
  100. grouped = dataFrameMerge.groupby(Field_CodeOfTurbine)
  101. result_rows = []
  102. # Create output directories if they don't exist
  103. for column in temperatureCols:
  104. if not column in dataFrameMerge.columns:
  105. continue
  106. # 判断是否是机舱温度本身
  107. is_nac_temp = (column == Field_NacTemp)
  108. columnZH = TemperatureColumns.get(column)
  109. if not is_nac_temp:
  110. columnZH = f"{columnZH}温差"
  111. outputPath = os.path.join(outputAnalysisDir, column)
  112. dir.create_directory(outputPath)
  113. fig = go.Figure()
  114. # 获取 Plotly Express 中的颜色序列
  115. colors = px.colors.sequential.Turbo
  116. # 创建一个列表来存储各个风电机组的数据
  117. turbine_data_list = []
  118. # Add traces for each turbine
  119. for idx, (name, group) in enumerate(grouped):
  120. currTurbineInfo_group = self.common.getTurbineInfo(
  121. conf.dataContract.dataFilter.powerFarmID, name, self.turbineInfo)
  122. # --- 数据计算逻辑 ---
  123. x_data = group[Field_PowerFloor]
  124. y_data = group[column]
  125. # 如果不是机舱温度本身,且存在机舱温度数据,则计算温差(部件温度 - 机舱温度)
  126. if not is_nac_temp and Field_NacTemp in group.columns:
  127. y_data = y_data - group[Field_NacTemp]
  128. # ------------------
  129. fig.add_trace(go.Scatter(
  130. x=group[Field_PowerFloor],
  131. y=group[column],
  132. mode='lines',
  133. name=currTurbineInfo_group[Field_NameOfTurbine],
  134. # 从 'Rainbow' 色组中循环选择颜色
  135. line=dict(color=colors[idx % len(colors)])
  136. ))
  137. # 提取数据
  138. turbine_data_total = {
  139. "engineName": currTurbineInfo_group[Field_NameOfTurbine],
  140. "engineCode": name,
  141. "xData": group[Field_PowerFloor].tolist(),
  142. "yData": group[column].tolist(),
  143. "color": colors[idx % len(colors)]
  144. }
  145. turbine_data_list.append(turbine_data_total)
  146. # Update layout and axes
  147. fig.update_layout(
  148. title={'text': f'{columnZH}分布-{turbineModelInfo[Field_MachineTypeCode]}', 'x': 0.5},
  149. xaxis_title='功率',
  150. yaxis_title=y_name,
  151. xaxis=dict(
  152. range=[
  153. self.axisLowerLimitActivePower,
  154. self.axisUpperLimitActivePower
  155. ],
  156. dtick=self.axisStepActivePower
  157. ),
  158. yaxis=dict(
  159. range=[0, 100],
  160. dtick=20
  161. ),
  162. # legend_title_text='Turbine',
  163. legend=dict(
  164. orientation="h", # Horizontal orientation
  165. xanchor="center", # Anchor the legend to the center
  166. x=0.5, # Position legend at the center of the x-axis
  167. y=-0.2, # Position legend below the x-axis
  168. # itemsizing='constant', # Keep the size of the legend entries constant
  169. # # Set the width of the legend items (in pixels)
  170. # itemwidth=50
  171. )
  172. )
  173. # Save the plot as a PNG/HTML file
  174. # filePathOfImage = os.path.join(outputPath, f"{columnZH}-{turbineModelInfo[Field_MillTypeCode]}.png")
  175. # fig.write_image(filePathOfImage, scale=3)
  176. # filePathOfHtml = os.path.join(outputPath, f"{columnZH}-{turbineModelInfo[Field_MillTypeCode]}.html")
  177. #fig.write_html(filePathOfHtml)
  178. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  179. if isinstance(engineTypeCode, pd.Series):
  180. engineTypeCode = engineTypeCode.iloc[0]
  181. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  182. if isinstance(engineTypeName, pd.Series):
  183. engineTypeName = engineTypeName.iloc[0]
  184. # 构建最终的JSON对象
  185. json_output = {
  186. "analysisTypeCode": "大部件温度传感器分析",
  187. "typecode": turbineModelInfo[Field_MillTypeCode],
  188. "engineCode": engineTypeCode,
  189. "engineTypeName": engineTypeName,
  190. "title": f'{columnZH}分布-{turbineModelInfo[Field_MachineTypeCode]}',
  191. "xaixs": "功率(kW)",
  192. "yaixs": y_name,
  193. "data": turbine_data_list
  194. }
  195. # 将JSON对象保存到文件
  196. output_json_path = os.path.join(outputPath,f"{column}_{turbineModelInfo[Field_MillTypeCode]}.json")
  197. with open(output_json_path, 'w', encoding='utf-8') as f:
  198. import json
  199. json.dump(json_output, f, ensure_ascii=False, indent=4)
  200. # result_rows.append({
  201. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  202. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  203. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  204. # Field_CodeOfTurbine: Const_Output_Total,
  205. # Field_Return_FilePath: filePathOfImage,
  206. # Field_Return_IsSaveDatabase: False
  207. # })
  208. # result_rows.append({
  209. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  210. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  211. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  212. # Field_CodeOfTurbine: Const_Output_Total,
  213. # Field_Return_FilePath: filePathOfHtml,
  214. # Field_Return_IsSaveDatabase: True
  215. # })
  216. # 如果需要返回DataFrame,可以包含文件路径
  217. result_rows.append({
  218. Field_Return_TypeAnalyst: self.typeAnalyst(),
  219. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  220. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  221. Field_CodeOfTurbine: 'total',
  222. Field_MillTypeCode: f'{column}_{turbineModelInfo[Field_MillTypeCode]}',
  223. Field_Return_FilePath: output_json_path,
  224. Field_Return_IsSaveDatabase: True
  225. })
  226. # Create individual plots with specific turbine highlighted
  227. # for name, group in grouped:
  228. for idx, (name, group) in enumerate(grouped):
  229. single_fig = go.Figure()
  230. currTurbineInfo_each = self.common.getTurbineInfo(
  231. conf.dataContract.dataFilter.powerFarmID, name, self.turbineInfo)
  232. # 创建一个列表来存储各个风电机组的数据
  233. turbine_data_list_each = []
  234. # Add all other turbines in grey first
  235. # for other_name, other_group in grouped:
  236. for idx, (other_name, other_group) in enumerate(grouped):
  237. if other_name != name:
  238. tempTurbineInfo = self.common.getTurbineInfo(
  239. conf.dataContract.dataFilter.powerFarmID, other_name, self.turbineInfo)
  240. # --- 数据计算 ---
  241. ox_data = other_group[Field_PowerFloor]
  242. oy_data = other_group[column]
  243. if not is_nac_temp and Field_NacTemp in other_group.columns:
  244. oy_data = oy_data - other_group[Field_NacTemp]
  245. # ---------------
  246. single_fig.add_trace(go.Scatter(
  247. x=ox_data,
  248. y=oy_data,
  249. mode='lines',
  250. name=tempTurbineInfo[Field_NameOfTurbine],
  251. line=dict(color='lightgrey', width=1),
  252. showlegend=False
  253. ))
  254. # 提取数据
  255. turbine_data_other_each = {
  256. "engineName": tempTurbineInfo[Field_NameOfTurbine],
  257. "engineCode": other_name,
  258. "xData": other_group[Field_PowerFloor].tolist(),
  259. "yData": other_group[column].tolist(),
  260. }
  261. turbine_data_list_each.append(turbine_data_other_each)
  262. # Add the turbine of interest in dark blue
  263. # --- 数据计算 ---
  264. gx_data = group[Field_PowerFloor]
  265. gy_data = group[column]
  266. if not is_nac_temp and Field_NacTemp in group.columns:
  267. gy_data = gy_data - group[Field_NacTemp]
  268. # ---------------
  269. single_fig.add_trace(go.Scatter(
  270. x=gx_data,
  271. y=gy_data,
  272. mode='lines',
  273. # Make it slightly thicker for visibility
  274. line=dict(color='darkblue', width=2),
  275. showlegend=False # Disable legend for cleaner look
  276. ))
  277. turbine_data_curr = {
  278. "engineName": currTurbineInfo_each[Field_NameOfTurbine],
  279. "engineCode": currTurbineInfo_each[Field_CodeOfTurbine],
  280. "xData": group[Field_PowerFloor].tolist(),
  281. "yData": group[column].tolist(),
  282. }
  283. turbine_data_list_each.append(turbine_data_curr)
  284. # Update layout and axes for the individual plot
  285. single_fig.update_layout(
  286. # title={'text': f'Turbine: {name}', 'x': 0.5},
  287. title={'text': f'{columnZH}分布: {currTurbineInfo_each[Field_NameOfTurbine]}', 'x': 0.5},
  288. xaxis_title='功率',
  289. yaxis_title=y_name,
  290. xaxis=dict(
  291. range=[0, Field_RatedPower],
  292. dtick=200
  293. ),
  294. yaxis=dict(
  295. range=[0, 100],
  296. dtick=20
  297. )
  298. )
  299. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  300. if isinstance(engineTypeCode, pd.Series):
  301. engineTypeCode = engineTypeCode.iloc[0]
  302. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  303. if isinstance(engineTypeName, pd.Series):
  304. engineTypeName = engineTypeName.iloc[0]
  305. # 构建最终的JSON对象
  306. json_output = {
  307. "analysisTypeCode": "大部件温度传感器分析",
  308. "typecode": turbineModelInfo[Field_MillTypeCode],
  309. "engineCode": engineTypeCode,
  310. "engineTypeName": engineTypeName,
  311. "title": f'{columnZH}分布: {currTurbineInfo_each[Field_NameOfTurbine]}',
  312. "xaixs": "功率(kW)",
  313. "yaixs": y_name,
  314. "data": turbine_data_list_each
  315. }
  316. # 将JSON对象保存到文件
  317. output_json_path_each = os.path.join(outputPath, f"{currTurbineInfo_each[Field_NameOfTurbine]}.json")
  318. with open(output_json_path_each, 'w', encoding='utf-8') as f:
  319. import json
  320. json.dump(json_output, f, ensure_ascii=False, indent=4)
  321. # filePathOfImage = os.path.join(
  322. # outputPath, f"{currTurbineInfo_each[Field_NameOfTurbine]}.png")
  323. # single_fig.write_image(filePathOfImage, scale=3)
  324. # filePathOfHtml = os.path.join(
  325. # outputPath, f"{name}.html")
  326. # single_fig.write_html(filePathOfHtml)
  327. # result_rows.append({
  328. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  329. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  330. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  331. # Field_CodeOfTurbine: group[Field_CodeOfTurbine].iloc[0],
  332. # Field_Return_FilePath: filePathOfImage,
  333. # Field_Return_IsSaveDatabase: False
  334. # })
  335. # 如果需要返回DataFrame,可以包含文件路径
  336. result_rows.append({
  337. Field_Return_TypeAnalyst: self.typeAnalyst(),
  338. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  339. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  340. Field_CodeOfTurbine: group[Field_CodeOfTurbine].iloc[0],
  341. Field_Return_FilePath: output_json_path_each,
  342. Field_Return_IsSaveDatabase: True
  343. })
  344. # result_rows.append({
  345. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  346. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  347. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  348. # Field_CodeOfTurbine: group[Field_CodeOfTurbine].iloc[0],
  349. # Field_Return_FilePath: filePathOfHtml,
  350. # Field_Return_IsSaveDatabase: True
  351. # })
  352. for idx, (name, group) in enumerate(grouped):
  353. # 绘制每台机组发电机的,驱动轴承温度、非驱动轴承温度、发电机轴承温度BIAS、发电机轴承温度和机舱温度BIAS 均与有功功率的折线图
  354. if not Field_GeneratorDE in group.columns or not Field_GeneratorNDE in group.columns or group[Field_GeneratorDE].isna().all() or group[Field_GeneratorNDE].isna().all():
  355. self.logger.warning(f"{name} 不具备发电机温度测点,或测点值全为空")
  356. continue
  357. refFieldTemperature = Field_EnvTemp if group[Field_NacTemp].isna(
  358. ).all() else Field_NacTemp
  359. result_rows1 = self.drawGeneratorTemperature(
  360. group, conf, Field_GeneratorDE, Field_GeneratorNDE, refFieldTemperature, Field_PowerFloor, name, outputDir)
  361. result_rows.extend(result_rows1)
  362. result_df = pd.DataFrame(result_rows)
  363. return result_df
  364. def drawGeneratorTemperature(self, dataFrame: pd.DataFrame, conf: Contract, yAxisDE, yAxisNDE, diffTemperature, xAxis, turbineCode, outputDir):
  365. tempTurbineInfo1 = self.common.getTurbineInfo(
  366. conf.dataContract.dataFilter.powerFarmID, turbineCode, self.turbineInfo)
  367. turbineName = tempTurbineInfo1[Field_NameOfTurbine]
  368. # 发电机驱动轴承温度 和 发电机非驱动轴承 温差
  369. fieldBIAS_DE_NDE = 'BIAS_DE-NDE'
  370. fieldBIAS_DE = 'BIAS_DE'
  371. fieldBIAS_NDE = 'BIAS_NDE'
  372. # Prepare Data
  373. dataFrame[fieldBIAS_DE] = dataFrame[yAxisDE] - \
  374. dataFrame[diffTemperature]
  375. dataFrame[fieldBIAS_NDE] = dataFrame[yAxisNDE] - \
  376. dataFrame[diffTemperature]
  377. dataFrame[fieldBIAS_DE_NDE] = dataFrame[yAxisDE] - dataFrame[yAxisNDE]
  378. # Create a plot with dual y-axes
  379. fig = make_subplots(specs=[[{"secondary_y": True}]])
  380. plot_data_list_each = []
  381. # Plot DE Bearing Temperature
  382. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[yAxisDE], name='驱动端轴承温度', line=dict(
  383. color='blue')), secondary_y=False)
  384. plot_data_curr = {
  385. "Name": '驱动端轴承温度',
  386. "xData": dataFrame[xAxis].tolist(),
  387. "yData": dataFrame[yAxisDE].tolist(),
  388. "color": 'blue',
  389. }
  390. plot_data_list_each.append(plot_data_curr)
  391. # Plot NDE Bearing Temperature
  392. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[yAxisNDE], name='非驱动端轴承温度', line=dict(
  393. color='green')), secondary_y=False)
  394. plot_data_curr = {
  395. "Name": '非驱动端轴承温度',
  396. "xData": dataFrame[xAxis].tolist(),
  397. "yData": dataFrame[yAxisNDE].tolist(),
  398. "color": 'green',
  399. }
  400. plot_data_list_each.append(plot_data_curr)
  401. # Plot Temperature Differences
  402. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[fieldBIAS_DE], name='驱动端轴承温度与机舱温度偏差', line=dict(
  403. color='blue', dash='dot')), secondary_y=False)
  404. plot_data_curr = {
  405. "Name": '驱动端轴承温度与机舱温度偏差',
  406. "xData": dataFrame[xAxis].tolist(),
  407. "yData": dataFrame[fieldBIAS_DE].tolist(),
  408. "color": 'blue',
  409. }
  410. plot_data_list_each.append(plot_data_curr)
  411. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[fieldBIAS_NDE], name='非驱动端轴承温度与机舱温度偏差', line=dict(
  412. color='green', dash='dot')), secondary_y=False)
  413. plot_data_curr = {
  414. "Name": '非驱动端轴承温度与机舱温度偏差',
  415. "xData": dataFrame[xAxis].tolist(),
  416. "yData": dataFrame[fieldBIAS_NDE].tolist(),
  417. "color": 'green',
  418. }
  419. plot_data_list_each.append(plot_data_curr)
  420. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[fieldBIAS_DE_NDE],
  421. name='驱动端轴承与非驱动端轴承温度偏差', line=dict(color='black', dash='dash')), secondary_y=False)
  422. plot_data_curr = {
  423. "Name": '驱动端轴承与非驱动端轴承温度偏差',
  424. "xData": dataFrame[xAxis].tolist(),
  425. "yData": dataFrame[fieldBIAS_DE_NDE].tolist(),
  426. "color": 'black',
  427. }
  428. plot_data_list_each.append(plot_data_curr)
  429. # Plot Nacelle Temperature
  430. fig.add_trace(go.Scatter(x=dataFrame[xAxis], y=dataFrame[diffTemperature],
  431. name='机舱温度', line=dict(color='orange')), secondary_y=False)
  432. plot_data_curr = {
  433. "Name": '机舱温度',
  434. "xData": dataFrame[xAxis].tolist(),
  435. "yData": dataFrame[diffTemperature].tolist(),
  436. "color": 'orange',
  437. }
  438. plot_data_list_each.append(plot_data_curr)
  439. # Add horizontal reference lines
  440. fig.add_hline(y=5, line_dash="dot", line_color="#FFDB58")
  441. fig.add_hline(y=-5, line_dash="dot", line_color="#FFDB58")
  442. fig.add_hline(y=15, line_dash="dot", line_color="red")
  443. fig.add_hline(y=-15, line_dash="dot", line_color="red")
  444. # Update layout
  445. fig.update_layout(
  446. title={'text': f'发电机温度偏差: {turbineName}'},
  447. xaxis_title="功率",
  448. yaxis_title='轴承温度 & 偏差',
  449. legend_title='温度 & 偏差',
  450. legend=dict(x=1.1, y=0.5, bgcolor='rgba(255, 255, 255, 0.5)'),
  451. margin=dict(r=200) # Adjust margin to fit legend
  452. )
  453. fig.update_yaxes(range=[-20, 100], secondary_y=False)
  454. # 构建最终的JSON对象
  455. json_output = {
  456. "analysisTypeCode": "发电机温度传感器分析",
  457. "turbineName": tempTurbineInfo1[Field_NameOfTurbine],
  458. "turbineCode": tempTurbineInfo1[Field_CodeOfTurbine],
  459. "title": f'发电机温度偏差: {turbineName}',
  460. "xaixs": "功率(kW)",
  461. "yaixs": '轴承温度 & 偏差(℃)',
  462. "data": plot_data_list_each
  463. }
  464. # 将JSON对象保存到文件
  465. output_json_path_each = os.path.join(outputDir, f"{turbineName}.json")
  466. with open(output_json_path_each, 'w', encoding='utf-8') as f:
  467. import json
  468. json.dump(json_output, f, ensure_ascii=False, indent=4)
  469. # Save the plot as a PNG/HTML file
  470. # filePathOfImage = os.path.join(outputDir, f"{turbineName}.png")
  471. # fig.write_image(filePathOfImage, width=800, height=600, scale=3)
  472. # filePathOfHtml = os.path.join(outputDir, f"{turbineName}.html")
  473. # fig.write_html(filePathOfHtml)
  474. result_rows1 = []
  475. # result_rows1.append({
  476. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  477. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  478. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  479. # Field_CodeOfTurbine: dataFrame[Field_CodeOfTurbine].iloc[0],
  480. # Field_Return_FilePath: filePathOfImage,
  481. # Field_Return_IsSaveDatabase: False
  482. # })
  483. # 如果需要返回DataFrame,可以包含文件路径
  484. result_rows1.append({
  485. Field_Return_TypeAnalyst: self.typeAnalyst(),
  486. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  487. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  488. Field_CodeOfTurbine: dataFrame[Field_CodeOfTurbine].iloc[0],
  489. Field_Return_FilePath: output_json_path_each,
  490. Field_Return_IsSaveDatabase: True
  491. })
  492. # result_rows1.append({
  493. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  494. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  495. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  496. # Field_CodeOfTurbine: dataFrame[Field_CodeOfTurbine].iloc[0],
  497. # Field_Return_FilePath: filePathOfHtml,
  498. # Field_Return_IsSaveDatabase: True
  499. # })
  500. return result_rows1