YewErrorBarChart.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import puppeteer from "puppeteer";
  2. import fs from "fs-extra";
  3. import path from "path";
  4. import FormData from "form-data";
  5. import axios from "axios"; // 导入 axios
  6. import { colorSchemes } from "../colors.js";
  7. export const getYewErrorBarChart = async (
  8. data,
  9. bucketName,
  10. objectName,
  11. analysisTypeCode
  12. ) => {
  13. try {
  14. // 提取机组编号和偏航误差值
  15. const xData = data.map((item) => item.engine_name); // 机组编号
  16. const yData = data.map((item) => item.yaw_error1); // 偏航误差值
  17. // 为每个数据点分配颜色
  18. const colors = yData.map((value) => {
  19. if (value <= 3) {
  20. return "#8AC8BE"; // (0, 3] 蓝色
  21. } else if (value <= 5) {
  22. return "#407DB3"; // (3, 5] 绿色
  23. } else {
  24. return "#1B2973"; // (5, ∞] 红色
  25. }
  26. });
  27. const trace = {
  28. x: xData, // 横坐标数据
  29. y: yData, // 纵坐标数据
  30. type: "bar", // 当前图表类型
  31. marker: {
  32. color: colors, // 每个点的颜色
  33. },
  34. name: "偏航误差值", // 图例名称
  35. };
  36. // 创建虚拟的 trace 以便显示图例
  37. const legendTrace1 = {
  38. x: [null],
  39. y: [null],
  40. name: "(0, 3]",
  41. mode: "markers",
  42. marker: { color: "#8AC8BE", size: 10 },
  43. };
  44. const legendTrace2 = {
  45. x: [null],
  46. y: [null],
  47. name: "(3, 5]",
  48. mode: "markers",
  49. marker: { color: "#407DB3", size: 10 },
  50. };
  51. const legendTrace3 = {
  52. x: [null],
  53. y: [null],
  54. name: "(5, ∞]",
  55. mode: "markers",
  56. marker: { color: "#1B2973", size: 10 },
  57. };
  58. const layout = {
  59. title: {
  60. text: "机组静态偏航误差值", // 图表标题
  61. font: {
  62. size: 16, // 设置标题字体大小(默认 16)
  63. weight: "bold",
  64. },
  65. },
  66. xaxis: {
  67. title: {
  68. text: "机组编号",
  69. }, // 横坐标标题
  70. tickmode: "array",
  71. tickvals: xData, // 设置刻度值(机组编号)
  72. ticktext: xData, // 设置刻度文本(机组编号)
  73. gridcolor: "rgb(255,255,255)",
  74. tickcolor: "rgb(255,255,255)",
  75. backgroundcolor: "#e5ecf6",
  76. },
  77. yaxis: {
  78. title: {
  79. text: "静态偏航误差值(度)",
  80. }, // 纵坐标标题
  81. gridcolor: "rgb(255,255,255)",
  82. tickcolor: "rgb(255,255,255)",
  83. backgroundcolor: "#e5ecf6",
  84. },
  85. margin: {
  86. l: 50,
  87. r: 50,
  88. t: 50,
  89. b: 50,
  90. },
  91. plot_bgcolor: "#e5ecf6",
  92. gridcolor: "#fff",
  93. bgcolor: "#e5ecf6", // 设置背景颜色
  94. autosize: true, // 开启自适应
  95. showlegend: true, // 显示图例
  96. };
  97. // 创建临时目录
  98. const tempDir = path.join(process.cwd(), "images");
  99. await fs.ensureDir(tempDir);
  100. const tempFilePath = path.join(
  101. tempDir,
  102. `temp_yew_error_bar_chart_${Date.now()}.png`
  103. );
  104. // 获取 plotly.js 的绝对路径
  105. const plotlyPath = path.join(
  106. process.cwd(),
  107. "src",
  108. "public",
  109. "js",
  110. "plotly-3.0.1.min.js"
  111. );
  112. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  113. // 使用 Puppeteer 生成图表的截图
  114. const browser = await puppeteer.launch({
  115. headless: "new",
  116. args: ["--no-sandbox", "--disable-setuid-sandbox"],
  117. });
  118. try {
  119. const page = await browser.newPage();
  120. const traces = [trace, legendTrace1, legendTrace2, legendTrace3];
  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. traces
  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: "png" });
  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. } finally {
  170. await browser.close();
  171. }
  172. } catch (error) {
  173. console.error("发生错误:", error);
  174. }
  175. };