Files
MuditaOS/module-sys/Service/Service.cpp
Adam Dobrowolski f7f5bc377a [EGD-8208] Added stop audio route on async response
We get Token on play, and require it in stop. While we cant
stop sound by title & data set to play. And adding standard
handling causes either totally synchronous calls or requires
multiple additional guards it seemed like best use.
Fixed expectation for the same named Response, but meant for bell
Needed so that
- we ring only when needed
- we handle CLIP and RING properly
Applied style
Missed file added
Moved SML logger as it's usefull to some more common parts of code
2022-02-22 13:36:27 +01:00

300 lines
9.4 KiB
C++

// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
#include <Service/Service.hpp>
#include "FreeRTOSConfig.h" // for configASSERT
#include "MessageType.hpp" // for MessageType, MessageType::MessageType...
#include "Service/Mailbox.hpp" // for Mailbox
#include <Service/Message.hpp> // for Message, MessagePointer, DataMessage, Resp...
#include "Timers/SystemTimer.hpp"
#include "Timers/TimerHandle.hpp" // for Timer
#include "Timers/TimerMessage.hpp" // for TimerMessage
#include <log/debug.hpp> // for DEBUG_SERVICE_MESSAGES
#include <log/log.hpp> // for LOG_ERROR, LOG_DEBUG, LOG_FATAL
#include "mutex.hpp" // for cpp_freertos
#include "portmacro.h" // for UBaseType_t
#include "thread.hpp" // for Thread
#include "ticks.hpp" // for Ticks
#include <algorithm> // for remove_if
#include <cstdint> // for uint32_t, uint64_t, UINT32_MAX
#include <iosfwd> // for std
#include <typeinfo> // for type_info
#include <system/Constants.hpp>
// this could use Scoped() class from utils to print execution time too
void debug_msg(sys::Service *srvc, const sys::Message *ptr)
{
#if (DEBUG_SERVICE_MESSAGES > 0)
const char *realname = typeid(*ptr).name();
assert(srvc);
assert(ptr);
if (xPortIsInsideInterrupt()) {
throw std::runtime_error("message sent from irq");
}
if (xTaskGetCurrentTaskHandle() == nullptr) {
throw std::runtime_error("message sent from crit section");
}
LOG_DEBUG("([%s] -> [%s] (%s) data: %s | %s",
ptr ? ptr->sender.c_str() : "",
srvc ? srvc->GetName().c_str() : "",
realname,
std::string(*ptr).c_str(),
ptr->to_string().c_str());
#else
#endif
}
#if (DEBUG_SERVICE_MESSAGES > 0)
#define log_debug(...) LOG_DEBUG(__VA_ARGS__)
#else
#define log_debug(...)
#endif
namespace sys
{
using namespace cpp_freertos;
using namespace std;
Service::Service(
std::string name, std::string parent, uint32_t stackDepth, ServicePriority priority, Watchdog &watchdog)
: cpp_freertos::Thread(name, stackDepth / 4 /* Stack depth in bytes */, static_cast<UBaseType_t>(priority)),
parent(parent), bus(this, watchdog), mailbox(this), watchdog(watchdog), isReady(false), enableRunLoop(false)
{}
Service::~Service()
{
enableRunLoop = false;
LOG_DEBUG("%s", (GetName() + ":Service base destructor").c_str());
}
void Service::StartService()
{
bus.connect();
enableRunLoop = true;
if (!Start()) {
LOG_FATAL("FATAL ERROR: Start service failed!:%s", GetName().c_str());
configASSERT(0);
}
}
void Service::CloseService()
{
bus.disconnect();
}
void Service::Run()
{
while (enableRunLoop) {
auto msg = mailbox.pop();
if (!msg) {
continue;
}
const bool respond = msg->type != Message::Type::Response && GetName() != msg->sender;
currentlyProcessing = msg;
auto response = msg->Execute(this);
if (response == nullptr || !respond) {
continue;
}
bus.sendResponse(response, msg);
}
CloseService();
};
auto Service::MessageEntry(Message *message, ResponseMessage *response) -> MessagePointer
{
return response == nullptr ? HandleMessage(message) : HandleResponse(response);
}
auto Service::HandleMessage(Message *message) -> MessagePointer
{
debug_msg(this, message);
if (const auto &[handled, ret] = ExecuteMessageHandler(message); handled) {
return ret;
}
if (auto dataMsg = dynamic_cast<DataMessage *>(message); dataMsg != nullptr) {
return DataReceivedHandler(dataMsg, nullptr);
}
LOG_ERROR("Failed to handle message of type [%s]", typeid(*message).name());
return nullptr;
}
auto Service::HandleResponse(ResponseMessage *message) -> MessagePointer
{
debug_msg(this, message);
if (const auto &[handled, ret] = ExecuteMessageHandler(message); handled) {
return ret;
}
DataMessage dummy(MessageType::MessageTypeUninitialized);
return DataReceivedHandler(&dummy, message);
}
auto Service::ExecuteMessageHandler(Message *message) -> std::pair<bool, MessagePointer>
{
const auto idx = type_index(typeid(*message));
if (const auto handler = message_handlers.find(idx); handler != message_handlers.end()) {
const auto &handlerFunction = handler->second;
if (handlerFunction == nullptr) {
return {true, nullptr};
}
return {true, handlerFunction(message)};
}
return {false, nullptr};
}
bool Service::connect(const type_info &type, MessageHandler handler)
{
auto idx = type_index(type);
if (message_handlers.find(idx) == message_handlers.end()) {
log_debug("Registering new message handler on %s", type.name());
message_handlers[idx] = std::move(handler);
return true;
}
LOG_ERROR("Handler for: %s already registered!", type.name());
return false;
}
bool Service::connect(Message *msg, MessageHandler handler)
{
auto &type = typeid(*msg);
return connect(type, handler);
}
bool Service::connect(Message &&msg, MessageHandler handler)
{
return connect(&msg, handler);
}
bool Service::disconnect(const std::type_info &type)
{
auto iter = message_handlers.find(type_index(type));
if (iter == std::end(message_handlers)) {
return false;
}
message_handlers.erase(iter);
return true;
}
void Service::CloseHandler()
{
timers.stop();
enableRunLoop = false;
}
auto Service::ProcessCloseReason(CloseReason closeReason) -> void{};
auto Service::ProcessCloseReasonHandler(CloseReason closeReason) -> void
{
ProcessCloseReason(closeReason);
// to reject all messages except system ones
isReady = false;
sendCloseReadyMessage(this);
};
auto Service::TimerHandle(SystemMessage &message) -> ReturnCodes
{
auto timer_message = dynamic_cast<sys::TimerMessage *>(&message);
if (timer_message == nullptr) {
LOG_ERROR("Wrong message in system message handler");
return ReturnCodes::Failure;
}
auto sysTimer = timer_message->getTimer();
auto timer = timers.get(sysTimer);
if (timer == nullptr) {
LOG_ERROR("No such timer registered in Service");
return ReturnCodes::Failure;
}
timer->onTimeout();
return ReturnCodes::Success;
}
void Service::Timers::attach(timer::SystemTimer *timer)
{
list.push_back(timer);
}
void Service::Timers::detach(timer::SystemTimer *timer)
{
const auto it = std::find(list.begin(), list.end(), timer);
if (it != list.end()) {
list.erase(it);
}
}
void Service::Timers::stop()
{
for (auto timer : list) {
timer->stop();
}
}
auto Service::Timers::get(timer::SystemTimer *timer) noexcept -> timer::SystemTimer *
{
auto it = std::find(list.begin(), list.end(), timer);
if (it == list.end()) {
return nullptr;
}
return *it;
}
void Service::sendCloseReadyMessage(Service *service)
{
auto msg = std::make_shared<sys::ReadyToCloseMessage>();
service->bus.sendUnicast(std::move(msg), service::name::system_manager);
}
std::string Service::getCurrentProcessing()
{
return currentlyProcessing ? std::string(typeid(*currentlyProcessing).name()) : "nothing in progress";
}
auto Proxy::handleMessage(Service *service, Message *message, ResponseMessage *response) -> MessagePointer
{
if (service->isReady) {
return service->MessageEntry(message, response);
}
return std::make_shared<ResponseMessage>(ReturnCodes::ServiceDoesntExist);
}
auto Proxy::handleSystemMessage(Service *service, SystemMessage *message) -> MessagePointer
{
auto ret = ReturnCodes::Success;
switch (message->systemMessageType) {
case SystemMessageType::SwitchPowerMode:
service->SwitchPowerModeHandler(message->powerMode);
break;
case SystemMessageType::Exit:
ret = service->DeinitHandler();
service->CloseHandler();
break;
case SystemMessageType::Timer:
ret = service->TimerHandle(*message);
break;
case SystemMessageType::Start:
ret = service->InitHandler();
if (ret == ReturnCodes::Success) {
service->isReady = true;
}
break;
case SystemMessageType::ServiceCloseReason:
service->ProcessCloseReasonHandler(static_cast<ServiceCloseReasonMessage *>(message)->getCloseReason());
break;
}
return std::make_shared<ResponseMessage>(ret);
}
bool Service::isConnected(std::type_index idx)
{
return message_handlers.find(idx) != message_handlers.end();
}
} // namespace sys