diff --git a/.gitignore b/.gitignore index a15b9233fb1eaf13a108d3a161c2d72f9c52a510..47e3242aa7cbc3177f1176fe4b1fc4d47ddf4aa5 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,4 @@ __pycache__ # IDEs .vscode +.DS_Store diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0b3ef569bb6f779875aacc94788e471b2e836135..fdc375a8c758b58f4a066d8e636fc913261ee5dd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -40,17 +40,19 @@ cppcheck: - ./cppcheck-html-report clang-tidy: - image: spacedot/clang-tools:13.0.0-html-1.3.7 + image: spacedot/clang-tools:13.0.0-html-1.4.1 stage: analyze script: - cd $CI_PROJECT_DIR - cmake -B ./build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON - - clang-tidy -p $CI_PROJECT_DIR/build/compile_commands.json --checks=* `find $CI_PROJECT_DIR/src -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` >> clang-tidy-output.log + - clang-tidy -p $CI_PROJECT_DIR/build/compile_commands.json --config-file=$CI_PROJECT_DIR/ci/.clang-tidy --use-color `find $CI_PROJECT_DIR/src $CI_PROJECT_DIR/inc -not -path "*/Platform/*" -type f -regextype posix-egrep -regex '.*\.(cpp|hpp|c|h)'` | tee clang-tidy-output.log after_script: + - sed -e 's/\x1b\[[0-9;]*m//g' -i clang-tidy-output.log - mkdir clang-tidy-html-report - clang-tidy-html clang-tidy-output.log - mv clang.html clang-tidy-html-report artifacts: + when: always paths: - ./clang-tidy-html-report @@ -85,9 +87,8 @@ ikos: script: - cd $CI_PROJECT_DIR - ikos-scan cmake . - - ikos-scan make + - ikos-scan make tests - ikos tests.bc - - ikos ecss_services.bc - ikos-report -o=ikos-report.txt output.db after_script: - mv ikos-report.txt ikos-report @@ -105,7 +106,7 @@ tests: - cd $CI_PROJECT_DIR - cmake . -DCMAKE_CXX_FLAGS="-g -O0 --coverage" && make tests -j$(nproc) - lcov -q --capture --initial --directory . -o coverage_base - - ./tests --use-colour yes + - ./tests --colour-mode ansi - lcov -q --capture --directory . -o coverage_tests - lcov -q -a coverage_base -a coverage_tests -o coverage_total_unfiltered - lcov -q --remove coverage_total_unfiltered "${PWD}/lib/*" "${PWD}/CMakeFiles/*" "${PWD}/test/*" "${PWD}/src/main.cpp" -o coverage_total_filtered @@ -126,8 +127,10 @@ pages: - find ./.public - mv .public public after_script: + - "echo Artifacts for this build: ${CI_JOB_URL}/artifacts/browse" - "echo Base page for this branch: ${CI_PAGES_URL}" artifacts: + expose_as: 'build artifacts and documentation' paths: - public # Upload the resulting website # only: diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 72b50578eec402676f04dcd624ca933b979aeb3e..388f330c5907583761a5dca29765574542fc76fd 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -3,10 +3,8 @@ <component name="VcsDirectoryMappings"> <mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="$PROJECT_DIR$/ci/page_style/doxygen_dark_theme" vcs="Git" /> - <mapping directory="$PROJECT_DIR$/doxygen-awesome-css" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/Catch2" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/etl" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/logger" vcs="Git" /> - <mapping directory="$PROJECT_DIR$/lib/logger/lib/etl" vcs="Git" /> </component> </project> \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 24ae6a8fd012616ff86b4a546faef0c52dd084a2..9088f50cbff637517328b69234b6768a36a86836 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,8 @@ set(ECSS_CONFIGURATION "${PROJECT_SOURCE_DIR}/inc/Platform/x86" CACHE PATH ) include_directories(${ECSS_CONFIGURATION}) +add_compile_options(-Wvla) + # Specify the .cpp files common across all targets add_library(common OBJECT lib/logger/src/Logger.cpp @@ -73,5 +75,5 @@ IF (EXISTS "${PROJECT_SOURCE_DIR}/lib/Catch2/CMakeLists.txt") ${test_main_SRC} ${test_SRC} ) - target_link_libraries(tests Catch2::Catch2) + target_link_libraries(tests Catch2::Catch2WithMain) ENDIF () diff --git a/Doxyfile b/Doxyfile index 419b742a3ebf4acbc6929bfea78c59b610269b4d..713a0c961b63058f89cd68f9fbb437732586a0fa 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1534,7 +1534,7 @@ FORMULA_TRANSPARENT = YES # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -USE_MATHJAX = NO +USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: diff --git a/ci/.clang-tidy b/ci/.clang-tidy index 478e052795977bbfe87cab6a8dc73eeeee60e665..e86d3dac3087d45dd2185437a3b8a88b3624a0ce 100644 --- a/ci/.clang-tidy +++ b/ci/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: > +Checks: > -clang-diagnostic-error, clang-analyzer-*, bugprone-*, @@ -7,20 +7,26 @@ Checks: > 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, + google-*,-google-readability-todo,-google-build-using-namespace,-google-runtime-int, fuchsia-statically-constructed-objects, hicpp-exception-baseclass, hicpp-signed-bitwise, - llvm-*,-llvm-include-order,-llvm-header-guard, + llvm-*,-llvm-include-order,-llvm-header-guard,-llvm-else-after-return, modernize-use-auto, modernize-use-equals-default, misc-*, -misc-non-private-member-variables-in-classes, performance-*, - readability-*,-readability-magic-numbers, + readability-*,-readability-magic-numbers,-readability-else-after-return, 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\/).)*$' +WarningsAsErrors: '*,-bugprone-easily-swappable-parameters,-misc-unused-parameters,-llvm-header-guard, +-cppcoreguidelines-pro-type-member-init,-cppcoreguidelines-non-private-member-variables-in-classes, +-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, +-performance-no-int-to-ptr' +#HeaderFilterRegex: '^ecss-services\/((?!lib\/).)*$' +#HeaderFilterRegex: '^((?!/lib/).)*$' AnalyzeTemporaryDtors: false ... diff --git a/docs/ecss_overview.md b/docs/ecss_overview.md index 82e0011181d26b321d3163e2f5bb1d947e97eb54..ba95f4d2cc280dd5d5bb119d90fc4fb522380a67 100644 --- a/docs/ecss_overview.md +++ b/docs/ecss_overview.md @@ -63,6 +63,8 @@ Each service is further divided into some **subservices** that are logical group large space mission may split every subservice into different parts of the hardware. However, this implementation _makes no distinction between subservices_. +### Standard's Services + <b>`ST[01]`: Request verification</b> Provides acknowledgement or failure reports for executed commands. This service essentially informs the operators about @@ -182,7 +184,11 @@ Provides the capability of executing TCs when the spacecraft reaches a specific Provides the capability of managing on-board file systems, with functions such as *copy*, *move*, *delete*, or *create directory*. -The capability also exists to define _custom_ services and message types, if needed for each mission. +### Custom Services + +<b>`ST[128]`: Dummy Service</b> + +Provides the capability of storing log strings as ECSS Messages. ## Trivia - Version C of the standard contains 656 pages, often filled with verbose requirements and difficult definitions. Every diff --git a/docs/usage_with_microcontroller.md b/docs/usage_with_microcontroller.md index 05f09b6f1f8e3e714846e0a0eab2e618b4d81c35..293072c75069b4ac6ddbac858131addf68b75894 100644 --- a/docs/usage_with_microcontroller.md +++ b/docs/usage_with_microcontroller.md @@ -17,6 +17,7 @@ compile this library independently for different projects. ## Examples Some people have already integrated this library with their own code: + - [AcubeSAT OBC Software](https://gitlab.com/acubesat/obc/obc-software), integration with ATSAMV71Q21 and FreeRTOS - [AcubeSAT FDIR Thesis](https://github.com/kongr45gpen/fdir-demo), integration with ATSAMV71Q21 and FreeRTOS - [AcubeSAT OBC Mockup](https://gitlab.com/acubesat/obc/mockup-4), integration with STM32L4S9 and FreeRTOS @@ -57,13 +58,15 @@ your project. The following sections list and explain the functions that need to be implemented by library users. It is enough to place the function body inside a compiled `.cpp` file. Otherwise, the linker will show errors about undefined functions. -For more details on the requirements, arguments and return values of each function, refer to their respective documentation. +For more details on the requirements, arguments and return values of each function, refer to their respective +documentation. ### Logger The logger is responsible for outputting messages through a serial interface used for debugging purposes. You need to define the following function: + ```cpp void Logger::log(Logger::LogLevel level, etl::istring & message); ``` @@ -73,6 +76,7 @@ be inspected by the developers. It is suggested to add any extra information tha execution thread. An example definition can be as follows: + ```cpp void Logger::log(Logger::LogLevel level, etl::istring &message) { etl::string<20> time; @@ -88,16 +92,19 @@ void Logger::log(Logger::LogLevel level, etl::istring &message) { ``` #### Setting the log level + You will also need to set the **minimum log level** of your application by setting the relevant `LOGLEVEL` constants. For example, you can add this to your `CMakeLists.txt` to log all messages: + ```cmake add_compile_definitions(LOGLEVEL_TRACE) ``` For a list of all possible log levels, refer to the documentation of the Logger. -@note If you want to have different log levels for different parts of your application, you can use [`target_compile_definitions`](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html). +@note If you want to have different log levels for different parts of your application, you can +use [`target_compile_definitions`](https://cmake.org/cmake/help/latest/command/target_compile_definitions.html). @note All logs with a level lower than the specified one will not be compiled at all, and will not be included in any form in the resulting binary. This means that disabled log messages _will not_ have any negative impact on the @@ -111,6 +118,7 @@ Whenever PUS telemetry is generated, it needs to be transmitted or sent to a rec In this function, you can transmit the message via an antenna, send it through an interface for debugging, or both. An example definition can be as follows: + ```cpp void Service::storeMessage(Message& message) { message.finalize(); @@ -129,6 +137,7 @@ The @ref ErrorHandler::logError is responsible for logging errors for debugging ErrorHandler::logError function is used strictly for debugging purposes, and can be left empty if desired. An example definition can be as follows: + ```cpp template <typename ErrorType> void ErrorHandler::logError(const Message& message, ErrorType errorType) { @@ -144,13 +153,35 @@ void ErrorHandler::logError(ErrorType errorType) { } ``` +### Using the Timer + +A significant portion of the PUS functionalities uses timestamps, for example in order to compare message reception times, +or reporting event times as telemetry. However, time tracking is performed by the hardware, so the time-related functions +existing in the Services should be re-implemented, as the current functions contain dummy values. The **TimeGetter** class +is responsible for giving access to a Real-Time Clock, enabling the above capabilities. + +#### Getting the current UTC time + +The **getCurrentTimeUTC** function computes the current UTC time. In order to actually acquire the current time, +modify the arguments (which are dummy by default), so that they correspond to their real values. An example is given +below. + +```cpp +UTCTimestamp TimeGetter::getCurrentTimeUTC() { + UTCTimestamp currentTime(onBoardYear, onBoardMonth, onBoardDay, onBoardHour, onBoardMinute, onBoardSecond); + return currentTime; +} +``` + ## Service initialisation Platform-specific code also gives a chance to every Service to use pre-initialised entities (e.g. parameters, monitoring definitions etc.) during boot. The following functions are called at initialisation: + 1. @ref ParameterService::initializeParameterMap An example definition can be as follows: + ```cpp Parameter<uint8_t> parameter1(200); Parameter<uint8_t> parameter2(150); @@ -160,11 +191,62 @@ void ParameterService::initializeParameterMap() { parameters = {{0, parameter1}, {1, parameter2}, {2, parameter3}}; } ``` +2. @ref TimeBasedSchedulingService::notifyNewActivityAddition + +The TimeBasedSchedulingService is responsible for storing the activities/TCs to be executed. However, if this list of +stored activities is emptied, the Platform Task responsible to execute these TCs should be paused. With this +function, a notifier can be implemented and called by `TimeBasedScheduling::insertActivities()` to notify the +Task to restart after a new activity is inserted. + +An example implementation for FreeRTOS can be as follows: +```cpp +void TimeBasedSchedulingService::notifyNewActivityAddition() { + if (scheduledActivities.size() >= 1) { + xTaskNotify(TaskList::timeBasedSchedulingTask->taskHandle, 0, eNoAction); + } +} +``` + +3. @ref ParameterStatisticsService::initializeStatisticsMap + +The function is called by the constructor of the class, making sure that the statistics will be initialized properly +after creating an instance of the ST04 Service. This function should be implemented according to the specific +needs, structure and parameters of an individual project. It basically iterates over the statistics list and initializes +statistics for parameters of selected IDs. An example is provided below. Let's assume that we have stored the +parameter IDs, and we use a data structure to store the statistics. Our initialization function, based on such +structure should look like this: + +```c++ +namespace PlatformParameters { + enum ParameterIDs : uint16_t { + OnBoardMinute = 0, + Temperature = 1, + }; +} + +namespace ParameterStatistics { + static etl::array<Statistic, ECSSMaxStatisticParameters> statistics = { + Statistic(), + Statistic(), + }; +} + +void ParameterStatisticsService::initializeStatisticsMap() { + using namespace PlatformParameters; + uint16_t statisticParameterIDs[] = {parameterIDs::onBoardMinute, parameterIDs::temperature}; + uint8_t idIndex = 0; + + for (auto& statistic: ParameterStatistics::statistics) { + statisticsMap.insert({statisticParameterIDs[idIndex++], statistic}); + } +} +``` ## Receiving messages After making sure that your code compiles, you need to provide a way of feeding received TC into the services. This can be done easily: + ```cpp MessageParser::parse(string, size); ``` diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 79eb5bd7b14d153b5230a8359e314eed492a4cc9..9801dbb6b1875570c5bec58444ff3b7f4502fc66 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -1,6 +1,7 @@ #ifndef ECSS_SERVICES_ECSS_DEFINITIONS_H #define ECSS_SERVICES_ECSS_DEFINITIONS_H +#include <chrono> #include <cstdint> /** * @defgroup ECSSDefinitions ECSS Defined Constants @@ -24,10 +25,25 @@ */ inline const uint16_t ECSSMaxMessageSize = 1024U; +/** + * The size of each CCSDS Space packet primary header + */ +inline const uint16_t CCSDSPrimaryHeaderSize = 6U; + +/** + * The size of each ECSS Telemetry packet's secondary header + */ +inline const uint16_t ECSSSecondaryTMHeaderSize = 11U; + +/** + * The size of each ECSS Telecommand packet's secondary header + */ +inline const uint16_t ECSSSecondaryTCHeaderSize = 5U; + /** * The maximum size of a regular ECSS message, plus its headers and trailing data, in bytes */ -inline const uint16_t CCSDSMaxMessageSize = ECSSMaxMessageSize + 6u + 6u + 2u; +inline const uint16_t CCSDSMaxMessageSize = ECSSMaxMessageSize + CCSDSPrimaryHeaderSize + ECSSSecondaryTMHeaderSize + 2U; /** * The maximum size of a string to be read or appended to a Message, in bytes @@ -92,7 +108,7 @@ inline const uint8_t ECSSMaxNumberOfTimeSchedActivities = 10; * have in order * @see TimeBasedSchedulingService */ -inline const uint8_t ECSSTimeMarginForActivation = 60; +inline constexpr std::chrono::duration<uint8_t> ECSSTimeMarginForActivation(60); /** * @brief Maximum size of an event's auxiliary data @@ -104,7 +120,7 @@ inline const uint8_t ECSSEventDataAuxiliaryMaxSize = 64; * @brief Size of the multimap that holds every event-action definition * @see EventActionService */ -inline const uint16_t ECSSEventActionStructMapSize = 256; +inline const uint16_t ECSSEventActionStructMapSize = 100; /** * The maximum delta between the specified release time and the actual release time @@ -115,7 +131,7 @@ inline const uint8_t ECSSMaxDeltaOfReleaseTime = 60; /** * The max number of simply commutated parameters per housekeeping structure in ST[03] */ -inline const uint16_t ECSSMaxSimplyCommutatedParameters = 10; +inline const uint16_t ECSSMaxSimplyCommutatedParameters = 30; /** * The number of functions supported by the \ref FunctionManagementService @@ -142,7 +158,7 @@ inline const uint16_t LoggerMaxMessageSize = 512; /** * @brief Size of the map holding references to each Parameter object for the ST[20] parameter service */ -inline const uint8_t ECSSParameterCount = 12; +inline const uint16_t ECSSParameterCount = 500; /** * @brief Defines whether the optional CRC field is included @@ -167,7 +183,7 @@ inline const uint16_t ECSSMaxPacketStoreSizeInBytes = 1000; /** * @brief the max number of TM packets that a packet store in ST[15] can store */ -inline const uint16_t ECSSMaxPacketStoreSize = 20; +inline const uint16_t ECSSMaxPacketStoreSize = 10; /** * @brief the max number of packet stores that a packet selection subservice can handle in ST[15] @@ -226,7 +242,7 @@ inline const uint8_t ECSSMaxEventDefinitionIDs = 15; /** * Limits noting the minimum and maximum valid Virtual Channels used by the Storage and Retrieval subservice */ -inline struct { +inline const struct { uint8_t min = 1; uint8_t max = 10; } VirtualChannelLimits; @@ -236,5 +252,11 @@ inline struct { */ inline const uint8_t ECSSMaxMonitoringDefinitions = 4; +/** + * 6.18.2.2 The applicationId that is assigned on the specific device that runs these Services. + * In the ECSS-E-ST-70-41C the application ID is also referred as application process. + */ +inline const uint16_t ApplicationId = 1; + /** @} */ #endif // ECSS_SERVICES_ECSS_DEFINITIONS_H diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index 216c9b1bce25a21d9903973c7f1a4dcf543b033e..b10eee51c56186d2c56a6972c49778d504337358 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -60,7 +60,6 @@ public: * Asked a Message type that doesn't exist */ UnknownMessageType = 7, - /** * Asked to append unnecessary spare bits */ @@ -93,6 +92,14 @@ public: * Invalid TimeStamp parameters at creation */ InvalidTimeStampInput = 15, + /** + * A requested element is not found + */ + ElementNotInArray = 16, + /** + * Timestamp out of bounds to be stored or converted + */ + TimeStampOutOfBounds = 17, }; /** @@ -131,240 +138,250 @@ public: UnknownExecutionStartError = 0, /** * In the Event Action Service, in the addEventActionDefinition function an attempt was - * made to add an event Action Definition with an eventActionDefinitionID that exists + * made to add an event Action Definition with an eventDefinitionID that exists */ - EventActionDefinitionIDExistsError = 1, + EventDefinitionIDExistsError = 1, + + /** + *In the Event Action Service, in the addEventActionDefinition function an attempt was + * made to add an event Action Definition that is already enabled + */ + EventActionEnabledError = 2, /** * In the Event Action Service, in the deleteEventActionDefinition function, an attempt * was made to delete an event action definition that was enabled */ - EventActionDeleteEnabledDefinitionError = 2, + EventActionDeleteEnabledDefinitionError = 3, /** * In the Event Action Service, an access attempt was made to an unknown event * action definition */ - EventActionUnknownEventDefinitionError = 3, + EventActionUnknownEventActionDefinitionError = 4, /** * EventAction refers to the service, EventActionIDefinitionID refers to the variable - * In the Event Action Service, an access attempt was made to an unknown eventActionDefinitionID + * In the Event Action Service, an access attempt was made to an unknown eventDefinitionID */ - EventActionUnknownEventActionDefinitionIDError = 4, - SubServiceExecutionStartError = 5, - InstructionExecutionStartError = 6, + EventActionUnknownEventActionDefinitionIDError = 5, + SubServiceExecutionStartError = 6, + InstructionExecutionStartError = 7, /** * Attempt to change the value of a non existing parameter (ST[20]) */ - SetNonExistingParameter = 7, + SetNonExistingParameter = 8, /** * Attempt to access a non existing parameter (ST[20]) */ - GetNonExistingParameter = 8, + GetNonExistingParameter = 9, /** * Attempt to access a packet store that does not exist (ST[15]) */ - NonExistingPacketStore = 9, + NonExistingPacketStore = 10, /** * Attempt to change the start time tag of a packet store, whose open retrieval status is in progress (ST[15]) */ - SetPacketStoreWithOpenRetrievalInProgress = 10, + SetPacketStoreWithOpenRetrievalInProgress = 11, /** * Attempt to resume open retrieval of a packet store, whose by-time-range retrieval is enabled (ST[15]) */ - SetPacketStoreWithByTimeRangeRetrieval = 11, + SetPacketStoreWithByTimeRangeRetrieval = 12, /** * Attempt to access a packet with by-time range retrieval enabled (ST[15]) */ - GetPacketStoreWithByTimeRangeRetrieval = 12, + GetPacketStoreWithByTimeRangeRetrieval = 13, /** * Attempt to start the by-time-range retrieval of packet store, whose open retrieval is in progress (ST[15]) */ - GetPacketStoreWithOpenRetrievalInProgress = 13, + GetPacketStoreWithOpenRetrievalInProgress = 14, /** * Attempt to start by-time-range retrieval when its already enabled (ST[15]) */ - ByTimeRangeRetrievalAlreadyEnabled = 14, + ByTimeRangeRetrievalAlreadyEnabled = 15, /** * Attempt to create packet store, whose ID already exists (ST[15]) */ - AlreadyExistingPacketStore = 15, + AlreadyExistingPacketStore = 16, /** * Attempt to create packet store, when the max number of packet stores is already reached (ST[15]) */ - MaxNumberOfPacketStoresReached = 16, + MaxNumberOfPacketStoresReached = 17, /** * Attempt to access a packet store with the storage status enabled (ST[15]) */ - GetPacketStoreWithStorageStatusEnabled = 17, + GetPacketStoreWithStorageStatusEnabled = 18, /** * Attempt to delete a packet whose by time range retrieval status is enabled (ST[15]) */ - DeletionOfPacketWithByTimeRangeRetrieval = 18, + DeletionOfPacketWithByTimeRangeRetrieval = 19, /** * Attempt to delete a packet whose open retrieval status is in progress (ST[15]) */ - DeletionOfPacketWithOpenRetrievalInProgress = 19, + DeletionOfPacketWithOpenRetrievalInProgress = 20, /** * Requested a time window where the start time is larger than the end time (ST[15]) */ - InvalidTimeWindow = 20, + InvalidTimeWindow = 21, /** * Attempt to copy a packet store to a destination packet store that is not empty (ST[15]) */ - DestinationPacketStoreNotEmtpy = 21, + DestinationPacketStoreNotEmtpy = 22, /** * Attempt to set a reporting rate which is smaller than the parameter sampling rate. * ST[04] */ - InvalidReportingRateError = 22, + InvalidReportingRateError = 23, /** * Attempt to add definition to the struct map but its already full.(ST[19]) */ - EventActionDefinitionsMapIsFull = 23, + EventActionDefinitionsMapIsFull = 24, /** * Attempt to report/delete non existing housekeeping structure (ST[03]) */ - RequestedNonExistingStructure = 24, + RequestedNonExistingStructure = 25, /** * Attempt to create already created structure (ST[03]) */ - RequestedAlreadyExistingStructure = 25, + RequestedAlreadyExistingStructure = 26, /** * Attempt to delete structure which has the periodic reporting status enabled (ST[03]) as per 6.3.3.5.2(d-2) */ - RequestedDeletionOfEnabledHousekeeping = 26, + RequestedDeletionOfEnabledHousekeeping = 27, /** * Attempt to append a new parameter ID to a housekeeping structure, but the ID is already in the structure * (ST[03]) */ - AlreadyExistingParameter = 27, + AlreadyExistingParameter = 28, /** * Attempt to append a new parameter id to a housekeeping structure, but the periodic generation status is * enabled (ST[03]) */ - RequestedAppendToEnabledHousekeeping = 28, + RequestedAppendToEnabledHousekeeping = 29, /** * Attempt to create a new housekeeping structure in Housekeeping Service, when the maximum number of * housekeeping structures is already reached (ST[03]) */ - ExceededMaxNumberOfHousekeepingStructures = 29, + ExceededMaxNumberOfHousekeepingStructures = 30, /** * Attempt to add a new simply commutated parameter in a specific housekeeping structure, but the maximum * number of simply commutated parameters for this structure is already reached (ST[03]) */ - ExceededMaxNumberOfSimplyCommutatedParameters = 30, + ExceededMaxNumberOfSimplyCommutatedParameters = 31, /** * Attempt to set a reporting rate which is smaller than the parameter sampling rate. * ST[04] */ - InvalidSamplingRateError = 31, + InvalidSamplingRateError = 32, /** * Attempt to add new statistic definition but the maximum number is already reached (ST[04]) */ - MaxStatisticDefinitionsReached = 32, + MaxStatisticDefinitionsReached = 33, /** * Attempt to set the virtual channel of a packet store to a invalid value (ST[15]) */ - InvalidVirtualChannel = 33, + InvalidVirtualChannel = 34, /** * Attempt to delete a packet store, whose storage status is enabled (ST[15]) */ - DeletionOfPacketStoreWithStorageStatusEnabled = 34, + DeletionOfPacketStoreWithStorageStatusEnabled = 35, /** * Attempt to copy packets from a packet store to another, but either no packet timestamp falls inside the * specified timestamp, or more than one boolean argument were given as true in the 'copyPacketsTo' function * (ST[15]) */ - CopyOfPacketsFailed = 35, + CopyOfPacketsFailed = 36, /** * Attempt to set a packet store size to a value that the available memory cannot handle (ST[15]). */ - UnableToHandlePacketStoreSize = 36, + UnableToHandlePacketStoreSize = 37, /** * Attempt to delete all parameter monitoring definitions but the Parameter Monitoring Function Status is * enabled. */ - InvalidRequestToDeleteAllParameterMonitoringDefinitions = 37, + InvalidRequestToDeleteAllParameterMonitoringDefinitions = 38, /** * Attempt to delete one parameter monitoring definition but its Parameter Monitoring Status is * enabled. */ - InvalidRequestToDeleteParameterMonitoringDefinition = 38, + InvalidRequestToDeleteParameterMonitoringDefinition = 39, /** * Attempt to add a parameter that already exists to the Parameter Monitoring List. */ - AddAlreadyExistingParameter = 39, + AddAlreadyExistingParameter = 40, /** * Attempt to add a parameter in the Parameter Monitoring List but it's full */ - ParameterMonitoringListIsFull = 40, + ParameterMonitoringListIsFull = 41, /** * Attempt to add or modify a limit check parameter monitoring definition, but the high limit is lower than * the low limit. */ - HighLimitIsLowerThanLowLimit = 41, + HighLimitIsLowerThanLowLimit = 42, /** * Attempt to add or modify a delta check parameter monitoring definition, but the high threshold is lower than * the low threshold. */ - HighThresholdIsLowerThanLowThreshold = 42, + HighThresholdIsLowerThanLowThreshold = 43, /** * Attempt to modify a non existent Parameter Monitoring definition. */ - ModifyParameterNotInTheParameterMonitoringList = 43, + ModifyParameterNotInTheParameterMonitoringList = 44, /** * Attempt to modify a parameter monitoring definition, but the instruction refers to a monitored parameter * that is not the one used in that parameter monitoring definition. */ - DifferentParameterMonitoringDefinitionAndMonitoredParameter = 44, + DifferentParameterMonitoringDefinitionAndMonitoredParameter = 45, /** * Attempt to get a parameter monitoring definition that does not exist. */ - GetNonExistingParameterMonitoringDefinition = 45, + GetNonExistingParameterMonitoringDefinition = 46, /** * Request to report a non existent parameter monitoring definition. */ - ReportParameterNotInTheParameterMonitoringList = 46, + ReportParameterNotInTheParameterMonitoringList = 47, /** * Attempt to add a new report type, when the addition of all report types is already enabled in the * Application Process configuration (ST[14]) */ - AllReportTypesAlreadyAllowed = 47, - /** - * Attempt to add a new service type, when the addition of all service types is already enabled in the - * Application Process configuration (ST[14]) - */ AllServiceTypesAlreadyAllowed = 48, - /** * Attempt to add a new report type, when the max number of reports types allowed per service type * definition in the Application Process configuration is already reached (ST[14]) */ MaxReportTypesReached = 49, - /** * Attempt to add a new service type, when the max number of service types allowed per application process * definition in the Application Process configuration is already reached (ST[14]) */ MaxServiceTypesReached = 50, - /** * Attempt to add a report/event definition/housekeeping report type, when the specified application process * ID is not controlled by the Service (ST[14]) */ NotControlledApplication = 51, + /** + * Parameter is requested, but the provider of the parameter value does not exist yet + */ + ParameterValueMissing = 52, + /** + * Attempted to write to a read-only parameter + */ + ParameterReadOnly = 53, + /** + * Attempted to read from a write-only parameter + */ + ParameterWriteOnly = 54, /** * Attempt to access a non-existing report type definition, from the application process configuration (ST[14]) */ - NonExistingReportTypeDefinition = 52, + NonExistingReportTypeDefinition = 55, /** * Attempt to access a non-existing service type definition, from the application process configuration (ST[14]) */ - NonExistingServiceTypeDefinition = 53, + NonExistingServiceTypeDefinition = 56, /** * Attempt to access a non-existing application process definition, from the application process * configuration (ST[14]) */ - NonExistingApplication = 54, + NonExistingApplication = 57, }; /** @@ -499,7 +516,7 @@ public: * @return The corresponding ErrorSource */ template <typename ErrorType> - inline static ErrorSource findErrorSource(ErrorType error) { + inline static ErrorSource findErrorSource(ErrorType errorType) { // Static type checking ErrorSource source = Internal; diff --git a/inc/Helpers/AllMessageTypes.hpp b/inc/Helpers/AllMessageTypes.hpp index fb5c2c488647f0b54fa783dd09c72eff28801092..7e4ffd5aced08b9f539e4c4a9c188d30a4f42e34 100644 --- a/inc/Helpers/AllMessageTypes.hpp +++ b/inc/Helpers/AllMessageTypes.hpp @@ -13,7 +13,7 @@ namespace AllMessageTypes { * Map containing all the message types, per service. The key is the ServiceType and the value, * an etl vector containing the message types. */ - extern etl::map<uint8_t, etl::vector<uint8_t, ECSSMaxReportTypeDefinitions>, ECSSMaxServiceTypeDefinitions> messagesOfService; + extern const etl::map<uint8_t, etl::vector<uint8_t, ECSSMaxReportTypeDefinitions>, ECSSMaxServiceTypeDefinitions> MessagesOfService; } // namespace AllMessageTypes diff --git a/inc/Helpers/HousekeepingStructure.hpp b/inc/Helpers/HousekeepingStructure.hpp index 98271aa37526f9b4254d357b454e9b871ed96a64..342568379fbc3a9f5bf00a1d269cd6b3f742a0bd 100644 --- a/inc/Helpers/HousekeepingStructure.hpp +++ b/inc/Helpers/HousekeepingStructure.hpp @@ -12,8 +12,7 @@ * * @author Petridis Konstantinos <petridkon@gmail.com> */ -class HousekeepingStructure { -public: +struct HousekeepingStructure { uint8_t structureId; /** @@ -34,4 +33,4 @@ public: HousekeepingStructure() = default; }; -#endif \ No newline at end of file +#endif diff --git a/inc/Helpers/LazyParameter.hpp b/inc/Helpers/LazyParameter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d28c3baf3a85ce963bf54077739c2c6710b90c6b --- /dev/null +++ b/inc/Helpers/LazyParameter.hpp @@ -0,0 +1,105 @@ +#ifndef ECSS_SERVICES_LAZYPARAMETER_HPP +#define ECSS_SERVICES_LAZYPARAMETER_HPP + +#include <etl/optional.h> +#include <functional> +#include "Parameter.hpp" + +/** + * A Lazy Parameter is a ParameterService parameter that does not keep a value in + * memory, but calls an external function to fetch a new value whenever needed. + * + * The LazyParameter allows its users to call expensive value-fetching operators + * ONLY when a value is requested. This prevents having to update a value in + * memory every so often. + * + * This "lazy" fetching is useful when it is expensive (in terms of time, power + * etc.) to get updated values, e.g. from peripherals or difficult calculations. + * + * @warning This class is NOT re-entrant. The developer will have to make sure + * that only one thread has access to it at a time, otherwise undefined behaviour + * will occur. + * + * @tparam DataType The data type of the parameter's value + */ +template <typename DataType> +class LazyParameter : public ParameterBase { +public: + /** + * The type of the function that returns the current value of this parameter. + */ + using Getter = std::function<DataType()>; + + /** + * LazyParameter constructor without a getter function. + * + * When a getter function is not present, an error may be shown when fetching a value + * for the parameter. A @p fallback value will be returned in this case, if needed. + * @param fallback + */ + explicit LazyParameter(const DataType& fallback = 0) : fallback(fallback) {} + + /** + * LazyParameter constructor with a pre-defined getter function + * @param getter + * @param fallback + */ + explicit LazyParameter(const Getter& getter, const DataType& fallback = 0) : getter(getter), fallback(fallback) {} + + /** + * Set a getter function for this parameter. The getter function is called + * whenever a value for this parameter is requested. + */ + void setGetter(const Getter& _getter) { + LazyParameter::getter = _getter; + } + + /** + * Remove the getter function of this parameter. + */ + void unsetGetter() { + getter.reset(); + } + + /** + * Get the current value of this parameter, if the getter is defined. + * + * @note This function may take some time to return a value, since it calls + * the "expensive" getter function. + */ + etl::optional<DataType> getValue() { + if (getter) { + return (*getter)(); + } else { + return {}; + } + } + + inline double getValueAsDouble() override { + if constexpr (std::is_arithmetic_v<DataType>) { + return static_cast<double>(getValue().value_or(fallback)); + } else { + return 0; + } + } + + inline void appendValueToMessage(Message& message) override { + if (getter) { + message.append<DataType>((*getter)()); + } else { + message.append<DataType>(fallback); + ErrorHandler::reportError(message, ErrorHandler::ParameterValueMissing); + } + }; + + inline void setValueFromMessage(Message& message) override { + [[maybe_unused]] auto skippedBytes = message.read<DataType>(); + ErrorHandler::reportError(message, ErrorHandler::ParameterReadOnly); + }; +private: + etl::optional<Getter> getter; + DataType fallback; +}; + + +#endif //ECSS_SERVICES_LAZYPARAMETER_HPP diff --git a/inc/Helpers/NotifyParameter.hpp b/inc/Helpers/NotifyParameter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..286b1d24bfdfa372aaac15f2ca99630b4202e5af --- /dev/null +++ b/inc/Helpers/NotifyParameter.hpp @@ -0,0 +1,93 @@ +#ifndef ECSS_SERVICES_NOTIFYPARAMETER_HPP +#define ECSS_SERVICES_NOTIFYPARAMETER_HPP + +#include <etl/optional.h> +#include <functional> +#include "Parameter.hpp" + +/** + * A Notifying parameter will call a function whenever its value is written to. + * + * This is useful for updating the state of things when a parameter is changed, + * for example to disable/enable peripherals, to make configuration changes etc. + * + * @warning Calling NotifyParameter::setValue will *not* call the notifier + * function. You should use setValueLoudly for this purpose instead. + * + * @tparam DataType The data type of the parameter's value + */ +template <typename DataType> +class NotifyParameter : public Parameter<DataType> { +public: + using Notifier = std::function<void(const DataType&)>; + using Parent = Parameter<DataType>; + + /** + * Constructor without a notifier function. Nothing will then happen when the parameter is updated. + */ + explicit NotifyParameter(DataType initialValue) : Parent(initialValue) {} + + /** + * Constructor with a default notifier function. + */ + NotifyParameter(DataType initialValue, const Notifier& notifier) : Parent(initialValue), notifier(notifier) {} + + /** + * Same as Parameter::setValue(), but also calls the NotifyParameter::notifier function, if it + * exists. + */ + inline void setValueLoudly(DataType value) { + Parent::setValue(value); + + if (notifier) { + (*notifier)(Parent::currentValue); + } + } + + /** + * Call the notifier if it exists, without updating the value + */ + inline void notify() { + if (notifier) { + (*notifier)(Parent::currentValue); + } + } + + inline void setValueFromMessage(Message& message) override { + Parent::setValueFromMessage(message); + + if (notifier) { + (*notifier)(Parent::currentValue); + } + } + + /** + * Set the notifier function, to be called whenever the value of this parameter is updated. + * + * @note This function will be called even when a _parameter update_ command is received, but the + * new value is the same as the previous one. This is done so that there is an option to repair + * systems with a weird or unknown state. + * @param call Whether to also call the notifier function immediately, to ensure that a change is + * made. + */ + void setNotifier(const Notifier& _notifier, bool call=true) { + notifier = _notifier; + + if (call) { + _notifier(Parent::currentValue); + } + } + + /** + * Unset the notifier function, so that nothing is called when the value of this function is updated. + */ + void unsetNotifier() { + notifier.reset(); + } + +private: + etl::optional<Notifier> notifier; +}; + + +#endif //ECSS_SERVICES_NOTIFYPARAMETER_HPP diff --git a/inc/Helpers/PMONBase.hpp b/inc/Helpers/PMONBase.hpp index 66c00e58ff877d4b3c4f11b718ef6c83aeb7860e..0a56357ab63544a450c34f869f64de671386c3e2 100644 --- a/inc/Helpers/PMONBase.hpp +++ b/inc/Helpers/PMONBase.hpp @@ -1,13 +1,13 @@ #ifndef ECSS_SERVICES_PMONBASE_HPP #define ECSS_SERVICES_PMONBASE_HPP #include <cstdint> +#include "ECSS_Definitions.hpp" +#include "Helpers/Parameter.hpp" #include "Message.hpp" -#include "etl/array.h" #include "Service.hpp" -#include "Helpers/Parameter.hpp" -#include "etl/map.h" -#include "ECSS_Definitions.hpp" +#include "etl/array.h" #include "etl/list.h" +#include "etl/map.h" /** * Base class for Parameter Monitoring definitions. Contains the common variables of all check types. diff --git a/inc/Helpers/Parameter.hpp b/inc/Helpers/Parameter.hpp index ccc55ae9d8e24241afd8176f50ccf373a55130ec..9341affdc47b02b13852a445ebf06955d31fb6ce 100644 --- a/inc/Helpers/Parameter.hpp +++ b/inc/Helpers/Parameter.hpp @@ -32,8 +32,22 @@ */ class ParameterBase { public: + /** + * Given an ECSS message that contains this parameter as its first input, this loads the value from that parameter + */ virtual void appendValueToMessage(Message& message) = 0; + + /** + * Appends the parameter as an ECSS value to an ECSS Message + */ virtual void setValueFromMessage(Message& message) = 0; + + /** + * Converts the value of a parameter to a double. + * + * Some precision may be lost in the process. If the value is not arithmetic, + * then usually 0 is returned. + */ virtual double getValueAsDouble() = 0; }; @@ -44,7 +58,7 @@ public: */ template <typename DataType> class Parameter : public ParameterBase { -private: +protected: DataType currentValue; public: @@ -59,19 +73,17 @@ public: } inline double getValueAsDouble() override { - return static_cast<double>(currentValue); + if constexpr (std::is_arithmetic_v<DataType>) { + return static_cast<double>(currentValue); + } else { + return 0; + } } - /** - * Given an ECSS message that contains this parameter as its first input, this loads the value from that paremeter - */ inline void setValueFromMessage(Message& message) override { currentValue = message.read<DataType>(); }; - /** - * Appends the parameter as an ECSS value to an ECSS Message - */ inline void appendValueToMessage(Message& message) override { message.append<DataType>(currentValue); }; diff --git a/inc/Helpers/Statistic.hpp b/inc/Helpers/Statistic.hpp index 8934b9fcc434346d57dd535b625e6177fd44b5dd..cbf122db76a3e8f9e12c1435051f2ed378e7d5f4 100644 --- a/inc/Helpers/Statistic.hpp +++ b/inc/Helpers/Statistic.hpp @@ -2,11 +2,10 @@ #define ECSS_SERVICES_STATISTIC_HPP #include "ECSS_Definitions.hpp" -#include "Service.hpp" #include "ErrorHandler.hpp" +#include "Service.hpp" +#include "TimeGetter.hpp" #include "etl/vector.h" -#include <cmath> -#include <cfloat> /** * Class containing all the statistics for every parameter. Includes functions that calculate and append the @@ -16,8 +15,8 @@ class Statistic { public: uint16_t selfSamplingInterval = 0; uint16_t sampleCounter = 0; - uint32_t maxTime = 0; - uint32_t minTime = 0; // TODO: CUC Format timestamp + Time::DefaultCUC timeOfMaxValue; + Time::DefaultCUC timeOfMinValue; double max = -std::numeric_limits<double>::infinity(); double min = std::numeric_limits<double>::infinity(); double sumOfSquares = 0; @@ -40,7 +39,7 @@ public: * Appends itself to the received Message * message. */ - void appendStatisticsToMessage(Message& report); + void appendStatisticsToMessage(Message& report) const; /** * Setter function @@ -50,7 +49,7 @@ public: /** * Check if all the statistics are initialized */ - bool statisticsAreInitialized(); + bool statisticsAreInitialized() const; }; -#endif \ No newline at end of file +#endif diff --git a/inc/Helpers/TimeGetter.hpp b/inc/Helpers/TimeGetter.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4417d6735d040c4be9e40dec10b9043552231cd9 --- /dev/null +++ b/inc/Helpers/TimeGetter.hpp @@ -0,0 +1,34 @@ +#ifndef ECSS_SERVICES_TIMEGETTER_HPP +#define ECSS_SERVICES_TIMEGETTER_HPP + +#include <cstdint> +#include <ctime> +#include "Time/TimeStamp.hpp" +#include "Time/UTCTimestamp.hpp" + +/** + * @brief Get the current time + */ +class TimeGetter { +public: + /** + * Returns the current UTC time. + * @note + * The information needed to compute the UTC time is implementation-specific. This function should + * be reimplemented to work for every format of the time-related parameters. + */ + static UTCTimestamp getCurrentTimeUTC(); + + /** + * Converts the current UTC time, to a CUC formatted timestamp. + * @note + * The original format of the CUC (etl array of bits), is not used here, because it's easier to append + * a type uint32_t to a message object, rather than a whole array. Thus, we use the custom CUC format. + * + * @return CUC timestamp, formatted as elapsed ticks. + * @see Time + */ + static Time::DefaultCUC getCurrentTimeDefaultCUC(); +}; + +#endif // ECSS_SERVICES_TIMEGETTER_HPP diff --git a/inc/Message.hpp b/inc/Message.hpp index 9cb09f8281b1da17b104e74e1121f78e5073019a..f0b682b72ac88d037de60a3a8302de0edb523234 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -1,11 +1,12 @@ #ifndef ECSS_SERVICES_PACKET_H #define ECSS_SERVICES_PACKET_H -#include "ECSS_Definitions.hpp" +#include <Time/TimeStamp.hpp> #include <cstdint> #include <etl/String.hpp> #include <etl/wstring.h> -#include "ErrorHandler.hpp" +#include "ECSS_Definitions.hpp" +#include "Time/Time.hpp" #include "macros.hpp" /** @@ -72,7 +73,7 @@ public: enum PacketType { TM = 0, ///< Telemetry - TC = 1 ///< Telecommand + TC = 1 ///< Telecommand }; // The service and message IDs are 8 bits (5.3.1b, 5.3.3.1d) @@ -89,7 +90,7 @@ public: */ uint16_t applicationId; - // 7.4.3.1b + //> 7.4.3.1b uint16_t messageTypeCounter = 0; // 7.4.1, as defined in CCSDS 133.0-B-1 @@ -147,6 +148,24 @@ public: */ void appendWord(uint32_t value); + /** + * Appends any CUC timestamp to the message, including the header. + */ + template <class Ts> + void appendCUCTimeStamp(const Ts& timestamp) { + etl::array<uint8_t, Time::CUCTimestampMaximumSize> text = timestamp.formatAsCUC(); + + appendString(String<Time::CUCTimestampMaximumSize>(text.data(), text.size())); + } + + /** + * Appends a default timestamp object to the message, without the header + */ + void appendDefaultCUCTimeStamp(Time::DefaultCUC timestamp) { + static_assert(std::is_same_v<uint32_t, decltype(timestamp.formatAsBytes())>, "The default timestamp should be 4 bytes"); + appendUint32(timestamp.formatAsBytes()); + } + /** * Appends a number of bytes to the message * @@ -224,8 +243,8 @@ public: */ void readCString(char* string, uint16_t size); -public: Message(uint8_t serviceType, uint8_t messageType, PacketType packetType, uint16_t applicationId); + Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType); /** * Adds a single-byte boolean value to the end of the message @@ -339,6 +358,23 @@ public: return appendWord(reinterpret_cast<uint32_t&>(value)); } + + /** + * Adds a 8 byte signed integer to the end of the message + * + * PTC = 4, PFC = 16 + */ + void appendSint64(int64_t value) { + return appendUint64(reinterpret_cast<uint64_t&>(value)); + } + + /** + * Adds an 8 byte time Offset to the message + */ + void appendRelativeTime(Time::RelativeTime value) { + return appendSint64(value); + } + /** * Adds a 4-byte single-precision floating point number to the end of the message * @@ -506,6 +542,23 @@ public: return reinterpret_cast<int32_t&>(value); } + /** + * Fetches a 4-byte unsigned integer from the current position in the message + * + * PTC = 4, PFC = 14 + */ + int64_t readSint64() { + uint64_t value = readUint64(); + return reinterpret_cast<int64_t&>(value); + } + + /** + * Fetches an 8 byte time Offset from the current position in the message + */ + Time::RelativeTime readRelativeTime() { + return readSint64(); + }; + /** * Fetches an 4-byte single-precision floating point number from the current position in the * message @@ -521,13 +574,23 @@ public: return reinterpret_cast<float&>(value); } - float readDouble() { + double readDouble() { static_assert(sizeof(uint64_t) == sizeof(double), "Double numbers must be 64 bits long"); uint64_t value = readUint64(); return reinterpret_cast<double&>(value); } + /** + * Fetches a timestamp in a custom CUC format consisting of 4 bytes from the current position in the message + */ + Time::DefaultCUC readDefaultCUCTimeStamp() { + auto time = readUint32(); + std::chrono::duration<uint32_t, Time::DefaultCUC::Ratio> duration(time); + + return Time::DefaultCUC(duration); + } + /** * Fetches a N-byte string from the current position in the message * @@ -601,7 +664,7 @@ public: * * @return True if the message is of correct type, false if not */ - bool assertType(Message::PacketType expectedPacketType, uint8_t expectedServiceType, uint8_t expectedMessageType) { + bool assertType(Message::PacketType expectedPacketType, uint8_t expectedServiceType, uint8_t expectedMessageType) const { bool status = true; if ((packetType != expectedPacketType) || (serviceType != expectedServiceType) || @@ -617,7 +680,7 @@ public: * Alias for Message::assertType(Message::TC, \p expectedServiceType, \p * expectedMessageType) */ - bool assertTC(uint8_t expectedServiceType, uint8_t expectedMessageType) { + bool assertTC(uint8_t expectedServiceType, uint8_t expectedMessageType) const { return assertType(TC, expectedServiceType, expectedMessageType); } @@ -625,7 +688,7 @@ public: * Alias for Message::assertType(Message::TM, \p expectedServiceType, \p * expectedMessageType) */ - bool assertTM(uint8_t expectedServiceType, uint8_t expectedMessageType) { + bool assertTM(uint8_t expectedServiceType, uint8_t expectedMessageType) const { return assertType(TM, expectedServiceType, expectedMessageType); } }; @@ -676,6 +739,14 @@ template <> inline void Message::append(const double& value) { appendDouble(value); } +template <> +inline void Message::append(const Time::DefaultCUC& value) { + appendDefaultCUCTimeStamp(value); +} +template <> +inline void Message::append(const Time::RelativeTime& value) { + appendRelativeTime(value); +} /** * Appends an ETL string to the message. ETL strings are handled as ECSS octet strings, meaning that the string size @@ -686,7 +757,14 @@ template <> inline void Message::append(const etl::istring& value) { appendOctetString(value); } - +template <typename T> +inline void Message::append(const T& value) { + append(std::underlying_type_t<T>(value)); +} +template <typename T> +inline T Message::read() { + return static_cast<T>(read<std::underlying_type_t<T>>()); +} template <> inline uint8_t Message::read() { return readUint8(); @@ -721,17 +799,23 @@ template <> inline bool Message::read<bool>() { return readBoolean(); } -template <> -inline char Message::read() { - return readByte(); -} + template <> inline float Message::read() { return readFloat(); } + template <> inline double Message::read() { return readDouble(); } +template <> +inline Time::DefaultCUC Message::read() { + return readDefaultCUCTimeStamp(); +} +template <> +inline Time::RelativeTime Message::read() { + return readRelativeTime(); +} #endif // ECSS_SERVICES_PACKET_H diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp index 031a6fc505c992189f3169ceccb6877dac04ecb0..825548d9042c1dc42344cda2ca9d948f701c5471 100644 --- a/inc/MessageParser.hpp +++ b/inc/MessageParser.hpp @@ -75,7 +75,7 @@ public: * 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 */ - static String<CCSDSMaxMessageSize> composeECSS(const Message& message, uint16_t size = 0u); // Ignore-MISRA + static String<CCSDSMaxMessageSize> 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 diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp index bd4e0163632d1b4b4b2bae10a6f96c24c1ff3872..bfce212b456249d58e9b7614c7d1fd1effd486a6 100644 --- a/inc/Platform/x86/ECSS_Configuration.hpp +++ b/inc/Platform/x86/ECSS_Configuration.hpp @@ -10,7 +10,7 @@ */ /** - * @defgroup ServiceDefinitions Service compilation switches + * @name 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. * @@ -18,7 +18,8 @@ * @{ */ -#define SERVICE_ALL +#define SERVICE_ALL ///< Enables compilation of all the ECSS services +#define SERVICE_DUMMY ///< Compile ST[128] dummy service #define SERVICE_EVENTACTION ///< Compile ST[19] event-action #define SERVICE_EVENTREPORT ///< Compile ST[05] event reporting #define SERVICE_FUNCTION ///< Compile ST[08] function management @@ -28,11 +29,12 @@ #define SERVICE_ONBOARDMONITORING ///< Compile ST[12] on-board monitoring #define SERVICE_PARAMETER ///< Compile ST[20] parameter management #define SERVICE_PARAMETERSTATISTICS ///< Compile ST[04] parameter statistics -#define SERVICE_REALTIMEFORWARDINGCONTROL ///< Compile ST[14] real time forwarding control +#define SERVICE_REALTIMEFORWARDINGCONTROL ///< Compile ST[14] real time forwarding control #define SERVICE_REQUESTVERIFICATION ///< Compile ST[01] request verification #define SERVICE_STORAGEANDRETRIEVAL ///< Compile ST[15] storage-and-retrieval of tm packets #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 // ECSS_SERVICES_ECSS_CONFIGURATION_HPP diff --git a/inc/Platform/x86/Helpers/EnumMagic.hpp b/inc/Platform/x86/Helpers/EnumMagic.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6e5582406bb1e09b75e5121ff75ea3c8c6158217 --- /dev/null +++ b/inc/Platform/x86/Helpers/EnumMagic.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include <string> +#include <utility> + +#if !defined(__GNUC__) && !defined(__clang__) +#define __PRETTY_FUNCTION__ "E V = Sorry, feature not supported!" +#endif + +/** + * A simplified version of https://github.com/Neargye/magic_enum, this file uses some gcc compiler options to provide an @ref enumName function. + * This function converts an enum value to a string. It's used for debugging purposes only, to improve the readability of values that are hidden + * behind enums. + * + * Use with care. NOT intended for a microcontroller. + * + * @note This functionality is designed for simple, sequence-like enums. Their values should start from 0 and increase 1-by-1 until the end. It will probably + * result in slow compilation or incorrect results if used for other, "weirder" enums. + * + * This hack aims to be safe from errors by using `constexpr` lavishly. Enum values which are out of bounds (e.g. not defined or beyond + * EnumMagic_::MaximumValues) will not throw errors or cause unexpected events, but will probably display some kind of string. + * + * @license MIT, Daniil Goncharov + */ +namespace EnumMagic_ { + /** + * The highest enum value that can be handled. + * + * Values higher than this will just result in a number or malformed text, but _not_ a compilation or runtime error. + */ + constexpr auto MaximumValues = 256; + + /** + * Returns the identifier of an enum value. + * + * For example, if you define an enum: + * @code + * enum Something { + * First = 0, + * Second = 1 + * } + * @endcode + * then calling this function with `Something::First` will return the string `"Something::First"`. + * + * Internally, this function uses the [`__PRETTY_FUNCTION__`](https://gcc.gnu.org/onlinedocs/gcc/Function-Names.html) + * magic constant defined by GCC. The constant contains the template arguments of the function in a string, + * which themselves contain the identifier of the enum value. + * + * @note Tested with GCC version 11 only, other versions or compilers might return incorrect values + * + * @tparam E Enum type + * @tparam V Enum value (of type E) + * @return The identifier of the enum value + */ + template <typename E, E V> + constexpr std::basic_string_view<char> enumHack() { + static_assert(std::is_enum_v<E>, "enumHack() requires enum type."); + + std::string_view function = __PRETTY_FUNCTION__; + auto pos = function.find("E V = "); + std::string_view output = function.substr(pos + 6); + output.remove_suffix(1); + return output; + } + + /** + * Shortcut definition of enumHack for easier access + */ + template <typename Enum, Enum Value> + inline constexpr auto enumNameV = enumHack<Enum, Value>(); + + /** + * Given an enum type and a sequence of values, this function returns a list + * + * @tparam Indexes A list of potential enum values in numerical format + * @return An std::array of names for every enum value in the indexes + */ + template <typename Enum, std::size_t... Indexes> + constexpr auto names(std::index_sequence<Indexes...>) noexcept { + static_assert(std::is_enum_v<Enum>, "names() requires enum type."); + + return std::array<std::string_view, sizeof...(Indexes)>{{enumNameV<Enum, static_cast<Enum>(Indexes)>...}}; + } + + /** + * For the specified Enum enumeration, this variable will contain a list of the names of all its values + */ + template <typename Enum> + inline constexpr std::array namesV = names<Enum>(std::make_index_sequence<MaximumValues>{}); + + /** + * Given a nested name (e.g. `Services::Parameter::value`), this function returns the innermost name (e.g. `value`) + */ + constexpr std::string_view removeNamespace(std::string_view in) { + auto pos = in.find_last_of("::"); + return in.substr(pos + 1); + } +} // namespace EnumMagic_ + +/** + * Get the name of an enum value, as defined in the source code. + */ +template <typename Enum> +std::string enumName(Enum value) { + using namespace EnumMagic_; + + auto index = static_cast<size_t>(value); + + if (index >= namesV<Enum>.size()) { + return "(Unknown)"; + } + + return std::string(removeNamespace(namesV<Enum>[index])); +} diff --git a/inc/Platform/x86/TimeGetter.hpp b/inc/Platform/x86/TimeGetter.hpp deleted file mode 100644 index d5da817d62de9548c355a0e757c8ebc275e40614..0000000000000000000000000000000000000000 --- a/inc/Platform/x86/TimeGetter.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef ECSS_SERVICES_TIMEGETTER_HPP -#define ECSS_SERVICES_TIMEGETTER_HPP - -#include <cstdint> -#include <ctime> - -/** - * @brief Get the current time - */ -class TimeGetter { -public: - /** - * @brief Gets the current time in UNIX epoch - * @return Current UNIX epoch time, in elapsed seconds - */ - static inline uint32_t getSeconds() { - return static_cast<uint32_t>(time(nullptr)); - } -}; - -#endif // ECSS_SERVICES_TIMEGETTER_HPP diff --git a/inc/Service.hpp b/inc/Service.hpp index 53bf0bae49366814e9e8bff2a0d1436abbb877c8..763858973ef010e5a5f1e8dcd85177d15477e37e 100644 --- a/inc/Service.hpp +++ b/inc/Service.hpp @@ -4,8 +4,6 @@ #include <cstdint> #include "Message.hpp" -class ServicePool; - /** * @defgroup Services Services * ECSS Services implementations, as defined in ECSS-E-ST-70-41C. These services receive TC Messages, and output TM @@ -35,10 +33,9 @@ protected: * @param messageType The ID of the message type, as specified in the standard. For example, * 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 */ - Message createTM(uint8_t messageType) { - return Message(serviceType, messageType, Message::TM, 0); + Message createTM(uint8_t messageType) const { + return Message(serviceType, messageType, Message::TM); } /** diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp index 503d94cd55309a5f949b2b6f5a3706fbebd27b4a..23067caeec17d9fc3671c7bef920945dd3894106 100644 --- a/inc/ServicePool.hpp +++ b/inc/ServicePool.hpp @@ -2,6 +2,7 @@ #define ECSS_SERVICES_SERVICEPOOL_HPP #include "ECSS_Configuration.hpp" +#include "Services/DummyService.hpp" #include "Services/EventActionService.hpp" #include "Services/EventReportService.hpp" #include "Services/FunctionManagementService.hpp" @@ -30,7 +31,6 @@ class ServicePool { * 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, ECSSTotalMessageTypes> messageTypeCounter; /** @@ -39,6 +39,10 @@ class ServicePool { uint16_t packetSequenceCounter = 0; public: +#ifdef SERVICE_DUMMY + DummyService dummyService; +#endif + #ifdef SERVICE_EVENTACTION EventActionService eventAction; #endif @@ -95,6 +99,7 @@ public: TimeBasedSchedulingService timeBasedScheduling; #endif + /** * The default ServicePool constructor */ @@ -133,6 +138,6 @@ public: /** * A global variable that defines the basic pool where services can be fetched from */ -extern ServicePool Services; +extern ServicePool Services; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) #endif // ECSS_SERVICES_SERVICEPOOL_HPP diff --git a/inc/Services/DummyService.hpp b/inc/Services/DummyService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0463ff75aed7710f10248a9970c536d57fd77b83 --- /dev/null +++ b/inc/Services/DummyService.hpp @@ -0,0 +1,41 @@ +#ifndef ECSS_SERVICES_DUMMYSERVICE_HPP +#define ECSS_SERVICES_DUMMYSERVICE_HPP + +#include "Logger_Definitions.hpp" +#include "Service.hpp" + +/** + * This is a dummy Service used during testing. Its functionality is to contain LOG_ data but be sent through CAN bus. + * During the environmental tests, we will use both CAN and UART to send data from the MCU to the PC. However, the + * current CAN protocol does not accommodate random data strings. Therefore, we will use this dummy service to send such + * messages. + * + * Per the ECSS-E-ST-70-41C standard, p. 27-28, custom Services and Messages should start above 127. + */ +class DummyService : public Service { +private: + etl::string<1> termination = "\n"; + +public: + inline static const uint8_t ServiceType = 128; + enum MessageType : uint8_t { + LogString = 128, + }; + + DummyService() { + serviceType = ServiceType; + } + + /** + * Send data as a part of a custom ECSS Message + * Creates a TM[128, 128] + */ + void logAsECSSMessage(const etl::string<LOGGER_MAX_MESSAGE_SIZE>& data) { + Message log = createTM(MessageType::LogString); + log.appendString(data); + log.appendString(termination); + storeMessage(log); + } +}; + +#endif //ECSS_SERVICES_DUMMYSERVICE_HPP diff --git a/inc/Services/EventActionService.hpp b/inc/Services/EventActionService.hpp index 6ed438d8f07834d475c6ada312d2c3ec8d1555d8..1d4776d90df5fdab48f2d22ee54c4c6bfd2e1d9d 100644 --- a/inc/Services/EventActionService.hpp +++ b/inc/Services/EventActionService.hpp @@ -2,8 +2,8 @@ #define ECSS_SERVICES_EVENTACTIONSERVICE_HPP #include "Service.hpp" -#include "etl/String.hpp" #include "Services/EventReportService.hpp" +#include "etl/String.hpp" #include "etl/multimap.h" /** @@ -12,34 +12,19 @@ * 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 - * be executed when one event takes place. This defies the standard. + * * @note The application ID was decided to be abolished as an identifier of the event-action * definition * @attention Every event action definition ID should be different, regardless of the application ID - * - * @todo Since there are multiple actions per event and in delete/enable/disable functions are - * multiple instances are accessed, should I find a more efficient way to access them? - * @todo check if eventActionFunctionStatus should be private or not - * @todo check if eventAction map of definitions should be private or not */ class EventActionService : public Service { private: /** * Event-action function status */ - bool eventActionFunctionStatus; - - /** - * Custom function that is called right after an event takes place, to initiate - * the execution of the action - */ - void executeAction(uint16_t eventID); + bool eventActionFunctionStatus = false; public: - inline static const uint8_t ServiceType = 19; enum MessageType : uint8_t { @@ -55,30 +40,26 @@ public: }; struct EventActionDefinition { - // TODO: APID = 0 is the Ground Station APID. This should be changed - uint16_t applicationId = 0; - uint16_t eventDefinitionID = 65535; // The ID of the event that might take place - uint16_t eventActionDefinitionID = 0; // The ID of the event-action - String<64> request = ""; + uint16_t applicationID = 0; + inline static const uint16_t MaxDefinitionID = 65535; + uint16_t eventDefinitionID = MaxDefinitionID; + String<ECSSTCRequestStringSize> request = ""; bool enabled = false; + + EventActionDefinition(uint16_t applicationID, uint16_t eventDefinitionID, Message& message); }; friend EventReportService; etl::multimap<uint16_t, EventActionDefinition, ECSSEventActionStructMapSize> - eventActionDefinitionMap; + eventActionDefinitionMap; - EventActionService() { - serviceType = 19; - eventActionFunctionStatus = true; + EventActionService() : eventActionFunctionStatus(true) { + serviceType = ServiceType; } /** * TC[19,1] add event-action definitions - * - * Note: We have abolished multiple additions in one Telecommand packet. Only one - * event-action definition will be added per TC packet. That means there will be just an - * application ID, an event definition ID and the TC request. */ void addEventActionDefinitions(Message& message); @@ -118,7 +99,7 @@ public: void enableEventActionFunction(Message& message); /** - * TC[19,9] disable the event-actioni function + * TC[19,9] disable the event-action function */ void disableEventActionFunction(Message& message); @@ -133,10 +114,16 @@ public: * Getter for event-action function status * @return eventActionFunctionStatus */ - bool getEventActionFunctionStatus() { + bool getEventActionFunctionStatus() const { return eventActionFunctionStatus; } + /** + * Custom function that is called right after an event takes place, to initiate + * the execution of the action + */ + void executeAction(uint16_t eventDefinitionID); + /** * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet * is the ground station. diff --git a/inc/Services/EventReportService.hpp b/inc/Services/EventReportService.hpp index 839dff8bc3ca013acee789fbb9ad257b730fa18e..a5e13105cfd57d92a8ba97a2334389ba886b186f 100644 --- a/inc/Services/EventReportService.hpp +++ b/inc/Services/EventReportService.hpp @@ -1,8 +1,8 @@ #ifndef ECSS_SERVICES_EVENTREPORTSERVICE_HPP #define ECSS_SERVICES_EVENTREPORTSERVICE_HPP -#include "Service.hpp" #include <etl/bitset.h> +#include "Service.hpp" /** * Implementation of ST[05] event reporting service @@ -20,7 +20,6 @@ private: etl::bitset<numberOfEvents> stateOfEvents; public: - inline static const uint8_t ServiceType = 5; enum MessageType : uint8_t { @@ -35,34 +34,24 @@ public: }; // Variables that count the event reports per severity level - uint16_t lowSeverityReportCount; - uint16_t mediumSeverityReportCount; - uint16_t highSeverityReportCount; + uint16_t lowSeverityReportCount = 0; + uint16_t mediumSeverityReportCount = 0; + uint16_t highSeverityReportCount = 0; // Variables that count the event occurences per severity level - uint16_t lowSeverityEventCount; - uint16_t mediumSeverityEventCount; - uint16_t highSeverityEventCount; + uint16_t lowSeverityEventCount = 0; + uint16_t mediumSeverityEventCount = 0; + uint16_t highSeverityEventCount = 0; - uint16_t disabledEventsCount; + uint16_t disabledEventsCount = 0; - uint16_t lastLowSeverityReportID; - uint16_t lastMediumSeverityReportID; - uint16_t lastHighSeverityReportID; + uint16_t lastLowSeverityReportID = 65535; + uint16_t lastMediumSeverityReportID = 65535; + uint16_t lastHighSeverityReportID = 65535; EventReportService() { stateOfEvents.set(); - serviceType = 5; - lowSeverityReportCount = 0; - mediumSeverityReportCount = 0; - highSeverityReportCount = 0; - disabledEventsCount = 0; - lowSeverityEventCount = 0; - mediumSeverityEventCount = 0; - highSeverityEventCount = 0; - lastLowSeverityReportID = 65535; - lastMediumSeverityReportID = 65535; - lastHighSeverityReportID = 65535; + serviceType = ServiceType; } /** diff --git a/inc/Services/FunctionManagementService.hpp b/inc/Services/FunctionManagementService.hpp index 4dd0922560dc4d550045ade585dc3b5d4e2dd942..78c771049d511a8f9eee0e2a7f7368ef28fdd12a 100644 --- a/inc/Services/FunctionManagementService.hpp +++ b/inc/Services/FunctionManagementService.hpp @@ -45,7 +45,9 @@ 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. */ - FunctionManagementService() = default; + FunctionManagementService() { + serviceType = ServiceType; + } /** * Calls the function described in the TC[8,1] message *msg*, passing the arguments contained @@ -87,7 +89,7 @@ public: */ void include(String<ECSSFunctionNameLength> funcName, void (*ptr)(String<ECSSFunctionMaxArgLength>)); - int getMapSize() { + size_t getMapSize() { return funcPtrIndex.size(); } diff --git a/inc/Services/HousekeepingService.hpp b/inc/Services/HousekeepingService.hpp index 2336f094e8e15a0afee5f28927502541494fee86..8614b5529f153f757c68ab4c8029ff6b915f2743 100644 --- a/inc/Services/HousekeepingService.hpp +++ b/inc/Services/HousekeepingService.hpp @@ -1,11 +1,12 @@ #ifndef ECSS_SERVICES_HOUSEKEEPINGSERVICE_HPP #define ECSS_SERVICES_HOUSEKEEPINGSERVICE_HPP -#include "etl/map.h" +#include <optional> #include "ECSS_Definitions.hpp" -#include "Service.hpp" #include "ErrorHandler.hpp" #include "Helpers/HousekeepingStructure.hpp" +#include "Service.hpp" +#include "etl/map.h" /** * Implementation of the ST[03] Housekeeping Reporting Service. The job of the Housekeeping Service is to store @@ -31,11 +32,11 @@ private: static bool existsInVector(const etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters>& ids, uint16_t parameterId); - /** + /** * Initializes Housekeeping Structures with the Parameters found in the obc-software. * The function definition is also found in the obc-software repo. */ - void initializeHousekeepingStructures(); + void initializeHousekeepingStructures(); public: inline static const uint8_t ServiceType = 3; @@ -60,9 +61,152 @@ public: HousekeepingPeriodicPropertiesReport = 35, }; - HousekeepingService() { - initializeHousekeepingStructures(); - }; + HousekeepingService() { + serviceType = ServiceType; + initializeHousekeepingStructures(); + }; + + /** + * Returns the periodic generation action status of a Housekeeping structure. + * @param id Housekeeping structure ID + * @return boolean True if periodic generation of housekeeping reports is enabled, false otherwise + */ + inline bool getPeriodicGenerationActionStatus(uint8_t id) { + HousekeepingStructure newStructure{}; + if (hasNonExistingStructInternalError(id)) { + return newStructure.periodicGenerationActionStatus; + } + return housekeepingStructures.at(id).periodicGenerationActionStatus; + } + + /** + * Returns a reference to the structure at position of "id" in the map. + * @param id Housekeeping structure ID + * @return optional<std::reference_wrapper<HousekeepingStructure>> Reference to Housekeeping Structure + */ + inline std::optional<std::reference_wrapper<HousekeepingStructure>> getStruct(uint8_t id) { + if (hasNonExistingStructInternalError(id)) { + return {}; + } + return housekeepingStructures.at(id); + } + + /** + * Returns the collection interval (how often data is collected) of a Housekeeping structure. + * @param id Housekeeping structure ID + * @return uint32_t Integer multiples of the minimum sampling interval + */ + inline uint32_t getCollectionInterval(uint8_t id) { + HousekeepingStructure newStructure{}; + if (hasNonExistingStructInternalError(id)) { + return newStructure.collectionInterval; + } + return housekeepingStructures.at(id).collectionInterval; + } + + /** + * Sets the periodic generation action status of a Housekeeping structure. + * @param id Housekeeping structure ID + * @param status Periodic generation status of housekeeping reports + */ + inline void setPeriodicGenerationActionStatus(uint8_t id, bool status) { + if (hasNonExistingStructInternalError(id)) { + return; + } + housekeepingStructures.at(id).periodicGenerationActionStatus = status; + } + + /** + * Sets the collection interval of a Housekeeping structure. + * @param id Housekeeping structure ID + * @param interval Integer multiples of the minimum sampling interval + */ + inline void setCollectionInterval(uint8_t id, uint32_t interval) { + if (hasNonExistingStructInternalError(id)) { + return; + } + housekeepingStructures.at(id).collectionInterval = interval; + } + + /** + * Checks if the structure exists in the map. + * @param id Housekeeping structure ID + * @return boolean True if the structure exists, false otherwise + */ + inline bool structExists(uint8_t id) { + return (housekeepingStructures.find(id) != housekeepingStructures.end()); + } + + /** + * Checks if the structure doesn't exist in the map and then accordingly reports execution start error. + * @param id Housekeeping structure ID + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if the structure doesn't exist, false otherwise + */ + bool hasNonExistingStructExecutionError(uint8_t id, Message& request); + + /** + * Checks if the structure doesn't exist in the map and then accordingly reports error. + * @param id Housekeeping structure ID + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if the structure doesn't exist, false otherwise + */ + bool hasNonExistingStructError(uint8_t id, Message& request); + + /** + * Checks if the structure doesn't exist in the map and then accordingly reports internal error. + * @param id Housekeeping structure ID + * @return boolean True if the structure doesn't exist, false otherwise + */ + bool hasNonExistingStructInternalError(uint8_t id); + + /** + * Checks if the parameter exists in the vector and if it does it reports an error. + * @param id Parameter ID + * @param housekeepingStruct Housekkeping Structure + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if the parameter exists, false otherwise + */ + static bool hasAlreadyExistingParameterError(HousekeepingStructure& housekeepingStruct, uint8_t id, Message& request); + + /** + * Checks if the struct requested exists and if it exists reports execution error. + * @param id Housekeeping structure ID + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if the structure exists, false otherwise + */ + bool hasAlreadyExistingStructError(uint8_t id, Message& request); + + /** + * Reports execution error if the max number of housekeeping structures is exceeded. + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if max number of housekeeping structures is exceeded, false otherwise + */ + bool hasExceededMaxNumOfHousekeepingStructsError(Message& request); + + /** + * Reports execution error if it's attempted to append a new parameter id to a housekeeping structure, but the periodic generation status is enabled. + * @param housekeepingStruct Housekkeping Structure + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if periodic generation status is enabled, false otherwise + */ + static bool hasRequestedAppendToEnabledHousekeepingError(HousekeepingStructure& housekeepingStruct, Message& request); + + /** + * Reports execution error if it's attempted to delete structure which has the periodic reporting status enabled. + * @param id Housekeeping structure ID + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if periodic reporting status is enabled, false otherwise + */ + bool hasRequestedDeletionOfEnabledHousekeepingError(uint8_t id, Message& request); + + /** + * Reports execution error if the max number of simply commutated parameters is exceeded. + * @param housekeepingStruct Housekkeping Structure + * @param request Telemetry (TM) or telecommand (TC) message + * @return boolean True if max number of simply commutated parameters is exceeded, false otherwise + */ + static bool hasExceededMaxNumOfSimplyCommutatedParamsError(HousekeepingStructure& housekeepingStruct, Message& request); /** * Implementation of TC[3,1]. Request to create a housekeeping parameters report structure. diff --git a/inc/Services/LargePacketTransferService.hpp b/inc/Services/LargePacketTransferService.hpp index a3ef0a2770c0a745d80d31283f0ecb925bd10b98..154f7c6dfde4e41f421b540c3866be5b28ad2853 100644 --- a/inc/Services/LargePacketTransferService.hpp +++ b/inc/Services/LargePacketTransferService.hpp @@ -1,8 +1,8 @@ #ifndef ECSS_SERVICES_LARGEPACKETTRANSFERSERVICE_HPP #define ECSS_SERVICES_LARGEPACKETTRANSFERSERVICE_HPP -#include "Service.hpp" #include <etl/String.hpp> +#include "Service.hpp" /** * Implementation of the ST[13] large packet transfer service @@ -16,7 +16,6 @@ class LargePacketTransferService : public Service { public: - inline static const uint8_t ServiceType = 13; @@ -30,11 +29,11 @@ public: * Default constructor since only functions will be used. */ LargePacketTransferService() { - serviceType = 13; + serviceType = ServiceType; } /** - * Function that handles the first part of the download report + * TM[13,1] Function that handles the first part of the download report * @param largeMessageTransactionIdentifier The identifier of the large packet * @param partSequenceNumber The identifier of the part of the large packet * @param string The data contained in this part of the large packet @@ -43,7 +42,7 @@ public: const String<ECSSMaxFixedOctetStringSize>& string); /** - * Function that handles the n-2 parts of tbe n-part download report + * TM[13,2] Function that handles the n-2 parts of tbe n-part download report * @param largeMessageTransactionIdentifier The identifier of the large packet * @param partSequenceNumber The identifier of the part of the large packet * @param string The data contained in this part of the large packet @@ -52,7 +51,7 @@ public: const String<ECSSMaxFixedOctetStringSize>& string); /** - * Function that handles the last part of the download report + * TM[13,3] Function that handles the last part of the download report * @param largeMessageTransactionIdentifier The identifier of the large packet * @param partSequenceNumber The identifier of the part of the large packet * @param string The data contained in this part of the large packet @@ -64,23 +63,23 @@ public: // 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 + * TC[13,9] Function that handles the first part of the uplink request * @param string This will change when these function will be modified */ - String<ECSSMaxFixedOctetStringSize> firstUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string); + static String<ECSSMaxFixedOctetStringSize> firstUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string); /** - * Function that handles the n-2 parts of tbe n-part uplink request + * TC[13,10] Function that handles the n-2 parts of the n-part uplink request * @param string This will change when these function will be modified */ - String<ECSSMaxFixedOctetStringSize> + static String<ECSSMaxFixedOctetStringSize> intermediateUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string); /** - * Function that handles the last part of the uplink request + * TC[13,11] Function that handles the last part of the uplink request * @param string This will change when these function will be modified */ - String<ECSSMaxFixedOctetStringSize> lastUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string); + static String<ECSSMaxFixedOctetStringSize> lastUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string); /** * Function that splits large messages diff --git a/inc/Services/MemoryManagementService.hpp b/inc/Services/MemoryManagementService.hpp index 6439bfa64f844603916c2d148b5d46899b7628c4..1c208342843cffea3c0c294e338a5df302977aea 100644 --- a/inc/Services/MemoryManagementService.hpp +++ b/inc/Services/MemoryManagementService.hpp @@ -2,17 +2,16 @@ #define ECSS_SERVICES_MEMMANGSERVICE_HPP #include <memory> -#include "Service.hpp" -#include "Helpers/CRCHelper.hpp" #include "ErrorHandler.hpp" +#include "Helpers/CRCHelper.hpp" #include "Platform/STM32F7/MemoryAddressLimits.hpp" +#include "Service.hpp" /** * @ingroup Services */ class MemoryManagementService : public Service { public: - inline static const uint8_t ServiceType = 6; enum MessageType : uint8_t { @@ -30,7 +29,7 @@ public: RAM_D2, RAM_D3, ITCMRAM, - FLASH, + FLASH_MEMORY, EXTERNAL, }; @@ -50,16 +49,6 @@ public: public: explicit RawDataMemoryManagement(MemoryManagementService& parent); - /** - * TC[6,2] load raw values to memory - * - * @details This function loads new values to memory data areas - * specified in the request - * @param request Provide the received message as a parameter - * @todo Only allow aligned memory address to be start addresses - */ - void loadRawData(Message& request); - /** * TC[6,5] read raw memory values * @@ -82,9 +71,19 @@ public: * different memory types * @todo Only allow aligned memory address to be start addresses */ - void checkRawData(Message &request); + void checkRawData(Message& request); } rawDataMemorySubservice; + /** + * TC[6,2] load raw values to memory + * + * @details This function loads new values to memory data areas + * specified in the request + * @param request Provide the received message as a parameter + * @todo Only allow aligned memory address to be start addresses + */ + static void loadRawData(Message& request); + /** * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet * is the ground station. @@ -101,20 +100,20 @@ private: * @param memId The ID of the memory to check is passed * @param address Takes the address to be checked for validity */ - bool addressValidator(MemoryManagementService::MemoryID memId, uint64_t address); + static bool addressValidator(MemoryManagementService::MemoryID memId, uint64_t address); /** * Check if the provided memory ID is valid * * @param memId The memory ID for validation */ - bool memoryIdValidator(MemoryManagementService::MemoryID memId); + static bool memoryIdValidator(MemoryManagementService::MemoryID memId); /** * Validate the data according to checksum calculation * */ - bool dataValidator(const uint8_t* data, uint16_t checksum, uint16_t length); + static bool dataValidator(const uint8_t* data, uint16_t checksum, uint16_t length); }; #endif // ECSS_SERVICES_MEMMANGSERVICE_HPP diff --git a/inc/Services/OnBoardMonitoringService.hpp b/inc/Services/OnBoardMonitoringService.hpp index 1ef954f2f6f4356436047b1909bc75dcd95d45b4..d67bf35176f980da78bcde8ca4c1c59f0f734f08 100644 --- a/inc/Services/OnBoardMonitoringService.hpp +++ b/inc/Services/OnBoardMonitoringService.hpp @@ -40,6 +40,11 @@ public: ReportStatusOfParameterMonitoringDefinition = 13, ParameterMonitoringDefinitionStatusReport = 14 }; + + OnBoardMonitoringService() { + serviceType = ServiceType; + } + /** * The maximum time between two transition reports. * Measured in "on-board parameter minimum sampling interval" units (see 5.4.3.2c in ECSS-E-ST-70-41C). @@ -93,4 +98,4 @@ public: void execute(Message& message); }; -#endif // ECSS_SERVICES_ONBOARDMONITORINGSERVICE_HPP +#endif // ECSS_SERVICES_ONBOARDMONITORINGSERVICE_HPP \ No newline at end of file diff --git a/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp index 437bd395ad06cffe0a97ef1d2959d4f2c531ee2f..8718015057d96e4332666f41d9ba195906835c85 100644 --- a/inc/Services/ParameterService.hpp +++ b/inc/Services/ParameterService.hpp @@ -3,9 +3,9 @@ #include <optional> #include "ECSS_Definitions.hpp" -#include "Service.hpp" #include "ErrorHandler.hpp" #include "Helpers/Parameter.hpp" +#include "Service.hpp" #include "etl/map.h" /** @@ -57,6 +57,7 @@ public: * by calling \fn initializeParametersArray */ ParameterService() { + serviceType = ServiceType; initializeParameterMap(); } @@ -104,7 +105,7 @@ public: * * @param newParamValues: a valid TC[20, 3] message carrying parameter ID and replacement value */ - void setParameters(Message& newParamValues); + void setParameters(Message& newParamValues) const; /** * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet diff --git a/inc/Services/ParameterStatisticsService.hpp b/inc/Services/ParameterStatisticsService.hpp index b6f98580fca55357a6968d90fb7c4e9342e05db4..d04a12afa22932ba01b887b182bfbc7cd3fc36a5 100644 --- a/inc/Services/ParameterStatisticsService.hpp +++ b/inc/Services/ParameterStatisticsService.hpp @@ -2,9 +2,10 @@ #define ECSS_SERVICES_PARAMETERSTATISTICSSERVICE_HPP #include "ECSS_Definitions.hpp" -#include "Service.hpp" #include "ErrorHandler.hpp" #include "Helpers/Statistic.hpp" +#include "Helpers/TimeGetter.hpp" +#include "Service.hpp" #include "etl/deque.h" #include "etl/map.h" @@ -15,6 +16,28 @@ * @author Konstantinos Petridis <petridkon@gmail.com> */ class ParameterStatisticsService : public Service { +private: + /** + * The time at which the evaluation of statistics is initialized. It is basically the time when the statistics + * are reset. + */ + Time::DefaultCUC evaluationStartTime; + + /** + * true means that the periodic statistics reporting is enabled + */ + bool periodicStatisticsReportingStatus = true; + + /** + * The parameter statistics reporting interval + */ + uint16_t reportingIntervalMs = 700; + + /** + * Initializer of the statistics map, so that its content can be accessed by FreeRTOS tasks. + */ + void initializeStatisticsMap(); + public: inline static const uint8_t ServiceType = 4; @@ -30,15 +53,13 @@ public: ParameterStatisticsDefinitionsReport = 9, }; + ParameterStatisticsService(); + /** * Map containing parameters' IDs followed by the statistics that correspond to the specified parameter */ etl::map<uint16_t, Statistic, ECSSMaxStatisticParameters> statisticsMap; - /** - * true means that the periodic statistics reporting is enabled - */ - bool periodicStatisticsReportingStatus = false; /** * If true, after every report reset the parameter statistics. */ @@ -47,16 +68,43 @@ public: * Indicates whether to append/read the sampling interval to/from message */ const bool supportsSamplingInterval = true; + /** - * The parameter statistics reporting interval + * Returns the periodic statistics reporting status + */ + inline bool getPeriodicReportingStatus() const { + return periodicStatisticsReportingStatus; + } + + /** + * Sets the value of the periodic statistics reporting status + */ + inline void setPeriodicReportingStatus(bool status) { + periodicStatisticsReportingStatus = status; + } + + /** + * Returns the periodic statistics reporting status */ - uint16_t reportingInterval = 5; // TODO: Must define units. Same as parameter sampling rates + inline uint16_t getReportingIntervalMs() const { + return reportingIntervalMs; + } /** * TC[4,1] report the parameter statistics, by calling parameterStatisticsReport() */ void reportParameterStatistics(Message& request); + /** + * Report the parameter statistics, by calling parameterStatisticsReport() + * This is **NOT** the function called by TC. It was created so that this function could be called + * from within a Platform (MCU, x86...) without needing to create a fake TC and pass through multiple functions. + * + * @param reset indicates whether each Statistic should be reset. Simulates the argument contained in the TC[4,1] + * that calls reportParameterStatistics(Message& request) + */ + void reportParameterStatistics(bool reset); + /** * Constructs and stores a TM[4,2] packet containing the parameter statistics report. */ diff --git a/inc/Services/RealTimeForwardingControlService.hpp b/inc/Services/RealTimeForwardingControlService.hpp index 580c5e61776b83aaf2766a40382a47d341c04e76..a43642d948f9b1a12ebdfed7e1d070f57eb95727 100644 --- a/inc/Services/RealTimeForwardingControlService.hpp +++ b/inc/Services/RealTimeForwardingControlService.hpp @@ -17,7 +17,7 @@ * * @author Konstantinos Petridis <petridkon@gmail.com> */ -class RealTimeForwardingControlService { +class RealTimeForwardingControlService : Service { public: inline static const uint8_t ServiceType = 14; @@ -27,7 +27,9 @@ public: EventReportConfigurationContentReport = 16, }; - RealTimeForwardingControlService() = default; + RealTimeForwardingControlService() { + serviceType = ServiceType; + } /** * Contains the Application IDs, controlled by the Service. diff --git a/inc/Services/RequestVerificationService.hpp b/inc/Services/RequestVerificationService.hpp index c6ccd8720f2a8de05349959fe3caaffe097e3ffd..08c00834cc68bbc5897a57474d5cce7976653263 100644 --- a/inc/Services/RequestVerificationService.hpp +++ b/inc/Services/RequestVerificationService.hpp @@ -1,10 +1,10 @@ #ifndef ECSS_SERVICES_REQUESTVERIFICATIONSERVICE_HPP #define ECSS_SERVICES_REQUESTVERIFICATIONSERVICE_HPP -#include "Service.hpp" -#include "Message.hpp" -#include "ErrorHandler.hpp" #include "ECSS_Definitions.hpp" +#include "ErrorHandler.hpp" +#include "Message.hpp" +#include "Service.hpp" /** * Implementation of the ST[01] request verification service @@ -20,7 +20,6 @@ */ class RequestVerificationService : public Service { public: - inline static const uint8_t ServiceType = 1; enum MessageType : uint8_t { @@ -36,7 +35,7 @@ public: }; RequestVerificationService() { - serviceType = 1; + serviceType = ServiceType; } /** @@ -133,7 +132,7 @@ public: * telecommand packet that failed the routing * @param errorCode The cause of creating this type of report */ - void failRoutingVerification(const Message &request, ErrorHandler::RoutingErrorType errorCode); + void failRoutingVerification(const Message& request, ErrorHandler::RoutingErrorType errorCode); }; #endif // ECSS_SERVICES_REQUESTVERIFICATIONSERVICE_HPP diff --git a/inc/Services/StorageAndRetrievalService.hpp b/inc/Services/StorageAndRetrievalService.hpp index 95142327f8d73d2e272de7cd5d192dec4252d33d..0387c8ff87c76fd9b42a00b215915144809ea9fe 100644 --- a/inc/Services/StorageAndRetrievalService.hpp +++ b/inc/Services/StorageAndRetrievalService.hpp @@ -206,7 +206,9 @@ public: ChangeVirtualChannel = 28 }; - StorageAndRetrievalService() = default; + StorageAndRetrievalService() { + serviceType = ServiceType; + } /** * Adds new packet store into packet stores. diff --git a/inc/Services/TestService.hpp b/inc/Services/TestService.hpp index 82156b23e558583afad4183acd92bdfba9f354b8..63e3fbc258f4f4c51b95b4775ef84aceb5bfb1dc 100644 --- a/inc/Services/TestService.hpp +++ b/inc/Services/TestService.hpp @@ -10,8 +10,6 @@ */ class TestService : public Service { public: - - inline static const uint8_t ServiceType = 17; enum MessageType : uint8_t { @@ -22,7 +20,7 @@ public: }; TestService() { - serviceType = 17; + serviceType = ServiceType; } /** @@ -30,19 +28,27 @@ public: */ void areYouAlive(Message& request); + /** + * TM[17,2] are-you-alive connection test report to show that the MCU is alive and well + */ + void areYouAliveReport(); + /** * TC[17,3] perform an on-board connection test * - * @todo Only respond if we have the correct APID */ void onBoardConnection(Message& request); + /** + * TM[17,4] on-board connection test report to show that the MCU is connected to the on-board + */ + void onBoardConnectionReport(uint16_t applicationProcessId); + /** * It is responsible to call the suitable function that execute the proper subservice. The * way that the subservices are selected is for the time being based on the messageType(class * member of class Message) of the param message * - * @todo Error handling for the switch() in the implementation of this execute function */ void execute(Message& message); }; diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp index f49d8067a02a738a521c71ffcbee1d7b4718fc19..c0d5c3b62d33faf8cca15101a2ee88bcdd12efd0 100644 --- a/inc/Services/TimeBasedSchedulingService.hpp +++ b/inc/Services/TimeBasedSchedulingService.hpp @@ -1,14 +1,21 @@ #ifndef ECSS_SERVICES_TIMEBASEDSCHEDULINGSERVICE_HPP #define ECSS_SERVICES_TIMEBASEDSCHEDULINGSERVICE_HPP -#include "etl/list.h" -#include "Service.hpp" #include "ErrorHandler.hpp" -#include "MessageParser.hpp" #include "Helpers/CRCHelper.hpp" +#include "MessageParser.hpp" +#include "Service.hpp" +#include "etl/list.h" // Include platform specific files -#include "Platform/x86/TimeGetter.hpp" +#include "Helpers/TimeGetter.hpp" + + +/** + * @def GROUPS_ENABLED + * @brief Indicates whether scheduling groups are enabled + */ +#define GROUPS_ENABLED 0 // NOLINT(cppcoreguidelines-macro-usage) /** * @def SUB_SCHEDULES_ENABLED @@ -16,21 +23,15 @@ * * @details Sub-schedules are currently not implemented so this has no effect */ -/** - * @def GROUPS_ENABLED - * @brief Indicates whether scheduling groups are enabled - */ -#define GROUPS_ENABLED 0 -#define SUB_SCHEDULES_ENABLED 0 +#define SUB_SCHEDULES_ENABLED 0 // NOLINT(cppcoreguidelines-macro-usage) /** * @brief Namespace to access private members during test * * @details Define a namespace for the access of the private members to avoid conflicts */ -namespace unit_test -{ -struct Tester; +namespace unit_test { + struct Tester; } // namespace unit_test /** @@ -47,11 +48,11 @@ class TimeBasedSchedulingService : public Service { private: /** * @brief Indicator of the schedule execution - * + * True indicates "enabled" and False "disabled" state * @details The schedule execution indicator will be updated by the process that is running * the time scheduling service. */ - bool executionFunctionStatus = false; // True indicates "enabled" and False "disabled" state + bool executionFunctionStatus = false; /** * @brief Request identifier of the received packet @@ -62,7 +63,7 @@ private: struct RequestID { uint16_t applicationID = 0; ///< Application process ID uint16_t sequenceCount = 0; ///< Packet sequence count - uint8_t sourceID = 0; ///< Packet source ID + uint8_t sourceID = 0; ///< Packet source ID bool operator!=(const RequestID& rightSide) const { return (sequenceCount != rightSide.sequenceCount) or (applicationID != rightSide.applicationID) or @@ -80,9 +81,9 @@ private: * @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 + Message request; ///< Hold the received command request + RequestID requestID; ///< Request ID, characteristic of the definition + Time::DefaultCUC requestReleaseTime{0}; ///< Keep the command release time }; /** @@ -99,7 +100,7 @@ private: * @details The ECSS standard requires that the activities are sorted in the TM message * response. Also it is better to have the activities sorted. */ - inline void + inline static void sortActivitiesReleaseTime(etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& schedActivities) { schedActivities.sort([](ScheduledActivity const& leftSide, ScheduledActivity const& rightSide) { // cppcheck-suppress @@ -116,12 +117,12 @@ private: */ friend struct ::unit_test::Tester; -public: - - /* -* ST[11] TimeBased Scheduling Service and Sub-Service Macros, for readability purpose -*/ + /** + * Notifies the timeBasedSchedulingTask after the insertion of activities to scheduleActivity list. + */ + void notifyNewActivityAddition(); +public: inline static const uint8_t ServiceType = 11; enum MessageType : uint8_t { @@ -145,6 +146,12 @@ public: */ TimeBasedSchedulingService(); + /** + * This function executes the next activity and removes it from the list. + * @return the requestReleaseTime of next activity to be executed after this time + */ + Time::DefaultCUC executeScheduledActivity(Time::DefaultCUC currentTime); + /** * @brief TC[11,1] enable the time-based schedule execution function * @@ -210,6 +217,15 @@ public: */ void detailReportAllActivities(Message& request); + /** + * @brief TM[11,10] time-based schedule detail report + * + * @details Send a detailed report about the status of the activities listed + * on the provided list. Generates a TM[11,10] response. + * @param listOfActivities Provide the list of activities that need to be reported on + */ + void timeBasedScheduleDetailReport(const etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& listOfActivities); + /** * @brief TC[11,9] detail-report activities identified by request identifier * @@ -237,6 +253,15 @@ public: */ void summaryReportActivitiesByID(Message& request); + /** + * @brief TM[11,13] time-based schedule summary report + * + * @details Send a summary report about the status of the activities listed + * on the provided list. Generates a TM[11,13] response. + * @param listOfActivities Provide the list of activities that need to be reported on + */ + void timeBasedScheduleSummaryReport(const etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& listOfActivities); + /** * @brief TC[11,5] delete time-based scheduled activities identified by a request identifier * @@ -260,7 +285,7 @@ public: * Also if an activity with a specified request identifier is not found, generate a failed * start of execution for that specific instruction. */ - void timeShiftActivitiesByID(Message &request); + void timeShiftActivitiesByID(Message& request); /** * It is responsible to call the suitable function that executes a telecommand packet. The source of that packet diff --git a/inc/Time/Time.hpp b/inc/Time/Time.hpp index 55dbaa16ec64dfac8a811d7b05ea719296c1e7b4..1b4fb89dc15b8d2665b91485c9a756e96f9ecaf2 100644 --- a/inc/Time/Time.hpp +++ b/inc/Time/Time.hpp @@ -1,8 +1,11 @@ -#pragma once +#ifndef ECSS_TIMEHPP +#define ECSS_TIMEHPP + +#include <chrono> #include <cstdint> -#include <Message.hpp> -#include "macros.hpp" +#include "ErrorHandler.hpp" #include "etl/String.hpp" +#include "macros.hpp" /** * @defgroup Time Time @@ -68,201 +71,222 @@ * @ingroup Time * @author Baptiste Fournier */ -namespace Time -{ -inline constexpr uint8_t SecondsPerMinute = 60; -inline constexpr uint16_t SecondsPerHour = 3600; -inline constexpr uint32_t SecondsPerDay = 86400; -static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -/** - * Number of bytes used for the basic time units of the CUC header for this mission - */ -inline constexpr uint8_t CUCSecondsBytes = 2; - -/** - * Number of bytes used for the fractional time units of the CUC header for this mission - */ -inline constexpr uint8_t CUCFractionalBytes = 2; - -/** - * The system epoch (clock measurement starting time) - * All timestamps emitted by the ECSS services will show the elapsed time (seconds, days etc.) from this epoch. - */ -inline constexpr struct { - uint16_t year; - uint8_t month; - uint8_t day; -} Epoch{ - 2020, - 1, - 1, -}; - -/** - * Number of seconds elapsed between the UNIX epoch (1 January 1970) and the system epoch. - * - * The system epoch is defined by @ref Epoch. - * This constant is used for conversion between Unix and other timestamps. - * Leap seconds are not taken into account here. - * - * @warning This value MUST be updated after every change of the system @ref Epoch. You can use utilities such as - * https://www.unixtimestamp.com/ to obtain a correct result. - */ -inline constexpr uint32_t EpochSecondsFromUnix = 1577836800; - -/** - * The maximum theoretical size in bytes of a CUC timestamp, including headers (P-field and T-field) - */ -inline constexpr uint8_t CUCTimestampMaximumSize = 9; - -static_assert(Epoch.year >= 2019); -static_assert(Epoch.month < 11 && Epoch.month >= 0); -static_assert(Epoch.day < DaysOfMonth[Epoch.month]); - -/** - * Builds the short P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. - * - * The short P-field contains only one byte. It is used when many octets are used to represent the basic or fractional - * time units. - * - * @see CCSDS 301.0-B-4, Section 3.2.2 - * @tparam secondsBytes The number of octets used to represent the basic time units - * @tparam fractionalBytes The number of octets used to represent the fractional time units - * @return A single byte, representing the P-field contents - */ -template <int secondsBytes, int fractionalBytes> -inline constexpr uint8_t buildShortCUCHeader() { - static_assert(secondsBytes <= 4, "Use buildLongCUCHeader instead"); - static_assert(fractionalBytes <= 3, "Use buildLongCUCHeader instead"); - - uint8_t header = 0; - - // P-Field extension is 0, CUC header is not extended - header += 0; - - // We are using a custom TAI epoch ("agency-defined epoch") - header <<= 3U; - header += 0b010; - - // Number of bytes in the basic time unit - header <<= 2U; - header += secondsBytes - 1; - - // Number of bytes in the fractional unit - header <<= 2U; - header += fractionalBytes; - - return header; -} - -/** - * Builds the long P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. - * - * The long P-field contains two bytes. The 2nd byte is used to define the size of the additional octets added to the - * timestamp, which could not fit in a short P-field. - * - * @see CCSDS 301.0-B-4, Section 3.2.2 - * @tparam secondsBytes The number of octets used to represent the basic time units - * @tparam fractionalBytes The number of octets used to represent the fractional time units - * @return Two bytes, representing the P-field contents - */ -template <int secondsBytes, int fractionalBytes> -inline constexpr uint16_t buildLongCUCHeader() { - // cppcheck-suppress redundantCondition - static_assert(secondsBytes > 4 || fractionalBytes > 3, "Use buildShortCUCHeader instead"); - static_assert(secondsBytes <= 7, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); - static_assert(fractionalBytes <= 6, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); - - uint16_t header = 0; - - uint8_t octet1secondsBytes = std::min(4, secondsBytes); - uint8_t octet2secondsBytes = secondsBytes - octet1secondsBytes; - - uint8_t octet1fractionalBytes = std::min(3, fractionalBytes); - uint8_t octet2fractionalBytes = fractionalBytes - octet1fractionalBytes; - - // P-Field extension is 1, CUC header is extended - header += 1; - - // We are using custom a TAI epoch - header <<= 3U; - header += 0b010; - - // Number of bytes in the basic time unit - header <<= 2U; - header += octet1secondsBytes - 1; - - // Number of bytes in the fractional unit - header <<= 2U; - header += octet1fractionalBytes; - - // P-Field extension is 1, CUC header was extended - header <<= 1U; - header += 1; - - // Number of bytes in the extended basic time unit - header <<= 2U; - header += octet2secondsBytes; - - // Number of bytes in the extended fractional unit - header <<= 3U; - header += octet2fractionalBytes; - - // Last 3 LSB are reserved for custom mission use - header <<= 2U; - header += 0; - - return header; -} - -/** - * Builds the entire P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. - * - * The P-field contains the metadata of the timestamp, including information about its size and epoch. This function - * is implemented for arbitrary sizes (_octet count_) for the basic and fractional time units. - * - * The following options cannot be changed: - * - Time-code identification is set to an _agency-defined epoch_. This is represented by @ref Epoch. - * - Bits 6-7 of octet 2 (_reserved_) are set to `0` - * - * @note The P-field (header) does not contain the timestamp information, but only the description of the timestamp's - * structure. It may be entirely omitted if the structure is known beforehand. - * - * @see CCSDS 301.0-B-4, Section 3.2.2 - * @tparam T An arbitrary `uint` return type of the header - * @tparam secondsBytes The number of octets used to represent the basic time units - * @tparam fractionalBytes The number of octets used to represent the fractional time units - * @return One or two bytes representing the header - */ -template <typename T, int secondsBytes, int fractionalBytes> -inline constexpr T buildCUCHeader() { - // TODO: Gitlab issue #106 - static_assert((secondsBytes + fractionalBytes) <= 8, "Complete arbitrary precision not supported"); - // cppcheck-suppress syntaxError - // cppcheck-suppress redundantCondition - if constexpr (secondsBytes <= 4 && fractionalBytes <= 3) { - return buildShortCUCHeader<secondsBytes, fractionalBytes>(); - } else { - return buildLongCUCHeader<secondsBytes, fractionalBytes>(); +namespace Time { + inline constexpr uint8_t SecondsPerMinute = 60; + inline constexpr uint16_t SecondsPerHour = 3600; + inline constexpr uint32_t SecondsPerDay = 86400; + inline constexpr uint8_t MonthsPerYear = 12; + static constexpr uint8_t DaysOfMonth[MonthsPerYear] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + /** + * Number of bytes used for the basic time units of the CUC header for this mission + */ + inline constexpr uint8_t CUCSecondsBytes = 4; + + /** + * Number of bytes used for the fractional time units of the CUC header for this mission + */ + inline constexpr uint8_t CUCFractionalBytes = 0; + + /** + * The system epoch (clock measurement starting time). + * All timestamps emitted by the ECSS services will show the elapsed time (seconds, days etc.) from this epoch. + */ + inline constexpr struct { + uint16_t year; + uint8_t month; + uint8_t day; + } Epoch{ + 2020, + 1, + 1, + }; + + /** + * Number of seconds elapsed between the UNIX epoch (1 January 1970) and the system epoch. + * + * The system epoch is defined by @ref Epoch. + * This constant is used for conversion between Unix and other timestamps. + * Leap seconds are not taken into account here. + * + * @warning This value MUST be updated after every change of the system @ref Epoch. You can use utilities such as + * https://www.unixtimestamp.com/ to obtain a correct result. + */ + inline constexpr uint32_t EpochSecondsFromUnix = 1577836800; + + /** + * The maximum theoretical size in bytes of a CUC timestamp, including headers (P-field and T-field) + */ + inline constexpr uint8_t CUCTimestampMaximumSize = 9; + + static_assert(Epoch.year >= 2019); + static_assert(Epoch.month < 11 && Epoch.month >= 0); + static_assert(Epoch.day < DaysOfMonth[Epoch.month]); + + /** + * Builds the short P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. + * + * The short P-field contains only one byte. It is used when many octets are used to represent the basic or fractional + * time units. + * + * @see CCSDS 301.0-B-4, Section 3.2.2 + * @tparam secondsBytes The number of octets used to represent the basic time units + * @tparam fractionalBytes The number of octets used to represent the fractional time units + * @return A single byte, representing the P-field contents + */ + template <int secondsBytes, int fractionalBytes> + inline constexpr uint8_t buildShortCUCHeader() { + static_assert(secondsBytes <= 4, "Use buildLongCUCHeader instead"); + static_assert(fractionalBytes <= 3, "Use buildLongCUCHeader instead"); + + uint8_t header = 0; + + // P-Field extension is 0, CUC header is not extended + header += 0; + + // We are using a custom TAI epoch ("agency-defined epoch") + header <<= 3U; + header += 0b010; + + // Number of bytes in the basic time unit + header <<= 2U; + header += secondsBytes - 1; + + // Number of bytes in the fractional unit + header <<= 2U; + header += fractionalBytes; + + return header; } -} -/** - * Returns whether a year is a leap year according to the Gregorian calendar - */ -constexpr bool isLeapYear(uint16_t year) { - if ((year % 4) != 0) { - return false; + /** + * Builds the long P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. + * + * The long P-field contains two bytes. The 2nd byte is used to define the size of the additional octets added to the + * timestamp, which could not fit in a short P-field. + * + * @see CCSDS 301.0-B-4, Section 3.2.2 + * @tparam secondsBytes The number of octets used to represent the basic time units + * @tparam fractionalBytes The number of octets used to represent the fractional time units + * @return Two bytes, representing the P-field contents + */ + template <int secondsBytes, int fractionalBytes> + inline constexpr uint16_t buildLongCUCHeader() { + // cppcheck-suppress redundantCondition + static_assert(secondsBytes > 4 || fractionalBytes > 3, "Use buildShortCUCHeader instead"); + static_assert(secondsBytes <= 7, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); + static_assert(fractionalBytes <= 6, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); + + uint16_t header = 0; + + uint8_t octet1secondsBytes = std::min(4, secondsBytes); + uint8_t octet2secondsBytes = secondsBytes - octet1secondsBytes; + + uint8_t octet1fractionalBytes = std::min(3, fractionalBytes); + uint8_t octet2fractionalBytes = fractionalBytes - octet1fractionalBytes; + + // P-Field extension is 1, CUC header is extended + header += 1; + + // We are using custom a TAI epoch + header <<= 3U; + header += 0b010; + + // Number of bytes in the basic time unit + header <<= 2U; + header += octet1secondsBytes - 1; + + // Number of bytes in the fractional unit + header <<= 2U; + header += octet1fractionalBytes; + + // P-Field extension is 1, CUC header was extended + header <<= 1U; + header += 1; + + // Number of bytes in the extended basic time unit + header <<= 2U; + header += octet2secondsBytes; + + // Number of bytes in the extended fractional unit + header <<= 3U; + header += octet2fractionalBytes; + + // Last 3 LSB are reserved for custom mission use + header <<= 2U; + header += 0; + + return header; } - if ((year % 100) != 0) { - return true; + + /** + * Builds the entire P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. + * + * The P-field contains the metadata of the timestamp, including information about its size and epoch. This function + * is implemented for arbitrary sizes (_octet count_) for the basic and fractional time units. + * + * The following options cannot be changed: + * - Time-code identification is set to an _agency-defined epoch_. This is represented by @ref Epoch. + * - Bits 6-7 of octet 2 (_reserved_) are set to `0` + * + * @note The P-field (header) does not contain the timestamp information, but only the description of the timestamp's + * structure. It may be entirely omitted if the structure is known beforehand. + * + * @see CCSDS 301.0-B-4, Section 3.2.2 + * @tparam T An arbitrary `uint` return type of the header + * @tparam secondsBytes The number of octets used to represent the basic time units + * @tparam fractionalBytes The number of octets used to represent the fractional time units + * @return One or two bytes representing the header + */ + template <typename T, int secondsBytes, int fractionalBytes> + inline constexpr T buildCUCHeader() { + static_assert((secondsBytes + fractionalBytes) <= 8, "Complete arbitrary precision not supported"); + // cppcheck-suppress syntaxError + // cppcheck-suppress redundantCondition + if constexpr (secondsBytes <= 4 && fractionalBytes <= 3) { + return buildShortCUCHeader<secondsBytes, fractionalBytes>(); + } else { + return buildLongCUCHeader<secondsBytes, fractionalBytes>(); + } } - return (year % 400) == 0; -} -typedef struct { - uint64_t elapsed100msTicks = 0; -} CustomCUC_t; + /** + * Returns whether a year is a leap year according to the Gregorian calendar + */ + constexpr bool isLeapYear(uint16_t year) { + if ((year % 4) != 0) { + return false; + } + if ((year % 100) != 0) { + return true; + } + return (year % 400) == 0; + } + /** + * A time shift for scheduled activities measured in seconds + */ + typedef int64_t RelativeTime; + + /** + * is_duration definition to check if a variable is std::chrono::duration + */ + template <typename T> + struct is_duration + : std::false_type {}; + + /** + * is_duration definition to check if a variable is std::chrono::duration + */ + template <typename Rep, typename Period> + struct is_duration<std::chrono::duration<Rep, Period>> + : std::true_type {}; + + /** + * True if T is std::chrono::duration, false if not + */ + template <class T> + inline constexpr bool is_duration_v = is_duration<T>::value; } // namespace Time + +#endif diff --git a/inc/Time/TimeStamp.hpp b/inc/Time/TimeStamp.hpp index 499fb3c66ac9869be384e9fde471e4eb903d5c05..5bdc1c3700dacdd686f67a2ea6975295493debbf 100644 --- a/inc/Time/TimeStamp.hpp +++ b/inc/Time/TimeStamp.hpp @@ -1,37 +1,95 @@ -#ifndef ECSS_SERVICES_TIME_HPP -#define ECSS_SERVICES_TIME_HPP +#pragma once -#include <cstdint> #include <algorithm> -#include "macros.hpp" +#include <chrono> +#include <cstdint> #include <etl/array.h> #include "Time.hpp" #include "UTCTimestamp.hpp" +#include "macros.hpp" /** * A class that represents an instant in time, with convenient conversion * to and from usual time and date representations * + * This class is compatible with the CUC (Unsegmented Time Code) format defined in CCSDS 301.0-B-4. It allows specifying: + * - Different amount of bytes for the basic time unit + * - Different amount of bytes for the fractional time unit + * - Different basic time units + * + * The timestamp is defined in relation to a user-defined epoch, set in @ref Time::Epoch. + * + * @section baseunit Setting the base time unit + * By default, this class measures time in the order of **seconds**. Binary fractions of a second can be specified by increasing the FractionBytes. + * However, the user can change the base time unit by setting the @p Num and @p Denom template parameters. + * + * The base time unit (or period) is then represented by the following: + * \f[ + * \text{time unit} = \frac{Num}{Denom} \cdot \text{second} + * \f] + * * @note * This class uses internally TAI time, and handles UTC leap seconds at conversion to and * from UTC time system. * + * @tparam BaseBytes The number of bytes used for the basic time units. This essentially defines the maximum duration from Epoch that this timestamp can represent. + * @tparam FractionBytes The number of bytes used for the fraction of one basic time unit. This essentially defines the precision of the timestamp. + * @tparam Num The numerator of the base type ratio (see @ref baseunit) + * @tparam Denom The numerator of the base type ratio (see @ref baseunit) + * * @ingroup Time * @author Baptiste Fournier + * @author Konstantinos Kanavouras * @see [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) */ -template <uint8_t secondsBytes, uint8_t fractionalBytes> +template <uint8_t BaseBytes = 4, uint8_t FractionBytes = 0, int Num = 1, int Denom = 1> class TimeStamp { +public: + /** + * The period of the base type, in relation to the second + * + * This type represents the base type of the timestamp. + * + * A ratio of `<1, 1>` (or 1/1) means that this timestamp represents seconds. A ratio of `<60, 1>` (or 60/1) means + * that this class represents 60s of seconds, or minutes. A ratio of `<1, 1000>` (or 1/1000) means that this class + * represents 1000ths of seconds, or milliseconds. + * + * This type has essentially the same meaning of `Rep` in [std::chrono::duration](https://en.cppreference.com/w/cpp/chrono/duration). + * + * @note std::ratio will simplify the fractions numerator and denominator + */ + using Ratio = std::ratio<Num, Denom>; + private: - static_assert(secondsBytes + fractionalBytes <= 8, + static_assert(BaseBytes + FractionBytes <= 8, "Currently, this class is not suitable for storage on internal counter larger than uint64_t"); - typedef typename std::conditional<(secondsBytes < 4 && fractionalBytes < 3), uint8_t, uint16_t>::type CUCHeader_t; - typedef typename std::conditional<(secondsBytes + fractionalBytes < 4), uint32_t, uint64_t>::type TAICounter_t; + using CUCHeader_t = typename std::conditional<(BaseBytes < 4 && FractionBytes < 3), uint8_t, uint16_t>::type; + using TAICounter_t = typename std::conditional<(BaseBytes + FractionBytes <= 4), uint32_t, uint64_t>::type; + + /** + * The period of the internal counter + * + * Same as @ref Ratio, but instead of representing the Base bytes, it represents the entire value held by @ref taiCounter. + */ + using RawRatio = std::ratio<Num, Denom * 1UL << (8 * FractionBytes)>; + + /** + * An std::chrono::duration representation of the base type (without the fractional part) + */ + using BaseDuration = std::chrono::duration<TAICounter_t, Ratio>; + + /** + * An std::chrono::duration representation of the complete @ref taiCounter (including the fractional part) + */ + using RawDuration = std::chrono::duration<TAICounter_t, RawRatio>; + + template <uint8_t, uint8_t, int, int> + friend class TimeStamp; /** * Integer counter of time units since the @ref Time::Epoch. This number essentially represents the timestamp. * - * The unit represented by this variable depends on `secondsBytes` and `fractionalBytes`. The fractional + * The unit represented by this variable depends on `BaseBytes` and `FractionBytes`. The fractional * part is included as the least significant bits of this variable, and the base part follows. */ TAICounter_t taiCounter; @@ -39,13 +97,20 @@ private: /** * The constant header ("P-value") of the timestamp, if needed to be attached to any message */ - static constexpr CUCHeader_t CUCHeader = Time::buildCUCHeader<CUCHeader_t, secondsBytes, fractionalBytes>(); + static constexpr CUCHeader_t CUCHeader = Time::buildCUCHeader<CUCHeader_t, BaseBytes, FractionBytes>(); /** - * The maximum value that can fit in @ref taiCounter, or the maximum number of seconds since epoch that can be - * represented in this base class + * The maximum value of the base type (seconds, larger or smaller) that can fit in @ref taiCounter */ - static constexpr uint64_t maxSecondCounterValue = (1U << (8U * secondsBytes)) - 1; + static constexpr uint64_t MaxBase = + (BaseBytes == 8) + ? std::numeric_limits<uint64_t>::max() + : (uint64_t{1} << 8 * BaseBytes) - 1; + + /** + * The maximum number of seconds since epoch that can be represented in this class + */ + static constexpr uint64_t MaxSeconds = std::chrono::duration_cast<std::chrono::duration<uint64_t>>(BaseDuration(MaxBase)).count(); /** * Returns whether the amount of `seconds` can be represented by this TimeStamp. @@ -68,19 +133,10 @@ public: */ explicit TimeStamp(uint64_t taiSecondsFromEpoch); - /** - * Initialize the TimeStamp from a count of 100ms ticks from epoch in TAI (leap seconds not accounted) - * - * @param customCUCTimestamp An struct containing a 64 bit unsigned number of 100ms - * ticks from the custom @ref Time::Epoch - */ - explicit TimeStamp(Time::CustomCUC_t customCUCTimestamp); - /** * Initialize the TimeStamp from the bytes of a CUC time stamp * - * @param timestamp A complete CUC timestamp including header, of the maximum possible size, zero padded to the - * right + * @param timestamp A complete CUC timestamp including header, of the maximum possible size, zero padded to the right */ explicit TimeStamp(etl::array<uint8_t, Time::CUCTimestampMaximumSize> timestamp); @@ -92,25 +148,36 @@ public: explicit TimeStamp(const UTCTimestamp& timestamp); /** - * Get the representation as seconds from epoch in TAI + * Convert a TimeStamp to a TimeStamp with different parameters * - * @return The seconds elapsed in TAI since @ref Time::Epoch. This function is explicitly defined + * This constructor will convert based on the number of bytes, and base units + * + * @note Internally uses double-precision floating point to allow for arbitrary ratios */ - TAICounter_t asTAIseconds(); + template <uint8_t BaseBytesIn, uint8_t FractionBytesIn, int NumIn = 1, int DenomIn = 1> + explicit TimeStamp(TimeStamp<BaseBytesIn, FractionBytesIn, NumIn, DenomIn> input); /** - * Get the representation as a struct containing 100ms ticks from epoch in TAI + * Convert an [std::chrono::duration](https://en.cppreference.com/w/cpp/chrono/duration) representing seconds from @ref Time::Epoch + * to a timestamp * - * @return An struct containing a 64 bit unsigned number of 100ms - * ticks from the custom @ref Time::Epoch. This function is explicitly defined. + * @warning This function does not perform overflow calculations. It is up to the user to ensure that the types are + * compatible so that no overflow occurs. */ - Time::CustomCUC_t asCustomCUCTimestamp(); + template <class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + explicit TimeStamp(Duration duration); + + /** + * Get the representation as seconds from epoch in TAI + * + * @return The seconds elapsed in TAI since @ref Time::Epoch. This function is explicitly defined + */ + TAICounter_t asTAIseconds(); /** * Get the representation as seconds from epoch in TAI, for a floating-point representation. * For an integer result, see the overloaded @ref asTAIseconds function. * - * @todo Implement integer seconds in this function * @tparam T The return type of the seconds (float or double). * @return The seconds elapsed in TAI since @ref Time::Epoch */ @@ -118,11 +185,24 @@ public: T asTAIseconds(); /** - * Get the representation as CUC formatted bytes + * Converts a TimeStamp to a duration of seconds since the @ref Time::Epoch. * - * @return The TimeStamp, represented in the CCSDS CUC format + * @warning This function does not perform overflow calculations. It is up to the user to ensure that the types are compatible so that no overflow occurs. + */ + template <class Duration = std::chrono::seconds> + Duration asDuration() const; + + /** + * Get the representation as CUC formatted bytes, including the header (P-field and T-field) */ - etl::array<uint8_t, Time::CUCTimestampMaximumSize> toCUCtimestamp(); + etl::array<uint8_t, Time::CUCTimestampMaximumSize> formatAsCUC(); + + /** + * Get the representation as CUC formatted bytes, without the header (T-field only) + */ + TAICounter_t formatAsBytes() const { + return taiCounter; + } /** * Get the representation as a UTC timestamp @@ -132,37 +212,137 @@ public: UTCTimestamp toUTCtimestamp(); /** - * Compare two timestamps. + * Get the maximum timestamp that can be represented by this class * - * @param timestamp the date that will be compared with the pointer `this` - * @return true if the condition is satisfied + * Can be used to represent null or infinite amounts of time */ - bool operator<(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter < timestamp.taiCounter; + static TimeStamp<BaseBytes, FractionBytes, Num, Denom> max() { + TimeStamp<BaseBytes, FractionBytes, Num, Denom> timestamp; + timestamp.taiCounter = std::numeric_limits<TAICounter_t>::max(); + return timestamp; } - bool operator>(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter > timestamp.taiCounter; + /** + * Adds any arbitrary duration to a timestamp. + * + * You can play with default C++ durations with this function: + * ```cpp + * using namespace std::literals; + * + * timestamp + std::chrono::seconds(5); // adds 5 seconds + * timestamp + std::chrono::milliseconds(500); // adds 5 milliseconds + * timestamp + 60s; // adds 60 seconds + */ + template <class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + TimeStamp<BaseBytes, FractionBytes, Num, Denom> operator+(const Duration& duration) const { + auto output = *this; + output += duration; + return output; } - bool operator==(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter == timestamp.taiCounter; + template <class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + TimeStamp<BaseBytes, FractionBytes, Num, Denom>& operator+=(const Duration& duration) { + if (duration < Duration::zero()) { + taiCounter -= std::chrono::duration_cast<RawDuration>(-duration).count(); + } else { + taiCounter += std::chrono::duration_cast<RawDuration>(duration).count(); + } + + return *this; } - bool operator!=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter != timestamp.taiCounter; + template <class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + TimeStamp<BaseBytes, FractionBytes, Num, Denom> operator-(const Duration& duration) const { + auto output = *this; + output -= duration; + return output; } - bool operator<=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter <= timestamp.taiCounter; + template <class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + TimeStamp<BaseBytes, FractionBytes, Num, Denom>& operator-=(const Duration& duration) { + if (duration < Duration::zero()) { + taiCounter += std::chrono::duration_cast<RawDuration>(-duration).count(); + } else { + taiCounter -= std::chrono::duration_cast<RawDuration>(duration).count(); + } + + return *this; } - bool operator>=(const TimeStamp<secondsBytes, fractionalBytes>& timestamp) const { - return taiCounter >= timestamp.taiCounter; + /** + * Subtraction between two timestamps. + * + * Given 2 absolute moments in time, returns the relative duration between them. + * @tparam Duration The duration returned is equal to the RawDuration of the first timestamp, + * but it's signed instead of unsigned, so that negative results can be represented. + */ + template < + uint8_t BaseBytesIn, uint8_t FractionBytesIn, int NumIn = 1, int DenomIn = 1, // Template parameters of the 2nd timestamp + class Duration = std::chrono::duration< // Create a new Duration based on our RawDuration... + typename std::make_signed<typename RawDuration::rep>::type, // the Duration base type is equal to the RawDuration, but converted to signed from unsigned + typename RawDuration::period>> + Duration operator-(const TimeStamp<BaseBytesIn, FractionBytesIn, NumIn, DenomIn>& operand) const { + Duration myDuration = asDuration<Duration>(); + Duration operandDuration = operand.template asDuration<Duration>(); + + return myDuration - operandDuration; } + + /** + * @name Comparison operators between timestamps + * @{ + */ + template <class OtherTimestamp> + bool operator<(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) < typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + + template <class OtherTimestamp> + bool operator>(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) > typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + + template <class OtherTimestamp> + bool operator==(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) == typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + + template <class OtherTimestamp> + bool operator!=(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) != typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + + template <class OtherTimestamp> + bool operator<=(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) <= typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + + template <class OtherTimestamp> + bool operator>=(const OtherTimestamp& timestamp) const { + return RawDuration(taiCounter) >= typename OtherTimestamp::RawDuration(timestamp.taiCounter); + } + /** + * @} + */ }; -#include "TimeStamp.tpp" -typedef TimeStamp<Time::CUCSecondsBytes, Time::CUCFractionalBytes> AcubeSATTimeStamp_t; +namespace Time { + using DefaultCUC = TimeStamp<4, 0, 1, 10>; + + /** + * Creates a custom literal to specify timestamp ticks. + * + * For example, this code: + * ```cpp + * Time::DefaultCUC timestamp(1000_t); + * ``` + * will define a timestamp 1000 ticks from the epoch. + * + * The time amount of a "tick" is the period defined by the DefaultCUC::Ratio + */ + constexpr std::chrono::duration<uint32_t, DefaultCUC::Ratio> operator""_t(unsigned long long s) { + return std::chrono::duration<uint32_t, DefaultCUC::Ratio>(s); + } +} // namespace Time -#endif +#include "TimeStamp.tpp" diff --git a/inc/Time/TimeStamp.tpp b/inc/Time/TimeStamp.tpp index 34edf5a2c291153b1a38778ebc0462ec661d9fd6..4df8d7990812fb72205165167f16d5022f88291a 100644 --- a/inc/Time/TimeStamp.tpp +++ b/inc/Time/TimeStamp.tpp @@ -1,46 +1,39 @@ -template <uint8_t secondsBytes, uint8_t fractionalBytes> -constexpr bool TimeStamp<secondsBytes, fractionalBytes>::areSecondsValid(TimeStamp::TAICounter_t seconds) { - return seconds < maxSecondCounterValue; +#include <cmath> +#include "TimeStamp.hpp" + +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +constexpr bool TimeStamp<BaseBytes, FractionBytes, Num, Denom>::areSecondsValid(TimeStamp::TAICounter_t seconds) { + return seconds <= MaxSeconds; } -template <uint8_t secondsBytes, uint8_t fractionalBytes> -TimeStamp<secondsBytes, fractionalBytes>::TimeStamp(uint64_t taiSecondsFromEpoch) { - ASSERT_INTERNAL(areSecondsValid((taiSecondsFromEpoch)), ErrorHandler::InternalErrorType::InvalidTimeStampInput); +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TimeStamp(uint64_t taiSecondsFromEpoch) { + ASSERT_INTERNAL(areSecondsValid((taiSecondsFromEpoch)), ErrorHandler::InternalErrorType::TimeStampOutOfBounds); - taiCounter = static_cast<TAICounter_t>(taiSecondsFromEpoch) << 8 * fractionalBytes; -} + using FromDuration = std::chrono::duration<uint64_t>; + const auto duration = FromDuration(taiSecondsFromEpoch); -template <uint8_t secondsBytes, uint8_t fractionalBytes> -TimeStamp<secondsBytes, fractionalBytes>::TimeStamp(Time::CustomCUC_t customCUCTimestamp) { - ASSERT_INTERNAL(areSecondsValid((customCUCTimestamp.elapsed100msTicks / 10)), - ErrorHandler::InternalErrorType::InvalidTimeStampInput); - taiCounter = static_cast<TAICounter_t>(customCUCTimestamp.elapsed100msTicks / 10); - if (fractionalBytes > 0) { - TAICounter_t fractionalPart = static_cast<TAICounter_t>(customCUCTimestamp.elapsed100msTicks) - 10 * taiCounter; - taiCounter = taiCounter << 8; - taiCounter += fractionalPart * 256 / 10; - taiCounter = taiCounter << 8 * (fractionalBytes - 1); - } + taiCounter = std::chrono::duration_cast<RawDuration>(duration).count(); } -template <uint8_t secondsCounter, uint8_t fractionalBytes> -TimeStamp<secondsCounter, fractionalBytes>::TimeStamp(etl::array<uint8_t, Time::CUCTimestampMaximumSize> timestamp) { +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TimeStamp(etl::array<uint8_t, Time::CUCTimestampMaximumSize> timestamp) { // process header uint8_t headerSize = 1; if ((timestamp[0] & 0b10000000U) != 0) { headerSize = 2; } - uint8_t inputSecondsBytes = ((timestamp[0] & 0b00001100U) >> 2U) + 1U; - uint8_t inputFractionalBytes = (timestamp[0] & 0b00000011U) >> 0U; + uint8_t inputBaseBytes = ((timestamp[0] & 0b00001100U) >> 2U) + 1U; + uint8_t inputFractionBytes = (timestamp[0] & 0b00000011U) >> 0U; if (headerSize == 2) { - inputSecondsBytes += (timestamp[1] & 0b01100000U) >> 5U; - inputFractionalBytes += (timestamp[1] & 0b00011100U) >> 2U; + inputBaseBytes += (timestamp[1] & 0b01100000U) >> 5U; + inputFractionBytes += (timestamp[1] & 0b00011100U) >> 2U; } // check input validity (useless bytes set to 0) - for (int i = headerSize + inputSecondsBytes + inputFractionalBytes; i < Time::CUCTimestampMaximumSize; i++) { + for (int i = headerSize + inputBaseBytes + inputFractionBytes; i < Time::CUCTimestampMaximumSize; i++) { if (timestamp[i] != 0) { ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::InvalidTimeStampInput); break; @@ -48,78 +41,82 @@ TimeStamp<secondsCounter, fractionalBytes>::TimeStamp(etl::array<uint8_t, Time:: } // do checks wrt template precision parameters - ASSERT_INTERNAL(inputSecondsBytes <= secondsCounter, ErrorHandler::InternalErrorType::InvalidTimeStampInput); - ASSERT_INTERNAL(inputFractionalBytes <= fractionalBytes, ErrorHandler::InternalErrorType::InvalidTimeStampInput); + ASSERT_INTERNAL(inputBaseBytes <= BaseBytes, ErrorHandler::InternalErrorType::InvalidTimeStampInput); + ASSERT_INTERNAL(inputFractionBytes <= FractionBytes, ErrorHandler::InternalErrorType::InvalidTimeStampInput); // put timestamp into internal counter taiCounter = 0; // add seconds until run out of bytes on input array - for (auto i = 0; i < inputSecondsBytes + inputFractionalBytes; i++) { + for (auto i = 0; i < inputBaseBytes + inputFractionBytes; i++) { taiCounter = taiCounter << 8; taiCounter += timestamp[headerSize + i]; } // pad rightmost bytes to full length - taiCounter = taiCounter << 8 * (fractionalBytes - inputFractionalBytes); + taiCounter = taiCounter << 8 * (FractionBytes - inputFractionBytes); } -template <uint8_t seconds_counter_bytes, uint8_t fractional_counter_bytes> -TimeStamp<seconds_counter_bytes, fractional_counter_bytes>::TimeStamp(const UTCTimestamp& timestamp) { +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TimeStamp(const UTCTimestamp& timestamp) { TAICounter_t seconds = 0; + + /** + * Add to the seconds variable, with an overflow check + */ + auto secondsAdd = [&seconds](TAICounter_t value) { + seconds += value; + if (seconds < value) { + ErrorHandler::reportInternalError(ErrorHandler::TimeStampOutOfBounds); + } + }; + for (int year = Time::Epoch.year; year < timestamp.year; ++year) { - seconds += (Time::isLeapYear(year) ? 366 : 365) * Time::SecondsPerDay; + secondsAdd((Time::isLeapYear(year) ? 366 : 365) * Time::SecondsPerDay); } for (int month = Time::Epoch.month; month < timestamp.month; ++month) { - seconds += Time::DaysOfMonth[month - 1] * Time::SecondsPerDay; + secondsAdd(Time::DaysOfMonth[month - 1] * Time::SecondsPerDay); if ((month == 2U) && Time::isLeapYear(timestamp.year)) { - seconds += Time::SecondsPerDay; + secondsAdd(Time::SecondsPerDay); } } - seconds += (timestamp.day - Time::Epoch.day) * Time::SecondsPerDay; - seconds += timestamp.hour * Time::SecondsPerHour; - seconds += timestamp.minute * Time::SecondsPerMinute; - seconds += timestamp.second; - // TODO: Add check that `seconds` is within bounds (?) - taiCounter = static_cast<TAICounter_t>(seconds) << 8 * fractional_counter_bytes; -} -template <uint8_t secondsBytes, uint8_t fractionalBytes> -typename TimeStamp<secondsBytes, fractionalBytes>::TAICounter_t -TimeStamp<secondsBytes, fractionalBytes>::asTAIseconds() { - return taiCounter >> (8 * fractionalBytes); + secondsAdd((timestamp.day - Time::Epoch.day) * Time::SecondsPerDay); + secondsAdd(timestamp.hour * Time::SecondsPerHour); + secondsAdd(timestamp.minute * Time::SecondsPerMinute); + secondsAdd(timestamp.second); + + ASSERT_INTERNAL(areSecondsValid(seconds), ErrorHandler::TimeStampOutOfBounds); + + taiCounter = std::chrono::duration_cast<RawDuration>(std::chrono::duration<TAICounter_t>(seconds)).count(); } -template <uint8_t secondsBytes, uint8_t fractionalBytes> -Time::CustomCUC_t TimeStamp<secondsBytes, fractionalBytes>::asCustomCUCTimestamp() { - TAICounter_t temp = taiCounter; - Time::CustomCUC_t return_s = {0}; - if (fractionalBytes > 0) { - temp = temp >> 8 * (fractionalBytes - 1); - return_s.elapsed100msTicks += temp * 10 / 256; - } else { - return_s.elapsed100msTicks += temp * 10; - } - return return_s; +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +typename TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TAICounter_t +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::asTAIseconds() { + const auto duration = RawDuration(taiCounter); + using ToDuration = std::chrono::duration<TAICounter_t>; + + return std::chrono::duration_cast<ToDuration>(duration).count(); } -template <uint8_t secondsBytes, uint8_t fractionalBytes> +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> template <typename T> -T TimeStamp<secondsBytes, fractionalBytes>::asTAIseconds() { +T TimeStamp<BaseBytes, FractionBytes, Num, Denom>::asTAIseconds() { static_assert(std::is_floating_point_v<T>, "TimeStamp::asTAIseconds() only accepts numeric types."); - static_assert(std::numeric_limits<T>::max() >= maxSecondCounterValue); + static_assert(std::numeric_limits<T>::max() >= MaxSeconds); - TAICounter_t decimalPart = taiCounter >> (8 * fractionalBytes); + TAICounter_t decimalPart = taiCounter >> (8 * FractionBytes); - T fractionalPart = taiCounter - (decimalPart << (8 * fractionalBytes)); - T fractionalPartMax = (1U << (8U * fractionalBytes)) - 1U; + T fractionalPart = taiCounter - (decimalPart << (8 * FractionBytes)); + T fractionalPartMax = (1U << (8U * FractionBytes)) - 1U; return decimalPart + fractionalPart / fractionalPartMax; } -template <uint8_t secondsBytes, uint8_t fractionalBytes> -etl::array<uint8_t, Time::CUCTimestampMaximumSize> TimeStamp<secondsBytes, fractionalBytes>::toCUCtimestamp() { +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +etl::array<uint8_t, Time::CUCTimestampMaximumSize> TimeStamp<BaseBytes, FractionBytes, Num, Denom>::formatAsCUC() { etl::array<uint8_t, Time::CUCTimestampMaximumSize> returnArray = {0}; - static constexpr uint8_t headerBytes = (secondsBytes < 4 && fractionalBytes < 3) ? 1 : 2; + static constexpr uint8_t headerBytes = (BaseBytes < 4 && FractionBytes < 3) ? 1 : 2; if (headerBytes == 1) { returnArray[0] = static_cast<uint8_t>(CUCHeader); @@ -128,60 +125,54 @@ etl::array<uint8_t, Time::CUCTimestampMaximumSize> TimeStamp<secondsBytes, fract returnArray[0] = static_cast<uint8_t>(CUCHeader >> 8); } - for (auto byte = 0; byte < secondsBytes + fractionalBytes; byte++) { - uint8_t taiCounterIndex = 8 * (secondsBytes + fractionalBytes - byte - 1); + for (auto byte = 0; byte < BaseBytes + FractionBytes; byte++) { + uint8_t taiCounterIndex = 8 * (BaseBytes + FractionBytes - byte - 1); returnArray[headerBytes + byte] = taiCounter >> taiCounterIndex; } return returnArray; } -template <uint8_t secondsBytes, uint8_t fractionalBytes> -UTCTimestamp TimeStamp<secondsBytes, fractionalBytes>::toUTCtimestamp() { - using namespace Time; - - uint32_t totalSeconds = asTAIseconds(); +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +UTCTimestamp TimeStamp<BaseBytes, FractionBytes, Num, Denom>::toUTCtimestamp() { + UTCTimestamp timestamp(Time::Epoch.year, Time::Epoch.month, Time::Epoch.day, 0, 0, 0); + timestamp += RawDuration(taiCounter); - uint16_t yearUTC = Epoch.year; - uint8_t monthUTC = Epoch.month; - uint8_t dayUTC = Epoch.day; - uint8_t hour = 0; - uint8_t minute = 0; - uint8_t second = 0; + return timestamp; +} - // calculate years - while (totalSeconds >= (isLeapYear(yearUTC) ? 366 : 365) * SecondsPerDay) { - totalSeconds -= (isLeapYear(yearUTC) ? 366 : 365) * SecondsPerDay; - yearUTC++; +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +template <uint8_t BaseBytesIn, uint8_t FractionBytesIn, int NumIn, int DenomIn> +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TimeStamp(TimeStamp<BaseBytesIn, FractionBytesIn, NumIn, DenomIn> input) { + if constexpr (std::is_same_v<decltype(*this), decltype(input)>) { + taiCounter = input.taiCounter; + return; } - // calculate months - int currentMonth = 0; - while (totalSeconds >= (DaysOfMonth[currentMonth] * SecondsPerDay)) { - monthUTC++; - totalSeconds -= (DaysOfMonth[currentMonth] * SecondsPerDay); - currentMonth++; - if ((currentMonth == 1U) && isLeapYear(yearUTC)) { - if (totalSeconds <= (28 * SecondsPerDay)) { - break; - } - monthUTC++; - totalSeconds -= 29 * SecondsPerDay; - currentMonth++; - } - } + constexpr double InputRatio = static_cast<double>(NumIn) / DenomIn; + constexpr double OutputRatio = static_cast<double>(Num) / Denom; + + double inputSeconds = input.taiCounter / static_cast<double>(1 << (8 * FractionBytesIn)); + inputSeconds *= InputRatio; - dayUTC = totalSeconds / SecondsPerDay; - totalSeconds -= dayUTC * SecondsPerDay; - dayUTC++; // add 1 day because we start count from 1 January (and not 0 January!) + ErrorHandler::assertInternal(inputSeconds <= MaxSeconds, ErrorHandler::TimeStampOutOfBounds); - hour = totalSeconds / SecondsPerHour; - totalSeconds -= hour * SecondsPerHour; + double output = inputSeconds / OutputRatio * (1UL << (8 * FractionBytes)); - minute = totalSeconds / SecondsPerMinute; - totalSeconds -= minute * SecondsPerMinute; + taiCounter = static_cast<TAICounter_t>(round(output)); +} - second = totalSeconds; +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +template <class Duration> +Duration TimeStamp<BaseBytes, FractionBytes, Num, Denom>::asDuration() const { + auto duration = RawDuration(taiCounter); + + return std::chrono::duration_cast<Duration>(duration); +} - return {yearUTC, monthUTC, dayUTC, hour, minute, second}; +template <uint8_t BaseBytes, uint8_t FractionBytes, int Num, int Denom> +template <class Duration, typename> +TimeStamp<BaseBytes, FractionBytes, Num, Denom>::TimeStamp(Duration duration) { + auto outputDuration = std::chrono::duration_cast<RawDuration>(duration); + taiCounter = outputDuration.count(); } diff --git a/inc/Time/UTCTimestamp.hpp b/inc/Time/UTCTimestamp.hpp index 062402df0926efcf9d739cadd7182ca1710a8fe2..a2fe7bf6caa2d0159f078538ac1255e821d1db21 100644 --- a/inc/Time/UTCTimestamp.hpp +++ b/inc/Time/UTCTimestamp.hpp @@ -2,6 +2,7 @@ #include <cstdint> #include <etl/String.hpp> +#include "Time.hpp" /** * A class that represents a UTC time and date according to ISO 8601 @@ -29,8 +30,7 @@ public: UTCTimestamp(); /** - * - * @todo See if this implements leap seconds + * @todo Add support for leap seconds * @todo Implement leap seconds as ST[20] parameter * @param year the year as it used in Gregorian calendar * @param month the month as it used in Gregorian calendar (1-12 inclusive) @@ -42,19 +42,92 @@ public: UTCTimestamp(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); /** - * @param textTimestamp the timestamp to parse into a UTC date - * @todo Too expensive to implement (?). It is better to remove this and open it as another issue, or create - * a platform-specific converter that will be only used in x86. + * Add a duration to the timestamp + * + * @note Overflow checks are not performed. + * @tparam Duration A duration of type std::chrono::duration. You can use the default values offered by C++, or anything + * used by the TimeStamp class. Negative duration values are not supported. */ - explicit UTCTimestamp(etl::string<32> textTimestamp); + template<class Duration, typename = std::enable_if_t<Time::is_duration_v<Duration>>> + void operator+=(const Duration& in) { + using namespace std::chrono; + using namespace Time; + + if (in < Duration::zero()) { + ErrorHandler::reportInternalError(ErrorHandler::InvalidTimeStampInput); + return; + } + + uint64_t seconds = duration_cast<duration<uint64_t>>(in).count(); + + while (seconds >= (isLeapYear(year) ? 366L : 365L) * SecondsPerDay) { + seconds -= (isLeapYear(year) ? 366L : 365L) * SecondsPerDay; + year++; + } + + while (seconds >= (daysOfMonth() * uint64_t{SecondsPerDay})) { + seconds -= daysOfMonth() * uint64_t{SecondsPerDay}; + month++; + + if (month > MonthsPerYear) { + // Month overflow needs to be taken care here, so that daysOfMonth() knows + // what month it is. + month -= MonthsPerYear; + year++; + } + } + + day += seconds / SecondsPerDay; + seconds -= (seconds / SecondsPerDay) * SecondsPerDay; + + hour += seconds / SecondsPerHour; + seconds -= (seconds / SecondsPerHour) * SecondsPerHour; + + minute += seconds / SecondsPerMinute; + seconds -= (seconds / SecondsPerMinute) * SecondsPerMinute; + + second += seconds; + + repair(); + } /** - * Compare two timestamps. - * @param Date the date that will be compared with the pointer `this` + * @name Comparison operators + * @{ */ bool operator<(const UTCTimestamp& Date) const; - bool operator>(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator< - bool operator==(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator< - bool operator<=(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator< - bool operator>=(const UTCTimestamp& Date) const; ///< @copydoc UTCTimestamp::operator< + bool operator>(const UTCTimestamp& Date) const; + bool operator==(const UTCTimestamp& Date) const; + bool operator<=(const UTCTimestamp& Date) const; + bool operator>=(const UTCTimestamp& Date) const; + /** + * @} + */ + +private: + /** + * Makes sure that all time fields are within their bounds + * + * For example, if `hours == 1, minutes == 63`, then this function will carry over the numbers so that + * `hours == 2, minutes == 3`. + * + * @note This performs max one propagation for every field. + * For example, if `hours == 1, minutes == 123`, then only the first 60 minutes will be carried over. + */ + void repair(); + + /** + * Find the number of days within the current @ref month. + * Includes leap year calculation. + */ + uint8_t daysOfMonth() const { + using namespace Time; + + uint8_t daysOfMonth = DaysOfMonth[month - 1]; + if (month == 2 && isLeapYear(year)) { + daysOfMonth++; + } + + return daysOfMonth; + } }; diff --git a/inc/etl/String.hpp b/inc/etl/String.hpp index 7691f78323f636c5741a68b9cb2144e7c362fa86..c97573e96aba354afad870c822d91a0209159926 100644 --- a/inc/etl/String.hpp +++ b/inc/etl/String.hpp @@ -2,7 +2,7 @@ #define ECSS_SERVICES_ETL_STRING_HPP #include <cstddef> -#include <etl/cstring.h> +#include <etl/string.h> /** * A fixed-size string diff --git a/inc/etl_profile.h b/inc/etl_profile.h index 14e6e6b8ff6105ce7d593d65e404171340b27107..8e82b7b11de0ca22e8a63c2a65a4eba9b447e8b2 100644 --- a/inc/etl_profile.h +++ b/inc/etl_profile.h @@ -5,7 +5,6 @@ #ifndef ECSS_SERVICES_ETL_PROFILE_H #define ECSS_SERVICES_ETL_PROFILE_H -#define ETL_THROW_EXCEPTIONS #define ETL_VERBOSE_ERRORS #define ETL_CHECK_PUSH_POP diff --git a/inc/macros.hpp b/inc/macros.hpp index 12cce6c0072f87f7b2a1d9c368dcca2f10aa8da5..473104869ba4d4401d16b90c660d512b1c02155a 100644 --- a/inc/macros.hpp +++ b/inc/macros.hpp @@ -6,6 +6,7 @@ * * @todo Actually hold program execution or throw an exception here */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ASSERT_INTERNAL(cond, error) (ErrorHandler::assertInternal((cond), (error))) /** @@ -13,6 +14,7 @@ * * Only to be used within the Message class. */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ASSERT_REQUEST(cond, error) (ErrorHandler::assertRequest((cond), *this, (error))) #endif // ECSS_SERVICES_MACROS_HPP diff --git a/lib/Catch2 b/lib/Catch2 index d63307279412de3870cf97cc6802bae8ab36089e..605a34765aa5d5ecbf476b4598a862ada971b0cc 160000 --- a/lib/Catch2 +++ b/lib/Catch2 @@ -1 +1 @@ -Subproject commit d63307279412de3870cf97cc6802bae8ab36089e +Subproject commit 605a34765aa5d5ecbf476b4598a862ada971b0cc diff --git a/lib/etl b/lib/etl index 5a7e181100dae73659db133783ae964c7661437b..fce2b5ca8d2a139e0aefb450773b82e2cdfaa55f 160000 --- a/lib/etl +++ b/lib/etl @@ -1 +1 @@ -Subproject commit 5a7e181100dae73659db133783ae964c7661437b +Subproject commit fce2b5ca8d2a139e0aefb450773b82e2cdfaa55f diff --git a/lib/logger b/lib/logger index 4de567283a883fa4d93f454b6979cd4c90c5382b..c71e518061bc3fff6287800d709b40c9e8312b97 160000 --- a/lib/logger +++ b/lib/logger @@ -1 +1 @@ -Subproject commit 4de567283a883fa4d93f454b6979cd4c90c5382b +Subproject commit c71e518061bc3fff6287800d709b40c9e8312b97 diff --git a/src/Helpers/AllMessageTypes.cpp b/src/Helpers/AllMessageTypes.cpp index c67e09b31d3ebf98fd7a477d051b23beb7949a62..0ee958f3081ca105e4af345ccad70ff2cfa326b3 100644 --- a/src/Helpers/AllMessageTypes.cpp +++ b/src/Helpers/AllMessageTypes.cpp @@ -12,17 +12,17 @@ #include "Services/TimeBasedSchedulingService.hpp" namespace AllMessageTypes { - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st01Messages = {RequestVerificationService::MessageType::FailedAcceptanceReport, - RequestVerificationService::MessageType::FailedCompletionOfExecution, - RequestVerificationService::MessageType::FailedProgressOfExecution, - RequestVerificationService::MessageType::FailedRoutingReport, - RequestVerificationService::MessageType::FailedStartOfExecution, - RequestVerificationService::MessageType::SuccessfulAcceptanceReport, - RequestVerificationService::MessageType::SuccessfulCompletionOfExecution, - RequestVerificationService::MessageType::SuccessfulProgressOfExecution, - RequestVerificationService::MessageType::SuccessfulStartOfExecution}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST01Messages = {RequestVerificationService::MessageType::FailedAcceptanceReport, + RequestVerificationService::MessageType::FailedCompletionOfExecution, + RequestVerificationService::MessageType::FailedProgressOfExecution, + RequestVerificationService::MessageType::FailedRoutingReport, + RequestVerificationService::MessageType::FailedStartOfExecution, + RequestVerificationService::MessageType::SuccessfulAcceptanceReport, + RequestVerificationService::MessageType::SuccessfulCompletionOfExecution, + RequestVerificationService::MessageType::SuccessfulProgressOfExecution, + RequestVerificationService::MessageType::SuccessfulStartOfExecution}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st03Messages = { + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST03Messages = { HousekeepingService::MessageType::DisablePeriodicHousekeepingParametersReport, HousekeepingService::MessageType::EnablePeriodicHousekeepingParametersReport, HousekeepingService::MessageType::GenerateOneShotHousekeepingReport, @@ -30,43 +30,43 @@ namespace AllMessageTypes { HousekeepingService::MessageType::HousekeepingPeriodicPropertiesReport, HousekeepingService::MessageType::HousekeepingStructuresReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st04Messages = { + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST04Messages = { ParameterStatisticsService::MessageType::ParameterStatisticsDefinitionsReport, ParameterStatisticsService::MessageType::ParameterStatisticsReport, }; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st05Messages = {EventReportService::MessageType::HighSeverityAnomalyReport, - EventReportService::MessageType::DisabledListEventReport, - EventReportService::MessageType::InformativeEventReport, - EventReportService::MessageType::LowSeverityAnomalyReport, - EventReportService::MessageType::MediumSeverityAnomalyReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST05Messages = {EventReportService::MessageType::HighSeverityAnomalyReport, + EventReportService::MessageType::DisabledListEventReport, + EventReportService::MessageType::InformativeEventReport, + EventReportService::MessageType::LowSeverityAnomalyReport, + EventReportService::MessageType::MediumSeverityAnomalyReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st06Messages = {MemoryManagementService::MessageType::CheckRawMemoryDataReport, - MemoryManagementService::MessageType::DumpRawMemoryDataReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST06Messages = {MemoryManagementService::MessageType::CheckRawMemoryDataReport, + MemoryManagementService::MessageType::DumpRawMemoryDataReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st11Messages = {TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST11Messages = {TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st13Messages = {LargePacketTransferService::MessageType::FirstDownlinkPartReport, - LargePacketTransferService::MessageType::InternalDownlinkPartReport, - LargePacketTransferService::MessageType::LastDownlinkPartReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST13Messages = {LargePacketTransferService::MessageType::FirstDownlinkPartReport, + LargePacketTransferService::MessageType::InternalDownlinkPartReport, + LargePacketTransferService::MessageType::LastDownlinkPartReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st17Messages = {TestService::MessageType::AreYouAliveTestReport, - TestService::MessageType::OnBoardConnectionTestReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST17Messages = {TestService::MessageType::AreYouAliveTestReport, + TestService::MessageType::OnBoardConnectionTestReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st19Messages = {EventActionService::MessageType::EventActionStatusReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST19Messages = {EventActionService::MessageType::EventActionStatusReport}; - etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> st20Messages = {ParameterService::MessageType::ParameterValuesReport}; + const etl::vector<uint8_t, ECSSMaxReportTypeDefinitions> ST20Messages = {ParameterService::MessageType::ParameterValuesReport}; - etl::map<uint8_t, etl::vector<uint8_t, ECSSMaxReportTypeDefinitions>, ECSSMaxServiceTypeDefinitions> messagesOfService = { - {RequestVerificationService::ServiceType, st01Messages}, - {HousekeepingService::ServiceType, st03Messages}, - {ParameterStatisticsService::ServiceType, st04Messages}, - {EventReportService::ServiceType, st05Messages}, - {MemoryManagementService::ServiceType, st06Messages}, - {TimeBasedSchedulingService::ServiceType, st11Messages}, - {LargePacketTransferService::ServiceType, st13Messages}, - {TestService::ServiceType, st17Messages}, - {EventActionService::ServiceType, st19Messages}, - {ParameterService::ServiceType, st20Messages}}; + const etl::map<uint8_t, etl::vector<uint8_t, ECSSMaxReportTypeDefinitions>, ECSSMaxServiceTypeDefinitions> MessagesOfService = { + {RequestVerificationService::ServiceType, ST01Messages}, + {HousekeepingService::ServiceType, ST03Messages}, + {ParameterStatisticsService::ServiceType, ST04Messages}, + {EventReportService::ServiceType, ST05Messages}, + {MemoryManagementService::ServiceType, ST06Messages}, + {TimeBasedSchedulingService::ServiceType, ST11Messages}, + {LargePacketTransferService::ServiceType, ST13Messages}, + {TestService::ServiceType, ST17Messages}, + {EventActionService::ServiceType, ST19Messages}, + {ParameterService::ServiceType, ST20Messages}}; } // namespace AllMessageTypes diff --git a/src/Helpers/PMONBase.cpp b/src/Helpers/PMONBase.cpp index 85f163fd24029d46f6dc3300b57a4dc79049d6e6..2e64cb3473b3ecd5a9fe705a74f9a1b97f7c113b 100644 --- a/src/Helpers/PMONBase.cpp +++ b/src/Helpers/PMONBase.cpp @@ -3,7 +3,5 @@ PMONBase::PMONBase(uint16_t monitoredParameterId, uint16_t repetitionNumber) - : monitoredParameter(monitoredParameter), monitoredParameterId(monitoredParameterId), - repetitionNumber(repetitionNumber) { - monitoredParameter = Services.parameterManagement.getParameter(monitoredParameterId)->get(); -} \ No newline at end of file + : monitoredParameter(Services.parameterManagement.getParameter(monitoredParameterId)->get()), monitoredParameterId(monitoredParameterId), + repetitionNumber(repetitionNumber) {} \ No newline at end of file diff --git a/src/Helpers/Statistic.cpp b/src/Helpers/Statistic.cpp index 70236a557d90496c08ac807b8d3783c38aca069c..024c586bcd6b10977ae91ee9d74e5f28543fe2cb 100644 --- a/src/Helpers/Statistic.cpp +++ b/src/Helpers/Statistic.cpp @@ -1,20 +1,14 @@ -#include <iostream> #include "Helpers/Statistic.hpp" +#include <cmath> void Statistic::updateStatistics(double value) { - /* - * TODO: - * if periodic, just calculate next time without the CUC - * function. - * */ - if (value > max) { max = value; - // TODO: maxTime = as_CUC_timestamp(); + timeOfMaxValue = TimeGetter::getCurrentTimeDefaultCUC(); } if (value < min) { min = value; - // TODO: minTime = as_CUC_timestamp(); + timeOfMinValue = TimeGetter::getCurrentTimeDefaultCUC(); } if (sampleCounter + 1 > 0) { mean = (mean * sampleCounter + value) / (sampleCounter + 1); @@ -23,14 +17,14 @@ void Statistic::updateStatistics(double value) { sampleCounter++; } -void Statistic::appendStatisticsToMessage(Message& report) { +void Statistic::appendStatisticsToMessage(Message& report) const { report.appendFloat(static_cast<float>(max)); - report.appendUint32(maxTime); + report.append(timeOfMaxValue); report.appendFloat(static_cast<float>(min)); - report.appendUint32(minTime); + report.append(timeOfMinValue); report.appendFloat(static_cast<float>(mean)); - if (SupportsStandardDeviation) { + if constexpr (SupportsStandardDeviation) { double standardDeviation = 0; if (sampleCounter == 0) { standardDeviation = 0; @@ -49,14 +43,15 @@ void Statistic::setSelfSamplingInterval(uint16_t samplingInterval) { void Statistic::resetStatistics() { max = -std::numeric_limits<double>::infinity(); min = std::numeric_limits<double>::infinity(); - maxTime = 0; - minTime = 0; + timeOfMaxValue = Time::DefaultCUC(0); + timeOfMinValue = Time::DefaultCUC(0); mean = 0; sumOfSquares = 0; sampleCounter = 0; } -bool Statistic::statisticsAreInitialized() { - return (sampleCounter == 0 and mean == 0 and sumOfSquares == 0 and maxTime == 0 and minTime == 0 and +bool Statistic::statisticsAreInitialized() const { + return (sampleCounter == 0 and mean == 0 and sumOfSquares == 0 and + timeOfMaxValue == Time::DefaultCUC(0) and timeOfMinValue == Time::DefaultCUC(0) and max == -std::numeric_limits<double>::infinity() and min == std::numeric_limits<double>::infinity()); } diff --git a/src/Message.cpp b/src/Message.cpp index 0ba4d75aaed85e3584d6b7e81369390cb13bd6a3..c5846604e3b495d27ad98b85670635a739525c0d 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -1,13 +1,17 @@ #include "Message.hpp" -#include "macros.hpp" -#include <cstring> #include <ErrorHandler.hpp> -#include <ServicePool.hpp> #include <MessageParser.hpp> +#include <cstring> +#include "ServicePool.hpp" +#include "macros.hpp" + -Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, uint16_t applicationId) +Message::Message(uint8_t serviceType, uint8_t messageType, PacketType packetType, uint16_t applicationId) : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(applicationId) {} +Message::Message(uint8_t serviceType, uint8_t messageType, PacketType packetType) + : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(ApplicationId) {} + void Message::appendBits(uint8_t numBits, uint16_t data) { // TODO: Add assertion that data does not contain 1s outside of numBits bits ASSERT_INTERNAL(numBits <= 16, ErrorHandler::TooManyBitsAppend); @@ -174,7 +178,7 @@ void Message::appendString(const etl::istring& string) { void Message::appendFixedString(const etl::istring& string) { ASSERT_INTERNAL((dataSize + string.max_size()) < ECSSMaxMessageSize, ErrorHandler::MessageTooLarge); std::copy(string.data(), string.data() + string.size(), data + dataSize); - (void)memset(data + dataSize + string.size(), 0, string.max_size() - string.size()); + (void) memset(data + dataSize + string.size(), 0, string.max_size() - string.size()); dataSize += string.max_size(); } diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp index 7ce06c23b64058b079a73eb0bf449462cd471b82..7ae9d4ac9f4322390013f4e1547990746d5736b7 100644 --- a/src/MessageParser.cpp +++ b/src/MessageParser.cpp @@ -86,7 +86,7 @@ void MessageParser::execute(Message& message) { } Message MessageParser::parse(uint8_t* data, uint32_t length) { - ASSERT_INTERNAL(length >= 6, ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(length >= CCSDSPrimaryHeaderSize, ErrorHandler::UnacceptablePacket); uint16_t packetHeaderIdentification = (data[0] << 8) | data[1]; uint16_t packetSequenceControl = (data[2] << 8) | data[3]; @@ -104,22 +104,22 @@ Message MessageParser::parse(uint8_t* data, uint32_t length) { ASSERT_INTERNAL(versionNumber == 0U, ErrorHandler::UnacceptablePacket); ASSERT_INTERNAL(secondaryHeaderFlag, ErrorHandler::UnacceptablePacket); ASSERT_INTERNAL(sequenceFlags == 0x3U, ErrorHandler::UnacceptablePacket); - ASSERT_INTERNAL(packetDataLength == (length - 6U), ErrorHandler::UnacceptablePacket); + ASSERT_INTERNAL(packetDataLength == (length - CCSDSPrimaryHeaderSize), ErrorHandler::UnacceptablePacket); Message message(0, 0, packetType, APID); message.packetSequenceCount = packetSequenceCount; if (packetType == Message::TC) { - parseECSSTCHeader(data + 6, packetDataLength, message); + parseECSSTCHeader(data + CCSDSPrimaryHeaderSize, packetDataLength, message); } else { - parseECSSTMHeader(data + 6, packetDataLength, message); + parseECSSTMHeader(data + CCSDSPrimaryHeaderSize, packetDataLength, message); } return message; } void MessageParser::parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message) { - ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage); + ErrorHandler::assertRequest(length >= ECSSSecondaryTCHeaderSize, message, ErrorHandler::UnacceptableMessage); // Individual fields of the TC header uint8_t pusVersion = data[0] >> 4; @@ -129,12 +129,12 @@ void MessageParser::parseECSSTCHeader(const uint8_t* data, uint16_t length, Mess ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage); // Remove the length of the header - length -= 5; + length -= ECSSSecondaryTCHeaderSize; // Copy the data to the message message.serviceType = serviceType; message.messageType = messageType; - std::copy(data + 5, data + 5 + length, message.data); + std::copy(data + ECSSSecondaryTCHeaderSize, data + ECSSSecondaryTCHeaderSize + length, message.data); message.dataSize = length; } @@ -154,23 +154,33 @@ Message MessageParser::parseECSSTC(uint8_t* data) { } String<CCSDSMaxMessageSize> MessageParser::composeECSS(const Message& message, uint16_t size) { - uint8_t header[5]; + // Unfortunately to avoid using VLAs, we will create an array with the maximum size. + uint8_t header[ECSSSecondaryTMHeaderSize]; if (message.packetType == Message::TC) { header[0] = ECSSPUSVersion << 4U; // Assign the pusVersion = 2 + header[0] |= 0x00; //ack flags header[1] = message.serviceType; header[2] = message.messageType; - header[3] = 0; - header[4] = 0; + header[3] = message.applicationId >> 8U; + header[4] = message.applicationId; } else { header[0] = ECSSPUSVersion << 4U; // Assign the pusVersion = 2 + header[0] |= 0x00; // Spacecraft time reference status 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); + header[5] = message.applicationId >> 8U; // DestinationID + header[6] = message.applicationId; + uint32_t ticks = TimeGetter::getCurrentTimeDefaultCUC().formatAsBytes(); + header[7] = (ticks >> 24) & 0xffU; + header[8] = (ticks >> 16) & 0xffU; + header[9] = (ticks >> 8) & 0xffU; + header[10] = (ticks) & 0xffU; } - String<CCSDSMaxMessageSize> dataString(header, 5); + String<CCSDSMaxMessageSize> dataString(header, ((message.packetType == Message::TM) ? ECSSSecondaryTMHeaderSize : ECSSSecondaryTCHeaderSize)); dataString.append(message.data, message.dataSize); // Make sure to reach the requested size @@ -190,20 +200,20 @@ String<CCSDSMaxMessageSize> MessageParser::composeECSS(const Message& message, u } String<CCSDSMaxMessageSize> MessageParser::compose(const Message& message) { - uint8_t header[6]; + uint8_t header[CCSDSPrimaryHeaderSize]; // First, compose the ECSS part String<CCSDSMaxMessageSize> ecssMessage = MessageParser::composeECSS(message); // Sanity check that there is enough space for the string - ASSERT_INTERNAL((ecssMessage.size() + 6U) <= CCSDSMaxMessageSize, ErrorHandler::StringTooLarge); + ASSERT_INTERNAL((ecssMessage.size() + CCSDSPrimaryHeaderSize) <= CCSDSMaxMessageSize, 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(); + uint16_t packetDataLength = ecssMessage.size() - 1; // Compile the header header[0] = packetId >> 8U; @@ -214,7 +224,7 @@ String<CCSDSMaxMessageSize> MessageParser::compose(const Message& message) { header[5] = packetDataLength & 0xffU; // Compile the final message by appending the header - String<CCSDSMaxMessageSize> ccsdsMessage(header, 6); + String<CCSDSMaxMessageSize> ccsdsMessage(header, CCSDSPrimaryHeaderSize); ccsdsMessage.append(ecssMessage); #if ECSS_CRC_INCLUDED @@ -228,7 +238,7 @@ String<CCSDSMaxMessageSize> MessageParser::compose(const Message& message) { } void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message) { - ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage); + ErrorHandler::assertRequest(length >= ECSSSecondaryTMHeaderSize, message, ErrorHandler::UnacceptableMessage); // Individual fields of the TM header uint8_t pusVersion = data[0] >> 4; @@ -238,11 +248,11 @@ void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Mess ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage); // Remove the length of the header - length -= 5; + length -= ECSSSecondaryTMHeaderSize; // Copy the data to the message message.serviceType = serviceType; message.messageType = messageType; - std::copy(data + 5, data + 5 + length, message.data); + std::copy(data + ECSSSecondaryTMHeaderSize, data + ECSSSecondaryTMHeaderSize + length, message.data); message.dataSize = length; } diff --git a/src/Platform/x86/ErrorHandler.cpp b/src/Platform/x86/ErrorHandler.cpp index ffddffd2d0501e688779674251ae1da83aa035d7..572d34246c301c3aa4f30c57ae48540fe4ac737f 100644 --- a/src/Platform/x86/ErrorHandler.cpp +++ b/src/Platform/x86/ErrorHandler.cpp @@ -8,6 +8,8 @@ #include <ErrorHandler.hpp> #include <Message.hpp> #include <Logger.hpp> +#include <type_traits> +#include "Helpers/EnumMagic.hpp" // TODO: Find a way to reduce the number of copies of this chunk template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType); @@ -26,7 +28,7 @@ void ErrorHandler::logError(const Message& message, ErrorType errorType) { */ << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << static_cast<uint16_t>(message.messageType) - << "]: " << errorType; + << "]: " << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")"; } template <typename ErrorType> @@ -36,5 +38,7 @@ void ErrorHandler::logError(ErrorType errorType) { * 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; + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) + << " Error: " + << enumName(errorType) << " (" << static_cast<std::underlying_type_t<ErrorType>>(errorType) << ")"; } diff --git a/src/Platform/x86/Service.cpp b/src/Platform/x86/Service.cpp index 60786fc650c32da32cb98860886291537e378fce..f316a764a1a679cf1126e8f3f0e1994592b8c8bc 100644 --- a/src/Platform/x86/Service.cpp +++ b/src/Platform/x86/Service.cpp @@ -1,7 +1,47 @@ -#include <iostream> -#include <iomanip> -#include <Logger.hpp> #include "Service.hpp" +#include <Logger.hpp> +#include <MessageParser.hpp> +#include <arpa/inet.h> +#include <iomanip> +#include <iostream> +#include <netinet/in.h> +#include <string> +#include <sys/socket.h> +#include <unistd.h> + +class PacketSender { +private: + const char* hostname = "127.0.0.1"; + const uint16_t port = 10015; + sockaddr_in destination; + int socket; + +public: + PacketSender() { + socket = ::socket(AF_INET, SOCK_DGRAM, 0); + destination.sin_family = AF_INET; + destination.sin_port = htons(port); + destination.sin_addr.s_addr = inet_addr(hostname); + }; + + ~PacketSender() { + ::close(socket); + }; + + void sendPacketToYamcs(Message& message) { + // Add ECSS and CCSDS header + String<CCSDSMaxMessageSize> createdPacket = MessageParser::compose(message); + auto bytesSent = ::sendto(socket, createdPacket.c_str(), createdPacket.length(), 0, reinterpret_cast<sockaddr*>(&destination), sizeof(destination)); + LOG_DEBUG << bytesSent << " bytes sent"; + } +}; + +PacketSender packetSender; + +/** + * If set to true, the created messages will be sent to port 10025 on localhost for testing purposes. + */ +inline const bool SendToYamcs = true; void Service::storeMessage(Message& message) { // appends the remaining bits to complete a byte @@ -14,12 +54,17 @@ void Service::storeMessage(Message& message) { ss << "New " << ((message.packetType == Message::TM) ? "TM" : "TC") << "[" << std::hex << static_cast<int>(message.serviceType) << "," // Ignore-MISRA - << static_cast<int>(message.messageType) // 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 } + + // Send data to YAMCS port + if constexpr (SendToYamcs) { + packetSender.sendPacketToYamcs(message); + } LOG_DEBUG << ss.str(); } diff --git a/src/Platform/x86/Services/ParameterService.cpp b/src/Platform/x86/Services/ParameterService.cpp index da7b4c439e937b854db079a4efd93bf972153c0f..268cfd279b2d9d773f34965643d9e30d30b63c70 100644 --- a/src/Platform/x86/Services/ParameterService.cpp +++ b/src/Platform/x86/Services/ParameterService.cpp @@ -1,8 +1,8 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_PARAMETER -#include "Services/ParameterService.hpp" #include "Parameters/PlatformParameters.hpp" +#include "Services/ParameterService.hpp" void ParameterService::initializeParameterMap() { parameters = {{static_cast<uint16_t>(0), PlatformParameters::parameter1}, @@ -10,4 +10,4 @@ void ParameterService::initializeParameterMap() { {static_cast<uint16_t>(2), PlatformParameters::parameter3}}; } -#endif \ No newline at end of file +#endif diff --git a/src/Platform/x86/Services/ParameterStatisticsService.cpp b/src/Platform/x86/Services/ParameterStatisticsService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac6e9aa2e093bc9e3a72a26b89b8818cfabec395 --- /dev/null +++ b/src/Platform/x86/Services/ParameterStatisticsService.cpp @@ -0,0 +1,10 @@ +#include "Services/ParameterStatisticsService.hpp" +#include "ECSS_Configuration.hpp" + +#ifdef SERVICE_PARAMETERSTATISTICS + +void ParameterStatisticsService::initializeStatisticsMap() { + statisticsMap = {}; +} + +#endif diff --git a/src/Platform/x86/Services/TimeBasedSchedulingService.cpp b/src/Platform/x86/Services/TimeBasedSchedulingService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86afc2bd2d8e0467d8cf9048821c285cab7b5284 --- /dev/null +++ b/src/Platform/x86/Services/TimeBasedSchedulingService.cpp @@ -0,0 +1,8 @@ +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_TIMESCHEDULING + +#include "Services/TimeBasedSchedulingService.hpp" + +void TimeBasedSchedulingService::notifyNewActivityAddition() {} + +#endif \ No newline at end of file diff --git a/src/Platform/x86/TimeGetter.cpp b/src/Platform/x86/TimeGetter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..acff1688c887152d17d5b09fa621fd86c807be62 --- /dev/null +++ b/src/Platform/x86/TimeGetter.cpp @@ -0,0 +1,15 @@ +#include "Helpers/TimeGetter.hpp" + +UTCTimestamp TimeGetter::getCurrentTimeUTC() { + time_t timeInSeconds = static_cast<time_t>(time(nullptr)); + tm* UTCTimeStruct = gmtime(&timeInSeconds); + UTCTimestamp currentTime(UTCTimeStruct->tm_year, UTCTimeStruct->tm_mon, + UTCTimeStruct->tm_mday, UTCTimeStruct->tm_hour, + UTCTimeStruct->tm_min, UTCTimeStruct->tm_sec); + return currentTime; +} + +Time::DefaultCUC TimeGetter::getCurrentTimeDefaultCUC() { + UTCTimestamp timeUTC = getCurrentTimeUTC(); + return Time::DefaultCUC(timeUTC); +} diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp index 016647fda0cf1e2142123300802652a6b2899222..a2e0dc89c82dfa9c1bdbe4ba6968d9b9d6f6efd1 100644 --- a/src/Platform/x86/main.cpp +++ b/src/Platform/x86/main.cpp @@ -1,25 +1,25 @@ -#include <iostream> #include <Logger.hpp> -#include <Time/UTCTimestamp.hpp> #include <Platform/x86/Helpers/UTCTimestamp.hpp> +#include <Time/UTCTimestamp.hpp> +#include <ctime> +#include <iostream> +#include "ErrorHandler.hpp" #include "Helpers/CRCHelper.hpp" -#include "Services/TestService.hpp" -#include "Services/ParameterService.hpp" -#include "Services/RequestVerificationService.hpp" -#include "Services/MemoryManagementService.hpp" +#include "Helpers/Statistic.hpp" +#include "Message.hpp" +#include "MessageParser.hpp" +#include "ServicePool.hpp" +#include "Services/EventActionService.hpp" #include "Services/EventReportService.hpp" #include "Services/FunctionManagementService.hpp" -#include "Services/EventActionService.hpp" #include "Services/LargePacketTransferService.hpp" -#include "Services/TimeBasedSchedulingService.hpp" +#include "Services/MemoryManagementService.hpp" +#include "Services/ParameterService.hpp" #include "Services/ParameterStatisticsService.hpp" -#include "Helpers/Statistic.hpp" -#include "Message.hpp" -#include "MessageParser.hpp" -#include "ErrorHandler.hpp" +#include "Services/RequestVerificationService.hpp" +#include "Services/TestService.hpp" +#include "Services/TimeBasedSchedulingService.hpp" #include "etl/String.hpp" -#include "ServicePool.hpp" -#include <ctime> int main() { LOG_NOTICE << "ECSS Services test application"; @@ -55,19 +55,19 @@ int main() { // Test code for reportParameter Message sentPacket = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); // application id is a dummy number (1) - sentPacket.appendUint16(2); // number of contained IDs - sentPacket.appendUint16(0); // first ID - sentPacket.appendUint16(1); // second ID + sentPacket.appendUint16(2); // number of contained IDs + sentPacket.appendUint16(0); // first ID + sentPacket.appendUint16(1); // second ID paramService.reportParameters(sentPacket); // Test code for setParameter Message sentPacket2 = Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); // application id is a dummy number (1) - sentPacket2.appendUint16(2); // number of contained IDs - sentPacket2.appendUint16(0); // first parameter ID - sentPacket2.appendUint32(63238); // settings for first parameter - sentPacket2.appendUint16(1); // 2nd parameter ID - sentPacket2.appendUint32(45823); // settings for 2nd parameter + sentPacket2.appendUint16(2); // number of contained IDs + sentPacket2.appendUint16(0); // first parameter ID + sentPacket2.appendUint32(63238); // settings for first parameter + sentPacket2.appendUint16(1); // 2nd parameter ID + sentPacket2.appendUint32(45823); // settings for 2nd parameter paramService.setParameters(sentPacket2); paramService.reportParameters(sentPacket); @@ -84,9 +84,9 @@ int main() { Message rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::DumpRawMemoryData, Message::TC, 1); rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - rcvPack.appendUint16(3); // Iteration count - rcvPack.appendUint64(reinterpret_cast<uint64_t>(string)); // Start address - rcvPack.appendUint16(sizeof(string) / sizeof(string[0])); // Data read length + rcvPack.appendUint16(3); // Iteration count + rcvPack.appendUint64(reinterpret_cast<uint64_t>(string)); // Start address + rcvPack.appendUint16(sizeof(string) / sizeof(string[0])); // Data read length rcvPack.appendUint64(reinterpret_cast<uint64_t>(anotherStr)); rcvPack.appendUint16(sizeof(anotherStr) / sizeof(anotherStr[0])); @@ -100,21 +100,21 @@ int main() { uint8_t data[2] = {'h', 'R'}; rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - rcvPack.appendUint16(2); // Iteration count - rcvPack.appendUint64(reinterpret_cast<uint64_t>(pStr)); // Start address + rcvPack.appendUint16(2); // Iteration count + rcvPack.appendUint64(reinterpret_cast<uint64_t>(pStr)); // Start address rcvPack.appendOctetString(String<2>(data, 2)); - rcvPack.appendBits(16, CRCHelper::calculateCRC(data, 2)); // Append the CRC value + rcvPack.appendBits(16, CRCHelper::calculateCRC(data, 2)); // Append the CRC value rcvPack.appendUint64(reinterpret_cast<uint64_t>(pStr + 1)); // Start address rcvPack.appendOctetString(String<1>(data, 1)); rcvPack.appendBits(16, CRCHelper::calculateCRC(data, 1)); // Append the CRC value - memMangService.rawDataMemorySubservice.loadRawData(rcvPack); + memMangService.loadRawData(rcvPack); rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::CheckRawMemoryData, Message::TC, 1); rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - rcvPack.appendUint16(2); // Iteration count - rcvPack.appendUint64(reinterpret_cast<uint64_t>(data)); // Start address + rcvPack.appendUint16(2); // Iteration count + rcvPack.appendUint64(reinterpret_cast<uint64_t>(data)); // Start address rcvPack.appendUint16(2); rcvPack.appendUint64(reinterpret_cast<uint64_t>(data + 1)); // Start address rcvPack.appendUint16(1); @@ -245,7 +245,7 @@ int main() { eventActionService.addEventActionDefinitions(eventActionDefinition7); std::cout << "Status should be 000:"; - for (auto& element : eventActionService.eventActionDefinitionMap) { + for (auto& element: eventActionService.eventActionDefinitionMap) { std::cout << element.second.enabled; } @@ -264,7 +264,7 @@ int main() { eventActionService.enableEventActionDefinitions(eventActionDefinition5); std::cout << "\nStatus should be 111:"; - for (auto& element : eventActionService.eventActionDefinitionMap) { + for (auto& element: eventActionService.eventActionDefinitionMap) { std::cout << element.second.enabled; } @@ -282,7 +282,7 @@ int main() { eventActionDefinition3.appendUint16(4); eventActionService.disableEventActionDefinitions(eventActionDefinition3); std::cout << "Status should be 000:"; - for (auto& element : eventActionService.eventActionDefinitionMap) { + for (auto& element: eventActionService.eventActionDefinitionMap) { std::cout << element.second.enabled; } @@ -327,7 +327,7 @@ int main() { TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction, Message::TC, 1); Message testMessage1(6, 5, Message::TC, 1); Message testMessage2(4, 5, Message::TC, 1); - testMessage1.appendUint16(4253); // Append dummy data + testMessage1.appendUint16(4253); // Append dummy data testMessage2.appendUint16(45667); // Append dummy data timeBasedSchedulingService.enableScheduleExecution(receivedMsg); // Enable the schedule @@ -363,6 +363,8 @@ int main() { LOG_NOTICE << "ECSS Services test complete"; + ErrorHandler::reportInternalError(static_cast<ErrorHandler::InternalErrorType>(254)); + std::cout << UTCTimestamp() << std::endl; return 0; } diff --git a/src/ServicePool.cpp b/src/ServicePool.cpp index b8e7c963ca5af536baf4f0b52456a9432452bbb7..e36989475be54ea9444ef2959fc5deec6aa6942d 100644 --- a/src/ServicePool.cpp +++ b/src/ServicePool.cpp @@ -1,6 +1,6 @@ #include "ServicePool.hpp" -ServicePool Services = ServicePool(); +ServicePool Services = ServicePool(); // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) void ServicePool::reset() { // Call the destructor @@ -15,7 +15,7 @@ void ServicePool::reset() { 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 + return (messageTypeCounter[key])++; // Fetch and increase the value } uint16_t ServicePool::getAndUpdatePacketSequenceCounter() { diff --git a/src/Services/EventActionService.cpp b/src/Services/EventActionService.cpp old mode 100644 new mode 100755 index 0963cf942b8a2dbd57a0a60fd972be37c78793a1..6fb59212dc078db140fbcc02d0aa9c891329ceb5 --- a/src/Services/EventActionService.cpp +++ b/src/Services/EventActionService.cpp @@ -1,191 +1,187 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_EVENTACTION -#include "Services/EventActionService.hpp" #include "Message.hpp" #include "MessageParser.hpp" +#include "Services/EventActionService.hpp" +EventActionService::EventActionDefinition::EventActionDefinition(uint16_t applicationID, uint16_t eventDefinitionID, Message& message) + : applicationID(applicationID), eventDefinitionID(eventDefinitionID), request(message.data + message.readPosition) { + message.readPosition += ECSSTCRequestStringSize; +} void EventActionService::addEventActionDefinitions(Message& message) { - // TC[19,1] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction); - uint16_t applicationID = message.readEnum16(); - uint16_t eventDefinitionID = message.readEnum16(); - uint16_t eventActionDefinitionID = message.readEnum16(); - bool canBeAdded = true; - if (eventActionDefinitionMap.find(eventDefinitionID) != eventActionDefinitionMap.end()) { - auto range = eventActionDefinitionMap.equal_range(eventDefinitionID); - for (auto& element = range.first; element != range.second; ++element) { - if (element->second.eventActionDefinitionID == eventActionDefinitionID) { - canBeAdded = false; - ErrorHandler::reportError(message, ErrorHandler::EventActionDefinitionIDExistsError); + if (!message.assertTC(ServiceType, MessageType::AddEventAction)) { + return; + } + uint8_t numberOfEventActionDefinitions = message.readUint8(); + while (numberOfEventActionDefinitions-- != 0) { + uint16_t applicationID = message.readEnum16(); + uint16_t eventDefinitionID = message.readEnum16(); + bool canBeAdded = true; + + for (auto& element: eventActionDefinitionMap) { + if (element.first == eventDefinitionID) { + if (element.second.enabled) { + canBeAdded = false; + ErrorHandler::reportError(message, ErrorHandler::EventActionEnabledError); + } else { + eventActionDefinitionMap.erase(eventDefinitionID); + } + break; } } - } - - if ((message.dataSize - 6) > ECSSTCRequestStringSize) { - canBeAdded = false; - ErrorHandler::reportInternalError(ErrorHandler::MessageTooLarge); - } - if (canBeAdded) { - char data[ECSSTCRequestStringSize] = { 0 }; - message.readString(data, message.dataSize - 6); - EventActionDefinition temp; - temp.enabled = false; - temp.applicationId = applicationID; - temp.eventDefinitionID = eventDefinitionID; - temp.eventActionDefinitionID = eventActionDefinitionID; - temp.request = String<ECSSTCRequestStringSize>(data); - if (eventActionDefinitionMap.size() == ECSSEventActionStructMapSize) { - ErrorHandler::reportError(message,ErrorHandler::EventActionDefinitionsMapIsFull); - } else { - eventActionDefinitionMap.insert(std::make_pair(eventDefinitionID, temp)); + if (canBeAdded) { + if (eventActionDefinitionMap.size() == ECSSEventActionStructMapSize) { + ErrorHandler::reportError(message, ErrorHandler::EventActionDefinitionsMapIsFull); + continue; + } + EventActionDefinition temporaryEventActionDefinition(applicationID, eventDefinitionID, message); + eventActionDefinitionMap.insert(std::make_pair(eventDefinitionID, temporaryEventActionDefinition)); } } } void EventActionService::deleteEventActionDefinitions(Message& message) { - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction); - uint16_t numberOfEventActionDefinitions = message.readUint16(); - bool definitionIDexists = false; - for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) { - message.skipBytes(2); + if (!message.assertTC(ServiceType, MessageType::DeleteEventAction)) { + return; + } + uint8_t numberOfEventActionDefinitions = message.readUint8(); + while (numberOfEventActionDefinitions-- != 0) { + uint16_t applicationID = message.readEnum16(); uint16_t eventDefinitionID = message.readEnum16(); - uint16_t eventActionDefinitionID = message.readEnum16(); - if (eventActionDefinitionMap.find(eventDefinitionID) != eventActionDefinitionMap.end()) { - auto range = eventActionDefinitionMap.equal_range(eventDefinitionID); - for (auto& element = range.first; element != range.second; ++element) { - if (element->second.eventActionDefinitionID == eventActionDefinitionID) { - definitionIDexists = true; - if (element->second.enabled) { - ErrorHandler::reportError(message, ErrorHandler::EventActionDeleteEnabledDefinitionError); - } else { - eventActionDefinitionMap.erase(element); - } + bool actionDefinitionExists = false; + + for (auto& element: eventActionDefinitionMap) { + if (element.first == eventDefinitionID) { + actionDefinitionExists = true; + if (element.second.applicationID != applicationID) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); + } else if (element.second.enabled) { + ErrorHandler::reportError(message, ErrorHandler::EventActionDeleteEnabledDefinitionError); + } else { + eventActionDefinitionMap.erase(eventActionDefinitionMap.find(eventDefinitionID)); } + break; } - if (not definitionIDexists) { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionIDError); - } - } else { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventDefinitionError); + } + if (not actionDefinitionExists) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); } } } void EventActionService::deleteAllEventActionDefinitions(Message& message) { - // TC[19,3] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::DeleteAllEventAction); - + if (!message.assertTC(ServiceType, MessageType::DeleteAllEventAction)) { + return; + } setEventActionFunctionStatus(false); eventActionDefinitionMap.clear(); } void EventActionService::enableEventActionDefinitions(Message& message) { - // TC[19,4] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction); - uint16_t numberOfEventActionDefinitions = message.readUint16(); + if (!message.assertTC(ServiceType, MessageType::EnableEventAction)) { + return; + } + uint8_t numberOfEventActionDefinitions = message.readUint8(); if (numberOfEventActionDefinitions != 0U) { - for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) { - message.skipBytes(2); // Skips reading the application ID + while (numberOfEventActionDefinitions-- != 0) { + uint16_t applicationID = message.readEnum16(); uint16_t eventDefinitionID = message.readEnum16(); - uint16_t eventActionDefinitionID = message.readEnum16(); - if (eventActionDefinitionMap.find(eventDefinitionID) != eventActionDefinitionMap.end()) { - bool definitionIDexists = false; - auto range = eventActionDefinitionMap.equal_range(eventDefinitionID); - for (auto& element = range.first; element != range.second; ++element) { - if (element->second.eventActionDefinitionID == eventActionDefinitionID) { - element->second.enabled = true; - definitionIDexists = true; - break; + bool actionDefinitionExists = false; + + for (auto& element: eventActionDefinitionMap) { + if (element.first == eventDefinitionID) { + actionDefinitionExists = true; + if (element.second.applicationID != applicationID) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); + } else { + element.second.enabled = true; } + break; } - if (not definitionIDexists) { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionIDError); - } - } else { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventDefinitionError); + } + if (not actionDefinitionExists) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); } } } else { - for (auto& element : eventActionDefinitionMap) { + for (auto& element: eventActionDefinitionMap) { element.second.enabled = true; } } } void EventActionService::disableEventActionDefinitions(Message& message) { - // TC[19,5] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction); - uint16_t numberOfEventActionDefinitions = message.readUint16(); + if (!message.assertTC(ServiceType, MessageType::DisableEventAction)) { + return; + } + uint8_t numberOfEventActionDefinitions = message.readUint8(); if (numberOfEventActionDefinitions != 0U) { - for (uint16_t i = 0; i < numberOfEventActionDefinitions; i++) { - message.skipBytes(2); // Skips reading applicationID + while (numberOfEventActionDefinitions-- != 0) { + uint16_t applicationID = message.readEnum16(); uint16_t eventDefinitionID = message.readEnum16(); - uint16_t eventActionDefinitionID = message.readEnum16(); - if (eventActionDefinitionMap.find(eventDefinitionID) != eventActionDefinitionMap.end()) { - bool definitionIDexists = false; - auto range = eventActionDefinitionMap.equal_range(eventDefinitionID); - for (auto& element = range.first; element != range.second; ++element) { - if (element->second.eventActionDefinitionID == eventActionDefinitionID) { - element->second.enabled = false; - definitionIDexists = true; + bool actionDefinitionExists = false; + + for (auto& element: eventActionDefinitionMap) { + if (element.first == eventDefinitionID) { + actionDefinitionExists = true; + if (element.second.applicationID != applicationID) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); + } else { + element.second.enabled = false; } + break; } - if (not definitionIDexists) { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionIDError); - } - } else { - ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventDefinitionError); + } + if (not actionDefinitionExists) { + ErrorHandler::reportError(message, ErrorHandler::EventActionUnknownEventActionDefinitionError); } } } else { - for (auto& element : eventActionDefinitionMap) { + for (auto& element: eventActionDefinitionMap) { element.second.enabled = false; } } } void EventActionService::requestEventActionDefinitionStatus(Message& message) { - // TC[19,6] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::ReportStatusOfEachEventAction); - + if (!message.assertTC(ServiceType, MessageType::ReportStatusOfEachEventAction)) { + return; + } eventActionStatusReport(); } void EventActionService::eventActionStatusReport() { - // TM[19,7] Message report = createTM(EventActionStatusReport); uint16_t count = eventActionDefinitionMap.size(); report.appendUint16(count); - for (const auto& element : eventActionDefinitionMap) { - report.appendEnum16(element.second.applicationId); + for (const auto& element: eventActionDefinitionMap) { + report.appendEnum16(element.second.applicationID); report.appendEnum16(element.second.eventDefinitionID); - report.appendEnum16(element.second.eventActionDefinitionID); report.appendBoolean(element.second.enabled); } storeMessage(report); } void EventActionService::enableEventActionFunction(Message& message) { - // TC[19,8] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::EnableEventActionFunction); - + if (!message.assertTC(ServiceType, MessageType::EnableEventActionFunction)) { + return; + } setEventActionFunctionStatus(true); } void EventActionService::disableEventActionFunction(Message& message) { - // TC[19,9] - message.assertTC(EventActionService::ServiceType, EventActionService::MessageType::DisableEventActionFunction); - + if (!message.assertTC(ServiceType, MessageType::DisableEventActionFunction)) { + return; + } setEventActionFunctionStatus(false); } -// TODO: Should I use applicationID too? -void EventActionService::executeAction(uint16_t eventID) { +void EventActionService::executeAction(uint16_t eventDefinitionID) { // Custom function if (eventActionFunctionStatus) { - auto range = eventActionDefinitionMap.equal_range(eventID); + auto range = eventActionDefinitionMap.equal_range(eventDefinitionID); for (auto& element = range.first; element != range.second; ++element) { if (element->second.enabled) { Message message = MessageParser::parseECSSTC(element->second.request); @@ -215,7 +211,7 @@ void EventActionService::execute(Message& message) { case ReportStatusOfEachEventAction: requestEventActionDefinitionStatus(message); break; - case EnableEventActionFunction : + case EnableEventActionFunction: enableEventActionFunction(message); break; case DisableEventActionFunction: diff --git a/src/Services/EventReportService.cpp b/src/Services/EventReportService.cpp index 53dc18fb4d314d9a728d3f675a39cedd0253d7e0..5f2b23a4080ab397f0d0db21cb1b4daaed6a3979 100644 --- a/src/Services/EventReportService.cpp +++ b/src/Services/EventReportService.cpp @@ -1,8 +1,8 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_EVENTREPORT -#include <Services/EventReportService.hpp> #include <Services/EventActionService.hpp> +#include <Services/EventReportService.hpp> #include "Message.hpp" /** @@ -22,8 +22,7 @@ void EventReportService::informativeEventReport(Event eventID, const String<ECSS } } -void -EventReportService::lowSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { +void EventReportService::lowSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { lowSeverityEventCount++; // TM[5,2] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -39,8 +38,7 @@ EventReportService::lowSeverityAnomalyReport(Event eventID, const String<ECSSEve } } -void -EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { +void EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { mediumSeverityEventCount++; // TM[5,3] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -56,8 +54,7 @@ EventReportService::mediumSeverityAnomalyReport(Event eventID, const String<ECSS } } -void -EventReportService::highSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { +void EventReportService::highSeverityAnomalyReport(Event eventID, const String<ECSSEventDataAuxiliaryMaxSize>& data) { highSeverityEventCount++; // TM[5,4] if (stateOfEvents[static_cast<uint16_t>(eventID)]) { @@ -75,19 +72,17 @@ EventReportService::highSeverityAnomalyReport(Event eventID, const String<ECSSEv void EventReportService::enableReportGeneration(Message message) { // TC[5,5] - message.assertTC(EventReportService::ServiceType, EventReportService::MessageType::EnableReportGenerationOfEvents); + if (!message.assertTC(ServiceType, MessageType::EnableReportGenerationOfEvents)) { + return; + } /** * @todo: Report an error if length > numberOfEvents */ uint16_t length = message.readUint16(); - Event eventID[length]; - for (uint16_t i = 0; i < length; i++) { - eventID[i] = static_cast<Event>(message.readEnum16()); - } if (length <= numberOfEvents) { for (uint16_t i = 0; i < length; i++) { - stateOfEvents[static_cast<uint16_t>(eventID[i])] = true; + stateOfEvents[message.readEnum16()] = true; } } disabledEventsCount = stateOfEvents.size() - stateOfEvents.count(); @@ -95,19 +90,17 @@ void EventReportService::enableReportGeneration(Message message) { void EventReportService::disableReportGeneration(Message message) { // TC[5,6] - message.assertTC(EventReportService::ServiceType, EventReportService::MessageType::DisableReportGenerationOfEvents); + if (!message.assertTC(ServiceType, MessageType::DisableReportGenerationOfEvents)) { + return; + } /** * @todo: Report an error if length > numberOfEvents */ uint16_t length = message.readUint16(); - Event eventID[length]; - for (uint16_t i = 0; i < length; i++) { - eventID[i] = static_cast<Event>(message.readEnum16()); - } if (length <= numberOfEvents) { for (uint16_t i = 0; i < length; i++) { - stateOfEvents[static_cast<uint16_t>(eventID[i])] = false; + stateOfEvents[message.readEnum16()] = false; } } disabledEventsCount = stateOfEvents.size() - stateOfEvents.count(); @@ -115,7 +108,9 @@ void EventReportService::disableReportGeneration(Message message) { void EventReportService::requestListOfDisabledEvents(Message message) { // TC[5,7] - message.assertTC(EventReportService::ServiceType, EventReportService::MessageType::ReportListOfDisabledEvents); + if (!message.assertTC(ServiceType, MessageType::ReportListOfDisabledEvents)) { + return; + } listOfDisabledEventsReport(); } diff --git a/src/Services/FunctionManagementService.cpp b/src/Services/FunctionManagementService.cpp index 15361ff688031e211c11b637af0b229804760345..e299d0a5b82b654b1cf45f0d231b24e056d12b02 100644 --- a/src/Services/FunctionManagementService.cpp +++ b/src/Services/FunctionManagementService.cpp @@ -5,15 +5,13 @@ void FunctionManagementService::call(Message& msg) { msg.resetRead(); - ErrorHandler::assertRequest(msg.packetType == Message::TC, msg, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(msg.messageType == FunctionManagementService::MessageType::PerformFunction, msg, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(msg.serviceType == FunctionManagementService::ServiceType, msg, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - uint8_t funcName[ECSSFunctionNameLength] = { 0 }; // the function's name - uint8_t funcArgs[ECSSFunctionMaxArgLength] = { 0 }; // arguments for the function + if (!msg.assertTC(ServiceType, MessageType::PerformFunction)) { + return; + } + + uint8_t funcName[ECSSFunctionNameLength] = {0}; // the function's name + uint8_t funcArgs[ECSSFunctionMaxArgLength] = {0}; // arguments for the function msg.readString(funcName, ECSSFunctionNameLength); msg.readString(funcArgs, ECSSFunctionMaxArgLength); @@ -28,21 +26,20 @@ void FunctionManagementService::call(Message& msg) { // locate the appropriate function pointer String<ECSSFunctionNameLength> name(funcName); FunctionMap::iterator iter = funcPtrIndex.find(name); - void (*selected)(String<ECSSFunctionMaxArgLength>); - if (iter != funcPtrIndex.end()) { - selected = *iter->second; - } else { + if (iter == funcPtrIndex.end()) { ErrorHandler::reportError(msg, ErrorHandler::ExecutionStartErrorType::UnknownExecutionStartError); return; } + auto selected = *iter->second; + // execute the function if there are no obvious flaws (defined in the standard, pg.158) selected(funcArgs); } void FunctionManagementService::include(String<ECSSFunctionNameLength> funcName, - void (* ptr)(String<ECSSFunctionMaxArgLength>)) { + void (*ptr)(String<ECSSFunctionMaxArgLength>)) { 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(ECSSFunctionNameLength - funcName.length(), 0); diff --git a/src/Services/HousekeepingService.cpp b/src/Services/HousekeepingService.cpp index 35beaa2ab3163197ebd778e805a408c739beeaea..0a714e065f10a7101447559927a949397e28308a 100644 --- a/src/Services/HousekeepingService.cpp +++ b/src/Services/HousekeepingService.cpp @@ -2,16 +2,15 @@ #include "ServicePool.hpp" void HousekeepingService::createHousekeepingReportStructure(Message& request) { - request.assertTC(ServiceType, MessageType::CreateHousekeepingReportStructure); + if (!request.assertTC(ServiceType, MessageType::CreateHousekeepingReportStructure)) { + return; + } uint8_t idToCreate = request.readUint8(); - if (housekeepingStructures.find(idToCreate) != housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedAlreadyExistingStructure); + if (hasAlreadyExistingStructError(idToCreate, request)) { return; } - if (housekeepingStructures.size() >= ECSSMaxHousekeepingStructures) { - ErrorHandler::reportError(request, - ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfHousekeepingStructures); + if (hasExceededMaxNumOfHousekeepingStructsError(request)) { return; } HousekeepingStructure newStructure; @@ -23,8 +22,7 @@ void HousekeepingService::createHousekeepingReportStructure(Message& request) { for (uint16_t i = 0; i < numOfSimplyCommutatedParams; i++) { uint16_t newParamId = request.readUint16(); - if (existsInVector(newStructure.simplyCommutatedParameterIds, newParamId)) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter); + if (hasAlreadyExistingParameterError(newStructure, newParamId, request)) { continue; } newStructure.simplyCommutatedParameterIds.push_back(newParamId); @@ -33,17 +31,17 @@ void HousekeepingService::createHousekeepingReportStructure(Message& request) { } void HousekeepingService::deleteHousekeepingReportStructure(Message& request) { - request.assertTC(ServiceType, MessageType::DeleteHousekeepingReportStructure); + if (!request.assertTC(ServiceType, MessageType::DeleteHousekeepingReportStructure)) { + return; + } uint8_t numOfStructuresToDelete = request.readUint8(); for (uint8_t i = 0; i < numOfStructuresToDelete; i++) { uint8_t structureId = request.readUint8(); - if (housekeepingStructures.find(structureId) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(structureId, request)) { continue; } - if (housekeepingStructures.at(structureId).periodicGenerationActionStatus) { - ErrorHandler::reportError(request, - ErrorHandler::ExecutionStartErrorType::RequestedDeletionOfEnabledHousekeeping); + + if (hasRequestedDeletionOfEnabledHousekeepingError(structureId, request)) { continue; } housekeepingStructures.erase(structureId); @@ -51,75 +49,80 @@ void HousekeepingService::deleteHousekeepingReportStructure(Message& request) { } void HousekeepingService::enablePeriodicHousekeepingParametersReport(Message& request) { - request.assertTC(ServiceType, MessageType::EnablePeriodicHousekeepingParametersReport); + if (!request.assertTC(ServiceType, MessageType::EnablePeriodicHousekeepingParametersReport)) { + return; + } uint8_t numOfStructIds = request.readUint8(); for (uint8_t i = 0; i < numOfStructIds; i++) { uint8_t structIdToEnable = request.readUint8(); - if (housekeepingStructures.find(structIdToEnable) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::RequestedNonExistingStructure); + if (hasNonExistingStructError(structIdToEnable, request)) { continue; } - housekeepingStructures.at(structIdToEnable).periodicGenerationActionStatus = true; + setPeriodicGenerationActionStatus(structIdToEnable, true); } } void HousekeepingService::disablePeriodicHousekeepingParametersReport(Message& request) { - request.assertTC(ServiceType, MessageType::DisablePeriodicHousekeepingParametersReport); + if (!request.assertTC(ServiceType, MessageType::DisablePeriodicHousekeepingParametersReport)) { + return; + } uint8_t numOfStructIds = request.readUint8(); for (uint8_t i = 0; i < numOfStructIds; i++) { uint8_t structIdToDisable = request.readUint8(); - if (housekeepingStructures.find(structIdToDisable) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::RequestedNonExistingStructure); + if (hasNonExistingStructError(structIdToDisable, request)) { continue; } - housekeepingStructures.at(structIdToDisable).periodicGenerationActionStatus = false; + setPeriodicGenerationActionStatus(structIdToDisable, false); } } void HousekeepingService::reportHousekeepingStructures(Message& request) { - request.assertTC(ServiceType, MessageType::ReportHousekeepingStructures); + if (!request.assertTC(ServiceType, MessageType::ReportHousekeepingStructures)) { + return; + } uint8_t numOfStructsToReport = request.readUint8(); for (uint8_t i = 0; i < numOfStructsToReport; i++) { uint8_t structureId = request.readUint8(); - if (housekeepingStructures.find(structureId) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(structureId, request)) { continue; } + housekeepingStructureReport(structureId); } } void HousekeepingService::housekeepingStructureReport(uint8_t structIdToReport) { auto housekeepingStructure = housekeepingStructures.find(structIdToReport); - if (housekeepingStructure == housekeepingStructures.end()) { - ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentHousekeeping); + if (hasNonExistingStructInternalError(structIdToReport)) { return; } - Message structReport(ServiceType, MessageType::HousekeepingStructuresReport, Message::TM, 1); + Message structReport = createTM(MessageType::HousekeepingStructuresReport); structReport.appendUint8(structIdToReport); structReport.appendBoolean(housekeepingStructure->second.periodicGenerationActionStatus); structReport.appendUint32(housekeepingStructure->second.collectionInterval); structReport.appendUint16(housekeepingStructure->second.simplyCommutatedParameterIds.size()); - for (auto parameterId : housekeepingStructure->second.simplyCommutatedParameterIds) { + for (auto parameterId: housekeepingStructure->second.simplyCommutatedParameterIds) { structReport.appendUint16(parameterId); } storeMessage(structReport); } void HousekeepingService::housekeepingParametersReport(uint8_t structureId) { - if (housekeepingStructures.find(structureId) == housekeepingStructures.end()) { - ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentHousekeeping); + if (hasNonExistingStructInternalError(structureId)) { return; } - Message housekeepingReport(ServiceType, MessageType::HousekeepingParametersReport, Message::TM, 1); + + auto& housekeepingStructure = getStruct(structureId)->get(); + + Message housekeepingReport = createTM(MessageType::HousekeepingParametersReport); housekeepingReport.appendUint8(structureId); - for (auto id : housekeepingStructures.at(structureId).simplyCommutatedParameterIds) { + for (auto id: housekeepingStructure.simplyCommutatedParameterIds) { if (auto parameter = Services.parameterManagement.getParameter(id)) { parameter->get().appendValueToMessage(housekeepingReport); } @@ -128,38 +131,38 @@ void HousekeepingService::housekeepingParametersReport(uint8_t structureId) { } void HousekeepingService::generateOneShotHousekeepingReport(Message& request) { - request.assertTC(ServiceType, MessageType::GenerateOneShotHousekeepingReport); + if (!request.assertTC(ServiceType, MessageType::GenerateOneShotHousekeepingReport)) { + return; + } uint8_t numOfStructsToReport = request.readUint8(); for (uint8_t i = 0; i < numOfStructsToReport; i++) { uint8_t structureId = request.readUint8(); - if (housekeepingStructures.find(structureId) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(structureId, request)) { continue; } + housekeepingParametersReport(structureId); } } void HousekeepingService::appendParametersToHousekeepingStructure(Message& request) { - request.assertTC(ServiceType, MessageType::AppendParametersToHousekeepingStructure); + if (!request.assertTC(ServiceType, MessageType::AppendParametersToHousekeepingStructure)) { + return; + } uint8_t targetStructId = request.readUint8(); - if (housekeepingStructures.find(targetStructId) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(targetStructId, request)) { return; } - auto& housekeepingStructure = housekeepingStructures.at(targetStructId); - if (housekeepingStructure.periodicGenerationActionStatus) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedAppendToEnabledHousekeeping); + auto& housekeepingStructure = getStruct(targetStructId)->get(); + if (hasRequestedAppendToEnabledHousekeepingError(housekeepingStructure, request)) { return; } uint16_t numOfSimplyCommutatedParameters = request.readUint16(); for (uint16_t i = 0; i < numOfSimplyCommutatedParameters; i++) { - if (housekeepingStructure.simplyCommutatedParameterIds.size() >= ECSSMaxSimplyCommutatedParameters) { - ErrorHandler::reportError( - request, ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfSimplyCommutatedParameters); + if (hasExceededMaxNumOfSimplyCommutatedParamsError(housekeepingStructure, request)) { return; } uint16_t newParamId = request.readUint16(); @@ -167,8 +170,7 @@ void HousekeepingService::appendParametersToHousekeepingStructure(Message& reque ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::GetNonExistingParameter); continue; } - if (existsInVector(housekeepingStructure.simplyCommutatedParameterIds, newParamId)) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter); + if (hasAlreadyExistingParameterError(housekeepingStructure, newParamId, request)) { continue; } housekeepingStructure.simplyCommutatedParameterIds.push_back(newParamId); @@ -176,40 +178,42 @@ void HousekeepingService::appendParametersToHousekeepingStructure(Message& reque } void HousekeepingService::modifyCollectionIntervalOfStructures(Message& request) { - request.assertTC(ServiceType, MessageType::ModifyCollectionIntervalOfStructures); + if (!request.assertTC(ServiceType, MessageType::ModifyCollectionIntervalOfStructures)) { + return; + } uint8_t numOfTargetStructs = request.readUint8(); for (uint8_t i = 0; i < numOfTargetStructs; i++) { uint8_t targetStructId = request.readUint8(); uint32_t newCollectionInterval = request.readUint32(); - if (housekeepingStructures.find(targetStructId) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(targetStructId, request)) { continue; } - housekeepingStructures.at(targetStructId).collectionInterval = newCollectionInterval; + setCollectionInterval(targetStructId, newCollectionInterval); } } void HousekeepingService::reportHousekeepingPeriodicProperties(Message& request) { - request.assertTC(ServiceType, MessageType::ReportHousekeepingPeriodicProperties); + if (!request.assertTC(ServiceType, MessageType::ReportHousekeepingPeriodicProperties)) { + return; + } uint8_t numOfValidIds = 0; uint8_t numOfStructIds = request.readUint8(); for (uint8_t i = 0; i < numOfStructIds; i++) { uint8_t structIdToReport = request.readUint8(); - if (housekeepingStructures.find(structIdToReport) != housekeepingStructures.end()) { + if (structExists(structIdToReport)) { numOfValidIds++; } } - Message periodicPropertiesReport(ServiceType, MessageType::HousekeepingPeriodicPropertiesReport, Message::TM, 1); + Message periodicPropertiesReport = createTM(MessageType::HousekeepingPeriodicPropertiesReport); periodicPropertiesReport.appendUint8(numOfValidIds); request.resetRead(); request.readUint8(); for (uint8_t i = 0; i < numOfStructIds; i++) { uint8_t structIdToReport = request.readUint8(); - if (housekeepingStructures.find(structIdToReport) == housekeepingStructures.end()) { - ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + if (hasNonExistingStructExecutionError(structIdToReport, request)) { continue; } appendPeriodicPropertiesToMessage(periodicPropertiesReport, structIdToReport); @@ -219,8 +223,8 @@ void HousekeepingService::reportHousekeepingPeriodicProperties(Message& request) void HousekeepingService::appendPeriodicPropertiesToMessage(Message& report, uint8_t structureId) { report.appendUint8(structureId); - report.appendBoolean(housekeepingStructures.at(structureId).periodicGenerationActionStatus); - report.appendUint32(housekeepingStructures.at(structureId).collectionInterval); + report.appendBoolean(getPeriodicGenerationActionStatus(structureId)); + report.appendUint32(getCollectionInterval(structureId)); } void HousekeepingService::execute(Message& message) { @@ -262,25 +266,99 @@ bool HousekeepingService::existsInVector(const etl::vector<uint16_t, ECSSMaxSimp uint32_t HousekeepingService::reportPendingStructures(uint32_t currentTime, uint32_t previousTime, uint32_t expectedDelay) { - uint32_t nextCollection = std::numeric_limits<uint32_t>::max(); - - for (auto &housekeepingStructure: housekeepingStructures) { - if (housekeepingStructure.second.collectionInterval == 0) { - housekeepingParametersReport(housekeepingStructure.second.structureId); - nextCollection = 0; - continue; - } - if (currentTime != 0 and (currentTime % housekeepingStructure.second.collectionInterval == 0 or - (previousTime + expectedDelay) % housekeepingStructure.second.collectionInterval == - 0)) { - housekeepingParametersReport(housekeepingStructure.second.structureId); - } - uint32_t structureTimeToCollection = housekeepingStructure.second.collectionInterval - - currentTime % housekeepingStructure.second.collectionInterval; - if (nextCollection > structureTimeToCollection) { - nextCollection = structureTimeToCollection; - } - } - - return nextCollection; + uint32_t nextCollection = std::numeric_limits<uint32_t>::max(); + + for (auto& housekeepingStructure: housekeepingStructures) { + if (!housekeepingStructure.second.periodicGenerationActionStatus) { + continue; + } + if (housekeepingStructure.second.collectionInterval == 0) { + housekeepingParametersReport(housekeepingStructure.second.structureId); + nextCollection = 0; + continue; + } + if (currentTime != 0 and (currentTime % housekeepingStructure.second.collectionInterval == 0 or + (previousTime + expectedDelay) % housekeepingStructure.second.collectionInterval == + 0)) { + housekeepingParametersReport(housekeepingStructure.second.structureId); + } + uint32_t structureTimeToCollection = housekeepingStructure.second.collectionInterval - + currentTime % housekeepingStructure.second.collectionInterval; + if (nextCollection > structureTimeToCollection) { + nextCollection = structureTimeToCollection; + } + } + + return nextCollection; +} + +bool HousekeepingService::hasNonExistingStructExecutionError(uint8_t id, Message& req) { + if (!structExists(id)) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + return true; + } + return false; +} + +bool HousekeepingService::hasNonExistingStructError(uint8_t id, Message& req) { + if (!structExists(id)) { + ErrorHandler::reportError(req, ErrorHandler::RequestedNonExistingStructure); + return true; + } + return false; +} + +bool HousekeepingService::hasNonExistingStructInternalError(uint8_t id) { + if (!structExists(id)) { + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentHousekeeping); + return true; + } + return false; +} +bool HousekeepingService::hasAlreadyExistingParameterError(HousekeepingStructure& housekeepingStruct, uint8_t id, Message& req) { + if (existsInVector(housekeepingStruct.simplyCommutatedParameterIds, id)) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter); + return true; + } + return false; } + +bool HousekeepingService::hasAlreadyExistingStructError(uint8_t id, Message& req) { + if (structExists(id)) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::RequestedAlreadyExistingStructure); + return true; + } + return false; +} + +bool HousekeepingService::hasExceededMaxNumOfHousekeepingStructsError(Message& req) { + if (housekeepingStructures.size() >= ECSSMaxHousekeepingStructures) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfHousekeepingStructures); + return true; + } + return false; +} + +bool HousekeepingService::hasRequestedAppendToEnabledHousekeepingError(HousekeepingStructure& housekeepingStruct, Message& req) { + if (housekeepingStruct.periodicGenerationActionStatus) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::RequestedAppendToEnabledHousekeeping); + return true; + } + return false; +} + +bool HousekeepingService::hasRequestedDeletionOfEnabledHousekeepingError(uint8_t id, Message& req) { + if (getPeriodicGenerationActionStatus(id)) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::RequestedDeletionOfEnabledHousekeeping); + return true; + } + return false; +} + +bool HousekeepingService::hasExceededMaxNumOfSimplyCommutatedParamsError(HousekeepingStructure& housekeepingStruct, Message& req) { + if (housekeepingStruct.simplyCommutatedParameterIds.size() >= ECSSMaxSimplyCommutatedParameters) { + ErrorHandler::reportError(req, ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfSimplyCommutatedParameters); + return true; + } + return false; +} \ No newline at end of file diff --git a/src/Services/LargePacketTransferService.cpp b/src/Services/LargePacketTransferService.cpp index 03bebbcd4d9df1b8df3b39d6552bcfb174420a80..b96c03aed3d886f475a08d27daeb90839f5a7975 100644 --- a/src/Services/LargePacketTransferService.cpp +++ b/src/Services/LargePacketTransferService.cpp @@ -2,58 +2,52 @@ #ifdef SERVICE_LARGEPACKET #include <Services/LargePacketTransferService.hpp> -#include "Message.hpp" #include <etl/String.hpp> +#include "Message.hpp" void LargePacketTransferService::firstDownlinkPartReport(uint16_t largeMessageTransactionIdentifier, uint16_t partSequenceNumber, const String<ECSSMaxFixedOctetStringSize>& string) { - // TM[13,1] - Message report = createTM(LargePacketTransferService::MessageType::FirstDownlinkPartReport); report.appendUint16(largeMessageTransactionIdentifier); // large message transaction identifier - report.appendUint16(partSequenceNumber); // part sequence number - report.appendOctetString(string); // fixed octet-string + report.appendUint16(partSequenceNumber); // part sequence number + report.appendOctetString(string); // fixed octet-string storeMessage(report); } void LargePacketTransferService::intermediateDownlinkPartReport( uint16_t largeMessageTransactionIdentifier, uint16_t partSequenceNumber, const String<ECSSMaxFixedOctetStringSize>& string) { - // TM[13,2] + Message report = createTM(LargePacketTransferService::MessageType::InternalDownlinkPartReport); report.appendUint16(largeMessageTransactionIdentifier); // large message transaction identifier - report.appendUint16(partSequenceNumber); // part sequence number - report.appendOctetString(string); // fixed octet-string + report.appendUint16(partSequenceNumber); // part sequence number + report.appendOctetString(string); // fixed octet-string storeMessage(report); } void LargePacketTransferService::lastDownlinkPartReport(uint16_t largeMessageTransactionIdentifier, uint16_t partSequenceNumber, const String<ECSSMaxFixedOctetStringSize>& string) { - // TM[13,3] Message report = createTM(LargePacketTransferService::MessageType::LastDownlinkPartReport); report.appendUint16(largeMessageTransactionIdentifier); // large message transaction identifier - report.appendUint16(partSequenceNumber); // part sequence number - report.appendOctetString(string); // fixed octet-string + report.appendUint16(partSequenceNumber); // part sequence number + report.appendOctetString(string); // fixed octet-string storeMessage(report); } String<ECSSMaxFixedOctetStringSize> LargePacketTransferService::firstUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string) { - // TC[13,9] return string; } String<ECSSMaxFixedOctetStringSize> LargePacketTransferService::intermediateUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string) { - // TC[13,10] return string; } String<ECSSMaxFixedOctetStringSize> LargePacketTransferService::lastUplinkPart(const String<ECSSMaxFixedOctetStringSize>& string) { - // TC[13, 11] return string; } @@ -61,19 +55,19 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr //TODO: Should this be uint32? uint16_t size = message.dataSize; uint16_t positionCounter = 0; - uint16_t parts = (size/ECSSMaxFixedOctetStringSize) + 1; + uint16_t parts = (size / ECSSMaxFixedOctetStringSize) + 1; String<ECSSMaxFixedOctetStringSize> stringPart(""); uint8_t dataPart[ECSSMaxFixedOctetStringSize]; - for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++){ + for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++) { dataPart[i] = message.data[positionCounter]; positionCounter++; } stringPart = dataPart; firstDownlinkPartReport(largeMessageTransactionIdentifier, 0, stringPart); - for (uint16_t part = 1; part < (parts - 1U); part++){ - for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++){ + for (uint16_t part = 1; part < (parts - 1U); part++) { + for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++) { dataPart[i] = message.data[positionCounter]; positionCounter++; } @@ -81,8 +75,8 @@ void LargePacketTransferService::split(Message& message, uint16_t largeMessageTr intermediateDownlinkPartReport(largeMessageTransactionIdentifier, part, stringPart); } - for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++){ - if (message.dataSize == positionCounter){ + for (uint16_t i = 0; i < ECSSMaxFixedOctetStringSize; i++) { + if (message.dataSize == positionCounter) { dataPart[i] = 0; // To prevent from filling the rest of the String with garbage info } dataPart[i] = message.data[positionCounter]; diff --git a/src/Services/MemoryManagementService.cpp b/src/Services/MemoryManagementService.cpp index db821cbfb7a531045774856682d0ef72576179d6..fba8b0e0315d3f6d5c4219c70a63ea956abe0c05 100644 --- a/src/Services/MemoryManagementService.cpp +++ b/src/Services/MemoryManagementService.cpp @@ -1,11 +1,10 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_MEMORY -#include "Services/MemoryManagementService.hpp" #include <cerrno> #include <etl/String.hpp> +#include "Services/MemoryManagementService.hpp" -// Define the constructors for the classes MemoryManagementService::MemoryManagementService() : rawDataMemorySubservice(*this) { serviceType = MemoryManagementService::ServiceType; } @@ -13,8 +12,7 @@ MemoryManagementService::MemoryManagementService() : rawDataMemorySubservice(*th MemoryManagementService::RawDataMemoryManagement::RawDataMemoryManagement(MemoryManagementService& parent) : mainService(parent) {} -// Function declarations for the raw data memory management subservice -void MemoryManagementService::RawDataMemoryManagement::loadRawData(Message& request) { +void MemoryManagementService::loadRawData(Message& request) { /** * Bear in mind that there is currently no error checking for invalid parameters. * A future version will include error checking and the corresponding error report/notification, @@ -23,149 +21,131 @@ void MemoryManagementService::RawDataMemoryManagement::loadRawData(Message& requ * @todo Add error checking and reporting for the parameters * @todo Add failure reporting */ - // Check if we have the correct packet request.assertTC(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::LoadRawMemoryDataAreas); - - // Read the memory ID from the request auto memoryID = MemoryManagementService::MemoryID(request.readEnum8()); - // Check for a valid memory ID first - if (mainService.memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { - // Variable declaration - uint8_t readData[ECSSMaxStringSize]; // Preallocate the array - uint16_t iterationCount = request.readUint16(); // Get the iteration count - - if (memoryID == MemoryManagementService::MemoryID::FLASH) { - // todo: Define FLASH specific access code when we transfer to embedded - } else { - for (std::size_t j = 0; j < iterationCount; j++) { - uint64_t startAddress = request.readUint64(); // Start address of the memory - uint16_t dataLength = request.readOctetString(readData); // Data length to load - uint16_t checksum = request.readBits(16); // Get the CRC checksum from the message - - // Continue only if the checksum passes - if (mainService.dataValidator(readData, checksum, dataLength)) { - if (mainService.addressValidator(memoryID, startAddress) && - mainService.addressValidator(memoryID, startAddress + dataLength)) { - for (std::size_t i = 0; i < dataLength; i++) { - *(reinterpret_cast<uint8_t*>(startAddress) + i) = readData[i]; - } - - // Read the loaded data for checksum validation and perform a check - for (std::size_t i = 0; i < dataLength; i++) { - readData[i] = *(reinterpret_cast<uint8_t*>(startAddress) + i); - } - if (checksum != CRCHelper::calculateCRC(readData, dataLength)) { - ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); - } - } else { - ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); - } - } else { - ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); - continue; // Continue to the next command - } + if (!memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { + // TODO: Send a failed start of execution + return; + } + + uint8_t readData[ECSSMaxStringSize]; + uint16_t iterationCount = request.readUint16(); + + if (memoryID == MemoryManagementService::MemoryID::FLASH_MEMORY) { + // TODO: Define FLASH specific access code when we transfer to embedded + } else { + for (std::size_t j = 0; j < iterationCount; j++) { + uint64_t startAddress = request.readUint64(); + uint16_t dataLength = request.readOctetString(readData); + uint16_t checksum = request.readBits(16); + + if (!dataValidator(readData, checksum, dataLength)) { + ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); + continue; + } + + if (!addressValidator(memoryID, startAddress) || + !addressValidator(memoryID, startAddress + dataLength)) { + ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); + continue; + } + + for (std::size_t i = 0; i < dataLength; i++) { + *(reinterpret_cast<uint8_t*>(startAddress) + i) = readData[i]; + } + + for (std::size_t i = 0; i < dataLength; i++) { + readData[i] = *(reinterpret_cast<uint8_t*>(startAddress) + i); + } + + if (checksum != CRCHelper::calculateCRC(readData, dataLength)) { + ErrorHandler::reportError(request, ErrorHandler::ChecksumFailed); } } - } else { - // todo: Send a failed start of execution } } void MemoryManagementService::RawDataMemoryManagement::dumpRawData(Message& request) { - // Check if we have the correct packet - request.assertTC(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::DumpRawMemoryData); + if (!request.assertTC(ServiceType, MessageType::DumpRawMemoryData)) { + return; + } - // Create the report message object of telemetry message subtype 6 Message report = mainService.createTM(MemoryManagementService::MessageType::DumpRawMemoryDataReport); - uint8_t memoryID = request.readEnum8(); // Read the memory ID from the request + uint8_t memoryID = request.readEnum8(); - // Check for a valid memory ID first - if (mainService.memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { - // Variable declaration - uint8_t readData[ECSSMaxStringSize]; // Preallocate the array - uint16_t iterationCount = request.readUint16(); // Get the iteration count + if (memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { + uint8_t readData[ECSSMaxStringSize]; + uint16_t iterationCount = request.readUint16(); - // Append the data to report message - report.appendEnum8(memoryID); // Memory ID - report.appendUint16(iterationCount); // Iteration count + report.appendEnum8(memoryID); + report.appendUint16(iterationCount); - // Iterate N times, as specified in the command message for (std::size_t j = 0; j < iterationCount; j++) { - uint64_t startAddress = request.readUint64(); // Data length to read - uint16_t readLength = request.readUint16(); // Start address for the memory read + uint64_t startAddress = request.readUint64(); + uint16_t readLength = request.readUint16(); - // Read memory data, an octet at a time, checking for a valid address first - if (mainService.addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress) && - mainService.addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress + readLength)) { + if (addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress) && + addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress + readLength)) { for (std::size_t i = 0; i < readLength; i++) { readData[i] = *(reinterpret_cast<uint8_t*>(startAddress) + i); } - // This part is repeated N-times (N = iteration count) - report.appendUint64(startAddress); // Start address - report.appendOctetString(String<1024>(readData, readLength)); // Save the - // read data + report.appendUint64(startAddress); + report.appendOctetString(String<1024>(readData, readLength)); report.appendBits(16, CRCHelper::calculateCRC(readData, readLength)); } else { ErrorHandler::reportError(request, ErrorHandler::AddressOutOfRange); } } - mainService.storeMessage(report); // Save the report message - request.resetRead(); // Reset the reading count + mainService.storeMessage(report); + request.resetRead(); } else { - // todo: Send a failed start of execution + // TODO: Send a failed start of execution } } void MemoryManagementService::RawDataMemoryManagement::checkRawData(Message& request) { - // Check if we have the correct packet - request.assertTC(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::CheckRawMemoryData); + if (!request.assertTC(ServiceType, MessageType::CheckRawMemoryData)) { + return; + } - // Create the report message object of telemetry message subtype 10 Message report = mainService.createTM(MemoryManagementService::MessageType::CheckRawMemoryDataReport); - uint8_t memoryID = request.readEnum8(); // Read the memory ID from the request + uint8_t memoryID = request.readEnum8(); - if (mainService.memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { - // Variable declaration - uint8_t readData[ECSSMaxStringSize]; // Preallocate the array - uint16_t iterationCount = request.readUint16(); // Get the iteration count + if (memoryIdValidator(MemoryManagementService::MemoryID(memoryID))) { + uint8_t readData[ECSSMaxStringSize]; + uint16_t iterationCount = request.readUint16(); - // Append the data to report message - report.appendEnum8(memoryID); // Memory ID - report.appendUint16(iterationCount); // Iteration count + report.appendEnum8(memoryID); + report.appendUint16(iterationCount); - // Iterate N times, as specified in the command message for (std::size_t j = 0; j < iterationCount; j++) { - uint64_t startAddress = request.readUint64(); // Data length to read - uint16_t readLength = request.readUint16(); // Start address for the memory read + uint64_t startAddress = request.readUint64(); + uint16_t readLength = request.readUint16(); - // Check whether the first and the last addresses are within the limits - if (mainService.addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress) && - mainService.addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress + readLength)) { - // Read memory data and save them for checksum calculation + if (addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress) && + addressValidator(MemoryManagementService::MemoryID(memoryID), startAddress + readLength)) { for (std::size_t i = 0; i < readLength; i++) { readData[i] = *(reinterpret_cast<uint8_t*>(startAddress) + i); } - // This part is repeated N-times (N = iteration count) - report.appendUint64(startAddress); // Start address - report.appendUint16(readLength); // Save the read data - report.appendBits(16, CRCHelper::calculateCRC(readData, readLength)); // Append CRC + report.appendUint64(startAddress); + report.appendUint16(readLength); + report.appendBits(16, CRCHelper::calculateCRC(readData, readLength)); } else { ErrorHandler::reportError(request, ErrorHandler::AddressOutOfRange); } } - mainService.storeMessage(report); // Save the report message - request.resetRead(); // Reset the reading count + mainService.storeMessage(report); + request.resetRead(); } else { - // todo: Send a failed start of execution report + // TODO: Send a failed start of execution report } } -// Private function declaration section bool MemoryManagementService::addressValidator(MemoryManagementService::MemoryID memId, uint64_t address) { bool validIndicator = false; @@ -195,14 +175,14 @@ bool MemoryManagementService::addressValidator(MemoryManagementService::MemoryID validIndicator = true; } break; - case MemoryManagementService::MemoryID::FLASH: + case MemoryManagementService::MemoryID::FLASH_MEMORY: if ((address >= FlashLowerLim) && (address <= FlashUpperLim)) { validIndicator = true; } break; default: - validIndicator = true; // todo: Implemented so addresses from PC can be read. Remove. + validIndicator = true; // TODO: Implemented so addresses from PC can be read. Remove. break; } @@ -215,7 +195,7 @@ inline bool MemoryManagementService::memoryIdValidator(MemoryManagementService:: (memId == MemoryManagementService::MemoryID::RAM_D3) || (memId == MemoryManagementService::MemoryID::DTCMRAM) || (memId == MemoryManagementService::MemoryID::ITCMRAM) || - (memId == MemoryManagementService::MemoryID::FLASH) || + (memId == MemoryManagementService::MemoryID::FLASH_MEMORY) || (memId == MemoryManagementService::MemoryID::EXTERNAL); } @@ -226,7 +206,7 @@ inline bool MemoryManagementService::dataValidator(const uint8_t* data, uint16_t void MemoryManagementService::execute(Message& message) { switch (message.messageType) { case LoadRawMemoryDataAreas: - rawDataMemorySubservice.loadRawData(message); + loadRawData(message); break; case DumpRawMemoryData: rawDataMemorySubservice.dumpRawData(message); diff --git a/src/Services/OnBoardMonitoringService.cpp b/src/Services/OnBoardMonitoringService.cpp index f644546db72bc8f8bfc59ec14352064893258cb1..06bbefd252acb63ff82d6af5d5f76160b5137f92 100644 --- a/src/Services/OnBoardMonitoringService.cpp +++ b/src/Services/OnBoardMonitoringService.cpp @@ -5,7 +5,9 @@ #include "etl/map.h" void OnBoardMonitoringService::enableParameterMonitoringDefinitions(Message& message) { - message.assertTC(ServiceType, EnableParameterMonitoringDefinitions); + if (!message.assertTC(ServiceType, EnableParameterMonitoringDefinitions)) { + return; + } uint16_t numberOfPMONDefinitions = message.readUint16(); for (uint16_t i = 0; i < numberOfPMONDefinitions; i++) { @@ -22,7 +24,9 @@ void OnBoardMonitoringService::enableParameterMonitoringDefinitions(Message& mes } void OnBoardMonitoringService::disableParameterMonitoringDefinitions(Message& message) { - message.assertTC(ServiceType, DisableParameterMonitoringDefinitions); + if (!message.assertTC(ServiceType, DisableParameterMonitoringDefinitions)) { + return; + } uint16_t numberOfPMONDefinitions = message.readUint16(); for (uint16_t i = 0; i < numberOfPMONDefinitions; i++) { @@ -39,12 +43,16 @@ void OnBoardMonitoringService::disableParameterMonitoringDefinitions(Message& me } void OnBoardMonitoringService::changeMaximumTransitionReportingDelay(Message& message) { - message.assertTC(ServiceType, ChangeMaximumTransitionReportingDelay); + if (!message.assertTC(ServiceType, ChangeMaximumTransitionReportingDelay)) { + return; + } maximumTransitionReportingDelay = message.readUint16(); } void OnBoardMonitoringService::deleteAllParameterMonitoringDefinitions(Message& message) { - message.assertTC(ServiceType, DeleteAllParameterMonitoringDefinitions); + if (!message.assertTC(ServiceType, DeleteAllParameterMonitoringDefinitions)) { + return; + } if (parameterMonitoringFunctionStatus) { ErrorHandler::reportError( message, ErrorHandler::ExecutionStartErrorType::InvalidRequestToDeleteAllParameterMonitoringDefinitions); diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp index 417debf669c2e5d6bad761917138a21850e57611..60ebd04c1c9e66647eb2e4a617a1a68e50839945 100644 --- a/src/Services/ParameterService.cpp +++ b/src/Services/ParameterService.cpp @@ -1,21 +1,16 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_PARAMETER -#include "Services/ParameterService.hpp" #include "Helpers/Parameter.hpp" +#include "Services/ParameterService.hpp" void ParameterService::reportParameters(Message& paramIds) { - // TM[20,2] - Message parameterReport(ParameterService::ServiceType, ParameterService::MessageType::ParameterValuesReport, - Message::TM, 1); - ErrorHandler::assertRequest(paramIds.packetType == Message::TC, paramIds, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(paramIds.messageType == ParameterService::MessageType::ReportParameterValues, paramIds, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(paramIds.serviceType == ParameterService::ServiceType, paramIds, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); + if (!paramIds.assertTC(ServiceType, ReportParameterValues)) { + return; + } + Message parameterReport = createTM(ParameterValuesReport); uint16_t numOfIds = paramIds.readUint16(); uint16_t numberOfValidIds = 0; @@ -30,35 +25,33 @@ void ParameterService::reportParameters(Message& paramIds) { numOfIds = paramIds.readUint16(); for (uint16_t i = 0; i < numOfIds; i++) { uint16_t currId = paramIds.readUint16(); - if (auto parameter = getParameter(currId)) { - parameterReport.appendUint16(currId); - parameter->get().appendValueToMessage(parameterReport); - } else { + auto parameter = getParameter(currId); + if (!parameter) { ErrorHandler::reportError(paramIds, ErrorHandler::GetNonExistingParameter); + continue; } + parameterReport.appendUint16(currId); + parameter->get().appendValueToMessage(parameterReport); } storeMessage(parameterReport); } -void ParameterService::setParameters(Message& newParamValues) { - ErrorHandler::assertRequest(newParamValues.packetType == Message::TC, newParamValues, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(newParamValues.messageType == ParameterService::MessageType::SetParameterValues, - newParamValues, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); - ErrorHandler::assertRequest(newParamValues.serviceType == ParameterService::ServiceType, newParamValues, - ErrorHandler::AcceptanceErrorType::UnacceptableMessage); +void ParameterService::setParameters(Message& newParamValues) const { + if (!newParamValues.assertTC(ServiceType, MessageType::SetParameterValues)) { + return; + } uint16_t numOfIds = newParamValues.readUint16(); for (uint16_t i = 0; i < numOfIds; i++) { uint16_t currId = newParamValues.readUint16(); - if (auto parameter = getParameter(currId)) { - parameter->get().setValueFromMessage(newParamValues); - } else { + auto parameter = getParameter(currId); + if (!parameter) { ErrorHandler::reportError(newParamValues, ErrorHandler::SetNonExistingParameter); break; // Setting next parameters is impossible, since the size of value to be read is unknown } + parameter->get().setValueFromMessage(newParamValues); } } diff --git a/src/Services/ParameterStatisticsService.cpp b/src/Services/ParameterStatisticsService.cpp index 432be671a3356fcf071d53f632aa2ad5b582cf9e..6bcfc9b489bc5195bec737dfbbd86fc413d2b42a 100644 --- a/src/Services/ParameterStatisticsService.cpp +++ b/src/Services/ParameterStatisticsService.cpp @@ -1,32 +1,40 @@ -#include <iostream> #include "ECSS_Configuration.hpp" #ifdef SERVICE_PARAMETER -#include "Services/ParameterStatisticsService.hpp" #include "ServicePool.hpp" +#include "Services/ParameterStatisticsService.hpp" + +ParameterStatisticsService::ParameterStatisticsService() : evaluationStartTime(TimeGetter::getCurrentTimeDefaultCUC()) { + initializeStatisticsMap(); + serviceType = ServiceType; +} void ParameterStatisticsService::reportParameterStatistics(Message& request) { - request.assertTC(ServiceType, MessageType::ReportParameterStatistics); + if (!request.assertTC(ServiceType, MessageType::ReportParameterStatistics)) { + return; + } parameterStatisticsReport(); - // TODO: append start time and end time to the report + if (hasAutomaticStatisticsReset or request.readBoolean()) { + resetParameterStatistics(); + } +} + +void ParameterStatisticsService::reportParameterStatistics(bool reset) { + parameterStatisticsReport(); - if (hasAutomaticStatisticsReset) { + if (hasAutomaticStatisticsReset or reset) { resetParameterStatistics(); - } else { - bool resetFlagValue = request.readBoolean(); - if (resetFlagValue) { - resetParameterStatistics(); - } } } void ParameterStatisticsService::parameterStatisticsReport() { - Message report(ServiceType, MessageType::ParameterStatisticsReport, Message::TM, 1); - report.appendUint16(1); // Dummy value for start and end time, will change in the end - report.appendUint16(1); - uint16_t numOfValidParameters = 0; + Message report = createTM(ParameterStatisticsReport); + report.append(evaluationStartTime); + auto evaluationStopTime = TimeGetter::getCurrentTimeDefaultCUC(); + report.append(evaluationStopTime); - for (auto& currentStatistic : statisticsMap) { + uint16_t numOfValidParameters = 0; + for (auto& currentStatistic: statisticsMap) { uint16_t numOfSamples = currentStatistic.second.sampleCounter; if (numOfSamples == 0) { continue; @@ -35,7 +43,7 @@ void ParameterStatisticsService::parameterStatisticsReport() { } report.appendUint16(numOfValidParameters); - for (auto& currentStatistic : statisticsMap) { + for (auto& currentStatistic: statisticsMap) { uint16_t currentId = currentStatistic.first; uint16_t numOfSamples = currentStatistic.second.sampleCounter; if (numOfSamples == 0) { @@ -49,16 +57,17 @@ void ParameterStatisticsService::parameterStatisticsReport() { } void ParameterStatisticsService::resetParameterStatistics(Message& request) { - request.assertTC(ServiceType, MessageType::ResetParameterStatistics); + if (!request.assertTC(ServiceType, MessageType::ResetParameterStatistics)) { + return; + } resetParameterStatistics(); } void ParameterStatisticsService::resetParameterStatistics() { - // TODO: Stop the evaluation of parameter statistics - for (auto& it : statisticsMap) { + for (auto& it: statisticsMap) { it.second.resetStatistics(); } - // TODO: Restart the evaluation of parameter statistics + evaluationStartTime = TimeGetter::getCurrentTimeDefaultCUC(); } void ParameterStatisticsService::enablePeriodicStatisticsReporting(Message& request) { @@ -68,7 +77,9 @@ void ParameterStatisticsService::enablePeriodicStatisticsReporting(Message& requ */ uint16_t SAMPLING_PARAMETER_INTERVAL = 5; - request.assertTC(ServiceType, MessageType::EnablePeriodicParameterReporting); + if (!request.assertTC(ServiceType, MessageType::EnablePeriodicParameterReporting)) { + return; + } uint16_t timeInterval = request.readUint16(); if (timeInterval < SAMPLING_PARAMETER_INTERVAL) { @@ -76,18 +87,22 @@ void ParameterStatisticsService::enablePeriodicStatisticsReporting(Message& requ return; } periodicStatisticsReportingStatus = true; - reportingInterval = timeInterval; + reportingIntervalMs = timeInterval; } void ParameterStatisticsService::disablePeriodicStatisticsReporting(Message& request) { - request.assertTC(ServiceType, MessageType::DisablePeriodicParameterReporting); + if (!request.assertTC(ServiceType, MessageType::DisablePeriodicParameterReporting)) { + return; + } periodicStatisticsReportingStatus = false; - reportingInterval = 0; + reportingIntervalMs = 0; } void ParameterStatisticsService::addOrUpdateStatisticsDefinitions(Message& request) { - request.assertTC(ServiceType, MessageType::AddOrUpdateParameterStatisticsDefinitions); + if (!request.assertTC(ServiceType, MessageType::AddOrUpdateParameterStatisticsDefinitions)) { + return; + } uint16_t numOfIds = request.readUint16(); for (uint16_t i = 0; i < numOfIds; i++) { @@ -103,7 +118,7 @@ void ParameterStatisticsService::addOrUpdateStatisticsDefinitions(Message& reque uint16_t interval = 0; if (supportsSamplingInterval) { interval = request.readUint16(); - if (interval < reportingInterval) { + if (interval < reportingIntervalMs) { ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::InvalidSamplingRateError); continue; } @@ -130,7 +145,9 @@ void ParameterStatisticsService::addOrUpdateStatisticsDefinitions(Message& reque } void ParameterStatisticsService::deleteStatisticsDefinitions(Message& request) { - request.assertTC(ServiceType, MessageType::DeleteParameterStatisticsDefinitions); + if (!request.assertTC(ServiceType, MessageType::DeleteParameterStatisticsDefinitions)) { + return; + } uint16_t numOfIds = request.readUint16(); if (numOfIds == 0) { @@ -152,21 +169,23 @@ void ParameterStatisticsService::deleteStatisticsDefinitions(Message& request) { } void ParameterStatisticsService::reportStatisticsDefinitions(Message& request) { - request.assertTC(ServiceType, MessageType::ReportParameterStatisticsDefinitions); + if (!request.assertTC(ServiceType, MessageType::ReportParameterStatisticsDefinitions)) { + return; + } statisticsDefinitionsReport(); } void ParameterStatisticsService::statisticsDefinitionsReport() { - Message definitionsReport(ServiceType, MessageType::ParameterStatisticsDefinitionsReport, Message::TM, 1); + Message definitionsReport = createTM(ParameterStatisticsDefinitionsReport); - uint16_t currentReportingInterval = 0; + uint16_t currentReportingIntervalMs = 0; if (periodicStatisticsReportingStatus) { - currentReportingInterval = reportingInterval; + currentReportingIntervalMs = reportingIntervalMs; } - definitionsReport.appendUint16(currentReportingInterval); + definitionsReport.appendUint16(currentReportingIntervalMs); definitionsReport.appendUint16(statisticsMap.size()); - for (auto& currentParam : statisticsMap) { + for (auto& currentParam: statisticsMap) { uint16_t currentId = currentParam.first; uint16_t samplingInterval = currentParam.second.selfSamplingInterval; definitionsReport.appendUint16(currentId); diff --git a/src/Services/RealTimeForwardingControlService.cpp b/src/Services/RealTimeForwardingControlService.cpp index 2ca94a03289880696d9663881d71ae22a43fa067..a23d4990969b6a9ce7905469f4d7d40677497ca0 100644 --- a/src/Services/RealTimeForwardingControlService.cpp +++ b/src/Services/RealTimeForwardingControlService.cpp @@ -2,14 +2,14 @@ #include <iostream> void RealTimeForwardingControlService::addAllReportsOfApplication(uint8_t applicationID) { - for (auto& service: AllMessageTypes::messagesOfService) { + for (const auto& service: AllMessageTypes::MessagesOfService) { uint8_t serviceType = service.first; addAllReportsOfService(applicationID, serviceType); } } void RealTimeForwardingControlService::addAllReportsOfService(uint8_t applicationID, uint8_t serviceType) { - for (auto& messageType: AllMessageTypes::messagesOfService[serviceType]) { + for (const auto& messageType: AllMessageTypes::MessagesOfService.at(serviceType)) { auto appServicePair = std::make_pair(applicationID, serviceType); applicationProcessConfiguration.definitions[appServicePair].push_back(messageType); } @@ -18,7 +18,7 @@ void RealTimeForwardingControlService::addAllReportsOfService(uint8_t applicatio uint8_t RealTimeForwardingControlService::countServicesOfApplication(uint8_t applicationID) { uint8_t serviceCounter = 0; for (auto& definition: applicationProcessConfiguration.definitions) { - auto& pair = definition.first; + const auto& pair = definition.first; if (pair.first == applicationID) { serviceCounter++; } @@ -79,7 +79,7 @@ bool RealTimeForwardingControlService::checkService(Message& request, uint8_t ap bool RealTimeForwardingControlService::maxReportTypesReached(Message& request, uint8_t applicationID, uint8_t serviceType) { - if (countReportsOfService(applicationID, serviceType) >= AllMessageTypes::messagesOfService[serviceType].size()) { + if (countReportsOfService(applicationID, serviceType) >= AllMessageTypes::MessagesOfService.at(serviceType).size()) { ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::MaxReportTypesReached); return true; } @@ -88,11 +88,8 @@ bool RealTimeForwardingControlService::maxReportTypesReached(Message& request, u bool RealTimeForwardingControlService::checkMessage(Message& request, uint8_t applicationID, uint8_t serviceType, uint8_t messageType) { - if (maxReportTypesReached(request, applicationID, serviceType) or - reportExistsInAppProcessConfiguration(applicationID, serviceType, messageType)) { - return false; - } - return true; + return !maxReportTypesReached(request, applicationID, serviceType) and + !reportExistsInAppProcessConfiguration(applicationID, serviceType, messageType); } bool RealTimeForwardingControlService::reportExistsInAppProcessConfiguration(uint8_t applicationID, uint8_t serviceType, @@ -103,7 +100,9 @@ bool RealTimeForwardingControlService::reportExistsInAppProcessConfiguration(uin } void RealTimeForwardingControlService::addReportTypesToAppProcessConfiguration(Message& request) { - request.assertTC(ServiceType, MessageType::AddReportTypesToAppProcessConfiguration); + if (!request.assertTC(ServiceType, MessageType::AddReportTypesToAppProcessConfiguration)) { + return; + } uint8_t numOfApplications = request.readUint8(); for (uint8_t i = 0; i < numOfApplications; i++) { diff --git a/src/Services/RequestVerificationService.cpp b/src/Services/RequestVerificationService.cpp index 33546c778b51a0d57832080b7acc3a4e86e6832f..42b0ab05393ad17563d100804c32198c3095b1c8 100644 --- a/src/Services/RequestVerificationService.cpp +++ b/src/Services/RequestVerificationService.cpp @@ -8,11 +8,11 @@ void RequestVerificationService::successAcceptanceVerification(const Message& re Message report = createTM(RequestVerificationService::MessageType::SuccessfulAcceptanceReport); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); @@ -24,13 +24,13 @@ void RequestVerificationService::failAcceptanceVerification(const Message& reque Message report = createTM(RequestVerificationService::MessageType::FailedAcceptanceReport); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendEnum16(errorCode); // error code + report.appendEnum16(errorCode); // error code storeMessage(report); } @@ -40,11 +40,11 @@ void RequestVerificationService::successStartExecutionVerification(const Message Message report = createTM(RequestVerificationService::MessageType::SuccessfulStartOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); @@ -56,13 +56,13 @@ void RequestVerificationService::failStartExecutionVerification(const Message& r Message report = createTM(RequestVerificationService::MessageType::FailedStartOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendEnum16(errorCode); // error code + report.appendEnum16(errorCode); // error code storeMessage(report); } @@ -72,13 +72,13 @@ void RequestVerificationService::successProgressExecutionVerification(const Mess Message report = createTM(RequestVerificationService::MessageType::SuccessfulProgressOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendByte(stepID); // step ID + report.appendByte(stepID); // step ID storeMessage(report); } @@ -90,14 +90,14 @@ void RequestVerificationService::failProgressExecutionVerification(const Message Message report = createTM(RequestVerificationService::MessageType::FailedProgressOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendByte(stepID); // step ID - report.appendEnum16(errorCode); // error code + report.appendByte(stepID); // step ID + report.appendEnum16(errorCode); // error code storeMessage(report); } @@ -107,11 +107,11 @@ void RequestVerificationService::successCompletionExecutionVerification(const Me Message report = createTM(RequestVerificationService::MessageType::SuccessfulCompletionOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count storeMessage(report); @@ -123,13 +123,13 @@ void RequestVerificationService::failCompletionExecutionVerification( Message report = createTM(RequestVerificationService::MessageType::FailedCompletionOfExecution); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendEnum16(errorCode); // error code + report.appendEnum16(errorCode); // error code storeMessage(report); } @@ -140,13 +140,13 @@ void RequestVerificationService::failRoutingVerification(const Message& request, Message report = createTM(RequestVerificationService::MessageType::FailedRoutingReport); - report.appendEnumerated(3, CCSDSPacketVersion); // packet version number - report.appendEnumerated(1, request.packetType); // packet type - report.appendBits(1, 1); // secondary header flag + report.appendEnumerated(3, CCSDSPacketVersion); // packet version number + report.appendEnumerated(1, request.packetType); // packet type + report.appendBits(1, 1); // secondary header flag report.appendEnumerated(11, request.applicationId); // application process ID - report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags + report.appendEnumerated(2, ECSSSequenceFlags); // sequence flags report.appendBits(14, request.packetSequenceCount); // packet sequence count - report.appendEnum16(errorCode); // error code + report.appendEnum16(errorCode); // error code storeMessage(report); } diff --git a/src/Services/StorageAndRetrievalService.cpp b/src/Services/StorageAndRetrievalService.cpp index 23f06f48b380f5359ed367787e32a95a057ab27c..2b9e1d8cfdc6cef4c585cd5f9e6198aa273e929f 100644 --- a/src/Services/StorageAndRetrievalService.cpp +++ b/src/Services/StorageAndRetrievalService.cpp @@ -25,7 +25,7 @@ void StorageAndRetrievalService::copyFromTagToTag(Message& request) { return; } - for (auto& packet : packetStores[fromPacketStoreId].storedTelemetryPackets) { + for (auto& packet: packetStores[fromPacketStoreId].storedTelemetryPackets) { if (packet.first < startTime) { continue; } @@ -46,7 +46,7 @@ void StorageAndRetrievalService::copyAfterTimeTag(Message& request) { return; } - for (auto& packet : packetStores[fromPacketStoreId].storedTelemetryPackets) { + for (auto& packet: packetStores[fromPacketStoreId].storedTelemetryPackets) { if (packet.first < startTime) { continue; } @@ -64,7 +64,7 @@ void StorageAndRetrievalService::copyBeforeTimeTag(Message& request) { return; } - for (auto& packet : packetStores[fromPacketStoreId].storedTelemetryPackets) { + for (auto& packet: packetStores[fromPacketStoreId].storedTelemetryPackets) { if (packet.first > endTime) { break; } @@ -159,7 +159,7 @@ void StorageAndRetrievalService::createContentSummary(Message& report, report.appendUint32(packetStores[packetStoreId].openRetrievalStartTimeTag); - auto filledPercentage1 = static_cast<uint16_t>(packetStores[packetStoreId].storedTelemetryPackets.size() * 100.0f / + auto filledPercentage1 = static_cast<uint16_t>(static_cast<float>(packetStores[packetStoreId].storedTelemetryPackets.size()) * 100 / ECSSMaxPacketStoreSize); report.appendUint16(filledPercentage1); @@ -169,7 +169,7 @@ void StorageAndRetrievalService::createContentSummary(Message& report, std::end(packetStores[packetStoreId].storedTelemetryPackets), [this, &packetStoreId](auto packet) { return packet.first >= packetStores[packetStoreId].openRetrievalStartTimeTag; }); - auto filledPercentage2 = static_cast<uint16_t>(numOfPacketsToBeTransferred * 100.0f / ECSSMaxPacketStoreSize); + auto filledPercentage2 = static_cast<uint16_t>(static_cast<float>(numOfPacketsToBeTransferred) * 100 / ECSSMaxPacketStoreSize); report.appendUint16(filledPercentage2); } @@ -217,9 +217,7 @@ uint16_t StorageAndRetrievalService::currentNumberOfPacketStores() { PacketStore& StorageAndRetrievalService::getPacketStore(const String<ECSSPacketStoreIdSize>& packetStoreId) { auto packetStore = packetStores.find(packetStoreId); - if (packetStore == packetStores.end()) { - assert(ErrorHandler::ExecutionStartErrorType::NonExistingPacketStore); - } + ASSERT_INTERNAL(packetStore != packetStores.end(), ErrorHandler::InternalErrorType::ElementNotInArray); return packetStore->second; } @@ -231,7 +229,7 @@ void StorageAndRetrievalService::executeOnPacketStores(Message& request, const std::function<void(PacketStore&)>& function) { uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { function(packetStore.second); } return; @@ -249,19 +247,25 @@ void StorageAndRetrievalService::executeOnPacketStores(Message& request, } void StorageAndRetrievalService::enableStorageFunction(Message& request) { - request.assertTC(ServiceType, MessageType::EnableStorageInPacketStores); + if (!request.assertTC(ServiceType, MessageType::EnableStorageInPacketStores)) { + return; + } executeOnPacketStores(request, [](PacketStore& p) { p.storageStatus = true; }); } void StorageAndRetrievalService::disableStorageFunction(Message& request) { - request.assertTC(ServiceType, MessageType::DisableStorageInPacketStores); + if (!request.assertTC(ServiceType, MessageType::DisableStorageInPacketStores)) { + return; + } executeOnPacketStores(request, [](PacketStore& p) { p.storageStatus = false; }); } void StorageAndRetrievalService::startByTimeRangeRetrieval(Message& request) { - request.assertTC(ServiceType, MessageType::StartByTimeRangeRetrieval); + if (!request.assertTC(ServiceType, MessageType::StartByTimeRangeRetrieval)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); bool errorFlag = false; @@ -290,13 +294,15 @@ void StorageAndRetrievalService::startByTimeRangeRetrieval(Message& request) { } void StorageAndRetrievalService::deletePacketStoreContent(Message& request) { - request.assertTC(ServiceType, MessageType::DeletePacketStoreContent); + if (!request.assertTC(ServiceType, MessageType::DeletePacketStoreContent)) { + return; + } uint32_t timeLimit = request.readUint32(); // todo: decide the time-format uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { if (packetStore.second.byTimeRangeRetrievalStatus) { ErrorHandler::reportError( request, ErrorHandler::ExecutionStartErrorType::SetPacketStoreWithByTimeRangeRetrieval); @@ -332,14 +338,16 @@ void StorageAndRetrievalService::deletePacketStoreContent(Message& request) { } void StorageAndRetrievalService::packetStoreContentSummaryReport(Message& request) { - request.assertTC(ServiceType, MessageType::ReportContentSummaryOfPacketStores); + if (!request.assertTC(ServiceType, MessageType::ReportContentSummaryOfPacketStores)) { + return; + } - Message report(ServiceType, MessageType::PacketStoreContentSummaryReport, Message::TM, 1); + Message report = createTM(PacketStoreContentSummaryReport); uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { report.appendUint16(packetStores.size()); - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { auto packetStoreId = packetStore.first; report.appendString(packetStoreId); createContentSummary(report, packetStoreId); @@ -371,7 +379,9 @@ void StorageAndRetrievalService::packetStoreContentSummaryReport(Message& reques } void StorageAndRetrievalService::changeOpenRetrievalStartTimeTag(Message& request) { - request.assertTC(ServiceType, MessageType::ChangeOpenRetrievalStartingTime); + if (!request.assertTC(ServiceType, MessageType::ChangeOpenRetrievalStartingTime)) { + return; + } uint32_t newStartTimeTag = request.readUint32(); /** @@ -379,7 +389,7 @@ void StorageAndRetrievalService::changeOpenRetrievalStartTimeTag(Message& reques */ uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { if (packetStore.second.openRetrievalStatus == PacketStore::InProgress) { ErrorHandler::reportError( request, ErrorHandler::ExecutionStartErrorType::SetPacketStoreWithOpenRetrievalInProgress); @@ -406,11 +416,13 @@ void StorageAndRetrievalService::changeOpenRetrievalStartTimeTag(Message& reques } void StorageAndRetrievalService::resumeOpenRetrievalOfPacketStores(Message& request) { - request.assertTC(ServiceType, MessageType::ResumeOpenRetrievalOfPacketStores); + if (!request.assertTC(ServiceType, MessageType::ResumeOpenRetrievalOfPacketStores)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { if (packetStore.second.byTimeRangeRetrievalStatus) { ErrorHandler::reportError( request, ErrorHandler::ExecutionStartErrorType::SetPacketStoreWithByTimeRangeRetrieval); @@ -437,11 +449,13 @@ void StorageAndRetrievalService::resumeOpenRetrievalOfPacketStores(Message& requ } void StorageAndRetrievalService::suspendOpenRetrievalOfPacketStores(Message& request) { - request.assertTC(ServiceType, MessageType::SuspendOpenRetrievalOfPacketStores); + if (!request.assertTC(ServiceType, MessageType::SuspendOpenRetrievalOfPacketStores)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { packetStore.second.openRetrievalStatus = PacketStore::Suspended; } return; @@ -457,11 +471,13 @@ void StorageAndRetrievalService::suspendOpenRetrievalOfPacketStores(Message& req } void StorageAndRetrievalService::abortByTimeRangeRetrieval(Message& request) { - request.assertTC(ServiceType, MessageType::AbortByTimeRangeRetrieval); + if (!request.assertTC(ServiceType, MessageType::AbortByTimeRangeRetrieval)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { packetStore.second.byTimeRangeRetrievalStatus = false; } return; @@ -477,11 +493,13 @@ void StorageAndRetrievalService::abortByTimeRangeRetrieval(Message& request) { } void StorageAndRetrievalService::packetStoresStatusReport(Message& request) { - request.assertTC(ServiceType, MessageType::ReportStatusOfPacketStores); + if (!request.assertTC(ServiceType, MessageType::ReportStatusOfPacketStores)) { + return; + } - Message report(ServiceType, MessageType::PacketStoresStatusReport, Message::TM, 1); + Message report = createTM(PacketStoresStatusReport); report.appendUint16(packetStores.size()); - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { auto packetStoreId = packetStore.first; report.appendString(packetStoreId); report.appendBoolean(packetStore.second.storageStatus); @@ -492,7 +510,9 @@ void StorageAndRetrievalService::packetStoresStatusReport(Message& request) { } void StorageAndRetrievalService::createPacketStores(Message& request) { - request.assertTC(ServiceType, MessageType::CreatePacketStores); + if (!request.assertTC(ServiceType, MessageType::CreatePacketStores)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); for (uint16_t i = 0; i < numOfPacketStores; i++) { @@ -529,13 +549,15 @@ void StorageAndRetrievalService::createPacketStores(Message& request) { } void StorageAndRetrievalService::deletePacketStores(Message& request) { - request.assertTC(ServiceType, MessageType::DeletePacketStores); + if (!request.assertTC(ServiceType, MessageType::DeletePacketStores)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); if (numOfPacketStores == 0) { - int numOfPacketStoresToDelete = 0; - etl::string<ECSSPacketStoreIdSize> packetStoresToDelete[packetStores.size()]; - for (auto& packetStore : packetStores) { + uint16_t numOfPacketStoresToDelete = 0; + etl::string<ECSSPacketStoreIdSize> packetStoresToDelete[ECSSMaxPacketStores]; + for (auto& packetStore: packetStores) { if (packetStore.second.storageStatus) { ErrorHandler::reportError( request, ErrorHandler::ExecutionStartErrorType::DeletionOfPacketStoreWithStorageStatusEnabled); @@ -592,11 +614,13 @@ void StorageAndRetrievalService::deletePacketStores(Message& request) { } void StorageAndRetrievalService::packetStoreConfigurationReport(Message& request) { - request.assertTC(ServiceType, MessageType::ReportConfigurationOfPacketStores); + if (!request.assertTC(ServiceType, MessageType::ReportConfigurationOfPacketStores)) { + return; + } + Message report = createTM(PacketStoreConfigurationReport); - Message report(ServiceType, MessageType::PacketStoreConfigurationReport, Message::TM, 1); report.appendUint16(packetStores.size()); - for (auto& packetStore : packetStores) { + for (auto& packetStore: packetStores) { auto packetStoreId = packetStore.first; report.appendString(packetStoreId); report.appendUint16(packetStore.second.sizeInBytes); @@ -608,7 +632,9 @@ void StorageAndRetrievalService::packetStoreConfigurationReport(Message& request } void StorageAndRetrievalService::copyPacketsInTimeWindow(Message& request) { - request.assertTC(ServiceType, MessageType::CopyPacketsInTimeWindow); + if (!request.assertTC(ServiceType, MessageType::CopyPacketsInTimeWindow)) { + return; + } uint8_t typeOfTimeWindow = request.readEnum8(); switch (typeOfTimeWindow) { @@ -628,7 +654,9 @@ void StorageAndRetrievalService::copyPacketsInTimeWindow(Message& request) { } void StorageAndRetrievalService::resizePacketStores(Message& request) { - request.assertTC(ServiceType, MessageType::ResizePacketStores); + if (!request.assertTC(ServiceType, MessageType::ResizePacketStores)) { + return; + } uint16_t numOfPacketStores = request.readUint16(); for (uint16_t i = 0; i < numOfPacketStores; i++) { @@ -664,7 +692,9 @@ void StorageAndRetrievalService::resizePacketStores(Message& request) { } void StorageAndRetrievalService::changeTypeToCircular(Message& request) { - request.assertTC(ServiceType, MessageType::ChangeTypeToCircular); + if (!request.assertTC(ServiceType, MessageType::ChangeTypeToCircular)) { + return; + } auto idToChange = readPacketStoreId(request); if (packetStores.find(idToChange) == packetStores.end()) { @@ -692,7 +722,9 @@ void StorageAndRetrievalService::changeTypeToCircular(Message& request) { } void StorageAndRetrievalService::changeTypeToBounded(Message& request) { - request.assertTC(ServiceType, MessageType::ChangeTypeToBounded); + if (!request.assertTC(ServiceType, MessageType::ChangeTypeToBounded)) { + return; + } auto idToChange = readPacketStoreId(request); if (packetStores.find(idToChange) == packetStores.end()) { @@ -720,7 +752,9 @@ void StorageAndRetrievalService::changeTypeToBounded(Message& request) { } void StorageAndRetrievalService::changeVirtualChannel(Message& request) { - request.assertTC(ServiceType, MessageType::ChangeVirtualChannel); + if (!request.assertTC(ServiceType, MessageType::ChangeVirtualChannel)) { + return; + } auto idToChange = readPacketStoreId(request); uint8_t virtualChannel = request.readUint8(); diff --git a/src/Services/TestService.cpp b/src/Services/TestService.cpp index 93c1ebeed096ba0197428622e4417985925a0a66..e6dbc7e3fac8c9482dd2d1784e9eecfed7becf99 100644 --- a/src/Services/TestService.cpp +++ b/src/Services/TestService.cpp @@ -1,23 +1,32 @@ #include "ECSS_Configuration.hpp" #ifdef SERVICE_TEST +#include "ServicePool.hpp" #include "Services/TestService.hpp" void TestService::areYouAlive(Message& request) { - request.assertTC(TestService::ServiceType, TestService::MessageType::AreYouAliveTest); - // TM[17,2] are-you-alive connection test report - Message report = createTM(TestService::MessageType::AreYouAliveTestReport); + if (!request.assertTC(TestService::ServiceType, TestService::MessageType::AreYouAliveTest)) { + return; + } + areYouAliveReport(); +} +void TestService::areYouAliveReport() { + Message report = createTM(TestService::MessageType::AreYouAliveTestReport); storeMessage(report); } void TestService::onBoardConnection(Message& request) { - request.assertTC(TestService::ServiceType, TestService::MessageType::OnBoardConnectionTest); - // TM[17,4] on-board connection test report - Message report = createTM(TestService::MessageType::OnBoardConnectionTestReport); + if (!request.assertTC(TestService::ServiceType, TestService::MessageType::OnBoardConnectionTest)) { + return; + } + uint16_t applicationProcessId = request.readUint16(); + onBoardConnectionReport(applicationProcessId); +} - report.appendUint16(request.readUint16()); - // just print it on the screen +void TestService::onBoardConnectionReport(uint16_t applicationProcessId) { + Message report = createTM(TestService::MessageType::OnBoardConnectionTestReport); + report.appendUint16(applicationProcessId); storeMessage(report); } diff --git a/src/Services/TimeBasedSchedulingService.cpp b/src/Services/TimeBasedSchedulingService.cpp index b88f46a48db0c5d1aa5ba79cc15d95bd54005451..f5d2efc29226aac7d038fbefddb72aa738a9d064 100644 --- a/src/Services/TimeBasedSchedulingService.cpp +++ b/src/Services/TimeBasedSchedulingService.cpp @@ -7,55 +7,65 @@ TimeBasedSchedulingService::TimeBasedSchedulingService() { serviceType = TimeBasedSchedulingService::ServiceType; } -void TimeBasedSchedulingService::enableScheduleExecution(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction); +Time::DefaultCUC TimeBasedSchedulingService::executeScheduledActivity(Time::DefaultCUC currentTime) { + if (currentTime >= scheduledActivities.front().requestReleaseTime && !scheduledActivities.empty()) { + if (scheduledActivities.front().requestID.applicationID == ApplicationId) { + MessageParser::execute(scheduledActivities.front().request); + } + scheduledActivities.pop_front(); + } + + if (!scheduledActivities.empty()) { + return scheduledActivities.front().requestReleaseTime; + } else { + return Time::DefaultCUC::max(); + } +} - executionFunctionStatus = true; // Enable the service +void TimeBasedSchedulingService::enableScheduleExecution(Message& request) { + if (!request.assertTC(ServiceType, MessageType::EnableTimeBasedScheduleExecutionFunction)) { + return; + } + executionFunctionStatus = true; } void TimeBasedSchedulingService::disableScheduleExecution(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DisableTimeBasedScheduleExecutionFunction); - - executionFunctionStatus = false; // Disable the service + if (!request.assertTC(ServiceType, MessageType::DisableTimeBasedScheduleExecutionFunction)) { + return; + } + executionFunctionStatus = false; } void TimeBasedSchedulingService::resetSchedule(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::ResetTimeBasedSchedule); - - executionFunctionStatus = false; // Disable the service - scheduledActivities.clear(); // Delete all scheduled activities + if (!request.assertTC(ServiceType, MessageType::ResetTimeBasedSchedule)) { + return; + } + executionFunctionStatus = false; + scheduledActivities.clear(); // todo: Add resetting for sub-schedules and groups, if defined } void TimeBasedSchedulingService::insertActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::InsertActivities); + if (!request.assertTC(ServiceType, MessageType::InsertActivities)) { + return; + } // todo: Get the sub-schedule ID if they are implemented - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { // todo: Get the group ID first, if groups are used - uint32_t currentTime = TimeGetter::getSeconds(); // Get the current system time + Time::DefaultCUC currentTime = TimeGetter::getCurrentTimeDefaultCUC(); - uint32_t releaseTime = request.readUint32(); // Get the specified release time + Time::DefaultCUC releaseTime = request.readDefaultCUCTimeStamp(); if ((scheduledActivities.available() == 0) || (releaseTime < (currentTime + ECSSTimeMarginForActivation))) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); request.skipBytes(ECSSTCRequestStringSize); } else { - // Get the TC packet request uint8_t requestData[ECSSTCRequestStringSize] = {0}; request.readString(requestData, ECSSTCRequestStringSize); Message receivedTCPacket = MessageParser::parseECSSTC(requestData); - ScheduledActivity newActivity; // Create the new activity + ScheduledActivity newActivity; - // Assign the attributes to the newly created activity newActivity.request = receivedTCPacket; newActivity.requestReleaseTime = releaseTime; @@ -63,95 +73,89 @@ void TimeBasedSchedulingService::insertActivities(Message& request) { newActivity.requestID.applicationID = request.applicationId; newActivity.requestID.sequenceCount = request.packetSequenceCount; - scheduledActivities.push_back(newActivity); // Insert the new activities + scheduledActivities.push_back(newActivity); } } - sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time + sortActivitiesReleaseTime(scheduledActivities); + notifyNewActivityAddition(); } void TimeBasedSchedulingService::timeShiftAllActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities); + if (!request.assertTC(ServiceType, MessageType::TimeShiftALlScheduledActivities)) { + return; + } - uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time + Time::DefaultCUC current_time = TimeGetter::getCurrentTimeDefaultCUC(); - // Find the earliest release time. It will be the first element of the iterator pair const auto releaseTimes = etl::minmax_element(scheduledActivities.begin(), scheduledActivities.end(), [](ScheduledActivity const& leftSide, ScheduledActivity const& rightSide) { return leftSide.requestReleaseTime < rightSide.requestReleaseTime; }); // todo: Define what the time format is going to be - int32_t relativeOffset = request.readSint32(); // Get the relative offset - if ((releaseTimes.first->requestReleaseTime + relativeOffset) < (current_time + ECSSTimeMarginForActivation)) { - // Report the error + Time::RelativeTime relativeOffset = request.readRelativeTime(); + if ((releaseTimes.first->requestReleaseTime + std::chrono::seconds(relativeOffset)) < (current_time + ECSSTimeMarginForActivation)) { ErrorHandler::reportError(request, ErrorHandler::SubServiceExecutionStartError); - } else { - for (auto& activity : scheduledActivities) { - activity.requestReleaseTime += relativeOffset; // Time shift each activity - } + return; + } + for (auto& activity: scheduledActivities) { + activity.requestReleaseTime += std::chrono::seconds(relativeOffset); } } void TimeBasedSchedulingService::timeShiftActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftActivitiesById); + if (!request.assertTC(ServiceType, MessageType::TimeShiftActivitiesById)) { + return; + } - uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time + Time::DefaultCUC current_time = TimeGetter::getCurrentTimeDefaultCUC(); - int32_t relativeOffset = request.readSint32(); // Get the offset first - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + auto relativeOffset = std::chrono::seconds(request.readRelativeTime()); + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - // If the relative offset does not meet the restrictions issue an error if ((requestIDMatch->requestReleaseTime + relativeOffset) < (current_time + ECSSTimeMarginForActivation)) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } else { - requestIDMatch->requestReleaseTime += relativeOffset; // Add the time offset + requestIDMatch->requestReleaseTime += relativeOffset; } } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time + sortActivitiesReleaseTime(scheduledActivities); } void TimeBasedSchedulingService::deleteActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DeleteActivitiesById); + if (!request.assertTC(ServiceType, MessageType::DeleteActivitiesById)) { + return; + } - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID const auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - scheduledActivities.erase(requestIDMatch); // Delete activity from the schedule + scheduledActivities.erase(requestIDMatch); } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } @@ -159,82 +163,70 @@ void TimeBasedSchedulingService::deleteActivitiesByID(Message& request) { } void TimeBasedSchedulingService::detailReportAllActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities); + if (!request.assertTC(ServiceType, MessageType::DetailReportAllScheduledActivities)) { + return; + } - // Create the report message object of telemetry message subtype 10 for each activity - Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById); - report.appendUint16(static_cast<uint16_t>(scheduledActivities.size())); + timeBasedScheduleDetailReport(scheduledActivities); +} - for (auto& activity : scheduledActivities) { - // todo: append sub-schedule and group ID if they are defined +void TimeBasedSchedulingService::timeBasedScheduleDetailReport(const etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& listOfActivities) { + // todo: append sub-schedule and group ID if they are defined + Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById); + report.appendUint16(static_cast<uint16_t>(listOfActivities.size())); - report.appendUint32(activity.requestReleaseTime); + for (const auto& activity: listOfActivities) { + report.appendDefaultCUCTimeStamp(activity.requestReleaseTime); // todo: Replace with the time parser report.appendString(MessageParser::composeECSS(activity.request)); } - storeMessage(report); // Save the report + storeMessage(report); } void TimeBasedSchedulingService::detailReportActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DetailReportActivitiesById); + if (!request.assertTC(ServiceType, MessageType::DetailReportActivitiesById)) { + return; + } - // Create the report message object of telemetry message subtype 10 for each activity - Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById); etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities> matchedActivities; - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID const auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - matchedActivities.push_back(*requestIDMatch); // Save the matched activity + matchedActivities.push_back(*requestIDMatch); } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time + sortActivitiesReleaseTime(matchedActivities); - // todo: append sub-schedule and group ID if they are defined - report.appendUint16(static_cast<uint16_t>(matchedActivities.size())); - for (auto& match : matchedActivities) { - report.appendUint32(match.requestReleaseTime); // todo: Replace with the time parser - report.appendString(MessageParser::composeECSS(match.request)); - } - storeMessage(report); // Save the report + timeBasedScheduleDetailReport(matchedActivities); } void TimeBasedSchedulingService::summaryReportActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById); + if (!request.assertTC(ServiceType, MessageType::ActivitiesSummaryReportById)) { + return; + } - // Create the report message object of telemetry message subtype 13 for each activity - Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport); etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities> matchedActivities; - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; @@ -246,18 +238,24 @@ void TimeBasedSchedulingService::summaryReportActivitiesByID(Message& request) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time + sortActivitiesReleaseTime(matchedActivities); + + timeBasedScheduleSummaryReport(matchedActivities); +} + +void TimeBasedSchedulingService::timeBasedScheduleSummaryReport(const etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities>& listOfActivities) { + Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport); // todo: append sub-schedule and group ID if they are defined - report.appendUint16(static_cast<uint16_t>(matchedActivities.size())); - for (auto& match : matchedActivities) { + report.appendUint16(static_cast<uint16_t>(listOfActivities.size())); + for (const auto& match: listOfActivities) { // todo: append sub-schedule and group ID if they are defined - report.appendUint32(match.requestReleaseTime); + report.appendDefaultCUCTimeStamp(match.requestReleaseTime); report.appendUint8(match.requestID.sourceID); report.appendUint16(match.requestID.applicationID); report.appendUint16(match.requestID.sequenceCount); } - storeMessage(report); // Save the report + storeMessage(report); } void TimeBasedSchedulingService::execute(Message& message) { diff --git a/src/Time/UTCTimestamp.cpp b/src/Time/UTCTimestamp.cpp index 072d6b67ff32e1db02d1a835a6ae61c3d43d9a1f..f534455f81b67ed19d8fa4bb4267a66324caa401 100644 --- a/src/Time/UTCTimestamp.cpp +++ b/src/Time/UTCTimestamp.cpp @@ -131,3 +131,33 @@ bool UTCTimestamp::operator<=(const UTCTimestamp& Date) const { bool UTCTimestamp::operator>=(const UTCTimestamp& Date) const { return ((*this > Date) || (*this == Date)); } +void UTCTimestamp::repair() { + using namespace Time; + + if (second > Time::SecondsPerMinute) { + second -= Time::SecondsPerMinute; + minute++; + } + + const auto MinutesPerHour = SecondsPerHour / SecondsPerMinute; + if (minute >= MinutesPerHour) { + minute -= MinutesPerHour; + hour++; + } + + const auto HoursPerDay = SecondsPerDay / SecondsPerHour; + if (hour >= HoursPerDay) { + hour -= HoursPerDay; + day++; + } + + if (day > daysOfMonth()) { + day -= daysOfMonth(); + month++; + } + + if (month > MonthsPerYear) { + month -= MonthsPerYear; + year++; + } +} diff --git a/test/ErrorHandler.cpp b/test/ErrorHandlerTests.cpp similarity index 99% rename from test/ErrorHandler.cpp rename to test/ErrorHandlerTests.cpp index 2db17bf40a7f120fa9e8e6b86e8df02c3d815d04..b5924e1803e5af7119fb87877085a92390103bd9 100644 --- a/test/ErrorHandler.cpp +++ b/test/ErrorHandlerTests.cpp @@ -1,7 +1,7 @@ -#include <catch2/catch.hpp> #include <ErrorHandler.hpp> -#include "Services/ServiceTests.hpp" +#include <catch2/catch_all.hpp> #include "Services/RequestVerificationService.hpp" +#include "Services/ServiceTests.hpp" TEST_CASE("Error: Failed Acceptance", "[errors]") { Message failedMessage(38, 32, Message::TC, 47); diff --git a/test/Helpers/CRCHelper.cpp b/test/Helpers/CRCHelperTests.cpp similarity index 83% rename from test/Helpers/CRCHelper.cpp rename to test/Helpers/CRCHelperTests.cpp index 5988eb3ee1be035397b87530493ef7d9e7b8526a..7d94c4f838bd26741a4556df41340f2e9b0289f4 100644 --- a/test/Helpers/CRCHelper.cpp +++ b/test/Helpers/CRCHelperTests.cpp @@ -1,11 +1,11 @@ -#include "catch2/catch.hpp" #include "Helpers/CRCHelper.hpp" +#include "catch2/catch_all.hpp" TEST_CASE("CRC calculation - Basic String tests") { - CHECK(CRCHelper::calculateCRC((uint8_t*)"Raccoon Squad!", 14) == 0x08FC); - CHECK(CRCHelper::calculateCRC((uint8_t*)"ASAT", 4) == 0xBFFA); - CHECK(CRCHelper::calculateCRC((uint8_t*)"All your space are belong to us", 31) == 0x545F); - CHECK(CRCHelper::calculateCRC((uint8_t*)"SPAAAAAAAAACE!", 14) == 0xB441); + CHECK(CRCHelper::calculateCRC((uint8_t*) "Raccoon Squad!", 14) == 0x08FC); + CHECK(CRCHelper::calculateCRC((uint8_t*) "ASAT", 4) == 0xBFFA); + CHECK(CRCHelper::calculateCRC((uint8_t*) "All your space are belong to us", 31) == 0x545F); + CHECK(CRCHelper::calculateCRC((uint8_t*) "SPAAAAAAAAACE!", 14) == 0xB441); } TEST_CASE("CRC calculation - Basic byte tests") { diff --git a/test/Helpers/PacketStore.cpp b/test/Helpers/PacketStoreTests.cpp similarity index 96% rename from test/Helpers/PacketStore.cpp rename to test/Helpers/PacketStoreTests.cpp index 072303a5b3d8a4c5e92316eb3a446b9ff6e5eb21..ea50e7ed93db25c9ba3b2821975780a80cf8edab 100644 --- a/test/Helpers/PacketStore.cpp +++ b/test/Helpers/PacketStoreTests.cpp @@ -1,5 +1,5 @@ #include "Helpers/PacketStore.hpp" -#include "catch2/catch.hpp" +#include "catch2/catch_all.hpp" TEST_CASE("Counting a packet store's size in bytes") { SECTION("Correct counting of size in bytes") { diff --git a/test/Helpers/PlatformParameters.cpp b/test/Helpers/PlatformParameters.cpp index adccb72716c9eb5979d953c3f97a1ba63858335b..833254a181d9b51481cd59923aba9cc2d12b41d1 100644 --- a/test/Helpers/PlatformParameters.cpp +++ b/test/Helpers/PlatformParameters.cpp @@ -1,7 +1,7 @@ #include "Platform/x86/Parameters/PlatformParameters.hpp" -#include "catch2/catch.hpp" #include "../Services/ServiceTests.hpp" #include "ECSS_Definitions.hpp" +#include "catch2/catch_all.hpp" TEST_CASE("Getting a reference of a parameter") { SECTION("Valid parameter requested") { diff --git a/test/Helpers/Statistic.cpp b/test/Helpers/StatisticTests.cpp similarity index 78% rename from test/Helpers/Statistic.cpp rename to test/Helpers/StatisticTests.cpp index 5db84a3876685ace98f57408b009d48e4eb8fb3f..5fb8a3e8dd1b93ec2a72cd3f1adcdd0a68f53b22 100644 --- a/test/Helpers/Statistic.cpp +++ b/test/Helpers/StatisticTests.cpp @@ -1,6 +1,6 @@ #include "Helpers/Statistic.hpp" #include "Services/ParameterStatisticsService.hpp" -#include "catch2/catch.hpp" +#include "catch2/catch_all.hpp" TEST_CASE("Statistics updating function") { SECTION("values in one by one") { @@ -17,25 +17,25 @@ TEST_CASE("Statistics updating function") { stat1.updateStatistics(value); CHECK(stat1.max == 3.24); CHECK(stat1.min == 1.3); - CHECK(stat1.mean == Approx(2.27).epsilon(0.01)); + CHECK(stat1.mean == Catch::Approx(2.27).epsilon(0.01)); value = 5.8; stat1.updateStatistics(value); CHECK(stat1.max == 5.8); CHECK(stat1.min == 1.3); - CHECK(stat1.mean == Approx(3.446).epsilon(0.001)); + CHECK(stat1.mean == Catch::Approx(3.446).epsilon(0.001)); } SECTION("Multiple consecutive values") { double values[10] = {8.3001, 2.3, 6.4, 1.1, 8.35, 3.4, 6, 8.31, 4.7, 1.09}; Statistic stat; CHECK(stat.statisticsAreInitialized()); - for (auto& value : values) { + for (auto& value: values) { stat.updateStatistics(value); } CHECK(stat.max == 8.35); CHECK(stat.min == 1.09); - CHECK(stat.mean == Approx(4.99501).epsilon(0.00001)); + CHECK(stat.mean == Catch::Approx(4.99501).epsilon(0.00001)); } } @@ -46,17 +46,17 @@ TEST_CASE("Appending of statistics to message") { double values[10] = {8.3001, 2.3, 6.4, 1.1, 8.35, 3.4, 6, 8.31, 4.7, 1.09}; Statistic stat; CHECK(stat.statisticsAreInitialized()); - for (auto& value : values) { + for (auto& value: values) { stat.updateStatistics(value); } stat.appendStatisticsToMessage(report); REQUIRE(report.readFloat() == 8.35f); - REQUIRE(report.readUint32() == 0); // No CUC integration yet + REQUIRE(report.readUint32() == 86769000); // dummy time value REQUIRE(report.readFloat() == 1.09f); - REQUIRE(report.readUint32() == 0); - REQUIRE(report.readFloat() == Approx(4.99501).epsilon(0.00001)); - REQUIRE(report.readFloat() == Approx(2.76527).epsilon(0.00001)); + REQUIRE(report.readUint32() == 86769000); + REQUIRE(report.readFloat() == Catch::Approx(4.99501).epsilon(0.00001)); + REQUIRE(report.readFloat() == Catch::Approx(2.76527).epsilon(0.00001)); } } @@ -74,11 +74,11 @@ TEST_CASE("Reset statistics") { double values[10] = {8.3001, 2.3, 6.4, 1.1, 8.35, 3.4, 6, 8.31, 4.7, 1.09}; Statistic stat; CHECK(stat.statisticsAreInitialized()); - for (auto& value : values) { + for (auto& value: values) { stat.updateStatistics(value); } CHECK(not stat.statisticsAreInitialized()); stat.resetStatistics(); REQUIRE(stat.statisticsAreInitialized()); } -} \ No newline at end of file +} diff --git a/test/MessageParser.cpp b/test/MessageParserTests.cpp similarity index 59% rename from test/MessageParser.cpp rename to test/MessageParserTests.cpp index 93cfa695a26cb1a97455db4c6e6ddf4310ec98c5..b513b90945df366054584b45e5579207100bd89e 100644 --- a/test/MessageParser.cpp +++ b/test/MessageParserTests.cpp @@ -1,8 +1,9 @@ -#include <catch2/catch.hpp> +#include "MessageParser.hpp" #include <Message.hpp> +#include <catch2/catch_all.hpp> #include <cstring> #include "Helpers/CRCHelper.hpp" -#include "MessageParser.hpp" +#include "Helpers/TimeGetter.hpp" TEST_CASE("TC message parsing", "[MessageParser]") { uint8_t packet[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x0a, 0x20, 0x81, 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; @@ -18,8 +19,8 @@ TEST_CASE("TC message parsing", "[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}; + uint8_t wantedPacket[] = {0x18, 0x07, 0xe0, 0x07, 0x00, 0x09, 0x20, 0x81, + 0x1f, 0x00, 0x07, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; Message message; message.packetType = Message::TC; @@ -43,13 +44,20 @@ TEST_CASE("TC Message parsing into a string", "[MessageParser]") { CHECK(createdPacket.size() == 16); CHECK((createdPacket == String<16>(wantedPacket))); #endif + } TEST_CASE("TM message parsing", "[MessageParser]") { - uint8_t packet[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x0c, 0x20, 0x16, 0x11, - 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; + uint8_t packet[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x12, 0x20, 0x16, + 0x11,0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, + 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; + uint32_t time = TimeGetter::getCurrentTimeDefaultCUC().formatAsBytes(); + packet[13] = (time >> 24) & 0xFF; + packet[14] = (time >> 16) & 0xFF; + packet[15] = (time >> 8) & 0xFF; + packet[16] = (time) & 0xFF; - Message message = MessageParser::parse(packet, 18); + Message message = MessageParser::parse(packet, 24); CHECK(message.packetType == Message::TM); CHECK(message.applicationId == 2); CHECK(message.packetSequenceCount == 77); @@ -57,11 +65,23 @@ TEST_CASE("TM message parsing", "[MessageParser]") { CHECK(message.serviceType == 22); CHECK(message.messageType == 17); CHECK(memcmp(message.data, "hellohi", 7) == 0); + + // Add ECSS and CCSDS header + String<CCSDSMaxMessageSize> createdPacket = MessageParser::compose(message); + uint32_t messageTime = (createdPacket[16] & 0xFF) | ((createdPacket[15] & 0xFF ) << 8) | ((createdPacket[14] & 0xFF) << 16) | ((createdPacket[13] & 0xFF ) << 24); + CHECK(messageTime == time); + } 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}; + uint8_t wantedPacket[] = {0x08, 0x02, 0xc0, 0x4d, 0x00, 0x11, 0x20, 0x16, + 0x11,0x00, 0x00,0x00, 0x02,0x00, 0x00,0x00, + 0x00, 0x68,0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; + uint32_t time = TimeGetter::getCurrentTimeDefaultCUC().formatAsBytes(); + wantedPacket[13] = (time >> 24) & 0xFF; + wantedPacket[14] = (time >> 16) & 0xFF; + wantedPacket[15] = (time >> 8) & 0xFF; + wantedPacket[16] = (time) & 0xFF; Message message; message.packetType = Message::TM; @@ -75,14 +95,14 @@ TEST_CASE("TM Message parsing into a string", "[MessageParser]") { String<CCSDSMaxMessageSize> createdPacket = MessageParser::compose(message); #if ECSS_CRC_INCLUDED - CHECK(createdPacket.size() == 20); - CHECK(memcmp(createdPacket.data(), wantedPacket, 18) == 0); + CHECK(createdPacket.size() == 26); + CHECK(memcmp(createdPacket.data(), wantedPacket, 24) == 0); const uint8_t* packet = reinterpret_cast<uint8_t*>(&createdPacket.data()[0]); - uint8_t crc_verification = CRCHelper::validateCRC(packet, 20); + uint8_t crc_verification = CRCHelper::validateCRC(packet, 26); CHECK(crc_verification == 0); #else - CHECK(createdPacket.size() == 18); - CHECK((createdPacket == String<18>(wantedPacket))); + CHECK(createdPacket.size() == 24); + CHECK((createdPacket == String<24>(wantedPacket))); #endif } diff --git a/test/Message.cpp b/test/MessageTests.cpp similarity index 76% rename from test/Message.cpp rename to test/MessageTests.cpp index 84a0c58d2fe440b0aa5d7c1918a3608581d8145d..840d3e2eda157742842af5e106ef7b252afc1471 100644 --- a/test/Message.cpp +++ b/test/MessageTests.cpp @@ -1,8 +1,8 @@ -#include <catch2/catch.hpp> #include <Message.hpp> #include <ServicePool.hpp> -#include "etl/String.hpp" +#include <catch2/catch_all.hpp> #include "Services/EventReportService.hpp" +#include "etl/String.hpp" TEST_CASE("Message is usable", "[message]") { Message message(EventReportService::ServiceType, 17, Message::TC, 3); @@ -94,15 +94,13 @@ TEST_CASE("Requirement 7.3.3 (Enumerated)", "[message][ecss]") { TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); - message.append<char>(110); message.append<uint8_t>(230); message.append<uint16_t>(15933); message.append<uint32_t>(2000001); message.append<uint64_t>(12446744073709551615ULL); - REQUIRE(message.dataSize == 1 + 1 + 2 + 4 + 8); + REQUIRE(message.dataSize == 1 + 2 + 4 + 8); - CHECK(message.read<char>() == 110); CHECK(message.read<uint8_t>() == 230); CHECK(message.read<uint16_t>() == 15933); CHECK(message.read<uint32_t>() == 2000001); @@ -115,8 +113,8 @@ TEST_CASE("Requirement 7.3.4 (Unsigned integer)", "[message][ecss]") { * processors store data in little endian format. As a result, special care needs to be * taken for compliance. */ - CHECK(message.data[2] == 0x3e); - CHECK(message.data[3] == 0x3d); + CHECK(message.data[1] == 0x3e); + CHECK(message.data[2] == 0x3d); } } @@ -163,7 +161,54 @@ TEST_CASE("Test appending double") { REQUIRE(message.dataSize == 8); - CHECK(message.read<double>() == Approx(2.324).epsilon(0.0001)); + CHECK(message.read<double>() == Catch::Approx(2.324).epsilon(0.0001)); +} + +TEST_CASE("Test appending offset") { + Message message(0, 0, Message::TC, 0); + message.append<Time::RelativeTime>(555); + + REQUIRE(message.dataSize == 8); + + CHECK(message.read<Time::RelativeTime>() == 555); +} + +TEST_CASE("Test appending a CUC timestamp") { + using namespace Time; + + SECTION("Test 1") { + auto timeCUC = TimeGetter::getCurrentTimeDefaultCUC(); + REQUIRE(timeCUC.formatAsBytes() == 86769000); + + Message message(0, 0, Message::TC, 0); + message.appendDefaultCUCTimeStamp(timeCUC); + + REQUIRE(message.readUint32() == 86769000); + } + + SECTION("Test 2") { + DefaultCUC timeCUC(34511_t); + + Message message(0, 0, Message::TC, 0); + message.appendDefaultCUCTimeStamp(timeCUC); + + REQUIRE(message.readUint32() == 34511); + } +} + +TEST_CASE("Test reading a custom CUC timestamp") { + using namespace Time; + /** + * Append a custom CUC Time Stamp to a message object and check if is it read corretly + */ + DefaultCUC timeCUC(34511_t); + + Message message(0, 0, Message::TC, 0); + message.appendDefaultCUCTimeStamp(timeCUC); + + auto returnTimeCUC = message.readDefaultCUCTimeStamp(); + + REQUIRE(returnTimeCUC.formatAsBytes() == 34511); } TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") { @@ -350,3 +395,64 @@ TEST_CASE("Packet sequence counter", "[message]") { CHECK(message2.packetSequenceCount == 0); } } + +TEST_CASE("Storing and retrieving enums in Messages") { + enum ActiveBus : uint8_t { + Main = 0x0, + Redundant = 0x1 + }; + + SECTION("Using Enums in parameters") { + auto parameter1 = Parameter<ActiveBus>(Redundant); + auto parameter2 = Parameter<ActiveBus>(Main); + Message message; + + parameter1.appendValueToMessage(message); + parameter2.setValueFromMessage(message); + + CHECK(parameter1.getValue() == parameter2.getValue()); + } + + SECTION("Using Enums in variables") { + ActiveBus variable1 = Redundant; + ActiveBus variable2 = Main; + Message message; + + message.append(variable1); + variable2 = message.read<ActiveBus>(); + + CHECK(variable1 == variable2); + } + + SECTION("Another type of enum") { + enum Numbers : uint64_t { + First = 1577829600, + Second = 1667045679 + }; + + auto parameter1 = Parameter<Numbers>(First); + auto parameter2 = Parameter<Numbers>(Second); + Message message; + + parameter1.appendValueToMessage(message); + parameter2.setValueFromMessage(message); + + CHECK(parameter1.getValue() == parameter2.getValue()); + } + + SECTION("Another type of enum") { + enum MemoryPartitionUsed : bool { + first = false, + second = true + }; + + auto parameter3 = Parameter<MemoryPartitionUsed>(first); + auto parameter4 = Parameter<MemoryPartitionUsed>(second); + Message message; + + parameter3.appendValueToMessage(message); + parameter4.setValueFromMessage(message); + + CHECK(parameter3.getValue() == parameter4.getValue()); + } +} diff --git a/test/Parameters/LazyParameterTests.cpp b/test/Parameters/LazyParameterTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef291663fc136290ead8f9588aa1cc5c60524ee8 --- /dev/null +++ b/test/Parameters/LazyParameterTests.cpp @@ -0,0 +1,85 @@ +#include "../Services/ServiceTests.hpp" +#include "Helpers/LazyParameter.hpp" +#include "Message.hpp" +#include "catch2/catch_all.hpp" + +TEST_CASE("Lazy Parameter: Getter") { + LazyParameter<uint32_t> parameter; + CHECK(parameter.getValue().has_value() == false); + + parameter.setGetter([]() -> auto{ + static uint32_t value = 99100; + return value++; + }); + + CHECK(parameter.getValue().value() == 99100); + CHECK(parameter.getValue().value() == 99101); + CHECK(parameter.getValueAsDouble() == 99102); + + parameter.unsetGetter(); + + CHECK(parameter.getValue().has_value() == false); + CHECK(parameter.getValueAsDouble() == 0); +} + +TEST_CASE("Lazy Parameter: Messages") { + const int16_t fallback = -1; + const int16_t value = 42; + + LazyParameter<int16_t> parameter( + []() -> auto{ + return value; + }, + fallback); + + SECTION("Getter works") { + Message message(0, 0, Message::TC); + parameter.appendValueToMessage(message); + + CHECK(message.dataSize == 2); + CHECK(message.readSint16() == value); + CHECK(ServiceTests::hasNoErrors()); + } + + SECTION("Getter broken") { + parameter.unsetGetter(); + + Message message(0, 0, Message::TC); + parameter.appendValueToMessage(message); + + CHECK(message.dataSize == 2); + CHECK(message.readSint16() == fallback); + CHECK(ServiceTests::thrownError(ErrorHandler::ParameterValueMissing)); + } + + SECTION("Attempt to write") { + Message message(0, 0, Message::TC); + message.appendSint16(99); + message.appendSint16(100); + + parameter.setValueFromMessage(message); + CHECK(ServiceTests::thrownError(ErrorHandler::ParameterReadOnly)); + + // Make sure that the appropriate amount of bytes has been skipped + CHECK(message.readSint16() == 100); + } +} + +TEST_CASE("Lazy Parameter: Complex Types") { + using namespace Time; + + LazyParameter<Time::DefaultCUC> parameter(Time::DefaultCUC(100_t)); + + SECTION("Default value") { + CHECK(parameter.getValue().has_value() == false); + } + + SECTION("Set value") { + parameter.setGetter([]() -> Time::DefaultCUC { + return Time::DefaultCUC(200_t); + }); + + CHECK(parameter.getValue().value() == Time::DefaultCUC{200_t}); + CHECK(parameter.getValueAsDouble() == 0); + } +} diff --git a/test/Parameters/NotifyParameterTests.cpp b/test/Parameters/NotifyParameterTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4950f7680e2ac53185632cda60247624f187dcf0 --- /dev/null +++ b/test/Parameters/NotifyParameterTests.cpp @@ -0,0 +1,63 @@ +#include "Helpers/NotifyParameter.hpp" +#include "Message.hpp" +#include "catch2/catch_all.hpp" + +TEST_CASE("Notify Parameter: Notifier") { + int counter = 0; + + NotifyParameter<uint32_t> parameter(0); + + SECTION("Notifier not set") { + parameter.setValueLoudly(1); + CHECK(counter == 0); + } + + parameter.setNotifier([&counter](auto) -> auto{ + counter++; + }); + CHECK(counter == 1); + + parameter.setValueLoudly(2); + CHECK(counter == 2); + + parameter.setValueLoudly(2); + CHECK(counter == 3); + + parameter.unsetNotifier(); + parameter.setValueLoudly(3); + CHECK(counter == 3); +} + +TEST_CASE("Notify Parameter: Messages") { + int16_t storage; + + NotifyParameter<uint32_t> parameter( + 0, [&storage](auto v) -> auto{ + storage = v; + }); + + Message message(0, 0, Message::TC); + message.appendUint32(184); + + parameter.setValueFromMessage(message); + + CHECK(storage == 184); +} + +TEST_CASE("Notify Parameter: Extra functionality") { + int counter = 0; + + NotifyParameter<uint32_t> parameter(0); + auto notifier = [&counter](auto) -> auto{ + counter++; + }; + + parameter.setNotifier(notifier); + CHECK(counter == 1); + + parameter.setNotifier(notifier, false); + CHECK(counter == 1); + + parameter.notify(); + CHECK(counter == 2); +} \ No newline at end of file diff --git a/test/Services/Parameter.cpp b/test/Parameters/ParameterTests.cpp similarity index 85% rename from test/Services/Parameter.cpp rename to test/Parameters/ParameterTests.cpp index 10c47ac78cebf73604199fa4aa3c4c56e6ee6fe0..d7b373f1289734445ccad5d589e2bfb4b791950e 100644 --- a/test/Services/Parameter.cpp +++ b/test/Parameters/ParameterTests.cpp @@ -1,8 +1,8 @@ -#include <iostream> -#include "catch2/catch.hpp" #include "Helpers/Parameter.hpp" -#include "Services/ParameterService.hpp" +#include <iostream> #include "Message.hpp" +#include "Services/ParameterService.hpp" +#include "catch2/catch_all.hpp" TEST_CASE("Parameter Append") { SECTION("Check correct appending") { @@ -49,24 +49,24 @@ TEST_CASE("Get value as double") { auto parameter1 = Parameter<uint8_t>(7); uint8_t value = 13; parameter1.setValue(value); - CHECK(parameter1.getValueAsDouble() == Approx(13.0).epsilon(0.1)); + CHECK(parameter1.getValueAsDouble() == Catch::Approx(13.0).epsilon(0.1)); } SECTION("uint16 to double") { auto parameter2 = Parameter<uint32_t>(8); uint16_t value = 264; parameter2.setValue(value); - CHECK(parameter2.getValueAsDouble() == Approx(264.0).epsilon(0.1)); + CHECK(parameter2.getValueAsDouble() == Catch::Approx(264.0).epsilon(0.1)); } SECTION("uint32 to double") { auto parameter3 = Parameter<uint32_t>(9); uint32_t value = 544; parameter3.setValue(value); - CHECK(parameter3.getValueAsDouble() == Approx(544.0).epsilon(0.1)); + CHECK(parameter3.getValueAsDouble() == Catch::Approx(544.0).epsilon(0.1)); } SECTION("float to double") { auto parameter4 = Parameter<float>(10); float value = 14.237; parameter4.setValue(value); - CHECK(parameter4.getValueAsDouble() == Approx(14.237).epsilon(0.001)); + CHECK(parameter4.getValueAsDouble() == Catch::Approx(14.237).epsilon(0.001)); } } diff --git a/test/Services/DummyService.cpp b/test/Services/DummyService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ff3549355b8386514e84f8cedafd7bbc937ecccf --- /dev/null +++ b/test/Services/DummyService.cpp @@ -0,0 +1,17 @@ +#include "Services/DummyService.hpp" +#include <catch2/catch_all.hpp> +#include "ServicePool.hpp" +#include "ServiceTests.hpp" + +DummyService& dummyService = Services.dummyService; + +TEST_CASE("Log string as message TM[128, 128]", "[service][st128]") { + etl::string<LOGGER_MAX_MESSAGE_SIZE> log = "An amazing log that is very informative"; + dummyService.logAsECSSMessage(log); + Message report = ServiceTests::get(0); + CHECK(report.serviceType == DummyService::ServiceType); + CHECK(report.messageType == DummyService::MessageType::LogString); + char logOutput[39]; + report.readString(logOutput, 39); + CHECK(memcmp(logOutput, "An amazing log that is very informative", 39) == 0); +} diff --git a/test/Services/EventActionService.cpp b/test/Services/EventActionService.cpp deleted file mode 100644 index 0d26065c77360f45dca03f13f0f1fe842064aa2c..0000000000000000000000000000000000000000 --- a/test/Services/EventActionService.cpp +++ /dev/null @@ -1,415 +0,0 @@ -#include <catch2/catch.hpp> -#include <Services/EventActionService.hpp> -#include <Message.hpp> -#include "ServiceTests.hpp" -#include <etl/String.hpp> -#include <cstring> -#include <ServicePool.hpp> - -EventActionService& eventActionService = Services.eventAction; - -TEST_CASE("Add event-action definitions TC[19,1]", "[service][st19]") { - - // Add a message that is too large to check for the corresponding error - Message message(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message.appendEnum16(0); - message.appendEnum16(1); - message.appendEnum16(1); - String<128> data = "0123456789012345678901234567890123456789012345678901234567890123456789"; - message.appendString(data); - - MessageParser::execute(message); - CHECK(ServiceTests::thrownError(ErrorHandler::MessageTooLarge)); - CHECK(ServiceTests::countErrors() == 1); - - // Add an event-action definition to check if the values are inserted correctly - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(0); - message1.appendEnum16(2); - message1.appendEnum16(1); - data = "01234"; - message1.appendString(data); - MessageParser::execute(message1); - - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(2)->second.applicationId == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(2)->second.eventDefinitionID == 2); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(2)->second.enabled == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(2)->second.request.compare(data) == 0); - - // Add a second event-action definition - Message message2(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message2.appendEnum16(1); - message2.appendEnum16(3); - message2.appendEnum16(1); - data = "456"; - message2.appendString(data); - MessageParser::execute(message2); - - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(3)->second.applicationId == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(3)->second.eventDefinitionID == 3); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(3)->second.enabled == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(3)->second.request.compare(data) == 0); - - // Adding the same message to check for error - Message message3(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message3.appendEnum16(1); - message3.appendEnum16(3); - message3.appendEnum16(1); - data = "456"; - message3.appendString(data); - MessageParser::execute(message3); - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionDefinitionIDExistsError)); - CHECK(ServiceTests::countErrors() == 2); -} - -TEST_CASE("Delete event-action definitions TC[19,2]", "[service][st19]") { - - // Add messages for the purpose of deleting them - Message message0(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message0.appendEnum16(1); - message0.appendEnum16(0); - message0.appendEnum16(1); - String<64> data = "0"; - message0.appendString(data); - MessageParser::execute(message0); - - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(1); - message1.appendEnum16(1); - message1.appendEnum16(1); - data = "1"; - message1.appendString(data); - MessageParser::execute(message1); - - Message message2(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message2.appendEnum16(1); - message2.appendEnum16(2); - message2.appendEnum16(1); - data = "2"; - message2.appendString(data); - MessageParser::execute(message2); - - Message message5(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); - message5.appendUint16(1); - message5.appendEnum16(1); - message5.appendEnum16(2); - message5.appendEnum16(1); - MessageParser::execute(message5); - - // Checking the values after deleting some definitions - - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.applicationId == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.eventDefinitionID == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.request.compare("0") == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 0); - - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.applicationId == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.eventDefinitionID == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.request.compare("1") == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 0); - - CHECK(eventActionService.eventActionDefinitionMap.find(2) == eventActionService.eventActionDefinitionMap.end()); - - // Enabling a definition to check for errors in the case of an attempt to delete it - Message message8(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message8.appendUint16(1); - message8.appendEnum16(1); - message8.appendEnum16(1); - message8.appendEnum16(1); - MessageParser::execute(message8); - - Message message6(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); - message6.appendUint16(1); - message6.appendEnum16(1); - message6.appendEnum16(1); - message6.appendEnum16(1); - MessageParser::execute(message6); - - // Checking for errors in the case mentioned above - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionDeleteEnabledDefinitionError)); - CHECK(ServiceTests::countErrors() == 1); - - // Checking for errors in the case of an unknown definition - Message message7(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); - message7.appendUint16(1); - message7.appendEnum16(1); - message7.appendEnum16(10); - message7.appendEnum16(1); - MessageParser::execute(message7); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventDefinitionError)); - CHECK(ServiceTests::countErrors() == 2); - - Message message9(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); - message9.appendUint16(1); - message9.appendEnum16(1); - message9.appendEnum16(1); - message9.appendEnum16(10); - MessageParser::execute(message9); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionIDError)); - CHECK(ServiceTests::countErrors() == 3); -} - -TEST_CASE("Delete all event-action definitions TC[19,3]", "[service][st19]") { - - // Adding event action definitions to delete them later - Message message0(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message0.appendEnum16(1); - message0.appendEnum16(0); - message0.appendEnum16(1); - String<64> data = "0"; - message0.appendString(data); - MessageParser::execute(message0); - - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(1); - message1.appendEnum16(1); - message1.appendEnum16(1); - data = "1"; - message1.appendString(data); - MessageParser::execute(message1); - - Message message2(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message2.appendEnum16(1); - message2.appendEnum16(2); - message2.appendEnum16(1); - data = "2"; - message2.appendString(data); - MessageParser::execute(message2); - - Message message3(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message3.appendEnum16(1); - message3.appendEnum16(3); - message3.appendEnum16(1); - data = "3"; - message3.appendString(data); - MessageParser::execute(message3); - - Message message4(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message4.appendEnum16(1); - message4.appendEnum16(4); - message4.appendEnum16(1); - data = "4"; - message4.appendString(data); - MessageParser::execute(message4); - - Message message(EventActionService::ServiceType, EventActionService::MessageType::DeleteAllEventAction, Message::TC, 0); - MessageParser::execute(message); - - // Checking the content of the map - for (int i = 0; i < 256; i++){ - CHECK(eventActionService.eventActionDefinitionMap.find(i) == eventActionService.eventActionDefinitionMap.end()); - } -} - -TEST_CASE("Enable event-action definitions TC[19,4]", "[service][st19]") { - - // Adding event action definitions to enable them - Message message0(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message0.appendEnum16(1); - message0.appendEnum16(0); - message0.appendEnum16(1); - String<64> data = "0"; - message0.appendString(data); - MessageParser::execute(message0); - - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(1); - message1.appendEnum16(1); - message1.appendEnum16(1); - data = "00"; - message1.appendString(data); - MessageParser::execute(message1); - - // Checking their enabled status - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 0); - - // Creating a message to enable the previous messages - Message message3(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message3.appendUint16(2); - message3.appendEnum16(1); - message3.appendEnum16(0); - message3.appendEnum16(1); - message3.appendEnum16(1); - message3.appendEnum16(1); - message3.appendEnum16(1); - MessageParser::execute(message3); - - // Checking if the messages are enabled - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 1); - - // Checking for errors in the case of an attempt to enable an unknown definition - Message message7(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message7.appendUint16(1); - message7.appendEnum16(1); - message7.appendEnum16(6); - MessageParser::execute(message7); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventDefinitionError)); - CHECK(ServiceTests::countErrors() == 1); - - // Checking for errors in the case of an attempt to enable an unknown definition - Message message8(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message8.appendUint16(1); - message8.appendEnum16(1); - message8.appendEnum16(1); - message8.appendEnum16(10); - MessageParser::execute(message8); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionIDError)); - CHECK(ServiceTests::countErrors() == 2); -} - -TEST_CASE("Disable event-action definitions TC[19,5]", "[service][st19]") { - - // Adding event action definitions to enable them - Message message0(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message0.appendEnum16(1); - message0.appendEnum16(0); - message0.appendEnum16(1); - String<64> data = "0"; - message0.appendString(data); - MessageParser::execute(message0); - - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(1); - message1.appendEnum16(1); - message1.appendEnum16(1); - data = "00"; - message1.appendString(data); - MessageParser::execute(message1); - - // Checking their enabled status - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 0); - - // Creating a message to enable the previous messages - Message message3(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message3.appendUint16(2); - message3.appendEnum16(1); - message3.appendEnum16(0); - message3.appendEnum16(1); - message3.appendEnum16(1); - message3.appendEnum16(1); - message3.appendEnum16(1); - MessageParser::execute(message3); - - // Checking if the messages are enabled - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 1); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 1); - - // Creating a message to enable the previous messages - Message message4(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); - message4.appendUint16(2); - message4.appendEnum16(1); - message4.appendEnum16(0); - message4.appendEnum16(1); - message4.appendEnum16(1); - message4.appendEnum16(1); - message4.appendEnum16(1); - MessageParser::execute(message4); - - // Checking if the messages are enabled - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(0)->second.enabled == 0); - CHECK(eventActionService.eventActionDefinitionMap.lower_bound(1)->second.enabled == 0); - - // Checking for errors in the case of an attempt to enable an unknown definition - Message message7(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); - message7.appendUint16(1); - message7.appendEnum16(1); - message7.appendEnum16(6); - MessageParser::execute(message7); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventDefinitionError)); - CHECK(ServiceTests::countErrors() == 1); - - // Checking for errors in the case of an attempt to enable an unknown definition - Message message8(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); - message8.appendUint16(1); - message8.appendEnum16(1); - message8.appendEnum16(1); - message8.appendEnum16(10); - MessageParser::execute(message8); - - CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionIDError)); - CHECK(ServiceTests::countErrors() == 2); -} - -TEST_CASE("Request event-action definition status TC[19,6]", "[service][st19]") { - - // Creating a request for a report on the event action definitions - Message message(EventActionService::ServiceType, EventActionService::MessageType::ReportStatusOfEachEventAction, Message::TC, 0); - MessageParser::execute(message); - REQUIRE(ServiceTests::hasOneMessage()); - - // Checking that the report was made - Message report = ServiceTests::get(0); - CHECK(report.messageType == 7); -} - -TEST_CASE("Event-action status report TM[19,7]", "[service][st19]") { - - // Adding event-action definitions to report them - Message message0(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message0.appendEnum16(1); - message0.appendEnum16(0); - message0.appendEnum16(1); - String<64> data = "0"; - message0.appendString(data); - MessageParser::execute(message0); - - Message message1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); - message1.appendEnum16(1); - message1.appendEnum16(2); - message1.appendEnum16(1); - data = "2"; - message1.appendString(data); - MessageParser::execute(message1); - - // Enablilng one of the two - Message message2(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); - message2.appendUint16(1); - message2.appendEnum16(1); - message2.appendEnum16(0); - message2.appendEnum16(1); - eventActionService.enableEventActionDefinitions(message2); - - eventActionService.eventActionStatusReport(); - REQUIRE(ServiceTests::hasOneMessage()); - - // Checking the contents of the report - Message report = ServiceTests::get(0); - CHECK(report.readUint16() == 2); - CHECK(report.readEnum16() == 1); - CHECK(report.readEnum16() == 0); - CHECK(report.readEnum16() == 1); - CHECK(report.readBoolean() == 1); - CHECK(report.readEnum16() == 1); - CHECK(report.readEnum16() == 2); - CHECK(report.readEnum16() == 1); - CHECK(report.readBoolean() == 0); -} - -TEST_CASE("Enable event-action function TC[19,8]", "[service][st19]") { - - // A message to enable event action function - - Message message(EventActionService::ServiceType, EventActionService::MessageType::EnableEventActionFunction, Message::TC, 0); - eventActionService.enableEventActionFunction(message); - CHECK(eventActionService.getEventActionFunctionStatus()); -} - -TEST_CASE("Disable event-action function TC[19,9]", "[service][st19]") { - - // A message to disable event action function - Message message(EventActionService::ServiceType, EventActionService::MessageType::DisableEventActionFunction, Message::TC, 0); - eventActionService.disableEventActionFunction(message); - CHECK(eventActionService.getEventActionFunctionStatus() == false); -} - -TEST_CASE("Execute a TC request", "[service][st19]"){ - -} diff --git a/test/Services/EventActionServiceTests.cpp b/test/Services/EventActionServiceTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4b2365af7905526c060a8bde494a603e1266ceff --- /dev/null +++ b/test/Services/EventActionServiceTests.cpp @@ -0,0 +1,534 @@ +#include <Message.hpp> +#include <ServicePool.hpp> +#include <Services/EventActionService.hpp> +#include <catch2/catch_all.hpp> +#include <etl/String.hpp> +#include "ServiceTests.hpp" + +EventActionService& eventActionService = Services.eventAction; + +/** + * Initializes 9 Event Action Definitions with eventDefinitionIDs = {0, 4, 2, 12, 1, 5, 8, 23, 3} + */ +void initializeEventActionDefinitions() { + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + uint8_t numberOfEventActionDefinitions = 9; + uint16_t applicationIDs[] = {1, 0, 1, 0, 0, 2, 0, 1, 0}; + uint16_t eventDefinitionIDs[] = {0, 4, 2, 12, 1, 5, 8, 23, 3}; + String<ECSSTCRequestStringSize> data[] = {"\0", "1", "\0", "3", "4", "5", "6", "7", "8"}; + addDefinitions.appendUint8(numberOfEventActionDefinitions); + for (auto i = 0; i < numberOfEventActionDefinitions; i++) { + addDefinitions.appendEnum16(applicationIDs[i]); + addDefinitions.appendEnum16(eventDefinitionIDs[i]); + addDefinitions.appendFixedString(data[i]); + } + MessageParser::execute(addDefinitions); + + REQUIRE(eventActionService.eventActionDefinitionMap.size() == 9); + for (auto i = 0; i < numberOfEventActionDefinitions; i++) { + REQUIRE(eventActionService.eventActionDefinitionMap.find(eventDefinitionIDs[i]) != eventActionService.eventActionDefinitionMap.end()); + } +} + +TEST_CASE("Add event-action definitions TC[19,1]", "[service][st19]") { + SECTION("Add an event-action definition to check if the values are inserted correctly") { + Message addDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinition.appendUint8(1); + addDefinition.appendEnum16(0); + addDefinition.appendEnum16(2); + String<ECSSTCRequestStringSize> data = "12345abcdefg"; + addDefinition.appendFixedString(data); + MessageParser::execute(addDefinition); + + auto element = eventActionService.eventActionDefinitionMap.find(2); + CHECK(element->second.applicationID == 0); + CHECK(element->second.eventDefinitionID == 2); + CHECK(!element->second.enabled); + for (auto i = 0; i < data.size(); ++i) { + CHECK(data[i] == element->second.request[i]); + } + } + + SECTION("Adding multiple event-action definitions for different events") { + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + uint8_t numberOfEventActionDefinitions = 3; + uint16_t applicationIDs[] = {0, 1, 2}; + uint16_t eventDefinitionIDs[] = {3, 5, 4}; + String<ECSSTCRequestStringSize> dataArray[] = {"123", "456", "789"}; + addDefinitions.appendUint8(numberOfEventActionDefinitions); + for (auto i = 0; i < numberOfEventActionDefinitions; i++) { + addDefinitions.appendEnum16(applicationIDs[i]); + addDefinitions.appendEnum16(eventDefinitionIDs[i]); + addDefinitions.appendFixedString(dataArray[i]); + } + MessageParser::execute(addDefinitions); + + for (auto i = 0; i < numberOfEventActionDefinitions; i++) { + auto element = eventActionService.eventActionDefinitionMap.find(eventDefinitionIDs[i]); + CHECK(element->second.applicationID == applicationIDs[i]); + CHECK(element->second.eventDefinitionID == eventDefinitionIDs[i]); + CHECK(element->second.request.substr(0, 3) == dataArray[i]); + CHECK(!element->second.enabled); + CHECK(element->second.request.size() == ECSSTCRequestStringSize); + } + + eventActionService.eventActionDefinitionMap.clear(); + ServiceTests::reset(); + } + + SECTION("Add an event definition ID that already exists") { + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinitions.appendUint8(2); + addDefinitions.appendEnum16(1); + addDefinitions.appendEnum16(3); + String<ECSSTCRequestStringSize> data = "123"; + addDefinitions.appendFixedString(data); + addDefinitions.appendEnum16(6); + addDefinitions.appendEnum16(3); + data = "123"; + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + addDefinitions.appendUint8(1); + addDefinitions.appendEnum16(3); + addDefinitions.appendEnum16(3); + data = "234"; + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + auto element = eventActionService.eventActionDefinitionMap.find(3); + CHECK(element->second.applicationID == 3); + CHECK(element->second.request.substr(0, 3) == data); + + + eventActionService.eventActionDefinitionMap.clear(); + ServiceTests::reset(); + } + + SECTION("Add an event action definition that is already enabled") { + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinitions.appendUint8(1); + addDefinitions.appendEnum16(0); + addDefinitions.appendEnum16(3); + String<ECSSTCRequestStringSize> data = "456"; + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + Message enableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + + enableDefinitions.appendUint8(1); + enableDefinitions.appendEnum16(0); + enableDefinitions.appendEnum16(3); + MessageParser::execute(enableDefinitions); + + addDefinitions.appendUint8(1); + addDefinitions.appendEnum16(5); + addDefinitions.appendEnum16(3); + data = "456"; + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionEnabledError)); + CHECK(ServiceTests::countErrors() == 1); + + eventActionService.eventActionDefinitionMap.clear(); + ServiceTests::reset(); + } + + SECTION("Add an event-action definition when the eventActionDefinitionMap is full") { + Message message(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + String<ECSSTCRequestStringSize> data = "123"; + message.appendFixedString(data); + uint16_t applicationID = 257; + + for (uint16_t eventDefinitionID = 0; eventDefinitionID < 100; ++eventDefinitionID) { + EventActionService::EventActionDefinition temp(--applicationID, eventDefinitionID, message); + eventActionService.eventActionDefinitionMap.insert(std::make_pair(eventDefinitionID, temp)); + message.resetRead(); + } + + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinitions.appendUint8(2); + addDefinitions.appendEnum16(1); + addDefinitions.appendEnum16(100); + addDefinitions.appendEnum16(0); + addDefinitions.appendEnum16(101); + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionDefinitionsMapIsFull)); + CHECK(ServiceTests::countErrors() == 2); + eventActionService.eventActionDefinitionMap.clear(); + } +} + +TEST_CASE("Enable event-action definitions TC[19,4]", "[service][st19]") { + SECTION("Simple enable some event-action definitions") { + initializeEventActionDefinitions(); + + auto element = eventActionService.eventActionDefinitionMap.find(4); + REQUIRE(!element->second.enabled); + REQUIRE(!eventActionService.eventActionDefinitionMap.find(1)->second.enabled); + + Message enableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinitions.appendUint8(3); + enableDefinitions.appendEnum16(0); + enableDefinitions.appendEnum16(4); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(2); + enableDefinitions.appendEnum16(0); + enableDefinitions.appendEnum16(1); + MessageParser::execute(enableDefinitions); + + CHECK(element->second.enabled); + CHECK(eventActionService.eventActionDefinitionMap.find(1)->second.enabled); + } + + SECTION("Trying to enable an unknown definition") { + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinition.appendUint8(1); + enableDefinition.appendUint16(1); + enableDefinition.appendEnum16(7); + MessageParser::execute(enableDefinition); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 1); + } + + SECTION("Trying to enable an existing event-action definition with the wrong event definition ID") { + Message enableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinitions.appendUint8(2); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(4); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(3); + MessageParser::execute(enableDefinitions); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 2); + + eventActionService.eventActionDefinitionMap.clear(); + } + + SECTION("Enable all event action definitions") { + initializeEventActionDefinitions(); + + Message enableAllDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableAllDefinitions.appendUint8(0); + MessageParser::execute(enableAllDefinitions); + + for (const auto& iterator: eventActionService.eventActionDefinitionMap) { + CHECK(iterator.second.enabled); + } + + eventActionService.eventActionDefinitionMap.clear(); + } +} + +TEST_CASE("Delete event-action definitions TC[19,2]", "[service][st19]") { + SECTION("Delete an event-action definition") { + initializeEventActionDefinitions(); + + Message deleteDefinition(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); + deleteDefinition.appendUint8(2); + deleteDefinition.appendEnum16(1); + deleteDefinition.appendEnum16(2); + deleteDefinition.appendEnum16(2); + deleteDefinition.appendEnum16(5); + MessageParser::execute(deleteDefinition); + + REQUIRE(eventActionService.eventActionDefinitionMap.count(0) == 1); + REQUIRE(eventActionService.eventActionDefinitionMap.count(2) == 0); + REQUIRE(eventActionService.eventActionDefinitionMap.count(1) == 1); + REQUIRE(eventActionService.eventActionDefinitionMap.count(5) == 0); + } + + SECTION("Trying to delete an enabled event-action definition") { + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinition.appendUint8(1); + enableDefinition.appendEnum16(0); + enableDefinition.appendEnum16(1); + MessageParser::execute(enableDefinition); + + Message deleteDefinition(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); + deleteDefinition.appendUint8(1); + deleteDefinition.appendEnum16(0); + deleteDefinition.appendEnum16(1); + MessageParser::execute(deleteDefinition); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionDeleteEnabledDefinitionError)); + CHECK(ServiceTests::countErrors() == 1); + } + + SECTION("Trying to delete an unknown event-action definition") { + Message deleteDefinition(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); + deleteDefinition.appendUint8(1); + deleteDefinition.appendEnum16(1); + deleteDefinition.appendEnum16(8); + MessageParser::execute(deleteDefinition); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 1); + } + + SECTION("Trying to delete an existing event-action definition with the wrong event definition ID") { + Message deleteDefinition(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 0); + deleteDefinition.appendUint8(2); + deleteDefinition.appendEnum16(0); + deleteDefinition.appendEnum16(0); + deleteDefinition.appendEnum16(0); + deleteDefinition.appendEnum16(5); + MessageParser::execute(deleteDefinition); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 2); + + eventActionService.eventActionDefinitionMap.clear(); + } +} + +TEST_CASE("Delete all event-action definitions TC[19,3]", "[service][st19]") { + initializeEventActionDefinitions(); + + REQUIRE(eventActionService.eventActionDefinitionMap.size() == 9); + + Message deleteAllDefinitions(EventActionService::ServiceType, EventActionService::MessageType::DeleteAllEventAction, Message::TC, 0); + MessageParser::execute(deleteAllDefinitions); + + CHECK(eventActionService.eventActionDefinitionMap.empty()); +} + +TEST_CASE("Disable event-action definitions TC[19,5]", "[service][st19]") { + SECTION("Simple disable some event-action definitions") { + initializeEventActionDefinitions(); + + Message enableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinitions.appendUint8(4); + enableDefinitions.appendEnum16(0); + enableDefinitions.appendEnum16(4); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(2); + enableDefinitions.appendEnum16(0); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(1); + enableDefinitions.appendEnum16(23); + MessageParser::execute(enableDefinitions); + + auto range = eventActionService.eventActionDefinitionMap.find(4); + REQUIRE(range->second.enabled); + REQUIRE(eventActionService.eventActionDefinitionMap.find(1)->second.enabled); + + Message disableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); + disableDefinitions.appendUint8(4); + disableDefinitions.appendEnum16(0); + disableDefinitions.appendEnum16(4); + disableDefinitions.appendEnum16(2); + disableDefinitions.appendEnum16(5); + disableDefinitions.appendEnum16(1); + disableDefinitions.appendEnum16(2); + disableDefinitions.appendEnum16(0); + disableDefinitions.appendEnum16(1); + MessageParser::execute(disableDefinitions); + + CHECK(!range->second.enabled); + CHECK(!eventActionService.eventActionDefinitionMap.find(5)->second.enabled); + CHECK(!eventActionService.eventActionDefinitionMap.find(1)->second.enabled); + } + + SECTION("Trying to disable unknown definitions") { + Message disableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); + disableDefinitions.appendUint8(2); + disableDefinitions.appendUint16(1); + disableDefinitions.appendEnum16(6); + disableDefinitions.appendUint16(0); + disableDefinitions.appendEnum16(13); + MessageParser::execute(disableDefinitions); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 2); + } + + SECTION("Trying to disable an existing event-action definition with the wrong event definition ID") { + Message disableDefinitions(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); + disableDefinitions.appendUint8(2); + disableDefinitions.appendEnum16(1); + disableDefinitions.appendEnum16(5); + disableDefinitions.appendEnum16(0); + disableDefinitions.appendEnum16(5); + MessageParser::execute(disableDefinitions); + + CHECK(ServiceTests::thrownError(ErrorHandler::EventActionUnknownEventActionDefinitionError)); + CHECK(ServiceTests::countErrors() == 2); + } + + SECTION("Disable all event action definitions") { + REQUIRE(!eventActionService.eventActionDefinitionMap.empty()); + + Message enableAllDefinitions(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableAllDefinitions.appendUint8(0); + MessageParser::execute(enableAllDefinitions); + + Message disableAllDefinitions(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); + disableAllDefinitions.appendUint8(0); + MessageParser::execute(disableAllDefinitions); + + for (const auto& iterator: eventActionService.eventActionDefinitionMap) { + CHECK(!iterator.second.enabled); + } + } +} + +TEST_CASE("Request event-action definition status TC[19,6]", "[service][st19]") { + Message statusRequest(EventActionService::ServiceType, EventActionService::MessageType::ReportStatusOfEachEventAction, Message::TC, 0); + MessageParser::execute(statusRequest); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + CHECK(report.messageType == 7); +} + +TEST_CASE("Event-action status report TM[19,7]", "[service][st19]") { + Message addDefinitions(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinitions.appendUint8(2); + addDefinitions.appendEnum16(1); + addDefinitions.appendEnum16(0); + String<ECSSTCRequestStringSize> data = "0"; + addDefinitions.appendFixedString(data); + addDefinitions.appendEnum16(1); + addDefinitions.appendEnum16(2); + data = "2"; + addDefinitions.appendFixedString(data); + MessageParser::execute(addDefinitions); + + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, + 0); + enableDefinition.appendUint8(1); + enableDefinition.appendEnum16(1); + enableDefinition.appendEnum16(0); + MessageParser::execute(enableDefinition); + + eventActionService.eventActionStatusReport(); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + CHECK(report.readUint16() == 2); + CHECK(report.readEnum16() == 1); + CHECK(report.readEnum16() == 0); + CHECK(report.readBoolean() == 1); + CHECK(report.readEnum16() == 1); + CHECK(report.readEnum16() == 2); + CHECK(report.readBoolean() == 0); +} + +TEST_CASE("Enable event-action function TC[19,8]", "[service][st19]") { + Message message(EventActionService::ServiceType, EventActionService::MessageType::EnableEventActionFunction, Message::TC, 0); + eventActionService.enableEventActionFunction(message); + CHECK(eventActionService.getEventActionFunctionStatus()); +} + +TEST_CASE("Disable event-action function TC[19,9]", "[service][st19]") { + Message message(EventActionService::ServiceType, EventActionService::MessageType::DisableEventActionFunction, Message::TC, 0); + eventActionService.disableEventActionFunction(message); + CHECK(eventActionService.getEventActionFunctionStatus() == false); +} + +TEST_CASE("Execute a TC request", "[service][st19]") { + SECTION("Action: Disable event-action definition") { + Message addDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinition.appendUint8(1); + addDefinition.appendEnum16(0); + addDefinition.appendEnum16(15); + + Message messageToBeExecuted(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 0); + messageToBeExecuted.appendUint8(1); + messageToBeExecuted.appendEnum16(0); + messageToBeExecuted.appendEnum16(15); + addDefinition.appendMessage(messageToBeExecuted, ECSSTCRequestStringSize); + MessageParser::execute(addDefinition); + + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinition.appendUint8(1); + enableDefinition.appendEnum16(0); + enableDefinition.appendEnum16(15); + MessageParser::execute(enableDefinition); + CHECK(eventActionService.eventActionDefinitionMap.find(15)->second.enabled); + + Message enableFunction(EventActionService::ServiceType, EventActionService::MessageType::EnableEventActionFunction, Message::TC, 0); + eventActionService.enableEventActionFunction(enableFunction); + REQUIRE(eventActionService.getEventActionFunctionStatus()); + + eventActionService.executeAction(15); + + CHECK(!eventActionService.eventActionDefinitionMap.find(15)->second.enabled); + CHECK(ServiceTests::countErrors() == 0); + + ServiceTests::reset(); + } + + SECTION("Action: Add event-action definition") { + Message addDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinition.appendUint8(1); + addDefinition.appendEnum16(0); + addDefinition.appendEnum16(9); + + Message messageToBeExecuted(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + messageToBeExecuted.appendUint8(1); + messageToBeExecuted.appendEnum16(0); + messageToBeExecuted.appendEnum16(74); + String<ECSSTCRequestStringSize> data = "12345"; + messageToBeExecuted.appendString(data); + addDefinition.appendMessage(messageToBeExecuted, ECSSTCRequestStringSize); + + MessageParser::execute(addDefinition); + + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinition.appendUint8(1); + enableDefinition.appendEnum16(0); + enableDefinition.appendEnum16(9); + MessageParser::execute(enableDefinition); + + eventActionService.executeAction(9); + + auto element = eventActionService.eventActionDefinitionMap.find(74); + CHECK(element->second.applicationID == 0); + CHECK(element->second.eventDefinitionID == 74); + CHECK(!element->second.enabled); + CHECK(element->second.request.size() == ECSSTCRequestStringSize); + } + + SECTION("Action: ParameterService::ReportParameterValues") { + Message addDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 0); + addDefinition.appendUint8(1); + addDefinition.appendEnum16(0); + addDefinition.appendEnum16(10); + + Message TCToBeExecuted = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); + TCToBeExecuted.appendUint16(3); + TCToBeExecuted.appendUint16(0); + TCToBeExecuted.appendUint16(1); + TCToBeExecuted.appendUint16(2); + addDefinition.appendMessage(TCToBeExecuted, ECSSTCRequestStringSize); + + MessageParser::execute(addDefinition); + + Message enableDefinition(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 0); + enableDefinition.appendUint8(1); + enableDefinition.appendEnum16(0); + enableDefinition.appendEnum16(10); + MessageParser::execute(enableDefinition); + + eventActionService.executeAction(10); + + Message report = ServiceTests::get(0); + CHECK(report.serviceType == ParameterService::ServiceType); + CHECK(report.messageType == ParameterService::MessageType::ParameterValuesReport); + CHECK(report.readUint16() == 3); + CHECK(report.readUint16() == 0); + CHECK(report.readUint8() == 3); + CHECK(report.readUint16() == 1); + CHECK(report.readUint16() == 7); + CHECK(report.readUint16() == 2); + CHECK(report.readUint32() == 10); + } +} \ No newline at end of file diff --git a/test/Services/EventReportService.cpp b/test/Services/EventReportServiceTests.cpp similarity index 99% rename from test/Services/EventReportService.cpp rename to test/Services/EventReportServiceTests.cpp index 25a6b54f8a28f689ffb40198fb7ae9c86e1fed58..47f9bfaed247ab89cd56b0cd69a39f58fe0548c9 100644 --- a/test/Services/EventReportService.cpp +++ b/test/Services/EventReportServiceTests.cpp @@ -1,8 +1,8 @@ -#include <catch2/catch.hpp> -#include <Services/EventReportService.hpp> #include <Message.hpp> -#include "ServiceTests.hpp" +#include <Services/EventReportService.hpp> +#include <catch2/catch_all.hpp> #include <cstring> +#include "ServiceTests.hpp" EventReportService& eventReportService = Services.eventReport; diff --git a/test/Services/FunctionManagementService.cpp b/test/Services/FunctionManagementServiceTests.cpp similarity index 94% rename from test/Services/FunctionManagementService.cpp rename to test/Services/FunctionManagementServiceTests.cpp index b6548d79b46bbcb482431b602130084874cd10b7..cf9f7a95db13cb5ebc5295c49cabcea0c78d7006 100644 --- a/test/Services/FunctionManagementService.cpp +++ b/test/Services/FunctionManagementServiceTests.cpp @@ -1,9 +1,9 @@ -#include "catch2/catch.hpp" #include "Services/FunctionManagementService.hpp" -#include "Services/RequestVerificationService.hpp" +#include <iostream> #include "ServicePool.hpp" #include "ServiceTests.hpp" -#include <iostream> +#include "Services/RequestVerificationService.hpp" +#include "catch2/catch_all.hpp" FunctionManagementService& fms = Services.functionManagement; @@ -20,7 +20,7 @@ TEST_CASE("ST[08] - Call Tests") { fms.include(String<ECSSFunctionNameLength>("test"), &test); Message msg(FunctionManagementService::ServiceType, FunctionManagementService::MessageType::PerformFunction, - Message::TC, 1); + Message::TC, 1); msg.appendFixedString(String<ECSSFunctionNameLength>("test")); msg.appendByte(199); @@ -52,8 +52,7 @@ TEST_CASE("ST[08] - Call Tests") { fms.include(String<ECSSFunctionNameLength>("test"), &test); Message msg(FunctionManagementService::ServiceType, FunctionManagementService::MessageType::PerformFunction, Message::TC, 1); msg.appendFixedString(String<ECSSFunctionNameLength>("test")); - msg.appendString(String<65> - ("eqrhjweghjhwqgthjkrghthjkdsfhgsdfhjsdjsfdhgkjdfsghfjdgkdfsgdfgsgd")); + msg.appendString(String<65>("eqrhjweghjhwqgthjkrghthjkdsfhgsdfhjsdjsfdhgkjdfsghfjdgkdfsgdfgsgd")); MessageParser::execute(msg); CHECK(ServiceTests::get(0).messageType == RequestVerificationService::MessageType::FailedStartOfExecution); diff --git a/test/Services/HousekeepingService.cpp b/test/Services/HousekeepingServiceTests.cpp similarity index 57% rename from test/Services/HousekeepingService.cpp rename to test/Services/HousekeepingServiceTests.cpp index 1aec796a3d4f5aaf3a1dc706e7e6adbf1f7fd64c..b1787fa73c14aa08fdb916d8ac528a0c50722806 100644 --- a/test/Services/HousekeepingService.cpp +++ b/test/Services/HousekeepingServiceTests.cpp @@ -1,8 +1,8 @@ -#include "Services/HousekeepingService.hpp" #include <iostream> #include "Message.hpp" #include "ServiceTests.hpp" -#include "catch2/catch.hpp" +#include "Services/HousekeepingService.hpp" +#include "catch2/catch_all.hpp" #include "etl/algorithm.h" HousekeepingService& housekeepingService = Services.housekeeping; @@ -252,8 +252,7 @@ TEST_CASE("Delete housekeeping structure") { TEST_CASE("Enable the periodic generation of housekeeping structures") { SECTION("Both valid and invalid structure IDs in same request") { initializeHousekeepingStructures(); - Message request2(HousekeepingService::ServiceType, - HousekeepingService::MessageType::EnablePeriodicHousekeepingParametersReport, Message::TC, 1); + Message request2(HousekeepingService::ServiceType, HousekeepingService::MessageType::EnablePeriodicHousekeepingParametersReport, Message::TC, 1); uint8_t numOfStructs = 5; uint8_t idsToEnable[5] = {1, 3, 4, 6, 7}; request2.appendUint8(numOfStructs); @@ -532,8 +531,12 @@ TEST_CASE("Append parameters in housekeeping report structure") { Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); - uint16_t numOfSimplyCommutatedParams = 13; - etl::vector<uint16_t, 13> simplyCommutatedIds = {0, 1, 2, 3, 6, 7, 8, 9, 12, 13, 14, 15, 16}; + uint16_t numOfSimplyCommutatedParams = 34; + + etl::vector<uint16_t, 34> simplyCommutatedIds; + for (uint16_t i = 0; i < 34; i++) { + simplyCommutatedIds.push_back(i); + } request.appendUint8(structId); request.appendUint16(numOfSimplyCommutatedParams); @@ -546,11 +549,11 @@ TEST_CASE("Append parameters in housekeeping report structure") { MessageParser::execute(request); - REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 10); - CHECK(ServiceTests::count() == 2); + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 30); + CHECK(ServiceTests::count() == 4); CHECK(ServiceTests::countThrownErrors( ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfSimplyCommutatedParameters) == 1); - CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 3); ServiceTests::reset(); Services.reset(); @@ -623,65 +626,470 @@ TEST_CASE("Reporting of housekeeping structure periodic properties") { } TEST_CASE("Periodically reporting Housekeeping Structures") { - uint32_t nextCollection = 0; + uint32_t nextCollection = 0; uint32_t currentTime = 0; uint32_t previousTime = 0; - SECTION("Non existent structures") { - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); - CHECK(ServiceTests::count() == 0); - CHECK(nextCollection == std::numeric_limits<uint32_t>::max()); - } - SECTION("Collection Intervals set to max") { - initializeHousekeepingStructures(); - for (auto &housekeepingStructure: housekeepingService.housekeepingStructures) { - housekeepingStructure.second.collectionInterval = std::numeric_limits<uint32_t>::max(); - } - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); - CHECK(ServiceTests::count() == 0); - CHECK(nextCollection == std::numeric_limits<uint32_t>::max()); - } - SECTION("Calculating properly defined collection intervals") { - housekeepingService.housekeepingStructures.at(0).collectionInterval = 900; - housekeepingService.housekeepingStructures.at(4).collectionInterval = 1000; - housekeepingService.housekeepingStructures.at(6).collectionInterval = 2700; - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + SECTION("Non existent structures") { + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(ServiceTests::count() == 0); + CHECK(nextCollection == std::numeric_limits<uint32_t>::max()); + } + SECTION("Collection Intervals set to max") { + initializeHousekeepingStructures(); + for (auto& housekeepingStructure: housekeepingService.housekeepingStructures) { + housekeepingStructure.second.collectionInterval = std::numeric_limits<uint32_t>::max(); + } + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(ServiceTests::count() == 0); + CHECK(nextCollection == std::numeric_limits<uint32_t>::max()); + } + SECTION("Calculating properly defined collection intervals") { + housekeepingService.housekeepingStructures.at(0).collectionInterval = 900; + housekeepingService.housekeepingStructures.at(4).collectionInterval = 1000; + housekeepingService.housekeepingStructures.at(6).collectionInterval = 2700; + housekeepingService.housekeepingStructures.at(0).periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures.at(4).periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures.at(6).periodicGenerationActionStatus = true; + + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(currentTime == 900); - CHECK(ServiceTests::count() == 0); - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(currentTime == 900); + CHECK(ServiceTests::count() == 0); + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(currentTime == 1000); - CHECK(ServiceTests::count() == 1); + CHECK(currentTime == 1000); + CHECK(ServiceTests::count() == 1); currentTime += 6; - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(currentTime == 1800); - CHECK(ServiceTests::count() == 2); - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(currentTime == 1800); + CHECK(ServiceTests::count() == 2); + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(ServiceTests::count() == 3); - CHECK(currentTime == 2000); + CHECK(ServiceTests::count() == 3); + CHECK(currentTime == 2000); currentTime += 15; - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(ServiceTests::count() == 4); - CHECK(currentTime == 2700); - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(ServiceTests::count() == 4); + CHECK(currentTime == 2700); + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); previousTime = currentTime; currentTime += nextCollection; - CHECK(ServiceTests::count() == 6); - CHECK(currentTime == 3000); - } - SECTION("Collection Intervals set to 0") { - for (auto &housekeepingStructure: housekeepingService.housekeepingStructures) { - housekeepingStructure.second.collectionInterval = 0; - } - nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); - CHECK(nextCollection == 0); - } -} \ No newline at end of file + CHECK(ServiceTests::count() == 6); + CHECK(currentTime == 3000); + } + SECTION("Collection Intervals set to 0") { + for (auto& housekeepingStructure: housekeepingService.housekeepingStructures) { + housekeepingStructure.second.periodicGenerationActionStatus = true; + housekeepingStructure.second.collectionInterval = 0; + } + nextCollection = housekeepingService.reportPendingStructures(currentTime, previousTime, nextCollection); + CHECK(nextCollection == 0); + } +} + +TEST_CASE("Check getPeriodicGenerationActionStatus function") { + SECTION("Returns periodic generation status") { + initializeHousekeepingStructures(); + housekeepingService.housekeepingStructures.at(4).periodicGenerationActionStatus = true; + + CHECK(housekeepingService.getPeriodicGenerationActionStatus(0) == false); + CHECK(housekeepingService.getPeriodicGenerationActionStatus(4) == true); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure ID in request") { + initializeHousekeepingStructures(); + + housekeepingService.getPeriodicGenerationActionStatus(1); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check getStruct function") { + SECTION("Returns periodic generation status") { + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + uint8_t idToCreate = 2; + uint32_t interval = 7; + uint16_t numOfSimplyCommutatedParams = 3; + etl::array<uint16_t, 3> simplyCommutatedIds = {4, 5, 8}; + + request.appendUint8(idToCreate); + request.appendUint32(interval); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id: simplyCommutatedIds) { + request.appendUint16(id); + } + + MessageParser::execute(request); + HousekeepingStructure newStruct = housekeepingService.housekeepingStructures[idToCreate]; + + CHECK(std::addressof(housekeepingService.housekeepingStructures.at(2)) == std::addressof(housekeepingService.getStruct(2)->get())); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure ID in request") { + initializeHousekeepingStructures(); + + housekeepingService.getStruct(1); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check getCollectionInterval function") { + SECTION("Returns Collection Interval") { + initializeHousekeepingStructures(); + + CHECK(housekeepingService.getCollectionInterval(0) == 7); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure ID in request") { + initializeHousekeepingStructures(); + + housekeepingService.getCollectionInterval(1); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check setPeriodicGenerationActionStatus function") { + SECTION("Set Periodic Generation Action Status") { + initializeHousekeepingStructures(); + + housekeepingService.setPeriodicGenerationActionStatus(0, true); + + CHECK(housekeepingService.housekeepingStructures.at(0).periodicGenerationActionStatus == true); + CHECK(housekeepingService.housekeepingStructures.at(4).periodicGenerationActionStatus == false); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure ID in request") { + initializeHousekeepingStructures(); + + housekeepingService.setPeriodicGenerationActionStatus(1, true); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check setCollectionInterval function") { + SECTION("Sets Collection Interval") { + initializeHousekeepingStructures(); + + housekeepingService.setCollectionInterval(0, 8); + + CHECK(housekeepingService.housekeepingStructures.at(0).collectionInterval == 8); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure ID in request") { + initializeHousekeepingStructures(); + + housekeepingService.setCollectionInterval(1, 8); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check structExists function") { + SECTION("Check if it returns correct boolean") { + initializeHousekeepingStructures(); + + CHECK(housekeepingService.structExists(0) == true); + CHECK(housekeepingService.structExists(1) == false); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasNonExistingStructExecutionError function") { + SECTION("Check if it returns correct boolean") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + initializeHousekeepingStructures(); + + CHECK(housekeepingService.hasNonExistingStructExecutionError(0, request) == false); + CHECK(housekeepingService.hasNonExistingStructExecutionError(1, request) == true); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasNonExistingStructError function") { + SECTION("Check if it returns correct boolean") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + initializeHousekeepingStructures(); + + CHECK(housekeepingService.hasNonExistingStructError(0, request) == false); + CHECK(housekeepingService.hasNonExistingStructError(1, request) == true); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::RequestedNonExistingStructure) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasNonExistingStructInternalError function") { + SECTION("Check if it returns correct boolean") { + initializeHousekeepingStructures(); + + CHECK(housekeepingService.hasNonExistingStructInternalError(0) == false); + CHECK(housekeepingService.hasNonExistingStructInternalError(1) == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasAlreadyExistingParameterError function") { + SECTION("Check if it returns correct boolean") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + uint8_t idToCreate = 2; + uint32_t interval = 7; + uint16_t numOfSimplyCommutatedParams = 3; + etl::array<uint16_t, 3> simplyCommutatedIds = {4, 5, 8}; + + request.appendUint8(idToCreate); + request.appendUint32(interval); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id: simplyCommutatedIds) { + request.appendUint16(id); + } + + MessageParser::execute(request); + HousekeepingStructure newStruct = housekeepingService.housekeepingStructures[idToCreate]; + + auto& housekeepingStructure = housekeepingService.getStruct(idToCreate)->get(); + + CHECK(housekeepingService.hasAlreadyExistingParameterError(housekeepingStructure, 6, request) == false); + CHECK(housekeepingService.hasAlreadyExistingParameterError(housekeepingStructure, 5, request) == true); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasAlreadyExistingStructError function") { + SECTION("Check if it returns correct boolean") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + initializeHousekeepingStructures(); + + CHECK(housekeepingService.hasAlreadyExistingStructError(1, request) == false); + CHECK(housekeepingService.hasAlreadyExistingStructError(0, request) == true); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedAlreadyExistingStructure) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasExceededMaxNumOfHousekeepingStructsError function") { + SECTION("Check if it returns correct boolean") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + uint8_t idsToCreate[11] = {1, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14}; + uint16_t numOfSimplyCommutatedParams = 3; + etl::vector<uint16_t, 3> simplyCommutatedIds = {8, 4, 5}; + uint32_t interval = 12; + + REQUIRE(housekeepingService.housekeepingStructures.size() == 0); + + for (auto& structId: idsToCreate) { + request.appendUint8(structId); + request.appendUint32(interval); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& parameterId: simplyCommutatedIds) { + request.appendUint16(parameterId); + } + MessageParser::execute(request); + + if (housekeepingService.housekeepingStructures.size() < 9) { + CHECK(housekeepingService.hasExceededMaxNumOfHousekeepingStructsError(request) == false); + } + } + + REQUIRE(housekeepingService.housekeepingStructures.size() == 10); + + CHECK(housekeepingService.hasExceededMaxNumOfHousekeepingStructsError(request) == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfHousekeepingStructures) == 2); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasRequestedAppendToEnabledHousekeepingError function") { + SECTION("Error exists") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::EnablePeriodicHousekeepingParametersReport, Message::TC, 1); + // Enable 1 periodic struct with id=0 + HousekeepingStructure newStruct; + newStruct.structureId = 0; + newStruct.periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures.insert({0, newStruct}); + + request.appendUint8(1); + request.appendUint8(0); + MessageParser::execute(request); + + REQUIRE(housekeepingService.housekeepingStructures.at(0).periodicGenerationActionStatus); + Message request2(HousekeepingService::ServiceType, HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); + uint8_t structId = 0; + request2.appendUint8(structId); + MessageParser::execute(request2); + + CHECK(housekeepingService.hasRequestedAppendToEnabledHousekeepingError(newStruct, request) == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedAppendToEnabledHousekeeping) == 2); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Error doesn't exist") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::EnablePeriodicHousekeepingParametersReport, Message::TC, 1); + // Enable 1 periodic struct with id=0 + HousekeepingStructure newStruct; + newStruct.structureId = 0; + newStruct.periodicGenerationActionStatus = false; + housekeepingService.housekeepingStructures.insert({0, newStruct}); + + request.appendUint8(1); + request.appendUint8(0); + MessageParser::execute(request); + + REQUIRE(housekeepingService.housekeepingStructures.at(0).periodicGenerationActionStatus); + Message request2(HousekeepingService::ServiceType, HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); + uint8_t structId = 0; + request2.appendUint8(structId); + MessageParser::execute(request2); + + + CHECK(housekeepingService.hasRequestedAppendToEnabledHousekeepingError(newStruct, request) == false); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasRequestedDeletionOfEnabledHousekeepingError function") { + SECTION("Error exists") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::DeleteHousekeepingReportStructure, Message::TC, 1); + HousekeepingStructure periodicStruct; + periodicStruct.structureId = 4; + periodicStruct.periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures.insert({4, periodicStruct}); + + uint8_t numOfStructs = 1; + uint8_t structureId = 4; + request.appendUint8(numOfStructs); + request.appendUint8(structureId); + + MessageParser::execute(request); + + + CHECK(housekeepingService.hasRequestedDeletionOfEnabledHousekeepingError(4, request) == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedDeletionOfEnabledHousekeeping) == 2); + + ServiceTests::reset(); + Services.reset(); + } + SECTION("Error doesn't exists") { + Message request(HousekeepingService::ServiceType, HousekeepingService::MessageType::DeleteHousekeepingReportStructure, Message::TC, 1); + HousekeepingStructure periodicStruct; + periodicStruct.structureId = 4; + periodicStruct.periodicGenerationActionStatus = false; + housekeepingService.housekeepingStructures.insert({4, periodicStruct}); + + uint8_t numOfStructs = 1; + uint8_t structureId = 4; + request.appendUint8(numOfStructs); + request.appendUint8(structureId); + + MessageParser::execute(request); + + + CHECK(housekeepingService.hasRequestedDeletionOfEnabledHousekeepingError(4, request) == false); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Check hasExceededMaxNumOfSimplyCommutatedParamsError function") { + SECTION("Check if it returns correct boolean") { + initializeHousekeepingStructures(); + uint8_t structId = 6; + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); + + uint16_t numOfSimplyCommutatedParams = 34; + + etl::vector<uint16_t, 34> simplyCommutatedIds; + for (uint16_t i = 0; i < 34; i++) { + simplyCommutatedIds.push_back(i); + } + + request.appendUint8(structId); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id: simplyCommutatedIds) { + request.appendUint16(id); + + if (housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() < 30) { + CHECK(housekeepingService.hasExceededMaxNumOfSimplyCommutatedParamsError(housekeepingService.housekeepingStructures[structId], request) == false); + } + } + REQUIRE(housekeepingService.housekeepingStructures.find(structId) != housekeepingService.housekeepingStructures.end()); + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 3); + + MessageParser::execute(request); + + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 30); + + CHECK(housekeepingService.hasExceededMaxNumOfSimplyCommutatedParamsError(housekeepingService.housekeepingStructures[structId], request) == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfSimplyCommutatedParameters) == 2); + + ServiceTests::reset(); + Services.reset(); + } +} diff --git a/test/Services/LargePacketTransferService.cpp b/test/Services/LargePacketTransferServiceTests.cpp similarity index 96% rename from test/Services/LargePacketTransferService.cpp rename to test/Services/LargePacketTransferServiceTests.cpp index 100e7be06eefab28b11f39c6aa1dac58066f72eb..3cdb99433cb6420435bf3e2eb769dafdb6fa7418 100644 --- a/test/Services/LargePacketTransferService.cpp +++ b/test/Services/LargePacketTransferServiceTests.cpp @@ -1,10 +1,10 @@ -#include <catch2/catch.hpp> -#include <Services/LargePacketTransferService.hpp> #include <Message.hpp> -#include "ServiceTests.hpp" +#include <Services/LargePacketTransferService.hpp> +#include <catch2/catch_all.hpp> #include <cstring> #include <etl/String.hpp> #include "ECSS_Definitions.hpp" +#include "ServiceTests.hpp" LargePacketTransferService& lPT = Services.largePacketTransferService; @@ -70,19 +70,19 @@ TEST_CASE("Last Uplink Part TC[13,11]", "[service][st13]") { TEST_CASE("Split function", "[no][service]") { Message message(13, 0, Message::TC, 0); - for (uint16_t i = 0; i < 800; i++){ + for (uint16_t i = 0; i < 800; i++) { message.appendUint8(UINT8_MAX); } uint16_t largeMessageTransactionIdentifier = 1; lPT.split(message, largeMessageTransactionIdentifier); Message message5(13, 0, Message::TC, 0); - for (int i = 0; i < 4; i++){ + for (int i = 0; i < 4; i++) { uint16_t partSequenceNumber = i; CHECK(largeMessageTransactionIdentifier == ServiceTests::get(i).readUint16()); CHECK(partSequenceNumber == ServiceTests::get(i).readUint16()); CHECK(ECSSMaxFixedOctetStringSize == ServiceTests::get(i).readUint16()); - for (int j = 0; j < 256; j++){ + for (int j = 0; j < 256; j++) { message5.appendUint8(ServiceTests::get(i).readUint8()); } } diff --git a/test/Services/MemoryManagementService.cpp b/test/Services/MemoryManagementServiceTests.cpp similarity index 90% rename from test/Services/MemoryManagementService.cpp rename to test/Services/MemoryManagementServiceTests.cpp index 5e99f191027fae3b4d2ed6c9f9608b8a14a3aa74..2ad8097ee385bcc0a64ae88ad54da3ad60993571 100644 --- a/test/Services/MemoryManagementService.cpp +++ b/test/Services/MemoryManagementServiceTests.cpp @@ -1,8 +1,8 @@ -#include <catch2/catch.hpp> -#include <Services/MemoryManagementService.hpp> #include <Message.hpp> -#include "ServiceTests.hpp" +#include <Services/MemoryManagementService.hpp> +#include <catch2/catch_all.hpp> #include "Helpers/CRCHelper.hpp" +#include "ServiceTests.hpp" MemoryManagementService& memMangService = Services.memoryManagement; @@ -16,18 +16,20 @@ TEST_CASE("TC[6,2]", "[service][st06]") { Message receivedPacket = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::LoadRawMemoryDataAreas, Message::TC, 1); receivedPacket.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - receivedPacket.appendUint16(2); // Iteration count - receivedPacket.appendUint64(reinterpret_cast<uint64_t>(pStr)); // Start address + receivedPacket.appendUint16(2); // Iteration count + receivedPacket.appendUint64(reinterpret_cast<uint64_t>(pStr)); // Start address receivedPacket.appendOctetString(String<2>(data)); - receivedPacket.appendBits(16, CRCHelper::calculateCRC(data, 2)); // Append CRC + receivedPacket.appendBits(16, CRCHelper::calculateCRC(data, 2)); // Append CRC receivedPacket.appendUint64(reinterpret_cast<uint64_t>(pStr + 2)); // Start address - receivedPacket.appendOctetString(String<1>(data)); // Append CRC + receivedPacket.appendOctetString(String<1>(data)); // Append CRC receivedPacket.appendBits(16, CRCHelper::calculateCRC(data, 1)); MessageParser::execute(receivedPacket); CHECK(pStr[0] == 'h'); CHECK(pStr[1] == 'R'); CHECK(pStr[2] == 'h'); + + free(pStr); } TEST_CASE("TC[6,5]", "[service][st06]") { @@ -39,9 +41,9 @@ TEST_CASE("TC[6,5]", "[service][st06]") { uint16_t readSize = 0, checksum = 0; Message receivedPacket = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::DumpRawMemoryData, Message::TC, 1); - receivedPacket.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - receivedPacket.appendUint16(3); // Iteration count (Equal to 3 test strings) - receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_1)); // Start address + receivedPacket.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID + receivedPacket.appendUint16(3); // Iteration count (Equal to 3 test strings) + receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_1)); // Start address receivedPacket.appendUint16(sizeof(testString_1) / sizeof(testString_1[0])); // Data read length receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_2)); @@ -100,9 +102,9 @@ TEST_CASE("TC[6,9]", "[service][st06]") { uint16_t readSize = 0, checksum = 0; Message receivedPacket = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::CheckRawMemoryData, Message::TC, 1); - receivedPacket.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID - receivedPacket.appendUint16(2); // Iteration count - receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_1)); // Start address + receivedPacket.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID + receivedPacket.appendUint16(2); // Iteration count + receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_1)); // Start address receivedPacket.appendUint16(sizeof(testString_1) / sizeof(testString_1[0])); // Data read length receivedPacket.appendUint64(reinterpret_cast<uint64_t>(testString_2)); diff --git a/test/Services/OnBoardMonitoringService.cpp b/test/Services/OnBoardMonitoringServiceTests.cpp similarity index 99% rename from test/Services/OnBoardMonitoringService.cpp rename to test/Services/OnBoardMonitoringServiceTests.cpp index 548cf83969d2fe97e21987e877dc6b147e7b3147..6e9074823189f79d775de4565c1b2509aa62aa7e 100644 --- a/test/Services/OnBoardMonitoringService.cpp +++ b/test/Services/OnBoardMonitoringServiceTests.cpp @@ -1,10 +1,10 @@ -#include <catch2/catch.hpp> -#include <Services/OnBoardMonitoringService.hpp> #include <Message.hpp> -#include "ServiceTests.hpp" -#include <etl/array.h> -#include <etl/String.hpp> #include <ServicePool.hpp> +#include <Services/OnBoardMonitoringService.hpp> +#include <catch2/catch_all.hpp> +#include <etl/String.hpp> +#include <etl/array.h> +#include "ServiceTests.hpp" OnBoardMonitoringService& onBoardMonitoringService = Services.onBoardMonitoringService; diff --git a/test/Services/ParameterService.cpp b/test/Services/ParameterServiceTests.cpp similarity index 98% rename from test/Services/ParameterService.cpp rename to test/Services/ParameterServiceTests.cpp index 018bc9af98dabca06187836692cdf81695b5741c..fc80719b56751228fb6bfdf20658fbdf8ca1ff95 100644 --- a/test/Services/ParameterService.cpp +++ b/test/Services/ParameterServiceTests.cpp @@ -1,8 +1,8 @@ -#include "catch2/catch.hpp" -#include "Message.hpp" -#include "ServiceTests.hpp" #include "Services/ParameterService.hpp" +#include "Message.hpp" #include "Parameters/PlatformParameters.hpp" +#include "ServiceTests.hpp" +#include "catch2/catch_all.hpp" static void resetParameterValues() { PlatformParameters::parameter1.setValue(3); @@ -13,7 +13,7 @@ static void resetParameterValues() { TEST_CASE("Parameter Report Subservice") { SECTION("All requested parameters invalid") { Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, - Message::TC, 1); + Message::TC, ApplicationId); request.appendUint16(3); request.appendUint16(54432); request.appendUint16(60000); diff --git a/test/Services/ParameterStatisticsService.cpp b/test/Services/ParameterStatisticsServiceTests.cpp similarity index 66% rename from test/Services/ParameterStatisticsService.cpp rename to test/Services/ParameterStatisticsServiceTests.cpp index fbc1c361efd386cfd25a92d5a53c048d0212280c..25ae3c908379fff554f93e189be57bc00f29bfa7 100644 --- a/test/Services/ParameterStatisticsService.cpp +++ b/test/Services/ParameterStatisticsServiceTests.cpp @@ -1,8 +1,8 @@ #include <iostream> -#include "catch2/catch.hpp" +#include "ECSS_Definitions.hpp" #include "Message.hpp" #include "ServiceTests.hpp" -#include "ECSS_Definitions.hpp" +#include "catch2/catch_all.hpp" /** * System-statistics initialization, so there are actual statistics in the map to work with. @@ -27,12 +27,8 @@ void initializeStatistics(uint16_t interval1, uint16_t interval2) { Services.parameterStatistics.statisticsMap.insert({id2, stat2}); } -void resetSystem() { - Services.parameterStatistics.statisticsMap.clear(); -} - TEST_CASE("Reporting of statistics") { - SECTION("Report statistics, with auto statistic reset disabled") { + SECTION("Report statistics, with auto statistic reset disabled with TC") { initializeStatistics(6, 7); Message request = Message(ParameterStatisticsService::ServiceType, ParameterStatisticsService::MessageType::ReportParameterStatistics, Message::TC, 1); @@ -44,33 +40,33 @@ TEST_CASE("Reporting of statistics") { Message report = ServiceTests::get(0); CHECK(report.serviceType == ParameterStatisticsService::ServiceType); CHECK(report.messageType == ParameterStatisticsService::MessageType::ParameterStatisticsReport); - CHECK(report.readUint16() == 1); // start time - CHECK(report.readUint16() == 1); // end time - CHECK(report.readUint16() == 2); // number of parameters reported + CHECK(report.readUint32() == 86769000); // start time + CHECK(report.readUint32() == 86769000); // end time + CHECK(report.readUint16() == 2); // number of parameters reported // Parameter B - CHECK(report.readUint16() == 5); // ID-2 - CHECK(report.readUint16() == 6); // number of samples - CHECK(report.readFloat() == 13); // max value - CHECK(report.readUint32() == 0); // max time - CHECK(report.readFloat() == 3); // min value - CHECK(report.readUint32() == 0); // min time - CHECK(report.readFloat() == 8); // mean - CHECK(report.readFloat() == Approx(3.41565).epsilon(0.01)); + CHECK(report.readUint16() == 5); // ID-2 + CHECK(report.readUint16() == 6); // number of samples + CHECK(report.readFloat() == 13); // max value + CHECK(report.readUint32() == 86769000); // max time + CHECK(report.readFloat() == 3); // min value + CHECK(report.readUint32() == 86769000); // min time + CHECK(report.readFloat() == 8); // mean + CHECK(report.readFloat() == Catch::Approx(3.41565).epsilon(0.01)); // Parameter A - CHECK(report.readUint16() == 7); // ID-1 - CHECK(report.readUint16() == 3); // number of samples - CHECK(report.readFloat() == 5); // max value - CHECK(report.readUint32() == 0); // max time - CHECK(report.readFloat() == 1); // min value - CHECK(report.readUint32() == 0); // min time - CHECK(report.readFloat() == 3); // mean + CHECK(report.readUint16() == 7); // ID-1 + CHECK(report.readUint16() == 3); // number of samples + CHECK(report.readFloat() == 5); // max value + CHECK(report.readUint32() == 86769000); // max time + CHECK(report.readFloat() == 1); // min value + CHECK(report.readUint32() == 86769000); // min time + CHECK(report.readFloat() == 3); // mean CHECK(static_cast<int>(report.readFloat()) == 1); // stddev CHECK(not Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); CHECK(not Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); } - SECTION("Report statistics, with auto statistics reset enabled") { + SECTION("Report statistics, with auto statistics reset enabled with TC") { Message request = Message(ParameterStatisticsService::ServiceType, ParameterStatisticsService::MessageType::ReportParameterStatistics, Message::TC, 1); Services.parameterStatistics.hasAutomaticStatisticsReset = true; @@ -95,10 +91,66 @@ TEST_CASE("Reporting of statistics") { CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + } - resetSystem(); - ServiceTests::reset(); - Services.reset(); + ServiceTests::reset(); + + SECTION("Report statistics, with auto statistic reset disabled without TC") { + initializeStatistics(6, 7); + Services.parameterStatistics.reportParameterStatistics(false); + Services.parameterStatistics.hasAutomaticStatisticsReset = false; + + CHECK(ServiceTests::count() == 1); + + Message report = ServiceTests::get(0); + CHECK(report.serviceType == ParameterStatisticsService::ServiceType); + CHECK(report.messageType == ParameterStatisticsService::MessageType::ParameterStatisticsReport); + CHECK(report.readUint32() == 86769000); // start time + CHECK(report.readUint32() == 86769000); // end time + CHECK(report.readUint16() == 2); // number of parameters reported + // Parameter B + CHECK(report.readUint16() == 5); // ID-2 + CHECK(report.readUint16() == 6); // number of samples + CHECK(report.readFloat() == 13); // max value + CHECK(report.readUint32() == 86769000); // max time + CHECK(report.readFloat() == 3); // min value + CHECK(report.readUint32() == 86769000); // min time + CHECK(report.readFloat() == 8); // mean + CHECK(report.readFloat() == Catch::Approx(3.41565).epsilon(0.01)); + // Parameter A + CHECK(report.readUint16() == 7); // ID-1 + CHECK(report.readUint16() == 3); // number of samples + CHECK(report.readFloat() == 5); // max value + CHECK(report.readUint32() == 86769000); // max time + CHECK(report.readFloat() == 1); // min value + CHECK(report.readUint32() == 86769000); // min time + CHECK(report.readFloat() == 3); // mean + CHECK(static_cast<int>(report.readFloat()) == 1); // stddev + + CHECK(not Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(not Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + } + + SECTION("Report statistics, with auto statistics reset enabled without TC") { + Services.parameterStatistics.hasAutomaticStatisticsReset = true; + Services.parameterStatistics.reportParameterStatistics(false); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + } + + SECTION("Report statistics, with auto statistics reset disabled, but reset is given with args, without TC") { + Services.parameterStatistics.statisticsMap[5].mean = 5; + Services.parameterStatistics.statisticsMap[7].mean = 3; + Services.parameterStatistics.hasAutomaticStatisticsReset = false; + + CHECK(not Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(not Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + Services.parameterStatistics.reportParameterStatistics(true); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); } } @@ -116,9 +168,7 @@ TEST_CASE("Resetting the parameter statistics") { CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); - resetSystem(); ServiceTests::reset(); - Services.reset(); } SECTION("Reset without TC") { @@ -132,9 +182,7 @@ TEST_CASE("Resetting the parameter statistics") { CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } @@ -145,14 +193,14 @@ TEST_CASE("Enable the periodic reporting of statistics") { Message(ParameterStatisticsService::ServiceType, ParameterStatisticsService::MessageType::EnablePeriodicParameterReporting, Message::TC, 1); request.appendUint16(6); - Services.parameterStatistics.periodicStatisticsReportingStatus = false; - CHECK(Services.parameterStatistics.reportingInterval == 5); + Services.parameterStatistics.setPeriodicReportingStatus(false); + CHECK(Services.parameterStatistics.getReportingIntervalMs() == 700); MessageParser::execute(request); CHECK(ServiceTests::count() == 0); - CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == true); - CHECK(Services.parameterStatistics.reportingInterval == 6); + CHECK(Services.parameterStatistics.getPeriodicReportingStatus() == true); + CHECK(Services.parameterStatistics.getReportingIntervalMs() == 6); } SECTION("Invalid reporting interval requested") { @@ -160,18 +208,16 @@ TEST_CASE("Enable the periodic reporting of statistics") { Message(ParameterStatisticsService::ServiceType, ParameterStatisticsService::MessageType::EnablePeriodicParameterReporting, Message::TC, 1); request2.appendUint16(3); - Services.parameterStatistics.periodicStatisticsReportingStatus = false; - CHECK(Services.parameterStatistics.reportingInterval == 6); + Services.parameterStatistics.setPeriodicReportingStatus(false); + CHECK(Services.parameterStatistics.getReportingIntervalMs() == 6); MessageParser::execute(request2); CHECK(ServiceTests::count() == 1); CHECK(ServiceTests::countThrownErrors(ErrorHandler::InvalidSamplingRateError) == 1); - CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == false); - CHECK(Services.parameterStatistics.reportingInterval == 6); + CHECK(Services.parameterStatistics.getPeriodicReportingStatus() == false); + CHECK(Services.parameterStatistics.getReportingIntervalMs() == 6); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } @@ -181,20 +227,18 @@ TEST_CASE("Disabling the periodic reporting of statistics") { Message request = Message(ParameterStatisticsService::ServiceType, ParameterStatisticsService::MessageType::DisablePeriodicParameterReporting, Message::TC, 1); - Services.parameterStatistics.periodicStatisticsReportingStatus = true; + Services.parameterStatistics.setPeriodicReportingStatus(true); MessageParser::execute(request); - REQUIRE(Services.parameterStatistics.periodicStatisticsReportingStatus == false); + REQUIRE(Services.parameterStatistics.getPeriodicReportingStatus() == false); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } TEST_CASE("Add/Update statistics definitions") { SECTION("Update existing parameter statistic definition") { - initializeStatistics(7, 6); + initializeStatistics(750, 2400); Statistic newStatistic; newStatistic.setSelfSamplingInterval(0); Services.parameterStatistics.statisticsMap.insert({0, newStatistic}); @@ -206,7 +250,7 @@ TEST_CASE("Add/Update statistics definitions") { request.appendUint16(numOfIds); uint16_t paramId1 = 0; - uint16_t interval1 = 14; + uint16_t interval1 = 1400; request.appendUint16(paramId1); request.appendUint16(interval1); @@ -216,11 +260,9 @@ TEST_CASE("Add/Update statistics definitions") { REQUIRE(ServiceTests::count() == 0); CHECK(Services.parameterStatistics.statisticsMap.size() == 3); - CHECK(Services.parameterStatistics.statisticsMap[0].selfSamplingInterval == 14); + CHECK(Services.parameterStatistics.statisticsMap[0].selfSamplingInterval == 1400); - resetSystem(); ServiceTests::reset(); - Services.reset(); } SECTION("Add new statistic definition") { @@ -232,7 +274,7 @@ TEST_CASE("Add/Update statistics definitions") { request.appendUint16(numOfIds); uint16_t paramId1 = 1; - uint16_t interval1 = 32; + uint16_t interval1 = 3200; request.appendUint16(paramId1); request.appendUint16(interval1); @@ -242,15 +284,13 @@ TEST_CASE("Add/Update statistics definitions") { REQUIRE(ServiceTests::count() == 0); CHECK(Services.parameterStatistics.statisticsMap.size() == 3); - CHECK(Services.parameterStatistics.statisticsMap[1].selfSamplingInterval == 32); + CHECK(Services.parameterStatistics.statisticsMap[1].selfSamplingInterval == 3200); - resetSystem(); ServiceTests::reset(); - Services.reset(); } SECTION("All possible invalid requests combined with add/update") { - initializeStatistics(7, 6); + initializeStatistics(7000, 6000); Statistic newStatistic; newStatistic.setSelfSamplingInterval(0); Services.parameterStatistics.statisticsMap.insert({0, newStatistic}); @@ -268,12 +308,12 @@ TEST_CASE("Add/Update statistics definitions") { uint16_t paramId5 = ECSSParameterCount + 1; uint16_t paramId6 = 3; - uint16_t interval1 = 14; - uint16_t interval2 = 32; - uint16_t interval3 = 2; - uint16_t interval4 = 7; - uint16_t interval5 = 8; - uint16_t interval6 = 9; + uint16_t interval1 = 14000; + uint16_t interval2 = 32000; + uint16_t interval3 = 20; + uint16_t interval4 = 7000; + uint16_t interval5 = 8000; + uint16_t interval6 = 9000; request.appendUint16(paramId1); request.appendUint16(interval1); @@ -297,12 +337,10 @@ TEST_CASE("Add/Update statistics definitions") { CHECK(ServiceTests::countThrownErrors(ErrorHandler::InvalidSamplingRateError) == 1); CHECK(ServiceTests::countThrownErrors(ErrorHandler::SetNonExistingParameter) == 2); CHECK(ServiceTests::countThrownErrors(ErrorHandler::MaxStatisticDefinitionsReached) == 1); - CHECK(Services.parameterStatistics.statisticsMap[0].selfSamplingInterval == 14); - CHECK(Services.parameterStatistics.statisticsMap[1].selfSamplingInterval == 32); + CHECK(Services.parameterStatistics.statisticsMap[0].selfSamplingInterval == 14000); + CHECK(Services.parameterStatistics.statisticsMap[1].selfSamplingInterval == 32000); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } @@ -330,10 +368,10 @@ TEST_CASE("Delete statistics definitions") { request.appendUint16(id1); request.appendUint16(id2); - Services.parameterStatistics.periodicStatisticsReportingStatus = true; + Services.parameterStatistics.setPeriodicReportingStatus(true); MessageParser::execute(request); - CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == true); + CHECK(Services.parameterStatistics.getPeriodicReportingStatus() == true); CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 1); CHECK(Services.parameterStatistics.statisticsMap.size() == 2); } @@ -347,13 +385,10 @@ TEST_CASE("Delete statistics definitions") { MessageParser::execute(request); - CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == false); - CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 1); + CHECK(Services.parameterStatistics.getPeriodicReportingStatus() == false); CHECK(Services.parameterStatistics.statisticsMap.empty()); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } @@ -372,15 +407,13 @@ TEST_CASE("Parameter statistics definition report") { CHECK(ServiceTests::count() == 1); Message report = ServiceTests::get(0); - CHECK(report.readUint16() == 0); // Reporting interval - CHECK(report.readUint16() == 2); // Num of valid Ids - CHECK(report.readUint16() == 5); // Valid parameter ID - CHECK(report.readUint16() == 12); // Sampling interval + CHECK(report.readUint16() == 700); // Reporting interval + CHECK(report.readUint16() == 2); // Num of valid Ids + CHECK(report.readUint16() == 5); // Valid parameter ID + CHECK(report.readUint16() == 12); // Sampling interval CHECK(report.readUint16() == 7); CHECK(report.readUint16() == 0); - resetSystem(); ServiceTests::reset(); - Services.reset(); } } diff --git a/test/Services/RealTimeForwardingControl.cpp b/test/Services/RealTimeForwardingControl.cpp index dd1a3351766df0e610fb046a60d9eded54635edc..f648802ce5bac4983428b13198c55dc02f0a8570 100644 --- a/test/Services/RealTimeForwardingControl.cpp +++ b/test/Services/RealTimeForwardingControl.cpp @@ -3,9 +3,9 @@ #include "Message.hpp" #include "ServiceTests.hpp" #include "Services/RealTimeForwardingControlService.hpp" -#include "catch2/catch.hpp" +#include "catch2/catch_all.hpp" -RealTimeForwardingControlService& realTimeForwarding = Services.realTimeForwarding; +static RealTimeForwardingControlService& realTimeForwarding = Services.realTimeForwarding; uint8_t applications[] = {1}; uint8_t services[] = {3, 5}; @@ -307,7 +307,7 @@ TEST_CASE("Add report types to the Application Process Configuration") { realTimeForwarding.controlledApplications.push_back(applicationID); validReportTypes(request); - for (auto message: AllMessageTypes::messagesOfService[serviceType]) { + for (auto& message: AllMessageTypes::MessagesOfService.at(serviceType)) { realTimeForwarding.applicationProcessConfiguration.definitions[std::make_pair(applicationID, serviceType)] .push_back(message); } @@ -319,7 +319,7 @@ TEST_CASE("Add report types to the Application Process Configuration") { 2); REQUIRE( realTimeForwarding.applicationProcessConfiguration.definitions[std::make_pair(applicationID, serviceType)] - .size() == AllMessageTypes::messagesOfService[serviceType].size()); + .size() == AllMessageTypes::MessagesOfService.at(serviceType).size()); resetAppProcessConfiguration(); ServiceTests::reset(); @@ -345,8 +345,8 @@ TEST_CASE("Add report types to the Application Process Configuration") { REQUIRE(applicationProcessConfig.definitions[appServicePair1].empty()); REQUIRE(applicationProcessConfig.definitions[appServicePair2].empty()); - auto numOfMessages1 = AllMessageTypes::messagesOfService[serviceType1].size(); - auto numOfMessages2 = AllMessageTypes::messagesOfService[serviceType2].size(); + auto numOfMessages1 = AllMessageTypes::MessagesOfService.at(serviceType1).size(); + auto numOfMessages2 = AllMessageTypes::MessagesOfService.at(serviceType2).size(); for (uint8_t i = 0; i < numOfMessages1 - 1; i++) { applicationProcessConfig.definitions[appServicePair1].push_back(i); @@ -450,7 +450,7 @@ TEST_CASE("Add report types to the Application Process Configuration") { auto& applicationProcesses = realTimeForwarding.applicationProcessConfiguration.definitions; for (auto serviceType: services) { REQUIRE(applicationProcesses[std::make_pair(applicationID1, serviceType)].size() == - AllMessageTypes::messagesOfService[serviceType].size()); + AllMessageTypes::MessagesOfService.at(serviceType).size()); } resetAppProcessConfiguration(); @@ -491,11 +491,11 @@ TEST_CASE("Add report types to the Application Process Configuration") { for (auto& serviceType: allServices) { REQUIRE(definitions[std::make_pair(applicationID1, serviceType)].size() == - AllMessageTypes::messagesOfService[serviceType].size()); + AllMessageTypes::MessagesOfService.at(serviceType).size()); } for (auto& serviceType: services) { REQUIRE(definitions[std::make_pair(applicationID2, serviceType)].size() == - AllMessageTypes::messagesOfService[serviceType].size()); + AllMessageTypes::MessagesOfService.at(serviceType).size()); } resetAppProcessConfiguration(); @@ -520,7 +520,7 @@ TEST_CASE("Add report types to the Application Process Configuration") { for (auto serviceType: allServices) { REQUIRE(std::equal(definitions[std::make_pair(applicationID1, serviceType)].begin(), definitions[std::make_pair(applicationID1, serviceType)].end(), - AllMessageTypes::messagesOfService[serviceType].begin())); + AllMessageTypes::MessagesOfService.at(serviceType).begin())); } resetAppProcessConfiguration(); diff --git a/test/Services/RequestVerificationService.cpp b/test/Services/RequestVerificationServiceTests.cpp similarity index 56% rename from test/Services/RequestVerificationService.cpp rename to test/Services/RequestVerificationServiceTests.cpp index 223cd1169c9d2688083d4b2dd7090a1e6d5b8254..99e11ed9530281f06092805bb605575432c8ea9f 100644 --- a/test/Services/RequestVerificationService.cpp +++ b/test/Services/RequestVerificationServiceTests.cpp @@ -1,6 +1,6 @@ -#include <catch2/catch.hpp> -#include <Services/RequestVerificationService.hpp> #include <Message.hpp> +#include <Services/RequestVerificationService.hpp> +#include <catch2/catch_all.hpp> #include "ServiceTests.hpp" RequestVerificationService& reqVerifService = Services.requestVerification; @@ -13,17 +13,17 @@ TEST_CASE("TM[1,1]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulAcceptanceReport); + CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulAcceptanceReport); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 4); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count } TEST_CASE("TM[1,2]", "[service][st01]") { @@ -34,17 +34,17 @@ TEST_CASE("TM[1,2]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::FailedAcceptanceReport); + CHECK(response.messageType == RequestVerificationService::MessageType::FailedAcceptanceReport); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 6); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) - CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count CHECK(response.readEnum16() == ErrorHandler::UnknownAcceptanceError); // error code } @@ -56,17 +56,17 @@ TEST_CASE("TM[1,3]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulStartOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulStartOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 4); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count } TEST_CASE("TM[1,4]", "[service][st01]") { @@ -77,17 +77,17 @@ TEST_CASE("TM[1,4]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::FailedStartOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::FailedStartOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 6); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) - CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count CHECK(response.readEnum16() == ErrorHandler::UnknownExecutionStartError); // error code } @@ -99,18 +99,18 @@ TEST_CASE("TM[1,5]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulProgressOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulProgressOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 5); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count - CHECK(response.readByte() == 0); // step ID + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readByte() == 0); // step ID } TEST_CASE("TM[1,6]", "[service][st01]") { @@ -121,18 +121,18 @@ TEST_CASE("TM[1,6]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::FailedProgressOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::FailedProgressOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 7); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count - CHECK(response.readByte() == 0); // step ID + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readByte() == 0); // step ID CHECK(response.readEnum16() == ErrorHandler::UnknownExecutionProgressError); } @@ -144,17 +144,17 @@ TEST_CASE("TM[1,7]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulCompletionOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::SuccessfulCompletionOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 4); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count } TEST_CASE("TM[1,8]", "[service][st01]") { @@ -164,17 +164,17 @@ TEST_CASE("TM[1,8]", "[service][st01]") { Message response = ServiceTests::get(0); CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::FailedCompletionOfExecution); + CHECK(response.messageType == RequestVerificationService::MessageType::FailedCompletionOfExecution); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 6); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) - CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count CHECK(response.readEnum16() == ErrorHandler::UnknownExecutionCompletionError); // error code } @@ -186,16 +186,16 @@ TEST_CASE("TM[1,10]", "[service][st01]") { Message response = ServiceTests::get(0); // Checks for the data-members of the object response CHECK(response.serviceType == RequestVerificationService::ServiceType); - CHECK(response.messageType == RequestVerificationService::MessageType::FailedRoutingReport); + CHECK(response.messageType == RequestVerificationService::MessageType::FailedRoutingReport); CHECK(response.packetType == Message::TM); // packet type - CHECK(response.applicationId == 0); + CHECK(response.applicationId == ApplicationId); REQUIRE(response.dataSize == 6); // dataSize is the number of bytes of data array // Check for the value that is stored in <<data>> array(data-member of object response) - CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number - CHECK(response.readEnumerated(1) == Message::TC); // packet type - CHECK(response.readBits(1) == 1); // secondary header flag - CHECK(response.readEnumerated(11) == 3); // application process ID - CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags - CHECK(response.readBits(14) == 0); // packet sequence count + CHECK(response.readEnumerated(3) == CCSDSPacketVersion); // packet version number + CHECK(response.readEnumerated(1) == Message::TC); // packet type + CHECK(response.readBits(1) == 1); // secondary header flag + CHECK(response.readEnumerated(11) == 3); // application process ID + CHECK(response.readEnumerated(2) == ECSSSequenceFlags); // sequence flags + CHECK(response.readBits(14) == 0); // packet sequence count CHECK(response.readEnum16() == ErrorHandler::UnknownRoutingError); // error code } diff --git a/test/Services/ServiceTests.hpp b/test/Services/ServiceTests.hpp index d5b76b882b1f788b7da4267f5ab4d3f4841f5168..f45726d85f89bf15afa216d3fdadd684dec47239 100644 --- a/test/Services/ServiceTests.hpp +++ b/test/Services/ServiceTests.hpp @@ -84,13 +84,17 @@ public: return count() == 1; } + static void resetErrors() { + queuedMessages.clear(); + thrownErrors.clear(); + expectingErrors = false; + } + /** * Reset the testing environment, starting from zero for all parameters */ static void reset() { - queuedMessages.clear(); - thrownErrors.clear(); - expectingErrors = false; + resetErrors(); Services.reset(); } @@ -150,6 +154,19 @@ public: return thrownErrors.count(std::make_pair(errorSource, errorType)); } + + /** + * Get the list of all thrown errors + */ + static std::vector<std::pair<ErrorHandler::ErrorSource, uint16_t>> getThrownErrors() { + std::vector<std::pair<ErrorHandler::ErrorSource, uint16_t>> errors; + + for (auto error : thrownErrors) { + errors.push_back(error.first); + } + + return errors; + } }; #endif // ECSS_SERVICES_TESTS_SERVICES_SERVICETESTS_HPP diff --git a/test/Services/StorageAndRetrievalService.cpp b/test/Services/StorageAndRetrievalServiceTests.cpp similarity index 96% rename from test/Services/StorageAndRetrievalService.cpp rename to test/Services/StorageAndRetrievalServiceTests.cpp index fd7020db5c5f9deb294b670be75474fd0a45099b..55b2193be7bd0bbcbb9d0f94b658e0ce9c97d8c3 100644 --- a/test/Services/StorageAndRetrievalService.cpp +++ b/test/Services/StorageAndRetrievalServiceTests.cpp @@ -1,8 +1,8 @@ #include <iostream> -#include "catch2/catch.hpp" #include "Message.hpp" #include "ServiceTests.hpp" #include "Services/StorageAndRetrievalService.hpp" +#include "catch2/catch_all.hpp" StorageAndRetrievalService& storageAndRetrieval = Services.storageAndRetrieval; @@ -123,7 +123,7 @@ void padWithZeros(etl::array<String<ECSSPacketStoreIdSize>, 4>& packetStoreIds) uint8_t offsets[] = {3, 4, 5, 6}; int index = 0; // Padding every empty position with zeros, to avoid memory garbage collection, which leads to a faulty result. - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { uint8_t startingPosition = offsets[index++]; for (uint8_t i = startingPosition; i < ECSSPacketStoreIdSize; i++) { packetStoreId[i] = 0; @@ -134,16 +134,16 @@ void padWithZeros(etl::array<String<ECSSPacketStoreIdSize>, 4>& packetStoreIds) void addTelemetryPacketsInPacketStores() { auto packetStoreIds = validPacketStoreIds(); - for (auto& timestamp : timestamps1) { + for (auto& timestamp: timestamps1) { storageAndRetrieval.addTelemetryToPacketStore(packetStoreIds[0], timestamp); } - for (auto& timestamp : timestamps2) { + for (auto& timestamp: timestamps2) { storageAndRetrieval.addTelemetryToPacketStore(packetStoreIds[1], timestamp); } - for (auto& timestamp : timestamps3) { + for (auto& timestamp: timestamps3) { storageAndRetrieval.addTelemetryToPacketStore(packetStoreIds[2], timestamp); } - for (auto& timestamp : timestamps4) { + for (auto& timestamp: timestamps4) { storageAndRetrieval.addTelemetryToPacketStore(packetStoreIds[3], timestamp); } } @@ -242,7 +242,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -270,7 +270,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 0; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -297,7 +297,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -332,7 +332,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(not storageAndRetrieval.packetStoreExists(packetStoreId)); request.appendString(packetStoreId); } @@ -358,7 +358,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 0; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -394,7 +394,7 @@ TEST_CASE("Deleting packet stores") { uint16_t numOfPacketStores = 8; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -402,7 +402,7 @@ TEST_CASE("Deleting packet stores") { request.appendString(packetStoreId); } - for (auto& packetStoreId : wrongPacketStoreIds) { + for (auto& packetStoreId: wrongPacketStoreIds) { REQUIRE(not storageAndRetrieval.packetStoreExists(packetStoreId)); request.appendString(packetStoreId); } @@ -498,7 +498,7 @@ TEST_CASE("Enabling the storage of packet stores") { MessageParser::execute(request); CHECK(ServiceTests::count() == 0); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).storageStatus == true); } @@ -578,7 +578,7 @@ TEST_CASE("Disabling the storage of packet stores") { MessageParser::execute(request); CHECK(ServiceTests::count() == 0); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).storageStatus == false); } @@ -602,7 +602,7 @@ TEST_CASE("Changing the open retrieval start-time-tag") { request.appendUint32(startTimeTag); request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStartTimeTag == 0); storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; request.appendString(packetStoreId); @@ -677,7 +677,7 @@ TEST_CASE("Changing the open retrieval start-time-tag") { request.appendUint32(startTimeTag); request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStartTimeTag == 0); storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; } @@ -711,7 +711,7 @@ TEST_CASE("Resuming the open retrieval process") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; request.appendString(packetStoreId); @@ -787,7 +787,7 @@ TEST_CASE("Resuming the open retrieval process") { uint16_t numOfPacketStores = 0; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; } @@ -821,7 +821,7 @@ TEST_CASE("Suspending the open retrieval process") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::InProgress; request.appendString(packetStoreId); } @@ -894,7 +894,7 @@ TEST_CASE("Suspending the open retrieval process") { uint16_t numOfPacketStores = 0; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.packetStoreExists(packetStoreId)); storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::InProgress; } @@ -930,7 +930,7 @@ TEST_CASE("Starting the by-time-range retrieval of packet stores") { uint32_t timeTags2[4] = {60, 70, 80, 90}; int index = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; request.appendString(packetStoreId); @@ -1027,7 +1027,7 @@ TEST_CASE("Starting the by-time-range retrieval of packet stores") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; request.appendString(packetStoreId); @@ -1047,7 +1047,7 @@ TEST_CASE("Starting the by-time-range retrieval of packet stores") { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreIds[2]).byTimeRangeRetrievalStatus == false); REQUIRE(storageAndRetrieval.getPacketStore(packetStoreIds[3]).byTimeRangeRetrievalStatus == false); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).retrievalStartTime == 0); } @@ -1069,7 +1069,7 @@ TEST_CASE("Aborting the by-time-range retrieval of packet stores") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = true; request.appendString(packetStoreId); @@ -1100,7 +1100,7 @@ TEST_CASE("Aborting the by-time-range retrieval of packet stores") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = true; } @@ -1114,7 +1114,7 @@ TEST_CASE("Aborting the by-time-range retrieval of packet stores") { CHECK(ServiceTests::count() == 3); CHECK(ServiceTests::countThrownErrors(ErrorHandler::NonExistingPacketStore) == 3); - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = true; } @@ -1134,7 +1134,7 @@ TEST_CASE("Aborting the by-time-range retrieval of packet stores") { uint16_t numOfPacketStores = 0; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = true; } @@ -1165,7 +1165,7 @@ TEST_CASE("Reporting the status of packet stores") { uint8_t packetStoreData4[ECSSPacketStoreIdSize] = "ps5555"; int count = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = (count % 2 == 0); packetStore.byTimeRangeRetrievalStatus = (count % 2 != 0); @@ -1228,7 +1228,7 @@ TEST_CASE("Reporting the configuration of packet stores") { uint8_t packetStoreData4[ECSSPacketStoreIdSize] = "ps5555"; int count = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).packetStoreType = (count % 2 == 0) ? PacketStore::Circular : PacketStore::Bounded; count++; @@ -1291,7 +1291,7 @@ TEST_CASE("Resizing the packet stores") { uint16_t numOfPacketStores = 3; request.appendUint16(numOfPacketStores); int index = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.openRetrievalStatus = PacketStore::Suspended; @@ -1328,7 +1328,7 @@ TEST_CASE("Resizing the packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); int index = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = (index % 2 == 0); packetStore.byTimeRangeRetrievalStatus = (index == 1); @@ -1347,7 +1347,7 @@ TEST_CASE("Resizing the packet stores") { CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetPacketStoreWithByTimeRangeRetrieval) == 1); int i = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).sizeInBytes == oldSizes[i++]); } @@ -1369,7 +1369,7 @@ TEST_CASE("Resizing the packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); int index = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.storageStatus = false; packetStore.byTimeRangeRetrievalStatus = false; @@ -1402,7 +1402,7 @@ TEST_CASE("Resizing the packet stores") { uint16_t numOfPacketStores = 4; request.appendUint16(numOfPacketStores); - for (auto& packetStoreId : wrongPacketStoreIds) { + for (auto& packetStoreId: wrongPacketStoreIds) { request.appendString(packetStoreId); request.appendUint16(35); } @@ -1412,7 +1412,7 @@ TEST_CASE("Resizing the packet stores") { CHECK(ServiceTests::count() == 4); CHECK(ServiceTests::countThrownErrors(ErrorHandler::NonExistingPacketStore) == 4); int i = 0; - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).sizeInBytes == oldSizes[i++]); } @@ -1428,7 +1428,7 @@ TEST_CASE("Changing the packet store type to circular") { auto packetStoreIds = validPacketStoreIds(); padWithZeros(packetStoreIds); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.packetStoreType = PacketStore::Bounded; packetStore.storageStatus = false; @@ -1472,7 +1472,7 @@ TEST_CASE("Changing the packet store type to circular") { padWithZeros(correctPacketStoreIds); int count = 0; - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { auto& packetStore = storageAndRetrieval.getPacketStore(packetStoreId); packetStore.packetStoreType = PacketStore::Bounded; packetStore.storageStatus = (count == 0); @@ -1482,7 +1482,7 @@ TEST_CASE("Changing the packet store type to circular") { } String<ECSSPacketStoreIdSize> finalIds[4] = {wrongPacketStoreIds[0], correctPacketStoreIds[0], - correctPacketStoreIds[1], correctPacketStoreIds[2]}; + correctPacketStoreIds[1], correctPacketStoreIds[2]}; ErrorHandler::ExecutionStartErrorType expectedErrors[4] = { ErrorHandler::ExecutionStartErrorType::NonExistingPacketStore, @@ -1517,7 +1517,7 @@ TEST_CASE("Changing the packet store type to bounded") { auto packetStoreIds = validPacketStoreIds(); padWithZeros(packetStoreIds); - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).packetStoreType = PacketStore::Circular; storageAndRetrieval.getPacketStore(packetStoreId).storageStatus = false; storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; @@ -1560,7 +1560,7 @@ TEST_CASE("Changing the packet store type to bounded") { padWithZeros(correctPacketStoreIds); int count = 0; - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).packetStoreType = PacketStore::Circular; storageAndRetrieval.getPacketStore(packetStoreId).storageStatus = (count == 0); storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = (count == 1); @@ -1570,7 +1570,7 @@ TEST_CASE("Changing the packet store type to bounded") { } String<ECSSPacketStoreIdSize> finalIds[4] = {wrongPacketStoreIds[0], correctPacketStoreIds[0], - correctPacketStoreIds[1], correctPacketStoreIds[2]}; + correctPacketStoreIds[1], correctPacketStoreIds[2]}; ErrorHandler::ExecutionStartErrorType expectedErrors[4] = { ErrorHandler::ExecutionStartErrorType::NonExistingPacketStore, @@ -1607,7 +1607,7 @@ TEST_CASE("Changing the virtual channel of packet stores") { uint8_t virtualChannels[2] = {1, 5}; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = false; storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = PacketStore::Suspended; } @@ -1654,7 +1654,7 @@ TEST_CASE("Changing the virtual channel of packet stores") { uint8_t oldVirtualChannels[4] = {4, 6, 1, 2}; int count = 0; - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = (count == 0); storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = (count == 1) ? PacketStore::InProgress : PacketStore::Suspended; @@ -1662,7 +1662,7 @@ TEST_CASE("Changing the virtual channel of packet stores") { } String<ECSSPacketStoreIdSize> finalIds[4] = {wrongPacketStoreIds[0], correctPacketStoreIds[0], - correctPacketStoreIds[1], correctPacketStoreIds[2]}; + correctPacketStoreIds[1], correctPacketStoreIds[2]}; ErrorHandler::ExecutionStartErrorType expectedErrors[4] = { ErrorHandler::ExecutionStartErrorType::NonExistingPacketStore, @@ -1682,7 +1682,7 @@ TEST_CASE("Changing the virtual channel of packet stores") { } int index = 0; - for (auto& packetStoreId : correctPacketStoreIds) { + for (auto& packetStoreId: correctPacketStoreIds) { REQUIRE(storageAndRetrieval.getPacketStore(packetStoreId).virtualChannel == oldVirtualChannels[index]); index++; } @@ -1728,16 +1728,16 @@ TEST_CASE("Reporting the content summary of packet stores") { CHECK(report.readUint32() == timestamps1[0]); CHECK(report.readUint32() == timestamps1[5]); CHECK(report.readUint32() == 5); - CHECK(report.readUint16() == 30); - CHECK(report.readUint16() == 20); + CHECK(report.readUint16() == 60); + CHECK(report.readUint16() == 40); // Packet store 2 report.readString(data, ECSSPacketStoreIdSize); CHECK(std::equal(std::begin(packetStoreData2), std::end(packetStoreData2), std::begin(data))); CHECK(report.readUint32() == timestamps2[0]); CHECK(report.readUint32() == timestamps2[4]); CHECK(report.readUint32() == 5); - CHECK(report.readUint16() == 25); - CHECK(report.readUint16() == 10); + CHECK(report.readUint16() == 50); + CHECK(report.readUint16() == 20); ServiceTests::reset(); Services.reset(); @@ -1752,7 +1752,7 @@ TEST_CASE("Reporting the content summary of packet stores") { padWithZeros(packetStoreIds); int count = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStartTimeTag = (count == 3) ? 20 : 15; count++; } @@ -1782,7 +1782,7 @@ TEST_CASE("Reporting the content summary of packet stores") { CHECK(report.readUint32() == timestamps1[0]); CHECK(report.readUint32() == timestamps1[5]); CHECK(report.readUint32() == 15); - CHECK(report.readUint16() == 30); + CHECK(report.readUint16() == 60); CHECK(report.readUint16() == 0); // Packet store 2 report.readString(data, ECSSPacketStoreIdSize); @@ -1790,23 +1790,23 @@ TEST_CASE("Reporting the content summary of packet stores") { CHECK(report.readUint32() == timestamps2[0]); CHECK(report.readUint32() == timestamps2[4]); CHECK(report.readUint32() == 15); - CHECK(report.readUint16() == 25); - CHECK(report.readUint16() == 10); + CHECK(report.readUint16() == 50); + CHECK(report.readUint16() == 20); // Packet store 3 report.readString(data, ECSSPacketStoreIdSize); CHECK(std::equal(std::begin(packetStoreData3), std::end(packetStoreData3), std::begin(data))); CHECK(report.readUint32() == timestamps4[0]); CHECK(report.readUint32() == timestamps4[7]); CHECK(report.readUint32() == 20); - CHECK(report.readUint16() == 40); - CHECK(report.readUint16() == 30); + CHECK(report.readUint16() == 80); + CHECK(report.readUint16() == 60); // Packet store 4 report.readString(data, ECSSPacketStoreIdSize); CHECK(std::equal(std::begin(packetStoreData4), std::end(packetStoreData4), std::begin(data))); CHECK(report.readUint32() == timestamps3[0]); CHECK(report.readUint32() == timestamps3[3]); CHECK(report.readUint32() == 15); - CHECK(report.readUint16() == 20); + CHECK(report.readUint16() == 40); CHECK(report.readUint16() == 0); ServiceTests::reset(); @@ -1823,7 +1823,7 @@ TEST_CASE("Reporting the content summary of packet stores") { padWithZeros(correctPacketStoreIds); String<ECSSPacketStoreIdSize> finalIds[3] = {wrongPacketStoreIds[0], wrongPacketStoreIds[1], - correctPacketStoreIds[0]}; + correctPacketStoreIds[0]}; storageAndRetrieval.getPacketStore(correctPacketStoreIds[0]).openRetrievalStartTimeTag = 5; @@ -1854,8 +1854,8 @@ TEST_CASE("Reporting the content summary of packet stores") { CHECK(report.readUint32() == timestamps1[0]); CHECK(report.readUint32() == timestamps1[5]); CHECK(report.readUint32() == 5); - CHECK(report.readUint16() == 30); - CHECK(report.readUint16() == 20); + CHECK(report.readUint16() == 60); + CHECK(report.readUint16() == 40); ServiceTests::reset(); Services.reset(); @@ -1897,11 +1897,11 @@ TEST_CASE("Deleting packet store content") { uint32_t leftTimeStamps2[2]; int count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[0]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[0]).storedTelemetryPackets) { leftTimeStamps1[count++] = tmPacket.first; } count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[1]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[1]).storedTelemetryPackets) { leftTimeStamps2[count++] = tmPacket.first; } REQUIRE(storageAndRetrieval.getPacketStore(packetStoreIds[0]).storedTelemetryPackets.size() == 3); @@ -1949,11 +1949,11 @@ TEST_CASE("Deleting packet store content") { uint32_t leftTimeStamps2[8]; int count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[2]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[2]).storedTelemetryPackets) { leftTimeStamps1[count++] = tmPacket.first; } count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[3]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[3]).storedTelemetryPackets) { leftTimeStamps2[count++] = tmPacket.first; } REQUIRE(storageAndRetrieval.getPacketStore(packetStoreIds[2]).storedTelemetryPackets.size() == 4); @@ -2019,7 +2019,7 @@ TEST_CASE("Deleting packet store content") { request.appendUint16(numOfPacketStores); int count = 0; - for (auto& packetStoreId : packetStoreIds) { + for (auto& packetStoreId: packetStoreIds) { storageAndRetrieval.getPacketStore(packetStoreId).byTimeRangeRetrievalStatus = (count == 0); storageAndRetrieval.getPacketStore(packetStoreId).openRetrievalStatus = (count == 1) ? PacketStore::InProgress : PacketStore::Suspended; @@ -2050,15 +2050,15 @@ TEST_CASE("Deleting packet store content") { uint32_t leftTimeStamps4[6]; count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[0]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[0]).storedTelemetryPackets) { leftTimeStamps1[count++] = tmPacket.first; } count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[1]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[1]).storedTelemetryPackets) { leftTimeStamps2[count++] = tmPacket.first; } count = 0; - for (auto& tmPacket : storageAndRetrieval.getPacketStore(packetStoreIds[3]).storedTelemetryPackets) { + for (auto& tmPacket: storageAndRetrieval.getPacketStore(packetStoreIds[3]).storedTelemetryPackets) { leftTimeStamps4[count++] = tmPacket.first; } @@ -2083,7 +2083,7 @@ TEST_CASE("Deleting packet store content") { padWithZeros(correctPacketStoreIds); String<ECSSPacketStoreIdSize> finalIds[7] = { - wrongPacketStoreIds[0], wrongPacketStoreIds[1], wrongPacketStoreIds[2], correctPacketStoreIds[0], + wrongPacketStoreIds[0], wrongPacketStoreIds[1], wrongPacketStoreIds[2], correctPacketStoreIds[0], correctPacketStoreIds[1], correctPacketStoreIds[2], correctPacketStoreIds[3]}; Message request(StorageAndRetrievalService::ServiceType, @@ -2213,7 +2213,7 @@ TEST_CASE("Copying packets in time window, from tag to tag") { auto& targetPacketStore = storageAndRetrieval.getPacketStore(toPacketStoreId); REQUIRE(targetPacketStore.storedTelemetryPackets.size() == 2); int index = 0; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { REQUIRE(tmPacket.first == timestamps1[index++]); } @@ -2260,7 +2260,7 @@ TEST_CASE("Copying packets in time window, from tag to tag") { auto& targetPacketStore = storageAndRetrieval.getPacketStore(toPacketStoreId); REQUIRE(targetPacketStore.storedTelemetryPackets.size() == 4); int index = 3; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { REQUIRE(tmPacket.first == timestamps4[index++]); } @@ -2307,7 +2307,7 @@ TEST_CASE("Copying packets in time window, from tag to tag") { auto& targetPacketStore = storageAndRetrieval.getPacketStore(toPacketStoreId); REQUIRE(targetPacketStore.storedTelemetryPackets.size() == 3); int index = 2; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { REQUIRE(tmPacket.first == timestamps2[index++]); } @@ -2541,7 +2541,7 @@ TEST_CASE("Copying packets in time window, after time-tag") { uint32_t existingTimestamps[3]; int index = 0; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { existingTimestamps[index++] = tmPacket.first; } REQUIRE( @@ -2584,7 +2584,7 @@ TEST_CASE("Copying packets in time window, after time-tag") { uint32_t existingTimestamps[6]; int index = 0; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { existingTimestamps[index++] = tmPacket.first; } REQUIRE(std::equal(std::begin(timestamps1), std::end(timestamps1), std::begin(existingTimestamps))); @@ -2665,7 +2665,7 @@ TEST_CASE("Copying packets in time window, before time-tag") { uint32_t existingTimestamps[3]; int index = 0; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { existingTimestamps[index++] = tmPacket.first; } REQUIRE( @@ -2708,7 +2708,7 @@ TEST_CASE("Copying packets in time window, before time-tag") { uint32_t existingTimestamps[6]; int index = 0; - for (auto& tmPacket : targetPacketStore.storedTelemetryPackets) { + for (auto& tmPacket: targetPacketStore.storedTelemetryPackets) { existingTimestamps[index++] = tmPacket.first; } REQUIRE(std::equal(std::begin(timestamps1), std::end(timestamps1), std::begin(existingTimestamps))); diff --git a/test/Services/TestService.cpp b/test/Services/TestServiceTests.cpp similarity index 55% rename from test/Services/TestService.cpp rename to test/Services/TestServiceTests.cpp index 7fb18f85a7c52fad0349ce9fbde5011736b13d7e..caee8703e9874cfc4b6dafa4007ab5f4c325c9a3 100644 --- a/test/Services/TestService.cpp +++ b/test/Services/TestServiceTests.cpp @@ -1,11 +1,11 @@ -#include <catch2/catch.hpp> -#include <Services/TestService.hpp> #include <Message.hpp> +#include <Services/TestService.hpp> +#include <catch2/catch_all.hpp> #include "ServiceTests.hpp" TestService& testService = Services.testService; -TEST_CASE("TM[17,1]", "[service][st17]") { +TEST_CASE("TC[17,1]", "[service][st17]") { Message receivedPacket = Message(TestService::ServiceType, TestService::MessageType::AreYouAliveTest, Message::TC, 1); MessageParser::execute(receivedPacket); REQUIRE(ServiceTests::hasOneMessage()); @@ -16,12 +16,31 @@ TEST_CASE("TM[17,1]", "[service][st17]") { REQUIRE(response.dataSize == 0); } -TEST_CASE("TM[17,3]", "[service][st17]") { +TEST_CASE("TM[17,2]", "[service][st17]") { + testService.areYouAliveReport(); + + Message response = ServiceTests::get(0); + CHECK(response.serviceType == TestService::ServiceType); + CHECK(response.messageType == TestService::MessageType::AreYouAliveTestReport); + REQUIRE(response.dataSize == 0); +} + +TEST_CASE("TC[17,3]", "[service][st17]") { Message receivedPacket = Message(TestService::ServiceType, TestService::MessageType::OnBoardConnectionTest, Message::TC, 1); - receivedPacket.appendEnum16(40); + receivedPacket.appendEnum16(ApplicationId); MessageParser::execute(receivedPacket); REQUIRE(ServiceTests::hasOneMessage()); + Message response = ServiceTests::get(0); + CHECK(response.serviceType == TestService::ServiceType); + CHECK(response.messageType == TestService::MessageType::OnBoardConnectionTestReport); + REQUIRE(response.dataSize == 2); + CHECK(response.readEnum16() == ApplicationId); +} + +TEST_CASE("TM[17,4]", "[service][st17]") { + testService.onBoardConnectionReport(40); + Message response = ServiceTests::get(0); CHECK(response.serviceType == TestService::ServiceType); CHECK(response.messageType == TestService::MessageType::OnBoardConnectionTestReport); diff --git a/test/Services/TimeBasedSchedulingService.cpp b/test/Services/TimeBasedSchedulingServiceTests.cpp similarity index 52% rename from test/Services/TimeBasedSchedulingService.cpp rename to test/Services/TimeBasedSchedulingServiceTests.cpp index c1f601b671e184b1d7db352b5f8f1487c0e9a7f3..a48361e9112b716f75851498424cb075c007410c 100644 --- a/test/Services/TimeBasedSchedulingService.cpp +++ b/test/Services/TimeBasedSchedulingServiceTests.cpp @@ -1,17 +1,18 @@ -#include <catch2/catch.hpp> -#include "ServiceTests.hpp" #include <Services/TimeBasedSchedulingService.hpp> +#include <catch2/catch_all.hpp> +#include "ServiceTests.hpp" #include <ctime> #include <vector> +using namespace std::chrono_literals; + /* * A namespace defined explicitly for the purposes of testing. This namespace contains a * structure, which has been declared as a friend in the TimeBasedSchedulingService class, so * that it can access the private members required for testing validation. */ -namespace unit_test -{ +namespace unit_test { struct Tester { static bool executionFunctionStatus(TimeBasedSchedulingService& tmService) { return tmService.executionFunctionStatus; @@ -24,8 +25,9 @@ namespace unit_test static auto scheduledActivities(TimeBasedSchedulingService& tmService) { std::vector<TimeBasedSchedulingService::ScheduledActivity*> listElements; - std::transform(tmService.scheduledActivities.begin(), tmService.scheduledActivities.end(), - std::back_inserter(listElements), [](auto& activity) -> auto { return &activity; }); + std::transform( + tmService.scheduledActivities.begin(), tmService.scheduledActivities.end(), + std::back_inserter(listElements), [](auto& activity) -> auto{ return &activity; }); return listElements; // Return the list elements } @@ -33,8 +35,8 @@ namespace unit_test } // namespace unit_test Message testMessage1, testMessage2, testMessage3, testMessage4; -auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time -bool messagesPopulated = false; // Indicate whether the test messages are initialized +Time::DefaultCUC currentTime = TimeGetter::getCurrentTimeDefaultCUC(); // Get the current system time +bool messagesPopulated = false; // Indicate whether the test messages are initialized // Run this function to set the service up before moving on with further testing auto activityInsertion(TimeBasedSchedulingService& timeService) { @@ -43,13 +45,13 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { testMessage1.serviceType = 6; testMessage1.messageType = 5; testMessage1.packetType = Message::TC; - testMessage1.applicationId = 8; // todo: Remove the dummy application ID + testMessage1.applicationId = 8; // todo: Remove the dummy application ID testMessage1.appendUint16(4253); // Append dummy data testMessage2.serviceType = 4; testMessage2.messageType = 5; testMessage2.packetType = Message::TC; - testMessage2.applicationId = 4; // todo: Remove the dummy application ID + testMessage2.applicationId = 4; // todo: Remove the dummy application ID testMessage2.appendUint16(45667); // Append dummy data testMessage3.serviceType = 3; @@ -58,7 +60,7 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { testMessage3.appendUint16(456); // Append dummy data testMessage4.serviceType = 12; - testMessage4.messageType = 23; + testMessage4.messageType = 3; testMessage4.packetType = Message::TC; testMessage4.appendUint16(934); // Append dummy data @@ -69,19 +71,19 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { receivedMessage.appendUint16(4); // Total number of requests // Test activity 1 - receivedMessage.appendUint32(currentTime + 1556435); + receivedMessage.appendDefaultCUCTimeStamp(currentTime + 155643s); receivedMessage.appendMessage(testMessage1, ECSSTCRequestStringSize); // Test activity 2 - receivedMessage.appendUint32(currentTime + 1957232); + receivedMessage.appendDefaultCUCTimeStamp(currentTime + 195723s); receivedMessage.appendMessage(testMessage2, ECSSTCRequestStringSize); // Test activity 3 - receivedMessage.appendUint32(currentTime + 1726435); + receivedMessage.appendDefaultCUCTimeStamp(currentTime + 172643s); receivedMessage.appendMessage(testMessage3, ECSSTCRequestStringSize); // Test activity 4 - receivedMessage.appendUint32(currentTime + 17248435); + receivedMessage.appendDefaultCUCTimeStamp(currentTime + 1724843s); receivedMessage.appendMessage(testMessage4, ECSSTCRequestStringSize); // Insert activities in the schedule. They have to be inserted sorted @@ -91,13 +93,54 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { } -TimeBasedSchedulingService & timeBasedService = Services.timeBasedScheduling; +TimeBasedSchedulingService& timeBasedService = Services.timeBasedScheduling; + +TEST_CASE("Execute the first activity, removes it from the list and return the release time of next activity to be executed") { + Services.reset(); + auto scheduledActivities = activityInsertion(timeBasedService); + + auto nextActivityExecutionCUCTime = timeBasedService.executeScheduledActivity(currentTime + 155643s); + REQUIRE(nextActivityExecutionCUCTime == currentTime + 172643s); + + Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities, Message::TC, 1); + timeBasedService.detailReportAllActivities(receivedMessage); + Message response = ServiceTests::get(0); + uint16_t iterationCount = response.readUint16(); + REQUIRE(iterationCount == 3); + + nextActivityExecutionCUCTime = timeBasedService.executeScheduledActivity(currentTime + 10s); + REQUIRE(nextActivityExecutionCUCTime == currentTime + 172643s); + + nextActivityExecutionCUCTime = timeBasedService.executeScheduledActivity(currentTime + 172643s); + REQUIRE(nextActivityExecutionCUCTime == currentTime + 195723s); + + timeBasedService.detailReportAllActivities(receivedMessage); + response = ServiceTests::get(1); + iterationCount = response.readUint16(); + REQUIRE(iterationCount == 2); + + nextActivityExecutionCUCTime = timeBasedService.executeScheduledActivity(currentTime + 195723s); + REQUIRE(nextActivityExecutionCUCTime == currentTime + 1724843s); + + timeBasedService.detailReportAllActivities(receivedMessage); + response = ServiceTests::get(2); + iterationCount = response.readUint16(); + REQUIRE(iterationCount == 1); + + nextActivityExecutionCUCTime = timeBasedService.executeScheduledActivity(currentTime + 1724843s); + REQUIRE(nextActivityExecutionCUCTime == Time::DefaultCUC::max()); + + timeBasedService.detailReportAllActivities(receivedMessage); + response = ServiceTests::get(3); + iterationCount = response.readUint16(); + REQUIRE(iterationCount == 0); +} TEST_CASE("TC[11,1] Enable Schedule Execution", "[service][st11]") { Services.reset(); Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction, Message::TC, 1); - MessageParser::execute(receivedMessage);//timeService.enableScheduleExecution(receivedMessage); + MessageParser::execute(receivedMessage); //timeService.enableScheduleExecution(receivedMessage); CHECK(unit_test::Tester::executionFunctionStatus(timeBasedService)); } @@ -105,7 +148,7 @@ TEST_CASE("TC[11,2] Disable Schedule Execution", "[service][st11]") { Services.reset(); Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DisableTimeBasedScheduleExecutionFunction, Message::TC, 1); - MessageParser::execute(receivedMessage);//timeService.disableScheduleExecution(receivedMessage); + MessageParser::execute(receivedMessage); //timeService.disableScheduleExecution(receivedMessage); CHECK(not unit_test::Tester::executionFunctionStatus(timeBasedService)); } @@ -115,10 +158,11 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") { 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)->requestReleaseTime == currentTime + 155643s); + REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 172643s); + REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 195723s); + REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1724843s); + REQUIRE(testMessage1.bytesEqualWith(scheduledActivities.at(0)->request)); REQUIRE(testMessage3.bytesEqualWith(scheduledActivities.at(1)->request)); REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(2)->request)); @@ -128,8 +172,8 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") { Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::InsertActivities, Message::TC, 1); receivedMessage.appendUint16(1); // Total number of requests - receivedMessage.appendUint32(currentTime - 15564350); - MessageParser::execute(receivedMessage);//timeService.insertActivities(receivedMessage); + receivedMessage.appendDefaultCUCTimeStamp(currentTime - 155643s); + MessageParser::execute(receivedMessage); //timeService.insertActivities(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); } @@ -140,37 +184,36 @@ TEST_CASE("TC[11,15] Time shift all scheduled activities", "[service][st11]") { Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities, Message::TC, 1); auto scheduledActivities = activityInsertion(timeBasedService); - const int32_t timeShift = 6789; + const Time::RelativeTime timeShift = 6789; SECTION("Positive Shift") { - receivedMessage.appendSint32(-timeShift); + receivedMessage.appendRelativeTime(-timeShift); CHECK(scheduledActivities.size() == 4); - MessageParser::execute(receivedMessage);//timeService.timeShiftAllActivities(receivedMessage); + MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); - REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 1556435 - timeShift); - REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435 - timeShift); - REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232 - timeShift); - REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 17248435 - timeShift); + REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 155643s - std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 172643s - std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 195723s - std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1724843s - std::chrono::seconds(timeShift)); } SECTION("Negative Shift") { - receivedMessage.appendSint32(timeShift); + receivedMessage.appendRelativeTime(timeShift); CHECK(scheduledActivities.size() == 4); - MessageParser::execute(receivedMessage);//timeService.timeShiftAllActivities(receivedMessage); - - REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 1556435 + timeShift); - REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435 + timeShift); - REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232 + timeShift); - REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 17248435 + timeShift); + MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); + REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 155643s + std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 172643s + std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 195723s + std::chrono::seconds(timeShift)); + REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1724843s + std::chrono::seconds(timeShift)); } SECTION("Error throwing") { - receivedMessage.appendSint32(-6789000); // Provide a huge time shift to cause an error + receivedMessage.appendRelativeTime(-6789000); // Provide a huge time shift to cause an error CHECK(scheduledActivities.size() == 4); - MessageParser::execute(receivedMessage);//timeService.timeShiftAllActivities(receivedMessage); + MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::SubServiceExecutionStartError)); } @@ -184,55 +227,55 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID CHECK(scheduledActivities.size() == 4); - const int32_t timeShift = 67890000; // Relative time-shift value + const Time::RelativeTime timeShift = 67890000; // Relative time-shift value SECTION("Positive Shift") { - receivedMessage.appendSint32(timeShift); // Time-shift value - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendRelativeTime(timeShift); // Time-shift value + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count timeBasedService.timeShiftActivitiesByID(receivedMessage); scheduledActivities = unit_test::Tester::scheduledActivities(timeBasedService); // Make sure the new value is inserted sorted - REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 1957232 + timeShift); + REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 195723s + std::chrono::seconds(timeShift)); REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(3)->request)); } SECTION("Negative Shift") { - receivedMessage.appendSint32(-250000); // Time-shift value - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendRelativeTime(-25000); // Time-shift value + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count timeBasedService.timeShiftActivitiesByID(receivedMessage); scheduledActivities = unit_test::Tester::scheduledActivities(timeBasedService); // Output should be sorted - REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1957232 - 250000); + REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 195723s - 25000s); REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(1)->request)); } SECTION("Error throw on wrong request ID") { - receivedMessage.appendSint32(-250000); // Time-shift value - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Dummy source ID - receivedMessage.appendUint16(80); // Dummy application ID to throw an error - receivedMessage.appendUint16(0); // Dummy sequence count + receivedMessage.appendRelativeTime(-250000); // Time-shift value + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Dummy source ID + receivedMessage.appendUint16(80); // Dummy application ID to throw an error + receivedMessage.appendUint16(0); // Dummy sequence count timeBasedService.timeShiftActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); } SECTION("Error throw on wrong time offset") { - receivedMessage.appendSint32(-6789000); // Time-shift value - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendRelativeTime(-6789000); // Time-shift value + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count timeBasedService.timeShiftActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); @@ -251,14 +294,14 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]" scheduledActivities.at(0)->requestID.applicationID = 8; // Append a dummy application ID scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID - receivedMessage.appendUint16(2); // Two instructions in the request - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(2); // Two instructions in the request + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage1.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count timeBasedService.detailReportActivitiesByID(receivedMessage); REQUIRE(ServiceTests::hasOneMessage()); @@ -270,13 +313,12 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]" uint16_t iterationCount = response.readUint16(); CHECK(iterationCount == 2); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::DefaultCUC receivedReleaseTime = response.readDefaultCUCTimeStamp(); Message receivedTCPacket; uint8_t receivedDataStr[ECSSTCRequestStringSize]; response.readString(receivedDataStr, ECSSTCRequestStringSize); receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); - if (i == 0) { REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime); REQUIRE(receivedTCPacket == scheduledActivities.at(0)->request); @@ -288,16 +330,64 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]" } SECTION("Error throw on wrong request ID") { - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Dummy source ID + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Dummy source ID receivedMessage.appendUint16(80); // Dummy application ID to throw an error - receivedMessage.appendUint16(0); // Dummy sequence count + receivedMessage.appendUint16(0); // Dummy sequence count timeBasedService.detailReportActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); } } +TEST_CASE("TM[11,10] time-based schedule detail report", "[service][st11]") { + Services.reset(); + Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DetailReportActivitiesById, Message::TC, 1); + + auto scheduledActivities = activityInsertion(timeBasedService); + + SECTION("Detailed activity report") { + // Verify that everything is in place + CHECK(scheduledActivities.size() == 4); + scheduledActivities.at(0)->requestID.applicationID = 8; // Append a dummy application ID + scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID + + receivedMessage.appendUint16(2); // Two instructions in the request + receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + + receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(testMessage1.applicationId); // todo: Remove the dummy app ID + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + + timeBasedService.detailReportActivitiesByID(receivedMessage); + REQUIRE(ServiceTests::hasOneMessage()); + + Message response = ServiceTests::get(0); + CHECK(response.serviceType == 11); + CHECK(response.messageType == 10); + + uint16_t iterationCount = response.readUint16(); + CHECK(iterationCount == 2); + for (uint16_t i = 0; i < iterationCount; i++) { + Time::DefaultCUC receivedReleaseTime = response.readDefaultCUCTimeStamp(); + + Message receivedTCPacket; + uint8_t receivedDataStr[ECSSTCRequestStringSize]; + response.readString(receivedDataStr, ECSSTCRequestStringSize); + receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); + if (i == 0) { + REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime); + REQUIRE(receivedTCPacket == scheduledActivities.at(0)->request); + } else { + REQUIRE(receivedReleaseTime == scheduledActivities.at(2)->requestReleaseTime); + REQUIRE(receivedTCPacket == scheduledActivities.at(2)->request); + } + } + } +} + TEST_CASE("TC[11,12] Summary report scheduled activities by ID", "[service][st11]") { Services.reset(); Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById, Message::TC, 1); @@ -310,14 +400,14 @@ TEST_CASE("TC[11,12] Summary report scheduled activities by ID", "[service][st11 scheduledActivities.at(0)->requestID.applicationID = 8; // Append a dummy application ID scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID - receivedMessage.appendUint16(2); // Two instructions in the request - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(2); // Two instructions in the request + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage1.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count timeBasedService.summaryReportActivitiesByID(receivedMessage); REQUIRE(ServiceTests::hasOneMessage()); @@ -328,7 +418,7 @@ TEST_CASE("TC[11,12] Summary report scheduled activities by ID", "[service][st11 uint16_t iterationCount = response.readUint16(); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::DefaultCUC receivedReleaseTime = response.readDefaultCUCTimeStamp(); uint8_t receivedSourceID = response.readUint8(); uint16_t receivedApplicationID = response.readUint16(); uint16_t receivedSequenceCount = response.readUint16(); @@ -348,16 +438,66 @@ TEST_CASE("TC[11,12] Summary report scheduled activities by ID", "[service][st11 } SECTION("Error throw on wrong request ID") { - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Dummy source ID + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Dummy source ID receivedMessage.appendUint16(80); // Dummy application ID to throw an error - receivedMessage.appendUint16(0); // Dummy sequence count + receivedMessage.appendUint16(0); // Dummy sequence count timeBasedService.summaryReportActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); } } +TEST_CASE("TM[11,13] time-based schedule summary report", "[service][st11]") { + Services.reset(); + Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById, Message::TC, 1); + + auto scheduledActivities = activityInsertion(timeBasedService); + + SECTION("Summary report") { + // Verify that everything is in place + CHECK(scheduledActivities.size() == 4); + scheduledActivities.at(0)->requestID.applicationID = 8; // Append a dummy application ID + scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID + + receivedMessage.appendUint16(2); // Two instructions in the request + receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + + receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(testMessage1.applicationId); // todo: Remove the dummy app ID + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + + timeBasedService.summaryReportActivitiesByID(receivedMessage); + REQUIRE(ServiceTests::hasOneMessage()); + + Message response = ServiceTests::get(0); + CHECK(response.serviceType == 11); + CHECK(response.messageType == 13); + + uint16_t iterationCount = response.readUint16(); + for (uint16_t i = 0; i < iterationCount; i++) { + Time::DefaultCUC receivedReleaseTime = response.readDefaultCUCTimeStamp(); + uint8_t receivedSourceID = response.readUint8(); + uint16_t receivedApplicationID = response.readUint16(); + uint16_t receivedSequenceCount = response.readUint16(); + + if (i == 0) { + REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime); + REQUIRE(receivedSourceID == scheduledActivities.at(0)->requestID.sourceID); + REQUIRE(receivedApplicationID == scheduledActivities.at(0)->requestID.applicationID); + REQUIRE(receivedSequenceCount == scheduledActivities.at(0)->requestID.sequenceCount); + } else { + REQUIRE(receivedReleaseTime == scheduledActivities.at(2)->requestReleaseTime); + REQUIRE(receivedSourceID == scheduledActivities.at(2)->requestID.sourceID); + REQUIRE(receivedApplicationID == scheduledActivities.at(2)->requestID.applicationID); + REQUIRE(receivedSequenceCount == scheduledActivities.at(2)->requestID.sequenceCount); + } + } + } +} + TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]") { Services.reset(); auto scheduledActivities = activityInsertion(timeBasedService); @@ -374,13 +514,12 @@ TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]") REQUIRE(iterationCount == scheduledActivities.size()); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::DefaultCUC receivedReleaseTime = response.readDefaultCUCTimeStamp(); Message receivedTCPacket; uint8_t receivedDataStr[ECSSTCRequestStringSize]; response.readString(receivedDataStr, ECSSTCRequestStringSize); receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); - REQUIRE(receivedReleaseTime == scheduledActivities.at(i)->requestReleaseTime); REQUIRE(receivedTCPacket.bytesEqualWith(scheduledActivities.at(i)->request)); } @@ -397,25 +536,25 @@ TEST_CASE("TC[11,5] Activity deletion by ID", "[service][st11]") { CHECK(scheduledActivities.size() == 4); scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID - receivedMessage.appendUint16(1); // Just one instruction to delete an activity - receivedMessage.appendUint8(0); // Source ID is not implemented + receivedMessage.appendUint16(1); // Just one instruction to delete an activity + receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID - receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count + receivedMessage.appendUint16(0); // todo: Remove the dummy sequence count CHECK(scheduledActivities.size() == 4); timeBasedService.deleteActivitiesByID(receivedMessage); scheduledActivities = unit_test::Tester::scheduledActivities(timeBasedService); REQUIRE(scheduledActivities.size() == 3); - REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 17248435); + REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1724843s); REQUIRE(testMessage4.bytesEqualWith(scheduledActivities.at(2)->request)); } SECTION("Error throw on wrong request ID") { - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Dummy source ID + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Dummy source ID receivedMessage.appendUint16(80); // Dummy application ID to throw an error - receivedMessage.appendUint16(0); // Dummy sequence count + receivedMessage.appendUint16(0); // Dummy sequence count timeBasedService.deleteActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); diff --git a/test/TestPlatform.cpp b/test/TestPlatform.cpp index d5df134fce675f822a5fa7a5ea2f8e41e3bb23b4..d0f491f171f4230313d67611ea24b07bdec2f425 100644 --- a/test/TestPlatform.cpp +++ b/test/TestPlatform.cpp @@ -1,13 +1,28 @@ #define CATCH_CONFIG_EXTERNAL_INTERFACES -#include <catch2/catch.hpp> + +#include <Logger.hpp> #include <Message.hpp> #include <Service.hpp> -#include <Logger.hpp> -#include "Services/ServiceTests.hpp" +#include <catch2/catch_all.hpp> +#include <cxxabi.h> #include "Helpers/Parameter.hpp" -#include "Services/ParameterService.hpp" +#include "Helpers/TimeGetter.hpp" #include "Parameters/PlatformParameters.hpp" +#include "Services/ParameterService.hpp" +#include "Services/ParameterStatisticsService.hpp" +#include "Services/ServiceTests.hpp" + +UTCTimestamp TimeGetter::getCurrentTimeUTC() { + UTCTimestamp currentTime(2020, 4, 10, 10, 15, 0); + return currentTime; +} + +Time::DefaultCUC TimeGetter::getCurrentTimeDefaultCUC() { + UTCTimestamp timeUTC = getCurrentTimeUTC(); + Time::DefaultCUC timeCUC(timeUTC); + return timeCUC; +} // Explicit template specializations for the logError() function template void ErrorHandler::logError(const Message&, ErrorHandler::AcceptanceErrorType); @@ -36,23 +51,36 @@ void ErrorHandler::logError(const Message& message, ErrorType errorType) { template <typename ErrorType> void ErrorHandler::logError(ErrorType errorType) { ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType); + + auto errorCategory = abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr); + auto errorNumber = std::underlying_type_t<ErrorType>(errorType); + + LOG_ERROR << "Error " << errorCategory << " with number " << errorNumber; } void Logger::log(Logger::LogLevel level, etl::istring& message) { - // Logs while testing are completely ignored + // Logs while testing are passed on to Catch2, if they are important enough + if (level >= Logger::warning) { + UNSCOPED_INFO(message.c_str()); + } } -struct ServiceTestsListener : Catch::TestEventListenerBase { - using TestEventListenerBase::TestEventListenerBase; // inherit constructor +struct ServiceTestsListener : Catch::EventListenerBase { + using EventListenerBase::EventListenerBase; // inherit constructor void sectionEnded(Catch::SectionStats const& sectionStats) override { // Make sure we don't have any errors if (not ServiceTests::isExpectingErrors()) { // 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. - + UNSCOPED_INFO("Found " << ServiceTests::countErrors() << " errors at end of section: "); + for (auto error: ServiceTests::getThrownErrors()) { + UNSCOPED_INFO(" Error " << error.second << " (type " << error.first << ")"); + } CHECK(ServiceTests::hasNoErrors()); } + + ServiceTests::resetErrors(); } void testCaseEnded(Catch::TestCaseStats const& testCaseStats) override { @@ -75,23 +103,76 @@ namespace PlatformParameters { inline Parameter<uint32_t> parameter10(43); inline Parameter<uint32_t> parameter11(91); inline Parameter<uint8_t> parameter12(1); -} + inline Parameter<uint8_t> parameter13(1); + inline Parameter<uint8_t> parameter14(1); + inline Parameter<uint8_t> parameter15(1); + inline Parameter<uint8_t> parameter16(1); + inline Parameter<uint8_t> parameter17(1); + inline Parameter<uint8_t> parameter18(1); + inline Parameter<uint8_t> parameter19(1); + inline Parameter<uint8_t> parameter20(1); + inline Parameter<uint8_t> parameter21(1); + inline Parameter<uint8_t> parameter22(1); + inline Parameter<uint8_t> parameter23(1); + inline Parameter<uint8_t> parameter24(1); + inline Parameter<uint8_t> parameter25(1); + inline Parameter<uint8_t> parameter26(1); + inline Parameter<uint8_t> parameter27(1); + inline Parameter<uint8_t> parameter28(1); + inline Parameter<uint8_t> parameter29(1); + inline Parameter<uint8_t> parameter30(1); + inline Parameter<uint8_t> parameter31(1); + inline Parameter<uint8_t> parameter32(1); + inline Parameter<uint8_t> parameter33(1); + inline Parameter<uint8_t> parameter34(1); + +} // namespace PlatformParameters /** * Specific definition for \ref ParameterService's initialize function, for testing purposes. */ void ParameterService::initializeParameterMap() { - parameters = {{static_cast<uint16_t>(0), PlatformParameters::parameter1}, - {static_cast<uint16_t>(1), PlatformParameters::parameter2}, - {static_cast<uint16_t>(2), PlatformParameters::parameter3}, - {static_cast<uint16_t>(3), PlatformParameters::parameter4}, - {static_cast<uint16_t>(4), PlatformParameters::parameter5}, - {static_cast<uint16_t>(5), PlatformParameters::parameter6}, - {static_cast<uint16_t>(6), PlatformParameters::parameter7}, - {static_cast<uint16_t>(7), PlatformParameters::parameter8}, - {static_cast<uint16_t>(8), PlatformParameters::parameter9}, - {static_cast<uint16_t>(9), PlatformParameters::parameter10}, - {static_cast<uint16_t>(10), PlatformParameters::parameter11}, - {static_cast<uint16_t>(11), PlatformParameters::parameter12}}; + parameters = { + {uint16_t{0}, PlatformParameters::parameter1}, + {uint16_t{1}, PlatformParameters::parameter2}, + {uint16_t{2}, PlatformParameters::parameter3}, + {uint16_t{3}, PlatformParameters::parameter4}, + {uint16_t{4}, PlatformParameters::parameter5}, + {uint16_t{5}, PlatformParameters::parameter6}, + {uint16_t{6}, PlatformParameters::parameter7}, + {uint16_t{7}, PlatformParameters::parameter8}, + {uint16_t{8}, PlatformParameters::parameter9}, + {uint16_t{9}, PlatformParameters::parameter10}, + {uint16_t{10}, PlatformParameters::parameter11}, + {uint16_t{11}, PlatformParameters::parameter12}, + {uint16_t{12}, PlatformParameters::parameter13}, + {uint16_t{13}, PlatformParameters::parameter14}, + {uint16_t{14}, PlatformParameters::parameter15}, + {uint16_t{15}, PlatformParameters::parameter16}, + {uint16_t{16}, PlatformParameters::parameter17}, + {uint16_t{17}, PlatformParameters::parameter18}, + {uint16_t{18}, PlatformParameters::parameter19}, + {uint16_t{19}, PlatformParameters::parameter20}, + {uint16_t{20}, PlatformParameters::parameter21}, + {uint16_t{21}, PlatformParameters::parameter22}, + {uint16_t{22}, PlatformParameters::parameter23}, + {uint16_t{23}, PlatformParameters::parameter24}, + {uint16_t{24}, PlatformParameters::parameter25}, + {uint16_t{25}, PlatformParameters::parameter26}, + {uint16_t{26}, PlatformParameters::parameter27}, + {uint16_t{27}, PlatformParameters::parameter28}, + {uint16_t{28}, PlatformParameters::parameter29}, + {uint16_t{29}, PlatformParameters::parameter30}, + {uint16_t{30}, PlatformParameters::parameter31}, + {uint16_t{31}, PlatformParameters::parameter32}, + {uint16_t{32}, PlatformParameters::parameter33}, + {uint16_t{33}, PlatformParameters::parameter34}}; } + +void TimeBasedSchedulingService::notifyNewActivityAddition() {} + +void ParameterStatisticsService::initializeStatisticsMap() { + statisticsMap = {}; +} + CATCH_REGISTER_LISTENER(ServiceTestsListener) diff --git a/test/Time/TimeFormats.cpp b/test/Time/TimeFormats.cpp deleted file mode 100644 index 883af362b940848bb07bfe4cae5a117fb5178098..0000000000000000000000000000000000000000 --- a/test/Time/TimeFormats.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "catch2/catch.hpp" -#include "Time/Time.hpp" -#include "Time/UTCTimestamp.hpp" -#include "../Services/ServiceTests.hpp" - -TEST_CASE("UTC timestamps") { - // invalid year - UTCTimestamp Timestamp0{1950, 4, 10, 10, 15, 0}; - - // invalid month - UTCTimestamp Timestamp1{2020, 60, 10, 10, 15, 0}; - - // invalid day - UTCTimestamp Timestamp2{2020, 4, 35, 10, 15, 0}; - - // invalid hour - UTCTimestamp Timestamp3{2020, 4, 10, 100, 15, 0}; - - // invalid minute - UTCTimestamp Timestamp4{2020, 4, 10, 10, 200, 0}; - - // invalid second - UTCTimestamp Timestamp5{2020, 4, 10, 10, 15, 100}; - - CHECK(ServiceTests::countErrors() == 6); - CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate)); -} diff --git a/test/Time/TimeFormatsTests.cpp b/test/Time/TimeFormatsTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c7c0720930c619335c5fff1c7d323d032a38e525 --- /dev/null +++ b/test/Time/TimeFormatsTests.cpp @@ -0,0 +1,79 @@ +#include "../Services/ServiceTests.hpp" +#include "Time/Time.hpp" +#include "Time/UTCTimestamp.hpp" +#include "Message.hpp" +#include "catch2/catch_all.hpp" + +TEST_CASE("UTC timestamps") { + // invalid year + UTCTimestamp Timestamp0{1950, 4, 10, 10, 15, 0}; + + // invalid month + UTCTimestamp Timestamp1{2020, 60, 10, 10, 15, 0}; + + // invalid day + UTCTimestamp Timestamp2{2020, 4, 35, 10, 15, 0}; + + // invalid hour + UTCTimestamp Timestamp3{2020, 4, 10, 100, 15, 0}; + + // invalid minute + UTCTimestamp Timestamp4{2020, 4, 10, 10, 200, 0}; + + // invalid second + UTCTimestamp Timestamp5{2020, 4, 10, 10, 15, 100}; + + CHECK(ServiceTests::countErrors() == 6); + CHECK(ServiceTests::thrownError(ErrorHandler::InvalidDate)); +} + +TEST_CASE("UTC timestamp addition") { + using namespace std::chrono_literals; + + UTCTimestamp time1 = UTCTimestamp{2020, 1, 1, 0, 0, 0}; + UTCTimestamp time2 = UTCTimestamp{2035, 11, 19, 23, 57, 24}; + + SECTION("Valid ranges") { + auto time = time1; + time += -1s; + CHECK(ServiceTests::thrownError(ErrorHandler::InvalidTimeStampInput)); + } + + SECTION("Simple addition") { + auto time = time1; + time += 10s; + CHECK(time == UTCTimestamp{2020, 1, 1, 0, 0, 10}); + + time += 25h; + CHECK(time == UTCTimestamp{2020, 1, 2, 1, 0, 10}); + } + + SECTION("Overflow within range") { + auto time = time2; + time += 1209780s; + CHECK(time == UTCTimestamp{2035, 12, 4, 0, 0, 24}); + + time += 60 * 24h; + CHECK(time == UTCTimestamp{2036, 2, 2, 0, 0, 24}); + } + + SECTION("Future dates") { + auto time = time2; + time += 999999h; + CHECK(time == UTCTimestamp{2149, 12, 18, 14, 57, 24}); + } +} + +TEST_CASE("CUC Custom Timestamp as Parameter") { + using namespace Time; + DefaultCUC time(999_t); + + auto parameter = Parameter<DefaultCUC>(time); + + auto message = Message(0, 0, Message::TC); + parameter.appendValueToMessage(message); + CHECK(message.dataSize == 4); + + parameter.setValueFromMessage(message); + CHECK(time == parameter.getValue()); +} diff --git a/test/Time/TimeStamp.cpp b/test/Time/TimeStamp.cpp deleted file mode 100644 index ac99522ba966f817f7a048a615ab4782975007bd..0000000000000000000000000000000000000000 --- a/test/Time/TimeStamp.cpp +++ /dev/null @@ -1,190 +0,0 @@ -#include "catch2/catch.hpp" -#include "Time/TimeStamp.hpp" - -using namespace Time; - -TEST_CASE("TimeStamp class construction") { - // SECTION("Initialize with excessive precision, breaks at compile time"){ - // TimeStamp<5, 10> Epoch3; - // TimeStamp<4, 4> Epoch4; - // } -} - -TEST_CASE("CUC headers generation") { - auto cuc_header1 = buildCUCHeader<uint8_t, 2, 2>(); - CHECK(cuc_header1 == 0b00100110); - - auto cuc_header2 = buildCUCHeader<uint8_t, 4, 1>(); - CHECK(cuc_header2 == 0b00101101); - - auto cuc_header3 = buildCUCHeader<uint8_t, 1, 1>(); - CHECK(cuc_header3 == 0b00100001); - - auto cuc_header4 = buildCUCHeader<uint16_t, 5, 1>(); - CHECK(cuc_header4 == 0b1010110110100000); - - auto cuc_header5 = buildCUCHeader<uint16_t, 1, 6>(); - CHECK(cuc_header5 == 0b1010001110001100); - - auto cuc_header6 = buildCUCHeader<uint16_t, 7, 1>(); - CHECK(cuc_header6 == 0b1010110111100000); -} - -TEST_CASE("TAI idempotence") { - int input_time = 1000; - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(input_time); - - REQUIRE(time.asTAIseconds() == input_time); -} - -TEST_CASE("CUC idempotence") { - etl::array<uint8_t, 9> input1 = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; - TimeStamp<3, 2> time1(input1); - etl::array<uint8_t, 9> cuc1 = time1.toCUCtimestamp(); - - for (uint8_t i = 0; i < 9; i++) { - CHECK(input1[i] == cuc1[i]); - } - - etl::array<uint8_t, 9> input2 = {0b10101101, 0b10100000, 218, 103, 11, 0, 3, 23, 0}; - TimeStamp<5, 1> time2(input2); - etl::array<uint8_t, 9> cuc2 = time2.toCUCtimestamp(); - - for (auto i = 0; i < 9; i++) { - CHECK(input2[i] == cuc2[i]); - } - - etl::array<uint8_t, 9> input3 = {0b10100011, 0b10001100, 218, 103, 11, 0, 3, 23, 2}; - TimeStamp<1, 6> time3(input3); - etl::array<uint8_t, 9> cuc3 = time3.toCUCtimestamp(); - - for (auto i = 0; i < 9; i++) { - CHECK(input3[i] == cuc3[i]); - } -} - -TEST_CASE("Conversion between CUC formats") { - SECTION("Base unit conversion") { - TimeStamp<2, 2> time1(20123); - TimeStamp<5, 2> time2(time1.toCUCtimestamp()); - CHECK(time1.asTAIseconds() == time2.asTAIseconds()); - } - - SECTION("Floating unit conversion") { - etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; - TimeStamp<3, 2> time1(timeInput); - - TimeStamp<3, 5> time2(time1.toCUCtimestamp()); - CHECK(time1.asTAIseconds() == time2.asTAIseconds()); - } - - SECTION("All units conversion") { - etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; - TimeStamp<3, 2> time1(timeInput); - - TimeStamp<4, 4> time2(time1.toCUCtimestamp()); - CHECK(time1.asTAIseconds() == time2.asTAIseconds()); - } -} - -TEST_CASE("Use of custom Acubesat CUC format") { - SECTION("Check forward conversion") { - Time::CustomCUC_t customCUC1 = {1001}; - TimeStamp<3, 0> time1(customCUC1); - CHECK(time1.asTAIseconds() == 100); - CHECK(time1.asCustomCUCTimestamp().elapsed100msTicks == 1000); - TimeStamp<3, 2> time2(customCUC1); - CHECK(time2.asTAIseconds() == 100); - CHECK(time2.asCustomCUCTimestamp().elapsed100msTicks == 1000); - - // check rounding errors - Time::CustomCUC_t customCUC2 = {1004}; - TimeStamp<3, 0> time3(customCUC2); - CHECK(time3.asTAIseconds() == 100); - CHECK(time3.asCustomCUCTimestamp().elapsed100msTicks == 1000); - TimeStamp<3, 2> time4(customCUC2); - CHECK(time4.asTAIseconds() == 100); - CHECK(time4.asCustomCUCTimestamp().elapsed100msTicks == 1003); - - // check rounding errors - Time::CustomCUC_t customCUC3 = {1005}; - TimeStamp<3, 0> time5(customCUC3); - CHECK(time5.asTAIseconds() == 100); - CHECK(time5.asCustomCUCTimestamp().elapsed100msTicks == 1000); - TimeStamp<3, 2> time6(customCUC3); - CHECK(time6.asTAIseconds() == 100); - CHECK(time6.asCustomCUCTimestamp().elapsed100msTicks == 1005); - } - - SECTION("Check idempotence") { - Time::CustomCUC_t customCUC1 = {1000}; - TimeStamp<3, 3> time1(customCUC1); - Time::CustomCUC_t customCUC2 = time1.asCustomCUCTimestamp(); - CHECK(customCUC1.elapsed100msTicks == customCUC2.elapsed100msTicks); - } -} - -TEST_CASE("UTC idempotence") { - { - UTCTimestamp timestamp1(2020, 4, 10, 10, 15, 0); // 10 Apr 2020, 10:15:00; - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); - UTCTimestamp timestamp2 = time.toUTCtimestamp(); - bool cond = (timestamp2 == timestamp1); - REQUIRE(cond); - } - { - UTCTimestamp timestamp1(2035, 1, 1, 0, 0, 1); // 1 Jan 2035 midnight passed; - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); - UTCTimestamp timestamp2 = time.toUTCtimestamp(); - bool cond = (timestamp2 == timestamp1); - REQUIRE(cond); - } -} - -TEST_CASE("UTC conversion to and from seconds timestamps") { - { - UTCTimestamp timestamp1(2020, 12, 5, 0, 0, 0); - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); - REQUIRE(time.asTAIseconds() == 29289600); - } - { - UTCTimestamp timestamp1(2020, 2, 29, 0, 0, 0); - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); - REQUIRE(time.asTAIseconds() == 5097600); - } - { - UTCTimestamp timestamp1(2025, 3, 10, 0, 0, 0); - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); - REQUIRE(time.asTAIseconds() == 163728000); - } -} - -// SECTION("Check different templates, should break at compile"){ -// TimeStamp<1, 2> time1; -// TimeStamp<4, 4> time2; -// REQUIRE(time1==time2); -// } - -TEST_CASE("Time operators") { - TimeStamp<1, 2> time1; - TimeStamp<1, 2> time2; - TimeStamp<1, 2> time3(10); - TimeStamp<1, 2> time4(12); - TimeStamp<1, 2> time5(10); - TimeStamp<2, 2> time6; - REQUIRE(time1 == time2); - REQUIRE(time2 == time1); - REQUIRE(time3 == time5); - REQUIRE(time1 != time3); - REQUIRE(time3 != time4); - REQUIRE(time3 <= time4); - REQUIRE(time3 < time4); - - // REQUIRE(time1 == time6); //should fail at compile, different templates -} - -TEST_CASE("Time runtime class size") { - int input_time = 1000; - TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(input_time); - REQUIRE(sizeof(time) < 32); -} diff --git a/test/Time/TimeStampTests.cpp b/test/Time/TimeStampTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f5338de0d5cc600cb0bfcd0967229aeac60658b0 --- /dev/null +++ b/test/Time/TimeStampTests.cpp @@ -0,0 +1,353 @@ +#include "../Services/ServiceTests.hpp" +#include "Time/TimeStamp.hpp" +#include "catch2/catch_all.hpp" + +using namespace Time; +using Catch::Approx; + +TEST_CASE("TimeStamp class construction") { + // SECTION("Initialize with excessive precision, breaks at compile time"){ + // TimeStamp<5, 10> Epoch3; + // TimeStamp<4, 4> Epoch4; + // } +} + +TEST_CASE("CUC headers generation") { + auto cuc_header1 = buildCUCHeader<uint8_t, 2, 2>(); + CHECK(cuc_header1 == 0b00100110); + + auto cuc_header2 = buildCUCHeader<uint8_t, 4, 1>(); + CHECK(cuc_header2 == 0b00101101); + + auto cuc_header3 = buildCUCHeader<uint8_t, 1, 1>(); + CHECK(cuc_header3 == 0b00100001); + + auto cuc_header4 = buildCUCHeader<uint16_t, 5, 1>(); + CHECK(cuc_header4 == 0b1010110110100000); + + auto cuc_header5 = buildCUCHeader<uint16_t, 1, 6>(); + CHECK(cuc_header5 == 0b1010001110001100); + + auto cuc_header6 = buildCUCHeader<uint16_t, 7, 1>(); + CHECK(cuc_header6 == 0b1010110111100000); +} + +TEST_CASE("TAI idempotence") { + int input_time = 1000; + TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(input_time); + + REQUIRE(time.asTAIseconds() == input_time); +} + +TEST_CASE("CUC idempotence") { + etl::array<uint8_t, 9> input1 = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; + TimeStamp<3, 2> time1(input1); + etl::array<uint8_t, 9> cuc1 = time1.formatAsCUC(); + + for (uint8_t i = 0; i < 9; i++) { + CHECK(input1[i] == cuc1[i]); + } + + etl::array<uint8_t, 9> input2 = {0b10101101, 0b10100000, 218, 103, 11, 0, 3, 23, 0}; + TimeStamp<5, 1> time2(input2); + etl::array<uint8_t, 9> cuc2 = time2.formatAsCUC(); + + for (auto i = 0; i < 9; i++) { + CHECK(input2[i] == cuc2[i]); + } + + etl::array<uint8_t, 9> input3 = {0b10100011, 0b10001100, 218, 103, 11, 0, 3, 23, 2}; + TimeStamp<1, 6> time3(input3); + etl::array<uint8_t, 9> cuc3 = time3.formatAsCUC(); + + for (auto i = 0; i < 9; i++) { + CHECK(input3[i] == cuc3[i]); + } +} + +TEST_CASE("Conversion between CUC formats") { + SECTION("Base unit conversion") { + TimeStamp<2, 2> time1(20123); + TimeStamp<5, 2> time2(time1.formatAsCUC()); + CHECK(time1.asTAIseconds() == time2.asTAIseconds()); + } + + SECTION("Floating unit conversion") { + etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; + TimeStamp<3, 2> time1(timeInput); + + TimeStamp<3, 5> time2(time1.formatAsCUC()); + CHECK(time1.asTAIseconds() == time2.asTAIseconds()); + } + + SECTION("All units conversion") { + etl::array<uint8_t, 9> timeInput = {0b00101010, 0, 1, 1, 3, 0, 0, 0, 0}; + TimeStamp<3, 2> time1(timeInput); + + TimeStamp<4, 4> time2(time1.formatAsCUC()); + CHECK(time1.asTAIseconds() == time2.asTAIseconds()); + } +} + +TEST_CASE("Use of custom Acubesat CUC format") { + SECTION("Check forward conversion") { + using namespace Time; + DefaultCUC defaultCUC1(1001_t); + TimeStamp<3, 0> time1(defaultCUC1); + CHECK(time1.asTAIseconds() == 100); + CHECK(DefaultCUC(time1).formatAsBytes() == 1000); + TimeStamp<3, 2> time2(defaultCUC1); + CHECK(time2.asTAIseconds() == 100); + CHECK(DefaultCUC(time2).formatAsBytes() == 1001); + + // check rounding errors + Time::DefaultCUC defaultCUC2(1004_t); + TimeStamp<3, 0> time3(defaultCUC2); + CHECK(time3.asTAIseconds() == 100); + CHECK(DefaultCUC(time3).formatAsBytes() == 1000); + TimeStamp<3, 2> time4(defaultCUC2); + CHECK(time4.asTAIseconds() == 100); + CHECK(DefaultCUC(time4).formatAsBytes() == 1004); + + // check rounding errors + Time::DefaultCUC defaultCUC3(1005_t); + TimeStamp<3, 0> time5(defaultCUC3); + CHECK(time5.asTAIseconds() == 101); + CHECK(DefaultCUC(time5).formatAsBytes() == 1010); + TimeStamp<3, 2> time6(defaultCUC3); + CHECK(time6.asTAIseconds() == 100); + CHECK(DefaultCUC(time6).formatAsBytes() == 1005); + } + + SECTION("Check idempotence") { + Time::DefaultCUC defaultCUC1(1000_t); + TimeStamp<3, 3> time1(defaultCUC1); + Time::DefaultCUC defaultCUC2(time1); + CHECK(defaultCUC1 == defaultCUC2); + } +} + +TEST_CASE("UTC idempotence") { + { + UTCTimestamp timestamp1(2020, 4, 10, 10, 15, 0); // 10 Apr 2020, 10:15:00; + TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); + UTCTimestamp timestamp2 = time.toUTCtimestamp(); + bool cond = (timestamp2 == timestamp1); + CHECK(cond); + } + { + UTCTimestamp timestamp1(2035, 1, 1, 0, 0, 1); // 1 Jan 2035 midnight passed; + TimeStamp<CUCSecondsBytes, CUCFractionalBytes> time(timestamp1); + UTCTimestamp timestamp2 = time.toUTCtimestamp(); + bool cond = (timestamp2 == timestamp1); + CHECK(cond); + } +} + +TEST_CASE("UTC conversion to and from seconds timestamps") { + { + UTCTimestamp utc(2020, 12, 5, 0, 0, 0); + TimeStamp<4, 1> time(utc); + CHECK(time.asTAIseconds() == 29289600); + } + { + UTCTimestamp utc(2020, 2, 29, 0, 0, 0); + TimeStamp<4, 1> time(utc); + CHECK(time.asTAIseconds() == 5097600); + } + { + UTCTimestamp utc(2025, 3, 10, 0, 0, 0); + TimeStamp<4, 1> time(utc); + CHECK(time.asTAIseconds() == 163728000); + } + { + UTCTimestamp utc(2025, 3, 10, 0, 0, 0); + TimeStamp<4, 1, 2, 3> time(utc); + CHECK(time.asTAIseconds() == 163728000); + } +} + +TEST_CASE("UTC overflow tests") { + SECTION("Year too high") { + UTCTimestamp utc(2999, 3, 11, 0, 0, 0); + TimeStamp<2, 1> time(utc); + REQUIRE(ServiceTests::thrownError(ErrorHandler::TimeStampOutOfBounds)); + } + SECTION("Seconds too high, small variable") { + UTCTimestamp utc(Epoch.year, Epoch.month, Epoch.day, 0, 7, 0); + TimeStamp<1, 1> time(utc); + REQUIRE(ServiceTests::thrownError(ErrorHandler::TimeStampOutOfBounds)); + } + SECTION("Seconds too high, wide variable") { + UTCTimestamp utc(Epoch.year, Epoch.month, Epoch.day, 0, 7, 0); + TimeStamp<1, 4> time(utc); + REQUIRE(ServiceTests::thrownError(ErrorHandler::TimeStampOutOfBounds)); + } +} + +TEST_CASE("Time comparison operators") { + SECTION("Same type") { + TimeStamp<1, 2> time1; + TimeStamp<1, 2> time2; + TimeStamp<1, 2> time3(10); + TimeStamp<1, 2> time4(12); + TimeStamp<1, 2> time5(10); + CHECK(time1 == time2); + CHECK(time2 == time1); + CHECK(time3 == time5); + CHECK(time1 != time3); + CHECK(time3 != time4); + CHECK(time3 <= time4); + CHECK(time3 < time4); + } + + SECTION("Different size") { + TimeStamp<1, 2> time1(10); + TimeStamp<2, 1> time2(10); + TimeStamp<3, 2> time3(15); + TimeStamp<2, 2> time4(5); + CHECK(time1 == time2); + CHECK(time3 != time2); + CHECK(time4 < time2); + CHECK(time3 > time4); + CHECK(time2 >= time1); + CHECK(time1 <= time3); + } + + SECTION("Different units") { + TimeStamp<1, 2, 10> time1(10); + TimeStamp<1, 2, 1, 10> time2(10); + TimeStamp<3, 2, 3, 2> time3(15); + TimeStamp<2, 2, 57, 89> time4(5); + CHECK(time1 == time2); + CHECK(time3 != time2); + CHECK(time4 < time2); + CHECK(time3 > time4); + CHECK(time2 >= time1); + CHECK(time1 <= time3); + } + + SECTION("Overflow") { + TimeStamp<1, 0> time1(1); + TimeStamp<4, 4> time2(std::numeric_limits<uint32_t>::max()); + + CHECK(time1 != time2); + CHECK_FALSE(time1 == time2); + CHECK(time1 < time2); + CHECK(time1 <= time2); + CHECK(time2 > time1); + CHECK(time2 >= time1); + } +} + +TEST_CASE("Finding distance between times") { + using namespace std::literals; + + SECTION("Same type") { + TimeStamp<1, 2> time1(15); + TimeStamp<1, 2> time2(30); + + CHECK(time2 - time1 == 15s); + CHECK(time1 - time2 == -15s); + } + + SECTION("Different type") { + TimeStamp<1, 2> time1(15); + TimeStamp<2, 1> time2(30); + TimeStamp<1, 0, 1, 1000> time3(300ms); + TimeStamp<1, 2> time4(300ms); + TimeStamp<1, 3> time5(300ms); + + CHECK(time2 - time1 == 15s); + CHECK(time1 - time2 == -15s); + CHECK(time3 - time1 == -14700ms); + CHECK(time1 - time4 == time1 - time5); + } +} + +TEST_CASE("Time runtime class size") { + REQUIRE(sizeof(TimeStamp<CUCSecondsBytes, CUCFractionalBytes>) <= 8); +} + +TEST_CASE("CUC conversions") { + SECTION("Base unit, without fractions") { + TimeStamp<2, 0, 10, 1> time1(100); + CHECK(time1.asTAIseconds() == 100); + + TimeStamp<2, 0, 1, 10> time2(time1); + CHECK(time2.asTAIseconds() == 100); + } + + SECTION("Base unit, with fractions") { + TimeStamp<2, 2, 10, 1> time1(100); + CHECK(time1.asTAIseconds() == 100); + + TimeStamp<2, 2, 1, 10> time2(time1); + CHECK(time2.asTAIseconds() == 100); + } + + SECTION("Addition of fraction") { + TimeStamp<2, 0, 1, 1> time1(100); + CHECK(time1.asTAIseconds() == 100); + + TimeStamp<2, 2, 1, 1> time2(time1); + CHECK(time2.asTAIseconds() == 100); + } + + SECTION("Removal of fraction") { + TimeStamp<2, 2, 1, 1> time1(100); + CHECK(time1.asTAIseconds() == 100); + + TimeStamp<2, 0, 1, 1> time2(time1); + CHECK(time2.asTAIseconds() == 100); + } + + SECTION("Many changes") { + TimeStamp<2, 2, 3, 2> time1(1000); + CHECK(time1.asTAIseconds() == Approx(1000).epsilon(1)); + + TimeStamp<3, 4, 100, 29> time2(time1); + CHECK(time2.asTAIseconds() == Approx(1000).epsilon(1)); + + TimeStamp<2, 1, 1, 1> time3(time1); + CHECK(time3.asTAIseconds() == Approx(1000).epsilon(1)); + } + + SECTION("Large numbers") { + TimeStamp<4, 0, 1, 1> time1(10000); + CHECK(time1.asTAIseconds() == 10000); + + TimeStamp<2, 1, 7907, 7559> time2(time1); + CHECK(time2.asTAIseconds() == 9999); + } +} + +TEST_CASE("Duration conversions") { + using namespace std::chrono_literals; + + SECTION("Conversion to duration") { + TimeStamp<2, 2, 1, 1> time(3600); + auto duration = time.asDuration<std::chrono::hours>(); + CHECK(duration == 1h); + } + + SECTION("Conversion from duration") { + auto duration = 90min; + TimeStamp<2, 2, 1, 1> time(duration); + CHECK(time.asTAIseconds() == 5400); + } + + SECTION("Duration idempotence") { + auto duration = 13532s; + TimeStamp<2, 2, 1, 1> time(duration); + + CHECK(time.asDuration() == duration); + } + + SECTION("Overflow") { + auto duration = 24h; + TimeStamp<2, 2, 1, 1> time(duration); + + CHECK(time.asTAIseconds() == 20864); + } +} diff --git a/test/tests.cpp b/test/tests.cpp index f481b16fe25e9b620f1dcf078a53f4f357d466c5..13928b9a80b6b91a1f06b6c17727c32de4098f69 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -1,3 +1,3 @@ #define CATCH_CONFIG_MAIN -#include <catch2/catch.hpp> +#include <catch2/catch_all.hpp>