Offline Codec Development

A codec can convert binary messages into JSON messages. The JSON format is defined in the profile. Therefore, before developing a codec, you must define the product model of the device.

Codec demo projects are provided to improve the integration efficiency of offline codec development. You are advised to perform secondary development based on a demo project.

Note: Offline codec development is complex and time-consuming. Therefore, graphical development is recommended.

Preparing the Development Environment

  • Download the Eclipse installation package from the official website and decompress it to a local directory. You can use the software without installation.
  • Download the Maven plug-in package (in .zip format) from the official website and decompress it to a local directory.
  • Install the JDK and configure the Java development environment.

Maven configuration involves setting environment variables on Windows and setting Maven on Eclipse. For details on setting environment variables on Windows, see other online resources. Maven can be configured on Eclipse as follows:

  1. Start Eclipse and choose Windows > Preferences. In the Preferences window, choose Maven > Installations. On the right pane, click Add.

  2. Select the path where the Maven plug-in package is stored and click Finish to import the Maven plug-in.

  3. Select the imported Maven plug-in and click OK.

Importing the Demo Project of the Codec

  1. Download the demo project, obtain the codecDemo.zip file from the source_code folder, and decompress the file to a local directory.

  2. Open Eclipse, right-click the blank area in Project Explorer on the left of Eclipse, and choose Import > Import....

  3. Expand Maven, select Existing Maven Projects, and click Next.

  4. Click Browse, select the codecDemo folder obtained in step 1, select /pom.xml, and click Finish.

Implementation Sample Interpretation

The following figure shows the structure of the imported codec demo project.

This project is a Maven project. You can modify the following content based on this sample project to obtain the required codec.

  1. Modify the configuration files of the Maven project.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    
    <groupId>com.thrid.party</groupId>
    <!-- Change it to the name of your codec. The naming rule is as follows: device type-manufacturer ID, for example: WaterMeter-Huawei.-->
    <artifactId>WaterMeter-Huawei</artifactId>
    <version>1.0.0</version>
    <!-- Check that the value is bundle. The value cannot be jar. -->
    <packaging>bundle</packaging>
    
    ......
    
    <dependencies>
    ......
        <!-- Codec interface provided by Huawei, which must be introduced. -->
        <!-- Replace systemPath with your local \codecDemo\lib\com.huawei.m2m.cig.tup-1.3.1.jar -->
        <dependency>
            <groupId>com.huawei</groupId>
            <artifactId>protocal-jar</artifactId>
            <version>1.3.1</version>
            <scope>system</scope>
            <systemPath>${basedir}/lib/com.huawei.m2m.cig.tup-1.3.1.jar</systemPath>
        </dependency>
    ......
    </dependencies>
    <build>
    <plugins>
        <!-- OSGi packaging configuration -->
        <plugin>
            <configuration>
                <instructions>
                    <!-- Change it to the name of your codec. The naming rule is as follows: device type-manufacturer ID, for example: WaterMeter-Huawei. -->
                    <Bundle-SymbolicName>WaterMeter-Huawei</Bundle-SymbolicName>
                </instructions>
            </configuration>
        </plugin>
    </plugins>
    </build>
    </project>
    

  2. In the ProtocolAdapterImpl.java file, change the values of MANU_FACTURERID.

    1
    2
    3
    private static final Logger logger = LoggerFactory.getLogger(ProtocolAdapterImpl.class);
    //Manufacturer name
    private static final String MANU_FACTURERID = "Huawei";
    

  3. Modify the code in the CmdProcess.java file so that the codec can encode delivered commands and responses to reported data.

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    package com.Huawei.NBIoTDevice.WaterMeter;
    
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    public class CmdProcess {
    
        //private String identifier = "123";
        private String msgType = "deviceReq";
        private String serviceId = "Brightness";
        private String cmd = "SET_DEVICE_LEVEL";
        private int hasMore = 0;
        private int errcode = 0;
        private int mid = 0;
        private JsonNode paras;
    
    
        public CmdProcess() {
        }
    
        public CmdProcess(ObjectNode input) {
    
            try {
                // this.identifier = input.get("identifier").asText();
                this.msgType = input.get("msgType").asText();
                /*
                The IoT platform receives messages reported by the device and encodes the ACK message.
                {
                    "identifier":"0",
                    "msgType":"cloudRsp",
                    "request": ***,//Stream reported by the device
                    "errcode":0,
                    "hasMore":0
                }
                * */
                if (msgType.equals("cloudRsp")) {
                    //Assemble the values of fields in the ACK message.
                    this.errcode = input.get("errcode").asInt();
                    this.hasMore = input.get("hasMore").asInt();
                } else {
                /*
                The IoT platform delivers a command to the device with parameters specified as follows:
                {
                    "identifier":0,
                    "msgType":"cloudReq",
                    "serviceId":"WaterMeter",
                    "cmd":"SET_DEVICE_LEVEL",
                    "paras":{"value":"20"},
                    "hasMore":0
    
                }
                * */
                    //Compatibility must be considered. If the MID is not transferred, it is not encoded.
                    if (input.get("mid") != null) {
                        this.mid = input.get("mid").intValue();
                    }
                    this.cmd = input.get("cmd").asText();
                    this.paras = input.get("paras");
                    this.hasMore = input.get("hasMore").asInt();
                }
    
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        public byte[] toByte() {
            try {
                if (this.msgType.equals("cloudReq")) {
                    /*
                    The NA delivers a control command. In this example, there is only one command: SET_DEVICE_LEVEL.
                    If there are other commands, determine them.
                    * */
                    if (this.cmd.equals("SET_DEVICE_LEVEL")) {
                        int brightlevel = paras.get("value").asInt();
                        byte[] byteRead = new byte[5];
                        ByteBufUtils buf = new ByteBufUtils(byteRead);
                        buf.writeByte((byte) 0xAA);
                        buf.writeByte((byte) 0x72);
                        buf.writeByte((byte) brightlevel);
    
                       //Compatibility must be considered. If the MID is not transferred, it is not encoded.
                        if (Utilty.getInstance().isValidofMid(mid)) {
                            byte[] byteMid = new byte[2];
                            byteMid = Utilty.getInstance().int2Bytes(mid, 2);
                            buf.writeByte(byteMid[0]);
                            buf.writeByte(byteMid[1]);
                        }
    
                        return byteRead;
                    }
                }
    
                /*
                After receiving the data reported by the device, the IoT platform encodes the ACK message as required and responds to the device. If null is returned, the IoT platform does not need to respond.
                * */
                else if (this.msgType.equals("cloudRsp")) {
                    byte[] ack = new byte[4];
                    ByteBufUtils buf = new ByteBufUtils(ack);
                    buf.writeByte((byte) 0xAA);
                    buf.writeByte((byte) 0xAA);
                    buf.writeByte((byte) this.errcode);
                    buf.writeByte((byte) this.hasMore)
                    return ack;
                }
                return null;
            } catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
                return null;
            }
        }
    
    }
    

  4. Modify the code in the ReportProcess.java file so that the codec can decode data reported by devices and command execution results.

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    package com.Huawei.NBIoTDevice.WaterMeter;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ArrayNode;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    
    public class ReportProcess {
        //private String identifier;
    
        private String msgType = "deviceReq";
        private int hasMore = 0;
        private int errcode = 0;
        private byte bDeviceReq = 0x00;
        private byte bDeviceRsp = 0x01;
    
        //serviceId = Brightness
        private int brightness = 0;
    
        //serviceId = Electricity
        private double voltage = 0.0;
        private int current = 0;
        private double frequency = 0.0;
        private double powerfactor = 0.0;
    
        //serviceId = Temperature
        private int temperature = 0;
    
        private byte noMid = 0x00;
        private byte hasMid = 0x01;
        private boolean isContainMid = false;
        private int mid = 0;
    
        /**
         * @param binaryData: Payload of the CoAP packet sent by the device to the IoT platform
         *                   Input parameters in this example: AA 72 00 00 32 08 8D 03 20 62 33 99
         *                   byte[0]--byte[1]: AA 72 command header
         *                   byte[2]:   00 mstType: 00 represents deviceReq, which indicates that data is reported by the device.
         *                   byte[3]:   00 hasMore: 0 indicates that there is no subsequent data and 1 indicates that there is subsequent data. If the hasMore field is not contained, the value 0 is used.
         *                   byte[4]--byte[11]: indicates service data, which is parsed as required.//If the service data is deviceRsp, byte[4] indicates whether the MID is carried and byte[5] to byte[6] indicate the short command ID.
         * @return
         */
        public ReportProcess(byte[] binaryData) {
            //The identifier parameter can be obtained based on the input parameter stream. In this example, the default value is 123.
            // identifier = "123";
    
            /*
            If the data is reported by the device, the return value is in the following format:
            {
                "identifier":"123",
                "msgType":"deviceReq",
                "hasMore":0,
                "data":[{"serviceId":"Brightness",
                          "serviceData":{"brightness":50},
                          {
                          "serviceId":"Electricity",
                          "serviceData":{"voltage":218.9,"current":800,"frequency":50.1,"powerfactor":0.98},
                          {
                          "serviceId":"Temperature",
                          "serviceData":{"temperature":25},
                          ]
    	    }
    	    */
            if (binaryData[2] == bDeviceReq) {
                msgType = "deviceReq";
                hasMore = binaryData[3];
    
                //serviceId = Brightness
                brightness = binaryData[4];
    
                //serviceId = Electricity
                voltage = (double) (((binaryData[5] << 8) + (binaryData[6] & 0xFF)) * 0.1f);
                current = (binaryData[7] << 8) + binaryData[8];
                powerfactor = (double) (binaryData[9] * 0.01);
                frequency = (double) binaryData[10] * 0.1f + 45;
    
                //serviceId = Temperature
                temperature = (int) binaryData[11] & 0xFF - 128;
            }
            /*
            If the data is a response sent by the device to a command of the IoT platform, the return value is in the following format:
           {
                "identifier":"123",
                "msgType":"deviceRsp",
                "errcode":0,
                "body" :{****} Note that the body is a JSON structure.
            }
    	    */
            else if (binaryData[2] == bDeviceRsp) {
                msgType = "deviceRsp";
                errcode = binaryData[3];
                //Compatibility must be considered. If the MID is not transferred, it is not encoded.
                if (binaryData[4] == hasMid) {
                    mid = Utilty.getInstance().bytes2Int(binaryData, 5, 2);
                    if (Utilty.getInstance().isValidofMid(mid)) {
                        isContainMid = true;
                    }
    
                }
            } else {
                return;
            }
    
    
        }
    
        public ObjectNode toJsonNode() {
            try {
                //Assemble the body.
                ObjectMapper mapper = new ObjectMapper();
                ObjectNode root = mapper.createObjectNode();
    
                // root.put("identifier", this.identifier);
                root.put("msgType", this.msgType);
    
                //Assemble the message body based on the msgType field.
                if (this.msgType.equals("deviceReq")) {
                    root.put("hasMore", this.hasMore);
                    ArrayNode arrynode = mapper.createArrayNode();
    
                    //serviceId = Brightness
                    ObjectNode brightNode = mapper.createObjectNode();
                    brightNode.put("serviceId", "Brightness");
                    ObjectNode brightData = mapper.createObjectNode();
                    brightData.put("brightness", this.brightness);
                    brightNode.put("serviceData", brightData);
                    arrynode.add(brightNode);
                    //serviceId = Electricity
                    ObjectNode electricityNode = mapper.createObjectNode();
                    electricityNode.put("serviceId", "Electricity");
                    ObjectNode electricityData = mapper.createObjectNode();
                    electricityData.put("voltage", this.voltage);
                    electricityData.put("current", this.current);
                    electricityData.put("frequency", this.frequency);
                    electricityData.put("powerfactor", this.powerfactor);
                    electricityNode.put("serviceData", electricityData);
                    arrynode.add(electricityNode);
                    //serviceId = Temperature
                    ObjectNode temperatureNode = mapper.createObjectNode();
                    temperatureNode.put("serviceId", "Temperature");
                    ObjectNode temperatureData = mapper.createObjectNode();
                    temperatureData.put("temperature", this.temperature);
                    temperatureNode.put("serviceData", temperatureData);
                    arrynode.add(temperatureNode);
    
                    //serviceId = Connectivity
                    ObjectNode ConnectivityNode = mapper.createObjectNode();
                    ConnectivityNode.put("serviceId", "Connectivity");
                    ObjectNode  ConnectivityData = mapper.createObjectNode();
                    ConnectivityData.put("signalStrength", 5);
                    ConnectivityData.put("linkQuality", 10);
                    ConnectivityData.put("cellId", 9);
                    ConnectivityNode.put("serviceData", ConnectivityData);
                    arrynode.add(ConnectivityNode);
    
                    //serviceId = Battery
                    ObjectNode batteryNode = mapper.createObjectNode();
                    batteryNode.put("serviceId", "battery");
                    ObjectNode batteryData = mapper.createObjectNode();
                    batteryData.put("batteryVoltage", 25);
                    batteryData.put("battervLevel", 12);
                    batteryNode.put("serviceData", batteryData);
                    arrynode.add(batteryNode);
    
                    root.put("data", arrynode);
    
                } else {
                    root.put("errcode", this.errcode);
                    //Compatibility must be considered. If the MID is not transferred, it is not decoded.
                    if (isContainMid) {
                        root.put("mid", this.mid);//mid
                    }
                    //Assemble the body. The body must be an ObjectNode object.
                    ObjectNode body = mapper.createObjectNode();
                    body.put("result", 0);
                    root.put("body", body);
                }
                return root;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    }
    

Description of decode API

The input parameter binaryData over the decode API is the payload in the CoAP message sent by a device.

Upstream messages reported by the device need to be processed by the codec in the following two scenarios (message (4) is the protocol ACK message returned by the module and does not need to be processed by the codec):

  • Reported device data (message (1) in the figure)

    Parameter

    Type

    Mandatory or Optional

    Description

    identifier

    String

    No

    Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.

    msgType

    String

    Yes

    This parameter has a fixed value of deviceReq, which indicates that the device reports data to the IoT platform.

    hasMore

    Int

    No

    Specifies whether the IoT platform has subsequent commands to deliver. 0: The IoT platform does not have subsequent commands to deliver. 1: The IoT platform has subsequent commands to deliver.

    Subsequent data indicates that a piece of data reported by a device may be reported multiple times. After the data is reported the current time, the IoT platform determines whether there are subsequent messages using the hasMore field. The hasMore field is valid only in PSM mode. When the hasMore field of reported data is set to 1, the IoT platform does not deliver cached commands until it receives reported data whose hasMore field is set to 0. If the reported data does not contain the hasMore field, the IoT platform processes the data on the basis that the hasMore field is set to 0.

    data

    ArrayNode

    Yes

    Content of the data reported by the device.

    Table 1 Definition of ArrayNode

    Parameter

    Type

    Mandatory or Optional

    Description

    serviceId

    String

    Yes

    Service ID.

    serviceData

    ObjectNode

    Yes

    Data of a service. The detailed parameters are defined in the profile.

    eventTime

    String

    No

    Specifies the time when the device collects data. The format is yyyyMMddTHHmmssZ,

    for example, 20161219T114920Z.

    Example:

    {
        "identifier": "123",
        "msgType": "deviceReq",
        "hasMore": 0,
        "data": [{
    	"serviceId": "NBWaterMeterCommon",
    	"serviceData": {
    	    "meterId": "xxxx",
    	    "dailyActivityTime": 120,
    	    "flow": "565656",
    	    "cellId": "5656",
    	    "signalStrength": "99",
    	    "batteryVoltage": "3.5"
    	},
    	"eventTime": "20160503T121540Z"
        },
        {
    	"serviceId": "waterMeter",
    	"serviceData": {
    		"internalTemperature": 256
    	},
    	"eventTime": "20160503T121540Z"
        }]
    }
  • Device response to the command delivered by the IoT platform (message (5) in the figure)

    Parameter

    Type

    Description

    Mandatory or Optional

    identifier

    String

    Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.

    No

    msgType

    String

    This parameter has a fixed value of deviceRsp, which indicates a response sent by a device to the IoT platform.

    Yes

    mid

    Int

    Specifies a 2-byte unsigned command ID. If the device must return the command execution result (deviceRsp), this field is used to associate the command execution result (deviceRsp) with the corresponding command.

    When the IoT platform delivers a command over the encode API, the IoT platform places the MID allocated by the IoT platform into a stream and delivers the stream to the device together with the command. When the device reports the command execution result (deviceRsp), the device returns the MID to the IoT platform. In this way, the IoT platform associates the delivered command with the command execution result (deviceRsp) and updates the command delivery status accordingly.

    Yes

    errcode

    Int

    Request processing result code. The IoT platform determines the command delivery status based on this field.

    The value 0 indicates success, and the value 1 indicates failure.

    Yes

    body

    ObjectNode

    Command response, whose fields are defined in the profile.

    Note: The body is not an array.

    No

    Example:

    { 
        "identifier": "123", 
        "msgType": "deviceRsp", 
        "mid": 2016, 
        "errcode": 0, 
        "body": { 
            "result": 0 
        } 
    }

Description of encode API

Input parameters of the encode API are commands or responses in JSON format delivered by the IoT platform.

The downstream messages of the IoT platform can be classified into two types:

  • Response from the IoT platform to the data reported by the device (message (2) in the figure)
    Table 2 Definition of input parameters of the encode API over which the IoT platform responds to data reported by a device

    Parameter

    Type

    Description

    Mandatory or Optional

    identifier

    String

    Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.

    No

    msgType

    String

    This field has a fixed value of cloudRsp, which indicates that the IoT platform sends a response to data reported by a device.

    Yes

    request

    byte[]

    Indicates the data reported by the device.

    Yes

    errcode

    int

    Request processing result code. The IoT platform determines the command delivery status based on this field.

    The value 0 indicates success, and the value 1 indicates failure.

    Yes

    hasMore

    int

    Specifies whether the IoT platform has subsequent messages to be sent. The value 0 indicates that the IoT platform does not have subsequent messages to be sent. The value 1 indicates that the IoT platform has subsequent messages to be sent.

    Subsequent messages indicate that the IoT platform still needs to deliver commands, and the hasMore field is used to tell the device not to sleep. The hasMore field is valid only in PSM mode with the downstream message indication function enabled.

    Yes

    Note: If msgType is set to cloudRsp and null is returned by the codec detection tool, the codec does not define the response to the reported data and the IoT platform does not need to respond.

    Example:

    { 
        "identifier": "123", 
        "msgType": "cloudRsp", 
        "request": [ 
            1, 
            2 
        ], 
        "errcode": 0, 
        "hasMore": 0 
    }
  • Commands delivered by the IoT platform (message (3) in the figure)
    Table 3 Definition of input parameters of the encode API over which the IoT platform delivers commands

    Parameter

    Type

    Description

    Mandatory or Optional

    identifier

    String

    Identifier of the device in the application protocol. The IoT platform obtains the parameter over the decode API, encodes the parameter over the encode API, and places the parameter in a stream.

    No

    msgType

    String

    This parameter has a fixed value of cloudReq, which indicates a command delivered by the IoT platform.

    Yes

    serviceId

    String

    Service ID.

    Yes

    cmd

    String

    Command name. For details, see the profile.

    Yes

    paras

    ObjectNode

    Command parameters, which are defined in the profile.

    Yes

    hasMore

    Int

    Specifies whether the IoT platform has subsequent commands to deliver. 0: The IoT platform does not have subsequent commands to deliver. 1: The IoT platform has subsequent commands to deliver.

    Subsequent commands indicate that the IoT platform still needs to deliver commands, and the hasMore field is used to tell the device not to sleep. The hasMore field is valid only in PSM mode with the downstream message indication function enabled.

    Yes

    mid

    Int

    A 2-byte unsigned command ID that is allocated by the IoT platform. (The value ranges from 1 to 65535.)

    When the IoT platform delivers a command over the encode API, the IoT platform places the MID allocated by the IoT platform into a stream and delivers the stream to the device together with the command. When the device reports the command execution result (deviceRsp), the device returns the MID to the IoT platform. In this way, the IoT platform associates the delivered command with the command execution result (deviceRsp) and updates the command delivery status accordingly.

    Yes

    Example:

    { 
        "identifier": "123", 
        "msgType": "cloudReq", 
        "serviceId": "NBWaterMeterCommon", 
        "mid": 2016, 
        "cmd": "SET_TEMPERATURE_READ_PERIOD", 
        "paras": { 
            "value": 4 
        }, 
        "hasMore": 0} 
    }

Description of getManufacturerId API

This API is used to return the manufacturer ID in the format of a character string. The IoT platform calls this API to obtain the manufacturer ID.

Example:

@Override 
public String getManufacturerId() { 
    return "TestUtf8ManuId"; 
}

Precautions on Interface Implementation

Support for Thread Security Required

The decode and encode functions must ensure thread security. Therefore, member or static variables cannot be added to cache intermediate data.

  • Incorrect example: When multiple threads are started at the same time, the status of thread A is set to Failed while the status of thread B is set to Success. As a result, the status is incorrect, and the program running is abnormal.
    public class ProtocolAdapter {
    private String status;
    
    @Override
    public ObjectNode decode(finalbyte[] binaryData) throws Exception {
        if (binaryData == null) {
            status = "Failed";
            return null;
        }
        ObjectNode node;
        ...;
        status = "Success";//The thread is insecure.
        return node;
    }
    }
  • Correct example: Encoding and decoding are performed based on the input parameters, and the encoding and decoding library does not process services.

Explanation of the mid Field

The IoT platform delivers orders in sequence. However, the IoT platform does not respond to the order execution results in the same sequence as the delivered orders. The MID is used to associate the order execution result response with the delivered order. On the IoT platform, whether the MID is implemented affects the message flow.

  • When the MID is implemented:

    If the MID is implemented and the order execution result is reported successfully:

    1. The status (SUCCESSFUL/FAILED) in the order execution result is updated to the record of the order in the IoT platform database.
    2. The order execution result notification sent by the IoT platform to the NA server contains commandId.
    3. The query result of the NA server indicates that the status of the order is SUCCESSFUL/FAILED.
  • When the MID is not implemented:

    If the MID is not implemented and the order execution result is reported successfully:

    1. The status (SUCCESSFUL/FAILED) in the order execution result is not updated to the record of the order in the IoT platform database.
    2. The order execution result notification sent by the IoT platform to the NA server does not contain commandId.
    3. The query result of the NA server indicates that the final status of the order is DELIVERED.

The preceding two message flows are used to explain the function of the mid field. Some message flows are simplified in the figures.

In scenarios where whether orders are sent to the device is of concern but the order execution is not, the device and codec do not need to implement the mid field.

If the mid field is not implemented, the NA server cannot obtain the order execution result from the IoT platform. Therefore, the NA server needs to implement the solution by itself. For example, after receiving the order execution result response (without commandId), the NA server can do as follows:
  • Match the response with the order according to the sequence in which orders are delivered. In this way, when the IoT platform delivers multiple orders to the same device at the same time, the order execution result is matched with the delivered order incorrectly if packet loss occurs. Therefore, it is recommended that the NA server deliver only one order to the same device each time. After receiving the order execution result response, the NA server delivers the next order.
  • The codec can add order-related information, such as an order code, to the resultDetail field of the order response to help identify the order. The NA server identifies the mapping between the order execution result response and the delivered order according to the information in the resultDetail field.

Do Not Use DirectMemory

The DirectMemory field directly calls the OS interface to apply for memory and is not controlled by the JVM. Improper use of the DirectMemory field may cause insufficient memory of the OS. Therefore, the DirectMemory cannot be used in codec plug-in code.

Example of improper use: Use UNSAFE.allocateMemory to apply for direct memory.

if ((maybeDirectBufferConstructor instanceof Constructor))
{
      address = UNSAFE.allocateMemory(1L);
      Constructor<?> directBufferConstructor;
      ...
}
else
{
      ...
}

Codec Input and Output Examples

The following table describes the definition of a service supported by a kind of water meter.

Service Type

Property Name

Property Description

Property Type (Data Type)

Battery

-

-

-

-

batteryLevel

Specifies the battery level in the unit of percent. The value ranges from 0 to 100.

int

Meter

-

-

-

-

signalStrength

Indicates the signal strength.

int

-

currentReading

Specifies the current read value.

int

-

dailyActivityTime

Specifies the daily activated communication duration.

string

The following shows the decode interface output for data reported by a device to the IoT platform.

{
    "identifier": "12345678",
    "msgType": "deviceReq",
    "data": [
        {
            "serviceId": "Meter",
            "serviceData": {
                "currentReading": "46.3",
                "signalStrength": 16,
                "dailyActivityTime": 5706
            },
            "eventTime": "20160503T121540Z"
        },
        {
            "serviceId": "Battery",
            "serviceData": {
                "batteryLevel": 10
            },
            "eventTime": "20160503T121540Z"
        }
    ]
}

The following shows the encode interface input when the IoT platform receives data reported by the device and sends a response to the device.

{
    "identifier": "123",
    "msgType": "cloudRsp",
    "request":[
        1,
        2
    ],
    "errcode": 0,
    "hasMore": 0
}

The following table describes the commands supported by a kind of water meter.

Basic Function

Category

Name

Command Parameter

Data Type

Enumerated Value

WaterMeter

Water meter

-

-

-

-

-

CMD

SET_TEMPERATURE_READ_PERIOD

-

-

-

-

-

-

value

int

-

-

RSP

SET_TEMPERATURE_READ_PERIOD_RSP

-

-

-

-

-

-

result

int

The value 0 indicates success. The value 1 indicates invalid input. The value 2 indicates execution failed.

The following shows the input parameters of the encode interface when the IoT platform sends an order to the device.

{
    "identifier": "12345678",
    "msgType": "cloudReq",
    "serviceId": "WaterMeter",
    "cmd": "SET_TEMPERATURE_READ_PERIOD",
    "paras": {
        "value": 4
    },
    "hasMore": 0
}

After the IoT platform receives a response from the device, the IoT platform invokes the decode interface for decoding. The decode interface output is as follows:

{
    "identifier": "123",
    "msgType": "deviceRsp",
    "errcode": 0,
    "body": {
        "result": 0
    }
}

Packaging the Codec

After the codec is developed, use the Maven to pack the codec into a JAR package and create it as a codec package.

Maven Packaging

  1. Open the DOS window and access the directory where the pom.xml file is located.
  2. Run mvn package.
  3. After BUILD SUCCESS is displayed in the DOS window, open the target folder in the same directory as the pom.xml file to obtain the .jar package.

    The naming rule of the .jar package is as follows: device type-manufacturer ID-device model-version.jar, for example: WaterMeter-Huawei-NBIoTDevice-version.jar.

    • The com directory stores class files.
    • The META-INF directory stores description files of .jar packages under the OSGi framework, which are generated based on configurations in the pom.xml file.
    • The OSGI-INF directory stores service configuration files and is used to register the codec as a service for the platform to call (only one .xml file can be called).
    • Other .jar packages are .jar packages referenced by codecs.

Preparing a Codec Package

  1. Create a folder named package, which contains the preload/ sub-folder.
  2. Place the packaged .jar package in the preload/ folder.

  3. In the package folder, create the package-info.json file. The fields and templates in this file are described as follows:

    Note: The package-info.json file is encoded using UTF-8 without BOM. Only English characters are supported.
    Table 4 Description of fields in the package-info.json file

    Parameter

    Description

    Mandatory or Optional

    specVersion

    Specifies the version of the description file. The value is fixed at 1.0.

    Yes

    fileName

    Specifies the name of the software package. The value is fixed at codec-demo.

    Yes

    version

    Specifies the version number of the software package. The version of the package.zip file must be the same as the value of bundleVersion.

    Yes

    deviceType

    Specifies the device type, which must be the same as that defined in the profile.

    Yes

    manufacturerName

    Specifies the manufacturer name, which must be the same as that defined in the profile. Otherwise, the package-info.json file cannot be uploaded to the IoT platform.

    Yes

    platform

    Specifies the platform type, which is the operating system of the IoT platform on which the codec package runs. The value is fixed at linux.

    Yes

    packageType

    Specifies the software package type. This field is used to describe the IoT platform module where the codec is deployed. The value is fixed at CIGPlugin.

    Yes

    date

    Specifies the time when a packet is sent. The format is as follows: yyyy-MM-dd HH-mm-ss. For example, 2017-05-06 20:48:59.

    No

    description

    Specifies the self-defined description about the software package.

    No

    ignoreList

    Specifies the list of bundles to be ignored. The default value is null.

    Yes

    bundles

    Specifies the description of a bundle.

    Note: A bundle is a .jar package in a compressed package. Only one bundle needs to be described.

    Yes

    Table 5 Description of the bundles field

    Parameter

    Description

    Mandatory or Optional

    bundleName

    Specifies the bundle name, which is consistent with the value of Bundle-SymbolicName in the pom.xml file.

    Yes

    bundleVersion

    Specifies the bundle version, which must be the same as the value of version.

    Yes

    priority

    Specifies the bundle priority. This parameter can be set to the default value 5.

    Yes

    fileName

    Specifies the codec file name.

    Yes

    bundleDesc

    Describes the bundle function.

    Yes

    versionDesc

    Describes the functions and features of different versions.

    Yes

    Template of the package-info.json file

    { 
        "specVersion":"1.0", 
        "fileName":"codec-demo", 
        "version":"1.0.0", 
        "deviceType":"WaterMeter", 
        "manufacturerName":"Huawei", 
        "description":"codec", 
        "platform":"linux", 
        "packageType":"CIGPlugin", 
        "date":"2017-02-06 12:16:59", 
        "ignoreList":[], 
        "bundles":[ 
        { 
            "bundleName": "WaterMeter-Huawei", 
            "bundleVersion": "1.0.0", 
            "priority":5, 
            "fileName": "WaterMeter-Huawei-1.0.0.jar", 
            "bundleDesc":"", 
            "versionDesc":"" 
        }] 
    }

  4. Select all files in the package folder and compress them into a package.zip file.

    Note: The package.zip file cannot contain the package directory.