Matter DiagnosticLogs 集群升级指南:从 `SetDiagnosticLogsProviderDelegate()` 到 `SetDelegate()` 的 API 迁移实战
2026/9/19 3:34:53 网站建设 项目流程

Matter DiagnosticLogs 集群升级指南:从SetDiagnosticLogsProviderDelegate()SetDelegate()的 API 迁移实战

【免费下载链接】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

本文是 connectedhomeip(Matter SDK)仓库中 DiagnosticLogs 集群升级说明 的深度展开。它面向正在为设备接入 Diagnostic Logs(诊断日志)集群的开发者,梳理该集群由「按 Endpoint 作用域」演进为「Node 级单例」后引发的 API 变更,给出新旧 API 的精确替换方式,并结合仓库源码说明新接口的底层实现、委托(Delegate)生命周期与测试验证方法。读完本文,你将能够把基于旧接口的集成代码平滑迁移到DiagnosticLogsCluster::Instance().SetDelegate(),并理解为什么这次迁移不仅仅是改名。

一、背景:DiagnosticLogs 集群的作用域模型变更

Matter 规范对 Diagnostic Logs 集群有一个关键约束:诊断日志是 Node 级的(Node-wide),不是某个 Endpoint 专属的。规范原文表述为:

Diagnostic logs will be Node-wide and not specific to any subset of Endpoints. When present, this Cluster SHALL be implemented once for the Node.

这一约束直接体现在 CodegenIntegration.cpp 的实现注释与代码中:

// Specification: // Diagnostic logs will be Node-wide and not specific to any subset of Endpoints. // When present, this Cluster SHALL be implemented once for the Node. Global<DiagnosticLogsCluster> gServer;

同时,初始化回调 MatterDiagnosticLogsClusterInitCallback 也明确要求集群只挂在根 Endpoint(kRootEndpointId)上:

void MatterDiagnosticLogsClusterInitCallback(EndpointId endpoint) { // We implement the cluster as a singleton on the root endpoint. VerifyOrReturn(endpoint == kRootEndpointId); (void) CodegenDataModelProvider::Instance().Registry().Register(ClusterRegistration()); }

而 DiagnosticLogsCluster 类的构造函数同样把集群锚定在根 Endpoint 上:

class DiagnosticLogsCluster : public DefaultServerCluster { public: DiagnosticLogsCluster() : DefaultServerCluster({ kRootEndpointId, DiagnosticLogs::Id }) {} static DiagnosticLogsCluster & Instance() { ... } };

既然集群本身是 Node 级单例、只存在于根 Endpoint,那么旧 API 却要求调用方传入EndpointId就显得既多余又具有误导性——这正是本次 API 变更的根本动因。

二、变更核心:SetDelegate()替代SetDiagnosticLogsProviderDelegate()

旧 API(已废弃)

旧接口通过DiagnosticLogsServer::Instance()获取服务器实例,并强制要求传入endpointId

using chip::app::clusters::DiagnosticLogs; DiagnosticLogsServer::Instance().SetDiagnosticLogsProviderDelegate(endpointId, delegate);

旧接口存在两个问题:

  • 必须显式传入EndpointId:调用方需要维护并传入正确的 Endpoint 编号;
  • 语义误导:传入endpointId会让人误以为该集群可以按 Endpoint 分别配置不同的日志提供者,而实际上 Diagnostic Logs 是 Node 级全局单例,endpoint参数在实现中根本不会参与逻辑。

新 API(推荐)

using chip::app::Clusters; DiagnosticLogsCluster::Instance().SetDelegate(delegate);

新接口的两点改进:

  • 不再需要endpointId:集群是单例,直接通过Instance()获取;
  • 意图更清晰:明确表达「为这个单例集群设置全局委托」。

三、新 API 的源码实现剖析

3.1DiagnosticLogsCluster::SetDelegate()的定义

新接口定义在 DiagnosticLogsCluster.h:

/** * Set the default delegate of the diagnostic logs cluster for the specified endpoint * * @param endpoint ID of the endpoint * * @param delegate The log provider delegate at the endpoint */ void SetDelegate(DiagnosticLogs::DiagnosticLogsProviderDelegate * delegate) { mDelegate = delegate; }

它是一个内联的单行实现,直接将传入的委托指针保存到成员变量mDelegate中:

private: DiagnosticLogs::DiagnosticLogsProviderDelegate * mDelegate = nullptr;

3.2Instance()单例模式

Instance()采用函数局部静态变量的方式实现线程安全的惰性单例:

static DiagnosticLogsCluster & Instance() { static DiagnosticLogsCluster instance; return instance; }

3.3 旧接口到新接口的转发实现

值得注意,仓库并没有立刻删除旧接口,而是通过 CodegenIntegration.h 中的DiagnosticLogsServer保留了兼容层:

/** * Set the default delegate of the diagnostic logs cluster. * * @note The `endpoint` parameter is ignored, * as diagnostic logs are a node-wide utility and not specific to any endpoint. * * @param endpoint Ignored. Was intended to be the ID of the endpoint. * @param delegate The log provider delegate. */ void SetDiagnosticLogsProviderDelegate(EndpointId endpoint, DiagnosticLogsProviderDelegate * delegate);

其实现(CodegenIntegration.cpp)直接忽略endpoint参数并转发给新接口:

void DiagnosticLogsServer::SetDiagnosticLogsProviderDelegate(EndpointId endpoint, DiagnosticLogsProviderDelegate * delegate) { gServer->SetDelegate(delegate); }

从源码结构看,旧 API 已进入「兼容过渡期」:endpoint参数被显式标注为Ignored,新代码应直接改用SetDelegate()

3.4 头文件与命名空间的变迁

旧代码使用的命名空间是chip::app::clusters::DiagnosticLogs(小写clusters),而新 API 使用chip::app::Clusters(大写Clusters,也是仓库主流的命名空间形式)。迁移时请同步调整using声明:

// 旧 using chip::app::clusters::DiagnosticLogs; // 新 using chip::app::Clusters;

四、Delegate 接口:迁移后你依然要实现这些方法

无论是旧 API 还是新 API,传入的委托类型都是同一个DiagnosticLogs::DiagnosticLogsProviderDelegate。该抽象类定义在 DiagnosticLogsProviderDelegate.h,迁移后的集成方需要实现以下纯虚方法:

方法作用关键语义
StartLogCollection(intent, outHandle, outTimeStamp, outTimeSinceBoot)开始一次日志采集会话返回唯一的LogSessionHandle标识会话;若无对应日志,返回kInvalidLogSessionHandleUINT16_MAX
CollectLog(sessionHandle, outBuffer, outIsEndOfLog)逐块读取日志数据outBuffer会被缩放为实际读取字节数;outIsEndOfLog标记是否还有更多数据
GetSizeForIntent(intent)查询某类日志的总字节数无日志返回 0
GetLogForIntent(intent, outBuffer, outTimeStamp, outTimeSinceBoot)一次性取回最新日志用于日志可塞进响应报文(Response Payload)的场景

另外还有两个可选的虚方法:

  • EndLogCollection(LogSessionHandle):结束采集会话,默认返回CHIP_ERROR_NOT_IMPLEMENTED
  • EndLogCollection(LogSessionHandle, CHIP_ERROR error):带错误码的重载版本,默认转发到单参数版本。头文件注释提示新实现应优先重载两参数版本,因为它才是日志采集过程中被真正调用的主方法。

LogSessionHandle定义为uint16_t,且UINT16_MAX被保留为非法句柄值(见 DiagnosticLogsProviderDelegate.h)。

五、集群内部如何消费 Delegate:两条日志传输路径

深入 DiagnosticLogsCluster.cpp 可以看到,集群收到的RetrieveLogsRequest会根据requestedProtocol字段分发到两条路径(InvokeCommand):

if (protocol == TransferProtocolEnum::kResponsePayload) { HandleLogRequestForResponsePayload(handler, request.path, commandData.intent); return std::nullopt; } return HandleLogRequestForBdx(handler, request.path, commandData.intent, commandData.transferFileDesignator);

5.1 Response Payload 路径(小日志)

HandleLogRequestForResponsePayload()(DiagnosticLogsCluster.cpp)通过LogRequestHandler::Process()(DiagnosticLogsCluster.cpp)完成:

  1. delegate为空 → 返回kNoLogs
  2. 缓冲区为空 → 返回kDenied
  3. GetSizeForIntent()返回 0 → 返回kNoLogs
  4. 调用GetLogForIntent(),出错返回kNoLogs/kDenied
  5. 成功则携带logContentUTCTimeStamptimeSinceBoot组装RetrieveLogsResponse

单条响应报文能携带的日志上限由chip::bdx::DiagnosticLogs::kMaxLogContentSize决定,缓冲区按该值分配。

5.2 BDX 路径(大日志,需编译开关)

HandleLogRequestForBdx()(DiagnosticLogsCluster.cpp)处理使用 BDX(Bulk Data Exchange)协议传输大体积日志的场景,逻辑要点:

  • 请求 BDX 但未提供TransferFileDesignatorINVALID_COMMAND(规范强制要求);
  • TransferFileDesignator超长 →ConstraintError
  • 无 delegate →kNoLogs
  • 日志大小 ≤kMaxLogContentSize时直接走 Response Payload 并以kExhausted状态返回(规范约定:能塞进报文就不开 BDX 会话);
  • BDX 传输是否可用受编译开关CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER控制:关闭时回退到 Response Payload +kExhausted;开启时若 BDX Provider 忙则返回kBusy,初始化传输失败则返回kDenied

实现中,HandleLogRequestForBdx还会调用gBDXDiagnosticLogsProvider.InitializeTransfer(...)(见 BDXDiagnosticLogsProvider.cpp)启动 BDX 会话,并在会话生命周期内回调StartLogCollection/CollectLog/EndLogCollection

六、迁移实操:以 all-clusters-app Linux 为例

仓库中 all-clusters-app/linux/main-common.cpp 是一个典型的旧 API 集成范例,可作为对照迁移的起点:

using namespace chip::app::Clusters::DiagnosticLogs; void emberAfDiagnosticLogsClusterInitCallback(chip::EndpointId endpoint) { auto & logProvider = LogProvider::GetInstance(); logProvider.SetEndUserSupportLogFilePath(AppOptions::GetEndUserSupportLogFilePath()); logProvider.SetNetworkDiagnosticsLogFilePath(AppOptions::GetNetworkDiagnosticsLogFilePath()); logProvider.SetCrashLogFilePath(AppOptions::GetCrashLogFilePath()); DiagnosticLogsServer::Instance().SetDiagnosticLogsProviderDelegate(endpoint, &logProvider); }

迁移到新 API 后:

using namespace chip::app::Clusters; void emberAfDiagnosticLogsClusterInitCallback(chip::EndpointId endpoint) { auto & logProvider = LogProvider::GetInstance(); logProvider.SetEndUserSupportLogFilePath(AppOptions::GetEndUserSupportLogFilePath()); logProvider.SetNetworkDiagnosticsLogFilePath(AppOptions::GetNetworkDiagnosticsLogFilePath()); logProvider.SetCrashLogFilePath(AppOptions::GetCrashLogFilePath()); DiagnosticLogsCluster::Instance().SetDelegate(&logProvider); }

迁移要点:

  1. using语句从chip::app::clusters::DiagnosticLogs改为chip::app::Clusters
  2. 删除endpoint实参;
  3. DiagnosticLogsServer::Instance().SetDiagnosticLogsProviderDelegate(...)替换为DiagnosticLogsCluster::Instance().SetDelegate(...)

如果你的代码仍依赖旧头文件 diagnostic-logs-server.h,仓库保留了兼容性 shim:该头文件仅做间接包含,内部转发到 CodegenIntegration.h,以兼容历史示例代码。

七、测试验证:新 API 的行为由单元测试背书

仓库为DiagnosticLogsCluster提供了完整的单元测试,见 tests/TestDiagnosticLogsCluster.cpp,其中MockDelegate实现了全部四个纯虚方法,并用diagnosticLogsCluster.SetDelegate(&delegate)注入委托。几个关键用例可作为你迁移后验证的对照清单:

测试用例场景期望结果
ResponsePayload_WithDelegate_Success有 delegate,Response Payload 传输kSuccesslogContent完整
Bdx_WithDelegate_kExhausted请求 BDX 但日志能塞进报文kExhausted,走 Response Payload
Bdx_WithDelegate_kExhausted_with_buffer_greater_than_kMaxLogContentSize日志超过报文上限且未开启 BDX 能力kExhausted,内容被截断到kMaxLogContentSize
ResponsePayload_NoDelegate_NoLogs未设置 delegatekNoLogs
ResponsePayload_ZeroBufferSize_NoLogsdelegate 报告 0 字节日志kNoLogs
Bdx_NoDelegate_NoLogsBDX 请求但无 delegatekNoLogs

从这些用例可以总结出迁移后的关键行为约定:

  • 未设置 delegate 时,无论哪种传输协议,一律返回kNoLogs
  • delegate 报告 0 字节日志时同样返回kNoLogs
  • 能塞进响应报文就优先塞,此时即使客户端请求的是 BDX,也返回kExhausted

这些行为直接由 DiagnosticLogsCluster.cpp 中的判定逻辑保证,与你采用哪个 API 设置 delegate 无关。

八、迁移检查清单

完成迁移后,建议逐项核对:

  1. ✅ 所有DiagnosticLogsServer::Instance().SetDiagnosticLogsProviderDelegate(endpoint, delegate)调用已替换为DiagnosticLogsCluster::Instance().SetDelegate(delegate)
  2. using声明已从chip::app::clusters::DiagnosticLogs更新为chip::app::Clusters
  3. ✅ 确认委托对象生命周期长于集群生命周期(mDelegate只是裸指针,不负责所有权);
  4. ✅ 确认委托实现的四个纯虚方法语义正确(尤其GetSizeForIntent返回 0 会被视为无日志);
  5. ✅ 若需要 BDX 大文件传输,确认编译开关CHIP_CONFIG_ENABLE_BDX_LOG_TRANSFER已开启;
  6. ✅ 参考 TestDiagnosticLogsCluster.cpp 的用例,回归验证kSuccess/kNoLogs/kExhausted/kBusy/kDenied各状态分支。

九、总结

本次 Diagnostic Logs 集群 API 变更的本质,是让接口与集群的「Node 级单例」本质对齐:DiagnosticLogsCluster::Instance().SetDelegate(delegate)不再要求(也不再接受)EndpointId,语义上明确这是对整个 Node 生效的全局委托。旧接口在仓库中仍保留为转发 shim(endpoint参数被忽略),但新代码应直接采用新 API。无论使用哪种 API,底层DiagnosticLogsProviderDelegate的职责与集群的两条日志传输路径(Response Payload 与 BDX)均保持不变,相关行为都有 单元测试 提供背书,迁移风险可控。

【免费下载链接】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),仅供参考

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

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

立即咨询