main.js 14 KB

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