Merge remote-tracking branch 'upstream/master' into mueller/parameter-convergence

This commit is contained in:
Robin Müller 2020-11-02 15:55:07 +01:00
commit 71382032a1
110 changed files with 5186 additions and 176 deletions

0
.gitmodules vendored Normal file
View File

11
FSFWVersion.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef FSFW_DEFAULTCFG_VERSION_H_
#define FSFW_DEFAULTCFG_VERSION_H_
const char* const FSFW_VERSION_NAME = "fsfw";
#define FSFW_VERSION 0
#define FSFW_SUBVERSION 0
#endif /* FSFW_DEFAULTCFG_VERSION_H_ */

4
README.md Normal file
View File

@ -0,0 +1,4 @@
Flight Software Framework (FSFW)
======
I want to be written!

View File

@ -3,8 +3,9 @@
#include "../ipc/MessageQueueSenderIF.h"
#include "../objectmanager/ObjectManagerIF.h"
ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) :
owner(setOwner), queueToUse(useThisQueue), ipcStore(nullptr) {
ActionHelper::ActionHelper(HasActionsIF* setOwner,
MessageQueueIF* useThisQueue) :
owner(setOwner), queueToUse(useThisQueue) {
}
ActionHelper::~ActionHelper() {
@ -33,13 +34,15 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) {
return HasReturnvaluesIF::RETURN_OK;
}
void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) {
void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo,
ActionId_t commandId, ReturnValue_t result) {
CommandMessage reply;
ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result);
queueToUse->sendMessage(reportTo, &reply);
}
void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) {
void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId,
ReturnValue_t result) {
CommandMessage reply;
ActionMessage::setCompletionReply(&reply, commandId, result);
queueToUse->sendMessage(reportTo, &reply);
@ -49,8 +52,8 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) {
queueToUse = queue;
}
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
store_address_t dataAddress) {
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy,
ActionId_t actionId, store_address_t dataAddress) {
const uint8_t* dataPtr = NULL;
size_t size = 0;
ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
@ -62,6 +65,11 @@ void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t act
}
result = owner->executeAction(actionId, commandedBy, dataPtr, size);
ipcStore->deleteData(dataAddress);
if(result == HasActionsIF::EXECUTION_FINISHED) {
CommandMessage reply;
ActionMessage::setCompletionReply(&reply, actionId, result);
queueToUse->sendMessage(commandedBy, &reply);
}
if (result != HasReturnvaluesIF::RETURN_OK) {
CommandMessage reply;
ActionMessage::setStepReply(&reply, actionId, 0, result);
@ -86,21 +94,27 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo,
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
result = data->serialize(&dataPtr, &size, maxSize, SerializeIF::Endianness::BIG);
result = data->serialize(&dataPtr, &size, maxSize,
SerializeIF::Endianness::BIG);
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeAddress);
return result;
}
//We don't need to report the objectId, as we receive REQUESTED data before the completion success message.
//True aperiodic replies need to be reported with another dedicated message.
// We don't need to report the objectId, as we receive REQUESTED data
// before the completion success message.
// True aperiodic replies need to be reported with
// another dedicated message.
ActionMessage::setDataReply(&reply, replyId, storeAddress);
//TODO Service Implementation sucks at the moment
// If the sender needs to be hidden, for example to handle packet
// as unrequested reply, this will be done here.
if (hideSender) {
result = MessageQueueSenderIF::sendMessage(reportTo, &reply);
} else {
}
else {
result = queueToUse->sendMessage(reportTo, &reply);
}
if (result != HasReturnvaluesIF::RETURN_OK){
ipcStore->deleteData(storeAddress);
}
@ -109,3 +123,39 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo,
void ActionHelper::resetHelper() {
}
ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo,
ActionId_t replyId, const uint8_t *data, size_t dataSize,
bool hideSender) {
CommandMessage reply;
store_address_t storeAddress;
ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeAddress);
return result;
}
// We don't need to report the objectId, as we receive REQUESTED data
// before the completion success message.
// True aperiodic replies need to be reported with
// another dedicated message.
ActionMessage::setDataReply(&reply, replyId, storeAddress);
// If the sender needs to be hidden, for example to handle packet
// as unrequested reply, this will be done here.
if (hideSender) {
result = MessageQueueSenderIF::sendMessage(reportTo, &reply);
}
else {
result = queueToUse->sendMessage(reportTo, &reply);
}
if (result != HasReturnvaluesIF::RETURN_OK){
ipcStore->deleteData(storeAddress);
}
return result;
}

View File

@ -1,15 +1,18 @@
#ifndef ACTIONHELPER_H_
#define ACTIONHELPER_H_
#ifndef FSFW_ACTION_ACTIONHELPER_H_
#define FSFW_ACTION_ACTIONHELPER_H_
#include "ActionMessage.h"
#include "../serialize/SerializeIF.h"
#include "../ipc/MessageQueueIF.h"
/**
* \brief Action Helper is a helper class which handles action messages
* @brief Action Helper is a helper class which handles action messages
*
* Components which use the HasActionIF this helper can be used to handle the action messages.
* It does handle step messages as well as other answers to action calls. It uses the executeAction function
* of its owner as callback. The call of the initialize function is mandatory and it needs a valid messageQueueIF pointer!
* Components which use the HasActionIF this helper can be used to handle
* the action messages.
* It does handle step messages as well as other answers to action calls.
* It uses the executeAction function of its owner as callback.
* The call of the initialize function is mandatory and needs a
* valid MessageQueueIF pointer!
*/
class HasActionsIF;
@ -18,7 +21,8 @@ public:
/**
* Constructor of the action helper
* @param setOwner Pointer to the owner of the interface
* @param useThisQueue messageQueue to be used, can be set during initialize function as well.
* @param useThisQueue messageQueue to be used, can be set during
* initialize function as well.
*/
ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue);
@ -26,28 +30,36 @@ public:
/**
* Function to be called from the owner with a new command message
*
* If the message is a valid action message the helper will use the executeAction function from HasActionsIF.
* If the message is invalid or the callback fails a message reply will be send to the sender of the message automatically.
* If the message is a valid action message the helper will use the
* executeAction function from HasActionsIF.
* If the message is invalid or the callback fails a message reply will be
* send to the sender of the message automatically.
*
* @param command Pointer to a command message received by the owner
* @return HasReturnvaluesIF::RETURN_OK if the message is a action message, CommandMessage::UNKNOW_COMMAND if this message ID is unkown
* @return HasReturnvaluesIF::RETURN_OK if the message is a action message,
* CommandMessage::UNKNOW_COMMAND if this message ID is unkown
*/
ReturnValue_t handleActionMessage(CommandMessage* command);
/**
* Helper initialize function. Must be called before use of any other helper function
* @param queueToUse_ Pointer to the messageQueue to be used, optional if queue was set in constructor
* Helper initialize function. Must be called before use of any other
* helper function
* @param queueToUse_ Pointer to the messageQueue to be used, optional
* if queue was set in constructor
* @return Returns RETURN_OK if successful
*/
ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr);
/**
* Function to be called from the owner to send a step message. Success or failure will be determined by the result value.
* Function to be called from the owner to send a step message.
* Success or failure will be determined by the result value.
*
* @param step Number of steps already done
* @param reportTo The messageQueueId to report the step message to
* @param commandId ID of the executed command
* @param result Result of the execution
*/
void step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
void step(uint8_t step, MessageQueueId_t reportTo,
ActionId_t commandId,
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
/**
* Function to be called by the owner to send a action completion message
*
@ -55,39 +67,59 @@ public:
* @param commandId ID of the executed command
* @param result Result of the execution
*/
void finish(MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
void finish(MessageQueueId_t reportTo, ActionId_t commandId,
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK);
/**
* Function to be called by the owner if an action does report data
*
* @param reportTo MessageQueueId_t to report the action completion message to
* Function to be called by the owner if an action does report data.
* Takes a SerializeIF* pointer and serializes it into the IPC store.
* @param reportTo MessageQueueId_t to report the action completion
* message to
* @param replyId ID of the executed command
* @param data Pointer to the data
* @return Returns RETURN_OK if successful, otherwise failure code
*/
ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, SerializeIF* data, bool hideSender = false);
ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId,
SerializeIF* data, bool hideSender = false);
/**
* Function to setup the MessageQueueIF* of the helper. Can be used to set the messageQueueIF* if
* message queue is unavailable at construction and initialize but must be setup before first call of other functions.
* Function to be called by the owner if an action does report data.
* Takes the raw data and writes it into the IPC store.
* @param reportTo MessageQueueId_t to report the action completion
* message to
* @param replyId ID of the executed command
* @param data Pointer to the data
* @return Returns RETURN_OK if successful, otherwise failure code
*/
ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId,
const uint8_t* data, size_t dataSize, bool hideSender = false);
/**
* Function to setup the MessageQueueIF* of the helper. Can be used to
* set the MessageQueueIF* if message queue is unavailable at construction
* and initialize but must be setup before first call of other functions.
* @param queue Queue to be used by the helper
*/
void setQueueToUse(MessageQueueIF *queue);
protected:
static const uint8_t STEP_OFFSET = 1;//!< Increase of value of this per step
//!< Increase of value of this per step
static const uint8_t STEP_OFFSET = 1;
HasActionsIF* owner;//!< Pointer to the owner
MessageQueueIF* queueToUse;//!< Queue to be used as response sender, has to be set with
StorageManagerIF* ipcStore;//!< Pointer to an IPC Store, initialized during construction or initialize(MessageQueueIF* queueToUse_) or with setQueueToUse(MessageQueueIF *queue)
//! Queue to be used as response sender, has to be set in ctor or with
//! setQueueToUse
MessageQueueIF* queueToUse;
//! Pointer to an IPC Store, initialized during construction or
StorageManagerIF* ipcStore = nullptr;
/**
*Internal function called by handleActionMessage(CommandMessage* command)
*
* Internal function called by handleActionMessage
* @param commandedBy MessageQueueID of Commander
* @param actionId ID of action to be done
* @param dataAddress Address of additional data in IPC Store
*/
virtual void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress);
virtual void prepareExecution(MessageQueueId_t commandedBy,
ActionId_t actionId, store_address_t dataAddress);
/**
*
* @brief Default implementation is empty.
*/
virtual void resetHelper();
};
#endif /* ACTIONHELPER_H_ */
#endif /* FSFW_ACTION_ACTIONHELPER_H_ */

View File

@ -1,11 +1,12 @@
#ifndef FRAMEWORK_ACTION_HASACTIONSIF_H_
#define FRAMEWORK_ACTION_HASACTIONSIF_H_
#ifndef FSFW_ACTION_HASACTIONSIF_H_
#define FSFW_ACTION_HASACTIONSIF_H_
#include "ActionHelper.h"
#include "ActionMessage.h"
#include "SimpleActionHelper.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../ipc/MessageQueueIF.h"
/**
* @brief
* Interface for component which uses actions
@ -47,14 +48,16 @@ public:
virtual MessageQueueId_t getCommandQueue() const = 0;
/**
* Execute or initialize the execution of a certain function.
* Returning #EXECUTION_FINISHED or a failure code, nothing else needs to
* be done. When needing more steps, return RETURN_OK and issue steps and
* completion manually.
* One "step failed" or completion report must be issued!
* The ActionHelpers will execute this function and behave differently
* depending on the returnvalue.
*
* @return
* -@c EXECUTION_FINISHED Finish reply will be generated
* -@c Not RETURN_OK Step failure reply will be generated
*/
virtual ReturnValue_t executeAction(ActionId_t actionId,
MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0;
};
#endif /* FRAMEWORK_ACTION_HASACTIONSIF_H_ */
#endif /* FSFW_ACTION_HASACTIONSIF_H_ */

View File

@ -1,16 +1,17 @@
#include "HasActionsIF.h"
#include "SimpleActionHelper.h"
SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner,
MessageQueueIF* useThisQueue) :
ActionHelper(setOwner, useThisQueue), isExecuting(false), lastCommander(
0), lastAction(0), stepCount(0) {
ActionHelper(setOwner, useThisQueue), isExecuting(false) {
}
SimpleActionHelper::~SimpleActionHelper() {
}
void SimpleActionHelper::step(ReturnValue_t result) {
//STEP_OFFESET is subtracted to compensate for adding offset in base method, which is not necessary here.
// STEP_OFFESET is subtracted to compensate for adding offset in base
// method, which is not necessary here.
ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction,
result);
if (result != HasReturnvaluesIF::RETURN_OK) {

View File

@ -1,8 +1,13 @@
#ifndef SIMPLEACTIONHELPER_H_
#define SIMPLEACTIONHELPER_H_
#ifndef FSFW_ACTION_SIMPLEACTIONHELPER_H_
#define FSFW_ACTION_SIMPLEACTIONHELPER_H_
#include "ActionHelper.h"
/**
* @brief This is an action helper which is only able to service one action
* at a time but remembers last commander and last action which
* simplifies usage
*/
class SimpleActionHelper: public ActionHelper {
public:
SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue);
@ -12,13 +17,14 @@ public:
ReturnValue_t reportData(SerializeIF* data);
protected:
void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress);
void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId,
store_address_t dataAddress);
virtual void resetHelper();
private:
bool isExecuting;
MessageQueueId_t lastCommander;
ActionId_t lastAction;
uint8_t stepCount;
MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE;
ActionId_t lastAction = 0;
uint8_t stepCount = 0;
};
#endif /* SIMPLEACTIONHELPER_H_ */

6
defaultcfg/README.md Normal file
View File

@ -0,0 +1,6 @@
# How to setup configuration folder for FSFW
It is recommended to copy the content of the defaultcfg folder
into a config folder which is in the same directory as the Flight
Software Framework submodule. After that, the config.mk folder should be
included by the primary Makefile with CURRENTPATH set correctly.

View File

@ -0,0 +1,54 @@
#ifndef CONFIG_FSFWCONFIG_H_
#define CONFIG_FSFWCONFIG_H_
#include <FSFWVersion.h>
#include <cstddef>
//! Used to determine whether C++ ostreams are used
//! Those can lead to code bloat.
#define FSFW_CPP_OSTREAM_ENABLED 1
//! Reduced printout to further decrese code size
//! Be careful, this also turns off most diagnostic prinouts!
#define FSFW_REDUCED_PRINTOUT 0
//! Can be used to enable debugging printouts for developing the FSFW
#define FSFW_DEBUGGING 0
//! Defines the FIFO depth of each commanding service base which
//! also determines how many commands a CSB service can handle in one cycle
//! simulataneously. This will increase the required RAM for
//! each CSB service !
#define FSFW_CSB_FIFO_DEPTH 6
//! If FSFW_OBJ_EVENT_TRANSLATION is set to one,
//! additional output which requires the translation files translateObjects
//! and translateEvents (and their compiled source files)
#define FSFW_OBJ_EVENT_TRANSLATION 0
#if FSFW_OBJ_EVENT_TRANSLATION == 1
#define FSFW_DEBUG_OUTPUT 1
//! Specify whether info events are printed too.
#define FSFW_DEBUG_INFO 1
#include <translateObjects.h>
#include <translateEvents.h>
#else
#define FSFW_DEBUG_OUTPUT 0
#endif
//! When using the newlib nano library, C99 support for stdio facilities
//! will not be provided. This define should be set to 1 if this is the case.
#define FSFW_NO_C99_IO 1
namespace fsfwconfig {
//! Default timestamp size. The default timestamp will be an eight byte CDC
//! short timestamp.
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8;
//! Configure the allocated pool sizes for the event manager.
static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240;
static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120;
static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
}
#endif /* CONFIG_FSFWCONFIG_H_ */

View File

@ -0,0 +1,16 @@
#ifndef CONFIG_OBSWCONFIG_H_
#define CONFIG_OBSWCONFIG_H_
#include "OBSWVersion.h"
#ifdef __cplusplus
namespace config {
#endif
/* Add mission configuration flags here */
#ifdef __cplusplus
}
#endif
#endif /* CONFIG_OBSWCONFIG_H_ */

View File

@ -0,0 +1,9 @@
#ifndef CONFIG_VERSION_H_
#define CONFIG_VERSION_H_
/* OBSW versioning can be specified in this file */
#define OBSW_VERSION 0
#define OBSW_SUBVERSION 0
#endif /* CONFIG_VERSION_H_ */

View File

@ -0,0 +1,15 @@
CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/tmtc/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/devices/*.cpp)
INCLUDES += $(CURRENTPATH)
INCLUDES += $(CURRENTPATH)/objects
INCLUDES += $(CURRENTPATH)/returnvalues
INCLUDES += $(CURRENTPATH)/tmtc
INCLUDES += $(CURRENTPATH)/events
INCLUDES += $(CURRENTPATH)/devices
INCLUDES += $(CURRENTPATH)/pollingsequence
INCLUDES += $(CURRENTPATH)/ipc

View File

@ -0,0 +1,5 @@
#include "logicalAddresses.h"

View File

@ -0,0 +1,18 @@
#ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_
#define CONFIG_DEVICES_LOGICALADDRESSES_H_
#include <config/objects/systemObjectList.h>
#include <fsfw/devicehandlers/CookieIF.h>
#include <cstdint>
/**
* Can be used for addresses for physical devices like I2C adresses.
*/
namespace addresses {
/* Logical addresses have uint32_t datatype */
enum logicalAddresses: address_t {
};
}
#endif /* CONFIG_DEVICES_LOGICALADDRESSES_H_ */

View File

@ -0,0 +1,4 @@
#include "powerSwitcherList.h"

View File

@ -0,0 +1,12 @@
#ifndef CONFIG_DEVICES_POWERSWITCHERLIST_H_
#define CONFIG_DEVICES_POWERSWITCHERLIST_H_
namespace switches {
/* Switches are uint8_t datatype and go from 0 to 255 */
enum switcherList {
};
}
#endif /* CONFIG_DEVICES_POWERSWITCHERLIST_H_ */

View File

@ -0,0 +1,18 @@
#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#include <cstdint>
#include <fsfw/events/fwSubsystemIdRanges.h>
/**
* @brief Custom subsystem IDs can be added here
* @details
* Subsystem IDs are used to create unique events.
*/
namespace SUBSYSTEM_ID {
enum: uint8_t {
SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE,
};
}
#endif /* CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ */

View File

@ -0,0 +1,11 @@
#include <config/ipc/MissionMessageTypes.h>
#include <fsfw/ipc/CommandMessageIF.h>
void messagetypes::clearMissionMessage(CommandMessage* message) {
switch(message->getMessageType()) {
default:
break;
}
}

View File

@ -0,0 +1,21 @@
#ifndef CONFIG_IPC_MISSIONMESSAGETYPES_H_
#define CONFIG_IPC_MISSIONMESSAGETYPES_H_
#include <fsfw/ipc/CommandMessage.h>
#include <fsfw/ipc/FwMessageTypes.h>
/**
* Custom command messages are specified here.
* Most messages needed to use FSFW are already located in
* <fsfw/ipc/FwMessageTypes.h>
* @param message Generic Command Message
*/
namespace messagetypes {
enum CustomMessageTypes {
MISSION_MESSAGE_TYPE_START = FW_MESSAGES_COUNT
};
void clearMissionMessage(CommandMessage* message);
}
#endif /* CONFIG_IPC_MISSIONMESSAGETYPES_H_ */

View File

@ -0,0 +1,54 @@
#include "Factory.h"
#include "../tmtc/apid.h"
#include "../tmtc/pusIds.h"
#include "../objects/systemObjectList.h"
#include "../devices/logicalAddresses.h"
#include "../devices/powerSwitcherList.h"
#include <fsfw/devicehandlers/DeviceHandlerBase.h>
#include <fsfw/events/EventManager.h>
#include <fsfw/health/HealthTable.h>
#include <fsfw/tmtcpacket/pus/TmPacketStored.h>
#include <fsfw/tmtcservices/CommandingServiceBase.h>
#include <fsfw/tmtcservices/PusServiceBase.h>
#include <cstdint>
/**
* This class should be used to create all system objects required for
* the on-board software, using the object ID list from the configuration
* folder.
*
* The objects are registered in the internal object manager automatically.
* This is used later to add objects to tasks.
*
* This file also sets static framework IDs.
*
* Framework objects are created first.
* @ingroup init
*/
void Factory::produce(void) {
setStaticFrameworkObjectIds();
new EventManager(objects::EVENT_MANAGER);
new HealthTable(objects::HEALTH_TABLE);
//new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
}
void Factory::setStaticFrameworkObjectIds() {
PusServiceBase::packetSource = objects::PUS_PACKET_DISTRIBUTOR;
PusServiceBase::packetDestination = objects::TM_FUNNEL;
CommandingServiceBase::defaultPacketSource = objects::PUS_PACKET_DISTRIBUTOR;
CommandingServiceBase::defaultPacketDestination = objects::TM_FUNNEL;
VerificationReporter::messageReceiver = objects::PUS_SERVICE_1_VERIFICATION;
DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
DeviceHandlerBase::rawDataReceiverId = objects::PUS_SERVICE_2_DEVICE_ACCESS;
DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT;
TmPacketStored::timeStamperId = objects::PUS_TIME;
//TmFunnel::downlinkDestination = objects::NO_OBJECT;
}

View File

@ -0,0 +1,17 @@
#ifndef FACTORY_H_
#define FACTORY_H_
#include <fsfw/objectmanager/SystemObjectIF.h>
#include <cstddef>
namespace Factory {
/**
* @brief Creates all SystemObject elements which are persistent
* during execution.
*/
void produce();
void setStaticFrameworkObjectIds();
}
#endif /* FACTORY_H_ */

View File

@ -0,0 +1,16 @@
#ifndef CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#define CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#include <cstdint>
#include <fsfw/objectmanager/frameworkObjects.h>
// The objects will be instantiated in the ID order
namespace objects {
enum sourceObjects: uint32_t {
/* All addresses between start and end are reserved for the FSFW */
FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION,
FSFW_CONFIG_RESERVED_END = TM_STORE
};
}
#endif /* BSP_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ */

View File

@ -0,0 +1,23 @@
#include "PollingSequenceFactory.h"
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/tasks/FixedTimeslotTaskIF.h>
ReturnValue_t pst::pollingSequenceInitDefault(
FixedTimeslotTaskIF *thisSequence) {
/* Length of a communication cycle */
uint32_t length = thisSequence->getPeriodMs();
/* Add polling sequence table here */
if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) {
return HasReturnvaluesIF::RETURN_OK;
}
else {
sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!"
<< std::endl;
return HasReturnvaluesIF::RETURN_FAILED;
}
}

View File

@ -0,0 +1,32 @@
#ifndef POLLINGSEQUENCEFACTORY_H_
#define POLLINGSEQUENCEFACTORY_H_
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
class FixedTimeslotTaskIF;
/**
* All device handlers are scheduled by adding them into Polling Sequence Tables (PST)
* to satisfy stricter timing requirements of device communication,
* A device handler has four different communication steps:
* 1. DeviceHandlerIF::SEND_WRITE -> Send write via interface
* 2. DeviceHandlerIF::GET_WRITE -> Get confirmation for write
* 3. DeviceHandlerIF::SEND_READ -> Send read request
* 4. DeviceHandlerIF::GET_READ -> Read from interface
* The PST specifies precisely when the respective ComIF functions are called
* during the communication cycle time.
* The task is created using the FixedTimeslotTaskIF,
* which utilises the underlying Operating System Abstraction Layer (OSAL)
*
* @param thisSequence FixedTimeslotTaskIF * object is passed inside the Factory class when creating the PST
* @return
*/
namespace pst {
/* Default PST */
ReturnValue_t pollingSequenceInitDefault(FixedTimeslotTaskIF *thisSequence);
}
#endif /* POLLINGSEQUENCEINIT_H_ */

View File

@ -0,0 +1,16 @@
#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_
#define CONFIG_RETURNVALUES_CLASSIDS_H_
#include <fsfw/returnvalues/FwClassIds.h>
/**
* @brief CLASS_ID defintions which are required for custom returnvalues.
*/
namespace CLASS_ID {
enum {
MISSION_CLASS_ID_START = FW_CLASS_ID_COUNT,
};
}
#endif /* CONFIG_RETURNVALUES_CLASSIDS_H_ */

View File

@ -0,0 +1,18 @@
#ifndef CONFIG_TMTC_APID_H_
#define CONFIG_TMTC_APID_H_
#include <cstdint>
/**
* Application Process Definition: entity, uniquely identified by an
* application process ID (APID), capable of generating telemetry source
* packets and receiving telecommand packets.
*
* Chose APID(s) for mission and define it here.
*/
namespace apid {
static const uint16_t DEFAULT_APID = 0x00;
}
#endif /* CONFIG_TMTC_APID_H_ */

View File

@ -0,0 +1,23 @@
#ifndef CONFIG_TMTC_PUSIDS_HPP_
#define CONFIG_TMTC_PUSIDS_HPP_
namespace pus {
enum Ids: uint8_t {
PUS_SERVICE_1 = 1,
PUS_SERVICE_2 = 2,
PUS_SERVICE_3 = 3,
PUS_SERVICE_5 = 5,
PUS_SERVICE_6 = 6,
PUS_SERVICE_8 = 8,
PUS_SERVICE_9 = 9,
PUS_SERVICE_11 = 11,
PUS_SERVICE_17 = 17,
PUS_SERVICE_19 = 19,
PUS_SERVICE_20 = 20,
PUS_SERVICE_23 = 23,
PUS_SERVICE_200 = 200,
PUS_SERVICE_201 = 201,
};
};
#endif /* CONFIG_TMTC_PUSIDS_HPP_ */

View File

@ -4,7 +4,7 @@
#include <stdint.h>
#include "fwSubsystemIdRanges.h"
//could be move to more suitable location
#include <config/tmtc/subsystemIdRanges.h>
#include <subsystemIdRanges.h>
typedef uint16_t EventId_t;
typedef uint8_t EventSeverity_t;

View File

@ -1,5 +1,7 @@
#include "EventManager.h"
#include "EventMessage.h"
#include <FSFWConfig.h>
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../ipc/QueueFactory.h"
#include "../ipc/MutexFactory.h"
@ -12,8 +14,10 @@ const uint16_t EventManager::POOL_SIZES[N_POOLS] = {
// objects registering for certain events.
// Each listener requires 1 or 2 EventIdMatcher and 1 or 2 ReportRangeMatcher.
// So a good guess is 75 to a max of 100 pools required for each, which fits well.
// SHOULDDO: Shouldn't this be in the config folder and passed via ctor?
const uint16_t EventManager::N_ELEMENTS[N_POOLS] = { 240, 120, 120 };
const uint16_t EventManager::N_ELEMENTS[N_POOLS] = {
fsfwconfig::FSFW_EVENTMGMR_MATCHTREE_NODES ,
fsfwconfig::FSFW_EVENTMGMT_EVENTIDMATCHERS,
fsfwconfig::FSFW_EVENTMGMR_RANGEMATCHERS };
EventManager::EventManager(object_id_t setObjectId) :
SystemObject(setObjectId),

13
fsfw.mk
View File

@ -28,12 +28,25 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/*.cpp)
# select the OS
ifeq ($(OS_FSFW),rtems)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/rtems/*.cpp)
else ifeq ($(OS_FSFW),linux)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/linux/*.cpp)
else ifeq ($(OS_FSFW),freeRTOS)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/FreeRTOS/*.cpp)
else ifeq ($(OS_FSFW),host)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/host/*.cpp)
ifeq ($(OS),Windows_NT)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/osal/windows/*.cpp)
else
# For now, the linux UDP bridge sources needs to be included manually by upper makefile
# for host OS because we can't be sure the OS is linux.
# Following lines can be used to do this:
# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TcUnixUdpPollingTask.cpp
# CXXSRC += $(FRAMEWORK_PATH)/osal/linux/TmTcUnixUdpBridge.cpp
endif
else
$(error invalid OS_FSFW specified, valid OS_FSFW are rtems, linux, freeRTOS, host)
endif

View File

@ -1,5 +1,5 @@
#ifndef HASHEALTHIF_H_
#define HASHEALTHIF_H_
#ifndef FSFW_HEALTH_HASHEALTHIF_H_
#define FSFW_HEALTH_HASHEALTHIF_H_
#include "../events/Event.h"
#include "../returnvalues/HasReturnvaluesIF.h"
@ -8,9 +8,13 @@
class HasHealthIF {
public:
typedef enum {
HEALTHY = 1, FAULTY = 0, EXTERNAL_CONTROL = 2, NEEDS_RECOVERY = 3, PERMANENT_FAULTY = 4
} HealthState;
enum HealthState: uint8_t {
HEALTHY = 1,
FAULTY = 0,
EXTERNAL_CONTROL = 2,
NEEDS_RECOVERY = 3,
PERMANENT_FAULTY = 4
};
static const uint8_t INTERFACE_ID = CLASS_ID::HAS_HEALTH_IF;
static const ReturnValue_t OBJECT_NOT_HEALTHY = MAKE_RETURN_CODE(1);
@ -31,20 +35,17 @@ public:
virtual MessageQueueId_t getCommandQueue() const = 0;
/**
* set the Health State
*
* @brief Set the Health State
* The parent will be informed, if the Health changes
*
* @param health
*/
virtual ReturnValue_t setHealth(HealthState health) = 0;
/**
* get Health State
*
* @brief Get Health State
* @return Health State of the object
*/
virtual HasHealthIF::HealthState getHealth() = 0;
};
#endif /* HASHEALTHIF_H_ */
#endif /* FSFW_HEALTH_HASHEALTHIF_H_ */

View File

@ -12,13 +12,15 @@
#include "../returnvalues/HasReturnvaluesIF.h"
/**
* Helper class for Objects that implement HasHealthIF
* @brief Helper class for Objects that implement HasHealthIF
* @details
* It takes care of registering with the Health Table as well as handling
* health commands (including replying to the sender) and updating
* the Health Table.
*
* It takes care of registering with the Health Table as well as handling health commands
* (including replying to the sender) and updating the Health Table.
*
* If a parent is set in the ctor, the parent will be informed with a @c HEALTH_INFO message
* about changes in the health state. Note that a @c HEALTH_INFO is only generated if the Health
* If a parent is set in the ctor, the parent will be informed with a
* @c HEALTH_INFO message about changes in the health state.
* Note that a @c HEALTH_INFO is only generated if the Health
* changes, not for all @c HEALTH_SET commands received.
*
* It does NOT handle @c HEALTH_INFO messages
@ -27,10 +29,9 @@ class HealthHelper {
public:
/**
* ctor
*
* @param owner
* @param objectId the object Id to use when communication with the HealthTable
* @param objectId The object Id to use when communication with
* the HealthTable
*/
HealthHelper(HasHealthIF* owner, object_id_t objectId);
@ -57,7 +58,8 @@ public:
* @param message
* @return
* -@c RETURN_OK if the message was handled
* -@c RETURN_FAILED if the message could not be handled (ie it was not a @c HEALTH_SET or @c HEALTH_READ message)
* -@c RETURN_FAILED if the message could not be handled
* (ie it was not a @c HEALTH_SET or @c HEALTH_READ message)
*/
ReturnValue_t handleHealthCommand(CommandMessage *message);
@ -78,15 +80,18 @@ public:
HasHealthIF::HealthState getHealth();
/**
* @param parentQueue the Queue id of the parent object. Set to 0 if no parent present
* @param parentQueue The queue ID of the parent object.
* Set to 0 if no parent present
*/
void setParentQueue(MessageQueueId_t parentQueue);
/**
*
* @param parentQueue the Queue id of the parent object. Set to 0 if no parent present
* @param parentQueue The queue ID of the parent object.
* Set to 0 if no parent present
* @return
* -@c RETURN_OK if the Health Table was found and the object could be registered
* -@c RETURN_OK if the Health Table was found and the object
* could be registered
* -@c RETURN_FAILED else
*/
ReturnValue_t initialize(MessageQueueId_t parentQueue );
@ -110,11 +115,15 @@ private:
HasHealthIF* owner;
/**
* if the #parentQueue is not NULL, a @c HEALTH_INFO message will be sent to this queue
* @param health the health is passed as parameter so that the number of calls to the health table can be minimized
* if the #parentQueue is not NULL, a @c HEALTH_INFO message
* will be sent to this queue
* @param health
* The health is passed as parameter so that the number of
* calls to the health table can be minimized
* @param oldHealth information of the previous health state.
*/
void informParent(HasHealthIF::HealthState health, HasHealthIF::HealthState oldHealth);
void informParent(HasHealthIF::HealthState health,
HasHealthIF::HealthState oldHealth);
void handleSetHealthCommand(CommandMessage *message);
};

View File

@ -7,11 +7,13 @@ void HealthMessage::setHealthMessage(CommandMessage* message, Command_t command,
message->setParameter2(oldHealth);
}
void HealthMessage::setHealthMessage(CommandMessage* message, Command_t command) {
void HealthMessage::setHealthMessage(CommandMessage* message,
Command_t command) {
message->setCommand(command);
}
HasHealthIF::HealthState HealthMessage::getHealth(const CommandMessage* message) {
HasHealthIF::HealthState HealthMessage::getHealth(
const CommandMessage* message) {
return (HasHealthIF::HealthState) message->getParameter();
}

View File

@ -1,5 +1,5 @@
#ifndef HEALTHMESSAGE_H_
#define HEALTHMESSAGE_H_
#ifndef FSFW_HEALTH_HEALTHMESSAGE_H_
#define FSFW_HEALTH_HEALTHMESSAGE_H_
#include "HasHealthIF.h"
#include "../ipc/CommandMessage.h"
@ -7,14 +7,20 @@
class HealthMessage {
public:
static const uint8_t MESSAGE_ID = messagetypes::HEALTH_COMMAND;
static const Command_t HEALTH_SET = MAKE_COMMAND_ID(1);//REPLY_COMMAND_OK/REPLY_REJECTED
static const Command_t HEALTH_ANNOUNCE = MAKE_COMMAND_ID(3); //NO REPLY!
static const Command_t HEALTH_SET = MAKE_COMMAND_ID(1);
// No reply expected, health will be announced as event!
static const Command_t HEALTH_ANNOUNCE = MAKE_COMMAND_ID(2);
// Same as before, but all objects in health table will
// announce their health as events.
static const Command_t HEALTH_ANNOUNCE_ALL = MAKE_COMMAND_ID(3);
static const Command_t HEALTH_INFO = MAKE_COMMAND_ID(5);
static const Command_t REPLY_HEALTH_SET = MAKE_COMMAND_ID(6);
static void setHealthMessage(CommandMessage *message, Command_t command,
HasHealthIF::HealthState health, HasHealthIF::HealthState oldHealth = HasHealthIF::FAULTY);
HasHealthIF::HealthState health,
HasHealthIF::HealthState oldHealth = HasHealthIF::FAULTY);
static void setHealthMessage(CommandMessage *message, Command_t command);
static HasHealthIF::HealthState getHealth(const CommandMessage *message);
@ -27,4 +33,4 @@ private:
HealthMessage();
};
#endif /* HEALTHMESSAGE_H_ */
#endif /* FSFW_HEALTH_HEALTHMESSAGE_H_ */

View File

@ -1,6 +1,7 @@
#include "HealthTable.h"
#include "../serialize/SerializeAdapter.h"
#include "../ipc/MutexHelper.h"
#include "../ipc/MutexFactory.h"
#include "../serialize/SerializeAdapter.h"
HealthTable::HealthTable(object_id_t objectid) :
SystemObject(objectid) {
@ -9,6 +10,12 @@ HealthTable::HealthTable(object_id_t objectid) :
mapIterator = healthMap.begin();
}
void HealthTable::setMutexTimeout(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
this->timeoutType = timeoutType;
this->mutexTimeoutMs = timeoutMs;
}
HealthTable::~HealthTable() {
MutexFactory::instance()->deleteMutex(mutex);
}
@ -18,74 +25,63 @@ ReturnValue_t HealthTable::registerObject(object_id_t object,
if (healthMap.count(object) != 0) {
return HasReturnvaluesIF::RETURN_FAILED;
}
healthMap.insert(
std::pair<object_id_t, HasHealthIF::HealthState>(object,
initilialState));
healthMap.emplace(object, initilialState);
return HasReturnvaluesIF::RETURN_OK;
}
void HealthTable::setHealth(object_id_t object,
HasHealthIF::HealthState newState) {
mutex->lockMutex(MutexIF::BLOCKING);
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
iter->second = newState;
}
mutex->unlockMutex();
}
HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
HasHealthIF::HealthState state = HasHealthIF::HEALTHY;
mutex->lockMutex(MutexIF::BLOCKING);
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
state = iter->second;
}
mutex->unlockMutex();
return state;
}
uint32_t HealthTable::getPrintSize() {
mutex->lockMutex(MutexIF::BLOCKING);
uint32_t size = healthMap.size() * 5 + 2;
mutex->unlockMutex();
bool HealthTable::hasHealth(object_id_t object) {
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
return true;
}
return false;
}
size_t HealthTable::getPrintSize() {
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
uint32_t size = healthMap.size() * sizeof(object_id_t) +
sizeof(HasHealthIF::HealthState) + sizeof(uint16_t);
return size;
}
bool HealthTable::hasHealth(object_id_t object) {
bool exits = false;
mutex->lockMutex(MutexIF::BLOCKING);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
exits = true;
}
mutex->unlockMutex();
return exits;
}
void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
mutex->lockMutex(MutexIF::BLOCKING);
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
size_t size = 0;
uint16_t count = healthMap.size();
ReturnValue_t result = SerializeAdapter::serialize(&count,
SerializeAdapter::serialize(&count,
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
HealthMap::iterator iter;
for (iter = healthMap.begin();
iter != healthMap.end() && result == HasReturnvaluesIF::RETURN_OK;
++iter) {
result = SerializeAdapter::serialize(&iter->first,
for (const auto& health: healthMap) {
SerializeAdapter::serialize(&health.first,
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
uint8_t health = iter->second;
result = SerializeAdapter::serialize(&health, &pointer, &size,
uint8_t healthValue = health.second;
SerializeAdapter::serialize(&healthValue, &pointer, &size,
maxSize, SerializeIF::Endianness::BIG);
}
mutex->unlockMutex();
}
ReturnValue_t HealthTable::iterate(
std::pair<object_id_t, HasHealthIF::HealthState> *value, bool reset) {
ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
mutex->lockMutex(MutexIF::BLOCKING);
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
if (reset) {
mapIterator = healthMap.begin();
}
@ -94,7 +90,5 @@ ReturnValue_t HealthTable::iterate(
}
*value = *mapIterator;
mapIterator++;
mutex->unlockMutex();
return result;
}

View File

@ -1,35 +1,47 @@
#ifndef HEALTHTABLE_H_
#define HEALTHTABLE_H_
#ifndef FSFW_HEALTH_HEALTHTABLE_H_
#define FSFW_HEALTH_HEALTHTABLE_H_
#include "HealthTableIF.h"
#include "../objectmanager/SystemObject.h"
#include "../ipc/MutexIF.h"
#include <map>
typedef std::map<object_id_t, HasHealthIF::HealthState> HealthMap;
class HealthTable: public HealthTableIF, public SystemObject {
public:
HealthTable(object_id_t objectid);
virtual ~HealthTable();
void setMutexTimeout(MutexIF::TimeoutType timeoutType, uint32_t timeoutMs);
/** HealthTableIF overrides */
virtual ReturnValue_t registerObject(object_id_t object,
HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY);
HasHealthIF::HealthState initilialState =
HasHealthIF::HEALTHY) override;
virtual size_t getPrintSize() override;
virtual void printAll(uint8_t *pointer, size_t maxSize) override;
virtual bool hasHealth(object_id_t object);
virtual void setHealth(object_id_t object, HasHealthIF::HealthState newState);
virtual HasHealthIF::HealthState getHealth(object_id_t);
virtual uint32_t getPrintSize();
virtual void printAll(uint8_t *pointer, size_t maxSize);
/** ManagesHealthIF overrides */
virtual bool hasHealth(object_id_t object) override;
virtual void setHealth(object_id_t object,
HasHealthIF::HealthState newState) override;
virtual HasHealthIF::HealthState getHealth(object_id_t) override;
protected:
using HealthMap = std::map<object_id_t, HasHealthIF::HealthState>;
using HealthEntry = std::pair<object_id_t, HasHealthIF::HealthState>;
MutexIF* mutex;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeoutMs = 20;
HealthMap healthMap;
HealthMap::iterator mapIterator;
virtual ReturnValue_t iterate(std::pair<object_id_t,HasHealthIF::HealthState> *value, bool reset = false);
virtual ReturnValue_t iterate(
HealthEntry* value,
bool reset = false) override;
};
#endif /* HEALTHTABLE_H_ */
#endif /* FSFW_HEALTH_HEALTHTABLE_H_ */

View File

@ -1,26 +1,24 @@
#ifndef HEALTHTABLEIF_H_
#define HEALTHTABLEIF_H_
#ifndef FSFW_HEALTH_HEALTHTABLEIF_H_
#define FSFW_HEALTH_HEALTHTABLEIF_H_
#include "ManagesHealthIF.h"
#include "../objectmanager/ObjectManagerIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include <map>
class HealthTableIF: public ManagesHealthIF {
friend class HealthCommandingService;
public:
virtual ~HealthTableIF() {
}
virtual ~HealthTableIF() {}
virtual ReturnValue_t registerObject(object_id_t object,
HasHealthIF::HealthState initilialState = HasHealthIF::HEALTHY) = 0;
virtual uint32_t getPrintSize() = 0;
virtual size_t getPrintSize() = 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;
virtual ReturnValue_t iterate(
std::pair<object_id_t,HasHealthIF::HealthState> *value,
bool reset = false) = 0;
};
#endif /* HEALTHTABLEIF_H_ */
#endif /* FRAMEWORK_HEALTH_HEALTHTABLEIF_H_ */

View File

@ -1,8 +1,9 @@
#ifndef FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_
#define FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_
#ifndef FSFW_HEALTH_MANAGESHEALTHIF_H_
#define FSFW_HEALTH_MANAGESHEALTHIF_H_
#include "HasHealthIF.h"
#include "../objectmanager/ObjectManagerIF.h"
class ManagesHealthIF {
public:
virtual ~ManagesHealthIF() {
@ -49,4 +50,4 @@ public:
}
};
#endif /* FRAMEWORK_HEALTH_MANAGESHEALTHIF_H_ */
#endif /* FSFW_HEALTH_MANAGESHEALTHIF_H_ */

View File

@ -1,6 +1,11 @@
#include "MessageQueue.h"
#include "../../ipc/MessageQueueSenderIF.h"
#include "../../ipc/MessageQueueMessageIF.h"
#include "../../ipc/QueueFactory.h"
#include "../../osal/host/MessageQueue.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include <cstring>
QueueFactory* QueueFactory::factoryInstance = nullptr;

View File

@ -0,0 +1,148 @@
#include "TcWinUdpPollingTask.h"
#include "../../globalfunctions/arrayprinter.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include <winsock2.h>
#include <windows.h>
TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
object_id_t tmtcUnixUdpBridge, size_t frameSize,
double timeoutSeconds): SystemObject(objectId),
tmtcBridgeId(tmtcUnixUdpBridge) {
if(frameSize > 0) {
this->frameSize = frameSize;
}
else {
this->frameSize = DEFAULT_MAX_FRAME_SIZE;
}
// Set up reception buffer with specified frame size.
// For now, it is assumed that only one frame is held in the buffer!
receptionBuffer.reserve(this->frameSize);
receptionBuffer.resize(this->frameSize);
if(timeoutSeconds == -1) {
receptionTimeout = DEFAULT_TIMEOUT;
}
else {
receptionTimeout = timevalOperations::toTimeval(timeoutSeconds);
}
}
TcWinUdpPollingTask::~TcWinUdpPollingTask() {}
ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
// Poll for new UDP datagrams in permanent loop.
while(true) {
//! Sender Address is cached here.
struct sockaddr_in senderAddress;
int senderAddressSize = sizeof(senderAddress);
ssize_t bytesReceived = recvfrom(serverUdpSocket,
reinterpret_cast<char*>(receptionBuffer.data()), frameSize,
receptionFlags, reinterpret_cast<sockaddr*>(&senderAddress),
&senderAddressSize);
if(bytesReceived == SOCKET_ERROR) {
// handle error
sif::error << "TcWinUdpPollingTask::performOperation: Reception"
" error." << std::endl;
handleReadError();
continue;
}
//sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived
// << " bytes received" << std::endl;
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
if(result != HasReturnvaluesIF::RETURN_FAILED) {
}
tmtcBridge->registerCommConnect();
tmtcBridge->checkAndSetClientAddress(senderAddress);
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
store_address_t storeId;
ReturnValue_t result = tcStore->addData(&storeId,
receptionBuffer.data(), bytesRead);
// arrayprinter::print(receptionBuffer.data(), bytesRead);
if (result != HasReturnvaluesIF::RETURN_OK) {
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data "
"storage failed" << std::endl;
sif::error << "Packet size: " << bytesRead << std::endl;
return HasReturnvaluesIF::RETURN_FAILED;
}
TmTcMessage message(storeId);
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
if (result != HasReturnvaluesIF::RETURN_OK) {
sif::error << "Serial Polling: Sending message to queue failed"
<< std::endl;
tcStore->deleteData(storeId);
}
return result;
}
ReturnValue_t TcWinUdpPollingTask::initialize() {
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
if (tcStore == nullptr) {
sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!"
<< std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
tmtcBridge = objectManager->get<TmTcWinUdpBridge>(tmtcBridgeId);
if(tmtcBridge == nullptr) {
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid"
" TMTC bridge object!" << std::endl;
return ObjectManagerIF::CHILD_INIT_FAILED;
}
serverUdpSocket = tmtcBridge->serverSocket;
//sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket "
// << serverUdpSocket << std::endl;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() {
// Initialize the destination after task creation. This ensures
// that the destination has already been set in the TMTC bridge.
targetTcDestination = tmtcBridge->getRequestQueue();
return HasReturnvaluesIF::RETURN_OK;
}
void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) {
DWORD timeoutMs = timeoutSeconds * 1000.0;
int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*>(&timeoutMs), sizeof(DWORD));
if(result == -1) {
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting "
"receive timeout failed with " << strerror(errno) << std::endl;
}
}
void TcWinUdpPollingTask::handleReadError() {
int error = WSAGetLastError();
switch(error) {
case(WSANOTINITIALISED): {
sif::info << "TmTcWinUdpBridge::handleReadError: WSANOTINITIALISED: "
<< "WSAStartup(...) call " << "necessary" << std::endl;
break;
}
case(WSAEFAULT): {
sif::info << "TmTcWinUdpBridge::handleReadError: WSADEFAULT: "
<< "Bad address " << std::endl;
break;
}
default: {
sif::info << "TmTcWinUdpBridge::handleReadError: Error code: "
<< error << std::endl;
break;
}
}
// to prevent spam.
Sleep(1000);
}

View File

@ -0,0 +1,67 @@
#ifndef FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_
#define FSFW_OSAL_WINDOWS_TCSOCKETPOLLINGTASK_H_
#include "TmTcWinUdpBridge.h"
#include "../../objectmanager/SystemObject.h"
#include "../../tasks/ExecutableObjectIF.h"
#include "../../storagemanager/StorageManagerIF.h"
#include <vector>
/**
* @brief This class can be used to implement the polling of a Unix socket,
* using UDP for now.
* @details
* The task will be blocked while the specified number of bytes has not been
* received, so TC reception is handled inside a separate task.
* This class caches the IP address of the sender. It is assumed there
* is only one sender for now.
*/
class TcWinUdpPollingTask: public SystemObject,
public ExecutableObjectIF {
friend class TmTcWinUdpBridge;
public:
static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048;
//! 0.5 default milliseconds timeout for now.
static constexpr timeval DEFAULT_TIMEOUT = {.tv_sec = 0, .tv_usec = 500};
TcWinUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
size_t frameSize = 0, double timeoutSeconds = -1);
virtual~ TcWinUdpPollingTask();
/**
* Turn on optional timeout for UDP polling. In the default mode,
* the receive function will block until a packet is received.
* @param timeoutSeconds
*/
void setTimeout(double timeoutSeconds);
virtual ReturnValue_t performOperation(uint8_t opCode) override;
virtual ReturnValue_t initialize() override;
virtual ReturnValue_t initializeAfterTaskCreation() override;
protected:
StorageManagerIF* tcStore = nullptr;
private:
//! TMTC bridge is cached.
object_id_t tmtcBridgeId = objects::NO_OBJECT;
TmTcWinUdpBridge* tmtcBridge = nullptr;
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
//! Reception flags: https://linux.die.net/man/2/recvfrom.
int receptionFlags = 0;
//! Server socket, which is member of TMTC bridge and is assigned in
//! constructor
SOCKET serverUdpSocket = 0;
std::vector<uint8_t> receptionBuffer;
size_t frameSize = 0;
timeval receptionTimeout;
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
void handleReadError();
};
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */

View File

@ -0,0 +1,176 @@
#include <fsfw/ipc/MutexHelper.h>
#include "TmTcWinUdpBridge.h"
TmTcWinUdpBridge::TmTcWinUdpBridge(object_id_t objectId,
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId,
uint16_t serverPort, uint16_t clientPort):
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
mutex = MutexFactory::instance()->createMutex();
// Initiates Winsock DLL.
WSAData wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int err = WSAStartup(wVersionRequested, &wsaData);
if (err != 0) {
/* Tell the user that we could not find a usable */
/* Winsock DLL. */
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge:"
"WSAStartup failed with error: " << err << std::endl;
return;
}
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT;
if(serverPort != 0xFFFF) {
setServerPort = serverPort;
}
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT;
if(clientPort != 0xFFFF) {
setClientPort = clientPort;
}
// Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html
//clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(serverSocket == INVALID_SOCKET) {
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not open"
" UDP socket!" << std::endl;
handleSocketError();
return;
}
serverAddress.sin_family = AF_INET;
// Accept packets from any interface. (potentially insecure).
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(setServerPort);
serverAddressLen = sizeof(serverAddress);
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
reinterpret_cast<const char*>(&serverSocketOptions),
sizeof(serverSocketOptions));
clientAddress.sin_family = AF_INET;
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
clientAddress.sin_port = htons(setClientPort);
clientAddressLen = sizeof(clientAddress);
int result = bind(serverSocket,
reinterpret_cast<struct sockaddr*>(&serverAddress),
serverAddressLen);
if(result != 0) {
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: Could not bind "
"local port " << setServerPort << " to server socket!"
<< std::endl;
handleBindError();
}
}
TmTcWinUdpBridge::~TmTcWinUdpBridge() {
WSACleanup();
}
ReturnValue_t TmTcWinUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
int flags = 0;
//clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
//clientAddressLen = sizeof(serverAddress);
// char ipAddress [15];
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
ssize_t bytesSent = sendto(serverSocket,
reinterpret_cast<const char*>(data), dataLen, flags,
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen);
if(bytesSent == SOCKET_ERROR) {
sif::error << "TmTcWinUdpBridge::sendTm: Send operation failed."
<< std::endl;
handleSendError();
}
// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
// " sent." << std::endl;
return HasReturnvaluesIF::RETURN_OK;
return HasReturnvaluesIF::RETURN_OK;
}
void TmTcWinUdpBridge::checkAndSetClientAddress(sockaddr_in newAddress) {
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
// char ipAddress [15];
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
// sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
// Set new IP address if it has changed.
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr;
clientAddressLen = sizeof(clientAddress);
}
}
void TmTcWinUdpBridge::handleSocketError() {
int errCode = WSAGetLastError();
switch(errCode) {
case(WSANOTINITIALISED): {
sif::info << "TmTcWinUdpBridge::handleSocketError: WSANOTINITIALISED: "
<< "WSAStartup(...) call " << "necessary" << std::endl;
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
sif::info << "TmTcWinUdpBridge::handleSocketError: Error code: "
<< errCode << std::endl;
break;
}
}
}
void TmTcWinUdpBridge::handleBindError() {
int errCode = WSAGetLastError();
switch(errCode) {
case(WSANOTINITIALISED): {
sif::info << "TmTcWinUdpBridge::handleBindError: WSANOTINITIALISED: "
<< "WSAStartup(...) call " << "necessary" << std::endl;
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
sif::info << "TmTcWinUdpBridge::handleBindError: Error code: "
<< errCode << std::endl;
break;
}
}
}
void TmTcWinUdpBridge::handleSendError() {
int errCode = WSAGetLastError();
switch(errCode) {
case(WSANOTINITIALISED): {
sif::info << "TmTcWinUdpBridge::handleSendError: WSANOTINITIALISED: "
<< "WSAStartup(...) call " << "necessary" << std::endl;
break;
}
case(WSAEADDRNOTAVAIL): {
sif::info << "TmTcWinUdpBridge::handleReadError: WSAEADDRNOTAVAIL: "
<< "Check target address. " << std::endl;
break;
}
default: {
/*
https://docs.microsoft.com/en-us/windows/win32/winsock/
windows-sockets-error-codes-2
*/
sif::info << "TmTcWinUdpBridge::handleSendError: Error code: "
<< errCode << std::endl;
break;
}
}
}

View File

@ -0,0 +1,49 @@
#ifndef FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_
#define FSFW_OSAL_WINDOWS_TMTCWINUDPBRIDGE_H_
#include "../../tmtcservices/TmTcBridge.h"
#include <winsock2.h>
#include <windows.h>
class TmTcWinUdpBridge: public TmTcBridge {
friend class TcWinUdpPollingTask;
public:
// The ports chosen here should not be used by any other process.
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301;
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302;
TmTcWinUdpBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId,
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF);
virtual~ TmTcWinUdpBridge();
void checkAndSetClientAddress(sockaddr_in clientAddress);
protected:
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
private:
SOCKET serverSocket = 0;
const int serverSocketOptions = 0;
struct sockaddr_in clientAddress;
int clientAddressLen = 0;
struct sockaddr_in serverAddress;
int serverAddressLen = 0;
//! Access to the client address is mutex protected as it is set
//! by another task.
MutexIF* mutex;
void handleSocketError();
void handleBindError();
void handleSendError();
};
#endif /* FSFW_OSAL_HOST_TMTCWINUDPBRIDGE_H_ */

View File

@ -1,8 +1,8 @@
#ifndef FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_
#define FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_
#ifndef FSFW_RETURNVALUES_HASRETURNVALUESIF_H_
#define FSFW_RETURNVALUES_HASRETURNVALUESIF_H_
#include "FwClassIds.h"
#include <config/returnvalues/classIds.h>
#include <returnvalues/classIds.h>
#include <cstdint>
#define MAKE_RETURN_CODE( number ) ((INTERFACE_ID << 8) + (number))
@ -15,9 +15,17 @@ public:
static const ReturnValue_t RETURN_FAILED = 1;
virtual ~HasReturnvaluesIF() {}
static ReturnValue_t makeReturnCode(uint8_t interfaceId, uint8_t number) {
return (interfaceId << 8) + number;
/**
* It is discouraged to use the input parameters 0,0 and 0,1 as this
* will generate the RETURN_OK and RETURN_FAILED returnvalues.
* @param interfaceId
* @param number
* @return
*/
static constexpr ReturnValue_t makeReturnCode(uint8_t interfaceId,
uint8_t number) {
return (static_cast<ReturnValue_t>(interfaceId) << 8) + number;
}
};
#endif /* FRAMEWORK_RETURNVALUES_HASRETURNVALUESIF_H_ */
#endif /* FSFW_RETURNVALUES_HASRETURNVALUESIF_H_ */

View File

@ -1,8 +1,9 @@
#include "../timemanager/CCSDSTime.h"
#include "CCSDSTime.h"
#include <cstdio>
#include <cinttypes>
#include <cmath>
#include <FSFWConfig.h>
CCSDSTime::CCSDSTime() {
}
@ -158,15 +159,16 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
}
// 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
#if FSFW_NO_C99_IO == 1
uint16_t year;
uint16_t month;
uint16_t day;
uint16_t hour;
uint16_t minute;
float second;
int count = sscanf((char *) from, "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%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;
@ -179,12 +181,13 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
}
// try Code B (yyyy-ddd)
count = sscanf((char *) from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 ":%2" SCNu16 ":%fZ", &year, &day,
&hour, &minute, &second);
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));
reinterpret_cast<uint8_t *>(&month),
reinterpret_cast<uint8_t *>(&tempDay));
if (result != RETURN_OK) {
return RETURN_FAILED;
}

59
unittest/README.md Normal file
View File

@ -0,0 +1,59 @@
## FSFW Testing
This repository contains testing and unit testing components.
[Catch2](https://github.com/catchorg/Catch2) has been used as a framework,
and these unit tests can only be run on a linux host machine.
The makefile with default settings creates the unit test binary which can be
run in the terminal or in eclipse.
### Instructions
To run the fsfw unittests in the project, perform following steps:
1. Copy the testcfg folder the project root (folder containing the FSFW).
2. There is a makefile inside the testcfg folder which can be used to have
a starting point to compile the unit tests. Copy that Makefile to the project
root
3. Create a folder named catch2 (can have other name which requires Makefile
adaption) and copy the Catch2 header files there (NOTE: CMake support
not enabled yet!)
### Eclipse CDT settings
The default eclipse terminal has issues displaying the colors used
when running the unit test binary by catch2. To fix this issue,
install the ANSI Escape In Console package from the eclipse marketplace.
### GCOV integration
GCOV has been integrated as a code coverage tool.
It can be enabled by adding `GCOV=1` to the build process as an additional argument.
Coverage data will be provided in form of .gcno and .gcda files.
These can be displayed in eclipse by looking
for a .gcno or .gcda file in the \_obj folder, double-clicking it
and picking the right source-binary. This will generate
information about which lines of a file have run, provided it is open in
eclipse.
### LCOV integration
The files generated by GCOV can also be processed by the tool LCOV.
On ubuntu, the tool can be installed with the following command:
```sh
sudo apt-get install lcov
````
After that, the tool can be run by building the unit tests with `GCOV=1`,
running them at least one time and then executing the `lcov.sh` script.
### Adding unit tests
The catch unit tests are located in unittest/testfw. To add new unit tests,
add them to the UnitTestCatch.cpp file or add a new source file which
includes catch.hpp.
For writing basics tests, the [assertion documentation](https://github.com/catchorg/Catch2/blob/master/docs/assertions.md#top)
or the existing examples are a good guideliens.
For more advanced tests, refer to the [catch2 documentation](https://github.com/catchorg/Catch2/blob/master/docs/Readme.md#top).

View File

@ -0,0 +1,11 @@
#include "CatchDefinitions.h"
#include <fsfw/objectmanager/ObjectManagerIF.h>
StorageManagerIF* tglob::getIpcStoreHandle() {
if(objectManager != nullptr) {
return objectManager->get<StorageManagerIF>(objects::IPC_STORE);
} else {
sif::error << "Global object manager uninitialized" << std::endl;
return nullptr;
}
}

View File

@ -0,0 +1,21 @@
#ifndef FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
#define FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_
#include <fsfw/ipc/messageQueueDefinitions.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <fsfw/storagemanager/StorageManagerIF.h>
namespace retval {
static constexpr int CATCH_OK = static_cast<int>(HasReturnvaluesIF::RETURN_OK);
static constexpr int CATCH_FAILED = static_cast<int>(HasReturnvaluesIF::RETURN_FAILED);
}
namespace tconst {
static constexpr MessageQueueId_t testQueueId = 42;
}
namespace tglob {
StorageManagerIF* getIpcStoreHandle();
}
#endif /* FSFW_UNITTEST_CORE_CATCHDEFINITIONS_H_ */

View File

@ -0,0 +1,31 @@
/**
* @file CatchSource.cpp
* @brief Source file to compile catch framework.
* @details All tests should be written in other files.
* For eclipse console output, install ANSI Escape in Console
* from the eclipse market place to get colored characters.
*/
#ifndef NO_UNIT_TEST_FRAMEWORK
#define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp>
#if CUSTOM_UNITTEST_RUNNER == 0
extern int customSetup();
int main( int argc, char* argv[] ) {
customSetup();
// Catch internal function call
int result = Catch::Session().run( argc, argv );
// global clean-up
return result;
}
#endif
#endif

View File

@ -0,0 +1,42 @@
#include "CatchDefinitions.h"
#include <testcfg/cdatapool/dataPoolInit.h>
#include <testcfg/objects/Factory.h>
#ifdef GCOV
#include <gcov.h>
#endif
#include "../../objectmanager/ObjectManager.h"
#include "../../objectmanager/ObjectManagerIF.h"
#include "../../storagemanager/StorageManagerIF.h"
#include "../../datapool/DataPool.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
/* Global instantiations normally done in main.cpp */
/* Initialize Data Pool */
//namespace glob {
DataPool dataPool(datapool::dataPoolInit);
//}
namespace sif {
/* Set up output streams */
ServiceInterfaceStream debug("DEBUG");
ServiceInterfaceStream info("INFO");
ServiceInterfaceStream error("ERROR");
ServiceInterfaceStream warning("WARNING");
}
/* Global object manager */
ObjectManagerIF *objectManager;
int customSetup() {
// global setup
objectManager = new ObjectManager(Factory::produce);
objectManager -> initialize();
return 0;
}

3
unittest/core/core.mk Normal file
View File

@ -0,0 +1,3 @@
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)
INCLUDES += $(CURRENTPATH)

View File

@ -0,0 +1,10 @@
#include <fsfw/unittest/core/printChar.h>
#include <cstdio>
void printChar(const char* character, bool errStream) {
if(errStream) {
std::putc(*character, stderr);
return;
}
std::putc(*character, stdout);
}

View File

@ -0,0 +1,8 @@
#ifndef FSFW_UNITTEST_CORE_PRINTCHAR_H_
#define FSFW_UNITTEST_CORE_PRINTCHAR_H_
extern "C" void printChar(const char*, bool errStream);
#endif /* FSFW_UNITTEST_CORE_PRINTCHAR_H_ */

View File

@ -0,0 +1,27 @@
#include "InternalUnitTester.h"
#include "UnittDefinitions.h"
#include "osal/IntTestMq.h"
#include "osal/IntTestSemaphore.h"
#include "osal/IntTestMutex.h"
#include "serialize/IntTestSerialization.h"
#include <cstdlib>
InternalUnitTester::InternalUnitTester() {}
InternalUnitTester::~InternalUnitTester() {}
ReturnValue_t InternalUnitTester::performTests() {
sif::info << "Running internal unit tests.." << std::endl;
testserialize::test_serialization();
testmq::testMq();
testsemaph::testBinSemaph();
testsemaph::testCountingSemaph();
testmutex::testMutex();
sif::info << "Internal unit tests finished." << std::endl;
return RETURN_OK;
}

View File

@ -0,0 +1,29 @@
#ifndef FRAMEWORK_TEST_UNITTESTCLASS_H_
#define FRAMEWORK_TEST_UNITTESTCLASS_H_
#include "UnittDefinitions.h"
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
/**
* @brief Can be used for internal testing, for example for hardware specific
* tests which can not be run on a host-machine.
*
* TODO: A lot of ways to improve this class. A way for tests to subscribe
* in this central class would be nice. Right now, this is the class
* which simply calls all other tests from other files manually.
* Maybe there is a better way..
*/
class InternalUnitTester: public HasReturnvaluesIF {
public:
InternalUnitTester();
virtual~ InternalUnitTester();
/**
* Some function which calls all other tests
* @return
*/
virtual ReturnValue_t performTests();
};
#endif /* FRAMEWORK_TEST_UNITTESTCLASS_H_ */

View File

@ -0,0 +1,7 @@
#include <fsfw/unittest/internal/UnittDefinitions.h>
ReturnValue_t unitt::put_error(std::string errorId) {
sif::error << "Unit Tester error: Failed at test ID "
<< errorId << "\n" << std::flush;
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -0,0 +1,33 @@
#ifndef UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
#define UNITTEST_INTERNAL_UNITTDEFINITIONS_H_
#include "../../returnvalues/HasReturnvaluesIF.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include <cstdint>
#include <cstddef>
namespace tv {
// POD test values
static const bool tv_bool = true;
static const uint8_t tv_uint8 {5};
static const uint16_t tv_uint16 {283};
static const uint32_t tv_uint32 {929221};
static const uint64_t tv_uint64 {2929329429};
static const int8_t tv_int8 {-16};
static const int16_t tv_int16 {-829};
static const int32_t tv_int32 {-2312};
static const float tv_float {8.2149214};
static const float tv_sfloat = {-922.2321321};
static const double tv_double {9.2132142141e8};
static const double tv_sdouble {-2.2421e19};
}
namespace unitt {
ReturnValue_t put_error(std::string errorId);
}
#endif /* UNITTEST_INTERNAL_UNITTDEFINITIONS_H_ */

View File

@ -0,0 +1,3 @@
CXXSRC += $(wildcard $(CURRENTPATH)/osal/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/serialize/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/*.cpp)

View File

@ -0,0 +1,52 @@
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/unittest/internal/osal/IntTestMq.h>
#include <fsfw/unittest/internal/UnittDefinitions.h>
#include <array>
using retval = HasReturnvaluesIF;
void testmq::testMq() {
std::string id = "[testMq]";
MessageQueueIF* testSenderMq =
QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testSenderMqId = testSenderMq->getId();
MessageQueueIF* testReceiverMq =
QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testReceiverMqId = testReceiverMq->getId();
std::array<uint8_t, 20> testData { 0 };
testData[0] = 42;
MessageQueueMessage testMessage(testData.data(), 1);
testSenderMq->setDefaultDestination(testReceiverMqId);
auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
if(result != retval::RETURN_OK) {
unitt::put_error(id);
}
MessageQueueMessage recvMessage;
result = testReceiverMq->receiveMessage(&recvMessage);
if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
unitt::put_error(id);
}
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
if(result != retval::RETURN_OK) {
unitt::put_error(id);
}
MessageQueueId_t senderId = 0;
result = testReceiverMq->receiveMessage(&recvMessage,&senderId);
if(result != retval::RETURN_OK or recvMessage.getData()[0] != 42) {
unitt::put_error(id);
}
if(senderId != testSenderMqId) {
unitt::put_error(id);
}
senderId = testReceiverMq->getLastPartner();
if(senderId != testSenderMqId) {
unitt::put_error(id);
}
}

View File

@ -0,0 +1,9 @@
#ifndef UNITTEST_INTERNAL_INTESTMQ_H_
#define UNITTEST_INTERNAL_INTESTMQ_H_
namespace testmq {
void testMq();
}
#endif /* UNITTEST_INTERNAL_INTESTMQ_H_ */

View File

@ -0,0 +1,42 @@
#include "IntTestMutex.h"
#include <fsfw/ipc/MutexFactory.h>
#include <unittest/internal/UnittDefinitions.h>
#if defined(hosted)
#include <fsfw/osal/hosted/Mutex.h>
#include <thread>
#include <future>
#endif
void testmutex::testMutex() {
std::string id = "[testMutex]";
MutexIF* mutex = MutexFactory::instance()->createMutex();
auto result = mutex->lockMutex(MutexIF::POLLING);
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
// timed_mutex from the C++ library specifies undefined behaviour if
// the timed mutex is locked twice from the same thread.
#if defined(hosted)
// hold on, this actually worked ? :-D This calls the function from
// another thread and stores the returnvalue in a future.
auto future = std::async(&MutexIF::lockMutex, mutex, 1);
result = future.get();
#else
result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, 1);
#endif
if(result != MutexIF::MUTEX_TIMEOUT) {
unitt::put_error(id);
}
result = mutex->unlockMutex();
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
result = mutex->unlockMutex();
if(result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) {
unitt::put_error(id);
}
}

View File

@ -0,0 +1,10 @@
#ifndef UNITTEST_INTERNAL_INTTESTMUTEX_H_
#define UNITTEST_INTERNAL_INTTESTMUTEX_H_
namespace testmutex {
void testMutex();
}
#endif /* UNITTEST_INTERNAL_INTTESTMUTEX_H_ */

View File

@ -0,0 +1,160 @@
#include "IntTestSemaphore.h"
#include <fsfw/tasks/SemaphoreFactory.h>
#include <unittest/internal/UnittDefinitions.h>
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
#include <fsfw/timemanager/Stopwatch.h>
void testsemaph::testBinSemaph() {
std::string id = "[BinSemaphore]";
SemaphoreIF* binSemaph =
SemaphoreFactory::instance()->createBinarySemaphore();
if(binSemaph == nullptr) {
return;
}
testBinSemaphoreImplementation(binSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(binSemaph);
#if defined(freeRTOS)
SemaphoreIF* binSemaphUsingTask =
SemaphoreFactory::instance()->createBinarySemaphore(1);
testBinSemaphoreImplementation(binSemaphUsingTask, id);
SemaphoreFactory::instance()->deleteSemaphore(binSemaphUsingTask);
#endif
}
void testsemaph::testCountingSemaph() {
std::string id = "[CountingSemaph]";
{
// First test: create a binary semaphore by using a counting semaphore.
SemaphoreIF* countingSemaph = SemaphoreFactory::instance()->
createCountingSemaphore(1,1);
if(countingSemaph == nullptr) {
return;
}
testBinSemaphoreImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#if defined(freeRTOS)
countingSemaph = SemaphoreFactory::instance()->
createCountingSemaphore(1, 1, 1);
testBinSemaphoreImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#endif
}
{
// Second test: counting semaphore with count 3 and init count of 3.
SemaphoreIF* countingSemaph = SemaphoreFactory::instance()->
createCountingSemaphore(3,3);
testCountingSemaphImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#if defined(freeRTOS)
countingSemaph = SemaphoreFactory::instance()->
createCountingSemaphore(3, 0, 1);
uint8_t semaphCount = countingSemaph->getSemaphoreCounter();
if(semaphCount != 0) {
unitt::put_error(id);
}
// release 3 times in a row
for(int i = 0; i < 3; i++) {
auto result = countingSemaph->release();
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
testCountingSemaphImplementation(countingSemaph, id);
SemaphoreFactory::instance()->deleteSemaphore(countingSemaph);
#endif
}
}
void testsemaph::testBinSemaphoreImplementation(SemaphoreIF* binSemaph,
std::string id) {
uint8_t semaphCount = binSemaph->getSemaphoreCounter();
if(semaphCount != 1) {
unitt::put_error(id);
}
ReturnValue_t result = binSemaph->release();
if(result != SemaphoreIF::SEMAPHORE_NOT_OWNED) {
unitt::put_error(id);
}
result = binSemaph->acquire(SemaphoreIF::BLOCKING);
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
// There is not really a point in testing time related, the task
// might get interrupted..
{
//Stopwatch stopwatch(false);
result = binSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10);
//dur_millis_t time = stopwatch.stop();
// if(abs(time - 10) > 2) {
// sif::error << "UnitTester: Semaphore timeout measured incorrect."
// << std::endl;
// unitt::put_error(id);
// }
}
if(result != SemaphoreIF::SEMAPHORE_TIMEOUT) {
unitt::put_error(id);
}
semaphCount = binSemaph->getSemaphoreCounter();
if(semaphCount != 0) {
unitt::put_error(id);
}
result = binSemaph->release();
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
void testsemaph::testCountingSemaphImplementation(SemaphoreIF* countingSemaph,
std::string id) {
// check count getter function
uint8_t semaphCount = countingSemaph->getSemaphoreCounter();
if(semaphCount != 3) {
unitt::put_error(id);
}
ReturnValue_t result = countingSemaph->release();
if(result != SemaphoreIF::SEMAPHORE_NOT_OWNED) {
unitt::put_error(id);
}
// acquire 3 times in a row
for(int i = 0; i < 3; i++) {
result = countingSemaph->acquire(SemaphoreIF::BLOCKING);
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
{
Stopwatch stopwatch(false);
// attempt to take when count is 0, measure time
result = countingSemaph->acquire(SemaphoreIF::TimeoutType::WAITING, 10);
dur_millis_t time = stopwatch.stop();
if(abs(time - 10) > 1) {
unitt::put_error(id);
}
}
if(result != SemaphoreIF::SEMAPHORE_TIMEOUT) {
unitt::put_error(id);
}
// release 3 times in a row
for(int i = 0; i < 3; i++) {
result = countingSemaph->release();
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
}
// assert correct full count
if(countingSemaph->getSemaphoreCounter() != 3) {
unitt::put_error(id);
}
}

View File

@ -0,0 +1,15 @@
#ifndef UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
#define UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_
class SemaphoreIF;
#include <string>
namespace testsemaph {
void testBinSemaph();
void testBinSemaphoreImplementation(SemaphoreIF* binSemaph, std::string id);
void testCountingSemaph();
void testCountingSemaphImplementation(SemaphoreIF* countingSemaph,
std::string id);
}
#endif /* UNITTEST_INTERNAL_INTTESTSEMAPHORE_H_ */

View File

@ -0,0 +1,230 @@
#include "IntTestSerialization.h"
#include <fsfw/serialize/SerializeElement.h>
#include <fsfw/serialize/SerialBufferAdapter.h>
#include <unittest/internal/UnittDefinitions.h>
#include <fsfw/serialize/SerializeIF.h>
#include <array>
using retval = HasReturnvaluesIF;
std::array<uint8_t, 512> testserialize::test_array = { 0 };
ReturnValue_t testserialize::test_serialization() {
// Here, we test all serialization tools. First test basic cases.
ReturnValue_t result = test_endianness_tools();
if(result != retval::RETURN_OK) {
return result;
}
result = test_autoserialization();
if(result != retval::RETURN_OK) {
return result;
}
result = test_serial_buffer_adapter();
if(result != retval::RETURN_OK) {
return result;
}
return retval::RETURN_OK;
}
ReturnValue_t testserialize::test_endianness_tools() {
std::string id = "[test_endianness_tools]";
test_array[0] = 0;
test_array[1] = 0;
uint16_t two_byte_value = 1;
size_t size = 0;
uint8_t* p_array = test_array.data();
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2,
SerializeIF::Endianness::MACHINE);
// Little endian: Value one on first byte
if(test_array[0] != 1 and test_array[1] != 0) {
return unitt::put_error(id);
}
p_array = test_array.data();
size = 0;
SerializeAdapter::serialize(&two_byte_value, &p_array, &size, 2,
SerializeIF::Endianness::BIG);
// Big endian: Value one on second byte
if(test_array[0] != 0 and test_array[1] != 1) {
return unitt::put_error(id);
}
return retval::RETURN_OK;
}
ReturnValue_t testserialize::test_autoserialization() {
std::string id = "[test_autoserialization]";
// Unit Test getSerializedSize
if(SerializeAdapter::
getSerializedSize(&tv::tv_bool) != sizeof(tv::tv_bool) or
SerializeAdapter::
getSerializedSize(&tv::tv_uint8) != sizeof(tv::tv_uint8) or
SerializeAdapter::
getSerializedSize(&tv::tv_uint16) != sizeof(tv::tv_uint16) or
SerializeAdapter::
getSerializedSize(&tv::tv_uint32) != sizeof(tv::tv_uint32) or
SerializeAdapter::
getSerializedSize(&tv::tv_uint64) != sizeof(tv::tv_uint64) or
SerializeAdapter::
getSerializedSize(&tv::tv_int8) != sizeof(tv::tv_int8) or
SerializeAdapter::
getSerializedSize(&tv::tv_double) != sizeof(tv::tv_double) or
SerializeAdapter::
getSerializedSize(&tv::tv_int16) != sizeof(tv::tv_int16) or
SerializeAdapter::
getSerializedSize(&tv::tv_int32) != sizeof(tv::tv_int32) or
SerializeAdapter::
getSerializedSize(&tv::tv_float) != sizeof(tv::tv_float))
{
return unitt::put_error(id);
}
size_t serialized_size = 0;
uint8_t * p_array = test_array.data();
SerializeAdapter::serialize(&tv::tv_bool, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint8, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint16, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint32, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int8, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int16, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_int32, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_uint64, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_float, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_double, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_sfloat, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv::tv_sdouble, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
// expected size is 1 + 1 + 2 + 4 + 1 + 2 + 4 + 8 + 4 + 8 + 4 + 8
if(serialized_size != 47) {
return unitt::put_error(id);
}
p_array = test_array.data();
size_t remaining_size = serialized_size;
bool tv_bool;
uint8_t tv_uint8;
uint16_t tv_uint16;
uint32_t tv_uint32;
int8_t tv_int8;
int16_t tv_int16;
int32_t tv_int32;
uint64_t tv_uint64;
float tv_float;
double tv_double;
float tv_sfloat;
double tv_sdouble;
SerializeAdapter::deSerialize(&tv_bool,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint8,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint16,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint32,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int8,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int16,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_int32,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_uint64,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_float,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_double,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_sfloat,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
SerializeAdapter::deSerialize(&tv_sdouble,
const_cast<const uint8_t**>(&p_array), &remaining_size, SerializeIF::Endianness::MACHINE);
if(tv_bool != tv::tv_bool or tv_uint8 != tv::tv_uint8 or
tv_uint16 != tv::tv_uint16 or tv_uint32 != tv::tv_uint32 or
tv_uint64 != tv::tv_uint64 or tv_int8 != tv::tv_int8 or
tv_int16 != tv::tv_int16 or tv_int32 != tv::tv_int32)
{
return unitt::put_error(id);
}
// These epsilon values were just guessed.. It appears to work though.
if(abs(tv_float - tv::tv_float) > 0.0001 or
abs(tv_double - tv::tv_double) > 0.01 or
abs(tv_sfloat - tv::tv_sfloat) > 0.0001 or
abs(tv_sdouble - tv::tv_sdouble) > 0.01) {
return unitt::put_error(id);
}
// Check overflow
return retval::RETURN_OK;
}
// TODO: Also test for constant buffers.
ReturnValue_t testserialize::test_serial_buffer_adapter() {
std::string id = "[test_serial_buffer_adapter]";
// I will skip endian swapper testing, its going to be changed anyway..
// uint8_t tv::tv_uint8_swapped = EndianSwapper::swap(tv::tv_uint8);
size_t serialized_size = 0;
uint8_t * p_array = test_array.data();
std::array<uint8_t, 5> test_serial_buffer {5, 4, 3, 2, 1};
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter =
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(),
test_serial_buffer.size(), false);
uint16_t testUint16 = 16;
SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv_serial_buffer_adapter, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
if(serialized_size != 8 or test_array[0] != true or test_array[1] != 5
or test_array[2] != 4 or test_array[3] != 3 or test_array[4] != 2
or test_array[5] != 1)
{
return unitt::put_error(id);
}
memcpy(&testUint16, test_array.data() + 6, sizeof(testUint16));
if(testUint16 != 16) {
return unitt::put_error(id);
}
// Serialize with size field
SerialBufferAdapter<uint8_t> tv_serial_buffer_adapter2 =
SerialBufferAdapter<uint8_t>(test_serial_buffer.data(),
test_serial_buffer.size(), true);
serialized_size = 0;
p_array = test_array.data();
SerializeAdapter::serialize(&tv::tv_bool, &p_array,&serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&tv_serial_buffer_adapter2, &p_array,
&serialized_size, test_array.size(), SerializeIF::Endianness::MACHINE);
SerializeAdapter::serialize(&testUint16, &p_array, &serialized_size,
test_array.size(), SerializeIF::Endianness::MACHINE);
if(serialized_size != 9 or test_array[0] != true or test_array[1] != 5
or test_array[2] != 5 or test_array[3] != 4 or test_array[4] != 3
or test_array[5] != 2 or test_array[6] != 1)
{
return unitt::put_error(id);
}
memcpy(&testUint16, test_array.data() + 7, sizeof(testUint16));
if(testUint16 != 16) {
return unitt::put_error(id);
}
return retval::RETURN_OK;
}

View File

@ -0,0 +1,15 @@
#ifndef UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
#define UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <array>
namespace testserialize {
ReturnValue_t test_serialization();
ReturnValue_t test_endianness_tools();
ReturnValue_t test_autoserialization();
ReturnValue_t test_serial_buffer_adapter();
extern std::array<uint8_t, 512> test_array;
}
#endif /* UNITTEST_INTERNAL_INTTESTSERIALIZATION_H_ */

3
unittest/lcov.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory _coverage

View File

@ -0,0 +1,46 @@
#ifndef CONFIG_FSFWCONFIG_H_
#define CONFIG_FSFWCONFIG_H_
#include <FSFWVersion.h>
//! Used to determine whether C++ ostreams are used
//! Those can lead to code bloat.
#define FSFW_CPP_OSTREAM_ENABLED 1
//! Reduced printout to further decrese code size
//! Be careful, this also turns off most diagnostic prinouts!
#define FSFW_REDUCED_PRINTOUT 0
//! Can be used to enable debugging printouts for developing the FSFW
#define FSFW_DEBUGGING 0
//! Defines the FIFO depth of each commanding service base which
//! also determines how many commands a CSB service can handle in one cycle
//! simulataneously. This will increase the required RAM for
//! each CSB service !
#define FSFW_CSB_FIFO_DEPTH 6
//! If FSFW_OBJ_EVENT_TRANSLATION is set to one,
//! additional output which requires the translation files translateObjects
//! and translateEvents (and their compiled source files)
#define FSFW_OBJ_EVENT_TRANSLATION 0
//! If -DDEBUG is supplied in the build defines, there will be
//! additional output which requires the translation files translateObjects
//! and translateEvents (and their compiles source files)
#if FSFW_OBJ_EVENT_TRANSLATION == 1
#define FSFW_DEBUG_OUTPUT 1
//! Specify whether info events are printed too.
#define FSFW_DEBUG_INFO 1
#include <translateObjects.h>
#include <translateEvents.h>
#else
#define FSFW_DEBUG_OUTPUT 0
#endif
//! When using the newlib nano library, C99 support for stdio facilities
//! will not be provided. This define should be set to 1 if this is the case.
#define FSFW_NO_C99_IO 1
#endif /* CONFIG_FSFWCONFIG_H_ */

View File

@ -0,0 +1,415 @@
#-------------------------------------------------------------------------------
# Makefile for FSFW Test
#-------------------------------------------------------------------------------
# User-modifiable options
#-------------------------------------------------------------------------------
# Fundamentals on the build process of C/C++ Software:
# https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html
# Make documentation: https://www.gnu.org/software/make/manual/make.pdf
# Online: https://www.gnu.org/software/make/manual/make.html
# General rules: http://make.mad-scientist.net/papers/rules-of-makefiles/#rule3
SHELL = /bin/sh
# Chip & board used for compilation
# (can be overriden by adding CHIP=chip and BOARD=board to the command-line)
# Unit Test can only be run on host machine for now (Linux)
FRAMEWORK_PATH = fsfw
FILE_ROOT = $(FRAMEWORK_PATH)/unittest
BOARD = unittest
LINUX = 1
OS_FSFW = linux
CUSTOM_DEFINES += -D$(OS_FSFW)
# Copied from stackoverflow, can be used to differentiate between Windows
# and Linux
ifeq ($(OS),Windows_NT)
CUSTOM_DEFINES += -DWIN32
ifeq ($(PROCESSOR_ARCHITEW6432),AMD64)
CUSTOM_DEFINES += -DAMD64
else
ifeq ($(PROCESSOR_ARCHITECTURE),AMD64)
CUSTOM_DEFINES += -DAMD64
endif
ifeq ($(PROCESSOR_ARCHITECTURE),x86)
CUSTOM_DEFINES += -DIA32
endif
endif
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Linux)
DETECTED_OS = LINUX
CUSTOM_DEFINES += -DLINUX
endif
ifeq ($(UNAME_S),Darwin)
CUSTOM_DEFINES += -DOSX
endif
UNAME_P := $(shell uname -p)
ifeq ($(UNAME_P),x86_64)
CUSTOM_DEFINES += -DAMD64
endif
ifneq ($(filter %86,$(UNAME_P)),)
CUSTOM_DEFINES += -DIA32
endif
ifneq ($(filter arm%,$(UNAME_P)),)
CUSTOM_DEFINES += -DARM
endif
endif
UNIT_TEST = 1
# General folder paths
CONFIG_PATH = $(FILE_ROOT)/config
UNIT_TEST_PATH = $(FILE_ROOT)/tests
CORE_PATH = $(FILE_ROOT)/core
# Output file basename
BASENAME = fsfw
BINARY_NAME := $(BASENAME)-$(BOARD)
# Output files will be put in this directory inside
OUTPUT_FOLDER = $(OS)
# Optimization level. Optimized for debugging.
OPTIMIZATION = -O0
# Default debug output. Optimized for debugging.
DEBUG_LEVEL = -g3
ifdef GCOV
CUSTOM_DEFINES += -DGCOV
endif
# Output directories
BUILDPATH = _bin
DEPENDPATH = _dep
OBJECTPATH = _obj
ifeq ($(MAKECMDGOALS),mission)
BUILD_FOLDER = mission
else
BUILD_FOLDER = devel
endif
DEPENDDIR = $(DEPENDPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER)
OBJDIR = $(OBJECTPATH)/$(OUTPUT_FOLDER)/$(BUILD_FOLDER)
BINDIR = $(BUILDPATH)
CLEANDEP = $(DEPENDPATH)/$(OUTPUT_FOLDER)
CLEANOBJ = $(OBJECTPATH)/$(OUTPUT_FOLDER)
CLEANBIN = $(BUILDPATH)
#-------------------------------------------------------------------------------
# Tools and Includes
#-------------------------------------------------------------------------------
# Tool suffix when cross-compiling
CROSS_COMPILE =
# C Compiler
CC = $(CROSS_COMPILE)gcc
# C++ compiler
CXX = $(CROSS_COMPILE)g++
# Additional Tools
SIZE = $(CROSS_COMPILE)size
STRIP = $(CROSS_COMPILE)strip
CP = $(CROSS_COMPILE)objcopy
HEXCOPY = $(CP) -O ihex
BINCOPY = $(CP) -O binary
# files to be compiled, will be filled in by include makefiles
# := assignment is neccessary so we get all paths right
# https://www.gnu.org/software/make/manual/html_node/Flavors.html
CSRC :=
CXXSRC :=
ASRC :=
INCLUDES :=
# Directories where $(directoryname).mk files should be included from
SUBDIRS := $(FRAMEWORK_PATH) $(TEST_PATH) $(UNIT_TEST_PATH) $(CONFIG_PATH) \
$(CORE_PATH)
I_INCLUDES += $(addprefix -I, $(INCLUDES))
# This is a hack from http://make.mad-scientist.net/the-eval-function/
#
# The problem is, that included makefiles should be aware of their relative path
# but not need to guess or hardcode it. So we set $(CURRENTPATH) for them. If
# we do this globally and the included makefiles want to include other makefiles as
# well, they would overwrite $(CURRENTPATH), screwing the include after them.
#
# By using a for-loop with an eval'd macro, we can generate the code to include all
# sub-makefiles (with the correct $(CURRENTPATH) set) before actually evaluating
# (and by this possibly changing $(CURRENTPATH)) them.
#
# This works recursively, if an included makefile wants to include, it can safely set
# $(SUBDIRS) (which has already been evaluated here) and do
# "$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE)))"
# $(SUBDIRS) must be relative to the project root, so to include subdir foo, set
# $(SUBDIRS) = $(CURRENTPATH)/foo.
define INCLUDE_FILE
CURRENTPATH := $S
include $(S)/$(notdir $S).mk
endef
$(foreach S,$(SUBDIRS),$(eval $(INCLUDE_FILE)))
INCLUDES += $(FILE_ROOT)
INCLUDES += $(FILE_ROOT)/catch2/
#-------------------------------------------------------------------------------
# Source Files
#-------------------------------------------------------------------------------
# All source files which are not includes by the .mk files are added here
# Please ensure that no files are included by both .mk file and here !
# if a target is not listed in the current directory,
# make searches in the directories specified with VPATH
# All C Sources included by .mk files are assigned here
# Add the objects to sources so dependency handling works
C_OBJECTS += $(CSRC:.c=.o)
# Objects built from Assembly source files
ASM_OBJECTS = $(ASRC:.S=.o)
# Objects built from C++ source files
CXX_OBJECTS += $(CXXSRC:.cpp=.o)
#-------------------------------------------------------------------------------
# Build Configuration + Output
#-------------------------------------------------------------------------------
TARGET = Debug build.
DEBUG_MESSAGE = Off
OPTIMIZATION_MESSAGE = Off
# Define Messages
MSG_INFO = Software: Hosted unittest \(Catch2\) for the FSFW.
MSG_OPTIMIZATION = Optimization: $(OPTIMIZATION), $(OPTIMIZATION_MESSAGE)
MSG_TARGET = Target Build: $(TARGET)
MSG_DEBUG = Debug level: $(DEBUG_LEVEL), FSFW Debugging: $(DEBUG_MESSAGE)
MSG_LINKING = Linking:
MSG_COMPILING = Compiling:
MSG_ASSEMBLING = Assembling:
MSG_DEPENDENCY = Collecting dependencies for:
MSG_BINARY = Generate binary:
# See https://stackoverflow.com/questions/6687630/how-to-remove-unused-c-c-symbols-with-gcc-and-ld
# Used to throw away unused code. Reduces code size significantly !
# -Wl,--gc-sections: needs to be passed to the linker to throw aways unused code
ifdef KEEP_UNUSED_CODE
PROTOTYPE_OPTIMIZATION =
UNUSED_CODE_REMOVAL =
else
PROTOTYPE_OPTIMIZATION = -ffunction-sections -fdata-sections
UNUSED_CODE_REMOVAL = -Wl,--gc-sections
# Link time optimization
# See https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html for reference
# Link time is larger and size of object files can not be retrieved
# but resulting binary is smaller. Could be used in mission/deployment build
# Requires -ffunction-section in linker call
LINK_TIME_OPTIMIZATION = -flto
OPTIMIZATION += $(PROTOTYPE_OPTIMIZATION)
endif
# Dependency Flags
# These flags tell the compiler to build dependencies
# See: https://www.gnu.org/software/make/manual/html_node/Automatic-Prerequisites.html
# Using following guide: http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/#combine
DEPFLAGS = -MT $@ -MMD -MP -MF $(DEPENDDIR)/$*.d
# Flags for the compiler call
# - std: Which C++ version to use. Common versions: c++11, c++14 and c++17
# - Wall: enable all warnings
# - Wextra: enable extra warnings
# - g: defines debug level
# - fmessage-length: to control the formatting algorithm for diagnostic messages;
# =0 means no line-wrapping is done; each error message appears on a single line
# - fno-exceptions: stops generating extra code needed to propagate exceptions,
# which can produce significant data size overhead
CUSTOM_DEFINES += -DUNIT_TEST
WARNING_FLAGS = -Wall -Wshadow=local -Wextra -Wimplicit-fallthrough=1 \
-Wno-unused-parameter
CXXDEFINES := $(CUSTOM_DEFINES)
CFLAGS +=
CXXFLAGS += -I. $(DEBUG_LEVEL) $(WARNING_FLAGS) $(DEPFLAGS) -fmessage-length=0 $(OPTIMIZATION)\
$(I_INCLUDES) $(CXXDEFINES)
CPPFLAGS += -std=c++11
# Flags for the linker call
# LINK_INCLUDES specify the path to used libraries and the linker script
# LINK_LIBRARIES: Link real time support
LDFLAGS := $(DEBUG_LEVEL) $(UNUSED_CODE_REMOVAL) $(OPTIMIZATION) -pthread
LINK_INCLUDES :=
LINK_LIBRARIES :=
ifdef LINUX
LINK_LIBRARIES += -lrt
endif
ifeq ($(OS),Windows_NT)
LINK_LIBRARIES += -lwsock32 -lws2_32
LDFLASGS += -fuse-ld=lld
endif
# Gnu Coverage Tools Flags
ifdef GCOV
GCOV_CXXFLAGS = -fprofile-arcs -ftest-coverage --coverage -fno-inline \
-fno-inline-small-functions -fno-default-inline
CXXFLAGS += $(GCOV_CXXFLAGS)
GCOV_LINKER_LIBS = -lgcov -fprofile-arcs -ftest-coverage
LINK_LIBRARIES += $(GCOV_LINKER_LIBS)
endif
# $(info $${CXXFLAGS} is [${CXXFLAGS}])
#-------------------------------------------------------------------------------
# Rules
#-------------------------------------------------------------------------------
# the call function assigns parameters to temporary variables
# https://www.gnu.org/software/make/manual/make.html#Call-Function
# $(1) = Memory names
# Rules are called for each memory type
# Two Expansion Symbols $$ are to escape the dollar sign for eval.
# See: http://make.mad-scientist.net/the-eval-function/
default: all
# Cleans all files
hardclean:
-rm -rf $(BUILDPATH)
-rm -rf $(OBJECTPATH)
-rm -rf $(DEPENDPATH)
# Only clean files for current build
clean:
-rm -rf $(CLEANOBJ)
-rm -rf $(CLEANBIN)
-rm -rf $(CLEANDEP)
# Only clean binaries. Useful for changing the binary type when object files
# are already compiled so complete rebuild is not necessary
cleanbin:
-rm -rf $(CLEANBIN)
# In this section, the binaries are built for all selected memories
# notestfw: all
all: executable
# Build target configuration
release: OPTIMIZATION = -Os $(PROTOTYPE_OPTIMIZATION) $(LINK_TIME_OPTIMIZATION)
release: LINK_TIME_OPTIMIZATION = -flto
release: TARGET = Mission build.
release: OPTIMIZATION_MESSAGE = On with Link Time Optimization
debug: CXXDEFINES += -DDEBUG
debug: TARGET = Debug
debug: DEBUG_MESSAGE = On
ifndef KEEP_UNUSED_CODE
debug release: OPTIMIZATION_MESSAGE += , no unused code removal
endif
debug release notestfw: executable
executable: $(BINDIR)/$(BINARY_NAME).elf
@echo
@echo $(MSG_INFO)
@echo $(MSG_TARGET)
@echo $(MSG_OPTIMIZATION)
@echo $(MSG_DEBUG)
C_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(C_OBJECTS))
CXX_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(CXX_OBJECTS))
ASM_OBJECTS_PREFIXED = $(addprefix $(OBJDIR)/, $(ASM_OBJECTS))
ALL_OBJECTS = $(ASM_OBJECTS_PREFIXED) $(C_OBJECTS_PREFIXED) \
$(CXX_OBJECTS_PREFIXED)
# Useful for debugging the Makefile
# Also see: https://www.oreilly.com/openbook/make3/book/ch12.pdf
# $(info $${ALL_OBJECTS} is [${ALL_OBJECTS}])
# $(info $${CXXSRC} is [${CXXSRC}])
# Automatic variables are used here extensively. Some of them
# are escaped($$) to suppress immediate evaluation. The most important ones are:
# $@: Name of Target (left side of rule)
# $<: Name of the first prerequisite (right side of rule)
# @^: List of all prerequisite, omitting duplicates
# @D: Directory and file-within-directory part of $@
# Generates binary and displays all build properties
# -p with mkdir ignores error and creates directory when needed.
# SHOW_DETAILS = 1
# Link with required libraries: HAL (Hardware Abstraction Layer) and
# HCC (File System Library)
$(BINDIR)/$(BINARY_NAME).elf: $(ALL_OBJECTS)
@echo
@echo $(MSG_LINKING) Target $@
@mkdir -p $(@D)
ifdef SHOW_DETAILS
$(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES)
else
@$(CXX) $(LDFLAGS) $(LINK_INCLUDES) -o $@ $^ $(LINK_LIBRARIES)
endif
ifeq ($(BUILD_FOLDER), mission)
# With Link Time Optimization, section size is not available
$(SIZE) $@
else
$(SIZE) $^ $@
endif
$(BINDIR)/$(BINARY_NAME).hex: $(BINDIR)/$(BINARY_NAME).elf
@echo
@echo $(MSG_BINARY)
@mkdir -p $(@D)
$(HEXCOPY) $< $@
# Build new objects for changed dependencies.
$(OBJDIR)/%.o: %.cpp
$(OBJDIR)/%.o: %.cpp $(DEPENDDIR)/%.d | $(DEPENDDIR)
@echo
@echo $(MSG_COMPILING) $<
@mkdir -p $(@D)
ifdef SHOW_DETAILS
$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
else
@$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<
endif
$(OBJDIR)/%.o: %.c
$(OBJDIR)/%.o: %.c $(DEPENDDIR)/%.d | $(DEPENDDIR)
@echo
@echo $(MSG_COMPILING) $<
@mkdir -p $(@D)
ifdef SHOW_DETAILS
$(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $<
else
@$(CC) $(CXXFLAGS) $(CFLAGS) -c -o $@ $<
endif
#-------------------------------------------------------------------------------
# Dependency Handling
#-------------------------------------------------------------------------------
# Dependency Handling according to following guide:
# http://make.mad-scientist.net/papers/advanced-auto-dependency-generation/
$(DEPENDDIR):
@mkdir -p $(@D)
DEPENDENCY_RELATIVE = $(CSRC:.c=.d) $(CXXSRC:.cpp=.d)
# This is the list of all dependencies
DEPFILES = $(addprefix $(DEPENDDIR)/, $(DEPENDENCY_RELATIVE))
# Create subdirectories for dependencies
$(DEPFILES):
@mkdir -p $(@D)
# Include all dependencies
include $(wildcard $(DEPFILES))
# .PHONY tells make that these targets aren't files
.PHONY: clean release debug all hardclean cleanbin

View File

@ -0,0 +1,8 @@
#ifndef FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_
#define FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_
#define CUSTOM_UNITTEST_RUNNER 0
#endif /* FSFW_UNITTEST_CONFIG_TESTSCONFIG_H_ */

View File

@ -0,0 +1,5 @@
#include "dataPoolInit.h"
void datapool::dataPoolInit(std::map<uint32_t, PoolEntryIF*> * poolMap) {
}

View File

@ -0,0 +1,17 @@
#ifndef HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_
#define HOSTED_CONFIG_CDATAPOOL_DATAPOOLINIT_H_
#include <fsfw/datapool/DataPool.h>
#include <fsfw/datapool/PoolEntryIF.h>
#include <map>
#include <cstdint>
namespace datapool {
void dataPoolInit(std::map<uint32_t, PoolEntryIF*> * poolMap);
enum datapoolvariables {
NO_PARAMETER = 0,
};
}
#endif /* CONFIG_CDATAPOOL_DATAPOOLINIT_H_ */

View File

@ -0,0 +1,5 @@
#include "logicalAddresses.h"

View File

@ -0,0 +1,15 @@
#ifndef CONFIG_DEVICES_LOGICALADDRESSES_H_
#define CONFIG_DEVICES_LOGICALADDRESSES_H_
#include <fsfw/devicehandlers/CookieIF.h>
#include <fsfw/unittest/config/objects/systemObjectList.h>
#include <cstdint>
namespace addresses {
/* Logical addresses have uint32_t datatype */
enum logicalAddresses: address_t {
};
}
#endif /* CONFIG_DEVICES_LOGICALADDRESSES_H_ */

View File

@ -0,0 +1,4 @@
#include "powerSwitcherList.h"

View File

@ -0,0 +1,12 @@
#ifndef CONFIG_DEVICES_POWERSWITCHERLIST_H_
#define CONFIG_DEVICES_POWERSWITCHERLIST_H_
namespace switches {
/* Switches are uint8_t datatype and go from 0 to 255 */
enum switcherList {
};
}
#endif /* CONFIG_DEVICES_POWERSWITCHERLIST_H_ */

View File

@ -0,0 +1,18 @@
#ifndef CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#define CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_
#include <cstdint>
#include <fsfw/events/fwSubsystemIdRanges.h>
/**
* @brief Custom subsystem IDs can be added here
* @details
* Subsystem IDs are used to create unique events.
*/
namespace SUBSYSTEM_ID {
enum: uint8_t {
SUBSYSTEM_ID_START = FW_SUBSYSTEM_ID_RANGE,
};
}
#endif /* CONFIG_EVENTS_SUBSYSTEMIDRANGES_H_ */

View File

@ -0,0 +1,12 @@
#include "MissionMessageTypes.h"
#include <fsfw/ipc/CommandMessage.h>
void messagetypes::clearMissionMessage(CommandMessage* message) {
switch(message->getMessageType()) {
default:
break;
}
}

View File

@ -0,0 +1,22 @@
#ifndef CONFIG_IPC_MISSIONMESSAGETYPES_H_
#define CONFIG_IPC_MISSIONMESSAGETYPES_H_
#include <fsfw/ipc/FwMessageTypes.h>
class CommandMessage;
/**
* Custom command messages are specified here.
* Most messages needed to use FSFW are already located in
* <fsfw/ipc/FwMessageTypes.h>
* @param message Generic Command Message
*/
namespace messagetypes{
enum MESSAGE_TYPE {
MISSION_MESSAGE_TYPE_START = FW_MESSAGES_COUNT,
};
void clearMissionMessage(CommandMessage* message);
}
#endif /* CONFIG_IPC_MISSIONMESSAGETYPES_H_ */

View File

@ -0,0 +1,34 @@
#include "Factory.h"
#include <fsfw/events/EventManager.h>
#include <fsfw/health/HealthTable.h>
#include <fsfw/internalError/InternalErrorReporter.h>
#include <fsfw/objectmanager/frameworkObjects.h>
/**
* @brief Produces system objects.
* @details
* Build tasks by using SystemObject Interface (Interface).
* Header files of all tasks must be included
* Please note that an object has to implement the system object interface
* if the interface validity is checked or retrieved later by using the
* get<TargetInterface>(object_id) function from the ObjectManagerIF.
*
* Framework objects are created first.
*
* @ingroup init
*/
void Factory::produce(void) {
setStaticFrameworkObjectIds();
new EventManager(objects::EVENT_MANAGER);
new HealthTable(objects::HEALTH_TABLE);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
}
void Factory::setStaticFrameworkObjectIds() {
}

View File

@ -0,0 +1,16 @@
#ifndef FACTORY_H_
#define FACTORY_H_
#include <fsfw/objectmanager/SystemObjectIF.h>
namespace Factory {
/**
* @brief Creates all SystemObject elements which are persistent
* during execution.
*/
void produce();
void setStaticFrameworkObjectIds();
}
#endif /* FACTORY_H_ */

View File

@ -0,0 +1,16 @@
#ifndef HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#define HOSTED_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_
#include <cstdint>
#include <fsfw/objectmanager/frameworkObjects.h>
// The objects will be instantiated in the ID order
namespace objects {
enum sourceObjects: uint32_t {
/* All addresses between start and end are reserved for the FSFW */
FSFW_CONFIG_RESERVED_START = PUS_SERVICE_1_VERIFICATION,
FSFW_CONFIG_RESERVED_END = TM_STORE
};
}
#endif /* BSP_CONFIG_OBJECTS_SYSTEMOBJECTLIST_H_ */

View File

@ -0,0 +1,23 @@
#include "PollingSequenceFactory.h"
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>
#include <fsfw/devicehandlers/DeviceHandlerIF.h>
#include <fsfw/tasks/FixedTimeslotTaskIF.h>
ReturnValue_t pst::pollingSequenceInitDefault(
FixedTimeslotTaskIF *thisSequence) {
/* Length of a communication cycle */
uint32_t length = thisSequence->getPeriodMs();
/* Add polling sequence table here */
if (thisSequence->checkSequence() == HasReturnvaluesIF::RETURN_OK) {
return HasReturnvaluesIF::RETURN_OK;
}
else {
sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!"
<< std::endl;
return HasReturnvaluesIF::RETURN_FAILED;
}
}

View File

@ -0,0 +1,32 @@
#ifndef POLLINGSEQUENCEFACTORY_H_
#define POLLINGSEQUENCEFACTORY_H_
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
class FixedTimeslotTaskIF;
/**
* All device handlers are scheduled by adding them into Polling Sequence Tables (PST)
* to satisfy stricter timing requirements of device communication,
* A device handler has four different communication steps:
* 1. DeviceHandlerIF::SEND_WRITE -> Send write via interface
* 2. DeviceHandlerIF::GET_WRITE -> Get confirmation for write
* 3. DeviceHandlerIF::SEND_READ -> Send read request
* 4. DeviceHandlerIF::GET_READ -> Read from interface
* The PST specifies precisely when the respective ComIF functions are called
* during the communication cycle time.
* The task is created using the FixedTimeslotTaskIF,
* which utilises the underlying Operating System Abstraction Layer (OSAL)
*
* @param thisSequence FixedTimeslotTaskIF * object is passed inside the Factory class when creating the PST
* @return
*/
namespace pst {
/* Default PST */
ReturnValue_t pollingSequenceInitDefault(FixedTimeslotTaskIF *thisSequence);
}
#endif /* POLLINGSEQUENCEINIT_H_ */

View File

@ -0,0 +1,16 @@
#ifndef CONFIG_RETURNVALUES_CLASSIDS_H_
#define CONFIG_RETURNVALUES_CLASSIDS_H_
#include <fsfw/returnvalues/FwClassIds.h>
/**
* @brief CLASS_ID defintions which are required for custom returnvalues.
*/
namespace CLASS_ID {
enum {
MISSION_CLASS_ID_START = FW_CLASS_ID_COUNT,
};
}
#endif /* CONFIG_RETURNVALUES_CLASSIDS_H_ */

View File

@ -0,0 +1,15 @@
CXXSRC += $(wildcard $(CURRENTPATH)/cdatapool/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/ipc/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/objects/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/pollingsequence/*.cpp)
CXXSRC += $(wildcard $(CURRENTPATH)/events/*.cpp)
INCLUDES += $(CURRENTPATH)
INCLUDES += $(CURRENTPATH)/objects
INCLUDES += $(CURRENTPATH)/ipc
INCLUDES += $(CURRENTPATH)/pollingsequence
INCLUDES += $(CURRENTPATH)/returnvalues
INCLUDES += $(CURRENTPATH)/tmtc
INCLUDES += $(CURRENTPATH)/events
INCLUDES += $(CURRENTPATH)/devices
INCLUDES += $(CURRENTPATH)/cdatapool

View File

@ -0,0 +1,18 @@
#ifndef CONFIG_TMTC_APID_H_
#define CONFIG_TMTC_APID_H_
#include <cstdint>
/**
* Application Process Definition: entity, uniquely identified by an
* application process ID (APID), capable of generating telemetry source
* packets and receiving telecommand packets.
*
* Chose APID(s) for mission and define it here.
*/
namespace apid {
static const uint16_t DEFAULT_APID = 0x00;
}
#endif /* CONFIG_TMTC_APID_H_ */

View File

@ -0,0 +1,25 @@
#ifndef CONFIG_TMTC_PUSIDS_HPP_
#define CONFIG_TMTC_PUSIDS_HPP_
#include <cstdint>
namespace pus {
enum Ids: uint8_t {
PUS_SERVICE_1 = 1,
PUS_SERVICE_2 = 2,
PUS_SERVICE_3 = 3,
PUS_SERVICE_5 = 5,
PUS_SERVICE_6 = 6,
PUS_SERVICE_8 = 8,
PUS_SERVICE_9 = 9,
PUS_SERVICE_11 = 11,
PUS_SERVICE_17 = 17,
PUS_SERVICE_19 = 19,
PUS_SERVICE_20 = 20,
PUS_SERVICE_23 = 23,
PUS_SERVICE_200 = 200,
PUS_SERVICE_201 = 201,
};
};
#endif /* CONFIG_TMTC_PUSIDS_HPP_ */

View File

@ -0,0 +1,106 @@
//#include "TestActionHelper.h"
//#include <fsfw/action/ActionHelper.h>
//#include <fsfw/ipc/CommandMessage.h>
//#include <catch2/catch.hpp>
//#include "../../core/CatchDefinitions.h"
//
//
//TEST_CASE( "Action Helper" , "[ActionHelper]") {
// ActionHelperOwnerMockBase testDhMock;
// MessageQueueMockBase testMqMock;
// ActionHelper actionHelper = ActionHelper(
// &testDhMock, dynamic_cast<MessageQueueIF*>(&testMqMock));
// CommandMessage actionMessage;
// ActionId_t testActionId = 777;
// std::array <uint8_t, 3> testParams {1, 2, 3};
// store_address_t paramAddress;
// StorageManagerIF *ipcStore = tglob::getIpcStoreHandle();
// ipcStore->addData(&paramAddress, testParams.data(), 3);
// REQUIRE(actionHelper.initialize() == retval::CATCH_OK);
//
// SECTION ("Simple tests") {
// ActionMessage::setCommand(&actionMessage, testActionId, paramAddress);
// CHECK(not testDhMock.executeActionCalled);
// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
// CHECK(testDhMock.executeActionCalled);
// // No message is sent if everything is alright.
// CHECK(not testMqMock.wasMessageSent());
// store_address_t invalidAddress;
// ActionMessage::setCommand(&actionMessage, testActionId, invalidAddress);
// actionHelper.handleActionMessage(&actionMessage);
// CHECK(testMqMock.wasMessageSent());
// const uint8_t* ptr = nullptr;
// size_t size = 0;
// REQUIRE(ipcStore->getData(paramAddress, &ptr, &size) == static_cast<uint32_t>(StorageManagerIF::DATA_DOES_NOT_EXIST));
// REQUIRE(ptr == nullptr);
// REQUIRE(size == 0);
// testDhMock.getBuffer(&ptr, &size);
// REQUIRE(size == 3);
// for(uint8_t i = 0; i<3;i++){
// REQUIRE(ptr[i] == (i+1));
// }
// testDhMock.clearBuffer();
// }
//
// SECTION("Handle failures"){
// actionMessage.setCommand(1234);
// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == static_cast<uint32_t>(CommandMessage::UNKNOWN_COMMAND));
// CHECK(not testMqMock.wasMessageSent());
// uint16_t step = 5;
// ReturnValue_t status = 0x1234;
// actionHelper.step(step, testMqMock.getId(), testActionId, status);
// step += 1;
// CHECK(testMqMock.wasMessageSent());
// CommandMessage testMessage;
// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
// REQUIRE(testMessage.getParameter() == static_cast<uint32_t>(testActionId));
// uint32_t parameter2 = ((uint32_t)step << 16) | (uint32_t)status;
// REQUIRE(testMessage.getParameter2() == parameter2);
// REQUIRE(ActionMessage::getStep(&testMessage) == step);
// }
//
// SECTION("Handle finish"){
// CHECK(not testMqMock.wasMessageSent());
// ReturnValue_t status = 0x9876;
// actionHelper.finish(testMqMock.getId(), testActionId, status);
// CHECK(testMqMock.wasMessageSent());
// CommandMessage testMessage;
// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::COMPLETION_FAILED));
// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId);
// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast<uint32_t>(status));
// }
//
// SECTION("Handle failed"){
// store_address_t toLongParamAddress = StorageManagerIF::INVALID_ADDRESS;
// std::array<uint8_t, 5> toLongData = {5, 4, 3, 2, 1};
// REQUIRE(ipcStore->addData(&toLongParamAddress, toLongData.data(), 5) == retval::CATCH_OK);
// ActionMessage::setCommand(&actionMessage, testActionId, toLongParamAddress);
// CHECK(not testDhMock.executeActionCalled);
// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
// REQUIRE(ipcStore->getData(toLongParamAddress).first == static_cast<uint32_t>(StorageManagerIF::DATA_DOES_NOT_EXIST));
// CommandMessage testMessage;
// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
// REQUIRE(ActionMessage::getReturnCode(&testMessage) == 0xAFFE);
// REQUIRE(ActionMessage::getStep(&testMessage) == 0);
// REQUIRE(ActionMessage::getActionId(&testMessage) == testActionId);
// }
//
// SECTION("Missing IPC Data"){
// ActionMessage::setCommand(&actionMessage, testActionId, StorageManagerIF::INVALID_ADDRESS);
// CHECK(not testDhMock.executeActionCalled);
// REQUIRE(actionHelper.handleActionMessage(&actionMessage) == retval::CATCH_OK);
// CommandMessage testMessage;
// REQUIRE(testMqMock.receiveMessage(&testMessage) == static_cast<uint32_t>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(testMessage.getCommand() == static_cast<uint32_t>(ActionMessage::STEP_FAILED));
// REQUIRE(ActionMessage::getReturnCode(&testMessage) == static_cast<uint32_t>(StorageManagerIF::ILLEGAL_STORAGE_ID));
// REQUIRE(ActionMessage::getStep(&testMessage) == 0);
// }
//
//
// SECTION("Data Reply"){
//
// }
//}

View File

@ -0,0 +1,131 @@
//#ifndef UNITTEST_HOSTED_TESTACTIONHELPER_H_
//#define UNITTEST_HOSTED_TESTACTIONHELPER_H_
//
//#include <fsfw/action/HasActionsIF.h>
//#include <fsfw/ipc/MessageQueueIF.h>
//#include <fsfw/unittest/core/CatchDefinitions.h>
//#include <cstring>
//
//
//class ActionHelperOwnerMockBase: public HasActionsIF {
//public:
// bool getCommandQueueCalled = false;
// bool executeActionCalled = false;
// static const size_t MAX_SIZE = 3;
// uint8_t buffer[MAX_SIZE] = {0, 0, 0};
// size_t size = 0;
//
// MessageQueueId_t getCommandQueue() const override {
// return tconst::testQueueId;
// }
//
// ReturnValue_t executeAction(ActionId_t actionId, MessageQueueId_t commandedBy,
// const uint8_t* data, size_t size) override {
// executeActionCalled = true;
// if(size > MAX_SIZE){
// return 0xAFFE;
// }
// this->size = size;
// memcpy(buffer, data, size);
// return HasReturnvaluesIF::RETURN_OK;
// }
//
// void clearBuffer(){
// this->size = 0;
// for(size_t i = 0; i<MAX_SIZE; i++){
// buffer[i] = 0;
// }
// }
//
// void getBuffer(const uint8_t** ptr, size_t* size){
// if(size != nullptr){
// *size = this->size;
// }
// if(ptr != nullptr){
// *ptr = buffer;
// }
// }
//};
//
//
//class MessageQueueMockBase: public MessageQueueIF {
//public:
// MessageQueueId_t myQueueId = 0;
// bool defaultDestSet = false;
// bool messageSent = false;
//
//
//
// bool wasMessageSent() {
// bool tempMessageSent = messageSent;
// messageSent = false;
// return tempMessageSent;
// }
//
// virtual ReturnValue_t reply( MessageQueueMessage* message ) {
// messageSent = true;
// lastMessage = (*message);
// return HasReturnvaluesIF::RETURN_OK;
// };
// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message,
// MessageQueueId_t *receivedFrom) {
// (*message) = lastMessage;
// lastMessage.clear();
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual ReturnValue_t receiveMessage(MessageQueueMessage* message) {
// (*message) = lastMessage;
// lastMessage.clear();
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual ReturnValue_t flush(uint32_t* count) {
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual MessageQueueId_t getLastPartner() const {
// return tconst::testQueueId;
// }
// virtual MessageQueueId_t getId() const {
// return tconst::testQueueId;
// }
// virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo,
// MessageQueueMessage* message, MessageQueueId_t sentFrom,
// bool ignoreFault = false ) {
// messageSent = true;
// lastMessage = (*message);
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo,
// MessageQueueMessage* message, bool ignoreFault = false ) override {
// messageSent = true;
// lastMessage = (*message);
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessage* message,
// MessageQueueId_t sentFrom, bool ignoreFault = false ) {
// messageSent = true;
// lastMessage = (*message);
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual ReturnValue_t sendToDefault( MessageQueueMessage* message ) {
// messageSent = true;
// lastMessage = (*message);
// return HasReturnvaluesIF::RETURN_OK;
// }
// virtual void setDefaultDestination(MessageQueueId_t defaultDestination) {
// myQueueId = defaultDestination;
// defaultDestSet = true;
// }
//
// virtual MessageQueueId_t getDefaultDestination() const {
// return myQueueId;
// }
// virtual bool isDefaultDestinationSet() const {
// return defaultDestSet;
// }
//private:
// MessageQueueMessage lastMessage;
//
//};
//
//
//#endif /* UNITTEST_TESTFW_NEWTESTS_TESTACTIONHELPER_H_ */

View File

@ -0,0 +1,327 @@
#include <fsfw/container/SimpleRingBuffer.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
#include <cstring>
TEST_CASE("Ring Buffer Test" , "[RingBufferTest]") {
uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
SimpleRingBuffer ringBuffer(10, false, 5);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for(uint8_t i = 0; i< 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
}
TEST_CASE("Ring Buffer Test2" , "[RingBufferTest2]") {
uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
uint8_t* newBuffer = new uint8_t[10];
SimpleRingBuffer ringBuffer(newBuffer, 10, true, 5);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for(uint8_t i = 0; i< 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
SECTION("Overflow"){
REQUIRE(ringBuffer.availableWriteSpace()==9);
//Writing more than the buffer is large, technically thats allowed
//But it is senseless and has undesired impact on read call
REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData()==3);
ringBuffer.clear();
uint8_t * ptr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK);
REQUIRE(ptr != nullptr);
memcpy(ptr, testData, 13);
ringBuffer.confirmBytesWritten(13);
REQUIRE(ringBuffer.getAvailableReadData()==3);
REQUIRE(ringBuffer.readData(readBuffer, 3, true)== retval::CATCH_OK);
for(auto i =0;i<3;i++){
REQUIRE(readBuffer[i] == testData[i+10]);
}
}
}
TEST_CASE("Ring Buffer Test3" , "[RingBufferTest3]") {
uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
uint8_t* newBuffer = new uint8_t[10];
SimpleRingBuffer ringBuffer(newBuffer, 10, true, 15);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 8);
REQUIRE(ringBuffer.writeTillWrap() == 2);
// too many excess bytes.
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_FAILED);
// Less Execss bytes overwrites before
REQUIRE(ringBuffer.getFreeElement(&testPtr, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 1);
std::memcpy(testPtr, testData, 3);
ringBuffer.confirmBytesWritten(3);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
ringBuffer.readData(readBuffer, 3, true);
for(uint8_t i = 0; i< 3; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for(uint8_t i = 0; i< 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
SECTION("Overflow"){
REQUIRE(ringBuffer.availableWriteSpace()==9);
//Writing more than the buffer is large, technically thats allowed
//But it is senseless and has undesired impact on read call
REQUIRE(ringBuffer.writeData(testData, 13) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData()==3);
ringBuffer.clear();
uint8_t * ptr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&ptr, 13) == retval::CATCH_OK);
REQUIRE(ptr != nullptr);
memcpy(ptr, testData, 13);
ringBuffer.confirmBytesWritten(13);
REQUIRE(ringBuffer.getAvailableReadData()==3);
REQUIRE(ringBuffer.readData(readBuffer, 3, true)== retval::CATCH_OK);
for(auto i =0;i<3;i++){
REQUIRE(readBuffer[i] == testData[i+10]);
}
}
}
TEST_CASE("Ring Buffer Test4" , "[RingBufferTest4]") {
uint8_t testData[13]= {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
uint8_t readBuffer[10] = {13, 13, 13, 13, 13, 13, 13, 13, 13, 13};
SimpleRingBuffer ringBuffer(10, false, 15);
SECTION("Simple Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.readData(readBuffer, 5, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.availableWriteSpace() == 5);
ringBuffer.clear();
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 4) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 4, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 4; i++) {
CHECK(readBuffer[i] == i);
}
REQUIRE(ringBuffer.writeData(testData, 9) == retval::CATCH_OK);
REQUIRE(ringBuffer.readData(readBuffer, 9, true) == retval::CATCH_OK);
for(uint8_t i = 0; i< 9; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Get Free Element Test") {
REQUIRE(ringBuffer.availableWriteSpace() == 9);
REQUIRE(ringBuffer.writeData(testData, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 1);
REQUIRE(ringBuffer.readData(readBuffer, 8, true) == retval::CATCH_OK);
REQUIRE(ringBuffer.availableWriteSpace() == 9);
uint8_t *testPtr = nullptr;
REQUIRE(ringBuffer.getFreeElement(&testPtr, 10) == retval::CATCH_FAILED);
REQUIRE(ringBuffer.writeTillWrap() == 2);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 8) == retval::CATCH_OK);
REQUIRE(ringBuffer.getFreeElement(&testPtr, 5) == retval::CATCH_OK);
REQUIRE(ringBuffer.getExcessBytes() == 3);
std::memcpy(testPtr, testData, 5);
ringBuffer.confirmBytesWritten(5);
REQUIRE(ringBuffer.getAvailableReadData() == 5);
ringBuffer.readData(readBuffer, 5, true);
for(uint8_t i = 0; i< 5; i++) {
CHECK(readBuffer[i] == i);
}
}
SECTION("Read Remaining Test") {
REQUIRE(ringBuffer.writeData(testData, 3) == retval::CATCH_OK);
REQUIRE(ringBuffer.getAvailableReadData() == 3);
REQUIRE(ringBuffer.readData(readBuffer, 5, false, false, nullptr) == retval::CATCH_FAILED);
size_t trueSize = 0;
REQUIRE(ringBuffer.readData(readBuffer, 5, false, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
for(uint8_t i = 0; i< 3; i++) {
CHECK(readBuffer[i] == i);
}
trueSize = 0;
REQUIRE(ringBuffer.deleteData(5, false, &trueSize) == retval::CATCH_FAILED);
REQUIRE(trueSize == 0);
REQUIRE(ringBuffer.deleteData(5, true, &trueSize) == retval::CATCH_OK);
REQUIRE(trueSize == 3);
}
}

View File

@ -0,0 +1,90 @@
#include <fsfw/container/ArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
/**
* @brief Array List test
*/
TEST_CASE("Array List" , "[ArrayListTest]") {
//perform set-up here
ArrayList<uint16_t> list(20);
struct TestClass{
public:
TestClass(){};
TestClass(uint32_t number1, uint64_t number2):
number1(number1), number2(number2){};
uint32_t number1 = -1;
uint64_t number2 = -1;
bool operator==(const TestClass& other){
return ((this->number1 == other.number1) and (this->number2 == other.number2));
};
};
ArrayList<TestClass> complexList(20);
SECTION("SimpleTest") {
REQUIRE(list.maxSize()==20);
REQUIRE(list.size == 0);
REQUIRE(list.insert(10) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(list[0] == 10);
REQUIRE(list.front() != nullptr);
REQUIRE((*list.front()) == 10);
REQUIRE(list.back() != nullptr);
REQUIRE((*list.back()) == 10);
// Need to test the const version of back as well
const uint16_t* number = const_cast<const ArrayList<uint16_t>*>(&list)->back();
REQUIRE(*number == 10);
list.clear();
REQUIRE(list.size == 0);
}
SECTION("Fill and check"){
//This is an invalid element but its not a nullptr
REQUIRE(list.back() != nullptr);
for (auto i =0; i < 20; i++){
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(list.insert(20) == static_cast<int>(ArrayList<uint16_t>::FULL));
ArrayList<uint16_t>::Iterator it = list.begin();
REQUIRE((*it) == 0);
it++;
REQUIRE((*it) == 1);
it--;
REQUIRE((*it) == 0);
it++;
for(auto it2 = list.begin(); it2!=list.end(); it2++){
if (it == it2){
REQUIRE((*it) == (*it2));
break;
}else{
REQUIRE((*it2) == 0);
REQUIRE(it2 != it);
}
}
}
SECTION("Const Iterator"){
ArrayList<uint16_t>::Iterator it = list.begin();
for (auto i =0; i < 10; i++){
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
it++;
const uint16_t* number = it.value;
REQUIRE(*number == 1);
}
SECTION("Const Iterator"){
ArrayList<TestClass>::Iterator it = complexList.begin();
for (auto i =0; i < 10; i++){
REQUIRE(complexList.insert(TestClass(i, i+1)) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
it++;
const TestClass* secondTest = it.value;
bool compare = TestClass(1, 2) == *secondTest;
REQUIRE(compare);
it++;
REQUIRE(it->number1 == 2);
REQUIRE(it->number2 == 3);
const ArrayList<TestClass>::Iterator it4(&(complexList[2]));
REQUIRE(it4->number1 == 2);
REQUIRE((*it4).number2 == 3);
REQUIRE(complexList.remaining()==10);
}
}

View File

@ -0,0 +1,149 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch.hpp>
#include <CatchDefinitions.h>
TEST_CASE( "Dynamic Fifo Tests", "[TestDynamicFifo]") {
INFO("Dynamic Fifo Tests");
struct Test{
uint64_t number1;
uint32_t number2;
uint8_t number3;
bool operator==(struct Test& other){
if ((other.number1 == this->number1) and
(other.number1 == this->number1) and
(other.number1 == this->number1)){
return true;
}
return false;
}
};
DynamicFIFO<Test> fifo(3);
std::vector<Test> list;
struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX});
struct Test structTwo({0, 1, 2});
struct Test structThree({42, 43, 44});
list.push_back(structThree);
list.push_back(structTwo);
list.push_back(structOne);
SECTION("Insert, retrieval test"){
REQUIRE(fifo.getMaxCapacity()==3);
REQUIRE(fifo.size()==0);
REQUIRE(fifo.empty());
REQUIRE(not fifo.full());
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(FIFOBase<Test>::FULL));
struct Test testptr;
REQUIRE(fifo.peek(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == structOne;
REQUIRE(equal);
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
for(size_t i=2;i<3;i--){
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo.size()==i);
}
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr)==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.peek(&testptr)==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(not fifo.full());
REQUIRE(fifo.empty());
REQUIRE(fifo.pop()==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==1);
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==2);
REQUIRE(fifo.pop()==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==1);
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.peek(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == structTwo;
REQUIRE(equal);
REQUIRE(fifo.pop()==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==0);
REQUIRE(fifo.empty());
//struct Test* ptr = nullptr;
//REQUIRE(fifo.retrieve(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
//REQUIRE(fifo.peek(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
};
SECTION("Copy Test"){
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(fifo);
REQUIRE(fifo2.size()==3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
};
SECTION("Assignment Test"){
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(6);
fifo2 = fifo;
REQUIRE(fifo2.size()==3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for(size_t i=2;i<3;i--){
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size()==i);
}
};
SECTION("Assignment Test Smaller"){
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
DynamicFIFO<Test> fifo2(2);
fifo2 = fifo;
REQUIRE(fifo2.size()==3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for(size_t i=2;i<3;i--){
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size()==i);
}
};
};

View File

@ -0,0 +1,138 @@
#include <fsfw/container/DynamicFIFO.h>
#include <fsfw/container/FIFO.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
TEST_CASE( "Static Fifo Tests", "[TestFifo]") {
INFO("Fifo Tests");
struct Test{
uint64_t number1;
uint32_t number2;
uint8_t number3;
bool operator==(struct Test& other){
if ((other.number1 == this->number1) and
(other.number1 == this->number1) and
(other.number1 == this->number1)){
return true;
}
return false;
}
};
FIFO<Test, 3> fifo;
std::vector<Test> list;
struct Test structOne({UINT64_MAX, UINT32_MAX, UINT8_MAX});
struct Test structTwo({0, 1, 2});
struct Test structThree({42, 43, 44});
list.push_back(structThree);
list.push_back(structTwo);
list.push_back(structOne);
SECTION("Insert, retrieval test"){
REQUIRE(fifo.getMaxCapacity()==3);
REQUIRE(fifo.size()==0);
REQUIRE(fifo.empty());
REQUIRE(not fifo.full());
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(FIFOBase<Test>::FULL));
struct Test testptr;
REQUIRE(fifo.peek(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == structOne;
REQUIRE(equal);
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
for(size_t i=2;i<3;i--){
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo.size()==i);
}
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
REQUIRE(fifo.retrieve(&testptr)==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.peek(&testptr)==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(not fifo.full());
REQUIRE(fifo.empty());
REQUIRE(fifo.pop()==static_cast<int>(FIFOBase<Test>::EMPTY));
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==1);
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==2);
REQUIRE(fifo.pop()==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==1);
testptr.number1 = 0;
testptr.number2 = 0;
testptr.number3 = 0;
// Test that retrieve and peek will not cause a nullptr dereference
struct Test* ptr = nullptr;
REQUIRE(fifo.retrieve(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
REQUIRE(fifo.peek(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
REQUIRE(fifo.peek(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
equal = testptr == structTwo;
REQUIRE(equal);
REQUIRE(fifo.pop()==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==0);
REQUIRE(fifo.empty());
};
SECTION("Copy Test"){
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
FIFO<Test, 3> fifo2(fifo);
REQUIRE(fifo2.size()==3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for(size_t i=2;i<3;i--){
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size()==i);
}
};
SECTION("Assignment Test"){
REQUIRE(fifo.insert(structOne)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structTwo)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.insert(structThree)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(fifo.size()==3);
REQUIRE(fifo.full());
REQUIRE(not fifo.empty());
FIFO<Test, 3> fifo2;
fifo2 = fifo;
REQUIRE(fifo2.size()==3);
REQUIRE(fifo2.full());
REQUIRE(not fifo2.empty());
for(size_t i=2;i<3;i--){
struct Test testptr = {0, 0, 0};
REQUIRE(fifo2.retrieve(&testptr)==static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool equal = testptr == list[i];
REQUIRE(equal);
REQUIRE(fifo2.size()==i);
}
};
};

View File

@ -0,0 +1,42 @@
#include "../../core/CatchDefinitions.h"
#include <fsfw/container/FixedArrayList.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
TEST_CASE( "FixedArrayList Tests", "[TestFixedArrayList]") {
INFO("FixedArrayList Tests");
using testList = FixedArrayList<uint32_t, 260, uint16_t>;
testList list;
REQUIRE(list.size==0);
REQUIRE(list.insert(10) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(list.size==1);
REQUIRE(list.maxSize()==260);
SECTION("Copy Constructor"){
testList list2(list);
REQUIRE(list2.size==1);
REQUIRE(list2[0] == 10);
REQUIRE(list.maxSize()==260);
};
SECTION("Assignment copy"){
testList list2;
REQUIRE(list2.size==0);
list2 = list;
REQUIRE(list2.size==1);
REQUIRE(list2[0] == 10);
REQUIRE(list.maxSize()==260);
};
SECTION("Fill"){
for(auto i=1;i<260;i++){
REQUIRE(list.insert(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(list.insert(260) == static_cast<int>(ArrayList<uint32_t, uint16_t>::FULL));
list.clear();
REQUIRE(list.size == 0);
}
}

View File

@ -0,0 +1,172 @@
#include <fsfw/container/FixedMap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
template class FixedMap<unsigned int, unsigned short>;
TEST_CASE( "FixedMap Tests", "[TestFixedMap]") {
INFO("FixedMap Tests");
FixedMap<unsigned int, unsigned short> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
REQUIRE(map.getSerializedSize() == sizeof(uint32_t));
REQUIRE(map.empty());
REQUIRE(not map.full());
SECTION("Fill and erase"){
for (uint16_t i=0;i<30;i++){
REQUIRE(map.insert(std::make_pair(i, i+1))== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(i)->second==i+1);
REQUIRE(not map.empty());
}
REQUIRE(map.insert(0, 0) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_ALREADY_EXISTS));
REQUIRE(map.insert(31, 0) == static_cast<int>(FixedMap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
REQUIRE(map.full());
{
uint16_t* ptr;
REQUIRE(map.find(5,&ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(*ptr == 6);
REQUIRE(*(map.findValue(6)) == 7);
REQUIRE(map.find(31,&ptr) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.getSerializedSize() == (sizeof(uint32_t)+ 30*(sizeof(uint32_t) + sizeof(uint16_t))));
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element: map){
if (element.first == 5){
REQUIRE(element.second == 6);
}
}
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++){
REQUIRE(it->second == it->first + 1);
REQUIRE((*it).second == (*it).first + 1);
it->second = it->second + 1;
REQUIRE(it->second == it->first + 2);
}
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++){
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
for (FixedMap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++){
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Insert variants"){
FixedMap<uint32_t, uint16_t>::Iterator it = map.end();
REQUIRE(map.insert(36, 37, &it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
REQUIRE(it->second == 37);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, 24, nullptr) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_ALREADY_EXISTS));
REQUIRE(map.find(37)->second != 24);
REQUIRE(map.size() == 2);
};
SECTION("Serialize and DeSerialize") {
REQUIRE(map.insert(36, 37, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
uint8_t buffer[sizeof(uint32_t)
+ 2 * (sizeof(uint32_t) + sizeof(uint16_t))];
REQUIRE(
map.getSerializedSize()
== (sizeof(uint32_t)
+ 2 * (sizeof(uint32_t) + sizeof(uint16_t))));
uint8_t *loc_ptr = buffer;
size_t size = 0;
REQUIRE(
map.serialize(&loc_ptr, &size, 10, SerializeIF::Endianness::BIG)
== static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
loc_ptr = buffer;
size = 0;
REQUIRE(
map.serialize(&loc_ptr, &size,
sizeof(uint32_t)
+ 2 * (sizeof(uint32_t) + sizeof(uint16_t)),
SerializeIF::Endianness::BIG)
== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(size == 16);
uint32_t internal_size = 0;
const uint8_t *ptr2 = buffer;
REQUIRE(
SerializeAdapter::deSerialize(&internal_size, &ptr2, &size,
SerializeIF::Endianness::BIG)
== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(internal_size == 2);
for (uint8_t i = 36; i < 38; i++) {
uint32_t first_element = 0;
REQUIRE(
SerializeAdapter::deSerialize(&first_element, &ptr2, &size,
SerializeIF::Endianness::BIG)
== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(first_element == i);
uint16_t second_element = 0;
REQUIRE(
SerializeAdapter::deSerialize(&second_element, &ptr2, &size,
SerializeIF::Endianness::BIG)
== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(second_element == i + 1);
}
REQUIRE(size == 0);
map.clear();
const uint8_t* constPtr = buffer;
size = 16;
REQUIRE(map.size() == 0);
REQUIRE(map.deSerialize(&constPtr, &size,
SerializeIF::Endianness::BIG) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.size() == 2);
REQUIRE(map.find(36)->second == 37);
for(auto& element: map){
REQUIRE((element.first+1) == element.second);
}
};
SECTION("Failed erase and deSerialize"){
FixedMap<uint32_t, uint16_t>::Iterator it;
std::pair<uint32_t, uint16_t> pair = std::make_pair(44, 43);
it = FixedMap<uint32_t, uint16_t>::Iterator(&pair);
REQUIRE(map.erase(&it) == static_cast<int>(FixedMap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(45) == map.end());
size_t toLargeMap = 100;
const uint8_t* ptr = reinterpret_cast<uint8_t*>(&toLargeMap);
size_t size = sizeof(size_t);
REQUIRE(map.deSerialize(&ptr, &size, SerializeIF::Endianness::BIG) ==
static_cast<int>(SerializeIF::TOO_MANY_ELEMENTS));
};
SECTION("Little Endianess"){
map.clear();
map.insert(10,20, nullptr);
uint8_t newBuffer[sizeof(uint32_t)+ 1*(sizeof(uint32_t) + sizeof(uint16_t))];
uint8_t* ptr = newBuffer;
size_t size = 0;
size_t max_size = sizeof(uint32_t)+ 1*(sizeof(uint32_t) + sizeof(uint16_t));
REQUIRE(map.serialize(&ptr, &size, max_size,
SerializeIF::Endianness::LITTLE) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
map.clear();
REQUIRE(map.size()==0);
const uint8_t* ptr2 = newBuffer;
REQUIRE(map.deSerialize(&ptr2, &size,
SerializeIF::Endianness::LITTLE) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.size()==1);
REQUIRE(map.find(10)->second == 20);
};
}

View File

@ -0,0 +1,203 @@
#include <fsfw/container/FixedOrderedMultimap.h>
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <catch2/catch.hpp>
#include "../../core/CatchDefinitions.h"
TEST_CASE( "FixedOrderedMultimap Tests", "[TestFixedOrderedMultimap]") {
INFO("FixedOrderedMultimap Tests");
FixedOrderedMultimap<unsigned int, unsigned short> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
SECTION("Test insert, find, exists"){
for (uint16_t i=0;i<30;i++){
REQUIRE(map.insert(std::make_pair(i, i+1))== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(i)->second==i+1);
}
REQUIRE(map.insert(0, 0) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
{
uint16_t* ptr;
REQUIRE(map.find(5,&ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(*ptr == 6);
REQUIRE(map.find(31,&ptr) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element: map){
if (element.first == 5){
REQUIRE(element.second == 6);
}
}
for (FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++){
REQUIRE(it->second == it->first + 1);
REQUIRE((*it).second == (*it).first + 1);
it->second = it->second + 1;
REQUIRE(it->second == it->first + 2);
}
{
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin();
while(it != map.end()){
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
}
for (FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.begin(); it != map.end(); it++){
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Test different insert variants")
{
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it = map.end();
REQUIRE(map.insert(36, 37, &it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
REQUIRE(it->second == 37);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, 38, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, 24, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(37)->second == 38);
REQUIRE(map.insert(0, 1, nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.find(0)->second == 1);
REQUIRE(map.size() == 4);
map.clear();
REQUIRE(map.size() == 0);
}
SECTION("Test different erase and find with no entries"){
FixedOrderedMultimap<uint32_t, uint16_t>::Iterator it;
it = map.end();
REQUIRE(map.erase(&it) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(1)== map.end());
}
}
TEST_CASE( "FixedOrderedMultimap Non Trivial Type", "[TestFixedOrderedMultimapNonTrivial]") {
INFO("FixedOrderedMultimap Non Trivial Type");
class TestClass{
public:
TestClass(){};
TestClass(uint32_t number1, uint64_t number2):
number1(number1),number2(number2){};
~TestClass(){};
bool operator==(const TestClass& lhs){
return ((this->number1 == lhs.number1) and (this->number2 == lhs.number2));
}
bool operator!=(const TestClass& lhs){
return not(this->operator ==(lhs));
}
TestClass(const TestClass& other){
this->number1 = other.number1;
this->number2 = other.number2;
};
TestClass& operator=(const TestClass& other){
this->number1 = other.number1;
this->number2 = other.number2;
return *this;
};
private:
uint32_t number1 = 0;
uint64_t number2 = 5;
};
FixedOrderedMultimap<unsigned int, TestClass> map(30);
REQUIRE(map.size() == 0);
REQUIRE(map.maxSize() == 30);
SECTION("Test insert, find, exists"){
for (uint16_t i=0;i<30;i++){
REQUIRE(map.insert(std::make_pair(i, TestClass(i+1,i)))== static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.exists(i) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool compare = map.find(i)->second == TestClass(i+1,i);
REQUIRE(compare);
}
REQUIRE(map.insert(0, TestClass()) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::MAP_FULL));
REQUIRE(map.exists(31) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 30);
{
TestClass* ptr = nullptr;
REQUIRE(map.find(5,&ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
bool compare = *ptr == TestClass(6, 5);
REQUIRE(compare);
REQUIRE(map.find(31,&ptr) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
}
REQUIRE(map.erase(2) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(map.erase(31) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.exists(2) == static_cast<int>(FixedOrderedMultimap<uint32_t, uint16_t>::KEY_DOES_NOT_EXIST));
REQUIRE(map.size() == 29);
for (auto element: map){
if (element.first == 5){
bool compare = element.second == TestClass(6, 5);
REQUIRE(compare);
}
}
for (FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin(); it != map.end(); it++){
bool compare = it->second == TestClass(it->first + 1, it->first);
REQUIRE(compare);
compare = (*it).second == TestClass((*it).first + 1, (*it).first);
REQUIRE(compare);
it->second = TestClass(it->first + 2, it->first);
compare = it->second == TestClass(it->first + 2, it->first);
REQUIRE(compare);
}
{
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin();
while(it != map.end()){
REQUIRE(map.erase(&it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
}
REQUIRE(map.size() == 0);
}
for (FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.begin(); it != map.end(); it++){
// This line should never executed if begin and end is correct
FAIL("Should never be reached, Iterators invalid");
}
};
SECTION("Test different insert variants")
{
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it = map.end();
REQUIRE(map.insert(36, TestClass(37, 36), &it) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
REQUIRE(it->first == 36);
bool compare = it->second == TestClass(37, 36);
REQUIRE(compare);
REQUIRE(map.size() == 1);
REQUIRE(map.insert(37, TestClass(38, 37), nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(37)->second == TestClass(38, 37);
REQUIRE(compare);
REQUIRE(map.size() == 2);
REQUIRE(map.insert(37, TestClass(24, 37), nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(37)->second == TestClass(38, 37);
REQUIRE(compare);
REQUIRE(map.insert(0, TestClass(1, 0), nullptr) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
compare = map.find(0)->second == TestClass(1, 0);
REQUIRE(compare);
REQUIRE(map.size() == 4);
map.clear();
REQUIRE(map.size() == 0);
}
SECTION("Test different erase and find with no entries"){
FixedOrderedMultimap<uint32_t, TestClass>::Iterator it;
it = map.end();
REQUIRE(map.erase(&it) == static_cast<int>(FixedOrderedMultimap<uint32_t, TestClass>::KEY_DOES_NOT_EXIST));
REQUIRE(map.find(1)== map.end());
}
}

View File

@ -0,0 +1,45 @@
//#include <fsfw/container/PlacementFactory.h>
//#include <fsfw/storagemanager/LocalPool.h>
//#include <fsfw/returnvalues/HasReturnvaluesIF.h>
//#include <fsfw/container/ArrayList.h>
//
//#include <catch2/catch.hpp>
//#include "../../core/CatchDefinitions.h"
//
//TEST_CASE( "PlacementFactory Tests", "[TestPlacementFactory]") {
// INFO("PlacementFactory Tests");
//
// const uint16_t element_sizes[3] = {sizeof(uint16_t), sizeof(uint32_t), sizeof(uint64_t)};
// const uint16_t n_elements[3] = {1, 1, 1};
// LocalPool<3> storagePool(0x1, element_sizes, n_elements, false, true);
// PlacementFactory factory(&storagePool);
//
// SECTION("Pool overload"){
// store_address_t address;
// uint8_t* ptr = nullptr;
// REQUIRE(storagePool.getFreeElement(&address, sizeof(ArrayList<uint32_t, uint16_t>), &ptr)
// == static_cast<int>(StorageManagerIF::DATA_TOO_LARGE));
// ArrayList<uint32_t, uint16_t>* list2 = factory.generate<ArrayList<uint32_t, uint16_t> >(80);
// REQUIRE(list2 == nullptr);
// }
//
// SECTION("Test generate and destroy"){
// uint64_t* number = factory.generate<uint64_t>(32000);
// REQUIRE(number != nullptr);
// REQUIRE(*number == 32000);
// store_address_t address;
// uint8_t* ptr = nullptr;
// REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr)
// == static_cast<int>(StorageManagerIF::DATA_TOO_LARGE));
// uint64_t* number2 = factory.generate<uint64_t>(12345);
// REQUIRE(number2 == nullptr);
// REQUIRE(factory.destroy(number) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(storagePool.getFreeElement(&address, sizeof(uint64_t), &ptr)
// == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
// REQUIRE(storagePool.deleteData(address) == static_cast<int>(HasReturnvaluesIF::RETURN_OK));
//
// //Check that PlacementFactory checks for nullptr
// ptr = nullptr;
// REQUIRE(factory.destroy(ptr) == static_cast<int>(HasReturnvaluesIF::RETURN_FAILED));
// }
//}

View File

@ -0,0 +1,40 @@
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include "catch.hpp"
#include <array>
#include "core/CatchDefinitions.h"
TEST_CASE("MessageQueue Basic Test","[TestMq]") {
MessageQueueIF* testSenderMq =
QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testSenderMqId = testSenderMq->getId();
MessageQueueIF* testReceiverMq =
QueueFactory::instance()->createMessageQueue(1);
MessageQueueId_t testReceiverMqId = testReceiverMq->getId();
std::array<uint8_t, 20> testData { 0 };
testData[0] = 42;
MessageQueueMessage testMessage(testData.data(), 1);
testSenderMq->setDefaultDestination(testReceiverMqId);
SECTION("Simple Tests") {
auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == retval::CATCH_OK);
MessageQueueMessage recvMessage;
result = testReceiverMq->receiveMessage(&recvMessage);
REQUIRE(result == retval::CATCH_OK);
CHECK(recvMessage.getData()[0] == 42);
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == retval::CATCH_OK);
MessageQueueId_t senderId = 0;
result = testReceiverMq->receiveMessage(&recvMessage,&senderId);
REQUIRE(result == retval::CATCH_OK);
CHECK(recvMessage.getData()[0] == 42);
CHECK(senderId == testSenderMqId);
senderId = testReceiverMq->getLastPartner();
CHECK(senderId == testSenderMqId);
}
}

Some files were not shown because too many files have changed in this diff Show More