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

This commit is contained in:
Robin Mueller 2022-03-08 13:47:52 +01:00
commit 7ddfc21030
No known key found for this signature in database
GPG Key ID: 11D4952C8CCEF814
45 changed files with 511 additions and 273 deletions

View File

@ -8,6 +8,22 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [unreleased] # [unreleased]
# [v5.0.0]
## Changes
- HAL Devicehandlers: Periodic printout is run-time configurable now
- `oneShotAction` flag in the `TestTask` class is not static anymore
## Removed
- Removed the `HkSwitchHelper`. This module should not be needed anymore, now that the local
datapools have been implemented
## Additions
- Linux HAL: Add wiretapping option for I2C. Enabled with `FSFW_HAL_I2C_WIRETAPPING` defined to 1
# [v4.0.0] # [v4.0.0]
## Additions ## Additions

View File

@ -281,6 +281,24 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-Wimplicit-fallthrough=1 -Wimplicit-fallthrough=1
-Wno-unused-parameter -Wno-unused-parameter
-Wno-psabi -Wno-psabi
-Wduplicated-cond # check for duplicate conditions
-Wduplicated-branches # check for duplicate branches
-Wlogical-op # Search for bitwise operations instead of logical
-Wnull-dereference # Search for NULL dereference
-Wundef # Warn if undefind marcos are used
-Wformat=2 # Format string problem detection
-Wformat-overflow=2 # Formatting issues in printf
-Wformat-truncation=2 # Formatting issues in printf
-Wformat-security # Search for dangerous printf operations
-Wstrict-overflow=3 # Warn if integer overflows might happen
-Warray-bounds=2 # Some array bounds violations will be found
-Wshift-overflow=2 # Search for bit left shift overflows (<c++14)
-Wcast-qual # Warn if the constness is cast away
-Wstringop-overflow=4
# -Wstack-protector # Emits a few false positives for low level access
# -Wconversion # Creates many false positives
# -Warith-conversion # Use with Wconversion to find more implicit conversions
# -fanalyzer # Should be used to look through problems
) )
endif() endif()

View File

@ -71,9 +71,9 @@ add and link against the FSFW library in general.
4. Link against the FSFW library 4. Link against the FSFW library
```cmake ```cmake
target_link_libraries(<YourProjectName> PRIVATE fsfw) target_link_libraries(${YourProjectName} PRIVATE fsfw)
``` ```
5. It should now be possible use the FSFW as a static library from the user code. 5. It should now be possible use the FSFW as a static library from the user code.

View File

@ -21,14 +21,14 @@ pipeline {
stage('Build') { stage('Build') {
steps { steps {
dir(BUILDDIR) { dir(BUILDDIR) {
sh 'cmake --build . -j' sh 'cmake --build . -j4'
} }
} }
} }
stage('Unittests') { stage('Unittests') {
steps { steps {
dir(BUILDDIR) { dir(BUILDDIR) {
sh 'cmake --build . -- fsfw-tests_coverage -j' sh 'cmake --build . -- fsfw-tests_coverage -j4'
} }
} }
} }

View File

@ -8,11 +8,7 @@ GyroHandlerL3GD20H::GyroHandlerL3GD20H(object_id_t objectId, object_id_t deviceC
CookieIF *comCookie, uint32_t transitionDelayMs) CookieIF *comCookie, uint32_t transitionDelayMs)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie), : DeviceHandlerBase(objectId, deviceCommunication, comCookie),
transitionDelayMs(transitionDelayMs), transitionDelayMs(transitionDelayMs),
dataset(this) { dataset(this) {}
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1
debugDivider = new PeriodicOperationDivider(10);
#endif
}
GyroHandlerL3GD20H::~GyroHandlerL3GD20H() {} GyroHandlerL3GD20H::~GyroHandlerL3GD20H() {}
@ -193,22 +189,22 @@ ReturnValue_t GyroHandlerL3GD20H::interpretDeviceReply(DeviceCommandId_t id,
int8_t temperaturOffset = (-1) * packet[L3GD20H::TEMPERATURE_IDX]; int8_t temperaturOffset = (-1) * packet[L3GD20H::TEMPERATURE_IDX];
float temperature = 25.0 + temperaturOffset; float temperature = 25.0 + temperaturOffset;
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1 if (periodicPrintout) {
if (debugDivider->checkAndIncrement()) { if (debugDivider.checkAndIncrement()) {
/* Set terminal to utf-8 if there is an issue with micro printout. */ /* Set terminal to utf-8 if there is an issue with micro printout. */
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "GyroHandlerL3GD20H: Angular velocities (deg/s):" << std::endl; sif::info << "GyroHandlerL3GD20H: Angular velocities (deg/s):" << std::endl;
sif::info << "X: " << angVelocX << std::endl; sif::info << "X: " << angVelocX << std::endl;
sif::info << "Y: " << angVelocY << std::endl; sif::info << "Y: " << angVelocY << std::endl;
sif::info << "Z: " << angVelocZ << std::endl; sif::info << "Z: " << angVelocZ << std::endl;
#else #else
sif::printInfo("GyroHandlerL3GD20H: Angular velocities (deg/s):\n"); sif::printInfo("GyroHandlerL3GD20H: Angular velocities (deg/s):\n");
sif::printInfo("X: %f\n", angVelocX); sif::printInfo("X: %f\n", angVelocX);
sif::printInfo("Y: %f\n", angVelocY); sif::printInfo("Y: %f\n", angVelocY);
sif::printInfo("Z: %f\n", angVelocZ); sif::printInfo("Z: %f\n", angVelocZ);
#endif #endif
}
} }
#endif
PoolReadGuard readSet(&dataset); PoolReadGuard readSet(&dataset);
if (readSet.getReadResult() == HasReturnvaluesIF::RETURN_OK) { if (readSet.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
@ -272,3 +268,8 @@ void GyroHandlerL3GD20H::setAbsoluteLimits(float limitX, float limitY, float lim
this->absLimitY = limitY; this->absLimitY = limitY;
this->absLimitZ = limitZ; this->absLimitZ = limitZ;
} }
void GyroHandlerL3GD20H::enablePeriodicPrintouts(bool enable, uint8_t divider) {
periodicPrintout = enable;
debugDivider.setDivider(divider);
}

View File

@ -5,7 +5,6 @@
#include <fsfw/globalfunctions/PeriodicOperationDivider.h> #include <fsfw/globalfunctions/PeriodicOperationDivider.h>
#include "devicedefinitions/GyroL3GD20Definitions.h" #include "devicedefinitions/GyroL3GD20Definitions.h"
#include "fsfw/FSFW.h"
/** /**
* @brief Device Handler for the L3GD20H gyroscope sensor * @brief Device Handler for the L3GD20H gyroscope sensor
@ -22,6 +21,8 @@ class GyroHandlerL3GD20H : public DeviceHandlerBase {
uint32_t transitionDelayMs); uint32_t transitionDelayMs);
virtual ~GyroHandlerL3GD20H(); virtual ~GyroHandlerL3GD20H();
void enablePeriodicPrintouts(bool enable, uint8_t divider);
/** /**
* Set the absolute limit for the values on the axis in degrees per second. * Set the absolute limit for the values on the axis in degrees per second.
* The dataset values will be marked as invalid if that limit is exceeded * The dataset values will be marked as invalid if that limit is exceeded
@ -80,9 +81,8 @@ class GyroHandlerL3GD20H : public DeviceHandlerBase {
// Set default value // Set default value
float sensitivity = L3GD20H::SENSITIVITY_00; float sensitivity = L3GD20H::SENSITIVITY_00;
#if FSFW_HAL_L3GD20_GYRO_DEBUG == 1 bool periodicPrintout = false;
PeriodicOperationDivider *debugDivider = nullptr; PeriodicOperationDivider debugDivider = PeriodicOperationDivider(3);
#endif
}; };
#endif /* MISSION_DEVICES_GYROL3GD20HANDLER_H_ */ #endif /* MISSION_DEVICES_GYROL3GD20HANDLER_H_ */

View File

@ -12,9 +12,6 @@ MgmLIS3MDLHandler::MgmLIS3MDLHandler(object_id_t objectId, object_id_t deviceCom
: DeviceHandlerBase(objectId, deviceCommunication, comCookie), : DeviceHandlerBase(objectId, deviceCommunication, comCookie),
dataset(this), dataset(this),
transitionDelay(transitionDelay) { transitionDelay(transitionDelay) {
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1
debugDivider = new PeriodicOperationDivider(10);
#endif
// Set to default values right away // Set to default values right away
registers[0] = MGMLIS3MDL::CTRL_REG1_DEFAULT; registers[0] = MGMLIS3MDL::CTRL_REG1_DEFAULT;
registers[1] = MGMLIS3MDL::CTRL_REG2_DEFAULT; registers[1] = MGMLIS3MDL::CTRL_REG2_DEFAULT;
@ -272,23 +269,24 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
float mgmZ = static_cast<float>(mgmMeasurementRawZ) * sensitivityFactor * float mgmZ = static_cast<float>(mgmMeasurementRawZ) * sensitivityFactor *
MGMLIS3MDL::GAUSS_TO_MICROTESLA_FACTOR; MGMLIS3MDL::GAUSS_TO_MICROTESLA_FACTOR;
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1 if (periodicPrintout) {
if (debugDivider->checkAndIncrement()) { if (debugDivider.checkAndIncrement()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MGMHandlerLIS3: Magnetic field strength in" sif::info << "MGMHandlerLIS3: Magnetic field strength in"
" microtesla:" " microtesla:"
<< std::endl; << std::endl;
sif::info << "X: " << mgmX << " uT" << std::endl; sif::info << "X: " << mgmX << " uT" << std::endl;
sif::info << "Y: " << mgmY << " uT" << std::endl; sif::info << "Y: " << mgmY << " uT" << std::endl;
sif::info << "Z: " << mgmZ << " uT" << std::endl; sif::info << "Z: " << mgmZ << " uT" << std::endl;
#else #else
sif::printInfo("MGMHandlerLIS3: Magnetic field strength in microtesla:\n"); sif::printInfo("MGMHandlerLIS3: Magnetic field strength in microtesla:\n");
sif::printInfo("X: %f uT\n", mgmX); sif::printInfo("X: %f uT\n", mgmX);
sif::printInfo("Y: %f uT\n", mgmY); sif::printInfo("Y: %f uT\n", mgmY);
sif::printInfo("Z: %f uT\n", mgmZ); sif::printInfo("Z: %f uT\n", mgmZ);
#endif /* FSFW_CPP_OSTREAM_ENABLED == 0 */ #endif /* FSFW_CPP_OSTREAM_ENABLED == 0 */
}
} }
#endif /* OBSW_VERBOSE_LEVEL >= 1 */
PoolReadGuard readHelper(&dataset); PoolReadGuard readHelper(&dataset);
if (readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) { if (readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) {
if (std::abs(mgmX) < absLimitX) { if (std::abs(mgmX) < absLimitX) {
@ -318,15 +316,16 @@ ReturnValue_t MgmLIS3MDLHandler::interpretDeviceReply(DeviceCommandId_t id, cons
case MGMLIS3MDL::READ_TEMPERATURE: { case MGMLIS3MDL::READ_TEMPERATURE: {
int16_t tempValueRaw = packet[2] << 8 | packet[1]; int16_t tempValueRaw = packet[2] << 8 | packet[1];
float tempValue = 25.0 + ((static_cast<float>(tempValueRaw)) / 8.0); float tempValue = 25.0 + ((static_cast<float>(tempValueRaw)) / 8.0);
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1 if (periodicPrintout) {
if (debugDivider->check()) { if (debugDivider.check()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MGMHandlerLIS3: Temperature: " << tempValue << " C" << std::endl; sif::info << "MGMHandlerLIS3: Temperature: " << tempValue << " C" << std::endl;
#else #else
sif::printInfo("MGMHandlerLIS3: Temperature: %f C\n"); sif::printInfo("MGMHandlerLIS3: Temperature: %f C\n");
#endif #endif
}
} }
#endif
ReturnValue_t result = dataset.read(); ReturnValue_t result = dataset.read();
if (result == HasReturnvaluesIF::RETURN_OK) { if (result == HasReturnvaluesIF::RETURN_OK) {
dataset.temperature = tempValue; dataset.temperature = tempValue;
@ -484,3 +483,8 @@ void MgmLIS3MDLHandler::setAbsoluteLimits(float xLimit, float yLimit, float zLim
this->absLimitY = yLimit; this->absLimitY = yLimit;
this->absLimitZ = zLimit; this->absLimitZ = zLimit;
} }
void MgmLIS3MDLHandler::enablePeriodicPrintouts(bool enable, uint8_t divider) {
periodicPrintout = enable;
debugDivider.setDivider(divider);
}

View File

@ -3,8 +3,8 @@
#include "devicedefinitions/MgmLIS3HandlerDefs.h" #include "devicedefinitions/MgmLIS3HandlerDefs.h"
#include "events/subsystemIdRanges.h" #include "events/subsystemIdRanges.h"
#include "fsfw/FSFW.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h" #include "fsfw/devicehandlers/DeviceHandlerBase.h"
#include "fsfw/globalfunctions/PeriodicOperationDivider.h"
class PeriodicOperationDivider; class PeriodicOperationDivider;
@ -30,6 +30,7 @@ class MgmLIS3MDLHandler : public DeviceHandlerBase {
uint32_t transitionDelay); uint32_t transitionDelay);
virtual ~MgmLIS3MDLHandler(); virtual ~MgmLIS3MDLHandler();
void enablePeriodicPrintouts(bool enable, uint8_t divider);
/** /**
* Set the absolute limit for the values on the axis in microtesla. The dataset values will * Set the absolute limit for the values on the axis in microtesla. The dataset values will
* be marked as invalid if that limit is exceeded * be marked as invalid if that limit is exceeded
@ -167,9 +168,8 @@ class MgmLIS3MDLHandler : public DeviceHandlerBase {
*/ */
ReturnValue_t prepareCtrlRegisterWrite(); ReturnValue_t prepareCtrlRegisterWrite();
#if FSFW_HAL_LIS3MDL_MGM_DEBUG == 1 bool periodicPrintout = false;
PeriodicOperationDivider *debugDivider; PeriodicOperationDivider debugDivider = PeriodicOperationDivider(3);
#endif
}; };
#endif /* MISSION_DEVICES_MGMLIS3MDLHANDLER_H_ */ #endif /* MISSION_DEVICES_MGMLIS3MDLHANDLER_H_ */

View File

@ -10,11 +10,7 @@ MgmRM3100Handler::MgmRM3100Handler(object_id_t objectId, object_id_t deviceCommu
CookieIF *comCookie, uint32_t transitionDelay) CookieIF *comCookie, uint32_t transitionDelay)
: DeviceHandlerBase(objectId, deviceCommunication, comCookie), : DeviceHandlerBase(objectId, deviceCommunication, comCookie),
primaryDataset(this), primaryDataset(this),
transitionDelay(transitionDelay) { transitionDelay(transitionDelay) {}
#if FSFW_HAL_RM3100_MGM_DEBUG == 1
debugDivider = new PeriodicOperationDivider(10);
#endif
}
MgmRM3100Handler::~MgmRM3100Handler() {} MgmRM3100Handler::~MgmRM3100Handler() {}
@ -337,23 +333,23 @@ ReturnValue_t MgmRM3100Handler::handleDataReadout(const uint8_t *packet) {
float fieldStrengthY = fieldStrengthRawY * scaleFactorX; float fieldStrengthY = fieldStrengthRawY * scaleFactorX;
float fieldStrengthZ = fieldStrengthRawZ * scaleFactorX; float fieldStrengthZ = fieldStrengthRawZ * scaleFactorX;
#if FSFW_HAL_RM3100_MGM_DEBUG == 1 if (periodicPrintout) {
if (debugDivider->checkAndIncrement()) { if (debugDivider.checkAndIncrement()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << "MgmRM3100Handler: Magnetic field strength in" sif::info << "MgmRM3100Handler: Magnetic field strength in"
" microtesla:" " microtesla:"
<< std::endl; << std::endl;
sif::info << "X: " << fieldStrengthX << " uT" << std::endl; sif::info << "X: " << fieldStrengthX << " uT" << std::endl;
sif::info << "Y: " << fieldStrengthY << " uT" << std::endl; sif::info << "Y: " << fieldStrengthY << " uT" << std::endl;
sif::info << "Z: " << fieldStrengthZ << " uT" << std::endl; sif::info << "Z: " << fieldStrengthZ << " uT" << std::endl;
#else #else
sif::printInfo("MgmRM3100Handler: Magnetic field strength in microtesla:\n"); sif::printInfo("MgmRM3100Handler: Magnetic field strength in microtesla:\n");
sif::printInfo("X: %f uT\n", fieldStrengthX); sif::printInfo("X: %f uT\n", fieldStrengthX);
sif::printInfo("Y: %f uT\n", fieldStrengthY); sif::printInfo("Y: %f uT\n", fieldStrengthY);
sif::printInfo("Z: %f uT\n", fieldStrengthZ); sif::printInfo("Z: %f uT\n", fieldStrengthZ);
#endif #endif
}
} }
#endif
// TODO: Sanity check on values? // TODO: Sanity check on values?
PoolReadGuard readGuard(&primaryDataset); PoolReadGuard readGuard(&primaryDataset);
@ -365,3 +361,8 @@ ReturnValue_t MgmRM3100Handler::handleDataReadout(const uint8_t *packet) {
} }
return RETURN_OK; return RETURN_OK;
} }
void MgmRM3100Handler::enablePeriodicPrintouts(bool enable, uint8_t divider) {
periodicPrintout = enable;
debugDivider.setDivider(divider);
}

View File

@ -2,12 +2,8 @@
#define MISSION_DEVICES_MGMRM3100HANDLER_H_ #define MISSION_DEVICES_MGMRM3100HANDLER_H_
#include "devicedefinitions/MgmRM3100HandlerDefs.h" #include "devicedefinitions/MgmRM3100HandlerDefs.h"
#include "fsfw/FSFW.h"
#include "fsfw/devicehandlers/DeviceHandlerBase.h" #include "fsfw/devicehandlers/DeviceHandlerBase.h"
#if FSFW_HAL_RM3100_MGM_DEBUG == 1
#include "fsfw/globalfunctions/PeriodicOperationDivider.h" #include "fsfw/globalfunctions/PeriodicOperationDivider.h"
#endif
/** /**
* @brief Device Handler for the RM3100 geomagnetic magnetometer sensor * @brief Device Handler for the RM3100 geomagnetic magnetometer sensor
@ -33,6 +29,7 @@ class MgmRM3100Handler : public DeviceHandlerBase {
uint32_t transitionDelay); uint32_t transitionDelay);
virtual ~MgmRM3100Handler(); virtual ~MgmRM3100Handler();
void enablePeriodicPrintouts(bool enable, uint8_t divider);
/** /**
* Configure device handler to go to normal mode after startup immediately * Configure device handler to go to normal mode after startup immediately
* @param enable * @param enable
@ -98,9 +95,9 @@ class MgmRM3100Handler : public DeviceHandlerBase {
size_t commandDataLen); size_t commandDataLen);
ReturnValue_t handleDataReadout(const uint8_t *packet); ReturnValue_t handleDataReadout(const uint8_t *packet);
#if FSFW_HAL_RM3100_MGM_DEBUG == 1
PeriodicOperationDivider *debugDivider; bool periodicPrintout = false;
#endif PeriodicOperationDivider debugDivider = PeriodicOperationDivider(3);
}; };
#endif /* MISSION_DEVICEHANDLING_MGMRM3100HANDLER_H_ */ #endif /* MISSION_DEVICEHANDLING_MGMRM3100HANDLER_H_ */

View File

@ -471,7 +471,7 @@ ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie& uartCookie, UartDevi
auto bufferPtr = iter->second.replyBuffer.data(); auto bufferPtr = iter->second.replyBuffer.data();
// Size check to prevent buffer overflow // Size check to prevent buffer overflow
if (requestLen > uartCookie.getMaxReplyLen()) { if (requestLen > uartCookie.getMaxReplyLen()) {
#if OBSW_VERBOSE_LEVEL >= 1 #if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::requestReceiveMessage: Next read would cause overflow!" sif::warning << "UartComIF::requestReceiveMessage: Next read would cause overflow!"
<< std::endl; << std::endl;

View File

@ -97,11 +97,11 @@ def handle_docs_type(args, build_dir_list: list):
build_directory = determine_build_dir(build_dir_list) build_directory = determine_build_dir(build_dir_list)
os.chdir(build_directory) os.chdir(build_directory)
if args.build: if args.build:
os.system("cmake --build . -j") cmd_runner("cmake --build . -j")
if args.open: if args.open:
if not os.path.isfile("docs/sphinx/index.html"): if not os.path.isfile("docs/sphinx/index.html"):
# try again.. # try again..
os.system("cmake --build . -j") cmd_runner("cmake --build . -j")
if not os.path.isfile("docs/sphinx/index.html"): if not os.path.isfile("docs/sphinx/index.html"):
print( print(
"No Sphinx documentation file detected. " "No Sphinx documentation file detected. "
@ -143,25 +143,21 @@ def handle_tests_type(args, build_dir_list: list):
if which("valgrind") is None: if which("valgrind") is None:
print("Please install valgrind first") print("Please install valgrind first")
sys.exit(1) sys.exit(1)
if os.path.split(os.getcwd())[1] != UNITTEST_FOLDER_NAME: cmd_runner("valgrind --leak-check=full ./fsfw-tests")
# If we are in a different directory we try to switch into it but
# this might fail
os.chdir(UNITTEST_FOLDER_NAME)
os.system("valgrind --leak-check=full ./fsfw-tests")
os.chdir("..") os.chdir("..")
def create_tests_build_cfg(): def create_tests_build_cfg():
os.mkdir(UNITTEST_FOLDER_NAME) os.mkdir(UNITTEST_FOLDER_NAME)
os.chdir(UNITTEST_FOLDER_NAME) os.chdir(UNITTEST_FOLDER_NAME)
os.system("cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..") cmd_runner("cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..")
os.chdir("..") os.chdir("..")
def create_docs_build_cfg(): def create_docs_build_cfg():
os.mkdir(DOCS_FOLDER_NAME) os.mkdir(DOCS_FOLDER_NAME)
os.chdir(DOCS_FOLDER_NAME) os.chdir(DOCS_FOLDER_NAME)
os.system("cmake -DFSFW_OSAL=host -DFSFW_BUILD_DOCS=ON ..") cmd_runner("cmake -DFSFW_OSAL=host -DFSFW_BUILD_DOCS=ON ..")
os.chdir("..") os.chdir("..")
@ -184,7 +180,7 @@ def check_for_cmake_build_dir(build_dir_list: list) -> list:
def perform_lcov_operation(directory: str, chdir: bool): def perform_lcov_operation(directory: str, chdir: bool):
if chdir: if chdir:
os.chdir(directory) os.chdir(directory)
os.system("cmake --build . -- fsfw-tests_coverage -j") cmd_runner("cmake --build . -- fsfw-tests_coverage -j")
def determine_build_dir(build_dir_list: List[str]): def determine_build_dir(build_dir_list: List[str]):
@ -206,5 +202,10 @@ def determine_build_dir(build_dir_list: List[str]):
return build_directory return build_directory
def cmd_runner(cmd: str):
print(f"Executing command: {cmd}")
os.system(cmd)
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -62,16 +62,4 @@
#define FSFW_HAL_I2C_WIRETAPPING 0 #define FSFW_HAL_I2C_WIRETAPPING 0
#endif #endif
#ifndef FSFW_HAL_L3GD20_GYRO_DEBUG
#define FSFW_HAL_L3GD20_GYRO_DEBUG 0
#endif /* FSFW_HAL_L3GD20_GYRO_DEBUG */
#ifndef FSFW_HAL_RM3100_MGM_DEBUG
#define FSFW_HAL_RM3100_MGM_DEBUG 0
#endif /* FSFW_HAL_RM3100_MGM_DEBUG */
#ifndef FSFW_HAL_LIS3MDL_MGM_DEBUG
#define FSFW_HAL_LIS3MDL_MGM_DEBUG 0
#endif /* FSFW_HAL_LIS3MDL_MGM_DEBUG */
#endif /* FSFW_FSFW_H_ */ #endif /* FSFW_FSFW_H_ */

View File

@ -6,10 +6,13 @@
#include <fsfw/cfdp/tlv/Tlv.h> #include <fsfw/cfdp/tlv/Tlv.h>
#include <fsfw/cfdp/tlv/TlvIF.h> #include <fsfw/cfdp/tlv/TlvIF.h>
#include <fsfw/serialize/SerializeIF.h> #include <fsfw/serialize/SerializeIF.h>
#include <fsfw/serviceinterface/ServiceInterface.h>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include "fsfw/FSFW.h"
namespace cfdp { namespace cfdp {
enum FilestoreActionCode { enum FilestoreActionCode {

View File

@ -1,4 +1,4 @@
target_sources(${LIB_FSFW_NAME} PRIVATE target_sources(${LIB_FSFW_NAME} PRIVATE
PoolDataSetBase.cpp PoolDataSetBase.cpp
PoolEntry.cpp PoolEntry.cpp
) )

View File

@ -84,8 +84,8 @@ ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() {
return result; return result;
} }
printWarningOrError(sif::OutputTypes::OUT_WARNING, "initialize", HasReturnvaluesIF::RETURN_FAILED, printWarningOrError(sif::OutputTypes::OUT_WARNING, "initializeHousekeepingPoolEntriesOnce",
"The map should only be initialized once"); HasReturnvaluesIF::RETURN_FAILED, "The map should only be initialized once");
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }

View File

@ -824,7 +824,7 @@ void DeviceHandlerBase::handleReply(const uint8_t* receivedData, DeviceCommandId
} }
ReturnValue_t DeviceHandlerBase::getStorageData(store_address_t storageAddress, uint8_t** data, ReturnValue_t DeviceHandlerBase::getStorageData(store_address_t storageAddress, uint8_t** data,
uint32_t* len) { size_t* len) {
size_t lenTmp; size_t lenTmp;
if (IPCStore == nullptr) { if (IPCStore == nullptr) {

View File

@ -673,7 +673,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
//! Pointer to the raw packet that will be sent. //! Pointer to the raw packet that will be sent.
uint8_t *rawPacket = nullptr; uint8_t *rawPacket = nullptr;
//! Size of the #rawPacket. //! Size of the #rawPacket.
uint32_t rawPacketLen = 0; size_t rawPacketLen = 0;
/** /**
* The mode the device handler is currently in. * The mode the device handler is currently in.
@ -1250,7 +1250,7 @@ class DeviceHandlerBase : public DeviceHandlerIF,
* - @c RETURN_FAILED IPCStore is nullptr * - @c RETURN_FAILED IPCStore is nullptr
* - the return value from the IPCStore if it was not @c RETURN_OK * - the return value from the IPCStore if it was not @c RETURN_OK
*/ */
ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, uint32_t *len); ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, size_t *len);
/** /**
* @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW, nothing else! * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW, nothing else!

View File

@ -28,7 +28,7 @@ const uint16_t CRC::crc16ccitt_table[256] = {
// CRC implementation // CRC implementation
uint16_t CRC::crc16ccitt(uint8_t const input[], uint32_t length, uint16_t startingCrc) { uint16_t CRC::crc16ccitt(uint8_t const input[], uint32_t length, uint16_t startingCrc) {
uint8_t *data = (uint8_t *)input; const uint8_t *data = static_cast<const uint8_t *>(input);
unsigned int tbl_idx; unsigned int tbl_idx;
while (length--) { while (length--) {
@ -39,88 +39,4 @@ uint16_t CRC::crc16ccitt(uint8_t const input[], uint32_t length, uint16_t starti
} }
return startingCrc & 0xffff; return startingCrc & 0xffff;
// The part below is not used!
// bool temr[16];
// bool xor_out[16];
// bool r[16];
// bool d[8];
// uint16_t crc_value = 0;
//
//
// for (int i=0; i<16 ;i++) {
// temr[i] = false;
// xor_out[i] = false;
// }
//
//
// for (int i=0; i<16 ;i++)
// r[i] = true; // initialize with 0xFFFF
//
//
//
// for (int j=0; j<length ;j++)
// {
//
// for (int i=0; i<8 ;i++)
// if ((input[j] & 1<<i) == 1<<i)
// d[7-i]=true; // reverse input data
// else
// d[7-i]=false; // reverse input data
//
//
//
// temr[0] = d[4] ^ d[0];
// temr[1] = d[5] ^ d[1];
// temr[2] = d[6] ^ d[2];
// temr[3] = d[7] ^ d[3];
// temr[4] = r[12] ^ r[8];
// temr[5] = r[13] ^ r[9];
// temr[6] = r[14] ^ r[10];
// temr[7] = r[15] ^ r[11];
// temr[8] = d[4] ^ r[12];
// temr[9] = d[5] ^ r[13];
// temr[10] = d[6] ^ r[14];
// temr[11] = d[7] ^ r[15];
// temr[12] = temr[0] ^ temr[4];
// temr[13] = temr[1] ^ temr[5];
// temr[14] = temr[2] ^ temr[6];
// temr[15] = temr[3] ^ temr[7];
//
//
// xor_out[0] = temr[12];
// xor_out[1] = temr[13];
// xor_out[2] = temr[14];
// xor_out[3] = temr[15];
// xor_out[4] = temr[8];
// xor_out[5] = temr[9] ^ temr[12];
// xor_out[6] = temr[10] ^ temr[13];
// xor_out[7] = temr[11] ^ temr[14];
// xor_out[8] = temr[15] ^ r[0];
// xor_out[9] = temr[8] ^ r[1];
// xor_out[10] = temr[9] ^ r[2];
// xor_out[11] = temr[10] ^ r[3];
// xor_out[12] = temr[11] ^ temr[12] ^ r[4];
// xor_out[13] = temr[13] ^ r[5];
// xor_out[14] = temr[14] ^ r[6];
// xor_out[15] = temr[15] ^ r[7];
//
// for (int i=0; i<16 ;i++)
// {
// r[i]= xor_out[i] ;
// }
//
// }
//
//
//
// for (int i=0; i<16 ;i++)
// {
// if (xor_out[i] == true)
// crc_value = crc_value + pow(2,(15 -i)); // reverse CrC result before
//Final XOR
// }
//
// crc_value = 0;// for debug mode
// return (crc_value);
} /* Calculate_CRC() */ } /* Calculate_CRC() */

View File

@ -1,6 +1,7 @@
#include "fsfw/globalfunctions/arrayprinter.h" #include "fsfw/globalfunctions/arrayprinter.h"
#include <bitset> #include <bitset>
#include <cmath>
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface/ServiceInterface.h"
@ -68,7 +69,7 @@ void arrayprinter::printHex(const uint8_t *data, size_t size, size_t maxCharPerL
currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]); currentPos += snprintf(printBuffer + currentPos, 6, "%02x", data[i]);
if (i < size - 1) { if (i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ","); currentPos += sprintf(printBuffer + currentPos, ",");
if (i > 0 and (i + 1) % maxCharPerLine == 0) { if ((i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n"); currentPos += sprintf(printBuffer + currentPos, "\n");
} }
} }
@ -97,20 +98,21 @@ void arrayprinter::printDec(const uint8_t *data, size_t size, size_t maxCharPerL
} }
std::cout << "]" << std::endl; std::cout << "]" << std::endl;
#else #else
// General format: 32, 243, -12 so it is number of chars times 5 // General format: 32,243,-12 so it is number of chars times 4
// plus line break plus small safety margin. // plus line break plus small safety margin.
char printBuffer[(size + 1) * 5 + 1] = {}; uint16_t expectedLines = ceil((double)size / maxCharPerLine);
char printBuffer[size * 4 + 1 + expectedLines] = {};
size_t currentPos = 0; size_t currentPos = 0;
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
// To avoid buffer overflows. // To avoid buffer overflows.
if (sizeof(printBuffer) - currentPos <= 5) { if (sizeof(printBuffer) - currentPos <= 4) {
break; break;
} }
currentPos += snprintf(printBuffer + currentPos, 3, "%d", data[i]); currentPos += snprintf(printBuffer + currentPos, 4, "%d", data[i]);
if (i < size - 1) { if (i < size - 1) {
currentPos += sprintf(printBuffer + currentPos, ","); currentPos += sprintf(printBuffer + currentPos, ",");
if (i > 0 and (i + 1) % maxCharPerLine == 0) { if ((i + 1) % maxCharPerLine == 0) {
currentPos += sprintf(printBuffer + currentPos, "\n"); currentPos += sprintf(printBuffer + currentPos, "\n");
} }
} }

View File

@ -57,6 +57,9 @@ ReturnValue_t InternalErrorReporter::performOperation(uint8_t opCode) {
internalErrorDataset.storeHits.value += newStoreHits; internalErrorDataset.storeHits.value += newStoreHits;
internalErrorDataset.tmHits.value += newTmHits; internalErrorDataset.tmHits.value += newTmHits;
internalErrorDataset.setValidity(true, true); internalErrorDataset.setValidity(true, true);
if ((newQueueHits != 0) or (newStoreHits != 0) or (newTmHits != 0)) {
internalErrorDataset.setChanged(true);
}
} }
} }
@ -77,14 +80,6 @@ uint32_t InternalErrorReporter::getAndResetQueueHits() {
return value; return value;
} }
uint32_t InternalErrorReporter::getQueueHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = queueHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementQueueHits() { void InternalErrorReporter::incrementQueueHits() {
mutex->lockMutex(timeoutType, timeoutMs); mutex->lockMutex(timeoutType, timeoutMs);
queueHits++; queueHits++;
@ -100,14 +95,6 @@ uint32_t InternalErrorReporter::getAndResetTmHits() {
return value; return value;
} }
uint32_t InternalErrorReporter::getTmHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = tmHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementTmHits() { void InternalErrorReporter::incrementTmHits() {
mutex->lockMutex(timeoutType, timeoutMs); mutex->lockMutex(timeoutType, timeoutMs);
tmHits++; tmHits++;
@ -125,14 +112,6 @@ uint32_t InternalErrorReporter::getAndResetStoreHits() {
return value; return value;
} }
uint32_t InternalErrorReporter::getStoreHits() {
uint32_t value;
mutex->lockMutex(timeoutType, timeoutMs);
value = storeHits;
mutex->unlockMutex();
return value;
}
void InternalErrorReporter::incrementStoreHits() { void InternalErrorReporter::incrementStoreHits() {
mutex->lockMutex(timeoutType, timeoutMs); mutex->lockMutex(timeoutType, timeoutMs);
storeHits++; storeHits++;

View File

@ -46,11 +46,11 @@ class InternalErrorReporter : public SystemObject,
virtual ReturnValue_t initializeAfterTaskCreation() override; virtual ReturnValue_t initializeAfterTaskCreation() override;
virtual ReturnValue_t performOperation(uint8_t opCode) override; virtual ReturnValue_t performOperation(uint8_t opCode) override;
virtual void queueMessageNotSent(); virtual void queueMessageNotSent() override;
virtual void lostTm(); virtual void lostTm() override;
virtual void storeFull(); virtual void storeFull() override;
virtual void setTaskIF(PeriodicTaskIF* task) override; virtual void setTaskIF(PeriodicTaskIF* task) override;
@ -74,15 +74,12 @@ class InternalErrorReporter : public SystemObject,
uint32_t storeHits = 0; uint32_t storeHits = 0;
uint32_t getAndResetQueueHits(); uint32_t getAndResetQueueHits();
uint32_t getQueueHits();
void incrementQueueHits(); void incrementQueueHits();
uint32_t getAndResetTmHits(); uint32_t getAndResetTmHits();
uint32_t getTmHits();
void incrementTmHits(); void incrementTmHits();
uint32_t getAndResetStoreHits(); uint32_t getAndResetStoreHits();
uint32_t getStoreHits();
void incrementStoreHits(); void incrementStoreHits();
}; };

View File

@ -1,22 +1,34 @@
#ifndef INTERNALERRORREPORTERIF_H_ #ifndef INTERNALERRORREPORTERIF_H_
#define INTERNALERRORREPORTERIF_H_ #define INTERNALERRORREPORTERIF_H_
/**
* @brief Interface which is used to report internal errors like full message queues or stores.
* @details
* This interface smust be used for the InteralErrorReporter object.
* It should be used to indicate that there was a Problem with Queues or Stores.
*
* It can be used to report missing Telemetry which could not be sent due to a internal problem.
*
*/
class InternalErrorReporterIF { class InternalErrorReporterIF {
public: public:
virtual ~InternalErrorReporterIF() {} virtual ~InternalErrorReporterIF() {}
/** /**
* Thread safe * @brief Function to be called if a message queue could not be sent.
* @details OSAL Implementations should call this function to indicate that
* a message was lost.
*
* Implementations are required to be Thread safe
*/ */
virtual void queueMessageNotSent() = 0; virtual void queueMessageNotSent() = 0;
/** /**
* Thread safe * @brief Function to be called if Telemetry could not be sent
* @details Implementations must be Thread safe
*/ */
virtual void lostTm() = 0; virtual void lostTm() = 0;
/** /**
* Thread safe * @brief Function to be called if a onboard storage is full
* @details Implementations must be Thread safe
*/ */
virtual void storeFull() = 0; virtual void storeFull() = 0;
}; };

View File

@ -125,6 +125,13 @@ ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo,
memcpy(targetQueue->messageQueue.back().data(), message->getBuffer(), memcpy(targetQueue->messageQueue.back().data(), message->getBuffer(),
message->getMaximumMessageSize()); message->getMaximumMessageSize());
} else { } else {
if (not ignoreFault) {
InternalErrorReporterIF* internalErrorReporter =
ObjectManager::instance()->get<InternalErrorReporterIF>(objects::INTERNAL_ERROR_REPORTER);
if (internalErrorReporter != nullptr) {
internalErrorReporter->queueMessageNotSent();
}
}
return MessageQueueIF::FULL; return MessageQueueIF::FULL;
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;

View File

@ -1,6 +1,7 @@
#include "fsfw/osal/linux/BinarySemaphore.h" #include "fsfw/osal/linux/BinarySemaphore.h"
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <time.h> #include <time.h>
#include <cstring> #include <cstring>

View File

@ -65,9 +65,10 @@ class PeriodicPosixTask : public PosixThread, public PeriodicTaskIF {
/** /**
* @brief The function containing the actual functionality of the task. * @brief The function containing the actual functionality of the task.
* @details The method sets and starts * @details The method sets and starts
* the task's period, then enters a loop that is repeated indefinitely. Within the * the task's period, then enters a loop that is repeated indefinitely. Within
* loop, all performOperation methods of the added objects are called. Afterwards the task will be * the loop, all performOperation methods of the added objects are called. Afterwards the task
* blocked until the next period. On missing the deadline, the deadlineMissedFunction is executed. * will be blocked until the next period. On missing the deadline, the deadlineMissedFunction is
* executed.
*/ */
virtual void taskFunctionality(void); virtual void taskFunctionality(void);
/** /**

View File

@ -13,8 +13,8 @@ class ExecutableObjectIF;
* @brief This class represents a specialized task for periodic activities of multiple objects. * @brief This class represents a specialized task for periodic activities of multiple objects.
* *
* @details MultiObjectTask is an extension to ObjectTask in the way that it is able to execute * @details MultiObjectTask is an extension to ObjectTask in the way that it is able to execute
* multiple objects that implement the ExecutableObjectIF interface. The objects * multiple objects that implement the ExecutableObjectIF interface. The
* must be added prior to starting the task. * objects must be added prior to starting the task.
* @author baetz * @author baetz
* @ingroup task_handling * @ingroup task_handling
*/ */

View File

@ -169,8 +169,8 @@ class RMAP : public HasReturnvaluesIF {
* @param buffer the data to write * @param buffer the data to write
* @param length length of data * @param length length of data
* @return * @return
* - @c COMMAND_NULLPOINTER datalen was != 0 but data was == NULL in * - @c COMMAND_NULLPOINTER datalen was != 0 but data was ==
* write command * NULL in write command
* - return codes of RMAPChannelIF::sendCommand() * - return codes of RMAPChannelIF::sendCommand()
*/ */
static ReturnValue_t sendWriteCommand(RMAPCookie *cookie, const uint8_t *buffer, size_t length); static ReturnValue_t sendWriteCommand(RMAPCookie *cookie, const uint8_t *buffer, size_t length);
@ -205,8 +205,8 @@ class RMAP : public HasReturnvaluesIF {
* @param cookie to cookie to read from * @param cookie to cookie to read from
* @param expLength the expected maximum length of the reply * @param expLength the expected maximum length of the reply
* @return * @return
* - @c COMMAND_NULLPOINTER datalen was != 0 but data was == NULL in * - @c COMMAND_NULLPOINTER datalen was != 0 but data was ==
* write command, or nullpointer in read command * NULL in write command, or nullpointer in read command
* - return codes of RMAPChannelIF::sendCommand() * - return codes of RMAPChannelIF::sendCommand()
*/ */
static ReturnValue_t sendReadCommand(RMAPCookie *cookie, uint32_t expLength); static ReturnValue_t sendReadCommand(RMAPCookie *cookie, uint32_t expLength);

View File

@ -75,11 +75,11 @@ class RMAPChannelIF {
* - @c RETURN_OK * - @c RETURN_OK
* - @c COMMAND_NO_DESCRIPTORS_AVAILABLE no descriptors available for sending * - @c COMMAND_NO_DESCRIPTORS_AVAILABLE no descriptors available for sending
* command; command was not sent * command; command was not sent
* - @c COMMAND_BUFFER_FULL no receiver buffer available for expected len; * - @c COMMAND_BUFFER_FULL no receiver buffer available for
* command was not sent * expected len; command was not sent
* - @c COMMAND_TOO_BIG the data that was to be sent was too long for the hw * - @c COMMAND_TOO_BIG the data that was to be sent was too
* to handle (write command) or the expected len was bigger than maximal expected len (read * long for the hw to handle (write command) or the expected len was bigger than maximal expected
* command) command was not sent * len (read command) command was not sent
* - @c COMMAND_CHANNEL_DEACTIVATED the channel has no port set * - @c COMMAND_CHANNEL_DEACTIVATED the channel has no port set
* - @c NOT_SUPPORTED if you dont feel like * - @c NOT_SUPPORTED if you dont feel like
* implementing something... * implementing something...
@ -97,8 +97,8 @@ class RMAPChannelIF {
* - @c REPLY_NO_REPLY no reply was received * - @c REPLY_NO_REPLY no reply was received
* - @c REPLY_NOT_SENT command was not sent, implies no reply * - @c REPLY_NOT_SENT command was not sent, implies no reply
* - @c REPLY_NOT_YET_SENT command is still waiting to be sent * - @c REPLY_NOT_YET_SENT command is still waiting to be sent
* - @c WRITE_REPLY_INTERFACE_BUSY Interface is busy (transmission buffer still * - @c WRITE_REPLY_INTERFACE_BUSY Interface is busy (transmission
* being processed) * buffer still being processed)
* - @c WRITE_REPLY_TRANSMISSION_ERROR Interface encountered errors during last * - @c WRITE_REPLY_TRANSMISSION_ERROR Interface encountered errors during last
* operation, data could not be processed. (transmission error) * operation, data could not be processed. (transmission error)
* - @c WRITE_REPLY_INVALID_DATA Invalid data (amount / value) * - @c WRITE_REPLY_INVALID_DATA Invalid data (amount / value)

View File

@ -1,6 +1,8 @@
#ifndef FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER #ifndef FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER
#define FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER #define FSFW_SERVICEINTERFACE_SERVICEINTERFACEPRINTER
#include "fsfw/FSFW.h"
#if FSFW_DISABLE_PRINTOUT == 0 #if FSFW_DISABLE_PRINTOUT == 0
#include <cstdio> #include <cstdio>
#endif #endif

View File

@ -110,7 +110,7 @@ ReturnValue_t LocalPool::modifyData(store_address_t storeId, uint8_t** packetPtr
} }
ReturnValue_t LocalPool::deleteData(store_address_t storeId) { ReturnValue_t LocalPool::deleteData(store_address_t storeId) {
#if FSFW_VERBOSE_PRINTOUT == 2 #if FSFW_VERBOSE_LEVEL >= 2
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "Delete: Pool: " << std::dec << storeId.poolIndex sif::debug << "Delete: Pool: " << std::dec << storeId.poolIndex
<< " Index: " << storeId.packetIndex << std::endl; << " Index: " << storeId.packetIndex << std::endl;
@ -148,7 +148,7 @@ ReturnValue_t LocalPool::deleteData(uint8_t* ptr, size_t size, store_address_t*
// element of an object. // element of an object.
localId.packetIndex = deltaAddress / elementSizes[n]; localId.packetIndex = deltaAddress / elementSizes[n];
result = deleteData(localId); result = deleteData(localId);
#if FSFW_VERBOSE_PRINTOUT == 2 #if FSFW_VERBOSE_LEVEL >= 2
if (deltaAddress % elementSizes[n] != 0) { if (deltaAddress % elementSizes[n] != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "LocalPool::deleteData: Address not aligned!" << std::endl; sif::error << "LocalPool::deleteData: Address not aligned!" << std::endl;
@ -219,7 +219,7 @@ ReturnValue_t LocalPool::reserveSpace(const size_t size, store_address_t* storeI
status = findEmpty(storeId->poolIndex, &storeId->packetIndex); status = findEmpty(storeId->poolIndex, &storeId->packetIndex);
} }
if (status == RETURN_OK) { if (status == RETURN_OK) {
#if FSFW_VERBOSE_PRINTOUT == 2 #if FSFW_VERBOSE_LEVEL >= 2
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "Reserve: Pool: " << std::dec << storeId->poolIndex sif::debug << "Reserve: Pool: " << std::dec << storeId->poolIndex
<< " Index: " << storeId->packetIndex << std::endl; << " Index: " << storeId->packetIndex << std::endl;
@ -257,7 +257,7 @@ void LocalPool::setToSpillToHigherPools(bool enable) { this->spillsToHigherPools
ReturnValue_t LocalPool::getSubPoolIndex(size_t packetSize, uint16_t* subpoolIndex, ReturnValue_t LocalPool::getSubPoolIndex(size_t packetSize, uint16_t* subpoolIndex,
uint16_t startAtIndex) { uint16_t startAtIndex) {
for (uint16_t n = startAtIndex; n < NUMBER_OF_SUBPOOLS; n++) { for (uint16_t n = startAtIndex; n < NUMBER_OF_SUBPOOLS; n++) {
#if FSFW_VERBOSE_PRINTOUT == 2 #if FSFW_VERBOSE_LEVEL >= 2
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: " << n sif::debug << "LocalPool " << getObjectId() << "::getPoolIndex: Pool: " << n
<< ", Element Size: " << elementSizes[n] << std::endl; << ", Element Size: " << elementSizes[n] << std::endl;

View File

@ -17,7 +17,7 @@ ReturnValue_t PoolManager::reserveSpace(const size_t size, store_address_t* addr
} }
ReturnValue_t PoolManager::deleteData(store_address_t storeId) { ReturnValue_t PoolManager::deleteData(store_address_t storeId) {
#if FSFW_VERBOSE_PRINTOUT == 2 #if FSFW_VERBOSE_LEVEL >= 2
#if FSFW_CPP_OSTREAM_ENABLED == 1 #if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "PoolManager( " << translateObject(getObjectId()) << " )::deleteData from store " sif::debug << "PoolManager( " << translateObject(getObjectId()) << " )::deleteData from store "
<< storeId.poolIndex << ". id is " << storeId.packetIndex << std::endl; << storeId.poolIndex << ". id is " << storeId.packetIndex << std::endl;

View File

@ -109,8 +109,8 @@ ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* f
if (result != RETURN_OK) { if (result != RETURN_OK) {
return result; return result;
} }
// At this point we made sure that this is a valid ccs time
Ccs_mseconds* temp = (Ccs_mseconds*)from; const Ccs_mseconds* temp = reinterpret_cast<const Ccs_mseconds*>(from);
to->year = (temp->yearMSB << 8) + temp->yearLSB; to->year = (temp->yearMSB << 8) + temp->yearLSB;
to->hour = temp->hour; to->hour = temp->hour;
@ -118,16 +118,19 @@ ReturnValue_t CCSDSTime::convertFromCCS(Clock::TimeOfDay_t* to, const uint8_t* f
to->second = temp->second; to->second = temp->second;
if (temp->pField & (1 << 3)) { // day of year variation if (temp->pField & (1 << 3)) { // day of year variation
uint16_t tempDay = (temp->month << 8) + temp->day; uint16_t tempDayOfYear = (temp->month << 8) + temp->day;
result = convertDaysOfYear(tempDay, to->year, &(temp->month), &(temp->day)); uint8_t tempDay = 0;
uint8_t tempMonth = 0;
result = convertDaysOfYear(tempDayOfYear, to->year, &tempMonth, &tempDay);
if (result != RETURN_OK) { if (result != RETURN_OK) {
return result; return result;
} }
to->month = tempMonth;
to->day = tempDay;
} else {
to->month = temp->month;
to->day = temp->day;
} }
to->month = temp->month;
to->day = temp->day;
to->usecond = 0; to->usecond = 0;
if (subsecondsLength > 0) { if (subsecondsLength > 0) {
*foundLength += 1; *foundLength += 1;
@ -162,7 +165,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
uint16_t hour; uint16_t hour;
uint16_t minute; uint16_t minute;
float second; float second;
int count = sscanf((char*)from, int count = sscanf((const char*)from,
"%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16 "%4" SCNu16 "-%2" SCNu16 "-%2" SCNu16
"T%" "T%"
"2" SCNu16 ":%2" SCNu16 ":%fZ", "2" SCNu16 ":%2" SCNu16 ":%fZ",
@ -179,7 +182,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
} }
// try Code B (yyyy-ddd) // try Code B (yyyy-ddd)
count = sscanf((char*)from, count = sscanf((const char*)from,
"%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16 "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu16
":%" ":%"
"2" SCNu16 ":%fZ", "2" SCNu16 ":%fZ",
@ -211,7 +214,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
float second; float second;
// try Code A (yyyy-mm-dd) // try Code A (yyyy-mm-dd)
int count = int count =
sscanf((char*)from, "%4" SCNu16 "-%2" SCNu8 "-%2" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ", sscanf((const char*)from, "%4" SCNu16 "-%2" SCNu8 "-%2" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ",
&year, &month, &day, &hour, &minute, &second); &year, &month, &day, &hour, &minute, &second);
if (count == 6) { if (count == 6) {
to->year = year; to->year = year;
@ -225,8 +228,8 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
} }
// try Code B (yyyy-ddd) // try Code B (yyyy-ddd)
count = sscanf((char*)from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ", &year, &day, count = sscanf((const char*)from, "%4" SCNu16 "-%3" SCNu16 "T%2" SCNu8 ":%2" SCNu8 ":%fZ", &year,
&hour, &minute, &second); &day, &hour, &minute, &second);
if (count == 5) { if (count == 5) {
uint8_t tempDay; uint8_t tempDay;
ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, &month, &tempDay); ReturnValue_t result = CCSDSTime::convertDaysOfYear(day, year, &month, &tempDay);
@ -248,7 +251,7 @@ ReturnValue_t CCSDSTime::convertFromASCII(Clock::TimeOfDay_t* to, const uint8_t*
} }
ReturnValue_t CCSDSTime::checkCcs(const uint8_t* time, uint8_t length) { ReturnValue_t CCSDSTime::checkCcs(const uint8_t* time, uint8_t length) {
Ccs_mseconds* time_struct = (Ccs_mseconds*)time; const Ccs_mseconds* time_struct = reinterpret_cast<const Ccs_mseconds*>(time);
uint8_t additionalBytes = time_struct->pField & 0b111; uint8_t additionalBytes = time_struct->pField & 0b111;
if ((additionalBytes == 0b111) || (length < (additionalBytes + 8))) { if ((additionalBytes == 0b111) || (length < (additionalBytes + 8))) {
@ -278,7 +281,7 @@ ReturnValue_t CCSDSTime::checkCcs(const uint8_t* time, uint8_t length) {
return INVALID_TIME_FORMAT; return INVALID_TIME_FORMAT;
} }
uint8_t* additionalByte = &time_struct->secondEminus2; const uint8_t* additionalByte = &time_struct->secondEminus2;
for (; additionalBytes != 0; additionalBytes--) { for (; additionalBytes != 0; additionalBytes--) {
if (*additionalByte++ > 99) { if (*additionalByte++ > 99) {

View File

@ -3,7 +3,6 @@
#include <fsfw/objectmanager/ObjectManager.h> #include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/serviceinterface/ServiceInterface.h> #include <fsfw/serviceinterface/ServiceInterface.h>
bool TestTask::oneShotAction = true;
MutexIF* TestTask::testLock = nullptr; MutexIF* TestTask::testLock = nullptr;
TestTask::TestTask(object_id_t objectId) : SystemObject(objectId), testMode(testModes::A) { TestTask::TestTask(object_id_t objectId) : SystemObject(objectId), testMode(testModes::A) {

View File

@ -29,7 +29,7 @@ class TestTask : public SystemObject, public ExecutableObjectIF, public HasRetur
bool testFlag = false; bool testFlag = false;
private: private:
static bool oneShotAction; bool oneShotAction = true;
static MutexIF* testLock; static MutexIF* testLock;
StorageManagerIF* IPCStore; StorageManagerIF* IPCStore;
}; };

View File

@ -23,3 +23,4 @@ add_subdirectory(timemanager)
add_subdirectory(tmtcpacket) add_subdirectory(tmtcpacket)
add_subdirectory(cfdp) add_subdirectory(cfdp)
add_subdirectory(hal) add_subdirectory(hal)
add_subdirectory(internalerror)

View File

@ -2,4 +2,5 @@ target_sources(${FSFW_TEST_TGT} PRIVATE
testDleEncoder.cpp testDleEncoder.cpp
testOpDivider.cpp testOpDivider.cpp
testBitutil.cpp testBitutil.cpp
testCRC.cpp
) )

View File

@ -0,0 +1,14 @@
#include <array>
#include "catch2/catch_test_macros.hpp"
#include "fsfw/globalfunctions/CRC.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("CRC", "[CRC]") {
std::array<uint8_t, 10> testData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
uint16_t crc = CRC::crc16ccitt(testData.data(), 10);
REQUIRE(crc == 49729);
for (uint8_t index = 0; index < testData.size(); index++) {
REQUIRE(testData[index] == index);
}
}

View File

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

View File

@ -0,0 +1,118 @@
#include <fsfw/housekeeping/HousekeepingSnapshot.h>
#include <fsfw/internalerror/InternalErrorReporter.h>
#include <fsfw/ipc/MessageQueueIF.h>
#include <fsfw/ipc/QueueFactory.h>
#include <fsfw/objectmanager/ObjectManager.h>
#include <fsfw/timemanager/CCSDSTime.h>
#include <array>
#include <catch2/catch_test_macros.hpp>
#include "fsfw/action/ActionMessage.h"
#include "fsfw/ipc/CommandMessage.h"
#include "fsfw/ipc/MessageQueueMessage.h"
#include "fsfw/objectmanager/frameworkObjects.h"
#include "fsfw_tests/unit/CatchDefinitions.h"
#include "fsfw_tests/unit/mocks/PeriodicTaskIFMock.h"
TEST_CASE("Internal Error Reporter", "[TestInternalError]") {
PeriodicTaskMock task(10);
ObjectManagerIF* manager = ObjectManager::instance();
if (manager == nullptr) {
FAIL();
}
InternalErrorReporter* internalErrorReporter = dynamic_cast<InternalErrorReporter*>(
ObjectManager::instance()->get<InternalErrorReporterIF>(objects::INTERNAL_ERROR_REPORTER));
if (internalErrorReporter == nullptr) {
FAIL();
}
task.addComponent(objects::INTERNAL_ERROR_REPORTER);
MessageQueueIF* testQueue = QueueFactory::instance()->createMessageQueue(1);
MessageQueueIF* hkQueue = QueueFactory::instance()->createMessageQueue(1);
internalErrorReporter->getSubscriptionInterface()->subscribeForSetUpdateMessage(
InternalErrorDataset::ERROR_SET_ID, objects::NO_OBJECT, hkQueue->getId(), true);
StorageManagerIF* ipcStore = ObjectManager::instance()->get<StorageManagerIF>(objects::IPC_STORE);
SECTION("MessageQueueFull") {
CommandMessage message;
ActionMessage::setCompletionReply(&message, 10, true);
auto result = hkQueue->sendMessage(testQueue->getId(), &message);
REQUIRE(result == retval::CATCH_OK);
uint32_t queueHits = 0;
uint32_t lostTm = 0;
uint32_t storeHits = 0;
/* We don't know if another test caused a queue Hit so we will enforce one,
then remeber the queueHit count and force another hit */
internalErrorReporter->queueMessageNotSent();
internalErrorReporter->performOperation(0);
{
CommandMessage hkMessage;
result = hkQueue->receiveMessage(&hkMessage);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(hkMessage.getCommand() == HousekeepingMessage::UPDATE_SNAPSHOT_SET);
store_address_t storeAddress;
gp_id_t gpid =
HousekeepingMessage::getUpdateSnapshotVariableCommand(&hkMessage, &storeAddress);
REQUIRE(gpid.objectId == objects::INTERNAL_ERROR_REPORTER);
// We need the object ID of the reporter here (NO_OBJECT)
InternalErrorDataset dataset(objects::INTERNAL_ERROR_REPORTER);
CCSDSTime::CDS_short time;
ConstAccessorPair data = ipcStore->getData(storeAddress);
REQUIRE(data.first == HasReturnvaluesIF::RETURN_OK);
HousekeepingSnapshot hkSnapshot(&time, &dataset);
const uint8_t* buffer = data.second.data();
size_t size = data.second.size();
result = hkSnapshot.deSerialize(&buffer, &size, SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Remember the amount of queueHits before to see the increase
queueHits = dataset.queueHits.value;
lostTm = dataset.tmHits.value;
storeHits = dataset.storeHits.value;
}
result = hkQueue->sendMessage(testQueue->getId(), &message);
REQUIRE(result == MessageQueueIF::FULL);
internalErrorReporter->lostTm();
internalErrorReporter->storeFull();
{
internalErrorReporter->performOperation(0);
CommandMessage hkMessage;
result = hkQueue->receiveMessage(&hkMessage);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(hkMessage.getCommand() == HousekeepingMessage::UPDATE_SNAPSHOT_SET);
store_address_t storeAddress;
gp_id_t gpid =
HousekeepingMessage::getUpdateSnapshotVariableCommand(&hkMessage, &storeAddress);
REQUIRE(gpid.objectId == objects::INTERNAL_ERROR_REPORTER);
ConstAccessorPair data = ipcStore->getData(storeAddress);
REQUIRE(data.first == HasReturnvaluesIF::RETURN_OK);
CCSDSTime::CDS_short time;
// We need the object ID of the reporter here (NO_OBJECT)
InternalErrorDataset dataset(objects::INTERNAL_ERROR_REPORTER);
HousekeepingSnapshot hkSnapshot(&time, &dataset);
const uint8_t* buffer = data.second.data();
size_t size = data.second.size();
result = hkSnapshot.deSerialize(&buffer, &size, SerializeIF::Endianness::MACHINE);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Test that we had one more queueHit
REQUIRE(dataset.queueHits.value == (queueHits + 1));
REQUIRE(dataset.tmHits.value == (lostTm + 1));
REQUIRE(dataset.storeHits.value == (storeHits + 1));
}
// Complete Coverage
internalErrorReporter->setDiagnosticPrintout(true);
internalErrorReporter->setMutexTimeout(MutexIF::TimeoutType::BLOCKING, 0);
{
// Message Queue Id
MessageQueueId_t id = internalErrorReporter->getCommandQueue();
REQUIRE(id != MessageQueueIF::NO_QUEUE);
CommandMessage message;
sid_t sid(objects::INTERNAL_ERROR_REPORTER, InternalErrorDataset::ERROR_SET_ID);
HousekeepingMessage::setToggleReportingCommand(&message, sid, true, false);
result = hkQueue->sendMessage(id, &message);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
internalErrorReporter->performOperation(0);
}
}
QueueFactory::instance()->deleteMessageQueue(testQueue);
QueueFactory::instance()->deleteMessageQueue(hkQueue);
}

View File

@ -0,0 +1,37 @@
#ifndef FSFW_UNITTEST_TESTS_MOCKS_PERIODICTASKMOCK_H_
#define FSFW_UNITTEST_TESTS_MOCKS_PERIODICTASKMOCK_H_
#include <fsfw/tasks/ExecutableObjectIF.h>
#include <fsfw/tasks/PeriodicTaskIF.h>
class PeriodicTaskMock : public PeriodicTaskIF {
public:
PeriodicTaskMock(uint32_t period = 5) : period(period) {}
/**
* @brief A virtual destructor as it is mandatory for interfaces.
*/
virtual ~PeriodicTaskMock() {}
/**
* @brief With the startTask method, a created task can be started
* for the first time.
*/
virtual ReturnValue_t startTask() override { return HasReturnvaluesIF::RETURN_OK; };
virtual ReturnValue_t addComponent(object_id_t object) override {
ExecutableObjectIF* executableObject =
ObjectManager::instance()->get<ExecutableObjectIF>(objects::INTERNAL_ERROR_REPORTER);
if (executableObject == nullptr) {
return HasReturnvaluesIF::RETURN_FAILED;
}
executableObject->setTaskIF(this);
executableObject->initializeAfterTaskCreation();
return HasReturnvaluesIF::RETURN_OK;
};
virtual ReturnValue_t sleepFor(uint32_t ms) override { return HasReturnvaluesIF::RETURN_OK; };
virtual uint32_t getPeriodMs() const override { return period; };
uint32_t period;
};
#endif // FSFW_UNITTEST_TESTS_MOCKS_PERIODICTASKMOCK_H_

View File

@ -35,4 +35,25 @@ TEST_CASE("MessageQueue Basic Test", "[TestMq]") {
senderId = testReceiverMq->getLastPartner(); senderId = testReceiverMq->getLastPartner();
CHECK(senderId == testSenderMqId); CHECK(senderId == testSenderMqId);
} }
SECTION("Test Full") {
auto result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == retval::CATCH_OK);
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == MessageQueueIF::FULL);
// We try another message
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == MessageQueueIF::FULL);
MessageQueueMessage recvMessage;
result = testReceiverMq->receiveMessage(&recvMessage);
REQUIRE(result == retval::CATCH_OK);
CHECK(recvMessage.getData()[0] == 42);
result = testSenderMq->sendMessage(testReceiverMqId, &testMessage);
REQUIRE(result == retval::CATCH_OK);
result = testReceiverMq->receiveMessage(&recvMessage);
REQUIRE(result == retval::CATCH_OK);
CHECK(recvMessage.getData()[0] == 42);
}
// We have to clear MQs ourself ATM
QueueFactory::instance()->deleteMessageQueue(testSenderMq);
QueueFactory::instance()->deleteMessageQueue(testReceiverMq);
} }

View File

@ -11,10 +11,12 @@
//! More FSFW related printouts depending on level. Useful for development. //! More FSFW related printouts depending on level. Useful for development.
#define FSFW_VERBOSE_LEVEL 0 #define FSFW_VERBOSE_LEVEL 0
//! Can be used to completely disable printouts, even the C stdio ones. //! Can be used to completely disable printouts, even the C stdio ones.
#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0 #if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0
#define FSFW_DISABLE_PRINTOUT 1 #define FSFW_DISABLE_PRINTOUT 1
#else
#define FSFW_DISABLE_PRINTOUT 0
#endif #endif
#define FSFW_USE_PUS_C_TELEMETRY 1 #define FSFW_USE_PUS_C_TELEMETRY 1

View File

@ -1,3 +1,4 @@
target_sources(${FSFW_TEST_TGT} PRIVATE target_sources(${FSFW_TEST_TGT} PRIVATE
TestCountdown.cpp TestCountdown.cpp
TestCCSDSTime.cpp
) )

View File

@ -0,0 +1,92 @@
#include <fsfw/timemanager/CCSDSTime.h>
#include <array>
#include <catch2/catch_approx.hpp>
#include <catch2/catch_test_macros.hpp>
#include "fsfw_tests/unit/CatchDefinitions.h"
TEST_CASE("CCSDSTime Tests", "[TestCCSDSTime]") {
INFO("CCSDSTime Tests");
CCSDSTime::Ccs_mseconds cssMilliSecconds;
Clock::TimeOfDay_t time;
time.year = 2020;
time.month = 2;
time.day = 29;
time.hour = 13;
time.minute = 24;
time.second = 45;
time.usecond = 123456;
SECTION("Test CCS Time") {
auto result = CCSDSTime::convertToCcsds(&cssMilliSecconds, &time);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cssMilliSecconds.pField == 0x52); // 0b01010010
REQUIRE(cssMilliSecconds.yearMSB == 0x07);
REQUIRE(cssMilliSecconds.yearLSB == 0xe4);
REQUIRE(cssMilliSecconds.month == 2);
REQUIRE(cssMilliSecconds.day == 29);
REQUIRE(cssMilliSecconds.hour == 13);
REQUIRE(cssMilliSecconds.minute == 24);
REQUIRE(cssMilliSecconds.second == 45);
uint16_t secondsMinus4 = (static_cast<uint16_t>(cssMilliSecconds.secondEminus2) * 100) +
cssMilliSecconds.secondEminus4;
REQUIRE(secondsMinus4 == 1234);
Clock::TimeOfDay_t timeTo;
const uint8_t* dataPtr = reinterpret_cast<const uint8_t*>(&cssMilliSecconds);
size_t length = sizeof(CCSDSTime::Ccs_mseconds);
result = CCSDSTime::convertFromCCS(&timeTo, dataPtr, &length, sizeof(CCSDSTime::Ccs_mseconds));
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cssMilliSecconds.pField == 0x52); // 0b01010010
REQUIRE(cssMilliSecconds.yearMSB == 0x07);
REQUIRE(cssMilliSecconds.yearLSB == 0xe4);
REQUIRE(cssMilliSecconds.month == 2);
REQUIRE(cssMilliSecconds.day == 29);
REQUIRE(cssMilliSecconds.hour == 13);
REQUIRE(cssMilliSecconds.minute == 24);
REQUIRE(cssMilliSecconds.second == 45);
REQUIRE(timeTo.year == 2020);
REQUIRE(timeTo.month == 2);
REQUIRE(timeTo.day == 29);
REQUIRE(timeTo.hour == 13);
REQUIRE(timeTo.minute == 24);
REQUIRE(timeTo.second == 45);
REQUIRE(timeTo.usecond == 123400);
}
SECTION("CCS_Day of Year") {
Clock::TimeOfDay_t timeTo;
std::array<uint8_t, 8> ccsDayOfYear = {0b01011000, 0x07, 0xe4, 0, 60, 13, 24, 45};
size_t length = ccsDayOfYear.size();
auto result =
CCSDSTime::convertFromCCS(&timeTo, ccsDayOfYear.data(), &length, ccsDayOfYear.size());
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
// Check constness
REQUIRE(ccsDayOfYear[0] == 0b01011000);
REQUIRE(ccsDayOfYear[1] == 0x07);
REQUIRE(ccsDayOfYear[2] == 0xe4);
REQUIRE(ccsDayOfYear[3] == 0x0);
REQUIRE(ccsDayOfYear[4] == 60);
REQUIRE(ccsDayOfYear[5] == 13);
REQUIRE(ccsDayOfYear[6] == 24);
REQUIRE(ccsDayOfYear[7] == 45);
REQUIRE(timeTo.year == 2020);
REQUIRE(timeTo.month == 2);
REQUIRE(timeTo.day == 29);
REQUIRE(timeTo.hour == 13);
REQUIRE(timeTo.minute == 24);
REQUIRE(timeTo.second == 45);
REQUIRE(timeTo.usecond == 0);
}
SECTION("Test convertFromASCII") {
std::string timeAscii = "2022-12-31T23:59:59.123Z";
Clock::TimeOfDay_t timeTo;
const uint8_t* timeChar = reinterpret_cast<const uint8_t*>(timeAscii.c_str());
CCSDSTime::convertFromASCII(&timeTo, timeChar, timeAscii.length());
REQUIRE(timeTo.year == 2022);
REQUIRE(timeTo.month == 12);
REQUIRE(timeTo.day == 31);
REQUIRE(timeTo.hour == 23);
REQUIRE(timeTo.minute == 59);
REQUIRE(timeTo.second == 59);
REQUIRE(timeTo.usecond == Catch::Approx(123000));
}
}