ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Matter IDL 语法与代码生成:connectedhomeip 中基于 IDL 的 Codegen 全解析

Matter IDL 语法与代码生成:connectedhomeip 中基于 IDL 的 Codegen 全解析 Matter IDL 语法与代码生成connectedhomeip 中基于 IDL 的 Codegen 全解析【免费下载链接】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本文以 scripts/py_matter_idl/matter/idl/README.md 为主体骨架结合matter-idlPython 包的解析器、Lark 文法与生成器实现展开。Matter IDL 是 connectedhomeip 中用于描述数据结构、Cluster 定义与端点绑定的文本格式是 SDK 代码生成的单一事实来源source of truth。读完本文你将掌握 Matter IDL 的完整语法要素、AST 解析链路以及如何通过 Jinja2 模板为 Java/Kotlin/C 等多语言目标编写自己的代码生成器。什么是 Matter IDLMatter IDL 是一种文本文件目标是以简洁的方式表示数据结构data structures、Cluster 定义cluster definitions与绑定bindings。它同时满足两类使用者的需求人类可读语法清晰简洁、支持注释同时支持/* */与//两种注释风格便于开发者直接阅读与维护机器可解析语法有严格定义由 Lark 文法约束而非自由格式文本便于程序化解析并生成 AST。Matter IDL 正在持续演进目标是完整表示 Matter 规范Matter Specification的方方面面包括规范中定义的可选命令/事件optional commands/events、约束constraints、一致性conformance与元数据metadata。最终它将覆盖旧版 ZAP/ZCL XML 配置可能不支持、但已在新工具如 Alchemy 与 Data ModelDMXML中定义的特性。多来源汇聚一个格式容纳全部输入当前仓库中存在多个可生成/解析 IDL 文件的来源包括由ZAP生成的.matter文件会省略部分高级规范细节ZAP XML文件Data Model XML文件对应仓库中 data_model/ 目录下各版本 spec 数据由Alchemy解析的规范文本。Matter IDL 格式的设计目标正是通用到足以容纳并统一表示以上所有来源。这样一来序列化后的.matter文件就可以被信任为代码库的单一事实来源——无论是 Java/Kotlin 控制器代码、C 服务端 Cluster 配置还是 TLV 元数据都能从同一份 IDL 驱动生成。Matter IDL 语法速览一个 Cluster 的完整示例Matter IDL 的实际文法定义在 matter_grammar.lark 中基于 Lark 解析器。但通过示例理解要直观得多。以下是 README 中给出的完整示例几乎覆盖了 IDL 的全部语法要素/* C 风格注释支持 */ // C 风格注释也支持 // 每个 Cluster 都有一个由 Matter 规范定义的标识符此处为 31 cluster AccessControl 31 { // 当前描述的 Cluster 的 revision // 如果未指定则默认为 1 revision 3; // 枚举enum和结构体struct可以定义在全局也可以定义在 Cluster 内部。 // IDL 生成规则会考虑作用域优先使用本地定义的名字 // 一个 Cluster 中定义的内容对另一个 Cluster 不可见。 // // 常量constant支持额外的 spec_name 属性用于在名称生成逻辑 // 依赖首字母缩写展开与否如特殊字符、空格、全大写/数字逻辑时 // 澄清常量的命名来源。 enum SomeEnum : ENUM8 { kPASE 1 [spec_name PASE]; kCASE 2 [spec_name CASE]; kGroup 3; kMediaTVRoom 54 [spec_name Media/TV Room]; } // 结构体可以通过标记为 fabric_scoped 实现 fabric 作用域 // 在 fabric scoped 的结构体中字段可以标记为 fabric_sensitive fabric_scoped struct AccessControlEntry { fabric_idx fabricIndex 0; fabric_sensitive Privilege privilege 1; fabric_sensitive AuthMode authMode 2; nullable fabric_sensitive INT64U subjects[] 3; // 结构体字段可以是列表 nullable fabric_sensitive Target targets[] 4; // 也可以带属性nullable } // request 结构体是作为命令输入使用的普通结构体。 // 打上特殊标记以明确用途。 request struct AnnounceOTAProviderRequest {} request struct ConnectNetworkRequest { OCTET_STRING networkID 0; INT64U breadcrumb 1; } // response 结构体用于命令输出 // response 被编码为命令并使用唯一 ID 进行编码 response struct ConnectNetworkResponse 123 { CHAR_STRING debugText 1; INT32S errorValue 2; } // 事件可以指定类型critical/info并可以包含数据 critical event StartUp 0 { INT32U softwareVersion 0; } // 支持无数据事件 info event Leave 2 { } // 事件默认使用 view 权限但可以被修改 info event access(read: manage) RestrictedEvent 3 { } // 支持可选事件 info optional event OptionalEvent 4 { } attribute AccessControlEntry acl[] 0; // 属性默认是读写的 attribute ExtensionEntry extension[] 1; // 并且需要规范定义的编号 // 属性可以要求 timed writes timedwrite attribute int16u require_timed_writes 3; // 属性的访问控制权限默认为 // // access(read: view, write: operate) // // 这些默认值可以修改为 view/operate/manage/administer 任一角色。 attribute access(read: manage, write: administer) int32u customAcl 3; // 属性也可以是只读的 readonly attribute int16u clusterRevision 65533; // 命令具有规范定义的编号用于无线over-the-wire调用。 // // 命令具有输入和输出数据类型通常编码为结构体。 command ConnectNetwork(ConnectNetworkRequest): ConnectNetworkResponse 0; // 输出始终可用即使只是 ok/failure // 但 IDL 明确不为 DefaultSuccess 定义结构体 // 它被视为内部类型。 command AnnounceOTAProvider(AnnounceOTAProviderRequest): DefaultSuccess 1; // 有些命令可以不带任何输入 command On(): DefaultSuccess 2; command Off(): DefaultSuccess 3; // 命令调用默认为 operate 权限同样可以修改 command access(invoke: administer) Off(): DefaultSuccess 4; // 命令调用可以要求使用 timed invoke timed command access(invoke: administer) RevokeCommissioning(): DefaultSuccess 2; // 命令可以是 fabric scoped 的 fabric command ViewGroup(ViewGroupRequest): ViewGroupResponse 1; // 命令可以同时具有多个属性 provisional fabric timed optional command RequiresTimedInvoke(): DefaultSuccess 7; // 条目可以有 API 稳定性前缀 // - provisional 表示通常可能发生变化 // - internal 用于 SDK 内部开发/使用/测试 provisional critical event StartUp 0 { INT32U softwareVersion 0; } internal struct SomeInternalStruct {} struct StructThatIsBeingChanged { CHAR_STRING debugText 1; provisional INT32S errorValue 2; } provisional timedwrite attribute int16u attributeInDevelopment 10; internal command FactoryReset(): DefaultSuccess 10; } // Cluster 也可以是 provisional 或 internal 的 provisional cluster SomeClusterInDevelopment 1234 { /// ... 内容省略 } // 在每个非动态端点号上可以暴露一系列 Cluster endpoint 0 { // binding cluster 是可以被绑定bind以供应用使用的 Cluster。 // // 例如一个灯开关可以绑定到一个灯泡 // 或者一个 Cluster 可以绑定到一个 OTA provider 以用于更新。 binding cluster OtaSoftwareUpdateProvider; // server cluster 是暴露给外界的服务器。 // // 例如一个灯泡可以暴露 OnOff Cluster。 server cluster OtaSoftwareUpdateRequestor { // 每个端点 server cluster 实例会有各自选择的属性用于存储/默认值 // // 如果没有给出存储默认值则值初始化为 0/false/empty // // 默认值目前仅支持原始类型即不支持 list/struct/array但支持字符串 ram attribute zeroInit; // 初始化为 0。 ram attribute stringDefault defaultabc; // 字符串可以有默认值。 ram attribute boolDefault defaulttrue; // bool 可以有默认值。 ram attribute inRam default123; // 存储在 RAM 中重启后丢失。 persist attribute persist; // 持久化在 NVM 中跨重启保留。 callback attribute usesCallback; // zap/ember EXTERNAL 回调。 } }语法要素拆解对照 matter_grammar.lark 中的文法规则可以将上述示例的语法要素归类如下语法要素文法规则说明Cluster[maturity] (client\|server)? cluster id int { ... }可带client/server前缀仅用于向后兼容当前无直接语义Cluster 编号由规范定义revisionrevision positive_integer ;未指定时默认为 1enum / bitmapenum id : type { constant_entry* }枚举基于基础类型如ENUM8bitmap 同理条目为id intconstant_entry[maturity] id int [[ spec_name ESCAPED_STRING ]] ;常量可附带spec_name澄清命名来源struct[shared] struct_qualities struct id { field* }struct_quality支持fabric_scopedshared表示按名称跨 Cluster 共享struct_field[maturity] member_attribute* field字段属性包括optional、nullable、fabric_sensitivefielddata_type id list_marker? positive_integer字段类型、名称、[]列表标记、字段编号request/response structrequest struct/response struct id int { ... }request 作为命令输入response 必须带响应 ID编码为命令eventevent_qualities event_priority [optional] event [access] id int { ... }优先级为critical/info/debugquality 支持fabric_sensitive默认view权限attributeattribute_qualities attribute [access] field ;qualities 包括readonly、writeonly、nosubscribe、timedwritecommandcommand_qualities [optional] command [access] id ( id? ) : id int ;qualities 包括fabric、timed默认operate调用权限endpointendpoint int { content* }内容可以是 device type、binding cluster或server cluster属性存储ram/persist/callbackRAM 存储、NVM 持久化、回调EXTERNAL端点点级命令/事件emits event id ;/handle command id ;端点实例可声明发出的事件与处理的命令数值POSITIVE_INTEGER: /\d/、HEX_INTEGER: /0x[A-Fa-f0-9]/支持十进制与十六进制标识符ID: /[a-zA-Z_][a-zA-Z0-9_]*/字母下划线开头字母数字下划线注释与空白%ignore WS、%ignore C_COMMENT、%ignore CPP_COMMENT空白、C/C 注释均被忽略API 稳定性前缀maturity是 IDL 中一个重要的跨类型标记matter_grammar.lark中定义了四级对应 matter_idl_types.py 中的ApiMaturity枚举STABLE/PROVISIONAL/INTERNAL/DEPRECATED不带前缀 stable稳定provisional临时通常可能发生变化internal内部用于 SDK 内部开发/使用/测试deprecated已弃用。该前缀可作用于 Cluster、struct、enum/bitmap 条目、attribute、command、event 乃至单个字段例如示例中的provisional critical event StartUp、internal command FactoryReset()、provisional timedwrite attribute attributeInDevelopment。访问控制权限方面access_privilege支持四级view、operate、manage、administer。事件默认view属性默认access(read: view, write: operate)命令默认operate均可显式覆盖如示例中的event access(read: manage)、attribute access(read: manage, write: administer)与command access(invoke: administer)。IDL 的解析从文本到类型安全的 ASTIDL 的解析在matter-idlPython 包内完成即本 README 所在的目录scripts/py_matter_idl/matter/idl/。绝大多数繁重工作由 Lark 借助 matter_grammar.lark 完成随后转换成 ASTmatter_grammar.lark负责解析并校验文本内容matter_idl_parser.py包含一个 transformer将 Lark 给出的文本转换为在 matter_idl_types.py 中定义的、类型更安全也更类型丰富的 AST。AST 类型体系从 matter_idl_types.py 可以看到 AST 的核心数据类Idl顶层容器持有clusters、endpoints以及三类全局类型global_bitmaps/global_enums/global_structs还有解析的parse_file_nameCluster持有name、code、revision默认 1、enums、bitmaps、events、attributes、structs、commands与descriptionFielddata_type、code、name、is_list、qualitiesoptional/nullable/fabric_sensitive 标志位与api_maturityAttribute包装definition: Field并携带readacl/writeacl默认 VIEW/OPERATE、default值以及is_readable/is_writable/is_subscribable/requires_timed_write等便捷属性Commandinput_param/output_param、invokeacl默认 OPERATE、is_timed_invoke/is_optional/is_fabric_scopedEventpriorityDEBUG/INFO/CRITICAL、readacl、is_fabric_sensitive/is_optionalEndpoint/ServerClusterInstantiation/AttributeInstantiation/CommandInstantiation描述端点及其上 server cluster 实例的属性存储方式AttributeStorage枚举RAM / PERSIST / CALLBACK与默认值ParseMetaData记录每个条目的line、column、start_pos便于在日志与错误报告中引用数据项的位置。值得留意的是matter_idl_parser.py中实现的PrefixCppDocComment机制它会把紧邻 IDL 元素前面的/** ... */文档注释提取出来作为该元素的description附加到 AST 上——支持 cluster、attribute、command、struct 及其字段、event 及其字段、enum/bitmap 及其条目等所有元素类型。这解释了为什么 IDL 既能保持机器可解析又能保留面向人的文档信息。端点的 transform 机制解析器还内置了一组端点变换transform例如AddServerClusterToEndpointTransform、AddBindingToEndpointTransform与AddDeviceTypeToEndpointTransform它们的apply(endpoint)方法会把解析出的 server cluster、binding 或 device type 挂载到对应 Endpoint 上最终填充Idl.endpoints。代码生成从 AST 到多语言产物代码生成器定义在generators目录下其职责是把解析好的 AST 转换为一个或多个输出文件。大多数情况下输出会按 Cluster 拆分从而避免生成巨大的 C 文件——这样编译更快还能实现更好的并行编译。从 generators/ 目录结构可以看到仓库内置的生成器家族cpp/包含application如CallbackStubSource.jinja、ClusterCallbacksSource.jinja、ServerClusterConfig.jinja、sdk如AttributeIds.h.jinja、CommandIds.h.jinja、Metadata.h.jinja与tlvmetaTLV 元数据头/源文件三组模板java/ChipClusters_java.jinja、ChipEventStructs_java.jinja、ChipStructs_java.jinja、ClusterIDMapping.jinja等kotlin/MatterClusters.jinja、MatterStructs.jinja、MatterFiles_gni.jinja等markdown/clusters_markdown.jinja生成 Cluster 文档idl/MatterIdl.jinjaIDL 到 IDL 的重写/规范化。CodeGenerator 基类生成器使用Jinja2作为模板语言。generators/init.py 中的通用CodeGenerator类提供了基于 Jinja 模板输出文件的能力其公共接口非常精简storage指定生成代码的输出位置GeneratorStorageidl作为生成输入的被解析 ASTIdl类型render(dry_runFalse)执行所有文件的渲染dry_runTrue时仅记录输出而不真正写盘internal_render_all()由子类实现驱动整个生成过程internal_render_one_output(template_path, output_file_name, template_vars)子类调用它来实际渲染单个模板。两个值得注意的工程细节增量生成优化CodeGenerator会尝试读取已存在的数据如果文件内容没有变化就不重写不更新文件写入时间从而避免触发不必要的重编译——这一点在 generators/storage.py 的输出存储层配合下完成通用过滤器注册构造函数中通过RegisterCommonFilters注册公共过滤器保证所有模板共享同一套数据变换工具。在构建可用的 Jinja2 模板之前还需要对 AST 数据做进一步处理generators/types.pyREADME 中提及的类型处理设施负责查找命名空间例如先在 Cluster 内搜索具名数据类型再回退到全局以及把数据类型解释为更具体的类型。实现自定义生成器除了默认的 AST 处理之外每个生成器还需要为自身模板添加语言相关的过滤器包括为数据添加额外的过滤器与变换添加语言相关的类型处理逻辑。README 特别指出可以参照generators/java下的 Java 代码生成器作为 codegen 的实现范例参见 generators/java/ 目录下的各类.jinja模板及其驱动代码。生成器的测试golden 文件比对生成器的测试基于给定输入必须匹配预期输出的原则。tests/available_tests.yaml 描述了每个输入文件与生成器组合所对应的预期输出golden 文件。其格式为generator input_file output_file : golden_path其中generator是生成器类型input_file是输入 IDLoutput_file/golden_path分别是预期输出的文件名与该文件的黄金参考路径。测试用例速览从 tests/inputs/ 与 tests/outputs/ 目录可以看到当前仓库的测试输入与输出测试输入.matter覆盖的生成器输出验证点several_clusters.matterjava-class、cpp-app、custom-example-protoJava 控制器全套类ChipClusters、ChipStructs、ClusterIDMapping 等、C app 回调与静态 Cluster 配置、proto 文件large_all_clusters_app.mattercpp-app覆盖约 90 个 Cluster 的static-cluster-config/*.h与PluginApplicationCallbacks.h、callback-stub.cpp、cluster-callbacks.cpplarge_lighting_app.mattercpp-applighting 应用的静态 Cluster 配置全套cluster_with_commands.matter、cluster_struct_attribute.mattercpp-tlvmetaTLV 元数据clusters_meta.cpp/.hsimple_attribute.matter基础解析验证最小语法client cluster MyCluster 123 { attribute int16u clusterAttr 1; }optional_argument.matter、global_struct_attribute.matter、cluster_with_commands.matter解析/生成专项验证可选参数、全局结构体属性、带命令 Cluster 等特性测试的设计意图测试的设计意图是聚焦且易于查看差异deltas输入 IDL 应当尽量小聚焦于某一项具体功能测试输出在 codegen 逻辑变化时预期由人工审查——也就是说当生成逻辑有意变更时golden 文件需要随之更新并经人确认避免静默回归。这些生成器测试由 test_generators.py 运行。此外matter-idl包还配套了针对解析器test_matter_idl_parser.py、IDL 生成器test_idl_generator.py、ZAP XMLtest_zapxml.py、Data Model XMLtest_data_model_xml.py、向后兼容test_backwards_compatibility.py与类型支持test_supported_types.py等维度的测试文件。IDL 与示例应用的联动Matter IDL 不只是规范描述文件它直接驱动着示例应用与生成代码。在仓库中可以找到大量真实.matter文件例如examples/lighting-app 与 examples/all-clusters-app 等示例应用目录下的.matter/.zap文件examples/chef/devices 目录下 55 个.matter文件对应 55 个.zap文件——chef 设备矩阵即由 IDL 描述。all-clusters-app与lighting-app的 IDL 还被直接用作 tests/available_tests.yaml 中的大输入测试验证cpp-app生成器能够在全量 Cluster 集合上产出正确的static-cluster-config头文件。这印证了 README 的核心论断序列化后的.matter文件是代码库的可靠事实来源从它生成的代码与示例应用保持严格一致。小结Matter IDL 是 connectedhomeip 代码生成体系的中枢一种格式以人类可读、机器可解析的文本统一描述 Cluster、数据结构、事件、属性、命令与端点绑定并持续向完整表达 Matter 规范演进两级解析Lark 文法matter_grammar.lark完成词法与语法解析transformermatter_idl_parser.py产出类型安全的 ASTmatter_idl_types.pyJinja2 生成CodeGenerator基类generators/init.py按 Cluster 拆分输出支持增量写入避免无谓重编译Java/Kotlin/C/TLV 元数据/文档均有现成生成器golden 测试保障通过 tests/available_tests.yaml 的输入-输出映射与人工审查机制确保 codegen 变更可被清晰审查与验证。对开发者而言无论你是要新增一个 Cluster、为某个平台定制代码生成还是希望深入理解 SDK 从.matter文件到 C/Java/Kotlin 代码的完整流水线scripts/py_matter_idl/matter/idl/目录本 README 所在位置都是最直接的起点。【免费下载链接】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),仅供参考
返回列表