main.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. import {app, BrowserWindow, ipcMain, dialog} from 'electron';
  2. import {exec, spawn} from 'child_process';
  3. import path from 'path';
  4. import {fileURLToPath} from 'url';
  5. import fs from 'fs';
  6. import axios from 'axios';
  7. import {resolve} from "node:dns";
  8. import {execSync} from "node:child_process";
  9. import * as url from "node:url";
  10. const __filename = fileURLToPath(import.meta.url);
  11. const __dirname = path.dirname(__filename);
  12. function createWindow() {
  13. const win = new BrowserWindow({
  14. width: 800,
  15. height: 600,
  16. webPreferences: {
  17. preload: path.join(__dirname, 'preload.js'), // 确保路径正确
  18. contextIsolation: true,
  19. enableRemoteModule: false,
  20. nodeIntegration: false,
  21. }
  22. });
  23. win.webContents.openDevTools(); // 打开开发者工具进行调试
  24. win.loadURL('http://localhost:5173'); // 开发环境
  25. // win.loadURL('http://36.110.106.156:81'); // 生产环境
  26. console.log('Window created and URL loaded');
  27. }
  28. app.whenReady().then(createWindow);
  29. app.on('window-all-closed', () => {
  30. if (process.platform !== 'darwin') {
  31. app.quit();
  32. }
  33. });
  34. // app.on('ready', () => {
  35. // // 异步
  36. //
  37. // })
  38. app.on('activate', () => {
  39. if (BrowserWindow.getAllWindows().length === 0) {
  40. createWindow();
  41. }
  42. });
  43. // ------------- 进程通信 -------------
  44. ipcMain.handle('dialog:open', async (event, options = {}) => {
  45. const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0], options);
  46. return result.canceled ? null : result.filePaths;
  47. });
  48. // 下载文件
  49. ipcMain.handle('download-file', async (event, { url, fileName, savePath, overwriteFlag }) => {
  50. try {
  51. // console.log("url", url)
  52. // console.log("response", response)
  53. const filePath = path.join(savePath, fileName);
  54. // 查找文件是否存在
  55. const fileExists = fs.existsSync(filePath)
  56. if (fileExists && !overwriteFlag) { // 已存在且不覆盖
  57. return {success: true, filePath}
  58. } else { // 不存在
  59. const response = await axios.get(url, {responseType: 'stream'});
  60. // 写入文件
  61. const writer = fs.createWriteStream(filePath);
  62. return new Promise((resolve, reject) => {
  63. response.data.pipe(writer);
  64. let error = null;
  65. writer.on('error', err => {
  66. error = err;
  67. writer.close();
  68. reject(err);
  69. });
  70. writer.on('close', () => {
  71. if (!error) {
  72. resolve({success: true, filePath}); // 返回成功信息
  73. } else {
  74. reject({success: false, error: error.message}); // 返回错误信息
  75. }
  76. });
  77. });
  78. }
  79. } catch (error) {
  80. // console.error('下载文件出错:', error);
  81. throw error;
  82. }
  83. });
  84. // 删除文件
  85. ipcMain.on('delete-file', (event, {fileName, savePath}) => {
  86. const filePath = path.join(savePath, fileName);
  87. // 检查文件是否存在
  88. if (fs.existsSync(filePath)) {
  89. // 文件存在,尝试删除
  90. fs.unlink(filePath, (err) => {
  91. if (err) {
  92. event.reply('delete-file-response', { success: false, message: err.message });
  93. } else {
  94. event.reply('delete-file-response', { success: true, message: 'File deleted successfully' });
  95. }
  96. });
  97. } else {
  98. // 文件不存在
  99. event.reply('delete-file-response', { success: false, message: 'File does not exist' });
  100. }
  101. });
  102. // 上传文件
  103. ipcMain.on('upload-file', async (event, {filePath, uploadUrl}) => {
  104. if (!fs.existsSync(filePath)) {
  105. console.log("File does not exist");
  106. event.reply('upload-file-response', {success: false, message: 'File does not exist'});
  107. return;
  108. }
  109. // 读取文件
  110. // fs.readFile(filePath, async (err, data) => {
  111. // if(err) {
  112. // console.log("Error reading file:", err);
  113. // event.reply('upload-file-response', { success: false, message: err.message });
  114. // return;
  115. // }
  116. //
  117. // // 创建formData对象
  118. // const formData = new FormData();
  119. // formData.append('file', new Blob([data]), path.basename(filePath));
  120. //
  121. // try {
  122. // // 使用axios上传文件
  123. // const response = await axios.post(uploadUrl, formData, {
  124. // headers: {
  125. // 'Content-Type': 'multipart/form-data'
  126. // }
  127. // })
  128. // console.log('response', response)
  129. // event.reply('upload-file-response', { success: true, message: 'File uploaded successfully' });
  130. // } catch (err) {
  131. // console.log("Error uploading file:", err);
  132. // event.reply('upload-file-response', { success: false, message: err.message });
  133. // }
  134. // })
  135. try {
  136. const result = await uploadFile(filePath, uploadUrl);
  137. console.log("result", result);
  138. if (result.status) {
  139. event.reply('upload-file-response', { success: true, message: 'File uploaded successfully', details: result.details });
  140. } else {
  141. event.reply('upload-file-response', { success: false, message: 'Error uploading file', details: '' });
  142. }
  143. } catch (err) {
  144. event.reply('upload-file-response', { success: false, message: err.message, details: '' });
  145. }
  146. });
  147. async function uploadFile(filePath, uploadUrl) {
  148. const data = fs.readFileSync(filePath)
  149. // 创建formData对象
  150. const formData = new FormData();
  151. formData.append('file', new Blob([data]), path.basename(filePath));
  152. try {
  153. // 使用axios上传文件
  154. const response = await axios.post(uploadUrl, formData, {
  155. headers: {
  156. 'Content-Type': 'multipart/form-data'
  157. }
  158. })
  159. // console.log('response', response.data)
  160. return response.data
  161. } catch (err) {
  162. console.log("Error uploading file:", err);
  163. return { status: false, code: '', message: '上传stl文件失败', details: '' };
  164. }
  165. }
  166. // 导入算法镜像
  167. ipcMain.on('docker-import', (event, filePath, tag) => {
  168. const command = 'bash /home/cicv/work/pji_desktop/docker_import/run_docker_import.sh ' + filePath + ' pji_nav ' + tag
  169. console.log('Docker import command:', command);
  170. exec(command, (error, stdout, stderr) => {
  171. if (error) {
  172. console.error(`exec error: ${error}`);
  173. event.reply('docker-import-response', { success: false, message: error.message });
  174. return;
  175. } else {
  176. console.log(`stdout: ${stdout}`);
  177. console.error(`stderr: ${stderr}`);
  178. event.reply('docker-import-response', { success: true, message: 'Docker image imported successfully' });
  179. }
  180. });
  181. });
  182. // 生成world
  183. ipcMain.on('generate-world', (event, {rosbag_path}) => {
  184. const command = 'bash /home/cicv/work/pji_desktop/simulation/generate_world.sh ' + rosbag_path
  185. console.log('World generation command:', command);
  186. exec(command, (error, stdout, stderr) => {
  187. if (error) {
  188. console.error(`exec error: ${error}`);
  189. event.reply('generate-world-response', { success: false, message: error.message });
  190. } else {
  191. console.log(`stdout: ${stdout}`);
  192. console.error(`stderr: ${stderr}`);
  193. event.reply('generate-world-response', { success: true, message: 'World generated successfully' });
  194. }
  195. });
  196. });
  197. // 启动仿真环境
  198. ipcMain.on('start-container', (event, {zip_file_path, image_name, container_name}) => {
  199. // 使用 spawn 启动脚本
  200. const serviceProcess = spawn('bash', ["/home/cicv/work/pji_desktop/simulation/data_preparation.sh", zip_file_path], { detached: true });
  201. // 设置为后台进程
  202. serviceProcess.unref();
  203. // 监听输出
  204. serviceProcess.stdout.on('data', (data) => {
  205. console.log(`第一个脚本的输出: ${data}`);
  206. // 根据第一个脚本的输出判断其是否准备好
  207. if (data.toString().includes('Data preparation done')) {
  208. startSecondScript();
  209. }
  210. });
  211. // 监听错误
  212. serviceProcess.stderr.on('data', (data) => {
  213. console.error(`执行第一个脚本时出错: ${data}`);
  214. });
  215. // 监听关闭
  216. serviceProcess.on('close', (data) => {
  217. console.log(`第一个脚本已关闭: ${data}`);
  218. });
  219. function startSecondScript() {
  220. let command = "bash /home/cicv/work/pji_desktop/simulation/start_container.sh " + image_name + " " + container_name
  221. // 启动第二个脚本
  222. const script2 = exec(command, (error, stdout, stderr) => {
  223. if (error) {
  224. console.error(`执行第二个脚本时出错: ${error}`);
  225. event.sender.send('start-container-response', { success: false, output: error });
  226. return;
  227. }
  228. console.log(`第二个脚本的输出: ${stdout}`);
  229. event.sender.send('start-container-response', { success: true, output: stdout });
  230. });
  231. script2.on('exit', (code) => {
  232. console.log(`第二个脚本已退出,退出码: ${code}`);
  233. });
  234. }
  235. });
  236. // 执行仿真
  237. ipcMain.on('run-simulation', (event, {random_flag, count, obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
  238. const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + random_flag + ' ' + count + ' ' + obstacle_flag + ' ' +
  239. default_start_flag + ' ' + default_end_flag + ' ' + start_point + ' ' + end_point;
  240. console.log('command:', command);
  241. exec(command, (error, stdout, stderr) => {
  242. if (error) {
  243. console.error(`exec error: ${error}`);
  244. event.reply('run-simulation-response', { success: false, message: error.message });
  245. } else {
  246. console.log(`stdout: ${stdout}`);
  247. console.error(`stderr: ${stderr}`);
  248. if (stdout.toString().includes('Evaluation finished')) { // 判断结束标志
  249. event.reply('run-simulation-response', { success: true, message: 'Run simulation successfully' });
  250. } else {
  251. event.reply('run-simulation-response', { success: false, message: 'Error running simulation' });
  252. }
  253. }
  254. });
  255. });
  256. // 读取并上传算法评价结果
  257. ipcMain.handle('process-evaluation-files', async (event, {N, equipmentNo, sceneNo}) => {
  258. const result = []
  259. const evalDir = "/home/cicv/work/pji_desktop/simulation/data/evaluation";
  260. const bagDir = "/home/cicv/work/pji_desktop/simulation/data/record_bag";
  261. console.log('N', N)
  262. // 记录时间戳
  263. const timeStamp = Date.now()
  264. console.log("timeStamp", timeStamp);
  265. // 遍历评价结果文件夹,读取并上传相关数据
  266. for (let i = 1; i <= N; i++) {
  267. const subDir = path.join(evalDir, `${i}/result`);
  268. console.log("subDir", subDir);
  269. // 文件路径 - report.json
  270. const jsonFilePath = path.join(subDir, "report.json");
  271. console.log("jsonFilePath", jsonFilePath);
  272. // 文件路径 - report.pdf
  273. const pdfFilePath = path.join(subDir, "report.pdf");
  274. console.log("pdfFilePath", pdfFilePath);
  275. // 文件路径 - test-${i}.bag
  276. const bagFilePath = path.join(bagDir, `test-${i}.bag`);
  277. console.log("bagFilePath", bagFilePath);
  278. const uploadPdfUrl = "http://127.0.0.1:8888/simulation/upload/pdf?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo +
  279. "&timeStamp=" + timeStamp + "&round=" + i;
  280. const uploadBagUrl = "http://127.0.0.1:8888/simulation/upload/bag?equipmentNo=" + equipmentNo + "&sceneNo=" + sceneNo +
  281. "&timeStamp=" + timeStamp + "&round=" + i;
  282. try {
  283. // 读取json文件
  284. const jsonData = fs.readFileSync(jsonFilePath, "utf-8");
  285. const parsedData = JSON.parse(jsonData);
  286. console.log("parsedData", parsedData);
  287. // 上传pdf文件
  288. const uploadPdfResult = await uploadFile(pdfFilePath, uploadPdfUrl);
  289. console.log("uploadPdfResult", uploadPdfResult);
  290. if (!uploadPdfResult.status) { break}
  291. // 上传bag文件
  292. const uploadBagResult = await uploadFile(bagFilePath, uploadBagUrl);
  293. console.log("uploadBagUrl", uploadBagUrl);
  294. if (!uploadBagResult.status) { break}
  295. // 整合结果
  296. result.push({
  297. "round": i,
  298. "jsonData": parsedData,
  299. "uploadPdfKey": uploadPdfResult.details,
  300. "uploadBagKey": uploadBagResult.details,
  301. "timeStamp": timeStamp
  302. })
  303. } catch (error) {
  304. console.log("error", error);
  305. return { success: false, message: "Error uploading evaluation results", data: [] }
  306. }
  307. }
  308. // 判断数据上传是否完整
  309. if (result.length === N) {
  310. return { success: true, message: "Evaluation results uploaded successfully", data: result }
  311. } else {
  312. return { success: false, message: "Error uploading evaluation results", data: [] }
  313. }
  314. })