车联网Tbox电源模式管理

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。

电源模式状态转换图

车联网Tbox电源模式管理_第1张图片
事件清单
  • 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流程

车联网Tbox电源模式管理_第2张图片

代码实现

#include 
#include 
 
#include 
#include 
 
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
    {
        // InitStates
        struct Init:msmf::state<> {};
        
        //Choices
        struct Choice_:msmf::state<>{};
        
        //Working
        struct Working:msmf::state<> 
        {
            // Entry action
            template 
            void on_entry(Event const&, Fsm&) 
            {
                std::cout << "Working::on_entry()" << std::endl;
            }
            // Exit actions
            template 
            void on_exit(Event const&, Fsm&) 
            {
                std::cout << "Working::on_exit()" << std::endl;
            }
        };       

        //standby
        struct Standby:msmf::state<> 
        {
            // Entry action
            template 
            void on_entry(Event const&, Fsm&) 
            {
                std::cout << "Standby::on_entry()" << std::endl;
            }
            // Exit actions
            template 
            void on_exit(Event const&, Fsm&) 
            {
                std::cout << "Standby::on_exit()" << std::endl;
            }
        };       

        //Sleeping
        struct Sleeping:msmf::state<> 
        {
            // Entry action
            template 
            void on_entry(Event const&, Fsm&) 
            {
                std::cout << "Sleeping::on_entry()" << std::endl;
            }
            // Exit actions
            template 
            void on_exit(Event const&, Fsm&) 
            {
                std::cout << "Sleeping::on_exit()" << std::endl;
            }
        };       

        struct SleepPoll_:msmf::state_machine_def
        {

            // Entry action
            template 
            void on_entry(Event const&, Fsm&) 
            {
                BOOST_STATIC_ASSERT((boost::is_convertible::value));
                std::cout << "SleepPoll::on_entry()" << std::endl;
            }
            // Exit actions
            template 
            void on_exit(Event const&, Fsm&) 
            {
                BOOST_STATIC_ASSERT((boost::is_convertible::value));
                std::cout << "SleepPoll::on_exit()" << std::endl;
            }

            //SubSleep
            struct SubSleep:msmf::state<> 
            {
                // Entry action
                template 
                void on_entry(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible::value));
                    std::cout << "SubSleep::on_entry()" << std::endl;
                }
                // Exit actions
                template 
                void on_exit(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible::value));
                    std::cout << "SubSleep::on_exit()" << std::endl;
                }
            };

            //Sleeping
            struct SubWork:msmf::state<> 
            {
                // Entry action
                template 
                void on_entry(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible::value));
                    std::cout << "SubWork::on_entry()" << std::endl;
                }
                // Exit actions
                template 
                void on_exit(Event const&, Fsm&) 
                {
                    BOOST_STATIC_ASSERT((boost::is_convertible::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 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;

        struct GuardCondition
        {
            template 
            bool operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
            {
                if(1 == f.condition)    return true;
                return false;
            }
        };
        // Actions
        struct ActionAssign 
        {
            template 
            void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
            {
                f.condition = 0;
                std::cout << "ActionAssign()" << std::endl;
            }
        };

        struct  ActionInitToWorking
        {
            template 
            void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const
            {
                std::cout << "ActionInitToWorking() condition = " << f.condition << std::endl;
            }
        };
        
        struct ActionInitToSleepPoll
        {

            template 
            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, msmf::none, msmf::none>,
            msmf::Row< SleepPoll::exit_pt, EventSubWrkToSleep, Sleeping, msmf::none, msmf::none>,
            msmf::Row< SleepPoll::exit_pt, EventSMS, Working, msmf::none, msmf::none>,
            msmf::Row< SleepPoll::exit_pt, EventCall, Working, msmf::none, msmf::none>,
            msmf::Row< SleepPoll,   EventMCU, Working, msmf::none,    msmf::none >

        > {};

        //template 
        ~TemSm_()
        {
            std::cout << "hello" << std::endl;
        }

        private:
            int condition;
    };

    // Pick a back-end
    typedef msm::back::state_machine 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电源模式管理)