Time3DChart.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * @Author: your name
  3. * @Date: 2025-04-14 17:49:33
  4. * @LastEditTime: 2025-05-21 15:11:06
  5. * @LastEditors: bogon
  6. * @Description: In User Settings Edit
  7. * @FilePath: /performance-test/downLoadServer/src/server/utils/chartsCom/Time3DChart.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. // 格式化日期为 YY-MM 格式
  16. const formatDate = (dateString) => {
  17. const date = new Date(dateString);
  18. const year = date.getFullYear(); // 获取年份后两位
  19. const month = ("0" + (date.getMonth() + 1)).slice(-2); // 获取月份并确保两位数
  20. return `${year}-${month}`;
  21. };
  22. export const generateTime3DChart = async (data, bucketName, objectName) => {
  23. try {
  24. // 创建临时目录
  25. const tempDir = path.join(process.cwd(), "images");
  26. await fs.ensureDir(tempDir);
  27. const tempFilePath = path.join(
  28. tempDir,
  29. `temp_heatmap_chart_${Date.now()}.jpeg`
  30. );
  31. // 获取 plotly.js 的绝对路径
  32. const plotlyPath = path.join(
  33. process.cwd(),
  34. "src",
  35. "public",
  36. "js",
  37. "plotly-3.0.1.min.js"
  38. );
  39. const plotlyContent = await fs.readFile(plotlyPath, "utf-8");
  40. // 创建浏览器实例
  41. const browser = await puppeteer.launch({
  42. headless: "new",
  43. args: [
  44. "--no-sandbox",
  45. "--disable-setuid-sandbox",
  46. "--window-size=1920,1080",
  47. ],
  48. });
  49. try {
  50. const page = await browser.newPage();
  51. // 准备图表数据
  52. const uniqueMonths = Array.from(
  53. new Set(data.data[0].yData.map((date) => formatDate(date)))
  54. );
  55. // 设置每个月份对应的颜色
  56. const monthColors = colorSchemes[0].colors;
  57. const traces = uniqueMonths.map((month, monthIndex) => {
  58. const monthData = data.data[0].yData
  59. .map((date, index) => (formatDate(date) === month ? index : -1))
  60. .filter((index) => index !== -1);
  61. return {
  62. x: monthData.map((index) => data.data[0].xData[index]), // 发电机转速
  63. y: monthData.map((index) => data.data[0].yData[index]), // 时间
  64. z: monthData.map((index) => data.data[0].zData[index]), // 有功功率
  65. mode: "markers", // 根据需要设置模式
  66. type: "scatter3d",
  67. marker: {
  68. size: 1, // 使用动态点大小
  69. color: monthColors[monthIndex], // 使用配色方案
  70. colorscale: "YlGnBu",
  71. },
  72. name: ` ${month}`,
  73. };
  74. });
  75. // 准备布局配置
  76. const layout = {
  77. title: {
  78. text: data.data[0].title,
  79. font: {
  80. size: 16,
  81. weight: "bold",
  82. },
  83. },
  84. scene: {
  85. xaxis: {
  86. gridcolor: "#fff",
  87. backgroundcolor: "#e0e7f1",
  88. showbackground: true,
  89. linecolor: "black",
  90. ticks: "outside",
  91. ticklen: 10,
  92. tickcolor: "black",
  93. zeroline: false,
  94. tickangle: -10,
  95. title: {
  96. text: data.xaixs,
  97. standoff: 100,
  98. },
  99. },
  100. yaxis: {
  101. type: "category", // 让 Y 轴按类别均匀分布
  102. categoryorder: "category ascending", // 按类别字母顺序排列
  103. type: "date",
  104. tickformat: "%Y-%m",
  105. dtick: "M3",
  106. gridcolor: "#fff",
  107. tickcolor: "#e5ecf6",
  108. backgroundcolor: "#e0e7f1",
  109. showbackground: true,
  110. linecolor: "black",
  111. ticks: "outside",
  112. tickcolor: "black",
  113. zeroline: false,
  114. tickangle: 25,
  115. title: {
  116. text: data.yaixs,
  117. },
  118. },
  119. zaxis: {
  120. gridcolor: "#fff",
  121. tickcolor: "#fff",
  122. backgroundcolor: "#e0e7f1",
  123. showbackground: true,
  124. linecolor: "black",
  125. ticks: "outside",
  126. tickcolor: "black",
  127. zeroline: false,
  128. tickangle: -90,
  129. title: {
  130. text: data.zaixs,
  131. },
  132. },
  133. plot_bgcolor: "#e5ecf6",
  134. gridcolor: "#fff",
  135. bgcolor: "#e5ecf6", // 设置背景颜色
  136. aspectmode: "manual",
  137. aspectratio: {
  138. x: 2.0000000000000018,
  139. y: 1.5454545454545465,
  140. z: 0.9090909090909098,
  141. },
  142. camera: {
  143. up: {
  144. x: 0.19380723218588866,
  145. y: 0.2540224158731985,
  146. z: 0.9475818534492884,
  147. },
  148. center: {
  149. x: -0.052807476121180814,
  150. y: 0.02451796399554085,
  151. z: -0.022911006648570736,
  152. },
  153. eye: {
  154. x: -2.126389777109588,
  155. y: -2.5514260394238466,
  156. z: 1.091739681861482,
  157. },
  158. projection: {
  159. type: "orthographic",
  160. },
  161. },
  162. },
  163. margin: { t: 50, b: 50, l: 50, r: 50 },
  164. staticPlot: false,
  165. showlegend: true,
  166. legend: {
  167. marker: {
  168. size: 10, // 图例中点的大小
  169. },
  170. },
  171. };
  172. // 准备 HTML 内容
  173. const htmlContent = `
  174. <!DOCTYPE html>
  175. <html>
  176. <head>
  177. <meta charset="UTF-8">
  178. <title>3D图</title>
  179. <script>${plotlyContent}</script>
  180. </head>
  181. <body>
  182. <div id="chart" style="width: 100%; height: 500px"></div>
  183. <script>
  184. const traces = ${JSON.stringify(traces)};
  185. const layout = ${JSON.stringify(layout)};
  186. Plotly.newPlot('chart', traces, layout, { responsive: true }).then(() => {
  187. window.chartRendered = true; // 确保在图表渲染完成后设置
  188. console.log("图表渲染完成");
  189. }).catch((error) => {
  190. console.error("图表渲染错误:", error); // 捕获渲染错误
  191. });
  192. </script>
  193. </body>
  194. </html>
  195. `;
  196. // ... existing code ...
  197. // 设置页面内容
  198. await page.setContent(htmlContent, {
  199. waitUntil: "networkidle0",
  200. });
  201. // 等待图表渲染完成,延长超时时间
  202. await page.waitForFunction(() => window.chartRendered === true, {
  203. timeout: 150000, // 延长到 120 秒
  204. });
  205. // 截图并保存到临时文件
  206. const chartElement = await page.$("#chart");
  207. await chartElement.screenshot({
  208. path: tempFilePath,
  209. type: "jpeg",
  210. });
  211. // 上传图片到服务器
  212. const formData = new FormData();
  213. formData.append("file", fs.createReadStream(tempFilePath));
  214. // return formData;
  215. // 发送上传请求
  216. const response = await axios.post(
  217. `${process.env.API_BASE_URL}/examples/upload`,
  218. { filePath: tempFilePath, bucketName, objectName }
  219. );
  220. return response?.data?.url;
  221. } catch (error) {
  222. console.error("生成3D图失败:", error);
  223. throw error;
  224. } finally {
  225. await browser.close();
  226. }
  227. } catch (error) {
  228. console.error("生成3D图失败:", error);
  229. throw error;
  230. }
  231. };