camera.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // electron/utils/readConfig.js
  2. const path = require('path');
  3. const fs = require('fs');
  4. const Log = require('ee-core/log');
  5. const { spawn } = require('child_process');
  6. const { EventEmitter } = require('events');
  7. const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow,minimizeSmartShooter } = require('../api/camera');
  8. const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
  9. const CoreWindow = require("ee-core/electron/window");
  10. const { readConfigFile, writeConfigFile } = require('../utils/config');
  11. // 进程状态管理
  12. let cameraProcess = null; // 子进程引用
  13. let isProcessRunning = false; // 进程运行状态
  14. const processEmitter = new EventEmitter(); // 进程事件发射器
  15. // Smart Shooter 5 / 6 与 digiCamControl 并存。
  16. // 5 和 6 共用同一套 External API(54543 事件 / 54544 请求),不能同时开,按 controlType 只启动其中一个。
  17. const CONTROLLERS = {
  18. digiCamControl: {
  19. exe: "CameraControl.exe",
  20. pathKey: "digiCamControlPath",
  21. label: "digiCamControl",
  22. },
  23. SmartShooter: {
  24. exe: "SmartShooter5.exe",
  25. pathKey: "SmartShooterPath",
  26. label: "Smart Shooter 5",
  27. },
  28. SmartShooter6: {
  29. exe: "SmartShooter6.exe",
  30. pathKey: "SmartShooter6Path",
  31. label: "Smart Shooter 6",
  32. },
  33. }
  34. // 历史配置 controlType 仍是 SmartShooter,保持原行为
  35. function normalizeControlType(type) {
  36. return CONTROLLERS[type] ? type : "SmartShooter"
  37. }
  38. function isSmartShooterType(type) {
  39. return type === "SmartShooter" || type === "SmartShooter6"
  40. }
  41. function getController() {
  42. const config = readConfigFile() || {}
  43. const type = normalizeControlType(config.controlType)
  44. const spec = CONTROLLERS[type]
  45. return {
  46. type,
  47. ...spec,
  48. dir: config.controlPath || config[spec.pathKey] || "",
  49. }
  50. }
  51. function getExePath () {
  52. const controller = getController()
  53. const exePath = controller.dir ? path.join(controller.dir, controller.exe) : ""
  54. console.log('ex============ePath');
  55. console.log(exePath);
  56. return exePath
  57. }
  58. // 用户手动选安装目录时,按目录里实际存在的 exe 识别,5 和 6 互不覆盖
  59. function detectController(dir) {
  60. if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter6.exe))) return "SmartShooter6"
  61. if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter.exe))) return "SmartShooter"
  62. if (fs.existsSync(path.join(dir, CONTROLLERS.digiCamControl.exe))) return "digiCamControl"
  63. return ""
  64. }
  65. async function checkCameraControlCmdExists() {
  66. try {
  67. // 拼接 CameraControlCmd.exe 的完整路径
  68. let exePath = getExePath()
  69. // 检查文件是否存在
  70. const exists = await fs.promises.access(exePath, fs.constants.F_OK)
  71. .then(() => true)
  72. .catch(() => false);
  73. if (!exists) {
  74. // 弹出文件夹选择对话框
  75. const { canceled, filePaths } = await dialog.showOpenDialog({
  76. title: '选择 相机控制安装软件 文件夹',
  77. properties: ['openDirectory']
  78. });
  79. if (!canceled && filePaths.length > 0) {
  80. const selectedPath = filePaths[0];
  81. const detected = detectController(selectedPath);
  82. if (!detected) {
  83. console.error('所选目录不是已知的相机控制软件');
  84. return {
  85. status:-1,
  86. msg:"所选目录中未找到 SmartShooter6.exe、SmartShooter5.exe 或 CameraControl.exe",
  87. }
  88. }
  89. writeConfigFile("controlType", detected)
  90. writeConfigFile("controlPath", selectedPath)
  91. writeConfigFile(CONTROLLERS[detected].pathKey, selectedPath)
  92. } else {
  93. console.error('用户未选择文件夹');
  94. return {
  95. status:-1,
  96. msg:"无法找到 SmartShooter6.exe、SmartShooter5.exe 或 CameraControl.exe",
  97. }
  98. }
  99. }
  100. const res = await openCameraControlCmd();
  101. // Smart Shooter 5 / 6 启动后最小化窗口;digiCamControl 走自己的最小化命令
  102. if(isSmartShooterType(getController().type)){
  103. // 等待软件启动完成
  104. setTimeout(async () => {
  105. try {
  106. await minimizeSmartShooter();
  107. } catch (error) {
  108. console.log('最小化SmartShooter失败:', error);
  109. }
  110. }, 3000); // 等待3秒让软件完全启动
  111. }
  112. return res;
  113. } catch (error) {
  114. Log.error('检查 第三方相机控制器 是否存在时出错:', error);
  115. throw error;
  116. }
  117. }
  118. async function openCameraControlCmd(digiCamControlPath) {
  119. return new Promise(async (resolve, reject) => {
  120. try {
  121. // 获取 digiCamControl 文件夹路径
  122. // 拼接 CameraControlCmd.exe 的完整路径
  123. let exePath = getExePath()
  124. // 检查文件是否存在
  125. await fs.promises.access(exePath, fs.constants.F_OK);
  126. try {
  127. // 保存进程引用并标记为运行中
  128. cameraProcess = spawn(exePath);
  129. isProcessRunning = true;
  130. console.log('[CAMERA-DEBUG] spawn PID:', cameraProcess.pid, '时间:', new Date().toISOString());
  131. cameraProcess.stdout.on('data', (data) => {
  132. console.log('[CAMERA-DEBUG] stdout 收到数据:', data.toString().substring(0, 100));
  133. resolve(true)
  134. });
  135. // 监听进程关闭事件
  136. cameraProcess.on('close', (code) => {
  137. console.log('[CAMERA-DEBUG] close 事件触发! PID:', cameraProcess?.pid, '退出码:', code, '时间:', new Date().toISOString());
  138. isProcessRunning = false;
  139. cameraProcess = null;
  140. Log.info(`相机控制软件已关闭,退出码: ${code}`);
  141. processEmitter.emit('camera-process-closed');
  142. });
  143. cameraProcess.on('error', (err) => {
  144. console.log('[CAMERA-DEBUG] error 事件触发:', err.message);
  145. isProcessRunning = false;
  146. cameraProcess = null;
  147. Log.error('相机控制软件进程错误:', err);
  148. });
  149. } catch (error) {
  150. Log.error('error 第三方相机控制器:', error);
  151. throw error;
  152. }
  153. } catch (error) {
  154. Log.error('无法找到或运行 第三方相机控制器:', error);
  155. throw error;
  156. }
  157. })
  158. }
  159. async function closeCameraControlTips() {
  160. try {
  161. await closeOtherWindow()
  162. }catch (e) {
  163. console.log(e)
  164. }
  165. }
  166. module.exports = {
  167. checkCameraControlCmdExists,
  168. closeCameraControlTips,
  169. minimizeSmartShooter,
  170. CONTROLLERS,
  171. isSmartShooterType,
  172. getController,
  173. // 进程状态管理
  174. isProcessRunning: () => isProcessRunning,
  175. onProcessClosed: (callback) => processEmitter.on('camera-process-closed', callback),
  176. removeProcessClosedListener: (callback) => processEmitter.removeListener('camera-process-closed', callback),
  177. };