main.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. ipcMain.on('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
  37. const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
  38. ' ' + start_point + ' ' + end_point;
  39. console.log('command:', command);
  40. exec(command, (error, stdout, stderr) => {
  41. if (error) {
  42. console.error(`exec error: ${error}`);
  43. event.reply('run-simulation-response', { success: false, message: error.message });
  44. return;
  45. } else {
  46. console.log(`stdout: ${stdout}`);
  47. console.error(`stderr: ${stderr}`);
  48. event.reply('run-simulation-response', { success: true, message: 'Run simulation successfully' });
  49. }
  50. });
  51. });
  52. })
  53. app.on('activate', () => {
  54. if (BrowserWindow.getAllWindows().length === 0) {
  55. createWindow();
  56. }
  57. });
  58. // ------------- 进程通信 -------------
  59. ipcMain.on('open-gazebo', (event, arg) => {
  60. console.log('Received open-gazebo event');
  61. exec('gazebo', (error, stdout, stderr) => {
  62. if (error) {
  63. console.error(`exec error: ${error}`);
  64. return;
  65. }
  66. console.log(`stdout: ${stdout}`);
  67. console.error(`stderr: ${stderr}`);
  68. });
  69. });
  70. ipcMain.on('open-rviz', (event, arg) => {
  71. console.log('Received open-rviz event');
  72. exec('rviz', (error, stdout, stderr) => {
  73. if (error) {
  74. console.error(`exec error: ${error}`);
  75. return;
  76. }
  77. console.log(`stdout: ${stdout}`);
  78. console.error(`stderr: ${stderr}`);
  79. });
  80. });
  81. ipcMain.on('close-gazebo', (event, arg) => {
  82. console.log('Received close-gazebo event');
  83. exec('pkill -f gzserver & pkill -f gzclient', (error, stdout, stderr) => {
  84. if (error) {
  85. console.error(`exec error: ${error}`);
  86. return;
  87. }
  88. console.log(`stdout: ${stdout}`);
  89. console.error(`stderr: ${stderr}`);
  90. });
  91. });
  92. ipcMain.on('close-rviz', (event, arg) => {
  93. console.log('Received close-rviz event');
  94. exec('pkill -f rviz', (error, stdout, stderr) => {
  95. if (error) {
  96. console.error(`exec error: ${error}`);
  97. return;
  98. }
  99. console.log(`stdout: ${stdout}`);
  100. console.error(`stderr: ${stderr}`);
  101. });
  102. });
  103. ipcMain.handle('dialog:open', async (event, options = {}) => {
  104. const result = await dialog.showOpenDialog(BrowserWindow.getFocusedWindow() || BrowserWindow.getAllWindows()[0], options);
  105. return result.canceled ? null : result.filePaths;
  106. });
  107. ipcMain.on('docker-import', (event, filePath, tag) => {
  108. const command = 'bash /home/cicv/work/pji_desktop/docker_import/run_docker_import.sh ' + filePath + ' pji_nav ' + tag
  109. console.log('Docker import command:', command);
  110. exec(command, (error, stdout, stderr) => {
  111. if (error) {
  112. console.error(`exec error: ${error}`);
  113. event.reply('docker-import-response', { success: false, message: error.message });
  114. return;
  115. } else {
  116. console.log(`stdout: ${stdout}`);
  117. console.error(`stderr: ${stderr}`);
  118. event.reply('docker-import-response', { success: true, message: 'Docker image imported successfully' });
  119. }
  120. });
  121. });
  122. ipcMain.on('generate-world', (event, {rosbag_path}) => {
  123. const command = 'bash /home/cicv/work/pji_desktop/simulation/generate_world.sh ' + rosbag_path
  124. console.log('World generation command:', command);
  125. exec(command, (error, stdout, stderr) => {
  126. if (error) {
  127. console.error(`exec error: ${error}`);
  128. event.reply('generate-world-response', { success: false, message: error.message });
  129. } else {
  130. console.log(`stdout: ${stdout}`);
  131. console.error(`stderr: ${stderr}`);
  132. event.reply('generate-world-response', { success: true, message: 'World generated successfully' });
  133. }
  134. });
  135. });
  136. ipcMain.on('start-container', (event, {zip_file_path, image_name, container_name}) => {
  137. // 使用 spawn 启动脚本
  138. const serviceProcess = spawn('bash', ["/home/cicv/work/pji_desktop/simulation/data_preparation.sh", zip_file_path], { detached: true });
  139. // 设置为后台进程
  140. serviceProcess.unref();
  141. // 监听输出
  142. serviceProcess.stdout.on('data', (data) => {
  143. console.log(`第一个脚本的输出: ${data}`);
  144. // 根据第一个脚本的输出判断其是否准备好
  145. if (data.toString().includes('Data preparation done')) {
  146. startSecondScript();
  147. }
  148. });
  149. // 监听错误
  150. serviceProcess.stderr.on('data', (data) => {
  151. console.error(`执行第一个脚本时出错: ${data}`);
  152. });
  153. // 监听关闭
  154. serviceProcess.on('close', (data) => {
  155. console.log(`第一个脚本已关闭: ${data}`);
  156. });
  157. function startSecondScript() {
  158. let command = "bash /home/cicv/work/pji_desktop/simulation/start_container.sh " + image_name + " " + container_name
  159. // 启动第二个脚本
  160. const script2 = exec(command, (error, stdout, stderr) => {
  161. if (error) {
  162. console.error(`执行第二个脚本时出错: ${error}`);
  163. event.sender.send('start-container-response', { success: false, output: error });
  164. return;
  165. }
  166. console.log(`第二个脚本的输出: ${stdout}`);
  167. event.sender.send('start-container-response', { success: true, output: stdout });
  168. });
  169. script2.on('exit', (code) => {
  170. console.log(`第二个脚本已退出,退出码: ${code}`);
  171. });
  172. }
  173. });
  174. // 同步
  175. // ipcMain.handle('run-simulation', (event, {obstacle_flag, default_start_flag, default_end_flag, start_point, end_point}) => {
  176. // const command = 'bash /home/cicv/work/pji_desktop/simulation/run_simulation.sh ' + obstacle_flag + ' ' + default_start_flag + ' ' + default_end_flag +
  177. // ' ' + start_point + ' ' + end_point;
  178. // console.log('command:', command);
  179. //
  180. // // 同步
  181. // try {
  182. // const output = execSync(command)
  183. // console.log("output", output.toString());
  184. // return { success: false, message: error.message }
  185. // } catch (error) {
  186. // console.error(`exec error: ${error}`);
  187. // return { success: true, message: 'Run simulation successfully' }
  188. // }
  189. // });
  190. ipcMain.handle('download-file', async (event, { url, fileName, savePath, overwriteFlag }) => {
  191. try {
  192. // console.log("url", url)
  193. // console.log("response", response)
  194. const filePath = path.join(savePath, fileName);
  195. // 查找文件是否存在
  196. const fileExists = fs.existsSync(filePath)
  197. if (fileExists && !overwriteFlag) { // 已存在且不覆盖
  198. return {success: true, filePath}
  199. } else { // 不存在
  200. const response = await axios.get(url, {responseType: 'stream'});
  201. // 写入文件
  202. const writer = fs.createWriteStream(filePath);
  203. return new Promise((resolve, reject) => {
  204. response.data.pipe(writer);
  205. let error = null;
  206. writer.on('error', err => {
  207. error = err;
  208. writer.close();
  209. reject(err);
  210. });
  211. writer.on('close', () => {
  212. if (!error) {
  213. resolve({success: true, filePath}); // 返回成功信息
  214. } else {
  215. reject({success: false, error: error.message}); // 返回错误信息
  216. }
  217. });
  218. });
  219. }
  220. } catch (error) {
  221. // console.error('下载文件出错:', error);
  222. throw error;
  223. }
  224. });
  225. // 删除文件
  226. ipcMain.on('delete-file', (event, {fileName, savePath}) => {
  227. const filePath = path.join(savePath, fileName);
  228. // 检查文件是否存在
  229. if (fs.existsSync(filePath)) {
  230. // 文件存在,尝试删除
  231. fs.unlink(filePath, (err) => {
  232. if (err) {
  233. event.reply('delete-file-response', { success: false, message: err.message });
  234. } else {
  235. event.reply('delete-file-response', { success: true, message: 'File deleted successfully' });
  236. }
  237. });
  238. } else {
  239. // 文件不存在
  240. event.reply('delete-file-response', { success: false, message: 'File does not exist' });
  241. }
  242. });
  243. // 上传文件
  244. ipcMain.on('upload-file', (event, {filePath, uploadUrl}) => {
  245. if (!fs.existsSync(filePath)) {
  246. console.log("File does not exist");
  247. event.reply('upload-file-response', { success: false, message: 'File does not exist' });
  248. return;
  249. }
  250. // 读取文件
  251. fs.readFile(filePath, async (err, data) => {
  252. if(err) {
  253. console.log("Error reading file:", err);
  254. event.reply('upload-file-response', { success: false, message: err.message });
  255. return;
  256. }
  257. // 创建formData对象
  258. const formData = new FormData();
  259. formData.append('file', new Blob([data]), path.basename(filePath));
  260. try {
  261. // 使用axios上传文件
  262. const response = await axios.post(uploadUrl, formData, {
  263. headers: {
  264. 'Content-Type': 'multipart/form-data'
  265. }
  266. })
  267. console.log('response', response)
  268. event.reply('upload-file-response', { success: true, message: 'File uploaded successfully' });
  269. } catch (err) {
  270. console.log("Error uploading file:", err);
  271. event.reply('upload-file-response', { success: false, message: err.message });
  272. }
  273. })
  274. });