ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Matter SDK 设备信息提供者(DeviceInfoProvider)示例实现解析:FixedLabel、UserLabel、区域设置与日历类型

Matter SDK 设备信息提供者(DeviceInfoProvider)示例实现解析:FixedLabel、UserLabel、区域设置与日历类型 Matter SDK 设备信息提供者DeviceInfoProvider示例实现解析FixedLabel、UserLabel、区域设置与日历类型【免费下载链接】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导读本篇文章围绕 examples/providers/README.md 展开深入解析 MatterConnected Home over IPCHIPSDK 中应用专用 Providerprovider的示例实现机制。文章以 Device Info Provider 为主线完整介绍其职责边界、两个示例实现空实现版与 All-Clusters 常量版的代码结构与设计意图、四个迭代器接口FixedLabel / UserLabel / SupportedLocales / SupportedCalendarTypes的底层语义、持久化存储实现以及如何在真实应用如 all-clusters-app中注册使用。读完本文你将掌握如何为自己的 Matter 产品自定义设备信息提供者并理解 Matter 1.5 之后对示例默认值采取的安全拒绝策略。1. Provider 目录的定位应用专用的可替换实现examples/providers/README.md 开篇就明确了该目录的核心定位This folder contains example implementations of providers that are generally application-specific. Final applications should generally have their own implementations of these.即examples/providers/存放的是**应用专用application-specific**的 Provider 示例实现。所谓 Provider在 Matter SDK 中指的是向协议栈上层尤其是各类 Cluster提供业务数据的一组可插拔接口。最终产品不应直接复用这些示例代码而应基于它们派生并定制自己的实现。从目录结构看该目录共包含 6 个文件文件作用DeviceInfoProviderImpl.h / .cpp空实现版 Device Info Provider安全默认AllClustersExampleDeviceInfoProviderImpl.h / .cpp带常量数据的 All-Clusters 示例版BUILD.gn两个静态库目标的 GN 构建配置README.md目录说明本文的关联文档其中 README 明确点名的Device Info Provider是目录中唯一被详细介绍的主题其职责是Supports device-specific information like labels, locales and calendar types.即向设备端 Cluster 提供**标签labels、区域设置locales和日历类型calendar types**等设备特有信息。这三类数据分别对应 Matter 规范中的 Fixed Label、User Label、Localization Configuration 和 Time Format Localization 等 Cluster。2. 核心接口DeviceInfoProvider 抽象基类在深入示例实现之前需要先理解 SDK 定义的抽象基类 src/include/platform/DeviceInfoProvider.h。它是所有设备信息 Provider 的公共契约位于chip::DeviceLayer命名空间下。2.1 四个纯虚迭代器接口基类定义了四个必须实现的纯虚迭代器 0分别覆盖 README 中提到的 labels、locales、calendar types纯虚方法返回迭代器类型数据来源 Cluster元素类型IterateFixedLabel(EndpointId)FixedLabelIterator *Fixed Label固定标签FixedLabelTypeIterateUserLabel(EndpointId)UserLabelIterator *User Label用户标签UserLabelTypeIterateSupportedLocales()SupportedLocalesIterator *Localization Configuration区域配置CharSpanIterateSupportedCalendarTypes()SupportedCalendarTypesIterator *Time Format Localization时间格式本地化CalendarType所有迭代器都基于同一个模板IteratorT定义其协议非常简单template typename T class Iterator { public: virtual size_t Count() 0; // 返回总共将迭代的条目数 virtual bool Next(T item) 0; // 成功取回下一个元素返回 true无更多条目返回 false virtual void Release() 0; // 释放迭代器分配的内存指针离开作用域前必须调用 };其中元素类型在基类中做了别名绑定using FixedLabelType app::Clusters::FixedLabel::Structs::LabelStruct::Type; using UserLabelType app::Clusters::UserLabel::Structs::LabelStruct::Type; using CalendarType app::Clusters::TimeFormatLocalization::CalendarTypeEnum;2.2 平台侧定义的常量约束基类头部同时定义了 Provider 实现需要遵守的容量上限src/include/platform/DeviceInfoProvider.hstatic constexpr size_t kMaxUserLabelListLength 4; // 每个 Endpoint 的用户标签列表最大 4 条 static constexpr size_t kMaxLabelNameLength 16; // 标签名最大 16 字节 static constexpr size_t kMaxLabelValueLength 16; // 标签值最大 16 字节 static constexpr size_t kMaxActiveLocaleLength 35; // 活动区域设置串最大 35 字节这些常量直接决定了示例实现中内部缓冲区的尺寸详见第 4 节。2.3 User Label 的写操作与全局 Provider 注册除了读侧迭代器基类还向派生类暴露了一组protected 虚函数用于对 User Label 列表做持久化读写SetUserLabelAt(endpoint, index, userLabel)设置指定索引处的标签DeleteUserLabelAt(endpoint, index)删除指定索引处的标签SetUserLabelLength(endpoint, val)/GetUserLabelLength(endpoint, val)读写标签列表长度。公共的列表级操作SetUserLabelList、ClearUserLabelList、AppendUserLabel则由基类在 src/platform/DeviceInfoProvider.cpp 中基于上述原语组合实现例如AppendUserLabel会先读取当前长度、校验不超过kMaxUserLabelListLength否则返回CHIP_ERROR_NO_MEMORY再依次写入长度与新标签。全局访问通过GetDeviceInfoProvider()/SetDeviceInfoProvider(DeviceInfoProvider *)完成两者维护一个进程级单例指针src/platform/DeviceInfoProvider.cpp。3. 两个示例实现的设计意图空实现 vs 常量数据目录中提供了两套风格截然不同的实现体现了不同的使用场景DeviceInfoProviderImpl空实现所有迭代器返回空数据是Matter 1.5 之后引入的安全默认AllClustersExampleDeviceInfoProviderImpl常量实现填充硬编码的示例值专供 all-clusters 类测试应用使用。3.1 空实现版防止坏数据泄漏进产品在 DeviceInfoProviderImpl.h 的头部源码用四个感叹号标注了非常醒目的 WARNINGDO NOT USE THESE DEFAULT IMPLEMENTATIONS WITH DEFAULT VALUES IN PRODUCTION PRODUCTS WITHOUT AUDITING THEM! ... Here, all providers have empty implementations to force empty lists which prevent bad values from leaking into products like happened before Matter 1.5.这段话传递了两个关键信息历史教训Matter 1.5 之前示例默认值曾直接泄漏到产品中设计对策此后默认示例强制返回空列表如果产品确实要用 FixedLabel、LocalizationConfiguration、Time Format Localization 等 Cluster必须重新实现 Provider且填入的值必须经过正确性审查。具体到代码DeviceInfoProviderImpl.cppDeviceInfoProvider::FixedLabelIterator * DeviceInfoProviderImpl::IterateFixedLabel(EndpointId endpoint) { // We dont include fixed label data in this sample one. Returning nullptr returns empty list. (void) endpoint; return nullptr; }IterateFixedLabel直接返回nullptr按照基类文档约定returns nullptr if no iterator instances are available调用方会将其解释为空列表。3.2 常量实现版面向 all-clusters 测试AllClustersExampleDeviceInfoProviderImpl则为测试目的填充了经过审查的常量值其头部警告措辞相对缓和但也明确指出FixedLabel cluster, if used, should have values that have been vetted for correctness in the productAllClustersExampleDeviceInfoProviderImpl.cpp。两套实现的差异汇总维度DeviceInfoProviderImplAllClustersExampleDeviceInfoProviderImplFixedLabel返回nullptr空列表返回 1 条硬编码标签directionup/downUserLabel持久化存储读写持久化存储读写SupportedLocales2 个en-US、en-GB8 个en-US、de-DE、fr-FR、en-GB、es-ES、zh-CN、it-IT、ja-JPSupportedCalendarTypes1 个kGregorian1 个kGregorian4. 源码级剖析四个迭代器的完整实现下面以AllClustersExampleDeviceInfoProviderImpl为主要样本逐个剖析四个迭代器的实现空实现版逻辑相同只是数据量为 0 或nullptr。4.1 FixedLabelIteratorImpl按 Endpoint 区分的固定标签DeviceInfoProvider::FixedLabelIterator * AllClustersExampleDeviceInfoProviderImpl::IterateFixedLabel(EndpointId endpoint) { return chip::Platform::NewFixedLabelIteratorImpl(endpoint); }迭代器在堆上通过chip::Platform::New分配Release()时通过chip::Platform::Delete(this)释放这与基类必须调用 Release() 释放内存的契约一致。FixedLabelIteratorImpl::Next()的实现展示了按 Endpoint 返回不同值的手法AllClustersExampleDeviceInfoProviderImpl.cppcase 0: output.label direction_span; if (mEndpoint 1) { output.value up_span; } else { output.value down_span; } break;即固定标签名direction对 Endpoint 1 返回up其余 Endpoint 返回down。源码注释还提醒真实产品中这些值很可能需要按 Endpoint 动态生成此时应把 span 指向正确的 buffersize而非直接使用const char*支撑的CharSpan。4.2 UserLabelIteratorImpl基于持久化存储的读写User Label 与前两者最大的不同是可写——用户或控制器可以通过 Cluster 命令增删标签。因此示例实现将其落到PersistentStorageDelegate上。写入侧SetUserLabelAt把标签序列化为 TLV 结构后按 key 存储AllClustersExampleDeviceInfoProviderImpl.cppuint8_t buf[UserLabelTLVMaxSize()]; TLV::TLVWriter writer; writer.Init(buf); TLV::TLVType outerType; ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::kTLVType_Structure, outerType)); ReturnErrorOnFailure(writer.PutString(kLabelNameTag, userLabel.label)); // ContextTag(0) ReturnErrorOnFailure(writer.PutString(kLabelValueTag, userLabel.value)); // ContextTag(1) ReturnErrorOnFailure(writer.EndContainer(outerType)); return mStorage-SyncSetKeyValue( DefaultStorageKeyAllocator::UserLabelIndexKey(endpoint, static_castuint32_t(index)).KeyName(), buf, static_castuint16_t(writer.GetLengthWritten()));其中kLabelNameTag/kLabelValueTag分别是TLV::ContextTag(0)/ContextTag(1)缓冲区大小由UserLabelTLVMaxSize()决定即TLV::EstimateStructOverhead(kMaxLabelNameLength, kMaxLabelValueLength)16 字节 name 16 字节 value TLV 结构开销存储 key 由DefaultStorageKeyAllocator::UserLabelLengthKey(endpoint)长度与UserLabelIndexKey(endpoint, index)条目统一生成。读取侧UserLabelIteratorImpl::Next()先从存储读出 TLV 字节再用TLV::ContiguousBufferTLVReader逐字段解析最终把 label/value 拷贝进内部缓冲后以CharSpan返回Platform::CopyString(mUserLabelNameBuf, label); Platform::CopyString(mUserLabelValueBuf, value); output.label CharSpan::fromCharString(mUserLabelNameBuf); output.value CharSpan::fromCharString(mUserLabelValueBuf);迭代器的Count()与Next()均基于构造时读到的列表长度mTotal工作超出范围即返回false结束迭代。两个示例实现空实现版与常量版的 User Label 读写逻辑完全一致因为这一部分与示例数据无关属于通用存储逻辑。4.3 SupportedLocalesIteratorImpl区域设置清单空实现版硬编码 2 个区域DeviceInfoProviderImpl.cppstatic const char * kAllSupportedLocales[kNumSupportedLocales] { en-US, en-GB };常量实现版扩展为 8 个常用区域AllClustersExampleDeviceInfoProviderImpl.cppstatic const char * kAllSupportedLocales[kNumSupportedLocales] { en-US, de-DE, fr-FR, en-GB, es-ES, zh-CN, it-IT, ja-JP };区域字符串是 BCP-47 风格的语言-国家/地区格式直接对应 Localization Configuration Cluster 中SupportedLocales属性的枚举值。4.4 SupportedCalendarTypesIteratorImpl日历类型清单两套实现都只支持一种日历——公历static const CalendarType kAllSupportedCalendarTypes[kNumSupportedCalendarTypes] { app::Clusters::TimeFormatLocalization::CalendarTypeEnum::kGregorian };该枚举值来自TimeFormatLocalizationCluster其迭代器元素类型是CalendarType枚举与区域设置迭代器返回的CharSpan不同。5. 构建配置两个静态库目标examples/providers/BUILD.gn 将两套实现分别封装为静态库static_library(device_info_provider_please_do_not_reuse_as_is) { output_name libMatterDeviceInfoProviderExample sources [ DeviceInfoProviderImpl.cpp, DeviceInfoProviderImpl.h, ] public_deps [ ${chip_root}/src/lib/support, ${chip_root}/src/platform, ] public_configs [ :include_providers_dir ] } static_library(all_clusters_device_info_provider) { output_name libMatterAllClustersDeviceInfoProviderExample sources [ AllClustersExampleDeviceInfoProviderImpl.cpp, AllClustersExampleDeviceInfoProviderImpl.h, ] public_deps [ ${chip_root}/src/lib/support, ${chip_root}/src/platform, ] public_configs [ :include_providers_dir ] }值得注意的命名细节第一个目标名device_info_provider_please_do_not_reuse_as_is本身就携带了请勿照搬复用的语义与源码头部的 WARNING 相呼应include_providers_dirconfig 同时把examples根目录加入 include 路径因此下游可以通过#include providers/AllClustersExampleDeviceInfoProviderImpl.h这种形如providers/...的头文件引用方式引入实现。两个库都依赖${chip_root}/src/lib/support与${chip_root}/src/platform因为它们使用了DefaultStorageKeyAllocator、TLV、Platform内存管理等 SDK 基础设施。6. 在真实应用中注册使用all-clusters-app 实例示例 Provider 在 all-clusters-app 的 fuzzing 入口中有完整使用示范examples/all-clusters-app/linux/fuzzing-main.cpp#include providers/AllClustersExampleDeviceInfoProviderImpl.h // ... chip::DeviceLayer::AllClustersExampleDeviceInfoProviderImpl gAllClustersExampleDeviceInfoProvider; // ... DeviceLayer::SetDeviceInfoProvider(gAllClustersExampleDeviceInfoProvider);完整的接入流程是实例化在全局或初始化阶段构造 Provider 子类对象设置存储委托调用SetStorageDelegate(PersistentStorageDelegate *)注入非易失存储基类SetStorageDelegate对空指针会VerifyOrDiesrc/platform/DeviceInfoProvider.cppUser Label 的持久化读写依赖该存储注册全局实例调用SetDeviceInfoProvider(provider)挂入DeviceLayer的全局指针协议栈使用此后 Fixed Label、User Label、Localization Configuration、Time Format Localization 等 Cluster 的数据读写都会路由到该 Provider。这四条步骤同样适用于任何自定义 Provider继承DeviceInfoProvider→ 实现四个迭代器与四个 User Label 原语 → 注入存储 → 全局注册。7. 面向产品开发者的最佳实践综合 README 的定位说明与源码中的 WARNING可以总结出以下实践要点示例仅供学习与测试examples/providers/中的实现是应用专用的示例最终产品应拥有自己的实现空实现是安全基线Matter 1.5 之后默认空实现刻意返回空列表避免未审查的示例值进入产品如果你确实启用 FixedLabel、LocalizationConfiguration、TimeFormatLocalization 等 Cluster务必重写 Provider 并提供经过验证的值User Label 需要持久化该能力依赖PersistentStorageDelegate务必在注册 Provider 前通过SetStorageDelegate注入有效存储否则断言失败遵守容量约束标签名与标签值均不超过 16 字节、每 Endpoint 用户标签最多 4 条kMaxUserLabelListLength列表操作在超限时返回CHIP_ERROR_NO_MEMORY迭代器生命周期管理所有迭代器通过chip::Platform::New分配调用方必须在迭代结束后调用Release()且迭代期间不允许修改标签数据。参考文件索引关联文档examples/providers/README.md空实现版DeviceInfoProviderImpl.h / DeviceInfoProviderImpl.cpp常量实现版AllClustersExampleDeviceInfoProviderImpl.h / AllClustersExampleDeviceInfoProviderImpl.cpp构建配置examples/providers/BUILD.gn抽象基类src/include/platform/DeviceInfoProvider.h基类通用实现src/platform/DeviceInfoProvider.cpp应用接入示例examples/all-clusters-app/linux/fuzzing-main.cpp【免费下载链接】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),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表