diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d60013b3ee2fb8f46558d46b891e11be189d1c8..51091706143f679881f5743fe96f47001f63aa8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,9 +37,11 @@ add_library(common OBJECT src/Services/RequestVerificationService.cpp src/Services/TestService.cpp src/Services/LargePacketTransferService.cpp - src/Services/EventActionService.cpp + src/Services/EventActionService.cpp src/Services/TimeBasedSchedulingService.cpp src/Services/FunctionManagementService.cpp + src/Services/ParameterStatisticsService.cpp + src/Helpers/Statistic.cpp ) # Specify the .cpp files for the executables diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 3ae97055f0221c61079d2c14bf1d86af01f1b56f..216ea418d9ed7f8912a887ca00839fa939cde4b4 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -23,7 +23,6 @@ */ #define ECSS_MAX_MESSAGE_SIZE 1024U - /** * The maximum size of a regular ECSS message, plus its headers and trailing data, in bytes */ @@ -144,11 +143,21 @@ /** * @brief Size of the array holding the Parameter objects for the ST[20] parameter service */ -#define ECSS_PARAMETER_COUNT 3 +#define ECSS_PARAMETER_COUNT 4 /** * @brief Defines whether the optional CRC field is included */ #define ECSS_CRC_INCLUDED true +/** + * Number of parameters whose statistics we need and are going to be stored into the statisticsMap + */ +#define ECSS_MAX_STATISTIC_PARAMETERS 4 + +/** + * Whether the ST[04] statistics calculation supports the reporting of stddev + */ +#define SUPPORTS_STANDARD_DEVIATION true + #endif // ECSS_SERVICES_ECSS_DEFINITIONS_H diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index 0c07fe079c4f98002cbcc7d17c56a17e93e521d2..d836ba26a95d90745c85c166699a65ef373294be 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -142,7 +142,25 @@ public: /** * Attempt to access a non existing parameter (ST[20]) */ - GetNonExistingParameter = 8 + GetNonExistingParameter = 8, + /** + * Attempt to set a reporting rate which is smaller than the parameter sampling rate. + * ST[04] + */ + InvalidReportingRateError = 9, + /** + * Attempt to set a sampling rate which is greater than the parameter reporting rate. + * ST[04] + */ + InvalidSamplingRateError = 10, + /** + * Attempt to add definition to the struct map but its already full. (ST[19]) + */ + EventActionDefinitionsMapIsFull = 11, + /** + * Attempt to add new statistic definition but the maximum number is already reached (ST[04]) + */ + MaxStatisticDefinitionsReached = 12, }; /** diff --git a/inc/Services/Parameter.hpp b/inc/Helpers/Parameter.hpp similarity index 94% rename from inc/Services/Parameter.hpp rename to inc/Helpers/Parameter.hpp index d2fbe07053aa21e514b3782cd14c109151b02116..7967011acf674d70453a84fa8b0990e6c902f983 100644 --- a/inc/Services/Parameter.hpp +++ b/inc/Helpers/Parameter.hpp @@ -34,6 +34,7 @@ class ParameterBase { public: virtual void appendValueToMessage(Message& message) = 0; virtual void setValueFromMessage(Message& message) = 0; + virtual double getValueAsDouble() = 0; }; /** @@ -57,6 +58,10 @@ public: return currentValue; } + inline double getValueAsDouble() override { + return static_cast<double>(currentValue); + } + /** * Given an ECSS message that contains this parameter as its first input, this loads the value from that paremeter */ @@ -72,4 +77,4 @@ public: }; }; -#endif //ECSS_SERVICES_PARAMETER_HPP +#endif // ECSS_SERVICES_PARAMETER_HPP diff --git a/inc/Helpers/Statistic.hpp b/inc/Helpers/Statistic.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8934b9fcc434346d57dd535b625e6177fd44b5dd --- /dev/null +++ b/inc/Helpers/Statistic.hpp @@ -0,0 +1,56 @@ +#ifndef ECSS_SERVICES_STATISTIC_HPP +#define ECSS_SERVICES_STATISTIC_HPP + +#include "ECSS_Definitions.hpp" +#include "Service.hpp" +#include "ErrorHandler.hpp" +#include "etl/vector.h" +#include <cmath> +#include <cfloat> + +/** + * Class containing all the statistics for every parameter. Includes functions that calculate and append the + * statistics to messages + */ +class Statistic { +public: + uint16_t selfSamplingInterval = 0; + uint16_t sampleCounter = 0; + uint32_t maxTime = 0; + uint32_t minTime = 0; // TODO: CUC Format timestamp + double max = -std::numeric_limits<double>::infinity(); + double min = std::numeric_limits<double>::infinity(); + double sumOfSquares = 0; + double mean = 0; + + Statistic() = default; + + /** + * Gets the value from the sensor as argument and updates the statistics without storing it + * @param value returned value from "getValue()" of Parameter.hpp, i.e. the last sampled value from a parameter + */ + void updateStatistics(double value); + + /** + * Resets all statistics calculated to default values + */ + void resetStatistics(); + + /** + * Appends itself to the received Message + * message. + */ + void appendStatisticsToMessage(Message& report); + + /** + * Setter function + */ + void setSelfSamplingInterval(uint16_t samplingInterval); + + /** + * Check if all the statistics are initialized + */ + bool statisticsAreInitialized(); +}; + +#endif \ No newline at end of file diff --git a/inc/Message.hpp b/inc/Message.hpp index 2ee8d3c85f0d302c6d2e71ff73d7cb3d110f2d43..0cb719c68b11c69b2788e4a4074da6f784de0b20 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -350,6 +350,15 @@ public: return appendWord(reinterpret_cast<uint32_t&>(value)); } + /** + * Adds a double to the end of the message + */ + void appendDouble(double value) { + static_assert(sizeof(uint64_t) == sizeof(value), "Double numbers must be 64 bits long"); + + return appendUint64(reinterpret_cast<uint64_t&>(value)); + } + /** * Adds a N-byte string to the end of the message * @@ -512,6 +521,13 @@ public: return reinterpret_cast<float&>(value); } + float 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 N-byte string from the current position in the message * @@ -535,7 +551,7 @@ public: * https://www.fluentcpp.com/2017/08/15/function-templates-partial-specialization-cpp/ * @tparam MAX_SIZE The memory size of the string in bytes, which corresponds to the max string size */ - template<const size_t MAX_SIZE> + template <const size_t MAX_SIZE> String<MAX_SIZE> readOctetString() { String<MAX_SIZE> string(""); @@ -614,37 +630,108 @@ public: } }; -template<> inline void Message::append(const uint8_t& value) { appendUint8(value); } -template<> inline void Message::append(const uint16_t& value) { appendUint16(value); } -template<> inline void Message::append(const uint32_t& value) { appendUint32(value); } -template<> inline void Message::append(const uint64_t& value) { appendUint64(value); } - -template<> inline void Message::append(const int8_t& value) { appendSint8(value); } -template<> inline void Message::append(const int16_t& value) { appendSint16(value); } -template<> inline void Message::append(const int32_t& value) { appendSint32(value); } - -template<> inline void Message::append(const bool& value) { appendBoolean(value); } -template<> inline void Message::append(const char& value) { appendByte(value); } -template<> inline void Message::append(const float& value) { appendFloat(value); } +template <> +inline void Message::append(const uint8_t& value) { + appendUint8(value); +} +template <> +inline void Message::append(const uint16_t& value) { + appendUint16(value); +} +template <> +inline void Message::append(const uint32_t& value) { + appendUint32(value); +} +template <> +inline void Message::append(const uint64_t& value) { + appendUint64(value); +} + +template <> +inline void Message::append(const int8_t& value) { + appendSint8(value); +} +template <> +inline void Message::append(const int16_t& value) { + appendSint16(value); +} +template <> +inline void Message::append(const int32_t& value) { + appendSint32(value); +} + +template <> +inline void Message::append(const bool& value) { + appendBoolean(value); +} +template <> +inline void Message::append(const char& value) { + appendByte(value); +} +template <> +inline void Message::append(const float& value) { + appendFloat(value); +} +template <> +inline void Message::append(const double& value) { + appendDouble(value); +} /** * Appends an ETL string to the message. ETL strings are handled as ECSS octet strings, meaning that the string size * is appended as a byte before the string itself. To append other string sequences, see the Message::appendString() * functions */ -template<> inline void Message::append(const etl::istring& value) { appendOctetString(value); } - -template<> inline uint8_t Message::read() { return readUint8(); } -template<> inline uint16_t Message::read() { return readUint16(); } -template<> inline uint32_t Message::read() { return readUint32(); } -template<> inline uint64_t Message::read() { return readUint64(); } - -template<> inline int8_t Message::read() { return readSint8(); } -template<> inline int16_t Message::read() { return readSint16(); } -template<> inline int32_t Message::read() { return readSint32(); } - -template<> inline bool Message::read<bool>() { return readBoolean(); } -template<> inline char Message::read() { return readByte(); } -template<> inline float Message::read() { return readFloat(); } +template <> +inline void Message::append(const etl::istring& value) { + appendOctetString(value); +} + +template <> +inline uint8_t Message::read() { + return readUint8(); +} +template <> +inline uint16_t Message::read() { + return readUint16(); +} +template <> +inline uint32_t Message::read() { + return readUint32(); +} +template <> +inline uint64_t Message::read() { + return readUint64(); +} + +template <> +inline int8_t Message::read() { + return readSint8(); +} +template <> +inline int16_t Message::read() { + return readSint16(); +} +template <> +inline int32_t Message::read() { + return readSint32(); +} + +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(); +} #endif // ECSS_SERVICES_PACKET_H diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp index 82517e8203cd2eb0735e3625c82ef97b9eb0646f..5b89e20cb4925833e2a2541c88cda7d758df9acd 100644 --- a/inc/MessageParser.hpp +++ b/inc/MessageParser.hpp @@ -33,7 +33,7 @@ class MessageParser { public: /** - * This function takes as input TC packets and calls the proper services' functions that have been + * This function takes as input TC packets and calls the proper services' functions that have been * implemented to handle TC packets. * * @param message Contains the necessary parameters to call the suitable subservice diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp index a627d61ae8dcb1790d5c6db133c18096a1d6f7f4..d1ee738207484dd77aa4c4f8b53b3cbd0d77f811 100644 --- a/inc/Platform/x86/ECSS_Configuration.hpp +++ b/inc/Platform/x86/ECSS_Configuration.hpp @@ -30,6 +30,7 @@ #define SERVICE_LARGEPACKET ///< Compile ST[13] large packet transfer #define SERVICE_MEMORY ///< Compile ST[06] memory management #define SERVICE_PARAMETER ///< Compile ST[20] parameter management +#define SERVICE_PARAMETERSTATISTICS ///< Compile ST[04] parameter statistics #define SERVICE_REQUESTVERIFICATION ///< Compile ST[01] request verification #define SERVICE_TEST ///< Compile ST[17] test #define SERVICE_TIME ///< Compile ST[09] time management diff --git a/inc/Platform/x86/Parameters/SystemParameters.hpp b/inc/Platform/x86/Parameters/SystemParameters.hpp index 1e58687e46c5d39b4d374fcc68cb4d2aa8b8f564..45ee285d545754238cdea46883c03b03784c6521 100644 --- a/inc/Platform/x86/Parameters/SystemParameters.hpp +++ b/inc/Platform/x86/Parameters/SystemParameters.hpp @@ -1,4 +1,4 @@ -#include "Services/Parameter.hpp" +#include "Helpers/Parameter.hpp" #include "etl/vector.h" /** * @author Athanasios Theocharis <athatheoc@gmail.com> @@ -19,12 +19,12 @@ public: Parameter<uint8_t> parameter1 = Parameter<uint8_t>(3); Parameter<uint16_t> parameter2 = Parameter<uint16_t>(7); Parameter<uint32_t> parameter3 = Parameter<uint32_t>(10); + Parameter<uint32_t> parameter4 = Parameter<uint32_t>(24); /** * The key of the array is the ID of the parameter as specified in PUS */ - etl::array<std::reference_wrapper<ParameterBase>, ECSS_PARAMETER_COUNT> parametersArray = { - parameter1, parameter2, parameter3 - }; + etl::array<std::reference_wrapper<ParameterBase>, ECSS_PARAMETER_COUNT> parametersArray = {parameter1, parameter2, + parameter3, parameter4}; SystemParameters() = default; }; diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp index 293b00c3b51ce2a33522dcb40a6d47214e4cd72d..bdfd7103c9527019641c1671be70806159b07f46 100644 --- a/inc/ServicePool.hpp +++ b/inc/ServicePool.hpp @@ -11,6 +11,7 @@ #include "Services/TestService.hpp" #include "Services/MemoryManagementService.hpp" #include "Services/FunctionManagementService.hpp" +#include "Services/ParameterStatisticsService.hpp" /** * Defines a class that contains instances of all Services. @@ -22,8 +23,8 @@ class ServicePool { * A counter for messages * * Each key-value pair corresponds to one MessageType within a Service. For the key, the most significant 8 bits are - * the number of the service, while the least significant 8 bits are the number of the Message. The value is the - * counter of each MessageType. + * the number of the service, while the least significant 8 bits are the number of the Message. The value is the + * counter of each MessageType. */ etl::map<uint16_t, uint16_t, ECSS_TOTAL_MESSAGE_TYPES> messageTypeCounter; @@ -31,7 +32,12 @@ class ServicePool { * A counter for messages that corresponds to the total number of TM packets sent from an APID */ uint16_t packetSequenceCounter = 0; + public: +#ifdef SERVICE_PARAMETERSTATISTICS + ParameterStatisticsService parameterStatistics; +#endif + #ifdef SERVICE_EVENTACTION EventActionService eventAction; #endif diff --git a/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp index 45fd2fc59d7defed4c3d0faad6d65e62379c4712..eefbea6e0b487d61f53a8b9b61c709698a3cbeab 100644 --- a/inc/Services/ParameterService.hpp +++ b/inc/Services/ParameterService.hpp @@ -4,8 +4,7 @@ #include "ECSS_Definitions.hpp" #include "Service.hpp" #include "ErrorHandler.hpp" -#include "Parameter.hpp" -#include "Parameters/SystemParameters.hpp" +#include "Helpers/Parameter.hpp" /** * Implementation of the ST[20] parameter management service, diff --git a/inc/Services/ParameterStatisticsService.hpp b/inc/Services/ParameterStatisticsService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cd6de679831b05ca947368c11c0464240db77ad6 --- /dev/null +++ b/inc/Services/ParameterStatisticsService.hpp @@ -0,0 +1,114 @@ +#ifndef ECSS_SERVICES_PARAMETERSTATISTICSSERVICE_HPP +#define ECSS_SERVICES_PARAMETERSTATISTICSSERVICE_HPP + +#include "ECSS_Definitions.hpp" +#include "Service.hpp" +#include "ErrorHandler.hpp" +#include "Parameters/SystemParameters.hpp" +#include "Helpers/Statistic.hpp" +#include "etl/deque.h" +#include "etl/map.h" + +/** + * Implementation of the ST[04] parameter statistics reporting service, as defined in ECSS-E-ST-70-41C. + * @author Konstantinos Petridis <petridkon@gmail.com> + */ +class ParameterStatisticsService : public Service { +public: + inline static const uint8_t ServiceType = 4; + + enum MessageType : uint8_t { + ReportParameterStatistics = 1, + ParameterStatisticsReport = 2, + ResetParameterStatistics = 3, + EnablePeriodicParameterReporting = 4, + DisablePeriodicParameterReporting = 5, + AddOrUpdateParameterStatisticsDefinitions = 6, + DeleteParameterStatisticsDefinitions = 7, + ReportParameterStatisticsDefinitions = 8, + ParameterStatisticsDefinitionsReport = 9, + }; + + /** + * Map containing parameters' IDs followed by the statistics that correspond to the specified parameter + */ + etl::map<uint16_t, Statistic, ECSS_MAX_STATISTIC_PARAMETERS> statisticsMap; + + /** + * true means that the periodic statistics reporting is enabled + */ + bool periodicStatisticsReportingStatus = false; + /** + * If true, after every report reset the parameter statistics. + */ + bool hasAutomaticStatisticsReset = false; // todo: do const + /** + * Indicates whether to append/read the sampling interval to/from message + */ + const bool supportsSamplingInterval = true; + /** + * The parameter statistics reporting interval + */ + uint16_t reportingInterval = 5; // TODO: Must define units. Same as parameter sampling rates + + /** + * TC[4,1] report the parameter statistics, by calling parameterStatisticsReport() + */ + void reportParameterStatistics(Message& request); + + /** + * Constructs and stores a TM[4,2] packet containing the parameter statistics report. + */ + void parameterStatisticsReport(); + + /** + * TC[4,3] reset parameter statistics, clearing all samples and values. This is the function called by TC from + * the GS. + */ + void resetParameterStatistics(Message& request); + + /** + * This function clears all the samples. + */ + void resetParameterStatistics(); + + /** + * TC[4,4] enable periodic parameter statistics reporting + */ + void enablePeriodicStatisticsReporting(Message& request); + + /** + * TC[4,5] disable periodic parameter statistics reporting + */ + void disablePeriodicStatisticsReporting(Message& request); + + /** + * TC[4,6] add or update parameter statistics definitions + */ + void addOrUpdateStatisticsDefinitions(Message& request); + + /** + * TC[4,7] delete parameter statistics definitions. + */ + void deleteStatisticsDefinitions(Message& request); + + /** + * TC[4,8] report the parameter statistics definitions, by calling statisticsDefinitionsReport() + */ + void reportStatisticsDefinitions(Message& request); + /** + * Constructs and stores a TM[4,9] packet containing the parameter statistics definitions report. + */ + void statisticsDefinitionsReport(); + + /** + * Calls the suitable function that executes a telecommand packet. The source of that packet + * is the ground station. + * + * @note This function is called from the main execute() that is defined in the file MessageParser.hpp + * @param message Contains the necessary parameters to call the suitable subservice + */ + void execute(Message& message); +}; + +#endif \ No newline at end of file diff --git a/src/Helpers/Statistic.cpp b/src/Helpers/Statistic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70af6e62bbb42b740ceee5e357b2aa5863c931c5 --- /dev/null +++ b/src/Helpers/Statistic.cpp @@ -0,0 +1,62 @@ +#include <iostream> +#include "Helpers/Statistic.hpp" + +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(); + } + if (value < min) { + min = value; + // TODO: minTime = as_CUC_timestamp(); + } + if (sampleCounter + 1 > 0) { + mean = (mean * sampleCounter + value) / (sampleCounter + 1); + } + sumOfSquares += pow(value, 2); + sampleCounter++; +} + +void Statistic::appendStatisticsToMessage(Message& report) { + report.appendFloat(static_cast<float>(max)); + report.appendUint32(maxTime); + report.appendFloat(static_cast<float>(min)); + report.appendUint32(minTime); + report.appendFloat(static_cast<float>(mean)); + + if (SUPPORTS_STANDARD_DEVIATION) { + double standardDeviation = 0; + if (sampleCounter == 0) { + standardDeviation = 0; + } else { + double meanOfSquares = sumOfSquares / sampleCounter; + standardDeviation = sqrt(abs(meanOfSquares - pow(mean, 2))); + } + report.appendFloat(static_cast<float>(standardDeviation)); + } +} + +void Statistic::setSelfSamplingInterval(uint16_t samplingInterval) { + this->selfSamplingInterval = samplingInterval; +} + +void Statistic::resetStatistics() { + max = -std::numeric_limits<double>::infinity(); + min = std::numeric_limits<double>::infinity(); + maxTime = 0; + minTime = 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 + max == -std::numeric_limits<double>::infinity() and min == std::numeric_limits<double>::infinity()); +} diff --git a/src/Message.cpp b/src/Message.cpp index 21732cb98ab8586e1fc26635dff5884da089328f..1a2ea7a593e4e8cb2f49c6f0512287d197849ae5 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -6,7 +6,7 @@ #include <MessageParser.hpp> Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, uint16_t applicationId) - : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(applicationId) {} + : 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 @@ -174,7 +174,7 @@ void Message::appendString(const etl::istring& string) { void Message::appendFixedString(const etl::istring& string) { ASSERT_INTERNAL((dataSize + string.max_size()) < ECSS_MAX_MESSAGE_SIZE, 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 da379e2ceeb681aff0a856fff0883a4c2236a7d3..b23235a42a39b777d2a4bdf95dae663f4b8ebb8d 100644 --- a/src/MessageParser.cpp +++ b/src/MessageParser.cpp @@ -7,42 +7,56 @@ void MessageParser::execute(Message& message) { switch (message.serviceType) { +#ifdef SERVICE_PARAMETERSTATISTICS + case 4: + Services.parameterStatistics.execute(message); + break; +#endif + #ifdef SERVICE_EVENTREPORT - case 5: Services.eventReport.execute(message); // ST[05] + case 5: + Services.eventReport.execute(message); // ST[05] break; #endif #ifdef SERVICE_MEMORY - case 6: Services.memoryManagement.execute(message); // ST[06] + case 6: + Services.memoryManagement.execute(message); // ST[06] break; #endif #ifdef SERVICE_FUNCTION - case 8: Services.functionManagement.execute(message); // ST[08] + case 8: + Services.functionManagement.execute(message); // ST[08] break; #endif #ifdef SERVICE_TIMESCHEDULING - case 11: Services.timeBasedScheduling.execute(message); // ST[11] + case 11: + Services.timeBasedScheduling.execute(message); // ST[11] break; #endif #ifdef SERVICE_TEST - case 17: Services.testService.execute(message); // ST[17] + case 17: + Services.testService.execute(message); // ST[17] break; #endif #ifdef SERVICE_EVENTACTION - case 19: Services.eventAction.execute(message); // ST[19] + case 19: + Services.eventAction.execute(message); // ST[19] break; #endif #ifdef SERVICE_PARAMETER - case 20: Services.parameterManagement.execute(message); // ST[20] + case 20: + Services.parameterManagement.execute(message); // ST[20] break; #endif - default: ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); + default: + ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); } } @@ -180,8 +194,7 @@ String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::compose(const Message& message) { #if ECSS_CRC_INCLUDED // Append CRC field - uint16_t crcField = CRCHelper::calculateCRC(reinterpret_cast<uint8_t*>(ccsdsMessage.data()), 6 + - packetDataLength); + uint16_t crcField = CRCHelper::calculateCRC(reinterpret_cast<uint8_t*>(ccsdsMessage.data()), 6 + packetDataLength); ccsdsMessage.push_back(static_cast<uint8_t>(crcField >> 8U)); ccsdsMessage.push_back(static_cast<uint8_t>(crcField & 0xFF)); #endif @@ -189,7 +202,6 @@ String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::compose(const Message& message) { return ccsdsMessage; } - void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message) { ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage); diff --git a/src/Platform/x86/main.cpp b/src/Platform/x86/main.cpp index b74c6eab60a719933816cb5a5ce68ede6f52d684..a9060ed119a802eaf0d1c580d4d34a39245f6c9e 100644 --- a/src/Platform/x86/main.cpp +++ b/src/Platform/x86/main.cpp @@ -1,5 +1,4 @@ #include <iostream> -#include <ServicePool.hpp> #include <Logger.hpp> #include "Helpers/CRCHelper.hpp" #include "Helpers/TimeHelper.hpp" @@ -12,12 +11,14 @@ #include "Services/EventActionService.hpp" #include "Services/LargePacketTransferService.hpp" #include "Services/TimeBasedSchedulingService.hpp" -#include "ServicePool.hpp" +#include "Services/ParameterStatisticsService.hpp" +#include "Helpers/Statistic.hpp" #include "Message.hpp" #include "MessageParser.hpp" -#include "Helpers/CRCHelper.hpp" #include "ErrorHandler.hpp" #include "etl/String.hpp" +#include "ServicePool.hpp" +#include <ctime> int main() { LOG_NOTICE << "ECSS Services test application"; @@ -40,7 +41,8 @@ int main() { // ST[17] test TestService& testService = Services.testService; - Message receivedPacket = Message(TestService::ServiceType, TestService::MessageType::AreYouAliveTest, Message::TC, 1); + Message receivedPacket = + Message(TestService::ServiceType, TestService::MessageType::AreYouAliveTest, Message::TC, 1); testService.areYouAlive(receivedPacket); receivedPacket = Message(TestService::ServiceType, TestService::MessageType::OnBoardConnectionTest, Message::TC, 1); receivedPacket.appendUint16(7); @@ -50,14 +52,16 @@ int main() { ParameterService& paramService = Services.parameterManagement; // Test code for reportParameter - Message sentPacket = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); // application id is a dummy number (1) + 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 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) + 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 @@ -76,7 +80,8 @@ int main() { *(pStr + 2) = 0; MemoryManagementService& memMangService = Services.memoryManagement; - Message rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::DumpRawMemoryData, Message::TC, 1); + 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 @@ -89,7 +94,8 @@ int main() { rcvPack.appendUint16(sizeof(yetAnotherStr) / sizeof(yetAnotherStr[0])); memMangService.rawDataMemorySubservice.dumpRawData(rcvPack); - rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::LoadRawMemoryDataAreas, Message::TC, 1); + rcvPack = Message(MemoryManagementService::ServiceType, + MemoryManagementService::MessageType::LoadRawMemoryDataAreas, Message::TC, 1); uint8_t data[2] = {'h', 'R'}; rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID @@ -102,7 +108,8 @@ int main() { rcvPack.appendBits(16, CRCHelper::calculateCRC(data, 1)); // Append the CRC value memMangService.rawDataMemorySubservice.loadRawData(rcvPack); - rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::CheckRawMemoryData, Message::TC, 1); + rcvPack = Message(MemoryManagementService::ServiceType, MemoryManagementService::MessageType::CheckRawMemoryData, + Message::TC, 1); rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID rcvPack.appendUint16(2); // Iteration count @@ -116,33 +123,41 @@ int main() { RequestVerificationService& reqVerifService = Services.requestVerification; - Message receivedMessage = Message(RequestVerificationService::ServiceType, - RequestVerificationService::MessageType::SuccessfulAcceptanceReport, - Message::TC, 3); + Message receivedMessage = + Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::SuccessfulAcceptanceReport, Message::TC, 3); reqVerifService.successAcceptanceVerification(receivedMessage); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::FailedAcceptanceReport, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::FailedAcceptanceReport, Message::TC, 3); reqVerifService.failAcceptanceVerification(receivedMessage, ErrorHandler::UnknownAcceptanceError); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::SuccessfulStartOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::SuccessfulStartOfExecution, Message::TC, 3); reqVerifService.successStartExecutionVerification(receivedMessage); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::FailedStartOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::FailedStartOfExecution, Message::TC, 3); reqVerifService.failStartExecutionVerification(receivedMessage, ErrorHandler::UnknownExecutionStartError); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::SuccessfulProgressOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::SuccessfulProgressOfExecution, Message::TC, 3); reqVerifService.successProgressExecutionVerification(receivedMessage, 0); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::FailedProgressOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::FailedProgressOfExecution, Message::TC, 3); reqVerifService.failProgressExecutionVerification(receivedMessage, ErrorHandler::UnknownExecutionProgressError, 0); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::SuccessfulCompletionOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::SuccessfulCompletionOfExecution, Message::TC, 3); reqVerifService.successCompletionExecutionVerification(receivedMessage); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::FailedCompletionOfExecution, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::FailedCompletionOfExecution, Message::TC, 3); reqVerifService.failCompletionExecutionVerification(receivedMessage, ErrorHandler::UnknownExecutionCompletionError); - receivedMessage = Message(RequestVerificationService::ServiceType, RequestVerificationService::MessageType::FailedRoutingReport, Message::TC, 3); + receivedMessage = Message(RequestVerificationService::ServiceType, + RequestVerificationService::MessageType::FailedRoutingReport, Message::TC, 3); reqVerifService.failRoutingVerification(receivedMessage, ErrorHandler::UnknownRoutingError); // ST[05] (5,1 to 5,4) test [works] @@ -169,16 +184,19 @@ int main() { EventReportService::Event eventIDs[] = {EventReportService::HighSeverityUnknownEvent, EventReportService::MediumSeverityUnknownEvent}; EventReportService::Event eventIDs2[] = {EventReportService::HighSeverityUnknownEvent}; - Message eventMessage(EventReportService::ServiceType, EventReportService::MessageType::DisableReportGenerationOfEvents, Message::TC, 1); + Message eventMessage(EventReportService::ServiceType, + EventReportService::MessageType::DisableReportGenerationOfEvents, Message::TC, 1); eventMessage.appendUint16(2); eventMessage.appendEnum16(eventIDs[0]); eventMessage.appendEnum16(eventIDs[1]); - Message eventMessage2(EventReportService::ServiceType, EventReportService::MessageType::EnableReportGenerationOfEvents, Message::TC, 1); + Message eventMessage2(EventReportService::ServiceType, + EventReportService::MessageType::EnableReportGenerationOfEvents, Message::TC, 1); eventMessage2.appendUint16(1); eventMessage2.appendEnum16(eventIDs2[0]); - Message eventMessage3(EventReportService::ServiceType, EventReportService::MessageType::ReportListOfDisabledEvent, Message::TC, 1); + Message eventMessage3(EventReportService::ServiceType, EventReportService::MessageType::ReportListOfDisabledEvent, + Message::TC, 1); eventReportService.disableReportGeneration(eventMessage); eventReportService.listOfDisabledEventsReport(); eventReportService.enableReportGeneration(eventMessage2); @@ -186,9 +204,10 @@ int main() { // ST[19] test - EventActionService & eventActionService = Services.eventAction; + EventActionService& eventActionService = Services.eventAction; - Message eventActionDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 1); + Message eventActionDefinition(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, + Message::TC, 1); eventActionDefinition.appendEnum16(0); eventActionDefinition.appendEnum16(2); eventActionDefinition.appendEnum16(1); @@ -196,7 +215,8 @@ int main() { eventActionDefinition.appendString(TCdata); eventActionService.addEventActionDefinitions(eventActionDefinition); - Message eventActionDefinition1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 1); + Message eventActionDefinition1(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, + Message::TC, 1); eventActionDefinition1.appendEnum16(0); eventActionDefinition1.appendEnum16(2); eventActionDefinition1.appendEnum16(1); @@ -205,7 +225,8 @@ int main() { std::cout << "After this message there should be a failed start of execution error \n"; eventActionService.addEventActionDefinitions(eventActionDefinition1); - Message eventActionDefinition2(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 1); + Message eventActionDefinition2(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, + Message::TC, 1); eventActionDefinition2.appendEnum16(0); eventActionDefinition2.appendEnum16(4); eventActionDefinition2.appendEnum16(2); @@ -213,7 +234,8 @@ int main() { eventActionDefinition2.appendString(TCdata); eventActionService.addEventActionDefinitions(eventActionDefinition2); - Message eventActionDefinition7(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, Message::TC, 1); + Message eventActionDefinition7(EventActionService::ServiceType, EventActionService::MessageType::AddEventAction, + Message::TC, 1); eventActionDefinition7.appendEnum16(0); eventActionDefinition7.appendEnum16(4); eventActionDefinition7.appendEnum16(4); @@ -222,11 +244,12 @@ 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; } - Message eventActionDefinition5(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, Message::TC, 1); + Message eventActionDefinition5(EventActionService::ServiceType, EventActionService::MessageType::EnableEventAction, + Message::TC, 1); eventActionDefinition5.appendUint16(3); eventActionDefinition5.appendUint16(0); eventActionDefinition5.appendUint16(2); @@ -240,11 +263,12 @@ 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; } - Message eventActionDefinition3(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 1); + Message eventActionDefinition3(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, + Message::TC, 1); eventActionDefinition3.appendUint16(3); eventActionDefinition3.appendUint16(0); eventActionDefinition3.appendUint16(2); @@ -257,13 +281,14 @@ 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; } eventActionService.enableEventActionDefinitions(eventActionDefinition5); - Message eventActionDefinition4(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, Message::TC, 1); + Message eventActionDefinition4(EventActionService::ServiceType, EventActionService::MessageType::DeleteEventAction, + Message::TC, 1); eventActionDefinition4.appendUint16(1); eventActionDefinition4.appendUint16(0); eventActionDefinition4.appendUint16(2); @@ -272,7 +297,8 @@ int main() { std::cout << "After this message there should be a failed start of execution error \n"; eventActionService.deleteEventActionDefinitions(eventActionDefinition4); - Message eventActionDefinition6(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, Message::TC, 1); + Message eventActionDefinition6(EventActionService::ServiceType, EventActionService::MessageType::DisableEventAction, + Message::TC, 1); eventActionDefinition6.appendUint16(1); eventActionDefinition6.appendUint16(0); eventActionDefinition6.appendUint16(2); @@ -281,13 +307,13 @@ int main() { std::cout << "After this message there should NOT be a failed start of execution error \n"; eventActionService.deleteEventActionDefinitions(eventActionDefinition4); - // ST13 test LargePacketTransferService largePacketTransferService; String<256> dataToTransfer = "12345678"; largePacketTransferService.firstDownlinkPartReport(LargePacketTransferService::ServiceType, - LargePacketTransferService::MessageType::FirstDownlinkPartReport, dataToTransfer); + LargePacketTransferService::MessageType::FirstDownlinkPartReport, + dataToTransfer); // ST[11] test TimeBasedSchedulingService timeBasedSchedulingService; @@ -295,7 +321,9 @@ int main() { std::cout << "\n\nST[11] service is running"; std::cout << "\nCurrent time in seconds (UNIX epoch): " << currentTime << std::endl; - Message receivedMsg = Message(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction, Message::TC, 1); + Message receivedMsg = + Message(TimeBasedSchedulingService::ServiceType, + 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 @@ -304,7 +332,8 @@ int main() { timeBasedSchedulingService.enableScheduleExecution(receivedMsg); // Enable the schedule // Insert activities in the schedule - receivedMsg = Message(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::InsertActivities, Message::TC, 1); + receivedMsg = Message(TimeBasedSchedulingService::ServiceType, + TimeBasedSchedulingService::MessageType::InsertActivities, Message::TC, 1); receivedMsg.appendUint16(2); // Total number of requests receivedMsg.appendUint32(currentTime + 1556435U); @@ -315,17 +344,20 @@ int main() { timeBasedSchedulingService.insertActivities(receivedMsg); // Time shift activities - receivedMsg = Message(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities, Message::TC, 1); + receivedMsg = Message(TimeBasedSchedulingService::ServiceType, + TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities, Message::TC, 1); receivedMsg.appendSint32(-6789); timeBasedSchedulingService.timeShiftAllActivities(receivedMsg); std::cout << "Activities should be time shifted by: " << -6789 << " seconds." << std::endl; // Report the activities - receivedMsg = Message(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities, Message::TC, 1); + receivedMsg = Message(TimeBasedSchedulingService::ServiceType, + TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities, Message::TC, 1); timeBasedSchedulingService.detailReportAllActivities(receivedMsg); // Report the activities by ID - receivedMsg = Message(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById, Message::TC, 1); + receivedMsg = Message(TimeBasedSchedulingService::ServiceType, + TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById, Message::TC, 1); timeBasedSchedulingService.summaryReportActivitiesByID(receivedMsg); LOG_NOTICE << "ECSS Services test complete"; diff --git a/src/Services/EventActionService.cpp b/src/Services/EventActionService.cpp index bc739f745f55942eec3787944eec99dd5e540809..84c727bef38a330796aaa6c0da4d990828240ab4 100644 --- a/src/Services/EventActionService.cpp +++ b/src/Services/EventActionService.cpp @@ -35,7 +35,11 @@ void EventActionService::addEventActionDefinitions(Message& message) { temp.eventDefinitionID = eventDefinitionID; temp.eventActionDefinitionID = eventActionDefinitionID; temp.request = String<ECSS_TC_REQUEST_STRING_SIZE>(data); - eventActionDefinitionMap.insert(std::make_pair(eventDefinitionID, temp)); + if (eventActionDefinitionMap.size() == ECSS_EVENT_ACTION_STRUCT_MAP_SIZE) { + ErrorHandler::reportError(message,ErrorHandler::EventActionDefinitionsMapIsFull); + } else { + eventActionDefinitionMap.insert(std::make_pair(eventDefinitionID, temp)); + } } } diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp index f951d42af3f991f6eb39c6d1073502c384100ebb..ee308a693d85d5ad269386385ee06560278603cc 100644 --- a/src/Services/ParameterService.cpp +++ b/src/Services/ParameterService.cpp @@ -2,7 +2,8 @@ #ifdef SERVICE_PARAMETER #include "Services/ParameterService.hpp" -#include "Services/Parameter.hpp" +#include "Helpers/Parameter.hpp" +#include "Parameters/SystemParameters.hpp" void ParameterService::reportParameters(Message& paramIds) { // TM[20,2] diff --git a/src/Services/ParameterStatisticsService.cpp b/src/Services/ParameterStatisticsService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..35c23b7cea2a2a15e6d7f759b38e01847740d5ae --- /dev/null +++ b/src/Services/ParameterStatisticsService.cpp @@ -0,0 +1,207 @@ +#include <iostream> +#include "ECSS_Configuration.hpp" +#ifdef SERVICE_PARAMETER +#include "Services/ParameterStatisticsService.hpp" + +void ParameterStatisticsService::reportParameterStatistics(Message& request) { + request.assertTC(ServiceType, MessageType::ReportParameterStatistics); + parameterStatisticsReport(); + + // TODO: append start time and end time to the report + + if (hasAutomaticStatisticsReset) { + 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; + + for (auto& currentStatistic : statisticsMap) { + uint16_t numOfSamples = currentStatistic.second.sampleCounter; + if (numOfSamples == 0) { + continue; + } + numOfValidParameters++; + } + report.appendUint16(numOfValidParameters); + + for (auto& currentStatistic : statisticsMap) { + uint16_t currentId = currentStatistic.first; + uint16_t numOfSamples = currentStatistic.second.sampleCounter; + if (numOfSamples == 0) { + continue; + } + report.appendUint16(currentId); + report.appendUint16(numOfSamples); + currentStatistic.second.appendStatisticsToMessage(report); + } + storeMessage(report); +} + +void ParameterStatisticsService::resetParameterStatistics(Message& request) { + request.assertTC(ServiceType, MessageType::ResetParameterStatistics); + resetParameterStatistics(); +} + +void ParameterStatisticsService::resetParameterStatistics() { + // TODO: Stop the evaluation of parameter statistics + for (auto& it : statisticsMap) { + it.second.resetStatistics(); + } + // TODO: Restart the evaluation of parameter statistics +} + +void ParameterStatisticsService::enablePeriodicStatisticsReporting(Message& request) { + /** + * @todo: The sampling interval of each parameter. the "timeInterval" requested should not exceed it. + * It has to be defined as a constant. + */ + uint16_t SAMPLING_PARAMETER_INTERVAL = 5; + + request.assertTC(ServiceType, MessageType::EnablePeriodicParameterReporting); + + uint16_t timeInterval = request.readUint16(); + if (timeInterval < SAMPLING_PARAMETER_INTERVAL) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::InvalidSamplingRateError); + return; + } + periodicStatisticsReportingStatus = true; + reportingInterval = timeInterval; +} + +void ParameterStatisticsService::disablePeriodicStatisticsReporting(Message& request) { + request.assertTC(ServiceType, MessageType::DisablePeriodicParameterReporting); + + periodicStatisticsReportingStatus = false; + reportingInterval = 0; +} + +void ParameterStatisticsService::addOrUpdateStatisticsDefinitions(Message& request) { + request.assertTC(ServiceType, MessageType::AddOrUpdateParameterStatisticsDefinitions); + + uint16_t numOfIds = request.readUint16(); + for (uint16_t i = 0; i < numOfIds; i++) { + uint16_t currentId = request.readUint16(); + if (currentId >= systemParameters.parametersArray.size()) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::SetNonExistingParameter); + if (supportsSamplingInterval) { + request.skipBytes(2); + } + continue; + } + bool exists = statisticsMap.find(currentId) != statisticsMap.end(); + uint16_t interval = 0; + if (supportsSamplingInterval) { + interval = request.readUint16(); + if (interval < reportingInterval) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::InvalidSamplingRateError); + continue; + } + } + if (not exists) { + if (statisticsMap.size() >= ECSS_MAX_STATISTIC_PARAMETERS) { + ErrorHandler::reportError(request, + ErrorHandler::ExecutionStartErrorType::MaxStatisticDefinitionsReached); + return; + } + Statistic newStatistic; + if (supportsSamplingInterval) { + newStatistic.setSelfSamplingInterval(interval); + } + statisticsMap.insert({currentId, newStatistic}); + // TODO: start the evaluation of statistics for this parameter. + } else { + if (supportsSamplingInterval) { + statisticsMap.at(currentId).setSelfSamplingInterval(interval); + } + statisticsMap.at(currentId).resetStatistics(); + } + } +} + +void ParameterStatisticsService::deleteStatisticsDefinitions(Message& request) { + request.assertTC(ServiceType, MessageType::DeleteParameterStatisticsDefinitions); + + uint16_t numOfIds = request.readUint16(); + if (numOfIds == 0) { + statisticsMap.clear(); + periodicStatisticsReportingStatus = false; + return; + } + for (uint16_t i = 0; i < numOfIds; i++) { + uint16_t currentId = request.readUint16(); + if (currentId >= systemParameters.parametersArray.size()) { + ErrorHandler::reportError(request, ErrorHandler::GetNonExistingParameter); + continue; + } + statisticsMap.erase(currentId); + } + if (statisticsMap.empty()) { + periodicStatisticsReportingStatus = false; + } +} + +void ParameterStatisticsService::reportStatisticsDefinitions(Message& request) { + request.assertTC(ServiceType, MessageType::ReportParameterStatisticsDefinitions); + statisticsDefinitionsReport(); +} + +void ParameterStatisticsService::statisticsDefinitionsReport() { + Message definitionsReport(ServiceType, MessageType::ParameterStatisticsDefinitionsReport, Message::TM, 1); + + uint16_t currentReportingInterval = 0; + if (periodicStatisticsReportingStatus) { + currentReportingInterval = reportingInterval; + } + definitionsReport.appendUint16(currentReportingInterval); + definitionsReport.appendUint16(statisticsMap.size()); + + for (auto& currentParam : statisticsMap) { + uint16_t currentId = currentParam.first; + uint16_t samplingInterval = currentParam.second.selfSamplingInterval; + definitionsReport.appendUint16(currentId); + if (supportsSamplingInterval) { + definitionsReport.appendUint16(samplingInterval); + } + } + storeMessage(definitionsReport); +} + +void ParameterStatisticsService::execute(Message& message) { + switch (message.messageType) { + case 1: + reportParameterStatistics(message); + break; + case 3: + resetParameterStatistics(message); + break; + case 4: + enablePeriodicStatisticsReporting(message); + break; + case 5: + disablePeriodicStatisticsReporting(message); + break; + case 6: + addOrUpdateStatisticsDefinitions(message); + break; + case 7: + deleteStatisticsDefinitions(message); + break; + case 8: + reportStatisticsDefinitions(message); + break; + default: + ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType); + } +} + +#endif diff --git a/test/Helpers/Statistic.cpp b/test/Helpers/Statistic.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5db84a3876685ace98f57408b009d48e4eb8fb3f --- /dev/null +++ b/test/Helpers/Statistic.cpp @@ -0,0 +1,84 @@ +#include "Helpers/Statistic.hpp" +#include "Services/ParameterStatisticsService.hpp" +#include "catch2/catch.hpp" + +TEST_CASE("Statistics updating function") { + SECTION("values in one by one") { + Statistic stat1; + CHECK(stat1.statisticsAreInitialized()); + + double value = 3.24; + stat1.updateStatistics(value); + CHECK(stat1.max == 3.24); + CHECK(stat1.min == 3.24); + CHECK(stat1.mean == 3.24); + + value = 1.3; + stat1.updateStatistics(value); + CHECK(stat1.max == 3.24); + CHECK(stat1.min == 1.3); + CHECK(stat1.mean == 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)); + } + + 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) { + stat.updateStatistics(value); + } + CHECK(stat.max == 8.35); + CHECK(stat.min == 1.09); + CHECK(stat.mean == Approx(4.99501).epsilon(0.00001)); + } +} + +TEST_CASE("Appending of statistics to message") { + SECTION("Successful appending of statistics") { + Message report(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ParameterStatisticsReport, Message::TM, 1); + 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) { + stat.updateStatistics(value); + } + stat.appendStatisticsToMessage(report); + + REQUIRE(report.readFloat() == 8.35f); + REQUIRE(report.readUint32() == 0); // No CUC integration yet + 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)); + } +} + +TEST_CASE("Change the value of the sampling interval") { + SECTION("Test the setter function") { + Statistic stat; + CHECK(stat.selfSamplingInterval == 0); + stat.setSelfSamplingInterval(4); + REQUIRE(stat.selfSamplingInterval == 4); + } +} + +TEST_CASE("Reset statistics") { + SECTION("Successfully reset all the statistics of a parameter") { + 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) { + stat.updateStatistics(value); + } + CHECK(not stat.statisticsAreInitialized()); + stat.resetStatistics(); + REQUIRE(stat.statisticsAreInitialized()); + } +} \ No newline at end of file diff --git a/test/Message.cpp b/test/Message.cpp index 4e1d12f56280bd21d2e122bd3544d42e716d03ea..84a0c58d2fe440b0aa5d7c1918a3608581d8145d 100644 --- a/test/Message.cpp +++ b/test/Message.cpp @@ -157,6 +157,15 @@ TEST_CASE("Requirement 7.3.6 (Real)", "[message][ecss]") { CHECK(message.read<float>() == -9003.53135f); } +TEST_CASE("Test appending double") { + Message message(0, 0, Message::TC, 0); + message.append<double>(2.324); + + REQUIRE(message.dataSize == 8); + + CHECK(message.read<double>() == Approx(2.324).epsilon(0.0001)); +} + TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") { Message message(0, 0, Message::TC, 0); diff --git a/test/MessageParser.cpp b/test/MessageParser.cpp index e840b990044573bd0509c5e62777931db3614d84..325daf70304338ccda549c6cb1bf302e17192135 100644 --- a/test/MessageParser.cpp +++ b/test/MessageParser.cpp @@ -4,7 +4,6 @@ #include "Helpers/CRCHelper.hpp" #include "MessageParser.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}; @@ -19,8 +18,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, 0x0a, 0x20, 0x81, + 0x1f, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f}; Message message; message.packetType = Message::TC; @@ -48,7 +47,7 @@ TEST_CASE("TC Message parsing into a string", "[MessageParser]") { 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}; + 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; Message message = MessageParser::parse(packet, 18); CHECK(message.packetType == Message::TM); @@ -62,7 +61,7 @@ TEST_CASE("TM message parsing", "[MessageParser]") { 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}; + 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x68, 0x69}; Message message; message.packetType = Message::TM; diff --git a/test/Services/Parameter.cpp b/test/Services/Parameter.cpp index 837be7643c39cb5021a261723bde9e5bb9d1488f..10c47ac78cebf73604199fa4aa3c4c56e6ee6fe0 100644 --- a/test/Services/Parameter.cpp +++ b/test/Services/Parameter.cpp @@ -1,11 +1,13 @@ +#include <iostream> #include "catch2/catch.hpp" -#include "Services/Parameter.hpp" +#include "Helpers/Parameter.hpp" #include "Services/ParameterService.hpp" #include "Message.hpp" TEST_CASE("Parameter Append") { SECTION("Check correct appending") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); + Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); auto parameter1 = Parameter<uint8_t>(1); auto parameter2 = Parameter<uint16_t>(500); auto parameter3 = Parameter<uint32_t>(70000); @@ -22,7 +24,8 @@ TEST_CASE("Parameter Append") { TEST_CASE("Parameter Set") { SECTION("Check correct setting") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); + Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); auto parameter1 = Parameter<uint8_t>(1); auto parameter2 = Parameter<uint16_t>(500); auto parameter3 = Parameter<uint32_t>(70000); @@ -40,3 +43,30 @@ TEST_CASE("Parameter Set") { CHECK(parameter3.getValue() == 70001); } } + +TEST_CASE("Get value as double") { + SECTION("uint8 to double") { + auto parameter1 = Parameter<uint8_t>(7); + uint8_t value = 13; + parameter1.setValue(value); + CHECK(parameter1.getValueAsDouble() == 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)); + } + 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)); + } + 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)); + } +} diff --git a/test/Services/ParameterStatisticsService.cpp b/test/Services/ParameterStatisticsService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a831c73fb6de9652c4408ee3981383cd754ec7c --- /dev/null +++ b/test/Services/ParameterStatisticsService.cpp @@ -0,0 +1,385 @@ +#include <iostream> +#include "catch2/catch.hpp" +#include "Message.hpp" +#include "ServiceTests.hpp" + +/** + * System-statistics initialization, so there are actual statistics in the map to work with. + */ +void initializeStatistics(uint16_t interval1, uint16_t interval2) { + Statistic stat1; + Statistic stat2; + stat1.selfSamplingInterval = interval1; + stat2.selfSamplingInterval = interval2; + uint16_t id1 = 7; + uint16_t id2 = 5; + + int numOfSamples = 3; + for (int i = 0; i < numOfSamples; i++) { // Values of stat-1: [ 1, 3, 5 ] + stat1.updateStatistics(i * 2 + 1); + } + numOfSamples = 6; + for (int i = 0; i < numOfSamples; i++) { // Values of stat-2: [ 3, 5, 7, 9, 11, 13 ] + stat2.updateStatistics(i * 2 + 3); + } + Services.parameterStatistics.statisticsMap.insert({id1, stat1}); + 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") { + initializeStatistics(6, 7); + Message request = Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ReportParameterStatistics, Message::TC, 1); + Services.parameterStatistics.hasAutomaticStatisticsReset = false; + + MessageParser::execute(request); + CHECK(ServiceTests::count() == 1); + + 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 + // 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)); + // 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(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") { + Message request = Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ReportParameterStatistics, Message::TC, 1); + Services.parameterStatistics.hasAutomaticStatisticsReset = true; + MessageParser::execute(request); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + } + + SECTION("Report statistics, with auto statistics reset disabled, but reset is given by TC") { + Message request = Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ReportParameterStatistics, Message::TC, 1); + request.appendBoolean(true); + 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()); + + MessageParser::execute(request); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Resetting the parameter statistics") { + SECTION("Reset via TC") { + initializeStatistics(6, 7); + Message request = Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ResetParameterStatistics, Message::TC, 1); + + CHECK(not Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(not Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + MessageParser::execute(request); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Reset without TC") { + initializeStatistics(6, 7); + + CHECK(not Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(not Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + Services.parameterStatistics.resetParameterStatistics(); + + CHECK(Services.parameterStatistics.statisticsMap[5].statisticsAreInitialized()); + CHECK(Services.parameterStatistics.statisticsMap[7].statisticsAreInitialized()); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Enable the periodic reporting of statistics") { + SECTION("Valid reporting interval requested") { + initializeStatistics(6, 7); + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::EnablePeriodicParameterReporting, Message::TC, 1); + request.appendUint16(6); + Services.parameterStatistics.periodicStatisticsReportingStatus = false; + CHECK(Services.parameterStatistics.reportingInterval == 5); + + MessageParser::execute(request); + CHECK(ServiceTests::count() == 0); + + CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == true); + CHECK(Services.parameterStatistics.reportingInterval == 6); + } + + SECTION("Invalid reporting interval requested") { + Message request2 = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::EnablePeriodicParameterReporting, Message::TC, 1); + request2.appendUint16(3); + Services.parameterStatistics.periodicStatisticsReportingStatus = false; + CHECK(Services.parameterStatistics.reportingInterval == 6); + + MessageParser::execute(request2); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InvalidSamplingRateError) == 1); + CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == false); + CHECK(Services.parameterStatistics.reportingInterval == 6); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Disabling the periodic reporting of statistics") { + SECTION("Successfully disable the periodic reporting") { + initializeStatistics(6, 7); + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::DisablePeriodicParameterReporting, Message::TC, 1); + Services.parameterStatistics.periodicStatisticsReportingStatus = true; + + MessageParser::execute(request); + REQUIRE(Services.parameterStatistics.periodicStatisticsReportingStatus == false); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Add/Update statistics definitions") { + SECTION("Update existing parameter statistic definition") { + initializeStatistics(7, 6); + Statistic newStatistic; + newStatistic.setSelfSamplingInterval(0); + Services.parameterStatistics.statisticsMap.insert({0, newStatistic}); + + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::AddOrUpdateParameterStatisticsDefinitions, Message::TC, 1); + uint16_t numOfIds = 1; + request.appendUint16(numOfIds); + + uint16_t paramId1 = 0; + uint16_t interval1 = 14; + request.appendUint16(paramId1); + request.appendUint16(interval1); + + CHECK(Services.parameterStatistics.statisticsMap.size() == 3); + + MessageParser::execute(request); + + REQUIRE(ServiceTests::count() == 0); + CHECK(Services.parameterStatistics.statisticsMap.size() == 3); + CHECK(Services.parameterStatistics.statisticsMap[0].selfSamplingInterval == 14); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Add new statistic definition") { + initializeStatistics(7, 6); + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::AddOrUpdateParameterStatisticsDefinitions, Message::TC, 1); + uint16_t numOfIds = 1; + request.appendUint16(numOfIds); + + uint16_t paramId1 = 1; + uint16_t interval1 = 32; + request.appendUint16(paramId1); + request.appendUint16(interval1); + + CHECK(Services.parameterStatistics.statisticsMap.size() == 2); + + MessageParser::execute(request); + + REQUIRE(ServiceTests::count() == 0); + CHECK(Services.parameterStatistics.statisticsMap.size() == 3); + CHECK(Services.parameterStatistics.statisticsMap[1].selfSamplingInterval == 32); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } + + SECTION("All possible invalid requests combined with add/update") { + initializeStatistics(7, 6); + Statistic newStatistic; + newStatistic.setSelfSamplingInterval(0); + Services.parameterStatistics.statisticsMap.insert({0, newStatistic}); + + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::AddOrUpdateParameterStatisticsDefinitions, Message::TC, 1); + uint16_t numOfIds = 6; + request.appendUint16(numOfIds); + + uint16_t paramId1 = 0; + uint16_t paramId2 = 1; + uint16_t paramId3 = 2; + uint16_t paramId4 = 7; + uint16_t paramId5 = 11; + 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; + + request.appendUint16(paramId1); + request.appendUint16(interval1); + request.appendUint16(paramId2); + request.appendUint16(interval2); + request.appendUint16(paramId3); + request.appendUint16(interval3); + request.appendUint16(paramId4); + request.appendUint16(interval4); + request.appendUint16(paramId5); + request.appendUint16(interval5); + request.appendUint16(paramId6); + request.appendUint16(interval6); + + CHECK(Services.parameterStatistics.statisticsMap.size() == 3); + + MessageParser::execute(request); + + REQUIRE(ServiceTests::count() == 4); + CHECK(Services.parameterStatistics.statisticsMap.size() == 4); + 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); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Delete statistics definitions") { + SECTION("Delete specified definitions") { + Statistic stat1; + Statistic stat2; + Statistic stat3; + Services.parameterStatistics.statisticsMap.insert({0, stat1}); + Services.parameterStatistics.statisticsMap.insert({1, stat2}); + Services.parameterStatistics.statisticsMap.insert({2, stat3}); + + REQUIRE(Services.parameterStatistics.statisticsMap.size() == 3); + REQUIRE(Services.parameterStatistics.statisticsMap.find(0) != Services.parameterStatistics.statisticsMap.end()); + REQUIRE(Services.parameterStatistics.statisticsMap.find(1) != Services.parameterStatistics.statisticsMap.end()); + REQUIRE(Services.parameterStatistics.statisticsMap.find(2) != Services.parameterStatistics.statisticsMap.end()); + + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::DeleteParameterStatisticsDefinitions, Message::TC, 1); + uint16_t numIds = 2; + uint16_t id1 = 0; + uint16_t id2 = 255; // Invalid ID + request.appendUint16(numIds); + request.appendUint16(id1); + request.appendUint16(id2); + + Services.parameterStatistics.periodicStatisticsReportingStatus = true; + MessageParser::execute(request); + + CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == true); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 1); + CHECK(Services.parameterStatistics.statisticsMap.size() == 2); + } + + SECTION("Delete all definitions") { + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::DeleteParameterStatisticsDefinitions, Message::TC, 1); + uint16_t numIds = 0; + request.appendUint16(numIds); + + MessageParser::execute(request); + + CHECK(Services.parameterStatistics.periodicStatisticsReportingStatus == false); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::GetNonExistingParameter) == 1); + CHECK(Services.parameterStatistics.statisticsMap.empty()); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Parameter statistics definition report") { + SECTION("Check if the stored report is valid") { + initializeStatistics(0, 12); + REQUIRE(Services.parameterStatistics.statisticsMap.size() == 2); + REQUIRE(Services.parameterStatistics.statisticsMap.find(7) != Services.parameterStatistics.statisticsMap.end()); + REQUIRE(Services.parameterStatistics.statisticsMap.find(5) != Services.parameterStatistics.statisticsMap.end()); + + Message request = + Message(ParameterStatisticsService::ServiceType, + ParameterStatisticsService::MessageType::ReportParameterStatisticsDefinitions, Message::TC, 1); + + MessageParser::execute(request); + + 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() == 7); + CHECK(report.readUint16() == 0); + + resetSystem(); + ServiceTests::reset(); + Services.reset(); + } +} diff --git a/test/TestPlatform.cpp b/test/TestPlatform.cpp index 6833d3edeb9222d5d106f0ddf5dcac0e4f7ad5a7..68c6ecf4fbae55e9e8cf87f116c5b9fda8d83b45 100644 --- a/test/TestPlatform.cpp +++ b/test/TestPlatform.cpp @@ -35,7 +35,7 @@ void ErrorHandler::logError(ErrorType errorType) { ServiceTests::addError(ErrorHandler::findErrorSource(errorType), errorType); } -void Logger::log(Logger::LogLevel level, etl::istring & message) { +void Logger::log(Logger::LogLevel level, etl::istring& message) { // Logs while testing are completely ignored }