事件服务

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

本文介绍涂鸦事件服务。

概述

基本概念

涂鸦 TuyaOS Event 是一个轻量级的事件通知库。其特点是:

  • 支持订阅-发布模式的事件机制,使用非常简单方便,可以在任意位置、任意时间进行事件的订阅。
  • 轻量级,专注于进程内部的事件通知,代码小巧,仅 400 行。
  • 跨平台,基于涂鸦 TuyaOS 的跨平台特性,可以在任意平台上运行。
  • 有弹性,默认情况下事件是同步调用。基于涂鸦 TuyaOS 的跨平台特性,可以在订阅函数里使用工作队列实现异步机制。

功能描述

事件订阅发布机制可较好实现功能模块业务解耦,您可订阅系统启动、网络相关事件。实现事件按订阅顺序发送、紧急订阅类型先发送以及订阅单次发送功能。

工作原理/实现方案

ty_subscribe_eventty_publish_event轮询事件链表事件链表匹配 name 成功轮询事件链表匹配 name 成功订阅的事件相关处理回调存入对应事件链表结构体中的订阅链表轮询事件链表中的订阅链表返回轮询节点数据匹配节点 name 数据成功则执行订阅节点的回调完成数据传递轮询事件链表事件链表匹配 name 失败轮询事件链表匹配 name 失败订阅事件放入到未启动的订阅链表中创建事件节点并存在事件链表轮询未启动的事件链表中的订阅链表返回轮询节点数据匹配 name 成功则放入到事件结构体订阅链表中执行事件链表中的订阅链表成员回调完成数据同步ty_subscribe_eventty_publish_event

开发指导

运行环境

Wi-Fi 开发框架的通用功能,无特殊要求即可运行。

类型定义

#define EVENT_NAME_MAX_LEN (16)  // move to tuya_iot_config.h. use kconfig config. default is 16

/**
 * @brief max length of event description
 *
 */
#define EVENT_DESC_MAX_LEN (32)

/**
 * @brief subscriber type
 *
 */
typedef BYTE_T SUBSCRIBE_TYPE_E;

#define SUBSCRIBE_TYPE_NORMAL    0  // normal type, dispatch by the subscribe order, remove when unsubscribe

#define SUBSCRIBE_TYPE_EMERGENCY 1  // emergency type, dispatch first, remove when unsubscribe

#define SUBSCRIBE_TYPE_ONETIME   2  // one time type, dispatch by the subscribe order, remove after first time dispath

  • EVENT_NAME_MAX_LEN:事件名称最大长度,默认 16 字节。

  • EVENT_DESC_MAX_LEN:事件描述最大长度,32 字节。

  • SUBSCRIBE_TYPE_E:事件订阅类型。

    • SUBSCRIBE_TYPE_NORMAL:正常类型,按订阅顺序发送,取消订阅时删除。

    • SUBSCRIBE_TYPE_EMERGENCY:紧急类型,先发送,取消订阅时删除。

    • SUBSCRIBE_TYPE_ONETIME:单次类型,按订阅订单发送,第一次发送后删除。

回调函数

/**
* @brief event subscribe callback function type
*
*/
typedef INT_T(*EVENT_SUBSCRIBE_CB)(VOID_T *data);

API 说明

发布事件

/**
 * @brief: publish event
 *
 * @param[in] name: event name
 * @param[in] data: event data
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET ty_publish_event(CONST CHAR_T* name, VOID_T *data);

发布一个事件通知,包含事件的数据,告知所有订阅者进行处理。事件发布首先会搜索是否当前事件是否已经创建。

  • 没有被创建:说明之前并没有发布过相同的事件,但是可能存在订阅者,由于他们没有找到事件,会被暂存到 free_subscribe_root。因此需要从 free_subscribe_root 里查找是否有该事件的订阅者。如果有,则从 free_subscribe_root 里获取这些订阅者,挂载到事件的 subscribe_root,然后再进行事件的发布。

  • 已经被创建:说明之前已经发布过相同的事件,所有订阅者都已经被正常处理,无需再关心是否有订阅者在 free_subscribe_root 中,可以直接进行事件发布。

  • 事件发布,遍历事件 subscribe_root,对于每个订阅者,发布事件的数据,调用订阅者的回调函数,判断并记录回调函数的返回值。如果不存在订阅者,意味着不需要发布,也不需要创建事件的资源。

订阅事件

/**
 * @brief: subscribe event
 *
 * @param[in] name: event name
 * @param[in] desc: subscribe description
 * @param[in] cb: subscribe callback function
 * @param[in] type: subscribe type
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
int ty_subscribe_event(const char *name, const char *desc, const event_subscribe_cb cb, SUBSCRIBE_TYPE_E type);

订阅一个事件,包含事件的名称、关注的用途以及处理数据的回调函数。事件订阅首先搜索当前事件是否已经创建。

  • 没有被创建:说明此事件没有产生过,需要把订阅者暂存到 free_subscribe_root
  • 已经被创建:说明此事件已经产生过,可以直接把订阅者挂载到事件的 subscribe_root

订阅事件不会获取事件的上一次状态,一个原因是如果暂存数据会消耗较大的资源,另外一个原因是没有必要。

取消事件订阅

/**
 * @brief: unsubscribe event
 *
 * @param[in] name: event name
 * @param[in] desc: subscribe description
 * @param[in] cb: subscribe callback function
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
int ty_unsubscribe_event(const char *name, const char *desc, event_subscribe_cb cb);

取消订阅一个事件,包含事件的名称、关注的用途以及处理数据的回调函数。如果当前订阅者没有绑定事件,则从 free_subscribe_root 移除。

如果订阅者绑定了事件且为最后一个订阅者,则需要从事件的 subscribe_root 移除,并销毁该事件。否则,仅从事件的 subscribe_root 中移除订阅者。

使用示例

#define EVENT_SAMPLE "publish.sample"
OPERATE_RET sample_subcribe_cb(event_data_t *raw_data)
{
    event_data_t *data = (event_data_t*)raw_data;

    TAL_PR_DEBUG("recv event");
    return OPRT_OK;
}

OPERATE_RET sample_subcribe_emergence_cb(event_data_t *raw_data)
{
    event_data_t *data = (event_data_t*)raw_data;

    TAL_PR_DEBUG("recv event emergence");
    return OPRT_OK;
}

OPERATE_RET sample_subcribe_onetime_cb(event_data_t *raw_data)
{
    event_data_t *data = (event_data_t*)raw_data;

    TAL_PR_DEBUG("recv event emergence");
    return OPRT_OK;
}

OPERATE_RET sample_event()
{
    OPERATE_RET rt = OPRT_OK;

     // 发布事件,事件没有订阅者,不会创建
    rt = ty_publish_event(EVENT_SAMPLE, NULL);
    EXPECT_EQ(rt, OPRT_OK);

    // 订阅事件
    char desc[] = "subscribe.sample";
    rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_cb, EVENT_TYPE_NORMAL);
    EXPECT_EQ(rt, OPRT_OK);

    // 发布事件
    rt = ty_publish_event(EVENT_SAMPLE, NULL);
    EXPECT_EQ(rt, OPRT_OK);

    // 紧急订阅事件
    rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_emergence_cb,                                         SUBSCRIBE_TYPE_EMERGENCY);
    EXPECT_EQ(rt, OPRT_OK);

    // 取消紧急订阅
    rt = ty_unsubscribe_event(EVENT_SAMPLE, desc, sample_subcribe_emergence_cb);
    EXPECT_EQ(rt, OPRT_OK);

    // 订阅一次性事件,一次性事件不需要手动取消订阅
    rt = ty_subscribe_event(EVENT_SAMPLE, desc, sample_subcribe_onetime_cb,                                         EVENT_TYPE_ONETIME);
    EXPECT_EQ(rt, OPRT_OK);

    // 发布事件
    rt = ty_publish_event(EVENT_SAMPLE, NULL);
    EXPECT_EQ(rt, OPRT_OK);

     // 取消订阅,当事件没有订阅者,会自动销毁
    rt = ty_unsubscribe_event(EVENT_SAMPLE, desc, sample_subcribe_cb);
    EXPECT_EQ(rt, OPRT_OK);

    return OPRT_OK;
}