Przeglądaj źródła

fix(camera): 修复相机控制软件进程管理和预览功能问题

- 添加详细的调试日志来追踪相机控制流程
- 修复进程状态检测机制,确保 isSoftwareStarted 与实际进程状态同步
- 优化预览开启关闭逻辑,防止重复操作和竞态条件
- 添加预览切换时的状态检查和延迟处理机制
- 修复拍摄完成后预览的显示逻辑
- 移除 Python 服务器自动启动功能
- 优化前端组件中的点位配置显示逻辑
panqiuyao 1 dzień temu
rodzic
commit
5a1078a5a4

+ 11 - 0
electron/controller/camera.js

@@ -31,7 +31,9 @@ class CameraController extends Controller {
       console.log(readConfigFile());
 
       // 主动检测:即使 isSoftwareStarted 为 true,也要检查进程是否还在运行(兜底机制)
+      console.log('[CAMERA-DEBUG] connect() 检查状态 - isSoftwareStarted:', isSoftwareStarted, 'isProcessRunning():', isProcessRunning());
       if (isSoftwareStarted && !isProcessRunning()) {
+        console.log('[CAMERA-DEBUG] 检测到 isSoftwareStarted 为 true 但进程已关闭,重置标志');
         Log.info('检测到 isSoftwareStarted 为 true 但进程已关闭,重置标志');
         isSoftwareStarted = false;
       }
@@ -41,6 +43,7 @@ class CameraController extends Controller {
 
         // 主动检测:如果软件未启动或进程已关闭,先启动软件
         if (!isSoftwareStarted || !isProcessRunning()) {
+          console.log('[CAMERA-DEBUG] 准备启动 digiCamControl - isSoftwareStarted:', isSoftwareStarted, 'isProcessRunning:', isProcessRunning());
           console.log('启动 digiCamControl 软件...');
           await checkCameraControlCmdExists();
           await CMD('All_Minimize');
@@ -71,10 +74,15 @@ class CameraController extends Controller {
       }else{
         // SmartShooter 逻辑
         // 主动检测:如果软件未启动或进程已关闭,先启动软件
+        console.log('[CAMERA-DEBUG] SmartShooter 分支 - isSoftwareStarted:', isSoftwareStarted, 'isProcessRunning():', isProcessRunning());
         if (!isSoftwareStarted || !isProcessRunning()) {
+          console.log('[CAMERA-DEBUG] 准备启动 SmartShooter - isSoftwareStarted:', isSoftwareStarted, 'isProcessRunning:', isProcessRunning());
           Log.info('启动 SmartShooter 软件...');
+          Log.info('isSoftwareStarted...'+isSoftwareStarted);
+          Log.info('isProcessRunning...'+isProcessRunning);
           await checkCameraControlCmdExists();
           isSoftwareStarted = true;
+          console.log('[CAMERA-DEBUG] checkCameraControlCmdExists() 完成,isSoftwareStarted:', isSoftwareStarted, 'isProcessRunning():', isProcessRunning());
           Log.info('SmartShooter 软件启动完成,等待初始化...');
           await new Promise(resolve => setTimeout(resolve, 10000)); // 等待软件初始化
         }
@@ -130,6 +138,8 @@ class CameraController extends Controller {
   async liveShow(data) {
     try {
       const point_name = data?.point_name || 'A';
+
+      Log.info('liveShow:打开预览'+point_name);
       await liveShowApi(point_name);
       if(readConfigFile().controlType === 'digiCamControl'){
         await  CMD('All_Minimize')
@@ -147,6 +157,7 @@ class CameraController extends Controller {
   async liveHide(data) {
     try {
       const point_name = data?.point_name || 'A';
+      Log.info('liveShow:关闭'+point_name);
       await liveHideApi(point_name);
       return { success: true, point_name };
     } catch (error) {

+ 1 - 1
electron/preload/index.js

@@ -13,5 +13,5 @@ module.exports = async () => {
   Addon.get("security").create();
   Addon.get("awaken").create();
   Addon.get("autoUpdater").create();
-  Services.get("cross").createPythonServer();
+ // Services.get("cross").createPythonServer();
 };

+ 4 - 0
electron/utils/camera.js

@@ -115,13 +115,16 @@ async function openCameraControlCmd(digiCamControlPath) {
         // 保存进程引用并标记为运行中
         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}`);
@@ -129,6 +132,7 @@ async function openCameraControlCmd(digiCamControlPath) {
         });
 
         cameraProcess.on('error', (err) => {
+          console.log('[CAMERA-DEBUG] error 事件触发:', err.message);
           isProcessRunning = false;
           cameraProcess = null;
           Log.error('相机控制软件进程错误:', err);

+ 114 - 44
frontend/src/views/Photography/check.vue

@@ -96,12 +96,18 @@ const emit = defineEmits([ 'confirm','onClose']);
 import  configInfo  from '@/stores/modules/config';
 const configInfoStore = configInfo();
 
-const confirm = ()=>{
-  hideVideo(currentPointName.value)
+const confirm = async () => {
+  console.log('confirm');
+  setTimeout(()=>{
+     hideVideo(currentPointName.value)
+  },1000)
   emit('confirm')
 }
-const onClose = ()=>{
-  hideVideo(currentPointName.value)
+const onClose = async () => {
+  console.log('onClose');
+  setTimeout(()=>{
+     hideVideo(currentPointName.value)
+  },1000)
   emit('onClose')
 }
 // 定义 props
@@ -156,9 +162,10 @@ async function checkConfirm(init){
   loading.value = false;
 }
 
-// editRow 数据加载完成后调用 showVideo
+// editRow 数据加载完成后调用,仅记录点位,首次不打开预览
 function onEditRowReady(point_name) {
-  showVideo(point_name)
+  console.log('onEditRowReady,记录点位:' + point_name)
+  currentPointName.value = point_name
 }
 
 const init = ref(true)
@@ -175,37 +182,88 @@ const showrEditRow = ref(false)
 
 let interval:any = null
 let currentPointName = ref('A')
+let isVideoShowing = ref(false)
+let isHidingVideo = ref(false)
+let hideVideoTimer: any = null
+let pendingShowVideoPoint: string | null = null
+let isFirstShow = false  // 标记是否已首次显示预览
+
+function clearPreviewInterval() {
+  if (interval) {
+    clearInterval(interval)
+    interval = null
+  }
+  if (hideVideoTimer) {
+    clearTimeout(hideVideoTimer)
+    hideVideoTimer = null
+  }
+}
 
-function showVideo(point_name = 'A'){
+function showVideo(point_name = 'A') {
+  if (isHidingVideo.value) {
+    console.log('正在关闭预览中,延迟打开:' + point_name)
+    pendingShowVideoPoint = point_name
+    return
+  }
+
+  if (isVideoShowing.value && currentPointName.value === point_name) {
+    console.log('预览已在显示,跳过重复调用')
+    return
+  }
+  console.log('打开预览=====================' + point_name)
+  clearPreviewInterval()
+  isVideoShowing.value = true
   currentPointName.value = point_name
 
   clientStore.ipc.removeAllListeners(icpList.camera.PreviewShow);
 
   clientStore.ipc.send(icpList.camera.PreviewShow, { point_name });
   clientStore.ipc.on(icpList.camera.PreviewShow, async (event, result) => {
-
-    setTimeout(()=>{
-      interval = setInterval(()=>{
+    clearPreviewInterval()
+    hideVideoTimer = setTimeout(() => {
+      interval = setInterval(() => {
         previewKey.value++;
-      },200)
-    },500)
-
+      }, 200)
+    }, 500)
   })
-
 }
 
-function hideVideo(point_name = 'A'){
+async function hideVideo(point_name = 'A') {
+  console.log('关闭预览=====================' + point_name)
+  clearPreviewInterval()
+  isVideoShowing.value = false
+  isHidingVideo.value = true
+
   return new Promise((resolve) => {
     clientStore.ipc.removeAllListeners(icpList.camera.PreviewHide);
     clientStore.ipc.send(icpList.camera.PreviewHide, { point_name });
     clientStore.ipc.on(icpList.camera.PreviewHide, async (event, result) => {
-      if(interval) clearInterval(interval)
+      isHidingVideo.value = false
+      // 如果有待执行的打开请求,执行它
+      if (pendingShowVideoPoint) {
+        const point = pendingShowVideoPoint
+        pendingShowVideoPoint = null
+        setTimeout(() => showVideo(point), 100)
+      }
       resolve(true)
     })
   })
 }
 
 async function switchPreviewPoint(point_name = 'A') {
+  console.log('switchPreviewPoint:' + point_name);
+  // 如果点位相同,不做任何处理
+  if (point_name === currentPointName.value) {
+    console.log('点位相同,跳过切换');
+    return;
+  }
+  // 首次显示预览之前不做切换
+/*  if (!isFirstShow) {
+    console.log('首次显示预览前,跳过切换');
+    currentPointName.value = point_name;
+    return;
+  }*/
+  // 执行切换
   await hideVideo(currentPointName.value)
   showVideo(point_name)
 }
@@ -222,31 +280,31 @@ function takePictures() {
   console.log(editData.value.editRowData);
 
   if (clientStore.isClient) {
-
     loading.value = true;
-    hideVideo(currentPointName.value)
-    socketStore.sendMessage({
-      type: 'run_mcu_single',
-      data: {
-        camera_height: Number(editData.value.editRowData.camera_height),
-        camera_angle:  Number(editData.value.editRowData.camera_angle),
-        led_switch:editData.value.editRowData.led_switch,
-        id:0,
-        mode_type:editData.value.editRowData.mode_type,
-        turntable_position:Number(editData.value.editRowData.turntable_position),
-        action_name:editData.value.editRowData.action_name || '测试',
-        turntable_angle: Number(editData.value.editRowData.turntable_angle),
-        shoe_upturn: Number(editData.value.editRowData.shoe_upturn),
-        action_index:1,
-        number_focus:0,
-        take_picture:true,
-        pre_delay:0,
-        after_delay:0,
-      }
-    });
-
-
-
+    console.log('takePictures');
+    hideVideo(currentPointName.value).then(() => {
+      socketStore.sendMessage({
+        type: 'run_mcu_single',
+        data: {
+          camera_height: Number(editData.value.editRowData.camera_height),
+          camera_angle:  Number(editData.value.editRowData.camera_angle),
+          led_switch:editData.value.editRowData.led_switch,
+          id:0,
+          mode_type:editData.value.editRowData.mode_type,
+          turntable_position:Number(editData.value.editRowData.turntable_position),
+          action_name:editData.value.editRowData.action_name || '测试',
+          turntable_angle: Number(editData.value.editRowData.turntable_angle),
+          shoe_upturn: Number(editData.value.editRowData.shoe_upturn),
+          action_index:1,
+          number_focus:0,
+          take_picture:true,
+          pre_delay:0,
+          after_delay:0,
+          point_name:editData.value.editRowData.point_name || currentPointName.value  || 'A',
+          is_move_device:editData.value.editRowData.is_move_device || 1
+        }
+      });
+    })
   }
 }
 
@@ -261,11 +319,12 @@ function  createMainImage (file_path){
   clientStore.ipc.on(icpList.takePhoto.createMainImage, async (event, result) => {
     if(result.code === 0 && result.data?.main_out_path){
       imageTplPath.value  = result.data?.main_out_path
-      hideVideo(currentPointName.value)
+      // 预览已在 takePictures 中关闭,不再重复调用 hideVideo
       step.value = 2
       loading.value = false;
     }else if(result.msg){
       loading.value = false;
+      console.log('createMainImage');
       showVideo(currentPointName.value)
       if(result.code !== 0) ElMessage.error(result.msg)
     }
@@ -282,11 +341,16 @@ clientStore.ipc.on(icpList.socket.message+'_smart_shooter_photo_take', async (ev
   console.log(result);
   if(result.code === 0 && result.data?.photo_file_name){
     imageTplPath.value  = result.data?.photo_file_name
-    hideVideo(currentPointName.value)
     step.value = 2
     loading.value = false;
+    // 首次拍摄完成后打开预览用于检查镜头
+    if (!isFirstShow) {
+      isFirstShow = true
+      setTimeout(() => showVideo(currentPointName.value), 100)
+    }
   }else {
     loading.value = false;
+    console.log('_smart_shooter_photo_take');
     showVideo(currentPointName.value)
     if(result.code !== 0 && result.msg) ElMessage.error(result.msg)
   }
@@ -301,11 +365,16 @@ clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, resul
 
   if(result.code === 0 && result.data?.file_path){
     imageTplPath.value  = result.data?.file_path
-    hideVideo(currentPointName.value)
     step.value = 2
     loading.value = false;
+    // 首次拍摄完成后打开预览用于检查镜头
+    if (!isFirstShow) {
+      isFirstShow = true
+      setTimeout(() => showVideo(currentPointName.value), 100)
+    }
   }else {
     loading.value = false;
+    console.log('_run_mcu_single');
     showVideo(currentPointName.value)
     if(result.code !== 0 && result.msg) ElMessage.error(result.msg)
   }
@@ -328,7 +397,6 @@ clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, resul
 
 const exampleImage = ref('https://huilimaimg.cnhqt.com/frontend/zhihuiyin/demo.jpg?x-oss-process=image/resize,w_400')
 onMounted(async ()=>{
-  if(isSetting.value)   showVideo()
   await  configInfoStore.getAppConfig()
   if(configInfoStore.appConfig.controlType === "SmartShooter"){
     preview.value = configInfoStore.appConfig.userDataPath  + "\\preview\\liveview.png"
@@ -341,6 +409,8 @@ onMounted(async ()=>{
  * 页面卸载时移除所有事件监听器。
  */
 onBeforeUnmount(() => {
+  clearPreviewInterval()
+  console.log('onBeforeUnmount');
   hideVideo(currentPointName.value)
   clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
   clientStore.ipc.removeAllListeners(icpList.camera.PreviewHide);

+ 2 - 2
frontend/src/views/Photography/components/editRow.vue

@@ -11,7 +11,7 @@
       </el-form-item>
 
       <!-- 多相机模式 - 拍摄点位 -->
-      <el-form-item label="拍摄点位" v-if="isMultiCameraMode && !props.disablePointConfig">
+      <el-form-item label="拍摄点位" v-if="isMultiCameraMode">
         <el-select v-model="editRowData.point_name" placeholder="请选择点位" style="width: 170px;">
           <el-option label="点位 A" value="A"/>
           <el-option label="点位 B" value="B"/>
@@ -20,7 +20,7 @@
       </el-form-item>
 
       <!-- 多相机模式 - 仅拍照模式 -->
-      <el-form-item label="是否移动设备" v-if="isMultiCameraMode && !props.disablePointConfig">
+      <el-form-item label="是否移动设备" v-if="isMultiCameraMode">
         <div class="flex-row">
           <el-radio-group v-model="editRowData.is_move_device">
             <el-radio :label="0">不移动</el-radio>