diff --git a/CMakeLists.txt b/CMakeLists.txt
index 224ebe5..b5636d6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,7 +33,7 @@ daq_protobuf_codegen( opmon/*.proto )
##############################################################################
# Main library
-daq_add_library(DAQModule*.cpp ConfigurationManager.cpp ModuleConfiguration.cpp
+daq_add_library(Application.cpp DAQModule.cpp DAQModuleManager.cpp ConfigurationManager.cpp ModuleConfiguration.cpp
LINK_LIBRARIES ${APPFWK_DEPENDENCIES})
##############################################################################
diff --git a/docs/ActionPlans.md b/docs/ActionPlans.md
new file mode 100755
index 0000000..7b70f86
--- /dev/null
+++ b/docs/ActionPlans.md
@@ -0,0 +1,72 @@
+# Action Plans
+
+## Overview
+
+An ActionPlan defines a series of ActionSteps, each one consisting of a set of DAQModule classes to run the command on. Each ActionPlan is associated with a FSMCommand object, and is run by the appliction when it recieves the corresponding command. If a command is received and no ActionPlan is defined, the application currently runs a "dummy" ActionPlan consisting of a single step where registered Actions matching the command name are all run in parallel.
+
+## Defining an ActionPlan
+
+ActionPlans are defined in configuration using these objects:
+
+```XML
+
+
+
+
+
+
+
+
+```
+
+1. ActionPlan relates a set of ActionSteps to a FSMCommand instance.
+1. ActionStep lists the module types to run in parallel at a given point in the sequence
+
+ActionPlans are validated by the application to ensure that every module type has registered methods corresponding to the command linked to the ActionPlan.
+
+### Example test/config/appfwk.data.xml
+
+The DAQModuleManager_test unit test defines several ActionPlans used within the test. For example, the "do_stuff" action:
+
+```XML
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+The ActionPlans are associated with the Application instance as follows:
+
+```XML
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## Notes
+
+* DAQModules register their action methods in the same way as before, however the specification of valid states for an action has been removed
+* ActionSteps target module types, with the assumption that if multiple modules of the same class are present within an application, they will all run their defined action methods in unison (or at least within the same parallel processing step)
diff --git a/docs/README.md b/docs/README.md
index 18f31cf..457a215 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -10,7 +10,7 @@ appfwk consists of a generic DAQ application (`daq_application`) which can be co
appfwk provides the scaffolding on which all DUNE DAQ software processes can be developed. The running DAQ typically consists of multiple distinct processes assigned various tasks: filtering data, requesting it, saving it to storage, etc. There are many different types of process, some of which may not even have been conceived of yet, and it would be cumbersome to recompile multiple different types of process across many packages every time one wanted to change the behavior of the DAQ. To solve this problem, the approach that's been taken is to have a standard DUNE DAQ software process [`daq_application`](Daq-Application.md) which can be configured at runtime by Run Control in order to perform some particular function in the DAQ.
-`daq_application` is designed as a flexible container of "DAQ modules" (units of code designed to perform specific tasks) and "connections" (designed to move data between DAQ modules that can be in the same or in different DAQ applications). These specific tasks can vary widely; they include [producing fake data for testing purposes](https://github.com/DUNE-DAQ/readoutmodules/blob/develop/plugins/FakeCardReader.hpp), [putting data into long term storage](https://github.com/DUNE-DAQ/dfmodules/blob/develop/plugins/DataWriterModule.hpp), and so forth. DAQ modules will typically execute user-defined functions when receiving standard transitions from Run Control: "conf", "start", etc. appfwk provides the `DAQModule` base class which users should derive their DAQ module class from in their own packages.
+`daq_application` is designed as a flexible container of "DAQ modules" (units of code designed to perform specific tasks) and "connections" (designed to move data between DAQ modules that can be in the same or in different DAQ applications). These specific tasks can vary widely; they include [producing fake data for testing purposes](https://github.com/DUNE-DAQ/readoutmodules/blob/develop/plugins/FakeCardReader.hpp), [putting data into long term storage](https://github.com/DUNE-DAQ/dfmodules/blob/develop/plugins/DataWriterModule.hpp), and so forth. DAQ modules will typically execute user-defined functions when receiving standard transitions from Run Control: "conf", "start", etc. appfwk provides the `DAQModule` base class which users should derive their DAQ module class from in their own packages. Read more about ActionPlans [here](ActionPlans.md).
![daq_application](https://github.com/DUNE-DAQ/appfwk/raw/develop/docs/Application.png)
diff --git a/include/appfwk/DAQModule.hpp b/include/appfwk/DAQModule.hpp
index f80d10a..e89d7f0 100644
--- a/include/appfwk/DAQModule.hpp
+++ b/include/appfwk/DAQModule.hpp
@@ -137,12 +137,13 @@ ERS_DECLARE_ISSUE_BASE(appfwk, ///< Namespace
/**
* @brief The MissingConnection DAQModule ERS Issue
*/
-ERS_DECLARE_ISSUE_BASE(appfwk, ///< Namespace
- MissingConnection, ///< Type of the Issue
- appfwk::GeneralDAQModuleIssue, ///< Base class of the Issue
- "Required Connection Not Found. Type: " << type << ", direction: " << direction, ///< Log Message from the issue
- ((std::string)name), ///< Base class attributes
- ((std::string)type)((std::string)direction) ///< Attribute of this class
+ERS_DECLARE_ISSUE_BASE(appfwk, ///< Namespace
+ MissingConnection, ///< Type of the Issue
+ appfwk::GeneralDAQModuleIssue, ///< Base class of the Issue
+ "Required Connection Not Found. Type: " << type << ", direction: "
+ << direction, ///< Log Message from the issue
+ ((std::string)name), ///< Base class attributes
+ ((std::string)type)((std::string)direction) ///< Attribute of this class
)
// Re-enable coverage collection LCOV_EXCL_STOP
@@ -197,11 +198,11 @@ namespace appfwk {
* Non-accepted commands or failure should return an ERS exception
* indicating this result.
*/
- void execute_command(const std::string& name, const std::string& state, const data_t& data = {});
+ void execute_command(const std::string& name, const data_t& data = {});
std::vector get_commands() const;
- bool has_command(const std::string& name, const std::string& state) const;
+ bool has_command(const std::string& name) const;
protected:
/**
@@ -209,9 +210,7 @@ namespace appfwk {
* Returns whether the command was inserted (false meaning that command `cmd` already exists)
*/
template
- void register_command(const std::string& name,
- void (Child::*f)(const data_t&),
- const std::set& valid_states = std::set{ "ANY" });
+ void register_command(const std::string& name, void (Child::*f)(const data_t&));
DAQModule(DAQModule const&) = delete;
DAQModule(DAQModule&&) = delete;
@@ -219,7 +218,7 @@ namespace appfwk {
DAQModule& operator=(DAQModule&&) = delete;
private:
- using CommandMap_t = std::map, std::function>>;
+ using CommandMap_t = std::map>;
CommandMap_t m_commands;
};
diff --git a/include/appfwk/ModuleConfiguration.hpp b/include/appfwk/ModuleConfiguration.hpp
index ecef18d..b2f6c20 100644
--- a/include/appfwk/ModuleConfiguration.hpp
+++ b/include/appfwk/ModuleConfiguration.hpp
@@ -12,6 +12,7 @@
#define APPFWK_INCLUDE_MODULECONFIGURATION_HPP_
#include "appfwk/ConfigurationManager.hpp"
+#include "confmodel/ActionPlan.hpp"
#include "confmodel/DaqModule.hpp"
#include "iomanager/IOManager.hpp"
#include "conffwk/Configuration.hpp"
@@ -26,10 +27,10 @@ ERS_DECLARE_ISSUE(appfwk,
"Application contains a resource " << res << " that is not a DaqModule", ///< Message
((std::string)res) ///< Message parameters
)
-ERS_DECLARE_ISSUE(appfwk, ///< Namespace
- NotADaqApplication, ///< Issue class name
+ERS_DECLARE_ISSUE(appfwk, ///< Namespace
+ NotADaqApplication, ///< Issue class name
"Application " << app << " is neither a DaqApplication nor a SmartDaqApplication ", ///< Message
- ((std::string)app) ///< Message parameters
+ ((std::string)app) ///< Message parameters
)
namespace confmodel {
@@ -41,10 +42,11 @@ namespace appfwk {
class ModuleConfiguration
{
+ std::shared_ptr m_config_mgr;
+ std::unordered_map m_action_plans;
std::vector m_modules;
iomanager::Queues_t m_queues;
iomanager::Connections_t m_networkconnections;
- std::shared_ptr m_config_mgr;
public:
explicit ModuleConfiguration(std::shared_ptr mgr);
@@ -53,6 +55,9 @@ class ModuleConfiguration
const iomanager::Connections_t& networkconnections() { return m_networkconnections; }
const std::vector& modules() { return m_modules; }
+ const std::unordered_map& action_plans() { return m_action_plans; }
+ const dunedaq::confmodel::ActionPlan* action_plan(std::string cmd) const;
+
std::shared_ptr configuration_manager() { return m_config_mgr; }
template
diff --git a/include/appfwk/detail/DAQModule.hxx b/include/appfwk/detail/DAQModule.hxx
index 482d413..3b29522 100644
--- a/include/appfwk/detail/DAQModule.hxx
+++ b/include/appfwk/detail/DAQModule.hxx
@@ -3,12 +3,11 @@ namespace dunedaq::appfwk {
template
void
DAQModule::register_command(const std::string& cmd_name,
- void (Child::*f)(const data_t&),
- const std::set& states)
+ void (Child::*f)(const data_t&))
{
using namespace std::placeholders;
- bool done = m_commands.emplace(cmd_name, std::make_pair(states, std::bind(f, dynamic_cast(this), _1))).second;
+ bool done = m_commands.emplace(cmd_name, std::bind(f, dynamic_cast(this), _1)).second;
if (!done) {
// Throw here
throw CommandRegistrationFailed(ERS_HERE, get_name(), cmd_name);
diff --git a/src/detail/Application.hxx b/src/Application.cpp
similarity index 98%
rename from src/detail/Application.hxx
rename to src/Application.cpp
index d13d2ea..dd8e92d 100644
--- a/src/detail/Application.hxx
+++ b/src/Application.cpp
@@ -6,6 +6,8 @@
* received with this code.
*/
+#include "Application.hpp"
+
#include "appfwk/Issues.hpp"
#include "appfwk/opmon/application.pb.h"
#include "appfwk/cmd/Nljs.hpp"
@@ -109,7 +111,7 @@ Application::execute(const dataobj_t& cmd_data)
}
try {
- m_mod_mgr.execute(get_state(), cmdname, rc_cmd.data);
+ m_mod_mgr.execute(cmdname, rc_cmd.data);
m_busy.store(false);
if (rc_cmd.exit_state != "ANY")
set_state(rc_cmd.exit_state);
diff --git a/src/Application.hpp b/src/Application.hpp
index 954da31..7ac44f9 100644
--- a/src/Application.hpp
+++ b/src/Application.hpp
@@ -112,6 +112,4 @@ class Application
} // namespace appfwk
} // namespace dunedaq
-#include "detail/Application.hxx"
-
#endif // APPFWK_INCLUDE_APPFWK_APPLICATION_HPP_
diff --git a/src/DAQModule.cpp b/src/DAQModule.cpp
index f92707e..807467d 100644
--- a/src/DAQModule.cpp
+++ b/src/DAQModule.cpp
@@ -15,15 +15,11 @@
namespace dunedaq::appfwk {
void
-DAQModule::execute_command(const std::string& cmd_name, const std::string& state, const data_t& data)
+DAQModule::execute_command(const std::string& cmd_name, const data_t& data)
{
if (auto cmd = m_commands.find(cmd_name); cmd != m_commands.end()) {
- if (cmd->second.first.find("ANY") != cmd->second.first.end() ||
- (cmd->second.first.find(state) != cmd->second.first.end())) {
- std::invoke(cmd->second.second, data);
+ std::invoke(cmd->second, data);
return;
- }
- throw InvalidState(ERS_HERE, get_name(), cmd_name, state);
}
throw UnknownCommand(ERS_HERE, get_name(), cmd_name);
}
@@ -38,14 +34,10 @@ DAQModule::get_commands() const
}
bool
-DAQModule::has_command(const std::string& cmd_name, const std::string& state) const
+DAQModule::has_command(const std::string& cmd_name) const
{
if (auto cmd = m_commands.find(cmd_name); cmd != m_commands.end()) {
- if (cmd->second.first.find("ANY") != cmd->second.first.end() ||
- (cmd->second.first.find(state) != cmd->second.first.end())) {
return true;
- }
- ers::warning(InvalidState(ERS_HERE, get_name(), cmd_name, state));
}
return false;
}
diff --git a/src/DAQModuleManager.cpp b/src/DAQModuleManager.cpp
new file mode 100644
index 0000000..8c8f121
--- /dev/null
+++ b/src/DAQModuleManager.cpp
@@ -0,0 +1,286 @@
+/**
+ * @file DAQModuleManager.cpp DAQModuleManager implementataion
+ *
+ * This is part of the DUNE DAQ Application Framework, copyright 2020.
+ * Licensing/copyright details are in the COPYING file that you should have
+ * received with this code.
+ */
+
+#include "DAQModuleManager.hpp"
+
+#include "cmdlib/cmd/Nljs.hpp"
+
+#include "appfwk/Issues.hpp"
+#include "appfwk/app/Nljs.hpp"
+#include "appfwk/cmd/Nljs.hpp"
+
+#include "appfwk/DAQModule.hpp"
+
+#include "confmodel/ActionStep.hpp"
+#include "confmodel/Session.hpp"
+
+#include "iomanager/IOManager.hpp"
+
+#include "logging/Logging.hpp"
+
+#include
+#include