33. Eclipse建模支持

通过Eclipse Papyrus框架支持使用UI建模定义状态机配置

从eclipse向导创建一个带有UML图表语言的新Papyrus模型。 在这个例子中,它被命名为simple-machine。 然后,您可以选择各种图表类型,并且必须选择一个StateMachine图表。

我们想要创建一个具有两个状态的机器,S1和S2,其中S1是初始状态。 然后创建事件E1以执行从S1到S2的转换。 在纸莎草纸上,机器会看起来像下面所示的东西。

在一个场景后面,一个原始的uml文件看起如下。

<?xml version="1.0" encoding="UTF-8"?>
<uml:Model xmi:version="20131001" xmlns:xmi="http://www.omg.org/spec/XMI/20131001" xmlns:uml="http://www.eclipse.org/uml2/5.0.0/UML" xmi:id="_AMP3IP8fEeW45bORGB4c_A" name="RootElement">
  <packagedElement xmi:type="uml:StateMachine" xmi:id="_AMRFQP8fEeW45bORGB4c_A" name="StateMachine">
    <region xmi:type="uml:Region" xmi:id="_AMRsUP8fEeW45bORGB4c_A" name="Region1">
      <transition xmi:type="uml:Transition" xmi:id="_chgcgP8fEeW45bORGB4c_A" source="_EZrg4P8fEeW45bORGB4c_A" target="_FAvg4P8fEeW45bORGB4c_A">
        <trigger xmi:type="uml:Trigger" xmi:id="_hs5jUP8fEeW45bORGB4c_A" event="_NeH84P8fEeW45bORGB4c_A"/>
      </transition>
      <transition xmi:type="uml:Transition" xmi:id="_egLIoP8fEeW45bORGB4c_A" source="_Fg0IEP8fEeW45bORGB4c_A" target="_EZrg4P8fEeW45bORGB4c_A"/>
      <subvertex xmi:type="uml:State" xmi:id="_EZrg4P8fEeW45bORGB4c_A" name="S1"/>
      <subvertex xmi:type="uml:State" xmi:id="_FAvg4P8fEeW45bORGB4c_A" name="S2"/>
      <subvertex xmi:type="uml:Pseudostate" xmi:id="_Fg0IEP8fEeW45bORGB4c_A"/>
    </region>
  </packagedElement>
  <packagedElement xmi:type="uml:Signal" xmi:id="_L01D0P8fEeW45bORGB4c_A" name="E1"/>
  <packagedElement xmi:type="uml:SignalEvent" xmi:id="_NeH84P8fEeW45bORGB4c_A" name="SignalEventE1" signal="_L01D0P8fEeW45bORGB4c_A"/>
</uml:Model>

当打开定义为uml的现有uml模型时,您将有三个文件,.di,.notation和.uml。 如果model没有在你的eclipse会话中创建,它不知道如何打开一个实际的状态图。 这是一个Papyrus插件中的已知问题,并且有一个简单的解决方法。 在Papyrus透视图中,您会看到模型浏览器模型,双击Diagram StateMachine Diagram,它将指示eclipse在其适当的Papyrus建模插件中打开此特定模型。

33.1 使用 UmlStateMachineModelFactory

在您的项目中使用uml文件后,可以使用StateMachineModelConfigurer将其导入到配置中,其中StateMachineModelFactory与模型关联。 UmlStateMachineModelFactory是一个知道如何处理Eclipse Papyrus生成的uml结构的特殊工厂。 源uml文件可以作为Spring资源或常规位置字符串提供。

@Configuration
@EnableStateMachine
public static class Config1 extends StateMachineConfigurerAdapter<String, String> {

    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model
            .withModel()
                .factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        return new UmlStateMachineModelFactory("classpath:org/springframework/statemachine/uml/docs/simple-machine.uml");
    }
}

正如Spring StateMachine通常使用被定义为bean的Guards和Actions一样,那些需要通过其内部建模结构被挂钩到uml中。 在下面的章节中,您将看到如何在uml定义中定义自定义bean引用。 也认为可以手动注册特定的方法,而无需将它们定义为bean。

如果UmlStateMachineModelFactory被创建为一个bean,那么它的ResourceLoader会自动连线以查找已注册的操作和警卫。 也可以手动定义一个StateMachineComponentResolver,然后用它来查找这些组件。 工厂也有方法registerAction和registerGuard可以用来注册这些组件。 更多关于这部分在第33.1.1节“StateMachineComponentResolver”。

Uml模型相对松散,像Spring StateMachine本身一样实现。 有许多实现需要为uml支持采取什么选择,因为它为实现决定了许多特性和功能。 下面的章节将介绍Spring StateMachine如何基于Eclipse Papyrus插件实现uml模型。

33.1.1 StateMachineComponentResolver

以下示例显示了如何使用分别注册简单函数myAction和myGuard的StateMachineComponentResolver定义UmlStateMachineModelFactory。 当你注意到这些组件不是作为bean创建的。

@Configuration
@EnableStateMachine
public static class Config2 extends StateMachineConfigurerAdapter<String, String> {

    @Override
    public void configure(StateMachineModelConfigurer<String, String> model) throws Exception {
        model
            .withModel()
                .factory(modelFactory());
    }

    @Bean
    public StateMachineModelFactory<String, String> modelFactory() {
        UmlStateMachineModelFactory factory = new UmlStateMachineModelFactory(
                "classpath:org/springframework/statemachine/uml/docs/simple-machine.uml");
        factory.setStateMachineComponentResolver(stateMachineComponentResolver());
        return factory;
    }

    @Bean
    public StateMachineComponentResolver<String, String> stateMachineComponentResolver() {
        DefaultStateMachineComponentResolver<String, String> resolver = new DefaultStateMachineComponentResolver<>();
        resolver.registerAction("myAction", myAction());
        resolver.registerGuard("myGuard", myGuard());
        return resolver;
    }

    public Action<String, String> myAction() {
        return new Action<String, String>() {

            @Override
            public void execute(StateContext<String, String> context) {
            }
        };
    }

    public Guard<String, String> myGuard() {
        return new Guard<String, String>() {

            @Override
            public boolean evaluate(StateContext<String, String> context) {
                return false;
            }
        };
    }
}

33.2 创建模型

首先创建一个空的状态机模型。

您将开始创建一个新模型并给它起个名字。

然后你需要选择一个状态机图。

你最终有一个空的状态机。

在上面的示例命名模型中,最终会生成三个文件,即model.di,model.notation和model.uml,然后可以在任何其他eclipse实例中使用它们,并且可以通过将model.uml导入Spring Statemachine来使用它。

33.3 定义状态

状态标识符只是来自图表中的组件名称。 你必须在你的机器中有初始状态,这是通过添加Initial来完成的,然后向你自己的初始状态绘制一个转换。

在上面我们添加了一个状态S1,初始状态,并在这两个状态之间画出一个转换来表示S1是一个初始状态。

在上面我们添加了第二个状态S2并在这两个状态之间添加了一个转换。

33.4 定义事件

要将一个事件关联到一个转换,您需要创建一个Signal E1。 从RootElement→新建子项→信号完成。

然后用定义信号E1的SignalEvent。 从RootElement→新建子项→SignalEvent完成。

现在您已经定义了一个SignalEvent,它可以用来将触发器与转换相关联。 更多关于第33.5节“定义转换”的内容。

33.4.1 延迟事件

事件可以推迟到更适当的时间进行处理。 在UML中,这是从状态本身完成的。 选择任何状态并在可延迟触发器下创建一个新的触发器,并选择与您希望推迟的Signal匹配的SignalEvent。

33.5 定义转换

过渡只是通过绘制源状态和目标状态之间的转换线来创建的。 在上面我们已经说明了S1和S2以及这两者之间的匿名转换。 我们希望将事件E1与该转换相关联。 我们选择一个转换,创建一个新的触发器并为其定义SignalEventE1。

这会给你如下所示的东西。

如果SignalEvent在转换时被省略,它将成为匿名转换。

33.6 定义定时器

转换也可以基于定时事件发生。 Spring Statemachine支持两种类型的定时器,一种在后台持续触发,另一种在进入状态时延迟触发一次。

将新的TimeEvent子项添加到模型资源管理器中,将as as expression定义为LiteralInteger时进行修改。 它的价值是定时器为毫秒。 相对于虚假的定时器持续着火。要在输入状态时定义一个基于时间的事件,它与上面的完全相同,但现在将相对定义为true。

然后,用户留下的是选择这些时间事件而不是特定转换的信号事件。

33.7 定义选择状态

选择状态是通过将一个输入转换画成CHOICE状态并将其从多个输出转换为目标状态来简单定义的。 我们的StateConfigurer中的配置模型允许定义if / elseif / else结构,但是使用uml我们只需要针对外出转换使用单个Guards。

确保为过渡定义的警卫不重叠,以便无论发生什么事情,只有一名警卫在任何给定时间评估为TRUE。 这为选择分支评估提供了精确和可预测的结果。 另外建议在没有警卫的情况下离开一次过渡,以保证至少有一条过渡路径。

连接点非常相似,除了它允许多个传入转换。 因此,与选择相比,其行为纯粹是学术性的。 选择传出转换的实际逻辑完全相同。

33.8 定义连接点

参见第33.7节“定义选择状态”。

33.9 定义入口/出口

EntryPoint和ExitPoint用于控制进入和退出状态有子状态。 在下面的状态图中,事件E1和E2将通过进入和退出状态S2来执行正常状态行为,其中状态行为通过进入初始状态S21而发生。

使用事件S3将机器带入入口点ENTRY,然后导入S22,而无需在任何时间激活初始状态S21。 类似地,带有事件S4的ExitPoint EXIT控制特定的退出进入状态S4,而来自S2的正常退出行为将使机器进入状态S3。 处于状态S22时,您可以选择事件E4或E2以分别将机器置于状态S3或S4。

如果状态被定义为子机参考,并且需要使用入口/出口点,则必须在外部定义ConnectionPointReference,将其入口/出口参考设置为指向子机参考内的正确入口/出口点。 只有在此之后,才有可能针对从外部正确链接到冲床引用内部的转换。 使用ConnectionPointReference,您可能需要从Properties→Advanced→UML→Entry / Exit中找到这些设置。 UML Spec允许定义多个条目并退出,但只有一个状态机可以使用。

33.10 定义历史

在处理历史状态时,三种不同的概念正在发挥作用。 UML定义了深层历史和浅层历史。 当历史状态尚不知道时,默认历史状态开始起作用。 这些在下一节中介绍

33.10.1 Shallow

简单的选择浅历史,并定义一个转换。

33.10.2 Deep

深层历史记录用于具有其他深层嵌套状态的状态,因此有机会保存整个嵌套状态结构。

33.10.3 Default

在状态未被输入或达到其最终状态时历史终止于历史的情况下,可以使用默认历史机制来强制转移到特定子状态。 要做到这一点,只需将转换定义为默认状态即可。 这将是从SH到S22的过渡。

在下面的例子中,如果状态S2从未被激活,则状态S22将被输入,因为其历史从未被记录过。 如果状态S2已被激活,那么S20或S21将被选择。

33.11 定义Fork / Join

纸叉和联接在纸莎草纸中都被表示为酒吧。 如下所示,您需要画出一个从FORK到具有正交区域的状态S2的输出转换。 然后JOIN反向,通过传入转换将联接状态收集在一起。

33.12 定义动作

状态进入和退出动作可以通过使用一个行为来关联,更多关于这个的内容请参见第33.14节“定义Bean参考”。

33.12.1 初始动作

如第11.7节“配置操作”中所示的初始操作在uml中通过添加从初始状态标记到实际状态的转换中的操作来定义。 这个动作然后在状态机启动时执行。

33.13 定义 Guards

Guard可以通过首先添加约束然后将其规范定义为OpaqueExpression来定义,该方法的工作方式与第33.14节“定义Bean参考”的方式相同。

33.14 定义Bean引用

当需要在任何uml效果,动作或警卫中创建一个bean引用时,支持的方法可以通过FunctionBehavior或OpaqueBehavior进行,其中定义的语言需要是bean和具有bean引用id的语言主体。

33.15 定义SpEL 引用

当需要在任何uml效果,动作或警卫中使用SpEL而不是bean引用时,支持的方法是通过FunctionBehavior或OpaqueBehavior,其中定义的语言需要是spel和具有SpEL表达式的语言体。

33.16 使用子机参考

通常在使用子状态时,它们本身就被简单地绘制成状态图。 图表本身可能会变得有点复杂并且很难遵循,所以我们也支持将子状态定义为国家机器参考。

首先创建一个新图,并为其命名即SubStateMachine Diagram。

给新的图表设计一个你需要的。

从想要链接的状态(在这种情况下为状态S2),单击子机领域并选择您的链接机器,即SubStateMachine。

最后,您会看到状态S2作为子状态链接到SubStateMachine。

results matching ""

    No results matching ""