
Matter Power Source 集群在 CodeDriven 数据模型下的模板化实现与集成指南【免费下载链接】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本文以 connectedhomeipMatter SDK仓库中 Power Source 集群服务端实现 的官方文档为骨架深入讲解如何在基于CodeDrivenDataModelProvider的 code-driven代码驱动数据模型下手动创建并注册 Power Source 集群实例。文章覆盖模板特化的设计动机、有线电源/电池电源的配置示例、usedOptionalAttributes的用法、预定义特化类型以及源码级原理与测试验证帮助开发者在带 Flash 容量限制的嵌入式设备上以最小类型代价实现符合 Matter 规范的电源状态上报能力。集群职责与实现边界Power Source 集群Cluster ID 见 codegen 头文件 中的PowerSource::Id引用用于监控并上报某个电源的状态电源可以是**电池battery或有线电源wired**两类之一。本目录下的 PowerSourceCluster.h 提供了该集群在 code-driven 数据模型下的一套全新实现。这套实现刻意做了以下职责切分需要集成方明确Order属性不做持久化Order是只读ReadOnly但标记为 Persistent 的属性只有集群的直接使用者即设备应用本身能修改它因此持久化责任交由集成方承担。源码中SetOrder()仅调用SetAttributeValue更新内存值并发出变更通知PowerSourceCluster.h并注释说明了这一设计取舍。固定字符串属性与endpointList不做存储Description、BatReplacementDescription等 CharSpan 字符串属性以及EndpointList属性由调用方持有底层存储SetEndpointList()按视图view接收数据不拷贝内容PowerSourceCluster.h。属性变更通知完全按规范执行哪些属性在变化时触发NotifyAttributeChanged、哪些被标记为Changes Omitted不触发通知、哪些需要经定时器去抖后通知均由实现按照 Matter 规范精确处理详见后文源码级原理。对比参考目录下还保留了基于 ZAP 代码生成体系的旧实现 codegen/power-source-server.hPowerSourceServer/PowerSourceAttrAccess负责端点列表存储等新实现与旧实现相互独立本文只讨论 code-driven 新实现。使用方式手动创建 注册到 CodeDriven 数据模型与 ZAP 自动实例化不同code-driven 模型下的集群必须手动创建并直接注册到CodeDrivenDataModelProvider。注册入口有两类定义于 SingleEndpointServerClusterRegistry.hRegisteredServerClusterClusterType立即构造实例生命周期由注册者管理LazyRegisteredServerClusterClusterType延迟创建整体提供动态生命周期管理适合按需挂载/卸载。两者的Registration()返回值交给CodeDrivenDataModelProvider::Instance().Registry().Register(...)注册成功的同时会调用集群的Startup()。注册表要求同一注册条目的路径必须属于同一端点并提供ClustersOnEndpoint()、UnregisterAllFromEndpoint()等辅助方法。工作原理为 Flash 而生的模板化设计由于嵌入式设备 Flash 空间紧张这套实现把集群做成了基于特性集feature set与属性集attribute set的模板。模板参数supportedFeatureBits与supportedOptionalAttributeBits会在编译期决定类的成员布局见 Modules.hMandatoryModule、WiredMandatoryModule、BatteryMandatoryModule、ReplaceableMandatoryModule、RechargeableMandatoryModule及各可选属性模块均以template bool used特化usedfalse时对应成员不占任何空间属性读取/写入的代码路径ReadAttribute中的if constexpr分支见 PowerSourceCluster.h对外暴露的 Getter/Setter 接口集合。模板内建静态断言与约束PowerSourceCluster.hkWired与kBattery必须且只能启用一个static_assert因此一套特化无法同时表达有线电池需要混合供电时必须写两套特化见下文示例三调用未启用属性对应的函数会直接编译报错ENABLE_IF_ATTRIBUTE_SUPPORTED宏内部同时使用static_assert和if constexprPowerSourceCluster.h把用错属性从运行时错误提前为编译期错误。另外一个关键函数是PowerSource::detail::GetValidOptionalAttributeSetPowerSourceCluster.h它根据启用的 feature 位自动把对应特性的强制mandatory属性并入支持集、把其它特性专属的可选属性从支持集剔除确保最终暴露给数据模型的属性列表严格符合规范语义。示例一一个带可选属性的有线电源目标有线电源除强制属性外还支持WiredAssessedInputVoltage实测输入电压与WiredNominalVoltage标称电压两个可选属性。首先引入头文件并定义端点号#include app/clusters/power-source-server/PowerSourceCluster.h #include app/server-cluster/SingleEndpointServerClusterRegistry.h #include data-model-providers/codedriven/CodeDrivenDataModelProvider.h using namespace app; using namespace app::Clusters; constexpr EndpointId powerSourceEndpointId 1;定义模板特化。文档给出了两种等效写法更正确的写法与简洁写法// 写法一逐位开启可选属性 constexpr uint32_t GetOptionalAttributeBits() { PowerSource::PowerSourceOptionalAttributeSet optAttributes{}; optAttributes.SetPowerSource::Attributes::WiredAssessedInputVoltage::Id(); optAttributes.SetPowerSource::Attributes::WiredNominalVoltage::Id(); return optAttributes.Raw(); } constexpr uint32_t optionalAttributeBits GetOptionalAttributeBits(); // 写法二直接列出属性 ID constexpr uint32_t optionalAttributeBits OptionalAttributeSet PowerSource::Attributes::WiredAssessedInputVoltage::Id, PowerSource::Attributes::WiredNominalVoltage::Id::All(); constexpr uint32_t featureBits BitFlagsPowerSource::Feature(PowerSource::Feature::kWired).Raw(); using MyWiredPowerSourceCluster PowerSourceClusterfeatureBits, optionalAttributeBits;创建配置对象、集群实例并注册使用// 配置有线电源构造器参数为 (描述, 电流类型) MyWiredPowerSourceCluster::Config config(Wired Power Source (description)_span, PowerSource::WiredCurrentTypeEnum::kDc); config.wiredNominalVoltage 23000; // 23V // 集群实例立即创建 RegisteredServerClusterMyWiredPowerSourceCluster powerSourceInstance(powerSourceEndpointId, config); // 注册注册过程会调用 Startup CodeDrivenDataModelProvider::Instance().Registry().Register(powerSourceInstance.Registration()); // 使用更新实测输入电压 powerSourceInstance.Cluster().SetWiredAssessedInputVoltage(20000); // 20V电压/电流单位约定wiredNominalVoltage、WiredAssessedInputVoltage以毫伏mV为单位23000 即 23V这一点在PowerSourceClusterConfig中wiredNominalVoltage成员uint32_tModules.h与SetWiredAssessedInputVoltage(DataModel::Nullableuint32_t)上保持一致。注意调用与所启用属性不符的函数会在编译期报错。例如本特化未启用BatPercentRemaining任何尝试调用电池相关 setter 的代码都无法通过编译。示例二三个电池普通 / 可充电 / 可更换共用一套特化需求同一设备上有三个电池电源端点——普通电池只用BatPercentRemaining可充电电池用BatPercentRemaining、BatChargingCurrent可更换电池用BatTimeRemaining、BatCapacity。强烈建议为三者共用同一套模板特化编译期类型只有一个再通过配置对象区分实际能力#include app/clusters/power-source-server/PowerSourceCluster.h #include app/server-cluster/SingleEndpointServerClusterRegistry.h #include platform/DefaultTimerDelegate.h #include data-model-providers/codedriven/CodeDrivenDataModelProvider.h using namespace app; using namespace app::Clusters; constexpr EndpointId powerSourceEndpointId1 1; constexpr EndpointId powerSourceEndpointId2 2; constexpr EndpointId powerSourceEndpointId3 3; DefaultTimerDelegate timerDelegate;电池集群需要TimerDelegate用于安静通知定时见后文因此额外引入platform/DefaultTimerDelegate.h。constexpr uint32_t GetOptionalAttributeBits() { PowerSource::PowerSourceOptionalAttributeSet optAttributes{}; // 把三个配置用到的全部可选属性都打开 optAttributes.SetPowerSource::Attributes::BatPercentRemaining::Id(); optAttributes.SetPowerSource::Attributes::BatTimeRemaining::Id(); optAttributes.SetPowerSource::Attributes::BatChargingCurrent::Id(); optAttributes.SetPowerSource::Attributes::BatCapacity::Id(); return optAttributes.Raw(); } constexpr uint32_t optionalAttributeBits GetOptionalAttributeBits(); // 把所有用到的 feature 全部打开 constexpr uint32_t featureBits BitFlagsPowerSource::Feature( PowerSource::Feature::kBattery, PowerSource::Feature::kRechargeable, PowerSource::Feature::kReplaceable ).Raw(); using MyBatteryPowerSourceCluster PowerSourceClusterfeatureBits, optionalAttributeBits;三个实例在可调用的函数与配置字段层面完全相同但可以通过配置字段usedOptionalAttributes精确告知数据模型本实例实际支持哪些属性。即便模板在技术上具备支持某属性的能力只要厂商在该端点不支持数据模型就会将其视为不支持。usedOptionalAttributes的默认值为UINT32_MAX即默认全部已启用属性都被视为已使用见 PowerSourceCluster.h并且Attributes()在生成属性列表时会执行GetValidOptionalAttributeSet(usedOptionalAttributes, ...) supportedOptionalAttributeSet双重过滤PowerSourceCluster.h。// 各配置对应的属性集合 PowerSource::PowerSourceOptionalAttributeSet attrSet1{}; attrSet1.SetPowerSource::Attributes::BatPercentRemaining::Id(); PowerSource::PowerSourceOptionalAttributeSet attrSet2{}; attrSet2.SetPowerSource::Attributes::BatPercentRemaining::Id(); attrSet2.SetPowerSource::Attributes::BatChargingCurrent::Id(); PowerSource::PowerSourceOptionalAttributeSet attrSet3{}; attrSet3.SetPowerSource::Attributes::BatTimeRemaining::Id(); attrSet3.SetPowerSource::Attributes::BatCapacity::Id(); // 配置一普通电池描述, 可更换性, timerDelegate MyBatteryPowerSourceCluster::Config config1(Simple battery cluster_span, PowerSource::BatReplaceabilityEnum::kUnspecified, timerDelegate); config1.batPercentRemaining 200; // 200 表示 100%百分比翻倍编码 config1.usedOptionalAttributes attrSet1; // 配置二可充电电池 MyBatteryPowerSourceCluster::Config config2(Rechargeable battery cluster_span, PowerSource::BatReplaceabilityEnum::kNotReplaceable, timerDelegate); config2.MakeRechargeable(); // 否则对数据模型而言与普通集群无异 config2.batPercentRemaining 200; config2.batChargingCurrent 5000; // 5A config2.usedOptionalAttributes attrSet2; // 配置三可更换电池 MyBatteryPowerSourceCluster::Config config3(Replaceable battery cluster_span, PowerSource::BatReplaceabilityEnum::kUserReplaceable, timerDelegate); config3.MakeReplaceable(Description for replacement_span, /* quantity */ 1); config3.batTimeRemaining 3600; // 预计续航 1 小时 config3.batCapacity 2000; // 容量 2000 mAh config3.usedOptionalAttributes attrSet3; // 集群实例 RegisteredServerClusterMyBatteryPowerSourceCluster simpleBatteryInstance(powerSourceEndpointId1, config1); RegisteredServerClusterMyBatteryPowerSourceCluster rechargeableBatteryInstance(powerSourceEndpointId2, config2); RegisteredServerClusterMyBatteryPowerSourceCluster replaceableBatteryInstance(powerSourceEndpointId3, config3); // 逐个注册均会触发各自 Startup CodeDrivenDataModelProvider::Instance().Registry().Register(simpleBatteryInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(rechargeableBatteryInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(replaceableBatteryInstance.Registration()); // 使用上报电池需要更换 replaceableBatteryInstance.Cluster().SetBatReplacementNeeded(true);几个关键配置点说明MakeRechargeable()/MakeReplaceable()会真正影响FeatureMap 的运行时取值Features()根据配置中的batRechargeable/batReplaceable标志动态构造PowerSourceCluster.h这比写死在模板参数里更灵活——同一模板既可用于普通电池也可用于可充电电池batPercentRemaining编码为百分比×2最大值 200即 100%。SetBatPercentRemaining()对超过 200 的取值返回CHIP_IM_GLOBAL_STATUS(ConstraintError)PowerSourceCluster.hbatChargingCurrent单位毫安5000 5A、batCapacity单位毫安时mAh、batTimeRemaining单位秒。示例三有线 电池混合需要两套特化当设备同时拥有有线电源与电池电源时按设计无法用一套模板特化表达因为kWired与kBattery互斥必须声明两个特化类型// 有线特化仅 kWired无可选属性 using MyWiredPowerSourceCluster PowerSourceClusterBitFlagsPowerSource::Feature(PowerSource::Feature::kWired).Raw(), 0 /* no optional attributes */; // 电池特化仅 kBattery无可选属性 using MyBatteryPowerSourceCluster PowerSourceClusterBitFlagsPowerSource::Feature(PowerSource::Feature::kBattery).Raw(), 0 /* no optional attributes */;创建两个配置与两个实例并注册// 有线配置初始状态设为 Unavailable MyWiredPowerSourceCluster::Config config1(Wired cluster_span, PowerSource::WiredCurrentTypeEnum::kAc); config1.status PowerSource::PowerSourceStatusEnum::kUnavailable; // 电池配置 MyBatteryPowerSourceCluster::Config config2(Simple battery cluster_span, PowerSource::BatReplaceabilityEnum::kNotReplaceable, timerDelegate); // 集群实例 RegisteredServerClusterMyWiredPowerSourceCluster wiredInstance(powerSourceEndpointId1, config1); RegisteredServerClusterMyBatteryPowerSourceCluster batteryInstance(powerSourceEndpointId2, config2); // 注册 CodeDrivenDataModelProvider::Instance().Registry().Register(wiredInstance.Registration()); CodeDrivenDataModelProvider::Instance().Registry().Register(batteryInstance.Registration()); // 使用 wiredInstance.Cluster().SetOrder(2);注意config1.status在配置阶段直接赋值——Status是强制属性且拥有运行时 setterSetStatus()会拒绝kUnknownEnumValue见 PowerSourceCluster.h两种途径均可更新。预定义特化NamedPowerSourceClusters.h为方便使用NamedPowerSourceClusters.h 提供了 4 个现成命名特化类型启用 Feature可选属性适用场景MinimalWiredPowerSourceClusterkWired无0最小有线电源仅有强制属性MinimalBatteryPowerSourceClusterkBattery无0最小电池电源FullWiredPowerSourceClusterkWired全部UINT32_MAX支持所有有线可选属性FullBatteryPowerSourceClusterkBatterykReplaceablekRechargeable全部UINT32_MAX支持所有电池可选属性且同时可更换、可充电每个类型都配套同名*Config如FullWiredPowerSourceConfig用法与自定义特化完全一致。这些预定义类型同时被测试代码复用见 PowerSourceClusterTestCommon.h 对NamedPowerSourceClusters.h的引用。如果对 Flash 没有限制可以对所有电源一律使用Full*PowerSourceCluster——模板化设计的全部意义就是为所需功能生成尽可能小的类型牺牲类型大小即可换取最全能力。源码级原理与行为细节强制属性与 Fixed 属性每个 Power Source 集群实例都必然暴露以下强制属性Status、Order、Description、EndpointList有线/电池各自还会按 feature 增加强制属性如WiredCurrentType、BatChargeLevel、BatReplacementNeeded、BatReplaceability等。测试用例 TestMinimalWiredPowerSourceCluster.cpp 验证了最小有线集群的属性列表恰好为{ Status, Order, Description, WiredCurrentType, EndpointList }。标记为Fixed的属性只能在构造Config 对象时设置没有 setter包括Description、WiredCurrentType、WiredNominalVoltage、WiredMaximumCurrent、BatReplaceability、BatReplacementDescription、BatCommonDesignation、BatANSIDesignation、BatIECDesignation、BatApprovedChemistry、BatCapacity、BatQuantity源码注释见 PowerSourceCluster.h。这一点与 README 中的说明一致部分属性只能在 config 对象中设置因为它们是 Fixed 属性。通知Notification语义普通属性SetStatus、SetOrder、SetWiredPresent、SetBatChargeLevel、SetBatReplacementNeeded、SetBatPresent、SetBatChargeState、SetBatFunctionalWhileCharging等会立即触发NotifyAttributeChangedChanges Omitted 属性WiredAssessedInputVoltage、WiredAssessedInputFrequency、WiredAssessedCurrent、BatVoltage、BatChargingCurrent的 setter 仅更新内部值、不发送通知源码注释明确标注 no notifying because attribute marked with Changes Omitted quality安静通知quiet notificationBatPercentRemaining、BatTimeRemaining、BatTimeToFullCharge这类易频繁变化的可空属性走SetQuietNullableAttribute()路径PowerSourceCluster.h——非空值变化时通过BatteryTimerContext挂起一个默认 10 秒kNotifyTimerDuration System::Clock::Seconds16(10)见 PowerSourceCluster.h的去抖定时器合并短时间内的多次变化一旦值变为null则取消定时器并立即通知保证由空转非空的下一跳变化也能被及时上报故障事件调用SetActiveWiredFaults/AddActiveWiredFault/RemoveActiveWiredFault以及对应的ActiveBatFaults、ActiveBatChargeFaults时若位集发生变化会通过mContext-interactionContext.eventsGenerator.GenerateEvent生成WiredFaultChange/BatFaultChange/BatChargeFaultChange事件同时更新属性并通知PowerSourceCluster.h。端点列表EndpointListEndpointList属性由GetEndpointList()读取、SetEndpointList()写入PowerSourceCluster.h。SetEndpointList按引用接收Spanconst EndpointId不拷贝数据调用方必须保证底层存储的生命周期直到下一次成功调用为止若新列表内容与旧列表不同会触发一次属性变更通知任何元素为kInvalidEndpointId则返回CHIP_ERROR_INVALID_ARGUMENT且不更新内部指针。变更通知的触发对象集群通过实现PowerSource::detail::BatteryTimerContext::NotifierDelegate::Notify()PowerSourceCluster.h把定时器到期的通知转发为NotifyAttributeChanged(id)从而以规范要求的方式通知数据模型属性已变化。测试与构建单元测试位于 src/app/clusters/power-source-server/tests针对 4 个预定义特化各有一个测试文件TestFullBatteryPowerSourceCluster.cpp、TestFullWiredPowerSourceCluster.cpp、TestMinimalBatteryPowerSourceCluster.cpp、TestMinimalWiredPowerSourceCluster.cpp测试覆盖属性列表、属性读取ReadAttribute、Getter/Setter、字符串属性长度边界TestStringAttributeReadLength见 PowerSourceClusterTestCommon.h等维度可作为集成行为的参考用例。构建目标为power-source-server源集source_set见 BUILD.gn其依赖包括data-model-provider、server-cluster、lib/support以及zzz_generated/app-common/clusters/PowerSource集群属性/枚举/元数据代码生成物。CMake 侧通过 app_config_dependent_sources.cmake 与 GN 侧的 app_config_dependent_sources.gni 接入应用构建。集成清单速查按供电类型确定 feature 位有线用kWired电池可选加kReplaceable/kRechargeable按实际需要打开可选属性位形成模板特化类型PowerSourceClusterfeatureBits, optionalAttributeBits创建ClusterType::Config有线用(desc, WiredCurrentTypeEnum)电池用(desc, BatReplaceabilityEnum, timerDelegate)设置 Fixed 属性与usedOptionalAttributes必要时调用MakeReplaceable()/MakeRechargeable()用RegisteredServerCluster或LazyRegisteredServerCluster包一层并注册到CodeDrivenDataModelProvider::Instance().Registry()通过instance.Cluster().SetXxx(...)上报运行状态无 Flash 约束时可直接使用Full*PowerSourceCluster预定义类型。【免费下载链接】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),仅供参考