|
@@ -11,7 +11,7 @@ const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
|
|
|
const CoreWindow = require("ee-core/electron/window");
|
|
const CoreWindow = require("ee-core/electron/window");
|
|
|
|
|
|
|
|
|
|
|
|
|
-const { readConfigFile, writeConfigFile } = require('../utils/config');
|
|
|
|
|
|
|
+const { readConfigFile, updateConfigFile } = require('../utils/config');
|
|
|
const defaultConfig = require('./config.default');
|
|
const defaultConfig = require('./config.default');
|
|
|
|
|
|
|
|
// 进程状态管理
|
|
// 进程状态管理
|
|
@@ -72,12 +72,46 @@ function findSmartShooter5Dir(config) {
|
|
|
return ""
|
|
return ""
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function saveControllerChoice(type, dir) {
|
|
|
|
|
+ const spec = CONTROLLERS[type]
|
|
|
|
|
+ if (!spec || !dir) return
|
|
|
|
|
+ updateConfigFile({
|
|
|
|
|
+ controlType: type,
|
|
|
|
|
+ controlPath: dir,
|
|
|
|
|
+ [spec.pathKey]: dir,
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function controllerChoiceMatches(config, type, dir) {
|
|
|
|
|
+ const spec = CONTROLLERS[type]
|
|
|
|
|
+ if (!spec) return false
|
|
|
|
|
+ return config.controlType === type
|
|
|
|
|
+ && sameDir(config.controlPath, dir)
|
|
|
|
|
+ && sameDir(config[spec.pathKey], dir)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function getController() {
|
|
function getController() {
|
|
|
const config = readConfigFile() || {}
|
|
const config = readConfigFile() || {}
|
|
|
const type = normalizeControlType(config.controlType)
|
|
const type = normalizeControlType(config.controlType)
|
|
|
const spec = CONTROLLERS[type]
|
|
const spec = CONTROLLERS[type]
|
|
|
const dir = config.controlPath || config[spec.pathKey] || ""
|
|
const dir = config.controlPath || config[spec.pathKey] || ""
|
|
|
|
|
|
|
|
|
|
+ // 以目录里实际存在的 exe 为准。
|
|
|
|
|
+ // 否则会出现 controlPath 已是 Smart Shooter 6,controlType 仍是 digiCamControl,
|
|
|
|
|
+ // 程序却去这个目录里找 CameraControl.exe。
|
|
|
|
|
+ const detected = detectController(dir)
|
|
|
|
|
+ if (detected) {
|
|
|
|
|
+ if (!controllerChoiceMatches(config, detected, dir)) {
|
|
|
|
|
+ Log.info(`按安装目录识别为 ${CONTROLLERS[detected].label}: ${dir}`)
|
|
|
|
|
+ saveControllerChoice(detected, dir)
|
|
|
|
|
+ }
|
|
|
|
|
+ return {
|
|
|
|
|
+ type: detected,
|
|
|
|
|
+ ...CONTROLLERS[detected],
|
|
|
|
|
+ dir,
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 默认 Smart Shooter 6 目录下没有程序时,再找 Smart Shooter 5。只影响本次启动,不回写配置。
|
|
// 默认 Smart Shooter 6 目录下没有程序时,再找 Smart Shooter 5。只影响本次启动,不回写配置。
|
|
|
if (type === "SmartShooter6" && isDefaultSmartShooter6Dir(dir) && !fs.existsSync(path.join(dir, spec.exe))) {
|
|
if (type === "SmartShooter6" && isDefaultSmartShooter6Dir(dir) && !fs.existsSync(path.join(dir, spec.exe))) {
|
|
|
const ss5Dir = findSmartShooter5Dir(config)
|
|
const ss5Dir = findSmartShooter5Dir(config)
|
|
@@ -109,6 +143,7 @@ function getExePath () {
|
|
|
|
|
|
|
|
// 用户手动选安装目录时,按目录里实际存在的 exe 识别,5 和 6 互不覆盖
|
|
// 用户手动选安装目录时,按目录里实际存在的 exe 识别,5 和 6 互不覆盖
|
|
|
function detectController(dir) {
|
|
function detectController(dir) {
|
|
|
|
|
+ if (!dir) return ""
|
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter6.exe))) return "SmartShooter6"
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter6.exe))) return "SmartShooter6"
|
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter.exe))) return "SmartShooter"
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.SmartShooter.exe))) return "SmartShooter"
|
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.digiCamControl.exe))) return "digiCamControl"
|
|
if (fs.existsSync(path.join(dir, CONTROLLERS.digiCamControl.exe))) return "digiCamControl"
|
|
@@ -118,7 +153,18 @@ function detectController(dir) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+let ensureControllerPromise = null
|
|
|
|
|
+
|
|
|
async function checkCameraControlCmdExists() {
|
|
async function checkCameraControlCmdExists() {
|
|
|
|
|
+ if (!ensureControllerPromise) {
|
|
|
|
|
+ ensureControllerPromise = ensureCameraController().finally(() => {
|
|
|
|
|
+ ensureControllerPromise = null
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ return ensureControllerPromise
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+async function ensureCameraController() {
|
|
|
try {
|
|
try {
|
|
|
|
|
|
|
|
// 拼接 CameraControlCmd.exe 的完整路径
|
|
// 拼接 CameraControlCmd.exe 的完整路径
|
|
@@ -147,9 +193,7 @@ async function checkCameraControlCmdExists() {
|
|
|
msg:"所选目录中未找到 SmartShooter6.exe、SmartShooter5.exe 或 CameraControl.exe",
|
|
msg:"所选目录中未找到 SmartShooter6.exe、SmartShooter5.exe 或 CameraControl.exe",
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- writeConfigFile("controlType", detected)
|
|
|
|
|
- writeConfigFile("controlPath", selectedPath)
|
|
|
|
|
- writeConfigFile(CONTROLLERS[detected].pathKey, selectedPath)
|
|
|
|
|
|
|
+ saveControllerChoice(detected, selectedPath)
|
|
|
} else {
|
|
} else {
|
|
|
console.error('用户未选择文件夹');
|
|
console.error('用户未选择文件夹');
|
|
|
return {
|
|
return {
|
|
@@ -180,55 +224,57 @@ async function checkCameraControlCmdExists() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
-async function openCameraControlCmd(digiCamControlPath) {
|
|
|
|
|
- return new Promise(async (resolve, reject) => {
|
|
|
|
|
- try {
|
|
|
|
|
- // 获取 digiCamControl 文件夹路径
|
|
|
|
|
-
|
|
|
|
|
- // 拼接 CameraControlCmd.exe 的完整路径
|
|
|
|
|
- let exePath = getExePath()
|
|
|
|
|
|
|
+function openCameraControlCmd() {
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ const exePath = getExePath()
|
|
|
|
|
+ fs.promises.access(exePath, fs.constants.F_OK).then(() => {
|
|
|
|
|
+ let settled = false
|
|
|
|
|
+ const finish = (fn, value) => {
|
|
|
|
|
+ if (settled) return
|
|
|
|
|
+ settled = true
|
|
|
|
|
+ fn(value)
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 检查文件是否存在
|
|
|
|
|
- await fs.promises.access(exePath, fs.constants.F_OK);
|
|
|
|
|
|
|
+ let child
|
|
|
try {
|
|
try {
|
|
|
|
|
+ child = spawn(exePath, [], { cwd: path.dirname(exePath) })
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ Log.error('无法启动第三方相机控制器:', error)
|
|
|
|
|
+ finish(reject, error)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 保存进程引用并标记为运行中
|
|
|
|
|
- cameraProcess = spawn(exePath);
|
|
|
|
|
- isProcessRunning = true;
|
|
|
|
|
- console.log('[CAMERA-DEBUG] spawn PID:', cameraProcess.pid, '时间:', new Date().toISOString());
|
|
|
|
|
-
|
|
|
|
|
- cameraProcess.stdout.on('data', (data) => {
|
|
|
|
|
- console.log('[CAMERA-DEBUG] stdout 收到数据:', data.toString().substring(0, 100));
|
|
|
|
|
- resolve(true)
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- // 监听进程关闭事件
|
|
|
|
|
- cameraProcess.on('close', (code) => {
|
|
|
|
|
- console.log('[CAMERA-DEBUG] close 事件触发! PID:', cameraProcess?.pid, '退出码:', code, '时间:', new Date().toISOString());
|
|
|
|
|
- isProcessRunning = false;
|
|
|
|
|
- cameraProcess = null;
|
|
|
|
|
- Log.info(`相机控制软件已关闭,退出码: ${code}`);
|
|
|
|
|
- processEmitter.emit('camera-process-closed');
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- cameraProcess.on('error', (err) => {
|
|
|
|
|
- console.log('[CAMERA-DEBUG] error 事件触发:', err.message);
|
|
|
|
|
- isProcessRunning = false;
|
|
|
|
|
- cameraProcess = null;
|
|
|
|
|
- Log.error('相机控制软件进程错误:', err);
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ cameraProcess = child
|
|
|
|
|
+ isProcessRunning = true
|
|
|
|
|
+ console.log('[CAMERA-DEBUG] spawn PID:', child.pid, 'cwd:', path.dirname(exePath), '时间:', new Date().toISOString());
|
|
|
|
|
|
|
|
|
|
+ child.stdout.on('data', (data) => {
|
|
|
|
|
+ console.log('[CAMERA-DEBUG] stdout 收到数据:', data.toString().substring(0, 100));
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
|
|
+ child.on('close', (code) => {
|
|
|
|
|
+ console.log('[CAMERA-DEBUG] close 事件触发! PID:', cameraProcess?.pid, '退出码:', code, '时间:', new Date().toISOString());
|
|
|
|
|
+ isProcessRunning = false;
|
|
|
|
|
+ cameraProcess = null;
|
|
|
|
|
+ Log.info(`相机控制软件已关闭,退出码: ${code}`);
|
|
|
|
|
+ processEmitter.emit('camera-process-closed');
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- Log.error('error 第三方相机控制器:', error);
|
|
|
|
|
- throw error;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ child.on('error', (err) => {
|
|
|
|
|
+ console.log('[CAMERA-DEBUG] error 事件触发:', err.message);
|
|
|
|
|
+ isProcessRunning = false;
|
|
|
|
|
+ cameraProcess = null;
|
|
|
|
|
+ Log.error('相机控制软件进程错误:', err);
|
|
|
|
|
+ finish(reject, err)
|
|
|
|
|
+ });
|
|
|
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
|
|
+ // 图形界面程序通常没有 stdout,进程拉起即视为启动成功
|
|
|
|
|
+ child.once('spawn', () => finish(resolve, true))
|
|
|
|
|
+ if (child.pid) finish(resolve, true)
|
|
|
|
|
+ }).catch((error) => {
|
|
|
Log.error('无法找到或运行 第三方相机控制器:', error);
|
|
Log.error('无法找到或运行 第三方相机控制器:', error);
|
|
|
- throw error;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ reject(error)
|
|
|
|
|
+ })
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|