From 374faa0b5565202d503fac99a5d86932dceecb9e Mon Sep 17 00:00:00 2001
From: kongr45gpen <electrovesta@gmail.com>
Date: Sat, 10 Aug 2019 00:54:40 +0300
Subject: [PATCH] Complete ECSS & CCSDS MessageParser::compose() functions

---
 inc/ECSS_Definitions.hpp |  6 ++++
 inc/ErrorHandler.hpp     |  4 +++
 inc/MessageParser.hpp    | 28 ++++++++++------
 inc/etl/String.hpp       | 19 +++++++++++
 src/MessageParser.cpp    | 72 +++++++++++++++++++++++++++++++++++-----
 5 files changed, 110 insertions(+), 19 deletions(-)

diff --git a/inc/ECSS_Definitions.hpp b/inc/ECSS_Definitions.hpp
index 05ab25c7..90eec544 100644
--- a/inc/ECSS_Definitions.hpp
+++ b/inc/ECSS_Definitions.hpp
@@ -23,6 +23,12 @@
  */
 #define ECSS_MAX_MESSAGE_SIZE 1024U
 
+
+/**
+ * The maximum size of a regular ECSS message, plus its headers and trailing data, in bytes
+ */
+#define CCSDS_MAX_MESSAGE_SIZE (ECSS_MAX_MESSAGE_SIZE + 6u + 6u + 2u)
+
 /**
  * The maximum size of a string to be read or appended to a Message, in bytes
  *
diff --git a/inc/ErrorHandler.hpp b/inc/ErrorHandler.hpp
index 97732f38..238d685d 100644
--- a/inc/ErrorHandler.hpp
+++ b/inc/ErrorHandler.hpp
@@ -73,6 +73,10 @@ public:
 		 * Attempt to insert new function in a full function map (ST[08])
 		 */
 		FunctionMapFull = 10,
+		/**
+		 * A Message that is included within another message is too large
+		 */
+		NestedMessageTooLarge = 11,
 	};
 
 	/**
diff --git a/inc/MessageParser.hpp b/inc/MessageParser.hpp
index e7ebe863..7bc486ef 100644
--- a/inc/MessageParser.hpp
+++ b/inc/MessageParser.hpp
@@ -51,7 +51,7 @@ public:
 	 * @param length The size of the message
 	 * @return A new object that represents the parsed message
 	 */
-	Message parse(uint8_t* data, uint32_t length);
+	static Message parse(uint8_t* data, uint32_t length);
 
 	/**
 	 * Parse data that contains the ECSS packet header, without the CCSDS space packet header
@@ -60,23 +60,31 @@ public:
 	 * this great analysis:
 	 * stackoverflow.com/questions/15078638/can-i-turn-unsigned-char-into-char-and-vice-versa
 	 */
-	Message parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data);
+	static Message parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data);
 
 	/**
 	 * @brief Overloaded version of \ref MessageParser::parseECSSTC(String<ECSS_TC_REQUEST_STRING_SIZE> data)
 	 * @param data A uint8_t array of the TC packet data
 	 * @return Parsed message
 	 */
-	Message parseECSSTC(uint8_t* data);
+	static Message parseECSSTC(uint8_t* data);
 
 	/**
-	 * @brief Converts a TC message to a string, appending just the ECSS header
+	 * @brief Converts a TC or TM message to a message string, appending just the ECSS header
+	 * @todo Add time reference, as soon as it is available and the format has been specified
 	 * @param message The Message object to be parsed to a String
-	 * @return A String class containing the parsed TC request
-	 * @attention The returned String has a fixed size, therefore the message size is considered
-	 * fixed and equal to the ECSS_TC_REQUEST_STRING_SIZE definition.
+	 * @param size The wanted size of the message (including the headers). Messages larger than \ref size display an
+	 * error. Messages smaller than \ref size are padded with zeros. When `size = 0`, there is no size limit.
+	 * @return A String class containing the parsed Message
 	 */
-	String<ECSS_TC_REQUEST_STRING_SIZE> createECSSTC(Message& message);
+	static String<CCSDS_MAX_MESSAGE_SIZE> composeECSS(const Message& message, uint16_t size = 0u); // Ignore-MISRA
+
+	/**
+	 * @brief Converts a TC or TM message to a packet string, appending the ECSS and then the CCSDS header
+	 * @param message The Message object to be parsed to a String
+	 * @return A String class containing the parsed Message
+	 */
+	static String<CCSDS_MAX_MESSAGE_SIZE> compose(const Message& message);
 
 private:
 	/**
@@ -88,7 +96,7 @@ private:
 	 * @param length The size of the header
 	 * @param message The Message to modify based on the header
 	 */
-	void parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message);
+	static void parseECSSTCHeader(const uint8_t* data, uint16_t length, Message& message);
 
 	/**
 	 * Parse the ECSS Telemetry packet secondary header
@@ -99,7 +107,7 @@ private:
 	 * @param length The size of the header
 	 * @param message The Message to modify based on the header
 	 */
-	void parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message);
+	static void parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message);
 };
 
 #endif // ECSS_SERVICES_MESSAGEPARSER_HPP
diff --git a/inc/etl/String.hpp b/inc/etl/String.hpp
index 11c04b67..d393c676 100644
--- a/inc/etl/String.hpp
+++ b/inc/etl/String.hpp
@@ -56,6 +56,25 @@ public:
 	 */
 	String(const char* text) // NOLINTNEXTLINE(google-explicit-constructor)
 	    : etl::string<MAX_SIZE>(text) {}
+
+	using etl::istring::append; // Use the append function from the parent
+
+    /**
+     * Append a specified with of bytes from a uint8_t array to the String
+     * @details The array does NOT need to be null-terminated
+     * @param data The characters to append
+     * @param n The number of characters that \ref data contains
+     * @return This string
+     */
+	String& append(const uint8_t* data, size_t n) {
+		etl::string<MAX_SIZE>::append(reinterpret_cast<const char*>(data), n);
+		return *this;
+	}
+
+//	String& append(const String<MAX_SIZE> & str) {
+//		etl::string<MAX_SIZE>::append(str);
+//		return *this;
+//	}
 };
 
 #endif // ECSS_SERVICES_ETL_STRING_HPP
diff --git a/src/MessageParser.cpp b/src/MessageParser.cpp
index b300816b..2c6534e7 100644
--- a/src/MessageParser.cpp
+++ b/src/MessageParser.cpp
@@ -76,8 +76,6 @@ void MessageParser::parseECSSTCHeader(const uint8_t* data, uint16_t length, Mess
 	uint8_t serviceType = data[1];
 	uint8_t messageType = data[2];
 
-	// todo: Fix this parsing function, because it assumes PUS header in data, which is not true
-	//  with the current implementation
 	ErrorHandler::assertRequest(pusVersion == 2U, message, ErrorHandler::UnacceptableMessage);
 
 	// Remove the length of the header
@@ -106,18 +104,74 @@ Message MessageParser::parseECSSTC(uint8_t* data) {
 	return message;
 }
 
-String<ECSS_TC_REQUEST_STRING_SIZE> MessageParser::createECSSTC(Message& message) {
-	uint8_t tempString[ECSS_TC_REQUEST_STRING_SIZE] = {0};
+String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::composeECSS(const Message& message, uint16_t size) {
+	uint8_t header[5];
 
-	tempString[0] = ECSS_PUS_VERSION << 4; // Assign the pusVersion = 2
-	tempString[1] = message.serviceType;
-	tempString[2] = message.messageType;
-	memcpy(tempString + 5, message.data, ECSS_TC_REQUEST_STRING_SIZE - 5);
-	String<ECSS_TC_REQUEST_STRING_SIZE> dataString(tempString);
+	if (message.packetType == Message::TC) {
+		header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2
+		header[1] = message.serviceType;
+		header[2] = message.messageType;
+		header[3] = 0;
+		header[4] = 0;
+	} else {
+		header[0] = ECSS_PUS_VERSION << 4U; // Assign the pusVersion = 2
+		header[1] = message.serviceType;
+		header[2] = message.messageType;
+		header[3] = static_cast<uint8_t>(message.messageTypeCounter >> 8U);
+		header[4] = static_cast<uint8_t>(message.messageTypeCounter & 0xffU);
+	}
+
+	String<CCSDS_MAX_MESSAGE_SIZE> dataString(header, 5);
+	dataString.append(message.data, message.dataSize);
+
+	// Make sure to reach the requested size
+	if (size != 0) {
+		if (dataString.size() > size) {
+			// Message overflow
+			ErrorHandler::reportInternalError(ErrorHandler::NestedMessageTooLarge);
+		} else if (dataString.size() < size) {
+			// Append some 0s
+			dataString.append(size - dataString.size(), 0);
+		} else {
+			// The message has an equal size to the requested one - do nothing
+		}
+	}
 
 	return dataString;
 }
 
+String<CCSDS_MAX_MESSAGE_SIZE> MessageParser::compose(const Message& message) {
+	uint8_t header[6];
+
+	// First, compose the ECSS part
+	String<CCSDS_MAX_MESSAGE_SIZE> ecssMessage = MessageParser::composeECSS(message);
+
+	// Sanity check that there is enough space for the string
+	ASSERT_INTERNAL((ecssMessage.size() + 6U) <= CCSDS_MAX_MESSAGE_SIZE, ErrorHandler::StringTooLarge);
+
+	// Parts of the header
+	uint16_t packetId = 0; // TODO: Add the APID here
+	packetId |= (1U << 11U); // Secondary header flag
+	packetId |= (message.packetType == Message::TC) ? (1U << 12U) : (0U); // Ignore-MISRA
+	uint16_t packetSequenceControl = message.packetSequenceCount;
+	uint16_t packetDataLength = ecssMessage.size();
+
+	// Compile the header
+	header[0] = packetId >> 8U;
+	header[1] = packetId & 0xffU;
+	header[2] = packetSequenceControl >> 8U;
+	header[3] = packetSequenceControl & 0xffU;
+	header[4] = packetDataLength >> 8U;
+	header[5] = packetDataLength & 0xffU;
+
+	// Compile the final message by appending the header
+	String<CCSDS_MAX_MESSAGE_SIZE> ccsdsMessage(header, 6);
+	ccsdsMessage.append(ecssMessage);
+
+	return ccsdsMessage;
+}
+
+
 void MessageParser::parseECSSTMHeader(const uint8_t* data, uint16_t length, Message& message) {
 	ErrorHandler::assertRequest(length >= 5, message, ErrorHandler::UnacceptableMessage);
 
-- 
GitLab