Merge remote-tracking branch 'upstream/mueller/master' into mueller/master

This commit is contained in:
Robin Müller 2021-08-06 11:36:33 +02:00
commit 420b104a0c
20 changed files with 437 additions and 25 deletions

View File

@ -1,4 +1,4 @@
![FSFW Logo](logo/FSFW_Logo_V3_bw.png) ![FSFW Logo](misc/logo/FSFW_Logo_V3_bw.png)
# Flight Software Framework (FSFW) # Flight Software Framework (FSFW)

View File

@ -82,7 +82,7 @@ ReturnValue_t SpiComIF::initializeInterface(CookieIF *cookie) {
gpioComIF->pullHigh(gpioId); gpioComIF->pullHigh(gpioId);
} }
size_t spiSpeed = 0; uint32_t spiSpeed = 0;
spi::SpiModes spiMode = spi::SpiModes::MODE_0; spi::SpiModes spiMode = spi::SpiModes::MODE_0;
SpiCookie::UncommonParameters params; SpiCookie::UncommonParameters params;

View File

@ -443,6 +443,51 @@ ReturnValue_t UartComIF::readReceivedMessage(CookieIF *cookie,
return RETURN_OK; return RETURN_OK;
} }
ReturnValue_t UartComIF::flushUartRxBuffer(CookieIF *cookie) {
std::string deviceFile;
UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) {
sif::warning << "UartComIF::flushUartRxBuffer: Invalid uart cookie!" << std::endl;
return NULLPOINTER;
}
deviceFile = uartCookie->getDeviceFile();
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
int fd = uartDeviceMapIter->second.fileDescriptor;
tcflush(fd, TCIFLUSH);
return RETURN_OK;
}
ReturnValue_t UartComIF::flushUartTxBuffer(CookieIF *cookie) {
std::string deviceFile;
UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) {
sif::warning << "UartComIF::flushUartTxBuffer: Invalid uart cookie!" << std::endl;
return NULLPOINTER;
}
deviceFile = uartCookie->getDeviceFile();
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
int fd = uartDeviceMapIter->second.fileDescriptor;
tcflush(fd, TCOFLUSH);
return RETURN_OK;
}
ReturnValue_t UartComIF::flushUartTxAndRxBuf(CookieIF *cookie) {
std::string deviceFile;
UartDeviceMapIter uartDeviceMapIter;
UartCookie* uartCookie = dynamic_cast<UartCookie*>(cookie);
if(uartCookie == nullptr) {
sif::warning << "UartComIF::flushUartTxAndRxBuf: Invalid uart cookie!" << std::endl;
return NULLPOINTER;
}
deviceFile = uartCookie->getDeviceFile();
uartDeviceMapIter = uartDeviceMap.find(deviceFile);
int fd = uartDeviceMapIter->second.fileDescriptor;
tcflush(fd, TCIOFLUSH);
return RETURN_OK;
}
void UartComIF::setUartMode(struct termios *options, UartCookie &uartCookie) { void UartComIF::setUartMode(struct termios *options, UartCookie &uartCookie) {
UartModes uartMode = uartCookie.getUartMode(); UartModes uartMode = uartCookie.getUartMode();
if(uartMode == UartModes::NON_CANONICAL) { if(uartMode == UartModes::NON_CANONICAL) {

View File

@ -41,6 +41,21 @@ public:
ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer, ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
size_t *size) override; size_t *size) override;
/**
* @brief This function discards all data received but not read in the UART buffer.
*/
ReturnValue_t flushUartRxBuffer(CookieIF *cookie);
/**
* @brief This function discards all data in the transmit buffer of the UART driver.
*/
ReturnValue_t flushUartTxBuffer(CookieIF *cookie);
/**
* @brief This function discards both data in the transmit and receive buffer of the UART.
*/
ReturnValue_t flushUartTxAndRxBuf(CookieIF *cookie);
private: private:
using UartDeviceFile_t = std::string; using UartDeviceFile_t = std::string;

View File

@ -1,12 +1,10 @@
#ifndef FSFW_DEFAULTCFG_VERSION_H_ #ifndef FSFW_VERSION_H_
#define FSFW_DEFAULTCFG_VERSION_H_ #define FSFW_VERSION_H_
const char* const FSFW_VERSION_NAME = "ASTP"; const char* const FSFW_VERSION_NAME = "ASTP";
#define FSFW_VERSION 1 #define FSFW_VERSION 1
#define FSFW_SUBVERSION 0 #define FSFW_SUBVERSION 3
#define FSFW_REVISION 0 #define FSFW_REVISION 0
#endif /* FSFW_VERSION_H_ */
#endif /* FSFW_DEFAULTCFG_VERSION_H_ */

View File

@ -55,7 +55,19 @@ void ActionHelper::setQueueToUse(MessageQueueIF* queue) {
void ActionHelper::prepareExecution(MessageQueueId_t commandedBy, void ActionHelper::prepareExecution(MessageQueueId_t commandedBy,
ActionId_t actionId, store_address_t dataAddress) { ActionId_t actionId, store_address_t dataAddress) {
const uint8_t* dataPtr = NULL; if(ipcStore == nullptr) {
#if FSFW_VERBOSE_LEVEL >= 1
#if FSFW_CPP_OSTREAM_ENABLED == 1
sif::warning << "ActionHelper::prepareExecution: IPC Store not set. Call initialize first"
<< std::endl;
#else
sif::printWarning("ActionHelper::prepareExecution: "
"IPC Store not set. Call initialize first\n");
#endif
#endif /* FSFW_VERBOSE_LEVEL >= 1 */
return;
}
const uint8_t* dataPtr = nullptr;
size_t size = 0; size_t size = 0;
ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size); ReturnValue_t result = ipcStore->getData(dataAddress, &dataPtr, &size);
if (result != HasReturnvaluesIF::RETURN_OK) { if (result != HasReturnvaluesIF::RETURN_OK) {

View File

@ -17,7 +17,7 @@
#endif #endif
const std::string TcpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; const std::string TcpTmTcBridge::DEFAULT_SERVER_PORT = tcpip::DEFAULT_TCP_SERVER_PORT;
TcpTmTcBridge::TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination, TcpTmTcBridge::TcpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
object_id_t tmStoreId, object_id_t tcStoreId): object_id_t tmStoreId, object_id_t tcStoreId):

View File

@ -29,8 +29,8 @@ class TcpTmTcBridge:
public TmTcBridge { public TmTcBridge {
friend class TcpTmTcServer; friend class TcpTmTcServer;
public: public:
/* The ports chosen here should not be used by any other process. */ // The ports chosen here should not be used by any other process
static const std::string DEFAULT_UDP_SERVER_PORT; static const std::string DEFAULT_SERVER_PORT;
/** /**
* Constructor * Constructor

View File

@ -17,13 +17,13 @@
#define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0 #define FSFW_UDP_SEND_WIRETAPPING_ENABLED 0
#endif #endif
const std::string UdpTmTcBridge::DEFAULT_UDP_SERVER_PORT = tcpip::DEFAULT_SERVER_PORT; const std::string UdpTmTcBridge::DEFAULT_SERVER_PORT = tcpip::DEFAULT_UDP_SERVER_PORT;
UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, UdpTmTcBridge::UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
std::string udpServerPort, object_id_t tmStoreId, object_id_t tcStoreId): std::string udpServerPort, object_id_t tmStoreId, object_id_t tcStoreId):
TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) { TmTcBridge(objectId, tcDestination, tmStoreId, tcStoreId) {
if(udpServerPort == "") { if(udpServerPort == "") {
this->udpServerPort = DEFAULT_UDP_SERVER_PORT; this->udpServerPort = DEFAULT_SERVER_PORT;
} }
else { else {
this->udpServerPort = udpServerPort; this->udpServerPort = udpServerPort;

View File

@ -28,7 +28,7 @@ class UdpTmTcBridge:
friend class UdpTcPollingTask; friend class UdpTcPollingTask;
public: public:
/* The ports chosen here should not be used by any other process. */ /* The ports chosen here should not be used by any other process. */
static const std::string DEFAULT_UDP_SERVER_PORT; static const std::string DEFAULT_SERVER_PORT;
UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination, UdpTmTcBridge(object_id_t objectId, object_id_t tcDestination,
std::string udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE, std::string udpServerPort = "", object_id_t tmStoreId = objects::TM_STORE,

View File

@ -13,7 +13,8 @@
namespace tcpip { namespace tcpip {
const char* const DEFAULT_SERVER_PORT = "7301"; const char* const DEFAULT_UDP_SERVER_PORT = "7301";
const char* const DEFAULT_TCP_SERVER_PORT = "7303";
enum class Protocol { enum class Protocol {
UDP, UDP,

View File

@ -16,6 +16,7 @@ target_sources(${LIB_FSFW_NAME}
Timer.cpp Timer.cpp
tcpipHelpers.cpp tcpipHelpers.cpp
unixUtility.cpp unixUtility.cpp
CommandExecutor.cpp
) )
find_package(Threads REQUIRED) find_package(Threads REQUIRED)

View File

@ -0,0 +1,185 @@
#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) {
return executeBlocking();
}
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) {
sif::error << funcName << " pclose failed with code " <<
lastError << ": " << strerror(lastError) << std::endl;
}
}
void CommandExecutor::setRingBuffer(SimpleRingBuffer *ringBuffer,
DynamicFIFO<uint16_t>* sizesFifo) {
this->ringBuffer = ringBuffer;
this->sizesFifo = sizesFifo;
}
ReturnValue_t CommandExecutor::check(bool& bytesRead) {
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
sif::warning << "CommandExecutor::check: "
"No bytes read after poll event.." << std::endl;
break;
}
else if(readBytes > 0) {
bytesRead = true;
if(printOutput) {
// It is assumed the command output is line terminated
sif::info << currentCmd << " | " << readVec.data();
}
if(ringBuffer != nullptr) {
ringBuffer->writeData(reinterpret_cast<const uint8_t*>(
readVec.data()), readBytes);
}
if(sizesFifo != nullptr) {
if(not sizesFifo->full()) {
sizesFifo->insert(readBytes);
}
}
return BYTES_READ;
}
else {
// Should also not happen
sif::warning << "CommandExecutor::check: Error " << errno << ": " <<
strerror(errno) << std::endl;
}
}
else if(waiter.revents & POLLERR) {
sif::warning << "CommandExecuter::check: Poll error" << std::endl;
return COMMAND_ERROR;
}
else if(waiter.revents & POLLHUP) {
int result = pclose(currentCmdFile);
if(result != 0) {
lastError = result;
return HasReturnvaluesIF::RETURN_FAILED;
}
state = States::IDLE;
currentCmdFile = nullptr;
currentFd = 0;
return EXECUTION_FINISHED;
}
break;
}
}
return HasReturnvaluesIF::RETURN_OK;
}
void CommandExecutor::reset() {
CommandExecutor::close();
currentCmdFile = nullptr;
currentFd = 0;
state = States::IDLE;
}
int CommandExecutor::getLastError() const {
return 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) {
sif::info << currentCmd << " | " << output;
}
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,134 @@
#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
* - COMMAND_ERROR internal poll error
*/
ReturnValue_t check(bool& bytesRead);
/**
* 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

@ -27,6 +27,7 @@ int Timer::setTimer(uint32_t intervalMs) {
timer.it_value.tv_nsec = (intervalMs * 1000000) % (1000000000); timer.it_value.tv_nsec = (intervalMs * 1000000) % (1000000000);
timer.it_interval.tv_sec = 0; timer.it_interval.tv_sec = 0;
timer.it_interval.tv_nsec = 0; timer.it_interval.tv_nsec = 0;
set = true;
return timer_settime(timerId, 0, &timer, NULL); return timer_settime(timerId, 0, &timer, NULL);
} }
@ -43,3 +44,14 @@ int Timer::getTimer(uint32_t* remainingTimeMs){
return status; return status;
} }
bool Timer::isSet() const {
return this->set;
}
void Timer::resetTimer() {
if(not this->set) {
set = false;
}
setTimer(0);
}

View File

@ -38,7 +38,11 @@ public:
*/ */
int getTimer(uint32_t* remainingTimeMs); int getTimer(uint32_t* remainingTimeMs);
bool isSet() const;
void resetTimer();
private: private:
bool set = true;
timer_t timerId; timer_t timerId;
}; };

View File

@ -72,6 +72,7 @@ enum: uint8_t {
PUS_SERVICE_3, //PUS3 PUS_SERVICE_3, //PUS3
PUS_SERVICE_9, //PUS9 PUS_SERVICE_9, //PUS9
FILE_SYSTEM, //FILS FILE_SYSTEM, //FILS
LINUX_OSAL, //UXOS
HAL_SPI, //HSPI HAL_SPI, //HSPI
HAL_UART, //HURT HAL_UART, //HURT
HAL_I2C, //HI2C HAL_I2C, //HI2C

View File

@ -22,9 +22,9 @@ public:
* @param number * @param number
* @return * @return
*/ */
static constexpr ReturnValue_t makeReturnCode(uint8_t interfaceId, static constexpr ReturnValue_t makeReturnCode(uint8_t classId,
uint8_t number) { uint8_t number) {
return (static_cast<ReturnValue_t>(interfaceId) << 8) + number; return (static_cast<ReturnValue_t>(classId) << 8) + number;
} }
}; };

View File

@ -146,8 +146,8 @@ std::string* ServiceInterfaceBuffer::getPreamble(size_t * preambleSize) {
#endif #endif
int32_t charCount = sprintf(parsePosition, int32_t charCount = sprintf(parsePosition,
"%s: | %02" SCNu32 ":%02" SCNu32 ":%02" SCNu32 ".%03" SCNu32 " | ", "%s%s | %02" SCNu32 ":%02" SCNu32 ":%02" SCNu32 ".%03" SCNu32 " | ",
this->logMessage.c_str(), loggerTime.hour, this->logMessage.c_str(), sif::ANSI_COLOR_RESET, loggerTime.hour,
loggerTime.minute, loggerTime.minute,
loggerTime.second, loggerTime.second,
loggerTime.usecond /1000); loggerTime.usecond /1000);

View File

@ -56,25 +56,29 @@ void fsfwPrint(sif::PrintLevel printType, const char* fmt, va_list arg) {
#endif #endif
if (printType == sif::PrintLevel::INFO_LEVEL) { if (printType == sif::PrintLevel::INFO_LEVEL) {
len += sprintf(bufferPosition + len, "INFO: "); len += sprintf(bufferPosition + len, "INFO");
} }
if(printType == sif::PrintLevel::DEBUG_LEVEL) { if(printType == sif::PrintLevel::DEBUG_LEVEL) {
len += sprintf(bufferPosition + len, "DEBUG: "); len += sprintf(bufferPosition + len, "DEBUG");
} }
if(printType == sif::PrintLevel::WARNING_LEVEL) { if(printType == sif::PrintLevel::WARNING_LEVEL) {
len += sprintf(bufferPosition + len, "WARNING: "); len += sprintf(bufferPosition + len, "WARNING");
} }
if(printType == sif::PrintLevel::ERROR_LEVEL) { if(printType == sif::PrintLevel::ERROR_LEVEL) {
len += sprintf(bufferPosition + len, "ERROR: "); len += sprintf(bufferPosition + len, "ERROR");
} }
#if FSFW_COLORED_OUTPUT == 1
len += sprintf(bufferPosition + len, sif::ANSI_COLOR_RESET);
#endif
Clock::TimeOfDay_t now; Clock::TimeOfDay_t now;
Clock::getDateAndTime(&now); Clock::getDateAndTime(&now);
/* /*
* Log current time to terminal if desired. * Log current time to terminal if desired.
*/ */
len += sprintf(bufferPosition + len, "| %lu:%02lu:%02lu.%03lu | ", len += sprintf(bufferPosition + len, " | %lu:%02lu:%02lu.%03lu | ",
(unsigned long) now.hour, (unsigned long) now.hour,
(unsigned long) now.minute, (unsigned long) now.minute,
(unsigned long) now.second, (unsigned long) now.second,