diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 5b44ac96da73d9acc2af3bb992aa3611e574d766..f25060c0a0ccdd77d361f71cffaeeb85e993991b 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,27 +3,8 @@ <option name="RIGHT_MARGIN" value="100" /> <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" /> <Objective-C-extensions> - <file> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" /> - </file> - <class> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" /> - <option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" /> - </class> <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 91d4361702769bdfe6326464a61298263c79e5e3..8d27e92289fda7a84ec8630f7524d1a11b9eb00a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -9,5 +9,8 @@ <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 5b47f76be72a6f43473ff978f1ac24dfbbe9e245..d709806f9a52996a68922e321a364f40e032ff64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,8 +15,11 @@ 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/MessageParser.cpp + src/Helpers/CRCHelper.cpp + src/Services/EventReportService.cpp src/Services/MemoryManagementService.cpp src/Services/ParameterService.cpp src/Services/RequestVerificationService.cpp diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp index 9bbcb5c1a433955104eb8f545a6dd713dd78228b..3a2eeedba9ebd9fd6e080c054e485a7936880ae5 100644 --- a/inc/ECSS_Definitions.hpp +++ b/inc/ECSS_Definitions.hpp @@ -12,4 +12,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/Helpers/CRCHelper.hpp b/inc/Helpers/CRCHelper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d70ca5782b7a33add0110990bd23c0c2253dd1e7 --- /dev/null +++ b/inc/Helpers/CRCHelper.hpp @@ -0,0 +1,43 @@ +#ifndef ECSS_SERVICES_CRCHELPER_HPP +#define ECSS_SERVICES_CRCHELPER_HPP + +#include <cstdint> + +class CRCHelper { + + /** + * CRC32 calculation helper class + * This class declares a function which calculates the CRC16 checksum of the given data. + * + * For now the actual implementation is the CRC16/CCITT variant (ECSS-E-ST-70-41C, pg.615) + * (polynomial 0x1021, normal input), but this can change at any time + * (even to a hardware CRC implementation, if available) + * + * Please report all found bugs. + * + * @author (CRC explanation) http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html + * @author (class code & dox) Grigoris Pavlakis <grigpavl@ece.auth.gr> + */ + +// TODO: Change this to hardware implementation or a trusted software one +// TODO: Use CRC with received TC and transmitted TM packets +public: + /** + * Actual CRC calculation function. + * @param message (pointer to the data to be checksummed) + * @param length (size in bytes) + * @return the CRC32 checksum of the input data + */ + static uint16_t calculateCRC(const uint8_t* message, uint32_t length); + + /** + * CRC validation function. Make sure the passed message actually contains a CRC checksum + * appended at the very end! + * @param message (pointer to the data to be validated) + * @param length (in bytes, plus 2 bytes for the CRC checksum) + * @return 0 when the data is valid, a nonzero uint16 when the data is corrupted + */ + static uint16_t validateCRC(const uint8_t* message, uint32_t length); +}; + +#endif //ECSS_SERVICES_CRCHELPER_HPP diff --git a/inc/Message.hpp b/inc/Message.hpp index 0e311b4e1b796b00cb0bd6294a3b5afd9e5cb651..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; @@ -87,7 +94,7 @@ public: * @todo See if more than uint8_t strings will be supported * @todo Is uint16_t size too much or not enough? It has to be defined */ - void appendString(uint16_t size, uint8_t *value); + void appendString(uint16_t size, const uint8_t *value); /** * Reads the next \p numBits bits from the the message in a big-endian format diff --git a/inc/Services/EventReportService.hpp b/inc/Services/EventReportService.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4f516648a2a681a17eb14879d2c676dd7fb56164 --- /dev/null +++ b/inc/Services/EventReportService.hpp @@ -0,0 +1,128 @@ +#ifndef ECSS_SERVICES_EVENTREPORTSERVICE_HPP +#define ECSS_SERVICES_EVENTREPORTSERVICE_HPP + +#include "Service.hpp" +/** + * Implementation of ST[05] event reporting service + * @todo add enum event definition id (and maybe some appending?) + * + * @todo changes enums event IDs + * + * Note: enum IDs are these just for test purposes + * + */ +#define CSS_EVENTS_MAX_COUNT 16 +#define ECSS_EVENTS_BITS 16 + +class EventReportService : public Service { +public: + EventReportService() { + serviceType = 5; + } + + /** + * Type of the information event + */ + enum InformationEvent { + /** + * An unknown event occured + */ + InformativeUnknownEvent = 0, + /** + * Watchdogs have reset + */ + WWDGReset = 1, + /** + * An assertion has failed + */ + AssertionFail = 2, + /** + * Microcontroller has started + */ + MCUStart = 3, + }; + + /** + * Type of the low severity anomaly event + */ + enum LowSeverityAnomalyEvent { + /** + * An unknown anomaly of low severity anomalyhas occurred + */ + LowSeverityUnknownEvent = 1, + }; + + /** + * Type of the medium severity anomaly event + */ + enum MediumSeverityAnomalyEvent { + /** + * An unknown anomaly of medium severity has occurred + */ + MediumSeverityUnknownEvent = 2, + }; + + /** + * Type of the high severity anomaly event + */ + enum HighSeverityAnomalyEvent { + /** + * An unknown anomaly of high severity has occurred + */ + HighSeverityUnknownEvent = 3, + }; + + /** + * TM[5,1] informative event report + * Send report to inform the respective recipients about an event + * + * Note: The parameters are defined by the standard, but the event definition id is missing! + * + * @param eventID event definition ID + * @param data the data of the report + * @param length the length of the data + */ + void informativeEventReport(InformationEvent eventID, const uint8_t *data, uint8_t length); + + /** + * TM[5,2] low severiity anomaly report + * Send report when there is an anomaly event of low severity to the respective recipients + * + * Note: The parameters are defined by the standard, but the event definition id is missing! + * + * @param eventID event definition ID + * @param data the data of the report + * @param length the length of the data + */ + void + lowSeverityAnomalyReport(LowSeverityAnomalyEvent eventID, const uint8_t *data, uint8_t length); + + /** + * TM[5,3] medium severity anomaly report + * Send report when there is an anomaly event of medium severity to the respective recipients + * + * Note: The parameters are defined by the standard, but the event definition id is missing! + * + * @param eventID event definition ID + * @param data the data of the report + * @param length the length of the data + */ + void mediumSeverityAnomalyReport(MediumSeverityAnomalyEvent eventID, const uint8_t *data, + uint8_t length); + + /** + * TM[5,4] high severity anomaly report + * Send report when there is an anomaly event of hgih severity to the respective recipients + * + * Note: The parameters are defined by the standard, but the event definition id is missing! + * + * @param eventID event definition ID + * @param data the data of the report + * @param length the length of the data + */ + void highSeverityAnomalyReport(HighSeverityAnomalyEvent eventID, const uint8_t *data, + uint8_t length); + +}; + +#endif //ECSS_SERVICES_EVENTREPORTSERVICE_HPP 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/Helpers/CRCHelper.cpp b/src/Helpers/CRCHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e0cd4ebf914ff885c7f50a52aeec4cd3e42c8da8 --- /dev/null +++ b/src/Helpers/CRCHelper.cpp @@ -0,0 +1,34 @@ +#include "Helpers/CRCHelper.hpp" + +// TODO: THIS IS TEMPORARY CODE, WILL SURELY BE REPLACED + +uint16_t CRCHelper::calculateCRC(const uint8_t* message, uint32_t length) { + // shift register contains all 1's initially (ECSS-E-ST-70-41C, Annex B - CRC and ISO checksum) + uint16_t shiftReg = 0xFFFFu; + + // CRC16-CCITT generator polynomial (as specified in standard) + uint16_t polynomial = 0x1021u; + + for (int i = 0; i < length; i++) { + // "copy" (XOR w/ existing contents) the current msg bits into the MSB of the shift register + shiftReg ^= (message[i] << 8u); + + for (int j = 0; j < 8; j++) { + // if the MSB is set, the bitwise AND gives 1 + if ((shiftReg & 0x8000u) != 0) { + // toss out of the register the MSB and divide (XOR) its content with the generator + shiftReg = ((shiftReg << 1u) ^ polynomial); + } + else { + // just toss out the MSB and make room for a new bit + shiftReg <<= 1u; + } + } + } + return shiftReg; +} + +uint16_t CRCHelper::validateCRC(const uint8_t *message, uint32_t length) { + return calculateCRC(message, length); + // CRC result of a correct msg w/checksum appended is 0 +} diff --git a/src/Message.cpp b/src/Message.cpp index 997b617ba4035f5e8af9e40ad27715df94551db9..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,17 +67,17 @@ 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); dataSize += size; } -void Message::appendString(uint16_t size, uint8_t *value) { - assert(dataSize + size <= ECSS_MAX_MESSAGE_SIZE); - assert(size < ECSS_MAX_STRING_SIZE); +void Message::appendString(uint16_t size, const uint8_t *value) { + 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, 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/Services/EventReportService.cpp b/src/Services/EventReportService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0a89f5c9765aa49e8932c0df9c1a4f1ed732a4ac --- /dev/null +++ b/src/Services/EventReportService.cpp @@ -0,0 +1,46 @@ +#include "Services/EventReportService.hpp" +#include "Message.hpp" + + +void EventReportService::informativeEventReport(InformationEvent eventID, const uint8_t *data, + uint8_t length) { + // TM[5,1] + Message report = createTM(1); + report.appendEnum16(eventID); + report.appendString(length, data); + + storeMessage(report); +} + +void +EventReportService::lowSeverityAnomalyReport(LowSeverityAnomalyEvent eventID, const uint8_t *data, + uint8_t length) { + // TM[5,2] + Message report = createTM(2); + report.appendEnum16(eventID); + report.appendString(length, data); + + storeMessage(report); +} + +void EventReportService::mediumSeverityAnomalyReport(MediumSeverityAnomalyEvent eventID, + const uint8_t *data, + uint8_t length) { + // TM[5,3] + Message report = createTM(3); + report.appendEnum16(eventID); + report.appendString(length, data); + + storeMessage(report); +} + +void +EventReportService::highSeverityAnomalyReport(HighSeverityAnomalyEvent eventID, const uint8_t *data, + uint8_t length) { + // TM[5,4] + Message report = createTM(4); + report.appendEnum16(eventID); + report.appendString(length, data); + + storeMessage(report); +} diff --git a/src/main.cpp b/src/main.cpp index 8f4bf906c97b832f8a5e1d2bf8298160deed8521..de68902f98961a2f8781b450f8fd9a83eb328f2e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,14 @@ #include <iostream> +#include "Helpers/CRCHelper.hpp" #include "Services/TestService.hpp" #include "Services/ParameterService.hpp" #include "Services/RequestVerificationService.hpp" #include "Services/MemoryManagementService.hpp" +#include "Services/EventReportService.hpp" #include "Message.hpp" #include "MessageParser.hpp" #include "Services/MemoryManagementService.hpp" +#include "ErrorHandler.hpp" int main() { Message packet = Message(0, 0, Message::TC, 1); @@ -108,6 +111,18 @@ int main() { receivedMessage = Message(1, 10, Message::TC, 3); reqVerifService.failRoutingVerification(receivedMessage); + // ST[05] test [works] + const unsigned char eventReportData[12] = "Hello World"; + EventReportService eventReportService; + eventReportService.informativeEventReport(EventReportService::InformativeUnknownEvent, + eventReportData, 11); + eventReportService.lowSeverityAnomalyReport(EventReportService::LowSeverityUnknownEvent, + eventReportData, 11); + eventReportService.mediumSeverityAnomalyReport(EventReportService::MediumSeverityUnknownEvent, + eventReportData, 11); + eventReportService.highSeverityAnomalyReport(EventReportService::HighSeverityUnknownEvent, + eventReportData, 11); + // MessageParser class test std::cout << "\n"; @@ -131,5 +146,14 @@ int main() { message = Message(1, 10, Message::TC, 3); 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); +} diff --git a/test/Helpers/CRCHelper.cpp b/test/Helpers/CRCHelper.cpp new file mode 100644 index 0000000000000000000000000000000000000000..73909a920026b63e62ba0364f0db51777ea665c9 --- /dev/null +++ b/test/Helpers/CRCHelper.cpp @@ -0,0 +1,51 @@ +#include "catch2/catch.hpp" +#include "Helpers/CRCHelper.hpp" + +TEST_CASE("CRC calculation - Basic string tests") { + CHECK(CRCHelper::calculateCRC((uint8_t*) "Raccoon Squad!", 14) == 0x08FC); + CHECK(CRCHelper::calculateCRC((uint8_t*) "ASAT", 4) == 0xBFFA); + CHECK(CRCHelper::calculateCRC((uint8_t*) "All your space are belong to us", 31) == 0x545F); + CHECK(CRCHelper::calculateCRC((uint8_t*) "SPAAAAAAAAACE!", 14) == 0xB441); +} + +TEST_CASE("CRC calculation - Basic byte tests") { + SECTION("ECSS compliance verification tests (p.617)") { + uint8_t data1[2] = {0x00, 0x00}; + uint8_t data2[3] = {0x00, 0x00, 0x00}; + uint8_t data3[4] = {0xAB, 0xCD, 0xEF, 0x01}; + uint8_t data4[6] = {0x14, 0x56, 0xF8, 0x9A, 0x00, 0x01}; + + CHECK(CRCHelper::calculateCRC(data1, 2) == 0x1D0F); + CHECK(CRCHelper::calculateCRC(data2, 3) == 0xCC9C); + CHECK(CRCHelper::calculateCRC(data3, 4) == 0x04A2); + CHECK(CRCHelper::calculateCRC(data4, 6) == 0x7FD5); + } + + SECTION("Null (0x00) before ending") { + uint8_t data1[5] = {0x45, 0xF2, 0x00, 0xA2, 0x01}; + uint8_t data2[8] = {0x21, 0x65, 0xDF, 0x00, 0xC4, 0x00, 0x00, 0xBF}; + uint8_t data3[4] = {0x07, 0x00, 0x05, 0xFF}; + + CHECK(CRCHelper::calculateCRC(data1, 5) == 0x3A2B); + CHECK(CRCHelper::calculateCRC(data2, 8) == 0x89EE); + CHECK(CRCHelper::calculateCRC(data3, 4) == 0x34E8); + } +} + +TEST_CASE("CRC validation - Basic tests") { + uint8_t data1[7] = {'H', 'e', 'l', 'l', 'o', 0xDA, 0xDA}; + uint8_t data2[6] = {'A', 'S', 'A', 'T', 0xBF, 0xFA}; + uint8_t data3[10] = {'S', 'p', 'A', '@', 'A', 'c', '3', '!', 0xB4, 0x41}; + // checksum from original SPAAAAAAAAACE! string + uint8_t data4[6] = {'A', 0x43, 0x52, 0xDF, 0xBF, 0xFA}; + // ASAT, but "corrupted" with the last 2 bytes the original checksum of the 'ASAT' string + + uint8_t data5[9] = {'C', 'U', 'B', 'E', 'S', 'A', 'T', 0x53, 0x15}; //corrupted CRC checksum + + + CHECK(CRCHelper::validateCRC(data1, 7) == 0x0); + CHECK(CRCHelper::validateCRC(data2, 6) == 0x0); + CHECK(CRCHelper::validateCRC(data3, 10) != 0x0); + CHECK(CRCHelper::validateCRC(data4, 6) != 0x0); + CHECK(CRCHelper::validateCRC(data5, 9) != 0x0); +} diff --git a/test/Services/EventReportService.cpp b/test/Services/EventReportService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32f4f30b18ee8d9e38c9c5a059fcfd07ad27c9f1 --- /dev/null +++ b/test/Services/EventReportService.cpp @@ -0,0 +1,88 @@ +#include <catch2/catch.hpp> +#include <Services/EventReportService.hpp> +#include <Message.hpp> +#include "ServiceTests.hpp" +#include <cstring> + +/* + * @todo Change the reinterpret_cast + */ +TEST_CASE("Informative Event Report TM[5,1]", "[service][st05]") { + EventReportService eventReportService; + const unsigned char eventReportData[] = "HelloWorld"; + char checkString[255]; + eventReportService.informativeEventReport(EventReportService::InformativeUnknownEvent, + eventReportData, 10); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + // Checks for the data-members of the report Message created + CHECK(report.serviceType == 5); + CHECK(report.messageType == 1); + CHECK(report.packetType == Message::TM); // packet type(TM = 0, TC = 1) + REQUIRE(report.dataSize == 12); + // Check for the value that is stored in <<data>> array(data-member of object response) + CHECK(report.readEnum16() == 0); + report.readString(checkString, 10); + CHECK(strcmp(checkString, reinterpret_cast<const char *>(eventReportData)) == 0); +} + +TEST_CASE("Low Severity Anomaly Report TM[5,2]", "[service][st05]") { + EventReportService eventReportService; + const unsigned char eventReportData[] = "HelloWorld"; + char checkString[255]; + eventReportService.lowSeverityAnomalyReport(EventReportService::LowSeverityUnknownEvent, + eventReportData, 10); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + // Checks for the data-members of the report Message created + CHECK(report.serviceType == 5); + CHECK(report.messageType == 2); + CHECK(report.packetType == Message::TM); // packet type(TM = 0, TC = 1) + REQUIRE(report.dataSize == 12); + // Check for the value that is stored in <<data>> array(data-member of object response) + CHECK(report.readEnum16() == 1); + report.readString(checkString, 10); + CHECK(strcmp(checkString, reinterpret_cast<const char *>(eventReportData)) == 0); +} + +TEST_CASE("Medium Severity Anomaly Report TM[5,3]", "[service][st05]") { + EventReportService eventReportService; + const unsigned char eventReportData[] = "HelloWorld"; + char checkString[255]; + eventReportService.mediumSeverityAnomalyReport + (EventReportService::MediumSeverityUnknownEvent, eventReportData, 10); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + // Checks for the data-members of the report Message created + CHECK(report.serviceType == 5); + CHECK(report.messageType == 3); + CHECK(report.packetType == Message::TM); // packet type(TM = 0, TC = 1) + REQUIRE(report.dataSize == 12); + // Check for the value that is stored in <<data>> array(data-member of object response) + CHECK(report.readEnum16() == 2); + report.readString(checkString, 10); + CHECK(strcmp(checkString, reinterpret_cast<const char *>(eventReportData)) == 0); +} + +TEST_CASE("High Severity Anomaly Report TM[5,4]", "[service][st05]") { + EventReportService eventReportService; + const unsigned char eventReportData[] = "HelloWorld"; + char checkString[255]; + eventReportService.highSeverityAnomalyReport(EventReportService::HighSeverityUnknownEvent, + eventReportData, 10); + REQUIRE(ServiceTests::hasOneMessage()); + + Message report = ServiceTests::get(0); + // Checks for the data-members of the report Message created + CHECK(report.serviceType == 5); + CHECK(report.messageType == 4); + CHECK(report.packetType == Message::TM); // packet type(TM = 0, TC = 1) + REQUIRE(report.dataSize == 12); + // Check for the value that is stored in <<data>> array(data-member of object response) + CHECK(report.readEnum16() == 3); + report.readString(checkString, 10); + CHECK(strcmp(checkString, reinterpret_cast<const char *>(eventReportData)) == 0); +}