diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 000000000..add8e1a5b --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,98 @@ +## Changes from ASTP 0.0.1 to 1.0.0 + +### Host OSAL + +- Bugfix in MessageQueue, which caused the sender not to be set properly + +### FreeRTOS OSAL + +- vRequestContextSwitchFromISR is declared extern "C" so it can be defined in +a C file without issues + +### PUS Services + +- It is now possible to change the message queue depth for the telecommand verification service (PUS1) +- The same is possible for the event reporting service (PUS5) +- PUS Health Service added, which allows to command and retrieve health via PUS packets + + +### EnhancedControllerBase + +- New base class for a controller which also implements HasActionsIF and HasLocalDataPoolIF + +### Local Pool + +- Interface of LocalPools has changed. LocalPool is not a template anymore. Instead the size and +bucket number of the pools per page and the number of pages are passed to the ctor instead of +two ctor arguments and a template parameter + +### Parameter Service + +- The API of the parameter service has been changed to prevent inconsistencies +between documentation and actual code and to clarify usage. +- The parameter ID now consists of: + 1. Domain ID (1 byte) + 2. Unique Identifier (1 byte) + 3. Linear Index (2 bytes) +The linear index can be used for arrays as well as matrices. +The parameter load command now explicitely expects the ECSS PTC and PFC +information as well as the rows and column number. Rows and column will +default to one, which is equivalent to one scalar parameter (the most +important use-case) + +### File System Interface + +- A new interfaces specifies the functions for a software object which exposes the file system of +a given hardware to use message based file handling (e.g. PUS commanding) + +### Internal Error Reporter + +- The new internal error reporter uses the local data pools. The pool IDs for +the exisiting three error values and the new error set will be hardcoded for +now, the the constructor for the internal error reporter just takes an object +ID for now. + +### Device Handler Base + +- There is an additional `PERFORM_OPERATION` step for the device handler base. It is important +that DHB users adapt their polling sequence tables to perform this step. This steps allows for +a clear distinction between operation and communication steps +- setNormalDatapoolEntriesInvalid is not an abstract method and a default implementation was provided +- getTransitionDelayMs is now an abstract method + +### DeviceHandlerIF + +- Typo for UNKNOWN_DEVICE_REPLY + +### Events + +- makeEvent function: Now takes three input parameters instead of two and +allows setting a unique ID. Event.cpp source file removed, functions now +defined in header directly. Namespaces renamed. Functions declared `constexpr` +now + +### Commanding Service Base + +- CSB uses the new fsfwconfig::FSFW_CSB_FIFO_DEPTH variable to determine the FIFO depth for each +CSB instance. This variable has to be set in the FSFWConfig.h file + +### Service Interface + +- Proper printf support contained in ServiceInterfacePrinter.h +- CPP ostream support now optional (can reduce executable size by 150 - 250 kB) +- Amalagated header which determines automatically which service interface to use depending on FSFWConfig.h configuration. + Users can just use #include +- If CPP streams are excluded, sif:: calls won't work anymore and need to be replaced by their printf counterparts. + For the fsfw, this can be done by checking the processor define FSFW_CPP_OSTREAM_ENABLED from FSFWConfig.h. + For mission code, developers need to replace sif:: calls by the printf counterparts, but only if the CPP stream are excluded. + If this is not the case, everything should work as usual. + +### ActionHelper and ActionMessage + +- ActionHelper finish function and ActionMessage::setCompletionReply now expects explicit +information whether to report a success or failure message instead of deriving it implicitely +from returnvalue + +### PUS Parameter Service 20 + +Added PUS parameter service 20 (only custom subservices available). diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..8ba6a187e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,206 @@ +cmake_minimum_required(VERSION 3.13) + +option(FSFW_GENERATE_SECTIONS + "Generate function and data sections. Required to remove unused code" ON +) + +if(FSFW_GENERATE_SECTIONS) + option(FSFW_REMOVE_UNUSED_CODE "Remove unused code" ON) +endif() + +option(FSFW_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON) +# Options to exclude parts of the FSFW from compilation. +option(FSFW_USE_RMAP "Compile with RMAP" ON) +option(FSFW_USE_DATALINKLAYER "Compile with Data Link Layer" ON) + +set(LIB_FSFW_NAME fsfw) +add_library(${LIB_FSFW_NAME}) + +set_property(CACHE OS_FSFW PROPERTY STRINGS host linux rtems freertos) + +if(NOT OS_FSFW) + message(STATUS "No OS for FSFW via OS_FSFW set. Assuming host OS") + # Assume host OS and autodetermine from OS_FSFW + if(UNIX) + set(OS_FSFW "linux" + CACHE STRING + "OS abstraction layer used in the FSFW" + ) + elseif(WIN32) + set(OS_FSFW "host" + CACHE STRING "OS abstraction layer used in the FSFW" + ) + endif() + +endif() + +set(FSFW_OSAL_DEFINITION FSFW_HOST) + +if(${OS_FSFW} STREQUAL host) + set(OS_FSFW_NAME "Host") +elseif(${OS_FSFW} STREQUAL linux) + set(OS_FSFW_NAME "Linux") + set(FSFW_OSAL_DEFINITION FSFW_LINUX) +elseif(${OS_FSFW} STREQUAL freertos) + set(OS_FSFW_NAME "FreeRTOS") + set(FSFW_OSAL_DEFINITION FSFW_FREERTOS) + target_link_libraries(${LIB_FSFW_NAME} PRIVATE + ${LIB_OS_NAME} + ) +elseif(${OS_FSFW} STREQUAL rtems) + set(OS_FSFW_NAME "RTEMS") + set(FSFW_OSAL_DEFINITION FSFW_RTEMS) +else() + message(WARNING + "Invalid operating system for FSFW specified! Setting to host.." + ) + set(OS_FSFW_NAME "Host") + set(OS_FSFW "host") +endif() + +target_compile_definitions(${LIB_FSFW_NAME} PRIVATE + ${FSFW_OSAL_DEFINITION} +) + +target_compile_definitions(${LIB_FSFW_NAME} INTERFACE + ${FSFW_OSAL_DEFINITION} +) + +message(STATUS "Compiling FSFW for the ${OS_FSFW_NAME} operating system.") + +add_subdirectory(action) +add_subdirectory(container) +add_subdirectory(controller) +add_subdirectory(coordinates) + +if(FSFW_USE_DATALINKLAYER) + add_subdirectory(datalinklayer) +endif() + +add_subdirectory(datapool) +add_subdirectory(datapoollocal) +add_subdirectory(housekeeping) +add_subdirectory(devicehandlers) +add_subdirectory(events) +add_subdirectory(fdir) +add_subdirectory(globalfunctions) +add_subdirectory(health) +add_subdirectory(internalError) +add_subdirectory(ipc) +add_subdirectory(memory) +add_subdirectory(modes) +add_subdirectory(monitoring) +add_subdirectory(objectmanager) +add_subdirectory(osal) +add_subdirectory(parameters) +add_subdirectory(power) +add_subdirectory(pus) + +if(FSFW_USE_RMAP) + add_subdirectory(rmap) +endif() + +add_subdirectory(serialize) +add_subdirectory(serviceinterface) +add_subdirectory(storagemanager) +add_subdirectory(subsystem) +add_subdirectory(tasks) +add_subdirectory(tcdistribution) +add_subdirectory(thermal) +add_subdirectory(timemanager) +add_subdirectory(tmstorage) +add_subdirectory(tmtcpacket) +add_subdirectory(tmtcservices) +add_subdirectory(unittest) + +# The project CMakeLists file has to set the FSFW_CONFIG_PATH and add it. +# If this is not given, we include the default configuration and emit a warning. +if(NOT FSFW_CONFIG_PATH) + message(WARNING "Flight Software Framework configuration path not set!") + message(WARNING "Setting default configuration!") + add_subdirectory(defaultcfg/fsfwconfig) +endif() + +# FSFW might be part of a possibly complicated folder structure, so we +# extract the absolute path of the fsfwconfig folder. +if(IS_ABSOLUTE ${FSFW_CONFIG_PATH}) + set(FSFW_CONFIG_PATH_ABSOLUTE ${FSFW_CONFIG_PATH}) +else() + get_filename_component(FSFW_CONFIG_PATH_ABSOLUTE + ${FSFW_CONFIG_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR} + ) +endif() + +foreach(INCLUDE_PATH ${FSFW_ADDITIONAL_INC_PATH}) + if(IS_ABSOLUTE ${INCLUDE_PATH}) + set(CURR_ABS_INC_PATH "${FREERTOS_PATH}") + else() + get_filename_component(CURR_ABS_INC_PATH + ${INCLUDE_PATH} REALPATH BASE_DIR ${CMAKE_SOURCE_DIR}) + endif() + + if(CMAKE_VERBOSE) + message(STATUS "FSFW include path: ${CURR_ABS_INC_PATH}") + endif() + + list(APPEND FSFW_ADD_INC_PATHS_ABS ${CURR_ABS_INC_PATH}) +endforeach() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(NOT DEFINED FSFW_WARNING_FLAGS) + set(FSFW_WARNING_FLAGS + -Wall + -Wextra + -Wimplicit-fallthrough=1 + -Wno-unused-parameter + -Wno-psabi + ) + endif() + + if(FSFW_GENERATE_SECTIONS) + target_compile_options(${LIB_FSFW_NAME} PRIVATE + "-ffunction-sections" + "-fdata-sections" + ) + endif() + + if(FSFW_REMOVE_UNUSED_CODE) + target_link_options(${LIB_FSFW_NAME} PRIVATE + "Wl,--gc-sections" + ) + endif() + + if(FSFW_WARNING_SHADOW_LOCAL_GCC) + list(APPEND WARNING_FLAGS "-Wshadow=local") + endif() + +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(COMPILER_FLAGS "/permissive-") +endif() + +# Required include paths to compile the FSFW +target_include_directories(${LIB_FSFW_NAME} INTERFACE + ${CMAKE_SOURCE_DIR} + ${FSFW_CONFIG_PATH_ABSOLUTE} + ${FSFW_ADD_INC_PATHS_ABS} +) + +# Includes path required to compile FSFW itself as well +# We assume that the fsfwconfig folder uses include relative to the project +# root here! +target_include_directories(${LIB_FSFW_NAME} PRIVATE + ${CMAKE_SOURCE_DIR} + ${FSFW_CONFIG_PATH_ABSOLUTE} + ${FSFW_ADD_INC_PATHS_ABS} +) + +target_compile_options(${LIB_FSFW_NAME} PRIVATE + ${FSFW_WARNING_FLAGS} + ${COMPILER_FLAGS} +) + +target_link_libraries(${LIB_FSFW_NAME} PRIVATE + ${FSFW_ADDITIONAL_LINK_LIBS} +) \ No newline at end of file diff --git a/FSFWVersion.h b/FSFWVersion.h index 11a608919..df2d49a51 100644 --- a/FSFWVersion.h +++ b/FSFWVersion.h @@ -3,9 +3,9 @@ const char* const FSFW_VERSION_NAME = "ASTP"; -#define FSFW_VERSION 0 -#define FSFW_SUBVERSION 0 -#define FSFW_REVISION 1 +#define FSFW_VERSION 1 +#define FSFW_SUBVERSION 0 +#define FSFW_REVISION 0 diff --git a/README.md b/README.md index 4aabe36a9..fb3be429e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ![FSFW Logo](logo/FSFW_Logo_V3_bw.png) + # Flight Software Framework (FSFW) The Flight Software Framework is a C++ Object Oriented Framework for unmanned, @@ -8,152 +9,40 @@ The initial version of the Flight Software Framework was developed during the Flying Laptop Project by the University of Stuttgart in cooperation with Airbus Defence and Space GmbH. -## Intended Use +## Quick facts -The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. -Therefore, a mode and health system provides control over the states of the software and the controlled devices. -In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. +The framework is designed for systems, which communicate with external devices, perform control loops, receive telecommands and send telemetry, and need to maintain a high level of availability. Therefore, a mode and health system provides control over the states of the software and the controlled devices. In addition, a simple mechanism of event based fault detection, isolation and recovery is implemented as well. -The recommended hardware is a microprocessor with more than 2 MB of RAM and 1 MB of non-volatile Memory. -For reference, current Applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. +The FSFW provides abstraction layers for operating systems to provide a uniform operating system abstraction layer (OSAL). Some components of this OSAL are required internally by the FSFW but is also very useful for developers to implement the same application logic on different operating systems with a uniform interface. + +Currently, the FSFW provides the following OSALs: + +- Linux +- Host +- FreeRTOS +- RTEMS + +The recommended hardware is a microprocessor with more than 1 MB of RAM and 1 MB of non-volatile Memory. For reference, current applications use a Cobham Gaisler UT699 (LEON3FT), a ISISPACE IOBC or a Zynq-7020 SoC. The `fsfw` was also successfully run on the STM32H743ZI-Nucleo board and on a Raspberry Pi and is currently running on the active satellite mission Flying Laptop. + +## Getting started + +The [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example) provides a good starting point and a demo to see the FSFW capabilities and build it with the Make or the CMake build system. It is recommended to evaluate the FSFW by building and playing around with the demo application. + +Generally, the FSFW is included in a project by compiling the FSFW sources and providing +a configuration folder and adding it to the include path. There are some functions like `printChar` which are different depending on the target architecture and need to be implemented by the mission developer. + +A template configuration folder was provided and can be copied into the project root to have +a starting point. The [configuration section](doc/README-config.md#top) provides more specific information about the possible options. + +## Index + +[1. High-level overview](doc/README-highlevel.md#top)
+[2. Core components](doc/README-core.md#top)
+[3. OSAL overview](doc/README-osal.md#top)
+[4. PUS services](doc/README-pus.md#top)
+[5. Device Handler overview](doc/README-devicehandlers.md#top)
+[6. Controller overview](doc/README-controllers.md#top)
+[7. Local Data Pools](doc/README-localpools.md#top)
-## Structure -The general structure is driven by the usage of interfaces provided by objects. The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. -The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. -This simplifies the instantiation of objects and allows the usage of some standard containers. -Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. -The fsfw uses Run-time type information. -Exceptions are not allowed. - -### Failure Handling - -Functions should return a defined ReturnValue_t to signal to the caller that something is gone wrong. -Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. -The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. - -### OSAL -The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. A independent OSAL called "host" is currently not finished. This aims to be running on windows as well. -The OSAL provides periodic tasks, message queues, clocks and Semaphores as well as Mutexes. - -### Core Components - -Clock: - * This is a class of static functions that can be used at anytime - * Leap Seconds must be set if any time conversions from UTC to other times is used - -ObjectManager (must be created): - -* The component which handles all references. All SystemObjects register at this component. -* Any SystemObject needs to have a unique ObjectId. Those can be managed like objects::framework_objects. -* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call. -A nullptr check of the returning Pointer must be done. This function is based on Run-time type information. - -``` c++ - template T* ObjectManagerIF::get( object_id_t id ) - -``` -* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation. -By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards. - -Event Manager: - -* Component which allows routing of events -* Other objects can subscribe to specific events, ranges of events or all events of an object. -* Subscriptions can be done during runtime but should be done during initialization -* Amounts of allowed subscriptions must be configured by setting this parameters: - -``` c++ -namespace fsfwconfig { -//! Configure the allocated pool sizes for the event manager. -static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; -static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; -static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; -} -``` - - -Health Table: - -* A component which holds every health state -* Provides a thread safe way to access all health states without the need of message exchanges - -Stores - -* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can be exchanged with Stores. With this, only the store address must be exchanged in the message. -* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC Store is used. For outgoing TM a TM store is used. -* All of them should use the Thread Safe Class storagemanager/PoolManager - -Tasks - -There are two different types of tasks: - * The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. - * FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence - - -### Static Ids in the framework - -Some parts of the framework use a static routing address for communication. -An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". - -### Events - -Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. -Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. -Every SystemObject can call triggerEvent from the parent class. -Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. - -### Internal Communication - -Components communicate mostly over Message through Queues. -Those queues are created by calling the singleton QueueFactory::instance()->create(). - -### External Communication - -The external communication with the mission control system is mostly up to the user implementation. -The FSFW provides PUS Services which can be used to but don't need to be used. -The services can be seen as a conversion from a TC to a message based communication and back. - -#### CCSDS Frames, CCSDS Space Packets and PUS - -If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. -If Space Packets are used, a timestamper must be created. -An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. - -#### DeviceHandling - -DeviceHandlers are a core component of the FSFW. -The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. -By separating the underlying Communication Interface with DeviceCommunicationIF, a DH can be tested on different hardware. -The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. -A standard FDIR component for the DH will be created automatically but can be overwritten by the user. - -#### Modes, Health - -The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. -On-board Mode Management is implement in hierarchy system. -DeviceHandlers and Controllers are the lowest part of the hierarchy. -The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. -Assemblies share a common core with the next level which are the Subsystems. - -Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. -The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. -Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. -The target table is used to describe the state that is checked continuously by the subsystem. -All of this allows System Modes to be generated as Subsystem object as well from the same database. -This System contains list of subsystem modes in the transition and target tables. -Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. - -The health state represents if the component is able to perform its tasks. -This can be used to signal the system to avoid using this component instead of a redundant one. -The on-board FDIR uses the health state for isolation and recovery. - -## Example config - -A example config can be found in defaultcfg/fsfwconfig. - -## Unit Tests - -Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. -See README.md in the unittest Folder. \ No newline at end of file diff --git a/action/ActionHelper.cpp b/action/ActionHelper.cpp index 285579160..b2374ed69 100644 --- a/action/ActionHelper.cpp +++ b/action/ActionHelper.cpp @@ -3,151 +3,119 @@ #include "../ipc/MessageQueueSenderIF.h" #include "../objectmanager/ObjectManagerIF.h" +#include "../serviceinterface/ServiceInterface.h" ActionHelper::ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue) : - owner(setOwner), queueToUse(useThisQueue) { + owner(setOwner), queueToUse(useThisQueue) { } ActionHelper::~ActionHelper() { } ReturnValue_t ActionHelper::handleActionMessage(CommandMessage* command) { - if (command->getCommand() == ActionMessage::EXECUTE_ACTION) { - ActionId_t currentAction = ActionMessage::getActionId(command); - prepareExecution(command->getSender(), currentAction, - ActionMessage::getStoreId(command)); - return HasReturnvaluesIF::RETURN_OK; - } else { - return CommandMessage::UNKNOWN_COMMAND; - } + if (command->getCommand() == ActionMessage::EXECUTE_ACTION) { + ActionId_t currentAction = ActionMessage::getActionId(command); + prepareExecution(command->getSender(), currentAction, + ActionMessage::getStoreId(command)); + return HasReturnvaluesIF::RETURN_OK; + } else { + return CommandMessage::UNKNOWN_COMMAND; + } } ReturnValue_t ActionHelper::initialize(MessageQueueIF* queueToUse_) { - ipcStore = objectManager->get(objects::IPC_STORE); - if (ipcStore == nullptr) { - return HasReturnvaluesIF::RETURN_FAILED; - } - if(queueToUse_ != nullptr) { - setQueueToUse(queueToUse_); - } + ipcStore = objectManager->get(objects::IPC_STORE); + if (ipcStore == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + if(queueToUse_ != nullptr) { + setQueueToUse(queueToUse_); + } - return HasReturnvaluesIF::RETURN_OK; + return HasReturnvaluesIF::RETURN_OK; } void ActionHelper::step(uint8_t step, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { - CommandMessage reply; - ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result); - queueToUse->sendMessage(reportTo, &reply); + CommandMessage reply; + ActionMessage::setStepReply(&reply, commandId, step + STEP_OFFSET, result); + queueToUse->sendMessage(reportTo, &reply); } -void ActionHelper::finish(MessageQueueId_t reportTo, ActionId_t commandId, +void ActionHelper::finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId, ReturnValue_t result) { - CommandMessage reply; - ActionMessage::setCompletionReply(&reply, commandId, result); - queueToUse->sendMessage(reportTo, &reply); + CommandMessage reply; + ActionMessage::setCompletionReply(&reply, commandId, success, result); + queueToUse->sendMessage(reportTo, &reply); } void ActionHelper::setQueueToUse(MessageQueueIF* queue) { - queueToUse = queue; + queueToUse = queue; } void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, store_address_t dataAddress) { - const uint8_t* dataPtr = NULL; - size_t size = 0; - ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); - if (result != HasReturnvaluesIF::RETURN_OK) { - CommandMessage reply; - ActionMessage::setStepReply(&reply, actionId, 0, result); - queueToUse->sendMessage(commandedBy, &reply); - return; - } - result = owner->executeAction(actionId, commandedBy, dataPtr, size); - ipcStore->deleteData(dataAddress); - if(result == HasActionsIF::EXECUTION_FINISHED) { - CommandMessage reply; - ActionMessage::setCompletionReply(&reply, actionId, result); - queueToUse->sendMessage(commandedBy, &reply); - } - if (result != HasReturnvaluesIF::RETURN_OK) { - CommandMessage reply; - ActionMessage::setStepReply(&reply, actionId, 0, result); - queueToUse->sendMessage(commandedBy, &reply); - return; - } + const uint8_t* dataPtr = NULL; + size_t size = 0; + ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); + if (result != HasReturnvaluesIF::RETURN_OK) { + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, result); + queueToUse->sendMessage(commandedBy, &reply); + return; + } + result = owner->executeAction(actionId, commandedBy, dataPtr, size); + ipcStore->deleteData(dataAddress); + if(result == HasActionsIF::EXECUTION_FINISHED) { + CommandMessage reply; + ActionMessage::setCompletionReply(&reply, actionId, true, result); + queueToUse->sendMessage(commandedBy, &reply); + } + if (result != HasReturnvaluesIF::RETURN_OK) { + CommandMessage reply; + ActionMessage::setStepReply(&reply, actionId, 0, result); + queueToUse->sendMessage(commandedBy, &reply); + return; + } } ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, - ActionId_t replyId, SerializeIF* data, bool hideSender) { - CommandMessage reply; - store_address_t storeAddress; - uint8_t *dataPtr; - size_t maxSize = data->getSerializedSize(); - if (maxSize == 0) { - //No error, there's simply nothing to report. - return HasReturnvaluesIF::RETURN_OK; - } - size_t size = 0; - ReturnValue_t result = ipcStore->getFreeElement(&storeAddress, maxSize, - &dataPtr); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - result = data->serialize(&dataPtr, &size, maxSize, - SerializeIF::Endianness::BIG); - if (result != HasReturnvaluesIF::RETURN_OK) { - ipcStore->deleteData(storeAddress); - return result; - } - // We don't need to report the objectId, as we receive REQUESTED data - // before the completion success message. - // True aperiodic replies need to be reported with - // another dedicated message. - ActionMessage::setDataReply(&reply, replyId, storeAddress); - - // If the sender needs to be hidden, for example to handle packet - // as unrequested reply, this will be done here. - if (hideSender) { - result = MessageQueueSenderIF::sendMessage(reportTo, &reply); - } - else { - result = queueToUse->sendMessage(reportTo, &reply); - } - - if (result != HasReturnvaluesIF::RETURN_OK){ - ipcStore->deleteData(storeAddress); - } - return result; -} - -void ActionHelper::resetHelper() { -} - -ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, - ActionId_t replyId, const uint8_t *data, size_t dataSize, - bool hideSender) { + ActionId_t replyId, SerializeIF* data, bool hideSender) { CommandMessage reply; store_address_t storeAddress; - ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); + uint8_t *dataPtr; + size_t maxSize = data->getSerializedSize(); + if (maxSize == 0) { + /* No error, there's simply nothing to report. */ + return HasReturnvaluesIF::RETURN_OK; + } + size_t size = 0; + ReturnValue_t result = ipcStore->getFreeElement(&storeAddress, maxSize, + &dataPtr); if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "ActionHelper::reportData: Getting free element from IPC store failed!" << + std::endl; +#else + sif::printWarning("ActionHelper::reportData: Getting free element from IPC " + "store failed!\n"); +#endif return result; } - + result = data->serialize(&dataPtr, &size, maxSize, + SerializeIF::Endianness::BIG); if (result != HasReturnvaluesIF::RETURN_OK) { ipcStore->deleteData(storeAddress); return result; } - // We don't need to report the objectId, as we receive REQUESTED data - // before the completion success message. - // True aperiodic replies need to be reported with - // another dedicated message. + /* We don't need to report the objectId, as we receive REQUESTED data before the completion + success message. True aperiodic replies need to be reported with another dedicated message. */ ActionMessage::setDataReply(&reply, replyId, storeAddress); - // If the sender needs to be hidden, for example to handle packet - // as unrequested reply, this will be done here. + /* If the sender needs to be hidden, for example to handle packet + as unrequested reply, this will be done here. */ if (hideSender) { result = MessageQueueSenderIF::sendMessage(reportTo, &reply); } @@ -160,3 +128,40 @@ ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, } return result; } + +void ActionHelper::resetHelper() { +} + +ReturnValue_t ActionHelper::reportData(MessageQueueId_t reportTo, + ActionId_t replyId, const uint8_t *data, size_t dataSize, + bool hideSender) { + CommandMessage reply; + store_address_t storeAddress; + ReturnValue_t result = ipcStore->addData(&storeAddress, data, dataSize); + if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "ActionHelper::reportData: Adding data to IPC store failed!" << std::endl; +#else + sif::printWarning("ActionHelper::reportData: Adding data to IPC store failed!\n"); +#endif + return result; + } + + /* We don't need to report the objectId, as we receive REQUESTED data before the completion + success message. True aperiodic replies need to be reported with another dedicated message. */ + ActionMessage::setDataReply(&reply, replyId, storeAddress); + + /* If the sender needs to be hidden, for example to handle packet + as unrequested reply, this will be done here. */ + if (hideSender) { + result = MessageQueueSenderIF::sendMessage(reportTo, &reply); + } + else { + result = queueToUse->sendMessage(reportTo, &reply); + } + + if (result != HasReturnvaluesIF::RETURN_OK) { + ipcStore->deleteData(storeAddress); + } + return result; +} diff --git a/action/ActionHelper.h b/action/ActionHelper.h index a91722f39..c90247478 100644 --- a/action/ActionHelper.h +++ b/action/ActionHelper.h @@ -18,68 +18,68 @@ class HasActionsIF; class ActionHelper { public: - /** - * Constructor of the action helper - * @param setOwner Pointer to the owner of the interface - * @param useThisQueue messageQueue to be used, can be set during - * initialize function as well. - */ - ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); + /** + * Constructor of the action helper + * @param setOwner Pointer to the owner of the interface + * @param useThisQueue messageQueue to be used, can be set during + * initialize function as well. + */ + ActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); - virtual ~ActionHelper(); - /** - * Function to be called from the owner with a new command message - * - * If the message is a valid action message the helper will use the - * executeAction function from HasActionsIF. - * If the message is invalid or the callback fails a message reply will be - * send to the sender of the message automatically. - * - * @param command Pointer to a command message received by the owner - * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, - * CommandMessage::UNKNOW_COMMAND if this message ID is unkown - */ - ReturnValue_t handleActionMessage(CommandMessage* command); - /** - * Helper initialize function. Must be called before use of any other - * helper function - * @param queueToUse_ Pointer to the messageQueue to be used, optional - * if queue was set in constructor - * @return Returns RETURN_OK if successful - */ - ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr); - /** - * Function to be called from the owner to send a step message. - * Success or failure will be determined by the result value. - * - * @param step Number of steps already done - * @param reportTo The messageQueueId to report the step message to - * @param commandId ID of the executed command - * @param result Result of the execution - */ - void step(uint8_t step, MessageQueueId_t reportTo, - ActionId_t commandId, - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - /** - * Function to be called by the owner to send a action completion message - * - * @param reportTo MessageQueueId_t to report the action completion message to - * @param commandId ID of the executed command - * @param result Result of the execution - */ - void finish(MessageQueueId_t reportTo, ActionId_t commandId, - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - /** - * Function to be called by the owner if an action does report data. - * Takes a SerializeIF* pointer and serializes it into the IPC store. - * @param reportTo MessageQueueId_t to report the action completion - * message to - * @param replyId ID of the executed command - * @param data Pointer to the data - * @return Returns RETURN_OK if successful, otherwise failure code - */ - ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, - SerializeIF* data, bool hideSender = false); + virtual ~ActionHelper(); + /** + * Function to be called from the owner with a new command message + * + * If the message is a valid action message the helper will use the + * executeAction function from HasActionsIF. + * If the message is invalid or the callback fails a message reply will be + * send to the sender of the message automatically. + * + * @param command Pointer to a command message received by the owner + * @return HasReturnvaluesIF::RETURN_OK if the message is a action message, + * CommandMessage::UNKNOW_COMMAND if this message ID is unkown + */ + ReturnValue_t handleActionMessage(CommandMessage* command); + /** + * Helper initialize function. Must be called before use of any other + * helper function + * @param queueToUse_ Pointer to the messageQueue to be used, optional + * if queue was set in constructor + * @return Returns RETURN_OK if successful + */ + ReturnValue_t initialize(MessageQueueIF* queueToUse_ = nullptr); + /** + * Function to be called from the owner to send a step message. + * Success or failure will be determined by the result value. + * + * @param step Number of steps already done + * @param reportTo The messageQueueId to report the step message to + * @param commandId ID of the executed command + * @param result Result of the execution + */ + void step(uint8_t step, MessageQueueId_t reportTo, + ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + /** + * Function to be called by the owner to send a action completion message + * @param success Specify whether action was completed successfully or not. + * @param reportTo MessageQueueId_t to report the action completion message to + * @param commandId ID of the executed command + * @param result Result of the execution + */ + void finish(bool success, MessageQueueId_t reportTo, ActionId_t commandId, + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + /** + * Function to be called by the owner if an action does report data. + * Takes a SerializeIF* pointer and serializes it into the IPC store. + * @param reportTo MessageQueueId_t to report the action completion + * message to + * @param replyId ID of the executed command + * @param data Pointer to the data + * @return Returns RETURN_OK if successful, otherwise failure code + */ + ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, + SerializeIF* data, bool hideSender = false); /** * Function to be called by the owner if an action does report data. * Takes the raw data and writes it into the IPC store. @@ -91,35 +91,36 @@ public: */ ReturnValue_t reportData(MessageQueueId_t reportTo, ActionId_t replyId, const uint8_t* data, size_t dataSize, bool hideSender = false); - /** - * Function to setup the MessageQueueIF* of the helper. Can be used to - * set the MessageQueueIF* if message queue is unavailable at construction - * and initialize but must be setup before first call of other functions. - * @param queue Queue to be used by the helper - */ - void setQueueToUse(MessageQueueIF *queue); + /** + * Function to setup the MessageQueueIF* of the helper. Can be used to + * set the MessageQueueIF* if message queue is unavailable at construction + * and initialize but must be setup before first call of other functions. + * @param queue Queue to be used by the helper + */ + void setQueueToUse(MessageQueueIF *queue); protected: - //!< Increase of value of this per step - static const uint8_t STEP_OFFSET = 1; - HasActionsIF* owner;//!< Pointer to the owner - //! Queue to be used as response sender, has to be set in ctor or with - //! setQueueToUse - MessageQueueIF* queueToUse; - //! Pointer to an IPC Store, initialized during construction or - StorageManagerIF* ipcStore = nullptr; + //! Increase of value of this per step + static const uint8_t STEP_OFFSET = 1; + //! Pointer to the owner + HasActionsIF* owner; + //! Queue to be used as response sender, has to be set in ctor or with + //! setQueueToUse + MessageQueueIF* queueToUse; + //! Pointer to an IPC Store, initialized during construction or + StorageManagerIF* ipcStore = nullptr; - /** - * Internal function called by handleActionMessage - * @param commandedBy MessageQueueID of Commander - * @param actionId ID of action to be done - * @param dataAddress Address of additional data in IPC Store - */ - virtual void prepareExecution(MessageQueueId_t commandedBy, - ActionId_t actionId, store_address_t dataAddress); - /** - * @brief Default implementation is empty. - */ - virtual void resetHelper(); + /** + * Internal function called by handleActionMessage + * @param commandedBy MessageQueueID of Commander + * @param actionId ID of action to be done + * @param dataAddress Address of additional data in IPC Store + */ + virtual void prepareExecution(MessageQueueId_t commandedBy, + ActionId_t actionId, store_address_t dataAddress); + /** + * @brief Default implementation is empty. + */ + virtual void resetHelper(); }; #endif /* FSFW_ACTION_ACTIONHELPER_H_ */ diff --git a/action/ActionMessage.cpp b/action/ActionMessage.cpp index b0bcabaa0..66c7f0581 100644 --- a/action/ActionMessage.cpp +++ b/action/ActionMessage.cpp @@ -1,4 +1,6 @@ #include "ActionMessage.h" +#include "HasActionsIF.h" + #include "../objectmanager/ObjectManagerIF.h" #include "../storagemanager/StorageManagerIF.h" @@ -9,71 +11,72 @@ ActionMessage::~ActionMessage() { } void ActionMessage::setCommand(CommandMessage* message, ActionId_t fid, - store_address_t parameters) { - message->setCommand(EXECUTE_ACTION); - message->setParameter(fid); - message->setParameter2(parameters.raw); + store_address_t parameters) { + message->setCommand(EXECUTE_ACTION); + message->setParameter(fid); + message->setParameter2(parameters.raw); } ActionId_t ActionMessage::getActionId(const CommandMessage* message) { - return ActionId_t(message->getParameter()); + return ActionId_t(message->getParameter()); } store_address_t ActionMessage::getStoreId(const CommandMessage* message) { - store_address_t temp; - temp.raw = message->getParameter2(); - return temp; + store_address_t temp; + temp.raw = message->getParameter2(); + return temp; } void ActionMessage::setStepReply(CommandMessage* message, ActionId_t fid, uint8_t step, - ReturnValue_t result) { - if (result == HasReturnvaluesIF::RETURN_OK) { - message->setCommand(STEP_SUCCESS); - } else { - message->setCommand(STEP_FAILED); - } - message->setParameter(fid); - message->setParameter2((step << 16) + result); + ReturnValue_t result) { + if (result == HasReturnvaluesIF::RETURN_OK) { + message->setCommand(STEP_SUCCESS); + } else { + message->setCommand(STEP_FAILED); + } + message->setParameter(fid); + message->setParameter2((step << 16) + result); } uint8_t ActionMessage::getStep(const CommandMessage* message) { - return uint8_t((message->getParameter2() >> 16) & 0xFF); + return uint8_t((message->getParameter2() >> 16) & 0xFF); } ReturnValue_t ActionMessage::getReturnCode(const CommandMessage* message) { - return message->getParameter2() & 0xFFFF; + return message->getParameter2() & 0xFFFF; } void ActionMessage::setDataReply(CommandMessage* message, ActionId_t actionId, - store_address_t data) { - message->setCommand(DATA_REPLY); - message->setParameter(actionId); - message->setParameter2(data.raw); + store_address_t data) { + message->setCommand(DATA_REPLY); + message->setParameter(actionId); + message->setParameter2(data.raw); } void ActionMessage::setCompletionReply(CommandMessage* message, - ActionId_t fid, ReturnValue_t result) { - if (result == HasReturnvaluesIF::RETURN_OK) { - message->setCommand(COMPLETION_SUCCESS); - } else { - message->setCommand(COMPLETION_FAILED); - } - message->setParameter(fid); - message->setParameter2(result); + ActionId_t fid, bool success, ReturnValue_t result) { + if (success) { + message->setCommand(COMPLETION_SUCCESS); + } + else { + message->setCommand(COMPLETION_FAILED); + } + message->setParameter(fid); + message->setParameter2(result); } void ActionMessage::clear(CommandMessage* message) { - switch(message->getCommand()) { - case EXECUTE_ACTION: - case DATA_REPLY: { - StorageManagerIF *ipcStore = objectManager->get( - objects::IPC_STORE); - if (ipcStore != NULL) { - ipcStore->deleteData(getStoreId(message)); - } - break; - } - default: - break; - } + switch(message->getCommand()) { + case EXECUTE_ACTION: + case DATA_REPLY: { + StorageManagerIF *ipcStore = objectManager->get( + objects::IPC_STORE); + if (ipcStore != NULL) { + ipcStore->deleteData(getStoreId(message)); + } + break; + } + default: + break; + } } diff --git a/action/ActionMessage.h b/action/ActionMessage.h index 7a859de0d..0c64588ca 100644 --- a/action/ActionMessage.h +++ b/action/ActionMessage.h @@ -4,33 +4,44 @@ #include "../ipc/CommandMessage.h" #include "../objectmanager/ObjectManagerIF.h" #include "../storagemanager/StorageManagerIF.h" -typedef uint32_t ActionId_t; +using ActionId_t = uint32_t; + +/** + * @brief These messages are part of the action module of the FSFW. + * @details + * These messages are sent amongst objects implementing the HasActionsIF. Classes like the + * ActionHelper are able to process these messages. + */ class ActionMessage { private: - ActionMessage(); + ActionMessage(); public: - static const uint8_t MESSAGE_ID = messagetypes::ACTION; - static const Command_t EXECUTE_ACTION = MAKE_COMMAND_ID(1); - static const Command_t STEP_SUCCESS = MAKE_COMMAND_ID(2); - static const Command_t STEP_FAILED = MAKE_COMMAND_ID(3); - static const Command_t DATA_REPLY = MAKE_COMMAND_ID(4); - static const Command_t COMPLETION_SUCCESS = MAKE_COMMAND_ID(5); - static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6); - virtual ~ActionMessage(); - static void setCommand(CommandMessage* message, ActionId_t fid, - store_address_t parameters); - static ActionId_t getActionId(const CommandMessage* message ); - static store_address_t getStoreId(const CommandMessage* message ); - static void setStepReply(CommandMessage* message, ActionId_t fid, - uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - static uint8_t getStep(const CommandMessage* message ); - static ReturnValue_t getReturnCode(const CommandMessage* message ); - static void setDataReply(CommandMessage* message, ActionId_t actionId, - store_address_t data); - static void setCompletionReply(CommandMessage* message, ActionId_t fid, - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - static void clear(CommandMessage* message); + static const uint8_t MESSAGE_ID = messagetypes::ACTION; + static const Command_t EXECUTE_ACTION = MAKE_COMMAND_ID(1); + static const Command_t STEP_SUCCESS = MAKE_COMMAND_ID(2); + static const Command_t STEP_FAILED = MAKE_COMMAND_ID(3); + static const Command_t DATA_REPLY = MAKE_COMMAND_ID(4); + static const Command_t COMPLETION_SUCCESS = MAKE_COMMAND_ID(5); + static const Command_t COMPLETION_FAILED = MAKE_COMMAND_ID(6); + + virtual ~ActionMessage(); + static void setCommand(CommandMessage* message, ActionId_t fid, + store_address_t parameters); + + static ActionId_t getActionId(const CommandMessage* message ); + static store_address_t getStoreId(const CommandMessage* message); + + static void setStepReply(CommandMessage* message, ActionId_t fid, + uint8_t step, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + static uint8_t getStep(const CommandMessage* message ); + static ReturnValue_t getReturnCode(const CommandMessage* message ); + static void setDataReply(CommandMessage* message, ActionId_t actionId, + store_address_t data); + static void setCompletionReply(CommandMessage* message, ActionId_t fid, + bool success, ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + + static void clear(CommandMessage* message); }; #endif /* FSFW_ACTION_ACTIONMESSAGE_H_ */ diff --git a/action/CMakeLists.txt b/action/CMakeLists.txt new file mode 100644 index 000000000..f9ac451df --- /dev/null +++ b/action/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + ActionHelper.cpp + ActionMessage.cpp + CommandActionHelper.cpp + SimpleActionHelper.cpp +) \ No newline at end of file diff --git a/action/CommandActionHelper.cpp b/action/CommandActionHelper.cpp index 4e88f3c34..148b36575 100644 --- a/action/CommandActionHelper.cpp +++ b/action/CommandActionHelper.cpp @@ -5,123 +5,123 @@ #include "../objectmanager/ObjectManagerIF.h" CommandActionHelper::CommandActionHelper(CommandsActionsIF *setOwner) : - owner(setOwner), queueToUse(NULL), ipcStore( - NULL), commandCount(0), lastTarget(0) { + owner(setOwner), queueToUse(NULL), ipcStore( + NULL), commandCount(0), lastTarget(0) { } CommandActionHelper::~CommandActionHelper() { } ReturnValue_t CommandActionHelper::commandAction(object_id_t commandTo, - ActionId_t actionId, SerializeIF *data) { - HasActionsIF *receiver = objectManager->get(commandTo); - if (receiver == NULL) { - return CommandsActionsIF::OBJECT_HAS_NO_FUNCTIONS; - } - store_address_t storeId; - uint8_t *storePointer; - size_t maxSize = data->getSerializedSize(); - ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, - &storePointer); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - size_t size = 0; - result = data->serialize(&storePointer, &size, maxSize, - SerializeIF::Endianness::BIG); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - return sendCommand(receiver->getCommandQueue(), actionId, storeId); + ActionId_t actionId, SerializeIF *data) { + HasActionsIF *receiver = objectManager->get(commandTo); + if (receiver == NULL) { + return CommandsActionsIF::OBJECT_HAS_NO_FUNCTIONS; + } + store_address_t storeId; + uint8_t *storePointer; + size_t maxSize = data->getSerializedSize(); + ReturnValue_t result = ipcStore->getFreeElement(&storeId, maxSize, + &storePointer); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + size_t size = 0; + result = data->serialize(&storePointer, &size, maxSize, + SerializeIF::Endianness::BIG); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return sendCommand(receiver->getCommandQueue(), actionId, storeId); } ReturnValue_t CommandActionHelper::commandAction(object_id_t commandTo, - ActionId_t actionId, const uint8_t *data, uint32_t size) { -// if (commandCount != 0) { -// return CommandsFunctionsIF::ALREADY_COMMANDING; -// } - HasActionsIF *receiver = objectManager->get(commandTo); - if (receiver == NULL) { - return CommandsActionsIF::OBJECT_HAS_NO_FUNCTIONS; - } - store_address_t storeId; - ReturnValue_t result = ipcStore->addData(&storeId, data, size); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - return sendCommand(receiver->getCommandQueue(), actionId, storeId); + ActionId_t actionId, const uint8_t *data, uint32_t size) { +// if (commandCount != 0) { +// return CommandsFunctionsIF::ALREADY_COMMANDING; +// } + HasActionsIF *receiver = objectManager->get(commandTo); + if (receiver == NULL) { + return CommandsActionsIF::OBJECT_HAS_NO_FUNCTIONS; + } + store_address_t storeId; + ReturnValue_t result = ipcStore->addData(&storeId, data, size); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + return sendCommand(receiver->getCommandQueue(), actionId, storeId); } ReturnValue_t CommandActionHelper::sendCommand(MessageQueueId_t queueId, - ActionId_t actionId, store_address_t storeId) { - CommandMessage command; - ActionMessage::setCommand(&command, actionId, storeId); - ReturnValue_t result = queueToUse->sendMessage(queueId, &command); - if (result != HasReturnvaluesIF::RETURN_OK) { - ipcStore->deleteData(storeId); - } - lastTarget = queueId; - commandCount++; - return result; + ActionId_t actionId, store_address_t storeId) { + CommandMessage command; + ActionMessage::setCommand(&command, actionId, storeId); + ReturnValue_t result = queueToUse->sendMessage(queueId, &command); + if (result != HasReturnvaluesIF::RETURN_OK) { + ipcStore->deleteData(storeId); + } + lastTarget = queueId; + commandCount++; + return result; } ReturnValue_t CommandActionHelper::initialize() { - ipcStore = objectManager->get(objects::IPC_STORE); - if (ipcStore == NULL) { - return HasReturnvaluesIF::RETURN_FAILED; - } + ipcStore = objectManager->get(objects::IPC_STORE); + if (ipcStore == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } - queueToUse = owner->getCommandQueuePtr(); - if (queueToUse == NULL) { - return HasReturnvaluesIF::RETURN_FAILED; - } - return HasReturnvaluesIF::RETURN_OK; + queueToUse = owner->getCommandQueuePtr(); + if (queueToUse == NULL) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return HasReturnvaluesIF::RETURN_OK; } ReturnValue_t CommandActionHelper::handleReply(CommandMessage *reply) { - if (reply->getSender() != lastTarget) { - return HasReturnvaluesIF::RETURN_FAILED; - } - switch (reply->getCommand()) { - case ActionMessage::COMPLETION_SUCCESS: - commandCount--; - owner->completionSuccessfulReceived(ActionMessage::getActionId(reply)); - return HasReturnvaluesIF::RETURN_OK; - case ActionMessage::COMPLETION_FAILED: - commandCount--; - owner->completionFailedReceived(ActionMessage::getActionId(reply), - ActionMessage::getReturnCode(reply)); - return HasReturnvaluesIF::RETURN_OK; - case ActionMessage::STEP_SUCCESS: - owner->stepSuccessfulReceived(ActionMessage::getActionId(reply), - ActionMessage::getStep(reply)); - return HasReturnvaluesIF::RETURN_OK; - case ActionMessage::STEP_FAILED: - commandCount--; - owner->stepFailedReceived(ActionMessage::getActionId(reply), - ActionMessage::getStep(reply), - ActionMessage::getReturnCode(reply)); - return HasReturnvaluesIF::RETURN_OK; - case ActionMessage::DATA_REPLY: - extractDataForOwner(ActionMessage::getActionId(reply), - ActionMessage::getStoreId(reply)); - return HasReturnvaluesIF::RETURN_OK; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } + if (reply->getSender() != lastTarget) { + return HasReturnvaluesIF::RETURN_FAILED; + } + switch (reply->getCommand()) { + case ActionMessage::COMPLETION_SUCCESS: + commandCount--; + owner->completionSuccessfulReceived(ActionMessage::getActionId(reply)); + return HasReturnvaluesIF::RETURN_OK; + case ActionMessage::COMPLETION_FAILED: + commandCount--; + owner->completionFailedReceived(ActionMessage::getActionId(reply), + ActionMessage::getReturnCode(reply)); + return HasReturnvaluesIF::RETURN_OK; + case ActionMessage::STEP_SUCCESS: + owner->stepSuccessfulReceived(ActionMessage::getActionId(reply), + ActionMessage::getStep(reply)); + return HasReturnvaluesIF::RETURN_OK; + case ActionMessage::STEP_FAILED: + commandCount--; + owner->stepFailedReceived(ActionMessage::getActionId(reply), + ActionMessage::getStep(reply), + ActionMessage::getReturnCode(reply)); + return HasReturnvaluesIF::RETURN_OK; + case ActionMessage::DATA_REPLY: + extractDataForOwner(ActionMessage::getActionId(reply), + ActionMessage::getStoreId(reply)); + return HasReturnvaluesIF::RETURN_OK; + default: + return HasReturnvaluesIF::RETURN_FAILED; + } } uint8_t CommandActionHelper::getCommandCount() const { - return commandCount; + return commandCount; } void CommandActionHelper::extractDataForOwner(ActionId_t actionId, store_address_t storeId) { - const uint8_t * data = NULL; - size_t size = 0; - ReturnValue_t result = ipcStore->getData(storeId, &data, &size); - if (result != HasReturnvaluesIF::RETURN_OK) { - return; - } - owner->dataReceived(actionId, data, size); - ipcStore->deleteData(storeId); + const uint8_t * data = NULL; + size_t size = 0; + ReturnValue_t result = ipcStore->getData(storeId, &data, &size); + if (result != HasReturnvaluesIF::RETURN_OK) { + return; + } + owner->dataReceived(actionId, data, size); + ipcStore->deleteData(storeId); } diff --git a/action/CommandActionHelper.h b/action/CommandActionHelper.h index cfed8c941..96ef1763a 100644 --- a/action/CommandActionHelper.h +++ b/action/CommandActionHelper.h @@ -11,26 +11,26 @@ class CommandsActionsIF; class CommandActionHelper { - friend class CommandsActionsIF; + friend class CommandsActionsIF; public: - CommandActionHelper(CommandsActionsIF* owner); - virtual ~CommandActionHelper(); - ReturnValue_t commandAction(object_id_t commandTo, - ActionId_t actionId, const uint8_t* data, uint32_t size); - ReturnValue_t commandAction(object_id_t commandTo, - ActionId_t actionId, SerializeIF* data); - ReturnValue_t initialize(); - ReturnValue_t handleReply(CommandMessage* reply); - uint8_t getCommandCount() const; + CommandActionHelper(CommandsActionsIF* owner); + virtual ~CommandActionHelper(); + ReturnValue_t commandAction(object_id_t commandTo, + ActionId_t actionId, const uint8_t* data, uint32_t size); + ReturnValue_t commandAction(object_id_t commandTo, + ActionId_t actionId, SerializeIF* data); + ReturnValue_t initialize(); + ReturnValue_t handleReply(CommandMessage* reply); + uint8_t getCommandCount() const; private: - CommandsActionsIF* owner; - MessageQueueIF* queueToUse; - StorageManagerIF* ipcStore; - uint8_t commandCount; - MessageQueueId_t lastTarget; - void extractDataForOwner(ActionId_t actionId, store_address_t storeId); - ReturnValue_t sendCommand(MessageQueueId_t queueId, ActionId_t actionId, - store_address_t storeId); + CommandsActionsIF* owner; + MessageQueueIF* queueToUse; + StorageManagerIF* ipcStore; + uint8_t commandCount; + MessageQueueId_t lastTarget; + void extractDataForOwner(ActionId_t actionId, store_address_t storeId); + ReturnValue_t sendCommand(MessageQueueId_t queueId, ActionId_t actionId, + store_address_t storeId); }; #endif /* COMMANDACTIONHELPER_H_ */ diff --git a/action/CommandsActionsIF.h b/action/CommandsActionsIF.h index 491bfc703..fe7e856c2 100644 --- a/action/CommandsActionsIF.h +++ b/action/CommandsActionsIF.h @@ -15,22 +15,22 @@ * - replyReceived(id, step, cause) (if cause == OK, it's a success). */ class CommandsActionsIF { - friend class CommandActionHelper; + friend class CommandActionHelper; public: - static const uint8_t INTERFACE_ID = CLASS_ID::COMMANDS_ACTIONS_IF; - static const ReturnValue_t OBJECT_HAS_NO_FUNCTIONS = MAKE_RETURN_CODE(1); - static const ReturnValue_t ALREADY_COMMANDING = MAKE_RETURN_CODE(2); - virtual ~CommandsActionsIF() {} - virtual MessageQueueIF* getCommandQueuePtr() = 0; + static const uint8_t INTERFACE_ID = CLASS_ID::COMMANDS_ACTIONS_IF; + static const ReturnValue_t OBJECT_HAS_NO_FUNCTIONS = MAKE_RETURN_CODE(1); + static const ReturnValue_t ALREADY_COMMANDING = MAKE_RETURN_CODE(2); + virtual ~CommandsActionsIF() {} + virtual MessageQueueIF* getCommandQueuePtr() = 0; protected: - virtual void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) = 0; - virtual void stepFailedReceived(ActionId_t actionId, uint8_t step, - ReturnValue_t returnCode) = 0; - virtual void dataReceived(ActionId_t actionId, const uint8_t* data, - uint32_t size) = 0; - virtual void completionSuccessfulReceived(ActionId_t actionId) = 0; - virtual void completionFailedReceived(ActionId_t actionId, - ReturnValue_t returnCode) = 0; + virtual void stepSuccessfulReceived(ActionId_t actionId, uint8_t step) = 0; + virtual void stepFailedReceived(ActionId_t actionId, uint8_t step, + ReturnValue_t returnCode) = 0; + virtual void dataReceived(ActionId_t actionId, const uint8_t* data, + uint32_t size) = 0; + virtual void completionSuccessfulReceived(ActionId_t actionId) = 0; + virtual void completionFailedReceived(ActionId_t actionId, + ReturnValue_t returnCode) = 0; }; diff --git a/action/HasActionsIF.h b/action/HasActionsIF.h index 690f03698..056e674c3 100644 --- a/action/HasActionsIF.h +++ b/action/HasActionsIF.h @@ -35,28 +35,28 @@ */ class HasActionsIF { public: - static const uint8_t INTERFACE_ID = CLASS_ID::HAS_ACTIONS_IF; - static const ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(1); - static const ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(2); - static const ReturnValue_t EXECUTION_FINISHED = MAKE_RETURN_CODE(3); - static const ReturnValue_t INVALID_ACTION_ID = MAKE_RETURN_CODE(4); - virtual ~HasActionsIF() { } - /** - * Function to get the MessageQueueId_t of the implementing object - * @return MessageQueueId_t of the object - */ - virtual MessageQueueId_t getCommandQueue() const = 0; - /** - * Execute or initialize the execution of a certain function. - * The ActionHelpers will execute this function and behave differently - * depending on the returnvalue. - * - * @return - * -@c EXECUTION_FINISHED Finish reply will be generated - * -@c Not RETURN_OK Step failure reply will be generated - */ - virtual ReturnValue_t executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0; + static const uint8_t INTERFACE_ID = CLASS_ID::HAS_ACTIONS_IF; + static const ReturnValue_t IS_BUSY = MAKE_RETURN_CODE(1); + static const ReturnValue_t INVALID_PARAMETERS = MAKE_RETURN_CODE(2); + static const ReturnValue_t EXECUTION_FINISHED = MAKE_RETURN_CODE(3); + static const ReturnValue_t INVALID_ACTION_ID = MAKE_RETURN_CODE(4); + virtual ~HasActionsIF() { } + /** + * Function to get the MessageQueueId_t of the implementing object + * @return MessageQueueId_t of the object + */ + virtual MessageQueueId_t getCommandQueue() const = 0; + /** + * Execute or initialize the execution of a certain function. + * The ActionHelpers will execute this function and behave differently + * depending on the returnvalue. + * + * @return + * -@c EXECUTION_FINISHED Finish reply will be generated + * -@c Not RETURN_OK Step failure reply will be generated + */ + virtual ReturnValue_t executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t* data, size_t size) = 0; }; diff --git a/action/SimpleActionHelper.cpp b/action/SimpleActionHelper.cpp index af57736c8..8d34f40f2 100644 --- a/action/SimpleActionHelper.cpp +++ b/action/SimpleActionHelper.cpp @@ -2,74 +2,74 @@ #include "SimpleActionHelper.h" SimpleActionHelper::SimpleActionHelper(HasActionsIF* setOwner, - MessageQueueIF* useThisQueue) : - ActionHelper(setOwner, useThisQueue), isExecuting(false) { + MessageQueueIF* useThisQueue) : + ActionHelper(setOwner, useThisQueue), isExecuting(false) { } SimpleActionHelper::~SimpleActionHelper() { } void SimpleActionHelper::step(ReturnValue_t result) { - // STEP_OFFESET is subtracted to compensate for adding offset in base - // method, which is not necessary here. - ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction, - result); - if (result != HasReturnvaluesIF::RETURN_OK) { - resetHelper(); - } + // STEP_OFFESET is subtracted to compensate for adding offset in base + // method, which is not necessary here. + ActionHelper::step(stepCount - STEP_OFFSET, lastCommander, lastAction, + result); + if (result != HasReturnvaluesIF::RETURN_OK) { + resetHelper(); + } } void SimpleActionHelper::finish(ReturnValue_t result) { - ActionHelper::finish(lastCommander, lastAction, result); - resetHelper(); + ActionHelper::finish(lastCommander, lastAction, result); + resetHelper(); } ReturnValue_t SimpleActionHelper::reportData(SerializeIF* data) { - return ActionHelper::reportData(lastCommander, lastAction, data); + return ActionHelper::reportData(lastCommander, lastAction, data); } void SimpleActionHelper::resetHelper() { - stepCount = 0; - isExecuting = false; - lastAction = 0; - lastCommander = 0; + stepCount = 0; + isExecuting = false; + lastAction = 0; + lastCommander = 0; } void SimpleActionHelper::prepareExecution(MessageQueueId_t commandedBy, - ActionId_t actionId, store_address_t dataAddress) { - CommandMessage reply; - if (isExecuting) { - ipcStore->deleteData(dataAddress); - ActionMessage::setStepReply(&reply, actionId, 0, - HasActionsIF::IS_BUSY); - queueToUse->sendMessage(commandedBy, &reply); - } - const uint8_t* dataPtr = NULL; - size_t size = 0; - ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); - if (result != HasReturnvaluesIF::RETURN_OK) { - ActionMessage::setStepReply(&reply, actionId, 0, result); - queueToUse->sendMessage(commandedBy, &reply); - return; - } - lastCommander = commandedBy; - lastAction = actionId; - result = owner->executeAction(actionId, commandedBy, dataPtr, size); - ipcStore->deleteData(dataAddress); - switch (result) { - case HasReturnvaluesIF::RETURN_OK: - isExecuting = true; - stepCount++; - break; - case HasActionsIF::EXECUTION_FINISHED: - ActionMessage::setCompletionReply(&reply, actionId, - HasReturnvaluesIF::RETURN_OK); - queueToUse->sendMessage(commandedBy, &reply); - break; - default: - ActionMessage::setStepReply(&reply, actionId, 0, result); - queueToUse->sendMessage(commandedBy, &reply); - break; - } + ActionId_t actionId, store_address_t dataAddress) { + CommandMessage reply; + if (isExecuting) { + ipcStore->deleteData(dataAddress); + ActionMessage::setStepReply(&reply, actionId, 0, + HasActionsIF::IS_BUSY); + queueToUse->sendMessage(commandedBy, &reply); + } + const uint8_t* dataPtr = NULL; + size_t size = 0; + ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); + if (result != HasReturnvaluesIF::RETURN_OK) { + ActionMessage::setStepReply(&reply, actionId, 0, result); + queueToUse->sendMessage(commandedBy, &reply); + return; + } + lastCommander = commandedBy; + lastAction = actionId; + result = owner->executeAction(actionId, commandedBy, dataPtr, size); + ipcStore->deleteData(dataAddress); + switch (result) { + case HasReturnvaluesIF::RETURN_OK: + isExecuting = true; + stepCount++; + break; + case HasActionsIF::EXECUTION_FINISHED: + ActionMessage::setCompletionReply(&reply, actionId, + true, HasReturnvaluesIF::RETURN_OK); + queueToUse->sendMessage(commandedBy, &reply); + break; + default: + ActionMessage::setStepReply(&reply, actionId, 0, result); + queueToUse->sendMessage(commandedBy, &reply); + break; + } } diff --git a/action/SimpleActionHelper.h b/action/SimpleActionHelper.h index 1f35d9fdf..7c6c4e00e 100644 --- a/action/SimpleActionHelper.h +++ b/action/SimpleActionHelper.h @@ -4,27 +4,27 @@ #include "ActionHelper.h" /** - * @brief This is an action helper which is only able to service one action - * at a time but remembers last commander and last action which - * simplifies usage + * @brief This is an action helper which is only able to service one action + * at a time but remembers last commander and last action which + * simplifies usage */ class SimpleActionHelper: public ActionHelper { public: - SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); - virtual ~SimpleActionHelper(); - void step(ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - void finish(ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); - ReturnValue_t reportData(SerializeIF* data); + SimpleActionHelper(HasActionsIF* setOwner, MessageQueueIF* useThisQueue); + virtual ~SimpleActionHelper(); + void step(ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + void finish(ReturnValue_t result = HasReturnvaluesIF::RETURN_OK); + ReturnValue_t reportData(SerializeIF* data); protected: - void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, - store_address_t dataAddress); - virtual void resetHelper(); + void prepareExecution(MessageQueueId_t commandedBy, ActionId_t actionId, + store_address_t dataAddress); + virtual void resetHelper(); private: - bool isExecuting; - MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE; - ActionId_t lastAction = 0; - uint8_t stepCount = 0; + bool isExecuting; + MessageQueueId_t lastCommander = MessageQueueIF::NO_QUEUE; + ActionId_t lastAction = 0; + uint8_t stepCount = 0; }; #endif /* SIMPLEACTIONHELPER_H_ */ diff --git a/container/ArrayList.h b/container/ArrayList.h index 6bd5c1d5a..25e281a27 100644 --- a/container/ArrayList.h +++ b/container/ArrayList.h @@ -6,7 +6,7 @@ #include "../serialize/SerializeIF.h" /** - * @brief A List that stores its values in an array. + * @brief A List that stores its values in an array. * @details * The underlying storage is an array that can be allocated by the class * itself or supplied via ctor. @@ -15,237 +15,237 @@ */ template class ArrayList { - template friend class SerialArrayListAdapter; + template friend class SerialArrayListAdapter; public: - static const uint8_t INTERFACE_ID = CLASS_ID::ARRAY_LIST; - static const ReturnValue_t FULL = MAKE_RETURN_CODE(0x01); + static const uint8_t INTERFACE_ID = CLASS_ID::ARRAY_LIST; + static const ReturnValue_t FULL = MAKE_RETURN_CODE(0x01); - /** - * This is the allocating constructor. - * It allocates an array of the specified size. - * @param maxSize - */ - ArrayList(count_t maxSize) : - size(0), maxSize_(maxSize), allocated(true) { - entries = new T[maxSize]; - } + /** + * This is the allocating constructor. + * It allocates an array of the specified size. + * @param maxSize + */ + ArrayList(count_t maxSize) : + size(0), maxSize_(maxSize), allocated(true) { + entries = new T[maxSize]; + } - /** - * This is the non-allocating constructor - * - * It expects a pointer to an array of a certain size and initializes - * itself to it. - * - * @param storage the array to use as backend - * @param maxSize size of storage - * @param size size of data already present in storage - */ - ArrayList(T *storage, count_t maxSize, count_t size = 0) : - size(size), entries(storage), maxSize_(maxSize), allocated(false) { - } + /** + * This is the non-allocating constructor + * + * It expects a pointer to an array of a certain size and initializes + * itself to it. + * + * @param storage the array to use as backend + * @param maxSize size of storage + * @param size size of data already present in storage + */ + ArrayList(T *storage, count_t maxSize, count_t size = 0) : + size(size), entries(storage), maxSize_(maxSize), allocated(false) { + } - /** - * Copying is forbiden by declaring copy ctor and copy assignment deleted - * It is too ambigous in this case. - * (Allocate a new backend? Use the same? What to do in an modifying call?) - */ - ArrayList(const ArrayList& other) = delete; - const ArrayList& operator=(const ArrayList& other) = delete; + /** + * Copying is forbiden by declaring copy ctor and copy assignment deleted + * It is too ambigous in this case. + * (Allocate a new backend? Use the same? What to do in an modifying call?) + */ + ArrayList(const ArrayList& other) = delete; + const ArrayList& operator=(const ArrayList& other) = delete; - /** - * Number of Elements stored in this List - */ - count_t size; + /** + * Number of Elements stored in this List + */ + count_t size; - /** - * Destructor, if the allocating constructor was used, it deletes the array. - */ - virtual ~ArrayList() { - if (allocated) { - delete[] entries; - } - } + /** + * Destructor, if the allocating constructor was used, it deletes the array. + */ + virtual ~ArrayList() { + if (allocated) { + delete[] entries; + } + } - /** - * An Iterator to go trough an ArrayList - * - * It stores a pointer to an element and increments the - * pointer when incremented itself. - */ - class Iterator { - public: - /** - * Empty ctor, points to NULL - */ - Iterator(): value(0) {} + /** + * An Iterator to go trough an ArrayList + * + * It stores a pointer to an element and increments the + * pointer when incremented itself. + */ + class Iterator { + public: + /** + * Empty ctor, points to NULL + */ + Iterator(): value(0) {} - /** - * Initializes the Iterator to point to an element - * - * @param initialize - */ - Iterator(T *initialize) { - value = initialize; - } + /** + * Initializes the Iterator to point to an element + * + * @param initialize + */ + Iterator(T *initialize) { + value = initialize; + } - /** - * The current element the iterator points to - */ - T *value; + /** + * The current element the iterator points to + */ + T *value; - Iterator& operator++() { - value++; - return *this; - } + Iterator& operator++() { + value++; + return *this; + } - Iterator operator++(int) { - Iterator tmp(*this); - operator++(); - return tmp; - } + Iterator operator++(int) { + Iterator tmp(*this); + operator++(); + return tmp; + } - Iterator& operator--() { - value--; - return *this; - } + Iterator& operator--() { + value--; + return *this; + } - Iterator operator--(int) { - Iterator tmp(*this); - operator--(); - return tmp; - } + Iterator operator--(int) { + Iterator tmp(*this); + operator--(); + return tmp; + } - T& operator*() { - return *value; - } + T& operator*() { + return *value; + } - const T& operator*() const { - return *value; - } + const T& operator*() const { + return *value; + } - T *operator->() { - return value; - } + T *operator->() { + return value; + } - const T *operator->() const { - return value; - } - }; + const T *operator->() const { + return value; + } + }; - friend bool operator==(const ArrayList::Iterator& lhs, - const ArrayList::Iterator& rhs) { - return (lhs.value == rhs.value); - } + friend bool operator==(const ArrayList::Iterator& lhs, + const ArrayList::Iterator& rhs) { + return (lhs.value == rhs.value); + } - friend bool operator!=(const ArrayList::Iterator& lhs, - const ArrayList::Iterator& rhs) { - return not (lhs.value == rhs.value); - } + friend bool operator!=(const ArrayList::Iterator& lhs, + const ArrayList::Iterator& rhs) { + return not (lhs.value == rhs.value); + } - /** - * Iterator pointing to the first stored elmement - * - * @return Iterator to the first element - */ - Iterator begin() const { - return Iterator(&entries[0]); - } + /** + * Iterator pointing to the first stored elmement + * + * @return Iterator to the first element + */ + Iterator begin() const { + return Iterator(&entries[0]); + } - /** - * returns an Iterator pointing to the element after the last stored entry - * - * @return Iterator to the element after the last entry - */ - Iterator end() const { - return Iterator(&entries[size]); - } + /** + * returns an Iterator pointing to the element after the last stored entry + * + * @return Iterator to the element after the last entry + */ + Iterator end() const { + return Iterator(&entries[size]); + } - T & operator[](count_t i) const { - return entries[i]; - } + T & operator[](count_t i) const { + return entries[i]; + } - /** - * The first element - * - * @return pointer to the first stored element - */ - T *front() { - return entries; - } + /** + * The first element + * + * @return pointer to the first stored element + */ + T *front() { + return entries; + } - /** - * The last element - * - * does not return a valid pointer if called on an empty list. - * - * @return pointer to the last stored element - */ - T *back() { - return &entries[size - 1]; - //Alternative solution - //return const_cast(static_cast(*this).back()); - } + /** + * The last element + * + * does not return a valid pointer if called on an empty list. + * + * @return pointer to the last stored element + */ + T *back() { + return &entries[size - 1]; + //Alternative solution + //return const_cast(static_cast(*this).back()); + } - const T* back() const{ - return &entries[size-1]; - } + const T* back() const{ + return &entries[size-1]; + } - /** - * The maximum number of elements this List can contain - * - * @return maximum number of elements - */ - size_t maxSize() const { - return this->maxSize_; - } + /** + * The maximum number of elements this List can contain + * + * @return maximum number of elements + */ + size_t maxSize() const { + return this->maxSize_; + } - /** - * Insert a new element into the list. - * - * The new element is inserted after the last stored element. - * - * @param entry - * @return - * -@c FULL if the List is full - * -@c RETURN_OK else - */ - ReturnValue_t insert(T entry) { - if (size >= maxSize_) { - return FULL; - } - entries[size] = entry; - ++size; - return HasReturnvaluesIF::RETURN_OK; - } + /** + * Insert a new element into the list. + * + * The new element is inserted after the last stored element. + * + * @param entry + * @return + * -@c FULL if the List is full + * -@c RETURN_OK else + */ + ReturnValue_t insert(T entry) { + if (size >= maxSize_) { + return FULL; + } + entries[size] = entry; + ++size; + return HasReturnvaluesIF::RETURN_OK; + } - /** - * clear the List - * - * This does not actually clear all entries, it only sets the size to 0. - */ - void clear() { - size = 0; - } + /** + * clear the List + * + * This does not actually clear all entries, it only sets the size to 0. + */ + void clear() { + size = 0; + } - count_t remaining() { - return (maxSize_ - size); - } + count_t remaining() { + return (maxSize_ - size); + } protected: - /** - * pointer to the array in which the entries are stored - */ - T *entries; - /** - * remembering the maximum size - */ - size_t maxSize_; + /** + * pointer to the array in which the entries are stored + */ + T *entries; + /** + * remembering the maximum size + */ + size_t maxSize_; - /** - * true if the array was allocated and needs to be deleted in the destructor. - */ - bool allocated; + /** + * true if the array was allocated and needs to be deleted in the destructor. + */ + bool allocated; }; diff --git a/container/BinaryTree.h b/container/BinaryTree.h index 73073dea3..6200cbb1f 100644 --- a/container/BinaryTree.h +++ b/container/BinaryTree.h @@ -7,65 +7,65 @@ template class BinaryNode { public: - BinaryNode(Tp* setValue) : - value(setValue), left(NULL), right(NULL), parent(NULL) { - } - Tp *value; - BinaryNode* left; - BinaryNode* right; - BinaryNode* parent; + BinaryNode(Tp* setValue) : + value(setValue), left(NULL), right(NULL), parent(NULL) { + } + Tp *value; + BinaryNode* left; + BinaryNode* right; + BinaryNode* parent; }; template class ExplicitNodeIterator { public: - typedef ExplicitNodeIterator _Self; - typedef BinaryNode _Node; - typedef Tp value_type; - typedef Tp* pointer; - typedef Tp& reference; - ExplicitNodeIterator() : - element(NULL) { - } - ExplicitNodeIterator(_Node* node) : - element(node) { - } - BinaryNode* element; - _Self up() { - return _Self(element->parent); - } - _Self left() { - if (element != NULL) { - return _Self(element->left); - } else { - return _Self(NULL); - } + typedef ExplicitNodeIterator _Self; + typedef BinaryNode _Node; + typedef Tp value_type; + typedef Tp* pointer; + typedef Tp& reference; + ExplicitNodeIterator() : + element(NULL) { + } + ExplicitNodeIterator(_Node* node) : + element(node) { + } + BinaryNode* element; + _Self up() { + return _Self(element->parent); + } + _Self left() { + if (element != NULL) { + return _Self(element->left); + } else { + return _Self(NULL); + } - } - _Self right() { - if (element != NULL) { - return _Self(element->right); - } else { - return _Self(NULL); - } + } + _Self right() { + if (element != NULL) { + return _Self(element->right); + } else { + return _Self(NULL); + } - } - bool operator==(const _Self& __x) const { - return element == __x.element; - } - bool operator!=(const _Self& __x) const { - return element != __x.element; - } + } + bool operator==(const _Self& __x) const { + return element == __x.element; + } + bool operator!=(const _Self& __x) const { + return element != __x.element; + } pointer operator->() const { - if (element != NULL) { - return element->value; - } else { - return NULL; - } + if (element != NULL) { + return element->value; + } else { + return NULL; + } } pointer operator*() const { - return this->operator->(); + return this->operator->(); } }; @@ -75,77 +75,77 @@ public: template class BinaryTree { public: - typedef ExplicitNodeIterator iterator; - typedef BinaryNode Node; - typedef std::pair children; - BinaryTree() : - rootNode(NULL) { - } - BinaryTree(Node* rootNode) : - rootNode(rootNode) { - } - iterator begin() const { - return iterator(rootNode); - } - static iterator end() { - return iterator(NULL); - } - iterator insert(bool insertLeft, iterator parentNode, Node* newNode ) { - newNode->parent = parentNode.element; - if (parentNode.element != NULL) { - if (insertLeft) { - parentNode.element->left = newNode; - } else { - parentNode.element->right = newNode; - } - } else { - //Insert first element. - rootNode = newNode; - } - return iterator(newNode); - } - //No recursion to children. Needs to be done externally. - children erase(iterator node) { - if (node.element == rootNode) { - //We're root node - rootNode = NULL; - } else { - //Delete parent's reference - if (node.up().left() == node) { - node.up().element->left = NULL; - } else { - node.up().element->right = NULL; - } - } - return children(node.element->left, node.element->right); - } - static uint32_t countLeft(iterator start) { - if (start == end()) { - return 0; - } - //We also count the start node itself. - uint32_t count = 1; - while (start.left() != end()) { - count++; - start = start.left(); - } - return count; - } - static uint32_t countRight(iterator start) { - if (start == end()) { - return 0; - } - //We also count the start node itself. - uint32_t count = 1; - while (start.right() != end()) { - count++; - start = start.right(); - } - return count; - } + typedef ExplicitNodeIterator iterator; + typedef BinaryNode Node; + typedef std::pair children; + BinaryTree() : + rootNode(NULL) { + } + BinaryTree(Node* rootNode) : + rootNode(rootNode) { + } + iterator begin() const { + return iterator(rootNode); + } + static iterator end() { + return iterator(NULL); + } + iterator insert(bool insertLeft, iterator parentNode, Node* newNode ) { + newNode->parent = parentNode.element; + if (parentNode.element != NULL) { + if (insertLeft) { + parentNode.element->left = newNode; + } else { + parentNode.element->right = newNode; + } + } else { + //Insert first element. + rootNode = newNode; + } + return iterator(newNode); + } + //No recursion to children. Needs to be done externally. + children erase(iterator node) { + if (node.element == rootNode) { + //We're root node + rootNode = NULL; + } else { + //Delete parent's reference + if (node.up().left() == node) { + node.up().element->left = NULL; + } else { + node.up().element->right = NULL; + } + } + return children(node.element->left, node.element->right); + } + static uint32_t countLeft(iterator start) { + if (start == end()) { + return 0; + } + //We also count the start node itself. + uint32_t count = 1; + while (start.left() != end()) { + count++; + start = start.left(); + } + return count; + } + static uint32_t countRight(iterator start) { + if (start == end()) { + return 0; + } + //We also count the start node itself. + uint32_t count = 1; + while (start.right() != end()) { + count++; + start = start.right(); + } + return count; + } protected: - Node* rootNode; + Node* rootNode; }; diff --git a/container/CMakeLists.txt b/container/CMakeLists.txt new file mode 100644 index 000000000..13eced1d0 --- /dev/null +++ b/container/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + SharedRingBuffer.cpp + SimpleRingBuffer.cpp +) \ No newline at end of file diff --git a/container/DynamicFIFO.h b/container/DynamicFIFO.h index 86d43f7d7..e1bc3ef2f 100644 --- a/container/DynamicFIFO.h +++ b/container/DynamicFIFO.h @@ -5,8 +5,8 @@ #include /** - * @brief Simple First-In-First-Out data structure. The maximum size - * can be set in the constructor. + * @brief Simple First-In-First-Out data structure. The maximum size + * can be set in the constructor. * @details * The maximum capacity can be determined at run-time, so this container * performs dynamic memory allocation! @@ -17,39 +17,39 @@ template class DynamicFIFO: public FIFOBase { public: - DynamicFIFO(size_t maxCapacity): FIFOBase(nullptr, maxCapacity), - fifoVector(maxCapacity) { - // trying to pass the pointer of the uninitialized vector - // to the FIFOBase constructor directly lead to a super evil bug. - // So we do it like this now. - this->setContainer(fifoVector.data()); - }; + DynamicFIFO(size_t maxCapacity): FIFOBase(nullptr, maxCapacity), + fifoVector(maxCapacity) { + // trying to pass the pointer of the uninitialized vector + // to the FIFOBase constructor directly lead to a super evil bug. + // So we do it like this now. + this->setContainer(fifoVector.data()); + }; - /** - * @brief Custom copy constructor which prevents setting the - * underlying pointer wrong. This function allocates memory! - * @details This is a very heavy operation so try to avoid this! - * - */ - DynamicFIFO(const DynamicFIFO& other): FIFOBase(other), - fifoVector(other.maxCapacity) { - this->fifoVector = other.fifoVector; - this->setContainer(fifoVector.data()); - } + /** + * @brief Custom copy constructor which prevents setting the + * underlying pointer wrong. This function allocates memory! + * @details This is a very heavy operation so try to avoid this! + * + */ + DynamicFIFO(const DynamicFIFO& other): FIFOBase(other), + fifoVector(other.maxCapacity) { + this->fifoVector = other.fifoVector; + this->setContainer(fifoVector.data()); + } - /** - * @brief Custom assignment operator - * @details This is a very heavy operation so try to avoid this! - * @param other DyamicFIFO to copy from - */ - DynamicFIFO& operator=(const DynamicFIFO& other){ - FIFOBase::operator=(other); - this->fifoVector = other.fifoVector; - this->setContainer(fifoVector.data()); - return *this; - } + /** + * @brief Custom assignment operator + * @details This is a very heavy operation so try to avoid this! + * @param other DyamicFIFO to copy from + */ + DynamicFIFO& operator=(const DynamicFIFO& other){ + FIFOBase::operator=(other); + this->fifoVector = other.fifoVector; + this->setContainer(fifoVector.data()); + return *this; + } private: - std::vector fifoVector; + std::vector fifoVector; }; #endif /* FSFW_CONTAINER_DYNAMICFIFO_H_ */ diff --git a/container/FIFO.h b/container/FIFO.h index 701138520..79ead3139 100644 --- a/container/FIFO.h +++ b/container/FIFO.h @@ -5,8 +5,8 @@ #include /** - * @brief Simple First-In-First-Out data structure with size fixed at - * compile time + * @brief Simple First-In-First-Out data structure with size fixed at + * compile time * @details * Performs no dynamic memory allocation. * The public interface of FIFOBase exposes the user interface for the FIFO. @@ -16,32 +16,32 @@ template class FIFO: public FIFOBase { public: - FIFO(): FIFOBase(nullptr, capacity) { - this->setContainer(fifoArray.data()); - }; + FIFO(): FIFOBase(nullptr, capacity) { + this->setContainer(fifoArray.data()); + }; - /** - * @brief Custom copy constructor to set pointer correctly. - * @param other - */ - FIFO(const FIFO& other): FIFOBase(other) { - this->fifoArray = other.fifoArray; - this->setContainer(fifoArray.data()); - } + /** + * @brief Custom copy constructor to set pointer correctly. + * @param other + */ + FIFO(const FIFO& other): FIFOBase(other) { + this->fifoArray = other.fifoArray; + this->setContainer(fifoArray.data()); + } - /** - * @brief Custom assignment operator - * @param other - */ - FIFO& operator=(const FIFO& other){ - FIFOBase::operator=(other); - this->fifoArray = other.fifoArray; - this->setContainer(fifoArray.data()); - return *this; - } + /** + * @brief Custom assignment operator + * @param other + */ + FIFO& operator=(const FIFO& other){ + FIFOBase::operator=(other); + this->fifoArray = other.fifoArray; + this->setContainer(fifoArray.data()); + return *this; + } private: - std::array fifoArray; + std::array fifoArray; }; #endif /* FSFW_CONTAINER_FIFO_H_ */ diff --git a/container/FIFOBase.h b/container/FIFOBase.h index edd66d37a..eaa80346e 100644 --- a/container/FIFOBase.h +++ b/container/FIFOBase.h @@ -8,70 +8,70 @@ template class FIFOBase { public: - static const uint8_t INTERFACE_ID = CLASS_ID::FIFO_CLASS; - static const ReturnValue_t FULL = MAKE_RETURN_CODE(1); - static const ReturnValue_t EMPTY = MAKE_RETURN_CODE(2); + static const uint8_t INTERFACE_ID = CLASS_ID::FIFO_CLASS; + static const ReturnValue_t FULL = MAKE_RETURN_CODE(1); + static const ReturnValue_t EMPTY = MAKE_RETURN_CODE(2); - /** Default ctor, takes pointer to first entry of underlying container - * and maximum capacity */ - FIFOBase(T* values, const size_t maxCapacity); + /** Default ctor, takes pointer to first entry of underlying container + * and maximum capacity */ + FIFOBase(T* values, const size_t maxCapacity); - /** - * Insert value into FIFO - * @param value - * @return RETURN_OK on success, FULL if full - */ - ReturnValue_t insert(T value); - /** - * Retrieve item from FIFO. This removes the item from the FIFO. - * @param value Must point to a valid T - * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed - */ - ReturnValue_t retrieve(T *value); - /** - * Retrieve item from FIFO without removing it from FIFO. - * @param value Must point to a valid T - * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed - */ - ReturnValue_t peek(T * value); - /** - * Remove item from FIFO. - * @return RETURN_OK on success, EMPTY if empty - */ - ReturnValue_t pop(); + /** + * Insert value into FIFO + * @param value + * @return RETURN_OK on success, FULL if full + */ + ReturnValue_t insert(T value); + /** + * Retrieve item from FIFO. This removes the item from the FIFO. + * @param value Must point to a valid T + * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed + */ + ReturnValue_t retrieve(T *value); + /** + * Retrieve item from FIFO without removing it from FIFO. + * @param value Must point to a valid T + * @return RETURN_OK on success, EMPTY if empty and FAILED if nullptr check failed + */ + ReturnValue_t peek(T * value); + /** + * Remove item from FIFO. + * @return RETURN_OK on success, EMPTY if empty + */ + ReturnValue_t pop(); - /*** - * Check if FIFO is empty - * @return True if empty, False if not - */ - bool empty(); - /*** - * Check if FIFO is Full - * @return True if full, False if not - */ - bool full(); - /*** - * Current used size (elements) used - * @return size_t in elements - */ - size_t size(); - /*** - * Get maximal capacity of fifo - * @return size_t with max capacity of this fifo - */ - size_t getMaxCapacity() const; + /*** + * Check if FIFO is empty + * @return True if empty, False if not + */ + bool empty(); + /*** + * Check if FIFO is Full + * @return True if full, False if not + */ + bool full(); + /*** + * Current used size (elements) used + * @return size_t in elements + */ + size_t size(); + /*** + * Get maximal capacity of fifo + * @return size_t with max capacity of this fifo + */ + size_t getMaxCapacity() const; protected: - void setContainer(T* data); - size_t maxCapacity = 0; + void setContainer(T* data); + size_t maxCapacity = 0; - T* values; + T* values; - size_t readIndex = 0; - size_t writeIndex = 0; - size_t currentSize = 0; + size_t readIndex = 0; + size_t writeIndex = 0; + size_t currentSize = 0; - size_t next(size_t current); + size_t next(size_t current); }; #include "FIFOBase.tpp" diff --git a/container/FIFOBase.tpp b/container/FIFOBase.tpp index 763004b6f..2e6a3829c 100644 --- a/container/FIFOBase.tpp +++ b/container/FIFOBase.tpp @@ -7,87 +7,87 @@ template inline FIFOBase::FIFOBase(T* values, const size_t maxCapacity): - maxCapacity(maxCapacity), values(values){}; + maxCapacity(maxCapacity), values(values){}; template inline ReturnValue_t FIFOBase::insert(T value) { - if (full()) { - return FULL; - } else { - values[writeIndex] = value; - writeIndex = next(writeIndex); - ++currentSize; - return HasReturnvaluesIF::RETURN_OK; - } + if (full()) { + return FULL; + } else { + values[writeIndex] = value; + writeIndex = next(writeIndex); + ++currentSize; + return HasReturnvaluesIF::RETURN_OK; + } }; template inline ReturnValue_t FIFOBase::retrieve(T* value) { - if (empty()) { - return EMPTY; - } else { - if (value == nullptr){ - return HasReturnvaluesIF::RETURN_FAILED; - } - *value = values[readIndex]; - readIndex = next(readIndex); - --currentSize; - return HasReturnvaluesIF::RETURN_OK; - } + if (empty()) { + return EMPTY; + } else { + if (value == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } + *value = values[readIndex]; + readIndex = next(readIndex); + --currentSize; + return HasReturnvaluesIF::RETURN_OK; + } }; template inline ReturnValue_t FIFOBase::peek(T* value) { - if(empty()) { - return EMPTY; - } else { - if (value == nullptr){ - return HasReturnvaluesIF::RETURN_FAILED; - } - *value = values[readIndex]; - return HasReturnvaluesIF::RETURN_OK; - } + if(empty()) { + return EMPTY; + } else { + if (value == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } + *value = values[readIndex]; + return HasReturnvaluesIF::RETURN_OK; + } }; template inline ReturnValue_t FIFOBase::pop() { - T value; - return this->retrieve(&value); + T value; + return this->retrieve(&value); }; template inline bool FIFOBase::empty() { - return (currentSize == 0); + return (currentSize == 0); }; template inline bool FIFOBase::full() { - return (currentSize == maxCapacity); + return (currentSize == maxCapacity); } template inline size_t FIFOBase::size() { - return currentSize; + return currentSize; } template inline size_t FIFOBase::next(size_t current) { - ++current; - if (current == maxCapacity) { - current = 0; - } - return current; + ++current; + if (current == maxCapacity) { + current = 0; + } + return current; } template inline size_t FIFOBase::getMaxCapacity() const { - return maxCapacity; + return maxCapacity; } template inline void FIFOBase::setContainer(T *data) { - this->values = data; + this->values = data; } #endif diff --git a/container/FixedArrayList.h b/container/FixedArrayList.h index e9e127cf0..7af636b67 100644 --- a/container/FixedArrayList.h +++ b/container/FixedArrayList.h @@ -8,30 +8,32 @@ */ template class FixedArrayList: public ArrayList { - static_assert(MAX_SIZE <= (pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE"); +#if !defined(_MSC_VER) + static_assert(MAX_SIZE <= (std::pow(2,sizeof(count_t)*8)-1), "count_t is not large enough to hold MAX_SIZE"); +#endif private: - T data[MAX_SIZE]; + T data[MAX_SIZE]; public: - FixedArrayList() : - ArrayList(data, MAX_SIZE) { - } + FixedArrayList() : + ArrayList(data, MAX_SIZE) { + } - FixedArrayList(const FixedArrayList& other) : - ArrayList(data, MAX_SIZE) { - memcpy(this->data, other.data, sizeof(this->data)); - this->entries = data; - this->size = other.size; - } + FixedArrayList(const FixedArrayList& other) : + ArrayList(data, MAX_SIZE) { + memcpy(this->data, other.data, sizeof(this->data)); + this->entries = data; + this->size = other.size; + } - FixedArrayList& operator=(FixedArrayList other) { - memcpy(this->data, other.data, sizeof(this->data)); - this->entries = data; - this->size = other.size; - return *this; - } + FixedArrayList& operator=(FixedArrayList other) { + memcpy(this->data, other.data, sizeof(this->data)); + this->entries = data; + this->size = other.size; + return *this; + } - virtual ~FixedArrayList() { - } + virtual ~FixedArrayList() { + } }; diff --git a/container/FixedMap.h b/container/FixedMap.h index 7a5220faa..bd8e0f835 100644 --- a/container/FixedMap.h +++ b/container/FixedMap.h @@ -18,212 +18,212 @@ */ template class FixedMap: public SerializeIF { - static_assert (std::is_trivially_copyable::value or - std::is_base_of::value, - "Types used in FixedMap must either be trivial copy-able or a " - "derived class from SerializeIF to be serialize-able"); + static_assert (std::is_trivially_copyable::value or + std::is_base_of::value, + "Types used in FixedMap must either be trivial copy-able or a " + "derived class from SerializeIF to be serialize-able"); public: - static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP; - static const ReturnValue_t KEY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); - static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x02); - static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); + static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MAP; + static const ReturnValue_t KEY_ALREADY_EXISTS = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x02); + static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x03); private: - static const key_t EMPTY_SLOT = -1; - ArrayList, uint32_t> theMap; - uint32_t _size; + static const key_t EMPTY_SLOT = -1; + ArrayList, uint32_t> theMap; + uint32_t _size; - uint32_t findIndex(key_t key) const { - if (_size == 0) { - return 1; - } - uint32_t i = 0; - for (i = 0; i < _size; ++i) { - if (theMap[i].first == key) { - return i; - } - } - return i; - } + uint32_t findIndex(key_t key) const { + if (_size == 0) { + return 1; + } + uint32_t i = 0; + for (i = 0; i < _size; ++i) { + if (theMap[i].first == key) { + return i; + } + } + return i; + } public: - FixedMap(uint32_t maxSize) : - theMap(maxSize), _size(0) { - } + FixedMap(uint32_t maxSize) : + theMap(maxSize), _size(0) { + } - class Iterator: public ArrayList, uint32_t>::Iterator { - public: - Iterator() : - ArrayList, uint32_t>::Iterator() { - } + class Iterator: public ArrayList, uint32_t>::Iterator { + public: + Iterator() : + ArrayList, uint32_t>::Iterator() { + } - Iterator(std::pair *pair) : - ArrayList, uint32_t>::Iterator(pair) { - } - }; + Iterator(std::pair *pair) : + ArrayList, uint32_t>::Iterator(pair) { + } + }; - friend bool operator==(const typename FixedMap::Iterator& lhs, - const typename FixedMap::Iterator& rhs) { - return (lhs.value == rhs.value); - } + friend bool operator==(const typename FixedMap::Iterator& lhs, + const typename FixedMap::Iterator& rhs) { + return (lhs.value == rhs.value); + } - friend bool operator!=(const typename FixedMap::Iterator& lhs, - const typename FixedMap::Iterator& rhs) { - return not (lhs.value == rhs.value); - } + friend bool operator!=(const typename FixedMap::Iterator& lhs, + const typename FixedMap::Iterator& rhs) { + return not (lhs.value == rhs.value); + } - Iterator begin() const { - return Iterator(&theMap[0]); - } + Iterator begin() const { + return Iterator(&theMap[0]); + } - Iterator end() const { - return Iterator(&theMap[_size]); - } + Iterator end() const { + return Iterator(&theMap[_size]); + } - uint32_t size() const { - return _size; - } + uint32_t size() const { + return _size; + } - ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr) { - if (exists(key) == HasReturnvaluesIF::RETURN_OK) { - return KEY_ALREADY_EXISTS; - } - if (_size == theMap.maxSize()) { - return MAP_FULL; - } - theMap[_size].first = key; - theMap[_size].second = value; - if (storedValue != nullptr) { - *storedValue = Iterator(&theMap[_size]); - } - ++_size; - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr) { + if (exists(key) == HasReturnvaluesIF::RETURN_OK) { + return KEY_ALREADY_EXISTS; + } + if (_size == theMap.maxSize()) { + return MAP_FULL; + } + theMap[_size].first = key; + theMap[_size].second = value; + if (storedValue != nullptr) { + *storedValue = Iterator(&theMap[_size]); + } + ++_size; + return HasReturnvaluesIF::RETURN_OK; + } - ReturnValue_t insert(std::pair pair) { - return insert(pair.first, pair.second); - } + ReturnValue_t insert(std::pair pair) { + return insert(pair.first, pair.second); + } - ReturnValue_t exists(key_t key) const { - ReturnValue_t result = KEY_DOES_NOT_EXIST; - if (findIndex(key) < _size) { - result = HasReturnvaluesIF::RETURN_OK; - } - return result; - } + ReturnValue_t exists(key_t key) const { + ReturnValue_t result = KEY_DOES_NOT_EXIST; + if (findIndex(key) < _size) { + result = HasReturnvaluesIF::RETURN_OK; + } + return result; + } - ReturnValue_t erase(Iterator *iter) { - uint32_t i; - if ((i = findIndex((*iter).value->first)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - theMap[i] = theMap[_size - 1]; - --_size; - --((*iter).value); - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t erase(Iterator *iter) { + uint32_t i; + if ((i = findIndex((*iter).value->first)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + theMap[i] = theMap[_size - 1]; + --_size; + --((*iter).value); + return HasReturnvaluesIF::RETURN_OK; + } - ReturnValue_t erase(key_t key) { - uint32_t i; - if ((i = findIndex(key)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - theMap[i] = theMap[_size - 1]; - --_size; - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t erase(key_t key) { + uint32_t i; + if ((i = findIndex(key)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + theMap[i] = theMap[_size - 1]; + --_size; + return HasReturnvaluesIF::RETURN_OK; + } - T *findValue(key_t key) const { - return &theMap[findIndex(key)].second; - } + T *findValue(key_t key) const { + return &theMap[findIndex(key)].second; + } - Iterator find(key_t key) const { - ReturnValue_t result = exists(key); - if (result != HasReturnvaluesIF::RETURN_OK) { - return end(); - } - return Iterator(&theMap[findIndex(key)]); - } + Iterator find(key_t key) const { + ReturnValue_t result = exists(key); + if (result != HasReturnvaluesIF::RETURN_OK) { + return end(); + } + return Iterator(&theMap[findIndex(key)]); + } - ReturnValue_t find(key_t key, T **value) const { - ReturnValue_t result = exists(key); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - *value = &theMap[findIndex(key)].second; - return HasReturnvaluesIF::RETURN_OK; - } + ReturnValue_t find(key_t key, T **value) const { + ReturnValue_t result = exists(key); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + *value = &theMap[findIndex(key)].second; + return HasReturnvaluesIF::RETURN_OK; + } - bool empty() { - if(_size == 0) { - return true; - } - else { - return false; - } - } + bool empty() { + if(_size == 0) { + return true; + } + else { + return false; + } + } - bool full() { - if(_size >= theMap.maxSize()) { - return true; - } - else { - return false; - } - } + bool full() { + if(_size >= theMap.maxSize()) { + return true; + } + else { + return false; + } + } - void clear() { - _size = 0; - } + void clear() { + _size = 0; + } - uint32_t maxSize() const { - return theMap.maxSize(); - } + uint32_t maxSize() const { + return theMap.maxSize(); + } - virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - ReturnValue_t result = SerializeAdapter::serialize(&this->_size, - buffer, size, maxSize, streamEndianness); - uint32_t i = 0; - while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->_size)) { - result = SerializeAdapter::serialize(&theMap[i].first, buffer, - size, maxSize, streamEndianness); - result = SerializeAdapter::serialize(&theMap[i].second, buffer, size, - maxSize, streamEndianness); - ++i; - } - return result; - } + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + size_t maxSize, Endianness streamEndianness) const { + ReturnValue_t result = SerializeAdapter::serialize(&this->_size, + buffer, size, maxSize, streamEndianness); + uint32_t i = 0; + while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->_size)) { + result = SerializeAdapter::serialize(&theMap[i].first, buffer, + size, maxSize, streamEndianness); + result = SerializeAdapter::serialize(&theMap[i].second, buffer, size, + maxSize, streamEndianness); + ++i; + } + return result; + } - virtual size_t getSerializedSize() const { - uint32_t printSize = sizeof(_size); - uint32_t i = 0; + virtual size_t getSerializedSize() const { + uint32_t printSize = sizeof(_size); + uint32_t i = 0; - for (i = 0; i < _size; ++i) { - printSize += SerializeAdapter::getSerializedSize( - &theMap[i].first); - printSize += SerializeAdapter::getSerializedSize(&theMap[i].second); - } + for (i = 0; i < _size; ++i) { + printSize += SerializeAdapter::getSerializedSize( + &theMap[i].first); + printSize += SerializeAdapter::getSerializedSize(&theMap[i].second); + } - return printSize; - } + return printSize; + } - virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) { - ReturnValue_t result = SerializeAdapter::deSerialize(&this->_size, - buffer, size, streamEndianness); - if (this->_size > theMap.maxSize()) { - return SerializeIF::TOO_MANY_ELEMENTS; - } - uint32_t i = 0; - while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->_size)) { - result = SerializeAdapter::deSerialize(&theMap[i].first, buffer, - size, streamEndianness); - result = SerializeAdapter::deSerialize(&theMap[i].second, buffer, size, - streamEndianness); - ++i; - } - return result; - } + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness) { + ReturnValue_t result = SerializeAdapter::deSerialize(&this->_size, + buffer, size, streamEndianness); + if (this->_size > theMap.maxSize()) { + return SerializeIF::TOO_MANY_ELEMENTS; + } + uint32_t i = 0; + while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->_size)) { + result = SerializeAdapter::deSerialize(&theMap[i].first, buffer, + size, streamEndianness); + result = SerializeAdapter::deSerialize(&theMap[i].second, buffer, size, + streamEndianness); + ++i; + } + return result; + } }; diff --git a/container/FixedOrderedMultimap.h b/container/FixedOrderedMultimap.h index 96bc00735..cb8abdcaf 100644 --- a/container/FixedOrderedMultimap.h +++ b/container/FixedOrderedMultimap.h @@ -3,6 +3,7 @@ #include "ArrayList.h" #include +#include /** * @brief An associative container which allows multiple entries of the same key. @@ -33,172 +34,172 @@ template> class FixedOrderedMultimap { public: - static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MULTIMAP; - static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x01); - static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x02); + static const uint8_t INTERFACE_ID = CLASS_ID::FIXED_MULTIMAP; + static const ReturnValue_t MAP_FULL = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t KEY_DOES_NOT_EXIST = MAKE_RETURN_CODE(0x02); - /*** - * Constructor which needs a size_t for the maximum allowed size - * - * Can not be resized during runtime - * - * Allocates memory at construction - * @param maxSize size_t of Maximum allowed size - */ + /*** + * Constructor which needs a size_t for the maximum allowed size + * + * Can not be resized during runtime + * + * Allocates memory at construction + * @param maxSize size_t of Maximum allowed size + */ FixedOrderedMultimap(size_t maxSize):theMap(maxSize), _size(0){ } - /*** - * Virtual destructor frees Memory by deleting its member - */ - virtual ~FixedOrderedMultimap() { - } + /*** + * Virtual destructor frees Memory by deleting its member + */ + virtual ~FixedOrderedMultimap() { + } - /*** - * Special iterator for FixedOrderedMultimap - */ - class Iterator: public ArrayList, size_t>::Iterator { - public: - Iterator() : - ArrayList, size_t>::Iterator() { - } + /*** + * Special iterator for FixedOrderedMultimap + */ + class Iterator: public ArrayList, size_t>::Iterator { + public: + Iterator() : + ArrayList, size_t>::Iterator() { + } - Iterator(std::pair *pair) : - ArrayList, size_t>::Iterator(pair) { - } - }; + Iterator(std::pair *pair) : + ArrayList, size_t>::Iterator(pair) { + } + }; - /*** - * Returns an iterator pointing to the first element - * @return Iterator pointing to first element - */ - Iterator begin() const { - return Iterator(&theMap[0]); - } + /*** + * Returns an iterator pointing to the first element + * @return Iterator pointing to first element + */ + Iterator begin() const { + return Iterator(&theMap[0]); + } - /** - * Returns an iterator pointing to one element past the end - * @return Iterator pointing to one element past the end - */ - Iterator end() const { - return Iterator(&theMap[_size]); - } + /** + * Returns an iterator pointing to one element past the end + * @return Iterator pointing to one element past the end + */ + Iterator end() const { + return Iterator(&theMap[_size]); + } - /*** - * Returns the current size of the map (not maximum size!) - * @return Current size - */ - size_t size() const{ - return _size; - } + /*** + * Returns the current size of the map (not maximum size!) + * @return Current size + */ + size_t size() const{ + return _size; + } - /** - * Clears the map, does not deallocate any memory - */ - void clear(){ - _size = 0; - } + /** + * Clears the map, does not deallocate any memory + */ + void clear(){ + _size = 0; + } - /** - * Returns the maximum size of the map - * @return Maximum size of the map - */ - size_t maxSize() const{ - return theMap.maxSize(); - } + /** + * Returns the maximum size of the map + * @return Maximum size of the map + */ + size_t maxSize() const{ + return theMap.maxSize(); + } - /*** - * Used to insert a key and value separately. - * - * @param[in] key Key of the new element - * @param[in] value Value of the new element - * @param[in/out] (optional) storedValue On success this points to the new value, otherwise a nullptr - * @return RETURN_OK if insert was successful, MAP_FULL if no space is available - */ - ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr); + /*** + * Used to insert a key and value separately. + * + * @param[in] key Key of the new element + * @param[in] value Value of the new element + * @param[in/out] (optional) storedValue On success this points to the new value, otherwise a nullptr + * @return RETURN_OK if insert was successful, MAP_FULL if no space is available + */ + ReturnValue_t insert(key_t key, T value, Iterator *storedValue = nullptr); - /*** - * Used to insert new pair instead of single values - * - * @param pair Pair to be inserted - * @return RETURN_OK if insert was successful, MAP_FULL if no space is available - */ - ReturnValue_t insert(std::pair pair); + /*** + * Used to insert new pair instead of single values + * + * @param pair Pair to be inserted + * @return RETURN_OK if insert was successful, MAP_FULL if no space is available + */ + ReturnValue_t insert(std::pair pair); - /*** - * Can be used to check if a certain key is in the map - * @param key Key to be checked - * @return RETURN_OK if the key exists KEY_DOES_NOT_EXIST otherwise - */ - ReturnValue_t exists(key_t key) const; + /*** + * Can be used to check if a certain key is in the map + * @param key Key to be checked + * @return RETURN_OK if the key exists KEY_DOES_NOT_EXIST otherwise + */ + ReturnValue_t exists(key_t key) const; - /*** - * Used to delete the element in the iterator - * - * The iterator will point to the element before or begin(), - * but never to one element in front of the map. - * - * @warning The iterator needs to be valid and dereferenceable - * @param[in/out] iter Pointer to iterator to the element that needs to be ereased - * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this - */ - ReturnValue_t erase(Iterator *iter); + /*** + * Used to delete the element in the iterator + * + * The iterator will point to the element before or begin(), + * but never to one element in front of the map. + * + * @warning The iterator needs to be valid and dereferenceable + * @param[in/out] iter Pointer to iterator to the element that needs to be ereased + * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this + */ + ReturnValue_t erase(Iterator *iter); - /*** - * Used to erase by key - * @param key Key to be erased - * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this - */ - ReturnValue_t erase(key_t key); + /*** + * Used to erase by key + * @param key Key to be erased + * @return RETURN_OK if erased, KEY_DOES_NOT_EXIST if the there is no element like this + */ + ReturnValue_t erase(key_t key); - /*** - * Find returns the first appearance of the key - * - * If the key does not exist, it points to end() - * - * @param key Key to search for - * @return Iterator pointing to the first entry of key - */ - Iterator find(key_t key) const{ - ReturnValue_t result = exists(key); - if (result != HasReturnvaluesIF::RETURN_OK) { - return end(); - } - return Iterator(&theMap[findFirstIndex(key)]); - }; + /*** + * Find returns the first appearance of the key + * + * If the key does not exist, it points to end() + * + * @param key Key to search for + * @return Iterator pointing to the first entry of key + */ + Iterator find(key_t key) const{ + ReturnValue_t result = exists(key); + if (result != HasReturnvaluesIF::RETURN_OK) { + return end(); + } + return Iterator(&theMap[findFirstIndex(key)]); + }; - /*** - * Finds first entry of the given key and returns a - * pointer to the value - * - * @param key Key to search for - * @param value Found value - * @return RETURN_OK if it points to the value, - * KEY_DOES_NOT_EXIST if the key is not in the map - */ - ReturnValue_t find(key_t key, T **value) const; + /*** + * Finds first entry of the given key and returns a + * pointer to the value + * + * @param key Key to search for + * @param value Found value + * @return RETURN_OK if it points to the value, + * KEY_DOES_NOT_EXIST if the key is not in the map + */ + ReturnValue_t find(key_t key, T **value) const; - friend bool operator==(const typename FixedOrderedMultimap::Iterator& lhs, - const typename FixedOrderedMultimap::Iterator& rhs) { - return (lhs.value == rhs.value); - } + friend bool operator==(const typename FixedOrderedMultimap::Iterator& lhs, + const typename FixedOrderedMultimap::Iterator& rhs) { + return (lhs.value == rhs.value); + } - friend bool operator!=(const typename FixedOrderedMultimap::Iterator& lhs, - const typename FixedOrderedMultimap::Iterator& rhs) { - return not (lhs.value == rhs.value); - } + friend bool operator!=(const typename FixedOrderedMultimap::Iterator& lhs, + const typename FixedOrderedMultimap::Iterator& rhs) { + return not (lhs.value == rhs.value); + } private: - typedef KEY_COMPARE compare; - compare myComp; - ArrayList, size_t> theMap; - size_t _size; + typedef KEY_COMPARE compare; + compare myComp; + ArrayList, size_t> theMap; + size_t _size; - size_t findFirstIndex(key_t key, size_t startAt = 0) const; + size_t findFirstIndex(key_t key, size_t startAt = 0) const; - size_t findNicePlace(key_t key) const; + size_t findNicePlace(key_t key) const; - void removeFromPosition(size_t position); + void removeFromPosition(size_t position); }; #include "FixedOrderedMultimap.tpp" diff --git a/container/FixedOrderedMultimap.tpp b/container/FixedOrderedMultimap.tpp index 4aa85e974..294a161fe 100644 --- a/container/FixedOrderedMultimap.tpp +++ b/container/FixedOrderedMultimap.tpp @@ -4,105 +4,105 @@ template inline ReturnValue_t FixedOrderedMultimap::insert(key_t key, T value, Iterator *storedValue) { - if (_size == theMap.maxSize()) { - return MAP_FULL; - } - size_t position = findNicePlace(key); - memmove(static_cast(&theMap[position + 1]),static_cast(&theMap[position]), - (_size - position) * sizeof(std::pair)); - theMap[position].first = key; - theMap[position].second = value; - ++_size; - if (storedValue != nullptr) { - *storedValue = Iterator(&theMap[position]); - } - return HasReturnvaluesIF::RETURN_OK; + if (_size == theMap.maxSize()) { + return MAP_FULL; + } + size_t position = findNicePlace(key); + memmove(static_cast(&theMap[position + 1]),static_cast(&theMap[position]), + (_size - position) * sizeof(std::pair)); + theMap[position].first = key; + theMap[position].second = value; + ++_size; + if (storedValue != nullptr) { + *storedValue = Iterator(&theMap[position]); + } + return HasReturnvaluesIF::RETURN_OK; } template inline ReturnValue_t FixedOrderedMultimap::insert(std::pair pair) { - return insert(pair.first, pair.second); + return insert(pair.first, pair.second); } template inline ReturnValue_t FixedOrderedMultimap::exists(key_t key) const { - ReturnValue_t result = KEY_DOES_NOT_EXIST; - if (findFirstIndex(key) < _size) { - result = HasReturnvaluesIF::RETURN_OK; - } - return result; + ReturnValue_t result = KEY_DOES_NOT_EXIST; + if (findFirstIndex(key) < _size) { + result = HasReturnvaluesIF::RETURN_OK; + } + return result; } template inline ReturnValue_t FixedOrderedMultimap::erase(Iterator *iter) { - size_t i; - if ((i = findFirstIndex((*iter).value->first)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - removeFromPosition(i); - if (*iter != begin()) { - (*iter)--; - } else { - *iter = begin(); - } - return HasReturnvaluesIF::RETURN_OK; + size_t i; + if ((i = findFirstIndex((*iter).value->first)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + removeFromPosition(i); + if (*iter != begin()) { + (*iter)--; + } else { + *iter = begin(); + } + return HasReturnvaluesIF::RETURN_OK; } template inline ReturnValue_t FixedOrderedMultimap::erase(key_t key) { - size_t i; - if ((i = findFirstIndex(key)) >= _size) { - return KEY_DOES_NOT_EXIST; - } - do { - removeFromPosition(i); - i = findFirstIndex(key, i); - } while (i < _size); - return HasReturnvaluesIF::RETURN_OK; + size_t i; + if ((i = findFirstIndex(key)) >= _size) { + return KEY_DOES_NOT_EXIST; + } + do { + removeFromPosition(i); + i = findFirstIndex(key, i); + } while (i < _size); + return HasReturnvaluesIF::RETURN_OK; } template inline ReturnValue_t FixedOrderedMultimap::find(key_t key, T **value) const { - ReturnValue_t result = exists(key); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - *value = &theMap[findFirstIndex(key)].second; - return HasReturnvaluesIF::RETURN_OK; + ReturnValue_t result = exists(key); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + *value = &theMap[findFirstIndex(key)].second; + return HasReturnvaluesIF::RETURN_OK; } template inline size_t FixedOrderedMultimap::findFirstIndex(key_t key, size_t startAt) const { - if (startAt >= _size) { - return startAt + 1; - } - size_t i = startAt; - for (i = startAt; i < _size; ++i) { - if (theMap[i].first == key) { - return i; - } - } - return i; + if (startAt >= _size) { + return startAt + 1; + } + size_t i = startAt; + for (i = startAt; i < _size; ++i) { + if (theMap[i].first == key) { + return i; + } + } + return i; } template inline size_t FixedOrderedMultimap::findNicePlace(key_t key) const { - size_t i = 0; - for (i = 0; i < _size; ++i) { - if (myComp(key, theMap[i].first)) { - return i; - } - } - return i; + size_t i = 0; + for (i = 0; i < _size; ++i) { + if (myComp(key, theMap[i].first)) { + return i; + } + } + return i; } template inline void FixedOrderedMultimap::removeFromPosition(size_t position) { - if (_size <= position) { - return; - } - memmove(static_cast(&theMap[position]), static_cast(&theMap[position + 1]), - (_size - position - 1) * sizeof(std::pair)); - --_size; + if (_size <= position) { + return; + } + memmove(static_cast(&theMap[position]), static_cast(&theMap[position + 1]), + (_size - position - 1) * sizeof(std::pair)); + --_size; } diff --git a/container/HybridIterator.h b/container/HybridIterator.h index 8d020cb93..6e33e4611 100644 --- a/container/HybridIterator.h +++ b/container/HybridIterator.h @@ -6,85 +6,85 @@ template class HybridIterator: public LinkedElement::Iterator, - public ArrayList::Iterator { + public ArrayList::Iterator { public: - HybridIterator() {} + HybridIterator() {} - HybridIterator(typename LinkedElement::Iterator *iter) : - LinkedElement::Iterator(*iter), value(iter->value), - linked(true) { + HybridIterator(typename LinkedElement::Iterator *iter) : + LinkedElement::Iterator(*iter), value(iter->value), + linked(true) { - } + } - HybridIterator(LinkedElement *start) : - LinkedElement::Iterator(start), value(start->value), - linked(true) { + HybridIterator(LinkedElement *start) : + LinkedElement::Iterator(start), value(start->value), + linked(true) { - } + } - HybridIterator(typename ArrayList::Iterator start, - typename ArrayList::Iterator end) : - ArrayList::Iterator(start), value(start.value), - linked(false), end(end.value) { - if (value == this->end) { - value = NULL; - } - } + HybridIterator(typename ArrayList::Iterator start, + typename ArrayList::Iterator end) : + ArrayList::Iterator(start), value(start.value), + linked(false), end(end.value) { + if (value == this->end) { + value = NULL; + } + } - HybridIterator(T *firstElement, T *lastElement) : - ArrayList::Iterator(firstElement), value(firstElement), - linked(false), end(++lastElement) { - if (value == end) { - value = NULL; - } - } + HybridIterator(T *firstElement, T *lastElement) : + ArrayList::Iterator(firstElement), value(firstElement), + linked(false), end(++lastElement) { + if (value == end) { + value = NULL; + } + } - HybridIterator& operator++() { - if (linked) { - LinkedElement::Iterator::operator++(); - if (LinkedElement::Iterator::value != nullptr) { - value = LinkedElement::Iterator::value->value; - } else { - value = nullptr; - } - } else { - ArrayList::Iterator::operator++(); - value = ArrayList::Iterator::value; + HybridIterator& operator++() { + if (linked) { + LinkedElement::Iterator::operator++(); + if (LinkedElement::Iterator::value != nullptr) { + value = LinkedElement::Iterator::value->value; + } else { + value = nullptr; + } + } else { + ArrayList::Iterator::operator++(); + value = ArrayList::Iterator::value; - if (value == end) { - value = nullptr; - } - } - return *this; - } + if (value == end) { + value = nullptr; + } + } + return *this; + } - HybridIterator operator++(int) { - HybridIterator tmp(*this); - operator++(); - return tmp; - } + HybridIterator operator++(int) { + HybridIterator tmp(*this); + operator++(); + return tmp; + } - bool operator==(const HybridIterator& other) const { - return value == other.value; - } + bool operator==(const HybridIterator& other) const { + return value == other.value; + } - bool operator!=(const HybridIterator& other) const { - return !(*this == other); - } + bool operator!=(const HybridIterator& other) const { + return !(*this == other); + } - T operator*() { - return *value; - } + T operator*() { + return *value; + } - T *operator->() { - return value; - } + T *operator->() { + return value; + } - T* value = nullptr; + T* value = nullptr; private: - bool linked = false; - T *end = nullptr; + bool linked = false; + T *end = nullptr; }; #endif /* FRAMEWORK_CONTAINER_HYBRIDITERATOR_H_ */ diff --git a/container/IndexedRingMemoryArray.h b/container/IndexedRingMemoryArray.h index 0d85b49bc..df8980f7e 100644 --- a/container/IndexedRingMemoryArray.h +++ b/container/IndexedRingMemoryArray.h @@ -10,687 +10,687 @@ template class Index: public SerializeIF{ - /** - * Index is the Type used for the list of indices. The template parameter is the type which describes the index, it needs to be a child of SerializeIF to be able to make it persistent - */ - static_assert(std::is_base_of::value,"Wrong Type for Index, Type must implement SerializeIF"); + /** + * Index is the Type used for the list of indices. The template parameter is the type which describes the index, it needs to be a child of SerializeIF to be able to make it persistent + */ + static_assert(std::is_base_of::value,"Wrong Type for Index, Type must implement SerializeIF"); public: - Index():blockStartAddress(0),size(0),storedPackets(0){} + Index():blockStartAddress(0),size(0),storedPackets(0){} - Index(uint32_t startAddress):blockStartAddress(startAddress),size(0),storedPackets(0){ + Index(uint32_t startAddress):blockStartAddress(startAddress),size(0),storedPackets(0){ - } + } - void setBlockStartAddress(uint32_t newAddress){ - this->blockStartAddress = newAddress; - } + void setBlockStartAddress(uint32_t newAddress){ + this->blockStartAddress = newAddress; + } - uint32_t getBlockStartAddress() const { - return blockStartAddress; - } + uint32_t getBlockStartAddress() const { + return blockStartAddress; + } - const T* getIndexType() const { - return &indexType; - } + const T* getIndexType() const { + return &indexType; + } - T* modifyIndexType(){ - return &indexType; - } - /** - * Updates the index Type. Uses = operator - * @param indexType Type to copy from - */ - void setIndexType(T* indexType) { - this->indexType = *indexType; - } + T* modifyIndexType(){ + return &indexType; + } + /** + * Updates the index Type. Uses = operator + * @param indexType Type to copy from + */ + void setIndexType(T* indexType) { + this->indexType = *indexType; + } - uint32_t getSize() const { - return size; - } + uint32_t getSize() const { + return size; + } - void setSize(uint32_t size) { - this->size = size; - } + void setSize(uint32_t size) { + this->size = size; + } - void addSize(uint32_t size){ - this->size += size; - } + void addSize(uint32_t size){ + this->size += size; + } - void setStoredPackets(uint32_t newStoredPackets){ - this->storedPackets = newStoredPackets; - } + void setStoredPackets(uint32_t newStoredPackets){ + this->storedPackets = newStoredPackets; + } - void addStoredPackets(uint32_t packets){ - this->storedPackets += packets; - } + void addStoredPackets(uint32_t packets){ + this->storedPackets += packets; + } - uint32_t getStoredPackets() const{ - return this->storedPackets; - } + uint32_t getStoredPackets() const{ + return this->storedPackets; + } - ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - ReturnValue_t result = SerializeAdapter::serialize(&blockStartAddress,buffer,size,maxSize,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = indexType.serialize(buffer,size,maxSize,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = SerializeAdapter::serialize(&this->size,buffer,size,maxSize,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = SerializeAdapter::serialize(&this->storedPackets,buffer,size,maxSize,streamEndianness); - return result; - } + ReturnValue_t serialize(uint8_t** buffer, size_t* size, + size_t maxSize, Endianness streamEndianness) const { + ReturnValue_t result = SerializeAdapter::serialize(&blockStartAddress,buffer,size,maxSize,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = indexType.serialize(buffer,size,maxSize,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = SerializeAdapter::serialize(&this->size,buffer,size,maxSize,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = SerializeAdapter::serialize(&this->storedPackets,buffer,size,maxSize,streamEndianness); + return result; + } - ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness){ - ReturnValue_t result = SerializeAdapter::deSerialize(&blockStartAddress,buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = indexType.deSerialize(buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = SerializeAdapter::deSerialize(&this->size,buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = SerializeAdapter::deSerialize(&this->storedPackets,buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - return result; - } + ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness){ + ReturnValue_t result = SerializeAdapter::deSerialize(&blockStartAddress,buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = indexType.deSerialize(buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = SerializeAdapter::deSerialize(&this->size,buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = SerializeAdapter::deSerialize(&this->storedPackets,buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + return result; + } - size_t getSerializedSize() const { - uint32_t size = SerializeAdapter::getSerializedSize(&blockStartAddress); - size += indexType.getSerializedSize(); - size += SerializeAdapter::getSerializedSize(&this->size); - size += SerializeAdapter::getSerializedSize(&this->storedPackets); - return size; - } + size_t getSerializedSize() const { + uint32_t size = SerializeAdapter::getSerializedSize(&blockStartAddress); + size += indexType.getSerializedSize(); + size += SerializeAdapter::getSerializedSize(&this->size); + size += SerializeAdapter::getSerializedSize(&this->storedPackets); + return size; + } - bool operator==(const Index& other){ - return ((blockStartAddress == other.getBlockStartAddress()) && (size==other.getSize())) && (indexType == *(other.getIndexType())); - } + bool operator==(const Index& other){ + return ((blockStartAddress == other.getBlockStartAddress()) && (size==other.getSize())) && (indexType == *(other.getIndexType())); + } private: - uint32_t blockStartAddress; - uint32_t size; - uint32_t storedPackets; - T indexType; + uint32_t blockStartAddress; + uint32_t size; + uint32_t storedPackets; + T indexType; }; template class IndexedRingMemoryArray: public SerializeIF, public ArrayList, uint32_t>{ - /** - * Indexed Ring Memory Array is a class for a ring memory with indices. It assumes that the newest data comes in last - * It uses the currentWriteBlock as pointer to the current writing position - * The currentReadBlock must be set manually - */ + /** + * Indexed Ring Memory Array is a class for a ring memory with indices. It assumes that the newest data comes in last + * It uses the currentWriteBlock as pointer to the current writing position + * The currentReadBlock must be set manually + */ public: - IndexedRingMemoryArray(uint32_t startAddress, uint32_t size, uint32_t bytesPerBlock, SerializeIF* additionalInfo, - bool overwriteOld) :ArrayList,uint32_t>(NULL,(uint32_t)10,(uint32_t)0),totalSize(size),indexAddress(startAddress),currentReadSize(0),currentReadBlockSizeCached(0),lastBlockToReadSize(0), additionalInfo(additionalInfo),overwriteOld(overwriteOld){ + IndexedRingMemoryArray(uint32_t startAddress, uint32_t size, uint32_t bytesPerBlock, SerializeIF* additionalInfo, + bool overwriteOld) :ArrayList,uint32_t>(NULL,(uint32_t)10,(uint32_t)0),totalSize(size),indexAddress(startAddress),currentReadSize(0),currentReadBlockSizeCached(0),lastBlockToReadSize(0), additionalInfo(additionalInfo),overwriteOld(overwriteOld){ - //Calculate the maximum number of indices needed for this blocksize - uint32_t maxNrOfIndices = floor(static_cast(size)/static_cast(bytesPerBlock)); + //Calculate the maximum number of indices needed for this blocksize + uint32_t maxNrOfIndices = floor(static_cast(size)/static_cast(bytesPerBlock)); - //Calculate the Size needeed for the index itself - uint32_t serializedSize = 0; - if(additionalInfo!=NULL){ - serializedSize += additionalInfo->getSerializedSize(); - } - //Size of current iterator type - Index tempIndex; - serializedSize += tempIndex.getSerializedSize(); + //Calculate the Size needeed for the index itself + uint32_t serializedSize = 0; + if(additionalInfo!=NULL){ + serializedSize += additionalInfo->getSerializedSize(); + } + //Size of current iterator type + Index tempIndex; + serializedSize += tempIndex.getSerializedSize(); - //Add Size of Array - serializedSize += sizeof(uint32_t); //size of array - serializedSize += (tempIndex.getSerializedSize() * maxNrOfIndices); //size of elements - serializedSize += sizeof(uint16_t); //size of crc + //Add Size of Array + serializedSize += sizeof(uint32_t); //size of array + serializedSize += (tempIndex.getSerializedSize() * maxNrOfIndices); //size of elements + serializedSize += sizeof(uint16_t); //size of crc - //Calculate new size after index - if(serializedSize > totalSize){ - error << "IndexedRingMemory: Store is too small for index" << std::endl; - } - uint32_t useableSize = totalSize - serializedSize; - //Update the totalSize for calculations - totalSize = useableSize; + //Calculate new size after index + if(serializedSize > totalSize){ + error << "IndexedRingMemory: Store is too small for index" << std::endl; + } + uint32_t useableSize = totalSize - serializedSize; + //Update the totalSize for calculations + totalSize = useableSize; - //True StartAddress - uint32_t trueStartAddress = indexAddress + serializedSize; + //True StartAddress + uint32_t trueStartAddress = indexAddress + serializedSize; - //Calculate True number of Blocks and reset size of true Number of Blocks - uint32_t trueNumberOfBlocks = floor(static_cast(totalSize) / static_cast(bytesPerBlock)); + //Calculate True number of Blocks and reset size of true Number of Blocks + uint32_t trueNumberOfBlocks = floor(static_cast(totalSize) / static_cast(bytesPerBlock)); - //allocate memory now - this->entries = new Index[trueNumberOfBlocks]; - this->size = trueNumberOfBlocks; - this->maxSize_ = trueNumberOfBlocks; - this->allocated = true; + //allocate memory now + this->entries = new Index[trueNumberOfBlocks]; + this->size = trueNumberOfBlocks; + this->maxSize_ = trueNumberOfBlocks; + this->allocated = true; - //Check trueNumberOfBlocks - if(trueNumberOfBlocks<1){ - error << "IndexedRingMemory: Invalid Number of Blocks: " << trueNumberOfBlocks; - } + //Check trueNumberOfBlocks + if(trueNumberOfBlocks<1){ + error << "IndexedRingMemory: Invalid Number of Blocks: " << trueNumberOfBlocks; + } - //Fill address into index - uint32_t address = trueStartAddress; - for (typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it) { - it->setBlockStartAddress(address); - it->setSize(0); - it->setStoredPackets(0); - address += bytesPerBlock; - } + //Fill address into index + uint32_t address = trueStartAddress; + for (typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it) { + it->setBlockStartAddress(address); + it->setSize(0); + it->setStoredPackets(0); + address += bytesPerBlock; + } - //Initialize iterators - currentWriteBlock = this->begin(); - currentReadBlock = this->begin(); - lastBlockToRead = this->begin(); + //Initialize iterators + currentWriteBlock = this->begin(); + currentReadBlock = this->begin(); + lastBlockToRead = this->begin(); - //Check last blockSize - uint32_t lastBlockSize = (trueStartAddress + useableSize) - (this->back()->getBlockStartAddress()); - if((lastBlockSizesize > 1)){ - //remove the last Block so the second last block has more size - this->size -= 1; - debug << "IndexedRingMemory: Last Block is smaller than bytesPerBlock, removed last block" << std::endl; - } - } + //Check last blockSize + uint32_t lastBlockSize = (trueStartAddress + useableSize) - (this->back()->getBlockStartAddress()); + if((lastBlockSizesize > 1)){ + //remove the last Block so the second last block has more size + this->size -= 1; + debug << "IndexedRingMemory: Last Block is smaller than bytesPerBlock, removed last block" << std::endl; + } + } - /** - * Resets the whole index, the iterators and executes the given reset function on every index type - * @param typeResetFnc static reset function which accepts a pointer to the index Type - */ - void reset(void (*typeResetFnc)(T*)){ - currentReadBlock = this->begin(); - currentWriteBlock = this->begin(); - lastBlockToRead = this->begin(); - currentReadSize = 0; - currentReadBlockSizeCached = 0; - lastBlockToReadSize = 0; - for(typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it){ - it->setSize(0); - it->setStoredPackets(0); - (*typeResetFnc)(it->modifyIndexType()); - } - } + /** + * Resets the whole index, the iterators and executes the given reset function on every index type + * @param typeResetFnc static reset function which accepts a pointer to the index Type + */ + void reset(void (*typeResetFnc)(T*)){ + currentReadBlock = this->begin(); + currentWriteBlock = this->begin(); + lastBlockToRead = this->begin(); + currentReadSize = 0; + currentReadBlockSizeCached = 0; + lastBlockToReadSize = 0; + for(typename IndexedRingMemoryArray::Iterator it = this->begin();it!=this->end();++it){ + it->setSize(0); + it->setStoredPackets(0); + (*typeResetFnc)(it->modifyIndexType()); + } + } - void resetBlock(typename IndexedRingMemoryArray::Iterator it,void (*typeResetFnc)(T*)){ - it->setSize(0); - it->setStoredPackets(0); - (*typeResetFnc)(it->modifyIndexType()); - } + void resetBlock(typename IndexedRingMemoryArray::Iterator it,void (*typeResetFnc)(T*)){ + it->setSize(0); + it->setStoredPackets(0); + (*typeResetFnc)(it->modifyIndexType()); + } - /* - * Reading - */ + /* + * Reading + */ - void setCurrentReadBlock(typename IndexedRingMemoryArray::Iterator it){ - currentReadBlock = it; - currentReadBlockSizeCached = it->getSize(); - } + void setCurrentReadBlock(typename IndexedRingMemoryArray::Iterator it){ + currentReadBlock = it; + currentReadBlockSizeCached = it->getSize(); + } - void resetRead(){ - currentReadBlock = this->begin(); - currentReadSize = 0; - currentReadBlockSizeCached = this->begin()->getSize(); - lastBlockToRead = currentWriteBlock; - lastBlockToReadSize = currentWriteBlock->getSize(); - } - /** - * Sets the last block to read to this iterator. - * Can be used to dump until block x - * @param it The iterator for the last read block - */ - void setLastBlockToRead(typename IndexedRingMemoryArray::Iterator it){ - lastBlockToRead = it; - lastBlockToReadSize = it->getSize(); - } + void resetRead(){ + currentReadBlock = this->begin(); + currentReadSize = 0; + currentReadBlockSizeCached = this->begin()->getSize(); + lastBlockToRead = currentWriteBlock; + lastBlockToReadSize = currentWriteBlock->getSize(); + } + /** + * Sets the last block to read to this iterator. + * Can be used to dump until block x + * @param it The iterator for the last read block + */ + void setLastBlockToRead(typename IndexedRingMemoryArray::Iterator it){ + lastBlockToRead = it; + lastBlockToReadSize = it->getSize(); + } - /** - * Set the read pointer to the first written Block, which is the first non empty block in front of the write block - * Can be the currentWriteBlock as well - */ - void readOldest(){ - resetRead(); - currentReadBlock = getNextNonEmptyBlock(); - currentReadBlockSizeCached = currentReadBlock->getSize(); + /** + * Set the read pointer to the first written Block, which is the first non empty block in front of the write block + * Can be the currentWriteBlock as well + */ + void readOldest(){ + resetRead(); + currentReadBlock = getNextNonEmptyBlock(); + currentReadBlockSizeCached = currentReadBlock->getSize(); - } + } - /** - * Sets the current read iterator to the next Block and resets the current read size - * The current size of the block will be cached to avoid race condition between write and read - * If the end of the ring is reached the read pointer will be set to the begin - */ - void readNext(){ - currentReadSize = 0; - if((this->size != 0) && (currentReadBlock.value ==this->back())){ - currentReadBlock = this->begin(); - }else{ - currentReadBlock++; - } + /** + * Sets the current read iterator to the next Block and resets the current read size + * The current size of the block will be cached to avoid race condition between write and read + * If the end of the ring is reached the read pointer will be set to the begin + */ + void readNext(){ + currentReadSize = 0; + if((this->size != 0) && (currentReadBlock.value ==this->back())){ + currentReadBlock = this->begin(); + }else{ + currentReadBlock++; + } - currentReadBlockSizeCached = currentReadBlock->getSize(); - } + currentReadBlockSizeCached = currentReadBlock->getSize(); + } - /** - * Returns the address which is currently read from - * @return Address to read from - */ - uint32_t getCurrentReadAddress() const { - return getAddressOfCurrentReadBlock() + currentReadSize; - } - /** - * Adds readSize to the current size and checks if the read has no more data left and advances the read block - * @param readSize The size that was read - * @return Returns true if the read can go on - */ - bool addReadSize(uint32_t readSize) { - if(currentReadBlock == lastBlockToRead){ - //The current read block is the last to read - if((currentReadSize+readSize) return true - currentReadSize += readSize; - return true; - }else{ - //Reached end of read -> return false - currentReadSize = lastBlockToReadSize; - return false; - } - }else{ - //We are not in the last Block - if((currentReadSize + readSize)::Iterator it(currentReadBlock); - //Search if any block between this and the last block is not empty - for(;it!=lastBlockToRead;++it){ - if(it == this->end()){ - //This is the end, next block is the begin - it = this->begin(); - if(it == lastBlockToRead){ - //Break if the begin is the lastBlockToRead - break; - } - } - if(it->getSize()!=0){ - //This is a non empty block. Go on reading with this block - currentReadBlock = it; - currentReadBlockSizeCached = it->getSize(); - return true; - } - } - //reached lastBlockToRead and every block was empty, check if the last block is also empty - if(lastBlockToReadSize!=0){ - //go on with last Block - currentReadBlock = lastBlockToRead; - currentReadBlockSizeCached = lastBlockToReadSize; - return true; - } - //There is no non empty block left - return false; - } - //Size is larger than 0 - return true; - } - } - } - uint32_t getRemainigSizeOfCurrentReadBlock() const{ - if(currentReadBlock == lastBlockToRead){ - return (lastBlockToReadSize - currentReadSize); - }else{ - return (currentReadBlockSizeCached - currentReadSize); - } - } + /** + * Returns the address which is currently read from + * @return Address to read from + */ + uint32_t getCurrentReadAddress() const { + return getAddressOfCurrentReadBlock() + currentReadSize; + } + /** + * Adds readSize to the current size and checks if the read has no more data left and advances the read block + * @param readSize The size that was read + * @return Returns true if the read can go on + */ + bool addReadSize(uint32_t readSize) { + if(currentReadBlock == lastBlockToRead){ + //The current read block is the last to read + if((currentReadSize+readSize) return true + currentReadSize += readSize; + return true; + }else{ + //Reached end of read -> return false + currentReadSize = lastBlockToReadSize; + return false; + } + }else{ + //We are not in the last Block + if((currentReadSize + readSize)::Iterator it(currentReadBlock); + //Search if any block between this and the last block is not empty + for(;it!=lastBlockToRead;++it){ + if(it == this->end()){ + //This is the end, next block is the begin + it = this->begin(); + if(it == lastBlockToRead){ + //Break if the begin is the lastBlockToRead + break; + } + } + if(it->getSize()!=0){ + //This is a non empty block. Go on reading with this block + currentReadBlock = it; + currentReadBlockSizeCached = it->getSize(); + return true; + } + } + //reached lastBlockToRead and every block was empty, check if the last block is also empty + if(lastBlockToReadSize!=0){ + //go on with last Block + currentReadBlock = lastBlockToRead; + currentReadBlockSizeCached = lastBlockToReadSize; + return true; + } + //There is no non empty block left + return false; + } + //Size is larger than 0 + return true; + } + } + } + uint32_t getRemainigSizeOfCurrentReadBlock() const{ + if(currentReadBlock == lastBlockToRead){ + return (lastBlockToReadSize - currentReadSize); + }else{ + return (currentReadBlockSizeCached - currentReadSize); + } + } - uint32_t getAddressOfCurrentReadBlock() const { - return currentReadBlock->getBlockStartAddress(); - } + uint32_t getAddressOfCurrentReadBlock() const { + return currentReadBlock->getBlockStartAddress(); + } - /** - * Gets the next non empty Block after the current write block, - * @return Returns the iterator to the block. If there is non, the current write block is returned - */ - typename IndexedRingMemoryArray::Iterator getNextNonEmptyBlock() const { - for(typename IndexedRingMemoryArray::Iterator it = getNextWrite();it!=currentWriteBlock;++it){ - if(it == this->end()){ - it = this->begin(); - if(it == currentWriteBlock){ - break; - } - } - if(it->getSize()!=0){ - return it; - } - } - return currentWriteBlock; - } + /** + * Gets the next non empty Block after the current write block, + * @return Returns the iterator to the block. If there is non, the current write block is returned + */ + typename IndexedRingMemoryArray::Iterator getNextNonEmptyBlock() const { + for(typename IndexedRingMemoryArray::Iterator it = getNextWrite();it!=currentWriteBlock;++it){ + if(it == this->end()){ + it = this->begin(); + if(it == currentWriteBlock){ + break; + } + } + if(it->getSize()!=0){ + return it; + } + } + return currentWriteBlock; + } - /** - * Returns a copy of the oldest Index type - * @return Type of Index - */ - T* getOldest(){ - return (getNextNonEmptyBlock()->modifyIndexType()); - } + /** + * Returns a copy of the oldest Index type + * @return Type of Index + */ + T* getOldest(){ + return (getNextNonEmptyBlock()->modifyIndexType()); + } - /* - * Writing - */ - uint32_t getAddressOfCurrentWriteBlock() const{ - return currentWriteBlock->getBlockStartAddress(); - } + /* + * Writing + */ + uint32_t getAddressOfCurrentWriteBlock() const{ + return currentWriteBlock->getBlockStartAddress(); + } - uint32_t getSizeOfCurrentWriteBlock() const{ - return currentWriteBlock->getSize(); - } + uint32_t getSizeOfCurrentWriteBlock() const{ + return currentWriteBlock->getSize(); + } - uint32_t getCurrentWriteAddress() const{ - return getAddressOfCurrentWriteBlock() + getSizeOfCurrentWriteBlock(); - } + uint32_t getCurrentWriteAddress() const{ + return getAddressOfCurrentWriteBlock() + getSizeOfCurrentWriteBlock(); + } - void clearCurrentWriteBlock(){ - currentWriteBlock->setSize(0); - currentWriteBlock->setStoredPackets(0); - } + void clearCurrentWriteBlock(){ + currentWriteBlock->setSize(0); + currentWriteBlock->setStoredPackets(0); + } - void addCurrentWriteBlock(uint32_t size, uint32_t storedPackets){ - currentWriteBlock->addSize(size); - currentWriteBlock->addStoredPackets(storedPackets); - } + void addCurrentWriteBlock(uint32_t size, uint32_t storedPackets){ + currentWriteBlock->addSize(size); + currentWriteBlock->addStoredPackets(storedPackets); + } - T* modifyCurrentWriteBlockIndexType(){ - return currentWriteBlock->modifyIndexType(); - } - void updatePreviousWriteSize(uint32_t size, uint32_t storedPackets){ - typename IndexedRingMemoryArray::Iterator it = getPreviousBlock(currentWriteBlock); - it->addSize(size); - it->addStoredPackets(storedPackets); - } + T* modifyCurrentWriteBlockIndexType(){ + return currentWriteBlock->modifyIndexType(); + } + void updatePreviousWriteSize(uint32_t size, uint32_t storedPackets){ + typename IndexedRingMemoryArray::Iterator it = getPreviousBlock(currentWriteBlock); + it->addSize(size); + it->addStoredPackets(storedPackets); + } - /** - * Checks if the block has enough space for sizeToWrite - * @param sizeToWrite The data to be written in the Block - * @return Returns true if size to write is smaller the remaining size of the block - */ - bool hasCurrentWriteBlockEnoughSpace(uint32_t sizeToWrite){ - typename IndexedRingMemoryArray::Iterator next = getNextWrite(); - uint32_t addressOfNextBlock = next->getBlockStartAddress(); - uint32_t availableSize = ((addressOfNextBlock+totalSize) - (getAddressOfCurrentWriteBlock()+getSizeOfCurrentWriteBlock()))%totalSize; - return (sizeToWrite < availableSize); - } + /** + * Checks if the block has enough space for sizeToWrite + * @param sizeToWrite The data to be written in the Block + * @return Returns true if size to write is smaller the remaining size of the block + */ + bool hasCurrentWriteBlockEnoughSpace(uint32_t sizeToWrite){ + typename IndexedRingMemoryArray::Iterator next = getNextWrite(); + uint32_t addressOfNextBlock = next->getBlockStartAddress(); + uint32_t availableSize = ((addressOfNextBlock+totalSize) - (getAddressOfCurrentWriteBlock()+getSizeOfCurrentWriteBlock()))%totalSize; + return (sizeToWrite < availableSize); + } - /** - * Checks if the store is full if overwrite old is false - * @return Returns true if it is writeable and false if not - */ - bool isNextBlockWritable(){ - //First check if this is the end of the list - typename IndexedRingMemoryArray::Iterator next; - next = getNextWrite(); - if((next->getSize()!=0) && (!overwriteOld)){ - return false; - } - return true; - } + /** + * Checks if the store is full if overwrite old is false + * @return Returns true if it is writeable and false if not + */ + bool isNextBlockWritable(){ + //First check if this is the end of the list + typename IndexedRingMemoryArray::Iterator next; + next = getNextWrite(); + if((next->getSize()!=0) && (!overwriteOld)){ + return false; + } + return true; + } - /** - * Updates current write Block Index Type - * @param infoOfNewBlock - */ - void updateCurrentBlock(T* infoOfNewBlock){ - currentWriteBlock->setIndexType(infoOfNewBlock); - } + /** + * Updates current write Block Index Type + * @param infoOfNewBlock + */ + void updateCurrentBlock(T* infoOfNewBlock){ + currentWriteBlock->setIndexType(infoOfNewBlock); + } - /** - * Succeed to next block, returns FAILED if overwrite is false and the store is full - * @return - */ - ReturnValue_t writeNext(){ - //Check Next Block - if(!isNextBlockWritable()){ - //The Index is full and does not overwrite old - return HasReturnvaluesIF::RETURN_FAILED; - } - //Next block can be written, update Metadata - currentWriteBlock = getNextWrite(); - currentWriteBlock->setSize(0); - currentWriteBlock->setStoredPackets(0); - return HasReturnvaluesIF::RETURN_OK; - } + /** + * Succeed to next block, returns FAILED if overwrite is false and the store is full + * @return + */ + ReturnValue_t writeNext(){ + //Check Next Block + if(!isNextBlockWritable()){ + //The Index is full and does not overwrite old + return HasReturnvaluesIF::RETURN_FAILED; + } + //Next block can be written, update Metadata + currentWriteBlock = getNextWrite(); + currentWriteBlock->setSize(0); + currentWriteBlock->setStoredPackets(0); + return HasReturnvaluesIF::RETURN_OK; + } - /** - * Serializes the Index and calculates the CRC. - * Parameters according to HasSerializeIF - * @param buffer - * @param size - * @param maxSize - * @param streamEndianness - * @return - */ - ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const{ - uint8_t* crcBuffer = *buffer; - uint32_t oldSize = *size; - if(additionalInfo!=NULL){ - additionalInfo->serialize(buffer,size,maxSize,streamEndianness); - } - ReturnValue_t result = currentWriteBlock->serialize(buffer,size,maxSize,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - result = SerializeAdapter::serialize(&this->size,buffer,size,maxSize,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } + /** + * Serializes the Index and calculates the CRC. + * Parameters according to HasSerializeIF + * @param buffer + * @param size + * @param maxSize + * @param streamEndianness + * @return + */ + ReturnValue_t serialize(uint8_t** buffer, size_t* size, + size_t maxSize, Endianness streamEndianness) const{ + uint8_t* crcBuffer = *buffer; + uint32_t oldSize = *size; + if(additionalInfo!=NULL){ + additionalInfo->serialize(buffer,size,maxSize,streamEndianness); + } + ReturnValue_t result = currentWriteBlock->serialize(buffer,size,maxSize,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + result = SerializeAdapter::serialize(&this->size,buffer,size,maxSize,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } - uint32_t i = 0; - while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { - result = SerializeAdapter::serialize(&this->entries[i], buffer, size, - maxSize, streamEndianness); - ++i; - } - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - uint16_t crc = Calculate_CRC(crcBuffer,(*size-oldSize)); - result = SerializeAdapter::serialize(&crc,buffer,size,maxSize,streamEndianness); - return result; - } + uint32_t i = 0; + while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { + result = SerializeAdapter::serialize(&this->entries[i], buffer, size, + maxSize, streamEndianness); + ++i; + } + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + uint16_t crc = Calculate_CRC(crcBuffer,(*size-oldSize)); + result = SerializeAdapter::serialize(&crc,buffer,size,maxSize,streamEndianness); + return result; + } - /** - * Get the serialized Size of the index - * @return The serialized size of the index - */ - size_t getSerializedSize() const { + /** + * Get the serialized Size of the index + * @return The serialized size of the index + */ + size_t getSerializedSize() const { - uint32_t size = 0; - if(additionalInfo!=NULL){ - size += additionalInfo->getSerializedSize(); - } - size += currentWriteBlock->getSerializedSize(); - size += SerializeAdapter::getSerializedSize(&this->size); - size += (this->entries[0].getSerializedSize()) * this->size; - uint16_t crc = 0; - size += SerializeAdapter::getSerializedSize(&crc); - return size; - } - /** - * DeSerialize the Indexed Ring from a buffer, deSerializes the current write iterator - * CRC Has to be checked before! - * @param buffer - * @param size - * @param streamEndianness - * @return - */ + uint32_t size = 0; + if(additionalInfo!=NULL){ + size += additionalInfo->getSerializedSize(); + } + size += currentWriteBlock->getSerializedSize(); + size += SerializeAdapter::getSerializedSize(&this->size); + size += (this->entries[0].getSerializedSize()) * this->size; + uint16_t crc = 0; + size += SerializeAdapter::getSerializedSize(&crc); + return size; + } + /** + * DeSerialize the Indexed Ring from a buffer, deSerializes the current write iterator + * CRC Has to be checked before! + * @param buffer + * @param size + * @param streamEndianness + * @return + */ - ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness){ + ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + Endianness streamEndianness){ - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - if(additionalInfo!=NULL){ - result = additionalInfo->deSerialize(buffer,size,streamEndianness); - } - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + if(additionalInfo!=NULL){ + result = additionalInfo->deSerialize(buffer,size,streamEndianness); + } + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } - Index tempIndex; - result = tempIndex.deSerialize(buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - uint32_t tempSize = 0; - result = SerializeAdapter::deSerialize(&tempSize,buffer,size,streamEndianness); - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - if(this->size != tempSize){ - return HasReturnvaluesIF::RETURN_FAILED; - } - uint32_t i = 0; - while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { - result = SerializeAdapter::deSerialize( - &this->entries[i], buffer, size, - streamEndianness); - ++i; - } - if(result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - typename IndexedRingMemoryArray::Iterator cmp(&tempIndex); - for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ - if(*(cmp.value) == *(it.value)){ - currentWriteBlock = it; - return HasReturnvaluesIF::RETURN_OK; - } - } - //Reached if current write block iterator is not found - return HasReturnvaluesIF::RETURN_FAILED; - } + Index tempIndex; + result = tempIndex.deSerialize(buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + uint32_t tempSize = 0; + result = SerializeAdapter::deSerialize(&tempSize,buffer,size,streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + if(this->size != tempSize){ + return HasReturnvaluesIF::RETURN_FAILED; + } + uint32_t i = 0; + while ((result == HasReturnvaluesIF::RETURN_OK) && (i < this->size)) { + result = SerializeAdapter::deSerialize( + &this->entries[i], buffer, size, + streamEndianness); + ++i; + } + if(result != HasReturnvaluesIF::RETURN_OK){ + return result; + } + typename IndexedRingMemoryArray::Iterator cmp(&tempIndex); + for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ + if(*(cmp.value) == *(it.value)){ + currentWriteBlock = it; + return HasReturnvaluesIF::RETURN_OK; + } + } + //Reached if current write block iterator is not found + return HasReturnvaluesIF::RETURN_FAILED; + } - uint32_t getIndexAddress() const { - return indexAddress; - } + uint32_t getIndexAddress() const { + return indexAddress; + } - /* - * Statistics - */ - uint32_t getStoredPackets() const { - uint32_t size = 0; - for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ - size += it->getStoredPackets(); - } - return size; - } + /* + * Statistics + */ + uint32_t getStoredPackets() const { + uint32_t size = 0; + for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ + size += it->getStoredPackets(); + } + return size; + } - uint32_t getTotalSize() const { - return totalSize; - } + uint32_t getTotalSize() const { + return totalSize; + } - uint32_t getCurrentSize() const{ - uint32_t size = 0; - for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ - size += it->getSize(); - } - return size; - } + uint32_t getCurrentSize() const{ + uint32_t size = 0; + for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ + size += it->getSize(); + } + return size; + } - bool isEmpty() const{ - return getCurrentSize()==0; - } + bool isEmpty() const{ + return getCurrentSize()==0; + } - double getPercentageFilled() const{ - uint32_t filledSize = 0; - for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ - filledSize += it->getSize(); - } + double getPercentageFilled() const{ + uint32_t filledSize = 0; + for(typename IndexedRingMemoryArray::Iterator it= this->begin();it!=this->end();++it){ + filledSize += it->getSize(); + } - return (double)filledSize/(double)this->totalSize; - } + return (double)filledSize/(double)this->totalSize; + } - typename IndexedRingMemoryArray::Iterator getCurrentWriteBlock() const{ - return currentWriteBlock; - } - /** - * Get the next block of the currentWriteBlock. - * Returns the first one if currentWriteBlock is the last one - * @return Iterator pointing to the next block after currentWriteBlock - */ - typename IndexedRingMemoryArray::Iterator getNextWrite() const{ - typename IndexedRingMemoryArray::Iterator next(currentWriteBlock); - if((this->size != 0) && (currentWriteBlock.value == this->back())){ - next = this->begin(); - }else{ - ++next; - } - return next; - } - /** - * Get the block in front of the Iterator - * Returns the last block if it is the first block - * @param it iterator which you want the previous block from - * @return pointing to the block before it - */ - typename IndexedRingMemoryArray::Iterator getPreviousBlock(typename IndexedRingMemoryArray::Iterator it) { - if(this->begin() == it){ - typename IndexedRingMemoryArray::Iterator next((this->back())); - return next; - } - typename IndexedRingMemoryArray::Iterator next(it); - --next; - return next; - } + typename IndexedRingMemoryArray::Iterator getCurrentWriteBlock() const{ + return currentWriteBlock; + } + /** + * Get the next block of the currentWriteBlock. + * Returns the first one if currentWriteBlock is the last one + * @return Iterator pointing to the next block after currentWriteBlock + */ + typename IndexedRingMemoryArray::Iterator getNextWrite() const{ + typename IndexedRingMemoryArray::Iterator next(currentWriteBlock); + if((this->size != 0) && (currentWriteBlock.value == this->back())){ + next = this->begin(); + }else{ + ++next; + } + return next; + } + /** + * Get the block in front of the Iterator + * Returns the last block if it is the first block + * @param it iterator which you want the previous block from + * @return pointing to the block before it + */ + typename IndexedRingMemoryArray::Iterator getPreviousBlock(typename IndexedRingMemoryArray::Iterator it) { + if(this->begin() == it){ + typename IndexedRingMemoryArray::Iterator next((this->back())); + return next; + } + typename IndexedRingMemoryArray::Iterator next(it); + --next; + return next; + } private: - //The total size used by the blocks (without index) - uint32_t totalSize; + //The total size used by the blocks (without index) + uint32_t totalSize; - //The address of the index - const uint32_t indexAddress; + //The address of the index + const uint32_t indexAddress; - //The iterators for writing and reading - typename IndexedRingMemoryArray::Iterator currentWriteBlock; - typename IndexedRingMemoryArray::Iterator currentReadBlock; + //The iterators for writing and reading + typename IndexedRingMemoryArray::Iterator currentWriteBlock; + typename IndexedRingMemoryArray::Iterator currentReadBlock; - //How much of the current read block is read already - uint32_t currentReadSize; + //How much of the current read block is read already + uint32_t currentReadSize; - //Cached Size of current read block - uint32_t currentReadBlockSizeCached; + //Cached Size of current read block + uint32_t currentReadBlockSizeCached; - //Last block of current write (should be write block) - typename IndexedRingMemoryArray::Iterator lastBlockToRead; - //current size of last Block to read - uint32_t lastBlockToReadSize; + //Last block of current write (should be write block) + typename IndexedRingMemoryArray::Iterator lastBlockToRead; + //current size of last Block to read + uint32_t lastBlockToReadSize; - //Additional Info to be serialized with the index - SerializeIF* additionalInfo; + //Additional Info to be serialized with the index + SerializeIF* additionalInfo; - //Does it overwrite old blocks? - const bool overwriteOld; + //Does it overwrite old blocks? + const bool overwriteOld; }; diff --git a/container/PlacementFactory.h b/container/PlacementFactory.h index a0aebb7d1..94b4aeefd 100644 --- a/container/PlacementFactory.h +++ b/container/PlacementFactory.h @@ -22,50 +22,50 @@ */ class PlacementFactory { public: - PlacementFactory(StorageManagerIF* backend) : - dataBackend(backend) { - } + PlacementFactory(StorageManagerIF* backend) : + dataBackend(backend) { + } - /*** - * Generates an object of type T in the backend storage. - * - * @warning Do not use with any Type that allocates memory internally! - * - * @tparam T Type of Object - * @param args Constructor Arguments to be passed - * @return A pointer to the new object or a nullptr in case of failure - */ - template - T* generate(Args&&... args) { - store_address_t tempId; - uint8_t* pData = nullptr; - ReturnValue_t result = dataBackend->getFreeElement(&tempId, sizeof(T), - &pData); - if (result != HasReturnvaluesIF::RETURN_OK) { - return nullptr; - } - T* temp = new (pData) T(std::forward(args)...); - return temp; - } - /*** - * Function to destroy the object allocated with generate and free space in backend. - * This must be called by the user. - * - * @param thisElement Element to be destroyed - * @return RETURN_OK if the element was destroyed, different errors on failure - */ - template - ReturnValue_t destroy(T* thisElement) { - if (thisElement == nullptr){ - return HasReturnvaluesIF::RETURN_FAILED; - } - //Need to call destructor first, in case something was allocated by the object (shouldn't do that, however). - thisElement->~T(); - uint8_t* pointer = (uint8_t*) (thisElement); - return dataBackend->deleteData(pointer, sizeof(T)); - } + /*** + * Generates an object of type T in the backend storage. + * + * @warning Do not use with any Type that allocates memory internally! + * + * @tparam T Type of Object + * @param args Constructor Arguments to be passed + * @return A pointer to the new object or a nullptr in case of failure + */ + template + T* generate(Args&&... args) { + store_address_t tempId; + uint8_t* pData = nullptr; + ReturnValue_t result = dataBackend->getFreeElement(&tempId, sizeof(T), + &pData); + if (result != HasReturnvaluesIF::RETURN_OK) { + return nullptr; + } + T* temp = new (pData) T(std::forward(args)...); + return temp; + } + /*** + * Function to destroy the object allocated with generate and free space in backend. + * This must be called by the user. + * + * @param thisElement Element to be destroyed + * @return RETURN_OK if the element was destroyed, different errors on failure + */ + template + ReturnValue_t destroy(T* thisElement) { + if (thisElement == nullptr){ + return HasReturnvaluesIF::RETURN_FAILED; + } + //Need to call destructor first, in case something was allocated by the object (shouldn't do that, however). + thisElement->~T(); + uint8_t* pointer = (uint8_t*) (thisElement); + return dataBackend->deleteData(pointer, sizeof(T)); + } private: - StorageManagerIF* dataBackend; + StorageManagerIF* dataBackend; }; #endif /* FRAMEWORK_CONTAINER_PLACEMENTFACTORY_H_ */ diff --git a/container/RingBufferBase.h b/container/RingBufferBase.h index 886b9fab9..9277c50bf 100644 --- a/container/RingBufferBase.h +++ b/container/RingBufferBase.h @@ -7,107 +7,107 @@ template class RingBufferBase { public: - RingBufferBase(size_t startAddress, const size_t size, bool overwriteOld) : - start(startAddress), write(startAddress), size(size), - overwriteOld(overwriteOld) { - for (uint8_t count = 0; count < N_READ_PTRS; count++) { - read[count] = startAddress; - } - } + RingBufferBase(size_t startAddress, const size_t size, bool overwriteOld) : + start(startAddress), write(startAddress), size(size), + overwriteOld(overwriteOld) { + for (uint8_t count = 0; count < N_READ_PTRS; count++) { + read[count] = startAddress; + } + } - virtual ~RingBufferBase() {} + virtual ~RingBufferBase() {} - bool isFull(uint8_t n = 0) { - return (availableWriteSpace(n) == 0); - } - bool isEmpty(uint8_t n = 0) { - return (getAvailableReadData(n) == 0); - } + bool isFull(uint8_t n = 0) { + return (availableWriteSpace(n) == 0); + } + bool isEmpty(uint8_t n = 0) { + return (getAvailableReadData(n) == 0); + } - size_t getAvailableReadData(uint8_t n = 0) const { - return ((write + size) - read[n]) % size; - } - size_t availableWriteSpace(uint8_t n = 0) const { - //One less to avoid ambiguous full/empty problem. - return (((read[n] + size) - write - 1) % size); - } + size_t getAvailableReadData(uint8_t n = 0) const { + return ((write + size) - read[n]) % size; + } + size_t availableWriteSpace(uint8_t n = 0) const { + //One less to avoid ambiguous full/empty problem. + return (((read[n] + size) - write - 1) % size); + } - bool overwritesOld() const { - return overwriteOld; - } + bool overwritesOld() const { + return overwriteOld; + } - size_t getMaxSize() const { - return size - 1; - } + size_t getMaxSize() const { + return size - 1; + } - void clear() { - write = start; - for (uint8_t count = 0; count < N_READ_PTRS; count++) { - read[count] = start; - } - } + void clear() { + write = start; + for (uint8_t count = 0; count < N_READ_PTRS; count++) { + read[count] = start; + } + } - size_t writeTillWrap() { - return (start + size) - write; - } + size_t writeTillWrap() { + return (start + size) - write; + } - size_t readTillWrap(uint8_t n = 0) { - return (start + size) - read[n]; - } + size_t readTillWrap(uint8_t n = 0) { + return (start + size) - read[n]; + } - size_t getStart() const { - return start; - } + size_t getStart() const { + return start; + } protected: - const size_t start; - size_t write; - size_t read[N_READ_PTRS]; - const size_t size; - const bool overwriteOld; + const size_t start; + size_t write; + size_t read[N_READ_PTRS]; + const size_t size; + const bool overwriteOld; - void incrementWrite(uint32_t amount) { - write = ((write + amount - start) % size) + start; - } - void incrementRead(uint32_t amount, uint8_t n = 0) { - read[n] = ((read[n] + amount - start) % size) + start; - } + void incrementWrite(uint32_t amount) { + write = ((write + amount - start) % size) + start; + } + void incrementRead(uint32_t amount, uint8_t n = 0) { + read[n] = ((read[n] + amount - start) % size) + start; + } - ReturnValue_t readData(uint32_t amount, uint8_t n = 0) { - if (getAvailableReadData(n) >= amount) { - incrementRead(amount, n); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } + ReturnValue_t readData(uint32_t amount, uint8_t n = 0) { + if (getAvailableReadData(n) >= amount) { + incrementRead(amount, n); + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } + } - ReturnValue_t writeData(uint32_t amount) { - if (availableWriteSpace() >= amount or overwriteOld) { - incrementWrite(amount); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } + ReturnValue_t writeData(uint32_t amount) { + if (availableWriteSpace() >= amount or overwriteOld) { + incrementWrite(amount); + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } + } - size_t getRead(uint8_t n = 0) const { - return read[n]; - } + size_t getRead(uint8_t n = 0) const { + return read[n]; + } - void setRead(uint32_t read, uint8_t n = 0) { - if (read >= start && read < (start+size)) { - this->read[n] = read; - } - } + void setRead(uint32_t read, uint8_t n = 0) { + if (read >= start && read < (start+size)) { + this->read[n] = read; + } + } - uint32_t getWrite() const { - return write; - } + uint32_t getWrite() const { + return write; + } - void setWrite(uint32_t write) { - this->write = write; - } + void setWrite(uint32_t write) { + this->write = write; + } }; #endif /* FSFW_CONTAINER_RINGBUFFERBASE_H_ */ diff --git a/container/SharedRingBuffer.cpp b/container/SharedRingBuffer.cpp index 6ddb3d3ee..fe36341dd 100644 --- a/container/SharedRingBuffer.cpp +++ b/container/SharedRingBuffer.cpp @@ -1,25 +1,28 @@ #include "SharedRingBuffer.h" #include "../ipc/MutexFactory.h" -#include "../ipc/MutexHelper.h" +#include "../ipc/MutexGuard.h" SharedRingBuffer::SharedRingBuffer(object_id_t objectId, const size_t size, - bool overwriteOld, size_t maxExcessBytes): - SystemObject(objectId), SimpleRingBuffer(size, overwriteOld, - maxExcessBytes) { - mutex = MutexFactory::instance()->createMutex(); + bool overwriteOld, size_t maxExcessBytes): + SystemObject(objectId), SimpleRingBuffer(size, overwriteOld, + maxExcessBytes) { + mutex = MutexFactory::instance()->createMutex(); } SharedRingBuffer::SharedRingBuffer(object_id_t objectId, uint8_t *buffer, - const size_t size, bool overwriteOld, size_t maxExcessBytes): - SystemObject(objectId), SimpleRingBuffer(buffer, size, overwriteOld, - maxExcessBytes) { - mutex = MutexFactory::instance()->createMutex(); + const size_t size, bool overwriteOld, size_t maxExcessBytes): + SystemObject(objectId), SimpleRingBuffer(buffer, size, overwriteOld, + maxExcessBytes) { + mutex = MutexFactory::instance()->createMutex(); } +SharedRingBuffer::~SharedRingBuffer() { + MutexFactory::instance()->deleteMutex(mutex); +} void SharedRingBuffer::setToUseReceiveSizeFIFO(size_t fifoDepth) { - this->fifoDepth = fifoDepth; + this->fifoDepth = fifoDepth; } ReturnValue_t SharedRingBuffer::lockRingBufferMutex( @@ -38,18 +41,20 @@ MutexIF* SharedRingBuffer::getMutexHandle() const { } ReturnValue_t SharedRingBuffer::initialize() { - if(fifoDepth > 0) { - receiveSizesFIFO = new DynamicFIFO(fifoDepth); - } - return SystemObject::initialize(); + if(fifoDepth > 0) { + receiveSizesFIFO = new DynamicFIFO(fifoDepth); + } + return SystemObject::initialize(); } DynamicFIFO* SharedRingBuffer::getReceiveSizesFIFO() { - if(receiveSizesFIFO == nullptr) { - // Configuration error. - sif::warning << "SharedRingBuffer::getReceiveSizesFIFO: Ring buffer" - << " was not configured to have sizes FIFO, returning nullptr!" - << std::endl; - } - return receiveSizesFIFO; + if(receiveSizesFIFO == nullptr) { + // Configuration error. +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "SharedRingBuffer::getReceiveSizesFIFO: Ring buffer" + << " was not configured to have sizes FIFO, returning nullptr!" + << std::endl; +#endif + } + return receiveSizesFIFO; } diff --git a/container/SharedRingBuffer.h b/container/SharedRingBuffer.h index 64d7ee291..9d6ea56c2 100644 --- a/container/SharedRingBuffer.h +++ b/container/SharedRingBuffer.h @@ -15,76 +15,79 @@ * and unlock operations. */ class SharedRingBuffer: public SystemObject, - public SimpleRingBuffer { + public SimpleRingBuffer { public: - /** - * This constructor allocates a new internal buffer with the supplied size. - * @param size - * @param overwriteOld - * If the ring buffer is overflowing at a write operartion, the oldest data - * will be overwritten. - */ - SharedRingBuffer(object_id_t objectId, const size_t size, - bool overwriteOld, size_t maxExcessBytes); + /** + * This constructor allocates a new internal buffer with the supplied size. + * @param size + * @param overwriteOld + * If the ring buffer is overflowing at a write operartion, the oldest data + * will be overwritten. + */ + SharedRingBuffer(object_id_t objectId, const size_t size, + bool overwriteOld, size_t maxExcessBytes); + /** + * This constructor takes an external buffer with the specified size. + * @param buffer + * @param size + * @param overwriteOld + * If the ring buffer is overflowing at a write operartion, the oldest data + * will be overwritten. + */ + SharedRingBuffer(object_id_t objectId, uint8_t* buffer, const size_t size, + bool overwriteOld, size_t maxExcessBytes); - /** - * @brief This function can be used to add an optional FIFO to the class - * @details - * This FIFO will be allocated in the initialize function (and will - * have a fixed maximum size after that). It can be used to store - * values like packet sizes, for example for a shared ring buffer - * used by producer/consumer tasks. - */ - void setToUseReceiveSizeFIFO(size_t fifoDepth); + virtual~ SharedRingBuffer(); - /** - * This constructor takes an external buffer with the specified size. - * @param buffer - * @param size - * @param overwriteOld - * If the ring buffer is overflowing at a write operartion, the oldest data - * will be overwritten. - */ - SharedRingBuffer(object_id_t objectId, uint8_t* buffer, const size_t size, - bool overwriteOld, size_t maxExcessBytes); + /** + * @brief This function can be used to add an optional FIFO to the class + * @details + * This FIFO will be allocated in the initialize function (and will + * have a fixed maximum size after that). It can be used to store + * values like packet sizes, for example for a shared ring buffer + * used by producer/consumer tasks. + */ + void setToUseReceiveSizeFIFO(size_t fifoDepth); - /** - * Unless a read-only constant value is read, all operations on the - * shared ring buffer should be protected by calling this function. - * @param timeoutType - * @param timeout - * @return - */ - virtual ReturnValue_t lockRingBufferMutex(MutexIF::TimeoutType timeoutType, - dur_millis_t timeout); - /** - * Any locked mutex also has to be unlocked, otherwise, access to the - * shared ring buffer will be blocked. - * @return - */ - virtual ReturnValue_t unlockRingBufferMutex(); - /** - * The mutex handle can be accessed directly, for example to perform - * the lock with the #MutexHelper for a RAII compliant lock operation. - * @return - */ - MutexIF* getMutexHandle() const; - ReturnValue_t initialize() override; + /** + * Unless a read-only constant value is read, all operations on the + * shared ring buffer should be protected by calling this function. + * @param timeoutType + * @param timeout + * @return + */ + virtual ReturnValue_t lockRingBufferMutex(MutexIF::TimeoutType timeoutType, + dur_millis_t timeout); + /** + * Any locked mutex also has to be unlocked, otherwise, access to the + * shared ring buffer will be blocked. + * @return + */ + virtual ReturnValue_t unlockRingBufferMutex(); - /** - * If the shared ring buffer was configured to have a sizes FIFO, a handle - * to that FIFO can be retrieved with this function. - * Do not forget to protect access with a lock if required! - * @return - */ - DynamicFIFO* getReceiveSizesFIFO(); + /** + * The mutex handle can be accessed directly, for example to perform + * the lock with the #MutexGuard for a RAII compliant lock operation. + * @return + */ + MutexIF* getMutexHandle() const; + + ReturnValue_t initialize() override; + + /** + * If the shared ring buffer was configured to have a sizes FIFO, a handle + * to that FIFO can be retrieved with this function. + * Do not forget to protect access with a lock if required! + * @return + */ + DynamicFIFO* getReceiveSizesFIFO(); private: - MutexIF* mutex = nullptr; + MutexIF* mutex = nullptr; - size_t fifoDepth = 0; - DynamicFIFO* receiveSizesFIFO = nullptr; + size_t fifoDepth = 0; + DynamicFIFO* receiveSizesFIFO = nullptr; }; diff --git a/container/SimpleRingBuffer.cpp b/container/SimpleRingBuffer.cpp index 88c9290ef..8544acbf3 100644 --- a/container/SimpleRingBuffer.cpp +++ b/container/SimpleRingBuffer.cpp @@ -2,31 +2,31 @@ #include SimpleRingBuffer::SimpleRingBuffer(const size_t size, bool overwriteOld, - size_t maxExcessBytes) : - RingBufferBase<>(0, size, overwriteOld), - maxExcessBytes(maxExcessBytes) { - if(maxExcessBytes > size) { - this->maxExcessBytes = size; - } - else { - this->maxExcessBytes = maxExcessBytes; - } - buffer = new uint8_t[size + maxExcessBytes]; + size_t maxExcessBytes) : + RingBufferBase<>(0, size, overwriteOld), + maxExcessBytes(maxExcessBytes) { + if(maxExcessBytes > size) { + this->maxExcessBytes = size; + } + else { + this->maxExcessBytes = maxExcessBytes; + } + buffer = new uint8_t[size + maxExcessBytes]; } SimpleRingBuffer::SimpleRingBuffer(uint8_t *buffer, const size_t size, - bool overwriteOld, size_t maxExcessBytes): + bool overwriteOld, size_t maxExcessBytes): RingBufferBase<>(0, size, overwriteOld), buffer(buffer) { - if(maxExcessBytes > size) { - this->maxExcessBytes = size; - } - else { - this->maxExcessBytes = maxExcessBytes; - } + if(maxExcessBytes > size) { + this->maxExcessBytes = size; + } + else { + this->maxExcessBytes = maxExcessBytes; + } } SimpleRingBuffer::~SimpleRingBuffer() { - delete[] buffer; + delete[] buffer; } ReturnValue_t SimpleRingBuffer::getFreeElement(uint8_t **writePointer, @@ -48,58 +48,58 @@ ReturnValue_t SimpleRingBuffer::getFreeElement(uint8_t **writePointer, } void SimpleRingBuffer::confirmBytesWritten(size_t amount) { - if(getExcessBytes() > 0) { - moveExcessBytesToStart(); - } - incrementWrite(amount); + if(getExcessBytes() > 0) { + moveExcessBytesToStart(); + } + incrementWrite(amount); } ReturnValue_t SimpleRingBuffer::writeData(const uint8_t* data, - size_t amount) { - if (availableWriteSpace() >= amount or overwriteOld) { - size_t amountTillWrap = writeTillWrap(); - if (amountTillWrap >= amount) { - // remaining size in buffer is sufficient to fit full amount. - memcpy(&buffer[write], data, amount); - } - else { - memcpy(&buffer[write], data, amountTillWrap); - memcpy(buffer, data + amountTillWrap, amount - amountTillWrap); - } - incrementWrite(amount); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } + size_t amount) { + if (availableWriteSpace() >= amount or overwriteOld) { + size_t amountTillWrap = writeTillWrap(); + if (amountTillWrap >= amount) { + // remaining size in buffer is sufficient to fit full amount. + memcpy(&buffer[write], data, amount); + } + else { + memcpy(&buffer[write], data, amountTillWrap); + memcpy(buffer, data + amountTillWrap, amount - amountTillWrap); + } + incrementWrite(amount); + return HasReturnvaluesIF::RETURN_OK; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } } ReturnValue_t SimpleRingBuffer::readData(uint8_t* data, size_t amount, - bool incrementReadPtr, bool readRemaining, size_t* trueAmount) { - size_t availableData = getAvailableReadData(READ_PTR); - size_t amountTillWrap = readTillWrap(READ_PTR); - if (availableData < amount) { - if (readRemaining) { - // more data available than amount specified. - amount = availableData; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } - if (trueAmount != nullptr) { - *trueAmount = amount; - } - if (amountTillWrap >= amount) { - memcpy(data, &buffer[read[READ_PTR]], amount); - } else { - memcpy(data, &buffer[read[READ_PTR]], amountTillWrap); - memcpy(data + amountTillWrap, buffer, amount - amountTillWrap); - } + bool incrementReadPtr, bool readRemaining, size_t* trueAmount) { + size_t availableData = getAvailableReadData(READ_PTR); + size_t amountTillWrap = readTillWrap(READ_PTR); + if (availableData < amount) { + if (readRemaining) { + // more data available than amount specified. + amount = availableData; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } + } + if (trueAmount != nullptr) { + *trueAmount = amount; + } + if (amountTillWrap >= amount) { + memcpy(data, &buffer[read[READ_PTR]], amount); + } else { + memcpy(data, &buffer[read[READ_PTR]], amountTillWrap); + memcpy(data + amountTillWrap, buffer, amount - amountTillWrap); + } - if(incrementReadPtr) { - deleteData(amount, readRemaining); - } - return HasReturnvaluesIF::RETURN_OK; + if(incrementReadPtr) { + deleteData(amount, readRemaining); + } + return HasReturnvaluesIF::RETURN_OK; } size_t SimpleRingBuffer::getExcessBytes() const { @@ -114,18 +114,18 @@ void SimpleRingBuffer::moveExcessBytesToStart() { } ReturnValue_t SimpleRingBuffer::deleteData(size_t amount, - bool deleteRemaining, size_t* trueAmount) { - size_t availableData = getAvailableReadData(READ_PTR); - if (availableData < amount) { - if (deleteRemaining) { - amount = availableData; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } - if (trueAmount != nullptr) { - *trueAmount = amount; - } - incrementRead(amount, READ_PTR); - return HasReturnvaluesIF::RETURN_OK; + bool deleteRemaining, size_t* trueAmount) { + size_t availableData = getAvailableReadData(READ_PTR); + if (availableData < amount) { + if (deleteRemaining) { + amount = availableData; + } else { + return HasReturnvaluesIF::RETURN_FAILED; + } + } + if (trueAmount != nullptr) { + *trueAmount = amount; + } + incrementRead(amount, READ_PTR); + return HasReturnvaluesIF::RETURN_OK; } diff --git a/container/SimpleRingBuffer.h b/container/SimpleRingBuffer.h index 37ad5679f..6f31c5fba 100644 --- a/container/SimpleRingBuffer.h +++ b/container/SimpleRingBuffer.h @@ -5,7 +5,7 @@ #include /** - * @brief Circular buffer implementation, useful for buffering + * @brief Circular buffer implementation, useful for buffering * into data streams. * @details * Note that the deleteData() has to be called to increment the read pointer. @@ -25,104 +25,104 @@ public: * with getFreeElement. * */ - SimpleRingBuffer(const size_t size, bool overwriteOld, - size_t maxExcessBytes = 0); - /** - * This constructor takes an external buffer with the specified size. - * @param buffer - * @param size - * @param overwriteOld - * If the ring buffer is overflowing at a write operartion, the oldest data + SimpleRingBuffer(const size_t size, bool overwriteOld, + size_t maxExcessBytes = 0); + /** + * This constructor takes an external buffer with the specified size. + * @param buffer + * @param size + * @param overwriteOld + * If the ring buffer is overflowing at a write operartion, the oldest data * will be overwritten. - * @param maxExcessBytes - * If the buffer can accomodate additional bytes for contigous write - * operations with getFreeElement, this is the maximum allowed additional - * size - */ - SimpleRingBuffer(uint8_t* buffer, const size_t size, bool overwriteOld, - size_t maxExcessBytes = 0); + * @param maxExcessBytes + * If the buffer can accomodate additional bytes for contigous write + * operations with getFreeElement, this is the maximum allowed additional + * size + */ + SimpleRingBuffer(uint8_t* buffer, const size_t size, bool overwriteOld, + size_t maxExcessBytes = 0); - virtual ~SimpleRingBuffer(); + virtual ~SimpleRingBuffer(); - /** - * Write to circular buffer and increment write pointer by amount. - * @param data - * @param amount - * @return -@c RETURN_OK if write operation was successfull - * -@c RETURN_FAILED if - */ - ReturnValue_t writeData(const uint8_t* data, size_t amount); + /** + * Write to circular buffer and increment write pointer by amount. + * @param data + * @param amount + * @return -@c RETURN_OK if write operation was successfull + * -@c RETURN_FAILED if + */ + ReturnValue_t writeData(const uint8_t* data, size_t amount); - /** - * Returns a pointer to a free element. If the remaining buffer is - * not large enough, the data will be written past the actual size - * and the amount of excess bytes will be cached. This function - * does not increment the write pointer! - * @param writePointer Pointer to a pointer which can be used to write - * contiguous blocks into the ring buffer - * @param amount - * @return - */ - ReturnValue_t getFreeElement(uint8_t** writePointer, size_t amount); + /** + * Returns a pointer to a free element. If the remaining buffer is + * not large enough, the data will be written past the actual size + * and the amount of excess bytes will be cached. This function + * does not increment the write pointer! + * @param writePointer Pointer to a pointer which can be used to write + * contiguous blocks into the ring buffer + * @param amount + * @return + */ + ReturnValue_t getFreeElement(uint8_t** writePointer, size_t amount); - /** - * This increments the write pointer and also copies the excess bytes - * to the beginning. It should be called if the write operation - * conducted after calling getFreeElement() was performed. - * @return - */ - void confirmBytesWritten(size_t amount); + /** + * This increments the write pointer and also copies the excess bytes + * to the beginning. It should be called if the write operation + * conducted after calling getFreeElement() was performed. + * @return + */ + void confirmBytesWritten(size_t amount); - virtual size_t getExcessBytes() const; - /** - * Helper functions which moves any excess bytes to the start - * of the ring buffer. - * @return - */ - virtual void moveExcessBytesToStart(); + virtual size_t getExcessBytes() const; + /** + * Helper functions which moves any excess bytes to the start + * of the ring buffer. + * @return + */ + virtual void moveExcessBytesToStart(); - /** - * Read from circular buffer at read pointer. - * @param data - * @param amount - * @param incrementReadPtr - * If this is set to true, the read pointer will be incremented. - * If readRemaining is set to true, the read pointer will be incremented - * accordingly. - * @param readRemaining - * If this is set to true, the data will be read even if the amount - * specified exceeds the read data available. - * @param trueAmount [out] - * If readRemaining was set to true, the true amount read will be assigned - * to the passed value. - * @return - * - @c RETURN_OK if data was read successfully - * - @c RETURN_FAILED if not enough data was available and readRemaining - * was set to false. - */ - ReturnValue_t readData(uint8_t* data, size_t amount, - bool incrementReadPtr = false, bool readRemaining = false, - size_t* trueAmount = nullptr); + /** + * Read from circular buffer at read pointer. + * @param data + * @param amount + * @param incrementReadPtr + * If this is set to true, the read pointer will be incremented. + * If readRemaining is set to true, the read pointer will be incremented + * accordingly. + * @param readRemaining + * If this is set to true, the data will be read even if the amount + * specified exceeds the read data available. + * @param trueAmount [out] + * If readRemaining was set to true, the true amount read will be assigned + * to the passed value. + * @return + * - @c RETURN_OK if data was read successfully + * - @c RETURN_FAILED if not enough data was available and readRemaining + * was set to false. + */ + ReturnValue_t readData(uint8_t* data, size_t amount, + bool incrementReadPtr = false, bool readRemaining = false, + size_t* trueAmount = nullptr); - /** - * Delete data by incrementing read pointer. - * @param amount - * @param deleteRemaining - * If the amount specified is larger than the remaing size to read and this - * is set to true, the remaining amount will be deleted as well - * @param trueAmount [out] - * If deleteRemaining was set to true, the amount deleted will be assigned - * to the passed value. - * @return - */ - ReturnValue_t deleteData(size_t amount, bool deleteRemaining = false, - size_t* trueAmount = nullptr); + /** + * Delete data by incrementing read pointer. + * @param amount + * @param deleteRemaining + * If the amount specified is larger than the remaing size to read and this + * is set to true, the remaining amount will be deleted as well + * @param trueAmount [out] + * If deleteRemaining was set to true, the amount deleted will be assigned + * to the passed value. + * @return + */ + ReturnValue_t deleteData(size_t amount, bool deleteRemaining = false, + size_t* trueAmount = nullptr); private: - static const uint8_t READ_PTR = 0; - uint8_t* buffer = nullptr; - size_t maxExcessBytes; - size_t excessBytes = 0; + static const uint8_t READ_PTR = 0; + uint8_t* buffer = nullptr; + size_t maxExcessBytes; + size_t excessBytes = 0; }; #endif /* FSFW_CONTAINER_SIMPLERINGBUFFER_H_ */ diff --git a/container/SinglyLinkedList.h b/container/SinglyLinkedList.h index eb6ae276a..02d590c5c 100644 --- a/container/SinglyLinkedList.h +++ b/container/SinglyLinkedList.h @@ -5,71 +5,71 @@ #include /** - * @brief Linked list data structure, - * each entry has a pointer to the next entry (singly) + * @brief Linked list data structure, + * each entry has a pointer to the next entry (singly) * @ingroup container */ template class LinkedElement { public: - T *value; - class Iterator { - public: - LinkedElement *value = nullptr; - Iterator() {} + T *value; + class Iterator { + public: + LinkedElement *value = nullptr; + Iterator() {} - Iterator(LinkedElement *element) : - value(element) { - } + Iterator(LinkedElement *element) : + value(element) { + } - Iterator& operator++() { - value = value->getNext(); - return *this; - } + Iterator& operator++() { + value = value->getNext(); + return *this; + } - Iterator operator++(int) { - Iterator tmp(*this); - operator++(); - return tmp; - } + Iterator operator++(int) { + Iterator tmp(*this); + operator++(); + return tmp; + } - bool operator==(Iterator other) { - return value == other.value; - } + bool operator==(Iterator other) { + return value == other.value; + } - bool operator!=(Iterator other) { - return !(*this == other); - } - T *operator->() { - return value->value; - } - }; + bool operator!=(Iterator other) { + return !(*this == other); + } + T *operator->() { + return value->value; + } + }; - LinkedElement(T* setElement, LinkedElement* setNext = nullptr): - value(setElement), next(setNext) {} + LinkedElement(T* setElement, LinkedElement* setNext = nullptr): + value(setElement), next(setNext) {} - virtual ~LinkedElement(){} + virtual ~LinkedElement(){} - virtual LinkedElement* getNext() const { - return next; - } + virtual LinkedElement* getNext() const { + return next; + } - virtual void setNext(LinkedElement* next) { - this->next = next; - } + virtual void setNext(LinkedElement* next) { + this->next = next; + } - virtual void setEnd() { - this->next = nullptr; - } + virtual void setEnd() { + this->next = nullptr; + } - LinkedElement* begin() { - return this; - } - LinkedElement* end() { - return nullptr; - } + LinkedElement* begin() { + return this; + } + LinkedElement* end() { + return nullptr; + } private: - LinkedElement *next; + LinkedElement *next; }; template @@ -77,52 +77,52 @@ class SinglyLinkedList { public: using ElementIterator = typename LinkedElement::Iterator; - SinglyLinkedList() {} + SinglyLinkedList() {} - SinglyLinkedList(ElementIterator start) : - start(start.value) {} + SinglyLinkedList(ElementIterator start) : + start(start.value) {} - SinglyLinkedList(LinkedElement* startElement) : - start(startElement) {} + SinglyLinkedList(LinkedElement* startElement) : + start(startElement) {} - ElementIterator begin() const { - return ElementIterator::Iterator(start); - } + ElementIterator begin() const { + return ElementIterator::Iterator(start); + } - /** Returns iterator to nulltr */ - ElementIterator end() const { - return ElementIterator::Iterator(); - } + /** Returns iterator to nulltr */ + ElementIterator end() const { + return ElementIterator::Iterator(); + } - /** - * Returns last element in singly linked list. - * @return - */ - ElementIterator back() const { - LinkedElement *element = start; - while (element->getNext() != nullptr) { - element = element->getNext(); - } - return ElementIterator::Iterator(element); - } + /** + * Returns last element in singly linked list. + * @return + */ + ElementIterator back() const { + LinkedElement *element = start; + while (element->getNext() != nullptr) { + element = element->getNext(); + } + return ElementIterator::Iterator(element); + } - size_t getSize() const { - size_t size = 0; - LinkedElement *element = start; - while (element != nullptr) { - size++; - element = element->getNext(); - } - return size; - } - void setStart(LinkedElement* firstElement) { - start = firstElement; - } + size_t getSize() const { + size_t size = 0; + LinkedElement *element = start; + while (element != nullptr) { + size++; + element = element->getNext(); + } + return size; + } + void setStart(LinkedElement* firstElement) { + start = firstElement; + } - void setNext(LinkedElement* currentElement, - LinkedElement* nextElement) { - currentElement->setNext(nextElement); - } + void setNext(LinkedElement* currentElement, + LinkedElement* nextElement) { + currentElement->setNext(nextElement); + } void setLast(LinkedElement* lastElement) { lastElement->setEnd(); @@ -148,7 +148,7 @@ public: } protected: - LinkedElement *start = nullptr; + LinkedElement *start = nullptr; }; #endif /* SINGLYLINKEDLIST_H_ */ diff --git a/controller/CMakeLists.txt b/controller/CMakeLists.txt new file mode 100644 index 000000000..550acfcdd --- /dev/null +++ b/controller/CMakeLists.txt @@ -0,0 +1,4 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + ControllerBase.cpp + ExtendedControllerBase.cpp +) \ No newline at end of file diff --git a/controller/ControllerBase.cpp b/controller/ControllerBase.cpp index 695517310..89f0ff681 100644 --- a/controller/ControllerBase.cpp +++ b/controller/ControllerBase.cpp @@ -1,137 +1,137 @@ -#include "../subsystem/SubsystemBase.h" #include "ControllerBase.h" + #include "../subsystem/SubsystemBase.h" #include "../ipc/QueueFactory.h" #include "../action/HasActionsIF.h" -ControllerBase::ControllerBase(uint32_t setObjectId, uint32_t parentId, - size_t commandQueueDepth) : - SystemObject(setObjectId), parentId(parentId), mode(MODE_OFF), submode( - SUBMODE_NONE), commandQueue(NULL), modeHelper( - this), healthHelper(this, setObjectId),hkSwitcher(this),executingTask(NULL) { - commandQueue = QueueFactory::instance()->createMessageQueue(commandQueueDepth); - +ControllerBase::ControllerBase(object_id_t setObjectId, object_id_t parentId, + size_t commandQueueDepth) : + SystemObject(setObjectId), parentId(parentId), mode(MODE_OFF), + submode(SUBMODE_NONE), modeHelper(this), + healthHelper(this, setObjectId) { + commandQueue = QueueFactory::instance()->createMessageQueue( + commandQueueDepth); } ControllerBase::~ControllerBase() { - QueueFactory::instance()->deleteMessageQueue(commandQueue); + QueueFactory::instance()->deleteMessageQueue(commandQueue); } ReturnValue_t ControllerBase::initialize() { - ReturnValue_t result = SystemObject::initialize(); - if (result != RETURN_OK) { - return result; - } + ReturnValue_t result = SystemObject::initialize(); + if (result != RETURN_OK) { + return result; + } - MessageQueueId_t parentQueue = 0; - if (parentId != 0) { - SubsystemBase *parent = objectManager->get(parentId); - if (parent == NULL) { - return RETURN_FAILED; - } - parentQueue = parent->getCommandQueue(); + MessageQueueId_t parentQueue = 0; + if (parentId != objects::NO_OBJECT) { + SubsystemBase *parent = objectManager->get(parentId); + if (parent == nullptr) { + return RETURN_FAILED; + } + parentQueue = parent->getCommandQueue(); - parent->registerChild(getObjectId()); - } + parent->registerChild(getObjectId()); + } - result = healthHelper.initialize(parentQueue); - if (result != RETURN_OK) { - return result; - } + result = healthHelper.initialize(parentQueue); + if (result != RETURN_OK) { + return result; + } - result = modeHelper.initialize(parentQueue); - if (result != RETURN_OK) { - return result; - } + result = modeHelper.initialize(parentQueue); + if (result != RETURN_OK) { + return result; + } - result = hkSwitcher.initialize(); - if (result != RETURN_OK) { - return result; - } - return RETURN_OK; + return RETURN_OK; } MessageQueueId_t ControllerBase::getCommandQueue() const { - return commandQueue->getId(); + return commandQueue->getId(); } void ControllerBase::handleQueue() { - CommandMessage message; - ReturnValue_t result; - for (result = commandQueue->receiveMessage(&message); result == RETURN_OK; - result = commandQueue->receiveMessage(&message)) { + CommandMessage command; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + for (result = commandQueue->receiveMessage(&command); + result == RETURN_OK; + result = commandQueue->receiveMessage(&command)) { - result = modeHelper.handleModeCommand(&message); - if (result == RETURN_OK) { - continue; - } + result = modeHelper.handleModeCommand(&command); + if (result == RETURN_OK) { + continue; + } - result = healthHelper.handleHealthCommand(&message); - if (result == RETURN_OK) { - continue; - } - result = handleCommandMessage(&message); - if (result == RETURN_OK) { - continue; - } - message.setToUnknownCommand(); - commandQueue->reply(&message); - } + result = healthHelper.handleHealthCommand(&command); + if (result == RETURN_OK) { + continue; + } + result = handleCommandMessage(&command); + if (result == RETURN_OK) { + continue; + } + command.setToUnknownCommand(); + commandQueue->reply(&command); + } } void ControllerBase::startTransition(Mode_t mode, Submode_t submode) { - changeHK(this->mode, this->submode, false); - triggerEvent(CHANGING_MODE, mode, submode); - this->mode = mode; - this->submode = submode; - modeHelper.modeChanged(mode, submode); - modeChanged(mode, submode); - announceMode(false); - changeHK(this->mode, this->submode, true); + changeHK(this->mode, this->submode, false); + triggerEvent(CHANGING_MODE, mode, submode); + this->mode = mode; + this->submode = submode; + modeHelper.modeChanged(mode, submode); + modeChanged(mode, submode); + announceMode(false); + changeHK(this->mode, this->submode, true); } void ControllerBase::getMode(Mode_t* mode, Submode_t* submode) { - *mode = this->mode; - *submode = this->submode; + *mode = this->mode; + *submode = this->submode; } void ControllerBase::setToExternalControl() { - healthHelper.setHealth(EXTERNAL_CONTROL); + healthHelper.setHealth(EXTERNAL_CONTROL); } void ControllerBase::announceMode(bool recursive) { - triggerEvent(MODE_INFO, mode, submode); + triggerEvent(MODE_INFO, mode, submode); } ReturnValue_t ControllerBase::performOperation(uint8_t opCode) { - handleQueue(); - hkSwitcher.performOperation(); - performControlOperation(); - return RETURN_OK; + handleQueue(); + performControlOperation(); + return RETURN_OK; } void ControllerBase::modeChanged(Mode_t mode, Submode_t submode) { - return; + return; } ReturnValue_t ControllerBase::setHealth(HealthState health) { - switch (health) { - case HEALTHY: - case EXTERNAL_CONTROL: - healthHelper.setHealth(health); - return RETURN_OK; - default: - return INVALID_HEALTH_STATE; - } + switch (health) { + case HEALTHY: + case EXTERNAL_CONTROL: + healthHelper.setHealth(health); + return RETURN_OK; + default: + return INVALID_HEALTH_STATE; + } } HasHealthIF::HealthState ControllerBase::getHealth() { - return healthHelper.getHealth(); + return healthHelper.getHealth(); } void ControllerBase::setTaskIF(PeriodicTaskIF* task_){ - executingTask = task_; + executingTask = task_; } void ControllerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { } + +ReturnValue_t ControllerBase::initializeAfterTaskCreation() { + return HasReturnvaluesIF::RETURN_OK; +} diff --git a/controller/ControllerBase.h b/controller/ControllerBase.h index f5182ceb3..f583342fe 100644 --- a/controller/ControllerBase.h +++ b/controller/ControllerBase.h @@ -1,5 +1,5 @@ -#ifndef CONTROLLERBASE_H_ -#define CONTROLLERBASE_H_ +#ifndef FSFW_CONTROLLER_CONTROLLERBASE_H_ +#define FSFW_CONTROLLER_CONTROLLERBASE_H_ #include "../health/HasHealthIF.h" #include "../health/HealthHelper.h" @@ -7,73 +7,88 @@ #include "../modes/ModeHelper.h" #include "../objectmanager/SystemObject.h" #include "../tasks/ExecutableObjectIF.h" +#include "../tasks/PeriodicTaskIF.h" #include "../datapool/HkSwitchHelper.h" - +/** + * @brief Generic base class for controller classes + * @details + * Implements common interfaces for controllers, which generally have + * a mode and a health state. This avoids boilerplate code. + */ class ControllerBase: public HasModesIF, - public HasHealthIF, - public ExecutableObjectIF, - public SystemObject, - public HasReturnvaluesIF { + public HasHealthIF, + public ExecutableObjectIF, + public SystemObject, + public HasReturnvaluesIF { public: + static const Mode_t MODE_NORMAL = 2; - static const Mode_t MODE_NORMAL = 2; + ControllerBase(object_id_t setObjectId, object_id_t parentId, + size_t commandQueueDepth = 3); + virtual ~ControllerBase(); - ControllerBase(uint32_t setObjectId, uint32_t parentId, - size_t commandQueueDepth = 3); - virtual ~ControllerBase(); + /** SystemObject override */ + virtual ReturnValue_t initialize() override; - ReturnValue_t initialize(); + virtual MessageQueueId_t getCommandQueue() const override; - virtual MessageQueueId_t getCommandQueue() const; - - virtual ReturnValue_t performOperation(uint8_t opCode); - - virtual ReturnValue_t setHealth(HealthState health); - - virtual HasHealthIF::HealthState getHealth(); - - /** - * Implementation of ExecutableObjectIF function - * - * Used to setup the reference of the task, that executes this component - * @param task_ Pointer to the taskIF of this task - */ - virtual void setTaskIF(PeriodicTaskIF* task_); + /** HasHealthIF overrides */ + virtual ReturnValue_t setHealth(HealthState health) override; + virtual HasHealthIF::HealthState getHealth() override; + /** ExecutableObjectIF overrides */ + virtual ReturnValue_t performOperation(uint8_t opCode) override; + virtual void setTaskIF(PeriodicTaskIF* task) override; + virtual ReturnValue_t initializeAfterTaskCreation() override; protected: - const uint32_t parentId; - Mode_t mode; + /** + * Implemented by child class. Handle command messages which are not + * mode or health messages. + * @param message + * @return + */ + virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; - Submode_t submode; + /** + * Periodic helper, implemented by child class. + */ + virtual void performControlOperation() = 0; - MessageQueueIF* commandQueue; + virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode) = 0; - ModeHelper modeHelper; + const object_id_t parentId; - HealthHelper healthHelper; + Mode_t mode; - HkSwitchHelper hkSwitcher; + Submode_t submode; - /** - * Pointer to the task which executes this component, is invalid before setTaskIF was called. - */ - PeriodicTaskIF* executingTask; + MessageQueueIF* commandQueue = nullptr; - void handleQueue(); + ModeHelper modeHelper; - virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; - virtual void performControlOperation() = 0; - virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, - uint32_t *msToReachTheMode) = 0; - virtual void modeChanged(Mode_t mode, Submode_t submode); - virtual void startTransition(Mode_t mode, Submode_t submode); - virtual void getMode(Mode_t *mode, Submode_t *submode); - virtual void setToExternalControl(); - virtual void announceMode(bool recursive); - virtual void changeHK(Mode_t mode, Submode_t submode, bool enable); + HealthHelper healthHelper; + + /** + * Pointer to the task which executes this component, + * is invalid before setTaskIF was called. + */ + PeriodicTaskIF* executingTask = nullptr; + + /** Handle mode and health messages */ + virtual void handleQueue(); + + /** Mode helpers */ + virtual void modeChanged(Mode_t mode, Submode_t submode); + virtual void startTransition(Mode_t mode, Submode_t submode); + virtual void getMode(Mode_t *mode, Submode_t *submode); + virtual void setToExternalControl(); + virtual void announceMode(bool recursive); + /** HK helpers */ + virtual void changeHK(Mode_t mode, Submode_t submode, bool enable); }; -#endif /* CONTROLLERBASE_H_ */ +#endif /* FSFW_CONTROLLER_CONTROLLERBASE_H_ */ diff --git a/controller/ExtendedControllerBase.cpp b/controller/ExtendedControllerBase.cpp new file mode 100644 index 000000000..b5b8c6601 --- /dev/null +++ b/controller/ExtendedControllerBase.cpp @@ -0,0 +1,104 @@ +#include "ExtendedControllerBase.h" + + +ExtendedControllerBase::ExtendedControllerBase(object_id_t objectId, + object_id_t parentId, size_t commandQueueDepth): + ControllerBase(objectId, parentId, commandQueueDepth), + poolManager(this, commandQueue), + actionHelper(this, commandQueue) { +} + +ExtendedControllerBase::~ExtendedControllerBase() { +} + +ReturnValue_t ExtendedControllerBase::executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t *data, size_t size) { + /* Needs to be overriden and implemented by child class. */ + return HasReturnvaluesIF::RETURN_OK; +} + +object_id_t ExtendedControllerBase::getObjectId() const { + return SystemObject::getObjectId(); +} + +uint32_t ExtendedControllerBase::getPeriodicOperationFrequency() const { + return this->executingTask->getPeriodMs(); +} + +ReturnValue_t ExtendedControllerBase::handleCommandMessage( + CommandMessage *message) { + ReturnValue_t result = actionHelper.handleActionMessage(message); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + return poolManager.handleHousekeepingMessage(message); +} + +void ExtendedControllerBase::handleQueue() { + CommandMessage command; + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + for (result = commandQueue->receiveMessage(&command); + result == RETURN_OK; + result = commandQueue->receiveMessage(&command)) { + result = actionHelper.handleActionMessage(&command); + if (result == RETURN_OK) { + continue; + } + + result = modeHelper.handleModeCommand(&command); + if (result == RETURN_OK) { + continue; + } + + result = healthHelper.handleHealthCommand(&command); + if (result == RETURN_OK) { + continue; + } + + result = poolManager.handleHousekeepingMessage(&command); + if (result == RETURN_OK) { + continue; + } + + result = handleCommandMessage(&command); + if (result == RETURN_OK) { + continue; + } + command.setToUnknownCommand(); + commandQueue->reply(&command); + } +} + +ReturnValue_t ExtendedControllerBase::initialize() { + ReturnValue_t result = ControllerBase::initialize(); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = actionHelper.initialize(commandQueue); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + return poolManager.initialize(commandQueue); +} + +ReturnValue_t ExtendedControllerBase::initializeAfterTaskCreation() { + return poolManager.initializeAfterTaskCreation(); +} + +ReturnValue_t ExtendedControllerBase::performOperation(uint8_t opCode) { + handleQueue(); + performControlOperation(); + /* We do this after performing control operation because variables will be set changed + in this function. */ + poolManager.performHkOperation(); + return RETURN_OK; +} + +MessageQueueId_t ExtendedControllerBase::getCommandQueue() const { + return commandQueue->getId(); +} + +LocalDataPoolManager* ExtendedControllerBase::getHkManagerHandle() { + return &poolManager; +} diff --git a/controller/ExtendedControllerBase.h b/controller/ExtendedControllerBase.h new file mode 100644 index 000000000..d5d43933b --- /dev/null +++ b/controller/ExtendedControllerBase.h @@ -0,0 +1,73 @@ +#ifndef FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ +#define FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ + +#include "ControllerBase.h" + +#include "../action/HasActionsIF.h" +#include "../datapoollocal/HasLocalDataPoolIF.h" +#include "../action/ActionHelper.h" +#include "../datapoollocal/LocalDataPoolManager.h" + +/** + * @brief Extendes the basic ControllerBase with the common components + * HasActionsIF for commandability and HasLocalDataPoolIF to keep + * a pool of local data pool variables. + * @details + * Default implementations required for the interfaces will be empty and have + * to be implemented by child class. + */ +class ExtendedControllerBase: public ControllerBase, + public HasActionsIF, + public HasLocalDataPoolIF { +public: + ExtendedControllerBase(object_id_t objectId, object_id_t parentId, + size_t commandQueueDepth = 3); + virtual ~ExtendedControllerBase(); + + /* SystemObjectIF overrides */ + virtual ReturnValue_t initialize() override; + + virtual MessageQueueId_t getCommandQueue() const override; + + /* ExecutableObjectIF overrides */ + virtual ReturnValue_t performOperation(uint8_t opCode) override; + virtual ReturnValue_t initializeAfterTaskCreation() override; + +protected: + LocalDataPoolManager poolManager; + ActionHelper actionHelper; + + /** + * Implemented by child class. Handle all command messages which are + * not health, mode, action or housekeeping messages. + * @param message + * @return + */ + virtual ReturnValue_t handleCommandMessage(CommandMessage *message) = 0; + + /** + * Periodic helper from ControllerBase, implemented by child class. + */ + virtual void performControlOperation() = 0; + + /* Handle the four messages mentioned above */ + void handleQueue() override; + + /* HasActionsIF overrides */ + virtual ReturnValue_t executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t* data, + size_t size) override; + + /* HasLocalDatapoolIF overrides */ + virtual LocalDataPoolManager* getHkManagerHandle() override; + virtual object_id_t getObjectId() const override; + virtual uint32_t getPeriodicOperationFrequency() const override; + + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override = 0; + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override = 0; +}; + + + +#endif /* FSFW_CONTROLLER_EXTENDEDCONTROLLERBASE_H_ */ diff --git a/coordinates/CMakeLists.txt b/coordinates/CMakeLists.txt new file mode 100644 index 000000000..a1fa1e52b --- /dev/null +++ b/coordinates/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + CoordinateTransformations.cpp + Sgp4Propagator.cpp +) \ No newline at end of file diff --git a/coordinates/Sgp4Propagator.h b/coordinates/Sgp4Propagator.h index 3949547e1..f813c6f4d 100644 --- a/coordinates/Sgp4Propagator.h +++ b/coordinates/Sgp4Propagator.h @@ -1,7 +1,9 @@ #ifndef SGP4PROPAGATOR_H_ #define SGP4PROPAGATOR_H_ +#ifndef WIN32 #include +#endif #include "../contrib/sgp4/sgp4unit.h" #include "../returnvalues/HasReturnvaluesIF.h" diff --git a/datalinklayer/CMakeLists.txt b/datalinklayer/CMakeLists.txt new file mode 100644 index 000000000..148e7c5de --- /dev/null +++ b/datalinklayer/CMakeLists.txt @@ -0,0 +1,12 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + Clcw.cpp + DataLinkLayer.cpp + Farm1StateLockout.cpp + Farm1StateOpen.cpp + Farm1StateWait.cpp + MapPacketExtraction.cpp + TcTransferFrame.cpp + TcTransferFrameLocal.cpp + VirtualChannelReception.cpp +) \ No newline at end of file diff --git a/datalinklayer/Clcw.cpp b/datalinklayer/Clcw.cpp index dc435a2b4..13971929c 100644 --- a/datalinklayer/Clcw.cpp +++ b/datalinklayer/Clcw.cpp @@ -55,7 +55,9 @@ void Clcw::setBitLock(bool bitLock) { } void Clcw::print() { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "Clcw::print: Clcw is: " << std::hex << getAsWhole() << std::dec << std::endl; +#endif } void Clcw::setWhole(uint32_t rawClcw) { diff --git a/datalinklayer/DataLinkLayer.cpp b/datalinklayer/DataLinkLayer.cpp index 75f2edda0..1bdaa4f5f 100644 --- a/datalinklayer/DataLinkLayer.cpp +++ b/datalinklayer/DataLinkLayer.cpp @@ -98,8 +98,10 @@ ReturnValue_t DataLinkLayer::processFrame(uint16_t length) { receivedDataLength = length; ReturnValue_t status = allFramesReception(); if (status != RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "DataLinkLayer::processFrame: frame reception failed. " "Error code: " << std::hex << status << std::dec << std::endl; +#endif // currentFrame.print(); return status; } else { @@ -124,7 +126,9 @@ ReturnValue_t DataLinkLayer::initialize() { if ( virtualChannels.begin() != virtualChannels.end() ) { clcw->setVirtualChannel( virtualChannels.begin()->second->getChannelId() ); } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "DataLinkLayer::initialize: No VC assigned to this DLL instance! " << std::endl; +#endif return RETURN_FAILED; } diff --git a/datalinklayer/DataLinkLayer.h b/datalinklayer/DataLinkLayer.h index 5a900099e..17a57d61d 100644 --- a/datalinklayer/DataLinkLayer.h +++ b/datalinklayer/DataLinkLayer.h @@ -19,12 +19,12 @@ class VirtualChannelReception; class DataLinkLayer : public CCSDSReturnValuesIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::SYSTEM_1; - static const Event RF_AVAILABLE = MAKE_EVENT(0, SEVERITY::INFO); //!< A RF available signal was detected. P1: raw RFA state, P2: 0 - static const Event RF_LOST = MAKE_EVENT(1, SEVERITY::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0 - static const Event BIT_LOCK = MAKE_EVENT(2, SEVERITY::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0 - static const Event BIT_LOCK_LOST = MAKE_EVENT(3, SEVERITY::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0 -// static const Event RF_CHAIN_LOST = MAKE_EVENT(4, SEVERITY::INFO); //!< The CCSDS Board detected that either bit lock or RF available or both are lost. No parameters. - static const Event FRAME_PROCESSING_FAILED = MAKE_EVENT(5, SEVERITY::LOW); //!< The CCSDS Board could not interpret a TC + static const Event RF_AVAILABLE = MAKE_EVENT(0, severity::INFO); //!< A RF available signal was detected. P1: raw RFA state, P2: 0 + static const Event RF_LOST = MAKE_EVENT(1, severity::INFO); //!< A previously found RF available signal was lost. P1: raw RFA state, P2: 0 + static const Event BIT_LOCK = MAKE_EVENT(2, severity::INFO); //!< A Bit Lock signal. Was detected. P1: raw BLO state, P2: 0 + static const Event BIT_LOCK_LOST = MAKE_EVENT(3, severity::INFO); //!< A previously found Bit Lock signal was lost. P1: raw BLO state, P2: 0 +// static const Event RF_CHAIN_LOST = MAKE_EVENT(4, severity::INFO); //!< The CCSDS Board detected that either bit lock or RF available or both are lost. No parameters. + static const Event FRAME_PROCESSING_FAILED = MAKE_EVENT(5, severity::LOW); //!< The CCSDS Board could not interpret a TC /** * The Constructor sets the passed parameters and nothing else. * @param set_frame_buffer The buffer in which incoming frame candidates are stored. diff --git a/datalinklayer/MapPacketExtraction.cpp b/datalinklayer/MapPacketExtraction.cpp index d64348e15..cdc9ae270 100644 --- a/datalinklayer/MapPacketExtraction.cpp +++ b/datalinklayer/MapPacketExtraction.cpp @@ -1,10 +1,3 @@ -/** - * @file MapPacketExtraction.cpp - * @brief This file defines the MapPacketExtraction class. - * @date 26.03.2013 - * @author baetz - */ - #include "MapPacketExtraction.h" #include "../ipc/QueueFactory.h" #include "../serviceinterface/ServiceInterfaceStream.h" @@ -12,14 +5,14 @@ #include "../tmtcpacket/SpacePacketBase.h" #include "../tmtcservices/AcceptsTelecommandsIF.h" #include "../tmtcservices/TmTcMessage.h" -#include +#include MapPacketExtraction::MapPacketExtraction(uint8_t setMapId, object_id_t setPacketDestination) : - lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), packetLength(0), bufferPosition( - packetBuffer), packetDestination(setPacketDestination), packetStore( - NULL), tcQueueId(MessageQueueIF::NO_QUEUE) { - memset(packetBuffer, 0, sizeof(packetBuffer)); + lastSegmentationFlag(NO_SEGMENTATION), mapId(setMapId), + bufferPosition(packetBuffer), packetDestination(setPacketDestination), + tcQueueId(MessageQueueIF::NO_QUEUE) { + std::memset(packetBuffer, 0, sizeof(packetBuffer)); } ReturnValue_t MapPacketExtraction::extractPackets(TcTransferFrame* frame) { @@ -36,9 +29,11 @@ ReturnValue_t MapPacketExtraction::extractPackets(TcTransferFrame* frame) { bufferPosition = &packetBuffer[packetLength]; status = RETURN_OK; } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "MapPacketExtraction::extractPackets. Packet too large! Size: " << packetLength << std::endl; +#endif clearBuffers(); status = CONTENT_TOO_LARGE; } @@ -58,24 +53,30 @@ ReturnValue_t MapPacketExtraction::extractPackets(TcTransferFrame* frame) { } status = RETURN_OK; } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "MapPacketExtraction::extractPackets. Packet too large! Size: " << packetLength << std::endl; +#endif clearBuffers(); status = CONTENT_TOO_LARGE; } } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "MapPacketExtraction::extractPackets. Illegal segment! Last flag: " << (int) lastSegmentationFlag << std::endl; +#endif clearBuffers(); status = ILLEGAL_SEGMENTATION_FLAG; } break; default: +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "MapPacketExtraction::extractPackets. Illegal segmentationFlag: " << (int) segmentationFlag << std::endl; +#endif clearBuffers(); status = DATA_CORRUPTED; break; @@ -142,10 +143,14 @@ ReturnValue_t MapPacketExtraction::initialize() { } void MapPacketExtraction::printPacketBuffer(void) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "DLL: packet_buffer contains: " << std::endl; +#endif for (uint32_t i = 0; i < this->packetLength; ++i) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "packet_buffer[" << std::dec << i << "]: 0x" << std::hex << (uint16_t) this->packetBuffer[i] << std::endl; +#endif } } diff --git a/datalinklayer/MapPacketExtraction.h b/datalinklayer/MapPacketExtraction.h index 507f13dbe..ddb867fb2 100644 --- a/datalinklayer/MapPacketExtraction.h +++ b/datalinklayer/MapPacketExtraction.h @@ -1,12 +1,5 @@ -/** - * @file MapPacketExtraction.h - * @brief This file defines the MapPacketExtraction class. - * @date 26.03.2013 - * @author baetz - */ - -#ifndef MAPPACKETEXTRACTION_H_ -#define MAPPACKETEXTRACTION_H_ +#ifndef FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ +#define FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ #include "MapPacketExtractionIF.h" #include "../objectmanager/ObjectManagerIF.h" @@ -20,17 +13,19 @@ class StorageManagerIF; * The class implements the full MAP Packet Extraction functionality as described in the CCSDS * TC Space Data Link Protocol. It internally stores incomplete segmented packets until they are * fully received. All found packets are forwarded to a single distribution entity. + * @author B. Baetz */ class MapPacketExtraction: public MapPacketExtractionIF { private: static const uint32_t MAX_PACKET_SIZE = 4096; uint8_t lastSegmentationFlag; //!< The segmentation flag of the last received frame. uint8_t mapId; //!< MAP ID of this MAP Channel. - uint32_t packetLength; //!< Complete length of the current Space Packet. + uint32_t packetLength = 0; //!< Complete length of the current Space Packet. uint8_t* bufferPosition; //!< Position to write to in the internal Packet buffer. uint8_t packetBuffer[MAX_PACKET_SIZE]; //!< The internal Space Packet Buffer. object_id_t packetDestination; - StorageManagerIF* packetStore; //!< Pointer to the store where full TC packets are stored. + //!< Pointer to the store where full TC packets are stored. + StorageManagerIF* packetStore = nullptr; MessageQueueId_t tcQueueId; //!< QueueId to send found packets to the distributor. /** * Debug method to print the packet Buffer's content. @@ -75,4 +70,4 @@ public: uint8_t getMapId() const; }; -#endif /* MAPPACKETEXTRACTION_H_ */ +#endif /* FSFW_DATALINKLAYER_MAPPACKETEXTRACTION_H_ */ diff --git a/datalinklayer/TcTransferFrame.cpp b/datalinklayer/TcTransferFrame.cpp index f1a70bdcf..ee094dc32 100644 --- a/datalinklayer/TcTransferFrame.cpp +++ b/datalinklayer/TcTransferFrame.cpp @@ -87,16 +87,25 @@ uint8_t* TcTransferFrame::getFullDataField() { } void TcTransferFrame::print() { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "Raw Frame: " << std::hex << std::endl; for (uint16_t count = 0; count < this->getFullSize(); count++ ) { sif::debug << (uint16_t)this->getFullFrame()[count] << " "; } sif::debug << std::dec << std::endl; -// debug << "Frame Header:" << std::endl; -// debug << "Version Number: " << std::hex << (uint16_t)this->current_frame.getVersionNumber() << std::endl; -// debug << "Bypass Flag set?| Ctrl Cmd Flag set?: " << (uint16_t)this->current_frame.bypassFlagSet() << " | " << (uint16_t)this->current_frame.controlCommandFlagSet() << std::endl; -// debug << "SCID : " << this->current_frame.getSpacecraftId() << std::endl; -// debug << "VCID : " << (uint16_t)this->current_frame.getVirtualChannelId() << std::endl; -// debug << "Frame length: " << std::dec << this->current_frame.getFrameLength() << std::endl; -// debug << "Sequence Number: " << (uint16_t)this->current_frame.getSequenceNumber() << std::endl; + + sif::debug << "Frame Header:" << std::endl; + sif::debug << "Version Number: " << std::hex + << (uint16_t)this->getVersionNumber() << std::endl; + sif::debug << "Bypass Flag set?| Ctrl Cmd Flag set?: " + << (uint16_t)this->bypassFlagSet() << " | " + << (uint16_t)this->controlCommandFlagSet() << std::endl; + sif::debug << "SCID : " << this->getSpacecraftId() << std::endl; + sif::debug << "VCID : " << (uint16_t)this->getVirtualChannelId() + << std::endl; + sif::debug << "Frame length: " << std::dec << this->getFrameLength() + << std::endl; + sif::debug << "Sequence Number: " << (uint16_t)this->getSequenceNumber() + << std::endl; +#endif } diff --git a/datalinklayer/TcTransferFrameLocal.cpp b/datalinklayer/TcTransferFrameLocal.cpp index e8785c92a..de8f568f4 100644 --- a/datalinklayer/TcTransferFrameLocal.cpp +++ b/datalinklayer/TcTransferFrameLocal.cpp @@ -37,7 +37,9 @@ TcTransferFrameLocal::TcTransferFrameLocal(bool bypass, bool controlCommand, uin this->getFullFrame()[getFullSize()-2] = (crc & 0xFF00) >> 8; this->getFullFrame()[getFullSize()-1] = (crc & 0x00FF); } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "TcTransferFrameLocal: dataSize too large: " << dataSize << std::endl; +#endif } } else { //No data in frame diff --git a/datalinklayer/VirtualChannelReception.cpp b/datalinklayer/VirtualChannelReception.cpp index cde28de37..3a56fe1e2 100644 --- a/datalinklayer/VirtualChannelReception.cpp +++ b/datalinklayer/VirtualChannelReception.cpp @@ -102,8 +102,10 @@ uint8_t VirtualChannelReception::getChannelId() const { ReturnValue_t VirtualChannelReception::initialize() { ReturnValue_t returnValue = RETURN_FAILED; if ((slidingWindowWidth > 254) || (slidingWindowWidth % 2 != 0)) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "VirtualChannelReception::initialize: Illegal sliding window width: " << (int) slidingWindowWidth << std::endl; +#endif return RETURN_FAILED; } for (mapChannelIterator iterator = mapChannels.begin(); iterator != mapChannels.end(); diff --git a/datapool/CMakeLists.txt b/datapool/CMakeLists.txt new file mode 100644 index 000000000..0d53e1ba1 --- /dev/null +++ b/datapool/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + HkSwitchHelper.cpp + PoolDataSetBase.cpp + PoolEntry.cpp +) \ No newline at end of file diff --git a/datapool/ControllerSet.cpp b/datapool/ControllerSet.cpp deleted file mode 100644 index 6339034c1..000000000 --- a/datapool/ControllerSet.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "ControllerSet.h" - -ControllerSet::ControllerSet() { - -} - -ControllerSet::~ControllerSet() { -} - -void ControllerSet::setInvalid() { - read(); - setToDefault(); - commit(PoolVariableIF::INVALID); -} diff --git a/datapool/ControllerSet.h b/datapool/ControllerSet.h deleted file mode 100644 index e27debff0..000000000 --- a/datapool/ControllerSet.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef CONTROLLERSET_H_ -#define CONTROLLERSET_H_ - -#include "DataSet.h" - -class ControllerSet :public DataSet { -public: - ControllerSet(); - virtual ~ControllerSet(); - - virtual void setToDefault() = 0; - void setInvalid(); -}; - -#endif /* CONTROLLERSET_H_ */ diff --git a/datapool/DataPool.cpp b/datapool/DataPool.cpp deleted file mode 100644 index 1b6cb7a19..000000000 --- a/datapool/DataPool.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include "DataPool.h" -#include "../serviceinterface/ServiceInterfaceStream.h" -#include "../ipc/MutexFactory.h" - -DataPool::DataPool( void ( *initFunction )( std::map* pool_map ) ) { - mutex = MutexFactory::instance()->createMutex(); - if (initFunction != NULL ) { - initFunction( &this->data_pool ); - } -} - -DataPool::~DataPool() { - MutexFactory::instance()->deleteMutex(mutex); - for ( std::map::iterator it = this->data_pool.begin(); it != this->data_pool.end(); ++it ) { - delete it->second; - } -} - -//The function checks PID, type and array length before returning a copy of the PoolEntry. In failure case, it returns a temp-Entry with size 0 and NULL-ptr. -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t sizeOrPosition ) { - std::map::iterator it = this->data_pool.find( data_pool_id ); - if ( it != this->data_pool.end() ) { - PoolEntry* entry = dynamic_cast< PoolEntry* >( it->second ); - if (entry != NULL ) { - if ( sizeOrPosition <= entry->length ) { - return entry; - } - } - } - return NULL; -} - -PoolEntryIF* DataPool::getRawData( uint32_t data_pool_id ) { - std::map::iterator it = this->data_pool.find( data_pool_id ); - if ( it != this->data_pool.end() ) { - return it->second; - } else { - return NULL; - } -} - -//uint8_t DataPool::getRawData( uint32_t data_pool_id, uint8_t* address, uint16_t* size, uint32_t maxSize ) { -// std::map::iterator it = this->data_pool.find( data_pool_id ); -// if ( it != this->data_pool.end() ) { -// if ( it->second->getByteSize() <= maxSize ) { -// *size = it->second->getByteSize(); -// memcpy( address, it->second->getRawData(), *size ); -// return DP_SUCCESSFUL; -// } -// } -// *size = 0; -// return DP_FAILURE; -//} - -ReturnValue_t DataPool::freeDataPoolLock() { - ReturnValue_t status = mutex->unlockMutex(); - if ( status != RETURN_OK ) { - sif::error << "DataPool::DataPool: unlock of mutex failed with error code: " << status << std::endl; - } - return status; -} - -ReturnValue_t DataPool::lockDataPool() { - ReturnValue_t status = mutex->lockMutex(MutexIF::BLOCKING); - if ( status != RETURN_OK ) { - sif::error << "DataPool::DataPool: lock of mutex failed with error code: " << status << std::endl; - } - return status; -} - -void DataPool::print() { - sif::debug << "DataPool contains: " << std::endl; - std::map::iterator dataPoolIt; - dataPoolIt = this->data_pool.begin(); - while( dataPoolIt != this->data_pool.end() ) { - sif::debug << std::hex << dataPoolIt->first << std::dec << " |"; - dataPoolIt->second->print(); - dataPoolIt++; - } -} - -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData(uint32_t data_pool_id, - uint8_t size); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData( uint32_t data_pool_id, uint8_t size ); -template PoolEntry* DataPool::getData(uint32_t data_pool_id, - uint8_t size); - - -uint32_t DataPool::PIDToDataPoolId(uint32_t parameter_id) { - return (parameter_id >> 8) & 0x00FFFFFF; -} - -uint8_t DataPool::PIDToArrayIndex(uint32_t parameter_id) { - return (parameter_id & 0x000000FF); -} - -uint32_t DataPool::poolIdAndPositionToPid(uint32_t poolId, uint8_t index) { - return (poolId << 8) + index; -} - - -//SHOULDDO: Do we need a mutex lock here... I don't think so, as we only check static const values of elements in a list that do not change. -//there is no guarantee in the standard, but it seems to me that the implementation is safe -UM -ReturnValue_t DataPool::getType(uint32_t parameter_id, Type* type) { - std::map::iterator it = this->data_pool.find( PIDToDataPoolId(parameter_id)); - if ( it != this->data_pool.end() ) { - *type = it->second->getType(); - return RETURN_OK; - } else { - *type = Type::UNKNOWN_TYPE; - return RETURN_FAILED; - } -} - -bool DataPool::exists(uint32_t parameterId) { - uint32_t poolId = PIDToDataPoolId(parameterId); - uint32_t index = PIDToArrayIndex(parameterId); - std::map::iterator it = this->data_pool.find( poolId ); - if (it != data_pool.end()) { - if (it->second->getSize() >= index) { - return true; - } - } - return false; -} diff --git a/datapool/DataPool.h b/datapool/DataPool.h deleted file mode 100644 index 7abf28980..000000000 --- a/datapool/DataPool.h +++ /dev/null @@ -1,135 +0,0 @@ -/** - * \file DataPool.h - * - * \date 10/17/2012 - * \author Bastian Baetz - * - * \brief This file contains the definition of the DataPool class and (temporarily) - * the "extern" definition of the global dataPool instance. - */ - -#ifndef DATAPOOL_H_ -#define DATAPOOL_H_ - -#include "PoolEntry.h" -#include "../globalfunctions/Type.h" -#include "../ipc/MutexIF.h" -#include - -/** - * \defgroup data_pool Data Pool - * This is the group, where all classes associated with Data Pool Handling belong to. - * This includes classes to access Data Pool variables. - */ - -#define DP_SUCCESSFUL 0 -#define DP_FAILURE 1 - -/** - * \brief This class represents the OBSW global data-pool. - * - * \details All variables are registered and space is allocated in an initialization - * function, which is passed do the constructor. - * Space for the variables is allocated on the heap (with a new call). - * The data is found by a data pool id, which uniquely represents a variable. - * Data pool variables should be used with a blackboard logic in mind, - * which means read data is valid (if flagged so), but not necessarily up-to-date. - * Variables are either single values or arrays. - * \ingroup data_pool - */ -class DataPool : public HasReturnvaluesIF { -private: - /** - * \brief This is the actual data pool itself. - * \details It is represented by a map - * with the data pool id as index and a pointer to a single PoolEntry as value. - */ - std::map data_pool; -public: - /** - * \brief The mutex is created in the constructor and makes access mutual exclusive. - * \details Locking and unlocking the pool is only done by the DataSet class. - */ - MutexIF* mutex; - /** - * \brief In the classes constructor, the passed initialization function is called. - * \details To enable filling the pool, - * a pointer to the map is passed, allowing direct access to the pool's content. - * On runtime, adding or removing variables is forbidden. - */ - DataPool( void ( *initFunction )( std::map* pool_map ) ); - /** - * \brief The destructor iterates through the data_pool map and calls all Entries destructors to clean up the heap. - */ - ~DataPool(); - /** - * \brief This is the default call to access the pool. - * \details A pointer to the PoolEntry object is returned. - * The call checks data pool id, type and array size. Returns NULL in case of failure. - * \param data_pool_id The data pool id to search. - * \param sizeOrPosition The array size (not byte size!) of the pool entry, or the position the user wants to read. - * If smaller than the entry size, everything's ok. - */ - template PoolEntry* getData( uint32_t data_pool_id, uint8_t sizeOrPosition ); - /** - * \brief An alternative call to get a data pool entry in case the type is not implicitly known - * (i.e. in Housekeeping Telemetry). - * \details It returns a basic interface and does NOT perform - * a size check. The caller has to assure he does not copy too much data. - * Returns NULL in case the entry is not found. - * \param data_pool_id The data pool id to search. - */ - PoolEntryIF* getRawData( uint32_t data_pool_id ); - /** - * \brief This is a small helper function to facilitate locking the global data pool. - * \details It fetches the pool's mutex id and tries to acquire the mutex. - */ - ReturnValue_t lockDataPool(); - /** - * \brief This is a small helper function to facilitate unlocking the global data pool. - * \details It fetches the pool's mutex id and tries to free the mutex. - */ - ReturnValue_t freeDataPoolLock(); - /** - * \brief The print call is a simple debug method. - * \details It prints the current content of the data pool. - * It iterates through the data_pool map and calls each entry's print() method. - */ - void print(); - /** - * Extracts the data pool id from a SCOS 2000 PID. - * @param parameter_id The passed Parameter ID. - * @return The data pool id as used within the OBSW. - */ - static uint32_t PIDToDataPoolId( uint32_t parameter_id ); - /** - * Extracts an array index out of a SCOS 2000 PID. - * @param parameter_id The passed Parameter ID. - * @return The index of the corresponding data pool entry. - */ - static uint8_t PIDToArrayIndex( uint32_t parameter_id ); - /** - * Retransforms a data pool id and an array index to a SCOS 2000 PID. - */ - static uint32_t poolIdAndPositionToPid( uint32_t poolId, uint8_t index ); - - /** - * Method to return the type of a pool variable. - * @param parameter_id A parameterID (not pool id) of a DP member. - * @param type Returns the type or TYPE::UNKNOWN_TYPE - * @return RETURN_OK if parameter exists, RETURN_FAILED else. - */ - ReturnValue_t getType( uint32_t parameter_id, Type* type ); - - /** - * Method to check if a PID exists. - * Does not lock, as there's no possibility to alter the list that is checked during run-time. - * @param parameterId The PID (not pool id!) of a parameter. - * @return true if exists, false else. - */ - bool exists(uint32_t parameterId); -}; - -//We assume someone globally instantiates a DataPool. -extern DataPool dataPool; -#endif /* DATAPOOL_H_ */ diff --git a/datapool/DataPoolAdmin.cpp b/datapool/DataPoolAdmin.cpp deleted file mode 100644 index f298d530d..000000000 --- a/datapool/DataPoolAdmin.cpp +++ /dev/null @@ -1,300 +0,0 @@ -#include "DataPool.h" -#include "DataPoolAdmin.h" -#include "DataSet.h" -#include "PoolRawAccess.h" -#include "../ipc/CommandMessage.h" -#include "../ipc/QueueFactory.h" -#include "../parameters/ParameterMessage.h" - -DataPoolAdmin::DataPoolAdmin(object_id_t objectId) : - SystemObject(objectId), storage(NULL), commandQueue(NULL), memoryHelper( - this, NULL), actionHelper(this, NULL) { - commandQueue = QueueFactory::instance()->createMessageQueue(); -} - -DataPoolAdmin::~DataPoolAdmin() { - QueueFactory::instance()->deleteMessageQueue(commandQueue); -} - -ReturnValue_t DataPoolAdmin::performOperation(uint8_t opCode) { - handleCommand(); - return RETURN_OK; -} - -MessageQueueId_t DataPoolAdmin::getCommandQueue() const { - return commandQueue->getId(); -} - -ReturnValue_t DataPoolAdmin::executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { - if (actionId != SET_VALIDITY) { - return INVALID_ACTION_ID; - } - - if (size != 5) { - return INVALID_PARAMETERS; - } - - uint32_t address = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) - | data[3]; - - uint8_t valid = data[4]; - - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); - - DataSet mySet; - PoolRawAccess variable(poolId, 0, &mySet, PoolVariableIF::VAR_READ_WRITE); - ReturnValue_t status = mySet.read(); - if (status != RETURN_OK) { - return INVALID_ADDRESS; - } - if (valid != 0) { - variable.setValid(PoolVariableIF::VALID); - } else { - variable.setValid(PoolVariableIF::INVALID); - } - - mySet.commit(); - - return EXECUTION_FINISHED; -} - -ReturnValue_t DataPoolAdmin::getParameter(uint8_t domainId, - uint16_t parameterId, ParameterWrapper* parameterWrapper, - const ParameterWrapper* newValues, uint16_t startAtIndex) { - return HasReturnvaluesIF::RETURN_FAILED; -} - -void DataPoolAdmin::handleCommand() { - CommandMessage command; - ReturnValue_t result = commandQueue->receiveMessage(&command); - if (result != RETURN_OK) { - return; - } - - result = actionHelper.handleActionMessage(&command); - - if (result == HasReturnvaluesIF::RETURN_OK) { - return; - } - - result = handleParameterCommand(&command); - if (result == HasReturnvaluesIF::RETURN_OK) { - return; - } - - result = memoryHelper.handleMemoryCommand(&command); - if (result != RETURN_OK) { - command.setToUnknownCommand(); - commandQueue->reply(&command); - } -} - -ReturnValue_t DataPoolAdmin::handleMemoryLoad(uint32_t address, - const uint8_t* data, size_t size, uint8_t** dataPointer) { - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); - uint8_t arrayIndex = ::dataPool.PIDToArrayIndex(address); - DataSet testSet; - PoolRawAccess varToGetSize(poolId, arrayIndex, &testSet, - PoolVariableIF::VAR_READ); - ReturnValue_t status = testSet.read(); - if (status != RETURN_OK) { - return INVALID_ADDRESS; - } - uint8_t typeSize = varToGetSize.getSizeOfType(); - - if (size % typeSize != 0) { - return INVALID_SIZE; - } - - if (size > varToGetSize.getSizeTillEnd()) { - return INVALID_SIZE; - } - const uint8_t* readPosition = data; - - for (; size > 0; size -= typeSize) { - DataSet rawSet; - PoolRawAccess variable(poolId, arrayIndex, &rawSet, - PoolVariableIF::VAR_READ_WRITE); - status = rawSet.read(); - if (status == RETURN_OK) { - status = variable.setEntryFromBigEndian(readPosition, typeSize); - if (status == RETURN_OK) { - status = rawSet.commit(); - } - } - arrayIndex += 1; - readPosition += typeSize; - } - return ACTIVITY_COMPLETED; -} - -ReturnValue_t DataPoolAdmin::handleMemoryDump(uint32_t address, size_t size, - uint8_t** dataPointer, uint8_t* copyHere) { - uint32_t poolId = ::dataPool.PIDToDataPoolId(address); - uint8_t arrayIndex = ::dataPool.PIDToArrayIndex(address); - DataSet testSet; - PoolRawAccess varToGetSize(poolId, arrayIndex, &testSet, - PoolVariableIF::VAR_READ); - ReturnValue_t status = testSet.read(); - if (status != RETURN_OK) { - return INVALID_ADDRESS; - } - uint8_t typeSize = varToGetSize.getSizeOfType(); - if (size > varToGetSize.getSizeTillEnd()) { - return INVALID_SIZE; - } - uint8_t* ptrToCopy = copyHere; - for (; size > 0; size -= typeSize) { - DataSet rawSet; - PoolRawAccess variable(poolId, arrayIndex, &rawSet, - PoolVariableIF::VAR_READ); - status = rawSet.read(); - if (status == RETURN_OK) { - size_t temp = 0; - status = variable.getEntryEndianSafe(ptrToCopy, &temp, size); - if (status != RETURN_OK) { - return RETURN_FAILED; - } - } else { - //Error reading parameter. - } - arrayIndex += 1; - ptrToCopy += typeSize; - } - return ACTIVITY_COMPLETED; -} - -ReturnValue_t DataPoolAdmin::initialize() { - ReturnValue_t result = SystemObject::initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - result = memoryHelper.initialize(commandQueue); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - storage = objectManager->get(objects::IPC_STORE); - if (storage == NULL) { - return HasReturnvaluesIF::RETURN_FAILED; - } - - result = actionHelper.initialize(commandQueue); - - return result; -} - -//mostly identical to ParameterHelper::handleParameterMessage() -ReturnValue_t DataPoolAdmin::handleParameterCommand(CommandMessage* command) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - switch (command->getCommand()) { - case ParameterMessage::CMD_PARAMETER_DUMP: { - uint8_t domain = HasParametersIF::getDomain( - ParameterMessage::getParameterId(command)); - uint16_t parameterId = HasParametersIF::getMatrixId( - ParameterMessage::getParameterId(command)); - - DataPoolParameterWrapper wrapper; - result = wrapper.set(domain, parameterId); - - if (result == HasReturnvaluesIF::RETURN_OK) { - result = sendParameter(command->getSender(), - ParameterMessage::getParameterId(command), &wrapper); - } - } - break; - case ParameterMessage::CMD_PARAMETER_LOAD: { - - uint8_t domain = HasParametersIF::getDomain( - ParameterMessage::getParameterId(command)); - uint16_t parameterId = HasParametersIF::getMatrixId( - ParameterMessage::getParameterId(command)); - uint8_t index = HasParametersIF::getIndex( - ParameterMessage::getParameterId(command)); - - const uint8_t *storedStream; - size_t storedStreamSize; - result = storage->getData(ParameterMessage::getStoreId(command), - &storedStream, &storedStreamSize); - if (result != HasReturnvaluesIF::RETURN_OK) { - break; - } - - ParameterWrapper streamWrapper; - result = streamWrapper.set(storedStream, storedStreamSize); - if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(ParameterMessage::getStoreId(command)); - break; - } - - DataPoolParameterWrapper poolWrapper; - result = poolWrapper.set(domain, parameterId); - if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(ParameterMessage::getStoreId(command)); - break; - } - - result = poolWrapper.copyFrom(&streamWrapper, index); - - storage->deleteData(ParameterMessage::getStoreId(command)); - - if (result == HasReturnvaluesIF::RETURN_OK) { - result = sendParameter(command->getSender(), - ParameterMessage::getParameterId(command), &poolWrapper); - } - } - break; - default: - return HasReturnvaluesIF::RETURN_FAILED; - } - - if (result != HasReturnvaluesIF::RETURN_OK) { - rejectCommand(command->getSender(), result, command->getCommand()); - } - - return HasReturnvaluesIF::RETURN_OK; - -} - -//identical to ParameterHelper::sendParameter() -ReturnValue_t DataPoolAdmin::sendParameter(MessageQueueId_t to, uint32_t id, - const DataPoolParameterWrapper* wrapper) { - size_t serializedSize = wrapper->getSerializedSize(); - - uint8_t *storeElement; - store_address_t address; - - ReturnValue_t result = storage->getFreeElement(&address, serializedSize, - &storeElement); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - size_t storeElementSize = 0; - - result = wrapper->serialize(&storeElement, &storeElementSize, - serializedSize, SerializeIF::Endianness::BIG); - - if (result != HasReturnvaluesIF::RETURN_OK) { - storage->deleteData(address); - return result; - } - - CommandMessage reply; - - ParameterMessage::setParameterDumpReply(&reply, id, address); - - commandQueue->sendMessage(to, &reply); - - return HasReturnvaluesIF::RETURN_OK; -} - -//identical to ParameterHelper::rejectCommand() -void DataPoolAdmin::rejectCommand(MessageQueueId_t to, ReturnValue_t reason, - Command_t initialCommand) { - CommandMessage reply; - reply.setReplyRejected(reason, initialCommand); - commandQueue->sendMessage(to, &reply); -} diff --git a/datapool/DataPoolAdmin.h b/datapool/DataPoolAdmin.h deleted file mode 100644 index e2d1e9e18..000000000 --- a/datapool/DataPoolAdmin.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef DATAPOOLADMIN_H_ -#define DATAPOOLADMIN_H_ - -#include "../memory/MemoryHelper.h" -#include "../action/HasActionsIF.h" -#include "../action/SimpleActionHelper.h" -#include "../objectmanager/SystemObject.h" -#include "../returnvalues/HasReturnvaluesIF.h" -#include "../tasks/ExecutableObjectIF.h" -#include "../parameters/ReceivesParameterMessagesIF.h" -#include "DataPoolParameterWrapper.h" -#include "../ipc/MessageQueueIF.h" - -class DataPoolAdmin: public HasActionsIF, - public ExecutableObjectIF, - public AcceptsMemoryMessagesIF, - public HasReturnvaluesIF, - public ReceivesParameterMessagesIF, - public SystemObject { -public: - static const ActionId_t SET_VALIDITY = 1; - - DataPoolAdmin(object_id_t objectId); - - ~DataPoolAdmin(); - - ReturnValue_t performOperation(uint8_t opCode); - - MessageQueueId_t getCommandQueue() const; - - ReturnValue_t handleMemoryLoad(uint32_t address, const uint8_t* data, - size_t size, uint8_t** dataPointer); - ReturnValue_t handleMemoryDump(uint32_t address, size_t size, - uint8_t** dataPointer, uint8_t* copyHere); - - ReturnValue_t executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, const uint8_t* data, size_t size); - - //not implemented as ParameterHelper is no used - ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, - ParameterWrapper *parameterWrapper, - const ParameterWrapper *newValues, uint16_t startAtIndex); - - ReturnValue_t initialize(); -private: - StorageManagerIF *storage; - MessageQueueIF* commandQueue; - MemoryHelper memoryHelper; - SimpleActionHelper actionHelper; - void handleCommand(); - ReturnValue_t handleParameterCommand(CommandMessage *command); - ReturnValue_t sendParameter(MessageQueueId_t to, uint32_t id, - const DataPoolParameterWrapper* wrapper); - void rejectCommand(MessageQueueId_t to, ReturnValue_t reason, - Command_t initialCommand); -}; - -#endif /* DATAPOOLADMIN_H_ */ diff --git a/datapool/DataPoolParameterWrapper.cpp b/datapool/DataPoolParameterWrapper.cpp deleted file mode 100644 index 79367405a..000000000 --- a/datapool/DataPoolParameterWrapper.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "DataPoolParameterWrapper.h" - -//for returncodes -#include "../parameters/HasParametersIF.h" - -#include "DataSet.h" -#include "PoolRawAccess.h" - -DataPoolParameterWrapper::DataPoolParameterWrapper() : - type(Type::UNKNOWN_TYPE), rows(0), columns(0), poolId( - PoolVariableIF::NO_PARAMETER) { - -} - -DataPoolParameterWrapper::~DataPoolParameterWrapper() { - -} - -ReturnValue_t DataPoolParameterWrapper::set(uint8_t domainId, - uint16_t parameterId) { - poolId = (domainId << 16) + parameterId; - - DataSet mySet; - PoolRawAccess raw(poolId, 0, &mySet, PoolVariableIF::VAR_READ); - ReturnValue_t status = mySet.read(); - if (status != HasReturnvaluesIF::RETURN_OK) { - //should only fail for invalid pool id - return HasParametersIF::INVALID_MATRIX_ID; - } - - type = raw.getType(); - rows = raw.getArraySize(); - columns = 1; - - return HasReturnvaluesIF::RETURN_OK; -} - -ReturnValue_t DataPoolParameterWrapper::serialize(uint8_t** buffer, - size_t* size, size_t maxSize, Endianness streamEndianness) const { - ReturnValue_t result; - - result = SerializeAdapter::serialize(&type, buffer, size, maxSize, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - result = SerializeAdapter::serialize(&columns, buffer, size, - maxSize, streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - result = SerializeAdapter::serialize(&rows, buffer, size, maxSize, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - for (uint8_t index = 0; index < rows; index++){ - DataSet mySet; - PoolRawAccess raw(poolId, index, &mySet,PoolVariableIF::VAR_READ); - mySet.read(); - result = raw.serialize(buffer,size,maxSize,streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK){ - return result; - } - } - return HasReturnvaluesIF::RETURN_OK; -} - -//same as ParameterWrapper -size_t DataPoolParameterWrapper::getSerializedSize() const { - size_t serializedSize = 0; - serializedSize += type.getSerializedSize(); - serializedSize += sizeof(rows); - serializedSize += sizeof(columns); - serializedSize += rows * columns * type.getSize(); - - return serializedSize; -} - -ReturnValue_t DataPoolParameterWrapper::deSerialize(const uint8_t** buffer, - size_t* size, Endianness streamEndianness) { - return HasReturnvaluesIF::RETURN_FAILED; -} - -template -ReturnValue_t DataPoolParameterWrapper::deSerializeData(uint8_t startingRow, - uint8_t startingColumn, const void* from, uint8_t fromRows) { - //treat from as a continuous Stream as we copy all of it - const uint8_t *fromAsStream = (const uint8_t *) from; - - ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; - - for (uint8_t fromRow = 0; fromRow < fromRows; fromRow++) { - - DataSet mySet; - PoolRawAccess raw(poolId, startingRow + fromRow, &mySet, - PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - - result = raw.setEntryFromBigEndian(fromAsStream, sizeof(T)); - - fromAsStream += sizeof(T); - - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - - mySet.commit(); - } - - return HasReturnvaluesIF::RETURN_OK; -} - -ReturnValue_t DataPoolParameterWrapper::copyFrom(const ParameterWrapper* from, - uint16_t startWritingAtIndex) { - if (poolId == PoolVariableIF::NO_PARAMETER) { - return ParameterWrapper::NOT_SET; - } - - if (type != from->type) { - return ParameterWrapper::DATATYPE_MISSMATCH; - } - - //check if from fits into this - uint8_t startingRow = startWritingAtIndex / columns; - uint8_t startingColumn = startWritingAtIndex % columns; - - if ((from->rows > (rows - startingRow)) - || (from->columns > (columns - startingColumn))) { - return ParameterWrapper::TOO_BIG; - } - - ReturnValue_t result; - //copy data - if (from->pointsToStream) { - switch (type) { - case Type::UINT8_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::INT8_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::UINT16_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::INT16_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::UINT32_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::INT32_T: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::FLOAT: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - case Type::DOUBLE: - result = deSerializeData(startingRow, startingColumn, - from->readonlyData, from->rows); - break; - default: - result = ParameterWrapper::UNKNOW_DATATYPE; - break; - } - } else { - //not supported - return HasReturnvaluesIF::RETURN_FAILED; - } - - return result; -} diff --git a/datapool/DataPoolParameterWrapper.h b/datapool/DataPoolParameterWrapper.h deleted file mode 100644 index b1e505a75..000000000 --- a/datapool/DataPoolParameterWrapper.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef DATAPOOLPARAMETERWRAPPER_H_ -#define DATAPOOLPARAMETERWRAPPER_H_ - -#include "../globalfunctions/Type.h" -#include "../parameters/ParameterWrapper.h" - -class DataPoolParameterWrapper: public SerializeIF { -public: - DataPoolParameterWrapper(); - virtual ~DataPoolParameterWrapper(); - - ReturnValue_t set(uint8_t domainId, uint16_t parameterId); - - virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const override; - - virtual size_t getSerializedSize() const override; - - virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) override; - - ReturnValue_t copyFrom(const ParameterWrapper *from, - uint16_t startWritingAtIndex); - -private: - Type type; - uint8_t rows; - uint8_t columns; - - uint32_t poolId; - - template - ReturnValue_t deSerializeData(uint8_t startingRow, uint8_t startingColumn, - const void *from, uint8_t fromRows); - -}; - -#endif /* DATAPOOLPARAMETERWRAPPER_H_ */ diff --git a/datapool/DataSet.cpp b/datapool/DataSet.cpp deleted file mode 100644 index e41489bd9..000000000 --- a/datapool/DataSet.cpp +++ /dev/null @@ -1,150 +0,0 @@ -#include "DataSet.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -DataSet::DataSet() : - fill_count(0), state(DATA_SET_UNINITIALISED) { - for (unsigned count = 0; count < DATA_SET_MAX_SIZE; count++) { - registeredVariables[count] = NULL; - } -} - -DataSet::~DataSet() { - //Don't do anything with your variables, they are dead already! (Destructor is already called) -} - -ReturnValue_t DataSet::read() { - ReturnValue_t result = RETURN_OK; - if (state == DATA_SET_UNINITIALISED) { - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - ReturnValue_t status = registeredVariables[count]->read(); - if (status != RETURN_OK) { - result = INVALID_PARAMETER_DEFINITION; - break; - } - } - } - state = DATA_SET_WAS_READ; - freeDataPoolLock(); - } else { - sif::error << "DataSet::read(): Call made in wrong position." << std::endl; - result = SET_WAS_ALREADY_READ; - } - return result; -} - -ReturnValue_t DataSet::commit(uint8_t valid) { - setValid(valid); - return commit(); -} - -ReturnValue_t DataSet::commit() { - if (state == DATA_SET_WAS_READ) { - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - registeredVariables[count]->commit(); - } - } - state = DATA_SET_UNINITIALISED; - freeDataPoolLock(); - return RETURN_OK; - } else { - ReturnValue_t result = RETURN_OK; - lockDataPool(); - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - == PoolVariableIF::VAR_WRITE - && registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - registeredVariables[count]->commit(); - } else if (registeredVariables[count]->getDataPoolId() - != PoolVariableIF::NO_PARAMETER) { - if (result != COMMITING_WITHOUT_READING) { - sif::error << - "DataSet::commit(): commit-without-read " - "call made with non write-only variable." << std::endl; - result = COMMITING_WITHOUT_READING; - } - } - } - state = DATA_SET_UNINITIALISED; - freeDataPoolLock(); - return result; - } - -} - -void DataSet::registerVariable(PoolVariableIF* variable) { - if (state == DATA_SET_UNINITIALISED) { - if (variable != NULL) { - if (fill_count < DATA_SET_MAX_SIZE) { - registeredVariables[fill_count] = variable; - fill_count++; - return; - } - } - } - sif::error - << "DataSet::registerVariable: failed. Either NULL, or set is full, or call made in wrong position." - << std::endl; - return; -} - -uint8_t DataSet::freeDataPoolLock() { - return ::dataPool.freeDataPoolLock(); -} - -uint8_t DataSet::lockDataPool() { - return ::dataPool.lockDataPool(); -} - -ReturnValue_t DataSet::serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - ReturnValue_t result = RETURN_FAILED; - for (uint16_t count = 0; count < fill_count; count++) { - result = registeredVariables[count]->serialize(buffer, size, maxSize, - streamEndianness); - if (result != RETURN_OK) { - return result; - } - } - return result; -} - -size_t DataSet::getSerializedSize() const { - size_t size = 0; - for (uint16_t count = 0; count < fill_count; count++) { - size += registeredVariables[count]->getSerializedSize(); - } - return size; -} - -void DataSet::setValid(uint8_t valid) { - for (uint16_t count = 0; count < fill_count; count++) { - if (registeredVariables[count]->getReadWriteMode() - != PoolVariableIF::VAR_READ) { - registeredVariables[count]->setValid(valid); - } - } -} - -ReturnValue_t DataSet::deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) { - ReturnValue_t result = RETURN_FAILED; - for (uint16_t count = 0; count < fill_count; count++) { - result = registeredVariables[count]->deSerialize(buffer, size, - streamEndianness); - if (result != RETURN_OK) { - return result; - } - } - return result; -} diff --git a/datapool/DataSet.h b/datapool/DataSet.h deleted file mode 100644 index 04044bfe5..000000000 --- a/datapool/DataSet.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * \file DataSet.h - * - * \brief This file contains the DataSet class and a small structure called DataSetContent. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - * - */ - -#ifndef DATASET_H_ -#define DATASET_H_ - -#include "DataPool.h" -#include "DataSetIF.h" -#include "PoolRawAccess.h" -#include "PoolVariable.h" -#include "PoolVarList.h" -#include "PoolVector.h" -#include "../serialize/SerializeAdapter.h" -/** - * \brief The DataSet class manages a set of locally checked out variables. - * - * \details This class manages a list, where a set of local variables (or pool variables) are - * registered. They are checked-out (i.e. their values are looked up and copied) - * with the read call. After the user finishes working with the pool variables, - * he can write back all variable values to the pool with the commit call. - * The data set manages locking and freeing the data pool, to ensure that all values - * are read and written back at once. - * An internal state manages usage of this class. Variables may only be registered before - * the read call is made, and the commit call only after the read call. - * If pool variables are writable and not committed until destruction of the set, the - * DataSet class automatically sets the valid flag in the data pool to invalid (without) - * changing the variable's value. - * - * \ingroup data_pool - */ -class DataSet: public DataSetIF, public HasReturnvaluesIF, public SerializeIF { -private: - //SHOULDDO we could use a linked list of datapool variables - static const uint8_t DATA_SET_MAX_SIZE = 63; //!< This definition sets the maximum number of variables to register in one DataSet. - - /** - * \brief This array represents all pool variables registered in this set. - * \details It has a maximum size of DATA_SET_MAX_SIZE. - */ - PoolVariableIF* registeredVariables[DATA_SET_MAX_SIZE]; - /** - * \brief The fill_count attribute ensures that the variables register in the correct array - * position and that the maximum number of variables is not exceeded. - */ - uint16_t fill_count; - /** - * States of the seet. - */ - enum States { - DATA_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED - DATA_SET_WAS_READ //!< DATA_SET_WAS_READ - }; - /** - * \brief state manages the internal state of the data set, which is important e.g. for the - * behavior on destruction. - */ - States state; - /** - * \brief This is a small helper function to facilitate locking the global data pool. - * \details It makes use of the lockDataPool method offered by the DataPool class. - */ - uint8_t lockDataPool(); - /** - * \brief This is a small helper function to facilitate unlocking the global data pool. - * \details It makes use of the freeDataPoolLock method offered by the DataPool class. - */ - uint8_t freeDataPoolLock(); - -public: - static const uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; - static const ReturnValue_t INVALID_PARAMETER_DEFINITION = - MAKE_RETURN_CODE( 0x01 ); - static const ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE( 0x02 ); - static const ReturnValue_t COMMITING_WITHOUT_READING = - MAKE_RETURN_CODE(0x03); - - /** - * \brief The constructor simply sets the fill_count to zero and sets the state to "uninitialized". - */ - DataSet(); - /** - * \brief The destructor automatically manages writing the valid information of variables. - * \details In case the data set was read out, but not committed (indicated by state), - * the destructor parses all variables that are still registered to the set. - * For each, the valid flag in the data pool is set to "invalid". - */ - ~DataSet(); - /** - * \brief The read call initializes reading out all registered variables. - * \details It iterates through the list of registered variables and calls all read() - * functions of the registered pool variables (which read out their values from the - * data pool) which are not write-only. In case of an error (e.g. a wrong data type, - * or an invalid data pool id), the operation is aborted and - * \c INVALID_PARAMETER_DEFINITION returned. - * The data pool is locked during the whole read operation and freed afterwards. - * The state changes to "was written" after this operation. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c INVALID_PARAMETER_DEFINITION if PID, size or type of the - * requested variable is invalid. - * - \c SET_WAS_ALREADY_READ if read() is called twice without calling - * commit() in between - */ - ReturnValue_t read(); - /** - * \brief The commit call initializes writing back the registered variables. - * \details It iterates through the list of registered variables and calls - * the commit() method of the remaining registered variables (which write back - * their values to the pool). - * The data pool is locked during the whole commit operation and freed afterwards. - * The state changes to "was committed" after this operation. - * If the set does contain at least one variable which is not write-only commit() - * can only be called after read(). If the set only contains variables which are - * write only, commit() can be called without a preceding read() call. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c COMMITING_WITHOUT_READING if set was not read yet and contains non write-only - * variables - */ - ReturnValue_t commit(void); - /** - * Variant of method above which sets validity of all elements of the set. - * @param valid Validity information from PoolVariableIF. - * \return - \c RETURN_OK if all variables were read successfully. - * - \c COMMITING_WITHOUT_READING if set was not read yet and contains non write-only - * variables - */ - ReturnValue_t commit(uint8_t valid); - /** - * \brief This operation is used to register the local variables in the set. - * \details It copies all required information to the currently - * free space in the registeredVariables list. - */ - void registerVariable(PoolVariableIF* variable); - - /** - * Set the valid information of all variables contained in the set which are not readonly - * - * @param valid Validity information from PoolVariableIF. - */ - void setValid(uint8_t valid); - - ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const override; - - size_t getSerializedSize() const override; - - ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) override; - -}; - -#endif /* DATASET_H_ */ diff --git a/datapool/DataSetIF.h b/datapool/DataSetIF.h index 7741477db..ea1a7126e 100644 --- a/datapool/DataSetIF.h +++ b/datapool/DataSetIF.h @@ -1,39 +1,45 @@ -/** - * \file DataSetIF.h - * - * \brief This file contains the small interface to access the DataSet class. - * - * \date 10/23/2012 - * - * \author Bastian Baetz - * - */ - -#ifndef DATASETIF_H_ -#define DATASETIF_H_ +#ifndef FSFW_DATAPOOL_DATASETIF_H_ +#define FSFW_DATAPOOL_DATASETIF_H_ +#include "../returnvalues/HasReturnvaluesIF.h" +#include "../timemanager/Clock.h" class PoolVariableIF; /** - * \brief This class defines a small interface to register on a DataSet. + * @brief This class defines a small interface to register on a DataSet. * - * \details Currently, the only purpose of this interface is to provide a method for locally - * checked-out variables to register on a data set. Still, it may become useful for - * other purposes as well. - * - * \ingroup data_pool + * @details + * Currently, the only purpose of this interface is to provide a + * method for locally checked-out variables to register on a data set. + * Still, it may become useful for other purposes as well. + * @author Bastian Baetz + * @ingroup data_pool */ class DataSetIF { public: + static constexpr uint8_t INTERFACE_ID = CLASS_ID::DATA_SET_CLASS; + static constexpr ReturnValue_t INVALID_PARAMETER_DEFINITION = MAKE_RETURN_CODE(1); + static constexpr ReturnValue_t SET_WAS_ALREADY_READ = MAKE_RETURN_CODE(2); + static constexpr ReturnValue_t COMMITING_WITHOUT_READING = MAKE_RETURN_CODE(3); + + static constexpr ReturnValue_t DATA_SET_UNINITIALISED = MAKE_RETURN_CODE(4); + static constexpr ReturnValue_t DATA_SET_FULL = MAKE_RETURN_CODE(5); + static constexpr ReturnValue_t POOL_VAR_NULL = MAKE_RETURN_CODE(6); + /** - * \brief This is an empty virtual destructor, as it is proposed for C++ interfaces. + * @brief This is an empty virtual destructor, + * as it is proposed for C++ interfaces. */ virtual ~DataSetIF() {} + /** - * \brief This operation provides a method to register local data pool variables - * to register in a data set by passing itself to this DataSet operation. + * @brief This operation provides a method to register local data pool + * variables to register in a data set by passing itself + * to this DataSet operation. */ - virtual void registerVariable( PoolVariableIF* variable ) = 0; + virtual ReturnValue_t registerVariable(PoolVariableIF* variable) = 0; + + virtual uint16_t getFillCount() const = 0; }; -#endif /* DATASETIF_H_ */ +#endif /* FSFW_DATAPOOL_DATASETIF_H_ */ diff --git a/datapool/HkSwitchHelper.cpp b/datapool/HkSwitchHelper.cpp index 04096acaf..21e37f59d 100644 --- a/datapool/HkSwitchHelper.cpp +++ b/datapool/HkSwitchHelper.cpp @@ -1,5 +1,4 @@ -#include "HkSwitchHelper.h" -//#include +#include "../datapool/HkSwitchHelper.h" #include "../ipc/QueueFactory.h" HkSwitchHelper::HkSwitchHelper(EventReportingProxyIF* eventProxy) : @@ -8,7 +7,7 @@ HkSwitchHelper::HkSwitchHelper(EventReportingProxyIF* eventProxy) : } HkSwitchHelper::~HkSwitchHelper() { - // TODO Auto-generated destructor stub + QueueFactory::instance()->deleteMessageQueue(actionQueue); } ReturnValue_t HkSwitchHelper::initialize() { @@ -22,14 +21,14 @@ ReturnValue_t HkSwitchHelper::initialize() { } ReturnValue_t HkSwitchHelper::performOperation(uint8_t operationCode) { - CommandMessage message; - while (actionQueue->receiveMessage(&message) == HasReturnvaluesIF::RETURN_OK) { - ReturnValue_t result = commandActionHelper.handleReply(&message); + CommandMessage command; + while (actionQueue->receiveMessage(&command) == HasReturnvaluesIF::RETURN_OK) { + ReturnValue_t result = commandActionHelper.handleReply(&command); if (result == HasReturnvaluesIF::RETURN_OK) { continue; } - message.setToUnknownCommand(); - actionQueue->reply(&message); + command.setToUnknownCommand(); + actionQueue->reply(&command); } return HasReturnvaluesIF::RETURN_OK; diff --git a/datapool/HkSwitchHelper.h b/datapool/HkSwitchHelper.h index 385b2047f..bb9e7dc68 100644 --- a/datapool/HkSwitchHelper.h +++ b/datapool/HkSwitchHelper.h @@ -13,7 +13,7 @@ class HkSwitchHelper: public ExecutableObjectIF, public CommandsActionsIF { public: static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::HK; - static const Event SWITCHING_TM_FAILED = MAKE_EVENT(1, SEVERITY::LOW); //!< Commanding the HK Service failed, p1: error code, p2 action: 0 disable / 1 enable + static const Event SWITCHING_TM_FAILED = MAKE_EVENT(1, severity::LOW); //!< Commanding the HK Service failed, p1: error code, p2 action: 0 disable / 1 enable HkSwitchHelper(EventReportingProxyIF *eventProxy); virtual ~HkSwitchHelper(); diff --git a/datapool/PIDReader.h b/datapool/PIDReader.h deleted file mode 100644 index 3b38b51d1..000000000 --- a/datapool/PIDReader.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef PIDREADER_H_ -#define PIDREADER_H_ -#include "DataPool.h" -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" -#include "../serialize/SerializeAdapter.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -template class PIDReaderList; - -template -class PIDReader: public PoolVariableIF { - template friend class PIDReaderList; -protected: - uint32_t parameterId; - uint8_t valid; - ReturnValue_t read() { - uint8_t arrayIndex = DataPool::PIDToArrayIndex(parameterId); - PoolEntry *read_out = ::dataPool.getData( - DataPool::PIDToDataPoolId(parameterId), arrayIndex); - if (read_out != NULL) { - valid = read_out->valid; - value = read_out->address[arrayIndex]; - return HasReturnvaluesIF::RETURN_OK; - } else { - value = 0; - valid = false; - sif::error << "PIDReader: read of PID 0x" << std::hex << parameterId - << std::dec << " failed." << std::endl; - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * Never commit, is read-only. - * Reason is the possibility to access a single DP vector element, but if we commit, - * we set validity of the whole vector. - */ - ReturnValue_t commit() { - return HasReturnvaluesIF::RETURN_FAILED; - } - /** - * Empty ctor for List initialization - */ - PIDReader() : - parameterId(PoolVariableIF::NO_PARAMETER), valid( - PoolVariableIF::INVALID), value(0) { - - } -public: - /** - * \brief This is the local copy of the data pool entry. - */ - T value; - /** - * \brief In the constructor, the variable can register itself in a DataSet (if not NULL is - * passed). - * \details It DOES NOT fetch the current value from the data pool, but sets the value - * attribute to default (0). The value is fetched within the read() operation. - * \param set_id This is the id in the global data pool this instance of the access class - * corresponds to. - * \param dataSet The data set in which the variable shall register itself. If NULL, - * the variable is not registered. - * \param setWritable If this flag is set to true, changes in the value attribute can be - * written back to the data pool, otherwise not. - */ - PIDReader(uint32_t setParameterId, DataSetIF *dataSet) : - parameterId(setParameterId), valid(PoolVariableIF::INVALID), value( - 0) { - if (dataSet != NULL) { - dataSet->registerVariable(this); - } - } - - /** - * Copy ctor to copy classes containing Pool Variables. - */ - PIDReader(const PIDReader &rhs) : - parameterId(rhs.parameterId), valid(rhs.valid), value(rhs.value) { - } - - /** - * \brief The classes destructor is empty. - */ - ~PIDReader() { - - } - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const { - return DataPool::PIDToDataPoolId(parameterId); - } - uint32_t getParameterId() const { - return parameterId; - } - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const { - return VAR_READ; - } - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const { - if (valid) - return true; - else - return false; - } - - uint8_t getValid() { - return valid; - } - - void setValid(uint8_t valid) { - this->valid = valid; - } - - operator T() { - return value; - } - - PIDReader& operator=(T newValue) { - value = newValue; - return *this; - } - - virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const override { - return SerializeAdapter::serialize(&value, buffer, size, maxSize, - streamEndianness); - } - - virtual size_t getSerializedSize() const override { - return SerializeAdapter::getSerializedSize(&value); - } - - virtual ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) override { - return SerializeAdapter::deSerialize(&value, buffer, size, - streamEndianness); - } -}; - -#endif /* PIDREADER_H_ */ diff --git a/datapool/PIDReaderList.h b/datapool/PIDReaderList.h deleted file mode 100644 index 1f6aa99cd..000000000 --- a/datapool/PIDReaderList.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ -#define FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ - -#include "PIDReader.h" -#include "PoolVariableIF.h" -template -class PIDReaderList { -private: - PIDReader variables[n_var]; -public: - PIDReaderList( const uint32_t setPid[n_var], DataSetIF* dataSet) { - //I really should have a look at the new init list c++ syntax. - if (dataSet == NULL) { - return; - } - for (uint8_t count = 0; count < n_var; count++) { - variables[count].parameterId = setPid[count]; - dataSet->registerVariable(&variables[count]); - } - } - - PIDReader &operator [](int i) { return variables[i]; } -}; - - - -#endif /* FRAMEWORK_DATAPOOL_PIDREADERLIST_H_ */ diff --git a/datapool/PoolDataSetBase.cpp b/datapool/PoolDataSetBase.cpp new file mode 100644 index 000000000..1bd586986 --- /dev/null +++ b/datapool/PoolDataSetBase.cpp @@ -0,0 +1,233 @@ +#include "PoolDataSetBase.h" +#include "ReadCommitIFAttorney.h" + +#include "../serviceinterface/ServiceInterface.h" + +#include + +PoolDataSetBase::PoolDataSetBase(PoolVariableIF** registeredVariablesArray, + const size_t maxFillCount): + registeredVariables(registeredVariablesArray), + maxFillCount(maxFillCount) {} + +PoolDataSetBase::~PoolDataSetBase() {} + + +ReturnValue_t PoolDataSetBase::registerVariable(PoolVariableIF *variable) { + if(registeredVariables == nullptr) { + /* Underlying container invalid */ + return HasReturnvaluesIF::RETURN_FAILED; + } + if (state != States::STATE_SET_UNINITIALISED) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DataSet::registerVariable: Call made in wrong position." << std::endl; +#else + sif::printError("DataSet::registerVariable: Call made in wrong position."); +#endif + return DataSetIF::DATA_SET_UNINITIALISED; + } + if (variable == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DataSet::registerVariable: Pool variable is nullptr." << std::endl; +#else + sif::printError("DataSet::registerVariable: Pool variable is nullptr.\n"); +#endif + return DataSetIF::POOL_VAR_NULL; + } + if (fillCount >= maxFillCount) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DataSet::registerVariable: DataSet is full." << std::endl; +#else + sif::printError("DataSet::registerVariable: DataSet is full.\n"); +#endif + return DataSetIF::DATA_SET_FULL; + } + registeredVariables[fillCount] = variable; + fillCount++; + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::read(MutexIF::TimeoutType timeoutType, + uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + ReturnValue_t error = result; + if (state == States::STATE_SET_UNINITIALISED) { + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + result = readVariable(count); + if(result != RETURN_OK) { + error = result; + } + } + state = States::STATE_SET_WAS_READ; + unlockDataPool(); + } + else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "PoolDataSetBase::read: Call made in wrong position. Don't forget to " + "commit member datasets!" << std::endl; +#else + sif::printWarning("PoolDataSetBase::read: Call made in wrong position. Don't forget to " + "commit member datasets!\n"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + result = SET_WAS_ALREADY_READ; + } + + if(error != HasReturnvaluesIF::RETURN_OK) { + result = error; + } + return result; +} + +uint16_t PoolDataSetBase::getFillCount() const { + return fillCount; +} + +ReturnValue_t PoolDataSetBase::readVariable(uint16_t count) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + if(registeredVariables[count] == nullptr) { + /* Configuration error. */ + return HasReturnvaluesIF::RETURN_FAILED; + } + + /* These checks are often performed by the respective variable implementation too, but I guess + a double check does not hurt. */ + if (registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_WRITE and + registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER) { + if(protectEveryReadCommitCall) { + result = registeredVariables[count]->read(timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + /* The readWithoutLock function is protected, so we use the attorney here */ + result = ReadCommitIFAttorney::readWithoutLock(registeredVariables[count]); + } + + if(result != HasReturnvaluesIF::RETURN_OK) { + result = INVALID_PARAMETER_DEFINITION; + } + } + return result; +} + +ReturnValue_t PoolDataSetBase::commit(MutexIF::TimeoutType timeoutType, + uint32_t lockTimeout) { + if (state == States::STATE_SET_WAS_READ) { + handleAlreadyReadDatasetCommit(timeoutType, lockTimeout); + return HasReturnvaluesIF::RETURN_OK; + } + else { + return handleUnreadDatasetCommit(timeoutType, lockTimeout); + } +} + +void PoolDataSetBase::handleAlreadyReadDatasetCommit( + MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if ((registeredVariables[count]->getReadWriteMode() != PoolVariableIF::VAR_READ) and + (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) { + if(protectEveryReadCommitCall) { + registeredVariables[count]->commit(timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + /* The commitWithoutLock function is protected, so we use the attorney here */ + ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]); + } + } + } + state = States::STATE_SET_UNINITIALISED; + unlockDataPool(); +} + +ReturnValue_t PoolDataSetBase::handleUnreadDatasetCommit( + MutexIF::TimeoutType timeoutType, uint32_t lockTimeout) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + lockDataPool(timeoutType, lockTimeout); + for (uint16_t count = 0; count < fillCount; count++) { + if ((registeredVariables[count]->getReadWriteMode() == PoolVariableIF::VAR_WRITE) and + (registeredVariables[count]->getDataPoolId() != PoolVariableIF::NO_PARAMETER)) { + if(protectEveryReadCommitCall) { + result = registeredVariables[count]->commit(timeoutTypeForSingleVars, + mutexTimeoutForSingleVars); + } + else { + /* The commitWithoutLock function is protected, so we use the attorney here */ + ReadCommitIFAttorney::commitWithoutLock(registeredVariables[count]); + } + + } else if (registeredVariables[count]->getDataPoolId() + != PoolVariableIF::NO_PARAMETER) { + if (result != COMMITING_WITHOUT_READING) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DataSet::commit(): commit-without-read call made " + "with non write-only variable." << std::endl; +#endif + result = COMMITING_WITHOUT_READING; + } + } + } + state = States::STATE_SET_UNINITIALISED; + unlockDataPool(); + return result; +} + + +ReturnValue_t PoolDataSetBase::lockDataPool(MutexIF::TimeoutType timeoutType, + uint32_t lockTimeout) { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::unlockDataPool() { + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t PoolDataSetBase::serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->serialize(buffer, size, maxSize, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +ReturnValue_t PoolDataSetBase::deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->deSerialize(buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + return result; +} + +size_t PoolDataSetBase::getSerializedSize() const { + uint32_t size = 0; + for (uint16_t count = 0; count < fillCount; count++) { + size += registeredVariables[count]->getSerializedSize(); + } + return size; +} + +void PoolDataSetBase::setContainer(PoolVariableIF **variablesContainer) { + this->registeredVariables = variablesContainer; +} + +PoolVariableIF** PoolDataSetBase::getContainer() const { + return registeredVariables; +} + +void PoolDataSetBase::setReadCommitProtectionBehaviour( + bool protectEveryReadCommit, MutexIF::TimeoutType timeoutType, + uint32_t mutexTimeout) { + this->protectEveryReadCommitCall = protectEveryReadCommit; + this->timeoutTypeForSingleVars = timeoutType; + this->mutexTimeoutForSingleVars = mutexTimeout; +} diff --git a/datapool/PoolDataSetBase.h b/datapool/PoolDataSetBase.h new file mode 100644 index 000000000..043c860a7 --- /dev/null +++ b/datapool/PoolDataSetBase.h @@ -0,0 +1,180 @@ +#ifndef FSFW_DATAPOOL_POOLDATASETBASE_H_ +#define FSFW_DATAPOOL_POOLDATASETBASE_H_ + +#include "PoolDataSetIF.h" +#include "PoolVariableIF.h" +#include "../serialize/SerializeIF.h" +#include "../ipc/MutexIF.h" + +/** + * @brief The DataSetBase class manages a set of locally checked out variables. + * @details + * This class manages a list, where a set of local variables (or pool variables) + * are registered. They are checked-out (i.e. their values are looked + * up and copied) with the read call. After the user finishes working with the + * pool variables, he can write back all variable values to the pool with + * the commit call. The data set manages locking and freeing the data pool, + * to ensure that all values are read and written back at once. + * + * An internal state manages usage of this class. Variables may only be + * registered before the read call is made, and the commit call only + * after the read call. + * + * If pool variables are writable and not committed until destruction + * of the set, the DataSet class automatically sets the valid flag in the + * data pool to invalid (without) changing the variable's value. + * + * The base class lockDataPool und unlockDataPool implementation are empty + * and should be implemented to protect the underlying pool type. + * @author Bastian Baetz + * @ingroup data_pool + */ +class PoolDataSetBase: + public PoolDataSetIF, + public SerializeIF, + public HasReturnvaluesIF { +public: + + /** + * @brief Creates an empty dataset. Use registerVariable or + * supply a pointer to this dataset to PoolVariable + * initializations to register pool variables. + */ + PoolDataSetBase(PoolVariableIF** registeredVariablesArray, const size_t maxFillCount); + + /* Forbidden for now */ + PoolDataSetBase(const PoolDataSetBase& otherSet) = delete; + const PoolDataSetBase& operator=(const PoolDataSetBase& otherSet) = delete; + + virtual~ PoolDataSetBase(); + + /** + * @brief The read call initializes reading out all registered variables. + * It is mandatory to call commit after every read call! + * @details + * It iterates through the list of registered variables and calls all read() + * functions of the registered pool variables (which read out their values + * from the data pool) which are not write-only. + * In case of an error (e.g. a wrong data type, or an invalid data pool id), + * the operation is aborted and @c INVALID_PARAMETER_DEFINITION returned. + * + * The data pool is locked during the whole read operation and + * freed afterwards. It is mandatory to call commit after a read call, + * even if the read operation is not successful! + * @return + * - @c RETURN_OK if all variables were read successfully. + * - @c INVALID_PARAMETER_DEFINITION if a pool entry does not exist or there + * is a type conflict. + * - @c SET_WAS_ALREADY_READ if read() is called twice without calling + * commit() in between + */ + virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t lockTimeout = 20) override; + /** + * @brief The commit call initializes writing back the registered variables. + * @details + * It iterates through the list of registered variables and calls the + * commit() method of the remaining registered variables (which write back + * their values to the pool). + * + * The data pool is locked during the whole commit operation and + * freed afterwards. The state changes to "was committed" after this operation. + * + * If the set does contain at least one variable which is not write-only + * commit() can only be called after read(). If the set only contains + * variables which are write only, commit() can be called without a + * preceding read() call. Every read call must be followed by a commit call! + * @return - @c RETURN_OK if all variables were read successfully. + * - @c COMMITING_WITHOUT_READING if set was not read yet and + * contains non write-only variables + */ + virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t lockTimeout = 20) override; + + /** + * Register the passed pool variable instance into the data set. + * @param variable + * @return + */ + virtual ReturnValue_t registerVariable( PoolVariableIF* variable) override; + + /** + * Provides the means to lock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t lockDataPool( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + /** + * Provides the means to unlock the underlying data structure to ensure + * thread-safety. Default implementation is empty + * @return Always returns -@c RETURN_OK + */ + virtual ReturnValue_t unlockDataPool() override; + + virtual uint16_t getFillCount() const; + + /* SerializeIF implementations */ + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + + /** + * Can be used to individually protect every read and commit call. + * @param protectEveryReadCommit + * @param mutexTimeout + */ + void setReadCommitProtectionBehaviour(bool protectEveryReadCommit, + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t mutexTimeout = 20); +protected: + + /** + * @brief The fill_count attribute ensures that the variables + * register in the correct array position and that the maximum + * number of variables is not exceeded. + */ + uint16_t fillCount = 0; + /** + * States of the seet. + */ + enum class States { + STATE_SET_UNINITIALISED, //!< DATA_SET_UNINITIALISED + STATE_SET_WAS_READ //!< DATA_SET_WAS_READ + }; + /** + * @brief state manages the internal state of the data set, + * which is important e.g. for the behavior on destruction. + */ + States state = States::STATE_SET_UNINITIALISED; + + /** + * @brief This array represents all pool variables registered in this set. + * Child classes can use a static or dynamic container to create + * an array of registered variables and assign the first entry here. + */ + PoolVariableIF** registeredVariables = nullptr; + const size_t maxFillCount = 0; + + void setContainer(PoolVariableIF** variablesContainer); + PoolVariableIF** getContainer() const; + +private: + bool protectEveryReadCommitCall = false; + MutexIF::TimeoutType timeoutTypeForSingleVars = MutexIF::TimeoutType::WAITING; + uint32_t mutexTimeoutForSingleVars = 20; + + ReturnValue_t readVariable(uint16_t count); + void handleAlreadyReadDatasetCommit( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); + ReturnValue_t handleUnreadDatasetCommit( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); +}; + +#endif /* FSFW_DATAPOOL_POOLDATASETBASE_H_ */ diff --git a/datapool/PoolDataSetIF.h b/datapool/PoolDataSetIF.h new file mode 100644 index 000000000..f905cc4d7 --- /dev/null +++ b/datapool/PoolDataSetIF.h @@ -0,0 +1,36 @@ +#ifndef FSFW_DATAPOOL_POOLDATASETIF_H_ +#define FSFW_DATAPOOL_POOLDATASETIF_H_ + +#include "ReadCommitIF.h" +#include "DataSetIF.h" + +/** + * @brief Extendes the DataSetIF by adding abstract functions to lock + * and unlock a data pool and read/commit semantics. + */ +class PoolDataSetIF: + virtual public DataSetIF, + virtual public ReadCommitIF { +public: + virtual~ PoolDataSetIF() {}; + + /** + * @brief Most underlying data structures will have a pool like structure + * and will require a lock and unlock mechanism to ensure + * thread-safety + * @return Lock operation result + */ + virtual ReturnValue_t lockDataPool( + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) = 0; + + /** + * @brief Unlock call corresponding to the lock call. + * @return Unlock operation result + */ + virtual ReturnValue_t unlockDataPool() = 0; + + virtual bool isValid() const = 0; +}; + +#endif /* FSFW_DATAPOOL_POOLDATASETIF_H_ */ diff --git a/datapool/PoolEntry.cpp b/datapool/PoolEntry.cpp index 56e489a5e..6504e20c5 100644 --- a/datapool/PoolEntry.cpp +++ b/datapool/PoolEntry.cpp @@ -1,28 +1,24 @@ #include "PoolEntry.h" -#include "../serviceinterface/ServiceInterfaceStream.h" + +#include "../serviceinterface/ServiceInterface.h" #include "../globalfunctions/arrayprinter.h" #include +#include template -PoolEntry::PoolEntry(std::initializer_list initValue, uint8_t setLength, - bool setValid ) : length(setLength), valid(setValid) { +PoolEntry::PoolEntry(std::initializer_list initValue, bool setValid ): + length(static_cast(initValue.size())), valid(setValid) { this->address = new T[this->length]; if(initValue.size() == 0) { std::memset(this->address, 0, this->getByteSize()); } - else if (initValue.size() != setLength){ - sif::warning << "PoolEntry: setLength is not equal to initializer list" - "length! Performing zero initialization with given setLength" - << std::endl; - std::memset(this->address, 0, this->getByteSize()); - } else { std::copy(initValue.begin(), initValue.end(), this->address); } } template -PoolEntry::PoolEntry( T* initValue, uint8_t setLength, bool setValid ) : +PoolEntry::PoolEntry(T* initValue, uint8_t setLength, bool setValid): length(setLength), valid(setValid) { this->address = new T[this->length]; if (initValue != nullptr) { @@ -66,10 +62,26 @@ bool PoolEntry::getValid() { template void PoolEntry::print() { - sif::debug << "Pool Entry Validity: " << - (this->valid? " (valid) " : " (invalid) ") << std::endl; - arrayprinter::print(reinterpret_cast(address), length); - sif::debug << std::dec << std::endl; + const char* validString = nullptr; + if(valid) { + validString = "Valid"; + } + else { + validString = "Invalid"; + } +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "PoolEntry information." << std::endl; + sif::info << "PoolEntry validity: " << validString << std::endl; +#else + sif::printInfo("PoolEntry information.\n"); + sif::printInfo("PoolEntry validity: %s\n", validString); +#endif + arrayprinter::print(reinterpret_cast(address), getByteSize()); +} + +template +inline T* PoolEntry::getDataPtr() { + return this->address; } template @@ -80,8 +92,10 @@ Type PoolEntry::getType() { template class PoolEntry; template class PoolEntry; template class PoolEntry; +template class PoolEntry; template class PoolEntry; template class PoolEntry; template class PoolEntry; +template class PoolEntry; template class PoolEntry; template class PoolEntry; diff --git a/datapool/PoolEntry.h b/datapool/PoolEntry.h index 7b47c6739..30940320c 100644 --- a/datapool/PoolEntry.h +++ b/datapool/PoolEntry.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DATAPOOL_POOLENTRY_H_ -#define FRAMEWORK_DATAPOOL_POOLENTRY_H_ +#ifndef FSFW_DATAPOOL_POOLENTRY_H_ +#define FSFW_DATAPOOL_POOLENTRY_H_ #include "PoolEntryIF.h" @@ -35,24 +35,22 @@ public: "uint8_t"); /** * @brief In the classe's constructor, space is allocated on the heap and - * potential init values are copied to that space. + * potential initialization values are copied to that space. * @details * Not passing any arguments will initialize an non-array pool entry - * (setLength = 1) with an initial invalid state. - * Please note that if an initializer list is passed, the correct - * corresponding length should be passed too, otherwise a zero - * initialization will be performed with the given setLength. + * with an initial invalid state and the value 0. + * Please note that if an initializer list is passed, the length of the + * initializer list needs to be correct for vector entries because + * required allocated space will be deduced from the initializer list length + * and the pool entry type. * @param initValue - * Initializer list with values to initialize with, for example {0,0} to - * initialize the two entries to zero. - * @param setLength - * Defines the array length of this entry. Should be equal to the - * intializer list length. + * Initializer list with values to initialize with, for example {0, 0} to + * initialize the a pool entry of a vector with two entries to 0. * @param setValid * Sets the initialization flag. It is invalid by default. */ - PoolEntry(std::initializer_list initValue = {}, uint8_t setLength = 1, - bool setValid = false); + PoolEntry(std::initializer_list initValue = {0}, bool setValid = false); + /** * @brief In the classe's constructor, space is allocated on the heap and * potential init values are copied to that space. @@ -66,9 +64,9 @@ public: */ PoolEntry(T* initValue, uint8_t setLength = 1, bool setValid = false); - //! Explicitely deleted copy ctor, copying is not allowed! + //! Explicitely deleted copy ctor, copying is not allowed. PoolEntry(const PoolEntry&) = delete; - //! Explicitely deleted copy assignment, copying is not allowed! + //! Explicitely deleted copy assignment, copying is not allowed. PoolEntry& operator=(const PoolEntry&) = delete; /** @@ -82,21 +80,16 @@ public: ~PoolEntry(); /** - * @brief This is the address pointing to the allocated memory. + * Return typed pointer to start of data. + * @return */ - T* address; - /** - * @brief This attribute stores the length information. - */ - uint8_t length; - /** - * @brief Here, the validity information for a variable is stored. - * Every entry (single variable or vector) has one valid flag. - */ - uint8_t valid; + T* getDataPtr(); + /** * @brief getSize returns the array size of the entry. - * @details A single parameter has size 1. + * @details + * For non-array pool entries return type size, for vector entries + * return type size times the number of entries. */ uint8_t getSize(); /** @@ -123,8 +116,22 @@ public: * information to the screen. It prints all array entries in a row. */ void print(); - Type getType(); + +private: + /** + * @brief This attribute stores the length information. + */ + uint8_t length; + /** + * @brief Here, the validity information for a variable is stored. + * Every entry (single variable or vector) has one valid flag. + */ + uint8_t valid; + /** + * @brief This is the address pointing to the allocated memory. + */ + T* address; }; -#endif /* POOLENTRY_H_ */ +#endif /* FSFW_DATAPOOL_POOLENTRY_H_ */ diff --git a/datapool/PoolEntryIF.h b/datapool/PoolEntryIF.h index 462de18b8..d9db52375 100644 --- a/datapool/PoolEntryIF.h +++ b/datapool/PoolEntryIF.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DATAPOOL_POOLENTRYIF_H_ -#define FRAMEWORK_DATAPOOL_POOLENTRYIF_H_ +#ifndef FSFW_DATAPOOL_POOLENTRYIF_H_ +#define FSFW_DATAPOOL_POOLENTRYIF_H_ #include "../globalfunctions/Type.h" #include @@ -60,4 +60,4 @@ public: virtual Type getType() = 0; }; -#endif /* POOLENTRYIF_H_ */ +#endif /* FSFW_DATAPOOL_POOLENTRYIF_H_ */ diff --git a/datapool/PoolRawAccess.cpp b/datapool/PoolRawAccess.cpp deleted file mode 100644 index f92640813..000000000 --- a/datapool/PoolRawAccess.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "DataPool.h" -#include "PoolEntryIF.h" -#include "PoolRawAccess.h" -#include "../serviceinterface/ServiceInterfaceStream.h" -#include "../serialize/EndianConverter.h" - -#include - -PoolRawAccess::PoolRawAccess(uint32_t set_id, uint8_t setArrayEntry, - DataSetIF *data_set, ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), arrayEntry(setArrayEntry), valid(false), type( - Type::UNKNOWN_TYPE), typeSize(0), arraySize(0), sizeTillEnd(0), readWriteMode( - setReadWriteMode) { - memset(value, 0, sizeof(value)); - if (data_set != NULL) { - data_set->registerVariable(this); - } -} - -PoolRawAccess::~PoolRawAccess() { - -} - -ReturnValue_t PoolRawAccess::read() { - PoolEntryIF *read_out = ::dataPool.getRawData(dataPoolId); - if (read_out != NULL) { - valid = read_out->getValid(); - if (read_out->getSize() > arrayEntry) { - arraySize = read_out->getSize(); - typeSize = read_out->getByteSize() / read_out->getSize(); - type = read_out->getType(); - if (typeSize <= sizeof(value)) { - uint16_t arrayPosition = arrayEntry * typeSize; - sizeTillEnd = read_out->getByteSize() - arrayPosition; - uint8_t *ptr = - &((uint8_t*) read_out->getRawData())[arrayPosition]; - memcpy(value, ptr, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - //Error value type too large. - } - } else { - //Error index requested too large - } - } else { - //Error entry does not exist. - } - sif::error << "PoolRawAccess: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - valid = INVALID; - typeSize = 0; - sizeTillEnd = 0; - memset(value, 0, sizeof(value)); - return HasReturnvaluesIF::RETURN_FAILED; -} - -ReturnValue_t PoolRawAccess::commit() { - PoolEntryIF *write_back = ::dataPool.getRawData(dataPoolId); - if ((write_back != NULL) && (readWriteMode != VAR_READ)) { - write_back->setValid(valid); - uint8_t array_position = arrayEntry * typeSize; - uint8_t *ptr = &((uint8_t*) write_back->getRawData())[array_position]; - memcpy(ptr, value, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } -} - -uint8_t* PoolRawAccess::getEntry() { - return value; -} - -ReturnValue_t PoolRawAccess::getEntryEndianSafe(uint8_t *buffer, - size_t *writtenBytes, size_t maxSize) { - uint8_t *data_ptr = getEntry(); -// debug << "PoolRawAccess::getEntry: Array position: " << index * size_of_type << " Size of T: " << (int)size_of_type << " ByteSize: " << byte_size << " Position: " << *size << std::endl; - if (typeSize == 0) { - return DATA_POOL_ACCESS_FAILED; - } - if (typeSize > maxSize) { - return INCORRECT_SIZE; - } - EndianConverter::convertBigEndian(buffer, data_ptr, typeSize); - *writtenBytes = typeSize; - return HasReturnvaluesIF::RETURN_OK; -} - -Type PoolRawAccess::getType() { - return type; -} - -size_t PoolRawAccess::getSizeOfType() { - return typeSize; -} - -size_t PoolRawAccess::getArraySize() { - return arraySize; -} - -uint32_t PoolRawAccess::getDataPoolId() const { - return dataPoolId; -} - -PoolVariableIF::ReadWriteMode_t PoolRawAccess::getReadWriteMode() const { - return readWriteMode; -} - -ReturnValue_t PoolRawAccess::setEntryFromBigEndian(const uint8_t *buffer, - size_t setSize) { - if (typeSize == setSize) { - EndianConverter::convertBigEndian(value, buffer, typeSize); - return HasReturnvaluesIF::RETURN_OK; - } else { - sif::error - << "PoolRawAccess::setEntryFromBigEndian: Illegal sizes: Internal" - << (uint32_t) typeSize << ", Requested: " << setSize - << std::endl; - return INCORRECT_SIZE; - } -} - -bool PoolRawAccess::isValid() const { - if (valid != INVALID) - return true; - else - return false; -} - -void PoolRawAccess::setValid(uint8_t valid) { - this->valid = valid; -} - -size_t PoolRawAccess::getSizeTillEnd() const { - return sizeTillEnd; -} - -ReturnValue_t PoolRawAccess::serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const { - if (typeSize + *size <= maxSize) { - switch (streamEndianness) { - case (Endianness::BIG): - EndianConverter::convertBigEndian(*buffer, value, typeSize); - break; - case (Endianness::LITTLE): - EndianConverter::convertLittleEndian(*buffer, value, typeSize); - break; - default: - case (Endianness::MACHINE): - memcpy(*buffer, value, typeSize); - break; - } - *size += typeSize; - (*buffer) += typeSize; - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::BUFFER_TOO_SHORT; - } -} - -size_t PoolRawAccess::getSerializedSize() const { - return typeSize; -} - -ReturnValue_t PoolRawAccess::deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) { - - if (*size >= typeSize) { - switch (streamEndianness) { - case (Endianness::BIG): - EndianConverter::convertBigEndian(value, *buffer, typeSize); - break; - case (Endianness::LITTLE): - EndianConverter::convertLittleEndian(value, *buffer, typeSize); - break; - default: - case (Endianness::MACHINE): - memcpy(value, *buffer, typeSize); - break; - } - *size -= typeSize; - *buffer += typeSize; - return HasReturnvaluesIF::RETURN_OK; - } else { - return SerializeIF::STREAM_TOO_SHORT; - } -} diff --git a/datapool/PoolRawAccess.h b/datapool/PoolRawAccess.h deleted file mode 100644 index 0c7a06bf6..000000000 --- a/datapool/PoolRawAccess.h +++ /dev/null @@ -1,152 +0,0 @@ -#ifndef POOLRAWACCESS_H_ -#define POOLRAWACCESS_H_ - -#include "DataSetIF.h" -#include "PoolVariableIF.h" - -/** - * This class allows accessing Data Pool variables as raw bytes. - * This is necessary to have an access method for HK data, as the PID's alone do not - * provide a type information. - * \ingroup data_pool - */ -class PoolRawAccess: public PoolVariableIF { -private: - /** - * \brief To access the correct data pool entry on read and commit calls, the data pool id - * is stored. - */ - uint32_t dataPoolId; - /** - * \brief The array entry that is fetched from the data pool. - */ - uint8_t arrayEntry; - /** - * \brief The valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief This value contains the type of the data pool entry. - */ - Type type; - /** - * \brief This value contains the size of the data pool entry in bytes. - */ - size_t typeSize; - /** - * The size of the DP array (single values return 1) - */ - size_t arraySize; - /** - * The size (in bytes) from the selected entry till the end of this DataPool variable. - */ - size_t sizeTillEnd; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - static const uint8_t RAW_MAX_SIZE = sizeof(double); -protected: - /** - * \brief This is a call to read the value from the global data pool. - * \details When executed, this operation tries to fetch the pool entry with matching - * data pool id from the global data pool and copies the value and the valid - * information to its local attributes. In case of a failure (wrong type or - * pool id not found), the variable is set to zero and invalid. - * The operation does NOT provide any mutual exclusive protection by itself. - */ - ReturnValue_t read(); - /** - * \brief The commit call writes back the variable's value to the data pool. - * \details It checks type and size, as well as if the variable is writable. If so, - * the value is copied and the valid flag is automatically set to "valid". - * The operation does NOT provide any mutual exclusive protection by itself. - * - */ - ReturnValue_t commit(); -public: - static const uint8_t INTERFACE_ID = CLASS_ID::POOL_RAW_ACCESS_CLASS; - static const ReturnValue_t INCORRECT_SIZE = MAKE_RETURN_CODE(0x01); - static const ReturnValue_t DATA_POOL_ACCESS_FAILED = MAKE_RETURN_CODE(0x02); - uint8_t value[RAW_MAX_SIZE]; - PoolRawAccess(uint32_t data_pool_id, uint8_t arrayEntry, - DataSetIF *data_set, ReadWriteMode_t setReadWriteMode = - PoolVariableIF::VAR_READ); - /** - * \brief The classes destructor is empty. If commit() was not called, the local value is - * discarded and not written back to the data pool. - */ - ~PoolRawAccess(); - /** - * \brief This operation returns a pointer to the entry fetched. - * \details This means, it does not return a pointer to byte "index", but to the start byte of - * array entry "index". Example: If the original data pool array consists of an double - * array of size four, getEntry(1) returns &(this->value[8]). - */ - uint8_t* getEntry(); - /** - * \brief This operation returns the fetched entry from the data pool and - * flips the bytes, if necessary. - * \details It makes use of the getEntry call of this function, but additionally flips the - * bytes to big endian, which is the default for external communication (as House- - * keeping telemetry). To achieve this, the data is copied directly to the passed - * buffer, if it fits in the given maxSize. - * \param buffer A pointer to a buffer to write to - * \param writtenBytes The number of bytes written is returned with this value. - * \param maxSize The maximum size that the function may write to buffer. - * \return - \c RETURN_OK if entry could be acquired - * - \c RETURN_FAILED else. - */ - ReturnValue_t getEntryEndianSafe(uint8_t *buffer, size_t *size, - size_t maxSize); - /** - * With this method, the content can be set from a big endian buffer safely. - * @param buffer Pointer to the data to set - * @param size Size of the data to write. Must fit this->size. - * @return - \c RETURN_OK on success - * - \c RETURN_FAILED on failure - */ - ReturnValue_t setEntryFromBigEndian(const uint8_t *buffer, - size_t setSize); - /** - * \brief This operation returns the type of the entry currently stored. - */ - Type getType(); - /** - * \brief This operation returns the size of the entry currently stored. - */ - size_t getSizeOfType(); - /** - * - * @return the size of the datapool array - */ - size_t getArraySize(); - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const; - /** - * This method returns if the variable is read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const; - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const; - - void setValid(uint8_t valid); - /** - * Getter for the remaining size. - */ - size_t getSizeTillEnd() const; - - ReturnValue_t serialize(uint8_t **buffer, size_t *size, size_t maxSize, - Endianness streamEndianness) const override; - - size_t getSerializedSize() const override; - - ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) override; -}; - -#endif /* POOLRAWACCESS_H_ */ diff --git a/datapool/PoolReadGuard.h b/datapool/PoolReadGuard.h new file mode 100644 index 000000000..eb014f1e7 --- /dev/null +++ b/datapool/PoolReadGuard.h @@ -0,0 +1,62 @@ +#ifndef FSFW_DATAPOOL_POOLREADHELPER_H_ +#define FSFW_DATAPOOL_POOLREADHELPER_H_ + +#include "ReadCommitIF.h" +#include "../serviceinterface/ServiceInterface.h" +#include + +/** + * @brief Helper class to read data sets or pool variables + */ +class PoolReadGuard { +public: + PoolReadGuard(ReadCommitIF* readObject, + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + uint32_t mutexTimeout = 20): + readObject(readObject), mutexTimeout(mutexTimeout) { + if(readObject != nullptr) { + readResult = readObject->read(timeoutType, mutexTimeout); + if(readResult != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_VERBOSE_LEVEL == 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "PoolReadHelper: Read failed!" << std::endl; +#else + sif::printError("PoolReadHelper: Read failed!\n"); +#endif /* FSFW_PRINT_VERBOSITY_LEVEL == 1 */ +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + } + } + + ReturnValue_t getReadResult() const { + return readResult; + } + + /** + * @brief Can be used to suppress commit on destruction. + */ + void setNoCommitMode(bool commit) { + this->noCommit = commit; + } + + /** + * @brief Default destructor which will take care of commiting changed values. + */ + ~PoolReadGuard() { + if(readObject != nullptr and not noCommit) { + readObject->commit(timeoutType, mutexTimeout); + } + + } + +private: + ReadCommitIF* readObject = nullptr; + ReturnValue_t readResult = HasReturnvaluesIF::RETURN_OK; + bool noCommit = false; + MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING; + uint32_t mutexTimeout = 20; +}; + + + +#endif /* FSFW_DATAPOOL_POOLREADHELPER_H_ */ diff --git a/datapool/PoolVarList.h b/datapool/PoolVarList.h index 035ff2ac8..0b2b0f064 100644 --- a/datapool/PoolVarList.h +++ b/datapool/PoolVarList.h @@ -1,14 +1,15 @@ -#ifndef POOLVARLIST_H_ -#define POOLVARLIST_H_ +#ifndef FSFW_DATAPOOL_POOLVARLIST_H_ +#define FSFW_DATAPOOL_POOLVARLIST_H_ -#include "PoolVariable.h" -#include "PoolVariableIF.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapoolglob/GlobalPoolVariable.h" template class PoolVarList { private: - PoolVariable variables[n_var]; + GlobPoolVar variables[n_var]; public: - PoolVarList( const uint32_t set_id[n_var], DataSetIF* dataSet, PoolVariableIF::ReadWriteMode_t setReadWriteMode ) { + PoolVarList( const uint32_t set_id[n_var], DataSetIF* dataSet, + PoolVariableIF::ReadWriteMode_t setReadWriteMode ) { //I really should have a look at the new init list c++ syntax. if (dataSet == NULL) { return; @@ -20,9 +21,9 @@ public: } } - PoolVariable &operator [](int i) { return variables[i]; } + GlobPoolVar &operator [](int i) { return variables[i]; } }; -#endif /* POOLVARLIST_H_ */ +#endif /* FSFW_DATAPOOL_POOLVARLIST_H_ */ diff --git a/datapool/PoolVariable.h b/datapool/PoolVariable.h deleted file mode 100644 index a9d3407d0..000000000 --- a/datapool/PoolVariable.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * \file PoolVariable.h - * - * \brief This file contains the PoolVariable class, which locally represents a non-array data pool variable. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVARIABLE_H_ -#define POOLVARIABLE_H_ - -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" -#include "../serialize/SerializeAdapter.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -template class PoolVarList; - -/** - * \brief This is the access class for non-array data pool entries. - * - * \details To ensure safe usage of the data pool, operation is not done directly on the data pool - * entries, but on local copies. This class provides simple type-safe access to single - * data pool entries (i.e. entries with length = 1). - * The class can be instantiated as read-write and read only. - * It provides a commit-and-roll-back semantic, which means that the variable's value in - * the data pool is not changed until the commit call is executed. - * \tparam T The template parameter sets the type of the variable. Currently, all plain data types - * are supported, but in principle any type is possible. - * \ingroup data_pool - */ -template -class PoolVariable: public PoolVariableIF { - template friend class PoolVarList; -protected: - /** - * \brief To access the correct data pool entry on read and commit calls, the data pool id - * is stored. - */ - uint32_t dataPoolId; - /** - * \brief The valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - /** - * \brief This is a call to read the value from the global data pool. - * \details When executed, this operation tries to fetch the pool entry with matching - * data pool id from the global data pool and copies the value and the valid - * information to its local attributes. In case of a failure (wrong type or - * pool id not found), the variable is set to zero and invalid. - * The operation does NOT provide any mutual exclusive protection by itself. - */ - ReturnValue_t read() { - PoolEntry *read_out = ::dataPool.getData < T > (dataPoolId, 1); - if (read_out != NULL) { - valid = read_out->valid; - value = *(read_out->address); - return HasReturnvaluesIF::RETURN_OK; - } else { - value = 0; - valid = false; - sif::error << "PoolVariable: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * \brief The commit call writes back the variable's value to the data pool. - * \details It checks type and size, as well as if the variable is writable. If so, - * the value is copied and the valid flag is automatically set to "valid". - * The operation does NOT provide any mutual exclusive protection by itself. - * - */ - ReturnValue_t commit() { - PoolEntry *write_back = ::dataPool.getData < T > (dataPoolId, 1); - if ((write_back != NULL) && (readWriteMode != VAR_READ)) { - write_back->valid = valid; - *(write_back->address) = value; - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * Empty ctor for List initialization - */ - PoolVariable() : - dataPoolId(PoolVariableIF::NO_PARAMETER), valid( - PoolVariableIF::INVALID), readWriteMode(VAR_READ), value(0) { - - } -public: - /** - * \brief This is the local copy of the data pool entry. - * \details The user can work on this attribute - * just like he would on a simple local variable. - */ - T value; - /** - * \brief In the constructor, the variable can register itself in a DataSet (if not NULL is - * passed). - * \details It DOES NOT fetch the current value from the data pool, but sets the value - * attribute to default (0). The value is fetched within the read() operation. - * \param set_id This is the id in the global data pool this instance of the access class - * corresponds to. - * \param dataSet The data set in which the variable shall register itself. If NULL, - * the variable is not registered. - * \param setWritable If this flag is set to true, changes in the value attribute can be - * written back to the data pool, otherwise not. - */ - PoolVariable(uint32_t set_id, DataSetIF *dataSet, - ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), valid(PoolVariableIF::INVALID), readWriteMode( - setReadWriteMode), value(0) { - if (dataSet != NULL) { - dataSet->registerVariable(this); - } - } - /** - * Copy ctor to copy classes containing Pool Variables. - */ - PoolVariable(const PoolVariable &rhs) : - dataPoolId(rhs.dataPoolId), valid(rhs.valid), readWriteMode( - rhs.readWriteMode), value(rhs.value) { - } - - /** - * \brief The classes destructor is empty. - * \details If commit() was not called, the local value is - * discarded and not written back to the data pool. - */ - ~PoolVariable() { - - } - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const { - return dataPoolId; - } - /** - * This operation sets the data pool id of the variable. - * The method is necessary to set id's of data pool member variables with bad initialization. - */ - void setDataPoolId(uint32_t poolId) { - dataPoolId = poolId; - } - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const { - return readWriteMode; - } - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const { - if (valid) - return true; - else - return false; - } - - uint8_t getValid() { - return valid; - } - - void setValid(uint8_t valid) { - this->valid = valid; - } - - operator T() { - return value; - } - - operator T() const { - return value; - } - - PoolVariable& operator=(T newValue) { - value = newValue; - return *this; - } - - PoolVariable& operator=(PoolVariable newPoolVariable) { - value = newPoolVariable.value; - return *this; - } - - virtual ReturnValue_t serialize(uint8_t **buffer, size_t *size, - size_t maxSize, Endianness streamEndianness) const override { - return SerializeAdapter::serialize(&value, buffer, size, maxSize, - streamEndianness); - } - - virtual size_t getSerializedSize() const override { - return SerializeAdapter::getSerializedSize(&value); - } - - virtual ReturnValue_t deSerialize(const uint8_t **buffer, size_t *size, - Endianness streamEndianness) override { - return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness); - } -}; - -typedef PoolVariable db_uint8_t; -typedef PoolVariable db_uint16_t; -typedef PoolVariable db_uint32_t; -typedef PoolVariable db_int8_t; -typedef PoolVariable db_int16_t; -typedef PoolVariable db_int32_t; -typedef PoolVariable db_bool_t; -typedef PoolVariable db_float_t; -typedef PoolVariable db_double_t; -//Alternative (but I thing this is not as useful: code duplication, differences too small): - -//template -//class PoolReader : public PoolVariableIF { -//private: -// uint32_t parameter_id; -// uint8_t valid; -//public: -// T value; -// PoolReader( uint32_t set_id, DataSetIF* set ) : parameter_id(set_id), valid(false), value(0) { -// set->registerVariable( this ); -// } -// -// ~PoolReader() {}; -// -// uint8_t commit() { -// return HasReturnvaluesIF::RETURN_OK; -// } -// -// uint8_t read() { -// PoolEntry* read_out = ::dataPool.getData( parameter_id, 1 ); -// if ( read_out != NULL ) { -// valid = read_out->valid; -// value = *(read_out->address); -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// value = 0; -// valid = false; -// return CHECKOUT_FAILED; -// } -// } -// uint32_t getParameterId() { return parameter_id; } -// bool isWritable() { return false; }; -// bool isValid() { if (valid) return true; else return false; } -//}; -// -//template -//class PoolWriter : public PoolVariableIF { -//private: -// uint32_t parameter_id; -//public: -// T value; -// PoolWriter( uint32_t set_id, DataSetIF* set ) : parameter_id(set_id), value(0) { -// set->registerVariable( this ); -// } -// -// ~PoolWriter() {}; -// -// uint8_t commit() { -// PoolEntry* write_back = ::dataPool.getData( parameter_id, 1 ); -// if ( write_back != NULL ) { -// write_back->valid = true; -// *(write_back->address) = value; -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// return CHECKOUT_FAILED; -// } -// } -// uint8_t read() { -// PoolEntry* read_out = ::dataPool.getData( parameter_id, 1 ); -// if ( read_out != NULL ) { -// value = *(read_out->address); -// return HasReturnvaluesIF::RETURN_OK; -// } else { -// value = 0; -// return CHECKOUT_FAILED; -// } -// } -// uint32_t getParameterId() { return parameter_id; } -// bool isWritable() { return true; }; -// bool isValid() { return false; } -//}; - -#endif /* POOLVARIABLE_H_ */ diff --git a/datapool/PoolVariableIF.h b/datapool/PoolVariableIF.h index f47173df3..dbb9db15c 100644 --- a/datapool/PoolVariableIF.h +++ b/datapool/PoolVariableIF.h @@ -1,71 +1,68 @@ -/* - * \file PoolVariableIF.h - * - * \brief This file contains the interface definition for pool variables. - * - * \date 10/17/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVARIABLEIF_H_ -#define POOLVARIABLEIF_H_ +#ifndef FSFW_DATAPOOL_POOLVARIABLEIF_H_ +#define FSFW_DATAPOOL_POOLVARIABLEIF_H_ +#include "ReadCommitIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../serialize/SerializeIF.h" + /** - * \brief This interface is used to control local data pool variable representations. - * - * \details To securely handle data pool variables, all pool entries are locally managed by - * data pool variable access classes, which are called pool variables. To ensure a - * common state of a set of variables needed in a function, these local pool variables - * again are managed by other classes, e.g. the DataSet. This interface provides unified - * access to local pool variables for such manager classes. - * \ingroup data_pool + * @brief This interface is used to control data pool + * variable representations. + * @details + * To securely handle data pool variables, all pool entries are locally + * managed by data pool variable access classes, which are called pool + * variables. To ensure a common state of a set of variables needed in a + * function, these local pool variables again are managed by other classes, + * like the DataSet classes. This interface provides unified access to + * local pool variables for such manager classes. + * @author Bastian Baetz + * @ingroup data_pool */ -class PoolVariableIF : public SerializeIF { - friend class DataSet; -protected: - /** - * \brief The commit call shall write back a newly calculated local value to the data pool. - */ - virtual ReturnValue_t commit() = 0; - /** - * \brief The read call shall read the value of this parameter from the data pool and store - * the content locally. - */ - virtual ReturnValue_t read() = 0; +class PoolVariableIF : + public SerializeIF, + public ReadCommitIF { + public: - static const uint8_t VALID = 1; - static const uint8_t INVALID = 0; - static const uint32_t NO_PARAMETER = 0; - enum ReadWriteMode_t { - VAR_READ, VAR_WRITE, VAR_READ_WRITE - }; + static constexpr uint8_t INTERFACE_ID = CLASS_ID::POOL_VARIABLE_IF; + static constexpr ReturnValue_t INVALID_READ_WRITE_MODE = MAKE_RETURN_CODE(0xA0); + static constexpr ReturnValue_t INVALID_POOL_ENTRY = MAKE_RETURN_CODE(0xA1); - /** - * \brief This is an empty virtual destructor, as it is proposed for C++ interfaces. - */ - virtual ~PoolVariableIF() { - } - /** - * \brief This method returns if the variable is write-only, read-write or read-only. - */ - virtual ReadWriteMode_t getReadWriteMode() const = 0; - /** - * \brief This operation shall return the data pool id of the variable. - */ - virtual uint32_t getDataPoolId() const = 0; - /** - * \brief With this call, the valid information of the variable is returned. - */ - virtual bool isValid() const = 0; - /** - * \brief With this call, the valid information of the variable is set. - */ - virtual void setValid(uint8_t validity) = 0; + static constexpr bool VALID = 1; + static constexpr bool INVALID = 0; + static constexpr uint32_t NO_PARAMETER = 0xffffffff; + enum ReadWriteMode_t { + VAR_READ, VAR_WRITE, VAR_READ_WRITE + }; + + /** + * @brief This is an empty virtual destructor, + * as it is proposed for C++ interfaces. + */ + virtual ~PoolVariableIF() {} + /** + * @brief This method returns if the variable is write-only, + * read-write or read-only. + */ + virtual ReadWriteMode_t getReadWriteMode() const = 0; + virtual void setReadWriteMode(ReadWriteMode_t newMode) = 0; + + /** + * @brief This operation shall return the data pool id of the variable. + */ + virtual uint32_t getDataPoolId() const = 0; + /** + * @brief With this call, the valid information of the + * variable is returned. + */ + virtual bool isValid() const = 0; + /** + * @brief With this call, the valid information of the variable is set. + */ + virtual void setValid(bool validity) = 0; }; -#endif /* POOLVARIABLEIF_H_ */ +using pool_rwm_t = PoolVariableIF::ReadWriteMode_t; + +#endif /* FSFW_DATAPOOL_POOLVARIABLEIF_H_ */ diff --git a/datapool/PoolVector.h b/datapool/PoolVector.h deleted file mode 100644 index d4ca08426..000000000 --- a/datapool/PoolVector.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * \file PoolVector.h - * - * \brief This file contains the PoolVector class, the header only class to handle data pool vectors. - * - * \date 10/23/2012 - * - * \author Bastian Baetz - */ - -#ifndef POOLVECTOR_H_ -#define POOLVECTOR_H_ - -#include "DataSetIF.h" -#include "PoolEntry.h" -#include "PoolVariableIF.h" -#include "../serialize/SerializeAdapter.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -/** - * \brief This is the access class for array-type data pool entries. - * - * \details To ensure safe usage of the data pool, operation is not done directly on the data pool - * entries, but on local copies. This class provides simple type- and length-safe access - * to vector-style data pool entries (i.e. entries with length > 1). - * The class can be instantiated as read-write and read only. - * It provides a commit-and-roll-back semantic, which means that no array entry in - * the data pool is changed until the commit call is executed. - * There are two template parameters: - * \tparam T This template parameter specifies the data type of an array entry. Currently, all - * plain data types are supported, but in principle any type is possible. - * \tparam vector_size This template parameter specifies the vector size of this entry. - * Using a template parameter for this is not perfect, but avoids dynamic memory allocation. - * \ingroup data_pool - */ -template -class PoolVector: public PoolVariableIF { -private: - /** - * \brief To access the correct data pool entry on read and commit calls, the data pool id - * is stored. - */ - uint32_t dataPoolId; - /** - * \brief The valid information as it was stored in the data pool is copied to this attribute. - */ - uint8_t valid; - /** - * \brief The information whether the class is read-write or read-only is stored here. - */ - ReadWriteMode_t readWriteMode; - -protected: - /** - * \brief This is a call to read the array's values from the global data pool. - * \details When executed, this operation tries to fetch the pool entry with matching - * data pool id from the global data pool and copies all array values and the valid - * information to its local attributes. In case of a failure (wrong type, size or - * pool id not found), the variable is set to zero and invalid. - * The operation does NOT provide any mutual exclusive protection by itself. - */ - ReturnValue_t read() { - PoolEntry* read_out = ::dataPool.getData(this->dataPoolId, - vector_size); - if (read_out != NULL) { - this->valid = read_out->valid; - memcpy(this->value, read_out->address, read_out->getByteSize()); - - return HasReturnvaluesIF::RETURN_OK; - - } else { - memset(this->value, 0, vector_size * sizeof(T)); - sif::error << "PoolVector: read of DP Variable 0x" << std::hex - << dataPoolId << std::dec << " failed." << std::endl; - this->valid = INVALID; - return HasReturnvaluesIF::RETURN_FAILED; - } - } - /** - * \brief The commit call copies the array values back to the data pool. - * \details It checks type and size, as well as if the variable is writable. If so, - * the value is copied and the valid flag is automatically set to "valid". - * The operation does NOT provide any mutual exclusive protection by itself. - * - */ - ReturnValue_t commit() { - PoolEntry* write_back = ::dataPool.getData(this->dataPoolId, - vector_size); - if ((write_back != NULL) && (this->readWriteMode != VAR_READ)) { - write_back->valid = valid; - memcpy(write_back->address, this->value, write_back->getByteSize()); - return HasReturnvaluesIF::RETURN_OK; - } else { - return HasReturnvaluesIF::RETURN_FAILED; - } - } -public: - /** - * \brief This is the local copy of the data pool entry. - * \detials The user can work on this attribute - * just like he would on a local array of this type. - */ - T value[vector_size]; - /** - * \brief In the constructor, the variable can register itself in a DataSet (if not NULL is - * passed). - * \details It DOES NOT fetch the current value from the data pool, but sets the value - * attribute to default (0). The value is fetched within the read() operation. - * \param set_id This is the id in the global data pool this instance of the access class - * corresponds to. - * \param dataSet The data set in which the variable shall register itself. If NULL, - * the variable is not registered. - * \param setWritable If this flag is set to true, changes in the value attribute can be - * written back to the data pool, otherwise not. - */ - PoolVector(uint32_t set_id, DataSetIF* set, - ReadWriteMode_t setReadWriteMode) : - dataPoolId(set_id), valid(false), readWriteMode(setReadWriteMode) { - memset(this->value, 0, vector_size * sizeof(T)); - if (set != NULL) { - set->registerVariable(this); - } - } - /** - * Copy ctor to copy classes containing Pool Variables. - */ -// PoolVector(const PoolVector& rhs) { -// PoolVector temp(rhs.dataPoolId, rhs.) -// memcpy(value, rhs.value, sizeof(T)*vector_size); -// } - /** - * \brief The classes destructor is empty. - * \details If commit() was not called, the local value is - * discarded and not written back to the data pool. - */ - ~PoolVector() { - } - ; - /** - * \brief The operation returns the number of array entries in this variable. - */ - uint8_t getSize() { - return vector_size; - } - /** - * \brief This operation returns the data pool id of the variable. - */ - uint32_t getDataPoolId() const { - return dataPoolId; - } - /** - * This operation sets the data pool id of the variable. - * The method is necessary to set id's of data pool member variables with bad initialization. - */ - void setDataPoolId(uint32_t poolId) { - dataPoolId = poolId; - } - /** - * This method returns if the variable is write-only, read-write or read-only. - */ - ReadWriteMode_t getReadWriteMode() const { - return readWriteMode; - } - ; - /** - * \brief With this call, the valid information of the variable is returned. - */ - bool isValid() const { - if (valid != INVALID) - return true; - else - return false; - } - - void setValid(uint8_t valid) { - this->valid = valid; - } - - uint8_t getValid() { - return valid; - } - - T &operator [](int i) { - return value[i]; - } - - const T &operator [](int i) const { - return value[i]; - } - - PoolVector &operator=( - PoolVector newPoolVector) { - - for (uint16_t i = 0; i < vector_size; i++) { - this->value[i] = newPoolVector.value[i]; - } - return *this; - } - - virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, - size_t maxSize, Endianness streamEndianness) const { - uint16_t i; - ReturnValue_t result; - for (i = 0; i < vector_size; i++) { - result = SerializeAdapter::serialize(&(value[i]), buffer, size, - maxSize, streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; - } - - virtual size_t getSerializedSize() const { - return vector_size * SerializeAdapter::getSerializedSize(value); - } - - virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, - Endianness streamEndianness) { - uint16_t i; - ReturnValue_t result; - for (i = 0; i < vector_size; i++) { - result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, - streamEndianness); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - } - return result; - } -}; - -#endif /* POOLVECTOR_H_ */ diff --git a/datapool/ReadCommitIF.h b/datapool/ReadCommitIF.h new file mode 100644 index 000000000..3ad2b3c02 --- /dev/null +++ b/datapool/ReadCommitIF.h @@ -0,0 +1,34 @@ +#ifndef FSFW_DATAPOOL_READCOMMITIF_H_ +#define FSFW_DATAPOOL_READCOMMITIF_H_ + +#include "../returnvalues/HasReturnvaluesIF.h" +#include "../ipc/MutexIF.h" + +/** + * @brief Common interface for all software objects which employ read-commit + * semantics. + */ +class ReadCommitIF { + friend class ReadCommitIFAttorney; +public: + virtual ~ReadCommitIF() {} + virtual ReturnValue_t read(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) = 0; + virtual ReturnValue_t commit(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) = 0; + +protected: + + /* Optional and protected because this is interesting for classes grouping members with commit + and read semantics where the lock is only necessary once. */ + virtual ReturnValue_t readWithoutLock() { + return read(MutexIF::TimeoutType::WAITING, 20); + } + + virtual ReturnValue_t commitWithoutLock() { + return commit(MutexIF::TimeoutType::WAITING, 20); + } +}; + + +#endif /* FSFW_DATAPOOL_READCOMMITIF_H_ */ diff --git a/datapool/ReadCommitIFAttorney.h b/datapool/ReadCommitIFAttorney.h new file mode 100644 index 000000000..0291cf5cc --- /dev/null +++ b/datapool/ReadCommitIFAttorney.h @@ -0,0 +1,32 @@ +#ifndef FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ +#define FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ + +#include +#include + +/** + * @brief This class determines which members are allowed to access protected members + * of the ReadCommitIF. + */ +class ReadCommitIFAttorney { +private: + static ReturnValue_t readWithoutLock(ReadCommitIF* readCommitIF) { + if(readCommitIF == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return readCommitIF->readWithoutLock(); + } + + static ReturnValue_t commitWithoutLock(ReadCommitIF* readCommitIF) { + if(readCommitIF == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + return readCommitIF->commitWithoutLock(); + } + + friend class PoolDataSetBase; +}; + + + +#endif /* FSFW_DATAPOOL_READCOMMITIFATTORNEY_H_ */ diff --git a/datapool/SharedDataSetIF.h b/datapool/SharedDataSetIF.h new file mode 100644 index 000000000..4d23f87f5 --- /dev/null +++ b/datapool/SharedDataSetIF.h @@ -0,0 +1,16 @@ +#ifndef FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ +#define FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ + +#include "PoolDataSetIF.h" + +class SharedDataSetIF { +public: + virtual ~SharedDataSetIF() {}; + +private: + virtual ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType, + dur_millis_t mutexTimeout) = 0; + virtual ReturnValue_t unlockDataset() = 0; +}; + +#endif /* FRAMEWORK_DATAPOOL_SHAREDDATASETIF_H_ */ diff --git a/datapoollocal/AccessLocalPoolF.h b/datapoollocal/AccessLocalPoolF.h new file mode 100644 index 000000000..55f46e215 --- /dev/null +++ b/datapoollocal/AccessLocalPoolF.h @@ -0,0 +1,27 @@ +#ifndef FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ +#define FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ + +class LocalDataPoolManager; +class MutexIF; + +/** + * @brief Accessor class which can be used by classes which like to use the pool manager. + */ +class AccessPoolManagerIF { +public: + virtual ~AccessPoolManagerIF() {}; + + virtual MutexIF* getLocalPoolMutex() = 0; + + /** + * Can be used to get a handle to the local data pool manager. + * This function is protected because it should only be used by the + * class imlementing the interface. + */ + virtual LocalDataPoolManager* getPoolManagerHandle() = 0; + +protected: + +}; + +#endif /* FSFW_DATAPOOLLOCAL_ACCESSLOCALPOOLF_H_ */ diff --git a/datapoollocal/CMakeLists.txt b/datapoollocal/CMakeLists.txt new file mode 100644 index 000000000..e2db39eb0 --- /dev/null +++ b/datapoollocal/CMakeLists.txt @@ -0,0 +1,10 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + LocalDataPoolManager.cpp + LocalDataSet.cpp + LocalPoolDataSetBase.cpp + LocalPoolObjectBase.cpp + SharedLocalDataSet.cpp +) + +add_subdirectory(internal) \ No newline at end of file diff --git a/datapoollocal/HasLocalDataPoolIF.h b/datapoollocal/HasLocalDataPoolIF.h new file mode 100644 index 000000000..74e372c9e --- /dev/null +++ b/datapoollocal/HasLocalDataPoolIF.h @@ -0,0 +1,186 @@ +#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ +#define FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ + +#include "localPoolDefinitions.h" +#include "LocalDataPoolManager.h" + +#include "../datapool/PoolEntryIF.h" +#include "../serviceinterface/ServiceInterface.h" +#include "../ipc/MessageQueueSenderIF.h" +#include "../housekeeping/HousekeepingMessage.h" + +#include + +class AccessPoolManagerIF; +class ProvidesDataPoolSubscriptionIF; +class LocalPoolDataSetBase; +class LocalPoolObjectBase; + +/** + * @brief This interface is implemented by classes which posses a local data pool (not the + * managing class). It defines the relationship between the local data pool owner + * and the LocalDataPoolManager. + * @details + * Any class implementing this interface shall also have a LocalDataPoolManager member class which + * contains the actual pool data structure and exposes the public interface for it. + * + * The local data pool can be accessed using helper classes by using the + * LocalPoolVariable, LocalPoolVector or LocalDataSet classes. Every local pool variable can + * be uniquely identified by a global pool ID (gp_id_t) and every dataset tied + * to a pool manager can be uniqely identified by a global structure ID (sid_t). + * + * All software objects which want to use the local pool of another object shall also use this + * interface, for example to get a handle to the subscription interface. The interface + * can be retrieved using the object manager, provided the target object is a SystemObject. + * For example, the following line of code can be used to retrieve the interface + * + * HasLocalDataPoolIF* poolIF = objectManager->get(objects::SOME_OBJECT); + * if(poolIF != nullptr) { + * doSomething() + * } + */ +class HasLocalDataPoolIF { + friend class HasLocalDpIFManagerAttorney; + friend class HasLocalDpIFUserAttorney; +public: + virtual~ HasLocalDataPoolIF() {}; + + static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID; + + virtual object_id_t getObjectId() const = 0; + + /** Command queue for housekeeping messages. */ + virtual MessageQueueId_t getCommandQueue() const = 0; + + /** + * Is used by pool owner to initialize the pool map once + * The manager instance shall also be passed to this function. + * It can be used to subscribe for periodic packets for for updates. + */ + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) = 0; + + /** + * Returns the minimum sampling frequency in milliseconds, which will + * usually be the period the pool owner performs its periodic operation. + * @return + */ + virtual dur_millis_t getPeriodicOperationFrequency() const = 0; + + /** + * @brief This function will be called by the manager if an update + * notification is received. + * @details HasLocalDataPoolIF + * Can be overriden by the child class to handle changed datasets. + * @param sid SID of the updated set + * @param storeId If a snapshot was requested, data will be located inside + * the IPC store with this store ID. + * @param clearMessage If this is set to true, the pool manager will take care of + * clearing the store automatically + */ + virtual void handleChangedDataset(sid_t sid, + store_address_t storeId = storeId::INVALID_STORE_ADDRESS, + bool* clearMessage = nullptr) { + if(clearMessage != nullptr) { + *clearMessage = true; + } + } + + /** + * @brief This function will be called by the manager if an update + * notification is received. + * @details + * Can be overriden by the child class to handle changed pool variables. + * @param gpid GPID of the updated variable. + * @param storeId If a snapshot was requested, data will be located inside + * the IPC store with this store ID. + * @param clearMessage Relevant for snapshots. If the boolean this points to is set to true, + * the pool manager will take care of clearing the store automatically + * after the callback. + */ + virtual void handleChangedPoolVariable(gp_id_t gpid, + store_address_t storeId = storeId::INVALID_STORE_ADDRESS, + bool* clearMessage = nullptr) { + if(clearMessage != nullptr) { + *clearMessage = true; + } + } + + /** + * These function can be implemented by pool owner, if they are required + * and used by the housekeeping message interface. + * */ + virtual ReturnValue_t addDataSet(sid_t sid) { + return HasReturnvaluesIF::RETURN_FAILED; + }; + virtual ReturnValue_t removeDataSet(sid_t sid) { + return HasReturnvaluesIF::RETURN_FAILED; + }; + virtual ReturnValue_t changeCollectionInterval(sid_t sid, float newIntervalSeconds) { + return HasReturnvaluesIF::RETURN_FAILED; + }; + + /** + * This function can be used by data pool consumers to retrieve a handle + * which allows subscriptions to dataset and variable updates in form of messages. + * The consumers can then read the most recent variable value by calling read with + * an own pool variable or set instance or using the deserialized snapshot data. + * Returns the HK manager casted to the required interface by default. + * @return + */ + virtual ProvidesDataPoolSubscriptionIF* getSubscriptionInterface() { + return getHkManagerHandle(); + } + +protected: + + /** + * Every class implementing this interface should have a local data pool manager. This + * function will return a reference to the manager. + * @return + */ + virtual LocalDataPoolManager* getHkManagerHandle() = 0; + + /** + * Accessor handle required for internal handling. Not intended for users and therefore + * declared protected. Users should instead use pool variables, sets or the subscription + * interface to access pool entries. Returns the HK manager casted to a different interface + * by default. + * @return + */ + virtual AccessPoolManagerIF* getAccessorHandle() { + return getHkManagerHandle(); + } + + /** + * This function is used by the pool manager to get a valid dataset + * from a SID. This function is protected to prevent users from + * using raw data set pointers which could not be thread-safe. Users + * should use the #ProvidesDataPoolSubscriptionIF. + * @param sid Corresponding structure ID + * @return + */ + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) = 0; + + /** + * Similar to the function above, but used to get a local pool variable + * handle. This is only needed for update notifications, so it is not + * defined as abstract. This function is protected to prevent users from + * using raw pool variable pointers which could not be thread-safe. + * Users should use the #ProvidesDataPoolSubscriptionIF. + * @param localPoolId + * @return + */ + virtual LocalPoolObjectBase* getPoolObjectHandle(lp_id_t localPoolId) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "HasLocalDataPoolIF::getPoolObjectHandle: Not overriden. " + "Returning nullptr!" << std::endl; +#else + sif::printWarning("HasLocalDataPoolIF::getPoolObjectHandle: " + "Not overriden. Returning nullptr!\n"); +#endif + return nullptr; + } +}; + +#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDATAPOOLIF_H_ */ diff --git a/datapoollocal/LocalDataPoolManager.cpp b/datapoollocal/LocalDataPoolManager.cpp new file mode 100644 index 000000000..dbe68ff14 --- /dev/null +++ b/datapoollocal/LocalDataPoolManager.cpp @@ -0,0 +1,942 @@ +#include "HasLocalDataPoolIF.h" +#include "LocalDataPoolManager.h" +#include "LocalPoolObjectBase.h" +#include "LocalPoolDataSetBase.h" +#include "internal/LocalPoolDataSetAttorney.h" +#include "internal/HasLocalDpIFManagerAttorney.h" + +#include "../housekeeping/HousekeepingSetPacket.h" +#include "../housekeeping/HousekeepingSnapshot.h" +#include "../housekeeping/AcceptsHkPacketsIF.h" +#include "../timemanager/CCSDSTime.h" +#include "../ipc/MutexFactory.h" +#include "../ipc/MutexGuard.h" +#include "../ipc/QueueFactory.h" + +#include +#include + +object_id_t LocalDataPoolManager::defaultHkDestination = objects::PUS_SERVICE_3_HOUSEKEEPING; + +LocalDataPoolManager::LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, + bool appendValidityBuffer): + appendValidityBuffer(appendValidityBuffer) { + if(owner == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "LocalDataPoolManager", HasReturnvaluesIF::RETURN_FAILED, + "Invalid supplied owner"); + return; + } + this->owner = owner; + mutex = MutexFactory::instance()->createMutex(); + if(mutex == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "LocalDataPoolManager", HasReturnvaluesIF::RETURN_FAILED, + "Could not create mutex"); + } + + hkQueue = queueToUse; +} + +LocalDataPoolManager::~LocalDataPoolManager() { + if(mutex != nullptr) { + MutexFactory::instance()->deleteMutex(mutex); + } +} + +ReturnValue_t LocalDataPoolManager::initialize(MessageQueueIF* queueToUse) { + if(queueToUse == nullptr) { + /* Error, all destinations invalid */ + printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", + QUEUE_OR_DESTINATION_INVALID); + } + hkQueue = queueToUse; + + ipcStore = objectManager->get(objects::IPC_STORE); + if(ipcStore == nullptr) { + /* Error, all destinations invalid */ + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "initialize", HasReturnvaluesIF::RETURN_FAILED, + "Could not set IPC store."); + return HasReturnvaluesIF::RETURN_FAILED; + } + + + if(defaultHkDestination != objects::NO_OBJECT) { + AcceptsHkPacketsIF* hkPacketReceiver = + objectManager->get(defaultHkDestination); + if(hkPacketReceiver != nullptr) { + hkDestinationId = hkPacketReceiver->getHkQueue(); + } + else { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "initialize", QUEUE_OR_DESTINATION_INVALID); + return QUEUE_OR_DESTINATION_INVALID; + } + } + + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::initializeAfterTaskCreation( + uint8_t nonDiagInvlFactor) { + setNonDiagnosticIntervalFactor(nonDiagInvlFactor); + return initializeHousekeepingPoolEntriesOnce(); +} + +ReturnValue_t LocalDataPoolManager::initializeHousekeepingPoolEntriesOnce() { + if(not mapInitialized) { + ReturnValue_t result = owner->initializeLocalDataPool(localPoolMap, + *this); + if(result == HasReturnvaluesIF::RETURN_OK) { + mapInitialized = true; + } + return result; + } + + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "initialize", HasReturnvaluesIF::RETURN_FAILED, + "The map should only be initialized once"); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::performHkOperation() { + ReturnValue_t status = HasReturnvaluesIF::RETURN_OK; + for(auto& receiver: hkReceivers) { + switch(receiver.reportingType) { + case(ReportingType::PERIODIC): { + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + /* Periodic packets shall only be generated from datasets */ + continue; + } + performPeriodicHkGeneration(receiver); + break; + } + case(ReportingType::UPDATE_HK): { + handleHkUpdate(receiver, status); + break; + } + case(ReportingType::UPDATE_NOTIFICATION): { + handleNotificationUpdate(receiver, status); + break; + } + case(ReportingType::UPDATE_SNAPSHOT): { + handleNotificationSnapshot(receiver, status); + break; + } + default: + // This should never happen. + return HasReturnvaluesIF::RETURN_FAILED; + } + } + resetHkUpdateResetHelper(); + return status; +} + +ReturnValue_t LocalDataPoolManager::handleHkUpdate(HkReceiver& receiver, + ReturnValue_t& status) { + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + /* Update packets shall only be generated from datasets. */ + return HasReturnvaluesIF::RETURN_FAILED; + } + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, + receiver.dataId.sid); + if(dataSet == nullptr) { + return DATASET_NOT_FOUND; + } + if(dataSet->hasChanged()) { + /* Prepare and send update notification */ + ReturnValue_t result = generateHousekeepingPacket( + receiver.dataId.sid, dataSet, true); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + } + handleChangeResetLogic(receiver.dataType, receiver.dataId, + dataSet); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::handleNotificationUpdate(HkReceiver& receiver, + ReturnValue_t& status) { + MarkChangedIF* toReset = nullptr; + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + LocalPoolObjectBase* poolObj = HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, + receiver.dataId.localPoolId); + if(poolObj == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "handleNotificationUpdate", POOLOBJECT_NOT_FOUND); + return POOLOBJECT_NOT_FOUND; + } + if(poolObj->hasChanged()) { + /* Prepare and send update notification. */ + CommandMessage notification; + HousekeepingMessage::setUpdateNotificationVariableCommand(¬ification, + gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId)); + ReturnValue_t result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = poolObj; + } + + } + else { + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, + receiver.dataId.sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "handleNotificationUpdate", DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + if(dataSet->hasChanged()) { + /* Prepare and send update notification */ + CommandMessage notification; + HousekeepingMessage::setUpdateNotificationSetCommand(¬ification, + receiver.dataId.sid); + ReturnValue_t result = hkQueue->sendMessage( + receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = dataSet; + } + } + if(toReset != nullptr) { + handleChangeResetLogic(receiver.dataType, receiver.dataId, toReset); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::handleNotificationSnapshot( + HkReceiver& receiver, ReturnValue_t& status) { + MarkChangedIF* toReset = nullptr; + /* Check whether data has changed and send messages in case it has */ + if(receiver.dataType == DataType::LOCAL_POOL_VARIABLE) { + LocalPoolObjectBase* poolObj = HasLocalDpIFManagerAttorney::getPoolObjectHandle(owner, + receiver.dataId.localPoolId); + if(poolObj == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "handleNotificationSnapshot", POOLOBJECT_NOT_FOUND); + return POOLOBJECT_NOT_FOUND; + } + + if (not poolObj->hasChanged()) { + return HasReturnvaluesIF::RETURN_OK; + } + + /* Prepare and send update snapshot */ + timeval now; + Clock::getClock_timeval(&now); + CCSDSTime::CDS_short cds; + CCSDSTime::convertToCcsds(&cds, &now); + HousekeepingSnapshot updatePacket(reinterpret_cast(&cds), sizeof(cds), + HasLocalDpIFManagerAttorney::getPoolObjectHandle( + owner,receiver.dataId.localPoolId)); + + store_address_t storeId; + ReturnValue_t result = addUpdateToStore(updatePacket, storeId); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + CommandMessage notification; + HousekeepingMessage::setUpdateSnapshotVariableCommand(¬ification, + gp_id_t(owner->getObjectId(), receiver.dataId.localPoolId), storeId); + result = hkQueue->sendMessage(receiver.destinationQueue, + ¬ification); + if (result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = poolObj; + } + else { + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, + receiver.dataId.sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "handleNotificationSnapshot", DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + + if(not dataSet->hasChanged()) { + return HasReturnvaluesIF::RETURN_OK; + } + + /* Prepare and send update snapshot */ + timeval now; + Clock::getClock_timeval(&now); + CCSDSTime::CDS_short cds; + CCSDSTime::convertToCcsds(&cds, &now); + HousekeepingSnapshot updatePacket(reinterpret_cast(&cds), + sizeof(cds), HasLocalDpIFManagerAttorney::getDataSetHandle(owner, + receiver.dataId.sid)); + + store_address_t storeId; + ReturnValue_t result = addUpdateToStore(updatePacket, storeId); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + CommandMessage notification; + HousekeepingMessage::setUpdateSnapshotSetCommand( + ¬ification, receiver.dataId.sid, storeId); + result = hkQueue->sendMessage(receiver.destinationQueue, ¬ification); + if(result != HasReturnvaluesIF::RETURN_OK) { + status = result; + } + toReset = dataSet; + + } + if(toReset != nullptr) { + handleChangeResetLogic(receiver.dataType, + receiver.dataId, toReset); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::addUpdateToStore( + HousekeepingSnapshot& updatePacket, store_address_t& storeId) { + size_t updatePacketSize = updatePacket.getSerializedSize(); + uint8_t *storePtr = nullptr; + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + updatePacket.getSerializedSize(), &storePtr); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + size_t serializedSize = 0; + result = updatePacket.serialize(&storePtr, &serializedSize, + updatePacketSize, SerializeIF::Endianness::MACHINE); + return result;; +} + +void LocalDataPoolManager::handleChangeResetLogic( + DataType type, DataId dataId, MarkChangedIF* toReset) { + if(hkUpdateResetList == nullptr) { + /* Config error */ + return; + } + HkUpdateResetList& listRef = *hkUpdateResetList; + for(auto& changeInfo: listRef) { + if(changeInfo.dataType != type) { + continue; + } + if((changeInfo.dataType == DataType::DATA_SET) and + (changeInfo.dataId.sid != dataId.sid)) { + continue; + } + if((changeInfo.dataType == DataType::LOCAL_POOL_VARIABLE) and + (changeInfo.dataId.localPoolId != dataId.localPoolId)) { + continue; + } + + /* Only one update recipient, we can reset changes status immediately */ + if(changeInfo.updateCounter <= 1) { + toReset->setChanged(false); + } + /* All recipients have been notified, reset the changed flag */ + else if(changeInfo.currentUpdateCounter <= 1) { + toReset->setChanged(false); + changeInfo.currentUpdateCounter = 0; + } + /* Not all recipiens have been notified yet, decrement */ + else { + changeInfo.currentUpdateCounter--; + } + return; + } +} + +void LocalDataPoolManager::resetHkUpdateResetHelper() { + if(hkUpdateResetList == nullptr) { + return; + } + + for(auto& changeInfo: *hkUpdateResetList) { + changeInfo.currentUpdateCounter = changeInfo.updateCounter; + } +} + +ReturnValue_t LocalDataPoolManager::subscribeForPeriodicPacket(sid_t sid, + bool enableReporting, float collectionInterval, bool isDiagnostics, + object_id_t packetDestination) { + AcceptsHkPacketsIF* hkReceiverObject = + objectManager->get(packetDestination); + if(hkReceiverObject == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "subscribeForPeriodicPacket", QUEUE_OR_DESTINATION_INVALID); + return QUEUE_OR_DESTINATION_INVALID; + } + + struct HkReceiver hkReceiver; + hkReceiver.dataId.sid = sid; + hkReceiver.reportingType = ReportingType::PERIODIC; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); + + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet != nullptr) { + LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enableReporting); + LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); + LocalPoolDataSetAttorney::initializePeriodicHelper(*dataSet, collectionInterval, + owner->getPeriodicOperationFrequency()); + } + + hkReceivers.push_back(hkReceiver); + return HasReturnvaluesIF::RETURN_OK; +} + + +ReturnValue_t LocalDataPoolManager::subscribeForUpdatePacket(sid_t sid, + bool isDiagnostics, bool reportingEnabled, + object_id_t packetDestination) { + AcceptsHkPacketsIF* hkReceiverObject = + objectManager->get(packetDestination); + if(hkReceiverObject == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "subscribeForPeriodicPacket", QUEUE_OR_DESTINATION_INVALID); + return QUEUE_OR_DESTINATION_INVALID; + } + + struct HkReceiver hkReceiver; + hkReceiver.dataId.sid = sid; + hkReceiver.reportingType = ReportingType::UPDATE_HK; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.destinationQueue = hkReceiverObject->getHkQueue(); + + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet != nullptr) { + LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, true); + LocalPoolDataSetAttorney::setDiagnostic(*dataSet, isDiagnostics); + } + + hkReceivers.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::subscribeForSetUpdateMessage( + const uint32_t setId, object_id_t destinationObject, + MessageQueueId_t targetQueueId, bool generateSnapshot) { + struct HkReceiver hkReceiver; + hkReceiver.dataType = DataType::DATA_SET; + hkReceiver.dataId.sid = sid_t(owner->getObjectId(), setId); + hkReceiver.destinationQueue = targetQueueId; + hkReceiver.objectId = destinationObject; + if(generateSnapshot) { + hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT; + } + else { + hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; + } + + hkReceivers.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::subscribeForVariableUpdateMessage( + const lp_id_t localPoolId, object_id_t destinationObject, + MessageQueueId_t targetQueueId, bool generateSnapshot) { + struct HkReceiver hkReceiver; + hkReceiver.dataType = DataType::LOCAL_POOL_VARIABLE; + hkReceiver.dataId.localPoolId = localPoolId; + hkReceiver.destinationQueue = targetQueueId; + hkReceiver.objectId = destinationObject; + if(generateSnapshot) { + hkReceiver.reportingType = ReportingType::UPDATE_SNAPSHOT; + } + else { + hkReceiver.reportingType = ReportingType::UPDATE_NOTIFICATION; + } + + hkReceivers.push_back(hkReceiver); + + handleHkUpdateResetListInsertion(hkReceiver.dataType, hkReceiver.dataId); + return HasReturnvaluesIF::RETURN_OK; +} + +void LocalDataPoolManager::handleHkUpdateResetListInsertion(DataType dataType, + DataId dataId) { + if(hkUpdateResetList == nullptr) { + hkUpdateResetList = new std::vector(); + } + + for(auto& updateResetStruct: *hkUpdateResetList) { + if(dataType == DataType::DATA_SET) { + if(updateResetStruct.dataId.sid == dataId.sid) { + updateResetStruct.updateCounter++; + updateResetStruct.currentUpdateCounter++; + return; + } + } + else { + if(updateResetStruct.dataId.localPoolId == dataId.localPoolId) { + updateResetStruct.updateCounter++; + updateResetStruct.currentUpdateCounter++; + return; + } + } + + } + HkUpdateResetHelper hkUpdateResetHelper; + hkUpdateResetHelper.currentUpdateCounter = 1; + hkUpdateResetHelper.updateCounter = 1; + hkUpdateResetHelper.dataType = dataType; + if(dataType == DataType::DATA_SET) { + hkUpdateResetHelper.dataId.sid = dataId.sid; + } + else { + hkUpdateResetHelper.dataId.localPoolId = dataId.localPoolId; + } + hkUpdateResetList->push_back(hkUpdateResetHelper); +} + +ReturnValue_t LocalDataPoolManager::handleHousekeepingMessage( + CommandMessage* message) { + Command_t command = message->getCommand(); + sid_t sid = HousekeepingMessage::getSid(message); + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + switch(command) { + // Houskeeping interface handling. + case(HousekeepingMessage::ENABLE_PERIODIC_DIAGNOSTICS_GENERATION): { + result = togglePeriodicGeneration(sid, true, true); + break; + } + + case(HousekeepingMessage::DISABLE_PERIODIC_DIAGNOSTICS_GENERATION): { + result = togglePeriodicGeneration(sid, false, true); + break; + } + + case(HousekeepingMessage::ENABLE_PERIODIC_HK_REPORT_GENERATION): { + result = togglePeriodicGeneration(sid, true, false); + break; + } + + case(HousekeepingMessage::DISABLE_PERIODIC_HK_REPORT_GENERATION): { + result = togglePeriodicGeneration(sid, false, false); + break; + } + + case(HousekeepingMessage::REPORT_DIAGNOSTICS_REPORT_STRUCTURES): { + result = generateSetStructurePacket(sid, true); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + break; + } + + case(HousekeepingMessage::REPORT_HK_REPORT_STRUCTURES): { + result = generateSetStructurePacket(sid, false); + if(result == HasReturnvaluesIF::RETURN_OK) { + return result; + } + break; + } + case(HousekeepingMessage::MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL): + case(HousekeepingMessage::MODIFY_PARAMETER_REPORT_COLLECTION_INTERVAL): { + float newCollIntvl = 0; + HousekeepingMessage::getCollectionIntervalModificationCommand(message, + &newCollIntvl); + if(command == HousekeepingMessage:: + MODIFY_DIAGNOSTICS_REPORT_COLLECTION_INTERVAL) { + result = changeCollectionInterval(sid, newCollIntvl, true); + } + else { + result = changeCollectionInterval(sid, newCollIntvl, false); + } + break; + } + + case(HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT): + case(HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT): { + LocalPoolDataSetBase* dataSet =HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(command == HousekeepingMessage::GENERATE_ONE_PARAMETER_REPORT + and LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { + result = WRONG_HK_PACKET_TYPE; + break; + } + else if(command == HousekeepingMessage::GENERATE_ONE_DIAGNOSTICS_REPORT + and not LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { + result = WRONG_HK_PACKET_TYPE; + break; + } + return generateHousekeepingPacket(HousekeepingMessage::getSid(message), + dataSet, true); + } + + /* Notification handling */ + case(HousekeepingMessage::UPDATE_NOTIFICATION_SET): { + owner->handleChangedDataset(sid); + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_NOTIFICATION_VARIABLE): { + gp_id_t globPoolId = HousekeepingMessage::getUpdateNotificationVariableCommand(message); + owner->handleChangedPoolVariable(globPoolId); + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_SNAPSHOT_SET): { + store_address_t storeId; + HousekeepingMessage::getUpdateSnapshotSetCommand(message, &storeId); + bool clearMessage = true; + owner->handleChangedDataset(sid, storeId, &clearMessage); + if(clearMessage) { + message->clear(); + } + return HasReturnvaluesIF::RETURN_OK; + } + case(HousekeepingMessage::UPDATE_SNAPSHOT_VARIABLE): { + store_address_t storeId; + gp_id_t globPoolId = HousekeepingMessage::getUpdateSnapshotVariableCommand(message, + &storeId); + bool clearMessage = true; + owner->handleChangedPoolVariable(globPoolId, storeId, &clearMessage); + if(clearMessage) { + message->clear(); + } + return HasReturnvaluesIF::RETURN_OK; + } + + default: + return CommandMessageIF::UNKNOWN_COMMAND; + } + + CommandMessage reply; + if(result != HasReturnvaluesIF::RETURN_OK) { + HousekeepingMessage::setHkRequestFailureReply(&reply, sid, result); + } + else { + HousekeepingMessage::setHkRequestSuccessReply(&reply, sid); + } + hkQueue->sendMessage(hkDestinationId, &reply); + return result; +} + +ReturnValue_t LocalDataPoolManager::printPoolEntry( + lp_id_t localPoolId) { + auto poolIter = localPoolMap.find(localPoolId); + if (poolIter == localPoolMap.end()) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "printPoolEntry", + localpool::POOL_ENTRY_NOT_FOUND); + return localpool::POOL_ENTRY_NOT_FOUND; + } + poolIter->second->print(); + return HasReturnvaluesIF::RETURN_OK; +} + +MutexIF* LocalDataPoolManager::getMutexHandle() { + return mutex; +} + +HasLocalDataPoolIF* LocalDataPoolManager::getOwner() { + return owner; +} + +ReturnValue_t LocalDataPoolManager::generateHousekeepingPacket(sid_t sid, + LocalPoolDataSetBase* dataSet, bool forDownlink, + MessageQueueId_t destination) { + if(dataSet == nullptr) { + /* Configuration error. */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "generateHousekeepingPacket", + DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + + store_address_t storeId; + HousekeepingPacketDownlink hkPacket(sid, dataSet); + size_t serializedSize = 0; + ReturnValue_t result = serializeHkPacketIntoStore(hkPacket, storeId, + forDownlink, &serializedSize); + if(result != HasReturnvaluesIF::RETURN_OK or serializedSize == 0) { + return result; + } + + /* Now we set a HK message and send it the HK packet destination. */ + CommandMessage hkMessage; + if(LocalPoolDataSetAttorney::isDiagnostics(*dataSet)) { + HousekeepingMessage::setHkDiagnosticsReply(&hkMessage, sid, storeId); + } + else { + HousekeepingMessage::setHkReportReply(&hkMessage, sid, storeId); + } + + if(hkQueue == nullptr) { + /* Error, no queue available to send packet with. */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "generateHousekeepingPacket", + QUEUE_OR_DESTINATION_INVALID); + return QUEUE_OR_DESTINATION_INVALID; + } + if(destination == MessageQueueIF::NO_QUEUE) { + if(hkDestinationId == MessageQueueIF::NO_QUEUE) { + /* Error, all destinations invalid */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "generateHousekeepingPacket", + QUEUE_OR_DESTINATION_INVALID); + } + destination = hkDestinationId; + } + + return hkQueue->sendMessage(destination, &hkMessage); +} + +ReturnValue_t LocalDataPoolManager::serializeHkPacketIntoStore( + HousekeepingPacketDownlink& hkPacket, + store_address_t& storeId, bool forDownlink, + size_t* serializedSize) { + uint8_t* dataPtr = nullptr; + const size_t maxSize = hkPacket.getSerializedSize(); + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + maxSize, &dataPtr); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + + if(forDownlink) { + return hkPacket.serialize(&dataPtr, serializedSize, maxSize, + SerializeIF::Endianness::BIG); + } + return hkPacket.serialize(&dataPtr, serializedSize, maxSize, + SerializeIF::Endianness::MACHINE); +} + +void LocalDataPoolManager::setNonDiagnosticIntervalFactor( + uint8_t nonDiagInvlFactor) { + this->nonDiagnosticIntervalFactor = nonDiagInvlFactor; +} + +void LocalDataPoolManager::performPeriodicHkGeneration(HkReceiver& receiver) { + sid_t sid = receiver.dataId.sid; + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "performPeriodicHkGeneration", + DATASET_NOT_FOUND); + return; + } + + if(not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet)) { + return; + } + + PeriodicHousekeepingHelper* periodicHelper = + LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet); + + if(periodicHelper == nullptr) { + /* Configuration error */ + return; + } + + if(not periodicHelper->checkOpNecessary()) { + return; + } + + ReturnValue_t result = generateHousekeepingPacket( + sid, dataSet, true); + if(result != HasReturnvaluesIF::RETURN_OK) { + /* Configuration error */ +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalDataPoolManager::performHkOperation: HK generation failed." << + std::endl; +#else + sif::printWarning("LocalDataPoolManager::performHkOperation: HK generation failed.\n"); +#endif + } +} + + +ReturnValue_t LocalDataPoolManager::togglePeriodicGeneration(sid_t sid, + bool enable, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "togglePeriodicGeneration", + DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + + if((LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and not isDiagnostics) or + (not LocalPoolDataSetAttorney::isDiagnostics(*dataSet) and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + if((LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and enable) or + (not LocalPoolDataSetAttorney::getReportingEnabled(*dataSet) and not enable)) { + return REPORTING_STATUS_UNCHANGED; + } + + LocalPoolDataSetAttorney::setReportingEnabled(*dataSet, enable); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::changeCollectionInterval(sid_t sid, + float newCollectionInterval, bool isDiagnostics) { + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "changeCollectionInterval", + DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + + bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + PeriodicHousekeepingHelper* periodicHelper = + LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet); + + if(periodicHelper == nullptr) { + /* Configuration error, set might not have a corresponding pool manager */ + return PERIODIC_HELPER_INVALID; + } + + periodicHelper->changeCollectionInterval(newCollectionInterval); + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalDataPoolManager::generateSetStructurePacket(sid_t sid, + bool isDiagnostics) { + /* Get and check dataset first. */ + LocalPoolDataSetBase* dataSet = HasLocalDpIFManagerAttorney::getDataSetHandle(owner, sid); + if(dataSet == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "performPeriodicHkGeneration", DATASET_NOT_FOUND); + return DATASET_NOT_FOUND; + } + + + bool targetIsDiagnostics = LocalPoolDataSetAttorney::isDiagnostics(*dataSet); + if((targetIsDiagnostics and not isDiagnostics) or + (not targetIsDiagnostics and isDiagnostics)) { + return WRONG_HK_PACKET_TYPE; + } + + bool valid = dataSet->isValid(); + bool reportingEnabled = LocalPoolDataSetAttorney::getReportingEnabled(*dataSet); + float collectionInterval = LocalPoolDataSetAttorney::getPeriodicHelper(*dataSet)-> + getCollectionIntervalInSeconds(); + + // Generate set packet which can be serialized. + HousekeepingSetPacket setPacket(sid, + reportingEnabled, valid, collectionInterval, dataSet); + size_t expectedSize = setPacket.getSerializedSize(); + uint8_t* storePtr = nullptr; + store_address_t storeId; + ReturnValue_t result = ipcStore->getFreeElement(&storeId, + expectedSize,&storePtr); + if(result != HasReturnvaluesIF::RETURN_OK) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "generateSetStructurePacket", HasReturnvaluesIF::RETURN_FAILED, + "Could not get free element from IPC store."); + return result; + } + + // Serialize set packet into store. + size_t size = 0; + result = setPacket.serialize(&storePtr, &size, expectedSize, + SerializeIF::Endianness::BIG); + if(expectedSize != size) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "generateSetStructurePacket", HasReturnvaluesIF::RETURN_FAILED, + "Expected size is not equal to serialized size"); + } + + // Send structure reporting reply. + CommandMessage reply; + if(isDiagnostics) { + HousekeepingMessage::setDiagnosticsStuctureReportReply(&reply, + sid, storeId); + } + else { + HousekeepingMessage::setHkStuctureReportReply(&reply, + sid, storeId); + } + + hkQueue->reply(&reply); + return result; +} + +void LocalDataPoolManager::clearReceiversList() { + /* Clear the vector completely and releases allocated memory. */ + HkReceivers().swap(hkReceivers); + /* Also clear the reset helper if it exists */ + if(hkUpdateResetList != nullptr) { + HkUpdateResetList().swap(*hkUpdateResetList); + } +} + +MutexIF* LocalDataPoolManager::getLocalPoolMutex() { + return this->mutex; +} + +object_id_t LocalDataPoolManager::getCreatorObjectId() const { + return owner->getObjectId(); +} + +void LocalDataPoolManager::printWarningOrError(sif::OutputTypes outputType, + const char* functionName, ReturnValue_t error, const char* errorPrint) { +#if FSFW_VERBOSE_LEVEL >= 1 + if(errorPrint == nullptr) { + if(error == DATASET_NOT_FOUND) { + errorPrint = "Dataset not found"; + } + else if(error == POOLOBJECT_NOT_FOUND) { + errorPrint = "Pool Object not found"; + } + else if(error == HasReturnvaluesIF::RETURN_FAILED) { + if(outputType == sif::OutputTypes::OUT_WARNING) { + errorPrint = "Generic Warning"; + } + else { + errorPrint = "Generic error"; + } + } + else if(error == QUEUE_OR_DESTINATION_INVALID) { + errorPrint = "Queue or destination not set"; + } + else if(error == localpool::POOL_ENTRY_TYPE_CONFLICT) { + errorPrint = "Pool entry type conflict"; + } + else if(error == localpool::POOL_ENTRY_NOT_FOUND) { + errorPrint = "Pool entry not found"; + } + else { + errorPrint = "Unknown error"; + } + } + object_id_t objectId = 0xffffffff; + if(owner != nullptr) { + objectId = owner->getObjectId(); + } + + if(outputType == sif::OutputTypes::OUT_WARNING) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << + std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint << + std::dec << std::setfill(' ') << std::endl; +#else + sif::printWarning("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", + functionName, objectId, errorPrint); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } + else if(outputType == sif::OutputTypes::OUT_ERROR) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "LocalDataPoolManager::" << functionName << ": Object ID 0x" << + std::setw(8) << std::setfill('0') << std::hex << objectId << " | " << errorPrint << + std::dec << std::setfill(' ') << std::endl; +#else + sif::printError("LocalDataPoolManager::%s: Object ID 0x%08x | %s\n", + functionName, objectId, errorPrint); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + } +#endif /* #if FSFW_VERBOSE_LEVEL >= 1 */ +} + +LocalDataPoolManager* LocalDataPoolManager::getPoolManagerHandle() { + return this; +} diff --git a/datapoollocal/LocalDataPoolManager.h b/datapoollocal/LocalDataPoolManager.h new file mode 100644 index 000000000..2ec81f1cf --- /dev/null +++ b/datapoollocal/LocalDataPoolManager.h @@ -0,0 +1,415 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ +#define FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ + +#include "ProvidesDataPoolSubscriptionIF.h" +#include "AccessLocalPoolF.h" + +#include "../serviceinterface/ServiceInterface.h" +#include "../housekeeping/HousekeepingPacketDownlink.h" +#include "../housekeeping/HousekeepingMessage.h" +#include "../housekeeping/PeriodicHousekeepingHelper.h" +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntry.h" +#include "../objectmanager/SystemObjectIF.h" +#include "../ipc/MutexIF.h" +#include "../ipc/CommandMessage.h" +#include "../ipc/MessageQueueIF.h" +#include "../ipc/MutexGuard.h" + +#include +#include + +namespace Factory { +void setStaticFrameworkObjectIds(); +} + +class LocalPoolDataSetBase; +class HousekeepingSnapshot; +class HasLocalDataPoolIF; +class LocalDataPool; + +/** + * @brief This class is the managing instance for the local data pool. + * @details + * The actual data pool structure is a member of this class. Any class which + * has a local data pool shall have this manager class as a member and implement + * the HasLocalDataPoolIF. + * + * The manager offers some adaption points and functions which can be used + * by the owning class to simplify data handling significantly. + * + * Please ensure that both initialize and initializeAfterTaskCreation are + * called at some point by the owning class in the respective functions of the + * same name! + * + * Users of the data pool use the helper classes LocalDataSet, + * LocalPoolVariable and LocalPoolVector to access pool entries in + * a thread-safe and efficient way. + * + * The local data pools employ a blackboard logic: Only the most recent + * value is stored. The helper classes offer a read() and commit() interface + * through the PoolVariableIF which is used to read and update values. + * Each pool entry has a valid state too. + * @author R. Mueller + */ +class LocalDataPoolManager: + public ProvidesDataPoolSubscriptionIF, + public AccessPoolManagerIF { + friend void (Factory::setStaticFrameworkObjectIds)(); + //! Some classes using the pool manager directly need to access class internals of the + //! manager. The attorney provides granular control of access to these internals. + friend class LocalDpManagerAttorney; +public: + static constexpr uint8_t INTERFACE_ID = CLASS_ID::HOUSEKEEPING_MANAGER; + + static constexpr ReturnValue_t QUEUE_OR_DESTINATION_INVALID = MAKE_RETURN_CODE(0); + static constexpr ReturnValue_t WRONG_HK_PACKET_TYPE = MAKE_RETURN_CODE(1); + static constexpr ReturnValue_t REPORTING_STATUS_UNCHANGED = MAKE_RETURN_CODE(2); + static constexpr ReturnValue_t PERIODIC_HELPER_INVALID = MAKE_RETURN_CODE(3); + static constexpr ReturnValue_t POOLOBJECT_NOT_FOUND = MAKE_RETURN_CODE(4); + static constexpr ReturnValue_t DATASET_NOT_FOUND = MAKE_RETURN_CODE(5); + + + /** + * This constructor is used by a class which wants to implement + * a personal local data pool. The queueToUse can be supplied if it + * is already known. + * + * initialize() has to be called in any case before using the object! + * @param owner + * @param queueToUse + * @param appendValidityBuffer Specify whether a buffer containing the + * validity state is generated when serializing or deserializing packets. + */ + LocalDataPoolManager(HasLocalDataPoolIF* owner, MessageQueueIF* queueToUse, + bool appendValidityBuffer = true); + virtual~ LocalDataPoolManager(); + + /** + * Assigns the queue to use. Make sure to call this in the #initialize + * function of the owner. + * @param queueToUse + * @param nonDiagInvlFactor See #setNonDiagnosticIntervalFactor doc + * @return + */ + ReturnValue_t initialize(MessageQueueIF* queueToUse); + + /** + * Initializes the map by calling the map initialization function and + * setting the periodic factor for non-diagnostic packets. + * Don't forget to call this in the #initializeAfterTaskCreation call of + * the owner, otherwise the map will be invalid! + * @param nonDiagInvlFactor + * @return + */ + ReturnValue_t initializeAfterTaskCreation( + uint8_t nonDiagInvlFactor = 5); + + /** + * @brief This should be called in the periodic handler of the owner. + * @details + * This in generally called in the #performOperation function of the owner. + * It performs all the periodic functionalities of the data pool manager, + * for example generating periodic HK packets. + * Marked virtual as an adaption point for custom data pool managers. + * @return + */ + virtual ReturnValue_t performHkOperation(); + + /** + * @brief Subscribe for the generation of periodic packets. + * @details + * This subscription mechanism will generally be used by the data creator + * to generate housekeeping packets which are downlinked directly. + * @return + */ + ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, + float collectionInterval, bool isDiagnostics, + object_id_t packetDestination = defaultHkDestination) override; + + /** + * @brief Subscribe for the generation of packets if the dataset + * is marked as changed. + * @details + * This subscription mechanism will generally be used by the data creator. + * @param sid + * @param isDiagnostics + * @param packetDestination + * @return + */ + ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, + bool isDiagnostics, + object_id_t packetDestination = defaultHkDestination) override; + + /** + * @brief Subscribe for a notification message which will be sent + * if a dataset has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param setId Set ID of the set to receive update messages from. + * @param destinationObject + * @param targetQueueId + * @param generateSnapshot If this is set to true, a copy of the current + * data with a timestamp will be generated and sent via message. + * Otherwise, only an notification message is sent. + * @return + */ + ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId, + object_id_t destinationObject, + MessageQueueId_t targetQueueId, + bool generateSnapshot) override; + + /** + * @brief Subscribe for an notification message which will be sent if a + * pool variable has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param localPoolId Pool ID of the pool variable + * @param destinationObject + * @param targetQueueId + * @param generateSnapshot If this is set to true, a copy of the current + * data with a timestamp will be generated and sent via message. + * Otherwise, only an notification message is sent. + * @return + */ + ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId, + object_id_t destinationObject, + MessageQueueId_t targetQueueId, + bool generateSnapshot) override; + + /** + * Non-Diagnostics packets usually have a lower minimum sampling frequency + * than diagnostic packets. + * A factor can be specified to determine the minimum sampling frequency + * for non-diagnostic packets. The minimum sampling frequency of the + * diagnostics packets,which is usually jusst the period of the + * performOperation calls, is multiplied with that factor. + * @param factor + */ + void setNonDiagnosticIntervalFactor(uint8_t nonDiagInvlFactor); + + /** + * @brief The manager is also able to handle housekeeping messages. + * @details + * This most commonly is used to handle messages for the housekeeping + * interface, but the manager is also able to handle update notifications + * and calls a special function which can be overriden by a child class + * to handle data set or pool variable updates. This is relevant + * for classes like controllers which have their own local datapool + * but pull their data from other local datapools. + * @param message + * @return + */ + virtual ReturnValue_t handleHousekeepingMessage(CommandMessage* message); + + /** + * Generate a housekeeping packet with a given SID. + * @param sid + * @return + */ + ReturnValue_t generateHousekeepingPacket(sid_t sid, + LocalPoolDataSetBase* dataSet, bool forDownlink, + MessageQueueId_t destination = MessageQueueIF::NO_QUEUE); + + HasLocalDataPoolIF* getOwner(); + + ReturnValue_t printPoolEntry(lp_id_t localPoolId); + + /** + * Different types of housekeeping reporting are possible. + * 1. PERIODIC: + * HK packets are generated in fixed intervals and sent to + * destination. Fromat will be raw. + * 2. UPDATE_NOTIFICATION: + * Notification will be sent out if HK data has changed. + * 3. UPDATE_SNAPSHOT: + * HK packets are only generated if explicitely requested. + * Propably not necessary, just use multiple local data sets or + * shared datasets. + */ + enum class ReportingType: uint8_t { + //! Periodic generation of HK packets. + PERIODIC, + //! Housekeeping packet will be generated if values have changed. + UPDATE_HK, + //! Update notification will be sent out as message. + UPDATE_NOTIFICATION, + //! Notification will be sent out as message and a snapshot of the + //! current data will be generated. + UPDATE_SNAPSHOT, + }; + + /** Different data types are possible in the HK receiver map. For example, updates can be + requested for full datasets or for single pool variables. Periodic reporting is only possible + for data sets. */ + enum class DataType: uint8_t { + LOCAL_POOL_VARIABLE, + DATA_SET + }; + + /* Copying forbidden */ + LocalDataPoolManager(const LocalDataPoolManager &) = delete; + LocalDataPoolManager operator=(const LocalDataPoolManager&) = delete; + + /** + * This function can be used to clear the receivers list. This is + * intended for test functions and not for regular operations, because + * the insertion operations allocate dynamically. + */ + void clearReceiversList(); + + object_id_t getCreatorObjectId() const; + + /** + * Get the pointer to the mutex. Can be used to lock the data pool + * externally. Use with care and don't forget to unlock locked mutexes! + * For now, only friend classes can accss this function. + * @return + */ + MutexIF* getMutexHandle(); + + virtual LocalDataPoolManager* getPoolManagerHandle() override; + +protected: + + /** Core data structure for the actual pool data */ + localpool::DataPool localPoolMap; + /** Every housekeeping data manager has a mutex to protect access + to it's data pool. */ + MutexIF* mutex = nullptr; + + /** The class which actually owns the manager (and its datapool). */ + HasLocalDataPoolIF* owner = nullptr; + + uint8_t nonDiagnosticIntervalFactor = 0; + + /** Default receiver for periodic HK packets */ + static object_id_t defaultHkDestination; + MessageQueueId_t hkDestinationId = MessageQueueIF::NO_QUEUE; + + union DataId { + DataId(): sid() {}; + sid_t sid; + lp_id_t localPoolId; + }; + + /** The data pool manager will keep an internal map of HK receivers. */ + struct HkReceiver { + /** Object ID of receiver */ + object_id_t objectId = objects::NO_OBJECT; + + DataType dataType = DataType::DATA_SET; + DataId dataId; + + ReportingType reportingType = ReportingType::PERIODIC; + MessageQueueId_t destinationQueue = MessageQueueIF::NO_QUEUE; + }; + + /** This vector will contain the list of HK receivers. */ + using HkReceivers = std::vector; + + HkReceivers hkReceivers; + + struct HkUpdateResetHelper { + DataType dataType = DataType::DATA_SET; + DataId dataId; + uint8_t updateCounter; + uint8_t currentUpdateCounter; + }; + + using HkUpdateResetList = std::vector; + /** This list is used to manage creating multiple update packets and only resetting + the update flag if all of them were created. Will only be created when needed. */ + HkUpdateResetList* hkUpdateResetList = nullptr; + + /** This is the map holding the actual data. Should only be initialized + * once ! */ + bool mapInitialized = false; + /** This specifies whether a validity buffer is appended at the end + * of generated housekeeping packets. */ + bool appendValidityBuffer = true; + + /** + * @brief Queue used for communication, for example commands. + * Is also used to send messages. Can be set either in the constructor + * or in the initialize() function. + */ + MessageQueueIF* hkQueue = nullptr; + + /** Global IPC store is used to store all packets. */ + StorageManagerIF* ipcStore = nullptr; + + /** + * Read a variable by supplying its local pool ID and assign the pool + * entry to the supplied PoolEntry pointer. The type of the pool entry + * is deduced automatically. This call is not thread-safe! + * For now, only classes designated by the LocalDpManagerAttorney may use this function. + * @tparam T Type of the pool entry + * @param localPoolId Pool ID of the variable to read + * @param poolVar [out] Corresponding pool entry will be assigned to the + * supplied pointer. + * @return + */ + template ReturnValue_t fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry); + + /** + * This function is used to fill the local data pool map with pool + * entries. It should only be called once by the pool owner. + * @param localDataPoolMap + * @return + */ + ReturnValue_t initializeHousekeepingPoolEntriesOnce(); + + MutexIF* getLocalPoolMutex() override; + + ReturnValue_t serializeHkPacketIntoStore(HousekeepingPacketDownlink& hkPacket, + store_address_t& storeId, bool forDownlink, size_t* serializedSize); + + void performPeriodicHkGeneration(HkReceiver& hkReceiver); + ReturnValue_t togglePeriodicGeneration(sid_t sid, bool enable, bool isDiagnostics); + ReturnValue_t changeCollectionInterval(sid_t sid, float newCollectionInterval, + bool isDiagnostics); + ReturnValue_t generateSetStructurePacket(sid_t sid, bool isDiagnostics); + + void handleHkUpdateResetListInsertion(DataType dataType, DataId dataId); + void handleChangeResetLogic(DataType type, + DataId dataId, MarkChangedIF* toReset); + void resetHkUpdateResetHelper(); + + ReturnValue_t handleHkUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t handleNotificationUpdate(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t handleNotificationSnapshot(HkReceiver& hkReceiver, ReturnValue_t& status); + ReturnValue_t addUpdateToStore(HousekeepingSnapshot& updatePacket, store_address_t& storeId); + + void printWarningOrError(sif::OutputTypes outputType, const char* functionName, + ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED, + const char* errorPrint = nullptr); +}; + + +template inline +ReturnValue_t LocalDataPoolManager::fetchPoolEntry(lp_id_t localPoolId, PoolEntry **poolEntry) { + if(poolEntry == nullptr) { + return HasReturnvaluesIF::RETURN_FAILED; + } + + auto poolIter = localPoolMap.find(localPoolId); + if (poolIter == localPoolMap.end()) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry", + localpool::POOL_ENTRY_NOT_FOUND); + return localpool::POOL_ENTRY_NOT_FOUND; + } + + *poolEntry = dynamic_cast< PoolEntry* >(poolIter->second); + if(*poolEntry == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, "fetchPoolEntry", + localpool::POOL_ENTRY_TYPE_CONFLICT); + return localpool::POOL_ENTRY_TYPE_CONFLICT; + } + return HasReturnvaluesIF::RETURN_OK; +} + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALDATAPOOLMANAGER_H_ */ diff --git a/datapoollocal/LocalDataSet.cpp b/datapoollocal/LocalDataSet.cpp new file mode 100644 index 000000000..aeb5c6b32 --- /dev/null +++ b/datapoollocal/LocalDataSet.cpp @@ -0,0 +1,22 @@ +#include "LocalDataSet.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../serialize/SerializeAdapter.h" + +#include +#include + +LocalDataSet::LocalDataSet(HasLocalDataPoolIF *hkOwner, uint32_t setId, + const size_t maxNumberOfVariables): + LocalPoolDataSetBase(hkOwner, setId, nullptr, maxNumberOfVariables), + poolVarList(maxNumberOfVariables) { + this->setContainer(poolVarList.data()); +} + +LocalDataSet::LocalDataSet(sid_t sid, const size_t maxNumberOfVariables): + LocalPoolDataSetBase(sid, nullptr, maxNumberOfVariables), + poolVarList(maxNumberOfVariables) { + this->setContainer(poolVarList.data()); +} + +LocalDataSet::~LocalDataSet() {} + diff --git a/datapoollocal/LocalDataSet.h b/datapoollocal/LocalDataSet.h new file mode 100644 index 000000000..92bad9b57 --- /dev/null +++ b/datapoollocal/LocalDataSet.h @@ -0,0 +1,36 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include + +/** + * @brief This dataset type can be used to group related pool variables if the number of + * variables should not be fixed. + * @details + * This will is the primary data structure to organize pool variables into + * sets which can be accessed via the housekeeping service interface or + * which can be sent to other software objects. + * + * It is recommended to read the documentation of the LocalPoolDataSetBase + * class for more information on how this class works and how to use it. + * @tparam capacity Capacity of the static dataset, which is usually known + * beforehand. + */ +class LocalDataSet: public LocalPoolDataSetBase { +public: + LocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId, + const size_t maxSize); + + LocalDataSet(sid_t sid, const size_t maxSize); + + virtual~ LocalDataSet(); + + //! Copying forbidden for now. + LocalDataSet(const LocalDataSet&) = delete; + LocalDataSet& operator=(const LocalDataSet&) = delete; +private: + std::vector poolVarList; +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALDATASET_H_ */ diff --git a/datapoollocal/LocalPoolDataSetBase.cpp b/datapoollocal/LocalPoolDataSetBase.cpp new file mode 100644 index 000000000..a72e9db11 --- /dev/null +++ b/datapoollocal/LocalPoolDataSetBase.cpp @@ -0,0 +1,323 @@ +#include "LocalPoolDataSetBase.h" +#include "HasLocalDataPoolIF.h" +#include "internal/HasLocalDpIFUserAttorney.h" + +#include "../serviceinterface/ServiceInterface.h" +#include "../globalfunctions/bitutility.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../housekeeping/PeriodicHousekeepingHelper.h" +#include "../serialize/SerializeAdapter.h" + +#include +#include + +LocalPoolDataSetBase::LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, + uint32_t setId, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, bool periodicHandling): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + if(hkOwner == nullptr) { + // Configuration error. +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " + << "invalid!" << std::endl; +#else + sif::printError("LocalPoolDataSetBase::LocalPoolDataSetBase: Owner " + "invalid!\n\r"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + return; + } + AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner); + + if(accessor != nullptr) { + poolManager = accessor->getPoolManagerHandle(); + mutexIfSingleDataCreator = accessor->getLocalPoolMutex(); + } + + this->sid.objectId = hkOwner->getObjectId(); + this->sid.ownerSetId = setId; + + /* Data creators get a periodic helper for periodic HK data generation. */ + if(periodicHandling) { + periodicHelper = new PeriodicHousekeepingHelper(this); + } +} + +LocalPoolDataSetBase::LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + HasLocalDataPoolIF* hkOwner = objectManager->get( + sid.objectId); + if(hkOwner != nullptr) { + AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner); + if(accessor != nullptr) { + mutexIfSingleDataCreator = accessor->getLocalPoolMutex(); + poolManager = accessor->getPoolManagerHandle(); + } + } + + this->sid = sid; +} + +LocalPoolDataSetBase::LocalPoolDataSetBase(PoolVariableIF **registeredVariablesArray, + const size_t maxNumberOfVariables, bool protectEveryReadCommitCall): + PoolDataSetBase(registeredVariablesArray, maxNumberOfVariables) { + this->setReadCommitProtectionBehaviour(protectEveryReadCommitCall); +} + + +LocalPoolDataSetBase::~LocalPoolDataSetBase() { + /* We only delete objects which were created in the class constructor */ + if(periodicHelper != nullptr) { + delete periodicHelper; + } + /* In case set was read but not comitted, we commit all variables with an invalid state */ + if(state == States::STATE_SET_WAS_READ) { + for (uint16_t count = 0; count < fillCount; count++) { + if(registeredVariables[count] != nullptr) { + registeredVariables[count]->setValid(false); + registeredVariables[count]->commit(MutexIF::TimeoutType::WAITING, 20); + } + } + } +} + +ReturnValue_t LocalPoolDataSetBase::lockDataPool( + MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) { + if(mutexIfSingleDataCreator != nullptr) { + return mutexIfSingleDataCreator->lockMutex(timeoutType, timeoutMs); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalPoolDataSetBase::serializeWithValidityBuffer(uint8_t **buffer, + size_t *size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_OK; + const uint8_t validityMaskSize = std::ceil(static_cast(fillCount)/8.0); + uint8_t* validityPtr = nullptr; +#ifdef _MSC_VER + /* Use a std::vector here because MSVC will (rightly) not create a fixed size array + with a non constant size specifier */ + std::vector validityMask(validityMaskSize); + validityPtr = validityMask.data(); +#else + uint8_t validityMask[validityMaskSize] = {0}; + validityPtr = validityMask; +#endif + uint8_t validBufferIndex = 0; + uint8_t validBufferIndexBit = 0; + for (uint16_t count = 0; count < fillCount; count++) { + if(registeredVariables[count]->isValid()) { + /* Set bit at correct position */ + bitutil::bitSet(validityPtr + validBufferIndex, validBufferIndexBit); + } + if(validBufferIndexBit == 7) { + validBufferIndex ++; + validBufferIndexBit = 0; + } + else { + validBufferIndexBit ++; + } + + result = registeredVariables[count]->serialize(buffer, size, maxSize, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + + if(*size + validityMaskSize > maxSize) { + return SerializeIF::BUFFER_TOO_SHORT; + } + // copy validity buffer to end + std::memcpy(*buffer, validityPtr, validityMaskSize); + *size += validityMaskSize; + return result; +} + +ReturnValue_t LocalPoolDataSetBase::deSerializeWithValidityBuffer( + const uint8_t **buffer, size_t *size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t count = 0; count < fillCount; count++) { + result = registeredVariables[count]->deSerialize(buffer, size, + streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + } + + if(*size < std::ceil(static_cast(fillCount) / 8.0)) { + return SerializeIF::STREAM_TOO_SHORT; + } + + uint8_t validBufferIndex = 0; + uint8_t validBufferIndexBit = 0; + for (uint16_t count = 0; count < fillCount; count++) { + // set validity buffer here. + bool nextVarValid = bitutil::bitGet(*buffer + + validBufferIndex, validBufferIndexBit); + registeredVariables[count]->setValid(nextVarValid); + + if(validBufferIndexBit == 7) { + validBufferIndex ++; + validBufferIndexBit = 0; + } + else { + validBufferIndexBit ++; + } + } + return result; +} + +ReturnValue_t LocalPoolDataSetBase::unlockDataPool() { + if(mutexIfSingleDataCreator != nullptr) { + return mutexIfSingleDataCreator->unlockMutex(); + } + return HasReturnvaluesIF::RETURN_OK; +} + +ReturnValue_t LocalPoolDataSetBase::serializeLocalPoolIds(uint8_t** buffer, + size_t* size, size_t maxSize,SerializeIF::Endianness streamEndianness, + bool serializeFillCount) const { + /* Serialize fill count as uint8_t */ + uint8_t fillCount = this->fillCount; + if(serializeFillCount) { + SerializeAdapter::serialize(&fillCount, buffer, size, maxSize, + streamEndianness); + } + for (uint16_t count = 0; count < fillCount; count++) { + lp_id_t currentPoolId = registeredVariables[count]->getDataPoolId(); + auto result = SerializeAdapter::serialize(¤tPoolId, buffer, + size, maxSize, streamEndianness); + if(result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolDataSetBase::serializeLocalPoolIds: " + << "Serialization error!" << std::endl; +#else + sif::printWarning("LocalPoolDataSetBase::serializeLocalPoolIds: " + "Serialization error!\n\r"); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ + return result; + } + } + return HasReturnvaluesIF::RETURN_OK; +} + + +uint8_t LocalPoolDataSetBase::getLocalPoolIdsSerializedSize( + bool serializeFillCount) const { + if(serializeFillCount) { + return fillCount * sizeof(lp_id_t) + sizeof(uint8_t); + } + else { + return fillCount * sizeof(lp_id_t); + } +} + +size_t LocalPoolDataSetBase::getSerializedSize() const { + if(withValidityBuffer) { + uint8_t validityMaskSize = std::ceil(static_cast(fillCount)/8.0); + return validityMaskSize + PoolDataSetBase::getSerializedSize(); + } + else { + return PoolDataSetBase::getSerializedSize(); + } +} + +void LocalPoolDataSetBase::setValidityBufferGeneration( + bool withValidityBuffer) { + this->withValidityBuffer = withValidityBuffer; +} + +ReturnValue_t LocalPoolDataSetBase::deSerialize(const uint8_t **buffer, + size_t *size, SerializeIF::Endianness streamEndianness) { + if(withValidityBuffer) { + return this->deSerializeWithValidityBuffer(buffer, size, + streamEndianness); + } + else { + return PoolDataSetBase::deSerialize(buffer, size, streamEndianness); + } +} + +ReturnValue_t LocalPoolDataSetBase::serialize(uint8_t **buffer, size_t *size, + size_t maxSize, SerializeIF::Endianness streamEndianness) const { + if(withValidityBuffer) { + return this->serializeWithValidityBuffer(buffer, size, + maxSize, streamEndianness); + } + else { + return PoolDataSetBase::serialize(buffer, size, maxSize, + streamEndianness); + } +} + +void LocalPoolDataSetBase::setDiagnostic(bool isDiagnostics) { + this->diagnostic = isDiagnostics; +} + +bool LocalPoolDataSetBase::isDiagnostics() const { + return diagnostic; +} + +void LocalPoolDataSetBase::setReportingEnabled(bool reportingEnabled) { + this->reportingEnabled = reportingEnabled; +} + +bool LocalPoolDataSetBase::getReportingEnabled() const { + return reportingEnabled; +} + +void LocalPoolDataSetBase::initializePeriodicHelper(float collectionInterval, + dur_millis_t minimumPeriodicInterval, uint8_t nonDiagIntervalFactor) { + periodicHelper->initialize(collectionInterval, minimumPeriodicInterval, nonDiagIntervalFactor); +} + +void LocalPoolDataSetBase::setChanged(bool changed) { + this->changed = changed; +} + +bool LocalPoolDataSetBase::hasChanged() const { + return changed; +} + +sid_t LocalPoolDataSetBase::getSid() const { + return sid; +} + +bool LocalPoolDataSetBase::isValid() const { + return this->valid; +} + +void LocalPoolDataSetBase::setValidity(bool valid, bool setEntriesRecursively) { + if(setEntriesRecursively) { + for(size_t idx = 0; idx < this->getFillCount(); idx++) { + registeredVariables[idx]->setValid(valid); + } + } + this->valid = valid; +} + +object_id_t LocalPoolDataSetBase::getCreatorObjectId() { + if(poolManager != nullptr) { + return poolManager->getCreatorObjectId(); + } + return objects::NO_OBJECT; +} + +void LocalPoolDataSetBase::setAllVariablesReadOnly() { + for(size_t idx = 0; idx < this->getFillCount(); idx++) { + registeredVariables[idx]->setReadWriteMode(pool_rwm_t::VAR_READ); + } +} + +float LocalPoolDataSetBase::getCollectionInterval() const { + if(periodicHelper != nullptr) { + return periodicHelper->getCollectionIntervalInSeconds(); + } + else { + return 0.0; + } +} diff --git a/datapoollocal/LocalPoolDataSetBase.h b/datapoollocal/LocalPoolDataSetBase.h new file mode 100644 index 000000000..ab67dc3f9 --- /dev/null +++ b/datapoollocal/LocalPoolDataSetBase.h @@ -0,0 +1,241 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ + +#include "MarkChangedIF.h" +#include "localPoolDefinitions.h" + +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolDataSetBase.h" + +#include + +class LocalDataPoolManager; +class HasLocalDataPoolIF; +class PeriodicHousekeepingHelper; + +/** + * @brief The LocalDataSet class manages a set of locally checked out + * variables for local data pools + * @details + * Extends the PoolDataSetBase class for local data pools by introducing + * a validity state, a flag to mark the set as changed, and various other + * functions to make it usable by the LocalDataPoolManager class. + * + * This class manages a list, where a set of local variables (or pool variables) + * are registered. They are checked-out (i.e. their values are looked + * up and copied) with the read call. After the user finishes working with the + * pool variables, he can write back all variable values to the pool with + * the commit call. The data set manages locking and freeing the local data + * pools, to ensure thread-safety. + * + * Pool variables can be added to the dataset by using the constructor + * argument of the pool variable or using the #registerVariable member function. + * + * An internal state manages usage of this class. Variables may only be + * registered before any read call is made, and the commit call can only happen + * after the read call. + * + * If pool variables are writable and not committed until destruction + * of the set, the DataSet class automatically sets the valid flag in the + * data pool to invalid (without) changing the variable's value. + * + * @ingroup data_pool + */ +class LocalPoolDataSetBase: + public PoolDataSetBase, + public MarkChangedIF { + friend class LocalPoolDataSetAttorney; + friend class PeriodicHousekeepingHelper; +public: + /** + * @brief Constructor for the creator of local pool data. + * @details + * This constructor also initializes the components required for + * periodic handling. + */ + LocalPoolDataSetBase(HasLocalDataPoolIF *hkOwner, + uint32_t setId, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, bool periodicHandling = true); + + /** + * @brief Constructor for users of the local pool data, which need + * to access data created by one HK manager. + * @details + * Unlike the first constructor, no component for periodic handling + * will be initiated. + * @param sid Unique identifier of dataset consisting of object ID and + * set ID. + * @param registeredVariablesArray + * @param maxNumberOfVariables + */ + LocalPoolDataSetBase(sid_t sid, PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables); + + /** + * @brief Simple constructor, if the dataset is not the owner by + * a class with a HK manager. + * @details + * This constructor won't create components required for periodic handling + * and it also won't try to deduce the HK manager because no SID is + * supplied. This function should therefore be called by classes which need + * to access pool variables from different creators. + * + * If the class is intended to access pool variables from different + * creators, the third argument should be set to true. The mutex + * properties can be set with #setReadCommitProtectionBehaviour . + * @param registeredVariablesArray + * @param maxNumberOfVariables + * @param protectEveryReadCommitCall If the pool variables are created by + * multiple creators, this flag can be set to protect all read and + * commit calls separately. + */ + LocalPoolDataSetBase(PoolVariableIF** registeredVariablesArray, + const size_t maxNumberOfVariables, + bool protectEveryReadCommitCall = true); + + /** + * @brief The destructor automatically manages writing the valid + * information of variables. + * @details + * In case the data set was read out, but not committed (indicated by state), + * the destructor parses all variables that are still registered to the set. + * For each, the valid flag in the data pool is set to "invalid". + */ + ~LocalPoolDataSetBase(); + + /* The copy constructor and assingment constructor are forbidden for now. + The use-cases are limited and the first step would be to implement them properly for the + base class */ + LocalPoolDataSetBase(const LocalPoolDataSetBase& otherSet) = delete; + const LocalPoolDataSetBase& operator=(const LocalPoolDataSetBase& otherSet) = delete; + + /** + * Helper functions used to set all currently contained variables to read-only. + * It is recommended to call this in set constructors intended to be used + * by data consumers to prevent accidentally changing pool data. + */ + void setAllVariablesReadOnly(); + void setValidityBufferGeneration(bool withValidityBuffer); + + sid_t getSid() const; + + /** SerializeIF overrides */ + ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + ReturnValue_t deSerialize(const uint8_t** buffer, size_t *size, + SerializeIF::Endianness streamEndianness) override; + size_t getSerializedSize() const override; + + /** + * Special version of the serilization function which appends a + * validity buffer at the end. Each bit of this validity buffer + * denotes whether the container data set entries are valid from left + * to right, MSB first. (length = ceil(N/8), N = number of pool variables) + * @param buffer + * @param size + * @param maxSize + * @param bigEndian + * @param withValidityBuffer + * @return + */ + ReturnValue_t serializeWithValidityBuffer(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const; + ReturnValue_t deSerializeWithValidityBuffer(const uint8_t** buffer, + size_t *size, SerializeIF::Endianness streamEndianness); + ReturnValue_t serializeLocalPoolIds(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness, + bool serializeFillCount = true) const; + uint8_t getLocalPoolIdsSerializedSize(bool serializeFillCount = true) const; + + /** + * Set the dataset valid or invalid. These calls are mutex protected. + * @param setEntriesRecursively + * If this is true, all contained datasets will also be set recursively. + */ + void setValidity(bool valid, bool setEntriesRecursively); + bool isValid() const override; + + /** + * These calls are mutex protected. + * @param changed + */ + void setChanged(bool changed) override; + bool hasChanged() const override; + + object_id_t getCreatorObjectId(); + + bool getReportingEnabled() const; + + /** + * Returns the current periodic HK generation interval this set + * belongs to a HK manager and the interval is not 0. Otherwise, + * returns 0.0 + * @return + */ + float getCollectionInterval() const; + +protected: + sid_t sid; + //! This mutex is used if the data is created by one object only. + MutexIF* mutexIfSingleDataCreator = nullptr; + + bool diagnostic = false; + void setDiagnostic(bool diagnostics); + bool isDiagnostics() const; + + /** + * Used for periodic generation. + */ + bool reportingEnabled = false; + void setReportingEnabled(bool enabled); + + void initializePeriodicHelper(float collectionInterval, dur_millis_t minimumPeriodicInterval, + uint8_t nonDiagIntervalFactor = 5); + + /** + * If the valid state of a dataset is always relevant to the whole + * data set we can use this flag. + */ + bool valid = false; + + /** + * Can be used to mark the dataset as changed, which is used + * by the LocalDataPoolManager to send out update messages. + */ + bool changed = false; + + /** + * Specify whether the validity buffer is serialized too when serializing + * or deserializing the packet. Each bit of the validity buffer will + * contain the validity state of the pool variables from left to right. + * The size of validity buffer thus will be ceil(N / 8) with N = number of + * pool variables. + */ + bool withValidityBuffer = true; + + /** + * @brief This is a small helper function to facilitate locking + * the global data pool. + * @details + * It makes use of the lockDataPool method offered by the DataPool class. + */ + ReturnValue_t lockDataPool(MutexIF::TimeoutType timeoutType, + uint32_t timeoutMs) override; + + /** + * @brief This is a small helper function to facilitate + * unlocking the global data pool + * @details + * It makes use of the freeDataPoolLock method offered by the DataPool class. + */ + ReturnValue_t unlockDataPool() override; + + PeriodicHousekeepingHelper* periodicHelper = nullptr; + LocalDataPoolManager* poolManager = nullptr; + +}; + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETBASE_H_ */ diff --git a/datapoollocal/LocalPoolObjectBase.cpp b/datapoollocal/LocalPoolObjectBase.cpp new file mode 100644 index 000000000..b6db06089 --- /dev/null +++ b/datapoollocal/LocalPoolObjectBase.cpp @@ -0,0 +1,141 @@ +#include "LocalPoolObjectBase.h" +#include "LocalDataPoolManager.h" +#include "AccessLocalPoolF.h" +#include "HasLocalDataPoolIF.h" +#include "internal/HasLocalDpIFUserAttorney.h" + +#include "../objectmanager/ObjectManagerIF.h" + + +LocalPoolObjectBase::LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, + DataSetIF* dataSet, pool_rwm_t setReadWriteMode): + localPoolId(poolId), readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + << "which is the NO_PARAMETER value!" << std::endl; +#endif + } + if(hkOwner == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "LocalPoolVar::LocalPoolVar: The supplied pool " + << "owner is a invalid!" << std::endl; +#endif + return; + } + AccessPoolManagerIF* poolManAccessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner); + hkManager = poolManAccessor->getPoolManagerHandle(); + + if (dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF *dataSet, + pool_rwm_t setReadWriteMode): + localPoolId(poolId), readWriteMode(setReadWriteMode) { + if(poolId == PoolVariableIF::NO_PARAMETER) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + "which is the NO_PARAMETER value!" << std::endl; +#else + sif::printWarning("LocalPoolVar::LocalPoolVar: 0 passed as pool ID, " + "which is the NO_PARAMETER value!\n"); +#endif + } + HasLocalDataPoolIF* hkOwner = objectManager->get(poolOwner); + if(hkOwner == nullptr) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "LocalPoolVariable: The supplied pool owner did not implement the correct " + "interface HasLocalDataPoolIF!" << std::endl; +#else + sif::printError( "LocalPoolVariable: The supplied pool owner did not implement the correct " + "interface HasLocalDataPoolIF!\n"); +#endif + return; + } + + AccessPoolManagerIF* accessor = HasLocalDpIFUserAttorney::getAccessorHandle(hkOwner); + if(accessor != nullptr) { + hkManager = accessor->getPoolManagerHandle(); + } + + if(dataSet != nullptr) { + dataSet->registerVariable(this); + } +} + +pool_rwm_t LocalPoolObjectBase::getReadWriteMode() const { + return readWriteMode; +} + +bool LocalPoolObjectBase::isValid() const { + return valid; +} + +void LocalPoolObjectBase::setValid(bool valid) { + this->valid = valid; +} + +lp_id_t LocalPoolObjectBase::getDataPoolId() const { + return localPoolId; +} + +void LocalPoolObjectBase::setDataPoolId(lp_id_t poolId) { + this->localPoolId = poolId; +} + +void LocalPoolObjectBase::setChanged(bool changed) { + this->changed = changed; +} + +bool LocalPoolObjectBase::hasChanged() const { + return changed; +} + +void LocalPoolObjectBase::setReadWriteMode(pool_rwm_t newReadWriteMode) { + this->readWriteMode = newReadWriteMode; +} + +void LocalPoolObjectBase::reportReadCommitError(const char* variableType, + ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId) { +#if FSFW_DISABLE_PRINTOUT == 0 + const char* variablePrintout = variableType; + if(variablePrintout == nullptr) { + variablePrintout = "Unknown Type"; + } + const char* type = nullptr; + if(read) { + type = "read"; + } + else { + type = "commit"; + } + + const char* errMsg = nullptr; + if(error == localpool::POOL_ENTRY_NOT_FOUND) { + errMsg = "Pool entry not found"; + } + else if(error == localpool::POOL_ENTRY_TYPE_CONFLICT) { + errMsg = "Pool entry type conflict"; + } + else if(error == PoolVariableIF::INVALID_READ_WRITE_MODE) { + errMsg = "Pool variable wrong read-write mode"; + } + else if(error == PoolVariableIF::INVALID_POOL_ENTRY) { + errMsg = "Pool entry invalid"; + } + else { + errMsg = "Unknown error code"; + } + +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << variablePrintout << ": " << type << " call | " << errMsg << " | Owner: 0x" + << std::hex << std::setw(8) << std::setfill('0') << objectId << std::dec + << " LPID: " << lpId << std::endl; +#else + sif::printWarning("%s: %s call | %s | Owner: 0x%08x LPID: %lu\n", + variablePrintout, type, errMsg, objectId, lpId); +#endif /* FSFW_CPP_OSTREAM_ENABLED == 1 */ +#endif /* FSFW_DISABLE_PRINTOUT == 0 */ +} diff --git a/datapoollocal/LocalPoolObjectBase.h b/datapoollocal/LocalPoolObjectBase.h new file mode 100644 index 000000000..3f7fb6ddf --- /dev/null +++ b/datapoollocal/LocalPoolObjectBase.h @@ -0,0 +1,71 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ + +#include "MarkChangedIF.h" +#include "localPoolDefinitions.h" + +#include "../objectmanager/SystemObjectIF.h" +#include "../datapool/PoolVariableIF.h" +#include "../returnvalues/HasReturnvaluesIF.h" + +class LocalDataPoolManager; +class DataSetIF; +class HasLocalDataPoolIF; + +/** + * @brief This class serves as a non-template base for pool objects like pool variables + * or pool vectors. + */ +class LocalPoolObjectBase: public PoolVariableIF, + public HasReturnvaluesIF, + public MarkChangedIF { +public: + LocalPoolObjectBase(lp_id_t poolId, HasLocalDataPoolIF* hkOwner, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode); + + LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + void setReadWriteMode(pool_rwm_t newReadWriteMode); + pool_rwm_t getReadWriteMode() const; + + bool isValid() const override; + void setValid(bool valid) override; + + void setChanged(bool changed) override; + bool hasChanged() const override; + + lp_id_t getDataPoolId() const override; + void setDataPoolId(lp_id_t poolId); + +protected: + /** + * @brief To access the correct data pool entry on read and commit calls, + * the data pool id is stored. + */ + uint32_t localPoolId = PoolVariableIF::NO_PARAMETER; + /** + * @brief The valid information as it was stored in the data pool + * is copied to this attribute. + */ + bool valid = false; + + /** + * @brief A local pool variable can be marked as changed. + */ + bool changed = false; + + /** + * @brief The information whether the class is read-write or + * read-only is stored here. + */ + ReadWriteMode_t readWriteMode = pool_rwm_t::VAR_READ_WRITE; + + //! @brief Pointer to the class which manages the HK pool. + LocalDataPoolManager* hkManager = nullptr; + + void reportReadCommitError(const char* variableType, + ReturnValue_t error, bool read, object_id_t objectId, lp_id_t lpId); +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLOBJECTBASE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.h b/datapoollocal/LocalPoolVariable.h new file mode 100644 index 000000000..76629489a --- /dev/null +++ b/datapoollocal/LocalPoolVariable.h @@ -0,0 +1,203 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ + +#include "LocalPoolObjectBase.h" +#include "HasLocalDataPoolIF.h" +#include "LocalDataPoolManager.h" +#include "AccessLocalPoolF.h" +#include "internal/LocalDpManagerAttorney.h" + +#include "../datapool/PoolVariableIF.h" +#include "../datapool/DataSetIF.h" +#include "../serviceinterface/ServiceInterface.h" +#include "../objectmanager/ObjectManagerIF.h" +#include "../serialize/SerializeAdapter.h" + +/** + * @brief Local Pool Variable class which is used to access the local pools. + * @details + * This class is not stored in the map. Instead, it is used to access + * the pool entries by using a pointer to the map storing the pool + * entries. It can also be used to organize these pool entries into data sets. + * + * @tparam T The template parameter sets the type of the variable. Currently, + * all plain data types are supported, but in principle any type is possible. + * @ingroup data_pool + */ +template +class LocalPoolVariable: public LocalPoolObjectBase { +public: + //! Default ctor is forbidden. + LocalPoolVariable() = delete; + + /** + * This constructor is used by the data creators to have pool variable + * instances which can also be stored in datasets. + * + * It does not fetch the current value from the data pool, which + * has to be done by calling the read() operation. + * Datasets can be used to access multiple local pool entries in an + * efficient way. A pointer to a dataset can be passed to register + * the pool variable in that dataset directly. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + * @param setReadWriteMode Specify the read-write mode of the pool variable. + */ + LocalPoolVariable(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + /** + * This constructor is used by data users like controllers to have + * access to the local pool variables of data creators by supplying + * the respective creator object ID. + * + * It does not fetch the current value from the data pool, which + * has to be done by calling the read() operation. + * Datasets can be used to access multiple local pool entries in an + * efficient way. A pointer to a dataset can be passed to register + * the pool variable in that dataset directly. + * @param poolId ID of the local pool entry. + * @param hkOwner object ID of the pool owner. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * + */ + LocalPoolVariable(object_id_t poolOwner, lp_id_t poolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + /** + * Variation which takes the global unique identifier of a pool variable. + * @param globalPoolId + * @param dataSet + * @param setReadWriteMode + */ + LocalPoolVariable(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + virtual~ LocalPoolVariable() {}; + + /** + * @brief This is the local copy of the data pool entry. + * @details The user can work on this attribute + * just like he would on a simple local variable. + */ + T value = 0; + + ReturnValue_t serialize(uint8_t** buffer, size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + + /** + * @brief This is a call to read the array's values + * from the global data pool. + * @details + * When executed, this operation tries to fetch the pool entry with matching + * data pool id from the data pool and copies all array values and the valid + * information to its local attributes. + * In case of a failure (wrong type, size or pool id not found), the + * variable is set to zero and invalid. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + * + */ + ReturnValue_t read(MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + /** + * @brief The commit call copies the array values back to the data pool. + * @details + * It checks type and size, as well as if the variable is writable. If so, + * the value is copied and the local valid flag is written back as well. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + + /** + * @brief This commit function can be used to set the pool variable valid + * as well. + * @param setValid + * @param timeoutType + * @param timeoutMs + * @return + */ + ReturnValue_t commit(bool setValid, MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); + + LocalPoolVariable &operator=(const T& newValue); + LocalPoolVariable &operator=(const LocalPoolVariable& newPoolVariable); + + //! Explicit type conversion operator. Allows casting the class to + //! its template type to perform operations on value. + explicit operator T() const; + + bool operator==(const LocalPoolVariable& other) const; + bool operator==(const T& other) const; + + bool operator!=(const LocalPoolVariable& other) const; + bool operator!=(const T& other) const; + + bool operator<(const LocalPoolVariable& other) const; + bool operator<(const T& other) const; + + bool operator>(const LocalPoolVariable& other) const; + bool operator>(const T& other) const; + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + +#if FSFW_CPP_OSTREAM_ENABLED == 1 + // std::ostream is the type for object std::cout + template + friend std::ostream& operator<< (std::ostream &out, + const LocalPoolVariable &var); +#endif +}; + +#include "LocalPoolVariable.tpp" + +template +using lp_var_t = LocalPoolVariable; + +using lp_bool_t = LocalPoolVariable; +using lp_uint8_t = LocalPoolVariable; +using lp_uint16_t = LocalPoolVariable; +using lp_uint32_t = LocalPoolVariable; +using lp_uint64_t = LocalPoolVariable; +using lp_int8_t = LocalPoolVariable; +using lp_int16_t = LocalPoolVariable; +using lp_int32_t = LocalPoolVariable; +using lp_int64_t = LocalPoolVariable; +using lp_float_t = LocalPoolVariable; +using lp_double_t = LocalPoolVariable; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ */ diff --git a/datapoollocal/LocalPoolVariable.tpp b/datapoollocal/LocalPoolVariable.tpp new file mode 100644 index 000000000..9bb30611b --- /dev/null +++ b/datapoollocal/LocalPoolVariable.tpp @@ -0,0 +1,209 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ + +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_H_ +#error Include LocalPoolVariable.h before LocalPoolVariable.tpp! +#endif + +template +inline LocalPoolVariable::LocalPoolVariable(HasLocalDataPoolIF* hkOwner, + lp_id_t poolId, DataSetIF* dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} + +template +inline LocalPoolVariable::LocalPoolVariable(object_id_t poolOwner, + lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + + +template +inline LocalPoolVariable::LocalPoolVariable(gp_id_t globalPoolId, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode){} + + +template +inline ReturnValue_t LocalPoolVariable::read( + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + if(hkManager == nullptr) { + return readWithoutLock(); + } + MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager); + ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = readWithoutLock(); + mutex->unlockMutex(); + return result; +} + +template +inline ReturnValue_t LocalPoolVariable::readWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_WRITE) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", + PoolVariableIF::INVALID_READ_WRITE_MODE, true, targetObjectId, + localPoolId); + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, + &poolEntry); + if(result != RETURN_OK) { + object_id_t ownerObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVariable", result, + false, ownerObjectId, localPoolId); + return result; + } + + this->value = *(poolEntry->getDataPtr()); + this->valid = poolEntry->getValid(); + return RETURN_OK; +} + +template +inline ReturnValue_t LocalPoolVariable::commit(bool setValid, + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + this->setValid(setValid); + return commit(timeoutType, timeoutMs); +} + +template +inline ReturnValue_t LocalPoolVariable::commit( + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + if(hkManager == nullptr) { + return commitWithoutLock(); + } + MutexIF* mutex = LocalDpManagerAttorney::getMutexHandle(*hkManager); + ReturnValue_t result = mutex->lockMutex(timeoutType, timeoutMs); + if(result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + result = commitWithoutLock(); + mutex->unlockMutex(); + return result; +} + +template +inline ReturnValue_t LocalPoolVariable::commitWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_READ) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", + PoolVariableIF::INVALID_READ_WRITE_MODE, false, targetObjectId, + localPoolId); + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, + &poolEntry); + if(result != RETURN_OK) { + object_id_t ownerObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVariable", result, + false, ownerObjectId, localPoolId); + return result; + } + + *(poolEntry->getDataPtr()) = this->value; + poolEntry->setValid(this->valid); + return RETURN_OK; +} + +template +inline ReturnValue_t LocalPoolVariable::serialize(uint8_t** buffer, + size_t* size, const size_t max_size, + SerializeIF::Endianness streamEndianness) const { + return SerializeAdapter::serialize(&value, + buffer, size ,max_size, streamEndianness); +} + +template +inline size_t LocalPoolVariable::getSerializedSize() const { + return SerializeAdapter::getSerializedSize(&value); +} + +template +inline ReturnValue_t LocalPoolVariable::deSerialize(const uint8_t** buffer, + size_t* size, SerializeIF::Endianness streamEndianness) { + return SerializeAdapter::deSerialize(&value, buffer, size, streamEndianness); +} + +#if FSFW_CPP_OSTREAM_ENABLED == 1 +template +inline std::ostream& operator<< (std::ostream &out, + const LocalPoolVariable &var) { + out << var.value; + return out; +} +#endif + +template +inline LocalPoolVariable::operator T() const { + return value; +} + +template +inline LocalPoolVariable & LocalPoolVariable::operator=( + const T& newValue) { + value = newValue; + return *this; +} + +template +inline LocalPoolVariable& LocalPoolVariable::operator =( + const LocalPoolVariable& newPoolVariable) { + value = newPoolVariable.value; + return *this; +} + +template +inline bool LocalPoolVariable::operator ==( + const LocalPoolVariable &other) const { + return this->value == other.value; +} + +template +inline bool LocalPoolVariable::operator ==(const T &other) const { + return this->value == other; +} + + +template +inline bool LocalPoolVariable::operator !=( + const LocalPoolVariable &other) const { + return not (*this == other); +} + +template +inline bool LocalPoolVariable::operator !=(const T &other) const { + return not (*this == other); +} + + +template +inline bool LocalPoolVariable::operator <( + const LocalPoolVariable &other) const { + return this->value < other.value; +} + +template +inline bool LocalPoolVariable::operator <(const T &other) const { + return this->value < other; +} + + +template +inline bool LocalPoolVariable::operator >( + const LocalPoolVariable &other) const { + return not (*this < other); +} + +template +inline bool LocalPoolVariable::operator >(const T &other) const { + return not (*this < other); +} + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVARIABLE_TPP_ */ diff --git a/datapoollocal/LocalPoolVector.h b/datapoollocal/LocalPoolVector.h new file mode 100644 index 000000000..591dd6f38 --- /dev/null +++ b/datapoollocal/LocalPoolVector.h @@ -0,0 +1,188 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ + +#include "LocalPoolObjectBase.h" +#include "internal/LocalDpManagerAttorney.h" + +#include "../datapool/DataSetIF.h" +#include "../datapool/PoolEntry.h" +#include "../datapool/PoolVariableIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" +#include "../serialize/SerializeAdapter.h" +#include "../serviceinterface/ServiceInterface.h" + + +/** + * @brief This is the access class for array-type data pool entries. + * @details + * To ensure safe usage of the data pool, operation is not done directly on the + * data pool entries, but on local copies. This class provides simple type- + * and length-safe access to vector-style data pool entries (i.e. entries with + * length > 1). The class can be instantiated as read-write and read only. + * + * It provides a commit-and-roll-back semantic, which means that no array + * entry in the data pool is changed until the commit call is executed. + * There are two template parameters: + * @tparam T + * This template parameter specifies the data type of an array entry. Currently, + * all plain data types are supported, but in principle any type is possible. + * @tparam vector_size + * This template parameter specifies the vector size of this entry. Using a + * template parameter for this is not perfect, but avoids + * dynamic memory allocation. + * @ingroup data_pool + */ +template +class LocalPoolVector: public LocalPoolObjectBase { +public: + LocalPoolVector() = delete; + /** + * This constructor is used by the data creators to have pool variable + * instances which can also be stored in datasets. + * It does not fetch the current value from the data pool. This is performed + * by the read() operation (which is not thread-safe). + * Datasets can be used to access local pool entires in a thread-safe way. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + */ + LocalPoolVector(HasLocalDataPoolIF* hkOwner, lp_id_t poolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + /** + * This constructor is used by data users like controllers to have + * access to the local pool variables of data creators by supplying + * the respective creator object ID. + * It does not fetch the current value from the data pool. This is performed + * by the read() operation (which is not thread-safe). + * Datasets can be used to access local pool entires in a thread-safe way. + * @param poolId ID of the local pool entry. + * @param hkOwner Pointer of the owner. This will generally be the calling + * class itself which passes "this". + * @param setReadWriteMode Specify the read-write mode of the pool variable. + * @param dataSet The data set in which the variable shall register itself. + * If nullptr, the variable is not registered. + */ + LocalPoolVector(object_id_t poolOwner, lp_id_t poolId, + DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + /** + * Variation which takes the unique global identifier of a local pool + * vector. + * @param globalPoolId + * @param dataSet + * @param setReadWriteMode + */ + LocalPoolVector(gp_id_t globalPoolId, DataSetIF* dataSet = nullptr, + pool_rwm_t setReadWriteMode = pool_rwm_t::VAR_READ_WRITE); + + /** + * @brief This is the local copy of the data pool entry. + * @details + * The user can work on this attribute just like he would on a local + * array of this type. + */ + T value[vectorSize]= {}; + /** + * @brief The classes destructor is empty. + * @details If commit() was not called, the local value is + * discarded and not written back to the data pool. + */ + ~LocalPoolVector() {}; + /** + * @brief The operation returns the number of array entries + * in this variable. + */ + uint8_t getSize() { + return vectorSize; + } + + T& operator [](size_t i); + const T &operator [](size_t i) const; + + virtual ReturnValue_t serialize(uint8_t** buffer, size_t* size, + const size_t maxSize, + SerializeIF::Endianness streamEndiannes) const override; + virtual size_t getSerializedSize() const override; + virtual ReturnValue_t deSerialize(const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) override; + + /** + * @brief This is a call to read the array's values + * from the global data pool. + * @details + * When executed, this operation tries to fetch the pool entry with matching + * data pool id from the data pool and copies all array values and the valid + * information to its local attributes. + * In case of a failure (wrong type, size or pool id not found), the + * variable is set to zero and invalid. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t read(MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + + /** + * @brief The commit call copies the array values back to the data pool. + * @details + * It checks type and size, as well as if the variable is writable. If so, + * the value is copied and the local valid flag is written back as well. + * The read call is protected with a lock. + * It is recommended to use DataSets to read and commit multiple variables + * at once to avoid the overhead of unnecessary lock und unlock operations. + */ + ReturnValue_t commit(MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20) override; + + /** + * @brief This commit call also sets the validity of the pool entry. + * @details + */ + ReturnValue_t commit(bool valid, MutexIF::TimeoutType timeoutType = + MutexIF::TimeoutType::WAITING, + uint32_t timeoutMs = 20); + +protected: + /** + * @brief Like #read, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t readWithoutLock() override; + /** + * @brief Like #commit, but without a lock protection of the global pool. + * @details + * The operation does NOT provide any mutual exclusive protection by itself. + * This can be used if the lock is handled externally to avoid the overhead + * of consecutive lock und unlock operations. + * Declared protected to discourage free public usage. + */ + ReturnValue_t commitWithoutLock() override; + +private: + +#if FSFW_CPP_OSTREAM_ENABLED == 1 + // std::ostream is the type for object std::cout + template + friend std::ostream& operator<< (std::ostream &out, + const LocalPoolVector &var); +#endif + +}; + +#include "LocalPoolVector.tpp" + +template +using lp_vec_t = LocalPoolVector; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ */ diff --git a/datapoollocal/LocalPoolVector.tpp b/datapoollocal/LocalPoolVector.tpp new file mode 100644 index 000000000..044b8fa72 --- /dev/null +++ b/datapoollocal/LocalPoolVector.tpp @@ -0,0 +1,179 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ + +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_H_ +#error Include LocalPoolVector.h before LocalPoolVector.tpp! +#endif + +template +inline LocalPoolVector::LocalPoolVector( + HasLocalDataPoolIF* hkOwner, lp_id_t poolId, DataSetIF* dataSet, + pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolId, hkOwner, dataSet, setReadWriteMode) {} + +template +inline LocalPoolVector::LocalPoolVector(object_id_t poolOwner, + lp_id_t poolId, DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(poolOwner, poolId, dataSet, setReadWriteMode) {} + +template +inline LocalPoolVector::LocalPoolVector(gp_id_t globalPoolId, + DataSetIF *dataSet, pool_rwm_t setReadWriteMode): + LocalPoolObjectBase(globalPoolId.objectId, globalPoolId.localPoolId, + dataSet, setReadWriteMode) {} + +template +inline ReturnValue_t LocalPoolVector::read( + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs); + return readWithoutLock(); +} +template +inline ReturnValue_t LocalPoolVector::readWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_WRITE) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", + PoolVariableIF::INVALID_READ_WRITE_MODE, true, targetObjectId, + localPoolId); + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, + &poolEntry); + memset(this->value, 0, vectorSize * sizeof(T)); + + if(result != RETURN_OK) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", result, true, targetObjectId, + localPoolId); + return result; + } + std::memcpy(this->value, poolEntry->getDataPtr(), poolEntry->getByteSize()); + this->valid = poolEntry->getValid(); + return RETURN_OK; +} + +template +inline ReturnValue_t LocalPoolVector::commit(bool valid, + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + this->setValid(valid); + return commit(timeoutType, timeoutMs); +} + +template +inline ReturnValue_t LocalPoolVector::commit( + MutexIF::TimeoutType timeoutType, uint32_t timeoutMs) { + MutexGuard(LocalDpManagerAttorney::getMutexHandle(*hkManager), timeoutType, timeoutMs); + return commitWithoutLock(); +} + +template +inline ReturnValue_t LocalPoolVector::commitWithoutLock() { + if(readWriteMode == pool_rwm_t::VAR_READ) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", + PoolVariableIF::INVALID_READ_WRITE_MODE, false, targetObjectId, + localPoolId); + return PoolVariableIF::INVALID_READ_WRITE_MODE; + } + PoolEntry* poolEntry = nullptr; + ReturnValue_t result = LocalDpManagerAttorney::fetchPoolEntry(*hkManager, localPoolId, + &poolEntry); + if(result != RETURN_OK) { + object_id_t targetObjectId = hkManager->getCreatorObjectId(); + reportReadCommitError("LocalPoolVector", result, false, targetObjectId, + localPoolId); + return result; + } + std::memcpy(poolEntry->getDataPtr(), this->value, poolEntry->getByteSize()); + poolEntry->setValid(this->valid); + return RETURN_OK; +} + +template +inline T& LocalPoolVector::operator [](size_t i) { + if(i < vectorSize) { + return value[i]; + } + // If this happens, I have to set some value. I consider this + // a configuration error, but I wont exit here. +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolVector: Invalid index. Setting or returning" + " last value!" << std::endl; +#else + sif::printWarning("LocalPoolVector: Invalid index. Setting or returning" + " last value!\n"); +#endif + return value[vectorSize - 1]; +} + +template +inline const T& LocalPoolVector::operator [](size_t i) const { + if(i < vectorSize) { + return value[i]; + } + // If this happens, I have to set some value. I consider this + // a configuration error, but I wont exit here. +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "LocalPoolVector: Invalid index. Setting or returning" + " last value!" << std::endl; +#else + sif::printWarning("LocalPoolVector: Invalid index. Setting or returning" + " last value!\n"); +#endif + return value[vectorSize - 1]; +} + +template +inline ReturnValue_t LocalPoolVector::serialize(uint8_t** buffer, + size_t* size, size_t maxSize, + SerializeIF::Endianness streamEndianness) const { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t i = 0; i < vectorSize; i++) { + result = SerializeAdapter::serialize(&(value[i]), buffer, size, + maxSize, streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + break; + } + } + return result; +} + +template +inline size_t LocalPoolVector::getSerializedSize() const { + return vectorSize * SerializeAdapter::getSerializedSize(value); +} + +template +inline ReturnValue_t LocalPoolVector::deSerialize( + const uint8_t** buffer, size_t* size, + SerializeIF::Endianness streamEndianness) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + for (uint16_t i = 0; i < vectorSize; i++) { + result = SerializeAdapter::deSerialize(&(value[i]), buffer, size, + streamEndianness); + if (result != HasReturnvaluesIF::RETURN_OK) { + break; + } + } + return result; +} + +#if FSFW_CPP_OSTREAM_ENABLED == 1 +template +inline std::ostream& operator<< (std::ostream &out, + const LocalPoolVector &var) { + out << "Vector: ["; + for(int i = 0;i < vectorSize; i++) { + out << var.value[i]; + if(i < vectorSize - 1) { + out << ", "; + } + } + out << "]"; + return out; +} +#endif + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLVECTOR_TPP_ */ diff --git a/datapoollocal/MarkChangedIF.h b/datapoollocal/MarkChangedIF.h new file mode 100644 index 000000000..e575d1d3e --- /dev/null +++ b/datapoollocal/MarkChangedIF.h @@ -0,0 +1,17 @@ +#ifndef FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ +#define FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ + +/** + * Common interface for local pool entities which can be marked as changed. + */ +class MarkChangedIF { +public: + virtual~ MarkChangedIF() {}; + + virtual bool hasChanged() const = 0; + virtual void setChanged(bool changed) = 0; +}; + + + +#endif /* FSFW_DATAPOOLLOCAL_MARKCHANGEDIF_H_ */ diff --git a/datapoollocal/ProvidesDataPoolSubscriptionIF.h b/datapoollocal/ProvidesDataPoolSubscriptionIF.h new file mode 100644 index 000000000..1c34099ac --- /dev/null +++ b/datapoollocal/ProvidesDataPoolSubscriptionIF.h @@ -0,0 +1,70 @@ +#ifndef FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ +#define FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ + +#include "localPoolDefinitions.h" +#include "../ipc/messageQueueDefinitions.h" +#include "../returnvalues/HasReturnvaluesIF.h" + + +class ProvidesDataPoolSubscriptionIF { +public: + virtual ~ProvidesDataPoolSubscriptionIF(){}; + + /** + * @brief Subscribe for the generation of periodic packets. + * @details + * This subscription mechanism will generally be used by the data creator + * to generate housekeeping packets which are downlinked directly. + * @return + */ + virtual ReturnValue_t subscribeForPeriodicPacket(sid_t sid, bool enableReporting, + float collectionInterval, bool isDiagnostics, object_id_t packetDestination) = 0; + /** + * @brief Subscribe for the generation of packets if the dataset + * is marked as changed. + * @details + * This subscription mechanism will generally be used by the data creator. + * @param sid + * @param isDiagnostics + * @param packetDestination + * @return + */ + virtual ReturnValue_t subscribeForUpdatePacket(sid_t sid, bool reportingEnabled, + bool isDiagnostics, object_id_t packetDestination) = 0; + /** + * @brief Subscribe for a notification message which will be sent + * if a dataset has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param setId Set ID of the set to receive update messages from. + * @param destinationObject Object ID of the receiver. + * @param targetQueueId Receiver queue ID + * @param generateSnapshot If this is set to true, a copy of the current data with a + * timestamp will be generated and sent via message. + * Otherwise, only an notification message is sent. + * @return + */ + virtual ReturnValue_t subscribeForSetUpdateMessage(const uint32_t setId, + object_id_t destinationObject, MessageQueueId_t targetQueueId, + bool generateSnapshot) = 0; + /** + * @brief Subscribe for an notification message which will be sent if a + * pool variable has changed. + * @details + * This subscription mechanism will generally be used internally by + * other software components. + * @param localPoolId Pool ID of the pool variable + * @param destinationObject Object ID of the receiver + * @param targetQueueId Receiver queue ID + * @param generateSnapshot If this is set to true, a copy of the current data with a + * timestamp will be generated and sent via message. Otherwise, + * only an notification message is sent. + * @return + */ + virtual ReturnValue_t subscribeForVariableUpdateMessage(const lp_id_t localPoolId, + object_id_t destinationObject, MessageQueueId_t targetQueueId, + bool generateSnapshot) = 0; +}; + +#endif /* FSFW_DATAPOOLLOCAL_PROVIDESDATAPOOLSUBSCRIPTION_H_ */ diff --git a/datapoollocal/SharedLocalDataSet.cpp b/datapoollocal/SharedLocalDataSet.cpp new file mode 100644 index 000000000..84c2d1c37 --- /dev/null +++ b/datapoollocal/SharedLocalDataSet.cpp @@ -0,0 +1,37 @@ +#include "SharedLocalDataSet.h" + + +SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, sid_t sid, + const size_t maxSize): SystemObject(objectId), + LocalPoolDataSetBase(sid, nullptr, maxSize), poolVarVector(maxSize) { + this->setContainer(poolVarVector.data()); + datasetLock = MutexFactory::instance()->createMutex(); +} + +SharedLocalDataSet::SharedLocalDataSet(object_id_t objectId, + HasLocalDataPoolIF *owner, uint32_t setId, + const size_t maxSize): SystemObject(objectId), + LocalPoolDataSetBase(owner, setId, nullptr, maxSize), poolVarVector(maxSize) { + this->setContainer(poolVarVector.data()); + datasetLock = MutexFactory::instance()->createMutex(); +} + +ReturnValue_t SharedLocalDataSet::lockDataset(MutexIF::TimeoutType timeoutType, + dur_millis_t mutexTimeout) { + if(datasetLock != nullptr) { + return datasetLock->lockMutex(timeoutType, mutexTimeout); + } + return HasReturnvaluesIF::RETURN_FAILED; +} + + +SharedLocalDataSet::~SharedLocalDataSet() { + MutexFactory::instance()->deleteMutex(datasetLock); +} + +ReturnValue_t SharedLocalDataSet::unlockDataset() { + if(datasetLock != nullptr) { + return datasetLock->unlockMutex(); + } + return HasReturnvaluesIF::RETURN_FAILED; +} diff --git a/datapoollocal/SharedLocalDataSet.h b/datapoollocal/SharedLocalDataSet.h new file mode 100644 index 000000000..8d12610a8 --- /dev/null +++ b/datapoollocal/SharedLocalDataSet.h @@ -0,0 +1,38 @@ +#ifndef FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include "../datapool/SharedDataSetIF.h" +#include "../objectmanager/SystemObject.h" +#include + +/** + * This local dataset variation can be used if the dataset is used concurrently across + * multiple threads. It provides a lock in addition to all other functionalities provided + * by the LocalPoolDataSetBase class. + * + * The user is completely responsible for lockingand unlocking the dataset when using the + * shared dataset. + */ +class SharedLocalDataSet: + public SystemObject, + public LocalPoolDataSetBase, + public SharedDataSetIF { +public: + SharedLocalDataSet(object_id_t objectId, HasLocalDataPoolIF* owner, uint32_t setId, + const size_t maxSize); + SharedLocalDataSet(object_id_t objectId, sid_t sid, const size_t maxSize); + + virtual~ SharedLocalDataSet(); + + ReturnValue_t lockDataset(MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING, + dur_millis_t mutexTimeout = 20) override; + ReturnValue_t unlockDataset() override; +private: + + MutexIF* datasetLock = nullptr; + std::vector poolVarVector; +}; + + +#endif /* FSFW_DATAPOOLLOCAL_SHAREDLOCALDATASET_H_ */ diff --git a/datapoollocal/StaticLocalDataSet.h b/datapoollocal/StaticLocalDataSet.h new file mode 100644 index 000000000..08ef6de6a --- /dev/null +++ b/datapoollocal/StaticLocalDataSet.h @@ -0,0 +1,52 @@ +#ifndef FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ +#define FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ + +#include "LocalPoolDataSetBase.h" +#include "LocalPoolVariable.h" +#include "LocalPoolVector.h" + +#include "../objectmanager/SystemObjectIF.h" +#include + +/** + * @brief This dataset type can be used to group related pool variables if the number of + * variables is fixed. + * @details + * This will is the primary data structure to organize pool variables into + * sets which can be accessed via the housekeeping service interface or + * which can be sent to other software objects. + * + * It is recommended to read the documentation of the LocalPoolDataSetBase + * class for more information on how this class works and how to use it. + * @tparam capacity Capacity of the static dataset, which is usually known + * beforehand. + */ +template +class StaticLocalDataSet: public LocalPoolDataSetBase { +public: + /** + * Constructor used by data owner and creator like device handlers. + * This constructor also initialized the components required for + * periodic handling. + * @param hkOwner + * @param setId + */ + StaticLocalDataSet(HasLocalDataPoolIF* hkOwner, uint32_t setId): + LocalPoolDataSetBase(hkOwner, setId, nullptr, NUM_VARIABLES) { + this->setContainer(poolVarList.data()); + } + + /** + * Constructor used by data users like controllers. + * @param hkOwner + * @param setId + */ + StaticLocalDataSet(sid_t sid): LocalPoolDataSetBase(sid, nullptr, NUM_VARIABLES) { + this->setContainer(poolVarList.data()); + } + +private: + std::array poolVarList; +}; + +#endif /* FSFW_DATAPOOLLOCAL_STATICLOCALDATASET_H_ */ diff --git a/datapoollocal/datapoollocal.h b/datapoollocal/datapoollocal.h new file mode 100644 index 000000000..c5c470788 --- /dev/null +++ b/datapoollocal/datapoollocal.h @@ -0,0 +1,12 @@ +#ifndef FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ +#define FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ + +/* Collected related headers */ +#include "LocalPoolVariable.h" +#include "LocalPoolVector.h" +#include "StaticLocalDataSet.h" +#include "LocalDataSet.h" +#include "SharedLocalDataSet.h" + + +#endif /* FSFW_DATAPOOLLOCAL_DATAPOOLLOCAL_H_ */ diff --git a/datapoollocal/internal/CMakeLists.txt b/datapoollocal/internal/CMakeLists.txt new file mode 100644 index 000000000..554f3b884 --- /dev/null +++ b/datapoollocal/internal/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + HasLocalDpIFUserAttorney.cpp + HasLocalDpIFManagerAttorney.cpp +) diff --git a/datapoollocal/internal/HasLocalDpIFManagerAttorney.cpp b/datapoollocal/internal/HasLocalDpIFManagerAttorney.cpp new file mode 100644 index 000000000..65feda75e --- /dev/null +++ b/datapoollocal/internal/HasLocalDpIFManagerAttorney.cpp @@ -0,0 +1,18 @@ +#include "HasLocalDpIFManagerAttorney.h" +#include "../LocalPoolObjectBase.h" +#include "../LocalPoolDataSetBase.h" +#include "../HasLocalDataPoolIF.h" + +LocalPoolDataSetBase* HasLocalDpIFManagerAttorney::getDataSetHandle(HasLocalDataPoolIF* clientIF, + sid_t sid) { + return clientIF->getDataSetHandle(sid); +} + +LocalPoolObjectBase* HasLocalDpIFManagerAttorney::getPoolObjectHandle(HasLocalDataPoolIF* clientIF, + lp_id_t localPoolId) { + return clientIF->getPoolObjectHandle(localPoolId); +} + +object_id_t HasLocalDpIFManagerAttorney::getObjectId(HasLocalDataPoolIF* clientIF) { + return clientIF->getObjectId(); +} diff --git a/datapoollocal/internal/HasLocalDpIFManagerAttorney.h b/datapoollocal/internal/HasLocalDpIFManagerAttorney.h new file mode 100644 index 000000000..dfe333a64 --- /dev/null +++ b/datapoollocal/internal/HasLocalDpIFManagerAttorney.h @@ -0,0 +1,22 @@ +#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ +#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ + +#include "../localPoolDefinitions.h" + +class HasLocalDataPoolIF; +class LocalPoolDataSetBase; +class LocalPoolObjectBase; + +class HasLocalDpIFManagerAttorney { + + static LocalPoolDataSetBase* getDataSetHandle(HasLocalDataPoolIF* clientIF, sid_t sid); + + static LocalPoolObjectBase* getPoolObjectHandle(HasLocalDataPoolIF* clientIF, + lp_id_t localPoolId); + + static object_id_t getObjectId(HasLocalDataPoolIF* clientIF); + + friend class LocalDataPoolManager; +}; + +#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFMANAGERATTORNEY_H_ */ diff --git a/datapoollocal/internal/HasLocalDpIFUserAttorney.cpp b/datapoollocal/internal/HasLocalDpIFUserAttorney.cpp new file mode 100644 index 000000000..838204a76 --- /dev/null +++ b/datapoollocal/internal/HasLocalDpIFUserAttorney.cpp @@ -0,0 +1,7 @@ +#include "HasLocalDpIFUserAttorney.h" +#include "../AccessLocalPoolF.h" +#include "../HasLocalDataPoolIF.h" + +AccessPoolManagerIF* HasLocalDpIFUserAttorney::getAccessorHandle(HasLocalDataPoolIF *clientIF) { + return clientIF->getAccessorHandle(); +} diff --git a/datapoollocal/internal/HasLocalDpIFUserAttorney.h b/datapoollocal/internal/HasLocalDpIFUserAttorney.h new file mode 100644 index 000000000..77eaa8bde --- /dev/null +++ b/datapoollocal/internal/HasLocalDpIFUserAttorney.h @@ -0,0 +1,18 @@ +#ifndef FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_ +#define FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_ + +class HasLocalDataPoolIF; +class AccessPoolManagerIF; + +class HasLocalDpIFUserAttorney { +private: + + static AccessPoolManagerIF* getAccessorHandle(HasLocalDataPoolIF* clientIF); + + friend class LocalPoolObjectBase; + friend class LocalPoolDataSetBase; + +}; + + +#endif /* FSFW_DATAPOOLLOCAL_HASLOCALDPIFUSERATTORNEY_H_ */ diff --git a/datapoollocal/internal/LocalDpManagerAttorney.h b/datapoollocal/internal/LocalDpManagerAttorney.h new file mode 100644 index 000000000..994f0613b --- /dev/null +++ b/datapoollocal/internal/LocalDpManagerAttorney.h @@ -0,0 +1,31 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ +#define FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ + +#include "../LocalDataPoolManager.h" + +/** + * @brief This is a helper class implements the Attorney-Client idiom for access to + * LocalDataPoolManager internals + * @details + * This helper class provides better control over which classes are allowed to access + * LocalDataPoolManager internals in a granular and encapsulated way when compared to + * other methods like direct friend declarations. It allows these classes to use + * an explicit subset of the pool manager private/protected functions. + * See: https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Friendship_and_the_Attorney-Client + */ +class LocalDpManagerAttorney { +private: + template static ReturnValue_t fetchPoolEntry(LocalDataPoolManager& manager, + lp_id_t localPoolId, PoolEntry **poolEntry) { + return manager.fetchPoolEntry(localPoolId, poolEntry); + } + + static MutexIF* getMutexHandle(LocalDataPoolManager& manager) { + return manager.getMutexHandle(); + } + + template friend class LocalPoolVariable; + template friend class LocalPoolVector; +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALDPMANAGERATTORNEY_H_ */ diff --git a/datapoollocal/internal/LocalPoolDataSetAttorney.h b/datapoollocal/internal/LocalPoolDataSetAttorney.h new file mode 100644 index 000000000..f81428cd1 --- /dev/null +++ b/datapoollocal/internal/LocalPoolDataSetAttorney.h @@ -0,0 +1,38 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ + +#include "../LocalPoolDataSetBase.h" + +class LocalPoolDataSetAttorney { +private: + static void setDiagnostic(LocalPoolDataSetBase& set, bool diagnostics) { + set.setDiagnostic(diagnostics); + } + + static bool isDiagnostics(LocalPoolDataSetBase& set) { + return set.isDiagnostics(); + } + + static void initializePeriodicHelper(LocalPoolDataSetBase& set, float collectionInterval, + uint32_t minimumPeriodicIntervalMs, uint8_t nonDiagIntervalFactor = 5) { + set.initializePeriodicHelper(collectionInterval, minimumPeriodicIntervalMs, + nonDiagIntervalFactor); + } + + static void setReportingEnabled(LocalPoolDataSetBase& set, bool enabled) { + set.setReportingEnabled(enabled); + } + + static bool getReportingEnabled(LocalPoolDataSetBase& set) { + return set.getReportingEnabled(); + } + + static PeriodicHousekeepingHelper* getPeriodicHelper(LocalPoolDataSetBase& set) { + return set.periodicHelper; + } + + friend class LocalDataPoolManager; +}; + + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDATASETATTORNEY_H_ */ diff --git a/datapoollocal/localPoolDefinitions.h b/datapoollocal/localPoolDefinitions.h new file mode 100644 index 000000000..daab8b9c0 --- /dev/null +++ b/datapoollocal/localPoolDefinitions.h @@ -0,0 +1,108 @@ +#ifndef FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ +#define FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ + +#include "../datapool/PoolEntryIF.h" +#include "../objectmanager/SystemObjectIF.h" +#include "../objectmanager/frameworkObjects.h" + +#include +#include + +/** + * @brief Type definition for local pool entries. + */ +using lp_id_t = uint32_t; + +namespace localpool { +static constexpr uint32_t INVALID_LPID = -1; + +static constexpr uint8_t INTERFACE_ID = CLASS_ID::LOCAL_POOL_OWNER_IF; + +static constexpr ReturnValue_t POOL_ENTRY_NOT_FOUND = MAKE_RETURN_CODE(0x00); +static constexpr ReturnValue_t POOL_ENTRY_TYPE_CONFLICT = MAKE_RETURN_CODE(0x01); + +/** This is the core data structure of the local data pools. Users should insert all desired +pool variables, using the std::map interface. */ +using DataPool = std::map; +using DataPoolMapIter = DataPool::iterator; + +} + +/** + * Used as a unique identifier for data sets. Consists of 4 byte object ID and 4 byte set ID. + */ +union sid_t { + static constexpr uint64_t INVALID_SID = -1; + static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + static constexpr uint32_t INVALID_SET_ID = -1; + + + sid_t(): raw(INVALID_SID) {} + + sid_t(object_id_t objectId, uint32_t setId): + objectId(objectId), + ownerSetId(setId) {} + + struct { + object_id_t objectId ; + /** + * A generic 32 bit ID to identify unique HK packets for a single + * object. For example, the DeviceCommandId_t is used for + * DeviceHandlers + */ + uint32_t ownerSetId; + }; + /** + * Alternative access to the raw value. This is also the size of the type. + */ + uint64_t raw; + + bool notSet() const { + return raw == INVALID_SID; + } + + bool operator==(const sid_t& other) const { + return raw == other.raw; + } + + bool operator!=(const sid_t& other) const { + return not (raw == other.raw); + } +}; + +/** + * Used as a global unique identifier for local pool variables. Consists of 4 byte object ID + * and 4 byte local pool ID. + */ +union gp_id_t { + static constexpr uint64_t INVALID_GPID = -1; + static constexpr uint32_t INVALID_OBJECT_ID = objects::NO_OBJECT; + static constexpr uint32_t INVALID_LPID = localpool::INVALID_LPID; + + gp_id_t(): raw(INVALID_GPID) {} + + gp_id_t(object_id_t objectId, lp_id_t localPoolId): + objectId(objectId), + localPoolId(localPoolId) {} + + struct { + object_id_t objectId; + lp_id_t localPoolId; + }; + + uint64_t raw; + + bool notSet() const { + return raw == INVALID_GPID; + } + + bool operator==(const gp_id_t& other) const { + return raw == other.raw; + } + + bool operator!=(const gp_id_t& other) const { + return not (raw == other.raw); + } +}; + +#endif /* FSFW_DATAPOOLLOCAL_LOCALPOOLDEFINITIONS_H_ */ diff --git a/defaultcfg/fsfwconfig/CMakeLists.txt b/defaultcfg/fsfwconfig/CMakeLists.txt new file mode 100644 index 000000000..cbd4ecdec --- /dev/null +++ b/defaultcfg/fsfwconfig/CMakeLists.txt @@ -0,0 +1,15 @@ +target_sources(${LIB_FSFW_NAME} PRIVATE + ipc/missionMessageTypes.cpp + objects/FsfwFactory.cpp + pollingsequence/PollingSequenceFactory.cpp +) + +# Should be added to include path +target_include_directories(${TARGET_NAME} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if(NOT FSFW_CONFIG_PATH) + set(FSFW_CONFIG_PATH ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + diff --git a/defaultcfg/fsfwconfig/FSFWConfig.h b/defaultcfg/fsfwconfig/FSFWConfig.h index be3717b40..fe18a2f43 100644 --- a/defaultcfg/fsfwconfig/FSFWConfig.h +++ b/defaultcfg/fsfwconfig/FSFWConfig.h @@ -1,26 +1,24 @@ #ifndef CONFIG_FSFWCONFIG_H_ #define CONFIG_FSFWCONFIG_H_ -#include #include #include -//! Used to determine whether C++ ostreams are used -//! Those can lead to code bloat. +//! Used to determine whether C++ ostreams are used which can increase +//! the binary size significantly. If this is disabled, +//! the C stdio functions can be used alternatively #define FSFW_CPP_OSTREAM_ENABLED 1 -//! Reduced printout to further decrese code size -//! Be careful, this also turns off most diagnostic prinouts! -#define FSFW_REDUCED_PRINTOUT 0 +//! More FSFW related printouts depending on level. Useful for development. +#define FSFW_VERBOSE_LEVEL 1 -//! Can be used to enable debugging printouts for developing the FSFW -#define FSFW_DEBUGGING 0 +//! Can be used to completely disable printouts, even the C stdio ones. +#if FSFW_CPP_OSTREAM_ENABLED == 0 && FSFW_VERBOSE_LEVEL == 0 + #define FSFW_DISABLE_PRINTOUT 0 +#endif -//! Defines the FIFO depth of each commanding service base which -//! also determines how many commands a CSB service can handle in one cycle -//! simulataneously. This will increase the required RAM for -//! each CSB service ! -#define FSFW_CSB_FIFO_DEPTH 6 +//! Can be used to disable the ANSI color sequences for C stdio. +#define FSFW_COLORED_OUTPUT 1 //! If FSFW_OBJ_EVENT_TRANSLATION is set to one, //! additional output which requires the translation files translateObjects @@ -28,18 +26,26 @@ #define FSFW_OBJ_EVENT_TRANSLATION 0 #if FSFW_OBJ_EVENT_TRANSLATION == 1 -#define FSFW_DEBUG_OUTPUT 1 //! Specify whether info events are printed too. #define FSFW_DEBUG_INFO 1 -#include -#include +#include "objects/translateObjects.h" +#include "events/translateEvents.h" #else -#define FSFW_DEBUG_OUTPUT 0 #endif //! When using the newlib nano library, C99 support for stdio facilities //! will not be provided. This define should be set to 1 if this is the case. -#define FSFW_NO_C99_IO 1 +#define FSFW_NO_C99_IO 1 + +//! Specify whether a special mode store is used for Subsystem components. +#define FSFW_USE_MODESTORE 0 + +//! Defines if the real time scheduler for linux should be used. +//! If set to 0, this will also disable priority settings for linux +//! as most systems will not allow to set nice values without privileges +//! For embedded linux system set this to 1. +//! If set to 1 the binary needs "cap_sys_nice=eip" privileges to run +#define FSFW_USE_REALTIME_FOR_LINUX 1 namespace fsfwconfig { //! Default timestamp size. The default timestamp will be an eight byte CDC @@ -50,6 +56,15 @@ static constexpr uint8_t FSFW_MISSION_TIMESTAMP_SIZE = 8; static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; + +//! Defines the FIFO depth of each commanding service base which +//! also determines how many commands a CSB service can handle in one cycle +//! simultaneously. This will increase the required RAM for +//! each CSB service ! +static constexpr uint8_t FSFW_CSB_FIFO_DEPTH = 6; + +static constexpr size_t FSFW_PRINT_BUFFER_SIZE = 124; + } #endif /* CONFIG_FSFWCONFIG_H_ */ diff --git a/defaultcfg/fsfwconfig/OBSWConfig.h b/defaultcfg/fsfwconfig/OBSWConfig.h index a9f576386..6ed8ea2c4 100644 --- a/defaultcfg/fsfwconfig/OBSWConfig.h +++ b/defaultcfg/fsfwconfig/OBSWConfig.h @@ -4,6 +4,11 @@ #include "OBSWVersion.h" #ifdef __cplusplus + +#include "objects/systemObjectList.h" +#include "events/subsystemIdRanges.h" +#include "returnvalues/classIds.h" + namespace config { #endif diff --git a/defaultcfg/fsfwconfig/devices/logicalAddresses.h b/defaultcfg/fsfwconfig/devices/logicalAddresses.h index e0827ba33..53edcd544 100644 --- a/defaultcfg/fsfwconfig/devices/logicalAddresses.h +++ b/defaultcfg/fsfwconfig/devices/logicalAddresses.h @@ -2,7 +2,7 @@ #define CONFIG_DEVICES_LOGICALADDRESSES_H_ #include -#include "../objects/systemObjectList.h" +#include #include /** diff --git a/defaultcfg/fsfwconfig/objects/Factory.cpp b/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp similarity index 60% rename from defaultcfg/fsfwconfig/objects/Factory.cpp rename to defaultcfg/fsfwconfig/objects/FsfwFactory.cpp index 41333b1c3..428adf1d7 100644 --- a/defaultcfg/fsfwconfig/objects/Factory.cpp +++ b/defaultcfg/fsfwconfig/objects/FsfwFactory.cpp @@ -1,9 +1,5 @@ -#include "Factory.h" -#include "../tmtc/apid.h" -#include "../tmtc/pusIds.h" -#include "../objects/systemObjectList.h" -#include "../devices/logicalAddresses.h" -#include "../devices/powerSwitcherList.h" +#include "FsfwFactory.h" +#include #include #include @@ -11,24 +7,27 @@ #include #include #include -#include +#include #include /** - * This class should be used to create all system objects required for - * the on-board software, using the object ID list from the configuration - * folder. + * This function builds all system objects required for using + * the FSFW. It is recommended to build all other required objects + * in a function with an identical prototype, call this function in it and + * then pass the function to the object manager so it builds all system + * objects on software startup. * - * The objects are registered in the internal object manager automatically. - * This is used later to add objects to tasks. + * All system objects are registered in the internal object manager + * automatically. The objects should be added to tasks at a later stage, using + * their objects IDs. * - * This file also sets static framework IDs. + * This function also sets static framework IDs. * - * Framework objects are created first. + * Framework should be created first before creating mission system objects. * @ingroup init */ -void Factory::produce(void) { +void Factory::produceFsfwObjects(void) { setStaticFrameworkObjectIds(); new EventManager(objects::EVENT_MANAGER); new HealthTable(objects::HEALTH_TABLE); diff --git a/defaultcfg/fsfwconfig/objects/Factory.h b/defaultcfg/fsfwconfig/objects/FsfwFactory.h similarity index 61% rename from defaultcfg/fsfwconfig/objects/Factory.h rename to defaultcfg/fsfwconfig/objects/FsfwFactory.h index fe55defff..198b22f28 100644 --- a/defaultcfg/fsfwconfig/objects/Factory.h +++ b/defaultcfg/fsfwconfig/objects/FsfwFactory.h @@ -1,5 +1,5 @@ -#ifndef FACTORY_H_ -#define FACTORY_H_ +#ifndef FSFWCONFIG_OBJECTS_FACTORY_H_ +#define FSFWCONFIG_OBJECTS_FACTORY_H_ #include #include @@ -9,9 +9,9 @@ namespace Factory { * @brief Creates all SystemObject elements which are persistent * during execution. */ - void produce(); + void produceFsfwObjects(); void setStaticFrameworkObjectIds(); } -#endif /* FACTORY_H_ */ +#endif /* FSFWCONFIG_OBJECTS_FACTORY_H_ */ diff --git a/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp index f836a7462..b7f1fb3e1 100644 --- a/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp +++ b/defaultcfg/fsfwconfig/pollingsequence/PollingSequenceFactory.cpp @@ -15,8 +15,10 @@ ReturnValue_t pst::pollingSequenceInitDefault( return HasReturnvaluesIF::RETURN_OK; } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "pst::pollingSequenceInitDefault: Sequence invalid!" << std::endl; +#endif return HasReturnvaluesIF::RETURN_FAILED; } } diff --git a/devicehandlers/AcceptsDeviceResponsesIF.h b/devicehandlers/AcceptsDeviceResponsesIF.h index 8dc69de4f..8e13ba0c0 100644 --- a/devicehandlers/AcceptsDeviceResponsesIF.h +++ b/devicehandlers/AcceptsDeviceResponsesIF.h @@ -1,23 +1,19 @@ -/** - * @file AcceptsDeviceResponsesIF.h - * @brief This file defines the AcceptsDeviceResponsesIF class. - * @date 15.05.2013 - * @author baetz - */ - -#ifndef ACCEPTSDEVICERESPONSESIF_H_ -#define ACCEPTSDEVICERESPONSESIF_H_ +#ifndef FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ +#define FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ #include "../ipc/MessageQueueSenderIF.h" +/** + * This interface is used by the device handler to send a device response + * to the queue ID, which is returned in the implemented abstract method. + */ class AcceptsDeviceResponsesIF { public: /** * Default empty virtual destructor. */ - virtual ~AcceptsDeviceResponsesIF() { -} -virtual MessageQueueId_t getDeviceQueue() = 0; + virtual ~AcceptsDeviceResponsesIF() {} + virtual MessageQueueId_t getDeviceQueue() = 0; }; -#endif /* ACCEPTSDEVICERESPONSESIF_H_ */ +#endif /* FSFW_DEVICEHANDLERS_ACCEPTSDEVICERESPONSESIF_H_ */ diff --git a/devicehandlers/AssemblyBase.cpp b/devicehandlers/AssemblyBase.cpp index 8b3f7f4dc..46b2211ae 100644 --- a/devicehandlers/AssemblyBase.cpp +++ b/devicehandlers/AssemblyBase.cpp @@ -2,10 +2,10 @@ AssemblyBase::AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth) : - SubsystemBase(objectId, parentId, MODE_OFF, commandQueueDepth), internalState( - STATE_NONE), recoveryState(RECOVERY_IDLE), recoveringDevice( - childrenMap.end()), targetMode(MODE_OFF), targetSubmode( - SUBMODE_NONE) { + SubsystemBase(objectId, parentId, MODE_OFF, commandQueueDepth), + internalState(STATE_NONE), recoveryState(RECOVERY_IDLE), + recoveringDevice(childrenMap.end()), targetMode(MODE_OFF), + targetSubmode(SUBMODE_NONE) { recoveryOffTimer.setTimeout(POWER_OFF_TIME_MS); } @@ -165,9 +165,8 @@ ReturnValue_t AssemblyBase::checkChildrenState() { } ReturnValue_t AssemblyBase::checkChildrenStateOff() { - for (std::map::iterator iter = childrenMap.begin(); - iter != childrenMap.end(); iter++) { - if (checkChildOff(iter->first) != RETURN_OK) { + for (const auto& childIter: childrenMap) { + if (checkChildOff(childIter.first) != RETURN_OK) { return NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE; } } diff --git a/devicehandlers/AssemblyBase.h b/devicehandlers/AssemblyBase.h index bd2e8fada..6cac81b49 100644 --- a/devicehandlers/AssemblyBase.h +++ b/devicehandlers/AssemblyBase.h @@ -1,10 +1,37 @@ -#ifndef ASSEMBLYBASE_H_ -#define ASSEMBLYBASE_H_ +#ifndef FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ +#define FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ -#include "../container/FixedArrayList.h" #include "DeviceHandlerBase.h" +#include "../container/FixedArrayList.h" #include "../subsystem/SubsystemBase.h" +/** + * @brief Base class to implement reconfiguration and failure handling for + * redundant devices by monitoring their modes health states. + * @details + * Documentation: Dissertation Baetz p.156, 157. + * + * This class reduces the complexity of controller components which would + * otherwise be needed for the handling of redundant devices. + * + * The template class monitors mode and health state of its children + * and checks availability of devices on every detected change. + * AssemblyBase does not implement any redundancy logic by itself, but provides + * adaptation points for implementations to do so. Since most monitoring + * activities rely on mode and health state only and are therefore + * generic, it is sufficient for subclasses to provide: + * + * 1. check logic when active-> checkChildrenStateOn + * 2. transition logic to change the mode -> commandChildren + * + * Important: + * + * The implementation must call registerChild(object_id_t child) + * for all commanded children during initialization. + * The implementation must call the initialization function of the base class. + * (This will call the function in SubsystemBase) + * + */ class AssemblyBase: public SubsystemBase { public: static const uint8_t INTERFACE_ID = CLASS_ID::ASSEMBLY_BASE; @@ -16,10 +43,50 @@ public: static const ReturnValue_t NOT_ENOUGH_CHILDREN_IN_CORRECT_STATE = MAKE_RETURN_CODE(0xa1); - AssemblyBase(object_id_t objectId, object_id_t parentId, uint16_t commandQueueDepth = 8); + AssemblyBase(object_id_t objectId, object_id_t parentId, + uint16_t commandQueueDepth = 8); virtual ~AssemblyBase(); protected: + /** + * Command children to reach [mode,submode] combination + * Can be done by setting #commandsOutstanding correctly, + * or using executeTable() + * @param mode + * @param submode + * @return + * - @c RETURN_OK if ok + * - @c NEED_SECOND_STEP if children need to be commanded again + */ + virtual ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) = 0; + + /** + * Check whether desired assembly mode was achieved by checking the modes + * or/and health states of child device handlers. + * The assembly template class will also call this function if a health + * or mode change of a child device handler was detected. + * @param wantedMode + * @param wantedSubmode + * @return + */ + virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, + Submode_t wantedSubmode) = 0; + + /** + * Check whether a combination of mode and submode is valid. + * + * Ground Controller like precise return values from HasModesIF. + * So, please return any of them. + * + * @param mode The targeted mode + * @param submode The targeted submmode + * @return Any information why this combination is invalid from HasModesIF + * like HasModesIF::INVALID_SUBMODE. + * On success return HasReturnvaluesIF::RETURN_OK + */ + virtual ReturnValue_t isModeCombinationValid(Mode_t mode, + Submode_t submode) = 0; + enum InternalState { STATE_NONE, STATE_OVERWRITE_HEALTH, @@ -36,6 +103,7 @@ protected: RECOVERY_WAIT } recoveryState; //!< Indicates if one of the children requested a recovery. ChildrenMap::iterator recoveringDevice; + /** * the mode the current transition is trying to achieve. * Can be different from the modehelper.commandedMode! @@ -61,8 +129,8 @@ protected: bool handleChildrenChanged(); /** - * This method is called if the children changed its mode in a way that the current - * mode can't be kept. + * This method is called if the children changed its mode in a way that + * the current mode can't be kept. * Default behavior is to go to MODE_OFF. * @param result The failure code which was returned by checkChildrenState. */ @@ -75,9 +143,6 @@ protected: ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, uint32_t *msToReachTheMode); - virtual ReturnValue_t isModeCombinationValid(Mode_t mode, - Submode_t submode) = 0; - virtual void startTransition(Mode_t mode, Submode_t submode); virtual void doStartTransition(Mode_t mode, Submode_t submode); @@ -90,24 +155,6 @@ protected: void sendHealthCommand(MessageQueueId_t sendTo, HealthState health); - //SHOULDDO: Change that OVERWRITE_HEALTH may be returned (or return internalState directly?) - /** - * command children to reach mode,submode - * - * set #commandsOutstanding correctly, or use executeTable() - * - * @param mode - * @param submode - * @return - * - @c RETURN_OK if ok - * - @c NEED_SECOND_STEP if children need to be commanded again - */ - virtual ReturnValue_t commandChildren(Mode_t mode, Submode_t submode) = 0; - - //SHOULDDO: Remove wantedMode, wantedSubmode, as targetMode/submode is available? - virtual ReturnValue_t checkChildrenStateOn(Mode_t wantedMode, - Submode_t wantedSubmode) = 0; - virtual ReturnValue_t checkChildrenStateOff(); ReturnValue_t checkChildrenState(); @@ -125,8 +172,9 @@ protected: * Also sets state to STATE_OVERWRITE_HEATH. * @param objectId Must be a registered child. */ - void overwriteDeviceHealth(object_id_t objectId, HasHealthIF::HealthState oldHealth); + void overwriteDeviceHealth(object_id_t objectId, + HasHealthIF::HealthState oldHealth); }; -#endif /* ASSEMBLYBASE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_ASSEMBLYBASE_H_ */ diff --git a/devicehandlers/CMakeLists.txt b/devicehandlers/CMakeLists.txt new file mode 100644 index 000000000..50c1008fb --- /dev/null +++ b/devicehandlers/CMakeLists.txt @@ -0,0 +1,11 @@ +target_sources(${LIB_FSFW_NAME} + PRIVATE + AssemblyBase.cpp + ChildHandlerBase.cpp + ChildHandlerFDIR.cpp + DeviceHandlerBase.cpp + DeviceHandlerFailureIsolation.cpp + DeviceHandlerMessage.cpp + DeviceTmReportingWrapper.cpp + HealthDevice.cpp +) \ No newline at end of file diff --git a/devicehandlers/ChildHandlerBase.cpp b/devicehandlers/ChildHandlerBase.cpp index e800cd569..d4ef67ad8 100644 --- a/devicehandlers/ChildHandlerBase.cpp +++ b/devicehandlers/ChildHandlerBase.cpp @@ -1,16 +1,18 @@ +#include "ChildHandlerBase.h" #include "../subsystem/SubsystemBase.h" -#include "../devicehandlers/ChildHandlerBase.h" #include "../subsystem/SubsystemBase.h" ChildHandlerBase::ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, CookieIF * cookie, - uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId, - object_id_t parent, FailureIsolationBase* customFdir, - size_t cmdQueueSize) : + object_id_t hkDestination, uint32_t thermalStatePoolId, + uint32_t thermalRequestPoolId, + object_id_t parent, + FailureIsolationBase* customFdir, size_t cmdQueueSize) : DeviceHandlerBase(setObjectId, deviceCommunication, cookie, (customFdir == nullptr? &childHandlerFdir : customFdir), cmdQueueSize), parentId(parent), childHandlerFdir(setObjectId) { + this->setHkDestination(hkDestination); this->setThermalStateRequestPoolIds(thermalStatePoolId, thermalRequestPoolId); diff --git a/devicehandlers/ChildHandlerBase.h b/devicehandlers/ChildHandlerBase.h index f6bf318a0..eed4c95e2 100644 --- a/devicehandlers/ChildHandlerBase.h +++ b/devicehandlers/ChildHandlerBase.h @@ -1,17 +1,18 @@ -#ifndef FSFW_DEVICES_CHILDHANDLERBASE_H_ -#define FSFW_DEVICES_CHILDHANDLERBASE_H_ +#ifndef FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ +#define FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ -#include "ChildHandlerFDIR.h" #include "DeviceHandlerBase.h" +#include "ChildHandlerFDIR.h" class ChildHandlerBase: public DeviceHandlerBase { public: ChildHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, - CookieIF * cookie, uint32_t thermalStatePoolId, - uint32_t thermalRequestPoolId, + CookieIF * cookie, object_id_t hkDestination, + uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId, object_id_t parent = objects::NO_OBJECT, FailureIsolationBase* customFdir = nullptr, size_t cmdQueueSize = 20); + virtual ~ChildHandlerBase(); virtual ReturnValue_t initialize(); @@ -22,5 +23,4 @@ protected: }; -#endif /* FSFW_DEVICES_CHILDHANDLERBASE_H_ */ - +#endif /* FSFW_DEVICEHANDLER_CHILDHANDLERBASE_H_ */ diff --git a/devicehandlers/ChildHandlerFDIR.cpp b/devicehandlers/ChildHandlerFDIR.cpp index 14043a779..cb4c75eaf 100644 --- a/devicehandlers/ChildHandlerFDIR.cpp +++ b/devicehandlers/ChildHandlerFDIR.cpp @@ -1,6 +1,7 @@ #include "ChildHandlerFDIR.h" -ChildHandlerFDIR::ChildHandlerFDIR(object_id_t owner, object_id_t faultTreeParent, uint32_t recoveryCount) : +ChildHandlerFDIR::ChildHandlerFDIR(object_id_t owner, + object_id_t faultTreeParent, uint32_t recoveryCount) : DeviceHandlerFailureIsolation(owner, faultTreeParent) { recoveryCounter.setFailureThreshold(recoveryCount); } diff --git a/devicehandlers/ChildHandlerFDIR.h b/devicehandlers/ChildHandlerFDIR.h index ce8445183..13d1345c1 100644 --- a/devicehandlers/ChildHandlerFDIR.h +++ b/devicehandlers/ChildHandlerFDIR.h @@ -1,5 +1,5 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ -#define FRAMEWORK_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ +#ifndef FSFW_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ +#define FSFW_DEVICEHANDLERS_CHILDHANDLERFDIR_H_ #include "DeviceHandlerFailureIsolation.h" diff --git a/devicehandlers/CookieIF.h b/devicehandlers/CookieIF.h index 496cf0d2f..5911bfdb0 100644 --- a/devicehandlers/CookieIF.h +++ b/devicehandlers/CookieIF.h @@ -1,11 +1,12 @@ -#ifndef COOKIE_H_ -#define COOKIE_H_ +#ifndef FSFW_DEVICEHANDLER_COOKIE_H_ +#define FSFW_DEVICEHANDLER_COOKIE_H_ + #include /** * @brief Physical address type */ -typedef std::uint32_t address_t; +using address_t = uint32_t; /** * @brief This datatype is used to identify different connection over a @@ -16,7 +17,6 @@ typedef std::uint32_t address_t; * calling @code{.cpp} CookieIF* childCookie = new ChildCookie(...) * @endcode . * - * [not implemented yet] * This cookie is then passed to the child device handlers, which stores the * pointer and passes it to the communication interface functions. * @@ -31,4 +31,4 @@ public: virtual ~CookieIF() {}; }; -#endif /* COOKIE_H_ */ +#endif /* FSFW_DEVICEHANDLER_COOKIE_H_ */ diff --git a/devicehandlers/DeviceCommunicationIF.h b/devicehandlers/DeviceCommunicationIF.h index 11960d8e3..e0b473d36 100644 --- a/devicehandlers/DeviceCommunicationIF.h +++ b/devicehandlers/DeviceCommunicationIF.h @@ -1,9 +1,10 @@ -#ifndef DEVICECOMMUNICATIONIF_H_ -#define DEVICECOMMUNICATIONIF_H_ +#ifndef FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ +#define FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ #include "CookieIF.h" +#include "DeviceHandlerIF.h" + #include "../returnvalues/HasReturnvaluesIF.h" -#include /** * @defgroup interfaces Interfaces * @brief Interfaces for flight software objects @@ -19,8 +20,8 @@ * the device handler to allow reuse of these components. * @details * Documentation: Dissertation Baetz p.138. - * It works with the assumption that received data - * is polled by a component. There are four generic steps of device communication: + * It works with the assumption that received data is polled by a component. + * There are four generic steps of device communication: * * 1. Send data to a device * 2. Get acknowledgement for sending @@ -38,24 +39,20 @@ class DeviceCommunicationIF: public HasReturnvaluesIF { public: static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_COMMUNICATION_IF; - //! Standard Error Codes + //! This is returned in readReceivedMessage() if no reply was reived. + static const ReturnValue_t NO_REPLY_RECEIVED = MAKE_RETURN_CODE(0x01); + //! General protocol error. Define more concrete errors in child handler - static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0x01); + static const ReturnValue_t PROTOCOL_ERROR = MAKE_RETURN_CODE(0x02); //! If cookie is a null pointer - static const ReturnValue_t NULLPOINTER = MAKE_RETURN_CODE(0x02); - static const ReturnValue_t INVALID_COOKIE_TYPE = MAKE_RETURN_CODE(0x03); + static const ReturnValue_t NULLPOINTER = MAKE_RETURN_CODE(0x03); + static const ReturnValue_t INVALID_COOKIE_TYPE = MAKE_RETURN_CODE(0x04); // is this needed if there is no open/close call? static const ReturnValue_t NOT_ACTIVE = MAKE_RETURN_CODE(0x05); - static const ReturnValue_t INVALID_ADDRESS = MAKE_RETURN_CODE(0x06); - static const ReturnValue_t TOO_MUCH_DATA = MAKE_RETURN_CODE(0x07); - static const ReturnValue_t CANT_CHANGE_REPLY_LEN = MAKE_RETURN_CODE(0x08); - - //! Can be used in readReceivedMessage() if no reply was received. - static const ReturnValue_t NO_REPLY_RECEIVED = MAKE_RETURN_CODE(0xA1); + static const ReturnValue_t TOO_MUCH_DATA = MAKE_RETURN_CODE(0x06); virtual ~DeviceCommunicationIF() {} - /** * @brief Device specific initialization, using the cookie. * @details @@ -76,13 +73,13 @@ public: * by implementing and calling related drivers or wrapper functions. * @param cookie * @param data - * @param len + * @param len If this is 0, nothing shall be sent. * @return * - @c RETURN_OK for successfull send * - Everything else triggers failure event with returnvalue as parameter 1 */ - virtual ReturnValue_t sendMessage(CookieIF *cookie, const uint8_t * sendData, - size_t sendLen) = 0; + virtual ReturnValue_t sendMessage(CookieIF *cookie, + const uint8_t * sendData, size_t sendLen) = 0; /** * Called by DHB in the GET_WRITE doGetWrite(). @@ -108,7 +105,7 @@ public: * returnvalue as parameter 1 */ virtual ReturnValue_t requestReceiveMessage(CookieIF *cookie, - size_t requestLen) = 0; + size_t requestLen) = 0; /** * Called by DHB in the GET_WRITE doGetRead(). @@ -124,8 +121,8 @@ public: * - Everything else triggers failure event with * returnvalue as parameter 1 */ - virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, - size_t *size) = 0; + virtual ReturnValue_t readReceivedMessage(CookieIF *cookie, + uint8_t **buffer, size_t *size) = 0; }; -#endif /* DEVICECOMMUNICATIONIF_H_ */ +#endif /* FSFW_DEVICES_DEVICECOMMUNICATIONIF_H_ */ diff --git a/devicehandlers/DeviceHandlerBase.cpp b/devicehandlers/DeviceHandlerBase.cpp index 710e8a832..1623a1ac4 100644 --- a/devicehandlers/DeviceHandlerBase.cpp +++ b/devicehandlers/DeviceHandlerBase.cpp @@ -2,910 +2,991 @@ #include "AcceptsDeviceResponsesIF.h" #include "DeviceTmReportingWrapper.h" +#include "../serviceinterface/ServiceInterface.h" #include "../objectmanager/ObjectManager.h" #include "../storagemanager/StorageManagerIF.h" #include "../thermal/ThermalComponentIF.h" -#include "../datapool/DataSet.h" -#include "../datapool/PoolVariable.h" #include "../globalfunctions/CRC.h" -#include "../subsystem/SubsystemBase.h" +#include "../housekeeping/HousekeepingMessage.h" +#include "../ipc/MessageQueueMessage.h" #include "../ipc/QueueFactory.h" -#include "../serviceinterface/ServiceInterfaceStream.h" - -#include +#include "../subsystem/SubsystemBase.h" +#include "../datapoollocal/LocalPoolVariable.h" object_id_t DeviceHandlerBase::powerSwitcherId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::rawDataReceiverId = objects::NO_OBJECT; object_id_t DeviceHandlerBase::defaultFdirParentId = objects::NO_OBJECT; -DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, - object_id_t deviceCommunication, CookieIF * comCookie, - FailureIsolationBase* fdirInstance, size_t cmdQueueSize) : - SystemObject(setObjectId), mode(MODE_OFF), submode(SUBMODE_NONE), - wiretappingMode(OFF), storedRawData(StorageManagerIF::INVALID_ADDRESS), - deviceCommunicationId(deviceCommunication), comCookie(comCookie), - healthHelper(this,setObjectId), modeHelper(this), parameterHelper(this), - actionHelper(this, nullptr), childTransitionFailure(RETURN_OK), - fdirInstance(fdirInstance), hkSwitcher(this), - defaultFDIRUsed(fdirInstance == nullptr), switchOffWasReported(false), - childTransitionDelay(5000), transitionSourceMode(_MODE_POWER_DOWN), - transitionSourceSubMode(SUBMODE_NONE) { - commandQueue = QueueFactory::instance()->createMessageQueue(cmdQueueSize, - MessageQueueMessage::MAX_MESSAGE_SIZE); - insertInCommandMap(RAW_COMMAND_ID); - cookieInfo.state = COOKIE_UNUSED; - cookieInfo.pendingCommand = deviceCommandMap.end(); - if (comCookie == nullptr) { - sif::error << "DeviceHandlerBase: ObjectID 0x" << std::hex - << std::setw(8) << std::setfill('0') << this->getObjectId() - << std::dec << ": Do not pass nullptr as a cookie, consider " - << std::setfill(' ') << "passing a dummy cookie instead!" - << std::endl; - } - if (this->fdirInstance == nullptr) { - this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId, - defaultFdirParentId); - } +DeviceHandlerBase::DeviceHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, + CookieIF* comCookie, FailureIsolationBase* fdirInstance, size_t cmdQueueSize): + SystemObject(setObjectId), mode(MODE_OFF), submode(SUBMODE_NONE), + wiretappingMode(OFF), storedRawData(StorageManagerIF::INVALID_ADDRESS), + deviceCommunicationId(deviceCommunication), comCookie(comCookie), + healthHelper(this,setObjectId), modeHelper(this), parameterHelper(this), + actionHelper(this, nullptr), poolManager(this, nullptr), + childTransitionFailure(RETURN_OK), fdirInstance(fdirInstance), + defaultFDIRUsed(fdirInstance == nullptr), + switchOffWasReported(false), childTransitionDelay(5000), + transitionSourceMode(_MODE_POWER_DOWN), + transitionSourceSubMode(SUBMODE_NONE) { + commandQueue = QueueFactory::instance()->createMessageQueue(cmdQueueSize, + MessageQueueMessage::MAX_MESSAGE_SIZE); + insertInCommandMap(RAW_COMMAND_ID); + cookieInfo.state = COOKIE_UNUSED; + cookieInfo.pendingCommand = deviceCommandMap.end(); + if (comCookie == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, "DeviceHandlerBase", + HasReturnvaluesIF::RETURN_FAILED, "Invalid cookie"); + } + if (this->fdirInstance == nullptr) { + this->fdirInstance = new DeviceHandlerFailureIsolation(setObjectId, + defaultFdirParentId); + } +} + +void DeviceHandlerBase::setHkDestination(object_id_t hkDestination) { + this->hkDestination = hkDestination; } void DeviceHandlerBase::setThermalStateRequestPoolIds( - uint32_t thermalStatePoolId, uint32_t thermalRequestPoolId) { - this->deviceThermalRequestPoolId = thermalStatePoolId; - this->deviceThermalRequestPoolId = thermalRequestPoolId; + lp_id_t thermalStatePoolId, lp_id_t heaterRequestPoolId, + uint32_t thermalSetId) { + thermalSet = new DeviceHandlerThermalSet(this, thermalSetId, + thermalStatePoolId, heaterRequestPoolId); } DeviceHandlerBase::~DeviceHandlerBase() { - delete comCookie; - if (defaultFDIRUsed) { - delete fdirInstance; - } - QueueFactory::instance()->deleteMessageQueue(commandQueue); + delete comCookie; + if (defaultFDIRUsed) { + delete fdirInstance; + } + QueueFactory::instance()->deleteMessageQueue(commandQueue); } ReturnValue_t DeviceHandlerBase::performOperation(uint8_t counter) { - this->pstStep = counter; + this->pstStep = counter; + this->lastStep = this->pstStep; - if (getComAction() == SEND_WRITE) { - cookieInfo.state = COOKIE_UNUSED; - readCommandQueue(); - doStateMachine(); - checkSwitchState(); - decrementDeviceReplyMap(); - fdirInstance->checkForFailures(); - hkSwitcher.performOperation(); - performOperationHook(); - } - if (mode == MODE_OFF) { - return RETURN_OK; - } - switch (getComAction()) { - case SEND_WRITE: - if ((cookieInfo.state == COOKIE_UNUSED)) { - buildInternalCommand(); - } - doSendWrite(); - break; - case GET_WRITE: - doGetWrite(); - break; - case SEND_READ: - doSendRead(); - break; - case GET_READ: - doGetRead(); - cookieInfo.state = COOKIE_UNUSED; - break; - default: - break; - } - return RETURN_OK; + if (getComAction() == CommunicationAction::NOTHING) { + return HasReturnvaluesIF::RETURN_OK; + } + + if (getComAction() == CommunicationAction::PERFORM_OPERATION) { + cookieInfo.state = COOKIE_UNUSED; + readCommandQueue(); + doStateMachine(); + checkSwitchState(); + decrementDeviceReplyMap(); + fdirInstance->checkForFailures(); + performOperationHook(); + return RETURN_OK; + } + + if (mode == MODE_OFF) { + return RETURN_OK; + } + + switch (getComAction()) { + case CommunicationAction::SEND_WRITE: + if (cookieInfo.state == COOKIE_UNUSED) { + /* If no external command was specified, build internal command. */ + buildInternalCommand(); + } + doSendWrite(); + break; + case CommunicationAction::GET_WRITE: + doGetWrite(); + break; + case CommunicationAction::SEND_READ: + doSendRead(); + break; + case CommunicationAction::GET_READ: + doGetRead(); + /* This will be performed after datasets have been updated by the + custom device implementation. */ + poolManager.performHkOperation(); + break; + default: + break; + } + return RETURN_OK; } ReturnValue_t DeviceHandlerBase::initialize() { - ReturnValue_t result = SystemObject::initialize(); - if (result != RETURN_OK) { - return result; - } + ReturnValue_t result = SystemObject::initialize(); + if (result != RETURN_OK) { + return result; + } - communicationInterface = objectManager->get( - deviceCommunicationId); - if (communicationInterface == nullptr) { - sif::error << "DeviceHandlerBase::initialize: Communication interface " - "invalid." << std::endl; - sif::error << "Make sure it is set up properly and implements" - " DeviceCommunicationIF" << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } + communicationInterface = objectManager->get( + deviceCommunicationId); + if (communicationInterface == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", + ObjectManagerIF::CHILD_INIT_FAILED, + "Passed communication IF invalid"); + return ObjectManagerIF::CHILD_INIT_FAILED; + } - result = communicationInterface->initializeInterface(comCookie); - if (result != RETURN_OK) { - return result; - } + result = communicationInterface->initializeInterface(comCookie); + if (result != RETURN_OK) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", + ObjectManagerIF::CHILD_INIT_FAILED, + "ComIF initialization failed"); + return result; + } - IPCStore = objectManager->get(objects::IPC_STORE); - if (IPCStore == nullptr) { - sif::error << "DeviceHandlerBase::initialize: IPC store not set up in " - "factory." << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } + IPCStore = objectManager->get(objects::IPC_STORE); + if (IPCStore == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, "initialize", + ObjectManagerIF::CHILD_INIT_FAILED, "IPC Store not set up"); + return ObjectManagerIF::CHILD_INIT_FAILED; + } - if(rawDataReceiverId != objects::NO_OBJECT) { - AcceptsDeviceResponsesIF *rawReceiver = objectManager->get< - AcceptsDeviceResponsesIF>(rawDataReceiverId); + if(rawDataReceiverId != objects::NO_OBJECT) { + AcceptsDeviceResponsesIF *rawReceiver = objectManager->get< + AcceptsDeviceResponsesIF>(rawDataReceiverId); - if (rawReceiver == nullptr) { - sif::error << "DeviceHandlerBase::initialize: Raw receiver object " - "ID set but no valid object found." << std::endl; - sif::error << "Make sure the raw receiver object is set up properly" - " and implements AcceptsDeviceResponsesIF" << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } - defaultRawReceiver = rawReceiver->getDeviceQueue(); - } + if (rawReceiver == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "initialize", ObjectManagerIF::CHILD_INIT_FAILED, + "Raw receiver object ID set but no valid object found."); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "Make sure the raw receiver object is set up properly" + " and implements AcceptsDeviceResponsesIF" << std::endl; +#else + sif::printError("Make sure the raw receiver object is set up " + "properly and implements AcceptsDeviceResponsesIF\n"); +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + defaultRawReceiver = rawReceiver->getDeviceQueue(); + } - if(powerSwitcherId != objects::NO_OBJECT) { - powerSwitcher = objectManager->get(powerSwitcherId); - if (powerSwitcher == nullptr) { - sif::error << "DeviceHandlerBase::initialize: Power switcher " - << "object ID set but no valid object found." << std::endl; - sif::error << "Make sure the raw receiver object is set up properly" - << " and implements PowerSwitchIF" << std::endl; - return ObjectManagerIF::CHILD_INIT_FAILED; - } - } + if(powerSwitcherId != objects::NO_OBJECT) { + powerSwitcher = objectManager->get(powerSwitcherId); + if (powerSwitcher == nullptr) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "initialize", ObjectManagerIF::CHILD_INIT_FAILED, + "Power switcher set but no valid object found."); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "Make sure the power switcher object is set up " + << "properly and implements PowerSwitchIF" << std::endl; +#else + sif::printError("Make sure the power switcher object is set up " + "properly and implements PowerSwitchIF\n"); +#endif + return ObjectManagerIF::CHILD_INIT_FAILED; + } + } - result = healthHelper.initialize(); - if (result != RETURN_OK) { - return result; - } + result = healthHelper.initialize(); + if (result != RETURN_OK) { + return result; + } - result = modeHelper.initialize(); - if (result != RETURN_OK) { - return result; - } - result = actionHelper.initialize(commandQueue); - if (result != RETURN_OK) { - return result; - } - result = fdirInstance->initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + result = modeHelper.initialize(); + if (result != RETURN_OK) { + return result; + } + result = actionHelper.initialize(commandQueue); + if (result != RETURN_OK) { + return result; + } + result = fdirInstance->initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } - result = parameterHelper.initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + result = parameterHelper.initialize(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } - result = hkSwitcher.initialize(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } + result = poolManager.initialize(commandQueue); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } - fillCommandAndReplyMap(); + fillCommandAndReplyMap(); - //Set temperature target state to NON_OP. - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_WRITE); - mySet.read(); - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); + if(thermalSet != nullptr) { + //Set temperature target state to NON_OP. + result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + thermalSet->heaterRequest.value = + ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; + thermalSet->heaterRequest.setValid(true); + thermalSet->commit(); + } - return RETURN_OK; + } + return RETURN_OK; } void DeviceHandlerBase::decrementDeviceReplyMap() { - for (std::map::iterator iter = - deviceReplyMap.begin(); iter != deviceReplyMap.end(); iter++) { - if (iter->second.delayCycles != 0) { - iter->second.delayCycles--; - if (iter->second.delayCycles == 0) { - if (iter->second.periodic) { - iter->second.delayCycles = iter->second.maxDelayCycles; - } - replyToReply(iter, TIMEOUT); - missedReply(iter->first); - } - } - } + for (std::map::iterator iter = + deviceReplyMap.begin(); iter != deviceReplyMap.end(); iter++) { + if (iter->second.delayCycles != 0) { + iter->second.delayCycles--; + if (iter->second.delayCycles == 0) { + if (iter->second.periodic) { + iter->second.delayCycles = iter->second.maxDelayCycles; + } + replyToReply(iter, TIMEOUT); + missedReply(iter->first); + } + } + } } void DeviceHandlerBase::readCommandQueue() { - if (dontCheckQueue()) { - return; - } + if (dontCheckQueue()) { + return; + } - CommandMessage command; - ReturnValue_t result = commandQueue->receiveMessage(&command); - if (result != RETURN_OK) { - return; - } + CommandMessage command; + ReturnValue_t result = commandQueue->receiveMessage(&command); + if (result != RETURN_OK) { + return; + } - result = healthHelper.handleHealthCommand(&command); - if (result == RETURN_OK) { - return; - } + result = healthHelper.handleHealthCommand(&command); + if (result == RETURN_OK) { + return; + } - result = modeHelper.handleModeCommand(&command); - if (result == RETURN_OK) { - return; - } + result = modeHelper.handleModeCommand(&command); + if (result == RETURN_OK) { + return; + } - result = actionHelper.handleActionMessage(&command); - if (result == RETURN_OK) { - return; - } + result = actionHelper.handleActionMessage(&command); + if (result == RETURN_OK) { + return; + } - result = parameterHelper.handleParameterMessage(&command); - if (result == RETURN_OK) { - return; - } + result = parameterHelper.handleParameterMessage(&command); + if (result == RETURN_OK) { + return; + } -// result = hkManager.handleHousekeepingMessage(&command); -// if (result == RETURN_OK) { -// return; -// } + result = poolManager.handleHousekeepingMessage(&command); + if (result == RETURN_OK) { + return; + } - result = handleDeviceHandlerMessage(&command); - if (result == RETURN_OK) { - return; - } + result = handleDeviceHandlerMessage(&command); + if (result == RETURN_OK) { + return; + } - result = letChildHandleMessage(&command); - if (result == RETURN_OK) { - return; - } + result = letChildHandleMessage(&command); + if (result == RETURN_OK) { + return; + } - replyReturnvalueToCommand(CommandMessage::UNKNOWN_COMMAND); + replyReturnvalueToCommand(CommandMessage::UNKNOWN_COMMAND); } void DeviceHandlerBase::doStateMachine() { - switch (mode) { - case _MODE_START_UP: - case _MODE_SHUT_DOWN: - case _MODE_TO_NORMAL: - case _MODE_TO_ON: - case _MODE_TO_RAW: { - Mode_t currentMode = mode; - callChildStatemachine(); - //Only do timeout if child did not change anything - if (mode != currentMode) { - break; - } - uint32_t currentUptime; - Clock::getUptime(¤tUptime); - if (currentUptime - timeoutStart >= childTransitionDelay) { - triggerEvent(MODE_TRANSITION_FAILED, childTransitionFailure, 0); - setMode(transitionSourceMode, transitionSourceSubMode); - break; - } - } - break; - case _MODE_POWER_DOWN: - commandSwitch(PowerSwitchIF::SWITCH_OFF); - setMode(_MODE_WAIT_OFF); - break; - case _MODE_POWER_ON: - commandSwitch(PowerSwitchIF::SWITCH_ON); - setMode(_MODE_WAIT_ON); - break; - case _MODE_WAIT_ON: { - uint32_t currentUptime; - Clock::getUptime(¤tUptime); - if (powerSwitcher != nullptr and currentUptime - timeoutStart >= - powerSwitcher->getSwitchDelayMs()) { - triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, - 0); - setMode(_MODE_POWER_DOWN); - callChildStatemachine(); - break; - } - ReturnValue_t switchState = getStateOfSwitches(); - if ((switchState == PowerSwitchIF::SWITCH_ON) - || (switchState == NO_SWITCH)) { - //NOTE: TransitionSourceMode and -SubMode are set by handleCommandedModeTransition - childTransitionFailure = CHILD_TIMEOUT; - setMode(_MODE_START_UP); - callChildStatemachine(); - } - } - break; - case _MODE_WAIT_OFF: { - uint32_t currentUptime; - Clock::getUptime(¤tUptime); + switch (mode) { + case _MODE_START_UP: + case _MODE_SHUT_DOWN: + case _MODE_TO_NORMAL: + case _MODE_TO_ON: + case _MODE_TO_RAW: { + Mode_t currentMode = mode; + callChildStatemachine(); + //Only do timeout if child did not change anything + if (mode != currentMode) { + break; + } + uint32_t currentUptime; + Clock::getUptime(¤tUptime); + if (currentUptime - timeoutStart >= childTransitionDelay) { +#if FSFW_VERBOSE_LEVEL >= 1 && FSFW_OBJ_EVENT_TRANSLATION == 0 + char printout[60]; + sprintf(printout, "Transition timeout (%lu) occured !", + static_cast(childTransitionDelay)); + /* Common configuration error for development, so print it */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, "doStateMachine", + RETURN_FAILED, printout); +#endif + triggerEvent(MODE_TRANSITION_FAILED, childTransitionFailure, 0); + setMode(transitionSourceMode, transitionSourceSubMode); + break; + } + } + break; + case _MODE_POWER_DOWN: + commandSwitch(PowerSwitchIF::SWITCH_OFF); + setMode(_MODE_WAIT_OFF); + break; + case _MODE_POWER_ON: + commandSwitch(PowerSwitchIF::SWITCH_ON); + setMode(_MODE_WAIT_ON); + break; + case _MODE_WAIT_ON: { + uint32_t currentUptime; + Clock::getUptime(¤tUptime); + if (powerSwitcher != nullptr and currentUptime - timeoutStart >= + powerSwitcher->getSwitchDelayMs()) { + triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, + 0); + setMode(_MODE_POWER_DOWN); + callChildStatemachine(); + break; + } + ReturnValue_t switchState = getStateOfSwitches(); + if ((switchState == PowerSwitchIF::SWITCH_ON) + || (switchState == NO_SWITCH)) { + //NOTE: TransitionSourceMode and -SubMode are set by handleCommandedModeTransition + childTransitionFailure = CHILD_TIMEOUT; + setMode(_MODE_START_UP); + callChildStatemachine(); + } + } + break; + case _MODE_WAIT_OFF: { + uint32_t currentUptime; + Clock::getUptime(¤tUptime); - if(powerSwitcher == nullptr) { - setMode(MODE_OFF); - break; - } + if(powerSwitcher == nullptr) { + setMode(MODE_OFF); + break; + } - if (currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) { - triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, - 0); - setMode(MODE_ERROR_ON); - break; - } - ReturnValue_t switchState = getStateOfSwitches(); - if ((switchState == PowerSwitchIF::SWITCH_OFF) - || (switchState == NO_SWITCH)) { - setMode(_MODE_SWITCH_IS_OFF); - } - } - break; - case MODE_OFF: - doOffActivity(); - break; - case MODE_ON: - doOnActivity(); - break; - case MODE_RAW: - case MODE_NORMAL: - case MODE_ERROR_ON: - break; - case _MODE_SWITCH_IS_OFF: - setMode(MODE_OFF, SUBMODE_NONE); - break; - default: - triggerEvent(OBJECT_IN_INVALID_MODE, mode, submode); - setMode(_MODE_POWER_DOWN, 0); - break; - } + if (currentUptime - timeoutStart >= powerSwitcher->getSwitchDelayMs()) { + triggerEvent(MODE_TRANSITION_FAILED, PowerSwitchIF::SWITCH_TIMEOUT, + 0); + setMode(MODE_ERROR_ON); + break; + } + ReturnValue_t switchState = getStateOfSwitches(); + if ((switchState == PowerSwitchIF::SWITCH_OFF) + || (switchState == NO_SWITCH)) { + setMode(_MODE_SWITCH_IS_OFF); + } + } + break; + case MODE_OFF: + doOffActivity(); + break; + case MODE_ON: + doOnActivity(); + break; + case MODE_RAW: + case MODE_NORMAL: + case MODE_ERROR_ON: + break; + case _MODE_SWITCH_IS_OFF: + setMode(MODE_OFF, SUBMODE_NONE); + break; + default: + triggerEvent(OBJECT_IN_INVALID_MODE, mode, submode); + setMode(_MODE_POWER_DOWN, 0); + break; + } } ReturnValue_t DeviceHandlerBase::isModeCombinationValid(Mode_t mode, - Submode_t submode) { - switch (mode) { - case MODE_OFF: - case MODE_ON: - case MODE_NORMAL: - case MODE_RAW: - if (submode == SUBMODE_NONE) { - return RETURN_OK; - } else { - return INVALID_SUBMODE; - } - default: - return HasModesIF::INVALID_MODE; - } + Submode_t submode) { + switch (mode) { + case MODE_OFF: + case MODE_ON: + case MODE_NORMAL: + case MODE_RAW: + if (submode == SUBMODE_NONE) { + return RETURN_OK; + } else { + return INVALID_SUBMODE; + } + default: + return HasModesIF::INVALID_MODE; + } } ReturnValue_t DeviceHandlerBase::insertInCommandAndReplyMap( - DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, - size_t replyLen, bool periodic, bool hasDifferentReplyId, - DeviceCommandId_t replyId) { - //No need to check, as we may try to insert multiple times. - insertInCommandMap(deviceCommand); - if (hasDifferentReplyId) { - return insertInReplyMap(replyId, maxDelayCycles, replyLen, periodic); - } else { - return insertInReplyMap(deviceCommand, maxDelayCycles, replyLen, periodic); - } + DeviceCommandId_t deviceCommand, uint16_t maxDelayCycles, + LocalPoolDataSetBase* replyDataSet, size_t replyLen, bool periodic, + bool hasDifferentReplyId, DeviceCommandId_t replyId) { + //No need to check, as we may try to insert multiple times. + insertInCommandMap(deviceCommand); + if (hasDifferentReplyId) { + return insertInReplyMap(replyId, maxDelayCycles, + replyDataSet, replyLen, periodic); + } else { + return insertInReplyMap(deviceCommand, maxDelayCycles, + replyDataSet, replyLen, periodic); + } } ReturnValue_t DeviceHandlerBase::insertInReplyMap(DeviceCommandId_t replyId, - uint16_t maxDelayCycles, size_t replyLen, bool periodic) { - DeviceReplyInfo info; - info.maxDelayCycles = maxDelayCycles; - info.periodic = periodic; - info.delayCycles = 0; - info.replyLen = replyLen; - info.command = deviceCommandMap.end(); - auto resultPair = deviceReplyMap.emplace(replyId, info); - if (resultPair.second) { - return RETURN_OK; - } else { - return RETURN_FAILED; - } + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet, + size_t replyLen, bool periodic) { + DeviceReplyInfo info; + info.maxDelayCycles = maxDelayCycles; + info.periodic = periodic; + info.delayCycles = 0; + info.replyLen = replyLen; + info.dataSet = dataSet; + info.command = deviceCommandMap.end(); + auto resultPair = deviceReplyMap.emplace(replyId, info); + if (resultPair.second) { + return RETURN_OK; + } else { + return RETURN_FAILED; + } } -ReturnValue_t DeviceHandlerBase::insertInCommandMap( - DeviceCommandId_t deviceCommand) { - DeviceCommandInfo info; - info.expectedReplies = 0; - info.isExecuting = false; - info.sendReplyTo = NO_COMMANDER; - auto resultPair = deviceCommandMap.emplace(deviceCommand, info); - if (resultPair.second) { - return RETURN_OK; - } else { - return RETURN_FAILED; - } +ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceCommand) { + DeviceCommandInfo info; + info.expectedReplies = 0; + info.isExecuting = false; + info.sendReplyTo = NO_COMMANDER; + auto resultPair = deviceCommandMap.emplace(deviceCommand, info); + if (resultPair.second) { + return RETURN_OK; + } else { + return RETURN_FAILED; + } +} + +size_t DeviceHandlerBase::getNextReplyLength(DeviceCommandId_t commandId){ + DeviceReplyIter iter = deviceReplyMap.find(commandId); + if(iter != deviceReplyMap.end()) { + return iter->second.replyLen; + }else{ + return 0; + } } ReturnValue_t DeviceHandlerBase::updateReplyMapEntry(DeviceCommandId_t deviceReply, - uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) { - std::map::iterator iter = - deviceReplyMap.find(deviceReply); - if (iter == deviceReplyMap.end()) { - triggerEvent(INVALID_DEVICE_COMMAND, deviceReply); - return RETURN_FAILED; - } else { - DeviceReplyInfo *info = &(iter->second); - if (maxDelayCycles != 0) { - info->maxDelayCycles = maxDelayCycles; - } - info->delayCycles = delayCycles; - info->periodic = periodic; - return RETURN_OK; - } + uint16_t delayCycles, uint16_t maxDelayCycles, bool periodic) { + auto replyIter = deviceReplyMap.find(deviceReply); + if (replyIter == deviceReplyMap.end()) { + triggerEvent(INVALID_DEVICE_COMMAND, deviceReply); + return RETURN_FAILED; + } else { + DeviceReplyInfo *info = &(replyIter->second); + if (maxDelayCycles != 0) { + info->maxDelayCycles = maxDelayCycles; + } + info->delayCycles = delayCycles; + info->periodic = periodic; + return RETURN_OK; + } +} + + +ReturnValue_t DeviceHandlerBase::setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase *dataSet) { + auto replyIter = deviceReplyMap.find(replyId); + if(replyIter == deviceReplyMap.end()) { + return HasReturnvaluesIF::RETURN_FAILED; + } + replyIter->second.dataSet = dataSet; + return HasReturnvaluesIF::RETURN_OK; } void DeviceHandlerBase::callChildStatemachine() { - if (mode == _MODE_START_UP) { - doStartUp(); - } else if (mode == _MODE_SHUT_DOWN) { - doShutDown(); - } else if (mode & TRANSITION_MODE_CHILD_ACTION_MASK) { - doTransition(transitionSourceMode, transitionSourceSubMode); - } + if (mode == _MODE_START_UP) { + doStartUp(); + } else if (mode == _MODE_SHUT_DOWN) { + doShutDown(); + } else if (mode & TRANSITION_MODE_CHILD_ACTION_MASK) { + doTransition(transitionSourceMode, transitionSourceSubMode); + } } void DeviceHandlerBase::setTransition(Mode_t modeTo, Submode_t submodeTo) { - triggerEvent(CHANGING_MODE, modeTo, submodeTo); - childTransitionDelay = getTransitionDelayMs(mode, modeTo); - transitionSourceMode = mode; - transitionSourceSubMode = submode; - childTransitionFailure = CHILD_TIMEOUT; + triggerEvent(CHANGING_MODE, modeTo, submodeTo); + childTransitionDelay = getTransitionDelayMs(mode, modeTo); + transitionSourceMode = mode; + transitionSourceSubMode = submode; + childTransitionFailure = CHILD_TIMEOUT; - // transitionTargetMode is set by setMode - setMode((modeTo | TRANSITION_MODE_CHILD_ACTION_MASK), submodeTo); + // transitionTargetMode is set by setMode + setMode((modeTo | TRANSITION_MODE_CHILD_ACTION_MASK), submodeTo); } void DeviceHandlerBase::setMode(Mode_t newMode, uint8_t newSubmode) { - changeHK(mode, submode, false); - submode = newSubmode; - mode = newMode; - modeChanged(); - setNormalDatapoolEntriesInvalid(); - if (!isTransitionalMode()) { - modeHelper.modeChanged(newMode, newSubmode); - announceMode(false); - } - Clock::getUptime(&timeoutStart); + /* TODO: This will probably be done by the LocalDataPoolManager now */ + //changeHK(mode, submode, false); + submode = newSubmode; + mode = newMode; + modeChanged(); + setNormalDatapoolEntriesInvalid(); + if (!isTransitionalMode()) { + modeHelper.modeChanged(newMode, newSubmode); + announceMode(false); + } + Clock::getUptime(&timeoutStart); - if (mode == MODE_OFF) { - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_NON_OPERATIONAL; - } - mySet.commit(PoolVariableIF::VALID); - } - changeHK(mode, submode, true); + if (mode == MODE_OFF and thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if (thermalSet->heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + thermalSet->heaterRequest.value = ThermalComponentIF:: + STATE_REQUEST_NON_OPERATIONAL; + } + thermalSet->heaterRequest.commit(PoolVariableIF::VALID); + } + + } + /* TODO: This will probably be done by the LocalDataPoolManager now */ + //changeHK(mode, submode, true); } void DeviceHandlerBase::setMode(Mode_t newMode) { - setMode(newMode, submode); + setMode(newMode, submode); } void DeviceHandlerBase::replyReturnvalueToCommand(ReturnValue_t status, - uint32_t parameter) { - //This is actually the reply protocol for raw and misc DH commands. - if (status == RETURN_OK) { - CommandMessage reply(CommandMessage::REPLY_COMMAND_OK, 0, parameter); - commandQueue->reply(&reply); - } else { - CommandMessage reply(CommandMessage::REPLY_REJECTED, status, parameter); - commandQueue->reply(&reply); - } + uint32_t parameter) { + //This is actually the reply protocol for raw and misc DH commands. + if (status == RETURN_OK) { + CommandMessage reply(CommandMessage::REPLY_COMMAND_OK, 0, parameter); + commandQueue->reply(&reply); + } else { + CommandMessage reply(CommandMessage::REPLY_REJECTED, status, parameter); + commandQueue->reply(&reply); + } } void DeviceHandlerBase::replyToCommand(ReturnValue_t status, - uint32_t parameter) { -//Check if we reply to a raw command. - if (cookieInfo.pendingCommand->first == RAW_COMMAND_ID) { - if (status == NO_REPLY_EXPECTED) { - status = RETURN_OK; - } - replyReturnvalueToCommand(status, parameter); - //Always delete data from a raw command. - IPCStore->deleteData(storedRawData); - return; - } -//Check if we were externally commanded. - if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) { - MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo; - if (status == NO_REPLY_EXPECTED) { - actionHelper.finish(queueId, cookieInfo.pendingCommand->first, - RETURN_OK); - } else { - actionHelper.step(1, queueId, cookieInfo.pendingCommand->first, - status); - } - } + uint32_t parameter) { + // Check if we reply to a raw command. + if (cookieInfo.pendingCommand->first == RAW_COMMAND_ID) { + if (status == NO_REPLY_EXPECTED) { + status = RETURN_OK; + } + replyReturnvalueToCommand(status, parameter); + // Always delete data from a raw command. + IPCStore->deleteData(storedRawData); + return; + } + // Check if we were externally commanded. + if (cookieInfo.pendingCommand->second.sendReplyTo != NO_COMMANDER) { + MessageQueueId_t queueId = cookieInfo.pendingCommand->second.sendReplyTo; + if (status == NO_REPLY_EXPECTED) { + actionHelper.finish(true, queueId, cookieInfo.pendingCommand->first, + RETURN_OK); + } else { + actionHelper.step(1, queueId, cookieInfo.pendingCommand->first, + status); + } + } } void DeviceHandlerBase::replyToReply(DeviceReplyMap::iterator iter, - ReturnValue_t status) { -//No need to check if iter exists, as this is checked by callers. If someone else uses the method, add check. - if (iter->second.command == deviceCommandMap.end()) { - //Is most likely periodic reply. Silent return. - return; - } -//Check if more replies are expected. If so, do nothing. - DeviceCommandInfo* info = &(iter->second.command->second); - if (--info->expectedReplies == 0) { - //Check if it was transition or internal command. Don't send any replies in that case. - if (info->sendReplyTo != NO_COMMANDER) { - actionHelper.finish(info->sendReplyTo, iter->first, status); - } - info->isExecuting = false; - } + ReturnValue_t status) { + // No need to check if iter exists, as this is checked by callers. + // If someone else uses the method, add check. + if (iter->second.command == deviceCommandMap.end()) { + //Is most likely periodic reply. Silent return. + return; + } + // Check if more replies are expected. If so, do nothing. + DeviceCommandInfo* info = &(iter->second.command->second); + if (--info->expectedReplies == 0) { + // Check if it was transition or internal command. + // Don't send any replies in that case. + if (info->sendReplyTo != NO_COMMANDER) { + bool success = false; + if(status == HasReturnvaluesIF::RETURN_OK) { + success = true; + } + actionHelper.finish(success, info->sendReplyTo, iter->first, status); + } + info->isExecuting = false; + } } void DeviceHandlerBase::doSendWrite() { - if (cookieInfo.state == COOKIE_WRITE_READY) { + if (cookieInfo.state == COOKIE_WRITE_READY) { - ReturnValue_t result = communicationInterface->sendMessage(comCookie, - rawPacket, rawPacketLen); + ReturnValue_t result = communicationInterface->sendMessage(comCookie, + rawPacket, rawPacketLen); - if (result == RETURN_OK) { - cookieInfo.state = COOKIE_WRITE_SENT; - } else { - //always generate a failure event, so that FDIR knows what's up - triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result, - cookieInfo.pendingCommand->first); - replyToCommand(result); - cookieInfo.state = COOKIE_UNUSED; - cookieInfo.pendingCommand->second.isExecuting = false; - } - } + if (result == RETURN_OK) { + cookieInfo.state = COOKIE_WRITE_SENT; + } else { + // always generate a failure event, so that FDIR knows what's up + triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result, + cookieInfo.pendingCommand->first); + replyToCommand(result); + cookieInfo.state = COOKIE_UNUSED; + cookieInfo.pendingCommand->second.isExecuting = false; + } + } } void DeviceHandlerBase::doGetWrite() { - if (cookieInfo.state != COOKIE_WRITE_SENT) { - return; - } - cookieInfo.state = COOKIE_UNUSED; - ReturnValue_t result = communicationInterface->getSendSuccess(comCookie); - if (result == RETURN_OK) { - if (wiretappingMode == RAW) { - replyRawData(rawPacket, rawPacketLen, requestedRawTraffic, true); - } + if (cookieInfo.state != COOKIE_WRITE_SENT) { + return; + } + cookieInfo.state = COOKIE_UNUSED; + ReturnValue_t result = communicationInterface->getSendSuccess(comCookie); + if (result == RETURN_OK) { + if (wiretappingMode == RAW) { + replyRawData(rawPacket, rawPacketLen, requestedRawTraffic, true); + } - //We need to distinguish here, because a raw command never expects a reply. - //(Could be done in eRIRM, but then child implementations need to be careful. - result = enableReplyInReplyMap(cookieInfo.pendingCommand); - } else { - //always generate a failure event, so that FDIR knows what's up - triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result, - cookieInfo.pendingCommand->first); - } - if (result != RETURN_OK) { - cookieInfo.pendingCommand->second.isExecuting = false; - } - replyToCommand(result); + //We need to distinguish here, because a raw command never expects a reply. + //(Could be done in eRIRM, but then child implementations need to be careful. + result = enableReplyInReplyMap(cookieInfo.pendingCommand); + } else { + //always generate a failure event, so that FDIR knows what's up + triggerEvent(DEVICE_SENDING_COMMAND_FAILED, result, + cookieInfo.pendingCommand->first); + } + if (result != RETURN_OK) { + cookieInfo.pendingCommand->second.isExecuting = false; + } + replyToCommand(result); } void DeviceHandlerBase::doSendRead() { - ReturnValue_t result; + ReturnValue_t result; - size_t requestLen = 0; - if(cookieInfo.pendingCommand != deviceCommandMap.end()) { - DeviceReplyIter iter = deviceReplyMap.find( - cookieInfo.pendingCommand->first); - if(iter != deviceReplyMap.end()) { - requestLen = iter->second.replyLen; - } - } + size_t replyLen = 0; + if(cookieInfo.pendingCommand != deviceCommandMap.end()) { + replyLen = getNextReplyLength(cookieInfo.pendingCommand->first); + } - result = communicationInterface->requestReceiveMessage(comCookie, requestLen); + result = communicationInterface->requestReceiveMessage(comCookie, replyLen); - if (result == RETURN_OK) { - cookieInfo.state = COOKIE_READ_SENT; - } else { - triggerEvent(DEVICE_REQUESTING_REPLY_FAILED, result); - //We can't inform anyone, because we don't know which command was sent last. - //So, we need to wait for a timeout. - //but I think we can allow to ignore one missedReply. - ignoreMissedRepliesCount++; - cookieInfo.state = COOKIE_UNUSED; - } + if (result == RETURN_OK) { + cookieInfo.state = COOKIE_READ_SENT; + } else { + triggerEvent(DEVICE_REQUESTING_REPLY_FAILED, result); + //We can't inform anyone, because we don't know which command was sent last. + //So, we need to wait for a timeout. + //but I think we can allow to ignore one missedReply. + ignoreMissedRepliesCount++; + cookieInfo.state = COOKIE_UNUSED; + } } void DeviceHandlerBase::doGetRead() { - size_t receivedDataLen = 0; - uint8_t *receivedData = nullptr; + size_t receivedDataLen = 0; + uint8_t *receivedData = nullptr; - if (cookieInfo.state != COOKIE_READ_SENT) { - cookieInfo.state = COOKIE_UNUSED; - return; - } + if (cookieInfo.state != COOKIE_READ_SENT) { + cookieInfo.state = COOKIE_UNUSED; + return; + } - cookieInfo.state = COOKIE_UNUSED; + cookieInfo.state = COOKIE_UNUSED; - ReturnValue_t result = communicationInterface->readReceivedMessage( - comCookie, &receivedData, &receivedDataLen); + ReturnValue_t result = communicationInterface->readReceivedMessage( + comCookie, &receivedData, &receivedDataLen); - if (result != RETURN_OK) { - triggerEvent(DEVICE_REQUESTING_REPLY_FAILED, result); - //I think we can allow to ignore one missedReply. - ignoreMissedRepliesCount++; - return; - } + if (result != RETURN_OK) { + triggerEvent(DEVICE_REQUESTING_REPLY_FAILED, result); + //I think we can allow to ignore one missedReply. + ignoreMissedRepliesCount++; + return; + } - if (receivedDataLen == 0 or result == DeviceCommunicationIF::NO_REPLY_RECEIVED) - return; + if (receivedDataLen == 0 or result == DeviceCommunicationIF::NO_REPLY_RECEIVED) + return; - if (wiretappingMode == RAW) { - replyRawData(receivedData, receivedDataLen, requestedRawTraffic); - } + if (wiretappingMode == RAW) { + replyRawData(receivedData, receivedDataLen, requestedRawTraffic); + } - if (mode == MODE_RAW and defaultRawReceiver != MessageQueueIF::NO_QUEUE) { - replyRawReplyIfnotWiretapped(receivedData, receivedDataLen); - } - else { - parseReply(receivedData, receivedDataLen); - } + if (mode == MODE_RAW) { + if (defaultRawReceiver != MessageQueueIF::NO_QUEUE) { + replyRawReplyIfnotWiretapped(receivedData, receivedDataLen); + } + } + else { + parseReply(receivedData, receivedDataLen); + } } void DeviceHandlerBase::parseReply(const uint8_t* receivedData, - size_t receivedDataLen) { - ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; - DeviceCommandId_t foundId = 0xFFFFFFFF; - size_t foundLen = 0; - // The loop may not execute more often than the number of received bytes - // (worst case). This approach avoids infinite loops due to buggy - // scanForReply routines. - uint32_t remainingLength = receivedDataLen; - for (uint32_t count = 0; count < receivedDataLen; count++) { - result = scanForReply(receivedData, remainingLength, &foundId, - &foundLen); - switch (result) { - case RETURN_OK: - handleReply(receivedData, foundId, foundLen); - break; - case APERIODIC_REPLY: { - result = interpretDeviceReply(foundId, receivedData); - if (result != RETURN_OK) { - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, - foundId); - } - } - break; - case IGNORE_REPLY_DATA: - break; - case IGNORE_FULL_PACKET: - return; - default: - //We need to wait for timeout.. don't know what command failed and who sent it. - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_READING_REPLY_FAILED, result, foundLen); - break; - } - receivedData += foundLen; - if (remainingLength > foundLen) { - remainingLength -= foundLen; - } else { - return; - } - } + size_t receivedDataLen) { + ReturnValue_t result = HasReturnvaluesIF::RETURN_FAILED; + DeviceCommandId_t foundId = DeviceHandlerIF::NO_COMMAND_ID; + size_t foundLen = 0; + /* The loop may not execute more often than the number of received bytes + (worst case). This approach avoids infinite loops due to buggy scanForReply routines. */ + uint32_t remainingLength = receivedDataLen; + for (uint32_t count = 0; count < receivedDataLen; count++) { + result = scanForReply(receivedData, remainingLength, &foundId, &foundLen); + switch (result) { + case RETURN_OK: + handleReply(receivedData, foundId, foundLen); + if(foundLen == 0) { + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "parseReply", ObjectManagerIF::CHILD_INIT_FAILED, + "Found length is one, parsing might be stuck"); + } + break; + case APERIODIC_REPLY: { + result = interpretDeviceReply(foundId, receivedData); + if (result != RETURN_OK) { + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, + foundId); + } + if(foundLen == 0) { + printWarningOrError(sif::OutputTypes::OUT_ERROR, + "parseReply", ObjectManagerIF::CHILD_INIT_FAILED, + "Power switcher set but no valid object found."); +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "DeviceHandlerBase::parseReply: foundLen is 0!" + " Packet parsing will be stuck." << std::endl; +#endif + } + break; + } + case IGNORE_REPLY_DATA: + continue; + case IGNORE_FULL_PACKET: + return; + default: + // We need to wait for timeout.. don't know what command failed + // and who sent it. + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_READING_REPLY_FAILED, result, foundLen); + break; + } + receivedData += foundLen; + if (remainingLength > foundLen) { + remainingLength -= foundLen; + } else { + return; + } + } } void DeviceHandlerBase::handleReply(const uint8_t* receivedData, - DeviceCommandId_t foundId, uint32_t foundLen) { - ReturnValue_t result; - DeviceReplyMap::iterator iter = deviceReplyMap.find(foundId); + DeviceCommandId_t foundId, uint32_t foundLen) { + ReturnValue_t result; + DeviceReplyMap::iterator iter = deviceReplyMap.find(foundId); - if (iter == deviceReplyMap.end()) { - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_UNKNOWN_REPLY, foundId); - return; - } + if (iter == deviceReplyMap.end()) { + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_UNKNOWN_REPLY, foundId); + return; + } - DeviceReplyInfo *info = &(iter->second); + DeviceReplyInfo *info = &(iter->second); - if (info->delayCycles != 0) { + if (info->delayCycles != 0) { + result = interpretDeviceReply(foundId, receivedData); - if (info->periodic != false) { - info->delayCycles = info->maxDelayCycles; - } - else { - info->delayCycles = 0; - } + if(result == IGNORE_REPLY_DATA) { + return; + } - result = interpretDeviceReply(foundId, receivedData); + if (info->periodic) { + info->delayCycles = info->maxDelayCycles; + } + else { + info->delayCycles = 0; + } - if (result != RETURN_OK) { - // Report failed interpretation to FDIR. - replyRawReplyIfnotWiretapped(receivedData, foundLen); - triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, foundId); - } - replyToReply(iter, result); - } - else { - // Other completion failure messages are created by timeout. - // Powering down the device might take some time during which periodic - // replies may still come in. - if (mode != _MODE_WAIT_OFF) { - triggerEvent(DEVICE_UNREQUESTED_REPLY, foundId); - } - } + if (result != RETURN_OK) { + // Report failed interpretation to FDIR. + replyRawReplyIfnotWiretapped(receivedData, foundLen); + triggerEvent(DEVICE_INTERPRETING_REPLY_FAILED, result, foundId); + } + replyToReply(iter, result); + } + else { + /* Other completion failure messages are created by timeout. + Powering down the device might take some time during which periodic + replies may still come in. */ + if (mode != _MODE_WAIT_OFF) { + triggerEvent(DEVICE_UNREQUESTED_REPLY, foundId); + } + } } ReturnValue_t DeviceHandlerBase::getStorageData(store_address_t storageAddress, - uint8_t** data, uint32_t * len) { - size_t lenTmp; + uint8_t** data, uint32_t * len) { + size_t lenTmp; - if (IPCStore == nullptr) { - *data = nullptr; - *len = 0; - return RETURN_FAILED; - } - ReturnValue_t result = IPCStore->modifyData(storageAddress, data, &lenTmp); - if (result == RETURN_OK) { - *len = lenTmp; - return RETURN_OK; - } else { - triggerEvent(StorageManagerIF::GET_DATA_FAILED, result, - storageAddress.raw); - *data = nullptr; - *len = 0; - return result; - } + if (IPCStore == nullptr) { + *data = nullptr; + *len = 0; + return RETURN_FAILED; + } + ReturnValue_t result = IPCStore->modifyData(storageAddress, data, &lenTmp); + if (result == RETURN_OK) { + *len = lenTmp; + return RETURN_OK; + } else { + triggerEvent(StorageManagerIF::GET_DATA_FAILED, result, + storageAddress.raw); + *data = nullptr; + *len = 0; + return result; + } } void DeviceHandlerBase::replyRawData(const uint8_t *data, size_t len, - MessageQueueId_t sendTo, bool isCommand) { - if (IPCStore == nullptr or len == 0 or sendTo == MessageQueueIF::NO_QUEUE) { - return; - } - store_address_t address; - ReturnValue_t result = IPCStore->addData(&address, data, len); + MessageQueueId_t sendTo, bool isCommand) { + if (IPCStore == nullptr or len == 0 or sendTo == MessageQueueIF::NO_QUEUE) { + return; + } + store_address_t address; + ReturnValue_t result = IPCStore->addData(&address, data, len); - if (result != RETURN_OK) { - triggerEvent(StorageManagerIF::STORE_DATA_FAILED, result); - return; - } + if (result != RETURN_OK) { + triggerEvent(StorageManagerIF::STORE_DATA_FAILED, result); + return; + } - CommandMessage command; + CommandMessage command; - DeviceHandlerMessage::setDeviceHandlerRawReplyMessage(&command, - getObjectId(), address, isCommand); + DeviceHandlerMessage::setDeviceHandlerRawReplyMessage(&command, + getObjectId(), address, isCommand); - result = commandQueue->sendMessage(sendTo, &command); + result = commandQueue->sendMessage(sendTo, &command); - if (result != RETURN_OK) { - IPCStore->deleteData(address); - // Silently discard data, this indicates heavy TM traffic which - // should not be increased by additional events. - } + if (result != RETURN_OK) { + IPCStore->deleteData(address); + // Silently discard data, this indicates heavy TM traffic which + // should not be increased by additional events. + } } //Default child implementations -DeviceHandlerIF::CommunicationAction_t DeviceHandlerBase::getComAction() { - switch (pstStep) { - case 0: - return SEND_WRITE; - break; - case 1: - return GET_WRITE; - break; - case 2: - return SEND_READ; - break; - case 3: - return GET_READ; - break; - default: - break; - } - return NOTHING; +DeviceHandlerIF::CommunicationAction DeviceHandlerBase::getComAction() { + switch (pstStep) { + case 0: + return CommunicationAction::PERFORM_OPERATION; + break; + case 1: + return CommunicationAction::SEND_WRITE; + break; + case 2: + return CommunicationAction::GET_WRITE; + break; + case 3: + return CommunicationAction::SEND_READ; + break; + case 4: + return CommunicationAction::GET_READ; + break; + default: + break; + } + return CommunicationAction::NOTHING; } MessageQueueId_t DeviceHandlerBase::getCommandQueue() const { - return commandQueue->getId(); + return commandQueue->getId(); } void DeviceHandlerBase::buildRawDeviceCommand(CommandMessage* commandMessage) { - storedRawData = DeviceHandlerMessage::getStoreAddress(commandMessage); - ReturnValue_t result = getStorageData(storedRawData, &rawPacket, - &rawPacketLen); - if (result != RETURN_OK) { - replyReturnvalueToCommand(result, RAW_COMMAND_ID); - storedRawData.raw = StorageManagerIF::INVALID_ADDRESS; - } else { - cookieInfo.pendingCommand = deviceCommandMap.find( - (DeviceCommandId_t) RAW_COMMAND_ID); - cookieInfo.pendingCommand->second.isExecuting = true; - cookieInfo.state = COOKIE_WRITE_READY; - } + storedRawData = DeviceHandlerMessage::getStoreAddress(commandMessage); + ReturnValue_t result = getStorageData(storedRawData, &rawPacket, + &rawPacketLen); + if (result != RETURN_OK) { + replyReturnvalueToCommand(result, RAW_COMMAND_ID); + storedRawData.raw = StorageManagerIF::INVALID_ADDRESS; + } else { + cookieInfo.pendingCommand = deviceCommandMap.find( + (DeviceCommandId_t) RAW_COMMAND_ID); + cookieInfo.pendingCommand->second.isExecuting = true; + cookieInfo.state = COOKIE_WRITE_READY; + } } void DeviceHandlerBase::commandSwitch(ReturnValue_t onOff) { - if(powerSwitcher == nullptr) { - return; - } - const uint8_t *switches; - uint8_t numberOfSwitches = 0; - ReturnValue_t result = getSwitches(&switches, &numberOfSwitches); - if (result == RETURN_OK) { - while (numberOfSwitches > 0) { - powerSwitcher->sendSwitchCommand(switches[numberOfSwitches - 1], - onOff); - numberOfSwitches--; - } - } + if(powerSwitcher == nullptr) { + return; + } + const uint8_t *switches; + uint8_t numberOfSwitches = 0; + ReturnValue_t result = getSwitches(&switches, &numberOfSwitches); + if (result == RETURN_OK) { + while (numberOfSwitches > 0) { + powerSwitcher->sendSwitchCommand(switches[numberOfSwitches - 1], + onOff); + numberOfSwitches--; + } + } } ReturnValue_t DeviceHandlerBase::getSwitches(const uint8_t **switches, - uint8_t *numberOfSwitches) { - return DeviceHandlerBase::NO_SWITCH; + uint8_t *numberOfSwitches) { + return DeviceHandlerBase::NO_SWITCH; } void DeviceHandlerBase::modeChanged(void) { } ReturnValue_t DeviceHandlerBase::enableReplyInReplyMap( - DeviceCommandMap::iterator command, uint8_t expectedReplies, - bool useAlternativeId, DeviceCommandId_t alternativeReply) { - DeviceReplyMap::iterator iter; - if (useAlternativeId) { - iter = deviceReplyMap.find(alternativeReply); - } else { - iter = deviceReplyMap.find(command->first); - } - if (iter != deviceReplyMap.end()) { - DeviceReplyInfo *info = &(iter->second); - info->delayCycles = info->maxDelayCycles; - info->command = command; - command->second.expectedReplies = expectedReplies; - return RETURN_OK; - } else { - return NO_REPLY_EXPECTED; - } + DeviceCommandMap::iterator command, uint8_t expectedReplies, + bool useAlternativeId, DeviceCommandId_t alternativeReply) { + DeviceReplyMap::iterator iter; + if (useAlternativeId) { + iter = deviceReplyMap.find(alternativeReply); + } else { + iter = deviceReplyMap.find(command->first); + } + if (iter != deviceReplyMap.end()) { + DeviceReplyInfo *info = &(iter->second); + info->delayCycles = info->maxDelayCycles; + info->command = command; + command->second.expectedReplies = expectedReplies; + return RETURN_OK; + } else { + return NO_REPLY_EXPECTED; + } } void DeviceHandlerBase::doTransition(Mode_t modeFrom, Submode_t subModeFrom) { - setMode(getBaseMode(mode)); -} - -uint32_t DeviceHandlerBase::getTransitionDelayMs(Mode_t modeFrom, - Mode_t modeTo) { - return 0; + setMode(getBaseMode(mode)); } ReturnValue_t DeviceHandlerBase::getStateOfSwitches(void) { - if(powerSwitcher == nullptr) { - return NO_SWITCH; - } - uint8_t numberOfSwitches = 0; - const uint8_t *switches; + if(powerSwitcher == nullptr) { + return NO_SWITCH; + } + uint8_t numberOfSwitches = 0; + const uint8_t *switches; - ReturnValue_t result = getSwitches(&switches, &numberOfSwitches); - if ((result == RETURN_OK) && (numberOfSwitches != 0)) { - while (numberOfSwitches > 0) { - if (powerSwitcher->getSwitchState(switches[numberOfSwitches - 1]) - == PowerSwitchIF::SWITCH_OFF) { - return PowerSwitchIF::SWITCH_OFF; - } - numberOfSwitches--; - } - return PowerSwitchIF::SWITCH_ON; - } + ReturnValue_t result = getSwitches(&switches, &numberOfSwitches); + if ((result == RETURN_OK) && (numberOfSwitches != 0)) { + while (numberOfSwitches > 0) { + if (powerSwitcher->getSwitchState(switches[numberOfSwitches - 1]) + == PowerSwitchIF::SWITCH_OFF) { + return PowerSwitchIF::SWITCH_OFF; + } + numberOfSwitches--; + } + return PowerSwitchIF::SWITCH_ON; + } - return NO_SWITCH; + return NO_SWITCH; } Mode_t DeviceHandlerBase::getBaseMode(Mode_t transitionMode) { -//only child action special modes are handled, as a child should never see any base action modes - if (transitionMode == _MODE_START_UP) { - return _MODE_TO_ON; - } - if (transitionMode == _MODE_SHUT_DOWN) { - return _MODE_POWER_DOWN; - } - return transitionMode - & ~(TRANSITION_MODE_BASE_ACTION_MASK - | TRANSITION_MODE_CHILD_ACTION_MASK); + // only child action special modes are handled, as a child should + // never see any base action modes + if (transitionMode == _MODE_START_UP) { + return _MODE_TO_ON; + } + if (transitionMode == _MODE_SHUT_DOWN) { + return _MODE_POWER_DOWN; + } + return transitionMode + & ~(TRANSITION_MODE_BASE_ACTION_MASK + | TRANSITION_MODE_CHILD_ACTION_MASK); } //SHOULDDO: Allow transition from OFF to NORMAL to reduce complexity in assemblies. And, by the way, throw away DHB and write a new one: @@ -913,119 +994,127 @@ Mode_t DeviceHandlerBase::getBaseMode(Mode_t transitionMode) { // - Don't use modes for state transitions, reduce FSM (Finte State Machine) complexity. // - Modularization? ReturnValue_t DeviceHandlerBase::checkModeCommand(Mode_t commandedMode, - Submode_t commandedSubmode, uint32_t* msToReachTheMode) { - if (isTransitionalMode()) { - return IN_TRANSITION; - } - if ((mode == MODE_ERROR_ON) && (commandedMode != MODE_OFF)) { - return TRANS_NOT_ALLOWED; - } - if ((commandedMode == MODE_NORMAL) && (mode == MODE_OFF)) { - return TRANS_NOT_ALLOWED; - } + Submode_t commandedSubmode, uint32_t* msToReachTheMode) { + if (isTransitionalMode()) { + return IN_TRANSITION; + } + if ((mode == MODE_ERROR_ON) && (commandedMode != MODE_OFF)) { + return TRANS_NOT_ALLOWED; + } + if ((commandedMode == MODE_NORMAL) && (mode == MODE_OFF)) { + return TRANS_NOT_ALLOWED; + } - if ((commandedMode == MODE_ON) && (mode == MODE_OFF) - && (deviceThermalStatePoolId != PoolVariableIF::NO_PARAMETER)) { - DataSet mySet; - db_int8_t thermalState(deviceThermalStatePoolId, &mySet, - PoolVariableIF::VAR_READ); - db_int8_t thermalRequest(deviceThermalRequestPoolId, &mySet, - PoolVariableIF::VAR_READ); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - if (!ThermalComponentIF::isOperational(thermalState)) { - triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, - thermalState); - return NON_OP_TEMPERATURE; - } - } - } + if ((commandedMode == MODE_ON) && (mode == MODE_OFF) + and (thermalSet != nullptr)) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if((thermalSet->heaterRequest.value != + ThermalComponentIF::STATE_REQUEST_IGNORE) and (not + ThermalComponentIF::isOperational( + thermalSet->thermalState.value))) { + triggerEvent(ThermalComponentIF::TEMP_NOT_IN_OP_RANGE, + thermalSet->thermalState.value); + return NON_OP_TEMPERATURE; + } + } + } - return isModeCombinationValid(commandedMode, commandedSubmode); + return isModeCombinationValid(commandedMode, commandedSubmode); } void DeviceHandlerBase::startTransition(Mode_t commandedMode, - Submode_t commandedSubmode) { - switch (commandedMode) { - case MODE_ON: - if (mode == MODE_OFF) { - transitionSourceMode = _MODE_POWER_DOWN; - transitionSourceSubMode = SUBMODE_NONE; - setMode(_MODE_POWER_ON, commandedSubmode); - //already set the delay for the child transition so we don't need to call it twice - childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, - MODE_ON); - triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - DataSet mySet; - db_int8_t thermalRequest(deviceThermalRequestPoolId, - &mySet, PoolVariableIF::VAR_READ_WRITE); - mySet.read(); - if (thermalRequest != ThermalComponentIF::STATE_REQUEST_IGNORE) { - thermalRequest = ThermalComponentIF::STATE_REQUEST_OPERATIONAL; - mySet.commit(PoolVariableIF::VALID); - } - } else { - setTransition(MODE_ON, commandedSubmode); - } - break; - case MODE_OFF: - if (mode == MODE_OFF) { - triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - setMode(_MODE_POWER_DOWN, commandedSubmode); - } else { - //already set the delay for the child transition so we don't need to call it twice - childTransitionDelay = getTransitionDelayMs(mode, _MODE_POWER_DOWN); - transitionSourceMode = _MODE_POWER_DOWN; - transitionSourceSubMode = commandedSubmode; - childTransitionFailure = CHILD_TIMEOUT; - setMode(_MODE_SHUT_DOWN, commandedSubmode); - triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); - } - break; - case MODE_RAW: - if (mode != MODE_OFF) { - setTransition(MODE_RAW, commandedSubmode); - } else { - setMode(MODE_RAW, commandedSubmode); - } - break; - case MODE_NORMAL: - if (mode != MODE_OFF) { - setTransition(MODE_NORMAL, commandedSubmode); - } else { - replyReturnvalueToCommand(HasModesIF::TRANS_NOT_ALLOWED); - } - break; - } + Submode_t commandedSubmode) { + switch (commandedMode) { + case MODE_ON: + handleTransitionToOnMode(commandedMode, commandedSubmode); + break; + case MODE_OFF: + if (mode == MODE_OFF) { + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); + setMode(_MODE_POWER_DOWN, commandedSubmode); + } else { + // already set the delay for the child transition + // so we don't need to call it twice + childTransitionDelay = getTransitionDelayMs(mode, _MODE_POWER_DOWN); + transitionSourceMode = _MODE_POWER_DOWN; + transitionSourceSubMode = commandedSubmode; + childTransitionFailure = CHILD_TIMEOUT; + setMode(_MODE_SHUT_DOWN, commandedSubmode); + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); + } + break; + case MODE_RAW: + if (mode != MODE_OFF) { + setTransition(MODE_RAW, commandedSubmode); + } else { + setMode(MODE_RAW, commandedSubmode); + } + break; + case MODE_NORMAL: + if (mode != MODE_OFF) { + setTransition(MODE_NORMAL, commandedSubmode); + } else { + replyReturnvalueToCommand(HasModesIF::TRANS_NOT_ALLOWED); + } + break; + } +} + +void DeviceHandlerBase::handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode) { + if (mode == MODE_OFF) { + transitionSourceMode = _MODE_POWER_DOWN; + transitionSourceSubMode = SUBMODE_NONE; + setMode(_MODE_POWER_ON, commandedSubmode); + // already set the delay for the child transition so we don't + // need to call it twice + childTransitionDelay = getTransitionDelayMs(_MODE_START_UP, + MODE_ON); + triggerEvent(CHANGING_MODE, commandedMode, commandedSubmode); + if(thermalSet != nullptr) { + ReturnValue_t result = thermalSet->read(); + if(result == HasReturnvaluesIF::RETURN_OK) { + if(thermalSet->heaterRequest != + ThermalComponentIF::STATE_REQUEST_IGNORE) { + thermalSet->heaterRequest = + ThermalComponentIF::STATE_REQUEST_OPERATIONAL; + thermalSet->commit(); + } + } + } + } else { + setTransition(MODE_ON, commandedSubmode); + } } void DeviceHandlerBase::getMode(Mode_t* mode, Submode_t* submode) { - *mode = this->mode; - *submode = this->submode; + *mode = this->mode; + *submode = this->submode; } void DeviceHandlerBase::setToExternalControl() { - healthHelper.setHealth(EXTERNAL_CONTROL); + healthHelper.setHealth(EXTERNAL_CONTROL); } void DeviceHandlerBase::announceMode(bool recursive) { - triggerEvent(MODE_INFO, mode, submode); + triggerEvent(MODE_INFO, mode, submode); } bool DeviceHandlerBase::dontCheckQueue() { - return false; + return false; } void DeviceHandlerBase::missedReply(DeviceCommandId_t id) { - if (ignoreMissedRepliesCount > 0) { - ignoreMissedRepliesCount--; - } else { - triggerEvent(DEVICE_MISSED_REPLY, id); - } + if (ignoreMissedRepliesCount > 0) { + ignoreMissedRepliesCount--; + } else { + triggerEvent(DEVICE_MISSED_REPLY, id); + } } HasHealthIF::HealthState DeviceHandlerBase::getHealth() { - return healthHelper.getHealth(); + return healthHelper.getHealth(); } ReturnValue_t DeviceHandlerBase::setHealth(HealthState health) { @@ -1034,318 +1123,427 @@ ReturnValue_t DeviceHandlerBase::setHealth(HealthState health) { } void DeviceHandlerBase::checkSwitchState() { - if ((mode == MODE_ON || mode == MODE_NORMAL)) { - //We only check in ON and NORMAL, ignore RAW and ERROR_ON. - ReturnValue_t result = getStateOfSwitches(); - if (result == PowerSwitchIF::SWITCH_OFF && !switchOffWasReported) { - triggerEvent(PowerSwitchIF::SWITCH_WENT_OFF); - switchOffWasReported = true; - } - } else { - switchOffWasReported = false; - } + if ((mode == MODE_ON || mode == MODE_NORMAL)) { + //We only check in ON and NORMAL, ignore RAW and ERROR_ON. + ReturnValue_t result = getStateOfSwitches(); + if (result == PowerSwitchIF::SWITCH_OFF && !switchOffWasReported) { + triggerEvent(PowerSwitchIF::SWITCH_WENT_OFF); + switchOffWasReported = true; + } + } else { + switchOffWasReported = false; + } } void DeviceHandlerBase::doOnActivity() { } ReturnValue_t DeviceHandlerBase::acceptExternalDeviceCommands() { - if ((mode != MODE_ON) && (mode != MODE_NORMAL)) { - return WRONG_MODE_FOR_COMMAND; - } - return RETURN_OK; + if ((mode != MODE_ON) && (mode != MODE_NORMAL)) { + return WRONG_MODE_FOR_COMMAND; + } + return RETURN_OK; } void DeviceHandlerBase::replyRawReplyIfnotWiretapped(const uint8_t* data, - size_t len) { - if ((wiretappingMode == RAW) - && (defaultRawReceiver == requestedRawTraffic)) { - //The raw packet was already sent by the wiretapping service - } else { - replyRawData(data, len, defaultRawReceiver); - } + size_t len) { + if ((wiretappingMode == RAW) + && (defaultRawReceiver == requestedRawTraffic)) { + //The raw packet was already sent by the wiretapping service + } else { + replyRawData(data, len, defaultRawReceiver); + } } ReturnValue_t DeviceHandlerBase::handleDeviceHandlerMessage( - CommandMessage * message) { - switch (message->getCommand()) { - case DeviceHandlerMessage::CMD_WIRETAPPING: - switch (DeviceHandlerMessage::getWiretappingMode(message)) { - case RAW: - wiretappingMode = RAW; - requestedRawTraffic = commandQueue->getLastPartner(); - break; - case TM: - wiretappingMode = TM; - requestedRawTraffic = commandQueue->getLastPartner(); - break; - case OFF: - wiretappingMode = OFF; - break; - default: - replyReturnvalueToCommand(INVALID_COMMAND_PARAMETER); - wiretappingMode = OFF; - return RETURN_OK; - } - replyReturnvalueToCommand(RETURN_OK); - return RETURN_OK; -// case DeviceHandlerMessage::CMD_SWITCH_IOBOARD: -// if (mode != MODE_OFF) { -// replyReturnvalueToCommand(WRONG_MODE_FOR_COMMAND); -// } else { -// result = switchCookieChannel( -// DeviceHandlerMessage::getIoBoardObjectId(message)); -// if (result == RETURN_OK) { -// replyReturnvalueToCommand(RETURN_OK); -// } else { -// replyReturnvalueToCommand(CANT_SWITCH_IO_ADDRESS); -// } -// } -// return RETURN_OK; - case DeviceHandlerMessage::CMD_RAW: - if ((mode != MODE_RAW)) { - DeviceHandlerMessage::clear(message); - replyReturnvalueToCommand(WRONG_MODE_FOR_COMMAND); - } else { - buildRawDeviceCommand(message); - } - return RETURN_OK; - default: - return RETURN_FAILED; - } + CommandMessage * message) { + switch (message->getCommand()) { + case DeviceHandlerMessage::CMD_WIRETAPPING: + switch (DeviceHandlerMessage::getWiretappingMode(message)) { + case RAW: + wiretappingMode = RAW; + requestedRawTraffic = commandQueue->getLastPartner(); + break; + case TM: + wiretappingMode = TM; + requestedRawTraffic = commandQueue->getLastPartner(); + break; + case OFF: + wiretappingMode = OFF; + break; + default: + replyReturnvalueToCommand(INVALID_COMMAND_PARAMETER); + wiretappingMode = OFF; + return RETURN_OK; + } + replyReturnvalueToCommand(RETURN_OK); + return RETURN_OK; + case DeviceHandlerMessage::CMD_RAW: + if ((mode != MODE_RAW)) { + DeviceHandlerMessage::clear(message); + replyReturnvalueToCommand(WRONG_MODE_FOR_COMMAND); + } else { + buildRawDeviceCommand(message); + } + return RETURN_OK; + default: + return RETURN_FAILED; + } } void DeviceHandlerBase::setParentQueue(MessageQueueId_t parentQueueId) { - modeHelper.setParentQueue(parentQueueId); - healthHelper.setParentQueue(parentQueueId); + modeHelper.setParentQueue(parentQueueId); + healthHelper.setParentQueue(parentQueueId); } bool DeviceHandlerBase::isAwaitingReply() { - std::map::iterator iter; - for (iter = deviceReplyMap.begin(); iter != deviceReplyMap.end(); ++iter) { - if (iter->second.delayCycles != 0) { - return true; - } - } - return false; + std::map::iterator iter; + for (iter = deviceReplyMap.begin(); iter != deviceReplyMap.end(); ++iter) { + if (iter->second.delayCycles != 0) { + return true; + } + } + return false; } ReturnValue_t DeviceHandlerBase::letChildHandleMessage( - CommandMessage * message) { - return RETURN_FAILED; + CommandMessage * message) { + return RETURN_FAILED; } -void DeviceHandlerBase::handleDeviceTM(SerializeIF* data, - DeviceCommandId_t replyId, bool neverInDataPool, bool forceDirectTm) { - DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId); - if (iter == deviceReplyMap.end()) { - triggerEvent(DEVICE_UNKNOWN_REPLY, replyId); - return; - } - DeviceTmReportingWrapper wrapper(getObjectId(), replyId, data); - //replies to a command - if (iter->second.command != deviceCommandMap.end()) - { - MessageQueueId_t queueId = iter->second.command->second.sendReplyTo; +void DeviceHandlerBase::handleDeviceTM(SerializeIF *dataSet, DeviceCommandId_t replyId, + bool forceDirectTm) { + if(dataSet == nullptr) { + return; + } - if (queueId != NO_COMMANDER) { - //This may fail, but we'll ignore the fault. - actionHelper.reportData(queueId, replyId, data); - } + DeviceReplyMap::iterator iter = deviceReplyMap.find(replyId); + if (iter == deviceReplyMap.end()) { + triggerEvent(DEVICE_UNKNOWN_REPLY, replyId); + return; + } - //This check should make sure we get any TM but don't get anything doubled. - if (wiretappingMode == TM && (requestedRawTraffic != queueId)) { - actionHelper.reportData(requestedRawTraffic, replyId, &wrapper); - } - else if (forceDirectTm and (defaultRawReceiver != queueId) and - (defaultRawReceiver != MessageQueueIF::NO_QUEUE)) - { - // hiding of sender needed so the service will handle it as - // unexpected Data, no matter what state (progress or completed) - // it is in - actionHelper.reportData(defaultRawReceiver, replyId, &wrapper, - true); - } - } - //unrequested/aperiodic replies - else - { - if (wiretappingMode == TM) { - actionHelper.reportData(requestedRawTraffic, replyId, &wrapper); - } - else if (forceDirectTm and defaultRawReceiver != - MessageQueueIF::NO_QUEUE) - { - // hiding of sender needed so the service will handle it as - // unexpected Data, no matter what state (progress or completed) - // it is in - actionHelper.reportData(defaultRawReceiver, replyId, &wrapper, - true); - } - } - //Try to cast to GlobDataSet and commit data. - if (!neverInDataPool) { - DataSet* dataSet = dynamic_cast(data); - if (dataSet != NULL) { - dataSet->commit(PoolVariableIF::VALID); - } - } + /* Regular replies to a command */ + if (iter->second.command != deviceCommandMap.end()) + { + MessageQueueId_t queueId = iter->second.command->second.sendReplyTo; + + if (queueId != NO_COMMANDER) { + /* This may fail, but we'll ignore the fault. */ + actionHelper.reportData(queueId, replyId, dataSet); + } + + /* This check should make sure we get any TM but don't get anything doubled. */ + if (wiretappingMode == TM && (requestedRawTraffic != queueId)) { + DeviceTmReportingWrapper wrapper(getObjectId(), replyId, dataSet); + actionHelper.reportData(requestedRawTraffic, replyId, &wrapper); + } + + else if (forceDirectTm and (defaultRawReceiver != queueId) and + (defaultRawReceiver != MessageQueueIF::NO_QUEUE)) + { + // hiding of sender needed so the service will handle it as + // unexpected Data, no matter what state (progress or completed) + // it is in + actionHelper.reportData(defaultRawReceiver, replyId, dataSet, true); + } + } + /* Unrequested or aperiodic replies */ + else + { + DeviceTmReportingWrapper wrapper(getObjectId(), replyId, dataSet); + if (wiretappingMode == TM) { + actionHelper.reportData(requestedRawTraffic, replyId, &wrapper); + } + if (forceDirectTm and defaultRawReceiver != MessageQueueIF::NO_QUEUE) + { + // sid_t setSid = sid_t(this->getObjectId(), replyId); + // LocalPoolDataSetBase* dataset = getDataSetHandle(setSid); + // if(dataset != nullptr) { + // poolManager.generateHousekeepingPacket(setSid, dataset, true); + // } + + // hiding of sender needed so the service will handle it as + // unexpected Data, no matter what state (progress or completed) + // it is in + actionHelper.reportData(defaultRawReceiver, replyId, &wrapper, true); + } + } } ReturnValue_t DeviceHandlerBase::executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { - ReturnValue_t result = acceptExternalDeviceCommands(); - if (result != HasReturnvaluesIF::RETURN_OK) { - return result; - } - DeviceCommandMap::iterator iter = deviceCommandMap.find(actionId); - if (iter == deviceCommandMap.end()) { - result = COMMAND_NOT_SUPPORTED; - } else if (iter->second.isExecuting) { - result = COMMAND_ALREADY_SENT; - } else { - result = buildCommandFromCommand(actionId, data, size); - } - if (result == RETURN_OK) { - iter->second.sendReplyTo = commandedBy; - iter->second.isExecuting = true; - cookieInfo.pendingCommand = iter; - cookieInfo.state = COOKIE_WRITE_READY; - } - return result; + MessageQueueId_t commandedBy, const uint8_t* data, size_t size) { + ReturnValue_t result = acceptExternalDeviceCommands(); + if (result != HasReturnvaluesIF::RETURN_OK) { + return result; + } + DeviceCommandMap::iterator iter = deviceCommandMap.find(actionId); + if (iter == deviceCommandMap.end()) { + result = COMMAND_NOT_SUPPORTED; + } else if (iter->second.isExecuting) { + result = COMMAND_ALREADY_SENT; + } else { + result = buildCommandFromCommand(actionId, data, size); + } + if (result == RETURN_OK) { + iter->second.sendReplyTo = commandedBy; + iter->second.isExecuting = true; + cookieInfo.pendingCommand = iter; + cookieInfo.state = COOKIE_WRITE_READY; + } + return result; } void DeviceHandlerBase::buildInternalCommand(void) { -//Neither Raw nor Direct could build a command - ReturnValue_t result = NOTHING_TO_SEND; - DeviceCommandId_t deviceCommandId = NO_COMMAND_ID; - if (mode == MODE_NORMAL) { - result = buildNormalDeviceCommand(&deviceCommandId); - if (result == BUSY) { - //so we can track misconfigurations - sif::debug << std::hex << getObjectId() - << ": DHB::buildInternalCommand: Busy" << std::dec << std::endl; - result = NOTHING_TO_SEND; //no need to report this - } - } - else if (mode == MODE_RAW) { - result = buildChildRawCommand(); - deviceCommandId = RAW_COMMAND_ID; - } - else if (mode & TRANSITION_MODE_CHILD_ACTION_MASK) { - result = buildTransitionDeviceCommand(&deviceCommandId); - } - else { - return; - } + /* Neither raw nor direct could build a command */ + ReturnValue_t result = NOTHING_TO_SEND; + DeviceCommandId_t deviceCommandId = NO_COMMAND_ID; + if (mode == MODE_NORMAL) { + result = buildNormalDeviceCommand(&deviceCommandId); + if (result == BUSY) { + /* So we can track misconfigurations */ + printWarningOrError(sif::OutputTypes::OUT_WARNING, "buildInternalCommand", + HasReturnvaluesIF::RETURN_FAILED, "Busy."); + /* No need to report this */ + result = NOTHING_TO_SEND; + } + } + else if (mode == MODE_RAW) { + result = buildChildRawCommand(); + deviceCommandId = RAW_COMMAND_ID; + } + else if (mode & TRANSITION_MODE_CHILD_ACTION_MASK) { + result = buildTransitionDeviceCommand(&deviceCommandId); + } + else { + return; + } - if (result == NOTHING_TO_SEND) { - return; - } - if (result == RETURN_OK) { - DeviceCommandMap::iterator iter = deviceCommandMap.find( - deviceCommandId); - if (iter == deviceCommandMap.end()) { - result = COMMAND_NOT_SUPPORTED; - } else if (iter->second.isExecuting) { - sif::debug << std::hex << getObjectId() - << ": DHB::buildInternalCommand: Command " - << deviceCommandId << " isExecuting" << std::endl; //so we can track misconfigurations - return; //this is an internal command, no need to report a failure here, missed reply will track if a reply is too late, otherwise, it's ok - } else { - iter->second.sendReplyTo = NO_COMMANDER; - iter->second.isExecuting = true; - cookieInfo.pendingCommand = iter; - cookieInfo.state = COOKIE_WRITE_READY; - } - } - if (result != RETURN_OK) { - triggerEvent(DEVICE_BUILDING_COMMAND_FAILED, result, deviceCommandId); - } + if (result == NOTHING_TO_SEND) { + return; + } + if (result == RETURN_OK) { + DeviceCommandMap::iterator iter = deviceCommandMap.find( + deviceCommandId); + if (iter == deviceCommandMap.end()) { + result = COMMAND_NOT_SUPPORTED; + } + else if (iter->second.isExecuting) { +#if FSFW_DISABLE_PRINTOUT == 0 + char output[36]; + sprintf(output, "Command 0x%08x is executing", + static_cast(deviceCommandId)); + // so we can track misconfigurations + printWarningOrError(sif::OutputTypes::OUT_WARNING, + "buildInternalCommand", + HasReturnvaluesIF::RETURN_FAILED, + output); +#endif + // this is an internal command, no need to report a failure here, + // missed reply will track if a reply is too late, otherwise, it's ok + return; + } else { + iter->second.sendReplyTo = NO_COMMANDER; + iter->second.isExecuting = true; + cookieInfo.pendingCommand = iter; + cookieInfo.state = COOKIE_WRITE_READY; + } + } + if (result != RETURN_OK) { + triggerEvent(DEVICE_BUILDING_COMMAND_FAILED, result, deviceCommandId); + } } ReturnValue_t DeviceHandlerBase::buildChildRawCommand() { - return NOTHING_TO_SEND; + return NOTHING_TO_SEND; } uint8_t DeviceHandlerBase::getReplyDelayCycles( - DeviceCommandId_t deviceCommand) { - DeviceReplyMap::iterator iter = deviceReplyMap.find(deviceCommand); - if (iter == deviceReplyMap.end()) { - return 0; - } - return iter->second.delayCycles; + DeviceCommandId_t deviceCommand) { + DeviceReplyMap::iterator iter = deviceReplyMap.find(deviceCommand); + if (iter == deviceReplyMap.end()) { + return 0; + } + return iter->second.delayCycles; } Mode_t DeviceHandlerBase::getTransitionSourceMode() const { - return transitionSourceMode; + return transitionSourceMode; } Submode_t DeviceHandlerBase::getTransitionSourceSubMode() const { - return transitionSourceSubMode; + return transitionSourceSubMode; } void DeviceHandlerBase::triggerEvent(Event event, uint32_t parameter1, - uint32_t parameter2) { - fdirInstance->triggerEvent(event, parameter1, parameter2); + uint32_t parameter2) { + fdirInstance->triggerEvent(event, parameter1, parameter2); } void DeviceHandlerBase::forwardEvent(Event event, uint32_t parameter1, - uint32_t parameter2) const { - fdirInstance->triggerEvent(event, parameter1, parameter2); + uint32_t parameter2) const { + fdirInstance->triggerEvent(event, parameter1, parameter2); } void DeviceHandlerBase::doOffActivity() { } -ReturnValue_t DeviceHandlerBase::getParameter(uint8_t domainId, - uint16_t parameterId, ParameterWrapper* parameterWrapper, - const ParameterWrapper* newValues, uint16_t startAtIndex) { - ReturnValue_t result = fdirInstance->getParameter(domainId, parameterId, - parameterWrapper, newValues, startAtIndex); - if (result != INVALID_DOMAIN_ID) { - return result; - } - return INVALID_DOMAIN_ID; +ReturnValue_t DeviceHandlerBase::getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, + uint16_t startAtIndex) { + ReturnValue_t result = fdirInstance->getParameter(domainId, uniqueId, parameterWrapper, + newValues, startAtIndex); + if (result != INVALID_DOMAIN_ID) { + return result; + } + return INVALID_DOMAIN_ID; } bool DeviceHandlerBase::isTransitionalMode() { - return ((mode - & (TRANSITION_MODE_BASE_ACTION_MASK - | TRANSITION_MODE_CHILD_ACTION_MASK)) != 0); + return ((mode + & (TRANSITION_MODE_BASE_ACTION_MASK + | TRANSITION_MODE_CHILD_ACTION_MASK)) != 0); } bool DeviceHandlerBase::commandIsExecuting(DeviceCommandId_t commandId) { - auto iter = deviceCommandMap.find(commandId); - if (iter != deviceCommandMap.end()) { - return iter->second.isExecuting; - } else { - return false; - } + auto iter = deviceCommandMap.find(commandId); + if (iter != deviceCommandMap.end()) { + return iter->second.isExecuting; + } else { + return false; + } } -void DeviceHandlerBase::changeHK(Mode_t mode, Submode_t submode, bool enable) { +void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task){ + executingTask = task; } -void DeviceHandlerBase::setTaskIF(PeriodicTaskIF* task_){ - executingTask = task_; -} - -// Default implementations empty. void DeviceHandlerBase::debugInterface(uint8_t positionTracker, - object_id_t objectId, uint32_t parameter) {} + object_id_t objectId, uint32_t parameter) {} void DeviceHandlerBase::performOperationHook() { } +ReturnValue_t DeviceHandlerBase::initializeLocalDataPool( + localpool::DataPool &localDataPoolMap, + LocalDataPoolManager& poolManager) { + if(thermalSet != nullptr) { + localDataPoolMap.emplace(thermalSet->thermalStatePoolId, + new PoolEntry); + localDataPoolMap.emplace(thermalSet->heaterRequestPoolId, + new PoolEntry); + } + return RETURN_OK; +} + ReturnValue_t DeviceHandlerBase::initializeAfterTaskCreation() { // In this function, the task handle should be valid if the task // was implemented correctly. We still check to be 1000 % sure :-) if(executingTask != nullptr) { pstIntervalMs = executingTask->getPeriodMs(); } + this->poolManager.initializeAfterTaskCreation(); + + if(setStartupImmediately) { + startTransition(MODE_ON, SUBMODE_NONE); + } return HasReturnvaluesIF::RETURN_OK; } +LocalPoolDataSetBase* DeviceHandlerBase::getDataSetHandle(sid_t sid) { + auto iter = deviceReplyMap.find(sid.ownerSetId); + if(iter != deviceReplyMap.end()) { + return iter->second.dataSet; + } + else { + return nullptr; + } +} + +object_id_t DeviceHandlerBase::getObjectId() const { + return SystemObject::getObjectId(); +} + +void DeviceHandlerBase::setStartUpImmediately() { + this->setStartupImmediately = true; +} + +dur_millis_t DeviceHandlerBase::getPeriodicOperationFrequency() const { + return pstIntervalMs; +} + +DeviceCommandId_t DeviceHandlerBase::getPendingCommand() const { + if(cookieInfo.pendingCommand != deviceCommandMap.end()) { + return cookieInfo.pendingCommand->first; + } + return DeviceHandlerIF::NO_COMMAND_ID; +} + +void DeviceHandlerBase::setNormalDatapoolEntriesInvalid() { + for(const auto& reply: deviceReplyMap) { + if(reply.second.dataSet != nullptr) { + reply.second.dataSet->setValidity(false, true); + } + } +} + +void DeviceHandlerBase::printWarningOrError(sif::OutputTypes errorType, + const char *functionName, ReturnValue_t errorCode, + const char *errorPrint) { + if(errorPrint == nullptr) { + if(errorCode == ObjectManagerIF::CHILD_INIT_FAILED) { + errorPrint = "Initialization error"; + } + else if(errorCode == HasReturnvaluesIF::RETURN_FAILED) { + if(errorType == sif::OutputTypes::OUT_WARNING) { + errorPrint = "Generic Warning"; + } + else { + errorPrint = "Generic Error"; + } + } + else { + errorPrint = "Unknown error"; + } + } + if(functionName == nullptr) { + functionName = "unknown function"; + } + + if(errorType == sif::OutputTypes::OUT_WARNING) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "DeviceHandlerBase::" << functionName << ": Object ID 0x" << std::hex << + std::setw(8) << std::setfill('0') << this->getObjectId() << " | " << errorPrint << + std::dec << std::setfill(' ') << std::endl; +#else + sif::printWarning("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n", + functionName, this->getObjectId(), errorPrint); +#endif + } + else if(errorType == sif::OutputTypes::OUT_ERROR) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DeviceHandlerBase::" << functionName << ": Object ID 0x" + << std::hex << std::setw(8) << std::setfill('0') + << this->getObjectId() << " | " << errorPrint << std::dec + << std::setfill(' ') << std::endl; +#else + sif::printError("DeviceHandlerBase::%s: Object ID 0x%08x | %s\n", + functionName, this->getObjectId(), errorPrint); +#endif + } + +} + +LocalDataPoolManager* DeviceHandlerBase::getHkManagerHandle() { + return &poolManager; +} + +MessageQueueId_t DeviceHandlerBase::getCommanderId(DeviceCommandId_t replyId) const { + auto commandIter = deviceCommandMap.find(replyId); + if(commandIter == deviceCommandMap.end()) { + return MessageQueueIF::NO_QUEUE; + } + return commandIter->second.sendReplyTo; +} diff --git a/devicehandlers/DeviceHandlerBase.h b/devicehandlers/DeviceHandlerBase.h index 5666f35ea..496c08ffd 100644 --- a/devicehandlers/DeviceHandlerBase.h +++ b/devicehandlers/DeviceHandlerBase.h @@ -1,12 +1,14 @@ -#ifndef FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ -#define FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ #include "DeviceHandlerIF.h" #include "DeviceCommunicationIF.h" #include "DeviceHandlerFailureIsolation.h" +#include "DeviceHandlerThermalSet.h" +#include "../serviceinterface/ServiceInterface.h" +#include "../serviceinterface/serviceInterfaceDefintions.h" #include "../objectmanager/SystemObject.h" -#include "../tasks/PeriodicTaskIF.h" #include "../tasks/ExecutableObjectIF.h" #include "../returnvalues/HasReturnvaluesIF.h" #include "../action/HasActionsIF.h" @@ -14,10 +16,12 @@ #include "../modes/HasModesIF.h" #include "../power/PowerSwitchIF.h" #include "../ipc/MessageQueueIF.h" +#include "../tasks/PeriodicTaskIF.h" #include "../action/ActionHelper.h" #include "../health/HealthHelper.h" #include "../parameters/ParameterHelper.h" -#include "../datapool/HkSwitchHelper.h" +#include "../datapoollocal/HasLocalDataPoolIF.h" +#include "../datapoollocal/LocalDataPoolManager.h" #include @@ -38,17 +42,16 @@ class StorageManagerIF; * Documentation: Dissertation Baetz p.138,139, p.141-149 * * It features handling of @link DeviceHandlerIF::Mode_t Modes @endlink, - * communication with physical devices, using the @link DeviceCommunicationIF @endlink, - * and communication with commanding objects. - * It inherits SystemObject and thus can be created by the ObjectManagerIF. + * communication with physical devices, using the + * @link DeviceCommunicationIF @endlink, and communication with commanding + * objects. It inherits SystemObject and thus can be created by the + * ObjectManagerIF. * - * This class uses the opcode of ExecutableObjectIF to perform a step-wise execution. - * For each step an RMAP action is selected and executed. - * If data has been received (GET_READ), the data will be interpreted. - * The action for each step can be defined by the child class but as most - * device handlers share a 4-call (sendRead-getRead-sendWrite-getWrite) structure, - * a default implementation is provided. - * NOTE: RMAP is a standard which is used for FLP. + * This class uses the opcode of ExecutableObjectIF to perform a + * step-wise execution. For each step a different action is selected and + * executed. Currently, the device handler base performs a 4-step + * execution related to 4 communication steps (based on RMAP). + * NOTE: RMAP is a standard which is used for Flying Laptop. * RMAP communication is not mandatory for projects implementing the FSFW. * However, the communication principles are similar to RMAP as there are * two write and two send calls involved. @@ -69,9 +72,6 @@ class StorageManagerIF; * * Other important virtual methods with a default implementation * are the getTransitionDelayMs() function and the getSwitches() function. - * Please ensure that getSwitches() returns DeviceHandlerIF::NO_SWITCHES if - * power switches are not implemented yet. Otherwise, the device handler will - * not transition to MODE_ON, even if setMode(MODE_ON) is called. * If a transition to MODE_ON is desired without commanding, override the * intialize() function and call setMode(_MODE_START_UP) before calling * DeviceHandlerBase::initialize(). @@ -79,1116 +79,1208 @@ class StorageManagerIF; * @ingroup devices */ class DeviceHandlerBase: public DeviceHandlerIF, - public HasReturnvaluesIF, - public ExecutableObjectIF, - public SystemObject, - public HasModesIF, - public HasHealthIF, - public HasActionsIF, - public ReceivesParameterMessagesIF { - friend void (Factory::setStaticFrameworkObjectIds)(); +public HasReturnvaluesIF, +public ExecutableObjectIF, +public SystemObject, +public HasModesIF, +public HasHealthIF, +public HasActionsIF, +public ReceivesParameterMessagesIF, +public HasLocalDataPoolIF { + friend void (Factory::setStaticFrameworkObjectIds)(); public: - /** - * The constructor passes the objectId to the SystemObject(). - * - * @param setObjectId the ObjectId to pass to the SystemObject() Constructor - * @param maxDeviceReplyLen the length the RMAP getRead call will be sent with - * @param setDeviceSwitch the switch the device is connected to, - * for devices using two switches, overwrite getSwitches() - * @param deviceCommuncation Communcation Interface object which is used - * to implement communication functions - * @param thermalStatePoolId - * @param thermalRequestPoolId - * @param fdirInstance - * @param cmdQueueSize - */ - DeviceHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, - CookieIF * comCookie, FailureIsolationBase* fdirInstance = nullptr, - size_t cmdQueueSize = 20); + /** + * The constructor passes the objectId to the SystemObject(). + * + * @param setObjectId the ObjectId to pass to the SystemObject() Constructor + * @param deviceCommuncation Communcation Interface object which is used + * to implement communication functions + * @param comCookie This object will be passed to the communication inter- + * face and can contain user-defined information about the communication. + * @param fdirInstance + * @param cmdQueueSize + */ + DeviceHandlerBase(object_id_t setObjectId, object_id_t deviceCommunication, + CookieIF * comCookie, FailureIsolationBase* fdirInstance = nullptr, + size_t cmdQueueSize = 20); - void setThermalStateRequestPoolIds(uint32_t thermalStatePoolId, - uint32_t thermalRequestPoolId); + void setHkDestination(object_id_t hkDestination); - /** - * @brief This function is the device handler base core component and is - * called periodically. - * @details - * General sequence, showing where abstract virtual functions are called: - * If the State is SEND_WRITE: - * 1. Set the cookie state to COOKIE_UNUSED and read the command queue - * 2. Handles Device State Modes by calling doStateMachine(). - * This function calls callChildStatemachine() which calls the - * abstract functions doStartUp() and doShutDown() - * 3. Check switch states by calling checkSwitchStates() - * 4. Decrements counter for timeout of replies by calling - * decrementDeviceReplyMap() - * 5. Performs FDIR check for failures - * 6. Calls hkSwitcher.performOperation() - * 7. If the device mode is MODE_OFF, return RETURN_OK. + /** + * If the device handler is controlled by the FSFW thermal building blocks, + * this function should be called to initialize all required components. + * The device handler will then take care of creating local pool entries + * for the device thermal state and device heating request. + * Custom local pool IDs can be assigned as well. + * @param thermalStatePoolId + * @param thermalRequestPoolId + */ + void setThermalStateRequestPoolIds(lp_id_t thermalStatePoolId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t thermalRequestPoolId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID, + uint32_t thermalSetId = DeviceHandlerIF::DEFAULT_THERMAL_SET_ID); + /** + * @brief Helper function to ease device handler development. + * This will instruct the transition to MODE_ON immediately + * (leading to doStartUp() being called for the transition to the ON mode), + * so external mode commanding is not necessary anymore. + * + * This has to be called before the task is started! + * (e.g. in the task factory). This is only a helper function for + * development. Regular mode commanding should be performed by commanding + * the AssemblyBase or Subsystem objects resposible for the device handler. + */ + void setStartUpImmediately(); + + /** + * @brief This function is the device handler base core component and is + * called periodically. + * @details + * General sequence, showing where abstract virtual functions are called: + * If the State is SEND_WRITE: + * 1. Set the cookie state to COOKIE_UNUSED and read the command queue + * 2. Handles Device State Modes by calling doStateMachine(). + * This function calls callChildStatemachine() which calls the + * abstract functions doStartUp() and doShutDown() + * 3. Check switch states by calling checkSwitchStates() + * 4. Decrements counter for timeout of replies by calling + * decrementDeviceReplyMap() + * 5. Performs FDIR check for failures + * 6. If the device mode is MODE_OFF, return RETURN_OK. * Otherwise, perform the Action property and performs depending * on value specified by input value counter (incremented in PST). * The child class tells base class what to do by setting this value. - * - SEND_WRITE: Send data or commands to device by calling - * doSendWrite() which calls sendMessage function - * of #communicationInterface - * and calls buildInternalCommand if the cookie state is COOKIE_UNUSED - * - GET_WRITE: Get ackknowledgement for sending by calling doGetWrite() - * which calls getSendSuccess of #communicationInterface. - * Calls abstract functions scanForReply() and interpretDeviceReply(). - * - SEND_READ: Request reading data from device by calling doSendRead() - * which calls requestReceiveMessage of #communcationInterface - * - GET_READ: Access requested reading data by calling doGetRead() - * which calls readReceivedMessage of #communicationInterface - * @param counter Specifies which Action to perform - * @return RETURN_OK for successful execution - */ - virtual ReturnValue_t performOperation(uint8_t counter); + * - SEND_WRITE: Send data or commands to device by calling + * doSendWrite() which calls sendMessage function + * of #communicationInterface + * and calls buildInternalCommand if the cookie state is COOKIE_UNUSED + * - GET_WRITE: Get ackknowledgement for sending by calling doGetWrite() + * which calls getSendSuccess of #communicationInterface. + * Calls abstract functions scanForReply() and interpretDeviceReply(). + * - SEND_READ: Request reading data from device by calling doSendRead() + * which calls requestReceiveMessage of #communcationInterface + * - GET_READ: Access requested reading data by calling doGetRead() + * which calls readReceivedMessage of #communicationInterface + * @param counter Specifies which Action to perform + * @return RETURN_OK for successful execution + */ + virtual ReturnValue_t performOperation(uint8_t counter); - /** - * @brief Initializes the device handler - * @details - * Initialize Device Handler as system object and - * initializes all important helper classes. - * Calls fillCommandAndReplyMap(). - * @return - */ - virtual ReturnValue_t initialize(); - /** Destructor. */ - virtual ~DeviceHandlerBase(); + /** + * @brief Initializes the device handler + * @details + * Initialize Device Handler as system object and + * initializes all important helper classes. + * Calls fillCommandAndReplyMap(). + * @return + */ + virtual ReturnValue_t initialize(); + + /** + * @brief Intialization steps performed after all tasks have been created. + * This function will be called by the executing task. + * @return + */ + virtual ReturnValue_t initializeAfterTaskCreation() override; + + /** Destructor. */ + virtual ~DeviceHandlerBase(); + + + /** + * Implementation of ExecutableObjectIF function + * Used to setup the reference of the task, that executes this component + * @param task_ Pointer to the taskIF of this task + */ + virtual void setTaskIF(PeriodicTaskIF* task_) override; + virtual MessageQueueId_t getCommandQueue(void) const override; + + /** Explicit interface implementation of getObjectId */ + virtual object_id_t getObjectId() const override; + + /** + * @param parentQueueId + */ + virtual void setParentQueue(MessageQueueId_t parentQueueId); + + /** @brief Implementation required for HasActionIF */ + ReturnValue_t executeAction(ActionId_t actionId, + MessageQueueId_t commandedBy, const uint8_t* data, + size_t size) override; + + Mode_t getTransitionSourceMode() const; + Submode_t getTransitionSourceSubMode() const; + virtual void getMode(Mode_t *mode, Submode_t *submode); + HealthState getHealth(); + ReturnValue_t setHealth(HealthState health); + virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, + uint16_t startAtIndex) override; protected: - /** - * @brief This is used to let the child class handle the transition from - * mode @c _MODE_START_UP to @c MODE_ON - * @details - * It is only called when the device handler is in mode @c _MODE_START_UP. - * That means, the device switch(es) are already set to on. - * Device handler commands are read and can be handled by the child class. - * If the child class handles a command, it should also send - * an reply accordingly. - * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, - * the base class handles rejecting the command and sends a reply. - * The replies for mode transitions are handled by the base class. - * - * - If the device is started and ready for operation, the mode should be - * set to MODE_ON. It is possible to set the mode to _MODE_TO_ON to - * use the to on transition if available. - * - If the power-up fails, the mode should be set to _MODE_POWER_DOWN - * which will lead to the device being powered off. - * - If the device does not change the mode, the mode will be changed - * to _MODE_POWER_DOWN, after the timeout (from getTransitionDelay()) - * has passed. - * - * #transitionFailure can be set to a failure code indicating the reason - * for a failed transition - */ - virtual void doStartUp() = 0; + /** + * @brief This is used to let the child class handle the transition from + * mode @c _MODE_START_UP to @c MODE_ON + * @details + * It is only called when the device handler is in mode @c _MODE_START_UP. + * That means, the device switch(es) are already set to on. + * Device handler commands are read and can be handled by the child class. + * If the child class handles a command, it should also send + * an reply accordingly. + * If an Command is not handled (ie #DeviceHandlerCommand is not @c CMD_NONE, + * the base class handles rejecting the command and sends a reply. + * The replies for mode transitions are handled by the base class. + * + * - If the device is started and ready for operation, the mode should be + * set to MODE_ON. It is possible to set the mode to _MODE_TO_ON to + * use the to on transition if available. + * - If the power-up fails, the mode should be set to _MODE_POWER_DOWN + * which will lead to the device being powered off. + * - If the device does not change the mode, the mode will be changed + * to _MODE_POWER_DOWN, after the timeout (from getTransitionDelay()) + * has passed. + * + * #transitionFailure can be set to a failure code indicating the reason + * for a failed transition + */ + virtual void doStartUp() = 0; + /** + * @brief This is used to let the child class handle the transition + * from mode @c _MODE_SHUT_DOWN to @c _MODE_POWER_DOWN + * @details + * It is only called when the device handler is in mode @c _MODE_SHUT_DOWN. + * Device handler commands are read and can be handled by the child class. + * If the child class handles a command, it should also send an reply + * accordingly. + * If an Command is not handled (ie #DeviceHandlerCommand is not + * @c CMD_NONE, the base class handles rejecting the command and sends a + * reply. The replies for mode transitions are handled by the base class. + * + * - If the device ready to be switched off, + * the mode should be set to _MODE_POWER_DOWN. + * - If the device should not be switched off, the mode can be changed to + * _MODE_TO_ON (or MODE_ON if no transition is needed). + * - If the device does not change the mode, the mode will be changed to + * _MODE_POWER_DOWN, when the timeout (from getTransitionDelay()) + * has passed. + * + * #transitionFailure can be set to a failure code indicating the reason + * for a failed transition + */ + virtual void doShutDown() = 0; - /** - * @brief This is used to let the child class handle the transition - * from mode @c _MODE_SHUT_DOWN to @c _MODE_POWER_DOWN - * @details - * It is only called when the device handler is in mode @c _MODE_SHUT_DOWN. - * Device handler commands are read and can be handled by the child class. - * If the child class handles a command, it should also send an reply - * accordingly. - * If an Command is not handled (ie #DeviceHandlerCommand is not - * @c CMD_NONE, the base class handles rejecting the command and sends a - * reply. The replies for mode transitions are handled by the base class. - * - * - If the device ready to be switched off, - * the mode should be set to _MODE_POWER_DOWN. - * - If the device should not be switched off, the mode can be changed to - * _MODE_TO_ON (or MODE_ON if no transition is needed). - * - If the device does not change the mode, the mode will be changed to - * _MODE_POWER_DOWN, when the timeout (from getTransitionDelay()) - * has passed. - * - * #transitionFailure can be set to a failure code indicating the reason - * for a failed transition - */ - virtual void doShutDown() = 0; + /* Command handling */ + /** + * Build the device command to send for normal mode. + * + * This is only called in @c MODE_NORMAL. If multiple submodes for + * @c MODE_NORMAL are supported, different commands can built, + * depending on the submode. + * + * #rawPacket and #rawPacketLen must be set by this method to the + * packet to be sent. If variable command frequence is required, a counter + * can be used and the frequency in the reply map has to be set manually + * by calling updateReplyMap(). + * + * @param[out] id the device command id that has been built + * @return + * - @c RETURN_OK to send command after setting #rawPacket and + * #rawPacketLen. + * - @c NOTHING_TO_SEND when no command is to be sent. + * - Anything else triggers an even with the returnvalue as a parameter. + */ + virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) = 0; + /** + * Build the device command to send for a transitional mode. + * + * This is only called in @c _MODE_TO_NORMAL, @c _MODE_TO_ON, @c _MODE_TO_RAW, + * @c _MODE_START_UP and @c _MODE_SHUT_DOWN. So it is used by doStartUp() + * and doShutDown() as well as doTransition(), by setting those + * modes in the respective functions. + * + * A good idea is to implement a flag indicating a command has to be built + * and a variable containing the command number to be built + * and filling them in doStartUp(), doShutDown() and doTransition() so no + * modes have to be checked here. + * + * #rawPacket and #rawPacketLen must be set by this method to the + * packet to be sent. + * + * @param[out] id the device command id built + * @return + * - @c RETURN_OK when a command is to be sent + * - @c NOTHING_TO_SEND when no command is to be sent + * - Anything else triggers an even with the returnvalue as a parameter + */ + virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t * id) = 0; + /** + * @brief Build a device command packet from data supplied by a direct + * command (PUS Service 8) + * @details + * This will be called if an functional command via PUS Service 8 is received and is + * the primary interface for functional command instead of #executeAction for users. The + * supplied ActionId_t action ID will be converted to a DeviceCommandId_t command ID after + * an internal check whether the action ID is a key in the device command map. + * + * #rawPacket and #rawPacketLen should be set by this method to the packet to be sent. + * The existence of the command in the command map and the command size check against 0 are + * done by the base class. + * + * @param deviceCommand The command to build, already checked against deviceCommandMap + * @param commandData Pointer to the data from the direct command + * @param commandDataLen Length of commandData + * @return + * - @c RETURN_OK to send command after #rawPacket and #rawPacketLen + * have been set. + * - @c HasActionsIF::EXECUTION_COMPLETE to generate a finish reply immediately. This can + * be used if no reply is expected. Otherwise, the developer can call #actionHelper.finish + * to finish the command handling. + * - Anything else triggers an event with the return code as a parameter as well as a + * step reply failed with the return code + */ + virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, + const uint8_t * commandData, size_t commandDataLen) = 0; - /** - * Build the device command to send for normal mode. - * - * This is only called in @c MODE_NORMAL. If multiple submodes for - * @c MODE_NORMAL are supported, different commands can built, - * depending on the submode. - * - * #rawPacket and #rawPacketLen must be set by this method to the - * packet to be sent. If variable command frequence is required, a counter - * can be used and the frequency in the reply map has to be set manually - * by calling updateReplyMap(). - * - * @param[out] id the device command id that has been built - * @return - * - @c RETURN_OK to send command after setting #rawPacket and #rawPacketLen. - * - @c NOTHING_TO_SEND when no command is to be sent. - * - Anything else triggers an even with the returnvalue as a parameter. - */ - virtual ReturnValue_t buildNormalDeviceCommand(DeviceCommandId_t * id) = 0; + /* Reply handling */ + /** + * @brief Scans a buffer for a valid reply. + * @details + * This is used by the base class to check the data received for valid packets. + * It only checks if a valid packet starts at @c start. + * It also only checks the structural validy of the packet, + * e.g. checksums lengths and protocol data. No information check is done, + * e.g. range checks etc. + * + * Errors should be reported directly, the base class does NOT report any + * errors based on the return value of this function. + * + * @param start start of remaining buffer to be scanned + * @param len length of remaining buffer to be scanned + * @param[out] foundId the id of the data found in the buffer. + * @param[out] foundLen length of the data found. Is to be set in function, + * buffer is scanned at previous position + foundLen. + * @return + * - @c RETURN_OK a valid packet was found at @c start, @c foundLen is valid + * - @c RETURN_FAILED no reply could be found starting at @c start, + * implies @c foundLen is not valid, base class will call scanForReply() + * again with ++start + * - @c DeviceHandlerIF::INVALID_DATA a packet was found but it is invalid, + * e.g. checksum error, implies @c foundLen is valid, can be used to + * skip some bytes + * - @c DeviceHandlerIF::LENGTH_MISSMATCH @c len is invalid + * - @c DeviceHandlerIF::IGNORE_REPLY_DATA Ignore this specific part of + * the packet + * - @c DeviceHandlerIF::IGNORE_FULL_PACKET Ignore the packet + * - @c APERIODIC_REPLY if a valid reply is received that has not been + * requested by a command, but should be handled anyway + * (@see also fillCommandAndCookieMap() ) + */ + virtual ReturnValue_t scanForReply(const uint8_t *start, size_t len, + DeviceCommandId_t *foundId, size_t *foundLen) = 0; + /** + * @brief Interpret a reply from the device. + * @details + * This is called after scanForReply() found a valid packet, it can be + * assumed that the length and structure is valid. + * This routine extracts the data from the packet into a DataSet and then + * calls handleDeviceTM(), which either sends a TM packet or stores the + * data in the DataPool depending on whether it was an external command. + * No packet length is given, as it should be defined implicitly by the id. + * + * @param id the id found by scanForReply() + * @param packet + * @return + * - @c RETURN_OK when the reply was interpreted. + * - @c IGNORE_REPLY_DATA Ignore the reply and don't reset reply cycle + * counter. + * - @c RETURN_FAILED when the reply could not be interpreted, + * e.g. logical errors or range violations occurred + */ + virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, + const uint8_t *packet) = 0; + MessageQueueId_t getCommanderId(DeviceCommandId_t replyId) const; + /** + * Helper function to get pending command. This is useful for devices + * like SPI sensors to identify the last sent command. + * This only returns the command sent in the last SEND_WRITE cycle. + * @return + */ + DeviceCommandId_t getPendingCommand() const; - /** - * Build the device command to send for a transitional mode. - * - * This is only called in @c _MODE_TO_NORMAL, @c _MODE_TO_ON, @c _MODE_TO_RAW, - * @c _MODE_START_UP and @c _MODE_SHUT_DOWN. So it is used by doStartUp() - * and doShutDown() as well as doTransition(), by setting those - * modes in the respective functions. - * - * A good idea is to implement a flag indicating a command has to be built - * and a variable containing the command number to be built - * and filling them in doStartUp(), doShutDown() and doTransition() so no - * modes have to be checked here. - * - * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. - * - * @param[out] id the device command id built - * @return - * - @c RETURN_OK when a command is to be sent - * - @c NOTHING_TO_SEND when no command is to be sent - * - Anything else triggers an even with the returnvalue as a parameter - */ - virtual ReturnValue_t buildTransitionDeviceCommand(DeviceCommandId_t * id) = 0; + /* Specifying commands and replies */ + /** + * @brief Fill the #DeviceCommandMap and #DeviceReplyMap called by the #initialize + * of the base class + * @details + * This is used to let the base class know which replies are expected. + * There are different scenarios regarding this: + * + * - "Normal" commands. These are commands, that trigger a direct reply + * from the device. In this case, the id of the command should be added + * to the command map with a commandData_t where maxDelayCycles is set + * to the maximum expected number of PST cycles the reply will take. + * Then, scanForReply returns the id of the command and the base class + * can handle time-out and missing replies. + * + * - Periodic, unrequested replies. These are replies that, once enabled, + * are sent by the device on its own in a defined interval. + * In this case, the id of the reply or a placeholder id should be added + * to the deviceCommandMap with a commandData_t where maxDelayCycles is + * set to the maximum expected number of PST cycles between two replies + * (also a tolerance should be added, as an FDIR message will be + * generated if it is missed). + * From then on, the base class handles the reception. + * Then, scanForReply returns the id of the reply or the placeholder id + * and the base class will take care of checking that all replies are + * received and the interval is correct. + * + * - Aperiodic, unrequested replies. These are replies that are sent + * by the device without any preceding command and not in a defined + * interval. These are not entered in the deviceCommandMap but + * handled by returning @c APERIODIC_REPLY in scanForReply(). + */ + virtual void fillCommandAndReplyMap() = 0; + /** + * This is a helper method to facilitate inserting entries in the command map. + * @param deviceCommand Identifier of the command to add. + * @param maxDelayCycles The maximum number of delay cycles the command + * waits until it times out. + * @param replyLen Will be supplied to the requestReceiveMessage call of + * the communication interface. + * @param periodic Indicates if the command is periodic (i.e. it is sent + * by the device repeatedly without request) or not. Default is aperiodic (0) + * @return - @c RETURN_OK when the command was successfully inserted, + * - @c RETURN_FAILED else. + */ + ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, + uint16_t maxDelayCycles, + LocalPoolDataSetBase* replyDataSet = nullptr, + size_t replyLen = 0, bool periodic = false, + bool hasDifferentReplyId = false, DeviceCommandId_t replyId = 0); + /** + * @brief This is a helper method to insert replies in the reply map. + * @param deviceCommand Identifier of the reply to add. + * @param maxDelayCycles The maximum number of delay cycles the reply waits + * until it times out. + * @param periodic Indicates if the command is periodic (i.e. it is sent + * by the device repeatedly without request) or not. Default is aperiodic (0) + * @return - @c RETURN_OK when the command was successfully inserted, + * - @c RETURN_FAILED else. + */ + ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, + uint16_t maxDelayCycles, LocalPoolDataSetBase* dataSet = nullptr, + size_t replyLen = 0, bool periodic = false); - /** - * @brief Build a device command packet from data supplied by a direct command. - * - * @details - * #rawPacket and #rawPacketLen should be set by this method to the packet to be sent. - * The existence of the command in the command map and the command size check - * against 0 are done by the base class. - * - * @param deviceCommand the command to build, already checked against deviceCommandMap - * @param commandData pointer to the data from the direct command - * @param commandDataLen length of commandData - * @return - * - @c RETURN_OK to send command after #rawPacket and #rawPacketLen have been set. - * - Anything else triggers an event with the returnvalue as a parameter - */ - virtual ReturnValue_t buildCommandFromCommand(DeviceCommandId_t deviceCommand, - const uint8_t * commandData, size_t commandDataLen) = 0; + /** + * @brief A simple command to add a command to the commandList. + * @param deviceCommand The command to add + * @return - @c RETURN_OK when the command was successfully inserted, + * - @c RETURN_FAILED else. + */ + ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand); - /** - * @brief Scans a buffer for a valid reply. - * @details - * This is used by the base class to check the data received for valid packets. - * It only checks if a valid packet starts at @c start. - * It also only checks the structural validy of the packet, - * e.g. checksums lengths and protocol data. No information check is done, - * e.g. range checks etc. - * - * Errors should be reported directly, the base class does NOT report any - * errors based on the return value of this function. - * - * @param start start of remaining buffer to be scanned - * @param len length of remaining buffer to be scanned - * @param[out] foundId the id of the data found in the buffer. - * @param[out] foundLen length of the data found. Is to be set in function, - * buffer is scanned at previous position + foundLen. - * @return - * - @c RETURN_OK a valid packet was found at @c start, @c foundLen is valid - * - @c RETURN_FAILED no reply could be found starting at @c start, - * implies @c foundLen is not valid, base class will call scanForReply() - * again with ++start - * - @c DeviceHandlerIF::INVALID_DATA a packet was found but it is invalid, - * e.g. checksum error, implies @c foundLen is valid, can be used to - * skip some bytes - * - @c DeviceHandlerIF::LENGTH_MISSMATCH @c len is invalid - * - @c DeviceHandlerIF::IGNORE_REPLY_DATA Ignore this specific part of - * the packet - * - @c DeviceHandlerIF::IGNORE_FULL_PACKET Ignore the packet - * - @c APERIODIC_REPLY if a valid reply is received that has not been - * requested by a command, but should be handled anyway - * (@see also fillCommandAndCookieMap() ) - */ - virtual ReturnValue_t scanForReply(const uint8_t *start, size_t len, - DeviceCommandId_t *foundId, size_t *foundLen) = 0; + /** + * @brief This function returns the reply length of the next reply to read. + * + * @param deviceCommand The command which triggered the device reply. + * + * @details The default implementation assumes only one reply is triggered by the command. In + * case the command triggers multiple replies (e.g. one acknowledgment, one data, + * and one execution status reply), this function can be overwritten to get the + * reply length of the next reply to read. + */ + virtual size_t getNextReplyLength(DeviceCommandId_t deviceCommand); - /** - * @brief Interpret a reply from the device. - * @details - * This is called after scanForReply() found a valid packet, it can be - * assumed that the length and structure is valid. - * This routine extracts the data from the packet into a DataSet and then - * calls handleDeviceTM(), which either sends a TM packet or stores the - * data in the DataPool depending on whether it was an external command. - * No packet length is given, as it should be defined implicitly by the id. - * - * @param id the id found by scanForReply() - * @param packet - * @return - * - @c RETURN_OK when the reply was interpreted. - * - @c RETURN_FAILED when the reply could not be interpreted, - * e.g. logical errors or range violations occurred - */ - virtual ReturnValue_t interpretDeviceReply(DeviceCommandId_t id, - const uint8_t *packet) = 0; + /** + * @brief This is a helper method to facilitate updating entries + * in the reply map. + * @param deviceCommand Identifier of the reply to update. + * @param delayCycles The current number of delay cycles to wait. + * As stated in #fillCommandAndCookieMap, to disable periodic commands, + * this is set to zero. + * @param maxDelayCycles The maximum number of delay cycles the reply waits + * until it times out. By passing 0 the entry remains untouched. + * @param periodic Indicates if the command is periodic (i.e. it is sent + * by the device repeatedly without request) or not.Default is aperiodic (0). + * Warning: The setting always overrides the value that was entered in the map. + * @return - @c RETURN_OK when the command was successfully inserted, + * - @c RETURN_FAILED else. + */ + ReturnValue_t updateReplyMapEntry(DeviceCommandId_t deviceReply, + uint16_t delayCycles, uint16_t maxDelayCycles, + bool periodic = false); + /** + * @brief Can be used to set the dataset corresponding to a reply ID manually. + * @details + * Used by the local data pool manager. + */ + ReturnValue_t setReplyDataset(DeviceCommandId_t replyId, + LocalPoolDataSetBase* dataset); - /** - * @brief fill the #DeviceCommandMap and #DeviceReplyMap - * called by the initialize() of the base class - * @details - * This is used to let the base class know which replies are expected. - * There are different scenarios regarding this: - * - * - "Normal" commands. These are commands, that trigger a direct reply - * from the device. In this case, the id of the command should be added - * to the command map with a commandData_t where maxDelayCycles is set - * to the maximum expected number of PST cycles the reply will take. - * Then, scanForReply returns the id of the command and the base class - * can handle time-out and missing replies. - * - * - Periodic, unrequested replies. These are replies that, once enabled, - * are sent by the device on its own in a defined interval. - * In this case, the id of the reply or a placeholder id should be added - * to the deviceCommandMap with a commandData_t where maxDelayCycles is - * set to the maximum expected number of PST cycles between two replies - * (also a tolerance should be added, as an FDIR message will be - * generated if it is missed). - * - * (Robin) This part confuses me. "must do as soon as" implies that - * the developer must do something somewhere else in the code. Is - * that really the case? If I understood correctly, DHB performs - * almost everything (e.g. in erirm function) as long as the commands - * are inserted correctly. - * - * As soon as the replies are enabled, DeviceCommandInfo.periodic must - * be set to true, DeviceCommandInfo.delayCycles to - * DeviceCommandInfo.maxDelayCycles. - * From then on, the base class handles the reception. - * Then, scanForReply returns the id of the reply or the placeholder id - * and the base class will take care of checking that all replies are - * received and the interval is correct. - * When the replies are disabled, DeviceCommandInfo.periodic must be set - * to 0, DeviceCommandInfo.delayCycles to 0; - * - * - Aperiodic, unrequested replies. These are replies that are sent - * by the device without any preceding command and not in a defined - * interval. These are not entered in the deviceCommandMap but - * handled by returning @c APERIODIC_REPLY in scanForReply(). - */ - virtual void fillCommandAndReplyMap() = 0; + /** + * Get the time needed to transit from modeFrom to modeTo. + * + * Used for the following transitions: + * modeFrom -> modeTo: + * MODE_ON -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * MODE_NORMAL -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * MODE_RAW -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] + * _MODE_START_UP -> MODE_ON (do not include time to set the switches, + * the base class got you covered) + * + * The default implementation returns 0 ! + * @param modeFrom + * @param modeTo + * @return time in ms + */ + virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo) = 0; - /** - * This is a helper method to facilitate inserting entries in the command map. - * @param deviceCommand Identifier of the command to add. - * @param maxDelayCycles The maximum number of delay cycles the command - * waits until it times out. - * @param periodic Indicates if the command is periodic (i.e. it is sent - * by the device repeatedly without request) or not. Default is aperiodic (0) - * @return - @c RETURN_OK when the command was successfully inserted, - * - @c RETURN_FAILED else. - */ - ReturnValue_t insertInCommandAndReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false, - bool hasDifferentReplyId = false, DeviceCommandId_t replyId = 0); + /* Functions used by the local data pool manager */ + /** + * This function is used to initialize the local housekeeping pool + * entries. The default implementation leaves the pool empty. + * @param localDataPoolMap + * @return + */ + virtual ReturnValue_t initializeLocalDataPool(localpool::DataPool& localDataPoolMap, + LocalDataPoolManager& poolManager) override; + /** + * @brief Set all datapool variables that are update periodically in + * normal mode invalid + * @details + * The default implementation will set all datasets which have been added + * in #fillCommandAndReplyMap to invalid. It will also set all pool + * variables inside the dataset to invalid. The user can override this + * method optionally. + */ + virtual void setNormalDatapoolEntriesInvalid(); + /** + * @brief Get the dataset handle for a given SID. + * @details + * The default implementation will use the deviceCommandMap to look for the corresponding + * dataset handle. The user can override this function if this is not desired. + * @param sid + * @return + */ + virtual LocalPoolDataSetBase* getDataSetHandle(sid_t sid) override; - /** - * @brief This is a helper method to insert replies in the reply map. - * @param deviceCommand Identifier of the reply to add. - * @param maxDelayCycles The maximum number of delay cycles the reply waits - * until it times out. - * @param periodic Indicates if the command is periodic (i.e. it is sent - * by the device repeatedly without request) or not. Default is aperiodic (0) - * @return - @c RETURN_OK when the command was successfully inserted, - * - @c RETURN_FAILED else. - */ - ReturnValue_t insertInReplyMap(DeviceCommandId_t deviceCommand, - uint16_t maxDelayCycles, size_t replyLen = 0, bool periodic = false); + /* HasModesIF overrides */ + virtual void startTransition(Mode_t mode, Submode_t submode) override; + virtual void setToExternalControl() override; + virtual void announceMode(bool recursive) override; + /** + * @brief Set the device handler mode + * @details + * Sets #timeoutStart with every call Also sets #transitionTargetMode if necessary so + * transitional states can be entered from everywhere without breaking the state machine + * (which relies on a correct #transitionTargetMode). + * The submode is left unchanged. + * + * @param newMode + */ + void setMode(Mode_t newMode); + /** + * @overload + * @param submode + */ + void setMode(Mode_t newMode, Submode_t submode); + /** + * @brief Should be implemented properly by child class. + * @param mode + * @param submode + * @return + * - @c RETURN_OK if valid + * - @c RETURN_FAILED if invalid + */ + virtual ReturnValue_t isModeCombinationValid(Mode_t mode, + Submode_t submode); + /** + * @brief Notify child about mode change. + * @details + * Can be overriden to be used like a callback. + */ + virtual void modeChanged(); - /** - * @brief A simple command to add a command to the commandList. - * @param deviceCommand The command to add - * @return - @c RETURN_OK when the command was successfully inserted, - * - @c RETURN_FAILED else. - */ - ReturnValue_t insertInCommandMap(DeviceCommandId_t deviceCommand); - /** - * @brief This is a helper method to facilitate updating entries - * in the reply map. - * @param deviceCommand Identifier of the reply to update. - * @param delayCycles The current number of delay cycles to wait. - * As stated in #fillCommandAndCookieMap, to disable periodic commands, - * this is set to zero. - * @param maxDelayCycles The maximum number of delay cycles the reply waits - * until it times out. By passing 0 the entry remains untouched. - * @param periodic Indicates if the command is periodic (i.e. it is sent - * by the device repeatedly without request) or not.Default is aperiodic (0). - * Warning: The setting always overrides the value that was entered in the map. - * @return - @c RETURN_OK when the command was successfully inserted, - * - @c RETURN_FAILED else. - */ - ReturnValue_t updateReplyMapEntry(DeviceCommandId_t deviceReply, - uint16_t delayCycles, uint16_t maxDelayCycles, - bool periodic = false); + /* Power handling functions */ + /** + * Return the switches connected to the device. + * + * The default implementation returns one switch set in the ctor. + * + * @param[out] switches pointer to an array of switches + * @param[out] numberOfSwitches length of returned array + * @return + * - @c RETURN_OK if the parameters were set + * - @c RETURN_FAILED if no switches exist + */ + virtual ReturnValue_t getSwitches(const uint8_t **switches, + uint8_t *numberOfSwitches); - /** - * @brief Can be implemented by child handler to - * perform debugging - * @details Example: Calling this in performOperation - * to track values like mode. - * @param positionTracker Provide the child handler a way to know - * where the debugInterface was called - * @param objectId Provide the child handler object Id to - * specify actions for spefic devices - * @param parameter Supply a parameter of interest - * Please delete all debugInterface calls in DHB after debugging is finished ! - */ - virtual void debugInterface(uint8_t positionTracker = 0, - object_id_t objectId = 0, uint32_t parameter = 0); + /** + * @brief Helper function to report a missed reply + * @details Can be overwritten by children to act on missed replies or to fake reporting Id. + * @param id of the missed reply + */ + virtual void missedReply(DeviceCommandId_t id); - /** - * Get the time needed to transit from modeFrom to modeTo. - * - * Used for the following transitions: - * modeFrom -> modeTo: - * MODE_ON -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * MODE_NORMAL -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * MODE_RAW -> [MODE_ON, MODE_NORMAL, MODE_RAW, _MODE_POWER_DOWN] - * _MODE_START_UP -> MODE_ON (do not include time to set the switches, - * the base class got you covered) - * - * The default implementation returns 0 ! - * @param modeFrom - * @param modeTo - * @return time in ms - */ - virtual uint32_t getTransitionDelayMs(Mode_t modeFrom, Mode_t modeTo); - - /** - * Return the switches connected to the device. - * - * The default implementation returns one switch set in the ctor. - * - * @param[out] switches pointer to an array of switches - * @param[out] numberOfSwitches length of returned array - * @return - * - @c RETURN_OK if the parameters were set - * - @c RETURN_FAILED if no switches exist - */ - virtual ReturnValue_t getSwitches(const uint8_t **switches, - uint8_t *numberOfSwitches); - - /** - * This function is used to initialize the local housekeeping pool - * entries. The default implementation leaves the pool empty. - * @param localDataPoolMap - * @return - */ - //virtual ReturnValue_t initializePoolEntries( - // LocalDataPool& localDataPoolMap) override; - - /** Get the HK manager object handle */ - //virtual LocalDataPoolManager* getHkManagerHandle() override; - - /** - * @brief Hook function for child handlers which is called once per - * performOperation(). Default implementation is empty. - */ - virtual void performOperationHook(); -public: - /** - * @param parentQueueId - */ - virtual void setParentQueue(MessageQueueId_t parentQueueId); - - /** @brief Implementation required for HasActionIF */ - ReturnValue_t executeAction(ActionId_t actionId, - MessageQueueId_t commandedBy, const uint8_t* data, - size_t size) override; - - Mode_t getTransitionSourceMode() const; - Submode_t getTransitionSourceSubMode() const; - virtual void getMode(Mode_t *mode, Submode_t *submode); - HealthState getHealth(); - ReturnValue_t setHealth(HealthState health); - virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, - ParameterWrapper *parameterWrapper, - const ParameterWrapper *newValues, uint16_t startAtIndex) override; - /** - * Implementation of ExecutableObjectIF function - * - * Used to setup the reference of the task, that executes this component - * @param task_ Pointer to the taskIF of this task - */ - virtual void setTaskIF(PeriodicTaskIF* task_); - virtual MessageQueueId_t getCommandQueue(void) const; + /* Miscellaneous functions */ + /** + * @brief Hook function for child handlers which is called once per + * performOperation(). Default implementation is empty. + */ + virtual void performOperationHook(); + /** + * @brief Can be implemented by child handler to + * perform debugging + * @details Example: Calling this in performOperation + * to track values like mode. + * @param positionTracker Provide the child handler a way to know + * where the debugInterface was called + * @param objectId Provide the child handler object Id to + * specify actions for spefic devices + * @param parameter Supply a parameter of interest + * Please delete all debugInterface calls in DHB after debugging is finished ! + */ + virtual void debugInterface(uint8_t positionTracker = 0, + object_id_t objectId = 0, uint32_t parameter = 0); protected: - /** - * The Returnvalues id of this class, required by HasReturnvaluesIF - */ - static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE; - static const ReturnValue_t INVALID_CHANNEL = MAKE_RETURN_CODE(0xA0); - // Returnvalues for scanForReply() - static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(0xB0); //!< This is used to specify for replies from a device which are not replies to requests - static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(0xB1); //!< Ignore parts of the received packet - static const ReturnValue_t IGNORE_FULL_PACKET = MAKE_RETURN_CODE(0xB2); //!< Ignore full received packet - // Returnvalues for command building - static const ReturnValue_t NOTHING_TO_SEND = MAKE_RETURN_CODE(0xC0); //!< Return this if no command sending in required - static const ReturnValue_t COMMAND_MAP_ERROR = MAKE_RETURN_CODE(0xC2); - // Returnvalues for getSwitches() - static const ReturnValue_t NO_SWITCH = MAKE_RETURN_CODE(0xD0); - // Mode handling error Codes - static const ReturnValue_t CHILD_TIMEOUT = MAKE_RETURN_CODE(0xE0); - static const ReturnValue_t SWITCH_FAILED = MAKE_RETURN_CODE(0xE1); + static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_BASE; - static const DeviceCommandId_t RAW_COMMAND_ID = -1; - static const DeviceCommandId_t NO_COMMAND_ID = -2; - static const MessageQueueId_t NO_COMMANDER = 0; + static const ReturnValue_t INVALID_CHANNEL = MAKE_RETURN_CODE(0xA0); + /* Return codes for scanForReply */ + //! This is used to specify for replies from a device which are not replies to requests + static const ReturnValue_t APERIODIC_REPLY = MAKE_RETURN_CODE(0xB0); + //! Ignore parts of the received packet + static const ReturnValue_t IGNORE_REPLY_DATA = MAKE_RETURN_CODE(0xB1); + //! Ignore full received packet + static const ReturnValue_t IGNORE_FULL_PACKET = MAKE_RETURN_CODE(0xB2); + /* Return codes for command building */ + //! Return this if no command sending in required + static const ReturnValue_t NOTHING_TO_SEND = MAKE_RETURN_CODE(0xC0); + static const ReturnValue_t COMMAND_MAP_ERROR = MAKE_RETURN_CODE(0xC2); + // Return codes for getSwitches */ + static const ReturnValue_t NO_SWITCH = MAKE_RETURN_CODE(0xD0); + /* Mode handling error Codes */ + static const ReturnValue_t CHILD_TIMEOUT = MAKE_RETURN_CODE(0xE0); + static const ReturnValue_t SWITCH_FAILED = MAKE_RETURN_CODE(0xE1); - /** Pointer to the raw packet that will be sent.*/ - uint8_t *rawPacket = nullptr; - /** Size of the #rawPacket. */ - uint32_t rawPacketLen = 0; + static const MessageQueueId_t NO_COMMANDER = 0; - /** - * The mode the device handler is currently in. - * This should never be changed directly but only with setMode() - */ - Mode_t mode; - /** - * The submode the device handler is currently in. - * This should never be changed directly but only with setMode() - */ - Submode_t submode; + //! Pointer to the raw packet that will be sent. + uint8_t *rawPacket = nullptr; + //! Size of the #rawPacket. + uint32_t rawPacketLen = 0; - /** This is the counter value from performOperation(). */ - uint8_t pstStep = 0; - uint32_t pstIntervalMs = 0; + /** + * The mode the device handler is currently in. + * This should never be changed directly but only with setMode() + */ + Mode_t mode; + /** + * The submode the device handler is currently in. + * This should never be changed directly but only with setMode() + */ + Submode_t submode; - /** - * Wiretapping flag: - * - * indicates either that all raw messages to and from the device should be - * sent to #defaultRawReceiver - * or that all device TM should be downlinked to #defaultRawReceiver. - */ - enum WiretappingMode { - OFF = 0, RAW = 1, TM = 2 - } wiretappingMode; - /** - * @brief A message queue that accepts raw replies - * - * Statically initialized in initialize() to a configurable object. - * Used when there is no method of finding a recipient, ie raw mode and - * reporting erroneous replies - */ - MessageQueueId_t defaultRawReceiver = MessageQueueIF::NO_QUEUE; - store_address_t storedRawData; + /** This is the counter value from performOperation(). */ + uint8_t pstStep = 0; + uint8_t lastStep = 0; + uint32_t pstIntervalMs = 0; - /** - * @brief The message queue which wants to read all raw traffic - * If #isWiretappingActive all raw communication from and to the device - * will be sent to this queue - */ - MessageQueueId_t requestedRawTraffic = 0; + /** + * Wiretapping flag: + * + * indicates either that all raw messages to and from the device should be + * sent to #defaultRawReceiver + * or that all device TM should be downlinked to #defaultRawReceiver. + */ + enum WiretappingMode { + OFF = 0, RAW = 1, TM = 2 + } wiretappingMode; + /** + * @brief A message queue that accepts raw replies + * + * Statically initialized in initialize() to a configurable object. + * Used when there is no method of finding a recipient, ie raw mode and + * reporting erroneous replies + */ + MessageQueueId_t defaultRawReceiver = MessageQueueIF::NO_QUEUE; + store_address_t storedRawData; - /** - * Pointer to the IPCStore. - * This caches the pointer received from the objectManager in the constructor. - */ - StorageManagerIF *IPCStore = nullptr; - /** The comIF object ID is cached for the intialize() function */ - object_id_t deviceCommunicationId; - /** Communication object used for device communication */ - DeviceCommunicationIF * communicationInterface = nullptr; - /** Cookie used for communication */ - CookieIF * comCookie; + /** + * @brief The message queue which wants to read all raw traffic + * If #isWiretappingActive all raw communication from and to the device + * will be sent to this queue + */ + MessageQueueId_t requestedRawTraffic = 0; - /** Health helper for HasHealthIF */ - HealthHelper healthHelper; - /** Mode helper for HasModesIF */ - ModeHelper modeHelper; - /** Parameter helper for ReceivesParameterMessagesIF */ - ParameterHelper parameterHelper; - /** Action helper for HasActionsIF */ - ActionHelper actionHelper; - /** Housekeeping Manager */ - //LocalDataPoolManager hkManager; + /** + * Pointer to the IPCStore. + * This caches the pointer received from the objectManager in the constructor. + */ + StorageManagerIF *IPCStore = nullptr; + /** The comIF object ID is cached for the intialize() function */ + object_id_t deviceCommunicationId; + /** Communication object used for device communication */ + DeviceCommunicationIF * communicationInterface = nullptr; + /** Cookie used for communication */ + CookieIF * comCookie; - /** - * @brief Information about commands - */ - struct DeviceCommandInfo { - //! Indicates if the command is already executing. - bool isExecuting; - //! Dynamic value to indicate how many replies are expected. - //! Inititated with 0. - uint8_t expectedReplies; - //! if this is != NO_COMMANDER, DHB was commanded externally and shall - //! report everything to commander. - MessageQueueId_t sendReplyTo; - }; - using DeviceCommandMap = std::map ; - /** - * Information about commands - */ - DeviceCommandMap deviceCommandMap; + /* Health helper for HasHealthIF */ + HealthHelper healthHelper; + /* Mode helper for HasModesIF */ + ModeHelper modeHelper; + /* Parameter helper for ReceivesParameterMessagesIF */ + ParameterHelper parameterHelper; + /* Action helper for HasActionsIF */ + ActionHelper actionHelper; + /* Housekeeping Manager */ + LocalDataPoolManager poolManager; - /** - * @brief Information about expected replies - * This is used to keep track of pending replies. - */ - struct DeviceReplyInfo { - //! The maximum number of cycles the handler should wait for a reply - //! to this command. - uint16_t maxDelayCycles; - //! The currently remaining cycles the handler should wait for a reply, - //! 0 means there is no reply expected - uint16_t delayCycles; - size_t replyLen = 0; //!< Expected size of the reply. - //! if this is !=0, the delayCycles will not be reset to 0 but to - //! maxDelayCycles - bool periodic = false; - //! The dataset used to access housekeeping data related to the - //! respective device reply. Will point to a dataset held by - //! the child handler (if one is specified) - // DataSetIF* dataSet = nullptr; - //! The command that expects this reply. - DeviceCommandMap::iterator command; - }; + /** + * @brief Information about commands + */ + struct DeviceCommandInfo { + //! Indicates if the command is already executing. + bool isExecuting; + //! Dynamic value to indicate how many replies are expected. + //! Inititated with 0. + uint8_t expectedReplies; + //! if this is != NO_COMMANDER, DHB was commanded externally and shall + //! report everything to commander. + MessageQueueId_t sendReplyTo; + }; + using DeviceCommandMap = std::map ; + /** + * Information about commands + */ + DeviceCommandMap deviceCommandMap; - using DeviceReplyMap = std::map ; - using DeviceReplyIter = DeviceReplyMap::iterator; - /** - * This map is used to check and track correct reception of all replies. - * - * It has multiple use: - * - It stores the information on pending replies. If a command is sent, - * the DeviceCommandInfo.count is incremented. - * - It is used to time-out missing replies. If a command is sent, the - * DeviceCommandInfo.DelayCycles is set to MaxDelayCycles. - * - It is queried to check if a reply from the device can be interpreted. - * scanForReply() returns the id of the command a reply was found for. - * The reply is ignored in the following cases: - * - No entry for the returned id was found - * - The deviceReplyInfo.delayCycles is == 0 - */ - DeviceReplyMap deviceReplyMap; + /** + * @brief Information about expected replies + * This is used to keep track of pending replies. + */ + struct DeviceReplyInfo { + //! The maximum number of cycles the handler should wait for a reply + //! to this command. + uint16_t maxDelayCycles; + //! The currently remaining cycles the handler should wait for a reply, + //! 0 means there is no reply expected + uint16_t delayCycles; + size_t replyLen = 0; //!< Expected size of the reply. + //! if this is !=0, the delayCycles will not be reset to 0 but to + //! maxDelayCycles + bool periodic = false; + //! The dataset used to access housekeeping data related to the + //! respective device reply. Will point to a dataset held by + //! the child handler (if one is specified) + LocalPoolDataSetBase* dataSet = nullptr; + //! The command that expects this reply. + DeviceCommandMap::iterator command; + }; - //! The MessageQueue used to receive device handler commands - //! and to send replies. - MessageQueueIF* commandQueue = nullptr; + using DeviceReplyMap = std::map ; + using DeviceReplyIter = DeviceReplyMap::iterator; + /** + * This map is used to check and track correct reception of all replies. + * + * It has multiple use: + * - It stores the information on pending replies. If a command is sent, + * the DeviceCommandInfo.count is incremented. + * - It is used to time-out missing replies. If a command is sent, the + * DeviceCommandInfo.DelayCycles is set to MaxDelayCycles. + * - It is queried to check if a reply from the device can be interpreted. + * scanForReply() returns the id of the command a reply was found for. + * The reply is ignored in the following cases: + * - No entry for the returned id was found + * - The deviceReplyInfo.delayCycles is == 0 + */ + DeviceReplyMap deviceReplyMap; - /** - * this is the datapool variable with the thermal state of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - uint32_t deviceThermalStatePoolId = PoolVariableIF::NO_PARAMETER; + //! The MessageQueue used to receive device handler commands + //! and to send replies. + MessageQueueIF* commandQueue = nullptr; - /** - * this is the datapool variable with the thermal request of the device - * - * can be set to PoolVariableIF::NO_PARAMETER to deactivate thermal checking - */ - uint32_t deviceThermalRequestPoolId = PoolVariableIF::NO_PARAMETER; + DeviceHandlerThermalSet* thermalSet = nullptr; - /** - * Optional Error code - * Can be set in doStartUp(), doShutDown() and doTransition() to signal cause for Transition failure. - */ - ReturnValue_t childTransitionFailure; + /** + * Optional Error code. Can be set in doStartUp(), doShutDown() and + * doTransition() to signal cause for Transition failure. + */ + ReturnValue_t childTransitionFailure; - uint32_t ignoreMissedRepliesCount = 0; //!< Counts if communication channel lost a reply, so some missed replys can be ignored. + /** Counts if communication channel lost a reply, so some missed + * replys can be ignored. */ + uint32_t ignoreMissedRepliesCount = 0; - FailureIsolationBase* fdirInstance; //!< Pointer to the used FDIR instance. If not provided by child, default class is instantiated. + /** Pointer to the used FDIR instance. If not provided by child, + * default class is instantiated. */ + FailureIsolationBase* fdirInstance; - HkSwitchHelper hkSwitcher; + //! To correctly delete the default instance. + bool defaultFDIRUsed; - bool defaultFDIRUsed; //!< To correctly delete the default instance. + //! Indicates if SWITCH_WENT_OFF was already thrown. + bool switchOffWasReported; - bool switchOffWasReported; //!< Indicates if SWITCH_WENT_OFF was already thrown. + /** Pointer to the task which executes this component, + is invalid before setTaskIF was called. */ + PeriodicTaskIF* executingTask = nullptr; - //! Pointer to the task which executes this component, is invalid - //! before setTaskIF was called. - PeriodicTaskIF* executingTask = nullptr; + //! Object which switches power on and off. + static object_id_t powerSwitcherId; - static object_id_t powerSwitcherId; //!< Object which switches power on and off. + //! Object which receives RAW data by default. + static object_id_t rawDataReceiverId; - static object_id_t rawDataReceiverId; //!< Object which receives RAW data by default. + //! Object which may be the root cause of an identified fault. + static object_id_t defaultFdirParentId; - static object_id_t defaultFdirParentId; //!< Object which may be the root cause of an identified fault. - /** - * Helper function to report a missed reply - * - * Can be overwritten by children to act on missed replies or to fake reporting Id. - * - * @param id of the missed reply - */ - virtual void missedReply(DeviceCommandId_t id); + /** + * @brief Send a reply to a received device handler command. + * + * This also resets #DeviceHandlerCommand to 0. + * + * @param reply the reply type + * @param parameter parameter for the reply + */ + void replyReturnvalueToCommand(ReturnValue_t status, uint32_t parameter = 0); + /** + * TODO: Whats the difference between this and the upper command? + * @param status + * @param parameter + */ + void replyToCommand(ReturnValue_t status, uint32_t parameter = 0); - /** - * Send a reply to a received device handler command. - * - * This also resets #DeviceHandlerCommand to 0. - * - * @param reply the reply type - * @param parameter parameter for the reply - */ - void replyReturnvalueToCommand(ReturnValue_t status, - uint32_t parameter = 0); + /** + * Do the transition to the main modes (MODE_ON, MODE_NORMAL and MODE_RAW). + * + * If the transition is complete, the mode should be set to the target mode, + * which can be deduced from the current mode which is + * [_MODE_TO_ON, _MODE_TO_NORMAL, _MODE_TO_RAW] + * + * The intended target submode is already set. + * The origin submode can be read in subModeFrom. + * + * If the transition can not be completed, the child class can try to reach + * an working mode by setting the mode either directly + * or setting the mode to an transitional mode (TO_ON, TO_NORMAL, TO_RAW) + * if the device needs to be reconfigured. + * + * If nothing works, the child class can wait for the timeout and the base + * class will reset the mode to the mode where the transition + * originated from (the child should report the reason for the failed transition). + * + * The intended way to send commands is to set a flag (enum) indicating + * which command is to be sent here and then to check in + * buildTransitionCommand() for the flag. This flag can also be used by + * doStartUp() and doShutDown() to get a nice and clean implementation of + * buildTransitionCommand() without switching through modes. + * + * When the the condition for the completion of the transition is met, the + * mode can be set, for example in the scanForReply() function. + * + * The default implementation goes into the target mode directly. + * + * #transitionFailure can be set to a failure code indicating the reason + * for a failed transition + * + * @param modeFrom + * The mode the transition originated from: + * [MODE_ON, MODE_NORMAL, MODE_RAW and _MODE_POWER_DOWN (if the mode changed + * from _MODE_START_UP to _MODE_TO_ON)] + * @param subModeFrom the subMode of modeFrom + */ + virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom); - void replyToCommand(ReturnValue_t status, uint32_t parameter = 0); + /** + * Get the communication action for the current step. + * The step number can be read from #pstStep. + * @return The communication action to execute in this step + */ + virtual CommunicationAction getComAction(); - /** - * Set the device handler mode - * - * Sets #timeoutStart with every call. - * - * Sets #transitionTargetMode if necessary so transitional states can be - * entered from everywhere without breaking the state machine - * (which relies on a correct #transitionTargetMode). - * - * The submode is left unchanged. - * - * - * @param newMode - */ - void setMode(Mode_t newMode); + /** + * Checks state of switches in conjunction with mode and triggers an event + * if they don't fit. + */ + virtual void checkSwitchState(); - /** - * @overload - * @param submode - */ - void setMode(Mode_t newMode, Submode_t submode); + /** + * Reserved for the rare case where a device needs to perform additional + * operation cyclically in OFF mode. + */ + virtual void doOffActivity(); - /** - * Do the transition to the main modes (MODE_ON, MODE_NORMAL and MODE_RAW). - * - * If the transition is complete, the mode should be set to the target mode, - * which can be deduced from the current mode which is - * [_MODE_TO_ON, _MODE_TO_NORMAL, _MODE_TO_RAW] - * - * The intended target submode is already set. - * The origin submode can be read in subModeFrom. - * - * If the transition can not be completed, the child class can try to reach - * an working mode by setting the mode either directly - * or setting the mode to an transitional mode (TO_ON, TO_NORMAL, TO_RAW) - * if the device needs to be reconfigured. - * - * If nothing works, the child class can wait for the timeout and the base - * class will reset the mode to the mode where the transition - * originated from (the child should report the reason for the failed transition). - * - * The intended way to send commands is to set a flag (enum) indicating - * which command is to be sent here and then to check in - * buildTransitionCommand() for the flag. This flag can also be used by - * doStartUp() and doShutDown() to get a nice and clean implementation of - * buildTransitionCommand() without switching through modes. - * - * When the the condition for the completion of the transition is met, the - * mode can be set, for example in the scanForReply() function. - * - * The default implementation goes into the target mode directly. - * - * #transitionFailure can be set to a failure code indicating the reason - * for a failed transition - * - * @param modeFrom - * The mode the transition originated from: - * [MODE_ON, MODE_NORMAL, MODE_RAW and _MODE_POWER_DOWN (if the mode changed - * from _MODE_START_UP to _MODE_TO_ON)] - * @param subModeFrom the subMode of modeFrom - */ - virtual void doTransition(Mode_t modeFrom, Submode_t subModeFrom); + /** + * Reserved for the rare case where a device needs to perform additional + * operation cyclically in ON mode. + */ + virtual void doOnActivity(); - /** - * Is the combination of mode and submode valid? - * - * @param mode - * @param submode - * @return - * - @c RETURN_OK if valid - * - @c RETURN_FAILED if invalid - */ - virtual ReturnValue_t isModeCombinationValid(Mode_t mode, - Submode_t submode); + /** + * Required for HasLocalDataPoolIF, return a handle to the local pool manager. + * @return + */ + LocalDataPoolManager* getHkManagerHandle() override; - /** - * Get the Rmap action for the current step. - * - * The step number can be read from #pstStep. - * - * @return The Rmap action to execute in this step - */ + /** + * Returns the delay cycle count of a reply. + * A count != 0 indicates that the command is already executed. + * @param deviceCommand The command to look for + * @return + * The current delay count. If the command does not exist (should never + * happen) it returns 0. + */ + uint8_t getReplyDelayCycles(DeviceCommandId_t deviceCommand); - virtual CommunicationAction_t getComAction(); + /** + * Calls replyRawData() with #defaultRawReceiver, but checks if wiretapping + * is active and if so, does not send the data as the wiretapping will have + * sent it already + */ + void replyRawReplyIfnotWiretapped(const uint8_t *data, size_t len); - /** - * Build the device command to send for raw mode. - * - * This is only called in @c MODE_RAW. It is for the rare case that in raw mode packets - * are to be sent by the handler itself. It is NOT needed for the raw commanding service. - * Its only current use is in the STR handler which gets its raw packets from a different - * source. - * Also it can be used for transitional commands, to get the device ready for @c MODE_RAW - * - * As it is almost never used, there is a default implementation returning @c NOTHING_TO_SEND. - * - * #rawPacket and #rawPacketLen must be set by this method to the packet to be sent. - * - * @param[out] id the device command id built - * @return - * - @c RETURN_OK when a command is to be sent - * - not @c NOTHING_TO_SEND when no command is to be sent - */ - virtual ReturnValue_t buildChildRawCommand(); + /** + * Enable the reply checking for a command + * + * Is only called, if the command was sent (i.e. the getWriteReply was + * successful). Must ensure that all replies are activated and correctly + * linked to the command that initiated it. + * The default implementation looks for a reply with the same id as the + * command id in the replyMap or uses the alternativeReplyId if flagged so. + * When found, copies maxDelayCycles to delayCycles in the reply information + * and sets the command to expect one reply. + * + * Can be overwritten by the child, if a command activates multiple replies + * or replyId differs from commandId. + * Notes for child implementations: + * - If the command was not found in the reply map, + * NO_REPLY_EXPECTED MUST be returned. + * - A failure code may be returned if something went fundamentally wrong. + * + * @param deviceCommand + * @return - RETURN_OK if a reply was activated. + * - NO_REPLY_EXPECTED if there was no reply found. This is not an + * error case as many commands do not expect a reply. + */ + virtual ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator command, + uint8_t expectedReplies = 1, bool useAlternateId = false, + DeviceCommandId_t alternateReplyID = 0); - /** - * Returns the delay cycle count of a reply. - * A count != 0 indicates that the command is already executed. - * @param deviceCommand The command to look for - * @return The current delay count. If the command does not exist (should never happen) it returns 0. - */ - uint8_t getReplyDelayCycles(DeviceCommandId_t deviceCommand); + /** + * @brief Build the device command to send for raw mode. + * @details + * This is only called in @c MODE_RAW. It is for the rare case that in + * raw mode packets are to be sent by the handler itself. It is NOT needed + * for the raw commanding service. Its only current use is in the STR + * handler which gets its raw packets from a different source. + * Also it can be used for transitional commands, to get the device ready + * for @c MODE_RAW + * + * As it is almost never used, there is a default implementation + * returning @c NOTHING_TO_SEND. + * + * #rawPacket and #rawPacketLen must be set by this method to the packet + * to be sent. + * + * @param[out] id the device command id built + * @return + * - @c RETURN_OK when a command is to be sent + * - not @c NOTHING_TO_SEND when no command is to be sent + */ + virtual ReturnValue_t buildChildRawCommand(); - /** - * Construct a command reply containing a raw reply. - * - * It gets space in the #IPCStore, copies data there, then sends a raw reply - * containing the store address. - * - * This method is virtual, as the STR has a different channel to send raw replies - * and overwrites it accordingly. - * - * @param data data to send - * @param len length of @c data - * @param sendTo the messageQueueId of the one to send to - * @param isCommand marks the raw data as a command, the message then will be of type raw_command - */ - virtual void replyRawData(const uint8_t *data, size_t len, - MessageQueueId_t sendTo, bool isCommand = false); + /** + * @brief Construct a command reply containing a raw reply. + * @details + * It gets space in the #IPCStore, copies data there, then sends a raw reply + * containing the store address. This method is virtual, as devices can have different channels + * to send raw replies + * + * @param data data to send + * @param len length of @c data + * @param sendTo the messageQueueId of the one to send to + * @param isCommand marks the raw data as a command, the message then + * will be of type raw_command + */ + virtual void replyRawData(const uint8_t *data, size_t len, + MessageQueueId_t sendTo, bool isCommand = false); - /** - * Calls replyRawData() with #defaultRawReceiver, but checks if wiretapping is active and if so, - * does not send the Data as the wiretapping will have sent it already - */ - void replyRawReplyIfnotWiretapped(const uint8_t *data, size_t len); + /** + * Get the state of the PCDU switches in the local datapool + * @return + * - @c PowerSwitchIF::SWITCH_ON if all switches specified + * by #switches are on + * - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by + * #switches are off + * - @c PowerSwitchIF::RETURN_FAILED if an error occured + */ + ReturnValue_t getStateOfSwitches(); - /** - * notify child about mode change - */ - virtual void modeChanged(void); + /** + * Children can overwrite this function to suppress checking of the + * command Queue + * + * This can be used when the child does not want to receive a command in + * a certain situation. Care must be taken that checking is not + * permanentely disabled as this would render the handler unusable. + * + * @return whether checking the queue should NOT be done + */ + virtual bool dontCheckQueue(); - /** - * Enable the reply checking for a command - * - * Is only called, if the command was sent (ie the getWriteReply was successful). - * Must ensure that all replies are activated and correctly linked to the command that initiated it. - * The default implementation looks for a reply with the same id as the command id in the replyMap or - * uses the alternativeReplyId if flagged so. - * When found, copies maxDelayCycles to delayCycles in the reply information and sets the command to - * expect one reply. - * - * Can be overwritten by the child, if a command activates multiple replies - * or replyId differs from commandId. - * Notes for child implementations: - * - If the command was not found in the reply map, NO_REPLY_EXPECTED MUST be returned. - * - A failure code may be returned if something went fundamentally wrong. - * - * @param deviceCommand - * @return - RETURN_OK if a reply was activated. - * - NO_REPLY_EXPECTED if there was no reply found. This is not an - * error case as many commands do not expect a reply. - */ - virtual ReturnValue_t enableReplyInReplyMap(DeviceCommandMap::iterator cmd, - uint8_t expectedReplies = 1, bool useAlternateId = false, - DeviceCommandId_t alternateReplyID = 0); + Mode_t getBaseMode(Mode_t transitionMode); - /** - * get the state of the PCDU switches in the datapool - * - * @return - * - @c PowerSwitchIF::SWITCH_ON if all switches specified by #switches are on - * - @c PowerSwitchIF::SWITCH_OFF one of the switches specified by #switches are off - * - @c PowerSwitchIF::RETURN_FAILED if an error occured - */ - ReturnValue_t getStateOfSwitches(void); + bool isAwaitingReply(); - /** - * set all datapool variables that are update periodically in normal mode invalid - * - * Child classes should provide an implementation which sets all those variables invalid - * which are set periodically during any normal mode. - */ - virtual void setNormalDatapoolEntriesInvalid() = 0; + void handleDeviceTM(SerializeIF *dataSet, DeviceCommandId_t replyId, + bool forceDirectTm = false); + // void handleDeviceTM(uint8_t* data, size_t dataSize, DeviceCommandId_t replyId, + // bool forceDirectTm); - /** - * build a list of sids and pass it to the #hkSwitcher - */ - virtual void changeHK(Mode_t mode, Submode_t submode, bool enable); + virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, + uint32_t *msToReachTheMode); - /** - * Children can overwrite this function to suppress checking of the command Queue - * - * This can be used when the child does not want to receive a command in a certain - * situation. Care must be taken that checking is not permanentely disabled as this - * would render the handler unusable. - * - * @return whether checking the queue should NOT be done - */ - virtual bool dontCheckQueue(); + virtual ReturnValue_t letChildHandleMessage(CommandMessage *message); - Mode_t getBaseMode(Mode_t transitionMode); + /** + * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" + * faster about executed events. + * This is a bit sneaky, but improves responsiveness of the device FDIR. + * @param event The event to be thrown + * @param parameter1 Optional parameter 1 + * @param parameter2 Optional parameter 2 + */ + void triggerEvent(Event event, uint32_t parameter1 = 0, + uint32_t parameter2 = 0); + /** + * Same as triggerEvent, but for forwarding if object is used as proxy. + */ + virtual void forwardEvent(Event event, uint32_t parameter1 = 0, + uint32_t parameter2 = 0) const; - bool isAwaitingReply(); + /** + * Checks if current mode is transitional mode. + * @return true if mode is transitional, false else. + */ + bool isTransitionalMode(); - void handleDeviceTM(SerializeIF *dataSet, DeviceCommandId_t commandId, - bool neverInDataPool = false, bool forceDirectTm = false); + /** + * Checks if current handler state allows reception of external device commands. + * Default implementation allows commands only in plain MODE_ON and MODE_NORMAL. + * @return RETURN_OK if commands are accepted, anything else otherwise. + */ + virtual ReturnValue_t acceptExternalDeviceCommands(); - virtual ReturnValue_t checkModeCommand(Mode_t mode, Submode_t submode, - uint32_t *msToReachTheMode); - virtual void startTransition(Mode_t mode, Submode_t submode); - virtual void setToExternalControl(); - virtual void announceMode(bool recursive); + bool commandIsExecuting(DeviceCommandId_t commandId); - virtual ReturnValue_t letChildHandleMessage(CommandMessage *message); + /** + * set all switches returned by getSwitches() + * + * @param onOff on == @c SWITCH_ON; off != @c SWITCH_ON + */ + void commandSwitch(ReturnValue_t onOff); - /** - * Overwrites SystemObject::triggerEvent in order to inform FDIR"Helper" faster about executed events. - * This is a bit sneaky, but improves responsiveness of the device FDIR. - * @param event The event to be thrown - * @param parameter1 Optional parameter 1 - * @param parameter2 Optional parameter 2 - */ - void triggerEvent(Event event, uint32_t parameter1 = 0, - uint32_t parameter2 = 0); - /** - * Same as triggerEvent, but for forwarding if object is used as proxy. - */ - virtual void forwardEvent(Event event, uint32_t parameter1 = 0, - uint32_t parameter2 = 0) const; - /** - * Checks state of switches in conjunction with mode and triggers an event if they don't fit. - */ - virtual void checkSwitchState(); - - /** - * Reserved for the rare case where a device needs to perform additional operation cyclically in OFF mode. - */ - virtual void doOffActivity(); - - /** - * Reserved for the rare case where a device needs to perform additional operation cyclically in ON mode. - */ - virtual void doOnActivity(); - - /** - * Checks if current mode is transitional mode. - * @return true if mode is transitional, false else. - */ - bool isTransitionalMode(); - - /** - * Checks if current handler state allows reception of external device commands. - * Default implementation allows commands only in plain MODE_ON and MODE_NORMAL. - * @return RETURN_OK if commands are accepted, anything else otherwise. - */ - virtual ReturnValue_t acceptExternalDeviceCommands(); - - bool commandIsExecuting(DeviceCommandId_t commandId); - - /** - * set all switches returned by getSwitches() - * - * @param onOff on == @c SWITCH_ON; off != @c SWITCH_ON - */ - void commandSwitch(ReturnValue_t onOff); private: - /** - * State a cookie is in. - * - * Used to keep track of the state of the RMAP communication. - */ - enum CookieState_t { - COOKIE_UNUSED, //!< The Cookie is unused - COOKIE_WRITE_READY, //!< There's data available to send. - COOKIE_READ_SENT, //!< A sendRead command was sent with this cookie - COOKIE_WRITE_SENT //!< A sendWrite command was sent with this cookie - }; - /** - * Information about a cookie. - * - * This is stored in a map for each cookie, to not only track the state, but also information - * about the sent command. Tracking this information is needed as - * the state of a commandId (waiting for reply) is done when a RMAP write reply is received. - */ - struct CookieInfo { - CookieState_t state; - DeviceCommandMap::iterator pendingCommand; - }; + /** + * State a cookie is in. + * + * Used to keep track of the state of the RMAP communication. + */ + enum CookieState_t { + COOKIE_UNUSED, //!< The Cookie is unused + COOKIE_WRITE_READY, //!< There's data available to send. + COOKIE_READ_SENT, //!< A sendRead command was sent with this cookie + COOKIE_WRITE_SENT //!< A sendWrite command was sent with this cookie + }; + /** + * Information about a cookie. + * + * This is stored in a map for each cookie, to not only track the state, + * but also information about the sent command. Tracking this information + * is needed as the state of a commandId (waiting for reply) is done when a + * write reply is received. + */ + struct CookieInfo { + CookieState_t state; + DeviceCommandMap::iterator pendingCommand; + }; - /** - * @brief Info about the #cookie - * Used to track the state of the communication - */ - CookieInfo cookieInfo; + /** + * @brief Info about the #cookie + * Used to track the state of the communication + */ + CookieInfo cookieInfo; - /** the object used to set power switches */ - PowerSwitchIF *powerSwitcher = nullptr; + /** the object used to set power switches */ + PowerSwitchIF *powerSwitcher = nullptr; - /** - * @brief Used for timing out mode transitions. - * Set when setMode() is called. - */ - uint32_t timeoutStart = 0; + /** HK destination can also be set individually */ + object_id_t hkDestination = objects::NO_OBJECT; - /** - * Delay for the current mode transition, used for time out - */ - uint32_t childTransitionDelay; + /** + * @brief Used for timing out mode transitions. + * Set when setMode() is called. + */ + uint32_t timeoutStart = 0; - /** - * @brief The mode the current transition originated from - * - * This is private so the child can not change it and fuck up the timeouts - * - * IMPORTANT: This is not valid during _MODE_SHUT_DOWN and _MODE_START_UP!! - * (it is _MODE_POWER_DOWN during this modes) - * - * is element of [MODE_ON, MODE_NORMAL, MODE_RAW] - */ - Mode_t transitionSourceMode; + bool setStartupImmediately = false; - /** - * the submode of the source mode during a transition - */ - Submode_t transitionSourceSubMode; + /** + * Delay for the current mode transition, used for time out + */ + uint32_t childTransitionDelay; - /** - * read the command queue - */ - void readCommandQueue(void); + /** + * @brief The mode the current transition originated from + * + * This is private so the child can not change it and mess up the timeouts + * + * IMPORTANT: This is not valid during _MODE_SHUT_DOWN and _MODE_START_UP!! + * (it is _MODE_POWER_DOWN during this modes) + * + * is element of [MODE_ON, MODE_NORMAL, MODE_RAW] + */ + Mode_t transitionSourceMode; - /** - * Handle the device handler mode. - * - * - checks whether commands are valid for the current mode, rejects them accordingly - * - checks whether commanded mode transitions are required and calls handleCommandedModeTransition() - * - does the necessary action for the current mode or calls doChildStateMachine in modes @c MODE_TO_ON and @c MODE_TO_OFF - * - actions that happen in transitions (eg setting a timeout) are handled in setMode() - */ - void doStateMachine(void); + /** + * the submode of the source mode during a transition + */ + Submode_t transitionSourceSubMode; - void buildRawDeviceCommand(CommandMessage* message); - void buildInternalCommand(void); + /** + * read the command queue + */ + void readCommandQueue(void); -// /** -// * Send a reply with the current mode and submode. -// */ -// void announceMode(void); + /** + * Handle the device handler mode. + * + * - checks whether commands are valid for the current mode, rejects + * them accordingly + * - checks whether commanded mode transitions are required and calls + * handleCommandedModeTransition() + * - does the necessary action for the current mode or calls + * doChildStateMachine in modes @c MODE_TO_ON and @c MODE_TO_OFF + * - actions that happen in transitions (e.g. setting a timeout) are + * handled in setMode() + */ + void doStateMachine(void); - /** - * Decrement the counter for the timout of replies. - * - * This is called at the beginning of each cycle. It checks whether a reply has timed out (that means a reply was expected - * but not received). - */ - void decrementDeviceReplyMap(void); + void buildRawDeviceCommand(CommandMessage* message); + void buildInternalCommand(void); + /** + * Decrement the counter for the timout of replies. + * + * This is called at the beginning of each cycle. It checks whether a + * reply has timed out (that means a reply was expected but not received). + */ + void decrementDeviceReplyMap(void); + /** + * Convenience function to handle a reply. + * + * Called after scanForReply() has found a packet. Checks if the found ID + * is in the #deviceCommandMap, if so, calls + * #interpretDeviceReply for further action. + * + * It also resets the timeout counter for the command id. + * + * @param data the found packet + * @param id the found id + * @foundLen the length of the packet + */ + void handleReply(const uint8_t *data, DeviceCommandId_t id, uint32_t foundLen); + void replyToReply(DeviceReplyMap::iterator iter, ReturnValue_t status); - /** - * Convenience function to handle a reply. - * - * Called after scanForReply() has found a packet. Checks if the found id is in the #deviceCommandMap, if so, - * calls interpretDeviceReply(DeviceCommandId_t id, const uint8_t *packet) for further action. - * - * It also resets the timeout counter for the command id. - * - * @param data the found packet - * @param id the found id - * @foundLen the length of the packet - */ - void handleReply(const uint8_t *data, DeviceCommandId_t id, uint32_t foundLen); + /** + * Build and send a command to the device. + * + * This routine checks whether a raw or direct command has been received, + * checks the content of the received command and calls + * buildCommandFromCommand() for direct commands or sets #rawpacket + * to the received raw packet. + * If no external command is received or the received command is invalid and + * the current mode is @c MODE_NORMAL or a transitional mode, it asks the + * child class to build a command (via getNormalDeviceCommand() or + * getTransitionalDeviceCommand() and buildCommand()) and + * sends the command via RMAP. + */ + void doSendWrite(void); + /** + * Check if the RMAP sendWrite action was successful. + * + * Depending on the result, the following is done + * - if the device command was external commanded, a reply is sent + * indicating the result + * - if the action was successful, the reply timout counter is initialized + */ + void doGetWrite(void); + /** + * Send a RMAP getRead command. + * + * The size of the getRead command is #maxDeviceReplyLen. + * This is always executed, independently from the current mode. + */ + void doSendRead(void); + /** + * Check the getRead reply and the contained data. + * + * If data was received scanForReply() and, if successful, handleReply() + * are called. If the current mode is @c MODE_RAW, the received packet + * is sent to the commanding object via commandQueue. + */ + void doGetRead(void); - void replyToReply(DeviceReplyMap::iterator iter, ReturnValue_t status); - /** - * Build and send a command to the device. - * - * This routine checks whether a raw or direct command has been received, checks the content of the received command and - * calls buildCommandFromCommand() for direct commands or sets #rawpacket to the received raw packet. - * If no external command is received or the received command is invalid and the current mode is @c MODE_NORMAL or a transitional mode, - * it asks the child class to build a command (via getNormalDeviceCommand() or getTransitionalDeviceCommand() and buildCommand()) and - * sends the command via RMAP. - */ - void doSendWrite(void); + /** + * Retrive data from the #IPCStore. + * + * @param storageAddress + * @param[out] data + * @param[out] len + * @return + * - @c RETURN_OK @c data is valid + * - @c RETURN_FAILED IPCStore is nullptr + * - the return value from the IPCStore if it was not @c RETURN_OK + */ + ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, + uint32_t *len); - /** - * Check if the RMAP sendWrite action was successful. - * - * Depending on the result, the following is done - * - if the device command was external commanded, a reply is sent indicating the result - * - if the action was successful, the reply timout counter is initialized - */ - void doGetWrite(void); + /** + * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW, nothing else! + */ + void setTransition(Mode_t modeTo, Submode_t submodeTo); - /** - * Send a RMAP getRead command. - * - * The size of the getRead command is #maxDeviceReplyLen. - * This is always executed, independently from the current mode. - */ - void doSendRead(void); + /** + * Calls the right child function for the transitional submodes + */ + void callChildStatemachine(); - /** - * Check the getRead reply and the contained data. - * - * If data was received scanForReply() and, if successful, handleReply() are called. - * If the current mode is @c MODE_RAW, the received packet is sent to the commanding object - * via commandQueue. - */ - void doGetRead(void); + ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message); - /** - * Retrive data from the #IPCStore. - * - * @param storageAddress - * @param[out] data - * @param[out] len - * @return - * - @c RETURN_OK @c data is valid - * - @c RETURN_FAILED IPCStore is NULL - * - the return value from the IPCStore if it was not @c RETURN_OK - */ - ReturnValue_t getStorageData(store_address_t storageAddress, uint8_t **data, - uint32_t *len); + virtual dur_millis_t getPeriodicOperationFrequency() const override; + void parseReply(const uint8_t* receivedData, + size_t receivedDataLen); - /** - * @param modeTo either @c MODE_ON, MODE_NORMAL or MODE_RAW NOTHING ELSE!!! - */ - void setTransition(Mode_t modeTo, Submode_t submodeTo); + void handleTransitionToOnMode(Mode_t commandedMode, + Submode_t commandedSubmode); - /** - * calls the right child function for the transitional submodes - */ - void callChildStatemachine(); - - /** - * Switches the channel of the cookie used for the communication - * - * - * @param newChannel the object Id of the channel to switch to - * @return - * - @c RETURN_OK when cookie was changed - * - @c RETURN_FAILED when cookies could not be changed, eg because the newChannel is not enabled - * - @c returnvalues of RMAPChannelIF::isActive() - */ - ReturnValue_t switchCookieChannel(object_id_t newChannelId); - - ReturnValue_t handleDeviceHandlerMessage(CommandMessage *message); - - virtual ReturnValue_t initializeAfterTaskCreation() override; - - void parseReply(const uint8_t* receivedData, - size_t receivedDataLen); + /** + * Generic internal printer function which also handles printing the object ID. + * @param errorType + * @param functionName + * @param errorCode + * @param errorPrint + */ + void printWarningOrError(sif::OutputTypes errorType, + const char* functionName, + ReturnValue_t errorCode = HasReturnvaluesIF::RETURN_FAILED, + const char* errorPrint = nullptr); }; -#endif /* FRAMEWORK_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ - +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERBASE_H_ */ diff --git a/devicehandlers/DeviceHandlerFailureIsolation.cpp b/devicehandlers/DeviceHandlerFailureIsolation.cpp index 9fbe71d85..ba118090b 100644 --- a/devicehandlers/DeviceHandlerFailureIsolation.cpp +++ b/devicehandlers/DeviceHandlerFailureIsolation.cpp @@ -169,8 +169,10 @@ void DeviceHandlerFailureIsolation::clearFaultCounters() { ReturnValue_t DeviceHandlerFailureIsolation::initialize() { ReturnValue_t result = FailureIsolationBase::initialize(); if (result != HasReturnvaluesIF::RETURN_OK) { +#if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "DeviceHandlerFailureIsolation::initialize: Could not" " initialize FailureIsolationBase." << std::endl; +#endif return result; } ConfirmsFailuresIF* power = objectManager->get( @@ -191,7 +193,7 @@ void DeviceHandlerFailureIsolation::triggerEvent(Event event, uint32_t parameter uint32_t parameter2) { //Do not throw error events if fdirState != none. //This will still forward MODE and HEALTH INFO events in any case. - if (fdirState == NONE || EVENT::getSeverity(event) == SEVERITY::INFO) { + if (fdirState == NONE || event::getSeverity(event) == severity::INFO) { FailureIsolationBase::triggerEvent(event, parameter1, parameter2); } } @@ -201,26 +203,26 @@ bool DeviceHandlerFailureIsolation::isFdirActionInProgress() { } void DeviceHandlerFailureIsolation::startRecovery(Event reason) { - throwFdirEvent(FDIR_STARTS_RECOVERY, EVENT::getEventId(reason)); + throwFdirEvent(FDIR_STARTS_RECOVERY, event::getEventId(reason)); setOwnerHealth(HasHealthIF::NEEDS_RECOVERY); setFdirState(RECOVERY_ONGOING); } ReturnValue_t DeviceHandlerFailureIsolation::getParameter(uint8_t domainId, - uint16_t parameterId, ParameterWrapper* parameterWrapper, + uint8_t uniqueId, ParameterWrapper* parameterWrapper, const ParameterWrapper* newValues, uint16_t startAtIndex) { - ReturnValue_t result = strangeReplyCount.getParameter(domainId, parameterId, + ReturnValue_t result = strangeReplyCount.getParameter(domainId, uniqueId, parameterWrapper, newValues, startAtIndex); if (result != INVALID_DOMAIN_ID) { return result; } - result = missedReplyCount.getParameter(domainId, parameterId, - parameterWrapper, newValues, startAtIndex); + result = missedReplyCount.getParameter(domainId, uniqueId, parameterWrapper, newValues, + startAtIndex); if (result != INVALID_DOMAIN_ID) { return result; } - result = recoveryCounter.getParameter(domainId, parameterId, - parameterWrapper, newValues, startAtIndex); + result = recoveryCounter.getParameter(domainId, uniqueId, parameterWrapper, newValues, + startAtIndex); if (result != INVALID_DOMAIN_ID) { return result; } @@ -228,7 +230,7 @@ ReturnValue_t DeviceHandlerFailureIsolation::getParameter(uint8_t domainId, } void DeviceHandlerFailureIsolation::setFaulty(Event reason) { - throwFdirEvent(FDIR_TURNS_OFF_DEVICE, EVENT::getEventId(reason)); + throwFdirEvent(FDIR_TURNS_OFF_DEVICE, event::getEventId(reason)); setOwnerHealth(HasHealthIF::FAULTY); setFdirState(AWAIT_SHUTDOWN); } @@ -247,6 +249,16 @@ bool DeviceHandlerFailureIsolation::isFdirInActionOrAreWeFaulty( } return true; } + + if (owner == nullptr) { + // Configuration error. +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "DeviceHandlerFailureIsolation::" + << "isFdirInActionOrAreWeFaulty: Owner not set!" << std::endl; +#endif + return false; + } + if (owner->getHealth() == HasHealthIF::FAULTY || owner->getHealth() == HasHealthIF::PERMANENT_FAULTY) { //Ignore all events in case device is already faulty. diff --git a/devicehandlers/DeviceHandlerFailureIsolation.h b/devicehandlers/DeviceHandlerFailureIsolation.h index 8a3fd9dd9..016b18b3a 100644 --- a/devicehandlers/DeviceHandlerFailureIsolation.h +++ b/devicehandlers/DeviceHandlerFailureIsolation.h @@ -17,9 +17,9 @@ public: ReturnValue_t initialize(); void triggerEvent(Event event, uint32_t parameter1 = 0, uint32_t parameter2 = 0);bool isFdirActionInProgress(); - virtual ReturnValue_t getParameter(uint8_t domainId, uint16_t parameterId, - ParameterWrapper *parameterWrapper, - const ParameterWrapper *newValues, uint16_t startAtIndex); + virtual ReturnValue_t getParameter(uint8_t domainId, uint8_t uniqueId, + ParameterWrapper *parameterWrapper, const ParameterWrapper *newValues, + uint16_t startAtIndex); protected: FaultCounter strangeReplyCount; diff --git a/devicehandlers/DeviceHandlerIF.h b/devicehandlers/DeviceHandlerIF.h index 52a3be4d0..fc31cce09 100644 --- a/devicehandlers/DeviceHandlerIF.h +++ b/devicehandlers/DeviceHandlerIF.h @@ -1,12 +1,20 @@ -#ifndef DEVICEHANDLERIF_H_ -#define DEVICEHANDLERIF_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ -#include "../action/HasActionsIF.h" #include "DeviceHandlerMessage.h" + +#include "../datapoollocal/localPoolDefinitions.h" +#include "../action/HasActionsIF.h" #include "../events/Event.h" #include "../modes/HasModesIF.h" #include "../ipc/MessageQueueSenderIF.h" +/** + * This is used to uniquely identify commands that are sent to a device + * The values are defined in the device-specific implementations + */ +using DeviceCommandId_t = uint32_t; + /** * @brief This is the Interface used to communicate with a device handler. * @details Includes all expected return values, events and modes. @@ -14,9 +22,14 @@ */ class DeviceHandlerIF { public: + static const DeviceCommandId_t RAW_COMMAND_ID = -1; + static const DeviceCommandId_t NO_COMMAND_ID = -2; - static const uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; - static const uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; + static constexpr uint8_t TRANSITION_MODE_CHILD_ACTION_MASK = 0x20; + static constexpr uint8_t TRANSITION_MODE_BASE_ACTION_MASK = 0x10; + + using dh_heater_request_t = uint8_t; + using dh_thermal_state_t = int8_t; /** * @brief This is the mode the device handler is in. @@ -47,6 +60,8 @@ public: //! This is a transitional state which can not be commanded. //! The device handler performs all actions and commands to get the device //! shut down. When the device is off, the mode changes to @c MODE_OFF. + //! It is possible to set the mode to _MODE_SHUT_DOWN to use the to off + //! transition if available. static const Mode_t _MODE_SHUT_DOWN = TRANSITION_MODE_CHILD_ACTION_MASK | 6; //! It is possible to set the mode to _MODE_TO_ON to use the to on //! transition if available. @@ -81,22 +96,22 @@ public: static const Mode_t _MODE_SWITCH_IS_OFF = TRANSITION_MODE_BASE_ACTION_MASK | 5; static const uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::CDH; - static const Event DEVICE_BUILDING_COMMAND_FAILED = MAKE_EVENT(0, SEVERITY::LOW); - static const Event DEVICE_SENDING_COMMAND_FAILED = MAKE_EVENT(1, SEVERITY::LOW); - static const Event DEVICE_REQUESTING_REPLY_FAILED = MAKE_EVENT(2, SEVERITY::LOW); - static const Event DEVICE_READING_REPLY_FAILED = MAKE_EVENT(3, SEVERITY::LOW); - static const Event DEVICE_INTERPRETING_REPLY_FAILED = MAKE_EVENT(4, SEVERITY::LOW); - static const Event DEVICE_MISSED_REPLY = MAKE_EVENT(5, SEVERITY::LOW); - static const Event DEVICE_UNKNOWN_REPLY = MAKE_EVENT(6, SEVERITY::LOW); - static const Event DEVICE_UNREQUESTED_REPLY = MAKE_EVENT(7, SEVERITY::LOW); - static const Event INVALID_DEVICE_COMMAND = MAKE_EVENT(8, SEVERITY::LOW); //!< Indicates a SW bug in child class. - static const Event MONITORING_LIMIT_EXCEEDED = MAKE_EVENT(9, SEVERITY::LOW); - static const Event MONITORING_AMBIGUOUS = MAKE_EVENT(10, SEVERITY::HIGH); + static const Event DEVICE_BUILDING_COMMAND_FAILED = MAKE_EVENT(0, severity::LOW); + static const Event DEVICE_SENDING_COMMAND_FAILED = MAKE_EVENT(1, severity::LOW); + static const Event DEVICE_REQUESTING_REPLY_FAILED = MAKE_EVENT(2, severity::LOW); + static const Event DEVICE_READING_REPLY_FAILED = MAKE_EVENT(3, severity::LOW); + static const Event DEVICE_INTERPRETING_REPLY_FAILED = MAKE_EVENT(4, severity::LOW); + static const Event DEVICE_MISSED_REPLY = MAKE_EVENT(5, severity::LOW); + static const Event DEVICE_UNKNOWN_REPLY = MAKE_EVENT(6, severity::LOW); + static const Event DEVICE_UNREQUESTED_REPLY = MAKE_EVENT(7, severity::LOW); + static const Event INVALID_DEVICE_COMMAND = MAKE_EVENT(8, severity::LOW); //!< Indicates a SW bug in child class. + static const Event MONITORING_LIMIT_EXCEEDED = MAKE_EVENT(9, severity::LOW); + static const Event MONITORING_AMBIGUOUS = MAKE_EVENT(10, severity::HIGH); static const uint8_t INTERFACE_ID = CLASS_ID::DEVICE_HANDLER_IF; // Standard codes used when building commands. - static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); //!< If the command size is 0. Checked in DHB + static const ReturnValue_t NO_COMMAND_DATA = MAKE_RETURN_CODE(0xA0); //!< If no command data was given when expected. static const ReturnValue_t COMMAND_NOT_SUPPORTED = MAKE_RETURN_CODE(0xA1); //!< Command ID not in commandMap. Checked in DHB static const ReturnValue_t COMMAND_ALREADY_SENT = MAKE_RETURN_CODE(0xA2); //!< Command was already executed. Checked in DHB static const ReturnValue_t COMMAND_WAS_NOT_SENT = MAKE_RETURN_CODE(0xA3); @@ -117,7 +132,7 @@ public: // Standard codes used in interpretDeviceReply static const ReturnValue_t DEVICE_DID_NOT_EXECUTE = MAKE_RETURN_CODE(0xC0); //the device reported, that it did not execute the command static const ReturnValue_t DEVICE_REPORTED_ERROR = MAKE_RETURN_CODE(0xC1); - static const ReturnValue_t UNKNOW_DEVICE_REPLY = MAKE_RETURN_CODE(0xC2); //the deviceCommandId reported by scanforReply is unknown + static const ReturnValue_t UNKNOWN_DEVICE_REPLY = MAKE_RETURN_CODE(0xC2); //the deviceCommandId reported by scanforReply is unknown static const ReturnValue_t DEVICE_REPLY_INVALID = MAKE_RETURN_CODE(0xC3); //syntax etc is correct but still not ok, eg parameters where none are expected // Standard codes used in buildCommandFromCommand @@ -129,7 +144,8 @@ public: * * This is used by the child class to tell the base class what to do. */ - enum CommunicationAction_t: uint8_t { + enum CommunicationAction: uint8_t { + PERFORM_OPERATION, SEND_WRITE,//!< Send write GET_WRITE, //!< Get write SEND_READ, //!< Send read @@ -137,6 +153,14 @@ public: NOTHING //!< Do nothing. }; + static constexpr uint32_t DEFAULT_THERMAL_SET_ID = sid_t::INVALID_SET_ID - 1; + + static constexpr lp_id_t DEFAULT_THERMAL_STATE_POOL_ID = + localpool::INVALID_LPID - 2; + static constexpr lp_id_t DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID = + localpool::INVALID_LPID - 1; + + /** * Default Destructor */ @@ -150,4 +174,4 @@ public: }; -#endif /* DEVICEHANDLERIF_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERIF_H_ */ diff --git a/devicehandlers/DeviceHandlerMessage.cpp b/devicehandlers/DeviceHandlerMessage.cpp index 564fae213..cb9043db8 100644 --- a/devicehandlers/DeviceHandlerMessage.cpp +++ b/devicehandlers/DeviceHandlerMessage.cpp @@ -1,10 +1,6 @@ -#include "../objectmanager/ObjectManagerIF.h" #include "DeviceHandlerMessage.h" #include "../objectmanager/ObjectManagerIF.h" -DeviceHandlerMessage::DeviceHandlerMessage() { -} - store_address_t DeviceHandlerMessage::getStoreAddress( const CommandMessage* message) { return store_address_t(message->getParameter2()); @@ -25,14 +21,6 @@ uint8_t DeviceHandlerMessage::getWiretappingMode( return message->getParameter(); } -//void DeviceHandlerMessage::setDeviceHandlerDirectCommandMessage( -// CommandMessage* message, DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId) { -// message->setCommand(CMD_DIRECT); -// message->setParameter(deviceCommand); -// message->setParameter2(commandParametersStoreId.raw); -//} - void DeviceHandlerMessage::setDeviceHandlerRawCommandMessage( CommandMessage* message, store_address_t rawPacketStoreId) { message->setCommand(CMD_RAW); @@ -47,7 +35,7 @@ void DeviceHandlerMessage::setDeviceHandlerWiretappingMessage( void DeviceHandlerMessage::setDeviceHandlerSwitchIoBoardMessage( CommandMessage* message, uint32_t ioBoardIdentifier) { - message->setCommand(CMD_SWITCH_IOBOARD); + message->setCommand(CMD_SWITCH_ADDRESS); message->setParameter(ioBoardIdentifier); } @@ -79,18 +67,17 @@ void DeviceHandlerMessage::setDeviceHandlerDirectCommandReply( void DeviceHandlerMessage::clear(CommandMessage* message) { switch (message->getCommand()) { case CMD_RAW: -// case CMD_DIRECT: case REPLY_RAW_COMMAND: case REPLY_RAW_REPLY: case REPLY_DIRECT_COMMAND_DATA: { StorageManagerIF *ipcStore = objectManager->get( objects::IPC_STORE); - if (ipcStore != NULL) { + if (ipcStore != nullptr) { ipcStore->deleteData(getStoreAddress(message)); } } /* NO BREAK falls through*/ - case CMD_SWITCH_IOBOARD: + case CMD_SWITCH_ADDRESS: case CMD_WIRETAPPING: message->setCommand(CommandMessage::CMD_NONE); message->setParameter(0); diff --git a/devicehandlers/DeviceHandlerMessage.h b/devicehandlers/DeviceHandlerMessage.h index 8d1c94f41..e5da01c8f 100644 --- a/devicehandlers/DeviceHandlerMessage.h +++ b/devicehandlers/DeviceHandlerMessage.h @@ -1,69 +1,56 @@ -#ifndef DEVICEHANDLERMESSAGE_H_ -#define DEVICEHANDLERMESSAGE_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ #include "../action/ActionMessage.h" #include "../ipc/CommandMessage.h" #include "../objectmanager/SystemObjectIF.h" #include "../storagemanager/StorageManagerIF.h" -//SHOULDDO: rework the static constructors to name the type of command they are building, maybe even hide setting the commandID. +// SHOULDDO: rework the static constructors to name the type of command +// they are building, maybe even hide setting the commandID. + /** - * This is used to uniquely identify commands that are sent to a device - * - * The values are defined in the device-specific implementations - */ -typedef uint32_t DeviceCommandId_t; - -/** - * The DeviceHandlerMessage is used to send Commands to a DeviceHandlerIF + * @brief The DeviceHandlerMessage is used to send commands to classes + * implementing DeviceHandlerIF */ class DeviceHandlerMessage { -private: - DeviceHandlerMessage(); public: + /** + * Instantiation forbidden. Instead, use static functions to operate + * on messages. + */ + DeviceHandlerMessage() = delete; + virtual ~DeviceHandlerMessage() {} /** * These are the commands that can be sent to a DeviceHandlerBase */ static const uint8_t MESSAGE_ID = messagetypes::DEVICE_HANDLER_COMMAND; - static const Command_t CMD_RAW = MAKE_COMMAND_ID( 1 ); //!< Sends a raw command, setParameter is a ::store_id_t containing the raw packet to send -// static const Command_t CMD_DIRECT = MAKE_COMMAND_ID( 2 ); //!< Sends a direct command, setParameter is a ::DeviceCommandId_t, setParameter2 is a ::store_id_t containing the data needed for the command - static const Command_t CMD_SWITCH_IOBOARD = MAKE_COMMAND_ID( 3 ); //!< Requests a IO-Board switch, setParameter() is the IO-Board identifier - static const Command_t CMD_WIRETAPPING = MAKE_COMMAND_ID( 4 ); //!< (De)Activates the monitoring of all raw traffic in DeviceHandlers, setParameter is 0 to deactivate, 1 to activate + //! Sends a raw command, setParameter is a storeId containing the + //! raw packet to send + static const Command_t CMD_RAW = MAKE_COMMAND_ID(1); + //! Requests a IO-Board switch, setParameter() is the IO-Board identifier + static const Command_t CMD_SWITCH_ADDRESS = MAKE_COMMAND_ID(3); + //! (De)Activates the monitoring of all raw traffic in DeviceHandlers, + //! setParameter is 0 to deactivate, 1 to activate + static const Command_t CMD_WIRETAPPING = MAKE_COMMAND_ID(4); - /*static const Command_t REPLY_SWITCHED_IOBOARD = MAKE_COMMAND_ID(1 );//!< Reply to a @c CMD_SWITCH_IOBOARD, indicates switch was successful, getParameter() contains the board switched to (0: nominal, 1: redundant) - static const Command_t REPLY_CANT_SWITCH_IOBOARD = MAKE_COMMAND_ID( 2); //!< Reply to a @c CMD_SWITCH_IOBOARD, indicating the switch could not be performed, getParameter() contains the error message - static const Command_t REPLY_WIRETAPPING = MAKE_COMMAND_ID( 3); //!< Reply to a @c CMD_WIRETAPPING, getParameter() is the current state, 1 enabled, 0 disabled - - static const Command_t REPLY_COMMAND_WAS_SENT = MAKE_COMMAND_ID(4 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates the command was successfully sent to the device, getParameter() contains the ::DeviceCommandId_t - static const Command_t REPLY_COMMAND_NOT_SUPPORTED = MAKE_COMMAND_ID(5 );//!< Reply to a @c CMD_DIRECT, the requested ::DeviceCommand_t is not supported, getParameter() contains the requested ::DeviceCommand_t, getParameter2() contains the ::DeviceCommandId_t - static const Command_t REPLY_COMMAND_WAS_NOT_SENT = MAKE_COMMAND_ID(6 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates the command was not sent, getParameter contains the RMAP Return code (@see rmap.h), getParameter2() contains the ::DeviceCommandId_t - - static const Command_t REPLY_COMMAND_ALREADY_SENT = MAKE_COMMAND_ID(7 );//!< Reply to a @c CMD_DIRECT, the requested ::DeviceCommand_t has already been sent to the device and not ye been answered - static const Command_t REPLY_WRONG_MODE_FOR_CMD = MAKE_COMMAND_ID(8 );//!< Reply to a @c CMD_RAW or @c CMD_DIRECT, indicates that the requested command can not be sent in the curent mode, getParameter() contains the DeviceHandlerCommand_t - static const Command_t REPLY_NO_DATA = MAKE_COMMAND_ID(9 ); //!< Reply to a CMD_RAW or @c CMD_DIRECT, indicates that the ::store_id_t was invalid, getParameter() contains the ::DeviceCommandId_t, getPrameter2() contains the error code - */ - static const Command_t REPLY_DIRECT_COMMAND_SENT = ActionMessage::STEP_SUCCESS; //!< Signals that a direct command was sent - static const Command_t REPLY_RAW_COMMAND = MAKE_COMMAND_ID(0x11 ); //!< Contains a raw command sent to the Device - static const Command_t REPLY_RAW_REPLY = MAKE_COMMAND_ID( 0x12); //!< Contains a raw reply from the Device, getParameter() is the ObjcetId of the sender, getParameter2() is a ::store_id_t containing the raw packet received + //! Signals that a direct command was sent + static const Command_t REPLY_DIRECT_COMMAND_SENT = ActionMessage::STEP_SUCCESS; + //! Contains a raw command sent to the Device + static const Command_t REPLY_RAW_COMMAND = MAKE_COMMAND_ID(0x11); + //! Contains a raw reply from the Device, getParameter() is the ObjcetId + //! of the sender, getParameter2() is a ::store_id_t containing the + //! raw packet received + static const Command_t REPLY_RAW_REPLY = MAKE_COMMAND_ID(0x12); static const Command_t REPLY_DIRECT_COMMAND_DATA = ActionMessage::DATA_REPLY; - /** - * Default Destructor - */ - virtual ~DeviceHandlerMessage() { - } - static store_address_t getStoreAddress(const CommandMessage* message); static uint32_t getDeviceCommandId(const CommandMessage* message); static object_id_t getDeviceObjectId(const CommandMessage *message); static object_id_t getIoBoardObjectId(const CommandMessage* message); static uint8_t getWiretappingMode(const CommandMessage* message); -// static void setDeviceHandlerDirectCommandMessage(CommandMessage* message, -// DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId); - static void setDeviceHandlerDirectCommandReply(CommandMessage* message, object_id_t deviceObjectid, store_address_t commandParametersStoreId); @@ -75,11 +62,6 @@ public: object_id_t deviceObjectid, store_address_t rawPacketStoreId, bool isCommand); -// static void setDeviceHandlerMessage(CommandMessage* message, -// Command_t command, DeviceCommandId_t deviceCommand, -// store_address_t commandParametersStoreId); -// static void setDeviceHandlerMessage(CommandMessage* message, -// Command_t command, store_address_t rawPacketStoreId); static void setDeviceHandlerWiretappingMessage(CommandMessage* message, uint8_t wiretappingMode); static void setDeviceHandlerSwitchIoBoardMessage(CommandMessage* message, @@ -88,4 +70,4 @@ public: static void clear(CommandMessage* message); }; -#endif /* DEVICEHANDLERMESSAGE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERMESSAGE_H_ */ diff --git a/devicehandlers/DeviceHandlerThermalSet.h b/devicehandlers/DeviceHandlerThermalSet.h new file mode 100644 index 000000000..239012e26 --- /dev/null +++ b/devicehandlers/DeviceHandlerThermalSet.h @@ -0,0 +1,44 @@ +#ifndef FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ +#define FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ + +#include "DeviceHandlerIF.h" +#include "../datapoollocal/StaticLocalDataSet.h" +#include "../datapoollocal/LocalPoolVariable.h" + + +class DeviceHandlerThermalSet: public StaticLocalDataSet<2> { +public: + + DeviceHandlerThermalSet(HasLocalDataPoolIF* hkOwner, uint32_t setId = + DeviceHandlerIF::DEFAULT_THERMAL_SET_ID, + lp_id_t thermalStateId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t heaterRequestId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID): + DeviceHandlerThermalSet(hkOwner->getObjectId(), setId, + thermalStateId, heaterRequestId) {} + + DeviceHandlerThermalSet(object_id_t deviceHandler, uint32_t setId = + DeviceHandlerIF::DEFAULT_THERMAL_SET_ID, + lp_id_t thermalStateId = + DeviceHandlerIF::DEFAULT_THERMAL_STATE_POOL_ID, + lp_id_t thermalStateRequestId = + DeviceHandlerIF::DEFAULT_THERMAL_HEATING_REQUEST_POOL_ID): + StaticLocalDataSet(sid_t(deviceHandler, setId)), + thermalStatePoolId(thermalStateId), + heaterRequestPoolId(thermalStateRequestId) {} + + const lp_id_t thermalStatePoolId; + const lp_id_t heaterRequestPoolId; + + lp_var_t thermalState = + lp_var_t( + thermalStatePoolId, sid.objectId, this); + lp_var_t heaterRequest = + lp_var_t( + heaterRequestPoolId, sid.objectId, this); +}; + + + +#endif /* FSFW_DEVICEHANDLERS_DEVICEHANDLERTHERMALSET_H_ */ diff --git a/devicehandlers/DeviceTmReportingWrapper.cpp b/devicehandlers/DeviceTmReportingWrapper.cpp index 84f926f06..c70f57d6d 100644 --- a/devicehandlers/DeviceTmReportingWrapper.cpp +++ b/devicehandlers/DeviceTmReportingWrapper.cpp @@ -1,4 +1,3 @@ -#include "../serialize/SerializeAdapter.h" #include "DeviceTmReportingWrapper.h" #include "../serialize/SerializeAdapter.h" diff --git a/devicehandlers/DeviceTmReportingWrapper.h b/devicehandlers/DeviceTmReportingWrapper.h index a14c4261d..447e95f2c 100644 --- a/devicehandlers/DeviceTmReportingWrapper.h +++ b/devicehandlers/DeviceTmReportingWrapper.h @@ -1,5 +1,5 @@ -#ifndef DEVICETMREPORTINGWRAPPER_H_ -#define DEVICETMREPORTINGWRAPPER_H_ +#ifndef FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ +#define FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ #include "../action/HasActionsIF.h" #include "../objectmanager/SystemObjectIF.h" @@ -24,4 +24,4 @@ private: SerializeIF *data; }; -#endif /* DEVICETMREPORTINGWRAPPER_H_ */ +#endif /* FSFW_DEVICEHANDLERS_DEVICETMREPORTINGWRAPPER_H_ */ diff --git a/devicehandlers/HealthDevice.cpp b/devicehandlers/HealthDevice.cpp index b15e5d2ba..e23dd5b69 100644 --- a/devicehandlers/HealthDevice.cpp +++ b/devicehandlers/HealthDevice.cpp @@ -13,12 +13,12 @@ HealthDevice::~HealthDevice() { } ReturnValue_t HealthDevice::performOperation(uint8_t opCode) { - CommandMessage message; - ReturnValue_t result = commandQueue->receiveMessage(&message); + CommandMessage command; + ReturnValue_t result = commandQueue->receiveMessage(&command); if (result == HasReturnvaluesIF::RETURN_OK) { - healthHelper.handleHealthCommand(&message); + result = healthHelper.handleHealthCommand(&command); } - return HasReturnvaluesIF::RETURN_OK; + return result; } ReturnValue_t HealthDevice::initialize() { diff --git a/devicehandlers/HealthDevice.h b/devicehandlers/HealthDevice.h index 53b0ab097..738f0c7ea 100644 --- a/devicehandlers/HealthDevice.h +++ b/devicehandlers/HealthDevice.h @@ -1,5 +1,5 @@ -#ifndef HEALTHDEVICE_H_ -#define HEALTHDEVICE_H_ +#ifndef FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ +#define FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ #include "../health/HasHealthIF.h" #include "../health/HealthHelper.h" @@ -37,4 +37,4 @@ public: HealthHelper healthHelper; }; -#endif /* HEALTHDEVICE_H_ */ +#endif /* FSFW_DEVICEHANDLERS_HEALTHDEVICE_H_ */ diff --git a/doc/README-config.md b/doc/README-config.md new file mode 100644 index 000000000..036a7d14c --- /dev/null +++ b/doc/README-config.md @@ -0,0 +1,21 @@ + +## Configuring the FSFW + +The FSFW can be configured via the `fsfwconfig` folder. A template folder has +been provided to have a starting point for this. The folder should be added +to the include path. + + +### Configuring the Event Manager + +The number of allowed subscriptions can be modified with the following +parameters: + +``` c++ +namespace fsfwconfig { +//! Configure the allocated pool sizes for the event manager. +static constexpr size_t FSFW_EVENTMGMR_MATCHTREE_NODES = 240; +static constexpr size_t FSFW_EVENTMGMT_EVENTIDMATCHERS = 120; +static constexpr size_t FSFW_EVENTMGMR_RANGEMATCHERS = 120; +} +``` \ No newline at end of file diff --git a/doc/README-controllers.md b/doc/README-controllers.md new file mode 100644 index 000000000..7af0652d4 --- /dev/null +++ b/doc/README-controllers.md @@ -0,0 +1 @@ +## Controllers diff --git a/doc/README-core.md b/doc/README-core.md new file mode 100644 index 000000000..6431b8311 --- /dev/null +++ b/doc/README-core.md @@ -0,0 +1,49 @@ +## FSFW Core Modules + +These core modules provide the most important functionalities of the +Flight Software Framework + +### Clock + + * This is a class of static functions that can be used at anytime + * Leap Seconds must be set if any time conversions from UTC to other times is used + +### ObjectManager + +* Must be created during program startup +* The component which handles all references. All SystemObjects register at this component. +* Any SystemObject needs to have a unique ObjectId. Those can be managed like objects::framework_objects. +* A reference to an object can be get by calling the following function. T must be the specific Interface you want to call. +A nullptr check of the returning Pointer must be done. This function is based on Run-time type information. + +```cpp +template T* ObjectManagerIF::get( object_id_t id ) +``` +* A typical way to create all objects on startup is a handing a static produce function to the ObjectManager on creation. +By calling objectManager->initialize() the produce function will be called and all SystemObjects will be initialized afterwards. + +### Event Manager + +* Component which allows routing of events +* Other objects can subscribe to specific events, ranges of events or all events of an object. +* Subscriptions can be done during runtime but should be done during initialization +* Amounts of allowed subscriptions can be configured in `FSFWConfig.h` + +### Health Table + +* A component which holds every health state +* Provides a thread safe way to access all health states without the need of message exchanges + +### Stores + +* The message based communication can only exchange a few bytes of information inside the message itself. Therefore, additional information can + be exchanged with Stores. With this, only the store address must be exchanged in the message. +* Internally, the FSFW uses an IPC Store to exchange data between processes. For incoming TCs a TC Store is used. For outgoing TM a TM store is used. +* All of them should use the Thread Safe Class storagemanager/PoolManager + +### Tasks + +There are two different types of tasks: + * The PeriodicTask just executes objects that are of type ExecutableObjectIF in the order of the insertion to the Tasks. + * FixedTimeslotTask executes a list of calls in the order of the given list. This is intended for DeviceHandlers, where polling should be in a defined order. An example can be found in defaultcfg/fsfwconfig/pollingSequence + diff --git a/doc/README-devicehandlers.md b/doc/README-devicehandlers.md new file mode 100644 index 000000000..8b6551aa0 --- /dev/null +++ b/doc/README-devicehandlers.md @@ -0,0 +1 @@ +## Device Handlers diff --git a/doc/README-highlevel.md b/doc/README-highlevel.md new file mode 100644 index 000000000..fac89c536 --- /dev/null +++ b/doc/README-highlevel.md @@ -0,0 +1,99 @@ +# High-level overview + +## Structure + +The general structure is driven by the usage of interfaces provided by objects. +The FSFW uses C++11 as baseline. The intention behind this is that this C++ Standard should be widely available, even with older compilers. +The FSFW uses dynamic allocation during the initialization but provides static containers during runtime. +This simplifies the instantiation of objects and allows the usage of some standard containers. +Dynamic Allocation after initialization is discouraged and different solutions are provided in the FSFW to achieve that. +The fsfw uses run-time type information but exceptions are not allowed. + +### Failure Handling + +Functions should return a defined ReturnValue_t to signal to the caller that something has gone wrong. +Returnvalues must be unique. For this the function HasReturnvaluesIF::makeReturnCode or the Macro MAKE_RETURN can be used. +The CLASS_ID is a unique id for that type of object. See returnvalues/FwClassIds. + +### OSAL + +The FSFW provides operation system abstraction layers for Linux, FreeRTOS and RTEMS. +The OSAL provides periodic tasks, message queues, clocks and semaphores as well as mutexes. +The [OSAL README](doc/README-osal.md#top) provides more detailed information on provided components and how to use them. + +### Core Components + +The FSFW has following core components. More detailed informations can be found in the +[core component section](doc/README-core.md#top): + +1. Tasks: Abstraction for different (periodic) task types like periodic tasks or tasks with fixed timeslots +2. ObjectManager: This module stores all `SystemObjects` by mapping a provided unique object ID to the object handles. +3. Static Stores: Different stores are provided to store data of variable size (like telecommands or small telemetry) in a pool structure without + using dynamic memory allocation. These pools are allocated up front. +3. Clock: This module provided common time related functions +4. EventManager: This module allows routing of events generated by `SystemObjects` +5. HealthTable: A component which stores the health states of objects + +### Static IDs in the framework + +Some parts of the framework use a static routing address for communication. +An example setup of ids can be found in the example config in "defaultcft/fsfwconfig/objects/Factory::setStaticFrameworkObjectIds()". + +### Events + +Events are tied to objects. EventIds can be generated by calling the Macro MAKE_EVENT. This works analog to the returnvalues. +Every object that needs own EventIds has to get a unique SUBSYSTEM_ID. +Every SystemObject can call triggerEvent from the parent class. +Therefore, event messages contain the specific EventId and the objectId of the object that has triggered. + +### Internal Communication + +Components communicate mostly over Message through Queues. +Those queues are created by calling the singleton QueueFactory::instance()->create(). + +### External Communication + +The external communication with the mission control system is mostly up to the user implementation. +The FSFW provides PUS Services which can be used to but don't need to be used. +The services can be seen as a conversion from a TC to a message based communication and back. + +#### CCSDS Frames, CCSDS Space Packets and PUS + +If the communication is based on CCSDS Frames and Space Packets, several classes can be used to distributed the packets to the corresponding services. Those can be found in tcdistribution. +If Space Packets are used, a timestamper must be created. +An example can be found in the timemanager folder, this uses CCSDSTime::CDS_short. + +#### Device Handlers + +DeviceHandlers are another important component of the FSFW. +The idea is, to have a software counterpart of every physical device to provide a simple mode, health and commanding interface. +By separating the underlying Communication Interface with DeviceCommunicationIF, a device handler (DH) can be tested on different hardware. +The DH has mechanisms to monitor the communication with the physical device which allow for FDIR reaction. +Device Handlers can be created by overriding `DeviceHandlerBase`. +A standard FDIR component for the DH will be created automatically but can be overwritten by the user. +More information on DeviceHandlers can be found in the related [documentation section](doc/README-devicehandlers.md#top). + +#### Modes, Health + +The two interfaces HasModesIF and HasHealthIF provide access for commanding and monitoring of components. +On-board Mode Management is implement in hierarchy system. +DeviceHandlers and Controllers are the lowest part of the hierarchy. +The next layer are Assemblies. Those assemblies act as a component which handle redundancies of handlers. +Assemblies share a common core with the next level which are the Subsystems. + +Those Assemblies are intended to act as auto-generated components from a database which describes the subsystem modes. +The definitions contain transition and target tables which contain the DH, Assembly and Controller Modes to be commanded. +Transition tables contain as many steps as needed to reach the mode from any other mode, e.g. a switch into any higher AOCS mode might first turn on the sensors, than the actuators and the controller as last component. +The target table is used to describe the state that is checked continuously by the subsystem. +All of this allows System Modes to be generated as Subsystem object as well from the same database. +This System contains list of subsystem modes in the transition and target tables. +Therefore, it allows a modular system to create system modes and easy commanding of those, because only the highest components must be commanded. + +The health state represents if the component is able to perform its tasks. +This can be used to signal the system to avoid using this component instead of a redundant one. +The on-board FDIR uses the health state for isolation and recovery. + +## Unit Tests + +Unit Tests are provided in the unittest folder. Those use the catch2 framework but do not include catch2 itself. More information on how to run these tests can be found in the separate +[`fsfw_tests` reposoitory](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_tests) diff --git a/doc/README-localpools.md b/doc/README-localpools.md new file mode 100644 index 000000000..96ae2d0af --- /dev/null +++ b/doc/README-localpools.md @@ -0,0 +1,172 @@ +## Local Data Pools Developer Information + +The following text is targeted towards mission software developers which would like +to use the local data pools provided by the FSFW to store data like sensor values so they can be +used by other software objects like controllers as well. If a custom class should have a local +pool which can be used by other software objects as well, following steps have to be performed: + +1. Create a `LocalDataPoolManager` member object in the custom class +2. Implement the `HasLocalDataPoolIF` with specifies the interface between the local pool manager +and the class owning the local pool. + +The local data pool manager is also able to process housekeeping service requests in form +of messages, generate periodic housekeeping packet, generate notification and snapshots of changed +variables and datasets and process notifications and snapshots coming from other objects. +The two former tasks are related to the external interface using telemetry and telecommands (TMTC) +while the later two are related to data consumers like controllers only acting on data change +detected by the data creator instead of checking the data manually each cycle. Two important +framework classes `DeviceHandlerBase` and `ExtendedControllerBase` already perform the two steps +shown above so the steps required are altered slightly. + +### Storing and Accessing pool data + +The pool manager is responsible for thread-safe access of the pool data, but the actual +access to the pool data from the point of view of a mission software developer happens via proxy +classes like pool variable classes. These classes store a copy +of the pool variable with the matching datatype and copy the actual data from the local pool +on a `read` call. Changed variables can then be written to the local pool with a `commit` call. +The `read` and `commit` calls are thread-safe and can be called concurrently from data creators +and data consumers. Generally, a user will create a dataset class which in turn groups all +cohesive pool variables. These sets simply iterator over the list of variables and call the +`read` and `commit` functions of each variable. The following diagram shows the +high-level architecture of the local data pools. + +
+ +An example is shown for using the local data pools with a Gyroscope. +For example, the following code shows an implementation to access data from a Gyroscope taken +from the SOURCE CubeSat project: + +```cpp +class GyroPrimaryDataset: public StaticLocalDataSet<3 * sizeof(float)> { +public: + /** + * Constructor for data users + * @param gyroId + */ + GyroPrimaryDataset(object_id_t gyroId): + StaticLocalDataSet(sid_t(gyroId, gyrodefs::GYRO_DATA_SET_ID)) { + setAllVariablesReadOnly(); + } + + lp_var_t angVelocityX = lp_var_t(sid.objectId, + gyrodefs::ANGULAR_VELOCITY_X, this); + lp_var_t angVelocityY = lp_var_t(sid.objectId, + gyrodefs::ANGULAR_VELOCITY_Y, this); + lp_var_t angVelocityZ = lp_var_t(sid.objectId, + gyrodefs::ANGULAR_VELOCITY_Z, this); +private: + + friend class GyroHandler; + /** + * Constructor for data creator + * @param hkOwner + */ + GyroPrimaryDataset(HasLocalDataPoolIF* hkOwner): + StaticLocalDataSet(hkOwner, gyrodefs::GYRO_DATA_SET_ID) {} +}; +``` + +There is a public constructor for users which sets all variables to read-only and there is a +constructor for the GyroHandler data creator by marking it private and declaring the `GyroHandler` +as a friend class. Both the atittude controller and the `GyroHandler` can now +use the same class definition to access the pool variables with `read` and `commit` semantics +in a thread-safe way. Generally, each class requiring access will have the set class as a member +class. The data creator will also be generally a `DeviceHandlerBase` subclass and some additional +steps are necessary to expose the set for housekeeping purposes. + +### Using the local data pools in a `DeviceHandlerBase` subclass + +It is very common to store data generated by devices like a sensor into a pool which can +then be used by other objects. Therefore, the `DeviceHandlerBase` already has a +local pool. Using the aforementioned example, our `GyroHandler` will now have the set class +as a member: + +```cpp +class GyroHandler: ... { + +public: + ... +private: + ... + GyroPrimaryDataset gyroData; + ... +}; +``` + +The constructor used for the creators expects the owner class as a parameter, so we initialize +the object in the `GyroHandler` constructor like this: + +```cpp +GyroHandler::GyroHandler(object_id_t objectId, object_id_t comIF, + CookieIF *comCookie, uint8_t switchId): + DeviceHandlerBase(objectId, comIF, comCookie), switchId(switchId), + gyroData(this) {} +``` + +We need to assign the set to a reply ID used in the `DeviceHandlerBase`. +The combination of the `GyroHandler` object ID and the reply ID will be the 64-bit structure ID +`sid_t` and is used to globally identify the set, for example when requesting housekeeping data or +generating update messages. We need to assign our custom set class in some way so that the local +pool manager can access the custom data sets as well. +By default, the `getDataSetHandle` will take care of this tasks. The default implementation for a +`DeviceHandlerBase` subclass will use the internal command map to retrieve +a handle to a dataset from a given reply ID. Therefore, +we assign the set in the `fillCommandAndReplyMap` function: + +```cpp +void GyroHandler::fillCommandAndReplyMap() { + ... + this->insertInCommandAndReplyMap(gyrodefs::GYRO_DATA, 3, &gyroData); + ... +} +``` + +Now, we need to create the actual pool entries as well, using the `initializeLocalDataPool` +function. Here, we also immediately subscribe for periodic housekeeping packets +with an interval of 4 seconds. They are still disabled in this example and can be enabled +with a housekeeping service command. + +```cpp +ReturnValue_t GyroHandler::initializeLocalDataPool(localpool::DataPool &localDataPoolMap, + LocalDataPoolManager &poolManager) { + localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_X, + new PoolEntry({0.0})); + localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Y, + new PoolEntry({0.0})); + localDataPoolMap.emplace(gyrodefs::ANGULAR_VELOCITY_Z, + new PoolEntry({0.0})); + localDataPoolMap.emplace(gyrodefs::GENERAL_CONFIG_REG42, + new PoolEntry({0})); + localDataPoolMap.emplace(gyrodefs::RANGE_CONFIG_REG43, + new PoolEntry({0})); + + poolManager.subscribeForPeriodicPacket(gyroData.getSid(), false, 4.0, false); + return HasReturnvaluesIF::RETURN_OK; +} +``` + +Now, if we receive some sensor data and converted them into the right format, +we can write it into the pool like this, using a guard class to ensure the set is commited back +in any case: + +```cpp +PoolReadGuard readHelper(&gyroData); +if(readHelper.getReadResult() == HasReturnvaluesIF::RETURN_OK) { + if(not gyroData.isValid()) { + gyroData.setValidity(true, true); + } + + gyroData.angVelocityX = angularVelocityX; + gyroData.angVelocityY = angularVelocityY; + gyroData.angVelocityZ = angularVelocityZ; +} +``` + +The guard class will commit the changed data on destruction automatically. + +### Using the local data pools in a `ExtendedControllerBase` subclass + +Coming soon + + diff --git a/doc/README-osal.md b/doc/README-osal.md new file mode 100644 index 000000000..6f8ce60f5 --- /dev/null +++ b/doc/README-osal.md @@ -0,0 +1,32 @@ +# Operating System Abstraction Layer (OSAL) + +Some specific information on the provided OSALs are provided. + +## Linux OSAL + +This OSAL can be used to compile for Linux host systems like Ubuntu 20.04 or for +embedded Linux targets like the Raspberry Pi. This OSAL generally requires threading support +and real-time functionalities. For most UNIX systems, this is done by adding `-lrt` and `-lpthread` to the linked libraries in the compilation process. The CMake build support provided will do this automatically for the `fsfw` target. It should be noted that most UNIX systems need to be configured specifically to allow the real-time functionalities required by the FSFW. + +More information on how to set up a Linux system accordingly can be found in the +[Linux README of the FSFW example application](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example/src/branch/master/doc/README-linux.md#top) + +## Hosted OSAL + +This is the newest OSAL. Support for Semaphores has not been implemented yet and will propably be implemented as soon as C++20 with Semaphore support has matured. This OSAL can be used to run the FSFW on any host system, but currently has only been tested on Windows 10 and Ubuntu 20.04. Unlike the other OSALs, it uses dynamic memory allocation (e.g. for the message queue implementation). Cross-platform serial port (USB) support might be added soon. + +## FreeRTOS OSAL + +FreeRTOS is not included and the developer needs to take care of compiling the FreeRTOS sources and adding the `FreeRTOSConfig.h` file location to the include path. This OSAL has only been tested extensively with the pre-emptive scheduler configuration so far but it should in principle also be possible to use a cooperative scheduler. It is recommended to use the `heap_4` allocation scheme. When using newlib (nano), it is also recommended to add `#define configUSE_NEWLIB_REENTRANT` to the FreeRTOS configuration file to ensure thread-safety. + +When using this OSAL, developers also need to provide an implementation for the `vRequestContextSwitchFromISR` function. This has been done because the call to request a context switch from an ISR is generally located in the `portmacro.h` header and is different depending on the target architecture or device. + +## RTEMS OSAL + +The RTEMS OSAL was the first implemented OSAL which is also used on the active satellite Flying Laptop. + +## TCP/IP socket abstraction + +The Linux and Host OSAL provide abstraction layers for the socket API. Currently, only UDP sockets have been imlemented. This is very useful to test TMTC handling either on the host computer directly (targeting localhost with a TMTC application) or on embedded Linux devices, sending TMTC packets via Ethernet. + + diff --git a/doc/README-pus.md b/doc/README-pus.md new file mode 100644 index 000000000..928d2eda0 --- /dev/null +++ b/doc/README-pus.md @@ -0,0 +1 @@ +## PUS Services diff --git a/doc/doxy/.gitignore b/doc/doxy/.gitignore new file mode 100644 index 000000000..e9e9961d7 --- /dev/null +++ b/doc/doxy/.gitignore @@ -0,0 +1,3 @@ +html +latex +rtf \ No newline at end of file diff --git a/doc/doxy/OPUS.doxyfile b/doc/doxy/OPUS.doxyfile new file mode 100644 index 000000000..9a3f490dd --- /dev/null +++ b/doc/doxy/OPUS.doxyfile @@ -0,0 +1,2609 @@ +# Doxyfile 1.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Flight Software Framework (FSFW) STS-1" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = C:/Users/Robin/Documents/FSFW/fsfw_tests/fsfw/logo/FSFW_Logo_V3_bw.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = ../../ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = YES + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = NO + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = OPUSLayout.xml + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ../../ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.cpp \ + *.h \ + *.c \ + *.tpp + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = ../uml + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = NO + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /