美文网首页
车联网Tbox电源模式管理

车联网Tbox电源模式管理

作者: 守拙圆 | 来源:发表于2018-02-01 19:37 被阅读1198次

    Tbox电源模式

    当 Tbox 进行电源模式切换时应该通知 CSP。在车辆熄火(ignition off)后 Tbox 有不同的电源模式。在执行远程车辆信息服务时电源模式将对用户体验产生影响。

    Tbox 应该支持下面的电源模式:
    • Normal/working
      全功能,Tbox 与 CSP能够主要通过 mqtt 进行通信,如果 mqtt 通道无效,部分高优先级业务应该转到 SMS 通道。
    • Standby
      Tbox 处于低耗能状态,此状态支持 SMS、XCALL、MCU唤醒等。stanby 默认时长为熄火后 10 天。
    • sleep_poll
      在 Sleep 模式下,没有功能。定义一个轮询计划用来周期性唤醒tbox,并检查云端是否有待执行服务请求,与此同时,Tbox 将上报下一次唤醒时间以及一些车辆基本状态信息到 CSP。
      对于处于working模式的Tbox,CSP将仅仅发送MQTT消息。sleep_poll有两个阶段,阶段1持续 5 天,轮询频率周期为 2 hours;阶段2持续 10 天,轮询周期为 4 小时。
    • off
      无功能,无轮询,最小功耗。
    Tbox应该通知 CSP 如下信息:
    • 从 stanby / sleep_poll 到 working状态,当进入working状态后,应该通知到CSP。
    • 从 working 到 standby,当离开working 到 standby时,tbox应该通知 CSP 说明其将要进入 standby。
    • 从 stanby 到 Sleep_poll,当离开 standby进入 sleep_poll时,CSP应该被通知下一次被唤醒的时间。
    • 在 sleep_poll时(sleep--polling--sleep...),在polling阶段结束时(在确认 CSP没有待执行服务请求时),CSP应该被通知下一次唤醒的时间。
    • 当进入 off 状态时,CSP应该被通知不再有轮询计划。此状态将在轮询状态结束后进入,或 CarMode 模式改变。对于部分车型来说,当tbox上传 tbox状态(例如电量状态)到csp时,csp收到电量状态后,将检查是否电量过低,如果是,CSP将以告警的形式通知移动 APP。

    电源模式状态转换图

    事件清单
    • working->standby
      No Telematics Business in past 2 minutes
      && 接收到MCU请求modem进入standby消息

    MCU 根据 CAN_bus Sleep && KL 15 Off && USB off 等满足休眠条件后通知 modem 进入 standby。

    • standby->work
      Incoming SMS :modem唤醒后通知二次开发唤醒原因;
      || Incoming Call :modem唤醒后通知二次开发唤醒原因;
      || RTC Timer expired :提供设置/取消RTC接口,RTC唤醒通知唤醒原因;(modem RTC)
      || 或wakeup_in 有上延, mcu消息通知modem进入 working状态

    wakeup_in 包含了以下具体事件:
    || BTN pressed :XCALL案件,MCU唤醒;
    || Movement :MCU唤醒;
    || WAN Antenna removal :暂时不做;
    || KL30 removal :MCU处理
    || CAN_bus Normal :MCU硬件唤醒modem
    || KL15 On :MCU硬件唤醒modem
    || USB On (一体机是通过mcu控制hu电源模式,故hu开机时,mcu一定处于working状态,故此时也会唤醒模块,从而使 usb on)

    • stanby->sleep polling
      RTC Timer expired:standby的RTC到期

    • sleep-> working
      MCU拉高模块Power_key引脚,mcu通知模块进入working状态

    • sleep_poll ->sleep
      轮询结束。

    • sleep_poll->working
      此状态转换分为两种情况:

      1. subsleep -> working
        mcu拉高power_key引脚,mcu消息通知切换到working状态
      2. subworking -> working
        mcu 消息通知切换到working状态(此场景是modem处于sleep_polling下的subworking时(通常此时mcu应该处于休眠态),但如果mcu被车辆事件唤醒,虽然modem已经处于working状态,但仍然需要通知modem进入working状态的消息)
        || incoming SMS
        || incoming xcall
        || csp有待执行请求

    mcu启动modem流程

    代码实现

    #include <iostream>
    #include <boost/msm/back/state_machine.hpp>
     
    #include <boost/msm/front/state_machine_def.hpp>
    #include <boost/msm/front/functor_row.hpp>
     
    namespace
    {
        namespace msm = boost::msm;
        namespace msmf = boost::msm::front;
        namespace mpl = boost::mpl;
     
        // ----- Events
        struct EventMCU {};
        struct EventSMS {};
        struct EventCall{};
    
        struct EventWrkToStby{};
        struct EventStbyToSubSleep {};
        struct EventSubWrkToSubSleep{};
        struct EventSubWrkToSleep{};
    
             
        // ----- State machine
        struct TemSm_:msmf::state_machine_def<TemSm_>
        {
            // InitStates
            struct Init:msmf::state<> {};
            
            //Choices
            struct Choice_:msmf::state<>{};
            
            //Working
            struct Working:msmf::state<> 
            {
                // Entry action
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) 
                {
                    std::cout << "Working::on_entry()" << std::endl;
                }
                // Exit actions
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) 
                {
                    std::cout << "Working::on_exit()" << std::endl;
                }
            };       
    
            //standby
            struct Standby:msmf::state<> 
            {
                // Entry action
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) 
                {
                    std::cout << "Standby::on_entry()" << std::endl;
                }
                // Exit actions
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) 
                {
                    std::cout << "Standby::on_exit()" << std::endl;
                }
            };       
    
            //Sleeping
            struct Sleeping:msmf::state<> 
            {
                // Entry action
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) 
                {
                    std::cout << "Sleeping::on_entry()" << std::endl;
                }
                // Exit actions
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) 
                {
                    std::cout << "Sleeping::on_exit()" << std::endl;
                }
            };       
    
            struct SleepPoll_:msmf::state_machine_def<SleepPoll_>
            {
    
                // Entry action
                template <class Event,class Fsm>
                void on_entry(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, TemSm_>::value));
                    std::cout << "SleepPoll::on_entry()" << std::endl;
                }
                // Exit actions
                template <class Event,class Fsm>
                void on_exit(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, TemSm_>::value));
                    std::cout << "SleepPoll::on_exit()" << std::endl;
                }
    
                //SubSleep
                struct SubSleep:msmf::state<> 
                {
                    // Entry action
                    template <class Event,class Fsm>
                    void on_entry(Event const&, Fsm&) 
                    {
                        BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, SleepPoll_>::value));
                        std::cout << "SubSleep::on_entry()" << std::endl;
                    }
                    // Exit actions
                    template <class Event,class Fsm>
                    void on_exit(Event const&, Fsm&) 
                    {
                        BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, SleepPoll_>::value));
                        std::cout << "SubSleep::on_exit()" << std::endl;
                    }
                };
    
                //Sleeping
                struct SubWork:msmf::state<> 
                {
                    // Entry action
                    template <class Event,class Fsm>
                    void on_entry(Event const&, Fsm&) 
                    {
                        BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, SleepPoll_>::value));
                        std::cout << "SubWork::on_entry()" << std::endl;
                    }
                    // Exit actions
                    template <class Event,class Fsm>
                    void on_exit(Event const&, Fsm&) 
                    {
                        BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, SleepPoll_>::value));
                        std::cout << "SubWork::on_exit()" << std::endl;
                    }
                };       
    
                struct Entry:msmf::entry_pseudo_state<> {}; 
                struct ExitRTC:msmf::exit_pseudo_state< EventSubWrkToSleep > {};
                struct ExitSMS:msmf::exit_pseudo_state< EventSMS > {};
                struct ExitCall:msmf::exit_pseudo_state< EventCall > {};
    
                typedef mpl::vector<SubWork> initial_state;
    
                struct transition_table:mpl::vector<
                                        msmf::Row< Entry, boost::any, SubSleep, msmf::none, msmf::none >,
                                        msmf::Row< SubWork, EventSubWrkToSubSleep, SubSleep, msmf::none, msmf::none >,
                                        msmf::Row< SubWork, EventSubWrkToSleep, ExitRTC, msmf::none, msmf::none >,
                                        msmf::Row< SubWork, EventCall, ExitCall, msmf::none, msmf::none >,
                                        msmf::Row< SubWork, EventSMS, ExitSMS, msmf::none, msmf::none >
                                        >{};
                
            };
            // Set initial SourceState
            typedef Init initial_state;                                                     
    
            typedef msm::back::state_machine<SleepPoll_> SleepPoll;
    
            struct GuardCondition
            {
                template <class Event, class Fsm, class SourceState, class TargetState>
                bool operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
                {
                    if(1 == f.condition)    return true;
                    return false;
                }
            };
            // Actions
            struct ActionAssign 
            {
                template <class Event, class Fsm, class SourceState, class TargetState>
                void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
                {
                    f.condition = 0;
                    std::cout << "ActionAssign()" << std::endl;
                }
            };
    
            struct  ActionInitToWorking
            {
                template <class Event, class Fsm, class SourceState, class TargetState>
                void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
                {
                    std::cout << "ActionInitToWorking() condition = " << f.condition << std::endl;
                }
            };
            
            struct ActionInitToSleepPoll
            {
    
                template <class Event, class Fsm, class SourceState, class TargetState>
                void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
                {
                    std::cout << "ActionInitToSleepPoll() condition = " << f.condition << std::endl;
                }
            };
            // Transition transition_table
            struct transition_table:mpl::vector<
                          //Start       Event       Next        Action         Guard
                msmf::Row < Init,    msmf::none, Working,    ActionInitToWorking,    msmf::none >,
                msmf::Row< Working,     EventWrkToStby, Standby, msmf::none,    msmf::none >,
                msmf::Row< Standby,    EventStbyToSubSleep, SleepPoll::entry_pt<SleepPoll_::Entry>, msmf::none, msmf::none>,
                msmf::Row< SleepPoll::exit_pt<SleepPoll_::ExitRTC>, EventSubWrkToSleep, Sleeping, msmf::none, msmf::none>,
                msmf::Row< SleepPoll::exit_pt<SleepPoll_::ExitSMS>, EventSMS, Working, msmf::none, msmf::none>,
                msmf::Row< SleepPoll::exit_pt<SleepPoll_::ExitCall>, EventCall, Working, msmf::none, msmf::none>,
                msmf::Row< SleepPoll,   EventMCU, Working, msmf::none,    msmf::none >
    
            > {};
    
            //template <class Event,class Fsm>
            ~TemSm_()
            {
                std::cout << "hello" << std::endl;
            }
    
            private:
                int condition;
        };
    
        // Pick a back-end
        typedef msm::back::state_machine<TemSm_> TemSm;
        
        void test() 
        {        
            TemSm sm1;
            sm1.start(); 
            std::cout << "send EventWrkToStby" << std::endl;
            sm1.process_event(EventWrkToStby());
            std::cout << "send EventStbyToSubSleep" << std::endl;
            sm1.process_event(EventStbyToSubSleep());
            //std::cout << "send EventSubWrkToSleep" << std::endl;
           // sm1.process_event(EventSubWrkToSleep());
            //std::cout << "send EventCall" << std::endl;
           // sm1.process_event(EventCall());
            std::cout << "send EventMCU" << std::endl;
            sm1.process_event(EventMCU());
        }
    
    }
    
    int main()
    {
    
        test();
    
        return 0;
    }
    
    

    几个注意事项

    由于tbox状态机就运行在tbox上,故相关tbox异常场景需要考虑。

    1. 在tbox的电源模式中 sleep 模式中,各个进程都停止运行,从sleep模式中恢复时需要重启进程,要使状态机在sleep后能够延续,需要将状态机持久化。
     //保存状态机到本地
    #define POWERMODE_PATH "/oemimage/oemdata/PowerMode.fsm"
    std::ofstream ofs(POWERMODE_PATH);
    boost::archive::text_oarchive oa(ofs);
    oa << sm;
    
    //从本地文件中恢复状态机
    std::ifstream ifs(POWERMODE_PATH);
    boost::archive::text_iarchive ia(ifs);
    ia >> sm;
    

    2.为了区分进程异常退出还是正常退出:

    • 在类的析构函数来记录持久化标志,此方法不可行,在系统正常重启和异常重启都不会执行析构函数。
    • 进程退出回掉函数 atexit中来记录持久化标识,结果此方法也不可行,在系统正常重启和异常关机都不会执行此回掉函数。
    1. 场景考虑:
    • 场景1:若不进行持久化的话,由于sleep和subsleep状态实际上就是定时关机与关机的区别,故无法恢复到对应的subsleep状态。
      方案: 状态机持久化

    • 场景2:若在整个状态机变化的过程中都持久化状态机,当状态机处于standby时(此时状态机已经完成持久化),若在stanby时正常断电,异常断电关机,则此时开启系统将到standby状态,而实际处于work状态,故造成了状态不一致。
      方案:此异常场景的解决方案是:不区分异常与正常断电,重启后都从初始working状态开始。则要求不对状态机进行持久化。

    • 场景3:若在进入sleep与subsleep都进行持久化,若正常关机,则启动后恢复状态机,需要区分当前状态处于sleep还是subsleep,若处于subsleep则切换到subwork,若处于sleep,则切换到working。若是在subsleep期间异常关机,则异常重启后首先还是会进入subworking,此时则不对。
      方案:此时虽然错误处于subworking状态,但在此时只要mcu通知modem切换到working状态,都会使状态机切换到working,从而恢复正常。

    综上所述:此处处理的最佳方式是,状态机进入subsleep状态时,持久化状态机,状态机恢复完成后删除此持久化文件;则正常关机后,重新启动,状态为subsleep状态主动切换到subworking。若在sleep状态,无论正常关机还是异常关机,重新启动后都处于working状态。

    相关文章

      网友评论

          本文标题:车联网Tbox电源模式管理

          本文链接:https://www.haomeiwen.com/subject/khfjzxtx.html