65.9K
CodeProject 正在变化。 阅读更多。
Home

基于 C#/XML 的状态引擎

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (7投票s)

2004年7月21日

3分钟阅读

viewsIcon

48570

downloadIcon

645

如何创建一个基于 XML 的状态引擎来控制一个简单的实时系统

引言

在被要求研究如何更新旧的 C++ 软件时,我需要在 C# 中实现一个简单的状态引擎。 想要创建一个可以在整个代码中使用的通用解决方案,我研究了将实际的状态表写在 XML 中,以及一个使用反射处理该表的通用引擎。

Using the Code

XML 中的状态表仅定义了许多状态,并通过数字进行标识。 每个状态都有一个函数名称和一个相关的操作,该操作在函数调用完成时执行。

<?xml version="1.0" encoding="utf-8" ?> 
<StateTable>
    <State Number="0">
        <Function>CheckInputs</Function>
        <Action Type="SLEEP">
            <SleepTime>1000</SleepTime>
        </Action>
    </State>
    <State Number="10">
        <Function>FirstPressed</Function>
        <Action Type="MOVETONEXT">
            <SleepTime>100</SleepTime>
            <NextState>0</NextState>
        </Action>
    </State>
    <State Number="20">
        <Function>SecondPressed</Function>
        <Action Type="MOVETONEXT">
            <SleepTime>300</SleepTime>
            <NextState>0</NextState>
        </Action>
    </State>
    <State Number="30">
        <Function>BothPressed</Function>
        <Action Type="EXIT" />
    </State>
    <State Number="40">
        <Function>NonePressed</Function>
        <Action Type="MOVETONEXT">
            <SleepTime>1000</SleepTime>
            <NextState>0</NextState>
        </Action>
    </State>
</StateTable>

状态引擎通过使用反射来定位函数,并将其附加到状态的事件。 这是通过首先获取状态的事件类型,然后通过指定目标类 (this.engineMethodsClass) 实例和实例方法的名称 (functionName) 将委托附加到它来实现的。

// get our OnState event, then create and add a new delegate to the event
System.Reflection.EventInfo eventInfo = state.GetType().GetEvent("OnState");
System.Delegate eventDelegate = System.Delegate.CreateDelegate(
    eventInfo.EventHandlerType, this.engineMethodsClass, functionName);
eventInfo.AddEventHandler(state, eventDelegate);        

一个操作由以下枚举值组成

  • SLEEP - 导致引擎停留在当前状态并休眠一段时间(以毫秒为单位)
  • MOVETONEXT - 移至下一个状态(由 <NextState> 标签指示)。 也可以在这里添加睡眠时间。
  • EXIT - 导致引擎退出

要使用状态引擎,首先在处理状态引擎的类中创建一个变量。

private RQAssemblies.StateEngine.Engine engine = new 
        RQAssemblies.StateEngine.Engine();

为了将事件与主应用程序分开,您可以指定可以找到事件的类。 在本例中,我们将简单地指向我们的主应用程序。 然后,我们可以将 state 表加载到引擎中,并指示它 Run()

// initialise and start up the engine 
engine.EngineMethodsClass = this;
engine.LoadStateTable(@"..\..\BasicEngine.xml");
engine.Run();

引擎在其自己的线程上运行以进行执行,因此必须在应用程序终止时停止。 将以下代码放在应用程序的 Close 事件中。

// instruct the state engine to stop execution
engine.Stop();

当引擎到达一个 state 时,它会调用附加的事件。 在我们的示例 state 表中,我们需要定义 5 个事件,其中事件的原型是 public void functionName(Action actionOnExit)

public void CheckInputs(RQAssemblies.StateEngine.Action actionOnExit)
{
    if((this.input1.Checked == true) && (this.input2.Checked == false))
    {
        actionOnExit.Type = 
            RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT;
        actionOnExit.StateNumberToMoveTo = 10;
    }
    else if((this.input1.Checked == false) && (this.input2.Checked == true))
    {
        actionOnExit.Type = 
            RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT;
        actionOnExit.StateNumberToMoveTo = 20;
    }
    else if((this.input1.Checked == true) && (this.input2.Checked == true))
    {
        actionOnExit.Type = 
            RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT;
        actionOnExit.StateNumberToMoveTo = 30;
    }
    else if((this.input1.Checked == false) && (this.input2.Checked == false))
    {
        actionOnExit.Type = 
            RQAssemblies.StateEngine.Action.ActionType.MOVETONEXT;
        actionOnExit.StateNumberToMoveTo = 40;
    }
}

public void FirstPressed(RQAssemblies.StateEngine.Action actionOnExit)
{
    this.result.Text = "First Pressed";
}

public void SecondPressed(RQAssemblies.StateEngine.Action actionOnExit)
{
    this.result.Text = "Second Pressed";
}

public void BothPressed(RQAssemblies.StateEngine.Action actionOnExit)
{
    this.result.Text = "Both Pressed";
}

public void NonePressed(RQAssemblies.StateEngine.Action actionOnExit)
{
    this.result.Text = "None Pressed";
} 

在一个事件中,我们可以通过修改 actionOnExit 参数来修改状态的退出操作。

在我们的示例状态表中,状态 0 只是循环并检查 2 个按钮的状态。 根据按钮状态,我们修改退出操作条件,以便分支到 state 表的不同部分。

关注点

当我第一次开始学习 C# 时,我将反射视为一个从 C++ 带来的新鲜空气,但很难找到一个真实世界/与工作相关的应用程序来尝试。 现在我找到了一个,我可以看到它为我打开的门,以便在将来显着减少我的编码工作。

结论

状态引擎的实现非常基础,它只是为了展示如何在创建实时状态引擎时利用 XML 和反射的优势。 显然,这个例子并没有与一个实时系统交互,因为我无法将我的条形码扫描器与这篇文章一起压缩。 在未来,我打算修改此代码,使其在状态之间的链接方面更加灵活,并提供外部输入状态,以及可能提供“是/否”风格的状态处理。

但与此同时,我希望您在这篇文章中找到一些有用的东西,并且我期待着阅读您的评论。

历史

  • 版本 1.0 - 20 2004年7月 - 原始版本

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.