Skip to content
Snippets Groups Projects
TimeBasedSchedulingService.cpp 13.63 KiB
#include "ECSS_Configuration.hpp"
#ifdef SERVICE_TIMESCHEDULING

#include "Services/TimeBasedSchedulingService.hpp"

TimeBasedSchedulingService::TimeBasedSchedulingService() {
	serviceType = TimeBasedSchedulingService::ServiceType;
}

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
}

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
}

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
	// 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);

	// todo: Get the sub-schedule ID if they are implemented
	uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N)
	while (iterationCount-- != 0) {
		// todo: Get the group ID first, if groups are used
		uint32_t currentTime = TimeGetter::getSeconds(); // Get the current system time

		uint32_t releaseTime = request.readUint32(); // Get the specified release time
		if ((scheduledActivities.available() == 0) || (releaseTime < (currentTime + ECSS_TIME_MARGIN_FOR_ACTIVATION))) {
			ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
			request.skipBytes(ECSS_TC_REQUEST_STRING_SIZE);
		} else {
			// Get the TC packet request
			uint8_t requestData[ECSS_TC_REQUEST_STRING_SIZE] = {0};
			request.readString(requestData, ECSS_TC_REQUEST_STRING_SIZE);
			Message receivedTCPacket = MessageParser::parseECSSTC(requestData);
			ScheduledActivity newActivity; // Create the new activity

			// Assign the attributes to the newly created activity
			newActivity.request = receivedTCPacket;
			newActivity.requestReleaseTime = releaseTime;

			// todo: When implemented save the source ID
			newActivity.requestID.applicationID = request.applicationId;
			newActivity.requestID.sequenceCount = request.packetSequenceCount;

			scheduledActivities.push_back(newActivity); // Insert the new activities
		}
	}
	sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time
}

void TimeBasedSchedulingService::timeShiftAllActivities(Message& request) {
	// Check if the correct packet is being processed
	assert(request.serviceType == TimeBasedSchedulingService::ServiceType);
	assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftALlScheduledActivities);

	uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time

	// 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
	if ((releaseTimes.first->requestReleaseTime + relativeOffset) < (current_time + ECSS_TIME_MARGIN_FOR_ACTIVATION)) {
		// Report the error
		ErrorHandler::reportError(request, ErrorHandler::SubServiceExecutionStartError);
	} else {
		for (auto& activity : scheduledActivities) {
			activity.requestReleaseTime += relativeOffset; // Time shift each activity
		}
	}
}

void TimeBasedSchedulingService::timeShiftActivitiesByID(Message& request) {
	// Check if the correct packet is being processed
	assert(request.serviceType == TimeBasedSchedulingService::ServiceType);
	assert(request.messageType == TimeBasedSchedulingService::MessageType::TimeShiftActivitiesById);

	uint32_t current_time = TimeGetter::getSeconds(); // Get the current system time

	int32_t relativeOffset = request.readSint32(); // Get the offset first
	uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N)
	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

		// 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 + ECSS_TIME_MARGIN_FOR_ACTIVATION)) {
				ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
			} else {
				requestIDMatch->requestReleaseTime += relativeOffset; // Add the time offset
			}
		} else {
			ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
		}
	}
	sortActivitiesReleaseTime(scheduledActivities); // Sort activities by their release time
}

void TimeBasedSchedulingService::deleteActivitiesByID(Message& request) {
	// Check if the correct packet is being processed
	assert(request.serviceType == TimeBasedSchedulingService::ServiceType);
	assert(request.messageType == TimeBasedSchedulingService::MessageType::DeleteActivitiesById);

	uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N)
	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

		// 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
		} else {
			ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
		}
	}
}

void TimeBasedSchedulingService::detailReportAllActivities(Message& request) {
	// Check if the correct packet is being processed
	assert(request.serviceType == TimeBasedSchedulingService::ServiceType);
	assert(request.messageType == 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) {
		// todo: append sub-schedule and group ID if they are defined

		report.appendUint32(activity.requestReleaseTime);
		report.appendString(MessageParser::composeECSS(activity.request));
	}
	storeMessage(report); // Save the 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);

	// Create the report message object of telemetry message subtype 10 for each activity
	Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduleReportById);
	etl::list<ScheduledActivity, ECSS_MAX_NUMBER_OF_TIME_SCHED_ACTIVITIES> matchedActivities;

	uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N)
	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

		// 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
		} else {
			ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
		}
	}

	sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time
	// 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
		report.appendString(MessageParser::composeECSS(match.request));
	}
	storeMessage(report); // Save the 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);

	// Create the report message object of telemetry message subtype 13 for each activity
	Message report = createTM(TimeBasedSchedulingService::MessageType::TimeBasedScheduledSummaryReport);
	etl::list<ScheduledActivity, ECSS_MAX_NUMBER_OF_TIME_SCHED_ACTIVITIES> matchedActivities;

	uint16_t iterationCount = request.readUint16(); // Get the iteration count, (N)
	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

		// 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()) {
			matchedActivities.push_back(*requestIDMatch);
		} else {
			ErrorHandler::reportError(request, ErrorHandler::InstructionExecutionStartError);
		}
	}
	sortActivitiesReleaseTime(matchedActivities); // Sort activities by their release time

	// todo: append sub-schedule and group ID if they are defined
	report.appendUint16(static_cast<uint16_t>(matchedActivities.size()));
	for (auto& match : matchedActivities) {
		// todo: append sub-schedule and group ID if they are defined
		report.appendUint32(match.requestReleaseTime);
		report.appendUint8(match.requestID.sourceID);
		report.appendUint16(match.requestID.applicationID);
		report.appendUint16(match.requestID.sequenceCount);
	}
	storeMessage(report); // Save the report
}

void TimeBasedSchedulingService::execute(Message& message) {
	switch (message.messageType) {
		case 1:
			enableScheduleExecution(message); // TC[11,1]
			break;
		case 2:
			disableScheduleExecution(message); // TC[11,2]
			break;
		case 3:
			resetSchedule(message); // TC[11,3]
			break;
		case 4:
			insertActivities(message); // TC[11,4]
			break;
		case 5:
			deleteActivitiesByID(message); // TC[11,5]
			break;
		case 7:
			timeShiftActivitiesByID(message); // TC[11,7]
			break;
		case 9:
			detailReportActivitiesByID(message); // TC[11,9]
			break;
		case 12:
			summaryReportActivitiesByID(message); // TC[11,12]
			break;
		case 15:
			timeShiftAllActivities(message); // TC[11,15]
			break;
		case 16:
			detailReportAllActivities(message); // TC[11,16]
			break;
		default:
			ErrorHandler::reportInternalError(ErrorHandler::OtherMessageType);
	}
}

#endif