智能水表接入

更新时间:2024-06-20 03:28:41下载pdf

本文介绍了涂鸦智慧行业智能水表接入方案。

概述

为智能水表设备提供了涂鸦边缘⽹关接⼊的⽅案,帮助智能水表设备快速接⼊涂鸦开发者平台,方案架构如下图所示:

智能水表接入

对接协议

智能水表⽹关⽤ MQTT 协议与涂鸦边缘⽹关进⾏通信,其中智能水表⽹关做客户端 ,涂鸦边缘⽹关做 broker。

开发指南

如下是智能水表的具体设备属性信息,设备属性上报时作为参考。

智能水表标准属性值

DP ID 功能点名称 标识符 数据传输类型 数据类型 功能点属性
1 水流量 water_flow 只上报(ro) value 取值范围: 0~999999999, 单位:m3
2 月总用水量 monthly_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
3 日总用水量 daily_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
4 开关状态 switch_state 可下发可上报(rw) bool
5 日初始用水量 d_begin_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
6 日终用水量 d_end_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
7 日初始读取时间 d_begin_time 只上报(ro) string Unix 时间戳(Unix timestamp)
8 日终读取时间 d_end_time 只上报(ro) string Unix 时间戳(Unix timestamp)
9 阀门状态 valve_state 可下发可上报(rw) enum 枚举值: opened, closed, opening, closing
10 月初始用水量 m_begin_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
11 月终用水量 m_end_water_total 只上报(ro) value 取值范围: 0~999999999, 单位:m3
12 月初始读取时间 m_begin_time 只上报(ro) string Unix 时间戳(Unix timestamp)
13 月终读取时间 m_end_time 只上报(ro) string Unix 时间戳(Unix timestamp)
14 冷摊用量 cold_stall_userdata 只上报(ro) value 取值范围: 0~999999999, 单位:m3
15 冷摊方式 cold_stall_type 可下发可上报(rw) enum 枚举值: square_area, ignore, consumption, proportion, lease, power
16 热摊用量 hot_stall_userdata 只上报(ro) value 取值范围: 0~999999999, 单位:m3
17 热摊方式 hot_stall_type 可下发可上报(rw) enum 枚举值: square_area, ignore, consumption, proportion, lease, power
18 阀开启度 valve_open_degree 可下发可上报(rw) value 取值范围: 0~999999999, 单位: %

API 参考

设备侧 MQTT 接口参考

使用前必读

概述

MQTT 消息由固定报头(Fixed header)、可变报头(Variable header)和有效载荷(Payload)三部分组成。

其中固定报头(Fixed header)和可变报头(Variable header)格式的填写请参考 MQTT 标准规范,有效载荷(Payload)的格式由涂鸦定义,具体请参考下文。

MQTT 的语法和接口细节,请以 MQTT 标准规范 为准。

常见 MQTT 消息类型主要有 CONNECT、SUBSCRIBE、PUBLISH。

  • CONNECT:指客户端请求和服务端连接;有效载荷(Payload)的主要参数,参考 设备连接鉴权 填写。
  • SUBSCRIBE:指客户端订阅请求;有效载荷(Payload)中的主要参数“Topic name”,参考 Topic 定义 中订阅者为设备的 Topic。
  • PUBLISH:平台发布消息。
    • 可变报头(Variable header)中的主要参数“Topic name”,指设备上报到物联网平台时发布者为设备的 Topic。详细请参考 Topic 定义
    • 有效载荷(Payload)中的主要参数为完整的数据上报和命令下发的消息内容,目前是一个 JSON 对象。

使用限制

  • 上行 Topic 是指设备向平台发送请求,或上报数据,或回复响应。
  • 下行 Topic 是指平台向设备下发指令,或回复响应。
  • 设备与平台建立连接后,需要订阅下行 Topic,否则无法收到平台下发的指令或回复的响应。

设备与涂鸦开发者平台的通信方式

设备接入涂鸦边缘网关,通过涂鸦边缘网关进行通信。设备、涂鸦边缘网关和涂鸦开发者平台的通信流程示意图如下。

智能水表接入

设备发送数据到涂鸦边缘网关

设备接入涂鸦边缘网关后,便可与涂鸦边缘网关进行通信。设备可通过以下方式发送数据到平台。

设备属性上报:用于设备按产品模型中定义的格式将属性数据上报给涂鸦开发者平台。

应用服务器下发指令给设备

设备接入涂鸦边缘网关后,涂鸦边缘网关可通过以下方式发送指令到设备。

平台命令下发:应用服务器按产品模型中定义的命令格式下发控制命令给设备。

Topic 定义

设备使用 MQTT 协议接入涂鸦边缘网关时,涂鸦开发者平台和设备通过 Topic 进行通信。预置的 Topic 列表如下:

Topic 分类 Topic Publisher Subscriber 用途
设备同步相关 Topic gateway_id/{gateway_id}/devices/sync 设备 平台 设备激活
设备同步相关 Topic gateway_id/{gateway_id}/devices/sync/response 平台 设备 平台返回激活响应
设备命令相关 Topic gateway_id/{gateway_id}/commands 平台 设备 平台下发命令给设备
设备命令相关 Topic gateway_id/{gateway_id}/commands/response 设备 平台 设备返回命令响应
设备属性相关 Topic gateway_id/{gateway_id}/properties/report 设备 平台 设备属性上报
设备属性相关 Topic gateway_id/{gateway_id}/properties/report/response 平台 设备 平台返回上报属性响应
设备上下线 Topic gateway_id/{gateway_id}/devices/status 设备 平台 设备上下线上报

{gateway_id}用于标识 Topic 路由的目标设备,设备侧订阅该 Topic 或往 Topic 推送消息时,该值需要替换为设备与平台建立 MQTT 连接时使用的网关 ID 参数值。

QoS 等级设置

MQTT 协议中规定了消息服务质量(Quality of Service),它保证了在不同的网络环境下消息传递的可靠性,MQTT 设计了 3 个 QoS 等级。

  • QoS 0:消息最多传递一次,如果当时客户端不可用,则会丢失该消息。
  • QoS 1:消息传递至少 1 次。
  • QoS 2:消息仅传送 1 次。

QoS 级别越高,流程越复杂,系统资源消耗越大。应用程序可以根据自己的网络场景和业务需求,选择合适的 QoS 级别。

涂鸦建议三方设备对接方在消息的推送和订阅上 QoS 设置为 1。

连接鉴权

接口说明

涂鸦开发者平台设备侧支持 MQTT 协议的 connect 消息接口,鉴权通过后建立设备与平台间的 MQTT 连接。

参数说明
参数 必选/可选 类型 参数描述
broker 必选 String 涂鸦边缘⽹关 IP 地址,对接时由涂鸦提供。
port 必选 String TCP 固定为 21883
clientId 必选 String 三方的网关 ID,此参数要保证唯一。
username 必选 String 三⽅⼚商标识,对接时由涂鸦提供。
password 必选 String Password 的值为使用“md5”加密算法生成,具体请参考密码生成规则

说明:SSL/TLS 为单向认证模式。

密码生成规则

拼接 clientIdusername 变量,然后用 md5 加密算法对拼接后的变量加密。

下面用 Go 语言生成连接密码例子,供参考。

func generatePassword() string {
	var (
		clientId string
		username string
		password string
	)
	// clientId username 请先赋值
	password = md5V(clientId + username)
	return password
}

// md5 加密算法函数
func md5V(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

原生 MQTT 协议接入建链返回码

原生 MQTT 协议设备和平台建链时,常见返回码如下:

返回码 标示 返回码描述 原因
0x00 connection accepted 连接成功 连接成功
0x01 unacceptable protocol version 请求拒绝,协议版本错误 服务器不支持客户端请求 MQTT 协议版本
0x02 identifier rejected 请求拒绝,无效的客户端标识符 clientId不符合格式要求或者心跳时间间隔不满足平台要求
0x03 server Unavailable 请求拒绝,服务器不可用 平台服务不可用
0x04 bad user name or password 请求拒绝,用户名或密码错误 用户名或密码错误
0x05 not Authorized 请求拒绝,没有授权 客户端没有权限连接

设备同步

用于设备同步,在设备网关连接上涂鸦边缘网关的 MQTT 服务器后,要发送设备同步指令。涂鸦边缘网关会对上报的设备进行新增或更新。

必须先同步子设备才可进行后续操作,否则会出现找不到设备的错误。

Topic

  • 上行:gateway_id/{gateway_id}/devices/sync

  • 下行:gateway_id/{gateway_id}/devices/sync/response

上行参数说明
字段名 必选/可选 类型 参数描述
t 必选 Integer Unix 时间戳(Unix timestamp)
request_id 必选 String 请求标示,建议用 UUID 算法生成。
param 必选 Object 对象
param.cid 必选 String 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
param.product_id 必选 String 产品对应的 product_id
param.vendor_code 可选 String 厂商 code
param.comm_type 可选 String 设备和网关通讯方式,请填写“mqtt”
param.device_ip 可选 String 设备 IP 地址
param.mac_address 可选 String 设备 MAC 地址
param.device_desc 可选 String 设备描述
param.install_location 可选 String 设备安装位置
param.device_server_id 可选 String 设备服务 ID
param.product_sub_type 可选 String 子产品类型
下行参数说明
字段名 必选/可选 类型 参数描述
t 必选 Integer Unix 时间戳(Unix timestamp)
request_id 必选 String 请求唯一标示
param 必选 Object 对象
param.cid 必选 String 设备 CID
param.msg 必选 String 含义请参考全局错误码。
param.code 必选 Integer 标识命令的执行结果,0 表示成功,其他表示失败
上行参数示例
Topic: gateway_id/{gateway_id}/devices/sync
数据格式:
{
    "t":162728***,
    "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a***",
    "param":{
        "cid":"92dda538fc2e636fd***",
        "product_id":"",//请填写产品对应的 pid
        "vendor_code":"",//设备厂商标识
        "comm_type":"",
        "device_ip":"",
        "mac_address":"",
        "device_name":"",
        "device_desc":"",
        "install_location":"",
        "device_server_id":"",
        "product_sub_type":""
    }
}
下行参数示例
Topic: gateway_id/{gateway_id}/devices/sync/response
数据格式:
{
    "t":162728***,
    "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
    "param":{
        "cid":"92dda538fc2e636fd4b0c37f***",
        "msg":"success",
        "code":0
    }
}

设备命令

涂鸦开发者平台命令下发

用于涂鸦开发者平台向设备下发设备控制命令。下发命令后,需要设备及时将命令的执行结果返回给涂鸦开发者平台,如果设备没回响应,涂鸦开发者平台会认为命令执行超时。

Topic

  • 下行: gateway_id/{gateway_id}/commands

  • 上行:gateway_id/{gateway_id}/commands/response

下行请求参数说明
字段名 必选/可选 类型 参数描述
t 必选 Integer Unix 时间戳(Unix timestamp)
cid 必选 String 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
request_id 必选 String 请求标示,建议用 UUID 算法生成。
param 必选 Object 对象
param.id 必选 String 设备功能点 ID
param.value 必选 类型不固定 设备命令的执行参数。
上行响应参数说明

命令应答的 JSON 格式。

字段名 必选/可选 类型 参数描述
t 必选 Integer Unix 时间戳(Unix timestamp)
cid 必选 String 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
request_id 必选 String 请求唯一标示,建议用 UUID 算法生成。
param 必选 Object 对象
param.msg 必选 String 执行结果,如果成功,请填写 success。
param.code 必选 Integer 标识命令的执行结果,0 表示成功,其他表示失败。
下行请求示例
Topic: gateway_id/{gateway_id}/commands
数据格式:  
{
  "t":162728***,
  "cid": "92dda538fc2e636fd4b0c37f980***",
  "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
  "param": {
    "id":"1",
    "value": "1"
  }
}
上行响应示例
Topic:gateway_id/{gateway_id}/commands/response
数据格式:  
{
  "t":162728***,
  "cid": "92dda538fc2e636fd4b0c37f980***",
  "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a***",
  "param": {
    "msg": "success",
    "code":0
  }
}

设备属性

设备属性上报

用于设备按产品模型中定义的格式将属性数据上报给涂鸦开发者平台。涂鸦开发者平台在接到上报消息后,会将执行结果返回给设备。

Topic

  • 上行:gateway_id/{gateway_id}/properties/report

  • 下行:gateway_id/{gateway_id}/properties/report/response

上行参数说明
字段 必选/可选 类型 参数描述
t 必选 Integer 时间戳:为设备连接涂鸦开发者平台时的 UTC 时间。
request_id 必选 String 请求唯一标示,建议用 UUID 算法生成。
param 必选 Object 对象
param.cid 必选 String 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
param.id 必选 String 智能水表属性下面某一个 DPID。
param.value 必选 参考功能点类型 设备的属性值
下行参数说明
字段名 必选/可选 类型 参数描述
t 必选 Integer Unix 时间戳(Unix timestamp)
request_id 必选 String 请求唯一标示。
param 必选 Object 对象
param.cid 必选 String 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
param.msg 必选 String 含义请参考全局错误码。
param.code 必选 Integer 标识命令的执行结果,0 表示成功,其他表示失败。
上行参数示例
gateway_id/{gateway_id}/properties/report
{
    "t":162728***,
    "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a0***",
    "param":{
        "cid":"ee1880aca6b***",
        "id":"1",
        "value":"run"
    }
}
下行参数示例
Topic:gateway_id/{gateway_id}/properties/report/response
数据格式:  
{
    "t":162728***,
    "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a085c***",
    "param":{
        "cid":"92dda538fc2e636fd4b0c37f98***",
        "msg":"success",
        "code":0
    }
}

设备上下线

设备上下线上报

设备网关需要定时把设备的上线下线状态同步给涂鸦边缘网关,建议至少每隔一分钟上报一次子设备状态。

Topic

上行:gateway_id/{gateway_id}/devices/status

上行参数说明
字段 必选/可选 类型 参数描述
t 必选 Integer 时间戳:为设备连接涂鸦开发者平台时的 UTC 时间。
request_id 必选 String 请求唯一标示,建议用 UUID 算法生成。
param 必选 Object 对象数组
param.cid 必选 String 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。
param.status 必选 Bool 设备状态。true 代表上线、false 代表离线。
上行参数示例
Topic:gateway_id/{gateway_id}/devices/status
数据格式:  
{
  "t":162728***,
  "request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
  "param": [{
    "cid": "92dda538fc2e636fd4b0c37f98***",
    "status":true
  },
  {
    "cid": "da538fc2ebffdb463692dfdb0c3***",
    "status":false
  }         
 ]
}

应用示例

为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发例子,演示了服务连接、验证、敏感信息加/解密、推送订阅等基础功能(更多语言版本的开发库将在近期陆续提供

JAVA

将下面显示的依赖性定义添加到 maven pom 文件中。

<dependencies>
    <dependency>
        <groupId>org.eclipse.paho</groupId>
        <artifactId>org.eclipse.paho.client.mqttv3</artifactId>
        <version>1.2.0</version>
    </dependency>
</dependencies>
//消息发布
public class Publish {
    public static void main(String[] args) {
        String clientId = "";                     // 即 gatewayID, 第三⽅⼚商⽹关 ID
        String topic = ""; 												// 主题
        String content = "";                      // 内容
        int qos = 1;                              // 服务质量
        String broker = "";                       // 服务器 IP 地址
        String userName = "";                     // 用户名,由涂鸦提供
        String password = "";                     // 密码,请参考密码生成规则
        // 内存存储
        MemoryPersistence persistence = new MemoryPersistence();

        try {
            // 创建客户端
            MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
            // 创建链接参数
            MqttConnectOptions connOpts = new MqttConnectOptions();
            // 在重新启动和重新连接时记住状态
            connOpts.setCleanSession(false);
            // 设置连接的用户名
            connOpts.setUserName(userName);
            connOpts.setPassword(password.toCharArray());
            // 建立连接
            sampleClient.connect(connOpts);
            // 创建消息
            MqttMessage message = new MqttMessage(content.getBytes());
            // 设置消息的服务质量
            message.setQos(qos);
            // 发布消息
            sampleClient.publish(topic, message);
            // 断开连接
            sampleClient.disconnect();
            // 关闭客户端
            sampleClient.close();
        } catch (MqttException me) {
            System.out.println("reason " + me.getReasonCode());
            System.out.println("msg " + me.getMessage());
            System.out.println("loc " + me.getLocalizedMessage());
            System.out.println("cause " + me.getCause());
            System.out.println("excep " + me);
            me.printStackTrace();
        }
    }
}
//消息订阅
public class Subscribe {

    public static void main(String[] args) throws MqttException {  
        String clientId = "";                     // 即 gatewayID, 第三⽅⼚商⽹关 ID 
        String host = "";                         // 服务器 IP 地址
        String topic = "gateway/in/" + clientId;  // 主题
        int qos = 1;                              // 服务质量
        String userName = "";                     // 用户名,由涂鸦提供
        String password = "";                     // 密码,请参考密码生成规则
        try {
            MqttClient client = new MqttClient(host, clientid, new MemoryPersistence());
            // MQTT 的连接设置
            MqttConnectOptions options = new MqttConnectOptions();
            // 设置是否清空 session,这里如果设置为 false 表示服务器会保留客户端的连接记录,这里设置为 true 表示每次连接到服务器都以新的身份连接
            options.setCleanSession(true);
            // 设置连接的用户名
            options.setUserName(userName);
            // 设置连接的密码
            options.setPassword(passWord.toCharArray());
            // 设置超时时间 单位为秒
            options.setConnectionTimeout(10);
            // 设置会话心跳时间 单位为秒 
            options.setKeepAliveInterval(20);
            // 设置回调函数
            client.setCallback(new MqttCallback() {

                public void connectionLost(Throwable cause) {
    
                }
                public void messageArrived(String topic, MqttMessage message) throws Exception {
             // 消息处理部分
                    System.out.println("message content:"+new String(message.getPayload()));
                    
                }
                public void deliveryComplete(IMqttDeliveryToken token) {
                   
                }

            });
            client.connect(options);
            //订阅消息
            client.subscribe(topic, qos);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

GO

package main

import (
	"fmt"
	mqtt "github.com/eclipse/paho.mqtt.golang"
	"log"
	"os"
	"time"
)

const broker = ""                // 服务器 IP 地址,请联系涂鸦管理员获取
const username = ""         // 用户名,由涂鸦提供
const password = ""         // 密码,请参考密码生成规则
const clientID = ""           // 即 gatewayID, 第三⽅⼚商⽹关 ID
const topic = ""               // 主题
const port = "21883"          // 服务端口号

// 订阅消息 topic = "gateway/in/" + clientID
// 发布消息 topic = "gateway/out/" + clientID

var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
	fmt.Printf("TOPIC: %s\n", msg.Topic())
	fmt.Printf("MSG: %s\n", msg.Payload())
}

func main() {
	mqtt.DEBUG = log.New(os.Stdout, "", 0)
	mqtt.ERROR = log.New(os.Stdout, "", 0)
	opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%s", broker, port)).SetClientID(clientID).
		SetUsername(username).SetPassword(password)
	
	opts.SetKeepAlive(60 * time.Second)
	// 设置消息回调处理函数
	opts.SetDefaultPublishHandler(f)
	opts.SetPingTimeout(1 * time.Second)
	
	c := mqtt.NewClient(opts)
	if token := c.Connect(); token.Wait() && token.Error() != nil {
		panic(token.Error())
	}
	
	// 订阅主题
	if token := c.Subscribe(topic, 1, nil); token.Wait() && token.Error() != nil {
		fmt.Println(token.Error())
		os.Exit(1)
	}
	
	// 发布消息
	token := c.Publish(topic, 1, false, "Hello World")
	token.Wait()
	
	time.Sleep(6 * time.Second)
	
	// 取消订阅
	if token := c.Unsubscribe(topic); token.Wait() && token.Error() != nil {
		fmt.Println(token.Error())
		os.Exit(1)
	}
	
	// 断开连接
	c.Disconnect(250)
	time.Sleep(1 * time.Second)
}

PHP

消息推送

<?php
require("");                                // 引入 mqtt 类文件
$server    = '';                            // 服务器 IP 地址,请联系涂鸦管理员获取 
$port      = 21883;                         // 服务器端口
$username  = '';                            // 用户名,由涂鸦提供
$password  = '';                            // 密码,请参考密码生成规则
$client_id = ''                             // 即 gatewayID, 第三⽅⼚商⽹关 ID

$mqtt = new Bluerhinos\phpMQTT($server, $port, $client_id); 	// 实例化 MQTT 类

if ($mqtt->connect(true, NULL, $username, $password)) 	// 建立连接
{
    $topic = 'gateway/out/'.$client_id;	// 主题
    $key = ''	// 加密密钥 请填写连接认证时⽤的 password
    $msg = '';    // 推送消息,请参考上方消息格式
    // qos = 0:仅发一次,不管是否能收到
    // qos = 1:没返回一直发,可能有重复接收
    // qos = 2:保证必须收到,并且不重复
    $mqtt->publish($topic, $msg, 0);//发送数据
    $mqtt->close();	 //关闭连接
}
else
{
    echo "Time out!\n";
}

消息订阅

<?php
require("");                                // 引入 mqtt 类文件
$server    = '';                            // 服务器 IP 地址,请联系涂鸦管理员获取 
$port      = 21883;                         // 服务器端口
$username  = '';                            // 用户名,由涂鸦提供
$password  = '';                            // 密码,请参考秘密生成规则
$client_id = ''                             // 即 gatewayID, 第三⽅⼚商⽹关 ID

$mqtt = new Bluerhinos\phpMQTT($server, $port, $client_id);

$mqtt->debug = true;

if(!$mqtt->connect(true, NULL, $username, $password))
{
    echo "连接失败!\n";
    exit(1);
}
$topic = 'gateway/in/'.$client_id;	// 主题
// 订阅列表
$topics = [
    $topic => ['qos' => 0, 'function' => 'procmsg'],
];

$mqtt->subscribe($topics, 0);

while ($mqtt->proc()){}

$mqtt->close();

                                            
function procmsg($topic, $msg)	// 消息处理函数
{
    $key =  ''                              
  	echo $msg;	
}

全局错误码

当调用接口发生错误时,会返回自定义错误信息。本文介绍全局错误码的含义。

错误码 code 错误信息 msg 说明
400 tedge error: 上游具体错误信息 上游错误,具体信息由上游定义
500 system error,please contact the admin 系统错误,请联系管理员。
1001 request time is invalid 无效的请求时间。
1100 params is empty 参数为空。
1101 params range invalid 无效的参数范围。
1102 params is null 参数为 Null。
1103 params type is incorrect 参数类型错误。
2001 device is offline 设备已离线。
2002 command or value not support 不支持的指令或值。
2003 device not exist 设备不存在。