官网文档 States and Transitions in RSARTE 文档翻译
参考:https://rsarte.hcldoc.com/help/index.jsp?topic=%2Fcom.ibm.xtools.rsarte.webdoc%2Fusers-guide%2Foverview.html
外部状态转换会导致对象从一种状态更改为另一种状态。请参阅下图。假设状态机处于State1。如果转换Ping被触发,它将导致状态从State1变为State2。转换Ping被认为是一个外部转换,因为它导致状态机离开一个状态(State1),然后进入另一个(State2)。
转换Ping可能具有一个guard (布尔表达式),用于确定是否启用转换。当guard 表达式的计算结果为true时,将启用转换。只能触发已启用的转换。转换还可以具有在触发转换时执行的effect code。保护和效果代码的示例如下图所示。
为转换显示的文本的UML语法为:
Transition name [guard] / effect
在我们的例子中是:
Ping[booleanGuard()]/effectCode();
Thus:
Ping是转换的名称
booleanGuard()是Guard;计算结果为true或false的布尔表达式
effectCode()是effect代码;触发转换Ping时执行的代码
请注意以下内容:
- 上面两张图片来自同一个状态机图。唯一的区别是,在下图中,我们打开了首选项,以显示转换的保护和效果。有关详细信息,请参见状态图外观。
- RSARTE中用于上述示例Guard的实际语法为
return booleanGuard();
也就是说,必须返回布尔表达式。然而,为了清楚起见,没有返回除了转换之外,UML还允许每个状态都有进入或退出状态时执行的进入和退出代码。Transitions、guards和entry exit代码将在以下部分中进一步解释。
effect Code
effect code是在触发转换时执行的代码。在上面的例子中,effect Code 由对操作effectCode()的调用组成。effect Code可以是您可以在函数体中编写的任何有效的C++代码。如果它很短(一两行),您可以考虑在状态启动图上显示它。但如果时间较长,通常最好不要显示,否则图表可能会过于混乱。使用状态图筛选首选项(实时开发-图表-状态图-状态图筛选选项)来定义状态图上可见的内容
重要的是,effect code没有阻塞(例如,等待一些输入),因为这将阻塞整个capture,以及在同一物理线程中运行的其他capture。当转换触发时执行的所有代码都应该尽可能快。如果您需要执行更长时间的运行操作,您应该考虑将此类代码放在其自己的容器中,以便由单独的线程运行。
Transition Guard
转换保护是在实际触发转换之前计算的布尔表达式。如果保护为true,则转换被启用并可以被触发,否则不会
被触发并且状态机保持在相同的状态。
示例如下:
- X == 1
- isReady() 假设isReady()是一个布尔函数
对于上面的例子,我们使用了一个guard-effectCode()。这意味着,如果此操作返回true,则转换将触发状态机并将其从State1转移到State2。但是,如果函数返回false,则状态机将停留在State1中。
保护代码应该执行得很快并且没有副作用,因为它们经常被evaluated。
如上所述,RSARTE要求使用return语句返回转换guard表达式
State Entry and Exit Code
各state可能有entry exit代码。entry代码是每次进入状态时执行的代码,而exit代码是每次退出状态时执行,无论触发哪种转换。
让我们回到上面的例子。如果State1有exit代码,State2有entry代码,那么代码将按以下顺序执行:
- State1 Exit Code
- Ping Effect Code
- State2 Entry Code
Creating a Trigger for a Transition
源自一个状态的转换必须至少有一个trigger。trigger表示事件到达container capsule的其中一个端口。当指定的事件到达指定的端口时,触发器可能会触发转换。可以使用转换的“属性”视图为转换创建触发器。
假设我们有一个具有端口p1的Capture C,该端口由具有in事件inPing的协议P。
当inPing事件到达端口p1时,我们希望它引发状态图上的Ping转换。因此,我们应该为Ping转换创建一个触发器。
- 在状态图或项目资源管理器中,选择转换。
- 在Properties(属性)视图中,单击Triggers(触发器)选项卡
- 点击 the Insert New Transition Trigger 图标.
在“添加触发器”对话框中,选择端口和事件。
您还可以从状态图中选择的转换的上下文菜单中创建触发器图表(添加UML-触发器)
Receiving a Trigger Event
当inPing事件到达p1端口时,会发生以下情况(假设状态机器最初处于State1):
- 事件inPing到达端口p1
- 对转换Ping的Guad进行评估(true或false)
- 如果这个Guad是真的:
(1) State1 退出代码已执行
(2) Ping effect code 已执行
(3) State2 entry code is executed
(5) Capture 对象现在处于状态2 - 如果guard 是false
(1) capture对象保持在State1中,不再执行任何代码
符号解释
以下视觉标记符号可能出现在状态和转换上:
Effect code
①转换上的蓝色图标表示转换包含触发转换时执行的效果代码。
②将鼠标悬停在图标上将在弹出窗口中显示效果代码。
③单击该符号将在代码视图中显示效果代码。
④双击该符号将在编辑器中打开效果代码。
Entry code
①“传入箭头”表示该状态包含每当进入该状态时都会执行的输入代码。
②将鼠标悬停在图标上将在弹出窗口中显示输入代码
③双击该符号将允许您在编辑器中打开输入代码。
④请注意,如果状态同时具有entry代码和exit代码,则符号如下所示:
Entry point
①状态edge的空心圆表示过渡通过特定的进入点进入状态,然后可以进入特定的子状态。只有当状态具有子状态(即复合状态)时,入口点才有意义。否则,它与绘制到状态边缘的过渡相同。有关详细信息,请参见子状态。
Exit code
①“Exit箭头”表示该状态包含退出代码,该代码将在该状态退出时执行。
②将鼠标悬停在图标上将在弹出窗口中显示退出代码。
③双击该符号将允许您在编辑器中打开退出代码
Exit point
状态符号上带X的圆圈表示过渡从特定退出点离开状态。这允许您将转换连接到状态的特定子状态。只有当状态具有子状态(即复合状态)时,退出点才有意义。否则,它与从状态的边缘绘制过渡相同。有关详细信息,请参见子状态。
Guard code
①转换上的黄色图标表示转换包含在触发转换之前计算的guard表达式(布尔值)。 只有当guard为真时,才能触发转换。否则,就不会改变状态。
②将鼠标悬停在图标上将在弹出窗口中显示代码
③单击符号将在代码视图中显示代码
④双击符号将在编辑器中打开代码
Internal transition
①状态右上角的图标表示该状态至少包含一个内部转换
②双击图标将显示状态内部的内部转换,如下所示。在本例中,t1是内部过渡
Composite state
①一个状态右下角的两个相连圆表示它是一个复合状态,因此可能包含子状态。有关详细信息,请参见子状态。
Warning: no trigger
①转换上的黄色三角警告表示该转换没有定义任何触发器,并且必须至少有一个触发器。在这种情况下,转换的“属性”视图的“触发器”选项卡为空:
Redefined transition or redefined state
①蓝色小箭头表示转换或状态已重新定义。这意味着在其中一个父类capture的状态机中存在一个继承版本的转换。与此继承的转换相比,重新定义的转换或状态包含一些修改。例如,它可能具有不同的目标状态(在重新定义的转换的情况下)或不同的进入动作(在重新重新定义的状态下)。
②如果首选项“实时开发-图表-状态图-继承元素外观-线条样式”设置为“虚线”,则会使用“点划线”线条样式绘制重新定义的过渡或状态
Inherited state, transition or pseudo state
① 与本地定义的元素相比,继承的状态、转换或伪状态显示为灰色或虚线(取决于首选项实时开发-图表-状态图-继承的元素外观-线条样式)。这适用于所有继承的元素,而不仅仅是状态。如上图所示,重新定义的元素使用了一种特殊的虚线样式,使其更容易与其他继承的元素区分开来
Sub States
状态可能包含子状态,允许您将状态(复合状态)的行为分解为较小的状态(子状态)。下图显示了两个状态,State1和State2。状态2包含描述状态机处于状态2时的行为的子状态。
从State1到State2有三种转换:
1. ping:到状态2上的entry点(在子状态中标记为ping)
2. toHistory:到一个entry点(标记为entry,子状态中为H*)
3. sameAsHistory:到State2的edge(其行为与history相同)
有一个从State2返回State1的转换:
1. pong:离开State2的exit点(在子状态中标记为pong)
State Entry Points
Entry和Exit点允许连接状态机层次结构中不同级别的转换。转换可以在复合状态边缘的入口点处终止。然后,另一个转换可以将入口点与复合状态中的子状态连接起来。从而可以进入复合状态并直接进入某个子系统
转换可以通过三种方式进入复合状态:
- 通过Entry点
- 通过history 点
- 直接到state的edge。
Enter via an entry point
在上面的例子中,转换ping通过一个entry点进入State2。如果ping被触发,则将进入子状态State4。
请注意,子状态图中源自entry点的转换本身没有任何触发器。这些转换本身不能直接触发,而只能在连接到封闭状态机入口点的转换被触发时间接触发。因此,它们的行为类似于离开连接点的转换,并且可能具有effect code和guard,但没有触发器。
Enter via a history point
在上面的示例中,向history 的转换通过history 点进入State2。这是一种特殊的Entry点,它不是通过子状态图内部的转换连接的。它还标有“H*”,表示它是一个history 点。
用“H*”标记入口点以表示历史点的RSARTE表示法是使用显式深度历史伪状态的简写。因此,以下两种建模方式是等效的(前提是转换tx没有保护或效果代码)
以历史状态点进入状态时,最近活动过的子状态将再次变为活动状态。历史记录是“深度”的,这意味着如果激活的子状态也是一个复合状态,那么它也将使用历史状态点进入。这个过程会递归地继续下去,直到进入了非复合状态。如果复合状态以前没有被进入过,因此没有历史上活动的子状态,则使用它的初始状态转换来决定哪个子状态将变为活动状态。
让我们看看这些规则对我们的例子意味着什么。第一次触发toHistory转换以通过历史状态点进入State2,然后子状态State3将被激活,因为最初的转换将目标设置为此子状态。然后,假设触发了toState4转换,然后又在pong2转换中离开了State2。下一次触发toHistory转换时,我们现在将进入子状态State4,因为它是最近活动的子状态
Enter without using an entry point
如果在不使用入口点的情况下将转换绘制到复合状态的边缘,则实际上意味着与连接到历史状态点的情况相同。唯一的区别是,来自子状态图中不可见的传入转换。
在我们的示例中,sameAsHistory转换直接连接到State2的边缘。当它触发时,会发生与上述toHistory转换相同的事情。
RSARTE 推荐在复合状态中使用入口点,因为这样可以更清楚地了解状态进入时会发生什么。因此,如果您有一个非复合状态,它具有直接连接到其边缘的传入转换,然后将此状态转换为复合状态,则所有此类传入转换都将自动为其创建入口点。
需要注意的是,将在复合状态边缘结束的转换解释为使用历史状态点进入状态是 UML2 的非标准扩展。根据 UML2 标准,这样的转换将执行状态的“默认进入”,应该导致初始转换执行。在 RSARTE 中,只有在首次进入复合状态时才会发生这种情况。这是避免在不使用入口点的情况下进入复合状态的另一个原因。
State Exit Points
当一个状态包含了退出点时,退出点可以用于退出复合状态,同时在封闭状态图中由退出点引发的转换也将被执行,这个转换可以有一些实现代码和条件限制,来对模型的行为进行描述和控制。与汇聚点类似,退出点可以作为状态模型的一种重要元素,用于描述系统的行为和状态变化过程。
在我们的示例中,转换pong通过一个exit点离开State2。如果它被触发,那么封闭状态机中的转换pong将执行,这将导致进入State1。
也可以通过直接连接到复合状态边缘的转换,在不使用退出点的情况下退出复合状态。无论哪个子状态处于活动状态,都可以触发这种转换。
Summary of Sub State Transitions
表总结了使用上述示例的不同类型的子状态转换的行为。
To an entry point ping 当触发从State1到State2的ping转换时,State2将直接进入子状态State4.
To a history point toHistory 当触发从State1到State2的toHistory转换时,激活的State2的子状态将与上次退出State2时的活动状态相同。如果这是第一次进入State2,则子状态State3将被激活,因为这是初始转换的目标状态。
To the edge of a state sameAsHistory 这与上面描述的toHistory转换的行为完全相同。请注意,此转换在子状态图中不可见。
From an exit point pong 如果出现以下任一情况,将进行pong退出转换:
活动子状态为State3,并且触发转换pong
活动子状态为State4,转换pong2被触发
From the edge of a state toHistory 如果State1有一个子状态,那么无论其哪个子状态处于活动状态,都可以触发toHistory转换。
Code Execution Order
当一个事件到达并触发了一个退出和/或进入复合状态的转换时,将执行许多代码片段。首先会评估多个保护条件,从触发的转换开始。(这些保护条件)用于在会触发转换的情况下找到一系列具有满足保护条件的转换路径(称为复合转换),并以将被执行的最后一个转换作为结束点。如果在此评估过程中,在路径上找到一个保护条件为假的转换,则当前路径将不可用,评估程序将回溯并尝试找到另一条可用路径。这一目标的评估程序用于找到从当前状态到达一个目标状态的所有可能的路径,并确定选择哪条路径时条件的满足与否。
当已经找到一条可用转换路径时,接下来要做的是执行这些转换的效果代码。对于所有退出状态的转换,在执行转换效果之前,会首先执行该状态的退出代码。当到达一条路径上进入状态的转换时,会在执行转换效果后执行该状态的入口代码。
让我们看一个示例,它说明了上述规则:
假设以下情况:
- 当前的激活状态是S2。这意味着S1也是激活状态,因为S2被S1包含。我们称之为激活状态配置,由一组激活状态组成;在这种情况下是{S2, S1}。
- t1和t4都针对相同的事件E,且在相同的端口P上触发。
当事件E在端口P上到来时,如果t2的保护条件为假,且所有其他保护条件为真,则会发生以下情况:
- 评估了t1和t2的保护条件。由于t2的保护条件为假,转换路径的保护条件计算被中止,t3的保护条件不被评估。
- 在S2中没有更多的出站转换,因此转换到包含状态S1的转换,这意味着评估t4的保护条件。这是找到所有保护条件(仅一个)都满足的第一条可用路径,因此启用此转换并将触发它。
- 执行S2的退出代码。
- 执行S1的退出代码。
- 执行t4的effect code。
- 执行T1的入口代码。
- 执行t1的effect code。
- 执行T3的入口代码。
在这里,我们假设是第一次进入T1,因此将使用其初始转换到达子状态T3。
如果在E到达P时所有保护条件都为真,则会发生以下情况:
- 评估t1、t2和t3的保护条件。所有保护条件都满足,这意味着启用此转换路径,并将触发t1。
- 执行S2的退出代码。
- 执行t1的effect code。
- 执行S1的退出代码。
- 执行t2的effect code。
- 执行T1的入口代码
- 执行t3的effect code。
- 执行T2的入口代码。
Kinds of Transitions 转换的种类
有三种不同类型的转换:外部转换、内部转换和局部转换。它们在进入和退出代码的处理方式上有所不同。
转换类型是在转换的“属性”视图中的“类型”字段中设置的。
一类特殊的转换是自转换。这些转换的源状态和目标状态相同。所有的内部转换都是自转换,而外部和本地转换可以是自转换,但不一定是。
External Self Transitions
外部自转换的行为与其他外部转换类似(请参见外部状态转换),即退出状态,然后再次进入状态。这意味着代码执行顺序与所有其他外部转换相同;首先执行状态的退出代码,然后执行转换的效果代码,最后执行同一状态的进入代码
下面的图片显示了一个带有两个外部自转换的状态Top,即goActive和ExternalTrigger
Top是一种复合状态,这意味着当触发这些自转换中的任何一个时,将首先退出子状态(从内到外),然后退出Top,然后执行转换效果,然后进入Top,最后进入子状态(由外到内)。通常会发生以下情况:
Exit code for the innermost state is executed
Exit code for the next outer state is executed
…
Exit code for the outermost state is executed
Transition effect code is executed
Entry code for the outermost state is executed
Entry code for the next inner state is executed
…
Entry code for the innermost state is executed
Internal Transitions
当内部转换触发时,状态机保持在当前状态。这意味着唯一执行的代码片段是内部转换的效果代码。状态的退出和进入代码不执行。
内部转移通常在状态符号的单独区域中显示。符号右上角的图标显示内部转移的存在,并且您可以双击此图标以显示或隐藏内部转移区域。以下是具有内部转移 t1 的状态的示例:
在 State2 内,可以使用虚线来显示内部转移作为边界转移(线条样式为“点划线”,以区别于当偏好设置为“虚线”和“双点划线”时使用的线条样式)。它放置在状态图中,以进一步强调当触发时内部转移不会离开状态。
然而,RSARTE还支持另一种内部转移的可视化方式,即它们显示在状态图表格的下方特殊区域(在 diagram 框架内或外部),或在状态图表格之外的区域。这些区域以与封闭状态相似的方式显示内部转移。
内部转换可以用作事件错误处理器。例如,假设你有一个具有许多状态和转换的状态机。如果在状态机处于意外状态时出现事件,则该事件可能会无法处理。为了避免这种情况,您可以创建一个封闭的复合状态(我们称之为Top),并为状态机可以接收到的每个事件创建一个内部转换。如果一切都正常工作,这些内部转换将永远不会执行,因为会触发子状态之间的转换而不是Top上的内部转换。但是,如果到达一个事件不会触发任何其他转换(因为状态机处于意外状态),则会触发Top上的内部转换,并且可以在其效果代码中处理意外情况。
如果状态不包含退出或进入代码,则外部自转换在功能上与内部转换等效。在这种情况下,建议使用内部转换:
• 状态边界上的连线越少,图表的清晰度就越高。
• 由于无需调用任何退出或进入代码片段,生成的代码更有效。
您可以使用 Refactor 上下文菜单中的一个命令将外部自转换转换为内部转换:转换为内部转换。在状态中也提供类似的命令,以便您将其所有外部自转换转换为内部转换。
Local Transitions
局部转换执行转换的效果代码以及所有子状态的退出和进入代码,但不执行与其连接的状态的退出和进入代码。因此,局部转换仅对复合状态有意义(如果状态是非复合的,则其行为与内部转换相同)。
局部转换始终源自子状态图的边界,并使用实线进行绘制,以区别于内部转换,以防局部转换是自转换。
以下是具有局部自转换LOC的复合状态State1的示例:
Local Transitions
局部转换执行转换的效果代码以及所有子状态的退出和进入代码,但不执行与其连接的状态的退出和进入代码。因此,局部转换仅对复合状态有意义(如果状态是非复合的,则其行为与内部转换相同)。
局部转换始终源自子状态图的边界,并使用实线进行绘制,以区别于内部转换,以防局部转换是自转换。
以下是具有局部自转换LOC的复合状态State1的示例:
如果在触发LOC转移时状态INNER是活动的,则会发生以下情况:
- 执行 INNER 的退出代码。
- 执行 LOC 的effect代码。
- 执行 INNER 的入口代码。请注意,本地转移在历史点终止,由于 INNER 之前是活动的,所以它会再次进入而不执行初始转移。
通常情况下,当触发本地转移时,会发生以下情况:
当触发本地转移时,通常会按照以下顺序进行操作: - 执行最内层状态的退出代码。
- 执行下一个外层状态的退出代码。
- …
- 不执行最外层状态的退出代码。
- 执行转移效果代码。
- 不执行最外层状态的入口代码。
- 执行下一个内层状态的入口代码。
- …
- 执行最内层状态的入口代码。
本地转移可以用于简化状态图。如果我们有许多状态,并希望能够无论处于哪个状态都能够转移到特定状态,那么我们可以创建一个包含内部状态的组合状态,并从该状态绘制一个本地转移到我们想要转移到的状态。如果没有包含的组合状态,我们将不得不从每个状态创建到目标状态的转移。
以下是一个示例:下图中显示了 goMiddleActive 转移。它起始于 Top 状态的边缘,并终止于 Middle_Active 子状态。当执行此转移时,无论 Top 的哪个子状态处于活动状态,它都会导致 Top 状态转移到 Middle_Active 子状态。该转移是局部的,仅限于 Top 状态,不会导致任何 Top 的退出或进入代码执行,但会执行 Middle_Active 及其子状态的进入代码。
Example Use of Different Kinds of Transitions
作为使用不同类型转移的示例,让我们再看一下之前显示的 Top 状态的示例。为了更好地了解,请看下面的图片,其中显示了带有内联子状态的 Top 状态。
Top 状态包含以下转移:
外部转移:
ExternalTrigger
离开 Top 状态并返回到 Top 状态的边缘。因此,它与通过历史点进入 Top 相同。
goActive
离开一个退出点并返回到一个特定的进入点,该点将直接通过 toMiddleActive 转移进入 Middle_Active 子状态,然后通过 toInnerActive 转移进一步进入 Inner_Active 子状态。
Initial
使Top状态成为其状态机的默认状态。当草图被实现时,将进行初始转移并执行其效果代码。然后进入 Top 状态。Top 包含两个子状态 Middle_Init 和 Middle_Active,并具有以下转移:
内部转移
InternalTrigger
只有被触发时才会执行该转移的效果代码。不会执行任何进入或退出代码。
局部转移
LocalTrigger
该转移将执行其效果代码和活动子状态(直接或间接)的退出和进入代码,但不会执行 Top 的退出和进入代码。
goMiddleInit
当此转换触发时,将导致转换到状态 Middle_Init,而不管 Middle_Init 或 Middle_Active 是否为活动状态。将执行 Middle_Init 的进入代码,但不会执行 Top 的退出和进入代码。
goMiddleActive
当此转换触发时,将都会导致进入状态 Middle_Active,而不管 Middle_Init 或 Middle_Active 是否为活动状态。将执行 Middle_Active 的进入代码,但不会执行 Top 的退出和进入代码。
toMiddleActive
此转换始于 goActive 转换从封套状态机的 Top 状态进入的进入点。因此,该转换可能没有任何触发器。只有当 goActive 转换被触发时,它才能执行。但是,该转换可能具有效果代码和守卫条件。
外部转移
Initial
当没有使用任何进入点进入 Top 状态时,将进行此初始转换。这发生在实现草图时,因为封套状态机的初始转移流向 Top。
最后,让我们看看 Middle_Active 状态机。它包含两个子状态 Inner_Init 和 Inner_Active,并具有以下转移:
局部转移
goInnerInit
当此转换触发时,将导致转换到状态 Inner_Init,而不管 Inner_Init 或 Inner_Active 是否为活动状态。将执行 Inner_Init 的进入代码,但不会执行 Middle_Active 的退出和进入代码。
goInnerActive
当此转换触发时,将导致进入状态 Inner_Active,而不管 Inner_Init 或 Inner_Active 是否为活动状态。将执行 Inner_Active 的进入代码,但不会执行 Middle_Active 的退出和进入代码。
toInnerActive
此转换始于 toMiddleActive 转换到达的进入点。如果我们查看封套状态机,我们会发现此转换路径始于 Top 状态,并由 goActive、toMiddleActive 和 toInnerActive 转换组成。所有这些转换都可能具有自己的效果代码和守卫条件,但只有第一个(goActive)可能具有触发器。
外部转移
Initial
当没有使用任何进入点进入 Middle_Active 状态时,将进行此初始转换。这发生在实现草图时,因为封套状态机的初始转移流向 Middle_Active,并且顶层状态机的初始转移流向 Top。