diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 5b44ac96da73d9acc2af3bb992aa3611e574d766..543296c7d8bc297f1b7e5a88137a0e2892e2c3ea 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -3,25 +3,6 @@ <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="c" header="h" fileNamingConvention="NONE" /> diff --git a/.idea/misc.xml b/.idea/misc.xml index 91d4361702769bdfe6326464a61298263c79e5e3..383642545dce9706f039abbac53551e64d848c2b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -2,12 +2,12 @@ <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> </component> </project> \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fc2bf477b794ceb6345b7184c1de9cf2b994050..5b2e3cacbfec106d4ace5229fbb164da31b5ea2a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,8 @@ add_custom_target(check add_library(common OBJECT src/Message.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/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..28f6d572c5d187ce431a30e174a51b6f4bbb3fde 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -87,7 +87,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/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..1374287e6a57891293b76883021d6098d2758588 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -73,7 +73,7 @@ void Message::appendString(uint8_t size, const char *value) { dataSize += size; } -void Message::appendString(uint16_t size, uint8_t *value) { +void Message::appendString(uint16_t size, const uint8_t *value) { assert(dataSize + size <= ECSS_MAX_MESSAGE_SIZE); assert(size < ECSS_MAX_STRING_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 29d13406d0b8d74afab6b1b059bb0995ed5f0454..51ce18623dd29ef28087155d9d458a5c7dc6704e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,9 @@ #include <iostream> +#include "Helpers/CRCHelper.hpp" #include "Services/TestService.hpp" #include "Services/ParameterService.hpp" #include "Services/RequestVerificationService.hpp" +#include "Services/EventReportService.hpp" #include "Message.hpp" #include "MessageParser.hpp" #include "Services/MemoryManagementService.hpp" @@ -67,13 +69,13 @@ int main() { rcvPack.appendEnum8(MemoryManagementService::MemoryID::EXTERNAL); // Memory ID rcvPack.appendUint16(3); // Iteration count rcvPack.appendUint64(reinterpret_cast<uint64_t >(string)); // Start address - rcvPack.appendUint16(sizeof(string)/ sizeof(string[0])); // Data read length + rcvPack.appendUint16(sizeof(string) / sizeof(string[0])); // Data read length rcvPack.appendUint64(reinterpret_cast<uint64_t >(anotherStr)); - rcvPack.appendUint16(sizeof(anotherStr)/ sizeof(anotherStr[0])); + rcvPack.appendUint16(sizeof(anotherStr) / sizeof(anotherStr[0])); rcvPack.appendUint64(reinterpret_cast<uint64_t >(yetAnotherStr)); - rcvPack.appendUint16(sizeof(yetAnotherStr)/ sizeof(yetAnotherStr[0])); + rcvPack.appendUint16(sizeof(yetAnotherStr) / sizeof(yetAnotherStr[0])); memMangService.rawDataMemorySubservice.dumpRawData(rcvPack); rcvPack = Message(6, 2, Message::TC, 1); @@ -97,6 +99,18 @@ int main() { reqVerifService.failExecutionVerification(Message::TC, true, 2, 2, 10, 6); reqVerifService.failRoutingVerification(Message::TC, true, 2, 2, 10, 7); + // 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"; // ST[17] test 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); +}