Added from_to sequence diagram generator for json

This commit is contained in:
Bartek Kryza
2023-08-27 19:03:25 +02:00
parent ae55b7c054
commit bd19fe5bfb
5 changed files with 224 additions and 155 deletions

View File

@@ -609,6 +609,52 @@ void generator::generate_diagram(nlohmann::json &parent) const
} }
} }
for (const auto &ft : config().from_to()) {
// First, find the sequence of activities from 'from' location
// to 'to' location
assert(ft.size() == 2);
const auto &from_location = ft.front();
const auto &to_location = ft.back();
auto [from_activity_id, to_activity_id] =
model().get_from_to_activity_ids(from_location, to_location);
if (from_activity_id == 0 || to_activity_id == 0)
continue;
auto message_chains_unique = model().get_all_from_to_message_chains(
from_activity_id, to_activity_id);
nlohmann::json sequence;
sequence["from_to"]["from"]["location"] = from_location.location;
sequence["from_to"]["from"]["id"] = from_activity_id;
sequence["from_to"]["to"]["location"] = to_location.location;
sequence["from_to"]["to"]["id"] = to_activity_id;
block_statements_stack_.push_back(std::ref(sequence));
sequence["message_chains"] = nlohmann::json::array();
for (const auto &mc : message_chains_unique) {
nlohmann::json message_chain;
block_statements_stack_.push_back(std::ref(message_chain));
for (const auto &m : mc) {
generate_call(m, current_block_statement());
}
block_statements_stack_.pop_back();
sequence["message_chains"].push_back(std::move(message_chain));
}
block_statements_stack_.pop_back();
json_["sequences"].push_back(std::move(sequence));
}
for (const auto &sf : config().start_from()) { for (const auto &sf : config().start_from()) {
if (sf.location_type == location_t::function) { if (sf.location_type == location_t::function) {
common::model::diagram_element::id_t start_from{0}; common::model::diagram_element::id_t start_from{0};

View File

@@ -416,8 +416,14 @@ void generator::generate_diagram(std::ostream &ostr) const
const auto &from_location = ft.front(); const auto &from_location = ft.front();
const auto &to_location = ft.back(); const auto &to_location = ft.back();
auto message_chains_unique = auto [from_activity_id, to_activity_id] =
model().get_all_from_to_message_chains(from_location, to_location); model().get_from_to_activity_ids(from_location, to_location);
if (from_activity_id == 0 || to_activity_id == 0)
continue;
auto message_chains_unique = model().get_all_from_to_message_chains(
from_activity_id, to_activity_id);
bool first_separator_skipped{false}; bool first_separator_skipped{false};
for (const auto &mc : message_chains_unique) { for (const auto &mc : message_chains_unique) {

View File

@@ -209,158 +209,162 @@ std::vector<std::string> diagram::list_start_from_values() const
return result; return result;
} }
std::unordered_set<message_chain_t> diagram::get_all_from_to_message_chains( std::pair<common::model::diagram_element::id_t,
const config::source_location &from_location, common::model::diagram_element::id_t>
diagram::get_from_to_activity_ids(const config::source_location &from_location,
const config::source_location &to_location) const const config::source_location &to_location) const
{
common::model::diagram_element::id_t from_activity{0};
common::model::diagram_element::id_t to_activity{0};
for (const auto &[k, v] : sequences()) {
const auto &caller = *participants().at(v.from());
std::string vfrom = caller.full_name(false);
if (vfrom == from_location.location) {
LOG_DBG("Found sequence diagram start point '{}': {}", vfrom, k);
from_activity = k;
break;
}
}
if (from_activity == 0) {
LOG_WARN("Failed to find 'from' participant {} for start_from "
"condition",
from_location.location);
return {from_activity, to_activity};
}
for (const auto &[k, v] : sequences()) {
for (const auto &m : v.messages()) {
if (m.type() != common::model::message_t::kCall)
continue;
const auto &callee = *participants().at(m.to());
std::string vto = callee.full_name(false);
if (vto == to_location.location) {
LOG_DBG(
"Found sequence diagram end point '{}': {}", vto, m.to());
to_activity = m.to();
break;
}
}
}
if (to_activity == 0) {
LOG_WARN("Failed to find 'to' participant {} for from_to "
"condition",
to_location.location);
return {from_activity, to_activity};
}
return {from_activity, to_activity};
}
std::unordered_set<message_chain_t> diagram::get_all_from_to_message_chains(
const common::model::diagram_element::id_t from_activity,
const common::model::diagram_element::id_t to_activity) const
{ {
std::unordered_set<message_chain_t> message_chains_unique{}; std::unordered_set<message_chain_t> message_chains_unique{};
if ((from_location.location_type == config::location_t::function) && // Message (call) chains matching the specified from_to condition
(to_location.location_type == config::location_t::function)) { std::vector<message_chain_t> message_chains;
common::model::diagram_element::id_t from_activity{0};
common::model::diagram_element::id_t to_activity{0};
for (const auto &[k, v] : sequences()) { // First find all 'to_activity' call targets in the sequences, i.e.
const auto &caller = *participants().at(v.from()); // all messages pointing to the final 'to_activity' activity
std::string vfrom = caller.full_name(false); for (const auto &[k, v] : sequences()) {
if (vfrom == from_location.location) { for (const auto &m : v.messages()) {
LOG_DBG( if (m.type() != common::model::message_t::kCall)
"Found sequence diagram start point '{}': {}", vfrom, k); continue;
from_activity = k;
break; if (m.to() == to_activity) {
message_chains.push_back(message_chain_t{});
message_chains.back().push_back(m);
} }
} }
if (from_activity == 0) {
LOG_WARN("Failed to find 'from' participant {} for start_from "
"condition",
from_location.location);
return {};
}
for (const auto &[k, v] : sequences()) {
for (const auto &m : v.messages()) {
if (m.type() != common::model::message_t::kCall)
continue;
const auto &callee = *participants().at(m.to());
std::string vto = callee.full_name(false);
if (vto == to_location.location) {
LOG_DBG("Found sequence diagram end point '{}': {}", vto,
m.to());
to_activity = m.to();
break;
}
}
}
if (to_activity == 0) {
LOG_WARN("Failed to find 'to' participant {} for from_to "
"condition",
to_location.location);
return {};
}
// Message (call) chains matching the specified from_to condition
std::vector<message_chain_t> message_chains;
// First find all 'to_activity' call targets in the sequences, i.e.
// all messages pointing to the final 'to_activity' activity
for (const auto &[k, v] : sequences()) {
for (const auto &m : v.messages()) {
if (m.type() != common::model::message_t::kCall)
continue;
if (m.to() == to_activity) {
message_chains.push_back(message_chain_t{});
message_chains.back().push_back(m);
}
}
}
std::map<int, std::vector<model::message>> calls_to_current_chain;
std::map<int, message_chain_t> current_chain;
int iter = 0;
while (true) {
bool added_message_to_some_chain{false};
// If target of current message matches any of the
// 'from' constraints in the last messages in
// current chains found on previous iteration - append
if (!calls_to_current_chain.empty()) {
for (auto &[message_chain_index, messages] :
calls_to_current_chain) {
for (auto i = 0U; i < messages.size(); i++) {
message_chains.push_back(
current_chain[message_chain_index]);
message_chains.back().push_back(std::move(messages[i]));
}
}
calls_to_current_chain.clear();
}
LOG_TRACE("Message chains after iteration {}", iter++);
int message_chain_index{};
for (const auto &mc : message_chains) {
LOG_TRACE("\t{}: {}", message_chain_index++,
fmt::join(util::map<std::string>(mc,
[](const model::message &m) -> std::string {
return m.message_name();
}),
"<-"));
}
for (auto i = 0U; i < message_chains.size(); i++) {
auto &mc = message_chains[i];
current_chain[i] = mc;
for (const auto &[k, v] : sequences()) {
for (const auto &m : v.messages()) {
if (m.type() != common::model::message_t::kCall)
continue;
// Ignore recursive calls and call loops
if (m.to() == m.from() ||
std::any_of(
cbegin(mc), cend(mc), [&m](const auto &msg) {
return msg.to() == m.from();
})) {
continue;
}
if (m.to() == mc.back().from()) {
calls_to_current_chain[i].push_back(m);
added_message_to_some_chain = true;
}
}
}
// If there are more than one call to the current chain,
// duplicate it as many times as there are calls - 1
if (calls_to_current_chain.count(i) > 0 &&
calls_to_current_chain[i].size() >= 1) {
mc.push_back(calls_to_current_chain[i][0]);
calls_to_current_chain[i].erase(
calls_to_current_chain[i].begin());
}
}
// There is nothing more to find
if (!added_message_to_some_chain)
break;
}
// Reverse the message chains order (they were added starting from
// the destination activity)
for (auto &mc : message_chains)
std::reverse(mc.begin(), mc.end());
std::copy_if(message_chains.begin(), message_chains.end(),
std::inserter(message_chains_unique, message_chains_unique.begin()),
[from_activity](const message_chain_t &mc) {
return !mc.empty() && (mc.front().from() == from_activity);
});
} }
std::map<int, std::vector<model::message>> calls_to_current_chain;
std::map<int, message_chain_t> current_chain;
int iter = 0;
while (true) {
bool added_message_to_some_chain{false};
// If target of current message matches any of the
// 'from' constraints in the last messages in
// current chains found on previous iteration - append
if (!calls_to_current_chain.empty()) {
for (auto &[message_chain_index, messages] :
calls_to_current_chain) {
for (auto i = 0U; i < messages.size(); i++) {
message_chains.push_back(
current_chain[message_chain_index]);
message_chains.back().push_back(std::move(messages[i]));
}
}
calls_to_current_chain.clear();
}
LOG_TRACE("Message chains after iteration {}", iter++);
int message_chain_index{};
for (const auto &mc : message_chains) {
LOG_TRACE("\t{}: {}", message_chain_index++,
fmt::join(util::map<std::string>(mc,
[](const model::message &m) -> std::string {
return m.message_name();
}),
"<-"));
}
for (auto i = 0U; i < message_chains.size(); i++) {
auto &mc = message_chains[i];
current_chain[i] = mc;
for (const auto &[k, v] : sequences()) {
for (const auto &m : v.messages()) {
if (m.type() != common::model::message_t::kCall)
continue;
// Ignore recursive calls and call loops
if (m.to() == m.from() ||
std::any_of(
cbegin(mc), cend(mc), [&m](const auto &msg) {
return msg.to() == m.from();
})) {
continue;
}
if (m.to() == mc.back().from()) {
calls_to_current_chain[i].push_back(m);
added_message_to_some_chain = true;
}
}
}
// If there are more than one call to the current chain,
// duplicate it as many times as there are calls - 1
if (calls_to_current_chain.count(i) > 0 &&
calls_to_current_chain[i].size() >= 1) {
mc.push_back(calls_to_current_chain[i][0]);
calls_to_current_chain[i].erase(
calls_to_current_chain[i].begin());
}
}
// There is nothing more to find
if (!added_message_to_some_chain)
break;
}
// Reverse the message chains order (they were added starting from
// the destination activity)
for (auto &mc : message_chains)
std::reverse(mc.begin(), mc.end());
std::copy_if(message_chains.begin(), message_chains.end(),
std::inserter(message_chains_unique, message_chains_unique.begin()),
[from_activity](const message_chain_t &mc) {
return !mc.empty() && (mc.front().from() == from_activity);
});
return message_chains_unique; return message_chains_unique;
} }

View File

@@ -241,11 +241,25 @@ public:
/** /**
* @brief Generate a list of message chains matching a from_to constraint * @brief Generate a list of message chains matching a from_to constraint
* *
* @param from_activity Source activity for from_to message chain
* @param to_activity Target activity for from_to message chain
* @return List of message chains * @return List of message chains
*/ */
std::unordered_set<message_chain_t> get_all_from_to_message_chains( std::unordered_set<message_chain_t> get_all_from_to_message_chains(
const config::source_location &from, const common::model::diagram_element::id_t from_activity,
const config::source_location &to) const; const common::model::diagram_element::id_t to_activity) const;
/**
* @brief Get ids of from and to activities in from_to constraint
*
* @param from_activity Source activity for from_to message chain
* @param to_activity Target activity for from_to message chain
* @return Pair of activity ids (0 if not found)
*/
std::pair<common::model::diagram_element::id_t,
common::model::diagram_element::id_t>
get_from_to_activity_ids(const config::source_location &from_activity,
const config::source_location &to_activity) const;
/** /**
* @brief Once the diagram is complete, run any final processing. * @brief Once the diagram is complete, run any final processing.

View File

@@ -51,12 +51,11 @@ TEST_CASE("t20034", "[test-case][sequence]")
config.output_directory() + "/" + diagram->name + ".puml", puml); config.output_directory() + "/" + diagram->name + ".puml", puml);
} }
// { {
// auto j = generate_sequence_json(diagram, *model); auto j = generate_sequence_json(diagram, *model);
//
// using namespace json; using namespace json;
//
// save_json(config.output_directory() + "/" + diagram->name + save_json(config.output_directory() + "/" + diagram->name + ".json", j);
// ".json", j); }
// }
} }