Skip to content

Commit

Permalink
Merge pull request #359 from odin-detector/commands
Browse files Browse the repository at this point in the history
Implemented command and requestCommands methods in the FrameProcessor.
  • Loading branch information
ajgdls authored Oct 10, 2024
2 parents acdcc9d + 03168c3 commit 004b15e
Show file tree
Hide file tree
Showing 10 changed files with 219 additions and 1 deletion.
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

0 comments on commit 004b15e

Please sign in to comment.