Documente Academic
Documente Profesional
Documente Cultură
Page 1 of 18
The Problem
We want to write a program able to read the value of the active energy register of an electricity-meter of this kind...
... and the DLMS/COSEM protocol. The problem is not trivial, and to be able to design a solution, we first need to know more about some of the main concepts of the COSEM/DLMS standard.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 2 of 18
According to the Standard, all physical devices have to host a special logical device called the management logical device, with the predefined address 1. The management logical device itself may contain a lot of information, but, at the minimum, it has to contain a description of all the logical devices available in the physical meter, with their logical addresses and names.
So, how do we read the value of the active energy out of the logical devices?
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 3 of 18
It appears that the required OBIS code should be (in the standard notation) 1.x.1.8.0.255, for example 1.1.1.8.0.255. How can we know if such an object exists in our electricity-meter?
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 4 of 18
The data exchange between our program and the meter uses the client-server model. Our application is the client and the meter plays the role of server. The client sends requests and the server answers responses. For example, our application would send something like ''read the object 1.0.1.8.0.255'' and the server would answer ''1789.8 kWh''. Still, before being able to send such requests, the client has first to establish a connection with the other side. But, as we have seen, the other side is not just a physical device but a collection of logical devices, therefore an addressing system is needed.
The addressing
In the COSEM/DLMS communication framework, each side of the connection has an address. By definition, the client address is a byte value. Furthermore, the value of the client address determines also the real nature of the client. The Standard states that a client with address 16 (decimal) is a public client. There can be other kind of clients, for example a Data Collection System, a Manufacturer, a Consumer, though, the addresses of those clients are not defined in the Standard. The server address is the doublet: { address of the logical device, address of the physical device }.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 5 of 18
The lowest layer is the physical layer, it is the layer 1 in the OSI model. In our practical example, it boils down to a simple (3-wires) serial cable between a COM port of our PC and the appropriate connector of our meter. The connection between the two peers layers is accomplished...by mechanically connecting the two sides.
From here...
...
...
...to there.
Sends data to the peer layer and receives the response from the peer layer.
HDLCDisconnect();
Closes the connection. There are several parameters to set before calling the HDLC routines, the most important are the HDLC-addresses (also called MAC addresses). The client MAC address is a byte value, it identifies the client. We will use 16 (decimal) which means public client. The server (or meter) MAC address is divided in two parts, the upper part is the logical device address, and the lower part is the physical device address. In some cases, the lower part can be omitted. For example, in a point-to-point configuration with a single meter, we don't care about the physical address of the meter. Note, however, that this feature is not implemented by all manufacturers.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 6 of 18
To summarize: on the HDLC layer, the client address is always a byte, the server address consists of two parts and there are three variants: One byte addressing. There is just an upper address. It is a byte value. Two bytes addressing. There is an upper address on 1 byte and a lower address on 1 byte. Four bytes addressing. There is an upper address on 2 bytes and a lower address on two bytes. Note that not all manufacturers support the three variants. Here is an example of the HDLC-layer parameters as set in the XmlDemo application: We set 10 (hexa) for the client address (public client) and 1 for the logical device address (management logical device). The lower address is not specified (one byte addressing).
In XmlDemo, when we click ''Connect'', the library routine HDLCConnect sends a SNRM-frame to the server. The server responds with an UA-frame. The next picture shows the resulting frame exchange. The green frame is the SNRM-frame and the red is the UA-frame:
This frame exchange establishes the HDLC layer connection. From now on, data frames (I-Frame) can be exchanged between the two sides.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 7 of 18
the ASN.1 notation. However, in our tools and libraries, the specific instances of requests and responses are presented in a XML notation, like in the following:
<ReadRequest Qty="0001" > <ParameterisedAccess> <VariableName Value="60E8" /> <Selector Value="01" /> <Parameter> <Structure Qty="0004" > <Structure Qty="0004" > <LongUnsigned Value="0008" /> <OctetString Value="0000010000FF" /> <Integer Value="02" /> <LongUnsigned Value="0000" /> </Structure> <OctetString Value="07D8021DFF0C130DFF8000FF" /> <OctetString Value="07D90A1DFF0B3622FF8000FF" /> <Array Qty="0000" > </Array> </Structure> </Parameter> </ParameterisedAccess> </ReadRequest>
This is a ReadRequest element. How can we send such a request to the meter? Of course, the XML lines cannot be sent directly. They must be first encoded into a byte sequence known as a protocol data unit (PDU), according to the rules specified in the Standard. We will not go into the details of the encoding. Instead, in our target program, we will use our xmlpdu library, that implements the encoding and decoding procedures. The library exposes two routines:
XmlToPdu(XmlLines, Pdu);
Decodes the PDU into a sequence of XML lines. For example, the encoded PDU of the above ReadRequest is this bytes sequence...
05 01 04 60 E8 01 02 04 02 04 12 00 08 09 06 00 00 01 00 00 FF 0F 02 12 00 00 09 0C 07 D8 02 1D FF 0C 13 0D FF 80 00 FF 09 0C 07 D9 0A 1D FF 0B 36 22 FF 80 00 FF 01 00
...that we send to the meter using the HDLC layer, by calling HDLCSendReceive() and pass the bytes sequence as parameter. Then, when the meter responds with another bytes sequence, we translate it back to XML with PduToXml(). Finally, we parse the received XML using any appropriate method. To summarize: all request/response exchanges follow these five steps: Have the XML of a request. Encode the XML to PDU using XmlToPdu(). Send the PDU to the other side and get a response PDU, using HDLCSendReceive(). Decode the response PDU into XML with PduToXml(). Finally, parse the XML of the response.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 8 of 18
To come back to the problem: what requests will we have to send and what responses will we expect, if we want to read the energy register? The AssociationRequest The AssociationRequest is the first request that we have to send in order to establish the Association. It looks like this:
<AssociationRequest> <ApplicationContextName Value="SN" /> <InitiateRequest> <ProposedDlmsVersionNumber Value="06" /> <ProposedConformance> <ConformanceBit Name="ParametrizedAccess" /> <ConformanceBit Name="MultipleReferences" /> <ConformanceBit Name="Write" /> <ConformanceBit Name="Read" /> </ProposedConformance> <ProposedMaxPduSize Value="FFFF" /> </InitiateRequest> </AssociationRequest>
The exchanges with the meter can take place in two flavors called ''contexts'', that differ in the way the data is addressed (or referenced). In the logical name reference context (or LN) the data is referenced by the logical name (OBIS code). In the short name reference context (or SN) the data is referenced (addressed) by a unique 16 bit integer. Furthermore, the services used to access the data are different in each context. In LN context, the relevant services are Get (to read data) and Set (to write data) and in SN context the services are Read and Write. The context cannot be freely chosen by the client. Rather, it is fixed by the meter's manufacturer, it is a characteristic of its implementation. Therefore, the meter's context must be known beforehand.
The Proposed Dlms Version Number
This value is currently fixed to 6, it is the version number of the DLMS protocol.
The Proposed Conformance
This is a list of capabilities that we are expecting the meter to have. In our example, since we are in SN context, we are expecting the following capabilities: Read : to support the read service. Write: to support the write service. MultipleReferences: to be able to handle several reads (or writes) at once. ParametrizedAccess: to allow to read subsets of large attributes. For a LN meter we would have used this kind of AssociationRequest:
<AssociationRequest> <ApplicationContextName Value="LN" /> <InitiateRequest> <ProposedDlmsVersionNumber Value="06" /> <ProposedConformance> <ConformanceBit Name="Action" />
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 9 of 18
<ConformanceBit Name="EventNotification" /> <ConformanceBit Name="SelectiveAccess" /> <ConformanceBit Name="Set" /> <ConformanceBit Name="Get" /> <ConformanceBit Name="BlockTransferWithAction" /> <ConformanceBit Name="BlockTransferWithSet" /> <ConformanceBit Name="BlockTransferWithGet" /> <ConformanceBit Name="Attribute0SupportedWithGet" /> <ConformanceBit Name="PriorityMgmtSupported" /> <ConformanceBit Name="Attribute0SupportedWithSet" /> </ProposedConformance> <ProposedMaxPduSize Value="FFFF" /> </InitiateRequest> </AssociationRequest>
As a LN meter supports different service than a SN meter, then the requested capabilities are different. Otherwise the two requests are the same.
The Proposed MaxPdu Size
is the size of the longest PDU that the client is able to store. The value "FFFF" means "no limits". The AssociationResponse The meter's response to an AssociationRequest is an AssociationResponse. For example, a SN meter would send this response:
<AssociationResponse> <ApplicationContextName Value="SN" /> <AssociationResult Value="00" /> <ResultSourceDiagnostic> <AcseServiceUser Value="00" /> </ResultSourceDiagnostic> <InitiateResponse> <NegotiatedDlmsVersionNumber Value="06" /> <NegotiatedConformance> <ConformanceBit Name="ParametrizedAccess" /> <ConformanceBit Name="MultipleReferences" /> <ConformanceBit Name="Write" /> <ConformanceBit Name="Read" /> </NegotiatedConformance> <NegotiatedMaxPduSize Value="0960" /> <VaaName Value="FA00" /> </InitiateResponse> </AssociationResponse>
A value of zero indicates that the request succeeded. Any other value indicates a failure.
The Negotiated Conformance
The negotiated conformance is a list of capabilities that the meter implements. It contains all the capabilities that we have requested in the negotiated conformance of the AssociationRequest except the capabilities which are NOT supported by the meter.
The Negotiated MaxPdu Size
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 10 of 18
is the size of the longest PDU that the meter is able to store. A client should never send a PDU longer than this value.
The VaaName
is the short name of the (current) association object. We will ignore it. If the association fails, then the response will look like this:
<AssociationResponse> <ApplicationContextName Value="LN" /> <AssociationResult Value="01" /> <ResultSourceDiagnostic> <AcseServiceUser Value="0B" /> </ResultSourceDiagnostic> <InitiateResponse> <NegotiatedDlmsVersionNumber Value="06" /> <NegotiatedConformance> <ConformanceBit Name="Action" /> <ConformanceBit Name="SelectiveAccess" /> <ConformanceBit Name="Set" /> <ConformanceBit Name="Get" /> <ConformanceBit Name="BlockTransferWithGet" /> <ConformanceBit Name="Attribute0SupportedWithGet" /> </NegotiatedConformance> <NegotiatedMaxPduSize Value="2134" /> <VaaName Value="0007" /> </InitiateResponse> </AssociationResponse>
The association result is not null. The value of the AcseServiceUser element is an error code.
A ReadRequest allows to read several attributes at once, still, in the example, we just want to read one single attribute, therefore "Qty" is set to 1. The (short name) reference of the attribute we want to read is the value of the element "VariableName". Our goal is to read the value of the object with logical name 1.1.1.8.0.255, then, how do we know the relationship (mapping) between the logical names and the short names? The mapping between SN and LN The mapping information between the short names and the logical names is directly available from the meter itself, it is called the Object List and it is also the second attribute of the (current) Association object. In fact, the object list doesn't really contain the short name of all attributes but rather one single base name for each object instance. But what is then the base name of the Association object itself? Fortunately, this name is defined as ''FA00''.
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 11 of 18
From the base name to the short name By definition, the base name of an object instance is also the short name of its first attribute. Then, for example, the following request...
<ReadRequest Qty="0001" > <VariableName Value="FA00" /> </ReadRequest>
By definition, the first attribute (attribute 1) of any object instance is its logical name. Therefore, the responded OctetString is simply the logical name of the current Association:
What are the short names of the other attributes? By definition, the short name of attribute N is given by: ShortNameOfAttributeN = BaseName + 8 * (N-1) According to the standard, the object list is the attribute 2 of the current association. Therefore its short name is FA00 + 8 = FA08. Let's read the object list:
<ReadRequest Qty="0001" > <VariableName Value="FA08" /> </ReadRequest>
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 12 of 18
<Structure Qty="0004" > <Long Value="1770" /> <LongUnsigned Value="0003" /> <Unsigned Value="00" /> <OctetString Value="0101010800FF" /> </Structure> .... </Array> </Data> </ReadResponse>
The object list is an array of 240 (F0) entries. Each entry describes one object instance using a structure of 4 elements. Let's consider the item in blue: The first element is the base name of the object instance: "1770" The second element is the class identifier of the object instance. There are many classes in COSEM. Class 3 is the Register Class. The third element is the class version of the class of the object instance, here it is version 0. Finally, the fourth element is the logical name of the object instance: "0101010800FF" Thanks to the object list, we know that the active energy (OBIS 1.1.1.8.0.255) is an object instance of class 3 with a base name of 1770. The COSEM/DLMS Blue Book shows us that the layout of the Register Class is:
This tells us that the value attribute is attribute 2, therefore the short name of the active energy value is 1770 + 8 = 1778. Let's read the value:
<ReadRequest Qty="0001" > <VariableName Value="1778" /> </ReadRequest> <ReadResponse Qty="0001" > <Data> <Long64 Value="000000000000017D" /> </Data> </ReadResponse>
The response gives the ''digits'' of value of the active energy,17D (hexa) or 381 (dec), but we still don't have the ''real'' value. We also need to read the third attribute (scaler_unit):
<ReadRequest Qty="0001" > <VariableName Value="1780" /> </ReadRequest> <ReadResponse Qty="0001" > <Data>
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 13 of 18
<Structure Qty="0002" > <Integer Value="FF" /> <Enum Value="1E" /> </Structure> </Data> </ReadResponse>
The scaler-unit is a structure of two elements. The first element is an integer power of 10 (scaler). The second element gives the measuring unit. Therefore, scaler = -1, unit = 1E = Wh (according to the Blue Book). Finally, we have it: the value of the active energy is: ActiveEnergy = 381 * 10-1 = 38.1 Wh = 0.0381kWh Since our meter supports ''MultipleReferences'', we can also read the attributes 2 and 3 at once:
<ReadRequest Qty="0002" > <VariableName Value="1778" /> <VariableName Value="1780" /> </ReadRequest> <ReadResponse Qty="0002" > <Data> <Long64 Value="000000000000017D" /> </Data> <Data> <Structure Qty="0002" > <Integer Value="FF" /> <Enum Value="1E" /> </Structure> </Data> </ReadResponse>
InvokeIdAndPriority
is a byte value. The bit 7 selects the priority, 1 means high priority. The bit 6 selects the service class and will be set to 1. The remaining bits are an unsigned value identifying this particular invocation of GetRequestNormal. In our examples, we will always set InvokeIdAndPriority to "C1".
AttributeDescriptor
This 3 elements structure identifies the object instance and the attribute that we want
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 14 of 18
to read: ClassId is the class identifier of the object that hosts the attribute we want to read. InstanceId is the logical name of that object read. AttributeId is the attribute number of the attribute we want to read. The AttributeId can be found in the Blue Book. The ClassId and the InstanceId can be found in the object list. According to the Blue Book, the object list is the attribute 2 of the object 0.0.40.0.0.255, and, the ClassId is 15 (Association LN). Therefore, the GetRequestNormal to read the object list looks like this:
<GetRequest> <GetRequestNormal> <InvokeIdAndPriority Value="C1" /> <AttributeDescriptor> <ClassId Value="000E" /> <InstanceId Value="0000280000FF" /> <AttributeId Value="02" /> </AttributeDescriptor> </GetRequestNormal> </GetRequest>
This object list is very small. It has only two entries. Each entry describes one object instance using a structure of 4 elements. However, when the meter has many objects, a GetResponseNormal would result in a very long PDU. Therefore, provided that the
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 15 of 18
meter has the capability BlockTransferWithGet (which is listed in the NegotiatedConformance of the AssociationResponse), it will send the data in several blocks rather than in one large single chunk. This feature is called ''block transfer'', it is indeed a data segmentation mechanism supported in the application layer and we have to handle it in details. LN Block Transfer When a LN meter receives a GetRequestNormal (for example to read the object list) it may decide to respond with a GetResponseNormal or it may rather switch to block transfer and respond with a GetResponseWithDataBlock. In that case the response will be:
<GetResponse> <GetResponsewithDataBlock> <InvokeIdAndPriority Value="C1" /> <Result> <LastBlock Value="00" /> <BlockNumber Value="00000001" /> <Result> <RawData Value="018201B3020412...6020002031601000100" /> </Result> </Result> </GetResponsewithDataBlock> </GetResponse>
The element InvokeIdAndPriority is the same as in the request. The outer Result is a structure of 3 elements: LastBlock is a boolean which indicates if this block is the last block of the block transfer (00 = false). BlockNumber is an unsigned value and is the number of this block. Finally, the RawData (in the inner Result) is an octet string containing the first n bytes of our data. Having the first block, we save the RawData, say T, and ask for the next block using GetRequestForNextDataBlock:
<GetRequest> <GetRequestForNextDataBlock> <InvokeIdAndPriority Value="C1" /> <BlockNumber Value="00000001" /> </GetRequestForNextDataBlock> </GetRequest>
InvokeIdAndPriority stays the same. BlockNumber is the number of the last block that we have received. The response to this request will be another GetResponseWithDataBlock (BlockNumber 2) and we will right-concatenate its RawData to the T: T := T + RawData Then, we will repeat the process until we have received the last block (LastBlock = true):
<GetResponse> <GetResponsewithDataBlock> <InvokeIdAndPriority Value="81" />
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 16 of 18
<Result> <LastBlock Value="01" /> <BlockNumber Value="00000006" /> <Result> <RawData Value="02041200071...601000100" /> </Result> </Result> </GetResponsewithDataBlock> </GetResponse>
Here again, we right-concatenate the RawData to the data received so far. T := T + RawData The final data T will be a very long PDU. To convert T to XML we left-concatenate it with the byte FF: T := FF + T and pass it to PduToXml. The resulting XML will look like this:
<Data> <Array Qty="01B3" > .... <Structure Qty="0004" > <LongUnsigned Value="0003" /> <Unsigned Value="00" /> <OctetString Value="0101010800FF" /> <Structure Qty="0002" > <Array Qty="0003" > <Structure Qty="0003" > <Integer Value="01" /> <Enum Value="01" /> <NullData /> </Structure> <Structure Qty="0003" > <Integer Value="02" /> <Enum Value="01" /> <NullData /> </Structure> <Structure Qty="0003" > <Integer Value="03" /> <Enum Value="01" /> <NullData /> </Structure> </Array> <Array Qty="0000" > </Array> </Structure> </Structure> .... </Array> </Data>
The resulting data is an array with (possibly) many elements. There is one array entry for each object instance. Each entry is a structure of 4 elements:
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 17 of 18
The first element (LongUnsigned) is the ClassId of the instance. The second element (Unsigned) is the version number of the class of the instance. The third element is the logical name of the instance. Finally, the fourth element lists the access rights of the attributes and methods of the instance. Finally, as we know the parameters, we can send a GetRequestNormal to read the ''digits'' of the energy register:
<GetRequest> <GetRequestNormal> <InvokeIdAndPriority Value="C1" /> <AttributeDescriptor> <ClassId Value="0003" /> <InstanceId Value="0101010800FF" /> <AttributeId Value="02" /> </AttributeDescriptor> </GetRequestNormal> </GetRequest>
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011
Page 18 of 18
http://www.icube.ch/DLMSSurvivalKit/dsk1.html
6/28/2011