diff --git a/osal/host/Clock.cpp b/osal/host/Clock.cpp new file mode 100644 index 000000000..41321eeb5 --- /dev/null +++ b/osal/host/Clock.cpp @@ -0,0 +1,227 @@ +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../timemanager/Clock.h" + +#include +#if defined(WIN32) +#include +#elif defined(LINUX) +#include +#endif + +uint16_t Clock::leapSeconds = 0; +MutexIF* Clock::timeMutex = NULL; + +using SystemClock = std::chrono::system_clock; + +uint32_t Clock::getTicksPerSecond(void){ + sif::warning << "Clock::getTicksPerSecond: not implemented yet" << std::endl; + return 0; + //return CLOCKS_PER_SEC; + //uint32_t ticks = sysconf(_SC_CLK_TCK); + //return ticks; +} + +ReturnValue_t Clock::setClock(const TimeOfDay_t* time) { + // do some magic with chrono + sif::warning << "Clock::setClock: not implemented yet" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::setClock(const timeval* time) { + // do some magic with chrono +#if defined(WIN32) + return HasReturnvaluesIF::RETURN_OK; +#elif defined(LINUX) + return HasReturnvaluesIF::RETURN_OK; +#else + +#endif + sif::warning << "Clock::getUptime: Not implemented for found OS" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +} + +ReturnValue_t Clock::getClock_timeval(timeval* time) { +#if defined(WIN32) + auto now = std::chrono::system_clock::now(); + auto secondsChrono = std::chrono::time_point_cast(now); + auto epoch = now.time_since_epoch(); + time->tv_sec = std::chrono::duration_cast(epoch).count(); + auto fraction = now - secondsChrono; + time->tv_usec = std::chrono::duration_cast( + fraction).count(); + return HasReturnvaluesIF::RETURN_OK; +#elif defined(LINUX) + timespec timeUnix; + int status = clock_gettime(CLOCK_REALTIME,&timeUnix); + if(status!=0){ + return HasReturnvaluesIF::RETURN_FAILED; + } + time->tv_sec = timeUnix.tv_sec; + time->tv_usec = timeUnix.tv_nsec / 1000.0; + return HasReturnvaluesIF::RETURN_OK; +#else + sif::warning << "Clock::getUptime: Not implemented for found OS" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +#endif + +} + +ReturnValue_t Clock::getClock_usecs(uint64_t* time) { + // do some magic with chrono + sif::warning << "Clock::gerClock_usecs: not implemented yet" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +} + +timeval Clock::getUptime() { + timeval timeval; +#if defined(WIN32) + auto uptime = std::chrono::milliseconds(GetTickCount64()); + auto secondsChrono = std::chrono::duration_cast(uptime); + timeval.tv_sec = secondsChrono.count(); + auto fraction = uptime - secondsChrono; + timeval.tv_usec = std::chrono::duration_cast( + fraction).count(); +#elif defined(LINUX) + double uptimeSeconds; + if (std::ifstream("/proc/uptime", std::ios::in) >> uptimeSeconds) + { + // value is rounded down automatically + timeval.tv_sec = uptimeSeconds; + timeval.tv_usec = uptimeSeconds *(double) 1e6 - (timeval.tv_sec *1e6); + } +#else + sif::warning << "Clock::getUptime: Not implemented for found OS" << std::endl; +#endif + return timeval; +} + +ReturnValue_t Clock::getUptime(timeval* uptime) { + *uptime = getUptime(); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::getUptime(uint32_t* uptimeMs) { + timeval uptime = getUptime(); + *uptimeMs = uptime.tv_sec * 1000 + uptime.tv_usec / 1000; + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t Clock::getDateAndTime(TimeOfDay_t* time) { + // do some magic with chrono (C++20!) + // Right now, the library doesn't have the new features yet. + // so we work around that for now. + auto now = SystemClock::now(); + auto seconds = std::chrono::time_point_cast(now); + auto fraction = now - seconds; + time_t tt = SystemClock::to_time_t(now); + struct tm* timeInfo; + timeInfo = gmtime(&tt); + time->year = timeInfo->tm_year + 1900; + time->month = timeInfo->tm_mon+1; + time->day = timeInfo->tm_mday; + time->hour = timeInfo->tm_hour; + time->minute = timeInfo->tm_min; + time->second = timeInfo->tm_sec; + auto usecond = std::chrono::duration_cast(fraction); + time->usecond = usecond.count(); + + //sif::warning << "Clock::getDateAndTime: not implemented yet" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::convertTimeOfDayToTimeval(const TimeOfDay_t* from, + timeval* to) { + struct tm time_tm; + + time_tm.tm_year = from->year - 1900; + time_tm.tm_mon = from->month - 1; + time_tm.tm_mday = from->day; + + time_tm.tm_hour = from->hour; + time_tm.tm_min = from->minute; + time_tm.tm_sec = from->second; + + time_t seconds = mktime(&time_tm); + + to->tv_sec = seconds; + to->tv_usec = from->usecond; + //Fails in 2038.. + return HasReturnvaluesIF::RETURN_OK; + sif::warning << "Clock::convertTimeBla: not implemented yet" << std::endl; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::convertTimevalToJD2000(timeval time, double* JD2000) { + *JD2000 = (time.tv_sec - 946728000. + time.tv_usec / 1000000.) / 24. + / 3600.; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::convertUTCToTT(timeval utc, timeval* tt) { + //SHOULDDO: works not for dates in the past (might have less leap seconds) + if (timeMutex == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + uint16_t leapSeconds; + ReturnValue_t result = getLeapSeconds(&leapSeconds); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + timeval leapSeconds_timeval = { 0, 0 }; + leapSeconds_timeval.tv_sec = leapSeconds; + + //initial offset between UTC and TAI + timeval UTCtoTAI1972 = { 10, 0 }; + + timeval TAItoTT = { 32, 184000 }; + + *tt = utc + leapSeconds_timeval + UTCtoTAI1972 + TAItoTT; + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t Clock::setLeapSeconds(const uint16_t leapSeconds_) { + if(checkOrCreateClockMutex()!=HasReturnvaluesIF::RETURN_OK){ + return HasReturnvaluesIF::RETURN_FAILED; + } + ReturnValue_t result = timeMutex->lockMutex(MutexIF::BLOCKING); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + leapSeconds = leapSeconds_; + + result = timeMutex->unlockMutex(); + return result; +} + +ReturnValue_t Clock::getLeapSeconds(uint16_t* leapSeconds_) { + if(timeMutex == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } + ReturnValue_t result = timeMutex->lockMutex(MutexIF::BLOCKING); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + *leapSeconds_ = leapSeconds; + + result = timeMutex->unlockMutex(); + return result; +} + +ReturnValue_t Clock::checkOrCreateClockMutex(){ + if(timeMutex == nullptr){ + MutexFactory* mutexFactory = MutexFactory::instance(); + if (mutexFactory == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + timeMutex = mutexFactory->createMutex(); + if (timeMutex == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + } + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/osal/host/FixedTimeslotTask.cpp b/osal/host/FixedTimeslotTask.cpp new file mode 100644 index 000000000..e78c974a4 --- /dev/null +++ b/osal/host/FixedTimeslotTask.cpp @@ -0,0 +1,197 @@ +#include "../../osal/host/FixedTimeslotTask.h" + +#include "../../ipc/MutexFactory.h" +#include "../../osal/host/Mutex.h" +#include "../../osal/host/FixedTimeslotTask.h" + +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../tasks/ExecutableObjectIF.h" + +#include +#include + +#if defined(WIN32) +#include +#elif defined(LINUX) +#include +#endif + +FixedTimeslotTask::FixedTimeslotTask(const char *name, TaskPriority setPriority, + TaskStackSize setStack, TaskPeriod setPeriod, + void (*setDeadlineMissedFunc)()) : + started(false), pollingSeqTable(setPeriod*1000), taskName(name), + period(setPeriod), deadlineMissedFunc(setDeadlineMissedFunc) { + // It is propably possible to set task priorities by using the native + // task handles for Windows / Linux + mainThread = std::thread(&FixedTimeslotTask::taskEntryPoint, this, this); +#if defined(WIN32) + /* List of possible priority classes: + * https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ + * nf-processthreadsapi-setpriorityclass + * And respective thread priority numbers: + * https://docs.microsoft.com/en-us/windows/ + * win32/procthread/scheduling-priorities */ + int result = SetPriorityClass( + reinterpret_cast(mainThread.native_handle()), + ABOVE_NORMAL_PRIORITY_CLASS); + if(result != 0) { + sif::error << "FixedTimeslotTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + } + result = SetThreadPriority( + reinterpret_cast(mainThread.native_handle()), + THREAD_PRIORITY_NORMAL); + if(result != 0) { + sif::error << "FixedTimeslotTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + } +#elif defined(LINUX) + // we can just copy and paste the code from linux here. +#endif +} + +FixedTimeslotTask::~FixedTimeslotTask(void) { + //Do not delete objects, we were responsible for ptrs only. + terminateThread = true; + if(mainThread.joinable()) { + mainThread.join(); + } + delete this; +} + +void FixedTimeslotTask::taskEntryPoint(void* argument) { + FixedTimeslotTask *originalTask(reinterpret_cast(argument)); + + if (not originalTask->started) { + // we have to suspend/block here until the task is started. + // if semaphores are implemented, use them here. + std::unique_lock lock(initMutex); + initCondition.wait(lock); + } + + this->taskFunctionality(); + sif::debug << "FixedTimeslotTask::taskEntryPoint: " + "Returned from taskFunctionality." << std::endl; +} + +ReturnValue_t FixedTimeslotTask::startTask() { + started = true; + + // Notify task to start. + std::lock_guard lock(initMutex); + initCondition.notify_one(); + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t FixedTimeslotTask::sleepFor(uint32_t ms) { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + return HasReturnvaluesIF::RETURN_OK; +} + +void FixedTimeslotTask::taskFunctionality() { + pollingSeqTable.intializeSequenceAfterTaskCreation(); + + // A local iterator for the Polling Sequence Table is created to + // find the start time for the first entry. + auto slotListIter = pollingSeqTable.current; + + // Get start time for first entry. + chron_ms interval(slotListIter->pollingTimeMs); + auto currentStartTime { + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + }; + if(interval.count() > 0) { + delayForInterval(¤tStartTime, interval); + } + + /* Enter the loop that defines the task behavior. */ + for (;;) { + if(terminateThread.load()) { + break; + } + //The component for this slot is executed and the next one is chosen. + this->pollingSeqTable.executeAndAdvance(); + if (not pollingSeqTable.slotFollowsImmediately()) { + // we need to wait before executing the current slot + //this gives us the time to wait: + interval = chron_ms(this->pollingSeqTable.getIntervalToPreviousSlotMs()); + delayForInterval(¤tStartTime, interval); + //TODO deadline missed check + } + } +} + +ReturnValue_t FixedTimeslotTask::addSlot(object_id_t componentId, + uint32_t slotTimeMs, int8_t executionStep) { + ExecutableObjectIF* executableObject = objectManager-> + get(componentId); + if (executableObject != nullptr) { + pollingSeqTable.addSlot(componentId, slotTimeMs, executionStep, + executableObject, this); + return HasReturnvaluesIF::RETURN_OK; + } + + sif::error << "Component " << std::hex << componentId << + " not found, not adding it to pst" << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; +} + +ReturnValue_t FixedTimeslotTask::checkSequence() const { + return pollingSeqTable.checkSequence(); +} + +uint32_t FixedTimeslotTask::getPeriodMs() const { + return period * 1000; +} + +bool FixedTimeslotTask::delayForInterval(chron_ms * previousWakeTimeMs, + const chron_ms interval) { + bool shouldDelay = false; + //Get current wakeup time + auto currentStartTime = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + /* Generate the tick time at which the task wants to wake. */ + auto nextTimeToWake_ms = (*previousWakeTimeMs) + interval; + + if (currentStartTime < *previousWakeTimeMs) { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if ((nextTimeToWake_ms < *previousWakeTimeMs) + && (nextTimeToWake_ms > currentStartTime)) { + shouldDelay = true; + } + } else { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if ((nextTimeToWake_ms < *previousWakeTimeMs) + || (nextTimeToWake_ms > currentStartTime)) { + shouldDelay = true; + } + } + + /* Update the wake time ready for the next call. */ + + (*previousWakeTimeMs) = nextTimeToWake_ms; + + if (shouldDelay) { + auto sleepTime = std::chrono::duration_cast( + nextTimeToWake_ms - currentStartTime); + std::this_thread::sleep_for(sleepTime); + return true; + } + //We are shifting the time in case the deadline was missed like rtems + (*previousWakeTimeMs) = currentStartTime; + return false; + +} + + + + diff --git a/osal/host/FixedTimeslotTask.h b/osal/host/FixedTimeslotTask.h new file mode 100644 index 000000000..9985e2ee6 --- /dev/null +++ b/osal/host/FixedTimeslotTask.h @@ -0,0 +1,130 @@ +#ifndef FRAMEWORK_OSAL_HOST_FIXEDTIMESLOTTASK_H_ +#define FRAMEWORK_OSAL_HOST_FIXEDTIMESLOTTASK_H_ + +#include "../../objectmanager/ObjectManagerIF.h" +#include "../../tasks/FixedSlotSequence.h" +#include "../../tasks/FixedTimeslotTaskIF.h" +#include "../../tasks/Typedef.h" + +#include +#include +#include +#include + +class ExecutableObjectIF; + +/** + * @brief This class represents a task for periodic activities with multiple + * steps and strict timeslot requirements for these steps. + * @details + * @ingroup task_handling + */ +class FixedTimeslotTask: public FixedTimeslotTaskIF { +public: + /** + * @brief Standard constructor of the class. + * @details + * The class is initialized without allocated objects. These need to be + * added with #addComponent. + * @param priority + * @param stack_size + * @param setPeriod + * @param setDeadlineMissedFunc + * The function pointer to the deadline missed function that shall be + * assigned. + */ + FixedTimeslotTask(const char *name, TaskPriority setPriority, + TaskStackSize setStack, TaskPeriod setPeriod, + void (*setDeadlineMissedFunc)()); + /** + * @brief Currently, the executed object's lifetime is not coupled with + * the task object's lifetime, so the destructor is empty. + */ + virtual ~FixedTimeslotTask(void); + + /** + * @brief The method to start the task. + * @details The method starts the task with the respective system call. + * Entry point is the taskEntryPoint method described below. + * The address of the task object is passed as an argument + * to the system call. + */ + ReturnValue_t startTask(void); + + /** + * Add timeslot to the polling sequence table. + * @param componentId + * @param slotTimeMs + * @param executionStep + * @return + */ + ReturnValue_t addSlot(object_id_t componentId, + uint32_t slotTimeMs, int8_t executionStep); + + ReturnValue_t checkSequence() const override; + + uint32_t getPeriodMs() const; + + ReturnValue_t sleepFor(uint32_t ms); + +protected: + using chron_ms = std::chrono::milliseconds; + + bool started; + //!< Typedef for the List of objects. + typedef std::vector ObjectList; + std::thread mainThread; + std::atomic terminateThread = false; + + //! Polling sequence table which contains the object to execute + //! and information like the timeslots and the passed execution step. + FixedSlotSequence pollingSeqTable; + + std::condition_variable initCondition; + std::mutex initMutex; + std::string taskName; + /** + * @brief The period of the task. + * @details + * The period determines the frequency of the task's execution. + * It is expressed in clock ticks. + */ + TaskPeriod period; + + /** + * @brief The pointer to the deadline-missed function. + * @details + * This pointer stores the function that is executed if the task's deadline + * is missed. So, each may react individually on a timing failure. + * The pointer may be NULL, then nothing happens on missing the deadline. + * The deadline is equal to the next execution of the periodic task. + */ + void (*deadlineMissedFunc)(void); + /** + * @brief This is the function executed in the new task's context. + * @details + * It converts the argument back to the thread object type and copies the + * class instance to the task context. + * The taskFunctionality method is called afterwards. + * @param A pointer to the task object itself is passed as argument. + */ + + void taskEntryPoint(void* argument); + /** + * @brief The function containing the actual functionality of the task. + * @details + * The method sets and starts the task's period, then enters a loop that is + * repeated as long as the isRunning attribute is true. Within the loop, + * all performOperation methods of the added objects are called. Afterwards + * the checkAndRestartPeriod system call blocks the task until the next + * period. On missing the deadline, the deadlineMissedFunction is executed. + */ + void taskFunctionality(void); + + bool delayForInterval(chron_ms * previousWakeTimeMs, + const chron_ms interval); +}; + + + +#endif /* FRAMEWORK_OSAL_HOST_FIXEDTIMESLOTTASK_H_ */ diff --git a/osal/host/MessageQueue.cpp b/osal/host/MessageQueue.cpp new file mode 100644 index 000000000..bced37139 --- /dev/null +++ b/osal/host/MessageQueue.cpp @@ -0,0 +1,159 @@ +#include "MessageQueue.h" +#include "QueueMapManager.h" + +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../ipc/MutexFactory.h" +#include "../../ipc/MutexHelper.h" + +MessageQueue::MessageQueue(size_t messageDepth, size_t maxMessageSize): + messageSize(maxMessageSize), messageDepth(messageDepth) { + queueLock = MutexFactory::instance()->createMutex(); + auto result = QueueMapManager::instance()->addMessageQueue(this, &mqId); + if(result != HasReturnvaluesIF::RETURN_OK) { + sif::error << "MessageQueue::MessageQueue:" + << " Could not be created" << std::endl; + } +} + +MessageQueue::~MessageQueue() { + MutexFactory::instance()->deleteMutex(queueLock); +} + +ReturnValue_t MessageQueue::sendMessage(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, bool ignoreFault) { + return sendMessageFrom(sendTo, message, this->getId(), ignoreFault); +} + +ReturnValue_t MessageQueue::sendToDefault(MessageQueueMessageIF* message) { + return sendToDefaultFrom(message, this->getId()); +} + +ReturnValue_t MessageQueue::sendToDefaultFrom(MessageQueueMessageIF* message, + MessageQueueId_t sentFrom, bool ignoreFault) { + return sendMessageFrom(defaultDestination,message,sentFrom,ignoreFault); +} + +ReturnValue_t MessageQueue::reply(MessageQueueMessageIF* message) { + if (this->lastPartner != 0) { + return sendMessageFrom(this->lastPartner, message, this->getId()); + } else { + return MessageQueueIF::NO_REPLY_PARTNER; + } +} + +ReturnValue_t MessageQueue::sendMessageFrom(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return sendMessageFromMessageQueue(sendTo, message, sentFrom, + ignoreFault); +} + +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message, + MessageQueueId_t* receivedFrom) { + ReturnValue_t status = this->receiveMessage(message); + if(status == HasReturnvaluesIF::RETURN_OK) { + *receivedFrom = this->lastPartner; + } + return status; +} + +ReturnValue_t MessageQueue::receiveMessage(MessageQueueMessageIF* message) { + if(messageQueue.empty()) { + return MessageQueueIF::EMPTY; + } + // not sure this will work.. + //*message = std::move(messageQueue.front()); + MutexHelper mutexLock(queueLock, MutexIF::TimeoutType::WAITING, 20); + MessageQueueMessage* currentMessage = &messageQueue.front(); + std::copy(currentMessage->getBuffer(), + currentMessage->getBuffer() + messageSize, message->getBuffer()); + messageQueue.pop(); + // The last partner is the first uint32_t field in the message + this->lastPartner = message->getSender(); + return HasReturnvaluesIF::RETURN_OK; +} + +MessageQueueId_t MessageQueue::getLastPartner() const { + return lastPartner; +} + +ReturnValue_t MessageQueue::flush(uint32_t* count) { + *count = messageQueue.size(); + // Clears the queue. + messageQueue = std::queue(); + return HasReturnvaluesIF::RETURN_OK; +} + +MessageQueueId_t MessageQueue::getId() const { + return mqId; +} + +void MessageQueue::setDefaultDestination(MessageQueueId_t defaultDestination) { + defaultDestinationSet = true; + this->defaultDestination = defaultDestination; +} + +MessageQueueId_t MessageQueue::getDefaultDestination() const { + return defaultDestination; +} + +bool MessageQueue::isDefaultDestinationSet() const { + return defaultDestinationSet; +} + + +// static core function to send messages. +ReturnValue_t MessageQueue::sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + if(message->getMessageSize() > message->getMaximumMessageSize()) { + // Actually, this should never happen or an error will be emitted + // in MessageQueueMessage. + // But I will still return a failure here. + return HasReturnvaluesIF::RETURN_FAILED; + } + MessageQueue* targetQueue = dynamic_cast( + QueueMapManager::instance()->getMessageQueue(sendTo)); + if(targetQueue == nullptr) { + if(not ignoreFault) { + InternalErrorReporterIF* internalErrorReporter = + objectManager->get( + objects::INTERNAL_ERROR_REPORTER); + if (internalErrorReporter != nullptr) { + internalErrorReporter->queueMessageNotSent(); + } + } + // TODO: Better returnvalue + return HasReturnvaluesIF::RETURN_FAILED; + } + + if(targetQueue->messageQueue.size() < targetQueue->messageDepth) { + MutexHelper mutexLock(targetQueue->queueLock, + MutexIF::TimeoutType::WAITING, 20); + // not ideal, works for now though. + MessageQueueMessage* mqmMessage = + dynamic_cast(message); + if(message != nullptr) { + targetQueue->messageQueue.push(*mqmMessage); + } + else { + sif::error << "MessageQueue::sendMessageFromMessageQueue: Message" + "is not MessageQueueMessage!" << std::endl; + } + + } + else { + return MessageQueueIF::FULL; + } + message->setSender(sentFrom); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t MessageQueue::lockQueue(MutexIF::TimeoutType timeoutType, + dur_millis_t lockTimeout) { + return queueLock->lockMutex(timeoutType, lockTimeout); +} + +ReturnValue_t MessageQueue::unlockQueue() { + return queueLock->unlockMutex(); +} diff --git a/osal/host/MessageQueue.h b/osal/host/MessageQueue.h new file mode 100644 index 000000000..97a9e4915 --- /dev/null +++ b/osal/host/MessageQueue.h @@ -0,0 +1,231 @@ +#ifndef FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ +#define FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ + +#include "../../internalError/InternalErrorReporterIF.h" +#include "../../ipc/MessageQueueIF.h" +#include "../../ipc/MessageQueueMessage.h" +#include "../../ipc/MutexIF.h" +#include "../../timemanager/Clock.h" + +#include +#include + +/** + * @brief This class manages sending and receiving of + * message queue messages. + * @details + * Message queues are used to pass asynchronous messages between processes. + * They work like post boxes, where all incoming messages are stored in FIFO + * order. This class creates a new receiving queue and provides methods to fetch + * received messages. Being a child of MessageQueueSender, this class also + * provides methods to send a message to a user-defined or a default destination. + * In addition it also provides a reply method to answer to the queue it + * received its last message from. + * + * The MessageQueue should be used as "post box" for a single owning object. + * So all message queue communication is "n-to-one". + * For creating the queue, as well as sending and receiving messages, the class + * makes use of the operating system calls provided. + * + * Please keep in mind that FreeRTOS offers different calls for message queue + * operations if called from an ISR. + * For now, the system context needs to be switched manually. + * @ingroup osal + * @ingroup message_queue + */ +class MessageQueue : public MessageQueueIF { + friend class MessageQueueSenderIF; +public: + /** + * @brief The constructor initializes and configures the message queue. + * @details + * By making use of the according operating system call, a message queue is + * created and initialized. The message depth - the maximum number of + * messages to be buffered - may be set with the help of a parameter, + * whereas the message size is automatically set to the maximum message + * queue message size. The operating system sets the message queue id, or + * in case of failure, it is set to zero. + * @param message_depth + * The number of messages to be buffered before passing an error to the + * sender. Default is three. + * @param max_message_size + * With this parameter, the maximum message size can be adjusted. + * This should be left default. + */ + MessageQueue(size_t messageDepth = 3, + size_t maxMessageSize = MessageQueueMessage::MAX_MESSAGE_SIZE); + + /** Copying message queues forbidden */ + MessageQueue(const MessageQueue&) = delete; + MessageQueue& operator=(const MessageQueue&) = delete; + + /** + * @brief The destructor deletes the formerly created message queue. + * @details This is accomplished by using the delete call provided + * by the operating system. + */ + virtual ~MessageQueue(); + + /** + * @brief This operation sends a message to the given destination. + * @details It directly uses the sendMessage call of the MessageQueueSender + * parent, but passes its queue id as "sentFrom" parameter. + * @param sendTo This parameter specifies the message queue id of the + * destination message queue. + * @param message A pointer to a previously created message, which is sent. + * @param ignoreFault If set to true, the internal software fault counter + * is not incremented if queue is full. + */ + ReturnValue_t sendMessage(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, bool ignoreFault = false) override; + /** + * @brief This operation sends a message to the default destination. + * @details As in the sendMessage method, this function uses the + * sendToDefault call of the MessageQueueSender parent class and adds its + * queue id as "sentFrom" information. + * @param message A pointer to a previously created message, which is sent. + */ + ReturnValue_t sendToDefault(MessageQueueMessageIF* message) override; + /** + * @brief This operation sends a message to the last communication partner. + * @details This operation simplifies answering an incoming message by using + * the stored lastPartner information as destination. If there was no + * message received yet (i.e. lastPartner is zero), an error code is returned. + * @param message A pointer to a previously created message, which is sent. + */ + ReturnValue_t reply(MessageQueueMessageIF* message) override; + + /** + * @brief With the sendMessage call, a queue message is sent to a + * receiving queue. + * @details + * This method takes the message provided, adds the sentFrom information and + * passes it on to the destination provided with an operating system call. + * The OS's return value is returned. + * @param sendTo This parameter specifies the message queue id to send + * the message to. + * @param message This is a pointer to a previously created message, + * which is sent. + * @param sentFrom The sentFrom information can be set to inject the + * sender's queue id into the message. This variable is set to zero by + * default. + * @param ignoreFault If set to true, the internal software fault counter + * is not incremented if queue is full. + */ + virtual ReturnValue_t sendMessageFrom( MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault = false) override; + + /** + * @brief The sendToDefault method sends a queue message to the default + * destination. + * @details + * In all other aspects, it works identical to the sendMessage method. + * @param message This is a pointer to a previously created message, + * which is sent. + * @param sentFrom The sentFrom information can be set to inject the + * sender's queue id into the message. This variable is set to zero by + * default. + */ + virtual ReturnValue_t sendToDefaultFrom( MessageQueueMessageIF* message, + MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault = false) override; + + /** + * @brief This function reads available messages from the message queue + * and returns the sender. + * @details + * It works identically to the other receiveMessage call, but in addition + * returns the sender's queue id. + * @param message A pointer to a message in which the received data is stored. + * @param receivedFrom A pointer to a queue id in which the sender's id is stored. + */ + ReturnValue_t receiveMessage(MessageQueueMessageIF* message, + MessageQueueId_t *receivedFrom) override; + + /** + * @brief This function reads available messages from the message queue. + * @details + * If data is available it is stored in the passed message pointer. + * The message's original content is overwritten and the sendFrom + * information is stored in the lastPartner attribute. Else, the lastPartner + * information remains untouched, the message's content is cleared and the + * function returns immediately. + * @param message A pointer to a message in which the received data is stored. + */ + ReturnValue_t receiveMessage(MessageQueueMessageIF* message) override; + /** + * Deletes all pending messages in the queue. + * @param count The number of flushed messages. + * @return RETURN_OK on success. + */ + ReturnValue_t flush(uint32_t* count) override; + /** + * @brief This method returns the message queue id of the last + * communication partner. + */ + MessageQueueId_t getLastPartner() const override; + /** + * @brief This method returns the message queue id of this class's + * message queue. + */ + MessageQueueId_t getId() const override; + + /** + * @brief This method is a simple setter for the default destination. + */ + void setDefaultDestination(MessageQueueId_t defaultDestination) override; + /** + * @brief This method is a simple getter for the default destination. + */ + MessageQueueId_t getDefaultDestination() const override; + + bool isDefaultDestinationSet() const override; + + ReturnValue_t lockQueue(MutexIF::TimeoutType timeoutType, + dur_millis_t lockTimeout); + ReturnValue_t unlockQueue(); +protected: + /** + * @brief Implementation to be called from any send Call within + * MessageQueue and MessageQueueSenderIF. + * @details + * This method takes the message provided, adds the sentFrom information and + * passes it on to the destination provided with an operating system call. + * The OS's return value is returned. + * @param sendTo + * This parameter specifies the message queue id to send the message to. + * @param message + * This is a pointer to a previously created message, which is sent. + * @param sentFrom + * The sentFrom information can be set to inject the sender's queue id into + * the message. This variable is set to zero by default. + * @param ignoreFault + * If set to true, the internal software fault counter is not incremented + * if queue is full. + * @param context Specify whether call is made from task or from an ISR. + */ + static ReturnValue_t sendMessageFromMessageQueue(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom = NO_QUEUE, + bool ignoreFault=false); + + //static ReturnValue_t handleSendResult(BaseType_t result, bool ignoreFault); + +private: + std::queue messageQueue; + /** + * @brief The class stores the queue id it got assigned. + * If initialization fails, the queue id is set to zero. + */ + MessageQueueId_t mqId = 0; + size_t messageSize = 0; + size_t messageDepth = 0; + + MutexIF* queueLock; + + bool defaultDestinationSet = false; + MessageQueueId_t defaultDestination = 0; + MessageQueueId_t lastPartner = 0; +}; + +#endif /* FRAMEWORK_OSAL_HOST_MESSAGEQUEUE_H_ */ diff --git a/osal/host/Mutex.cpp b/osal/host/Mutex.cpp new file mode 100644 index 000000000..8471cab80 --- /dev/null +++ b/osal/host/Mutex.cpp @@ -0,0 +1,39 @@ +#include "Mutex.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + +Mutex::Mutex() {} + +ReturnValue_t Mutex::lockMutex(TimeoutType timeoutType, uint32_t timeoutMs) { + if(timeoutMs == MutexIF::BLOCKING) { + mutex.lock(); + locked = true; + return HasReturnvaluesIF::RETURN_OK; + } + else if(timeoutMs == MutexIF::POLLING) { + if(mutex.try_lock()) { + locked = true; + return HasReturnvaluesIF::RETURN_OK; + } + } + else if(timeoutMs > MutexIF::POLLING){ + auto chronoMs = std::chrono::milliseconds(timeoutMs); + if(mutex.try_lock_for(chronoMs)) { + locked = true; + return HasReturnvaluesIF::RETURN_OK; + } + } + return MutexIF::MUTEX_TIMEOUT; +} + +ReturnValue_t Mutex::unlockMutex() { + if(not locked) { + return MutexIF::CURR_THREAD_DOES_NOT_OWN_MUTEX; + } + mutex.unlock(); + locked = false; + return HasReturnvaluesIF::RETURN_OK; +} + +std::timed_mutex* Mutex::getMutexHandle() { + return &mutex; +} diff --git a/osal/host/Mutex.h b/osal/host/Mutex.h new file mode 100644 index 000000000..24dafbbda --- /dev/null +++ b/osal/host/Mutex.h @@ -0,0 +1,29 @@ +#ifndef FSFW_OSAL_HOSTED_MUTEX_H_ +#define FSFW_OSAL_HOSTED_MUTEX_H_ + +#include "../../ipc/MutexIF.h" + +#include + +/** + * @brief OS component to implement MUTual EXclusion + * + * @details + * Mutexes are binary semaphores which include a priority inheritance mechanism. + * Documentation: https://www.freertos.org/Real-time-embedded-RTOS-mutexes.html + * @ingroup osal + */ +class Mutex : public MutexIF { +public: + Mutex(); + ReturnValue_t lockMutex(TimeoutType timeoutType = + TimeoutType::BLOCKING, uint32_t timeoutMs = 0) override; + ReturnValue_t unlockMutex() override; + + std::timed_mutex* getMutexHandle(); +private: + bool locked = false; + std::timed_mutex mutex; +}; + +#endif /* FSFW_OSAL_HOSTED_MUTEX_H_ */ diff --git a/osal/host/MutexFactory.cpp b/osal/host/MutexFactory.cpp new file mode 100644 index 000000000..bf7707d11 --- /dev/null +++ b/osal/host/MutexFactory.cpp @@ -0,0 +1,28 @@ +#include "../../ipc/MutexFactory.h" +#include "../../osal/host/Mutex.h" + +//TODO: Different variant than the lazy loading in QueueFactory. +//What's better and why? -> one is on heap the other on bss/data +//MutexFactory* MutexFactory::factoryInstance = new MutexFactory(); +MutexFactory* MutexFactory::factoryInstance = nullptr; + +MutexFactory::MutexFactory() { +} + +MutexFactory::~MutexFactory() { +} + +MutexFactory* MutexFactory::instance() { + if (factoryInstance == nullptr){ + factoryInstance = new MutexFactory(); + } + return MutexFactory::factoryInstance; +} + +MutexIF* MutexFactory::createMutex() { + return new Mutex(); +} + +void MutexFactory::deleteMutex(MutexIF* mutex) { + delete mutex; +} diff --git a/osal/host/PeriodicTask.cpp b/osal/host/PeriodicTask.cpp new file mode 100644 index 000000000..f4ee079b1 --- /dev/null +++ b/osal/host/PeriodicTask.cpp @@ -0,0 +1,176 @@ +#include "Mutex.h" +#include "PeriodicTask.h" + +#include "../../ipc/MutexFactory.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include "../../tasks/ExecutableObjectIF.h" + +#include +#include + +#if defined(WIN32) +#include +#elif defined(LINUX) +#include +#endif + +PeriodicTask::PeriodicTask(const char *name, TaskPriority setPriority, + TaskStackSize setStack, TaskPeriod setPeriod, + void (*setDeadlineMissedFunc)()) : + started(false), taskName(name), period(setPeriod), + deadlineMissedFunc(setDeadlineMissedFunc) { + // It is propably possible to set task priorities by using the native + // task handles for Windows / Linux + mainThread = std::thread(&PeriodicTask::taskEntryPoint, this, this); +#if defined(WIN32) + /* List of possible priority classes: + * https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ + * nf-processthreadsapi-setpriorityclass + * And respective thread priority numbers: + * https://docs.microsoft.com/en-us/windows/ + * win32/procthread/scheduling-priorities */ + int result = SetPriorityClass( + reinterpret_cast(mainThread.native_handle()), + ABOVE_NORMAL_PRIORITY_CLASS); + if(result != 0) { + sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + } + result = SetThreadPriority( + reinterpret_cast(mainThread.native_handle()), + THREAD_PRIORITY_NORMAL); + if(result != 0) { + sif::error << "PeriodicTask: Windows SetPriorityClass failed with code " + << GetLastError() << std::endl; + } +#elif defined(LINUX) + // we can just copy and paste the code from linux here. +#endif +} + +PeriodicTask::~PeriodicTask(void) { + //Do not delete objects, we were responsible for ptrs only. + terminateThread = true; + if(mainThread.joinable()) { + mainThread.join(); + } + delete this; +} + +void PeriodicTask::taskEntryPoint(void* argument) { + PeriodicTask *originalTask(reinterpret_cast(argument)); + + + if (not originalTask->started) { + // we have to suspend/block here until the task is started. + // if semaphores are implemented, use them here. + std::unique_lock lock(initMutex); + initCondition.wait(lock); + } + + this->taskFunctionality(); + sif::debug << "PeriodicTask::taskEntryPoint: " + "Returned from taskFunctionality." << std::endl; +} + +ReturnValue_t PeriodicTask::startTask() { + started = true; + + // Notify task to start. + std::lock_guard lock(initMutex); + initCondition.notify_one(); + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PeriodicTask::sleepFor(uint32_t ms) { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + return HasReturnvaluesIF::RETURN_OK; +} + +void PeriodicTask::taskFunctionality() { + std::chrono::milliseconds periodChrono(static_cast(period*1000)); + auto currentStartTime { + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + }; + auto nextStartTime{ currentStartTime }; + + /* Enter the loop that defines the task behavior. */ + for (;;) { + if(terminateThread.load()) { + break; + } + for (ObjectList::iterator it = objectList.begin(); + it != objectList.end(); ++it) { + (*it)->performOperation(); + } + if(not delayForInterval(¤tStartTime, periodChrono)) { + sif::warning << "PeriodicTask: " << taskName << + " missed deadline!\n" << std::flush; + if(deadlineMissedFunc != nullptr) { + this->deadlineMissedFunc(); + } + } + } +} + +ReturnValue_t PeriodicTask::addComponent(object_id_t object) { + ExecutableObjectIF* newObject = objectManager->get( + object); + if (newObject == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + objectList.push_back(newObject); + return HasReturnvaluesIF::RETURN_OK; +} + +uint32_t PeriodicTask::getPeriodMs() const { + return period * 1000; +} + +bool PeriodicTask::delayForInterval(chron_ms* previousWakeTimeMs, + const chron_ms interval) { + bool shouldDelay = false; + //Get current wakeup time + auto currentStartTime = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + /* Generate the tick time at which the task wants to wake. */ + auto nextTimeToWake_ms = (*previousWakeTimeMs) + interval; + + if (currentStartTime < *previousWakeTimeMs) { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if ((nextTimeToWake_ms < *previousWakeTimeMs) + && (nextTimeToWake_ms > currentStartTime)) { + shouldDelay = true; + } + } else { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if ((nextTimeToWake_ms < *previousWakeTimeMs) + || (nextTimeToWake_ms > currentStartTime)) { + shouldDelay = true; + } + } + + /* Update the wake time ready for the next call. */ + + (*previousWakeTimeMs) = nextTimeToWake_ms; + + if (shouldDelay) { + auto sleepTime = std::chrono::duration_cast( + nextTimeToWake_ms - currentStartTime); + std::this_thread::sleep_for(sleepTime); + return true; + } + //We are shifting the time in case the deadline was missed like rtems + (*previousWakeTimeMs) = currentStartTime; + return false; + +} diff --git a/osal/host/PeriodicTask.h b/osal/host/PeriodicTask.h new file mode 100644 index 000000000..7689788ac --- /dev/null +++ b/osal/host/PeriodicTask.h @@ -0,0 +1,123 @@ +#ifndef FRAMEWORK_OSAL_HOST_PERIODICTASK_H_ +#define FRAMEWORK_OSAL_HOST_PERIODICTASK_H_ + +#include "../../objectmanager/ObjectManagerIF.h" +#include "../../tasks/PeriodicTaskIF.h" +#include "../../tasks/Typedef.h" + +#include +#include +#include +#include + +class ExecutableObjectIF; + +/** + * @brief This class represents a specialized task for + * periodic activities of multiple objects. + * @details + * + * @ingroup task_handling + */ +class PeriodicTask: public PeriodicTaskIF { +public: + /** + * @brief Standard constructor of the class. + * @details + * The class is initialized without allocated objects. These need to be + * added with #addComponent. + * @param priority + * @param stack_size + * @param setPeriod + * @param setDeadlineMissedFunc + * The function pointer to the deadline missed function that shall be + * assigned. + */ + PeriodicTask(const char *name, TaskPriority setPriority, TaskStackSize setStack, + TaskPeriod setPeriod,void (*setDeadlineMissedFunc)()); + /** + * @brief Currently, the executed object's lifetime is not coupled with + * the task object's lifetime, so the destructor is empty. + */ + virtual ~PeriodicTask(void); + + /** + * @brief The method to start the task. + * @details The method starts the task with the respective system call. + * Entry point is the taskEntryPoint method described below. + * The address of the task object is passed as an argument + * to the system call. + */ + ReturnValue_t startTask(void); + /** + * Adds an object to the list of objects to be executed. + * The objects are executed in the order added. + * @param object Id of the object to add. + * @return + * -@c RETURN_OK on success + * -@c RETURN_FAILED if the object could not be added. + */ + ReturnValue_t addComponent(object_id_t object); + + uint32_t getPeriodMs() const; + + ReturnValue_t sleepFor(uint32_t ms); + +protected: + using chron_ms = std::chrono::milliseconds; + bool started; + //!< Typedef for the List of objects. + typedef std::vector ObjectList; + std::thread mainThread; + std::atomic terminateThread = false; + + /** + * @brief This attribute holds a list of objects to be executed. + */ + ObjectList objectList; + + std::condition_variable initCondition; + std::mutex initMutex; + std::string taskName; + /** + * @brief The period of the task. + * @details + * The period determines the frequency of the task's execution. + * It is expressed in clock ticks. + */ + TaskPeriod period; + /** + * @brief The pointer to the deadline-missed function. + * @details + * This pointer stores the function that is executed if the task's deadline + * is missed. So, each may react individually on a timing failure. + * The pointer may be NULL, then nothing happens on missing the deadline. + * The deadline is equal to the next execution of the periodic task. + */ + void (*deadlineMissedFunc)(void); + /** + * @brief This is the function executed in the new task's context. + * @details + * It converts the argument back to the thread object type and copies the + * class instance to the task context. + * The taskFunctionality method is called afterwards. + * @param A pointer to the task object itself is passed as argument. + */ + + void taskEntryPoint(void* argument); + /** + * @brief The function containing the actual functionality of the task. + * @details + * The method sets and starts the task's period, then enters a loop that is + * repeated as long as the isRunning attribute is true. Within the loop, + * all performOperation methods of the added objects are called. Afterwards + * the checkAndRestartPeriod system call blocks the task until the next + * period. On missing the deadline, the deadlineMissedFunction is executed. + */ + void taskFunctionality(void); + + bool delayForInterval(chron_ms * previousWakeTimeMs, + const chron_ms interval); +}; + +#endif /* PERIODICTASK_H_ */ diff --git a/osal/host/QueueFactory.cpp b/osal/host/QueueFactory.cpp new file mode 100644 index 000000000..da3fea55a --- /dev/null +++ b/osal/host/QueueFactory.cpp @@ -0,0 +1,41 @@ +#include "../../ipc/QueueFactory.h" +#include "../../osal/host/MessageQueue.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" +#include + +QueueFactory* QueueFactory::factoryInstance = nullptr; + + +ReturnValue_t MessageQueueSenderIF::sendMessage(MessageQueueId_t sendTo, + MessageQueueMessageIF* message, MessageQueueId_t sentFrom, + bool ignoreFault) { + return MessageQueue::sendMessageFromMessageQueue(sendTo,message, + sentFrom,ignoreFault); + return HasReturnvaluesIF::RETURN_OK; +} + +QueueFactory* QueueFactory::instance() { + if (factoryInstance == nullptr) { + factoryInstance = new QueueFactory; + } + return factoryInstance; +} + +QueueFactory::QueueFactory() { +} + +QueueFactory::~QueueFactory() { +} + +MessageQueueIF* QueueFactory::createMessageQueue(uint32_t messageDepth, + size_t maxMessageSize) { + // A thread-safe queue can be implemented by using a combination + // of std::queue and std::mutex. This uses dynamic memory allocation + // which could be alleviated by using a custom allocator, external library + // (etl::queue) or simply using std::queue, we're on a host machine anyway. + return new MessageQueue(messageDepth, maxMessageSize); +} + +void QueueFactory::deleteMessageQueue(MessageQueueIF* queue) { + delete queue; +} diff --git a/osal/host/QueueMapManager.cpp b/osal/host/QueueMapManager.cpp new file mode 100644 index 000000000..1b2094e91 --- /dev/null +++ b/osal/host/QueueMapManager.cpp @@ -0,0 +1,52 @@ +#include "QueueMapManager.h" + +#include "../../ipc/MutexFactory.h" +#include "../../ipc/MutexHelper.h" + +QueueMapManager* QueueMapManager::mqManagerInstance = nullptr; + +QueueMapManager::QueueMapManager() { + mapLock = MutexFactory::instance()->createMutex(); +} + +QueueMapManager* QueueMapManager::instance() { + if (mqManagerInstance == nullptr){ + mqManagerInstance = new QueueMapManager(); + } + return QueueMapManager::mqManagerInstance; +} + +ReturnValue_t QueueMapManager::addMessageQueue( + MessageQueueIF* queueToInsert, MessageQueueId_t* id) { + // Not thread-safe, but it is assumed all message queues are created + // at software initialization now. If this is to be made thread-safe in + // the future, it propably would be sufficient to lock the increment + // operation here + uint32_t currentId = queueCounter++; + auto returnPair = queueMap.emplace(currentId, queueToInsert); + if(not returnPair.second) { + // this should never happen for the atomic variable. + sif::error << "QueueMapManager: This ID is already inside the map!" + << std::endl; + return HasReturnvaluesIF::RETURN_FAILED; + } + if (id != nullptr) { + *id = currentId; + } + return HasReturnvaluesIF::RETURN_OK; +} + +MessageQueueIF* QueueMapManager::getMessageQueue( + MessageQueueId_t messageQueueId) const { + MutexHelper(mapLock, MutexIF::TimeoutType::WAITING, 50); + auto queueIter = queueMap.find(messageQueueId); + if(queueIter != queueMap.end()) { + return queueIter->second; + } + else { + sif::warning << "QueueMapManager::getQueueHandle: The ID" << + messageQueueId << " does not exists in the map" << std::endl; + return nullptr; + } +} + diff --git a/osal/host/QueueMapManager.h b/osal/host/QueueMapManager.h new file mode 100644 index 000000000..d7d5c9154 --- /dev/null +++ b/osal/host/QueueMapManager.h @@ -0,0 +1,47 @@ +#ifndef FSFW_OSAL_HOST_QUEUEMAPMANAGER_H_ +#define FSFW_OSAL_HOST_QUEUEMAPMANAGER_H_ + +#include "../../ipc/MessageQueueSenderIF.h" +#include "../../osal/host/MessageQueue.h" +#include +#include + +using QueueMap = std::unordered_map; + + +/** + * An internal map to map message queue IDs to message queues. + * This propably should be a singleton.. + */ +class QueueMapManager { +public: + //! Returns the single instance of SemaphoreFactory. + static QueueMapManager* instance(); + + /** + * Insert a message queue into the map and returns a message queue ID + * @param queue The message queue to insert. + * @param id The passed value will be set unless a nullptr is passed + * @return + */ + ReturnValue_t addMessageQueue(MessageQueueIF* queue, MessageQueueId_t* + id = nullptr); + /** + * Get the message queue handle by providing a message queue ID. + * @param messageQueueId + * @return + */ + MessageQueueIF* getMessageQueue(MessageQueueId_t messageQueueId) const; + +private: + //! External instantiation is forbidden. + QueueMapManager(); + uint32_t queueCounter = 1; + MutexIF* mapLock; + QueueMap queueMap; + static QueueMapManager* mqManagerInstance; +}; + + + +#endif /* FSFW_OSAL_HOST_QUEUEMAPMANAGER_H_ */ diff --git a/osal/host/SemaphoreFactory.cpp b/osal/host/SemaphoreFactory.cpp new file mode 100644 index 000000000..b19b2c75a --- /dev/null +++ b/osal/host/SemaphoreFactory.cpp @@ -0,0 +1,39 @@ +#include "../../tasks/SemaphoreFactory.h" +#include "../../osal/linux/BinarySemaphore.h" +#include "../../osal/linux/CountingSemaphore.h" +#include "../../serviceinterface/ServiceInterfaceStream.h" + +SemaphoreFactory* SemaphoreFactory::factoryInstance = nullptr; + +SemaphoreFactory::SemaphoreFactory() { +} + +SemaphoreFactory::~SemaphoreFactory() { + delete factoryInstance; +} + +SemaphoreFactory* SemaphoreFactory::instance() { + if (factoryInstance == nullptr){ + factoryInstance = new SemaphoreFactory(); + } + return SemaphoreFactory::factoryInstance; +} + +SemaphoreIF* SemaphoreFactory::createBinarySemaphore(uint32_t arguments) { + // Just gonna wait for full C++20 for now. + sif::error << "SemaphoreFactory: Binary Semaphore not implemented yet." + " Returning nullptr!\n" << std::flush; + return nullptr; +} + +SemaphoreIF* SemaphoreFactory::createCountingSemaphore(const uint8_t maxCount, + uint8_t initCount, uint32_t arguments) { + // Just gonna wait for full C++20 for now. + sif::error << "SemaphoreFactory: Counting Semaphore not implemented yet." + " Returning nullptr!\n" << std::flush; + return nullptr; +} + +void SemaphoreFactory::deleteSemaphore(SemaphoreIF* semaphore) { + delete semaphore; +} diff --git a/osal/host/TaskFactory.cpp b/osal/host/TaskFactory.cpp new file mode 100644 index 000000000..bc65504c8 --- /dev/null +++ b/osal/host/TaskFactory.cpp @@ -0,0 +1,51 @@ +#include "../../osal/host/FixedTimeslotTask.h" +#include "../../osal/host/PeriodicTask.h" +#include "../../tasks/TaskFactory.h" +#include "../../returnvalues/HasReturnvaluesIF.h" +#include "../../tasks/PeriodicTaskIF.h" + +#include + +TaskFactory* TaskFactory::factoryInstance = new TaskFactory(); + +// Will propably not be used for hosted implementation +const size_t PeriodicTaskIF::MINIMUM_STACK_SIZE = 0; + +TaskFactory::TaskFactory() { +} + +TaskFactory::~TaskFactory() { +} + +TaskFactory* TaskFactory::instance() { + return TaskFactory::factoryInstance; +} + +PeriodicTaskIF* TaskFactory::createPeriodicTask(TaskName name_, + TaskPriority taskPriority_,TaskStackSize stackSize_, + TaskPeriod periodInSeconds_, + TaskDeadlineMissedFunction deadLineMissedFunction_) { + return new PeriodicTask(name_, taskPriority_, stackSize_, periodInSeconds_, + deadLineMissedFunction_); +} + +FixedTimeslotTaskIF* TaskFactory::createFixedTimeslotTask(TaskName name_, + TaskPriority taskPriority_,TaskStackSize stackSize_, + TaskPeriod periodInSeconds_, + TaskDeadlineMissedFunction deadLineMissedFunction_) { + return new FixedTimeslotTask(name_, taskPriority_, stackSize_, + periodInSeconds_, deadLineMissedFunction_); +} + +ReturnValue_t TaskFactory::deleteTask(PeriodicTaskIF* task) { + // This might block for some time! + delete task; + return HasReturnvaluesIF::RETURN_FAILED; +} + +ReturnValue_t TaskFactory::delayTask(uint32_t delayMs){ + std::this_thread::sleep_for(std::chrono::milliseconds(delayMs)); + return HasReturnvaluesIF::RETURN_OK; +} + + diff --git a/timemanager/CCSDSTime.cpp b/timemanager/CCSDSTime.cpp index a5a38e27d..f99f8fbb4 100644 --- a/timemanager/CCSDSTime.cpp +++ b/timemanager/CCSDSTime.cpp @@ -1,7 +1,8 @@ -#include "CCSDSTime.h" -#include -#include -#include +#include "../timemanager/CCSDSTime.h" +#include +#include +#include + CCSDSTime::CCSDSTime() { } diff --git a/timemanager/CCSDSTime.h b/timemanager/CCSDSTime.h index 8563cf0fd..89fcff92f 100644 --- a/timemanager/CCSDSTime.h +++ b/timemanager/CCSDSTime.h @@ -5,7 +5,7 @@ #include "Clock.h" #include "../returnvalues/HasReturnvaluesIF.h" -#include +#include bool operator<(const timeval& lhs, const timeval& rhs); bool operator<=(const timeval& lhs, const timeval& rhs);