Merge remote-tracking branch 'upstream/development' into mueller/cfdp-pdus

This commit is contained in:
Robin Müller 2022-02-02 10:19:31 +01:00
commit e5cc7069a6
No known key found for this signature in database
GPG Key ID: 71B58F8A3CDFA9AC
17 changed files with 650 additions and 83 deletions

View File

@ -5,7 +5,7 @@ RUN apt-get --yes upgrade
#tzdata is a dependency, won't install otherwise #tzdata is a dependency, won't install otherwise
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano RUN apt-get --yes install gcc g++ cmake make lcov git valgrind nano iputils-ping
RUN git clone https://github.com/catchorg/Catch2.git && \ RUN git clone https://github.com/catchorg/Catch2.git && \
cd Catch2 && \ cd Catch2 && \

View File

@ -3,7 +3,13 @@ cmake_minimum_required(VERSION 3.13)
# Can also be changed by upper CMakeLists.txt file # Can also be changed by upper CMakeLists.txt file
find_library(LIB_FSFW_NAME fsfw REQUIRED) find_library(LIB_FSFW_NAME fsfw REQUIRED)
option(FSFW_HAL_ADD_LINUX "Add the Linux HAL to the sources. Required gpiod library" OFF) option(FSFW_HAL_ADD_LINUX "Add the Linux HAL to the sources. Requires gpiod library" OFF)
# On by default for now because I did not have an issue including and compiling those files
# and libraries on a Desktop Linux system and the primary target of the FSFW is still embedded
# Linux. The only exception from this is the gpiod library which requires a dedicated installation,
# but CMake is able to determine whether this library is installed with find_library.
option(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS "Add peripheral drivers for embedded Linux" ON)
option(FSFW_HAL_ADD_RASPBERRY_PI "Add Raspberry Pi specific code to the sources" OFF) option(FSFW_HAL_ADD_RASPBERRY_PI "Add Raspberry Pi specific code to the sources" OFF)
option(FSFW_HAL_ADD_STM32H7 "Add the STM32H7 HAL to the sources" OFF) option(FSFW_HAL_ADD_STM32H7 "Add the STM32H7 HAL to the sources" OFF)
option(FSFW_HAL_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON) option(FSFW_HAL_WARNING_SHADOW_LOCAL_GCC "Enable -Wshadow=local warning in GCC" ON)

View File

@ -1,7 +1,7 @@
add_subdirectory(devicehandlers) add_subdirectory(devicehandlers)
add_subdirectory(common) add_subdirectory(common)
if(FSFW_HAL_ADD_LINUX) if(UNIX)
add_subdirectory(linux) add_subdirectory(linux)
endif() endif()

View File

@ -4,10 +4,13 @@ endif()
target_sources(${LIB_FSFW_NAME} PRIVATE target_sources(${LIB_FSFW_NAME} PRIVATE
UnixFileGuard.cpp UnixFileGuard.cpp
CommandExecutor.cpp
utility.cpp utility.cpp
) )
add_subdirectory(gpio) if(FSFW_HAL_LINUX_ADD_PERIPHERAL_DRIVERS)
add_subdirectory(spi) add_subdirectory(gpio)
add_subdirectory(i2c) add_subdirectory(spi)
add_subdirectory(uart) add_subdirectory(i2c)
add_subdirectory(uart)
endif()

View File

@ -0,0 +1,213 @@
#include "CommandExecutor.h"
#include "fsfw/serviceinterface.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/container/DynamicFIFO.h"
#include <unistd.h>
#include <cstring>
CommandExecutor::CommandExecutor(const size_t maxSize):
readVec(maxSize) {
waiter.events = POLLIN;
}
ReturnValue_t CommandExecutor::load(std::string command, bool blocking, bool printOutput) {
if(state == States::PENDING) {
return COMMAND_PENDING;
}
currentCmd = command;
this->blocking = blocking;
this->printOutput = printOutput;
if(state == States::IDLE) {
state = States::COMMAND_LOADED;
}
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t CommandExecutor::execute() {
if(state == States::IDLE) {
return NO_COMMAND_LOADED_OR_PENDING;
}
else if(state == States::PENDING) {
return COMMAND_PENDING;
}
currentCmdFile = popen(currentCmd.c_str(), "r");
if(currentCmdFile == nullptr) {
lastError = errno;
return HasReturnvaluesIF::RETURN_FAILED;
}
if(blocking) {
ReturnValue_t result = executeBlocking();
state = States::IDLE;
return result;
}
else {
currentFd = fileno(currentCmdFile);
waiter.fd = currentFd;
}
state = States::PENDING;
return HasReturnvaluesIF::RETURN_OK;
}
ReturnValue_t CommandExecutor::close() {
if(state == States::PENDING) {
// Attempt to close process, irrespective of if it is running or not
if(currentCmdFile != nullptr) {
pclose(currentCmdFile);
}
}
return HasReturnvaluesIF::RETURN_OK;
}
void CommandExecutor::printLastError(std::string funcName) const {
if(lastError != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << funcName << " pclose failed with code " << lastError << ": " <<
strerror(lastError) << std::endl;
#else
sif::printError("%s pclose failed with code %d: %s\n",
funcName, lastError, strerror(lastError));
#endif
}
}
void CommandExecutor::setRingBuffer(SimpleRingBuffer *ringBuffer,
DynamicFIFO<uint16_t>* sizesFifo) {
this->ringBuffer = ringBuffer;
this->sizesFifo = sizesFifo;
}
ReturnValue_t CommandExecutor::check(bool& replyReceived) {
if(blocking) {
return HasReturnvaluesIF::RETURN_OK;
}
switch(state) {
case(States::IDLE):
case(States::COMMAND_LOADED): {
return NO_COMMAND_LOADED_OR_PENDING;
}
case(States::PENDING): {
break;
}
}
int result = poll(&waiter, 1, 0);
switch(result) {
case(0): {
return HasReturnvaluesIF::RETURN_OK;
break;
}
case(1): {
if (waiter.revents & POLLIN) {
ssize_t readBytes = read(currentFd, readVec.data(), readVec.size());
if(readBytes == 0) {
// Should not happen
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "CommandExecutor::check: No bytes read "
"after poll event.." << std::endl;
#else
sif::printWarning("CommandExecutor::check: No bytes read after poll event..\n");
#endif
break;
}
else if(readBytes > 0) {
replyReceived = true;
if(printOutput) {
// It is assumed the command output is line terminated
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << currentCmd << " | " << readVec.data();
#else
sif::printInfo("%s | %s", currentCmd, readVec.data());
#endif
}
if(ringBuffer != nullptr) {
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(
readVec.data()), readBytes);
}
if(sizesFifo != nullptr) {
if(not sizesFifo->full()) {
sizesFifo->insert(readBytes);
}
}
}
else {
// Should also not happen
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "CommandExecutor::check: Error " << errno << ": " <<
strerror(errno) << std::endl;
#else
sif::printWarning("CommandExecutor::check: Error %d: %s\n", errno, strerror(errno));
#endif
}
}
if(waiter.revents & POLLERR) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "CommandExecuter::check: Poll error" << std::endl;
#else
sif::printWarning("CommandExecuter::check: Poll error\n");
#endif
return COMMAND_ERROR;
}
if(waiter.revents & POLLHUP) {
result = pclose(currentCmdFile);
ReturnValue_t retval = EXECUTION_FINISHED;
if(result != 0) {
lastError = result;
retval = HasReturnvaluesIF::RETURN_FAILED;
}
state = States::IDLE;
currentCmdFile = nullptr;
currentFd = 0;
return retval;
}
break;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
void CommandExecutor::reset() {
CommandExecutor::close();
currentCmdFile = nullptr;
currentFd = 0;
state = States::IDLE;
}
int CommandExecutor::getLastError() const {
// See: https://stackoverflow.com/questions/808541/any-benefit-in-using-wexitstatus-macro-in-c-over-division-by-256-on-exit-statu
return WEXITSTATUS(this->lastError);
}
CommandExecutor::States CommandExecutor::getCurrentState() const {
return state;
}
ReturnValue_t CommandExecutor::executeBlocking() {
while(fgets(readVec.data(), readVec.size(), currentCmdFile) != nullptr) {
std::string output(readVec.data());
if(printOutput) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::info << currentCmd << " | " << output;
#else
sif::printInfo("%s | %s", currentCmd, output);
#endif
}
if(ringBuffer != nullptr) {
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(output.data()), output.size());
}
if(sizesFifo != nullptr) {
if(not sizesFifo->full()) {
sizesFifo->insert(output.size());
}
}
}
int result = pclose(currentCmdFile);
if(result != 0) {
lastError = result;
return HasReturnvaluesIF::RETURN_FAILED;
}
return HasReturnvaluesIF::RETURN_OK;
}

View File

@ -0,0 +1,135 @@
#ifndef FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_
#define FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_
#include "fsfw/returnvalues/HasReturnvaluesIF.h"
#include "fsfw/returnvalues/FwClassIds.h"
#include <poll.h>
#include <string>
#include <vector>
class SimpleRingBuffer;
template <typename T> class DynamicFIFO;
/**
* @brief Helper class to execute shell commands in blocking and non-blocking mode
* @details
* This class is able to execute processes by using the Linux popen call. It also has the
* capability of writing the read output of a process into a provided ring buffer.
*
* The executor works by first loading the command which should be executed and specifying
* whether it should be executed blocking or non-blocking. After that, execution can be started
* with the execute command. In blocking mode, the execute command will block until the command
* has finished
*/
class CommandExecutor {
public:
enum class States {
IDLE,
COMMAND_LOADED,
PENDING
};
static constexpr uint8_t CLASS_ID = CLASS_ID::LINUX_OSAL;
//! [EXPORT] : [COMMENT] Execution of the current command has finished
static constexpr ReturnValue_t EXECUTION_FINISHED =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 0);
//! [EXPORT] : [COMMENT] Command is pending. This will also be returned if the user tries
//! to load another command but a command is still pending
static constexpr ReturnValue_t COMMAND_PENDING =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 1);
//! [EXPORT] : [COMMENT] Some bytes have been read from the executing process
static constexpr ReturnValue_t BYTES_READ =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 2);
//! [EXPORT] : [COMMENT] Command execution failed
static constexpr ReturnValue_t COMMAND_ERROR =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 3);
//! [EXPORT] : [COMMENT]
static constexpr ReturnValue_t NO_COMMAND_LOADED_OR_PENDING =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 4);
static constexpr ReturnValue_t PCLOSE_CALL_ERROR =
HasReturnvaluesIF::makeReturnCode(CLASS_ID, 6);
/**
* Constructor. Is initialized with maximum size of internal buffer to read data from the
* executed process.
* @param maxSize
*/
CommandExecutor(const size_t maxSize);
/**
* Load a new command which should be executed
* @param command
* @param blocking
* @param printOutput
* @return
*/
ReturnValue_t load(std::string command, bool blocking, bool printOutput = true);
/**
* Execute the loaded command.
* @return
* - In blocking mode, it will return RETURN_FAILED if
* the result of the system call was not 0. The error value can be accessed using
* getLastError
* - In non-blocking mode, this call will start
* the execution and then return RETURN_OK
*/
ReturnValue_t execute();
/**
* Only used in non-blocking mode. Checks the currently running command.
* @param bytesRead Will be set to the number of bytes read, if bytes have been read
* @return
* - BYTES_READ if bytes have been read from the executing process. It is recommended to call
* check again after this
* - RETURN_OK execution is pending, but no bytes have been read from the executing process
* - RETURN_FAILED if execution has failed, error value can be accessed using getLastError
* - EXECUTION_FINISHED if the process was executed successfully
* - NO_COMMAND_LOADED_OR_PENDING self-explanatory
* - COMMAND_ERROR internal poll error
*/
ReturnValue_t check(bool& replyReceived);
/**
* Abort the current command. Should normally not be necessary, check can be used to find
* out whether command execution was successful
* @return RETURN_OK
*/
ReturnValue_t close();
States getCurrentState() const;
int getLastError() const;
void printLastError(std::string funcName) const;
/**
* Assign a ring buffer and a FIFO which will be filled by the executor with the output
* read from the started process
* @param ringBuffer
* @param sizesFifo
*/
void setRingBuffer(SimpleRingBuffer* ringBuffer, DynamicFIFO<uint16_t>* sizesFifo);
/**
* Reset the executor. This calls close internally and then reset the state machine so new
* commands can be loaded and executed
*/
void reset();
private:
std::string currentCmd;
bool blocking = true;
FILE* currentCmdFile = nullptr;
int currentFd = 0;
bool printOutput = true;
std::vector<char> readVec;
struct pollfd waiter {};
SimpleRingBuffer* ringBuffer = nullptr;
DynamicFIFO<uint16_t>* sizesFifo = nullptr;
States state = States::IDLE;
int lastError = 0;
ReturnValue_t executeBlocking();
};
#endif /* FSFW_SRC_FSFW_OSAL_LINUX_COMMANDEXECUTOR_H_ */

View File

@ -1,12 +1,16 @@
target_sources(${LIB_FSFW_NAME} PRIVATE
LinuxLibgpioIF.cpp
)
# This abstraction layer requires the gpiod library. You can install this library # This abstraction layer requires the gpiod library. You can install this library
# with "sudo apt-get install -y libgpiod-dev". If you are cross-compiling, you need # with "sudo apt-get install -y libgpiod-dev". If you are cross-compiling, you need
# to install the package before syncing the sysroot to your host computer. # to install the package before syncing the sysroot to your host computer.
find_library(LIB_GPIO gpiod REQUIRED) find_library(LIB_GPIO gpiod)
if(${LIB_GPIO} MATCHES LIB_GPIO-NOTFOUND)
message(STATUS "gpiod library not found, not linking against it")
else()
target_sources(${LIB_FSFW_NAME} PRIVATE
LinuxLibgpioIF.cpp
)
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${LIB_GPIO}
)
endif()
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
${LIB_GPIO}
)

View File

@ -1,8 +1,10 @@
#include "fsfw/FSFW.h"
#include "fsfw_hal/linux/i2c/I2cComIF.h" #include "fsfw_hal/linux/i2c/I2cComIF.h"
#include "fsfw_hal/linux/utility.h" #include "fsfw_hal/linux/utility.h"
#include "fsfw_hal/linux/UnixFileGuard.h" #include "fsfw_hal/linux/UnixFileGuard.h"
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface.h"
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
@ -24,12 +26,16 @@ ReturnValue_t I2cComIF::initializeInterface(CookieIF* cookie) {
std::string deviceFile; std::string deviceFile;
if(cookie == nullptr) { if(cookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::initializeInterface: Invalid cookie!" << std::endl; sif::error << "I2cComIF::initializeInterface: Invalid cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie); I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
if(i2cCookie == nullptr) { if(i2cCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::initializeInterface: Invalid I2C cookie!" << std::endl; sif::error << "I2cComIF::initializeInterface: Invalid I2C cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
@ -41,15 +47,19 @@ ReturnValue_t I2cComIF::initializeInterface(CookieIF* cookie) {
I2cInstance i2cInstance = {std::vector<uint8_t>(maxReplyLen), 0}; I2cInstance i2cInstance = {std::vector<uint8_t>(maxReplyLen), 0};
auto statusPair = i2cDeviceMap.emplace(i2cAddress, i2cInstance); auto statusPair = i2cDeviceMap.emplace(i2cAddress, i2cInstance);
if (not statusPair.second) { if (not statusPair.second) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::initializeInterface: Failed to insert device with address " << sif::error << "I2cComIF::initializeInterface: Failed to insert device with address " <<
i2cAddress << "to I2C device " << "map" << std::endl; i2cAddress << "to I2C device " << "map" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
} }
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::initializeInterface: Device with address " << i2cAddress << sif::error << "I2cComIF::initializeInterface: Device with address " << i2cAddress <<
"already in use" << std::endl; "already in use" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -61,8 +71,10 @@ ReturnValue_t I2cComIF::sendMessage(CookieIF *cookie,
std::string deviceFile; std::string deviceFile;
if(sendData == nullptr) { if(sendData == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::sendMessage: Send Data is nullptr" sif::error << "I2cComIF::sendMessage: Send Data is nullptr"
<< std::endl; << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -72,15 +84,19 @@ ReturnValue_t I2cComIF::sendMessage(CookieIF *cookie,
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie); I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
if(i2cCookie == nullptr) { if(i2cCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::sendMessage: Invalid I2C Cookie!" << std::endl; sif::error << "I2cComIF::sendMessage: Invalid I2C Cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
address_t i2cAddress = i2cCookie->getAddress(); address_t i2cAddress = i2cCookie->getAddress();
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
if (i2cDeviceMapIter == i2cDeviceMap.end()) { if (i2cDeviceMapIter == i2cDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::sendMessage: i2cAddress of Cookie not " sif::error << "I2cComIF::sendMessage: i2cAddress of Cookie not "
<< "registered in i2cDeviceMap" << std::endl; << "registered in i2cDeviceMap" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -94,10 +110,12 @@ ReturnValue_t I2cComIF::sendMessage(CookieIF *cookie,
return result; return result;
} }
if (write(fd, sendData, sendLen) != (int)sendLen) { if (write(fd, sendData, sendLen) != static_cast<int>(sendLen)) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::sendMessage: Failed to send data to I2C " sif::error << "I2cComIF::sendMessage: Failed to send data to I2C "
"device with error code " << errno << ". Error description: " "device with error code " << errno << ". Error description: "
<< strerror(errno) << std::endl; << strerror(errno) << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
return HasReturnvaluesIF::RETURN_OK; return HasReturnvaluesIF::RETURN_OK;
@ -119,7 +137,9 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF *cookie,
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie); I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
if(i2cCookie == nullptr) { if(i2cCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::requestReceiveMessage: Invalid I2C Cookie!" << std::endl; sif::error << "I2cComIF::requestReceiveMessage: Invalid I2C Cookie!" << std::endl;
#endif
i2cDeviceMapIter->second.replyLen = 0; i2cDeviceMapIter->second.replyLen = 0;
return NULLPOINTER; return NULLPOINTER;
} }
@ -127,8 +147,10 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF *cookie,
address_t i2cAddress = i2cCookie->getAddress(); address_t i2cAddress = i2cCookie->getAddress();
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
if (i2cDeviceMapIter == i2cDeviceMap.end()) { if (i2cDeviceMapIter == i2cDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::requestReceiveMessage: i2cAddress of Cookie not " sif::error << "I2cComIF::requestReceiveMessage: i2cAddress of Cookie not "
<< "registered in i2cDeviceMap" << std::endl; << "registered in i2cDeviceMap" << std::endl;
#endif
i2cDeviceMapIter->second.replyLen = 0; i2cDeviceMapIter->second.replyLen = 0;
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -156,7 +178,9 @@ ReturnValue_t I2cComIF::requestReceiveMessage(CookieIF *cookie,
<< requestLen << " bytes" << std::endl; << requestLen << " bytes" << std::endl;
#endif #endif
i2cDeviceMapIter->second.replyLen = 0; i2cDeviceMapIter->second.replyLen = 0;
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen << " bytes" << std::endl; sif::debug << "I2cComIF::requestReceiveMessage: Read " << readLen << " of " << requestLen << " bytes" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
@ -168,15 +192,19 @@ ReturnValue_t I2cComIF::readReceivedMessage(CookieIF *cookie,
uint8_t **buffer, size_t* size) { uint8_t **buffer, size_t* size) {
I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie); I2cCookie* i2cCookie = dynamic_cast<I2cCookie*>(cookie);
if(i2cCookie == nullptr) { if(i2cCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::readReceivedMessage: Invalid I2C Cookie!" << std::endl; sif::error << "I2cComIF::readReceivedMessage: Invalid I2C Cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
address_t i2cAddress = i2cCookie->getAddress(); address_t i2cAddress = i2cCookie->getAddress();
i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress); i2cDeviceMapIter = i2cDeviceMap.find(i2cAddress);
if (i2cDeviceMapIter == i2cDeviceMap.end()) { if (i2cDeviceMapIter == i2cDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "I2cComIF::readReceivedMessage: i2cAddress of Cookie not " sif::error << "I2cComIF::readReceivedMessage: i2cAddress of Cookie not "
<< "found in i2cDeviceMap" << std::endl; << "found in i2cDeviceMap" << std::endl;
#endif
return HasReturnvaluesIF::RETURN_FAILED; return HasReturnvaluesIF::RETURN_FAILED;
} }
*buffer = i2cDeviceMapIter->second.replyBuffer.data(); *buffer = i2cDeviceMapIter->second.replyBuffer.data();

View File

@ -1,8 +1,8 @@
#include "UartComIF.h" #include "UartComIF.h"
#include "OBSWConfig.h" #include "fsfw/FSFW.h"
#include "fsfw_hal/linux/utility.h" #include "fsfw_hal/linux/utility.h"
#include "fsfw/serviceinterface/ServiceInterface.h" #include "fsfw/serviceinterface.h"
#include <cstring> #include <cstring>
#include <fcntl.h> #include <fcntl.h>
@ -26,7 +26,9 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) {
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if (uartCookie == nullptr) { if (uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "UartComIF::initializeInterface: Invalid UART Cookie!" << std::endl; sif::error << "UartComIF::initializeInterface: Invalid UART Cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
@ -42,14 +44,18 @@ ReturnValue_t UartComIF::initializeInterface(CookieIF* cookie) {
UartElements uartElements = {fileDescriptor, std::vector<uint8_t>(maxReplyLen), 0}; UartElements uartElements = {fileDescriptor, std::vector<uint8_t>(maxReplyLen), 0};
auto status = uartDeviceMap.emplace(deviceFile, uartElements); auto status = uartDeviceMap.emplace(deviceFile, uartElements);
if (status.second == false) { if (status.second == false) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::initializeInterface: Failed to insert device " << sif::warning << "UartComIF::initializeInterface: Failed to insert device " <<
deviceFile << "to UART device map" << std::endl; deviceFile << "to UART device map" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
} }
else { else {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::initializeInterface: UART device " << deviceFile << sif::warning << "UartComIF::initializeInterface: UART device " << deviceFile <<
" already in use" << std::endl; " already in use" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
@ -70,15 +76,19 @@ int UartComIF::configureUartPort(UartCookie* uartCookie) {
int fd = open(deviceFile.c_str(), flags); int fd = open(deviceFile.c_str(), flags);
if (fd < 0) { if (fd < 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::configureUartPort: Failed to open uart " << deviceFile << sif::warning << "UartComIF::configureUartPort: Failed to open uart " << deviceFile <<
"with error code " << errno << strerror(errno) << std::endl; "with error code " << errno << strerror(errno) << std::endl;
#endif
return fd; return fd;
} }
/* Read in existing settings */ /* Read in existing settings */
if(tcgetattr(fd, &options) != 0) { if(tcgetattr(fd, &options) != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::configureUartPort: Error " << errno << "from tcgetattr: " sif::warning << "UartComIF::configureUartPort: Error " << errno << "from tcgetattr: "
<< strerror(errno) << std::endl; << strerror(errno) << std::endl;
#endif
return fd; return fd;
} }
@ -99,8 +109,10 @@ int UartComIF::configureUartPort(UartCookie* uartCookie) {
/* Save option settings */ /* Save option settings */
if (tcsetattr(fd, TCSANOW, &options) != 0) { if (tcsetattr(fd, TCSANOW, &options) != 0) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::configureUartPort: Failed to set options with error " << sif::warning << "UartComIF::configureUartPort: Failed to set options with error " <<
errno << ": " << strerror(errno); errno << ": " << strerror(errno);
#endif
return fd; return fd;
} }
return fd; return fd;
@ -152,7 +164,9 @@ void UartComIF::setDatasizeOptions(struct termios* options, UartCookie* uartCook
options->c_cflag |= CS8; options->c_cflag |= CS8;
break; break;
default: default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::setDatasizeOptions: Invalid size specified" << std::endl; sif::warning << "UartComIF::setDatasizeOptions: Invalid size specified" << std::endl;
#endif
break; break;
} }
} }
@ -259,7 +273,9 @@ void UartComIF::configureBaudrate(struct termios* options, UartCookie* uartCooki
cfsetospeed(options, B460800); cfsetospeed(options, B460800);
break; break;
default: default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl; sif::warning << "UartComIF::configureBaudrate: Baudrate not supported" << std::endl;
#endif
break; break;
} }
} }
@ -275,29 +291,37 @@ ReturnValue_t UartComIF::sendMessage(CookieIF *cookie,
} }
if(sendData == nullptr) { if(sendData == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::sendMessage: Send data is nullptr" << std::endl; sif::warning << "UartComIF::sendMessage: Send data is nullptr" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::sendMessasge: Invalid UART Cookie!" << std::endl; sif::warning << "UartComIF::sendMessasge: Invalid UART Cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
deviceFile = uartCookie->getDeviceFile(); deviceFile = uartCookie->getDeviceFile();
uartDeviceMapIter = uartDeviceMap.find(deviceFile); uartDeviceMapIter = uartDeviceMap.find(deviceFile);
if (uartDeviceMapIter == uartDeviceMap.end()) { if (uartDeviceMapIter == uartDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartComIF::sendMessage: Device file " << deviceFile << sif::debug << "UartComIF::sendMessage: Device file " << deviceFile <<
"not in UART map" << std::endl; "not in UART map" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
fd = uartDeviceMapIter->second.fileDescriptor; fd = uartDeviceMapIter->second.fileDescriptor;
if (write(fd, sendData, sendLen) != (int)sendLen) { if (write(fd, sendData, sendLen) != static_cast<int>(sendLen)) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::error << "UartComIF::sendMessage: Failed to send data with error code " << sif::error << "UartComIF::sendMessage: Failed to send data with error code " <<
errno << ": Error description: " << strerror(errno) << std::endl; errno << ": Error description: " << strerror(errno) << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
@ -314,7 +338,9 @@ ReturnValue_t UartComIF::requestReceiveMessage(CookieIF *cookie, size_t requestL
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartComIF::requestReceiveMessage: Invalid Uart Cookie!" << std::endl; sif::debug << "UartComIF::requestReceiveMessage: Invalid Uart Cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
@ -327,8 +353,10 @@ ReturnValue_t UartComIF::requestReceiveMessage(CookieIF *cookie, size_t requestL
} }
if (uartDeviceMapIter == uartDeviceMap.end()) { if (uartDeviceMapIter == uartDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartComIF::requestReceiveMessage: Device file " << deviceFile sif::debug << "UartComIF::requestReceiveMessage: Device file " << deviceFile
<< " not in uart map" << std::endl; << " not in uart map" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
@ -425,8 +453,10 @@ ReturnValue_t UartComIF::handleNoncanonicalRead(UartCookie &uartCookie, UartDevi
} }
else if (bytesRead != static_cast<int>(requestLen)) { else if (bytesRead != static_cast<int>(requestLen)) {
if(uartCookie.isReplySizeFixed()) { if(uartCookie.isReplySizeFixed()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::requestReceiveMessage: Only read " << bytesRead << sif::warning << "UartComIF::requestReceiveMessage: Only read " << bytesRead <<
" of " << requestLen << " bytes" << std::endl; " of " << requestLen << " bytes" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
} }
@ -442,15 +472,19 @@ ReturnValue_t UartComIF::readReceivedMessage(CookieIF *cookie,
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartComIF::readReceivedMessage: Invalid uart cookie!" << std::endl; sif::debug << "UartComIF::readReceivedMessage: Invalid uart cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
deviceFile = uartCookie->getDeviceFile(); deviceFile = uartCookie->getDeviceFile();
uartDeviceMapIter = uartDeviceMap.find(deviceFile); uartDeviceMapIter = uartDeviceMap.find(deviceFile);
if (uartDeviceMapIter == uartDeviceMap.end()) { if (uartDeviceMapIter == uartDeviceMap.end()) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartComIF::readReceivedMessage: Device file " << deviceFile << sif::debug << "UartComIF::readReceivedMessage: Device file " << deviceFile <<
" not in uart map" << std::endl; " not in uart map" << std::endl;
#endif
return RETURN_FAILED; return RETURN_FAILED;
} }
@ -468,7 +502,9 @@ ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF *cookie) {
UartDeviceMapIter uartDeviceMapIter; UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::flushUartRxBuffer: Invalid uart cookie!" << std::endl; sif::warning << "UartComIF::flushUartRxBuffer: Invalid uart cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
deviceFile = uartCookie->getDeviceFile(); deviceFile = uartCookie->getDeviceFile();
@ -486,7 +522,9 @@ ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF *cookie) {
UartDeviceMapIter uartDeviceMapIter; UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::flushUartTxBuffer: Invalid uart cookie!" << std::endl; sif::warning << "UartComIF::flushUartTxBuffer: Invalid uart cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
deviceFile = uartCookie->getDeviceFile(); deviceFile = uartCookie->getDeviceFile();
@ -504,7 +542,9 @@ ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF *cookie) {
UartDeviceMapIter uartDeviceMapIter; UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie); UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) { if(uartCookie == nullptr) {
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "UartComIF::flushUartTxAndRxBuf: Invalid uart cookie!" << std::endl; sif::warning << "UartComIF::flushUartTxAndRxBuf: Invalid uart cookie!" << std::endl;
#endif
return NULLPOINTER; return NULLPOINTER;
} }
deviceFile = uartCookie->getDeviceFile(); deviceFile = uartCookie->getDeviceFile();

View File

@ -1,6 +1,6 @@
#include "fsfw_hal/linux/uart/UartCookie.h" #include "fsfw_hal/linux/uart/UartCookie.h"
#include <fsfw/serviceinterface/ServiceInterface.h> #include <fsfw/serviceinterface.h>
UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode, UartCookie::UartCookie(object_id_t handlerId, std::string deviceFile, UartModes uartMode,
uint32_t baudrate, size_t maxReplyLen): uint32_t baudrate, size_t maxReplyLen):
@ -42,7 +42,9 @@ void UartCookie::setBitsPerWord(uint8_t bitsPerWord_) {
case 8: case 8:
break; break;
default: default:
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::debug << "UartCookie::setBitsPerWord: Invalid bits per word specified" << std::endl; sif::debug << "UartCookie::setBitsPerWord: Invalid bits per word specified" << std::endl;
#endif
return; return;
} }
bitsPerWord = bitsPerWord_; bitsPerWord = bitsPerWord_;

View File

@ -9,36 +9,44 @@ import webbrowser
import shutil import shutil
import sys import sys
import time import time
from shutil import which
from typing import List from typing import List
UNITTEST_FOLDER_NAME = 'build-tests' UNITTEST_FOLDER_NAME = "build-tests"
DOCS_FOLDER_NAME = 'build-docs' DOCS_FOLDER_NAME = "build-docs"
def main(): def main():
parser = argparse.ArgumentParser(description="FSFW helper script") parser = argparse.ArgumentParser(description="FSFW helper script")
choices = ('docs', 'tests') choices = ("docs", "tests")
parser.add_argument( parser.add_argument(
'type', metavar='type', choices=choices, "type", metavar="type", choices=choices, help=f"Target type. Choices: {choices}"
help=f'Target type. Choices: {choices}'
) )
parser.add_argument( parser.add_argument(
'-a', '--all', action='store_true', "-a", "--all", action="store_true", help="Create, build and open specified type"
help='Create, build and open specified type'
) )
parser.add_argument( parser.add_argument(
'-c', '--create', action='store_true', "-c",
help='Create docs or test build configuration' "--create",
action="store_true",
help="Create docs or test build configuration",
) )
parser.add_argument( parser.add_argument(
'-b', '--build', action='store_true', "-b", "--build", action="store_true", help="Build the specified type"
help='Build the specified type'
) )
parser.add_argument( parser.add_argument(
'-o', '--open', action='store_true', "-o",
help='Open test or documentation data in webbrowser' "--open",
action="store_true",
help="Open test or documentation data in webbrowser",
)
parser.add_argument(
"-v",
"--valgrind",
action="store_true",
help="Run valgrind on generated test binary",
) )
args = parser.parse_args() args = parser.parse_args()
@ -46,26 +54,26 @@ def main():
args.build = True args.build = True
args.create = True args.create = True
args.open = True args.open = True
elif not args.build and not args.create and not args.open: elif not args.build and not args.create and not args.open and not args.valgrind:
print( print(
'Please select at least one operation to perform. ' "Please select at least one operation to perform. "
'Use helper.py -h for more information' "Use helper.py -h for more information"
) )
sys.exit(1) sys.exit(1)
# This script can be called from root and from script folder # This script can be called from root and from script folder
if not os.path.isfile('README.md'): if not os.path.isfile("README.md"):
os.chdir('..') os.chdir("..")
build_dir_list = [] build_dir_list = []
if not args.create: if not args.create:
build_dir_list = build_build_dir_list() build_dir_list = build_build_dir_list()
if args.type == 'tests': if args.type == "tests":
handle_tests_type(args, build_dir_list) handle_tests_type(args, build_dir_list)
elif args.type == 'docs': elif args.type == "docs":
handle_docs_type(args, build_dir_list) handle_docs_type(args, build_dir_list)
else: else:
print('Invalid or unknown type') print("Invalid or unknown type")
sys.exit(1) sys.exit(1)
@ -76,7 +84,9 @@ def handle_docs_type(args, build_dir_list: list):
create_docs_build_cfg() create_docs_build_cfg()
build_directory = DOCS_FOLDER_NAME build_directory = DOCS_FOLDER_NAME
elif len(build_dir_list) == 0: elif len(build_dir_list) == 0:
print('No valid CMake docs build directory found. Trying to set up docs build system') print(
"No valid CMake docs build directory found. Trying to set up docs build system"
)
shutil.rmtree(DOCS_FOLDER_NAME) shutil.rmtree(DOCS_FOLDER_NAME)
create_docs_build_cfg() create_docs_build_cfg()
build_directory = DOCS_FOLDER_NAME build_directory = DOCS_FOLDER_NAME
@ -87,18 +97,18 @@ def handle_docs_type(args, build_dir_list: list):
build_directory = determine_build_dir(build_dir_list) build_directory = determine_build_dir(build_dir_list)
os.chdir(build_directory) os.chdir(build_directory)
if args.build: if args.build:
os.system('cmake --build . -j') os.system("cmake --build . -j")
if args.open: if args.open:
if not os.path.isfile('docs/sphinx/index.html'): if not os.path.isfile("docs/sphinx/index.html"):
# try again.. # try again..
os.system('cmake --build . -j') os.system("cmake --build . -j")
if not os.path.isfile('docs/sphinx/index.html'): if not os.path.isfile("docs/sphinx/index.html"):
print( print(
"No Sphinx documentation file detected. " "No Sphinx documentation file detected. "
"Try to build it first with the -b argument" "Try to build it first with the -b argument"
) )
sys.exit(1) sys.exit(1)
webbrowser.open('docs/sphinx/index.html') webbrowser.open("docs/sphinx/index.html")
def handle_tests_type(args, build_dir_list: list): def handle_tests_type(args, build_dir_list: list):
@ -109,8 +119,8 @@ def handle_tests_type(args, build_dir_list: list):
build_directory = UNITTEST_FOLDER_NAME build_directory = UNITTEST_FOLDER_NAME
elif len(build_dir_list) == 0: elif len(build_dir_list) == 0:
print( print(
'No valid CMake tests build directory found. ' "No valid CMake tests build directory found. "
'Trying to set up test build system' "Trying to set up test build system"
) )
create_tests_build_cfg() create_tests_build_cfg()
build_directory = UNITTEST_FOLDER_NAME build_directory = UNITTEST_FOLDER_NAME
@ -123,24 +133,33 @@ def handle_tests_type(args, build_dir_list: list):
if args.build: if args.build:
perform_lcov_operation(build_directory, False) perform_lcov_operation(build_directory, False)
if args.open: if args.open:
if not os.path.isdir('fsfw-tests_coverage'): if not os.path.isdir("fsfw-tests_coverage"):
print("No Unittest folder detected. Try to build them first with the -b argument") print(
"No Unittest folder detected. Try to build them first with the -b argument"
)
sys.exit(1) sys.exit(1)
webbrowser.open('fsfw-tests_coverage/index.html') webbrowser.open("fsfw-tests_coverage/index.html")
if args.valgrind:
if which("valgrind") is None:
print("Please install valgrind first")
sys.exit(1)
os.chdir(UNITTEST_FOLDER_NAME)
os.system("valgrind --leak-check=full ./fsfw-tests")
os.chdir("..")
def create_tests_build_cfg(): def create_tests_build_cfg():
os.mkdir(UNITTEST_FOLDER_NAME) os.mkdir(UNITTEST_FOLDER_NAME)
os.chdir(UNITTEST_FOLDER_NAME) os.chdir(UNITTEST_FOLDER_NAME)
os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..') os.system("cmake -DFSFW_OSAL=host -DFSFW_BUILD_UNITTESTS=ON ..")
os.chdir('..') os.chdir("..")
def create_docs_build_cfg(): def create_docs_build_cfg():
os.mkdir(DOCS_FOLDER_NAME) os.mkdir(DOCS_FOLDER_NAME)
os.chdir(DOCS_FOLDER_NAME) os.chdir(DOCS_FOLDER_NAME)
os.system('cmake -DFSFW_OSAL=host -DFSFW_BUILD_DOCS=ON ..') os.system("cmake -DFSFW_OSAL=host -DFSFW_BUILD_DOCS=ON ..")
os.chdir('..') os.chdir("..")
def build_build_dir_list() -> list: def build_build_dir_list() -> list:

View File

@ -18,6 +18,10 @@
// FSFW core defines // FSFW core defines
#ifndef FSFW_TCP_RECV_WIRETAPPING_ENABLED
#define FSFW_TCP_RECV_WIRETAPPING_ENABLED 0
#endif
#ifndef FSFW_CPP_OSTREAM_ENABLED #ifndef FSFW_CPP_OSTREAM_ENABLED
#define FSFW_CPP_OSTREAM_ENABLED 1 #define FSFW_CPP_OSTREAM_ENABLED 1
#endif /* FSFW_CPP_OSTREAM_ENABLED */ #endif /* FSFW_CPP_OSTREAM_ENABLED */

View File

@ -5,8 +5,7 @@
#include <cstddef> #include <cstddef>
/** /**
* @brief Circular buffer implementation, useful for buffering * @brief Circular buffer implementation, useful for buffering into data streams.
* into data streams.
* @details * @details
* Note that the deleteData() has to be called to increment the read pointer. * Note that the deleteData() has to be called to increment the read pointer.
* This class allocated dynamically, so * This class allocated dynamically, so
@ -20,8 +19,8 @@ public:
* @param size * @param size
* @param overwriteOld If the ring buffer is overflowing at a write * @param overwriteOld If the ring buffer is overflowing at a write
* operation, the oldest data will be overwritten. * operation, the oldest data will be overwritten.
* @param maxExcessBytes These additional bytes will be allocated in addtion * @param maxExcessBytes These additional bytes will be allocated in addition
* to the specified size to accomodate contiguous write operations * to the specified size to accommodate continuous write operations
* with getFreeElement. * with getFreeElement.
* *
*/ */
@ -32,10 +31,10 @@ public:
* @param buffer * @param buffer
* @param size * @param size
* @param overwriteOld * @param overwriteOld
* If the ring buffer is overflowing at a write operartion, the oldest data * If the ring buffer is overflowing at a write operation, the oldest data
* will be overwritten. * will be overwritten.
* @param maxExcessBytes * @param maxExcessBytes
* If the buffer can accomodate additional bytes for contigous write * If the buffer can accommodate additional bytes for contiguous write
* operations with getFreeElement, this is the maximum allowed additional * operations with getFreeElement, this is the maximum allowed additional
* size * size
*/ */
@ -48,7 +47,7 @@ public:
* Write to circular buffer and increment write pointer by amount. * Write to circular buffer and increment write pointer by amount.
* @param data * @param data
* @param amount * @param amount
* @return -@c RETURN_OK if write operation was successfull * @return -@c RETURN_OK if write operation was successful
* -@c RETURN_FAILED if * -@c RETURN_FAILED if
*/ */
ReturnValue_t writeData(const uint8_t* data, size_t amount); ReturnValue_t writeData(const uint8_t* data, size_t amount);
@ -108,7 +107,7 @@ public:
* Delete data by incrementing read pointer. * Delete data by incrementing read pointer.
* @param amount * @param amount
* @param deleteRemaining * @param deleteRemaining
* If the amount specified is larger than the remaing size to read and this * If the amount specified is larger than the remaining size to read and this
* is set to true, the remaining amount will be deleted as well * is set to true, the remaining amount will be deleted as well
* @param trueAmount [out] * @param trueAmount [out]
* If deleteRemaining was set to true, the amount deleted will be assigned * If deleteRemaining was set to true, the amount deleted will be assigned

View File

@ -1,21 +1,19 @@
target_sources(${LIB_FSFW_NAME} target_sources(${LIB_FSFW_NAME} PRIVATE
PRIVATE Clock.cpp
Clock.cpp BinarySemaphore.cpp
BinarySemaphore.cpp CountingSemaphore.cpp
CountingSemaphore.cpp FixedTimeslotTask.cpp
FixedTimeslotTask.cpp InternalErrorCodes.cpp
InternalErrorCodes.cpp MessageQueue.cpp
MessageQueue.cpp Mutex.cpp
Mutex.cpp MutexFactory.cpp
MutexFactory.cpp PeriodicPosixTask.cpp
PeriodicPosixTask.cpp PosixThread.cpp
PosixThread.cpp QueueFactory.cpp
QueueFactory.cpp SemaphoreFactory.cpp
SemaphoreFactory.cpp TaskFactory.cpp
TaskFactory.cpp tcpipHelpers.cpp
tcpipHelpers.cpp unixUtility.cpp
unixUtility.cpp
CommandExecutor.cpp
) )
find_package(Threads REQUIRED) find_package(Threads REQUIRED)

View File

@ -22,3 +22,4 @@ add_subdirectory(globalfunctions)
add_subdirectory(timemanager) add_subdirectory(timemanager)
add_subdirectory(tmtcpacket) add_subdirectory(tmtcpacket)
add_subdirectory(cfdp) add_subdirectory(cfdp)
add_subdirectory(hal)

View File

@ -0,0 +1,3 @@
target_sources(${FSFW_TEST_TGT} PRIVATE
testCommandExecutor.cpp
)

View File

@ -0,0 +1,112 @@
#include <catch2/catch_test_macros.hpp>
#include "fsfw/serviceinterface.h"
#include "fsfw/container/SimpleRingBuffer.h"
#include "fsfw/container/DynamicFIFO.h"
#include "fsfw_hal/linux/CommandExecutor.h"
#include "fsfw/platform.h"
#include <unistd.h>
#include <fstream>
#ifdef PLATFORM_UNIX
static const char TEST_FILE_NAME[] = "/tmp/fsfw-unittest-test.txt";
TEST_CASE( "Command Executor" , "[cmd-exec]") {
// Check blocking mode first
CommandExecutor cmdExecutor(1024);
std::string cmd = "echo \"test\" >> " + std::string(TEST_FILE_NAME);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
ReturnValue_t result = cmdExecutor.load(cmd, true, true);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::COMMAND_LOADED);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cmdExecutor.execute() == HasReturnvaluesIF::RETURN_OK);
// Check that file exists with contents
std::ifstream file(TEST_FILE_NAME);
std::string line;
std::getline(file, line);
CHECK(line == "test");
std::remove(TEST_FILE_NAME);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
// Now check non-blocking mode
SimpleRingBuffer outputBuffer(524, true);
DynamicFIFO<uint16_t> sizesFifo(12);
cmdExecutor.setRingBuffer(&outputBuffer, &sizesFifo);
result = cmdExecutor.load("echo \"Hello World\"", false, false);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
cmdExecutor.execute();
bool bytesHaveBeenRead = false;
size_t limitIdx = 0;
while(result != CommandExecutor::EXECUTION_FINISHED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
usleep(500);
REQUIRE(limitIdx < 5);
}
limitIdx = 0;
CHECK(bytesHaveBeenRead == true);
CHECK(result == CommandExecutor::EXECUTION_FINISHED);
uint16_t readBytes = 0;
sizesFifo.retrieve(&readBytes);
REQUIRE(readBytes == 12);
REQUIRE(outputBuffer.getAvailableReadData() == 12);
uint8_t readBuffer[32] = {};
REQUIRE(outputBuffer.readData(readBuffer, 12) == HasReturnvaluesIF::RETURN_OK);
std::string readString(reinterpret_cast<char*>(readBuffer));
std::string cmpString = "Hello World\n";
CHECK(readString == cmpString);
outputBuffer.deleteData(12, true);
// Test more complex command
result = cmdExecutor.load("ping -c 1 localhost", false, false);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::COMMAND_LOADED);
REQUIRE(cmdExecutor.execute() == HasReturnvaluesIF::RETURN_OK);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::PENDING);
limitIdx = 0;
while(result != CommandExecutor::EXECUTION_FINISHED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
usleep(500);
REQUIRE(limitIdx < 20);
}
limitIdx = 0;
CHECK(bytesHaveBeenRead == true);
CHECK(result == CommandExecutor::EXECUTION_FINISHED);
REQUIRE(cmdExecutor.getCurrentState() == CommandExecutor::States::IDLE);
readBytes = 0;
sizesFifo.retrieve(&readBytes);
// That's about the size of the reply
bool beTrue = (readBytes > 200) and (readBytes < 300);
REQUIRE(beTrue);
uint8_t largerReadBuffer[1024] = {};
outputBuffer.readData(largerReadBuffer, readBytes);
// You can also check this output in the debugger
std::string allTheReply(reinterpret_cast<char*>(largerReadBuffer));
// I am just going to assume that this string is the same across ping implementations
// of different Linux systems
REQUIRE(allTheReply.find("localhost ping statistics") != std::string::npos);
// Now check failing command
result = cmdExecutor.load("false", false, false);
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
result = cmdExecutor.execute();
REQUIRE(result == HasReturnvaluesIF::RETURN_OK);
while(result != CommandExecutor::EXECUTION_FINISHED and result != HasReturnvaluesIF::RETURN_FAILED) {
limitIdx++;
result = cmdExecutor.check(bytesHaveBeenRead);
REQUIRE(result != CommandExecutor::COMMAND_ERROR);
usleep(500);
REQUIRE(limitIdx < 20);
}
REQUIRE(result == HasReturnvaluesIF::RETURN_FAILED);
REQUIRE(cmdExecutor.getLastError() == 1);
}
#endif