FaultAll.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  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 getFaultAllCharts = async (
  8. data,
  9. bucketName,
  10. objectName,
  11. analysisTypeCode
  12. ) => {
  13. // 获取 plotly.js 的绝对路径
  14. const plotlyPath = path.join(
  15. process.cwd(),
  16. "src",
  17. "public",
  18. "js",
  19. "plotly-3.0.1.min.js"
  20. );
  21. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  22. // console.log(data, "全场故障数据");
  23. try {
  24. // 提取故障类型、故障次数和故障时长
  25. const faultTypes = data.map((item) => item.fault_detail);
  26. const faultCounts = data.map((item) => item.count);
  27. const faultDurations = data.map((item) => item.fault_time_sum);
  28. // 故障次数的柱状图数据(左侧 Y 轴)
  29. const barTrace = {
  30. x: faultTypes.slice(0, 10),
  31. y: faultCounts.slice(0, 10),
  32. type: "bar",
  33. marker: { color: "#64ADC2" }, // 蓝色柱状图
  34. name: "故障次数",
  35. hovertemplate: `故障类型: %{x} <br> 故障次数: %{y} 次<br>`,
  36. };
  37. // 故障时长的折线图数据(右侧 Y 轴)
  38. const lineTrace = {
  39. x: faultTypes.slice(0, 10),
  40. y: faultDurations.slice(0, 10),
  41. type: "scatter",
  42. mode: "lines+markers", // 线性图 + 点标记
  43. line: { color: "#1A295D" }, // 红色折线
  44. name: "故障时长",
  45. yaxis: "y2", // 使用第二个 Y 轴(右侧)
  46. hovertemplate: `故障类型: %{x} <br> 故障时长: %{y} 小时 <br>`,
  47. };
  48. // 布局配置,设置双 Y 轴
  49. const layout = {
  50. title: {
  51. text: "全场故障次数与时长分析Top10",
  52. font: {
  53. size: 16, // 设置标题字体大小(默认 16)
  54. weight: "bold",
  55. },
  56. },
  57. xaxis: {
  58. title: {
  59. text: "故障类型",
  60. },
  61. tickangle: 30,
  62. tickmode: "array",
  63. tickvals: faultTypes.slice(0, 10),
  64. tickfont: { size: 12 },
  65. gridcolor: "rgb(255,255,255)",
  66. tickcolor: "rgb(255,255,255)",
  67. backgroundcolor: "#e5ecf6",
  68. },
  69. yaxis: {
  70. title: {
  71. text: "故障次数",
  72. },
  73. titlefont: { color: "#64ADC2" },
  74. tickfont: { color: "#64ADC2" },
  75. side: "left", // 左侧的 Y 轴
  76. showline: true,
  77. linecolor: "#64ADC2",
  78. gridcolor: "rgb(255,255,255)",
  79. tickcolor: "rgb(255,255,255)",
  80. backgroundcolor: "#e5ecf6",
  81. },
  82. yaxis2: {
  83. title: {
  84. text: "故障时长 (小时)",
  85. },
  86. titlefont: { color: "#1A295D" },
  87. tickfont: { color: "#1A295D" },
  88. overlaying: "y", // 在第一个 Y 轴上方绘制
  89. side: "right", // 右侧的 Y 轴
  90. position: 1, // 调整右侧轴的位置
  91. showline: true,
  92. linecolor: "#1A295D", // 设置右侧轴线颜色
  93. },
  94. barmode: "group", // 柱状图分组
  95. plot_bgcolor: "#e5ecf6",
  96. gridcolor: "#fff",
  97. bgcolor: "#e5ecf6", // 设置背景颜色
  98. showlegend: false,
  99. margin: {
  100. t: 80, // 上边距
  101. b: 150, // 下边距,给 X 轴标签更多空间
  102. },
  103. };
  104. // 创建临时目录
  105. const tempDir = path.join(process.cwd(), "images");
  106. await fs.ensureDir(tempDir);
  107. const tempFilePath = path.join(
  108. tempDir,
  109. `temp_fault_all_chart_${Date.now()}.jpeg`
  110. );
  111. // 使用 Puppeteer 生成图表的截图
  112. const browser = await puppeteer.launch({
  113. headless: "new",
  114. // 根据系统改路径
  115. executablePath: `${process.env.CHROME_PATH}`, // 根据系统改路径
  116. args: ["--no-sandbox", "--disable-setuid-sandbox"],
  117. });
  118. try {
  119. const page = await browser.newPage();
  120. const htmlContent = `
  121. <!DOCTYPE html>
  122. <html>
  123. <head>
  124. <meta charset="UTF-8">
  125. <title>全场故障</title>
  126. <script>${plotlyContent}</script>
  127. <style>
  128. body { margin: 0; }
  129. #chart { width: 800px; height: 600px; }
  130. </style>
  131. </head>
  132. <body>
  133. <div id="chart"></div>
  134. <script>
  135. window.onload = function() {
  136. Plotly.newPlot('chart', [${JSON.stringify(
  137. barTrace
  138. )}, ${JSON.stringify(lineTrace)}], ${JSON.stringify(
  139. layout
  140. )}).then(() => {
  141. window.chartRendered = true;
  142. });
  143. };
  144. </script>
  145. </body>
  146. </html>
  147. `;
  148. await page.setContent(htmlContent, { waitUntil: "networkidle0" });
  149. await page.waitForFunction(() => window.chartRendered === true, {
  150. timeout: 60000,
  151. });
  152. // 截图并保存到临时文件
  153. const chartElement = await page.$("#chart");
  154. await chartElement.screenshot({ path: tempFilePath, type: "jpeg" });
  155. // 上传图片到服务器
  156. const formData = new FormData();
  157. formData.append("file", fs.createReadStream(tempFilePath));
  158. const response = await axios.post(
  159. `${process.env.API_BASE_URL}/examples/upload`,
  160. {
  161. filePath: tempFilePath,
  162. bucketName,
  163. objectName,
  164. }
  165. );
  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. };