【EventOS Nano】EventOS Nano框架分析

1.核心数据结构

时间触发型事件结构体

// eos定时器事件
typedef struct eos_event_timer {
    eos_u32_t topic                         : 13;    // 事件主题
    eos_u32_t oneshoot                      : 1;     // 是否单次触发
    eos_u32_t unit                          : 2;     // 时间单位,共支持4种时间单位
    eos_u32_t period                        : 16;    // 触发周期时间值
    eos_u32_t timeout_ms;                            // 时间触发型事件超时时间(当前系统时间+触发周期时间)
} eos_event_timer_t;
// 可以理解为角色?
typedef struct eos_actor {
    eos_u32_t priority              : 5;   // 优先级
    eos_u32_t mode                  : 1;   // 模式
    eos_u32_t enabled               : 1;   // 使能位
    eos_u32_t reserve               : 1;
} eos_actor_t;

为了将占用内存控制到最小,将eos_tag定义为全局变量

typedef struct eos_tag
{
    eos_u32_t magic;
    eos_mcu_t *sub_table;                 // 事件订阅表

    // eos的actor参量
    eos_mcu_t actor_exist;                // actor存在标志位
    eos_mcu_t actor_enabled;              // acotr使能标志位
    eos_actor_t *actor[EOS_MAX_ACTORS];   // actor表

    // eos 的堆空间变量
    eos_heap_t heap;

    // eos时间触发型事件
    eos_event_timer_t etimer[EOS_MAX_TIME_EVENT];   // 时间触发型事件表
    eos_u32_t time;                                 // 系统时基(时间)     
    eos_u32_t timeout_min;                          // 超时事件最短的定时器
    eos_u8_t  timer_count;                          // 定时器事件数量

    // eos框架运行标志
    eos_u8_t enabled    :1;    // 是否使能
    eos_u8_t running    :1;    // 运行状态
    eos_u8_t init_end   :1;
}eos_t;

// 事件全局变量
static eos_t eos;

2.系统初始化

在Stm32F3的MCU平台上,eos的时基,是由Systick定时器中断提供的(stm32f10x_it.c),在刚开始移植到到Linux平台下做仿真调试的时候,发现eos_run()后,eos上并没有运行,发现是因为移植到Linux环境后就没有Systick硬定时器,所以创建了一个软件定时器,在软件定时器中执行eos_tick()。

// timer-----------------------------------------
//定时器触发函数
void SysTickHandler(int signo){
    eos_tick();  // 时钟中断一次,执行一次,eos.time+1
}

void init_sigaction(){
    struct sigaction act;
    act.sa_handler = SysTickHandler;
    act.sa_flags = 0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGPROF,&act,NULL); //设置信号 SIGPROF 的处理函数为 SysTickHandler
}

void init_time() {
    struct itimerval value;
    value.it_value.tv_sec=1;      //定时器启动后,每隔1秒将执行相应的函数
    value.it_value.tv_usec=0;
    value.it_interval=value.it_value;
    setitimer(ITIMER_PROF,&value,NULL);     //初始化 timer,到期发送 SIGPROF 信号
}

* main function ------------------------------------------------------------ */
int main(void)
{
    init_sigaction();
    init_time();

    eos_init();                                     // EventOS初始化
#if (EOS_USE_PUB_SUB != 0)
    eos_sub_init(eos_sub_table, Event_Max);         // 订阅表初始化
#endif

#if (EOS_USE_SM_MODE != 0)
    eos_sm_led_init();                              // LED状态机初始化
#endif
    eos_reactor_led_init();

    eos_run();                                      // EventOS启动

    return 0;
}

eos框架初始化


void eos_init(void)
{
    eos_clear();                // 将timer_count清为0

    eos.enabled = EOS_True;     // eos框架使能
    eos.running = EOS_False;    // eos没有正在运行
    eos.actor_exist = 0;        // 存在的状态机
    eos.actor_enabled = 0;      // 状态机不使能

    eos.sub_table = EOS_NULL;   // 订阅表为空

    eos_heap_init(&eos.heap);
    eos.init_end = 1;

    eos.time = 0;               // 时基初始化
}

// 初始化发布-订阅表
void eos_sub_init(eos_mcu_t *flag_sub, eos_topic_t topic_max)
{
    eos.sub_table = flag_sub;
    for (int i = 0; i < topic_max; i ++) {
        eos.sub_table[i] = 0;
    }
}

3.事件发布

eos中有两种事件类型,分别为时间触发型事件和携带数据型事件,时间触发型事件又分为周期事件和非周期事件。

3.1 时间触发型事件发布

发布周期触发型事件

void eos_event_pub_period(eos_topic_t topic, eos_u32_t time_ms_period)
{
    // topic:事件主题   time_ms_period:触发周期  EOS_False:是否只触发一次
    eos_event_pub_time(topic, time_ms_period, EOS_False);
}

发布延时时间事件(非周期时间型事件)

void eos_event_pub_delay(eos_topic_t topic, eos_u32_t time_ms)
{
    // topic:事件主题  time_ms:延时触发时间  EOS_True:只触发一次
    eos_event_pub_time(topic, time_ms, EOS_True);
}

两种时间型事件都是调用的eos_event_pub_time函数,原理是计算发布的时间型事件etimer(eos_event_timer_t类型)各项成员值,赋给eos.etimer[i],同时时间型事件数自增(eos.timer_count++):

void eos_event_pub_time(eos_topic_t topic, eos_u32_t time_ms, eos_bool_t oneshoot)
{
    EOS_ASSERT(time_ms != 0);
    EOS_ASSERT(time_ms <= timer_threshold[EosTimerUnit_Minute]);
    EOS_ASSERT(eos.timer_count < EOS_MAX_TIME_EVENT);

    // 检查重复,不允许重复发送。
    for (eos_u32_t i = 0; i < eos.timer_count; i ++) {
        EOS_ASSERT(topic != eos.etimer[i].topic);
    }

    eos_u32_t system_ms = eos.time;
    eos_u8_t unit = EosTimerUnit_Ms;
    eos_u16_t period;
    for (eos_u8_t i = 0; i < EosTimerUnit_Max; i ++) {
        if (time_ms > timer_threshold[i])
            continue;
        unit = i;
       
        if (i == EosTimerUnit_Ms) {
            period = time_ms;
            break;
        }
        period = (time_ms + (timer_unit[i] >> 1)) / timer_unit[i];
        break;
    }

    eos_u32_t timeout = (system_ms + time_ms);
    
         // 将计算好的值,赋给eos.etimer[eos.timer_count ++]
    eos.etimer[eos.timer_count ++] = (eos_event_timer_t) {
        topic, oneshoot, unit, period, timeout
    };
   
    // 更新系统当前最小的超时时间值
    if (eos.timeout_min > timeout) {
        eos.timeout_min = timeout;
    }
}

如果想取消时间型事件的发布可以调用:

void eos_event_time_cancel(eos_topic_t topic);

实现原理就是遍历eos.etimer[i],将传入topic对应的删除,并同步eos.timer_count -=1,重新计算所有事件计时器中最小的事件赋给eos.timeout_min。

3.2 携带数据型事件发布

只发布事件主题(topic)

void eos_event_pub_topic(eos_topic_t topic)
{
    // 入参只有topic
    eos_s8_t ret = eos_event_pub_ret(topic, EOS_NULL, 0);
    EOS_ASSERT(ret >= 0);
    (void)ret;
}

发布携带数据的事件(topic+*data)

void eos_event_pub(eos_topic_t topic, void *data, eos_u32_t size)
{
    eos_s8_t ret = eos_event_pub_ret(topic, data, size);
    EOS_ASSERT(ret >= 0);
    (void)ret;
}

两种方法调用的都是eos_event_pub_ret:

eos_s8_t eos_event_pub_ret(eos_topic_t topic, void *data, eos_u32_t size)
{
    if (eos.init_end == 0) {
        return (eos_s8_t)EosRunErr_NotInitEnd;
    }

#if (EOS_USE_PUB_SUB != 0)
    if (eos.sub_table == EOS_NULL) {
        return (eos_s8_t)EosRunErr_SubTableNull;
    }
#endif
    // 保证框架已经运行
    if (eos.enabled == 0) {
        return (eos_s8_t)EosRun_NotEnabled;
    }

    if (eos.actor_exist == 0) {
        return (eos_s8_t)EosRun_NoActor;
    }

    // 没有状态机使能,返回
    if (eos.actor_enabled == 0) {
        return (eos_s8_t)EosRun_NotEnabled;
    }
    // 没有输入topic事件的状态机订阅,返回
#if (EOS_USE_PUB_SUB != 0)
    if (eos.sub_table[topic] == 0) {    
        return (eos_s8_t)EosRun_NoActorSub;
    }
#endif


    eos_port_critical_enter();
    // 申请事件空间
    eos_event_inner_t *e = eos_heap_malloc(&eos.heap, (size + sizeof(eos_event_inner_t)));
    if (e == (eos_event_inner_t *)0) {
        eos_port_critical_exit();
        return (eos_s8_t)EosRunErr_MallocFail;
    }
    e->topic = topic;
#if (EOS_USE_PUB_SUB != 0)
    e->sub = eos.sub_table[e->topic];
#else
    e->sub = eos.actor_exist;
#endif
    eos.heap.sub_general |= e->sub;
    eos_u8_t *e_data = (eos_u8_t *)e + sizeof(eos_event_inner_t);
    for (eos_u32_t i = 0; i < size; i ++) {
        e_data[i] = ((eos_u8_t *)data)[i];
    }
    eos_port_critical_exit();


    return (eos_s8_t)EosRun_OK;
}

3.3 eos的运行和事件的处理

eos_run循环调用eos_once()

void eos_run(void)
{
    eos_hook_start();

    EOS_ASSERT(eos.enabled == EOS_True);
#if (EOS_USE_PUB_SUB != 0)
    EOS_ASSERT(eos.sub_table != 0);
#endif
#if (EOS_USE_EVENT_DATA != 0 && EOS_USE_HEAP != 0)
    EOS_ASSERT(eos.heap.size != 0);
#endif

    eos.running = EOS_True;
	
    // eos一直处于使能状态
    while (eos.enabled) {
	    // 实际一直调用eos_once()
        eos_s8_t ret = eos_once();
        EOS_ASSERT(ret >= 0);

        if (ret == EosRun_NotEnabled) {
            break;
        }

        if (ret == EosRun_NoActor || ret == EosRun_NoEvent) {
#if (EOS_USE_MAGIC != 0)
            EOS_ASSERT(eos.heap.magic == EOS_MAGIC_NUMBER);
            EOS_ASSERT(eos.magic == EOS_MAGIC_NUMBER);
            for (eos_u8_t i = 0; i < EOS_MAX_ACTORS; i ++) {
                if ((eos.actor_exist & (1 << i)) != 0) {
                    EOS_ASSERT(eos.actor[i]->magic == EOS_MAGIC_NUMBER);
                }
            }
#endif
            eos_hook_idle();
        }
    }
    // 如果EosRun_NotEnabled置位
    while (1) {
        eos_hook_idle();
    }
}

eos_once分别执行SM模式逻辑和reactor模式逻辑

eos_s8_t eos_once(void)
{
    if (eos.init_end == 0) {
        return (eos_s8_t)EosRunErr_NotInitEnd;
    }

#if (EOS_USE_PUB_SUB != 0)
    if (eos.sub_table == EOS_NULL) {
        return (eos_s8_t)EosRunErr_SubTableNull;
    }
#endif

    if (eos.enabled == EOS_False) {
        eos_clear();
        return (eos_s8_t)EosRun_NotEnabled;
    }

    // 检查是否有状态机的注册
    if (eos.actor_exist == 0 || eos.actor_enabled == 0) {
        return (eos_s8_t)EosRun_NoActor;
    }

#if (EOS_USE_TIME_EVENT != 0)
    /* 处理定时器事件,如果有某个定时器时间到达,则发布定时器事件的topic,并寻找
	 当前定时器数组中最近到达的超时时间,赋给eos.timeout_min*/
    eos_evttimer();
#endif

    if (eos.heap.empty == EOS_True) {
        return (eos_s8_t)EosRun_NoEvent;
    }

    // 寻找到优先级最高,且有事件需要处理的 Actor
    eos_actor_t *actor = eos.actor[0];
    eos_u8_t priority = EOS_MAX_ACTORS;
    for (eos_s8_t i = (eos_s8_t)(EOS_MAX_ACTORS - 1); i >= 0; i --) {
        // 检测是否有事件需要处理
        if ((eos.actor_exist & (1 << i)) == 0)
            continue;
        if ((eos.heap.sub_general & (1 << i)) == 0)
            continue;
        actor = eos.actor[i];
        priority = i;
        break;
    }
    // 如果没有找到,返回
    if (priority == EOS_MAX_ACTORS) {
        return (eos_s8_t)EosRun_NoActorSub;
    }

    // 寻找当前Actor的最老的事件
    eos_port_critical_enter();
    eos_event_inner_t * e = eos_heap_get_block(&eos.heap, priority);
    EOS_ASSERT(e != EOS_NULL);

    eos_port_critical_exit();
    eos_event_t event;
    event.topic = e->topic;
    event.data = (void *)((eos_pointer_t)e + sizeof(eos_event_inner_t));
    eos_block_t *block = (eos_block_t *)((eos_pointer_t)e - sizeof(eos_block_t));
    event.size = block->size - block->offset - sizeof(eos_event_inner_t);

    // 对事件进行执行
#if (EOS_USE_PUB_SUB != 0)
    if ((eos.sub_table[e->topic] & (1 << actor->priority)) != 0)
#endif
    {
#if (EOS_USE_SM_MODE != 0)
        if (actor->mode == EOS_Mode_StateMachine) {
            // SM模式的切换逻辑,执行状态的转换
            eos_sm_t *sm = (eos_sm_t *)actor;
            eos_sm_dispath(sm, &event);
        }
        else 
#endif
        {
            // reactor模式的处理逻辑,触发事件
            eos_reactor_t *reactor = (eos_reactor_t *)actor;
            reactor->event_handler(reactor, &event);
        }
    }
#if (EOS_USE_PUB_SUB != 0)
    else {
        return (eos_s8_t)EosRunErr_ActorNotSub;
    }
#endif
#if (EOS_USE_EVENT_DATA != 0)
    // 销毁过期事件与其携带的参数
    eos_port_critical_enter();
    eos_heap_gc(&eos.heap, e);
    eos_port_critical_exit();
#endif

    return (eos_s8_t)EosRun_OK;
}

3.4 eos退出

执行eos_stop,失能eos,并hook_stop执行退出,释放资源

void eos_stop(void)
{
    eos.enabled = EOS_False;
    eos_hook_stop();
}
posted @ 2022-11-23 22:02  Emma1111  阅读(517)  评论(0编辑  收藏  举报