Qt 5.8 如何使用SCXML框架与子状态机?

3
我在尝试建立一个由主状态机编排,执行不同任务的子状态机系统。作为Qt的长期用户,我研究了Qt 5.8中的新SCXML实现。然而,我无法弄清楚如何使用5.8提供的API来正确地实现子状态机。
我的想法是使用一个主状态机,然后在主状态机的状态中调用特定的子状态机。虽然调用子状态机可以工作,但我无法在顶级状态机对象发出invokedServicesChanged(..)后访问它们。我可以访问指向QScxmlInvokableService的指针,但无法访问有关的状态机。
此外,顶层状态机对象仅公开来自顶层的状态和事件,并未公开来自调用状态机的状态和事件。例如,topLevelStateMachine->activeStateNames()只列出顶级状态。
查看Qt的源代码,我发现QScxmlInvokableService实际上是QScxmlScxmlService的基类,后者包含有关状态机的指针。不幸的是,QScxmlScxmlService定义在qscxmlinvokableservice_p.h中,这是私有的,因为名称中的“_p”表示它是私有的。那么我该如何使用公共SCXML API呢?我错过了什么吗?我记得SCXML支持在5.7中是技术预览,但现在已经作为正常发行版的一部分包含在5.8中。

1
我认为这是API中的疏忽。请随意将QT+= scxml_private添加到项目中,并使用实现细节来完成您需要完成的工作。那个Qt模块相当新,确实缺少许多必要的部分,以使其有用。 - Kuba hasn't forgotten Monica
当你说“子状态机”时,是指单独的.scxml状态机,还是指复合状态?如果是前者,为什么不使用后者? - Phrogz
单独的状态机。我认为在一个XML文件中拥有一个大的状态机并不是一个好主意,但理论上也可以通过复合状态来解决。您会如何使用SCXML和Qt组织具有许多子状态的大型状态图? - Nils
1
我不确定如何一般性地回答那个问题。过去,我曾为几个“大型”(根据您的定义)状态图做过这样的事情。每个状态图都是不同的,有不同的方法来组织状态,并且对于如何并行和复合组织状态,我必须做出设计决策,每种方法都有不同的优缺点。一个好的可视化编辑器对此非常重要:请参见我的这篇白皮书中的图2,其中有一个摘录示例。 - Phrogz
谢谢您的快速回答,我需要学习这个 :) - Nils
1个回答

0

我花了最近一周的时间研究示例,然后编写了自己的状态机和响应代码。由于文档不够清晰,所以这花费了一些时间。

我发现activeStateNames确实检索了包含在状态机中的所有状态,包括子状态。

我读了几遍traffic light example才明白这一点。关键是子状态机包含在特定的状态中。(状态机的图形视图在这里有帮助。)

在示例中,总体上只有两个状态:workingbroken。转换由事件smashrepair控制。

在这两个状态中,都有小一些的状态机。Broken 状态包含了一个由两个状态 blinkingunblinking 组成的状态机。当进入 broken 状态时,状态机会从 blinking 开始。

当处于 broken 的子状态 blinking 中时,如果使用默认值 false 调用 activeStateNames 函数,它将返回 blinking。如果使用参数 true 调用该函数,则会返回 blinkingbroken 两种状态。

那么如何使用它呢?

如果我有一些东西想要根据特定状态设置/取消设置,我可以在机器中使用connectToState。我连接到的插槽将在状态在活动和非活动之间更改时被调用,并且它将接收一个布尔值,表示状态是否处于活动状态。在交通灯示例中,状态red连接到redLight。由于当处于red状态时应该打开redLight而不是其他情况,因此它连接到一个带有布尔值的插槽:true打开灯,false关闭灯。
好的,但如果我想在进入状态时捕获事件呢?
我只需选择状态,然后添加onEntry->send并指定事件名称。这将导致在进入状态时发送事件。在Qt 5.8中,可以使用connectToEvent将此事件路由到插槽中。[Qt 5.7版本仅具有通用的eventOccurred信号,您可以将其发送到插槽中,然后使用event.name()查询哪个事件。]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接