|
@@ -11,8 +11,11 @@ const { spawnSync } = require('child_process')
|
|
|
const configPath = process.env.STATIC_ROOT
|
|
const configPath = process.env.STATIC_ROOT
|
|
|
? path.join(path.dirname(process.env.STATIC_ROOT), 'configs', 'config.js')
|
|
? path.join(path.dirname(process.env.STATIC_ROOT), 'configs', 'config.js')
|
|
|
: path.join(__dirname, '..', '..', '..', '..', 'configs', 'config.js')
|
|
: path.join(__dirname, '..', '..', '..', '..', 'configs', 'config.js')
|
|
|
-const projectRoot = path.dirname(path.dirname(path.resolve(configPath)))
|
|
|
|
|
const config = fs.existsSync(configPath) ? require(configPath) : {}
|
|
const config = fs.existsSync(configPath) ? require(configPath) : {}
|
|
|
|
|
+// 打包后优先使用 config.projectRoot(如 package/x64/config.js 中的配置),否则按路径推导
|
|
|
|
|
+const projectRoot = (config.projectRoot && fs.existsSync(config.projectRoot))
|
|
|
|
|
+ ? config.projectRoot
|
|
|
|
|
+ : path.dirname(path.dirname(path.resolve(configPath)))
|
|
|
const imageMatchScriptPath = path.join(projectRoot, 'python', 'scripts', 'image-match.py')
|
|
const imageMatchScriptPath = path.join(projectRoot, 'python', 'scripts', 'image-match.py')
|
|
|
|
|
|
|
|
const tagName = 'img-center-point-location'
|
|
const tagName = 'img-center-point-location'
|
|
@@ -23,7 +26,7 @@ const schema = {
|
|
|
outputs: { variable: '中心点坐标(JSON 字符串格式,如:{"x":123,"y":456})' },
|
|
outputs: { variable: '中心点坐标(JSON 字符串格式,如:{"x":123,"y":456})' },
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** 解析 Python 可执行路径(与 config 中 pythonPath / pythonVenvPath 一致) */
|
|
|
|
|
|
|
+/** 解析 Python 可执行路径(与 config 中 pythonPath / pythonVenvPath 一致)。优先用 env(venv)以包含 opencv/numpy 等依赖。 */
|
|
|
function getPythonPath() {
|
|
function getPythonPath() {
|
|
|
const base = config.pythonPath?.path || config.pythonVenvPath || path.join(projectRoot, 'python', process.arch === 'arm64' ? 'arm64' : 'x64')
|
|
const base = config.pythonPath?.path || config.pythonVenvPath || path.join(projectRoot, 'python', process.arch === 'arm64' ? 'arm64' : 'x64')
|
|
|
const envPy = path.join(base, 'env', 'Scripts', 'python.exe')
|
|
const envPy = path.join(base, 'env', 'Scripts', 'python.exe')
|
|
@@ -43,6 +46,7 @@ function matchImageAndGetCoordinate(device, imagePath, scaleRange, centerRatio)
|
|
|
const maxScale = Number(scaleRange[1])
|
|
const maxScale = Number(scaleRange[1])
|
|
|
if (Number.isNaN(minScale) || Number.isNaN(maxScale) || minScale >= maxScale) return { success: false, error: '缩放比范围无效,需为两个数字且 min < max' }
|
|
if (Number.isNaN(minScale) || Number.isNaN(maxScale) || minScale >= maxScale) return { success: false, error: '缩放比范围无效,需为两个数字且 min < max' }
|
|
|
const templatePath = path.isAbsolute(imagePath) ? imagePath : path.resolve(projectRoot, imagePath)
|
|
const templatePath = path.isAbsolute(imagePath) ? imagePath : path.resolve(projectRoot, imagePath)
|
|
|
|
|
+ if (!fs.existsSync(templatePath)) return { success: false, error: `模板文件不存在: ${templatePath}` }
|
|
|
const ts = Date.now()
|
|
const ts = Date.now()
|
|
|
const templateDir = path.dirname(templatePath)
|
|
const templateDir = path.dirname(templatePath)
|
|
|
const templateBase = path.basename(templatePath, path.extname(templatePath))
|
|
const templateBase = path.basename(templatePath, path.extname(templatePath))
|
|
@@ -67,17 +71,31 @@ function matchImageAndGetCoordinate(device, imagePath, scaleRange, centerRatio)
|
|
|
if (ratio < 1) args.push('--center-ratio', String(ratio))
|
|
if (ratio < 1) args.push('--center-ratio', String(ratio))
|
|
|
}
|
|
}
|
|
|
if (hasCrop) args.push('--template-output', templatePath.replace(/\\/g, '/'))
|
|
if (hasCrop) args.push('--template-output', templatePath.replace(/\\/g, '/'))
|
|
|
- const r = spawnSync(pythonPath, args, {
|
|
|
|
|
- encoding: 'utf-8',
|
|
|
|
|
- timeout: 20000,
|
|
|
|
|
- env: { ...process.env, PYTHONIOENCODING: 'utf-8' },
|
|
|
|
|
- cwd: projectRoot
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const env = { ...process.env, PYTHONIOENCODING: 'utf-8' }
|
|
|
|
|
+ if (process.platform === 'win32') {
|
|
|
|
|
+ const pyDir = path.dirname(pythonPath)
|
|
|
|
|
+ const pyRoot = path.dirname(path.dirname(pyDir))
|
|
|
|
|
+ env.PATH = [pyDir, pyRoot, process.env.PATH].filter(Boolean).join(path.delimiter)
|
|
|
|
|
+ }
|
|
|
|
|
+ const spawnOpts = { encoding: 'utf-8', timeout: 20000, env, cwd: projectRoot }
|
|
|
|
|
+ const r = spawnSync(pythonPath, args, spawnOpts)
|
|
|
try { fs.unlinkSync(templateCopyPath) } catch (_) {}
|
|
try { fs.unlinkSync(templateCopyPath) } catch (_) {}
|
|
|
// 截图保留在模板同级目录(Screenshot-pic0.png 等),便于排查匹配失败原因
|
|
// 截图保留在模板同级目录(Screenshot-pic0.png 等),便于排查匹配失败原因
|
|
|
|
|
|
|
|
- if (r.status !== 0) return { success: false, error: (r.stderr || r.stdout || '').trim() || '图像匹配失败' }
|
|
|
|
|
- const out = JSON.parse(r.stdout.trim())
|
|
|
|
|
|
|
+ if (r.status !== 0) {
|
|
|
|
|
+ const msg = [r.stderr, r.stdout].filter(Boolean).map(s => String(s).trim()).join('\n') || '图像匹配失败'
|
|
|
|
|
+ const extra = r.signal ? ` [signal: ${r.signal}]` : (r.error ? ` [${r.error.message}]` : '')
|
|
|
|
|
+ const scriptExists = fs.existsSync(imageMatchScriptPath)
|
|
|
|
|
+ const pyExists = fs.existsSync(pythonPath)
|
|
|
|
|
+ const diag = ` | projectRoot=${projectRoot} script存在=${scriptExists} python存在=${pyExists} status=${r.status}`
|
|
|
|
|
+ return { success: false, error: msg + extra + diag }
|
|
|
|
|
+ }
|
|
|
|
|
+ let out
|
|
|
|
|
+ try {
|
|
|
|
|
+ out = JSON.parse(r.stdout.trim())
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ return { success: false, error: `脚本输出非 JSON: ${(r.stdout || r.stderr || '').slice(0, 200)}` }
|
|
|
|
|
+ }
|
|
|
if (!out.success) return { success: false, error: out.error || '未找到图片' }
|
|
if (!out.success) return { success: false, error: out.error || '未找到图片' }
|
|
|
return {
|
|
return {
|
|
|
success: true,
|
|
success: true,
|