车联网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。

电源模式状态转换图

事件清单
  • 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状态。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容