Updated on 2022-02-24 GMT+08:00

Implementation Sample Interpretation

In the DEMO project of the codec (click here to obtain), an example codec is provided. The following figure shows the sample project structure.

Figure 1 Sample project structure

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

NOTE:

Use the encryption algorithms supported by the JDK. For details about these encryption algorithms, see Appendix: Encryption Algorithms Supported by the JDK.

  • Maven configuration file

    In the pom.xml file, modify the name of the codec according to the naming rule.

    <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-device model, for example: WaterMeter-Huawei-NBIoTDevice.-->
    <artifactId>WaterMeter-Huawei-NBIoTDevice</artifactId>
    <version>1.0.0</version>
    <!-- Check that the value is bundle. The value cannot be jar. -->
    <packaging>bundle</packaging>
    
    <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <junit.version>4.11</junit.version>
    <fasterxml.jackson.version>2.7.4</fasterxml.jackson.version>
    <felix.maven.plugin.version>2.5.4.fixed2</felix.maven.plugin.version>
    <json.lib.version>2.4</json.lib.version>
    <m2m.cig.version>1.3.1</m2m.cig.version>
    <slf4j.api.version>1.7.6</slf4j.api.version>
    </properties>
    
    <dependencies>
    <!-- Used by unit test -->
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    </dependency>
    <!-- Used by logs -->
    <dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.api.version}</version>
    </dependency>
    <!-- Used for converting JSON; mandatory -->
    <dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>${fasterxml.jackson.version}</version>
    </dependency>
    <!-- Codec API provided by Huawei; mandatory -->
    <!-- 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>
    
    <!-- In this example, the JAR file used for data conversion is written here. Enter artifactId in the Embed-Dependency. -->
    <dependency>
    <groupId>net.sf.json-lib</groupId>
    <artifactId>json-lib</artifactId>
    <version>2.4</version>
    <classifier>jdk15</classifier>
    </dependency>
    
    </dependencies>
    <build>
    <plugins>
    <!-- The JDK1.8 version must be used. -->
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
    <source>1.8</source>
    <target>1.8</target>
    </configuration>
    </plugin>
    <!-- OSGi packaging configuration -->
    <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <version>${felix.maven.plugin.version}</version>
    <extensions>true</extensions>
    <configuration>
    <buildDirectory>./target</buildDirectory>
    <archive>
    <addMavenDescriptor>false</addMavenDescriptor>
    </archive>
    <instructions>
    <Bundle-RequiredExecutionEnvironment>J2SE-1.5</Bundle-RequiredExecutionEnvironment>
    <Bundle-Activator></Bundle-Activator>
    <Service-Component>OSGI-INF/*</Service-Component>
    <!-- Change it to the name of your codec. The naming rule is as follows: device type-manufacturer ID-device model, for example: WaterMeter-Huawei-NBIoTDevice. -->
    <Bundle-SymbolicName>WaterMeter-Huawei-NBIoTDevice</Bundle-SymbolicName>
    <Export-Package></Export-Package>
    <!-- Import packages in the code and use commas (,) to separate them. [JAR packages that start with java.** and that are referenced in Embed-Dependency do not need to be imported in Import-Package. Otherwise, the codec cannot be started.] -->
    <Import-Package>
    org.slf4j,
    org.slf4j.spi,
    org.apache.log4j.spi,
    com.huawei.m2m.cig.tup.modules.protocol_adapter,
    com.fasterxml.jackson.databind,
    com.fasterxml.jackson.databind.node
    </Import-Package>
    <!-- For all dependency packages except junit, slf4j-api, jackson-databind, and protocol-jar, set artifactId of each package to Embed-Dependency. Separate artifactId values by commas (,). During Maven packaging, pack your dependency packages into your JAR package. -->
    <Embed-Dependency>
    json-lib
    </Embed-Dependency>
    </instructions>
    </configuration>
    <executions>
    <execution>
    <id>generate-resource</id>
    <goals>
    <goal>manifest</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    </project>
  • Codec code implementation
    • In the ProtocolAdapterImpl.java file, change the values of MANU_FACTURERID and MODEL. The IoT platform associates the codec with the profile file using the manufacturer ID and device model.
      1
      2
      3
      4
      5
      private static final Logger logger = LoggerFactory.getLogger(ProtocolAdapterImpl.class);
      //Manufacturer name
      private static final String MANU_FACTURERID = "Huawei";
      //Model
      private static final String MODEL = "NBIoTDevice";
      
    • 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;
              }
          }
      
      }
      
    • 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 encoded.
                      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;
              }
          }
      }