
# 代码开发
#### 准备工作
- 开发环境要求：已经安装JDK（版本1.8.以上）和maven。
- 已下载泛协议SDK和样例。
 
#### 项目结构说明
项目结构说明
图1项目结构图   
![](https://support.huaweicloud.com/devg-iothub/figure/zh-cn_image_0000001923182673.png "点击放大")
表1类相关说明 
| 类名称                 | 描述                                      |
|:---|:---|
| Main                | 主启动类。                                   |
| BridgeService       | 网桥初始化：初始化同IoT平台的连接，设置平台下行数据监听           |
| TcpServer           | TCP协议服务端启动类。开启TCP协议监听端口，接收设备上报到服务端的消息。  |
| MessageDecoder      | 上行数据的消息解码，将TCP原始码流转换为具体JSON对象。          |
| MessageEncoder      | 下行数据的消息编码，将对象数据转换为TCP原始码流。              |
| UpLinkHandler       | 设备上行数据处理类。把TCP协议数据转成平台格式数据，并调用SDK接口进行上报 |
| DownLinkHandler     | IoT平台下发数据处理类。将平台下发数据转换为TCP协议数据，并下发给设备。  |
| DeviceSessionManger | 设备会话管理。管理设备同服务端的连接。                     |
   
#### 初始化网桥SDK
创建BridgeBootstrap对象实例，调用initBridge方法，在该方法中会读取环境变量的配置信息，并同IoT平台建立网桥连接。
环境变量说明：
表2环境变量说明 
| 环境变量名称                 | 参数说明            | 样例                                                |
|:---|:---|:---|
| NET_BRIDGE_ID          | 网桥ID            | bridge001                                         |
| NET_BRIDGE_SECRET      | 网桥密钥            | \*\*\*\*\*\*\*\*\*\*                              |
| NET_BRIDGE_SERVER_IP   | IoTDA平台地址       | \*\*\*\*\*.iot-mqtts.cn-north-4.myhuaweicloud.com |
| NET_BRIDGE_SERVER_PORT | IoTDA平台泛协议接入端口号 | 8883                                              |
   
初始化成功后，需要设置平台下行数据的监听器，监听平台的下行数据。
代码样例：
```
public void init() {
        //网桥启动初始化
        BridgeBootstrap bridgeBootstrap = new BridgeBootstrap();
        // 从环境变量获取配置进行初始化
        bridgeBootstrap.initBridge();
        bridgeClient = bridgeBootstrap.getBridgeDevice().getClient();
        // 设置平台下行数据监听器
        DownLinkHandler downLinkHandler = new DownLinkHandler();
        bridgeClient.setBridgeCommandListener(downLinkHandler)   // 设置平台命令下发监听器
            .setBridgeDeviceMessageListener(downLinkHandler)    // 设置平台消息下发监听器
            .setBridgeDeviceDisConnListener(downLinkHandler);   // 设置平台通知网桥主动断开设备连接的监听器
    }
```
#### 设备登录上线
设备登录上线的实现样例如下：
```
private void login(Channel channel, DeviceLoginMessage message) {
        int resultCode = BridgeService.getBridgeClient().loginSync(deviceId, secret, 5000);
        // 登录成功保存会话信息
        if (resultCode == 0) {
            deviceSession.setDeviceId(deviceId);
            deviceSession.setChannel(channel);
            DeviceSessionManger.getInstance().createSession(deviceId, deviceSession);
            NettyUtils.setDeviceId(channel, deviceId);
        }
}
```
设备上线时，需要从原始设备消息中解析出鉴权信息（设备ID和密钥），再调用SDK提供的login接口向平台发起登录请求，平台收到设备的login请求后，会对设备的鉴权信息进行认证，认证通过后会通过返回码告知网桥SDK设备的登录结果。您需要根据登录结果对设备进行记录会话信息、给设备返回响应等处理。
#### 设备数据上报
设备登录成功后，收到设备的上行数据时，可调用SDK的reportProperties将解码后的数据上报到IoT平台。
代码样例参考：
```
private void reportProperties(Channel channel, BaseMessage message) {
    String deviceId = message.getMsgHeader().getDeviceId();
    DeviceSession deviceSession = DeviceSessionManger.getInstance().getSession(deviceId);
    if (deviceSession == null || !deviceSession.isLoginSuccess()) {
        log.warn("device={} is not login", deviceId);
        sendResponse(channel, message, 1);
        return;
    }
    // 调用网桥reportProperties接口，上报设备属性数据
    BridgeService.getBridgeClient()
        .reportProperties(deviceId, Collections.singletonList(serviceProperty), new ActionListener() {
            @Override
            public void onSuccess(Object context) {
                sendResponse(channel, message, 0);
            }
            @Override
            public void onFailure(Object context, Throwable var2) {
                log.warn("device={} reportProperties failed: {}", deviceId, var2.getMessage());
                sendResponse(channel, message, 1);
            }
        });
}
```
#### 平台指令下发
网桥在初始化时向SDK注册了BridgeCommandListener的监听。当有下行指令时，网桥SDK就会回调BridgeCommandListener的onCommand方法。您可在onCommand中对平台的下行指令进行处理。
代码样例参考：
```
public void onCommand(String deviceId, String requestId, BridgeCommand bridgeCommand) {
    log.info("onCommand deviceId={}, requestId={}, bridgeCommand={}", deviceId, requestId, bridgeCommand);
    DeviceSession session = DeviceSessionManger.getInstance().getSession(deviceId);
    if (session == null) {
        log.warn("device={} session is null", deviceId);
        return;
    }
    // 设置位置上报的周期
    if (Constants.MSG_TYPE_FREQUENCY_LOCATION_SET.equals(bridgeCommand.getCommand().getCommandName())) {
        processLocationSetCommand(session, requestId, bridgeCommand);
    }
}
```
#### 设备离线
网桥检查到设备到服务端的长连接断开时，需要调用SDK的logout接口通知平台设备离线。
代码样例参考：
```
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    String deviceId = NettyUtils.getDeviceId(ctx.channel());
    DeviceSessionManger.getInstance().getSession(deviceId);
    if (deviceId == null) {
        return;
    }
    // 调用网桥的logout接口，通知平台设备离线
    DefaultActionListenerImpl defaultLogoutActionListener = new DefaultActionListenerImpl("logout");
    BridgeService.getBridgeClient().logout(deviceId, UUID.randomUUID().toString(), defaultLogoutActionListener);
    DeviceSessionManger.getInstance().deleteSession(deviceId);
    ctx.close();
}
```
