diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 543296c7d8bc297f1b7e5a88137a0e2892e2c3ea..f25060c0a0ccdd77d361f71cffaeeb85e993991b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -4,7 +4,7 @@ <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" /> <Objective-C-extensions> <extensions> - <pair source="cpp" header="hpp" fileNamingConvention="NONE" /> + <pair source="cpp" header="hpp" fileNamingConvention="PASCAL_CASE" /> <pair source="c" header="h" fileNamingConvention="NONE" /> </extensions> </Objective-C-extensions> diff --git a/.idea/misc.xml b/.idea/misc.xml index 383642545dce9706f039abbac53551e64d848c2b..8d27e92289fda7a84ec8630f7524d1a11b9eb00a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -2,12 +2,15 @@ <project version="4"> <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> <component name="CidrRootsConfiguration"> - <sourceRoots> - <file path="$PROJECT_DIR$/inc" /> - <file path="$PROJECT_DIR$/src" /> - </sourceRoots> - <libraryRoots> - <file path="$PROJECT_DIR$/lib" /> - </libraryRoots> + <sourceRoots> + <file path="$PROJECT_DIR$/inc" /> + <file path="$PROJECT_DIR$/src" /> + </sourceRoots> + <libraryRoots> + <file path="$PROJECT_DIR$/lib" /> + </libraryRoots> + <excludeRoots> + <file path="$PROJECT_DIR$/docs" /> + </excludeRoots> </component> -</project> \ No newline at end of file +</project> diff --git a/CMakeLists.txt b/CMakeLists.txt index 3411d8ae9fe898b3e804995e41da09bd10e49baa..4cefb30f61899a10a63251f697bc1b7b0170f8b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ add_custom_target(check # Specify the .cpp files common across all targets add_library(common OBJECT + src/ErrorHandler.cpp src/Message.cpp src/MessageParser.cpp src/Helpers/CRCHelper.cpp diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 1db5923dc5d2f3b692dc81061d993cd703edc328..baee71ec54496ccaf12154891507276735a0640f 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -9,4 +9,7 @@ // 7.4.4.1c #define ECSS_PUS_VERSION 2 +// 9.3.1a.1.e +#define ECSS_SEQUENCE_FLAGS 0x3 + #endif //ECSS_SERVICES_ECSS_DEFINITIONS_H diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp new file mode 100644 index 0000000000000000000000000000000000000000..56223cacaecfa11b03c0a12b0e3626d0d3d0b7c0 --- /dev/null +++ b/inc/ErrorHandler.hpp @@ -0,0 +1,157 @@ +#ifndef PROJECT_ERRORHANDLER_HPP +#define PROJECT_ERRORHANDLER_HPP + +#include "Message.hpp" + +/** + * A class that handles unexpected software errors, including internal errors or errors due to + * invalid & incorrect input data. + * + * @todo Add auxiliary data field to errors + */ +class ErrorHandler { +private: + /** + * Log the error to a logging facility. Currently, this just displays the error on the screen. + * + * @todo This function MUST be moved as platform-dependent code. Currently, it uses g++ specific + * functions for desktop. + */ + template<typename ErrorType> + static void logError(const Message &message, ErrorType errorType); + + /** + * Log an error without a Message to a logging facility. Currently, this just displays the error + * on the screen. + * + * @todo This function MUST be moved as platform-dependent code. Currently, it uses g++ specific + * functions for desktop. + */ + template<typename ErrorType> + static void logError(ErrorType errorType); + +public: + enum InternalErrorType { + UnknownInternalError = 0, + /** + * While writing (creating) a message, an amount of bytes was tried to be added but + * resulted in failure, since the message storage was not enough. + */ + MessageTooLarge = 1, + /** + * Asked to append a number of bits larger than supported + */ + TooManyBitsAppend = 2, + /** + * Asked to append a byte, while the previous byte was not complete + */ + ByteBetweenBits = 3, + /** + * A string is larger than the largest allowed string + */ + StringTooLarge = 4, + }; + + /** + * The error code for failed acceptance reports, as specified in ECSS 6.1.4.3d + * + * Note: Numbers are kept in code explicitly, so that there is no uncertainty when something + * changes. + */ + enum AcceptanceErrorType { + UnknownAcceptanceError = 0, + /** + * The received message does not contain enough information as specified + */ + MessageTooShort = 1, + /** + * Asked to read a number of bits larger than supported + */ + TooManyBitsRead = 2, + /** + * Cannot read a string, because it is larger than the largest allowed string + */ + StringTooShort = 4, + }; + + /** + * The error code for failed completion of execution reports, as specified in ECSS 5.3.5.2.3g + * + * Note: Numbers are kept in code explicitly, so that there is no uncertainty when something + * changes. + */ + enum ExecutionErrorType { + UnknownExecutionError = 0 + }; + + /** + * The error code for failed completion of execution reports, as specified in ECSS 6.1.3.3d + * + * Note: Numbers are kept in code explicitly, so that there is no uncertainty when something + * changes. + */ + enum RoutingErrorType { + UnknownRoutingError = 0 + }; + + /** + * The location where the error occurred + */ + enum ErrorSource { + Internal, + Acceptance, + ExecutionStart, + ExecutionProgress, + ExecutionCompletion, + Routing + }; + + /** + * Report a failure and, if applicable, store a failure report message + * + * @tparam ErrorType The Type struct of the error; can be AcceptanceErrorType, + * ExecutionErrorType, or RoutingErrorType. + * @param message The incoming message that prompted the failure + * @param errorCode The error's code, as defined in ErrorHandler + * @todo See if this needs to include InternalErrorType + */ + template<typename ErrorType> + static void reportError(const Message &message, ErrorType errorCode); + + /** + * Report a failure that occurred internally, not due to a failure of a received packet. + * + * Note that these errors correspond to bugs or faults in the software, and should be treated + * differently. Such an error may prompt a task or software reset. + */ + static void reportInternalError(InternalErrorType errorCode); + + /** + * Make an assertion, to ensure that a runtime condition is met. + * + * Reports a failure that occurred internally, not due to a failure of a received packet. + * + * Creates an error if \p condition is false. The created error is Internal. + */ + static void assertInternal(bool condition, InternalErrorType errorCode) { + if (not condition) { + reportInternalError(errorCode); + } + } + + /** + * Make an assertion, to ensure that a runtime condition is met. + * + * Reports a failure that occurred while processing a request, in any of the process phases. + * + * Creates an error if \p condition is false. The created error corresponds to a \p message. + */ + template<typename ErrorType> + static void assertRequest(bool condition, const Message &message, ErrorType errorCode) { + if (not condition) { + reportError(message, errorCode); + } + } +}; + +#endif //PROJECT_ERRORHANDLER_HPP diff --git a/inc/Message.hpp b/inc/Message.hpp index 28f6d572c5d187ce431a30e174a51b6f4bbb3fde..78bef90e7ee869cecdfe85f278df60437fd28f0c 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -26,12 +26,19 @@ public: // As specified in CCSDS 133.0-B-1 (TM or TC) PacketType packetType; - // Maximum value of 2047 (5.4.2.1c) + /** + * The destination APID of the message + * + * Maximum value of 2047 (5.4.2.1c) + */ uint16_t applicationId; // 7.4.3.1b uint16_t messageTypeCounter = 0; + // 7.4.1, as defined in CCSDS 133.0-B-1 + uint16_t packetSequenceCount = 0; + // TODO: Find out if we need more than 16 bits for this uint16_t dataSize = 0; diff --git a/inc/Services/RequestVerificationService.hpp b/inc/Services/RequestVerificationService.hpp index 9d9253e0bdf47c783b774c471edb378cd4739f05..67b0f5b102028497d04322f26121bef7cbc67c41 100644 --- a/inc/Services/RequestVerificationService.hpp +++ b/inc/Services/RequestVerificationService.hpp @@ -6,6 +6,9 @@ /** * Implementation of the ST[01] request verification service * + * Note: ST[01]'s messages should not contain calls to the ErrorHandler, as the ErrorHandler + * calls ST[01] functions again. Doing so would risk an infinite recursive loop. + * * @todo All telemetry packets shall have a telemetry packet secondary header * @todo See if it would be more efficient to use Messages as arguments instead of individual * parameters diff --git a/inc/macros.hpp b/inc/macros.hpp new file mode 100644 index 0000000000000000000000000000000000000000..25a0833b069d95c291b668d7bb2d7eb93d4f79b9 --- /dev/null +++ b/inc/macros.hpp @@ -0,0 +1,7 @@ +#ifndef ECSS_SERVICES_MACROS_HPP +#define ECSS_SERVICES_MACROS_HPP + +#define assertI(cond, error) (ErrorHandler::assertInternal((cond), (error))) +#define assertR(cond, error) (ErrorHandler::assertRequest((cond), *this, (error))) + +#endif //ECSS_SERVICES_MACROS_HPP diff --git a/src/ErrorHandler.cpp b/src/ErrorHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d278b1edcd06e67b2c24faea34c6b9f45f0975df --- /dev/null +++ b/src/ErrorHandler.cpp @@ -0,0 +1,77 @@ +#include <iostream> +#include <cxxabi.h> +#include <ErrorHandler.hpp> + +#include "Services/RequestVerificationService.hpp" + +// TODO: Use service singleton, as soon as singletons are ready +static RequestVerificationService requestVerificationService; + +template<> +void ErrorHandler::reportError(const Message &message, AcceptanceErrorType errorCode) { + requestVerificationService.failAcceptanceVerification( + message.packetType, + true, + message.applicationId, + ECSS_SEQUENCE_FLAGS, + message.packetSequenceCount, + static_cast<uint16_t>(errorCode) + ); + + logError(message, errorCode); +} + +template<> +void ErrorHandler::reportError(const Message &message, ExecutionErrorType errorCode) { + requestVerificationService.failExecutionVerification( + message.packetType, + true, + message.applicationId, + ECSS_SEQUENCE_FLAGS, + message.packetSequenceCount, + static_cast<uint16_t>(errorCode) + ); + + logError(message, errorCode); +} + +template<> +void ErrorHandler::reportError(const Message &message, RoutingErrorType errorCode) { + requestVerificationService.failRoutingVerification( + message.packetType, + true, + message.applicationId, + ECSS_SEQUENCE_FLAGS, + message.packetSequenceCount, + static_cast<uint16_t>(errorCode) + ); + + logError(message, errorCode); +} + +void ErrorHandler::reportInternalError(ErrorHandler::InternalErrorType errorCode) { + logError(UnknownInternalError); +} + +template<typename ErrorType> +void ErrorHandler::logError(const Message &message, ErrorType errorType) { + std::cerr + /* + * Gets the error class name from the template + * Note: This is g++-dependent code and should only be used for debugging. + */ + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) + << " Error " << "[" << static_cast<uint16_t>(message.serviceType) << "," << + static_cast<uint16_t>(message.messageType) << "]: " << errorType << std::endl; +} + +template<typename ErrorType> +void ErrorHandler::logError(ErrorType errorType) { + std::cerr + /* + * Gets the error class name from the template + * Note: This is g++-dependent code and should only be used for debugging. + */ + << abi::__cxa_demangle(typeid(ErrorType).name(), nullptr, nullptr, nullptr) + << " Error: " << errorType << std::endl; +} diff --git a/src/Message.cpp b/src/Message.cpp index 1374287e6a57891293b76883021d6098d2758588..b51954ec0ec3f08a29582f3bcf8bf8d7972d38fe 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -1,5 +1,7 @@ #include "Message.hpp" +#include "macros.hpp" #include <cstring> +#include <ErrorHandler.hpp> Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, @@ -8,10 +10,10 @@ Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType p void Message::appendBits(uint8_t numBits, uint16_t data) { // TODO: Add assertion that data does not contain 1s outside of numBits bits - assert(numBits <= 16); + assertI(numBits <= 16, ErrorHandler::TooManyBitsAppend); while (numBits > 0) { // For every sequence of 8 bits... - assert(dataSize < ECSS_MAX_MESSAGE_SIZE); + assertI(dataSize < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); if (currentBit + numBits >= 8) { // Will have to shift the bits and insert the next ones later @@ -35,16 +37,16 @@ void Message::appendBits(uint8_t numBits, uint16_t data) { } void Message::appendByte(uint8_t value) { - assert(dataSize < ECSS_MAX_MESSAGE_SIZE); - assert(currentBit == 0); + assertI(dataSize < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + assertI(currentBit == 0, ErrorHandler::ByteBetweenBits); data[dataSize] = value; dataSize++; } void Message::appendHalfword(uint16_t value) { - assert(dataSize + 2 <= ECSS_MAX_MESSAGE_SIZE); - assert(currentBit == 0); + assertI(dataSize + 2 <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + assertI(currentBit == 0, ErrorHandler::ByteBetweenBits); data[dataSize] = static_cast<uint8_t>((value >> 8) & 0xFF); data[dataSize + 1] = static_cast<uint8_t>(value & 0xFF); @@ -53,8 +55,8 @@ void Message::appendHalfword(uint16_t value) { } void Message::appendWord(uint32_t value) { - assert(dataSize + 4 <= ECSS_MAX_MESSAGE_SIZE); - assert(currentBit == 0); + assertI(dataSize + 4 <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + assertI(currentBit == 0, ErrorHandler::ByteBetweenBits); data[dataSize] = static_cast<uint8_t>((value >> 24) & 0xFF); data[dataSize + 1] = static_cast<uint8_t>((value >> 16) & 0xFF); @@ -65,8 +67,8 @@ void Message::appendWord(uint32_t value) { } void Message::appendString(uint8_t size, const char *value) { - assert(dataSize + size <= ECSS_MAX_MESSAGE_SIZE); - assert(size < ECSS_MAX_STRING_SIZE); + assertI(dataSize + size < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + assertI(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooLarge); memcpy(data + dataSize, value, size); @@ -74,8 +76,8 @@ void Message::appendString(uint8_t size, const char *value) { } void Message::appendString(uint16_t size, const uint8_t *value) { - assert(dataSize + size <= ECSS_MAX_MESSAGE_SIZE); - assert(size < ECSS_MAX_STRING_SIZE); + assertI(dataSize + size <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooLarge); + assertI(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooLarge); memcpy(data + dataSize, value, size); @@ -83,13 +85,13 @@ void Message::appendString(uint16_t size, const uint8_t *value) { } uint16_t Message::readBits(uint8_t numBits) { - assert(numBits <= 16); + assertR(numBits <= 16, ErrorHandler::TooManyBitsRead); // TODO: Add assert uint16_t value = 0x0; while (numBits > 0) { - assert(readPosition < ECSS_MAX_MESSAGE_SIZE); + assertR(readPosition < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); if (currentBit + numBits >= 8) { auto bitsToAddNow = static_cast<uint8_t>(8 - currentBit); @@ -111,7 +113,7 @@ uint16_t Message::readBits(uint8_t numBits) { } uint8_t Message::readByte() { - assert(readPosition < ECSS_MAX_MESSAGE_SIZE); + assertR(readPosition < ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); uint8_t value = data[readPosition]; readPosition++; @@ -120,7 +122,7 @@ uint8_t Message::readByte() { } uint16_t Message::readHalfword() { - assert(readPosition + 2 < ECSS_MAX_MESSAGE_SIZE); + assertR(readPosition + 2 <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); uint16_t value = (data[readPosition] << 8) | data[readPosition + 1]; readPosition += 2; @@ -129,7 +131,7 @@ uint16_t Message::readHalfword() { } uint32_t Message::readWord() { - assert(readPosition + 4 < ECSS_MAX_MESSAGE_SIZE); + assertR(readPosition + 4 <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); uint32_t value = (data[readPosition] << 24) | (data[readPosition + 1] << 16) | (data[readPosition + 2] << 8) | data[readPosition + 3]; @@ -139,8 +141,8 @@ uint32_t Message::readWord() { } void Message::readString(char *string, uint8_t size) { - assert(readPosition + size <= ECSS_MAX_MESSAGE_SIZE); - assert(size < ECSS_MAX_STRING_SIZE); + assertR(readPosition + size <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); + assertR(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooShort); memcpy(string, data + readPosition, size); string[size] = '\0'; // todo: Use that for now to avoid problems. Later to be removed @@ -149,8 +151,8 @@ void Message::readString(char *string, uint8_t size) { } void Message::readString(uint8_t *string, uint16_t size) { - assert(readPosition + size <= ECSS_MAX_MESSAGE_SIZE); - assert(size < ECSS_MAX_STRING_SIZE); + assertR(readPosition + size <= ECSS_MAX_MESSAGE_SIZE, ErrorHandler::MessageTooShort); + assertR(size < ECSS_MAX_STRING_SIZE, ErrorHandler::StringTooShort); memcpy(string, data + readPosition, size); diff --git a/src/main.cpp b/src/main.cpp index 0e0386f1b1afe5324bd524def92a7b6629c63253..b07291023e944d224842aa5ddf08d305f3785f95 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,8 @@ #include "MessageParser.hpp" #include "Services/MemoryManagementService.hpp" #include "Helpers/CRCHelper.hpp" +#include "ErrorHandler.hpp" + int main() { Message packet = Message(0, 0, Message::TC, 1); @@ -146,5 +148,14 @@ int main() { message = Message(1, 10, Message::TC, 2); messageParser.execute(message); + // ErrorHandler test + std::cout << std::flush; + std::cerr << std::flush; + ErrorHandler::reportError(receivedPacket, ErrorHandler::MessageTooShort); + ErrorHandler::reportInternalError(ErrorHandler::MessageTooLarge); + Message errorMessage(0, 0, Message::TC, 1); + errorMessage.appendBits(2, 7); + errorMessage.appendByte(15); + return 0; } diff --git a/test/ErrorHandler.cpp b/test/ErrorHandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23eaa2aef869e2501b7b74ea56157db7255e6503 --- /dev/null +++ b/test/ErrorHandler.cpp @@ -0,0 +1,69 @@ +#include <catch2/catch.hpp> +#include <ErrorHandler.hpp> +#include "Services/ServiceTests.hpp" + +TEST_CASE("Error: Failed Acceptance", "[errors]") { + Message failedMessage(38, 32, Message::TC, 47); + ErrorHandler::reportError(failedMessage, ErrorHandler::MessageTooShort); + + REQUIRE(ServiceTests::hasOneMessage()); + Message report = ServiceTests::get(0); + + // Check that a TM[1,2] message was returned + CHECK(report.serviceType == 1); + CHECK(report.messageType == 2); + CHECK(report.packetType == Message::TM); + REQUIRE(report.dataSize == 6); + + CHECK(report.readBits(3) == ECSS_PUS_VERSION); + CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); + CHECK(report.readBits(1) == true); + CHECK(report.readBits(11) == 47); + CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); + CHECK(report.readBits(14) == failedMessage.packetSequenceCount); + CHECK(report.readEnum16() == 1); +} + +TEST_CASE("Error: Failed Execution Completion", "[errors]") { + Message failedMessage(38, 32, Message::TC, 56); + ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownExecutionError); + + REQUIRE(ServiceTests::hasOneMessage()); + Message report = ServiceTests::get(0); + + // Check that a TM[1,8] message was returned + CHECK(report.serviceType == 1); + CHECK(report.messageType == 8); + CHECK(report.packetType == Message::TM); + REQUIRE(report.dataSize == 6); + + CHECK(report.readBits(3) == ECSS_PUS_VERSION); + CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); + CHECK(report.readBits(1) == true); + CHECK(report.readBits(11) == 56); + CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); + CHECK(report.readBits(14) == failedMessage.packetSequenceCount); + CHECK(report.readEnum16() == 0); +} + +TEST_CASE("Error: Failed Routing", "[errors]") { + Message failedMessage(38, 32, Message::TC, 71); + ErrorHandler::reportError(failedMessage, ErrorHandler::UnknownRoutingError); + + REQUIRE(ServiceTests::hasOneMessage()); + Message report = ServiceTests::get(0); + + // Check that a TM[1,8] message was returned + CHECK(report.serviceType == 1); + CHECK(report.messageType == 10); + CHECK(report.packetType == Message::TM); + REQUIRE(report.dataSize == 6); + + CHECK(report.readBits(3) == ECSS_PUS_VERSION); + CHECK(report.readBits(1) == static_cast<uint16_t>(Message::TC)); + CHECK(report.readBits(1) == true); + CHECK(report.readBits(11) == 71); + CHECK(report.readBits(2) == ECSS_SEQUENCE_FLAGS); + CHECK(report.readBits(14) == failedMessage.packetSequenceCount); + CHECK(report.readEnum16() == 0); +}