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

StateWizard VC++ 插件和带源代码的引擎

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (22投票s)

2006 年 6 月 11 日

CPOL

12分钟阅读

viewsIcon

196339

downloadIcon

2886

一个跨平台的面向状态的应用框架,以及一个类似于 ClassWizard 的、支持双向 UML 动态建模/开发工具,可在常用 IDE 中运行。旨在为 Win32/Linux 提供并发、分布式和实时应用开发工具。

引言

在软件系统日益复杂、并发和实时化日益增强的环境中,建模语言和工具的选择成为许多项目成功的关键。

本文介绍了一个跨平台的面向状态的应用框架,使用标准的 C++/C 为 Win32/Linux 编写,以及一个类似于 ClassWizard 的双向 UML 动态建模/开发工具:StateWizard。

关注点

下表列出了为本项目编写的广泛技术

  1. 使用 DTE 为 VS2003 或 VS2005 进行 Visual Studio 插件开发
  2. COM 开发
  3. 异步函数调用
  4. 跨平台操作系统相关库
  5. 高质量状态机引擎
  6. Win32 映射文件分析

有什么新鲜事?

UML:标准

统一建模语言TM - UML - 是 OMG 最常用的规范,也是世界各地用于建模应用程序结构、行为和架构,以及业务流程和数据结构的方式。[1]

UML 动态视图用于表达和建模系统随时间推移的行为,通过状态机、顺序图和活动图呈现。

状态机图捕捉软件系统的行为。状态机可用于建模类、子系统或整个应用程序的行为。它们还通过协议或事件驱动的系统,提供了与外部实体进行通信的绝佳方式。

顺序图用于显示用户、屏幕、对象和系统内实体之间的交互。它们提供了对象之间随时间传递消息的顺序图。

为什么选择 UML StateWizard?

你可能会问,为什么是 UML StateWizard,而市面上已经有许多优秀的 UML 工具,如 Rational Rose、VisualState 等。我们的答案是:

  • StateWizard 是一个类 ClassWizard、简单高效的 IDE 集成工具
  • StateWizard 是一个功能齐全的 UML 工具 [2]
  • StateWizard 基于扎实优化的概念、实现选择和数据结构
  • StateWizard 具有许多有用的“杂项”功能

1. 类 ClassWizard 式的、简单高效的 IDE 集成工具

在开发 StateWizard 时,我们始终牢记提供一个直观易用的解决方案。

  • 就像 Microsoft Visual C++ ClassWizard 一样,StateWizard 在集成开发环境内运行。无需在工具之间切换进行设计/开发,而许多 UML 建模工具则作为独立应用程序运行。
  • 无中间建模文件,直接根据源代码中的一组特定宏和标志构建 UML 图。
  • StateWizard 可快速构建基于状态机的框架应用程序。它高效地将状态图直接编码为平台无关的标准 C/C++。
  • StateWizard 为嵌入式系统开发提供状态跟踪、模拟和调试功能。

UML StateWizard 支持以下 IDE:

  • Visual C++ 6.0
  • eMbedded Visual C++ 4.0
  • Visual Studio 2003
  • Visual Studio 2005

2. 功能齐全的 UML 工具

一些 CASE 工具允许用户从一组规范生成代码。然而,有时生成的代码可能并不太有用,因为它可能冗长且难以理解。StateWizard 提供了以下解决方案:

  • UML 图支持:StateWizard 支持构成 UML 状态机的九种图。它支持状态树和状态图绘制。
  • 正向工程:StateWizard UML 工具的应用不止于对图进行图形化描述。由于图中定义的系统结构被开发人员翻译成实际源代码,StateWizard 通过生成带有方法存根的状态机框架源代码来弥合这一步骤。开发人员可以拿走这些存根代码并填充实际代码。这种自动化生成源代码的特性称为正向工程。
  • 逆向工程:逆向工程与正向工程完全相反。在逆向工程中,StateWizard 加载应用程序/系统的所有文件,识别应用程序之间的状态关系,并基本上重建整个状态机结构及其所有关系。
  • 双向工程:除了正向工程和逆向工程之外,另一个有用的功能是双向工程。正向工程和逆向工程本质上是一次性活动,它们接受输入并生成所需的输出。双向工程扩展了这些功能。软件设计的一个重要规则是,没有设计是永恒不变的。这对于小型系统和大型系统都同样适用。在开发过程中,UML 模型中定义的设计结构会发生变化,以适应设计中未预料到的物理实现差异。将系统设计与源代码中的更改保持同步变得非常困难。双向工程功能使用户能够让 UML 工具将模型与应用程序代码中的更改同步。StateWizard 倾向于强制 UML 工具能够根据代码部分的修改实时同步其模型。例如,开发人员可以对状态图进行拖放操作,之后源代码也会随之更改;反之,开发人员可以直接修改代码部分,修改将自动显示在状态图上。这使得 StateWizard 能够充当状态图和源代码之间的桥梁。
  • 文档:StateWizard 为设计人员提供了一种在图表中记录设计决策的方法。设计人员可以保存状态图为 BMP 文件,并将状态树保存为文本文件。

3. StateWizard 基于扎实优化的概念和实现选择

除了应用程序和端口等创新的概念外,StateWizard 还通过优化的数据结构和实现选择进行设计,保证了高效的应用代码生成和双向工程。在其功能中,我们可以指出:

状态图

状态图在没有沉重、昂贵的 CASE 工具的情况下对嵌入式系统进行建模。状态图为您提供了应用程序逻辑结构和流程的“鸟瞰图”。它以图形方式构建状态层次结构,将子状态嵌套在其父模式中。状态图相对于状态树的另一个主要优势是,它以从一个状态到另一个状态的箭头形式显示每个状态的转换。如果开发人员选择了一个状态或转换,然后右键单击鼠标,StateWizard 将根据所选状态或转换通过菜单提供一个操作列表。

Screenshot - 1StateChartPlayer24.JPG

图:状态图

状态树

状态树是状态机程序员的助手,位于 VC 工作区选项卡窗口中。在编码时,它将提供一个状态机框架。它使开发人员能够轻松执行某些路由任务,例如创建状态机应用程序、创建状态层次结构、为状态定义入口函数和退出函数,以及定义事件处理程序。运行时,它是所有活动应用程序的状态跟踪器。如果开发人员选择了一个状态树项,然后右键单击鼠标,StateWizard 将根据所选项目、应用程序或状态通过菜单提供一个操作列表。

Screenshot - State_Tree_Pseudo.jpg

图:状态树

StateTree 和 StateChart 如何支持逆向工程

除了 C/C++ 源代码之外,没有额外的状态机信息。StateTree 和 StateChart 如何支持逆向工程?就像 Visual C++ ClassWizard 一样,StateWizard 定义了一组宏来构建状态机、定义状态、子状态、声明事件处理程序和状态转换。

例如,以下 C++ 宏定义了一个名为 Player 的状态机,具有以下状态:

  • Player (PowerDown, PowerUp (Playing,Pause))

以及状态转换

  • (EXT_EVENT_ID_POWER, PowerDown -> PowerUp, OnPowerDownEXT_EVENT_ID_POWER)
  • (EXT_EVENT_ID_POWER, PowerUp -> PowerDown, OnPowerUpEXT_EVENT_ID_POWER)
  • (EXT_EVENT_ID_PAUSE_RESUME, Playing -> Pause, OnPlayingEXT_EVENT_ID_PAUSE_RESUME)
  • (EXT_EVENT_ID_PAUSE_RESUME, Pause -> Playing, OnPauseEXT_EVENT_ID_PAUSE_RESUME)
SME_HANDLER_CLASS_DEF(Player)
SME_BEGIN_STATE_DEF(Player,Player)
   /*{{SME_STATE_DEF(Player,Player)*/
   SME_STATE_ENTRY_FUNC(PlayerEntry)
   SME_STATE_EXIT_FUNC(PlayerExit)
   /*}}SME_STATE_DEF*/
   SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(Player,PowerDown)
   /*{{SME_STATE_DEF(Player,PowerDown)*/
   SME_STATE_ENTRY_FUNC(PowerDownEntry)
   SME_STATE_EXIT_FUNC(PowerDownExit)
   SME_ON_EVENT(EXT_EVENT_ID_POWER,OnPowerDownEXT_EVENT_ID_POWER,PowerUp)
   /*}}SME_STATE_DEF*/
   SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(Player,PowerUp)
   /*{{SME_STATE_DEF(Player,PowerUp)*/
   SME_STATE_ENTRY_FUNC(PowerUpEntry)
   SME_STATE_EXIT_FUNC(PowerUpExit)
   SME_ON_EVENT(EXT_EVENT_ID_POWER,OnPowerUpEXT_EVENT_ID_POWER,PowerDown)
   /*}}SME_STATE_DEF*/
   SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(Player,Playing)
   /*{{SME_STATE_DEF(Player,Playing)*/
   SME_STATE_ENTRY_FUNC(PlayingEntry)
   SME_STATE_EXIT_FUNC(PlayingExit)
   SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPlayingEXT_EVENT_ID_PAUSE_RESUME,Pause)
   /*}}SME_STATE_DEF*/
   SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(Player,Pause)
   /*{{SME_STATE_DEF(Player,Pause)*/
   SME_STATE_ENTRY_FUNC(PauseEntry)
   SME_STATE_EXIT_FUNC(PauseExit)
   SME_ON_EVENT(EXT_EVENT_ID_PAUSE_RESUME,OnPauseEXT_EVENT_ID_PAUSE_RESUME,Playing)
   /*}}SME_STATE_DEF*/
   SME_END_STATE_DEF
/*{{SME_STATE_STATETREE_SEPARATOR}}*/
SME_BEGIN_STATE_TREE_DEF(Player)
   /*{{SME_STATE_TREE_DEF(Player)*/
   SME_STATE(Player,Player,SME_INVALID_STATE,PowerDown)
   SME_STATE(Player,PowerDown,0,-1)
   SME_STATE(Player,PowerUp,0,Playing)
   SME_STATE(Player,Playing,PowerUp,-1)
   SME_STATE(Player,Pause,PowerUp,-1)
   /*}}SME_STATE_TREE_DEF*/
   SME_END_STATE_TREE_DEF
/*{{SME_DEC_IMP_SEPARATOR}}*/
SME_APPLICATION_DEF(Player, "Player")

状态机运行环境

实时或嵌入式应用程序的设计通常涉及将应用程序软件分解为对象,其中一些对象可能实现有限状态机来控制进程或系统。这些控制对象的示例可能是电话交换系统中的呼叫控制类,或医疗器械中的治疗控制类。有限状态机是响应式、事件驱动的控制结构,其行为由接收到的事件及其之前的事件历史决定。其他非响应式类提供计算或数据检索服务,不需要维护机器状态或刺激历史来完成其功能。

在 StateWizard 环境中,这些活动类实现状态机并可以处理输入事件。StateWizard 状态机引擎管理这些类并充当事件调度程序。

状态机对象可以处于活动状态或非活动状态。状态机引擎仅将事件分派给活动状态机对象。非活动状态机对象不处理事件。

StateWizard 运行时环境的架构如下所示,显示了运行在状态机引擎之上的应用程序对象和线程。RTOS 虚拟层提供了一个通用的接口,用于常见的 RTOS 服务,如信号量、互斥锁和线程控制。服务提供商层是与低级模块和硬件接口的接口层。模拟或目标环境层提供与目标环境或目标环境模拟的接口。软件开发过程中经常使用模拟,当真实的硬件尚未可用时,或者当需要广泛的调试功能但目标上不可用时。

Screenshot - Interaction.jpg

图:StateWizard 运行时架构

状态机线程

多个活动状态机对象可以在单个线程上下文中运行。线程上下文标识一个独立的执行线程,并包含线程标识符、一组对象、内部事件池和几个事件挂钩函数。

状态机应用程序框架

StateWizard 为状态机对象提供两个应用程序框架:

  • 标准嵌入式 C
  • 标准 C++

对于标准 C++ 应用程序框架,状态机对象由 C++ 类构造。

状态机对象激活

要激活,状态机对象通过调用 SmeActivateObj() 函数向状态机引擎标识自身。以下示例创建了一个名为 Player1Player 状态机对象。Player1 在一个名为 AppThreadProc 的单独线程下运行。线程上下文是 g_AppThreadContext

void* AppThreadProc(void *Param)
{
  // The second parameter (NULL) is the state machine Player1's parent.

  SmeActivateObj(&Player1,NULL);
  SmeRun();
}
SME_OBJ_DEF(Player1,Player)
SME_THREAD_CONTEXT_T g_AppThreadContext;
XTHREADHANDLE ThreadHandle = 0;
// Install thread local storage data functions.

XTlsAlloc();
SmeSetTlsProc(XSetThreadContext, XGetThreadContext);
// Initialize the engine

SmeInitEngine(&g_AppThreadContext);
// Install external event handler functions.
// They act as a plug-in of the state machine engine.

SmeSetExtEventOprProc(XGetExtEvent, XDelExtEvent, XPostThreadExtIntEvent, 
                      XPostThreadExtPtrEvent, XInitMsgBuf, XFreeMsgBuf);
int ret = XCreateThread(AppThreadProc, NULL, &AppThreadHandle);
// The second parameter is the parameter for the thread entry function.

播放器控制面板可以将外部事件发布到运行线程的事件队列中。引擎会将它们分派给活动的应用程序 Player1

XPostThreadExtIntEvent(&g_AppThreadContext, EXT_EVENT_ID_POWER, 0, 0, 
                       NULL,0,SME_EVENT_CAT_OTHER);

状态机对象去激活

通过调用 SmeDeactivateObj() 函数来去激活状态机对象。以下语句去激活 Player1 状态机对象。

SmeDeactivateObj(&Player1);

服务提供商

状态机应用程序可能与低层(其他模块)或硬件(称为服务提供商)紧密协作。服务通过一组可供服务用户(应用程序)使用的原语(操作)来正式定义。这些原语告诉服务执行某个操作,或报告对对等组件/实体所采取的操作。服务原语分为四类:

  • 请求
  • 指示
  • 响应
  • Confirm

[计算机网络,Andrew S.Tanenbaum]。请求和确认原语可以实现为服务调用的形式。指示和响应原语可以实现为外部事件发布的形式。

活动状态机对象可以使用以下机制之一与服务提供商通信,以交换信息:

  1. 同步服务调用。这些同步服务调用可能会阻塞,在操作完成后将控制权返回给应用程序。由于大多数实时系统必须在预定的时间内响应外部事件,因此不同步服务调用。
  2. 异步服务调用。异步函数不阻塞,继续执行并在操作完成之前返回。
  3. 主动事件指示。这些事件从服务组件发布到客户端组件,无需任何请求。例如,可能是由硬件中断服务例程发布的事件。

平台无关的嵌入式系统开发

嵌入式系统市场上有许多不同的实时操作系统(RTOS)。每个不同的 RTOS 都提供不同的 API 来访问一组通用的系统服务。StateWizard RTOS 虚拟层提供了一个平台独立的适配层,允许状态机在不同的操作系统之间移植。我们可以将应用程序从一个 RTOS 迁移到另一个 RTOS,而无需更改应用程序代码。RTOS 虚拟层中的外部事件接收器充当状态机引擎的插件。

模拟器

应考虑模拟器的情况:

  • 硬件不可用或成本高昂
  • 需要广泛的调试功能

模拟器是测试工具的几个重要类别之一。它们模拟软件最终将运行的环境,并通常包含有用的调试功能。它们还使测试人员能够更有效地控制测试环境,并且当稀缺的目标硬件必须在工程师之间共享时,可以节省关键的开发资源。

模拟允许在硬件原型可用之前开始软件测试。它还随着系统的大小和复杂性进行扩展,有助于在开发周期早期识别和解决问题,此时修复成本仍然较低。

在模拟环境中,开发人员可以在桌面平台(例如 Windows 或 Linux)上模拟服务提供商。这些模拟服务具有与目标服务提供商接口相同的接口。迁移到目标环境时,在模拟环境中测试过状态机应用程序后,应只需少量工作即可将其集成到实际环境中。活动应用程序可以调用服务提供商提供的服务,并通过 RTOS 函数接收由服务提供商触发的外部事件。

您可以将 StateWizard 应用于:

  • 通过窗口消息钩子技术进行基于状态机的 Win32/WinCE 程序开发
  • 跨平台嵌入式系统建模和模拟

具有产品质量的 StateWizard Pro 引擎

  • 分层状态机
  • 通过将状态树定义分离到多个 C/C++ 文件中,支持具有数百个状态的大型状态机
  • 状态历史信息和历史转换
  • 事件处理时的守护转换
  • 条件伪状态
  • 连接伪状态
  • 多线程正交状态
  • 内置状态计时器;进入状态时自动启动内置计时器,退出状态时停止计时器
  • Linux/Win32 的跨平台事件循环和操作系统相关 API 库

在此处 下载 StateWizard Pro

许可

您可以从这里 下载更多信息,这是 UML StateWizard 开源项目的官方网站,采用 LGPL 许可证。

参考文献

© . All rights reserved.