Merge branch 'mueller/master' into mueller/fsfw-update

This commit is contained in:
Robin Müller 2021-01-25 15:17:50 +01:00
commit 7e0cc471c9
45 changed files with 3685 additions and 755 deletions

View File

@ -18,7 +18,7 @@ public:
* This function is protected because it should only be used by the
* class imlementing the interface.
*/
virtual LocalDataPoolManager* getHkManagerHandle() = 0;
virtual LocalDataPoolManager* getPoolManagerHandle() = 0;
protected:

View File

@ -5,8 +5,8 @@
#include "internal/LocalPoolDataSetAttorney.h"
#include "internal/HasLocalDpIFManagerAttorney.h"
#include "../housekeeping/HousekeepingPacketUpdate.h"
#include "../housekeeping/HousekeepingSetPacket.h"
#include "../housekeeping/HousekeepingSnapshot.h"
#include "../housekeeping/AcceptsHkPacketsIF.h"
#include "../timemanager/CCSDSTime.h"
#include "../ipc/MutexFactory.h"
@ -226,7 +226,7 @@ ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot(
Clock::getClock_timeval(&now);
CCSDSTime::CDS_short cds;
CCSDSTime::convertToCcsds(&cds, &now);
HousekeepingPacketUpdate updatePacket(reinterpret_cast<uint8_t*>(&cds),
HousekeepingSnapshot updatePacket(reinterpret_cast<uint8_t*>(&cds),
sizeof(cds), HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner,
receiver.dataId.localPoolId));
@ -264,7 +264,7 @@ ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot(
Clock::getClock_timeval(&now);
CCSDSTime::CDS_short cds;
CCSDSTime::convertToCcsds(&cds, &now);
HousekeepingPacketUpdate updatePacket(reinterpret_cast<uint8_t*>(&cds),
HousekeepingSnapshot updatePacket(reinterpret_cast<uint8_t*>(&cds),
sizeof(cds), HasLocalDpIFManagerAttorney::getDataSetHandle(owner,
receiver.dataId.sid));
@ -292,7 +292,7 @@ ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot(
}
ReturnValue_t LocalDataPoolManager::addUpdateToStore(
HousekeepingPacketUpdate& updatePacket, store_address_t& storeId) {
HousekeepingSnapshot& updatePacket, store_address_t& storeId) {
size_t updatePacketSize = updatePacket.getSerializedSize();
uint8_t *storePtr = nullptr;
ReturnValue_t result = ipcStore->getFreeElement(&storeId,
@ -906,6 +906,6 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
}
}
LocalDataPoolManager* LocalDataPoolManager::getHkManagerHandle() {
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() {
return this;
}

View File

@ -24,7 +24,7 @@ void setStaticFrameworkObjectIds();
}
class LocalPoolDataSetBase;
class HousekeepingPacketUpdate;
class HousekeepingSnapshot;
class HasLocalDataPoolIF;
class LocalDataPool;
@ -52,7 +52,8 @@ class LocalDataPool;
* Each pool entry has a valid state too.
* @author R. Mueller
*/
class LocalDataPoolManager: public ProvidesDataPoolSubscriptionIF,
class LocalDataPoolManager:
public ProvidesDataPoolSubscriptionIF,
public AccessPoolManagerIF {
friend void (Factory::setStaticFrameworkObjectIds)();
//! Some classes using the pool manager directly need to access class internals of the
@ -62,7 +63,6 @@ public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER;
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_INVALID = MAKE_RETURN_CODE(0);
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(1);
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(2);
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(3);
@ -179,8 +179,6 @@ public:
MessageQueueId_t targetQueueId,
bool generateSnapshot) override;
MutexIF* getLocalPoolMutex() override;
/**
* Non-Diagnostics packets usually have a lower minimum sampling frequency
* than diagnostic packets.
@ -243,12 +241,9 @@ public:
UPDATE_SNAPSHOT,
};
/**
* Different data types are possible in the HK receiver map.
* For example, updates can be requested for full datasets or
* for single pool variables. Periodic reporting is only possible for
* data sets.
*/
/** Different data types are possible in the HK receiver map. For example, updates can be
requested for full datasets or for single pool variables. Periodic reporting is only possible
for data sets. */
enum class DataType: uint8_t {
LOCAL_POOL_VARIABLE,
DATA_SET
@ -267,11 +262,19 @@ public:
object_id_t getCreatorObjectId() const;
virtual LocalDataPoolManager* getHkManagerHandle() override;
/**
* Get the pointer to the mutex. Can be used to lock the data pool
* externally. Use with care and don't forget to unlock locked mutexes!
* For now, only friend classes can accss this function.
* @return
*/
MutexIF* getMutexHandle();
virtual LocalDataPoolManager* getPoolManagerHandle() override;
private:
localpool::DataPool localPoolMap;
//! Every housekeeping data manager has a mutex to protect access
//! to it's data pool.
/** Every housekeeping data manager has a mutex to protect access
to it's data pool. */
MutexIF* mutex = nullptr;
/** The class which actually owns the manager (and its datapool). */
@ -333,13 +336,6 @@ private:
/** Global IPC store is used to store all packets. */
StorageManagerIF* ipcStore = nullptr;
/**
* Get the pointer to the mutex. Can be used to lock the data pool
* externally. Use with care and don't forget to unlock locked mutexes!
* For now, only friend classes can accss this function.
* @return
*/
MutexIF* getMutexHandle();
/**
* Read a variable by supplying its local pool ID and assign the pool
@ -364,6 +360,8 @@ private:
*/
ReturnValue_t initializeHousekeepingPoolEntriesOnce();
MutexIF* getLocalPoolMutex() override;
ReturnValue_t serializeHkPacketIntoStore(
HousekeepingPacketDownlink& hkPacket,
store_address_t& storeId, bool forDownlink, size_t* serializedSize);
@ -386,7 +384,7 @@ private:
ReturnValue_t& status);
ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver,
ReturnValue_t& status);
ReturnValue_t addUpdateToStore(HousekeepingPacketUpdate& updatePacket,
ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket,
store_address_t& storeId);
void printWarningOrError(sif::OutputTypes outputType,

View File

@ -4,11 +4,26 @@
#include "LocalPoolDataSetBase.h"
#include <vector>
/**
* @brief This dataset type can be used to group related pool variables if the number of
* variables should not be fixed.
* @details
* This will is the primary data structure to organize pool variables into
* sets which can be accessed via the housekeeping service interface or
* which can be sent to other software objects.
*
* It is recommended to read the documentation of the LocalPoolDataSetBase
* class for more information on how this class works and how to use it.
* @tparam capacity Capacity of the static dataset, which is usually known
* beforehand.
*/
class LocalDataSet: public LocalPoolDataSetBase {
public:
LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId,
const size_t maxSize);
LocalDataSet(sid_t sid, const size_t maxSize);
virtual~ LocalDataSet();
//! Copying forbidden for now.

View File

@ -28,7 +28,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if(accessor != nullptr) {
poolManager = accessor->getHkManagerHandle();
poolManager = accessor->getPoolManagerHandle();
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
}

View File

@ -22,7 +22,7 @@ LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkO
return;
}
AccessPoolManagerIF* poolManAccessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
hkManager = poolManAccessor->getHkManagerHandle();
hkManager = poolManAccessor->getPoolManagerHandle();
if (dataSet != nullptr) {
dataSet->registerVariable(this);
@ -50,7 +50,7 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if(accessor != nullptr) {
hkManager = accessor->getHkManagerHandle();
hkManager = accessor->getPoolManagerHandle();
}
if(dataSet != nullptr) {

View File

@ -77,8 +77,7 @@ public:
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVector(gp_id_t globalPoolId,
DataSetIF* dataSet = nullptr,
LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
@ -87,7 +86,7 @@ public:
* The user can work on this attribute just like he would on a local
* array of this type.
*/
T value[vectorSize];
T value[vectorSize]= {};
/**
* @brief The classes destructor is empty.
* @details If commit() was not called, the local value is

View File

@ -16,7 +16,6 @@ inline LocalPoolVector<T, vectorSize>::LocalPoolVector(object_id_t poolOwner,
lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template<typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId,
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):

View File

@ -6,7 +6,8 @@
#include <array>
/**
* @brief This local dataset type is created on the stack.
* @brief This dataset type can be used to group related pool variables if the number of
* variables is fixed.
* @details
* This will is the primary data structure to organize pool variables into
* sets which can be accessed via the housekeeping service interface or

3
doc/doxy/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
html
latex
rtf

2609
doc/doxy/OPUS.doxyfile Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
#include "../ipc/QueueFactory.h"
#include "../ipc/MutexFactory.h"
MessageQueueId_t EventManagerIF::eventmanagerQueue = MessageQueueIF::NO_QUEUE;
// If one checks registerListener calls, there are around 40 (to max 50)
// objects registering for certain events.

View File

@ -1,11 +1,12 @@
#ifndef EVENTMANAGERIF_H_
#define EVENTMANAGERIF_H_
#ifndef FSFW_EVENTS_EVENTMANAGERIF_H_
#define FSFW_EVENTS_EVENTMANAGERIF_H_
#include "EventMessage.h"
#include "eventmatching/eventmatching.h"
#include "../objectmanager/ObjectManagerIF.h"
#include "../ipc/MessageQueueSenderIF.h"
#include "../ipc/MessageQueueIF.h"
#include "../serviceinterface/ServiceInterface.h"
class EventManagerIF {
public:
@ -41,11 +42,19 @@ public:
static void triggerEvent(EventMessage* message,
MessageQueueId_t sentFrom = 0) {
static MessageQueueId_t eventmanagerQueue = MessageQueueIF::NO_QUEUE;
if (eventmanagerQueue == MessageQueueIF::NO_QUEUE) {
EventManagerIF *eventmanager = objectManager->get<EventManagerIF>(
objects::EVENT_MANAGER);
if (eventmanager == nullptr) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "EventManagerIF::triggerEvent: EventManager invalid or not found!"
<< std::endl;
#else
sif::printWarning("EventManagerIF::triggerEvent: "
"EventManager invalid or not found!");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return;
}
eventmanagerQueue = eventmanager->getEventReportQueue();
@ -53,6 +62,10 @@ public:
MessageQueueSenderIF::sendMessage(eventmanagerQueue, message, sentFrom);
}
private:
//! Initialized by EventManager (C++11 does not allow header-only static member initialization).
static MessageQueueId_t eventmanagerQueue;
};
#endif /* EVENTMANAGERIF_H_ */
#endif /* FSFW_EVENTS_EVENTMANAGERIF_H_ */

View File

@ -2,16 +2,21 @@
#include "../serviceinterface/ServiceInterface.h"
#include <bitset>
void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
bool printInfo, size_t maxCharPerLine) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
if(printInfo) {
sif::info << "Printing data with size " << size << ": ";
sif::info << "Printing data with size " << size << ": " << std::endl;
}
sif::info << "[";
#else
sif::printInfo("Printing data with size %zu: [", size);
#endif
#if FSFW_NO_C99_IO == 1
sif::printInfo("Printing data with size %lu: \n", static_cast<unsigned long>(size));
#else
sif::printInfo("Printing data with size %zu: \n", size);
#endif /* FSFW_NO_C99_IO == 1 */
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
if(type == OutputType::HEX) {
arrayprinter::printHex(data, size, maxCharPerLine);
}
@ -26,52 +31,95 @@ void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
void arrayprinter::printHex(const uint8_t *data, size_t size,
size_t maxCharPerLine) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << std::hex;
if(sif::info.crAdditionEnabled()) {
std::cout << "\r" << std::endl;
}
std::cout << "[" << std::hex;
for(size_t i = 0; i < size; i++) {
sif::info << "0x" << static_cast<int>(data[i]);
if(i < size - 1){
sif::info << " , ";
if(i > 0 and i % maxCharPerLine == 0) {
sif::info << std::endl;
std::cout << "0x" << static_cast<int>(data[i]);
if(i < size - 1) {
std::cout << " , ";
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
std::cout << std::endl;
}
}
}
sif::info << std::dec;
sif::info << "]" << std::endl;
std::cout << std::dec;
std::cout << "]" << std::endl;
#else
// how much memory to reserve for printout?
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
// plus line break plus small safety margin.
char printBuffer[(size + 1) * 7 + 1];
size_t currentPos = 0;
for(size_t i = 0; i < size; i++) {
// To avoid buffer overflows.
if(sizeof(printBuffer) - currentPos <= 7) {
break;
}
currentPos += snprintf(printBuffer + currentPos, 6, "0x%02x", data[i]);
if(i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ", ");
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
}
}
}
printf("[%s]\n", printBuffer);
#endif
}
void arrayprinter::printDec(const uint8_t *data, size_t size,
size_t maxCharPerLine) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << std::dec;
if(sif::info.crAdditionEnabled()) {
std::cout << "\r" << std::endl;
}
std::cout << "[" << std::dec;
for(size_t i = 0; i < size; i++) {
sif::info << static_cast<int>(data[i]);
std::cout << static_cast<int>(data[i]);
if(i < size - 1){
sif::info << " , ";
if(i > 0 and i % maxCharPerLine == 0) {
sif::info << std::endl;
std::cout << " , ";
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
std::cout << std::endl;
}
}
}
sif::info << "]" << std::endl;
std::cout << "]" << std::endl;
#else
// how much memory to reserve for printout?
// General format: 32, 243, -12 so it is number of chars times 5
// plus line break plus small safety margin.
char printBuffer[(size + 1) * 5 + 1];
size_t currentPos = 0;
for(size_t i = 0; i < size; i++) {
// To avoid buffer overflows.
if(sizeof(printBuffer) - currentPos <= 5) {
break;
}
currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]);
if(i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ", ");
if(i > 0 and (i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n");
}
}
}
printf("[%s]\n", printBuffer);
#endif
}
void arrayprinter::printBin(const uint8_t *data, size_t size) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "\n" << std::flush;
for(size_t i = 0; i < size; i++) {
sif::info << "Byte " << i + 1 << ": 0b"<<
std::bitset<8>(data[i]) << ",\n" << std::flush;
sif::info << "Byte " << i + 1 << ": 0b" << std::bitset<8>(data[i]) << std::endl;
}
sif::info << "]" << std::endl;
#else
// how much memory to reserve for printout?
for(size_t i = 0; i < size; i++) {
sif::printInfo("Byte %d: 0b" BYTE_TO_BINARY_PATTERN "\n", i + 1, BYTE_TO_BINARY(data[i]));
}
#endif
}

View File

@ -1,5 +1,6 @@
#ifndef FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_
#define FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_
#include <cstdint>
#include <cstddef>
@ -10,11 +11,15 @@ enum class OutputType {
};
namespace arrayprinter {
void print(const uint8_t* data, size_t size, OutputType type = OutputType::HEX,
bool printInfo = true, size_t maxCharPerLine = 12);
void printHex(const uint8_t* data, size_t size, size_t maxCharPerLine = 12);
void printDec(const uint8_t* data, size_t size, size_t maxCharPerLine = 12);
bool printInfo = true, size_t maxCharPerLine = 10);
void printHex(const uint8_t* data, size_t size, size_t maxCharPerLine = 10);
void printDec(const uint8_t* data, size_t size, size_t maxCharPerLine = 10);
void printBin(const uint8_t* data, size_t size);
}
#endif /* FRAMEWORK_GLOBALFUNCTIONS_ARRAYPRINTER_H_ */

View File

@ -1,24 +1,37 @@
#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_
#define FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_
#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGSNAPSHOT_H_
#define FSFW_HOUSEKEEPING_HOUSEKEEPINGSNAPSHOT_H_
#include "../serialize/SerialBufferAdapter.h"
#include "../serialize/SerialLinkedListAdapter.h"
#include "../datapoollocal/LocalPoolDataSetBase.h"
#include "../datapoollocal/LocalPoolObjectBase.h"
#include "../timemanager/CCSDSTime.h"
/**
* @brief This helper class will be used to serialize and deserialize
* update housekeeping packets into the store.
* @brief This helper class will be used to serialize and deserialize update housekeeping packets
* into the store.
*/
class HousekeepingPacketUpdate: public SerializeIF {
class HousekeepingSnapshot: public SerializeIF {
public:
/**
* Update packet constructor for datasets
* @param timeStamp
* @param timeStampSize
* @param hkData
* @param hkDataSize
* Update packet constructor for datasets.
* @param cdsShort If a CSD short timestamp is used, a reference should be
* supplied here
* @param dataSetPtr Pointer to the dataset instance to serialize or deserialize the
* data into
*/
HousekeepingPacketUpdate(uint8_t* timeStamp, size_t timeStampSize,
HousekeepingSnapshot(CCSDSTime::CDS_short* cdsShort, LocalPoolDataSetBase* dataSetPtr):
timeStamp(reinterpret_cast<uint8_t*>(cdsShort)),
timeStampSize(sizeof(CCSDSTime::CDS_short)), updateData(dataSetPtr) {};
/**
* Update packet constructor for datasets.
* @param timeStamp Pointer to the buffer where the timestamp will be stored.
* @param timeStampSize Size of the timestamp
* @param dataSetPtr Pointer to the dataset instance to deserialize the data into
*/
HousekeepingSnapshot(uint8_t* timeStamp, size_t timeStampSize,
LocalPoolDataSetBase* dataSetPtr):
timeStamp(timeStamp), timeStampSize(timeStampSize),
updateData(dataSetPtr) {};
@ -29,7 +42,7 @@ public:
* @param timeStampSize
* @param dataSetPtr
*/
HousekeepingPacketUpdate(uint8_t* timeStamp, size_t timeStampSize,
HousekeepingSnapshot(uint8_t* timeStamp, size_t timeStampSize,
LocalPoolObjectBase* dataSetPtr):
timeStamp(timeStamp), timeStampSize(timeStampSize),
updateData(dataSetPtr) {};
@ -89,4 +102,4 @@ private:
};
#endif /* FSFW_HOUSEKEEPING_HOUSEKEEPINGPACKETUPDATE_H_ */
#endif /* FSFW_HOUSEKEEPING_HOUSEKEEPINGSNAPSHOT_H_ */

View File

@ -53,8 +53,7 @@ ReturnValue_t Clock::getClock_timeval(timeval* time) {
auto epoch = now.time_since_epoch();
time->tv_sec = std::chrono::duration_cast<std::chrono::seconds>(epoch).count();
auto fraction = now - secondsChrono;
time->tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
fraction).count();
time->tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(fraction).count();
return HasReturnvaluesIF::RETURN_OK;
#elif defined(LINUX)
timespec timeUnix;
@ -67,7 +66,9 @@ ReturnValue_t Clock::getClock_timeval(timeval* time) {
return HasReturnvaluesIF::RETURN_OK;
#else
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "Clock::getUptime: Not implemented for found OS" << std::endl;
sif::warning << "Clock::getUptime: Not implemented for found OS!" << std::endl;
#else
sif::printWarning("Clock::getUptime: Not implemented for found OS!\n");
#endif
return HasReturnvaluesIF::RETURN_FAILED;
#endif

View File

@ -152,7 +152,7 @@ void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
" the requested " << stackMb << " MB" << std::endl;
#else
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %zu MB\n", stackMb);
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
#endif
}
else if(errno == EINVAL) {

View File

@ -29,7 +29,7 @@ ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string setMessage,
#if FSFW_COLORED_OUTPUT == 1
if(setMessage.find("DEBUG") != std::string::npos) {
colorPrefix = sif::ANSI_COLOR_MAGENTA;
colorPrefix = sif::ANSI_COLOR_CYAN;
}
else if(setMessage.find("INFO") != std::string::npos) {
colorPrefix = sif::ANSI_COLOR_GREEN;
@ -167,7 +167,9 @@ std::string* ServiceInterfaceBuffer::getPreamble(size_t * preambleSize) {
return &preamble;
}
bool ServiceInterfaceBuffer::crAdditionEnabled() const {
return addCrToPreamble;
}
#ifdef UT699
#include "../osal/rtems/Interrupt.h"

View File

@ -70,6 +70,8 @@ private:
void putChars(char const* begin, char const* end);
std::string* getPreamble(size_t * preambleSize = nullptr);
bool crAdditionEnabled() const;
};
#endif

View File

@ -45,7 +45,7 @@ void fsfwPrint(sif::PrintLevel printType, const char* fmt, va_list arg) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_GREEN);
}
else if(printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_MAGENTA);
len += sprintf(bufferPosition, sif::ANSI_COLOR_CYAN);
}
else if(printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_YELLOW);

View File

@ -1,7 +1,24 @@
#ifndef FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER
#define FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER
#if FSFW_DISABLE_PRINTOUT == 0
#include <cstdio>
#endif
//! https://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format
//! Can be used to print out binary numbers in human-readable format.
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
namespace sif {
enum PrintLevel {
@ -38,3 +55,4 @@ void printError(const char* fmt, ...);
}
#endif /* FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER */

View File

@ -15,5 +15,9 @@ std::string* ServiceInterfaceStream::getPreamble() {
return streambuf.getPreamble();
}
bool ServiceInterfaceStream::crAdditionEnabled() const {
return streambuf.crAdditionEnabled();
}
#endif

View File

@ -39,6 +39,13 @@ public:
*/
std::string* getPreamble();
/**
* Can be used to determine if the stream was configured to add CR characters in addition
* to newline characters.
* @return
*/
bool crAdditionEnabled() const;
protected:
ServiceInterfaceBuffer streambuf;
};

View File

@ -5,15 +5,15 @@
LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig,
bool registered, bool spillsToHigherPools):
SystemObject(setObjectId, registered),
NUMBER_OF_POOLS(poolConfig.size()),
NUMBER_OF_SUBPOOLS(poolConfig.size()),
spillsToHigherPools(spillsToHigherPools) {
if(NUMBER_OF_POOLS == 0) {
if(NUMBER_OF_SUBPOOLS == 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPool::LocalPool: Passed pool configuration is "
<< " invalid!" << std::endl;
#endif
}
max_pools_t index = 0;
max_subpools_t index = 0;
for (const auto& currentPoolConfig: poolConfig) {
this->numberOfElements[index] = currentPoolConfig.first;
this->elementSizes[index] = currentPoolConfig.second;
@ -98,7 +98,7 @@ ReturnValue_t LocalPool::modifyData(store_address_t storeId,
ReturnValue_t LocalPool::modifyData(store_address_t storeId,
uint8_t **packetPtr, size_t *size) {
ReturnValue_t status = RETURN_FAILED;
if (storeId.poolIndex >= NUMBER_OF_POOLS) {
if (storeId.poolIndex >= NUMBER_OF_SUBPOOLS) {
return ILLEGAL_STORAGE_ID;
}
if ((storeId.packetIndex >= numberOfElements[storeId.poolIndex])) {
@ -127,7 +127,7 @@ ReturnValue_t LocalPool::deleteData(store_address_t storeId) {
#endif
ReturnValue_t status = RETURN_OK;
size_type pageSize = getPageSize(storeId.poolIndex);
size_type pageSize = getSubpoolElementSize(storeId.poolIndex);
if ((pageSize != 0) and
(storeId.packetIndex < numberOfElements[storeId.poolIndex])) {
uint16_t packetPosition = getRawPosition(storeId);
@ -151,7 +151,7 @@ ReturnValue_t LocalPool::deleteData(uint8_t *ptr, size_t size,
store_address_t *storeId) {
store_address_t localId;
ReturnValue_t result = ILLEGAL_ADDRESS;
for (uint16_t n = 0; n < NUMBER_OF_POOLS; n++) {
for (uint16_t n = 0; n < NUMBER_OF_SUBPOOLS; n++) {
//Not sure if new allocates all stores in order. so better be careful.
if ((store[n].data() <= ptr) and
(&store[n][numberOfElements[n]*elementSizes[n]] > ptr)) {
@ -192,7 +192,7 @@ ReturnValue_t LocalPool::initialize() {
}
//Check if any pool size is large than the maximum allowed.
for (uint8_t count = 0; count < NUMBER_OF_POOLS; count++) {
for (uint8_t count = 0; count < NUMBER_OF_SUBPOOLS; count++) {
if (elementSizes[count] >= STORAGE_FREE) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPool::initialize: Pool is too large! "
@ -217,7 +217,7 @@ void LocalPool::clearStore() {
ReturnValue_t LocalPool::reserveSpace(const size_t size,
store_address_t *storeId, bool ignoreFault) {
ReturnValue_t status = getPoolIndex(size, &storeId->poolIndex);
ReturnValue_t status = getSubPoolIndex(size, &storeId->poolIndex);
if (status != RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPool( " << std::hex << getObjectId() << std::dec
@ -227,7 +227,7 @@ ReturnValue_t LocalPool::reserveSpace(const size_t size,
}
status = findEmpty(storeId->poolIndex, &storeId->packetIndex);
while (status != RETURN_OK && spillsToHigherPools) {
status = getPoolIndex(size, &storeId->poolIndex, storeId->poolIndex + 1);
status = getSubPoolIndex(size, &storeId->poolIndex, storeId->poolIndex + 1);
if (status != RETURN_OK) {
//We don't find any fitting pool anymore.
break;
@ -263,9 +263,9 @@ void LocalPool::write(store_address_t storeId, const uint8_t *data,
sizeLists[storeId.poolIndex][storeId.packetIndex] = size;
}
LocalPool::size_type LocalPool::getPageSize(max_pools_t poolIndex) {
if (poolIndex < NUMBER_OF_POOLS) {
return elementSizes[poolIndex];
LocalPool::size_type LocalPool::getSubpoolElementSize(max_subpools_t subpoolIndex) {
if (subpoolIndex < NUMBER_OF_SUBPOOLS) {
return elementSizes[subpoolIndex];
}
else {
return 0;
@ -276,9 +276,9 @@ void LocalPool::setToSpillToHigherPools(bool enable) {
this->spillsToHigherPools = enable;
}
ReturnValue_t LocalPool::getPoolIndex(size_t packetSize, uint16_t *poolIndex,
ReturnValue_t LocalPool::getSubPoolIndex(size_t packetSize, uint16_t *subpoolIndex,
uint16_t startAtIndex) {
for (uint16_t n = startAtIndex; n < NUMBER_OF_POOLS; n++) {
for (uint16_t n = startAtIndex; n < NUMBER_OF_SUBPOOLS; n++) {
#if FSFW_VERBOSE_PRINTOUT == 2
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: "
@ -286,7 +286,7 @@ ReturnValue_t LocalPool::getPoolIndex(size_t packetSize, uint16_t *poolIndex,
#endif
#endif
if (elementSizes[n] >= packetSize) {
*poolIndex = n;
*subpoolIndex = n;
return RETURN_OK;
}
}
@ -313,7 +313,7 @@ ReturnValue_t LocalPool::findEmpty(n_pool_elem_t poolIndex, uint16_t *element) {
size_t LocalPool::getTotalSize(size_t* additionalSize) {
size_t totalSize = 0;
size_t sizesSize = 0;
for(uint8_t idx = 0; idx < NUMBER_OF_POOLS; idx ++) {
for(uint8_t idx = 0; idx < NUMBER_OF_SUBPOOLS; idx ++) {
totalSize += elementSizes[idx] * numberOfElements[idx];
sizesSize += numberOfElements[idx] * sizeof(size_type);
}
@ -331,7 +331,7 @@ void LocalPool::getFillCount(uint8_t *buffer, uint8_t *bytesWritten) {
uint16_t reservedHits = 0;
uint8_t idx = 0;
uint16_t sum = 0;
for(; idx < NUMBER_OF_POOLS; idx ++) {
for(; idx < NUMBER_OF_SUBPOOLS; idx ++) {
for(const auto& size: sizeLists[idx]) {
if(size != STORAGE_FREE) {
reservedHits++;
@ -343,21 +343,25 @@ void LocalPool::getFillCount(uint8_t *buffer, uint8_t *bytesWritten) {
sum += buffer[idx];
reservedHits = 0;
}
buffer[idx] = sum / NUMBER_OF_POOLS;
buffer[idx] = sum / NUMBER_OF_SUBPOOLS;
*bytesWritten += 1;
}
void LocalPool::clearPage(max_pools_t pageIndex) {
if(pageIndex >= NUMBER_OF_POOLS) {
void LocalPool::clearSubPool(max_subpools_t subpoolIndex) {
if(subpoolIndex >= NUMBER_OF_SUBPOOLS) {
return;
}
// Mark the storage as free
for(auto& size: sizeLists[pageIndex]) {
for(auto& size: sizeLists[subpoolIndex]) {
size = STORAGE_FREE;
}
// Set all the page content to 0.
std::memset(store[pageIndex].data(), 0, elementSizes[pageIndex]);
std::memset(store[subpoolIndex].data(), 0, elementSizes[subpoolIndex]);
}
LocalPool::max_subpools_t LocalPool::getNumberOfSubPools() const {
return NUMBER_OF_SUBPOOLS;
}

View File

@ -60,15 +60,6 @@ public:
};
using LocalPoolConfig = std::multiset<LocalPoolCfgPair, LocalPoolConfigCmp>;
/**
* @brief This definition generally sets the number of
* different sized pools. It is derived from the number of pairs
* inside the LocalPoolConfig set on object creation.
* @details
* This must be less than the maximum number of pools (currently 0xff).
*/
const max_pools_t NUMBER_OF_POOLS;
/**
* @brief This is the default constructor for a pool manager instance.
* @details
@ -143,9 +134,15 @@ public:
void getFillCount(uint8_t* buffer, uint8_t* bytesWritten) override;
void clearStore() override;
void clearPage(max_pools_t pageIndex) override;
void clearSubPool(max_subpools_t poolIndex) override;
ReturnValue_t initialize() override;
/**
* Get number sub pools. Each pool has pages with a specific bucket size.
* @return
*/
max_subpools_t getNumberOfSubPools() const override;
protected:
/**
* With this helper method, a free element of @c size is reserved.
@ -158,6 +155,16 @@ protected:
store_address_t* address, bool ignoreFault);
private:
/**
* @brief This definition generally sets the number of
* different sized pools. It is derived from the number of pairs
* inside the LocalPoolConfig set on object creation.
* @details
* This must be less than the maximum number of pools (currently 0xff).
*/
const max_subpools_t NUMBER_OF_SUBPOOLS;
/**
* Indicates that this element is free.
* This value limits the maximum size of a pool.
@ -170,20 +177,20 @@ private:
* must be set in ascending order on construction.
*/
std::vector<size_type> elementSizes =
std::vector<size_type>(NUMBER_OF_POOLS);
std::vector<size_type>(NUMBER_OF_SUBPOOLS);
/**
* @brief n_elements stores the number of elements per pool.
* @details These numbers are maintained for internal pool management.
*/
std::vector<uint16_t> numberOfElements =
std::vector<uint16_t>(NUMBER_OF_POOLS);
std::vector<uint16_t>(NUMBER_OF_SUBPOOLS);
/**
* @brief store represents the actual memory pool.
* @details It is an array of pointers to memory, which was allocated with
* a @c new call on construction.
*/
std::vector<std::vector<uint8_t>> store =
std::vector<std::vector<uint8_t>>(NUMBER_OF_POOLS);
std::vector<std::vector<uint8_t>>(NUMBER_OF_SUBPOOLS);
/**
* @brief The size_list attribute stores the size values of every pool element.
@ -191,7 +198,7 @@ private:
* is also dynamically allocated there.
*/
std::vector<std::vector<size_type>> sizeLists =
std::vector<std::vector<size_type>>(NUMBER_OF_POOLS);
std::vector<std::vector<size_type>>(NUMBER_OF_SUBPOOLS);
//! A variable to determine whether higher n pools are used if
//! the store is full.
@ -210,7 +217,7 @@ private:
* @param pool_index The pool in which to look.
* @return Returns the size of an element or 0.
*/
size_type getPageSize(max_pools_t poolIndex);
size_type getSubpoolElementSize(max_subpools_t subpoolIndex);
/**
* @brief This helper method looks up a fitting pool for a given size.
@ -221,7 +228,7 @@ private:
* @return - @c RETURN_OK on success,
* - @c DATA_TOO_LARGE otherwise.
*/
ReturnValue_t getPoolIndex(size_t packetSize, uint16_t* poolIndex,
ReturnValue_t getSubPoolIndex(size_t packetSize, uint16_t* subpoolIndex,
uint16_t startAtIndex = 0);
/**
* @brief This helper method calculates the true array position in store

View File

@ -29,7 +29,7 @@ using ConstAccessorPair = std::pair<ReturnValue_t, ConstStorageAccessor>;
class StorageManagerIF : public HasReturnvaluesIF {
public:
using size_type = size_t;
using max_pools_t = uint8_t;
using max_subpools_t = uint8_t;
static const uint8_t INTERFACE_ID = CLASS_ID::STORAGE_MANAGER_IF; //!< The unique ID for return codes for this interface.
static const ReturnValue_t DATA_TOO_LARGE = MAKE_RETURN_CODE(1); //!< This return code indicates that the data to be stored is too large for the store.
@ -149,10 +149,10 @@ public:
virtual ReturnValue_t modifyData(store_address_t packet_id,
uint8_t** packet_ptr, size_t* size) = 0;
/**
* This method reserves an element of \c size.
* This method reserves an element of @c size.
*
* It returns the packet id of this element as well as a direct pointer to the
* data of the element. It must be assured that exactly \c size data is
* data of the element. It must be assured that exactly @c size data is
* written to p_data!
* @param storageId A pointer to the storageId to retrieve.
* @param size The size of the space to be reserved.
@ -171,20 +171,29 @@ public:
virtual void clearStore() = 0;
/**
* Clears a page in the store. Use with care!
* Clears a pool in the store with the given pool index. Use with care!
* @param pageIndex
*/
virtual void clearPage(uint8_t pageIndex) = 0;
virtual void clearSubPool(uint8_t poolIndex) = 0;
/**
* Get the fill count of the pool. The exact form will be implementation
* dependant.
* Get the fill count of the pool. Each character inside the provided
* buffer will be assigned to a rounded percentage fill count for each
* page. The last written byte (at the index bytesWritten - 1)
* will contain the total fill count of the pool as a mean of the
* percentages of single pages.
* @param buffer
* @param bytesWritten
* @param maxSize
*/
virtual void getFillCount(uint8_t* buffer, uint8_t* bytesWritten) = 0;
virtual size_t getTotalSize(size_t* additionalSize) = 0;
/**
* Get number of pools.
* @return
*/
virtual max_subpools_t getNumberOfSubPools() const = 0;
};
#endif /* FSFW_STORAGEMANAGER_STORAGEMANAGERIF_H_ */

View File

@ -1,5 +1,5 @@
#include "Stopwatch.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
#include <iomanip>
Stopwatch::Stopwatch(bool displayOnDestruction,
@ -28,9 +28,13 @@ double Stopwatch::stopSeconds() {
void Stopwatch::display() {
if(displayMode == StopwatchDisplayMode::MILLIS) {
dur_millis_t timeMillis = static_cast<dur_millis_t>(
elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000);
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Stopwatch: Operation took " << (elapsedTime.tv_sec * 1000 +
elapsedTime.tv_usec / 1000) << " milliseconds" << std::endl;
sif::info << "Stopwatch: Operation took " << timeMillis << " milliseconds" << std::endl;
#else
sif::printInfo("Stopwatch: Operation took %lu milliseconds\n\r",
static_cast<unsigned int>(timeMillis));
#endif
}
else if(displayMode == StopwatchDisplayMode::SECONDS) {
@ -38,6 +42,9 @@ void Stopwatch::display() {
sif::info <<"Stopwatch: Operation took " << std::setprecision(3)
<< std::fixed << timevalOperations::toDouble(elapsedTime)
<< " seconds" << std::endl;
#else
sif::printInfo("Stopwatch: Operation took %.3f seconds\n\r",
static_cast<float>(timevalOperations::toDouble(elapsedTime)));
#endif
}
}

View File

@ -1,2 +1,5 @@
add_subdirectory(internal)
add_subdirectory(tests)
if(LINK_CATCH2)
add_subdirectory(tests)
endif()

View File

@ -5,3 +5,4 @@ target_sources(${TARGET_NAME} PRIVATE
add_subdirectory(osal)
add_subdirectory(serialize)
add_subdirectory(globalfunctions)

View File

@ -5,6 +5,7 @@
#include "osal/IntTestSemaphore.h"
#include "osal/IntTestMutex.h"
#include "serialize/IntTestSerialization.h"
#include "globalfunctions/TestArrayPrinter.h"
#include <cstdlib>
@ -12,17 +13,26 @@ InternalUnitTester::InternalUnitTester() {}
InternalUnitTester::~InternalUnitTester() {}
ReturnValue_t InternalUnitTester::performTests() {
ReturnValue_t InternalUnitTester::performTests(struct InternalUnitTester::TestConfig& testConfig) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Running internal unit tests.." << std::endl;
#else
sif::printInfo("Running internal unit tests..\n");
#endif
testserialize::test_serialization();
testmq::testMq();
testsemaph::testBinSemaph();
testsemaph::testCountingSemaph();
testmutex::testMutex();
if(testConfig.testArrayPrinter) {
arrayprinter::testArrayPrinter();
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "Internal unit tests finished." << std::endl;
#else
sif::printInfo("Internal unit tests finished.\n");
#endif
return RETURN_OK;
}

View File

@ -4,6 +4,7 @@
#include "UnittDefinitions.h"
#include "../../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.
@ -15,6 +16,10 @@
*/
class InternalUnitTester: public HasReturnvaluesIF {
public:
struct TestConfig {
bool testArrayPrinter;
};
InternalUnitTester();
virtual~ InternalUnitTester();
@ -22,7 +27,7 @@ public:
* Some function which calls all other tests
* @return
*/
virtual ReturnValue_t performTests();
virtual ReturnValue_t performTests(struct InternalUnitTester::TestConfig& testConfig);
};

View File

@ -5,7 +5,7 @@
sif::error << "Unit Tester error: Failed at test ID "
<< errorId << std::endl;
#else
sif::printError("Unit Tester error: Failed at test ID 0x%08x", errorId);
sif::printError("Unit Tester error: Failed at test ID %s\n", errorId.c_str());
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -0,0 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE
TestArrayPrinter.cpp
)

View File

@ -0,0 +1,29 @@
#include "TestArrayPrinter.h"
void arrayprinter::testArrayPrinter() {
{
const std::array<uint8_t, 5> testDataSmall = {0x01, 0x02, 0x03, 0x04, 0x05};
arrayprinter::print(testDataSmall.data(), testDataSmall.size());
arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::DEC);
arrayprinter::print(testDataSmall.data(), testDataSmall.size(), OutputType::BIN);
}
{
std::array<uint8_t, 16> testDataMed;
for(size_t idx = 0; idx < testDataMed.size(); idx ++) {
testDataMed[idx] = testDataMed.size() - idx;
}
arrayprinter::print(testDataMed.data(), testDataMed.size());
arrayprinter::print(testDataMed.data(), testDataMed.size(), OutputType::DEC, 8);
}
{
std::array<uint8_t, 32> testDataLarge;
for(size_t idx = 0; idx < testDataLarge.size(); idx ++) {
testDataLarge[idx] = idx;
}
arrayprinter::print(testDataLarge.data(), testDataLarge.size());
arrayprinter::print(testDataLarge.data(), testDataLarge.size(), OutputType::DEC);
}
}

View File

@ -0,0 +1,13 @@
#ifndef FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_
#define FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_
#include <fsfw/globalfunctions/arrayprinter.h>
#include <array>
namespace arrayprinter {
void testArrayPrinter();
}
#endif /* FSFW_UNITTEST_INTERNAL_GLOBALFUNCTIONS_TESTARRAYPRINTER_H_ */

View File

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

View File

@ -3,8 +3,8 @@
#include <fsfw/ipc/MutexFactory.h>
#include <fsfw/unittest/internal/UnittDefinitions.h>
#if defined(hosted)
#include <fsfw/osal/hosted/Mutex.h>
#if defined(WIN32) || defined(UNIX)
#include <fsfw/osal/host/Mutex.h>
#include <thread>
#include <future>
#endif
@ -19,10 +19,11 @@ void testmutex::testMutex() {
}
// timed_mutex from the C++ library specifies undefined behaviour if
// the timed mutex is locked twice from the same thread.
#if defined(hosted)
// TODO: we should pass a define like FSFW_OSAL_HOST to the build.
#if defined(WIN32) || defined(UNIX)
// This calls the function from
// another thread and stores the returnvalue in a future.
auto future = std::async(&MutexIF::lockMutex, mutex, 1);
auto future = std::async(&MutexIF::lockMutex, mutex, MutexIF::TimeoutType::WAITING, 1);
result = future.get();
#else
result = mutex->lockMutex(MutexIF::TimeoutType::WAITING, 1);
@ -35,8 +36,12 @@ void testmutex::testMutex() {
if(result != HasReturnvaluesIF::RETURN_OK) {
unitt::put_error(id);
}
// TODO: we should pass a define like FSFW_OSAL_HOST to the build.
#if !defined(WIN32) && !defined(UNIX)
result = mutex->unlockMutex();
if(result != MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX) {
unitt::put_error(id);
}
#endif
}

View File

@ -1,10 +1,16 @@
#include "LocalPoolOwnerBase.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>
#include <fsfw/datapool/PoolReadHelper.h>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/housekeeping/HousekeepingSnapshot.h>
#include <fsfw/ipc/CommandMessageCleaner.h>
#include <fsfw/timemanager/CCSDSTime.h>
#include <unittest/core/CatchDefinitions.h>
#include <iostream>
TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
@ -14,7 +20,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK);
REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK);
//REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK);
MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle();
REQUIRE(mqMock != nullptr);
CommandMessage messageSent;
@ -22,11 +28,11 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
SECTION("BasicTest") {
// Subscribe for message generation on update.
/* Subscribe for message generation on update. */
REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK);
// Subscribe for an update message.
/* Subscribe for an update message. */
poolOwner->dataset.setChanged(true);
// Now the update message should be generated.
/* Now the update message should be generated. */
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(mqMock->wasMessageSent() == true);
@ -34,9 +40,9 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
// Should have been reset.
/* Should have been reset. */
CHECK(poolOwner->dataset.hasChanged() == false);
// Set changed again, result should be the same.
/* Set changed again, result should be the same. */
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
@ -46,14 +52,14 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
// now subscribe for set update HK as well.
/* Now subscribe for set update HK as well. */
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 2);
// first message sent should be the update notification, considering
// the internal list is a vector checked in insertion order.
/* first message sent should be the update notification, considering
the internal list is a vector checked in insertion order. */
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
@ -61,16 +67,82 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::HK_REPORT));
// clear message to avoid memory leak, our mock won't do it for us (yet)
/* Clear message to avoid memory leak, our mock won't do it for us (yet) */
CommandMessageCleaner::clearCommandMessage(&messageSent);
}
SECTION("SnapshotUpdateTests") {
/* Set the variables in the set to certain values. These are checked later. */
{
PoolReadHelper readHelper(&poolOwner->dataset);
REQUIRE(readHelper.getReadResult() == retval::CATCH_OK);
poolOwner->dataset.localPoolVarUint8.value = 5;
poolOwner->dataset.localPoolVarFloat.value = -12.242;
poolOwner->dataset.localPoolUint16Vec.value[0] = 2;
poolOwner->dataset.localPoolUint16Vec.value[1] = 32;
poolOwner->dataset.localPoolUint16Vec.value[2] = 42932;
}
/* Subscribe for snapshot generation on update. */
REQUIRE(poolOwner->subscribeWrapperSetUpdateSnapshot() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
/* Store current time, we are going to check the (approximate) time equality later */
CCSDSTime::CDS_short timeCdsNow;
timeval now;
Clock::getClock_timeval(&now);
CCSDSTime::convertToCcsds(&timeCdsNow, &now);
/* Trigger generation of snapshot */
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
/* Check that snapshot was generated */
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_SNAPSHOT_SET));
/* Now we deserialize the snapshot into a new dataset instance */
CCSDSTime::CDS_short cdsShort;
LocalPoolTestDataSet newSet;
HousekeepingSnapshot snapshot(&cdsShort, &newSet);
store_address_t storeId;
HousekeepingMessage::getUpdateSnapshotSetCommand(&messageSent, &storeId);
ConstAccessorPair accessorPair = tglob::getIpcStoreHandle()->getData(storeId);
REQUIRE(accessorPair.first == retval::CATCH_OK);
const uint8_t* readOnlyPtr = accessorPair.second.data();
size_t sizeToDeserialize = accessorPair.second.size();
CHECK(newSet.localPoolVarFloat.value == 0);
CHECK(newSet.localPoolVarUint8 == 0);
CHECK(newSet.localPoolUint16Vec.value[0] == 0);
CHECK(newSet.localPoolUint16Vec.value[1] == 0);
CHECK(newSet.localPoolUint16Vec.value[2] == 0);
/* Fill the dataset and timestamp */
REQUIRE(snapshot.deSerialize(&readOnlyPtr, &sizeToDeserialize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
/* Now we check that the snapshot is actually correct */
CHECK(newSet.localPoolVarFloat.value == Catch::Approx(-12.242));
CHECK(newSet.localPoolVarUint8 == 5);
CHECK(newSet.localPoolUint16Vec.value[0] == 2);
CHECK(newSet.localPoolUint16Vec.value[1] == 32);
CHECK(newSet.localPoolUint16Vec.value[2] == 42932);
/* Now we check that both times are equal */
CHECK(cdsShort.pField == timeCdsNow.pField);
CHECK(cdsShort.dayLSB == Catch::Approx(timeCdsNow.dayLSB).margin(1));
CHECK(cdsShort.dayMSB == Catch::Approx(timeCdsNow.dayMSB).margin(1));
CHECK(cdsShort.msDay_h == Catch::Approx(timeCdsNow.msDay_h).margin(1));
CHECK(cdsShort.msDay_hh == Catch::Approx(timeCdsNow.msDay_hh).margin(1));
CHECK(cdsShort.msDay_l == Catch::Approx(timeCdsNow.msDay_l).margin(1));
CHECK(cdsShort.msDay_ll == Catch::Approx(timeCdsNow.msDay_ll).margin(1));
}
SECTION("AdvancedTests") {
// we need to reset the subscription list because the pool owner
// is a global object.
poolOwner->resetSubscriptionList();
// Subscribe for variable update as well
REQUIRE(not poolOwner->dataset.hasChanged());
/* Acquire subscription interface */
ProvidesDataPoolSubscriptionIF* subscriptionIF = poolOwner->getSubscriptionInterface();
REQUIRE(subscriptionIF != nullptr);
/* Subscribe for variable update */
REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) ==
retval::CATCH_OK);
lp_var_t<uint8_t>* poolVar = dynamic_cast<lp_var_t<uint8_t>*>(
@ -79,22 +151,22 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
poolVar->setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// Check update notification was sent.
/* Check update notification was sent. */
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
// Should have been reset.
/* Should have been reset. */
CHECK(poolVar->hasChanged() == false);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
// now subscribe for the dataset update (HK and update) again
REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK);
/* Now subscribe for the dataset update (HK and update) again with subscription interface */
REQUIRE(subscriptionIF->subscribeForSetUpdateMessages(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false) == retval::CATCH_OK);
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// now two messages should be sent.
/* Now two messages should be sent. */
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 2);
mqMock->clearMessages(true);
@ -102,7 +174,7 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
poolOwner->dataset.setChanged(true);
poolVar->setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// now three messages should be sent.
/* Now three messages should be sent. */
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 3);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
@ -118,5 +190,10 @@ TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
REQUIRE(mqMock->receiveMessage(&messageSent) ==
static_cast<int>(MessageQueueIF::EMPTY));
}
/* we need to reset the subscription list because the pool owner
is a global object. */
poolOwner->resetSubscriptionList();
mqMock->clearMessages(true);
}

View File

@ -20,33 +20,43 @@ static constexpr lp_id_t int64Vec2Id = 4;
static constexpr uint32_t testSetId = 0;
static constexpr uint8_t dataSetMaxVariables = 10;
static const sid_t testSid = sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE,
testSetId);
static const sid_t testSid = sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, testSetId);
static const gp_id_t uint8VarGpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint8VarId);
static const gp_id_t floatVarGpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, floatVarId);
static const gp_id_t uint32Gpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint32VarId);
static const gp_id_t uint16Vec3Gpid = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, uint16Vec3Id);
static const gp_id_t uint64Vec2Id = gp_id_t(objects::TEST_LOCAL_POOL_OWNER_BASE, int64Vec2Id);
}
class LocalPoolTestDataSet: public LocalDataSet {
public:
LocalPoolTestDataSet():
LocalDataSet(lpool::testSid, lpool::dataSetMaxVariables) {
}
LocalPoolTestDataSet(HasLocalDataPoolIF* owner, uint32_t setId):
LocalDataSet(owner, setId, lpool::dataSetMaxVariables) {
}
ReturnValue_t assignPointers() {
PoolVariableIF** rawVarArray = getContainer();
localPoolVarUint8 = dynamic_cast<lp_var_t<uint8_t>*>(rawVarArray[0]);
localPoolVarFloat = dynamic_cast<lp_var_t<float>*>(rawVarArray[1]);
localPoolUint16Vec = dynamic_cast<lp_vec_t<uint16_t, 3>*>(
rawVarArray[2]);
if(localPoolVarUint8 == nullptr or localPoolVarFloat == nullptr or
localPoolUint16Vec == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
}
// ReturnValue_t assignPointers() {
// PoolVariableIF** rawVarArray = getContainer();
// localPoolVarUint8 = dynamic_cast<lp_var_t<uint8_t>*>(rawVarArray[0]);
// localPoolVarFloat = dynamic_cast<lp_var_t<float>*>(rawVarArray[1]);
// localPoolUint16Vec = dynamic_cast<lp_vec_t<uint16_t, 3>*>(
// rawVarArray[2]);
// if(localPoolVarUint8 == nullptr or localPoolVarFloat == nullptr or
// localPoolUint16Vec == nullptr) {
// return HasReturnvaluesIF::RETURN_FAILED;
// }
// return HasReturnvaluesIF::RETURN_OK;
// }
lp_var_t<uint8_t>* localPoolVarUint8 = nullptr;
lp_var_t<float>* localPoolVarFloat = nullptr;
lp_vec_t<uint16_t, 3>* localPoolUint16Vec = nullptr;
lp_var_t<uint8_t> localPoolVarUint8 = lp_var_t<uint8_t>(lpool::uint8VarGpid, this);
lp_var_t<float> localPoolVarFloat = lp_var_t<float>(lpool::floatVarGpid, this);
lp_vec_t<uint16_t, 3> localPoolUint16Vec = lp_vec_t<uint16_t, 3>(lpool::uint16Vec3Gpid, this);
private:
};
@ -155,7 +165,12 @@ public:
ReturnValue_t subscribeWrapperSetUpdate() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId,
objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false);
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, false);
}
ReturnValue_t subscribeWrapperSetUpdateSnapshot() {
return poolManager.subscribeForSetUpdateMessages(lpool::testSetId,
objects::NO_OBJECT, objects::HK_RECEIVER_MOCK, true);
}
ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) {
@ -165,7 +180,7 @@ public:
ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) {
return poolManager.subscribeForVariableUpdateMessages(localPoolId,
MessageQueueIF::NO_QUEUE, objects::NO_OBJECT, false);
MessageQueueIF::NO_QUEUE, objects::HK_RECEIVER_MOCK, false);
}
void resetSubscriptionList() {

View File

@ -14,7 +14,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
== retval::CATCH_OK);
SECTION("Basic Tests") {
// very basic test.
/* very basic test. */
lp_var_t<uint8_t> testVariable = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId);
REQUIRE(testVariable.read() == retval::CATCH_OK);
@ -77,7 +77,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
SECTION("ErrorHandling") {
// not try to use a local pool variable which does not exist
/* now try to use a local pool variable which does not exist */
lp_var_t<uint8_t> invalidVariable = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
REQUIRE(invalidVariable.read() ==
@ -85,7 +85,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
REQUIRE(invalidVariable.commit() ==
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
// now try to access with wrong type
/* now try to access with wrong type */
lp_var_t<int8_t> invalidVariable2 = lp_var_t<int8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId);
REQUIRE(invalidVariable2.read() ==
@ -108,7 +108,7 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
sif::info << "LocalPoolVariable printout: " <<uint32tVar << std::endl;
#endif
// for code coverage. If program does not crash -> OK
/* for code coverage. If program does not crash -> OK */
lp_var_t<uint8_t> invalidObjectVar = lp_var_t<uint8_t>(
0xffffffff, lpool::uint8VarId);
gp_id_t globPoolId(0xffffffff,

View File

@ -35,13 +35,13 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
CHECK(testVector[0] == 5);
// This is invalid access, so the last value will be set instead.
// (we can't throw exceptions)
/* This is invalid access, so the last value will be set instead.
(we can't throw exceptions) */
testVector[4] = 12;
CHECK(testVector[2] == 12);
CHECK(testVector.commit() == retval::CATCH_OK);
// Use read-only reference.
/* Use read-only reference. */
const lp_vec_t<uint16_t, 3>& roTestVec = testVector;
uint16_t valueOne = roTestVec[0];
CHECK(valueOne == 5);
@ -87,7 +87,7 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
}
SECTION("ErrorHandling") {
// not try to use a local pool variable which does not exist
/* Now try to use a local pool variable which does not exist */
lp_vec_t<uint16_t, 3> invalidVector = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
REQUIRE(invalidVector.read() ==
@ -95,7 +95,7 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
REQUIRE(invalidVector.commit() ==
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
// now try to access with wrong type
/* Now try to access with wrong type */
lp_vec_t<uint32_t, 3> invalidVector2 = lp_vec_t<uint32_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
REQUIRE(invalidVector2.read() ==

View File

@ -0,0 +1,3 @@
target_sources(${TARGET_NAME} PRIVATE
TestArrayPrinter.cpp
)

View File

@ -276,7 +276,7 @@ TEST_CASE( "Local Pool Extended Tests [3 Pools]" , "[TestPool2]") {
CHECK(receptionArray[3] == 66);
// now clear first page
simplePool.clearPage(0);
simplePool.clearSubPool(0);
bytesWritten = 0;
simplePool.getFillCount(receptionArray.data(), &bytesWritten);
// Second page full, median fill count is 33 %