Skip to content

Commit

Permalink
Merge pull request #139 from DUNE-DAQ/johnfreeman/issue135_multiple_s…
Browse files Browse the repository at this point in the history
…essions

JCF: Issue #135: add a session argument to create_config_plot to allo…
  • Loading branch information
jcfreeman2 authored Oct 10, 2024
2 parents 35adc5c + d7f6554 commit a89ea0f
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 30 deletions.
14 changes: 8 additions & 6 deletions apps/create_config_plot.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ int main(int argc, char* argv[])
std::string oksfilename {""};
std::string outputfilename {""};
std::string object_uid {""};
std::string sessionname {""};

bpo::options_description options_description (
"Allowed options", 128 );
Expand All @@ -44,8 +45,9 @@ int main(int argc, char* argv[])

( "file,f", bpo::value<std::string> ( &oksfilename ), "OKS database file name" )

( "root-object,r", bpo::value<std::string>(&object_uid), "OKS object UID of root vertex; must be session, segment or application")

( "root-object,r", bpo::value<std::string>(&object_uid)->default_value(""), "OKS object UID of root vertex; must be session, segment or application")
( "session,s", bpo::value<std::string> ( &sessionname )->default_value(""),
"Name of the session associated with the root object (only needed if >1 session in the database)" )
( "output,o", bpo::value<std::string> ( &outputfilename )->default_value("config.dot"),
"Output DOT file which can be used as input to GraphViz" );

Expand All @@ -57,7 +59,7 @@ int main(int argc, char* argv[])
<< "create_config_plot : Generate dot graphs from database files"
<< std::endl
<< std::endl
<< "Usage: create_config_plot -f/--file <input OKS file> -r/--root-object <object UID for session, segment or application> (-o/--output <output DOT file, default is config.dot>)"
<< "Usage: create_config_plot -f/--file <input OKS file> -r/--root-object <object UID for session, segment or application> (-s/--session <session containing root-object>) (-o/--output <output DOT file, default is config.dot>)"
<< std::endl
<< std::endl
<< options_description
Expand All @@ -70,13 +72,13 @@ int main(int argc, char* argv[])
args );
bpo::notify ( args );

if ( args.count ( "help" ) || ! args.count ( "file" ) || ! args.count("root-object") )
if ( args.count ( "help" ) || ! args.count ( "file" ) )
{
display_help_message();
return EXIT_FAILURE;
}
appmodel::GraphBuilder graphbuilder(oksfilename);

appmodel::GraphBuilder graphbuilder(oksfilename, sessionname);
graphbuilder.construct_graph(object_uid);
graphbuilder.write_graph(outputfilename);

Expand Down
14 changes: 9 additions & 5 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,19 @@ and Connections.

## Creating A Diagram of Your Configuration

In `appmodel` it's possible to create a diagram of a DAQ configuration in the [DOT graph description language](https://en.wikipedia.org/wiki/DOT_(graph_description_language)), which can then be fed to the `dot` program to generate a viewable graphic. This is accomplished using the `create_config_plot` application. You can run `create_config_plot -h` to see how it's used, but to explain it simply, you provide it with a database file and the name of a session, segment or application in the database, and it will create a plot using that object as the root of the plot. E.g., with the `appmodel` repo you can generate a plot of its entire test DAQ session via:
In `appmodel` it's possible to create a diagram of a DAQ configuration in the [DOT graph description language](https://en.wikipedia.org/wiki/DOT_(graph_description_language)), which can then be fed to the `dot` program to generate a viewable graphic. This is accomplished using the `create_config_plot` application. You can run `create_config_plot -h` to see how it's used, but to explain it simply, you provide it with a database file, the name of a session in that database, and then the name of an object associated with the session. It will create a plot using that object as the root of the plot. Note the object either needs to be the session itself, or a segment or an application in it. E.g., with the `daqsystemtest` repo you can generate a plot of the `mlt` application in its `ehn1-local-2x3-config` test DAQ session via:
```
create_config_plot -f $APPMODEL_SHARE/test/config/test-session.data.xml -r test-session
create_config_plot -f config/daqsystemtest/example-configs.data.xml -s ehn1-local-2x3-config -r mlt
```
and if you want to plot the `mlt` application in there, you can just do
and if you want to plot the entire session, you can run
```
create_config_plot -f $APPMODEL_SHARE/test/config/test-session.data.xml -r mlt
create_config_plot -f config/daqsystemtest/example-configs.data.xml -s ehn1-local-2x3-config -r ehn1-local-2x3-config
```
Either of these commands will create (or clobber) a file called `config.dot`. If you wish to give the file a different name you can use the `-o` option, e.g., `-o mypreferredname.dot`. Once you have the DOT file, you can generate a graphic by doing the following:
or a shorthand version of that command is
```
create_config_plot -f config/daqsystemtest/example-configs.data.xml -s ehn1-local-2x3-config
```
...where if no root object is specified the default is for `create_config_plot` to take the session passed to `-s` and use it as the root object. Also note that in the event that there's a single session in the database, the `-s <session-name>` argument isn't required. Any of the above commands will create (or clobber) a file called `config.dot`. If you wish to give the file a different name you can use the `-o` option, e.g., `-o mypreferredname.dot`. Once you have the DOT file, you can generate a graphic by doing the following:
```
dot -Tsvg -o mypreferredname.svg config.dot
```
Expand Down
71 changes: 56 additions & 15 deletions src/GraphBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

namespace appmodel {

GraphBuilder::GraphBuilder(const std::string& oksfilename) :
GraphBuilder::GraphBuilder(const std::string& oksfilename, const std::string& sessionname) :
m_oksfilename { oksfilename },
m_confdb { nullptr },
m_included_classes {
Expand All @@ -56,7 +56,8 @@ namespace appmodel {
{ ObjectKind::kModule, {"Module"} }
},
m_root_object_kind { ObjectKind::kUndefined },
m_session { nullptr }
m_session { nullptr },
m_session_name { sessionname }
{

// Open the database represented by the OKS XML file
Expand All @@ -68,17 +69,28 @@ namespace appmodel {
throw exc;
}

// Get the session in the database. Can handle one and only one session.
// Get the session in the database
std::vector<ConfigObject> session_objects {};

m_confdb->get("Session", session_objects);

if (session_objects.size() == 1) {
m_session_name = session_objects[0].UID();
} else {
std::stringstream errmsg;
errmsg << "Did not find one and only one Session instance in \"" << m_oksfilename << "\" and its includes";
throw appmodel::GeneralGraphToolError(ERS_HERE, errmsg.str());
if (m_session_name == "") { // If no session name given, use the one-and-only session expected in the database
if (session_objects.size() == 1) {
m_session_name = session_objects[0].UID();
} else {
std::stringstream errmsg;
errmsg << "No Session instance name was provided, and since " << session_objects.size() << " session instances were found in \"" << m_oksfilename << "\" this is an error";

throw appmodel::GeneralGraphToolError(ERS_HERE, errmsg.str());
}
} else { // session name provided by the user, let's make sure it's there
auto it = std::ranges::find_if(session_objects, [&](const ConfigObject& obj) { return obj.UID() == m_session_name; });

if (it == session_objects.end()) {
std::stringstream errmsg;
errmsg << "Did not find Session instance \"" << m_session_name << "\" in \"" << m_oksfilename << "\" and its includes";
throw appmodel::GeneralGraphToolError(ERS_HERE, errmsg.str());
}
}

// The following not-brief section of code is dedicated to
Expand Down Expand Up @@ -169,6 +181,35 @@ namespace appmodel {

void GraphBuilder::calculate_graph(const std::string& root_obj_uid) {

// To start, get the session / segments / applications in the
// session by setting up a temporary graph with the session as its
// root. This way we can check to see if the actual requested root
// object lies within the session in question.

auto true_root_object_kind = m_root_object_kind;
m_root_object_kind = ObjectKind::kSession;
find_candidate_objects();

auto it_session = std::ranges::find_if(m_all_objects, [&](const ConfigObject& obj) { return obj.UID() == m_session_name; });

find_objects_and_connections(*it_session);

if (!m_objects_for_graph.contains(root_obj_uid)) {
std::stringstream errmsg;
errmsg << "Unable to find requested object \"" << root_obj_uid << "\" in session \"" << m_session_name << "\"";
throw appmodel::GeneralGraphToolError(ERS_HERE, errmsg.str());
}

// Since we used our first call to find_objects_and_connections
// only as a fact-finding mission, reset the containers it filled

m_objects_for_graph.clear();
m_incoming_connections.clear();
m_outgoing_connections.clear();

m_candidate_objects.clear();

m_root_object_kind = true_root_object_kind;
find_candidate_objects();

bool found = false;
Expand All @@ -180,11 +221,7 @@ namespace appmodel {
}
}

if (!found) {
std::stringstream errmsg;
errmsg << "Unable to find requested object \"" << root_obj_uid << "\"";
throw appmodel::GeneralGraphToolError(ERS_HERE, errmsg.str());
}
assert(found);

calculate_network_connections(); // Put differently, "find the edges between the vertices"
}
Expand Down Expand Up @@ -405,7 +442,11 @@ namespace appmodel {
m_objects_for_graph.insert( {object.UID(), starting_object} );
}

void GraphBuilder::construct_graph(const std::string& root_obj_uid) {
void GraphBuilder::construct_graph(std::string root_obj_uid) {

if (root_obj_uid == "") {
root_obj_uid = m_session_name;
}

// Next several lines just mean "tell me the class type of the root object in the config plot's graph"

Expand Down
11 changes: 7 additions & 4 deletions src/GraphBuilder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*
* GraphBuilder is the tool we can use to plot configurations. A quick overview:
*
* - Constructed from an OKS database file (XML)
* - GraphBuilder::construct_graph will take a "root object" and
* - Constructed from an OKS database file (XML) and the name of a session contained within it
* - GraphBuilder::construct_graph will take a "root object" from the session and
* construct a graph accordingly
* - GraphBuilder::write_graph will take the name of an output DOT
* file and write the graph to it
Expand Down Expand Up @@ -79,9 +79,9 @@ namespace appmodel {
const std::string displaylabel {"undefined"};
};

explicit GraphBuilder(const std::string& oksfilename);
explicit GraphBuilder(const std::string& oksfilename, const std::string& sessionname);

void construct_graph(const std::string& root_obj_uid);
void construct_graph(std::string root_obj_uid);
void write_graph(const std::string& outputfilename) const;

GraphBuilder(const GraphBuilder&) = delete;
Expand Down Expand Up @@ -120,6 +120,9 @@ namespace appmodel {
void find_candidate_objects();
[[nodiscard]] std::vector<dunedaq::conffwk::ConfigObject> find_child_objects(const ConfigObject& parent_obj);
void calculate_graph(const std::string& root_obj_uid);

// find_objects_and_connections fills m_objects_for_graph as well
// as m_incoming_connections and m_outgoing_connections

void find_objects_and_connections(const ConfigObject& object);
void calculate_network_connections();
Expand Down

0 comments on commit a89ea0f

Please sign in to comment.