SysML 块定义图(Block Definition Diagram)
最常见的一种SysML图就是模块定义图(Block Definition Diagram,BDD)。可以在BDD中显示不同类型的模型元素和关系,以说明系统结构的信息。
1、目的
在BDD中显示的模型元素——模块、执行者、值类型、约束模块、流说明、接口——都是其他模型元素的类型,它们会出现在其他8种SysML图中。把出现在BDD中的元素叫做定义元素。在实际情况下,定义元素形成了系统模型中其他内容的基础。
定义元素非常重要,它们之间的结构关系——关联、泛化和依赖——可能更重要。 在BDD上也会显示这些关系。使用这些关系,通常会创建说明系统分解和类型分类的BDD。
2、何时创建BDD
经常——你应该经常创建BDD。
这看起来是一种滑头的答案,但很准确。BDD并不会与系统生命周期的特定阶段或者设计等级绑定。你和团队会在执行所有这些系统工程活动的时候创建它(并参考它):利益相关者需求分析、需求定义、架构设计、性能分析、测试案例开发、集成。你通常会和其他SysML图一起创建BDD,从而为所关注系统的一个方面提供完整的视图。
简而言之,你应该——也会经常创建BDD。
3、BDD外框
模块定义图的类型缩写是BDD。图的外框代表的模型元素类型可以是以下这些:
- 1)包
- 2)模型
- 3)模型库
- 4)视图
- 5)模块
- 6)约束模块
图代表的模型元素会作为图中显示的其他元素的命名空间。命名空间只是一种模型元素,可以在其中包含其他模型元素。也就是说,它可以包含其他模型层次结构中的元素。因此,命名空间是在一种只在系统模型中才有意义的概念,它在系统实例中 没有任何意义。
很多类型的SysML元素都可以作为命名空间。然而,包是针对出现在BDD中各种定义元素常见的命名空间。因此,BDD中头部命名的元素通常是在模型层级结构某处创建的包。
图3.1中的BDD的名称是“ DellSat-77卫星结构和属性”。图的头部也告诉我们这个图表示系统模型中的Structure包。因此,Structure包就是图中显示的元素的命名空间。
4、BDD中的主要建模元素
4.1、模块(Block)
模块是SysML结构中的基本单元。可以使用模块为系统中或者系统外部环境中任意一种感兴趣的实体类型创建模型。
注意定义和实例(SysML指的是“使用”)之间的区别。这种区别涉及系统设计基本的概念,并且在SysML中经常出现。某些类型的模型元素(例如:模块、值类型、约束模块)表示对类型的定义;其他类型的模型元素(例如:组成部分属性、值属性、约束属性)代表那些类型的实例。打个比方,房屋的蓝图是对房屋类型的定义;在各个地点根据图纸盖起来的房屋就是那个类型的实例。
知道上面的内容之后,我可以重新说明一下:模块代表的是实体的类型,而不是一个实例的类型。例如,可以在系统模型中创建名为DesktopWorkstation的模块。 那个模块代表一种类型,其中定义了一系列属性——像显示器、键盘、鼠标、CPU、 制造厂商、磁盘空间、价格等——它们对于所有实例都通用。而IT部门为每个办公 室和工位购头的每台桌面工作站都是那个DesktopWorkstation模块的不同实例。
你很容易就能够指出元素的定义和在系统模型中对元素的使用之间的区别。元素的定义只有一个名称(例如,DesktopWorkstation),而对元素的使用则有名称和类型,二者之间用冒号分隔(例如,SDXI205LJD : DesktopWorkstation)。
模块的注释是带有元类型《block》的矩形,后面是名称分隔框中的名称(如图3.2所示)。需要显示模块的名称分隔框。通常会显示另外的可选分隔框,其中会显示模块的特性。
特性有两种:结构特性(也叫做属性)和行为特性。接下来的两节深人讨论这两类特性。
以下是可以选择显示的分隔框:
- 1)组成部分
- 2)参考
- 3)值
- 4)约朿
- 5)操作
- 6)接收
- 7)标准端口(在SysML v1.2及更早版本中)
- 8)流端口(在SysML v1.2及更早版本中)
- 9)完整端口(在SysML v1.3中)
- 10)代理端口(在SysML v1.3中)
- 11)流属性(在SysML v1.3中)
- 12)结构
结构分隔框只是分隔框,其中不会列举特性。它只是一种图形分隔框,显示模块的内部结构。你可以在那个分隔框中显示所有你能够在内部模块图中显示的相同标记。建模者一般不会显示这些分隔框。
请注意,尽管我们也可以在分隔框中显示模块的端口,但更常见的是把端口显示为模块边框上的小方块(如图3.2所示)。
4.1.1、结构特性
模块可以拥有五种结构特性(也叫做属性):
- 1)组成部分属性
- 2)引用属性
- 3)值属性
- 4)约束属性
- 5)端口
4.1.1.1、组成部分属性
组成部分属性会列在模块的组成部分分隔框中(如图3.3所示)。组成部分属性代表模块内部的结构。换句话说,模块是由组成部分属性构成的。这种关系是一种所属关系。
然而,SysML并没有定义所属关系,这个概念在不同的领域有不同的意义。在硬件领域,所属关系通常指的是物理组成关系。例如,图3.3表示通信和数据处理子系统模块的可用实例是一些必不可少的物理组成部分:飞行电脑、调制器、解调 器、发射机、接收机和天线。而在软件领域,所属关系通常指的是一个对象负责创建和销毁另一个对象。当为一个复杂对象分配内存的时候,也会为它的每个组成部 分分配;同样,当为一个复杂对象释放内存的时候,也会为每个组成部分释放内存。
但是SysML明确声明,所属关系意味着一个组成部分属性一次只能属于一个复杂结构。然而,一个组成部分属性可以从复杂结构的一个实例上移除,然后添加到另 一个复杂结构的实例上。例如,我一次只能把特定的天线安装在一个_卫星上,而无法安装在两个甚至多个上面。但是,那个天线可以从一个卫星上拆除下来,然后在某个 时候重新安装在另一个卫星上。
当你在模块的组成部分分隔框中列举一个组成部分属性的时候,会显示下面这样的字符串:
<part name> : <type> [<multiplicity>]
组成部分名称由建模者定义,类型一般是你在系统模型某处创建的模块的名称。 多重性是一种约束,限制复杂对象中组成部分属性的实例数量,以单个整数或者一系列 整数表示。
例如,图3.3表示通信和数据处理子系统模块的有效实例必须只包含一个飞行计算机模块的实例——那个实例会起到主要计算机的角色。此外,它必须再包含一到两个飞行计算机的实例——起到备份计算机的角色。
如果你想要一个组成部分属性代表任意数量的实例,那么就可以把多重注设置为也就是 “0或者多个”。或者,可以把多重性设置为*,那是0..*的缩写。
如果对于组成部分属性没有设置任何多重性,那么默认就是1 (等同于1..1 )。注意,1几乎总是SysML中的默认多重性设置。
当一个组成部分属性的多重性的上限大于1的时候(例如:1..2, 0..10, *),我们就称那个组成部分属性为(实例的)集合。关键的问题是组成部分属性和实例意义并不相同;如果复杂结构的多重性允许,那么其中单独的组成部分属性可能代表多个 实例。
4.1.1.2、引用属性
引用属性会列举在模块的引用分隔框中(如图3.4所示)。引用属性代表模块外部的一种结构。
和组成部分属性不同,引用属性并不表示所属关系。引用属性可以大概描述为 “需要”的关系;带有引用属性的模块因为某种目的需要那个外部结构,或者是为了提供一种服务,或者为了交换事件、能量或者数据。这意味在它们之间必须存在某种联系。
注意,在模块之中出现的引用属性,本身不会说明其目的。如果你需要说明那个目的,可以在内部模块图中这么做。
当在模块的引用分隔框中列举一个引用属性的时候,会显示这种格式的字符串:
<reference name>: <type> [<multiplicity>]
引用名称由建模者定义。类型必须是你已在系统模型某处创建的模块或者执行者的名称。多重性是引用属性能够表示的实例的数量限制。
例如,图3.4显示电子电力子系统模块拥有名为cdhs的引用属性。这个模块表示电子电力子系统的实例只需要一个通信和数据处理子系统(以满足它的设计目的)。 需要再次说明的是,这个视图本身并没有表达那样做的目的是什么,它只是表示二者之间必须存在某种类型的关系。
和组成部分属性一样,引用属性的默认多重性是1 (如果没有显示任何多重性设置)。同样和组成部分属性类似,当多重性的上限大于1时,引用属性代表的是一个集合。
4.1.1.3、值属性
值属性列举在模块的值分隔框中(如图3.5所示)。值属性可以代表一个数字(某种类型)、一个布尔值或者一个字符串。然而,更常见的情况下,可以为值属性赋予一个数字。当和约束属性结合的时候,值属性会特别有用,那样可以构建系统的数学模型。
当模块的值分隔框中列举一个值属性的时候,它会是下面这种格式的字符串:
<value name>: <type> [<multiplicity>]= <default value>
值的名称由建模者定义。类型必须是你已在系统模型中某处创建的值类型的名称。多重性是值属性能够持有的值的数量的限制。默认值是可选信息,代表它所属模块的实例第一次创建的时候赋予值属性的值。
图3.5展示DellSat-77卫星模块拥有多个值属性。eventTimes值属性可以持有任意多个Timestamp值(多重性表示为0..*)。Timestamp是一种值类型,它存在于模型层级关系的某处。
和组成部分属性及引用属性类似,值属性的默认多重性也是1(如果没有设置多重性)。类似地,当它的多重性的上限大于1时,值属性就代表一个集合。
某些值属性会持有赋予的值,其他的会持有从系统模型中其他值属性继承(计算)得到的,为了表示一个值属性是继承得来的,需要在它的名称之前放置一个反斜杠(/)。 例如,图3.5显示DellSat-77卫星模块拥有两个继承得到的值属性:mass和period。模型的这种视图不会标识用来计算那些值的等式,也不会显示哪些值属性为那些等式提供了输人,需要使用约束表达式来指定那些数学关系。
4.1.1.4、约束属性
约束属性会列举在模块的约束分隔框中(如图3.6所示)。约束属性一般代表一种数学关系(一个等式或者不等式),它会使用一系列值属性。在这里所需要的模型精确度要比大多数建模项目所需要的都高。然而,对于构建系统的数学模型来说,约束属性是一种核心组成部分,你会在参数图中显示它。
当在模块的约束分隔框中列举约束属性的时候,它会显示为这种格式的字符串:
<constraint name>: <type>
约束名称由建模者定义。类型必须是你已在系统模型某处创建的约束模块的名称。
约束模块只是一种特殊的模块——创建它是为了封装可重用的约束表达式。常见的情况下,约束表达式是一个等式或者不等式。例如,图3.7显示了一个名为Sufficient Memory的约束模块,其中封装了约束表达式:
memoryCapacity >= dataPerOrbit * 3
这个约束模块是作为飞行计算机模块中约束属性sm的类型而存在的(如图3.6所示)。这表示两个值属性中(MemoryCapacity和dataPerOrbit)持有的两个值必须始终满足那个数学关系(在名义上操作的系统中)。
注意,这里并没有要求你使用约束模块来影响值属性上的数学关系。直接在模块的约束分隔框中指定约束表达式也没有问题(如图3.8所示)。当只有一个模块需要那个约束表达式的时候,你就会这么做(即,不需要在多处重用)。然而,我还是建议你好最好总是把等式和不等式封装到约束模块中;如果需求增长,这可以重用。
需要记住这些关键点:
- 1)模块可以拥有约束属性(以约束值属性)。
- 2)约束属性的类型是约束模块,其中一般会封装数学关系。
4.1.1.5、端口
端口是代表结构边缘不同交互点的一种属性,通过那些点外部实体可以和那个结构交互或者是提供服务,或者是请求服务,或者是交换事件、能量和数据。
当你为模块添加端口的时候,其实就是把一种结构针对它的环境建模为一个黑盒,结构的内部实现会对客户端隐藏。那些客户端只知道结构的接口(它所提供和请求的服务,以及能够流入、流出的事件、能量和数据的类型)。需要特别声明的是,接口会将模块的客户端与所有特定的内部实现解耦。
使用一系列端口来封装模块,让你可以在稍后重新设计模块的内部实现,而不会影响系统其他部分的设计。当客户的需求在生命周期后期发生变更的时候,这种实践会减少系统修改所需要花费的时间,而时间上的节省会转换成为成本上的节省。
端口可以代表你所需要建模的任意类型的交互点。例如,它可以代表硬件对象边界上的物理对象(例如:龙头、HDMI接口、燃油喷嘴,或者仪表)。它也可以代表软件对象边界上的交互点(例如:TCP/IP插槽、消息队列、共享内存片段、图形化用户界面、数据文件)。它还可以代表两家商业组织之间的交互点(例如: 支付订单、传件员、网站、邮箱)。SysML不会对能够代表什么样的端口做任何限制。
SysML v1.2 (以及更早版本)定义了两种端口——标准端口和流端口——你可以把它们添加到模块上,以指定不同类型的接口。标准端口可以指定一个交互点,关注模块提供或请求的服务;流端口可以指定一个交互点,关注能够流入、流出模块的事件、能量或者数据的类型。
主意 SysML v1.3不再支持标准端口和流端口,作为替代,它定义了两种新的端 口:完整端口和代理端口。我在这一 章中会关注标准端口和流端口,因为在我撰写这本书的时候,它们仍是系统模型中端口的主要类型。此外,当前版本的OCSMP认证考试也覆盖了标准 端口和流端口的内容。另外,某些建模工具没有跟上SysML的改变,暂时还不支持完整端口和代理端口。
标准端口是模块在边界交互点上提供或者请求的服务(行为)的模型。常见的情况下,你会把标准端口表示为模块边界上的小方块(如图3.9所示)。你会注意到,在标准端口分隔框中把标准端口以字符串形式列举也是可以的,但那种标识法并不常见。
标准端口可以拥有建模者定义的名称(例如:sp_cdhs、sp_eps),以字符串的形式显示在标准端口旁边(可能在模块边界之内,也可能在之外)。标准端口可以拥有一种或多种类型;类型是你赋予它的接口(例如:Power Generation、Status Reporting) 。
和模块类似,接口也是对元素的一种定义——它定义了一系列操作和接收信息,也就是客户端和提供方需要遵循的行为契约。你可以在BDD中把接口显示为一个矩形框,名称前面带有关键字<interface> ;你可以在第二个和第三个分隔框中显示它的操作和接收信息。图3.10用这种标识法显示了 Power Generation和状态报告接口。
当你为标准端口赋予接口的时候,可能会将其赋予提供接口,也可能赋请求接口。提供接口使用圆形标识——和标准端口连接,像棒棒糖一样的符号(如图3.9所 示)。提供接口的模块必须实现接口所有操作和接收信息。例如,图3.9表示通信和数据处理子系统模块提供了状态报告接口,而这意味着它会实现那个接口中的两个操作和两种接收信息。
请求接口使用槽状的标识来表示——和标准端口连接,带有半圆的直线(如 图3.9所示)。请求接口的模块可能会在系统操作的某些时候调用一个或多个——不一定是所有——操作或者接收信息。例如,图3.9表示电子电力子系统模块需要请求状态报告接口,这意味着它可能会调用那个接口中四种操作和接收信息中的任意多个 (或者所有)。
使用标准端口建模这种方式,可以对客户端和提供程序提供解耦,让你可以为抽象而不是具体实现来设计。这种可扩展性让你可以在任何时候添加新的接口提供程序,而不会影响那些接口的现有客户端。
流端口会为能够在边界交互点流入、流出模块的事件、能量或者数据建模。和标准端口一样,你通常会把流端口显示为连接在模块边缘上的小方块(如图3.11所示)。 然而,和标准端口不同的是,流端口在小方块中会显示一个符号。把流端口显示为分隔框中的字符串也没有问题——叫做“流端口”的分隔框——但那并不是一种常见的标识法。
流端可以拥有建模者定义的名称(例如:dataOut、dataln);它也可以拥有类型(例如:Housekeeping Data)。名称和类型都显示为流端口附近的字符串,以名称:类型的格式用冒号分隔。你为流端口制定的类型以及显示在方块中的符号取决于你建模的流端口的种类。SysML提供两种流端口:非原子流端口和原子流端口。
图3.11显示了非原子流端口的例子。当你需要为通过端口流入或流出的多种项目类型建模时,就会为模块添加非原子的流端口(符号是 <>)。非原子流端口的类型必须是你在系统模型某处创建的流类别的名称。
和模块一样,流类别也是对元素的一种定义——它会定义一系列能够流入、流出非原子流端口的流属性。可以在BDD上把流类别显示为一个矩形框,名称前面带有元类型说明<<flowSpecification>>。 可以在名为“流属性”( flowProperties )的分隔框中显示它的流属性 。图 3.12 显示了使用这种标识法显示的 Housekeeping Data 流类别 。
流属性代表的是能够通过流端口流r入、流出模块的特定项目。每个流属性都有其自身的方向、名称、类型,以下面的格式显示为一个字符串:
<direction> <name>: <type>
方向可以是in、out或inout。名称由建模者定义。类型必须是你已经在模型层级关系中某处创建的值类型、模块或者记号的名称。
图3.11显示飞行计算机模块拥有名为dataIn的非原子流端口,它的类型是 Housekeeping Data流类别。这个模型表示温度和电压值在系统操作的某个时间点可以传人到飞行计算机的实例中。
图3.11还显示电子电力子系统模块拥有名为dataOut的非原子流端口,它的类型也是Housekeeping Data流类别。然而,在这种情况下,类型Housekeeping Data前面有一个波浪线。这个符号表示dataOut流端口是共轭的。这意味着Housekeeping Data流类别中流属性的方向会针对那个流端口反转。
另一种那个流端口是原子流端口。图3.13显示了这种端口的例子。当你需要为能够通过那个端口流入或流出的单一类型项目建模时,就会添加原子流端口。小方块中的符号是一个箭头,表示流的方向。原子流端口的类型必须是你在模型层级结构某种定义的值类型、模块或者信号的名称。
图3.13显示Modulator模块和Transmitter模块拥有名为coupler的原子流端口, 类型是同样的值类型Radio Frequency cycle。这些端口只在流的方向上有所不同。这个模型表示射频信号可以通过耦合器——位于各自边界上的交互点——从发射机传递到调制器上。
4.4.2、行为特性
在上一部分讨论的所有特性都是结构特性。然而,在大多数建模项目中,仅指定模块的组成部分、引用、约束、值属性和端口是不够的。它们确实很重要,但只传递了设计一个方面的信息。同样重要的方面是模块能够执行的一系列行为。通过为模块添加行为特性,你可以传递设计的这方面信息。
SysML提供了两种类型的行为特性:操作(operation)和接收(reception)。 前面讨论接口的时候对其进行了简要的说明。然而,它们不仅限于接口使用。你还可以为模块添加操作和接收。把行为特性添加在模块上还是添加到(模块提供或者需要的)接口上取决于你选择的建模方法以及设计原则。 SysML不会指定任何一种,而且在两种情况下显示操作和接受信息的格式也是一 样的。
现在让我们详细看一下这两种行为特性。
4.4.2.1、操作
操作代表客户端调用模块的时候它所执行的行为。正式的说法是,操作是由调用事件触发的。
最常见的情况下,操作代表的是一种同步行为。这意味着调用者会一直等待行为结束,然后再继续自身的执行。然而,SysML不需要这样,你可以把任何行为表示为操作——即便调用者没有等待它完成。
你会在BDD中把操作表示为模块操作分隔框中的字符串(如图3.14所示)。那个字符串的格式会是下面这样:
<operation name> ( <parameter list> ) : <return type>
[<multiplicity>]
操作名称是由建模者定义的。参数列表是由逗号分隔,拥有零个或多个参数的列表。(每种参数的格式都会简短显示。)返回值(如果有)必须是你在系统模型中某处创建的值类型或者模块的名称。多重性会约束操作完成时能够给调用方返回类型的实例的数量。
参数列表中的参数代表操作的输人和输出。列表中的每个参数都会以下面的格式显示:
<direction> <parameter name>: <type> [<multiplicity>] =
<default value>
方向可以是in、out或inout。参数名称是由建模者定义的。类型必须是在你的模型某处存在的值类型或者模块的名称。多重性会约束参数能够代表的类型实例的数量。当操作被调用时,如果没有指定一个值作为参数,则赋予参数默认值。
图3.14显示电子电力子系统模块和通信及数据处理子系统模块都拥有多种操作——它们代表的是,如果在系统操作中被调用,这些模块的实例能够执行的行为。 操作的一个例子是processCommand。这个模型表示客户端可以调用通信和数据处理 子系统来执行这种操作。当调用的时候,客户端可以传递给操作一个或多个命令作为输人,当操作完成时,它会给调用方返回状态值。
尽量使用动词短语(像processCommand)为操作命名是很好的,毕竟操作代表的是行为。另外,不要让参数列表过多,只是向模块添加操作(不指定参数)通常就足够保证模型的准确性了。如果你的团队需要在系统模型中为操作指定参数,那么一定要准确判断选择哪些显示在特定的BDD上。就算只是显示了几个参数,操作的完整字符串也会在BDD上占用大量空间。
4.4.2.2、接收
接收代表一种行为,当客户端发送信号来触发的时候,模块就会执行这种行为。 正式的说法是,接收是由信号事件触发的。
接收和操作之间的关键区别在于,接收总是代表一种异步的行为。这意味着客户端发送信号——会触发接收操作来接收它——然后立刻继续自身的执行,它不会等待接收完成(甚至都不会等待它开始)。
另一个关键点是,信号本身也是一种模型元素。你可以使用信号来代表任意类型的事件、能量和数据,它们都是系统的一部分传递给另一部分的 ,一般的目的是要触发接收端的行为。和模块一样,信号可以有属性。大多数情况下,那些属性代表信号从客户端到目的地所传递的数据。当信号到达目的地并触发接收动作的时候,信号的属性就会成为接收动作的输入。
图3.15显示了名为AnalogTempDataSampIed的信号。这个信号有两种属性: temp (类型是摄氏度)和time (类型是TimeStamp)。当客户端在系统操作过程中生成这个信号的实例吋,就可以为这两个属性提供值。客户端可以把信号实例发送给接受它的目的地(例如,图3.16中所显示的通信和数据处理子系统模块)。
如果信号的目的地拥有和信号名称相同的接收,那么这种结构就是无效的。此外,接收必须拥有和信号的每个属性类型兼容的参数。通信和数据处理子系统模块符合这些标准。当操作系统中这个模块的一个实例接收了 AnalogTempDataSampled信号的实例,那么就会触发接收行为,信号的两个属性中持有的值就会成为那种行为的输入。
当你在模块的接收分隔框中显示接收的时候,会显示下面这种格式的字符串:
«signal » <reception name> ( <parameter list>)
关键字<signal>必须总是作为接收名称的前缀。正如之前提到的,接收名称必须与模型中触发它的信号名称匹配。只要有必要,你在参数列表中可以显示任意多个参数。列表中的每个参数都会以下面的这样格式显示:
<parameter name>: <type> [<multiplicity>]= <default value>
参数名称是由建模者定义的。类型必须是在你的模型某处存在的值类型或者模块的名称。多重性会约束参数可以代表的类型实例的数量。如果在信号相关属性中没有提供任何值,那么默认值就是赋予参数的值。
和操作不同,接收无法拥有返回值。接收是异步的,发送信号的客户端不会等待回复。因为同样的原因,接收的参数只能是输入而不可能是输出。
4.2、模块之间的关系
上文关注的是模块以及它能够拥有的各种各样的属性。模块是系统结构化模型的重要部分,而模块之间的关系至少要和它们同等重要。
在模块之间可以存在三种主要类型的关系:关联、泛化和依赖。
4.2.1、关联
在前文讨论引用属性和组成部分属性的部分,顺带提到了模块间的关联。 让我们重新回顾一下关键的知识点:引用属性代表模块外部的结构——模块因为某种目的需要与之连接的结构。组成部分属性代表的是模块内部的结构——换句话说,是构成模块的结构。
引用属性和组成部分属性分别和经常在模块之间创建并在BDD中显示的两类关联相关:引用关联和构成关联。关联只是表示系统中这些结构化关系的另一种标识法。 让我们详细看一下这两种关联。
4.2.1.1、引用关联
两个模块之间的引用关联意味着操作系统的那些模块的实例之间可以存在一种连接。那些实例可以跨越连接为了某种目的彼此访问。
BDD中引用关联的标识法是两个模块之间的实线。如果一端有箭头表示单向的访问,如果两端都没有箭头则表示双向访问。
图3.17上面的BDD显示的是电子电力子系统模块与飞行计算机模块之间的引用关联。关联可以有多种标签。你可以选择在线的中间位置显示关联的名称,还可以选择在线的任一端显示角色名称和多重性。关联名称是由建模者定义的字符串,描述能够存在于两个模块的实例之间的关联类型。例如,在图3.17中,引用关联的名称是电力电缆,这个名称描述的是一种关联类型,它可以存在于正确组装的卫星中电子电 力子系统和飞行计算机之间。
显示在引用关联末尾的角色名称与引用属性的名称相关——引用属性属于另一端的模块,它的类型是与之连接的下一个模块。例如,在图3.17上面的BDD中,角色名称eps代表属于飞行计算机模块的一个引用属性,它的类型是电子电力子系统模块。角色名称fc代表属于电子电力子系统模块的引用属性,它的类型是飞行计算机模块。图3.17中下面的BDD显示的是同一模型的等价视图,只是它使用的是引用分隔框标识法,而不是引用关联。
类似地,在引用关联的一端(接近于角色名称)显示的多重性与同一引用属性的多重性相关。这种相关性也反映在图3.17的两个BDD中。
有时候模块会拥有同样类型的多个引用属性(如图3.18中飞行计算机模块中的引用分隔框所示)。你也可以通过在同样的两个模块(如图3.18在飞行计算机模块和星球传感器模块之间所示)之间绘制多个引用关联来表达。每种引用关联都代表不同的引用属性。在同一幅图中显示两种标识法是多余的。我之所以在这里这么做,只是为了说明这些相关概念之间的联系。
选择使用引用分隔框标识法还是引用关联取决于你要在BDD上暴露多少信息。 例如,在图3.18中,引用关联标识法可以暴露星球传感器模块的值属性;与之相比, 引用分隔框标识法就会隐藏星球传感器模块的所有特性。
在做决定的时候,另一个需要考虑的因素是在内部块图(Internal Block Diagram,IBD)中是否需要为连接器指定类型。如果你想要这么做,那么就需要在两个模块之间创建引用关联,并为其命名。在 这种情况下,分隔框标识法无法满足你的需求。
4.2.1.2、组合关联
两个模块之间的组合关联表示结构上的分解。组合端的模块实例由一些组成部分端模块的实例组合而成。
BDD中组合关联的标识法是两个模块之间的实线,在组合端有实心的菱形。在组成部分端有箭头表示从组合端对组成部分的单向访问,如果没有箭头则表示双向访 问(也就是说,组成部分拥有对组合的引用)。
图3.19显示了从DellSat-77卫星模块到子系统模块的四种组合关联的例子。(组合端的实心菱形可以重合为一个,而且这也是常用的方式。)这幅BDD表示正确制造和组装的DellSat-77卫星会由一个电子电力子系统、一个高度及轨道控制子系统、一 个环境控制子系统以及一个通信和数据处理子系统构成,允许的实例数量由四个组合关联组成部分端的多重性表示。
在组合关联的组成部分端显示的角色名称与组成部分属性的名称相关,那个属性由组合端的模块所有,它的类型是组成部分端的模块。例如,在图3.19中,角色 aocs代表DellSat-77卫星模块拥有的组成部分属性,它的类型是高度和轨道控制子系 统模块。这种相关性以类似的方式反映在DellSat-77卫星模块的组成部分分隔框中。 在同一幅图中显示组成部分分隔框标识法和组合关联是多余的,我在此这样做是为了 加强这些概念之间的联系。
组合关联组成部分端的多重性并不是必须的。组合结构可以由任意多个组成部分的实例组成,然而很多系统都需要说明。
然而,在组合端的多重性是必需的。根据定义,一个组成部分一次只能属于一个组合体。因此,组合端的多重性的上限必须总是1 (如图3.19所示)。多重性的下限可以是0或者1。下限为0表示组成部分可以从组合结构中移除;下限为1表示不能移除(在有效的系统实例中,它必须总是依附于组合结构)。
前文提到过在SysML中,元素的默认多重性总是1。然而,这个规则有一种重要的例外情况,就在这里:组合关联的组合端默认多重性是0..1。(然而,在组成部分端,默认的多重性还是通常情况,1。)
有时候模块会拥有相同类型的多个组成部分属性(正如图3.20中通信和数据处理子系统组成部分分隔框中所示)。你也可以通过在相同点两个模块之间绘制多条组合关联表达这个意义(从通信和数据处理子系统模块到飞行计算机模块的关系如图3.20 所示)。每个组合关联都代表不同的组成部分属性。
4.2.2、泛化
泛化是你通常会在BDD中显示的另一种关系。这种关系表示两种元素之间的继承关系:一个更加一般化的元素,叫做超类型;以及一个更具体的元素,叫做子类型。 使用泛化在系统模型中创建分类树(类型层级关系)。
泛化的标识法是一条实线,在超类型的一端带有空心的三角箭头。这种关系从子类型向超类型读作“……是一种……”。例如,图3.21中的BDD显示从 Gyroscope模块向Sensor模块的泛化关系。这种关系表示—个陀螺仪是一种传感器。
如果同一幅BDD中显示一个超类型有多个子类型,那么建模者通常会在超类型端重叠空心的三角箭头,从而节省图上的空间(如图3.21所示)。纯粹主义者会告诉你,重叠那些箭头实际上表示对子类型的特殊分组,叫做泛化集。这是语言一种稍高 一级的特性,你稍后会发现这很有用。就现在而言,尽管把那些箭头重合了以提高图的可读性。
一个关键的问题是,泛化是可传递的。图3.21中的模型显示,星球地图仪是一 种星球传感器,而星球传感器又是一种传感器。因此,星球地图仪就是一种传感器。 模型中的类型层级结构可以达到任意深度。
泛化表示子类型会继承超类型的所有特性:结构化特性(属性)和行为特性(操作和接收)。除了它继承的特性之外,子类型还可能拥有超类型所不具备的其他特性。 因此,建模者通常会称子类型是超类型的一种特殊情况。
例如,星球传感器模块是传感器模块的一种特殊情况。它从传感器模块继承了四种值厲性以及三种操作,并且添加了第五种值属性:分辨率(resolution),而传感器模块并不具备这种属性。类似地,星球地图仪模块从星球传感器模块继承了五种值屈性以及三种操作,然后添加了两种新的、超类型并不具备的值属性 (hasAutonomousMode 和 maxNumStarsMapped)。
你创建泛化关系是为了在系统设计中定义抽象。一种超类型(像传感器模块)是对其子类型的一种抽象,它会抽取子类型中共通的那些特性。抽象让你可以在模型中的一个地方——在超类型中——定义通用特性,那些通用特性会沿着类型层级关系传播到所有子类型中。如果之后你需要修改那些通用特性,只需要回到模型中的一个位置并修改,这样模型中的所有子类型就会马上更新。
抽象是非常强大的设计原则,它表示的是可置换性,意味着在需要超类型的地方,也会接受子类型。例如,图3.22显示,飞行计算机模块拥有传感器类型名为 sensorArray的引用属性。这个模型表示飞行计算机可能需要访问一个或者多个对所有传感器通用的特性——结构化或者行为特性。因此,Sensor的五种子类型对于飞 行计算机都是可以接受的,因为所有类型都从超类型Sensor那里继承了那些通用的 特性。
这是针对抽象的设计的例子。这种实践在设计中创造了可扩展性。如果客户的需求在生命周期中发生变更,你需要向卫星设计中添加一种新类型的传感器,那么 你只需要在系统模型中定义Sensor模块新的子类型,这项添加操作对于所有引用了 Sensor模块的客户端(像飞行计算机)来说都是透明的。因为所有这些原因,把泛化构建到你的模型中,在生命周期进展,你需要修改系统设计时候,它会大大减少所需的时间——这种功能会直接转化为成本上的节省。
4.2.3、依赖
依赖是你可以在 BDD 中显示的第三类关系 。 顾名思义,它意味着:模型中的一种元素,客户端,依赖于模型中的另一种元素,提供者 。 更准确的说法是,依赖表示当提供者元素发生改变时,客户端元素可能也需要改变 。
最常见的情况是,你在两种模型元素之间创建依赖,只是为了确立二者之间的可跟踪性 。当你对设计做出变更的时候,依赖关系让你可以使用模型工具来检查下面的影响。对一种元素做出变更的时候,你可以查询模型工具,以生成模型中可能被变更影响的其他元素的列表;模型工具会遍历你在元素之间创建的一系列依赖,从而生成那个列表。
这是在你的模型中创建依赖的实际原因 。 然而,你很少有理由把它们显示在BDD 中。它们是模型结构的一部分,而不是模型代表的系统的一部分 。 你会花费大部分时间来创建 BDD ,以向利益相关者传达系统结构。
当依赖出现在 BDD 中的时候,标记是带有箭头的虚线,箭头方向从客户端指向提供者 。 例如,在图 3.23 中, Attitude and Orbit Control Subsystem 模块是客户端,而 Data Handling 接口是提供者 。 这个模型表示模块依赖于接口;如果接口发生改变,那么模块可能也需要随之变化。
注意 , SysML 定义了特定类型的依赖关系(例如 : 包导入、视点符合 以及几种需求关系)。尽管依赖很少在 BDD 中显示,但通常会在包图和需求图中显示特定类型的依赖。
4.3、执行者
执行者代表某人或者某件事物,它拥有系统的外部接口。执行者的名称表示人、 组织或者其他系统在与你的系统交互时所扮演的角色。
SysML为执行者定义了两种标识法:一种是火柴人,一种是在名称前面带有关键 字<<actor>>的矩形。图3.24显示了两种标识法的例子。对于任意类型的执行者—— 人或者系统——使用任意一种标记法都是合法的。然而,建模者通常会采用火柴人图形标识法来代表人,矩形标识法来代表系统,尽管语言并没有对此做出严格规定。
你偶尔会在BDD中显示执行者,来说明执行者之间或者执行者和模块之间关联的泛化关系(如图3.24所示)。但是,更常见的是在用例图中显示执行者,其中你表达的是每个执行者参与了哪种用例。
当执行者涉及泛化、引用关联以及组合关联这些关系的时候,所有关于那些关系的关键点也全部适用。但有两条约束:
- 1)你无法在执行者和模块之间定义泛化。
- 2)执行者不能拥有组成部分。也就是说,它无法出现在组合关联的组合端(我们总是认为执行者是一个“黑盒”)。
4.4、值类型
和模块一样,值类型也是一种定义元素,一般会定义一种数量类型。我之所以说“一般",是因为在SysML中有两种值类型——布尔型和字符串型——并不是数量。
你可以在模型中很多地方使用值类型。最常见的情况下,它会以值属性——模块的一种结构化特性——类型出现。但是那并不是值类型出现的唯一地方,在系统模型中它会经常出现。它还可以作为以下情况中的类型 出现:
- 1)模块或者执行者的原子流端口
- 2)流规格中的流属性
- 3)约束模块中的约束参数
- 4)连接器上的条目流和条目属性
- 5)操作的返回值
- 6)操作和接收的参数
- 7)活动中的对象节点、固定点和活动参数
你通常可以在系统模型中定义三种值类型——原始值类型、结构值类型和枚举值类型。原始值类型没有任何内部结构(它不拥有任何值属性)。它的标识法是名称前带有元类型<<valueType>>的矩形。
SysML定义了四种原始值类型:String、Boolean、Integer和Real。当然,你可以定义向己的原始值类型,作为这四种类型的特殊情况(子类型)。例如,图3.25显示了三种值类型(°、V和℃),它们都是Real型的子类型。
顾名思义,结构值类型拥有内部结构,一般是两种或多种值属性。和原始值类型一样,结构值类型的标识法也是名称前面带有<<valueType>>的矩形。SysML定义了一种结构值类型:Complex。它的结构包括两个值属性 realPart和imaginaryPart,二者都是Real类型。一种结构值类型可能会是另一种结构值类型中的值属性类型。这样,你可以创建值类型任意复杂的系统。
枚举值类型——通常叫做枚举——只定义一系列数值(有效的值)。如果一项操作的参数(或者在之前的项目列表中显示的某种其他类型元素)的类型是枚举, 那么它在任何时候所持有的值必须是枚举中的值。图3.25中的BDD显示了名为 CommandKind的枚举,它定义了两个值:Stored和Real-Time。例如,我可以使用这个枚举来定义名为buildCommand的操作中名为kind的输入参数的类型。当客户端(在运行的系统中)调用这个操作的时候,能够通过的有效值只有Stored和 Real-Time。
我之前提到过使用泛化可以建立一种值类型和另一种之间的关系。值类型层级结构可以有任意深度,而泛化一你可能已经回忆起来——是传递的。例如,图3.25 表示值类型VDC和VAC是Real (间接)的子类型。置换原则在此也适用,就像在模块之间的泛化情况中适用一样:在需要VDC和VAC的超类型(V和Real)的地方,这两个类型也是可以接受的。这些超类型都是抽象。针对抽象设计的原则——以及相关的可扩展性一也对创建值类型层级关系的这种实践适用。这是广泛且强大的建模实践。
4.5、约束模块
和模块一样,约束模块也是一种定义元素——它定义了一种布尔型的约束表达式 (结果要么是true要么是false的表达式)。最常见的情况下,你在约束模块中定义的约束表达式是一个等式或者不等式:用来约束模块的值属性的数学关系。你之所以这么做,可能是因为以下两种原因:
- 1)为了在操作系统中对可用的系统值指定断言
- 2)为了在生命周期的设计阶段执行工程分析
约束表达式中的变量叫做约束参数。一般它们代表的是数量,所以类型经常是值类型。例如,图3.26显示的是名为Transfer Orbit Size的约束模块,它定义了 一个约束表达式,其中包含三个约束参数:semiraajorAxis、initialOrbitRadius和 finalOrbitRadiuSo。这三个约束参数的类型都是值类型km。
约束参数会从它们绑定的值属性那里获得值——也就是被约束的值属性。在任意特定的时刻,那些值要么满足约束表达式,要么不满足;系统或者是正常操作,或者不正常。然而,要注意BDD本身无法传递约束参数和值属性是如何彼此绑定的。你要在参数图中说明这些信息。
BDD中约束模块的标识法是名称前面带有元类型<<constraint>>的矩形。约束表达式在约束分隔框中总是会显示在大括号({})之间。约束表达式中的约束参数会分别列在参数分隔框中。
有时候你会基于简单的约束模块构建更复杂的约束模块。这样可以从简单的等式和不等式创建出更加复杂的数学关系。更复杂的约束模块可以把它的组成部分显示为约束分隔框中的一系列约束属性。回顾一下前文的内容,约束属性有名称和类型,格式是:名称:类型。正如之前所提到的,类型必须是约束模块的名称。
例如,图3.26显示约束模块Hohmann Transfer由两个约束属性 ttof和tos 组成,这代表分别对约束模块 Transfer Time of Flight 和 Transfer Orbit Size的使用。这个模型表示Hohmann Transfer定义了一个约束表达式,它由两个更简单 的约束表达式组成,从而定义更复杂的数学关系。
但是,要注意这个BDD没有(无法)传达的信息:那两个更简单的约束表达式在哪里指定彼此连接,从而创建组合的约束表达式。参数图会传达这些额外的信息。
你可以使用组合关联替换约束分隔框标识法,来表示一个约束模块由其他更简单的模块组成(如图3.26所示)。注意,两个组合关联的组成部分端显示的角色名称与 Hohmann Transfer约束模块中的约束属性的名称相关。这是等价的标识法。当你需要暴露简单约束模块的细节时,就可以使用组合关联;当图的关注点不是那些细节时,你就可以使用约束分隔框标识法来隐藏它们。
4.6、注释
SysML有很多规则(它们之所以存在,是出于一个很实用的目的:让你的设计在大家传看的时候不会引起歧义)。然而,你有时候需要在图中以不受约束的方式在文本框中表达一些信息,使用注释就可以做到这一点。
实际上,注释是一种模型元素。它包含唯一的属性:一段文字,我们称之为注释体。你可以在注释体中传递任何需要的信息,你还可以选择在图中把注释附加给其他元素,以提供关于它的额外信息。你可以在所有九种SysML图中使用注释。
注释的标识法通常是一个笔记符号:一个矩形,右上角有折角。你可以使用虚线把注释附加给其他元素(如图3.27显示的BDD的底部所示)。如果需要,你可以把注释同时附加给多个模型元素,每个使用一条单独的虚线。
建模者有时会在图中放置单独的带有超链接的注释,让读者可以快速导航到模型的相关图(或者一个外部文档)。图3.27中BDD的左上角就显示了一个这样的例 子。但是,你要清楚,这是你使用的建模工具的一种功能,并非所有工具都支持。而 SysML没有任何对这项功能的叙述。
SysML定义了一些特殊类型的注释:原理、问题和图的描述。它们都显示为笔记符号,只是注释体前面会分別带有不同的元类型。图3.27在BDD的右上角显示了一 个图描述注释的例子。原理注释通常会和需求关系或分配一起使用。
5、小结
当你创建用来沟通系统设计的结构化信息时,BDD是主要的一类图。BDD 能够说明在系统内部以及系统外部环境中存在的结构类型。你还可以使用BDD来说明每种结构提供和需要的服务类型、每种结构必须遵循的约束类型,以及可以存在于运作的系统中的值类型。
元素之间的泛化关系让你可以定义类型的层级关系以及针对抽象设计。这是一种强大的设计技术——通过解耦服务的客户端和提供那些服务的特定提供者,从而在系统设计中实现可扩展性。当利益相关者的需求随着时间的推移而发展的时候,你可以修改现存的提供者,或者添加新的提供者,从而小化对其他系统设计的影响。