嵌入式:简单的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
    评论
Apache Dubbo 是一款高性能、轻量级的开源 Java 服务框架。 功能特点: Apache Dubbo提供了六大核心能力:面向接口代理的高性能RPC调用,智能容错和负载均衡,服务自动注册和发现,高度可扩展能力,运行期流量调度,可视化的服务治理与运维。 1、面向接口代理的高性能RPC调用 提供高性能的基于代理的远程调用能力,服务以接口为粒度,为开发者屏蔽远程调用底层细节。 2、智能负载均衡 内置多种负载均衡策略,智能感知下游节点健康状况,显著减少调用延迟,提高系统吞吐量。 3、服务自动注册与发现 支持多种注册中心服务,服务实例上下线实时感知。 4、高度可扩展能力 遵循微内核+插件的设计原则,所有核心能力如Protocol、Transport、Serialization被设计为扩展点,平等对待内置实现和第三方实现。 5、运行期流量调度 内置条件、脚本等路由策略,通过配置不同的路由规则,轻松实现灰度发布,同机房优先等功能。 6、可视化的服务治理与运维 提供丰富服务治理、运维工具:随时查询服务元数据、服务健康状态及调用统计,实时下发路由策略、调整配置参数。 Apache Dubbo 更新日志: v2.6.10.1 注意 1. 脚本路由器 默认情况下不会激活 ScriptRouter。如果用户还想使用它,请在selfs spi文件中添加scriptRouterFactory。 看dubbo-cluster/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.rpc.cluster.RouterFactory 2. 基因调用 默认情况下不会激活本机 Java 反序列化。如果用户仍然想使用它,请设置dubbo.security.serialize.generic.native-java-enable为true环境。 3. 序列化块列表 中引入了嵌入式序列化块列表dubbo-common/src/main/resources/security/serialize.blockedlist。 如果用户想添加允许列表,请参考dubbo.security.serialize.allowedClassList。 4. 序列号检查 可以启用一个可选的检查器来检查消费者是否发送了允许的序列化 ID,消费者是否收到了与发送给提供者相同的序列化 ID。您可以设置serialization.security.check为true启用此功能。 更改列表 疏散不必要的例子 初始化、创建 资源成本 修复 TPS 限制器在动态配置下不起作用 解决懒惰模式下共享连接不共享的问题 在 FailoverClusterInvoker 中修复 methodName 和重试 修复 Dubbo qos 命令对离线提供程序不起作用 为netty4客户端添加socks5代理支持 支持高版本Nacos 添加一些序列化检查 修复 MonitorService 缺少 side=consumer 参数问题 修复 netty3 积压 修复反序列化漏洞
嵌入式ROS开发工程师面试试题包括以下几点: 1. 请简要介绍一下嵌入式系统和ROS。 嵌入式系统是一种嵌入在其他设备中,用于控制和执行特定功能的计算机系统。ROS(机器人操作系统)是一个开源的机器人软件框架,用于编写机器人应用程序。它提供了一系列工具、库和软件包,方便开发人员在嵌入式系统上构建机器人控制应用。 2. 请描述一下你在嵌入式系统开发方面的经验。 回答这个问题时,可以提到自己之前从事过嵌入式系统开发项目,包括硬件和软件方面的经验,例如使用C/C++编程语言开发嵌入式系统,熟悉常用的嵌入式开发工具和环境,以及具备硬件接口和外设驱动的开发经验等。 3. 请简要介绍一下ROS中常用的通信机制。 ROS中常用的通信机制包括话题(Topic)、服务(Service)和动作(Action)。话题是一种发布者-订阅者模式,用于在ROS节点之间传递消息。服务是一种请求-响应模式,用于在ROS节点之间进行函数调用。动作是一种高级机制,用于支持异步、可重试的行为。 4. 请简要描述一下ROS节点和ROS话题。 ROS节点是ROS中运行的一个独立的进程,可以有多个节点同时运行。每个节点都可以发布消息到话题,也可以订阅其他节点发布的消息。话题是一种通过发布者-订阅者关系连接了多个节点的通信机制。 5. 请简要解释ROS的launch文件是什么以及它的作用是什么。 ROS的launch文件是一个XML格式的文件,用于启动和组织ROS节点。通过launch文件,可以同时启动多个节点,设置节点的参数和命名空间,以及定义节点之间的关系,简化了系统启动和配置的过程。 以上是对嵌入式ROS开发工程师面试试题的回答,总结了嵌入式系统、ROS通信机制、ROS节点和话题、以及launch文件的基本概念和作用。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值