
# 物模型通信场景迁移实践
本文以第三方云智慧路灯设备迁移到华为云IoT为例，介绍如何进行物模型场景的设备迁移。
#### 业务场景说明
假设路灯设备上报一条光照强度（luminance）消息；支持远程控制路灯开关状态的命令（switch）。
表1业务场景 
| 业务场景     | 物模型样例                                                                                                                                                                                                                                                                                                                                                                       |
|:---|:---|
| 设备属性上报数据 | { "service_id": "BasicData", "properties": { "luminance": 30 } }                                                                       |
| 服务端控制指令  | { "command_name": "switch", "service_id": "LightControl", "paras": { "value": "ON" } } |
   
- 设备使用一机一密的认证方式。
- 设备集成了第三方云SDK，采用物模型通信进行属性上报。
- 服务端集成了第三方云SDK，进行指令下发。
 
#### 迁移方案说明
图1迁移方案
![](https://support.huaweicloud.com/bestpractice-iothub/figure/zh-cn_image_0000001910720041.png "点击放大")
物模型通通信场景的迁移方案分为下面三个步骤：
1. 在控制台进行存量设备信息、物模型数据的迁移和配置数据流转规则，请参见[平台端配置开发]。
   
2. 设备端集成华为云SDK，请参见[设备端集成华为云SDK]。
   
3. 应用端集成华为云SDK进行业务开发，实现接收设备数据和下发控制指令，请参见[服务端集成华为云SDK]。
   
 #### 平台端配置开发
1. 存量设备信息迁移。 
   1. 选择左侧导航栏的"设备发放 \> 设备数据同步"，单击页面右侧的"创建同步任务"。
   
   2. 参考下表填写参数后，单击"确定"。
      表2同步任务参数说明 
      | 参数名       | 配置说明                       |
      |:---|:---|
      | 同步平台类型    | 选择"第三方物联网平台"。              |
      | 第三方平台接入凭证 | 添加第三方平台接入凭证。               |
      | 源平台所在区域   | 第三方物联网平台平台所在区域。            |
      | 源平台实例     | 第三方物联网平台平台所属实例，有多个实例时需要填写。 |
      | 目标平台所在区域  | 选择"北京四"。                   |
      | 目标平台实例    | 选择设备需要同步的目标平台所在实例。         |
      | 同步方式      | 选择"按资源空间整体"。               |
      | 所属产品      | 选择设备所属的产品。                 |
         
      
   
   3. 完成设备数据同步配置后，会自动进行设备数据同步。
   
   
   
   
2. 配置数据转发规则。 
   1. 参考[配置AMQP服务端订阅](https://support.huaweicloud.com/usermanual-iothub/iot_01_00101.html)，配置AMQP订阅。配置参数如下：
      表3AMQP订阅参数说明 
      | 参数值  | 配置说明           |
      |:---|:---|
      | 规则名称 | 填写为"MyRule"。   |
      | 规则描述 | 填写为"RuleTest"。 |
      | 数据来源 | 选择"设备属性"。      |
      | 触发事件 | 选择"设备属性上报"。    |
      | 资源空间 | 选择对应资源空间。      |
         
      
   
   2. 添加到AMQP推送消息队列目标，配置参数如下：
      表4参数说明 
      | 参数名  | 配置说明              |
      |:---|:---|
      | 转发目标 | 选择"AMQP推送消息队列"。   |
      | 消息队列 | 选择"DefaultQueue"。 |
         
      
   
   
   
   
 
 #### 设备端集成华为云SDK
设备端可以通过集成华为云IoT提供的[设备端SDK](https://support.huaweicloud.com/sdkreference-iothub/iot_02_0178.html)，快速连接华为云IoT，进行命令的接收和属性上报。
1. 配置设备侧SDK，配置连接参数，并进行命令的监听。 
   ```
   //加载iot平台的ca证书，获取链接参考：https://support.huaweicloud.com/devg-iothub/iot_02_1004.html#section3
   URL resource = MessageSample.class.getClassLoader().getResource("ca.jks");
   File file = new File(resource.getPath());
   //域名获取方式：登录华为云IoTDA控制台左侧导航栏“总览”页签，在选择的实例基本信息中，单击“接入信息”。选择8883端口对应的接入域名。
   String serverUrl = "ssl://******.st1.iotda-device.cn-north-4.myhuaweicloud.com:8883";
   //在IoT平台创建的设备ID。
   String deviceId = "deviceId";
   //设备ID对应的密钥。
   String deviceSecret = "******";
   //创建设备
   IoTDevice device = new IoTDevice(serverUrl, deviceId, deviceSecret, file);
   if (device.init() != 0) {
       return;
   }
   //设置命令下发回调函数、发送响应。
   client.setCommandListener(new CommandListener() {
       @Override
       public void onCommand(String requestId, String serviceId, String commandName, Map<String, Object> paras) {
           log.info("onCommand, serviceId = " +serviceId);
           log.info("onCommand , name = " + commandName);
           log.info("onCommand, paras =  " + paras.toString());
           //处理命令用户自定义
           //发送命令响应
           device.getClient().respondCommand(requestId, new CommandRsp(0));
       }   
   });
   ```
   
   

2. 上报设备属性。 
   ```
   Map<String ,Object> json = new HashMap<>();
   Random rand = new Random();
   //按照物模型设置属性
   json.put("luminance", 30);
   ServiceProperty serviceProperty = new ServiceProperty();
   serviceProperty.setProperties(json);
   serviceProperty.setServiceId("BasicData");//serviceId要和物模型一致
   device.getClient().reportProperties(Arrays.asList(serviceProperty), new ActionListener() {
       @Override
       public void onSuccess(Object context) {
           log.info("reportProperties success" );
       }
       @Override
       public void onFailure(Object context, Throwable var2) {
           log.error("reportProperties failed" + var2.toString());
       }   
   });
   ```
   
   
 
 #### 服务端集成华为云SDK
华为云IoTDA提供[应用端SDK](https://support.huaweicloud.com/sdkreference-iothub/iot_10_1002.html)，开发者只需做少量代码修改就可完成应用端适配，实现应用端的快速迁移。下面以应用端接收设备属性上报和应用端下发命令给设备为例，介绍如何进行应用端适配。
1. 参考[AMQP客户端接入说明](https://support.huaweicloud.com/usermanual-iothub/iot_01_00100_2.html)，[Java SDK接入示例](https://support.huaweicloud.com/usermanual-iothub/iot_01_00100_3.html)进行设备属性上报的适配处理。
   
   ```
   try {
        MessageConsumer consumer = amqpClient.newConsumer(AmqpConstants.DEFAULT_QUEUE);
        consumer.setMessageListener(message -> {
            try {
                // 此处进行消息处理。如果处理比较耗时，最好进行开启新的线程处理，否则可能造成心跳超时链接断开。
                processMessage(message.getBody(String.class));
                // 如果options.isAutoAcknowledge==false,此处应该调用message.acknowledge();
            } catch (Exception e) {
                log.warn("message.getBody error,exception is ", e);
            }
        });
    } catch (Exception e) {
        log.warn("Consumer initialize error,", e);
    }
   ```
   
   

2. 参考[命令下发使用示例](https://support.huaweicloud.com/usermanual-iothub/iot_01_0339.html) ,下发设备命令。服务端向单个设备下发命令样例如下：
   
   ```
   public class MessageDistributionSolution {
           ..................
           DeviceCommandRequest body = new DeviceCommandRequest();
           body.commandName("switch");
           body.serviceId("LightControl");
           body.withParas({"value": "ON"});
           request.withBody(body);
           try {
               CreateMessageResponse response = client.createMessage(request);
               System.out.println(response.toString());
           } catch (ConnectionException e) {
               e.printStackTrace();
           } catch (RequestTimeoutException e) {
               e.printStackTrace();
           } catch (ServiceResponseException e) {
               e.printStackTrace();
               System.out.println(e.getHttpStatusCode());
               System.out.println(e.getRequestId());
               System.out.println(e.getErrorCode());
               System.out.println(e.getErrorMsg());
           }
       }
   }
   ```
   
   
 
