Ran Wei / SysML v2 系列 / 模块 2
EN
SysML v2 — Ran Wei

模块二:核心语言概念

命名空间、包、导入/可见性、特化、多重性和完整的特征模型——你在每个 SysML v2 模型中都会用到的六种核心语言机制。每一种都是 KerML L1 概念在 SysML L2 层的实现。

KerML → SysML:第一层如何变成第二层

模块一介绍了 KerML 作为语义基础(L1)以及建立在其上的 SysML v2 语言层(L2)。这在实践中意味着什么?每一个 SysML v2 关键字都是某个 KerML 概念的受约束、命名化的应用。下表展示了本模块涉及的每个 SysML 特性背后对应的 KerML 构件。

SysML v2 概念(L2)底层 KerML 构件(L1)SysML 额外添加了什么
packageNamespace(非 Type 的特化)约定它只持有定义;没有实例语义
part def / partClass(一种 Classifier)关键字 part 意味着复合所有权和默认多重性 [1..1]
attribute def / attributeDataType(一种 Classifier)关键字 attribute 意味着值类型语义
action def / actionBehavior(一种 Classifier)关键字 action 意味着随时间发生的语义
port def / portPortDefinition(特化自 StructureDefinition)/ 由 PortDefinition 类型化的有向复合 Feature关键字 port 意味着边界交互点;共轭(~)反转所有方向
specializesSubclassification(一种 Relationship)相同语义;SysML 将其限制在定义级别使用
subsetsSubsetting(一种 Relationship)相同语义;SysML 将其限制在用法级别的特征
redefinesRedefinition(一种 Relationship)相同语义;SysML 将其限制在用法级别的特征
importImport(一种 Namespace 成员关系)SysML 增加了 public/private 可见性和递归(::**)变体
多重性 [m..n]类型化元素上的 Multiplicity FeatureSysML 对 part/attribute/port 强制默认 [1..1];KerML 默认是 [0..*]
表 0 — SysML v2(L2)关键字与其 KerML(L1)来源的对照
NOTE

你不需要 KerML——你写的是 SysML v2。但了解每个关键字的 KerML 来源,能解释为什么规则是这样的。当某件事让你困惑时,答案总是在 KerML 这一层找到。

有了这张对照图,本模块接下来深入介绍每个 SysML v2 概念,并注明哪些行为继承自 KerML、哪些是 SysML 自己添加的约束。

1

命名空间 — 每个模型元素的骨架

KerML 来源:Namespace(根层,§8.3.2.4.5)

命名空间(Namespace)是一种可以包含其他元素的 Element,这些被包含的元素叫做它的成员。这是 KerML 中最基本的结构概念。文本表示法中每一对花括号 { } 都划定了一个命名空间边界。SysML v2 原封不动地继承了这个概念——这就是为什么 part defaction def,甚至各个特征体都充当命名空间。

💡 关键洞察

在 SysML v2 中,每一个 Type 也是一个 Namespace。每一个 part defaction defport def,甚至每一个特征体,都充当一个命名空间。在 SysML v1 中,只有 Package 和 Model 是命名空间——在 v2 中,这个概念无处不在。

名称解析与限定名

元素可以携带两个标识符:一个声明名(普通标识符或单引号括起来的非限制名,如 'Ordinary Cargo Bay')和一个用尖括号括起来的短名(如 <CB>)。限定名使用 :: 作为分隔符:

1Vehicles::Powertrain::Engine

当一个简单(非限定)名在当前作用域链中没有歧义时,可以直接使用。当存在冲突或元素不在作用域内时,需要使用限定名。

KerML 裸命名空间的文本写法

1namespace Outer {
2 namespace Inner {
3 // 这里的元素:Outer::Inner::elementName
4 }
5}
⚠️ 初学者常见错误

认为只有 Package 是命名空间——因为每个 Type 都是 Namespace,所以在定义内部使用导入是合法且有用的。这种技术可以避免把只与一个定义相关的名称污染外层包:

1part def Reindeer {
2 private import Datatypes::Color; // 仅限 Reindeer 的命名空间
3 attribute noseColor : Color;
4}
2

包 — 没有语义的分组

KerML 来源:特化 TypeNamespace

包(Package)是一种命名空间,但与 part defaction def 不同,它在 KerML 中特化 Type。这一事实解释了一切:Type 对建模领域的实例进行分类;Package 根本没有实例语义——它只用来组织模型的元素。

1package VehicleModel {
2 import ScalarValues::*;
3 import ISQ::*;
4
5 part def Vehicle {
6 attribute mass : ISQ::MassValue;
7 part engine : Engine;
8 }
9
10 part def Engine;
11
12 package Powertrain {
13 part def Transmission;
14 part def Drivetrain;
15 }
16}

库包

可复用的模型库使用 library package 形式。标准库使用 standard library package

1library package CommonParts {
2 part def Fastener;
3 part def Seal;
4}
⚠️ 初学者常见错误

一个包必须位于单个文件中——没有跨多个源文件重新打开或扩展包的机制(与 C# 的 partial class 不同)。请提前规划包的粒度。

3

导入与可见性

KerML 来源:Import(一种 Membership 关系);每个 Membership 上的 VisibilityKind

在 KerML 中,Import 是一种 Membership 关系,它将元素从一个命名空间引入到另一个命名空间中。SysML v2 原封不动地继承这一机制,并添加了两个约束:一个控制重新导出的可见性注解(publicprivate),以及一个递归形式(::**)。命名空间中的每个元素也有自己的 VisibilityKind——两者是正交的。

SysML v2 的导入系统因此控制三个正交维度:导入哪些元素、如何导入,以及是否重新导出。

可见性种类

每个成员关系都携带一个 VisibilityKind

关键字作用域
public(默认)在命名空间外部可见
private仅在所属命名空间内部可见
NOTE

默认可见性是 public——除非你明确声明,否则你声明的一切都是可见的。最佳实践:对实现细节声明 private

两种导入机制

1// MembershipImport — 单个命名元素
2import Sensors::Accelerometer;
3
4// NamespaceImport — 一次导入所有公开成员
5import Sensors::*;
6
7// 私有导入 — 名称不会重新导出
8private import InternalUtils::*;
9
10// 递归导入 — 包含所有嵌套命名空间
11import VehicleModel::**;
12
13// 别名 — 创建一个替代名称
14alias Accel for Sensors::Accelerometer;

公有 vs 私有导入 — 控制重新导出

导入关系本身的可见性控制着当命名空间被其他命名空间导入时,被导入的名称是否会重新导出:

1package HighLevel {
2 public import Sensors::*; // 名称会被重新导出
3 private import InternalUtils::*; // 名称留在这里
4}
⚠️ 常见陷阱

由于成员和导入可见性都默认为 public,通配符导入链可能会将名称传播到远超预期的范围。始终使用 private import,除非你明确想要重新导出。

4

特化链

KerML 来源:SubclassificationSubsettingRedefinition(Relationship 的子类型)

SysML v2 将 UML 的泛化、子集和重定义统一在一个概念下:特化(Specialisation)。这三种关系都存在于 KerML 层——SysML 只是通过关键字 specializessubsetsredefines 将它们暴露出来,并限制了它们的使用范围:specializes 适用于定义之间,而 subsetsredefines 适用于用法之间。由于 KerML 将分类器和特征都视为 Type,特化可以在整个模型中统一应用。

四种特化关系

关系适用对象关键字简写语义效果
子分类定义 ↔ 定义specializes:>子类型继承所有特征
子集用法 ↔ 用法subsets:>值构成子集
重定义用法 ↔ 用法redefines:>>替换继承的特征
特征类型化用法 → 定义typed by:指定类型
表 1 — 特化关系及其简写
💡 上下文相关操作符

:> 在定义之间表示子分类,在用法之间表示子集。定义/用法的区别决定了含义。

子集示例

1part def ReindeerTeam {
2 abstract part reindeer : Reindeer[9]; // 共9只驯鹿
3 part rudolph : Reindeer subsets reindeer; // rudolph 是其中之一
4 part dasher : Reindeer subsets reindeer; // dasher 是另一只
5}

重定义示例

1part def Vehicle {
2 part eng : Engine;
3 attribute vendor : ScalarValues::String;
4}
5
6part def DieselVehicle specializes Vehicle {
7 part eng : DieselEngine redefines eng; // 缩窄类型
8 attribute vendor : ScalarValues::String = "BoschMotorsport"
9 redefines vendor; // 绑定值
10}

共轭 — 镜像端口

共轭创建一个类型的镜像,其中所有特征方向都被反转:inoutinout 保持不变。~ 前缀表示共轭形式——主要用于端口兼容性:

1port def FuelPort {
2 out item fuelSupply : Fuel;
3 in item fuelReturn : Fuel;
4}
5
6part def FuelTank { port tankPort : FuelPort; } // 发送燃油
7part def Engine { port engFuelPort : ~FuelPort; } // 接收燃油
⚠️ 常见陷阱

混淆 subsetsredefines。将 subsets(子集)用在你想要 redefines(重定义)的地方,会创建一个额外的特征而不是替换继承的特征——模型最终同时拥有原始特征和新特征。redefines 是替换;subsets 是并列添加。

5

多重性

KerML 来源:Multiplicity(一种 Feature——类型化元素将 Multiplicity Feature 作为拥有的子元素携带)

在 KerML 中,多重性本身就是一个 Feature——它不是 UML 风格的注解,而是由类型化元素拥有的一等模型元素。这允许模型级的参数化多重性,并且多重性参与与其他所有 Feature 相同的机制(继承、重定义、约束)。SysML v2 直接继承这一点,并在 KerML 的开放式 [0..*] 上额外施加了特定领域的默认値。

语法和常见形式

1part def Vehicle {
2 part engine : Engine; // 默认 [1..1]
3 part wheels : Wheel[4]; // 恰好4个
4 part passengers : Person[0..5]; // 0到5个
5 attribute tags : String[*]; // 零个或多个(= [0..*])
6 part sensors : Sensor[1..*]; // 一个或多个
7 ref part trailer: Trailer[0..1]; // 可选引用
8}

默认多重性 — 关键细节

上下文默认多重性
KerML feature(未指定)[0..*] — 无约束
SysML part 用法[1..1] — 恰好一个
SysML attribute 用法[1..1] — 恰好一个
SysML port 用法[1..1] — 恰好一个
表 2 — KerML 层与 SysML 层的默认多重性

有序、唯一与集合类型

关键字集合类型
(默认)集合 — 无序,唯一
ordered有序集合 — 有序,唯一
nonunique包 — 无序,允许重复
ordered nonunique序列 — 有序,允许重复
⚠️ 初学者常见错误

特征上的多重性约束的是每个被特征化实例的值数量(例如,每辆 Vehicle 有4个 Wheel)。分类器上的多重性约束的是整个宇宙中该类型的总体数量。这是非常不同的概念——实践中特征级含义占绝大多数。

NOTE

在 KerML 中,集合类型(orderednonunique)被编码为 Multiplicity Feature 本身的属性。SysML 将这些标志作为关键字修饰符继承下来。默认(无关键字)是集合:无序且唯一——与数学集合论一致。

6

特征模型

KerML 来源:Feature(一种 Type——核心层)

Feature 是一个 Type,它对事物之间的关系进行分类。由于 Feature 是 Type,它们可以拥有子特征、被特化、携带多重性,并参与每种 Type 关系。这种统一——UML 中没有的——是 KerML 中最重要的架构决策。在 SysML v2 中,每个用法关键字(partattributeactionport)在底层都产生一个 KerML Feature;关键字只是在原始 KerML Feature 之上添加了特定领域的约束。

特征方向:inoutinout

方向声明了数据或控制信号相对于拥有它的元素流向哪一侧。它只对参与交互边界的特征有意义——端口内的流动项、动作的参数,或函数的输入和输出。

🏠 ANALOGY

想象墙上的电源插座。从建筑的角度看,墙壁供应电力——这是 out。从电器的角度看,它接收电力——这是 in。同一条物理连接,从一侧看是 out,从另一侧看是 in。这正是共轭(~)所捕捉的概念:它创建一个镜像端口类型,其中每个方向都被反转。

1port def PowerPort {
2 out item voltage : ElectricalPower; // 此端口向外供应电力
3 in item load : LoadSignal; // 此端口接收负载信号
4}
5
6// ~PowerPort 是共轭——反转所有方向:
7// in item voltage : ElectricalPower (现在接收电力)
8// out item load : LoadSignal (现在发送负载信号)
9
10action def MotorControl {
11 in rpm : Real; // 输入:控制器期望的转速
12 in voltage : Real; // 输入:供电电压
13 out torque : Real; // 输出:产生的扭矩
14}
NOTE

有方向的特征自动是引用性的——不需要添加 ref端口本身没有方向:不要写 in port p。方向属于端口定义内部的特征,而不是端口本身。

复合 vs 引用:part vs ref part

这个区别控制所有权和生命周期。它回答的问题是:这个元素的存在是因为它的父级存在,还是它独立于父级而存在?

🏠 ANALOGY

复合就像焊接在汽车里的发动机:它与汽车共生共死。引用就像坐在车里的司机:他们下车后仍然独立存在。

1part def Hospital {
2 part ward : Ward[1..*]; // 复合:病房只存在于这家医院内
3 ref part doctor: Doctor[0..*]; // 引用:医生在这里工作,但独立存在
4 attribute name : String; // 值:只是数据,没有独立存在
5}
6
7// 后果:删除 Hospital 也会删除它的 Ward。
8// Doctor 不受影响——它们独立存在。

四种值赋值方式

SysML v2 对如何赋值做了精确区分,将永久约束与初始起始值区分开来,将可继承的默认值与固定值区分开来。每种形式回答的问题:

语法名称回答的问题能否被覆盖?
= expr绑定值这个值始终是什么?不能——永久不变量,不可改变
:= expr初始值这个值什么开始?能——实例生命周期内可以改变
default = expr默认绑定除非特化覆盖,值默认是什么?能——重定义的子类型可以覆盖
default := expr默认初始除非特化覆盖,值默认从什么开始?能——重定义的子类型可以覆盖
表 3 — SysML v2 中的四种值赋值方式
🏠 ANALOGY

想想汽车仪表盘。绑定值就像车轮数量永远是 4——永不改变的结构事实。初始值就像出厂时油量从满格开始——使用过程中会变化。默认绑定就像所有车型共用的标准胎压——除非运动车型覆盖它。默认初始就像座椅位置的出厂设置——每个司机都可以更改。

1part def Engine {
2 attribute cylinders : Integer = 4; // 绑定:始终为4,不可改变
3 attribute rpm : Real := 0; // 初始:从0开始,运行时会变化
4 attribute idleRPM : Real default = 800; // 默认绑定:800,子类型可覆盖
5 attribute warmupTime : Real default := 30; // 默认初始:从30秒开始,可覆盖
6}
7
8part def RacingEngine specializes Engine {
9 attribute idleRPM : Real = 1200 redefines idleRPM; // 覆盖默认绑定
10}
⚠️ 常见陷阱

=永久不变量,不是起始点。写 attribute rpm : Real = 0 意味着转速在实例整个生命周期内都不能偏离零。当你的意思是"从这个值开始,但运行过程中会变化"时,请使用 :=

特征链 — 点表示法路径

特征链让你使用点表示法穿越嵌套特征的路径。它构建一个派生特征,其值是沿整条链导航的结果——链中的每一步产生一组值,这组值成为下一步的输入域。

你在三种主要情况下使用特征链:

🏠 ANALOGY

"我的堂兄弟"不是一个直接关系——你必须沿路径走:我的父母,然后他们的兄弟姐妹,然后他们的孩子。特征链正是把这种多跳路径命名为一个单一可达的特征。

1// ── 使用点表示法定义派生特征 ────────────────────────────────
2part def Company {
3 part department : Department[1..*];
4 // allEmployees 导航路径:Company -> Department -> Employee
5 feature allEmployees : Employee[*] = department.employee;
6}
7part def Department {
8 part employee : Employee[1..*];
9}
10
11// ── 流语句中的点表示法 ──────────────────────────────────────
12// 流不能只说"从 tank 到 engine"——必须命名
13// 嵌套在每个端口内的具体流动项端点。
14part def DriveSystem {
15 part tank : FuelTank { port fuelOut : FuelPort; }
16 part engine : Engine { port fuelIn : ~FuelPort; }
17
18 flow of Fuel from tank.fuelOut.fuelSupply // tank -> 端口 fuelOut -> 流动项 fuelSupply
19 to engine.fuelIn.fuelSupply; // engine -> 端口 fuelIn -> 流动项 fuelSupply
20}
21
22// ── 约束中访问嵌套属性 ──────────────────────────────────────
23requirement def PowerBudget {
24 subject sys : ElectricalSystem;
25 require constraint {
26 sys.battery.voltage >= 12[V] // sys -> battery(部件)-> voltage(属性)
27 }
28}
NOTE

SysML v2 中的点表示法有精确的 KerML 语义:a.b.c 的每一段都缩窄了前一段产生的值的集合。链式特征是模型中一个真实的 Feature,其多重性由链派生而来——它不只是语法糖。

7

完整示例 — 多包系统

以下示例改编自官方 Sensmetry SysML v2 速查表,在一个完整的模型中演示了本模块的大多数概念:

1package VehicleSystem {
2 private import ScalarValues::*;
3 private import ISQ::*;
4 private import SI::*;
5
6 package Definitions {
7 part def Vehicle {
8 attribute mass : ISQ::MassValue;
9 part engine : Engine;
10 part fuelTank: FuelTank;
11 port fuelPort: FuelPort;
12 }
13
14 part def Engine {
15 attribute power : ISQ::PowerValue;
16 port engineFuelPort : ~FuelPort;
17 }
18
19 port def FuelPort {
20 out item fuelSupply : Fuel;
21 in item fuelReturn : Fuel;
22 }
23
24 part def SportsCar specializes Vehicle {
25 part engine : TurboEngine redefines engine;
26 }
27
28 part def TurboEngine specializes Engine {
29 attribute boostPressure : ISQ::PressureValue;
30 }
31 }
32
33 package Instances {
34 private import Definitions::*;
35
36 part myCar : SportsCar {
37 attribute mass : ISQ::MassValue = 1400[kg] redefines mass;
38 }
39 }
40
41 package Requirements {
42 private import Definitions::*;
43 private import Instances::*;
44
45 requirement def MassLimit {
46 subject vehicle : Vehicle;
47 attribute massLimit : ISQ::MassValue;
48 require constraint { vehicle.mass <= massLimit }
49 }
50
51 requirement vehicleMassReq : MassLimit {
52 subject vehicle = myCar;
53 attribute massLimit : ISQ::MassValue = 1800[kg] redefines massLimit;
54 }
55
56 assert satisfy vehicleMassReq by myCar;
57 }
58}

此模型演示了:包和嵌套、私有通配符导入、子分类(specializes)、重定义(redefines)、带有方向特征的端口定义、共轭(~)、特征链、约束、需求,以及定义/用法模式。

8

本模块总结

SysML v2 概念KerML 来源关键规则
packageNamespace(不特化 Type无实例语义;仅用于组织模型
importImport(一种 Membership 关系)默认是公开的;除非有意重新导出,否则使用 private import
可见性(public/private每个 Membership 上的 VisibilityKind独立作用于成员和导入
specializesSubclassification 关系仅用于定义之间;子类型继承所有特征
subsetsSubsetting 关系用于用法之间;在继承特征旁并列添加命名子集
redefinesRedefinition 关系用于用法之间;完全替换继承的特征
共轭(~TKerML Conjugation 关系反转类型内 Feature 上的所有方向
多重性 [m..n]Multiplicity Feature(拥有的子元素)KerML 默认:[0..*];SysML part/attribute/port:[1..1]
ordered / nonuniqueMultiplicity Feature 上的属性编码集合/包/序列/有序集合类型
特征方向(in/out/inoutFeature 上的方向属性仅在交互边界(端口、动作)处有意义
特征链(点表示法)FeatureChaining 关系多跳点表示法路径(如 a.b.c)构建具有从链派生多重性的派生 Feature
表 — 模块二概念与其 KerML 来源
下一讲

模块三 — 结构建模 — 部件、属性、项和分解:使用 SysML v2 构建完整的结构模型。