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)換分為兩種情況:- subsleep -> working
mcu拉高power_key引腳,mcu消息通知切換到working狀態(tài) - 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í)行請求
- subsleep -> working
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異常場景需要考慮。
- 在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:若不進行持久化的話,由于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)。