/** * laser_cal Node.js 调用封装 * * 支持三种调用模式: * 1. module 模式:通过 Python 解释器 + laser_wrapper.py 加载 .pyd/.so 共享库 * 2. exe 模式:直接调用编译后的可执行程序(standalone 目录) * 3. onefile 模式:直接调用编译后的单文件可执行程序 * * 使用示例: * const laser = require('./laser'); * * // 自动检测模式 * const result = await laser.callLaser('getpath'); * const result = await laser.callLaser('loaddata', base64JsonData); */ const { execFile } = require('child_process'); const path = require('path'); const fs = require('fs'); // ============================================================ // 配置区(根据实际部署路径修改) // ============================================================ const PROJECT_NAME = 'laser_cal'; const DIST_DIR = path.join(__dirname, '..', 'dist', 'program'); const WRAPPER_SCRIPT = path.join(__dirname, 'laser_wrapper.py'); // Python 路径(module 模式需要) // 可通过环境变量 PYTHON_PATH 覆盖 const PYTHON_CMD = process.env.PYTHON_PATH || (process.platform === 'win32' ? path.join(__dirname, '..', 'myvenv', 'Scripts', 'python.exe') : 'python3'); // ============================================================ // 自动检测可用的调用模式 // ============================================================ function detectMode() { const exeName = process.platform === 'win32' ? `${PROJECT_NAME}.exe` : PROJECT_NAME; // 优先检测 onefile 模式(exe 在 dist/program/ 下) const onefilePath = path.join(DIST_DIR, exeName); if (fs.existsSync(onefilePath)) { return { mode: 'exe', execPath: onefilePath }; } // 检测 standalone 模式(exe 在 dist/program/api_test.dist/ 下) const standalonePath = path.join(DIST_DIR, 'api_test.dist', exeName); if (fs.existsSync(standalonePath)) { return { mode: 'exe', execPath: standalonePath }; } // 回退到 module 模式(使用 Python + wrapper) return { mode: 'module', execPath: null }; } // ============================================================ // 核心调用函数 // ============================================================ /** * 调用 laser_cal 后端 * @param {string} apiName - API 名称: getpath | loaddata | historydata | deletedata * @param {string|null} b64Data - Base64 编码的 JSON 参数(loaddata/historydata/deletedata 需要) * @returns {Promise} - 解析后的 JSON 结果 */ function callLaser(apiName, b64Data = null) { const detected = detectMode(); if (detected.mode === 'exe') { return callExe(detected.execPath, apiName, b64Data); } else { return callModule(apiName, b64Data); } } /** * 直接调用可执行文件(exe/standalone 模式) */ function callExe(exePath, apiName, b64Data = null) { return new Promise((resolve, reject) => { const args = [apiName]; if (b64Data) { args.push(b64Data); } const options = { maxBuffer: 1024 * 1024 * 50, cwd: path.dirname(exePath) // 设置工作目录为 exe 所在目录(确保找到 license) }; execFile(exePath, args, options, (error, stdout, stderr) => { if (error) { return reject(new Error(`Execution failed: ${error.message}\nStderr: ${stderr}`)); } parseOutput(stdout, resolve, reject); }); }); } /** * 通过 Python 解释器调用(module 模式) */ function callModule(apiName, b64Data = null) { return new Promise((resolve, reject) => { const args = [WRAPPER_SCRIPT, apiName]; if (b64Data) { args.push(b64Data); } const options = { maxBuffer: 1024 * 1024 * 50 }; execFile(PYTHON_CMD, args, options, (error, stdout, stderr) => { if (error) { return reject(new Error(`Python execution failed: ${error.message}\nStderr: ${stderr}`)); } parseOutput(stdout, resolve, reject); }); }); } /** * 解析 stdout JSON 输出 */ function parseOutput(stdout, resolve, reject) { try { const lines = stdout.trim().split('\n'); const lastLine = lines[lines.length - 1]; const result = JSON.parse(lastLine); if (result.error) { reject(new Error(result.error)); } else { resolve(result); } } catch (e) { reject(new Error(`Failed to parse output: ${stdout}\nError: ${e.message}`)); } } // ============================================================ // 兼容旧接口 // ============================================================ /** * 兼容旧版 callPython 接口 */ function callPython(apiName, b64Data = null) { return callLaser(apiName, b64Data); } // ============================================================ // 导出 // ============================================================ module.exports = { callLaser, callPython, // 向后兼容 callExe, callModule, detectMode };