模块四:行为建模
如何描述系统的行为——动作、数据流、控制流和状态机——以及每个行为关键字如何映射到底层 KerML Occurrence 层构件。
KerML → SysML:行为建模概念对应关系
SysML v2 的行为建模建立在 KerML 的 Occurrence(发生事件)层上——该语言层用于描述随时间发生的事物,与描述持续存在的事物的结构层相对应。本模块中的每个行为关键字都是该 KerML Occurrence 层中某个 L1 构件在 SysML L2 的应用。
| SysML v2 概念(L2) | 底层 KerML 构件(L1) | SysML 额外添加了什么 |
|---|---|---|
action def | Behavior(一种 Classifier,KerML Occurrence 层) | 关键字表示某个在时间区间内发生的事情;实例是发生事件,不是持久对象 |
action(用法) | 由 Behavior 类型化的 Feature | 默认复合 [1..1];在父动作的生命周期内执行 |
perform action | 引用 Behavior 的 FeatureMembership | 通过名称调用行为的简写,无需声明子动作体 |
in / out / inout 参数 | Behavior 上带方向的 Feature | 方向限定谁提供或接收値;可由任意分类器类型化 |
| 流(动作上的流动项流) | ItemFlow(Interaction 的特化) | 声明在两个动作参数端点之间传递的流动项 |
succession | Succession(带时间语义的 Connector) | 源必须完成后目标才可开始;编码时间顺序 |
first / then | Succession 的简写 | 一对顺序约束;可链接成动作序列 |
if / else / merge | 决策/合并 ControlNode 模式 | 条件路由:根据年守条件执行一个分支;merge 重新合并分支 |
loop | 带年守的 Succession 循环 | 条件成立时重复体 |
fork / join | Fork/Join ControlNode 模式 | fork 同时激活多个动作;join 等待所有完成 |
state def / state | StateDefinition(特化自 ActionDefinition / BehaviorDefinition) | 为行为添加 entry/do/exit 语义和带触发的转换 |
transition | TransitionUsage(一种 Succession) | 为状态间的时间顺序添加触发器、年守和效果 |
accept | AcceptActionUsage | 等待消息或信号到达后再继续 |
send | SendActionUsage | 向另一个元素发送消息或信号 |
occurrence def | Occurrence(KerML Occurrence 层根) | 可复用的「发生的事物」类型;action def、state def 等的基类 |
在 KerML 中,Occurrence 层位于 Core 层之上。Occurrence 特化自 Class,并添加了「在时间区间内发生」的概念。Behavior 进一步特化自 Occurrence,添加了「被执行」的概念。SysML v2 的每个行为关键字——action def、state def、transition——在 KerML 层最终都是 Behavior 或 Occurrence 的特化。
动作与行为
KerML 来源:action def → Behavior(Occurrence 层); action → 由 Behavior 类型化的 Feature
动作(action)描述系统在时间区间内所做的事情。这是与模块三的根本区别:部件存在,动作发生。在 KerML 层面,Behavior 特化自 Occurrence,而 Occurrence 特化自 Class——因此结构建模中所有的机制(特化、特征、多重性、链接)同样适用于行为。
汽车持续存在。启动发动机是一个动作——它在时间区间内发生,有输入(騥匙、燃油),产生输出(运行中的发动机),并最终终止。汽车(结构)和启动序列(行为)是两种不同类型的模型元素,但在 KerML 中都是 Classifier。
定义和使用动作
模块一中的定义/用法分离同样适用于此。action def 定义模板;action 在特定上下文中使用它:
1 action def FuelPump { 2 in item fuelRequest : FuelRequest; // 输入:请求内容 3 out item fuelDelivery : Fuel; // 输出:输送的燃油 4 } 5 6 action def EngineStart { 7 action primeSystem : PrimeFuelSystem; // 子动作:由其 action def 类型化的 Feature 8 action crankEngine : CrankEngine; 9 action fireIgnition : FireIgnition; 10 }
嵌套与分解
动作可以任意层次嵌套。每个子动作是一个复合 Feature,其执行在父动作的生命周期内——与结构建模中复合 part 被其父部件拥有的方式完全一致:
1 action def MissionProfile { 2 action takeOff : TakeOff; 3 action cruise : CruiseSegment; 4 action land : Landing; 5 // 没有显式顺序时,执行顺序未定义。 6 // 使用 succession / then 指定顺序(见第 3 节)。 7 }
action def 映射到 KerML Behavior。action 用法映射到由该 Behavior 类型化的 Feature。由于 Behavior 本身是 Class,动作定义可以相互特化(specializes),动作用法可以使用 subsets 和 redefines——与结构特征完全相同。
调用已有动作定义
使用 perform action 通过名称调用一个已定义的动作,无需重新声明其体。这是一个成员引用,不是新定义:
1 action def SystemTest { 2 perform action runDiagnostics : Diagnostics; // 按类型引用 Diagnostics 3 perform action logResults : Logger; 4 }
action 不是编程意义上的函数调用。它是由 Behavior 类型化的 Feature——有生命周期,可以并发执行子动作,并参与完整类型系统。把它当作方法调用来思考,当引入顺序、并发和数据流时就会产生困惑。
动作参数与数据流
KerML 来源:参数 → Behavior 上带方向的 Feature; 流动项流 → ItemFlow
动作通过参数传递流动项来相互通信。参数是动作定义上带方向的 Feature——与模块三中端口特征的 in / out / inout 机制相同,但应用于动作。KerML 来源相同:Classifier 上带方向的 Feature。
声明参数
1 action def ComputeTrajectory { 2 in attribute startPos : GeoCoordinate; // 输入:起始位置 3 in attribute targetPos : GeoCoordinate; // 输入:目标位置 4 in attribute windSpeed : ISQ::SpeedValue; // 输入:环境数据 5 out attribute trajectory : Trajectory; // 输出:计算的路径 6 out attribute flightTime : ISQ::TimeValue; // 输出:预估时间 7 }
子动作之间的流动项流
在复合动作内部,流动项流将一个子动作的输出参数连接到另一个的输入参数。这是结构 flow 语句在行为领域的类比,映射到 KerML ItemFlow:
1 action def DriveControl { 2 action sense : SenseEnvironment { out item data : SensorData; } 3 action plan : PlanRoute { in item data : SensorData; 4 out item cmd : DriveCommand; } 5 action execute : ExecuteCommand { in item cmd : DriveCommand; } 6 7 // 流动项流:一个动作的输出首先进入下一个的输入 8 flow sense.data to plan.data; 9 flow plan.cmd to execute.cmd; 10 }
想想一条生产线。燊接工位(sense)把它的输出交给涂调工位(plan),后者再把输出交给装配工位(execute)。流动项流就是各工位间的传送带。
通过层次绑定参数
使用 bind 可以将外部动作的参数绑定到内部子动作的参数,在不创建流动项流对象的情况下跨动作边界传递値:
1 action def MissionPlan { 2 in attribute origin : GeoCoordinate; 3 out attribute path : Trajectory; 4 5 action compute : ComputeTrajectory { 6 bind compute.startPos = origin; // 外部输入绑定到内部输入 7 bind compute.trajectory = path; // 内部输出绑定到外部输出 8 } 9 }
参数是 Feature,因此遵守所有 Feature 规则:它们携带多重性,可由任意 Classifier 类型化(包括 DataType 和 Class),并可在动作定义的特化中被子集或重定义。
控制流与顺序
KerML 来源:succession → Succession(一种 Connector); if/else → 决策/合并 ControlNode; fork/join → Fork/Join ControlNode
默认情况下,复合动作中的子动作没有定义的执行顺序。它们可以并发执行、以任意顺序执行,或仅在数据依赖关系满足时执行。要强制指定顺序,SysML v2 提供了 succession 以及更高级的控制流构件。
Succession:显式顺序
两个动作之间的 succession 意味着:第一个动作必须完成后,第二个才可以开始。这是结构连接器在行为领域的类比——它是一个 KerML Succession,本身是 Connector 的特化:
1 action def EngineStart { 2 action prime : PrimeFuelSystem; 3 action crank : CrankEngine; 4 action fire : FireIgnition; 5 6 succession prime then crank; // prime 必须完成后 crank 才能开始 7 succession crank then fire; // crank 必须完成后 fire 才能开始 8 }
first / then 简写可以紧凑地链接顺序:
1 action def EngineStart { 2 action prime : PrimeFuelSystem; 3 action crank : CrankEngine; 4 action fire : FireIgnition; 5 6 first prime then crank then fire; // 等价于两个 succession 语句 7 }
条件分支:if / else
if 块声明一个决策节点:年守表达式被评估,执行恰好一个分支。KerML 模型是带有年守出口 Succession 的 DecisionNode 控制节点:
1 action def HandleRequest { 2 action validate : ValidateRequest; 3 action process : ProcessRequest; 4 action reject : RejectRequest; 5 action respond : SendResponse; 6 7 first validate; 8 if validate.isValid { 9 then process; 10 } else { 11 then reject; 12 } 13 // merge 节点隐式合并两个分支 14 then respond; 15 }
merge 关键字将多个入站分支重新合并为一个出口流。当两个或多个分支在共同的继续前汇聚时,就需要它——没有它,继续将要求所有分支都完成(join 语义)而不是任意一个分支完成。
并行执行:fork / join
fork 同时激活多个子动作。join 等待直到所有Fork 出的动作都完成才继续。它们映射到 KerML Fork/Join 控制节点模式:
1 action def SystemInitialise { 2 action loadFirmware : LoadFirmware; 3 action selfTest : RunSelfTest; 4 action calibrateSensors : Calibrate; 5 action connectNetwork : ConnectNetwork; 6 action reportReady : ReportReady; 7 8 // 四个初始化任务并行运行 9 fork { 10 then loadFirmware; 11 then selfTest; 12 then calibrateSensors; 13 then connectNetwork; 14 } 15 // 等待四个任务都完成后完成报告 16 join then reportReady; 17 }
循环
1 action def PollSensor { 2 action read : ReadSensorValue; 3 action process : ProcessReading; 4 5 loop { 6 first read then process; 7 } while process.shouldContinue; 8 }
Succession 和 Fork/Join 建模的是完成顺序,不是时间。Succession 不是说“100 毫秒后开始下一个动作”——它说的是第一个必须完成后第二个才可以开始。对于实时时间约束,请使用关于动作持续时间的需求约束,而不是使用 succession。
状态机
KerML 来源:state def → StateMachine(特化自 Behavior); transition → TransitionUsage(一种 Succession)
状态机建模行为取决于当前状态的系统。在 SysML v2 中,state def 映射到 KerML StateMachine,它本身是 Behavior 的特化。这意味着状态机是行为——它们可以作为动作用法出现,接收参数,并被组合进更大的动作网络中。
想想交通信号控制器。它不执行顺序算法——它总是处于三种状态之一(红灯、黄灯、绿灯),并在收到事件时改变状态(计时器触发、传感器触发)。状态机捕捉的正是这种响应式、事件驱动的行为。
定义状态
每个状态可以有可选的 entry、do 和 exit 动作:
1 state def TrafficLightController { 2 entry; // 初始伪状态——执行从这里开始 3 4 state RED { 5 entry action activateRed : ActivateRedLight; // 进入时执行 6 do action holdRed : HoldRedSignal; // 处于状态时持续执行 7 exit action deactivateRed : DeactivateLight; // 离开时执行 8 } 9 10 state AMBER { 11 entry action activateAmber : ActivateAmberLight; 12 } 13 14 state GREEN { 15 entry action activateGreen : ActivateGreenLight; 16 do action holdGreen : HoldGreenSignal; 17 } 18 }
转换
transition 是一个 TransitionUsage——一个添加了触发器、可选的年守和可选的效果动作的 KerML Succession:
1 // 语法:transition <源状态> accept <触发器> if <守卫> do <效果> then <目标状态> 2 3 state def TrafficLightController { 4 state RED; state AMBER; state GREEN; 5 6 entry; then RED; // 初始转换:从 RED 状态开始 7 8 transition RED 9 accept TimerExpired 10 then GREEN; 11 12 transition GREEN 13 accept TimerExpired | PedestrianRequest 14 then AMBER; 15 16 transition AMBER 17 accept TimerExpired 18 then RED; 19 }
年守条件与效果
1 state def SafetyController { 2 state NORMAL; state DEGRADED; state SAFE_SHUTDOWN; 3 4 entry; then NORMAL; 5 6 transition NORMAL 7 accept HealthCheck // 触发器:由 HealthCheck 事件触发 8 if systemHealth < 0.5 // 守卫:仅当健康值较低时触发 9 do action log : LogDegradation // 效果:转换过程中执行 10 then DEGRADED; 11 12 transition DEGRADED 13 accept HealthCheck 14 if systemHealth < 0.1 15 then SAFE_SHUTDOWN; 16 }
状态内的 accept 和 send
1 state def CommandProcessor { 2 state IDLE; state PROCESSING; 3 4 entry; then IDLE; 5 6 transition IDLE 7 accept CommandReceived // 等待 CommandReceived 事件 8 then PROCESSING; 9 10 state PROCESSING { 11 do action work : ProcessCommand; 12 exit action notify { 13 send : CompletionSignal; // 离开时发送信号 14 } 15 } 16 17 transition PROCESSING accept work.done then IDLE; 18 }
在 SysML v2 中,状态机是一等公民的行为。state def 可以作为更大动作序列中 action 用法的类型。这意味着你可以将状态机与顺序和并行动作流组合——这在 SysML v1 中是嵌嵌扣扣的。
发生事件与快照
KerML 来源:occurrence def → Occurrence(KerML Occurrence 层根); snapshot → 在时间熵上的结构投影
KerML 的 Occurrence 层引入了两个支撑本模块所有内容的基本概念:发生事件(在时间区间内发生的事物)和快照(单一时刻宜发生事件的样子)。SysML v2 中的每个动作、状态和消息最终都是一个发生事件。
发生事件定义
occurrence def 命名一种可复用的「发生的事物」类型——在将其特化为动作或状态机之前最通用的形式。当你想描述事件或交互而不需要立即确定它们是动作、状态还是消息时,它很有用:
1 occurrence def SystemEvent; 2 occurrence def FaultEvent specializes SystemEvent { 3 attribute severity : Integer; 4 attribute component : String; 5 } 6 7 occurrence def RecoveryAction specializes SystemEvent { 8 attribute targetComponent : String; 9 }
快照——穿越时间的结构切片
快照捕捉一个部件在发生事件特定时刻的状态。它是一个结构投影——一个关于在该时刻持有哪些属性値和子部件配置的观点。快照用于声明前置条件、后置条件和不变量:
1 action def ChargeBattery { 2 in part battery : Battery; 3 4 // 前置条件:充电开始时电池的状态 5 snapshot startState { 6 assert constraint { battery.stateOfCharge < 0.9 } 7 } 8 9 action doCharge : Charge; 10 11 // 后置条件:充电结束时电池的状态 12 snapshot endState { 13 assert constraint { battery.stateOfCharge >= 0.99 } 14 } 15 }
快照不是插入动作序列中的检查点。它们描述在发生事件开始和结束时必须成立的结构条件。工具利用它们进行验证:如果仿真或形式分析表明后置条件无法到达,则存在建模缺陷。
时间切片与时间顺序
1 requirement def ResponseTime { 2 subject mission : MissionAction; 3 require constraint { 4 // 任务的结束必须在其开始后 60 秒内发生 5 mission.endShot.time - mission.startShot.time <= 60[s] 6 } 7 }
发生事件就像一段影片片段:它覆盖一段时间。快照是这段片段的单帧画面。时间切片是从一个时刻到另一个时刻的子片段。SysML v2 让你可以对单帧(快照)或整段片段(发生事件)做出断言。
完整示例
以下模型综合了本模块的所有内容:一个 UAV 自驾仪行为,结合了顺序动作、数据流、判断、并发和流模式管理状态机。
1 package UAVBehaviour { 2 private import ISQ::*; private import SI::*; 3 private import UAVSystem::*; // 模块三工作示例中的部件 4 5 // ── 发生事件(共享事件词汇) ─────────────────────── 6 occurrence def FlightEvent; 7 occurrence def TakeoffCleared specializes FlightEvent; 8 occurrence def LandingCleared specializes FlightEvent; 9 occurrence def FaultDetected specializes FlightEvent { 10 attribute severity : Integer; 11 } 12 13 // ── 状态机:飞行模式 ───────────────────────────────── 14 state def FlightModeController { 15 state GROUND; state TAKEOFF; state CRUISE; 16 state LANDING; state EMERGENCY; 17 18 entry; then GROUND; 19 20 transition GROUND 21 accept TakeoffCleared 22 then TAKEOFF; 23 24 transition TAKEOFF 25 accept AltitudeReached 26 if altitude >= 50[m] 27 then CRUISE; 28 29 transition CRUISE 30 accept LandingCleared 31 then LANDING; 32 33 transition LANDING 34 accept TouchdownDetected 35 then GROUND; 36 37 // 从每个状态:故障触发紧急状态 38 transition GROUND 39 accept FaultDetected 40 if severity >= 2 41 do action log : LogEmergency 42 then EMERGENCY; 43 transition TAKEOFF 44 accept FaultDetected 45 if severity >= 2 46 then EMERGENCY; 47 transition CRUISE 48 accept FaultDetected 49 if severity >= 2 50 then EMERGENCY; 51 transition LANDING 52 accept FaultDetected 53 if severity >= 2 54 then EMERGENCY; 55 } 56 57 // ── 传感器融合动作 ───────────────────────────────────── 58 action def SensorFusion { 59 in item gpsData : GPSReading; 60 in item imuData : IMUReading; 61 out attribute navEstimate : NavigationState; 62 } 63 64 // ── 导引律动作 ─────────────────────────────────────────── 65 action def GuidanceLaw { 66 in attribute navEstimate : NavigationState; 67 in attribute targetWP : Waypoint; 68 out attribute motorCmds : MotorCommandSet; 69 } 70 71 // ── 顶层自驾仪循环 ───────────────────────────────────── 72 action def AutopilotLoop { 73 in part uav : UAV; 74 75 // 状态机与导引循环并发运行 76 action modeCtrl : FlightModeController; 77 78 action fuse : SensorFusion; 79 action guide : GuidanceLaw; 80 action actuate : ActuateMotors; 81 82 // 数据流:融合输出首先进入导引输入 83 flow fuse.navEstimate to guide.navEstimate; 84 flow guide.motorCmds to actuate.motorCmds; 85 86 // 控制流:每次迭代内的序列 87 loop { 88 first fuse then guide then actuate; 89 } while uav.flightMode != FlightMode::GROUND; 90 91 // 前置条件快照 92 snapshot preCheck { 93 assert constraint { uav.battery.health >= 0.2 } 94 } 95 } 96 }
此模型演示了:occurrence def(共享事件词汇)、带触发器和年守的 state def、动作参数和流动项流(传感器融合管道)、带 first/then 的 succession、带终止条件的 loop、与顺序循环并发运行的并发状态机,以及 快照前置条件。
本模块总结
| SysML v2 概念 | KerML 来源 | 关键规则 |
|---|---|---|
action def | Behavior(Occurrence 层) | 实例是发生事件,不是持久对象;在时间区间内发生 |
action(用法) | 由 Behavior 类型化的 Feature | 默认复合 [1..1];在父动作的生命周期内执行 |
参数(in/out/inout) | Behavior 上带方向的 Feature | 与端口特征相同的机制;可由任意 Classifier 类型化 |
流动项流(flow X to Y) | ItemFlow | 将一个子动作的输出参数连接到另一个的输入参数 |
bind X = Y | BindingConnector | 不创建流动项流对象,跨动作边界传递値 |
succession / first/then | Succession(一种 Connector) | 源必须完成后目标才可开始;建模时间顿序 |
if / else / merge | 决策/合并 ControlNode | 条件分支;merge 将分支重新合并为共同的继续 |
fork / join | Fork/Join ControlNode | fork:同时激活所有;join:等待所有完成 |
loop | 带年守的 Succession 循环 | 条件成立时重复体 |
state def | StateDefinition(特化自 ActionDefinition / BehaviorDefinition) | 响应式行为;每个状态都有 entry/do/exit 动作 |
transition | TransitionUsage(一种 Succession) | 为状态间的顺序添加触发器、年守和效果 |
accept | AcceptActionUsage | 等待信号或消息到达后再继续 |
send | SendActionUsage | 向另一个元素发送信号或消息 |
occurrence def | Occurrence(KerML Occurrence 层根) | 最通用的发生事件类型;action def、state def 等的基类 |
snapshot | 时间熵上的结构投影 | 就发生事件声明结构条件(前/后置条件) |