pitchPowerAnalyst.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. import os
  2. from datetime import datetime
  3. import numpy as np
  4. import pandas as pd
  5. import plotly.express as px
  6. import plotly.graph_objects as go
  7. from algorithmContract.confBusiness import *
  8. from algorithmContract.contract import Contract
  9. from behavior.analystWithGoodBadPoint import AnalystWithGoodBadPoint
  10. class PitchPowerAnalyst(AnalystWithGoodBadPoint):
  11. """
  12. 风电机组变桨-功率分析
  13. """
  14. def typeAnalyst(self):
  15. return "pitch_power"
  16. def selectColumns(self):
  17. return [Field_DeviceCode, Field_Time,Field_WindSpeed, Field_ActiverPower, Field_PitchAngel1]
  18. def turbinesAnalysis(self, outputAnalysisDir, conf: Contract, turbineCodes):
  19. dictionary = self.processTurbineData(turbineCodes, conf,self.selectColumns())
  20. dataFrame = self.userDataFrame(dictionary,conf.dataContract.configAnalysis,self)
  21. turbineInfos = self.common.getTurbineInfos(conf.dataContract.dataFilter.powerFarmID, turbineCodes,
  22. self.turbineInfo)
  23. result_df1 = self.plot_power_pitch_angle(dataFrame, turbineInfos, outputAnalysisDir, conf)
  24. result_df2 = self.drawScatterGraph(dataFrame, turbineInfos, outputAnalysisDir, conf)
  25. result_df = pd.concat([result_df1, result_df2], ignore_index=True)
  26. return result_df
  27. def plot_power_pitch_angle(self, dataFrame:pd.DataFrame, turbineModelInfo: pd.Series, outputAnalysisDir:str, conf: Contract):
  28. # 按设备名分组数据
  29. dataFrameMerge = dataFrame[(dataFrame[Field_ActiverPower] > 0)].sort_values(by=Field_YearMonth)
  30. grouped = dataFrameMerge.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
  31. # 定义固定的颜色映射列表
  32. fixed_colors = [
  33. "#3E409C",
  34. "#476CB9",
  35. "#3586BF",
  36. "#4FA4B5",
  37. "#52A3AE",
  38. "#60C5A3",
  39. "#85D0AE",
  40. "#A8DCA2",
  41. "#CFEE9E",
  42. "#E4F39E",
  43. "#EEF9A7",
  44. "#FBFFBE",
  45. "#FDF1A9",
  46. "#FFE286",
  47. "#FFC475",
  48. "#FCB06C",
  49. "#F78F4F",
  50. "#F96F4A",
  51. "#E4574C",
  52. "#CA3756",
  53. "#AF254F"
  54. ]
  55. # 将 fixed_colors 转换为 Plotly 的 colorscale 格式
  56. fixed_colorscale = [
  57. [i / (len(fixed_colors) - 1), color] for i, color in enumerate(fixed_colors)
  58. ]
  59. # 遍历每个设备并绘制散点图
  60. result_rows1 = []
  61. for name, group in grouped:
  62. # 创建图形
  63. fig = go.Figure()
  64. # 添加散点图
  65. fig.add_trace(go.Scatter(
  66. x=group[Field_ActiverPower],
  67. y=group[Field_PitchAngel1],
  68. mode='markers',
  69. # marker=dict(color='blue', size=3.5)
  70. marker=dict(
  71. color=group[Field_UnixYearMonth],
  72. colorscale=fixed_colorscale,
  73. size=3,
  74. opacity=0.7,
  75. colorbar=dict(
  76. tickvals=np.linspace(
  77. group[Field_UnixYearMonth].min(), group[Field_UnixYearMonth].max(), 6),
  78. ticktext=[datetime.fromtimestamp(ts).strftime('%Y-%m') for ts in np.linspace(
  79. group[Field_UnixYearMonth].min(), group[Field_UnixYearMonth].max(), 6)],
  80. thickness=18,
  81. len=1, # 设置颜色条的长度,使其占据整个图的高度
  82. outlinecolor='rgba(255,255,255,0)'
  83. ),
  84. showscale=True
  85. ),
  86. showlegend=False
  87. ))
  88. # 设置图形布局
  89. fig.update_layout(
  90. title=f'机组: {name[0]}',
  91. xaxis=dict(
  92. title='功率',
  93. range=[self.axisLowerLimitActivePower,
  94. self.axisUpperLimitActivePower],
  95. dtick=self.axisStepActivePower,
  96. tickangle=-45 # 设置x轴刻度值旋转角度为45度,如果需要
  97. ),
  98. yaxis=dict(
  99. title='桨距角',
  100. range=[self.axisLowerLimitPitchAngle,
  101. self.axisUpperLimitPitchAngle],
  102. dtick=self.axisStepPitchAngle
  103. ),
  104. coloraxis=dict(
  105. colorbar=dict(
  106. title="时间",
  107. ticks="outside",
  108. len=1, # 设置颜色条的长度,使其占据整个图的高度
  109. thickness=20, # 调整颜色条的宽度
  110. orientation='v', # 设置颜色条为垂直方向
  111. tickmode='array', # 确保刻度按顺序排列
  112. tickvals=dataFrameMerge[Field_YearMonth].unique(
  113. ).tolist(), # 确保刻度为唯一的年月
  114. ticktext=dataFrameMerge[Field_YearMonth].unique(
  115. ).tolist() # 以%Y-%m格式显示标签
  116. )
  117. )
  118. )
  119. # 确保从 Series 中提取的是具体的值
  120. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  121. if isinstance(engineTypeCode, pd.Series):
  122. engineTypeCode = engineTypeCode.iloc[0]
  123. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  124. if isinstance(engineTypeName, pd.Series):
  125. engineTypeName = engineTypeName.iloc[0]
  126. group[Field_UnixYearMonth] = pd.to_datetime(group[Field_UnixYearMonth], unit='s').dt.strftime(
  127. '%Y-%m-%d %H:%M:%S')
  128. scada = self.getTimeGranularitys(conf)[0]
  129. # 构建最终的JSON对象
  130. json_output = {
  131. "field_code": self.currPowerFarmInfo[Field_PowerFarmCode],
  132. "scada": scada,
  133. "analysisTypeCode": "变桨和有功功率协调性分析",
  134. "engineCode": engineTypeCode,
  135. "engineTypeName": engineTypeName,
  136. "xaixs": "功率(kW)",
  137. "yaixs": "桨距角(°)",
  138. "data": [{
  139. "engineName": name[0],
  140. "engineCode": name[1],
  141. "title": f' 机组: {name[0]}',
  142. "xData": group[Field_ActiverPower].tolist(),
  143. "yData": group[Field_PitchAngel1].tolist(),
  144. "timeData": group[Field_UnixYearMonth].tolist(),
  145. # "colorbar": dataFrameMerge[Field_YearMonth].unique().tolist(),
  146. "colorbar": group[Field_YearMonth].tolist(),
  147. }]
  148. }
  149. # 保存图像
  150. # filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}.png")
  151. # fig.write_image(filePathOfImage, width=800, height=600, scale=3)
  152. # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}.html")
  153. # fig.write_html(filePathOfHtml)
  154. # 将JSON对象保存到文件
  155. output_json_path = os.path.join(outputAnalysisDir, f"pitch_Power_Analyst{name[0]}.json")
  156. with open(output_json_path, 'w', encoding='utf-8') as f:
  157. import json
  158. json.dump(json_output, f, ensure_ascii=False, indent=4)
  159. # result_rows1.append({
  160. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  161. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  162. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  163. # Field_CodeOfTurbine: name[1],
  164. # Field_Return_FilePath: filePathOfImage,
  165. # Field_Return_IsSaveDatabase: False
  166. # })
  167. result_rows1.append({
  168. Field_Return_TypeAnalyst: self.typeAnalyst(),
  169. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  170. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  171. Field_CodeOfTurbine: name[1],
  172. Field_MillTypeCode: 'total',
  173. Field_Return_FilePath: output_json_path,
  174. Field_Return_IsSaveDatabase: True
  175. })
  176. result_df1 = pd.DataFrame(result_rows1)
  177. return result_df1
  178. def drawScatterGraph(self, dataFrame: pd.DataFrame, turbineModelInfo: pd.Series, outputAnalysisDir: str, conf: Contract):
  179. dataFrame = dataFrame[(dataFrame[Field_ActiverPower] > 0)].sort_values(
  180. by=Field_YearMonth)
  181. grouped = dataFrame.groupby([Field_NameOfTurbine, Field_CodeOfTurbine])
  182. '''
  183. # 遍历每个设备的数据
  184. result_rows2 = []
  185. for name, group in grouped:
  186. if len(group[Field_YearMonth].unique()) > 1:
  187. fig = px.scatter_3d(dataFrame,
  188. x=Field_PitchAngel1,
  189. y=Field_YearMonth,
  190. z=Field_ActiverPower,
  191. color=Field_YearMonth,
  192. labels={Field_PitchAngel1: '桨距角',
  193. Field_YearMonth: '时间', Field_ActiverPower: '功率'},
  194. )
  195. # 设置固定散点大小
  196. fig.update_traces(marker=dict(size=1.5))
  197. # 更新图形的布局
  198. fig.update_layout(
  199. title={
  200. "text": f'月度桨距角功率3D散点图: {name[0]}',
  201. "x": 0.5
  202. },
  203. scene=dict(
  204. xaxis=dict(
  205. title='桨距角',
  206. dtick=self.axisStepPitchAngle,
  207. range=[self.axisLowerLimitPitchAngle,
  208. self.axisUpperLimitPitchAngle],
  209. ),
  210. yaxis=dict(
  211. title='时间',
  212. tickformat='%Y-%m', # 日期格式,
  213. dtick='M1', # 每月一个刻度
  214. showgrid=True, # 显示网格线
  215. ),
  216. zaxis=dict(
  217. title='功率',
  218. dtick=self.axisStepActivePower,
  219. range=[self.axisLowerLimitActivePower,
  220. self.axisUpperLimitActivePower],
  221. showgrid=True, # 显示网格线
  222. )
  223. ),
  224. scene_camera=dict(
  225. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  226. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  227. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  228. ),
  229. # 设置图例标题
  230. # legend_title_text='Time',
  231. legend=dict(
  232. orientation="h",
  233. itemsizing="constant", # Use constant size for legend items
  234. itemwidth=80 # Set the width of legend items to 50 pixels
  235. )
  236. )
  237. '''
  238. # 假设 colorsList 已经在代码的其他部分定义
  239. colorsList = [
  240. "#3E409C",
  241. "#3586BF",
  242. "#52A3AE",
  243. "#85D0AE",
  244. "#A8DCA2",
  245. "#FBFFBE",
  246. "#FDF1A9",
  247. "#FFE286",
  248. "#FCB06C",
  249. "#F96F4A",
  250. "#E4574C",
  251. "#AF254F"
  252. ]
  253. # 遍历每个设备的数据
  254. result_rows2 = []
  255. for name, group in grouped:
  256. if len(group[Field_YearMonth].unique()) > 1:
  257. fig = px.scatter_3d(
  258. group,
  259. x=Field_PitchAngel1,
  260. y=Field_YearMonth,
  261. z=Field_ActiverPower,
  262. color=Field_YearMonth,
  263. color_discrete_sequence=colorsList, # 使用 colorsList 作为颜色映射
  264. labels={
  265. Field_PitchAngel1: '桨距角',
  266. Field_YearMonth: '时间',
  267. Field_ActiverPower: '功率'
  268. },
  269. )
  270. # 设置固定散点大小
  271. fig.update_traces(marker=dict(size=1.5))
  272. # 更新图形的布局
  273. fig.update_layout(
  274. title={
  275. "text": f'月度桨距角功率3D散点图: {name[0]}',
  276. "x": 0.5
  277. },
  278. scene=dict(
  279. xaxis=dict(
  280. title='桨距角',
  281. dtick=self.axisStepPitchAngle,
  282. range=[self.axisLowerLimitPitchAngle, self.axisUpperLimitPitchAngle],
  283. ),
  284. yaxis=dict(
  285. title='时间',
  286. tickformat='%Y-%m', # 日期格式,
  287. dtick='M1', # 每月一个刻度
  288. showgrid=True, # 显示网格线
  289. ),
  290. zaxis=dict(
  291. title='功率',
  292. dtick=self.axisStepActivePower,
  293. range=[self.axisLowerLimitActivePower, self.axisUpperLimitActivePower],
  294. showgrid=True, # 显示网格线
  295. )
  296. ),
  297. scene_camera=dict(
  298. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  299. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  300. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  301. ),
  302. legend=dict(
  303. orientation="h",
  304. itemsizing="constant", # Use constant size for legend items
  305. itemwidth=80 # Set the width of legend items to 50 pixels
  306. )
  307. )
  308. # 确保从 Series 中提取的是具体的值
  309. engineTypeCode = turbineModelInfo.get(Field_MillTypeCode, "")
  310. if isinstance(engineTypeCode, pd.Series):
  311. engineTypeCode = engineTypeCode.iloc[0]
  312. engineTypeName = turbineModelInfo.get(Field_MachineTypeCode, "")
  313. if isinstance(engineTypeName, pd.Series):
  314. engineTypeName = engineTypeName.iloc[0]
  315. # 构建最终的JSON对象
  316. json_output = {
  317. "analysisTypeCode": "变桨和有功功率协调性分析",
  318. "engineCode": engineTypeCode,
  319. "engineTypeName": engineTypeName,
  320. "xaixs": "桨距角(°)",
  321. "yaixs": "时间",
  322. "zaixs": "有功功率(kW)",
  323. "data": [{
  324. "engineName": name[0],
  325. "engineCode": name[1],
  326. "title": f' 月度桨距角功率3D散点图: {name[0]}',
  327. "xData": group[Field_PitchAngel1].tolist(),
  328. "yData": group[Field_YearMonth].tolist(),
  329. "zData": group[Field_ActiverPower].tolist(),
  330. "color":group[Field_YearMonth].tolist()
  331. }]
  332. }
  333. # 保存图像
  334. # filePathOfImage = os.path.join(outputAnalysisDir, f"{name[0]}_3D.png")
  335. # fig.write_image(filePathOfImage, width=800, height=600, scale=3)
  336. # filePathOfHtml = os.path.join(outputAnalysisDir, f"{name[0]}_3D.html")
  337. # fig.write_html(filePathOfHtml)
  338. # 将JSON对象保存到文件
  339. output_json_path = os.path.join(outputAnalysisDir, f"pitch_Power_Analyst{name[0]}_3D.json")
  340. with open(output_json_path, 'w', encoding='utf-8') as f:
  341. import json
  342. json.dump(json_output, f, ensure_ascii=False, indent=4)
  343. # result_rows2.append({
  344. # Field_Return_TypeAnalyst: self.typeAnalyst(),
  345. # Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  346. # Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  347. # Field_CodeOfTurbine: name[1],
  348. # Field_Return_FilePath: filePathOfImage,
  349. # Field_Return_IsSaveDatabase: False
  350. # })
  351. result_rows2.append({
  352. Field_Return_TypeAnalyst: self.typeAnalyst(),
  353. Field_PowerFarmCode: conf.dataContract.dataFilter.powerFarmID,
  354. Field_Return_BatchCode: conf.dataContract.dataFilter.dataBatchNum,
  355. Field_CodeOfTurbine: name[1],
  356. Field_MillTypeCode: 'total',
  357. Field_Return_FilePath: output_json_path,
  358. Field_Return_IsSaveDatabase: True
  359. })
  360. result_df2 = pd.DataFrame(result_rows2)
  361. return result_df2
  362. # self.drawScatterGraphOfTurbine(
  363. # group, outputAnalysisDir, conf, name)
  364. # def drawScatterGraphOfTurbine(self, dataFrame: pd.DataFrame, outputAnalysisDir: str, conf: Contract, turbineName: str):
  365. # # 创建3D散点图
  366. # fig = px.scatter_3d(dataFrame,
  367. # x=Field_PitchAngel1,
  368. # y=Field_YearMonth,
  369. # z=Field_ActiverPower,
  370. # color=Field_YearMonth,
  371. # labels={Field_PitchAngel1: 'Pitch Angle',
  372. # Field_YearMonth: 'Time', Field_ActiverPower: 'Power'},
  373. # )
  374. # # 设置固定散点大小
  375. # fig.update_traces(marker=dict(size=1.5))
  376. # # 更新图形的布局
  377. # fig.update_layout(
  378. # title={
  379. # "text": f'Monthly Pitch-Power 3D Scatter Plot: {turbineName}',
  380. # "x": 0.5
  381. # },
  382. # scene=dict(
  383. # xaxis=dict(
  384. # title='Pitch Angle',
  385. # range=[conf.dataContract.graphSets["pitchAngle"]["min"] if not self.common.isNone(conf.dataContract.graphSets["pitchAngle"]["min"]) else -2,
  386. # conf.dataContract.graphSets["pitchAngle"]["max"] if not self.common.isNone(conf.dataContract.graphSets["pitchAngle"]["max"]) else 28],
  387. # dtick=conf.dataContract.graphSets["pitchAngle"]["step"] if not self.common.isNone(conf.dataContract.graphSets["pitchAngle"]["step"]) else 2,
  388. # ),
  389. # yaxis=dict(
  390. # title='Time',
  391. # tickformat='%Y-%m', # 日期格式,
  392. # dtick='M1', # 每月一个刻度
  393. # showgrid=True, # 显示网格线
  394. # ),
  395. # zaxis=dict(
  396. # title='Power',
  397. # dtick=conf.dataContract.graphSets["activePower"]["step"] if not self.common.isNone(
  398. # conf.dataContract.graphSets["activePower"]) and not self.common.isNone(
  399. # conf.dataContract.graphSets["activePower"]["step"]) else 250,
  400. # range=[conf.dataContract.graphSets["activePower"]["min"] if not self.common.isNone(
  401. # conf.dataContract.graphSets["activePower"]["min"]) else 0, conf.dataContract.graphSets["activePower"]["max"] if not self.common.isNone(conf.dataContract.graphSets["activePower"]["max"]) else conf.rated_power*1.2],
  402. # showgrid=True, # 显示网格线
  403. # )
  404. # ),
  405. # scene_camera=dict(
  406. # up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  407. # center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  408. # eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  409. # ),
  410. # # 设置图例标题
  411. # legend_title_text='Time'
  412. # )
  413. # # 保存图像
  414. # outputFileHtml = os.path.join(
  415. # outputAnalysisDir, "{}_3D.html".format(turbineName))
  416. # fig.write_html(outputFileHtml)
  417. """"
  418. def drawScatterGraph(self, dataFrame: pd.DataFrame, outputAnalysisDir, conf: Contract):
  419. ## 绘制变桨-功率分布图并保存为文件。
  420. ## 参数:
  421. ## dataFrameMerge (pd.DataFrame): 包含数据的DataFrame,需要包含设备名、风速和功率列。
  422. ## outputAnalysisDir (str): 分析输出目录。
  423. ## conf (ConfBusiness): 配置
  424. ## 按设备名分组数据
  425. colorsList = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd',
  426. '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf', '#aec7e8', '#ffbb78']
  427. grouped = dataFrame.groupby(Field_NameOfTurbine)
  428. # 遍历每个设备的数据
  429. for name, group in grouped:
  430. # 创建颜色映射,将每个年月映射到一个唯一的颜色
  431. unique_months = group[Field_YearMonth].unique()
  432. colors = [
  433. colorsList[i % 12] for i in range(len(unique_months))]
  434. color_map = dict(zip(unique_months, colors))
  435. # 使用go.Scatter3d创建3D散点图
  436. trace = go.Scatter3d(
  437. x=group[Field_PitchAngel1],
  438. y=group[Field_YearMonth],
  439. z=group[Field_ActiverPower],
  440. mode='markers',
  441. marker=dict(
  442. color=[color_map[month]
  443. for month in group[Field_YearMonth]],
  444. size=1.5,
  445. line=dict(
  446. color='rgba(0, 0, 0, 0)', # 设置边框颜色为透明,以去掉白色边框
  447. width=0 # 设置边框宽度为0,进一步确保没有边框
  448. ),
  449. opacity=0.8 # 调整散点的透明度,增加透视效果
  450. )
  451. )
  452. # 创建图形
  453. fig = go.Figure(data=[trace])
  454. # 更新图形的布局
  455. fig.update_layout(
  456. title={
  457. "text": f'三维散点图{name}',
  458. "x": 0.5
  459. },
  460. scene=dict(
  461. xaxis=dict(
  462. title='桨距角',
  463. dtick=conf.dataContract.graphSets["pitchAngle"]["step"] if not self.common.isNone(
  464. conf.dataContract.graphSets["pitchAngle"]["step"]) else 2, # 设置y轴刻度间隔为0.1
  465. range=[conf.dataContract.graphSets["pitchAngle"]["min"] if not self.common.isNone(
  466. conf.dataContract.graphSets["pitchAngle"]["min"]) else -2, conf.dataContract.graphSets["pitchAngle"]["max"] if not self.common.isNone(conf.dataContract.graphSets["pitchAngle"]["max"]) else 28], # 设置y轴的范围从0到1
  467. showgrid=True, # 显示网格线
  468. ),
  469. yaxis=dict(
  470. title='时间',
  471. tickmode='array',
  472. tickvals=unique_months,
  473. ticktext=unique_months,
  474. showgrid=True, # 显示网格线
  475. categoryorder='category ascending'
  476. ),
  477. zaxis=dict(
  478. title='功率',
  479. dtick=conf.dataContract.graphSets["activePower"]["step"] if not self.common.isNone(
  480. conf.dataContract.graphSets["activePower"]) and not self.common.isNone(
  481. conf.dataContract.graphSets["activePower"]["step"]) else 250,
  482. range=[conf.dataContract.graphSets["activePower"]["min"] if not self.common.isNone(
  483. conf.dataContract.graphSets["activePower"]["min"]) else 0, conf.dataContract.graphSets["activePower"]["max"] if not self.common.isNone(conf.dataContract.graphSets["activePower"]["max"]) else conf.rated_power*1.2],
  484. )
  485. ),
  486. scene_camera=dict(
  487. up=dict(x=0, y=0, z=1), # 保持相机向上方向不变
  488. center=dict(x=0, y=0, z=0), # 保持相机中心位置不变
  489. eye=dict(x=-1.8, y=-1.8, z=1.2) # 调整eye属性以实现水平旋转180°
  490. ),
  491. margin=dict(t=50, b=10) # t为顶部(top)间距,b为底部(bottom)间距
  492. )
  493. # 保存图像
  494. outputFileHtml = os.path.join(
  495. outputAnalysisDir, "{}.html".format(name))
  496. fig.write_html(outputFileHtml)
  497. """