main.js 10.0 KB

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