ef-compiler.js 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // EasyFlow 编译器 - 工作流任务解析和执行器(主机,单文件 ≤500 行)
  2. // ========== 入口说明:外部通过 require 本模块后,使用下方 module.exports 导出的 5 个方法作为调用入口 ==========
  3. const path = require('path')
  4. // --- 配置(原 compiler-config.js 合并到此)---
  5. const projectRoot = path.resolve(__dirname, '..', '..')
  6. const funcDir = path.join(__dirname, 'fun')
  7. const adbInteractPath = path.join(projectRoot, 'nodejs', 'adb', 'adb-interact.js')
  8. const DEFAULT_STEP_INTERVAL = 1000
  9. const DEFAULT_SCROLL_DISTANCE = 100
  10. const compilerConfig = { projectRoot, funcDir, adbInteractPath, DEFAULT_STEP_INTERVAL, DEFAULT_SCROLL_DISTANCE }
  11. // --- 依赖 ---
  12. const setParser = require('./components/actions/set-parser.js')
  13. const expressionEvaluator = require('./components/expression-evaluator.js')
  14. const runtimeApi = require('./components/runtime-api.js')
  15. const workflowJsonParser = require('./components/workflow-json-parser.js')
  16. const sequenceRunner = require('./components/sequence-runner.js')
  17. const actions = require('./components/actions/fun-parser.js')
  18. // --- 功能模块(fun 目录)与运行时 API ---
  19. const { matchImageAndGetCoordinate } = require('./fun/img-center-point-location.js')
  20. const { readTextFile } = require('./fun/read-txt.js')
  21. const { writeTextFile } = require('./fun/save-txt.js')
  22. const electronAPI = runtimeApi.createElectronAPI({ matchImageAndGetCoordinate, readTextFile, writeTextFile }, compilerConfig)
  23. // --- 共享状态(变量上下文、步骤计数、当前工作流目录等)---
  24. const state = {
  25. variableContext: {},
  26. variableContextInitialized: false,
  27. globalStepCounter: 0,
  28. currentWorkflowFolderPath: null,
  29. }
  30. // --- 从各组件抽出的工具方法(供本文件与 ctx 使用)---
  31. const extractVarName = setParser.extractVarName
  32. const replaceVariablesInString = setParser.replaceVariablesInString
  33. const resolveValue = setParser.resolveValue
  34. const parseDelayString = setParser.parseDelayString
  35. const calculateWaitTime = setParser.calculateWaitTime
  36. const evaluateCondition = expressionEvaluator.evaluateCondition
  37. const evaluateExpression = expressionEvaluator.evaluateExpression
  38. const getActionName = workflowJsonParser.getActionName
  39. // --- 日志与变量输出 ---
  40. async function logMessage(message, folderPath = null) {
  41. try {
  42. const targetFolderPath = folderPath || state.currentWorkflowFolderPath
  43. if (targetFolderPath && electronAPI.appendLog) await electronAPI.appendLog(targetFolderPath, message)
  44. } catch (err) {}
  45. }
  46. async function logOutVars(action, variableContext, folderPath = null) {
  47. if (!action.outVars || !Array.isArray(action.outVars) || action.outVars.length === 0) return
  48. const outVarsInfo = action.outVars.map((varName) => {
  49. const varNameClean = extractVarName(varName)
  50. const value = variableContext[varNameClean]
  51. let displayValue = value
  52. if (typeof value === 'string' && value.length > 100) displayValue = value.substring(0, 100) + '...'
  53. return `${varNameClean}: ${JSON.stringify(displayValue)}`
  54. })
  55. }
  56. // --- 对外入口 1:解析整份工作流 ---
  57. function parseWorkflow(workflow) {
  58. const loaderState = {
  59. variableContext: state.variableContext,
  60. getInitialized: () => state.variableContextInitialized,
  61. setInitialized: (v) => { state.variableContextInitialized = v },
  62. }
  63. return workflowJsonParser.parseWorkflow(workflow, loaderState)
  64. }
  65. // --- 对外入口 2:仅解析动作列表 ---
  66. function parseActions(actions) {
  67. const loaderState = {
  68. variableContext: state.variableContext,
  69. getInitialized: () => state.variableContextInitialized,
  70. setInitialized: () => {},
  71. }
  72. return workflowJsonParser.parseActions(actions, loaderState)
  73. }
  74. // --- 对外入口 3:执行单条动作 ---
  75. async function executeAction(action, device, folderPath, resolution) {
  76. const ctx = {
  77. variableContext: state.variableContext,
  78. compilerConfig,
  79. electronAPI,
  80. extractVarName,
  81. replaceVariablesInString,
  82. resolveValue,
  83. evaluateCondition,
  84. evaluateExpression,
  85. getActionName,
  86. logMessage,
  87. logOutVars,
  88. parseDelayString,
  89. calculateWaitTime,
  90. DEFAULT_SCROLL_DISTANCE,
  91. registry: workflowJsonParser.registry,
  92. executeAction: workflowJsonParser.executeAction,
  93. }
  94. try {
  95. return await actions.runAction(action, device, folderPath, resolution, ctx)
  96. } catch (error) {
  97. const now = new Date()
  98. const timeStr = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}/${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}:${String(now.getSeconds()).padStart(2, '0')}`
  99. const errorMsg = `[错误] 操作执行失败: ${error.message} [系统时间: ${timeStr}]`
  100. await logMessage(errorMsg, folderPath).catch(() => {})
  101. return { success: false, error: error.message }
  102. }
  103. }
  104. // --- 对外入口 4:按顺序执行动作序列(含 schedule/if/for/while 等)---
  105. async function executeActionSequence(actions, device, folderPath, resolution, stepInterval = DEFAULT_STEP_INTERVAL, onStepComplete = null, shouldStop = null, depth = 0) {
  106. const ctx = {
  107. executeAction,
  108. logMessage,
  109. evaluateCondition,
  110. getActionName,
  111. parseDelayString,
  112. calculateWaitTime,
  113. state,
  114. DEFAULT_STEP_INTERVAL,
  115. }
  116. return sequenceRunner.executeActionSequence(actions, device, folderPath, resolution, stepInterval, onStepComplete, shouldStop, depth, ctx)
  117. }
  118. // --- 编译/执行入口:对外暴露的 API ---
  119. module.exports = {
  120. parseWorkflow,
  121. parseActions,
  122. executeAction,
  123. executeActionSequence,
  124. }