diff --git a/CHANGELOG.md b/CHANGELOG.md index f442439ed..803384466 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Fixes +- Only delete health table entry in `HealthHelper` destructor if + health table was set. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/710/files +- I2C Bugfixes: Do not keep iterator as member and fix some incorrect handling with the iterator. + Also properly reset the reply size for successfull transfers and erroneous transfers. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/700 - Bugfix for Serial Buffer Stream: Setting `doActive` to false now actually fully disables printing. PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/680 @@ -21,11 +27,24 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Added +- `DleParser` helper class to parse DLE encoded packets from a byte stream. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/711 +- `UioMapper` is able to resolve symlinks now. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/709 - Add new `UnsignedByteField` class PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/660 ## Changes +- `AcceptsTelemetryIF`: `getReportReceptionQueue` is const now + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/712 +- Moved some container returnvalues to dedicated header and namespace + to they can be used without template specification. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/707 +- Remove default secondary header argument for + `uint16_t getTcSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag)` and + `uint16_t getTmSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag)` + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/689 - Removed `HasReturnvaluesIF` class in favor of `returnvalue` namespace with `OK` and `FAILED` constants. PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/659 @@ -50,6 +69,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - `DeviceHandlerBase`: New signature of `handleDeviceTm` which expects a `const SerializeIF&` and additional helper variant which expects `const uint8_t*` PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/671 +- Move some generic `StorageManagerIF` implementations from `LocalPool` to + interface itself so it can be re-used more easily. Also add new + abstract function `bool hasDataAtId(store_address_t storeId) const`. + PR: https://egit.irs.uni-stuttgart.de/fsfw/fsfw/pulls/685 - Improvements for `AcceptsTelemetryIF` and `AcceptsTelecommandsIF`: - Make functions `const` where it makes sense - Add `const char* getName const` abstract function diff --git a/CMakeLists.txt b/CMakeLists.txt index 46e09141a..8308f5234 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,6 +122,7 @@ if(UNIX) option(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS "Add Linux peripheral drivers" OFF) option(FSFW_HAL_LINUX_ADD_LIBGPIOD "Attempt to add Linux GPIOD drivers" OFF) + option(FSFW_HAL_LINUX_ADD_SERIAL_DRIVERS "Add serial drivers" ON) endif() # Optional sources @@ -326,7 +327,8 @@ if(FSFW_BUILD_TESTS) "/usr/local/include/*" "*/fsfw_tests/*" "*/catch2-src/*" - "*/fsfw_hal/*") + "*/fsfw_hal/*" + "unittests/*") endif() target_link_options(${FSFW_TEST_TGT} PRIVATE -fprofile-arcs @@ -344,8 +346,15 @@ if(FSFW_BUILD_TESTS) DEPENDENCIES ${FSFW_TEST_TGT}) else() setup_target_for_coverage_lcov( - NAME ${FSFW_TEST_TGT}_coverage EXECUTABLE ${FSFW_TEST_TGT} - DEPENDENCIES ${FSFW_TEST_TGT}) + NAME + ${FSFW_TEST_TGT}_coverage + EXECUTABLE + ${FSFW_TEST_TGT} + DEPENDENCIES + ${FSFW_TEST_TGT} + GENHTML_ARGS + --html-epilog + ${CMAKE_SOURCE_DIR}/unittests/lcov_epilog.html) endif() endif() endif() diff --git a/automation/Dockerfile b/automation/Dockerfile index 5bd7d3828..1bd39b3fb 100644 --- a/automation/Dockerfile +++ b/automation/Dockerfile @@ -5,7 +5,7 @@ RUN apt-get --yes upgrade #tzdata is a dependency, won't install otherwise ARG DEBIAN_FRONTEND=noninteractive -RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano iputils-ping python3 pip doxygen graphviz +RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano iputils-ping python3 pip doxygen graphviz rsync RUN python3 -m pip install sphinx breathe @@ -23,3 +23,7 @@ RUN git clone https://github.com/ETLCPP/etl.git && \ #ssh needs a valid user to work RUN adduser --uid 114 jenkins + +#add documentation server to known hosts +RUN echo "|1|/LzCV4BuTmTb2wKnD146l9fTKgQ=|NJJtVjvWbtRt8OYqFgcYRnMQyVw= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNL8ssTonYtgiR/6RRlSIK9WU1ywOcJmxFTLcEblAwH7oifZzmYq3XRfwXrgfMpylEfMFYfCU8JRqtmi19xc21A=" >> /etc/ssh/ssh_known_hosts +RUN echo "|1|CcBvBc3EG03G+XM5rqRHs6gK/Gg=|oGeJQ+1I8NGI2THIkJsW92DpTzs= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNL8ssTonYtgiR/6RRlSIK9WU1ywOcJmxFTLcEblAwH7oifZzmYq3XRfwXrgfMpylEfMFYfCU8JRqtmi19xc21A=" >> /etc/ssh/ssh_known_hosts \ No newline at end of file diff --git a/automation/Jenkinsfile b/automation/Jenkinsfile index 8b123fa96..271366062 100644 --- a/automation/Jenkinsfile +++ b/automation/Jenkinsfile @@ -5,7 +5,7 @@ pipeline { } agent { docker { - image 'fsfw-ci:d5' + image 'fsfw-ci:d6' args '--network host' } } @@ -52,14 +52,12 @@ pipeline { sh 'cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..' sh 'make Sphinx' sshagent(credentials: ['documentation-buildfix']) { - sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/development/*' - sh 'scp -o StrictHostKeyChecking=no -r docs/sphinx/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/development' + sh 'rsync -r --delete docs/sphinx/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/development' } } dir(BUILDDIR) { sshagent(credentials: ['documentation-buildfix']) { - sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/coverage/development/*' - sh 'scp -o StrictHostKeyChecking=no -r fsfw-tests_coverage/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/coverage/development' + sh 'rsync -r --delete fsfw-tests_coverage/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/coverage/development' } } } @@ -73,14 +71,12 @@ pipeline { sh 'cmake -DFSFW_BUILD_DOCS=ON -DFSFW_OSAL=host ..' sh 'make Sphinx' sshagent(credentials: ['documentation-buildfix']) { - sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/master/*' - sh 'scp -o StrictHostKeyChecking=no -r docs/sphinx/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/master' + sh 'rsync -r --delete docs/sphinx/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/master' } } dir(BUILDDIR) { sshagent(credentials: ['documentation-buildfix']) { - sh 'ssh -o StrictHostKeyChecking=no buildfix@documentation.intra.irs.uni-stuttgart.de rm -rf /mnt/data/www/html/fsfw/coverage/master/*' - sh 'scp -o StrictHostKeyChecking=no -r fsfw-tests_coverage/* buildfix@documentation.intra.irs.uni-stuttgart.de:/mnt/data/www/html/fsfw/coverage/master' + sh 'rsync -r --delete fsfw-tests_coverage/* buildfix@documentation.irs.uni-stuttgart.de:/fsfw/coverage/master' } } } diff --git a/cmake/cmake-modules/bilke/CodeCoverage.cmake b/cmake/cmake-modules/bilke/CodeCoverage.cmake index aef3d9436..c8b9e4aeb 100644 --- a/cmake/cmake-modules/bilke/CodeCoverage.cmake +++ b/cmake/cmake-modules/bilke/CodeCoverage.cmake @@ -140,7 +140,7 @@ find_program( GCOV_PATH gcov ) find_program( LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl) find_program( FASTCOV_PATH NAMES fastcov fastcov.py ) find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat ) -find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test) +find_program( GCOVR_PATH gcovr ) find_program( CPPFILT_PATH NAMES c++filt ) if(NOT GCOV_PATH) diff --git a/docs/README-config.md b/docs/README-config.md deleted file mode 100644 index d71feb97b..000000000 --- a/docs/README-config.md +++ /dev/null @@ -1,40 +0,0 @@ -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. The primary configuration file is the `FSFWConfig.h` folder. Some -of the available options will be explained in more detail here. - -# Auto-Translation of Events - -The FSFW allows the automatic translation of events, which allows developers to track triggered -events directly via console output. Using this feature requires: - -1. `FSFW_OBJ_EVENT_TRANSLATION` set to 1 in the configuration file. -2. Special auto-generated translation files which translate event IDs and object IDs into - human readable strings. These files can be generated using the - [modgen Python scripts](https://git.ksat-stuttgart.de/source/modgen.git). -3. The generated translation files for the object IDs should be named `translatesObjects.cpp` - and `translateObjects.h` and should be copied to the `fsfwconfig/objects` folder -4. The generated translation files for the event IDs should be named `translateEvents.cpp` and - `translateEvents.h` and should be copied to the `fsfwconfig/events` folder - -An example implementations of these translation file generators can be found as part -of the [SOURCE project here](https://git.ksat-stuttgart.de/source/sourceobsw/-/tree/development/generators) -or the [FSFW example](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/generators) - -## 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; -} -``` - diff --git a/docs/README-controllers.md b/docs/README-controllers.md deleted file mode 100644 index 7af0652d4..000000000 --- a/docs/README-controllers.md +++ /dev/null @@ -1 +0,0 @@ -## Controllers diff --git a/docs/README-core.md b/docs/README-core.md deleted file mode 100644 index e34890aef..000000000 --- a/docs/README-core.md +++ /dev/null @@ -1,55 +0,0 @@ -## 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` folder - diff --git a/docs/README-devicehandlers.md b/docs/README-devicehandlers.md deleted file mode 100644 index 8b6551aa0..000000000 --- a/docs/README-devicehandlers.md +++ /dev/null @@ -1 +0,0 @@ -## Device Handlers diff --git a/docs/README-highlevel.md b/docs/README-highlevel.md deleted file mode 100644 index 9005af8d5..000000000 --- a/docs/README-highlevel.md +++ /dev/null @@ -1,135 +0,0 @@ -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 `returnvalue::makeCode` -or the macro `MAKE_RETURN` can be used. The `CLASS_ID` is a unique id for that type of object. -See `returnvalues/FwClassIds` folder. The user can add custom `CLASS_ID`s via the -`fsfwconfig` folder. - -# 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` - inside the function `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 via Messages through Queues. -Those queues are created by calling the singleton `QueueFactory::instance()->create()` which -will create `MessageQueue` instances for the used OSAL. - -# 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. - -## TMTC Communication - -The FSFW provides some components to facilitate TMTC handling via the PUS commands. -For example, a UDP or TCP PUS server socket can be opened on a specific port using the -files located in `osal/common`. The FSFW example uses this functionality to allow sending telecommands -and receiving telemetry using the [TMTC commander application](https://github.com/spacefisch/tmtccmd). -Simple commands like the PUS Service 17 ping service can be tested by simply running the -`tmtc_client_cli.py` or `tmtc_client_gui.py` utility in -the [example tmtc folder](https://egit.irs.uni-stuttgart.de/fsfw/fsfw_example_public/src/branch/master/tmtc) -while the `fsfw_example` application is running. - -More generally, any class responsible for handling incoming telecommands and sending telemetry -can implement the generic `TmTcBridge` class located in `tmtcservices`. Many applications -also use a dedicated polling task for reading telecommands which passes telecommands -to the `TmTcBridge` implementation. - -## 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 has to be provided by the user. -An example can be found in the `timemanager` folder, which 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 implementing `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 and 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/docs/README-localpools.md b/docs/README-localpools.md deleted file mode 100644 index 1770efb9f..000000000 --- a/docs/README-localpools.md +++ /dev/null @@ -1,174 +0,0 @@ -## 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. - -.. image:: ../misc/logo/FSFW_Logo_V3_bw.png - :alt: FSFW Logo - - -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 returnvalue::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() == returnvalue::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/docs/README-osal.md b/docs/README-osal.md deleted file mode 100644 index 6f8ce60f5..000000000 --- a/docs/README-osal.md +++ /dev/null @@ -1,32 +0,0 @@ -# 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/docs/README-pus.md b/docs/README-pus.md deleted file mode 100644 index 928d2eda0..000000000 --- a/docs/README-pus.md +++ /dev/null @@ -1 +0,0 @@ -## PUS Services diff --git a/docs/conf.py b/docs/conf.py index 700b0ee99..a4232026b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -50,6 +50,11 @@ exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] # html_theme = "alabaster" +html_theme_options = { + "extra_nav_links": {"Impressum" : "https://www.uni-stuttgart.de/impressum", "Datenschutz": "https://info.irs.uni-stuttgart.de/datenschutz/datenschutzWebmit.html"} +} + + # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". diff --git a/docs/highlevel.rst b/docs/highlevel.rst index c77ff4c29..ee35c549e 100644 --- a/docs/highlevel.rst +++ b/docs/highlevel.rst @@ -6,15 +6,14 @@ High-level overview Structure ---------- -The general structure is driven by the usage of interfaces provided by objects. -The FSFW uses C++17 as baseline. -It also 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 will not throw exceptions. +The general structure is driven by the usage of interfaces provided by objects. +The FSFW uses C++17 as baseline. Most modern compilers like GCC should have support for this +standard, even for micocontrollers. + +The FSFW might use dynamic allocation during program initialization but not during runtime. +It offers pool objects, static containers and it also exposes the +`Embedded Template Library `_ to allow writing code which does not perform +allocation during runtime. The fsfw uses run-time type information but will not throw exceptions. Failure Handling ----------------- diff --git a/scripts/helper.py b/scripts/helper.py index 484e52274..59a1b7830 100755 --- a/scripts/helper.py +++ b/scripts/helper.py @@ -207,7 +207,7 @@ def check_for_cmake_build_dir(build_dir_list: list) -> list: def perform_lcov_operation(directory: str, chdir: bool): if chdir: os.chdir(directory) - cmd_runner("cmake --build . -- fsfw-tests_coverage -j") + cmd_runner("cmake --build . -j -- fsfw-tests_coverage") def determine_build_dir(build_dir_list: List[str]): diff --git a/src/fsfw/container/ArrayList.h b/src/fsfw/container/ArrayList.h index f5f74cf16..928345584 100644 --- a/src/fsfw/container/ArrayList.h +++ b/src/fsfw/container/ArrayList.h @@ -4,6 +4,7 @@ #include "../returnvalues/returnvalue.h" #include "../serialize/SerializeAdapter.h" #include "../serialize/SerializeIF.h" +#include "definitions.h" /** * @brief A List that stores its values in an array. @@ -19,9 +20,6 @@ class ArrayList { friend class SerialArrayListAdapter; public: - 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. @@ -187,7 +185,7 @@ class ArrayList { */ ReturnValue_t insert(T entry) { if (size >= maxSize_) { - return FULL; + return containers::LIST_FULL; } entries[size] = entry; ++size; diff --git a/src/fsfw/container/FixedArrayList.h b/src/fsfw/container/FixedArrayList.h index fc8be393a..97ade7e8f 100644 --- a/src/fsfw/container/FixedArrayList.h +++ b/src/fsfw/container/FixedArrayList.h @@ -20,15 +20,19 @@ class FixedArrayList : public ArrayList { 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; + for (size_t idx = 0; idx < this->size; idx++) { + data[idx] = other.data[idx]; + } } FixedArrayList& operator=(FixedArrayList other) { - memcpy(this->data, other.data, sizeof(this->data)); this->entries = data; this->size = other.size; + for (size_t idx = 0; idx < this->size; idx++) { + data[idx] = other.data[idx]; + } return *this; } diff --git a/src/fsfw/container/FixedMap.h b/src/fsfw/container/FixedMap.h index def219d16..ce90d8144 100644 --- a/src/fsfw/container/FixedMap.h +++ b/src/fsfw/container/FixedMap.h @@ -4,8 +4,8 @@ #include #include -#include "../returnvalues/returnvalue.h" #include "ArrayList.h" +#include "definitions.h" /** * @brief Map implementation for maps with a pre-defined size. @@ -24,11 +24,6 @@ class FixedMap : public SerializeIF { "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); - private: static const key_t EMPTY_SLOT = -1; ArrayList, uint32_t> theMap; @@ -76,10 +71,10 @@ class FixedMap : public SerializeIF { ReturnValue_t insert(key_t key, T value, Iterator* storedValue = nullptr) { if (exists(key) == returnvalue::OK) { - return KEY_ALREADY_EXISTS; + return containers::KEY_ALREADY_EXISTS; } if (_size == theMap.maxSize()) { - return MAP_FULL; + return containers::MAP_FULL; } theMap[_size].first = key; theMap[_size].second = value; @@ -93,7 +88,7 @@ class FixedMap : public SerializeIF { 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; + ReturnValue_t result = containers::KEY_DOES_NOT_EXIST; if (findIndex(key) < _size) { result = returnvalue::OK; } @@ -103,7 +98,7 @@ class FixedMap : public SerializeIF { ReturnValue_t erase(Iterator* iter) { uint32_t i; if ((i = findIndex((*iter).value->first)) >= _size) { - return KEY_DOES_NOT_EXIST; + return containers::KEY_DOES_NOT_EXIST; } theMap[i] = theMap[_size - 1]; --_size; @@ -114,7 +109,7 @@ class FixedMap : public SerializeIF { ReturnValue_t erase(key_t key) { uint32_t i; if ((i = findIndex(key)) >= _size) { - return KEY_DOES_NOT_EXIST; + return containers::KEY_DOES_NOT_EXIST; } theMap[i] = theMap[_size - 1]; --_size; diff --git a/src/fsfw/container/definitions.h b/src/fsfw/container/definitions.h new file mode 100644 index 000000000..b50ea45d7 --- /dev/null +++ b/src/fsfw/container/definitions.h @@ -0,0 +1,14 @@ +#ifndef FSFW_CONTAINER_DEFINITIONS_H_ +#define FSFW_CONTAINER_DEFINITIONS_H_ + +#include "fsfw/retval.h" + +namespace containers { +static const ReturnValue_t KEY_ALREADY_EXISTS = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x01); +static const ReturnValue_t MAP_FULL = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x02); +static const ReturnValue_t KEY_DOES_NOT_EXIST = returnvalue::makeCode(CLASS_ID::FIXED_MAP, 0x03); + +static const ReturnValue_t LIST_FULL = returnvalue::makeCode(CLASS_ID::ARRAY_LIST, 0x01); +} // namespace containers + +#endif /* FSFW_CONTAINER_DEFINITIONS_H_ */ diff --git a/src/fsfw/datapoollocal/LocalPoolObjectBase.cpp b/src/fsfw/datapoollocal/LocalPoolObjectBase.cpp index 82aefc18c..5575c3d8b 100644 --- a/src/fsfw/datapoollocal/LocalPoolObjectBase.cpp +++ b/src/fsfw/datapoollocal/LocalPoolObjectBase.cpp @@ -48,12 +48,12 @@ LocalPoolObjectBase::LocalPoolObjectBase(object_id_t poolOwner, lp_id_t poolId, if (hkOwner == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "LocalPoolVariable: The supplied pool owner 0x" << std::hex << poolOwner - << std::dec << " did not implement the correct interface " + << std::dec << " does not exist or does not implement the correct interface " << "HasLocalDataPoolIF" << std::endl; #else sif::printError( - "LocalPoolVariable: The supplied pool owner 0x%08x did not implement the correct " - "interface HasLocalDataPoolIF\n", + "LocalPoolVariable: The supplied pool owner 0x%08x does not exist or does not implement " + "the correct interface HasLocalDataPoolIF\n", poolOwner); #endif return; diff --git a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp index 3968142c7..60966501a 100644 --- a/src/fsfw/devicehandlers/DeviceHandlerBase.cpp +++ b/src/fsfw/devicehandlers/DeviceHandlerBase.cpp @@ -359,6 +359,8 @@ void DeviceHandlerBase::doStateMachine() { if ((switchState == PowerSwitchIF::SWITCH_ON) || (switchState == NO_SWITCH)) { // NOTE: TransitionSourceMode and -SubMode are set by handleCommandedModeTransition childTransitionFailure = CHILD_TIMEOUT; + transitionSourceMode = _MODE_SHUT_DOWN; + transitionSourceSubMode = SUBMODE_NONE; setMode(_MODE_START_UP); callChildStatemachine(); } @@ -458,7 +460,7 @@ ReturnValue_t DeviceHandlerBase::insertInCommandMap(DeviceCommandId_t deviceComm info.expectedReplies = 0; info.isExecuting = false; info.sendReplyTo = NO_COMMANDER; - info.useAlternativeReplyId = alternativeReplyId; + info.useAlternativeReplyId = useAlternativeReply; info.alternativeReplyId = alternativeReplyId; auto resultPair = deviceCommandMap.emplace(deviceCommand, info); if (resultPair.second) { @@ -517,16 +519,16 @@ ReturnValue_t DeviceHandlerBase::updatePeriodicReply(bool enable, DeviceCommandI if (enable) { info->active = true; if (info->countdown != nullptr) { - info->delayCycles = info->maxDelayCycles; - } else { info->countdown->resetTimer(); + } else { + info->delayCycles = info->maxDelayCycles; } } else { info->active = false; if (info->countdown != nullptr) { - info->delayCycles = 0; - } else { info->countdown->timeOut(); + } else { + info->delayCycles = 0; } } } diff --git a/src/fsfw/globalfunctions/CMakeLists.txt b/src/fsfw/globalfunctions/CMakeLists.txt index cfa02696e..cf8a25d54 100644 --- a/src/fsfw/globalfunctions/CMakeLists.txt +++ b/src/fsfw/globalfunctions/CMakeLists.txt @@ -4,6 +4,7 @@ target_sources( AsciiConverter.cpp CRC.cpp DleEncoder.cpp + DleParser.cpp PeriodicOperationDivider.cpp timevalOperations.cpp Type.cpp diff --git a/src/fsfw/globalfunctions/DleParser.cpp b/src/fsfw/globalfunctions/DleParser.cpp new file mode 100644 index 000000000..cc695babe --- /dev/null +++ b/src/fsfw/globalfunctions/DleParser.cpp @@ -0,0 +1,173 @@ +#include "DleParser.h" + +#include + +#include +#include +#include + +DleParser::DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf, + BufPair decodedBuf) + : decodeRingBuf(decodeRingBuf), + decoder(decoder), + encodedBuf(encodedBuf), + decodedBuf(decodedBuf) {} + +ReturnValue_t DleParser::passData(const uint8_t* data, size_t len) { + if (data == nullptr or len == 0) { + return returnvalue::FAILED; + } + return decodeRingBuf.writeData(data, len); +} + +ReturnValue_t DleParser::parseRingBuf(size_t& readSize) { + ctx.setType(DleParser::ContextType::NONE); + size_t availableData = decodeRingBuf.getAvailableReadData(); + if (availableData == 0) { + return NO_PACKET_FOUND; + } + if (availableData > encodedBuf.second) { + ErrorInfo info; + info.len = decodeRingBuf.getAvailableReadData(); + setErrorContext(ErrorTypes::DECODING_BUF_TOO_SMALL, info); + return returnvalue::FAILED; + } + ReturnValue_t result = decodeRingBuf.readData(encodedBuf.first, availableData); + if (result != returnvalue::OK) { + ErrorInfo info; + info.res = result; + setErrorContext(ErrorTypes::RING_BUF_ERROR, info); + return result; + } + bool stxFound = false; + size_t stxIdx = 0; + for (size_t vectorIdx = 0; vectorIdx < availableData; vectorIdx++) { + // handle STX char + if (encodedBuf.first[vectorIdx] == DleEncoder::STX_CHAR) { + if (not stxFound) { + stxFound = true; + stxIdx = vectorIdx; + } else { + // might be lost packet, so we should advance the read pointer + // without skipping the STX + readSize = vectorIdx; + ErrorInfo info; + setErrorContext(ErrorTypes::CONSECUTIVE_STX_CHARS, info); + return POSSIBLE_PACKET_LOSS; + } + } + // handle ETX char + if (encodedBuf.first[vectorIdx] == DleEncoder::ETX_CHAR) { + if (stxFound) { + // This is propably a packet, so we decode it. + size_t decodedLen = 0; + size_t dummy = 0; + + ReturnValue_t result = + decoder.decode(&encodedBuf.first[stxIdx], availableData - stxIdx, &dummy, + decodedBuf.first, decodedBuf.second, &decodedLen); + if (result == returnvalue::OK) { + ctx.setType(ContextType::PACKET_FOUND); + ctx.decodedPacket.first = decodedBuf.first; + ctx.decodedPacket.second = decodedLen; + readSize = ++vectorIdx; + return returnvalue::OK; + } else { + // invalid packet, skip. + readSize = ++vectorIdx; + ErrorInfo info; + info.res = result; + setErrorContext(ErrorTypes::DECODE_ERROR, info); + return POSSIBLE_PACKET_LOSS; + } + } else { + // might be lost packet, so we should advance the read pointer + readSize = ++vectorIdx; + ErrorInfo info; + info.len = 0; + setErrorContext(ErrorTypes::CONSECUTIVE_ETX_CHARS, info); + return POSSIBLE_PACKET_LOSS; + } + } + } + return NO_PACKET_FOUND; +} + +void DleParser::defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args) { +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "DleParserBase::handleFoundPacket: Detected DLE packet with " << len << " bytes" + << std::endl; +#else + sif::printInfo("DleParserBase::handleFoundPacket: Detected DLE packet with %d bytes\n", len); +#endif +#endif +} + +void DleParser::defaultErrorHandler() { + if (ctx.getType() != DleParser::ContextType::ERROR) { + errorPrinter("No error"); + return; + } + switch (ctx.error.first) { + case (ErrorTypes::NONE): { + errorPrinter("No error"); + break; + } + case (ErrorTypes::DECODE_ERROR): { + errorPrinter("Decode Error"); + break; + } + case (ErrorTypes::RING_BUF_ERROR): { + errorPrinter("Ring Buffer Error"); + break; + } + case (ErrorTypes::ENCODED_BUF_TOO_SMALL): + case (ErrorTypes::DECODING_BUF_TOO_SMALL): { + char opt[64]; + snprintf(opt, sizeof(opt), ": Too small for packet with length %zu", + ctx.decodedPacket.second); + if (ctx.error.first == ErrorTypes::ENCODED_BUF_TOO_SMALL) { + errorPrinter("Encoded buf too small", opt); + } else { + errorPrinter("Decoding buf too small", opt); + } + break; + } + case (ErrorTypes::CONSECUTIVE_STX_CHARS): { + errorPrinter("Consecutive STX chars detected"); + break; + } + case (ErrorTypes::CONSECUTIVE_ETX_CHARS): { + errorPrinter("Consecutive ETX chars detected"); + break; + } + } +} + +void DleParser::errorPrinter(const char* str, const char* opt) { + if (opt == nullptr) { + opt = ""; + } +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::info << "DleParserBase::handleParseError: " << str << opt << std::endl; +#else + sif::printInfo("DleParserBase::handleParseError: %s%s\n", str, opt); +#endif +#endif +} + +void DleParser::setErrorContext(ErrorTypes err, ErrorInfo info) { + ctx.setType(ContextType::ERROR); + ctx.error.first = err; + ctx.error.second = info; +} + +ReturnValue_t DleParser::confirmBytesRead(size_t bytesRead) { + return decodeRingBuf.deleteData(bytesRead); +} + +const DleParser::Context& DleParser::getContext() { return ctx; } + +void DleParser::reset() { decodeRingBuf.clear(); } diff --git a/src/fsfw/globalfunctions/DleParser.h b/src/fsfw/globalfunctions/DleParser.h new file mode 100644 index 000000000..9802017a5 --- /dev/null +++ b/src/fsfw/globalfunctions/DleParser.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include + +#include +#include + +/** + * @brief This base helper class can be used to extract DLE encoded packets from a data stream + * @details + * The core API of the parser takes received packets which can contains DLE packets. The parser + * can deal with DLE packets split across multiple packets. It does so by using a dedicated + * decoding ring buffer. The user can process received packets and detect errors by + * overriding two provided virtual methods. This also allows detecting multiple DLE packets + * inside one passed packet. + */ +class DleParser { + public: + static constexpr ReturnValue_t NO_PACKET_FOUND = returnvalue::makeCode(1, 1); + static constexpr ReturnValue_t POSSIBLE_PACKET_LOSS = returnvalue::makeCode(1, 2); + using BufPair = std::pair; + + enum class ContextType { NONE, PACKET_FOUND, ERROR }; + + enum class ErrorTypes { + NONE, + ENCODED_BUF_TOO_SMALL, + DECODING_BUF_TOO_SMALL, + DECODE_ERROR, + RING_BUF_ERROR, + CONSECUTIVE_STX_CHARS, + CONSECUTIVE_ETX_CHARS + }; + + union ErrorInfo { + size_t len; + ReturnValue_t res; + }; + + using ErrorPair = std::pair; + + struct Context { + public: + Context() { setType(ContextType::PACKET_FOUND); } + + void setType(ContextType type) { + this->type = type; + if (type == ContextType::PACKET_FOUND) { + error.first = ErrorTypes::NONE; + error.second.len = 0; + } else { + decodedPacket.first = nullptr; + decodedPacket.second = 0; + } + } + + ContextType getType() const { return type; } + + BufPair decodedPacket = {}; + ErrorPair error; + + private: + ContextType type; + }; + + /** + * Base class constructor + * @param decodeRingBuf Ring buffer used to store multiple packets to allow detecting DLE packets + * split across multiple packets + * @param decoder Decoder instance + * @param encodedBuf Buffer used to store encoded packets. It has to be large enough to hold + * the largest expected encoded DLE packet size + * @param decodedBuf Buffer used to store decoded packets. It has to be large enough to hold the + * largest expected decoded DLE packet size + * @param handler Function which will be called on a found packet + * @param args Arbitrary user argument + */ + DleParser(SimpleRingBuffer& decodeRingBuf, DleEncoder& decoder, BufPair encodedBuf, + BufPair decodedBuf); + + /** + * This function allows to pass new data into the parser. It then scans for DLE packets + * automatically and inserts (part of) the packet into a ring buffer if necessary. + * @param data + * @param len + * @return + */ + ReturnValue_t passData(const uint8_t* data, size_t len); + + ReturnValue_t parseRingBuf(size_t& bytesRead); + + ReturnValue_t confirmBytesRead(size_t bytesRead); + + const Context& getContext(); + /** + * Example found packet handler + * function call + * @param packet Decoded packet + * @param len Length of detected packet + */ + void defaultFoundPacketHandler(uint8_t* packet, size_t len, void* args); + /** + * Will be called if an error occured in the #passData call + * @param err + * @param ctx Context information depending on the error type + * - For buffer length errors, will be set to the detected packet length which is too large + * - For decode or ring buffer errors, will be set to the result returned from the failed call + */ + void defaultErrorHandler(); + + static void errorPrinter(const char* str, const char* opt = nullptr); + + void setErrorContext(ErrorTypes err, ErrorInfo ctx); + /** + * Resets the parser by resetting the internal states and clearing the decoding ring buffer + */ + void reset(); + + private: + SimpleRingBuffer& decodeRingBuf; + DleEncoder& decoder; + BufPair encodedBuf; + BufPair decodedBuf; + Context ctx; +}; diff --git a/src/fsfw/globalfunctions/timevalOperations.cpp b/src/fsfw/globalfunctions/timevalOperations.cpp index e2b35512d..79c07eaf3 100644 --- a/src/fsfw/globalfunctions/timevalOperations.cpp +++ b/src/fsfw/globalfunctions/timevalOperations.cpp @@ -1,8 +1,8 @@ #include "fsfw/globalfunctions/timevalOperations.h" timeval& operator+=(timeval& lhs, const timeval& rhs) { - int64_t sum = lhs.tv_sec * 1000000. + lhs.tv_usec; - sum += rhs.tv_sec * 1000000. + rhs.tv_usec; + int64_t sum = static_cast(lhs.tv_sec) * 1000000. + lhs.tv_usec; + sum += static_cast(rhs.tv_sec) * 1000000. + rhs.tv_usec; lhs.tv_sec = sum / 1000000; lhs.tv_usec = sum - lhs.tv_sec * 1000000; return lhs; diff --git a/src/fsfw/health/HealthHelper.cpp b/src/fsfw/health/HealthHelper.cpp index bf1a92d2e..f6077ea15 100644 --- a/src/fsfw/health/HealthHelper.cpp +++ b/src/fsfw/health/HealthHelper.cpp @@ -5,7 +5,11 @@ HealthHelper::HealthHelper(HasHealthIF* owner, object_id_t objectId) : objectId(objectId), owner(owner) {} -HealthHelper::~HealthHelper() { healthTable->removeObject(objectId); } +HealthHelper::~HealthHelper() { + if (healthTable != nullptr) { + healthTable->removeObject(objectId); + } +} ReturnValue_t HealthHelper::handleHealthCommand(CommandMessage* message) { switch (message->getCommand()) { diff --git a/src/fsfw/osal/common/TcpIpBase.cpp b/src/fsfw/osal/common/TcpIpBase.cpp index 7e989c362..486a5171f 100644 --- a/src/fsfw/osal/common/TcpIpBase.cpp +++ b/src/fsfw/osal/common/TcpIpBase.cpp @@ -1,6 +1,7 @@ #include "fsfw/osal/common/TcpIpBase.h" #include "fsfw/platform.h" +#include "fsfw/serviceinterface.h" #ifdef PLATFORM_UNIX #include diff --git a/src/fsfw/osal/host/Clock.cpp b/src/fsfw/osal/host/Clock.cpp index 29c6c1a62..dbf6529c9 100644 --- a/src/fsfw/osal/host/Clock.cpp +++ b/src/fsfw/osal/host/Clock.cpp @@ -8,6 +8,7 @@ #if defined(PLATFORM_WIN) #include +#define timegm _mkgmtime #elif defined(PLATFORM_UNIX) #include #endif diff --git a/src/fsfw/osal/windows/winTaskHelpers.cpp b/src/fsfw/osal/windows/winTaskHelpers.cpp index 206ee7a77..235dca1e5 100644 --- a/src/fsfw/osal/windows/winTaskHelpers.cpp +++ b/src/fsfw/osal/windows/winTaskHelpers.cpp @@ -1,5 +1,7 @@ #include "fsfw/osal/windows/winTaskHelpers.h" +#include + #include TaskPriority tasks::makeWinPriority(PriorityClass prioClass, PriorityNumber prioNumber) { diff --git a/src/fsfw/osal/windows/winTaskHelpers.h b/src/fsfw/osal/windows/winTaskHelpers.h index 87cd92ce0..2d6ef9b40 100644 --- a/src/fsfw/osal/windows/winTaskHelpers.h +++ b/src/fsfw/osal/windows/winTaskHelpers.h @@ -1,10 +1,12 @@ #include #include -#include "../../tasks/TaskFactory.h" +#include "fsfw/tasks/TaskFactory.h" #ifdef _WIN32 +#include + namespace tasks { enum PriorityClass : uint16_t { diff --git a/src/fsfw/storagemanager/LocalPool.cpp b/src/fsfw/storagemanager/LocalPool.cpp index 970b05f52..b62c19b60 100644 --- a/src/fsfw/storagemanager/LocalPool.cpp +++ b/src/fsfw/storagemanager/LocalPool.cpp @@ -31,9 +31,8 @@ LocalPool::LocalPool(object_id_t setObjectId, const LocalPoolConfig& poolConfig, LocalPool::~LocalPool() = default; -ReturnValue_t LocalPool::addData(store_address_t* storageId, const uint8_t* data, size_t size, - bool ignoreFault) { - ReturnValue_t status = reserveSpace(size, storageId, ignoreFault); +ReturnValue_t LocalPool::addData(store_address_t* storageId, const uint8_t* data, size_t size) { + ReturnValue_t status = reserveSpace(size, storageId); if (status == returnvalue::OK) { write(*storageId, data, size); } @@ -49,8 +48,8 @@ ReturnValue_t LocalPool::getData(store_address_t packetId, const uint8_t** packe } ReturnValue_t LocalPool::getFreeElement(store_address_t* storageId, const size_t size, - uint8_t** pData, bool ignoreFault) { - ReturnValue_t status = reserveSpace(size, storageId, ignoreFault); + uint8_t** pData) { + ReturnValue_t status = reserveSpace(size, storageId); if (status == returnvalue::OK) { *pData = &store[storageId->poolIndex][getRawPosition(*storageId)]; } else { @@ -167,7 +166,7 @@ void LocalPool::clearStore() { } } -ReturnValue_t LocalPool::reserveSpace(size_t size, store_address_t* storeId, bool ignoreFault) { +ReturnValue_t LocalPool::reserveSpace(size_t size, store_address_t* storeId) { ReturnValue_t status = getSubPoolIndex(size, &storeId->poolIndex); if (status != returnvalue::OK) { #if FSFW_CPP_OSTREAM_ENABLED == 1 @@ -318,27 +317,3 @@ bool LocalPool::hasDataAtId(store_address_t storeId) const { } return false; } - -ReturnValue_t LocalPool::getFreeElement(store_address_t* storeId, size_t size, uint8_t** pData) { - return StorageManagerIF::getFreeElement(storeId, size, pData); -} - -ConstAccessorPair LocalPool::getData(store_address_t storeId) { - return StorageManagerIF::getData(storeId); -} - -ReturnValue_t LocalPool::addData(store_address_t* storeId, const uint8_t* data, size_t size) { - return StorageManagerIF::addData(storeId, data, size); -} - -ReturnValue_t LocalPool::getData(store_address_t storeId, ConstStorageAccessor& accessor) { - return StorageManagerIF::getData(storeId, accessor); -} - -ReturnValue_t LocalPool::modifyData(store_address_t storeId, StorageAccessor& accessor) { - return StorageManagerIF::modifyData(storeId, accessor); -} - -AccessorPair LocalPool::modifyData(store_address_t storeId) { - return StorageManagerIF::modifyData(storeId); -} diff --git a/src/fsfw/storagemanager/LocalPool.h b/src/fsfw/storagemanager/LocalPool.h index 54d704e67..f82acd54c 100644 --- a/src/fsfw/storagemanager/LocalPool.h +++ b/src/fsfw/storagemanager/LocalPool.h @@ -86,21 +86,13 @@ class LocalPool : public SystemObject, public StorageManagerIF { /** * Documentation: See StorageManagerIF.h */ - ReturnValue_t addData(store_address_t* storeId, const uint8_t* data, size_t size, - bool ignoreFault) override; ReturnValue_t addData(store_address_t* storeId, const uint8_t* data, size_t size) override; ReturnValue_t getFreeElement(store_address_t* storeId, size_t size, uint8_t** pData) override; - ReturnValue_t getFreeElement(store_address_t* storeId, size_t size, uint8_t** pData, - bool ignoreFault) override; - ConstAccessorPair getData(store_address_t storeId) override; - ReturnValue_t getData(store_address_t storeId, ConstStorageAccessor& accessor) override; ReturnValue_t getData(store_address_t storeId, const uint8_t** packet_ptr, size_t* size) override; - AccessorPair modifyData(store_address_t storeId) override; ReturnValue_t modifyData(store_address_t storeId, uint8_t** packet_ptr, size_t* size) override; - ReturnValue_t modifyData(store_address_t storeId, StorageAccessor& accessor) override; ReturnValue_t deleteData(store_address_t storeId) override; ReturnValue_t deleteData(uint8_t* ptr, size_t size, store_address_t* storeId) override; @@ -136,6 +128,12 @@ class LocalPool : public SystemObject, public StorageManagerIF { [[nodiscard]] max_subpools_t getNumberOfSubPools() const override; [[nodiscard]] bool hasDataAtId(store_address_t storeId) const override; + // Using functions provided by StorageManagerIF requires either a fully qualified path + // like for example localPool.StorageManagerIF::getFreeElement(...) or re-exporting + // the fully qualified path with the using directive. + using StorageManagerIF::getData; + using StorageManagerIF::modifyData; + protected: /** * With this helper method, a free element of @c size is reserved. @@ -144,7 +142,7 @@ class LocalPool : public SystemObject, public StorageManagerIF { * @return - returnvalue::OK on success, * - the return codes of #getPoolIndex or #findEmpty otherwise. */ - virtual ReturnValue_t reserveSpace(size_t size, store_address_t* address, bool ignoreFault); + virtual ReturnValue_t reserveSpace(size_t size, store_address_t* address); private: /** @@ -188,6 +186,8 @@ class LocalPool : public SystemObject, public StorageManagerIF { std::vector> sizeLists = std::vector>(NUMBER_OF_SUBPOOLS); + bool ignoreFault = false; + //! A variable to determine whether higher n pools are used if //! the store is full. bool spillsToHigherPools = false; diff --git a/src/fsfw/storagemanager/PoolManager.cpp b/src/fsfw/storagemanager/PoolManager.cpp index 41e140b2b..840a7dcc2 100644 --- a/src/fsfw/storagemanager/PoolManager.cpp +++ b/src/fsfw/storagemanager/PoolManager.cpp @@ -9,10 +9,9 @@ PoolManager::PoolManager(object_id_t setObjectId, const LocalPoolConfig& localPo PoolManager::~PoolManager() { MutexFactory::instance()->deleteMutex(mutex); } -ReturnValue_t PoolManager::reserveSpace(const size_t size, store_address_t* address, - bool ignoreFault) { +ReturnValue_t PoolManager::reserveSpace(const size_t size, store_address_t* address) { MutexGuard mutexHelper(mutex, MutexIF::TimeoutType::WAITING, mutexTimeoutMs); - ReturnValue_t status = LocalPool::reserveSpace(size, address, ignoreFault); + ReturnValue_t status = LocalPool::reserveSpace(size, address); return status; } diff --git a/src/fsfw/storagemanager/PoolManager.h b/src/fsfw/storagemanager/PoolManager.h index eaa978ef4..aa8c93ddc 100644 --- a/src/fsfw/storagemanager/PoolManager.h +++ b/src/fsfw/storagemanager/PoolManager.h @@ -57,7 +57,7 @@ class PoolManager : public LocalPool { //! Default mutex timeout value to prevent permanent blocking. uint32_t mutexTimeoutMs = 20; - ReturnValue_t reserveSpace(size_t size, store_address_t* address, bool ignoreFault) override; + ReturnValue_t reserveSpace(size_t size, store_address_t* address) override; /** * @brief The mutex is created in the constructor and makes diff --git a/src/fsfw/storagemanager/StorageManagerIF.h b/src/fsfw/storagemanager/StorageManagerIF.h index 2845e581f..e48e19dbb 100644 --- a/src/fsfw/storagemanager/StorageManagerIF.h +++ b/src/fsfw/storagemanager/StorageManagerIF.h @@ -55,7 +55,7 @@ class StorageManagerIF { /** * @brief This is the empty virtual destructor as required for C++ interfaces. */ - ~StorageManagerIF() = default; + virtual ~StorageManagerIF() = default; /** * @brief With addData, a free storage position is allocated and data * stored there. @@ -66,12 +66,7 @@ class StorageManagerIF { * @return Returns @returnvalue::OK if data was added. * @returnvalue::FAILED if data could not be added, storageId is unchanged then. */ - virtual ReturnValue_t addData(store_address_t* storageId, const uint8_t* data, size_t size, - bool ignoreFault) = 0; - - virtual ReturnValue_t addData(store_address_t* storageId, const uint8_t* data, size_t size) { - return addData(storageId, data, size, false); - } + virtual ReturnValue_t addData(store_address_t* storageId, const uint8_t* data, size_t size) = 0; /** * @brief With deleteData, the storageManager frees the memory region @@ -186,12 +181,8 @@ class StorageManagerIF { * @return Returns @returnvalue::OK if data was added. * @returnvalue::FAILED if data could not be added, storageId is unchanged then. */ - virtual ReturnValue_t getFreeElement(store_address_t* storageId, size_t size, uint8_t** dataPtr, - bool ignoreFault) = 0; - - virtual ReturnValue_t getFreeElement(store_address_t* storageId, size_t size, uint8_t** dataPtr) { - return getFreeElement(storageId, size, dataPtr, false); - } + virtual ReturnValue_t getFreeElement(store_address_t* storageId, size_t size, + uint8_t** dataPtr) = 0; [[nodiscard]] virtual bool hasDataAtId(store_address_t storeId) const = 0; diff --git a/src/fsfw/tcdistribution/definitions.h b/src/fsfw/tcdistribution/definitions.h index 01fc30852..bec1dd1b4 100644 --- a/src/fsfw/tcdistribution/definitions.h +++ b/src/fsfw/tcdistribution/definitions.h @@ -17,11 +17,11 @@ static constexpr ReturnValue_t INVALID_PACKET_TYPE = MAKE_RETURN_CODE(3); static constexpr ReturnValue_t INVALID_SEC_HEADER_FIELD = MAKE_RETURN_CODE(4); static constexpr ReturnValue_t INCORRECT_PRIMARY_HEADER = MAKE_RETURN_CODE(5); -static constexpr ReturnValue_t INCOMPLETE_PACKET = MAKE_RETURN_CODE(5); -static constexpr ReturnValue_t INVALID_PUS_VERSION = MAKE_RETURN_CODE(6); -static constexpr ReturnValue_t INCORRECT_CHECKSUM = MAKE_RETURN_CODE(7); -static constexpr ReturnValue_t ILLEGAL_PACKET_SUBTYPE = MAKE_RETURN_CODE(8); -static constexpr ReturnValue_t INCORRECT_SECONDARY_HEADER = MAKE_RETURN_CODE(9); +static constexpr ReturnValue_t INCOMPLETE_PACKET = MAKE_RETURN_CODE(7); +static constexpr ReturnValue_t INVALID_PUS_VERSION = MAKE_RETURN_CODE(8); +static constexpr ReturnValue_t INCORRECT_CHECKSUM = MAKE_RETURN_CODE(9); +static constexpr ReturnValue_t ILLEGAL_PACKET_SUBTYPE = MAKE_RETURN_CODE(10); +static constexpr ReturnValue_t INCORRECT_SECONDARY_HEADER = MAKE_RETURN_CODE(11); static constexpr uint8_t SUBSYSTEM_ID = SUBSYSTEM_ID::TMTC_DISTRIBUTION; //! P1: Returnvalue, P2: 0 for TM issues, 1 for TC issues diff --git a/src/fsfw/timemanager/ClockCommon.cpp b/src/fsfw/timemanager/ClockCommon.cpp index 45755f355..d0ac90043 100644 --- a/src/fsfw/timemanager/ClockCommon.cpp +++ b/src/fsfw/timemanager/ClockCommon.cpp @@ -61,10 +61,16 @@ ReturnValue_t Clock::convertTimevalToTimeOfDay(const timeval* from, TimeOfDay_t* if (result != returnvalue::OK) { return result; } - MutexGuard helper(timeMutex); // gmtime writes its output in a global buffer which is not Thread Safe // Therefore we have to use a Mutex here + MutexGuard helper(timeMutex); +#ifdef PLATFORM_WIN + time_t time; + time = from->tv_sec; + timeInfo = gmtime(&time); +#else timeInfo = gmtime(&from->tv_sec); +#endif to->year = timeInfo->tm_year + 1900; to->month = timeInfo->tm_mon + 1; to->day = timeInfo->tm_mday; diff --git a/src/fsfw/timemanager/TimeReaderIF.h b/src/fsfw/timemanager/TimeReaderIF.h index 8fd7415fd..aa64b7548 100644 --- a/src/fsfw/timemanager/TimeReaderIF.h +++ b/src/fsfw/timemanager/TimeReaderIF.h @@ -1,8 +1,16 @@ #ifndef FSFW_TIMEMANAGER_TIMEREADERIF_H #define FSFW_TIMEMANAGER_TIMEREADERIF_H +#include #include +#include "fsfw/platform.h" + +#ifdef PLATFORM_WIN +// wtf? Required for timeval! +#include +#endif + #include "TimeStampIF.h" #include "fsfw/returnvalues/returnvalue.h" #include "fsfw/serialize/SerializeIF.h" diff --git a/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.cpp b/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.cpp index 605bbd217..262653c90 100644 --- a/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.cpp +++ b/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.cpp @@ -21,7 +21,7 @@ SpacePacketReader::~SpacePacketReader() = default; inline uint16_t SpacePacketReader::getPacketIdRaw() const { return ccsds::getPacketId(*spHeader); } -const uint8_t* SpacePacketReader::getPacketData() { return packetDataField; } +const uint8_t* SpacePacketReader::getPacketData() const { return packetDataField; } ReturnValue_t SpacePacketReader::setData(uint8_t* data, size_t maxSize_, void* args) { return setInternalFields(data, maxSize_); diff --git a/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.h b/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.h index ff22510c0..61939217b 100644 --- a/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.h +++ b/src/fsfw/tmtcpacket/ccsds/SpacePacketReader.h @@ -71,7 +71,7 @@ class SpacePacketReader : public SpacePacketIF, // Helper methods: [[nodiscard]] ReturnValue_t checkSize() const; - const uint8_t* getPacketData(); + const uint8_t* getPacketData() const; ReturnValue_t setReadOnlyData(const uint8_t* data, size_t maxSize); diff --git a/src/fsfw/tmtcpacket/ccsds/defs.h b/src/fsfw/tmtcpacket/ccsds/defs.h index 1c7de5408..da7c524d4 100644 --- a/src/fsfw/tmtcpacket/ccsds/defs.h +++ b/src/fsfw/tmtcpacket/ccsds/defs.h @@ -23,11 +23,11 @@ constexpr uint16_t getSpacePacketIdFromApid(bool isTc, uint16_t apid, return ((isTc << 4) | (secondaryHeaderFlag << 3) | ((apid >> 8) & 0x07)) << 8 | (apid & 0x00ff); } -constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag = true) { +constexpr uint16_t getTcSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag) { return getSpacePacketIdFromApid(true, apid, secondaryHeaderFlag); } -constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag = true) { +constexpr uint16_t getTmSpacePacketIdFromApid(uint16_t apid, bool secondaryHeaderFlag) { return getSpacePacketIdFromApid(false, apid, secondaryHeaderFlag); } diff --git a/src/fsfw/tmtcservices/AcceptsTelemetryIF.h b/src/fsfw/tmtcservices/AcceptsTelemetryIF.h index 0c5621c8b..5b421cf96 100644 --- a/src/fsfw/tmtcservices/AcceptsTelemetryIF.h +++ b/src/fsfw/tmtcservices/AcceptsTelemetryIF.h @@ -23,7 +23,9 @@ class AcceptsTelemetryIF { */ [[nodiscard]] virtual MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const = 0; - [[nodiscard]] virtual MessageQueueId_t getReportReceptionQueue() const { return getReportReceptionQueue(0); } + [[nodiscard]] virtual MessageQueueId_t getReportReceptionQueue() const { + return getReportReceptionQueue(0); + } }; #endif /* FSFW_TMTCSERVICES_ACCEPTSTELEMETRYIF_H_ */ diff --git a/src/fsfw/tmtcservices/SourceSequenceCounter.h b/src/fsfw/tmtcservices/SourceSequenceCounter.h index da96dc4eb..f530b4445 100644 --- a/src/fsfw/tmtcservices/SourceSequenceCounter.h +++ b/src/fsfw/tmtcservices/SourceSequenceCounter.h @@ -5,20 +5,28 @@ class SourceSequenceCounter { private: - uint16_t sequenceCount; + uint16_t sequenceCount = 0; public: - SourceSequenceCounter() : sequenceCount(0) {} - void increment() { - sequenceCount = (sequenceCount + 1) % (SpacePacketBase::LIMIT_SEQUENCE_COUNT); + SourceSequenceCounter(uint16_t initialSequenceCount = 0) : sequenceCount(initialSequenceCount) {} + void increment() { sequenceCount = (sequenceCount + 1) % (ccsds::LIMIT_SEQUENCE_COUNT); } + void decrement() { sequenceCount = (sequenceCount - 1) % (ccsds::LIMIT_SEQUENCE_COUNT); } + uint16_t get() const { return this->sequenceCount; } + void reset(uint16_t toValue = 0) { sequenceCount = toValue % (ccsds::LIMIT_SEQUENCE_COUNT); } + SourceSequenceCounter& operator++(int) { + this->increment(); + return *this; } - void decrement() { - sequenceCount = (sequenceCount - 1) % (SpacePacketBase::LIMIT_SEQUENCE_COUNT); + SourceSequenceCounter& operator--(int) { + this->decrement(); + return *this; } - uint16_t get() { return this->sequenceCount; } - void reset(uint16_t toValue = 0) { - sequenceCount = toValue % (SpacePacketBase::LIMIT_SEQUENCE_COUNT); + SourceSequenceCounter& operator=(const uint16_t& newCount) { + sequenceCount = newCount; + return *this; } + + operator uint16_t() { return this->get(); } }; #endif /* FSFW_TMTCSERVICES_SOURCESEQUENCECOUNTER_H_ */ diff --git a/src/fsfw/tmtcservices/TmTcBridge.h b/src/fsfw/tmtcservices/TmTcBridge.h index d74c612b8..ed4d254e7 100644 --- a/src/fsfw/tmtcservices/TmTcBridge.h +++ b/src/fsfw/tmtcservices/TmTcBridge.h @@ -65,7 +65,7 @@ class TmTcBridge : public AcceptsTelemetryIF, ReturnValue_t performOperation(uint8_t operationCode = 0) override; /** AcceptsTelemetryIF override */ - MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel = 0) const override; + MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; /** AcceptsTelecommandsIF override */ uint32_t getIdentifier() const override; diff --git a/src/fsfw_hal/common/gpio/gpioDefinitions.h b/src/fsfw_hal/common/gpio/gpioDefinitions.h index eb90629eb..9f8b5e32c 100644 --- a/src/fsfw_hal/common/gpio/gpioDefinitions.h +++ b/src/fsfw_hal/common/gpio/gpioDefinitions.h @@ -5,6 +5,14 @@ #include #include +#ifdef PLATFORM_WIN +// Defined in Windows header for whatever reason, and leads to nameclash issues with +// class enums which have entries of the same name. +#undef IN +#undef OUT +#undef CALLBACK +#endif + using gpioId_t = uint16_t; namespace gpio { diff --git a/src/fsfw_hal/linux/CMakeLists.txt b/src/fsfw_hal/linux/CMakeLists.txt index ffa5f5ee5..d22d7ff13 100644 --- a/src/fsfw_hal/linux/CMakeLists.txt +++ b/src/fsfw_hal/linux/CMakeLists.txt @@ -5,11 +5,14 @@ endif() target_sources(${LIB_FSFW_NAME} PRIVATE UnixFileGuard.cpp CommandExecutor.cpp utility.cpp) +if(FSFW_HAL_LINUX_ADD_LIBGPIOD) + add_subdirectory(gpio) +endif() +if(FSFW_HAL_LINUX_ADD_SERIAL_DRIVERS) + add_subdirectory(serial) +endif() + if(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS) - if(FSFW_HAL_LINUX_ADD_LIBGPIOD) - add_subdirectory(gpio) - endif() - add_subdirectory(uart) # Adding those does not really make sense on Apple systems which are generally # host systems. It won't even compile as the headers are missing if(NOT APPLE) diff --git a/src/fsfw_hal/linux/CommandExecutor.cpp b/src/fsfw_hal/linux/CommandExecutor.cpp index 8b81d94f1..27cf8aca8 100644 --- a/src/fsfw_hal/linux/CommandExecutor.cpp +++ b/src/fsfw_hal/linux/CommandExecutor.cpp @@ -32,6 +32,8 @@ ReturnValue_t CommandExecutor::execute() { } else if (state == States::PENDING) { return COMMAND_PENDING; } + // Reset data in read vector + std::memset(readVec.data(), 0, readVec.size()); currentCmdFile = popen(currentCmd.c_str(), "r"); if (currentCmdFile == nullptr) { lastError = errno; @@ -205,3 +207,5 @@ ReturnValue_t CommandExecutor::executeBlocking() { } return returnvalue::OK; } + +const std::vector& CommandExecutor::getReadVector() const { return readVec; } diff --git a/src/fsfw_hal/linux/CommandExecutor.h b/src/fsfw_hal/linux/CommandExecutor.h index 81f989e76..80996bae4 100644 --- a/src/fsfw_hal/linux/CommandExecutor.h +++ b/src/fsfw_hal/linux/CommandExecutor.h @@ -107,6 +107,8 @@ class CommandExecutor { */ void reset(); + const std::vector& getReadVector() const; + private: std::string currentCmd; bool blocking = true; diff --git a/src/fsfw_hal/linux/i2c/I2cComIF.cpp b/src/fsfw_hal/linux/i2c/I2cComIF.cpp index 8edeb8ab2..1ad19e827 100644 --- a/src/fsfw_hal/linux/i2c/I2cComIF.cpp +++ b/src/fsfw_hal/linux/i2c/I2cComIF.cpp @@ -41,7 +41,7 @@ ReturnValue_t I2cComIF::initializeInterface(CookieIF* cookie) { i2cAddress = i2cCookie->getAddress(); - i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + auto i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); if (i2cDeviceMapIter == i2cDeviceMap.end()) { size_t maxReplyLen = i2cCookie->getMaxReplyLen(); I2cInstance i2cInstance = {std::vector(maxReplyLen), 0}; @@ -89,7 +89,7 @@ ReturnValue_t I2cComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, s } address_t i2cAddress = i2cCookie->getAddress(); - i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + auto i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); if (i2cDeviceMapIter == i2cDeviceMap.end()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "I2cComIF::sendMessage: i2cAddress of Cookie not " @@ -140,20 +140,19 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "I2cComIF::requestReceiveMessage: Invalid I2C Cookie!" << std::endl; #endif - i2cDeviceMapIter->second.replyLen = 0; return NULLPOINTER; } address_t i2cAddress = i2cCookie->getAddress(); - i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + auto i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); if (i2cDeviceMapIter == i2cDeviceMap.end()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "I2cComIF::requestReceiveMessage: i2cAddress of Cookie not " << "registered in i2cDeviceMap" << std::endl; #endif - i2cDeviceMapIter->second.replyLen = 0; return returnvalue::FAILED; } + i2cDeviceMapIter->second.replyLen = 0; deviceFile = i2cCookie->getDeviceFile(); UnixFileGuard fileHelper(deviceFile, &fd, O_RDWR, "I2cComIF::requestReceiveMessage"); @@ -162,7 +161,6 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe } result = openDevice(deviceFile, i2cAddress, &fd); if (result != returnvalue::OK) { - i2cDeviceMapIter->second.replyLen = 0; return result; } @@ -170,14 +168,19 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLe int readLen = read(fd, replyBuffer, requestLen); if (readLen != static_cast(requestLen)) { -#if FSFW_VERBOSE_LEVEL >= 1 and FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "I2cComIF::requestReceiveMessage: Reading from I2C " - << "device failed with error code " << errno << ". Description" - << " of error: " << strerror(errno) << std::endl; - sif::error << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from " << requestLen - << " bytes" << std::endl; +#if FSFW_VERBOSE_LEVEL >= 1 +#if FSFW_CPP_OSTREAM_ENABLED == 1 + if (readLen < 0) { + sif::warning << "I2cComIF::requestReceiveMessage: Reading from I2C " + << "device failed with error code " << errno << " | " << strerror(errno) + << std::endl; + } else { + sif::warning << "I2cComIF::requestReceiveMessage: Read only " << readLen << " from " + << requestLen << " bytes" << std::endl; + } +#else +#endif #endif - i2cDeviceMapIter->second.replyLen = 0; #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen << " bytes" << std::endl; @@ -204,7 +207,7 @@ ReturnValue_t I2cComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, } address_t i2cAddress = i2cCookie->getAddress(); - i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); + auto i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); if (i2cDeviceMapIter == i2cDeviceMap.end()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "I2cComIF::readReceivedMessage: i2cAddress of Cookie not " @@ -214,7 +217,7 @@ ReturnValue_t I2cComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, } *buffer = i2cDeviceMapIter->second.replyBuffer.data(); *size = i2cDeviceMapIter->second.replyLen; - + i2cDeviceMapIter->second.replyLen = 0; return returnvalue::OK; } diff --git a/src/fsfw_hal/linux/i2c/I2cComIF.h b/src/fsfw_hal/linux/i2c/I2cComIF.h index 0a15c3a4d..8c44cee01 100644 --- a/src/fsfw_hal/linux/i2c/I2cComIF.h +++ b/src/fsfw_hal/linux/i2c/I2cComIF.h @@ -36,12 +36,10 @@ class I2cComIF : public DeviceCommunicationIF, public SystemObject { }; using I2cDeviceMap = std::unordered_map; - using I2cDeviceMapIter = I2cDeviceMap::iterator; /* In this map all i2c devices will be registered with their address and * the appropriate file descriptor will be stored */ I2cDeviceMap i2cDeviceMap; - I2cDeviceMapIter i2cDeviceMapIter; /** * @brief This function opens an I2C device and binds the opened file diff --git a/src/fsfw_hal/linux/serial/CMakeLists.txt b/src/fsfw_hal/linux/serial/CMakeLists.txt new file mode 100644 index 000000000..07d20d29d --- /dev/null +++ b/src/fsfw_hal/linux/serial/CMakeLists.txt @@ -0,0 +1,2 @@ +target_sources(${LIB_FSFW_NAME} PUBLIC SerialComIF.cpp SerialCookie.cpp + helper.cpp) diff --git a/src/fsfw_hal/linux/uart/UartComIF.cpp b/src/fsfw_hal/linux/serial/SerialComIF.cpp similarity index 62% rename from src/fsfw_hal/linux/uart/UartComIF.cpp rename to src/fsfw_hal/linux/serial/SerialComIF.cpp index 8947c5622..177d74e45 100644 --- a/src/fsfw_hal/linux/uart/UartComIF.cpp +++ b/src/fsfw_hal/linux/serial/SerialComIF.cpp @@ -1,4 +1,4 @@ -#include "UartComIF.h" +#include "SerialComIF.h" #include #include @@ -11,19 +11,18 @@ #include "fsfw/serviceinterface.h" #include "fsfw_hal/linux/utility.h" -UartComIF::UartComIF(object_id_t objectId) : SystemObject(objectId) {} +SerialComIF::SerialComIF(object_id_t objectId) : SystemObject(objectId) {} -UartComIF::~UartComIF() {} +SerialComIF::~SerialComIF() {} -ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) { +ReturnValue_t SerialComIF::initializeInterface(CookieIF* cookie) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; if (cookie == nullptr) { return NULLPOINTER; } - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::error << "UartComIF::initializeInterface: Invalid UART Cookie!" << std::endl; @@ -33,7 +32,7 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) { deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { int fileDescriptor = configureUartPort(uartCookie); if (fileDescriptor < 0) { @@ -60,7 +59,7 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) { return returnvalue::OK; } -int UartComIF::configureUartPort(UartCookie* uartCookie) { +int SerialComIF::configureUartPort(SerialCookie* uartCookie) { struct termios options = {}; std::string deviceFile = uartCookie->getDeviceFile(); @@ -89,11 +88,11 @@ int UartComIF::configureUartPort(UartCookie* uartCookie) { return fd; } - setParityOptions(&options, uartCookie); + uart::setParity(options, uartCookie->getParity()); setStopBitOptions(&options, uartCookie); setDatasizeOptions(&options, uartCookie); setFixedOptions(&options); - setUartMode(&options, *uartCookie); + uart::setMode(options, uartCookie->getUartMode()); if (uartCookie->getInputShouldBeFlushed()) { tcflush(fd, TCIFLUSH); } @@ -102,7 +101,7 @@ int UartComIF::configureUartPort(UartCookie* uartCookie) { options.c_cc[VTIME] = 0; options.c_cc[VMIN] = 0; - configureBaudrate(&options, uartCookie); + uart::setBaudrate(options, uartCookie->getBaudrate()); /* Save option settings */ if (tcsetattr(fd, TCSANOW, &options) != 0) { @@ -115,24 +114,7 @@ int UartComIF::configureUartPort(UartCookie* uartCookie) { return fd; } -void UartComIF::setParityOptions(struct termios* options, UartCookie* uartCookie) { - /* Clear parity bit */ - options->c_cflag &= ~PARENB; - switch (uartCookie->getParity()) { - case Parity::EVEN: - options->c_cflag |= PARENB; - options->c_cflag &= ~PARODD; - break; - case Parity::ODD: - options->c_cflag |= PARENB; - options->c_cflag |= PARODD; - break; - default: - break; - } -} - -void UartComIF::setStopBitOptions(struct termios* options, UartCookie* uartCookie) { +void SerialComIF::setStopBitOptions(struct termios* options, SerialCookie* uartCookie) { /* Clear stop field. Sets stop bit to one bit */ options->c_cflag &= ~CSTOPB; switch (uartCookie->getStopBits()) { @@ -144,7 +126,7 @@ void UartComIF::setStopBitOptions(struct termios* options, UartCookie* uartCooki } } -void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCookie) { +void SerialComIF::setDatasizeOptions(struct termios* options, SerialCookie* uartCookie) { /* Clear size bits */ options->c_cflag &= ~CSIZE; switch (uartCookie->getBitsPerWord()) { @@ -168,7 +150,7 @@ void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCook } } -void UartComIF::setFixedOptions(struct termios* options) { +void SerialComIF::setFixedOptions(struct termios* options) { /* Disable RTS/CTS hardware flow control */ options->c_cflag &= ~CRTSCTS; /* Turn on READ & ignore ctrl lines (CLOCAL = 1) */ @@ -191,142 +173,9 @@ void UartComIF::setFixedOptions(struct termios* options) { options->c_oflag &= ~ONLCR; } -void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCookie) { - switch (uartCookie->getBaudrate()) { - case UartBaudRate::RATE_50: - cfsetispeed(options, B50); - cfsetospeed(options, B50); - break; - case UartBaudRate::RATE_75: - cfsetispeed(options, B75); - cfsetospeed(options, B75); - break; - case UartBaudRate::RATE_110: - cfsetispeed(options, B110); - cfsetospeed(options, B110); - break; - case UartBaudRate::RATE_134: - cfsetispeed(options, B134); - cfsetospeed(options, B134); - break; - case UartBaudRate::RATE_150: - cfsetispeed(options, B150); - cfsetospeed(options, B150); - break; - case UartBaudRate::RATE_200: - cfsetispeed(options, B200); - cfsetospeed(options, B200); - break; - case UartBaudRate::RATE_300: - cfsetispeed(options, B300); - cfsetospeed(options, B300); - break; - case UartBaudRate::RATE_600: - cfsetispeed(options, B600); - cfsetospeed(options, B600); - break; - case UartBaudRate::RATE_1200: - cfsetispeed(options, B1200); - cfsetospeed(options, B1200); - break; - case UartBaudRate::RATE_1800: - cfsetispeed(options, B1800); - cfsetospeed(options, B1800); - break; - case UartBaudRate::RATE_2400: - cfsetispeed(options, B2400); - cfsetospeed(options, B2400); - break; - case UartBaudRate::RATE_4800: - cfsetispeed(options, B4800); - cfsetospeed(options, B4800); - break; - case UartBaudRate::RATE_9600: - cfsetispeed(options, B9600); - cfsetospeed(options, B9600); - break; - case UartBaudRate::RATE_19200: - cfsetispeed(options, B19200); - cfsetospeed(options, B19200); - break; - case UartBaudRate::RATE_38400: - cfsetispeed(options, B38400); - cfsetospeed(options, B38400); - break; - case UartBaudRate::RATE_57600: - cfsetispeed(options, B57600); - cfsetospeed(options, B57600); - break; - case UartBaudRate::RATE_115200: - cfsetispeed(options, B115200); - cfsetospeed(options, B115200); - break; - case UartBaudRate::RATE_230400: - cfsetispeed(options, B230400); - cfsetospeed(options, B230400); - break; -#ifndef __APPLE__ - case UartBaudRate::RATE_460800: - cfsetispeed(options, B460800); - cfsetospeed(options, B460800); - break; - case UartBaudRate::RATE_500000: - cfsetispeed(options, B500000); - cfsetospeed(options, B500000); - break; - case UartBaudRate::RATE_576000: - cfsetispeed(options, B576000); - cfsetospeed(options, B576000); - break; - case UartBaudRate::RATE_921600: - cfsetispeed(options, B921600); - cfsetospeed(options, B921600); - break; - case UartBaudRate::RATE_1000000: - cfsetispeed(options, B1000000); - cfsetospeed(options, B1000000); - break; - case UartBaudRate::RATE_1152000: - cfsetispeed(options, B1152000); - cfsetospeed(options, B1152000); - break; - case UartBaudRate::RATE_1500000: - cfsetispeed(options, B1500000); - cfsetospeed(options, B1500000); - break; - case UartBaudRate::RATE_2000000: - cfsetispeed(options, B2000000); - cfsetospeed(options, B2000000); - break; - case UartBaudRate::RATE_2500000: - cfsetispeed(options, B2500000); - cfsetospeed(options, B2500000); - break; - case UartBaudRate::RATE_3000000: - cfsetispeed(options, B3000000); - cfsetospeed(options, B3000000); - break; - case UartBaudRate::RATE_3500000: - cfsetispeed(options, B3500000); - cfsetospeed(options, B3500000); - break; - case UartBaudRate::RATE_4000000: - cfsetispeed(options, B4000000); - cfsetospeed(options, B4000000); - break; -#endif // ! __APPLE__ - default: -#if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl; -#endif - break; - } -} - -ReturnValue_t UartComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { +ReturnValue_t SerialComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) { int fd = 0; std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; if (sendLen == 0) { return returnvalue::OK; @@ -339,7 +188,7 @@ ReturnValue_t UartComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, return returnvalue::FAILED; } - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "UartComIF::sendMessasge: Invalid UART Cookie!" << std::endl; @@ -348,7 +197,7 @@ ReturnValue_t UartComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, } deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "UartComIF::sendMessage: Device file " << deviceFile << "not in UART map" @@ -370,13 +219,12 @@ ReturnValue_t UartComIF::sendMessage(CookieIF* cookie, const uint8_t* sendData, return returnvalue::OK; } -ReturnValue_t UartComIF::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; } +ReturnValue_t SerialComIF::getSendSuccess(CookieIF* cookie) { return returnvalue::OK; } -ReturnValue_t UartComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { +ReturnValue_t SerialComIF::requestReceiveMessage(CookieIF* cookie, size_t requestLen) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "UartComIF::requestReceiveMessage: Invalid Uart Cookie!" << std::endl; @@ -386,7 +234,7 @@ ReturnValue_t UartComIF::requestReceiveMessage(CookieIF* cookie, size_t requestL UartModes uartMode = uartCookie->getUartMode(); deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartMode == UartModes::NON_CANONICAL and requestLen == 0) { return returnvalue::OK; @@ -409,8 +257,8 @@ ReturnValue_t UartComIF::requestReceiveMessage(CookieIF* cookie, size_t requestL } } -ReturnValue_t UartComIF::handleCanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, - size_t requestLen) { +ReturnValue_t SerialComIF::handleCanonicalRead(SerialCookie& uartCookie, + UartDeviceMap::iterator& iter, size_t requestLen) { ReturnValue_t result = returnvalue::OK; uint8_t maxReadCycles = uartCookie.getReadCycles(); uint8_t currentReadCycles = 0; @@ -467,8 +315,9 @@ ReturnValue_t UartComIF::handleCanonicalRead(UartCookie& uartCookie, UartDeviceM return result; } -ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, - size_t requestLen) { +ReturnValue_t SerialComIF::handleNoncanonicalRead(SerialCookie& uartCookie, + UartDeviceMap::iterator& iter, + size_t requestLen) { int fd = iter->second.fileDescriptor; auto bufferPtr = iter->second.replyBuffer.data(); // Size check to prevent buffer overflow @@ -501,11 +350,10 @@ ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie& uartCookie, UartDevi return returnvalue::OK; } -ReturnValue_t UartComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { +ReturnValue_t SerialComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, size_t* size) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "UartComIF::readReceivedMessage: Invalid uart cookie!" << std::endl; @@ -514,7 +362,7 @@ ReturnValue_t UartComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, } deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter == uartDeviceMap.end()) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::debug << "UartComIF::readReceivedMessage: Device file " << deviceFile << " not in uart map" @@ -532,10 +380,9 @@ ReturnValue_t UartComIF::readReceivedMessage(CookieIF* cookie, uint8_t** buffer, return returnvalue::OK; } -ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF* cookie) { +ReturnValue_t SerialComIF::flushUartRxBuffer(CookieIF* cookie) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "UartComIF::flushUartRxBuffer: Invalid uart cookie!" << std::endl; @@ -543,7 +390,7 @@ ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF* cookie) { return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCIFLUSH); @@ -552,10 +399,9 @@ ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF* cookie) { return returnvalue::FAILED; } -ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF* cookie) { +ReturnValue_t SerialComIF::flushUartTxBuffer(CookieIF* cookie) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "UartComIF::flushUartTxBuffer: Invalid uart cookie!" << std::endl; @@ -563,7 +409,7 @@ ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF* cookie) { return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCOFLUSH); @@ -572,10 +418,9 @@ ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF* cookie) { return returnvalue::FAILED; } -ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF* cookie) { +ReturnValue_t SerialComIF::flushUartTxAndRxBuf(CookieIF* cookie) { std::string deviceFile; - UartDeviceMapIter uartDeviceMapIter; - UartCookie* uartCookie = dynamic_cast(cookie); + SerialCookie* uartCookie = dynamic_cast(cookie); if (uartCookie == nullptr) { #if FSFW_CPP_OSTREAM_ENABLED == 1 sif::warning << "UartComIF::flushUartTxAndRxBuf: Invalid uart cookie!" << std::endl; @@ -583,7 +428,7 @@ ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF* cookie) { return NULLPOINTER; } deviceFile = uartCookie->getDeviceFile(); - uartDeviceMapIter = uartDeviceMap.find(deviceFile); + auto uartDeviceMapIter = uartDeviceMap.find(deviceFile); if (uartDeviceMapIter != uartDeviceMap.end()) { int fd = uartDeviceMapIter->second.fileDescriptor; tcflush(fd, TCIOFLUSH); @@ -591,13 +436,3 @@ ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF* cookie) { } return returnvalue::FAILED; } - -void UartComIF::setUartMode(struct termios* options, UartCookie& uartCookie) { - UartModes uartMode = uartCookie.getUartMode(); - if (uartMode == UartModes::NON_CANONICAL) { - /* Disable canonical mode */ - options->c_lflag &= ~ICANON; - } else if (uartMode == UartModes::CANONICAL) { - options->c_lflag |= ICANON; - } -} diff --git a/src/fsfw_hal/linux/uart/UartComIF.h b/src/fsfw_hal/linux/serial/SerialComIF.h similarity index 69% rename from src/fsfw_hal/linux/uart/UartComIF.h rename to src/fsfw_hal/linux/serial/SerialComIF.h index 77318166e..907da7e00 100644 --- a/src/fsfw_hal/linux/uart/UartComIF.h +++ b/src/fsfw_hal/linux/serial/SerialComIF.h @@ -3,12 +3,12 @@ #include #include +#include +#include #include #include -#include "UartCookie.h" - /** * @brief This is the communication interface to access serial ports on linux based operating * systems. @@ -18,7 +18,7 @@ * * @author J. Meier */ -class UartComIF : public DeviceCommunicationIF, public SystemObject { +class SerialComIF : public DeviceCommunicationIF, public SystemObject { public: static constexpr uint8_t uartRetvalId = CLASS_ID::HAL_UART; @@ -26,9 +26,9 @@ class UartComIF : public DeviceCommunicationIF, public SystemObject { static constexpr ReturnValue_t UART_READ_SIZE_MISSMATCH = returnvalue::makeCode(uartRetvalId, 2); static constexpr ReturnValue_t UART_RX_BUFFER_TOO_SMALL = returnvalue::makeCode(uartRetvalId, 3); - UartComIF(object_id_t objectId); + SerialComIF(object_id_t objectId); - virtual ~UartComIF(); + virtual ~SerialComIF(); ReturnValue_t initializeInterface(CookieIF* cookie) override; ReturnValue_t sendMessage(CookieIF* cookie, const uint8_t* sendData, size_t sendLen) override; @@ -62,7 +62,6 @@ class UartComIF : public DeviceCommunicationIF, public SystemObject { }; using UartDeviceMap = std::unordered_map; - using UartDeviceMapIter = UartDeviceMap::iterator; /** * The uart devie map stores informations of initialized uart ports. @@ -76,20 +75,9 @@ class UartComIF : public DeviceCommunicationIF, public SystemObject { * uart device file, baudrate, parity, stopbits etc. * @return The file descriptor of the configured uart. */ - int configureUartPort(UartCookie* uartCookie); + int configureUartPort(SerialCookie* uartCookie); - /** - * @brief This function adds the parity settings to the termios options struct. - * - * @param options Pointer to termios options struct which will be modified to enable or disable - * parity checking. - * @param uartCookie Pointer to uart cookie containing the information about the desired - * parity settings. - * - */ - void setParityOptions(struct termios* options, UartCookie* uartCookie); - - void setStopBitOptions(struct termios* options, UartCookie* uartCookie); + void setStopBitOptions(struct termios* options, SerialCookie* uartCookie); /** * @brief This function sets options which are not configurable by the uartCookie. @@ -99,19 +87,11 @@ class UartComIF : public DeviceCommunicationIF, public SystemObject { /** * @brief With this function the datasize settings are added to the termios options struct. */ - void setDatasizeOptions(struct termios* options, UartCookie* uartCookie); + void setDatasizeOptions(struct termios* options, SerialCookie* uartCookie); - /** - * @brief This functions adds the baudrate specified in the uartCookie to the termios options - * struct. - */ - void configureBaudrate(struct termios* options, UartCookie* uartCookie); - - void setUartMode(struct termios* options, UartCookie& uartCookie); - - ReturnValue_t handleCanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, + ReturnValue_t handleCanonicalRead(SerialCookie& uartCookie, UartDeviceMap::iterator& iter, size_t requestLen); - ReturnValue_t handleNoncanonicalRead(UartCookie& uartCookie, UartDeviceMapIter& iter, + ReturnValue_t handleNoncanonicalRead(SerialCookie& uartCookie, UartDeviceMap::iterator& iter, size_t requestLen); }; diff --git a/src/fsfw_hal/linux/serial/SerialCookie.cpp b/src/fsfw_hal/linux/serial/SerialCookie.cpp new file mode 100644 index 000000000..1b7e9b511 --- /dev/null +++ b/src/fsfw_hal/linux/serial/SerialCookie.cpp @@ -0,0 +1,51 @@ +#include "SerialCookie.h" + +#include + +SerialCookie::SerialCookie(object_id_t handlerId, std::string deviceFile, UartBaudRate baudrate, + size_t maxReplyLen, UartModes uartMode) + : handlerId(handlerId), + deviceFile(deviceFile), + uartMode(uartMode), + baudrate(baudrate), + maxReplyLen(maxReplyLen) {} + +SerialCookie::~SerialCookie() {} + +UartBaudRate SerialCookie::getBaudrate() const { return baudrate; } + +size_t SerialCookie::getMaxReplyLen() const { return maxReplyLen; } + +std::string SerialCookie::getDeviceFile() const { return deviceFile; } + +void SerialCookie::setParityOdd() { parity = Parity::ODD; } + +void SerialCookie::setParityEven() { parity = Parity::EVEN; } + +Parity SerialCookie::getParity() const { return parity; } + +void SerialCookie::setBitsPerWord(BitsPerWord bitsPerWord_) { bitsPerWord = bitsPerWord_; } + +BitsPerWord SerialCookie::getBitsPerWord() const { return bitsPerWord; } + +StopBits SerialCookie::getStopBits() const { return stopBits; } + +void SerialCookie::setTwoStopBits() { stopBits = StopBits::TWO_STOP_BITS; } + +void SerialCookie::setOneStopBit() { stopBits = StopBits::ONE_STOP_BIT; } + +UartModes SerialCookie::getUartMode() const { return uartMode; } + +void SerialCookie::setReadCycles(uint8_t readCycles) { this->readCycles = readCycles; } + +void SerialCookie::setToFlushInput(bool enable) { this->flushInput = enable; } + +uint8_t SerialCookie::getReadCycles() const { return readCycles; } + +bool SerialCookie::getInputShouldBeFlushed() { return this->flushInput; } + +object_id_t SerialCookie::getHandlerId() const { return this->handlerId; } + +void SerialCookie::setNoFixedSizeReply() { replySizeFixed = false; } + +bool SerialCookie::isReplySizeFixed() { return replySizeFixed; } diff --git a/src/fsfw_hal/linux/uart/UartCookie.h b/src/fsfw_hal/linux/serial/SerialCookie.h similarity index 78% rename from src/fsfw_hal/linux/uart/UartCookie.h rename to src/fsfw_hal/linux/serial/SerialCookie.h index 6840b3521..7a9c0eca5 100644 --- a/src/fsfw_hal/linux/uart/UartCookie.h +++ b/src/fsfw_hal/linux/serial/SerialCookie.h @@ -3,50 +3,10 @@ #include #include +#include #include -enum class Parity { NONE, EVEN, ODD }; - -enum class StopBits { ONE_STOP_BIT, TWO_STOP_BITS }; - -enum class UartModes { CANONICAL, NON_CANONICAL }; - -enum class BitsPerWord { BITS_5, BITS_6, BITS_7, BITS_8 }; - -enum class UartBaudRate { - RATE_50, - RATE_75, - RATE_110, - RATE_134, - RATE_150, - RATE_200, - RATE_300, - RATE_600, - RATE_1200, - RATE_1800, - RATE_2400, - RATE_4800, - RATE_9600, - RATE_19200, - RATE_38400, - RATE_57600, - RATE_115200, - RATE_230400, - RATE_460800, - RATE_500000, - RATE_576000, - RATE_921600, - RATE_1000000, - RATE_1152000, - RATE_1500000, - RATE_2000000, - RATE_2500000, - RATE_3000000, - RATE_3500000, - RATE_4000000 -}; - /** * @brief Cookie for the UartComIF. There are many options available to configure the UART driver. * The constructor only requests for common options like the baudrate. Other options can @@ -54,7 +14,7 @@ enum class UartBaudRate { * * @author J. Meier */ -class UartCookie : public CookieIF { +class SerialCookie : public CookieIF { public: /** * @brief Constructor for the uart cookie. @@ -69,10 +29,10 @@ class UartCookie : public CookieIF { * 8 databits (number of bits transfered with one uart frame) * One stop bit */ - UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode, - UartBaudRate baudrate, size_t maxReplyLen); + SerialCookie(object_id_t handlerId, std::string deviceFile, UartBaudRate baudrate, + size_t maxReplyLen, UartModes uartMode = UartModes::NON_CANONICAL); - virtual ~UartCookie(); + virtual ~SerialCookie(); UartBaudRate getBaudrate() const; size_t getMaxReplyLen() const; diff --git a/src/fsfw_hal/linux/serial/helper.cpp b/src/fsfw_hal/linux/serial/helper.cpp new file mode 100644 index 000000000..ba975f2f9 --- /dev/null +++ b/src/fsfw_hal/linux/serial/helper.cpp @@ -0,0 +1,163 @@ +#include +#include + +#include "fsfw/serviceinterface.h" + +void uart::setMode(struct termios& options, UartModes mode) { + if (mode == UartModes::NON_CANONICAL) { + /* Disable canonical mode */ + options.c_lflag &= ~ICANON; + } else if (mode == UartModes::CANONICAL) { + options.c_lflag |= ICANON; + } +} + +void uart::setBaudrate(struct termios& options, UartBaudRate baud) { + switch (baud) { + case UartBaudRate::RATE_50: + cfsetspeed(&options, B50); + break; + case UartBaudRate::RATE_75: + cfsetspeed(&options, B75); + break; + case UartBaudRate::RATE_110: + cfsetspeed(&options, B110); + break; + case UartBaudRate::RATE_134: + cfsetspeed(&options, B134); + break; + case UartBaudRate::RATE_150: + cfsetspeed(&options, B150); + break; + case UartBaudRate::RATE_200: + cfsetspeed(&options, B200); + break; + case UartBaudRate::RATE_300: + cfsetspeed(&options, B300); + break; + case UartBaudRate::RATE_600: + cfsetspeed(&options, B600); + break; + case UartBaudRate::RATE_1200: + cfsetspeed(&options, B1200); + break; + case UartBaudRate::RATE_1800: + cfsetspeed(&options, B1800); + break; + case UartBaudRate::RATE_2400: + cfsetspeed(&options, B2400); + break; + case UartBaudRate::RATE_4800: + cfsetspeed(&options, B4800); + break; + case UartBaudRate::RATE_9600: + cfsetspeed(&options, B9600); + break; + case UartBaudRate::RATE_19200: + cfsetspeed(&options, B19200); + break; + case UartBaudRate::RATE_38400: + cfsetspeed(&options, B38400); + break; + case UartBaudRate::RATE_57600: + cfsetspeed(&options, B57600); + break; + case UartBaudRate::RATE_115200: + cfsetspeed(&options, B115200); + break; + case UartBaudRate::RATE_230400: + cfsetspeed(&options, B230400); + break; +#ifndef __APPLE__ + case UartBaudRate::RATE_460800: + cfsetspeed(&options, B460800); + break; + case UartBaudRate::RATE_500000: + cfsetspeed(&options, B500000); + break; + case UartBaudRate::RATE_576000: + cfsetspeed(&options, B576000); + break; + case UartBaudRate::RATE_921600: + cfsetspeed(&options, B921600); + break; + case UartBaudRate::RATE_1000000: + cfsetspeed(&options, B1000000); + break; + case UartBaudRate::RATE_1152000: + cfsetspeed(&options, B1152000); + break; + case UartBaudRate::RATE_1500000: + cfsetspeed(&options, B1500000); + break; + case UartBaudRate::RATE_2000000: + cfsetspeed(&options, B2000000); + break; + case UartBaudRate::RATE_2500000: + cfsetspeed(&options, B2500000); + break; + case UartBaudRate::RATE_3000000: + cfsetspeed(&options, B3000000); + break; + case UartBaudRate::RATE_3500000: + cfsetspeed(&options, B3500000); + break; + case UartBaudRate::RATE_4000000: + cfsetspeed(&options, B4000000); + break; +#endif // ! __APPLE__ + default: +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl; +#endif + break; + } +} + +void uart::setBitsPerWord(struct termios& options, BitsPerWord bits) { + options.c_cflag &= ~CSIZE; // Clear all the size bits + if (bits == BitsPerWord::BITS_5) { + options.c_cflag |= CS5; + } else if (bits == BitsPerWord::BITS_6) { + options.c_cflag |= CS6; + } else if (bits == BitsPerWord::BITS_7) { + options.c_cflag |= CS7; + } else if (bits == BitsPerWord::BITS_8) { + options.c_cflag |= CS8; + } +} + +void uart::enableRead(struct termios& options) { options.c_cflag |= CREAD; } + +void uart::ignoreCtrlLines(struct termios& options) { options.c_cflag |= CLOCAL; } + +void uart::setParity(struct termios& options, Parity parity) { + /* Clear parity bit */ + options.c_cflag &= ~PARENB; + switch (parity) { + case Parity::EVEN: + options.c_cflag |= PARENB; + options.c_cflag &= ~PARODD; + break; + case Parity::ODD: + options.c_cflag |= PARENB; + options.c_cflag |= PARODD; + break; + default: + break; + } +} + +int uart::readCountersAndErrors(int serialPort, serial_icounter_struct& icounter) { + return ioctl(serialPort, TIOCGICOUNT, &icounter); +} + +void uart::setStopbits(struct termios& options, StopBits bits) { + if (bits == StopBits::TWO_STOP_BITS) { + // Use two stop bits + options.c_cflag |= CSTOPB; + } else { + // Clear stop field, only one stop bit used in communication + options.c_cflag &= ~CSTOPB; + } +} diff --git a/src/fsfw_hal/linux/serial/helper.h b/src/fsfw_hal/linux/serial/helper.h new file mode 100644 index 000000000..7f067d001 --- /dev/null +++ b/src/fsfw_hal/linux/serial/helper.h @@ -0,0 +1,71 @@ +#ifndef FSFW_HAL_LINUX_UART_HELPER_H_ +#define FSFW_HAL_LINUX_UART_HELPER_H_ + +#include +#include + +enum class Parity { NONE, EVEN, ODD }; + +enum class StopBits { ONE_STOP_BIT, TWO_STOP_BITS }; + +enum class UartModes { CANONICAL, NON_CANONICAL }; + +enum class BitsPerWord { BITS_5, BITS_6, BITS_7, BITS_8 }; + +enum class UartBaudRate { + RATE_50, + RATE_75, + RATE_110, + RATE_134, + RATE_150, + RATE_200, + RATE_300, + RATE_600, + RATE_1200, + RATE_1800, + RATE_2400, + RATE_4800, + RATE_9600, + RATE_19200, + RATE_38400, + RATE_57600, + RATE_115200, + RATE_230400, + RATE_460800, + RATE_500000, + RATE_576000, + RATE_921600, + RATE_1000000, + RATE_1152000, + RATE_1500000, + RATE_2000000, + RATE_2500000, + RATE_3000000, + RATE_3500000, + RATE_4000000 +}; + +namespace uart { + +void setMode(struct termios& options, UartModes mode); +/** + * @brief This functions adds the baudrate specified in the uartCookie to the termios options + * struct. + */ +void setBaudrate(struct termios& options, UartBaudRate baud); + +void setStopbits(struct termios& options, StopBits bits); + +void setBitsPerWord(struct termios& options, BitsPerWord bits); + +void enableRead(struct termios& options); + +void setParity(struct termios& options, Parity parity); + +void ignoreCtrlLines(struct termios& options); + +int readCountersAndErrors(int serialPort, serial_icounter_struct& icounter); + +} // namespace uart + +#endif /* FSFW_HAL_LINUX_UART_HELPER_H_ */ diff --git a/src/fsfw_hal/linux/uart/CMakeLists.txt b/src/fsfw_hal/linux/uart/CMakeLists.txt deleted file mode 100644 index 9cad62a40..000000000 --- a/src/fsfw_hal/linux/uart/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -target_sources(${LIB_FSFW_NAME} PUBLIC UartComIF.cpp UartCookie.cpp) diff --git a/src/fsfw_hal/linux/uart/UartCookie.cpp b/src/fsfw_hal/linux/uart/UartCookie.cpp deleted file mode 100644 index 3fedc9d44..000000000 --- a/src/fsfw_hal/linux/uart/UartCookie.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "UartCookie.h" - -#include - -UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode, - UartBaudRate baudrate, size_t maxReplyLen) - : handlerId(handlerId), - deviceFile(deviceFile), - uartMode(uartMode), - baudrate(baudrate), - maxReplyLen(maxReplyLen) {} - -UartCookie::~UartCookie() {} - -UartBaudRate UartCookie::getBaudrate() const { return baudrate; } - -size_t UartCookie::getMaxReplyLen() const { return maxReplyLen; } - -std::string UartCookie::getDeviceFile() const { return deviceFile; } - -void UartCookie::setParityOdd() { parity = Parity::ODD; } - -void UartCookie::setParityEven() { parity = Parity::EVEN; } - -Parity UartCookie::getParity() const { return parity; } - -void UartCookie::setBitsPerWord(BitsPerWord bitsPerWord_) { bitsPerWord = bitsPerWord_; } - -BitsPerWord UartCookie::getBitsPerWord() const { return bitsPerWord; } - -StopBits UartCookie::getStopBits() const { return stopBits; } - -void UartCookie::setTwoStopBits() { stopBits = StopBits::TWO_STOP_BITS; } - -void UartCookie::setOneStopBit() { stopBits = StopBits::ONE_STOP_BIT; } - -UartModes UartCookie::getUartMode() const { return uartMode; } - -void UartCookie::setReadCycles(uint8_t readCycles) { this->readCycles = readCycles; } - -void UartCookie::setToFlushInput(bool enable) { this->flushInput = enable; } - -uint8_t UartCookie::getReadCycles() const { return readCycles; } - -bool UartCookie::getInputShouldBeFlushed() { return this->flushInput; } - -object_id_t UartCookie::getHandlerId() const { return this->handlerId; } - -void UartCookie::setNoFixedSizeReply() { replySizeFixed = false; } - -bool UartCookie::isReplySizeFixed() { return replySizeFixed; } diff --git a/src/fsfw_hal/linux/uio/UioMapper.cpp b/src/fsfw_hal/linux/uio/UioMapper.cpp index 3d7e5987e..33e4fd97a 100644 --- a/src/fsfw_hal/linux/uio/UioMapper.cpp +++ b/src/fsfw_hal/linux/uio/UioMapper.cpp @@ -1,6 +1,7 @@ #include "UioMapper.h" #include +#include #include #include @@ -13,7 +14,23 @@ const char UioMapper::UIO_PATH_PREFIX[] = "/sys/class/uio/"; const char UioMapper::MAP_SUBSTR[] = "/maps/map"; const char UioMapper::SIZE_FILE_PATH[] = "/size"; -UioMapper::UioMapper(std::string uioFile, int mapNum) : uioFile(uioFile), mapNum(mapNum) {} +UioMapper::UioMapper(std::string uioFile, int mapNum) : mapNum(mapNum) { + struct stat buf; + lstat(uioFile.c_str(), &buf); + if (S_ISLNK(buf.st_mode)) { + char* res = realpath(uioFile.c_str(), nullptr); + if (res) { + this->uioFile = res; + free(res); + } else { +#if FSFW_CPP_OSTREAM_ENABLED == 1 + sif::error << "Could not resolve real path of UIO file " << uioFile << std::endl; +#endif + } + } else { + this->uioFile = std::move(uioFile); + } +} UioMapper::~UioMapper() {} @@ -22,7 +39,7 @@ ReturnValue_t UioMapper::getMappedAdress(uint32_t** address, Permissions permiss int fd = open(uioFile.c_str(), O_RDWR); if (fd < 1) { #if FSFW_CPP_OSTREAM_ENABLED == 1 - sif::error << "PtmeAxiConfig::initialize: Invalid UIO device file" << std::endl; + sif::error << "UioMapper::getMappedAdress: Invalid UIO device file " << uioFile << std::endl; #endif return returnvalue::FAILED; } diff --git a/unittests/container/TestArrayList.cpp b/unittests/container/TestArrayList.cpp index 69b08faae..0c3276855 100644 --- a/unittests/container/TestArrayList.cpp +++ b/unittests/container/TestArrayList.cpp @@ -43,7 +43,7 @@ TEST_CASE("Array List", "[containers]") { for (auto i = 0; i < 20; i++) { REQUIRE(list.insert(i) == static_cast(returnvalue::OK)); } - REQUIRE(list.insert(20) == static_cast(ArrayList::FULL)); + REQUIRE(list.insert(20) == static_cast(containers::LIST_FULL)); ArrayList::Iterator it = list.begin(); REQUIRE((*it) == 0); it++; diff --git a/unittests/container/TestFixedArrayList.cpp b/unittests/container/TestFixedArrayList.cpp index 876d6792d..f3131c64d 100644 --- a/unittests/container/TestFixedArrayList.cpp +++ b/unittests/container/TestFixedArrayList.cpp @@ -31,7 +31,7 @@ TEST_CASE("FixedArrayList Tests", "[containers]") { for (auto i = 1; i < 260; i++) { REQUIRE(list.insert(i) == static_cast(returnvalue::OK)); } - REQUIRE(list.insert(260) == static_cast(ArrayList::FULL)); + REQUIRE(list.insert(260) == static_cast(containers::LIST_FULL)); list.clear(); REQUIRE(list.size == 0); } diff --git a/unittests/container/TestFixedMap.cpp b/unittests/container/TestFixedMap.cpp index 8bfe9fed3..83ff975d7 100644 --- a/unittests/container/TestFixedMap.cpp +++ b/unittests/container/TestFixedMap.cpp @@ -7,6 +7,8 @@ template class FixedMap; +using namespace returnvalue; + TEST_CASE("FixedMap Tests", "[containers]") { INFO("FixedMap Tests"); @@ -24,9 +26,9 @@ TEST_CASE("FixedMap Tests", "[containers]") { REQUIRE(map.find(i)->second == i + 1); REQUIRE(not map.empty()); } - REQUIRE(map.insert(0, 0) == static_cast(FixedMap::KEY_ALREADY_EXISTS)); - REQUIRE(map.insert(31, 0) == static_cast(FixedMap::MAP_FULL)); - REQUIRE(map.exists(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.insert(0, 0) == static_cast(containers::KEY_ALREADY_EXISTS)); + REQUIRE(map.insert(31, 0) == static_cast(containers::MAP_FULL)); + REQUIRE(map.exists(31) == static_cast(containers::KEY_DOES_NOT_EXIST)); REQUIRE(map.size() == 30); REQUIRE(map.full()); { @@ -34,15 +36,14 @@ TEST_CASE("FixedMap Tests", "[containers]") { REQUIRE(map.find(5, &ptr) == static_cast(returnvalue::OK)); REQUIRE(*ptr == 6); REQUIRE(*(map.findValue(6)) == 7); - REQUIRE(map.find(31, &ptr) == - static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.find(31, &ptr) == static_cast(containers::KEY_DOES_NOT_EXIST)); } REQUIRE(map.getSerializedSize() == (sizeof(uint32_t) + 30 * (sizeof(uint32_t) + sizeof(uint16_t)))); REQUIRE(map.erase(2) == static_cast(returnvalue::OK)); - REQUIRE(map.erase(31) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); - REQUIRE(map.exists(2) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.erase(31) == static_cast(containers::KEY_DOES_NOT_EXIST)); + REQUIRE(map.exists(2) == static_cast(containers::KEY_DOES_NOT_EXIST)); REQUIRE(map.size() == 29); for (auto element : map) { @@ -79,8 +80,7 @@ TEST_CASE("FixedMap Tests", "[containers]") { REQUIRE(map.insert(37, 38, nullptr) == static_cast(returnvalue::OK)); REQUIRE(map.find(37)->second == 38); REQUIRE(map.size() == 2); - REQUIRE(map.insert(37, 24, nullptr) == - static_cast(FixedMap::KEY_ALREADY_EXISTS)); + REQUIRE(map.insert(37, 24, nullptr) == static_cast(containers::KEY_ALREADY_EXISTS)); REQUIRE(map.find(37)->second != 24); REQUIRE(map.size() == 2); }; @@ -137,7 +137,7 @@ TEST_CASE("FixedMap Tests", "[containers]") { FixedMap::Iterator it; std::pair pair = std::make_pair(44, 43); it = FixedMap::Iterator(&pair); - REQUIRE(map.erase(&it) == static_cast(FixedMap::KEY_DOES_NOT_EXIST)); + REQUIRE(map.erase(&it) == static_cast(containers::KEY_DOES_NOT_EXIST)); REQUIRE(map.find(45) == map.end()); size_t toLargeMap = 100; const uint8_t* ptr = reinterpret_cast(&toLargeMap); diff --git a/unittests/globalfunctions/testTimevalOperations.cpp b/unittests/globalfunctions/testTimevalOperations.cpp index 155e6b153..0f89338bb 100644 --- a/unittests/globalfunctions/testTimevalOperations.cpp +++ b/unittests/globalfunctions/testTimevalOperations.cpp @@ -56,61 +56,65 @@ TEST_CASE("TimevalTest", "[timevalOperations]") { } SECTION("Operators") { timeval t1; - t1.tv_sec = 1648227422; - t1.tv_usec = 123456; - timeval t2; - t2.tv_sec = 1648227422; - t2.tv_usec = 123456; - timeval t3 = t1 - t2; - REQUIRE(t3.tv_sec == 0); - REQUIRE(t3.tv_usec == 0); - timeval t4 = t1 - t3; - REQUIRE(t4.tv_sec == 1648227422); - REQUIRE(t4.tv_usec == 123456); - timeval t5 = t3 - t1; - REQUIRE(t5.tv_sec == -1648227422); - REQUIRE(t5.tv_usec == -123456); + if (sizeof(t1.tv_sec) == 8) { + t1.tv_sec = 1648227422; + t1.tv_usec = 123456; + timeval t2; + t2.tv_sec = 1648227422; + t2.tv_usec = 123456; + timeval t3 = t1 - t2; + REQUIRE(t3.tv_sec == 0); + REQUIRE(t3.tv_usec == 0); + timeval t4 = t1 - t3; + REQUIRE(t4.tv_sec == 1648227422); + REQUIRE(t4.tv_usec == 123456); + timeval t5 = t3 - t1; + REQUIRE(t5.tv_sec == -1648227422); + REQUIRE(t5.tv_usec == -123456); - timeval t6; - t6.tv_sec = 1648227400; - t6.tv_usec = 999999; + timeval t6; + t6.tv_sec = 1648227400; + t6.tv_usec = 999999; - timeval t7 = t6 + t1; - REQUIRE(t7.tv_sec == (1648227422ull + 1648227400ull + 1ull)); - REQUIRE(t7.tv_usec == 123455); + timeval t7 = t6 + t1; + // Overflow test + REQUIRE(t7.tv_sec == (1648227422ull + 1648227400ull + 1ull)); - timeval t8 = t1 - t6; - REQUIRE(t8.tv_sec == 1648227422 - 1648227400 - 1); - REQUIRE(t8.tv_usec == 123457); + REQUIRE(t7.tv_usec == 123455); - double scalar = 2; - timeval t9 = t1 * scalar; - REQUIRE(t9.tv_sec == 3296454844); - REQUIRE(t9.tv_usec == 246912); - timeval t10 = scalar * t1; - REQUIRE(t10.tv_sec == 3296454844); - REQUIRE(t10.tv_usec == 246912); - timeval t11 = t6 * scalar; - REQUIRE(t11.tv_sec == (3296454800 + 1)); - REQUIRE(t11.tv_usec == 999998); + timeval t8 = t1 - t6; + REQUIRE(t8.tv_sec == 1648227422 - 1648227400 - 1); + REQUIRE(t8.tv_usec == 123457); - timeval t12 = t1 / scalar; - REQUIRE(t12.tv_sec == 824113711); - REQUIRE(t12.tv_usec == 61728); + double scalar = 2; + timeval t9 = t1 * scalar; + REQUIRE(t9.tv_sec == 3296454844); + REQUIRE(t9.tv_usec == 246912); + timeval t10 = scalar * t1; + REQUIRE(t10.tv_sec == 3296454844); + REQUIRE(t10.tv_usec == 246912); + timeval t11 = t6 * scalar; + REQUIRE(t11.tv_sec == (3296454800 + 1)); + REQUIRE(t11.tv_usec == 999998); - timeval t13 = t6 / scalar; - REQUIRE(t13.tv_sec == 824113700); - // Rounding issue - REQUIRE(t13.tv_usec == 499999); + timeval t12 = t1 / scalar; + REQUIRE(t12.tv_sec == 824113711); + REQUIRE(t12.tv_usec == 61728); - double scalar2 = t9 / t1; - REQUIRE(scalar2 == Catch::Approx(2.0)); - double scalar3 = t1 / t6; - REQUIRE(scalar3 == Catch::Approx(1.000000013)); - double scalar4 = t3 / t1; - REQUIRE(scalar4 == Catch::Approx(0)); - double scalar5 = t12 / t1; - REQUIRE(scalar5 == Catch::Approx(0.5)); + timeval t13 = t6 / scalar; + REQUIRE(t13.tv_sec == 824113700); + // Rounding issue + REQUIRE(t13.tv_usec == 499999); + + double scalar2 = t9 / t1; + REQUIRE(scalar2 == Catch::Approx(2.0)); + double scalar3 = t1 / t6; + REQUIRE(scalar3 == Catch::Approx(1.000000013)); + double scalar4 = t3 / t1; + REQUIRE(scalar4 == Catch::Approx(0)); + double scalar5 = t12 / t1; + REQUIRE(scalar5 == Catch::Approx(0.5)); + } } SECTION("timevalOperations::toTimeval") { diff --git a/unittests/hal/CMakeLists.txt b/unittests/hal/CMakeLists.txt index 5d1c78465..76aabd51a 100644 --- a/unittests/hal/CMakeLists.txt +++ b/unittests/hal/CMakeLists.txt @@ -1,2 +1,5 @@ -target_sources(${FSFW_TEST_TGT} PRIVATE testCommandExecutor.cpp - testHostFilesystem.cpp testFsMock.cpp) +target_sources(${FSFW_TEST_TGT} PRIVATE testHostFilesystem.cpp testFsMock.cpp) + +if(UNIX) + target_sources(${FSFW_TEST_TGT} PRIVATE testCommandExecutor.cpp) +endif() diff --git a/unittests/lcov_epilog.html b/unittests/lcov_epilog.html new file mode 100644 index 000000000..6ed9a1b2d --- /dev/null +++ b/unittests/lcov_epilog.html @@ -0,0 +1,2 @@ +
Impressum Datenschutz
+ diff --git a/unittests/mocks/AcceptsTmMock.cpp b/unittests/mocks/AcceptsTmMock.cpp index 7b9970478..2f718e61b 100644 --- a/unittests/mocks/AcceptsTmMock.cpp +++ b/unittests/mocks/AcceptsTmMock.cpp @@ -6,7 +6,7 @@ AcceptsTmMock::AcceptsTmMock(object_id_t registeredId, MessageQueueId_t queueToR AcceptsTmMock::AcceptsTmMock(MessageQueueId_t queueToReturn) : SystemObject(objects::NO_OBJECT, false), returnedQueue(queueToReturn) {} -MessageQueueId_t AcceptsTmMock::getReportReceptionQueue(uint8_t virtualChannel) { +MessageQueueId_t AcceptsTmMock::getReportReceptionQueue(uint8_t virtualChannel) const { return returnedQueue; } diff --git a/unittests/mocks/AcceptsTmMock.h b/unittests/mocks/AcceptsTmMock.h index d6cc7f85d..b12e1094a 100644 --- a/unittests/mocks/AcceptsTmMock.h +++ b/unittests/mocks/AcceptsTmMock.h @@ -9,7 +9,7 @@ class AcceptsTmMock : public SystemObject, public AcceptsTelemetryIF { AcceptsTmMock(object_id_t registeredId, MessageQueueId_t queueToReturn); explicit AcceptsTmMock(MessageQueueId_t queueToReturn); - MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) override; + MessageQueueId_t getReportReceptionQueue(uint8_t virtualChannel) const override; const char* getName() const override; MessageQueueId_t returnedQueue; diff --git a/unittests/mocks/StorageManagerMock.cpp b/unittests/mocks/StorageManagerMock.cpp index 5cd3a5759..8a287cd15 100644 --- a/unittests/mocks/StorageManagerMock.cpp +++ b/unittests/mocks/StorageManagerMock.cpp @@ -1,11 +1,11 @@ #include "StorageManagerMock.h" ReturnValue_t StorageManagerMock::addData(store_address_t *storageId, const uint8_t *data, - size_t size, bool ignoreFault) { + size_t size) { if (nextAddDataCallFails.first) { return nextAddDataCallFails.second; } - return LocalPool::addData(storageId, data, size, ignoreFault); + return LocalPool::addData(storageId, data, size); } ReturnValue_t StorageManagerMock::deleteData(store_address_t packet_id) { if (nextDeleteDataCallFails.first) { @@ -36,11 +36,11 @@ ReturnValue_t StorageManagerMock::modifyData(store_address_t packet_id, uint8_t } ReturnValue_t StorageManagerMock::getFreeElement(store_address_t *storageId, size_t size, - uint8_t **p_data, bool ignoreFault) { + uint8_t **p_data) { if (nextFreeElementCallFails.first) { return nextFreeElementCallFails.second; } - return LocalPool::getFreeElement(storageId, size, p_data, ignoreFault); + return LocalPool::getFreeElement(storageId, size, p_data); } bool StorageManagerMock::hasDataAtId(store_address_t storeId) const { diff --git a/unittests/mocks/StorageManagerMock.h b/unittests/mocks/StorageManagerMock.h index a52e46dbf..a0a59a478 100644 --- a/unittests/mocks/StorageManagerMock.h +++ b/unittests/mocks/StorageManagerMock.h @@ -8,15 +8,13 @@ class StorageManagerMock : public LocalPool { public: StorageManagerMock(object_id_t setObjectId, const LocalPoolConfig &poolConfig); - ReturnValue_t addData(store_address_t *storageId, const uint8_t *data, size_t size, - bool ignoreFault) override; + ReturnValue_t addData(store_address_t *storageId, const uint8_t *data, size_t size) override; ReturnValue_t deleteData(store_address_t packet_id) override; ReturnValue_t deleteData(uint8_t *buffer, size_t size, store_address_t *storeId) override; ReturnValue_t getData(store_address_t packet_id, const uint8_t **packet_ptr, size_t *size) override; ReturnValue_t modifyData(store_address_t packet_id, uint8_t **packet_ptr, size_t *size) override; - ReturnValue_t getFreeElement(store_address_t *storageId, size_t size, uint8_t **p_data, - bool ignoreFault) override; + ReturnValue_t getFreeElement(store_address_t *storageId, size_t size, uint8_t **p_data) override; [[nodiscard]] bool hasDataAtId(store_address_t storeId) const override; void clearStore() override; void clearSubPool(uint8_t poolIndex) override; diff --git a/unittests/tmtcpacket/testCcsdsCreator.cpp b/unittests/tmtcpacket/testCcsdsCreator.cpp index 9c113bb6b..cd9b3e977 100644 --- a/unittests/tmtcpacket/testCcsdsCreator.cpp +++ b/unittests/tmtcpacket/testCcsdsCreator.cpp @@ -14,11 +14,17 @@ TEST_CASE("CCSDS Creator", "[ccsds-creator]") { size_t serLen = 0; SECTION("Constexpr Helpers") { - REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x22) == 0x1822); - REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x22) == 0x0822); + REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x22, true) == 0x1822); + REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x22, true) == 0x0822); - REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x7ff) == 0x1fff); - REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x7ff) == 0xfff); + REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x22, false) == 0x1022); + REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x22, false) == 0x0022); + + REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x7ff, true) == 0x1fff); + REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x7ff, true) == 0xfff); + + REQUIRE(ccsds::getTcSpacePacketIdFromApid(0x7ff, false) == 0x17ff); + REQUIRE(ccsds::getTmSpacePacketIdFromApid(0x7ff, false) == 0x7ff); } SECTION("Basic Test") {