第四章:Composition与连接——把模块拼起来
好,咱们进入实战环节了。前面几章我们把基础概念过了一遍,现在该动手了。这一章讲的是Composition——说白了,就是怎么把一个个小模块拼成一个大系统。
我在做第一个DaVinci项目时,最头疼的就是模块之间的连接。明明每个模块单独跑都没问题,一拼起来就各种报错。后来才发现,是Composition的层级设计出了问题。嗯,咱们今天就把它彻底讲透。
4.1 创建Composition——搭积木的第一步
Composition是什么?你可以把它想象成一个容器。这个容器里可以放各种SWC(软件组件),也可以放别的Composition。就像搭积木,大积木里套小积木。
创建Composition的步骤其实很简单:
- 在DaVinci Config中,右键点击你的项目
- 选择"New Composition"
- 给它起个名字(我建议用有意义的命名,比如"EngineControl_Comp")
- 选择类型——是Atomic还是Composition
这里有个坑:Composition的命名不能有空格。我曾经有个同事用了"Engine Control Comp",结果编译时报了一堆莫名其妙的错误。找了半天才发现是空格的问题。
4.2 SWC实例化——让组件活起来
创建好Composition后,下一步就是往里面放SWC。这个过程叫实例化。
你想想看,SWC就像是一个类,实例化就是把它变成具体的对象。同一个SWC可以被实例化多次,每个实例都有自己的数据空间。
实例化的操作:
- 在Composition的编辑界面,找到"SWC"选项卡
- 拖拽你需要的SWC到Composition中
- 给每个实例起一个唯一的名称(比如"SpeedSensor_Inst1")
重要:每个实例的名称在整个项目中必须唯一。我见过有人偷懒,两个实例用了同一个名字,结果运行时数据互相覆盖,查了三天才找到原因。
实例化时还要注意一点:SWC的端口类型。每个SWC都有输入输出端口,实例化后这些端口会暴露出来,供后续连接使用。
4.3 端口连接(Connector)——把线接上
好了,现在Composition有了,SWC实例也有了。接下来就是最关键的步骤——端口连接。
端口连接说白了就是画线。把A组件的输出端口,连到B组件的输入端口。但这里有很多讲究:
4.3.1 端口类型匹配
连接时,端口类型必须一致。比如:
| 端口A类型 | 端口B类型 | 能否连接 |
|---|---|---|
| Sender-Receiver | Sender-Receiver | 可以 |
| Client-Server | Client-Server | 可以 |
| Sender-Receiver | Client-Server | 不行 |
4.3.2 数据一致性
除了类型,还要看数据类型。比如端口A输出的是uint8,端口B输入的是uint16,虽然能连上,但会有精度损失。我建议:尽量保持数据类型一致。
连接的操作步骤:
- 在Composition编辑界面,点击"Connector"工具
- 从源端口拖出一条线
- 连接到目标端口
- 系统会自动生成一个Connector对象
代码示例(生成的ARXML片段):
<CONNECTOR>
<SHORT-NAME>Conn_SpeedToControl</SHORT-NAME>
<CONNECTOR-PORT-REF>
<PORT-REF>/System_Comp/SpeedSensor_Inst1/SpeedOut</PORT-REF>
</CONNECTOR-PORT-REF>
<CONNECTOR-PORT-REF>
<PORT-REF>/System_Comp/Control_Inst1/SpeedIn</PORT-REF>
</CONNECTOR-PORT-REF>
</CONNECTOR>
4.4 Composition层级设计——别把线搞乱了
这是我最想强调的部分。Composition的层级设计,直接决定了项目的可维护性。
我见过最糟糕的项目:所有SWC都放在一个Composition里,端口连接线密密麻麻,像一团蜘蛛网。别说维护了,看都看不清。
好的层级设计应该是这样的:
- 顶层Composition:只放几个大的子Composition,比如"Engine_Comp"、"Transmission_Comp"、"Body_Comp"
- 中间层Composition:每个子Composition里再细分,比如"Engine_Comp"里放"Sensor_Comp"、"Actuator_Comp"
- 底层Composition:直接放具体的SWC实例
为什么要这样设计?说白了就是降低复杂度。每个层级只关注自己的事情,上层不用管下层的细节。
举个例子:
System_Comp (顶层)
├── Engine_Comp (中间层)
│ ├── Sensor_Comp (底层)
│ │ ├── SpeedSensor_Inst
│ │ └── TempSensor_Inst
│ └── Actuator_Comp (底层)
│ ├── Injector_Inst
│ └── Throttle_Inst
├── Transmission_Comp (中间层)
│ ├── GearControl_Inst
│ └── ClutchControl_Inst
└── Body_Comp (中间层)
├── LightControl_Inst
└── DoorControl_Inst
你看,这样一分层,每个Composition的职责就清晰了。连接线也只存在于同一个层级内部,或者上下层级之间。不会出现跨三层以上的连接。
4.5 避坑指南
最后,分享几个我踩过的坑:
- 不要用默认名称:DaVinci Config生成的默认名称像"Connector_1"、"Connector_2",根本看不出是连什么的。我习惯用"Conn_SpeedToControl"这种命名方式。
- 注意端口方向:连接时一定要看清楚端口方向。我曾经把输出连到了输出,系统没报错,但数据就是传不过去。
- 定期检查连接:项目大了以后,连接很容易出错。我每周都会用DaVinci Config的"Check Consistency"功能跑一遍,确保所有连接都是有效的。
嗯,这一章的内容就到这里。Composition和连接是DaVinci Config的核心,搞懂了这些,后面的内容就顺了。下一章我们讲运行时配置,到时候会用到今天学的这些知识。