效果演示
点击StartAll启动执行,每一个单元完成后根据连接关系决定下一步需要执行的单元。
代码结构
1、DirectUI模块用于绘制图形界面,如上图右边的逻辑图,可以实时显示各个单元的执行状态。
2、FSM模块用于实现有限状态机的执行逻辑。
3、ThreadMessage用于在执行过程中显示一些消息。
核心执行器代码
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Test1.DirectUI;
using Test1.DirectUI.LogicCompoments;
using Test1.FSM.DataManage;
using Test1.FSM.Items;
using Doraemon.Helper;
namespace Test1.FSM
{
public class FSMExecuter
{
private FSMEditor editor;
private List<Context> starts = new List<Context>();
private List<Task> tasks = new List<Task>();
private bool stopFlag = false;
private Database database = new Database();
public FSMExecuter(FSMEditor editor)
{
this.editor = editor;
}
public void Run()
{
lock (this)
{
if (IsRunning)
return;
stopFlag = false;
}
ResetAllItemAndFindStarts();
foreach (Context st in starts)
{
Task task = new Task(new Action<object>((s) =>
{
RunStart(s);
}), st);
task.Start();
tasks.Add(task);
}
RemoveCompletedTask();
}
private void RunStart(object s)
{
Stopwatch sw = new Stopwatch();
sw.Start();
float scanTicks = 0;
int scanCount = 0;
List<Context> nextSts = new List<Context>();
Context start = s as Context;
nextSts.Add(start);
foreach (Context curSt in nextSts)
curSt.StateItem.ResetStatus();
List<Context> nextList = new List<Context>();
do
{
long startTicks = SystemHelper.SystemTicks;
nextList.Clear();
foreach (Context curSt in nextSts)
{
List<Context> tmp = curSt.StateItem.Handle(curSt);
nextList.AddRange(tmp);
}
nextSts.Clear();
nextSts.AddRange(nextList);
//删除已经完成的StateItem
List<Context> delList = new List<Context>();
foreach (Context next in nextSts)
{
if (next.StateItem.IsFinished)
delList.Add(next);
}
foreach (Context next in delList)
{
if (next.StateItem.IsFinished)
nextSts.Remove(next);
}
Thread.Sleep(0);
scanTicks += (SystemHelper.SystemTicks - startTicks)/1000.0f;
scanCount++;
if (sw.ElapsedMilliseconds > 500)
{
scanTicks /= scanCount;
sw.Restart();
(start.StateItem as Start).ScanTicks = scanTicks;
scanTicks = 0;
scanCount = 0;
}
} while (nextSts.Count > 0 && !stopFlag);
foreach(Context curSt in nextList)
{
curSt.StateItem.Stop();
}
}
private void ResetAllItemAndFindStarts()
{
starts.Clear();
foreach (Control c in editor.FSMDesignner.RootControl.Controls)
{
if (c is LogicUnit)
{
(c as LogicUnit).StateItem.ResetStatus();
Start start = (c as LogicUnit).StateItem as Start;
if (start != null)
starts.Add(new Context(this, start));
}
}
}
public bool IsRunning
{
get
{
RemoveCompletedTask();
return tasks.Count != 0;
}
}
public Database Database
{
get
{
return database;
}
}
private void RemoveCompletedTask()
{
List<Task> delTaskList = new List<Task>();
foreach (Task t in tasks)
{
if (t.IsCompleted)
delTaskList.Add(t);
}
foreach (Task t in delTaskList)
{
tasks.Remove(t);
}
}
internal void Stop()
{
lock(this)
{
stopFlag = true;
}
}
}
}
其中RunStart为整体执行逻辑,其接受一个传入的Start对象,使得程序可以从Start对象开始执行,整个流程执行完成后线程便会终止,是否结束主要看当前完成单元的输出口是否连接有其他单元,并且此输出口是当前要转移的输出口。参考上面的动图,在弹窗单元选择了OK后便从OK输出口转移出去,如果选择了Cancel则从Cancel输出口转移出去。