Files
MuditaOS/module-sys/SystemManager/SystemManager.cpp
Adam Dobrowolski 2cee195e09 EGD-3056 subscribtion based execution (on typeid) added and works fine
Till now there was no clear way to register hanlder for message in
Service, with this approach one can register function/lambda handler
for Message which encurages handling messages in functions rather than
in hudge switch cases.
With aproach like this our Messages resembe something between Command
and State pattern. In state pattern `Message` would be `Event` send,
whereas Service would be `Context`.
2020-04-15 16:50:19 +02:00

342 lines
11 KiB
C++

/*
* Service.cpp
*
* Created on: Mar 7, 2019
* Author: mati
*/
#include "SystemManager.hpp"
#include "thread.hpp"
#include "ticks.hpp"
#include "critical.hpp"
#include <algorithm>
#include <../module-services/service-evtmgr/Constants.hpp>
namespace sys
{
using namespace cpp_freertos;
using namespace std;
using namespace sys;
const char *systemManagerServiceName = "SysMgrService";
void SystemManager::set(enum State state)
{
LOG_DEBUG("System manager state: [%s] -> [%s]", c_str(this->state), c_str(state));
this->state = state;
}
SystemManager::SystemManager(TickType_t pingInterval)
: Service(systemManagerServiceName, "", 8192), pingInterval(pingInterval)
{
// Specify list of channels which System Manager is registered to
busChannels = {BusChannels::SystemManagerRequests};
}
SystemManager::~SystemManager()
{
LOG_DEBUG("%s", (GetName() + ":destructor").c_str());
}
void SystemManager::Run()
{
InitHandler();
if (userInit) {
userInit();
}
// in shutdown we need to wait till event manager tells us that it's ok to stfu
while (state == State::Running || state == State::Shutdown) {
auto msg = mailbox.pop();
msg->Execute(this);
}
DestroyService(service::name::evt_manager, this);
Bus::Remove(shared_from_this());
EndScheduler();
// Power off system
switch (state) {
case State::Reboot:
powerManager->Reboot();
break;
case State::Shutdown:
powerManager->PowerOff();
break;
default:
LOG_FATAL("State changed after reset/shutdown was requested! this is terrible failure!");
exit(1);
};
}
void SystemManager::StartSystem(std::function<int()> init)
{
LOG_FATAL("Initializing system...");
powerManager = std::make_unique<PowerManager>();
// Switch system to full functionality(clocks and power domains configured to max values)
powerManager->Switch(PowerManager::Mode::FullSpeed);
userInit = init;
// Start System manager
StartService();
// M.P: Ping/pong mechanism is turned off. It doesn't bring any value to the system.
// pingPongTimerID = CreateTimer(Ticks::MsToTicks(pingInterval), true);
// ReloadTimer(pingPongTimerID);
}
bool SystemManager::CloseSystem(Service *s)
{
Bus::SendUnicast(std::make_shared<SystemManagerCmd>(Code::CloseSystem), systemManagerServiceName, s);
return true;
}
bool SystemManager::Reboot(Service *s)
{
Bus::SendUnicast(std::make_shared<SystemManagerCmd>(Code::Reboot), systemManagerServiceName, s);
return true;
}
bool SystemManager::SuspendSystem(Service *caller)
{
if (powerManager->GetCurrentMode() != PowerManager::Mode::FullSpeed) {
LOG_WARN("System is already suspended.");
return false;
}
for (auto w = servicesList.rbegin(); w != servicesList.rend(); ++w) {
if ((*w)->parent == "" && (*w)->GetName() != caller->GetName()) {
auto ret = Bus::SendUnicast(
std::make_shared<SystemMessage>(SystemMessageType::SwitchPowerMode, ServicePowerMode::SuspendToRAM),
(*w)->GetName(),
caller,
1000);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first != ReturnCodes::Success && (resp->retCode != ReturnCodes::Success)) {
LOG_FATAL("Service %s failed to enter low-power mode", (*w)->GetName().c_str());
}
}
}
powerManager->Switch(PowerManager::Mode::LowPowerIdle);
return true;
}
bool SystemManager::ResumeSystem(Service *caller)
{
if (powerManager->GetCurrentMode() == PowerManager::Mode::FullSpeed) {
LOG_WARN("System is already resumed.");
return false;
}
powerManager->Switch(PowerManager::Mode::FullSpeed);
for (const auto &w : servicesList) {
if (w->parent == "" && w->GetName() != caller->GetName()) {
auto ret = Bus::SendUnicast(
std::make_shared<SystemMessage>(SystemMessageType::SwitchPowerMode, ServicePowerMode::Active),
w->GetName(),
caller,
1000);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first != ReturnCodes::Success && (resp->retCode != ReturnCodes::Success)) {
LOG_FATAL("Service %s failed to exit low-power mode", w->GetName().c_str());
}
}
}
return true;
}
bool SystemManager::SuspendService(const std::string &name, sys::Service *caller)
{
auto ret = Bus::SendUnicast(
std::make_shared<SystemMessage>(SystemMessageType::SwitchPowerMode, ServicePowerMode::SuspendToRAM),
name,
caller,
1000);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first != ReturnCodes::Success && (resp->retCode != ReturnCodes::Success)) {
LOG_FATAL("Service %s failed to exit low-power mode", name.c_str());
}
return true;
}
bool SystemManager::ResumeService(const std::string &name, sys::Service *caller)
{
auto ret = Bus::SendUnicast(
std::make_shared<SystemMessage>(SystemMessageType::SwitchPowerMode, ServicePowerMode::Active),
name,
caller,
1000);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first != ReturnCodes::Success && (resp->retCode != ReturnCodes::Success)) {
LOG_FATAL("Service %s failed to exit low-power mode", name.c_str());
}
return true;
}
bool SystemManager::CreateService(std::shared_ptr<Service> service, Service *caller, TickType_t timeout)
{
CriticalSection::Enter();
servicesList.push_back(service);
CriticalSection::Exit();
service->StartService();
auto msg = std::make_shared<SystemMessage>(SystemMessageType::Start);
auto ret = Bus::SendUnicast(msg, service->GetName(), caller, timeout);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first == ReturnCodes::Success && (resp->retCode == ReturnCodes::Success)) {
return true;
}
else {
return false;
}
}
bool SystemManager::DestroyService(const std::string &name, Service *caller, TickType_t timeout)
{
auto msg = std::make_shared<SystemMessage>(SystemMessageType::Exit);
auto ret = Bus::SendUnicast(msg, name, caller, timeout);
auto resp = std::static_pointer_cast<ResponseMessage>(ret.second);
if (ret.first == ReturnCodes::Success && (resp->retCode == ReturnCodes::Success)) {
cpp_freertos::LockGuard lck(destroyMutex);
auto serv = std::find_if(servicesList.begin(), servicesList.end(), [&](std::shared_ptr<Service> const &s) {
return s->GetName() == name;
});
if (serv == servicesList.end()) {
LOG_ERROR("No such service to destroy in services list: %s", name.c_str());
return false;
}
servicesList.erase(serv);
return true;
}
else {
LOG_ERROR("Service to close: %s doesn't exist", name.c_str());
return false;
}
}
void SystemManager::kill(std::shared_ptr<Service> const &toKill)
{
// TODO timeout on deinit hanlder? anything? message it?
auto ret = toKill->DeinitHandler();
if (ret != sys::ReturnCodes::Success) {
LOG_DEBUG("deinit handler: %s", c_str(ret));
}
toKill->CloseHandler();
}
ReturnCodes SystemManager::InitHandler()
{
isReady = true;
SystemManagerCmd cmd;
subscribe(&cmd, [&](DataMessage *msg, ResponseMessage *resp) {
if (msg->channel == BusChannels::SystemManagerRequests) {
auto *data = static_cast<SystemManagerCmd *>(msg);
switch (data->type) {
case Code::CloseSystem:
CloseSystemHandler();
break;
case Code::Reboot:
RebootHandler();
break;
case Code::None:
break;
}
}
return Message_t();
});
return ReturnCodes::Success;
}
void SystemManager::TickHandler(uint32_t id)
{
if (id == pingPongTimerID) {
for (auto &w : servicesList) {
if (w->pingTimestamp == 0) {
// no reponse for ping messages, restart system
LOG_FATAL("%s", (w->GetName() + " failed to response to ping message").c_str());
exit(1);
}
else {
w->pingTimestamp = 0;
}
}
Bus::SendBroadcast(std::make_shared<SystemMessage>(SystemMessageType::Ping), this);
}
}
Message_t SystemManager::DataReceivedHandler(DataMessage *msg, ResponseMessage *resp)
{
return std::make_shared<ResponseMessage>();
}
void SystemManager::CloseSystemHandler()
{
LOG_DEBUG("Invoking closing procedure...");
DeleteTimer(pingPongTimerID);
// We are going to remove services in reversed order of creation
CriticalSection::Enter();
std::reverse(servicesList.begin(), servicesList.end());
CriticalSection::Exit();
retry:
for (auto &service : servicesList) {
if (service->GetName() == service::name::evt_manager) {
LOG_DEBUG("Delay closing %s", service::name::evt_manager.c_str());
continue;
}
if (service->parent == "") {
auto ret = DestroyService(service->GetName(), this);
if (!ret) {
// no response to exit message,
LOG_FATAL("%s", (service->GetName() + " failed to response to exit message").c_str());
kill(service);
}
goto retry;
}
}
set(State::Shutdown);
}
void SystemManager::RebootHandler()
{
CloseSystemHandler();
set(State::Reboot);
}
std::vector<std::shared_ptr<Service>> SystemManager::servicesList;
cpp_freertos::MutexStandard SystemManager::destroyMutex;
std::unique_ptr<PowerManager> SystemManager::powerManager;
} // namespace sys