diff --git a/CMakeLists.txt b/CMakeLists.txt index 4882db545..5ff66a6d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ elseif(${CMAKE_CXX_STANDARD} LESS 11) endif() # Backwards comptability -if(OS_FSFW) +if(OS_FSFW AND NOT FSFW_OSAL) message(WARNING "Please pass the FSFW OSAL as FSFW_OSAL instead of OS_FSFW") set(FSFW_OSAL OS_FSFW) endif() @@ -63,35 +63,28 @@ endif() set(FSFW_OSAL_DEFINITION FSFW_OSAL_HOST) if(FSFW_OSAL MATCHES host) - set(OS_FSFW_NAME "Host") + set(OS_FSFW_NAME "Host") + set(FSFW_OSAL_HOST ON) elseif(FSFW_OSAL MATCHES linux) - set(OS_FSFW_NAME "Linux") - set(FSFW_OSAL_DEFINITION FSFW_OSAL_LINUX) + set(OS_FSFW_NAME "Linux") + set(FSFW_OSAL_LINUX ON) elseif(FSFW_OSAL MATCHES freertos) - set(OS_FSFW_NAME "FreeRTOS") - set(FSFW_OSAL_DEFINITION FSFW_OSAL_FREERTOS) - target_link_libraries(${LIB_FSFW_NAME} PRIVATE + set(OS_FSFW_NAME "FreeRTOS") + set(FSFW_OSAL_FREERTOS ON) + target_link_libraries(${LIB_FSFW_NAME} PRIVATE ${LIB_OS_NAME} - ) + ) elseif(FSFW_OSAL STREQUAL rtems) - set(OS_FSFW_NAME "RTEMS") - set(FSFW_OSAL_DEFINITION FSFW_OSAL_RTEMS) + set(OS_FSFW_NAME "RTEMS") + set(FSFW_OSAL_RTEMS ON) else() - message(WARNING - "Invalid operating system for FSFW specified! Setting to host.." - ) - set(OS_FSFW_NAME "Host") - set(OS_FSFW "host") + message(WARNING + "Invalid operating system for FSFW specified! Setting to host.." + ) + set(OS_FSFW_NAME "Host") + 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(src) diff --git a/hal/src/fsfw_hal/linux/UnixFileGuard.cpp b/hal/src/fsfw_hal/linux/UnixFileGuard.cpp index f79010189..5019343e8 100644 --- a/hal/src/fsfw_hal/linux/UnixFileGuard.cpp +++ b/hal/src/fsfw_hal/linux/UnixFileGuard.cpp @@ -1,5 +1,10 @@ +#include "fsfw/FSFW.h" +#include "fsfw/serviceinterface.h" #include "fsfw_hal/linux/UnixFileGuard.h" +#include +#include + UnixFileGuard::UnixFileGuard(std::string device, int* fileDescriptor, int flags, std::string diagnosticPrefix): fileDescriptor(fileDescriptor) { @@ -10,12 +15,11 @@ UnixFileGuard::UnixFileGuard(std::string device, int* fileDescriptor, int flags, if (*fileDescriptor < 0) { #if FSFW_VERBOSE_LEVEL >= 1 #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << diagnosticPrefix <<"Opening device failed with error code " << errno << - "." << std::endl; - sif::warning << "Error description: " << strerror(errno) << std::endl; + sif::warning << diagnosticPrefix << ": Opening device failed with error code " << + errno << ": " << strerror(errno) << std::endl; #else - sif::printError("%sOpening device failed with error code %d.\n", diagnosticPrefix); - sif::printWarning("Error description: %s\n", strerror(errno)); + sif::printWarning("%s: Opening device failed with error code %d: %s\n", + diagnosticPrefix, errno, strerror(errno)); #endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ #endif /* FSFW_VERBOSE_LEVEL >= 1 */ openStatus = OPEN_FILE_FAILED; diff --git a/hal/src/fsfw_hal/linux/spi/SpiComIF.cpp b/hal/src/fsfw_hal/linux/spi/SpiComIF.cpp index fafe67bee..48bf74491 100644 --- a/hal/src/fsfw_hal/linux/spi/SpiComIF.cpp +++ b/hal/src/fsfw_hal/linux/spi/SpiComIF.cpp @@ -90,7 +90,7 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) { int fileDescriptor = 0; UnixFileGuard fileHelper(spiCookie->getSpiDevice(), &fileDescriptor, O_RDWR, - "SpiComIF::initializeInterface: "); + "SpiComIF::initializeInterface"); if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { return fileHelper.getOpenResult(); } @@ -184,7 +184,7 @@ ReturnValue_t SpiComIF::performRegularSendOperation(SpiCookie *spiCookie, const /* Prepare transfer */ int fileDescriptor = 0; std::string device = spiCookie->getSpiDevice(); - UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage: "); + UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, "SpiComIF::sendMessage"); if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { return OPENING_FILE_FAILED; } @@ -273,7 +273,7 @@ ReturnValue_t SpiComIF::performHalfDuplexReception(SpiCookie* spiCookie) { std::string device = spiCookie->getSpiDevice(); int fileDescriptor = 0; UnixFileGuard fileHelper(device, &fileDescriptor, O_RDWR, - "SpiComIF::requestReceiveMessage: "); + "SpiComIF::requestReceiveMessage"); if(fileHelper.getOpenResult() != HasReturnvaluesIF::RETURN_OK) { return OPENING_FILE_FAILED; } diff --git a/src/fsfw/FSFW.h.in b/src/fsfw/FSFW.h.in index 4d4b8aee1..f0eb93656 100644 --- a/src/fsfw/FSFW.h.in +++ b/src/fsfw/FSFW.h.in @@ -3,6 +3,11 @@ #include "FSFWConfig.h" +#cmakedefine FSFW_OSAL_RTEMS +#cmakedefine FSFW_OSAL_FREERTOS +#cmakedefine FSFW_OSAL_LINUX +#cmakedefine FSFW_OSAL_HOST + #cmakedefine FSFW_ADD_RMAP #cmakedefine FSFW_ADD_DATALINKLAYER #cmakedefine FSFW_ADD_TMSTORAGE diff --git a/src/fsfw/FSFWVersion.h b/src/fsfw/FSFWVersion.h index df2d49a51..f8e896940 100644 --- a/src/fsfw/FSFWVersion.h +++ b/src/fsfw/FSFWVersion.h @@ -1,12 +1,10 @@ -#ifndef FSFW_DEFAULTCFG_VERSION_H_ -#define FSFW_DEFAULTCFG_VERSION_H_ +#ifndef FSFW_VERSION_H_ +#define FSFW_VERSION_H_ const char* const FSFW_VERSION_NAME = "ASTP"; #define FSFW_VERSION 1 -#define FSFW_SUBVERSION 0 -#define FSFW_REVISION 0 +#define FSFW_SUBVERSION 2 +#define FSFW_REVISION 0 - - -#endif /* FSFW_DEFAULTCFG_VERSION_H_ */ +#endif /* FSFW_VERSION_H_ */ diff --git a/src/fsfw/action/ActionHelper.cpp b/src/fsfw/action/ActionHelper.cpp index 3dfe8b50f..bebd55a79 100644 --- a/src/fsfw/action/ActionHelper.cpp +++ b/src/fsfw/action/ActionHelper.cpp @@ -32,6 +32,17 @@ ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) { setQueueToUse(queueToUse_); } + if(queueToUse == nullptr) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "ActionHelper::initialize: No queue set" << std::endl; +#else + sif::printWarning("ActionHelper::initialize: No queue set\n"); +#endif +#endif /* FSFW_VERBOSE_LEVEL >= 1 */ + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; } diff --git a/src/fsfw/globalfunctions/DleEncoder.cpp b/src/fsfw/globalfunctions/DleEncoder.cpp index 11df8ad71..47ea5c4e5 100644 --- a/src/fsfw/globalfunctions/DleEncoder.cpp +++ b/src/fsfw/globalfunctions/DleEncoder.cpp @@ -1,124 +1,296 @@ #include "fsfw/globalfunctions/DleEncoder.h" -DleEncoder::DleEncoder() {} +DleEncoder::DleEncoder(bool escapeStxEtx, bool escapeCr): + escapeStxEtx(escapeStxEtx), escapeCr(escapeCr) {} DleEncoder::~DleEncoder() {} ReturnValue_t DleEncoder::encode(const uint8_t* sourceStream, - size_t sourceLen, uint8_t* destStream, size_t maxDestLen, - size_t* encodedLen, bool addStxEtx) { - if (maxDestLen < 2) { - return STREAM_TOO_SHORT; - } - size_t encodedIndex = 0, sourceIndex = 0; - uint8_t nextByte; - if (addStxEtx) { - destStream[0] = STX_CHAR; - ++encodedIndex; - } + size_t sourceLen, uint8_t* destStream, size_t maxDestLen, + size_t* encodedLen, bool addStxEtx) { + if(escapeStxEtx) { + return encodeStreamEscaped(sourceStream, sourceLen, + destStream, maxDestLen, encodedLen, addStxEtx); + } + else { + return encodeStreamNonEscaped(sourceStream, sourceLen, + destStream, maxDestLen, encodedLen, addStxEtx); + } - while (encodedIndex < maxDestLen and sourceIndex < sourceLen) - { - nextByte = sourceStream[sourceIndex]; - // STX, ETX and CR characters in the stream need to be escaped with DLE - if (nextByte == STX_CHAR or nextByte == ETX_CHAR or nextByte == CARRIAGE_RETURN) { - if (encodedIndex + 1 >= maxDestLen) { - return STREAM_TOO_SHORT; - } - else { - destStream[encodedIndex] = DLE_CHAR; - ++encodedIndex; - /* Escaped byte will be actual byte + 0x40. This prevents - * STX, ETX, and carriage return characters from appearing - * in the encoded data stream at all, so when polling an - * encoded stream, the transmission can be stopped at ETX. - * 0x40 was chosen at random with special requirements: - * - Prevent going from one control char to another - * - Prevent overflow for common characters */ - destStream[encodedIndex] = nextByte + 0x40; - } - } - // DLE characters are simply escaped with DLE. - else if (nextByte == DLE_CHAR) { - if (encodedIndex + 1 >= maxDestLen) { - return STREAM_TOO_SHORT; - } - else { - destStream[encodedIndex] = DLE_CHAR; - ++encodedIndex; - destStream[encodedIndex] = DLE_CHAR; - } - } - else { - destStream[encodedIndex] = nextByte; - } - ++encodedIndex; - ++sourceIndex; - } +} - if (sourceIndex == sourceLen and encodedIndex < maxDestLen) { - if (addStxEtx) { - destStream[encodedIndex] = ETX_CHAR; - ++encodedIndex; - } - *encodedLen = encodedIndex; - return RETURN_OK; - } - else { - return STREAM_TOO_SHORT; - } +ReturnValue_t DleEncoder::encodeStreamEscaped(const uint8_t *sourceStream, size_t sourceLen, + uint8_t *destStream, size_t maxDestLen, size_t *encodedLen, + bool addStxEtx) { + size_t encodedIndex = 0; + size_t sourceIndex = 0; + uint8_t nextByte = 0; + if(addStxEtx) { + if(maxDestLen < 1) { + return STREAM_TOO_SHORT; + } + destStream[encodedIndex++] = STX_CHAR; + } + while (encodedIndex < maxDestLen and sourceIndex < sourceLen) { + nextByte = sourceStream[sourceIndex]; + // STX, ETX and CR characters in the stream need to be escaped with DLE + if ((nextByte == STX_CHAR or nextByte == ETX_CHAR) or + (this->escapeCr and nextByte == CARRIAGE_RETURN)) { + if (encodedIndex + 1 >= maxDestLen) { + return STREAM_TOO_SHORT; + } + else { + destStream[encodedIndex] = DLE_CHAR; + ++encodedIndex; + /* Escaped byte will be actual byte + 0x40. This prevents + * STX, ETX, and carriage return characters from appearing + * in the encoded data stream at all, so when polling an + * encoded stream, the transmission can be stopped at ETX. + * 0x40 was chosen at random with special requirements: + * - Prevent going from one control char to another + * - Prevent overflow for common characters */ + destStream[encodedIndex] = nextByte + 0x40; + } + } + // DLE characters are simply escaped with DLE. + else if (nextByte == DLE_CHAR) { + if (encodedIndex + 1 >= maxDestLen) { + return STREAM_TOO_SHORT; + } + else { + destStream[encodedIndex] = DLE_CHAR; + ++encodedIndex; + destStream[encodedIndex] = DLE_CHAR; + } + } + else { + destStream[encodedIndex] = nextByte; + } + ++encodedIndex; + ++sourceIndex; + } + + if (sourceIndex == sourceLen) { + if (addStxEtx) { + if(encodedIndex + 1 >= maxDestLen) { + return STREAM_TOO_SHORT; + } + destStream[encodedIndex] = ETX_CHAR; + ++encodedIndex; + } + *encodedLen = encodedIndex; + return RETURN_OK; + } + else { + return STREAM_TOO_SHORT; + } +} + +ReturnValue_t DleEncoder::encodeStreamNonEscaped(const uint8_t *sourceStream, size_t sourceLen, + uint8_t *destStream, size_t maxDestLen, size_t *encodedLen, + bool addStxEtx) { + size_t encodedIndex = 0; + size_t sourceIndex = 0; + uint8_t nextByte = 0; + if(addStxEtx) { + if(maxDestLen < 2) { + return STREAM_TOO_SHORT; + } + destStream[encodedIndex++] = DLE_CHAR; + destStream[encodedIndex++] = STX_CHAR; + } + while (encodedIndex < maxDestLen and sourceIndex < sourceLen) { + nextByte = sourceStream[sourceIndex]; + // DLE characters are simply escaped with DLE. + if (nextByte == DLE_CHAR) { + if (encodedIndex + 1 >= maxDestLen) { + return STREAM_TOO_SHORT; + } + else { + destStream[encodedIndex] = DLE_CHAR; + ++encodedIndex; + destStream[encodedIndex] = DLE_CHAR; + } + } + else { + destStream[encodedIndex] = nextByte; + } + ++encodedIndex; + ++sourceIndex; + } + + if (sourceIndex == sourceLen) { + if (addStxEtx) { + if(encodedIndex + 2 >= maxDestLen) { + return STREAM_TOO_SHORT; + } + destStream[encodedIndex++] = DLE_CHAR; + destStream[encodedIndex++] = ETX_CHAR; + } + *encodedLen = encodedIndex; + return RETURN_OK; + } + else { + return STREAM_TOO_SHORT; + } } ReturnValue_t DleEncoder::decode(const uint8_t *sourceStream, - size_t sourceStreamLen, size_t *readLen, uint8_t *destStream, - size_t maxDestStreamlen, size_t *decodedLen) { - size_t encodedIndex = 0, decodedIndex = 0; - uint8_t nextByte; - if (*sourceStream != STX_CHAR) { - return DECODING_ERROR; - } - ++encodedIndex; - - while ((encodedIndex < sourceStreamLen) && (decodedIndex < maxDestStreamlen) - && (sourceStream[encodedIndex] != ETX_CHAR) - && (sourceStream[encodedIndex] != STX_CHAR)) { - if (sourceStream[encodedIndex] == DLE_CHAR) { - nextByte = sourceStream[encodedIndex + 1]; - // The next byte is a DLE character that was escaped by another - // DLE character, so we can write it to the destination stream. - if (nextByte == DLE_CHAR) { - destStream[decodedIndex] = nextByte; - } - else { - /* The next byte is a STX, DTX or 0x0D character which - * was escaped by a DLE character. The actual byte was - * also encoded by adding + 0x40 to prevent having control chars, - * in the stream at all, so we convert it back. */ - if (nextByte == 0x42 or nextByte == 0x43 or nextByte == 0x4D) { - destStream[decodedIndex] = nextByte - 0x40; - } - else { - return DECODING_ERROR; - } - } - ++encodedIndex; - } - else { - destStream[decodedIndex] = sourceStream[encodedIndex]; - } - - ++encodedIndex; - ++decodedIndex; - } - - if (sourceStream[encodedIndex] != ETX_CHAR) { - *readLen = ++encodedIndex; - return DECODING_ERROR; - } - else { - *readLen = ++encodedIndex; - *decodedLen = decodedIndex; - return RETURN_OK; - } + size_t sourceStreamLen, size_t *readLen, uint8_t *destStream, + size_t maxDestStreamlen, size_t *decodedLen) { + if(escapeStxEtx) { + return decodeStreamEscaped(sourceStream, sourceStreamLen, + readLen, destStream, maxDestStreamlen, decodedLen); + } + else { + return decodeStreamNonEscaped(sourceStream, sourceStreamLen, + readLen, destStream, maxDestStreamlen, decodedLen); + } } +ReturnValue_t DleEncoder::decodeStreamEscaped(const uint8_t *sourceStream, size_t sourceStreamLen, + size_t *readLen, uint8_t *destStream, + size_t maxDestStreamlen, size_t *decodedLen) { + size_t encodedIndex = 0; + size_t decodedIndex = 0; + uint8_t nextByte; + + //init to 0 so that we can just return in the first checks (which do not consume anything from + //the source stream) + *readLen = 0; + + if(maxDestStreamlen < 1) { + return STREAM_TOO_SHORT; + } + if (sourceStream[encodedIndex++] != STX_CHAR) { + return DECODING_ERROR; + } + while ((encodedIndex < sourceStreamLen) + and (decodedIndex < maxDestStreamlen) + and (sourceStream[encodedIndex] != ETX_CHAR) + and (sourceStream[encodedIndex] != STX_CHAR)) { + if (sourceStream[encodedIndex] == DLE_CHAR) { + if(encodedIndex + 1 >= sourceStreamLen) { + //reached the end of the sourceStream + *readLen = sourceStreamLen; + return DECODING_ERROR; + } + nextByte = sourceStream[encodedIndex + 1]; + // The next byte is a DLE character that was escaped by another + // DLE character, so we can write it to the destination stream. + if (nextByte == DLE_CHAR) { + destStream[decodedIndex] = nextByte; + } + else { + /* The next byte is a STX, DTX or 0x0D character which + * was escaped by a DLE character. The actual byte was + * also encoded by adding + 0x40 to prevent having control chars, + * in the stream at all, so we convert it back. */ + if ((nextByte == STX_CHAR + 0x40 or nextByte == ETX_CHAR + 0x40) or + (this->escapeCr and nextByte == CARRIAGE_RETURN + 0x40)) { + destStream[decodedIndex] = nextByte - 0x40; + } + else { + // Set readLen so user can resume parsing after incorrect data + *readLen = encodedIndex + 2; + return DECODING_ERROR; + } + } + ++encodedIndex; + } + else { + destStream[decodedIndex] = sourceStream[encodedIndex]; + } + + ++encodedIndex; + ++decodedIndex; + } + if (sourceStream[encodedIndex] != ETX_CHAR) { + if(decodedIndex == maxDestStreamlen) { + //so far we did not find anything wrong here, so let user try again + *readLen = 0; + return STREAM_TOO_SHORT; + } + else { + *readLen = ++encodedIndex; + return DECODING_ERROR; + } + } + else { + *readLen = ++encodedIndex; + *decodedLen = decodedIndex; + return RETURN_OK; + } +} + +ReturnValue_t DleEncoder::decodeStreamNonEscaped(const uint8_t *sourceStream, + size_t sourceStreamLen, size_t *readLen, uint8_t *destStream, + size_t maxDestStreamlen, size_t *decodedLen) { + size_t encodedIndex = 0; + size_t decodedIndex = 0; + uint8_t nextByte; + + //init to 0 so that we can just return in the first checks (which do not consume anything from + //the source stream) + *readLen = 0; + + if(maxDestStreamlen < 2) { + return STREAM_TOO_SHORT; + } + if (sourceStream[encodedIndex++] != DLE_CHAR) { + return DECODING_ERROR; + } + if (sourceStream[encodedIndex++] != STX_CHAR) { + *readLen = 1; + return DECODING_ERROR; + } + while ((encodedIndex < sourceStreamLen) && (decodedIndex < maxDestStreamlen)) { + if (sourceStream[encodedIndex] == DLE_CHAR) { + if(encodedIndex + 1 >= sourceStreamLen) { + *readLen = encodedIndex; + return DECODING_ERROR; + } + nextByte = sourceStream[encodedIndex + 1]; + if(nextByte == STX_CHAR) { + // Set readLen so the DLE/STX char combination is preserved. Could be start of + // another frame + *readLen = encodedIndex; + return DECODING_ERROR; + } + else if(nextByte == DLE_CHAR) { + // The next byte is a DLE character that was escaped by another + // DLE character, so we can write it to the destination stream. + destStream[decodedIndex] = nextByte; + ++encodedIndex; + } + else if(nextByte == ETX_CHAR) { + // End of stream reached + *readLen = encodedIndex + 2; + *decodedLen = decodedIndex; + return RETURN_OK; + } + else { + *readLen = encodedIndex; + return DECODING_ERROR; + } + } + else { + destStream[decodedIndex] = sourceStream[encodedIndex]; + } + ++encodedIndex; + ++decodedIndex; + } + + if(decodedIndex == maxDestStreamlen) { + //so far we did not find anything wrong here, so let user try again + *readLen = 0; + return STREAM_TOO_SHORT; + } else { + *readLen = encodedIndex; + return DECODING_ERROR; + } +} + +void DleEncoder::setEscapeMode(bool escapeStxEtx) { + this->escapeStxEtx = escapeStxEtx; +} diff --git a/src/fsfw/globalfunctions/DleEncoder.h b/src/fsfw/globalfunctions/DleEncoder.h index 6d073f9a2..09fa27262 100644 --- a/src/fsfw/globalfunctions/DleEncoder.h +++ b/src/fsfw/globalfunctions/DleEncoder.h @@ -1,7 +1,7 @@ #ifndef FRAMEWORK_GLOBALFUNCTIONS_DLEENCODER_H_ #define FRAMEWORK_GLOBALFUNCTIONS_DLEENCODER_H_ -#include "../returnvalues/HasReturnvaluesIF.h" +#include "fsfw/returnvalues/HasReturnvaluesIF.h" #include /** @@ -12,52 +12,69 @@ * https://en.wikipedia.org/wiki/C0_and_C1_control_codes * * This encoder can be used to achieve a basic transport layer when using - * char based transmission systems. - * The passed source strean is converted into a encoded stream by adding - * a STX marker at the start of the stream and an ETX marker at the end of - * the stream. Any STX, ETX, DLE and CR occurrences in the source stream are - * escaped by a DLE character. The encoder also replaces escaped control chars - * by another char, so STX, ETX and CR should not appear anywhere in the actual - * encoded data stream. + * char based transmission systems. There are two implemented variants: * - * When using a strictly char based reception of packets encoded with DLE, + * 1. Escaped variant + * + * The encoded stream starts with a STX marker and ends with an ETX marker. + * STX and ETX occurrences in the stream are escaped and internally encoded as well so the + * receiver side can simply check for STX and ETX markers as frame delimiters. When using a + * strictly char based reception of packets encoded with DLE, * STX can be used to notify a reader that actual data will start to arrive * while ETX can be used to notify the reader that the data has ended. + * + * 2. Non-escaped variant + * + * The encoded stream starts with DLE STX and ends with DLE ETX. All DLE occurrences in the stream + * are escaped with DLE. If the receiver detects a DLE char, it needs to read the next char + * to determine whether a start (STX) or end (ETX) of a frame has been detected. */ class DleEncoder: public HasReturnvaluesIF { -private: - DleEncoder(); - virtual ~DleEncoder(); - public: - static constexpr uint8_t INTERFACE_ID = CLASS_ID::DLE_ENCODER; - static constexpr ReturnValue_t STREAM_TOO_SHORT = MAKE_RETURN_CODE(0x01); - static constexpr ReturnValue_t DECODING_ERROR = MAKE_RETURN_CODE(0x02); + /** + * Create an encoder instance with the given configuration. + * @param escapeStxEtx Determines whether the algorithm works in escaped or non-escaped mode + * @param escapeCr In escaped mode, escape all CR occurrences as well + */ + DleEncoder(bool escapeStxEtx = true, bool escapeCr = false); - //! Start Of Text character. First character is encoded stream - static constexpr uint8_t STX_CHAR = 0x02; - //! End Of Text character. Last character in encoded stream - static constexpr uint8_t ETX_CHAR = 0x03; - //! Data Link Escape character. Used to escape STX, ETX and DLE occurrences - //! in the source stream. - static constexpr uint8_t DLE_CHAR = 0x10; - static constexpr uint8_t CARRIAGE_RETURN = 0x0D; + void setEscapeMode(bool escapeStxEtx); + + virtual ~DleEncoder(); + + static constexpr uint8_t INTERFACE_ID = CLASS_ID::DLE_ENCODER; + static constexpr ReturnValue_t STREAM_TOO_SHORT = MAKE_RETURN_CODE(0x01); + static constexpr ReturnValue_t DECODING_ERROR = MAKE_RETURN_CODE(0x02); + + //! Start Of Text character. First character is encoded stream + static constexpr uint8_t STX_CHAR = 0x02; + //! End Of Text character. Last character in encoded stream + static constexpr uint8_t ETX_CHAR = 0x03; + //! Data Link Escape character. Used to escape STX, ETX and DLE occurrences + //! in the source stream. + static constexpr uint8_t DLE_CHAR = 0x10; + static constexpr uint8_t CARRIAGE_RETURN = 0x0D; /** * Encodes the give data stream by preceding it with the STX marker - * and ending it with an ETX marker. STX, ETX and DLE characters inside - * the stream are escaped by DLE characters and also replaced by adding - * 0x40 (which is reverted in the decoding process). + * and ending it with an ETX marker. DLE characters inside + * the stream are escaped by DLE characters. STX, ETX and CR characters can be escaped with a + * DLE character as well. The escaped characters are also encoded by adding + * 0x40 (which is reverted in the decoding process). This is performed so the source stream + * does not have STX/ETX/CR occurrences anymore, so the receiving side can simply parse for + * start and end markers * @param sourceStream * @param sourceLen * @param destStream * @param maxDestLen * @param encodedLen - * @param addStxEtx - * Adding STX and ETX can be omitted, if they are added manually. + * @param addStxEtx Adding STX start marker and ETX end marker can be omitted, + * if they are added manually * @return + * - RETURN_OK for successful encoding operation + * - STREAM_TOO_SHORT if the destination stream is too short */ - static ReturnValue_t encode(const uint8_t *sourceStream, size_t sourceLen, + ReturnValue_t encode(const uint8_t *sourceStream, size_t sourceLen, uint8_t *destStream, size_t maxDestLen, size_t *encodedLen, bool addStxEtx = true); @@ -70,10 +87,32 @@ public: * @param maxDestStreamlen * @param decodedLen * @return + * - RETURN_OK for successful decode operation + * - DECODE_ERROR if the source stream is invalid + * - STREAM_TOO_SHORT if the destination stream is too short */ - static ReturnValue_t decode(const uint8_t *sourceStream, - size_t sourceStreamLen, size_t *readLen, uint8_t *destStream, - size_t maxDestStreamlen, size_t *decodedLen); + ReturnValue_t decode(const uint8_t *sourceStream, + size_t sourceStreamLen, size_t *readLen, uint8_t *destStream, + size_t maxDestStreamlen, size_t *decodedLen); + +private: + + ReturnValue_t encodeStreamEscaped(const uint8_t *sourceStream, size_t sourceLen, + uint8_t *destStream, size_t maxDestLen, size_t *encodedLen, + bool addStxEtx = true); + + ReturnValue_t encodeStreamNonEscaped(const uint8_t *sourceStream, size_t sourceLen, + uint8_t *destStream, size_t maxDestLen, size_t *encodedLen, + bool addStxEtx = true); + + ReturnValue_t decodeStreamEscaped(const uint8_t *sourceStream, size_t sourceStreamLen, + size_t *readLen, uint8_t *destStream, size_t maxDestStreamlen, size_t *decodedLen); + + ReturnValue_t decodeStreamNonEscaped(const uint8_t *sourceStream, size_t sourceStreamLen, + size_t *readLen, uint8_t *destStream, size_t maxDestStreamlen, size_t *decodedLen); + + bool escapeStxEtx; + bool escapeCr; }; #endif /* FRAMEWORK_GLOBALFUNCTIONS_DLEENCODER_H_ */ diff --git a/src/fsfw/osal/common/TcpTmTcBridge.cpp b/src/fsfw/osal/common/TcpTmTcBridge.cpp index 24f1a2813..3cd03c368 100644 --- a/src/fsfw/osal/common/TcpTmTcBridge.cpp +++ b/src/fsfw/osal/common/TcpTmTcBridge.cpp @@ -1,6 +1,5 @@ #include "fsfw/platform.h" #include "fsfw/osal/common/TcpTmTcBridge.h" -#include "fsfw/osal/common/tcpipHelpers.h" #include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/ipc/MutexGuard.h" @@ -17,8 +16,6 @@ #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) { diff --git a/src/fsfw/osal/common/TcpTmTcBridge.h b/src/fsfw/osal/common/TcpTmTcBridge.h index 6cfacb9fa..dc46f1f01 100644 --- a/src/fsfw/osal/common/TcpTmTcBridge.h +++ b/src/fsfw/osal/common/TcpTmTcBridge.h @@ -2,7 +2,7 @@ #define FSFW_OSAL_COMMON_TCPTMTCBRIDGE_H_ #include "TcpIpBase.h" -#include "../../tmtcservices/TmTcBridge.h" +#include "fsfw/tmtcservices/TmTcBridge.h" #ifdef _WIN32 @@ -29,8 +29,6 @@ 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 diff --git a/src/fsfw/osal/common/TcpTmTcServer.cpp b/src/fsfw/osal/common/TcpTmTcServer.cpp index f94449bb8..11ab71af8 100644 --- a/src/fsfw/osal/common/TcpTmTcServer.cpp +++ b/src/fsfw/osal/common/TcpTmTcServer.cpp @@ -22,14 +22,14 @@ #define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0 #endif -const std::string TcpTmTcServer::DEFAULT_TCP_SERVER_PORT = "7303"; +const std::string TcpTmTcServer::DEFAULT_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; 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; + tcpPort = DEFAULT_SERVER_PORT; } } @@ -200,6 +200,10 @@ void TcpTmTcServer::setTcpBacklog(uint8_t tcpBacklog) { this->tcpBacklog = tcpBacklog; } +std::string TcpTmTcServer::getTcpPort() const { + return tcpPort; +} + 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); diff --git a/src/fsfw/osal/common/TcpTmTcServer.h b/src/fsfw/osal/common/TcpTmTcServer.h index f7c36d69c..c69160807 100644 --- a/src/fsfw/osal/common/TcpTmTcServer.h +++ b/src/fsfw/osal/common/TcpTmTcServer.h @@ -3,13 +3,14 @@ #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" +#include "fsfw/platform.h" +#include "fsfw/osal/common/tcpipHelpers.h" +#include "fsfw/ipc/messageQueueDefinitions.h" +#include "fsfw/ipc/MessageQueueIF.h" +#include "fsfw/objectmanager/frameworkObjects.h" +#include "fsfw/objectmanager/SystemObject.h" +#include "fsfw/storagemanager/StorageManagerIF.h" +#include "fsfw/tasks/ExecutableObjectIF.h" #ifdef PLATFORM_UNIX #include @@ -41,10 +42,9 @@ class TcpTmTcServer: 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 const std::string DEFAULT_SERVER_PORT; - static constexpr size_t ETHERNET_MTU_SIZE = 1500; + static constexpr size_t ETHERNET_MTU_SIZE = 1500; /** * TCP Server Constructor @@ -65,6 +65,8 @@ public: ReturnValue_t performOperation(uint8_t opCode) override; ReturnValue_t initializeAfterTaskCreation() override; + std::string getTcpPort() const; + protected: StorageManagerIF* tcStore = nullptr; StorageManagerIF* tmStore = nullptr; diff --git a/src/fsfw/osal/common/UdpTmTcBridge.cpp b/src/fsfw/osal/common/UdpTmTcBridge.cpp index 7015cf4a5..734a2b15b 100644 --- a/src/fsfw/osal/common/UdpTmTcBridge.cpp +++ b/src/fsfw/osal/common/UdpTmTcBridge.cpp @@ -17,13 +17,13 @@ #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 #endif -const std::string UdpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; +const std::string UdpTmTcBridge::DEFAULT_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; + this->udpServerPort = DEFAULT_SERVER_PORT; } else { this->udpServerPort = udpServerPort; @@ -108,6 +108,10 @@ UdpTmTcBridge::~UdpTmTcBridge() { } } +std::string UdpTmTcBridge::getUdpPort() const { + return udpServerPort; +} + ReturnValue_t UdpTmTcBridge::sendTm(const uint8_t *data, size_t dataLen) { int flags = 0; diff --git a/src/fsfw/osal/common/UdpTmTcBridge.h b/src/fsfw/osal/common/UdpTmTcBridge.h index 7a346de57..4d634e649 100644 --- a/src/fsfw/osal/common/UdpTmTcBridge.h +++ b/src/fsfw/osal/common/UdpTmTcBridge.h @@ -2,8 +2,8 @@ #define FSFW_OSAL_COMMON_TMTCUDPBRIDGE_H_ #include "TcpIpBase.h" -#include "../../platform.h" -#include "../../tmtcservices/TmTcBridge.h" +#include "fsfw/platform.h" +#include "fsfw/tmtcservices/TmTcBridge.h" #ifdef PLATFORM_WIN #include @@ -28,7 +28,7 @@ class UdpTmTcBridge: friend class UdpTcPollingTask; public: /* The ports chosen here should not be used by any other process. */ - static const std::string DEFAULT_UDP_SERVER_PORT; + static const std::string DEFAULT_SERVER_PORT; UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, std::string udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE, @@ -44,6 +44,8 @@ public: void checkAndSetClientAddress(sockaddr& clientAddress); + std::string getUdpPort() const; + protected: virtual ReturnValue_t sendTm(const uint8_t * data, size_t dataLen) override; diff --git a/src/fsfw/osal/common/tcpipCommon.h b/src/fsfw/osal/common/tcpipCommon.h index ce7a90cd1..5a04144e1 100644 --- a/src/fsfw/osal/common/tcpipCommon.h +++ b/src/fsfw/osal/common/tcpipCommon.h @@ -1,7 +1,7 @@ #ifndef FSFW_OSAL_COMMON_TCPIPCOMMON_H_ #define FSFW_OSAL_COMMON_TCPIPCOMMON_H_ -#include "../../timemanager/clockDefinitions.h" +#include "fsfw/timemanager/clockDefinitions.h" #include #ifdef _WIN32 @@ -13,7 +13,7 @@ namespace tcpip { -const char* const DEFAULT_SERVER_PORT = "7301"; +static constexpr char DEFAULT_SERVER_PORT[] = "7301"; enum class Protocol { UDP, diff --git a/tests/src/fsfw_tests/unit/CatchFactory.h b/tests/src/fsfw_tests/unit/CatchFactory.h index ae0629e5d..38ec46bd0 100644 --- a/tests/src/fsfw_tests/unit/CatchFactory.h +++ b/tests/src/fsfw_tests/unit/CatchFactory.h @@ -1,7 +1,7 @@ #ifndef FSFW_CATCHFACTORY_H_ #define FSFW_CATCHFACTORY_H_ -#include "TestConfig.h" +#include "TestsConfig.h" #include "fsfw/objectmanager/SystemObjectIF.h" #include "fsfw/objectmanager/ObjectManager.h" diff --git a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt index 8e57e01be..617c7f5a0 100644 --- a/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt +++ b/tests/src/fsfw_tests/unit/globalfunctions/CMakeLists.txt @@ -1,2 +1,3 @@ target_sources(${TARGET_NAME} PRIVATE + testDleEncoder.cpp ) diff --git a/tests/src/fsfw_tests/unit/globalfunctions/testDleEncoder.cpp b/tests/src/fsfw_tests/unit/globalfunctions/testDleEncoder.cpp new file mode 100644 index 000000000..a82ac73a9 --- /dev/null +++ b/tests/src/fsfw_tests/unit/globalfunctions/testDleEncoder.cpp @@ -0,0 +1,222 @@ +#include "fsfw/globalfunctions/DleEncoder.h" +#include "fsfw_tests/unit/CatchDefinitions.h" +#include "catch2/catch_test_macros.hpp" + +#include + +const std::vector TEST_ARRAY_0 = { 0, 0, 0, 0, 0 }; +const std::vector TEST_ARRAY_1 = { 0, DleEncoder::DLE_CHAR, 5}; +const std::vector TEST_ARRAY_2 = { 0, DleEncoder::STX_CHAR, 5}; +const std::vector TEST_ARRAY_3 = { 0, DleEncoder::CARRIAGE_RETURN, DleEncoder::ETX_CHAR}; +const std::vector TEST_ARRAY_4 = { DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR, + DleEncoder::STX_CHAR }; + +const std::vector TEST_ARRAY_0_ENCODED_ESCAPED = { + DleEncoder::STX_CHAR, 0, 0, 0, 0, 0, DleEncoder::ETX_CHAR +}; +const std::vector TEST_ARRAY_0_ENCODED_NON_ESCAPED = { + DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, 0, 0, 0, 0, + DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR +}; + +const std::vector TEST_ARRAY_1_ENCODED_ESCAPED = { + DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, 5, DleEncoder::ETX_CHAR +}; +const std::vector TEST_ARRAY_1_ENCODED_NON_ESCAPED = { + DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, + 5, DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR +}; + +const std::vector TEST_ARRAY_2_ENCODED_ESCAPED = { + DleEncoder::STX_CHAR, 0, DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR + 0x40, + 5, DleEncoder::ETX_CHAR +}; +const std::vector TEST_ARRAY_2_ENCODED_NON_ESCAPED = { + DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, + DleEncoder::STX_CHAR, 5, DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR +}; + +const std::vector TEST_ARRAY_3_ENCODED_ESCAPED = { + DleEncoder::STX_CHAR, 0, DleEncoder::CARRIAGE_RETURN, + DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR + 0x40, DleEncoder::ETX_CHAR +}; +const std::vector TEST_ARRAY_3_ENCODED_NON_ESCAPED = { + DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, 0, + DleEncoder::CARRIAGE_RETURN, DleEncoder::ETX_CHAR, DleEncoder::DLE_CHAR, + DleEncoder::ETX_CHAR +}; + +const std::vector TEST_ARRAY_4_ENCODED_ESCAPED = { + DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, + DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR + 0x40, DleEncoder::DLE_CHAR, + DleEncoder::STX_CHAR + 0x40, DleEncoder::ETX_CHAR +}; +const std::vector TEST_ARRAY_4_ENCODED_NON_ESCAPED = { + DleEncoder::DLE_CHAR, DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::DLE_CHAR, + DleEncoder::ETX_CHAR, DleEncoder::STX_CHAR, DleEncoder::DLE_CHAR, DleEncoder::ETX_CHAR +}; + + +TEST_CASE("DleEncoder" , "[DleEncoder]") { + DleEncoder dleEncoder; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + std::array buffer; + + size_t encodedLen = 0; + size_t readLen = 0; + size_t decodedLen = 0; + + auto testLambdaEncode = [&](DleEncoder& encoder, const std::vector& vecToEncode, + const std::vector& expectedVec) { + result = encoder.encode(vecToEncode.data(), vecToEncode.size(), + buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == retval::CATCH_OK); + for(size_t idx = 0; idx < expectedVec.size(); idx++) { + REQUIRE(buffer[idx] == expectedVec[idx]); + } + REQUIRE(encodedLen == expectedVec.size()); + }; + + auto testLambdaDecode = [&](DleEncoder& encoder, const std::vector& testVecEncoded, + const std::vector& expectedVec) { + result = encoder.decode(testVecEncoded.data(), + testVecEncoded.size(), + &readLen, buffer.data(), buffer.size(), &decodedLen); + REQUIRE(result == retval::CATCH_OK); + REQUIRE(readLen == testVecEncoded.size()); + REQUIRE(decodedLen == expectedVec.size()); + for(size_t idx = 0; idx < decodedLen; idx++) { + REQUIRE(buffer[idx] == expectedVec[idx]); + } + }; + + SECTION("Encoding") { + testLambdaEncode(dleEncoder, TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_ESCAPED); + + auto testFaultyEncoding = [&](const std::vector& vecToEncode, + const std::vector& expectedVec) { + + for(size_t faultyDestSize = 0; faultyDestSize < expectedVec.size(); faultyDestSize ++) { + result = dleEncoder.encode(vecToEncode.data(), vecToEncode.size(), + buffer.data(), faultyDestSize, &encodedLen); + REQUIRE(result == DleEncoder::STREAM_TOO_SHORT); + } + }; + + testFaultyEncoding(TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_ESCAPED); + testFaultyEncoding(TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_ESCAPED); + testFaultyEncoding(TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_ESCAPED); + testFaultyEncoding(TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_ESCAPED); + testFaultyEncoding(TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_ESCAPED); + + dleEncoder.setEscapeMode(false); + testLambdaEncode(dleEncoder, TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_NON_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_NON_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_NON_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_NON_ESCAPED); + testLambdaEncode(dleEncoder, TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_NON_ESCAPED); + + testFaultyEncoding(TEST_ARRAY_0, TEST_ARRAY_0_ENCODED_NON_ESCAPED); + testFaultyEncoding(TEST_ARRAY_1, TEST_ARRAY_1_ENCODED_NON_ESCAPED); + testFaultyEncoding(TEST_ARRAY_2, TEST_ARRAY_2_ENCODED_NON_ESCAPED); + testFaultyEncoding(TEST_ARRAY_3, TEST_ARRAY_3_ENCODED_NON_ESCAPED); + testFaultyEncoding(TEST_ARRAY_4, TEST_ARRAY_4_ENCODED_NON_ESCAPED); + dleEncoder.setEscapeMode(true); + } + + SECTION("Decoding") { + testLambdaDecode(dleEncoder, TEST_ARRAY_0_ENCODED_ESCAPED, TEST_ARRAY_0); + testLambdaDecode(dleEncoder, TEST_ARRAY_1_ENCODED_ESCAPED, TEST_ARRAY_1); + testLambdaDecode(dleEncoder, TEST_ARRAY_2_ENCODED_ESCAPED, TEST_ARRAY_2); + testLambdaDecode(dleEncoder, TEST_ARRAY_3_ENCODED_ESCAPED, TEST_ARRAY_3); + testLambdaDecode(dleEncoder, TEST_ARRAY_4_ENCODED_ESCAPED, TEST_ARRAY_4); + + // Faulty source data + auto testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_ESCAPED; + testArray1EncodedFaulty[3] = 0; + result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + auto testArray2EncodedFaulty = TEST_ARRAY_2_ENCODED_ESCAPED; + testArray2EncodedFaulty[5] = 0; + result = dleEncoder.decode(testArray2EncodedFaulty.data(), testArray2EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + auto testArray4EncodedFaulty = TEST_ARRAY_4_ENCODED_ESCAPED; + testArray4EncodedFaulty[2] = 0; + result = dleEncoder.decode(testArray4EncodedFaulty.data(), testArray4EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + auto testArray4EncodedFaulty2 = TEST_ARRAY_4_ENCODED_ESCAPED; + testArray4EncodedFaulty2[4] = 0; + result = dleEncoder.decode(testArray4EncodedFaulty2.data(), testArray4EncodedFaulty2.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + + auto testFaultyDecoding = [&](const std::vector& vecToDecode, + const std::vector& expectedVec) { + for(size_t faultyDestSizes = 0; + faultyDestSizes < expectedVec.size(); + faultyDestSizes ++) { + result = dleEncoder.decode(vecToDecode.data(), + vecToDecode.size(), &readLen, + buffer.data(), faultyDestSizes, &decodedLen); + REQUIRE(result == static_cast(DleEncoder::STREAM_TOO_SHORT)); + } + }; + + testFaultyDecoding(TEST_ARRAY_0_ENCODED_ESCAPED, TEST_ARRAY_0); + testFaultyDecoding(TEST_ARRAY_1_ENCODED_ESCAPED, TEST_ARRAY_1); + testFaultyDecoding(TEST_ARRAY_2_ENCODED_ESCAPED, TEST_ARRAY_2); + testFaultyDecoding(TEST_ARRAY_3_ENCODED_ESCAPED, TEST_ARRAY_3); + testFaultyDecoding(TEST_ARRAY_4_ENCODED_ESCAPED, TEST_ARRAY_4); + + dleEncoder.setEscapeMode(false); + testLambdaDecode(dleEncoder, TEST_ARRAY_0_ENCODED_NON_ESCAPED, TEST_ARRAY_0); + testLambdaDecode(dleEncoder, TEST_ARRAY_1_ENCODED_NON_ESCAPED, TEST_ARRAY_1); + testLambdaDecode(dleEncoder, TEST_ARRAY_2_ENCODED_NON_ESCAPED, TEST_ARRAY_2); + testLambdaDecode(dleEncoder, TEST_ARRAY_3_ENCODED_NON_ESCAPED, TEST_ARRAY_3); + testLambdaDecode(dleEncoder, TEST_ARRAY_4_ENCODED_NON_ESCAPED, TEST_ARRAY_4); + + testFaultyDecoding(TEST_ARRAY_0_ENCODED_NON_ESCAPED, TEST_ARRAY_0); + testFaultyDecoding(TEST_ARRAY_1_ENCODED_NON_ESCAPED, TEST_ARRAY_1); + testFaultyDecoding(TEST_ARRAY_2_ENCODED_NON_ESCAPED, TEST_ARRAY_2); + testFaultyDecoding(TEST_ARRAY_3_ENCODED_NON_ESCAPED, TEST_ARRAY_3); + testFaultyDecoding(TEST_ARRAY_4_ENCODED_NON_ESCAPED, TEST_ARRAY_4); + + // Faulty source data + testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED; + auto prevVal = testArray1EncodedFaulty[0]; + testArray1EncodedFaulty[0] = 0; + result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + testArray1EncodedFaulty[0] = prevVal; + testArray1EncodedFaulty[1] = 0; + result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + + testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED; + testArray1EncodedFaulty[6] = 0; + result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + testArray1EncodedFaulty = TEST_ARRAY_1_ENCODED_NON_ESCAPED; + testArray1EncodedFaulty[7] = 0; + result = dleEncoder.decode(testArray1EncodedFaulty.data(), testArray1EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + testArray4EncodedFaulty = TEST_ARRAY_4_ENCODED_NON_ESCAPED; + testArray4EncodedFaulty[3] = 0; + result = dleEncoder.decode(testArray4EncodedFaulty.data(), testArray4EncodedFaulty.size(), + &readLen, buffer.data(), buffer.size(), &encodedLen); + REQUIRE(result == static_cast(DleEncoder::DECODING_ERROR)); + + dleEncoder.setEscapeMode(true); + } +}