/** * 执行操作序列(schedule/if/for/while + 普通步骤) * 单文件 ≤500 行。ctx: executeAction, logMessage, evaluateCondition, getActionName, parseDelayString, calculateWaitTime, state * state: variableContext, globalStepCounter, currentWorkflowFolderPath, variableContextInitialized */ async function executeActionSequence( actions, device, folderPath, resolution, stepInterval, onStepComplete, shouldStop, depth, ctx ) { const { executeAction, logMessage, evaluateCondition, getActionName, parseDelayString, calculateWaitTime, state } = ctx const variableContext = state.variableContext const DEFAULT_STEP_INTERVAL = ctx.DEFAULT_STEP_INTERVAL ?? 1000 if (depth === 0) { state.globalStepCounter = 0 state.variableContextInitialized = false state.currentWorkflowFolderPath = folderPath await logMessage('========================', folderPath) } let completedSteps = 0 const interval = stepInterval ?? DEFAULT_STEP_INTERVAL for (let i = 0; i < actions.length; i++) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } const action = actions[i] if (action.type === 'schedule') { const condition = action.condition || {} const intervalStr = condition.interval || '0s' const repeat = condition.repeat !== undefined ? condition.repeat : 1 const actionsToExecute = action.interval || [] const intervalMs = parseDelayString(intervalStr) || 0 const maxIterations = repeat === -1 ? Infinity : (typeof repeat === 'number' ? repeat : 1) let iteration = 0 while (iteration < maxIterations) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } iteration++ if (iteration > 1 && intervalMs > 0) { let remainingTime = intervalMs const countdownInterval = 100 while (remainingTime > 0) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } const waitTime = Math.min(countdownInterval, remainingTime) await new Promise(resolve => setTimeout(resolve, waitTime)) remainingTime -= waitTime } } if (actionsToExecute.length > 0) { const result = await executeActionSequence(actionsToExecute, device, folderPath, resolution, interval, onStepComplete, shouldStop, depth + 1, ctx) if (!result.success) return result completedSteps += result.completedSteps || 0 } } continue } if (action.type === 'if') { const conditionResult = evaluateCondition(action.condition, variableContext) const actionsToExecute = conditionResult ? (action.then || action.ture || []) : (action.else || action.false || []) if (actionsToExecute.length > 0) { const result = await executeActionSequence(actionsToExecute, device, folderPath, resolution, interval, onStepComplete, shouldStop, depth + 1, ctx) if (!result.success) return result completedSteps += result.completedSteps || 0 } continue } if (action.type === 'for') { const items = Array.isArray(action.items) ? action.items : [] for (const item of items) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } if (action.variable) variableContext[action.variable] = item if (action.body && action.body.length > 0) { const result = await executeActionSequence(action.body, device, folderPath, resolution, interval, onStepComplete, shouldStop, depth + 1, ctx) if (!result.success) return result completedSteps += result.completedSteps || 0 } } continue } if (action.type === 'while') { while (evaluateCondition(action.condition, variableContext)) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } if (action.body && action.body.length > 0) { const result = await executeActionSequence(action.body, device, folderPath, resolution, interval, onStepComplete, shouldStop, depth + 1, ctx) if (!result.success) return result completedSteps += result.completedSteps || 0 } } continue } const times = action.times || 1 if (onStepComplete) onStepComplete(i + 1, actions.length, getActionName(action), 0, times, 0) const waitTime = calculateWaitTime(action.data, action.delay) if (waitTime > 0) { let remainingTime = waitTime const countdownInterval = 100 const stepName = getActionName(action) while (remainingTime > 0) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } if (onStepComplete) onStepComplete(i + 1, actions.length, stepName, remainingTime, times, 0) const waitTimeChunk = Math.min(countdownInterval, remainingTime) await new Promise(resolve => setTimeout(resolve, waitTimeChunk)) remainingTime -= waitTimeChunk } } for (let t = 0; t < times; t++) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } if (onStepComplete) onStepComplete(i + 1, actions.length, getActionName(action), 0, times, t + 1) state.globalStepCounter++ const typeName = getActionName(action) await logMessage(`[步骤] 开始: ${typeName}`, folderPath).catch(() => {}) const result = await executeAction(action, device, folderPath, resolution) if (result.success && result.skipped) await logMessage(`[提示] 步骤已跳过(条件不满足): ${typeName}`, folderPath).catch(() => {}) if (!result.success) { const now = new Date() 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')}` const errorMsg = `[错误] ${getActionName(action)} 执行失败: ${result.error} [系统时间: ${timeStr}]` await logMessage(errorMsg, folderPath).catch(() => {}) return { success: false, error: result.error, completedSteps: i } } if (t < times - 1) await new Promise(resolve => setTimeout(resolve, 500)) } completedSteps++ if (onStepComplete) onStepComplete(i + 1, actions.length, getActionName(action), 0, times, times) if (i < actions.length - 1) { let remainingTime = interval const countdownInterval = 100 const nextStepName = getActionName(actions[i + 1]) const nextTimes = actions[i + 1].times || 1 while (remainingTime > 0) { if (shouldStop && shouldStop()) return { success: false, error: '执行被停止', completedSteps } if (onStepComplete) onStepComplete(i + 1, actions.length, nextStepName, remainingTime, nextTimes, 0) const waitTime = Math.min(countdownInterval, remainingTime) await new Promise(resolve => setTimeout(resolve, waitTime)) remainingTime -= waitTime } } } return { success: true, completedSteps } } module.exports = { executeActionSequence }