DLMS协议开发指引

1 进入DLMS模式

某些电表一开始未进入DLMS传送模式,需要进行切换。按照IEC62056-21协议模式E的规定,先发送以下内容:

  1. 典型的串口设置为:波特率300,校验位Even,数据位7。(也可校验位无,数据位8)

  2. 开启传送请求信息:

    / ? ! CR LF
    2FH 3FH 21H 0DH 0AH
  3. 协议和波特率调整:

    ACK V Z Y CR LF
    06H 32H 35H 32H 0DH 0AH

    V:ASCII码的2,代表使用HDLC。

    Y:ASCII码的2,代表使用HDLC。

    Z:ASCII码,波特率选择。

    • 1:600Bd

    • 2:1200Bd

    • 3:2400Bd

    • 4:4800Bd

    • 5:9600Bd

    • 6:19200Bd

  4. 根据以上设置,此时波特率可调整为9600,进入DLMS数据传送。

2 DLMS概况

2.1 基本的传送流程

使用DLMS传送数据,需要先建立链路层连接,建立应用层连接,再进行数据读取或写入,最后关闭连接,如下图所示:

2.2 分层结构

DLMS协议传送的每一帧数据都是一个分层结构,典型的结构如下图所示:

基本上可以理解为,在原数据的头尾加上一些数据,即为进入下一层。

3 HDLC层

3.1 数据结构

HDLC层数据结构如下所示:

7EH Frame Format Frame Length Dest Addr Src Addr Control HCS Information FCS 7EH
1 1 1 n n 1 2 n 2 1

Frame Format:A0H或A8H,A0H表示后面没有后续帧,A8H表示数据未完待续。实际上这个字节有一部分用来描述长度,但一般长度不会超过256,所以可以简单理解。

Frame Length:不包括头尾两个7EH的长度。

HCS和FCS都是校验和,HCS从Frame Format到Control,FCS从Frame Format到Information。具体算法见附录1。

其他内容在下面详述,Dest Addr和Src Addr在3.2节,Control在3.3节。Information是应用层数据,从第8章开始讲述。

3.2 地址

HDLC地址有三种表示方式,分别是1字节、2字节和4字节。

  • 1字节

    编码地址=逻辑设备地址*2+1

  • 2字节

    编码地址(第1字节)=逻辑设备地址*2

    编码地址(第2字节)=物理设备地址*2

  • 4字节

    编码地址(第1字节)=逻辑设备地址高位*2

    编码地址(第2字节)=物理设备地址高位*2

    编码地址(第3字节)=逻辑设备地址低位*2+1

    编码地址(第4字节)=物理设备地址低位*2+1

比较典型的目标地址是1,源地址是16。都用1字节表示方式。

3.3 控制码

3.3.1 帧类型

常用帧的格式如下:

帧类型 帧结构
I R R R P S S S 0
RR R R R P 0 0 0 1
RNR R R R P 0 1 0 1
SNRM 1 0 0 P 0 0 1 1
DISC 0 1 0 P 0 0 1 1
UA 0 1 1 F 0 0 1 1
DM 0 0 0 F 1 1 1 1
FRMR 1 0 0 F 0 1 1 1
UI 0 0 0 P 0 0 1 1

RRR为接收序号,SSS为发送序号。P为是否允许回应,一般为1。F为是否发送结束,在窗口大小超过1的时候使用。

RRR和SSS初始都为0。一般情况下,发送数据时,SSS需要加1。接收数据时,RRR会被加1。下次再发送数据,相对于前一帧,RRR加1。如此反复。当RRR或SSS超过7时,复位为0。

在2.1节可以看到各种帧的传送方式,而数据的读取和写入使用I帧,如果需要分段续传则使用RR帧。

3.3.2 控制码计算

为了描述方便,进行以下定义:SF为上次发送的帧,RF为上次收到的帧。SF_R表示SF的RRR部分,SF_S表示SF的SSS部分。RF同理。那么对于本次要发送的帧,其RRR和SSS的计算如下表所示:

SF类型 RF类型 本次要发送的帧类型 RRR SSS
I I I SF_R+1 SF_S+1
I RR I SF_R RF_R
RR I I RF_S+1 RF_R
/ I RR RF_S+1 0
/ RR RR SF_R 0

4 LLC层

LLC层数据结构如下所示:

E6H E6H/E7H 00H 应用数据
1 1 1 n

第2个字节,下行为E6H,上行为E7H。也就是发送是E6H,收到的回复是E7H。

5 多层数据组合

本章讲述如何拼合出一条最终发送的命令。在2.2节中,可以看到所有指令都是一个分层结构。

应用层的数据将会在后面介绍,我们现在假定应用层数据如下所示:

应用层数据需要放在LLC层中,也就是在前面加上一个LLC头,变为:

LLC头 E6 E6 00
应用层数据 05 01 02 2B C8

上面的数据再放到HDLC层中。

假设目标地址是1,源地址是16,那么Dest Addr=03H,Src Addr=21H。

上面字节串的长度是8,整个HDLC帧的长度(除去头尾7EH)将会是17,也就是11H。

控制码我们假定上次接收的是32H,那么这次发送,SSS加1,也就是34H。

HCS和FCS我们暂不计算。

那么HDLC帧将会如下所示:

HDLC头 7E A0 11 03 21 34 HCS HCS
LLC头 E6 E6 00
应用层数据 05 01 02 2B C8
HDLC尾 FCS FCS 7E

上面的字节串就是要发送的内容。

6 建立连接

6.1 建立链路连接

建立链路连接,需发送SNRM帧。SNRM帧可以没有用户数据,也就是在HDLC层,HCS和Information皆为空,Control为SNRM(93H)。

正确的回复是一个UA帧,也就是回复内容的Control是UA(73H)。回复中会带有通信数据长度和窗口大小等信息。

以下是示例报文:

发送:

7E 帧头
A0 07 无后续帧,长度7
03 目标地址1
21 源地址16
93 控制码:SNRM
0F 01 HCS
7E 帧尾

接收:

7E 帧头
A0 1E 无后续帧,长度30
21 源地址16
03 目标地址1
73 控制码:UA
C3 7A HCS
81 格式标识
12 长度18
05 01 发送时应用层最大长度
80 最大长度128
06 01 接收时应用层最大长度
3E 最大长度63
07 04 发送窗大小
00 00 00 01 一对一帧关系
08 04 接收窗大小
00 00 00 01 一对一帧关系
07 22 FCS
7E 帧尾

6.2 建立应用连接

建立应用连接的内容相当复杂,包含了是否加密、使用LN还是SN、读写权限等参数。为了简单起见,使用默认配置即可。

发送的AARQ和回复的AARE都是I帧。

以下是示例报文:

发送:7E A0 2B 03 21 10 FB AF E6 E6 00 60 1D A1 09 06 07 60 85 74 05 08 01 02 BE 10 04 0E 01 00 00 00 06 5F 1F 04 00 20 1E 5D FF FF E8 3F 7E
接收:7E A0 37 21 03 30 6C 7C E6 E7 00 61 28 A1 09 06 07 60 85 74 05 08 01 02 A2 03 02 01 00 A3 05 A1 03 02 01 00 BE 0F 04 0D 08 00 06 5F 1F 04 00 00 02 00 09 60 FA 00 BE A3 7E

7 应用层概况

7.1 LN和SN

读取或写入一个物理量,需要给物理量一个编码。这个编码的表示方式有两种:

  • 逻辑名LN,使用ClassId和OBIS编码表示,同一物理量此编码相同。

  • 短名SN,一个二字节数,代表了一个地址,同一物理量在不同设备上有可能有不同的地址。

7.1.1 ClassId

ClassId具体内容见IEC62056-62,常用的ClassId如下表所示:

ClassId 数据类别
1 Data
3 Register
4 Extended register
5 Demand register
6 Register activation
7 Profile generic
8 Clock
9 Script table
10 Schedule
11 Special days table
12 Association SN
15 Association LN
17 SAP assignment
20 Activity calendar
21 Register monitor
22 Singl action schedule
26 Utility

7.1.2 OBIS

OBIS具体内容见IEC62056-61,它是一个由六字节组成的编码,表示成ABCDEF。

  • A:1为与电有关的对象,其他有与气体、冷水、热水有关的对象,但我们一般只会用到与电有关。

  • B:通道,0是无指定通道,1是通道1,2是通道2,以此类推。一般用0或1都可以。

  • C:具体的物理量,例如是正向有功功率、反向视在功率、电流、电压等。

  • D:物理量的统计方法,有瞬时值、积分值、最大值等。C和D的组合示例如下:

    C 含义 D 含义 总编码 总含义
    1 正向有功功率 7 瞬时值 1.7 正向有功功率
    8 时间积分值 1.8 正向有功电能
  • E:0为总量,1为费率1,2为费率2,以此类推。

  • F:一般不用,为255。

常用的OBIS编码见附录2。

7.2 数据类型

在DLMS中一个数据可以是各种类型,包括数字、字符串、数组、结构体等。以下是常用的类型:

编码 类型 字节数
01H Array 指定元素个数
02H Structure 指定成员个数
03H Boolean 1
05H Int32 4
06H UInt32 4
09H Octet-string 指定长度
0AH Visible-string 指定长度
0FH Int8 1
10H Int16 2
11H UInt8 1
12H UInt16 2

需要指定长度的类型,后续1个字节是长度,否则是数据内容。

例如,int a=7的表示为:

05 00 00 00 07

string b="book"的表示为:

0A 04 62 6F 6F 6B

byte[] c=[0x04,0x05]的表示为:

01 02 11 04 11 05

struct d={"fox",0x02}的表示为:

02 02 0A 03 66 6F 78 11 02

8 读取SN数据

8.1 请求帧

为了简单起见,只读一个地址。请求帧的内容如下:

读要求 读数量 访问选择 地址
05H 01H 02H 2字节

访问选择02H表示没有附加参数,默认使用。

8.2 回复帧

收到的回复帧内容如下:

读响应 响应数量 成功 具体数据
0CH 01H 00H

具体数据的内容是数据类型+数据值,例如一个Int32类型,类型码是06H,接下来的4个字节描述数值。不同的数据类型可参考7.2节。

8.3 分段数据

当数据过长,需要分段传送时,数据会通过多帧进行传送。第一帧会是完整的数据,后续帧则不会包含LLC头和回复头。如下所示:

第一帧 HDLC头 LLC头 回复头 数据1 HDLC尾
后续帧 HDLC头 数据2 HDLC尾

解析数据时,需要先把数据接收完,然后拼合所有数据,再进行解析。

9 写入SN数据

9.1 请求帧

为了简单起见,只写一个地址。请求帧的内容如下:

写要求 写数量 访问选择 地址 数据数量 具体数据
06H 01H 02H 2字节 01H

访问选择02H表示没有附加参数,默认使用。

9.2 回复帧

收到的回复帧内容如下:

写响应 响应数量 成功
0DH 01H 00H

9.3 响应结果

回复帧中的响应结果解析如下:

响应码 解析
00H success
01H hardware-fault
02H temporary-failure
03H read-write-denied
04H object-undefined
09H object-class-inconsistent
0BH object-unavailable
0CH type-unmatched
0DH scope-of-access-violated
0EH data-block-unavailable
0FH long-get-aborted
10H no-long-get-in-progress
11H long-set-aborted
12H no-long-set-in-progress
FAH other-reason

10 获取LN数据

10.1 请求帧

请求帧的内容如下所示:

获取请求 Normal模式 Invoke-Id-And-Priority 结果类型 ClassId OBIS 属性
C0H 01H 81H 00H 1字节 6字节 02H 00H

前3个字节可以固定为表中数值,一般不需修改。结果类型00H为数据,01H为访问结果。属性02H为数值,03H是单位和比率。

数值、单位和比率的关系举例:

数值 比率 单位 数据
263788 -3 m3 263.783 m3
593 3 Wh 593k Wh

常见单位编码列表见附录3。

10.2 回复帧

回复帧的内容如下所示:

获取回复 Normal模式 Invoke-Id-And-Priority 结果类型 数据内容
C4H 01H 81H 00H

具体数据的内容是数据类型+数据值,例如一个Int32类型,类型码是06H,接下来的4个字节描述数值。不同的数据类型可参考7.2节。

附录1 校验和算法

以下为校验码计算代码:

public static ushort CountFCS16(byte[] buff, int index, int count)
{
    int FCS16 = 0xFFFF;
    for (int pos = index; pos < index + count; ++pos)
    {
        FCS16 = ((FCS16 >> 8) ^ FCS16Table[(FCS16 ^ buff[pos]) & 0xFF]) & 0xFFFF;
    }
    FCS16 = ~FCS16;
    FCS16 = ((FCS16 >> 8) & 0xFF) | (FCS16 << 8);
    return (ushort)FCS16;
}
​
private static readonly ushort[] FCS16Table = {
    0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
    0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
    0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
    0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
    0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
    0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
    0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
    0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
    0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
    0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
    0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
    0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
    0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
    0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
    0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
    0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
    0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
    0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
    0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
    0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
    0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
    0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
    0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
    0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
    0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
    0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
    0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
    0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
    0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
    0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
    0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
    0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
};

附录2 常见OBIS编码

在以下的编码中,只列出CD部分,A部分为1,B部分为0或1,E部分为0,F部分为255。

物理量 相位 方向 有无功 编码
电能 有功 15.8
正向 有功 1.8
无功 3.8
视在 9.8
反向 有功 2.8
无功 4.8
视在 10.8
A 正向 有功 21.8
无功 23.8
视在 29.8
反向 有功 22.8
无功 24.8
视在 30.8
B 正向 有功 41.8
无功 43.8
视在 49.8
反向 有功 42.8
无功 44.8
视在 50.8
C 正向 有功 61.8
无功 63.8
视在 69.8
反向 有功 62.8
无功 64.8
视在 70.8
功率 有功 15.7
正向 有功 1.7
无功 3.7
视在 9.7
反向 有功 2.7
无功 4.7
视在 10.7
A 正向 有功 21.7
无功 23.7
视在 29.7
反向 有功 22.7
无功 24.7
视在 30.7
B 正向 有功 41.7
无功 43.7
视在 49.7
反向 有功 42.7
无功 44.7
视在 50.7
C 正向 有功 61.7
无功 63.7
视在 69.7
反向 有功 62.7
无功 64.7
视在 70.7
电压 A 32.7
B 52.7
C 72.7
92.7
电流 A 31.7
B 51.7
C 71.7
91.7
电网频率 14.7

附录3 常见单位编码

以下列出电学常见单位编码:

代码 单位
27 W
28 VA
29 var
30 Wh
31 Vah
32 varh
33 A
35 V
44 Hz

猜你喜欢

转载自blog.csdn.net/lweiyue/article/details/132035550
今日推荐