diff --git a/inc/Helpers/TimeGetter.hpp b/inc/Helpers/TimeGetter.hpp index d4b1c1cdd1f7a856b1978442f08afe07ab925cea..d2dfaf7e7fc45787e695e0b0ba1a1e4a5073daba 100644 --- a/inc/Helpers/TimeGetter.hpp +++ b/inc/Helpers/TimeGetter.hpp @@ -11,14 +11,6 @@ */ class TimeGetter { public: - /** - * @brief Gets the current time in UNIX epoch - * @return Current UNIX epoch time, in elapsed seconds - */ - static inline uint32_t getSeconds() { - return static_cast<uint32_t>(time(nullptr)); - } - /** * Returns the current UTC time. * @note diff --git a/inc/Message.hpp b/inc/Message.hpp index be03a417e01d4843b721842bdd81982aabe953c1..39f3ced9559551cb8abfaa14676bea1601415fe0 100644 --- a/inc/Message.hpp +++ b/inc/Message.hpp @@ -1,12 +1,12 @@ #ifndef ECSS_SERVICES_PACKET_H #define ECSS_SERVICES_PACKET_H -#include "ECSS_Definitions.hpp" #include <cstdint> #include <etl/String.hpp> #include <etl/wstring.h> -#include "macros.hpp" +#include "ECSS_Definitions.hpp" #include "Time/Time.hpp" +#include "macros.hpp" /** * A telemetry (TM) or telecommand (TC) message (request/report), as specified in ECSS-E-ST-70-41C @@ -72,7 +72,7 @@ public: enum PacketType { TM = 0, ///< Telemetry - TC = 1 ///< Telecommand + TC = 1 ///< Telecommand }; // The service and message IDs are 8 bits (5.3.1b, 5.3.3.1d) @@ -346,6 +346,23 @@ public: return appendWord(reinterpret_cast<uint32_t&>(value)); } + + /** + * Adds a 8 byte signed integer to the end of the message + * + * PTC = 4, PFC = 16 + */ + void appendSint64(int64_t value) { + return appendUint64(reinterpret_cast<uint64_t&>(value)); + } + + /** + * Adds an 8 byte time Offset to the message + */ + void appendRelativeTime(Time::RelativeTime value) { + return appendSint64(value); + } + /** * Adds a 4-byte single-precision floating point number to the end of the message * @@ -513,6 +530,23 @@ public: return reinterpret_cast<int32_t&>(value); } + /** + * Fetches a 4-byte unsigned integer from the current position in the message + * + * PTC = 4, PFC = 14 + */ + int64_t readSint64() { + uint64_t value = readUint64(); + return reinterpret_cast<int64_t&>(value); + } + + /** + * Fetches an 8 byte time Offset from the current position in the message + */ + Time::RelativeTime readRelativeTime() { + return readSint64(); + }; + /** * Fetches an 4-byte single-precision floating point number from the current position in the * message @@ -697,6 +731,10 @@ template <> inline void Message::append(const Time::CustomCUC_t& timeCUC) { appendCustomCUCTimeStamp(timeCUC); } +template <> +inline void Message::append(const Time::RelativeTime& value) { + appendRelativeTime(value); +} /** * Appends an ETL string to the message. ETL strings are handled as ECSS octet strings, meaning that the string size @@ -758,5 +796,9 @@ template <> inline Time::CustomCUC_t Message::read() { return readCustomCUCTimeStamp(); } +template <> +inline Time::RelativeTime Message::read() { + return readRelativeTime(); +} #endif // ECSS_SERVICES_PACKET_H diff --git a/inc/Services/TimeBasedSchedulingService.hpp b/inc/Services/TimeBasedSchedulingService.hpp index 95780f21da786c7b8fbe9d7c6dc69abc6a42f3c4..a027190b936de5ebf6b1222dc555916e9111d21f 100644 --- a/inc/Services/TimeBasedSchedulingService.hpp +++ b/inc/Services/TimeBasedSchedulingService.hpp @@ -82,7 +82,7 @@ private: struct ScheduledActivity { Message request; ///< Hold the received command request RequestID requestID; ///< Request ID, characteristic of the definition - uint32_t requestReleaseTime = 0; ///< Keep the command release time + Time::CustomCUC_t requestReleaseTime{0}; ///< Keep the command release time }; /** diff --git a/inc/Time/Time.hpp b/inc/Time/Time.hpp index 465f729b38d3b03dcc03e3a5df8e864c107ca47e..77c0cb0878964b9a6593a0d9bda429f4867a0f5e 100644 --- a/inc/Time/Time.hpp +++ b/inc/Time/Time.hpp @@ -2,9 +2,9 @@ #define ECSS_TIMEHPP #include <cstdint> -#include "macros.hpp" -#include "etl/String.hpp" #include "ErrorHandler.hpp" +#include "etl/String.hpp" +#include "macros.hpp" /** * @defgroup Time Time @@ -70,38 +70,37 @@ * @ingroup Time * @author Baptiste Fournier */ -namespace Time -{ -inline constexpr uint8_t SecondsPerMinute = 60; -inline constexpr uint16_t SecondsPerHour = 3600; -inline constexpr uint32_t SecondsPerDay = 86400; -static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; +namespace Time { + inline constexpr uint8_t SecondsPerMinute = 60; + inline constexpr uint16_t SecondsPerHour = 3600; + inline constexpr uint32_t SecondsPerDay = 86400; + static constexpr uint8_t DaysOfMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; -/** + /** * Number of bytes used for the basic time units of the CUC header for this mission */ -inline constexpr uint8_t CUCSecondsBytes = 2; + inline constexpr uint8_t CUCSecondsBytes = 2; -/** + /** * Number of bytes used for the fractional time units of the CUC header for this mission */ -inline constexpr uint8_t CUCFractionalBytes = 2; + inline constexpr uint8_t CUCFractionalBytes = 2; -/** + /** * The system epoch (clock measurement starting time) * All timestamps emitted by the ECSS services will show the elapsed time (seconds, days etc.) from this epoch. */ -inline constexpr struct { - uint16_t year; - uint8_t month; - uint8_t day; -} Epoch{ - 2020, - 1, - 1, -}; - -/** + inline constexpr struct { + uint16_t year; + uint8_t month; + uint8_t day; + } Epoch{ + 2020, + 1, + 1, + }; + + /** * Number of seconds elapsed between the UNIX epoch (1 January 1970) and the system epoch. * * The system epoch is defined by @ref Epoch. @@ -111,18 +110,18 @@ inline constexpr struct { * @warning This value MUST be updated after every change of the system @ref Epoch. You can use utilities such as * https://www.unixtimestamp.com/ to obtain a correct result. */ -inline constexpr uint32_t EpochSecondsFromUnix = 1577836800; + inline constexpr uint32_t EpochSecondsFromUnix = 1577836800; -/** + /** * The maximum theoretical size in bytes of a CUC timestamp, including headers (P-field and T-field) */ -inline constexpr uint8_t CUCTimestampMaximumSize = 9; + inline constexpr uint8_t CUCTimestampMaximumSize = 9; -static_assert(Epoch.year >= 2019); -static_assert(Epoch.month < 11 && Epoch.month >= 0); -static_assert(Epoch.day < DaysOfMonth[Epoch.month]); + static_assert(Epoch.year >= 2019); + static_assert(Epoch.month < 11 && Epoch.month >= 0); + static_assert(Epoch.day < DaysOfMonth[Epoch.month]); -/** + /** * Builds the short P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. * * The short P-field contains only one byte. It is used when many octets are used to represent the basic or fractional @@ -133,32 +132,32 @@ static_assert(Epoch.day < DaysOfMonth[Epoch.month]); * @tparam fractionalBytes The number of octets used to represent the fractional time units * @return A single byte, representing the P-field contents */ -template <int secondsBytes, int fractionalBytes> -inline constexpr uint8_t buildShortCUCHeader() { - static_assert(secondsBytes <= 4, "Use buildLongCUCHeader instead"); - static_assert(fractionalBytes <= 3, "Use buildLongCUCHeader instead"); + template <int secondsBytes, int fractionalBytes> + inline constexpr uint8_t buildShortCUCHeader() { + static_assert(secondsBytes <= 4, "Use buildLongCUCHeader instead"); + static_assert(fractionalBytes <= 3, "Use buildLongCUCHeader instead"); - uint8_t header = 0; + uint8_t header = 0; - // P-Field extension is 0, CUC header is not extended - header += 0; + // P-Field extension is 0, CUC header is not extended + header += 0; - // We are using a custom TAI epoch ("agency-defined epoch") - header <<= 3U; - header += 0b010; + // We are using a custom TAI epoch ("agency-defined epoch") + header <<= 3U; + header += 0b010; - // Number of bytes in the basic time unit - header <<= 2U; - header += secondsBytes - 1; + // Number of bytes in the basic time unit + header <<= 2U; + header += secondsBytes - 1; - // Number of bytes in the fractional unit - header <<= 2U; - header += fractionalBytes; + // Number of bytes in the fractional unit + header <<= 2U; + header += fractionalBytes; - return header; -} + return header; + } -/** + /** * Builds the long P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. * * The long P-field contains two bytes. The 2nd byte is used to define the size of the additional octets added to the @@ -169,56 +168,56 @@ inline constexpr uint8_t buildShortCUCHeader() { * @tparam fractionalBytes The number of octets used to represent the fractional time units * @return Two bytes, representing the P-field contents */ -template <int secondsBytes, int fractionalBytes> -inline constexpr uint16_t buildLongCUCHeader() { - // cppcheck-suppress redundantCondition - static_assert(secondsBytes > 4 || fractionalBytes > 3, "Use buildShortCUCHeader instead"); - static_assert(secondsBytes <= 7, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); - static_assert(fractionalBytes <= 6, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); + template <int secondsBytes, int fractionalBytes> + inline constexpr uint16_t buildLongCUCHeader() { + // cppcheck-suppress redundantCondition + static_assert(secondsBytes > 4 || fractionalBytes > 3, "Use buildShortCUCHeader instead"); + static_assert(secondsBytes <= 7, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); + static_assert(fractionalBytes <= 6, "Number of bytes for seconds over maximum number of octets allowed by CCSDS"); - uint16_t header = 0; + uint16_t header = 0; - uint8_t octet1secondsBytes = std::min(4, secondsBytes); - uint8_t octet2secondsBytes = secondsBytes - octet1secondsBytes; + uint8_t octet1secondsBytes = std::min(4, secondsBytes); + uint8_t octet2secondsBytes = secondsBytes - octet1secondsBytes; - uint8_t octet1fractionalBytes = std::min(3, fractionalBytes); - uint8_t octet2fractionalBytes = fractionalBytes - octet1fractionalBytes; + uint8_t octet1fractionalBytes = std::min(3, fractionalBytes); + uint8_t octet2fractionalBytes = fractionalBytes - octet1fractionalBytes; - // P-Field extension is 1, CUC header is extended - header += 1; + // P-Field extension is 1, CUC header is extended + header += 1; - // We are using custom a TAI epoch - header <<= 3U; - header += 0b010; + // We are using custom a TAI epoch + header <<= 3U; + header += 0b010; - // Number of bytes in the basic time unit - header <<= 2U; - header += octet1secondsBytes - 1; + // Number of bytes in the basic time unit + header <<= 2U; + header += octet1secondsBytes - 1; - // Number of bytes in the fractional unit - header <<= 2U; - header += octet1fractionalBytes; + // Number of bytes in the fractional unit + header <<= 2U; + header += octet1fractionalBytes; - // P-Field extension is 1, CUC header was extended - header <<= 1U; - header += 1; + // P-Field extension is 1, CUC header was extended + header <<= 1U; + header += 1; - // Number of bytes in the extended basic time unit - header <<= 2U; - header += octet2secondsBytes; + // Number of bytes in the extended basic time unit + header <<= 2U; + header += octet2secondsBytes; - // Number of bytes in the extended fractional unit - header <<= 3U; - header += octet2fractionalBytes; + // Number of bytes in the extended fractional unit + header <<= 3U; + header += octet2fractionalBytes; - // Last 3 LSB are reserved for custom mission use - header <<= 2U; - header += 0; + // Last 3 LSB are reserved for custom mission use + header <<= 2U; + header += 0; - return header; -} + return header; + } -/** + /** * Builds the entire P-field of the CUC (CCSDS Unsegmented Time Code) format, as defined in CCSDS 301.0-B-4. * * The P-field contains the metadata of the timestamp, including information about its size and epoch. This function @@ -237,35 +236,61 @@ inline constexpr uint16_t buildLongCUCHeader() { * @tparam fractionalBytes The number of octets used to represent the fractional time units * @return One or two bytes representing the header */ -template <typename T, int secondsBytes, int fractionalBytes> -inline constexpr T buildCUCHeader() { - // TODO: Gitlab issue #106 - static_assert((secondsBytes + fractionalBytes) <= 8, "Complete arbitrary precision not supported"); - // cppcheck-suppress syntaxError - // cppcheck-suppress redundantCondition - if constexpr (secondsBytes <= 4 && fractionalBytes <= 3) { - return buildShortCUCHeader<secondsBytes, fractionalBytes>(); - } else { - return buildLongCUCHeader<secondsBytes, fractionalBytes>(); + template <typename T, int secondsBytes, int fractionalBytes> + inline constexpr T buildCUCHeader() { + // TODO: Gitlab issue #106 + static_assert((secondsBytes + fractionalBytes) <= 8, "Complete arbitrary precision not supported"); + // cppcheck-suppress syntaxError + // cppcheck-suppress redundantCondition + if constexpr (secondsBytes <= 4 && fractionalBytes <= 3) { + return buildShortCUCHeader<secondsBytes, fractionalBytes>(); + } else { + return buildLongCUCHeader<secondsBytes, fractionalBytes>(); + } } -} -/** + /** * Returns whether a year is a leap year according to the Gregorian calendar */ -constexpr bool isLeapYear(uint16_t year) { - if ((year % 4) != 0) { - return false; + constexpr bool isLeapYear(uint16_t year) { + if ((year % 4) != 0) { + return false; + } + if ((year % 100) != 0) { + return true; + } + return (year % 400) == 0; } - if ((year % 100) != 0) { - return true; + + typedef struct { + uint64_t elapsed100msTicks = 0; + } CustomCUC_t; + + /** + * A time shift for scheduled activities measured in seconds + */ + typedef int64_t RelativeTime; + + inline Time::CustomCUC_t operator+(const Time::CustomCUC_t time, RelativeTime relativeTime) { + return Time::CustomCUC_t{time.elapsed100msTicks + relativeTime / 10}; } - return (year % 400) == 0; -} -typedef struct { - uint64_t elapsed100msTicks = 0; -} CustomCUC_t; + inline Time::CustomCUC_t operator-(const Time::CustomCUC_t time, RelativeTime relativeTime) { + return Time::CustomCUC_t{time.elapsed100msTicks - relativeTime / 10}; + } + + inline Time::CustomCUC_t& operator+=(Time::CustomCUC_t& time, RelativeTime relativeTime) { + time.elapsed100msTicks += relativeTime / 10; + return time; + } + + inline bool operator<(Time::CustomCUC_t time1, Time::CustomCUC_t time2) { + return time1.elapsed100msTicks < time2.elapsed100msTicks; + } + + inline bool operator==(const Time::CustomCUC_t time1, Time::CustomCUC_t time2) { + return time1.elapsed100msTicks == time2.elapsed100msTicks; + } } // namespace Time diff --git a/src/Message.cpp b/src/Message.cpp index 0ba4d75aaed85e3584d6b7e81369390cb13bd6a3..4733fb914066ac246daa9adb216f60914997c669 100644 --- a/src/Message.cpp +++ b/src/Message.cpp @@ -1,9 +1,9 @@ #include "Message.hpp" -#include "macros.hpp" -#include <cstring> #include <ErrorHandler.hpp> -#include <ServicePool.hpp> #include <MessageParser.hpp> +#include <ServicePool.hpp> +#include <cstring> +#include "macros.hpp" Message::Message(uint8_t serviceType, uint8_t messageType, Message::PacketType packetType, uint16_t applicationId) : serviceType(serviceType), messageType(messageType), packetType(packetType), applicationId(applicationId) {} diff --git a/src/Platform/x86/TimeGetter.cpp b/src/Platform/x86/TimeGetter.cpp index d66917cabdf044516ec0bb3abffdd6ae59274b15..ea34fbce5d3e3af4eb3e106cd986e04fea97a498 100644 --- a/src/Platform/x86/TimeGetter.cpp +++ b/src/Platform/x86/TimeGetter.cpp @@ -1,7 +1,11 @@ #include "Helpers/TimeGetter.hpp" UTCTimestamp TimeGetter::getCurrentTimeUTC() { - UTCTimestamp currentTime(2020, 4, 10, 10, 15, 0); + time_t timeInSeconds = static_cast<time_t>(time(nullptr)); + tm* UTCTimeStruct = gmtime(&timeInSeconds); + UTCTimestamp currentTime(UTCTimeStruct->tm_year, UTCTimeStruct->tm_mon, + UTCTimeStruct->tm_mday, UTCTimeStruct->tm_hour, + UTCTimeStruct->tm_min, UTCTimeStruct->tm_sec); return currentTime; } diff --git a/src/Services/TimeBasedSchedulingService.cpp b/src/Services/TimeBasedSchedulingService.cpp index b88f46a48db0c5d1aa5ba79cc15d95bd54005451..864a9a70adc7de432117ed14678d1ad4c93f6c99 100644 --- a/src/Services/TimeBasedSchedulingService.cpp +++ b/src/Services/TimeBasedSchedulingService.cpp @@ -8,54 +8,41 @@ TimeBasedSchedulingService::TimeBasedSchedulingService() { } void TimeBasedSchedulingService::enableScheduleExecution(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction); - - executionFunctionStatus = true; // Enable the service + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::EnableTimeBasedScheduleExecutionFunction); + executionFunctionStatus = true; } void TimeBasedSchedulingService::disableScheduleExecution(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DisableTimeBasedScheduleExecutionFunction); - - executionFunctionStatus = false; // Disable the service + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DisableTimeBasedScheduleExecutionFunction); + executionFunctionStatus = false; } void TimeBasedSchedulingService::resetSchedule(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::ResetTimeBasedSchedule); - - executionFunctionStatus = false; // Disable the service - scheduledActivities.clear(); // Delete all scheduled activities + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::ResetTimeBasedSchedule); + executionFunctionStatus = false; + scheduledActivities.clear(); // todo: Add resetting for sub-schedules and groups, if defined } void TimeBasedSchedulingService::insertActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::InsertActivities); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::InsertActivities); // todo: Get the sub-schedule ID if they are implemented - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { // todo: Get the group ID first, if groups are used - uint32_t currentTime = TimeGetter::getSeconds(); // Get the current system time + Time::CustomCUC_t currentTime = TimeGetter::getCurrentTimeCustomCUC(); - uint32_t releaseTime = request.readUint32(); // Get the specified release time + Time::CustomCUC_t releaseTime = request.readCustomCUCTimeStamp(); if ((scheduledActivities.available() == 0) || (releaseTime < (currentTime + ECSSTimeMarginForActivation))) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); request.skipBytes(ECSSTCRequestStringSize); } else { - // Get the TC packet request uint8_t requestData[ECSSTCRequestStringSize] = {0}; request.readString(requestData, ECSSTCRequestStringSize); Message receivedTCPacket = MessageParser::parseECSSTC(requestData); - ScheduledActivity newActivity; // Create the new activity + ScheduledActivity newActivity; - // Assign the attributes to the newly created activity newActivity.request = receivedTCPacket; newActivity.requestReleaseTime = releaseTime; @@ -63,95 +50,82 @@ void TimeBasedSchedulingService::insertActivities(Message& request) { newActivity.requestID.applicationID = request.applicationId; newActivity.requestID.sequenceCount = request.packetSequenceCount; - scheduledActivities.push_back(newActivity); // Insert the new activities + scheduledActivities.push_back(newActivity); } } - sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time + sortActivitiesReleaseTime(scheduledActivities); } void TimeBasedSchedulingService::timeShiftAllActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities); - uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time + Time::CustomCUC_t current_time = TimeGetter::getCurrentTimeCustomCUC(); - // Find the earliest release time. It will be the first element of the iterator pair const auto releaseTimes = etl::minmax_element(scheduledActivities.begin(), scheduledActivities.end(), [](ScheduledActivity const& leftSide, ScheduledActivity const& rightSide) { return leftSide.requestReleaseTime < rightSide.requestReleaseTime; }); // todo: Define what the time format is going to be - int32_t relativeOffset = request.readSint32(); // Get the relative offset + Time::RelativeTime relativeOffset = request.readRelativeTime(); if ((releaseTimes.first->requestReleaseTime + relativeOffset) < (current_time + ECSSTimeMarginForActivation)) { - // Report the error ErrorHandler::reportError(request, ErrorHandler::SubServiceExecutionStartError); } else { - for (auto& activity : scheduledActivities) { - activity.requestReleaseTime += relativeOffset; // Time shift each activity + for (auto& activity: scheduledActivities) { + activity.requestReleaseTime += relativeOffset; } } } void TimeBasedSchedulingService::timeShiftActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftActivitiesById); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::TimeShiftActivitiesById); - uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time + Time::CustomCUC_t current_time = TimeGetter::getCurrentTimeCustomCUC(); - int32_t relativeOffset = request.readSint32(); // Get the offset first - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + Time::RelativeTime relativeOffset = request.readRelativeTime(); + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - // If the relative offset does not meet the restrictions issue an error if ((requestIDMatch->requestReleaseTime + relativeOffset) < (current_time + ECSSTimeMarginForActivation)) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } else { - requestIDMatch->requestReleaseTime += relativeOffset; // Add the time offset + requestIDMatch->requestReleaseTime += relativeOffset; } } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time + sortActivitiesReleaseTime(scheduledActivities); } void TimeBasedSchedulingService::deleteActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DeleteActivitiesById); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DeleteActivitiesById); - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID const auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - scheduledActivities.erase(requestIDMatch); // Delete activity from the schedule + scheduledActivities.erase(requestIDMatch); } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } @@ -159,82 +133,69 @@ void TimeBasedSchedulingService::deleteActivitiesByID(Message& request) { } void TimeBasedSchedulingService::detailReportAllActivities(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DetailReportAllScheduledActivities); - // Create the report message object of telemetry message subtype 10 for each activity Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById); report.appendUint16(static_cast<uint16_t>(scheduledActivities.size())); - for (auto& activity : scheduledActivities) { + for (auto& activity: scheduledActivities) { // todo: append sub-schedule and group ID if they are defined - report.appendUint32(activity.requestReleaseTime); + report.appendCustomCUCTimeStamp(activity.requestReleaseTime); report.appendString(MessageParser::composeECSS(activity.request)); } - storeMessage(report); // Save the report + storeMessage(report); } void TimeBasedSchedulingService::detailReportActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::DetailReportActivitiesById); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::DetailReportActivitiesById); - // Create the report message object of telemetry message subtype 10 for each activity Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById); etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities> matchedActivities; - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID const auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; }); if (requestIDMatch != scheduledActivities.end()) { - matchedActivities.push_back(*requestIDMatch); // Save the matched activity + matchedActivities.push_back(*requestIDMatch); } else { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time + sortActivitiesReleaseTime(matchedActivities); // todo: append sub-schedule and group ID if they are defined report.appendUint16(static_cast<uint16_t>(matchedActivities.size())); - for (auto& match : matchedActivities) { - report.appendUint32(match.requestReleaseTime); // todo: Replace with the time parser + for (auto& match: matchedActivities) { + report.appendCustomCUCTimeStamp(match.requestReleaseTime); // todo: Replace with the time parser report.appendString(MessageParser::composeECSS(match.request)); } - storeMessage(report); // Save the report + storeMessage(report); } void TimeBasedSchedulingService::summaryReportActivitiesByID(Message& request) { - // Check if the correct packet is being processed - assert(request.serviceType == TimeBasedSchedulingService::ServiceType); - assert(request.messageType == TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById); + request.assertTC(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::ActivitiesSummaryReportById); - // Create the report message object of telemetry message subtype 13 for each activity Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport); etl::list<ScheduledActivity, ECSSMaxNumberOfTimeSchedActivities> matchedActivities; - uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N) + uint16_t iterationCount = request.readUint16(); while (iterationCount-- != 0) { - // Parse the request ID - RequestID receivedRequestID; // Save the received request ID - receivedRequestID.sourceID = request.readUint8(); // Get the source ID - receivedRequestID.applicationID = request.readUint16(); // Get the application ID - receivedRequestID.sequenceCount = request.readUint16(); // Get the sequence count + RequestID receivedRequestID; + receivedRequestID.sourceID = request.readUint8(); + receivedRequestID.applicationID = request.readUint16(); + receivedRequestID.sequenceCount = request.readUint16(); - // Try to find the activity with the requested request ID auto requestIDMatch = etl::find_if_not(scheduledActivities.begin(), scheduledActivities.end(), [&receivedRequestID](ScheduledActivity const& currentElement) { return receivedRequestID != currentElement.requestID; @@ -246,18 +207,18 @@ void TimeBasedSchedulingService::summaryReportActivitiesByID(Message& request) { ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError); } } - sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time + sortActivitiesReleaseTime(matchedActivities); // todo: append sub-schedule and group ID if they are defined report.appendUint16(static_cast<uint16_t>(matchedActivities.size())); - for (auto& match : matchedActivities) { + for (auto& match: matchedActivities) { // todo: append sub-schedule and group ID if they are defined - report.appendUint32(match.requestReleaseTime); + report.appendCustomCUCTimeStamp(match.requestReleaseTime); report.appendUint8(match.requestID.sourceID); report.appendUint16(match.requestID.applicationID); report.appendUint16(match.requestID.sequenceCount); } - storeMessage(report); // Save the report + storeMessage(report); } void TimeBasedSchedulingService::execute(Message& message) { diff --git a/test/Message.cpp b/test/Message.cpp index 43cabf15d673b76fd040cbae7fb8281705a99750..23d2511f6f007d292c7a35176742c16a1c188fb2 100644 --- a/test/Message.cpp +++ b/test/Message.cpp @@ -166,7 +166,16 @@ TEST_CASE("Test appending double") { CHECK(message.read<double>() == Catch::Approx(2.324).epsilon(0.0001)); } -TEST_CASE("Append a CUC timestamp") { +TEST_CASE("Test appending offset") { + Message message(0, 0, Message::TC, 0); + message.append<Time::RelativeTime>(555); + + REQUIRE(message.dataSize == 8); + + CHECK(message.read<Time::RelativeTime>() == 555); +} + +TEST_CASE("Test appending a CUC timestamp") { SECTION("Test 1") { auto timeCUC = TimeGetter::getCurrentTimeCustomCUC(); REQUIRE(timeCUC.elapsed100msTicks == 86769000); @@ -188,19 +197,19 @@ TEST_CASE("Append a CUC timestamp") { } } -TEST_CASE("Read a CUC timestamp") { +TEST_CASE("Test reading a custom CUC timestamp") { /** * Append a custom CUC Time Stamp to a message object and check if is it read corretly */ - Time::CustomCUC_t timeCUC; - timeCUC.elapsed100msTicks = 34511; + Time::CustomCUC_t timeCUC; + timeCUC.elapsed100msTicks = 34511; - Message message(0, 0, Message::TC, 0); - message.appendCustomCUCTimeStamp(timeCUC); + Message message(0, 0, Message::TC, 0); + message.appendCustomCUCTimeStamp(timeCUC); - auto returnTimeCUC = message.readCustomCUCTimeStamp(); + auto returnTimeCUC = message.readCustomCUCTimeStamp(); - REQUIRE(returnTimeCUC.elapsed100msTicks == 34511); + REQUIRE(returnTimeCUC.elapsed100msTicks == 34511); } TEST_CASE("Requirement 7.3.8 (Octet-string)", "[message][ecss]") { diff --git a/test/Services/TimeBasedSchedulingService.cpp b/test/Services/TimeBasedSchedulingService.cpp index 5f74eb1cd61fdffbfbba401bdfbe79cdad151b85..55dadb1c331ad70208613b53c5bf723b24b8e1d6 100644 --- a/test/Services/TimeBasedSchedulingService.cpp +++ b/test/Services/TimeBasedSchedulingService.cpp @@ -25,7 +25,7 @@ namespace unit_test { std::transform( tmService.scheduledActivities.begin(), tmService.scheduledActivities.end(), - std::back_inserter(listElements), [](auto& activity) -> auto { return &activity; }); + std::back_inserter(listElements), [](auto& activity) -> auto{ return &activity; }); return listElements; // Return the list elements } @@ -33,8 +33,8 @@ namespace unit_test { } // namespace unit_test Message testMessage1, testMessage2, testMessage3, testMessage4; -auto currentTime = static_cast<uint32_t>(time(nullptr)); // Get the current system time -bool messagesPopulated = false; // Indicate whether the test messages are initialized +Time::CustomCUC_t currentTime = TimeGetter::getCurrentTimeCustomCUC(); // Get the current system time +bool messagesPopulated = false; // Indicate whether the test messages are initialized // Run this function to set the service up before moving on with further testing auto activityInsertion(TimeBasedSchedulingService& timeService) { @@ -69,19 +69,19 @@ auto activityInsertion(TimeBasedSchedulingService& timeService) { receivedMessage.appendUint16(4); // Total number of requests // Test activity 1 - receivedMessage.appendUint32(currentTime + 1556435); + receivedMessage.appendCustomCUCTimeStamp(currentTime + 1556435); receivedMessage.appendMessage(testMessage1, ECSSTCRequestStringSize); // Test activity 2 - receivedMessage.appendUint32(currentTime + 1957232); + receivedMessage.appendCustomCUCTimeStamp(currentTime + 1957232); receivedMessage.appendMessage(testMessage2, ECSSTCRequestStringSize); // Test activity 3 - receivedMessage.appendUint32(currentTime + 1726435); + receivedMessage.appendCustomCUCTimeStamp(currentTime + 1726435); receivedMessage.appendMessage(testMessage3, ECSSTCRequestStringSize); // Test activity 4 - receivedMessage.appendUint32(currentTime + 17248435); + receivedMessage.appendCustomCUCTimeStamp(currentTime + 17248435); receivedMessage.appendMessage(testMessage4, ECSSTCRequestStringSize); // Insert activities in the schedule. They have to be inserted sorted @@ -119,6 +119,7 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") { REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435); REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232); REQUIRE(scheduledActivities.at(3)->requestReleaseTime == currentTime + 17248435); + REQUIRE(testMessage1.bytesEqualWith(scheduledActivities.at(0)->request)); REQUIRE(testMessage3.bytesEqualWith(scheduledActivities.at(1)->request)); REQUIRE(testMessage2.bytesEqualWith(scheduledActivities.at(2)->request)); @@ -128,7 +129,7 @@ TEST_CASE("TC[11,4] Activity Insertion", "[service][st11]") { Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::InsertActivities, Message::TC, 1); receivedMessage.appendUint16(1); // Total number of requests - receivedMessage.appendUint32(currentTime - 15564350); + receivedMessage.appendCustomCUCTimeStamp(currentTime - 15564350); MessageParser::execute(receivedMessage); //timeService.insertActivities(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); @@ -140,10 +141,10 @@ TEST_CASE("TC[11,15] Time shift all scheduled activities", "[service][st11]") { Message receivedMessage(TimeBasedSchedulingService::ServiceType, TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities, Message::TC, 1); auto scheduledActivities = activityInsertion(timeBasedService); - const int32_t timeShift = 6789; + const Time::RelativeTime timeShift = 6789; SECTION("Positive Shift") { - receivedMessage.appendSint32(-timeShift); + receivedMessage.appendRelativeTime(-timeShift); CHECK(scheduledActivities.size() == 4); MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); @@ -155,11 +156,10 @@ TEST_CASE("TC[11,15] Time shift all scheduled activities", "[service][st11]") { } SECTION("Negative Shift") { - receivedMessage.appendSint32(timeShift); + receivedMessage.appendRelativeTime(timeShift); CHECK(scheduledActivities.size() == 4); MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); - REQUIRE(scheduledActivities.at(0)->requestReleaseTime == currentTime + 1556435 + timeShift); REQUIRE(scheduledActivities.at(1)->requestReleaseTime == currentTime + 1726435 + timeShift); REQUIRE(scheduledActivities.at(2)->requestReleaseTime == currentTime + 1957232 + timeShift); @@ -167,7 +167,7 @@ TEST_CASE("TC[11,15] Time shift all scheduled activities", "[service][st11]") { } SECTION("Error throwing") { - receivedMessage.appendSint32(-6789000); // Provide a huge time shift to cause an error + receivedMessage.appendRelativeTime(-6789000); // Provide a huge time shift to cause an error CHECK(scheduledActivities.size() == 4); MessageParser::execute(receivedMessage); //timeService.timeShiftAllActivities(receivedMessage); @@ -184,10 +184,10 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { scheduledActivities.at(2)->requestID.applicationID = 4; // Append a dummy application ID CHECK(scheduledActivities.size() == 4); - const int32_t timeShift = 67890000; // Relative time-shift value + const Time::RelativeTime timeShift = 67890000; // Relative time-shift value SECTION("Positive Shift") { - receivedMessage.appendSint32(timeShift); // Time-shift value + receivedMessage.appendRelativeTime(timeShift); // Time-shift value receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID @@ -202,7 +202,7 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { } SECTION("Negative Shift") { - receivedMessage.appendSint32(-250000); // Time-shift value + receivedMessage.appendRelativeTime(-250000); // Time-shift value receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID @@ -217,18 +217,18 @@ TEST_CASE("TC[11,7] Time shift activities by ID", "[service][st11]") { } SECTION("Error throw on wrong request ID") { - receivedMessage.appendSint32(-250000); // Time-shift value - receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity - receivedMessage.appendUint8(0); // Dummy source ID - receivedMessage.appendUint16(80); // Dummy application ID to throw an error - receivedMessage.appendUint16(0); // Dummy sequence count + receivedMessage.appendRelativeTime(-250000); // Time-shift value + receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity + receivedMessage.appendUint8(0); // Dummy source ID + receivedMessage.appendUint16(80); // Dummy application ID to throw an error + receivedMessage.appendUint16(0); // Dummy sequence count timeBasedService.timeShiftActivitiesByID(receivedMessage); REQUIRE(ServiceTests::thrownError(ErrorHandler::InstructionExecutionStartError)); } SECTION("Error throw on wrong time offset") { - receivedMessage.appendSint32(-6789000); // Time-shift value + receivedMessage.appendRelativeTime(-6789000); // Time-shift value receivedMessage.appendUint16(1); // Just one instruction to time-shift an activity receivedMessage.appendUint8(0); // Source ID is not implemented receivedMessage.appendUint16(testMessage2.applicationId); // todo: Remove the dummy app ID @@ -270,13 +270,12 @@ TEST_CASE("TC[11,9] Detail report scheduled activities by ID", "[service][st11]" uint16_t iterationCount = response.readUint16(); CHECK(iterationCount == 2); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::CustomCUC_t receivedReleaseTime = response.readCustomCUCTimeStamp(); Message receivedTCPacket; uint8_t receivedDataStr[ECSSTCRequestStringSize]; response.readString(receivedDataStr, ECSSTCRequestStringSize); receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); - if (i == 0) { REQUIRE(receivedReleaseTime == scheduledActivities.at(0)->requestReleaseTime); REQUIRE(receivedTCPacket == scheduledActivities.at(0)->request); @@ -328,7 +327,7 @@ TEST_CASE("TC[11,12] Summary report scheduled activities by ID", "[service][st11 uint16_t iterationCount = response.readUint16(); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::CustomCUC_t receivedReleaseTime = response.readCustomCUCTimeStamp(); uint8_t receivedSourceID = response.readUint8(); uint16_t receivedApplicationID = response.readUint16(); uint16_t receivedSequenceCount = response.readUint16(); @@ -374,13 +373,12 @@ TEST_CASE("TC[11,16] Detail report all scheduled activities", "[service][st11]") REQUIRE(iterationCount == scheduledActivities.size()); for (uint16_t i = 0; i < iterationCount; i++) { - uint32_t receivedReleaseTime = response.readUint32(); + Time::CustomCUC_t receivedReleaseTime = response.readCustomCUCTimeStamp(); Message receivedTCPacket; uint8_t receivedDataStr[ECSSTCRequestStringSize]; response.readString(receivedDataStr, ECSSTCRequestStringSize); receivedTCPacket = MessageParser::parseECSSTC(receivedDataStr); - REQUIRE(receivedReleaseTime == scheduledActivities.at(i)->requestReleaseTime); REQUIRE(receivedTCPacket.bytesEqualWith(scheduledActivities.at(i)->request)); }