added new command executor
This commit is contained in:
parent
1a4a85ceb2
commit
c8472beb5f
@ -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)
|
||||||
|
180
src/fsfw/osal/linux/CommandExecutor.cpp
Normal file
180
src/fsfw/osal/linux/CommandExecutor.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 EXECUTION_FINISHED;
|
||||||
|
}
|
133
src/fsfw/osal/linux/CommandExecutor.h
Normal file
133
src/fsfw/osal/linux/CommandExecutor.h
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
#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();
|
||||||
|
|
||||||
|
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_ */
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user