車聯(lián)網(wǎng)Tbox電源模式管理

Tbox電源模式

當(dāng) Tbox 進行電源模式切換時應(yīng)該通知 CSP。在車輛熄火(ignition off)后 Tbox 有不同的電源模式。在執(zhí)行遠(yuǎn)程車輛信息服務(wù)時電源模式將對用戶體驗產(chǎn)生影響。

Tbox 應(yīng)該支持下面的電源模式:
  • Normal/working
    全功能,Tbox 與 CSP能夠主要通過 mqtt 進行通信,如果 mqtt 通道無效,部分高優(yōu)先級業(yè)務(wù)應(yīng)該轉(zhuǎn)到 SMS 通道。
  • Standby
    Tbox 處于低耗能狀態(tài),此狀態(tài)支持 SMS、XCALL、MCU喚醒等。stanby 默認(rèn)時長為熄火后 10 天。
  • sleep_poll
    在 Sleep 模式下,沒有功能。定義一個輪詢計劃用來周期性喚醒tbox,并檢查云端是否有待執(zhí)行服務(wù)請求,與此同時,Tbox 將上報下一次喚醒時間以及一些車輛基本狀態(tài)信息到 CSP。
    對于處于working模式的Tbox,CSP將僅僅發(fā)送MQTT消息。sleep_poll有兩個階段,階段1持續(xù) 5 天,輪詢頻率周期為 2 hours;階段2持續(xù) 10 天,輪詢周期為 4 小時。
  • off
    無功能,無輪詢,最小功耗。
Tbox應(yīng)該通知 CSP 如下信息:
  • 從 stanby / sleep_poll 到 working狀態(tài),當(dāng)進入working狀態(tài)后,應(yīng)該通知到CSP。
  • 從 working 到 standby,當(dāng)離開working 到 standby時,tbox應(yīng)該通知 CSP 說明其將要進入 standby。
  • 從 stanby 到 Sleep_poll,當(dāng)離開 standby進入 sleep_poll時,CSP應(yīng)該被通知下一次被喚醒的時間。
  • 在 sleep_poll時(sleep--polling--sleep...),在polling階段結(jié)束時(在確認(rèn) CSP沒有待執(zhí)行服務(wù)請求時),CSP應(yīng)該被通知下一次喚醒的時間。
  • 當(dāng)進入 off 狀態(tài)時,CSP應(yīng)該被通知不再有輪詢計劃。此狀態(tài)將在輪詢狀態(tài)結(jié)束后進入,或 CarMode 模式改變。對于部分車型來說,當(dāng)tbox上傳 tbox狀態(tài)(例如電量狀態(tài))到csp時,csp收到電量狀態(tài)后,將檢查是否電量過低,如果是,CSP將以告警的形式通知移動 APP。

電源模式狀態(tài)轉(zhuǎn)換圖

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

MCU 根據(jù) CAN_bus Sleep && KL 15 Off && USB off 等滿足休眠條件后通知 modem 進入 standby。

  • standby->work
    Incoming SMS :modem喚醒后通知二次開發(fā)喚醒原因;
    || Incoming Call :modem喚醒后通知二次開發(fā)喚醒原因;
    || RTC Timer expired :提供設(shè)置/取消RTC接口,RTC喚醒通知喚醒原因;(modem RTC)
    || 或wakeup_in 有上延, mcu消息通知modem進入 working狀態(tài)

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狀態(tài),故此時也會喚醒模塊,從而使 usb on)

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

  • sleep-> working
    MCU拉高模塊Power_key引腳,mcu通知模塊進入working狀態(tài)

  • sleep_poll ->sleep
    輪詢結(jié)束。

  • sleep_poll->working
    此狀態(tài)轉(zhuǎn)換分為兩種情況:

    1. subsleep -> working
      mcu拉高power_key引腳,mcu消息通知切換到working狀態(tài)
    2. subworking -> working
      mcu 消息通知切換到working狀態(tài)(此場景是modem處于sleep_polling下的subworking時(通常此時mcu應(yīng)該處于休眠態(tài)),但如果mcu被車輛事件喚醒,雖然modem已經(jīng)處于working狀態(tài),但仍然需要通知modem進入working狀態(tài)的消息)
      || incoming SMS
      || incoming xcall
      || csp有待執(zhí)行請求

mcu啟動modem流程

代碼實現(xiàn)

#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狀態(tài)機就運行在tbox上,故相關(guān)tbox異常場景需要考慮。

  1. 在tbox的電源模式中 sleep 模式中,各個進程都停止運行,從sleep模式中恢復(fù)時需要重啟進程,要使?fàn)顟B(tài)機在sleep后能夠延續(xù),需要將狀態(tài)機持久化。
 //保存狀態(tài)機到本地
#define POWERMODE_PATH "/oemimage/oemdata/PowerMode.fsm"
std::ofstream ofs(POWERMODE_PATH);
boost::archive::text_oarchive oa(ofs);
oa << sm;

//從本地文件中恢復(fù)狀態(tài)機
std::ifstream ifs(POWERMODE_PATH);
boost::archive::text_iarchive ia(ifs);
ia >> sm;

2.為了區(qū)分進程異常退出還是正常退出:

  • 在類的析構(gòu)函數(shù)來記錄持久化標(biāo)志,此方法不可行,在系統(tǒng)正常重啟和異常重啟都不會執(zhí)行析構(gòu)函數(shù)。
  • 進程退出回掉函數(shù) atexit中來記錄持久化標(biāo)識,結(jié)果此方法也不可行,在系統(tǒng)正常重啟和異常關(guān)機都不會執(zhí)行此回掉函數(shù)。
  1. 場景考慮:
  • 場景1:若不進行持久化的話,由于sleep和subsleep狀態(tài)實際上就是定時關(guān)機與關(guān)機的區(qū)別,故無法恢復(fù)到對應(yīng)的subsleep狀態(tài)。
    方案: 狀態(tài)機持久化

  • 場景2:若在整個狀態(tài)機變化的過程中都持久化狀態(tài)機,當(dāng)狀態(tài)機處于standby時(此時狀態(tài)機已經(jīng)完成持久化),若在stanby時正常斷電,異常斷電關(guān)機,則此時開啟系統(tǒng)將到standby狀態(tài),而實際處于work狀態(tài),故造成了狀態(tài)不一致。
    方案:此異常場景的解決方案是:不區(qū)分異常與正常斷電,重啟后都從初始working狀態(tài)開始。則要求不對狀態(tài)機進行持久化。

  • 場景3:若在進入sleep與subsleep都進行持久化,若正常關(guān)機,則啟動后恢復(fù)狀態(tài)機,需要區(qū)分當(dāng)前狀態(tài)處于sleep還是subsleep,若處于subsleep則切換到subwork,若處于sleep,則切換到working。若是在subsleep期間異常關(guān)機,則異常重啟后首先還是會進入subworking,此時則不對。
    方案:此時雖然錯誤處于subworking狀態(tài),但在此時只要mcu通知modem切換到working狀態(tài),都會使?fàn)顟B(tài)機切換到working,從而恢復(fù)正常。

綜上所述:此處處理的最佳方式是,狀態(tài)機進入subsleep狀態(tài)時,持久化狀態(tài)機,狀態(tài)機恢復(fù)完成后刪除此持久化文件;則正常關(guān)機后,重新啟動,狀態(tài)為subsleep狀態(tài)主動切換到subworking。若在sleep狀態(tài),無論正常關(guān)機還是異常關(guān)機,重新啟動后都處于working狀態(tài)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容