Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented command and requestCommands methods in the FrameProcessor. #359

Merged
merged 2 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cpp/common/include/IpcMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class IpcMessage
MsgValCmdStatus, //!< Status command message
MsgValCmdConfigure, //!< Configure command message
MsgValCmdRequestConfiguration, //!< Request configuration command message
MsgValCmdExecute, //!< Execute a command message
MsgValCmdRequestCommands, //!< Request available commands message
MsgValCmdRequestVersion, //!< Request version information message
MsgValCmdBufferConfigRequest, //!< Buffer configuration request
MsgValCmdBufferPrechargeRequest, //!< Buffer precharge request
Expand Down
2 changes: 2 additions & 0 deletions cpp/common/src/IpcMessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,8 @@ void IpcMessage::msg_val_map_init()
msg_val_map_.insert(MsgValMapEntry("status", MsgValCmdStatus));
msg_val_map_.insert(MsgValMapEntry("configure", MsgValCmdConfigure));
msg_val_map_.insert(MsgValMapEntry("request_configuration", MsgValCmdRequestConfiguration));
msg_val_map_.insert(MsgValMapEntry("execute", MsgValCmdExecute));
msg_val_map_.insert(MsgValMapEntry("request_commands", MsgValCmdRequestCommands));
msg_val_map_.insert(MsgValMapEntry("request_version", MsgValCmdRequestVersion));
msg_val_map_.insert(MsgValMapEntry("request_buffer_config", MsgValCmdBufferConfigRequest));
msg_val_map_.insert(MsgValMapEntry("request_buffer_precharge", MsgValCmdBufferPrechargeRequest));
Expand Down
5 changes: 5 additions & 0 deletions cpp/frameProcessor/include/DummyUDPProcessPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class DummyUDPProcessPlugin : public FrameProcessorPlugin

void configure(OdinData::IpcMessage& config, OdinData::IpcMessage& reply);
void requestConfiguration(OdinData::IpcMessage& reply);
void execute(const std::string& command, OdinData::IpcMessage& reply);
std::vector<std::string> requestCommands();
void status(OdinData::IpcMessage& status);
bool reset_statistics(void);

Expand All @@ -54,6 +56,9 @@ class DummyUDPProcessPlugin : public FrameProcessorPlugin
/** Configuraiton constant for copy frame mode **/
static const std::string CONFIG_COPY_FRAME;

/** Command execution constant for print command **/
static const std::string EXECUTE_PRINT;

void process_frame(boost::shared_ptr<Frame> frame);
void process_lost_packets(boost::shared_ptr<Frame>& frame);

Expand Down
5 changes: 5 additions & 0 deletions cpp/frameProcessor/include/FrameProcessorController.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class FrameProcessorController : public IFrameCallback,
void provideVersion(OdinData::IpcMessage& reply);
void configure(OdinData::IpcMessage& config, OdinData::IpcMessage& reply);
void requestConfiguration(OdinData::IpcMessage& reply);
void execute(OdinData::IpcMessage& config, OdinData::IpcMessage& reply);
void requestCommands(OdinData::IpcMessage& reply);
void resetStatistics(OdinData::IpcMessage& reply);
void configurePlugin(OdinData::IpcMessage& config, OdinData::IpcMessage& reply);
void loadPlugin(const std::string& index, const std::string& name, const std::string& library);
Expand Down Expand Up @@ -109,6 +111,9 @@ class FrameProcessorController : public IFrameCallback,
/** Configuration constant for the value of a stored configuration object **/
static const std::string CONFIG_VALUE;

/** Configuration constant for the a command to execute **/
static const std::string COMMAND_KEY;

/** Configuration constant for the meta TX channel high water mark **/
static const int META_TX_HWM;

Expand Down
2 changes: 2 additions & 0 deletions cpp/frameProcessor/include/FrameProcessorPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class FrameProcessorPlugin : public IFrameCallback, public OdinData::IVersionedO
std::vector<std::string> get_warnings();
virtual void configure(OdinData::IpcMessage& config, OdinData::IpcMessage& reply);
virtual void requestConfiguration(OdinData::IpcMessage& reply);
virtual void execute(const std::string& command, OdinData::IpcMessage& reply);
virtual std::vector<std::string> requestCommands();
virtual void status(OdinData::IpcMessage& status);
void add_performance_stats(OdinData::IpcMessage& status);
void reset_performance_stats();
Expand Down
39 changes: 39 additions & 0 deletions cpp/frameProcessor/src/DummyUDPProcessPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ namespace FrameProcessor
const std::string DummyUDPProcessPlugin::CONFIG_IMAGE_WIDTH = "width";
const std::string DummyUDPProcessPlugin::CONFIG_IMAGE_HEIGHT = "height";
const std::string DummyUDPProcessPlugin::CONFIG_COPY_FRAME = "copy_frame";
// Command and parameters
const std::string DummyUDPProcessPlugin::EXECUTE_PRINT = "print";

/**
* The constructor sets up default configuration parameters and logging used within the class.
Expand Down Expand Up @@ -135,6 +137,43 @@ namespace FrameProcessor
reply.set_param(base_str + DummyUDPProcessPlugin::CONFIG_COPY_FRAME, copy_frame_);
}

/**
* Execute a command on the plugin. This receives an IpcMessage which should be processed
* to execute a command within the plugin, and any response can be added to the reply IpcMessage.
* The dummy plugin implements a single command "print" that prints the value of the parameter named.
*
* \param[in] config - String containing the command to execute.
* \param[out] reply - Reference to the reply IpcMessage object.
*/
void DummyUDPProcessPlugin::execute(const std::string& command, OdinData::IpcMessage& reply)
{
if (command == DummyUDPProcessPlugin::EXECUTE_PRINT){
LOG4CXX_INFO(logger_, "Image width is " << image_width_);
LOG4CXX_INFO(logger_, "Image height is " << image_height_);
LOG4CXX_INFO(logger_, "Copy frame is " << copy_frame_);
} else {
std::stringstream is;
is << "Submitted command not supported: " << command;
LOG4CXX_ERROR(logger_, is.str());
throw std::runtime_error(is.str().c_str());
}
}

/**
* Respond to command execution requests from clients.
*
* This method responds to command executions requests from client, populating the supplied IpcMessage
* reply with the commands and command parameters supported by this plugin.
*
* \return - Vector containing supported command strings.
*/
std::vector<std::string> DummyUDPProcessPlugin::requestCommands()
{
// Reply with a vector of supported command strings.
std::vector<std::string> cmds = {DummyUDPProcessPlugin::EXECUTE_PRINT};
return cmds;
}

/**
* Collate status information for the plugin. The status is added to the status IpcMessage object.
*
Expand Down
82 changes: 82 additions & 0 deletions cpp/frameProcessor/src/FrameProcessorController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ const std::string FrameProcessorController::CONFIG_EXECUTE = "exec
const std::string FrameProcessorController::CONFIG_INDEX = "index";
const std::string FrameProcessorController::CONFIG_VALUE = "value";

const std::string FrameProcessorController::COMMAND_KEY = "command";

const int FrameProcessorController::META_TX_HWM = 10000;

/** Construct a new FrameProcessorController class.
Expand Down Expand Up @@ -135,6 +137,20 @@ void FrameProcessorController::handleCtrlChannel()
LOG4CXX_DEBUG_LEVEL(3, logger_, "Control thread reply message (request configuration): "
<< replyMsg.encode());
}
else if ((ctrlMsg.get_msg_type() == OdinData::IpcMessage::MsgTypeCmd) &&
(ctrlMsg.get_msg_val() == OdinData::IpcMessage::MsgValCmdExecute)) {
replyMsg.set_msg_type(OdinData::IpcMessage::MsgTypeAck);
this->execute(ctrlMsg, replyMsg);
LOG4CXX_DEBUG_LEVEL(3, logger_, "Control thread reply message (command): "
<< replyMsg.encode());
}
else if ((ctrlMsg.get_msg_type() == OdinData::IpcMessage::MsgTypeCmd) &&
(ctrlMsg.get_msg_val() == OdinData::IpcMessage::MsgValCmdRequestCommands)) {
replyMsg.set_msg_type(OdinData::IpcMessage::MsgTypeAck);
this->requestCommands(replyMsg);
LOG4CXX_DEBUG_LEVEL(3, logger_, "Control thread reply message (request commands): "
<< replyMsg.encode());
}
else if ((ctrlMsg.get_msg_type() == OdinData::IpcMessage::MsgTypeCmd) &&
(ctrlMsg.get_msg_val() == OdinData::IpcMessage::MsgValCmdStatus)) {
replyMsg.set_msg_type(OdinData::IpcMessage::MsgTypeAck);
Expand Down Expand Up @@ -507,6 +523,72 @@ void FrameProcessorController::requestConfiguration(OdinData::IpcMessage& reply)
}
}

/**
* Submit commands to the FrameProcessor plugins.
*
* Submits command(s) to execute on individual plugins if they
* support commands. The IpcMessage should contain the command
* name and a structure of any parameters required by the command.
*
* This method searches for command objects that have the same
* index as loaded plugins. If any of these are found then the
* commands are passed down to the plugin for execution.
*
* \param[in] config - IpcMessage containing command and any parameter data.
* \param[out] reply - Response IpcMessage.
*/
void FrameProcessorController::execute(OdinData::IpcMessage& config, OdinData::IpcMessage& reply)
{
LOG4CXX_DEBUG_LEVEL(1, logger_, "Command submitted: " << config.encode());

// Loop over plugins, checking for command messages
std::map<std::string, boost::shared_ptr<FrameProcessorPlugin> >::iterator iter;
bool commandPresent = false;
for (iter = plugins_.begin(); iter != plugins_.end(); ++iter) {
if (config.has_param(iter->first)) {
OdinData::IpcMessage subConfig(config.get_param<const rapidjson::Value&>(iter->first),
config.get_msg_type(),
config.get_msg_val());
// Check if the payload has a command for this plugin
if (subConfig.has_param(FrameProcessorController::COMMAND_KEY)){
commandPresent = true;
// Extract the command and execute on the plugin
std::string commandName = subConfig.get_param<std::string>(FrameProcessorController::COMMAND_KEY);
iter->second->execute(commandName, reply);
}
}
}
if (!commandPresent){
// If no valid commands have been found after checking through all plugins then NACK the reply
reply.set_nack("No valid commands found");
}
}

/**
* Request the command set supported by this FrameProcessorController and
* its loaded plugins.
*
* The method searches through all loaded plugins. Each plugin is
* also sent a request for its supported commands.
*
* \param[out] reply - Response IpcMessage with the current supported command set.
*/
void FrameProcessorController::requestCommands(OdinData::IpcMessage& reply)
{
LOG4CXX_DEBUG_LEVEL(3, logger_, "Request for supported commands made");

// Loop over plugins and request current supported commands from each
std::map<std::string, boost::shared_ptr<FrameProcessorPlugin> >::iterator iter;
std::vector<std::string>::iterator cmd;
for (iter = plugins_.begin(); iter != plugins_.end(); ++iter) {
std::vector<std::string> commands = iter->second->requestCommands();
for (cmd = commands.begin(); cmd != commands.end(); ++cmd) {
std::string command_str = iter->first + "/" + FrameProcessorController::COMMAND_KEY + "[]";
reply.set_param(command_str, *cmd);
}
}
}

/**
* Reset statistics on all of the loaded plugins.
*
Expand Down
34 changes: 33 additions & 1 deletion cpp/frameProcessor/src/FrameProcessorPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ std::vector<std::string> FrameProcessorPlugin::get_warnings()
return warning_messages_;
}

/** Configure the plugin.
/** Configure the plugin.
*
* In this abstract class the configure method does perform any
* actions, this should be overridden by subclasses.
Expand All @@ -168,6 +168,38 @@ void FrameProcessorPlugin::requestConfiguration(OdinData::IpcMessage& reply)
// Default method simply does nothing
}

/** Execute a command within the plugin.
*
* In this abstract class the command method does perform any
* actions, this should be overridden by subclasses.
*
* \param[in] command - String containing the command to execute.
* \param[out] reply - Response IpcMessage.
*/
void FrameProcessorPlugin::execute(const std::string& command, OdinData::IpcMessage& reply)
{
// A command has been submitted and this plugin has no execute implementation defined,
// throw a runtime error to report this.
std::stringstream is;
is << "Submitted command not supported: " << command;
LOG4CXX_ERROR(logger_, is.str());
throw std::runtime_error(is.str().c_str());
}

/** Request the plugin's supported commands.
*
* In this abstract class the request method does perform any
* actions, this should be overridden by subclasses.
*
* \return - Vector containing supported command strings.
*/
std::vector<std::string> FrameProcessorPlugin::requestCommands()
{
// Default returns an empty vector.
std::vector<std::string> reply;
return reply;
}

/**
* Collate status information for the plugin.
*
Expand Down
2 changes: 2 additions & 0 deletions cpp/frameProcessor/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ file(GLOB TEST_SOURCES
FrameProcessorTest.cpp
GapFillPluginTest.cpp
MetaMessageTest.cpp
DummyUDPProcessPluginTest.cpp
)
# Add tests for BloscPlugin if Blosc is present
if (${BLOSC_FOUND})
Expand Down Expand Up @@ -39,6 +40,7 @@ target_link_libraries(frameProcessorTest
LiveViewPlugin
SumPlugin
GapFillPlugin
DummyUDPProcessPlugin
${COMMON_LIBRARY})

# Link for BloscPlugin if Blosc is present
Expand Down
47 changes: 47 additions & 0 deletions cpp/frameProcessor/test/DummyUDPProcessPluginTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* DummyUDPProcessPluginTest.cpp
*
* Created on: 25 Sep 2024
* Author: Alan Greer
*/

#include <boost/test/unit_test.hpp>
#include <DebugLevelLogger.h>
#include "FrameProcessorDefinitions.h"
#include "DummyUDPProcessPlugin.h"
#include "IpcMessage.h"

class DummyUDPProcessPluginTestFixture {
public:
DummyUDPProcessPluginTestFixture() {
set_debug_level(3);

dummy_plugin.set_name("dummy");
}

~DummyUDPProcessPluginTestFixture() {}

FrameProcessor::DummyUDPProcessPlugin dummy_plugin;
};

BOOST_FIXTURE_TEST_SUITE(DummyUDPProcessPluginUnitTest, DummyUDPProcessPluginTestFixture);

BOOST_AUTO_TEST_CASE( DummyUDPProcessPlugin_commands )
{
std::vector<std::string> commands_reply;
OdinData::IpcMessage command_reply;

// Request the command set of the DummyUDPProcessPlugin
BOOST_REQUIRE_NO_THROW(commands_reply = dummy_plugin.requestCommands());

// Verify the returned command set is as expected
BOOST_CHECK_EQUAL(commands_reply[0], std::string("print"));

// Verify that an incorrect commmand request is rejected
BOOST_CHECK_THROW(dummy_plugin.execute("bad_command", command_reply), std::runtime_error);

// Verify that a supported commmand request is accepted
BOOST_REQUIRE_NO_THROW(dummy_plugin.execute("print", command_reply));
};

BOOST_AUTO_TEST_SUITE_END(); //DummyUDPProcessPluginUnitTest
Loading