chore: migrate project into clean repository

This commit is contained in:
yuuux
2026-08-13 16:50:52 +08:00
commit d1d25a09e7
27405 changed files with 9422808 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
cmake_minimum_required(VERSION 3.10)
# 检查是否在扫描笔工程中构建
if(DEFINED ENV{ARCS_BASE})
# 扫描笔工程环境下的构建
listenai_library_named(ebus)
listenai_library_sources(
ebus.c
)
listenai_include_directories(
./include
./port
)
else()
add_library(ebus STATIC "")
target_include_directories(ebus PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/include
)
target_sources(ebus PRIVATE
./ebus.c
)
if(CONFIG_EBUS_ENV_OS_POSIX)
target_link_libraries(ebus PRIVATE pthread)
else()
target_link_libraries(ebus PUBLIC freertos)
endif()
if(DEFINED LISTENAI_CMAKE_PATH)
listenai_link_libraries(ebus)
endif()
endif()
# else()
# # Linux环境下的构建
# project(ebus C)
# # 设置C标准
# set(CMAKE_C_STANDARD 99)
# set(CMAKE_C_STANDARD_REQUIRED ON)
# # 添加配置选项
# option(EBUS_ENV_OS_POSIX "使用POSIX环境" ON)
# option(EBUS_ENV_OS_FREERTOS "使用FreeRTOS环境" OFF)
# # 根据选项设置编译定义
# if(EBUS_ENV_OS_POSIX)
# add_definitions(-DCONFIG_EBUS_ENV_OS_POSIX=1)
# elseif(EBUS_ENV_OS_FREERTOS)
# add_definitions(-DCONFIG_EBUS_ENV_OS_FREERTOS=1)
# endif()
# # 添加源文件
# add_library(ebus STATIC
# ebus.c
# )
# # 添加头文件路径
# target_include_directories(ebus PUBLIC
# ${CMAKE_CURRENT_SOURCE_DIR}/inlcude
# ${CMAKE_CURRENT_SOURCE_DIR}/port
# )
# # 如果是POSIX环境链接pthread库
# if(EBUS_ENV_OS_POSIX)
# target_link_libraries(ebus PRIVATE pthread)
# endif()
# # 安装目标
# install(TARGETS ebus
# ARCHIVE DESTINATION lib
# LIBRARY DESTINATION lib
# )
# install(DIRECTORY inlcude/
# DESTINATION include
# FILES_MATCHING PATTERN "*.h"
# )
# endif()
# # 为POSIX环境创建port/posix目录下的实现
# if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/port/posix")
# file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/port/posix")
# endif()
# # 为FreeRTOS环境创建port/freertos目录下的实现
# if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/port/freertos")
# file(MAKE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/port/freertos")
# endif()

48
modules/ebus/Kconfig Normal file
View File

@@ -0,0 +1,48 @@
menuconfig EBUS
bool "ebus translation"
default n
help
Enable ebus support
if EBUS
choice
prompt "Select Operating System"
default EBUS_ENV_OS_FREERTOS
config EBUS_ENV_OS_POSIX
bool "POSIX"
config EBUS_ENV_OS_FREERTOS
bool "FreeRTOS"
endchoice
config EBUS_PERF_MONITOR
bool "Enable ebus performance monitor"
default y
help
Enable ebus performance monitor
config EBUS_PERF_MONITOR_TIME_MS_MAX
int "ebus performance monitor time ms max"
default 5000
depends on EBUS_PERF_MONITOR
help
Ebus performance monitor time ms max
config EBUS_PERF_MONITOR_WARN_THRESHOLD_MS
int "ebus performance monitor warn threshold ms"
default 20
depends on EBUS_PERF_MONITOR
help
Ebus performance monitor warn threshold ms
config EBUS_PERF_MONITOR_PANIC_ON_TIMEOUT
bool "ebus performance monitor panic on timeout"
default n
depends on EBUS_PERF_MONITOR
help
Ebus performance monitor panic on timeout
endif

528
modules/ebus/ebus.c Normal file
View File

@@ -0,0 +1,528 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#define TAG "ebus.core"
#include "port/dlist.h"
#include "port/platform.h"
#include "ebus/ebus.h"
#include "lisa_thread.h"
#include "lisa_log.h"
#include "lisa_queue.h"
#include "lisa_time.h"
#include "lisa_semaphore.h"
#define EBUS_NAME_MAX_LEN 32
#define EBUS_CHN_NAME_MAX_LEN 32
#define EBUS_CB_EXEC_TIME_MS_MAX (CONFIG_EBUS_PERF_MONITOR_TIME_MS_MAX)
#define EBUS_CB_EXEC_TIME_MS_WARN_THRESHOLD (CONFIG_EBUS_PERF_MONITOR_WARN_THRESHOLD_MS)
typedef struct ebus_chn {
char name[EBUS_CHN_NAME_MAX_LEN];
void *bus;
sys_dnode_t node;
sys_dlist_t subscriber_list;
ebus_env_mutex_handle_t mutex;
} ebus_chn_t;
typedef struct {
ebus_chn_cb_t cb;
void *user_data;
ebus_subscribe_type_e type;
uint32_t filter;
sys_dnode_t node;
} ebus_chn_subscriber_t;
typedef struct ebus_handle {
char name[EBUS_NAME_MAX_LEN];
sys_dnode_t node;
sys_dlist_t chn_list;
ebus_env_mutex_handle_t mutex;
lisa_queue_t *queue;
lisa_thread_t *thread;
lisa_thread_t *daemon_thread;
lisa_semaphore_t *daemon_start_sem;
lisa_semaphore_t *daemon_stop_sem;
void *curr_cb;
void *curr_evt;
void *curr_ch;
} ebus_handle_t;
typedef struct {
ebus_env_mutex_handle_t mutex;
sys_dlist_t bus_list;
} _ebus_list_t;
static _ebus_list_t *s_ebus_list = NULL;
struct ebus_msg {
ebus_chn_t *chn;
uint32_t evt;
void *data;
uint32_t len;
};
int ebus_init(void)
{
LOGI("ebus_init");
if (s_ebus_list != NULL) {
LOGI("ebus_init, ebus_list is not null");
return 0;
}
s_ebus_list = platform_malloc(sizeof(_ebus_list_t));
if (s_ebus_list == NULL) {
LOGE("ebus_init, malloc ebus_list failed");
return -ENOMEM;
}
memset(s_ebus_list, 0, sizeof(_ebus_list_t));
if (0 != ebus_env_mutex_create(&s_ebus_list->mutex)) {
LOGE("ebus_init, create mutex failed");
platform_free(s_ebus_list);
return -ENOMEM;
}
sys_dlist_init(&s_ebus_list->bus_list);
LOGI("ebus_init, ebus_list init success");
return 0;
}
ebus_handle_t *ebus_create(const char *bus_name)
{
ebus_handle_t *bus;
ebus_env_mutex_lock(&s_ebus_list->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&s_ebus_list->bus_list, bus, node)
{
if (strcmp(bus->name, bus_name) == 0) {
ebus_env_mutex_unlock(&s_ebus_list->mutex);
return NULL;
}
}
ebus_env_mutex_unlock(&s_ebus_list->mutex);
bus = platform_malloc(sizeof(ebus_handle_t));
if (bus == NULL) {
return NULL;
}
memset(bus, 0, sizeof(ebus_handle_t));
snprintf(bus->name, sizeof(bus->name), "%s", bus_name);
ebus_env_mutex_create(&bus->mutex);
sys_dlist_init(&bus->chn_list);
sys_dlist_append(&s_ebus_list->bus_list, &bus->node);
return bus;
_FOUND:
EBUS_ERR("ebus %s already exists", bus_name);
platform_free(bus);
return NULL;
}
ebus_handle_t *ebus_find(const char *bus_name)
{
ebus_handle_t *bus;
ebus_env_mutex_lock(&s_ebus_list->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&s_ebus_list->bus_list, bus, node)
{
if (strcmp(bus->name, bus_name) == 0) {
ebus_env_mutex_unlock(&s_ebus_list->mutex);
return bus;
}
}
ebus_env_mutex_unlock(&s_ebus_list->mutex);
return NULL;
}
ebus_handle_t *ebus_ch_bus_get(ebus_chn_t *ch)
{
return ch ? ch->bus : NULL;
}
ebus_chn_t *ebus_ch_find(ebus_handle_t *bus, const char *chn_name)
{
ebus_chn_t *chn;
if (bus == NULL || chn_name == NULL) {
return NULL;
}
ebus_env_mutex_lock(&bus->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&bus->chn_list, chn, node)
{
if (strcmp(chn->name, chn_name) == 0) {
ebus_env_mutex_unlock(&bus->mutex);
return chn;
}
}
ebus_env_mutex_unlock(&bus->mutex);
return NULL;
}
ebus_chn_t *ebus_ch_find_by_name(const char *bus_name, const char *chn_name)
{
ebus_handle_t *bus;
ebus_chn_t *chn;
bus = ebus_find(bus_name);
if (bus == NULL) {
return NULL;
}
chn = ebus_ch_find(bus, chn_name);
if (chn == NULL) {
return NULL;
}
return chn;
}
static void ebus_daemon_thread(void *arg)
{
ebus_handle_t *bus = (ebus_handle_t *)arg;
while (1) {
lisa_semaphore_take(bus->daemon_start_sem, LISA_OS_WAIT_FOREVER);
if (lisa_semaphore_take(bus->daemon_stop_sem, EBUS_CB_EXEC_TIME_MS_MAX) != LISA_OK) {
LOGE("ebus performance monitor, bus blocked, bus: %s, chn: %p evt: %d cb: %p", bus->name, bus->curr_ch,
bus->curr_evt, bus->curr_cb);
#if CONFIG_EBUS_PERF_MONITOR_PANIC_ON_TIMEOUT
/* 运行到这里, 说明总线上存在事件处理函数执行时间超过EBUS_CB_EXEC_TIME_MS_MAX */
assert(0);
#endif
}
}
}
static void ebus_thread_entry(void *arg)
{
ebus_handle_t *bus = (ebus_handle_t *)arg;
struct ebus_msg msg;
while (1) {
lisa_queue_t *queue = bus->queue;
assert(queue != NULL);
lisa_err_t st = lisa_queue_pop(queue, &msg, sizeof(msg), LISA_OS_WAIT_FOREVER);
if (st != LISA_OK) {
continue;
}
ebus_chn_t *chn = msg.chn;
if (chn == NULL) {
continue;
}
LOGI("ebus msg received, chn:%p, evt:%d", chn, msg.evt);
ebus_chn_subscriber_t *subscriber;
ebus_env_mutex_lock(&chn->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&chn->subscriber_list, subscriber, node)
{
if (((subscriber->filter == EBUS_EVENT_ALL) || (subscriber->filter == msg.evt)) &&
(subscriber->cb != NULL)) {
#if CONFIG_EBUS_PERF_MONITOR
uint32_t time = lisa_os_get_tick_ms();
bus->curr_ch = chn;
bus->curr_evt = msg.evt;
bus->curr_cb = subscriber->cb;
assert(bus->daemon_start_sem != NULL);
lisa_semaphore_give(bus->daemon_start_sem);
#endif
subscriber->cb(chn, msg.evt, msg.data, msg.len, subscriber->user_data);
#if CONFIG_EBUS_PERF_MONITOR
assert(bus->daemon_stop_sem != NULL);
lisa_semaphore_give(bus->daemon_stop_sem);
uint32_t elapsed = lisa_os_get_tick_ms() - time;
if (elapsed > EBUS_CB_EXEC_TIME_MS_WARN_THRESHOLD) {
LOGW("event bus performance monitor, event %d exec elapsed time: %d/%d ms, cb:%p", msg.evt, elapsed,
EBUS_CB_EXEC_TIME_MS_WARN_THRESHOLD, subscriber->cb);
}
#endif
}
}
ebus_env_mutex_unlock(&chn->mutex);
if (msg.data != NULL) {
platform_free(msg.data);
}
}
}
ebus_handle_t *ebus_create_async(const char *bus_name, uint32_t queue_size, uint32_t thread_stack_size,
uint32_t thread_priority)
{
ebus_handle_t *bus;
bus = ebus_find(bus_name);
if (bus != NULL) {
return bus;
}
bus = ebus_create(bus_name);
if (bus == NULL) {
return NULL;
}
bus->queue = lisa_queue_create(queue_size, (char *)bus_name, sizeof(struct ebus_msg));
if (bus->queue == NULL) {
ebus_destroy(bus);
return NULL;
}
#if CONFIG_EBUS_PERF_MONITOR
bus->curr_ch = NULL;
bus->curr_evt = NULL;
bus->curr_cb = NULL;
bus->daemon_start_sem = lisa_semaphore_create(1);
if (bus->daemon_start_sem == NULL) {
lisa_queue_delete(bus->queue);
ebus_destroy(bus);
return NULL;
}
bus->daemon_stop_sem = lisa_semaphore_create(1);
if (bus->daemon_stop_sem == NULL) {
lisa_queue_delete(bus->queue);
lisa_semaphore_delete(bus->daemon_start_sem);
ebus_destroy(bus);
return NULL;
}
lisa_thread_attr_t daemon_attr = {
.name = (char *)"ebus_daemon",
.stack_size = 2048,
.priority = thread_priority + 1,
};
bus->daemon_thread = lisa_thread_create(&daemon_attr, ebus_daemon_thread, bus);
if (bus->daemon_thread == NULL) {
lisa_queue_delete(bus->queue);
lisa_semaphore_delete(bus->daemon_start_sem);
lisa_semaphore_delete(bus->daemon_stop_sem);
ebus_destroy(bus);
return NULL;
}
#endif
lisa_thread_attr_t attr = {
.name = (char *)bus_name,
.stack_size = thread_stack_size,
.priority = thread_priority,
};
bus->thread = lisa_thread_create(&attr, ebus_thread_entry, bus);
if (bus->thread == NULL) {
#if CONFIG_EBUS_PERF_MONITOR
lisa_semaphore_delete(bus->daemon_start_sem);
lisa_semaphore_delete(bus->daemon_stop_sem);
lisa_thread_delete(bus->daemon_thread);
#endif
lisa_queue_delete(bus->queue);
ebus_destroy(bus);
return NULL;
}
return bus;
}
int ebus_destroy(ebus_handle_t *bus)
{
ebus_env_mutex_lock(&s_ebus_list->mutex, EBUS_ENV_MAX_DELAY);
/* remove bus node from the list */
sys_dlist_remove(&bus->node);
ebus_env_mutex_unlock(&s_ebus_list->mutex);
platform_free(bus);
return 0;
}
ebus_chn_t *ebus_chn_create_attach(ebus_handle_t *bus, const char *chn_name)
{
ebus_chn_t *chn;
ebus_chn_t *chn_tmp;
chn = platform_malloc(sizeof(ebus_chn_t));
if (chn == NULL) {
return NULL;
}
memset(chn, 0, sizeof(ebus_chn_t));
if (0 != ebus_env_mutex_create(&chn->mutex)) {
platform_free(chn);
return NULL;
}
snprintf(chn->name, sizeof(chn->name), "%s", chn_name);
sys_dlist_init(&chn->subscriber_list);
chn->bus = bus;
ebus_env_mutex_lock(&bus->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&bus->chn_list, chn_tmp, node)
{
if (strcmp(chn_tmp->name, chn_name) == 0) {
goto _FOUND;
}
}
sys_dlist_append(&bus->chn_list, &chn->node);
ebus_env_mutex_unlock(&bus->mutex);
return chn;
_FOUND:
EBUS_ERR("ebus %s chn %s already exists", bus->name, chn_name);
ebus_env_mutex_destroy(&chn->mutex);
platform_free(chn);
return NULL;
}
ebus_chn_t *ebus_chn_bind(const char *bus_name, const char *chn_name)
{
ebus_handle_t *bus;
ebus_chn_t *chn = NULL;
int found = 0;
if ((s_ebus_list == NULL) || (bus_name == NULL) || (chn_name == NULL)) {
return NULL;
}
while (!found) {
ebus_env_mutex_lock(&s_ebus_list->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&s_ebus_list->bus_list, bus, node)
{
if (strcmp(bus->name, bus_name) == 0) {
SYS_DLIST_FOR_EACH_CONTAINER(&bus->chn_list, chn, node)
{
if (strcmp(chn->name, chn_name) == 0) {
found = 1;
break;
}
}
if (found) {
break;
}
}
}
ebus_env_mutex_unlock(&s_ebus_list->mutex);
if (!found) {
ebus_env_thread_delay_ms(10);
}
}
return chn;
}
ebus_chn_t *ebus_chn_get(const char *bus_name, const char *chn_name)
{
return ebus_chn_bind(bus_name, chn_name);
}
int ebus_message_subscribe(ebus_chn_t *chn, ebus_subscribe_type_e type, uint32_t code, ebus_chn_cb_t cb,
void *user_data)
{
ebus_chn_subscriber_t *subscriber;
if ((s_ebus_list == NULL) || (chn == NULL)) {
return -EINVAL;
}
subscriber = platform_malloc(sizeof(ebus_chn_subscriber_t));
if (subscriber == NULL) {
return -ENOMEM;
}
memset(subscriber, 0, sizeof(ebus_chn_subscriber_t));
subscriber->cb = cb;
subscriber->type = type;
subscriber->filter = code;
subscriber->user_data = user_data;
ebus_env_mutex_lock(&chn->mutex, EBUS_ENV_MAX_DELAY);
sys_dlist_append(&chn->subscriber_list, &subscriber->node);
ebus_env_mutex_unlock(&chn->mutex);
return 0;
}
int ebus_message_unsubscribe(ebus_chn_t *chn, ebus_chn_cb_t cb)
{
ebus_chn_subscriber_t *subscriber;
ebus_env_mutex_lock(&chn->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&chn->subscriber_list, subscriber, node)
{
if (subscriber->cb == cb) {
sys_dlist_remove(&subscriber->node);
platform_free(subscriber);
}
}
ebus_env_mutex_unlock(&chn->mutex);
return 0;
}
int ebus_message_pub(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size)
{
ebus_chn_subscriber_t *subscriber;
ebus_env_mutex_lock(&chn->mutex, EBUS_ENV_MAX_DELAY);
SYS_DLIST_FOR_EACH_CONTAINER(&chn->subscriber_list, subscriber, node)
{
if (((subscriber->filter == EBUS_EVENT_ALL) || (subscriber->filter == code)) && (subscriber->cb != NULL)) {
subscriber->cb(chn, code, message, msg_size, subscriber->user_data);
}
}
ebus_env_mutex_unlock(&chn->mutex);
return 0;
}
int ebus_message_pub_async(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size)
{
return ebus_message_pub_async_timeout(chn, code, message, msg_size, LISA_OS_WAIT_FOREVER);
}
int ebus_message_pub_async_timeout(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size, int32_t timeout_ms)
{
struct ebus_msg msg;
ebus_chn_subscriber_t *subscriber;
memset(&msg, 0, sizeof(msg));
msg.chn = chn;
msg.evt = code;
msg.len = msg_size;
LOGI("ebus msg send, chn:%p, evt:%d", chn, code);
if (message != NULL) {
msg.data = platform_malloc(msg_size);
if (msg.data == NULL) {
LOGE("ebus msg send failed, no mem");
return -ENOMEM;
}
memcpy(msg.data, message, msg_size);
}
ebus_handle_t *bus = ebus_ch_bus_get(chn);
if (bus == NULL) {
LOGE("ebus msg send failed, bus not found");
return -EINVAL;
}
int ret = lisa_queue_push(bus->queue, &msg, sizeof(msg), timeout_ms);
if (ret != LISA_OK && msg.data != NULL) {
platform_free(msg.data);
}
return ret;
}

View File

@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.10)
project(ebus_example C)
# 设置C标准
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
# 添加调试符号
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0")
set(CONFIG_EBUS_ENV_OS_POSIX ON CACHE BOOL "Use POSIX environment" FORCE)
set(CONFIG_EBUS_ENV_OS_FREERTOS OFF CACHE BOOL "Use FreeRTOS environment" FORCE)
add_definitions(-DCONFIG_EBUS_ENV_OS_POSIX=1)
add_definitions(-UCONFIG_EBUS_ENV_OS_FREERTOS) # 取消FREERTOS定义
# 添加可执行文件
add_executable(ebus_example main.c)
# 设置包含目录
target_include_directories(ebus_example PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/../inlcude
)
# 链接ebus库
target_link_libraries(ebus_example PRIVATE
ebus
pthread
)
# 如果ebus库不在系统路径中需要指定库路径
if(NOT DEFINED LISTENAI_SDK_PATH)
# 添加ebus库的构建
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/ebus)
endif()
# 添加一个自定义目标,用于运行示例程序
add_custom_target(run_example
COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ebus_example
DEPENDS ebus_example
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "运行ebus示例程序"
)

View File

@@ -0,0 +1,159 @@
/**
* @file main.c
* @brief ebus框架示例程序
*
* 本示例展示了如何使用ebus框架创建总线、通道以及进行消息订阅和发布。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <ebus/ebus.h>
// 定义消息结构体
typedef struct {
int id;
char data[64];
} message_t;
// 全局变量,用于信号处理
volatile int g_running = 1;
// 信号处理函数
void signal_handler(int sig) {
printf("接收到信号 %d准备退出...\n", sig);
g_running = 0;
}
// 发布者通道回调函数
int publisher_callback(ebus_chn_t *chn, void *message, uint32_t msg_size, void *user_data) {
message_t *msg = (message_t *)message;
printf("发布者收到消息回复: ID=%d, 数据=%s\n", msg->id, msg->data);
return 0;
}
// 订阅者通道回调函数
int subscriber_callback(ebus_chn_t *chn, void *message, uint32_t msg_size, void *user_data) {
message_t *msg = (message_t *)message;
printf("订阅者收到消息: ID=%d, 数据=%s\n", msg->id, msg->data);
// 修改消息并返回
snprintf(msg->data, sizeof(msg->data), "已处理消息 %d", msg->id);
return 0;
}
int main(int argc, char *argv[]) {
// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
printf("ebus示例程序启动\n");
// 初始化ebus框架
int ret = ebus_init();
if (ret != EBUS_OK) {
printf("ebus初始化失败: %d\n", ret);
return -1;
}
printf("[###][%s %d]\n",__FUNCTION__,__LINE__);
printf("ebus初始化成功\n");
printf("[###][%s %d]\n",__FUNCTION__,__LINE__);
// 创建总线
ebus_handle_t *bus = ebus_create("test_bus");
if (bus == NULL) {
printf("创建总线失败\n");
return -1;
}
printf("创建总线 'test_bus' 成功\n");
// 创建并附加通道
ebus_chn_t *pub_channel = ebus_chn_create_attach(bus, "test_channel");
if (pub_channel == NULL) {
printf("创建通道失败\n");
ebus_destroy(bus);
return -1;
}
printf("创建通道 'test_channel' 成功\n");
// 订阅通道消息(发布者自己也可以订阅)
ret = ebus_message_subscribe(pub_channel, EBUS_SUBSCRIBER_TYPE_SYNC, publisher_callback);
if (ret != EBUS_OK) {
printf("发布者订阅通道失败: %d\n", ret);
ebus_destroy(bus);
return -1;
}
printf("发布者订阅通道成功\n");
// 创建第二个进程模拟订阅者
pid_t pid = fork();
if (pid < 0) {
printf("创建子进程失败\n");
ebus_destroy(bus);
return -1;
} else if (pid == 0) {
// 子进程 - 订阅者
printf("订阅者进程启动\n");
// 绑定到已存在的通道
ebus_chn_t *sub_channel = ebus_chn_bind("test_bus", "test_channel");
if (sub_channel == NULL) {
printf("订阅者绑定通道失败\n");
exit(-1);
}
printf("订阅者绑定通道成功\n");
// 订阅通道消息
ret = ebus_message_subscribe(sub_channel, EBUS_SUBSCRIBER_TYPE_SYNC, subscriber_callback);
if (ret != EBUS_OK) {
printf("订阅者订阅通道失败: %d\n", ret);
exit(-1);
}
printf("订阅者订阅通道成功\n");
// 子进程保持运行
while (g_running) {
sleep(1);
}
printf("订阅者进程退出\n");
exit(0);
} else {
// 父进程 - 发布者
printf("发布者进程继续运行\n");
// 等待子进程启动并订阅
sleep(2);
// 发布消息
int msg_count = 0;
while (g_running && msg_count < 10) {
message_t msg;
msg.id = msg_count + 1;
snprintf(msg.data, sizeof(msg.data), "测试消息 %d", msg.id);
printf("发布消息: ID=%d, 数据=%s\n", msg.id, msg.data);
ret = ebus_message_pub(pub_channel, &msg, sizeof(message_t), NULL);
if (ret != EBUS_OK) {
printf("发布消息失败: %d\n", ret);
} else {
printf("消息发布后的数据: ID=%d, 数据=%s\n", msg.id, msg.data);
}
msg_count++;
sleep(1);
}
// 等待子进程退出
kill(pid, SIGTERM);
int status;
waitpid(pid, &status, 0);
// 销毁总线
ebus_destroy(bus);
printf("总线已销毁,程序退出\n");
}
return 0;
}

View File

@@ -0,0 +1,145 @@
/**
* @file ebus.h
* @brief 软件总线通讯框架头文件
*
* ebus是一个轻量级的软件总线通讯框架用于组件间的消息传递。
* 它实现了发布-订阅模式,支持同步消息处理。
*/
#ifndef __EBUS_H__
#define __EBUS_H__
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 错误码定义
*/
#define EBUS_OK 0 /**< 操作成功 */
#define EBUS_ERROR -1 /**< 一般错误 */
#define EBUS_EVENT_ALL (0)
#define EBUS_EVENT_CODE_RESERVED (5)
/**
* @brief 总线句柄结构体(不透明类型)
*/
typedef struct ebus_handle ebus_handle_t;
/**
* @brief 通道结构体(不透明类型)
*/
typedef struct ebus_chn ebus_chn_t;
/**
* @brief 订阅类型枚举
*/
typedef enum {
EBUS_SUBSCRIBER_TYPE_SYNC, /**< 同步订阅 */
EBUS_SUBSCRIBER_TYPE_ASYNC, /**< 异步订阅(暂未实现)*/
} ebus_subscribe_type_e;
/**
* @brief 通道回调函数类型定义
*
* @param chn 通道句柄
* @param message 消息数据
* @param msg_size 消息大小
* @param user_data 用户数据
* @return int 回调处理结果0表示成功
*/
typedef int (*ebus_chn_cb_t)(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size, void *user_data);
/**
* @brief 初始化ebus框架
*
* 在使用ebus框架前必须先调用此函数进行初始化。
*
* @return int 0表示成功负值表示错误码
*/
int ebus_init(void);
/**
* @brief 创建一个总线
*
* @param bus_name 总线名称不能为NULL长度不超过EBUS_NAME_MAX_LEN
* @return ebus_handle_t* 成功返回总线句柄失败返回NULL
*/
ebus_handle_t *ebus_create(const char *bus_name);
/**
* @brief 销毁总线
*
* 释放总线相关的所有资源。
*
* @param bus 总线句柄不能为NULL
* @return int 0表示成功负值表示错误码
*/
int ebus_destroy(ebus_handle_t *bus);
/**
* @brief 创建并附加通道到总线
*
* @param bus 总线句柄不能为NULL
* @param chn_name 通道名称不能为NULL长度不超过EBUS_CHN_NAME_MAX_LEN
* @return ebus_chn_t* 成功返回通道句柄失败返回NULL
*/
ebus_chn_t *ebus_chn_create_attach(ebus_handle_t *bus, const char *chn_name);
/**
* @brief 绑定到已存在的通道
*
* 此函数会阻塞等待直到找到指定的通道。
*
* @param bus_name 总线名称不能为NULL
* @param chn_name 通道名称不能为NULL
* @return ebus_chn_t* 成功返回通道句柄失败返回NULL
*/
ebus_chn_t *ebus_chn_bind(const char *bus_name, const char *chn_name);
/**
* @brief 订阅通道消息
*
* @param chn 通道句柄不能为NULL
* @param code 消息代码
* @param type 订阅类型目前仅支持EBUS_SUBSCRIBER_TYPE_SYNC
* @param code 消息代码
* @param cb 回调函数不能为NULL
* @param user_data 用户数据,将传递给回调函数
* @return int 0表示成功负值表示错误码
*/
int ebus_message_subscribe(ebus_chn_t *chn, ebus_subscribe_type_e type, uint32_t code, ebus_chn_cb_t cb, void *user_data);
/**
* @brief 取消订阅
*
* @param chn 通道句柄不能为NULL
* @param cb 回调函数不能为NULL
* @return int
*/
int ebus_message_unsubscribe(ebus_chn_t *chn, ebus_chn_cb_t cb);
/**
* @brief 发布消息到通道
*
* 此函数会遍历所有订阅者并同步调用其回调函数。
*
* @param chn 通道句柄不能为NULL
* @param code 消息代码
* @param message 消息数据
* @param msg_size 消息大小
* @return int 0表示成功负值表示错误码
*/
int ebus_message_pub(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size);
int ebus_message_pub_async(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size);
int ebus_message_pub_async_timeout(ebus_chn_t *chn, uint32_t code, void *message, uint32_t msg_size, int32_t timeout_ms);
#ifdef __cplusplus
}
#endif
#endif /* __EBUS_H__ */

567
modules/ebus/port/dlist.h Normal file
View File

@@ -0,0 +1,567 @@
/*
* Copyright (c) 2013-2015 Wind River Systems, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @defgroup doubly-linked-list_apis Doubly-linked list
* @ingroup datastructure_apis
*
* @brief Doubly-linked list implementation
*
* Doubly-linked list implementation using inline macros/functions.
* This API is not thread safe, and thus if a list is used across threads,
* calls to functions must be protected with synchronization primitives.
*
* The lists are expected to be initialized such that both the head and tail
* pointers point to the list itself. Initializing the lists in such a fashion
* simplifies the adding and removing of nodes to/from the list.
*
* @{
*/
#ifndef _DLIST_H_
#define _DLIST_H_
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CONTAINER_OF
#define CONTAINER_OF(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
#endif
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif
struct _dnode {
union {
struct _dnode *head; /* ptr to head of list (sys_dlist_t) */
struct _dnode *next; /* ptr to next node (sys_dnode_t) */
};
union {
struct _dnode *tail; /* ptr to tail of list (sys_dlist_t) */
struct _dnode *prev; /* ptr to previous node (sys_dnode_t) */
};
};
/**
* @brief Doubly-linked list structure.
*/
typedef struct _dnode sys_dlist_t;
/**
* @brief Doubly-linked list node structure.
*/
typedef struct _dnode sys_dnode_t;
/**
* @brief Provide the primitive to iterate on a list
* Note: the loop is unsafe and thus __dn should not be removed
*
* User _MUST_ add the loop statement curly braces enclosing its own code:
*
* SYS_DLIST_FOR_EACH_NODE(l, n) {
* <user code>
* }
*
* This and other SYS_DLIST_*() macros are not thread safe.
*
* @param __dl A pointer on a sys_dlist_t to iterate on
* @param __dn A sys_dnode_t pointer to peek each node of the list
*/
#define SYS_DLIST_FOR_EACH_NODE(__dl, __dn) \
for (__dn = sys_dlist_peek_head(__dl); __dn != NULL; \
__dn = sys_dlist_peek_next(__dl, __dn))
/**
* @brief Provide the primitive to iterate on a list, from a node in the list
* Note: the loop is unsafe and thus __dn should not be removed
*
* User _MUST_ add the loop statement curly braces enclosing its own code:
*
* SYS_DLIST_ITERATE_FROM_NODE(l, n) {
* <user code>
* }
*
* Like SYS_DLIST_FOR_EACH_NODE(), but __dn already contains a node in the list
* where to start searching for the next entry from. If NULL, it starts from
* the head.
*
* This and other SYS_DLIST_*() macros are not thread safe.
*
* @param __dl A pointer on a sys_dlist_t to iterate on
* @param __dn A sys_dnode_t pointer to peek each node of the list;
* it contains the starting node, or NULL to start from the head
*/
#define SYS_DLIST_ITERATE_FROM_NODE(__dl, __dn) \
for (__dn = __dn ? sys_dlist_peek_next_no_check(__dl, __dn) \
: sys_dlist_peek_head(__dl); \
__dn != NULL; \
__dn = sys_dlist_peek_next(__dl, __dn))
/**
* @brief Provide the primitive to safely iterate on a list
* Note: __dn can be removed, it will not break the loop.
*
* User _MUST_ add the loop statement curly braces enclosing its own code:
*
* SYS_DLIST_FOR_EACH_NODE_SAFE(l, n, s) {
* <user code>
* }
*
* This and other SYS_DLIST_*() macros are not thread safe.
*
* @param __dl A pointer on a sys_dlist_t to iterate on
* @param __dn A sys_dnode_t pointer to peek each node of the list
* @param __dns A sys_dnode_t pointer for the loop to run safely
*/
#define SYS_DLIST_FOR_EACH_NODE_SAFE(__dl, __dn, __dns) \
for ((__dn) = sys_dlist_peek_head(__dl), \
(__dns) = sys_dlist_peek_next((__dl), (__dn)); \
(__dn) != NULL; (__dn) = (__dns), \
(__dns) = sys_dlist_peek_next(__dl, __dn))
/**
* @brief Provide the primitive to resolve the container of a list node
* Note: it is safe to use with NULL pointer nodes
*
* @param __dn A pointer on a sys_dnode_t to get its container
* @param __cn Container struct type pointer
* @param __n The field name of sys_dnode_t within the container struct
*/
#define SYS_DLIST_CONTAINER(__dn, __cn, __n) \
(((__dn) != NULL) ? CONTAINER_OF(__dn, __typeof__(*(__cn)), __n) : NULL)
/**
* @brief Provide the primitive to peek container of the list head
*
* @param __dl A pointer on a sys_dlist_t to peek
* @param __cn Container struct type pointer
* @param __n The field name of sys_dnode_t within the container struct
*/
#define SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n) \
SYS_DLIST_CONTAINER(sys_dlist_peek_head(__dl), __cn, __n)
/**
* @brief Provide the primitive to peek the next container
*
* @param __dl A pointer on a sys_dlist_t to peek
* @param __cn Container struct type pointer
* @param __n The field name of sys_dnode_t within the container struct
*/
#define SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n) \
(((__cn) != NULL) ? \
SYS_DLIST_CONTAINER(sys_dlist_peek_next((__dl), &((__cn)->__n)), \
__cn, __n) : NULL)
/**
* @brief Provide the primitive to iterate on a list under a container
* Note: the loop is unsafe and thus __cn should not be detached
*
* User _MUST_ add the loop statement curly braces enclosing its own code:
*
* SYS_DLIST_FOR_EACH_CONTAINER(l, c, n) {
* <user code>
* }
*
* @param __dl A pointer on a sys_dlist_t to iterate on
* @param __cn A container struct type pointer to peek each entry of the list
* @param __n The field name of sys_dnode_t within the container struct
*/
#define SYS_DLIST_FOR_EACH_CONTAINER(__dl, __cn, __n) \
for ((__cn) = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n); \
(__cn) != NULL; \
(__cn) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n))
/**
* @brief Provide the primitive to safely iterate on a list under a container
* Note: __cn can be detached, it will not break the loop.
*
* User _MUST_ add the loop statement curly braces enclosing its own code:
*
* SYS_DLIST_FOR_EACH_CONTAINER_SAFE(l, c, cn, n) {
* <user code>
* }
*
* @param __dl A pointer on a sys_dlist_t to iterate on
* @param __cn A container struct type pointer to peek each entry of the list
* @param __cns A container struct type pointer for the loop to run safely
* @param __n The field name of sys_dnode_t within the container struct
*/
#define SYS_DLIST_FOR_EACH_CONTAINER_SAFE(__dl, __cn, __cns, __n) \
for ((__cn) = SYS_DLIST_PEEK_HEAD_CONTAINER(__dl, __cn, __n), \
(__cns) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n); \
(__cn) != NULL; (__cn) = (__cns), \
(__cns) = SYS_DLIST_PEEK_NEXT_CONTAINER(__dl, __cn, __n))
/**
* @brief initialize list to its empty state
*
* @param list the doubly-linked list
*/
static inline void sys_dlist_init(sys_dlist_t *list)
{
list->head = (sys_dnode_t *)list;
list->tail = (sys_dnode_t *)list;
}
/**
* @brief Static initializer for a doubly-linked list
*/
#define SYS_DLIST_STATIC_INIT(ptr_to_list) { {(ptr_to_list)}, {(ptr_to_list)} }
/**
* @brief initialize node to its state when not in a list
*
* @param node the node
*/
static inline void sys_dnode_init(sys_dnode_t *node)
{
node->next = NULL;
node->prev = NULL;
}
/**
* @brief check if a node is a member of any list
*
* @param node the node
*
* @return true if node is linked into a list, false if it is not
*/
static inline bool sys_dnode_is_linked(const sys_dnode_t *node)
{
return node->next != NULL;
}
/**
* @brief check if a node is the list's head
*
* @param list the doubly-linked list to operate on
* @param node the node to check
*
* @return true if node is the head, false otherwise
*/
static inline bool sys_dlist_is_head(sys_dlist_t *list, sys_dnode_t *node)
{
return list->head == node;
}
/**
* @brief check if a node is the list's tail
*
* @param list the doubly-linked list to operate on
* @param node the node to check
*
* @return true if node is the tail, false otherwise
*/
static inline bool sys_dlist_is_tail(sys_dlist_t *list, sys_dnode_t *node)
{
return list->tail == node;
}
/**
* @brief check if the list is empty
*
* @param list the doubly-linked list to operate on
*
* @return true if empty, false otherwise
*/
static inline bool sys_dlist_is_empty(sys_dlist_t *list)
{
return list->head == list;
}
/**
* @brief check if more than one node present
*
* This and other sys_dlist_*() functions are not thread safe.
*
* @param list the doubly-linked list to operate on
*
* @return true if multiple nodes, false otherwise
*/
static inline bool sys_dlist_has_multiple_nodes(sys_dlist_t *list)
{
return list->head != list->tail;
}
/**
* @brief get a reference to the head item in the list
*
* @param list the doubly-linked list to operate on
*
* @return a pointer to the head element, NULL if list is empty
*/
static inline sys_dnode_t *sys_dlist_peek_head(sys_dlist_t *list)
{
return sys_dlist_is_empty(list) ? NULL : list->head;
}
/**
* @brief get a reference to the head item in the list
*
* The list must be known to be non-empty.
*
* @param list the doubly-linked list to operate on
*
* @return a pointer to the head element
*/
static inline sys_dnode_t *sys_dlist_peek_head_not_empty(sys_dlist_t *list)
{
return list->head;
}
/**
* @brief get a reference to the next item in the list, node is not NULL
*
* Faster than sys_dlist_peek_next() if node is known not to be NULL.
*
* @param list the doubly-linked list to operate on
* @param node the node from which to get the next element in the list
*
* @return a pointer to the next element from a node, NULL if node is the tail
*/
static inline sys_dnode_t *sys_dlist_peek_next_no_check(sys_dlist_t *list,
sys_dnode_t *node)
{
return (node == list->tail) ? NULL : node->next;
}
/**
* @brief get a reference to the next item in the list
*
* @param list the doubly-linked list to operate on
* @param node the node from which to get the next element in the list
*
* @return a pointer to the next element from a node, NULL if node is the tail
* or NULL (when node comes from reading the head of an empty list).
*/
static inline sys_dnode_t *sys_dlist_peek_next(sys_dlist_t *list,
sys_dnode_t *node)
{
return (node != NULL) ? sys_dlist_peek_next_no_check(list, node) : NULL;
}
/**
* @brief get a reference to the previous item in the list, node is not NULL
*
* Faster than sys_dlist_peek_prev() if node is known not to be NULL.
*
* @param list the doubly-linked list to operate on
* @param node the node from which to get the previous element in the list
*
* @return a pointer to the previous element from a node, NULL if node is the
* tail
*/
static inline sys_dnode_t *sys_dlist_peek_prev_no_check(sys_dlist_t *list,
sys_dnode_t *node)
{
return (node == list->head) ? NULL : node->prev;
}
/**
* @brief get a reference to the previous item in the list
*
* @param list the doubly-linked list to operate on
* @param node the node from which to get the previous element in the list
*
* @return a pointer to the previous element from a node, NULL if node is the
* tail or NULL (when node comes from reading the head of an empty
* list).
*/
static inline sys_dnode_t *sys_dlist_peek_prev(sys_dlist_t *list,
sys_dnode_t *node)
{
return (node != NULL) ? sys_dlist_peek_prev_no_check(list, node) : NULL;
}
/**
* @brief get a reference to the tail item in the list
*
* @param list the doubly-linked list to operate on
*
* @return a pointer to the tail element, NULL if list is empty
*/
static inline sys_dnode_t *sys_dlist_peek_tail(sys_dlist_t *list)
{
return sys_dlist_is_empty(list) ? NULL : list->tail;
}
/**
* @brief add node to tail of list
*
* This and other sys_dlist_*() functions are not thread safe.
*
* @param list the doubly-linked list to operate on
* @param node the element to append
*/
static inline void sys_dlist_append(sys_dlist_t *list, sys_dnode_t *node)
{
sys_dnode_t *const tail = list->tail;
node->next = list;
node->prev = tail;
tail->next = node;
list->tail = node;
}
/**
* @brief add node to head of list
*
* This and other sys_dlist_*() functions are not thread safe.
*
* @param list the doubly-linked list to operate on
* @param node the element to append
*/
static inline void sys_dlist_prepend(sys_dlist_t *list, sys_dnode_t *node)
{
sys_dnode_t *const head = list->head;
node->next = head;
node->prev = list;
head->prev = node;
list->head = node;
}
/**
* @brief Insert a node into a list
*
* Insert a node before a specified node in a dlist.
*
* @param successor the position before which "node" will be inserted
* @param node the element to insert
*/
static inline void sys_dlist_insert(sys_dnode_t *successor, sys_dnode_t *node)
{
sys_dnode_t *const prev = successor->prev;
node->prev = prev;
node->next = successor;
prev->next = node;
successor->prev = node;
}
/**
* @brief insert node at position
*
* Insert a node in a location depending on a external condition. The cond()
* function checks if the node is to be inserted _before_ the current node
* against which it is checked.
* This and other sys_dlist_*() functions are not thread safe.
*
* @param list the doubly-linked list to operate on
* @param node the element to insert
* @param cond a function that determines if the current node is the correct
* insert point
* @param data parameter to cond()
*/
static inline void sys_dlist_insert_at(sys_dlist_t *list, sys_dnode_t *node,
int (*cond)(sys_dnode_t *node, void *data), void *data)
{
if (sys_dlist_is_empty(list)) {
sys_dlist_append(list, node);
} else {
sys_dnode_t *pos = sys_dlist_peek_head(list);
while ((pos != NULL) && (cond(pos, data) == 0)) {
pos = sys_dlist_peek_next(list, pos);
}
if (pos != NULL) {
sys_dlist_insert(pos, node);
} else {
sys_dlist_append(list, node);
}
}
}
/**
* @brief remove a specific node from a list
*
* The list is implicit from the node. The node must be part of a list.
* This and other sys_dlist_*() functions are not thread safe.
*
* @param node the node to remove
*/
static inline void sys_dlist_remove(sys_dnode_t *node)
{
sys_dnode_t *const prev = node->prev;
sys_dnode_t *const next = node->next;
prev->next = next;
next->prev = prev;
sys_dnode_init(node);
}
/**
* @brief get the first node in a list
*
* This and other sys_dlist_*() functions are not thread safe.
*
* @param list the doubly-linked list to operate on
*
* @return the first node in the list, NULL if list is empty
*/
static inline sys_dnode_t *sys_dlist_get(sys_dlist_t *list)
{
sys_dnode_t *node = NULL;
if (!sys_dlist_is_empty(list)) {
node = list->head;
sys_dlist_remove(node);
}
return node;
}
/**
* @brief Compute the size of the given list in O(n) time
*
* @param list A pointer on the list
*
* @return an integer equal to the size of the list, or 0 if empty
*/
static inline size_t sys_dlist_len(sys_dlist_t *list)
{
size_t len = 0;
sys_dnode_t *node = NULL;
SYS_DLIST_FOR_EACH_NODE(list, node) {
len++;
}
return len;
}
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _DLIST_H_ */

View File

@@ -0,0 +1,222 @@
#ifndef __EBUS_PORT_PLATFORM_H_
#define __EBUS_PORT_PLATFORM_H_
#if CONFIG_EBUS_ENV_OS_FREERTOS
#include <stdlib.h>
#include "FreeRTOS.h"
#include "semphr.h"
#elif CONFIG_EBUS_ENV_OS_POSIX
#include <pthread.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#endif
#ifdef __cplusplus
extern "C"
{
#endif
#if CONFIG_EBUS_ENV_OS_FREERTOS
#define EBUS_ENV_MAX_DELAY 0xffffffff
// #define EBUS_LOG(fmt, ...)
// #define EBUS_ERR(fmt, ...)
#define EBUS_LOG(fmt, ...) printf("[EBUS] " fmt "\n", ##__VA_ARGS__)
#define EBUS_ERR(fmt, ...) printf("[EBUS ERROR] " fmt "\n", ##__VA_ARGS__)
static void *platform_malloc(uint32_t size)
{
return malloc(size);
}
static void platform_free(void *ptr)
{
free(ptr);
}
typedef struct
{
SemaphoreHandle_t mutex;
} ebus_env_mutex_handle_t;
static inline int ebus_env_mutex_create(ebus_env_mutex_handle_t *handle)
{
handle->mutex = xSemaphoreCreateRecursiveMutex();
return 0;
}
static inline int ebus_env_mutex_lock(ebus_env_mutex_handle_t *handle, int timeout_ms)
{
int ret;
if (EBUS_ENV_MAX_DELAY == timeout_ms)
{
ret = xSemaphoreTakeRecursive(handle->mutex, portMAX_DELAY);
}
else
{
ret = xSemaphoreTakeRecursive(handle->mutex, pdMS_TO_TICKS(timeout_ms));
}
return ret;
}
static inline int ebus_env_mutex_unlock(ebus_env_mutex_handle_t *handle)
{
int ret;
ret = xSemaphoreGiveRecursive(handle->mutex);
return ret;
}
static inline int ebus_env_mutex_destroy(ebus_env_mutex_handle_t *handle)
{
vSemaphoreDelete(handle->mutex);
return 0;
}
static inline int ebus_env_thread_delay_ms(uint32_t delay_ms)
{
vTaskDelay(pdMS_TO_TICKS(delay_ms));
return 0;
}
#elif CONFIG_EBUS_ENV_OS_POSIX
#define EBUS_ENV_MAX_DELAY -1
#define EBUS_LOG(fmt, ...) printf("[EBUS] " fmt "\n", ##__VA_ARGS__)
#define EBUS_ERR(fmt, ...) fprintf(stderr, "[EBUS ERROR] " fmt "\n", ##__VA_ARGS__)
typedef struct
{
pthread_mutex_t mutex;
int initialized;
} ebus_env_mutex_handle_t;
void *platform_malloc(uint32_t size)
{
return malloc(size);
}
void platform_free(void *ptr)
{
free(ptr);
}
static inline int ebus_env_mutex_create(ebus_env_mutex_handle_t *handle)
{
pthread_mutexattr_t attr;
int ret;
if (handle == NULL)
{
return -EINVAL;
}
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
ret = pthread_mutex_init(&handle->mutex, &attr);
pthread_mutexattr_destroy(&attr);
if (ret == 0)
{
handle->initialized = 1;
return 0;
}
return -ret;
}
static inline int ebus_env_mutex_lock(ebus_env_mutex_handle_t *handle, int timeout_ms)
{
int ret;
if (handle == NULL || !handle->initialized)
{
return -EINVAL;
}
if (timeout_ms == EBUS_ENV_MAX_DELAY)
{
// 无限等待
ret = pthread_mutex_lock(&handle->mutex);
return (ret == 0) ? 0 : -ret;
}
else
{
// 有超时的等待
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += timeout_ms / 1000;
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
// 处理纳秒溢出
if (ts.tv_nsec >= 1000000000)
{
ts.tv_sec += 1;
ts.tv_nsec -= 1000000000;
}
ret = pthread_mutex_timedlock(&handle->mutex, &ts);
return (ret == 0) ? 0 : -ret;
}
}
static inline int ebus_env_mutex_unlock(ebus_env_mutex_handle_t *handle)
{
int ret;
if (handle == NULL || !handle->initialized)
{
return -EINVAL;
}
ret = pthread_mutex_unlock(&handle->mutex);
return (ret == 0) ? 0 : -ret;
}
static inline int ebus_env_mutex_destroy(ebus_env_mutex_handle_t *handle)
{
int ret;
if (handle == NULL || !handle->initialized)
{
return -EINVAL;
}
ret = pthread_mutex_destroy(&handle->mutex);
if (ret == 0)
{
handle->initialized = 0;
}
return (ret == 0) ? 0 : -ret;
}
static inline int ebus_env_thread_delay_ms(uint32_t delay_ms)
{
struct timespec ts;
ts.tv_sec = delay_ms / 1000;
ts.tv_nsec = (delay_ms % 1000) * 1000000;
return nanosleep(&ts, NULL);
}
#endif
#ifdef __cplusplus
}
#endif
#endif

115
modules/ebus/readme.md Normal file
View File

@@ -0,0 +1,115 @@
# EBUS - 轻量级软件总线通讯框架
## 简介
EBUS是一个轻量级的软件总线通讯框架用于组件间的消息传递。它实现了发布-订阅模式,支持同步消息处理,为嵌入式系统提供了一种高效、灵活的组件间通信机制。
## 特性
- **轻量级设计**:针对资源受限的嵌入式系统优化
- **总线-通道-订阅者模型**:清晰的层次结构
- **同步消息处理**:简化并发控制
- **跨平台支持**支持FreeRTOS和POSIX环境
- **简单易用的API**:易于集成到现有项目中
## 架构
EBUS框架基于以下核心概念
- **总线(Bus)**:顶层通信实体,可以包含多个通道
- **通道(Channel)**:特定类型消息的传输通道
- **订阅者(Subscriber)**:接收并处理通道消息的实体
```
+-------------+
| 总线 |
+-------------+
|
+-------------+ +-------------+ +-------------+
| 通道1 | | 通道2 | | 通道3 |
+-------------+ +-------------+ +-------------+
| | |
+-------------+ +-------------+ +-------------+
| 订阅者1-1 | | 订阅者2-1 | | 订阅者3-1 |
+-------------+ +-------------+ +-------------+
| 订阅者1-2 | | 订阅者2-2 |
+-------------+ +-------------+
```
## 使用示例
### 完整示例
请参考 `examples/main.c` 文件中的完整示例程序,该示例展示了如何创建总线、通道,以及在进程间进行消息传递。
## 编译与配置
### 配置选项
`Kconfig` 文件中提供了以下配置选项:
```
config EBUS
bool "ebus support"
default n
help
Enable ebus support
choice
prompt "Select Operating System"
default EBUS_ENV_OS_FREERTOS
config EBUS_ENV_OS_POSIX
bool "POSIX"
config EBUS_ENV_OS_FREERTOS
bool "FreeRTOS"
endchoice
```
### 编译方法
#### 在扫描笔工程中编译
EBUS已集成到扫描笔工程中可以通过配置`CONFIG_EBUS=y`来启用。
#### 独立编译Linux环境
```bash
mkdir build && cd build
cmake ..
make
```
#### 编译示例程序
```bash
cd examples
mkdir build && cd build
cmake ..
make
./ebus_example
```
## 移植指南
EBUS框架设计为易于移植到不同操作系统。目前支持POSIX和FreeRTOS环境。
### 移植到新平台
1.`port/` 目录下创建新的平台适配文件
2. 实现 `platform.h` 中定义的接口
3. 修改 `CMakeLists.txt``Kconfig` 添加新平台支持
## 许可证
EBUS框架采用Apache 2.0许可证。
## 贡献
欢迎提交问题报告和改进建议。
## 联系方式
如有问题,请联系项目维护者。