DLMS协议报文解析:HDLC链路、ASN.1编码与负荷曲线分片

简介:《DMLS协议中文版》是一份面向电能量数据采集终端开发、调试及电能表通讯测试人员的中文协议手册,针对DLMS/IEC62056协议族在实际通讯中的理解与应用问题,系统梳理协议模型、物理层、HDLC链路层和应用层等核心内容。压缩包内共1个doc文件,大小约620KB,便于集中查阅和离线阅读。手册重点讲解ASN.1语法、BER与AXDR编码、AARQ与AARE数据帧及数据请求过程,并给出请求电量、瞬时量(电压、电流、功率)、负荷曲线和时间等实例数据包,可帮助读者理解帧结构、连接建立与数据交互细节。已有870人学习,适合作为协议入门、联调排错和报文对照的参考材料。

1. 从一块电表说起:DLMS 协议到底是什么

家里换过智能电表的人,多半有过这样的疑惑:抄表员再不上门,电费单上那个数字是怎么跑到供电那边的?答案藏在电表和采集终端之间的一段对话里。它们说的既不是人话,也不是随便定的一串暗号,而是一套有正式编号的约定——IEC 62056 协议族,业内更习惯叫它 DLMS。中文资料里常见的写法是「DMLS 协议」,字母顺序换了一下,指的仍是同一套东西,只是抄写时手滑留下的习惯写法。这份中文手册干的事,就是把这套原本全是英文标准、散落在十几份文档里的约定,翻译成一份能照着看的说明:三层模型、HDLC 链路层怎么组帧、ASN.1 怎么描述数据、BER 与 AXDR 怎么把描述变成字节,以及请求电量、电压电流、负荷曲线和时间的真实报文长什么样。搞懂它,你才知道电表屏幕上那个数字,究竟经历了多少道手才到你眼前。

2. 为什么电表通讯要分成三层:DLMS 协议模型在解决什么问题

寄一封挂号信,要经过好几个环节:邮车把信从 A 城拉到 B 城,这是运输;信封上写清收件人、贴单号、签收、丢件重发,这是投递的可靠性;信纸上写什么、按什么格式写,那才是通信双方真正要传递的信息。这三个环节各管一摊,互不干扰,任何一环出问题都能单独定位。DLMS 把电表通讯拆成物理层、链路层、应用层,用的就是同一套思路。

2.1 一头问一头答:Client 与 Server 的分工

DLMS 的通讯双方是明确的主从关系,但不是谁级别高谁级别低,而是谁发起、谁应答。数据请求端是采集器,扮演 Client;数据提供端是电表,扮演 Server。整个流程里,永远是 Client 先开口,Server 只负责回应。

这种设计不是随便定的。一个采集终端往往挂在几十甚至上百块表下面,如果每块表都可以随时主动开口,线路上就会撞车,谁在说话、说的是哪一次请求的回复,全乱套。反过来,让采集器排队一个个问,电表只需要记住「我正在回答谁的问题」,逻辑就简单多了。所以你在报文里会看到源地址、目的地址一对一对出现,Client 的地址永远在发起方那一侧,Server 的地址永远在被问的那一侧。

提示:Client 和 Server 在这里跟「客户端软件」「服务器硬件」没关系,纯粹描述谁发起请求、谁提供数据。采集器不是电脑,电表也不是服务器。

2.2 物理层:只负责把字节送出去

物理层在最底下,干的是最笨的活:打开端口、初始化端口、把链路层交下来的字节发到介质上、从介质上收字节交回链路层、用完关端口。它不关心这些字节代表什么,就像邮车司机不拆信。

DLMS 的一个特点是物理层可以换。同一套上层协议,跑在电话拨号线路上可以,跑在串行通道上可以,跑在别的承载上也可以。手册里明确写了,通常的嵌入式采集装置中,物理层对应的是系统底层驱动部分,一般不归通讯规约管,所以这部分没有展开讲。这个取舍很好理解:换介质的时候,上面两层几乎不用动,真正要改的就是驱动。

对读手册的人来说,这意味着一个很实际的判断——如果你的问题出在「端口打不开」「数据发不出去」,那多半不是协议层的事,先去看硬件和驱动,别一头扎进报文解析里。

2.3 链路层:把「送出去」变成「送到且没错」

链路层是整份手册里篇幅最重的一块,因为它负责所有「不放心」的事。线上跑的电平会受干扰,字节可能翻位;帧可能因为太长被截断;对端可能暂时忙不过来。这些都要在链路层解决。

具体来说,链路层干四件事:地址校验,确认这一帧确实是发给我的;帧长校验,确认收到的字节数和帧头声明的长度对得上;CRC 校验,确认内容没被改坏;长数据帧的拆包组包,把一大块数据切成小片发出去,到对端再拼回来。

链路层内部还分两个子层。LLC 子层是个纯粹的转发工,把 MAC 子层的数据递给应用层,或者反过来,自己不改数据。它的存在感体现在一个很具体的细节上:Client 端发给 Server 的应用层数据,前面要加三个字节0xE6 0xE6 0x00;Server 端回给 Client 的应用层数据,前面要加0xE6 0xE7 0x00。就这三个字节,方向一目了然。MAC 子层才是真正干活的,地址检查、CRC、打包拆包都在这里,用的是 HDLC 高速链路控制协议。

2.4 应用层:谈的究竟是哪一度电

到了应用层,问题变成「我要的那个数,在电表的哪个抽屉里」。电表里的数据项非常多:A 相电压、B 相电流、有功功率、正向有功总电量、某个费率时段的电量、某一天的负荷曲线……光靠一个名字是定位不了的。

DLMS 用两个东西组合定位:class id 和 OBIS 码。class id 说明这一类对象的性质,OBIS 码则是一串数字编号,像图书馆的分类号加书架号,把具体的数据项唯一标出来。Client 每次请求都要带上这两个标识,Server 才知道你要的是哪一个。手册里给出的请求实例——请求电量、请求瞬时量(电压、电流、功率)、请求负荷曲线、请求时间——每一个都是不同的 class id 与 OBIS 组合。

应用层还有一个别处不常见的设计:它不是用一张固定格式的表格来规定报文长什么样,而是用一种抽象语法语言去描述结构,再用编码把结构变成字节。这套东西就是后面要讲的 ASN.1、BER 和 AXDR。好处是抽象、通用、便于移植,代价是第一次看的人会觉得绕。

2.5 一次完整的对话要走六步

把三层串起来,一次典型的数据采集是这样的:

第一步,建立物理层连接,把端口打开,把调制解调器之类的设备初始化好。第二步,建立链路层连接,Client 发 SNRM 帧,Server 回 UA 帧表示链路通了;如果回的是 DM,说明链路根本没接上。第三步,建立应用层连接,这一步在 DLMS 里叫 Negotiation,Client 发 AARQ 帧,Server 回 AARE 帧,双方把版本号、最大接收长度这些参数谈妥。第四步才真正开始读数据,Client 发请求帧,Server 回响应帧,一个数据项一轮。第五步,数据读完了,发一个链路结束帧把这次通讯收尾。第六步,关掉物理端口,从物理上断开。

第五步这里有个容易被忽略的细节:理论上你可以什么都不发,靠 Server 端的超时挂断机制自己收场,但手册明确说,一般应当主动发结束帧。原因很实在——超时是兜底,不是正常流程,指望它等于把主动权交出去,还平白多占一段线路时间。这条经验,实际调试的时候能省下不少莫名其妙的「下一次连不上」。

3. 两个 0x7E 之间:HDLC 链路层到底在忙什么

如果只能记住链路层的一件事,那就记这个:一帧 HDLC 数据,永远以0x7E开头,以0x7E结尾。中间夹着的,就是这一帧要传的全部内容。弄懂中间那段的排布顺序,报文抓下来就不再是天书。

3.1 两个 0x7E 之间夹着什么

帧的结构分两种情形。不带应用层数据的时候,两个0x7E之间依次是:帧类型与帧长、目的地址域、源地址域、控制域、帧校验。这种帧用于建链、断链、确认这类纯链路层的动作。

带应用层数据的时候,中间会多出三项:帧头校验、LLC 帧头、用户数据信息。帧头校验是对前面那几个字段(帧类型与帧长、目的地址、源地址、控制域)单独算一次 CRC,目的是让接收方在还没处理完整个帧的时候就能判断帧头有没有坏。LLC 帧头就是前面提到的三字节方向标识。用户数据信息才是应用层真正要传的内容。

用户数据默认最大 128 字节。这个数不是拍脑袋定的,嵌入式设备内存有限,帧太长了对缓冲区要求高,出错重传的代价也大。如果业务确实需要更长的单帧,可以在 SNRM 帧里协商,把最大信息域长度改大。

下面这条是建链请求帧,逐段拆开看会很直观:

# 一条 SNRM 建链请求帧,按字节拆开看 snrm = ( "7E" # 帧头标志,一帧的开始 "A0 21" # 帧类型与帧长:类型 A(1010),S=0 表示未分片,长度 0x021 = 33 字节 "00 22 00 23" # 目的地址域:服务端地址,四字节结构 "03" # 源地址域:客户端地址,固定一字节 "93" # 控制域:帧计数与 P/F 位 "0B 14" # 帧头校验 HCS,只对前面这些字节算 CRC "81 80" # SNRM 帧标识,参数组长度 "12 05 01 80" # 参数:最大信息域长度(发送)= 128 字节 "06 01 80" # 参数:最大信息域长度(接收)= 128 字节 "07 04 00 00 00 01" # 参数:发送窗口大小 = 1 "08 04 00 00 00 01" # 参数:接收窗口大小 = 1 "65 5E" # 整帧的帧校验 FCS,16 位 CRC "7E" # 帧尾标志 )

A0这个字节值得单独说。它的高四位是1010,也就是十进制的 A,这是 HDLC 的帧类型编号,DLMS 只用类型 3,所以这个位置永远是 A。低四位里的最高位是 S 位,标明这一帧有没有被分片,这里是 0,说明是完整的一帧。剩下的位和下一个字节合起来表示帧长,0x021就是 33,正好是从帧类型字段到帧校验字段的字节数,不含两头的0x7E。

参数部分的写法是「标识 + 长度 + 值」三件套。12是最大信息域长度(发送)的标识,05是后面跟一个字节,01是长度,80是值 128。窗口大小那两项因为要占四个字节,所以长度写04,值写成00 00 00 01。这套写法在 SNRM 和 UA 里是对称的。

服务端回的 UA 帧结构几乎一样,只是源地址和目的地址对调,控制字换成 UA 的类型标识,参数部分把协商结果原样报回来:

# 服务端回的 UA 帧:确认建链,并把谈妥的参数回报一遍 ua = ( "7E" "A0 21" # 同样是 33 字节,都没分片 "03" # 源地址:这回是服务端自己 "00 22 00 23" # 目的地址:换成客户端 "73" # 控制域:UA 帧标识 "28 F0" # 帧头校验 "81 80 12 05 01 80 06 01 80 07 04 00 00 00 01 08 04 00 00 00 01" # 参数协商结果 "53 3B" # 整帧校验 "7E" )

断链过程更简单,因为没有参数要谈。Client 发 DISC 帧,Server 回 UA 表示「收到,已经断开了」,或者回 DM 表示「你发之前我就已经是断开状态了」。这两种回应在排错时意义完全不同:收到 UA 说明链路本来是通的,是这次主动断的;收到 DM 说明链路压根没建立起来过,得回头查 SNRM 那一步。

3.2 地址为什么要长成好几个字节

HDLC 用的是扩展编址技术,规则一句话能说完:地址字节的最低位如果是 0,说明这个地址域还没结束,后面还有字节;如果是 1,说明到此为止。用最低位当「还有没有后续」的标志,是为了省掉单独的长度字段。

Client 端的地址永远只有一个字节。因为只有一字节,按规则最低位必须是 1,剩下七位可用,所以 Client 的地址空间只有 128 个。这个数在小型采集场景够用,一个终端下挂的表不会超过这个量级。

Server 端就复杂了,为了实现一个物理地址对应多个逻辑地址,地址被拆成两部分:upper HDLC address 表示逻辑地址,lower HDLC address 表示物理地址。upper 总是要有,lower 在确认不需要的时候可以不出现。据此有三种结构:一字节,只有 upper;两字节,upper 和 lower 各一字节;四字节,upper 和 lower 各两字节。

手册里提到,某型号电表经实测只有四字节的 Server 地址结构可用。这种细节在标准原文里是找不到的,属于实打实的现场经验——标准允许三种,不代表你手上这块表三种都认。

还有一类保留地址,其中最重要的是广播地址。广播在什么场景用?比如对时、批量下发参数,一条帧让所有表都收到。但广播天然没有应答,可靠性靠重复发送来凑。

3.3 控制字里的计数:防丢包也防重复

控制域这个字节,管的是通讯里的帧计数和特殊帧标识。里面有两个三位字段:RRR 是接收帧计数,SSS 是发送帧计数,外加一个 P/F 位。

计数规则说起来有点绕,但逻辑很朴素。链路层连接建立之后,第一次请求数据(Client 端包括发 AARQ 的那一次),RRR 置 0,SSS 置 0。Server 收到后返回响应,RRR 变成 1,SSS 还是 0。Client 再请求时,RRR 加 1,SSS 也加 1。Server 回的时候,RRR 再加 1 变成 2,SSS 也加 1。就这么一轮一轮往上走。

这套计数解决两个问题:接收方可以判断这一帧是不是重复的(重复帧计数对不上,直接丢),也可以判断中间有没有丢帧(计数跳了号,说明前一帧没收到)。串口调试的时候,如果发现计数突然对不上,基本可以断定中间丢了帧,而不是数据内容本身有问题。

P/F 位是两个方向的语义。Poll 位由 Client 发送,置 1 表示要求 Server 回应,置 0 表示这次不用回。Final 位由 Server 发送,置 1 表示这一轮数据发完了,置 0 表示还有后续。要注意的是,Final 位只有在通讯窗口大于 1 的时候才有意义;窗口大小等于 1 时,Server 每次返回的帧里这一位总是 1。窗口大小默认就是 1,所以大多数场景下你看到 Final 位永远是 1,别以为是设备有问题。

3.4 九种帧类型,各管一段流程

HDLC 定义了多种帧类型,DLMS 用到的有这九种,每一种对应流程里的一个动作:

帧类型全称含义什么时候出现
I信息传输帧携带应用层数据的正常请求与响应
RR准备接收表示已准备好接收下一帧,也用于请求后续分片
RNR接收未就绪相当于别的协议里的忙帧,让对方先别发
SNRM设置正常响应模式建立链路层连接的第一步
UA无编号确认对 SNRM 和 DISC 的肯定响应
DISC断开连接主动结束链路
DM断开模式对 DISC 的响应,或表示链路本就断开
UI无编号信息可用于保持链路,对链路控制没有任何影响
FRMR帧拒绝因为某种不确定的原因拒绝收到的帧

排错时这张表能省很多时间。抓包看到 SNRM 发出去没回应,问题在线路或地址;看到 SNRM 换来 DM,问题在 Server 认为链路不通;看到 FRMR,说明帧格式本身有问题,重点查帧长和 CRC;看到 RNR 反复出现,是对方忙,别急着判定设备坏了。

3.5 128 字节装不下:长数据帧怎么分片

很多情况下,一次请求一次响应装不完所有数据。最典型的是负荷曲线——一条曲线包含几十上百个时间点的记录,随便就超过 128 字节。这时就要启动长数据帧的链路控制流程。

做法是把长帧切成若干短帧,依次发出去,接收端按顺序处理,拼成一个完整的大数据包。分片的标志就是帧类型字段里的 S 位:被切分的帧,S 位置 1。接收端检测到这一位置位,就知道后面还有,要做好接续的准备。

接收端怎么要后续的分片?发 RR 帧。前面说过 RR 是「准备接收」的意思,在这里它变成了「我准备好接下一片了,发过来吧」。所以长帧传输的过程中,会看到 I 帧和 RR 帧交替出现,一直到所有分片收完。

手册里特意点了一句:请求负荷曲线时,一定是使用长数据帧。这不是可选项,是必然。如果你在实现里没做分片处理,读电量、读电压可能都对,唯独一读负荷曲线就出问题——这是很典型的翻车点,而且现象往往是「数据只回来一小段就断了」,很容易误判成通讯不稳定。

帧校验用的是 16 位 CRC。具体的多项式取值和实现,手册没有展开抄,而是指向标准原文的附录 A。这个处理是对的:CRC 实现是标准件,抄错一个位反而害人,不如直接引用。

4. ASN.1、BER、AXDR 三件套:应用层的数据是怎么描述的

前面两层解决的是「怎么把字节安全送到」,应用层要解决的是「这些字节到底是什么」。多数简单协议到这一步就用表格把帧格式钉死了:第 1 字节是什么,第 2 字节是什么。DLMS 没这么干,它先用一种抽象语法把结构描述出来,再用编码把结构变成字节。多出来的这一层抽象,是很多人第一次读手册卡住的地方。

4.1 ASN.1:先给数据画一张结构图

ASN.1 是一种描述数据结构的语言,注意是「描述」,不是「实现」。它只负责说清一个数据帧由哪些项组成、每项是什么类型、哪些项可以省略,至于这些项最后变成什么字节,那是编码的事。

一个用 ASN.1 描述的数据帧,大致长这样:先是名字,然后是若干个数据项,每项带一个 tag,标明类型,后面可以跟 IMPLICIT 或 EXPLICIT,再后面是数据类型,最后可以用 OPTIONAL 标注这一项可以没有。

tag 里的 class 分四种。Universal 表示这个定义在所有 DLMS 应用里含义唯一,属于通用类型;Application 表示含义跟具体应用有关;Private 表示只在某厂家自定义范围内有效;Context-specific 表示含义要结合上下文看,同一个 tag 在不同结构里可能是不同的东西。tag 后面那个数字是这个数据项的标号,相当于句柄,出现在实际的应用数据单元里。

IMPLICIT 和 EXPLICIT 描述的是子数据帧和父数据帧的关系。IMPLICIT 会改变父数据帧的 tag,EXPLICIT 不改变。不注明 IMPLICIT 的项,默认就是 EXPLICIT。

常用的数据类型和它们在编码里的编号,手册给了一张表:

类型编号(十进制)编号(十六进制)
INTEGER202
BIT STRING303
OCTET STRING404
NULL505
OBJECT IDENTIFIER606
SEQUENCE 与 SEQUENCE OF1610
SET 与 SET OF1711
PrintableString1913
T61String2014
IA5String2216
UTCTime2317

这张表不用背,但值得看懂一件事:数据类型是有编号的,编号会出现在编码里。所以字节02出现在报文里,既可能是数据内容,也可能是在说「接下来是一个整数」——区别在于它出现在什么位置。这是读报文时最容易看花眼的地方。

4.2 SEQUENCE 与 CHOICE:一个排队,一个挑一个

复合类型里最常用的两个是 SEQUENCE 和 CHOICE,理解它们,ASN.1 就懂了大半。

SEQUENCE 表示内容是顺序排列的,一个接一个,顺序固定。手册里举的 GET-Request-Normal 就是这个类型,它包含三项:invoke-id-and-priority(调用标识与优先级)、cosem-attribute-descriptor(属性描述符)、access-selection-parameters(选择性访问参数,可选)。前两项必须有,第三项看情况。

CHOICE 表示从几个选项里挑一个,同一时刻只有一项成立。GET-Request 就是一个三选一的结构:普通读、读下一个、带列表读。这三者不会同时出现。

# GET-Request 的 CHOICE 结构,三个分支只能成立一个 GET_Request = { "choice": [ "get-request-normal", # [1] 普通读,最常用 "get-request-next", # [2] 读下一个分片,配合长帧 "get-request-with-list", # [3] 一次读多个对象,省往返 ], }

这个区分很实用。抓到一条请求帧,先看它是 CHOICE 里的哪一支:如果是get-request-next,说明这是接着上一次的续读,你不能当成一次独立请求处理;如果是get-request-with-list,说明这一帧里塞了多个对象,解析时要循环。

4.3 BER:每个字段自带门牌号和长度

ASN.1 只是语法,要写进数据帧还得编码。DLMS 用 ASN.1 描述的协议用 BER 编码,xDLMS 用 AXDR 编码。注意手册里这句提醒:DLMS 中只有 AARQ 和 AARE 这两个数据帧的部分内容使用 DLMS 协议,绝大多数应用层数据通讯用的是 XDLMS。所以两种编码都要会看。

BER 的基本结构是三段:数据标识、数据长度、数据内容,也就是常说的 TLV。内容部分可以再嵌套一层 BER 结构,层层套下去。

数据标识和数据长度可以合成一个 16 位位串来看。最高两位(bit15、bit14)是 class,对应 ASN.1 里的四种 class:Universal 是00,Application 是01,Context-specific 是10,Private 是11。bit13 是结构标志,0表示简单类型,1表示复合类型(数组或结构)。剩下的 bit12 到 bit0 表示数据内容的长度,单位是字节。

这套设计的好处是每个字段都自带说明:看到A1,高两位10说明是上下文相关类型,bit13 是1说明是复合结构,剩下位表示长度;你不用猜,编码自己会说。代价是冗余,同样两个整数,BER 要多花好几个字节说「我是整数」「我多长」。

AARE 帧就是个好例子。它的 tag 是61,对应[APPLICATION 1]——高两位01是 Application class,bit13 为1是复合类型。往下是A1,表示 application-context-name;接着09是长度;再往下06表示这是一个 OBJECT IDENTIFIER;07是内容长度;后面那串才是对象标识符本身的值。再往后A2是协商结果,A3是诊断信息,88是 ACSE 需求,89是机制名,AA是认证值。

最值得留意的是最后那段:BE表示 user-information,类型是 OCTET STRING(Universal 的04被包在上下文 tag 里),0E是长度 14 字节,这 14 个字节里装的就是 xDLMS 的内容。也就是说,一条 AARE 帧里,前面是纯 BER,后面突然切换成 AXDR 语法。这种「一段报文里两种编码并存」的结构,是读 DLMS 应用层最反直觉的地方——它不是全程一种编码,而是分段的。

4.4 AXDR:把门牌号省掉,只报数

AXDR 是对 XDR(外部数据表示法)编码的扩展,用在 xDLMS 上。它和 BER 最大的区别是:不写 tag,不写长度,只写值。

为什么可以省?因为 ASN.1 已经把结构描述清楚了。解析方手上有那份「结构图」,知道第一个字段是什么类型、第二个字段是什么类型,按顺序往下读就行,不需要每一段都自我说明。这就像两个人对暗号,事先约好第一句说名字、第二句说年龄,那就不用每句前面都加「姓名:」「年龄:」。

省掉 tag 和长度,报文明显变短,在带宽紧张、帧长受限的电力载波和串口场景里,这个收益是实打实的。代价是解析完全依赖双方对结构的理解一致——结构对不上,整条报文就全乱,不像 BER 还能靠 tag 猜。

手册里给的 AARQ 部分例子很能说明问题。xDLMS-Initiate.request 这个结构包含:dedicated-key(可选)、response-allowed(默认为真)、proposed-quality-of-service(可选)、proposed-dlms-version-number、proposed-conformance、client-max-receive-pdu-size。AXDR 编码时,第一字节01是 dlms pdu 标识,然后直接开始排值,没有哪个字节跳出来说「注意,下面是版本号」。

4.5 两种编码摆在一起看

同样两个整数,值分别是0x1234和0x5678,两种编码的结果差异一目了然:

# 两个整数,值分别为 0x1234 和 0x5678 ber_bytes = "30 08 02 02 12 34 02 02 56 78" # 序列标识 + 序列长度 + 两个整数的标识与长度 axdr_bytes = "12 34 56 78" # 只有值本身,结构靠语法约定

BER 那串里,30是 SEQUENCE 的标识,08是后面内容的长度,02是 INTEGER 的标识,02是整数长度,12 34才是值;第二个整数重复一遍。AXDR 那串就只有四个字节,值挨着值。

这条对比解释了一个经常被问的问题:为什么同一条请求,用 DLMS 和 xDLMS 抓出来的报文长度差那么多。不是设备实现不一样,是编码方式本来就有冗余度差异。看报文时如果发现长度对不上预期,先确认这一段是 BER 还是 AXDR,十有八九问题就出在这儿。

5. 五组最容易搞混的概念:DLMS 与 DMLS、BER 与 AXDR 的区别在哪

概念多不可怕,可怕的是两个概念长得像、名字像、用起来也像,但实际不是一回事。下面五组是初次接触这套协议时最容易踩的坑。

5.1 易混点一:DLMS 和 DMLS 是不是两个协议

这两个写法在中文资料里都出现过,很容易让人以为是两套不同的东西,甚至有人以为是不同版本。

混淆的原因很直接:四个字母,顺序一换,肉眼几乎不敏感,而且中文输入法下打错一个字母根本不会有提示。加上不少二手资料在转抄时没有校对,错写就这么传开了。

怎么区分:以标准编号为准。这套协议族的正式身份是 IEC 62056 系列,涵盖物理层、链路层、应用层、对象标识等多个部分,业内统一称呼是 DLMS。中文手册标题里写 DMLS,指的是同一套内容,不是另一个协议,也不是另一个版本。判断依据不看标题怎么写,看内容讲的是不是 IEC 62056 那套三层模型、HDLC 链路、COSEM 对象——是,就是同一个东西。搜索资料时两个写法都可以试,但心里要清楚,它们指向同一份标准。

5.2 易混点二:DLMS 是不是就等于 xDLMS

看到「DLMS」和「xDLMS」两个词,很多人会以为后者是前者的升级版,或者是一套更高级的扩展。

混淆的原因在于命名。两个词只差一个字母 x,而且经常在同一份文档里交替出现,看起来像同义词的不同写法。

怎么区分:它们是父子关系,不是新旧关系。DLMS 是整个协议族的统称,包含物理层、链路层、应用层全部内容;xDLMS 特指应用层里用 AXDR 编码的那一部分服务。手册里有一句关键提醒:DLMS 中只有 AARQ 与 AARE 这两个数据帧的部分内容使用 DLMS 协议,绝大多数应用层数据通讯使用 XDLMS。所以一次完整的采集过程里,建链阶段用的是 DLMS 那套 BER 编码,读数据阶段用的是 xDLMS 那套 AXDR 编码,两者在同一次通讯里先后登场。搞不清这个,就会在解析 AARE 帧时困惑:为什么前半段和后半段的编码规则完全不一样。

5.3 易混点三:BER 和 AXDR 是不是一回事

两个都是编码,都用来把 ASN.1 描述的结构变成字节,很容易被当成同一件事的两种叫法。

混淆的原因是功能重叠。它们解决的是同一个问题:抽象语法怎么落地成字节流。而且两者都基于 ASN.1 语法描述,看结构定义时完全一样。

怎么区分:看有没有自描述。BER 是自描述的,每个字段前面带 tag 和长度,解析方不需要事先知道结构也能一段段读下去;AXDR 是约定式的,只排值,解析方必须手里有结构图才能读。落到报文上,BER 明显更长更啰嗦,AXDR 紧凑得多。落到使用场景上,AARQ 和 AARE 用 BER,xDLMS 的数据请求响应用 AXDR。抓到一条报文,先数一数字节里有没有大量02、04、06这种类型标识反复出现,有就是 BER,纯值排列就是 AXDR。

5.4 易混点四:链路连上了是不是就能读数据

调试时经常遇到这种情况:SNRM 发出去,UA 回来了,链路看着是通的,可一发数据请求就没反应或者报错。

混淆的原因在于「连接」这个词被过度使用了。这套协议里有两层连接,链路层连接和应用层连接,很多人只记住了前面那个。

怎么区分:看流程走到哪一步。SNRM 换来 UA,只说明链路层通了,地址对、CRC 对、双方能收发帧;但应用层还没谈。DLMS 规定,链路层连接建立之后,还要建立应用层连接才能进行数据通讯,这个过程叫 Negotiation,Client 发 AARQ,Server 回 AARE,双方把版本号、最大接收长度这些配置参数谈妥。跳过这一步直接发数据请求,Server 是不认的。所以完整的顺序永远是:物理层连接、链路层连接、应用层连接、数据通讯、释放链路、断开物理层。中间少一步,后面全白费。这条在排错时特别好用——如果 AARQ 都没发就直接发 GET 请求,问题不在设备,在自己流程漏了。

5.5 易混点五:用户数据是不是想多长就多长

用户数据默认最大 128 字节,这个数字在手册里出现过好几次,容易被当成硬性上限,也容易被当成随便能改的软参数。两种理解都不太对。

混淆的原因是它既是默认值,又是可协商值。默认 128,但可以在 SNRM 里谈。

怎么区分:分两层看。单帧层面,128 字节是默认的最大信息域长度,可以在 SNRM 帧里通过参数协商改大,UA 帧会把双方谈妥的值回报一遍;应用数据层面,超过单帧容量的数据要靠长数据帧分片传输,靠 S 位标记、靠 RR 帧要后续,这跟单帧长度上限是两码事。请求负荷曲线时一定是长数据帧——这句话的意思是,哪怕你把单帧长度谈得再大,负荷曲线的数据量也常常装不下,分片机制必须实现。把这两层混在一起,就会得出「把 128 改大就不用分片了」的错误结论,然后在现场被负荷曲线教做人。

6. 什么时候该翻这份中文手册:几个判断边界和踩坑提醒

这份手册的定位很清楚:它是一份应用导向的说明,目标是让做电能量数据采集终端的人,能照着它实现和电表之间的通讯。它不是标准的完整翻译,手册自己也承认没有包含协议族的全部内容,但覆盖了应用中可能出现的大多数情况。

所以判断该不该用它,看三件事就够了:

你的处境适不适合翻原因
要对接电表,读电量、瞬时量、负荷曲线、时间适合手册给了这四类请求的实际报文范例
报文抓下来了,但看不出哪一段是什么适合帧结构、地址、控制字、编码都有逐段解释
建链成功但读不到数,怀疑流程漏步适合六步流程讲得很明确,能对照排查
想搞懂标准里某个边缘参数的精确定义不适合手册明确说没覆盖全部,得回原文
要设计一套全新的通讯协议不适合这是特定领域的协议说明,不是通用协议设计教材
需要某个具体型号设备的完整寄存器手册不适合手册只提了个别实测结论,不替代设备文档

还有一个边界要说清楚:手册讲物理层时明确跳过了底层驱动的细节,理由是那部分一般不受通讯规约控制。这不是省略,是划界——如果你的问题出在端口打不开、线路不通、电平不对,翻这本手册帮不上忙,得去看硬件和驱动。同理,它给的是协议层面的说明,不是某台设备的操作指南。

我印象最深的一次,是排查一条负荷曲线读不回来的问题。电量能读、电压能读、时间也能读,唯独负荷曲线每次只回来一小段。当时第一反应是线路不稳定,换了线、降了波特率、加了重试,全都没用。后来回头逐字看链路层那一节,才发现问题根本不在稳定性——负荷曲线一定是长数据帧,要分片,要靠 RR 帧去要后续分片。我的实现里压根没处理 S 位,收到第一片就当整条数据收完了。从那以后我每次对接新表,都强制走一遍完整流程:先确认 SNRM 换回的是 UA 不是 DM,再确认 AARQ 和 AARE 谈完,最后拿负荷曲线做一次长帧分片的验证,三步都过了才认为链路真的通了。这套笨办法救过我好几次,也希望帮到你。

如果你手上正好在做电表数据采集,或者被一串十六进制报文卡住过,这份中文手册里那些逐段拆解的帧例子和编码对照,大概能省下你不少对着标准原文猜的时间。

本文还有配套的精品资源,点击获取