#include "Service.hpp" #include "Bus.hpp" // for Bus #include "FreeRTOSConfig.h" // for configASSERT #include "MessageType.hpp" // for MessageType, MessageType::MessageType... #include "Service/Common.hpp" // for BusChannels, ReturnCodes, ReturnCodes... #include "Service/Mailbox.hpp" // for Mailbox #include "Service/Message.hpp" // for Message, Message_t, DataMessage, Resp... #include "Timer.hpp" // for Timer #include "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 // for remove_if #include // for uint32_t, uint64_t, UINT32_MAX #include // for std #include // for type_info // 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(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(msg->type)); ret = std::make_shared(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::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); } void Service::CloseHandler() { timers.stop(); enableRunLoop = false; }; auto Service::TimerHandle(SystemMessage &message) -> ReturnCodes { auto timer_message = dynamic_cast(&message); if (timer_message == nullptr) { LOG_ERROR("Wrong message in system message handler"); return ReturnCodes::Failure; } auto timer = timers.get(timer_message->getTimer()); if (timer == timers.noTimer()) { LOG_ERROR("No such timer registered in Service"); return ReturnCodes::Failure; } (*timer)->onTimeout(); return ReturnCodes::Success; } void Service::Timers::stop() { for (auto timer : list) { timer->stop(); } } } // namespace sys