内容


为嵌入式设备构建可执行的处理模型

使用 DITA、PHP 和 blob 构建模型

Comments

Darwin 信息类型化体系结构(Darwin Information Typing Architecture,DITA)为设计、书写、管理和出版用于印刷和 Web 上的很多种信息定义了一种 XML 体系结构。它提供一种方式,将关于组织中任何事情的知识组织在一种一致的、计算机可识别且人类可理解的结构中。这种有组织的知识可以是机械的装配说明、客户的付款流程或化学溶剂的配方等。

DITA 是一个 XML 构造集合,这些构造代表各种可用于构造和存储知识(基本上为文本)的方式。这些构造叫做主题(topic)— 即一个信息单元(此信息是自包含的,并且可以回答单个问题)。借助于那些帮助您在一系列主题之间导航的 DITA 地图(map) 或文档,主题之间可以相互关联。

概念(concept)构造为例。使用该主题来为理解关于产品或流程的重要信息提供背景知识。清单 1 展示的例子是一个针对轴承的概念主题。

清单 1. 针对轴承的概念主题
<concept id="bearing"> 
     <title>Bearings</title> 
       <conbody> 
          <p>Bearings are a mechanism to minimize friction and 
               loss of rotational energy</p> 
          <example> 
               <p>Common bearings mechanisms</p> 
               <ul> 
                    <li>Ball bearing</li> 
                    <li>Roller bearing</li> 
                    <li>Needle bearing</li> 
               </ul> 
          </example> 
          <p>Fully mechanical bearings require lubrication</p> 
          <example> 
               <p>Common lubricants</p> 
               <ul> 
                    <li>oil</li> 
                    <li>lithium grease</li> 
               </ul> 
          </example> 
     </conbody> 
</concept>

有好几种主题可用,但是本文中感兴趣的是任务(task)主题。

构建 DITA 任务主题

DITA 任务主题描述如何完成一个过程,以实现特定的目标。换句话说,它回答 “我怎么做……” 类型的问题。它的关键构造有:

  • <prereq>提供开始一个过程所需的信息。
  • <context>提供执行任务步骤的背景信息。
  • <steps>完成一个任务必须执行的特定操作(步骤必须包含一个或多个命令(或 <cmd>)元素,描述必须完成的具体操作)。
  • <result>任务的预期结果。
  • <example>一个可选的例子,演示任务的使用。
  • <postreq>成功完成该任务之后可以执行的一些可选的任务。

清单 2 提供了一个示例任务主题。

清单 2. 示例任务主题
<task id='add_auto_oil'>
     <title>Adding oil to an auto engine </title>
     <taskbody>
          <prereq>Oil container with optional opener</prereq>
          <context>Dashboard oil light on or set amount of 
                                      mileage elapsed</context>
          <steps>
               <step id='open_hood'>
                    <cmd>Locate 'Open hood' lever.</cmd>
                    <cmd>Operate 'Open hood' lever.</cmd>
                    <cmd>Raise and retain hood in open position.</cmd>
               </step>
               <step id='open_oil_reserve'>
                    <cmd>Locate oil reserve cap.</cmd>
                    <cmd>Twist open oil reserve cap over
                                           oil reserve opening
                    </cmd> 
               </step>
               <step id='fill_oil_reserve'>
                    <cmd>Locate oil container</cmd>
                    <cmd>Open oil container</cmd>
                    <cmd>Place oil container spout over oil 
                                           reserve opening
                    </cmd>
                    <cmd>Remove oil container when empty</cmd>
               </step>
               <step id='close_oil_reserve'>
                    <cmd>Locate oil reserve cap.</cmd>
                    <cmd>Twist close oil reserve cap over oil 
                                           reserve opening
                    </cmd>
               </step>
               <step id='close_hood'>
                    <cmd>Close and lock hood</cmd>
               </step>
          </steps>
          <result>Dashboard light off or engine running quietly</result>
     </taskbody>
</task>

在本文中,任务被改编来描述一个系统,然后被转换成一个有用的可执行的表单。该方法的关键在于使用了关系数据库中的 blob 表示法。

采用 blob

在关系数据库中,一个 blob 是一个二进制大对象,可以存有可变数量的数据,并且数据值被看作二进制或者说字节、字符串 — 实际上是任何字节序列,值可以是从 0x00 到 0xFF。

例如,可以在一个 blob 中存储一幅图像或者一个机器语言命令序列,最大大小通常不超过 65,536 字节。比这稍大一点的字符串存储到适当的行和列时会被截断。在本文中,是用 PHP 绑定 DITA 任务和 blob 的,它将任务映射到一种二进制格式。

PHP 的作用

前面提到过,DITA 由 XML 构造组成,其中一个构造是 DITA 任务主题。需要的是一个流水线化的工具,用来遍历任务结构。输入 PHP 及其 DOM 遍历类。将任务转换成一个有用的结构是通过命令行 PHP 脚本的强解析和 DOM 遍历功能完成的。

综合

既然关键元素都具备了,现在就该介绍把一切综合起来的方法了。简要地说,DITA 任务主题的内容被限制为一种计算机可识别的结构,叫做配方构造(recipe construct)。然后,使用一个配方构造支持的数据库和一个编译器,将配方构造转换成可执行的格式。

增强 DITA 任务主题

增强主题的第一步就是处理 DITA 任务主题元素的内容。回到 清单 2,元素的内容使用自由文本格式。必须将该格式改为结构化的、计算机可识别的格式。一般描述参见 清单 3

清单 3. 从自由格式更改为结构化格式
<task> 
     <title>[resourceURI]</title> 
     <taskbody>
          <context>[cont_iteration_yes_no]</context>
          <prereq> [resource_list]</prereq>
          <steps>
               <step id=”[stepID]”>
                    <cmd>[precondition]|[action_list]</cmd>
                    ................................
                    <cmd>[precondition]|[action_list]</cmd>
               </step>
               ....................................
               <step id=”[stepID]”>
                    <cmd>[precondition]|[action_list]</cmd>
                    ................................
                    <cmd>[precondition]|[action_list]</cmd>
               </step>
          </steps>
          <result>[resourceURI]</result>
     </taskbody>
</task>

[resource_list] = [resourceURI],[qty] {| [resource_list]} 
[precondition] = [action]([parm_list]) 
[condition] = [action]([parm_list]) 
[action_list] = [condition]->[action]([parm_list]) {;  [action_list]} 
[parm_list] = [parm]{,[parm_list]}		 
[parm] = [resourceURI] or [stepID] or [literal] 
[literal] = text or number 
[resourceURI] = URN 
[stepID] = label 
[action] = label  
[qty] = number 
[cont_iteration_yes_no] = iterate or oneshot
iterate = repeat the steps in the sequence they appear continually
oneshot = repeat the steps in the sequence they appear only once

{...} = optional element(s)

实际上,任务中的步骤使用 <prereq> 元素下列出的资源按照任务中指出的顺序执行。<result> 指向的资源包含(或者是)操作该任务的结果,而 <context> 与该任务执行的次数有关(一次或者不断执行)。

使用 <cmd> 元素中的 precondition 组件来检测,在执行命令的其余部分之前,是否已经具备某个具体的条件:

if [precondition] is true then perform [action_list]

这里,[action_list] 只是一组规则结构,依照这些规则,大于符号(>)左边的操作返回一个 Boolean 值(true 或 false);如果该操作返回 true 值,那么大于符号(>)右边的操作被执行。

比如说 清单 2 中任务主题的一部分,在转换成与 清单 3 一致的格式时,就变成了 清单 4 中的代码。

清单 4. 油箱盖遥控开关的配方构造
<task> 
     <title>urn:auto:oil:access</title> 
     <taskbody> 
          <context>oneshot</context> 
          <prereq>
               urn:robot:sense:image, 1 |
               urn:robot:arm, 2 |
               urn:auto:dashboard:hood_lever, 1 |
               urn:auto:engine:hood, 1 |
               urn:auto:engine:hood_lock, 1 |
               urn:auto:engine:oil_reserve_cap, 1 |
               urn:auto:engine:oil_reserve_intake, 1 |
               urn:robot:storage_pocket, 1
          </prereq> 
          <steps> 
               <step id='open_hood'> 
                    <cmd>
                    always() | always() ->
                         locate(urn:robot:sense:image,
                                urn:auto:dashboard:hood_lever)
                    </cmd>
                    <cmd>
                    always() | always() ->
                    activate(urn:robot:arm,
                              urn:auto:dashboard:hood_lever);
                         locate(urn:robot:sense:image,
                              urn:auto:engine:hood);
                    activate(urn:robot:arm,urn:auto:engine:hood)
                    </cmd>
                    <cmd>
                    fault_detected(urn:robot:sense:image) |
                    always() ->
                    report(urn:robot:sense:image);
                    always() -> shutdown(urn:robot);
                    </cmd> 
                    <cmd>
                    fault_detected(urn:robot:arm) |
                         status(urn:auto:engine:hood, "closed") ->
                         report(urn:robot:arm);
                         always() -> shutdown(urn:robot);
                    </cmd>
                    <cmd>
                    fault_detected(urn:robot:arm)|always() ->
                         lock(urn:robot:arm)
                    </cmd>
               </step>
          </steps>
          <result>urn:auto:engine:oil_reserve</result>
     </taskbody> 
</task>

其中:

  • always() = 总是返回一个值 True。
  • locate(resourceURI, resourceURI) = 使用第一个资源定位第二个资源。
  • activate(resourceURI, resourceURI) = 使用第一个资源激活第二个资源。
  • fault_detected(resourceURI) = 如果一个资源中检测到出错条件,则返回 True。
  • report(resourceURI) = 广播一个消息,指出资源的状态。
  • shutdown(resourceURI) = 关闭资源。
  • lock(resourceURI) = 将资源的操作冻结在当前状态。
  • move(resourceURI, resourceURI, resource URI) = 使用第一个资源 URI 将第二个资源 URI 移动到第三个资源 URI(容器)。
  • turn(resourceURI, direction, final_state) = 沿着一个方向(顺时针或逆时针)旋转资源,直到到达最终状态(打开或关闭)。

清单 4 假设存在一种自动设备(机器人),可以执行 auto oil filling 任务的步骤中列出的函数。这些函数定义在一个支持的数据库中,就是将任务转换成可执行表单时使用的那个数据库。

该代码展示了一个独步任务,即打开汽车发动机盖(open_hood)和油箱盖(open_oil_reserve)。第一步(打开发动机盖)中的命令是:

  1. 使用机器人的图像传感器无条件地定位控制杆,以打开发动机盖(locate 函数)。
  2. 使用机器人的手臂拉发动机控制杆(activate 函数),然后使用图像传感器定位机器人在发动机盖上的位置(locate 函数)。
  3. 使用机器人手臂打开发动机盖。

其余三个命令处理机器人图像传感器或手臂的错误行为。两种情况下都会报告错误(report 函数),对于手臂出错的情况,手臂被机械地锁定在当前状态(lock 函数)。在感知到遇到了什么错误之前,我们使用 fault_detected 操作来确定机器人是否处于出错状态。

可以使用 DITA 任务主题的这个改编版本来描述任何也涉及偶然因素(即从异常进行恢复)的循环过程。这个改编版本也是配方构造的基础。

返回去看一下 清单 4,向汽车发动机加油的过程用其核心过程(打开发动机盖、打开油箱盖、加油、关闭油箱盖、关闭发动机盖)进行描述,且每一步都可以包含处理异常情况的过程。

简要地说,这是一个配方构造— 即一个其元素内容可被编译成可执行表单的 DITA 任务主题。关于配方构造,还要注意一些其他事情:

  • 配方构造可以用异步方式调用另一个配方构造 — 就是说,一个配方构造可以在单独的执行线程中启动另一个配方构造的执行。
  • 因此,多个配方构造可以运行在一个多任务环境中。
  • 由于配方构造是通过 Uniform Resource Name (URN) 寻址来引用的,所以一个配方构造可以远程调用另一个配方构造 — 就是说,被调用的配方构造是通过网络寻址的。

因此,配方构造的执行空间可以涉及设备网络,并可以作为该网络上的异步任务进行操作。

以配方为中心的软件

配方构造是将任务描述转换成用于高端(例如,32位)微控制器环境中多任务环境的可执行表单过程的基础。配方构造可以用文本编辑器或者专门用于开发配方构造的工具来构建。

配方构造类似于菜谱,即步骤中用到了各种原材料,所有步骤必须按一致的顺序执行,才能最终装盘完成。在本例中,配方构造使用一组确定的资源和步骤来达到想要的结果 — 被监控环境中的一个产品或更改。

开始配方构造设计过程,即列出一组将自己分解为 <task> 构造的高端操作。例如,一组用于烘焙蛋糕的联网的批量制造机器:搅拌器、烤箱和涂布机。可以通过类似于 清单 4 内容的 <task> 构造,指定各自的混合、加热和涂抹装置操作 — 以及相关的装置诊断。

正如 清单 4 中所做的,列出的函数将存储在配方构造开发数据库中。该数据库加上一个配方构造编译器,组成了配方构造开发环境的核心。但是,首先必须了解已编译配方构造的目标环境。

RecipeJAUS

自治系统联合体系结构(Joint Architecture for Autonomous Systems,JAUS)是美国国防部(U.S. Department of Defense)开发的一个标准,目标是在自治系统(主要是汽车)的组件之间提供一个一致的消息传递框架。随着 OpenJAUS(参见 参考资料 中的链接,了解更多信息)的问世,JAUS 已经演变成一种开源技术。

JAUS 由于跟配方构造有关,所以定义了一种命令结构,跟微控制器的低级机器语言中的操作代码一样。一些 JAUS 命令已经被改编来提供配方构造的目标平台,叫做 RecipeJAUS。它由一个解释器和一个执行线程管理程序组成,前者用于处理配方构造编译器的输出,后者用于处理可作为多任务执行的配方构造元素。最后,通过一个编译器和相关的支持数据库,配方构造转换成一个 RecipeJAUS 命令序列。

打包过程

配方为中心的软件(recipe-centered software,RCS)数据库通过将配方构造任务步骤中使用的函数转换成相应的 RecipeJAUS 命令序列,支持配方构造的编译过程。清单 5 提供了 RCS 数据库的元数据结构。

清单 5. RCS 数据库元数据结构
# 
#       RC support database schema 
# 
# 

DROP DATABASE IF EXISTS RCSDB; 
CREATE DATABASE RCSDB; 
USE RCSDB; 

# 
#     Source for a recipe using DITA task format 
# 
#     NOTES - 
# 
#          (1) DITARecipeID - resource URI assigned to the recipe 
# 

CREATE TABLE DITATaskRecipe 
( 
       DITARecipeID     VARCHAR(50) NOT NULL, 
     TaskRecipeBody     TEXT,      
     PRIMARY KEY (DITARecipeID ASC) 
}; 
 
# 
#     Compilation of a DITA recipe into RecipeJAUS code 
# 
#     NOTES - 
# 
#          (1) JAUSRecipeID - DITARecipeID + ":" + date-time stamp (YYYYMMDDHHMMSS) 
# 

CREATE TABLE JAUSRecipe 
( 
       JAUSRecipeID     VARCHAR(60)     NOT NULL, 
       DITARecipeID      VARCHAR(50)     NOT NULL, 
     JAUSPackage          BLOB,
     PRIMARY KEY     (JAUSRecipeID ASC) 
}; 

#
#     Catalog of all possible actions that can be incorporated in a recipe source
#     organized by domain and action ID
#
#     NOTES -
#
#          (1) DomainURI - URI for the domain of this action
#          (2) ActionID - label for this action
#          (3) ParmList - comma-delimited list of arguments for this action with
#                      each argument in resourceURI format. Position in
#                      the list is important since the position index
#                      is used in the JAUSMapping below
#          (4) JAUSMapping - parametrized code for this action with links 
#                      to ParmList entries. These links are in inserted
#                       as a hex byte in the appropriate parameter position
#                      of a JAUS opcode. The value of this byte
#                      is the position index (0x00 - 0xFF) in the ParmList
#                      above
#

CREATE TABLE ActionCatalog 
( 
       DomainURI               VARCHAR(50) NOT NULL, 
       ActionID             VARCHAR(50) NOT NULL, 
       ParmList          TEXT, 
       JAUSMapping            BLOB, 
       PRIMARY KEY (DomainURI ASC, ActionID ASC) 
}; 

# 
#     Catalog of all possible resource URIs that can be incorporated in a recipe source 
#     organized by domain and action ID 
# 
#     NOTES - 
# 
#          (1) DomainURI - URI for the domain of this action 
#          (2) ResourceID - URI for this parameter type 
#          (3) Units - measurement units for this resource 
#          [4] JAUSEncoding - target JAUS reference for this resource 
# 

CREATE TABLE ResourceCatalog 
( 
       DomainURI                 VARCHAR(50) NOT NULL, 
       ResourceID                  VARCHAR(50) NOT NULL, 
       Units               VARCHAR(22), 
     JAUSEncoding     BLOB, 
       PRIMARY KEY (DomainURI ASC, ResourceID ASC) 
};

DITARecipe 表用于存储配方构造源任务构造,而 JAUSRecipe 表用于存储配方构造的最终信息包序列。因此,配方构造编译器保存配方构造及其已编译输出的一份归档,以便重用或者将配方构造设计映射到一个具有类似特征的优先配方构造。

ActionCatalog 包含配方构造步骤函数的一个参数化的 RecipeJAUS 命令序列。函数定义针对特定的领域。例如,一个针对机器人领域(urn:robot)资源的叫做 lock 的函数,与类似名称的针对自动化访问领域(urn:access:door)的函数,定义会不相同。

类似地,ResourceCatalog 定义应用程序领域中使用的资源的 URI。它也在 RecipeJAUS 平台提供该资源的一个内部地址引用(JAUSEncoding)。该引用对应于运行 RecipeJAUS 平台的硬件平台中访问该资源的物理连接。

RCS 编译器

RCS 编译器是一个 PHP 脚本,它利用 DOM 类及其方法来遍历配方构造的任务构造和计算 RecipeJAUS 信息包序列。该脚本已有提供(参见 下载),并且它使用 PHP 命令行界面 (CLI) 进行调用,如下所示:

php RCC.php <RC>.xml

<RC> 是包含配方构造任务构造的源文件的名称。带有 RecipeJAUS 步骤信息包序列的输出文件作为一个 JAUSRecipe 表记录存储在 RCS 数据库中,该记录的键值在编译结束时显示在控制台中。

RCS 编译器是一个原型版本,在通过 RCS 数据库的 ResourceCatalogActionCatalog 表转换成 RecipeJAUS 代码之前,产生 RecipeJAUS 序列。它当前在编译带有一个任务构造的文件。

一个样例配方构造应用程序

为了演示上面的配方构造转换过程,来看一个样例应用程序,它呈现为一个配方构造以及转换成 RecipeJAUS 命令序列之前的中间输出。

清单 6 提供了一个智能自动调温器的任务(或配方构造)描述。它考虑当前温度、当前湿度和当前季节,以便为所监视的区域确定供热或空调设置。

清单 6. 智能自动调温器配方构造
<task> 
     <title>urn:hvac:thermo>/title> 
     <taskbody> 
          <context>iterate</context> 
          <prereq> 
               urn:hvac:settings_lib,1| 
               urn:hvac:season_setting,1| 
               urn:hvac:tod_setting,1| 
               urn:hvac:temp_setting,1| 
               urn:hvac:hum_setting,1| 
               urn:hvac:temp_reading,1|
               urn:hvac:hum_reading,1| 
               urn:hvac:h_trigger,1| 
               urn:hvac:v_trigger,1|urn:hvac:ac_trigger,1 
          </prereq> 
          <steps> 
               <step id='S0'> 
                    <cmd>always() | always()->
                    get_setting(urn:hvac:settings_lib, 
                              urn:hvac:tod_setting,
                              urn:hvac:season_setting)
                    </cmd> 
                    <cmd> setting_set(urn:hvac:settings_lib,
                    urn:hvac:season_setting,urn:hvac:tod_setting) | 
                    always()-> 
                    set_setting(urn:hvac:settings_lib,
                              urn:hvac:temp_setting,
                              urn:hvac:hum_setting) 
                    </cmd> 
               </step> 
               <step id='S1'> 
                    <cmd> 
                    step_OK("S0") | always() ->
                    get_reading(urn:hvac:temp_reading,
                              urn:hvac:hum_reading) 
                    </cmd> 
                    <cmd> 
                    setting_at(urn:hvac:season_setting,"Fall") | 
                    reading(urn:hvac:hum_reading,A,
                         urn:hvac:hum_setting)->
                    set_trigger(urn:hvac:v_trigger);
                    reading(urn:hvac:hum_reading,B,
                         urn:hvac:hum_setting)->
                    reset_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:temp_reading,B,
                         urn:hvac:temp_setting)->
                    set_trigger(urn:hvac:h_trigger); 
                    </cmd> 
               </step> 
               <step id='S2'> 
                    <cmd>
                    step_OK("S0") | always()->
                    get_reading(urn:hvac:temp_reading,
                         urn:hvac:hum_reading)
                    </cmd> 
                    <cmd>
                    setting_at(urn:hvac:season_setting,"Winter") | 
                    reading(urn:hvac:hum_reading,A,
                         urn:hvac:hum_setting)->
                    set_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:hum_reading,B,
                         urn:hvac:hum_setting)->
                    reset_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:temp_reading,B,
                         urn:hvac:temp_setting)->
                    set_trigger(urn:hvac:h_trigger); 
                    </cmd> 
               </step> 
               <step id='S3'> 
                    <cmd>step_OK("S0") | always()->
                    get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)
                    </cmd> 
                    <cmd> 
                    setting_at(urn:hvac:season_setting,"Spring") | 
                    reading(urn:hvac:hum_reading,A,
                         urn:hvac:hum_setting)->
                    set_trigger(urn:hvac:v_trigger);
                    reading(urn:hvac:hum_reading,B,
                         urn:hvac:hum_setting)->
                    reset_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:temp_reading,A,
                         urn:hvac:temp_setting)->
                    set_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:temp_reading,B,
                         urn:hvac:temp_setting)->
                    reset_trigger(urn:hvac:v_trigger) 
                    </cmd> 
               </step> 
               <step id='S4'> 
                    <cmd>
                    step_OK("S0") | always()->
                    get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)
                    </cmd> 
                    >cmd> 
                    setting_at(urn:hvac:season_setting,"Summer") | 
                    reading(urn:hvac:hum_reading,A,
                         urn:hvac:hum_setting)->
                    set_trigger(urn:hvac:v_trigger);
                    reading(urn:hvac:hum_reading,B,
                         urn:hvac:hum_setting)->
                    reset_trigger(urn:hvac:v_trigger); 
                    reading(urn:hvac:temp_reading,A,
                         urn:hvac:temp_setting)->
                    set_trigger(urn:hvac:ac_trigger); 
                       reading(urn:hvac:temp_reading,B,
                         urn:hvac:temp_setting)->
                    reset_trigger(urn:hvac:v_trigger) 
                    </cmd> 
               </step> 
          </steps> 
          <result>urn:hvac:comfort_setting</result> 
     </taskbody> 
</task>

其中:

  • always() = 总是返回一个值 True。
  • get_setting(resourceURI, resourceURI, resourceURI) = 从第一个资源(参考结构)得到第二个和第三个资源的设置。
  • setting_set(resourceURI, resourceURI, resourceURI) = 如果根据第一个资源有第二个和第三个资源的设置,那么返回 True。
  • step_OK(step_ID) = 如果一个步骤完成正确,则返回 True。
  • get_reading(resourceURI, resourceURI) = 得到两个资源(都是传感器)的读数。
  • reading(resourceURI, comparator, resourceURI) = 使用比较操作(B = 低于,A = 高于),将第一个资源的读数与第二个资源(参考值)进行比较。
  • set_trigger(resourceURI) = 启用 resourceURI(继电器开关行为)。
  • reset_trigger(resourceURI) = 禁用 resourceURI(继电器开关行为)。

该任务使用 HVAC 领域中的以下资源,跟 prereq 元素中列出的一样:

  • 一个设置库,它包含当前时间(tod_setting)、当前季节(season_setting)、温度设置点(temp_setting)和湿度设置点(hum_setting)。
  • 一个感知到的温度读数(temp_reading)。
  • 一个感知到的湿度读数(hum_reading)。

该设置库是自动调温器中的存储设施,其值在外部设置,可以采用手动方式,也可以通过一个网络源、由该任务之前的另一个任务设置。

该任务通过以下五个步骤,不断地调整上下文元素中的每个值,并得到结果 —HVAC 领域的适当设置:

  1. 无条件地从设置库获得当前时间和季节设置(使用 setting_set 函数来查询设置和设置点的可用性,使用 set_setting 获得设置)。
  2. 无条件地获得最新温度和湿度传感器读数,在秋季,基于以下条件激活外部通风或供热:
    • 如果湿度不在设置点内,那么打开外部通风。
    • 如果温度读数低于设置点,那么打开供热。
  3. 无条件地获得最新温度和湿度传感器读数,在冬季,基于以下条件激活外部通风或供热:
    • 如果湿度不在设置点内,那么打开外部通风。
    • 如果温度读数低于设置点,那么打开供热。
  4. 无条件地获得最新温度和湿度传感器读数,在春季,基于以下条件激活或取消激活外部通风:
    • 如果湿度高于设置点,那么打开外部通风。
    • 如果湿度低于设置点,那么关闭外部通风。
    • 如果温度读数高于设置点,那么打开外部通风。
    • 如果温度读数低于设置点,那么关闭外部通风。
  5. 无条件地获得最新温度和湿度传感器读数,在夏季,基于以下条件激活外部通风或空调:
    • 如果湿度不在设置点内,那么打开外部通风。
    • 如果温度读数高于设置点,那么打开空调。
    • 如果温度读数低于设置点,那么打开外部通风。

最后四步设计为并行运行或者作为多线程的命令序列。

编译智能自动调温器配方构造

像下面这样调用编译器:

cd <source_file_path>
php RCC.php Thermo.xml

Thermo.xml 是包含内容的源文件的名称。智能自动调温器信息包最终的 RecipeJAUS 步骤信息包支持文件在 清单 7 中。使用该代码来创建 RecipeJAUS 信息包序列。

RecipeJAUS 信息包序列由 6 个部分组织成,每个部分前面都是一个字节计数器。这 6 个部分是:

  • 清单 7 中 __URI__ 部分的内容被转换成 清单 6 中每个 ResourceCatalog 表的 JAUSEncoding。
  • 清单 7 中 __CTX__ 部分中的设置被转换成 0x00 (iterate) 或 0x01 (oneshot)。
  • 清单 7 中 __RES__ 部分的内容被转换成 清单 6 中每个 ResourceCatalog 表的 JAUSEncoding。
  • 清单 7 中 __RSC__ 部分的内容被组织为一个单字节的项,然后针对每个资源的资源 URN,被转换成 清单 6 中每个 ResourceCatalog 表的 JAUSEncoding。
  • 清单 7 中 __THR__ 部分的内容被组织成一系列对应于步骤 ID 的两字节无符号十六进制数,其中 0xFF 表示进入下一多线程级别。
  • 使用 清单 6中的 ActionCatalog 表,__CMD__ 部分的内容被转换成相应的 RecipeJAUS 代码。

RecipeJAUS 运行时环境基本上就是一个线程管理器加上一个用于加载和执行 RecipeJAUS 信息包序列的 RecipeJAUS 代码解释器。

清单 7. 智能自动调温器 RecipeJAUS 信息包
__URI__ 
urn:hvac:thermo 
__CTX__ 
iterate 
__RES__ 
urn:hvac:comfort_setting 
__RSC__ 
urn:hvac:settings_lib_1 
urn:hvac:season_setting_1 
urn:hvac:tod_setting_1 
urn:hvac:temp_setting_1 
urn:hvac:hum_setting_1 
urn:hvac:temp_reading_1 
urn:hvac:hum_reading_1 
urn:hvac:h_trigger_1 
urn:hvac:v_trigger_1 
urn:hvac:ac_trigger_1 
__CMD__ 
always()_always()_get_setting(urn:hvac:settings_lib, 
urn:hvac:tod_setting, urn:hvac:season_setting)_S0:0 
setting_set(urn:hvac:settings_lib,urn:hvac:season_setting,urn:hvac:tod_setting)
_always()
_get_setting(urn:hvac:settings_lib,urn:hvac:temp_setting,urn:hvac:hum_setting)_S0:1 
step_OK("S0")_always()_get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)_S1:0 
setting_at(urn:hvac:season_setting,"Fall")
_reading(urn:hvac:hum_reading,A,urn:hvac:hum_setting)
_set_trigger(urn:hvac:v_trigger)_S1:1 
setting_at(urn:hvac:season_setting,"Fall")
_reading(urn:hvac:hum_reading,B,urn:hvac:hum_setting)
_reset_trigger(urn:hvac:v_trigger)_S1:2 
setting_at(urn:hvac:season_setting,"Fall")
_reading(urn:hvac:temp_reading,B,urn:hvac:temp_setting)
_set_trigger(urn:hvac:h_trigger)_S1:3 
setting_at(urn:hvac:season_setting,"Fall")
_reading(urn:hvac:hum_reading,A,urn:hvac:temp_setting)
_reset_trigger(urn:hvac:h_trigger)_S1:4 
step_OK("S0")_always()_get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)_S2:0 
setting_at(urn:hvac:season_setting,"Winter")
_reading(urn:hvac:hum_reading,A,urn:hvac:hum_setting)
_set_trigger(urn:hvac:v_trigger)_S2:1 
setting_at(urn:hvac:season_setting,"Winter")
_reading(urn:hvac:hum_reading,B,urn:hvac:hum_setting)
_reset_trigger(urn:hvac:v_trigger)_S2:2 
setting_at(urn:hvac:season_setting,"Winter")
_reading(urn:hvac:temp_reading,B,urn:hvac:temp_setting)
_set_trigger(urn:hvac:h_trigger)_S2:3 
setting_at(urn:hvac:season_setting,"Winter")
_reading(urn:hvac:hum_reading,A,urn:hvac:temp_setting)
_reset_trigger(urn:hvac:h_trigger)_S2:4 
step_OK("S0")_always()_get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)_S3:0 
setting_at(urn:hvac:season_setting,"Spring")
_reading(urn:hvac:hum_reading,A,urn:hvac:hum_setting)
_set_trigger(urn:hvac:v_trigger)_S3:1 
setting_at(urn:hvac:season_setting,"Spring")
_reading(urn:hvac:hum_reading,B,urn:hvac:hum_setting)
_reset_trigger(urn:hvac:v_trigger)_S3:2 
setting_at(urn:hvac:season_setting,"Spring")
_reading(urn:hvac:temp_reading,A,urn:hvac:temp_setting)
_set_trigger(urn:hvac:v_trigger)_S3:3 
setting_at(urn:hvac:season_setting,"Spring")
_reading(urn:hvac:hum_reading,B,urn:hvac:temp_setting)
_reset_trigger(urn:hvac:v_trigger)_S3:4 
step_OK("S0")_always()_get_reading(urn:hvac:temp_reading,urn:hvac:hum_reading)_S4:0 
setting_at(urn:hvac:season_setting,"Summer")
_reading(urn:hvac:hum_reading,A,urn:hvac:hum_setting)
_set_trigger(urn:hvac:v_trigger)_S4:1 
setting_at(urn:hvac:season_setting,"Summer")
_reading(urn:hvac:hum_reading,B,urn:hvac:hum_setting)
_reset_trigger(urn:hvac:v_trigger)_S4:2 
setting_at(urn:hvac:season_setting,"Summer")
_reading(urn:hvac:temp_reading,A,urn:hvac:temp_setting)
_set_trigger(urn:hvac:ac_trigger)_S4:3 
setting_at(urn:hvac:season_setting,"Summer")
_reading(urn:hvac:hum_reading,B,urn:hvac:temp_setting)
_reset_trigger(urn:hvac:ac_trigger)_S4 
__THR__ 
S0 
S1,S2,S3,S4 
__SRD__ 
S0|urn:hvac:settings_lib,urn:hvac:season_setting,urn:hvac:tod_setting,
urn:hvac:temp_setting,urn:hvac:hum_setting 
S1|urn:hvac:season_setting,urn:hvac:temp_setting,urn:hvac:hum_setting,
urn:hvac:temp_reading,urn:hvac:hum_reading,urn:hvac:h_trigger,urn:hvac:v_trigger 
S2|urn:hvac:season_setting,urn:hvac:temp_setting,urn:hvac:hum_setting,
urn:hvac:temp_reading,urn:hvac:hum_reading,urn:hvac:h_trigger,urn:hvac:v_trigger 
S3|urn:hvac:season_setting,urn:hvac:temp_setting,urn:hvac:hum_setting,
urn:hvac:temp_reading,urn:hvac:hum_reading,urn:hvac:v_trigger 
S4|urn:hvac:season_setting,urn:hvac:temp_setting,urn:hvac:hum_setting,
urn:hvac:temp_reading,urn:hvac:hum_reading,urn:hvac:v_trigger,urn:hvac:ac_trigger

该文件具有以下部分:

  • __URI__该配方的 URN。
  • __CTX__该配方的执行上下文。
  • __RES__该配方的结果 URN,该配方的输出内容存储在其中。
  • __RSC__该配方中使用的资源 URN 的列表。
  • __CMD__该配方的所有命令的 precondition-condition-action 三维组列表(注意,最后一项指出与特定三维组相关的步骤)。
  • __THR__该配方中步骤的多线程束。
  • __SRD__步骤资源依赖项。

编译器通过 RCS 数据库中的 ResourceCatalog 表,将 __RSC__ 中的资源转换成目标 RecipeJAUS 平台的注册地址。它也通过 ActionCategory__CMD__ 中的函数转换成它们的 RecipeJAUS 等价物,并将函数参数分解为已转换 __RSC__ 列表中的位置。

__THR__ 部分展示了可多线程执行的步骤序列。所以,在 清单 6 中,执行完步骤 S0 之后执行步骤 S1 到 S4,它们是同步执行的。

原型编译器的 PHP 源代码可从 下载 得到。

结束语

随着功能性、可靠性和安全性方面的要求不断增长,嵌入式系统开发人员需要一种方法来捕捉高端和低端系统细节,同时提供系统的总体视图。本文概要介绍了一种方法,使用 PHP(一个支持编译的数据库,用于为复杂的嵌入式系统设计和开发总体解决方案)和 RecipeJAUS(一种开放平台,支持此类系统的部署)将 DITA 任务主题模拟为配方构造。此外,可以将该方法扩展到大型环境(企业)中的系统。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Open source
ArticleID=481829
ArticleTitle=为嵌入式设备构建可执行的处理模型
publish-date=04122010