嵌入式:简单的service框架

目录

1:service框架简介

1.1:service框架简流程

2:service框架代码

2.1:代码大概框架思路

2.2:代码

2.2.1:代码思路基本流程

2.2.2:创建服务任务

2.2.3:初始化

1:用到的结构体

 2:创建互斥量、消息

 3:消息链表初始化

 4:各个业务服务注册

 5:消息发送函数

 6:消息接收解析

3:demo

3.1:数据的读写   

3.2:函数的注册

4:结语


1:service框架简介

        在嵌入式中,我们获取一个资源、设置一个变量、更新数据的话最直接的方式就是直接调用一个函数。这样的写法也可以满足基本的功能,前提是这个系统是只要一个任务,只有一个while(1)如图1.1。

 图1.1

        当有两个或者两个以上的任务的时候,同时调用一个函数(设置一个状态值、操作同一个资源、操作指针等行为)的流程如图1.2。

图1.2 

        当两个任务同时操作一个资源的时候,就会有重入的问题,会导致想不到的异常问题(数据不对、指针异常等)。

        为了解决这个重入的问题,我们就引入了这个服务框架,就是新增一个任务做中专站,统一处理资源。

1.1:service框架简流程

        

 图1.1.1 

        多个任务同时调用一个函数,通过服务框架发给另一个任务统一处理,这样就有效的防止代码的重入问题,解决不可发生的异常情况。

2:service框架代码

2.1:代码大概框架思路

        (1):独立的一个任务:service_task;

        (2):消息的ID、数据、数据长度、消息服务的名称;

        (3):消息发送的互斥锁;

        (4):消息发送的函数(依赖rtos)xMessageBufferSendFromISR、xMessageBufferSend;

        (5):一个消息链表,保存每个业务的服务消息; 

2.2:代码

2.2.1:代码思路基本流程

2.2.2:创建服务任务

void service_task_init(void)
{
    // create service task
    xTaskCreate(service_task,
        SERVICE_TASK_NAME,
        SERVICE_TASK_STACK_SIZE,
        NULL,
        SERVICE_TASK_PRIORITY,
        &g_service_task);

    if (g_service_task == NULL) {
        log_e("service_task_init error\n");
    } else {
        log_i("service task init ok\n");
    }
}

2.2.3:初始化

1:用到的结构体
#define SERVICE_ARRAY_SIZE(x)   (sizeof(x) / sizeof(x[0]))

#define META_LIST_FOR_EACH_ENTRY(item, list, type, member)                  \
    for (item = META_LIST_ENTRY((list)->pst_next, type, member);            \
         &(item)->member != (list);                                         \
         item = META_LIST_ENTRY((item)->member.pst_next, type, member))

typedef struct {
    uint16_t  service_count;                            /** service count */
    meta_list service_list;
} service_manager_t;


typedef struct {
    uint16_t msg_id;                                /** message id */
    uint16_t msg_len;                               /** message length */
}service_msg_t;

typedef struct {
    uint16_t version;                                                       /** Service version*/
    uint8_t is_local;                                                       /** Service is local or not */ 
    char name[SERVICE_NAME_MAX_LEN];                                        /** Service name */
    uint32_t (*init)(void);                                                 /** Service initialization */
    uint32_t (*read)(uint8_t* data, uint32_t len);                          /** Service read */
    uint32_t (*write)(uint8_t* data, uint32_t len);                         /** Service write */
    void*    (*ioctl)(uint32_t cmd, void* data, uint32_t len);              /** Service ioctl */
    uint32_t (*deinit)(void);                                               /** Service deinitialization */
    uint32_t (*handler)(uint32_t event_id, uint8_t* data, uint32_t len);    /** Service event handler */
} service_t;
 2:创建互斥量、消息
    // create service message buffer
    g_service_msgbuf_handle = xMessageBufferCreate(SERVICE_MSG_BUF_SIZE);
    if (g_service_msgbuf_handle == NULL) {
        log_e("service_msg_buf error");
        return;    
    }
    // create message mutex
    g_service_msg_mutex = xSemaphoreCreateMutex();
    if (g_service_msg_mutex == NULL) {
        log_e("service_msg_mutex error");
        return;
    }

    memset(g_msg_recv_buf, 0, sizeof(g_msg_recv_buf));
 3:消息链表初始化
uint32_t services_list_init(void)
{
    memset(&g_service_manager, 0, sizeof(service_manager_t));
    /**
     * The Service Management Link table is initialized
    */
    meta_list_init(&g_service_manager.service_list);
    g_service_manager.service_count = 0;
    return 0;
}
 4:各个业务服务注册
static void service_add(service_t *service)
{
    if (service == NULL) {
        log_e("service is NULL");
        return;
    }

    service_node_t *pst_service_list = NULL;
    pst_service_list = (service_node_t *)SERVICE_MALLOC(sizeof(service_node_t));
    if (pst_service_list == NULL) {
        log_e("service_node_t malloc failed");
        return;
    }

    pst_service_list->service = service;
    meta_list_tail_insert(&g_service_manager.service_list, &pst_service_list->list);
}

uint32_t service_register(service_t *service)
{
    if (service == NULL) {
        log_e("service is NULL");
        return SERVICE_RET_PARAM_ERR;
    }

    if (service->init == NULL) {
        log_e("service init is NULL");
        return SERVICE_RET_PARAM_ERR;
    }

    service->init();

    service_add(service);

    return SERVICE_RET_OK;
}
    
/**
 * Service Link table initialization
*/
for (int i = 0; i < SERVICE_ARRAY_SIZE(g_local_service_tab); i++) {
	// register to service list
	uint32_t ret = service_register((service_t *)&g_local_service_tab[i]);
	if (ret != SERVICE_RET_OK) {
		log_e("service_register error(%d)", ret);
	}
	log_i("register service %s", g_local_service_tab[i].name);
}
 5:消息发送函数
uint32_t service_msg_send(uint32_t msg_id, void *msg, uint16_t len)
{
    if (len > SERVICE_RECV_BUF_SIZE) {
        return SERVICE_RET_PARAM_ERR;
    }

    if((msg == NULL && len != 0) ||(msg != NULL && len == 0)) {
        return SERVICE_RET_PARAM_ERR;
    }

    uint8_t *buf = (uint8_t *)SERVICE_MALLOC(len + sizeof(service_msg_t));    
    if (buf == NULL) {
        log_e("service_msg_send malloc error");
        return SERVICE_RET_MALLOC_ERR;
    }

    /** 
     * Service Message Structure 
    */
    service_msg_t *msg_send = (service_msg_t *)buf;
    msg_send->msg_id = msg_id;
    msg_send->msg_len = len;
    uint16_t pos = sizeof(service_msg_t);
    memcpy(&buf[pos], msg, len);

    const TickType_t x100ms = pdMS_TO_TICKS(100);

    // send message
    if (__get_IPSR()) {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        // in interrupt
        // send message directly
        size_t length = xMessageBufferSendFromISR(g_service_msgbuf_handle, (void *)buf, len + sizeof(service_msg_t), &xHigherPriorityTaskWoken);
        if (length == 0) {
            log_e("service_msg_send error");
            SERVICE_FREE(buf);
            return SERVICE_RET_SEND_ERR;
        }
    } else {
        // in task
        if (msg_id != SERVICE_MSG_WEAR_TIMER_SWITCH) {
            log_i("service_msg_send in task");
        }
        // mutex lock
        service_msg_mutex_lock();

        // send message
        size_t length = xMessageBufferSend(g_service_msgbuf_handle, (void *)buf, len + sizeof(service_msg_t), x100ms);

        // mutex unlock
        service_msg_mutex_unlock();

        if (length == 0) {
            log_e("service_msg_send error");
            SERVICE_FREE(buf);
            return SERVICE_RET_SEND_ERR;
        }
        // wait for send complete
        vTaskDelay(1);
    }
    // free buffer
    SERVICE_FREE(buf);
    return SERVICE_RET_OK;
}
 6:消息接收解析
uint32_t services_manager_process_handler(uint32_t msg_id, void *msg_body, uint32_t msg_body_len)
{
    service_node_t *pst_service_list = NULL;

    META_LIST_FOR_EACH_ENTRY(pst_service_list, &g_service_manager.service_list, service_node_t, list) {
        if (pst_service_list->service->handler != NULL) {
            //TODO:
            pst_service_list->service->handler(msg_id, msg_body, msg_body_len);
        }
    }
    return SERVICE_RET_OK;
}

while (1) {
	size_t length = xMessageBufferReceive(g_service_msgbuf_handle, (void *)&g_msg_recv_buf, sizeof(g_msg_recv_buf), portMAX_DELAY);

	if (length > 0) {
		// service message
		service_msg_t *msg = (service_msg_t *)g_msg_recv_buf;
		// service id
		uint16_t msg_id = msg->msg_id;
		uint16_t msg_len = msg->msg_len;
		uint16_t pos = sizeof(service_msg_t);
		uint8_t *msg_buf = (uint8_t *)&g_msg_recv_buf[pos];
		// service handle
		services_manager_process_handler((uint32_t)msg_id, msg_buf, (uint32_t)msg_len);
	}
}

        在TODO:的地方应该还要添加判断当前发送的服务消息是否跟当前的handler函数是一致的,否则就会每个服务都会收到这个发送的ID。

        这样做的好处还有一个就是 SERVICE_MESSAGE_ID都是可以独立,也可以重复。

3:demo


uint8_t worship_angle = 0;

static void worship_set_angle(uint8_t angle)
{
    worship_angle = angle;
}

void worship_serv_set_angle(uint8_t angle)
{
    service_msg_send(SERVICE_MSG_WORSHIP_XXX, (void *)(&angle), sizeof(uint8_t));
}

void worship_serv_set_angle(uint8_t angle)
{
    service_node_t *worship_service =  get_service_by_name(WORSHIP_SERVICE_NAME);

    service_ioctl(worship_service, WORSHIP_IOCTL_XXX, (void *)(&angle), sizeof(uint8_t));
}

bool worship_serv_get_angle(void)
{
    service_node_t *worship_service =  get_service_by_name(WORSHIP_SERVICE_NAME);

    return (bool)(*(uint8_t *)service_ioctl(worship_service, WORSHIP_IOCTL_GET_ANGLE, NULL, 0));
}

uint32_t worship_service_init(void)
{
    return 0;
}

uint32_t worship_service_deinit(void)
{
    return 0;
}

uint32_t worship_service_read(uint8_t *data, uint32_t len)
{
    return 0;
}

uint32_t worship_service_write(uint8_t *data, uint32_t len)
{
    return 0;
}

void* worship_service_ioctl(uint32_t cmd, void *data, uint32_t len)
{
    switch (cmd) {
    case WORSHIP_IOCTL_GET_ANGLE:
        return &worship_angle;
        break;
    case MUSIC_IOCTL_SET_LYRIC_STATE:{
            worship_set_angle((uint8_t)(*(uint8_t *)data));
        }
    default:
        break;
    }
    return NULL;
}

uint32_t worship_service_handler(uint32_t event, uint8_t *data, uint32_t len)
{
    switch (event) {
    case SERVICE_MSG_WORSHIP_XXX: {
        worship_set_angle((uint8_t)(*(uint8_t *)data));
    }
        break;
    default:
        break;
    }
    return 0;
}

3.1:数据的读写   

        我们需要传递一个变量、结构体数据等,可以参考:worship_serv_set_angle。有两种方式可自行选择,其中的差异自行体会。

        获取数据可以参考:worship_serv_get_angle

3.2:函数的注册

        我们需要注册6个函数:init、read、write、ioctl、deinit、handler;

typedef enum {
    WORSHIP_SERVICE_ID = 0,
    SERVICE_ID_MAX
} SERVICE_ID_TYPE_E;

#define WORSHIP_SERVICE_NAME        "worship"

const service_t g_local_service_tab[SERVICE_ID_MAX] = {
    [WORSHIP_SERVICE_ID] = {
        .version = 0,
        .is_local = SERVICE_TYPE_INSIDE,
        .name = WORSHIP_SERVICE_NAME,
        .init = worship_service_init,
        .read = worship_service_read,
        .write = worship_service_write,
        .ioctl = worship_service_ioctl,
        .deinit = worship_service_deinit,
        .handler = worship_service_handler,
    },
}

        这样就会在我们上面“各个业务服务注册”那里进行注册;

4:结语

        上诉是一个最简单的嵌入式的服务框架了,基本能满足需求,如有其他的建议或者需求可以沟通一起学习。

        其中这个框架其实还可以加个一个发送自定义延迟发送的功能,这个功能还没有时间进行编写,但是感觉这个延迟功能还是不错;

        如果想要对这个框架进行升华的话可以参考安卓的H andler + MessageQueue + Looper,这样的。之前我导师写过,但是没有全部学过来。后续可以研究一下

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一些Spring Boot框架的常见面试问题和答案: 1. 什么是Spring Boot框架? Spring Boot框架是一个基于Spring框架的快速开发应用程序的框架。它提供了一种快速且简化的方式来构建Spring应用程序,同时还提供了一些额外的功能,比如自动配置和嵌入式Web服务器。 2. Spring Boot框架的优点是什么? Spring Boot框架有以下几个优点: - 快速开发:Spring Boot框架提供了很多自动配置的选项,使得开发人员可以快速地创建应用程序。 - 简化配置:Spring Boot框架提供了一种简化配置的方式,可以减少开发人员的工作量。 - 嵌入式Web服务器:Spring Boot框架内置了Tomcat、Jetty和Undertow等嵌入式Web服务器,可以方便地创建Web应用程序。 - 自动配置:Spring Boot框架提供了很多自动配置选项,可以自动配置数据库连接、日志、安全等方面的内容。 - 外部化配置:Spring Boot框架支持外部化配置,可以将应用程序的配置信息存储在外部配置文件中,方便管理和修改。 3. Spring Boot框架如何实现自动配置? Spring Boot框架使用条件化配置来实现自动配置。条件化配置是指,根据应用程序的环境和配置信息,选择性地加载和配置一些组件。Spring Boot框架提供了很多的条件化注解,比如@ConditionalOnBean、@ConditionalOnClass、@ConditionalOnProperty等,可以根据条件来控制组件的加载和配置。 4. Spring Boot框架的启动流程是什么? Spring Boot框架的启动流程主要包括以下几个步骤: - 加载启动类:Spring Boot框架会加载启动类,启动类通常包含了@SpringBootApplication注解。 - 扫描组件:Spring Boot框架会扫描应用程序中所有的组件,包括@Controller、@Service、@Repository等注解的组件。 - 自动配置:Spring Boot框架会根据应用程序的配置信息和环境,自动配置一些组件。 - 启动嵌入式Web服务器:如果应用程序是Web应用程序,Spring Boot框架会启动嵌入式Web服务器,比如Tomcat、Jetty或者Undertow。 - 运行应用程序:最后,Spring Boot框架会运行应用程序,处理请求和响应。 5. Spring Boot框架中的@Component、@Service和@Repository有什么区别? @Component、@Service和@Repository是Spring框架中常用的注解,用于标识组件。在Spring Boot框架中,它们的区别如下: - @Component注解用于标识一个普通的组件。 - @Service注解用于标识一个服务组件,通常用于业务逻辑层。 - @Repository注解用于标识一个数据访问组件,通常用于数据访问层。 这些注解的主要作用是告诉Spring框架需要将这些组件加载到应用程序上下文中,可以通过@Autowired或@Inject注入到其他组件中使用。 6. Spring Boot框架中如何处理异常? Spring Boot框架提供了很多处理异常的方式。其中最常见的方式是使用@ControllerAdvice注解,这个注解可以标识一个类,用于处理全局的异常。在这个类中,可以通过@ExceptionHandler注解定义具体的异常处理方法。 另外,Spring Boot框架还提供了很多内置的异常处理器,比如DefaultErrorAttributes、BasicErrorController等,可以用于处理不同类型的异常。 7. Spring Boot框架中的Actuator是什么? Actuator是Spring Boot框架中的一个功能模块,提供了一些监控和管理应用程序的功能。比如,它可以展示应用程序的健康状态、运行状态、配置信息等,还可以查看和管理应用程序的线程、内存、日志等信息。 Actuator提供了一些内置的端点(Endpoint),可以通过HTTP请求访问这些端点来获取应用程序的状态和信息。比如,/health端点可以获取应用程序的健康状态,/info端点可以获取应用程序的配置信息。 8. Spring Boot框架中如何处理跨域请求? Spring Boot框架提供了很多处理跨域请求的方法。其中最常用的方法是使用@CrossOrigin注解,这个注解可以标识一个Controller或者方法,用于允许跨域请求。 另外,Spring Boot框架还可以通过配置CorsFilter来处理跨域请求。比如,可以在WebSecurityConfigurerAdapter中添加cors()方法,配置CorsFilter。 9. Spring Boot框架中如何使用定时任务? Spring Boot框架提供了很多定时任务的方式。其中最常见的方式是使用@Scheduled注解,这个注解可以标识一个方法,用于定时执行某些任务。在这个方法中,可以使用Cron表达式来定义定时任务的执行时间。 另外,Spring Boot框架还可以通过实现SchedulingConfigurer接口来自定义定时任务的执行器、线程池等配置。 10. Spring Boot框架中如何使用缓存? Spring Boot框架提供了很多缓存的方式。其中最常见的方式是使用@Cacheable、@CachePut和@CacheEvict注解,这些注解可以标识一个方法,用于缓存某些数据。 在使用这些注解之前,需要先配置一个缓存管理器,比如使用@EnableCaching注解开启缓存功能,并配置一个CacheManager。在方法中使用这些注解后,Spring框架会自动将方法的返回值缓存起来,下次再调用该方法时,会直接从缓存中获取数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值