Merge branch 'mueller/master' into eive/develop

This commit is contained in:
Robin Müller 2021-05-18 16:18:47 +02:00
commit e9adaf672f
169 changed files with 5118 additions and 3079 deletions

View File

@ -1,5 +1,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)
# Options to exclude parts of the FSFW from compilation.
option(FSFW_USE_RMAP "Compile with RMAP" ON)
@ -26,15 +34,22 @@ if(NOT OS_FSFW)
endif()
set(FSFW_OSAL_DEFINITION FSFW_HOST)
if(${OS_FSFW} STREQUAL host)
set(OS_FSFW_NAME "Host")
elseif(${OS_FSFW} STREQUAL linux)
set(OS_FSFW_NAME "Linux")
set(FSFW_OSAL_DEFINITION FSFW_LINUX)
elseif(${OS_FSFW} STREQUAL 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)
set(OS_FSFW_NAME "RTEMS")
set(FSFW_OSAL_DEFINITION FSFW_RTEMS)
else()
message(WARNING
"Invalid operating system for FSFW specified! Setting to host.."
@ -43,6 +58,14 @@ else()
set(OS_FSFW "host")
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.")
add_subdirectory(action)
@ -88,6 +111,7 @@ add_subdirectory(timemanager)
add_subdirectory(tmstorage)
add_subdirectory(tmtcpacket)
add_subdirectory(tmtcservices)
add_subdirectory(unittest)
# 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.
@ -107,6 +131,21 @@ else()
)
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(NOT DEFINED FSFW_WARNING_FLAGS)
set(FSFW_WARNING_FLAGS
@ -117,7 +156,20 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-Wno-psabi
)
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)
list(APPEND WARNING_FLAGS "-Wshadow=local")
endif()
@ -132,6 +184,7 @@ endif()
target_include_directories(${LIB_FSFW_NAME} INTERFACE
${CMAKE_SOURCE_DIR}
${FSFW_CONFIG_PATH_ABSOLUTE}
${FSFW_ADD_INC_PATHS_ABS}
)
# 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
${CMAKE_SOURCE_DIR}
${FSFW_CONFIG_PATH_ABSOLUTE}
${FSFW_ADD_INC_PATHS_ABS}
)
target_compile_options(${LIB_FSFW_NAME} PRIVATE
${FSFW_WARNING_FLAGS}
${COMPILER_FLAGS}
)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${FSFW_ADDITIONAL_LINK_LIBS}
)

View File

@ -3,9 +3,9 @@
const char* const FSFW_VERSION_NAME = "ASTP";
#define FSFW_VERSION 0
#define FSFW_SUBVERSION 0
#define FSFW_REVISION 1
#define FSFW_VERSION 1
#define FSFW_SUBVERSION 0
#define FSFW_REVISION 0

View File

@ -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>
[2. Core components](doc/README-core.md#top) <br>
[3. OSAL overview](doc/README-osal.md#top) <br>
[4. PUS services](doc/README-pus.md#top) <br>
[5. Device Handler overview](doc/README-devicehandlers.md#top) <br>
[6. Controller overview](doc/README-controllers.md#top) <br>
[7. Local Data Pools](doc/README-localpools.md#top) <br>
[3. Configuration](doc/README-config.md#top) <br>
[4. OSAL overview](doc/README-osal.md#top) <br>
[5. PUS services](doc/README-pus.md#top) <br>
[6. Device Handler overview](doc/README-devicehandlers.md#top) <br>
[7. Controller overview](doc/README-controllers.md#top) <br>
[8. Local Data Pools](doc/README-localpools.md#top) <br>

View File

@ -147,11 +147,6 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo,
return result;
}
if (result != HasReturnvaluesIF::RETURN_OK) {
ipcStore->deleteData(storeAddress);
return result;
}
/* We don't need to report the objectId, as we receive REQUESTED data before the completion
success message. True aperiodic replies need to be reported with another dedicated message. */
ActionMessage::setDataReply(&reply, replyId, storeAddress);

View File

@ -8,7 +8,9 @@
*/
template<typename T, size_t MAX_SIZE, typename count_t = uint8_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:
T data[MAX_SIZE];
public:

View File

@ -1,6 +1,6 @@
#include "SharedRingBuffer.h"
#include "../ipc/MutexFactory.h"
#include "../ipc/MutexHelper.h"
#include "../ipc/MutexGuard.h"
SharedRingBuffer::SharedRingBuffer(object_id_t objectId, const size_t size,
bool overwriteOld, size_t maxExcessBytes):
@ -17,6 +17,9 @@ SharedRingBuffer::SharedRingBuffer(object_id_t objectId, uint8_t *buffer,
mutex = MutexFactory::instance()->createMutex();
}
SharedRingBuffer::~SharedRingBuffer() {
MutexFactory::instance()->deleteMutex(mutex);
}
void SharedRingBuffer::setToUseReceiveSizeFIFO(size_t fifoDepth) {
this->fifoDepth = fifoDepth;

View File

@ -26,6 +26,18 @@ public:
*/
SharedRingBuffer(object_id_t objectId, const size_t size,
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
@ -37,16 +49,7 @@ public:
*/
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
@ -66,7 +69,7 @@ public:
/**
* 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
*/
MutexIF* getMutexHandle() const;

View File

@ -17,14 +17,6 @@ ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId,
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 {
return SystemObject::getObjectId();
}
@ -107,14 +99,6 @@ MessageQueueId_t ExtendedControllerBase::getCommandQueue() const {
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() {
return &poolManager;
}

View File

@ -61,11 +61,11 @@ protected:
/* HasLocalDatapoolIF overrides */
virtual LocalDataPoolManager* getHkManagerHandle() 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 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;
};

View File

@ -19,7 +19,8 @@ class VirtualChannelReception;
class DataLinkLayer : public CCSDSReturnValuesIF {
public:
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 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

View File

@ -7,7 +7,7 @@ HkSwitchHelper::HkSwitchHelper(EventReportingProxyIF* eventProxy) :
}
HkSwitchHelper::~HkSwitchHelper() {
// TODO Auto-generated destructor stub
QueueFactory::instance()->deleteMessageQueue(actionQueue);
}
ReturnValue_t HkSwitchHelper::initialize() {

View File

@ -8,13 +8,16 @@
PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray,
const size_t maxFillCount):
registeredVariables(registeredVariablesArray),
maxFillCount(maxFillCount) {
}
maxFillCount(maxFillCount) {}
PoolDataSetBase::~PoolDataSetBase() {}
ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) {
if(registeredVariables == nullptr) {
/* Underlying container invalid */
return HasReturnvaluesIF::RETURN_FAILED;
}
if (state != States::STATE_SET_UNINITIALISED) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl;
@ -61,11 +64,11 @@ ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType,
}
else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "DataSet::read(): Call made in wrong position. Don't forget to commit"
" member datasets!" << std::endl;
sif::warning << "PoolDataSetBase::read: Call made in wrong position. Don't forget to "
"commit member datasets!" << std::endl;
#else
sif::printError("DataSet::read(): Call made in wrong position. Don't forget to commit"
" member datasets!\n");
sif::printWarning("PoolDataSetBase::read: Call made in wrong position. Don't forget to "
"commit member datasets!\n");
#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */
result = SET_WAS_ALREADY_READ;
}

View File

@ -9,8 +9,8 @@
* and unlock a data pool and read/commit semantics.
*/
class PoolDataSetIF:
public DataSetIF,
public ReadCommitIF {
virtual public DataSetIF,
virtual public ReadCommitIF {
public:
virtual~ PoolDataSetIF() {};

View File

@ -7,7 +7,7 @@
template <typename T>
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];
if(initValue.size() == 0) {
std::memset(this->address, 0, this->getByteSize());

View File

@ -1,13 +1,15 @@
#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_
#include "PoolDataSetIF.h"
class SharedDataSetIF: public PoolDataSetIF {
class SharedDataSetIF {
public:
virtual ~SharedDataSetIF() {};
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;
};

View File

@ -65,34 +65,45 @@ public:
* usually be the period the pool owner performs its periodic operation.
* @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
* notification is received.
* @details HasLocalDataPoolIF
* Can be overriden by the child class to handle changed datasets.
* @param sid
* @param storeId If a snapshot was requested, data will be located inside
* @param sid SID of the updated set
* @param storeId If a snapshot was requested, data will be located inside
* 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,
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
return;
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
bool* clearMessage = nullptr) {
if(clearMessage != nullptr) {
*clearMessage = true;
}
}
/**
* @brief This function will be called by the manager if an update
* notification is received.
* @details
* Can be overriden by the child class to handle changed pool IDs.
* @param sid
* @param storeId If a snapshot was requested, data will be located inside
* Can be overriden by the child class to handle changed pool variables.
* @param gpid GPID of the updated variable.
* @param storeId If a snapshot was requested, data will be located inside
* 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,
store_address_t storeId = storeId::INVALID_STORE_ADDRESS) {
return;
virtual void handleChangedPoolVariable(gp_id_t gpid,
store_address_t storeId = storeId::INVALID_STORE_ADDRESS,
bool* clearMessage = nullptr) {
if(clearMessage != nullptr) {
*clearMessage = true;
}
}
/**

View File

@ -10,7 +10,7 @@
#include "../housekeeping/AcceptsHkPacketsIF.h"
#include "../timemanager/CCSDSTime.h"
#include "../ipc/MutexFactory.h"
#include "../ipc/MutexHelper.h"
#include "../ipc/MutexGuard.h"
#include "../ipc/QueueFactory.h"
#include <array>
@ -38,7 +38,11 @@ LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQue
hkQueue = queueToUse;
}
LocalDataPoolManager::~LocalDataPoolManager() {}
LocalDataPoolManager::~LocalDataPoolManager() {
if(mutex != nullptr) {
MutexFactory::instance()->deleteMutex(mutex);
}
}
ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) {
if(queueToUse == nullptr) {
@ -132,13 +136,16 @@ ReturnValue_t LocalDataPoolManager::performHkOperation() {
ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver,
ReturnValue_t& status) {
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;
}
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner,
receiver.dataId.sid);
if(dataSet == nullptr) {
return DATASET_NOT_FOUND;
}
if(dataSet->hasChanged()) {
// prepare and send update notification
/* Prepare and send update notification */
ReturnValue_t result = generateHousekeepingPacket(
receiver.dataId.sid, dataSet, true);
if(result != HasReturnvaluesIF::RETURN_OK) {
@ -328,7 +335,7 @@ void LocalDataPoolManager::handleChangeResetLogic(
toReset->setChanged(false);
}
/* All recipients have been notified, reset the changed flag */
if(changeInfo.currentUpdateCounter <= 1) {
else if(changeInfo.currentUpdateCounter <= 1) {
toReset->setChanged(false);
changeInfo.currentUpdateCounter = 0;
}
@ -372,7 +379,7 @@ ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid,
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting);
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval,
owner->getPeriodicOperationFrequency(), isDiagnostics);
owner->getPeriodicOperationFrequency());
}
hkReceivers.push_back(hkReceiver);
@ -398,7 +405,6 @@ ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid,
hkReceiver.destinationQueue = hkReceiverObject->getHkQueue();
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
if(dataSet != nullptr) {
LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true);
LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics);
@ -516,11 +522,19 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
}
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): {
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_PARAMETER_REPORT_COLLECTION_INTERVAL): {
@ -540,14 +554,15 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT):
case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): {
LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT
and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
return WRONG_HK_PACKET_TYPE;
result = WRONG_HK_PACKET_TYPE;
break;
}
else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT
and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
return WRONG_HK_PACKET_TYPE;
result = WRONG_HK_PACKET_TYPE;
break;
}
return generateHousekeepingPacket(HousekeepingMessage::getSid(message),
dataSet, true);
@ -566,14 +581,22 @@ ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage(
case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): {
store_address_t 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;
}
case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): {
store_address_t storeId;
gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message,
&storeId);
owner->handleChangedPoolVariable(globPoolId, storeId);
bool clearMessage = true;
owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage);
if(clearMessage) {
message->clear();
}
return HasReturnvaluesIF::RETURN_OK;
}
@ -616,7 +639,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
LocalPoolDataSetBase* dataSet, bool forDownlink,
MessageQueueId_t destination) {
if(dataSet == nullptr) {
// Configuration error.
/* Configuration error. */
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"generateHousekeepingPacket",
DATASET_NOT_FOUND);
@ -632,7 +655,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
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;
if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) {
HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId);
@ -642,7 +665,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
}
if(hkQueue == nullptr) {
// error, no queue available to send packet with.
/* Error, no queue available to send packet with. */
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"generateHousekeepingPacket",
QUEUE_OR_DESTINATION_INVALID);
@ -650,7 +673,7 @@ ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid,
}
if(destination == MessageQueueIF::NO_QUEUE) {
if(hkDestinationId == MessageQueueIF::NO_QUEUE) {
// error, all destinations invalid
/* Error, all destinations invalid */
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"generateHousekeepingPacket",
QUEUE_OR_DESTINATION_INVALID);
@ -729,6 +752,12 @@ void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) {
ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
bool enable, bool isDiagnostics) {
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
(not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) {
return WRONG_HK_PACKET_TYPE;
@ -746,6 +775,12 @@ ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid,
ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
float newCollectionInterval, bool isDiagnostics) {
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);
if((targetIsDiagnostics and not isDiagnostics) or
(not targetIsDiagnostics and isDiagnostics)) {
@ -756,7 +791,7 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet);
if(periodicHelper == nullptr) {
// config error
/* Configuration error, set might not have a corresponding pool manager */
return PERIODIC_HELPER_INVALID;
}
@ -766,13 +801,11 @@ ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid,
ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
bool isDiagnostics) {
// Get and check dataset first.
//LocalPoolDataSetBase* dataSet = owner->getDataSetHandle(sid);
/* Get and check dataset first. */
LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid);
if(dataSet == nullptr) {
printWarningOrError(sif::OutputTypes::OUT_WARNING,
"performPeriodicHkGeneration",
DATASET_NOT_FOUND);
"performPeriodicHkGeneration", DATASET_NOT_FOUND);
return DATASET_NOT_FOUND;
}
@ -831,6 +864,10 @@ ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid,
void LocalDataPoolManager::clearReceiversList() {
/* Clear the vector completely and releases allocated memory. */
HkReceivers().swap(hkReceivers);
/* Also clear the reset helper if it exists */
if(hkUpdateResetList != nullptr) {
HkUpdateResetList().swap(*hkUpdateResetList);
}
}
MutexIF* LocalDataPoolManager::getLocalPoolMutex() {
@ -843,6 +880,7 @@ object_id_t LocalDataPoolManager::getCreatorObjectId() const {
void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
const char* functionName, ReturnValue_t error, const char* errorPrint) {
#if FSFW_VERBOSE_LEVEL >= 1
if(errorPrint == nullptr) {
if(error == DATASET_NOT_FOUND) {
errorPrint = "Dataset not found";
@ -871,33 +909,32 @@ void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType,
errorPrint = "Unknown error";
}
}
object_id_t objectId = 0xffffffff;
if(owner != nullptr) {
objectId = owner->getObjectId();
}
if(outputType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "LocalDataPoolManager::" << functionName
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
<< std::hex << owner->getObjectId() << " | " << errorPrint
<< std::dec << std::setfill(' ') << std::endl;
sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" <<
std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint <<
std::dec << std::setfill(' ') << std::endl;
#else
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_VERBOSE_LEVEL >= 1 */
}
else if(outputType == sif::OutputTypes::OUT_ERROR) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalDataPoolManager::" << functionName
<< ": Object ID 0x" << std::setw(8) << std::setfill('0')
<< std::hex << owner->getObjectId() << " | " << errorPrint
<< std::dec << std::setfill(' ') << std::endl;
sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" <<
std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint <<
std::dec << std::setfill(' ') << std::endl;
#else
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_VERBOSE_LEVEL >= 1 */
}
#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */
}
LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() {

View File

@ -14,7 +14,7 @@
#include "../ipc/MutexIF.h"
#include "../ipc/CommandMessage.h"
#include "../ipc/MessageQueueIF.h"
#include "../ipc/MutexHelper.h"
#include "../ipc/MutexGuard.h"
#include <map>
#include <vector>
@ -391,6 +391,10 @@ protected:
template<class T> inline
ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry<T> **poolEntry) {
if(poolEntry == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
auto poolIter = localPoolMap.find(localPoolId);
if (poolIter == localPoolMap.end()) {
printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry",

View File

@ -44,7 +44,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner,
LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray,
const size_t maxNumberOfVariables):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
HasLocalDataPoolIF* hkOwner = objectManager->get<HasLocalDataPoolIF>(
sid.objectId);
if(hkOwner != nullptr) {
@ -58,8 +58,7 @@ LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registere
this->sid = sid;
}
LocalPoolDataSetBase::LocalPoolDataSetBase(
PoolVariableIF **registeredVariablesArray,
LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray,
const size_t maxNumberOfVariables, bool protectEveryReadCommitCall):
PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) {
this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall);
@ -95,14 +94,23 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
size_t *size, size_t maxSize,
SerializeIF::Endianness streamEndianness) const {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
uint8_t validityMask[validityMaskSize] = {};
const uint8_t validityMaskSize = std::ceil(static_cast<float>(fillCount)/8.0);
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 validBufferIndexBit = 0;
for (uint16_t count = 0; count < fillCount; count++) {
if(registeredVariables[count]->isValid()) {
/* Set bit at correct position */
bitutil::bitSet(validityMask + validBufferIndex, validBufferIndexBit);
bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit);
}
if(validBufferIndexBit == 7) {
validBufferIndex ++;
@ -123,7 +131,7 @@ ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer
return SerializeIF::BUFFER_TOO_SHORT;
}
// copy validity buffer to end
std::memcpy(*buffer, validityMask, validityMaskSize);
std::memcpy(*buffer, validityPtr, validityMaskSize);
*size += validityMaskSize;
return result;
}
@ -262,11 +270,9 @@ bool LocalPoolDataSetBase::getReportingEnabled() const {
return reportingEnabled;
}
void LocalPoolDataSetBase::initializePeriodicHelper(
float collectionInterval, dur_millis_t minimumPeriodicInterval,
bool isDiagnostics, uint8_t nonDiagIntervalFactor) {
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval,
isDiagnostics, nonDiagIntervalFactor);
void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval,
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, nonDiagIntervalFactor);
}
void LocalPoolDataSetBase::setChanged(bool changed) {
@ -306,3 +312,12 @@ void LocalPoolDataSetBase::setAllVariablesReadOnly() {
registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ);
}
}
float LocalPoolDataSetBase::getCollectionInterval() const {
if(periodicHelper != nullptr) {
return periodicHelper->getCollectionIntervalInSeconds();
}
else {
return 0.0;
}
}

View File

@ -166,6 +166,16 @@ public:
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:
sid_t sid;
//! This mutex is used if the data is created by one object only.
@ -180,11 +190,9 @@ protected:
*/
bool reportingEnabled = false;
void setReportingEnabled(bool enabled);
bool getReportingEnabled() const;
void initializePeriodicHelper(float collectionInterval,
dur_millis_t minimumPeriodicInterval,
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5);
void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval,
uint8_t nonDiagIntervalFactor = 5);
/**
* If the valid state of a dataset is always relevant to the whole

View File

@ -25,7 +25,7 @@ inline LocalPoolVector<T, vectorSize>::LocalPoolVector(gp_id_t globalPoolId,
template<typename T, uint16_t vectorSize>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::read(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return readWithoutLock();
}
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>
inline ReturnValue_t LocalPoolVector<T, vectorSize>::commit(
MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) {
MutexHelper(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs);
return commitWithoutLock();
}

View File

@ -1,16 +1,37 @@
#include "SharedLocalDataSet.h"
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid,
const size_t maxSize): SystemObject(objectId),
LocalPoolDataSetBase(sid, nullptr, maxSize) {
LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) {
this->setContainer(poolVarVector.data());
datasetLock = MutexFactory::instance()->createMutex();
}
ReturnValue_t SharedLocalDataSet::lockDataset(dur_millis_t mutexTimeout) {
return datasetLock->lockMutex(MutexIF::TimeoutType::WAITING, mutexTimeout);
SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId,
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() {
return datasetLock->unlockMutex();
if(datasetLock != nullptr) {
return datasetLock->unlockMutex();
}
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -11,16 +11,22 @@
* multiple threads. It provides a lock in addition to all other functionalities provided
* by the LocalPoolDataSetBase class.
*
* TODO: override and protect read, commit and some other calls used by pool manager.
* The user is completely responsible for lockingand unlocking the dataset when using the
* shared dataset.
*/
class SharedLocalDataSet:
public SystemObject,
public LocalPoolDataSetBase,
public SharedDataSetIF {
public:
SharedLocalDataSet(object_id_t objectId, sid_t sid,
SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId,
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;
private:

View 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_ */

View File

@ -14,9 +14,8 @@ private:
}
static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval,
uint32_t minimumPeriodicIntervalMs,
bool isDiagnostics, uint8_t nonDiagIntervalFactor = 5) {
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, isDiagnostics,
uint32_t minimumPeriodicIntervalMs, uint8_t nonDiagIntervalFactor = 5) {
set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs,
nonDiagIntervalFactor);
}

View File

@ -96,11 +96,11 @@ union gp_id_t {
return raw == INVALID_GPID;
}
bool operator==(const sid_t& other) const {
bool operator==(const gp_id_t& other) const {
return raw == other.raw;
}
bool operator!=(const sid_t& other) const {
bool operator!=(const gp_id_t& other) const {
return not (raw == other.raw);
}
};

View File

@ -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
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
)
if(NOT FSFW_CONFIG_PATH)
set(FSFW_CONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR})
target_sources(${TARGET_NAME} PRIVATE
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()
# 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()

View File

@ -17,6 +17,8 @@
#define FSFW_DISABLE_PRINTOUT 0
#endif
#define FSFW_USE_PUS_C_TELEMETRY 1
//! Can be used to disable the ANSI color sequences for C stdio.
#define FSFW_COLORED_OUTPUT 1
@ -40,10 +42,17 @@
//! Specify whether a special mode store is used for Subsystem components.
#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 {
//! Default timestamp size. The default timestamp will be an eight byte CDC
//! short timestamp.
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8;
//! Default timestamp size. The default timestamp will be an seven byte CDC short timestamp.
static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 7;
//! Configure the allocated pool sizes for the event manager.
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
//! 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 !
static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6;
static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124;
}
#endif /* CONFIG_FSFWCONFIG_H_ */

View File

@ -24,6 +24,13 @@
* 1. check logic when active-> checkChildrenStateOn
* 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 {
public:
@ -41,9 +48,6 @@ public:
virtual ~AssemblyBase();
protected:
// SHOULDDO: Change that OVERWRITE_HEALTH may be returned
// (or return internalState directly?)
/**
* Command children to reach [mode,submode] combination
* Can be done by setting #commandsOutstanding correctly,
@ -68,6 +72,18 @@ protected:
virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode,
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,
Submode_t submode) = 0;

View File

@ -308,11 +308,11 @@ void DeviceHandlerBase::doStateMachine() {
uint32_t currentUptime;
Clock::getUptime(&currentUptime);
if (currentUptime - timeoutStart >= childTransitionDelay) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_VERBOSE_LEVEL >= 1 && FSFW_OBJ_EVENT_TRANSLATION == 0
char printout[60];
sprintf(printout, "Transition timeout (%lu) occured !",
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",
RETURN_FAILED, printout);
#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,
uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) {
auto replyIter = deviceReplyMap.find(deviceReply);
@ -646,16 +655,12 @@ void DeviceHandlerBase::doGetWrite() {
void DeviceHandlerBase::doSendRead() {
ReturnValue_t result;
size_t requestLen = 0;
size_t replyLen = 0;
if(cookieInfo.pendingCommand != deviceCommandMap.end()) {
DeviceReplyIter iter = deviceReplyMap.find(
cookieInfo.pendingCommand->first);
if(iter != deviceReplyMap.end()) {
requestLen = iter->second.replyLen;
}
replyLen = getNextReplyLength(cookieInfo.pendingCommand->first);
}
result = communicationInterface->requestReceiveMessage(comCookie, requestLen);
result = communicationInterface->requestReceiveMessage(comCookie, replyLen);
if (result == RETURN_OK) {
cookieInfo.state = COOKIE_READ_SENT;
@ -1491,7 +1496,7 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
if(errorCode == ObjectManagerIF::CHILD_INIT_FAILED) {
errorPrint = "Initialization error";
}
if(errorCode == HasReturnvaluesIF::RETURN_FAILED) {
else if(errorCode == HasReturnvaluesIF::RETURN_FAILED) {
if(errorType == sif::OutputTypes::OUT_WARNING) {
errorPrint = "Generic Warning";
}
@ -1503,6 +1508,9 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
errorPrint = "Unknown error";
}
}
if(functionName == nullptr) {
functionName = "unknown function";
}
if(errorType == sif::OutputTypes::OUT_WARNING) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
@ -1511,18 +1519,18 @@ void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType,
std::dec << std::setfill(' ') << std::endl;
#else
sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint);
functionName, this->getObjectId(), errorPrint);
#endif
}
else if(errorType == sif::OutputTypes::OUT_ERROR) {
#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')
<< this->getObjectId() << " | " << errorPrint << std::dec
<< std::setfill(' ') << std::endl;
#else
sif::printError("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n",
this->getObjectId(), errorPrint);
functionName, this->getObjectId(), errorPrint);
#endif
}

View File

@ -471,13 +471,27 @@ protected:
ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand,
uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr,
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
* @return - @c RETURN_OK when the command was successfully inserted,
* - @c RETURN_FAILED else.
*/
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
* in the reply map.
@ -953,7 +967,7 @@ protected:
* - NO_REPLY_EXPECTED if there was no reply found. This is not an
* 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,
DeviceCommandId_t alternateReplyID = 0);

View File

@ -16,9 +16,9 @@ ReturnValue_t HealthDevice::performOperation(uint8_t opCode) {
CommandMessage command;
ReturnValue_t result = commandQueue->receiveMessage(&command);
if (result == HasReturnvaluesIF::RETURN_OK) {
healthHelper.handleHealthCommand(&command);
result = healthHelper.handleHealthCommand(&command);
}
return HasReturnvaluesIF::RETURN_OK;
return result;
}
ReturnValue_t HealthDevice::initialize() {

View File

@ -1,12 +1,30 @@
## Configuring the FSFW
Configuring the FSFW
======
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
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
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_EVENTMGMR_RANGEMATCHERS = 120;
}
```
```

View File

@ -19,8 +19,9 @@ A nullptr check of the returning Pointer must be done. This function is based on
```cpp
template <typename T> T* ObjectManagerIF::get( object_id_t id )
```
* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation.
By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards.
* A typical way to create all objects on startup is a handing a static produce function to the
ObjectManager on creation. By calling objectManager->initialize() the produce function will be
called and all SystemObjects will be initialized afterwards.
### Event Manager
@ -36,14 +37,19 @@ By calling objectManager->initialize() the produce function will be called and a
### Stores
* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can
be exchanged with Stores. With this, only the 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.
* The message based communication can only exchange a few bytes of information inside the message
itself. Therefore, additional information can be exchanged with Stores. With this, only the
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
### 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.
* 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
* The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the
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

View File

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

View File

@ -1,8 +1,6 @@
#include "EventManager.h"
#include "EventMessage.h"
#include <FSFWConfig.h>
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../ipc/QueueFactory.h"
#include "../ipc/MutexFactory.h"
@ -115,53 +113,6 @@ ReturnValue_t EventManager::unsubscribeFromEventRange(MessageQueueId_t listener,
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() {
mutex->lockMutex(timeoutType, timeoutMs);
}
@ -175,3 +126,85 @@ void EventManager::setMutexTimeout(MutexIF::TimeoutType timeoutType,
this->timeoutType = timeoutType;
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 */

View File

@ -6,6 +6,7 @@
#include <FSFWConfig.h>
#include "../serviceinterface/ServiceInterface.h"
#include "../objectmanager/SystemObject.h"
#include "../storagemanager/LocalPool.h"
#include "../tasks/ExecutableObjectIF.h"
@ -67,6 +68,7 @@ protected:
#if FSFW_OBJ_EVENT_TRANSLATION == 1
void printEvent(EventMessage *message);
void printUtility(sif::OutputTypes printType, EventMessage* message);
#endif
void lockMutex();

View File

@ -109,6 +109,6 @@ bool EventMessage::isClearedEventMessage() {
return getEvent() == INVALID_EVENT;
}
size_t EventMessage::getMinimumMessageSize() {
size_t EventMessage::getMinimumMessageSize() const {
return EVENT_MESSAGE_SIZE;
}

View File

@ -45,7 +45,7 @@ public:
protected:
static const Event INVALID_EVENT = 0;
virtual size_t getMinimumMessageSize();
virtual size_t getMinimumMessageSize() const override;
};

View File

@ -1,30 +1,37 @@
#ifndef FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
#define FSFW_EVENTS_FWSUBSYSTEMIDRANGES_H_
#include <cstdint>
namespace SUBSYSTEM_ID {
enum {
MEMORY = 22,
OBSW = 26,
CDH = 28,
TCS_1 = 59,
PCDU_1 = 42,
PCDU_2 = 43,
HEATER = 50,
T_SENSORS = 52,
FDIR = 70,
FDIR_1 = 71,
FDIR_2 = 72,
HK = 73,
SYSTEM_MANAGER = 74,
SYSTEM_MANAGER_1 = 75,
SYSTEM_1 = 79,
PUS_SERVICE_1 = 80,
PUS_SERVICE_9 = 89,
PUS_SERVICE_17 = 97,
FW_SUBSYSTEM_ID_RANGE
enum: uint8_t {
MEMORY = 22,
OBSW = 26,
CDH = 28,
TCS_1 = 59,
PCDU_1 = 42,
PCDU_2 = 43,
HEATER = 50,
T_SENSORS = 52,
FDIR = 70,
FDIR_1 = 71,
FDIR_2 = 72,
HK = 73,
SYSTEM_MANAGER = 74,
SYSTEM_MANAGER_1 = 75,
SYSTEM_1 = 79,
PUS_SERVICE_1 = 80,
PUS_SERVICE_2 = 82,
PUS_SERVICE_3 = 83,
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_ */

View File

@ -60,7 +60,7 @@ void arrayprinter::printHex(const uint8_t *data, size_t size,
#else
// General format: 0x01, 0x02, 0x03 so it is number of chars times 6
// plus line break plus small safety margin.
char printBuffer[(size + 1) * 7 + 1];
char printBuffer[(size + 1) * 7 + 1] = {};
size_t currentPos = 0;
for(size_t i = 0; i < size; i++) {
// 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);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}
@ -101,7 +103,7 @@ void arrayprinter::printDec(const uint8_t *data, size_t size,
#else
// General format: 32, 243, -12 so it is number of chars times 5
// plus line break plus small safety margin.
char printBuffer[(size + 1) * 5 + 1];
char printBuffer[(size + 1) * 5 + 1] = {};
size_t currentPos = 0;
for(size_t i = 0; i < size; i++) {
// 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);
#endif /* FSFW_DISABLE_PRINTOUT == 0 */
#endif
}

View File

@ -1,5 +1,5 @@
#include "HealthTable.h"
#include "../ipc/MutexHelper.h"
#include "../ipc/MutexGuard.h"
#include "../ipc/MutexFactory.h"
#include "../serialize/SerializeAdapter.h"
@ -31,7 +31,7 @@ ReturnValue_t HealthTable::registerObject(object_id_t object,
void HealthTable::setHealth(object_id_t object,
HasHealthIF::HealthState newState) {
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
iter->second = newState;
@ -40,7 +40,7 @@ void HealthTable::setHealth(object_id_t object,
HasHealthIF::HealthState HealthTable::getHealth(object_id_t object) {
HasHealthIF::HealthState state = HasHealthIF::HEALTHY;
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
HealthMap::iterator iter = healthMap.find(object);
if (iter != healthMap.end()) {
state = iter->second;
@ -49,7 +49,7 @@ HasHealthIF::HealthState HealthTable::getHealth(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);
if (iter != healthMap.end()) {
return true;
@ -58,35 +58,51 @@ bool HealthTable::hasHealth(object_id_t object) {
}
size_t HealthTable::getPrintSize() {
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
uint32_t size = healthMap.size() * sizeof(object_id_t) +
sizeof(HasHealthIF::HealthState) + sizeof(uint16_t);
return size;
}
void HealthTable::printAll(uint8_t* pointer, size_t maxSize) {
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
size_t size = 0;
uint16_t count = healthMap.size();
SerializeAdapter::serialize(&count,
ReturnValue_t result = SerializeAdapter::serialize(&count,
&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) {
SerializeAdapter::serialize(&health.first,
result = SerializeAdapter::serialize(&health.first,
&pointer, &size, maxSize, SerializeIF::Endianness::BIG);
if(result != HasReturnvaluesIF::RETURN_OK) {
return;
}
uint8_t healthValue = health.second;
SerializeAdapter::serialize(&healthValue, &pointer, &size,
result = SerializeAdapter::serialize(&healthValue, &pointer, &size,
maxSize, SerializeIF::Endianness::BIG);
if(result != HasReturnvaluesIF::RETURN_OK) {
return;
}
}
}
ReturnValue_t HealthTable::iterate(HealthEntry *value, bool reset) {
ReturnValue_t result = HasReturnvaluesIF::RETURN_OK;
MutexHelper(mutex, timeoutType, mutexTimeoutMs);
MutexGuard(mutex, timeoutType, mutexTimeoutMs);
if (reset) {
mapIterator = healthMap.begin();
}
if (mapIterator == healthMap.end()) {
result = HasReturnvaluesIF::RETURN_FAILED;
return HasReturnvaluesIF::RETURN_FAILED;
}
*value = *mapIterator;
mapIterator++;

View File

@ -84,15 +84,21 @@ void HousekeepingMessage::setCollectionIntervalModificationCommand(
else {
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);
}
sid_t HousekeepingMessage::getCollectionIntervalModificationCommand(
const CommandMessage* command, float* newCollectionInterval) {
if(newCollectionInterval != nullptr) {
*newCollectionInterval = command->getParameter3();
std::memcpy(newCollectionInterval, command->getData() + 2 * sizeof(uint32_t),
sizeof(*newCollectionInterval));
}
return getSid(command);
@ -151,7 +157,8 @@ void HousekeepingMessage::clear(CommandMessage* message) {
case(DIAGNOSTICS_REPORT):
case(HK_DEFINITIONS_REPORT):
case(DIAGNOSTICS_DEFINITION_REPORT):
case(UPDATE_SNAPSHOT_SET): {
case(UPDATE_SNAPSHOT_SET):
case(UPDATE_SNAPSHOT_VARIABLE): {
store_address_t storeId;
getHkDataReply(message, &storeId);
StorageManagerIF *ipcStore = objectManager->get<StorageManagerIF>(

View File

@ -11,7 +11,8 @@
* @brief This helper class will be used to serialize and deserialize update housekeeping packets
* into the store.
*/
class HousekeepingSnapshot: public SerializeIF {
class HousekeepingSnapshot:
public SerializeIF {
public:
/**
@ -36,6 +37,17 @@ public:
timeStamp(timeStamp), timeStampSize(timeStampSize),
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.
* @param timeStamp
@ -47,8 +59,8 @@ public:
timeStamp(timeStamp), timeStampSize(timeStampSize),
updateData(dataSetPtr) {};
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size,
size_t maxSize, Endianness streamEndianness) const {
virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize,
Endianness streamEndianness) const {
if(timeStamp != nullptr) {
/* Endianness will always be MACHINE, so we can simply use memcpy
here. */

View File

@ -4,46 +4,87 @@
#include <cmath>
PeriodicHousekeepingHelper::PeriodicHousekeepingHelper(
LocalPoolDataSetBase* owner): owner(owner) {}
LocalPoolDataSetBase* owner): owner(owner) {}
void PeriodicHousekeepingHelper::initialize(float collectionInterval,
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
uint8_t nonDiagIntervalFactor) {
this->minimumPeriodicInterval = minimumPeriodicInterval;
if(not isDiagnostics) {
this->minimumPeriodicInterval = this->minimumPeriodicInterval *
nonDiagIntervalFactor;
}
collectionIntervalTicks = intervalSecondsToInterval(collectionInterval);
dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) {
this->minimumPeriodicInterval = minimumPeriodicInterval;
this->nonDiagIntervalFactor = nonDiagIntervalFactor;
collectionIntervalTicks = intervalSecondsToIntervalTicks(collectionInterval);
/* This will cause a checkOpNecessary call to be true immediately. I think it's okay
if a HK packet is generated immediately instead of waiting one generation cycle. */
internalTickCounter = collectionIntervalTicks;
}
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() {
return intervalToIntervalSeconds(collectionIntervalTicks);
float PeriodicHousekeepingHelper::getCollectionIntervalInSeconds() const {
return intervalTicksToSeconds(collectionIntervalTicks);
}
bool PeriodicHousekeepingHelper::checkOpNecessary() {
if(internalTickCounter >= collectionIntervalTicks) {
internalTickCounter = 1;
return true;
}
internalTickCounter++;
return false;
if(internalTickCounter >= collectionIntervalTicks) {
internalTickCounter = 1;
return true;
}
internalTickCounter++;
return false;
}
uint32_t PeriodicHousekeepingHelper::intervalSecondsToInterval(
float collectionIntervalSeconds) {
return std::ceil(collectionIntervalSeconds * 1000
/ minimumPeriodicInterval);
uint32_t PeriodicHousekeepingHelper::intervalSecondsToIntervalTicks(
float collectionIntervalSeconds) {
if(owner == nullptr) {
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(
uint32_t collectionInterval) {
return static_cast<float>(collectionInterval *
minimumPeriodicInterval);
float PeriodicHousekeepingHelper::intervalTicksToSeconds(
uint32_t collectionInterval) const {
/* Number of ticks times the minimum interval is in milliseconds, so we divide by 1000 to get
the value in seconds */
return static_cast<float>(collectionInterval * minimumPeriodicInterval / 1000.0);
}
void PeriodicHousekeepingHelper::changeCollectionInterval(
float newIntervalSeconds) {
collectionIntervalTicks = intervalSecondsToInterval(newIntervalSeconds);
float newIntervalSeconds) {
collectionIntervalTicks = intervalSecondsToIntervalTicks(newIntervalSeconds);
}

View File

@ -10,18 +10,19 @@ class PeriodicHousekeepingHelper {
public:
PeriodicHousekeepingHelper(LocalPoolDataSetBase* owner);
void initialize(float collectionInterval,
dur_millis_t minimumPeriodicInterval, bool isDiagnostics,
uint8_t nonDiagIntervalFactor);
void initialize(float collectionInterval, dur_millis_t minimumPeriodicInterval,
uint8_t nonDiagIntervalFactor);
void changeCollectionInterval(float newInterval);
float getCollectionIntervalInSeconds();
float getCollectionIntervalInSeconds() const;
bool checkOpNecessary();
private:
LocalPoolDataSetBase* owner = nullptr;
uint8_t nonDiagIntervalFactor = 0;
uint32_t intervalSecondsToInterval(float collectionIntervalSeconds);
float intervalToIntervalSeconds(uint32_t collectionInterval);
uint32_t intervalSecondsToIntervalTicks(float collectionIntervalSeconds);
float intervalTicksToSeconds(uint32_t collectionInterval) const;
dur_millis_t minimumPeriodicInterval = 0;
uint32_t internalTickCounter = 1;

View File

@ -3,19 +3,20 @@
#include "../ipc/QueueFactory.h"
#include "../ipc/MutexFactory.h"
#include "../serviceinterface/ServiceInterface.h"
#include "../datapool/PoolReadGuard.h"
InternalErrorReporter::InternalErrorReporter(object_id_t setObjectId,
uint32_t messageQueueDepth): SystemObject(setObjectId),
commandQueue(QueueFactory::instance()->
createMessageQueue(messageQueueDepth)),
poolManager(this, commandQueue),
internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID),
internalErrorDataset(this) {
mutex = MutexFactory::instance()->createMutex();
commandQueue(QueueFactory::instance()->
createMessageQueue(messageQueueDepth)),
poolManager(this, commandQueue),
internalErrorSid(setObjectId, InternalErrorDataset::ERROR_SET_ID),
internalErrorDataset(this) {
mutex = MutexFactory::instance()->createMutex();
}
InternalErrorReporter::~InternalErrorReporter() {
MutexFactory::instance()->deleteMutex(mutex);
MutexFactory::instance()->deleteMutex(mutex);
}
void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
@ -23,126 +24,128 @@ void InternalErrorReporter::setDiagnosticPrintout(bool enable) {
}
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 newTmHits = getAndResetTmHits();
uint32_t newStoreHits = getAndResetStoreHits();
uint32_t newQueueHits = getAndResetQueueHits();
uint32_t newTmHits = getAndResetTmHits();
uint32_t newStoreHits = getAndResetStoreHits();
#if FSFW_VERBOSE_LEVEL == 1
if(diagnosticPrintout) {
if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) {
#if FSFW_VERBOSE_LEVEL >= 1
if(diagnosticPrintout) {
if((newQueueHits > 0) or (newTmHits > 0) or (newStoreHits > 0)) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "InternalErrorReporter::performOperation: Errors "
<< "occured!" << std::endl;
sif::debug << "Queue errors: " << newQueueHits << std::endl;
sif::debug << "TM errors: " << newTmHits << std::endl;
sif::debug << "Store errors: " << newStoreHits << std::endl;
sif::debug << "InternalErrorReporter::performOperation: Errors "
<< "occured!" << std::endl;
sif::debug << "Queue errors: " << newQueueHits << std::endl;
sif::debug << "TM errors: " << newTmHits << std::endl;
sif::debug << "Store errors: " << newStoreHits << std::endl;
#else
sif::printDebug("InternalErrorReporter::performOperation: Errors occured!\n");
sif::printDebug("Queue errors: %lu\n", static_cast<unsigned int>(newQueueHits));
sif::printDebug("TM errors: %lu\n", static_cast<unsigned int>(newTmHits));
sif::printDebug("Store errors: %lu\n", static_cast<unsigned int>(newStoreHits));
sif::printDebug("InternalErrorReporter::performOperation: Errors occured!\n");
sif::printDebug("Queue errors: %lu\n", static_cast<unsigned int>(newQueueHits));
sif::printDebug("TM errors: %lu\n", static_cast<unsigned int>(newTmHits));
sif::printDebug("Store errors: %lu\n", static_cast<unsigned int>(newStoreHits));
#endif
}
}
}
}
#endif
internalErrorDataset.queueHits.value += newQueueHits;
internalErrorDataset.storeHits.value += newStoreHits;
internalErrorDataset.tmHits.value += newTmHits;
internalErrorDataset.setValidity(true, true);
internalErrorDataset.commit(timeoutType, timeoutMs);
{
PoolReadGuard readGuard(&internalErrorDataset);
if(readGuard.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
internalErrorDataset.queueHits.value += newQueueHits;
internalErrorDataset.storeHits.value += newStoreHits;
internalErrorDataset.tmHits.value += newTmHits;
internalErrorDataset.setValidity(true, true);
}
}
poolManager.performHkOperation();
CommandMessage message;
ReturnValue_t result = commandQueue->receiveMessage(&message);
if(result != MessageQueueIF::EMPTY) {
poolManager.handleHousekeepingMessage(&message);
}
return HasReturnvaluesIF::RETURN_OK;
poolManager.performHkOperation();
return HasReturnvaluesIF::RETURN_OK;
}
void InternalErrorReporter::queueMessageNotSent() {
incrementQueueHits();
incrementQueueHits();
}
void InternalErrorReporter::lostTm() {
incrementTmHits();
incrementTmHits();
}
uint32_t InternalErrorReporter::getAndResetQueueHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = queueHits;
queueHits = 0;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = queueHits;
queueHits = 0;
mutex->unlockMutex();
return value;
}
uint32_t InternalErrorReporter::getQueueHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = queueHits;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = queueHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementQueueHits() {
mutex->lockMutex(timeoutType, timeoutMs);
queueHits++;
mutex->unlockMutex();
mutex->lockMutex(timeoutType, timeoutMs);
queueHits++;
mutex->unlockMutex();
}
uint32_t InternalErrorReporter::getAndResetTmHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = tmHits;
tmHits = 0;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = tmHits;
tmHits = 0;
mutex->unlockMutex();
return value;
}
uint32_t InternalErrorReporter::getTmHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = tmHits;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = tmHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementTmHits() {
mutex->lockMutex(timeoutType, timeoutMs);
tmHits++;
mutex->unlockMutex();
mutex->lockMutex(timeoutType, timeoutMs);
tmHits++;
mutex->unlockMutex();
}
void InternalErrorReporter::storeFull() {
incrementStoreHits();
incrementStoreHits();
}
uint32_t InternalErrorReporter::getAndResetStoreHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = storeHits;
storeHits = 0;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = storeHits;
storeHits = 0;
mutex->unlockMutex();
return value;
}
uint32_t InternalErrorReporter::getStoreHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = storeHits;
mutex->unlockMutex();
return value;
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = storeHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementStoreHits() {
mutex->lockMutex(timeoutType, timeoutMs);
storeHits++;
mutex->unlockMutex();
mutex->lockMutex(timeoutType, timeoutMs);
storeHits++;
mutex->unlockMutex();
}
object_id_t InternalErrorReporter::getObjectId() const {
@ -155,14 +158,11 @@ MessageQueueId_t InternalErrorReporter::getCommandQueue() const {
ReturnValue_t InternalErrorReporter::initializeLocalDataPool(
localpool::DataPool &localDataPoolMap, LocalDataPoolManager &poolManager) {
localDataPoolMap.emplace(errorPoolIds::TM_HITS,
new PoolEntry<uint32_t>());
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS,
new PoolEntry<uint32_t>());
localDataPoolMap.emplace(errorPoolIds::STORE_HITS,
new PoolEntry<uint32_t>());
poolManager.subscribeForPeriodicPacket(internalErrorSid, false,
getPeriodicOperationFrequency(), true);
localDataPoolMap.emplace(errorPoolIds::TM_HITS, new PoolEntry<uint32_t>());
localDataPoolMap.emplace(errorPoolIds::QUEUE_HITS, new PoolEntry<uint32_t>());
localDataPoolMap.emplace(errorPoolIds::STORE_HITS, new PoolEntry<uint32_t>());
poolManager.subscribeForPeriodicPacket(internalErrorSid, false, getPeriodicOperationFrequency(),
true);
internalErrorDataset.setValidity(true, true);
return HasReturnvaluesIF::RETURN_OK;
}
@ -192,9 +192,9 @@ ReturnValue_t InternalErrorReporter::initializeAfterTaskCreation() {
}
void InternalErrorReporter::setMutexTimeout(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs) {
this->timeoutType = timeoutType;
this->timeoutMs = timeoutMs;
uint32_t timeoutMs) {
this->timeoutType = timeoutType;
this->timeoutMs = timeoutMs;
}
LocalDataPoolManager* InternalErrorReporter::getHkManagerHandle() {

View File

@ -17,77 +17,78 @@
* All functions were kept virtual so this class can be extended easily
* to store custom internal errors (e.g. communication interface errors).
*/
class InternalErrorReporter: public SystemObject,
public ExecutableObjectIF,
public InternalErrorReporterIF,
public HasLocalDataPoolIF {
class InternalErrorReporter:
public SystemObject,
public ExecutableObjectIF,
public InternalErrorReporterIF,
public HasLocalDataPoolIF {
public:
InternalErrorReporter(object_id_t setObjectId,
uint32_t messageQueueDepth = 5);
InternalErrorReporter(object_id_t setObjectId,
uint32_t messageQueueDepth = 5);
/**
* Enable diagnostic printout. Please note that this feature will
* only work if DEBUG has been supplied to the build defines.
* @param enable
*/
void setDiagnosticPrintout(bool enable);
/**
* Enable diagnostic printout. Please note that this feature will
* only work if DEBUG has been supplied to the build defines.
* @param enable
*/
void setDiagnosticPrintout(bool enable);
void setMutexTimeout(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs);
void setMutexTimeout(MutexIF::TimeoutType timeoutType,
uint32_t timeoutMs);
virtual ~InternalErrorReporter();
virtual ~InternalErrorReporter();
virtual object_id_t getObjectId() const override;
virtual MessageQueueId_t getCommandQueue() const override;
virtual ReturnValue_t initializeLocalDataPool(
localpool::DataPool& localDataPoolMap,
localpool::DataPool& localDataPoolMap,
LocalDataPoolManager& poolManager) override;
virtual dur_millis_t getPeriodicOperationFrequency() const override;
virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override;
LocalDataPoolManager* getHkManagerHandle() override;
LocalDataPoolManager* getHkManagerHandle() override;
virtual ReturnValue_t initialize() 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:
MessageQueueIF* commandQueue;
LocalDataPoolManager poolManager;
MessageQueueIF* commandQueue;
LocalDataPoolManager poolManager;
PeriodicTaskIF* executingTask = nullptr;
PeriodicTaskIF* executingTask = nullptr;
MutexIF* mutex = nullptr;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t timeoutMs = 20;
MutexIF* mutex = nullptr;
MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
uint32_t timeoutMs = 20;
sid_t internalErrorSid;
InternalErrorDataset internalErrorDataset;
sid_t internalErrorSid;
InternalErrorDataset internalErrorDataset;
bool diagnosticPrintout = true;
bool diagnosticPrintout = true;
uint32_t queueHits = 0;
uint32_t tmHits = 0;
uint32_t storeHits = 0;
uint32_t queueHits = 0;
uint32_t tmHits = 0;
uint32_t storeHits = 0;
uint32_t getAndResetQueueHits();
uint32_t getQueueHits();
void incrementQueueHits();
uint32_t getAndResetQueueHits();
uint32_t getQueueHits();
void incrementQueueHits();
uint32_t getAndResetTmHits();
uint32_t getTmHits();
void incrementTmHits();
uint32_t getAndResetTmHits();
uint32_t getTmHits();
void incrementTmHits();
uint32_t getAndResetStoreHits();
uint32_t getStoreHits();
void incrementStoreHits();
uint32_t getAndResetStoreHits();
uint32_t getStoreHits();
void incrementStoreHits();
};

View File

@ -22,12 +22,12 @@ public:
static const uint8_t INTERFACE_ID = CLASS_ID::MESSAGE_QUEUE_IF;
//! No new messages on the queue
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);
//! 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);
//! Returned if the target destination is invalid.
static constexpr ReturnValue_t DESTINVATION_INVALID = MAKE_RETURN_CODE(4);
//! [EXPORT] : [COMMENT] Returned if the target destination is invalid.
static constexpr ReturnValue_t DESTINATION_INVALID = MAKE_RETURN_CODE(4);
virtual ~MessageQueueIF() {}
/**

View File

@ -86,3 +86,7 @@ size_t MessageQueueMessage::getMaximumMessageSize() const {
return this->MAX_MESSAGE_SIZE;
}
size_t MessageQueueMessage::getMaximumDataSize() const {
return this->MAX_DATA_SIZE;
}

View File

@ -139,6 +139,7 @@ public:
virtual void setMessageSize(size_t messageSize) override;
virtual size_t getMinimumMessageSize() const override;
virtual size_t getMaximumMessageSize() const override;
virtual size_t getMaximumDataSize() const override;
/**
* @brief This is a debug method that prints the content.

View File

@ -72,6 +72,7 @@ public:
virtual void setMessageSize(size_t messageSize) = 0;
virtual size_t getMinimumMessageSize() const = 0;
virtual size_t getMaximumMessageSize() const = 0;
virtual size_t getMaximumDataSize() const = 0;
};

60
ipc/MutexGuard.h Normal file
View 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_ */

View File

@ -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_ */

View File

@ -108,9 +108,9 @@ void ObjectManager::initialize() {
result = it.second->checkObjectConnections();
if ( result != RETURN_OK ) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "ObjectManager::ObjectManager: Object " << std::hex <<
(int) it.first << " connection check failed with code 0x"
<< result << std::dec << std::endl;
sif::error << "ObjectManager::ObjectManager: Object 0x" << std::hex <<
(int) it.first << " connection check failed with code 0x" << result <<
std::dec << std::endl;
#endif
errorCount++;
}

View File

@ -4,7 +4,7 @@
#include "frameworkObjects.h"
#include "SystemObjectIF.h"
#include "../returnvalues/HasReturnvaluesIF.h"
#include "../serviceinterface/ServiceInterfaceStream.h"
#include "../serviceinterface/ServiceInterface.h"
/**
* @brief This class provides an interface to the global object manager.

View File

@ -1,8 +1,9 @@
#ifndef 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 {
enum framework_objects: object_id_t {
FSFW_OBJECTS_START = 0x53000000,
@ -16,6 +17,7 @@ enum framework_objects: object_id_t {
PUS_SERVICE_17_TEST = 0x53000017,
PUS_SERVICE_20_PARAMETERS = 0x53000020,
PUS_SERVICE_200_MODE_MGMT = 0x53000200,
PUS_SERVICE_201_HEALTH = 0x53000201,
//Generic IDs for IPC, modes, health, events
HEALTH_TABLE = 0x53010000,

View File

@ -1,34 +1,35 @@
# Check the OS_FSFW variable
if(${OS_FSFW} STREQUAL "freertos")
add_subdirectory(FreeRTOS)
add_subdirectory(FreeRTOS)
elseif(${OS_FSFW} STREQUAL "rtems")
add_subdirectory(rtems)
add_subdirectory(rtems)
elseif(${OS_FSFW} STREQUAL "linux")
add_subdirectory(linux)
add_subdirectory(linux)
elseif(${OS_FSFW} STREQUAL "host")
add_subdirectory(host)
if (WIN32)
add_subdirectory(windows)
elseif(UNIX)
target_sources(${LIB_FSFW_NAME}
PUBLIC
linux/TcUnixUdpPollingTask.cpp
linux/TmTcUnixUdpBridge.cpp
)
endif ()
add_subdirectory(host)
if (WIN32)
add_subdirectory(windows)
elseif(UNIX)
# We still need to pull in some Linux specific sources
target_sources(${LIB_FSFW_NAME} PUBLIC
linux/tcpipHelpers.cpp
)
endif ()
else()
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
if (WIN32)
add_subdirectory(host)
add_subdirectory(windows)
elseif (UNIX)
add_subdirectory(linux)
else ()
# MacOS or other OSes have not been tested yet / are not supported.
message(FATAL_ERROR "The host OS could not be determined! Aborting.")
endif()
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
if (WIN32)
add_subdirectory(host)
add_subdirectory(windows)
elseif (UNIX)
add_subdirectory(linux)
else ()
# MacOS or other OSes have not been tested yet / are not supported.
message(FATAL_ERROR "The host OS could not be determined! Aborting.")
endif()
endif()
endif()
add_subdirectory(common)

View File

@ -111,7 +111,7 @@ ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from,
timeval* to) {
struct tm time_tm;
struct tm time_tm = {};
time_tm.tm_year = from->year - 1900;
time_tm.tm_mon = from->month - 1;

View File

@ -135,7 +135,7 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
QueueHandle_t destination = nullptr;
if(sendTo == MessageQueueIF::NO_QUEUE or sendTo == 0x00) {
return MessageQueueIF::DESTINVATION_INVALID;
return MessageQueueIF::DESTINATION_INVALID;
}
else {
destination = reinterpret_cast<QueueHandle_t>(sendTo);

View 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
View 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
View 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_ */

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

View 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_ */

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

View 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_ */

View 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
}

View 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_ */

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

View 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_ */

View 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
View 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_ */

View 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_ */

View File

@ -10,6 +10,7 @@ target_sources(${LIB_FSFW_NAME}
QueueMapManager.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
taskHelpers.cpp
)
if(UNIX)

View File

@ -1,10 +1,12 @@
#include "../../serviceinterface/ServiceInterface.h"
#include "../../timemanager/Clock.h"
#include "../../platform.h"
#include <chrono>
#if defined(WIN32)
#if defined(PLATFORM_WIN)
#include <sysinfoapi.h>
#elif defined(LINUX)
#elif defined(PLATFORM_UNIX)
#include <fstream>
#endif
@ -46,7 +48,7 @@ ReturnValue_t Clock::setClock(const timeval* time) {
}
ReturnValue_t Clock::getClock_timeval(timeval* time) {
#if defined(WIN32)
#if defined(PLATFORM_WIN)
auto now = std::chrono::system_clock::now();
auto secondsChrono = std::chrono::time_point_cast<std::chrono::seconds>(now);
auto epoch = now.time_since_epoch();
@ -54,7 +56,7 @@ ReturnValue_t Clock::getClock_timeval(timeval* time) {
auto fraction = now - secondsChrono;
time->tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(fraction).count();
return HasReturnvaluesIF::RETURN_OK;
#elif defined(LINUX)
#elif defined(PLATFORM_UNIX)
timespec timeUnix;
int status = clock_gettime(CLOCK_REALTIME,&timeUnix);
if(status!=0){
@ -85,14 +87,14 @@ ReturnValue_t Clock::getClock_usecs(uint64_t* time) {
timeval Clock::getUptime() {
timeval timeval;
#if defined(WIN32)
#if defined(PLATFORM_WIN)
auto uptime = std::chrono::milliseconds(GetTickCount64());
auto secondsChrono = std::chrono::duration_cast<std::chrono::seconds>(uptime);
timeval.tv_sec = secondsChrono.count();
auto fraction = uptime - secondsChrono;
timeval.tv_usec = std::chrono::duration_cast<std::chrono::microseconds>(
fraction).count();
#elif defined(LINUX)
#elif defined(PLATFORM_UNIX)
double 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;
}
ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) {
/* Do some magic with chrono (C++20!) */
/* Right now, the library doesn't have the new features to get the required values yet.

View File

@ -1,18 +1,19 @@
#include "taskHelpers.h"
#include "../../platform.h"
#include "../../osal/host/FixedTimeslotTask.h"
#include "../../ipc/MutexFactory.h"
#include "../../osal/host/Mutex.h"
#include "../../osal/host/FixedTimeslotTask.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
#include "../../tasks/ExecutableObjectIF.h"
#include <thread>
#include <chrono>
#if defined(WIN32)
#if defined(PLATFORM_WIN)
#include <windows.h>
#elif defined(LINUX)
#include "../windows/winTaskHelpers.h"
#elif defined(PLATFORM_UNIX)
#include <pthread.h>
#endif
@ -24,34 +25,12 @@ FixedTimeslotTask::FixedTimeslotTask(const char *name, TaskPriority setPriority,
// It is propably possible to set task priorities by using the native
// task handles for Windows / Linux
mainThread = std::thread(&FixedTimeslotTask::taskEntryPoint, this, this);
#if defined(WIN32)
/* List of possible priority classes:
* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/
* nf-processthreadsapi-setpriorityclass
* 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.
#if defined(_WIN32)
tasks::setTaskPriority(reinterpret_cast<HANDLE>(mainThread.native_handle()), setPriority);
#elif defined(__unix__)
// TODO: We could reuse existing code here.
#endif
tasks::insertTaskName(mainThread.get_id(), taskName);
}
FixedTimeslotTask::~FixedTimeslotTask(void) {
@ -60,7 +39,6 @@ FixedTimeslotTask::~FixedTimeslotTask(void) {
if(mainThread.joinable()) {
mainThread.join();
}
delete this;
}
void FixedTimeslotTask::taskEntryPoint(void* argument) {
@ -141,8 +119,11 @@ ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId,
}
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Component " << std::hex << componentId <<
" not found, not adding it to pst" << std::endl;
sif::error << "Component " << std::hex << "0x" << componentId << "not found, "
"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
return HasReturnvaluesIF::RETURN_FAILED;
}

View File

@ -3,7 +3,9 @@
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../ipc/MutexFactory.h"
#include "../../ipc/MutexHelper.h"
#include "../../ipc/MutexGuard.h"
#include <cstring>
MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize):
messageSize(maxMessageSize), messageDepth(messageDepth) {
@ -63,12 +65,9 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
if(messageQueue.empty()) {
return MessageQueueIF::EMPTY;
}
// not sure this will work..
//*message = std::move(messageQueue.front());
MutexHelper mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
MessageQueueMessage* currentMessage = &messageQueue.front();
std::copy(currentMessage->getBuffer(),
currentMessage->getBuffer() + messageSize, message->getBuffer());
MutexGuard mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20);
std::copy(messageQueue.front().data(), messageQueue.front().data() + messageSize,
message->getBuffer());
messageQueue.pop();
// The last partner is the first uint32_t field in the message
this->lastPartner = message->getSender();
@ -82,7 +81,7 @@ MessageQueueId_t MessageQueue::getLastPartner() const {
ReturnValue_t MessageQueue::flush(uint32_t* count) {
*count = messageQueue.size();
// Clears the queue.
messageQueue = std::queue<MessageQueueMessage>();
messageQueue = std::queue<std::vector<uint8_t>>();
return HasReturnvaluesIF::RETURN_OK;
}
@ -108,6 +107,9 @@ bool MessageQueue::isDefaultDestinationSet() const {
ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
MessageQueueMessageIF* message, MessageQueueId_t sentFrom,
bool ignoreFault) {
if(message == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
message->setSender(sentFrom);
if(message->getMessageSize() > message->getMaximumMessageSize()) {
// Actually, this should never happen or an error will be emitted
@ -126,25 +128,13 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
internalErrorReporter->queueMessageNotSent();
}
}
// TODO: Better returnvalue
return HasReturnvaluesIF::RETURN_FAILED;
return MessageQueueIF::DESTINATION_INVALID;
}
if(targetQueue->messageQueue.size() < targetQueue->messageDepth) {
MutexHelper mutexLock(targetQueue->queueLock,
MutexIF::TimeoutType::WAITING, 20);
// not ideal, works for now though.
MessageQueueMessage* mqmMessage =
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
}
MutexGuard mutexLock(targetQueue->queueLock, MutexIF::TimeoutType::WAITING, 20);
targetQueue->messageQueue.push(std::vector<uint8_t>(message->getMaximumMessageSize()));
memcpy(targetQueue->messageQueue.back().data(), message->getBuffer(),
message->getMaximumMessageSize());
}
else {
return MessageQueueIF::FULL;

View File

@ -212,20 +212,20 @@ protected:
//static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault);
private:
std::queue<MessageQueueMessage> messageQueue;
std::queue<std::vector<uint8_t>> messageQueue;
/**
* @brief The class stores the queue id it got assigned.
* 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 messageDepth = 0;
MutexIF* queueLock;
bool defaultDestinationSet = false;
MessageQueueId_t defaultDestination = 0;
MessageQueueId_t lastPartner = 0;
MessageQueueId_t defaultDestination = MessageQueueIF::NO_QUEUE;
MessageQueueId_t lastPartner = MessageQueueIF::NO_QUEUE;
};
#endif /* FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ */

View File

@ -24,5 +24,7 @@ MutexIF* MutexFactory::createMutex() {
}
void MutexFactory::deleteMutex(MutexIF* mutex) {
delete mutex;
if(mutex != nullptr) {
delete mutex;
}
}

View File

@ -1,6 +1,8 @@
#include "Mutex.h"
#include "PeriodicTask.h"
#include "taskHelpers.h"
#include "../../platform.h"
#include "../../ipc/MutexFactory.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../tasks/ExecutableObjectIF.h"
@ -8,9 +10,10 @@
#include <thread>
#include <chrono>
#if defined(WIN32)
#if defined(PLATFORM_WIN)
#include <processthreadsapi.h>
#elif defined(LINUX)
#include <fsfw/osal/windows/winTaskHelpers.h>
#elif defined(PLATFORM_UNIX)
#include <pthread.h>
#endif
@ -19,37 +22,15 @@ PeriodicTask::PeriodicTask(const char *name, TaskPriority setPriority,
void (*setDeadlineMissedFunc)()) :
started(false), taskName(name), period(setPeriod),
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
mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this);
#if defined(WIN32)
/* List of possible priority classes:
* https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/
* nf-processthreadsapi-setpriorityclass
* 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.
#if defined(PLATFORM_WIN)
tasks::setTaskPriority(reinterpret_cast<HANDLE>(mainThread.native_handle()), setPriority);
#elif defined(PLATFORM_UNIX)
// TODO: We could reuse existing code here.
#endif
tasks::insertTaskName(mainThread.get_id(), taskName);
}
PeriodicTask::~PeriodicTask(void) {
@ -58,7 +39,6 @@ PeriodicTask::~PeriodicTask(void) {
if(mainThread.joinable()) {
mainThread.join();
}
delete this;
}
void PeriodicTask::taskEntryPoint(void* argument) {

View File

@ -2,7 +2,7 @@
#include "../../serviceinterface/ServiceInterface.h"
#include "../../ipc/MutexFactory.h"
#include "../../ipc/MutexHelper.h"
#include "../../ipc/MutexGuard.h"
QueueMapManager* QueueMapManager::mqManagerInstance = nullptr;
@ -10,6 +10,10 @@ QueueMapManager::QueueMapManager() {
mapLock = MutexFactory::instance()->createMutex();
}
QueueMapManager::~QueueMapManager() {
MutexFactory::instance()->deleteMutex(mapLock);
}
QueueMapManager* QueueMapManager::instance() {
if (mqManagerInstance == nullptr){
mqManagerInstance = new QueueMapManager();
@ -43,7 +47,7 @@ ReturnValue_t QueueMapManager::addMessageQueue(
MessageQueueIF* QueueMapManager::getMessageQueue(
MessageQueueId_t messageQueueId) const {
MutexHelper(mapLock, MutexIF::TimeoutType::WAITING, 50);
MutexGuard(mapLock, MutexIF::TimeoutType::WAITING, 50);
auto queueIter = queueMap.find(messageQueueId);
if(queueIter != queueMap.end()) {
return queueIter->second;

View File

@ -36,6 +36,8 @@ public:
private:
//! External instantiation is forbidden.
QueueMapManager();
~QueueMapManager();
uint32_t queueCounter = 0;
MutexIF* mapLock;
QueueMap queueMap;

View File

@ -1,7 +1,5 @@
#include "../../tasks/SemaphoreFactory.h"
#include "../../osal/linux/BinarySemaphore.h"
#include "../../osal/linux/CountingSemaphore.h"
#include "../../serviceinterface/ServiceInterfaceStream.h"
#include "../../serviceinterface/ServiceInterface.h"
SemaphoreFactory* SemaphoreFactory::factoryInstance = nullptr;

View File

@ -1,14 +1,16 @@
#include "taskHelpers.h"
#include "../../tasks/TaskFactory.h"
#include "../../osal/host/FixedTimeslotTask.h"
#include "../../osal/host/PeriodicTask.h"
#include "../../tasks/TaskFactory.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
#include "../../tasks/PeriodicTaskIF.h"
#include "../../serviceinterface/ServiceInterface.h"
#include <chrono>
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;
TaskFactory::TaskFactory() {
@ -49,8 +51,12 @@ ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){
}
void TaskFactory::printMissedDeadline() {
/* TODO: Implement */
return;
std::string name = tasks::getTaskName(std::this_thread::get_id());
#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
View 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
View 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_ */

View File

@ -13,9 +13,8 @@ target_sources(${LIB_FSFW_NAME}
QueueFactory.cpp
SemaphoreFactory.cpp
TaskFactory.cpp
TcUnixUdpPollingTask.cpp
TmTcUnixUdpBridge.cpp
Timer.cpp
tcpipHelpers.cpp
)
find_package(Threads REQUIRED)

View File

@ -190,13 +190,15 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
}else if(status==0){
}
else if (status==0) {
//Success but no message received
return MessageQueueIF::EMPTY;
} else {
}
else {
//No message was received. Keep lastPartner anyway, I might send
//something later. But still, delete packet content.
memset(message->getData(), 0, message->getMaximumMessageSize());
memset(message->getData(), 0, message->getMaximumDataSize());
switch(errno){
case EAGAIN:
//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 "
<< sentFrom << std::endl;
#endif
return DESTINVATION_INVALID;
return DESTINATION_INVALID;
}
case EINTR:
//The call was interrupted by a signal.

View File

@ -6,250 +6,263 @@
#include <errno.h>
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';
std::strncat(name, name_, PTHREAD_MAX_NAMELEN - 1);
}
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) {
//TODO sleep might be better with timer instead of sleep()
timespec time;
time.tv_sec = ns/1000000000;
time.tv_nsec = ns - time.tv_sec*1e9;
//TODO sleep might be better with timer instead of sleep()
timespec time;
time.tv_sec = ns/1000000000;
time.tv_nsec = ns - time.tv_sec*1e9;
//Remaining Time is not set here
int status = nanosleep(&time,NULL);
if(status != 0){
switch(errno){
case EINTR:
//The nanosleep() function was interrupted by a signal.
return HasReturnvaluesIF::RETURN_FAILED;
case EINVAL:
//The rqtp argument specified a nanosecond value less than zero or
// greater than or equal to 1000 million.
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
//Remaining Time is not set here
int status = nanosleep(&time,NULL);
if(status != 0){
switch(errno){
case EINTR:
//The nanosleep() function was interrupted by a signal.
return HasReturnvaluesIF::RETURN_FAILED;
case EINVAL:
//The rqtp argument specified a nanosecond value less than zero or
// greater than or equal to 1000 million.
return HasReturnvaluesIF::RETURN_FAILED;
default:
return HasReturnvaluesIF::RETURN_FAILED;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
return HasReturnvaluesIF::RETURN_OK;
}
void PosixThread::suspend() {
//Wait for SIGUSR1
int caughtSig = 0;
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
sigwait(&waitSignal, &caughtSig);
if (caughtSig != SIGUSR1) {
//Wait for SIGUSR1
int caughtSig = 0;
sigset_t waitSignal;
sigemptyset(&waitSignal);
sigaddset(&waitSignal, SIGUSR1);
sigwait(&waitSignal, &caughtSig);
if (caughtSig != SIGUSR1) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
caughtSig << std::endl;
sif::error << "FixedTimeslotTask: Unknown Signal received: " <<
caughtSig << std::endl;
#endif
}
}
}
void PosixThread::resume(){
/* 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 */
pthread_kill(thread,SIGUSR1);
/* 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 itsself this is not possible here
*/
pthread_kill(thread,SIGUSR1);
}
bool PosixThread::delayUntil(uint64_t* const prevoiusWakeTime_ms,
const uint64_t delayTime_ms) {
uint64_t nextTimeToWake_ms;
bool shouldDelay = false;
/* Get current Time */
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
/* Generate the tick time at which the task wants to wake. */
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
const uint64_t delayTime_ms) {
uint64_t nextTimeToWake_ms;
bool shouldDelay = false;
//Get current Time
const uint64_t currentTime_ms = getCurrentMonotonicTimeMs();
/* Generate the tick time at which the task wants to wake. */
nextTimeToWake_ms = (*prevoiusWakeTime_ms) + delayTime_ms;
if (currentTime_ms < *prevoiusWakeTime_ms) {
/* The tick count has overflowed since this function was
lasted called. In this case the only time we should ever
actually delay is if the wake time has also overflowed,
and the wake time is greater than the tick time. When this
is the case it is as if neither time had overflowed. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
&& (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
} else {
/* The tick time has not overflowed. In this case we will
delay if either the wake time has overflowed, and/or the
tick time is less than the wake time. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|| (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
}
if (currentTime_ms < *prevoiusWakeTime_ms) {
/* The tick count has overflowed since this function was
lasted called. In this case the only time we should ever
actually delay is if the wake time has also overflowed,
and the wake time is greater than the tick time. When this
is the case it is as if neither time had overflowed. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
&& (nextTimeToWake_ms > currentTime_ms)) {
shouldDelay = true;
}
} else {
/* The tick time has not overflowed. In this case we will
delay if either the wake time has overflowed, and/or the
tick time is less than the wake time. */
if ((nextTimeToWake_ms < *prevoiusWakeTime_ms)
|| (nextTimeToWake_ms > currentTime_ms)) {
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) {
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
PosixThread::sleep(sleepTime * 1000000ull);
return true;
}
/* We are shifting the time in case the deadline was missed like RTEMS */
(*prevoiusWakeTime_ms) = currentTime_ms;
return false;
if (shouldDelay) {
uint64_t sleepTime = nextTimeToWake_ms - currentTime_ms;
PosixThread::sleep(sleepTime * 1000000ull);
return true;
}
//We are shifting the time in case the deadline was missed like rtems
(*prevoiusWakeTime_ms) = currentTime_ms;
return false;
}
uint64_t PosixThread::getCurrentMonotonicTimeMs(){
timespec timeNow;
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
+ timeNow.tv_nsec / 1000000;
timespec timeNow;
clock_gettime(CLOCK_MONOTONIC_RAW, &timeNow);
uint64_t currentTime_ms = (uint64_t) timeNow.tv_sec * 1000
+ timeNow.tv_nsec / 1000000;
return currentTime_ms;
return currentTime_ms;
}
void PosixThread::createTask(void* (*fnc_)(void*), void* arg_) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
//sif::debug << "PosixThread::createTask" << std::endl;
//sif::debug << "PosixThread::createTask" << std::endl;
#endif
/*
* The attr argument points to a pthread_attr_t structure whose contents
* are used at thread creation time to determine attributes for the new
* thread; this structure is initialized using pthread_attr_init(3) and
* related functions. If attr is NULL, then the thread is created with
* default attributes.
*/
pthread_attr_t attributes;
int status = pthread_attr_init(&attributes);
if(status != 0){
/*
* The attr argument points to a pthread_attr_t structure whose contents
are used at thread creation time to determine attributes for the new
thread; this structure is initialized using pthread_attr_init(3) and
related functions. If attr is NULL, then the thread is created with
default attributes.
*/
pthread_attr_t attributes;
int status = pthread_attr_init(&attributes);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute init failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute init failed with: " <<
strerror(status) << std::endl;
#endif
}
void* stackPointer;
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
if(status != 0){
}
void* stackPointer;
status = posix_memalign(&stackPointer, sysconf(_SC_PAGESIZE), stackSize);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Stack init failed with: " <<
strerror(status) << std::endl;
sif::error << "PosixThread::createTask: Stack init failed with: " <<
strerror(status) << std::endl;
#endif
if(errno == ENOMEM) {
size_t stackMb = stackSize/10e6;
if(errno == ENOMEM) {
size_t stackMb = stackSize/10e6;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Insufficient memory for"
" the requested " << stackMb << " MB" << std::endl;
sif::error << "PosixThread::createTask: Insufficient memory for"
" the requested " << stackMb << " MB" << std::endl;
#else
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
sif::printError("PosixThread::createTask: Insufficient memory for "
"the requested %lu MB\n", static_cast<unsigned long>(stackMb));
#endif
}
else if(errno == EINVAL) {
}
else if(errno == EINVAL) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Wrong alignment argument!"
<< std::endl;
sif::error << "PosixThread::createTask: Wrong alignment argument!"
<< std::endl;
#else
sif::printError("PosixThread::createTask: "
"Wrong alignment argument!\n");
sif::printError("PosixThread::createTask: "
"Wrong alignment argument!\n");
#endif
}
return;
}
}
return;
}
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
if(status != 0){
status = pthread_attr_setstack(&attributes, stackPointer, stackSize);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: pthread_attr_setstack "
" failed with: " << strerror(status) << std::endl;
sif::error << "Make sure the specified stack size is valid and is "
"larger than the minimum allowed stack size." << std::endl;
sif::error << "PosixThread::createTask: pthread_attr_setstack "
" failed with: " << strerror(status) << std::endl;
sif::error << "Make sure the specified stack size is valid and is "
"larger than the minimum allowed stack size." << std::endl;
#endif
}
}
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
if(status != 0){
status = pthread_attr_setinheritsched(&attributes, PTHREAD_EXPLICIT_SCHED);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute setinheritsched failed with: " <<
strerror(status) << std::endl;
#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
status = pthread_attr_setschedpolicy(&attributes,SCHED_FIFO);
if(status != 0){
sched_param scheduleParams;
scheduleParams.__sched_priority = priority;
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute schedule policy failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute schedule params failed with: " <<
strerror(status) << std::endl;
#endif
}
sched_param scheduleParams;
scheduleParams.__sched_priority = priority;
status = pthread_attr_setschedparam(&attributes, &scheduleParams);
if(status != 0){
}
#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 attribute schedule params failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread sigmask failed failed with: " <<
strerror(status) << " errno: " << strerror(errno) << std::endl;
#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_);
if(status != 0){
status = pthread_create(&thread,&attributes,fnc_,arg_);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread create failed with: " <<
strerror(status) << std::endl;
sif::error << "PosixThread::createTask: Failed with: " <<
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
}
}
status = pthread_setname_np(thread,name);
if(status != 0){
status = pthread_setname_np(thread,name);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: setname failed with: " <<
strerror(status) << std::endl;
sif::error << "PosixThread::createTask: setname failed with: " <<
strerror(status) << std::endl;
#endif
if(status == ERANGE) {
if(status == ERANGE) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Task name length longer"
" than 16 chars. Truncating.." << std::endl;
sif::error << "PosixThread::createTask: Task name length longer"
" than 16 chars. Truncating.." << std::endl;
#endif
name[15] = '\0';
status = pthread_setname_np(thread,name);
if(status != 0){
name[15] = '\0';
status = pthread_setname_np(thread,name);
if(status != 0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "PosixThread::createTask: Setting name"
" did not work.." << std::endl;
sif::error << "PosixThread::createTask: Setting name"
" did not work.." << std::endl;
#endif
}
}
}
}
}
}
status = pthread_attr_destroy(&attributes);
if(status!=0){
status = pthread_attr_destroy(&attributes);
if(status!=0){
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "Posix Thread attribute destroy failed with: " <<
strerror(status) << std::endl;
sif::error << "Posix Thread attribute destroy failed with: " <<
strerror(status) << std::endl;
#endif
}
}
}

View File

@ -2,6 +2,7 @@
#include "PeriodicPosixTask.h"
#include "../../tasks/TaskFactory.h"
#include "../../serviceinterface/ServiceInterface.h"
#include "../../returnvalues/HasReturnvaluesIF.h"
//TODO: Different variant than the lazy loading in QueueFactory. What's better and why?

View File

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

View File

@ -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_ */

View File

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

View File

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

View File

@ -1,7 +1,7 @@
#include "RtemsBasic.h"
#include "../../timemanager/Clock.h"
#include "../../ipc/MutexHelper.h"
#include "../../ipc/MutexGuard.h"
#include <rtems/score/todimpl.h>
#include <rtems/rtems/clockimpl.h>
@ -183,7 +183,7 @@ ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) {
if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
MutexGuard helper(timeMutex);
leapSeconds = leapSeconds_;
@ -196,7 +196,7 @@ ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) {
if(timeMutex==nullptr){
return HasReturnvaluesIF::RETURN_FAILED;
}
MutexHelper helper(timeMutex);
MutexGuard helper(timeMutex);
*leapSeconds_ = leapSeconds;

View File

@ -61,7 +61,7 @@ ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) {
} else {
//No message was received. Keep lastPartner anyway, I might send something later.
//But still, delete packet content.
memset(message->getData(), 0, message->getMaximumMessageSize());
memset(message->getData(), 0, message->getMaximumDataSize());
}
return convertReturnCode(status);
}

View File

@ -1,11 +1,4 @@
target_sources(${LIB_FSFW_NAME}
PRIVATE
TcWinUdpPollingTask.cpp
TmTcWinUdpBridge.cpp
target_sources(${LIB_FSFW_NAME} PRIVATE
tcpipHelpers.cpp
winTaskHelpers.cpp
)
target_link_libraries(${LIB_FSFW_NAME}
PRIVATE
wsock32
ws2_32
)

View File

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