diff --git a/framework.mk b/framework.mk index a5a65364..ee0a7ba4 100644 --- a/framework.mk +++ b/framework.mk @@ -1,5 +1,5 @@ -# This file needs FRAMEWORK_PATH and API set correctly -# Valid API settings: rtems, linux, freeRTOS +# This file needs FRAMEWORK_PATH and OS_FSFW set correctly by another Makefile. +# Valid API settings: rtems, linux, freeRTOS, host CXXSRC += $(wildcard $(FRAMEWORK_PATH)/action/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/container/*.cpp) @@ -12,7 +12,6 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/devicehandlers/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/eventmatching/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/fdir/*.cpp) -CXXSRC += $(wildcard $(FRAMEWORK_PATH)/framework.mk/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/matching/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/globalfunctions/math/*.cpp) @@ -26,14 +25,16 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/objectmanager/*.cpp) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp) # select the OS -ifeq ($(OS),rtems) +ifeq ($(OS_FSFW),rtems) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp) -else ifeq ($(OS),linux) +else ifeq ($(OS_FSFW),linux) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp) -else ifeq ($(OS),freeRTOS) +else ifeq ($(OS_FSFW),freeRTOS) CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp) +else ifeq ($(OS_FSFW),host) +CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp) else -$(error invalid OS specified, valid OS are rtems, linux, freeRTOS) +$(error invalid OS specified, valid OS are rtems, linux, freeRTOS, host) endif CXXSRC += $(wildcard $(FRAMEWORK_PATH)/parameters/*.cpp) diff --git a/ipc/CommandMessage.cpp b/ipc/CommandMessage.cpp index fa41c653..679793e3 100644 --- a/ipc/CommandMessage.cpp +++ b/ipc/CommandMessage.cpp @@ -120,3 +120,12 @@ void CommandMessage::setReplyRejected(ReturnValue_t reason, setParameter(reason); setParameter2(initialCommand); } + +ReturnValue_t CommandMessage::getReplyRejectedReason( + Command_t *initialCommand) const { + ReturnValue_t reason = getParameter(); + if(initialCommand != nullptr) { + *initialCommand = getParameter2(); + } + return reason; +} diff --git a/ipc/CommandMessage.h b/ipc/CommandMessage.h index 2d966063..b75d94b1 100644 --- a/ipc/CommandMessage.h +++ b/ipc/CommandMessage.h @@ -124,6 +124,9 @@ public: */ void setToUnknownCommand(); void setReplyRejected(ReturnValue_t reason, Command_t initialCommand = CMD_NONE); + ReturnValue_t getReplyRejectedReason( + Command_t *initialCommand = nullptr) const; + size_t getMinimumMessageSize() const; }; diff --git a/objectmanager/ObjectManagerIF.h b/objectmanager/ObjectManagerIF.h index 90a1ce3b..dcef81cd 100644 --- a/objectmanager/ObjectManagerIF.h +++ b/objectmanager/ObjectManagerIF.h @@ -21,7 +21,8 @@ public: static constexpr uint8_t INTERFACE_ID = CLASS_ID::OBJECT_MANAGER_IF; static constexpr ReturnValue_t INSERTION_FAILED = MAKE_RETURN_CODE( 1 ); static constexpr ReturnValue_t NOT_FOUND = MAKE_RETURN_CODE( 2 ); - static constexpr ReturnValue_t CHILD_INIT_FAILED = MAKE_RETURN_CODE( 3 ); + + static constexpr ReturnValue_t CHILD_INIT_FAILED = MAKE_RETURN_CODE( 3 ); //!< Can be used if the initialization of a SystemObject failed. static constexpr ReturnValue_t INTERNAL_ERR_REPORTER_UNINIT = MAKE_RETURN_CODE( 4 ); protected: diff --git a/osal/FreeRTOS/FixedTimeslotTask.cpp b/osal/FreeRTOS/FixedTimeslotTask.cpp index c062101d..17804d90 100644 --- a/osal/FreeRTOS/FixedTimeslotTask.cpp +++ b/osal/FreeRTOS/FixedTimeslotTask.cpp @@ -125,19 +125,18 @@ void FixedTimeslotTask::checkMissedDeadline(const TickType_t xLastWakeTime, * it. */ TickType_t currentTickCount = xTaskGetTickCount(); TickType_t timeToWake = xLastWakeTime + interval; - // Tick count has overflown - if(currentTickCount < xLastWakeTime) { - // Time to wake has overflown as well. If the tick count - // is larger than the time to wake, a deadline was missed. - if(timeToWake < xLastWakeTime and - currentTickCount > timeToWake) { + // Time to wake has not overflown. + if(timeToWake > xLastWakeTime) { + /* If the current time has overflown exclusively or the current + * tick count is simply larger than the time to wake, a deadline was + * missed */ + if((currentTickCount < xLastWakeTime) or (currentTickCount > timeToWake)) { handleMissedDeadline(); } } - // No tick count overflow. If the timeToWake has not overflown - // and the current tick count is larger than the time to wake, - // a deadline was missed. - else if(timeToWake > xLastWakeTime and currentTickCount > timeToWake) { + /* Time to wake has overflown. A deadline was missed if the current time + * is larger than the time to wake */ + else if((timeToWake < xLastWakeTime) and (currentTickCount > timeToWake)) { handleMissedDeadline(); } } diff --git a/osal/FreeRTOS/PeriodicTask.cpp b/osal/FreeRTOS/PeriodicTask.cpp index deab2dc1..91135375 100644 --- a/osal/FreeRTOS/PeriodicTask.cpp +++ b/osal/FreeRTOS/PeriodicTask.cpp @@ -1,17 +1,19 @@ +#include "PeriodicTask.h" + #include #include -#include "PeriodicTask.h" PeriodicTask::PeriodicTask(const char *name, TaskPriority setPriority, TaskStackSize setStack, TaskPeriod setPeriod, void (*setDeadlineMissedFunc)()) : started(false), handle(NULL), period(setPeriod), deadlineMissedFunc( - setDeadlineMissedFunc) { - - BaseType_t status = xTaskCreate(taskEntryPoint, name, setStack, this, setPriority, &handle); + setDeadlineMissedFunc) +{ + BaseType_t status = xTaskCreate(taskEntryPoint, name, + setStack, this, setPriority, &handle); if(status != pdPASS){ - sif::debug << "PeriodicTask Insufficient heap memory remaining. Status: " - << status << std::endl; + sif::debug << "PeriodicTask Insufficient heap memory remaining. " + "Status: " << status << std::endl; } } @@ -21,16 +23,19 @@ PeriodicTask::~PeriodicTask(void) { } void PeriodicTask::taskEntryPoint(void* argument) { - //The argument is re-interpreted as PeriodicTask. The Task object is global, so it is found from any place. + // The argument is re-interpreted as PeriodicTask. The Task object is + // global, so it is found from any place. PeriodicTask *originalTask(reinterpret_cast(argument)); - // Task should not start until explicitly requested - // in FreeRTOS, tasks start as soon as they are created if the scheduler is running - // but not if the scheduler is not running. - // to be able to accommodate both cases we check a member which is set in #startTask() - // if it is not set and we get here, the scheduler was started before #startTask() was called and we need to suspend - // if it is set, the scheduler was not running before #startTask() was called and we can continue + /* Task should not start until explicitly requested, + * but in FreeRTOS, tasks start as soon as they are created if the scheduler + * is running but not if the scheduler is not running. + * To be able to accommodate both cases we check a member which is set in + * #startTask(). If it is not set and we get here, the scheduler was started + * before #startTask() was called and we need to suspend if it is set, + * the scheduler was not running before #startTask() was called and we + * can continue */ - if (!originalTask->started) { + if (not originalTask->started) { vTaskSuspend(NULL); } @@ -59,31 +64,72 @@ void PeriodicTask::taskFunctionality() { TickType_t xLastWakeTime; const TickType_t xPeriod = pdMS_TO_TICKS(this->period * 1000.); /* The xLastWakeTime variable needs to be initialized with the current tick - count. Note that this is the only time the variable is written to explicitly. - After this assignment, xLastWakeTime is updated automatically internally within - vTaskDelayUntil(). */ + count. Note that this is the only time the variable is written to + explicitly. After this assignment, xLastWakeTime is updated automatically + internally within vTaskDelayUntil(). */ xLastWakeTime = xTaskGetTickCount(); /* Enter the loop that defines the task behavior. */ for (;;) { - for (ObjectList::iterator it = objectList.begin(); - it != objectList.end(); ++it) { - (*it)->performOperation(); + for (auto const& object: objectList) { + object->performOperation(); } - //TODO deadline missed check + + checkMissedDeadline(xLastWakeTime, xPeriod); + vTaskDelayUntil(&xLastWakeTime, xPeriod); + } } -ReturnValue_t PeriodicTask::addComponent(object_id_t object) { +ReturnValue_t PeriodicTask::addComponent(object_id_t object, bool setTaskIF) { ExecutableObjectIF* newObject = objectManager->get( object); - if (newObject == NULL) { + if (newObject == nullptr) { + sif::error << "PeriodicTask::addComponent: Invalid object. Make sure" + "it implements ExecutableObjectIF!" << std::endl; return HasReturnvaluesIF::RETURN_FAILED; } objectList.push_back(newObject); + + if(setTaskIF) { + newObject->setTaskIF(this); + } return HasReturnvaluesIF::RETURN_OK; } uint32_t PeriodicTask::getPeriodMs() const { return period * 1000; } + +void PeriodicTask::checkMissedDeadline(const TickType_t xLastWakeTime, + const TickType_t interval) { + /* Check whether deadline was missed while also taking overflows + * into account. Drawing this on paper with a timeline helps to understand + * it. */ + TickType_t currentTickCount = xTaskGetTickCount(); + TickType_t timeToWake = xLastWakeTime + interval; + // Time to wake has not overflown. + if(timeToWake > xLastWakeTime) { + /* If the current time has overflown exclusively or the current + * tick count is simply larger than the time to wake, a deadline was + * missed */ + if((currentTickCount < xLastWakeTime) or (currentTickCount > timeToWake)) { + handleMissedDeadline(); + } + } + /* Time to wake has overflown. A deadline was missed if the current time + * is larger than the time to wake */ + else if((timeToWake < xLastWakeTime) and (currentTickCount > timeToWake)) { + handleMissedDeadline(); + } +} + +void PeriodicTask::handleMissedDeadline() { +#ifdef DEBUG + sif::warning << "PeriodicTask: " << pcTaskGetName(NULL) << + " missed deadline!\n" << std::flush; +#endif + if(deadlineMissedFunc != nullptr) { + this->deadlineMissedFunc(); + } +} diff --git a/osal/FreeRTOS/PeriodicTask.h b/osal/FreeRTOS/PeriodicTask.h index e7ba8dd2..09aa6bc7 100644 --- a/osal/FreeRTOS/PeriodicTask.h +++ b/osal/FreeRTOS/PeriodicTask.h @@ -1,48 +1,49 @@ -#ifndef MULTIOBJECTTASK_H_ -#define MULTIOBJECTTASK_H_ +#ifndef FRAMEWORK_OSAL_FREERTOS_PERIODICTASK_H_ +#define FRAMEWORK_OSAL_FREERTOS_PERIODICTASK_H_ #include #include #include -#include -#include "task.h" +#include +#include #include class ExecutableObjectIF; /** - * @brief This class represents a specialized task for periodic activities of multiple objects. - * - * @details MultiObjectTask is an extension to ObjectTask in the way that it is able to execute - * multiple objects that implement the ExecutableObjectIF interface. The objects must be - * added prior to starting the task. - * + * @brief This class represents a specialized task for + * periodic activities of multiple objects. * @ingroup task_handling */ class PeriodicTask: public PeriodicTaskIF { public: /** - * @brief Standard constructor of the class. - * @details The class is initialized without allocated objects. These need to be added - * with #addObject. - * In the underlying TaskBase class, a new operating system task is created. - * In addition to the TaskBase parameters, the period, the pointer to the - * aforementioned initialization function and an optional "deadline-missed" - * function pointer is passed. - * @param priority Sets the priority of a task. Values range from a low 0 to a high 99. - * @param stack_size The stack size reserved by the operating system for the task. - * @param setPeriod The length of the period with which the task's functionality will be - * executed. It is expressed in clock ticks. - * @param setDeadlineMissedFunc The function pointer to the deadline missed function - * that shall be assigned. + * Keep in Mind that you need to call before this vTaskStartScheduler()! + * A lot of task parameters are set in "FreeRTOSConfig.h". + * TODO: why does this need to be called before vTaskStartScheduler? + * @details + * The class is initialized without allocated objects. + * These need to be added with #addComponent. + * @param priority + * Sets the priority of a task. Values depend on freeRTOS configuration, + * high number means high priority. + * @param stack_size + * The stack size reserved by the operating system for the task. + * @param setPeriod + * The length of the period with which the task's + * functionality will be executed. It is expressed in clock ticks. + * @param setDeadlineMissedFunc + * The function pointer to the deadline missed function that shall + * be assigned. */ - PeriodicTask(const char *name, TaskPriority setPriority, TaskStackSize setStack, TaskPeriod setPeriod, - void (*setDeadlineMissedFunc)()); + PeriodicTask(const char *name, TaskPriority setPriority, + TaskStackSize setStack, TaskPeriod setPeriod, + void (*setDeadlineMissedFunc)()); /** - * @brief Currently, the executed object's lifetime is not coupled with the task object's - * lifetime, so the destructor is empty. + * @brief Currently, the executed object's lifetime is not coupled with + * the task object's lifetime, so the destructor is empty. */ virtual ~PeriodicTask(void); @@ -53,58 +54,72 @@ public: * The address of the task object is passed as an argument * to the system call. */ - ReturnValue_t startTask(void); + ReturnValue_t startTask() override; /** * Adds an object to the list of objects to be executed. * The objects are executed in the order added. * @param object Id of the object to add. - * @return RETURN_OK on success, RETURN_FAILED if the object could not be added. + * @return + * -@c RETURN_OK on success + * -@c RETURN_FAILED if the object could not be added. */ - ReturnValue_t addComponent(object_id_t object); + ReturnValue_t addComponent(object_id_t object, + bool setTaskIF = true) override; - uint32_t getPeriodMs() const; + uint32_t getPeriodMs() const override; - ReturnValue_t sleepFor(uint32_t ms); + ReturnValue_t sleepFor(uint32_t ms) override; protected: bool started; TaskHandle_t handle; - typedef std::vector ObjectList; //!< Typedef for the List of objects. + //! Typedef for the List of objects. + typedef std::vector ObjectList; /** * @brief This attribute holds a list of objects to be executed. */ ObjectList objectList; /** * @brief The period of the task. - * @details The period determines the frequency of the task's execution. It is expressed in clock ticks. + * @details + * The period determines the frequency of the task's execution. + * It is expressed in clock ticks. */ TaskPeriod period; /** * @brief The pointer to the deadline-missed function. - * @details This pointer stores the function that is executed if the task's deadline is missed. - * So, each may react individually on a timing failure. The pointer may be NULL, - * then nothing happens on missing the deadline. The deadline is equal to the next execution - * of the periodic task. + * @details + * This pointer stores the function that is executed if the task's deadline + * is missed so each may react individually on a timing failure. + * The pointer may be NULL, then nothing happens on missing the deadline. + * The deadline is equal to the next execution of the periodic task. */ void (*deadlineMissedFunc)(void); /** * @brief This is the function executed in the new task's context. - * @details It converts the argument back to the thread object type and copies the class instance - * to the task context. The taskFunctionality method is called afterwards. + * @details + * It converts the argument back to the thread object type and copies the + * class instance to the task context. The taskFunctionality method is + * called afterwards. * @param A pointer to the task object itself is passed as argument. */ static void taskEntryPoint(void* argument); /** * @brief The function containing the actual functionality of the task. - * @details The method sets and starts - * the task's period, then enters a loop that is repeated as long as the isRunning - * attribute is true. Within the loop, all performOperation methods of the added - * objects are called. Afterwards the checkAndRestartPeriod system call blocks the task - * until the next period. - * On missing the deadline, the deadlineMissedFunction is executed. + * @details + * The method sets and starts the task's period, then enters a loop that is + * repeated as long as the isRunning attribute is true. Within the loop, + * all performOperation methods of the added objects are called. + * Afterwards the checkAndRestartPeriod system call blocks the task until + * the next period. + * On missing the deadline, the deadlineMissedFunction is executed. */ void taskFunctionality(void); + + void checkMissedDeadline(const TickType_t xLastWakeTime, + const TickType_t interval); + void handleMissedDeadline(); }; -#endif /* MULTIOBJECTTASK_H_ */ +#endif /* PERIODICTASK_H_ */ diff --git a/osal/linux/PeriodicPosixTask.cpp b/osal/linux/PeriodicPosixTask.cpp index df5bbf93..c05f1030 100644 --- a/osal/linux/PeriodicPosixTask.cpp +++ b/osal/linux/PeriodicPosixTask.cpp @@ -21,13 +21,18 @@ void* PeriodicPosixTask::taskEntryPoint(void* arg) { return NULL; } -ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object) { +ReturnValue_t PeriodicPosixTask::addComponent(object_id_t object, + bool addTaskIF) { ExecutableObjectIF* newObject = objectManager->get( object); - if (newObject == NULL) { + if (newObject == nullptr) { return HasReturnvaluesIF::RETURN_FAILED; } objectList.push_back(newObject); + + if(setTaskIF) { + newObject->setTaskIF(this); + } return HasReturnvaluesIF::RETURN_OK; } diff --git a/osal/linux/PeriodicPosixTask.h b/osal/linux/PeriodicPosixTask.h index 85de0d59..bf1a995b 100644 --- a/osal/linux/PeriodicPosixTask.h +++ b/osal/linux/PeriodicPosixTask.h @@ -39,11 +39,12 @@ public: * @param object Id of the object to add. * @return RETURN_OK on success, RETURN_FAILED if the object could not be added. */ - ReturnValue_t addComponent(object_id_t object); + ReturnValue_t addComponent(object_id_t object, + bool addTaskIF = true) override; - uint32_t getPeriodMs() const; + uint32_t getPeriodMs() const override; - ReturnValue_t sleepFor(uint32_t ms); + ReturnValue_t sleepFor(uint32_t ms) override; private: typedef std::vector ObjectList; //!< Typedef for the List of objects. diff --git a/serialize/SerialArrayListAdapter.h b/serialize/SerialArrayListAdapter.h index bcd99818..21c6951d 100644 --- a/serialize/SerialArrayListAdapter.h +++ b/serialize/SerialArrayListAdapter.h @@ -1,18 +1,13 @@ -/** - * @file SerialArrayListAdapter.h - * @brief This file defines the SerialArrayListAdapter class. - * @date 22.07.2014 - * @author baetz - */ -#ifndef SERIALARRAYLISTADAPTER_H_ -#define SERIALARRAYLISTADAPTER_H_ +#ifndef FRAMEWORK_SERIALIZE_SERIALARRAYLISTADAPTER_H_ +#define FRAMEWORK_SERIALIZE_SERIALARRAYLISTADAPTER_H_ #include #include #include /** - * \ingroup serialize + * @ingroup serialize + * @author baetz */ template class SerialArrayListAdapter : public SerializeIF { @@ -25,14 +20,15 @@ public: return serialize(adaptee, buffer, size, maxSize, streamEndianness); } - static ReturnValue_t serialize(const ArrayList* list, uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) { + static ReturnValue_t serialize(const ArrayList* list, + uint8_t** buffer, size_t* size, size_t maxSize, + Endianness streamEndianness) { ReturnValue_t result = SerializeAdapter::serialize(&list->size, buffer, size, maxSize, streamEndianness); count_t i = 0; while ((result == HasReturnvaluesIF::RETURN_OK) && (i < list->size)) { - result = SerializeAdapter::serialize(&list->entries[i], buffer, size, - maxSize, streamEndianness); + result = SerializeAdapter::serialize(&list->entries[i], buffer, + size, maxSize, streamEndianness); ++i; } return result; @@ -58,11 +54,15 @@ public: return deSerialize(adaptee, buffer, size, streamEndianness); } - static ReturnValue_t deSerialize(ArrayList* list, const uint8_t** buffer, size_t* size, + static ReturnValue_t deSerialize(ArrayList* list, + const uint8_t** buffer, size_t* size, Endianness streamEndianness) { count_t tempSize = 0; ReturnValue_t result = SerializeAdapter::deSerialize(&tempSize, buffer, size, streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } if (tempSize > list->maxSize()) { return SerializeIF::TOO_MANY_ELEMENTS; } @@ -82,4 +82,4 @@ private: -#endif /* SERIALARRAYLISTADAPTER_H_ */ +#endif /* FRAMEWORK_SERIALIZE_SERIALARRAYLISTADAPTER_H_ */ diff --git a/serviceinterface/ServiceInterfaceBuffer.cpp b/serviceinterface/ServiceInterfaceBuffer.cpp index 58065994..8c510eac 100644 --- a/serviceinterface/ServiceInterfaceBuffer.cpp +++ b/serviceinterface/ServiceInterfaceBuffer.cpp @@ -1,11 +1,58 @@ #include #include #include +#include // to be implemented by bsp -extern "C" void printChar(const char*); +extern "C" void printChar(const char*, bool errStream); + +#ifndef UT699 + +ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string setMessage, + bool addCrToPreamble, bool buffered , bool errStream, uint16_t port): + isActive(true), logMessage(setMessage), + addCrToPreamble(addCrToPreamble), buffered(buffered), + errStream(errStream) { + if(buffered) { + // Set pointers if the stream is buffered. + setp( buf, buf + BUF_SIZE ); + } + preamble.reserve(MAX_PREAMBLE_SIZE); + preamble.resize(MAX_PREAMBLE_SIZE); +} + +void ServiceInterfaceBuffer::putChars(char const* begin, char const* end) { + char array[BUF_SIZE]; + uint32_t length = end - begin; + if (length > sizeof(array)) { + length = sizeof(array); + } + memcpy(array, begin, length); + + for(; begin != end; begin++){ + if(errStream) { + printChar(begin, true); + } + else { + printChar(begin, false); + } + } +} + +#endif int ServiceInterfaceBuffer::overflow(int c) { + if(not buffered and this->isActive) { + if (c != Traits::eof()) { + if(errStream) { + printChar(reinterpret_cast(&c), true); + } + else { + printChar(reinterpret_cast(&c), false); + } + } + return 0; + } // Handle output putChars(pbase(), pptr()); if (c != Traits::eof()) { @@ -20,52 +67,70 @@ int ServiceInterfaceBuffer::overflow(int c) { } int ServiceInterfaceBuffer::sync(void) { - if (this->isActive) { - Clock::TimeOfDay_t loggerTime; - Clock::getDateAndTime(&loggerTime); - char preamble[96] = { 0 }; - sprintf(preamble, "%s: | %lu:%02lu:%02lu.%03lu | ", - this->log_message.c_str(), (unsigned long) loggerTime.hour, - (unsigned long) loggerTime.minute, - (unsigned long) loggerTime.second, - (unsigned long) loggerTime.usecond /1000); - // Write log_message and time - this->putChars(preamble, preamble + sizeof(preamble)); - // Handle output - this->putChars(pbase(), pptr()); + if(not this->isActive and not buffered) { + if(not buffered) { + setp(buf, buf + BUF_SIZE - 1); + } + return 0; } + if(not buffered) { + return 0; + } + + size_t preambleSize = 0; + std::string* preamble = getPreamble(&preambleSize); + // Write logMessage and time + this->putChars(preamble->data(), preamble->data() + preambleSize); + // Handle output + this->putChars(pbase(), pptr()); // This tells that buffer is empty again setp(buf, buf + BUF_SIZE - 1); return 0; } -#ifndef UT699 - -ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, uint16_t port) { - this->log_message = set_message; - this->isActive = true; - setp( buf, buf + BUF_SIZE ); +bool ServiceInterfaceBuffer::isBuffered() const { + return buffered; } -void ServiceInterfaceBuffer::putChars(char const* begin, char const* end) { - char array[BUF_SIZE]; - uint32_t length = end - begin; - if (length > sizeof(array)) { - length = sizeof(array); +std::string* ServiceInterfaceBuffer::getPreamble(size_t * preambleSize) { + Clock::TimeOfDay_t loggerTime; + Clock::getDateAndTime(&loggerTime); + size_t currentSize = 0; + char* parsePosition = &preamble[0]; + if(addCrToPreamble) { + preamble[0] = '\r'; + currentSize += 1; + parsePosition += 1; } - memcpy(array, begin, length); - - for( ; begin != end; begin++){ - printChar(begin); + int32_t charCount = sprintf(parsePosition, + "%s: | %02" SCNu32 ":%02" SCNu32 ":%02" SCNu32 ".%03" SCNu32 " | ", + this->logMessage.c_str(), loggerTime.hour, + loggerTime.minute, + loggerTime.second, + loggerTime.usecond /1000); + if(charCount < 0) { + printf("ServiceInterfaceBuffer: Failure parsing preamble\r\n"); + return &preamble; } - + if(charCount > MAX_PREAMBLE_SIZE) { + printf("ServiceInterfaceBuffer: Char count too large for maximum " + "preamble size"); + return &preamble; + } + currentSize += charCount; + if(preambleSize != nullptr) { + *preambleSize = currentSize; + } + return &preamble; } -#endif + + #ifdef UT699 #include -ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, uint16_t port) { +ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string set_message, + uint16_t port) { this->log_message = set_message; this->isActive = true; setp( buf, buf + BUF_SIZE ); diff --git a/serviceinterface/ServiceInterfaceBuffer.h b/serviceinterface/ServiceInterfaceBuffer.h index b42c8a19..7a2ce2ee 100644 --- a/serviceinterface/ServiceInterfaceBuffer.h +++ b/serviceinterface/ServiceInterfaceBuffer.h @@ -1,51 +1,71 @@ #ifndef FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_ #define FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_ +#include #include -#include #include -#include +#include #ifndef UT699 -class ServiceInterfaceBuffer: public std::basic_streambuf > { + +/** + * @brief This is the underlying stream buffer which implements the + * streambuf class and overloads the overflow() and sync() methods + * @details + * This class is used to modify the output of the stream, for example by adding. + * It also calls the char printing function which is implemented in the + * board supply package (BSP). + */ +class ServiceInterfaceBuffer: + public std::streambuf { friend class ServiceInterfaceStream; public: - ServiceInterfaceBuffer(std::string set_message, uint16_t port); + static constexpr uint8_t MAX_PREAMBLE_SIZE = 40; + + ServiceInterfaceBuffer(std::string setMessage, bool addCrToPreamble, + bool buffered, bool errStream, uint16_t port); + protected: bool isActive; - // This is called when buffer becomes full. If - // buffer is not used, then this is called every - // time when characters are put to stream. - virtual int overflow(int c = Traits::eof()); + //! This is called when buffer becomes full. If + //! buffer is not used, then this is called every + //! time when characters are put to stream. + int overflow(int c = Traits::eof()) override; - // This function is called when stream is flushed, - // for example when std::endl is put to stream. - virtual int sync(void); + //! This function is called when stream is flushed, + //! for example when std::endl is put to stream. + int sync(void) override; + bool isBuffered() const; private: - // For additional message information - std::string log_message; + //! For additional message information + std::string logMessage; + std::string preamble; // For EOF detection typedef std::char_traits Traits; - // Work in buffer mode. It is also possible to work without buffer. + //! This is useful for some terminal programs which do not have + //! implicit carriage return with newline characters. + bool addCrToPreamble; + + //! Specifies whether the stream operates in buffered or unbuffered mode. + bool buffered; + //! This specifies to print to stderr and work in unbuffered mode. + bool errStream; + + //! Needed for buffered mode. static size_t const BUF_SIZE = 128; char buf[BUF_SIZE]; - // In this function, the characters are parsed. + //! In this function, the characters are parsed. void putChars(char const* begin, char const* end); + + std::string* getPreamble(size_t * preambleSize = nullptr); }; + #endif - - - - - - - #ifdef UT699 class ServiceInterfaceBuffer: public std::basic_streambuf > { diff --git a/serviceinterface/ServiceInterfaceStream.cpp b/serviceinterface/ServiceInterfaceStream.cpp index c2979f36..31bc7c73 100644 --- a/serviceinterface/ServiceInterfaceStream.cpp +++ b/serviceinterface/ServiceInterfaceStream.cpp @@ -1,11 +1,32 @@ #include +ServiceInterfaceStream::ServiceInterfaceStream(std::string setMessage, + bool addCrToPreamble, bool buffered, bool errStream, uint16_t port) : + std::ostream(&streambuf), + streambuf(setMessage, addCrToPreamble, buffered, errStream, port) {} + void ServiceInterfaceStream::setActive( bool myActive) { - this->buf.isActive = myActive; + this->streambuf.isActive = myActive; } -ServiceInterfaceStream::ServiceInterfaceStream(std::string set_message, - uint16_t port) : - std::basic_ostream >(&buf), buf( - set_message, port) { +std::string* ServiceInterfaceStream::getPreamble() { + return streambuf.getPreamble(); +} + +void ServiceInterfaceStream::print(std::string error, + bool withPreamble, bool withNewline, bool flush) { + if(not streambuf.isBuffered() and withPreamble) { + *this << getPreamble() << error; + } + else { + *this << error; + } + + if(withNewline) { + *this << "\n"; + } + // if mode is non-buffered, no need to flush. + if(flush and streambuf.isBuffered()) { + this->flush(); + } } diff --git a/serviceinterface/ServiceInterfaceStream.h b/serviceinterface/ServiceInterfaceStream.h index df736a1b..dc111459 100644 --- a/serviceinterface/ServiceInterfaceStream.h +++ b/serviceinterface/ServiceInterfaceStream.h @@ -3,28 +3,56 @@ #include #include -#include -#include #include -// Unfortunately, there must be a forward declaration of log_fe -// (MUST be defined in main), to let the system know where to write to. -namespace sif { -extern std::ostream debug; -extern std::ostream info; -extern std::ostream warning; -extern std::ostream error; -} - - -class ServiceInterfaceStream : public std::basic_ostream< char, std::char_traits< char > > { -protected: - ServiceInterfaceBuffer buf; +/** + * Generic service interface stream which can be used like std::cout or + * std::cerr but has additional capability. Add preamble and timestamp + * to output. Can be run in buffered or unbuffered mode. + */ +class ServiceInterfaceStream : public std::ostream { public: - ServiceInterfaceStream( std::string set_message, uint16_t port = 1234 ); + /** + * This constructor is used by specifying the preamble message. + * Optionally, the output can be directed to stderr and a CR character + * can be prepended to the preamble. + * @param setMessage message of preamble. + * @param addCrToPreamble Useful for applications like Puttty. + * @param buffered specify whether to use buffered mode. + * @param errStream specify which output stream to use (stderr or stdout). + */ + ServiceInterfaceStream(std::string setMessage, + bool addCrToPreamble = false, bool buffered = true, + bool errStream = false, uint16_t port = 1234); + + //! An inactive stream will not print anything. void setActive( bool ); + + /** + * This can be used to retrieve the preamble in case it should be printed in + * the unbuffered mode. + * @return Preamle consisting of log message and timestamp. + */ + std::string* getPreamble(); + + /** + * This prints an error with a preamble. Useful if using the unbuffered + * mode. Flushes in default mode (prints immediately). + */ + void print(std::string error, bool withPreamble = true, + bool withNewline = true, bool flush = true); + +protected: + ServiceInterfaceBuffer streambuf; }; - +// Forward declaration of interface streams. These should be instantiated in +// main. They can then be used like std::cout or std::cerr. +namespace sif { +extern ServiceInterfaceStream debug; +extern ServiceInterfaceStream info; +extern ServiceInterfaceStream warning; +extern ServiceInterfaceStream error; +} #endif /* FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACESTREAM_H_ */ diff --git a/tasks/PeriodicTaskIF.h b/tasks/PeriodicTaskIF.h index 304a7de6..6f490977 100644 --- a/tasks/PeriodicTaskIF.h +++ b/tasks/PeriodicTaskIF.h @@ -1,9 +1,11 @@ -#ifndef PERIODICTASKIF_H_ -#define PERIODICTASKIF_H_ +#ifndef FRAMEWORK_TASK_PERIODICTASKIF_H_ +#define FRAMEWORK_TASK_PERIODICTASKIF_H_ #include +#include #include class ExecutableObjectIF; + /** * New version of TaskIF * Follows RAII principles, i.e. there's no create or delete method. @@ -17,11 +19,27 @@ public: */ virtual ~PeriodicTaskIF() { } /** - * @brief With the startTask method, a created task can be started for the first time. + * @brief With the startTask method, a created task can be started + * for the first time. */ virtual ReturnValue_t startTask() = 0; - virtual ReturnValue_t addComponent(object_id_t object) {return HasReturnvaluesIF::RETURN_FAILED;}; + /** + * Add a component (object) to a periodic task. The pointer to the + * task can be set optionally + * @param object + * Add an object to the task. The most important case is to add an + * executable object with a function which will be called regularly + * (see ExecutableObjectIF) + * @param setTaskIF + * Can be used to specify whether the task object pointer is passed + * to the component. + * @return + */ + virtual ReturnValue_t addComponent(object_id_t object, + bool setTaskIF = true) { + return HasReturnvaluesIF::RETURN_FAILED; + }; virtual ReturnValue_t sleepFor(uint32_t ms) = 0; diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index 32032716..71b2539d 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -1,5 +1,6 @@ #include #include +#include #include CCSDSTime::CCSDSTime() { @@ -154,15 +155,17 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* if (length < 19) { return RETURN_FAILED; } +// Newlib nano can't parse uint8, see SCNu8 documentation and https://sourceware.org/newlib/README +// Suggestion: use uint16 all the time. This should work on all systems. +#ifdef NEWLIB_NANO_NO_C99_IO uint16_t year; - uint8_t month; + uint16_t month; uint16_t day; - uint8_t hour; - uint8_t minute; + uint16_t hour; + uint16_t minute; float second; -//try Code A (yyyy-mm-dd) - int count = sscanf((char *) from, "%4hi-%2hhi-%2hiT%2hhi:%2hhi:%fZ", &year, - &month, &day, &hour, &minute, &second); + int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, + &month, &day, &hour, &minute, &second); if (count == 6) { to->year = year; to->month = month; @@ -174,9 +177,52 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* return RETURN_OK; } - //try Code B (yyyy-ddd) - count = sscanf((char *) from, "%4hi-%3hiT%2hhi:%2hhi:%fZ", &year, &day, + // try Code B (yyyy-ddd) + count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &day, &hour, &minute, &second); + if (count == 5) { + uint8_t tempDay; + ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, + reinterpret_cast(&month), reinterpret_cast(&tempDay)); + if (result != RETURN_OK) { + return RETURN_FAILED; + } + to->year = year; + to->month = month; + to->day = tempDay; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } +// Warning: Compiler/Linker fails ambiguously if library does not implement +// C99 I/O +#else + uint16_t year; + uint8_t month; + uint16_t day; + uint8_t hour; + uint8_t minute; + float second; + //try Code A (yyyy-mm-dd) + int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu8 "-%2" SCNu16 + "T%2" SCNu8 ":%2" SCNu8 ":%fZ", &year, &month, &day, + &hour, &minute, &second); + if (count == 6) { + to->year = year; + to->month = month; + to->day = day; + to->hour = hour; + to->minute = minute; + to->second = second; + to->usecond = (second - floor(second)) * 1000000; + return RETURN_OK; + } + + //try Code B (yyyy-ddd) + count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu8 + ":%2" SCNu8 ":%fZ", &year, &day, &hour, &minute, &second); if (count == 5) { uint8_t tempDay; ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, &month, @@ -193,6 +239,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t* to->usecond = (second - floor(second)) * 1000000; return RETURN_OK; } +#endif return UNSUPPORTED_TIME_FORMAT; } diff --git a/tmtcservices/CommandingServiceBase.cpp b/tmtcservices/CommandingServiceBase.cpp index 4c9b5375..98cbbe2c 100644 --- a/tmtcservices/CommandingServiceBase.cpp +++ b/tmtcservices/CommandingServiceBase.cpp @@ -230,8 +230,8 @@ void CommandingServiceBase::handleRequestQueue() { address = message.getStorageId(); packet.setStoreAddress(address); - if (packet.getSubService() == 0 - or isValidSubservice(packet.getSubService()) != RETURN_OK) { + if ((packet.getSubService() == 0) + or (isValidSubservice(packet.getSubService()) != RETURN_OK)) { rejectPacket(TC_VERIFY::START_FAILURE, &packet, INVALID_SUBSERVICE); continue; } diff --git a/tmtcservices/CommandingServiceBase.h b/tmtcservices/CommandingServiceBase.h index 1bd18571..76eca6b7 100644 --- a/tmtcservices/CommandingServiceBase.h +++ b/tmtcservices/CommandingServiceBase.h @@ -116,7 +116,7 @@ public: * Used to setup the reference of the task, that executes this component * @param task Pointer to the taskIF of this task */ - virtual void setTaskIF(PeriodicTaskIF* task); + virtual void setTaskIF(PeriodicTaskIF* task) override; protected: /** @@ -173,9 +173,7 @@ protected: * This function is implemented by child services to specify how replies * to a command from another software component are handled. * @param reply - * This is the reply which can be accessed via the command message - * interface. The internal message pointer can be passed to different - * command message implementations (see CommandMessageIF) + * This is the reply in form of a generic read-only command message. * @param previousCommand * Command_t of related command * @param state [out/in] @@ -188,11 +186,12 @@ protected: * @return * - @c RETURN_OK, @c EXECUTION_COMPLETE or @c NO_STEP_MESSAGE to * generate TC verification success - * - @c INVALID_REPLY Calls handleUnrequestedReply - * - Anything else triggers a TC verification failure. If RETURN_FAILED - * is returned and the command ID is CommandMessage::REPLY_REJECTED, - * a failure verification message with the reason as the error parameter - * and the initial command as failure parameter 1. + * - @c INVALID_REPLY calls handleUnrequestedReply + * - Anything else triggers a TC verification failure. If RETURN_FAILED or + * INVALID_REPLY is returned and the command ID is + * CommandMessage::REPLY_REJECTED, a failure verification message + * with the reason as the error parameter and the initial command as + * failure parameter 1 is generated. */ virtual ReturnValue_t handleReply(const CommandMessage* reply, Command_t previousCommand, uint32_t *state, @@ -255,6 +254,7 @@ protected: uint32_t failureParameter1 = 0; uint32_t failureParameter2 = 0; + object_id_t packetSource; static object_id_t defaultPacketSource; object_id_t packetSource = objects::NO_OBJECT; static object_id_t defaultPacketDestination; @@ -303,6 +303,7 @@ protected: void checkAndExecuteFifo(CommandMapIter iter); private: + /** * This method handles internal execution of a command, * once it has been started by @sa{startExecution()} in the request diff --git a/tmtcservices/PusServiceBase.cpp b/tmtcservices/PusServiceBase.cpp index b1d97293..82e5ff5c 100644 --- a/tmtcservices/PusServiceBase.cpp +++ b/tmtcservices/PusServiceBase.cpp @@ -9,10 +9,11 @@ object_id_t PusServiceBase::packetSource = 0; object_id_t PusServiceBase::packetDestination = 0; -PusServiceBase::PusServiceBase(object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId) : - SystemObject(setObjectId), apid(setApid), serviceId(setServiceId), errorParameter1( - 0), errorParameter2(0), requestQueue(NULL) { - requestQueue = QueueFactory::instance()->createMessageQueue(PUS_SERVICE_MAX_RECEPTION); +PusServiceBase::PusServiceBase(object_id_t setObjectId, uint16_t setApid, + uint8_t setServiceId) : + SystemObject(setObjectId), apid(setApid), serviceId(setServiceId) { + requestQueue = QueueFactory::instance()-> + createMessageQueue(PUS_SERVICE_MAX_RECEPTION); } PusServiceBase::~PusServiceBase() { @@ -20,50 +21,64 @@ PusServiceBase::~PusServiceBase() { } ReturnValue_t PusServiceBase::performOperation(uint8_t opCode) { + handleRequestQueue(); + ReturnValue_t result = this->performService(); + if (result != RETURN_OK) { + sif::error << "PusService " << (uint16_t) this->serviceId + << ": performService returned with " << (int16_t) result + << std::endl; + return RETURN_FAILED; + } + return RETURN_OK; +} + +void PusServiceBase::setTaskIF(PeriodicTaskIF* taskHandle) { + this->taskHandle = taskHandle; +} + +void PusServiceBase::handleRequestQueue() { TmTcMessage message; + ReturnValue_t result = RETURN_FAILED; for (uint8_t count = 0; count < PUS_SERVICE_MAX_RECEPTION; count++) { ReturnValue_t status = this->requestQueue->receiveMessage(&message); - // debug << "PusServiceBase::performOperation: Receiving from MQ ID: " << std::hex << this->requestQueue.getId() - // << std::dec << " returned: " << status << std::endl; + // debug << "PusServiceBase::performOperation: Receiving from MQ ID: " + // << std::hex << this->requestQueue.getId() + // << std::dec << " returned: " << status << std::endl; if (status == RETURN_OK) { this->currentPacket.setStoreAddress(message.getStorageId()); - // info << "Service " << (uint16_t) this->serviceId << ": new packet!" << std::endl; + //info << "Service " << (uint16_t) this->serviceId << + // ": new packet!" << std::endl; - ReturnValue_t return_code = this->handleRequest(); - // debug << "Service " << (uint16_t)this->serviceId << ": handleRequest returned: " << (int)return_code << std::endl; - if (return_code == RETURN_OK) { + result = this->handleRequest(currentPacket.getSubService()); + + // debug << "Service " << (uint16_t)this->serviceId << + // ": handleRequest returned: " << (int)return_code << std::endl; + if (result == RETURN_OK) { this->verifyReporter.sendSuccessReport( TC_VERIFY::COMPLETION_SUCCESS, &this->currentPacket); - } else { + } + else { this->verifyReporter.sendFailureReport( TC_VERIFY::COMPLETION_FAILURE, &this->currentPacket, - return_code, 0, errorParameter1, errorParameter2); + result, 0, errorParameter1, errorParameter2); } this->currentPacket.deletePacket(); errorParameter1 = 0; errorParameter2 = 0; - } else if (status == MessageQueueIF::EMPTY) { + } + else if (status == MessageQueueIF::EMPTY) { status = RETURN_OK; - // debug << "PusService " << (uint16_t)this->serviceId << ": no new packet." << std::endl; + // debug << "PusService " << (uint16_t)this->serviceId << + // ": no new packet." << std::endl; break; - } else { - + } + else { sif::error << "PusServiceBase::performOperation: Service " << (uint16_t) this->serviceId << ": Error receiving packet. Code: " << std::hex << status << std::dec << std::endl; } } - ReturnValue_t return_code = this->performService(); - if (return_code == RETURN_OK) { - return RETURN_OK; - } else { - - sif::error << "PusService " << (uint16_t) this->serviceId - << ": performService returned with " << (int16_t) return_code - << std::endl; - return RETURN_FAILED; - } } uint16_t PusServiceBase::getIdentifier() { @@ -79,19 +94,28 @@ ReturnValue_t PusServiceBase::initialize() { if (result != RETURN_OK) { return result; } - AcceptsTelemetryIF* dest_service = objectManager->get( + AcceptsTelemetryIF* destService = objectManager->get( packetDestination); PUSDistributorIF* distributor = objectManager->get( packetSource); - if ((dest_service != NULL) && (distributor != NULL)) { + if ((destService != nullptr) && (distributor != nullptr)) { this->requestQueue->setDefaultDestination( - dest_service->getReportReceptionQueue()); + destService->getReportReceptionQueue()); distributor->registerService(this); return RETURN_OK; - } else { + } + else { sif::error << "PusServiceBase::PusServiceBase: Service " << (uint32_t) this->serviceId << ": Configuration error." - << " Make sure packetSource and packetDestination are defined correctly" << std::endl; + << " Make sure packetSource and packetDestination are defined " + "correctly" << std::endl; return RETURN_FAILED; } } + +ReturnValue_t PusServiceBase::initializeAfterTaskCreation() { + // If task parameters, for example task frequency are required, this + // function should be overriden and the system object task IF can + // be used to get those parameters. + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/tmtcservices/PusServiceBase.h b/tmtcservices/PusServiceBase.h index d318ff1e..6d3d9bac 100644 --- a/tmtcservices/PusServiceBase.h +++ b/tmtcservices/PusServiceBase.h @@ -1,5 +1,5 @@ -#ifndef PUSSERVICEBASE_H_ -#define PUSSERVICEBASE_H_ +#ifndef FRAMEWORK_TMTCSERVICES_PUSSERVICEBASE_H_ +#define FRAMEWORK_TMTCSERVICES_PUSSERVICEBASE_H_ #include #include @@ -9,7 +9,6 @@ #include #include #include -#include #include namespace Factory{ @@ -17,72 +16,100 @@ void setStaticFrameworkObjectIds(); } /** - * \defgroup pus_services PUS Service Framework + * @defgroup pus_services PUS Service Framework * These group contains all implementations of PUS Services in the OBSW. * Most of the Services are directly taken from the ECSS PUS Standard. */ /** - * \brief This class is the basis for all PUS Services, which can immediately process Telecommand Packets. - * It manages Telecommand reception and the generation of Verification Reports. Every class that inherits - * from this abstract class has to implement handleRequest and performService. Services that are created with this + * @brief This class is the basis for all PUS Services, + * which can immediately process Telecommand Packets. + * @details + * It manages Telecommand reception and the generation of Verification Reports. + * Every class that inherits from this abstract class has to implement + * handleRequest and performService. Services that are created with this * Base class have to handle any kind of request immediately on reception. - * All PUS Services are System Objects, so an Object ID needs to be specified on construction. - * \ingroup pus_services + * All PUS Services are System Objects, so an Object ID needs to be specified + * on construction. + * @ingroup pus_services */ -class PusServiceBase : public ExecutableObjectIF, public AcceptsTelecommandsIF, public SystemObject, public HasReturnvaluesIF { +class PusServiceBase : public ExecutableObjectIF, + public AcceptsTelecommandsIF, + public SystemObject, + public HasReturnvaluesIF { friend void (Factory::setStaticFrameworkObjectIds)(); public: /** - * The constructor for the class. - * The passed values are set, but inter-object initialization is done in the initialize method. - * @param setObjectId The system object identifier of this Service instance. - * @param set_apid The APID the Service is instantiated for. - * @param set_service_id The Service Identifier as specified in ECSS PUS. + * @brief The passed values are set, but inter-object initialization is + * done in the initialize method. + * @param setObjectId + * The system object identifier of this Service instance. + * @param setApid + * The APID the Service is instantiated for. + * @param setServiceId + * The Service Identifier as specified in ECSS PUS. */ - PusServiceBase( object_id_t setObjectId, uint16_t setApid, uint8_t setServiceId); + PusServiceBase( object_id_t setObjectId, uint16_t setApid, + uint8_t setServiceId); /** * The destructor is empty. */ virtual ~PusServiceBase(); /** - * @brief The handleRequest method shall handle any kind of Telecommand Request immediately. + * @brief The handleRequest method shall handle any kind of Telecommand + * Request immediately. * @details - * Implemetations can take the Telecommand in currentPacket and perform any kind of operation. - * They may send additional "Start Success (1,3)" messages with the verifyReporter, but Completion - * Success or Failure Reports are generated automatically after execution of this method. + * Implemetations can take the Telecommand in currentPacket and perform + * any kind of operation. + * They may send additional "Start Success (1,3)" messages with the + * verifyReporter, but Completion Success or Failure Reports are generated + * automatically after execution of this method. * * If a Telecommand can not be executed within one call cycle, * this Base class is not the right parent. * - * The child class may add additional error information by setting #errorParameters which are - * attached to the generated verification message. + * The child class may add additional error information by setting + * #errorParameters which aren attached to the generated verification + * message. * * Subservice checking should be implemented in this method. * - * @return The returned status_code is directly taken as main error code in the Verification Report. + * @return The returned status_code is directly taken as main error code + * in the Verification Report. * On success, RETURN_OK shall be returned. */ - virtual ReturnValue_t handleRequest() = 0; + virtual ReturnValue_t handleRequest(uint8_t subservice) = 0; /** - * In performService, implementations can handle periodic, non-TC-triggered activities. + * In performService, implementations can handle periodic, + * non-TC-triggered activities. * The performService method is always called. - * @return A success or failure code that does not trigger any kind of verification message. + * @return Currently, everything other that RETURN_OK only triggers + * diagnostic output. */ virtual ReturnValue_t performService() = 0; /** * This method implements the typical activity of a simple PUS Service. - * It checks for new requests, and, if found, calls handleRequest, sends completion verification messages and deletes + * It checks for new requests, and, if found, calls handleRequest, sends + * completion verification messages and deletes * the TC requests afterwards. * performService is always executed afterwards. - * @return \c RETURN_OK if the periodic performService was successful. - * \c RETURN_FAILED else. + * @return @c RETURN_OK if the periodic performService was successful. + * @c RETURN_FAILED else. */ - ReturnValue_t performOperation(uint8_t opCode); - virtual uint16_t getIdentifier(); - MessageQueueId_t getRequestQueue(); - virtual ReturnValue_t initialize(); + ReturnValue_t performOperation(uint8_t opCode) override; + virtual uint16_t getIdentifier() override; + MessageQueueId_t getRequestQueue() override; + virtual ReturnValue_t initialize() override; + + virtual void setTaskIF(PeriodicTaskIF* taskHandle) override; + virtual ReturnValue_t initializeAfterTaskCreation() override; protected: + /** + * @brief Handle to the underlying task + * @details + * Will be set by setTaskIF(), which is called on task creation. + */ + PeriodicTaskIF* taskHandle = nullptr; /** * The APID of this instance of the Service. */ @@ -94,19 +121,19 @@ protected: /** * One of two error parameters for additional error information. */ - uint32_t errorParameter1; + uint32_t errorParameter1 = 0; /** * One of two error parameters for additional error information. */ - uint32_t errorParameter2; + uint32_t errorParameter2 = 0; /** - * This is a complete instance of the Telecommand reception queue of the class. - * It is initialized on construction of the class. + * This is a complete instance of the telecommand reception queue + * of the class. It is initialized on construction of the class. */ - MessageQueueIF* requestQueue; + MessageQueueIF* requestQueue = nullptr; /** - * An instance of the VerificationReporter class, that simplifies sending any kind of - * Verification Message to the TC Verification Service. + * An instance of the VerificationReporter class, that simplifies + * sending any kind of verification message to the TC Verification Service. */ VerificationReporter verifyReporter; /** @@ -121,9 +148,12 @@ protected: private: /** * This constant sets the maximum number of packets accepted per call. - * Remember that one packet must be completely handled in one #handleRequest call. + * Remember that one packet must be completely handled in one + * #handleRequest call. */ static const uint8_t PUS_SERVICE_MAX_RECEPTION = 10; + + void handleRequestQueue(); }; #endif /* PUSSERVICEBASE_H_ */