构建工业自动化软件框架:流程核心搭建(二) 流程单步代码实现

工业自动化单步流程代码实现

文章同步在公众号与知乎更新,账号全部同名。搜索“小黄花呀小黄花”即可找到我。

前言

上一篇文章中,我们介绍了流程引擎与上下文模块的实现,奠定了流程执行的基础。

本文将进一步深入,重点介绍流程执行的核心单元 —— 单步流程步骤(FlowStep)的接口设计、基类封装及典型实现方式(如延时、跳转、循环、并行等)。

本文还将说明如何在步骤中灵活设置下一步逻辑,实现高度可配置的流程控制。

 类图

代码实现

单步流程接口

接口中包含一个名称,和一个可等待的方法。

public interface IFlowStep
{
    /// <summary>
    /// 步骤名称
    /// </summary>
    string StepName { get; }

    /// <summary>
    /// 执行步骤可等待方法
    /// </summary>
    /// <param name="context"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    Task<StepResult> AsyncExecuteStep(IFlowContext context, CancellationToken token = default);
}

单步流程基类实现

接口完成后,实现了一个基类,后续所有的步骤实现都继承整个类。

AsyncExecuteStep方法为步骤的核心方法,流程调用时就执行该方法,该方法返回一个结果,流程也会更具该结果进行判断和执行。AsyncExecuteStep内部会调用AsyncExecuteCore方法,子类后续只要实现两个抽象方法AsyncExecuteCore和SetNextStepIndex即可。

同时基类中实现了AsyncExecuteBranch方法,这个方法主要是用于循环和并行时使用,可以直接执行一段步骤。这个方法中还需要将上下文(流程引擎实现)分开操作,data可以公用,但是下一步这些的参数就是各自分支单独的。

public abstract class FlowStepBase : IFlowStep
{
    #region 构造函数    
    protected FlowStepBase(string stepName)
    {
        StepName = stepName;
    }
    #endregion

    #region 属性
    public string StepName { get; }
    #endregion

    #region 方法
    /// <summary>
    /// 执行该步骤的可等待方法,此方法可重写
    /// </summary>
    /// <param name="context"></param>
    /// <param name="token"></param>
    /// <returns></returns>
    public virtual async Task<StepResult> AsyncExecuteStep(IFlowContext context, CancellationToken token = default)
    {
        try
        {
            await AsyncExecuteCore(context, token);
            return StepResult.Success();
        }
        catch (OperationCanceledException)
        {
            return StepResult.Cancelled($"步骤 {StepName} 被取消");
        }
        catch (Exception e)
        {
            return StepResult.Failure($"步骤 {StepName} 执行失败: {e.Message}");
        }
    }

    /// <summary>
    /// 步骤的主要执行内容
    /// </summary>
    /// <param name="context"></param>
    /// <param name="token"></param>
    protected abstract Task AsyncExecuteCore(IFlowContext context, CancellationToken token);

    /// <summary>
    /// 设置下一步索引,用在ExecuteCoreAsync中
    /// </summary>
    /// <param name="context"></param>
    protected abstract void SetNextStepIndex(IFlowContext context);

    /// <summary>
    /// 当需要在步骤中执行一段流程时使用
    /// 如:循环/并行
    /// </summary>
    /// <param name="context"></param>
    /// <param name="token"></param>
    /// <param name="branch">流程</param>
    /// <param name="branchIndex">流程分支号</param>
    /// <returns></returns>
    protected async Task<StepResult> AsyncExecuteBranch(IFlowContext context, CancellationToken token, List<IFlowStep> branch, int branchIndex)
    {
        var branchContext = context.Clone();
        branchContext.NextStepIndex = 0;
        branchContext.TotalSteps = branch.Count;

        int index = 0;
        while (!token.IsCancellationRequested && index < branch.Count)
        {
            var _currentStep = branch[index];
            if (_currentStep != null)
            {
                context.Logger.Info($"步骤{StepName}分支{branchIndex}:开始执行步骤【{_currentStep.StepName}】...");
                var result = await _currentStep.AsyncExecuteStep(branchContext, token);
                if (result.Status != StepStatus.Success)
                {
                    context.Logger.Error($"步骤{StepName}分支{branchIndex}:步骤【{_currentStep.StepName}】执行失败:{result.Message}");
                    return result;
                }
            }
            index = branchContext.NextStepIndex;
        }
        context.Logger.Info($"步骤{StepName}分支{branchIndex}执行完成");
        return StepResult.Success();
    }
    #endregion
}

单步流程实际步骤实现

所有的实际步骤都继承自FlowStepBase。这些实现正常应该放在Application中,但是为了更好的编写基类,所以优先实现了几个特殊的步骤.

而他们的构造方法中的参数,当前先用构造方法注入,后续计划改成属性注入。

延时

public class FlowStep_Delay : FlowStepBase
{
    #region 构造函数
    /// <summary>
    /// 延时步骤构造函数
    /// </summary>
    /// <param name="stepName">步骤名称</param>
    /// <param name="delayTime_ms">延时时间ms</param>
    public FlowStep_Delay(string stepName, int delayTime_ms) : base(stepName)
    {
        _delayTime_ms = delayTime_ms;
    }
    #endregion

    #region 字段
    private readonly int _delayTime_ms;
    #endregion

    #region 方法
    protected override async Task AsyncExecuteCore(IFlowContext context, CancellationToken token)
    {
        await Task.Delay(_delayTime_ms, token);
        SetNextStepIndex(context);
    }

    protected override void SetNextStepIndex(IFlowContext context)
    {
        context.NextStepIndex++;
    }
    #endregion
}

跳转

public class FlowStep_JumpTo : FlowStepBase
{
    #region 构造函数
    public FlowStep_JumpTo(string stepName, int jumpToStepIndex) : base(stepName)
    {
        _jumpToStepIndex = jumpToStepIndex;
    }
    #endregion

    #region 字段
    private readonly int _jumpToStepIndex;
    #endregion

    #region 方法
    protected override async Task AsyncExecuteCore(IFlowContext context, CancellationToken token)
    {
        if (_jumpToStepIndex >= 0 && _jumpToStepIndex < context.TotalSteps)
        {
            SetNextStepIndex(context);
        }
        else
        {
            string msg = $"步骤{StepName}设置步数{_jumpToStepIndex}错误,超出总步数{context.TotalSteps}";
            context.Logger.Error(msg);
            throw new StepExecuteException(msg);
        }
        await Task.CompletedTask;
    }

    protected override void SetNextStepIndex(IFlowContext context)
    {
        context.NextStepIndex = _jumpToStepIndex;
    }
    #endregion
}

循环

public class FlowStep_Loop : FlowStepBase
{
    #region 构造函数
    public FlowStep_Loop(string stepName, List<IFlowStep> loopBranch, int totalLoops) : base(stepName)
    {
        _loopBranch = loopBranch ?? throw new ArgumentNullException(nameof(loopBranch));
        _totalLoops = totalLoops;
    }
    #endregion

    #region 属性

    #endregion

    #region 字段
    private readonly List<IFlowStep> _loopBranch;
    private readonly int _totalLoops;
    #endregion

    #region 方法

    protected override async Task AsyncExecuteCore(IFlowContext context, CancellationToken token)
    {
        for (int i = 0; i < _totalLoops; i++)
        {
            context.Logger.Info($"步骤{StepName}第{i}/{_totalLoops}次循环开始");
            await AsyncExecuteBranch(context, token, _loopBranch, i);
            context.Logger.Info($"步骤{StepName}第{i}/{_totalLoops}次循环完成始");
        }
        SetNextStepIndex(context);
    }

    protected override void SetNextStepIndex(IFlowContext context)
    {
        context.NextStepIndex++;
    }
    #endregion
}

并行

public class FlowStep_Parallel : FlowStepBase
{
    #region 构造函数
    public FlowStep_Parallel(string stepName, List<List<IFlowStep>> branches, bool waitAll = true)
        : base(stepName)
    {
        _parallelBranches = branches ?? throw new ArgumentNullException(nameof(branches));
        _waitAll = waitAll;
    }
    #endregion

    #region 字段
    private readonly List<List<IFlowStep>> _parallelBranches;
    private readonly bool _waitAll;
    #endregion

    #region 方法
    protected override async Task AsyncExecuteCore(IFlowContext context, CancellationToken token)
    {
        StepResult stepResult = null;
        var branchTasks = new List<Task<StepResult>>();
        for (int i = 0; i < _parallelBranches.Count; i++)
        {
            branchTasks.Add(AsyncExecuteBranch(context, token, _parallelBranches[i], i));
        }
        if (_waitAll)
        {
            var branchResults = await Task.WhenAll(branchTasks);
            stepResult = branchResults.All(x => x.Status == StepStatus.Success)
                ? StepResult.Success() : StepResult.Failure("部分分支执行失败");
        }
        else
        {
            var completed = await Task.WhenAny(branchTasks);
            stepResult = await completed;
        }
        if (stepResult.Status != StepStatus.Success)
        {
            throw new StepExecuteException($"并行步骤 {StepName} 失败");
        }
        SetNextStepIndex(context);
    }

    protected override void SetNextStepIndex(IFlowContext context)
    {
        context.NextStepIndex++;
    }
    #endregion
}

运行结果

创建了一个结果类,里添加静态方法,用于步骤结果的返回,可以同时返回结果与信息。

public class StepResult
{
    public StepStatus Status { get; set; }
    public string Message { get; set; }


    public static StepResult Success() =>
        new StepResult() { Status = StepStatus.Success };
    public static StepResult Failure(string msg) =>
        new StepResult() { Status = StepStatus.Failure, Message = msg };
    public static StepResult Timeout(string msg) =>
        new StepResult() { Status = StepStatus.Timeout, Message = msg };
    public static StepResult Cancelled(string msg) =>
        new StepResult() { Status = StepStatus.Cancelled, Message = msg };
    public static StepResult Skipped(string msg) =>
        new StepResult() { Status = StepStatus.Skipped, Message = msg };
}

public enum StepStatus
{
    Success,
    Failure,
    Timeout,
    Cancelled,
    Skipped
}

后记

本文完成了流程步骤模块的接口、基类与典型实现,构建了一个模块化的流程步骤系统,为后续支持各类工业动作、逻辑分支、数据处理提供了基础。

后续我将继续实现流程引擎中的状态机管理、事件系统等核心功能,并不断扩展实际流程步骤,逐步完善整个工业自动化软件框架。

欢迎关注专栏查看以往内容以及后续更新。构建工业自动化软件框架

代码放在了Github上,欢迎围观,欢迎 Star/Fork/ 提Issue。 ·

知乎公众号同步更新,欢迎关注公众号。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值