ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

DLMS/COSEM与HDLC协议栈实战:从帧结构到调试环境搭建

DLMS/COSEM与HDLC协议栈实战:从帧结构到调试环境搭建 简介DLMS/COSEM是智能电表与能源计量领域广泛采用的通信协议族由DLMS报文规范和COSEM对象模型共同构成对应IEC 62056系列标准。其底层HDLC数据链路层负责帧同步、差错校验与多帧重组是掌握协议栈的关键地基。理解HDLC帧格式、SNRM/UA建链及AARQ/AARE应用关联流程能帮助工程人员快速定位通信故障。该技术广泛用于电表、集中器、充电桩计费模块及用采主站对接等场景。借助Gurux等开源库与Wireshark抓包工具开发者可以将标准文档与源码对照学习快速搭建可运行的调试环境提升跨厂商设备互操作能力。 干这行的人应该都有过这种经历——客户抛过来一句我们的表计支持DLMS/COSEM你那边把通信模块调通然后你翻遍全网的资料文件下了几个G打开一看全是IEC 62056的标准扫描件、厂商手册、论坛提问愣是不知道从哪一页开始看。DLMS/COSEM这个协议栈说难不算难说简单也绝不简单尤其被它底层的HDLC协议卡住的时候你会觉得连一个字节都对不上号。这篇博文我就把你的学习路径理顺讲清楚DLMS/COSEM到底是什么、HDLC协议层怎么跟它配合、文档和软件源码应该怎么对照着看以及怎么用开源代码快速搭出一个能跑的调试环境。这篇文章适合刚接手智能电表、集中器、能源网关、充电桩计费模块的嵌入式工程师和软件工程师也适合做用电信息采集平台、需要对接多种厂商设备的上位机开发。我会尽量用大白话把协议讲透后面还会给出可以直接抄作业的代码片段和排查套路。1. DLMS/COSEM到底是什么鬼1.1 名字拆开看DLMS、COSEM分别解决什么问题先说结论DLMS/COSEM不是一个协议而是一整套协议族。DLMS是Device Language Message Specification设备语言报文规范它定义的是设备之间对话时报文应该用什么语法来写COSEM是Companion Specification for Energy Metering能源计量配套规范它定义的是电表里那些数据长什么样、怎么结构化地暴露给外部。用一个不严谨但好懂的类比DLMS是语法书COSEM是词典两者合在一起才构成一套完整的语言体系。所以你在文档里会经常看到两个词一起出现DLMS/COSEM或IEC 62056。前者是DLMS用户协会DLMS UA推广的叫法后者是国际电工委员会把它标准化之后的编号。国内电网行业也是基于这套标准做了衍生规范。不管叫什么核心目标是一致的让不同厂商的电表、集中器、主站软件之间能够互操作——你在A厂电表上能读到的东西换到B厂电表上也能用同样的方式读到。1.2 协议栈分层别把对象模型、应用层、数据链路层搅在一起DLMS/COSEM从下往上大致分这么几层每一层的职责非常清晰层次核心内容典型协议/规范对象模型层用COSEM类Interface Class、对象、属性、方法描述计量数据IEC 62056-62应用层xDLMS APDU的编码、服务Get/Set/Action/EventNotificationIEC 62056-53数据链路层可靠传输、帧同步、差错校验、分段重组HDLC协议IEC 62056-46物理层电信号、光电头/RS-485/网络传输介质IEC 62056-21 / 本地端口很多新手一上来就去看APDU应用协议数据单元的十六进制琢磨C0 01 C1 00 01 00 00 06 01 00 00 06 00这个读取请求是怎么组出来的却忽略了底层HDLC帧都还没打通。实际上DLMS的HDLC层跟应用层是解耦的你可以先用串口工具手动发帧、先保证链路能建立、能收到回复再往上走。正确顺序永远是先通链路再谈业务数据。1.3 为什么智能电表行业绕不开它全球范围内智能电表、集中器、数据采集终端大规模采用DLMS/COSEM作为国际通用的计量通信标准。ISKRA、LandisGyr、Itron、EDMI这些主流表计厂商几乎都能支持DLMS/COSEM国内光伏逆变器、充电桩电能计量模块为了出口海外也越来越多地要求支持这套协议。现实就是你的产品要进欧洲、中东、东南亚市场DLMS/COSEM基本是无法回避的选项。这不是因为它十全十美而是因为它的生态成熟有标准的对象模型可以抽象各种计量数据有完善的安全认证机制HLS、GMAC还有大量的开源实现可以验证互操作性。一旦你掌握了这个协议族的核心思路换个厂商、换个地区也只是换一套OBIS码和参数配置罢了。2. HDLC数据链路层最容易卡住的通信地基2.1 为什么DLMS要选HDLCHDLCHigh-Level Data Link Control高级数据链路控制是一种经典的数据链路层协议在通信领域用了很多年。DLMS/COSEM在数据链路层选择HDLC看中的是它几方面的能力帧同步可靠用0x7E作为帧起始和结束标志配合转义机制能清楚界定报文的边界。差错控制完备每个帧带帧校验序列Frame Check SequenceFCS接收方校验失败就丢弃或要求重传。支持多帧分段电表的计量数据动辄几十字节甚至上千字节HDLC可以分帧发送分帧接收再重组。但HDLC也是很多人学DLMS/COSEM时第一个崩溃的地方。原因很简单光一个帧头就有若干个bit位要理解地址字段还是可变长度的不同协议文档对同一个字段的称呼还不统一。所以这里必须把HDLC帧结构彻底讲明白你后面的所有抓包分析才有基础。2.2 HDLC帧逐字段拆解一个标准的DLMS/COSEM HDLC帧长这样7E | 帧格式(2字节) | 目的地址(1-N字节) | 源地址(1-N字节) | HCS(2字节) | LLC(3字节) | APDU(变长) | FCS(2字节) | 7E逐个字段说帧标志Flag固定0x7E表示一帧的开始和结束。因为数据字段里可能也会出现0x7E所以发送端会做0x7D转义处理接收端要反过来还原。这是一个隐藏很深的坑后面我会专门讲到。帧格式字段Frame Format2个字节承载了帧类型、分帧标志、帧计数、发送序号和接收序号。具体位分配在不同标准版本文档里画法不一样但你只需要知道三类关键信息就行帧类型I帧传数据S帧做确认和流量控制。DLMS的建链过程还会用到U帧SNRM/UA/DISC。分帧标志Segment Bit如果是1表示这是多帧分段中的一段不是完整报文。帧计数Frame Counter发送端每次发帧递增接收端用它识别是否漏帧。地址字段目的地址和源地址。DLMS/COSEM的地址字段是高字节扩展模式——每个地址字节的最高位如果是1表示后面还跟着下一个地址字节如果是0表示这个地址字节是最后一字节。实际中我们经常看到0x10代表客户端Client0x01代表服务端Server但这不是硬性规定不同厂商可能用不同的地址值所以通信失败时首先要确认两侧地址配置一致。HCS/FCSHCS是帧头校验序列FCS是整帧校验序列都使用CRC算法。HCS校验的范围是帧格式目的地址源地址也就是帧头部分FCS校验的范围是从帧格式开始到APDU结束。用两个不同位置的校验接收方可以先快速判断帧头有没有被干扰再判断整帧数据是否完整。LLC字段三字节E6 E6 00在DLMS/COSEM里基本是固定值表示上层协议是xDLMS。有些资料里也把LLC叫做链路层控制头它实际上是OSI模型第二层的残留概念。APDU应用层数据承载真正的业务请求/响应。比如读取正向有功电能的GET-Request就在这里面。帧的本体部分。2.3 一次完整的通信是怎么跑起来的在传输业务数据之前通信双方必须先打招呼建立数据链路。这个过程在DLMS/COSEM里分两步第一步物理层连通后客户端发送U帧SNRMSet Normal Response Mode设置正常响应模式。服务端收到后如果愿意通信回复UA帧Unnumbered Acknowledge无编号应答。这一步成功说明HDLC数据链路是通的。第二步在数据链路已建立的基础上客户端再发送应用层AARQApplication Association Request应用关联请求协商应用上下文、认证机制、协议版本。服务端回复AAREApplication Association Response应用关联响应带一个结果代码。这一步成功说明应用层可以开始干活了。之后才是Get/Set/Action等业务交互这些业务数据一般用I帧承载如果只是确认对方、调节流量或要求重传就用S帧RR、REJ等承载。你可以这么理解SNRM/UA是拨号成功AARQ/AARE是登录成功之后的I帧才是调数据。3. 文档与源码的双线攻略怎么学最快3.1 官方标准文档先别一口气全读DLMS/COSEM相关资料多到吓人DLMS UA有自己的Blue Book、Green Book、Yellow BookIEC又有一套62056系列国内还有电网企业的企业标准。如果打开第一份PDF就从头逐页读大概率三天后还在前三章打转。我的建议是先读主线再按需查支线。IEC 62056-62COSEM对象模型了解对象、接口类、属性、方法是干啥的。不需要背全但要知道结构。IEC 62056-53xDLMS应用层了解APDU的编码结构、Get/Set/Action服务怎么封装。IEC 62056-46HDLC数据链路层也就是本文重点理解帧格式、状态机、分帧和重传。DLMS UA的Blue Book内容更全含了前面好几本标准的内容适合当工具书查。国内电网企业标准一般是基于通用标准做的裁剪会规定具体的OBIS码、通信地址、波特率、认证参数。项目开始时一定要先拿这份规范它能帮你少走很多弯路。读的顺序我建议这样先看企业标准里给出的通信流程帧示例再对照通用标准里的帧结构定义最后才是抠OBIS码和对象模型。从具体帧示例入手比从规范条文入手效率高得多。3.2 开源代码地图Gurux是绕不开的宝藏说到软件源码DLMS/COSEM生态里最出名的就是Gurux。Gurux.DLMS是一个跨平台开源库官方提供了C#、Java、Python、C等语言版本。它做了什么把HDLC帧的打包/解析、APDU的编解码、对象模型映射、OBIS码管理都封装好了你调用它就能发SNRM、AARQ、GET-Request也能处理服务端回复。除Gurux之外还有几个常被提到的项目项目/库语言说明Gurux.DLMSC#/Java/Python/C等功能全、文档多、更新活跃最推荐DLMS官方Java库Java偏底层更贴近标准原语Wireshark内置DLMS解析器抓包工具插件不是协议库但调试必备各厂商SDK各平台常用在集成环境适合最后对照开源库最大的价值不是你直接拿去商用授权还得看协议而是让你能边跑边看标准文档里那些晦涩的话翻译成代码到底是什么样子。3.3 文档源码对照阅读法以读取正向有功电能为例我拿一个最典型的业务串起来讲读正向有功电能。第一步在对象模型文档IEC 62056-62或企业规范里查正向有功电能对应的OBIS码通常是1.0.1.8.0.255。OBIS码就是对象标识六个数字段分别代表计量类型、通道、测量项、分类等。查到这个码之后再定位它属于哪个接口类大概率是IC3寄存器类或IC1数据类。第二步打开开源库的源码看一下它怎么把OBIS码转换成对象引用。在Gurux里通常是GXDLMSData或GXDLMSRegister对象创建对象时把OBIS码传进去库内部会维护对象列表。第三步构造GET-Request。你的代码调用类似GetRequest的方法库会生成一个APDU里面包含invoke ID、对象属性描述符等。如果你在Wireshark里看到请求能直观地看到APDU里嵌着06 01 00 00 06 00这样的OBIS编码。第四步底层帧打包。库把APDU塞进HDLC帧前面加LLC、地址、帧格式算好HCS/FCS然后通过串口或网口发出去。如果你是真要做开发一定要学会需求 → OBIS码 → 对象属性 → APDU → HDLC帧 → 物理层这条链路的翻译能力。代码是可以复用的但这个翻译能力是核心。4. 自己动手搭建一个最小可跑的DLMS/COSEM调试环境4.1 环境准备与工具选择光看文档不跑代码等于白学。我建议你按这个组合准备工具一个DLMS/COSEM服务端模拟器Gurux有Gurux.DLMS.Server示例也可以搜到不少厂商的电表模拟器它能监听串口或网络端口扮演电表角色。一个DLMS/COSEM客户端程序用Gurux.DLMS的Client示例或者你自己写一个。Wireshark抓包分析帧结构装好之后记得确认它能解析DLMS协议。虚拟串口工具如果走串口比如Windows下的Virtual Serial Port Driver把客户端和服务端两个虚拟串口对接起来。调试顺序先用模拟器把整套流程跑通再接入真实电表。真实电表有各种厂商差异不要在第一步就被它劝退。4.2 手写一个极简HDLC打包器下面这个Python片段是我改的简化版目的是演示HDLC帧的结构不是完整的生产级实现。它把CRC16的计算、帧头组包、地址填充的逻辑表达清楚就够了def crc16_ccitt(data: bytes, poly: int 0x1021, init: int 0xFFFF) - int: crc init for b in data: crc ^ b 8 for _ in range(8): if crc 0x8000: crc ((crc 1) ^ poly) 0xFFFF else: crc (crc 1) 0xFFFF return crc def build_hdlc_frame(dest_addr: bytes, src_addr: bytes, apdu: bytes) - bytes: # 帧格式头这里用固定示例值 0xA0 0x1F实际项目中要按发送/接收序号动态维护 frame_format bytes([0xA0, 0x1F]) llc bytes([0xE6, 0xE6, 0x00]) header frame_format dest_addr src_addr hcs crc16_ccitt(header) body llc apdu fcs crc16_ccitt(header hcs.to_bytes(2, big) body) frame b\x7E header hcs.to_bytes(2, big) body fcs.to_bytes(2, big) b\x7E return frame你看到的关键点HCS只覆盖帧头FCS覆盖整个数据部分。地址字段每个字节高位置1表示还有后续字节。这个极简函数跑通后你再去读Gurux源码会轻松很多因为你对这行代码在干嘛有了预期。4.3 用Wireshark验证帧结构假设你已经跑通了客户端和服务端的网络连接给Wireshark配上DLMS的端口号默认监听某个TCP端口你就能看到完整的一次建链和读数据流程。推荐你去抓三类帧SNRM请求帧看U帧的命令字节看目的地址/源地址怎么填。UA应答帧等服务端回复看它的地址是否与请求的源地址/目的地址互换。读取请求和响应帧找I帧看APDU里的OBIS码和数据类型标签。我见过很多朋友卡在帧发了但收不到回复“或者收到回复但一直报HCS/FCS错误”这些基本都能在Wireshark里一眼定位。Wireshark的DLMS解析器会把帧格式字段、地址、校验值都标出来你只要对着第2节的字段表就能判断问题在哪一层。5. 常见问题与排查技巧实录5.1 通信建立失败的几个高频原因现象可能原因排查思路完全无响应串口参数/网络地址不对确认波特率、数据位、校验位、TCP端口收到SNRM但无UA回复服务端不允许该客户端地址检查目的地址/源地址是否匹配设备配置UA回复了但AARQ失败应用上下文名称不匹配检查协商的版本号/应用上下文IDAARQ成功但读数据异常权限不足或OBIS码不存在检查认证机制、对象列表地址问题是最常见的。很多电表默认服务端地址是0x01客户端地址是0x10但有些厂商的设备把服务端地址设成0x02或者一个多位地址如果不按对方手册配置SNRM发过去它也当没看见。5.2 多帧分段与转义处理当数据比较大比如读冻结数据、事件日志时DLMS会把APDU拆成多个HDLC帧。每一帧的分帧标志位置1发送序号递增接收方收齐后重组。这里容易踩的坑有几个分段阈值不一致发送端按256字节分段接收端只支持512字节会导致重组失败。尽量把MAX_PDU_SIZE配置对齐。窗口控制处理不对HDLC有发送窗口/接收窗口如果收到的I帧序号不连续要用REJ或SREJ请求重传。0x7E转义漏处理如果数据里出现0x7E发送端必须在它前面加0x7D并把0x7E做异或变换。有些自研实现忘了这步导致接收端把数据当帧标志处理整帧错乱。5.3 数据读出来却是错的如果你能正常读到响应但解析出来的数值不对通常问题在OBIS码选择、数据类型、标度系数和对齐方式上。比如OBIS码选错同一份数据读1.0.1.8.0.255和读1.0.1.8.1.255可能一个是正向有功电能一个是分时费率下的电能。数据类型不匹配寄存器可能是long也可能是double long甚至带一个signed/unsigned标志位解析时按错类型读取就会得到离谱的数。标度系数没处理电表字段带scaler如果值是1但实际数值是0.01你要根据scaler做乘法换算。建议调试时先用模拟器把响应固定成简单整数跑通之后再用真实电表数据。一上来就处理复杂现场数据很容易把自己绕晕。5.4 认证与权限问题DLMS/COSEM支持低级认证LLS和高级认证HLS如果设备配置了认证你不带认证信息直接访问会被拒绝。HLS认证还需要客户端和服务端做一次质询-响应的挑战牵扯到密钥管理。这块建议项目前期就跟设备厂商确认清楚设备初始是否开放公共客户端访问通常是Client Address0x10、认证级别最低还是必须走HLS。很多联调翻车都翻在对方默认关掉了公共访问。实操过程中的几点体会最后说点实在的。dlms/cosem这个东西光看不练没有任何意义光练习不看标准文档也容易走到歪路。我最开始做这个协议的时候被HDLC帧格式的位分配搞到怀疑人生后来真正打通一次抓包、看到UA帧回过来、再看到数据帧里那一串OBIS码时才算彻底通了。所以我的建议是先跑再读最后再回来抠细节。遇到问题不要急一个小坑一个小坑填等你攒够了这些坑位图DLMS/COSEM在你眼里就是一套无比清晰的协议栈而已。本文还有配套的精品资源点击获取
返回列表