diff --git a/CMakeLists.txt b/CMakeLists.txt index 99ba91acd1eaae9beffe3b64a8f7588c7e0ab9fe..976a40f96e21c5cc825db90bec7ab36a5da55cff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ add_library(common OBJECT src/Services/EventActionService.cpp src/Services/TimeBasedSchedulingService.cpp src/Services/FunctionManagementService.cpp + src/Services/HousekeepingService.cpp src/Services/ParameterStatisticsService.cpp src/Helpers/Statistic.cpp ) diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 5f8b5ef24bde1effc062e50dfc6ff87d2d2bf818..cf90152f45211e38fffb6e459c4abfd3abf58bae 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -113,9 +113,9 @@ inline const uint16_t ECSSEventActionStructMapSize = 256; inline const uint8_t ECSSMaxDeltaOfReleaseTime = 60; /** - * The maximum number of stored parameters in the \ref ParameterService + * The max number of simply commutated parameters per housekeeping structure in ST[03] */ -inline const uint8_t ECSSMaxParameters = 5; +inline const uint16_t ECSSMaxSimplyCommutatedParameters = 10; /** * The number of functions supported by the \ref FunctionManagementService @@ -134,8 +134,6 @@ inline const uint8_t ECSSFunctionNameLength = 32; */ inline const uint8_t ECSSFunctionMaxArgLength = 32; -/** @} */ - /** * @brief The maximum size of a log message */ @@ -144,7 +142,7 @@ inline const uint16_t LoggerMaxMessageSize = 512; /** * @brief Size of the array holding the Parameter objects for the ST[20] parameter service */ -inline const uint8_t ECSSParameterCount = 4; +inline const uint8_t ECSSParameterCount = 12; /** * @brief Defines whether the optional CRC field is included @@ -161,4 +159,11 @@ inline const uint8_t ECSSMaxStatisticParameters = 4; */ inline const bool SupportsStandardDeviation = true; +/** + * @brief Defines the max number of housekeeping structs that the housekeeping service can contain + */ +inline const uint8_t ECSSMaxHousekeepingStructures = 10; + +/** @} */ + #endif // ECSS_SERVICES_ECSS_DEFINITIONS_H diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp index d836ba26a95d90745c85c166699a65ef373294be..2936e1f2ffb4d7c5f7f41e281cde943c9936969a 100644 --- a/inc/ErrorHandler.hpp +++ b/inc/ErrorHandler.hpp @@ -76,7 +76,15 @@ public: /** * A Message that is included within another message is too large */ - NestedMessageTooLarge = 11 + NestedMessageTooLarge = 11, + /** + * A request to access a non existing housekeeping structure in ST[03] + */ + NonExistentHousekeeping = 12, + /** + * Attempt to access an invalid parameter in ST[03] + */ + NonExistentParameter = 13, }; /** @@ -144,23 +152,54 @@ public: */ GetNonExistingParameter = 8, /** - * Attempt to set a reporting rate which is smaller than the parameter sampling rate. - * ST[04] + * Attempt to add definition to the struct map but its already full. (ST[19]) */ - InvalidReportingRateError = 9, + EventActionDefinitionsMapIsFull = 11, /** - * Attempt to set a sampling rate which is greater than the parameter reporting rate. + * Attempt to report/delete non existing housekeeping structure (ST[03]) + */ + RequestedNonExistingStructure = 12, + /** + * Attempt to create already created structure (ST[03]) + */ + RequestedAlreadyExistingStructure = 13, + /** + * Attempt to delete structure which has the periodic reporting status enabled (ST[03]) as per 6.3.3.5.2(d-2) + */ + RequestedDeletionOfEnabledHousekeeping = 14, + /** + * Attempt to append a new parameter ID to a housekeeping structure, but the ID is already in the structure + * (ST[03]) + */ + AlreadyExistingParameter = 15, + /** + * Attempt to append a new parameter id to a housekeeping structure, but the periodic generation status is + * enabled (ST[03]) + */ + RequestedAppendToEnabledHousekeeping = 16, + /** + * Attempt to create a new housekeeping structure in Housekeeping Service, when the maximum number of + * housekeeping structures is already reached (ST[03]) + */ + ExceededMaxNumberOfHousekeepingStructures = 17, + /** + * 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 = 18, + /* Attempt to set a reporting rate which is smaller than the parameter sampling rate. * ST[04] */ - InvalidSamplingRateError = 10, + InvalidReportingRateError = 19, /** - * Attempt to add definition to the struct map but its already full. (ST[19]) + * Attempt to set a sampling rate which is greater than the parameter reporting rate. + * ST[04] */ - EventActionDefinitionsMapIsFull = 11, + InvalidSamplingRateError = 20, /** * Attempt to add new statistic definition but the maximum number is already reached (ST[04]) */ - MaxStatisticDefinitionsReached = 12, + MaxStatisticDefinitionsReached = 21, }; /** diff --git a/inc/Helpers/HousekeepingStructure.hpp b/inc/Helpers/HousekeepingStructure.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1e75430829b003bf49b83002a59dfddc068879f6 --- /dev/null +++ b/inc/Helpers/HousekeepingStructure.hpp @@ -0,0 +1,35 @@ +#ifndef ECSS_SERVICES_HOUSEKEEPINGSTRUCTURE_HPP +#define ECSS_SERVICES_HOUSEKEEPINGSTRUCTURE_HPP + +#include "ECSS_Definitions.hpp" +#include "ErrorHandler.hpp" +#include "etl/vector.h" +#include "Helpers/Parameter.hpp" + +/** + * Implementation of the Housekeeping report structure used by the Housekeeping Reporting Subservice (ST[03]). The + * current version includes only simply commutated parameters, i.e. parameters that contain a single sampled value. + * + * @author Petridis Konstantinos <petridkon@gmail.com> + */ +class HousekeepingStructure { +public: + uint8_t structureId; + /** + * Defined as integer multiples of the minimum sampling interval as per 6.3.3.2.c.5 #NOTE-2. + */ + uint32_t collectionInterval = 0; + /** + * Indicates whether the periodic generation of housekeeping reports is enabled. + */ + bool periodicGenerationActionStatus = false; + + /** + * Vector containing the IDs of the simply commutated parameters, contained in the housekeeping structure. + */ + etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters> simplyCommutatedParameterIds; + + HousekeepingStructure() = default; +}; + +#endif \ No newline at end of file diff --git a/inc/Helpers/Parameter.hpp b/inc/Helpers/Parameter.hpp index 7967011acf674d70453a84fa8b0990e6c902f983..ccc55ae9d8e24241afd8176f50ccf373a55130ec 100644 --- a/inc/Helpers/Parameter.hpp +++ b/inc/Helpers/Parameter.hpp @@ -54,7 +54,7 @@ public: currentValue = value; } - DataType getValue() { + inline DataType getValue() { return currentValue; } diff --git a/inc/Platform/x86/ECSS_Configuration.hpp b/inc/Platform/x86/ECSS_Configuration.hpp index 1e7f59f29fe3c3c2d081d35614c4ed2baa0f79db..07a47a4c254d5fb787f806e4c21aaeb4ae572d8a 100644 --- a/inc/Platform/x86/ECSS_Configuration.hpp +++ b/inc/Platform/x86/ECSS_Configuration.hpp @@ -24,6 +24,7 @@ #define SERVICE_EVENTACTION ///< Compile ST[19] event-action #define SERVICE_EVENTREPORT ///< Compile ST[05] event reporting #define SERVICE_FUNCTION ///< Compile ST[08] function management +#define SERVICE_HOUSEKEEPING ///< Compile ST[03] housekeeping #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 diff --git a/inc/Platform/x86/Parameters/SystemParameters.hpp b/inc/Platform/x86/Parameters/SystemParameters.hpp index a1a834afc32154f620880a76f5331ac10fb7a503..1ef75b416f65a0cf4497a7d74154ccf95894f2a5 100644 --- a/inc/Platform/x86/Parameters/SystemParameters.hpp +++ b/inc/Platform/x86/Parameters/SystemParameters.hpp @@ -1,4 +1,8 @@ +#ifndef ECSS_SERVICES_SYSTEMPARAMETERS_HPP +#define ECSS_SERVICES_SYSTEMPARAMETERS_HPP + #include "Helpers/Parameter.hpp" +#include <optional> #include "etl/vector.h" /** * @author Athanasios Theocharis <athatheoc@gmail.com> @@ -19,14 +23,39 @@ 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); + Parameter<uint32_t> parameter4 = Parameter<uint32_t>(5); + Parameter<uint8_t> parameter5 = Parameter<uint8_t>(11); + Parameter<uint32_t> parameter6 = Parameter<uint32_t>(23); + Parameter<uint32_t> parameter7 = Parameter<uint32_t>(53); + Parameter<uint8_t> parameter8 = Parameter<uint8_t>(55); + Parameter<uint16_t> parameter9 = Parameter<uint16_t>(32); + Parameter<uint32_t> parameter10 = Parameter<uint32_t>(43); + Parameter<uint32_t> parameter11 = Parameter<uint32_t>(91); + Parameter<uint8_t> parameter12 = Parameter<uint8_t>(1); + /** * The key of the array is the ID of the parameter as specified in PUS */ - etl::array<std::reference_wrapper<ParameterBase>, ECSSParameterCount> parametersArray = {parameter1, parameter2, - parameter3, parameter4}; + etl::array<std::reference_wrapper<ParameterBase>, ECSSParameterCount> parametersArray = { + parameter1, parameter2, parameter3, parameter4, parameter5, parameter6, + parameter7, parameter8, parameter9, parameter10, parameter11, parameter12}; SystemParameters() = default; + + /** + * This is a simple getter function, which returns a reference to a specified parameter, from the parametersArray. + * + * @param parameterId the id of the parameter, whose reference is to be returned. + */ + std::optional<std::reference_wrapper<ParameterBase>> getParameter(uint16_t parameterId) { + if (parameterId >= parametersArray.size()) { + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentParameter); + return {}; + } + return parametersArray[parameterId]; + } }; extern SystemParameters systemParameters; + +#endif diff --git a/inc/ServicePool.hpp b/inc/ServicePool.hpp index 7666d416803a0d701dc07f5aff2eac42e8540fd3..544b7460c93ca76b53d3ff76d5f321750085efb3 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/HousekeepingService.hpp" #include "Services/ParameterStatisticsService.hpp" /** @@ -51,6 +52,10 @@ public: FunctionManagementService functionManagement; #endif +#ifdef SERVICE_HOUSEKEEPING + HousekeepingService housekeeping; +#endif + #ifdef SERVICE_LARGEPACKET LargePacketTransferService largePacketTransferService; #endif diff --git a/inc/Services/HousekeepingService.hpp b/inc/Services/HousekeepingService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a3a902127a8382f14bf71ab5e1a2676732c38c86 --- /dev/null +++ b/inc/Services/HousekeepingService.hpp @@ -0,0 +1,134 @@ +#ifndef ECSS_SERVICES_HOUSEKEEPINGSERVICE_HPP +#define ECSS_SERVICES_HOUSEKEEPINGSERVICE_HPP + +#include "etl/map.h" +#include "ECSS_Definitions.hpp" +#include "Service.hpp" +#include "ErrorHandler.hpp" +#include "Parameters/SystemParameters.hpp" +#include "Helpers/HousekeepingStructure.hpp" + +/** + * Implementation of the ST[03] Housekeeping Reporting Service. The job of the Housekeeping Service is to store + * parameters in the housekeeping structures so that it can generate housekeeping reports periodically. + * + * @author Petridis Konstantinos <petridkon@gmail.com> + */ +class HousekeepingService : Service { +private: + /** + * Appends the periodic properties of a housekeeping structure to a message. + * + * @note The structureId is checked before being passed in this function, so there is a convention that the ID is + * valid. If this function needs to be called from another point of the code, the case of an invalid ID passed as + * argument will lead in undefined behavior. + */ + void appendPeriodicPropertiesToMessage(Message& report, uint8_t structureId); + + /** + * Returns true if the given parameter ID exists in the parameters contained in the housekeeping structure. + */ + static bool existsInVector(const etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters>& ids, + uint16_t parameterId); + +public: + inline static const uint8_t ServiceType = 3; + + /** + * Map containing the housekeeping structures. Map[i] contains the housekeeping structure with ID = i. + */ + etl::map<uint8_t, HousekeepingStructure, ECSSMaxHousekeepingStructures> housekeepingStructures; + + enum MessageType : uint8_t { + CreateHousekeepingReportStructure = 1, + DeleteHousekeepingReportStructure = 3, + EnablePeriodicHousekeepingParametersReport = 5, + DisablePeriodicHousekeepingParametersReport = 6, + ReportHousekeepingStructures = 9, + HousekeepingStructuresReport = 10, + HousekeepingParametersReport = 25, + GenerateOneShotHousekeepingReport = 27, + AppendParametersToHousekeepingStructure = 29, + ModifyCollectionIntervalOfStructures = 31, + ReportHousekeepingPeriodicProperties = 33, + HousekeepingPeriodicPropertiesReport = 35, + }; + + HousekeepingService() = default; + + /** + * Implementation of TC[3,1]. Request to create a housekeeping parameters report structure. + */ + void createHousekeepingReportStructure(Message& request); + + /** + * Implementation of TC[3,3]. Request to delete a housekeeping parameters report structure. + */ + void deleteHousekeepingReportStructure(Message& request); + + /** + * Implementation of TC[3,5]. Request to enable the periodic housekeeping parameters reporting for a specific + * housekeeping structure. + */ + void enablePeriodicHousekeepingParametersReport(Message& request); + + /** + * Implementation of TC[3,6]. Request to disable the periodic housekeeping parameters reporting for a specific + * housekeeping structure. + */ + void disablePeriodicHousekeepingParametersReport(Message& request); + + /** + * This function gets a message type TC[3,9] 'report housekeeping structures'. + */ + void reportHousekeepingStructures(Message& request); + + /** + * This function takes a structure ID as argument and constructs/stores a TM[3,10] housekeeping structure report. + */ + void housekeepingStructureReport(uint8_t structIdToReport); + + /** + * This function gets a housekeeping structure ID and stores a TM[3,25] 'housekeeping + * parameter report' message. + */ + void housekeepingParametersReport(uint8_t structureId); + + /** + * This function takes as argument a message type TC[3,27] 'generate one shot housekeeping report' and stores + * TM[3,25] report messages. + */ + void generateOneShotHousekeepingReport(Message& request); + + /** + * This function receives a message type TC[3,29] 'append new parameters to an already existing housekeeping + * structure' + * + * @note As per 6.3.3.8.d.4, in case of an invalid parameter, the whole message shall be rejected. However, a + * convention was made, saying that it would be more practical to just skip the invalid parameter and continue + * processing the rest of the message. + */ + void appendParametersToHousekeepingStructure(Message& request); + + /** + * This function receives a message type TC[3,31] 'modify the collection interval of specified structures'. + */ + void modifyCollectionIntervalOfStructures(Message& request); + + /** + * This function takes as argument a message type TC[3,33] 'report housekeeping periodic properties' and + * responds with a TM[3,35] 'housekeeping periodic properties report'. + */ + void reportHousekeepingPeriodicProperties(Message& request); + + /** + * It is responsible to call the suitable function that executes a TC 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/inc/Services/ParameterService.hpp b/inc/Services/ParameterService.hpp index eefbea6e0b487d61f53a8b9b61c709698a3cbeab..112476436420e5e454f9dfc0ccd8957b4da08ecf 100644 --- a/inc/Services/ParameterService.hpp +++ b/inc/Services/ParameterService.hpp @@ -23,7 +23,6 @@ */ class ParameterService : public Service { public: - inline static const uint8_t ServiceType = 20; enum MessageType : uint8_t { diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp index f0938627aaab14baa05046c01d23ef7ad551b54f..c35c525bb5d3f1fb8b5010d1104a9c845da387af 100644 --- a/src/MessageParser.cpp +++ b/src/MessageParser.cpp @@ -7,6 +7,12 @@ void MessageParser::execute(Message& message) { switch (message.serviceType) { +#ifdef SERVICE_HOUSEKEEPING + case HousekeepingService::ServiceType: + Services.housekeeping.execute(message); + break; +#endif + #ifdef SERVICE_PARAMETERSTATISTICS case ParameterStatisticsService::ServiceType: Services.parameterStatistics.execute(message); diff --git a/src/Services/HousekeepingService.cpp b/src/Services/HousekeepingService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..41888caafa883c6602ec90f38b9732c057f52389 --- /dev/null +++ b/src/Services/HousekeepingService.cpp @@ -0,0 +1,260 @@ +#include "Services/HousekeepingService.hpp" + +void HousekeepingService::createHousekeepingReportStructure(Message& request) { + request.assertTC(ServiceType, MessageType::CreateHousekeepingReportStructure); + + uint8_t idToCreate = request.readUint8(); + if (housekeepingStructures.find(idToCreate) != housekeepingStructures.end()) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedAlreadyExistingStructure); + return; + } + if (housekeepingStructures.size() >= ECSSMaxHousekeepingStructures) { + ErrorHandler::reportError(request, + ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfHousekeepingStructures); + return; + } + HousekeepingStructure newStructure; + newStructure.structureId = idToCreate; + newStructure.collectionInterval = request.readUint32(); + newStructure.periodicGenerationActionStatus = false; + + uint16_t numOfSimplyCommutatedParams = request.readUint16(); + + for (uint16_t i = 0; i < numOfSimplyCommutatedParams; i++) { + uint16_t newParamId = request.readUint16(); + if (existsInVector(newStructure.simplyCommutatedParameterIds, newParamId)) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter); + continue; + } + newStructure.simplyCommutatedParameterIds.push_back(newParamId); + } + housekeepingStructures.insert({idToCreate, newStructure}); +} + +void HousekeepingService::deleteHousekeepingReportStructure(Message& request) { + request.assertTC(ServiceType, MessageType::DeleteHousekeepingReportStructure); + 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); + continue; + } + if (housekeepingStructures.at(structureId).periodicGenerationActionStatus) { + ErrorHandler::reportError(request, + ErrorHandler::ExecutionStartErrorType::RequestedDeletionOfEnabledHousekeeping); + continue; + } + housekeepingStructures.erase(structureId); + } +} + +void HousekeepingService::enablePeriodicHousekeepingParametersReport(Message& request) { + request.assertTC(ServiceType, MessageType::EnablePeriodicHousekeepingParametersReport); + + 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); + continue; + } + housekeepingStructures.at(structIdToEnable).periodicGenerationActionStatus = true; + } +} + +void HousekeepingService::disablePeriodicHousekeepingParametersReport(Message& request) { + request.assertTC(ServiceType, MessageType::DisablePeriodicHousekeepingParametersReport); + + 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); + continue; + } + housekeepingStructures.at(structIdToDisable).periodicGenerationActionStatus = false; + } +} + +void HousekeepingService::reportHousekeepingStructures(Message& request) { + request.assertTC(ServiceType, MessageType::ReportHousekeepingStructures); + + 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); + continue; + } + housekeepingStructureReport(structureId); + } +} + +void HousekeepingService::housekeepingStructureReport(uint8_t structIdToReport) { + auto housekeepingStructure = housekeepingStructures.find(structIdToReport); + if (housekeepingStructure == housekeepingStructures.end()) { + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentHousekeeping); + return; + } + Message structReport(ServiceType, MessageType::HousekeepingStructuresReport, Message::TM, 1); + 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) { + structReport.appendUint16(parameterId); + } + storeMessage(structReport); +} + +void HousekeepingService::housekeepingParametersReport(uint8_t structureId) { + if (housekeepingStructures.find(structureId) == housekeepingStructures.end()) { + ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType::NonExistentHousekeeping); + return; + } + Message housekeepingReport(ServiceType, MessageType::HousekeepingParametersReport, Message::TM, 1); + + housekeepingReport.appendUint8(structureId); + for (auto id : housekeepingStructures.at(structureId).simplyCommutatedParameterIds) { + if (auto parameter = systemParameters.getParameter(id)) { + parameter->get().appendValueToMessage(housekeepingReport); + } + } + storeMessage(housekeepingReport); +} + +void HousekeepingService::generateOneShotHousekeepingReport(Message& request) { + request.assertTC(ServiceType, MessageType::GenerateOneShotHousekeepingReport); + + 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); + continue; + } + housekeepingParametersReport(structureId); + } +} + +void HousekeepingService::appendParametersToHousekeepingStructure(Message& request) { + request.assertTC(ServiceType, MessageType::AppendParametersToHousekeepingStructure); + + uint8_t targetStructId = request.readUint8(); + if (housekeepingStructures.find(targetStructId) == housekeepingStructures.end()) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure); + return; + } + auto& housekeepingStructure = housekeepingStructures.at(targetStructId); + if (housekeepingStructure.periodicGenerationActionStatus) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::RequestedAppendToEnabledHousekeeping); + 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); + return; + } + uint16_t newParamId = request.readUint16(); + if (newParamId >= systemParameters.parametersArray.size()) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::GetNonExistingParameter); + continue; + } + if (existsInVector(housekeepingStructure.simplyCommutatedParameterIds, newParamId)) { + ErrorHandler::reportError(request, ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter); + continue; + } + housekeepingStructure.simplyCommutatedParameterIds.push_back(newParamId); + } +} + +void HousekeepingService::modifyCollectionIntervalOfStructures(Message& request) { + request.assertTC(ServiceType, MessageType::ModifyCollectionIntervalOfStructures); + + 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); + continue; + } + housekeepingStructures.at(targetStructId).collectionInterval = newCollectionInterval; + } +} + +void HousekeepingService::reportHousekeepingPeriodicProperties(Message& request) { + request.assertTC(ServiceType, MessageType::ReportHousekeepingPeriodicProperties); + + 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()) { + numOfValidIds++; + } + } + Message periodicPropertiesReport(ServiceType, MessageType::HousekeepingPeriodicPropertiesReport, Message::TM, 1); + 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); + continue; + } + appendPeriodicPropertiesToMessage(periodicPropertiesReport, structIdToReport); + } + storeMessage(periodicPropertiesReport); +} + +void HousekeepingService::appendPeriodicPropertiesToMessage(Message& report, uint8_t structureId) { + report.appendUint8(structureId); + report.appendBoolean(housekeepingStructures.at(structureId).periodicGenerationActionStatus); + report.appendUint32(housekeepingStructures.at(structureId).collectionInterval); +} + +void HousekeepingService::execute(Message& message) { + switch (message.messageType) { + case CreateHousekeepingReportStructure: + createHousekeepingReportStructure(message); + break; + case DeleteHousekeepingReportStructure: + deleteHousekeepingReportStructure(message); + break; + case EnablePeriodicHousekeepingParametersReport: + enablePeriodicHousekeepingParametersReport(message); + break; + case DisablePeriodicHousekeepingParametersReport: + disablePeriodicHousekeepingParametersReport(message); + break; + case ReportHousekeepingStructures: + reportHousekeepingStructures(message); + break; + case GenerateOneShotHousekeepingReport: + generateOneShotHousekeepingReport(message); + break; + case AppendParametersToHousekeepingStructure: + appendParametersToHousekeepingStructure(message); + break; + case ModifyCollectionIntervalOfStructures: + modifyCollectionIntervalOfStructures(message); + break; + case ReportHousekeepingPeriodicProperties: + reportHousekeepingPeriodicProperties(message); + break; + } +} + +bool HousekeepingService::existsInVector(const etl::vector<uint16_t, ECSSMaxSimplyCommutatedParameters>& ids, + uint16_t parameterId) { + return std::find(std::begin(ids), std::end(ids), parameterId) != std::end(ids); +} diff --git a/src/Services/ParameterService.cpp b/src/Services/ParameterService.cpp index de4786275099ed5c73436a354f0e244849d7f94e..02c9d2189520facc0b067afd5105a619b4836298 100644 --- a/src/Services/ParameterService.cpp +++ b/src/Services/ParameterService.cpp @@ -7,7 +7,8 @@ void ParameterService::reportParameters(Message& paramIds) { // TM[20,2] - Message parameterReport(ParameterService::ServiceType, ParameterService::MessageType::ParameterValuesReport, Message::TM, 1); + Message parameterReport(ParameterService::ServiceType, ParameterService::MessageType::ParameterValuesReport, + Message::TM, 1); ErrorHandler::assertRequest(paramIds.packetType == Message::TC, paramIds, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); @@ -41,11 +42,10 @@ void ParameterService::reportParameters(Message& paramIds) { } 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.messageType == ParameterService::MessageType::SetParameterValues, + newParamValues, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); ErrorHandler::assertRequest(newParamValues.serviceType == ParameterService::ServiceType, newParamValues, ErrorHandler::AcceptanceErrorType::UnacceptableMessage); diff --git a/test/Helpers/systemParameters.cpp b/test/Helpers/systemParameters.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d5223ecec6d860a83309fb243fd7e1687ad6de9 --- /dev/null +++ b/test/Helpers/systemParameters.cpp @@ -0,0 +1,22 @@ +#include "Platform/x86/Parameters/SystemParameters.hpp" +#include "catch2/catch.hpp" +#include "../Services/ServiceTests.hpp" + +TEST_CASE("Getting a reference of a parameter") { + SECTION("Valid parameter requested") { + uint16_t parameterId = 7; + REQUIRE(static_cast<Parameter<uint16_t>&>(systemParameters.getParameter(parameterId)->get()).getValue() == 55); + + parameterId = 11; + REQUIRE(static_cast<Parameter<uint16_t>&>(systemParameters.getParameter(parameterId)->get()).getValue() == 1); + } + + SECTION("Invalid parameter requested") { + uint16_t parameterId = systemParameters.parametersArray.size() + 1; + REQUIRE(not systemParameters.getParameter(parameterId)); + + parameterId = parameterId + 24; + REQUIRE(not systemParameters.getParameter(parameterId)); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentParameter) == 2); + } +} \ No newline at end of file diff --git a/test/Services/HousekeepingService.cpp b/test/Services/HousekeepingService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..369803777fc14e1a0fb5a50c76241e8fef73d3f5 --- /dev/null +++ b/test/Services/HousekeepingService.cpp @@ -0,0 +1,620 @@ +#include <iostream> +#include "catch2/catch.hpp" +#include "Message.hpp" +#include "ServiceTests.hpp" +#include "Services/HousekeepingService.hpp" +#include "etl/algorithm.h" + +HousekeepingService& housekeepingService = Services.housekeeping; + +/** + * Helper function that forms the message that's going to be sent as request to create a new housekeeping structure. + */ +void buildRequest(Message& request, uint8_t idToCreate) { + uint32_t interval = 7; + uint16_t numOfSimplyCommutatedParams = 3; + etl::vector<uint16_t, 3> simplyCommutatedIds = {8, 4, 5}; + + request.appendUint8(idToCreate); + request.appendUint32(interval); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id : simplyCommutatedIds) { + request.appendUint16(id); + } +} + +/** + * Initializes 3 housekeeping structures with IDs = {0, 4, 6} + */ +void initializeHousekeepingStructures() { + uint8_t ids[3] = {0, 4, 6}; + uint32_t interval = 7; + etl::vector<uint16_t, 3> simplyCommutatedIds = {8, 4, 5}; + + HousekeepingStructure structures[3]; + int i = 0; + for (auto& newStructure : structures) { + newStructure.structureId = ids[i]; + newStructure.collectionInterval = interval; + newStructure.periodicGenerationActionStatus = false; + for (uint16_t parameterId : simplyCommutatedIds) { + newStructure.simplyCommutatedParameterIds.push_back(parameterId); + } + housekeepingService.housekeepingStructures.insert({ids[i], newStructure}); + i++; + } + + REQUIRE(housekeepingService.housekeepingStructures.size() == 3); + REQUIRE(housekeepingService.housekeepingStructures.find(0) != housekeepingService.housekeepingStructures.end()); + REQUIRE(housekeepingService.housekeepingStructures.find(4) != housekeepingService.housekeepingStructures.end()); + REQUIRE(housekeepingService.housekeepingStructures.find(6) != housekeepingService.housekeepingStructures.end()); +} + +/** + * Helper function that stores samples into simply commutated parameters of different data type each. + */ +void storeSamplesToParameters(uint16_t id1, uint16_t id2, uint16_t id3) { + Message samples(HousekeepingService::ServiceType, + HousekeepingService::MessageType::ReportHousekeepingPeriodicProperties, Message::TM, 1); + + static_cast<Parameter<uint16_t>&>(systemParameters.parametersArray[id1].get()).setValue(33); + static_cast<Parameter<uint8_t>&>(systemParameters.parametersArray[id2].get()).setValue(77); + static_cast<Parameter<uint32_t>&>(systemParameters.parametersArray[id3].get()).setValue(99); +} + +/** + * Helper function that forms the request to append new parameters to a housekeeping structure + */ +void appendNewParameters(Message& request, uint8_t idToAppend) { + uint16_t numOfSimplyCommutatedParams = 7; + etl::vector<uint16_t, 7> simplyCommutatedIds = {8, 4, 5, 9, 11, 10, 220}; + + request.appendUint8(idToAppend); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id : simplyCommutatedIds) { + request.appendUint16(id); + } +} + +TEST_CASE("Create housekeeping structure") { + SECTION("Valid structure creation request") { + 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(ServiceTests::count() == 0); + CHECK(newStruct.structureId == idToCreate); + CHECK(newStruct.simplyCommutatedParameterIds.size() == numOfSimplyCommutatedParams); + CHECK(newStruct.collectionInterval == interval); + CHECK(std::equal(newStruct.simplyCommutatedParameterIds.begin(), newStruct.simplyCommutatedParameterIds.end(), + simplyCommutatedIds.begin())); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure creation request") { + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + uint8_t structureId = 9; + HousekeepingStructure structure1; + structure1.structureId = structureId; + housekeepingService.housekeepingStructures.insert({structureId, structure1}); + + uint8_t idToCreate = 9; + request.appendUint8(idToCreate); + + MessageParser::execute(request); + + CHECK(housekeepingService.housekeepingStructures.size() == 1); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors( + ErrorHandler::ExecutionStartErrorType::RequestedAlreadyExistingStructure) == 1); + housekeepingService.housekeepingStructures.erase(structureId); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Exceeding max number of housekeeping structures") { + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + + uint8_t idsToCreate[16] = {1, 3, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + 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); + } + + REQUIRE(housekeepingService.housekeepingStructures.size() == 10); + CHECK(ServiceTests::count() == 6); + CHECK(ServiceTests::countThrownErrors( + ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfHousekeepingStructures) == 5); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Same simply commutated parameter twice in the request") { + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + uint8_t idToCreate = 2; + uint32_t interval = 7; + uint16_t numOfSimplyCommutatedParams = 9; + etl::vector<uint16_t, 9> simplyCommutatedIds = {8, 4, 5, 4, 8, 8, 5, 4, 11}; + + request.appendUint8(idToCreate); + request.appendUint32(interval); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id : simplyCommutatedIds) { + request.appendUint16(id); + } + + MessageParser::execute(request); + + CHECK(ServiceTests::count() == 5); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 5); + HousekeepingStructure newStruct = housekeepingService.housekeepingStructures[idToCreate]; + + REQUIRE(newStruct.simplyCommutatedParameterIds.size() == 4); + uint16_t existingParameterIds[4] = {8, 4, 5, 11}; + for (auto parameterId : newStruct.simplyCommutatedParameterIds) { + CHECK(std::find(std::begin(existingParameterIds), std::end(existingParameterIds), parameterId) != + std::end(existingParameterIds)); + } + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Delete housekeeping structure") { + SECTION("One valid request, the rest invalid") { + Message createStruct(HousekeepingService::ServiceType, + HousekeepingService::MessageType::CreateHousekeepingReportStructure, Message::TC, 1); + + buildRequest(createStruct, 2); + MessageParser::execute(createStruct); + + REQUIRE(housekeepingService.housekeepingStructures.size() == 1); + REQUIRE(housekeepingService.housekeepingStructures.find(2) != housekeepingService.housekeepingStructures.end()); + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::DeleteHousekeepingReportStructure, Message::TC, 1); + + uint8_t numOfStructs = 5; + uint8_t ids[5] = {2, 3, 4, 7, 8}; + request.appendUint8(numOfStructs); + for (auto& id : ids) { + request.appendUint8(id); + } + + MessageParser::execute(request); + + CHECK(ServiceTests::count() == 4); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 4); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid request of periodic structure") { + 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(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors( + ErrorHandler::ExecutionStartErrorType::RequestedDeletionOfEnabledHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +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); + uint8_t numOfStructs = 5; + uint8_t idsToEnable[5] = {1, 3, 4, 6, 7}; + request2.appendUint8(numOfStructs); + for (auto& id : idsToEnable) { + request2.appendUint8(id); + } + REQUIRE(not housekeepingService.housekeepingStructures[0].periodicGenerationActionStatus); + REQUIRE(not housekeepingService.housekeepingStructures[4].periodicGenerationActionStatus); + REQUIRE(not housekeepingService.housekeepingStructures[6].periodicGenerationActionStatus); + + MessageParser::execute(request2); + CHECK(ServiceTests::count() == 3); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 3); + CHECK(not housekeepingService.housekeepingStructures[0].periodicGenerationActionStatus); + CHECK(housekeepingService.housekeepingStructures[4].periodicGenerationActionStatus); + CHECK(housekeepingService.housekeepingStructures[6].periodicGenerationActionStatus); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Disable the periodic generation of housekeeping structures") { + SECTION("Both valid and invalid structure IDs in request") { + initializeHousekeepingStructures(); + Message request2(HousekeepingService::ServiceType, + HousekeepingService::MessageType::DisablePeriodicHousekeepingParametersReport, Message::TC, 1); + uint8_t numOfStructs = 4; + uint8_t idsToDisable[4] = {0, 1, 4, 6}; + request2.appendUint8(numOfStructs); + for (auto& id : idsToDisable) { + request2.appendUint8(id); + } + housekeepingService.housekeepingStructures[0].periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures[4].periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures[6].periodicGenerationActionStatus = true; + + MessageParser::execute(request2); + + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 1); + CHECK(not housekeepingService.housekeepingStructures[0].periodicGenerationActionStatus); + CHECK(not housekeepingService.housekeepingStructures[4].periodicGenerationActionStatus); + CHECK(not housekeepingService.housekeepingStructures[6].periodicGenerationActionStatus); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Reporting of housekeeping structures") { + SECTION("Both valid and invalid structure IDs in request") { + initializeHousekeepingStructures(); + Message request2(HousekeepingService::ServiceType, + HousekeepingService::MessageType::ReportHousekeepingStructures, Message::TC, 1); + uint8_t numOfStructs = 3; + uint8_t idsToReport[3] = {9, 4, 2}; + request2.appendUint8(numOfStructs); + for (auto& id : idsToReport) { + request2.appendUint8(id); + } + MessageParser::execute(request2); + + CHECK(ServiceTests::count() == 3); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 2); + + Message report = ServiceTests::get(1); // Both 0 and 2 are the error messages because only id=4 was valid. + + uint8_t validId = 4; + CHECK(report.readUint8() == validId); + CHECK(not report.readBoolean()); // periodic status + CHECK(report.readUint32() == 7); // interval + CHECK(report.readUint16() == 3); // number of simply commutated ids + CHECK(report.readUint16() == 8); + CHECK(report.readUint16() == 4); // ids + CHECK(report.readUint16() == 5); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Reporting of housekeeping structures without a TC message as argument") { + SECTION("Check the report message generated by the non-TC function") { + initializeHousekeepingStructures(); + uint8_t structureId = 4; + + housekeepingService.housekeepingStructureReport(structureId); + CHECK(ServiceTests::count() == 1); + Message report = ServiceTests::get(0); + + REQUIRE(report.messageType == HousekeepingService::MessageType::HousekeepingStructuresReport); + CHECK(report.readUint8() == structureId); + CHECK(not report.readBoolean()); + CHECK(report.readUint32() == 7); + CHECK(report.readUint16() == 3); + CHECK(report.readUint16() == 8); + CHECK(report.readUint16() == 4); + CHECK(report.readUint16() == 5); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Reporting of housekeeping parameters") { + SECTION("Valid structure request") { + storeSamplesToParameters(8, 4, 5); + initializeHousekeepingStructures(); + uint8_t structId = 6; + + housekeepingService.housekeepingParametersReport(structId); + + CHECK(ServiceTests::count() == 1); + Message report = ServiceTests::get(0); + REQUIRE(report.messageType == HousekeepingService::MessageType::HousekeepingParametersReport); + REQUIRE(report.readUint8() == structId); + CHECK(report.readUint16() == 33); + CHECK(report.readUint8() == 77); + CHECK(report.readUint32() == 99); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Invalid structure request") { + uint8_t structId = 8; + housekeepingService.housekeepingParametersReport(structId); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 1); + + structId = 12; + housekeepingService.housekeepingParametersReport(structId); + + CHECK(ServiceTests::countThrownErrors(ErrorHandler::InternalErrorType::NonExistentHousekeeping) == 2); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Reporting of housekeeping parameters without a TC request") { + SECTION("Check the report message generated by the non-TC function") { + storeSamplesToParameters(8, 4, 5); + initializeHousekeepingStructures(); + uint8_t structureId = 6; + + housekeepingService.housekeepingParametersReport(structureId); + CHECK(ServiceTests::count() == 1); + Message report = ServiceTests::get(0); + + REQUIRE(report.messageType == HousekeepingService::MessageType::HousekeepingParametersReport); + REQUIRE(report.readUint8() == structureId); + CHECK(report.readUint16() == 33); + CHECK(report.readUint8() == 77); + CHECK(report.readUint32() == 99); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("One-shot housekeeping parameter report generation") { + SECTION("Both valid and invalid structure IDs in request") { + storeSamplesToParameters(8, 4, 5); + initializeHousekeepingStructures(); + Message request2(HousekeepingService::ServiceType, + HousekeepingService::MessageType::GenerateOneShotHousekeepingReport, Message::TC, 1); + uint8_t numOfStructs = 5; + uint8_t structIds[5] = {0, 4, 7, 8, 11}; + request2.appendUint8(numOfStructs); + for (auto& id : structIds) { + request2.appendUint8(id); + } + MessageParser::execute(request2); + + CHECK(ServiceTests::count() == 5); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 3); + Message report1 = ServiceTests::get(0); + Message report2 = ServiceTests::get(1); + + REQUIRE(report1.messageType == HousekeepingService::MessageType::HousekeepingParametersReport); + REQUIRE(report2.messageType == HousekeepingService::MessageType::HousekeepingParametersReport); + + REQUIRE(report1.readUint8() == 0); + CHECK(report1.readUint16() == 33); + CHECK(report1.readUint8() == 77); + CHECK(report1.readUint32() == 99); + + REQUIRE(report2.readUint8() == 4); + CHECK(report2.readUint16() == 33); + CHECK(report2.readUint8() == 77); + CHECK(report2.readUint32() == 99); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Append parameters in housekeeping report structure") { + SECTION("Non existing structure") { + initializeHousekeepingStructures(); + Message request1(HousekeepingService::ServiceType, + HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); + uint8_t structId = 2; + request1.appendUint8(structId); + MessageParser::execute(request1); + CHECK(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 1); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Enabled structure") { + 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(ServiceTests::count() == 1); + CHECK(ServiceTests::countThrownErrors( + ErrorHandler::ExecutionStartErrorType::RequestedAppendToEnabledHousekeeping) == 1); + + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Valid request including both valid and invalid parameters") { + initializeHousekeepingStructures(); + Message request3(HousekeepingService::ServiceType, + HousekeepingService::MessageType::AppendParametersToHousekeepingStructure, Message::TC, 1); + uint8_t structId = 6; + appendNewParameters(request3, structId); + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 3); + + MessageParser::execute(request3); + CHECK(ServiceTests::count() == 4); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 3); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::GetNonExistingParameter) == 1); + + uint16_t currentlyExistingParameters[] = {8, 4, 5, 9, 10, 11}; + HousekeepingStructure structToCheck = housekeepingService.housekeepingStructures[structId]; + REQUIRE(structToCheck.simplyCommutatedParameterIds.size() == 6); + for (auto& existingParameter : currentlyExistingParameters) { + CHECK(std::find(std::begin(structToCheck.simplyCommutatedParameterIds), + std::end(structToCheck.simplyCommutatedParameterIds), + existingParameter) != std::end(structToCheck.simplyCommutatedParameterIds)); + } + ServiceTests::reset(); + Services.reset(); + } + + SECTION("Exceeding the maximum number of simply commutated parameters for the specified structure") { + initializeHousekeepingStructures(); + uint8_t structId = 6; + 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}; + + request.appendUint8(structId); + request.appendUint16(numOfSimplyCommutatedParams); + for (auto& id : simplyCommutatedIds) { + request.appendUint16(id); + } + REQUIRE(housekeepingService.housekeepingStructures.find(structId) != + housekeepingService.housekeepingStructures.end()); + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 3); + + MessageParser::execute(request); + + REQUIRE(housekeepingService.housekeepingStructures[structId].simplyCommutatedParameterIds.size() == 10); + CHECK(ServiceTests::count() == 2); + CHECK(ServiceTests::countThrownErrors( + ErrorHandler::ExecutionStartErrorType::ExceededMaxNumberOfSimplyCommutatedParameters) == 1); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::AlreadyExistingParameter) == 1); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Modification of housekeeping structures' interval") { + SECTION("Both valid and invalid requests") { + initializeHousekeepingStructures(); + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::ModifyCollectionIntervalOfStructures, Message::TC, 1); + uint8_t numOfStructs = 4; + uint8_t structIds[4] = {0, 4, 9, 10}; + uint32_t intervals[4] = {12, 21, 32, 17}; + request.appendUint8(numOfStructs); + int i = 0; + for (auto& id : structIds) { + request.appendUint8(id); + request.appendUint32(intervals[i++]); + } + MessageParser::execute(request); + + CHECK(ServiceTests::count() == 2); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 2); + REQUIRE(housekeepingService.housekeepingStructures[0].collectionInterval == 12); + REQUIRE(housekeepingService.housekeepingStructures[4].collectionInterval == 21); + + ServiceTests::reset(); + Services.reset(); + } +} + +TEST_CASE("Reporting of housekeeping structure periodic properties") { + SECTION("Both valid and invalid structure IDs in request") { + initializeHousekeepingStructures(); + Message request(HousekeepingService::ServiceType, + HousekeepingService::MessageType::ReportHousekeepingPeriodicProperties, Message::TC, 1); + uint8_t numOfStructs = 6; + uint8_t structIds[6] = {0, 4, 1, 6, 9, 10}; + request.appendUint8(numOfStructs); + for (auto& id : structIds) { + request.appendUint8(id); + } + housekeepingService.housekeepingStructures[0].periodicGenerationActionStatus = true; + housekeepingService.housekeepingStructures[4].collectionInterval = 24; + housekeepingService.housekeepingStructures[6].collectionInterval = 13; + + MessageParser::execute(request); + + CHECK(ServiceTests::count() == 4); + CHECK(ServiceTests::countThrownErrors(ErrorHandler::ExecutionStartErrorType::RequestedNonExistingStructure) == + 3); + + Message report = ServiceTests::get(3); + CHECK(report.readUint8() == 3); // Number of valid ids + CHECK(report.readUint8() == 0); // Id + CHECK(report.readBoolean() == true); // Periodic status + CHECK(report.readUint32() == 7); // Interval + CHECK(report.readUint8() == 4); // Id + CHECK(report.readBoolean() == false); // Periodic status + CHECK(report.readUint32() == 24); // Interval + CHECK(report.readUint8() == 6); // Id + CHECK(report.readBoolean() == false); // Periodic status + CHECK(report.readUint32() == 13); // Interval + + ServiceTests::reset(); + Services.reset(); + } +} diff --git a/test/Services/ParameterService.cpp b/test/Services/ParameterService.cpp index fa408e094b5241d14157c55a4eac3ea3f9f816f4..9f9d5fe92d5eb93ff50d04436080b098cd8d4eda 100644 --- a/test/Services/ParameterService.cpp +++ b/test/Services/ParameterService.cpp @@ -11,7 +11,8 @@ 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 request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); request.appendUint16(3); request.appendUint16(54432); request.appendUint16(60000); @@ -24,14 +25,15 @@ TEST_CASE("Parameter Report Subservice") { Message report = ServiceTests::get(3); CHECK(report.serviceType == ParameterService::ServiceType); CHECK(report.messageType == ParameterService::MessageType::ParameterValuesReport); - CHECK(report.readUint16() == 0); // the message shall be empty + CHECK(report.readUint16() == 0); // the message shall be empty ServiceTests::reset(); Services.reset(); } SECTION("Some requested parameters invalid") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); + Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); request.appendUint16(3); request.appendUint16(1); request.appendUint16(10000); @@ -55,7 +57,8 @@ TEST_CASE("Parameter Report Subservice") { } SECTION("Parameters are of different types") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, Message::TC, 1); + Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::ReportParameterValues, + Message::TC, 1); request.appendUint16(3); request.appendUint16(0); request.appendUint16(1); @@ -81,7 +84,8 @@ TEST_CASE("Parameter Report Subservice") { TEST_CASE("Parameter Setting Subservice") { SECTION("All parameter IDs are invalid") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); + Message request = + Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); request.appendUint16(3); request.appendUint16(54432); request.appendUint16(1); @@ -103,7 +107,8 @@ TEST_CASE("Parameter Setting Subservice") { } SECTION("The last parameter ID is invalid") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); + Message request = + Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); request.appendUint16(3); request.appendUint16(0); request.appendUint8(1); @@ -127,7 +132,8 @@ TEST_CASE("Parameter Setting Subservice") { } SECTION("The middle parameter ID is invalid") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); + Message request = + Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); request.appendUint16(3); request.appendUint16(0); request.appendUint8(1); @@ -151,7 +157,8 @@ TEST_CASE("Parameter Setting Subservice") { } SECTION("All IDs are valid") { - Message request = Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); + Message request = + Message(ParameterService::ServiceType, ParameterService::MessageType::SetParameterValues, Message::TC, 1); request.appendUint16(3); request.appendUint16(0); request.appendUint8(1); @@ -174,7 +181,6 @@ TEST_CASE("Parameter Setting Subservice") { } TEST_CASE("Wrong Messages") { - SECTION("Wrong Service Type Handling Test for Report") { Message falseRequest(62, 1, Message::TM, 1); diff --git a/test/Services/ParameterStatisticsService.cpp b/test/Services/ParameterStatisticsService.cpp index 3a831c73fb6de9652c4408ee3981383cd754ec7c..2e32020ae48220dd01a183f344c8868b3645724c 100644 --- a/test/Services/ParameterStatisticsService.cpp +++ b/test/Services/ParameterStatisticsService.cpp @@ -263,8 +263,8 @@ TEST_CASE("Add/Update statistics definitions") { uint16_t paramId1 = 0; uint16_t paramId2 = 1; uint16_t paramId3 = 2; - uint16_t paramId4 = 7; - uint16_t paramId5 = 11; + uint16_t paramId4 = systemParameters.parametersArray.size() + 24; + uint16_t paramId5 = systemParameters.parametersArray.size() + 1; uint16_t paramId6 = 3; uint16_t interval1 = 14;