yawErrorLine.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. /*
  2. * @Author: your name
  3. * @Date: 2025-05-14 10:49:00
  4. * @LastEditTime: 2025-05-26 11:02:41
  5. * @LastEditors: bogon
  6. * @Description: In User Settings Edit
  7. * @FilePath: /downLoadServer/src/server/utils/chartsCom/yawErrorLine.js
  8. */
  9. import puppeteer from "puppeteer";
  10. import fs from "fs-extra";
  11. import path from "path";
  12. import FormData from "form-data";
  13. import { colorSchemes } from "../colors.js";
  14. import axios from "axios";
  15. export const generateYawErrorLine = async (
  16. chartData,
  17. bucketName,
  18. objectName
  19. ) => {
  20. try {
  21. const data = [];
  22. const colors = [...colorSchemes[0].colors]; // 生成颜色
  23. // 获取 plotly.js 的绝对路径
  24. const plotlyPath = path.join(
  25. process.cwd(),
  26. "src",
  27. "public",
  28. "js",
  29. "plotly-3.0.1.min.js"
  30. );
  31. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  32. chartData &&
  33. chartData.data &&
  34. chartData.data.forEach((turbine, index) => {
  35. // 判断图表类型,根据类型调整绘制方式
  36. const chartConfig = {
  37. x: turbine.xData, // X 数据
  38. y: turbine.yData, // Y 数据
  39. name: turbine.legend, // 使用机组名称
  40. line: {
  41. color: colors[index % colors.length], // 为每个机组分配不同的颜色
  42. },
  43. marker: {
  44. color: colors[index % colors.length], // 为每个机组分配不同的颜色
  45. },
  46. hovertemplate:
  47. `${chartData.xaixs}:` +
  48. ` %{x} <br> ` +
  49. `${chartData.yaixs}:` +
  50. "%{y} <br> <extra></extra>",
  51. };
  52. if (chartData.chartType === "line") {
  53. chartConfig.mode = "lines+markers"; // 如果是折线图
  54. chartConfig.fill = "none";
  55. } else if (chartData.chartType === "bar") {
  56. chartConfig.fill = "tonexty";
  57. }
  58. data.push(chartConfig);
  59. });
  60. const layout = {
  61. title: {
  62. text: chartData.data[0].title,
  63. font: {
  64. size: 16, // 设置标题字体大小(默认 16)
  65. weight: "bold",
  66. },
  67. },
  68. xaxis: {
  69. gridcolor: "rgb(255,255,255)",
  70. tickcolor: "rgb(255,255,255)",
  71. backgroundcolor: "#e5ecf6",
  72. title: chartData.xaixs || "X轴", // 横坐标标题
  73. },
  74. yaxis: {
  75. gridcolor: "rgb(255,255,255)",
  76. tickcolor: "rgb(255,255,255)",
  77. backgroundcolor: "#e5ecf6",
  78. title: chartData.yaixs || "Y轴", // 纵坐标标题
  79. },
  80. // margin: {
  81. // l: 50,
  82. // r: 50,
  83. // t: 50,
  84. // b: 50,
  85. // },
  86. plot_bgcolor: "#e5ecf6",
  87. gridcolor: "#fff",
  88. bgcolor: "#e5ecf6", // 设置背景颜色
  89. autosize: true, // 开启自适应
  90. barmode: chartData.chartType === "bar" ? "stack" : "group", // 如果是柱状图则启用堆叠
  91. };
  92. if (
  93. chartData.contract_Cp_curve_yData &&
  94. chartData.contract_Cp_curve_yData.length > 0
  95. ) {
  96. data.push({
  97. x: chartData.contract_Cp_curve_xData,
  98. y: chartData.contract_Cp_curve_yData,
  99. mode: "lines+markers",
  100. name: "合同功率曲线",
  101. line: {
  102. color: "red",
  103. width: 1, // 设置线条的宽度为1
  104. },
  105. marker: { color: "red", size: 4 },
  106. });
  107. }
  108. // 使用 Puppeteer 生成图表的截图
  109. const browser = await puppeteer.launch({
  110. headless: "new",
  111. args: ["--no-sandbox", "--disable-setuid-sandbox"],
  112. });
  113. const tempDir = path.join(process.cwd(), "images");
  114. await fs.ensureDir(tempDir);
  115. const tempFilePath = path.join(
  116. tempDir,
  117. `temp_yaw_error_chart_${Date.now()}.jpeg`
  118. );
  119. try {
  120. const page = await browser.newPage();
  121. const htmlContent = `
  122. <!DOCTYPE html>
  123. <html>
  124. <head>
  125. <meta charset="UTF-8">
  126. <title>静态偏航折线分图</title>
  127. <script>${plotlyContent}</script>
  128. <style>
  129. body { margin: 0; }
  130. #chart { width: 800px; height: 600px; }
  131. </style>
  132. </head>
  133. <body>
  134. <div id="chart"></div>
  135. <script>
  136. window.onload = function() {
  137. Plotly.newPlot('chart', ${JSON.stringify(
  138. data
  139. )}, ${JSON.stringify(layout)}).then(() => {
  140. window.chartRendered = true;
  141. });
  142. };
  143. </script>
  144. </body>
  145. </html>
  146. `;
  147. await page.setContent(htmlContent, { waitUntil: "networkidle0" });
  148. await page.waitForFunction(() => window.chartRendered === true, {
  149. timeout: 60000,
  150. });
  151. // 截图并保存到临时文件
  152. const chartElement = await page.$("#chart");
  153. await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
  154. // 上传图片到服务器
  155. const formData = new FormData();
  156. formData.append("file", fs.createReadStream(tempFilePath));
  157. const response = await axios.post(
  158. `${process.env.API_BASE_URL}/examples/upload`,
  159. {
  160. filePath: tempFilePath,
  161. bucketName,
  162. objectName,
  163. }
  164. );
  165. console.log("上传成功:", response.data);
  166. return response?.data?.url;
  167. } catch (error) {
  168. console.error("生成折线图失败:", error);
  169. throw error;
  170. } finally {
  171. await browser.close();
  172. }
  173. } catch (error) {
  174. console.error("生成折线图失败:", error);
  175. throw error;
  176. }
  177. };