DP 模型与控制协议

更新时间:2023-12-19 08:36:57下载pdf

DP(Data Point)是涂鸦对产品功能定义的数据模型,用于描述产品的功能。面对海量各式各样的设备,为了规模化和数字化管理这些设备,则需要使用一种抽象语言来描述设备。使用 DP 描述设备可以屏蔽底层终端的差异,标准化设备能力,达到低耦合的效果。

基本概念

DP 的类型,DP 的传输属性,DP 的传输类型可在 涂鸦 IoT 开发平台 上添加产品的功能 DP 时,在页面上进行选择。

  • DP 数据类型

    • 对象类型(Object)

      • 布尔型:bool

        非真即假的二值型变量。例如开关功能,开/关。

      • 数值型:value

        适用于可线性调节类型的数据。例如温度调节,温度范围 20-40℃。

      • 枚举型:enum

        自定义的有限集合值。例如工作档位,低档/中档/高档。

      • 故障型:bitmap

        用于多状态的显示,一般是专门用于显示和统计故障的功能。

      • 字符型:string

        以字符串形式传输的功能,一般用于较复杂功能,最大 255 个字节。

    • 透传类型(Raw)

      以 Hex 格式(十六进制)传输的功能,一般用于较复杂功能。

  • DP 扩展属性

    • 被动上报:passive

      如果是被动上报,必须要先下发 DP 指令才能上报,防止上报过于频繁。

    • 重复上报:trigger

      开启则表示该 DP 支持重复上报相同值,否则框架会开启过滤机制。

    • DP 路由:route

      用来控制 蜂窝网络&蓝牙双协议设备 的 DP 传输通道。

      • 不填写,优先用蜂窝网络,当蜂窝网络断开时,使用蓝牙通道。

      • 1 代表:优先用蓝牙通道,当蓝牙未连接时使用蜂窝网络。

      • 2 代表:强制用蓝牙通道,当蓝牙未连接时,面板需要有错误提示。

  • DP 传输类型

    • RO (Read Only):可上报。数据只支持从设备传输给云端。
    • WR (Write Only):可下发。数据只支持从云端发送给设备。
    • RW (Read Write):可上报下发。指令数据可以发送给设备,设备数据可以传输给云端。
  • DP 时效性

    • 实时型 DP

      DP 用于实时数据上报或者下发,即强调时效性,DP 的时间就是上报的时间。如控制开关并同步开关状态。

    • 记录型 DP

      DP 用于记录某一时刻的设备状态和产生的数据,此时 DP 的时间是表示产生这条 DP 记录的时间而非上报时间。如门锁解锁记录上报,告警,电量统计等 DP。

功能描述

DP 上报

您可调用框架提供的各种数据上报接口,向云端和客户端同步设备的状态和一些本地数据,如开关状态,电量等。

上报接口内部会进行 DP 合法性判断,自动根据当前网络情况决定上报的通道。DP 上报方式有异步上报和同步上报两种方式。

  • 异步上报

    框架会缓存上报数据,等到框架内的上报线程被调度时再将数据上报,不会堵塞应用线程。当您不关心实际的上报次数,只需最后一次上报的状态和设备的实际状态保持一致时,就可以选择这种上报方式,如上报开关状态等。

  • 同步上报

    框架立即上报数据,会堵塞应用线程,且不会过滤相同值的 DP 数据。有些场景对 DP 的上报次数需要精准控制,应用需要立即知道这次上报的结果,做出相应处理。例如,上报一些统计类型数据如电量,如果本次上报不成功,应用需要存储数据,等待下一次上报。

    透传类型(Raw)DP 只支持同步上报的方式

DP 下发

框架是通过回调函数来通知应用处理收到的 DP 指令。涉及到的回调函数有三个,分别是 OBJ 类 DP 处理回调RAW 类 DP 处理回调DP 查询处理回调,这些回调是在 设备初始化 时由应用注册的。

DP 数据同步

同步的数据包会带同步标识用于区分设备主动上报数据包,防止误触发场景联动。

设备需要不定时的与云端和客户端同步状态,以确保多端的状态保持一致。当前同步方式主要分成设备主动同步,查询同步。

  • 主动同步

    设备在以下几个场景会主动同步框架内缓存的 OBJ 类 DP 的数据。

    Raw 类 DP 不会缓存数据,即也不会有主动同步的功能。

    • MQTT 上线,包括设备启动后第一次上线和 MQTT 断开后重新上线
    • 蓝牙连接配对成功
    • 上报数据失败或者同步数据失败时,会启动 2S 定时器触发同步上报,直到成功。
  • 查询同步

    云端或者客户端主动发起查询指令,设备收到查询指令后回复对应 DP 的状态。框架通过 DP 查询处理回调 通知应用处理查询指令。应用回复指令时需要调用对应的 查询上报的接口

DP 流控

流控是指为了避免单位时间内单个 DP 上报频率过高,导致云端流量消耗,固件端通过对不同 DP 使用不同时间粒度进行流量控制。

目前默认规则是 60s 内,单个 DP 上报次数不能超过 200 条,超过后会触发流控,模组会暂时停止上报。等当前上报周期结束,启动下个上报周期时,再重新往云端上报数据。

流程

DP 上报流程

上报开始
DP 校验通过
更新 DP (OBJ) 缓存
组上报数据包
向对应通道上发送数据
上报结束

DP 校验流程

YES
YES
YES
YES
YES
设备已激活
存在可用通道
合法性校验通过
没有超流控限制
DP 无需被过滤
校验通过

DP 下发流程

框架接收到 OBJ 型 DP 下发指令后会将下发的 DP 值与缓存中的 DP 值进行对比。如果两者一致,则框架会开启 DP 数据同步流程,并且不会通过回调函数通知应用处理。

如果是 RAW 型或者框架内此时没有有效的缓存值,则会通知应用处理,走后续流程。

YES
NO
接收下发的 DP 数据
DP 与缓存值不同
通过回调函数通知应用
应用处理 DP 指令
结束
同步缓存的数据

DP 数据同步流程

YES
NO
开始
通道选择
组对应通道的数据包
向对应通道发送数据包
上报成功
结束
启动 2S 的定时器

数据结构

单个 DP 数据结构体

上报 DP 时的时间戳填写规则:

  • 实时型 DP 的时间戳填 0,框架会自动在上报时填入上报时的时间。

  • 记录型 DP 的时间戳按照参数按照产生 DP 记录的时间来填写。

/**
 * @brief Definition of dp property type
 */
typedef BYTE_T DP_PROP_TP_E;
#define PROP_BOOL 0
#define PROP_VALUE 1
#define PROP_STR 2
#define PROP_ENUM 3
#define PROP_BITMAP 4

// DP 值联合体
typedef union {
    INT_T dp_value;             // valid when dp type is value
    UINT_T dp_enum;             // valid when dp type is enum
    CHAR_T *dp_str;             // valid when dp type is str
    BOOL_T dp_bool;             // valid when dp type is bool
    UINT_T dp_bitmap;           // valid when dp type is bitmap
} TY_OBJ_DP_VALUE_U;

typedef struct {
    /** dp ID */
    BYTE_T dpid;
    /** dp type, see DP_PROP_TP_E */
    DP_PROP_TP_E type;
    /** dp value, see TY_OBJ_DP_VALUE_U */
    TY_OBJ_DP_VALUE_U value;
    /** dp happen time. if 0, mean now */
    UINT_T time_stamp;
} TY_OBJ_DP_S;

接收 DP 数据结构体

应用收到 DP 指令通知,可以通过结构体获取到 DP 指令的来源传输的类型 等重要信息。

  • cid:

    • 单品类设备

      cid 始终是 NULL。

    • 网关类设备

      cid 是 NULL 时,则表示该 DP 指令是控制网关的。

      cid 有值时,cid 的内容是子设备的 ID,即该 DP 指令是发送给这个子设备的。

  • mb_id:

    群组 ID,主要用于网关类设备。

/**
 * @brief tuya sdk dp cmd type
 */
typedef BYTE_T DP_CMD_TYPE_E;
#define DP_CMD_LAN      0       // cmd from LAN
#define DP_CMD_MQ       1       // cmd from MQTT
#define DP_CMD_TIMER    2       // cmd from Local Timer
#define DP_CMD_SCENE_LINKAGE 3 // cmd from scene linkage
#define DP_CMD_RELIABLE_TRANSFER 4 // cmd from reliable transfer
#define DP_CMD_BT       5      // cmd from bt
#define DP_CMD_SCENE_LINKAGE_LAN 6 // cmd from lan scene linkage
#define DP_CMD_FFC      7       // cmd from ffc

/**
 * @brief tuya sdk dp trans type
 */
typedef BYTE_T DP_TRANS_TYPE_T;
#define DTT_SCT_UNC     0       // unicast
#define DTT_SCT_BNC     1       // boardcast
#define DTT_SCT_MNC     2       // multicast
#define DTT_SCT_SCENE   3       // scene

/**
 * @brief Definition of recved structured dp
 */
typedef struct {
    /** see DP_CMD_TYPE_E */
    DP_CMD_TYPE_E cmd_tp;
    /** see DP_TRANS_TYPE_T */
    DP_TRANS_TYPE_T dtt_tp;
    /** if(NULL == cid) then then the cid represents gwid */
    CHAR_T *cid;
    /** mb ID */
    CHAR_T *mb_id;
    /** count of dp */
    UINT_T dps_cnt;
    /** the dp data */
    TY_OBJ_DP_S dps[0];
} TY_RECV_OBJ_DP_S;

/**
 * @brief Definition of recved raw dp
 */
typedef struct {
    /** see DP_CMD_TYPE_E */
    DP_CMD_TYPE_E cmd_tp;
    /** see DP_TRANS_TYPE_T */
    DP_TRANS_TYPE_T dtt_tp;
    /** if(NULL == cid) then then the cid represents gwid */
    CHAR_T *cid;
    /** dp ID */
    BYTE_T dpid;
    /** mb ID */
    CHAR_T *mb_id;
    /** data len */
    UINT_T len;
    /** the data */
    BYTE_T data[0];
} TY_RECV_RAW_DP_S;

查询 DP 数据结构体

/**
 * @brief Definition of dp report type
 */
typedef BYTE_T DP_REPT_TYPE_E;
#define T_OBJ_REPT      0           // dp is value,str,enum,bool,bitmap
#define T_RAW_REPT      1           // raw type
#define T_STAT_REPT     2           // stat type
#define T_RE_TRANS_REPT 10          // repeat report

typedef struct {
    /** report type, see DP_REPT_TYPE_E */
    DP_REPT_TYPE_E dp_rept_type;
    /** obj:为 TY_OBJ_DP_REPT_S,stat:dp 为 TY_STAT_DP_REPT_S,RAW:TY_RAW_DP_REPT_S */
    VOID_T*         dp_data;
    /** Json decoded dp string */
    CHAR_T*         dp_data_json;
    /** is query or not */
    BYTE_T          is_query;
} DP_REPT_CB_DATA;

开发指导

关联头文件

  • tuya_iot_com_api.h

使用方法

  • 上报数据

    设备初始化 后,根据应用场景调用对应的上报接口。

  • 处理下发 DP 指令

    设备初始化 时应用注册 OBJ 类 DP 处理回调RAW 类 DP 处理回调 在回调函数中处理指令。

  • 返回查询 DP 的状态

    设备初始化 时应用注册 DP 查询处理回调,在回调函数中调用 查询上报的接口,返回查询 DP 所需的数据。

API 说明

对象类 DP 异步上报

异步上报接口内会对 DP 进行校验,DP 通过校验后将会更新缓存数据并组对应数据包,然后触发相应上报事件。

异步上报接口返回成功,只是表示触发上报事件成功。并不能表示 DP 已经被成功上报。

方式一:普通上报

该接口会根据 涂鸦 IoT 开发平台 上对 DP 能否重复上报的配置来决定是否开启过滤。

/**
 * @brief dev_report_dp_json_async
 * @desc report dp info a-synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                    if gateway/soc/mcu, then devid = NULL
 * @param[in] dp_data: dp array header
 * @param[in] cnt: dp array count
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_report_dp_json_async(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt);

方式二:强制上报

该接口不会开启过滤功能,无论 涂鸦 IoT 开发平台 配置如何都上报数据。

/**
 * @brief dev_report_dp_json_async_force
 * @desc report dp info a-synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                if gateway/soc/mcu, then devid = NULL
 * @param[in] dp_data: dp array header
 * @param[in] cnt: dp array count
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_report_dp_json_async_force(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt);

对象类 DP 同步上报 & 记录型数据上报

同步上报接口内会对 DP 进行校验,DP 通过校验后将会将数据立即上报。接口调用结束后,您可通过返回值得知本次上报是否成功。一般记录型数据都用该接口上报。如果上报失败了,您需要将数据存储起来或进行数据累加,然后启动线程或者定时器,隔一段时间后做下一次上报,直到上报成功后删除记录或者清空数据。

同步上报接口会堵塞应用线程,对应用的堆栈深度要求也会比较高。堵塞时间可通过接口参数进行配置,一般设置 5S。

/**
 * @brief dev_report_dp_stat_sync_extend
 * @desc: report dp status info synced.
 *        if time_stamp==0, time_stamp = time of msg arrival of the server
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                    if gateway/soc/mcu, then devid = NULL
 * @param[in] dp_data: dp status array header
 * @param[in] cnt: dp status array count
 * @param[in] timeout: function blocks until timeout seconds
 * @param[in] enable_auto_retrans
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_report_dp_stat_sync_extend(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data,
                                           IN CONST UINT_T cnt, IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans);
#define dev_report_dp_stat_sync(dev_id, dp_data, cnt, timeout) \
    dev_report_dp_stat_sync_extend(dev_id, dp_data, cnt, timeout, TRUE)

对象类 DP 查询上报

支持对象类型 DP 方式查询上报,在收到 DP 查询指令后,可调用该接口进行回复。这个上报接口内部组包时会带同步标识用于区分设备主动上报数据包,防止误触发场景联动。

/**
 * @brief dev_query_dp_json_async
 * @desc report dp info a-synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                if gateway/soc/mcu, then devid = NULL
 * @param[in] dp_data: dp array header
 * @param[in] cnt: dp array count
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_query_dp_json_async(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt);

/**
 * @brief dev_query_dp_json_async_force
 * @desc report dp info a-synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                if gateway/soc/mcu, then devid = NULL
 * @param[in] dp_data: dp array header
 * @param[in] cnt: dp array count
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_query_dp_json_async_force(IN CONST CHAR_T *dev_id, IN CONST TY_OBJ_DP_S *dp_data, IN CONST UINT_T cnt);

透传类型 DP 同步上报

目前透传类型 DP 只支持同步上报,且框架不会对该 DP 数据进行缓存,即也不会有过滤和数据同步机制。

方式一:不带时间戳

用于上报时间就是 DP 发生时间的场景。

/**
 * @brief dev_report_dp_raw_sync_extend
 * @desc report dp raw info synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                if gateway/soc/mcu, then devid = NULL
 * @param[in] dpid: raw dp ID
 * @param[in] data: raw data
 * @param[in] len: len of raw data
 * @param[in] timeout: function blocks until timeout seconds
 * @param[in] enable_auto_retrans
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_report_dp_raw_sync_extend(IN CONST CHAR_T *dev_id, IN CONST BYTE_T dpid,
                                          IN CONST BYTE_T *data, IN CONST UINT_T len,
                                          IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans);
#define dev_report_dp_raw_sync(dev_id, dpid, data, len, timeout) \
    dev_report_dp_raw_sync_extend(dev_id, dpid, data, len, timeout, TRUE)

方式二:带时间戳

用于上报时间与 DP 发生时间并不相同的场景。

/**
 * @brief dev_report_dp_raw_sync_extend_with_time
 * @desc report dp raw info synced.
 *
 * @param[in] dev_id: if sub-device, then devid = sub-device_id
 *                if gateway/soc/mcu, then devid = NULL
 * @param[in] dpid: raw dp ID
 * @param[in] data: raw data
 * @param[in] len: len of raw data
 * @param[in] timeout: function blocks until timeout seconds
 * @param[in] enable_auto_retrans
 * @param[in] time_str
 *
 * @return OPRT_OK: success Other: fail
 */
OPERATE_RET dev_report_dp_raw_sync_extend_with_time(IN CONST CHAR_T *dev_id, IN CONST BYTE_T dpid,
                                                    IN CONST BYTE_T *data, IN CONST UINT_T len,
                                                    IN CONST UINT_T timeout, IN CONST BOOL_T enable_auto_retrans,
                                                    IN CONST CHAR_T *time_str);
#define dev_report_dp_raw_sync_with_time(dev_id, dpid, data, len, timeout, time_str) \
    dev_report_dp_raw_sync_extend_with_time(dev_id, dpid, data, len, timeout, TRUE, time_str)

常见问题

DP 的取值范围分别是多少?

  • 布尔型 DP 范围:真或假。

  • 数值型 DP 是用 Integer 类型存储与传输的。范围即是 Integer 的范围:-2147483648 ~ 2147483647。

  • 枚举 DP 范围:0 ~(枚举对象的个数 - 1)。

  • 字符串 DP 范围:长度不超过 255。

  • 透传类型 DP 范围:长度一般不建议超过 255,实际可达到 1024。

产品的 DP 在 IoT 平台上修改后,设备端什么时候能生效?

产品量产后不建议再修改 DP,很可能会导致设备部分功能不可用甚至彻底不能用。

  • 在 TuyaOS 3.3.0 及以上版本,支持 DP 的动态增加,设备重启后就会更新。

  • 如果框架版本低于 3.3.0 版本,或者 DP 进行了删除和范围等修改,需要设备重新配网后才会更新。

如何定义浮点型数值 DP?

设备内部处理数值型 DP 都以整型数据处理。如果该 DP 实际代表的数值存在小数。对设备端来说需要将数值扩大成整数进行处理。

不过,在 涂鸦 IoT 开发平台 上,您可以设置 DP 的倍率(scale),客户端收到数值后除以该倍率,还原该数值。如实际数值有 3 位小数,scale 可设置成 3,DP 上报后,客户端收到数据会将数值除以 1000 显示。