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

This commit is contained in:
Robin Müller 2021-01-14 11:25:14 +01:00
commit f05af8f54a
89 changed files with 3119 additions and 1989 deletions

View File

@ -106,15 +106,17 @@ else()
)
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
set(WARNING_FLAGS
-Wall
-Wextra
-Wshadow=local
-Wimplicit-fallthrough=1
-Wno-unused-parameter
-Wno-psabi
)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
if(NOT DEFINED FSFW_WARNING_FLAGS)
set(FSFW_WARNING_FLAGS
-Wall
-Wextra
-Wshadow=local
-Wimplicit-fallthrough=1
-Wno-unused-parameter
-Wno-psabi
)
endif()
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
@ -135,8 +137,7 @@ target_include_directories(${LIB_FSFW_NAME} PRIVATE
${FSFW_CONFIG_PATH_ABSOLUTE}
)
# Machine specific options can be set with the ABI_FLAGS variable.
target_compile_options(${LIB_FSFW_NAME} PRIVATE
${WARNING_FLAGS}
${FSFW_WARNING_FLAGS}
${COMPILER_FLAGS}
)

133
README.md
View File

@ -9,121 +9,40 @@ The initial version of the Flight Software Framework was developed during
the Flying Laptop Project by the University of Stuttgart in cooperation
with Airbus Defence and Space GmbH.
## Intended Use
## Quick facts
The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability.
Therefore, a mode and health system provides control over the states of the software and the controlled devices.
In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well.
The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well.
The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory.
For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC.
The `fsfw` was also tested on the STM32H743ZI-Nucleo board.
The FSFW provides abstraction layers for operating systems to provide a uniform operating system abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is also very useful for developers to implement the same application logic on different operating systems with a uniform interface.
## How to Use
Currently, the FSFW provides the following OSALs:
- Linux
- Host
- FreeRTOS
- RTEMS
The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active satellite mission Flying Laptop.
## Getting started
The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo to see the FSFW capabilities and build it with the Make or the CMake build system. It is recommended to evaluate the FSFW by building and playing around with the demo application.
The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo
to see the FSFW capabilities and build it with the Make or the CMake build system.
Generally, the FSFW is included in a project by compiling the FSFW sources and providing
a configuration folder and adding it to the include path.
a configuration folder and adding it to the include path. There are some functions like `printChar` which are different depending on the target architecture and need to be implemented by the mission developer.
A template configuration folder was provided and can be copied into the project root to have
a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about
the possible options.
a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about the possible options.
## Structure
## Index
The general structure is driven by the usage of interfaces provided by objects.
The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers.
The FSFW uses dynamic allocation during the initialization but provides static containers during runtime.
This simplifies the instantiation of objects and allows the usage of some standard containers.
Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that.
The fsfw uses run-time type information but exceptions are not allowed.
[1. High-level overview](doc/README-highlevel.md#top) <br>
[2. Core components](doc/README-core.md#top) <br>
[3. OSAL overview](doc/README-osal.md#top) <br>
[4. PUS services](doc/README-pus.md#top) <br>
[5. Device Handler overview](doc/README-devicehandlers.md#top) <br>
[6. Controller overview](doc/README-controllers.md#top) <br>
[7. Local Data Pools](doc/README-localpools.md#top) <br>
### Failure Handling
Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong.
Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used.
The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds.
### OSAL
The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS.
A independent Host OSAL is in development which will provide abstraction for common type of
host OSes (tested for Linux and Windows, not for MacOS yet).
The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes.
### Core Components
The FSFW has following core components. More detailed informations can be found in the
[core component section](doc/README-core.md#top):
1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots
2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles.
3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without
using dynamic memory allocation. These pools are allocated up front.
3. Clock: This module provided common time related functions
4. EventManager: This module allows routing of events generated by `SystemObjects`
5. HealthTable: A component which stores the health states of objects
### Static Ids in the framework
Some parts of the framework use a static routing address for communication.
An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()".
### Events
Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues.
Every object that needs own EventIds has to get a unique SUBSYSTEM_ID.
Every SystemObject can call triggerEvent from the parent class.
Therefore, event messages contain the specific EventId and the objectId of the object that has triggered.
### Internal Communication
Components communicate mostly over Message through Queues.
Those queues are created by calling the singleton QueueFactory::instance()->create().
### External Communication
The external communication with the mission control system is mostly up to the user implementation.
The FSFW provides PUS Services which can be used to but don't need to be used.
The services can be seen as a conversion from a TC to a message based communication and back.
#### CCSDS Frames, CCSDS Space Packets and PUS
If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution.
If Space Packets are used, a timestamper must be created.
An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short.
#### Device Handlers
DeviceHandlers are another important component of the FSFW.
The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface.
By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware.
The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction.
Device Handlers can be created by overriding `DeviceHandlerBase`.
A standard FDIR component for the DH will be created automatically but can be overwritten by the user.
More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top).
#### Modes, Health
The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components.
On-board Mode Management is implement in hierarchy system.
DeviceHandlers and Controllers are the lowest part of the hierarchy.
The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers.
Assemblies share a common core with the next level which are the Subsystems.
Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes.
The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded.
Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component.
The target table is used to describe the state that is checked continuously by the subsystem.
All of this allows System Modes to be generated as Subsystem object as well from the same database.
This System contains list of subsystem modes in the transition and target tables.
Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded.
The health state represents if the component is able to perform its tasks.
This can be used to signal the system to avoid using this component instead of a redundant one.
The on-board FDIR uses the health state for isolation and recovery.
## Unit Tests
Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself.
See README.md in the unittest Folder.

View File

@ -4,7 +4,7 @@
ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId,
object_id_t parentId, size_t commandQueueDepth):
ControllerBase(objectId, parentId, commandQueueDepth),
localPoolManager(this, commandQueue),
poolManager(this, commandQueue),
actionHelper(this, commandQueue) {
}
@ -17,7 +17,7 @@ ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
ReturnValue_t ExtendedControllerBase::initializeLocalDataPool(
LocalDataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
// needs to be overriden and implemented by child class.
return HasReturnvaluesIF::RETURN_OK;
}
@ -26,8 +26,8 @@ object_id_t ExtendedControllerBase::getObjectId() const {
return SystemObject::getObjectId();
}
LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() {
return &localPoolManager;
AccessPoolManagerIF* ExtendedControllerBase::getAccessorHandle() {
return &poolManager;
}
uint32_t ExtendedControllerBase::getPeriodicOperationFrequency() const {
@ -40,7 +40,7 @@ ReturnValue_t ExtendedControllerBase::handleCommandMessage(
if(result == HasReturnvaluesIF::RETURN_OK) {
return result;
}
return localPoolManager.handleHousekeepingMessage(message);
return poolManager.handleHousekeepingMessage(message);
}
void ExtendedControllerBase::handleQueue() {
@ -64,7 +64,7 @@ void ExtendedControllerBase::handleQueue() {
continue;
}
result = localPoolManager.handleHousekeepingMessage(&command);
result = poolManager.handleHousekeepingMessage(&command);
if (result == RETURN_OK) {
continue;
}
@ -88,16 +88,16 @@ ReturnValue_t ExtendedControllerBase::initialize() {
return result;
}
return localPoolManager.initialize(commandQueue);
return poolManager.initialize(commandQueue);
}
ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() {
return localPoolManager.initializeAfterTaskCreation();
return poolManager.initializeAfterTaskCreation();
}
ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) {
handleQueue();
localPoolManager.performHkOperation();
poolManager.performHkOperation();
performControlOperation();
return RETURN_OK;
}
@ -113,3 +113,7 @@ LocalPoolDataSetBase* ExtendedControllerBase::getDataSetHandle(sid_t sid) {
#endif
return nullptr;
}
ProvidesDataPoolSubscriptionIF* ExtendedControllerBase::getSubscriptionInterface() {
return &poolManager;
}

View File

@ -32,10 +32,20 @@ public:
virtual ReturnValue_t performOperation(uint8_t opCode) override;
virtual ReturnValue_t initializeAfterTaskCreation() override;
/**
* Provides a subscription interface for objects which required updates on changed
* controller variables or datasets
* @return
*/
ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() override;
protected:
LocalDataPoolManager localPoolManager;
LocalDataPoolManager poolManager;
ActionHelper actionHelper;
//! Accessor handle required for internal handling
AccessPoolManagerIF* getAccessorHandle() override;
/**
* Implemented by child class. Handle all command messages which are
* not health, mode, action or housekeeping messages.
@ -60,9 +70,8 @@ protected:
/** HasLocalDatapoolIF overrides */
virtual object_id_t getObjectId() const override;
virtual ReturnValue_t initializeLocalDataPool(
LocalDataPool& localDataPoolMap,
localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
virtual LocalDataPoolManager* getHkManagerHandle() override;
virtual uint32_t getPeriodicOperationFrequency() const override;
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
};

View File

@ -88,7 +88,8 @@ ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) {
{
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->read(
MutexIF::TimeoutType::WAITING, mutexTimeout);
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
result = registeredVariables[count]->readWithoutLock();
@ -122,7 +123,8 @@ void PoolDataSetBase::handleAlreadyReadDatasetCommit(
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
registeredVariables[count]->commit(
MutexIF::TimeoutType::WAITING, mutexTimeout);
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
registeredVariables[count]->commitWithoutLock();
@ -144,7 +146,8 @@ ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit(
!= PoolVariableIF::NO_PARAMETER) {
if(protectEveryReadCommitCall) {
result = registeredVariables[count]->commit(
MutexIF::TimeoutType::WAITING, mutexTimeout);
timeoutTypeForSingleVars,
mutexTimeoutForSingleVars);
}
else {
result = registeredVariables[count]->commitWithoutLock();
@ -214,8 +217,14 @@ void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) {
this->registeredVariables = variablesContainer;
}
void PoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, uint32_t mutexTimeout) {
this->protectEveryReadCommitCall = protectEveryReadCommit;
this->mutexTimeout = mutexTimeout;
PoolVariableIF** PoolDataSetBase::getContainer() const {
return registeredVariables;
}
void PoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType,
uint32_t mutexTimeout) {
this->protectEveryReadCommitCall = protectEveryReadCommit;
this->timeoutTypeForSingleVars = timeoutType;
this->mutexTimeoutForSingleVars = mutexTimeout;
}

View File

@ -3,6 +3,7 @@
#include "PoolDataSetIF.h"
#include "PoolVariableIF.h"
#include "../serialize/SerializeIF.h"
#include "../ipc/MutexIF.h"
/**
@ -119,16 +120,16 @@ public:
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* Can be used to individually protect every read and commit call.
* @param protectEveryReadCommit
* @param mutexTimeout
*/
void setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20);
protected:
/**
* Can be used to individually protect every read and commit call.
* @param protectEveryReadCommit
* @param mutexTimeout
*/
void setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
uint32_t mutexTimeout = 20);
/**
* @brief The fill_count attribute ensures that the variables
* register in the correct array position and that the maximum
@ -157,10 +158,12 @@ protected:
const size_t maxFillCount = 0;
void setContainer(PoolVariableIF** variablesContainer);
PoolVariableIF** getContainer() const;
private:
bool protectEveryReadCommitCall = false;
uint32_t mutexTimeout = 20;
MutexIF::TimeoutType timeoutTypeForSingleVars;
uint32_t mutexTimeoutForSingleVars = 20;
ReturnValue_t readVariable(uint16_t count);
void handleAlreadyReadDatasetCommit(

View File

@ -1,32 +1,24 @@
#include "PoolEntry.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../globalfunctions/arrayprinter.h"
#include <cstring>
#include <algorithm>
template <typename T>
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, uint8_t setLength,
bool setValid ) : length(setLength), valid(setValid) {
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, bool setValid ):
length(initValue.size()), valid(setValid) {
this->address = new T[this->length];
if(initValue.size() == 0) {
std::memset(this->address, 0, this->getByteSize());
}
else if (initValue.size() != setLength){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "PoolEntry: setLength is not equal to initializer list"
"length! Performing zero initialization with given setLength"
<< std::endl;
#endif
std::memset(this->address, 0, this->getByteSize());
}
else {
std::copy(initValue.begin(), initValue.end(), this->address);
}
}
template <typename T>
PoolEntry<T>::PoolEntry( T* initValue, uint8_t setLength, bool setValid ) :
PoolEntry<T>::PoolEntry(T* initValue, uint8_t setLength, bool setValid):
length(setLength), valid(setValid) {
this->address = new T[this->length];
if (initValue != nullptr) {
@ -70,14 +62,26 @@ bool PoolEntry<T>::getValid() {
template <typename T>
void PoolEntry<T>::print() {
const char* validString = nullptr;
if(valid) {
validString = "Valid";
}
else {
validString = "Invalid";
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "Pool Entry Validity: " <<
(this->valid? " (valid) " : " (invalid) ") << std::endl;
#endif
arrayprinter::print(reinterpret_cast<uint8_t*>(address), length);
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << std::dec << std::endl;
sif::info << "PoolEntry information." << std::endl;
sif::info << "PoolEntry validity: " << validString << std::endl;
#else
sif::printInfo("PoolEntry information.\n");
sif::printInfo("PoolEntry validity: %s\n", validString);
#endif
arrayprinter::print(reinterpret_cast<uint8_t*>(address), getByteSize());
}
template<typename T>
inline T* PoolEntry<T>::getDataPtr() {
return this->address;
}
template<typename T>

View File

@ -35,24 +35,22 @@ public:
"uint8_t");
/**
* @brief In the classe's constructor, space is allocated on the heap and
* potential init values are copied to that space.
* potential initialization values are copied to that space.
* @details
* Not passing any arguments will initialize an non-array pool entry
* (setLength = 1) with an initial invalid state.
* Please note that if an initializer list is passed, the correct
* corresponding length should be passed too, otherwise a zero
* initialization will be performed with the given setLength.
* with an initial invalid state and the value 0.
* Please note that if an initializer list is passed, the length of the
* initializer list needs to be correct for vector entries because
* required allocated space will be deduced from the initializer list length
* and the pool entry type.
* @param initValue
* Initializer list with values to initialize with, for example {0,0} to
* initialize the two entries to zero.
* @param setLength
* Defines the array length of this entry. Should be equal to the
* intializer list length.
* Initializer list with values to initialize with, for example {0, 0} to
* initialize the a pool entry of a vector with two entries to 0.
* @param setValid
* Sets the initialization flag. It is invalid by default.
*/
PoolEntry(std::initializer_list<T> initValue = {}, uint8_t setLength = 1,
bool setValid = false);
PoolEntry(std::initializer_list<T> initValue = {0}, bool setValid = false);
/**
* @brief In the classe's constructor, space is allocated on the heap and
* potential init values are copied to that space.
@ -66,9 +64,9 @@ public:
*/
PoolEntry(T* initValue, uint8_t setLength = 1, bool setValid = false);
//! Explicitely deleted copy ctor, copying is not allowed!
//! Explicitely deleted copy ctor, copying is not allowed.
PoolEntry(const PoolEntry&) = delete;
//! Explicitely deleted copy assignment, copying is not allowed!
//! Explicitely deleted copy assignment, copying is not allowed.
PoolEntry& operator=(const PoolEntry&) = delete;
/**
@ -82,21 +80,16 @@ public:
~PoolEntry();
/**
* @brief This is the address pointing to the allocated memory.
* Return typed pointer to start of data.
* @return
*/
T* address;
/**
* @brief This attribute stores the length information.
*/
uint8_t length;
/**
* @brief Here, the validity information for a variable is stored.
* Every entry (single variable or vector) has one valid flag.
*/
uint8_t valid;
T* getDataPtr();
/**
* @brief getSize returns the array size of the entry.
* @details A single parameter has size 1.
* @details
* For non-array pool entries return type size, for vector entries
* return type size times the number of entries.
*/
uint8_t getSize();
/**
@ -123,8 +116,22 @@ public:
* information to the screen. It prints all array entries in a row.
*/
void print();
Type getType();
private:
/**
* @brief This attribute stores the length information.
*/
uint8_t length;
/**
* @brief Here, the validity information for a variable is stored.
* Every entry (single variable or vector) has one valid flag.
*/
uint8_t valid;
/**
* @brief This is the address pointing to the allocated memory.
*/
T* address;
};
#endif /* FSFW_DATAPOOL_POOLENTRY_H_ */

View File

@ -1,8 +1,8 @@
#ifndef FSFW_DATAPOOL_READCOMMITIF_H_
#define FSFW_DATAPOOL_READCOMMITIF_H_
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
#include <fsfw/ipc/MutexIF.h>
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../ipc/MutexIF.h"
/**
* @brief Common interface for all software objects which employ read-commit

View File

@ -0,0 +1,27 @@
#ifndef FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
#define FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_
class LocalDataPoolManager;
class MutexIF;
/**
* @brief Accessor class which can be used by classes which like to use the pool manager.
*/
class AccessPoolManagerIF {
public:
virtual ~AccessPoolManagerIF() {};
virtual MutexIF* getLocalPoolMutex() = 0;
/**
* Can be used to get a handle to the local data pool manager.
* This function is protected because it should only be used by the
* class imlementing the interface.
*/
virtual LocalDataPoolManager* getHkManagerHandle() = 0;
protected:
};
#endif /* FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ */

View File

@ -5,4 +5,6 @@ target_sources(${LIB_FSFW_NAME}
LocalPoolDataSetBase.cpp
LocalPoolObjectBase.cpp
SharedLocalDataSet.cpp
)
)
add_subdirectory(internal)

View File

@ -1,105 +1,66 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_
#include "locPoolDefinitions.h"
#include "localPoolDefinitions.h"
#include "../datapool/PoolEntryIF.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../ipc/MessageQueueSenderIF.h"
#include "../housekeeping/HousekeepingMessage.h"
#include <map>
class LocalDataPoolManager;
class AccessPoolManagerIF;
class ProvidesDataPoolSubscriptionIF;
class LocalPoolDataSetBase;
class LocalPoolObjectBase;
using LocalDataPool = std::map<lp_id_t, PoolEntryIF*>;
using LocalDataPoolMapIter = LocalDataPool::iterator;
class LocalDataPoolManager;
/**
* @brief This interface is implemented by classes which posses a local
* data pool (not the managing class). It defines the relationship
* between the local data pool owner and the LocalDataPoolManager.
* @brief This interface is implemented by classes which posses a local data pool (not the
* managing class). It defines the relationship between the local data pool owner
* and the LocalDataPoolManager.
* @details
* Any class implementing this interface shall also have a LocalDataPoolManager
* member class which contains the actual pool data structure
* and exposes the public interface for it.
* This is required because the pool entries are templates, which makes
* specifying an interface rather difficult. The local data pool can be
* accessed by using the LocalPoolVariable, LocalPoolVector or LocalDataSet
* classes.
* Any class implementing this interface shall also have a LocalDataPoolManager member class which
* contains the actual pool data structure and exposes the public interface for it.
* The local data pool can be accessed using helper classes by using the
* LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can
* be uniquely identified by a global pool ID (gp_id_t) and every dataset tied
* to a pool manager can be uniqely identified by a global structure ID (sid_t).
*
* Architectural Note:
* This could be circumvented by using a wrapper/accessor function or
* implementing the templated function in this interface..
* The first solution sounds better than the second but
* the LocalPoolVariable classes are templates as well, so this just shifts
* the problem somewhere else. Interfaces are nice, but the most
* pragmatic solution I found was to offer the client the full interface
* of the LocalDataPoolManager.
*/
class HasLocalDataPoolIF {
friend class HasLocalDpIFManagerAttorney;
friend class HasLocalDpIFUserAttorney;
public:
virtual~ HasLocalDataPoolIF() {};
virtual~ HasLocalDataPoolIF() {};
static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
virtual object_id_t getObjectId() const = 0;
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
/** Command queue for housekeeping messages. */
virtual MessageQueueId_t getCommandQueue() const = 0;
virtual object_id_t getObjectId() const = 0;
/**
* Is used by pool owner to initialize the pool map once
* The manager instance shall also be passed to this function.
* It can be used to subscribe for periodic packets for for updates.
*/
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) = 0;
/** Command queue for housekeeping messages. */
virtual MessageQueueId_t getCommandQueue() const = 0;
/**
* Is used by pool owner to initialize the pool map once
* The manager instance shall also be passed to this function.
* It can be used to subscribe for periodic packets for for updates.
*/
virtual ReturnValue_t initializeLocalDataPool(
LocalDataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) = 0;
/** Can be used to get a handle to the local data pool manager. */
virtual LocalDataPoolManager* getHkManagerHandle() = 0;
/**
* Returns the minimum sampling frequency in milliseconds, which will
* usually be the period the pool owner performs its periodic operation.
* @return
*/
virtual uint32_t getPeriodicOperationFrequency() const = 0;
/**
* This function is used by the pool manager to get a valid dataset
* from a SID
* @param sid Corresponding structure ID
* @return
*/
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0;
/**
* Similar to the function above, but used to get a local pool variable
* handle. This is only needed for update notifications, so it is not
* defined as abstract.
* @param localPoolId
* @return
*/
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden"
<< ". Returning nullptr!" << std::endl;
#endif
return nullptr;
}
/**
* Returns the minimum sampling frequency in milliseconds, which will
* usually be the period the pool owner performs its periodic operation.
* @return
*/
virtual uint32_t getPeriodicOperationFrequency() const = 0;
/**
* @brief This function will be called by the manager if an update
* notification is received.
* @details
* @details HasLocalDataPoolIF
* Can be overriden by the child class to handle changed datasets.
* @param sid
* @param storeId If a snapshot was requested, data will be located inside
@ -124,18 +85,68 @@ public:
return;
}
/* These function can be implemented by pool owner, as they are required
* by the housekeeping message interface */
virtual ReturnValue_t addDataSet(sid_t sid) {
return HasReturnvaluesIF::RETURN_FAILED;
};
virtual ReturnValue_t removeDataSet(sid_t sid) {
return HasReturnvaluesIF::RETURN_FAILED;
};
virtual ReturnValue_t changeCollectionInterval(sid_t sid,
float newIntervalSeconds) {
return HasReturnvaluesIF::RETURN_FAILED;
};
/**
* These function can be implemented by pool owner, if they are required
* and used by the housekeeping message interface.
* */
virtual ReturnValue_t addDataSet(sid_t sid) {
return HasReturnvaluesIF::RETURN_FAILED;
};
virtual ReturnValue_t removeDataSet(sid_t sid) {
return HasReturnvaluesIF::RETURN_FAILED;
};
virtual ReturnValue_t changeCollectionInterval(sid_t sid, float newIntervalSeconds) {
return HasReturnvaluesIF::RETURN_FAILED;
};
/**
* This function can be used by data pool consumers to retrieve a handle
* which allows subscriptions to dataset and variable updates in form of messages.
* The consumers can then read the most recent variable value by calling read with
* an own pool variable or set instance or using the deserialized snapshot data.
* @return
*/
virtual ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() = 0;
protected:
/**
* Accessor handle required for internal handling. Not intended for users and therefore
* declared protected. Users should instead use pool variables, sets or the subscription
* interface to access pool entries.
* @return
*/
virtual AccessPoolManagerIF* getAccessorHandle() = 0;
/**
* This function is used by the pool manager to get a valid dataset
* from a SID. This function is protected to prevent users from
* using raw data set pointers which could not be thread-safe. Users
* should use the #ProvidesDataPoolSubscriptionIF.
* @param sid Corresponding structure ID
* @return
*/
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0;
/**
* Similar to the function above, but used to get a local pool variable
* handle. This is only needed for update notifications, so it is not
* defined as abstract. This function is protected to prevent users from
* using raw pool variable pointers which could not be thread-safe.
* Users should use the #ProvidesDataPoolSubscriptionIF.
* @param localPoolId
* @return
*/
virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden"
<< ". Returning nullptr!" << std::endl;
#else
sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: "
"Not overriden. Returning nullptr!\n");
#endif
return nullptr;
}
};
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,8 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
#define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_
#include "HasLocalDataPoolIF.h"
#include "ProvidesDataPoolSubscriptionIF.h"
#include "AccessLocalPoolF.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../housekeeping/HousekeepingPacketDownlink.h"
@ -16,6 +17,7 @@
#include "../ipc/MutexHelper.h"
#include <map>
#include <vector>
namespace Factory {
void setStaticFrameworkObjectIds();
@ -23,6 +25,8 @@ void setStaticFrameworkObjectIds();
class LocalPoolDataSetBase;
class HousekeepingPacketUpdate;
class HasLocalDataPoolIF;
class LocalDataPool;
/**
* @brief This class is the managing instance for the local data pool.
@ -48,19 +52,23 @@ class HousekeepingPacketUpdate;
* Each pool entry has a valid state too.
* @author R. Mueller
*/
class LocalDataPoolManager {
template<typename T> friend class LocalPoolVariable;
template<typename T, uint16_t vecSize> friend class LocalPoolVector;
friend class LocalPoolDataSetBase;
class LocalDataPoolManager: public ProvidesDataPoolSubscriptionIF,
public AccessPoolManagerIF {
friend void (Factory::setStaticFrameworkObjectIds)();
//! Some classes using the pool manager directly need to access class internals of the
//! manager. The attorney provides granular control of access to these internals.
friend class LocalDpManagerAttorney;
public:
static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER;
static constexpr ReturnValue_t QUEUE_OR_DESTINATION_NOT_SET = MAKE_RETURN_CODE(0x0);
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);
static constexpr ReturnValue_t POOLOBJECT_NOT_FOUND = MAKE_RETURN_CODE(4);
static constexpr ReturnValue_t DATASET_NOT_FOUND = MAKE_RETURN_CODE(5);
static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(0x01);
static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(0x02);
static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(0x03);
/**
* This constructor is used by a class which wants to implement
@ -117,7 +125,7 @@ public:
*/
ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting,
float collectionInterval, bool isDiagnostics,
object_id_t packetDestination = defaultHkDestination);
object_id_t packetDestination = defaultHkDestination) override;
/**
* @brief Subscribe for the generation of packets if the dataset
@ -131,7 +139,7 @@ public:
*/
ReturnValue_t subscribeForUpdatePackets(sid_t sid, bool reportingEnabled,
bool isDiagnostics,
object_id_t packetDestination = defaultHkDestination);
object_id_t packetDestination = defaultHkDestination) override;
/**
* @brief Subscribe for a notification message which will be sent
@ -150,7 +158,7 @@ public:
ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot);
bool generateSnapshot) override;
/**
* @brief Subscribe for an notification message which will be sent if a
@ -169,7 +177,9 @@ public:
ReturnValue_t subscribeForVariableUpdateMessages(const lp_id_t localPoolId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot);
bool generateSnapshot) override;
MutexIF* getLocalPoolMutex() override;
/**
* Non-Diagnostics packets usually have a lower minimum sampling frequency
@ -248,8 +258,18 @@ public:
LocalDataPoolManager(const LocalDataPoolManager &) = delete;
LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete;
/**
* This function can be used to clear the receivers list. This is
* intended for test functions and not for regular operations, because
* the insertion operations allocate dynamically.
*/
void clearReceiversList();
object_id_t getCreatorObjectId() const;
virtual LocalDataPoolManager* getHkManagerHandle() override;
private:
LocalDataPool localPoolMap;
localpool::DataPool localPoolMap;
//! Every housekeeping data manager has a mutex to protect access
//! to it's data pool.
MutexIF* mutex = nullptr;
@ -368,6 +388,11 @@ private:
ReturnValue_t& status);
ReturnValue_t addUpdateToStore(HousekeepingPacketUpdate& updatePacket,
store_address_t& storeId);
void printWarningOrError(sif::OutputTypes outputType,
const char* functionName,
ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED,
const char* errorPrint = nullptr);
};
@ -376,26 +401,16 @@ ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId,
PoolEntry<T> **poolEntry) {
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HousekeepingManager::fechPoolEntry: Pool entry "
"not found." << std::endl;
#else
fsfw::printWarning("HousekeepingManager::fechPoolEntry: Pool entry "
"not found.");
#endif
return HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND;
printWarningOrError(sif::OutputTypes::OUT_ERROR, "fetchPoolEntry",
localpool::POOL_ENTRY_NOT_FOUND);
return localpool::POOL_ENTRY_NOT_FOUND;
}
*poolEntry = dynamic_cast< PoolEntry<T>* >(poolIter->second);
if(*poolEntry == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "HousekeepingManager::fetchPoolEntry:"
" Pool entry type conflict." << std::endl;
#else
fsfw::printWarning("HousekeepingManager::fetchPoolEntry:"
" Pool entry type conflict.");
#endif
return HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT;
printWarningOrError(sif::OutputTypes::OUT_ERROR, "fetchPoolEntry",
localpool::POOL_ENTRY_TYPE_CONFLICT);
return localpool::POOL_ENTRY_TYPE_CONFLICT;
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -9,13 +9,13 @@ LocalDataSet::LocalDataSet(HasLocalDataPoolIF *hkOwner, uint32_t setId,
const size_t maxNumberOfVariables):
LocalPoolDataSetBase(hkOwner, setId, nullptr, maxNumberOfVariables),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
this->setContainer(poolVarList.data());
}
LocalDataSet::LocalDataSet(sid_t sid, const size_t maxNumberOfVariables):
LocalPoolDataSetBase(sid, nullptr, maxNumberOfVariables),
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
poolVarList(maxNumberOfVariables) {
this->setContainer(poolVarList.data());
}
LocalDataSet::~LocalDataSet() {}

View File

@ -7,7 +7,7 @@
class LocalDataSet: public LocalPoolDataSetBase {
public:
LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId,
const size_t maxSize);
const size_t maxSize);
LocalDataSet(sid_t sid, const size_t maxSize);
virtual~ LocalDataSet();

View File

@ -1,4 +1,6 @@
#include "LocalPoolDataSetBase.h"
#include "HasLocalDataPoolIF.h"
#include "internal/HasLocalDpIFUserAttorney.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../datapoollocal/LocalDataPoolManager.h"
@ -18,17 +20,21 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
<< "invalid!" << std::endl;
#else
fsfw::printError("LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
sif::printError("LocalPoolDataSetBase::LocalPoolDataSetBase: Owner "
"invalid!\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return;
}
hkManager = hkOwner->getHkManagerHandle();
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if(accessor != nullptr) {
poolManager = accessor->getHkManagerHandle();
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
}
this->sid.objectId = hkOwner->getObjectId();
this->sid.ownerSetId = setId;
mutex = MutexFactory::instance()->createMutex();
// Data creators get a periodic helper for periodic HK data generation.
if(periodicHandling) {
periodicHelper = new PeriodicHousekeepingHelper(this);
@ -38,39 +44,40 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid,
PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
sid.objectId);
if(hkOwner != nullptr) {
hkManager = hkOwner->getHkManagerHandle();
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if(accessor != nullptr) {
mutexIfSingleDataCreator = accessor->getLocalPoolMutex();
}
}
this->sid = sid;
mutex = MutexFactory::instance()->createMutex();
this->sid = sid;
}
LocalPoolDataSetBase::LocalPoolDataSetBase(
PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectFunctions):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
if(protectFunctions) {
mutex = MutexFactory::instance()->createMutex();
}
PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
}
LocalPoolDataSetBase::~LocalPoolDataSetBase() {
if(periodicHelper != nullptr) {
delete periodicHelper;
}
}
ReturnValue_t LocalPoolDataSetBase::lockDataPool(
MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
if(hkManager != nullptr) {
MutexIF* poolMutex = hkManager->getMutexHandle();
return poolMutex->lockMutex(timeoutType, timeoutMs);
}
return HasReturnvaluesIF::RETURN_OK;
MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
if(mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs);
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer,
@ -146,11 +153,10 @@ ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer(
}
ReturnValue_t LocalPoolDataSetBase::unlockDataPool() {
if(hkManager != nullptr) {
MutexIF* mutex = hkManager->getMutexHandle();
return mutex->unlockMutex();
}
return HasReturnvaluesIF::RETURN_OK;
if(mutexIfSingleDataCreator != nullptr) {
return mutexIfSingleDataCreator->unlockMutex();
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer,
@ -169,10 +175,10 @@ ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer,
if(result != HasReturnvaluesIF::RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: "
<< "Serialization error!" << std::endl;
<< "Serialization error!" << std::endl;
#else
fsfw::printWarning("LocalPoolDataSetBase::serializeLocalPoolIds: "
"Serialization error!\n\r");
sif::printWarning("LocalPoolDataSetBase::serializeLocalPoolIds: "
"Serialization error!\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return result;
}
@ -235,8 +241,8 @@ void LocalPoolDataSetBase::bitSetter(uint8_t* byte, uint8_t position) const {
sif::warning << "LocalPoolDataSetBase::bitSetter: Invalid position!"
<< std::endl;
#else
fsfw::printWarning("LocalPoolDataSetBase::bitSetter: "
"Invalid position!\n\r");
sif::printWarning("LocalPoolDataSetBase::bitSetter: "
"Invalid position!\n\r");
#endif
return;
}
@ -268,19 +274,10 @@ void LocalPoolDataSetBase::initializePeriodicHelper(
}
void LocalPoolDataSetBase::setChanged(bool changed) {
if(mutex == nullptr) {
this->changed = changed;
return;
}
MutexHelper(mutex, MutexIF::TimeoutType::WAITING, mutexTimeout);
this->changed = changed;
}
bool LocalPoolDataSetBase::hasChanged() const {
if(mutex == nullptr) {
return changed;
}
MutexHelper(mutex, MutexIF::TimeoutType::WAITING, mutexTimeout);
return changed;
}
@ -302,32 +299,21 @@ bool LocalPoolDataSetBase::bitGetter(const uint8_t* byte,
}
bool LocalPoolDataSetBase::isValid() const {
if(mutex == nullptr) {
return this->valid;
}
MutexHelper(mutex, MutexIF::TimeoutType::WAITING, 5);
return this->valid;
}
void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) {
mutex->lockMutex(timeoutType, mutexTimeout);
if(setEntriesRecursively) {
for(size_t idx = 0; idx < this->getFillCount(); idx++) {
registeredVariables[idx] -> setValid(valid);
}
}
this->valid = valid;
mutex->unlockMutex();
}
void LocalPoolDataSetBase::setReadCommitProtectionBehaviour(
bool protectEveryReadCommit, uint32_t mutexTimeout) {
PoolDataSetBase::setReadCommitProtectionBehaviour(protectEveryReadCommit,
mutexTimeout);
}
void LocalPoolDataSetBase::setDataSetMutexTimeout(
MutexIF::TimeoutType timeoutType, uint32_t mutexTimeout) {
this->timeoutType = timeoutType;
this->mutexTimeout = mutexTimeout;
object_id_t LocalPoolDataSetBase::getCreatorObjectId() {
if(poolManager != nullptr) {
return poolManager->getCreatorObjectId();
}
return objects::NO_OBJECT;
}

View File

@ -1,16 +1,16 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_
#include "HasLocalDataPoolIF.h"
#include "MarkChangedIF.h"
#include "localPoolDefinitions.h"
#include "../datapool/DataSetIF.h"
#include "../datapool/PoolDataSetBase.h"
#include "../serialize/SerializeIF.h"
#include <vector>
class LocalDataPoolManager;
class HasLocalDataPoolIF;
class PeriodicHousekeepingHelper;
/**
@ -41,186 +41,185 @@ class PeriodicHousekeepingHelper;
*
* @ingroup data_pool
*/
class LocalPoolDataSetBase: public PoolDataSetBase,
class LocalPoolDataSetBase:
public PoolDataSetBase,
public MarkChangedIF {
friend class LocalDataPoolManager;
friend class PeriodicHousekeepingHelper;
friend class LocalPoolDataSetAttorney;
friend class PeriodicHousekeepingHelper;
public:
/**
* @brief Constructor for the creator of local pool data.
* @details
* This constructor also initializes the components required for
* periodic handling.
*/
LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
uint32_t setId, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables, bool periodicHandling = true);
/**
* @brief Constructor for the creator of local pool data.
* @details
* This constructor also initializes the components required for
* periodic handling.
*/
LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
uint32_t setId, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables, bool periodicHandling = true);
/**
* @brief Constructor for users of local pool data.
* @details
* @param sid Unique identifier of dataset consisting of object ID and
* set ID.
* @param registeredVariablesArray
* @param maxNumberOfVariables
*/
LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables);
/**
* @brief Constructor for users of the local pool data, which need
* to access data created by one (!) HK manager.
* @details
* Unlike the first constructor, no component for periodic handling
* will be initiated.
* @param sid Unique identifier of dataset consisting of object ID and
* set ID.
* @param registeredVariablesArray
* @param maxNumberOfVariables
*/
LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables);
/**
* Simple constructor, if the dataset is not owner permanently by
* a class with a HK manager.
* @param registeredVariablesArray
* @param maxNumberOfVariables
*/
LocalPoolDataSetBase(PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectFunctions = true);
/**
* @brief Simple constructor, if the dataset is not the owner by
* a class with a HK manager.
* @details
* This constructor won't create components required for periodic handling
* and it also won't try to deduce the HK manager because no SID is
* supplied. This function should therefore be called by classes which need
* to access pool variables from different creators.
*
* If the class is intended to access pool variables from different
* creators, the third argument should be set to true. The mutex
* properties can be set with #setReadCommitProtectionBehaviour .
* @param registeredVariablesArray
* @param maxNumberOfVariables
* @param protectEveryReadCommitCall If the pool variables are created by
* multiple creators, this flag can be set to protect all read and
* commit calls separately.
*/
LocalPoolDataSetBase(PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables,
bool protectEveryReadCommitCall = true);
/**
* @brief The destructor automatically manages writing the valid
* information of variables.
* @details
* In case the data set was read out, but not committed(indicated by state),
* the destructor parses all variables that are still registered to the set.
* For each, the valid flag in the data pool is set to "invalid".
*/
~LocalPoolDataSetBase();
/**
* @brief The destructor automatically manages writing the valid
* information of variables.
* @details
* In case the data set was read out, but not committed(indicated by state),
* the destructor parses all variables that are still registered to the set.
* For each, the valid flag in the data pool is set to "invalid".
*/
~LocalPoolDataSetBase();
/**
* If the data is pulled from different local data pools, every read and
* commit call should be mutex protected for thread safety.
* This can be specified with the second parameter.
* @param dataCreator
* @param protectEveryReadCommit
*/
void setReadCommitProtectionBehaviour(bool protectEveryReadCommit,
uint32_t mutexTimeout = 20);
void setValidityBufferGeneration(bool withValidityBuffer);
void setDataSetMutexTimeout(MutexIF::TimeoutType timeoutType,
uint32_t mutexTimeout);
sid_t getSid() const;
void setValidityBufferGeneration(bool withValidityBuffer);
/** SerializeIF overrides */
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t *size,
SerializeIF::Endianness streamEndianness) override;
size_t getSerializedSize() const override;
sid_t getSid() const;
/**
* Special version of the serilization function which appends a
* validity buffer at the end. Each bit of this validity buffer
* denotes whether the container data set entries are valid from left
* to right, MSB first. (length = ceil(N/8), N = number of pool variables)
* @param buffer
* @param size
* @param maxSize
* @param bigEndian
* @param withValidityBuffer
* @return
*/
ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer,
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const;
ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer,
size_t *size, SerializeIF::Endianness streamEndianness);
ReturnValue_t serializeLocalPoolIds(uint8_t** buffer,
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness,
bool serializeFillCount = true) const;
uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const;
/** SerializeIF overrides */
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
ReturnValue_t deSerialize(const uint8_t** buffer, size_t *size,
SerializeIF::Endianness streamEndianness) override;
size_t getSerializedSize() const override;
/**
* Set the dataset valid or invalid. These calls are mutex protected.
* @param setEntriesRecursively
* If this is true, all contained datasets will also be set recursively.
*/
void setValidity(bool valid, bool setEntriesRecursively);
bool isValid() const override;
/**
* Special version of the serilization function which appends a
* validity buffer at the end. Each bit of this validity buffer
* denotes whether the container data set entries are valid from left
* to right, MSB first. (length = ceil(N/8), N = number of pool variables)
* @param buffer
* @param size
* @param maxSize
* @param bigEndian
* @param withValidityBuffer
* @return
*/
ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer,
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const;
ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer,
size_t *size, SerializeIF::Endianness streamEndianness);
ReturnValue_t serializeLocalPoolIds(uint8_t** buffer,
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness,
bool serializeFillCount = true) const;
uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const;
/**
* Set the dataset valid or invalid. These calls are mutex protected.
* @param setEntriesRecursively
* If this is true, all contained datasets will also be set recursively.
*/
void setValidity(bool valid, bool setEntriesRecursively);
bool isValid() const override;
/**
* These calls are mutex protected.
* @param changed
*/
void setChanged(bool changed) override;
bool hasChanged() const override;
/**
* These calls are mutex protected.
* @param changed
*/
void setChanged(bool changed) override;
bool hasChanged() const override;
object_id_t getCreatorObjectId();
protected:
sid_t sid;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeout = 20;
/**
* This mutex is required because the dataset can potentially be accessed
* by multiple threads for information like change status or validity.
*/
MutexIF* mutex = nullptr;
sid_t sid;
//! This mutex is used if the data is created by one object only.
MutexIF* mutexIfSingleDataCreator = nullptr;
bool diagnostic = false;
void setDiagnostic(bool diagnostics);
bool isDiagnostics() const;
bool diagnostic = false;
void setDiagnostic(bool diagnostics);
bool isDiagnostics() const;
/**
* Used for periodic generation.
*/
bool reportingEnabled = false;
void setReportingEnabled(bool enabled);
bool getReportingEnabled() const;
/**
* Used for periodic generation.
*/
bool reportingEnabled = false;
void setReportingEnabled(bool enabled);
bool getReportingEnabled() const;
void initializePeriodicHelper(float collectionInterval,
dur_millis_t minimumPeriodicInterval,
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5);
void initializePeriodicHelper(float collectionInterval,
dur_millis_t minimumPeriodicInterval,
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5);
/**
* If the valid state of a dataset is always relevant to the whole
* data set we can use this flag.
*/
bool valid = false;
/**
* If the valid state of a dataset is always relevant to the whole
* data set we can use this flag.
*/
bool valid = false;
/**
* Can be used to mark the dataset as changed, which is used
* by the LocalDataPoolManager to send out update messages.
*/
bool changed = false;
/**
* Can be used to mark the dataset as changed, which is used
* by the LocalDataPoolManager to send out update messages.
*/
bool changed = false;
/**
* Specify whether the validity buffer is serialized too when serializing
* or deserializing the packet. Each bit of the validity buffer will
* contain the validity state of the pool variables from left to right.
* The size of validity buffer thus will be ceil(N / 8) with N = number of
* pool variables.
*/
bool withValidityBuffer = true;
/**
* Specify whether the validity buffer is serialized too when serializing
* or deserializing the packet. Each bit of the validity buffer will
* contain the validity state of the pool variables from left to right.
* The size of validity buffer thus will be ceil(N / 8) with N = number of
* pool variables.
*/
bool withValidityBuffer = true;
/**
* @brief This is a small helper function to facilitate locking
* the global data pool.
* @details
* It makes use of the lockDataPool method offered by the DataPool class.
*/
ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) override;
/**
* @brief This is a small helper function to facilitate locking
* the global data pool.
* @details
* It makes use of the lockDataPool method offered by the DataPool class.
*/
ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) override;
/**
* @brief This is a small helper function to facilitate
* unlocking the global data pool
* @details
* It makes use of the freeDataPoolLock method offered by the DataPool class.
*/
ReturnValue_t unlockDataPool() override;
/**
* @brief This is a small helper function to facilitate
* unlocking the global data pool
* @details
* It makes use of the freeDataPoolLock method offered by the DataPool class.
*/
ReturnValue_t unlockDataPool() override;
LocalDataPoolManager* hkManager = nullptr;
/**
* Set n-th bit of a byte, with n being the position from 0
* (most significant bit) to 7 (least significant bit)
*/
void bitSetter(uint8_t* byte, uint8_t position) const;
bool bitGetter(const uint8_t* byte, uint8_t position) const;
/**
* Set n-th bit of a byte, with n being the position from 0
* (most significant bit) to 7 (least significant bit)
*/
void bitSetter(uint8_t* byte, uint8_t position) const;
bool bitGetter(const uint8_t* byte, uint8_t position) const;
PeriodicHousekeepingHelper* periodicHelper = nullptr;
PeriodicHousekeepingHelper* periodicHelper = nullptr;
LocalDataPoolManager* poolManager = nullptr;
};

View File

@ -1,9 +1,13 @@
#include "LocalPoolObjectBase.h"
#include "LocalDataPoolManager.h"
#include "internal/HasLocalDpIFUserAttorney.h"
#include "HasLocalDataPoolIF.h"
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId,
HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode): localPoolId(poolId),
readWriteMode(setReadWriteMode) {
#include "../objectmanager/ObjectManagerIF.h"
LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner,
DataSetIF* dataSet, pool_rwm_t setReadWriteMode):
localPoolId(poolId), readWriteMode(setReadWriteMode) {
if(poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
@ -17,23 +21,24 @@ LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId,
#endif
return;
}
hkManager = hkOwner->getHkManagerHandle();
AccessPoolManagerIF* poolManAccessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
hkManager = poolManAccessor->getHkManagerHandle();
if (dataSet != nullptr) {
dataSet->registerVariable(this);
}
}
LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
DataSetIF *dataSet, pool_rwm_t setReadWriteMode): localPoolId(poolId),
readWriteMode(setReadWriteMode) {
LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF *dataSet,
pool_rwm_t setReadWriteMode):
localPoolId(poolId), readWriteMode(setReadWriteMode) {
if(poolId == PoolVariableIF::NO_PARAMETER) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalPoolVar<T>::LocalPoolVar: 0 passed as pool ID, "
<< "which is the NO_PARAMETER value!" << std::endl;
#endif
}
HasLocalDataPoolIF* hkOwner =
objectManager->get<HasLocalDataPoolIF>(poolOwner);
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(poolOwner);
if(hkOwner == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVariable: The supplied pool owner did not "
@ -42,7 +47,12 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId,
#endif
return;
}
hkManager = hkOwner->getHkManagerHandle();
AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner);
if(accessor != nullptr) {
hkManager = accessor->getHkManagerHandle();
}
if(dataSet != nullptr) {
dataSet->registerVariable(this);
}
@ -77,46 +87,44 @@ bool LocalPoolObjectBase::hasChanged() const {
}
void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) {
this->readWriteMode = newReadWriteMode;
this->readWriteMode = newReadWriteMode;
}
void LocalPoolObjectBase::reportReadCommitError(const char* variableType,
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) {
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) {
#if FSFW_DISABLE_PRINTOUT == 0
const char* type = nullptr;
if(read) {
type = "read";
}
else {
type = "commit";
}
const char* type = nullptr;
if(read) {
type = "read";
}
else {
type = "commit";
}
const char* errMsg = nullptr;
if(error == HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND) {
errMsg = "Pool entry not found";
}
else if(error == HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT) {
errMsg = "Pool entry type conflict";
}
else if(error == PoolVariableIF::INVALID_READ_WRITE_MODE) {
errMsg = "Pool variable wrong read-write mode";
}
else if(error == PoolVariableIF::INVALID_POOL_ENTRY) {
errMsg = "Pool entry invalid";
}
else {
errMsg = "Unknown error code";
}
const char* errMsg = nullptr;
if(error == localpool::POOL_ENTRY_NOT_FOUND) {
errMsg = "Pool entry not found";
}
else if(error == localpool::POOL_ENTRY_TYPE_CONFLICT) {
errMsg = "Pool entry type conflict";
}
else if(error == PoolVariableIF::INVALID_READ_WRITE_MODE) {
errMsg = "Pool variable wrong read-write mode";
}
else if(error == PoolVariableIF::INVALID_POOL_ENTRY) {
errMsg = "Pool entry invalid";
}
else {
errMsg = "Unknown error code";
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << variableType << ": " << type << " call | " << errMsg
<< " | Owner: " << std::hex << std::setw(8)
<< std::setfill('0') << objectId << " LPID: 0x" << lpId
<< std::dec << std::endl;
sif::warning << variableType << ": " << type << " call | " << errMsg << " | Owner: 0x"
<< std::hex << std::setw(8) << std::setfill('0') << objectId << std::dec
<< " LPID: " << lpId << std::endl;
#else
fsfw::printWarning("LocalPoolVariable: %s of local pool variable of "
"object 0x%08x and lp ID 0x%08x failed.\n\r",
type, objectId, lpId);
sif::printWarning("%s: %s call | %s | Owner: 0x%08x LPID: %lu\n",
variableType, type, errMsg, objectId, lpId);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
}

View File

@ -2,10 +2,20 @@
#define FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_
#include "MarkChangedIF.h"
#include "../datapoollocal/LocalDataPoolManager.h"
#include "localPoolDefinitions.h"
#include "../objectmanager/SystemObjectIF.h"
#include "../datapool/PoolVariableIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
class LocalDataPoolManager;
class DataSetIF;
class HasLocalDataPoolIF;
/**
* @brief This class serves as a non-template base for pool objects like pool variables
* or pool vectors.
*/
class LocalPoolObjectBase: public PoolVariableIF,
public HasReturnvaluesIF,
public MarkChangedIF {
@ -54,12 +64,10 @@ protected:
ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE;
//! @brief Pointer to the class which manages the HK pool.
LocalDataPoolManager* hkManager;
LocalDataPoolManager* hkManager = nullptr;
void reportReadCommitError(const char* variableType,
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId);
void reportReadCommitError(const char* variableType,
ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId);
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ */

View File

@ -4,6 +4,8 @@
#include "LocalPoolObjectBase.h"
#include "HasLocalDataPoolIF.h"
#include "LocalDataPoolManager.h"
#include "AccessLocalPoolF.h"
#include "internal/LocalDpManagerAttorney.h"
#include "../datapool/PoolVariableIF.h"
#include "../datapool/DataSetIF.h"
@ -25,159 +27,159 @@
template<typename T>
class LocalPoolVariable: public LocalPoolObjectBase {
public:
//! Default ctor is forbidden.
LocalPoolVariable() = delete;
//! Default ctor is forbidden.
LocalPoolVariable() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*/
LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*/
LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner object ID of the pool owner.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*
*/
LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the global unique identifier of a pool variable.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
*
* It does not fetch the current value from the data pool, which
* has to be done by calling the read() operation.
* Datasets can be used to access multiple local pool entries in an
* efficient way. A pointer to a dataset can be passed to register
* the pool variable in that dataset directly.
* @param poolId ID of the local pool entry.
* @param hkOwner object ID of the pool owner.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
* @param setReadWriteMode Specify the read-write mode of the pool variable.
*
*/
LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the global unique identifier of a pool variable.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
virtual~ LocalPoolVariable() {};
virtual~ LocalPoolVariable() {};
/**
* @brief This is the local copy of the data pool entry.
* @details The user can work on this attribute
* just like he would on a simple local variable.
*/
T value = 0;
/**
* @brief This is the local copy of the data pool entry.
* @details The user can work on this attribute
* just like he would on a simple local variable.
*/
T value = 0;
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This commit function can be used to set the pool variable valid
* as well.
* @param setValid
* @param timeoutType
* @param timeoutMs
* @return
*/
ReturnValue_t commit(bool setValid, MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
/**
* @brief This commit function can be used to set the pool variable valid
* as well.
* @param setValid
* @param timeoutType
* @param timeoutMs
* @return
*/
ReturnValue_t commit(bool setValid, MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
LocalPoolVariable<T> &operator=(const T& newValue);
LocalPoolVariable<T> &operator=(const LocalPoolVariable<T>& newPoolVariable);
LocalPoolVariable<T> &operator=(const T& newValue);
LocalPoolVariable<T> &operator=(const LocalPoolVariable<T>& newPoolVariable);
//! Explicit type conversion operator. Allows casting the class to
//! its template type to perform operations on value.
explicit operator T() const;
//! Explicit type conversion operator. Allows casting the class to
//! its template type to perform operations on value.
explicit operator T() const;
bool operator==(const LocalPoolVariable<T>& other) const;
bool operator==(const T& other) const;
bool operator==(const LocalPoolVariable<T>& other) const;
bool operator==(const T& other) const;
bool operator!=(const LocalPoolVariable<T>& other) const;
bool operator!=(const T& other) const;
bool operator!=(const LocalPoolVariable<T>& other) const;
bool operator!=(const T& other) const;
bool operator<(const LocalPoolVariable<T>& other) const;
bool operator<(const T& other) const;
bool operator<(const LocalPoolVariable<T>& other) const;
bool operator<(const T& other) const;
bool operator>(const LocalPoolVariable<T>& other) const;
bool operator>(const T& other) const;
bool operator>(const LocalPoolVariable<T>& other) const;
bool operator>(const T& other) const;
protected:
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
#if FSFW_CPP_OSTREAM_ENABLED == 1
// std::ostream is the type for object std::cout
template <typename U>
friend std::ostream& operator<< (std::ostream &out,
const LocalPoolVariable<U> &var);
// std::ostream is the type for object std::cout
template <typename U>
friend std::ostream& operator<< (std::ostream &out,
const LocalPoolVariable<U> &var);
#endif
};

View File

@ -7,31 +7,32 @@
template<typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(HasLocalDataPoolIF* hkOwner,
lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
template<typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(object_id_t poolOwner,
lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
template<typename T>
inline LocalPoolVariable<T>::LocalPoolVariable(gp_id_t globalPoolId,
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId,
dataSet, setReadWriteMode){}
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId,
dataSet, setReadWriteMode){}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::read(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(hkManager->getMutexHandle(), timeoutType, timeoutMs);
return readWithoutLock();
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return readWithoutLock();
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
<<<<<<< HEAD
if(readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector",
@ -61,24 +62,58 @@ inline ReturnValue_t LocalPoolVariable<T>::readWithoutLock() {
this->value = *(poolEntry->address);
this->valid = poolEntry->valid;
return RETURN_OK;
=======
if(readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, true, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
if(result != RETURN_OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result,
false, ownerObjectId, localPoolId);
return result;
}
// Actually this should never happen..
// if(poolEntry->address == nullptr) {
// result = PoolVariableIF::INVALID_POOL_ENTRY;
// object_id_t ownerObjectId = hkManager->getOwner()->getObjectId();
// reportReadCommitError("LocalPoolVariable", result,
// false, ownerObjectId, localPoolId);
// return result;
// }
this->value = *(poolEntry->getDataPtr());
this->valid = poolEntry->getValid();
return RETURN_OK;
>>>>>>> upstream/mueller/master
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit(bool setValid,
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
this->setValid(setValid);
return commit(timeoutType, timeoutMs);
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
this->setValid(setValid);
return commit(timeoutType, timeoutMs);
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commit(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(hkManager->getMutexHandle(), timeoutType, timeoutMs);
return commitWithoutLock();
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return commitWithoutLock();
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
<<<<<<< HEAD
if(readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector",
@ -99,31 +134,55 @@ inline ReturnValue_t LocalPoolVariable<T>::commitWithoutLock() {
*(poolEntry->address) = this->value;
poolEntry->valid = this->valid;
return RETURN_OK;
=======
if(readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, false, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
//ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
if(result != RETURN_OK) {
object_id_t ownerObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVariable", result,
false, ownerObjectId, localPoolId);
return result;
}
*(poolEntry->getDataPtr()) = this->value;
poolEntry->setValid(this->valid);
return RETURN_OK;
>>>>>>> upstream/mueller/master
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::serialize(uint8_t** buffer,
size_t* size, const size_t max_size,
SerializeIF::Endianness streamEndianness) const {
return SerializeAdapter::serialize(&value,
buffer, size ,max_size, streamEndianness);
size_t* size, const size_t max_size,
SerializeIF::Endianness streamEndianness) const {
return SerializeAdapter::serialize(&value,
buffer, size ,max_size, streamEndianness);
}
template<typename T>
inline size_t LocalPoolVariable<T>::getSerializedSize() const {
return SerializeAdapter::getSerializedSize(&value);
return SerializeAdapter::getSerializedSize(&value);
}
template<typename T>
inline ReturnValue_t LocalPoolVariable<T>::deSerialize(const uint8_t** buffer,
size_t* size, SerializeIF::Endianness streamEndianness) {
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
size_t* size, SerializeIF::Endianness streamEndianness) {
return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness);
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
template<typename T>
inline std::ostream& operator<< (std::ostream &out,
const LocalPoolVariable<T> &var) {
const LocalPoolVariable<T> &var) {
out << var.value;
return out;
}
@ -131,68 +190,68 @@ inline std::ostream& operator<< (std::ostream &out,
template<typename T>
inline LocalPoolVariable<T>::operator T() const {
return value;
return value;
}
template<typename T>
inline LocalPoolVariable<T> & LocalPoolVariable<T>::operator=(
const T& newValue) {
const T& newValue) {
value = newValue;
return *this;
}
template<typename T>
inline LocalPoolVariable<T>& LocalPoolVariable<T>::operator =(
const LocalPoolVariable<T>& newPoolVariable) {
value = newPoolVariable.value;
return *this;
const LocalPoolVariable<T>& newPoolVariable) {
value = newPoolVariable.value;
return *this;
}
template<typename T>
inline bool LocalPoolVariable<T>::operator ==(
const LocalPoolVariable<T> &other) const {
return this->value == other.value;
const LocalPoolVariable<T> &other) const {
return this->value == other.value;
}
template<typename T>
inline bool LocalPoolVariable<T>::operator ==(const T &other) const {
return this->value == other;
return this->value == other;
}
template<typename T>
inline bool LocalPoolVariable<T>::operator !=(
const LocalPoolVariable<T> &other) const {
return not (*this == other);
const LocalPoolVariable<T> &other) const {
return not (*this == other);
}
template<typename T>
inline bool LocalPoolVariable<T>::operator !=(const T &other) const {
return not (*this == other);
return not (*this == other);
}
template<typename T>
inline bool LocalPoolVariable<T>::operator <(
const LocalPoolVariable<T> &other) const {
return this->value < other.value;
const LocalPoolVariable<T> &other) const {
return this->value < other.value;
}
template<typename T>
inline bool LocalPoolVariable<T>::operator <(const T &other) const {
return this->value < other;
return this->value < other;
}
template<typename T>
inline bool LocalPoolVariable<T>::operator >(
const LocalPoolVariable<T> &other) const {
return not (*this < other);
const LocalPoolVariable<T> &other) const {
return not (*this < other);
}
template<typename T>
inline bool LocalPoolVariable<T>::operator >(const T &other) const {
return not (*this < other);
return not (*this < other);
}
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */

View File

@ -2,6 +2,8 @@
#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_
#include "LocalPoolObjectBase.h"
#include "internal/LocalDpManagerAttorney.h"
#include "../datapool/DataSetIF.h"
#include "../datapool/PoolEntry.h"
#include "../datapool/PoolVariableIF.h"
@ -33,139 +35,148 @@
template<typename T, uint16_t vectorSize>
class LocalPoolVector: public LocalPoolObjectBase {
public:
LocalPoolVector() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
* It does not fetch the current value from the data pool. This is performed
* by the read() operation (which is not thread-safe).
* Datasets can be used to access local pool entires in a thread-safe way.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param setReadWriteMode Specify the read-write mode of the pool variable.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
LocalPoolVector() = delete;
/**
* This constructor is used by the data creators to have pool variable
* instances which can also be stored in datasets.
* It does not fetch the current value from the data pool. This is performed
* by the read() operation (which is not thread-safe).
* Datasets can be used to access local pool entires in a thread-safe way.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param setReadWriteMode Specify the read-write mode of the pool variable.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
* It does not fetch the current value from the data pool. This is performed
* by the read() operation (which is not thread-safe).
* Datasets can be used to access local pool entires in a thread-safe way.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param setReadWriteMode Specify the read-write mode of the pool variable.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the unique global identifier of a local pool
* vector.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVector(gp_id_t globalPoolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* This constructor is used by data users like controllers to have
* access to the local pool variables of data creators by supplying
* the respective creator object ID.
* It does not fetch the current value from the data pool. This is performed
* by the read() operation (which is not thread-safe).
* Datasets can be used to access local pool entires in a thread-safe way.
* @param poolId ID of the local pool entry.
* @param hkOwner Pointer of the owner. This will generally be the calling
* class itself which passes "this".
* @param setReadWriteMode Specify the read-write mode of the pool variable.
* @param dataSet The data set in which the variable shall register itself.
* If nullptr, the variable is not registered.
*/
LocalPoolVector(object_id_t poolOwner, lp_id_t poolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* Variation which takes the unique global identifier of a local pool
* vector.
* @param globalPoolId
* @param dataSet
* @param setReadWriteMode
*/
LocalPoolVector(gp_id_t globalPoolId,
DataSetIF* dataSet = nullptr,
pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE);
/**
* @brief This is the local copy of the data pool entry.
* @details
* The user can work on this attribute just like he would on a local
* array of this type.
*/
T value[vectorSize];
/**
* @brief The classes destructor is empty.
* @details If commit() was not called, the local value is
* discarded and not written back to the data pool.
*/
~LocalPoolVector() {};
/**
* @brief The operation returns the number of array entries
* in this variable.
*/
uint8_t getSize() {
return vectorSize;
}
/**
* @brief This is the local copy of the data pool entry.
* @details
* The user can work on this attribute just like he would on a local
* array of this type.
*/
T value[vectorSize];
/**
* @brief The classes destructor is empty.
* @details If commit() was not called, the local value is
* discarded and not written back to the data pool.
*/
~LocalPoolVector() {};
/**
* @brief The operation returns the number of array entries
* in this variable.
*/
uint8_t getSize() {
return vectorSize;
}
T& operator [](int i);
const T &operator [](int i) const;
T& operator [](size_t i);
const T &operator [](size_t i) const;
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size,
const size_t maxSize,
SerializeIF::Endianness streamEndiannes) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size,
const size_t maxSize,
SerializeIF::Endianness streamEndiannes) const override;
virtual size_t getSerializedSize() const override;
virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This is a call to read the array's values
* from the global data pool.
* @details
* When executed, this operation tries to fetch the pool entry with matching
* data pool id from the data pool and copies all array values and the valid
* information to its local attributes.
* In case of a failure (wrong type, size or pool id not found), the
* variable is set to zero and invalid.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t read(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief The commit call copies the array values back to the data pool.
* @details
* It checks type and size, as well as if the variable is writable. If so,
* the value is copied and the local valid flag is written back as well.
* The read call is protected with a lock.
* It is recommended to use DataSets to read and commit multiple variables
* at once to avoid the overhead of unnecessary lock und unlock operations.
*/
ReturnValue_t commit(MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20) override;
/**
* @brief This commit call also sets the validity of the pool entry.
* @details
*/
ReturnValue_t commit(bool valid, MutexIF::TimeoutType timeoutType =
MutexIF::TimeoutType::WAITING,
uint32_t timeoutMs = 20);
protected:
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
/**
* @brief Like #read, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t readWithoutLock() override;
/**
* @brief Like #commit, but without a lock protection of the global pool.
* @details
* The operation does NOT provide any mutual exclusive protection by itself.
* This can be used if the lock is handled externally to avoid the overhead
* of consecutive lock und unlock operations.
* Declared protected to discourage free public usage.
*/
ReturnValue_t commitWithoutLock() override;
private:
#if FSFW_CPP_OSTREAM_ENABLED == 1
// std::ostream is the type for object std::cout
template <typename U, uint16_t otherSize>
friend std::ostream& operator<< (std::ostream &out,
const LocalPoolVector<U, otherSize> &var);
// std::ostream is the type for object std::cout
template <typename U, uint16_t otherSize>
friend std::ostream& operator<< (std::ostream &out,
const LocalPoolVector<U, otherSize> &var);
#endif
};

View File

@ -7,143 +7,158 @@
template<typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(
HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet,
pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {}
template<typename T, uint16_t vectorSize>
inline LocalPoolVector<T, vectorSize>::LocalPoolVector(object_id_t poolOwner,
lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {}
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):
LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId,
dataSet, setReadWriteMode) {}
DataSetIF *dataSet, pool_rwm_t setReadWriteMode):
LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId,
dataSet, setReadWriteMode) {}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(hkManager->getMutexHandle(), timeoutType, timeoutMs);
return readWithoutLock();
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return readWithoutLock();
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::readWithoutLock() {
if(readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, true, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
if(readWriteMode == pool_rwm_t::VAR_WRITE) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, true, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
memset(this->value, 0, vectorSize * sizeof(T));
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
memset(this->value, 0, vectorSize * sizeof(T));
if(result != RETURN_OK) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector", result, true, targetObjectId,
localPoolId);
return result;
}
std::memcpy(this->value, poolEntry->address, poolEntry->getByteSize());
this->valid = poolEntry->valid;
return RETURN_OK;
if(result != RETURN_OK) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", result, true, targetObjectId,
localPoolId);
return result;
}
std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize());
this->valid = poolEntry->getValid();
return RETURN_OK;
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(bool valid,
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
this->setValid(valid);
return commit(timeoutType, timeoutMs);
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(hkManager->getMutexHandle(), timeoutType, timeoutMs);
return commitWithoutLock();
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return commitWithoutLock();
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commitWithoutLock() {
if(readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, false, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = hkManager->fetchPoolEntry(localPoolId, &poolEntry);
if(result != RETURN_OK) {
object_id_t targetObjectId = hkManager->getOwner()->getObjectId();
reportReadCommitError("LocalPoolVector", result, false, targetObjectId,
localPoolId);
return result;
}
std::memcpy(poolEntry->address, this->value, poolEntry->getByteSize());
poolEntry->valid = this->valid;
return RETURN_OK;
if(readWriteMode == pool_rwm_t::VAR_READ) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector",
PoolVariableIF::INVALID_READ_WRITE_MODE, false, targetObjectId,
localPoolId);
return PoolVariableIF::INVALID_READ_WRITE_MODE;
}
PoolEntry<T>* poolEntry = nullptr;
ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId,
&poolEntry);
if(result != RETURN_OK) {
object_id_t targetObjectId = hkManager->getCreatorObjectId();
reportReadCommitError("LocalPoolVector", result, false, targetObjectId,
localPoolId);
return result;
}
std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize());
poolEntry->setValid(this->valid);
return RETURN_OK;
}
template<typename T, uint16_t vectorSize>
inline T& LocalPoolVector<T, vectorSize>::operator [](int i) {
if(i <= vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
inline T& LocalPoolVector<T, vectorSize>::operator [](size_t i) {
if(i < vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVector: Invalid index. Setting or returning"
" last value!" << std::endl;
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
" last value!" << std::endl;
#else
sif::printWarning("LocalPoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[i];
return value[vectorSize - 1];
}
template<typename T, uint16_t vectorSize>
inline const T& LocalPoolVector<T, vectorSize>::operator [](int i) const {
if(i <= vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
inline const T& LocalPoolVector<T, vectorSize>::operator [](size_t i) const {
if(i < vectorSize) {
return value[i];
}
// If this happens, I have to set some value. I consider this
// a configuration error, but I wont exit here.
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPoolVector: Invalid index. Setting or returning"
" last value!" << std::endl;
sif::warning << "LocalPoolVector: Invalid index. Setting or returning"
" last value!" << std::endl;
#else
sif::printWarning("LocalPoolVector: Invalid index. Setting or returning"
" last value!\n");
#endif
return value[i];
return value[vectorSize - 1];
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::serialize(uint8_t** buffer,
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::serialize(&(value[i]), buffer, size,
maxSize, streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
break;
}
}
return result;
size_t* size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::serialize(&(value[i]), buffer, size,
maxSize, streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
break;
}
}
return result;
}
template<typename T, uint16_t vectorSize>
inline size_t LocalPoolVector<T, vectorSize>::getSerializedSize() const {
return vectorSize * SerializeAdapter::getSerializedSize(value);
return vectorSize * SerializeAdapter::getSerializedSize(value);
}
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::deSerialize(
const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
break;
}
}
return result;
const uint8_t** buffer, size_t* size,
SerializeIF::Endianness streamEndianness) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
for (uint16_t i = 0; i < vectorSize; i++) {
result = SerializeAdapter::deSerialize(&(value[i]), buffer, size,
streamEndianness);
if (result != HasReturnvaluesIF::RETURN_OK) {
break;
}
}
return result;
}
#if FSFW_CPP_OSTREAM_ENABLED == 1

View File

@ -1,7 +1,8 @@
#ifndef FSFW_DATAPOOLLOCAL_POOLREADHELPER_H_
#define FSFW_DATAPOOLLOCAL_POOLREADHELPER_H_
#include <fsfw/datapoollocal/LocalPoolDataSetBase.h>
#include "LocalPoolDataSetBase.h"
#include "../serviceinterface/ServiceInterface.h"
#include <FSFWConfig.h>
/**
@ -9,36 +10,38 @@
*/
class PoolReadHelper {
public:
PoolReadHelper(ReadCommitIF* readObject,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) {
if(readObject != nullptr) {
readResult = readObject->read(timeoutType, mutexTimeout);
#if FSFW_PRINT_VERBOSITY_LEVEL == 1
PoolReadHelper(ReadCommitIF* readObject,
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
uint32_t mutexTimeout = 20):
readObject(readObject), mutexTimeout(mutexTimeout) {
if(readObject != nullptr) {
readResult = readObject->read(timeoutType, mutexTimeout);
#if FSFW_VERBOSE_LEVEL == 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PoolReadHelper: Read failed!" << std::endl;
#endif
#endif
}
}
sif::error << "PoolReadHelper: Read failed!" << std::endl;
#else
sif::printError("PoolReadHelper: Read failed!\n");
#endif /* FSFW_PRINT_VERBOSITY_LEVEL == 1 */
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
}
}
ReturnValue_t getReadResult() const {
return readResult;
}
ReturnValue_t getReadResult() const {
return readResult;
}
~PoolReadHelper() {
if(readObject != nullptr) {
readObject->commit(timeoutType, mutexTimeout);
}
~PoolReadHelper() {
if(readObject != nullptr) {
readObject->commit(timeoutType, mutexTimeout);
}
}
}
private:
ReadCommitIF* readObject = nullptr;
ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeout = 20;
ReadCommitIF* readObject = nullptr;
ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t mutexTimeout = 20;
};

View File

@ -0,0 +1,83 @@
#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_
#include "localPoolDefinitions.h"
#include "../ipc/messageQueueDefinitions.h"
#include "../returnvalues/HasReturnvaluesIF.h"
class ProvidesDataPoolSubscriptionIF {
public:
virtual ~ProvidesDataPoolSubscriptionIF(){};
/**
* @brief Subscribe for the generation of periodic packets.
* @details
* This subscription mechanism will generally be used by the data creator
* to generate housekeeping packets which are downlinked directly.
* @return
*/
virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid,
bool enableReporting,
float collectionInterval, bool isDiagnostics,
object_id_t packetDestination) = 0;
/**
* @brief Subscribe for the generation of packets if the dataset
* is marked as changed.
* @details
* This subscription mechanism will generally be used by the data creator.
* @param sid
* @param isDiagnostics
* @param packetDestination
* @return
*/
virtual ReturnValue_t subscribeForUpdatePackets(sid_t sid,
bool reportingEnabled,
bool isDiagnostics,
object_id_t packetDestination) = 0;
/**
* @brief Subscribe for a notification message which will be sent
* if a dataset has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param setId Set ID of the set to receive update messages from.
* @param destinationObject
* @param targetQueueId
* @param generateSnapshot If this is set to true, a copy of the current
* data with a timestamp will be generated and sent via message.
* Otherwise, only an notification message is sent.
* @return
*/
virtual ReturnValue_t subscribeForSetUpdateMessages(const uint32_t setId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0;
/**
* @brief Subscribe for an notification message which will be sent if a
* pool variable has changed.
* @details
* This subscription mechanism will generally be used internally by
* other software components.
* @param localPoolId Pool ID of the pool variable
* @param destinationObject
* @param targetQueueId
* @param generateSnapshot If this is set to true, a copy of the current
* data with a timestamp will be generated and sent via message.
* Otherwise, only an notification message is sent.
* @return
*/
virtual ReturnValue_t subscribeForVariableUpdateMessages(
const lp_id_t localPoolId,
object_id_t destinationObject,
MessageQueueId_t targetQueueId,
bool generateSnapshot) = 0;
};
#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */

View File

@ -6,7 +6,15 @@
#include "../objectmanager/SystemObject.h"
#include <vector>
class SharedLocalDataSet: public SystemObject,
/**
* This local dataset variation can be used if the dataset is used concurrently across
* multiple threads. It provides a lock in addition to all other functionalities provided
* by the LocalPoolDataSetBase class.
*
* TODO: override and protect read, commit and some other calls used by pool manager.
*/
class SharedLocalDataSet:
public SystemObject,
public LocalPoolDataSetBase,
public SharedDataSetIF {
public:

View File

@ -20,26 +20,24 @@
template <uint8_t NUM_VARIABLES>
class StaticLocalDataSet: public LocalPoolDataSetBase {
public:
/**
* Constructor used by data owner and creator like device handlers.
* This constructor also initialized the components required for
* periodic handling.
* @param hkOwner
* @param setId
*/
StaticLocalDataSet(HasLocalDataPoolIF* hkOwner,
uint32_t setId): LocalPoolDataSetBase(hkOwner, setId, nullptr,
NUM_VARIABLES) {
/**
* Constructor used by data owner and creator like device handlers.
* This constructor also initialized the components required for
* periodic handling.
* @param hkOwner
* @param setId
*/
StaticLocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId):
LocalPoolDataSetBase(hkOwner, setId, nullptr, NUM_VARIABLES) {
this->setContainer(poolVarList.data());
}
/**
* Constructor used by data users like controllers.
* @param hkOwner
* @param setId
*/
StaticLocalDataSet(sid_t sid): LocalPoolDataSetBase(sid, nullptr,
NUM_VARIABLES) {
/**
* Constructor used by data users like controllers.
* @param hkOwner
* @param setId
*/
StaticLocalDataSet(sid_t sid): LocalPoolDataSetBase(sid, nullptr, NUM_VARIABLES) {
this->setContainer(poolVarList.data());
}

View File

@ -0,0 +1,5 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
HasLocalDpIFUserAttorney.cpp
HasLocalDpIFManagerAttorney.cpp
)

View File

@ -0,0 +1,18 @@
#include "HasLocalDpIFManagerAttorney.h"
#include "../LocalPoolObjectBase.h"
#include "../LocalPoolDataSetBase.h"
#include "../HasLocalDataPoolIF.h"
LocalPoolDataSetBase* HasLocalDpIFManagerAttorney::getDataSetHandle(HasLocalDataPoolIF* clientIF,
sid_t sid) {
return clientIF->getDataSetHandle(sid);
}
LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
lp_id_t localPoolId) {
return clientIF->getPoolObjectHandle(localPoolId);
}
object_id_t HasLocalDpIFManagerAttorney::getObjectId(HasLocalDataPoolIF* clientIF) {
return clientIF->getObjectId();
}

View File

@ -0,0 +1,22 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_
#include "../localPoolDefinitions.h"
class HasLocalDataPoolIF;
class LocalPoolDataSetBase;
class LocalPoolObjectBase;
class HasLocalDpIFManagerAttorney {
static LocalPoolDataSetBase* getDataSetHandle(HasLocalDataPoolIF* clientIF, sid_t sid);
static LocalPoolObjectBase* getPoolObjectHandle(HasLocalDataPoolIF* clientIF,
lp_id_t localPoolId);
static object_id_t getObjectId(HasLocalDataPoolIF* clientIF);
friend class LocalDataPoolManager;
};
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ */

View File

@ -0,0 +1,7 @@
#include "HasLocalDpIFUserAttorney.h"
#include "../AccessLocalPoolF.h"
#include "../HasLocalDataPoolIF.h"
AccessPoolManagerIF* HasLocalDpIFUserAttorney::getAccessorHandle(HasLocalDataPoolIF *clientIF) {
return clientIF->getAccessorHandle();
}

View File

@ -0,0 +1,18 @@
#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_
class HasLocalDataPoolIF;
class AccessPoolManagerIF;
class HasLocalDpIFUserAttorney {
private:
static AccessPoolManagerIF* getAccessorHandle(HasLocalDataPoolIF* clientIF);
friend class LocalPoolObjectBase;
friend class LocalPoolDataSetBase;
};
#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_ */

View File

@ -0,0 +1,32 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_
#include "../LocalDataPoolManager.h"
/**
* @brief This is a helper class implements the Attorney-Client idiom for access to
* LocalDataPoolManager internals
* @details
* This helper class provides better control over which classes are allowed to access
* LocalDataPoolManager internals in a granular and encapsulated way when compared to
* other methods like direct friend declarations. It allows these classes to use
* an explicit subset of the pool manager private/protected functions.
* See: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Friendship_and_the_Attorney-Client
*/
class LocalDpManagerAttorney {
private:
template<typename T> static ReturnValue_t fetchPoolEntry(LocalDataPoolManager& manager,
lp_id_t localPoolId, PoolEntry<T> **poolEntry) {
return manager.fetchPoolEntry(localPoolId, poolEntry);
}
static MutexIF* getMutexHandle(LocalDataPoolManager& manager) {
return manager.getMutexHandle();
}
template<typename T> friend class LocalPoolVariable;
template<typename T, uint16_t vecSize> friend class LocalPoolVector;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ */

View File

@ -0,0 +1,39 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_
#include "../LocalPoolDataSetBase.h"
class LocalPoolDataSetAttorney {
private:
static void setDiagnostic(LocalPoolDataSetBase& set, bool diagnostics) {
set.setDiagnostic(diagnostics);
}
static bool isDiagnostics(LocalPoolDataSetBase& set) {
return set.isDiagnostics();
}
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
uint32_t minimumPeriodicIntervalMs,
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5) {
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, isDiagnostics,
nonDiagIntervalFactor);
}
static void setReportingEnabled(LocalPoolDataSetBase& set, bool enabled) {
set.setReportingEnabled(enabled);
}
static bool getReportingEnabled(LocalPoolDataSetBase& set) {
return set.getReportingEnabled();
}
static PeriodicHousekeepingHelper* getPeriodicHelper(LocalPoolDataSetBase& set) {
return set.periodicHelper;
}
friend class LocalDataPoolManager;
};
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ */

View File

@ -1,10 +1,13 @@
#ifndef FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_
#define FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_
#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
#define FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_
#include <cstdint>
#include "../datapool/PoolEntryIF.h"
#include "../objectmanager/SystemObjectIF.h"
#include "../objectmanager/frameworkObjects.h"
#include <cstdint>
#include <map>
/**
* @brief Type definition for local pool entries.
*/
@ -12,10 +15,21 @@ using lp_id_t = uint32_t;
namespace localpool {
static constexpr uint32_t INVALID_LPID = -1;
static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF;
static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00);
static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01);
//! This is the core data structure of the local data pools. Users should insert all desired
//! pool variables, using the std::map interface.
using DataPool = std::map<lp_id_t, PoolEntryIF*>;
using DataPoolMapIter = DataPool::iterator;
}
/**
* Used as a unique identifier for data sets.
* Used as a unique identifier for data sets. Consists of 4 byte object ID and 4 byte set ID.
*/
union sid_t {
static constexpr uint64_t INVALID_SID = -1;
@ -26,8 +40,8 @@ union sid_t {
sid_t(): raw(INVALID_SID) {}
sid_t(object_id_t objectId, uint32_t setId):
objectId(objectId),
ownerSetId(setId) {}
objectId(objectId),
ownerSetId(setId) {}
struct {
object_id_t objectId ;
@ -57,29 +71,30 @@ union sid_t {
};
/**
* Used as a global unique identifier for local pool variables.
* Used as a global unique identifier for local pool variables. Consists of 4 byte object ID
* and 4 byte local pool ID.
*/
union gp_id_t {
static constexpr uint64_t INVALID_GPID = -1;
static constexpr uint64_t INVALID_GPID = -1;
static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT;
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID;
gp_id_t(): raw(INVALID_GPID) {}
gp_id_t(): raw(INVALID_GPID) {}
gp_id_t(object_id_t objectId, lp_id_t localPoolId):
objectId(objectId),
localPoolId(localPoolId) {}
gp_id_t(object_id_t objectId, lp_id_t localPoolId):
objectId(objectId),
localPoolId(localPoolId) {}
struct {
object_id_t objectId;
lp_id_t localPoolId;
};
struct {
object_id_t objectId;
lp_id_t localPoolId;
};
uint64_t raw;
uint64_t raw;
bool notSet() const {
return raw == INVALID_GPID;
}
bool notSet() const {
return raw == INVALID_GPID;
}
bool operator==(const sid_t& other) const {
return raw == other.raw;
@ -90,4 +105,4 @@ union gp_id_t {
}
};
#endif /* FSFW_DATAPOOLLOCAL_LOCPOOLDEFINITIONS_H_ */
#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ */

View File

@ -9,18 +9,14 @@
//! the C stdio functions can be used alternatively
#define FSFW_CPP_OSTREAM_ENABLED 1
//! More FSFW related printouts.
//! Be careful, this also turns off most diagnostic prinouts!
#define FSFW_ENHANCED_PRINTOUT 0
//! More FSFW related printouts depending on level. Useful for development.
#define FSFW_VERBOSE_LEVEL 1
//! Can be used to completely disable printouts, even the C stdio ones.
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_ENHANCED_PRINTOUT == 0
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0
#define FSFW_DISABLE_PRINTOUT 0
#endif
//! Can be used to enable additional debugging printouts for developing the FSFW
#define FSFW_PRINT_VERBOSITY_LEVEL 0
//! Can be used to disable the ANSI color sequences for C stdio.
#define FSFW_COLORED_OUTPUT 1

View File

@ -2,7 +2,7 @@
#include "AcceptsDeviceResponsesIF.h"
#include "DeviceTmReportingWrapper.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../objectmanager/ObjectManager.h"
#include "../storagemanager/StorageManagerIF.h"
#include "../thermal/ThermalComponentIF.h"
@ -13,9 +13,6 @@
#include "../subsystem/SubsystemBase.h"
#include "../datapoollocal/LocalPoolVariable.h"
#include <iomanip>
object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
object_id_t DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT;
object_id_t DeviceHandlerBase::defaultFdirParentId = objects::NO_OBJECT;
@ -27,7 +24,7 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId,
wiretappingMode(OFF), storedRawData(StorageManagerIF::INVALID_ADDRESS),
deviceCommunicationId(deviceCommunication), comCookie(comCookie),
healthHelper(this,setObjectId), modeHelper(this), parameterHelper(this),
actionHelper(this, nullptr), hkManager(this, nullptr),
actionHelper(this, nullptr), poolManager(this, nullptr),
childTransitionFailure(RETURN_OK), fdirInstance(fdirInstance),
hkSwitcher(this), defaultFDIRUsed(fdirInstance == nullptr),
switchOffWasReported(false), childTransitionDelay(5000),
@ -39,13 +36,8 @@ DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId,
cookieInfo.state = COOKIE_UNUSED;
cookieInfo.pendingCommand = deviceCommandMap.end();
if (comCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase: ObjectID 0x" << std::hex
<< std::setw(8) << std::setfill('0') << this->getObjectId()
<< std::dec << ": Do not pass nullptr as a cookie, consider "
<< std::setfill(' ') << "passing a dummy cookie instead!"
<< std::endl;
#endif
printWarningOrError(sif::OutputTypes::OUT_ERROR, "DeviceHandlerBase",
HasReturnvaluesIF::RETURN_FAILED, "Invalid cookie");
}
if (this->fdirInstance == nullptr) {
this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId,
@ -115,7 +107,7 @@ ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) {
doGetRead();
// This will be performed after datasets have been updated by the
// custom device implementation.
hkManager.performHkOperation();
poolManager.performHkOperation();
break;
default:
break;
@ -132,30 +124,24 @@ ReturnValue_t DeviceHandlerBase::initialize() {
communicationInterface = objectManager->get<DeviceCommunicationIF>(
deviceCommunicationId);
if (communicationInterface == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::initialize: Communication interface "
"invalid." << std::endl;
sif::error << "Make sure it is set up properly and implements"
" DeviceCommunicationIF" << std::endl;
#endif
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
ObjectManagerIF::CHILD_INIT_FAILED,
"Passed communication IF invalid");
return ObjectManagerIF::CHILD_INIT_FAILED;
}
result = communicationInterface->initializeInterface(comCookie);
if (result != RETURN_OK) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::initialize: Initializing "
"communication interface failed!" << std::endl;
#endif
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
ObjectManagerIF::CHILD_INIT_FAILED,
"ComIF initialization failed");
return result;
}
IPCStore = objectManager->get<StorageManagerIF>(objects::IPC_STORE);
if (IPCStore == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::initialize: IPC store not set up in "
"factory." << std::endl;
#endif
printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize",
ObjectManagerIF::CHILD_INIT_FAILED, "IPC Store not set up");
return ObjectManagerIF::CHILD_INIT_FAILED;
}
@ -164,11 +150,15 @@ ReturnValue_t DeviceHandlerBase::initialize() {
AcceptsDeviceResponsesIF>(rawDataReceiverId);
if (rawReceiver == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_ERROR,
"initialize", ObjectManagerIF::CHILD_INIT_FAILED,
"Raw receiver object ID set but no valid object found.");
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::initialize: Raw receiver object "
"ID set but no valid object found." << std::endl;
sif::error << "Make sure the raw receiver object is set up properly"
" and implements AcceptsDeviceResponsesIF" << std::endl;
#else
sif::printError("Make sure the raw receiver object is set up "
"properly and implements AcceptsDeviceResponsesIF\n");
#endif
return ObjectManagerIF::CHILD_INIT_FAILED;
}
@ -178,11 +168,15 @@ ReturnValue_t DeviceHandlerBase::initialize() {
if(powerSwitcherId != objects::NO_OBJECT) {
powerSwitcher = objectManager->get<PowerSwitchIF>(powerSwitcherId);
if (powerSwitcher == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_ERROR,
"initialize", ObjectManagerIF::CHILD_INIT_FAILED,
"Power switcher set but no valid object found.");
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::initialize: Power switcher "
<< "object ID set but no valid object found." << std::endl;
sif::error << "Make sure the raw receiver object is set up properly"
<< " and implements PowerSwitchIF" << std::endl;
sif::error << "Make sure the power switcher object is set up "
<< "properly and implements PowerSwitchIF" << std::endl;
#else
sif::printError("Make sure the power switcher object is set up "
"properly and implements PowerSwitchIF\n");
#endif
return ObjectManagerIF::CHILD_INIT_FAILED;
}
@ -216,7 +210,7 @@ ReturnValue_t DeviceHandlerBase::initialize() {
return result;
}
result = hkManager.initialize(commandQueue);
result = poolManager.initialize(commandQueue);
if (result != HasReturnvaluesIF::RETURN_OK) {
return result;
}
@ -286,7 +280,7 @@ void DeviceHandlerBase::readCommandQueue() {
return;
}
result = hkManager.handleHousekeepingMessage(&command);
result = poolManager.handleHousekeepingMessage(&command);
if (result == RETURN_OK) {
return;
}
@ -556,17 +550,17 @@ void DeviceHandlerBase::replyReturnvalueToCommand(ReturnValue_t status,
void DeviceHandlerBase::replyToCommand(ReturnValue_t status,
uint32_t parameter) {
//Check if we reply to a raw command.
// Check if we reply to a raw command.
if (cookieInfo.pendingCommand->first == RAW_COMMAND_ID) {
if (status == NO_REPLY_EXPECTED) {
status = RETURN_OK;
}
replyReturnvalueToCommand(status, parameter);
//Always delete data from a raw command.
// Always delete data from a raw command.
IPCStore->deleteData(storedRawData);
return;
}
//Check if we were externally commanded.
// Check if we were externally commanded.
if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) {
MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo;
if (status == NO_REPLY_EXPECTED) {
@ -581,15 +575,17 @@ void DeviceHandlerBase::replyToCommand(ReturnValue_t status,
void DeviceHandlerBase::replyToReply(DeviceReplyMap::iterator iter,
ReturnValue_t status) {
//No need to check if iter exists, as this is checked by callers. If someone else uses the method, add check.
// No need to check if iter exists, as this is checked by callers.
// If someone else uses the method, add check.
if (iter->second.command == deviceCommandMap.end()) {
//Is most likely periodic reply. Silent return.
return;
}
//Check if more replies are expected. If so, do nothing.
// Check if more replies are expected. If so, do nothing.
DeviceCommandInfo* info = &(iter->second.command->second);
if (--info->expectedReplies == 0) {
//Check if it was transition or internal command. Don't send any replies in that case.
// Check if it was transition or internal command.
// Don't send any replies in that case.
if (info->sendReplyTo != NO_COMMANDER) {
actionHelper.finish(info->sendReplyTo, iter->first, status);
}
@ -606,7 +602,7 @@ void DeviceHandlerBase::doSendWrite() {
if (result == RETURN_OK) {
cookieInfo.state = COOKIE_WRITE_SENT;
} else {
//always generate a failure event, so that FDIR knows what's up
// always generate a failure event, so that FDIR knows what's up
triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result,
cookieInfo.pendingCommand->first);
replyToCommand(result);
@ -721,10 +717,9 @@ void DeviceHandlerBase::parseReply(const uint8_t* receivedData,
case RETURN_OK:
handleReply(receivedData, foundId, foundLen);
if(foundLen == 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!"
" Packet parsing will be stuck." << std::endl;
#endif
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"parseReply", ObjectManagerIF::CHILD_INIT_FAILED,
"Found length is one, parsing might be stuck");
}
break;
case APERIODIC_REPLY: {
@ -735,6 +730,9 @@ void DeviceHandlerBase::parseReply(const uint8_t* receivedData,
foundId);
}
if(foundLen == 0) {
printWarningOrError(sif::OutputTypes::OUT_ERROR,
"parseReply", ObjectManagerIF::CHILD_INIT_FAILED,
"Power switcher set but no valid object found.");
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!"
" Packet parsing will be stuck." << std::endl;
@ -747,7 +745,8 @@ void DeviceHandlerBase::parseReply(const uint8_t* receivedData,
case IGNORE_FULL_PACKET:
return;
default:
//We need to wait for timeout.. don't know what command failed and who sent it.
// We need to wait for timeout.. don't know what command failed
// and who sent it.
replyRawReplyIfnotWiretapped(receivedData, foundLen);
triggerEvent(DEVICE_READING_REPLY_FAILED, result, foundLen);
break;
@ -968,7 +967,8 @@ ReturnValue_t DeviceHandlerBase::getStateOfSwitches(void) {
}
Mode_t DeviceHandlerBase::getBaseMode(Mode_t transitionMode) {
//only child action special modes are handled, as a child should never see any base action modes
// only child action special modes are handled, as a child should
// never see any base action modes
if (transitionMode == _MODE_START_UP) {
return _MODE_TO_ON;
}
@ -1291,12 +1291,11 @@ void DeviceHandlerBase::buildInternalCommand(void) {
if (mode == MODE_NORMAL) {
result = buildNormalDeviceCommand(&deviceCommandId);
if (result == BUSY) {
//so we can track misconfigurations
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << std::hex << getObjectId()
<< ": DHB::buildInternalCommand: Busy" << std::dec
<< std::endl;
#endif
// so we can track misconfigurations
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"buildInternalCommand",
HasReturnvaluesIF::RETURN_FAILED,
"Busy.");
result = NOTHING_TO_SEND; //no need to report this
}
}
@ -1320,12 +1319,15 @@ void DeviceHandlerBase::buildInternalCommand(void) {
if (iter == deviceCommandMap.end()) {
result = COMMAND_NOT_SUPPORTED;
} else if (iter->second.isExecuting) {
//so we can track misconfigurations
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << std::hex << getObjectId()
<< ": DHB::buildInternalCommand: Command "
<< deviceCommandId << " isExecuting" << std::dec
<< std::endl;
#if FSFW_DISABLE_PRINTOUT == 0
char output[36];
sprintf(output, "Command 0x%08x is executing",
static_cast<unsigned int>(deviceCommandId));
// so we can track misconfigurations
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"buildInternalCommand",
HasReturnvaluesIF::RETURN_FAILED,
output);
#endif
// this is an internal command, no need to report a failure here,
// missed reply will track if a reply is too late, otherwise, it's ok
@ -1419,7 +1421,7 @@ void DeviceHandlerBase::performOperationHook() {
}
ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(
LocalDataPool &localDataPoolMap,
localpool::DataPool &localDataPoolMap,
LocalDataPoolManager& poolManager) {
if(thermalSet != nullptr) {
localDataPoolMap.emplace(thermalSet->thermalStatePoolId,
@ -1430,18 +1432,13 @@ ReturnValue_t DeviceHandlerBase::initializeLocalDataPool(
return RETURN_OK;
}
LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() {
return &hkManager;
}
ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() {
// In this function, the task handle should be valid if the task
// was implemented correctly. We still check to be 1000 % sure :-)
if(executingTask != nullptr) {
pstIntervalMs = executingTask->getPeriodMs();
}
this->hkManager.initializeAfterTaskCreation();
this->poolManager.initializeAfterTaskCreation();
if(setStartupImmediately) {
startTransition(MODE_ON, SUBMODE_NONE);
@ -1485,3 +1482,56 @@ void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() {
}
}
}
AccessPoolManagerIF* DeviceHandlerBase::getAccessorHandle() {
return &poolManager;
}
void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
const char *functionName, ReturnValue_t errorCode,
const char *errorPrint) {
if(errorPrint == nullptr) {
if(errorCode == ObjectManagerIF::CHILD_INIT_FAILED) {
errorPrint = "Initialization error";
}
if(errorCode == HasReturnvaluesIF::RETURN_FAILED) {
if(errorType == sif::OutputTypes::OUT_WARNING) {
errorPrint = "Generic Warning";
}
else {
errorPrint = "Generic Error";
}
}
else {
errorPrint = "Unknown error";
}
}
if(errorType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID "
<< std::hex << std::setw(8) << std::setfill('0')
<< this->getObjectId() << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
#else
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint);
#endif
}
else if(errorType == sif::OutputTypes::OUT_ERROR) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DeviceHandlerBase::" << functionName << ": Object ID "
<< std::hex << std::setw(8) << std::setfill('0')
<< this->getObjectId() << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
#else
sif::printError("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint);
#endif
}
}
ProvidesDataPoolSubscriptionIF* DeviceHandlerBase::getSubscriptionInterface() {
return &poolManager;
}

View File

@ -6,6 +6,8 @@
#include "DeviceHandlerFailureIsolation.h"
#include "DeviceHandlerThermalSet.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../serviceinterface/serviceInterfaceDefintions.h"
#include "../objectmanager/SystemObject.h"
#include "../tasks/ExecutableObjectIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
@ -512,11 +514,18 @@ protected:
* @param localDataPoolMap
* @return
*/
virtual ReturnValue_t initializeLocalDataPool(LocalDataPool& localDataPoolMap,
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
/** Get the HK manager object handle */
virtual LocalDataPoolManager* getHkManagerHandle() override;
/**
* Provides the subscription handle which can be used by classes like controllers
* to get messages on data updates.
* @return
*/
ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() override;
//! Accessor handle required for internal handling.
AccessPoolManagerIF* getAccessorHandle() override;
/**
* @brief Hook function for child handlers which is called once per
@ -646,7 +655,7 @@ protected:
/** Action helper for HasActionsIF */
ActionHelper actionHelper;
/** Housekeeping Manager */
LocalDataPoolManager hkManager;
LocalDataPoolManager poolManager;
/**
* @brief Information about commands
@ -1111,7 +1120,7 @@ private:
/**
* @brief The mode the current transition originated from
*
* This is private so the child can not change it and fuck up the timeouts
* This is private so the child can not change it and mess up the timeouts
*
* IMPORTANT: This is not valid during _MODE_SHUT_DOWN and _MODE_START_UP!!
* (it is _MODE_POWER_DOWN during this modes)
@ -1190,7 +1199,8 @@ private:
* Check if the RMAP sendWrite action was successful.
*
* Depending on the result, the following is done
* - if the device command was external commanded, a reply is sent indicating the result
* - if the device command was external commanded, a reply is sent
* indicating the result
* - if the action was successful, the reply timout counter is initialized
*/
void doGetWrite(void);
@ -1206,9 +1216,9 @@ private:
/**
* Check the getRead reply and the contained data.
*
* If data was received scanForReply() and, if successful, handleReply() are called.
* If the current mode is @c MODE_RAW, the received packet is sent to the commanding object
* via commandQueue.
* If data was received scanForReply() and, if successful, handleReply()
* are called. If the current mode is @c MODE_RAW, the received packet
* is sent to the commanding object via commandQueue.
*/
void doGetRead(void);
@ -1227,7 +1237,7 @@ private:
uint32_t *len);
/**
* @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW NOTHING ELSE!!!
* @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW, nothing else!
*/
void setTransition(Mode_t modeTo, Submode_t submodeTo);
@ -1247,6 +1257,11 @@ private:
void handleTransitionToOnMode(Mode_t commandedMode,
Submode_t commandedSubmode);
void printWarningOrError(sif::OutputTypes errorType,
const char* functionName,
ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED,
const char* errorPrint = nullptr);
};
#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */

View File

@ -3,8 +3,8 @@
#include "DeviceHandlerMessage.h"
#include "../datapoollocal/localPoolDefinitions.h"
#include "../action/HasActionsIF.h"
#include "../datapoollocal/locPoolDefinitions.h"
#include "../events/Event.h"
#include "../modes/HasModesIF.h"
#include "../ipc/MessageQueueSenderIF.h"

View File

@ -0,0 +1 @@
## Controllers

View File

@ -16,9 +16,8 @@ Flight Software Framework
* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call.
A nullptr check of the returning Pointer must be done. This function is based on Run-time type information.
``` c++
template <typename T> T* ObjectManagerIF::get( object_id_t id )
```cpp
template <typename T> T* ObjectManagerIF::get( object_id_t id )
```
* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation.
By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards.

View File

@ -1 +1 @@
## FSFW DeviceHandlers
## Device Handlers

99
doc/README-highlevel.md Normal file
View File

@ -0,0 +1,99 @@
# High-level overview
## Structure
The general structure is driven by the usage of interfaces provided by objects.
The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers.
The FSFW uses dynamic allocation during the initialization but provides static containers during runtime.
This simplifies the instantiation of objects and allows the usage of some standard containers.
Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that.
The fsfw uses run-time type information but exceptions are not allowed.
### Failure Handling
Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong.
Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used.
The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds.
### OSAL
The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS.
The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes.
The [OSAL README](doc/README-osal.md#top) provides more detailed information on provided components and how to use them.
### Core Components
The FSFW has following core components. More detailed informations can be found in the
[core component section](doc/README-core.md#top):
1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots
2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles.
3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without
using dynamic memory allocation. These pools are allocated up front.
3. Clock: This module provided common time related functions
4. EventManager: This module allows routing of events generated by `SystemObjects`
5. HealthTable: A component which stores the health states of objects
### Static IDs in the framework
Some parts of the framework use a static routing address for communication.
An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()".
### Events
Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues.
Every object that needs own EventIds has to get a unique SUBSYSTEM_ID.
Every SystemObject can call triggerEvent from the parent class.
Therefore, event messages contain the specific EventId and the objectId of the object that has triggered.
### Internal Communication
Components communicate mostly over Message through Queues.
Those queues are created by calling the singleton QueueFactory::instance()->create().
### External Communication
The external communication with the mission control system is mostly up to the user implementation.
The FSFW provides PUS Services which can be used to but don't need to be used.
The services can be seen as a conversion from a TC to a message based communication and back.
#### CCSDS Frames, CCSDS Space Packets and PUS
If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution.
If Space Packets are used, a timestamper must be created.
An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short.
#### Device Handlers
DeviceHandlers are another important component of the FSFW.
The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface.
By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware.
The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction.
Device Handlers can be created by overriding `DeviceHandlerBase`.
A standard FDIR component for the DH will be created automatically but can be overwritten by the user.
More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top).
#### Modes, Health
The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components.
On-board Mode Management is implement in hierarchy system.
DeviceHandlers and Controllers are the lowest part of the hierarchy.
The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers.
Assemblies share a common core with the next level which are the Subsystems.
Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes.
The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded.
Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component.
The target table is used to describe the state that is checked continuously by the subsystem.
All of this allows System Modes to be generated as Subsystem object as well from the same database.
This System contains list of subsystem modes in the transition and target tables.
Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded.
The health state represents if the component is able to perform its tasks.
This can be used to signal the system to avoid using this component instead of a redundant one.
The on-board FDIR uses the health state for isolation and recovery.
## Unit Tests
Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. More information on how to run these tests can be found in the separate
[`fsfw_tests` reposoitory](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests)

3
doc/README-localpools.md Normal file
View File

@ -0,0 +1,3 @@
## Local Data Pools

32
doc/README-osal.md Normal file
View File

@ -0,0 +1,32 @@
# Operating System Abstraction Layer (OSAL)
Some specific information on the provided OSALs are provided.
## Linux OSAL
This OSAL can be used to compile for Linux host systems like Ubuntu 20.04 or for
embedded Linux targets like the Raspberry Pi. This OSAL generally requires threading support
and real-time functionalities. For most UNIX systems, this is done by adding `-lrt` and `-lpthread` to the linked libraries in the compilation process. The CMake build support provided will do this automatically for the `fsfw` target. It should be noted that most UNIX systems need to be configured specifically to allow the real-time functionalities required by the FSFW.
More information on how to set up a Linux system accordingly can be found in the
[Linux README of the FSFW example application](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/master/doc/README-linux.md#top)
## Hosted OSAL
This is the newest OSAL. Support for Semaphores has not been implemented yet and will propably be implemented as soon as C++20 with Semaphore support has matured. This OSAL can be used to run the FSFW on any host system, but currently has only been tested on Windows 10 and Ubuntu 20.04. Unlike the other OSALs, it uses dynamic memory allocation (e.g. for the message queue implementation). Cross-platform serial port (USB) support might be added soon.
## FreeRTOS OSAL
FreeRTOS is not included and the developer needs to take care of compiling the FreeRTOS sources and adding the `FreeRTOSConfig.h` file location to the include path. This OSAL has only been tested extensively with the pre-emptive scheduler configuration so far but it should in principle also be possible to use a cooperative scheduler. It is recommended to use the `heap_4` allocation scheme. When using newlib (nano), it is also recommended to add `#define configUSE_NEWLIB_REENTRANT` to the FreeRTOS configuration file to ensure thread-safety.
When using this OSAL, developers also need to provide an implementation for the `vRequestContextSwitchFromISR` function. This has been done because the call to request a context switch from an ISR is generally located in the `portmacro.h` header and is different depending on the target architecture or device.
## RTEMS OSAL
The RTEMS OSAL was the first implemented OSAL which is also used on the active satellite Flying Laptop.
## TCP/IP socket abstraction
The Linux and Host OSAL provide abstraction layers for the socket API. Currently, only UDP sockets have been imlemented. This is very useful to test TMTC handling either on the host computer directly (targeting localhost with a TMTC application) or on embedded Linux devices, sending TMTC packets via Ethernet.

1
doc/README-pus.md Normal file
View File

@ -0,0 +1 @@
## PUS Services

View File

@ -10,6 +10,7 @@ CXXSRC += $(wildcard $(FRAMEWORK_PATH)/coordinates/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datalinklayer/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapool/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoollocal/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/datapoollocal/internal/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/housekeeping/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/devicehandlers/*.cpp)
CXXSRC += $(wildcard $(FRAMEWORK_PATH)/events/*.cpp)

View File

@ -1,5 +1,5 @@
#include "arrayprinter.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
#include <bitset>
void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
@ -9,6 +9,8 @@ void arrayprinter::print(const uint8_t *data, size_t size, OutputType type,
sif::info << "Printing data with size " << size << ": ";
}
sif::info << "[";
#else
sif::printInfo("Printing data with size %zu: [", size);
#endif
if(type == OutputType::HEX) {
arrayprinter::printHex(data, size, maxCharPerLine);
@ -37,6 +39,8 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
}
sif::info << std::dec;
sif::info << "]" << std::endl;
#else
// how much memory to reserve for printout?
#endif
}
@ -54,6 +58,8 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
}
}
sif::info << "]" << std::endl;
#else
// how much memory to reserve for printout?
#endif
}
@ -65,5 +71,7 @@ void arrayprinter::printBin(const uint8_t *data, size_t size) {
std::bitset<8>(data[i]) << ",\n" << std::flush;
}
sif::info << "]" << std::endl;
#else
// how much memory to reserve for printout?
#endif
}

View File

@ -1,7 +1,7 @@
#ifndef FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_
#define FSFW_HOUSEKEEPING_HOUSEKEEPINGMESSAGE_H_
#include "../datapoollocal/locPoolDefinitions.h"
#include "../datapoollocal/localPoolDefinitions.h"
#include "../ipc/CommandMessage.h"
#include "../ipc/FwMessageTypes.h"
#include "../objectmanager/frameworkObjects.h"

View File

@ -21,11 +21,11 @@ public:
InternalErrorDataset(object_id_t objectId):
StaticLocalDataSet(sid_t(objectId , ERROR_SET_ID)) {}
lp_var_t<uint32_t> tmHits = lp_var_t<uint32_t>(hkManager->getOwner(),
lp_var_t<uint32_t> tmHits = lp_var_t<uint32_t>(sid.objectId,
TM_HITS, this);
lp_var_t<uint32_t> queueHits = lp_var_t<uint32_t>(hkManager->getOwner(),
lp_var_t<uint32_t> queueHits = lp_var_t<uint32_t>(sid.objectId,
QUEUE_HITS, this);
lp_var_t<uint32_t> storeHits = lp_var_t<uint32_t>(hkManager->getOwner(),
lp_var_t<uint32_t> storeHits = lp_var_t<uint32_t>(sid.objectId,
STORE_HITS, this);
};

View File

@ -2,7 +2,7 @@
#include "../ipc/QueueFactory.h"
#include "../ipc/MutexFactory.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId,
uint32_t messageQueueDepth): SystemObject(setObjectId),
@ -29,7 +29,7 @@ ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
uint32_t newTmHits = getAndResetTmHits();
uint32_t newStoreHits = getAndResetStoreHits();
#if FSFW_ENHANCED_PRINTOUT == 1
#if FSFW_VERBOSE_LEVEL == 1
if(diagnosticPrintout) {
if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@ -38,6 +38,11 @@ ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
sif::debug << "Queue errors: " << newQueueHits << std::endl;
sif::debug << "TM errors: " << newTmHits << std::endl;
sif::debug << "Store errors: " << newStoreHits << std::endl;
#else
sif::printDebug("InternalErrorReporter::performOperation: Errors occured!\n");
sif::printDebug("Queue errors: %lu\n", static_cast<unsigned int>(newQueueHits));
sif::printDebug("TM errors: %lu\n", static_cast<unsigned int>(newTmHits));
sif::printDebug("Store errors: %lu\n", static_cast<unsigned int>(newStoreHits));
#endif
}
}
@ -134,6 +139,10 @@ uint32_t InternalErrorReporter::getStoreHits() {
return value;
}
AccessPoolManagerIF* InternalErrorReporter::getAccessorHandle() {
return &poolManager;
}
void InternalErrorReporter::incrementStoreHits() {
mutex->lockMutex(timeoutType, timeoutMs);
storeHits++;
@ -149,7 +158,7 @@ MessageQueueId_t InternalErrorReporter::getCommandQueue() const {
}
ReturnValue_t InternalErrorReporter::initializeLocalDataPool(
LocalDataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
localDataPoolMap.emplace(errorPoolIds::TM_HITS,
new PoolEntry<uint32_t>());
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS,
@ -162,10 +171,6 @@ ReturnValue_t InternalErrorReporter::initializeLocalDataPool(
return HasReturnvaluesIF::RETURN_OK;
}
LocalDataPoolManager* InternalErrorReporter::getHkManagerHandle() {
return &poolManager;
}
dur_millis_t InternalErrorReporter::getPeriodicOperationFrequency() const {
return this->executingTask->getPeriodMs();
}
@ -195,3 +200,7 @@ void InternalErrorReporter::setMutexTimeout(MutexIF::TimeoutType timeoutType,
this->timeoutType = timeoutType;
this->timeoutMs = timeoutMs;
}
ProvidesDataPoolSubscriptionIF* InternalErrorReporter::getSubscriptionInterface() {
return &poolManager;
}

View File

@ -41,11 +41,12 @@ public:
virtual object_id_t getObjectId() const override;
virtual MessageQueueId_t getCommandQueue() const override;
virtual ReturnValue_t initializeLocalDataPool(
LocalDataPool& localDataPoolMap,
localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
virtual LocalDataPoolManager* getHkManagerHandle() override;
virtual dur_millis_t getPeriodicOperationFrequency() const override;
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() override;
AccessPoolManagerIF* getAccessorHandle() override;
virtual ReturnValue_t initialize() override;
virtual ReturnValue_t initializeAfterTaskCreation() override;

View File

@ -5,7 +5,7 @@
#include "MonitoringIF.h"
#include "MonitoringMessageContent.h"
#include "../datapoollocal/locPoolDefinitions.h"
#include "../datapoollocal/localPoolDefinitions.h"
#include "../events/EventManagerIF.h"
#include "../parameters/HasParametersIF.h"

View File

@ -1,9 +1,10 @@
#ifndef MONITORINGMESSAGECONTENT_H_
#define MONITORINGMESSAGECONTENT_H_
#ifndef FSFW_MONITORING_MONITORINGMESSAGECONTENT_H_
#define FSFW_MONITORING_MONITORINGMESSAGECONTENT_H_
#include "HasMonitorsIF.h"
#include "MonitoringIF.h"
#include "../datapoollocal/locPoolDefinitions.h"
#include "../datapoollocal/localPoolDefinitions.h"
#include "../objectmanager/ObjectManagerIF.h"
#include "../serialize/SerialBufferAdapter.h"
#include "../serialize/SerialFixedArrayListAdapter.h"
@ -85,4 +86,4 @@ private:
template<typename T>
object_id_t MonitoringReportContent<T>::timeStamperId = 0;
#endif /* MONITORINGMESSAGECONTENT_H_ */
#endif /* FSFW_MONITORING_MONITORINGMESSAGECONTENT_H_ */

View File

@ -22,7 +22,6 @@ public:
std::timed_mutex* getMutexHandle();
private:
//bool locked = false;
std::timed_mutex mutex;
};

View File

@ -1,10 +1,11 @@
#include "BinarySemaphore.h"
#include "../../serviceinterface/ServiceInterfacePrinter.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
extern "C" {
#include <time.h>
#include <errno.h>
#include <string.h>
}
BinarySemaphore::BinarySemaphore() {
// Using unnamed semaphores for now
@ -113,7 +114,8 @@ uint8_t BinarySemaphore::getSemaphoreCounter(sem_t *handle) {
}
else if(result != 0 and errno == EINVAL) {
// Could be called from interrupt, use lightweight printf
printf("BinarySemaphore::getSemaphoreCounter: Invalid semaphore\n");
sif::printError("BinarySemaphore::getSemaphoreCounter: "
"Invalid semaphore\n");
return 0;
}
else {
@ -128,13 +130,17 @@ void BinarySemaphore::initSemaphore(uint8_t initCount) {
switch(errno) {
case(EINVAL):
// Value exceeds SEM_VALUE_MAX
case(ENOSYS):
// System does not support process-shared semaphores
case(ENOSYS): {
// System does not support process-shared semaphores
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "BinarySemaphore: Init failed with" << strerror(errno)
<< std::endl;
sif::error << "BinarySemaphore: Init failed with "
<< strerror(errno) << std::endl;
#else
sif::printError("BinarySemaphore: Init failed with %s\n",
strerror(errno));
#endif
}
}
}
}

View File

@ -1,5 +1,7 @@
#include "../../osal/linux/CountingSemaphore.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
#include <errno.h>
CountingSemaphore::CountingSemaphore(const uint8_t maxCount, uint8_t initCount):
maxCount(maxCount), initCount(initCount) {

View File

@ -1,5 +1,5 @@
#include "MessageQueue.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
#include "../../objectmanager/ObjectManagerIF.h"
#include <fstream>
@ -121,14 +121,16 @@ ReturnValue_t MessageQueue::handleError(mq_attr* attributes,
break;
}
default:
default: {
// Failed either the first time or the second time
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "MessageQueue::MessageQueue: Creating Queue " << std::hex
<< name << std::dec << " failed with status: "
<< strerror(errno) << std::endl;
sif::error << "MessageQueue::MessageQueue: Creating Queue " << name
<< " failed with status: " << strerror(errno) << std::endl;
#else
sif::printError("MessageQueue::MessageQueue: Creating Queue %s"
" failed with status: %s\n", name, strerror(errno));
#endif
}
}
return HasReturnvaluesIF::RETURN_FAILED;

View File

@ -1,6 +1,6 @@
#include "PosixThread.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
#include <cstring>
#include <errno.h>
@ -146,16 +146,22 @@ void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
strerror(status) << std::endl;
#endif
if(errno == ENOMEM) {
uint64_t stackMb = stackSize/10e6;
size_t stackMb = stackSize/10e6;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Insufficient memory for"
" the requested " << stackMb << " MB" << std::endl;
#else
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %zu MB\n", stackMb);
#endif
}
else if(errno == EINVAL) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Wrong alignment argument!"
<< std::endl;
#else
sif::printError("PosixThread::createTask: "
"Wrong alignment argument!\n");
#endif
}
return;

View File

@ -1,6 +1,8 @@
#include "TcUnixUdpPollingTask.h"
#include "../../globalfunctions/arrayprinter.h"
#include <errno.h>
TcUnixUdpPollingTask::TcUnixUdpPollingTask(object_id_t objectId,
object_id_t tmtcUnixUdpBridge, size_t frameSize,
double timeoutSeconds): SystemObject(objectId),

View File

@ -1,5 +1,5 @@
#include "TmTcUnixUdpBridge.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
#include "../../ipc/MutexHelper.h"
#include <errno.h>
@ -188,12 +188,16 @@ void TmTcUnixUdpBridge::handleBindError() {
void TmTcUnixUdpBridge::handleSendError() {
switch(errno) {
default:
default: {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "TmTcUnixBridge::handleSendError: "
<< strerror(errno) << std::endl;
#else
sif::printError("TmTcUnixBridge::handleSendError: %s\n",
strerror(errno));
#endif
}
}
}
void TmTcUnixUdpBridge::setClientAddressToAny(bool ipAddrAnySet){

View File

@ -3,6 +3,7 @@
#include "../osal/Endiness.h"
#include <cstring>
#include <cstdint>
/**
* Helper class to convert variables or bitstreams between machine

View File

@ -2,6 +2,7 @@
#define FSFW_SERVICEINTERFACE_SERVICEINTERFACE_H_
#include <FSFWConfig.h>
#include "serviceInterfaceDefintions.h"
#if FSFW_CPP_OSTREAM_ENABLED == 1
#include <fsfw/serviceinterface/ServiceInterfaceStream.h>

View File

@ -29,19 +29,19 @@ ServiceInterfaceBuffer::ServiceInterfaceBuffer(std::string setMessage,
#if FSFW_COLORED_OUTPUT == 1
if(setMessage.find("DEBUG") != std::string::npos) {
colorPrefix = fsfw::ANSI_COLOR_MAGENTA;
colorPrefix = sif::ANSI_COLOR_MAGENTA;
}
else if(setMessage.find("INFO") != std::string::npos) {
colorPrefix = fsfw::ANSI_COLOR_GREEN;
colorPrefix = sif::ANSI_COLOR_GREEN;
}
else if(setMessage.find("WARNING") != std::string::npos) {
colorPrefix = fsfw::ANSI_COLOR_YELLOW;
colorPrefix = sif::ANSI_COLOR_YELLOW;
}
else if(setMessage.find("ERROR") != std::string::npos) {
colorPrefix = fsfw::ANSI_COLOR_RED;
colorPrefix = sif::ANSI_COLOR_RED;
}
else {
colorPrefix = fsfw::ANSI_COLOR_RESET;
colorPrefix = sif::ANSI_COLOR_RESET;
}
#ifdef WIN32

View File

@ -6,16 +6,18 @@
#include <cstdarg>
#include <cstdint>
fsfw::PrintLevel printLevel = fsfw::PrintLevel::DEBUG;
static sif::PrintLevel printLevel = sif::PrintLevel::DEBUG_LEVEL;
#if defined(WIN32) && FSFW_COLORED_OUTPUT == 1
bool consoleInitialized = false;
static bool consoleInitialized = false;
#endif /* defined(WIN32) && FSFW_COLORED_OUTPUT == 1 */
#if FSFW_DISABLE_PRINTOUT == 0
static bool addCrAtEnd = false;
uint8_t printBuffer[fsfwconfig::FSFW_PRINT_BUFFER_SIZE];
void fsfwPrint(fsfw::PrintLevel printType, const char* fmt, va_list arg) {
void fsfwPrint(sif::PrintLevel printType, const char* fmt, va_list arg) {
#if defined(WIN32) && FSFW_COLORED_OUTPUT == 1
if(not consoleInitialized) {
@ -32,38 +34,38 @@ void fsfwPrint(fsfw::PrintLevel printType, const char* fmt, va_list arg) {
char* bufferPosition = reinterpret_cast<char*>(printBuffer);
/* Check logger level */
if(printType == fsfw::PrintLevel::NONE or printType > printLevel) {
if(printType == sif::PrintLevel::NONE or printType > printLevel) {
return;
}
/* Log message to terminal */
#if FSFW_COLORED_OUTPUT == 1
if(printType == fsfw::PrintLevel::INFO) {
len += sprintf(bufferPosition, fsfw::ANSI_COLOR_GREEN);
if(printType == sif::PrintLevel::INFO_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_GREEN);
}
else if(printType == fsfw::PrintLevel::DEBUG) {
len += sprintf(bufferPosition, fsfw::ANSI_COLOR_MAGENTA);
else if(printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_MAGENTA);
}
else if(printType == fsfw::PrintLevel::WARNING) {
len += sprintf(bufferPosition, fsfw::ANSI_COLOR_YELLOW);
else if(printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_YELLOW);
}
else if(printType == fsfw::PrintLevel::ERROR_TYPE) {
len += sprintf(bufferPosition, fsfw::ANSI_COLOR_RED);
else if(printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition, sif::ANSI_COLOR_RED);
}
#endif
if (printType == fsfw::PrintLevel::INFO) {
if (printType == sif::PrintLevel::INFO_LEVEL) {
len += sprintf(bufferPosition + len, "INFO: ");
}
if(printType == fsfw::PrintLevel::DEBUG) {
if(printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition + len, "DEBUG: ");
}
if(printType == fsfw::PrintLevel::WARNING) {
if(printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition + len, "WARNING: ");
}
if(printType == fsfw::PrintLevel::ERROR_TYPE) {
if(printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition + len, "ERROR: ");
}
@ -80,51 +82,59 @@ void fsfwPrint(fsfw::PrintLevel printType, const char* fmt, va_list arg) {
len += vsnprintf(bufferPosition + len, sizeof(printBuffer) - len, fmt, arg);
if(addCrAtEnd) {
len += sprintf(bufferPosition + len, "\r");
}
printf("%s", printBuffer);
}
void fsfw::printInfo(const char *fmt, ...) {
void sif::printInfo(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fsfwPrint(fsfw::PrintLevel::INFO, fmt, args);
fsfwPrint(sif::PrintLevel::INFO_LEVEL, fmt, args);
va_end(args);
}
void fsfw::printWarning(const char *fmt, ...) {
void sif::printWarning(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fsfwPrint(fsfw::PrintLevel::WARNING, fmt, args);
fsfwPrint(sif::PrintLevel::WARNING_LEVEL, fmt, args);
va_end(args);
}
void fsfw::printDebug(const char *fmt, ...) {
void sif::printDebug(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fsfwPrint(fsfw::PrintLevel::DEBUG, fmt, args);
fsfwPrint(sif::PrintLevel::DEBUG_LEVEL, fmt, args);
va_end(args);
}
void fsfw::printError(const char *fmt, ...) {
void sif::setToAddCrAtEnd(bool addCrAtEnd_) {
addCrAtEnd = addCrAtEnd_;
}
void sif::printError(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
fsfwPrint(fsfw::PrintLevel::ERROR_TYPE, fmt, args);
fsfwPrint(sif::PrintLevel::ERROR_LEVEL, fmt, args);
va_end(args);
}
#else
void fsfw::printInfo(const char *fmt, ...) {}
void fsfw::printWarning(const char *fmt, ...) {}
void fsfw::printDebug(const char *fmt, ...) {}
void fsfw::printError(const char *fmt, ...) {}
void sif::printInfo(const char *fmt, ...) {}
void sif::printWarning(const char *fmt, ...) {}
void sif::printDebug(const char *fmt, ...) {}
void sif::printError(const char *fmt, ...) {}
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
void fsfw::setPrintLevel(PrintLevel printLevel_) {
void sif::setPrintLevel(PrintLevel printLevel_) {
printLevel = printLevel_;
}
fsfw::PrintLevel fsfw::getPrintLevel() {
sif::PrintLevel sif::getPrintLevel() {
return printLevel;
}

View File

@ -1,21 +1,29 @@
#if FSFW_DISABLE_PRINTOUT == 0
#include <stdio.h>
#include <cstdio>
#endif
namespace fsfw {
namespace sif {
enum class PrintLevel {
enum PrintLevel {
NONE = 0,
//! Strange error when using just ERROR..
ERROR_TYPE = 1,
WARNING = 2,
INFO = 3,
DEBUG = 4
ERROR_LEVEL = 1,
WARNING_LEVEL = 2,
INFO_LEVEL = 3,
DEBUG_LEVEL = 4
};
/**
* Set the print level. All print types with a smaller level will be printed
* as well. For example, set to PrintLevel::WARNING to only enable error
* and warning output.
* @param printLevel
*/
void setPrintLevel(PrintLevel printLevel);
PrintLevel getPrintLevel();
void setToAddCrAtEnd(bool addCrAtEnd_);
/**
* These functions can be used like the C stdio printf and forward the
* supplied formatted string arguments to a printf function.

View File

@ -1,7 +1,14 @@
#ifndef FSFW_SERVICEINTERFACE_SERVICEINTERFACEDEFINTIONS_H_
#define FSFW_SERVICEINTERFACE_SERVICEINTERFACEDEFINTIONS_H_
namespace fsfw {
namespace sif {
enum class OutputTypes {
OUT_INFO,
OUT_DEBUG,
OUT_WARNING,
OUT_ERROR
};
static const char* const ANSI_COLOR_RED = "\x1b[31m";
static const char* const ANSI_COLOR_GREEN = "\x1b[32m";

View File

@ -1,5 +1,5 @@
#ifndef PLATFORM_TMTCSERVICES_TMSTOREBACKENDIF_H_
#define PLATFORM_TMTCSERVICES_TMSTOREBACKENDIF_H_
#ifndef FSFW_TMTCSERVICES_TMSTOREBACKENDIF_H_
#define FSFW_TMTCSERVICES_TMSTOREBACKENDIF_H_
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../objectmanager/SystemObjectIF.h"
@ -49,14 +49,33 @@ public:
static const Event AUTO_CATALOGS_SENDING_FAILED = MAKE_EVENT(15, severity::INFO);//!< Info that the a auto catalog report failed
virtual ~TmStoreBackendIF() {}
/**
* SHOULDDO: Specification on what has to be implemented here
* @param opCode
* @return
*/
virtual ReturnValue_t performOperation(uint8_t opCode) = 0;
virtual ReturnValue_t initialize() = 0;
/**
* Implement the storage of TM packets to mass memory
* @param tmPacket
* @return
*/
virtual ReturnValue_t storePacket(TmPacketMinimal* tmPacket) = 0;
virtual ReturnValue_t setFetchLimitTime(const timeval* loverLimit, const timeval* upperLimit) = 0;
virtual ReturnValue_t setFetchLimitBlocks(uint32_t startAddress, uint32_t endAddress) = 0;
virtual ReturnValue_t fetchPackets(bool fromBegin = false) = 0;
virtual ReturnValue_t initializeStore(object_id_t dumpTarget) = 0;
virtual ReturnValue_t dumpIndex(store_address_t* storeId) = 0;
/**
* SHOULDDO: Adapt for file management system?
* @param startAddress
* @param endAddress
* @return
*/
virtual ReturnValue_t deleteBlocks(uint32_t startAddress, uint32_t endAddress) = 0;
virtual ReturnValue_t deleteTime(const timeval* timeUntil,
uint32_t* deletedPackets) = 0;
@ -73,4 +92,4 @@ public:
#endif /* PLATFORM_TMTCSERVICES_TMSTOREBACKENDIF_H_ */
#endif /* FSFW_TMTCSERVICES_TMSTOREBACKENDIF_H_ */

View File

@ -1,9 +1,10 @@
#ifndef PLATFORM_TMTCSERVICES_TMSTOREFRONTENDIF_H_
#define PLATFORM_TMTCSERVICES_TMSTOREFRONTENDIF_H_
#ifndef FSFW_TMTCSERVICES_TMSTOREFRONTENDIF_H_
#define FSFW_TMTCSERVICES_TMSTOREFRONTENDIF_H_
#include "../returnvalues/HasReturnvaluesIF.h"
#include "TmStorePackets.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../ipc/MessageQueueSenderIF.h"
class TmPacketMinimal;
class SpacePacketBase;
class TmStoreBackendIF;
@ -11,6 +12,14 @@ class TmStoreBackendIF;
class TmStoreFrontendIF {
public:
virtual TmStoreBackendIF* getBackend() const = 0;
/**
* What do I need to implement here?
* This is propably used by PUS Service 15 so we should propably check for messages..
* Provide base implementation?
* @param opCode
* @return
*/
virtual ReturnValue_t performOperation(uint8_t opCode) = 0;
/**
* Callback from the back-end to indicate a certain packet was received.
@ -52,4 +61,4 @@ public:
#endif /* PLATFORM_TMTCSERVICES_TMSTOREFRONTENDIF_H_ */
#endif /* FSFW_TMTCSERVICES_TMSTOREFRONTENDIF_H_ */

View File

@ -1,5 +1,5 @@
#include "../objectmanager/ObjectManagerIF.h"
#include "TmStoreMessage.h"
#include "../objectmanager/ObjectManagerIF.h"
TmStoreMessage::~TmStoreMessage() {
@ -74,7 +74,7 @@ void TmStoreMessage::clear(CommandMessage* cmd) {
case DELETE_STORE_CONTENT_BLOCKS:
case DOWNLINK_STORE_CONTENT_BLOCKS:
case REPORT_INDEX_REQUEST:
cmd->setCommand(UNKNOWN_COMMAND);
cmd->setCommand(CommandMessage::UNKNOWN_COMMAND);
cmd->setParameter(0);
cmd->setParameter2(0);
break;

View File

@ -1,11 +1,12 @@
#ifndef FRAMEWORK_TMSTORAGE_TMSTOREMESSAGE_H_
#define FRAMEWORK_TMSTORAGE_TMSTOREMESSAGE_H_
#ifndef FSFW_TMSTORAGE_TMSTOREMESSAGE_H_
#define FSFW_TMSTORAGE_TMSTOREMESSAGE_H_
#include "TmStorePackets.h"
#include "../ipc/CommandMessage.h"
#include "../storagemanager/StorageManagerIF.h"
#include "TmStorePackets.h"
#include "../objectmanager/SystemObjectIF.h"
class TmStoreMessage: public CommandMessage {
class TmStoreMessage {
public:
static ReturnValue_t setEnableStoringMessage(CommandMessage* cmd,
bool setEnabled);
@ -25,8 +26,10 @@ public:
static void setDownlinkContentTimeMessage(CommandMessage* cmd,
store_address_t storeId);
static void setIndexReportMessage(CommandMessage* cmd, store_address_t storeId);
static ReturnValue_t setDeleteBlocksMessage(CommandMessage* cmd, uint32_t addressLow, uint32_t addressHigh);
static ReturnValue_t setDownlinkBlocksMessage(CommandMessage* cmd, uint32_t addressLow, uint32_t addressHigh);
static ReturnValue_t setDeleteBlocksMessage(CommandMessage* cmd,
uint32_t addressLow, uint32_t addressHigh);
static ReturnValue_t setDownlinkBlocksMessage(CommandMessage* cmd,
uint32_t addressLow, uint32_t addressHigh);
static ReturnValue_t setIndexRequestMessage(CommandMessage* cmd);
static void setDeleteContentTimeMessage(CommandMessage* cmd,
store_address_t storeId);
@ -60,4 +63,4 @@ private:
TmStoreMessage();
};
#endif /* FRAMEWORK_TMSTORAGE_TMSTOREMESSAGE_H_ */
#endif /* FSFW_TMSTORAGE_TMSTOREMESSAGE_H_ */

View File

@ -1,5 +1,5 @@
#ifndef FRAMEWORK_TMSTORAGE_TMSTOREPACKETS_H_
#define FRAMEWORK_TMSTORAGE_TMSTOREPACKETS_H_
#ifndef FSFW_TMSTORAGE_TMSTOREPACKETS_H_
#define FSFW_TMSTORAGE_TMSTOREPACKETS_H_
#include "../serialize/SerialFixedArrayListAdapter.h"
#include "../serialize/SerializeElement.h"
@ -140,8 +140,8 @@ public:
if(packet->isValid()){
timeval packetTime = {0,0};
size_t foundlen = 0;
CCSDSTime::convertFromCcsds(&packetTime,
&packet->rawTimestamp[0],&foundlen,sizeof(rawTimestamp));
CCSDSTime::convertFromCcsds(&packetTime,&packet->rawTimestamp[0],
&foundlen,sizeof(rawTimestamp));
if(packetTime <= *cmpTime){
return true;
}
@ -154,7 +154,7 @@ public:
timeval packetTime = {0,0};
size_t foundlen = 0;
CCSDSTime::convertFromCcsds(&packetTime,&packet->rawTimestamp[0],
&foundlen,sizeof(rawTimestamp));
&foundlen,sizeof(rawTimestamp));
if(packetTime >= *cmpTime){
return true;
}
@ -208,7 +208,7 @@ public:
timeval packetTime = {0,0};
size_t foundlen = 0;
CCSDSTime::convertFromCcsds(&packetTime, &this->rawTimestamp[0],
&foundlen,sizeof(rawTimestamp));
&foundlen, sizeof(rawTimestamp));
return packetTime;
}
@ -300,4 +300,4 @@ private:
uint8_t rawTimestamp[TimeStamperIF::MISSION_TIMESTAMP_SIZE];
};
#endif /* FRAMEWORK_TMSTORAGE_TMSTOREPACKETS_H_ */
#endif /* FSFW_TMSTORAGE_TMSTOREPACKETS_H_ */

View File

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

View File

@ -4,6 +4,7 @@
#include <fsfw/action/ActionHelper.h>
#include <fsfw/ipc/CommandMessage.h>
#include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h>
#include <catch2/catch_test_macros.hpp>

View File

@ -48,85 +48,4 @@ public:
};
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( MessageQueueMessageIF* message ) {
messageSent = true;
lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return HasReturnvaluesIF::RETURN_OK;
};
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t *receivedFrom) {
(*message) = lastMessage;
lastMessage.clear();
return HasReturnvaluesIF::RETURN_OK;
}
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) {
memcpy(message->getBuffer(), lastMessage.getBuffer(),
message->getMessageSize());
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,
MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
bool ignoreFault = false ) {
messageSent = true;
lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return HasReturnvaluesIF::RETURN_OK;
}
virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo,
MessageQueueMessageIF* message, bool ignoreFault = false ) override {
messageSent = true;
lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return HasReturnvaluesIF::RETURN_OK;
}
virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false ) {
messageSent = true;
lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return HasReturnvaluesIF::RETURN_OK;
}
virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) {
messageSent = true;
lastMessage = *(dynamic_cast<MessageQueueMessage*>(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

@ -1,3 +1,6 @@
target_sources(${TARGET_NAME} PRIVATE
LocalPoolVariableTest.cpp
LocalPoolVectorTest.cpp
DataSetTest.cpp
LocalPoolManagerTest.cpp
)

View File

@ -2,6 +2,7 @@
#include <catch2/catch_test_macros.hpp>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
@ -11,9 +12,10 @@ TEST_CASE("LocalDataSet" , "[LocDataSetTest]") {
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK);
const uint32_t setId = 0;
SECTION("BasicTest") {
//StaticLocalDataSet<3> localSet = StaticLocalDataSet<3>()
StaticLocalDataSet<3> localSet = StaticLocalDataSet<3>(
sid_t(objects::TEST_LOCAL_POOL_OWNER_BASE, setId));
}
}

View File

@ -0,0 +1,122 @@
#include "LocalPoolOwnerBase.h"
#include <catch2/catch_test_macros.hpp>
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/ipc/CommandMessageCleaner.h>
#include <unittest/core/CatchDefinitions.h>
TEST_CASE("LocalPoolManagerTest" , "[LocManTest]") {
LocalPoolOwnerBase* poolOwner = objectManager->
get<LocalPoolOwnerBase>(objects::TEST_LOCAL_POOL_OWNER_BASE);
REQUIRE(poolOwner != nullptr);
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK);
REQUIRE(poolOwner->dataset.assignPointers() == retval::CATCH_OK);
MessageQueueMockBase* mqMock = poolOwner->getMockQueueHandle();
REQUIRE(mqMock != nullptr);
CommandMessage messageSent;
uint8_t messagesSent = 0;
SECTION("BasicTest") {
// Subscribe for message generation on update.
REQUIRE(poolOwner->subscribeWrapperSetUpdate() == retval::CATCH_OK);
// Subscribe for an update message.
poolOwner->dataset.setChanged(true);
// Now the update message should be generated.
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(mqMock->wasMessageSent() == true);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
// Should have been reset.
CHECK(poolOwner->dataset.hasChanged() == false);
// Set changed again, result should be the same.
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
// 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.
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
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)
CommandMessageCleaner::clearCommandMessage(&messageSent);
}
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());
REQUIRE(poolOwner->subscribeWrapperVariableUpdate(lpool::uint8VarId) ==
retval::CATCH_OK);
lp_var_t<uint8_t>* poolVar = dynamic_cast<lp_var_t<uint8_t>*>(
poolOwner->getPoolObjectHandle(lpool::uint8VarId));
REQUIRE(poolVar != nullptr);
poolVar->setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// Check update notification was sent.
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 1);
// 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);
REQUIRE(poolOwner->subscribeWrapperSetUpdateHk() == retval::CATCH_OK);
poolOwner->dataset.setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// now two messages should be sent.
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 2);
mqMock->clearMessages(true);
poolOwner->dataset.setChanged(true);
poolVar->setChanged(true);
REQUIRE(poolOwner->poolManager.performHkOperation() == retval::CATCH_OK);
// now three messages should be sent.
REQUIRE(mqMock->wasMessageSent(&messagesSent) == true);
CHECK(messagesSent == 3);
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE));
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::UPDATE_NOTIFICATION_SET));
REQUIRE(mqMock->receiveMessage(&messageSent) == retval::CATCH_OK);
CHECK(messageSent.getCommand() == static_cast<int>(
HousekeepingMessage::HK_REPORT));
CommandMessageCleaner::clearCommandMessage(&messageSent);
REQUIRE(mqMock->receiveMessage(&messageSent) ==
static_cast<int>(MessageQueueIF::EMPTY));
}
}

View File

@ -2,11 +2,14 @@
#define FSFW_UNITTEST_TESTS_DATAPOOLLOCAL_LOCALPOOLOWNERBASE_H_
#include <fsfw/datapoollocal/HasLocalDataPoolIF.h>
#include <fsfw/datapoollocal/LocalDataSet.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <fsfw/datapoollocal/LocalPoolVariable.h>
#include <fsfw/datapoollocal/LocalPoolVector.h>
#include <fsfw/ipc/QueueFactory.h>
#include <testcfg/objects/systemObjectList.h>
#include <fsfw/datapoollocal/StaticLocalDataSet.h>
#include <fsfw/unittest/tests/mocks/MessageQueueMockBase.h>
namespace lpool {
static constexpr lp_id_t uint8VarId = 0;
@ -14,16 +17,48 @@ static constexpr lp_id_t floatVarId = 1;
static constexpr lp_id_t uint32VarId = 2;
static constexpr lp_id_t uint16Vec3Id = 3;
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);
}
class LocalPoolTestDataSet: public LocalDataSet {
public:
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;
}
lp_var_t<uint8_t>* localPoolVarUint8 = nullptr;
lp_var_t<float>* localPoolVarFloat = nullptr;
lp_vec_t<uint16_t, 3>* localPoolUint16Vec = nullptr;
private:
};
class LocalPoolOwnerBase: public SystemObject, public HasLocalDataPoolIF {
public:
LocalPoolOwnerBase(
object_id_t objectId = objects::TEST_LOCAL_POOL_OWNER_BASE):
SystemObject(objectId), hkManager(this, messageQueue) {
messageQueue = QueueFactory::instance()->createMessageQueue(10);
SystemObject(objectId), hkManager(this, messageQueue),
dataset(this, lpool::testSetId) {
messageQueue = new MessageQueueMockBase();
}
~LocalPoolOwnerBase() {
@ -57,7 +92,7 @@ public:
// This is called by initializeAfterTaskCreation of the HK manager.
virtual ReturnValue_t initializeLocalDataPool(
LocalDataPool& localDataPoolMap,
localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) {
// Default initialization empty for now.
localDataPoolMap.emplace(lpool::uint8VarId,
@ -74,7 +109,16 @@ public:
return HasReturnvaluesIF::RETURN_OK;
}
LocalDataPoolManager* getHkManagerHandle() override {
/**
* This function can be used by data pool consumers to retrieve a handle
* which allows subscriptions to dataset and variable updates.
* @return
*/
virtual ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() override {
return &hkManager;
}
virtual AccessPoolManagerIF* getAccessorHandle() override {
return &hkManager;
}
@ -89,22 +133,70 @@ public:
* @return
*/
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override {
// empty for now
return nullptr;
return &dataset;
}
virtual LocalPoolObjectBase* getPoolObjectHandle(
lp_id_t localPoolId) override {
if(localPoolId == lpool::uint8VarId) {
return &testUint8;
}
else if(localPoolId == lpool::uint16Vec3Id) {
return &testUint16Vec;
}
else if(localPoolId == lpool::floatVarId) {
return &testFloat;
}
else if(localPoolId == lpool::int64Vec2Id) {
return &testInt64Vec;
}
else if(localPoolId == lpool::uint32VarId) {
return &testUint32;
}
else {
return &testUint8;
}
}
MessageQueueMockBase* getMockQueueHandle() const {
return dynamic_cast<MessageQueueMockBase*>(messageQueue);
}
ReturnValue_t subscribeWrapperSetUpdate() {
return hkManager.subscribeForSetUpdateMessages(lpool::testSetId,
objects::NO_OBJECT, MessageQueueIF::NO_QUEUE, false);
}
ReturnValue_t subscribeWrapperSetUpdateHk(bool diagnostics = false) {
return hkManager.subscribeForUpdatePackets(lpool::testSid, diagnostics,
false, objects::HK_RECEIVER_MOCK);
}
ReturnValue_t subscribeWrapperVariableUpdate(lp_id_t localPoolId) {
return hkManager.subscribeForVariableUpdateMessages(localPoolId,
MessageQueueIF::NO_QUEUE, objects::NO_OBJECT, false);
}
void resetSubscriptionList() {
hkManager.clearReceiversList();
}
LocalDataPoolManager hkManager;
LocalPoolTestDataSet dataset;
private:
lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId);
lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId);
lp_var_t<uint8_t> testUint8 = lp_var_t<uint8_t>(this, lpool::uint8VarId,
&dataset);
lp_var_t<float> testFloat = lp_var_t<float>(this, lpool::floatVarId,
&dataset);
lp_var_t<uint32_t> testUint32 = lp_var_t<uint32_t>(this, lpool::uint32VarId);
lp_vec_t<uint16_t, 3> testUint16Vec = lp_vec_t<uint16_t, 3>(this,
lpool::uint16Vec3Id);
lpool::uint16Vec3Id, &dataset);
lp_vec_t<int64_t, 2> testInt64Vec = lp_vec_t<int64_t, 2>(this,
lpool::int64Vec2Id);
MessageQueueIF* messageQueue = nullptr;
LocalDataPoolManager hkManager;
bool initialized = false;
bool initializedAfterTaskCreation = false;

View File

@ -23,10 +23,10 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
REQUIRE(testVariable.commit() == retval::CATCH_OK);
REQUIRE(testVariable.read() == retval::CATCH_OK);
REQUIRE(testVariable.value == 5);
CHECK(not testVariable.isValid());
testVariable.setValid(true);
CHECK(testVariable.isValid());
CHECK(testVariable.commit(true) == retval::CATCH_OK);
testVariable.setReadWriteMode(pool_rwm_t::VAR_READ);
CHECK(testVariable.getReadWriteMode() == pool_rwm_t::VAR_READ);
@ -81,15 +81,15 @@ TEST_CASE("LocalPoolVariable" , "[LocPoolVarTest]") {
lp_var_t<uint8_t> invalidVariable = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, 0xffffffff);
REQUIRE(invalidVariable.read() ==
static_cast<int>(HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND));
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
REQUIRE(invalidVariable.commit() ==
static_cast<int>(HasLocalDataPoolIF::POOL_ENTRY_NOT_FOUND));
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
// 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() ==
static_cast<int>(HasLocalDataPoolIF::POOL_ENTRY_TYPE_CONFLICT));
static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
lp_var_t<uint8_t> readOnlyVar = lp_var_t<uint8_t>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint8VarId,

View File

@ -11,6 +11,110 @@ TEST_CASE("LocalPoolVector" , "[LocPoolVecTest]") {
REQUIRE(poolOwner->initializeHkManager() == retval::CATCH_OK);
REQUIRE(poolOwner->initializeHkManagerAfterTaskCreation()
== retval::CATCH_OK);
SECTION("BasicTest") {
// very basic test.
lp_vec_t<uint16_t, 3> testVector = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id);
REQUIRE(testVector.read() == retval::CATCH_OK);
testVector.value[0] = 5;
testVector.value[1] = 232;
testVector.value[2] = 32023;
REQUIRE(testVector.commit(true) == retval::CATCH_OK);
CHECK(testVector.isValid());
testVector.value[0] = 0;
testVector.value[1] = 0;
testVector.value[2] = 0;
CHECK(testVector.read() == retval::CATCH_OK);
CHECK(testVector.value[0] == 5);
CHECK(testVector.value[1] == 232);
CHECK(testVector.value[2] == 32023);
CHECK(testVector[0] == 5);
// 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.
const lp_vec_t<uint16_t, 3>& roTestVec = testVector;
uint16_t valueOne = roTestVec[0];
CHECK(valueOne == 5);
uint16_t lastVal = roTestVec[25];
CHECK(lastVal == 12);
size_t maxSize = testVector.getSerializedSize();
CHECK(maxSize == 6);
uint16_t serializedVector[3];
uint8_t* vecPtr = reinterpret_cast<uint8_t*>(serializedVector);
size_t serSize = 0;
REQUIRE(testVector.serialize(&vecPtr, &serSize,
maxSize, SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(serSize == 6);
CHECK(serializedVector[0] == 5);
CHECK(serializedVector[1] == 232);
CHECK(serializedVector[2] == 12);
maxSize = 1;
REQUIRE(testVector.serialize(&vecPtr, &serSize,
maxSize, SerializeIF::Endianness::MACHINE) ==
static_cast<int>(SerializeIF::BUFFER_TOO_SHORT));
serializedVector[0] = 16;
serializedVector[1] = 7832;
serializedVector[2] = 39232;
const uint8_t* constVecPtr = reinterpret_cast<const uint8_t*>(
serializedVector);
REQUIRE(testVector.deSerialize(&constVecPtr, &serSize,
SerializeIF::Endianness::MACHINE) == retval::CATCH_OK);
CHECK(testVector[0] == 16);
CHECK(testVector[1] == 7832);
CHECK(testVector[2] == 39232);
serSize = 1;
REQUIRE(testVector.deSerialize(&constVecPtr, &serSize,
SerializeIF::Endianness::MACHINE) ==
static_cast<int>(SerializeIF::STREAM_TOO_SHORT));
}
SECTION("ErrorHandling") {
// not 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() ==
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
REQUIRE(invalidVector.commit() ==
static_cast<int>(localpool::POOL_ENTRY_NOT_FOUND));
// 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() ==
static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
REQUIRE(invalidVector2.commit() ==
static_cast<int>(localpool::POOL_ENTRY_TYPE_CONFLICT));
lp_vec_t<uint16_t, 3> writeOnlyVec = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id,
nullptr, pool_rwm_t::VAR_WRITE);
REQUIRE(writeOnlyVec.read() ==
static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
lp_vec_t<uint16_t, 3> readOnlyVec = lp_vec_t<uint16_t, 3>(
objects::TEST_LOCAL_POOL_OWNER_BASE, lpool::uint16Vec3Id,
nullptr, pool_rwm_t::VAR_READ);
REQUIRE(readOnlyVec.commit() ==
static_cast<int>(PoolVariableIF::INVALID_READ_WRITE_MODE));
}
}

View File

@ -0,0 +1,20 @@
#ifndef FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_
#define FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_
#include <fsfw/housekeeping/AcceptsHkPacketsIF.h>
#include <fsfw/objectmanager/SystemObject.h>
class HkReceiverMock: public SystemObject, public AcceptsHkPacketsIF {
public:
HkReceiverMock(object_id_t objectId): SystemObject(objectId) {
}
MessageQueueId_t getHkQueue() const {
return MessageQueueIF::NO_QUEUE;
}
};
#endif /* FSFW_UNITTEST_TESTS_MOCKS_HKRECEIVERMOCK_H_ */

View File

@ -0,0 +1,121 @@
#ifndef FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_
#define FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/MessageQueueMessage.h>
#include <unittest/core/CatchDefinitions.h>
#include <cstring>
#include <queue>
class MessageQueueMockBase: public MessageQueueIF {
public:
MessageQueueId_t myQueueId = tconst::testQueueId;
uint8_t messageSentCounter = 0;
bool defaultDestSet = false;
bool messageSent = false;
bool wasMessageSent(uint8_t* messageSentCounter = nullptr,
bool resetCounter = true) {
bool tempMessageSent = messageSent;
messageSent = false;
if(messageSentCounter != nullptr) {
*messageSentCounter = this->messageSentCounter;
}
if(resetCounter) {
this->messageSentCounter = 0;
}
return tempMessageSent;
}
virtual ReturnValue_t reply( MessageQueueMessageIF* message ) {
//messageSent = true;
//lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return sendMessage(myQueueId, message);
return HasReturnvaluesIF::RETURN_OK;
};
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message,
MessageQueueId_t *receivedFrom) {
return receiveMessage(message);
}
virtual ReturnValue_t receiveMessage(MessageQueueMessageIF* message) {
if(messagesSentQueue.empty()) {
return MessageQueueIF::EMPTY;
}
std::memcpy(message->getBuffer(), messagesSentQueue.front().getBuffer(),
message->getMessageSize());
messagesSentQueue.pop();
return HasReturnvaluesIF::RETURN_OK;
}
virtual ReturnValue_t flush(uint32_t* count) {
return HasReturnvaluesIF::RETURN_OK;
}
virtual MessageQueueId_t getLastPartner() const {
return myQueueId;
}
virtual MessageQueueId_t getId() const {
return myQueueId;
}
virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo,
MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
bool ignoreFault = false ) {
//messageSent = true;
//lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
//return HasReturnvaluesIF::RETURN_OK;
return sendMessage(sendTo, message);
}
virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message,
MessageQueueId_t sentFrom, bool ignoreFault = false ) {
//messageSent = true;
//lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
//return HasReturnvaluesIF::RETURN_OK;
return sendMessage(myQueueId, message);
}
virtual ReturnValue_t sendToDefault( MessageQueueMessageIF* message ) {
//messageSent = true;
//lastMessage = *(dynamic_cast<MessageQueueMessage*>(message));
return sendMessage(myQueueId, message);
}
virtual ReturnValue_t sendMessage( MessageQueueId_t sendTo,
MessageQueueMessageIF* message, bool ignoreFault = false ) override {
messageSent = true;
messageSentCounter++;
MessageQueueMessage& messageRef = *(
dynamic_cast<MessageQueueMessage*>(message));
messagesSentQueue.push(messageRef);
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;
}
void clearMessages(bool clearCommandMessages = true) {
while(not messagesSentQueue.empty()) {
if(clearCommandMessages) {
CommandMessage message;
std::memcpy(message.getBuffer(),
messagesSentQueue.front().getBuffer(),
message.getMessageSize());
message.clear();
}
messagesSentQueue.pop();
}
}
private:
std::queue<MessageQueueMessage> messagesSentQueue;
//MessageQueueMessage lastMessage;
};
#endif /* FSFW_UNITTEST_TESTS_MOCKS_MESSAGEQUEUEMOCKBASE_H_ */

View File

@ -9,18 +9,14 @@
//! the C stdio functions can be used alternatively
#define FSFW_CPP_OSTREAM_ENABLED 1
//! More FSFW related printouts.
//! Be careful, this also turns off most diagnostic prinouts!
#define FSFW_ENHANCED_PRINTOUT 0
//! More FSFW related printouts depending on level. Useful for development.
#define FSFW_VERBOSE_LEVEL 1
//! Can be used to completely disable printouts, even the C stdio ones.
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_ENHANCED_PRINTOUT == 0
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0
#define FSFW_DISABLE_PRINTOUT 0
#endif
//! Can be used to enable additional debugging printouts for developing the FSFW
#define FSFW_PRINT_VERBOSITY_LEVEL 0
//! Can be used to disable the ANSI color sequences for C stdio.
#define FSFW_COLORED_OUTPUT 1

View File

@ -21,6 +21,7 @@ namespace objects {
TEST_ECHO_COM_IF = 20,
TEST_DEVICE = 21,
HK_RECEIVER_MOCK = 22,
TEST_LOCAL_POOL_OWNER_BASE = 25
};
}

View File

@ -0,0 +1 @@
add_subdirectory(core)

View File

@ -9,8 +9,8 @@ StorageManagerIF* tglob::getIpcStoreHandle() {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Global object manager uninitialized" << std::endl;
#else
fsfw::printError("Global object manager uninitialized");
#endif
sif::printError("Global object manager uninitialized\n\r");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
return nullptr;
}
}

View File

@ -10,6 +10,8 @@
#include <fsfw/tmtcpacket/pus/TmPacketStored.h>
#include <fsfw/tmtcservices/CommandingServiceBase.h>
#include <fsfw/tmtcservices/PusServiceBase.h>
#include <fsfw/unittest/tests/datapoollocal/LocalPoolOwnerBase.h>
#include <fsfw/unittest/tests/mocks/HkReceiverMock.h>
/**
* @brief Produces system objects.
@ -30,6 +32,9 @@ void Factory::produce(void) {
new HealthTable(objects::HEALTH_TABLE);
new InternalErrorReporter(objects::INTERNAL_ERROR_REPORTER);
new LocalPoolOwnerBase (objects::TEST_LOCAL_POOL_OWNER_BASE);
new HkReceiverMock(objects::HK_RECEIVER_MOCK);
{
PoolManager::LocalPoolConfig poolCfg = {
{100, 16}, {50, 32}, {25, 64} , {15, 128}, {5, 1024}
@ -65,7 +70,7 @@ void Factory::setStaticFrameworkObjectIds() {
DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT;
DeviceHandlerBase::rawDataReceiverId = objects::PUS_SERVICE_2_DEVICE_ACCESS;
LocalDataPoolManager::defaultHkDestination = objects::NO_OBJECT;
LocalDataPoolManager::defaultHkDestination = objects::HK_RECEIVER_MOCK;
DeviceHandlerFailureIsolation::powerConfirmationId = objects::NO_OBJECT;