diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9eb53ea82bae755ad23a03385aaf44e1a98ebf1e..0d90274bf9e0327fe706ffcfa337b49322e0bb3e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: lycantropos/cmake +image: lightspot21/acubesat-ci:latest variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" @@ -15,7 +15,7 @@ stages: - deploy before_script: - - apt-get update -qq && apt-get -qq -y install python3-pip && python3 -m pip install --upgrade pip + - apt-get update -qq && apt-get -qq -y install libstdc++6 python3-pip && python3 -m pip install --upgrade pip - python3 -V - python3 -m pip --version - g++ --version @@ -54,8 +54,6 @@ tests: cppcheck: stage: build before_script: - - echo deb http://deb.debian.org/debian testing main > /etc/apt/sources.list - - apt-get update -qq && apt-get -t testing install -y cppcheck - cppcheck --version script: - ci/cppcheck.sh @@ -63,9 +61,6 @@ cppcheck: cppcheck-misra: stage: build before_script: - # install cppcheck from the testing repos in order to get the latest version - - echo deb http://deb.debian.org/debian testing main > /etc/apt/sources.list - - apt-get update -qq && apt-get -t testing install -y cppcheck && apt-get -t testing install -y python3 - cppcheck --version script: - ci/cppcheck-misra.sh @@ -73,7 +68,6 @@ cppcheck-misra: vera: stage: build before_script: - - apt-get update -qq && apt-get install -y -qq vera++ - vera++ --version - cp ci/vera.profile /usr/lib/vera++/profiles/custom script: @@ -84,14 +78,8 @@ clang-tidy: variables: GIT_SUBMODULE_STRATEGY: normal TERM: xterm-color - before_script: - # Installing the `sid` repository to get the latest version of clang - - echo deb http://deb.debian.org/debian sid main > /etc/apt/sources.list - - apt-get update -qq && apt-get -t sid install -y -qq clang-tidy-7 - - clang-tidy-7 --version script: - # Running with `script` to give clang a tty so that it outputs colours - - script -c "bash -x ci/clang-tidy.sh" + - ci/clang-tidy.sh pages: stage: deploy @@ -103,8 +91,6 @@ pages: variables: GIT_SUBMODULE_STRATEGY: normal script: - - apt-get install -qq -y doxygen graphviz lcov - after_script: - ./ci/pages_deploy.sh - echo -e "\e[1;36mPublic directory contents\e[0m" && ls -l public/coverage # Print directory contents for debugging artifacts: diff --git a/CMakeLists.txt b/CMakeLists.txt index 59845186d702025b9ab7ce607c58ce8affbd6093..d75c731545f27339a595ce38b1f89a33a99d7c42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,17 @@ add_custom_target(check COMMAND ./cppcheck-misra.sh WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/ci") +# Allow the user to set the ECSS Configuration directory using the -DECSS_CONFIGURATION=... command line argument +set(ECSS_CONFIGURATION "${PROJECT_SOURCE_DIR}/inc/Platform/x86" CACHE PATH + "The include directory for the platform-specific ECSS configuration headers" + ) +include_directories(${ECSS_CONFIGURATION}) + # Specify the .cpp files common across all targets add_library(common OBJECT src/Service.cpp src/ErrorHandler.cpp + src/Logger.cpp src/Message.cpp src/MessageParser.cpp src/ServicePool.cpp @@ -40,7 +47,6 @@ add_library(common OBJECT # Specify the .cpp files for the executables file(GLOB x86_main_SRC "src/Platform/x86/*.cpp") add_executable(ecss_services - src/main.cpp $<TARGET_OBJECTS:common> ${x86_main_SRC} ) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..5d89f392d3d1e9e841f887a9f80e9c4d6350792f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,38 @@ +# This is the Dockerfile for the lightspot21/acubesat-ci:latest +# Docker image used at the pipeline. Please take care to generate +# and push a new image to the lightspot21/acubesat-ci repo every +# 1-2 weeks in order to ensure that the tools are at the latest version. +# +# P.S. Tag properly your images with --tag lightspot21/acubesat-ci when +# building. + +FROM alpine:latest + +# Set a new work directory. DO NOT DELETE THE LINE BELOW +WORKDIR /root/ + +# Set up clang-tidy version 8 +RUN echo "@testing http://dl-cdn.alpinelinux.org/alpine/edge/testing" \ + >> /etc/apk/repositories && apk update && \ + apk add --no-cache --virtual git-deps git && \ + apk add --no-cache build-base cmake && \ + apk add --no-cache python3 && \ + git clone --depth=1 https://github.com/llvm/llvm-project.git -b release/8.x && \ + cmake \ + -DLLVM_ENABLE_PROJECTS="clang-tools-extra;clang" \ + -DCMAKE_BUILD_TYPE=MinSizeRel \ + -DLLVM_TARGETS_TO_BUILD="host" \ + -G "Unix Makefiles" ./llvm-project/llvm && \ + make -j$(nproc) clang-tidy && mv bin/clang-tidy /usr/bin/clang-tidy && \ + rm -rf * && apk del git-deps + +# Update package lists and install cmake, cppcheck, doxygen, vera++, +# gcc and lcov with their dependencies +RUN apk add --no-cache findutils python3-dev \ + cppcheck doxygen vera++@testing lcov@testing + +# Install gcovr +RUN python3 -m pip install gcovr + +# Start a new shell +ENTRYPOINT ["/bin/sh", "-c"] diff --git a/ci/.clang-tidy b/ci/.clang-tidy index 38beddf8e90c12aa96ede94f6d80bf27cc253aef..478e052795977bbfe87cab6a8dc73eeeee60e665 100644 --- a/ci/.clang-tidy +++ b/ci/.clang-tidy @@ -4,7 +4,7 @@ Checks: > clang-analyzer-*, bugprone-*, cert-*, - cppcoreguidelines-*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-bounds-constant-array-index, + cppcoreguidelines-*,-cppcoreguidelines-pro-bounds-array-to-pointer-decay,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-cppcoreguidelines-pro-type-reinterpret-cast,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-avoid-c-arrays, misc-*,-misc-non-private-member-variables-in-classes, fuchsia-multiple-inheritance, google-*,-google-readability-todo, @@ -17,7 +17,7 @@ Checks: > misc-*, -misc-non-private-member-variables-in-classes, performance-*, - readability-*, + readability-*,-readability-magic-numbers, zircon-* WarningsAsErrors: '*,-misc-unused-parameters,-llvm-header-guard,-cppcoreguidelines-pro-type-member-init,-google-runtime-references,-clang-diagnostic-tautological-constant-out-of-range-compare,-readability-redundant-declaration,-modernize-use-equals-default,-fuchsia-statically-constructed-objects,-hicpp-signed-bitwise,-cert-err58-cpp,-clang-diagnostic-error,-misc-noexcept-move-constructor' HeaderFilterRegex: 'ecss-services\/((?!lib\/).)*$' diff --git a/ci/clang-tidy.sh b/ci/clang-tidy.sh index bdff8166981a4312ab0a2c28423a5359af80ad59..3ac4810e6a55343c491976dff0e11eac2a146689 100755 --- a/ci/clang-tidy.sh +++ b/ci/clang-tidy.sh @@ -10,6 +10,10 @@ echo -e "\033[0;34mRunning clang-tidy...\033[0m" cd "$(dirname "$0")" -clang-tidy-7 `find ../src/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \ - -extra-arg=-fcolor-diagnostics -- -std=c++17 -I../inc \ - -I/usr/include/c++/7/ -I/usr/include/x86_64-linux-gnu/c++/7 -I../lib/etl/include +GCCVERSION=`g++ -dumpversion` + +clang-tidy `find ../src/ -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` \ + -extra-arg=-fcolor-diagnostics -- -std=c++17 -I../inc -I../lib/etl/include -I../inc/Platform/x86 \ + -I/usr/include/c++/$GCCVERSION -I/usr/include/x86_64-linux-gnu/c++/$GCCVERSION \ + -I/usr/include/c++/$GCCVERSION/$MACHTYPE + diff --git a/ci/cppcheck-misra.sh b/ci/cppcheck-misra.sh index 737eb6f217aeb121635dd5cf225b4d3121f1c7e9..b1313b80c7e7cee69cd69e42c85dd2fee6424223 100755 --- a/ci/cppcheck-misra.sh +++ b/ci/cppcheck-misra.sh @@ -11,9 +11,12 @@ cd "$(dirname "$0")/.." echo -e "\u001b[34;1mGetting prerequisites...\u001b[0m" +# get the current cppcheck version +CPCVERSION=`cppcheck --version | awk '{print $2}'` + # grab the MISRA addon and the cppcheck addon interface from github -curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/misra.py > ci/misra.py -curl https://raw.githubusercontent.com/danmar/cppcheck/master/addons/cppcheckdata.py > ci/cppcheckdata.py +curl https://raw.githubusercontent.com/danmar/cppcheck/$CPCVERSION/addons/misra.py > ci/misra.py +curl https://raw.githubusercontent.com/danmar/cppcheck/$CPCVERSION/addons/cppcheckdata.py > ci/cppcheckdata.py # generate dump files (XML representations of AST etc.) for all headers, source files etc. echo -e "\u001b[34;1mGenerating dump files...\u001b[0m" @@ -30,8 +33,13 @@ sed -i -r 's/(.*Script.*)|(.*Checking.*)|(.*MISRA.*)|(.*Undefined: .*)|(.* \(-\) # run the summarizer for a nice, clean summary of errors echo -e "\u001b[34;1mSummarizing results...\u001b[0m" python3 ci/summarizer.py --report ci/report.msr --suppress 3.1 5.1 5.2 5.3 12.3 13.4 14.4 15.5 16.3 18.4 18.8 +EXIT_CODE=$? # clean up old files echo -e "\u001b[34;1mRemoving dump files...\u001b[0m" echo > ci/report.msr # clear the report file find inc/ src/ -type f -name "*.dump" | xargs rm -rf + +# finally return the return value of the summarizer.py script +exit $EXIT_CODE + diff --git a/ci/cppcheck.sh b/ci/cppcheck.sh index 2179a051c88e90de9c07d7f9898422c4c38beb6f..d0be486ccf6049895d93a76f070687dcb5c2e775 100755 --- a/ci/cppcheck.sh +++ b/ci/cppcheck.sh @@ -13,4 +13,4 @@ echo -e "\u001b[34;1mRunning cppcheck with default checklist...\u001b[0m" cd "$(dirname "$0")/.." cppcheck --enable=all --suppress=unusedFunction --suppress=noExplicitConstructor \ - --inline-suppr --error-exitcode=1 -I inc src test + --force --inline-suppr --error-exitcode=1 -I inc src test diff --git a/ci/summarizer.py b/ci/summarizer.py index 15e48d03c61bffd4caeb8c43804393abbf00016e..5139df77ab8d09d9c8acf4d90b3cd3471023f3dd 100755 --- a/ci/summarizer.py +++ b/ci/summarizer.py @@ -58,7 +58,7 @@ class Summarizer(object): with open(os.path.abspath(file_name)) as code_file: code_lines = code_file.readlines() # Read the source code file line_of_interest = code_lines[int(violation[0]) - 1] # Get the desired violation line - if line_of_interest.find("// Ignore-MISRA") >= 0: + if line_of_interest.find("// Ignore-MISRA") >= 0 or line_of_interest.find("/* Ignore-MISRA */") >= 0: continue if file_name not in self.violations_map.keys(): diff --git a/ci/vera.sh b/ci/vera.sh index 4481cfb2b2b49d904725a211d4c58b51fe4c4048..2ece81da50bfefdefba08effef4bbadc3f7d415f 100755 --- a/ci/vera.sh +++ b/ci/vera.sh @@ -10,4 +10,5 @@ echo -e "\033[0;34mRunning vera++...\033[0m" cd "$(dirname "$0")/.." -vera++ --error --parameter max-line-length=120 --profile custom `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` +vera++ --error --parameter max-line-length=120 --profile custom \ + `find src inc test -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)' -not -wholename 'inc/Logger.hpp'` diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 184e969afa195bf6fc1003da4d745abd5255e4fd..c59a2624d5c4ba9b77f3dc68db1aa4284b0f4d2d 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -1,19 +1,66 @@ #ifndef ECSS_SERVICES_ECSS_DEFINITIONS_H #define ECSS_SERVICES_ECSS_DEFINITIONS_H -#define ECSS_MAX_MESSAGE_SIZE 1024u +/** + * @defgroup ECSSDefinitions ECSS Defined Constants + * + * This file contains constant definitions that are used throughout the ECSS services. They often refer to maximum + * values and upper limits for the storage of data in the services. + * + * @todo All these constants need to be redefined and revised after the design and the requirements are finalized. + * + * @{ + */ + +/** + * @file + * This file contains constant definitions that are used throughout the ECSS services. + * @see ECSSDefinitions + */ + +/** + * The maximum size of a regular ECSS message, in bytes + */ +#define ECSS_MAX_MESSAGE_SIZE 1024U + + +/** + * The maximum size of a regular ECSS message, plus its headers and trailing data, in bytes + */ +#define CCSDS_MAX_MESSAGE_SIZE (ECSS_MAX_MESSAGE_SIZE + 6u + 6u + 2u) -#define ECSS_MAX_STRING_SIZE 256u +/** + * The maximum size of a string to be read or appended to a Message, in bytes + * + * This is used by the Message::appendString() and Message::readString() functions + */ +#define ECSS_MAX_STRING_SIZE 256U + +/** + * The maximum size of a string to be used by ST[13] \ref LargePacketTransferService, in bytes + * + * This is used by the Message::appendString() and Message::readString() functions + */ +#define ECSS_MAX_FIXED_OCTET_STRING_SIZE 256U -#define ECSS_MAX_FIXED_OCTET_STRING_SIZE 256u // For the ST13 large packet transfer service +/** + * The total number of different message types that can be handled by this project + */ +#define ECSS_TOTAL_MESSAGE_TYPES (10U * 20U) -// 7.4.1 +/** + * The CCSDS packet version, as specified in section 7.4.1 + */ #define CCSDS_PACKET_VERSION 0 -// 7.4.4.1c +/** + * The ECSS packet version, as specified in requirement 7.4.4.1c + */ #define ECSS_PUS_VERSION 2U -// 9.3.1a.1.e +/** + * The CCSDS sequence flags have the constant value 0x3, as specified in section 7.4.1 + */ #define ECSS_SEQUENCE_FLAGS 0x3 /** @@ -24,7 +71,7 @@ * their message request. * @attention This definition is probably dependent on the ECSS_TC_REQUEST_STRING_SIZE */ -#define ECSS_MAX_REQUEST_COUNT 20 // todo: Needs to be defined +#define ECSS_MAX_REQUEST_COUNT 20 /** * @brief Maximum length of a String converted TC packet message @@ -33,25 +80,66 @@ */ #define ECSS_TC_REQUEST_STRING_SIZE 64 -// todo: Define the maximum number of activities +/** + * The maximum number of activities that can be in the time-based schedule + * @see TimeBasedSchedulingService + */ #define ECSS_MAX_NUMBER_OF_TIME_SCHED_ACTIVITIES 10 /** * @brief Time margin used in the time based command scheduling service ST[11] * @details This defines the time margin in seconds, from the current rime, that an activity must * have in order - * @todo Define the time margin for the command activation + * @see TimeBasedSchedulingService */ #define ECSS_TIME_MARGIN_FOR_ACTIVATION 60 +/** + * @brief Maximum size of an event's auxiliary data + * @see EventReportService + */ +#define ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE 64 + /** * @brief Size of the multimap that holds every event-action definition + * @see EventActionService */ #define ECSS_EVENT_ACTION_STRUCT_MAP_SIZE 256 -// todo: Define the maximum delta between the specified +/** + * The maximum delta between the specified release time and the actual release time + * @see TimeBasedSchedulingService + */ #define ECSS_MAX_DELTA_OF_RELEASE_TIME 60 -// release time and the actual release time + +/** + * The maximum number of stored parameters in the \ref ParameterService + */ +#define ECSS_MAX_PARAMETERS 5 + +/** + * The number of functions supported by the \ref FunctionManagementService + */ +#define ECSS_FUNCTION_MAP_SIZE 5 + +/** + * The maximum length of a function name, in bytes + * @see FunctionManagementService + */ +#define ECSS_FUNCTION_NAME_LENGTH 32 + +/** + * The maximum length of the argument of a function + * @see FunctionManagementService + */ +#define ECSS_FUNCTION_MAX_ARG_LENGTH 32 + +/** @} */ + +/** + * @brief The maximum size of a log message + */ +#define LOGGER_MAX_MESSAGE_SIZE 512 /** * @brief Size of the map holding the Parameter objects for the ST[20] parameter service diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index 5dd1c6fb2064dd31a0ac893dda282493bf69b9a3..07510f25615205b3c3d46e226f10ed5bef065570 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -76,7 +76,11 @@ public: /** * Attempt to overwrite an existing parameter (ST[20]) */ - ExistingParameterId = 11 + ExistingParameterId = 11, + /** + * A Message that is included within another message is too large + */ + NestedMessageTooLarge = 12 }; /** @@ -229,11 +233,17 @@ public: * Reports a failure that occurred internally, not due to a failure of a received packet. * * Creates an error if \p condition is false. The created error is Internal. + * + * @param condition The condition to check. Throws an error if false. + * @param errorCode The error code that is assigned to this error. One of the \ref ErrorHandler enum values. + * @return Returns \p condition, i.e. true if the assertion is successful, false if not. */ - static void assertInternal(bool condition, InternalErrorType errorCode) { + static bool assertInternal(bool condition, InternalErrorType errorCode) { if (not condition) { reportInternalError(errorCode); } + + return condition; } /** @@ -242,12 +252,19 @@ public: * Reports a failure that occurred while processing a request, in any of the process phases. * * Creates an error if \p condition is false. The created error corresponds to a \p message. + * + * @param condition The condition to check. Throws an error if false. + * @param message The message to associate with this error + * @param errorCode The error code that is assigned to this error. One of the \ref ErrorHandler enum values. + * @return Returns \p condition, i.e. true if the assertion is successful, false if not. */ template <typename ErrorType> - static void assertRequest(bool condition, const Message& message, ErrorType errorCode) { + static bool assertRequest(bool condition, const Message& message, ErrorType errorCode) { if (not condition) { reportError(message, errorCode); } + + return condition; } /** diff --git a/inc/Helpers/TimeHelper.hpp b/inc/Helpers/TimeHelper.hpp index 7cd1920d5343fa6b8186f3c377369462a1cc8a91..027e078e817bf6748ac693821d7527a9e5c7c811 100644 --- a/inc/Helpers/TimeHelper.hpp +++ b/inc/Helpers/TimeHelper.hpp @@ -5,9 +5,9 @@ #include <Message.hpp> #include "TimeAndDate.hpp" -#define SECONDS_PER_MINUTE 60u -#define SECONDS_PER_HOUR 3600u -#define SECONDS_PER_DAY 86400u +#define SECONDS_PER_MINUTE 60U +#define SECONDS_PER_HOUR 3600U +#define SECONDS_PER_DAY 86400U /** * @todo If we use CUC time format then we should keep leap seconds up to date. Leap seconds are added in undefined @@ -33,7 +33,7 @@ * decides what the time unit will be, so this is a subject for discussion. The recommended time unit from the * standard is the second and it is probably the best solution for accuracy. * - * @notes + * @note * The defined epoch for both time formats is 1 January 1958 00:00:00 * * Since CDS format is UTC-based, the leap second correction must be made. The leap seconds that @@ -56,7 +56,7 @@ public: /** * Convert UTC date to elapsed seconds since Unix epoch (1/1/1970 00:00:00). * - * This is a reimplemented mktime() of <ctime> library in an embedded systems way + * This is a reimplemented `mktime()` of the `<ctime>` library in an embedded compatible way * * @note * This function can convert UTC dates after 1 January 2019 to elapsed seconds since Unix epoch @@ -71,7 +71,7 @@ public: /** * Convert elapsed seconds since Unix epoch to UTC date. * - * This is a reimplemented gmtime() of <ctime> library in an embedded systems way + * This is a reimplemented `gmtime()` of the `<ctime>` library in an embedded compatible way * * @note * This function can convert elapsed seconds since Unix epoch to UTC dates after 1 January 2019 @@ -110,7 +110,7 @@ public: * * Converts a UTC date to CUC time format. * - * @notes + * @note * The T-field is specified for the seconds passed from the defined epoch 1 January 1958. We use 4 octets(32 * bits) for the time unit (seconds) because 32 bits for the seconds are enough to count 136 years! But if we use 24 * bits for the seconds then it will count 0,5 years and this isn't enough. Remember we can use only integers diff --git a/inc/Logger.hpp b/inc/Logger.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b5e4b7f21546360ce2d71194b6fc2c7eae4a7cd3 --- /dev/null +++ b/inc/Logger.hpp @@ -0,0 +1,225 @@ +#ifndef ECSS_SERVICES_LOGGER_HPP +#define ECSS_SERVICES_LOGGER_HPP + +#include <cstdint> +#include <etl/String.hpp> +#include <etl/to_string.h> +#include "ECSS_Configuration.hpp" +#include "ECSS_Definitions.hpp" + +#if defined LOGLEVEL_TRACE +#define LOGLEVEL Logger::trace // Ignore-MISRA +#elif defined LOGLEVEL_DEBUG +#define LOGLEVEL Logger::debug // Ignore-MISRA +#elif defined LOGLEVEL_INFO +#define LOGLEVEL Logger::info // Ignore-MISRA +#elif defined LOGLEVEL_NOTICE +#define LOGLEVEL Logger::notice // Ignore-MISRA +#elif defined LOGLEVEL_WARNING +#define LOGLEVEL Logger::warning // Ignore-MISRA +#elif defined LOGLEVEL_ERROR +#define LOGLEVEL Logger::error // Ignore-MISRA +#elif defined LOGLEVEL_EMERGENCY +#define LOGLEVEL Logger::emergency // Ignore-MISRA +#elif !defined LOGLEVEL +#define LOGLEVEL Logger::disabled // Ignore-MISRA +#endif + +#define LOG_TRACE (LOG<Logger::trace>()) ///< @see LOG @relates Logger +#define LOG_DEBUG (LOG<Logger::debug>()) ///< @see LOG @relates Logger +#define LOG_INFO (LOG<Logger::info>()) ///< @see LOG @relates Logger +#define LOG_NOTICE (LOG<Logger::notice>()) ///< @see LOG @relates Logger +#define LOG_WARNING (LOG<Logger::warning>()) ///< @see LOG @relates Logger +#define LOG_ERROR (LOG<Logger::error>()) ///< @see LOG @relates Logger +#define LOG_EMERGENCY (LOG<Logger::emergency>()) ///< @see LOG @relates Logger + +/** + * A logging class for ECSS Services that supports ETL's String and is lightweight enough to be used in embedded + * development. + * + * @note Always use the \ref LOG function and its associated utility macros to log. Do not directly use the Logger + * class. + */ +class Logger { +public: + /** + * No need to instantiate a Logger object for now. + */ + Logger() = delete; + + /** + * The underlying type to be used for values of Logger::LogLevel. + */ + typedef uint8_t LogLevelType; + + /** + * ETL's string format specification, to be used for all logged messages + */ + static etl::format_spec format; + + /** + * Log levels supported by the logger. Each level represents a different severity of the logged Message, + * and messages of lower severities can be filtered on top of more significant ones. + * + * Each severity is tied to a number. The higher the number, the higher the severity. + */ + enum LogLevel : LogLevelType { + trace = 32, ///< Very detailed information, useful for tracking the individual steps of an operation + debug = 64, ///< General debugging information + info = 96, ///< Noteworthy or periodical events + notice = 128, ///< Uncommon but expected events + warning = 160, ///< Unexpected events that do not compromise the operability of a function + error = 192, ///< Unexpected failure of an operation + emergency = 254, ///< Unexpected failure that renders the entire system unusable + disabled = 255, ///< Use this log level to disable logging entirely. No message should be logged as disabled. + }; + + /** + * An empty enum representing a dummy log entry that will not be logged due to an insufficient level. + * + * @internal + */ + enum class NoLogEntry {}; + + /** + * A class that defines a log message. + * + * Instead of using this class, prefer one of the above macros. + * @see LOG + * @internal + */ + struct LogEntry { + String<LOGGER_MAX_MESSAGE_SIZE> message = ""; ///< The current log message itself, starting from a blank slate + LogLevel level; ///< The log level of this message + + explicit LogEntry(LogLevel level); ///< Create a new LogEntry + + /** + * The LogEntry destructor gets called whenever a log message is finalized, and ready to be shown to the + * user. This function is responsible for calling the Logger::log function. + * + * According to the C++ standard, a variable used only within an expression will be immediately destroyed once + * the processing of this expression is over. This allows a syntax such as `LogEntry(...) << "some" << "text"`, + * where the destructor will be called strictly **after** all the `<<` operations have been completed. This + * allows the destructor to send the finalized log entry for further processing. + */ + ~LogEntry(); + + LogEntry(LogEntry const&) = delete; ///< Unimplemented copy constructor + + /** + * Stream operator to append new values to a log record + * + * @tparam T The type of value to append + * @param value The new value to add + * @todo See if noexcept can be added here without triggering warnings + * @return The current Logger::LogEntry where the value has been appended + */ + template <class T> + Logger::LogEntry& operator<<(const T value) { + etl::to_string(value, message, format, true); + + return *this; + } + + Logger::LogEntry& operator<<(const std::string& value); + }; + + /** + * Returns whether a log entry of level \p level is logged, based on the compilation constants + * @param level The level of the log entry + * @return True if the logging is enabled for \p level, false if not + */ + static constexpr bool isLogged(LogLevelType level) { + return static_cast<LogLevelType>(LOGLEVEL) <= level; + } + + /** + * Store a new log message + */ + static void log(LogLevel level, etl::istring & message); +}; + +/** + * Create a stream to log a Message + * + * This functions appends one line to the Logs (which could be printed to screen, transferred via UART or stored for + * later use.) + * + * Examples of usage: + * @code + * LOG<Logger::debug>() << "Reached point of no return"; + * LOG<Logger::error>() << "More than " << 50 << " dogs found!"; + * @endcode + * + * You can also use one of the \ref LOG_TRACE, \ref LOG_DEBUG, \ref LOG_INFO, \ref LOG_NOTICE, \ref LOG_WARNING, + * \ref LOG_ERROR or \ref LOG_EMERGENCY defines, which avoid the need of explicitly passing the log level: + * @code + * LOG_DEBUG << "Reached point of no return"; + * LOG_ERROR << "More than " << 50 << " dogs found!"; + * @endcode + * + * See \ref Logger::LogLevel for an explanation of the different log levels. + * + * @par Implementation details + * Functions here are defined as `constexpr` in order to let them be optimized as soon as possible. The LOG() + * function returns an instance of \ref Logger::LogEntry if the level is high enough to be shown, or an instance of + * \ref Logger::NoLogEntry if the log entry will not be displayed. As this is a templated function, it is acceptable + * to support different return types using the `auto` keyword. + * + * @warning For messages that will not be logged, any calls to functions that contain **side effects will still take + * place**. + * @code + * LOG_DEBUG << "The temperature is: " << getTemperature(); + * @endcode + * In the above example, if `getTemperature()` will cause a side effect (e.g. an I2C connection or a `std::cout` print), + * it will still be executed, even if the debug message will not be printed to the screen due to an insufficient + * LOGLEVEL. + * + * @section GlobalLogLevels Global log levels + * The **global log level** defines the minimum severity of events to be displayed. Log entries with a severity equal + * to or higher than the global log level will be shown. Log entries with a severity smaller than the global log level + * will not be shown. + * + * The global log level can be set by defining one of the following constants: + * - `LOGLEVEL_TRACE` + * - `LOGLEVEL_DEBUG` + * - `LOGLEVEL_INFO` + * - `LOGLEVEL_NOTICE` + * - `LOGLEVEL_WARNING` + * - `LOGLEVEL_ERROR` + * - `LOGLEVEL_EMERGENCY` + * + * @relates Logger + * @tparam level The log level. A value of \ref Logger::LogLevel + * @return Returns \ref Logger::LogEntry if the level is sufficient to be logged, or \ref Logger::NoLogEntry if the + * message will not be logged. This is determined at compile-time. + */ +template <Logger::LogLevel level> +constexpr __attribute__((always_inline)) inline auto LOG() { + if constexpr (Logger::isLogged(level)) { + return Logger::LogEntry(level); + } else { + return Logger::NoLogEntry(); + } +}; + +/** + * A no-op function that considers an empty log entry that will not be displayed, processed or stored. + * + * @warning Note that functions containing **side effects** will get properly executed. Only use functions that return + * plain values as parts of the log function, so they might be optimized away at compile time. + * + * @tparam T The type of the data that will be ignored + * @param noLogEntry A dummy no-op log entry + * @param value The data that will be ignored + * @return A dummy no-op log entry + * @see Logger::LogEntry::operator<<(const T value) + * @relates Logger::LogEntry + */ +template <typename T> +[[maybe_unused]] constexpr Logger::NoLogEntry operator<<(const Logger::NoLogEntry noLogEntry, T value) { + return noLogEntry; +} + +#endif //ECSS_SERVICES_LOGGER_HPP diff --git a/inc/Message.hpp b/inc/Message.hpp index 6cd0ef1d73f06c6a2dda9fad42d17c8f4c86f3e4..f94c31bea5cf52138caec588a6f2ec3803794459 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -33,29 +33,46 @@ public: /** * @brief Overload the equality operator to compare messages * @details Compare two @ref ::Message objects, based on their contents and type - * @param The message content to compare against - * @todo Activate the dataSize check when the Message object data field is defined with a - * fixed size + * @param msg The message content to compare against * @return The result of comparison */ bool operator==(const Message& msg) const { - // todo: Enable the following check when the message data field has a fixed size padded - // with zeros. At the moment the data array is not padded with zeros to fulfil the - // maximum set number of a TC request message. - // if (this->dataSize != msg.dataSize) return false; - - for (uint16_t i = 0; i < ECSS_MAX_MESSAGE_SIZE; i++) { - if (this->data[i] != msg.data[i]) { - return false; - } + if (dataSize != msg.dataSize) { + return false; } - return (this->packetType == msg.packetType) && (this->messageType == msg.messageType) && - (this->serviceType == msg.serviceType); + + if (not isSameType(*this, msg)) { + return false; + } + + return std::equal(data, data + dataSize, msg.data); + } + + /** + * Checks the first \ref Message::dataSize bytes of \p msg for equality + * + * This performs an equality check for the first `[0, this->dataSize)` bytes of two messages. Useful to compare + * two messages that have the same content, but one of which does not know its length. + * + * @param msg The message to check. Its `dataSize` must be smaller than the object calling the function + * @return False if the messages are not of the same type, if `msg.dataSize < this->dataSize`, or if the first + * `this->dataSize` bytes are not equal between the two messages. + */ + bool bytesEqualWith(const Message& msg) const { + if (msg.dataSize < dataSize) { + return false; + } + + if (not isSameType(*this, msg)) { + return false; + } + + return std::equal(data, data + dataSize, msg.data); } enum PacketType { - TM = 0, // Telemetry - TC = 1 // Telecommand + TM = 0, ///< Telemetry + TC = 1 ///< Telecommand }; // The service and message IDs are 8 bits (5.3.1b, 5.3.3.1d) @@ -84,7 +101,9 @@ public: // Pointer to the contents of the message (excluding the PUS header) // We allocate this data statically, in order to make sure there is predictability in the // handling and storage of messages - // TODO: Is it a good idea to not initialise this to 0? + // + // @note This is initialized to 0 in order to prevent any mishaps with non-properly initialized values. \ref + // Message::appendBits() relies on this in order to easily OR the requested bits. uint8_t data[ECSS_MAX_MESSAGE_SIZE] = { 0 }; // private: @@ -108,7 +127,7 @@ public: * multiple of the padding word size declared for the application process * @todo Confirm that the overall packet size is an integer multiple of the padding word size * declared for every application process - * @todo check if wee need to define the spare field for the telemetry and telecommand + * @todo check if we need to define the spare field for the telemetry and telecommand * secondary headers */ void finalize(); @@ -140,8 +159,7 @@ public: * * @param string The string to insert */ - template <const size_t SIZE> - void appendString(const String<SIZE>& string); + void appendString(const etl::istring& string); /** * Appends a number of bytes to the message @@ -156,8 +174,7 @@ public: * * @param string The string to insert */ - template <const size_t SIZE> - void appendFixedString(const String<SIZE>& string); + void appendFixedString(const etl::istring& string); /** * Reads the next \p numBits bits from the the message in a big-endian format @@ -339,14 +356,18 @@ public: * * PTC = 7, PFC = 0 */ - template <const size_t SIZE> - void appendOctetString(const String<SIZE>& string) { - // Make sure that the string is large enough to count - ASSERT_INTERNAL(string.size() <= (std::numeric_limits<uint16_t>::max)(), ErrorHandler::StringTooLarge); + void appendOctetString(const etl::istring& string); - appendUint16(string.size()); - appendString(string); - } + /** + * Adds a nested TC or TM Message within the current Message + * + * As a design decision, nested TC & TM Messages always have a fixed width, specified in \ref ECSSDefinitions. This + * reduces the uncertainty and complexity of having to parse the nested Message itself to see how long it is, at + * the cost of more data to be transmitted. + * @param message The message to append + * @param size The fixed number of bytes that the message will take up. The empty last bytes are padded with 0s. + */ + void appendMessage(const Message & message, uint16_t size); /** * Fetches a single-byte boolean value from the current position in the message @@ -543,25 +564,4 @@ public: } }; -template <const size_t SIZE> -inline void Message::appendString(const String<SIZE>& string) { - ASSERT_INTERNAL(dataSize + string.size() < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); - // TODO: Do we need to keep this check? How does etl::string handle it? - ASSERT_INTERNAL(string.size() < string.capacity(), ErrorHandler::StringTooLarge); - - memcpy(data + dataSize, string.data(), string.size()); - - dataSize += string.size(); -} - -template <const size_t SIZE> -inline void Message::appendFixedString(const String<SIZE>& string) { - ASSERT_INTERNAL((dataSize + SIZE) < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); - - memcpy(data + dataSize, string.data(), string.size()); // Append the bytes with content - (void) memset(data + dataSize + string.size(), 0, SIZE - string.size()); // The rest of the bytes is set to 0 - - dataSize += SIZE; -} - #endif // ECSS_SERVICES_PACKET_H diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp index b3b1f0ded171bdcab16489dbcbc06282a9f7a655..82517e8203cd2eb0735e3625c82ef97b9eb0646f 100644 --- a/inc/MessageParser.hpp +++ b/inc/MessageParser.hpp @@ -7,17 +7,36 @@ /** * A generic class responsible for the execution and the parsing of the incoming telemetry and telecommand * packets + * + * This class is responsible for converting Packets and Messages to and from the internal representation used in this + * project. The following hierarchy is used between the different layers on ecss-services: + * + * \code + * ------------------- + * | User data field | + * ------------------- + * --------------------------- Application Layer + * | Packet secondary header | + * | (ECSS header) | + * --------------------------- -------------------------------- + * ------------------------- + * | Packet primary header | + * | (CCSDS header) | Network Layer + * ------------------------- + * \endcode + * + * The service data is encapsulated within the, **ECSS packet** which is encapsulated within the **CCSDS packet**. + * The MessageParser class is responsible for adding and processing both the ECSS and CCSDS headers. The target it uses + * for the internal representation of all received Telemetry (TM) and Telecommands (TC) is the \ref Message class. */ class MessageParser { public: /** - * This function takes as input TC packets and and calls the proper services' functions that have been + * This function takes as input TC packets and calls the proper services' functions that have been * implemented to handle TC packets. * - * @param Message Contains the necessary parameters to call the suitable subservice - * @todo Implement the execute() in the upcoming services or generally in the upcoming - * activities + * @param message Contains the necessary parameters to call the suitable subservice */ static void execute(Message& message); @@ -30,34 +49,40 @@ public: * @param length The size of the message * @return A new object that represents the parsed message */ - Message parse(uint8_t* data, uint32_t length); + static Message parse(uint8_t* data, uint32_t length); /** - * @todo: elaborate on this comment - * Create a message so that a string can be parsed + * Parse data that contains the ECSS packet header, without the CCSDS space packet header * * Note: conversion of char* to unsigned char* should flow without any problems according to * this great analysis: * stackoverflow.com/questions/15078638/can-i-turn-unsigned-char-into-char-and-vice-versa */ - Message parseRequestTC(String<ECSS_TC_REQUEST_STRING_SIZE> data); + static Message parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data); /** - * @brief Overloaded version + * @brief Overloaded version of \ref MessageParser::parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data) * @param data A uint8_t array of the TC packet data * @return Parsed message */ - Message parseRequestTC(uint8_t* data); + static Message parseECSSTC(uint8_t* data); /** - * @brief Converts a TC packet of type Message to a String - * @details Convert a parsed TC message to a string in order to be used by the services + * @brief Converts a TC or TM message to a message string, appending just the ECSS header + * @todo Add time reference, as soon as it is available and the format has been specified * @param message The Message object to be parsed to a String - * @return A String class containing the parsed TC request - * @attention The returned String has a fixed size, therefore the message size is considered - * fixed and equal to the ECSS_TC_REQUEST_STRING_SIZE definition. + * @param size The wanted size of the message (including the headers). Messages larger than \p size display an + * error. Messages smaller than \p size are padded with zeros. When `size = 0`, there is no size limit. + * @return A String class containing the parsed Message */ - String<ECSS_TC_REQUEST_STRING_SIZE> convertTCToStr(Message& message); + static String<CCSDS_MAX_MESSAGE_SIZE> composeECSS(const Message& message, uint16_t size = 0u); // Ignore-MISRA + + /** + * @brief Converts a TC or TM message to a packet string, appending the ECSS and then the CCSDS header + * @param message The Message object to be parsed to a String + * @return A String class containing the parsed Message + */ + static String<CCSDS_MAX_MESSAGE_SIZE> compose(const Message& message); private: /** @@ -69,7 +94,7 @@ private: * @param length The size of the header * @param message The Message to modify based on the header */ - void parseTC(const uint8_t* data, uint16_t length, Message& message); + static void parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message); /** * Parse the ECSS Telemetry packet secondary header @@ -80,7 +105,7 @@ private: * @param length The size of the header * @param message The Message to modify based on the header */ - void parseTM(const uint8_t* data, uint16_t length, Message& message); + static void parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message); }; #endif // ECSS_SERVICES_MESSAGEPARSER_HPP diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a627d61ae8dcb1790d5c6db133c18096a1d6f7f4 --- /dev/null +++ b/inc/Platform/x86/ECSS_Configuration.hpp @@ -0,0 +1,40 @@ +#ifndef ECSS_SERVICES_ECSS_CONFIGURATION_HPP +#define ECSS_SERVICES_ECSS_CONFIGURATION_HPP + +/** + * @file + * The configuration file for this ECSS Services platform. + * + * @see GlobalLogLevels Define the minimum level for logged messages + * @see ServiceDefinitions Define the service types that will be compiled + */ + +// Log all levels of messages +#define LOGLEVEL_TRACE + +/** + * @defgroup ServiceDefinitions Service compilation switches + * These preprocessor defines control whether the compilation of each ECSS service is enabled. By not defining one of + * those, the service will not be compiled, and no RAM or ROM will be spent storing it. + * + * Define these in the `ECSS_Configuration.hpp` file of your platform. + * @{ + */ + +#define SERVICE_ALL ///< Enables compilation of all the ECSS services + +#ifdef SERVICE_ALL +#define SERVICE_EVENTACTION ///< Compile ST[19] event-action +#define SERVICE_EVENTREPORT ///< Compile ST[05] event reporting +#define SERVICE_FUNCTION ///< Compile ST[08] function management +#define SERVICE_LARGEPACKET ///< Compile ST[13] large packet transfer +#define SERVICE_MEMORY ///< Compile ST[06] memory management +#define SERVICE_PARAMETER ///< Compile ST[20] parameter management +#define SERVICE_REQUESTVERIFICATION ///< Compile ST[01] request verification +#define SERVICE_TEST ///< Compile ST[17] test +#define SERVICE_TIME ///< Compile ST[09] time management +#define SERVICE_TIMESCHEDULING ///< Compile ST[11] time-based scheduling +#endif +/** @} */ + +#endif //ECSS_SERVICES_ECSS_CONFIGURATION_HPP diff --git a/inc/Platform/x86/TimeGetter.hpp b/inc/Platform/x86/TimeGetter.hpp index 7cb15d7389c5b3a7e3c9f69de719ca2ac0332c3e..d5da817d62de9548c355a0e757c8ebc275e40614 100644 --- a/inc/Platform/x86/TimeGetter.hpp +++ b/inc/Platform/x86/TimeGetter.hpp @@ -1,7 +1,6 @@ #ifndef ECSS_SERVICES_TIMEGETTER_HPP #define ECSS_SERVICES_TIMEGETTER_HPP -#include <iostream> #include <cstdint> #include <ctime> diff --git a/inc/Service.hpp b/inc/Service.hpp index 32e77e19387cd0b74236c082ffc271f3557535b7..53bf0bae49366814e9e8bff2a0d1436abbb877c8 100644 --- a/inc/Service.hpp +++ b/inc/Service.hpp @@ -3,17 +3,20 @@ #include <cstdint> #include "Message.hpp" -#include <iostream> // This file should be removed class ServicePool; +/** + * @defgroup Services Services + * ECSS Services implementations, as defined in ECSS-E-ST-70-41C. These services receive TC Messages, and output TM + * Messages. + */ + /** * A spacecraft service, as defined in ECSS-E-ST-70-41C * * A member of the Service class should be used as a singleton, i.e. must be created only once in * the code - * - * @todo Disable copy constructor */ class Service { private: @@ -33,7 +36,6 @@ protected: * the TC[17,3] message has `messageType = 3`. * @todo See if the Message must be returned by reference * @todo Set the application ID to the current application - * @todo Use the messageTypeCounter */ Message createTM(uint8_t messageType) { return Message(serviceType, messageType, Message::TM, 0); diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp index 97fbed7ff13ef836ad9de9da68f9c5d544050e7d..51c1fa201b856c3f1b0225113c1e60860f36ff1a 100644 --- a/inc/ServicePool.hpp +++ b/inc/ServicePool.hpp @@ -1,7 +1,8 @@ #ifndef ECSS_SERVICES_SERVICEPOOL_HPP #define ECSS_SERVICES_SERVICEPOOL_HPP -#include <Services/TimeBasedSchedulingService.hpp> +#include "ECSS_Configuration.hpp" +#include "Services/TimeBasedSchedulingService.hpp" #include "Services/LargePacketTransferService.hpp" #include "Services/RequestVerificationService.hpp" #include "Services/TimeManagementService.hpp" @@ -16,21 +17,61 @@ * Defines a class that contains instances of all Services. * * All Services should be stored here and should not be instantiated in a different way. - * - * @todo Find a way to disable services which are not used */ class ServicePool { + /** + * A counter for messages + * + * Each key-value pair corresponds to one MessageType within a Service. For the key, the most significant 8 bits are + * the number of the service, while the least significant 8 bits are the number of the Message. The value is the + * counter of each MessageType. + */ + etl::map<uint16_t, uint16_t, ECSS_TOTAL_MESSAGE_TYPES> messageTypeCounter; + + /** + * A counter for messages that corresponds to the total number of TM packets sent from an APID + */ + uint16_t packetSequenceCounter = 0; public: - RequestVerificationService requestVerification; +#ifdef SERVICE_EVENTACTION + EventActionService eventAction; +#endif + +#ifdef SERVICE_EVENTREPORT EventReportService eventReport; +#endif + +#ifdef SERVICE_FUNCTION + FunctionManagementService functionManagement; +#endif + +#ifdef SERVICE_LARGEPACKET + LargePacketTransferService largePacketTransferService; +#endif + +#ifdef SERVICE_MEMORY MemoryManagementService memoryManagement; - TimeManagementService timeManagement; - EventActionService eventAction; - TestService testService; +#endif + +#ifdef SERVICE_PARAMETER ParameterService parameterManagement; - LargePacketTransferService largePacketTransferService; - FunctionManagementService functionManagement; +#endif + +#ifdef SERVICE_REQUESTVERIFICATION + RequestVerificationService requestVerification; +#endif + +#ifdef SERVICE_TEST + TestService testService; +#endif + +#ifdef SERVICE_TIME + TimeManagementService timeManagement; +#endif + +#ifdef SERVICE_TIMESCHEDULING TimeBasedSchedulingService timeBasedScheduling; +#endif /** * The default ServicePool constructor @@ -44,6 +85,27 @@ public: * Services already stored as values will point to the "new" Services after a reset. */ void reset(); + + /** + * Get and increase the "message type counter" for the next message of a type + * + * The message type counter counts the type of generated messages per destination, according to requirement + * 5.4.2.1j. If the value reaches its max, it is wrapped back to 0. + * + * @param serviceType The service type ID + * @param messageType The message type ID + * @return The message type count + */ + uint16_t getAndUpdateMessageTypeCounter(uint8_t serviceType, uint8_t messageType); + + /** + * Get and increase the "packet sequence count" for the next message + * + * The packet sequence count is incremented each time a packet is released, with a maximum value of 2^14 - 1 + * + * @return The packet sequence count + */ + uint16_t getAndUpdatePacketSequenceCounter(); }; /** diff --git a/inc/Services/EventActionService.hpp b/inc/Services/EventActionService.hpp index aac524c061e7494204350e03c6cb6455a27d2a38..799ae996939bd4747c2fceb878d1c9dc05fdce60 100644 --- a/inc/Services/EventActionService.hpp +++ b/inc/Services/EventActionService.hpp @@ -11,6 +11,7 @@ * * ECSS 8.19 && 6.19 * + * @ingroup Services * @note: Make sure to check the note in the addEventActionDefinition() * @note: A third variable was added, the eventActionDefinitionID. This was added for the purpose of identifying * various eventActionDefinitions that correspond to the same eventDefinitionID. The goal is to have multiple actions diff --git a/inc/Services/EventReportService.hpp b/inc/Services/EventReportService.hpp index d1eac70f4c3f2e77feb895ea1c91e9cc71e13ea3..376b35d4a5194cf56397c02dd1c606c6f7a96c15 100644 --- a/inc/Services/EventReportService.hpp +++ b/inc/Services/EventReportService.hpp @@ -7,13 +7,12 @@ /** * Implementation of ST[05] event reporting service * + * @ingroup Services * @todo: add more enums event IDs * @todo: Make sure there isn't an event ID == 0, because there's a confliction with another service * Note: enum IDs are these just for test purposes * */ -#define CSS_EVENTS_MAX_COUNT 16 -#define ECSS_EVENTS_BITS 16 class EventReportService : public Service { private: @@ -101,9 +100,8 @@ public: * * @param eventID event definition ID * @param data the data of the report - * @param length the length of the data */ - void informativeEventReport(Event eventID, const String<64>& data); + void informativeEventReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data); /** * TM[5,2] low severiity anomaly report @@ -113,9 +111,8 @@ public: * * @param eventID event definition ID * @param data the data of the report - * @param length the length of the data */ - void lowSeverityAnomalyReport(Event eventID, const String<64>& data); + void lowSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data); /** * TM[5,3] medium severity anomaly report @@ -125,9 +122,8 @@ public: * * @param eventID event definition ID * @param data the data of the report - * @param length the length of the data */ - void mediumSeverityAnomalyReport(Event eventID, const String<64>& data); + void mediumSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data); /** * TM[5,4] high severity anomaly report @@ -137,9 +133,8 @@ public: * * @param eventID event definition ID * @param data the data of the report - * @param length the length of the data */ - void highSeverityAnomalyReport(Event eventID, const String<64>& data); + void highSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data); /** * TC[5,5] request to enable report generation @@ -164,7 +159,6 @@ public: /** * TM[5,8] disabled event definitions report * Telemetry package of a report of the disabled event definitions - * @param message */ void listOfDisabledEventsReport(); @@ -181,7 +175,7 @@ public: * is the ground station. * * @note This function is called from the main execute() that is defined in the file MessageParser.hpp - * @param param Contains the necessary parameters to call the suitable subservice + * @param message Contains the necessary parameters to call the suitable subservice */ void execute(Message& message); }; diff --git a/inc/Services/FunctionManagementService.hpp b/inc/Services/FunctionManagementService.hpp index 512ce3e39c71475915cfaf2bde38ee0a30d491fc..36f68d84ece877d7977d6f704a73f67337702d21 100644 --- a/inc/Services/FunctionManagementService.hpp +++ b/inc/Services/FunctionManagementService.hpp @@ -7,10 +7,6 @@ #include "Service.hpp" #include "ErrorHandler.hpp" -#define FUNC_MAP_SIZE 5 // size of the function map (number of elements) -#define FUNC_NAME_LENGTH 32 // max length of the function name -#define MAX_ARG_LENGTH 32 // maximum argument byte string length - /** * Implementation of the ST[08] function management service * @@ -25,6 +21,7 @@ * * You have been warned. * + * @ingroup Services * @author Grigoris Pavlakis <grigpavl@ece.auth.gr> */ @@ -49,10 +46,11 @@ * include(String<FUNC_NAME_LENGTH>("bar"), &bar); * include(String<FUNC_NAME_LENGTH>("baz"), &baz); * } + * @endcode */ -typedef String<FUNC_NAME_LENGTH> functionName; -typedef etl::map<functionName, void (*)(String<MAX_ARG_LENGTH>), FUNC_MAP_SIZE> FunctionMap; +typedef String<ECSS_FUNCTION_NAME_LENGTH> functionName; +typedef etl::map<functionName, void (*)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>), ECSS_FUNCTION_MAP_SIZE> FunctionMap; class FunctionManagementService : public Service { /** @@ -64,8 +62,6 @@ public: /** * Constructs the function pointer index with all the necessary functions at initialization time * These functions need to be in scope. Un-default when needed. - * - * @param None */ FunctionManagementService() = default; @@ -85,7 +81,7 @@ public: * @param ptr pointer to a function of void return type and a MAX_ARG_LENGTH-lengthed byte * string as argument (which contains the actual arguments of the function) */ - void include(String<FUNC_NAME_LENGTH> funcName, void (*ptr)(String<MAX_ARG_LENGTH>)); + void include(String<ECSS_FUNCTION_NAME_LENGTH> funcName, void (*ptr)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>)); int getMapSize() { return funcPtrIndex.size(); @@ -96,7 +92,7 @@ public: * is the ground station. * * @note This function is called from the main execute() that is defined in the file MessageParser.hpp - * @param param Contains the necessary parameters to call the suitable subservice + * @param message Contains the necessary parameters to call the suitable subservice */ void execute(Message& message); }; diff --git a/inc/Services/LargePacketTransferService.hpp b/inc/Services/LargePacketTransferService.hpp index e3219472881dfb84e59541aafa3d7c3c48725511..fe91522b0e50c5cc6007bdb0a247bb35ff584c7a 100644 --- a/inc/Services/LargePacketTransferService.hpp +++ b/inc/Services/LargePacketTransferService.hpp @@ -10,6 +10,8 @@ * maximum data size * * Note: More information can be found in the standards' manual, in p. 526-528 and in p. 229-236 + * + * @ingroup Services */ class LargePacketTransferService : public Service { @@ -49,7 +51,7 @@ public: const String<ECSS_MAX_FIXED_OCTET_STRING_SIZE>& string); // The three uplink functions should handle a TC request to "upload" data. Since there is not - // a createTC function ready, I just return the given string. + // a composeECSS function ready, I just return the given string. // @TODO: Modify these functions properly /** * Function that handles the first part of the uplink request @@ -72,7 +74,7 @@ public: /** * Function that splits large messages - * @param Message that is exceeds the standards and has to be split down + * @param message that is exceeds the standards and has to be split down * @param largeMessageTransactionIdentifier that is a value we assign to this splitting of the large message */ void split(Message& message, uint16_t largeMessageTransactionIdentifier); diff --git a/inc/Services/MemoryManagementService.hpp b/inc/Services/MemoryManagementService.hpp index f61ebad717ed06dd868233268cb5516306f3b795..95fca1ce82f975d26e14a87276a7b581bcab229e 100644 --- a/inc/Services/MemoryManagementService.hpp +++ b/inc/Services/MemoryManagementService.hpp @@ -2,12 +2,14 @@ #define ECSS_SERVICES_MEMMANGSERVICE_HPP #include <memory> -#include <iostream> #include "Service.hpp" #include "Helpers/CRCHelper.hpp" #include "ErrorHandler.hpp" #include "Platform/STM32F7/MemoryAddressLimits.hpp" +/** + * @ingroup Services + */ class MemoryManagementService : public Service { public: // Memory type ID's @@ -77,7 +79,7 @@ public: * is the ground station. * * @note This function is called from the main execute() that is defined in the file MessageParser.hpp - * @param param Contains the necessary parameters to call the suitable subservice + * @param message Contains the necessary parameters to call the suitable subservice */ void execute(Message& message); diff --git a/inc/Services/RequestVerificationService.hpp b/inc/Services/RequestVerificationService.hpp index 2d091de4033fe185829726133a8958e1ebecd459..c790029a8bab4d463b86d19248b13e6be1ddf09b 100644 --- a/inc/Services/RequestVerificationService.hpp +++ b/inc/Services/RequestVerificationService.hpp @@ -15,6 +15,8 @@ * * @todo See if the deduced data defined from the standard should still be ignored. This deduced * data exists only in reports that send failure signs(for example the TM[1,2]) + * + * @ingroup Services */ class RequestVerificationService : public Service { public: diff --git a/inc/Services/TestService.hpp b/inc/Services/TestService.hpp index 8ed5d917e9a0868901ee1fbcde7b0ec4936f6b95..bb344af81f3b2447fc5c879a5fa5f45a25c84d2d 100644 --- a/inc/Services/TestService.hpp +++ b/inc/Services/TestService.hpp @@ -5,6 +5,8 @@ /** * Implementation of the ST[17] test service + * + * @ingroup Services */ class TestService : public Service { public: diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp index 14581131ef478aa517555425ed22398197da4b50..575b03595e94158a5dae3b4e219d903ba608f942 100644 --- a/inc/Services/TimeBasedSchedulingService.hpp +++ b/inc/Services/TimeBasedSchedulingService.hpp @@ -41,6 +41,8 @@ struct Tester; * ground. * @todo Define whether the parsed absolute release time is saved in the scheduled activity as an * uint32_t or in the time format specified by the time management service. + * + * @ingroup Services */ class TimeBasedSchedulingService : public Service { private: @@ -52,21 +54,16 @@ private: */ bool executionFunctionStatus = false; // True indicates "enabled" and False "disabled" state - MessageParser msgParser; // Parse TC packets - /** * @brief Request identifier of the received packet * * @details The request identifier consists of the application process ID, the packet * sequence count and the source ID, all defined in the ECSS standard. - * @var applicationID Application process ID - * @var sequenceCount Packet sequence count - * @var sourceID Packet source ID */ struct RequestID { - uint16_t applicationID = 0; - uint16_t sequenceCount = 0; - uint8_t sourceID = 0; + uint16_t applicationID = 0; ///< Application process ID + uint16_t sequenceCount = 0; ///< Packet sequence count + uint8_t sourceID = 0; ///< Packet source ID bool operator!=(const RequestID& rightSide) const { return (sequenceCount != rightSide.sequenceCount) or (applicationID != rightSide.applicationID) or @@ -79,16 +76,14 @@ private: * * @details All scheduled activities must contain the request they exist for, their release * time and the corresponding request identifier. - * @var request Contains the received TC request - * @var requestID Contains the unique request identifier for that activity - * @var requestReleaseTime The absolute time is seconds of the request release + * + * @todo If we decide to use sub-schedules, the ID of that has to be defined + * @todo If groups are used, then the group ID has to be defined here */ struct ScheduledActivity { - Message request; // Hold the received command request - RequestID requestID; // Request ID, characteristic of the definition - uint32_t requestReleaseTime = 0; // Keep the command release time - // todo: If we decide to use sub-schedules, the ID of that has to be defined - // todo: If groups are used, then the group ID has to be defined here + Message request; ///< Hold the received command request + RequestID requestID; ///< Request ID, characteristic of the definition + uint32_t requestReleaseTime = 0; ///< Keep the command release time }; /** @@ -138,7 +133,7 @@ public: void enableScheduleExecution(Message& request); /** - * @breif TC[11,2] disable the time-based schedule execution function + * @brief TC[11,2] disable the time-based schedule execution function * * @details Disables the time-based command execution scheduling * @param request Provide the received message as a parameter diff --git a/inc/Services/TimeManagementService.hpp b/inc/Services/TimeManagementService.hpp index 4b6663c172208f52dc8201f7187e7da510a159da..54617aaf1aaf10ae8308741b2ff459f16ba38b20 100644 --- a/inc/Services/TimeManagementService.hpp +++ b/inc/Services/TimeManagementService.hpp @@ -7,7 +7,8 @@ /** * Implementation of the ST[09] time management. * - * @notes + * @ingroup Services + * @note * There is a noticeable difference between setting the time using GPS and setting the time * using space packets from the ground segment. The GPS module sends the actual time of UTC (123519 * is 12:35:19 UTC), while space packets, for time configuration, sends the elapsed time units diff --git a/inc/etl/String.hpp b/inc/etl/String.hpp index 11c04b6747c6c12e211fb5bdd30ba498169c07f6..7691f78323f636c5741a68b9cb2144e7c362fa86 100644 --- a/inc/etl/String.hpp +++ b/inc/etl/String.hpp @@ -56,6 +56,20 @@ public: */ String(const char* text) // NOLINTNEXTLINE(google-explicit-constructor) : etl::string<MAX_SIZE>(text) {} + + using etl::istring::append; // Use the append function from the parent + + /** + * Append a specified number of bytes from a uint8_t array to the String + * @details The array does NOT need to be null-terminated + * @param data The characters to append + * @param n The number of characters that \p data contains + * @return This string + */ + String& append(const uint8_t* data, size_t n) { + etl::string<MAX_SIZE>::append(reinterpret_cast<const char*>(data), n); + return *this; + } }; #endif // ECSS_SERVICES_ETL_STRING_HPP diff --git a/lib/etl b/lib/etl index f2f23395cde34704686233094d9619ad8d06453a..5a7e181100dae73659db133783ae964c7661437b 160000 --- a/lib/etl +++ b/lib/etl @@ -1 +1 @@ -Subproject commit f2f23395cde34704686233094d9619ad8d06453a +Subproject commit 5a7e181100dae73659db133783ae964c7661437b diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp index 1cba15ca835dca1022adb68c032bb6912592dd8f..e7bacfb0f1561985485542a48b29425cf8047d10 100644 --- a/src/ErrorHandler.cpp +++ b/src/ErrorHandler.cpp @@ -1,39 +1,48 @@ -#include <iostream> -#include <cxxabi.h> +#include "ECSS_Configuration.hpp" #include <ErrorHandler.hpp> #include <ServicePool.hpp> #include "Services/RequestVerificationService.hpp" template <> void ErrorHandler::reportError(const Message& message, AcceptanceErrorType errorCode) { +#ifdef SERVICE_REQUESTVERIFICATION Services.requestVerification.failAcceptanceVerification(message, errorCode); +#endif logError(message, errorCode); } template <> void ErrorHandler::reportError(const Message& message, ExecutionStartErrorType errorCode) { +#ifdef SERVICE_REQUESTVERIFICATION Services.requestVerification.failStartExecutionVerification(message, errorCode); +#endif logError(message, errorCode); } void ErrorHandler::reportProgressError(const Message& message, ExecutionProgressErrorType errorCode, uint8_t stepID) { +#ifdef SERVICE_REQUESTVERIFICATION Services.requestVerification.failProgressExecutionVerification(message, errorCode, stepID); +#endif logError(message, errorCode); } template <> void ErrorHandler::reportError(const Message& message, ExecutionCompletionErrorType errorCode) { +#ifdef SERVICE_REQUESTVERIFICATION Services.requestVerification.failCompletionExecutionVerification(message, errorCode); +#endif logError(message, errorCode); } template <> void ErrorHandler::reportError(const Message& message, RoutingErrorType errorCode) { +#ifdef SERVICE_REQUESTVERIFICATION Services.requestVerification.failRoutingVerification(message, errorCode); +#endif logError(message, errorCode); } diff --git a/src/Helpers/CRCHelper.cpp b/src/Helpers/CRCHelper.cpp index dda87aa34d4a7be7c539ab5642ee48cc81fc6f71..1e6a28fd4c3808bcf5459a6378cd21e4f7d7ef52 100644 --- a/src/Helpers/CRCHelper.cpp +++ b/src/Helpers/CRCHelper.cpp @@ -4,23 +4,23 @@ uint16_t CRCHelper::calculateCRC(const uint8_t* message, uint32_t length) { // shift register contains all 1's initially (ECSS-E-ST-70-41C, Annex B - CRC and ISO checksum) - uint16_t shiftReg = 0xFFFFu; + uint16_t shiftReg = 0xFFFFU; // CRC16-CCITT generator polynomial (as specified in standard) - uint16_t polynomial = 0x1021u; + uint16_t polynomial = 0x1021U; for (uint32_t i = 0; i < length; i++) { // "copy" (XOR w/ existing contents) the current msg bits into the MSB of the shift register - shiftReg ^= (message[i] << 8u); + shiftReg ^= (message[i] << 8U); for (int j = 0; j < 8; j++) { // if the MSB is set, the bitwise AND gives 1 - if ((shiftReg & 0x8000u) != 0u) { + if ((shiftReg & 0x8000U) != 0U) { // toss out of the register the MSB and divide (XOR) its content with the generator - shiftReg = ((shiftReg << 1u) ^ polynomial); + shiftReg = ((shiftReg << 1U) ^ polynomial); } else { // just toss out the MSB and make room for a new bit - shiftReg <<= 1u; + shiftReg <<= 1U; } } } diff --git a/src/Helpers/TimeAndDate.cpp b/src/Helpers/TimeAndDate.cpp index b531d2274ce3917f79e865fd63d20c7e5d85c602..54a17b6b17f0ca2838f741222858fc97777f25ae 100644 --- a/src/Helpers/TimeAndDate.cpp +++ b/src/Helpers/TimeAndDate.cpp @@ -15,8 +15,9 @@ TimeAndDate::TimeAndDate(uint16_t year, uint8_t month, uint8_t day, uint8_t hour ASSERT_INTERNAL(2019 <= year, ErrorHandler::InternalErrorType::InvalidDate); ASSERT_INTERNAL((1 <= month) && (month <= 12), ErrorHandler::InternalErrorType::InvalidDate); ASSERT_INTERNAL((1 <= day) && (day <= 31), ErrorHandler::InternalErrorType::InvalidDate); - ASSERT_INTERNAL(hour <= 24, ErrorHandler::InternalErrorType::InvalidDate); - ASSERT_INTERNAL(minute <= 60, ErrorHandler::InternalErrorType::InvalidDate); + ASSERT_INTERNAL(hour < 24, ErrorHandler::InternalErrorType::InvalidDate); + ASSERT_INTERNAL(minute < 60, ErrorHandler::InternalErrorType::InvalidDate); + // Seconds can be equal to 60, to account for leap seconds. ASSERT_INTERNAL(second <= 60, ErrorHandler::InternalErrorType::InvalidDate); this->year = year; diff --git a/src/Helpers/TimeHelper.cpp b/src/Helpers/TimeHelper.cpp index 16b6e16229b6469d6e0c554385be6e0f0ec665e7..04792af7c90b0be0153d8212aa3af4262673870b 100644 --- a/src/Helpers/TimeHelper.cpp +++ b/src/Helpers/TimeHelper.cpp @@ -25,8 +25,8 @@ uint32_t TimeHelper::utcToSeconds(TimeAndDate& TimeInfo) { secs += (IsLeapYear(y) ? 366 : 365) * SECONDS_PER_DAY; } for (uint16_t m = 1; m < TimeInfo.month; ++m) { - secs += DaysOfMonth[m - 1u] * SECONDS_PER_DAY; - if ((m == 2u) && IsLeapYear(TimeInfo.year)) { + secs += DaysOfMonth[m - 1U] * SECONDS_PER_DAY; + if ((m == 2U) && IsLeapYear(TimeInfo.year)) { secs += SECONDS_PER_DAY; } } @@ -62,7 +62,7 @@ struct TimeAndDate TimeHelper::secondsToUTC(uint32_t seconds) { TimeInfo.month++; seconds -= (DaysOfMonth[i] * SECONDS_PER_DAY); i++; - if ((i == 1u) && IsLeapYear(TimeInfo.year)) { + if ((i == 1U) && IsLeapYear(TimeInfo.year)) { if (seconds <= (28 * SECONDS_PER_DAY)) { break; } @@ -121,7 +121,7 @@ TimeAndDate TimeHelper::parseCDStimeFormat(const uint8_t* data) { uint32_t msOfDay = ((static_cast<uint32_t>(data[2])) << 24) | ((static_cast<uint32_t>(data[3]))) << 16 | ((static_cast<uint32_t>(data[4]))) << 8 | (static_cast<uint32_t>(data[5])); - uint32_t seconds = (elapsedDays * SECONDS_PER_DAY) + (msOfDay / 1000u); + uint32_t seconds = (elapsedDays * SECONDS_PER_DAY) + (msOfDay / 1000U); return secondsToUTC(seconds); } diff --git a/src/Logger.cpp b/src/Logger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76e36d59af7a58a5b9c34cd9fa3aca3592fee4b1 --- /dev/null +++ b/src/Logger.cpp @@ -0,0 +1,24 @@ +#include <Logger.hpp> + +etl::format_spec Logger::format; + +// Reimplementation of the function for variable C strings +template <> +Logger::LogEntry& Logger::LogEntry::operator<<(char* value) { + message.append(value); + return *this; +} + +// Reimplementation of the function for C strings +template <> +Logger::LogEntry& Logger::LogEntry::operator<<(const char* value) { + message.append(value); + return *this; +} + +Logger::LogEntry::LogEntry(LogLevel level) : level(level) {} + +Logger::LogEntry::~LogEntry() { + // When the destructor is called, the log message is fully "designed". Now we can finally "display" it to the user. + Logger::log(level, message); +} diff --git a/src/Message.cpp b/src/Message.cpp index efedb65c16b2388fc6a87326cb0ffbcd35f2fac9..0333117ed92f6dc8b352268f94ff98d51eae9d14 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -2,6 +2,8 @@ #include "macros.hpp" #include <cstring> #include <ErrorHandler.hpp> +#include <ServicePool.hpp> +#include <MessageParser.hpp> Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, uint16_t applicationId) : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(applicationId) {} @@ -36,11 +38,15 @@ void Message::appendBits(uint8_t numBits, uint16_t data) { void Message::finalize() { // Define the spare field in telemetry and telecommand user data field (7.4.3.2.c and 7.4.4.2.c) - if (currentBit != 0) { currentBit = 0; dataSize++; } + + if (packetType == PacketType::TM) { + messageTypeCounter = Services.getAndUpdateMessageTypeCounter(serviceType, messageType); + packetSequenceCount = Services.getAndUpdatePacketSequenceCounter(); + } } void Message::appendByte(uint8_t value) { @@ -84,7 +90,7 @@ uint16_t Message::readBits(uint8_t numBits) { if ((currentBit + numBits) >= 8) { auto bitsToAddNow = static_cast<uint8_t>(8 - currentBit); - uint8_t mask = ((1u << bitsToAddNow) - 1u); + uint8_t mask = ((1U << bitsToAddNow) - 1U); uint8_t maskedData = data[readPosition] & mask; value |= maskedData << (numBits - bitsToAddNow); @@ -156,3 +162,38 @@ void Message::resetRead() { readPosition = 0; currentBit = 0; } + +void Message::appendMessage(const Message& message, uint16_t size) { + appendString(MessageParser::composeECSS(message, size)); +} + +void Message::appendString(const etl::istring& string) { + ASSERT_INTERNAL(dataSize + string.size() <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + // TODO: Do we need to keep this check? How does etl::string handle it? + ASSERT_INTERNAL(string.size() <= string.capacity(), ErrorHandler::StringTooLarge); + + memcpy(data + dataSize, string.data(), string.size()); + + dataSize += string.size(); +} + +void Message::appendFixedString(const etl::istring& string) { + ASSERT_INTERNAL((dataSize + string.max_size()) < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + + // Append the bytes with content + memcpy(data + dataSize, string.data(), string.size()); + // The rest of the bytes is set to 0 + (void) memset(data + dataSize + string.size(), 0, string.max_size() - string.size()); + + dataSize += string.max_size(); +} + +void Message::appendOctetString(const etl::istring& string) { + // Make sure that the string is large enough to count + ASSERT_INTERNAL(string.size() <= (std::numeric_limits<uint16_t>::max)(), ErrorHandler::StringTooLarge); + // Redundant check to make sure we fail before appending string.size() + ASSERT_INTERNAL(dataSize + 2 + string.size() < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + + appendUint16(string.size()); + appendString(string); +} diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp index 29300cdbf7c0b761f443c7ee189f6f59bfb4589b..b4eb3953d2e6ecba5f4115759ba0ac062fb2847e 100644 --- a/src/MessageParser.cpp +++ b/src/MessageParser.cpp @@ -8,30 +8,54 @@ void MessageParser::execute(Message& message) { switch (message.serviceType) { +#ifdef SERVICE_EVENTREPORT case 5: Services.eventReport.execute(message); // ST[05] break; +#endif + +#ifdef SERVICE_MEMORY case 6: Services.memoryManagement.execute(message); // ST[06] break; +#endif + +#ifdef SERVICE_FUNCTION case 8: Services.functionManagement.execute(message); // ST[08] break; +#endif + +#ifdef SERVICE_TIME case 9: Services.timeManagement.execute(message); // ST[09] break; +#endif + +#ifdef SERVICE_TIMESCHEDULING case 11: Services.timeBasedScheduling.execute(message); // ST[11] break; +#endif + +#ifdef SERVICE_TEST case 17: Services.testService.execute(message); // ST[17] break; +#endif + +#ifdef SERVICE_EVENTACTION case 19: Services.eventAction.execute(message); // ST[19] break; +#endif + +#ifdef SERVICE_PARAMETER case 20: Services.parameterManagement.execute(message); // ST[20] break; +#endif + default: ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } @@ -47,28 +71,30 @@ Message MessageParser::parse(uint8_t* data, uint32_t length) { // Individual fields of the CCSDS Space Packet primary header uint8_t versionNumber = data[0] >> 5; Message::PacketType packetType = ((data[0] & 0x10) == 0) ? Message::TM : Message::TC; - uint8_t secondaryHeaderFlag = data[0] & static_cast<uint8_t>(0x08); + bool secondaryHeaderFlag = (data[0] & 0x08U) != 0U; uint16_t APID = packetHeaderIdentification & static_cast<uint16_t>(0x07ff); auto sequenceFlags = static_cast<uint8_t>(packetSequenceControl >> 14); + uint16_t packetSequenceCount = packetSequenceControl & (~ 0xc000U); // keep last 14 bits // Returning an internal error, since the Message is not available yet - ASSERT_INTERNAL(versionNumber == 0u, ErrorHandler::UnacceptablePacket); - ASSERT_INTERNAL(secondaryHeaderFlag == 1u, ErrorHandler::UnacceptablePacket); - ASSERT_INTERNAL(sequenceFlags == 0x3u, ErrorHandler::UnacceptablePacket); - ASSERT_INTERNAL(packetDataLength == (length - 6u), ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(versionNumber == 0U, ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(secondaryHeaderFlag, ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(sequenceFlags == 0x3U, ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(packetDataLength == (length - 6U), ErrorHandler::UnacceptablePacket); Message message(0, 0, packetType, APID); + message.packetSequenceCount = packetSequenceCount; if (packetType == Message::TC) { - parseTC(data + 6, packetDataLength, message); + parseECSSTCHeader(data + 6, packetDataLength, message); } else { - parseTM(data + 6, packetDataLength, message); + parseECSSTMHeader(data + 6, packetDataLength, message); } return message; } -void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& message) { +void MessageParser::parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message) { ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage); // Individual fields of the TC header @@ -76,9 +102,7 @@ void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& messa uint8_t serviceType = data[1]; uint8_t messageType = data[2]; - // todo: Fix this parsing function, because it assumes PUS header in data, which is not true - // with the current implementation - ErrorHandler::assertRequest(pusVersion == 2u, message, ErrorHandler::UnacceptableMessage); + ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage); // Remove the length of the header length -= 5; @@ -91,34 +115,90 @@ void MessageParser::parseTC(const uint8_t* data, uint16_t length, Message& messa message.dataSize = length; } -Message MessageParser::parseRequestTC(String<ECSS_TC_REQUEST_STRING_SIZE> data) { +Message MessageParser::parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data) { Message message; auto* dataInt = reinterpret_cast<uint8_t*>(data.data()); message.packetType = Message::TC; - parseTC(dataInt, ECSS_TC_REQUEST_STRING_SIZE, message); + parseECSSTCHeader(dataInt, ECSS_TC_REQUEST_STRING_SIZE, message); return message; } -Message MessageParser::parseRequestTC(uint8_t* data) { +Message MessageParser::parseECSSTC(uint8_t* data) { Message message; message.packetType = Message::TC; - parseTC(data, ECSS_TC_REQUEST_STRING_SIZE, message); + parseECSSTCHeader(data, ECSS_TC_REQUEST_STRING_SIZE, message); return message; } -String<ECSS_TC_REQUEST_STRING_SIZE> MessageParser::convertTCToStr(Message& message) { - uint8_t tempString[ECSS_TC_REQUEST_STRING_SIZE] = {0}; +String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::composeECSS(const Message& message, uint16_t size) { + uint8_t header[5]; - tempString[0] = ECSS_PUS_VERSION << 4; // Assign the pusVersion = 2 - tempString[1] = message.serviceType; - tempString[2] = message.messageType; - memcpy(tempString + 5, message.data, ECSS_TC_REQUEST_STRING_SIZE - 5); - String<ECSS_TC_REQUEST_STRING_SIZE> dataString(tempString); + if (message.packetType == Message::TC) { + header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2 + header[1] = message.serviceType; + header[2] = message.messageType; + header[3] = 0; + header[4] = 0; + } else { + header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2 + header[1] = message.serviceType; + header[2] = message.messageType; + header[3] = static_cast<uint8_t>(message.messageTypeCounter >> 8U); + header[4] = static_cast<uint8_t>(message.messageTypeCounter & 0xffU); + } + + String<CCSDS_MAX_MESSAGE_SIZE> dataString(header, 5); + dataString.append(message.data, message.dataSize); + + // Make sure to reach the requested size + if (size != 0) { + if (dataString.size() > size) { + // Message overflow + ErrorHandler::reportInternalError(ErrorHandler::NestedMessageTooLarge); + } else if (dataString.size() < size) { + // Append some 0s + dataString.append(size - dataString.size(), 0); + } else { + // The message has an equal size to the requested one - do nothing + } + } return dataString; } -void MessageParser::parseTM(const uint8_t* data, uint16_t length, Message& message) { +String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::compose(const Message& message) { + uint8_t header[6]; + + // First, compose the ECSS part + String<CCSDS_MAX_MESSAGE_SIZE> ecssMessage = MessageParser::composeECSS(message); + + // Sanity check that there is enough space for the string + ASSERT_INTERNAL((ecssMessage.size() + 6U) <= CCSDS_MAX_MESSAGE_SIZE, ErrorHandler::StringTooLarge); + + // Parts of the header + uint16_t packetId = message.applicationId; + packetId |= (1U << 11U); // Secondary header flag + packetId |= (message.packetType == Message::TC) ? (1U << 12U) : (0U); // Ignore-MISRA + uint16_t packetSequenceControl = message.packetSequenceCount | (3U << 14U); + uint16_t packetDataLength = ecssMessage.size(); + + // Compile the header + header[0] = packetId >> 8U; + header[1] = packetId & 0xffU; + header[2] = packetSequenceControl >> 8U; + header[3] = packetSequenceControl & 0xffU; + header[4] = packetDataLength >> 8U; + header[5] = packetDataLength & 0xffU; + + // Compile the final message by appending the header + String<CCSDS_MAX_MESSAGE_SIZE> ccsdsMessage(header, 6); + ccsdsMessage.append(ecssMessage); + + return ccsdsMessage; +} + + +void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message) { ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage); // Individual fields of the TM header @@ -126,7 +206,7 @@ void MessageParser::parseTM(const uint8_t* data, uint16_t length, Message& messa uint8_t serviceType = data[1]; uint8_t messageType = data[2]; - ErrorHandler::assertRequest(pusVersion == 2u, message, ErrorHandler::UnacceptableMessage); + ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage); // Remove the length of the header length -= 5; diff --git a/src/Platform/x86/ErrorHandler.cpp b/src/Platform/x86/ErrorHandler.cpp index a59e36514ddf1a7047ab4e2e2a47f05dfd4cfd89..ffddffd2d0501e688779674251ae1da83aa035d7 100644 --- a/src/Platform/x86/ErrorHandler.cpp +++ b/src/Platform/x86/ErrorHandler.cpp @@ -7,6 +7,7 @@ #include <cxxabi.h> #include <ErrorHandler.hpp> #include <Message.hpp> +#include <Logger.hpp> // TODO: Find a way to reduce the number of copies of this chunk template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType); @@ -18,23 +19,22 @@ template void ErrorHandler::logError(ErrorHandler::InternalErrorType); template <typename ErrorType> void ErrorHandler::logError(const Message& message, ErrorType errorType) { - std::cerr + LOG_ERROR /* * Gets the error class name from the template * Note: This is g++-dependent code and should only be used for debugging. */ << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType) - << "]: " << errorType << std::endl; + << "]: " << errorType; } template <typename ErrorType> void ErrorHandler::logError(ErrorType errorType) { - std::cerr + LOG_ERROR /* * Gets the error class name from the template * Note: This is g++-dependent code and should only be used for debugging. */ - << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType - << std::endl; + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error: " << errorType; } diff --git a/src/Platform/x86/Logger.cpp b/src/Platform/x86/Logger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..85d0797656a4831c718e5b64740eb977ddb98c1d --- /dev/null +++ b/src/Platform/x86/Logger.cpp @@ -0,0 +1,68 @@ +#include <Logger.hpp> +#include <etl/String.hpp> +#include <iostream> +#include <ECSS_Definitions.hpp> + +#include <chrono> +#include <iomanip> + +// The implementation of this function appends ANSI codes that should add colours to a compatible terminal +void Logger::log(Logger::LogLevel level, etl::istring & message) { + // Get the current time & date + std::time_t t = std::time(nullptr); + std::tm tm = *std::localtime(&t); + + // Get the log level and its colour + std::string name; + std::string colour; + bool keepColour = false; // Whether to keep the colour in the rest of the message + + if (level <= Logger::trace) { + name = "trace"; + colour = "90"; // bright black + keepColour = true; + } else if (level <= Logger::debug) { + name = "debug"; + colour = "90"; // bright black + } else if (level <= Logger::info) { + name = "info"; + colour = "32"; // green + } else if (level <= Logger::notice) { + name = "notice"; + colour = "36"; // cyan + } else if (level <= Logger::warning) { + name = "warning"; + colour = "33"; // yellow + } else if (level <= Logger::error) { + name = "error"; + colour = "31"; // red + } else { + name = "emergency"; + colour = "31"; // red + keepColour = true; + } + + std::ostringstream ss; // A string stream to create the log message + ss << "\033" "[0;90m" << std::put_time(&tm, "%FT%T%z") << "\033" "[0m "; // The date + ss << "[\033" "[1;" << colour << "m" << std::setfill(' ') << std::setw(7) << std::right // Ignore-MISRA + << name << std::setw(0) << "\033" "[0m] "; // The log level // Ignore-MISRA + + if (keepColour) { + ss << "\033" "[0;" << colour << "m"; // Ignore-MISRA + } + ss << message.c_str(); // The message itself + if (keepColour) { + ss << "\033" "[0m"; + } + + ss << "\n"; + std::cerr << ss.str(); +} + +// Reimplementation of the log function for C++ strings +// This is kept in the Platform files, since we don't want to mess with std::strings in the microcontroller +Logger::LogEntry& Logger::LogEntry::operator<<(const std::string & value) { + message.append(value.c_str()); + + return *this; +} diff --git a/src/Platform/x86/Service.cpp b/src/Platform/x86/Service.cpp index 7c20d2f90bea925cd43754c62a231c25bc2c5648..60786fc650c32da32cb98860886291537e378fce 100644 --- a/src/Platform/x86/Service.cpp +++ b/src/Platform/x86/Service.cpp @@ -1,21 +1,25 @@ #include <iostream> #include <iomanip> +#include <Logger.hpp> #include "Service.hpp" void Service::storeMessage(Message& message) { // appends the remaining bits to complete a byte message.finalize(); + // Create a new stream to display the packet + std::ostringstream ss; + // Just print it to the screen - std::cout << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "[" - << std::hex - // << std::dec - << static_cast<int>(message.serviceType) << "," << static_cast<int>(message.messageType) - << "] message!\n"; - // std::cout << std::hex << std::setfill('0') << std::setw(2); - for (int i = 0; i < message.dataSize; i++) { - std::cout << static_cast<int>(message.data[i]); - std::cout << " "; + ss << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "[" + << std::hex + << static_cast<int>(message.serviceType) << "," // Ignore-MISRA + << static_cast<int>(message.messageType) // Ignore-MISRA + << "] message! "; + + for (unsigned int i = 0; i < message.dataSize; i++) { + ss << static_cast<int>(message.data[i]) << " "; // Ignore-MISRA } - std::cout << std::endl; + + LOG_DEBUG << ss.str(); } diff --git a/src/main.cpp b/src/Platform/x86/main.cpp similarity index 96% rename from src/main.cpp rename to src/Platform/x86/main.cpp index aed43a835568e32cffa5c32ba04f5dcbcb8ce5a6..391780a790d452b7d5e0fd46a3aec0c280882818 100644 --- a/src/main.cpp +++ b/src/Platform/x86/main.cpp @@ -1,5 +1,6 @@ #include <iostream> #include <ServicePool.hpp> +#include <Logger.hpp> #include "Helpers/CRCHelper.hpp" #include "Helpers/TimeHelper.hpp" #include "Services/TestService.hpp" @@ -20,9 +21,11 @@ #include "etl/String.hpp" int main() { + LOG_NOTICE << "ECSS Services test application"; + Message packet = Message(0, 0, Message::TC, 1); - packet.appendString<5>("hello"); + packet.appendString(String<5>("hello")); packet.appendBits(15, 0x28a8); packet.appendBits(1, 1); packet.appendFloat(5.7); @@ -298,13 +301,13 @@ int main() { // ST[11] test TimeBasedSchedulingService timeBasedSchedulingService; - MessageParser msgParser; auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time std::cout << "\n\nST[11] service is running"; std::cout << "\nCurrent time in seconds (UNIX epoch): " << currentTime << std::endl; Message receivedMsg = Message(11, 1, Message::TC, 1); - Message testMessage1(6, 5, Message::TC, 1), testMessage2(4, 5, Message::TC, 1); + Message testMessage1(6, 5, Message::TC, 1); + Message testMessage2(4, 5, Message::TC, 1); testMessage1.appendUint16(4253); // Append dummy data testMessage2.appendUint16(45667); // Append dummy data @@ -314,11 +317,11 @@ int main() { receivedMsg = Message(11, 4, Message::TC, 1); receivedMsg.appendUint16(2); // Total number of requests - receivedMsg.appendUint32(currentTime + 1556435u); - receivedMsg.appendString(msgParser.convertTCToStr(testMessage1)); + receivedMsg.appendUint32(currentTime + 1556435U); + receivedMsg.appendString(MessageParser::composeECSS(testMessage1)); - receivedMsg.appendUint32(currentTime + 1957232u); - receivedMsg.appendString(msgParser.convertTCToStr(testMessage2)); + receivedMsg.appendUint32(currentTime + 1957232U); + receivedMsg.appendString(MessageParser::composeECSS(testMessage2)); timeBasedSchedulingService.insertActivities(receivedMsg); // Time shift activities @@ -335,5 +338,6 @@ int main() { receivedMsg = Message(11, 12, Message::TC, 1); timeBasedSchedulingService.summaryReportActivitiesByID(receivedMsg); + LOG_NOTICE << "ECSS Services test complete"; return 0; } diff --git a/src/ServicePool.cpp b/src/ServicePool.cpp index f76e2ee77d066b475c0678baec2159ad3dc1a246..b8e7c963ca5af536baf4f0b52456a9432452bbb7 100644 --- a/src/ServicePool.cpp +++ b/src/ServicePool.cpp @@ -12,3 +12,19 @@ void ServicePool::reset() { // statically allocated from before. new (this) ServicePool(); } + +uint16_t ServicePool::getAndUpdateMessageTypeCounter(uint8_t serviceType, uint8_t messageType) { + uint16_t key = (serviceType << 8U) | messageType; // Create the key of the map + return (messageTypeCounter[key])++; // Fetch and increase the value +} + +uint16_t ServicePool::getAndUpdatePacketSequenceCounter() { + uint16_t value = packetSequenceCounter; + + // Increase the value + if ((++packetSequenceCounter) >= (1U << 14U)) { // The value of the packet sequence counter is <= (2^14 - 1) + packetSequenceCounter = 0; + } + + return value; +} diff --git a/src/Services/EventActionService.cpp b/src/Services/EventActionService.cpp index e3788002c83a8c7b175e8c150a91880919c8f4f7..f42079794019ef1d4fca34b4ed176e7d40e5c18c 100644 --- a/src/Services/EventActionService.cpp +++ b/src/Services/EventActionService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_EVENTACTION + #include "Services/EventActionService.hpp" #include "Message.hpp" #include "MessageParser.hpp" @@ -77,7 +80,7 @@ void EventActionService::enableEventActionDefinitions(Message& message) { // TC[19,4] message.assertTC(19, 4); uint16_t numberOfEventActionDefinitions = message.readUint16(); - if (numberOfEventActionDefinitions != 0u) { + if (numberOfEventActionDefinitions != 0U) { for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) { message.skipBytes(2); // Skips reading the application ID uint16_t eventDefinitionID = message.readEnum16(); @@ -110,7 +113,7 @@ void EventActionService::disableEventActionDefinitions(Message& message) { // TC[19,5] message.assertTC(19, 5); uint16_t numberOfEventActionDefinitions = message.readUint16(); - if (numberOfEventActionDefinitions != 0u) { + if (numberOfEventActionDefinitions != 0U) { for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) { message.skipBytes(2); // Skips reading applicationID uint16_t eventDefinitionID = message.readEnum16(); @@ -180,8 +183,7 @@ void EventActionService::executeAction(uint16_t eventID) { auto range = eventActionDefinitionMap.equal_range(eventID); for (auto& element = range.first; element != range.second; ++element) { if (element->second.enabled) { - MessageParser messageParser; - Message message = messageParser.parseRequestTC(element->second.request); + Message message = MessageParser::parseECSSTC(element->second.request); MessageParser::execute(message); } } @@ -218,3 +220,5 @@ void EventActionService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif \ No newline at end of file diff --git a/src/Services/EventReportService.cpp b/src/Services/EventReportService.cpp index cbf8ee72ae08d669e65537d6cd6d08314ef28c41..ce97f874a51aa795556b313e901448237c68c13b 100644 --- a/src/Services/EventReportService.cpp +++ b/src/Services/EventReportService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_EVENTREPORT + #include <Services/EventReportService.hpp> #include <Services/EventActionService.hpp> #include "Message.hpp" @@ -6,7 +9,7 @@ * @todo: Add message type in TCs * @todo: this code is error prone, depending on parameters given, add fail safes (probably?) */ -void EventReportService::informativeEventReport(Event eventID, const String<64>& data) { +void EventReportService::informativeEventReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) { // TM[5,1] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { Message report = createTM(1); @@ -19,7 +22,8 @@ void EventReportService::informativeEventReport(Event eventID, const String<64>& } } -void EventReportService::lowSeverityAnomalyReport(Event eventID, const String<64>& data) { +void +EventReportService::lowSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) { lowSeverityEventCount++; // TM[5,2] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -35,7 +39,8 @@ void EventReportService::lowSeverityAnomalyReport(Event eventID, const String<64 } } -void EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<64>& data) { +void +EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) { mediumSeverityEventCount++; // TM[5,3] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -51,7 +56,8 @@ void EventReportService::mediumSeverityAnomalyReport(Event eventID, const String } } -void EventReportService::highSeverityAnomalyReport(Event eventID, const String<64>& data) { +void +EventReportService::highSeverityAnomalyReport(Event eventID, const String<ECSS_EVENT_DATA_AUXILIARY_MAX_SIZE>& data) { highSeverityEventCount++; // TM[5,4] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -120,7 +126,7 @@ void EventReportService::listOfDisabledEventsReport() { uint16_t numberOfDisabledEvents = stateOfEvents.size() - stateOfEvents.count(); report.appendHalfword(numberOfDisabledEvents); - for (uint16_t i = 0; i < stateOfEvents.size(); i++) { + for (size_t i = 0; i < stateOfEvents.size(); i++) { if (not stateOfEvents[i]) { report.appendEnum16(i); } @@ -141,3 +147,5 @@ void EventReportService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif diff --git a/src/Services/FunctionManagementService.cpp b/src/Services/FunctionManagementService.cpp index b6d5638b6efd990136f8173beced1618be85f247..5400f038bb6efb3193712b8a800881478c89a338 100644 --- a/src/Services/FunctionManagementService.cpp +++ b/src/Services/FunctionManagementService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_FUNCTION + #include "Services/FunctionManagementService.hpp" void FunctionManagementService::call(Message& msg) { @@ -7,13 +10,13 @@ void FunctionManagementService::call(Message& msg) { ErrorHandler::assertRequest(msg.messageType == 1, msg, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); ErrorHandler::assertRequest(msg.serviceType == 8, msg, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - uint8_t funcName[FUNC_NAME_LENGTH] = { 0 }; // the function's name - uint8_t funcArgs[MAX_ARG_LENGTH] = { 0 }; // arguments for the function + uint8_t funcName[ECSS_FUNCTION_NAME_LENGTH] = { 0 }; // the function's name + uint8_t funcArgs[ECSS_FUNCTION_MAX_ARG_LENGTH] = { 0 }; // arguments for the function - msg.readString(funcName, FUNC_NAME_LENGTH); - msg.readString(funcArgs, MAX_ARG_LENGTH); + msg.readString(funcName, ECSS_FUNCTION_NAME_LENGTH); + msg.readString(funcArgs, ECSS_FUNCTION_MAX_ARG_LENGTH); - if (msg.dataSize > (FUNC_NAME_LENGTH + MAX_ARG_LENGTH)) { + if (msg.dataSize > (ECSS_FUNCTION_NAME_LENGTH + ECSS_FUNCTION_MAX_ARG_LENGTH)) { ErrorHandler::reportError(msg, ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); // report failed // start of execution as requested by the standard @@ -21,9 +24,9 @@ void FunctionManagementService::call(Message& msg) { } // locate the appropriate function pointer - String<FUNC_NAME_LENGTH> name(funcName); + String<ECSS_FUNCTION_NAME_LENGTH> name(funcName); FunctionMap::iterator iter = funcPtrIndex.find(name); - void (*selected)(String<MAX_ARG_LENGTH>); + void (*selected)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>); if (iter != funcPtrIndex.end()) { selected = *iter->second; @@ -36,10 +39,11 @@ void FunctionManagementService::call(Message& msg) { selected(funcArgs); } -void FunctionManagementService::include(String<FUNC_NAME_LENGTH> funcName, void (*ptr)(String<MAX_ARG_LENGTH>)) { +void FunctionManagementService::include(String<ECSS_FUNCTION_NAME_LENGTH> funcName, + void (* ptr)(String<ECSS_FUNCTION_MAX_ARG_LENGTH>)) { if (not funcPtrIndex.full()) { // CAUTION: etl::map won't check by itself if it's full // before attempting to insert a key-value pair, causing segmentation faults. Check first! - funcName.append(FUNC_NAME_LENGTH - funcName.length(), 0); + funcName.append(ECSS_FUNCTION_NAME_LENGTH - funcName.length(), 0); funcPtrIndex.insert(std::make_pair(funcName, ptr)); } else { ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::MapFull); @@ -56,3 +60,5 @@ void FunctionManagementService::execute(Message& message) { break; } } + +#endif diff --git a/src/Services/LargePacketTransferService.cpp b/src/Services/LargePacketTransferService.cpp index 15b0e3c3609c41f242a6f6830f570b5598a30f9a..38a0a666b533eb7f7123389a4ad08a7c67a903c9 100644 --- a/src/Services/LargePacketTransferService.cpp +++ b/src/Services/LargePacketTransferService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_LARGEPACKET + #include <Services/LargePacketTransferService.hpp> #include "Message.hpp" #include <etl/String.hpp> @@ -69,7 +72,7 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr stringPart = dataPart; firstDownlinkPartReport(largeMessageTransactionIdentifier, 0, stringPart); - for (uint16_t part = 1; part < (parts - 1u); part++){ + for (uint16_t part = 1; part < (parts - 1U); part++){ for (uint16_t i = 0; i < ECSS_MAX_FIXED_OCTET_STRING_SIZE; i++){ dataPart[i] = message.data[positionCounter]; positionCounter++; @@ -86,5 +89,7 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr positionCounter++; } stringPart = dataPart; - lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1u), stringPart); + lastDownlinkPartReport(largeMessageTransactionIdentifier, (parts - 1U), stringPart); } + +#endif diff --git a/src/Services/MemoryManagementService.cpp b/src/Services/MemoryManagementService.cpp index d592da573bbc7de0b99d86abcc02241b228afb84..e14fce311ad01d1d9f7a2d3cfd46476c52042f41 100644 --- a/src/Services/MemoryManagementService.cpp +++ b/src/Services/MemoryManagementService.cpp @@ -1,5 +1,7 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_MEMORY + #include "Services/MemoryManagementService.hpp" -#include <iostream> #include <cerrno> #include <etl/String.hpp> @@ -236,3 +238,5 @@ void MemoryManagementService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp index a6cb189f271e8694b259931e76ed3f24ece29f86..aef2fd05b0d9662892394d5bab5f306a89ac209d 100644 --- a/src/Services/ParameterService.cpp +++ b/src/Services/ParameterService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_PARAMETER + #include "Services/ParameterService.hpp" #include "Services/Parameter.hpp" @@ -114,3 +117,5 @@ void ParameterService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif diff --git a/src/Services/RequestVerificationService.cpp b/src/Services/RequestVerificationService.cpp index fdab0b0cc3f85a808ef84cc1db546b7c17ff5537..56bcdf07e3d72f9334f9f82c6cb81c98e7f546e0 100644 --- a/src/Services/RequestVerificationService.cpp +++ b/src/Services/RequestVerificationService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_REQUESTVERIFICATION + #include "Services/RequestVerificationService.hpp" void RequestVerificationService::successAcceptanceVerification(const Message& request) { @@ -7,10 +10,10 @@ void RequestVerificationService::successAcceptanceVerification(const Message& re report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); } @@ -23,10 +26,10 @@ void RequestVerificationService::failAcceptanceVerification(const Message& reque report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendEnum16(errorCode); // error code storeMessage(report); @@ -39,10 +42,10 @@ void RequestVerificationService::successStartExecutionVerification(const Message report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count + report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); } @@ -55,10 +58,10 @@ void RequestVerificationService::failStartExecutionVerification(const Message& r report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendEnum16(errorCode); // error code storeMessage(report); @@ -71,10 +74,10 @@ void RequestVerificationService::successProgressExecutionVerification(const Mess report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendByte(stepID); // step ID storeMessage(report); @@ -89,10 +92,10 @@ void RequestVerificationService::failProgressExecutionVerification(const Message report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendByte(stepID); // step ID report.appendEnum16(errorCode); // error code @@ -106,10 +109,10 @@ void RequestVerificationService::successCompletionExecutionVerification(const Me report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); } @@ -122,10 +125,10 @@ void RequestVerificationService::failCompletionExecutionVerification( report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendEnum16(errorCode); // error code storeMessage(report); @@ -139,11 +142,13 @@ void RequestVerificationService::failRoutingVerification(const Message& request, report.appendEnumerated(3, CCSDS_PACKET_VERSION); // packet version number report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 0); // secondary header flag(not implemented) + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID report.appendEnumerated(2, ECSS_SEQUENCE_FLAGS); // sequence flags - report.appendBits(14, 0); // packet sequence count(not implemented) + report.appendBits(14, request.packetSequenceCount); // packet sequence count report.appendEnum16(errorCode); // error code storeMessage(report); } + +#endif diff --git a/src/Services/TestService.cpp b/src/Services/TestService.cpp index 001726da6eee06412b6fff685f9114610c05aaa8..53a42b10a2ee0644be2c610ac09ba05a0e5ae110 100644 --- a/src/Services/TestService.cpp +++ b/src/Services/TestService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_TEST + #include "Services/TestService.hpp" void TestService::areYouAlive(Message& request) { @@ -30,3 +33,5 @@ void TestService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif diff --git a/src/Services/TimeBasedSchedulingService.cpp b/src/Services/TimeBasedSchedulingService.cpp index 170f9c93f6141ea06d15a6ad6b3e4339896756b2..b88e04ec06ee58b32a282269b6d7ac610a0d3d9c 100644 --- a/src/Services/TimeBasedSchedulingService.cpp +++ b/src/Services/TimeBasedSchedulingService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_TIMESCHEDULING + #include "Services/TimeBasedSchedulingService.hpp" TimeBasedSchedulingService::TimeBasedSchedulingService() { @@ -49,7 +52,7 @@ void TimeBasedSchedulingService::insertActivities(Message& request) { // Get the TC packet request uint8_t requestData[ECSS_TC_REQUEST_STRING_SIZE] = {0}; request.readString(requestData, ECSS_TC_REQUEST_STRING_SIZE); - Message receivedTCPacket = msgParser.parseRequestTC(requestData); + Message receivedTCPacket = MessageParser::parseECSSTC(requestData); ScheduledActivity newActivity; // Create the new activity // Assign the attributes to the newly created activity @@ -168,7 +171,7 @@ void TimeBasedSchedulingService::detailReportAllActivities(Message& request) { // todo: append sub-schedule and group ID if they are defined report.appendUint32(activity.requestReleaseTime); - report.appendString(msgParser.convertTCToStr(activity.request)); + report.appendString(MessageParser::composeECSS(activity.request)); } storeMessage(report); // Save the report } @@ -209,7 +212,7 @@ void TimeBasedSchedulingService::detailReportActivitiesByID(Message& request) { report.appendUint16(static_cast<uint16_t>(matchedActivities.size())); for (auto& match : matchedActivities) { report.appendUint32(match.requestReleaseTime); // todo: Replace with the time parser - report.appendString(msgParser.convertTCToStr(match.request)); + report.appendString(MessageParser::composeECSS(match.request)); } storeMessage(report); // Save the report } @@ -293,3 +296,5 @@ void TimeBasedSchedulingService::execute(Message& message) { ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } + +#endif diff --git a/src/Services/TimeManagementService.cpp b/src/Services/TimeManagementService.cpp index 9e281450768a3978df10b857c7d763fcd21cbd78..d12061958917c080c0bda02b9aae15f839b4fec7 100644 --- a/src/Services/TimeManagementService.cpp +++ b/src/Services/TimeManagementService.cpp @@ -1,3 +1,6 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_TIME + #include "Services/TimeManagementService.hpp" void TimeManagementService::cdsTimeReport(TimeAndDate& TimeInfo) { @@ -35,3 +38,5 @@ void TimeManagementService::execute(Message& message) { break; } } + +#endif diff --git a/test/ErrorHandler.cpp b/test/ErrorHandler.cpp index c0dc1393a8a513b024a8a2ea597feda54bcd0b71..804657b3d95151ab4f2be4cdd98293d08df614e1 100644 --- a/test/ErrorHandler.cpp +++ b/test/ErrorHandler.cpp @@ -7,6 +7,7 @@ TEST_CASE("Error: Failed Acceptance", "[errors]") { ErrorHandler::reportError(failedMessage, ErrorHandler::MessageTooShort); REQUIRE(ServiceTests::hasOneMessage()); + CHECK(ServiceTests::thrownError(ErrorHandler::MessageTooShort)); Message report = ServiceTests::get(0); // Check that a TM[1,2] message was returned @@ -17,7 +18,7 @@ TEST_CASE("Error: Failed Acceptance", "[errors]") { CHECK(report.readBits(3) == CCSDS_PACKET_VERSION); CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); - CHECK(report.readBits(1) == 0); + CHECK(report.readBits(1) == 1); CHECK(report.readBits(11) == 47); CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); CHECK(report.readBits(14) == failedMessage.packetSequenceCount); @@ -29,6 +30,7 @@ TEST_CASE("Error: Failed Execution Start", "[errors]") { ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownExecutionStartError); REQUIRE(ServiceTests::hasOneMessage()); + CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionStartError)); Message report = ServiceTests::get(0); // Check that a TM[1,3] message was returned @@ -39,7 +41,7 @@ TEST_CASE("Error: Failed Execution Start", "[errors]") { CHECK(report.readBits(3) == CCSDS_PACKET_VERSION); CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); - CHECK(report.readBits(1) == 0); + CHECK(report.readBits(1) == 1); CHECK(report.readBits(11) == 56); CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); CHECK(report.readBits(14) == failedMessage.packetSequenceCount); @@ -51,6 +53,7 @@ TEST_CASE("Error: Failed Execution Progress", "[errors]") { ErrorHandler::reportProgressError(failedMessage, ErrorHandler::UnknownExecutionProgressError, 0); REQUIRE(ServiceTests::hasOneMessage()); + CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionProgressError)); Message report = ServiceTests::get(0); // Check that a TM[1,6] message was returned @@ -61,7 +64,7 @@ TEST_CASE("Error: Failed Execution Progress", "[errors]") { CHECK(report.readBits(3) == CCSDS_PACKET_VERSION); CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); - CHECK(report.readBits(1) == 0); + CHECK(report.readBits(1) == 1); CHECK(report.readBits(11) == 56); CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); CHECK(report.readBits(14) == failedMessage.packetSequenceCount); @@ -74,6 +77,7 @@ TEST_CASE("Error: Failed Execution Completion", "[errors]") { ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownExecutionCompletionError); REQUIRE(ServiceTests::hasOneMessage()); + CHECK(ServiceTests::thrownError(ErrorHandler::UnknownExecutionCompletionError)); Message report = ServiceTests::get(0); // Check that a TM[1,8] message was returned @@ -84,7 +88,7 @@ TEST_CASE("Error: Failed Execution Completion", "[errors]") { CHECK(report.readBits(3) == CCSDS_PACKET_VERSION); CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); - CHECK(report.readBits(1) == 0); + CHECK(report.readBits(1) == 1); CHECK(report.readBits(11) == 56); CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); CHECK(report.readBits(14) == failedMessage.packetSequenceCount); @@ -96,6 +100,7 @@ TEST_CASE("Error: Failed Routing", "[errors]") { ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownRoutingError); REQUIRE(ServiceTests::hasOneMessage()); + CHECK(ServiceTests::thrownError(ErrorHandler::UnknownRoutingError)); Message report = ServiceTests::get(0); // Check that a TM[1,8] message was returned @@ -106,7 +111,7 @@ TEST_CASE("Error: Failed Routing", "[errors]") { CHECK(report.readBits(3) == CCSDS_PACKET_VERSION); CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); - CHECK(report.readBits(1) == 0); + CHECK(report.readBits(1) == 1); CHECK(report.readBits(11) == 71); CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); CHECK(report.readBits(14) == failedMessage.packetSequenceCount); diff --git a/test/Helpers/TimeAndDate.cpp b/test/Helpers/TimeAndDate.cpp index f5bc35fe51a660fe403847e65eda1b1c21d4b9b3..13866f29ebff1432aa88562fdb7f4020690fc37a 100644 --- a/test/Helpers/TimeAndDate.cpp +++ b/test/Helpers/TimeAndDate.cpp @@ -1,5 +1,6 @@ #include "catch2/catch.hpp" #include "Helpers/TimeAndDate.hpp" +#include "../Services/ServiceTests.hpp" TEST_CASE("Date comparison", "[operands]") { SECTION("Invalid date") { @@ -9,6 +10,9 @@ TEST_CASE("Date comparison", "[operands]") { TimeAndDate InvalidDate3(2030, 2, 2, 74, 5, 6); // error in hour TimeAndDate InvalidDate4(2030, 2, 2, 4, 75, 6); // error in minute TimeAndDate InvalidDate5(2030, 2, 2, 4, 5, 76); // error in seconds + + CHECK(ServiceTests::countErrors() == 6); + CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate)); } SECTION("Different year") { diff --git a/test/Helpers/TimeHelper.cpp b/test/Helpers/TimeHelper.cpp index 00577f65203e1260d1d210e50df4b0f1c71ba4e2..cca057be3459f519e781c4c758d53483e4a1d32b 100644 --- a/test/Helpers/TimeHelper.cpp +++ b/test/Helpers/TimeHelper.cpp @@ -1,5 +1,6 @@ #include "catch2/catch.hpp" #include "Helpers/TimeHelper.hpp" +#include "../Services/ServiceTests.hpp" TEST_CASE("Time format implementation for CDS", "[CDS]") { SECTION("Invalid date") { @@ -16,7 +17,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeHelper::utcToSeconds(TimeInfo); // invalid month - TimeInfo.year = 2018; + TimeInfo.year = 2019; TimeInfo.month = 60; TimeInfo.day = 10; TimeInfo.hour = 10; @@ -26,7 +27,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeHelper::utcToSeconds(TimeInfo); // invalid day - TimeInfo.year = 2018; + TimeInfo.year = 2019; TimeInfo.month = 4; TimeInfo.day = 35; TimeInfo.hour = 10; @@ -36,7 +37,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeHelper::utcToSeconds(TimeInfo); // invalid hour - TimeInfo.year = 2018; + TimeInfo.year = 2019; TimeInfo.month = 4; TimeInfo.day = 10; TimeInfo.hour = 100; @@ -46,7 +47,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeHelper::utcToSeconds(TimeInfo); // invalid minute - TimeInfo.year = 2018; + TimeInfo.year = 2019; TimeInfo.month = 4; TimeInfo.day = 10; TimeInfo.hour = 10; @@ -56,7 +57,7 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeHelper::utcToSeconds(TimeInfo); // invalid second - TimeInfo.year = 2018; + TimeInfo.year = 2019; TimeInfo.month = 4; TimeInfo.day = 10; TimeInfo.hour = 10; @@ -64,6 +65,9 @@ TEST_CASE("Time format implementation for CDS", "[CDS]") { TimeInfo.second = 122; TimeHelper::utcToSeconds(TimeInfo); + + CHECK(ServiceTests::countErrors() == 6); + CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate)); } SECTION("Convert UTC date to elapsed seconds since Unix epoch") { diff --git a/test/Message.cpp b/test/Message.cpp index 25f098322d48d4ec9b80f8fce62937653b22b9ac..a13c29e2b7b59298188b77042069ac60b83af8f8 100644 --- a/test/Message.cpp +++ b/test/Message.cpp @@ -1,5 +1,6 @@ #include <catch2/catch.hpp> #include <Message.hpp> +#include <ServicePool.hpp> TEST_CASE("Message is usable", "[message]") { Message message(5, 17, Message::TC, 3); @@ -155,7 +156,7 @@ TEST_CASE("Requirement 7.3.6 (Real)", "[message][ecss]") { TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.appendString<4>("test"); + message.appendString(String<4>("test")); REQUIRE(message.dataSize == 4); @@ -177,17 +178,19 @@ TEST_CASE("Spare field", "[message]") { message1.appendByte(1); message1.appendHalfword(2); - message1.appendBits(1, 5); + message1.appendBits(1, 1); message1.finalize(); + CHECK(message1.data[3] == 0b10000000); CHECK(message1.dataSize == 4); Message message2(0, 0, Message::TM, 0); message2.appendByte(1); message2.appendHalfword(2); - message2.appendBits(2, 5); + message2.appendBits(2, 3); message2.finalize(); + CHECK(message2.data[3] == 0b11000000); CHECK(message2.dataSize == 4); Message message3(0, 0, Message::TM, 0); @@ -197,6 +200,7 @@ TEST_CASE("Spare field", "[message]") { message3.appendBits(3, 5); message3.finalize(); + CHECK(message3.data[3] == 0b10100000); CHECK(message3.dataSize == 4); Message message4(0, 0, Message::TM, 0); @@ -206,6 +210,7 @@ TEST_CASE("Spare field", "[message]") { message4.appendBits(4, 5); message4.finalize(); + CHECK(message4.data[3] == 0b01010000); CHECK(message4.dataSize == 4); Message message5(0, 0, Message::TM, 0); @@ -215,6 +220,7 @@ TEST_CASE("Spare field", "[message]") { message5.appendBits(5, 5); message5.finalize(); + CHECK(message5.data[3] == 0b00101000); CHECK(message5.dataSize == 4); Message message6(0, 0, Message::TM, 0); @@ -224,6 +230,7 @@ TEST_CASE("Spare field", "[message]") { message6.appendBits(6, 5); message6.finalize(); + CHECK(message6.data[3] == 0b00010100); CHECK(message6.dataSize == 4); Message message7(0, 0, Message::TM, 0); @@ -233,6 +240,7 @@ TEST_CASE("Spare field", "[message]") { message7.appendBits(7, 5); message7.finalize(); + CHECK(message7.data[3] == 0b00001010); CHECK(message7.dataSize == 4); Message message8(0, 0, Message::TM, 0); @@ -242,6 +250,7 @@ TEST_CASE("Spare field", "[message]") { message8.appendBits(8, 5); message8.finalize(); + CHECK(message8.data[3] == 0b00000101); CHECK(message8.dataSize == 4); Message message9(0, 0, Message::TM, 0); @@ -253,3 +262,74 @@ TEST_CASE("Spare field", "[message]") { CHECK(message9.dataSize == 3); } + +TEST_CASE("Message type counter", "[message]") { + SECTION("Message counting") { + Message message1(0, 0, Message::TM, 0); + message1.finalize(); + CHECK(message1.messageTypeCounter == 0); + + Message message2(0, 0, Message::TM, 0); + message2.finalize(); + CHECK(message2.messageTypeCounter == 1); + } + + SECTION("Different message types") { + Message message1(0, 1, Message::TM, 0); + message1.finalize(); + CHECK(message1.messageTypeCounter == 0); + + Message message2(0, 2, Message::TM, 0); + message2.finalize(); + CHECK(message2.messageTypeCounter == 0); + } + + SECTION("Message counter overflow") { + for (int i = 0; i <= 65534; i++) { + Message message(0, 3, Message::TM, 0); + message.finalize(); + } + + Message message1(0, 3, Message::TM, 0); + message1.finalize(); + CHECK(message1.messageTypeCounter == 65535); + + Message message2(0, 3, Message::TM, 0); + message2.finalize(); + CHECK(message2.messageTypeCounter == 0); + } +} + +TEST_CASE("Packet sequence counter", "[message]") { + SECTION("Packet counting") { + Message message1(0, 0, Message::TM, 0); + message1.finalize(); + CHECK(message1.packetSequenceCount == 0); + + Message message2(0, 0, Message::TM, 0); + message2.finalize(); + CHECK(message2.packetSequenceCount == 1); + + // Different message type check + Message message3(1, 2, Message::TM, 0); + message3.finalize(); + CHECK(message3.packetSequenceCount == 2); + } + + SECTION("Packet counter overflow") { + Services.reset(); + + for (int i = 0; i <= 16382; i++) { + Message message(0, 3, Message::TM, 0); + message.finalize(); + } + + Message message1(0, 3, Message::TM, 0); + message1.finalize(); + CHECK(message1.packetSequenceCount == 16383); + + Message message2(0, 3, Message::TM, 0); + message2.finalize(); + CHECK(message2.packetSequenceCount == 0); + } +} diff --git a/test/MessageParser.cpp b/test/MessageParser.cpp index d11d42ab54e4bfd796f1d726855bda45489458aa..c416f1b5d6cdecf2c0b19f311bde1963540b3716 100644 --- a/test/MessageParser.cpp +++ b/test/MessageParser.cpp @@ -9,30 +9,68 @@ TEST_CASE("TC message parsing", "[MessageParser]") { - MessageParser messageParser; - uint8_t packet[] = {0x18, 0x07, 0xc0, 0x4d, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; + uint8_t packet[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; - Message message = messageParser.parse(packet, 16); + Message message = MessageParser::parse(packet, 16); CHECK(message.packetType == Message::TC); CHECK(message.applicationId == 7); + CHECK(message.packetSequenceCount == 8199); CHECK(message.dataSize == 5); CHECK(message.serviceType == 129); CHECK(message.messageType == 31); CHECK(memcmp(message.data, "hello", 5) == 0); } -TEST_CASE("TC data parsing into a message", "[MessageParser]") {} +TEST_CASE("TC Message parsing into a string", "[MessageParser]") { + uint8_t wantedPacket[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, + 0x6f}; + + Message message; + message.packetType = Message::TC; + message.applicationId = 7; + message.serviceType = 129; + message.messageType = 31; + message.packetSequenceCount = 8199; + memcpy(message.data, "hello", 5); + message.dataSize = 5; + + String<CCSDS_MAX_MESSAGE_SIZE> createdPacket = MessageParser::compose(message); + + CHECK(createdPacket.size() == 16); + // The two parentheses are necessary so that Catch2 doesn't try to parse the strings here + CHECK((createdPacket == String<16>(wantedPacket))); +} TEST_CASE("TM message parsing", "[MessageParser]") { - MessageParser messageParser; uint8_t packet[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x0c, 0x20, 0x16, 0x11, - 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; + 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; - Message message = messageParser.parse(packet, 18); + Message message = MessageParser::parse(packet, 18); CHECK(message.packetType == Message::TM); CHECK(message.applicationId == 2); + CHECK(message.packetSequenceCount == 77); CHECK(message.dataSize == 7); CHECK(message.serviceType == 22); CHECK(message.messageType == 17); CHECK(memcmp(message.data, "hellohi", 7) == 0); } + +TEST_CASE("TM Message parsing into a string", "[MessageParser]") { + uint8_t wantedPacket[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x0c, 0x20, 0x16, 0x11, + 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; + + Message message; + message.packetType = Message::TM; + message.applicationId = 2; + message.packetSequenceCount = 77; + message.serviceType = 22; + message.messageType = 17; + memcpy(message.data, "hellohi", 7); + message.dataSize = 7; + + String<CCSDS_MAX_MESSAGE_SIZE> createdPacket = MessageParser::compose(message); + + CHECK(createdPacket.size() == 18); + // The two parentheses are necessary so that Catch2 doesn't try to parse the strings here + CHECK((createdPacket == String<18>(wantedPacket))); +} diff --git a/test/Services/FunctionManagementService.cpp b/test/Services/FunctionManagementService.cpp index 64683103f21ee59c80db329077d148ca6e335266..a793c3e9035ed37eb4aa9351d235fe1774f866a6 100644 --- a/test/Services/FunctionManagementService.cpp +++ b/test/Services/FunctionManagementService.cpp @@ -8,7 +8,7 @@ FunctionManagementService& fms = Services.functionManagement; uint8_t globalVariable = 10; -void test(String<MAX_ARG_LENGTH> a) { +void test(String<ECSS_FUNCTION_MAX_ARG_LENGTH> a) { globalVariable = a[0]; } @@ -17,10 +17,10 @@ TEST_CASE("ST[08] - Call Tests") { ServiceTests::reset(); globalVariable = 10; - fms.include(String<FUNC_NAME_LENGTH>("test"), &test); + fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test); Message msg(8, 1, Message::TC, 1); - msg.appendFixedString(String<FUNC_NAME_LENGTH>("test")); + msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("test")); msg.appendByte(199); MessageParser::execute(msg); @@ -32,9 +32,9 @@ TEST_CASE("ST[08] - Call Tests") { ServiceTests::reset(); globalVariable = 10; - fms.include(String<FUNC_NAME_LENGTH>("test"), &test); + fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test); Message msg(8, 1, Message::TC, 1); - msg.appendFixedString(String<FUNC_NAME_LENGTH>("t3st")); + msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("t3st")); MessageParser::execute(msg); CHECK(ServiceTests::get(0).messageType == 4); @@ -47,16 +47,16 @@ TEST_CASE("ST[08] - Call Tests") { ServiceTests::reset(); globalVariable = 10; - fms.include(String<FUNC_NAME_LENGTH>("test"), &test); + fms.include(String<ECSS_FUNCTION_NAME_LENGTH>("test"), &test); Message msg(8, 1, Message::TC, 1); - msg.appendFixedString(String<FUNC_NAME_LENGTH>("test")); + msg.appendFixedString(String<ECSS_FUNCTION_NAME_LENGTH>("test")); msg.appendString(String<65> ("eqrhjweghjhwqgthjkrghthjkdsfhgsdfhjsdjsfdhgkjdfsghfjdgkdfsgdfgsgd")); MessageParser::execute(msg); CHECK(ServiceTests::get(0).messageType == 4); CHECK(ServiceTests::get(0).serviceType == 1); - CHECK(ServiceTests::countErrors() == 2); + CHECK(ServiceTests::countErrors() == 1); CHECK(globalVariable == 10); } } @@ -67,9 +67,9 @@ TEST_CASE("ST[08] - Insert Tests") { ServiceTests::reset(); std::string name = "test"; // FOR TESTING ONLY! - for (int i = 0; i < FUNC_MAP_SIZE + 1; i++) { + for (int i = 0; i < ECSS_FUNCTION_MAP_SIZE + 1; i++) { name += std::to_string(i); // different names to fill up the map - fms.include(String<FUNC_NAME_LENGTH>(name.c_str()), &test); + fms.include(String<ECSS_FUNCTION_NAME_LENGTH>(name.c_str()), &test); } CHECK(ServiceTests::thrownError(ErrorHandler::InternalErrorType::MapFull)); } diff --git a/test/Services/LargePacketTransferService.cpp b/test/Services/LargePacketTransferService.cpp index 4d75409acdf09ea4a9a41c973c31423b77c2b575..be5c9fdf9baf89153f22b32e9f15f29e6dee67b3 100644 --- a/test/Services/LargePacketTransferService.cpp +++ b/test/Services/LargePacketTransferService.cpp @@ -86,5 +86,6 @@ TEST_CASE("Split function", "[no][service]") { message5.appendUint8(ServiceTests::get(i).readUint8()); } } - CHECK(message == message5); + + CHECK(message.bytesEqualWith(message5)); } diff --git a/test/Services/RequestVerificationService.cpp b/test/Services/RequestVerificationService.cpp index 6576bdbaa5b2183432e747a1a76b25788f646fda..df0ea7e26c31c971b940c391f453d9908fa15696 100644 --- a/test/Services/RequestVerificationService.cpp +++ b/test/Services/RequestVerificationService.cpp @@ -20,7 +20,7 @@ TEST_CASE("TM[1,1]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -41,7 +41,7 @@ TEST_CASE("TM[1,2]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -63,7 +63,7 @@ TEST_CASE("TM[1,3]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -84,7 +84,7 @@ TEST_CASE("TM[1,4]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -106,7 +106,7 @@ TEST_CASE("TM[1,5]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -128,7 +128,7 @@ TEST_CASE("TM[1,6]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -151,7 +151,7 @@ TEST_CASE("TM[1,7]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -171,7 +171,7 @@ TEST_CASE("TM[1,8]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count @@ -193,7 +193,7 @@ TEST_CASE("TM[1,10]", "[service][st01]") { // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDS_PACKET_VERSION); // packet version number CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 0); // secondary header flag + CHECK(response.readBits(1) == 1); // secondary header flag CHECK(response.readEnumerated(11) == 3); // application process ID CHECK(response.readEnumerated(2) == ECSS_SEQUENCE_FLAGS); // sequence flags CHECK(response.readBits(14) == 0); // packet sequence count diff --git a/test/Services/TimeBasedSchedulingService.cpp b/test/Services/TimeBasedSchedulingService.cpp index 56a7267df7c9b2859a9d813bcd4ed780f01430e2..e74ff765734bd695d8c0a8930f36158d5e2d4b20 100644 --- a/test/Services/TimeBasedSchedulingService.cpp +++ b/test/Services/TimeBasedSchedulingService.cpp @@ -33,7 +33,6 @@ namespace unit_test } // namespace unit_test Message testMessage1, testMessage2, testMessage3, testMessage4; -MessageParser msgParser; auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time bool messagesPopulated = false; // Indicate whether the test messages are initialized @@ -71,19 +70,19 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { // Test activity 1 receivedMessage.appendUint32(currentTime + 1556435); - receivedMessage.appendString(msgParser.convertTCToStr(testMessage1)); + receivedMessage.appendMessage(testMessage1, ECSS_TC_REQUEST_STRING_SIZE); // Test activity 2 receivedMessage.appendUint32(currentTime + 1957232); - receivedMessage.appendString(msgParser.convertTCToStr(testMessage2)); + receivedMessage.appendMessage(testMessage2, ECSS_TC_REQUEST_STRING_SIZE); // Test activity 3 receivedMessage.appendUint32(currentTime + 1726435); - receivedMessage.appendString(msgParser.convertTCToStr(testMessage3)); + receivedMessage.appendMessage(testMessage3, ECSS_TC_REQUEST_STRING_SIZE); // Test activity 4 receivedMessage.appendUint32(currentTime + 17248435); - receivedMessage.appendString(msgParser.convertTCToStr(testMessage4)); + receivedMessage.appendMessage(testMessage4, ECSS_TC_REQUEST_STRING_SIZE); // Insert activities in the schedule. They have to be inserted sorted timeService.insertActivities(receivedMessage); @@ -115,14 +114,15 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") { auto scheduledActivities = activityInsertion(timeBasedService); REQUIRE(scheduledActivities.size() == 4); + REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 1556435); REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435); REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232); REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 17248435); - REQUIRE(scheduledActivities.at(0)->request == testMessage1); - REQUIRE(scheduledActivities.at(1)->request == testMessage3); - REQUIRE(scheduledActivities.at(2)->request == testMessage2); - REQUIRE(scheduledActivities.at(3)->request == testMessage4); + REQUIRE(testMessage1.bytesEqualWith(scheduledActivities.at(0)->request)); + REQUIRE(testMessage3.bytesEqualWith(scheduledActivities.at(1)->request)); + REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(2)->request)); + REQUIRE(testMessage4.bytesEqualWith(scheduledActivities.at(3)->request)); SECTION("Error throw test") { Message receivedMessage(11, 4, Message::TC, 1); @@ -198,7 +198,7 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { // Make sure the new value is inserted sorted REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1957232 + timeShift); - REQUIRE(scheduledActivities.at(3)->request == testMessage2); + REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(3)->request)); } SECTION("Negative Shift") { @@ -213,7 +213,7 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { // Output should be sorted REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1957232 - 250000); - REQUIRE(scheduledActivities.at(1)->request == testMessage2); + REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(1)->request)); } SECTION("Error throw on wrong request ID") { @@ -275,7 +275,7 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]" Message receivedTCPacket; uint8_t receivedDataStr[ECSS_TC_REQUEST_STRING_SIZE]; response.readString(receivedDataStr, ECSS_TC_REQUEST_STRING_SIZE); - receivedTCPacket = msgParser.parseRequestTC(receivedDataStr); + receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); if (i == 0) { REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime); @@ -379,10 +379,10 @@ TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]") Message receivedTCPacket; uint8_t receivedDataStr[ECSS_TC_REQUEST_STRING_SIZE]; response.readString(receivedDataStr, ECSS_TC_REQUEST_STRING_SIZE); - receivedTCPacket = msgParser.parseRequestTC(receivedDataStr); + receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); REQUIRE(receivedReleaseTime == scheduledActivities.at(i)->requestReleaseTime); - REQUIRE(scheduledActivities.at(i)->request == receivedTCPacket); + REQUIRE(receivedTCPacket.bytesEqualWith(scheduledActivities.at(i)->request)); } } @@ -408,7 +408,7 @@ TEST_CASE("TC[11,5] Activity deletion by ID", "[service][st11]") { REQUIRE(scheduledActivities.size() == 3); REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 17248435); - REQUIRE(scheduledActivities.at(2)->request == testMessage4); + REQUIRE(testMessage4.bytesEqualWith(scheduledActivities.at(2)->request)); } SECTION("Error throw on wrong request ID") { diff --git a/test/TestPlatform.cpp b/test/TestPlatform.cpp index a4cbee4d07452934aec010d06061b4479cb2d775..6833d3edeb9222d5d106f0ddf5dcac0e4f7ad5a7 100644 --- a/test/TestPlatform.cpp +++ b/test/TestPlatform.cpp @@ -3,6 +3,7 @@ #include <catch2/catch.hpp> #include <Message.hpp> #include <Service.hpp> +#include <Logger.hpp> #include "Services/ServiceTests.hpp" // Explicit template specializations for the logError() function @@ -34,6 +35,10 @@ void ErrorHandler::logError(ErrorType errorType) { ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType); } +void Logger::log(Logger::LogLevel level, etl::istring & message) { + // Logs while testing are completely ignored +} + struct ServiceTestsListener : Catch::TestEventListenerBase { using TestEventListenerBase::TestEventListenerBase; // inherit constructor @@ -43,8 +48,7 @@ struct ServiceTestsListener : Catch::TestEventListenerBase { // An Error was thrown with this Message. If you expected this to happen, please call a // corresponding assertion function from ServiceTests to silence this message. - // TODO: Uncomment the following line as soon as Issue #19 is closed - // CHECK(ServiceTests::hasNoErrors()); + CHECK(ServiceTests::hasNoErrors()); } }