connectedhomeip(Matter SDK)集群与设备类型开发完全指南:从 XML 定义、ZAP/Ember 集成到单元测试
2026/9/16 14:51:41 网站建设 项目流程

connectedhomeip(Matter SDK)集群与设备类型开发完全指南:从 XML 定义、ZAP/Ember 集成到单元测试

【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip

导读

本文是 Matter 开源 SDK(connectedhomeip)中开发新 Cluster(集群)与 Device Type(设备类型)的端到端技术指南,内容源自仓库 docs/cluster_and_device_type_dev 目录下的系列文档,并辅以当前仓库源码验证。读完本文,你将掌握:如何在src/app/zap-templates/zcl/data-model/chip/下编写集群/设备类型的 XML 定义并接入 ZAP 代码生成链路;如何选择 Ember 层回调与AttributeAccessInterface/CommandHandlerInterface覆盖(Override)两种实现机制;以及如何使用ClusterTester辅助类对 code-driven 集群进行可维护、可认证的单元测试。

集群与设备类型开发的三大目标

开发一个新的 Cluster 或 Device Type,最终要交付三类产出物:

  1. 集群实现(Cluster implementation)——真正运行在设备上的 C++ 逻辑。
  2. 支撑 ZAP 代码生成的素材(ZAP glue)——让 ZAP 工具能识别新集群、为 Ember 层生成函数签名与默认实现。
  3. 测试与认证材料(Tests & certification)——单元测试、YAML/Python 测试计划与自动化脚本,用于证明代码正确性并支撑认证。

单元测试、测试计划与认证测试在 docs/testing 相关章节展开;本文聚焦第 1、2 点,即如何在 SDK 中实现集群与设备类型。

Cluster 定义(XML)

集群定义的载体是 XML 文件,它是对 Matter 规范中集群的直接翻译:

  • 描述结构体(struct)、枚举(enum)、属性(attribute)、命令(command)、事件(event)等;
  • 存放位置:src/app/zap-templates/zcl/data-model/chip(该目录下现存 100 余个集群定义文件,如access-control-cluster.xmlair-quality-cluster.xmlaudio-control-cluster.xml等,可直接作为新集群的参考模板)。

Cluster 实现

实现分客户端与服务器两端:

  • 客户端侧:主要依赖代码生成(codegen),开发者只需编写“胶水层”调用代码。
  • 服务器侧:通过 Ember 层生成代码和/或实现两个覆盖接口:
    • AttributeAccessInterface——拦截属性读写的接口;
    • CommandHandlerInterface——拦截命令处理的接口。
  • 实现文件位于src/app/clusters/<your_cluster_name>,构建配置通过 src/app/chip_data_model.gni 接入——该构建文件利用 codegen 产出的数据自动填充集群列表,在 ZAP 中勾选新集群后即可把对应代码编入固件。

Device Type 定义

设备类型同样以 XML 定义其一致性(conformance)要求,主文件为 src/app/zap-templates/zcl/data-model/chip/matter-devices.xml。从源码可见(matter-devices.xml),每个<deviceType>元素包含设备名称、domaintypeNameprofileIddeviceIdrevisionclass/scope以及强制/可选集群的<include cluster="..." client="..." server="..."/>列表,ZAP 据此决定端点配置中该设备类型可挂载的集群。

注意:定义完成后,还应使用 .matter 解析工具(详见 docs/guides/matter_idl_tooling.md)对照规范验证产出。

ZAP、Ember 与 Overrides:三层协作模型

ZAP(Zigbee Cluster Configurator 的演进版)的核心目标,是让工具链理解新集群,从而在任意设备上被 ZAP 配置所引用(XML 定义 + 胶水代码)。

集群定义与 ZAP 的关系

关于 ZAP 的入门介绍见 docs/zap_and_codegen/zap_intro.md。完成 XML 定义与注册步骤后,可以用 zaptool 打开任意.zap文件来验证集群是否出现在工具中:

./scripts/tools/zap/run_zaptool.sh <filename>

验证要点(依次在 ZAP GUI 中检查):

  1. 设备类型列表:打开端点(Endpoint)配置,确认新设备类型出现在设备类型列表中。
  2. 集群完整性:XML 中的domain参数决定集群归属的分组;确认集群包含全部预期属性、命令与事件。
  3. 属性存储选项:确认每个属性的存储方式(RAM / External)设置符合实现预期。

集群实现:Ember 层与 Overrides 的区别

Ember 层是用于在设备上建立/访问端点、属性、命令等的底层机制:

  • 构建时自动生成 Ember 函数签名与默认实现;
  • 集群服务器代码实现初始化、命令、属性访问、事件生成等回调;
  • 当 Interaction Model(交互模型)收到针对该集群的入站交互时,会调用对应的 Ember 函数。

**Overrides(覆盖)**与 Ember 层最大的差异在于生命周期:

  • Ember 层在编译时生成,而 Overrides 在运行时安装
  • 对属性访问或命令处理而言,Overrides 会先于Ember 层被调用;
  • 通过 AttributeAccessInterface 与 CommandHandlerInterface 实现,提供远高于 Ember 层的控制粒度,但复杂度也更高;
  • 关键优势:可单元测试(UNIT TESTABLE)
  • 若 ZAP 中保留了相应配置,两种机制都允许“回落(fall through)”到 Ember 层。

集群服务器初始化流程

入站消息从 Matter 核心流入集群初始化代码的完整流程如下:

  • EmberAfInitializeAttributes——针对 ZAP 中标记为RAM存储的全部属性,在 Ember 属性存储中写入默认值;
  • Matter<Cluster>PluginServerCallback——.h是生成文件,.cpp实现在集群服务器代码中编写。利用它完成集群初始化,并通过chip::app::AttributeAccessInterfaceRegistry::Instance().Register注册覆盖实例,以在外部处理属性读写。

集群服务器属性:两种机制的选择

存储方式(ZAP 中设置)Ember 层行为Override 要求
RAM自动分配存储,Ember 处理读写;生成文件 Accessors.h 为每个属性生成 Get/Set 函数可选。若覆盖函数中不对该属性编码,则回落至存储层。若总在覆盖函数里编码,则纯属浪费 RAM
External不分配任何存储,不回落 Ember 存储必须注册访问覆盖(access override),否则属性无法工作
通过 Override 读属性

属性读取在 AttributeAccessInterface::Read() 中实现。源码注释明确了三种处理方式:返回失败(转换为 StatusIB 返回给客户端);成功并编码数据(数据返回客户端);成功但不编码(回落 Ember 属性存储/外部回调)。典型实现:

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) { // 解析 aPath 确定被请求的属性 switch (aPath.mAttributeId) { case SomeAttribute::Id: // 直接编码值 aEncoder.Encode(mSomeValue); break; } // 小心 List 属性 —— 必须使用 EncodeList 才能正确处理分块(chunking) return CHIP_NO_ERROR; }
通过 Override 写属性

写入与读取走同一条路径,但落在 AttributeAccessInterface::Write() 中。源码注释同样给出三种行为:返回失败(转换为 StatusIB);成功并解码(视为成功写入);成功但不解码(回落 Ember 存储)。属性处理器(attribute handler)需要自行负责约束检查(constraint checking)属性持久化(attribute persistence)

属性持久化
  • 使用AttributeAccessInterface时,需要自行管理需要持久化的属性;
  • 可通过AttributePersistence(基于AttributePersistenceProvider)完成,它为读写任意类型的值到默认 PersistenceProvider 提供了统一 API,见 src/app/AttributePersistence.h;
  • 单元测试时,可将TestPersistentStorageDelegateDefaultAttributePersistenceProvider组合使用。
Ember 层读写

在 Ember 层函数中,编解码由 Ember 层自己处理。这对简单属性可行,但复杂属性交互会很有挑战,且Ember 层极难单元测试。Ember 层提供属性变更回调供应用处理:

void MatterPostAttributeChangeCallback(const chip::app::ConcreteAttributePath & attributePath, uint8_t type, uint16_t size, uint8_t * value)

注意:使用该回调时要确保实现方式能够跨多个示例(examples)复用。

集群服务器命令

命令处理与属性一样存在 Ember 与 Override 两条路:

  • Override:运行时注册(InteractionModelEngine::RegisterCommandHandler),实现CommandHandlerInterface
  • Ember:静态的emberAf<ClusterName><CommandName>Callback

命令分发流程如下图所示:

命令处理器编写要点
  • CommandHandlerInterface(见 CommandHandlerInterface.h):
    • 可使用HandleCommand便捷函数(自动标记为已处理);
    • 否则需显式设置命令是否已处理——若未处理,默认回落 Ember 层;
    • 若命令完全由该接口处理,在 src/app/common/templates/config-data.yaml 的CommandHandlerInterfaceOnlyClusters列表中登记,关闭 Ember 命令回调生成。
  • Ember 接口:返回true表示命令已处理;返回false会收到无效命令响应。
  • 两者通用
    • 必须在命令处理器中通过AddResponseAddStatus处理对调用方的返回;
    • 必须按规范进行约束检查并返回正确的状态码或响应体。

事件与属性订阅

  • 属性变更上报(Attribute change reporting)
    • 走 Ember 存储层(生成的属性 Get/Set 函数)时自动处理;
    • 使用AttributeAccessInterface时,需要主动通知上报引擎属性已变更——调用MatterReportingAttributeChangeCallback
  • 事件(Events)
    • Ember 层没有直接支持;
    • 调用 EventLogging.h 中的LogEvent函数。调用方需持有 Matter 栈锁(lock),或将事件排入 Matter 事件队列(queue)后再调用LogEvent

动态端点的补充说明

  • ZAP 配置在编译期是静态的;
  • 也可以在运行时注册动态端点(常见于桥接设备 bridge):
    • 使用emberAfSetDynamicEndpoint
    • 如果为属性等数据自建了存储,必须同时考虑动态端点与静态端点两种情况。

向 SDK 添加新集群与设备类型:完整操作清单

以下步骤均建立在 Matter 规范已评审通过的前提下。

第一步:添加集群定义到 SDK

  1. 在目录 src/app/zap-templates/zcl/data-model/chip 中,以恰当的命名新建集群定义 XML 文件。
  2. 将新集群定义引用登记到以下文件中:
    1. .github/workflows/tests.yaml

    2. scripts/rules.matterlint

    3. src/app/zap-templates/zcl/zcl-with-test-extensions.json

    4. src/app/zap-templates/zcl/zcl.json

    5. 若新集群是派生集群(derived cluster),需在基类集群定义中加入引用(例如在mode-base-cluster.xml中加入集群代码,否则运行regen_all.py时会抛出难以理解的异常):

      <struct name="ModeOptionStruct"> <cluster code="0x0051"/> <!-- Laundry Washer Mode --> <cluster code="YOUR NEW CLUSTER ID"/> </struct>
    6. src/controller/python/matter/clusters/init.py

  3. 在 src/controller/data_model/controller-clusters.zap 中启用新集群(作用于 Python 与 Android 客户端),需要借助 ZAP 工具编辑:
    • 若本机未安装 ZAP,可运行 zaptool 直接打开目标文件:

      ./scripts/tools/zap/run_zaptool.sh src/controller/data_model/controller-clusters.zap
    • GUI 操作步骤:

      1. 在左侧面板选择Endpoint-1
      2. 打开集群分组(例如Appliances);
      3. 找到要启用的集群(例如Dishwasher Control);
      4. 在 Enable 列的下拉框中为该集群选择Client
      5. File -> Save保存配置,然后关闭 GUI。
  4. 在 src/app/zap_cluster_list.json 的ClientDirectories一节添加条目。
  5. 更新 chip-tool
    • 重新生成全部 ZAP 生成代码:./scripts/tools/zap_regen_all.py(该脚本位于 scripts/tools/zap_regen_all.py);

    • 重新构建 chip-tool,即可获得新集群支持:

      ./scripts/examples/gn_build_example.sh examples/chip-tool SOME-PATH/

第二步:添加设备类型定义到 SDK

  1. 将设备 XML 定义加入 matter-devices.xml;

  2. 实现所有应用通用的入站命令行为:TLV 载荷到 C++ 结构体的解析由 XML 自动生成的代码完成(生成产物见 zzz_generated/app-common/app-common/zap-generated),其余功能需手动实现。重新生成自动代码的方法:

    • 全部重新生成:./scripts/tools/zap_regen_all.py

    • 仅重新生成 app-common 部分:

      ./scripts/tools/zap/generate.py -t src/app/common/templates/templates.json -o zzz_generated/app-common/app-common/zap-generated src/controller/data_model/controller-clusters.zap
  3. 为所有非全局属性(如CommandListAttributeList等全局属性无需实现)实现读写与存储操作。非 list/struct 类型属性的处理代码是通用生成的,通常无需额外工作;

  4. 实现所有应用通用的属性规范要求,例如特定范围(range)的强制约束、属性间交互的处理等。

第三步:实现代码与测试

  1. 新集群实现放在 src/app/clusters,编写规范见 docs/guides/writing_clusters.md;
  2. 测试放在 src/app/tests/suites;
  3. 实现示例集群服务器应用:
    • YAML 测试会针对该服务器运行;
    • 两个可选方案:
      1. all-clusters-app中启用新集群并作为示例服务器(可另附一个仅含相关集群的精简示例应用,属锦上添花);
      2. 若集群有复杂的全局应用需求,考虑独立示例应用(可参考 door lock、bridge、TV、OTA 集群的做法)。
mode-base 派生集群接入 all-clusters-app 的完整清单

假设新集群名为XYZMode(派生自mode-base):

  1. 在 src/app/zap-templates/zcl/data-model/chip 中创建精简的xyz-mode-cluster.xml(派生集群定义远小于普通集群,参考 dishwasher-mode-cluster.xml)。依据规范审视是否需要StartUpModeOnMode属性;
  2. 确认已在 mode-base-cluster.xml 中登记集群代码:
    • <struct name="ModeTagStruct"> <cluster code="0xXXXX">
    • <struct name="ModeOptionStruct"> <cluster code="0xXXXX">
    • <bitmap name="Feature" type="bitmap32"> <cluster code="0xXXXX">
  3. xyz-mode.h中定义模式/标签/tag,参考 dishwasher-mode.h;
  4. 添加实例化模式的 stub,参考 dishwasher-mode.cpp;
  5. 修改 examples/all-clusters-app/linux/main-common.cpp:
    • 添加#include "xyz-mode.h"
    • ApplicationShutdown()中调用Clusters::XYZMode::Shutdown();
  6. 在 examples/all-clusters-app/linux/BUILD.gn 的sources中加入xyz-mode.cpp
  7. 修改 src/app/common/templates/config-data.yaml:
    • EnumsNotUsedAsTypeInXML一节添加<XYZMode>::ModeTag(该节的作用是避免代码误以为可安全使用kUnknownEnumValue,派生集群尤其关键);
    • CommandHandlerInterfaceOnlyClusters一节添加XYZ Mode条目(该节用于声明完全由CommandHandlerInterface实现、无需生成命令分发的集群);
  8. 在 src/app/util/util.cpp 的// Cluster Init Functions...区域添加空实现void MatterXYZModePluginServerInitCallback() {}
  9. 修改 src/app/zap-templates/zcl/zcl-with-test-extensions.json:
    • xyz-mode-cluster.xml加入xmlFile列表;
    • attributeAccessInterfaceAttributes中添加"XYZ Mode": [ "SupportedModes", "CurrentMode", "FeatureMap" ]——这样 ZAP 就不会为这些属性生成处理代码;
  10. 修改 src/app/zap_cluster_list.json:
    • ClientDirectories对象中添加XYZ_MODE_CLUSTER: []
    • ServerDirectories对象中添加"XYZ_MODE_CLUSTER": ["mode-base-server"]

第四步:测试计划与 CI 集成

  1. 添加测试计划,使用模板:cluster_test_plan_template.adoc 所述流程对应的官方测试计划模板(位于 CHIP-Specifications/chip-test-plans 仓库);
  2. 注意:CHIP-Tool 参考客户端是从 XML 生成的(无需手写客户端代码);
  3. 按需添加测试:
    • 相对简单的测试:在 src/app/tests/suites/certification 添加 YAML 测试,并更新 src/app/tests/suites/certification/PICS.yaml;
    • 较复杂的测试:在 src/python_testing 添加 Python 测试;
    • 接入 CI:
      • Python 测试加入 .github/workflows/tests.yaml,需提供每个 Python 脚本的全部参数(如端点 PIXIT);
      • YAML 测试编辑 src/app/tests/suites/ciTests.json:创建"MyDevice"章节列出该设备的所有 YAML 测试,并将章节名加入"collection"列表,然后执行./scripts/tools/zap_regen_all.py重新生成 ZAP 代码;
  4. 将设备类型规范加入测试计划工具(device_type_requirements,供 src/app/tests/suites/certification/Test_TC_DESC_2_1.yaml 使用);
  5. 将设备类型加入 Chef:examples/chef/devices。

常见问题(Q&A)

  • Q1:测试活动可以用什么设备?一种实现可以是测试框架 + all-clusters 示例应用 + Raspberry Pi;两个独立实现需运行在目标硬件上(可以是 mock-up、原型机等)。
  • Q2:Chef 工具如何用于上述交付物?官方答复为 TBD(待定)。
  • Q3:如何使用 ZAP 自动生成代码并提交结果?搜索本文提到的zap_regen,运行后把变更文件全部加入 git 提交。
  • Q4:旧的集群定义在哪里?src/app/zap-templates/zcl/data-model/silabs/general.xml。
  • Q5:ZAP 文档在哪?ZAP 官方仓库的 README(外部链接,按需访问)。

面向可测试性与可移植性的集群设计

推荐的组合式实现(Combined Implementation)

新集群设计推荐组合式实现模式,以在保持完全可单元测试的同时优化 flash 与 RAM 占用:

  • 组合式实现(推荐):集群的逻辑、数据存储与ServerClusterInterface实现集中在一个类中,通常继承DefaultServerCluster。最小化样板代码与虚函数开销。
  • 模块化实现 /ClusterLogic(不推荐):把逻辑拆到独立的ClusterLogic类会带来明显的 flash 与 RAM 开销,新集群不建议采用。
  • 平台相关代码分离(按需):若集群需要触发平台/硬件特定动作,通过构造函数注入Delegate(或 Driver)接口。这既保证集群可移植,也便于在测试中 mock delegate。简单的上报型集群(如传感器读数)往往完全不需要 delegate——应用直接调用 setter 即可。

整体架构

Interaction Model --(Read/Write/Invoke)--> MyCluster --(可选 Hardware/Platform actions)--> Delegate
  • MyCluster实现ServerClusterInterface
  • Delegate为可选虚线连接,仅当需要平台动作时存在;
  • 组合式实现中DefaultServerCluster提供:
    • 数据版本(Data version)管理;
    • 路径管理(单端点/集群对);
    • 未处理属性/命令的默认状态响应;
    • ServerClusterContext的集成。

组合式集群实现示例

DataModel::ActionReturnStatus DiscoBallCluster::ReadAttribute(const DataModel::ReadAttributeRequest & request, AttributeValueEncoder & encoder) { switch (request.path.mAttributeId) { case Attributes::ClusterRevision::Id: return encoder.Encode(kRevision); case Attributes::Run::Id: return encoder.Encode(mRun); // ... } return Protocols::InteractionModel::Status::UnsupportedAttribute; }

Delegate / Driver 接口

Delegate 是可选组件,仅在集群需要触发平台/硬件特定动作时使用(例如驱动电机旋转、调节真实设备的亮度)。简单的上报集群(如传感器测量值)可完全跳过 delegate,由应用通过 setter 直接推送状态。

使用 delegate 时,通过构造函数注入以便在单元测试中 mock。集群在调用 delegate 之前,仍需完成所有规范定义的校验(如范围检查)。

class DiscoBallDelegate { public: virtual ~DiscoBallDelegate() = default; virtual void OnRunChanged(bool run) = 0; };

用 ClusterTester 为 Code-Driven 集群编写单元测试

单元测试应聚焦行为正确性与规范一致性。对于 code-driven 集群,ClusterTester辅助类是强制要求(mandatory)的测试组件。

ClusterTester 是什么

ClusterTester是位于 src/app/server-cluster/testing/ClusterTester.h 的 C++ 辅助类,为实现了ServerClusterInterface的 Matter 集群简化单元测试。其价值体现在:

  1. 自动 TLV 处理:写入/命令时自动把 C++ 结构编码为 TLV,读取/响应时自动把 TLV 解码为 C++ 结构;
  2. 内存安全:对于返回视图(view)的属性(如CharSpanList),ClusterTester持有底层 TLV 数据的所有权,测试作用域内可安全检视数据而无悬垂指针之忧;
  3. 类型安全:利用生成的数据模型类型(如Attributes::MyAttr::TypeInfo)确保参数与规范一致。

从源码看(ClusterTester.h),其核心 API 包括ReadAttributeWriteAttribute(含 list 专用重载,支持指定写入模式)与Invoke

基础用法:无 Fabric 上下文的集群

对于不需要 fabric 上下文(fabric context)的简单集群(如Boolean State),直接包装集群实例即可。

#include <app/server-cluster/testing/ClusterTester.h> #include <app/clusters/boolean-state-server/BooleanStateCluster.h> class TestBooleanStateCluster : public ::testing::Test { // ... (Setup memory and context) ... BooleanStateCluster booleanState{kRootEndpointId}; // 用集群实例初始化 tester chip::Testing::ClusterTester tester{booleanState}; void SetUp() override { booleanState.Startup(testContext.Get()); } };

读取属性:

TEST_F(TestBooleanStateCluster, ReadAttributeTest) { // 1. 读取简单整型属性 uint16_t revision{}; ASSERT_EQ(tester.ReadAttribute(Globals::Attributes::ClusterRevision::Id, revision), CHIP_NO_ERROR); // 2. 读取 bitmap uint32_t features{}; ASSERT_EQ(tester.ReadAttribute(FeatureMap::Id, features), CHIP_NO_ERROR); // 3. 读取布尔值 bool stateValue{}; ASSERT_EQ(tester.ReadAttribute(StateValue::Id, stateValue), CHIP_NO_ERROR); }

高级用法:Fabric 作用域集群

涉及访问控制(Access Control)或 fabric 作用域的复杂集群(如Group Key Management),ClusterTesterFabricTestFixture集成。不必把 fixture 传入 tester 构造器,但在执行操作前必须在 tester 上设置 active fabric index

class TestGroupKeyManagementCluster : public ::testing::Test { TestServerClusterContext mTestContext; FabricTestFixture fabricHelper{ &mTestContext.StorageDelegate() }; GroupKeyManagementCluster mCluster{ fabricHelper.GetFabricTable() }; // 仅用集群初始化 tester ClusterTester tester{ mCluster}; void SetUp() override { // ... (Startup cluster) ... // 初始化测试 fabric fabricHelper.SetUpTestFabric(kTestFabricIndex); // 重要:为 tester 设置 fabric index。 // 之后所有的写入/命令都会被当作来自该 fabric。 tester.SetFabricIndex(kTestFabricIndex); } };

写入复杂类型属性(Struct)

Tester 自动处理 Struct 等复杂类型(List 除外):

TEST_F(TestCameraAVStreamManagementCluster, TestReadWriteViewport) { ... // 写入一个合法的新值 Attributes::Viewport::TypeInfo::Type newViewport = { 0, 0, 1280, 720 }; EXPECT_EQ(mClusterTester.WriteAttribute(Attributes::Viewport::Id, newViewport), CHIP_NO_ERROR); }

写入 List 属性

list 属性存在两种主流写入模式,集群服务器通常都支持,因此测试应通过ListWritingPattern遍历两种模式以面向未来兼容:

TEST_F(TestUserLabelCluster, WriteValidLabelListTest) { // 每种 List 写入模式各跑一遍 for (ListWritingPattern listWritingPattern : { ListWritingPattern::ReplaceAll, ListWritingPattern::ClearAllThenAppendItems }) { ClusterTester tester(userLabel); Structs::LabelStruct::Type labels[] = { { .label = "room"_span, .value = "bedroom 2"_span }, { .label = "orientation"_span, .value = "North"_span }, }; // 包进 DataModel::List auto listToWrite = DataModel::List(labels); // 写入属性。 // 对 fabric 作用域属性,tester 自动处理 EncodeForWrite ASSERT_EQ(tester.WriteAttribute(LabelList::Id, listToWrite, listWritingPattern), CHIP_NO_ERROR); } }

调用命令

使用Invoke方法与生成的 Command Request 结构体交互:

TEST_F(TestGroupKeyManagementCluster, TestKeySetWriteCommand) { // 1. 构造 Request 结构体 GroupKeyManagement::Commands::KeySetWrite::Type requestData; requestData.groupKeySet.groupKeySetID = kTestKeySetId; requestData.groupKeySet.groupKeySecurityPolicy = GroupKeyManagement::GroupKeySecurityPolicyEnum::kTrustFirst; requestData.groupKeySet.epochStartTime0 = kStartTime; // ... 填充其余字段 ... // 2. 调用命令 // CommandId 由请求类型自动推导, // 也可以显式传入: tester.Invoke(CommandId, request) auto result = tester.Invoke(requestData); // 3. 校验结果 EXPECT_TRUE(result.IsSuccess()); }

关键行为与测试场景

完整类定义见 ClusterTester.h 头文件,以下为简化测试的若干内部行为:

  • 读操作的内存管理ReadAttribute自动管理底层 TLV 数据。含视图/列表的类型(CharSpanByteSpanDecodableList)会被 tester 在内部缓冲;该数据在ClusterTester实例生命周期内持续有效,可安全迭代列表或检视 span。
  • 写入的 Fabric 作用域处理WriteAttribute通过 C++ 内省(introspection)检测结构体是否为 fabric 作用域——若是则自动使用EncodeForWrite,否则使用标准Encode;同时自动应用与SetFabricIndex()对应 fabric 索引的访问控制主体描述符(subject descriptor)。
  • Invoke 的结果统一:返回InvokeResult<ResponseType>,同时携带状态与解码后的响应载荷(若命令返回数据)。IsSuccess()在状态为成功且(对返回数据的命令)响应存在时返回 true;GetStatusCode()返回std::optional<ClusterStatusCode>,可省去ASSERT_TRUE(status.has_value())直接以单个EXPECT_EQ()校验状态码;该 helper 面向同步执行的单元测试设计。
验证副作用(事件与上报)
  • 验证事件生成
// 1. 执行动作(如写属性) tester.WriteAttribute(MyAttribute::Id, newValue); // 2. 从队列弹出下一个事件 auto event = tester.GetNextGeneratedEvent(); ASSERT_TRUE(event.has_value()); // 3. 用规范生成的 decodable 类型解码事件载荷 MyCluster::Events::MyEvent::DecodableType eventData; ASSERT_EQ(event->GetEventData(eventData), CHIP_NO_ERROR); // ... 校验 eventData 字段 ...
  • 验证脏属性(Dirty Attributes)
// 1. 执行动作 tester.Invoke(myCommand); // 2. 检查特定属性是否被标记为 dirty EXPECT_TRUE(tester.IsAttributeDirty(MyAttribute::Id)); // 或者按需检视完整 dirty path 列表 auto & dirtyList = tester.GetDirtyList();

推荐测试清单

针对 code-driven 集群,至少覆盖以下测试类别:

  • 初始化Startup后初始属性值正确;
  • 范围检查:setter 与命令处理器对越界值返回错误(如ConstraintError);
  • 边界情况:最小值、最大值、可空(nullable)属性的 null 值行为;
  • 规范一致性:所有规范定义的错误条件均正确处理;
  • 副作用:状态变更时的事件生成与脏属性标记;
  • Delegate 交互:正确调用 delegate、正确处理 delegate 上报的错误;
  • Fabric 隔离:fabric 作用域属性与命令的正确行为。

测试既有集群的两个选项

  • 选项 1:重构(Refactor,推荐):将集群重构为使用DefaultServerClusterClusterTester,长期可维护性最佳;
  • 选项 2:接口边界测试(Test at Interface Boundary):实例化集群,通过ServerClusterInterface边界用ClusterTester交互。适合暂时无法完整重构的既有实现。

结语

至此,一条完整的新集群/设备类型开发流水线已经打通:从src/app/zap-templates/zcl/data-model/chip/下的 XML 规范翻译,到 ZAP/Ember/codegen 三层的注册与接线,再到AttributeAccessInterface/CommandHandlerInterface覆盖机制的精细控制,最后以DefaultServerCluster+ClusterTester的组合交付可单元测试、可移植、可认证的服务器实现。开发者可按 docs/cluster_and_device_type_dev/how_to_add_new_dts_and_clusters.md 的清单逐项落地,并以 docs/cluster_and_device_type_dev/unit_testing_clusters.md 与 docs/cluster_and_device_type_dev/cluster_tester.md 作为测试环节的规范依据,最终将新功能安全地带入 Matter 生态。

【免费下载链接】connectedhomeipMatter (formerly Project CHIP) creates more connections between more objects, simplifying development for manufacturers and increasing compatibility for consumers, guided by the Connectivity Standards Alliance.项目地址: https://gitcode.com/GitHub_Trending/co/connectedhomeip

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询