main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. const __filename = fileURLToPath(import.meta.url);
  10. const __dirname = path.dirname(__filename);
  11. function createWindow() {
  12. const win = new BrowserWindow({
  13. width: 800,
  14. height: 600,
  15. webPreferences: {
  16. preload: path.join(__dirname, 'preload.js'), // 确保路径正确
  17. contextIsolation: true,
  18. enableRemoteModule: false,
  19. nodeIntegration: false,
  20. }
  21. });
  22. win.webContents.openDevTools(); // 打开开发者工具进行调试
  23. win.loadURL('http://localhost:5173'); // 开发环境
  24. // win.loadURL('http://36.110.106.156:81'); // 生产环境
  25. console.log('Window created and URL loaded');
  26. }
  27. app.whenReady().then(createWindow);
  28. app.on('window-all-closed', () => {
  29. if (process.platform !== 'darwin') {
  30. app.quit();
  31. }
  32. });
  33. app.on('ready', () => {
  34. // 异步
  35. ipcMain.on('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
  36. const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
  37. ' ' + start_point + ' ' + end_point;
  38. console.log('command:', command);
  39. exec(command, (error, stdout, stderr) => {
  40. if (error) {
  41. console.error(`exec error: ${error}`);
  42. event.reply('run-simulation-response', { success: false, message: error.message });
  43. return;
  44. } else {
  45. console.log(`stdout: ${stdout}`);
  46. console.error(`stderr: ${stderr}`);
  47. event.reply('run-simulation-response', { success: true, message: 'Run simulation successfully' });
  48. }
  49. });
  50. });
  51. })
  52. app.on('activate', () => {
  53. if (BrowserWindow.getAllWindows().length === 0) {
  54. createWindow();
  55. }
  56. });
  57. // ------------- 进程通信 -------------
  58. ipcMain.on('open-gazebo', (event, arg) => {
  59. console.log('Received open-gazebo event');
  60. exec('gazebo', (error, stdout, stderr) => {
  61. if (error) {
  62. console.error(`exec error: ${error}`);
  63. return;
  64. }
  65. console.log(`stdout: ${stdout}`);
  66. console.error(`stderr: ${stderr}`);
  67. });
  68. });
  69. ipcMain.on('open-rviz', (event, arg) => {
  70. console.log('Received open-rviz event');
  71. exec('rviz', (error, stdout, stderr) => {
  72. if (error) {
  73. console.error(`exec error: ${error}`);
  74. return;
  75. }
  76. console.log(`stdout: ${stdout}`);
  77. console.error(`stderr: ${stderr}`);
  78. });
  79. });
  80. ipcMain.on('close-gazebo', (event, arg) => {
  81. console.log('Received close-gazebo event');
  82. exec('pkill -f gzserver & pkill -f gzclient', (error, stdout, stderr) => {
  83. if (error) {
  84. console.error(`exec error: ${error}`);
  85. return;
  86. }
  87. console.log(`stdout: ${stdout}`);
  88. console.error(`stderr: ${stderr}`);
  89. });
  90. });
  91. ipcMain.on('close-rviz', (event, arg) => {
  92. console.log('Received close-rviz event');
  93. exec('pkill -f rviz', (error, stdout, stderr) => {
  94. if (error) {
  95. console.error(`exec error: ${error}`);
  96. return;
  97. }
  98. console.log(`stdout: ${stdout}`);
  99. console.error(`stderr: ${stderr}`);
  100. });
  101. });
  102. ipcMain.handle('dialog:open', async (event, options = {}) => {
  103. const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0], options);
  104. return result.canceled ? null : result.filePaths;
  105. });
  106. ipcMain.on('docker-import', (event, filePath, tag) => {
  107. const command = 'bash /home/cicv/work/pji_desktop/docker_import/run_docker_import.sh ' + filePath + ' pji_nav ' + tag
  108. console.log('导入算法镜像文件:', command);
  109. exec(command, (error, stdout, stderr) => {
  110. if (error) {
  111. console.error(`exec error: ${error}`);
  112. event.reply('docker-import-response', { success: false, message: error.message });
  113. return;
  114. } else {
  115. console.log(`stdout: ${stdout}`);
  116. console.error(`stderr: ${stderr}`);
  117. event.reply('docker-import-response', { success: true, message: 'Docker image imported successfully' });
  118. }
  119. });
  120. });
  121. ipcMain.on('generate-world', (event, {rosbag_path}) => {
  122. // 使用 spawn 启动脚本
  123. const serviceProcess = spawn('bash', ["/home/cicv/work/pji_desktop/simulation/run_map2gazebo.sh"], { detached: true });
  124. // 设置为后台进程
  125. serviceProcess.unref();
  126. // 监听输出
  127. serviceProcess.stdout.on('data', (data) => {
  128. console.log(`第一个脚本的输出: ${data}`);
  129. // 根据第一个脚本的输出判断其是否准备好
  130. if (data.toString().includes('Service map2gazebo started')) {
  131. startSecondScript();
  132. }
  133. });
  134. // 监听错误
  135. serviceProcess.stderr.on('data', (data) => {
  136. console.error(`执行第一个脚本时出错: ${data}`);
  137. });
  138. // 监听关闭
  139. serviceProcess.on('close', (data) => {
  140. console.log(`第一个脚本已关闭: ${data}`);
  141. });
  142. function startSecondScript() {
  143. // 启动第二个脚本
  144. const script2 = exec('bash /home/cicv/work/pji_desktop/simulation/play_rosbag.sh ' + rosbag_path, (error, stdout, stderr) => {
  145. if (error) {
  146. console.error(`执行第二个脚本时出错: ${error}`);
  147. event.sender.send('generate-world-result', { success: false, output: error });
  148. return;
  149. }
  150. console.log(`第二个脚本的输出: ${stdout}`);
  151. event.sender.send('generate-world-result', { success: true, output: stdout });
  152. });
  153. script2.on('exit', (code) => {
  154. console.log(`第二个脚本已退出,退出码: ${code}`);
  155. });
  156. }
  157. });
  158. ipcMain.on('start-container', (event, {zip_file_path, image_name, container_name}) => {
  159. // 使用 spawn 启动脚本
  160. const serviceProcess = spawn('bash', ["/home/cicv/work/pji_desktop/simulation/data_preparation.sh", zip_file_path], { detached: true });
  161. // 设置为后台进程
  162. serviceProcess.unref();
  163. // 监听输出
  164. serviceProcess.stdout.on('data', (data) => {
  165. console.log(`第一个脚本的输出: ${data}`);
  166. // 根据第一个脚本的输出判断其是否准备好
  167. if (data.toString().includes('Data preparation done')) {
  168. startSecondScript();
  169. }
  170. });
  171. // 监听错误
  172. serviceProcess.stderr.on('data', (data) => {
  173. console.error(`执行第一个脚本时出错: ${data}`);
  174. });
  175. // 监听关闭
  176. serviceProcess.on('close', (data) => {
  177. console.log(`第一个脚本已关闭: ${data}`);
  178. });
  179. function startSecondScript() {
  180. let command = "bash /home/cicv/work/pji_desktop/simulation/start_container.sh " + image_name + " " + container_name
  181. // 启动第二个脚本
  182. const script2 = exec(command, (error, stdout, stderr) => {
  183. if (error) {
  184. console.error(`执行第二个脚本时出错: ${error}`);
  185. event.sender.send('generate-world-result', { success: false, output: error });
  186. return;
  187. }
  188. console.log(`第二个脚本的输出: ${stdout}`);
  189. event.sender.send('start-container-result', { success: true, output: stdout });
  190. });
  191. script2.on('exit', (code) => {
  192. console.log(`第二个脚本已退出,退出码: ${code}`);
  193. });
  194. }
  195. });
  196. // 同步
  197. // ipcMain.handle('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
  198. // const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
  199. // ' ' + start_point + ' ' + end_point;
  200. // console.log('command:', command);
  201. //
  202. // // 同步
  203. // try {
  204. // const output = execSync(command)
  205. // console.log("output", output.toString());
  206. // return { success: false, message: error.message }
  207. // } catch (error) {
  208. // console.error(`exec error: ${error}`);
  209. // return { success: true, message: 'Run simulation successfully' }
  210. // }
  211. // });
  212. ipcMain.handle('download-file', async (event, { url, fileName, savePath, overwriteFlag }) => {
  213. try {
  214. // console.log("url", url)
  215. // console.log("response", response)
  216. const filePath = path.join(savePath, fileName);
  217. // 查找文件是否存在
  218. const fileExists = fs.existsSync(filePath)
  219. if (fileExists && !overwriteFlag) { // 已存在且不覆盖
  220. return {success: true, filePath}
  221. } else { // 不存在
  222. const response = await axios.get(url, {responseType: 'stream'});
  223. // 写入文件
  224. const writer = fs.createWriteStream(filePath);
  225. return new Promise((resolve, reject) => {
  226. response.data.pipe(writer);
  227. let error = null;
  228. writer.on('error', err => {
  229. error = err;
  230. writer.close();
  231. reject(err);
  232. });
  233. writer.on('close', () => {
  234. if (!error) {
  235. resolve({success: true, filePath}); // 返回成功信息
  236. } else {
  237. reject({success: false, error: error.message}); // 返回错误信息
  238. }
  239. });
  240. });
  241. }
  242. } catch (error) {
  243. // console.error('下载文件出错:', error);
  244. throw error;
  245. }
  246. });
  247. // 监听从渲染进程传来的删除文件请求
  248. ipcMain.on('delete-file', (event, {fileName, savePath}) => {
  249. const filePath = path.join(savePath, fileName);
  250. // 检查文件是否存在
  251. if (fs.existsSync(filePath)) {
  252. // 文件存在,尝试删除
  253. fs.unlink(filePath, (err) => {
  254. if (err) {
  255. event.reply('delete-file-response', { success: false, message: err.message });
  256. } else {
  257. event.reply('delete-file-response', { success: true, message: 'File deleted successfully' });
  258. }
  259. });
  260. } else {
  261. // 文件不存在
  262. event.reply('delete-file-response', { success: false, message: 'File does not exist' });
  263. }
  264. });