PUS: PUS Services #139

Merged
gaisser merged 7 commits from KSat/fsfw:mueller_PusServices into master 2020-08-12 10:07:37 +02:00
31 changed files with 1148 additions and 693 deletions
Showing only changes of commit be6060ec94 - Show all commits

View File

@ -1,5 +1,5 @@
#ifndef HYBRIDITERATOR_H_
#define HYBRIDITERATOR_H_
#ifndef FRAMEWORK_CONTAINER_HYBRIDITERATOR_H_
#define FRAMEWORK_CONTAINER_HYBRIDITERATOR_H_
#include <framework/container/ArrayList.h>
#include <framework/container/SinglyLinkedList.h>
@ -8,34 +8,32 @@ template<typename T, typename count_t = uint8_t>
class HybridIterator: public LinkedElement<T>::Iterator,
public ArrayList<T, count_t>::Iterator {
public:
HybridIterator() :
value(NULL), linked(NULL), end(NULL) {
}
HybridIterator() {}
HybridIterator(typename LinkedElement<T>::Iterator *iter) :
LinkedElement<T>::Iterator(*iter), value(
iter->value), linked(true), end(NULL) {
LinkedElement<T>::Iterator(*iter), value(iter->value),
linked(true) {
}
HybridIterator(LinkedElement<T> *start) :
LinkedElement<T>::Iterator(start), value(
start->value), linked(true), end(NULL) {
LinkedElement<T>::Iterator(start), value(start->value),
linked(true) {
}
HybridIterator(typename ArrayList<T, count_t>::Iterator start,
typename ArrayList<T, count_t>::Iterator end) :
ArrayList<T, count_t>::Iterator(start), value(start.value), linked(
false), end(end.value) {
ArrayList<T, count_t>::Iterator(start), value(start.value),
linked(false), end(end.value) {
if (value == this->end) {
value = NULL;
}
}
HybridIterator(T *firstElement, T *lastElement) :
ArrayList<T, count_t>::Iterator(firstElement), value(firstElement), linked(
false), end(++lastElement) {
ArrayList<T, count_t>::Iterator(firstElement), value(firstElement),
linked(false), end(++lastElement) {
if (value == end) {
value = NULL;
}
@ -44,17 +42,17 @@ public:
HybridIterator& operator++() {
if (linked) {
LinkedElement<T>::Iterator::operator++();
if (LinkedElement<T>::Iterator::value != NULL) {
if (LinkedElement<T>::Iterator::value != nullptr) {
value = LinkedElement<T>::Iterator::value->value;
} else {
value = NULL;
value = nullptr;
}
} else {
ArrayList<T, count_t>::Iterator::operator++();
value = ArrayList<T, count_t>::Iterator::value;
if (value == end) {
value = NULL;
value = nullptr;
}
}
return *this;
@ -66,11 +64,11 @@ public:
return tmp;
}
bool operator==(HybridIterator other) {
return value == other->value;
bool operator==(const HybridIterator& other) const {
return value == other.value;
}
bool operator!=(HybridIterator other) {
bool operator!=(const HybridIterator& other) const {
return !(*this == other);
}
@ -82,11 +80,11 @@ public:
return value;
}
T* value;
T* value = nullptr;
private:
bool linked;
T *end;
bool linked = false;
T *end = nullptr;
};
#endif /* HYBRIDITERATOR_H_ */
#endif /* FRAMEWORK_CONTAINER_HYBRIDITERATOR_H_ */

View File

@ -35,7 +35,7 @@ ReturnValue_t ChildHandlerBase::initialize() {
parent->registerChild(getObjectId());
}
healthHelper.setParentQeueue(parentQueue);
healthHelper.setParentQueue(parentQueue);
modeHelper.setParentQueue(parentQueue);

View File

@ -1085,7 +1085,7 @@ ReturnValue_t DeviceHandlerBase::handleDeviceHandlerMessage(
void DeviceHandlerBase::setParentQueue(MessageQueueId_t parentQueueId) {
modeHelper.setParentQueue(parentQueueId);
healthHelper.setParentQeueue(parentQueueId);
healthHelper.setParentQueue(parentQueueId);
}
bool DeviceHandlerBase::isAwaitingReply() {

View File

@ -38,7 +38,7 @@ MessageQueueId_t HealthDevice::getCommandQueue() const {
}
void HealthDevice::setParentQueue(MessageQueueId_t parentQueue) {
healthHelper.setParentQeueue(parentQueue);
healthHelper.setParentQueue(parentQueue);
}
bool HealthDevice::hasHealthChanged() {

View File

@ -28,13 +28,13 @@ 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),host)
else ifeq ($(OS_FSFW),host)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp)
else
$(error invalid OS specified, valid OS are rtems, linux, freeRTOS, host)

View File

@ -29,11 +29,11 @@ HasHealthIF::HealthState HealthHelper::getHealth() {
}
ReturnValue_t HealthHelper::initialize(MessageQueueId_t parentQueue) {
setParentQeueue(parentQueue);
setParentQueue(parentQueue);
return initialize();
}
void HealthHelper::setParentQeueue(MessageQueueId_t parentQueue) {
void HealthHelper::setParentQueue(MessageQueueId_t parentQueue) {
this->parentQueue = parentQueue;
}

View File

@ -78,7 +78,7 @@ public:
/**
* @param parentQueue the Queue id of the parent object. Set to 0 if no parent present
*/
void setParentQeueue(MessageQueueId_t parentQueue);
void setParentQueue(MessageQueueId_t parentQueue);
/**
*

View File

@ -17,7 +17,7 @@ public:
HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0;
virtual uint32_t getPrintSize() = 0;
virtual void printAll(uint8_t *pointer, uint32_t maxSize) = 0;
virtual void printAll(uint8_t *pointer, size_t maxSize) = 0;
protected:
virtual ReturnValue_t iterate(std::pair<object_id_t,HasHealthIF::HealthState> *value, bool reset = false) = 0;

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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:

View File

@ -1,6 +1,7 @@
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include "FixedTimeslotTask.h"
#include <framework/serviceinterface/ServiceInterfaceStream.h>
uint32_t FixedTimeslotTask::deadlineMissedCount = 0;
const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = configMINIMAL_STACK_SIZE;
@ -18,16 +19,19 @@ FixedTimeslotTask::~FixedTimeslotTask() {
void FixedTimeslotTask::taskEntryPoint(void* argument) {
//The argument is re-interpreted as FixedTimeslotTask. The Task object is global, so it is found from any place.
// The argument is re-interpreted as FixedTimeslotTask. The Task object is
// global, so it is found from any place.
FixedTimeslotTask *originalTask(reinterpret_cast<FixedTimeslotTask*>(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);
}
@ -58,11 +62,6 @@ ReturnValue_t FixedTimeslotTask::startTask() {
ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId,
uint32_t slotTimeMs, int8_t executionStep) {
if (objectManager->get<ExecutableObjectIF>(componentId) != nullptr) {
if(slotTimeMs == 0) {
// FreeRTOS throws a sanity error for zero values, so we set
// the time to one millisecond.
slotTimeMs = 1;
}
pst.addSlot(componentId, slotTimeMs, executionStep, this);
return HasReturnvaluesIF::RETURN_OK;
}
@ -81,8 +80,9 @@ ReturnValue_t FixedTimeslotTask::checkSequence() const {
}
void FixedTimeslotTask::taskFunctionality() {
// A local iterator for the Polling Sequence Table is created to find the start time for the first entry.
auto slotListIter = pst.current;
// A local iterator for the Polling Sequence Table is created to find the
// start time for the first entry.
FixedSlotSequence::SlotListIter slotListIter = pst.current;
//The start time for the first entry is read.
uint32_t intervalMs = slotListIter->pollingTimeMs;
@ -90,32 +90,67 @@ void FixedTimeslotTask::taskFunctionality() {
TickType_t xLastWakeTime;
/* 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();
// wait for first entry's start time
vTaskDelayUntil(&xLastWakeTime, interval);
if(interval > 0) {
vTaskDelayUntil(&xLastWakeTime, interval);
}
/* Enter the loop that defines the task behavior. */
for (;;) {
//The component for this slot is executed and the next one is chosen.
this->pst.executeAndAdvance();
if (pst.slotFollowsImmediately()) {
//Do nothing
} else {
// we need to wait before executing the current slot
//this gives us the time to wait:
intervalMs = this->pst.getIntervalToPreviousSlotMs();
interval = pdMS_TO_TICKS(intervalMs);
vTaskDelayUntil(&xLastWakeTime, interval);
//TODO deadline missed check
}
this->pst.executeAndAdvance();
if (not pst.slotFollowsImmediately()) {
// Get the interval till execution of the next slot.
intervalMs = this->pst.getIntervalToPreviousSlotMs();
interval = pdMS_TO_TICKS(intervalMs);
checkMissedDeadline(xLastWakeTime, interval);
// Wait for the interval. This exits immediately if a deadline was
// missed while also updating the last wake time.
vTaskDelayUntil(&xLastWakeTime, interval);
}
}
}
void FixedTimeslotTask::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 FixedTimeslotTask::handleMissedDeadline() {
#ifdef DEBUG
sif::warning << "FixedTimeslotTask: " << pcTaskGetName(NULL) <<
" missed deadline!\n" << std::flush;
#endif
if(deadlineMissedFunc != nullptr) {
this->deadlineMissedFunc();
}
}
ReturnValue_t FixedTimeslotTask::sleepFor(uint32_t ms) {
vTaskDelay(pdMS_TO_TICKS(ms));
return HasReturnvaluesIF::RETURN_OK;

View File

@ -1,26 +1,27 @@
#ifndef POLLINGTASK_H_
#define POLLINGTASK_H_
#ifndef FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_
#define FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_
#include <framework/devicehandlers/FixedSlotSequence.h>
#include <framework/tasks/FixedTimeslotTaskIF.h>
#include <framework/tasks/Typedef.h>
extern "C" {
#include "FreeRTOS.h"
#include "task.h"
}
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
class FixedTimeslotTask: public FixedTimeslotTaskIF {
public:
/**
* @brief The standard constructor of the class.
*
* @details This is the general constructor of the class. In addition to the TaskBase parameters,
* the following variables are passed:
*
* @param (*setDeadlineMissedFunc)() The function pointer to the deadline missed function that shall be assigned.
*
* @param getPst The object id of the completely initialized polling sequence.
* Keep in mind that you need to call before vTaskStartScheduler()!
* A lot of task parameters are set in "FreeRTOSConfig.h".
* @param name Name of the task, lenght limited by configMAX_TASK_NAME_LEN
* @param setPriority Number of priorities specified by
* configMAX_PRIORITIES. High taskPriority_ number means high priority.
* @param setStack Stack size in words (not bytes!).
* Lower limit specified by configMINIMAL_STACK_SIZE
* @param overallPeriod Period in seconds.
* @param setDeadlineMissedFunc Callback if a deadline was missed.
* @return Pointer to the newly created task.
*/
FixedTimeslotTask(const char *name, TaskPriority setPriority,
TaskStackSize setStack, TaskPeriod overallPeriod,
@ -28,16 +29,18 @@ public:
/**
* @brief The destructor of the class.
*
* @details The destructor frees all heap memory that was allocated on thread initialization for the PST and
* the device handlers. This is done by calling the PST's destructor.
* @details
* The destructor frees all heap memory that was allocated on thread
* initialization for the PST and the device handlers. This is done by
* calling the PST's destructor.
*/
virtual ~FixedTimeslotTask(void);
ReturnValue_t startTask(void);
/**
* This static function can be used as #deadlineMissedFunc.
* It counts missedDeadlines and prints the number of missed deadlines every 10th time.
* It counts missedDeadlines and prints the number of missed deadlines
* every 10th time.
*/
static void missedDeadlineCounter();
/**
@ -46,13 +49,14 @@ public:
static uint32_t deadlineMissedCount;
ReturnValue_t addSlot(object_id_t componentId, uint32_t slotTimeMs,
int8_t executionStep);
int8_t executionStep) override;
uint32_t getPeriodMs() const;
uint32_t getPeriodMs() const override;
ReturnValue_t checkSequence() const;
ReturnValue_t checkSequence() const override;
ReturnValue_t sleepFor(uint32_t ms) override;
ReturnValue_t sleepFor(uint32_t ms);
protected:
bool started;
TaskHandle_t handle;
@ -60,32 +64,35 @@ protected:
FixedSlotSequence pst;
/**
* @brief This attribute holds a function pointer that is executed when a deadline was missed.
*
* @details Another function may be announced to determine the actions to perform when a deadline was missed.
* Currently, only one function for missing any deadline is allowed.
* If not used, it shall be declared NULL.
* @brief This attribute holds a function pointer that is executed when
* a deadline was missed.
* @details
* Another function may be announced to determine the actions to perform
* when a deadline was missed. Currently, only one function for missing
* any deadline is allowed. If not used, it shall be declared NULL.
*/
void (*deadlineMissedFunc)(void);
/**
* @brief This is the entry point in a new polling thread.
*
* @details This method, that is the generalOSAL::checkAndRestartPeriod( this->periodId, interval ); entry point in the new thread, is here set to generate
* and link the Polling Sequence Table to the thread object and start taskFunctionality()
* on success. If operation of the task is ended for some reason,
* the destructor is called to free allocated memory.
* @brief This is the entry point for a new task.
* @details
* This method starts the task by calling taskFunctionality(), as soon as
* all requirements (task scheduler has started and startTask()
* has been called) are met.
*/
static void taskEntryPoint(void* argument);
/**
* @brief This function holds the main functionality of the thread.
*
*
* @details Holding the main functionality of the task, this method is most important.
* It links the functionalities provided by FixedSlotSequence with the OS's System Calls
* to keep the timing of the periods.
* @details
* Core function holding the main functionality of the task
* It links the functionalities provided by FixedSlotSequence with the
* OS's System Calls to keep the timing of the periods.
*/
void taskFunctionality(void);
void checkMissedDeadline(const TickType_t xLastWakeTime,
const TickType_t interval);
void handleMissedDeadline();
};
#endif /* POLLINGTASK_H_ */
#endif /* FRAMEWORK_OSAL_FREERTOS_FIXEDTIMESLOTTASK_H_ */

View File

@ -1,17 +1,19 @@
#include "PeriodicTask.h"
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include <framework/tasks/ExecutableObjectIF.h>
#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<PeriodicTask*>(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<ExecutableObjectIF>(
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();
}
}

View File

@ -1,48 +1,49 @@
#ifndef MULTIOBJECTTASK_H_
#define MULTIOBJECTTASK_H_
#ifndef FRAMEWORK_OSAL_FREERTOS_PERIODICTASK_H_
#define FRAMEWORK_OSAL_FREERTOS_PERIODICTASK_H_
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/tasks/PeriodicTaskIF.h>
#include <framework/tasks/Typedef.h>
#include <FreeRTOS.h>
#include "task.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <vector>
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<ExecutableObjectIF*> ObjectList; //!< Typedef for the List of objects.
//! Typedef for the List of objects.
typedef std::vector<ExecutableObjectIF*> 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_ */

View File

@ -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<ExecutableObjectIF>(
object);
if (newObject == NULL) {
if (newObject == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
objectList.push_back(newObject);
if(setTaskIF) {
newObject->setTaskIF(this);
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -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<ExecutableObjectIF*> ObjectList; //!< Typedef for the List of objects.

View File

@ -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 <framework/container/ArrayList.h>
#include <framework/serialize/SerializeIF.h>
#include <utility>
/**
* \ingroup serialize
* @ingroup serialize
* @author baetz
*/
template<typename T, typename count_t = uint8_t>
class SerialArrayListAdapter : public SerializeIF {
@ -25,14 +20,15 @@ public:
return serialize(adaptee, buffer, size, maxSize, streamEndianness);
}
static ReturnValue_t serialize(const ArrayList<T, count_t>* list, uint8_t** buffer, size_t* size,
size_t maxSize, Endianness streamEndianness) {
static ReturnValue_t serialize(const ArrayList<T, count_t>* 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<T, count_t>* list, const uint8_t** buffer, size_t* size,
static ReturnValue_t deSerialize(ArrayList<T, count_t>* 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_ */

View File

@ -1,11 +1,58 @@
#include <framework/timemanager/Clock.h>
#include <framework/serviceinterface/ServiceInterfaceBuffer.h>
#include <cstring>
#include <inttypes.h>
// 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<const char*>(&c), true);
}
else {
printChar(reinterpret_cast<const char*>(&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 <framework/osal/rtems/Interrupt.h>
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 );

View File

@ -1,51 +1,71 @@
#ifndef FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_
#define FRAMEWORK_SERVICEINTERFACE_SERVICEINTERFACEBUFFER_H_
#include <framework/returnvalues/HasReturnvaluesIF.h>
#include <iostream>
#include <iosfwd>
#include <sstream>
#include <cstdio>
#include <iomanip>
#ifndef UT699
class ServiceInterfaceBuffer: public std::basic_streambuf<char,
std::char_traits<char> > {
/**
* @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<char> 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<char,
std::char_traits<char> > {

View File

@ -1,11 +1,32 @@
#include <framework/serviceinterface/ServiceInterfaceStream.h>
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<char, std::char_traits<char> >(&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();
}
}

View File

@ -3,28 +3,56 @@
#include <framework/serviceinterface/ServiceInterfaceBuffer.h>
#include <iostream>
#include <iosfwd>
#include <sstream>
#include <cstdio>
// 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_ */

View File

@ -1,15 +1,5 @@
/**
* @file ExecutableObjectIF.h
*
* @brief This file contains the definition for the ExecutableObjectIF interface.
*
* @author Bastian Baetz
*
* @date 12.03.2012
*/
#ifndef EXECUTABLEOBJECTIF_H_
#define EXECUTABLEOBJECTIF_H_
#ifndef FRAMEWORK_TASKS_EXECUTABLEOBJECTIF_H_
#define FRAMEWORK_TASKS_EXECUTABLEOBJECTIF_H_
class PeriodicTaskIF;
@ -20,6 +10,7 @@ class PeriodicTaskIF;
* @brief The interface provides a method to execute objects within a task.
* @details The performOperation method, that is required by the interface is
* executed cyclically within a task context.
* @author Bastian Baetz
*/
class ExecutableObjectIF {
public:
@ -37,13 +28,26 @@ public:
/**
* @brief Function called during setup assignment of object to task
* @details Has to be called from the function that assigns the object to a task and
* enables the object implementation to overwrite this function and get a reference to the executing task
* @details
* Has to be called from the function that assigns the object to a task and
* enables the object implementation to overwrite this function and get
* a reference to the executing task
* @param task_ Pointer to the taskIF of this task
*/
virtual void setTaskIF(PeriodicTaskIF* task_) {
virtual void setTaskIF(PeriodicTaskIF* task_) {};
/**
* This function should be called after the object was assigned to a
* specific task.
*
* Example: Can be used to get task execution frequency.
* The task is created after initialize() and the object ctors have been
* called so the execution frequency can't be cached in initialize()
* @return
*/
virtual ReturnValue_t initializeAfterTaskCreation() {
return HasReturnvaluesIF::RETURN_OK;
}
};
#endif /* EXECUTABLEOBJECTIF_H_ */
#endif /* FRAMEWORK_TASKS_EXECUTABLEOBJECTIF_H_ */

View File

@ -1,9 +1,11 @@
#ifndef PERIODICTASKIF_H_
#define PERIODICTASKIF_H_
#ifndef FRAMEWORK_TASK_PERIODICTASKIF_H_
#define FRAMEWORK_TASK_PERIODICTASKIF_H_
#include <framework/objectmanager/SystemObjectIF.h>
#include <framework/timemanager/Clock.h>
#include <cstddef>
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;

View File

@ -1,5 +1,6 @@
#include <framework/timemanager/CCSDSTime.h>
#include <stdio.h>
#include <inttypes.h>
#include <math.h>
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<uint8_t *>(&month), reinterpret_cast<uint8_t *>(&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;
}

View File

@ -1,22 +1,21 @@
/*
* CommandingServiceBase.cpp
*
* Created on: 28.08.2019
* Author: gaisser
*/
#include <framework/tcdistribution/PUSDistributorIF.h>
#include <framework/tmtcservices/AcceptsTelemetryIF.h>
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/tmtcservices/CommandingServiceBase.h>
#include <framework/tmtcservices/TmTcMessage.h>
#include <framework/ipc/QueueFactory.h>
#include <framework/tmtcpacket/pus/TcPacketStored.h>
#include <framework/tmtcpacket/pus/TmPacketStored.h>
CommandingServiceBase::CommandingServiceBase(object_id_t setObjectId,
uint16_t apid, uint8_t service, uint8_t numberOfParallelCommands,
uint16_t commandTimeout_seconds, object_id_t setPacketSource,
uint16_t commandTimeoutSeconds, object_id_t setPacketSource,
object_id_t setPacketDestination, size_t queueDepth) :
SystemObject(setObjectId), apid(apid), service(service), timeout_seconds(
commandTimeout_seconds), tmPacketCounter(0), IPCStore(NULL), TCStore(
NULL), commandQueue(NULL), requestQueue(NULL), commandMap(
numberOfParallelCommands), failureParameter1(0), failureParameter2(
0), packetSource(setPacketSource), packetDestination(
setPacketDestination),executingTask(NULL) {
SystemObject(setObjectId), apid(apid), service(service),
timeoutSeconds(commandTimeoutSeconds),
commandMap(numberOfParallelCommands), packetSource(setPacketSource),
packetDestination(setPacketDestination) {
commandQueue = QueueFactory::instance()->createMessageQueue(queueDepth);
requestQueue = QueueFactory::instance()->createMessageQueue(queueDepth);
}
@ -57,8 +56,10 @@ ReturnValue_t CommandingServiceBase::initialize() {
objectManager->get<AcceptsTelemetryIF>(packetDestination);
PUSDistributorIF* distributor = objectManager->get<PUSDistributorIF>(
packetSource);
if ((packetForwarding == NULL) && (distributor == NULL)) {
return RETURN_FAILED;
if (packetForwarding == nullptr or distributor == nullptr) {
sif::error << "CommandingServiceBase::intialize: Packet source or "
"packet destination invalid!" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
distributor->registerService(this);
@ -68,8 +69,10 @@ ReturnValue_t CommandingServiceBase::initialize() {
IPCStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
TCStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
if ((IPCStore == NULL) || (TCStore == NULL)) {
return RETURN_FAILED;
if (IPCStore == nullptr or TCStore == nullptr) {
sif::error << "CommandingServiceBase::intialize: IPC store or TC store "
"not initialized yet!" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
return RETURN_OK;
@ -77,97 +80,126 @@ ReturnValue_t CommandingServiceBase::initialize() {
}
void CommandingServiceBase::handleCommandQueue() {
CommandMessage reply, nextCommand;
ReturnValue_t result, sendResult = RETURN_OK;
bool isStep = false;
CommandMessage reply;
ReturnValue_t result = RETURN_FAILED;
for (result = commandQueue->receiveMessage(&reply); result == RETURN_OK;
result = commandQueue->receiveMessage(&reply)) {
isStep = false;
typename FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator iter;
if (reply.getSender() == MessageQueueIF::NO_QUEUE) {
handleUnrequestedReply(&reply);
continue;
}
if ((iter = commandMap.find(reply.getSender())) == commandMap.end()) {
handleUnrequestedReply(&reply);
continue;
}
nextCommand.setCommand(CommandMessage::CMD_NONE);
result = handleReply(&reply, iter->command, &iter->state, &nextCommand,
iter->objectId, &isStep);
switch (result) {
case EXECUTION_COMPLETE:
case RETURN_OK:
case NO_STEP_MESSAGE:
iter->command = nextCommand.getCommand();
if (nextCommand.getCommand() != CommandMessage::CMD_NONE) {
sendResult = commandQueue->sendMessage(reply.getSender(),
&nextCommand);
}
if (sendResult == RETURN_OK) {
if (isStep) {
if (result != NO_STEP_MESSAGE) {
verificationReporter.sendSuccessReport(
TC_VERIFY::PROGRESS_SUCCESS,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, ++iter->step);
}
} else {
verificationReporter.sendSuccessReport(
TC_VERIFY::COMPLETION_SUCCESS,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, 0);
checkAndExecuteFifo(&iter);
}
} else {
if (isStep) {
nextCommand.clearCommandMessage();
verificationReporter.sendFailureReport(
TC_VERIFY::PROGRESS_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, sendResult,
++iter->step, failureParameter1, failureParameter2);
} else {
nextCommand.clearCommandMessage();
verificationReporter.sendFailureReport(
TC_VERIFY::COMPLETION_FAILURE,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, sendResult, 0,
failureParameter1, failureParameter2);
}
failureParameter1 = 0;
failureParameter2 = 0;
checkAndExecuteFifo(&iter);
}
break;
case INVALID_REPLY:
//might be just an unrequested reply at a bad moment
handleUnrequestedReply(&reply);
break;
default:
if (isStep) {
verificationReporter.sendFailureReport(
TC_VERIFY::PROGRESS_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId, iter->tcInfo.tcSequenceControl,
result, ++iter->step, failureParameter1,
failureParameter2);
} else {
verificationReporter.sendFailureReport(
TC_VERIFY::COMPLETION_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId, iter->tcInfo.tcSequenceControl,
result, 0, failureParameter1, failureParameter2);
}
failureParameter1 = 0;
failureParameter2 = 0;
checkAndExecuteFifo(&iter);
break;
}
handleCommandMessage(&reply);
}
}
void CommandingServiceBase::handleCommandMessage(CommandMessage* reply) {
bool isStep = false;
CommandMessage nextCommand;
CommandMapIter iter = commandMap.find(reply->getSender());
// handle unrequested reply first
if (reply->getSender() == MessageQueueIF::NO_QUEUE or
iter == commandMap.end()) {
handleUnrequestedReply(reply);
return;
}
nextCommand.setCommand(CommandMessage::CMD_NONE);
// Implemented by child class, specifies what to do with reply.
ReturnValue_t result = handleReply(reply, iter->command, &iter->state,
&nextCommand, iter->objectId, &isStep);
/* If the child implementation does not implement special handling for
* rejected replies (RETURN_FAILED is returned), a failure verification
* will be generated with the reason as the return code and the initial
* command as failure parameter 1 */
if(reply->getCommand() == CommandMessage::REPLY_REJECTED and
result == RETURN_FAILED) {
result = reply->getReplyRejectedReason();
failureParameter1 = iter->command;
}
switch (result) {
case EXECUTION_COMPLETE:
case RETURN_OK:
case NO_STEP_MESSAGE:
// handle result of reply handler implemented by developer.
handleReplyHandlerResult(result, iter, &nextCommand, reply, isStep);
break;
case INVALID_REPLY:
//might be just an unrequested reply at a bad moment
handleUnrequestedReply(reply);
break;
default:
if (isStep) {
verificationReporter.sendFailureReport(
TC_VERIFY::PROGRESS_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId, iter->tcInfo.tcSequenceControl,
result, ++iter->step, failureParameter1,
failureParameter2);
} else {
verificationReporter.sendFailureReport(
TC_VERIFY::COMPLETION_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId, iter->tcInfo.tcSequenceControl,
result, 0, failureParameter1, failureParameter2);
}
failureParameter1 = 0;
failureParameter2 = 0;
checkAndExecuteFifo(iter);
break;
}
}
void CommandingServiceBase::handleReplyHandlerResult(ReturnValue_t result,
CommandMapIter iter, CommandMessage* nextCommand,
CommandMessage* reply, bool& isStep) {
iter->command = nextCommand->getCommand();
// In case a new command is to be sent immediately, this is performed here.
// If no new command is sent, only analyse reply result by initializing
// sendResult as RETURN_OK
ReturnValue_t sendResult = RETURN_OK;
if (nextCommand->getCommand() != CommandMessage::CMD_NONE) {
sendResult = commandQueue->sendMessage(reply->getSender(),
nextCommand);
}
if (sendResult == RETURN_OK) {
if (isStep and result != NO_STEP_MESSAGE) {
verificationReporter.sendSuccessReport(
TC_VERIFY::PROGRESS_SUCCESS,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, ++iter->step);
}
else {
verificationReporter.sendSuccessReport(
TC_VERIFY::COMPLETION_SUCCESS,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, 0);
checkAndExecuteFifo(iter);
}
}
else {
if (isStep) {
nextCommand->clearCommandMessage();
verificationReporter.sendFailureReport(
TC_VERIFY::PROGRESS_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, sendResult,
++iter->step, failureParameter1, failureParameter2);
} else {
nextCommand->clearCommandMessage();
verificationReporter.sendFailureReport(
TC_VERIFY::COMPLETION_FAILURE,
iter->tcInfo.ackFlags, iter->tcInfo.tcPacketId,
iter->tcInfo.tcSequenceControl, sendResult, 0,
failureParameter1, failureParameter2);
}
failureParameter1 = 0;
failureParameter2 = 0;
checkAndExecuteFifo(iter);
}
}
void CommandingServiceBase::handleRequestQueue() {
TmTcMessage message;
ReturnValue_t result;
@ -181,7 +213,7 @@ void CommandingServiceBase::handleRequestQueue() {
packet.setStoreAddress(address);
if ((packet.getSubService() == 0)
|| (isValidSubservice(packet.getSubService()) != RETURN_OK)) {
or (isValidSubservice(packet.getSubService()) != RETURN_OK)) {
rejectPacket(TC_VERIFY::START_FAILURE, &packet, INVALID_SUBSERVICE);
continue;
}
@ -194,8 +226,7 @@ void CommandingServiceBase::handleRequestQueue() {
}
//Is a command already active for the target object?
typename FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator iter;
CommandMapIter iter;
iter = commandMap.find(queue);
if (iter != commandMap.end()) {
@ -210,7 +241,7 @@ void CommandingServiceBase::handleRequestQueue() {
if (result != RETURN_OK) {
rejectPacket(TC_VERIFY::START_FAILURE, &packet, BUSY);
} else {
startExecution(&packet, &iter);
startExecution(&packet, iter);
}
}
@ -218,9 +249,9 @@ void CommandingServiceBase::handleRequestQueue() {
}
void CommandingServiceBase::sendTmPacket(uint8_t subservice,
const uint8_t* data, uint32_t dataLen, const uint8_t* headerData,
uint32_t headerSize) {
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice,
const uint8_t* data, size_t dataLen, const uint8_t* headerData,
size_t headerSize) {
TmPacketStored tmPacketStored(this->apid, this->service, subservice,
this->tmPacketCounter, data, dataLen, headerData, headerSize);
ReturnValue_t result = tmPacketStored.sendPacket(
@ -228,79 +259,79 @@ void CommandingServiceBase::sendTmPacket(uint8_t subservice,
if (result == HasReturnvaluesIF::RETURN_OK) {
this->tmPacketCounter++;
}
return result;
}
void CommandingServiceBase::sendTmPacket(uint8_t subservice,
object_id_t objectId, const uint8_t *data, uint32_t dataLen) {
uint8_t buffer[sizeof(object_id_t)];
uint8_t* pBuffer = buffer;
size_t size = 0;
SerializeAdapter::serialize(&objectId, &pBuffer, &size,
sizeof(object_id_t), SerializeIF::Endianness::BIG);
TmPacketStored tmPacketStored(this->apid, this->service, subservice,
this->tmPacketCounter, data, dataLen, buffer, size);
ReturnValue_t result = tmPacketStored.sendPacket(
requestQueue->getDefaultDestination(), requestQueue->getId());
if (result == HasReturnvaluesIF::RETURN_OK) {
this->tmPacketCounter++;
}
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice,
object_id_t objectId, const uint8_t *data, size_t dataLen) {
uint8_t buffer[sizeof(object_id_t)];
uint8_t* pBuffer = buffer;
size_t size = 0;
SerializeAdapter::serialize(&objectId, &pBuffer, &size,
sizeof(object_id_t), SerializeIF::Endianness::BIG);
TmPacketStored tmPacketStored(this->apid, this->service, subservice,
this->tmPacketCounter, data, dataLen, buffer, size);
ReturnValue_t result = tmPacketStored.sendPacket(
requestQueue->getDefaultDestination(), requestQueue->getId());
if (result == HasReturnvaluesIF::RETURN_OK) {
this->tmPacketCounter++;
}
return result;
}
void CommandingServiceBase::sendTmPacket(uint8_t subservice,
SerializeIF* content, SerializeIF* header) {
TmPacketStored tmPacketStored(this->apid, this->service, subservice,
this->tmPacketCounter, content, header);
ReturnValue_t result = tmPacketStored.sendPacket(
requestQueue->getDefaultDestination(), requestQueue->getId());
if (result == HasReturnvaluesIF::RETURN_OK) {
this->tmPacketCounter++;
}
ReturnValue_t CommandingServiceBase::sendTmPacket(uint8_t subservice,
SerializeIF* content, SerializeIF* header) {
TmPacketStored tmPacketStored(this->apid, this->service, subservice,
this->tmPacketCounter, content, header);
ReturnValue_t result = tmPacketStored.sendPacket(
requestQueue->getDefaultDestination(), requestQueue->getId());
if (result == HasReturnvaluesIF::RETURN_OK) {
this->tmPacketCounter++;
}
return result;
}
void CommandingServiceBase::startExecution(
TcPacketStored *storedPacket,
typename FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator *iter) {
ReturnValue_t result, sendResult = RETURN_OK;
CommandMessage message;
(*iter)->subservice = storedPacket->getSubService();
result = prepareCommand(&message, (*iter)->subservice,
storedPacket->getApplicationData(),
storedPacket->getApplicationDataSize(), &(*iter)->state,
(*iter)->objectId);
void CommandingServiceBase::startExecution(TcPacketStored *storedPacket,
CommandMapIter iter) {
ReturnValue_t result = RETURN_OK;
CommandMessage command;
iter->subservice = storedPacket->getSubService();
result = prepareCommand(&command, iter->subservice,
storedPacket->getApplicationData(),
storedPacket->getApplicationDataSize(), &iter->state,
iter->objectId);
ReturnValue_t sendResult = RETURN_OK;
switch (result) {
case RETURN_OK:
if (message.getCommand() != CommandMessage::CMD_NONE) {
sendResult = commandQueue->sendMessage((*iter).value->first,
&message);
if (command.getCommand() != CommandMessage::CMD_NONE) {
sendResult = commandQueue->sendMessage(iter.value->first,
&command);
}
if (sendResult == RETURN_OK) {
Clock::getUptime(&(*iter)->uptimeOfStart);
(*iter)->step = 0;
// (*iter)->state = 0;
(*iter)->subservice = storedPacket->getSubService();
(*iter)->command = message.getCommand();
(*iter)->tcInfo.ackFlags = storedPacket->getAcknowledgeFlags();
(*iter)->tcInfo.tcPacketId = storedPacket->getPacketId();
(*iter)->tcInfo.tcSequenceControl =
Clock::getUptime(&iter->uptimeOfStart);
iter->step = 0;
iter->subservice = storedPacket->getSubService();
iter->command = command.getCommand();
iter->tcInfo.ackFlags = storedPacket->getAcknowledgeFlags();
iter->tcInfo.tcPacketId = storedPacket->getPacketId();
iter->tcInfo.tcSequenceControl =
storedPacket->getPacketSequenceControl();
acceptPacket(TC_VERIFY::START_SUCCESS, storedPacket);
} else {
message.clearCommandMessage();
command.clearCommandMessage();
rejectPacket(TC_VERIFY::START_FAILURE, storedPacket, sendResult);
checkAndExecuteFifo(iter);
}
break;
case EXECUTION_COMPLETE:
if (message.getCommand() != CommandMessage::CMD_NONE) {
if (command.getCommand() != CommandMessage::CMD_NONE) {
//Fire-and-forget command.
sendResult = commandQueue->sendMessage((*iter).value->first,
&message);
sendResult = commandQueue->sendMessage(iter.value->first,
&command);
}
if (sendResult == RETURN_OK) {
verificationReporter.sendSuccessReport(TC_VERIFY::START_SUCCESS,
@ -308,7 +339,7 @@ void CommandingServiceBase::startExecution(
acceptPacket(TC_VERIFY::COMPLETION_SUCCESS, storedPacket);
checkAndExecuteFifo(iter);
} else {
message.clearCommandMessage();
command.clearCommandMessage();
rejectPacket(TC_VERIFY::START_FAILURE, storedPacket, sendResult);
checkAndExecuteFifo(iter);
}
@ -335,12 +366,10 @@ void CommandingServiceBase::acceptPacket(uint8_t reportId,
}
void CommandingServiceBase::checkAndExecuteFifo(
typename FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator *iter) {
void CommandingServiceBase::checkAndExecuteFifo(CommandMapIter iter) {
store_address_t address;
if ((*iter)->fifo.retrieve(&address) != RETURN_OK) {
commandMap.erase(iter);
if (iter->fifo.retrieve(&address) != RETURN_OK) {
commandMap.erase(&iter);
} else {
TcPacketStored newPacket(address);
startExecution(&newPacket, iter);
@ -348,8 +377,7 @@ void CommandingServiceBase::checkAndExecuteFifo(
}
void CommandingServiceBase::handleUnrequestedReply(
CommandMessage* reply) {
void CommandingServiceBase::handleUnrequestedReply(CommandMessage* reply) {
reply->clearCommandMessage();
}
@ -364,18 +392,18 @@ MessageQueueId_t CommandingServiceBase::getCommandQueue() {
void CommandingServiceBase::checkTimeout() {
uint32_t uptime;
Clock::getUptime(&uptime);
typename FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator iter;
CommandMapIter iter;
for (iter = commandMap.begin(); iter != commandMap.end(); ++iter) {
if ((iter->uptimeOfStart + (timeout_seconds * 1000)) < uptime) {
if ((iter->uptimeOfStart + (timeoutSeconds * 1000)) < uptime) {
verificationReporter.sendFailureReport(
TC_VERIFY::COMPLETION_FAILURE, iter->tcInfo.ackFlags,
iter->tcInfo.tcPacketId, iter->tcInfo.tcSequenceControl,
TIMEOUT);
checkAndExecuteFifo(&iter);
checkAndExecuteFifo(iter);
}
}
}
void CommandingServiceBase::setTaskIF(PeriodicTaskIF* task_) {
executingTask = task_;
}

View File

@ -1,35 +1,33 @@
#ifndef COMMANDINGSERVICEBASE_H_
#define COMMANDINGSERVICEBASE_H_
#ifndef FRAMEWORK_TMTCSERVICES_COMMANDINGSERVICEBASE_H_
#define FRAMEWORK_TMTCSERVICES_COMMANDINGSERVICEBASE_H_
#include <framework/container/FixedMap.h>
#include <framework/container/FIFO.h>
#include <framework/ipc/CommandMessage.h>
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/objectmanager/SystemObject.h>
#include <framework/serialize/SerializeAdapter.h>
#include <framework/storagemanager/StorageManagerIF.h>
#include <framework/tasks/ExecutableObjectIF.h>
#include <framework/tcdistribution/PUSDistributorIF.h>
#include <framework/tmtcpacket/pus/TcPacketStored.h>
#include <framework/tmtcpacket/pus/TmPacketStored.h>
#include <framework/ipc/MessageQueueIF.h>
#include <framework/tmtcservices/AcceptsTelecommandsIF.h>
#include <framework/tmtcservices/AcceptsTelemetryIF.h>
#include <framework/tmtcservices/TmTcMessage.h>
#include <framework/tmtcservices/VerificationReporter.h>
#include <framework/internalError/InternalErrorReporterIF.h>
#include <framework/ipc/QueueFactory.h>
#include <framework/timemanager/Clock.h>
#include <framework/ipc/CommandMessage.h>
#include <framework/container/FixedMap.h>
#include <framework/container/FIFO.h>
#include <framework/serialize/SerializeIF.h>
class TcPacketStored;
/**
* \brief This class is the basis for all PUS Services, which have to relay Telecommands to software bus.
* @brief This class is the basis for all PUS Services, which have to
* relay Telecommands to software bus.
*
* It manages Telecommand reception and the generation of Verification Reports like PUSServiceBase.
* Every class that inherits from this abstract class has to implement four adaption points:
* It manages Telecommand reception and the generation of Verification Reports
* similar to PusServiceBase. This class is used if a telecommand can't be
* handled immediately and must be relayed to the internal software bus.
* - isValidSubservice
* - getMessageQueueAndObject
* - prepareCommand
* - handleReply
* \ingroup pus_services
* @author gaisser
* @ingroup pus_services
*/
class CommandingServiceBase: public SystemObject,
public AcceptsTelecommandsIF,
@ -59,7 +57,7 @@ public:
*/
CommandingServiceBase(object_id_t setObjectId, uint16_t apid,
uint8_t service, uint8_t numberOfParallelCommands,
uint16_t commandTimeout_seconds, object_id_t setPacketSource,
uint16_t commandTimeoutSeconds, object_id_t setPacketSource,
object_id_t setPacketDestination, size_t queueDepth = 20);
virtual ~CommandingServiceBase();
@ -99,9 +97,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_){
executingTask = task_;
};
virtual void setTaskIF(PeriodicTaskIF* task_);
protected:
/**
@ -113,8 +109,9 @@ protected:
virtual ReturnValue_t isValidSubservice(uint8_t subservice) = 0;
/**
* Once a TC Request is valid, the existence of the destination and its target interface is checked and retrieved.
* The target message queue ID can then be acquired by using the target interface.
* Once a TC Request is valid, the existence of the destination and its
* target interface is checked and retrieved. The target message queue ID
* can then be acquired by using the target interface.
* @param subservice
* @param tcData Application Data of TC Packet
* @param tcDataLen
@ -125,48 +122,69 @@ protected:
* - @c CSB or implementation specific return codes
*/
virtual ReturnValue_t getMessageQueueAndObject(uint8_t subservice,
const uint8_t *tcData, uint32_t tcDataLen, MessageQueueId_t *id,
const uint8_t *tcData, size_t tcDataLen, MessageQueueId_t *id,
object_id_t *objectId) = 0;
/**
* After the Message Queue and Object ID are determined,
* the command is prepared by using an implementation specific CommandMessage type
* which is sent to the target object.
* It contains all necessary information for the device to execute telecommands.
* @param message[out] message to be sent to the object
* @param subservice[in] Subservice of the current communication
* @param tcData Additional data of the command
* @param tcDataLen Length of the additional data
* @param state[out] Setable state of the communication
* After the Message Queue and Object ID are determined, the command is
* prepared by using an implementation specific CommandMessage type
* which is sent to the target object. It contains all necessary information
* for the device to execute telecommands.
* @param message [out] message which can be set and is sent to the object
* @param subservice Subservice of the current communication
* @param tcData Application data of command
* @param tcDataLen Application data length
* @param state [out/in] Setable state of the communication.
* communication
* @param objectId Target object ID
* @return - @c RETURN_OK on success
* - @c EXECUTION_COMPLETE if exectuin is finished
* - any other return code will be part of (1,4) start failure
* @return
*/
virtual ReturnValue_t prepareCommand(CommandMessage *message,
uint8_t subservice, const uint8_t *tcData, uint32_t tcDataLen,
virtual ReturnValue_t prepareCommand(CommandMessage* message,
uint8_t subservice, const uint8_t *tcData, size_t tcDataLen,
uint32_t *state, object_id_t objectId) = 0;
/**
* This function is responsible for the communication between the Command Service Base
* and the respective PUS Commanding Service once the execution has started.
* The PUS Commanding Service receives replies from the target device and forwards them by calling this function.
* There are different translations of these replies to specify how the Command Service proceeds.
* @param reply Command Message which contains information about the command
* @param previousCommand Command_t of last command
* @param state state of the communication
* @param optionalNextCommand[out] An optional next command which can be set in this function
* 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 in form of a generic read-only command message.
* @param previousCommand
* Command_t of related command
* @param state [out/in]
* Additional parameter which can be used to pass state information.
* State of the communication
* @param optionalNextCommand [out]
* An optional next command which can be set in this function
* @param objectId Source object ID
* @param isStep Flag value to mark steps of command execution
* @return - @c RETURN_OK, @c EXECUTION_COMPLETE or @c NO_STEP_MESSAGE to generate TC verification success
* - @c INVALID_REPLY can handle unrequested replies
* - Anything else triggers a TC verification failure
* @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.
*/
virtual ReturnValue_t handleReply(const CommandMessage *reply,
virtual ReturnValue_t handleReply(const CommandMessage* reply,
Command_t previousCommand, uint32_t *state,
CommandMessage *optionalNextCommand, object_id_t objectId,
CommandMessage* optionalNextCommand, object_id_t objectId,
bool *isStep) = 0;
/**
* This function can be overidden to handle unrequested reply,
* when the reply sender ID is unknown or is not found is the command map.
* The default implementation will clear the command message and all
* its contents.
* @param reply
* Reply which is non-const so the default implementation can clear the
* message.
*/
virtual void handleUnrequestedReply(CommandMessage* reply);
virtual void doPeriodicOperation();
struct CommandInfo {
struct tcInfo {
uint8_t ackFlags;
@ -182,84 +200,92 @@ protected:
FIFO<store_address_t, 3> fifo;
};
using CommandMapIter = FixedMap<MessageQueueId_t,
CommandingServiceBase::CommandInfo>::Iterator;
const uint16_t apid;
const uint8_t service;
const uint16_t timeout_seconds;
const uint16_t timeoutSeconds;
uint8_t tmPacketCounter;
uint8_t tmPacketCounter = 0;
StorageManagerIF *IPCStore;
StorageManagerIF *IPCStore = nullptr;
StorageManagerIF *TCStore;
StorageManagerIF *TCStore = nullptr;
MessageQueueIF* commandQueue;
MessageQueueIF* commandQueue = nullptr;
MessageQueueIF* requestQueue;
MessageQueueIF* requestQueue = nullptr;
VerificationReporter verificationReporter;
FixedMap<MessageQueueId_t, CommandInfo> commandMap;
uint32_t failureParameter1; //!< May be set be children to return a more precise failure condition.
uint32_t failureParameter2; //!< May be set be children to return a more precise failure condition.
/* May be set be children to return a more precise failure condition. */
uint32_t failureParameter1 = 0;
uint32_t failureParameter2 = 0;
object_id_t packetSource;
object_id_t packetDestination;
/**
* Pointer to the task which executes this component, is invalid before setTaskIF was called.
* Pointer to the task which executes this component,
* is invalid before setTaskIF was called.
*/
PeriodicTaskIF* executingTask;
PeriodicTaskIF* executingTask = nullptr;
/**
* Send TM data from pointer to data. If a header is supplied it is added before data
* @brief Send TM data from pointer to data.
* If a header is supplied it is added before data
* @param subservice Number of subservice
* @param data Pointer to the data in the Packet
* @param dataLen Lenght of data in the Packet
* @param headerData HeaderData will be placed before data
* @param headerSize Size of HeaderData
*/
void sendTmPacket(uint8_t subservice, const uint8_t *data, uint32_t dataLen,
const uint8_t* headerData = NULL, uint32_t headerSize = 0);
ReturnValue_t sendTmPacket(uint8_t subservice, const uint8_t *data,
size_t dataLen, const uint8_t* headerData = nullptr,
size_t headerSize = 0);
/**
* To send TM packets of objects that still need to be serialized and consist of an object ID with appended data
* @brief To send TM packets of objects that still need to be serialized
* and consist of an object ID with appended data.
* @param subservice Number of subservice
* @param objectId ObjectId is placed before data
* @param data Data to append to the packet
* @param dataLen Length of Data
*/
void sendTmPacket(uint8_t subservice, object_id_t objectId,
const uint8_t *data, uint32_t dataLen);
ReturnValue_t sendTmPacket(uint8_t subservice, object_id_t objectId,
const uint8_t *data, size_t dataLen);
/**
* To send packets has data which is in form of a SerializeIF or Adapters implementing it
* @brief To send packets which are contained inside a class implementing
* SerializeIF.
* @param subservice Number of subservice
* @param content This is a pointer to the serialized packet
* @param header Serialize IF header which will be placed before content
*/
void sendTmPacket(uint8_t subservice, SerializeIF* content,
SerializeIF* header = NULL);
ReturnValue_t sendTmPacket(uint8_t subservice, SerializeIF* content,
SerializeIF* header = nullptr);
virtual void handleUnrequestedReply(CommandMessage *reply);
virtual void doPeriodicOperation();
void checkAndExecuteFifo(
typename FixedMap<MessageQueueId_t, CommandInfo>::Iterator *iter);
void checkAndExecuteFifo(CommandMapIter iter);
private:
/**
* This method handles internal execution of a command,
* once it has been started by @sa{startExecution()} in the Request Queue handler.
* It handles replies generated by the devices and relayed by the specific service implementation.
* This means that it determines further course of action depending on the return values specified
* in the service implementation.
* once it has been started by @sa{startExecution()} in the request
* queue handler.
* It handles replies generated by the devices and relayed by the specific
* service implementation. This means that it determines further course of
* action depending on the return values specified in the service
* implementation.
* This includes the generation of TC verification messages. Note that
* the static framework object ID @c VerificationReporter::messageReceiver needs to be set.
* the static framework object ID @c VerificationReporter::messageReceiver
* needs to be set.
* - TM[1,5] Step Successs
* - TM[1,6] Step Failure
* - TM[1,7] Completion Success
@ -280,8 +306,11 @@ private:
void acceptPacket(uint8_t reportId, TcPacketStored* packet);
void startExecution(TcPacketStored *storedPacket,
typename FixedMap<MessageQueueId_t, CommandInfo>::Iterator *iter);
void startExecution(TcPacketStored *storedPacket, CommandMapIter iter);
void handleCommandMessage(CommandMessage* reply);
void handleReplyHandlerResult(ReturnValue_t result, CommandMapIter iter,
CommandMessage* nextCommand, CommandMessage* reply, bool& isStep);
void checkTimeout();
};

View File

@ -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>(
AcceptsTelemetryIF* destService = objectManager->get<AcceptsTelemetryIF>(
packetDestination);
PUSDistributorIF* distributor = objectManager->get<PUSDistributorIF>(
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;
}

View File

@ -1,5 +1,5 @@
#ifndef PUSSERVICEBASE_H_
#define PUSSERVICEBASE_H_
#ifndef FRAMEWORK_TMTCSERVICES_PUSSERVICEBASE_H_
#define FRAMEWORK_TMTCSERVICES_PUSSERVICEBASE_H_
#include <framework/objectmanager/ObjectManagerIF.h>
#include <framework/objectmanager/SystemObject.h>
@ -9,7 +9,6 @@
#include <framework/tmtcservices/AcceptsTelecommandsIF.h>
#include <framework/tmtcservices/VerificationCodes.h>
#include <framework/tmtcservices/VerificationReporter.h>
#include <framework/internalError/InternalErrorReporterIF.h>
#include <framework/ipc/MessageQueueIF.h>
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_ */

View File

@ -5,11 +5,13 @@
#include <framework/serviceinterface/ServiceInterfaceStream.h>
#include <framework/globalfunctions/arrayprinter.h>
TmTcBridge::TmTcBridge(object_id_t objectId_,
object_id_t ccsdsPacketDistributor_): SystemObject(objectId_),
ccsdsPacketDistributor(ccsdsPacketDistributor_)
TmTcBridge::TmTcBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId):
SystemObject(objectId),tmStoreId(tmStoreId), tcStoreId(tcStoreId),
tcDestination(tcDestination)
{
TmTcReceptionQueue = QueueFactory::instance()->
tmTcReceptionQueue = QueueFactory::instance()->
createMessageQueue(TMTC_RECEPTION_QUEUE_DEPTH);
}
@ -22,8 +24,9 @@ ReturnValue_t TmTcBridge::setNumberOfSentPacketsPerCycle(
return RETURN_OK;
}
else {
sif::warning << "TmTcBridge: Number of packets sent per cycle "
"exceeds limits. Keeping default value." << std::endl;
sif::warning << "TmTcBridge::setNumberOfSentPacketsPerCycle: Number of "
<< "packets sent per cycle exceeds limits. "
<< "Keeping default value." << std::endl;
return RETURN_FAILED;
}
}
@ -35,27 +38,35 @@ ReturnValue_t TmTcBridge::setMaxNumberOfPacketsStored(
return RETURN_OK;
}
else {
sif::warning << "TmTcBridge: Number of packets stored "
"exceeds limits. Keeping default value." << std::endl;
sif::warning << "TmTcBridge::setMaxNumberOfPacketsStored: Number of "
<< "packets stored exceeds limits. "
<< "Keeping default value." << std::endl;
return RETURN_FAILED;
}
}
ReturnValue_t TmTcBridge::initialize() {
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
if (tcStore == NULL) {
return RETURN_FAILED;
tcStore = objectManager->get<StorageManagerIF>(tcStoreId);
if (tcStore == nullptr) {
sif::error << "TmTcBridge::initialize: TC store invalid. Make sure"
"it is created and set up properly." << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
tmStore = objectManager->get<StorageManagerIF>(objects::TM_STORE);
if (tmStore == NULL) {
return RETURN_FAILED;
tmStore = objectManager->get<StorageManagerIF>(tmStoreId);
if (tmStore == nullptr) {
sif::error << "TmTcBridge::initialize: TM store invalid. Make sure"
"it is created and set up properly." << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
AcceptsTelecommandsIF* tcDistributor =
objectManager->get<AcceptsTelecommandsIF>(ccsdsPacketDistributor);
if (tcDistributor == NULL) {
return RETURN_FAILED;
objectManager->get<AcceptsTelecommandsIF>(tcDestination);
if (tcDistributor == nullptr) {
sif::error << "TmTcBridge::initialize: TC Distributor invalid"
<< std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
TmTcReceptionQueue->setDefaultDestination(tcDistributor->getRequestQueue());
tmTcReceptionQueue->setDefaultDestination(tcDistributor->getRequestQueue());
return RETURN_OK;
}
@ -63,26 +74,25 @@ ReturnValue_t TmTcBridge::performOperation(uint8_t operationCode) {
ReturnValue_t result;
result = handleTc();
if(result != RETURN_OK) {
sif::error << "TMTC Bridge: Error handling TCs" << std::endl;
sif::debug << "TmTcBridge::performOperation: "
<< "Error handling TCs" << std::endl;
}
result = handleTm();
if (result != RETURN_OK) {
sif::error << "TMTC Bridge: Error handling TMs" << std::endl;
sif::debug << "TmTcBridge::performOperation: "
<< "Error handling TMs" << std::endl;
}
return result;
}
ReturnValue_t TmTcBridge::handleTc() {
uint8_t * recvBuffer = nullptr;
size_t recvLen = 0;
ReturnValue_t result = receiveTc(&recvBuffer, &recvLen);
return result;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TmTcBridge::handleTm() {
ReturnValue_t result = handleTmQueue();
if(result != RETURN_OK) {
sif::error << "TMTC Bridge: Reading TM Queue failed" << std::endl;
sif::warning << "TmTcBridge: Reading TM Queue failed" << std::endl;
return RETURN_FAILED;
}
@ -97,8 +107,8 @@ ReturnValue_t TmTcBridge::handleTmQueue() {
TmTcMessage message;
const uint8_t* data = nullptr;
size_t size = 0;
for (ReturnValue_t result = TmTcReceptionQueue->receiveMessage(&message);
result == RETURN_OK; result = TmTcReceptionQueue->receiveMessage(&message))
for (ReturnValue_t result = tmTcReceptionQueue->receiveMessage(&message);
result == RETURN_OK; result = tmTcReceptionQueue->receiveMessage(&message))
{
if(communicationLinkUp == false) {
result = storeDownlinkData(&message);
@ -112,7 +122,7 @@ ReturnValue_t TmTcBridge::handleTmQueue() {
result = sendTm(data, size);
if (result != RETURN_OK) {
sif::error << "TMTC Bridge: Could not send TM packet"<< std::endl;
sif::warning << "TmTcBridge: Could not send TM packet" << std::endl;
tmStore->deleteData(message.getStorageId());
return result;
@ -123,13 +133,12 @@ ReturnValue_t TmTcBridge::handleTmQueue() {
}
ReturnValue_t TmTcBridge::storeDownlinkData(TmTcMessage *message) {
//debug << "TMTC Bridge: Comm Link down. "
// "Saving packet ID to be sent later\r\n" << std::flush;
store_address_t storeId = 0;
if(tmFifo.full()) {
sif::error << "TMTC Bridge: TM downlink max. number of stored packet IDs "
"reached! Overwriting old data" << std::endl;
sif::error << "TmTcBridge::storeDownlinkData: TM downlink max. number "
<< "of stored packet IDs reached! "
<< "Overwriting old data" << std::endl;
tmFifo.retrieve(&storeId);
tmStore->deleteData(storeId);
}
@ -183,10 +192,20 @@ void TmTcBridge::registerCommDisconnect() {
}
MessageQueueId_t TmTcBridge::getReportReceptionQueue(uint8_t virtualChannel) {
return TmTcReceptionQueue->getId();
return tmTcReceptionQueue->getId();
}
void TmTcBridge::printData(uint8_t * data, size_t dataLen) {
arrayprinter::print(data, dataLen);
}
uint16_t TmTcBridge::getIdentifier() {
// This is no PUS service, so we just return 0
return 0;
}
MessageQueueId_t TmTcBridge::getRequestQueue() {
// Default implementation: Relay TC messages to TC distributor directly.
return tmTcReceptionQueue->getDefaultDestination();
}

View File

@ -1,16 +1,18 @@
#ifndef FRAMEWORK_TMTCSERVICES_TMTCBRIDGE_H_
#define FRAMEWORK_TMTCSERVICES_TMTCBRIDGE_H_
#include <framework/objectmanager/SystemObject.h>
#include <framework/tmtcservices/AcceptsTelemetryIF.h>
#include <framework/tasks/ExecutableObjectIF.h>
#include <framework/ipc/MessageQueueIF.h>
#include <framework/storagemanager/StorageManagerIF.h>
#include <framework/objectmanager/SystemObject.h>
#include <framework/tmtcservices/AcceptsTelecommandsIF.h>
#include <framework/tmtcservices/TmTcMessage.h>
#include <framework/container/FIFO.h>
#include <framework/tmtcservices/TmTcMessage.h>
class TmTcBridge : public AcceptsTelemetryIF,
public AcceptsTelecommandsIF,
public ExecutableObjectIF,
public HasReturnvaluesIF,
public SystemObject {
@ -22,7 +24,8 @@ public:
static constexpr uint8_t DEFAULT_STORED_DATA_SENT_PER_CYCLE = 5;
static constexpr uint8_t DEFAULT_DOWNLINK_PACKETS_STORED = 10;
TmTcBridge(object_id_t objectId_, object_id_t ccsdsPacketDistributor_);
TmTcBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId);
virtual ~TmTcBridge();
/**
@ -57,45 +60,42 @@ public:
*/
virtual ReturnValue_t performOperation(uint8_t operationCode = 0) override;
/**
* Return TMTC Reception Queue
* @param virtualChannel
* @return
*/
MessageQueueId_t getReportReceptionQueue(
/** AcceptsTelemetryIF override */
virtual MessageQueueId_t getReportReceptionQueue(
uint8_t virtualChannel = 0) override;
/** AcceptsTelecommandsIF override */
virtual uint16_t getIdentifier() override;
virtual MessageQueueId_t getRequestQueue() override;
protected:
//! Cached for initialize function.
object_id_t tmStoreId = objects::NO_OBJECT;
object_id_t tcStoreId = objects::NO_OBJECT;
object_id_t tcDestination = objects::NO_OBJECT;
//! Used to send and receive TMTC messages.
//! TmTcMessage is used to transport messages between tasks.
MessageQueueIF* TmTcReceptionQueue = nullptr;
StorageManagerIF* tcStore = nullptr;
//! The TmTcMessage class is used to transport messages between tasks.
MessageQueueIF* tmTcReceptionQueue = nullptr;
StorageManagerIF* tmStore = nullptr;
object_id_t ccsdsPacketDistributor = 0;
//! Used to specify whether communication link is up
bool communicationLinkUp = false;
StorageManagerIF* tcStore = nullptr;
//! Used to specify whether communication link is up. Will be true
//! by default, so telemetry will be handled immediately.
bool communicationLinkUp = true;
bool tmStored = false;
/**
* @brief Handle TC reception
* @details
* Default implementation provided, but is empty.
* Child handler should override this in most cases orsend TC to the
* TC distributor directly with the address of the reception queue by
* calling getReportRecptionQueue()
* In most cases, TC reception will be handled in a separate task anyway.
* @return
*/
virtual ReturnValue_t handleTc();
/**
* Implemented by child class. Perform receiving of Telecommand,
* for example by implementing specific drivers or wrappers,
* e.g. UART Communication or an ethernet stack
* @param recvBuffer [out] Received data
* @param size [out] Size of received data
* @return
*/
virtual ReturnValue_t receiveTc(uint8_t ** recvBuffer, size_t * size) = 0;
/**
* Handle Telemetry. Default implementation provided.
* Calls sendTm()
@ -104,7 +104,8 @@ protected:
virtual ReturnValue_t handleTm();
/**
* Read the TM Queue and send TM if necessary. Default implementation provided
* Read the TM Queue and send TM if necessary.
* Default implementation provided
* @return
*/
virtual ReturnValue_t handleTmQueue();
@ -117,7 +118,8 @@ protected:
/**
* Implemented by child class. Perform sending of Telemetry by implementing
* communication drivers or wrappers, e.g. UART communication or lwIP stack.
* communication drivers or wrappers, e.g. serial communication or a socket
* call.
* @param data
* @param dataLen
* @return