Ver Fonte

Merge remote-tracking branch 'origin/dev-frontend_0702' into smart-shooter-master

# Conflicts:
#	frontend/src/config.json
#	package.json
panqiuyao há 4 meses atrás
pai
commit
f9b02bf16e

+ 60 - 8
electron/api/camera.js

@@ -52,17 +52,53 @@ async function fetchExampleData(url) {
 }
 
 
+const socket = require('../utils/socket')
+const pySocket = new socket()
+const { readConfigFile } = require('../utils/config');
+
 
 module.exports = {
-  liveShow(){
-    return get({
-      url: '?CMD=LiveViewWnd_Show'
-    })
+  async liveShow(){
+    if(readConfigFile().controlType === 'digiCamControl'){
+      return get({
+        url: '?CMD=LiveViewWnd_Show'
+      })
+    }else{
+
+      await pySocket.sendMessage(JSON.stringify({
+        type: 'smart_shooter_enable_preview',
+        data:{
+          value:true
+        }
+      }))
+      return  new Promise(async (resolve, reject) => {
+        pySocket.onSocketMessage('smart_shooter_enable_preview',(message)=>{
+
+          resolve(message)
+        })
+      })
+    }
   },
-  liveHide(){
-    return get({
-      url: '?CMD=LiveViewWnd_Hide'
-    })
+  async liveHide(){
+    if(readConfigFile().controlType === 'digiCamControl'){
+      return get({
+        url: '?CMD=LiveViewWnd_Hide'
+      })
+    }else{
+
+      await pySocket.sendMessage(JSON.stringify({
+        type: 'smart_shooter_enable_preview',
+        data:{
+          value:false
+        }
+      }))
+      return  new Promise(async (resolve, reject) => {
+        pySocket.onSocketMessage('smart_shooter_enable_preview',(message)=>{
+
+          resolve(message)
+        })
+      })
+    }
   },
   captureLive(){
     return get({
@@ -95,6 +131,22 @@ module.exports = {
       url: '/close_other_window',
     })
   },
+  async checkCamera(){
+    if(readConfigFile().controlType === 'digiCamControl'){
+      return  fetchExampleData(`?slc=get&param1=iso`)
+    }else {
+
+      await pySocket.sendMessage(JSON.stringify({
+        type: 'smart_shooter_getinfo',
+        data:{}
+      }))
+     return  new Promise(async (resolve, reject) => {
+        pySocket.onSocketMessage('smart_shooter_getinfo',(message)=>{
+          resolve(message)
+        })
+      })
+    }
+  }
 }
 
 

+ 2 - 1
electron/api/request.js

@@ -1,5 +1,6 @@
 const axios = require('axios')
-const { pyapp } = require('../config/app.config.json')
+const { readConfigFile } = require('../utils/config');
+const pyapp = readConfigFile().pyapp
 
 
 /* axios.defaults.withCredentials = true*/

+ 0 - 3
electron/config/app.config.json

@@ -1,3 +0,0 @@
-{
-  "pyapp": "127.0.0.1"
-}

+ 63 - 139
electron/controller/camera.js

@@ -1,158 +1,80 @@
 'use strict';
-
-const path = require('path');
-const fs = require('fs');
 const { Controller } = require('ee-core');
-const { spawn } = require('child_process');
-const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow } = require('../api/camera');
-
-const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
-const { windowManager } = require('node-window-manager');
-const CoreWindow = require("ee-core/electron/window");
-
-async function checkCameraControlCmdExists(digiCamControlPath) {
-  try {
-
-    // 拼接 CameraControlCmd.exe 的完整路径
-    const exePath = path.join(digiCamControlPath, 'CameraControl.exe');
-
-    // 检查文件是否存在
-    const exists = await fs.promises.access(exePath, fs.constants.F_OK)
-      .then(() => true)
-      .catch(() => false);
-
-    if (!exists) {
-      // 弹出文件夹选择对话框
-      const { canceled, filePaths } = await dialog.showOpenDialog({
-        title: '选择 digiCamControl 文件夹',
-        properties: ['openDirectory']
-      });
-
-      if (!canceled && filePaths.length > 0) {
-        const selectedPath = filePaths[0];
-        // 更新 app.config.json 中的 digiCamControl 值
-       // config.digiCamControl = selectedPath;
-      //  fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
-        digiCamControlPath = selectedPath
-        const win = CoreWindow.getMainWindow()
-        win.webContents.send('controller.camera.digiCamControlPath', {
-          code:0,
-          data:selectedPath,
-        });
-      //  return true; // 重新检查文件是否存在
-      } else {
-        console.error('用户未选择文件夹');
-        return {
-          status:-1,
-          msg:"无法找到或运行 CameraControlCmd.exe",
-        }
-      }
-    }
-    const res = await openCameraControlCmd(digiCamControlPath);
-    return res;
-  } catch (error) {
-    console.error('检查 CameraControlCmd.exe 是否存在时出错:', error);
-    throw error;
-  }
-}
-
-
-async function openCameraControlCmd(digiCamControlPath) {
- return  new Promise(async (resolve, reject) => {
-    try {
-      // 获取 digiCamControl 文件夹路径
-
-      // 拼接 CameraControlCmd.exe 的完整路径
-      const exePath = path.join(digiCamControlPath, 'CameraControl.exe');
-
-      // 检查文件是否存在
-      await fs.promises.access(exePath, fs.constants.F_OK);
-      try {
-
-          const child = spawn(exePath);
-
-          child.stdout.on('data', (data) => {
-
-            resolve(true)
-          });
-
-          child.on('close', (code) => {
-            if (code === 0) {
-              reject(false)
-            }
-          });
-
-
-
-      } catch (error) {
-        console.error('error CameraControlCmd.exe:', error);
-        throw error;
-      }
-
-      console.log('CameraControlCmd.exe start');
-    } catch (error) {
-      console.error('无法找到或运行 CameraControlCmd.exe:', error);
-      throw error;
-    }
-  })
-}
-
-async function closeCameraControlTips() {
-  try {
-    await  closeOtherWindow()
-/*    const windows = windowManager.getWindows();
+const Log = require('ee-core/log');
+const { checkCameraControlCmdExists, closeCameraControlTips} = require('../utils/camera');
+const {
+  checkCamera,
+  liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow } = require('../api/camera');
 
-    for (const window of windows) {
-      const title = window.getTitle();
+const { readConfigFile } = require('../utils/config');
 
-      if (title.indexOf('digiCamControl')>=0) {
-        console.log(title);
-        console.log(window);
-        window.minimize(); // 关闭窗口
-        //   window.hide()
-      //  break;
-      }
-    }*/
-  }catch (e) {
-    console.log(e)
-  }
-
-
-}
 let  isOPen = true
 class CameraController extends Controller {
   constructor(ctx) {
     super(ctx);
   }
 
-  async connect(digiCamControlPath) {
+  async connect() {
     try {
-      await getParams('iso').catch(e=>{
-        isOPen = false;
-      })
-      if(!isOPen){
-        await checkCameraControlCmdExists(digiCamControlPath)
-        await  CMD('All_Minimize')
-        await closeCameraControlTips()
-        isOPen = true
-      }
-
-      const res = await getParams('iso')
-      if(res  === '未将对象引用设置到对象的实例。'){
+      console.log('==================');
+      console.log(readConfigFile());
+      if(readConfigFile().controlType === 'digiCamControl'){
+
+        console.log('========1==========');
+        await getParams('iso').catch(e=>{
+          isOPen = false;
+        })
+        if(!isOPen){
+          await checkCameraControlCmdExists()
+          await  CMD('All_Minimize')
+          await closeCameraControlTips()
+          isOPen = true
+        }
+        const res = await getParams('iso')
+        if(res  === '未将对象引用设置到对象的实例。'){
+          return {
+            status:-1,
+            msg:"相机未连接,请链接相机。",
+          }
+        }
         return {
-          status:-1,
-          msg:"相机未连接,请链接相机。",
+          status:2,
+          msg:res,
         }
+
+
+      }else{
+        let res = await checkCamera().catch(e=>{
+          isOPen = false;
+        })
+
+        if(res?.device_status  === -1 ){
+          isOPen = false;
+          await checkCameraControlCmdExists()
+          isOPen = true
+          await new Promise(resolve => setTimeout(resolve, 10000)); // 等待5秒
+          res = await checkCamera()
+        }
+
+        if(res?.device_status  === 2){
+          isOPen = true;
+          return  {
+            ...res,
+            status:2
+          };
+        }
+
+
       }
-      return {
-        status:2,
-        msg:res,
-      }
-      return true;
+
+
     } catch (error) {
+
+      let msg = '请安装digiCamControl软件,并打开digiCamControl软件的web服务,端口为5513'
+      if(readConfigFile().controlType === 'SmartShooter') msg = '请安装SmartShooter5软件'
       return {
         status:-1,
-        msg:"请安装digiCamControl软件,并打开digiCamControl软件的web服务,端口为5513",
+        msg,
       }
     }
   }
@@ -163,7 +85,9 @@ class CameraController extends Controller {
   async liveShow() {
     try {
       await liveShow();
-      await  CMD('All_Minimize')
+      if(readConfigFile().controlType === 'digiCamControl'){
+        await  CMD('All_Minimize')
+      }
       return true;
     } catch (error) {
       console.error('eeee启动直播失败:', error);

+ 7 - 91
electron/controller/socket.js

@@ -1,20 +1,10 @@
 'use strict';
 
 const { Controller } = require('ee-core');
-const Log = require('ee-core/log');
-const CoreWindow = require('ee-core/electron/window');
-const WebSocket = require('ws'); // 引入原生 ws 库
-let socket = null;
-const { pyapp } = require('../config/app.config.json')
+const socket = require('../utils/socket')
+const pySocket = new socket()
+
 
-const typeToMessage = {
-  run_mcu_single:['seeting','default'],
-  get_deviation_data:"developer",
-  set_deviation:"developer",
-  get_mcu_other_info:"developer",
-  set_mcu_other_info:"developer",
-  send_command:"developer"
-}
 class SocketController extends Controller {
   constructor(ctx) {
     super(ctx);
@@ -26,71 +16,7 @@ class SocketController extends Controller {
   async connect() {
 
      await new Promise(async (resolve,reject) => {
-
-       const win = CoreWindow.getMainWindow()
-       if(socket){
-         console.log('has socket ')
-         resolve(true);
-         win.webContents.send('controller.socket.connect_open', true);
-         return;
-       }
-
-      socket = new WebSocket('ws://'+pyapp+':7074/ws');
-
-      // 监听连接成功事件
-      socket.on('open', () => {
-        console.log('socket open')
-        resolve(true);
-        win.webContents.send('controller.socket.connect_open', true);
-      });
-
-      // 监听消息事件
-      socket.on('message', (data) => {
-        try {
-          let this_data = JSON.parse(data.toString());
-          console.log(this_data.msg_type);
-          console.log(this_data);
-          if(this_data.msg_type){
-            let channel = 'controller.socket.message_'+this_data.msg_type;
-            if(typeToMessage[this_data.msg_type]){
-              if(typeof typeToMessage[this_data.msg_type] === 'object'){
-
-                typeToMessage[this_data.msg_type].map(item=>{
-                  if(item === 'default'){
-                    win.webContents.send(channel, this_data);
-                  }else{
-                    if(this.app.electron[item]) this.app.electron[item].webContents.send(channel, this_data);
-                  }
-                })
-              }else{
-                if(this.app.electron[typeToMessage[this_data.msg_type]]) this.app.electron[typeToMessage[this_data.msg_type]].webContents.send(channel, this_data);
-              }
-            }else{
-              win.webContents.send(channel, this_data);
-            }
-          }
-        }catch (e){
-          console.log(e)
-        }
-      });
-
-      // 监听连接关闭事件
-      socket.on('close', () => {
-        console.log('socket close');
-        win.webContents.send('controller.socket.disconnect', null);
-        socket = null
-
-      });
-
-      // 监听错误事件
-      socket.on('error', (err) => {
-        console.log('socket error');
-        win.webContents.send('controller.socket.disconnect', null);
-        reject(true);
-
-      });
-
-      console.log('socket end')
+       pySocket.init(this.app)
 
     })
 
@@ -100,8 +26,7 @@ class SocketController extends Controller {
    * 发送 ping 消息
    */
   sendPing() {
-    const message = JSON.stringify({ data: 'node', type: 'ping' });
-    this.sendMessage(message);
+    pySocket.sendPing()
   }
 
   /**
@@ -109,23 +34,14 @@ class SocketController extends Controller {
    * @param {string} message - JSON 字符串
    */
   sendMessage(message) {
-    // 检查连接状态
-    console.log(message);
-    console.log(typeof socket);
-    if (socket?.readyState === WebSocket.OPEN) {
-      socket.send(message); // 使用 send() 发送
-    } else {
-    }
+    pySocket.sendMessage(message)
   }
 
   /**
    * 断开连接
    */
   disconnect() {
-    if (socket) {
-      socket.close(); // 使用 close() 方法
-      socket = null;
-    }
+    pySocket.disconnect()
   }
 }
 

+ 8 - 1
electron/controller/utils.js

@@ -7,7 +7,7 @@ const { dialog } = require('electron');
 const fs = require('fs');
 const path = require('path');
 const CoreWindow = require('ee-core/electron/window');
-const { BrowserWindow, Menu } = require('electron');
+const { BrowserWindow, Menu,app } = require('electron');
 
 const { readConfigFile } = require('../utils/config');
 const configDeault = readConfigFile();
@@ -159,6 +159,13 @@ class UtilsController extends Controller {
 
   }
 
+  getAppConfig(){
+    const config  =  readConfigFile()
+    return  {
+      ...config,
+      userDataPath: app.getPath('userData')
+    }
+  }
   async readFileImageForPath(filePath,maxWidth=1500){
 
     const getMimeType = (fileName)=>{

+ 1 - 1
electron/preload/bridge.js

@@ -7,4 +7,4 @@ const { contextBridge, ipcRenderer } = require('electron')
 
 contextBridge.exposeInMainWorld('electron', {
   ipcRenderer: ipcRenderer,
-})
+})

+ 138 - 0
electron/utils/camera.js

@@ -0,0 +1,138 @@
+// electron/utils/readConfig.js
+
+const path = require('path');
+const fs = require('fs');
+const Log = require('ee-core/log');
+const { spawn } = require('child_process');
+const { liveShow, liveHide, setParams, capture, getParams,CMD,captureLive,closeOtherWindow } = require('../api/camera');
+
+const { dialog } = require('electron'); // 引入 electron 的 dialog 模块
+const { windowManager } = require('node-window-manager');
+const CoreWindow = require("ee-core/electron/window");
+
+
+const { readConfigFile, writeConfigFile } = require('../utils/config');
+
+const exe = {
+  "digiCamControl":"CameraControl.exe",
+  "SmartShooter":"SmartShooter5.exe",
+}
+
+
+function getExePath () {
+  let exePath =  ""
+  if(readConfigFile().controlType === 'digiCamControl'){
+    exePath =  path.join( readConfigFile().controlPath || readConfigFile().digiCamControlPath, exe["digiCamControl"]);
+  }else if(readConfigFile().controlType === 'SmartShooter'){
+    exePath =  path.join( readConfigFile().controlPath || readConfigFile().SmartShooterPath,  exe["SmartShooter"]);
+  }
+
+  console.log('ex============ePath');
+  console.log(exePath);
+  return exePath
+}
+
+
+
+
+async function checkCameraControlCmdExists() {
+  try {
+
+    // 拼接 CameraControlCmd.exe 的完整路径
+
+    let exePath = getExePath()
+    // 检查文件是否存在
+    const exists = await fs.promises.access(exePath, fs.constants.F_OK)
+        .then(() => true)
+        .catch(() => false);
+
+    if (!exists) {
+      // 弹出文件夹选择对话框
+      const { canceled, filePaths } = await dialog.showOpenDialog({
+        title: '选择 相机控制安装软件 文件夹',
+        properties: ['openDirectory']
+      });
+
+      if (!canceled && filePaths.length > 0) {
+        const selectedPath = filePaths[0];
+
+        // Check if SmartShooter5.exe exists in the selected directory
+        const hasExe = path.join(selectedPath, exe["SmartShooter"]);
+
+        if (fs.existsSync(hasExe)) {
+          writeConfigFile("controlType","SmartShooter")
+        }else{
+          writeConfigFile("controlType","digiCamControl")
+        }
+        writeConfigFile("controlPath",selectedPath)
+      } else {
+        console.error('用户未选择文件夹');
+        return {
+          status:-1,
+          msg:"无法找到 CameraControlCmd.exe 或者  SmartShooter5.exe",
+        }
+      }
+    }
+    const res = await openCameraControlCmd();
+    return res;
+  } catch (error) {
+    Log.error('检查 第三方相机控制器 是否存在时出错:', error);
+    throw error;
+  }
+}
+
+
+async function openCameraControlCmd(digiCamControlPath) {
+  return  new Promise(async (resolve, reject) => {
+    try {
+      // 获取 digiCamControl 文件夹路径
+
+      // 拼接 CameraControlCmd.exe 的完整路径
+      let exePath = getExePath()
+
+      // 检查文件是否存在
+      await fs.promises.access(exePath, fs.constants.F_OK);
+      try {
+
+        const child = spawn(exePath);
+
+        child.stdout.on('data', (data) => {
+
+          resolve(true)
+        });
+
+        child.on('close', (code) => {
+          if (code === 0) {
+            reject(false)
+          }
+        });
+
+
+
+      } catch (error) {
+        Log.error('error 第三方相机控制器:', error);
+        throw error;
+      }
+
+    } catch (error) {
+      Log.error('无法找到或运行 第三方相机控制器:', error);
+      throw error;
+    }
+  })
+}
+
+async function closeCameraControlTips() {
+  try {
+    await  closeOtherWindow()
+
+  }catch (e) {
+    console.log(e)
+  }
+
+
+}
+
+module.exports = {
+  checkCameraControlCmdExists,
+  closeCameraControlTips
+};

+ 9 - 0
electron/utils/config.default.json

@@ -0,0 +1,9 @@
+{
+  "openDevTools":false,
+  "controlType": "SmartShooter",
+  "controlPath": "C:\\Program Files\\Smart Shooter 5",
+  "digiCamControlPath":"C:\\Program Files (x86)\\digiCamControl",
+  "SmartShooterPath":"C:\\Program Files\\Smart Shooter 5",
+  "pyapp": "127.0.0.1",
+  "env": "dev"
+}

+ 16 - 4
electron/utils/config.js

@@ -3,6 +3,7 @@
 const fs = require('fs');
 const path = require('path');
 const { app } = require('electron');
+const defaultConfig = require('./config.default');
 
 const configPath = path.join(app.getPath("userData"),'config.default.json');
 
@@ -15,12 +16,18 @@ function readConfigFile() {
   try {
     if (fs.existsSync(configPath)) {
       const data = fs.readFileSync(configPath, 'utf8');
-      return JSON.parse(data);
+
+      return {
+        ...defaultConfig,
+        ...JSON.parse(data),
+      };
     } else {
       console.log('配置文件不存在');
       // 创建空白JSON文件
       fs.writeFileSync(configPath, '{}', 'utf8');
-      return {};
+      return {
+        ...defaultConfig,
+      };
     }
   } catch (error) {
     console.error('读取配置文件出错:', error);
@@ -41,12 +48,17 @@ function writeConfigFile(key, value) {
     // 如果配置文件存在,则读取现有内容
     if (fs.existsSync(configPath)) {
       const data = fs.readFileSync(configPath, 'utf8');
-      config = JSON.parse(data);
+      config = {
+        ...defaultConfig,
+        ...JSON.parse(data),
+      };
     }else {
       console.log('配置文件不存在');
       // 创建空白JSON文件
       fs.writeFileSync(configPath, '{}', 'utf8');
-      config = {}
+      config = {
+        ...defaultConfig,
+      }
     }
 
     // 更新配置项

+ 183 - 0
electron/utils/socket.js

@@ -0,0 +1,183 @@
+
+const Log = require('ee-core/log');
+const CoreWindow = require('ee-core/electron/window');
+const WebSocket = require('ws'); // 引入原生 ws 库
+const { readConfigFile } = require('./config');
+const pyapp = readConfigFile().pyapp
+const { app } = require('electron');
+const path = require('path');
+const fs = require('fs');
+
+const typeToMessage = {
+  run_mcu_single:['seeting','default'],
+  get_deviation_data:"developer",
+  set_deviation:"developer",
+  get_mcu_other_info:"developer",
+  set_mcu_other_info:"developer",
+  send_command:"developer"
+}
+
+
+const previewPath = path.join(app.getPath("userData"),'preview','liveview.png');
+
+// 确保目录存在的函数
+function ensureDirectoryExistence(filePath) {
+  const dir = path.dirname(filePath);
+  if (fs.existsSync(dir)) {
+    return true;
+  }
+  ensureDirectoryExistence(path.dirname(dir));
+  fs.mkdirSync(dir);
+}
+
+function livePreview(data){
+  if(data.msg === '预览数据发送' && data.code === 1){
+
+    ensureDirectoryExistence(previewPath);
+    const tempFilePath = `${previewPath}.tmp`;
+
+    fs.writeFile(tempFilePath, data.data.smart_shooter_preview, 'base64', (err) => {
+      if (err) {
+        Log.error('写入临时文件失败:', err);
+      } else {
+        fs.rename(tempFilePath, previewPath, (renameErr) => {
+          if (renameErr) {
+          } else {
+          }
+        });
+      }
+    });
+
+ }
+
+}
+
+const pySocket = function () {
+
+  this.app = null;
+  this.init = async function (this_app) {
+    if(this_app)   this.app = this_app;
+    await new Promise(async (resolve,reject) => {
+
+      const win = CoreWindow.getMainWindow()
+      if(app.socket){
+        resolve(true);
+        win.webContents.send('controller.socket.connect_open', true);
+        return;
+      }
+
+      app.socket = new WebSocket('ws://'+pyapp+':7074/ws');
+
+      // 监听连接成功事件
+      app.socket.on('open', () => {
+        console.log('socket open')
+        resolve(true);
+        win.webContents.send('controller.socket.connect_open', true);
+      });
+
+      // 监听消息事件
+      app.socket.on('message', (data) => {
+        try {
+          let this_data = JSON.parse(data.toString());
+
+          if(!['blue_tooth','smart_shooter_enable_preview'].includes(this_data.msg_type)){ console.log(this_data);}
+          if(this_data.msg_type){
+            let notAllMessage = false
+            switch (this_data.msg_type){
+              case 'smart_shooter_enable_preview':
+                notAllMessage = true;
+                livePreview(this_data);
+                break;
+            }
+            if(notAllMessage) return;
+            let channel = 'controller.socket.message_'+this_data.msg_type;
+            if(typeToMessage[this_data.msg_type]){
+              if(typeof typeToMessage[this_data.msg_type] === 'object'){
+
+                typeToMessage[this_data.msg_type].map(item=>{
+                  if(item === 'default'){
+                    win.webContents.send(channel, this_data);
+                  }else{
+                    if(this.app.electron[item]) this.app.electron[item].webContents.send(channel, this_data);
+                  }
+                })
+              }else{
+                if(this.app.electron[typeToMessage[this_data.msg_type]]) this.app.electron[typeToMessage[this_data.msg_type]].webContents.send(channel, this_data);
+              }
+            }else{
+              win.webContents.send(channel, this_data);
+            }
+          }
+        }catch (e){
+          console.log(e)
+        }
+      });
+
+      // 监听连接关闭事件
+      app.socket.on('close', () => {
+        console.log('socket close');
+        win.webContents.send('controller.socket.disconnect', null);
+        app.socket = null
+
+      });
+
+      // 监听错误事件
+      app.socket.on('error', (err) => {
+        console.log('socket error');
+        win.webContents.send('controller.socket.disconnect', null);
+        reject(true);
+
+      });
+
+
+    })
+
+  }
+  this.sendPing = function () {
+    const message = JSON.stringify({ data: 'node', type: 'ping' });
+    this.sendMessage(message);
+  }
+  this.sendMessage = async function (message) {
+    console.log('socket.=========================sendMessage');
+    console.log('socket.sendMessage');
+    console.log(message);
+    console.log(app.socket?.readyState);
+    if(!app.socket){
+      await  this.init()
+    }
+    // 检查连接状态
+    if (app.socket?.readyState === WebSocket.OPEN) {
+      console.log('send');
+      app.socket.send(message); // 使用 send() 发送
+    }
+  }
+
+  this.disconnect = function () {
+    if (app.socket) {
+      app.socket.close(); // 使用 close() 方法
+      app.socket = null;
+    }
+  }
+  this.onSocketMessage = async function (message_type,callback) {  // 监听消息事件
+    return new Promise(async (resolve,reject) => {
+      app.socket.on('message', onSocketMessage);
+      async function onSocketMessage(data){
+          try {
+            let this_data = JSON.parse(data.toString());
+            if(this_data.msg_type === message_type){
+              app.socket.off('message', onSocketMessage);
+              callback(this_data)
+              resolve()
+            }
+          }catch (e){
+            Log.error(e)
+            reject(e)
+          }
+      }
+    })
+  }
+  return this;
+}
+
+
+module.exports = pySocket;

+ 3 - 1
frontend/src/components/header-bar/index.vue

@@ -66,7 +66,9 @@
       </div>
     </div>
     <div class="header-bar__title">
-      <span class="header-bar__text">{{ title }}</span>
+      <span class="header-bar__text">
+        <slot name="title">{{ title }}</slot>
+      </span>
     </div>
     <div class="header-bar__buttons" v-if="showUser">
       <div class="header-bar__button header-bar__button__user">

+ 12 - 2
frontend/src/config.json

@@ -1,4 +1,14 @@
 {
-    "api": "https://dev2.valimart.net",
-    "tkkWebUrl": "https://tkk.valimart.net"
+    "dev": {
+        "api": "https://dev2.pubdata.cn",
+        "tkkWebUrl": "https://tkk.pubdata.cn"
+    },
+    "prod": {
+        "api": "https://dev2.valimart.net",
+        "tkkWebUrl": "https://tkk.valimart.net"
+    },
+    "local": {
+        "api": "",
+        "tkkWebUrl": "https://localhost:3000/tkk"
+    }
 }

+ 5 - 0
frontend/src/router/plugins/authGuard.ts

@@ -2,6 +2,7 @@ import { Router, useRoute } from 'vue-router'
 import useUserInfo from "@/stores/modules/user";
 import tokenInfo from "@/stores/modules/token";
 const route = useRoute()
+import pinia from "@/stores/index";
 /**
  * 除了注册页,当没有 token 则跳转至注册页
  * @param router
@@ -14,6 +15,10 @@ export function authGuard(router: Router) {
 
     const useUserInfoStore = useUserInfo();
     const tokenInfoStore = tokenInfo();
+    const appConfig = pinia.state.value.config?.appConfig;
+    if(!appConfig)  return next()
+
+
     if (tokenInfoStore.getToken /* 已登录 */) {
       if(!useUserInfoStore.userInfo.id){
           await useUserInfoStore.getInfo()

+ 0 - 6
frontend/src/stores/modules/check.ts

@@ -110,13 +110,7 @@ export const checkInfo = defineStore('checkInfo', () => {
 
               clientStore.ipc.removeAllListeners(icpList.camera.connect);
               clientStore.ipc.send(icpList.camera.connect,configInfoStore.digiCamControlPath);
-              clientStore.ipc.on(icpList.camera.digiCamControlPath, async (event, result) => {
-                clientStore.ipc.removeAllListeners(icpList.camera.digiCamControlPath);
-                if(result.code === 0 && result.data){
-                    configInfoStore.updateDigiCamControlPath(result.data)
-                }
 
-              })
               clientStore.ipc.on(icpList.camera.connect, async (event, result) => {
 
 

+ 27 - 1
frontend/src/stores/modules/config.ts

@@ -1,13 +1,37 @@
 import { defineStore } from 'pinia';
 import { ref, computed } from 'vue';
+import client from "./client";
+import icpList from "../../utils/ipc";
 
+const clientStore = client();
 
 export const configInfo = defineStore('config',()=>{
+  //作废了
   const digiCamControlPath = ref("C:\\Program Files (x86)\\digiCamControl")
   const updateDigiCamControlPath = (data:string)=>{
     digiCamControlPath.value = data
   }
 
+  const appConfig = ref({})
+
+
+  const getAppConfig = async ()=>{
+    return new Promise((resolve, reject) => {
+      clientStore.ipc.send(icpList.utils.getAppConfig);
+      clientStore.ipc.on(icpList.utils.getAppConfig, async (event, result) => {
+        appConfig.value = result;
+        resolve(appConfig.value)
+      })
+    })
+  }
+
+
+  clientStore.ipc.send(icpList.utils.getAppConfig);
+  clientStore.ipc.on(icpList.utils.getAppConfig, async (event, result) => {
+    appConfig.value = result;
+  })
+
+
   //  1 为拍照并处理图像 2 为仅处理图像
   const appModel = ref(1)
   const updateAppModel = (data:number)=>{
@@ -17,7 +41,9 @@ export const configInfo = defineStore('config',()=>{
     digiCamControlPath,
     updateDigiCamControlPath,
     appModel,
-    updateAppModel
+    updateAppModel,
+    appConfig,
+    getAppConfig,
   }
 },{
   persist:true,

+ 8 - 2
frontend/src/utils/appfun.ts

@@ -1,7 +1,8 @@
 
-import { configs } from '@/utils/appconfig';
+import ENV_CONFIG from "@/config.json";
 import tokenInfo from '@/stores/modules/token';
 const tokenInfoStore = tokenInfo();
+import configInfo from "@/stores/modules/config";
 
 //获取文件路径
 export  function getFilePath (file_path){
@@ -23,6 +24,11 @@ export  function getWebUrlrUrl (config:{
     query:Object
 }){
 
+    const useConfigInfoStore = configInfo();
+    let env =  useConfigInfoStore.appConfig.env
+    const tkkWebUrl = ENV_CONFIG[env]?.tkkWebUrl || 'https://tkk.valimart.net';
+
+
     let params = '?source=camera&token=' + tokenInfoStore.getToken
     if(config.query){
         params +=  '&'
@@ -30,6 +36,6 @@ export  function getWebUrlrUrl (config:{
             return encodeURIComponent(key) + '=' + encodeURIComponent(config.query[key])
         }).join('&')
     }
-    let url  = configs.tkkWebUrl + config.url + params
+    let url  = tkkWebUrl + config.url + params
     return url
 }

+ 16 - 1
frontend/src/utils/http.ts

@@ -2,6 +2,8 @@ import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
 import { ElMessage as Message, ElMessageBox as MessageBox, ElLoading as Loading } from 'element-plus';
 import  tokenInfo  from '@/stores/modules/token';
 import useUserInfo from "@/stores/modules/user";
+import pinia from "@/stores/index";
+import ENV_CONFIG from "@/config.json";
 
 // 加载动画的并发管理
 const activeRequests = new Set<string>();
@@ -24,13 +26,26 @@ function loadingClose(requestId: string) {
  */
 const service = axios.create({
     timeout: 5000, // 设置请求超时时间
-    baseURL: '__API__',
+   // baseURL: '__API__',
 });
 console.log('__API__');
 
 // 请求拦截器
 service.interceptors.request.use(
     (config: AxiosRequestConfig) => {
+
+        // 动态设置baseURL
+        const appConfig = pinia.state.value.config?.appConfig;
+        let env = appConfig?.env || 'prod'; // 默认环境
+        if (env) {
+            // 从ENV_CONFIG获取对应环境的API地址
+            const apiConfig = ENV_CONFIG[env];
+            if (apiConfig?.api) {
+                config.baseURL = apiConfig.api;
+            }
+        }
+        console.log(`使用环境: ${env}, API地址: ${config.baseURL || '__API__'}`);
+
         // 在发送请求之前做些什么,例如添加 token
         const tokenInfoStore = tokenInfo();
         const token = tokenInfoStore.getToken; // 使用 getToken() 获取 token

+ 1 - 0
frontend/src/utils/ipc.ts

@@ -19,6 +19,7 @@ const icpList = {
         shellFun: 'controller.utils.shellFun',
         openDirectory:"controller.utils.openDirectory",
         openImage:"controller.utils.openImage",
+        getAppConfig:"controller.utils.getAppConfig",
         openFile:"controller.utils.openFile"
     },
     setting:{

+ 71 - 27
frontend/src/views/Photography/check.vue

@@ -64,6 +64,7 @@
       @confirm="confirm"
       ref="editData"
       @onClose="onClose"
+      @onRunMcuSingle="onRunMcuSingle"
       :addRowData="addRowData"
     />
 
@@ -86,6 +87,10 @@ const socketStore = socket(); // WebSocket状态管理实例
 
 const emit = defineEmits([ 'confirm','onClose']);
 
+
+import  configInfo  from '@/stores/modules/config';
+const configInfoStore = configInfo();
+
 const confirm = ()=>{
   hideVideo()
   emit('confirm')
@@ -120,14 +125,17 @@ import { digiCamControlWEB } from  '@/utils/appconfig'
 import { getFilePath } from '@/utils/appfun'
 import {ElMessage} from "element-plus";
 const previewKey = ref(0)
+
 const preview = ref(digiCamControlWEB+'liveview.jpg')
 
+
+
 const previewSrc = computed(()=>{
   let time = new Date().getTime()
   return preview.value+'?key='+previewKey.value+'&time='+time
 })
 const step = ref(1)
-function checkConfirm(init){
+async function checkConfirm(init){
   step.value =1
   if(menu.length === 0){
     menu.push({
@@ -143,6 +151,13 @@ function checkConfirm(init){
   showrEditRow.value = true
 }
 
+const init = ref(true)
+function onRunMcuSingle (){
+  if(init.value) {
+    loading.value = false
+    init.value = false
+  }
+}
 const showrEditRow = ref(false)
 
 
@@ -160,7 +175,7 @@ function showVideo(){
         interval = setInterval(()=>{
           previewKey.value++;
         },200)
-      },2000)
+      },500)
 
     })
 
@@ -176,7 +191,10 @@ function hideVideo(){
 
 }
 
-const loading = ref(false)
+function goArts(){
+
+}
+const loading = ref(true)
 const editData = ref(null);
 const imageTplPath = ref(null)
 function takePictures() {
@@ -212,33 +230,51 @@ function takePictures() {
   }
 }
 
+// 获取主图
+function  createMainImage (file_path){
+
+  loading.value = true;
+  clientStore.ipc.removeAllListeners(icpList.takePhoto.createMainImage);
+  clientStore.ipc.send(icpList.takePhoto.createMainImage,{
+    file_path: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()
+      step.value = 2
+      loading.value = false;
+    }else if(result.msg){
+      loading.value = false;
+      showVideo()
+      if(result.code !== 0) ElMessage.error(result.msg)
+    }
+    clientStore.ipc.removeAllListeners(icpList.takePhoto.createMainImage);
+
+
+  });
+}
+
+
+//拍照成功  SmartShooter
+clientStore.ipc.on(icpList.socket.message+'_smart_shooter_photo_take', async (event, result) => {
+  console.log('_smart_shooter_photo_take');
+  console.log(result);
+  if(result.code === 0 && result.data?.photo_file_name){
+    createMainImage(result.data?.photo_file_name)
+  }
+
+})
+
+
+//运行的时候  直接拍照  digiCamControl
 clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, result) => {
-  console.log('_run_mcu_single_check')
+  console.log('_run_mcu_single_check_on')
   console.log(result)
 
   if(result.code === 0 && result.data?.file_path){
-    clientStore.ipc.removeAllListeners(icpList.takePhoto.createMainImage);
-    clientStore.ipc.send(icpList.takePhoto.createMainImage,{
-      file_path:result.data.file_path
-    });
-    clientStore.ipc.on(icpList.takePhoto.createMainImage, async (event, result) => {
-      console.log('icpList.utils.createMainImage');
-      console.log(result);
-      if(result.code === 0 && result.data?.main_out_path){
-        imageTplPath.value  = result.data?.main_out_path
-        hideVideo()
-        step.value = 2
-        loading.value = false;
-      }else if(result.msg){
-        loading.value = false;
-        showVideo()
-        if(result.code !== 0) ElMessage.error(result.msg)
-      }
-      clientStore.ipc.removeAllListeners(icpList.takePhoto.createMainImage);
-
-
-    });
 
+    createMainImage(result.data?.file_path)
 
   }else if(result.msg){
     if( result.msg.indexOf('处理失败,请重试') >= 0){
@@ -250,8 +286,12 @@ clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, resul
   }
 
 })
-onMounted(()=>{
-  showVideo()
+onMounted(async ()=>{
+  if(isSetting.value)   showVideo()
+  await  configInfoStore.getAppConfig()
+  if(configInfoStore.appConfig.controlType === "SmartShooter"){
+    preview.value = configInfoStore.appConfig.userDataPath  + "\\preview\\liveview.png"
+  }
 })
 
 /**
@@ -262,6 +302,10 @@ onBeforeUnmount(() => {
   clientStore.ipc.removeAllListeners(icpList.camera.takePictures);
   clientStore.ipc.removeAllListeners(icpList.camera.PreviewHide);
   clientStore.ipc.removeAllListeners(icpList.camera.PreviewShow);
+  clientStore.ipc.removeAllListeners(icpList.socket.message+'_run_mcu_single');
+  clientStore.ipc.removeAllListeners(icpList.socket.message+'_smart_shooter_photo_take');
+
+
 })
 </script>
 <style scoped lang="scss">

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

@@ -204,15 +204,16 @@ function testShoesFlip(){
 
 
     clientStore.ipc.on(icpList.socket.message+'_run_mcu_single', async (event, result) => {
-      console.log('_run_mcu_single_row')
 
       captureLoading.value = false;
 
+      emit('onRunMcuSingle')
+
     })
   }
 }
 
-const emit = defineEmits([ 'confirm','onClose']);
+const emit = defineEmits([ 'confirm','onClose','onRunMcuSingle']);
 const close = ()=>{
   console.log('onClose')
   emit('onClose')

+ 43 - 3
frontend/src/views/Photography/shot.vue

@@ -108,12 +108,13 @@
                       </template>
                     </el-dropdown>
 
-                    <el-button size="small" :disabled="runLoading || takePictureLoading"  type="primary"  @click="reTakePictureNos(item.goods_art_no,item)" plain v-if="configInfoStore.appModel === 1">重拍</el-button>
                     <el-button size="small" :disabled="runLoading || takePictureLoading" @click="delGoods({goods_art_nos:[item.goods_art_no]})">删除</el-button>
                   </div>
                 </div>
-                <div class="flex  between flex-item  c-333">
+                <div class="flex  between flex-item  c-333" style="margin-top: 5px">
                   <div class="c-999 fs-12">{{ getTime(item.action_time) }}</div>
+                  <el-button size="small" :disabled="runLoading || takePictureLoading"  type="primary"  @click="reTakePictureNos(item.goods_art_no,item)" plain v-if="configInfoStore.appModel === 1">重拍</el-button>
+
                 </div>
                 <div class="mar-top-10 clearfix history-item_image_wrap" style="width: 100%" >
                     <component class="history-item_image"
@@ -544,7 +545,7 @@ const reTakePicture = async (img)=>{
 
     clientStore.ipc.removeAllListeners(icpList.socket.message + '_digicam_take_picture');
     socketStore.sendMessage({
-      type: 'digicam_take_picture',
+      type: 'smart_shooter_photo_take',
       "data":{"id":img.id,"goods_art_no":img.goods_art_no},
     })
     clientStore.ipc.on(icpList.socket.message + '_digicam_take_picture', (event, result) => {
@@ -816,6 +817,7 @@ onBeforeUnmount(() => {
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_photo_take_finish');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_update');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_stop_action');
+  clientStore.ipc.removeAllListeners(icpList.socket.message + '_smart_shooter_photo_take');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_run_mcu_stop');
   clientStore.ipc.removeAllListeners(icpList.socket.message + '_digicam_take_picture');
 
@@ -856,6 +858,44 @@ const  getLastPhotoRecord = async ()=>{
   });
 }
 
+let smartShooterTimeout = null; // 在合适的位置定义一个全局变量用于保存定时器
+
+//拍照成功  SmartShooter
+clientStore.ipc.on(icpList.socket.message+'_smart_shooter_photo_take', async (event, result) => {
+  console.log('_smart_shooter_photo_take');
+  console.log(result);
+
+  if( reNosObj.value.goods_art_no ){
+
+
+    runLoading.value = false;
+    if(result.code === 0){
+      getPhotoRecords()
+      setTimeout(()=>{
+        showlastPhoto.value = false
+      },6000)
+    }else if(result.msg) {
+
+      runLoading.value = false;
+      reNosObj.value.goods_art_no = ''
+      reNosObj.value.action = ''
+      ElMessage.error(result.msg)
+    }
+
+  }else{
+
+    if(result.code === 0 && result.data?.photo_file_name){
+      if (smartShooterTimeout) {
+        clearTimeout(smartShooterTimeout);
+      }
+      smartShooterTimeout = setTimeout(() => {
+        getPhotoRecords();
+      }, 2000);
+    }
+  }
+
+})
+
 
 // 监听拍照完成后的最终状态事件
 clientStore.ipc.on(icpList.socket.message + '_run_mcu_update', (event, result) => {

+ 53 - 0
frontend/src/views/Setting/components/DebugPanel.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="debug-panel" v-if="isVisible">
+    <div class="form-item flex left">
+        <el-button @click="openResourceDirectory">打开资源目录</el-button>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import client from "@/stores/modules/client";
+import  configInfo  from '@/stores/modules/config';
+import icpList from '@/utils/ipc';
+
+const clientStore = client();
+const configInfoStore = configInfo();
+
+const isVisible = ref(false);
+
+function showDebugPanel() {
+  isVisible.value = true;
+}
+
+function hideDebugPanel() {
+  isVisible.value = false;
+}
+
+function toggleVisibility() {
+  isVisible.value = !isVisible.value;
+}
+
+function openResourceDirectory() {
+  clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
+  let params = {
+    action: 'openPath',
+    params: configInfoStore.appConfig.userDataPath.replaceAll('/', '\\')
+  };
+  clientStore.ipc.send(icpList.utils.shellFun, params);
+}
+
+// 挂载时注册事件监听器
+defineExpose({
+  showDebugPanel,
+  hideDebugPanel,
+  toggleVisibility
+});
+</script>
+
+<style scoped>
+.debug-panel {
+  padding-bottom: 10px;
+}
+</style>

+ 32 - 2
frontend/src/views/Setting/index.vue

@@ -1,7 +1,8 @@
 <template>
   <headerBar
-    title="设置"
-  />
+  >
+    <template  #title><div @click="handleSettingClick">设置</div></template>
+  </headerBar>
   <div class="container">
     <nav class="settings-nav">
       <div class="nav-item" :class="{'active': activeIndex === 0}" @click="toggleTab(0)">
@@ -66,6 +67,8 @@
                     </el-select>
                     </div>
                 </div>
+
+               <DebugPanel ref="debugPanel" />
         </div>
       <!--基础配置-->
       <!--其他设置-->
@@ -147,6 +150,33 @@ import actionConfig from './components/action_config.vue'
 import otherConfig from './components/otherConfig'
 useCheckInfo();
 
+//点击三次  打开资源目录
+
+import DebugPanel from './components/DebugPanel.vue';
+// 在setup函数中创建调试面板实例
+const debugPanel = ref(null);
+// 添加设置点击计数器
+const settingClickCount = ref(0);
+
+// 修改headerBar的点击处理函数
+function handleSettingClick() {
+  console.log('handleSettingClickhandleSettingClick')
+  settingClickCount.value++;
+
+  if (settingClickCount.value >= 3) {
+    if (debugPanel.value) {
+      debugPanel.value.showDebugPanel();
+    }
+    settingClickCount.value = 0;
+  }
+
+  setTimeout(() => {
+    settingClickCount.value = 0;
+  }, 3000); // 3秒内未再次点击则重置计数器
+}
+
+
+
 // 路由和状态管理初始化
 const route = useRoute();
 const router = useRouter();

+ 2 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "name": "ZhiHuiYin",
-  "version": "1.1.2",
-  "description": "1、优化通过excel导入以及系统对接时,针对历史已经抠图和生成详情页的数据进行过滤操作\n2、提升抠图和详情页的生成速度",
+  "version": "1.2.0",
+  "description": "1、支持SmartShooter5软件控制相机",
   "main": "main.js",
   "scripts": {
     "dev": "ee-bin dev",

+ 1 - 1
public/dist/index.html

@@ -5,7 +5,7 @@
     <link rel="icon" type="image/svg+xml" href="./vite.svg" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <title>智惠映AI自动拍照机</title>
-    <script type="module" crossorigin src="./assets/index-CBoEZzqz.js"></script>
+    <script type="module" crossorigin src="./assets/index-Dx7tl9Wa.js"></script>
     <link rel="stylesheet" crossorigin href="./assets/index-CWbjMgyJ.css">
   </head>
   <body>