Merge remote-tracking branch 'upstream/development' into mueller/cfdp-pdus
This commit is contained in:
commit
e5cc7069a6
@ -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 && \
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
213
hal/src/fsfw_hal/linux/CommandExecutor.cpp
Normal file
213
hal/src/fsfw_hal/linux/CommandExecutor.cpp
Normal 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;
|
||||||
|
}
|
135
hal/src/fsfw_hal/linux/CommandExecutor.h
Normal file
135
hal/src/fsfw_hal/linux/CommandExecutor.h
Normal 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_ */
|
@ -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)
|
||||||
|
|
||||||
target_link_libraries(${LIB_FSFW_NAME} PRIVATE
|
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}
|
${LIB_GPIO}
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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_;
|
||||||
|
@ -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:
|
||||||
|
@ -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 */
|
||||||
|
@ -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
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
target_sources(${LIB_FSFW_NAME}
|
target_sources(${LIB_FSFW_NAME} PRIVATE
|
||||||
PRIVATE
|
|
||||||
Clock.cpp
|
Clock.cpp
|
||||||
BinarySemaphore.cpp
|
BinarySemaphore.cpp
|
||||||
CountingSemaphore.cpp
|
CountingSemaphore.cpp
|
||||||
@ -15,7 +14,6 @@ target_sources(${LIB_FSFW_NAME}
|
|||||||
TaskFactory.cpp
|
TaskFactory.cpp
|
||||||
tcpipHelpers.cpp
|
tcpipHelpers.cpp
|
||||||
unixUtility.cpp
|
unixUtility.cpp
|
||||||
CommandExecutor.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
@ -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)
|
||||||
|
3
tests/src/fsfw_tests/unit/hal/CMakeLists.txt
Normal file
3
tests/src/fsfw_tests/unit/hal/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target_sources(${FSFW_TEST_TGT} PRIVATE
|
||||||
|
testCommandExecutor.cpp
|
||||||
|
)
|
112
tests/src/fsfw_tests/unit/hal/testCommandExecutor.cpp
Normal file
112
tests/src/fsfw_tests/unit/hal/testCommandExecutor.cpp
Normal 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
|
Loading…
Reference in New Issue
Block a user