DLMS/COSEM协议编解码杂谈
DLMS协议编解码论述
DLMS/COSEM(Device Language Message Specification/Companion Specification for Energy Metering)是一种用于远程读取电力、燃气、水和热能表的通信协议。在DLMS/COSEM协议中,数据被编码为一系列的数据类型,每种数据类型都由一个标签(Tag)和一个长度(Length)字段组成。
-
标签(tag)是一个字节,用于表示数据的类型。
例如,八位字节字符串(octet-string)的标签是0x09,数组(array)的标签是0x01,结构(structure)的标签是0x02,等等。 -
长度字段表示数据的长度。
在DLMS/COSEM协议中,长度字段可以是一个或多个字节,具体取决于数据的长度。如果数据的长度小于或等于127字节,那么长度字段就是一个字节,其值就是数据的长度。如果数据的长度大于127字节,那么长度字段就是多个字节,第一个字节的最高位是1,其余位表示长度字段的字节数,后续的字节表示数据的长度。 -
数据本身紧跟在长度字段之后。
对于八位字节字符串,数据就是字符串的内容。对于数组和结构,数据是一个或多个编码为DLMS/COSEM数据类型的元素。
例如,一个包含三个字节(0x01, 0x02, 0x03)的八位字节字符串在DLMS/COSEM协议中会被编码为:0x09 0x03 0x01 0x02 0x03。其中,0x09是标签,表示这是一个八位字节字符串;0x03是长度字段,表示字符串的长度是3字节;0x01, 0x02, 0x03是数据,即字符串的内容。
解码DLMS/COSEM数据类型的过程就是编码过程的逆过程。
- 首先,读取标签来确定数据的类型;
- 然后,读取长度字段来确定数据的长度;
- 最后,读取数据本身。
DLMS/COSEM协议中,数据类型的标签(tag)是一个字节,用于表示数据的类型。以下是一些常见的数据类型和对应的标签:
- Null :0x00
- Array:0x01
- Structure:0x02
- Boolean:0x03
- Bit-string:0x04
- int32:0x05
- uint32:0x06
- Octet-string:0x09
- Visible-string:0x0A
- UTF8-string:0x0B
- BCD:0x0D
- int8:0x0F
- int16:0x10
- uint8:0x11
- uint16:0x12
- Compact array:0x13
- int64:0x14
- uint64:0x15
- enum:0x16
- float32:0x17
- float64:0x18
- DateTime:0x19
- Date:0x1A
- Time:0x1B
编码过程如下:
-
首先,将数据类型的标签(Tag)添加到编码结果中。
-
然后,根据数据的长度(Length),将长度编码为一个或多个字节,并添加到编码结果中。
-
最后,将数据本身添加到编码结果中。
例如,如果我们要编码一个长度为3的八位字节字符串"ABC",过程如下:
-
八位字节字符串(Octet-string)的标签是0x09,所以我们首先添加0x09到编码结果中。
-
字符串的长度是3,所以我们添加0x03到编码结果中。
-
最后,我们将字符串"ABC"的ASCII值(0x41, 0x42, 0x43)添加到编码结果中。
所以,编码结果是:0x09 0x03 0x41 0x42 0x43。
注意,对于一些数据类型(如数组和结构),数据本身可能包含多个子元素,每个子元素都需要按照上述过程进行编码。
在DLMS/COSEM协议中,数组和结构体的编码方式与其他数据类型类似,但是它们的数据部分包含多个元素,每个元素都是一个DLMS/COSEM数据类型。
对于数组和结构体,长度字段表示元素的数量,而不是字节的数量。
以下是一个数组的编码示例:
假设我们有一个数组,包含两个元素:一个INT8值3和一个八位字节字符串"AB"。
-
首先,我们添加数组的标签0x01到编码结果中。
-
然后,我们添加元素的数量2到编码结果中。
-
接下来,我们将每个元素编码为DLMS/COSEM数据类型,并添加到编码结果中。
3.1. 首先,我们添加INT8值3的编码结果:0x0F 0x03。
3.2. 然后,我们添加**八位字节字符串"AB"**的编码结果:0x09 0x02 0x41 0x42。
所以,整个数组的编码结果是:0x01 0x02 0x0F 0x03 0x09 0x02 0x41 0x42。
结构体的编码方式与数组类似,只是标签不同(结构体的标签是0x02)。
解码数组和结构体的过程与编码过程相反。
首先,读取标签来确定数据类型是数组还是结构体。然后,读取长度字段来确定元素的数量。最后,依次解码每个元素。
ToTLVString()和TLVtoEntity(std::string s_string)这两个函数的主要作用是实现TLV(Tag-Length-Value)格式的编码和解码。
-
ToTLVString()函数的作用是将实体的各个属性编码为TLV格式的字符串。TLV格式是一种编码格式,其中T代表标签(Tag),L代表长度(Length),V代表值(Value)。在这个函数中,每个属性都被编码为一个TLV元素,然后所有的TLV元素被连接在一起,形成一个字符串。
-
TLVtoEntity(std::string s_string)函数的作用是将一个TLV格式的字符串解码为实体的各个属性。这个函数的参数s_string是一个TLV格式的字符串。在这个函数中,首先从字符串中提取出一个TLV元素,然后根据这个元素的标签(Tag)确定它代表哪个属性,然后根据这个元素的长度(Length)和值(Value)来解码这个属性的值。