mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-30 08:52:01 -05:00
220 lines
6.9 KiB
C++
220 lines
6.9 KiB
C++
#include <algorithm>
|
|
#include "Service.hpp"
|
|
#include "Service/Message.hpp"
|
|
#include "thread.hpp"
|
|
#include "ticks.hpp"
|
|
#include "timer.hpp"
|
|
#include "Bus.hpp"
|
|
|
|
// this could use Scoped() class from utils to print execution time too
|
|
void debug_msg(sys::Service *srvc, sys::DataMessage *&ptr)
|
|
{
|
|
#if (DEBUG_SERVICE_MESSAGES > 0)
|
|
LOG_DEBUG("Handle message ([%s] -> [%s] (%s) data: %s",
|
|
ptr->sender.c_str(),
|
|
srvc->GetName().c_str(),
|
|
typeid(*ptr).name(),
|
|
std::string(*ptr).c_str());
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
namespace sys
|
|
{
|
|
|
|
using namespace cpp_freertos;
|
|
using namespace std;
|
|
|
|
Service::Service(std::string name, std::string parent, uint32_t stackDepth, ServicePriority priority)
|
|
: cpp_freertos::Thread(name, stackDepth / 4 /* Stack depth in bytes */, static_cast<UBaseType_t>(priority)),
|
|
parent(parent), mailbox(this), pingTimestamp(UINT32_MAX), isReady(false), enableRunLoop(false)
|
|
|
|
{
|
|
// System channel is mandatory for each service
|
|
busChannels = {BusChannels::System};
|
|
}
|
|
|
|
Service::~Service()
|
|
{
|
|
enableRunLoop = false;
|
|
LOG_DEBUG("%s", (GetName() + ":Service base destructor").c_str());
|
|
}
|
|
|
|
void Service::StartService()
|
|
{
|
|
Bus::Add(shared_from_this());
|
|
enableRunLoop = true;
|
|
if (!Start()) {
|
|
LOG_FATAL("FATAL ERROR: Start service failed!:%s", GetName().c_str());
|
|
configASSERT(0);
|
|
}
|
|
}
|
|
|
|
void Service::Run()
|
|
{
|
|
|
|
while (enableRunLoop) {
|
|
|
|
auto msg = mailbox.pop();
|
|
|
|
uint32_t timestamp = cpp_freertos::Ticks::GetTicks();
|
|
|
|
// Remove all staled messages
|
|
staleUniqueMsg.erase(std::remove_if(staleUniqueMsg.begin(),
|
|
staleUniqueMsg.end(),
|
|
[&](const auto &id) {
|
|
return ((id.first == msg->uniID) ||
|
|
((timestamp - id.second) >= 15000));
|
|
}),
|
|
staleUniqueMsg.end());
|
|
|
|
/// this is the only place that uses Reponse messages (service manager doesnt...)
|
|
auto ret = msg->Execute(this);
|
|
if (ret == nullptr) {
|
|
LOG_FATAL("NO MESSAGE from: %s msg type: %d", msg->sender.c_str(), static_cast<int>(msg->type));
|
|
ret = std::make_shared<DataMessage>(MessageType::MessageTypeUninitialized);
|
|
}
|
|
|
|
// Unicast messages always need response with the same ID as received message
|
|
// Don't send responses to themselves,
|
|
// Don't send responses to responses
|
|
if ((msg->transType == Message::TransmissionType ::Unicast) && (msg->type != Message::Type::Response) &&
|
|
(GetName() != msg->sender)) {
|
|
Bus::SendResponse(ret, msg, this);
|
|
}
|
|
}
|
|
|
|
Bus::Remove(shared_from_this());
|
|
};
|
|
|
|
auto Service::MessageEntry(DataMessage *message, ResponseMessage *response) -> Message_t
|
|
{
|
|
Message_t ret = std::make_shared<sys::ResponseMessage>(sys::ReturnCodes::Unresolved);
|
|
auto idx = type_index(typeid(*message));
|
|
auto handler = message_handlers.find(idx);
|
|
debug_msg(this, message);
|
|
if (handler != message_handlers.end()) {
|
|
ret = message_handlers[idx](message, response);
|
|
}
|
|
else {
|
|
ret = DataReceivedHandler(message, response);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
bool Service::connect(Message *msg, MessageHandler handler)
|
|
{
|
|
auto &type = typeid(*msg);
|
|
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] = handler;
|
|
return true;
|
|
}
|
|
LOG_ERROR("Handler for: %s already registered!", type.name());
|
|
return false;
|
|
}
|
|
|
|
bool Service::connect(Message &&msg, MessageHandler handler)
|
|
{
|
|
return Service::connect(&msg, handler);
|
|
}
|
|
|
|
// Create service timer
|
|
uint32_t Service::CreateTimer(uint32_t interval, bool isPeriodic, const std::string &name)
|
|
{
|
|
uint32_t unique = ServiceTimer::GetNextUniqueID();
|
|
std::string nameNew = name;
|
|
if (name.empty()) {
|
|
nameNew = GetName() + "Timer" + std::to_string(unique);
|
|
}
|
|
// this is bad... timer should message service, not run code on it
|
|
timersList.push_back(
|
|
std::make_unique<ServiceTimer>(nameNew, Ticks::MsToTicks(interval), isPeriodic, unique, this));
|
|
LOG_DEBUG("%s", std::string(nameNew + "'s ID: " + std::to_string(unique)).c_str());
|
|
return unique;
|
|
}
|
|
|
|
// Reload service timer
|
|
void Service::ReloadTimer(uint32_t id)
|
|
{
|
|
auto it = std::find_if(timersList.begin(), timersList.end(), [&](std::unique_ptr<ServiceTimer> const &s) {
|
|
return s->GetId() == id;
|
|
});
|
|
if (it == timersList.end()) {
|
|
// not found, error
|
|
}
|
|
else {
|
|
(*it)->Start(0);
|
|
}
|
|
}
|
|
// Delete timer
|
|
void Service::DeleteTimer(uint32_t id)
|
|
{
|
|
auto it = std::find_if(timersList.begin(), timersList.end(), [&](std::unique_ptr<ServiceTimer> const &s) {
|
|
return s->GetId() == id;
|
|
});
|
|
if (it == timersList.end()) {
|
|
// not found, error
|
|
}
|
|
else {
|
|
(*it)->Stop(0);
|
|
timersList.erase(it);
|
|
}
|
|
}
|
|
|
|
// Set period
|
|
void Service::setTimerPeriod(uint32_t id, uint32_t period)
|
|
{
|
|
auto it = std::find_if(timersList.begin(), timersList.end(), [&](std::unique_ptr<ServiceTimer> const &s) {
|
|
return s->GetId() == id;
|
|
});
|
|
if (it == timersList.end()) {
|
|
// not found, error
|
|
}
|
|
else {
|
|
(*it)->SetPeriod(period, 0);
|
|
}
|
|
}
|
|
|
|
// Set period
|
|
void Service::stopTimer(uint32_t id)
|
|
{
|
|
auto it = std::find_if(timersList.begin(), timersList.end(), [&](std::unique_ptr<ServiceTimer> const &s) {
|
|
return s->GetId() == id;
|
|
});
|
|
if (it == timersList.end()) {
|
|
// not found, error
|
|
}
|
|
else {
|
|
(*it)->Stop(0);
|
|
}
|
|
}
|
|
|
|
void Service::CloseHandler()
|
|
{
|
|
|
|
// Stop currently active timers
|
|
for (auto &w : timersList) {
|
|
w->Stop(0);
|
|
}
|
|
|
|
enableRunLoop = false;
|
|
};
|
|
|
|
ServiceTimer::ServiceTimer(
|
|
const std::string &name, TickType_t tick, bool isPeriodic, uint32_t idx, Service *service)
|
|
: Timer(name.c_str(), tick, isPeriodic), m_isPeriodic(isPeriodic), m_interval(tick), m_id(idx),
|
|
m_service(service)
|
|
|
|
{}
|
|
|
|
void ServiceTimer::Run()
|
|
{
|
|
m_service->TickHandler(m_id);
|
|
}
|
|
|
|
uint32_t ServiceTimer::m_timers_unique_idx = 0;
|
|
|
|
} // namespace sys
|