Merge branch 'mueller/master' into eive/develop
This commit is contained in:
commit
e9adaf672f
@ -1,5 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
|
option(FSFW_GENERATE_SECTIONS
|
||||||
|
"Generate function and data sections. Required to remove unused code" ON
|
||||||
|
)
|
||||||
|
|
||||||
|
if(FSFW_GENERATE_SECTIONS)
|
||||||
|
option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON)
|
||||||
|
endif()
|
||||||
|
|
||||||
option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
|
option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)
|
||||||
# Options to exclude parts of the FSFW from compilation.
|
# Options to exclude parts of the FSFW from compilation.
|
||||||
option(FSFW_USE_RMAP "Compile with RMAP" ON)
|
option(FSFW_USE_RMAP "Compile with RMAP" ON)
|
||||||
@ -26,15 +34,22 @@ if(NOT OS_FSFW)
|
|||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
set(FSFW_OSAL_DEFINITION FSFW_HOST)
|
||||||
|
|
||||||
if(${OS_FSFW} STREQUAL host)
|
if(${OS_FSFW} STREQUAL host)
|
||||||
set(OS_FSFW_NAME "Host")
|
set(OS_FSFW_NAME "Host")
|
||||||
elseif(${OS_FSFW} STREQUAL linux)
|
elseif(${OS_FSFW} STREQUAL linux)
|
||||||
set(OS_FSFW_NAME "Linux")
|
set(OS_FSFW_NAME "Linux")
|
||||||
|
set(FSFW_OSAL_DEFINITION FSFW_LINUX)
|
||||||
elseif(${OS_FSFW} STREQUAL freertos)
|
elseif(${OS_FSFW} STREQUAL freertos)
|
||||||
set(OS_FSFW_NAME "FreeRTOS")
|
set(OS_FSFW_NAME "FreeRTOS")
|
||||||
target_link_libraries(${LIB_FSFW_NAME} ${LIB_OS_NAME})
|
set(FSFW_OSAL_DEFINITION FSFW_FREERTOS)
|
||||||
|
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
${LIB_OS_NAME}
|
||||||
|
)
|
||||||
elseif(${OS_FSFW} STREQUAL rtems)
|
elseif(${OS_FSFW} STREQUAL rtems)
|
||||||
set(OS_FSFW_NAME "RTEMS")
|
set(OS_FSFW_NAME "RTEMS")
|
||||||
|
set(FSFW_OSAL_DEFINITION FSFW_RTEMS)
|
||||||
else()
|
else()
|
||||||
message(WARNING
|
message(WARNING
|
||||||
"Invalid operating system for FSFW specified! Setting to host.."
|
"Invalid operating system for FSFW specified! Setting to host.."
|
||||||
@ -43,6 +58,14 @@ else()
|
|||||||
set(OS_FSFW "host")
|
set(OS_FSFW "host")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
target_compile_definitions(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
${FSFW_OSAL_DEFINITION}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(${LIB_FSFW_NAME} INTERFACE
|
||||||
|
${FSFW_OSAL_DEFINITION}
|
||||||
|
)
|
||||||
|
|
||||||
message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.")
|
message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.")
|
||||||
|
|
||||||
add_subdirectory(action)
|
add_subdirectory(action)
|
||||||
@ -88,6 +111,7 @@ add_subdirectory(timemanager)
|
|||||||
add_subdirectory(tmstorage)
|
add_subdirectory(tmstorage)
|
||||||
add_subdirectory(tmtcpacket)
|
add_subdirectory(tmtcpacket)
|
||||||
add_subdirectory(tmtcservices)
|
add_subdirectory(tmtcservices)
|
||||||
|
add_subdirectory(unittest)
|
||||||
|
|
||||||
# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it.
|
# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it.
|
||||||
# If this is not given, we include the default configuration and emit a warning.
|
# If this is not given, we include the default configuration and emit a warning.
|
||||||
@ -107,6 +131,21 @@ else()
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
foreach(INCLUDE_PATH ${FSFW_ADDITIONAL_INC_PATH})
|
||||||
|
if(IS_ABSOLUTE ${INCLUDE_PATH})
|
||||||
|
set(CURR_ABS_INC_PATH "${FREERTOS_PATH}")
|
||||||
|
else()
|
||||||
|
get_filename_component(CURR_ABS_INC_PATH
|
||||||
|
${INCLUDE_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_VERBOSE)
|
||||||
|
message(STATUS "FSFW include path: ${CURR_ABS_INC_PATH}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
list(APPEND FSFW_ADD_INC_PATHS_ABS ${CURR_ABS_INC_PATH})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||||
if(NOT DEFINED FSFW_WARNING_FLAGS)
|
if(NOT DEFINED FSFW_WARNING_FLAGS)
|
||||||
set(FSFW_WARNING_FLAGS
|
set(FSFW_WARNING_FLAGS
|
||||||
@ -117,7 +156,20 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|||||||
-Wno-psabi
|
-Wno-psabi
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(FSFW_GENERATE_SECTIONS)
|
||||||
|
target_compile_options(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
"-ffunction-sections"
|
||||||
|
"-fdata-sections"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(FSFW_REMOVE_UNUSED_CODE)
|
||||||
|
target_link_options(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
"Wl,--gc-sections"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
if(FSFW_WARNING_SHADOW_LOCAL_GCC)
|
if(FSFW_WARNING_SHADOW_LOCAL_GCC)
|
||||||
list(APPEND WARNING_FLAGS "-Wshadow=local")
|
list(APPEND WARNING_FLAGS "-Wshadow=local")
|
||||||
endif()
|
endif()
|
||||||
@ -132,6 +184,7 @@ endif()
|
|||||||
target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
||||||
${CMAKE_SOURCE_DIR}
|
${CMAKE_SOURCE_DIR}
|
||||||
${FSFW_CONFIG_PATH_ABSOLUTE}
|
${FSFW_CONFIG_PATH_ABSOLUTE}
|
||||||
|
${FSFW_ADD_INC_PATHS_ABS}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Includes path required to compile FSFW itself as well
|
# Includes path required to compile FSFW itself as well
|
||||||
@ -140,9 +193,14 @@ target_include_directories(${LIB_FSFW_NAME} INTERFACE
|
|||||||
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
target_include_directories(${LIB_FSFW_NAME} PRIVATE
|
||||||
${CMAKE_SOURCE_DIR}
|
${CMAKE_SOURCE_DIR}
|
||||||
${FSFW_CONFIG_PATH_ABSOLUTE}
|
${FSFW_CONFIG_PATH_ABSOLUTE}
|
||||||
|
${FSFW_ADD_INC_PATHS_ABS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_compile_options(${LIB_FSFW_NAME} PRIVATE
|
target_compile_options(${LIB_FSFW_NAME} PRIVATE
|
||||||
${FSFW_WARNING_FLAGS}
|
${FSFW_WARNING_FLAGS}
|
||||||
${COMPILER_FLAGS}
|
${COMPILER_FLAGS}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
${FSFW_ADDITIONAL_LINK_LIBS}
|
||||||
|
)
|
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
const char* const FSFW_VERSION_NAME = "ASTP";
|
const char* const FSFW_VERSION_NAME = "ASTP";
|
||||||
|
|
||||||
#define FSFW_VERSION 0
|
#define FSFW_VERSION 1
|
||||||
#define FSFW_SUBVERSION 0
|
#define FSFW_SUBVERSION 0
|
||||||
#define FSFW_REVISION 1
|
#define FSFW_REVISION 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
11
README.md
11
README.md
@ -38,11 +38,12 @@ a starting point. The [configuration section](doc/README-config.md#top) provides
|
|||||||
|
|
||||||
[1. High-level overview](doc/README-highlevel.md#top) <br>
|
[1. High-level overview](doc/README-highlevel.md#top) <br>
|
||||||
[2. Core components](doc/README-core.md#top) <br>
|
[2. Core components](doc/README-core.md#top) <br>
|
||||||
[3. OSAL overview](doc/README-osal.md#top) <br>
|
[3. Configuration](doc/README-config.md#top) <br>
|
||||||
[4. PUS services](doc/README-pus.md#top) <br>
|
[4. OSAL overview](doc/README-osal.md#top) <br>
|
||||||
[5. Device Handler overview](doc/README-devicehandlers.md#top) <br>
|
[5. PUS services](doc/README-pus.md#top) <br>
|
||||||
[6. Controller overview](doc/README-controllers.md#top) <br>
|
[6. Device Handler overview](doc/README-devicehandlers.md#top) <br>
|
||||||
[7. Local Data Pools](doc/README-localpools.md#top) <br>
|
[7. Controller overview](doc/README-controllers.md#top) <br>
|
||||||
|
[8. Local Data Pools](doc/README-localpools.md#top) <br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -147,11 +147,6 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
ipcStore->deleteData(storeAddress);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We don't need to report the objectId, as we receive REQUESTED data before the completion
|
/* We don't need to report the objectId, as we receive REQUESTED data before the completion
|
||||||
success message. True aperiodic replies need to be reported with another dedicated message. */
|
success message. True aperiodic replies need to be reported with another dedicated message. */
|
||||||
ActionMessage::setDataReply(&reply, replyId, storeAddress);
|
ActionMessage::setDataReply(&reply, replyId, storeAddress);
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
*/
|
*/
|
||||||
template<typename T, size_t MAX_SIZE, typename count_t = uint8_t>
|
template<typename T, size_t MAX_SIZE, typename count_t = uint8_t>
|
||||||
class FixedArrayList: public ArrayList<T, count_t> {
|
class FixedArrayList: public ArrayList<T, count_t> {
|
||||||
static_assert(MAX_SIZE <= (pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE");
|
#if !defined(_MSC_VER)
|
||||||
|
static_assert(MAX_SIZE <= (std::pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE");
|
||||||
|
#endif
|
||||||
private:
|
private:
|
||||||
T data[MAX_SIZE];
|
T data[MAX_SIZE];
|
||||||
public:
|
public:
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "SharedRingBuffer.h"
|
#include "SharedRingBuffer.h"
|
||||||
#include "../ipc/MutexFactory.h"
|
#include "../ipc/MutexFactory.h"
|
||||||
#include "../ipc/MutexHelper.h"
|
#include "../ipc/MutexGuard.h"
|
||||||
|
|
||||||
SharedRingBuffer::SharedRingBuffer(object_id_t objectId, const size_t size,
|
SharedRingBuffer::SharedRingBuffer(object_id_t objectId, const size_t size,
|
||||||
bool overwriteOld, size_t maxExcessBytes):
|
bool overwriteOld, size_t maxExcessBytes):
|
||||||
@ -17,6 +17,9 @@ SharedRingBuffer::SharedRingBuffer(object_id_t objectId, uint8_t *buffer,
|
|||||||
mutex = MutexFactory::instance()->createMutex();
|
mutex = MutexFactory::instance()->createMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedRingBuffer::~SharedRingBuffer() {
|
||||||
|
MutexFactory::instance()->deleteMutex(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
void SharedRingBuffer::setToUseReceiveSizeFIFO(size_t fifoDepth) {
|
void SharedRingBuffer::setToUseReceiveSizeFIFO(size_t fifoDepth) {
|
||||||
this->fifoDepth = fifoDepth;
|
this->fifoDepth = fifoDepth;
|
||||||
|
@ -26,6 +26,18 @@ public:
|
|||||||
*/
|
*/
|
||||||
SharedRingBuffer(object_id_t objectId, const size_t size,
|
SharedRingBuffer(object_id_t objectId, const size_t size,
|
||||||
bool overwriteOld, size_t maxExcessBytes);
|
bool overwriteOld, size_t maxExcessBytes);
|
||||||
|
/**
|
||||||
|
* This constructor takes an external buffer with the specified size.
|
||||||
|
* @param buffer
|
||||||
|
* @param size
|
||||||
|
* @param overwriteOld
|
||||||
|
* If the ring buffer is overflowing at a write operartion, the oldest data
|
||||||
|
* will be overwritten.
|
||||||
|
*/
|
||||||
|
SharedRingBuffer(object_id_t objectId, uint8_t* buffer, const size_t size,
|
||||||
|
bool overwriteOld, size_t maxExcessBytes);
|
||||||
|
|
||||||
|
virtual~ SharedRingBuffer();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function can be used to add an optional FIFO to the class
|
* @brief This function can be used to add an optional FIFO to the class
|
||||||
@ -37,16 +49,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setToUseReceiveSizeFIFO(size_t fifoDepth);
|
void setToUseReceiveSizeFIFO(size_t fifoDepth);
|
||||||
|
|
||||||
/**
|
|
||||||
* This constructor takes an external buffer with the specified size.
|
|
||||||
* @param buffer
|
|
||||||
* @param size
|
|
||||||
* @param overwriteOld
|
|
||||||
* If the ring buffer is overflowing at a write operartion, the oldest data
|
|
||||||
* will be overwritten.
|
|
||||||
*/
|
|
||||||
SharedRingBuffer(object_id_t objectId, uint8_t* buffer, const size_t size,
|
|
||||||
bool overwriteOld, size_t maxExcessBytes);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unless a read-only constant value is read, all operations on the
|
* Unless a read-only constant value is read, all operations on the
|
||||||
@ -66,7 +69,7 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The mutex handle can be accessed directly, for example to perform
|
* The mutex handle can be accessed directly, for example to perform
|
||||||
* the lock with the #MutexHelper for a RAII compliant lock operation.
|
* the lock with the #MutexGuard for a RAII compliant lock operation.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
MutexIF* getMutexHandle() const;
|
MutexIF* getMutexHandle() const;
|
||||||
|
@ -17,14 +17,6 @@ ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
|
|||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t ExtendedControllerBase::initializeLocalDataPool(
|
|
||||||
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
|
|
||||||
/* Needs to be overriden and implemented by child class. */
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
object_id_t ExtendedControllerBase::getObjectId() const {
|
object_id_t ExtendedControllerBase::getObjectId() const {
|
||||||
return SystemObject::getObjectId();
|
return SystemObject::getObjectId();
|
||||||
}
|
}
|
||||||
@ -107,14 +99,6 @@ MessageQueueId_t ExtendedControllerBase::getCommandQueue() const {
|
|||||||
return commandQueue->getId();
|
return commandQueue->getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalPoolDataSetBase* ExtendedControllerBase::getDataSetHandle(sid_t sid) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::warning << "ExtendedControllerBase::getDataSetHandle: No child "
|
|
||||||
<< " implementation provided, returning nullptr!" << std::endl;
|
|
||||||
#endif
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() {
|
LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() {
|
||||||
return &poolManager;
|
return &poolManager;
|
||||||
}
|
}
|
||||||
|
@ -61,11 +61,11 @@ protected:
|
|||||||
/* HasLocalDatapoolIF overrides */
|
/* HasLocalDatapoolIF overrides */
|
||||||
virtual LocalDataPoolManager* getHkManagerHandle() override;
|
virtual LocalDataPoolManager* getHkManagerHandle() override;
|
||||||
virtual object_id_t getObjectId() const override;
|
virtual object_id_t getObjectId() const override;
|
||||||
virtual ReturnValue_t initializeLocalDataPool(
|
|
||||||
localpool::DataPool& localDataPoolMap,
|
|
||||||
LocalDataPoolManager& poolManager) override;
|
|
||||||
virtual uint32_t getPeriodicOperationFrequency() const override;
|
virtual uint32_t getPeriodicOperationFrequency() const override;
|
||||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
|
|
||||||
|
virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap,
|
||||||
|
LocalDataPoolManager& poolManager) override = 0;
|
||||||
|
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ class VirtualChannelReception;
|
|||||||
class DataLinkLayer : public CCSDSReturnValuesIF {
|
class DataLinkLayer : public CCSDSReturnValuesIF {
|
||||||
public:
|
public:
|
||||||
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_1;
|
static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_1;
|
||||||
static const Event RF_AVAILABLE = MAKE_EVENT(0, severity::INFO); //!< A RF available signal was detected. P1: raw RFA state, P2: 0
|
//! [EXPORT] : [COMMENT] A RF available signal was detected. P1: raw RFA state, P2: 0
|
||||||
|
static const Event RF_AVAILABLE = MAKE_EVENT(0, severity::INFO);
|
||||||
static const Event RF_LOST = MAKE_EVENT(1, severity::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0
|
static const Event RF_LOST = MAKE_EVENT(1, severity::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0
|
||||||
static const Event BIT_LOCK = MAKE_EVENT(2, severity::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0
|
static const Event BIT_LOCK = MAKE_EVENT(2, severity::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0
|
||||||
static const Event BIT_LOCK_LOST = MAKE_EVENT(3, severity::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0
|
static const Event BIT_LOCK_LOST = MAKE_EVENT(3, severity::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0
|
||||||
|
@ -7,7 +7,7 @@ HkSwitchHelper::HkSwitchHelper(EventReportingProxyIF* eventProxy) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
HkSwitchHelper::~HkSwitchHelper() {
|
HkSwitchHelper::~HkSwitchHelper() {
|
||||||
// TODO Auto-generated destructor stub
|
QueueFactory::instance()->deleteMessageQueue(actionQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t HkSwitchHelper::initialize() {
|
ReturnValue_t HkSwitchHelper::initialize() {
|
||||||
|
@ -8,13 +8,16 @@
|
|||||||
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
|
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
|
||||||
const size_t maxFillCount):
|
const size_t maxFillCount):
|
||||||
registeredVariables(registeredVariablesArray),
|
registeredVariables(registeredVariablesArray),
|
||||||
maxFillCount(maxFillCount) {
|
maxFillCount(maxFillCount) {}
|
||||||
}
|
|
||||||
|
|
||||||
PoolDataSetBase::~PoolDataSetBase() {}
|
PoolDataSetBase::~PoolDataSetBase() {}
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
|
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
|
||||||
|
if(registeredVariables == nullptr) {
|
||||||
|
/* Underlying container invalid */
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
if (state != States::STATE_SET_UNINITIALISED) {
|
if (state != States::STATE_SET_UNINITIALISED) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
|
sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
|
||||||
@ -61,11 +64,11 @@ ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType,
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "DataSet::read(): Call made in wrong position. Don't forget to commit"
|
sif::warning << "PoolDataSetBase::read: Call made in wrong position. Don't forget to "
|
||||||
" member datasets!" << std::endl;
|
"commit member datasets!" << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printError("DataSet::read(): Call made in wrong position. Don't forget to commit"
|
sif::printWarning("PoolDataSetBase::read: Call made in wrong position. Don't forget to "
|
||||||
" member datasets!\n");
|
"commit member datasets!\n");
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
result = SET_WAS_ALREADY_READ;
|
result = SET_WAS_ALREADY_READ;
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
* and unlock a data pool and read/commit semantics.
|
* and unlock a data pool and read/commit semantics.
|
||||||
*/
|
*/
|
||||||
class PoolDataSetIF:
|
class PoolDataSetIF:
|
||||||
public DataSetIF,
|
virtual public DataSetIF,
|
||||||
public ReadCommitIF {
|
virtual public ReadCommitIF {
|
||||||
public:
|
public:
|
||||||
virtual~ PoolDataSetIF() {};
|
virtual~ PoolDataSetIF() {};
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, bool setValid ):
|
PoolEntry<T>::PoolEntry(std::initializer_list<T> initValue, bool setValid ):
|
||||||
length(initValue.size()), valid(setValid) {
|
length(static_cast<uint8_t>(initValue.size())), valid(setValid) {
|
||||||
this->address = new T[this->length];
|
this->address = new T[this->length];
|
||||||
if(initValue.size() == 0) {
|
if(initValue.size() == 0) {
|
||||||
std::memset(this->address, 0, this->getByteSize());
|
std::memset(this->address, 0, this->getByteSize());
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||||
#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
|
||||||
|
|
||||||
#include "PoolDataSetIF.h"
|
#include "PoolDataSetIF.h"
|
||||||
|
|
||||||
class SharedDataSetIF: public PoolDataSetIF {
|
class SharedDataSetIF {
|
||||||
public:
|
public:
|
||||||
virtual ~SharedDataSetIF() {};
|
virtual ~SharedDataSetIF() {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ReturnValue_t lockDataset(dur_millis_t mutexTimeout) = 0;
|
virtual ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType,
|
||||||
|
dur_millis_t mutexTimeout) = 0;
|
||||||
virtual ReturnValue_t unlockDataset() = 0;
|
virtual ReturnValue_t unlockDataset() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -65,34 +65,45 @@ public:
|
|||||||
* usually be the period the pool owner performs its periodic operation.
|
* usually be the period the pool owner performs its periodic operation.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
virtual uint32_t getPeriodicOperationFrequency() const = 0;
|
virtual dur_millis_t getPeriodicOperationFrequency() const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function will be called by the manager if an update
|
* @brief This function will be called by the manager if an update
|
||||||
* notification is received.
|
* notification is received.
|
||||||
* @details HasLocalDataPoolIF
|
* @details HasLocalDataPoolIF
|
||||||
* Can be overriden by the child class to handle changed datasets.
|
* Can be overriden by the child class to handle changed datasets.
|
||||||
* @param sid
|
* @param sid SID of the updated set
|
||||||
* @param storeId If a snapshot was requested, data will be located inside
|
* @param storeId If a snapshot was requested, data will be located inside
|
||||||
* the IPC store with this store ID.
|
* the IPC store with this store ID.
|
||||||
|
* @param clearMessage If this is set to true, the pool manager will take care of
|
||||||
|
* clearing the store automatically
|
||||||
*/
|
*/
|
||||||
virtual void handleChangedDataset(sid_t sid,
|
virtual void handleChangedDataset(sid_t sid,
|
||||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
|
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
|
||||||
return;
|
bool* clearMessage = nullptr) {
|
||||||
|
if(clearMessage != nullptr) {
|
||||||
|
*clearMessage = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This function will be called by the manager if an update
|
* @brief This function will be called by the manager if an update
|
||||||
* notification is received.
|
* notification is received.
|
||||||
* @details
|
* @details
|
||||||
* Can be overriden by the child class to handle changed pool IDs.
|
* Can be overriden by the child class to handle changed pool variables.
|
||||||
* @param sid
|
* @param gpid GPID of the updated variable.
|
||||||
* @param storeId If a snapshot was requested, data will be located inside
|
* @param storeId If a snapshot was requested, data will be located inside
|
||||||
* the IPC store with this store ID.
|
* the IPC store with this store ID.
|
||||||
|
* @param clearMessage Relevant for snapshots. If the boolean this points to is set to true,
|
||||||
|
* the pool manager will take care of clearing the store automatically
|
||||||
|
* after the callback.
|
||||||
*/
|
*/
|
||||||
virtual void handleChangedPoolVariable(gp_id_t globPoolId,
|
virtual void handleChangedPoolVariable(gp_id_t gpid,
|
||||||
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
|
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
|
||||||
return;
|
bool* clearMessage = nullptr) {
|
||||||
|
if(clearMessage != nullptr) {
|
||||||
|
*clearMessage = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include "../housekeeping/AcceptsHkPacketsIF.h"
|
#include "../housekeeping/AcceptsHkPacketsIF.h"
|
||||||
#include "../timemanager/CCSDSTime.h"
|
#include "../timemanager/CCSDSTime.h"
|
||||||
#include "../ipc/MutexFactory.h"
|
#include "../ipc/MutexFactory.h"
|
||||||
#include "../ipc/MutexHelper.h"
|
#include "../ipc/MutexGuard.h"
|
||||||
#include "../ipc/QueueFactory.h"
|
#include "../ipc/QueueFactory.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -38,7 +38,11 @@ LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQue
|
|||||||
hkQueue = queueToUse;
|
hkQueue = queueToUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDataPoolManager::~LocalDataPoolManager() {}
|
LocalDataPoolManager::~LocalDataPoolManager() {
|
||||||
|
if(mutex != nullptr) {
|
||||||
|
MutexFactory::instance()->deleteMutex(mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
|
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
|
||||||
if(queueToUse == nullptr) {
|
if(queueToUse == nullptr) {
|
||||||
@ -132,13 +136,16 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() {
|
|||||||
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver,
|
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver,
|
||||||
ReturnValue_t& status) {
|
ReturnValue_t& status) {
|
||||||
if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) {
|
||||||
// Update packets shall only be generated from datasets.
|
/* Update packets shall only be generated from datasets. */
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner,
|
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner,
|
||||||
receiver.dataId.sid);
|
receiver.dataId.sid);
|
||||||
|
if(dataSet == nullptr) {
|
||||||
|
return DATASET_NOT_FOUND;
|
||||||
|
}
|
||||||
if(dataSet->hasChanged()) {
|
if(dataSet->hasChanged()) {
|
||||||
// prepare and send update notification
|
/* Prepare and send update notification */
|
||||||
ReturnValue_t result = generateHousekeepingPacket(
|
ReturnValue_t result = generateHousekeepingPacket(
|
||||||
receiver.dataId.sid, dataSet, true);
|
receiver.dataId.sid, dataSet, true);
|
||||||
if(result != HasReturnvaluesIF::RETURN_OK) {
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
@ -328,7 +335,7 @@ void LocalDataPoolManager::handleChangeResetLogic(
|
|||||||
toReset->setChanged(false);
|
toReset->setChanged(false);
|
||||||
}
|
}
|
||||||
/* All recipients have been notified, reset the changed flag */
|
/* All recipients have been notified, reset the changed flag */
|
||||||
if(changeInfo.currentUpdateCounter <= 1) {
|
else if(changeInfo.currentUpdateCounter <= 1) {
|
||||||
toReset->setChanged(false);
|
toReset->setChanged(false);
|
||||||
changeInfo.currentUpdateCounter = 0;
|
changeInfo.currentUpdateCounter = 0;
|
||||||
}
|
}
|
||||||
@ -372,7 +379,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid,
|
|||||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting);
|
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting);
|
||||||
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
|
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
|
||||||
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval,
|
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval,
|
||||||
owner->getPeriodicOperationFrequency(), isDiagnostics);
|
owner->getPeriodicOperationFrequency());
|
||||||
}
|
}
|
||||||
|
|
||||||
hkReceivers.push_back(hkReceiver);
|
hkReceivers.push_back(hkReceiver);
|
||||||
@ -398,7 +405,6 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid,
|
|||||||
hkReceiver.destinationQueue = hkReceiverObject->getHkQueue();
|
hkReceiver.destinationQueue = hkReceiverObject->getHkQueue();
|
||||||
|
|
||||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
|
||||||
if(dataSet != nullptr) {
|
if(dataSet != nullptr) {
|
||||||
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
|
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
|
||||||
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
|
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
|
||||||
@ -516,11 +522,19 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): {
|
case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): {
|
||||||
return generateSetStructurePacket(sid, true);
|
result = generateSetStructurePacket(sid, true);
|
||||||
|
if(result == HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): {
|
case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): {
|
||||||
return generateSetStructurePacket(sid, false);
|
result = generateSetStructurePacket(sid, false);
|
||||||
|
if(result == HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL):
|
case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL):
|
||||||
case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): {
|
case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): {
|
||||||
@ -540,14 +554,15 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
|||||||
case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT):
|
case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT):
|
||||||
case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): {
|
case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): {
|
||||||
LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
|
||||||
if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT
|
if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT
|
||||||
and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||||
return WRONG_HK_PACKET_TYPE;
|
result = WRONG_HK_PACKET_TYPE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT
|
else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT
|
||||||
and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||||
return WRONG_HK_PACKET_TYPE;
|
result = WRONG_HK_PACKET_TYPE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return generateHousekeepingPacket(HousekeepingMessage::getSid(message),
|
return generateHousekeepingPacket(HousekeepingMessage::getSid(message),
|
||||||
dataSet, true);
|
dataSet, true);
|
||||||
@ -566,14 +581,22 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
|
|||||||
case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
|
case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
|
||||||
store_address_t storeId;
|
store_address_t storeId;
|
||||||
HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId);
|
HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId);
|
||||||
owner->handleChangedDataset(sid, storeId);
|
bool clearMessage = true;
|
||||||
|
owner->handleChangedDataset(sid, storeId, &clearMessage);
|
||||||
|
if(clearMessage) {
|
||||||
|
message->clear();
|
||||||
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
|
case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
|
||||||
store_address_t storeId;
|
store_address_t storeId;
|
||||||
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message,
|
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message,
|
||||||
&storeId);
|
&storeId);
|
||||||
owner->handleChangedPoolVariable(globPoolId, storeId);
|
bool clearMessage = true;
|
||||||
|
owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage);
|
||||||
|
if(clearMessage) {
|
||||||
|
message->clear();
|
||||||
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -616,7 +639,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
|||||||
LocalPoolDataSetBase* dataSet, bool forDownlink,
|
LocalPoolDataSetBase* dataSet, bool forDownlink,
|
||||||
MessageQueueId_t destination) {
|
MessageQueueId_t destination) {
|
||||||
if(dataSet == nullptr) {
|
if(dataSet == nullptr) {
|
||||||
// Configuration error.
|
/* Configuration error. */
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||||
"generateHousekeepingPacket",
|
"generateHousekeepingPacket",
|
||||||
DATASET_NOT_FOUND);
|
DATASET_NOT_FOUND);
|
||||||
@ -632,7 +655,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// and now we set a HK message and send it the HK packet destination.
|
/* Now we set a HK message and send it the HK packet destination. */
|
||||||
CommandMessage hkMessage;
|
CommandMessage hkMessage;
|
||||||
if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
|
||||||
HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId);
|
HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId);
|
||||||
@ -642,7 +665,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(hkQueue == nullptr) {
|
if(hkQueue == nullptr) {
|
||||||
// error, no queue available to send packet with.
|
/* Error, no queue available to send packet with. */
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||||
"generateHousekeepingPacket",
|
"generateHousekeepingPacket",
|
||||||
QUEUE_OR_DESTINATION_INVALID);
|
QUEUE_OR_DESTINATION_INVALID);
|
||||||
@ -650,7 +673,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
|
|||||||
}
|
}
|
||||||
if(destination == MessageQueueIF::NO_QUEUE) {
|
if(destination == MessageQueueIF::NO_QUEUE) {
|
||||||
if(hkDestinationId == MessageQueueIF::NO_QUEUE) {
|
if(hkDestinationId == MessageQueueIF::NO_QUEUE) {
|
||||||
// error, all destinations invalid
|
/* Error, all destinations invalid */
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||||
"generateHousekeepingPacket",
|
"generateHousekeepingPacket",
|
||||||
QUEUE_OR_DESTINATION_INVALID);
|
QUEUE_OR_DESTINATION_INVALID);
|
||||||
@ -729,6 +752,12 @@ void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
|
|||||||
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
|
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
|
||||||
bool enable, bool isDiagnostics) {
|
bool enable, bool isDiagnostics) {
|
||||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||||
|
if(dataSet == nullptr) {
|
||||||
|
printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration",
|
||||||
|
DATASET_NOT_FOUND);
|
||||||
|
return DATASET_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
if((LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and not isDiagnostics) or
|
if((LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and not isDiagnostics) or
|
||||||
(not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) {
|
(not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) {
|
||||||
return WRONG_HK_PACKET_TYPE;
|
return WRONG_HK_PACKET_TYPE;
|
||||||
@ -746,6 +775,12 @@ ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
|
|||||||
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
||||||
float newCollectionInterval, bool isDiagnostics) {
|
float newCollectionInterval, bool isDiagnostics) {
|
||||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||||
|
if(dataSet == nullptr) {
|
||||||
|
printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval",
|
||||||
|
DATASET_NOT_FOUND);
|
||||||
|
return DATASET_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet);
|
bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet);
|
||||||
if((targetIsDiagnostics and not isDiagnostics) or
|
if((targetIsDiagnostics and not isDiagnostics) or
|
||||||
(not targetIsDiagnostics and isDiagnostics)) {
|
(not targetIsDiagnostics and isDiagnostics)) {
|
||||||
@ -756,7 +791,7 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
|||||||
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
|
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
|
||||||
|
|
||||||
if(periodicHelper == nullptr) {
|
if(periodicHelper == nullptr) {
|
||||||
// config error
|
/* Configuration error, set might not have a corresponding pool manager */
|
||||||
return PERIODIC_HELPER_INVALID;
|
return PERIODIC_HELPER_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -766,13 +801,11 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
|
|||||||
|
|
||||||
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
|
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
|
||||||
bool isDiagnostics) {
|
bool isDiagnostics) {
|
||||||
// Get and check dataset first.
|
/* Get and check dataset first. */
|
||||||
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
|
|
||||||
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
|
||||||
if(dataSet == nullptr) {
|
if(dataSet == nullptr) {
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
printWarningOrError(sif::OutputTypes::OUT_WARNING,
|
||||||
"performPeriodicHkGeneration",
|
"performPeriodicHkGeneration", DATASET_NOT_FOUND);
|
||||||
DATASET_NOT_FOUND);
|
|
||||||
return DATASET_NOT_FOUND;
|
return DATASET_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,6 +864,10 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
|
|||||||
void LocalDataPoolManager::clearReceiversList() {
|
void LocalDataPoolManager::clearReceiversList() {
|
||||||
/* Clear the vector completely and releases allocated memory. */
|
/* Clear the vector completely and releases allocated memory. */
|
||||||
HkReceivers().swap(hkReceivers);
|
HkReceivers().swap(hkReceivers);
|
||||||
|
/* Also clear the reset helper if it exists */
|
||||||
|
if(hkUpdateResetList != nullptr) {
|
||||||
|
HkUpdateResetList().swap(*hkUpdateResetList);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MutexIF* LocalDataPoolManager::getLocalPoolMutex() {
|
MutexIF* LocalDataPoolManager::getLocalPoolMutex() {
|
||||||
@ -843,6 +880,7 @@ object_id_t LocalDataPoolManager::getCreatorObjectId() const {
|
|||||||
|
|
||||||
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
||||||
const char* functionName, ReturnValue_t error, const char* errorPrint) {
|
const char* functionName, ReturnValue_t error, const char* errorPrint) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
if(errorPrint == nullptr) {
|
if(errorPrint == nullptr) {
|
||||||
if(error == DATASET_NOT_FOUND) {
|
if(error == DATASET_NOT_FOUND) {
|
||||||
errorPrint = "Dataset not found";
|
errorPrint = "Dataset not found";
|
||||||
@ -871,33 +909,32 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
|
|||||||
errorPrint = "Unknown error";
|
errorPrint = "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
object_id_t objectId = 0xffffffff;
|
||||||
|
if(owner != nullptr) {
|
||||||
|
objectId = owner->getObjectId();
|
||||||
|
}
|
||||||
|
|
||||||
if(outputType == sif::OutputTypes::OUT_WARNING) {
|
if(outputType == sif::OutputTypes::OUT_WARNING) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::warning << "LocalDataPoolManager::" << functionName
|
sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" <<
|
||||||
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
|
std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint <<
|
||||||
<< std::hex << owner->getObjectId() << " | " << errorPrint
|
std::dec << std::setfill(' ') << std::endl;
|
||||||
<< std::dec << std::setfill(' ') << std::endl;
|
|
||||||
#else
|
#else
|
||||||
sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
||||||
functionName, owner->getObjectId(), errorPrint);
|
functionName, objectId, errorPrint);
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
|
||||||
}
|
}
|
||||||
else if(outputType == sif::OutputTypes::OUT_ERROR) {
|
else if(outputType == sif::OutputTypes::OUT_ERROR) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "LocalDataPoolManager::" << functionName
|
sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" <<
|
||||||
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
|
std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint <<
|
||||||
<< std::hex << owner->getObjectId() << " | " << errorPrint
|
std::dec << std::setfill(' ') << std::endl;
|
||||||
<< std::dec << std::setfill(' ') << std::endl;
|
|
||||||
#else
|
#else
|
||||||
sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n",
|
||||||
functionName, owner->getObjectId(), errorPrint);
|
functionName, objectId, errorPrint);
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
|
||||||
}
|
}
|
||||||
|
#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() {
|
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
#include "../ipc/MutexIF.h"
|
#include "../ipc/MutexIF.h"
|
||||||
#include "../ipc/CommandMessage.h"
|
#include "../ipc/CommandMessage.h"
|
||||||
#include "../ipc/MessageQueueIF.h"
|
#include "../ipc/MessageQueueIF.h"
|
||||||
#include "../ipc/MutexHelper.h"
|
#include "../ipc/MutexGuard.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -391,6 +391,10 @@ protected:
|
|||||||
|
|
||||||
template<class T> inline
|
template<class T> inline
|
||||||
ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T> **poolEntry) {
|
ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T> **poolEntry) {
|
||||||
|
if(poolEntry == nullptr) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
auto poolIter = localPoolMap.find(localPoolId);
|
auto poolIter = localPoolMap.find(localPoolId);
|
||||||
if (poolIter == localPoolMap.end()) {
|
if (poolIter == localPoolMap.end()) {
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
|
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",
|
||||||
|
@ -44,7 +44,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
|
|||||||
|
|
||||||
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
|
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
|
||||||
const size_t maxNumberOfVariables):
|
const size_t maxNumberOfVariables):
|
||||||
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||||
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
|
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
|
||||||
sid.objectId);
|
sid.objectId);
|
||||||
if(hkOwner != nullptr) {
|
if(hkOwner != nullptr) {
|
||||||
@ -58,8 +58,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registere
|
|||||||
this->sid = sid;
|
this->sid = sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalPoolDataSetBase::LocalPoolDataSetBase(
|
LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray,
|
||||||
PoolVariableIF **registeredVariablesArray,
|
|
||||||
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall):
|
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall):
|
||||||
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
|
||||||
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
|
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
|
||||||
@ -95,14 +94,23 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
|
|||||||
size_t *size, size_t maxSize,
|
size_t *size, size_t maxSize,
|
||||||
SerializeIF::Endianness streamEndianness) const {
|
SerializeIF::Endianness streamEndianness) const {
|
||||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
|
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
|
||||||
uint8_t validityMask[validityMaskSize] = {};
|
uint8_t* validityPtr = nullptr;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* Use a std::vector here because MSVC will (rightly) not create a fixed size array
|
||||||
|
with a non constant size specifier */
|
||||||
|
std::vector<uint8_t> validityMask(validityMaskSize);
|
||||||
|
validityPtr = validityMask.data();
|
||||||
|
#else
|
||||||
|
uint8_t validityMask[validityMaskSize] = {0};
|
||||||
|
validityPtr = validityMask;
|
||||||
|
#endif
|
||||||
uint8_t validBufferIndex = 0;
|
uint8_t validBufferIndex = 0;
|
||||||
uint8_t validBufferIndexBit = 0;
|
uint8_t validBufferIndexBit = 0;
|
||||||
for (uint16_t count = 0; count < fillCount; count++) {
|
for (uint16_t count = 0; count < fillCount; count++) {
|
||||||
if(registeredVariables[count]->isValid()) {
|
if(registeredVariables[count]->isValid()) {
|
||||||
/* Set bit at correct position */
|
/* Set bit at correct position */
|
||||||
bitutil::bitSet(validityMask + validBufferIndex, validBufferIndexBit);
|
bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit);
|
||||||
}
|
}
|
||||||
if(validBufferIndexBit == 7) {
|
if(validBufferIndexBit == 7) {
|
||||||
validBufferIndex ++;
|
validBufferIndex ++;
|
||||||
@ -123,7 +131,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
|
|||||||
return SerializeIF::BUFFER_TOO_SHORT;
|
return SerializeIF::BUFFER_TOO_SHORT;
|
||||||
}
|
}
|
||||||
// copy validity buffer to end
|
// copy validity buffer to end
|
||||||
std::memcpy(*buffer, validityMask, validityMaskSize);
|
std::memcpy(*buffer, validityPtr, validityMaskSize);
|
||||||
*size += validityMaskSize;
|
*size += validityMaskSize;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -262,11 +270,9 @@ bool LocalPoolDataSetBase::getReportingEnabled() const {
|
|||||||
return reportingEnabled;
|
return reportingEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalPoolDataSetBase::initializePeriodicHelper(
|
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
|
||||||
float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
|
||||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor) {
|
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, nonDiagIntervalFactor);
|
||||||
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval,
|
|
||||||
isDiagnostics, nonDiagIntervalFactor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalPoolDataSetBase::setChanged(bool changed) {
|
void LocalPoolDataSetBase::setChanged(bool changed) {
|
||||||
@ -306,3 +312,12 @@ void LocalPoolDataSetBase::setAllVariablesReadOnly() {
|
|||||||
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
|
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float LocalPoolDataSetBase::getCollectionInterval() const {
|
||||||
|
if(periodicHelper != nullptr) {
|
||||||
|
return periodicHelper->getCollectionIntervalInSeconds();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -166,6 +166,16 @@ public:
|
|||||||
|
|
||||||
object_id_t getCreatorObjectId();
|
object_id_t getCreatorObjectId();
|
||||||
|
|
||||||
|
bool getReportingEnabled() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current periodic HK generation interval this set
|
||||||
|
* belongs to a HK manager and the interval is not 0. Otherwise,
|
||||||
|
* returns 0.0
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
float getCollectionInterval() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
sid_t sid;
|
sid_t sid;
|
||||||
//! This mutex is used if the data is created by one object only.
|
//! This mutex is used if the data is created by one object only.
|
||||||
@ -180,11 +190,9 @@ protected:
|
|||||||
*/
|
*/
|
||||||
bool reportingEnabled = false;
|
bool reportingEnabled = false;
|
||||||
void setReportingEnabled(bool enabled);
|
void setReportingEnabled(bool enabled);
|
||||||
bool getReportingEnabled() const;
|
|
||||||
|
|
||||||
void initializePeriodicHelper(float collectionInterval,
|
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
||||||
dur_millis_t minimumPeriodicInterval,
|
uint8_t nonDiagIntervalFactor = 5);
|
||||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the valid state of a dataset is always relevant to the whole
|
* If the valid state of a dataset is always relevant to the whole
|
||||||
|
@ -25,7 +25,7 @@ inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId,
|
|||||||
template<typename T, uint16_t vectorSize>
|
template<typename T, uint16_t vectorSize>
|
||||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(
|
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(
|
||||||
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||||
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||||
return readWithoutLock();
|
return readWithoutLock();
|
||||||
}
|
}
|
||||||
template<typename T, uint16_t vectorSize>
|
template<typename T, uint16_t vectorSize>
|
||||||
@ -64,7 +64,7 @@ inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(bool valid,
|
|||||||
template<typename T, uint16_t vectorSize>
|
template<typename T, uint16_t vectorSize>
|
||||||
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(
|
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(
|
||||||
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
|
||||||
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
|
||||||
return commitWithoutLock();
|
return commitWithoutLock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,16 +1,37 @@
|
|||||||
#include "SharedLocalDataSet.h"
|
#include "SharedLocalDataSet.h"
|
||||||
|
|
||||||
|
|
||||||
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid,
|
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid,
|
||||||
const size_t maxSize): SystemObject(objectId),
|
const size_t maxSize): SystemObject(objectId),
|
||||||
LocalPoolDataSetBase(sid, nullptr, maxSize) {
|
LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
|
||||||
this->setContainer(poolVarVector.data());
|
this->setContainer(poolVarVector.data());
|
||||||
datasetLock = MutexFactory::instance()->createMutex();
|
datasetLock = MutexFactory::instance()->createMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t SharedLocalDataSet::lockDataset(dur_millis_t mutexTimeout) {
|
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId,
|
||||||
return datasetLock->lockMutex(MutexIF::TimeoutType::WAITING, mutexTimeout);
|
HasLocalDataPoolIF *owner, uint32_t setId,
|
||||||
|
const size_t maxSize): SystemObject(objectId),
|
||||||
|
LocalPoolDataSetBase(owner, setId, nullptr, maxSize), poolVarVector(maxSize) {
|
||||||
|
this->setContainer(poolVarVector.data());
|
||||||
|
datasetLock = MutexFactory::instance()->createMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType,
|
||||||
|
dur_millis_t mutexTimeout) {
|
||||||
|
if(datasetLock != nullptr) {
|
||||||
|
return datasetLock->lockMutex(timeoutType, mutexTimeout);
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SharedLocalDataSet::~SharedLocalDataSet() {
|
||||||
|
MutexFactory::instance()->deleteMutex(datasetLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t SharedLocalDataSet::unlockDataset() {
|
ReturnValue_t SharedLocalDataSet::unlockDataset() {
|
||||||
return datasetLock->unlockMutex();
|
if(datasetLock != nullptr) {
|
||||||
|
return datasetLock->unlockMutex();
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -11,16 +11,22 @@
|
|||||||
* multiple threads. It provides a lock in addition to all other functionalities provided
|
* multiple threads. It provides a lock in addition to all other functionalities provided
|
||||||
* by the LocalPoolDataSetBase class.
|
* by the LocalPoolDataSetBase class.
|
||||||
*
|
*
|
||||||
* TODO: override and protect read, commit and some other calls used by pool manager.
|
* The user is completely responsible for lockingand unlocking the dataset when using the
|
||||||
|
* shared dataset.
|
||||||
*/
|
*/
|
||||||
class SharedLocalDataSet:
|
class SharedLocalDataSet:
|
||||||
public SystemObject,
|
public SystemObject,
|
||||||
public LocalPoolDataSetBase,
|
public LocalPoolDataSetBase,
|
||||||
public SharedDataSetIF {
|
public SharedDataSetIF {
|
||||||
public:
|
public:
|
||||||
SharedLocalDataSet(object_id_t objectId, sid_t sid,
|
SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId,
|
||||||
const size_t maxSize);
|
const size_t maxSize);
|
||||||
ReturnValue_t lockDataset(dur_millis_t mutexTimeout) override;
|
SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize);
|
||||||
|
|
||||||
|
virtual~ SharedLocalDataSet();
|
||||||
|
|
||||||
|
ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING,
|
||||||
|
dur_millis_t mutexTimeout = 20) override;
|
||||||
ReturnValue_t unlockDataset() override;
|
ReturnValue_t unlockDataset() override;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
12
datapoollocal/datapoollocal.h
Normal file
12
datapoollocal/datapoollocal.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||||
|
#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_
|
||||||
|
|
||||||
|
/* Collected related headers */
|
||||||
|
#include "LocalPoolVariable.h"
|
||||||
|
#include "LocalPoolVector.h"
|
||||||
|
#include "StaticLocalDataSet.h"
|
||||||
|
#include "LocalDataSet.h"
|
||||||
|
#include "SharedLocalDataSet.h"
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */
|
@ -14,9 +14,8 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
|
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
|
||||||
uint32_t minimumPeriodicIntervalMs,
|
uint32_t minimumPeriodicIntervalMs, uint8_t nonDiagIntervalFactor = 5) {
|
||||||
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5) {
|
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs,
|
||||||
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, isDiagnostics,
|
|
||||||
nonDiagIntervalFactor);
|
nonDiagIntervalFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,11 +96,11 @@ union gp_id_t {
|
|||||||
return raw == INVALID_GPID;
|
return raw == INVALID_GPID;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const sid_t& other) const {
|
bool operator==(const gp_id_t& other) const {
|
||||||
return raw == other.raw;
|
return raw == other.raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const sid_t& other) const {
|
bool operator!=(const gp_id_t& other) const {
|
||||||
return not (raw == other.raw);
|
return not (raw == other.raw);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,15 +1,23 @@
|
|||||||
target_sources(${LIB_FSFW_NAME} PRIVATE
|
|
||||||
ipc/missionMessageTypes.cpp
|
|
||||||
objects/FsfwFactory.cpp
|
|
||||||
pollingsequence/PollingSequenceFactory.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
# Should be added to include path
|
|
||||||
target_include_directories(${TARGET_NAME} PRIVATE
|
target_include_directories(${TARGET_NAME} PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT FSFW_CONFIG_PATH)
|
target_sources(${TARGET_NAME} PRIVATE
|
||||||
set(FSFW_CONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR})
|
ipc/missionMessageTypes.cpp
|
||||||
|
pollingsequence/PollingSequenceFactory.cpp
|
||||||
|
objects/FsfwFactory.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# If a special translation file for object IDs exists, compile it.
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp")
|
||||||
|
target_sources(${TARGET_NAME} PRIVATE
|
||||||
|
objects/translateObjects.cpp
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# If a special translation file for events exists, compile it.
|
||||||
|
if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/objects/translateObjects.cpp")
|
||||||
|
target_sources(${TARGET_NAME} PRIVATE
|
||||||
|
events/translateEvents.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
@ -17,6 +17,8 @@
|
|||||||
#define FSFW_DISABLE_PRINTOUT 0
|
#define FSFW_DISABLE_PRINTOUT 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FSFW_USE_PUS_C_TELEMETRY 1
|
||||||
|
|
||||||
//! Can be used to disable the ANSI color sequences for C stdio.
|
//! Can be used to disable the ANSI color sequences for C stdio.
|
||||||
#define FSFW_COLORED_OUTPUT 1
|
#define FSFW_COLORED_OUTPUT 1
|
||||||
|
|
||||||
@ -40,10 +42,17 @@
|
|||||||
//! Specify whether a special mode store is used for Subsystem components.
|
//! Specify whether a special mode store is used for Subsystem components.
|
||||||
#define FSFW_USE_MODESTORE 0
|
#define FSFW_USE_MODESTORE 0
|
||||||
|
|
||||||
|
//! Defines if the real time scheduler for linux should be used.
|
||||||
|
//! If set to 0, this will also disable priority settings for linux
|
||||||
|
//! as most systems will not allow to set nice values without privileges
|
||||||
|
//! For embedded linux system set this to 1.
|
||||||
|
//! If set to 1 the binary needs "cap_sys_nice=eip" privileges to run
|
||||||
|
#define FSFW_USE_REALTIME_FOR_LINUX 1
|
||||||
|
|
||||||
namespace fsfwconfig {
|
namespace fsfwconfig {
|
||||||
//! Default timestamp size. The default timestamp will be an eight byte CDC
|
|
||||||
//! short timestamp.
|
//! Default timestamp size. The default timestamp will be an seven byte CDC short timestamp.
|
||||||
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8;
|
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 7;
|
||||||
|
|
||||||
//! Configure the allocated pool sizes for the event manager.
|
//! Configure the allocated pool sizes for the event manager.
|
||||||
static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240;
|
static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240;
|
||||||
@ -52,11 +61,12 @@ static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
|
|||||||
|
|
||||||
//! Defines the FIFO depth of each commanding service base which
|
//! Defines the FIFO depth of each commanding service base which
|
||||||
//! also determines how many commands a CSB service can handle in one cycle
|
//! also determines how many commands a CSB service can handle in one cycle
|
||||||
//! simulataneously. This will increase the required RAM for
|
//! simultaneously. This will increase the required RAM for
|
||||||
//! each CSB service !
|
//! each CSB service !
|
||||||
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
|
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
|
||||||
|
|
||||||
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124;
|
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_FSFWCONFIG_H_ */
|
#endif /* CONFIG_FSFWCONFIG_H_ */
|
||||||
|
@ -24,6 +24,13 @@
|
|||||||
* 1. check logic when active-> checkChildrenStateOn
|
* 1. check logic when active-> checkChildrenStateOn
|
||||||
* 2. transition logic to change the mode -> commandChildren
|
* 2. transition logic to change the mode -> commandChildren
|
||||||
*
|
*
|
||||||
|
* Important:
|
||||||
|
*
|
||||||
|
* The implementation must call registerChild(object_id_t child)
|
||||||
|
* for all commanded children during initialization.
|
||||||
|
* The implementation must call the initialization function of the base class.
|
||||||
|
* (This will call the function in SubsystemBase)
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
class AssemblyBase: public SubsystemBase {
|
class AssemblyBase: public SubsystemBase {
|
||||||
public:
|
public:
|
||||||
@ -41,9 +48,6 @@ public:
|
|||||||
virtual ~AssemblyBase();
|
virtual ~AssemblyBase();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
// SHOULDDO: Change that OVERWRITE_HEALTH may be returned
|
|
||||||
// (or return internalState directly?)
|
|
||||||
/**
|
/**
|
||||||
* Command children to reach [mode,submode] combination
|
* Command children to reach [mode,submode] combination
|
||||||
* Can be done by setting #commandsOutstanding correctly,
|
* Can be done by setting #commandsOutstanding correctly,
|
||||||
@ -68,6 +72,18 @@ protected:
|
|||||||
virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode,
|
virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode,
|
||||||
Submode_t wantedSubmode) = 0;
|
Submode_t wantedSubmode) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether a combination of mode and submode is valid.
|
||||||
|
*
|
||||||
|
* Ground Controller like precise return values from HasModesIF.
|
||||||
|
* So, please return any of them.
|
||||||
|
*
|
||||||
|
* @param mode The targeted mode
|
||||||
|
* @param submode The targeted submmode
|
||||||
|
* @return Any information why this combination is invalid from HasModesIF
|
||||||
|
* like HasModesIF::INVALID_SUBMODE.
|
||||||
|
* On success return HasReturnvaluesIF::RETURN_OK
|
||||||
|
*/
|
||||||
virtual ReturnValue_t isModeCombinationValid(Mode_t mode,
|
virtual ReturnValue_t isModeCombinationValid(Mode_t mode,
|
||||||
Submode_t submode) = 0;
|
Submode_t submode) = 0;
|
||||||
|
|
||||||
|
@ -308,11 +308,11 @@ void DeviceHandlerBase::doStateMachine() {
|
|||||||
uint32_t currentUptime;
|
uint32_t currentUptime;
|
||||||
Clock::getUptime(¤tUptime);
|
Clock::getUptime(¤tUptime);
|
||||||
if (currentUptime - timeoutStart >= childTransitionDelay) {
|
if (currentUptime - timeoutStart >= childTransitionDelay) {
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
#if FSFW_VERBOSE_LEVEL >= 1 && FSFW_OBJ_EVENT_TRANSLATION == 0
|
||||||
char printout[60];
|
char printout[60];
|
||||||
sprintf(printout, "Transition timeout (%lu) occured !",
|
sprintf(printout, "Transition timeout (%lu) occured !",
|
||||||
static_cast<unsigned long>(childTransitionDelay));
|
static_cast<unsigned long>(childTransitionDelay));
|
||||||
/* Very common configuration error, so print it */
|
/* Common configuration error for development, so print it */
|
||||||
printWarningOrError(sif::OutputTypes::OUT_WARNING, "doStateMachine",
|
printWarningOrError(sif::OutputTypes::OUT_WARNING, "doStateMachine",
|
||||||
RETURN_FAILED, printout);
|
RETURN_FAILED, printout);
|
||||||
#endif
|
#endif
|
||||||
@ -456,6 +456,15 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceComm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t DeviceHandlerBase::getNextReplyLength(DeviceCommandId_t commandId){
|
||||||
|
DeviceReplyIter iter = deviceReplyMap.find(commandId);
|
||||||
|
if(iter != deviceReplyMap.end()) {
|
||||||
|
return iter->second.replyLen;
|
||||||
|
}else{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply,
|
ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply,
|
||||||
uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) {
|
uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) {
|
||||||
auto replyIter = deviceReplyMap.find(deviceReply);
|
auto replyIter = deviceReplyMap.find(deviceReply);
|
||||||
@ -646,16 +655,12 @@ void DeviceHandlerBase::doGetWrite() {
|
|||||||
void DeviceHandlerBase::doSendRead() {
|
void DeviceHandlerBase::doSendRead() {
|
||||||
ReturnValue_t result;
|
ReturnValue_t result;
|
||||||
|
|
||||||
size_t requestLen = 0;
|
size_t replyLen = 0;
|
||||||
if(cookieInfo.pendingCommand != deviceCommandMap.end()) {
|
if(cookieInfo.pendingCommand != deviceCommandMap.end()) {
|
||||||
DeviceReplyIter iter = deviceReplyMap.find(
|
replyLen = getNextReplyLength(cookieInfo.pendingCommand->first);
|
||||||
cookieInfo.pendingCommand->first);
|
|
||||||
if(iter != deviceReplyMap.end()) {
|
|
||||||
requestLen = iter->second.replyLen;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result = communicationInterface->requestReceiveMessage(comCookie, requestLen);
|
result = communicationInterface->requestReceiveMessage(comCookie, replyLen);
|
||||||
|
|
||||||
if (result == RETURN_OK) {
|
if (result == RETURN_OK) {
|
||||||
cookieInfo.state = COOKIE_READ_SENT;
|
cookieInfo.state = COOKIE_READ_SENT;
|
||||||
@ -1491,7 +1496,7 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
|
|||||||
if(errorCode == ObjectManagerIF::CHILD_INIT_FAILED) {
|
if(errorCode == ObjectManagerIF::CHILD_INIT_FAILED) {
|
||||||
errorPrint = "Initialization error";
|
errorPrint = "Initialization error";
|
||||||
}
|
}
|
||||||
if(errorCode == HasReturnvaluesIF::RETURN_FAILED) {
|
else if(errorCode == HasReturnvaluesIF::RETURN_FAILED) {
|
||||||
if(errorType == sif::OutputTypes::OUT_WARNING) {
|
if(errorType == sif::OutputTypes::OUT_WARNING) {
|
||||||
errorPrint = "Generic Warning";
|
errorPrint = "Generic Warning";
|
||||||
}
|
}
|
||||||
@ -1503,6 +1508,9 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
|
|||||||
errorPrint = "Unknown error";
|
errorPrint = "Unknown error";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(functionName == nullptr) {
|
||||||
|
functionName = "unknown function";
|
||||||
|
}
|
||||||
|
|
||||||
if(errorType == sif::OutputTypes::OUT_WARNING) {
|
if(errorType == sif::OutputTypes::OUT_WARNING) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
@ -1511,18 +1519,18 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
|
|||||||
std::dec << std::setfill(' ') << std::endl;
|
std::dec << std::setfill(' ') << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
|
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
|
||||||
this->getObjectId(), errorPrint);
|
functionName, this->getObjectId(), errorPrint);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if(errorType == sif::OutputTypes::OUT_ERROR) {
|
else if(errorType == sif::OutputTypes::OUT_ERROR) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "DeviceHandlerBase::" << functionName << ": Object ID "
|
sif::error << "DeviceHandlerBase::" << functionName << ": Object ID 0x"
|
||||||
<< std::hex << std::setw(8) << std::setfill('0')
|
<< std::hex << std::setw(8) << std::setfill('0')
|
||||||
<< this->getObjectId() << " | " << errorPrint << std::dec
|
<< this->getObjectId() << " | " << errorPrint << std::dec
|
||||||
<< std::setfill(' ') << std::endl;
|
<< std::setfill(' ') << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printError("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
|
sif::printError("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
|
||||||
this->getObjectId(), errorPrint);
|
functionName, this->getObjectId(), errorPrint);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,13 +471,27 @@ protected:
|
|||||||
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand,
|
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand,
|
||||||
uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr,
|
uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr,
|
||||||
size_t replyLen = 0, bool periodic = false);
|
size_t replyLen = 0, bool periodic = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A simple command to add a command to the commandList.
|
* @brief A simple command to add a command to the commandList.
|
||||||
* @param deviceCommand The command to add
|
* @param deviceCommand The command to add
|
||||||
* @return - @c RETURN_OK when the command was successfully inserted,
|
* @return - @c RETURN_OK when the command was successfully inserted,
|
||||||
* - @c RETURN_FAILED else.
|
* - @c RETURN_FAILED else.
|
||||||
*/
|
*/
|
||||||
ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand);
|
ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This function returns the reply length of the next reply to read.
|
||||||
|
*
|
||||||
|
* @param deviceCommand The command which triggered the device reply.
|
||||||
|
*
|
||||||
|
* @details The default implementation assumes only one reply is triggered by the command. In
|
||||||
|
* case the command triggers multiple replies (e.g. one acknowledgment, one data,
|
||||||
|
* and one execution status reply), this function can be overwritten to get the
|
||||||
|
* reply length of the next reply to read.
|
||||||
|
*/
|
||||||
|
virtual size_t getNextReplyLength(DeviceCommandId_t deviceCommand);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This is a helper method to facilitate updating entries
|
* @brief This is a helper method to facilitate updating entries
|
||||||
* in the reply map.
|
* in the reply map.
|
||||||
@ -953,7 +967,7 @@ protected:
|
|||||||
* - NO_REPLY_EXPECTED if there was no reply found. This is not an
|
* - NO_REPLY_EXPECTED if there was no reply found. This is not an
|
||||||
* error case as many commands do not expect a reply.
|
* error case as many commands do not expect a reply.
|
||||||
*/
|
*/
|
||||||
virtual ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator cmd,
|
virtual ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator command,
|
||||||
uint8_t expectedReplies = 1, bool useAlternateId = false,
|
uint8_t expectedReplies = 1, bool useAlternateId = false,
|
||||||
DeviceCommandId_t alternateReplyID = 0);
|
DeviceCommandId_t alternateReplyID = 0);
|
||||||
|
|
||||||
|
@ -16,9 +16,9 @@ ReturnValue_t HealthDevice::performOperation(uint8_t opCode) {
|
|||||||
CommandMessage command;
|
CommandMessage command;
|
||||||
ReturnValue_t result = commandQueue->receiveMessage(&command);
|
ReturnValue_t result = commandQueue->receiveMessage(&command);
|
||||||
if (result == HasReturnvaluesIF::RETURN_OK) {
|
if (result == HasReturnvaluesIF::RETURN_OK) {
|
||||||
healthHelper.handleHealthCommand(&command);
|
result = healthHelper.handleHealthCommand(&command);
|
||||||
}
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t HealthDevice::initialize() {
|
ReturnValue_t HealthDevice::initialize() {
|
||||||
|
@ -1,12 +1,30 @@
|
|||||||
|
Configuring the FSFW
|
||||||
## Configuring the FSFW
|
======
|
||||||
|
|
||||||
The FSFW can be configured via the `fsfwconfig` folder. A template folder has
|
The FSFW can be configured via the `fsfwconfig` folder. A template folder has
|
||||||
been provided to have a starting point for this. The folder should be added
|
been provided to have a starting point for this. The folder should be added
|
||||||
to the include path.
|
to the include path. The primary configuration file is the `FSFWConfig.h` folder. Some
|
||||||
|
of the available options will be explained in more detail here.
|
||||||
|
|
||||||
|
# Auto-Translation of Events
|
||||||
|
|
||||||
### Configuring the Event Manager
|
The FSFW allows the automatic translation of events, which allows developers to track triggered
|
||||||
|
events directly via console output. Using this feature requires:
|
||||||
|
|
||||||
|
1. `FSFW_OBJ_EVENT_TRANSLATION` set to 1 in the configuration file.
|
||||||
|
2. Special auto-generated translation files which translate event IDs and object IDs into
|
||||||
|
human readable strings. These files can be generated using the
|
||||||
|
[modgen Python scripts](https://git.ksat-stuttgart.de/source/modgen.git).
|
||||||
|
3. The generated translation files for the object IDs should be named `translatesObjects.cpp`
|
||||||
|
and `translateObjects.h` and should be copied to the `fsfwconfig/objects` folder
|
||||||
|
4. The generated translation files for the event IDs should be named `translateEvents.cpp` and
|
||||||
|
`translateEvents.h` and should be copied to the `fsfwconfig/events` folder
|
||||||
|
|
||||||
|
An example implementations of these translation file generators can be found as part
|
||||||
|
of the [SOURCE project here](https://git.ksat-stuttgart.de/source/sourceobsw/-/tree/development/generators)
|
||||||
|
or the [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/generators)
|
||||||
|
|
||||||
|
## Configuring the Event Manager
|
||||||
|
|
||||||
The number of allowed subscriptions can be modified with the following
|
The number of allowed subscriptions can be modified with the following
|
||||||
parameters:
|
parameters:
|
||||||
@ -18,4 +36,5 @@ static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240;
|
|||||||
static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120;
|
static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120;
|
||||||
static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
|
static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ A nullptr check of the returning Pointer must be done. This function is based on
|
|||||||
```cpp
|
```cpp
|
||||||
template <typename T> T* ObjectManagerIF::get( object_id_t id )
|
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.
|
* A typical way to create all objects on startup is a handing a static produce function to the
|
||||||
By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards.
|
ObjectManager on creation. By calling objectManager->initialize() the produce function will be
|
||||||
|
called and all SystemObjects will be initialized afterwards.
|
||||||
|
|
||||||
### Event Manager
|
### Event Manager
|
||||||
|
|
||||||
@ -36,14 +37,19 @@ By calling objectManager->initialize() the produce function will be called and a
|
|||||||
|
|
||||||
### Stores
|
### Stores
|
||||||
|
|
||||||
* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can
|
* The message based communication can only exchange a few bytes of information inside the message
|
||||||
be exchanged with Stores. With this, only the store address must be exchanged in the message.
|
itself. Therefore, additional information can be exchanged with Stores. With this, only the
|
||||||
* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC Store is used. For outgoing TM a TM store is used.
|
store address must be exchanged in the message.
|
||||||
|
* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC
|
||||||
|
Store is used. For outgoing TM a TM store is used.
|
||||||
* All of them should use the Thread Safe Class storagemanager/PoolManager
|
* All of them should use the Thread Safe Class storagemanager/PoolManager
|
||||||
|
|
||||||
### Tasks
|
### Tasks
|
||||||
|
|
||||||
There are two different types of tasks:
|
There are two different types of tasks:
|
||||||
* The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks.
|
* The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the
|
||||||
* FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence
|
insertion to the Tasks.
|
||||||
|
* FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for
|
||||||
|
DeviceHandlers, where polling should be in a defined order. An example can be found in
|
||||||
|
`defaultcfg/fsfwconfig/pollingSequence` folder
|
||||||
|
|
||||||
|
@ -1,99 +1,135 @@
|
|||||||
# High-level overview
|
High-level overview
|
||||||
|
======
|
||||||
|
|
||||||
## Structure
|
# Structure
|
||||||
|
|
||||||
The general structure is driven by the usage of interfaces provided by objects.
|
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 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.
|
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.
|
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.
|
Dynamic Allocation after initialization is discouraged and different solutions are provided in the
|
||||||
The fsfw uses run-time type information but exceptions are not allowed.
|
FSFW to achieve that. The fsfw uses run-time type information but exceptions are not allowed.
|
||||||
|
|
||||||
### Failure Handling
|
# Failure Handling
|
||||||
|
|
||||||
Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong.
|
Functions should return a defined `ReturnValue_t` to signal to the caller that something has
|
||||||
Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used.
|
gone wrong. Returnvalues must be unique. For this the function `HasReturnvaluesIF::makeReturnCode`
|
||||||
The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds.
|
or the macro `MAKE_RETURN` can be used. The `CLASS_ID` is a unique id for that type of object.
|
||||||
|
See `returnvalues/FwClassIds` folder. The user can add custom `CLASS_ID`s via the
|
||||||
|
`fsfwconfig` folder.
|
||||||
|
|
||||||
### OSAL
|
# OSAL
|
||||||
|
|
||||||
The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS.
|
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 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.
|
The [OSAL README](doc/README-osal.md#top) provides more detailed information on provided components
|
||||||
|
and how to use them.
|
||||||
|
|
||||||
### Core Components
|
# Core Components
|
||||||
|
|
||||||
The FSFW has following core components. More detailed informations can be found in the
|
The FSFW has following core components. More detailed informations can be found in the
|
||||||
[core component section](doc/README-core.md#top):
|
[core component section](doc/README-core.md#top):
|
||||||
|
|
||||||
1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots
|
1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks
|
||||||
2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles.
|
with fixed timeslots
|
||||||
3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without
|
2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID
|
||||||
using dynamic memory allocation. These pools are allocated up front.
|
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
|
3. Clock: This module provided common time related functions
|
||||||
4. EventManager: This module allows routing of events generated by `SystemObjects`
|
4. EventManager: This module allows routing of events generated by `SystemObjects`
|
||||||
5. HealthTable: A component which stores the health states of objects
|
5. HealthTable: A component which stores the health states of objects
|
||||||
|
|
||||||
### Static IDs in the framework
|
# Static IDs in the framework
|
||||||
|
|
||||||
Some parts of the framework use a static routing address for communication.
|
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()".
|
An example setup of ids can be found in the example config in `defaultcft/fsfwconfig/objects`
|
||||||
|
inside the function `Factory::setStaticFrameworkObjectIds()`.
|
||||||
|
|
||||||
### Events
|
# Events
|
||||||
|
|
||||||
Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues.
|
Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT.
|
||||||
Every object that needs own EventIds has to get a unique SUBSYSTEM_ID.
|
This works analog to the returnvalues. Every object that needs own EventIds has to get a
|
||||||
Every SystemObject can call triggerEvent from the parent class.
|
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.
|
Therefore, event messages contain the specific EventId and the objectId of the object that
|
||||||
|
has triggered.
|
||||||
|
|
||||||
### Internal Communication
|
# Internal Communication
|
||||||
|
|
||||||
Components communicate mostly over Message through Queues.
|
Components communicate mostly via Messages through Queues.
|
||||||
Those queues are created by calling the singleton QueueFactory::instance()->create().
|
Those queues are created by calling the singleton `QueueFactory::instance()->create()` which
|
||||||
|
will create `MessageQueue` instances for the used OSAL.
|
||||||
|
|
||||||
### External Communication
|
# External Communication
|
||||||
|
|
||||||
The external communication with the mission control system is mostly up to the user implementation.
|
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 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.
|
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
|
## TMTC Communication
|
||||||
|
|
||||||
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.
|
The FSFW provides some components to facilitate TMTC handling via the PUS commands.
|
||||||
If Space Packets are used, a timestamper must be created.
|
For example, a UDP or TCP PUS server socket can be opened on a specific port using the
|
||||||
An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short.
|
files located in `osal/common`. The FSFW example uses this functionality to allow sending telecommands
|
||||||
|
and receiving telemetry using the [TMTC commander application](https://github.com/spacefisch/tmtccmd).
|
||||||
|
Simple commands like the PUS Service 17 ping service can be tested by simply running the
|
||||||
|
`tmtc_client_cli.py` or `tmtc_client_gui.py` utility in
|
||||||
|
the [example tmtc folder](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/tmtc)
|
||||||
|
while the `fsfw_example` application is running.
|
||||||
|
|
||||||
#### Device Handlers
|
More generally, any class responsible for handling incoming telecommands and sending telemetry
|
||||||
|
can implement the generic `TmTcBridge` class located in `tmtcservices`. Many applications
|
||||||
|
also use a dedicated polling task for reading telecommands which passes telecommands
|
||||||
|
to the `TmTcBridge` implementation.
|
||||||
|
|
||||||
|
## 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 has to be provided by the user.
|
||||||
|
An example can be found in the `timemanager` folder, which uses `CCSDSTime::CDS_short`.
|
||||||
|
|
||||||
|
# Device Handlers
|
||||||
|
|
||||||
DeviceHandlers are another important component of the FSFW.
|
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.
|
The idea is, to have a software counterpart of every physical device to provide a simple mode,
|
||||||
By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware.
|
health and commanding interface. By separating the underlying Communication Interface with
|
||||||
The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction.
|
`DeviceCommunicationIF`, a device handler (DH) can be tested on different hardware.
|
||||||
Device Handlers can be created by overriding `DeviceHandlerBase`.
|
The DH has mechanisms to monitor the communication with the physical device which allow
|
||||||
A standard FDIR component for the DH will be created automatically but can be overwritten by the user.
|
for FDIR reaction. Device Handlers can be created by implementing `DeviceHandlerBase`.
|
||||||
More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top).
|
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
|
# Modes and Health
|
||||||
|
|
||||||
The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components.
|
The two interfaces `HasModesIF` and `HasHealthIF` provide access for commanding and monitoring
|
||||||
On-board Mode Management is implement in hierarchy system.
|
of components. On-board Mode Management is implement in hierarchy system.
|
||||||
DeviceHandlers and Controllers are the lowest part of the hierarchy.
|
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.
|
The next layer are Assemblies. Those assemblies act as a component which handle
|
||||||
Assemblies share a common core with the next level which are the Subsystems.
|
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.
|
Those Assemblies are intended to act as auto-generated components from a database which describes
|
||||||
The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded.
|
the subsystem modes. The definitions contain transition and target tables which contain the DH,
|
||||||
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
The on-board FDIR uses the health state for isolation and recovery.
|
||||||
|
|
||||||
## Unit Tests
|
# 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
|
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)
|
[`fsfw_tests` reposoitory](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests)
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
#include "EventManager.h"
|
#include "EventManager.h"
|
||||||
#include "EventMessage.h"
|
#include "EventMessage.h"
|
||||||
|
|
||||||
#include <FSFWConfig.h>
|
|
||||||
#include "../serviceinterface/ServiceInterfaceStream.h"
|
|
||||||
#include "../ipc/QueueFactory.h"
|
#include "../ipc/QueueFactory.h"
|
||||||
#include "../ipc/MutexFactory.h"
|
#include "../ipc/MutexFactory.h"
|
||||||
|
|
||||||
@ -115,53 +113,6 @@ ReturnValue_t EventManager::unsubscribeFromEventRange(MessageQueueId_t listener,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FSFW_OBJ_EVENT_TRANSLATION == 1
|
|
||||||
|
|
||||||
void EventManager::printEvent(EventMessage* message) {
|
|
||||||
const char *string = 0;
|
|
||||||
switch (message->getSeverity()) {
|
|
||||||
case severity::INFO:
|
|
||||||
#if DEBUG_INFO_EVENT == 1
|
|
||||||
string = translateObject(message->getReporter());
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::info << "EVENT: ";
|
|
||||||
if (string != 0) {
|
|
||||||
sif::info << string;
|
|
||||||
} else {
|
|
||||||
sif::info << "0x" << std::hex << message->getReporter() << std::dec;
|
|
||||||
}
|
|
||||||
sif::info << " reported " << translateEvents(message->getEvent()) << " ("
|
|
||||||
<< std::dec << message->getEventId() << std::hex << ") P1: 0x"
|
|
||||||
<< message->getParameter1() << " P2: 0x"
|
|
||||||
<< message->getParameter2() << std::dec << std::endl;
|
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
|
||||||
#endif /* DEBUG_INFO_EVENT == 1 */
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
string = translateObject(message->getReporter());
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::debug << "EventManager: ";
|
|
||||||
if (string != 0) {
|
|
||||||
sif::debug << string;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
sif::debug << "0x" << std::hex << message->getReporter() << std::dec;
|
|
||||||
}
|
|
||||||
sif::debug << " reported " << translateEvents(message->getEvent())
|
|
||||||
<< " (" << std::dec << message->getEventId() << ") "
|
|
||||||
<< std::endl;
|
|
||||||
sif::debug << std::hex << "P1 Hex: 0x" << message->getParameter1()
|
|
||||||
<< ", P1 Dec: " << std::dec << message->getParameter1()
|
|
||||||
<< std::endl;
|
|
||||||
sif::debug << std::hex << "P2 Hex: 0x" << message->getParameter2()
|
|
||||||
<< ", P2 Dec: " << std::dec << message->getParameter2()
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void EventManager::lockMutex() {
|
void EventManager::lockMutex() {
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
}
|
}
|
||||||
@ -175,3 +126,85 @@ void EventManager::setMutexTimeout(MutexIF::TimeoutType timeoutType,
|
|||||||
this->timeoutType = timeoutType;
|
this->timeoutType = timeoutType;
|
||||||
this->timeoutMs = timeoutMs;
|
this->timeoutMs = timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FSFW_OBJ_EVENT_TRANSLATION == 1
|
||||||
|
|
||||||
|
void EventManager::printEvent(EventMessage* message) {
|
||||||
|
switch (message->getSeverity()) {
|
||||||
|
case severity::INFO: {
|
||||||
|
#if FSFW_DEBUG_INFO == 1
|
||||||
|
printUtility(sif::OutputTypes::OUT_INFO, message);
|
||||||
|
#endif /* DEBUG_INFO_EVENT == 1 */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
printUtility(sif::OutputTypes::OUT_DEBUG, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EventManager::printUtility(sif::OutputTypes printType, EventMessage *message) {
|
||||||
|
const char *string = 0;
|
||||||
|
if(printType == sif::OutputTypes::OUT_INFO) {
|
||||||
|
string = translateObject(message->getReporter());
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::info << "EventManager: ";
|
||||||
|
if (string != 0) {
|
||||||
|
sif::info << string;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::info << "0x" << std::hex << std::setw(8) << std::setfill('0') <<
|
||||||
|
message->getReporter() << std::setfill(' ') << std::dec;
|
||||||
|
}
|
||||||
|
sif::info << " report event with ID " << message->getEventId() << std::endl;
|
||||||
|
sif::info << std::hex << "P1 Hex: 0x" << message->getParameter1() <<
|
||||||
|
" | P1 Dec: " << std::dec << message->getParameter1() << std::hex <<
|
||||||
|
" | P2 Hex: 0x" << message->getParameter2() << " | P2 Dec: " << std::dec <<
|
||||||
|
message->getParameter2() << std::endl;
|
||||||
|
#else
|
||||||
|
if (string != 0) {
|
||||||
|
sif::printInfo("Event Manager: %s reported event with ID %d\n", string,
|
||||||
|
message->getEventId());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::printInfo("Event Manager: Reporter ID 0x%08x reported event with ID %d\n",
|
||||||
|
message->getReporter(), message->getEventId());
|
||||||
|
}
|
||||||
|
sif::printInfo("P1 Hex: 0x%x | P1 Dec: %d | P2 Hex: 0x%x | P2 Dec: %d\n",
|
||||||
|
message->getParameter1(), message->getParameter1(),
|
||||||
|
message->getParameter2(), message->getParameter2());
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 0 */
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
string = translateObject(message->getReporter());
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::debug << "EventManager: ";
|
||||||
|
if (string != 0) {
|
||||||
|
sif::debug << string;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::debug << "0x" << std::hex << std::setw(8) << std::setfill('0') <<
|
||||||
|
message->getReporter() << std::setfill(' ') << std::dec;
|
||||||
|
}
|
||||||
|
sif::debug << " report event with ID " << message->getEventId() << std::endl;
|
||||||
|
sif::debug << std::hex << "P1 Hex: 0x" << message->getParameter1() <<
|
||||||
|
" | P1 Dec: " << std::dec << message->getParameter1() << std::hex <<
|
||||||
|
" | P2 Hex: 0x" << message->getParameter2() << " | P2 Dec: " << std::dec <<
|
||||||
|
message->getParameter2() << std::endl;
|
||||||
|
#else
|
||||||
|
if (string != 0) {
|
||||||
|
sif::printDebug("Event Manager: %s reported event with ID %d\n", string,
|
||||||
|
message->getEventId());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::printDebug("Event Manager: Reporter ID 0x%08x reported event with ID %d\n",
|
||||||
|
message->getReporter(), message->getEventId());
|
||||||
|
}
|
||||||
|
sif::printDebug("P1 Hex: 0x%x | P1 Dec: %d | P2 Hex: 0x%x | P2 Dec: %d\n",
|
||||||
|
message->getParameter1(), message->getParameter1(),
|
||||||
|
message->getParameter2(), message->getParameter2());
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 0 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FSFW_OBJ_EVENT_TRANSLATION == 1 */
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <FSFWConfig.h>
|
#include <FSFWConfig.h>
|
||||||
|
|
||||||
|
#include "../serviceinterface/ServiceInterface.h"
|
||||||
#include "../objectmanager/SystemObject.h"
|
#include "../objectmanager/SystemObject.h"
|
||||||
#include "../storagemanager/LocalPool.h"
|
#include "../storagemanager/LocalPool.h"
|
||||||
#include "../tasks/ExecutableObjectIF.h"
|
#include "../tasks/ExecutableObjectIF.h"
|
||||||
@ -67,6 +68,7 @@ protected:
|
|||||||
|
|
||||||
#if FSFW_OBJ_EVENT_TRANSLATION == 1
|
#if FSFW_OBJ_EVENT_TRANSLATION == 1
|
||||||
void printEvent(EventMessage *message);
|
void printEvent(EventMessage *message);
|
||||||
|
void printUtility(sif::OutputTypes printType, EventMessage* message);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void lockMutex();
|
void lockMutex();
|
||||||
|
@ -109,6 +109,6 @@ bool EventMessage::isClearedEventMessage() {
|
|||||||
return getEvent() == INVALID_EVENT;
|
return getEvent() == INVALID_EVENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t EventMessage::getMinimumMessageSize() {
|
size_t EventMessage::getMinimumMessageSize() const {
|
||||||
return EVENT_MESSAGE_SIZE;
|
return EVENT_MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
static const Event INVALID_EVENT = 0;
|
static const Event INVALID_EVENT = 0;
|
||||||
virtual size_t getMinimumMessageSize();
|
virtual size_t getMinimumMessageSize() const override;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,30 +1,37 @@
|
|||||||
#ifndef FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
|
#ifndef FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
|
||||||
#define FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
|
#define FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
namespace SUBSYSTEM_ID {
|
namespace SUBSYSTEM_ID {
|
||||||
enum {
|
enum: uint8_t {
|
||||||
MEMORY = 22,
|
MEMORY = 22,
|
||||||
OBSW = 26,
|
OBSW = 26,
|
||||||
CDH = 28,
|
CDH = 28,
|
||||||
TCS_1 = 59,
|
TCS_1 = 59,
|
||||||
PCDU_1 = 42,
|
PCDU_1 = 42,
|
||||||
PCDU_2 = 43,
|
PCDU_2 = 43,
|
||||||
HEATER = 50,
|
HEATER = 50,
|
||||||
T_SENSORS = 52,
|
T_SENSORS = 52,
|
||||||
FDIR = 70,
|
FDIR = 70,
|
||||||
FDIR_1 = 71,
|
FDIR_1 = 71,
|
||||||
FDIR_2 = 72,
|
FDIR_2 = 72,
|
||||||
HK = 73,
|
HK = 73,
|
||||||
SYSTEM_MANAGER = 74,
|
SYSTEM_MANAGER = 74,
|
||||||
SYSTEM_MANAGER_1 = 75,
|
SYSTEM_MANAGER_1 = 75,
|
||||||
SYSTEM_1 = 79,
|
SYSTEM_1 = 79,
|
||||||
PUS_SERVICE_1 = 80,
|
PUS_SERVICE_1 = 80,
|
||||||
PUS_SERVICE_9 = 89,
|
PUS_SERVICE_2 = 82,
|
||||||
PUS_SERVICE_17 = 97,
|
PUS_SERVICE_3 = 83,
|
||||||
FW_SUBSYSTEM_ID_RANGE
|
PUS_SERVICE_5 = 85,
|
||||||
|
PUS_SERVICE_6 = 86,
|
||||||
|
PUS_SERVICE_8 = 88,
|
||||||
|
PUS_SERVICE_9 = 89,
|
||||||
|
PUS_SERVICE_17 = 97,
|
||||||
|
PUS_SERVICE_23 = 103,
|
||||||
|
|
||||||
|
FW_SUBSYSTEM_ID_RANGE
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_ */
|
#endif /* FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_ */
|
||||||
|
@ -60,7 +60,7 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
|
|||||||
#else
|
#else
|
||||||
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
|
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
|
||||||
// plus line break plus small safety margin.
|
// plus line break plus small safety margin.
|
||||||
char printBuffer[(size + 1) * 7 + 1];
|
char printBuffer[(size + 1) * 7 + 1] = {};
|
||||||
size_t currentPos = 0;
|
size_t currentPos = 0;
|
||||||
for(size_t i = 0; i < size; i++) {
|
for(size_t i = 0; i < size; i++) {
|
||||||
// To avoid buffer overflows.
|
// To avoid buffer overflows.
|
||||||
@ -76,7 +76,9 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if FSFW_DISABLE_PRINTOUT == 0
|
||||||
printf("[%s]\n", printBuffer);
|
printf("[%s]\n", printBuffer);
|
||||||
|
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +103,7 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
|
|||||||
#else
|
#else
|
||||||
// General format: 32, 243, -12 so it is number of chars times 5
|
// General format: 32, 243, -12 so it is number of chars times 5
|
||||||
// plus line break plus small safety margin.
|
// plus line break plus small safety margin.
|
||||||
char printBuffer[(size + 1) * 5 + 1];
|
char printBuffer[(size + 1) * 5 + 1] = {};
|
||||||
size_t currentPos = 0;
|
size_t currentPos = 0;
|
||||||
for(size_t i = 0; i < size; i++) {
|
for(size_t i = 0; i < size; i++) {
|
||||||
// To avoid buffer overflows.
|
// To avoid buffer overflows.
|
||||||
@ -117,7 +119,9 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if FSFW_DISABLE_PRINTOUT == 0
|
||||||
printf("[%s]\n", printBuffer);
|
printf("[%s]\n", printBuffer);
|
||||||
|
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include "HealthTable.h"
|
#include "HealthTable.h"
|
||||||
#include "../ipc/MutexHelper.h"
|
#include "../ipc/MutexGuard.h"
|
||||||
#include "../ipc/MutexFactory.h"
|
#include "../ipc/MutexFactory.h"
|
||||||
#include "../serialize/SerializeAdapter.h"
|
#include "../serialize/SerializeAdapter.h"
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ ReturnValue_t HealthTable::registerObject(object_id_t object,
|
|||||||
|
|
||||||
void HealthTable::setHealth(object_id_t object,
|
void HealthTable::setHealth(object_id_t object,
|
||||||
HasHealthIF::HealthState newState) {
|
HasHealthIF::HealthState newState) {
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
HealthMap::iterator iter = healthMap.find(object);
|
HealthMap::iterator iter = healthMap.find(object);
|
||||||
if (iter != healthMap.end()) {
|
if (iter != healthMap.end()) {
|
||||||
iter->second = newState;
|
iter->second = newState;
|
||||||
@ -40,7 +40,7 @@ void HealthTable::setHealth(object_id_t object,
|
|||||||
|
|
||||||
HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
|
HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
|
||||||
HasHealthIF::HealthState state = HasHealthIF::HEALTHY;
|
HasHealthIF::HealthState state = HasHealthIF::HEALTHY;
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
HealthMap::iterator iter = healthMap.find(object);
|
HealthMap::iterator iter = healthMap.find(object);
|
||||||
if (iter != healthMap.end()) {
|
if (iter != healthMap.end()) {
|
||||||
state = iter->second;
|
state = iter->second;
|
||||||
@ -49,7 +49,7 @@ HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HealthTable::hasHealth(object_id_t object) {
|
bool HealthTable::hasHealth(object_id_t object) {
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
HealthMap::iterator iter = healthMap.find(object);
|
HealthMap::iterator iter = healthMap.find(object);
|
||||||
if (iter != healthMap.end()) {
|
if (iter != healthMap.end()) {
|
||||||
return true;
|
return true;
|
||||||
@ -58,35 +58,51 @@ bool HealthTable::hasHealth(object_id_t object) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t HealthTable::getPrintSize() {
|
size_t HealthTable::getPrintSize() {
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
uint32_t size = healthMap.size() * sizeof(object_id_t) +
|
uint32_t size = healthMap.size() * sizeof(object_id_t) +
|
||||||
sizeof(HasHealthIF::HealthState) + sizeof(uint16_t);
|
sizeof(HasHealthIF::HealthState) + sizeof(uint16_t);
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
|
void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
uint16_t count = healthMap.size();
|
uint16_t count = healthMap.size();
|
||||||
SerializeAdapter::serialize(&count,
|
ReturnValue_t result = SerializeAdapter::serialize(&count,
|
||||||
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
|
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "HealthTable::printAll: Serialization of health table failed" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("HealthTable::printAll: Serialization of health table failed\n");
|
||||||
|
#endif
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (const auto& health: healthMap) {
|
for (const auto& health: healthMap) {
|
||||||
SerializeAdapter::serialize(&health.first,
|
result = SerializeAdapter::serialize(&health.first,
|
||||||
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
|
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
uint8_t healthValue = health.second;
|
uint8_t healthValue = health.second;
|
||||||
SerializeAdapter::serialize(&healthValue, &pointer, &size,
|
result = SerializeAdapter::serialize(&healthValue, &pointer, &size,
|
||||||
maxSize, SerializeIF::Endianness::BIG);
|
maxSize, SerializeIF::Endianness::BIG);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) {
|
ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) {
|
||||||
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
|
||||||
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
|
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
if (reset) {
|
if (reset) {
|
||||||
mapIterator = healthMap.begin();
|
mapIterator = healthMap.begin();
|
||||||
}
|
}
|
||||||
if (mapIterator == healthMap.end()) {
|
if (mapIterator == healthMap.end()) {
|
||||||
result = HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
*value = *mapIterator;
|
*value = *mapIterator;
|
||||||
mapIterator++;
|
mapIterator++;
|
||||||
|
@ -84,15 +84,21 @@ void HousekeepingMessage::setCollectionIntervalModificationCommand(
|
|||||||
else {
|
else {
|
||||||
command->setCommand(MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL);
|
command->setCommand(MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL);
|
||||||
}
|
}
|
||||||
command->setParameter3(collectionInterval);
|
|
||||||
|
/* Raw storage of the float in the message. Do not use setParameter3, does
|
||||||
|
implicit conversion to integer type! */
|
||||||
|
std::memcpy(command->getData() + 2 * sizeof(uint32_t), &collectionInterval,
|
||||||
|
sizeof(collectionInterval));
|
||||||
|
|
||||||
setSid(command, sid);
|
setSid(command, sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
sid_t HousekeepingMessage::getCollectionIntervalModificationCommand(
|
sid_t HousekeepingMessage::getCollectionIntervalModificationCommand(
|
||||||
const CommandMessage* command, float* newCollectionInterval) {
|
const CommandMessage* command, float* newCollectionInterval) {
|
||||||
|
|
||||||
if(newCollectionInterval != nullptr) {
|
if(newCollectionInterval != nullptr) {
|
||||||
*newCollectionInterval = command->getParameter3();
|
std::memcpy(newCollectionInterval, command->getData() + 2 * sizeof(uint32_t),
|
||||||
|
sizeof(*newCollectionInterval));
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSid(command);
|
return getSid(command);
|
||||||
@ -151,7 +157,8 @@ void HousekeepingMessage::clear(CommandMessage* message) {
|
|||||||
case(DIAGNOSTICS_REPORT):
|
case(DIAGNOSTICS_REPORT):
|
||||||
case(HK_DEFINITIONS_REPORT):
|
case(HK_DEFINITIONS_REPORT):
|
||||||
case(DIAGNOSTICS_DEFINITION_REPORT):
|
case(DIAGNOSTICS_DEFINITION_REPORT):
|
||||||
case(UPDATE_SNAPSHOT_SET): {
|
case(UPDATE_SNAPSHOT_SET):
|
||||||
|
case(UPDATE_SNAPSHOT_VARIABLE): {
|
||||||
store_address_t storeId;
|
store_address_t storeId;
|
||||||
getHkDataReply(message, &storeId);
|
getHkDataReply(message, &storeId);
|
||||||
StorageManagerIF *ipcStore = objectManager->get<StorageManagerIF>(
|
StorageManagerIF *ipcStore = objectManager->get<StorageManagerIF>(
|
||||||
|
@ -11,7 +11,8 @@
|
|||||||
* @brief This helper class will be used to serialize and deserialize update housekeeping packets
|
* @brief This helper class will be used to serialize and deserialize update housekeeping packets
|
||||||
* into the store.
|
* into the store.
|
||||||
*/
|
*/
|
||||||
class HousekeepingSnapshot: public SerializeIF {
|
class HousekeepingSnapshot:
|
||||||
|
public SerializeIF {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,6 +37,17 @@ public:
|
|||||||
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
||||||
updateData(dataSetPtr) {};
|
updateData(dataSetPtr) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update packet constructor for pool variables.
|
||||||
|
* @param timeStamp
|
||||||
|
* @param timeStampSize
|
||||||
|
* @param dataSetPtr
|
||||||
|
*/
|
||||||
|
HousekeepingSnapshot(CCSDSTime::CDS_short* cdsShort, LocalPoolObjectBase* dataSetPtr):
|
||||||
|
timeStamp(reinterpret_cast<uint8_t*>(cdsShort)),
|
||||||
|
timeStampSize(sizeof(CCSDSTime::CDS_short)), updateData(dataSetPtr) {};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update packet constructor for pool variables.
|
* Update packet constructor for pool variables.
|
||||||
* @param timeStamp
|
* @param timeStamp
|
||||||
@ -47,8 +59,8 @@ public:
|
|||||||
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
timeStamp(timeStamp), timeStampSize(timeStampSize),
|
||||||
updateData(dataSetPtr) {};
|
updateData(dataSetPtr) {};
|
||||||
|
|
||||||
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size,
|
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
|
||||||
size_t maxSize, Endianness streamEndianness) const {
|
Endianness streamEndianness) const {
|
||||||
if(timeStamp != nullptr) {
|
if(timeStamp != nullptr) {
|
||||||
/* Endianness will always be MACHINE, so we can simply use memcpy
|
/* Endianness will always be MACHINE, so we can simply use memcpy
|
||||||
here. */
|
here. */
|
||||||
|
@ -4,46 +4,87 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
PeriodicHousekeepingHelper::PeriodicHousekeepingHelper(
|
PeriodicHousekeepingHelper::PeriodicHousekeepingHelper(
|
||||||
LocalPoolDataSetBase* owner): owner(owner) {}
|
LocalPoolDataSetBase* owner): owner(owner) {}
|
||||||
|
|
||||||
|
|
||||||
void PeriodicHousekeepingHelper::initialize(float collectionInterval,
|
void PeriodicHousekeepingHelper::initialize(float collectionInterval,
|
||||||
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
|
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
|
||||||
uint8_t nonDiagIntervalFactor) {
|
this->minimumPeriodicInterval = minimumPeriodicInterval;
|
||||||
this->minimumPeriodicInterval = minimumPeriodicInterval;
|
this->nonDiagIntervalFactor = nonDiagIntervalFactor;
|
||||||
if(not isDiagnostics) {
|
collectionIntervalTicks = intervalSecondsToIntervalTicks(collectionInterval);
|
||||||
this->minimumPeriodicInterval = this->minimumPeriodicInterval *
|
/* This will cause a checkOpNecessary call to be true immediately. I think it's okay
|
||||||
nonDiagIntervalFactor;
|
if a HK packet is generated immediately instead of waiting one generation cycle. */
|
||||||
}
|
internalTickCounter = collectionIntervalTicks;
|
||||||
collectionIntervalTicks = intervalSecondsToInterval(collectionInterval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() {
|
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() const {
|
||||||
return intervalToIntervalSeconds(collectionIntervalTicks);
|
return intervalTicksToSeconds(collectionIntervalTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PeriodicHousekeepingHelper::checkOpNecessary() {
|
bool PeriodicHousekeepingHelper::checkOpNecessary() {
|
||||||
if(internalTickCounter >= collectionIntervalTicks) {
|
if(internalTickCounter >= collectionIntervalTicks) {
|
||||||
internalTickCounter = 1;
|
internalTickCounter = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
internalTickCounter++;
|
internalTickCounter++;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PeriodicHousekeepingHelper::intervalSecondsToInterval(
|
uint32_t PeriodicHousekeepingHelper::intervalSecondsToIntervalTicks(
|
||||||
float collectionIntervalSeconds) {
|
float collectionIntervalSeconds) {
|
||||||
return std::ceil(collectionIntervalSeconds * 1000
|
if(owner == nullptr) {
|
||||||
/ minimumPeriodicInterval);
|
return 0;
|
||||||
|
}
|
||||||
|
bool isDiagnostics = owner->isDiagnostics();
|
||||||
|
|
||||||
|
/* Avoid division by zero */
|
||||||
|
if(minimumPeriodicInterval == 0) {
|
||||||
|
if(isDiagnostics) {
|
||||||
|
/* Perform operation each cycle */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nonDiagIntervalFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
dur_millis_t intervalInMs = collectionIntervalSeconds * 1000;
|
||||||
|
uint32_t divisor = minimumPeriodicInterval;
|
||||||
|
if(not isDiagnostics) {
|
||||||
|
/* We need to multiply the divisor because non-diagnostics only
|
||||||
|
allow a multiple of the minimum periodic interval */
|
||||||
|
divisor *= nonDiagIntervalFactor;
|
||||||
|
}
|
||||||
|
uint32_t ticks = std::ceil(static_cast<float>(intervalInMs) / divisor);
|
||||||
|
if(not isDiagnostics) {
|
||||||
|
/* Now we need to multiply the calculated ticks with the factor as as well
|
||||||
|
because the minimum tick count to generate a non-diagnostic is the factor itself.
|
||||||
|
|
||||||
|
Example calculation for non-diagnostic with
|
||||||
|
0.4 second interval and 0.2 second task interval.
|
||||||
|
Resultant tick count of 5 is equal to operation each second.
|
||||||
|
|
||||||
|
Examle calculation for non-diagnostic with 2.0 second interval and 0.2 second
|
||||||
|
task interval.
|
||||||
|
Resultant tick count of 10 is equal to operatin every 2 seconds.
|
||||||
|
|
||||||
|
Example calculation for diagnostic with 0.4 second interval and 0.3
|
||||||
|
second task interval. Resulting tick count of 2 is equal to operation
|
||||||
|
every 0.6 seconds. */
|
||||||
|
ticks *= nonDiagIntervalFactor;
|
||||||
|
}
|
||||||
|
return ticks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float PeriodicHousekeepingHelper::intervalToIntervalSeconds(
|
float PeriodicHousekeepingHelper::intervalTicksToSeconds(
|
||||||
uint32_t collectionInterval) {
|
uint32_t collectionInterval) const {
|
||||||
return static_cast<float>(collectionInterval *
|
/* Number of ticks times the minimum interval is in milliseconds, so we divide by 1000 to get
|
||||||
minimumPeriodicInterval);
|
the value in seconds */
|
||||||
|
return static_cast<float>(collectionInterval * minimumPeriodicInterval / 1000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeriodicHousekeepingHelper::changeCollectionInterval(
|
void PeriodicHousekeepingHelper::changeCollectionInterval(
|
||||||
float newIntervalSeconds) {
|
float newIntervalSeconds) {
|
||||||
collectionIntervalTicks = intervalSecondsToInterval(newIntervalSeconds);
|
collectionIntervalTicks = intervalSecondsToIntervalTicks(newIntervalSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,18 +10,19 @@ class PeriodicHousekeepingHelper {
|
|||||||
public:
|
public:
|
||||||
PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner);
|
PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner);
|
||||||
|
|
||||||
void initialize(float collectionInterval,
|
void initialize(float collectionInterval, dur_millis_t minimumPeriodicInterval,
|
||||||
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
|
uint8_t nonDiagIntervalFactor);
|
||||||
uint8_t nonDiagIntervalFactor);
|
|
||||||
|
|
||||||
void changeCollectionInterval(float newInterval);
|
void changeCollectionInterval(float newInterval);
|
||||||
float getCollectionIntervalInSeconds();
|
float getCollectionIntervalInSeconds() const;
|
||||||
bool checkOpNecessary();
|
bool checkOpNecessary();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LocalPoolDataSetBase* owner = nullptr;
|
LocalPoolDataSetBase* owner = nullptr;
|
||||||
|
uint8_t nonDiagIntervalFactor = 0;
|
||||||
|
|
||||||
uint32_t intervalSecondsToInterval(float collectionIntervalSeconds);
|
uint32_t intervalSecondsToIntervalTicks(float collectionIntervalSeconds);
|
||||||
float intervalToIntervalSeconds(uint32_t collectionInterval);
|
float intervalTicksToSeconds(uint32_t collectionInterval) const;
|
||||||
|
|
||||||
dur_millis_t minimumPeriodicInterval = 0;
|
dur_millis_t minimumPeriodicInterval = 0;
|
||||||
uint32_t internalTickCounter = 1;
|
uint32_t internalTickCounter = 1;
|
||||||
|
@ -3,19 +3,20 @@
|
|||||||
#include "../ipc/QueueFactory.h"
|
#include "../ipc/QueueFactory.h"
|
||||||
#include "../ipc/MutexFactory.h"
|
#include "../ipc/MutexFactory.h"
|
||||||
#include "../serviceinterface/ServiceInterface.h"
|
#include "../serviceinterface/ServiceInterface.h"
|
||||||
|
#include "../datapool/PoolReadGuard.h"
|
||||||
|
|
||||||
InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId,
|
InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId,
|
||||||
uint32_t messageQueueDepth): SystemObject(setObjectId),
|
uint32_t messageQueueDepth): SystemObject(setObjectId),
|
||||||
commandQueue(QueueFactory::instance()->
|
commandQueue(QueueFactory::instance()->
|
||||||
createMessageQueue(messageQueueDepth)),
|
createMessageQueue(messageQueueDepth)),
|
||||||
poolManager(this, commandQueue),
|
poolManager(this, commandQueue),
|
||||||
internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID),
|
internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID),
|
||||||
internalErrorDataset(this) {
|
internalErrorDataset(this) {
|
||||||
mutex = MutexFactory::instance()->createMutex();
|
mutex = MutexFactory::instance()->createMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
InternalErrorReporter::~InternalErrorReporter() {
|
InternalErrorReporter::~InternalErrorReporter() {
|
||||||
MutexFactory::instance()->deleteMutex(mutex);
|
MutexFactory::instance()->deleteMutex(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
|
void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
|
||||||
@ -23,126 +24,128 @@ void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
|
ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
|
||||||
internalErrorDataset.read(timeoutType, timeoutMs);
|
CommandMessage message;
|
||||||
|
ReturnValue_t result = commandQueue->receiveMessage(&message);
|
||||||
|
if(result != MessageQueueIF::EMPTY) {
|
||||||
|
poolManager.handleHousekeepingMessage(&message);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t newQueueHits = getAndResetQueueHits();
|
uint32_t newQueueHits = getAndResetQueueHits();
|
||||||
uint32_t newTmHits = getAndResetTmHits();
|
uint32_t newTmHits = getAndResetTmHits();
|
||||||
uint32_t newStoreHits = getAndResetStoreHits();
|
uint32_t newStoreHits = getAndResetStoreHits();
|
||||||
|
|
||||||
#if FSFW_VERBOSE_LEVEL == 1
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
if(diagnosticPrintout) {
|
if(diagnosticPrintout) {
|
||||||
if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) {
|
if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::debug << "InternalErrorReporter::performOperation: Errors "
|
sif::debug << "InternalErrorReporter::performOperation: Errors "
|
||||||
<< "occured!" << std::endl;
|
<< "occured!" << std::endl;
|
||||||
sif::debug << "Queue errors: " << newQueueHits << std::endl;
|
sif::debug << "Queue errors: " << newQueueHits << std::endl;
|
||||||
sif::debug << "TM errors: " << newTmHits << std::endl;
|
sif::debug << "TM errors: " << newTmHits << std::endl;
|
||||||
sif::debug << "Store errors: " << newStoreHits << std::endl;
|
sif::debug << "Store errors: " << newStoreHits << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printDebug("InternalErrorReporter::performOperation: Errors occured!\n");
|
sif::printDebug("InternalErrorReporter::performOperation: Errors occured!\n");
|
||||||
sif::printDebug("Queue errors: %lu\n", static_cast<unsigned int>(newQueueHits));
|
sif::printDebug("Queue errors: %lu\n", static_cast<unsigned int>(newQueueHits));
|
||||||
sif::printDebug("TM errors: %lu\n", static_cast<unsigned int>(newTmHits));
|
sif::printDebug("TM errors: %lu\n", static_cast<unsigned int>(newTmHits));
|
||||||
sif::printDebug("Store errors: %lu\n", static_cast<unsigned int>(newStoreHits));
|
sif::printDebug("Store errors: %lu\n", static_cast<unsigned int>(newStoreHits));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
internalErrorDataset.queueHits.value += newQueueHits;
|
{
|
||||||
internalErrorDataset.storeHits.value += newStoreHits;
|
PoolReadGuard readGuard(&internalErrorDataset);
|
||||||
internalErrorDataset.tmHits.value += newTmHits;
|
if(readGuard.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
|
||||||
internalErrorDataset.setValidity(true, true);
|
internalErrorDataset.queueHits.value += newQueueHits;
|
||||||
internalErrorDataset.commit(timeoutType, timeoutMs);
|
internalErrorDataset.storeHits.value += newStoreHits;
|
||||||
|
internalErrorDataset.tmHits.value += newTmHits;
|
||||||
|
internalErrorDataset.setValidity(true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
poolManager.performHkOperation();
|
poolManager.performHkOperation();
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
CommandMessage message;
|
|
||||||
ReturnValue_t result = commandQueue->receiveMessage(&message);
|
|
||||||
if(result != MessageQueueIF::EMPTY) {
|
|
||||||
poolManager.handleHousekeepingMessage(&message);
|
|
||||||
}
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::queueMessageNotSent() {
|
void InternalErrorReporter::queueMessageNotSent() {
|
||||||
incrementQueueHits();
|
incrementQueueHits();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::lostTm() {
|
void InternalErrorReporter::lostTm() {
|
||||||
incrementTmHits();
|
incrementTmHits();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getAndResetQueueHits() {
|
uint32_t InternalErrorReporter::getAndResetQueueHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = queueHits;
|
value = queueHits;
|
||||||
queueHits = 0;
|
queueHits = 0;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getQueueHits() {
|
uint32_t InternalErrorReporter::getQueueHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = queueHits;
|
value = queueHits;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::incrementQueueHits() {
|
void InternalErrorReporter::incrementQueueHits() {
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
queueHits++;
|
queueHits++;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getAndResetTmHits() {
|
uint32_t InternalErrorReporter::getAndResetTmHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = tmHits;
|
value = tmHits;
|
||||||
tmHits = 0;
|
tmHits = 0;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getTmHits() {
|
uint32_t InternalErrorReporter::getTmHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = tmHits;
|
value = tmHits;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::incrementTmHits() {
|
void InternalErrorReporter::incrementTmHits() {
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
tmHits++;
|
tmHits++;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::storeFull() {
|
void InternalErrorReporter::storeFull() {
|
||||||
incrementStoreHits();
|
incrementStoreHits();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getAndResetStoreHits() {
|
uint32_t InternalErrorReporter::getAndResetStoreHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = storeHits;
|
value = storeHits;
|
||||||
storeHits = 0;
|
storeHits = 0;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t InternalErrorReporter::getStoreHits() {
|
uint32_t InternalErrorReporter::getStoreHits() {
|
||||||
uint32_t value;
|
uint32_t value;
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
value = storeHits;
|
value = storeHits;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::incrementStoreHits() {
|
void InternalErrorReporter::incrementStoreHits() {
|
||||||
mutex->lockMutex(timeoutType, timeoutMs);
|
mutex->lockMutex(timeoutType, timeoutMs);
|
||||||
storeHits++;
|
storeHits++;
|
||||||
mutex->unlockMutex();
|
mutex->unlockMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
object_id_t InternalErrorReporter::getObjectId() const {
|
object_id_t InternalErrorReporter::getObjectId() const {
|
||||||
@ -155,14 +158,11 @@ MessageQueueId_t InternalErrorReporter::getCommandQueue() const {
|
|||||||
|
|
||||||
ReturnValue_t InternalErrorReporter::initializeLocalDataPool(
|
ReturnValue_t InternalErrorReporter::initializeLocalDataPool(
|
||||||
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
|
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
|
||||||
localDataPoolMap.emplace(errorPoolIds::TM_HITS,
|
localDataPoolMap.emplace(errorPoolIds::TM_HITS, new PoolEntry<uint32_t>());
|
||||||
new PoolEntry<uint32_t>());
|
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS, new PoolEntry<uint32_t>());
|
||||||
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS,
|
localDataPoolMap.emplace(errorPoolIds::STORE_HITS, new PoolEntry<uint32_t>());
|
||||||
new PoolEntry<uint32_t>());
|
poolManager.subscribeForPeriodicPacket(internalErrorSid, false, getPeriodicOperationFrequency(),
|
||||||
localDataPoolMap.emplace(errorPoolIds::STORE_HITS,
|
true);
|
||||||
new PoolEntry<uint32_t>());
|
|
||||||
poolManager.subscribeForPeriodicPacket(internalErrorSid, false,
|
|
||||||
getPeriodicOperationFrequency(), true);
|
|
||||||
internalErrorDataset.setValidity(true, true);
|
internalErrorDataset.setValidity(true, true);
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
@ -192,9 +192,9 @@ ReturnValue_t InternalErrorReporter::initializeAfterTaskCreation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InternalErrorReporter::setMutexTimeout(MutexIF::TimeoutType timeoutType,
|
void InternalErrorReporter::setMutexTimeout(MutexIF::TimeoutType timeoutType,
|
||||||
uint32_t timeoutMs) {
|
uint32_t timeoutMs) {
|
||||||
this->timeoutType = timeoutType;
|
this->timeoutType = timeoutType;
|
||||||
this->timeoutMs = timeoutMs;
|
this->timeoutMs = timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalDataPoolManager* InternalErrorReporter::getHkManagerHandle() {
|
LocalDataPoolManager* InternalErrorReporter::getHkManagerHandle() {
|
||||||
|
@ -17,77 +17,78 @@
|
|||||||
* All functions were kept virtual so this class can be extended easily
|
* All functions were kept virtual so this class can be extended easily
|
||||||
* to store custom internal errors (e.g. communication interface errors).
|
* to store custom internal errors (e.g. communication interface errors).
|
||||||
*/
|
*/
|
||||||
class InternalErrorReporter: public SystemObject,
|
class InternalErrorReporter:
|
||||||
public ExecutableObjectIF,
|
public SystemObject,
|
||||||
public InternalErrorReporterIF,
|
public ExecutableObjectIF,
|
||||||
public HasLocalDataPoolIF {
|
public InternalErrorReporterIF,
|
||||||
|
public HasLocalDataPoolIF {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
InternalErrorReporter(object_id_t setObjectId,
|
InternalErrorReporter(object_id_t setObjectId,
|
||||||
uint32_t messageQueueDepth = 5);
|
uint32_t messageQueueDepth = 5);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable diagnostic printout. Please note that this feature will
|
* Enable diagnostic printout. Please note that this feature will
|
||||||
* only work if DEBUG has been supplied to the build defines.
|
* only work if DEBUG has been supplied to the build defines.
|
||||||
* @param enable
|
* @param enable
|
||||||
*/
|
*/
|
||||||
void setDiagnosticPrintout(bool enable);
|
void setDiagnosticPrintout(bool enable);
|
||||||
|
|
||||||
void setMutexTimeout(MutexIF::TimeoutType timeoutType,
|
void setMutexTimeout(MutexIF::TimeoutType timeoutType,
|
||||||
uint32_t timeoutMs);
|
uint32_t timeoutMs);
|
||||||
|
|
||||||
virtual ~InternalErrorReporter();
|
virtual ~InternalErrorReporter();
|
||||||
|
|
||||||
virtual object_id_t getObjectId() const override;
|
virtual object_id_t getObjectId() const override;
|
||||||
virtual MessageQueueId_t getCommandQueue() const override;
|
virtual MessageQueueId_t getCommandQueue() const override;
|
||||||
virtual ReturnValue_t initializeLocalDataPool(
|
virtual ReturnValue_t initializeLocalDataPool(
|
||||||
localpool::DataPool& localDataPoolMap,
|
localpool::DataPool& localDataPoolMap,
|
||||||
LocalDataPoolManager& poolManager) override;
|
LocalDataPoolManager& poolManager) override;
|
||||||
virtual dur_millis_t getPeriodicOperationFrequency() const override;
|
virtual dur_millis_t getPeriodicOperationFrequency() const override;
|
||||||
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
|
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
|
||||||
LocalDataPoolManager* getHkManagerHandle() override;
|
LocalDataPoolManager* getHkManagerHandle() override;
|
||||||
|
|
||||||
virtual ReturnValue_t initialize() override;
|
virtual ReturnValue_t initialize() override;
|
||||||
virtual ReturnValue_t initializeAfterTaskCreation() override;
|
virtual ReturnValue_t initializeAfterTaskCreation() override;
|
||||||
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
||||||
|
|
||||||
virtual void queueMessageNotSent();
|
virtual void queueMessageNotSent();
|
||||||
|
|
||||||
virtual void lostTm();
|
virtual void lostTm();
|
||||||
|
|
||||||
virtual void storeFull();
|
virtual void storeFull();
|
||||||
|
|
||||||
virtual void setTaskIF(PeriodicTaskIF* task) override;
|
virtual void setTaskIF(PeriodicTaskIF* task) override;
|
||||||
protected:
|
protected:
|
||||||
MessageQueueIF* commandQueue;
|
MessageQueueIF* commandQueue;
|
||||||
LocalDataPoolManager poolManager;
|
LocalDataPoolManager poolManager;
|
||||||
|
|
||||||
PeriodicTaskIF* executingTask = nullptr;
|
PeriodicTaskIF* executingTask = nullptr;
|
||||||
|
|
||||||
MutexIF* mutex = nullptr;
|
MutexIF* mutex = nullptr;
|
||||||
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||||
uint32_t timeoutMs = 20;
|
uint32_t timeoutMs = 20;
|
||||||
|
|
||||||
sid_t internalErrorSid;
|
sid_t internalErrorSid;
|
||||||
InternalErrorDataset internalErrorDataset;
|
InternalErrorDataset internalErrorDataset;
|
||||||
|
|
||||||
bool diagnosticPrintout = true;
|
bool diagnosticPrintout = true;
|
||||||
|
|
||||||
uint32_t queueHits = 0;
|
uint32_t queueHits = 0;
|
||||||
uint32_t tmHits = 0;
|
uint32_t tmHits = 0;
|
||||||
uint32_t storeHits = 0;
|
uint32_t storeHits = 0;
|
||||||
|
|
||||||
uint32_t getAndResetQueueHits();
|
uint32_t getAndResetQueueHits();
|
||||||
uint32_t getQueueHits();
|
uint32_t getQueueHits();
|
||||||
void incrementQueueHits();
|
void incrementQueueHits();
|
||||||
|
|
||||||
uint32_t getAndResetTmHits();
|
uint32_t getAndResetTmHits();
|
||||||
uint32_t getTmHits();
|
uint32_t getTmHits();
|
||||||
void incrementTmHits();
|
void incrementTmHits();
|
||||||
|
|
||||||
uint32_t getAndResetStoreHits();
|
uint32_t getAndResetStoreHits();
|
||||||
uint32_t getStoreHits();
|
uint32_t getStoreHits();
|
||||||
void incrementStoreHits();
|
void incrementStoreHits();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,12 +22,12 @@ public:
|
|||||||
static const uint8_t INTERFACE_ID = CLASS_ID::MESSAGE_QUEUE_IF;
|
static const uint8_t INTERFACE_ID = CLASS_ID::MESSAGE_QUEUE_IF;
|
||||||
//! No new messages on the queue
|
//! No new messages on the queue
|
||||||
static const ReturnValue_t EMPTY = MAKE_RETURN_CODE(1);
|
static const ReturnValue_t EMPTY = MAKE_RETURN_CODE(1);
|
||||||
//! No space left for more messages
|
//! [EXPORT] : [COMMENT] No space left for more messages
|
||||||
static const ReturnValue_t FULL = MAKE_RETURN_CODE(2);
|
static const ReturnValue_t FULL = MAKE_RETURN_CODE(2);
|
||||||
//! Returned if a reply method was called without partner
|
//! [EXPORT] : [COMMENT] Returned if a reply method was called without partner
|
||||||
static const ReturnValue_t NO_REPLY_PARTNER = MAKE_RETURN_CODE(3);
|
static const ReturnValue_t NO_REPLY_PARTNER = MAKE_RETURN_CODE(3);
|
||||||
//! Returned if the target destination is invalid.
|
//! [EXPORT] : [COMMENT] Returned if the target destination is invalid.
|
||||||
static constexpr ReturnValue_t DESTINVATION_INVALID = MAKE_RETURN_CODE(4);
|
static constexpr ReturnValue_t DESTINATION_INVALID = MAKE_RETURN_CODE(4);
|
||||||
|
|
||||||
virtual ~MessageQueueIF() {}
|
virtual ~MessageQueueIF() {}
|
||||||
/**
|
/**
|
||||||
|
@ -86,3 +86,7 @@ size_t MessageQueueMessage::getMaximumMessageSize() const {
|
|||||||
return this->MAX_MESSAGE_SIZE;
|
return this->MAX_MESSAGE_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t MessageQueueMessage::getMaximumDataSize() const {
|
||||||
|
return this->MAX_DATA_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -139,6 +139,7 @@ public:
|
|||||||
virtual void setMessageSize(size_t messageSize) override;
|
virtual void setMessageSize(size_t messageSize) override;
|
||||||
virtual size_t getMinimumMessageSize() const override;
|
virtual size_t getMinimumMessageSize() const override;
|
||||||
virtual size_t getMaximumMessageSize() const override;
|
virtual size_t getMaximumMessageSize() const override;
|
||||||
|
virtual size_t getMaximumDataSize() const override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This is a debug method that prints the content.
|
* @brief This is a debug method that prints the content.
|
||||||
|
@ -72,6 +72,7 @@ public:
|
|||||||
virtual void setMessageSize(size_t messageSize) = 0;
|
virtual void setMessageSize(size_t messageSize) = 0;
|
||||||
virtual size_t getMinimumMessageSize() const = 0;
|
virtual size_t getMinimumMessageSize() const = 0;
|
||||||
virtual size_t getMaximumMessageSize() const = 0;
|
virtual size_t getMaximumMessageSize() const = 0;
|
||||||
|
virtual size_t getMaximumDataSize() const = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
60
ipc/MutexGuard.h
Normal file
60
ipc/MutexGuard.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef FRAMEWORK_IPC_MUTEXGUARD_H_
|
||||||
|
#define FRAMEWORK_IPC_MUTEXGUARD_H_
|
||||||
|
|
||||||
|
#include "MutexFactory.h"
|
||||||
|
#include "../serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
|
class MutexGuard {
|
||||||
|
public:
|
||||||
|
MutexGuard(MutexIF* mutex, MutexIF::TimeoutType timeoutType =
|
||||||
|
MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0):
|
||||||
|
internalMutex(mutex) {
|
||||||
|
if(mutex == nullptr) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "MutexGuard: Passed mutex is invalid!" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("MutexGuard: Passed mutex is invalid!\n");
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result = mutex->lockMutex(timeoutType,
|
||||||
|
timeoutMs);
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
if(result == MutexIF::MUTEX_TIMEOUT) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "MutexGuard: Lock of mutex failed with timeout of "
|
||||||
|
<< timeoutMs << " milliseconds!" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("MutexGuard: Lock of mutex failed with timeout of %lu milliseconds\n",
|
||||||
|
timeoutMs);
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "MutexGuard: Lock of Mutex failed with code " << result << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("MutexGuard: Lock of Mutex failed with code %d\n", result);
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t getLockResult() const {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MutexGuard() {
|
||||||
|
if(internalMutex != nullptr) {
|
||||||
|
internalMutex->unlockMutex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
MutexIF* internalMutex;
|
||||||
|
ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FRAMEWORK_IPC_MUTEXGUARD_H_ */
|
@ -1,57 +0,0 @@
|
|||||||
#ifndef FRAMEWORK_IPC_MUTEXHELPER_H_
|
|
||||||
#define FRAMEWORK_IPC_MUTEXHELPER_H_
|
|
||||||
|
|
||||||
#include "MutexFactory.h"
|
|
||||||
#include "../serviceinterface/ServiceInterface.h"
|
|
||||||
|
|
||||||
class MutexHelper {
|
|
||||||
public:
|
|
||||||
MutexHelper(MutexIF* mutex, MutexIF::TimeoutType timeoutType =
|
|
||||||
MutexIF::TimeoutType::BLOCKING, uint32_t timeoutMs = 0):
|
|
||||||
internalMutex(mutex) {
|
|
||||||
if(mutex == nullptr) {
|
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "MutexHelper: Passed mutex is invalid!" << std::endl;
|
|
||||||
#else
|
|
||||||
sif::printError("MutexHelper: Passed mutex is invalid!\n");
|
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ReturnValue_t status = mutex->lockMutex(timeoutType,
|
|
||||||
timeoutMs);
|
|
||||||
#if FSFW_VERBOSE_LEVEL >= 1
|
|
||||||
if(status == MutexIF::MUTEX_TIMEOUT) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "MutexHelper: Lock of mutex failed with timeout of "
|
|
||||||
<< timeoutMs << " milliseconds!" << std::endl;
|
|
||||||
#else
|
|
||||||
sif::printError("MutexHelper: Lock of mutex failed with timeout of %lu milliseconds\n",
|
|
||||||
timeoutMs);
|
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
|
||||||
|
|
||||||
}
|
|
||||||
else if(status != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "MutexHelper: Lock of Mutex failed with code " << status << std::endl;
|
|
||||||
#else
|
|
||||||
sif::printError("MutexHelper: Lock of Mutex failed with code %d\n", status);
|
|
||||||
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* To avoid unused variable warning */
|
|
||||||
static_cast<void>(status);
|
|
||||||
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
|
||||||
}
|
|
||||||
|
|
||||||
~MutexHelper() {
|
|
||||||
if(internalMutex != nullptr) {
|
|
||||||
internalMutex->unlockMutex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
MutexIF* internalMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* FRAMEWORK_IPC_MUTEXHELPER_H_ */
|
|
@ -108,9 +108,9 @@ void ObjectManager::initialize() {
|
|||||||
result = it.second->checkObjectConnections();
|
result = it.second->checkObjectConnections();
|
||||||
if ( result != RETURN_OK ) {
|
if ( result != RETURN_OK ) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "ObjectManager::ObjectManager: Object " << std::hex <<
|
sif::error << "ObjectManager::ObjectManager: Object 0x" << std::hex <<
|
||||||
(int) it.first << " connection check failed with code 0x"
|
(int) it.first << " connection check failed with code 0x" << result <<
|
||||||
<< result << std::dec << std::endl;
|
std::dec << std::endl;
|
||||||
#endif
|
#endif
|
||||||
errorCount++;
|
errorCount++;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
#include "frameworkObjects.h"
|
#include "frameworkObjects.h"
|
||||||
#include "SystemObjectIF.h"
|
#include "SystemObjectIF.h"
|
||||||
#include "../returnvalues/HasReturnvaluesIF.h"
|
#include "../returnvalues/HasReturnvaluesIF.h"
|
||||||
#include "../serviceinterface/ServiceInterfaceStream.h"
|
#include "../serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This class provides an interface to the global object manager.
|
* @brief This class provides an interface to the global object manager.
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
#ifndef FSFW_OBJECTMANAGER_FRAMEWORKOBJECTS_H_
|
#ifndef FSFW_OBJECTMANAGER_FRAMEWORKOBJECTS_H_
|
||||||
#define FSFW_OBJECTMANAGER_FRAMEWORKOBJECTS_H_
|
#define FSFW_OBJECTMANAGER_FRAMEWORKOBJECTS_H_
|
||||||
|
|
||||||
#include <fsfw/objectmanager/SystemObjectIF.h>
|
#include "SystemObjectIF.h"
|
||||||
|
|
||||||
|
// The objects will be instantiated in the ID order
|
||||||
namespace objects {
|
namespace objects {
|
||||||
enum framework_objects: object_id_t {
|
enum framework_objects: object_id_t {
|
||||||
FSFW_OBJECTS_START = 0x53000000,
|
FSFW_OBJECTS_START = 0x53000000,
|
||||||
@ -16,6 +17,7 @@ enum framework_objects: object_id_t {
|
|||||||
PUS_SERVICE_17_TEST = 0x53000017,
|
PUS_SERVICE_17_TEST = 0x53000017,
|
||||||
PUS_SERVICE_20_PARAMETERS = 0x53000020,
|
PUS_SERVICE_20_PARAMETERS = 0x53000020,
|
||||||
PUS_SERVICE_200_MODE_MGMT = 0x53000200,
|
PUS_SERVICE_200_MODE_MGMT = 0x53000200,
|
||||||
|
PUS_SERVICE_201_HEALTH = 0x53000201,
|
||||||
|
|
||||||
//Generic IDs for IPC, modes, health, events
|
//Generic IDs for IPC, modes, health, events
|
||||||
HEALTH_TABLE = 0x53010000,
|
HEALTH_TABLE = 0x53010000,
|
||||||
|
@ -1,34 +1,35 @@
|
|||||||
# Check the OS_FSFW variable
|
# Check the OS_FSFW variable
|
||||||
if(${OS_FSFW} STREQUAL "freertos")
|
if(${OS_FSFW} STREQUAL "freertos")
|
||||||
add_subdirectory(FreeRTOS)
|
add_subdirectory(FreeRTOS)
|
||||||
elseif(${OS_FSFW} STREQUAL "rtems")
|
elseif(${OS_FSFW} STREQUAL "rtems")
|
||||||
add_subdirectory(rtems)
|
add_subdirectory(rtems)
|
||||||
elseif(${OS_FSFW} STREQUAL "linux")
|
elseif(${OS_FSFW} STREQUAL "linux")
|
||||||
add_subdirectory(linux)
|
add_subdirectory(linux)
|
||||||
elseif(${OS_FSFW} STREQUAL "host")
|
elseif(${OS_FSFW} STREQUAL "host")
|
||||||
add_subdirectory(host)
|
add_subdirectory(host)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_subdirectory(windows)
|
add_subdirectory(windows)
|
||||||
elseif(UNIX)
|
elseif(UNIX)
|
||||||
target_sources(${LIB_FSFW_NAME}
|
# We still need to pull in some Linux specific sources
|
||||||
PUBLIC
|
target_sources(${LIB_FSFW_NAME} PUBLIC
|
||||||
linux/TcUnixUdpPollingTask.cpp
|
linux/tcpipHelpers.cpp
|
||||||
linux/TmTcUnixUdpBridge.cpp
|
)
|
||||||
)
|
endif ()
|
||||||
endif ()
|
|
||||||
|
|
||||||
else()
|
else()
|
||||||
|
|
||||||
message(WARNING "The OS_FSFW variable was not set. Assuming host OS..")
|
message(WARNING "The OS_FSFW variable was not set. Assuming host OS..")
|
||||||
# Not set. Assumuing this is a host build, try to determine host OS
|
# Not set. Assumuing this is a host build, try to determine host OS
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
add_subdirectory(host)
|
add_subdirectory(host)
|
||||||
add_subdirectory(windows)
|
add_subdirectory(windows)
|
||||||
elseif (UNIX)
|
elseif (UNIX)
|
||||||
add_subdirectory(linux)
|
add_subdirectory(linux)
|
||||||
else ()
|
else ()
|
||||||
# MacOS or other OSes have not been tested yet / are not supported.
|
# MacOS or other OSes have not been tested yet / are not supported.
|
||||||
message(FATAL_ERROR "The host OS could not be determined! Aborting.")
|
message(FATAL_ERROR "The host OS could not be determined! Aborting.")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(common)
|
@ -111,7 +111,7 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
|
|||||||
|
|
||||||
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from,
|
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from,
|
||||||
timeval* to) {
|
timeval* to) {
|
||||||
struct tm time_tm;
|
struct tm time_tm = {};
|
||||||
|
|
||||||
time_tm.tm_year = from->year - 1900;
|
time_tm.tm_year = from->year - 1900;
|
||||||
time_tm.tm_mon = from->month - 1;
|
time_tm.tm_mon = from->month - 1;
|
||||||
|
@ -135,7 +135,7 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
|||||||
QueueHandle_t destination = nullptr;
|
QueueHandle_t destination = nullptr;
|
||||||
|
|
||||||
if(sendTo == MessageQueueIF::NO_QUEUE or sendTo == 0x00) {
|
if(sendTo == MessageQueueIF::NO_QUEUE or sendTo == 0x00) {
|
||||||
return MessageQueueIF::DESTINVATION_INVALID;
|
return MessageQueueIF::DESTINATION_INVALID;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
destination = reinterpret_cast<QueueHandle_t>(sendTo);
|
destination = reinterpret_cast<QueueHandle_t>(sendTo);
|
||||||
|
17
osal/common/CMakeLists.txt
Normal file
17
osal/common/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
if(DEFINED WIN32 OR DEFINED UNIX)
|
||||||
|
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
tcpipCommon.cpp
|
||||||
|
TcpIpBase.cpp
|
||||||
|
UdpTcPollingTask.cpp
|
||||||
|
UdpTmTcBridge.cpp
|
||||||
|
TcpTmTcServer.cpp
|
||||||
|
TcpTmTcBridge.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
||||||
|
wsock32
|
||||||
|
ws2_32
|
||||||
|
)
|
||||||
|
endif()
|
53
osal/common/TcpIpBase.cpp
Normal file
53
osal/common/TcpIpBase.cpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#include "TcpIpBase.h"
|
||||||
|
#include "../../platform.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_UNIX
|
||||||
|
#include <errno.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TcpIpBase::TcpIpBase() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpIpBase::initialize() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Initiates Winsock DLL. */
|
||||||
|
WSAData wsaData;
|
||||||
|
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
int err = WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
if (err != 0) {
|
||||||
|
/* Tell the user that we could not find a usable Winsock DLL. */
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "TmTcWinUdpBridge::TmTcWinUdpBridge: WSAStartup failed with error: " <<
|
||||||
|
err << std::endl;
|
||||||
|
#endif
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpIpBase::~TcpIpBase() {
|
||||||
|
closeSocket(serverSocket);
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int TcpIpBase::closeSocket(socket_t socket) {
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
return closesocket(socket);
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
return close(socket);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int TcpIpBase::getLastSocketError() {
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
return WSAGetLastError();
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
return errno;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
47
osal/common/TcpIpBase.h
Normal file
47
osal/common/TcpIpBase.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_TCPIPIF_H_
|
||||||
|
#define FSFW_OSAL_COMMON_TCPIPIF_H_
|
||||||
|
|
||||||
|
#include "../../returnvalues/HasReturnvaluesIF.h"
|
||||||
|
#include "../../platform.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
#include <winsock2.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class TcpIpBase {
|
||||||
|
protected:
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
static constexpr int SHUT_RECV = SD_RECEIVE;
|
||||||
|
static constexpr int SHUT_SEND = SD_SEND;
|
||||||
|
static constexpr int SHUT_BOTH = SD_BOTH;
|
||||||
|
|
||||||
|
using socket_t = SOCKET;
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
using socket_t = int;
|
||||||
|
|
||||||
|
static constexpr int INVALID_SOCKET = -1;
|
||||||
|
static constexpr int SOCKET_ERROR = -1;
|
||||||
|
|
||||||
|
static constexpr int SHUT_RECV = SHUT_RD;
|
||||||
|
static constexpr int SHUT_SEND = SHUT_WR;
|
||||||
|
static constexpr int SHUT_BOTH = SHUT_RDWR;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TcpIpBase();
|
||||||
|
virtual ~TcpIpBase();
|
||||||
|
|
||||||
|
ReturnValue_t initialize();
|
||||||
|
|
||||||
|
int closeSocket(socket_t socket);
|
||||||
|
|
||||||
|
int getLastSocketError();
|
||||||
|
|
||||||
|
socket_t serverSocket = 0;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_TCPIPIF_H_ */
|
77
osal/common/TcpTmTcBridge.cpp
Normal file
77
osal/common/TcpTmTcBridge.cpp
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include "TcpTmTcBridge.h"
|
||||||
|
#include "tcpipHelpers.h"
|
||||||
|
|
||||||
|
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||||
|
#include <fsfw/ipc/MutexGuard.h>
|
||||||
|
#include <fsfw/osal/common/TcpTmTcBridge.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
#elif defined(__unix__)
|
||||||
|
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const std::string TcpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
|
||||||
|
|
||||||
|
TcpTmTcBridge::TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
|
||||||
|
object_id_t tmStoreId, object_id_t tcStoreId):
|
||||||
|
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
|
||||||
|
mutex = MutexFactory::instance()->createMutex();
|
||||||
|
// Connection is always up, TM is requested by connecting to server and receiving packets
|
||||||
|
registerCommConnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcBridge::initialize() {
|
||||||
|
ReturnValue_t result = TmTcBridge::initialize();
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "TcpTmTcBridge::initialize: TmTcBridge initialization failed!"
|
||||||
|
<< std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("TcpTmTcBridge::initialize: TmTcBridge initialization failed!\n");
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
TcpTmTcBridge::~TcpTmTcBridge() {
|
||||||
|
if(mutex != nullptr) {
|
||||||
|
MutexFactory::instance()->deleteMutex(mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcBridge::handleTm() {
|
||||||
|
// Simply store the telemetry in the FIFO, the server will use it to access the TM
|
||||||
|
MutexGuard guard(mutex, timeoutType, mutexTimeoutMs);
|
||||||
|
TmTcMessage message;
|
||||||
|
ReturnValue_t status = HasReturnvaluesIF::RETURN_OK;
|
||||||
|
for (ReturnValue_t result = tmTcReceptionQueue->receiveMessage(&message);
|
||||||
|
result == HasReturnvaluesIF::RETURN_OK;
|
||||||
|
result = tmTcReceptionQueue->receiveMessage(&message))
|
||||||
|
{
|
||||||
|
status = storeDownlinkData(&message);
|
||||||
|
if(status != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) {
|
||||||
|
// Not used. The Server uses the FIFO to access and send the telemetry.
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TcpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
|
||||||
|
dur_millis_t timeoutMs) {
|
||||||
|
this->timeoutType = timeoutType;
|
||||||
|
this->mutexTimeoutMs = timeoutMs;
|
||||||
|
}
|
71
osal/common/TcpTmTcBridge.h
Normal file
71
osal/common/TcpTmTcBridge.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_
|
||||||
|
#define FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_
|
||||||
|
|
||||||
|
#include "TcpIpBase.h"
|
||||||
|
#include "../../tmtcservices/TmTcBridge.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
|
||||||
|
#elif defined(__unix__)
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class should be used with the TcpTmTcServer to implement a TCP server
|
||||||
|
* for receiving and sending PUS telemetry and telecommands (TMTC)
|
||||||
|
* @details
|
||||||
|
* This bridge tasks takes care of filling a FIFO which generated telemetry. The TcpTmTcServer
|
||||||
|
* will take care of sending the telemetry stored in the FIFO if a client connects to the
|
||||||
|
* server. This bridge will also be the default destination for telecommands, but the telecommands
|
||||||
|
* will be relayed to a specified tcDestination directly.
|
||||||
|
*/
|
||||||
|
class TcpTmTcBridge:
|
||||||
|
public TmTcBridge {
|
||||||
|
friend class TcpTmTcServer;
|
||||||
|
public:
|
||||||
|
/* The ports chosen here should not be used by any other process. */
|
||||||
|
static const std::string DEFAULT_UDP_SERVER_PORT;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param objectId Object ID of the TcpTmTcBridge.
|
||||||
|
* @param tcDestination Destination for received TC packets. Any received telecommands will
|
||||||
|
* be sent there directly. The destination object needs to implement
|
||||||
|
* AcceptsTelecommandsIF.
|
||||||
|
* @param tmStoreId TM store object ID. It is recommended to the default object ID
|
||||||
|
* @param tcStoreId TC store object ID. It is recommended to the default object ID
|
||||||
|
*/
|
||||||
|
TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
|
||||||
|
object_id_t tmStoreId = objects::TM_STORE,
|
||||||
|
object_id_t tcStoreId = objects::TC_STORE);
|
||||||
|
virtual~ TcpTmTcBridge();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties of internal mutex.
|
||||||
|
*/
|
||||||
|
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
|
||||||
|
|
||||||
|
ReturnValue_t initialize() override;
|
||||||
|
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ReturnValue_t handleTm() override;
|
||||||
|
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
//! Access to the FIFO needs to be mutex protected because it is used by the bridge and
|
||||||
|
//! the server.
|
||||||
|
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||||
|
dur_millis_t mutexTimeoutMs = 20;
|
||||||
|
MutexIF* mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_ */
|
||||||
|
|
226
osal/common/TcpTmTcServer.cpp
Normal file
226
osal/common/TcpTmTcServer.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#include "TcpTmTcServer.h"
|
||||||
|
#include "TcpTmTcBridge.h"
|
||||||
|
#include "tcpipHelpers.h"
|
||||||
|
|
||||||
|
#include "../../platform.h"
|
||||||
|
#include "../../container/SharedRingBuffer.h"
|
||||||
|
#include "../../ipc/MessageQueueSenderIF.h"
|
||||||
|
#include "../../ipc/MutexGuard.h"
|
||||||
|
#include "../../objectmanager/ObjectManagerIF.h"
|
||||||
|
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
|
#include "../../tmtcservices/TmTcMessage.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
#include <netdb.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED
|
||||||
|
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const std::string TcpTmTcServer::DEFAULT_TCP_SERVER_PORT = "7303";
|
||||||
|
|
||||||
|
TcpTmTcServer::TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
||||||
|
size_t receptionBufferSize, std::string customTcpServerPort):
|
||||||
|
SystemObject(objectId), tmtcBridgeId(tmtcTcpBridge),
|
||||||
|
tcpPort(customTcpServerPort), receptionBuffer(receptionBufferSize) {
|
||||||
|
if(tcpPort == "") {
|
||||||
|
tcpPort = DEFAULT_TCP_SERVER_PORT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::initialize() {
|
||||||
|
using namespace tcpip;
|
||||||
|
|
||||||
|
ReturnValue_t result = TcpIpBase::initialize();
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
|
||||||
|
if (tcStore == nullptr) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "TcpTmTcServer::initialize: TC store uninitialized!" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("TcpTmTcServer::initialize: TC store uninitialized!\n");
|
||||||
|
#endif
|
||||||
|
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmtcBridge = objectManager->get<TcpTmTcBridge>(tmtcBridgeId);
|
||||||
|
|
||||||
|
int retval = 0;
|
||||||
|
struct addrinfo *addrResult = nullptr;
|
||||||
|
struct addrinfo hints = {};
|
||||||
|
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
// Listen to all addresses (0.0.0.0) by using AI_PASSIVE in the hint flags
|
||||||
|
retval = getaddrinfo(nullptr, tcpPort.c_str(), &hints, &addrResult);
|
||||||
|
if (retval != 0) {
|
||||||
|
handleError(Protocol::TCP, ErrorSources::GETADDRINFO_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open TCP (stream) socket
|
||||||
|
listenerTcpSocket = socket(addrResult->ai_family, addrResult->ai_socktype,
|
||||||
|
addrResult->ai_protocol);
|
||||||
|
if(listenerTcpSocket == INVALID_SOCKET) {
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
handleError(Protocol::TCP, ErrorSources::SOCKET_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind to the address found by getaddrinfo
|
||||||
|
retval = bind(listenerTcpSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
|
||||||
|
if(retval == SOCKET_ERROR) {
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
handleError(Protocol::TCP, ErrorSources::BIND_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TcpTmTcServer::~TcpTmTcServer() {
|
||||||
|
closeSocket(listenerTcpSocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::performOperation(uint8_t opCode) {
|
||||||
|
using namespace tcpip;
|
||||||
|
// If a connection is accepted, the corresponding socket will be assigned to the new socket
|
||||||
|
socket_t connSocket = 0;
|
||||||
|
// sockaddr clientSockAddr = {};
|
||||||
|
// socklen_t connectorSockAddrLen = 0;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
// Listen for connection requests permanently for lifetime of program
|
||||||
|
while(true) {
|
||||||
|
retval = listen(listenerTcpSocket, tcpBacklog);
|
||||||
|
if(retval == SOCKET_ERROR) {
|
||||||
|
handleError(Protocol::TCP, ErrorSources::LISTEN_CALL, 500);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//connSocket = accept(listenerTcpSocket, &clientSockAddr, &connectorSockAddrLen);
|
||||||
|
connSocket = accept(listenerTcpSocket, nullptr, nullptr);
|
||||||
|
|
||||||
|
if(connSocket == INVALID_SOCKET) {
|
||||||
|
handleError(Protocol::TCP, ErrorSources::ACCEPT_CALL, 500);
|
||||||
|
closeSocket(connSocket);
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleServerOperation(connSocket);
|
||||||
|
|
||||||
|
// Done, shut down connection and go back to listening for client requests
|
||||||
|
retval = shutdown(connSocket, SHUT_SEND);
|
||||||
|
if(retval != 0) {
|
||||||
|
handleError(Protocol::TCP, ErrorSources::SHUTDOWN_CALL);
|
||||||
|
}
|
||||||
|
closeSocket(connSocket);
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::initializeAfterTaskCreation() {
|
||||||
|
if(tmtcBridge == nullptr) {
|
||||||
|
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||||
|
}
|
||||||
|
/* Initialize the destination after task creation. This ensures
|
||||||
|
that the destination has already been set in the TMTC bridge. */
|
||||||
|
targetTcDestination = tmtcBridge->getRequestQueue();
|
||||||
|
tcStore = tmtcBridge->tcStore;
|
||||||
|
tmStore = tmtcBridge->tmStore;
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpTmTcServer::handleServerOperation(socket_t connSocket) {
|
||||||
|
int retval = 0;
|
||||||
|
do {
|
||||||
|
// Read all telecommands sent by the client
|
||||||
|
retval = recv(connSocket,
|
||||||
|
reinterpret_cast<char*>(receptionBuffer.data()),
|
||||||
|
receptionBuffer.capacity(),
|
||||||
|
tcpFlags);
|
||||||
|
if (retval > 0) {
|
||||||
|
handleTcReception(retval);
|
||||||
|
}
|
||||||
|
else if(retval == 0) {
|
||||||
|
// Client has finished sending telecommands, send telemetry now
|
||||||
|
handleTmSending(connSocket);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Should not happen
|
||||||
|
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::RECV_CALL);
|
||||||
|
}
|
||||||
|
} while(retval > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::handleTcReception(size_t bytesRecvd) {
|
||||||
|
#if FSFW_TCP_RECV_WIRETAPPING_ENABLED == 1
|
||||||
|
arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||||
|
#endif
|
||||||
|
store_address_t storeId;
|
||||||
|
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRecvd);
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning<< "TcpTmTcServer::handleServerOperation: Data storage failed." << std::endl;
|
||||||
|
sif::warning << "Packet size: " << bytesRecvd << std::endl;
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
TmTcMessage message(storeId);
|
||||||
|
|
||||||
|
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: "
|
||||||
|
" Sending message to queue failed" << std::endl;
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
tcStore->deleteData(storeId);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) {
|
||||||
|
this->tcpBacklog = tcpBacklog;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t TcpTmTcServer::handleTmSending(socket_t connSocket) {
|
||||||
|
// Access to the FIFO is mutex protected because it is filled by the bridge
|
||||||
|
MutexGuard(tmtcBridge->mutex, tmtcBridge->timeoutType, tmtcBridge->mutexTimeoutMs);
|
||||||
|
store_address_t storeId;
|
||||||
|
while((not tmtcBridge->tmFifo->empty()) and
|
||||||
|
(tmtcBridge->packetSentCounter < tmtcBridge->sentPacketsPerCycle)) {
|
||||||
|
tmtcBridge->tmFifo->retrieve(&storeId);
|
||||||
|
|
||||||
|
// Using the store accessor will take care of deleting TM from the store automatically
|
||||||
|
ConstStorageAccessor storeAccessor(storeId);
|
||||||
|
ReturnValue_t result = tmStore->getData(storeId, storeAccessor);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int retval = send(connSocket,
|
||||||
|
reinterpret_cast<const char*>(storeAccessor.data()),
|
||||||
|
storeAccessor.size(),
|
||||||
|
tcpTmFlags);
|
||||||
|
if(retval != static_cast<int>(storeAccessor.size())) {
|
||||||
|
tcpip::handleError(tcpip::Protocol::TCP, tcpip::ErrorSources::SEND_CALL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
93
osal/common/TcpTmTcServer.h
Normal file
93
osal/common/TcpTmTcServer.h
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_
|
||||||
|
#define FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_
|
||||||
|
|
||||||
|
#include "TcpIpBase.h"
|
||||||
|
|
||||||
|
#include "../../platform.h"
|
||||||
|
#include "../../ipc/messageQueueDefinitions.h"
|
||||||
|
#include "../../ipc/MessageQueueIF.h"
|
||||||
|
#include "../../objectmanager/frameworkObjects.h"
|
||||||
|
#include "../../objectmanager/SystemObject.h"
|
||||||
|
#include "../../storagemanager/StorageManagerIF.h"
|
||||||
|
#include "../../tasks/ExecutableObjectIF.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_UNIX
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class TcpTmTcBridge;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief TCP server implementation
|
||||||
|
* @details
|
||||||
|
* This server will run for the whole program lifetime and will take care of serving client
|
||||||
|
* requests on a specified TCP server port. This server was written in a generic way and
|
||||||
|
* can be used on Unix and on Windows systems.
|
||||||
|
*
|
||||||
|
* If a connection is accepted, the server will read all telecommands sent by a client and then
|
||||||
|
* send all telemetry currently found in the TMTC bridge FIFO.
|
||||||
|
*
|
||||||
|
* Reading telemetry without sending telecommands is possible by connecting, shutting down the
|
||||||
|
* send operation immediately and then reading the telemetry. It is therefore recommended to
|
||||||
|
* connect to the server regularly, even if no telecommands need to be sent.
|
||||||
|
*
|
||||||
|
* The server will listen to a specific port on all addresses (0.0.0.0).
|
||||||
|
*/
|
||||||
|
class TcpTmTcServer:
|
||||||
|
public SystemObject,
|
||||||
|
public TcpIpBase,
|
||||||
|
public ExecutableObjectIF {
|
||||||
|
public:
|
||||||
|
/* The ports chosen here should not be used by any other process. */
|
||||||
|
static const std::string DEFAULT_TCP_SERVER_PORT;
|
||||||
|
|
||||||
|
static constexpr size_t ETHERNET_MTU_SIZE = 1500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TCP Server Constructor
|
||||||
|
* @param objectId Object ID of the TCP Server
|
||||||
|
* @param tmtcTcpBridge Object ID of the TCP TMTC Bridge object
|
||||||
|
* @param receptionBufferSize This will be the size of the reception buffer. Default buffer
|
||||||
|
* size will be the Ethernet MTU size
|
||||||
|
* @param customTcpServerPort The user can specify another port than the default (7301) here.
|
||||||
|
*/
|
||||||
|
TcpTmTcServer(object_id_t objectId, object_id_t tmtcTcpBridge,
|
||||||
|
size_t receptionBufferSize = ETHERNET_MTU_SIZE + 1,
|
||||||
|
std::string customTcpServerPort = "");
|
||||||
|
virtual~ TcpTmTcServer();
|
||||||
|
|
||||||
|
void setTcpBacklog(uint8_t tcpBacklog);
|
||||||
|
|
||||||
|
ReturnValue_t initialize() override;
|
||||||
|
ReturnValue_t performOperation(uint8_t opCode) override;
|
||||||
|
ReturnValue_t initializeAfterTaskCreation() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StorageManagerIF* tcStore = nullptr;
|
||||||
|
StorageManagerIF* tmStore = nullptr;
|
||||||
|
private:
|
||||||
|
//! TMTC bridge is cached.
|
||||||
|
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
||||||
|
TcpTmTcBridge* tmtcBridge = nullptr;
|
||||||
|
|
||||||
|
std::string tcpPort;
|
||||||
|
int tcpFlags = 0;
|
||||||
|
socket_t listenerTcpSocket = 0;
|
||||||
|
struct sockaddr tcpAddress;
|
||||||
|
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||||
|
int tcpAddrLen = sizeof(tcpAddress);
|
||||||
|
int tcpBacklog = 3;
|
||||||
|
|
||||||
|
std::vector<uint8_t> receptionBuffer;
|
||||||
|
int tcpSockOpt = 0;
|
||||||
|
int tcpTmFlags = 0;
|
||||||
|
|
||||||
|
void handleServerOperation(socket_t connSocket);
|
||||||
|
ReturnValue_t handleTcReception(size_t bytesRecvd);
|
||||||
|
ReturnValue_t handleTmSending(socket_t connSocket);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_TCP_TMTC_SERVER_H_ */
|
177
osal/common/UdpTcPollingTask.cpp
Normal file
177
osal/common/UdpTcPollingTask.cpp
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
#include "UdpTcPollingTask.h"
|
||||||
|
#include "tcpipHelpers.h"
|
||||||
|
#include "../../platform.h"
|
||||||
|
#include "../../globalfunctions/arrayprinter.h"
|
||||||
|
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
#include <winsock2.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Debugging preprocessor define.
|
||||||
|
#define FSFW_UDP_RECV_WIRETAPPING_ENABLED 0
|
||||||
|
|
||||||
|
UdpTcPollingTask::UdpTcPollingTask(object_id_t objectId,
|
||||||
|
object_id_t tmtcUdpBridge, size_t maxRecvSize,
|
||||||
|
double timeoutSeconds): SystemObject(objectId),
|
||||||
|
tmtcBridgeId(tmtcUdpBridge) {
|
||||||
|
if(frameSize > 0) {
|
||||||
|
this->frameSize = frameSize;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->frameSize = DEFAULT_MAX_RECV_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up reception buffer with specified frame size.
|
||||||
|
For now, it is assumed that only one frame is held in the buffer! */
|
||||||
|
receptionBuffer.reserve(this->frameSize);
|
||||||
|
receptionBuffer.resize(this->frameSize);
|
||||||
|
|
||||||
|
if(timeoutSeconds == -1) {
|
||||||
|
receptionTimeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
receptionTimeout = timevalOperations::toTimeval(timeoutSeconds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UdpTcPollingTask::~UdpTcPollingTask() {}
|
||||||
|
|
||||||
|
ReturnValue_t UdpTcPollingTask::performOperation(uint8_t opCode) {
|
||||||
|
/* Sender Address is cached here. */
|
||||||
|
struct sockaddr senderAddress;
|
||||||
|
socklen_t senderAddressSize = sizeof(senderAddress);
|
||||||
|
|
||||||
|
/* Poll for new UDP datagrams in permanent loop. */
|
||||||
|
while(true) {
|
||||||
|
int bytesReceived = recvfrom(
|
||||||
|
this->serverSocket,
|
||||||
|
reinterpret_cast<char*>(receptionBuffer.data()),
|
||||||
|
frameSize,
|
||||||
|
receptionFlags,
|
||||||
|
&senderAddress,
|
||||||
|
&senderAddressSize
|
||||||
|
);
|
||||||
|
if(bytesReceived == SOCKET_ERROR) {
|
||||||
|
/* Handle error */
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "UdpTcPollingTask::performOperation: Reception error." << std::endl;
|
||||||
|
#endif
|
||||||
|
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::RECVFROM_CALL, 1000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#if FSFW_UDP_RECV_WIRETAPPING_ENABLED == 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::debug << "UdpTcPollingTask::performOperation: " << bytesReceived <<
|
||||||
|
" bytes received" << std::endl;
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
#endif /* FSFW_UDP_RCV_WIRETAPPING_ENABLED == 1 */
|
||||||
|
|
||||||
|
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_FAILED) {
|
||||||
|
|
||||||
|
}
|
||||||
|
tmtcBridge->checkAndSetClientAddress(senderAddress);
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ReturnValue_t UdpTcPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
||||||
|
store_address_t storeId;
|
||||||
|
|
||||||
|
#if FSFW_UDP_RECV_WIRETAPPING_ENABLED == 1
|
||||||
|
arrayprinter::print(receptionBuffer.data(), bytesRead);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ReturnValue_t result = tcStore->addData(&storeId, receptionBuffer.data(), bytesRead);
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning<< "UdpTcPollingTask::transferPusToSoftwareBus: Data storage failed." <<
|
||||||
|
std::endl;
|
||||||
|
sif::warning << "Packet size: " << bytesRead << std::endl;
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
TmTcMessage message(storeId);
|
||||||
|
|
||||||
|
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
||||||
|
if (result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_VERBOSE_LEVEL >= 1
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "UdpTcPollingTask::handleSuccessfullTcRead: "
|
||||||
|
" Sending message to queue failed" << std::endl;
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
|
||||||
|
tcStore->deleteData(storeId);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t UdpTcPollingTask::initialize() {
|
||||||
|
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
|
||||||
|
if (tcStore == nullptr) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "UdpTcPollingTask::initialize: TC store uninitialized!" << std::endl;
|
||||||
|
#endif
|
||||||
|
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmtcBridge = objectManager->get<UdpTmTcBridge>(tmtcBridgeId);
|
||||||
|
if(tmtcBridge == nullptr) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "UdpTcPollingTask::initialize: Invalid TMTC bridge object!" <<
|
||||||
|
std::endl;
|
||||||
|
#endif
|
||||||
|
return ObjectManagerIF::CHILD_INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t result = TcpIpBase::initialize();
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t UdpTcPollingTask::initializeAfterTaskCreation() {
|
||||||
|
/* Initialize the destination after task creation. This ensures
|
||||||
|
that the destination has already been set in the TMTC bridge. */
|
||||||
|
targetTcDestination = tmtcBridge->getRequestQueue();
|
||||||
|
/* The server socket is set up in the bridge intialization. Calling this function here
|
||||||
|
ensures that it is set up regardless of which class was initialized first */
|
||||||
|
this->serverSocket = tmtcBridge->serverSocket;
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UdpTcPollingTask::setTimeout(double timeoutSeconds) {
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
DWORD timeoutMs = timeoutSeconds * 1000.0;
|
||||||
|
int result = setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO,
|
||||||
|
reinterpret_cast<const char*>(&timeoutMs), sizeof(DWORD));
|
||||||
|
if(result == -1) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "TcWinUdpPollingTask::TcSocketPollingTask: Setting "
|
||||||
|
"receive timeout failed with " << strerror(errno) << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
timeval tval;
|
||||||
|
tval = timevalOperations::toTimeval(timeoutSeconds);
|
||||||
|
int result = setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO,
|
||||||
|
&tval, sizeof(receptionTimeout));
|
||||||
|
if(result == -1) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting "
|
||||||
|
"receive timeout failed with " << strerror(errno) << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
61
osal/common/UdpTcPollingTask.h
Normal file
61
osal/common/UdpTcPollingTask.h
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_
|
||||||
|
#define FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_
|
||||||
|
|
||||||
|
#include "UdpTmTcBridge.h"
|
||||||
|
#include "../../objectmanager/SystemObject.h"
|
||||||
|
#include "../../tasks/ExecutableObjectIF.h"
|
||||||
|
#include "../../storagemanager/StorageManagerIF.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class can be used with the UdpTmTcBridge to implement a UDP server
|
||||||
|
* for receiving and sending PUS TMTC.
|
||||||
|
* @details
|
||||||
|
* This task is exclusively used to poll telecommands from a given socket and transfer them
|
||||||
|
* to the FSFW software bus. It used the blocking recvfrom call to do this.
|
||||||
|
*/
|
||||||
|
class UdpTcPollingTask:
|
||||||
|
public TcpIpBase,
|
||||||
|
public SystemObject,
|
||||||
|
public ExecutableObjectIF {
|
||||||
|
friend class TmTcWinUdpBridge;
|
||||||
|
public:
|
||||||
|
static constexpr size_t DEFAULT_MAX_RECV_SIZE = 1500;
|
||||||
|
//! 0.5 default milliseconds timeout for now.
|
||||||
|
static constexpr timeval DEFAULT_TIMEOUT = {0, 500};
|
||||||
|
|
||||||
|
UdpTcPollingTask(object_id_t objectId, object_id_t tmtcUdpBridge,
|
||||||
|
size_t maxRecvSize = 0, double timeoutSeconds = -1);
|
||||||
|
virtual~ UdpTcPollingTask();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on optional timeout for UDP polling. In the default mode,
|
||||||
|
* the receive function will block until a packet is received.
|
||||||
|
* @param timeoutSeconds
|
||||||
|
*/
|
||||||
|
void setTimeout(double timeoutSeconds);
|
||||||
|
|
||||||
|
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
||||||
|
virtual ReturnValue_t initialize() override;
|
||||||
|
virtual ReturnValue_t initializeAfterTaskCreation() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
StorageManagerIF* tcStore = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! TMTC bridge is cached.
|
||||||
|
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
||||||
|
UdpTmTcBridge* tmtcBridge = nullptr;
|
||||||
|
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
||||||
|
int receptionFlags = 0;
|
||||||
|
|
||||||
|
std::vector<uint8_t> receptionBuffer;
|
||||||
|
|
||||||
|
size_t frameSize = 0;
|
||||||
|
timeval receptionTimeout;
|
||||||
|
|
||||||
|
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_UDPTCPOLLINGTASK_H_ */
|
162
osal/common/UdpTmTcBridge.cpp
Normal file
162
osal/common/UdpTmTcBridge.cpp
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#include "UdpTmTcBridge.h"
|
||||||
|
#include "tcpipHelpers.h"
|
||||||
|
|
||||||
|
#include "../../platform.h"
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
|
#include "../../ipc/MutexGuard.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Debugging preprocessor define.
|
||||||
|
#ifndef FSFW_UDP_SEND_WIRETAPPING_ENABLED
|
||||||
|
#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const std::string UdpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT;
|
||||||
|
|
||||||
|
UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
|
||||||
|
std::string udpServerPort, object_id_t tmStoreId, object_id_t tcStoreId):
|
||||||
|
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
|
||||||
|
if(udpServerPort == "") {
|
||||||
|
this->udpServerPort = DEFAULT_UDP_SERVER_PORT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this->udpServerPort = udpServerPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex = MutexFactory::instance()->createMutex();
|
||||||
|
communicationLinkUp = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t UdpTmTcBridge::initialize() {
|
||||||
|
ReturnValue_t result = TmTcBridge::initialize();
|
||||||
|
if(result != HasReturnvaluesIF::RETURN_OK) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "UdpTmTcBridge::initialize: TmTcBridge initialization failed!"
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
/* Initiates Winsock DLL. */
|
||||||
|
WSAData wsaData;
|
||||||
|
WORD wVersionRequested = MAKEWORD(2, 2);
|
||||||
|
int err = WSAStartup(wVersionRequested, &wsaData);
|
||||||
|
if (err != 0) {
|
||||||
|
/* Tell the user that we could not find a usable */
|
||||||
|
/* Winsock DLL. */
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "UdpTmTcBridge::UdpTmTcBridge: WSAStartup failed with error: " <<
|
||||||
|
err << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("UdpTmTcBridge::UdpTmTcBridge: WSAStartup failed with error: %d\n",
|
||||||
|
err);
|
||||||
|
#endif
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct addrinfo *addrResult = nullptr;
|
||||||
|
struct addrinfo hints = {};
|
||||||
|
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_DGRAM;
|
||||||
|
hints.ai_protocol = IPPROTO_UDP;
|
||||||
|
hints.ai_flags = AI_PASSIVE;
|
||||||
|
|
||||||
|
/* Set up UDP socket:
|
||||||
|
https://en.wikipedia.org/wiki/Getaddrinfo
|
||||||
|
Passing nullptr as the first parameter and specifying AI_PASSIVE in hints will cause
|
||||||
|
getaddrinfo to assign the address 0.0.0.0 (any address) */
|
||||||
|
int retval = getaddrinfo(nullptr, udpServerPort.c_str(), &hints, &addrResult);
|
||||||
|
if (retval != 0) {
|
||||||
|
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::GETADDRINFO_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSocket = socket(addrResult->ai_family, addrResult->ai_socktype, addrResult->ai_protocol);
|
||||||
|
if(serverSocket == INVALID_SOCKET) {
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SOCKET_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||||
|
tcpip::printAddress(addrResult->ai_addr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
retval = bind(serverSocket, addrResult->ai_addr, static_cast<int>(addrResult->ai_addrlen));
|
||||||
|
if(retval != 0) {
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::BIND_CALL);
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
freeaddrinfo(addrResult);
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
UdpTmTcBridge::~UdpTmTcBridge() {
|
||||||
|
if(mutex != nullptr) {
|
||||||
|
MutexFactory::instance()->deleteMutex(mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ReturnValue_t UdpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) {
|
||||||
|
int flags = 0;
|
||||||
|
|
||||||
|
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||||
|
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||||
|
|
||||||
|
#if FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||||
|
tcpip::printAddress(&clientAddress);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int bytesSent = sendto(
|
||||||
|
serverSocket,
|
||||||
|
reinterpret_cast<const char*>(data),
|
||||||
|
dataLen,
|
||||||
|
flags,
|
||||||
|
&clientAddress,
|
||||||
|
clientAddressLen
|
||||||
|
);
|
||||||
|
if(bytesSent == SOCKET_ERROR) {
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "TmTcUdpBridge::sendTm: Send operation failed." << std::endl;
|
||||||
|
#endif
|
||||||
|
tcpip::handleError(tcpip::Protocol::UDP, tcpip::ErrorSources::SENDTO_CALL);
|
||||||
|
}
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1 && FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||||
|
sif::debug << "TmTcUdpBridge::sendTm: " << bytesSent << " bytes were"
|
||||||
|
" sent." << std::endl;
|
||||||
|
#endif
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UdpTmTcBridge::checkAndSetClientAddress(sockaddr& newAddress) {
|
||||||
|
/* The target address can be set by different threads so this lock ensures thread-safety */
|
||||||
|
MutexGuard lock(mutex, timeoutType, mutexTimeoutMs);
|
||||||
|
|
||||||
|
#if FSFW_UDP_SEND_WIRETAPPING_ENABLED == 1
|
||||||
|
tcpip::printAddress(&newAddress);
|
||||||
|
tcpip::printAddress(&clientAddress);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
registerCommConnect();
|
||||||
|
|
||||||
|
/* Set new IP address to reply to */
|
||||||
|
clientAddress = newAddress;
|
||||||
|
clientAddressLen = sizeof(clientAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UdpTmTcBridge::setMutexProperties(MutexIF::TimeoutType timeoutType,
|
||||||
|
dur_millis_t timeoutMs) {
|
||||||
|
this->timeoutType = timeoutType;
|
||||||
|
this->mutexTimeoutMs = timeoutMs;
|
||||||
|
}
|
63
osal/common/UdpTmTcBridge.h
Normal file
63
osal/common/UdpTmTcBridge.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_
|
||||||
|
#define FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_
|
||||||
|
|
||||||
|
#include "TcpIpBase.h"
|
||||||
|
#include "../../platform.h"
|
||||||
|
#include "../../tmtcservices/TmTcBridge.h"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_WIN
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This class can be used with the UdpTcPollingTask to implement a UDP server
|
||||||
|
* for receiving and sending PUS TMTC.
|
||||||
|
* @details
|
||||||
|
* This bridge task will take care of sending telemetry back to a UDP client if a connection
|
||||||
|
* was established and store them in a FIFO if this was not done yet. It is also be the default
|
||||||
|
* destination for telecommands, but the telecommands will be relayed to a specified tcDestination
|
||||||
|
* directly.
|
||||||
|
*/
|
||||||
|
class UdpTmTcBridge:
|
||||||
|
public TmTcBridge,
|
||||||
|
public TcpIpBase {
|
||||||
|
friend class UdpTcPollingTask;
|
||||||
|
public:
|
||||||
|
/* The ports chosen here should not be used by any other process. */
|
||||||
|
static const std::string DEFAULT_UDP_SERVER_PORT;
|
||||||
|
|
||||||
|
UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
|
||||||
|
std::string udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE,
|
||||||
|
object_id_t tcStoreId = objects::TC_STORE);
|
||||||
|
virtual~ UdpTmTcBridge();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set properties of internal mutex.
|
||||||
|
*/
|
||||||
|
void setMutexProperties(MutexIF::TimeoutType timeoutType, dur_millis_t timeoutMs);
|
||||||
|
|
||||||
|
ReturnValue_t initialize() override;
|
||||||
|
|
||||||
|
void checkAndSetClientAddress(sockaddr& clientAddress);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string udpServerPort;
|
||||||
|
|
||||||
|
struct sockaddr clientAddress;
|
||||||
|
socklen_t clientAddressLen = 0;
|
||||||
|
|
||||||
|
//! Access to the client address is mutex protected as it is set by another task.
|
||||||
|
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
|
||||||
|
dur_millis_t mutexTimeoutMs = 20;
|
||||||
|
MutexIF* mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_ */
|
||||||
|
|
84
osal/common/tcpipCommon.cpp
Normal file
84
osal/common/tcpipCommon.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "tcpipCommon.h"
|
||||||
|
#include <fsfw/serviceinterface/ServiceInterface.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void tcpip::determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string &protStr,
|
||||||
|
std::string &srcString) {
|
||||||
|
if(protocol == Protocol::TCP) {
|
||||||
|
protStr = "TCP";
|
||||||
|
}
|
||||||
|
else if(protocol == Protocol::UDP) {
|
||||||
|
protStr = "UDP";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
protStr = "Unknown protocol";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(errorSrc == ErrorSources::SETSOCKOPT_CALL) {
|
||||||
|
srcString = "setsockopt call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::SOCKET_CALL) {
|
||||||
|
srcString = "socket call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::LISTEN_CALL) {
|
||||||
|
srcString = "listen call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::ACCEPT_CALL) {
|
||||||
|
srcString = "accept call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::RECVFROM_CALL) {
|
||||||
|
srcString = "recvfrom call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::SEND_CALL) {
|
||||||
|
srcString = "send call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::SENDTO_CALL) {
|
||||||
|
srcString = "sendto call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::GETADDRINFO_CALL) {
|
||||||
|
srcString = "getaddrinfo call";
|
||||||
|
}
|
||||||
|
else if(errorSrc == ErrorSources::SHUTDOWN_CALL) {
|
||||||
|
srcString = "shutdown call";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
srcString = "unknown call";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tcpip::printAddress(struct sockaddr* addr) {
|
||||||
|
char ipAddress[INET6_ADDRSTRLEN] = {};
|
||||||
|
const char* stringPtr = NULL;
|
||||||
|
switch(addr->sa_family) {
|
||||||
|
case AF_INET: {
|
||||||
|
struct sockaddr_in *addrIn = reinterpret_cast<struct sockaddr_in*>(addr);
|
||||||
|
stringPtr = inet_ntop(AF_INET, &(addrIn->sin_addr), ipAddress, INET_ADDRSTRLEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AF_INET6: {
|
||||||
|
struct sockaddr_in6 *addrIn = reinterpret_cast<struct sockaddr_in6*>(addr);
|
||||||
|
stringPtr = inet_ntop(AF_INET6, &(addrIn->sin6_addr), ipAddress, INET6_ADDRSTRLEN);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
if(stringPtr == NULL) {
|
||||||
|
sif::debug << "Could not convert IP address to text representation, error code "
|
||||||
|
<< errno << std::endl;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::debug << "IP Address Sender: " << ipAddress << std::endl;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(stringPtr == NULL) {
|
||||||
|
sif::printDebug("Could not convert IP address to text representation, error code %d\n",
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
sif::printDebug("IP Address Sender: %s\n", ipAddress);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
44
osal/common/tcpipCommon.h
Normal file
44
osal/common/tcpipCommon.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef FSFW_OSAL_COMMON_TCPIPCOMMON_H_
|
||||||
|
#define FSFW_OSAL_COMMON_TCPIPCOMMON_H_
|
||||||
|
|
||||||
|
#include "../../timemanager/clockDefinitions.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#else
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace tcpip {
|
||||||
|
|
||||||
|
const char* const DEFAULT_SERVER_PORT = "7301";
|
||||||
|
|
||||||
|
enum class Protocol {
|
||||||
|
UDP,
|
||||||
|
TCP
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ErrorSources {
|
||||||
|
GETADDRINFO_CALL,
|
||||||
|
SOCKET_CALL,
|
||||||
|
SETSOCKOPT_CALL,
|
||||||
|
BIND_CALL,
|
||||||
|
RECV_CALL,
|
||||||
|
RECVFROM_CALL,
|
||||||
|
LISTEN_CALL,
|
||||||
|
ACCEPT_CALL,
|
||||||
|
SEND_CALL,
|
||||||
|
SENDTO_CALL,
|
||||||
|
SHUTDOWN_CALL
|
||||||
|
};
|
||||||
|
|
||||||
|
void determineErrorStrings(Protocol protocol, ErrorSources errorSrc, std::string& protStr,
|
||||||
|
std::string& srcString);
|
||||||
|
|
||||||
|
void printAddress(struct sockaddr* addr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_COMMON_TCPIPCOMMON_H_ */
|
15
osal/common/tcpipHelpers.h
Normal file
15
osal/common/tcpipHelpers.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
|
||||||
|
#define FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_
|
||||||
|
|
||||||
|
#include "../../timemanager/clockDefinitions.h"
|
||||||
|
#include "tcpipCommon.h"
|
||||||
|
|
||||||
|
namespace tcpip {
|
||||||
|
|
||||||
|
void handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration = 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_WINDOWS_TCPIPHELPERS_H_ */
|
@ -10,6 +10,7 @@ target_sources(${LIB_FSFW_NAME}
|
|||||||
QueueMapManager.cpp
|
QueueMapManager.cpp
|
||||||
SemaphoreFactory.cpp
|
SemaphoreFactory.cpp
|
||||||
TaskFactory.cpp
|
TaskFactory.cpp
|
||||||
|
taskHelpers.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#include "../../serviceinterface/ServiceInterface.h"
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
#include "../../timemanager/Clock.h"
|
#include "../../timemanager/Clock.h"
|
||||||
|
#include "../../platform.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#if defined(WIN32)
|
|
||||||
|
#if defined(PLATFORM_WIN)
|
||||||
#include <sysinfoapi.h>
|
#include <sysinfoapi.h>
|
||||||
#elif defined(LINUX)
|
#elif defined(PLATFORM_UNIX)
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -46,7 +48,7 @@ ReturnValue_t Clock::setClock(const timeval* time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t Clock::getClock_timeval(timeval* time) {
|
ReturnValue_t Clock::getClock_timeval(timeval* time) {
|
||||||
#if defined(WIN32)
|
#if defined(PLATFORM_WIN)
|
||||||
auto now = std::chrono::system_clock::now();
|
auto now = std::chrono::system_clock::now();
|
||||||
auto secondsChrono = std::chrono::time_point_cast<std::chrono::seconds>(now);
|
auto secondsChrono = std::chrono::time_point_cast<std::chrono::seconds>(now);
|
||||||
auto epoch = now.time_since_epoch();
|
auto epoch = now.time_since_epoch();
|
||||||
@ -54,7 +56,7 @@ ReturnValue_t Clock::getClock_timeval(timeval* time) {
|
|||||||
auto fraction = now - secondsChrono;
|
auto fraction = now - secondsChrono;
|
||||||
time->tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(fraction).count();
|
time->tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(fraction).count();
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
#elif defined(LINUX)
|
#elif defined(PLATFORM_UNIX)
|
||||||
timespec timeUnix;
|
timespec timeUnix;
|
||||||
int status = clock_gettime(CLOCK_REALTIME,&timeUnix);
|
int status = clock_gettime(CLOCK_REALTIME,&timeUnix);
|
||||||
if(status!=0){
|
if(status!=0){
|
||||||
@ -85,14 +87,14 @@ ReturnValue_t Clock::getClock_usecs(uint64_t* time) {
|
|||||||
|
|
||||||
timeval Clock::getUptime() {
|
timeval Clock::getUptime() {
|
||||||
timeval timeval;
|
timeval timeval;
|
||||||
#if defined(WIN32)
|
#if defined(PLATFORM_WIN)
|
||||||
auto uptime = std::chrono::milliseconds(GetTickCount64());
|
auto uptime = std::chrono::milliseconds(GetTickCount64());
|
||||||
auto secondsChrono = std::chrono::duration_cast<std::chrono::seconds>(uptime);
|
auto secondsChrono = std::chrono::duration_cast<std::chrono::seconds>(uptime);
|
||||||
timeval.tv_sec = secondsChrono.count();
|
timeval.tv_sec = secondsChrono.count();
|
||||||
auto fraction = uptime - secondsChrono;
|
auto fraction = uptime - secondsChrono;
|
||||||
timeval.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
|
timeval.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||||
fraction).count();
|
fraction).count();
|
||||||
#elif defined(LINUX)
|
#elif defined(PLATFORM_UNIX)
|
||||||
double uptimeSeconds;
|
double uptimeSeconds;
|
||||||
if (std::ifstream("/proc/uptime", std::ios::in) >> uptimeSeconds)
|
if (std::ifstream("/proc/uptime", std::ios::in) >> uptimeSeconds)
|
||||||
{
|
{
|
||||||
@ -119,7 +121,6 @@ ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) {
|
|||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
|
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
|
||||||
/* Do some magic with chrono (C++20!) */
|
/* Do some magic with chrono (C++20!) */
|
||||||
/* Right now, the library doesn't have the new features to get the required values yet.
|
/* Right now, the library doesn't have the new features to get the required values yet.
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
|
#include "taskHelpers.h"
|
||||||
|
#include "../../platform.h"
|
||||||
#include "../../osal/host/FixedTimeslotTask.h"
|
#include "../../osal/host/FixedTimeslotTask.h"
|
||||||
|
|
||||||
#include "../../ipc/MutexFactory.h"
|
#include "../../ipc/MutexFactory.h"
|
||||||
#include "../../osal/host/Mutex.h"
|
#include "../../osal/host/Mutex.h"
|
||||||
#include "../../osal/host/FixedTimeslotTask.h"
|
#include "../../osal/host/FixedTimeslotTask.h"
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
|
||||||
#include "../../tasks/ExecutableObjectIF.h"
|
#include "../../tasks/ExecutableObjectIF.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(PLATFORM_WIN)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#elif defined(LINUX)
|
#include "../windows/winTaskHelpers.h"
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -24,34 +25,12 @@ FixedTimeslotTask::FixedTimeslotTask(const char *name, TaskPriority setPriority,
|
|||||||
// It is propably possible to set task priorities by using the native
|
// It is propably possible to set task priorities by using the native
|
||||||
// task handles for Windows / Linux
|
// task handles for Windows / Linux
|
||||||
mainThread = std::thread(&FixedTimeslotTask::taskEntryPoint, this, this);
|
mainThread = std::thread(&FixedTimeslotTask::taskEntryPoint, this, this);
|
||||||
#if defined(WIN32)
|
#if defined(_WIN32)
|
||||||
/* List of possible priority classes:
|
tasks::setTaskPriority(reinterpret_cast<HANDLE>(mainThread.native_handle()), setPriority);
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/
|
#elif defined(__unix__)
|
||||||
* nf-processthreadsapi-setpriorityclass
|
// TODO: We could reuse existing code here.
|
||||||
* And respective thread priority numbers:
|
|
||||||
* https://docs.microsoft.com/en-us/windows/
|
|
||||||
* win32/procthread/scheduling-priorities */
|
|
||||||
int result = SetPriorityClass(
|
|
||||||
reinterpret_cast<HANDLE>(mainThread.native_handle()),
|
|
||||||
ABOVE_NORMAL_PRIORITY_CLASS);
|
|
||||||
if(result != 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "FixedTimeslotTask: Windows SetPriorityClass failed with code "
|
|
||||||
<< GetLastError() << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
result = SetThreadPriority(
|
|
||||||
reinterpret_cast<HANDLE>(mainThread.native_handle()),
|
|
||||||
THREAD_PRIORITY_NORMAL);
|
|
||||||
if(result != 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "FixedTimeslotTask: Windows SetPriorityClass failed with code "
|
|
||||||
<< GetLastError() << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#elif defined(LINUX)
|
|
||||||
// TODO: we can just copy and paste the code from the linux OSAL here.
|
|
||||||
#endif
|
#endif
|
||||||
|
tasks::insertTaskName(mainThread.get_id(), taskName);
|
||||||
}
|
}
|
||||||
|
|
||||||
FixedTimeslotTask::~FixedTimeslotTask(void) {
|
FixedTimeslotTask::~FixedTimeslotTask(void) {
|
||||||
@ -60,7 +39,6 @@ FixedTimeslotTask::~FixedTimeslotTask(void) {
|
|||||||
if(mainThread.joinable()) {
|
if(mainThread.joinable()) {
|
||||||
mainThread.join();
|
mainThread.join();
|
||||||
}
|
}
|
||||||
delete this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FixedTimeslotTask::taskEntryPoint(void* argument) {
|
void FixedTimeslotTask::taskEntryPoint(void* argument) {
|
||||||
@ -141,8 +119,11 @@ ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Component " << std::hex << componentId <<
|
sif::error << "Component " << std::hex << "0x" << componentId << "not found, "
|
||||||
" not found, not adding it to pst" << std::endl;
|
"not adding it to PST.." << std::dec << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("Component 0x%08x not found, not adding it to PST..\n",
|
||||||
|
static_cast<unsigned int>(componentId));
|
||||||
#endif
|
#endif
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,9 @@
|
|||||||
|
|
||||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||||
#include "../../ipc/MutexFactory.h"
|
#include "../../ipc/MutexFactory.h"
|
||||||
#include "../../ipc/MutexHelper.h"
|
#include "../../ipc/MutexGuard.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize):
|
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize):
|
||||||
messageSize(maxMessageSize), messageDepth(messageDepth) {
|
messageSize(maxMessageSize), messageDepth(messageDepth) {
|
||||||
@ -63,12 +65,9 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
|
|||||||
if(messageQueue.empty()) {
|
if(messageQueue.empty()) {
|
||||||
return MessageQueueIF::EMPTY;
|
return MessageQueueIF::EMPTY;
|
||||||
}
|
}
|
||||||
// not sure this will work..
|
MutexGuard mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
|
||||||
//*message = std::move(messageQueue.front());
|
std::copy(messageQueue.front().data(), messageQueue.front().data() + messageSize,
|
||||||
MutexHelper mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
|
message->getBuffer());
|
||||||
MessageQueueMessage* currentMessage = &messageQueue.front();
|
|
||||||
std::copy(currentMessage->getBuffer(),
|
|
||||||
currentMessage->getBuffer() + messageSize, message->getBuffer());
|
|
||||||
messageQueue.pop();
|
messageQueue.pop();
|
||||||
// The last partner is the first uint32_t field in the message
|
// The last partner is the first uint32_t field in the message
|
||||||
this->lastPartner = message->getSender();
|
this->lastPartner = message->getSender();
|
||||||
@ -82,7 +81,7 @@ MessageQueueId_t MessageQueue::getLastPartner() const {
|
|||||||
ReturnValue_t MessageQueue::flush(uint32_t* count) {
|
ReturnValue_t MessageQueue::flush(uint32_t* count) {
|
||||||
*count = messageQueue.size();
|
*count = messageQueue.size();
|
||||||
// Clears the queue.
|
// Clears the queue.
|
||||||
messageQueue = std::queue<MessageQueueMessage>();
|
messageQueue = std::queue<std::vector<uint8_t>>();
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +107,9 @@ bool MessageQueue::isDefaultDestinationSet() const {
|
|||||||
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
||||||
MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
|
MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
|
||||||
bool ignoreFault) {
|
bool ignoreFault) {
|
||||||
|
if(message == nullptr) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
message->setSender(sentFrom);
|
message->setSender(sentFrom);
|
||||||
if(message->getMessageSize() > message->getMaximumMessageSize()) {
|
if(message->getMessageSize() > message->getMaximumMessageSize()) {
|
||||||
// Actually, this should never happen or an error will be emitted
|
// Actually, this should never happen or an error will be emitted
|
||||||
@ -126,25 +128,13 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
|||||||
internalErrorReporter->queueMessageNotSent();
|
internalErrorReporter->queueMessageNotSent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Better returnvalue
|
return MessageQueueIF::DESTINATION_INVALID;
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
|
||||||
}
|
}
|
||||||
if(targetQueue->messageQueue.size() < targetQueue->messageDepth) {
|
if(targetQueue->messageQueue.size() < targetQueue->messageDepth) {
|
||||||
MutexHelper mutexLock(targetQueue->queueLock,
|
MutexGuard mutexLock(targetQueue->queueLock, MutexIF::TimeoutType::WAITING, 20);
|
||||||
MutexIF::TimeoutType::WAITING, 20);
|
targetQueue->messageQueue.push(std::vector<uint8_t>(message->getMaximumMessageSize()));
|
||||||
// not ideal, works for now though.
|
memcpy(targetQueue->messageQueue.back().data(), message->getBuffer(),
|
||||||
MessageQueueMessage* mqmMessage =
|
message->getMaximumMessageSize());
|
||||||
dynamic_cast<MessageQueueMessage*>(message);
|
|
||||||
if(message != nullptr) {
|
|
||||||
targetQueue->messageQueue.push(*mqmMessage);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "MessageQueue::sendMessageFromMessageQueue: Message"
|
|
||||||
"is not MessageQueueMessage!" << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return MessageQueueIF::FULL;
|
return MessageQueueIF::FULL;
|
||||||
|
@ -212,20 +212,20 @@ protected:
|
|||||||
//static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault);
|
//static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::queue<MessageQueueMessage> messageQueue;
|
std::queue<std::vector<uint8_t>> messageQueue;
|
||||||
/**
|
/**
|
||||||
* @brief The class stores the queue id it got assigned.
|
* @brief The class stores the queue id it got assigned.
|
||||||
* If initialization fails, the queue id is set to zero.
|
* If initialization fails, the queue id is set to zero.
|
||||||
*/
|
*/
|
||||||
MessageQueueId_t mqId = 0;
|
MessageQueueId_t mqId = MessageQueueIF::NO_QUEUE;
|
||||||
size_t messageSize = 0;
|
size_t messageSize = 0;
|
||||||
size_t messageDepth = 0;
|
size_t messageDepth = 0;
|
||||||
|
|
||||||
MutexIF* queueLock;
|
MutexIF* queueLock;
|
||||||
|
|
||||||
bool defaultDestinationSet = false;
|
bool defaultDestinationSet = false;
|
||||||
MessageQueueId_t defaultDestination = 0;
|
MessageQueueId_t defaultDestination = MessageQueueIF::NO_QUEUE;
|
||||||
MessageQueueId_t lastPartner = 0;
|
MessageQueueId_t lastPartner = MessageQueueIF::NO_QUEUE;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ */
|
#endif /* FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ */
|
||||||
|
@ -24,5 +24,7 @@ MutexIF* MutexFactory::createMutex() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MutexFactory::deleteMutex(MutexIF* mutex) {
|
void MutexFactory::deleteMutex(MutexIF* mutex) {
|
||||||
delete mutex;
|
if(mutex != nullptr) {
|
||||||
|
delete mutex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#include "Mutex.h"
|
#include "Mutex.h"
|
||||||
#include "PeriodicTask.h"
|
#include "PeriodicTask.h"
|
||||||
|
#include "taskHelpers.h"
|
||||||
|
|
||||||
|
#include "../../platform.h"
|
||||||
#include "../../ipc/MutexFactory.h"
|
#include "../../ipc/MutexFactory.h"
|
||||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
||||||
#include "../../tasks/ExecutableObjectIF.h"
|
#include "../../tasks/ExecutableObjectIF.h"
|
||||||
@ -8,9 +10,10 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
#if defined(WIN32)
|
#if defined(PLATFORM_WIN)
|
||||||
#include <processthreadsapi.h>
|
#include <processthreadsapi.h>
|
||||||
#elif defined(LINUX)
|
#include <fsfw/osal/windows/winTaskHelpers.h>
|
||||||
|
#elif defined(PLATFORM_UNIX)
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -19,37 +22,15 @@ PeriodicTask::PeriodicTask(const char *name, TaskPriority setPriority,
|
|||||||
void (*setDeadlineMissedFunc)()) :
|
void (*setDeadlineMissedFunc)()) :
|
||||||
started(false), taskName(name), period(setPeriod),
|
started(false), taskName(name), period(setPeriod),
|
||||||
deadlineMissedFunc(setDeadlineMissedFunc) {
|
deadlineMissedFunc(setDeadlineMissedFunc) {
|
||||||
// It is propably possible to set task priorities by using the native
|
// It is probably possible to set task priorities by using the native
|
||||||
// task handles for Windows / Linux
|
// task handles for Windows / Linux
|
||||||
mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this);
|
mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this);
|
||||||
#if defined(WIN32)
|
#if defined(PLATFORM_WIN)
|
||||||
/* List of possible priority classes:
|
tasks::setTaskPriority(reinterpret_cast<HANDLE>(mainThread.native_handle()), setPriority);
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/
|
#elif defined(PLATFORM_UNIX)
|
||||||
* nf-processthreadsapi-setpriorityclass
|
// TODO: We could reuse existing code here.
|
||||||
* And respective thread priority numbers:
|
|
||||||
* https://docs.microsoft.com/en-us/windows/
|
|
||||||
* win32/procthread/scheduling-priorities */
|
|
||||||
int result = SetPriorityClass(
|
|
||||||
reinterpret_cast<HANDLE>(mainThread.native_handle()),
|
|
||||||
ABOVE_NORMAL_PRIORITY_CLASS);
|
|
||||||
if(result != 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "PeriodicTask: Windows SetPriorityClass failed with code "
|
|
||||||
<< GetLastError() << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
result = SetThreadPriority(
|
|
||||||
reinterpret_cast<HANDLE>(mainThread.native_handle()),
|
|
||||||
THREAD_PRIORITY_NORMAL);
|
|
||||||
if(result != 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "PeriodicTask: Windows SetPriorityClass failed with code "
|
|
||||||
<< GetLastError() << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#elif defined(LINUX)
|
|
||||||
// we can just copy and paste the code from linux here.
|
|
||||||
#endif
|
#endif
|
||||||
|
tasks::insertTaskName(mainThread.get_id(), taskName);
|
||||||
}
|
}
|
||||||
|
|
||||||
PeriodicTask::~PeriodicTask(void) {
|
PeriodicTask::~PeriodicTask(void) {
|
||||||
@ -58,7 +39,6 @@ PeriodicTask::~PeriodicTask(void) {
|
|||||||
if(mainThread.joinable()) {
|
if(mainThread.joinable()) {
|
||||||
mainThread.join();
|
mainThread.join();
|
||||||
}
|
}
|
||||||
delete this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeriodicTask::taskEntryPoint(void* argument) {
|
void PeriodicTask::taskEntryPoint(void* argument) {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "../../serviceinterface/ServiceInterface.h"
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
#include "../../ipc/MutexFactory.h"
|
#include "../../ipc/MutexFactory.h"
|
||||||
#include "../../ipc/MutexHelper.h"
|
#include "../../ipc/MutexGuard.h"
|
||||||
|
|
||||||
QueueMapManager* QueueMapManager::mqManagerInstance = nullptr;
|
QueueMapManager* QueueMapManager::mqManagerInstance = nullptr;
|
||||||
|
|
||||||
@ -10,6 +10,10 @@ QueueMapManager::QueueMapManager() {
|
|||||||
mapLock = MutexFactory::instance()->createMutex();
|
mapLock = MutexFactory::instance()->createMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QueueMapManager::~QueueMapManager() {
|
||||||
|
MutexFactory::instance()->deleteMutex(mapLock);
|
||||||
|
}
|
||||||
|
|
||||||
QueueMapManager* QueueMapManager::instance() {
|
QueueMapManager* QueueMapManager::instance() {
|
||||||
if (mqManagerInstance == nullptr){
|
if (mqManagerInstance == nullptr){
|
||||||
mqManagerInstance = new QueueMapManager();
|
mqManagerInstance = new QueueMapManager();
|
||||||
@ -43,7 +47,7 @@ ReturnValue_t QueueMapManager::addMessageQueue(
|
|||||||
|
|
||||||
MessageQueueIF* QueueMapManager::getMessageQueue(
|
MessageQueueIF* QueueMapManager::getMessageQueue(
|
||||||
MessageQueueId_t messageQueueId) const {
|
MessageQueueId_t messageQueueId) const {
|
||||||
MutexHelper(mapLock, MutexIF::TimeoutType::WAITING, 50);
|
MutexGuard(mapLock, MutexIF::TimeoutType::WAITING, 50);
|
||||||
auto queueIter = queueMap.find(messageQueueId);
|
auto queueIter = queueMap.find(messageQueueId);
|
||||||
if(queueIter != queueMap.end()) {
|
if(queueIter != queueMap.end()) {
|
||||||
return queueIter->second;
|
return queueIter->second;
|
||||||
|
@ -36,6 +36,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
//! External instantiation is forbidden.
|
//! External instantiation is forbidden.
|
||||||
QueueMapManager();
|
QueueMapManager();
|
||||||
|
~QueueMapManager();
|
||||||
|
|
||||||
uint32_t queueCounter = 0;
|
uint32_t queueCounter = 0;
|
||||||
MutexIF* mapLock;
|
MutexIF* mapLock;
|
||||||
QueueMap queueMap;
|
QueueMap queueMap;
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#include "../../tasks/SemaphoreFactory.h"
|
#include "../../tasks/SemaphoreFactory.h"
|
||||||
#include "../../osal/linux/BinarySemaphore.h"
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
#include "../../osal/linux/CountingSemaphore.h"
|
|
||||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
|
||||||
|
|
||||||
SemaphoreFactory* SemaphoreFactory::factoryInstance = nullptr;
|
SemaphoreFactory* SemaphoreFactory::factoryInstance = nullptr;
|
||||||
|
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
#include "taskHelpers.h"
|
||||||
|
#include "../../tasks/TaskFactory.h"
|
||||||
#include "../../osal/host/FixedTimeslotTask.h"
|
#include "../../osal/host/FixedTimeslotTask.h"
|
||||||
#include "../../osal/host/PeriodicTask.h"
|
#include "../../osal/host/PeriodicTask.h"
|
||||||
#include "../../tasks/TaskFactory.h"
|
|
||||||
#include "../../returnvalues/HasReturnvaluesIF.h"
|
#include "../../returnvalues/HasReturnvaluesIF.h"
|
||||||
#include "../../tasks/PeriodicTaskIF.h"
|
#include "../../tasks/PeriodicTaskIF.h"
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
TaskFactory* TaskFactory::factoryInstance = new TaskFactory();
|
TaskFactory* TaskFactory::factoryInstance = new TaskFactory();
|
||||||
|
|
||||||
// Will propably not be used for hosted implementation
|
// Not used for the host implementation for now because C++ thread abstraction is used
|
||||||
const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = 0;
|
const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = 0;
|
||||||
|
|
||||||
TaskFactory::TaskFactory() {
|
TaskFactory::TaskFactory() {
|
||||||
@ -49,8 +51,12 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TaskFactory::printMissedDeadline() {
|
void TaskFactory::printMissedDeadline() {
|
||||||
/* TODO: Implement */
|
std::string name = tasks::getTaskName(std::this_thread::get_id());
|
||||||
return;
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "TaskFactory::printMissedDeadline: " << name << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("TaskFactory::printMissedDeadline: %s\n", name);
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
27
osal/host/taskHelpers.cpp
Normal file
27
osal/host/taskHelpers.cpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#include "taskHelpers.h"
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
std::mutex nameMapLock;
|
||||||
|
std::map<std::thread::id, std::string> taskNameMap;
|
||||||
|
|
||||||
|
ReturnValue_t tasks::insertTaskName(std::thread::id threadId, std::string taskName) {
|
||||||
|
std::lock_guard<std::mutex> lg(nameMapLock);
|
||||||
|
auto returnPair = taskNameMap.emplace(threadId, taskName);
|
||||||
|
if(not returnPair.second) {
|
||||||
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
|
}
|
||||||
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string tasks::getTaskName(std::thread::id threadId) {
|
||||||
|
std::lock_guard<std::mutex> lg(nameMapLock);
|
||||||
|
auto resultIter = taskNameMap.find(threadId);
|
||||||
|
if(resultIter != taskNameMap.end()) {
|
||||||
|
return resultIter->second;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return "Unknown task";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
16
osal/host/taskHelpers.h
Normal file
16
osal/host/taskHelpers.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#ifndef FSFW_OSAL_HOST_TASKHELPERS_H_
|
||||||
|
#define FSFW_OSAL_HOST_TASKHELPERS_H_
|
||||||
|
|
||||||
|
#include <fsfw/returnvalues/HasReturnvaluesIF.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace tasks {
|
||||||
|
|
||||||
|
ReturnValue_t insertTaskName(std::thread::id threadId, std::string taskName);
|
||||||
|
std::string getTaskName(std::thread::id threadId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* FSFW_OSAL_HOST_TASKHELPERS_H_ */
|
@ -13,9 +13,8 @@ target_sources(${LIB_FSFW_NAME}
|
|||||||
QueueFactory.cpp
|
QueueFactory.cpp
|
||||||
SemaphoreFactory.cpp
|
SemaphoreFactory.cpp
|
||||||
TaskFactory.cpp
|
TaskFactory.cpp
|
||||||
TcUnixUdpPollingTask.cpp
|
|
||||||
TmTcUnixUdpBridge.cpp
|
|
||||||
Timer.cpp
|
Timer.cpp
|
||||||
|
tcpipHelpers.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
@ -190,13 +190,15 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
|
|||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}else if(status==0){
|
}
|
||||||
|
else if (status==0) {
|
||||||
//Success but no message received
|
//Success but no message received
|
||||||
return MessageQueueIF::EMPTY;
|
return MessageQueueIF::EMPTY;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
//No message was received. Keep lastPartner anyway, I might send
|
//No message was received. Keep lastPartner anyway, I might send
|
||||||
//something later. But still, delete packet content.
|
//something later. But still, delete packet content.
|
||||||
memset(message->getData(), 0, message->getMaximumMessageSize());
|
memset(message->getData(), 0, message->getMaximumDataSize());
|
||||||
switch(errno){
|
switch(errno){
|
||||||
case EAGAIN:
|
case EAGAIN:
|
||||||
//O_NONBLOCK or MQ_NONBLOCK was set and there are no messages
|
//O_NONBLOCK or MQ_NONBLOCK was set and there are no messages
|
||||||
@ -371,7 +373,7 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
|
|||||||
<<"mq_send to: " << sendTo << " sent from "
|
<<"mq_send to: " << sendTo << " sent from "
|
||||||
<< sentFrom << std::endl;
|
<< sentFrom << std::endl;
|
||||||
#endif
|
#endif
|
||||||
return DESTINVATION_INVALID;
|
return DESTINATION_INVALID;
|
||||||
}
|
}
|
||||||
case EINTR:
|
case EINTR:
|
||||||
//The call was interrupted by a signal.
|
//The call was interrupted by a signal.
|
||||||
|
@ -6,250 +6,263 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
PosixThread::PosixThread(const char* name_, int priority_, size_t stackSize_):
|
PosixThread::PosixThread(const char* name_, int priority_, size_t stackSize_):
|
||||||
thread(0),priority(priority_),stackSize(stackSize_) {
|
thread(0), priority(priority_), stackSize(stackSize_) {
|
||||||
name[0] = '\0';
|
name[0] = '\0';
|
||||||
std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1);
|
std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
PosixThread::~PosixThread() {
|
PosixThread::~PosixThread() {
|
||||||
//No deletion and no free of Stack Pointer
|
//No deletion and no free of Stack Pointer
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnValue_t PosixThread::sleep(uint64_t ns) {
|
ReturnValue_t PosixThread::sleep(uint64_t ns) {
|
||||||
//TODO sleep might be better with timer instead of sleep()
|
//TODO sleep might be better with timer instead of sleep()
|
||||||
timespec time;
|
timespec time;
|
||||||
time.tv_sec = ns/1000000000;
|
time.tv_sec = ns/1000000000;
|
||||||
time.tv_nsec = ns - time.tv_sec*1e9;
|
time.tv_nsec = ns - time.tv_sec*1e9;
|
||||||
|
|
||||||
//Remaining Time is not set here
|
//Remaining Time is not set here
|
||||||
int status = nanosleep(&time,NULL);
|
int status = nanosleep(&time,NULL);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
switch(errno){
|
switch(errno){
|
||||||
case EINTR:
|
case EINTR:
|
||||||
//The nanosleep() function was interrupted by a signal.
|
//The nanosleep() function was interrupted by a signal.
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
case EINVAL:
|
case EINVAL:
|
||||||
//The rqtp argument specified a nanosecond value less than zero or
|
//The rqtp argument specified a nanosecond value less than zero or
|
||||||
// greater than or equal to 1000 million.
|
// greater than or equal to 1000 million.
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
default:
|
default:
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
return HasReturnvaluesIF::RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PosixThread::suspend() {
|
void PosixThread::suspend() {
|
||||||
//Wait for SIGUSR1
|
//Wait for SIGUSR1
|
||||||
int caughtSig = 0;
|
int caughtSig = 0;
|
||||||
sigset_t waitSignal;
|
sigset_t waitSignal;
|
||||||
sigemptyset(&waitSignal);
|
sigemptyset(&waitSignal);
|
||||||
sigaddset(&waitSignal, SIGUSR1);
|
sigaddset(&waitSignal, SIGUSR1);
|
||||||
sigwait(&waitSignal, &caughtSig);
|
sigwait(&waitSignal, &caughtSig);
|
||||||
if (caughtSig != SIGUSR1) {
|
if (caughtSig != SIGUSR1) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
|
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
|
||||||
caughtSig << std::endl;
|
caughtSig << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PosixThread::resume(){
|
void PosixThread::resume(){
|
||||||
/* Signal the thread to start. Makes sense to call kill to start or? ;)
|
/* Signal the thread to start. Makes sense to call kill to start or? ;)
|
||||||
According to POSIX raise(signal) will call pthread_kill(pthread_self(), sig),
|
*
|
||||||
but as the call must be done from the thread itself this is not possible here */
|
* According to Posix raise(signal) will call pthread_kill(pthread_self(), sig),
|
||||||
pthread_kill(thread,SIGUSR1);
|
* but as the call must be done from the thread itsself this is not possible here
|
||||||
|
*/
|
||||||
|
pthread_kill(thread,SIGUSR1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms,
|
bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms,
|
||||||
const uint64_t delayTime_ms) {
|
const uint64_t delayTime_ms) {
|
||||||
uint64_t nextTimeToWake_ms;
|
uint64_t nextTimeToWake_ms;
|
||||||
bool shouldDelay = false;
|
bool shouldDelay = false;
|
||||||
/* Get current Time */
|
//Get current Time
|
||||||
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
|
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
|
||||||
/* Generate the tick time at which the task wants to wake. */
|
/* Generate the tick time at which the task wants to wake. */
|
||||||
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
|
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
|
||||||
|
|
||||||
if (currentTime_ms < *prevoiusWakeTime_ms) {
|
if (currentTime_ms < *prevoiusWakeTime_ms) {
|
||||||
/* The tick count has overflowed since this function was
|
/* The tick count has overflowed since this function was
|
||||||
lasted called. In this case the only time we should ever
|
lasted called. In this case the only time we should ever
|
||||||
actually delay is if the wake time has also overflowed,
|
actually delay is if the wake time has also overflowed,
|
||||||
and the wake time is greater than the tick time. When this
|
and the wake time is greater than the tick time. When this
|
||||||
is the case it is as if neither time had overflowed. */
|
is the case it is as if neither time had overflowed. */
|
||||||
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|
||||||
&& (nextTimeToWake_ms > currentTime_ms)) {
|
&& (nextTimeToWake_ms > currentTime_ms)) {
|
||||||
shouldDelay = true;
|
shouldDelay = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* The tick time has not overflowed. In this case we will
|
/* The tick time has not overflowed. In this case we will
|
||||||
delay if either the wake time has overflowed, and/or the
|
delay if either the wake time has overflowed, and/or the
|
||||||
tick time is less than the wake time. */
|
tick time is less than the wake time. */
|
||||||
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|
||||||
|| (nextTimeToWake_ms > currentTime_ms)) {
|
|| (nextTimeToWake_ms > currentTime_ms)) {
|
||||||
shouldDelay = true;
|
shouldDelay = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update the wake time ready for the next call. */
|
/* Update the wake time ready for the next call. */
|
||||||
|
|
||||||
(*prevoiusWakeTime_ms) = nextTimeToWake_ms;
|
(*prevoiusWakeTime_ms) = nextTimeToWake_ms;
|
||||||
|
|
||||||
if (shouldDelay) {
|
if (shouldDelay) {
|
||||||
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
|
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
|
||||||
PosixThread::sleep(sleepTime * 1000000ull);
|
PosixThread::sleep(sleepTime * 1000000ull);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
/* We are shifting the time in case the deadline was missed like RTEMS */
|
//We are shifting the time in case the deadline was missed like rtems
|
||||||
(*prevoiusWakeTime_ms) = currentTime_ms;
|
(*prevoiusWakeTime_ms) = currentTime_ms;
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint64_t PosixThread::getCurrentMonotonicTimeMs(){
|
uint64_t PosixThread::getCurrentMonotonicTimeMs(){
|
||||||
timespec timeNow;
|
timespec timeNow;
|
||||||
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
|
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
|
||||||
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
|
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
|
||||||
+ timeNow.tv_nsec / 1000000;
|
+ timeNow.tv_nsec / 1000000;
|
||||||
|
|
||||||
return currentTime_ms;
|
return currentTime_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
|
void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
//sif::debug << "PosixThread::createTask" << std::endl;
|
//sif::debug << "PosixThread::createTask" << std::endl;
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
* The attr argument points to a pthread_attr_t structure whose contents
|
* The attr argument points to a pthread_attr_t structure whose contents
|
||||||
* are used at thread creation time to determine attributes for the new
|
are used at thread creation time to determine attributes for the new
|
||||||
* thread; this structure is initialized using pthread_attr_init(3) and
|
thread; this structure is initialized using pthread_attr_init(3) and
|
||||||
* related functions. If attr is NULL, then the thread is created with
|
related functions. If attr is NULL, then the thread is created with
|
||||||
* default attributes.
|
default attributes.
|
||||||
*/
|
*/
|
||||||
pthread_attr_t attributes;
|
pthread_attr_t attributes;
|
||||||
int status = pthread_attr_init(&attributes);
|
int status = pthread_attr_init(&attributes);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread attribute init failed with: " <<
|
sif::error << "Posix Thread attribute init failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void* stackPointer;
|
void* stackPointer;
|
||||||
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
|
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: Stack init failed with: " <<
|
sif::error << "PosixThread::createTask: Stack init failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
if(errno == ENOMEM) {
|
if(errno == ENOMEM) {
|
||||||
size_t stackMb = stackSize/10e6;
|
size_t stackMb = stackSize/10e6;
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: Insufficient memory for"
|
sif::error << "PosixThread::createTask: Insufficient memory for"
|
||||||
" the requested " << stackMb << " MB" << std::endl;
|
" the requested " << stackMb << " MB" << std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printError("PosixThread::createTask: Insufficient memory for "
|
sif::printError("PosixThread::createTask: Insufficient memory for "
|
||||||
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
|
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else if(errno == EINVAL) {
|
else if(errno == EINVAL) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: Wrong alignment argument!"
|
sif::error << "PosixThread::createTask: Wrong alignment argument!"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
#else
|
#else
|
||||||
sif::printError("PosixThread::createTask: "
|
sif::printError("PosixThread::createTask: "
|
||||||
"Wrong alignment argument!\n");
|
"Wrong alignment argument!\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
|
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: pthread_attr_setstack "
|
sif::error << "PosixThread::createTask: pthread_attr_setstack "
|
||||||
" failed with: " << strerror(status) << std::endl;
|
" failed with: " << strerror(status) << std::endl;
|
||||||
sif::error << "Make sure the specified stack size is valid and is "
|
sif::error << "Make sure the specified stack size is valid and is "
|
||||||
"larger than the minimum allowed stack size." << std::endl;
|
"larger than the minimum allowed stack size." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
|
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
|
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#ifndef FSFW_USE_REALTIME_FOR_LINUX
|
||||||
|
#error "Please define FSFW_USE_REALTIME_FOR_LINUX with either 0 or 1"
|
||||||
|
#endif
|
||||||
|
#if FSFW_USE_REALTIME_FOR_LINUX == 1
|
||||||
|
// FIFO -> This needs root privileges for the process
|
||||||
|
status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO);
|
||||||
|
if(status != 0){
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::error << "Posix Thread attribute schedule policy failed with: " <<
|
||||||
|
strerror(status) << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// TODO FIFO -> This needs root privileges for the process
|
sched_param scheduleParams;
|
||||||
status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO);
|
scheduleParams.__sched_priority = priority;
|
||||||
if(status != 0){
|
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
|
||||||
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread attribute schedule policy failed with: " <<
|
sif::error << "Posix Thread attribute schedule params failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
sched_param scheduleParams;
|
//Set Signal Mask for suspend until startTask is called
|
||||||
scheduleParams.__sched_priority = priority;
|
sigset_t waitSignal;
|
||||||
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
|
sigemptyset(&waitSignal);
|
||||||
if(status != 0){
|
sigaddset(&waitSignal, SIGUSR1);
|
||||||
|
status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL);
|
||||||
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread attribute schedule params failed with: " <<
|
sif::error << "Posix Thread sigmask failed failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << " errno: " << strerror(errno) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set Signal Mask for suspend until startTask is called
|
|
||||||
sigset_t waitSignal;
|
|
||||||
sigemptyset(&waitSignal);
|
|
||||||
sigaddset(&waitSignal, SIGUSR1);
|
|
||||||
status = pthread_sigmask(SIG_BLOCK, &waitSignal, NULL);
|
|
||||||
if(status != 0){
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "Posix Thread sigmask failed failed with: " <<
|
|
||||||
strerror(status) << " errno: " << strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
status = pthread_create(&thread,&attributes,fnc_,arg_);
|
status = pthread_create(&thread,&attributes,fnc_,arg_);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread create failed with: " <<
|
sif::error << "PosixThread::createTask: Failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
|
sif::error << "For FSFW_USE_REALTIME_FOR_LINUX == 1 make sure to call " <<
|
||||||
|
"\"all sudo setcap 'cap_sys_nice=eip'\" on the application or set "
|
||||||
|
"/etc/security/limit.conf" << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printError("PosixThread::createTask: Create failed with: %s\n", strerror(status));
|
||||||
|
sif::printError("For FSFW_USE_REALTIME_FOR_LINUX == 1 make sure to call "
|
||||||
|
"\"all sudo setcap 'cap_sys_nice=eip'\" on the application or set "
|
||||||
|
"/etc/security/limit.conf\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
status = pthread_setname_np(thread,name);
|
status = pthread_setname_np(thread,name);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: setname failed with: " <<
|
sif::error << "PosixThread::createTask: setname failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
if(status == ERANGE) {
|
if(status == ERANGE) {
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: Task name length longer"
|
sif::error << "PosixThread::createTask: Task name length longer"
|
||||||
" than 16 chars. Truncating.." << std::endl;
|
" than 16 chars. Truncating.." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
name[15] = '\0';
|
name[15] = '\0';
|
||||||
status = pthread_setname_np(thread,name);
|
status = pthread_setname_np(thread,name);
|
||||||
if(status != 0){
|
if(status != 0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "PosixThread::createTask: Setting name"
|
sif::error << "PosixThread::createTask: Setting name"
|
||||||
" did not work.." << std::endl;
|
" did not work.." << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
status = pthread_attr_destroy(&attributes);
|
status = pthread_attr_destroy(&attributes);
|
||||||
if(status!=0){
|
if(status!=0){
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
sif::error << "Posix Thread attribute destroy failed with: " <<
|
sif::error << "Posix Thread attribute destroy failed with: " <<
|
||||||
strerror(status) << std::endl;
|
strerror(status) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "PeriodicPosixTask.h"
|
#include "PeriodicPosixTask.h"
|
||||||
|
|
||||||
#include "../../tasks/TaskFactory.h"
|
#include "../../tasks/TaskFactory.h"
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
#include "../../returnvalues/HasReturnvaluesIF.h"
|
#include "../../returnvalues/HasReturnvaluesIF.h"
|
||||||
|
|
||||||
//TODO: Different variant than the lazy loading in QueueFactory. What's better and why?
|
//TODO: Different variant than the lazy loading in QueueFactory. What's better and why?
|
||||||
|
@ -1,158 +0,0 @@
|
|||||||
#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),
|
|
||||||
tmtcBridgeId(tmtcUnixUdpBridge) {
|
|
||||||
|
|
||||||
if(frameSize > 0) {
|
|
||||||
this->frameSize = frameSize;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->frameSize = DEFAULT_MAX_FRAME_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up reception buffer with specified frame size.
|
|
||||||
// For now, it is assumed that only one frame is held in the buffer!
|
|
||||||
receptionBuffer.reserve(this->frameSize);
|
|
||||||
receptionBuffer.resize(this->frameSize);
|
|
||||||
|
|
||||||
if(timeoutSeconds == -1) {
|
|
||||||
receptionTimeout = DEFAULT_TIMEOUT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
receptionTimeout = timevalOperations::toTimeval(timeoutSeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TcUnixUdpPollingTask::~TcUnixUdpPollingTask() {}
|
|
||||||
|
|
||||||
ReturnValue_t TcUnixUdpPollingTask::performOperation(uint8_t opCode) {
|
|
||||||
// Poll for new UDP datagrams in permanent loop.
|
|
||||||
while(1) {
|
|
||||||
//! Sender Address is cached here.
|
|
||||||
struct sockaddr_in senderAddress;
|
|
||||||
socklen_t senderSockLen = sizeof(senderAddress);
|
|
||||||
ssize_t bytesReceived = recvfrom(serverUdpSocket,
|
|
||||||
receptionBuffer.data(), frameSize, receptionFlags,
|
|
||||||
reinterpret_cast<sockaddr*>(&senderAddress), &senderSockLen);
|
|
||||||
if(bytesReceived < 0) {
|
|
||||||
// handle error
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSocketPollingTask::performOperation: Reception"
|
|
||||||
"error." << std::endl;
|
|
||||||
#endif
|
|
||||||
handleReadError();
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
// sif::debug << "TcSocketPollingTask::performOperation: " << bytesReceived
|
|
||||||
// << " bytes received" << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
|
|
||||||
if(result != HasReturnvaluesIF::RETURN_FAILED) {
|
|
||||||
|
|
||||||
}
|
|
||||||
tmtcBridge->registerCommConnect();
|
|
||||||
tmtcBridge->checkAndSetClientAddress(senderAddress);
|
|
||||||
}
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t TcUnixUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
|
||||||
store_address_t storeId;
|
|
||||||
ReturnValue_t result = tcStore->addData(&storeId,
|
|
||||||
receptionBuffer.data(), bytesRead);
|
|
||||||
// arrayprinter::print(receptionBuffer.data(), bytesRead);
|
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data "
|
|
||||||
"storage failed" << std::endl;
|
|
||||||
sif::error << "Packet size: " << bytesRead << std::endl;
|
|
||||||
#endif
|
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
TmTcMessage message(storeId);
|
|
||||||
|
|
||||||
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "Serial Polling: Sending message to queue failed"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
tcStore->deleteData(storeId);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t TcUnixUdpPollingTask::initialize() {
|
|
||||||
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
|
|
||||||
if (tcStore == nullptr) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmtcBridge = objectManager->get<TmTcUnixUdpBridge>(tmtcBridgeId);
|
|
||||||
if(tmtcBridge == nullptr) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid"
|
|
||||||
" TMTC bridge object!" << std::endl;
|
|
||||||
#endif
|
|
||||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
serverUdpSocket = tmtcBridge->serverSocket;
|
|
||||||
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t TcUnixUdpPollingTask::initializeAfterTaskCreation() {
|
|
||||||
// Initialize the destination after task creation. This ensures
|
|
||||||
// that the destination will be set in the TMTC bridge.
|
|
||||||
targetTcDestination = tmtcBridge->getRequestQueue();
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcUnixUdpPollingTask::setTimeout(double timeoutSeconds) {
|
|
||||||
timeval tval;
|
|
||||||
tval = timevalOperations::toTimeval(timeoutSeconds);
|
|
||||||
int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO,
|
|
||||||
&tval, sizeof(receptionTimeout));
|
|
||||||
if(result == -1) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Setting "
|
|
||||||
"receive timeout failed with " << strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: sleep after error detection to prevent spam
|
|
||||||
void TcUnixUdpPollingTask::handleReadError() {
|
|
||||||
switch(errno) {
|
|
||||||
case(EAGAIN): {
|
|
||||||
// todo: When working in timeout mode, this will occur more often
|
|
||||||
// and is not an error.
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcUnixUdpPollingTask::handleReadError: Timeout."
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcUnixUdpPollingTask::handleReadError: "
|
|
||||||
<< strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
#ifndef FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_
|
|
||||||
#define FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_
|
|
||||||
|
|
||||||
#include "../../objectmanager/SystemObject.h"
|
|
||||||
#include "../../osal/linux/TmTcUnixUdpBridge.h"
|
|
||||||
#include "../../tasks/ExecutableObjectIF.h"
|
|
||||||
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief This class can be used to implement the polling of a Unix socket,
|
|
||||||
* using UDP for now.
|
|
||||||
* @details
|
|
||||||
* The task will be blocked while the specified number of bytes has not been
|
|
||||||
* received, so TC reception is handled inside a separate task.
|
|
||||||
* This class caches the IP address of the sender. It is assumed there
|
|
||||||
* is only one sender for now.
|
|
||||||
*/
|
|
||||||
class TcUnixUdpPollingTask: public SystemObject,
|
|
||||||
public ExecutableObjectIF {
|
|
||||||
friend class TmTcUnixUdpBridge;
|
|
||||||
public:
|
|
||||||
static constexpr size_t DEFAULT_MAX_FRAME_SIZE = 2048;
|
|
||||||
//! 0.5 default milliseconds timeout for now.
|
|
||||||
static constexpr timeval DEFAULT_TIMEOUT = {.tv_sec = 0, .tv_usec = 500};
|
|
||||||
|
|
||||||
TcUnixUdpPollingTask(object_id_t objectId, object_id_t tmtcUnixUdpBridge,
|
|
||||||
size_t frameSize = 0, double timeoutSeconds = -1);
|
|
||||||
virtual~ TcUnixUdpPollingTask();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn on optional timeout for UDP polling. In the default mode,
|
|
||||||
* the receive function will block until a packet is received.
|
|
||||||
* @param timeoutSeconds
|
|
||||||
*/
|
|
||||||
void setTimeout(double timeoutSeconds);
|
|
||||||
|
|
||||||
virtual ReturnValue_t performOperation(uint8_t opCode) override;
|
|
||||||
virtual ReturnValue_t initialize() override;
|
|
||||||
virtual ReturnValue_t initializeAfterTaskCreation() override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
StorageManagerIF* tcStore = nullptr;
|
|
||||||
|
|
||||||
private:
|
|
||||||
//! TMTC bridge is cached.
|
|
||||||
object_id_t tmtcBridgeId = objects::NO_OBJECT;
|
|
||||||
TmTcUnixUdpBridge* tmtcBridge = nullptr;
|
|
||||||
MessageQueueId_t targetTcDestination = MessageQueueIF::NO_QUEUE;
|
|
||||||
//! Reception flags: https://linux.die.net/man/2/recvfrom.
|
|
||||||
int receptionFlags = 0;
|
|
||||||
|
|
||||||
//! Server socket, which is member of TMTC bridge and is assigned in
|
|
||||||
//! constructor
|
|
||||||
int serverUdpSocket = 0;
|
|
||||||
|
|
||||||
std::vector<uint8_t> receptionBuffer;
|
|
||||||
|
|
||||||
size_t frameSize = 0;
|
|
||||||
timeval receptionTimeout;
|
|
||||||
|
|
||||||
ReturnValue_t handleSuccessfullTcRead(size_t bytesRead);
|
|
||||||
void handleReadError();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* FRAMEWORK_OSAL_LINUX_TCSOCKETPOLLINGTASK_H_ */
|
|
@ -1,206 +0,0 @@
|
|||||||
#include "TmTcUnixUdpBridge.h"
|
|
||||||
#include "../../serviceinterface/ServiceInterface.h"
|
|
||||||
#include "../../ipc/MutexHelper.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
|
|
||||||
TmTcUnixUdpBridge::TmTcUnixUdpBridge(object_id_t objectId,
|
|
||||||
object_id_t tcDestination, object_id_t tmStoreId, object_id_t tcStoreId,
|
|
||||||
uint16_t serverPort, uint16_t clientPort):
|
|
||||||
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
|
|
||||||
mutex = MutexFactory::instance()->createMutex();
|
|
||||||
|
|
||||||
uint16_t setServerPort = DEFAULT_UDP_SERVER_PORT;
|
|
||||||
if(serverPort != 0xFFFF) {
|
|
||||||
setServerPort = serverPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t setClientPort = DEFAULT_UDP_CLIENT_PORT;
|
|
||||||
if(clientPort != 0xFFFF) {
|
|
||||||
setClientPort = clientPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up UDP socket: https://man7.org/linux/man-pages/man7/ip.7.html
|
|
||||||
//clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
if(serverSocket < 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not open"
|
|
||||||
" UDP socket!" << std::endl;
|
|
||||||
#endif
|
|
||||||
handleSocketError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
serverAddress.sin_family = AF_INET;
|
|
||||||
|
|
||||||
// Accept packets from any interface.
|
|
||||||
//serverAddress.sin_addr.s_addr = inet_addr("127.73.73.0");
|
|
||||||
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
serverAddress.sin_port = htons(setServerPort);
|
|
||||||
serverAddressLen = sizeof(serverAddress);
|
|
||||||
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &serverSocketOptions,
|
|
||||||
sizeof(serverSocketOptions));
|
|
||||||
|
|
||||||
clientAddress.sin_family = AF_INET;
|
|
||||||
clientAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
||||||
clientAddress.sin_port = htons(setClientPort);
|
|
||||||
clientAddressLen = sizeof(clientAddress);
|
|
||||||
|
|
||||||
int result = bind(serverSocket,
|
|
||||||
reinterpret_cast<struct sockaddr*>(&serverAddress),
|
|
||||||
serverAddressLen);
|
|
||||||
if(result == -1) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixUdpBridge::TmTcUnixUdpBridge: Could not bind "
|
|
||||||
"local port " << setServerPort << " to server socket!"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
handleBindError();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TmTcUnixUdpBridge::~TmTcUnixUdpBridge() {
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t TmTcUnixUdpBridge::sendTm(const uint8_t *data, size_t dataLen) {
|
|
||||||
int flags = 0;
|
|
||||||
|
|
||||||
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
|
|
||||||
|
|
||||||
if(ipAddrAnySet){
|
|
||||||
clientAddress.sin_addr.s_addr = htons(INADDR_ANY);
|
|
||||||
//clientAddress.sin_addr.s_addr = inet_addr("127.73.73.1");
|
|
||||||
clientAddressLen = sizeof(serverAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
// char ipAddress [15];
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
|
||||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ssize_t bytesSent = sendto(serverSocket, data, dataLen, flags,
|
|
||||||
reinterpret_cast<sockaddr*>(&clientAddress), clientAddressLen);
|
|
||||||
if(bytesSent < 0) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixUdpBridge::sendTm: Send operation failed."
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
handleSendError();
|
|
||||||
}
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
// sif::debug << "TmTcUnixUdpBridge::sendTm: " << bytesSent << " bytes were"
|
|
||||||
// " sent." << std::endl;
|
|
||||||
#endif
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmTcUnixUdpBridge::checkAndSetClientAddress(sockaddr_in& newAddress) {
|
|
||||||
MutexHelper lock(mutex, MutexIF::TimeoutType::WAITING, 10);
|
|
||||||
|
|
||||||
// char ipAddress [15];
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
// sif::debug << "IP Address Sender: "<< inet_ntop(AF_INET,
|
|
||||||
// &newAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
|
||||||
// sif::debug << "IP Address Old: " << inet_ntop(AF_INET,
|
|
||||||
// &clientAddress.sin_addr.s_addr, ipAddress, 15) << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set new IP address if it has changed.
|
|
||||||
if(clientAddress.sin_addr.s_addr != newAddress.sin_addr.s_addr) {
|
|
||||||
clientAddress.sin_addr.s_addr = newAddress.sin_addr.s_addr;
|
|
||||||
clientAddressLen = sizeof(clientAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void TmTcUnixUdpBridge::handleSocketError() {
|
|
||||||
// See: https://man7.org/linux/man-pages/man2/socket.2.html
|
|
||||||
switch(errno) {
|
|
||||||
case(EACCES):
|
|
||||||
case(EINVAL):
|
|
||||||
case(EMFILE):
|
|
||||||
case(ENFILE):
|
|
||||||
case(EAFNOSUPPORT):
|
|
||||||
case(ENOBUFS):
|
|
||||||
case(ENOMEM):
|
|
||||||
case(EPROTONOSUPPORT):
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixBridge::handleSocketError: Socket creation failed"
|
|
||||||
<< " with " << strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixBridge::handleSocketError: Unknown error"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmTcUnixUdpBridge::handleBindError() {
|
|
||||||
// See: https://man7.org/linux/man-pages/man2/bind.2.html
|
|
||||||
switch(errno) {
|
|
||||||
case(EACCES): {
|
|
||||||
/*
|
|
||||||
Ephermeral ports can be shown with following command:
|
|
||||||
sysctl -A | grep ip_local_port_range
|
|
||||||
*/
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixBridge::handleBindError: Port access issue."
|
|
||||||
"Ports 1-1024 are reserved on UNIX systems and require root "
|
|
||||||
"rights while ephermeral ports should not be used as well."
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case(EADDRINUSE):
|
|
||||||
case(EBADF):
|
|
||||||
case(EINVAL):
|
|
||||||
case(ENOTSOCK):
|
|
||||||
case(EADDRNOTAVAIL):
|
|
||||||
case(EFAULT):
|
|
||||||
case(ELOOP):
|
|
||||||
case(ENAMETOOLONG):
|
|
||||||
case(ENOENT):
|
|
||||||
case(ENOMEM):
|
|
||||||
case(ENOTDIR):
|
|
||||||
case(EROFS): {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixBridge::handleBindError: Socket creation failed"
|
|
||||||
<< " with " << strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TmTcUnixBridge::handleBindError: Unknown error"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmTcUnixUdpBridge::handleSendError() {
|
|
||||||
switch(errno) {
|
|
||||||
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){
|
|
||||||
this->ipAddrAnySet = ipAddrAnySet;
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
|||||||
#ifndef FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_
|
|
||||||
#define FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_
|
|
||||||
|
|
||||||
#include "../../tmtcservices/AcceptsTelecommandsIF.h"
|
|
||||||
#include "../../tmtcservices/TmTcBridge.h"
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netinet/udp.h>
|
|
||||||
|
|
||||||
class TmTcUnixUdpBridge: public TmTcBridge {
|
|
||||||
friend class TcUnixUdpPollingTask;
|
|
||||||
public:
|
|
||||||
// The ports chosen here should not be used by any other process.
|
|
||||||
// List of used ports on Linux: /etc/services
|
|
||||||
static constexpr uint16_t DEFAULT_UDP_SERVER_PORT = 7301;
|
|
||||||
static constexpr uint16_t DEFAULT_UDP_CLIENT_PORT = 7302;
|
|
||||||
|
|
||||||
TmTcUnixUdpBridge(object_id_t objectId, object_id_t tcDestination,
|
|
||||||
object_id_t tmStoreId, object_id_t tcStoreId,
|
|
||||||
uint16_t serverPort = 0xFFFF,uint16_t clientPort = 0xFFFF);
|
|
||||||
virtual~ TmTcUnixUdpBridge();
|
|
||||||
|
|
||||||
void checkAndSetClientAddress(sockaddr_in& clientAddress);
|
|
||||||
|
|
||||||
void setClientAddressToAny(bool ipAddrAnySet);
|
|
||||||
protected:
|
|
||||||
virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int serverSocket = 0;
|
|
||||||
|
|
||||||
const int serverSocketOptions = 0;
|
|
||||||
|
|
||||||
struct sockaddr_in clientAddress;
|
|
||||||
socklen_t clientAddressLen = 0;
|
|
||||||
|
|
||||||
struct sockaddr_in serverAddress;
|
|
||||||
socklen_t serverAddressLen = 0;
|
|
||||||
|
|
||||||
bool ipAddrAnySet = false;
|
|
||||||
|
|
||||||
//! Access to the client address is mutex protected as it is set
|
|
||||||
//! by another task.
|
|
||||||
MutexIF* mutex;
|
|
||||||
|
|
||||||
void handleSocketError();
|
|
||||||
void handleBindError();
|
|
||||||
void handleSendError();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* FRAMEWORK_OSAL_LINUX_TMTCUNIXUDPBRIDGE_H_ */
|
|
109
osal/linux/tcpipHelpers.cpp
Normal file
109
osal/linux/tcpipHelpers.cpp
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
#include "../common/tcpipHelpers.h"
|
||||||
|
|
||||||
|
#include "../../serviceinterface/ServiceInterface.h"
|
||||||
|
#include "../../tasks/TaskFactory.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void tcpip::handleError(Protocol protocol, ErrorSources errorSrc, dur_millis_t sleepDuration) {
|
||||||
|
int errCode = errno;
|
||||||
|
std::string protocolString;
|
||||||
|
std::string errorSrcString;
|
||||||
|
determineErrorStrings(protocol, errorSrc, protocolString, errorSrcString);
|
||||||
|
std::string infoString;
|
||||||
|
switch(errCode) {
|
||||||
|
case(EACCES): {
|
||||||
|
infoString = "EACCES";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EINVAL): {
|
||||||
|
infoString = "EINVAL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EAGAIN): {
|
||||||
|
infoString = "EAGAIN";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EMFILE): {
|
||||||
|
infoString = "EMFILE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENFILE): {
|
||||||
|
infoString = "ENFILE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EAFNOSUPPORT): {
|
||||||
|
infoString = "EAFNOSUPPORT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENOBUFS): {
|
||||||
|
infoString = "ENOBUFS";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENOMEM): {
|
||||||
|
infoString = "ENOMEM";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EPROTONOSUPPORT): {
|
||||||
|
infoString = "EPROTONOSUPPORT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EADDRINUSE): {
|
||||||
|
infoString = "EADDRINUSE";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EBADF): {
|
||||||
|
infoString = "EBADF";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENOTSOCK): {
|
||||||
|
infoString = "ENOTSOCK";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EADDRNOTAVAIL): {
|
||||||
|
infoString = "EADDRNOTAVAIL";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EFAULT): {
|
||||||
|
infoString = "EFAULT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ELOOP): {
|
||||||
|
infoString = "ELOOP";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENAMETOOLONG): {
|
||||||
|
infoString = "ENAMETOOLONG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENOENT): {
|
||||||
|
infoString = "ENOENT";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(ENOTDIR): {
|
||||||
|
infoString = "ENOTDIR";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case(EROFS): {
|
||||||
|
infoString = "EROFS";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
infoString = "Error code: " + std::to_string(errCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
||||||
|
sif::warning << "tcpip::handleError: " << protocolString << " | " << errorSrcString <<
|
||||||
|
" | " << infoString << std::endl;
|
||||||
|
#else
|
||||||
|
sif::printWarning("tcpip::handleError: %s | %s | %s\n", protocolString.c_str(),
|
||||||
|
errorSrcString.c_str(), infoString.c_str());
|
||||||
|
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
|
||||||
|
|
||||||
|
if(sleepDuration > 0) {
|
||||||
|
TaskFactory::instance()->delayTask(sleepDuration);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
#include "RtemsBasic.h"
|
#include "RtemsBasic.h"
|
||||||
|
|
||||||
#include "../../timemanager/Clock.h"
|
#include "../../timemanager/Clock.h"
|
||||||
#include "../../ipc/MutexHelper.h"
|
#include "../../ipc/MutexGuard.h"
|
||||||
|
|
||||||
#include <rtems/score/todimpl.h>
|
#include <rtems/score/todimpl.h>
|
||||||
#include <rtems/rtems/clockimpl.h>
|
#include <rtems/rtems/clockimpl.h>
|
||||||
@ -183,7 +183,7 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
|
|||||||
if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){
|
if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
MutexHelper helper(timeMutex);
|
MutexGuard helper(timeMutex);
|
||||||
|
|
||||||
|
|
||||||
leapSeconds = leapSeconds_;
|
leapSeconds = leapSeconds_;
|
||||||
@ -196,7 +196,7 @@ ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
|
|||||||
if(timeMutex==nullptr){
|
if(timeMutex==nullptr){
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
return HasReturnvaluesIF::RETURN_FAILED;
|
||||||
}
|
}
|
||||||
MutexHelper helper(timeMutex);
|
MutexGuard helper(timeMutex);
|
||||||
|
|
||||||
*leapSeconds_ = leapSeconds;
|
*leapSeconds_ = leapSeconds;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
|
|||||||
} else {
|
} else {
|
||||||
//No message was received. Keep lastPartner anyway, I might send something later.
|
//No message was received. Keep lastPartner anyway, I might send something later.
|
||||||
//But still, delete packet content.
|
//But still, delete packet content.
|
||||||
memset(message->getData(), 0, message->getMaximumMessageSize());
|
memset(message->getData(), 0, message->getMaximumDataSize());
|
||||||
}
|
}
|
||||||
return convertReturnCode(status);
|
return convertReturnCode(status);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,4 @@
|
|||||||
target_sources(${LIB_FSFW_NAME}
|
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||||
PRIVATE
|
tcpipHelpers.cpp
|
||||||
TcWinUdpPollingTask.cpp
|
winTaskHelpers.cpp
|
||||||
TmTcWinUdpBridge.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${LIB_FSFW_NAME}
|
|
||||||
PRIVATE
|
|
||||||
wsock32
|
|
||||||
ws2_32
|
|
||||||
)
|
|
@ -1,177 +0,0 @@
|
|||||||
#include "TcWinUdpPollingTask.h"
|
|
||||||
#include "../../globalfunctions/arrayprinter.h"
|
|
||||||
#include "../../serviceinterface/ServiceInterfaceStream.h"
|
|
||||||
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
TcWinUdpPollingTask::TcWinUdpPollingTask(object_id_t objectId,
|
|
||||||
object_id_t tmtcUnixUdpBridge, size_t frameSize,
|
|
||||||
double timeoutSeconds): SystemObject(objectId),
|
|
||||||
tmtcBridgeId(tmtcUnixUdpBridge) {
|
|
||||||
if(frameSize > 0) {
|
|
||||||
this->frameSize = frameSize;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this->frameSize = DEFAULT_MAX_FRAME_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up reception buffer with specified frame size.
|
|
||||||
// For now, it is assumed that only one frame is held in the buffer!
|
|
||||||
receptionBuffer.reserve(this->frameSize);
|
|
||||||
receptionBuffer.resize(this->frameSize);
|
|
||||||
|
|
||||||
if(timeoutSeconds == -1) {
|
|
||||||
receptionTimeout = DEFAULT_TIMEOUT;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
receptionTimeout = timevalOperations::toTimeval(timeoutSeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TcWinUdpPollingTask::~TcWinUdpPollingTask() {}
|
|
||||||
|
|
||||||
ReturnValue_t TcWinUdpPollingTask::performOperation(uint8_t opCode) {
|
|
||||||
// Poll for new UDP datagrams in permanent loop.
|
|
||||||
while(true) {
|
|
||||||
//! Sender Address is cached here.
|
|
||||||
struct sockaddr_in senderAddress;
|
|
||||||
int senderAddressSize = sizeof(senderAddress);
|
|
||||||
ssize_t bytesReceived = recvfrom(serverUdpSocket,
|
|
||||||
reinterpret_cast<char*>(receptionBuffer.data()), frameSize,
|
|
||||||
receptionFlags, reinterpret_cast<sockaddr*>(&senderAddress),
|
|
||||||
&senderAddressSize);
|
|
||||||
if(bytesReceived == SOCKET_ERROR) {
|
|
||||||
// handle error
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcWinUdpPollingTask::performOperation: Reception"
|
|
||||||
" error." << std::endl;
|
|
||||||
#endif
|
|
||||||
handleReadError();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
//sif::debug << "TcWinUdpPollingTask::performOperation: " << bytesReceived
|
|
||||||
// << " bytes received" << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ReturnValue_t result = handleSuccessfullTcRead(bytesReceived);
|
|
||||||
if(result != HasReturnvaluesIF::RETURN_FAILED) {
|
|
||||||
|
|
||||||
}
|
|
||||||
tmtcBridge->registerCommConnect();
|
|
||||||
tmtcBridge->checkAndSetClientAddress(senderAddress);
|
|
||||||
}
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ReturnValue_t TcWinUdpPollingTask::handleSuccessfullTcRead(size_t bytesRead) {
|
|
||||||
store_address_t storeId;
|
|
||||||
ReturnValue_t result = tcStore->addData(&storeId,
|
|
||||||
receptionBuffer.data(), bytesRead);
|
|
||||||
// arrayprinter::print(receptionBuffer.data(), bytesRead);
|
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSerialPollingTask::transferPusToSoftwareBus: Data "
|
|
||||||
"storage failed" << std::endl;
|
|
||||||
sif::error << "Packet size: " << bytesRead << std::endl;
|
|
||||||
#endif
|
|
||||||
return HasReturnvaluesIF::RETURN_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
TmTcMessage message(storeId);
|
|
||||||
|
|
||||||
result = MessageQueueSenderIF::sendMessage(targetTcDestination, &message);
|
|
||||||
if (result != HasReturnvaluesIF::RETURN_OK) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "Serial Polling: Sending message to queue failed"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
tcStore->deleteData(storeId);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t TcWinUdpPollingTask::initialize() {
|
|
||||||
tcStore = objectManager->get<StorageManagerIF>(objects::TC_STORE);
|
|
||||||
if (tcStore == nullptr) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSerialPollingTask::initialize: TC Store uninitialized!"
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmtcBridge = objectManager->get<TmTcWinUdpBridge>(tmtcBridgeId);
|
|
||||||
if(tmtcBridge == nullptr) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcSocketPollingTask::TcSocketPollingTask: Invalid"
|
|
||||||
" TMTC bridge object!" << std::endl;
|
|
||||||
#endif
|
|
||||||
return ObjectManagerIF::CHILD_INIT_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
serverUdpSocket = tmtcBridge->serverSocket;
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
//sif::info << "TcWinUdpPollingTask::initialize: Server UDP socket "
|
|
||||||
// << serverUdpSocket << std::endl;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnValue_t TcWinUdpPollingTask::initializeAfterTaskCreation() {
|
|
||||||
// Initialize the destination after task creation. This ensures
|
|
||||||
// that the destination has already been set in the TMTC bridge.
|
|
||||||
targetTcDestination = tmtcBridge->getRequestQueue();
|
|
||||||
return HasReturnvaluesIF::RETURN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcWinUdpPollingTask::setTimeout(double timeoutSeconds) {
|
|
||||||
DWORD timeoutMs = timeoutSeconds * 1000.0;
|
|
||||||
int result = setsockopt(serverUdpSocket, SOL_SOCKET, SO_RCVTIMEO,
|
|
||||||
reinterpret_cast<const char*>(&timeoutMs), sizeof(DWORD));
|
|
||||||
if(result == -1) {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::error << "TcWinUdpPollingTask::TcSocketPollingTask: Setting "
|
|
||||||
"receive timeout failed with " << strerror(errno) << std::endl;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TcWinUdpPollingTask::handleReadError() {
|
|
||||||
int error = WSAGetLastError();
|
|
||||||
switch(error) {
|
|
||||||
case(WSANOTINITIALISED): {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSANOTINITIALISED: "
|
|
||||||
<< "WSAStartup(...) call " << "necessary" << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case(WSAEFAULT): {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSADEFAULT: "
|
|
||||||
<< "Bad address " << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case(WSAEINVAL): {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::info << "TcWinUdpPollingTask::handleReadError: WSAEINVAL: "
|
|
||||||
<< "Invalid input parameters. " << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
#if FSFW_CPP_OSTREAM_ENABLED == 1
|
|
||||||
sif::info << "TcWinUdpPollingTask::handleReadError: Error code: "
|
|
||||||
<< error << std::endl;
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// to prevent spam.
|
|
||||||
Sleep(1000);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user