From 786ec4ee1e81741b44d1f949464abfcc3338851e Mon Sep 17 00:00:00 2001
From: rakhimov
Date: Fri, 31 Mar 2017 13:13:37 -0700
Subject: [PATCH] Add event tree Sequence and Instruction
Only 'collect-expression' instruction is implemented
for use by Sequence.
Issue #150
---
src/event_tree.cc | 18 +++++++++-
src/event_tree.h | 37 +++++++++++++++++++--
src/initializer.cc | 32 ++++++++++++++++++
src/initializer.h | 13 ++++++--
src/model.cc | 7 ++++
src/model.h | 3 ++
src/reporter.cc | 1 +
tests/initializer_tests.cc | 4 ++-
tests/input/eta/doubly_defined_sequence.xml | 24 +++++++++++++
9 files changed, 132 insertions(+), 7 deletions(-)
create mode 100644 tests/input/eta/doubly_defined_sequence.xml
diff --git a/src/event_tree.cc b/src/event_tree.cc
index 3bf685cb7f..1a242d5511 100644
--- a/src/event_tree.cc
+++ b/src/event_tree.cc
@@ -20,6 +20,8 @@
#include "event_tree.h"
+#include "error.h"
+
namespace scram {
namespace mef {
@@ -28,7 +30,21 @@ Instruction::~Instruction() = default;
CollectExpression::CollectExpression(const ExpressionPtr& expression)
: expression_(expression) {}
-EventTree::EventTree(std::string name) : Element(std::move(name)) {}
+void Sequence::instructions(InstructionContainer instructions) {
+ if (instructions.empty()) {
+ throw LogicError("Sequence " + Element::name() +
+ " requires at least one instruction");
+ }
+ instructions_ = std::move(instructions);
+}
+
+void EventTree::Add(SequencePtr sequence) {
+ if (sequences_.count(sequence->name())) {
+ throw ValidationError("Duplicate sequence " + sequence->name() +
+ " in event tree " + Element::name());
+ }
+ sequences_.insert(std::move(sequence));
+}
} // namespace mef
} // namespace scram
diff --git a/src/event_tree.h b/src/event_tree.h
index e6b67dc34b..c188efbb3f 100644
--- a/src/event_tree.h
+++ b/src/event_tree.h
@@ -23,6 +23,7 @@
#include
#include
+#include
#include
@@ -38,6 +39,12 @@ class Instruction : private boost::noncopyable {
virtual ~Instruction() = 0;
};
+/// Instructions are assumed not to be shared.
+using InstructionPtr = std::unique_ptr;
+
+/// A collection of instructions.
+using InstructionContainer = std::vector;
+
/// The operation of collecting expressions for event tree sequences.
class CollectExpression : public Instruction {
public:
@@ -49,13 +56,37 @@ class CollectExpression : public Instruction {
ExpressionPtr expression_; ///< The probability expression to multiply.
};
+/// Representation of sequences in event trees.
+class Sequence : public Element {
+ public:
+ using Element::Element;
+
+ /// @param[in] instructions One or more instructions for the sequence.
+ ///
+ /// @throws LogicError The instructions are empty.
+ void instructions(InstructionContainer instructions);
+
+ private:
+ /// Instructions to execute with the sequence.
+ InstructionContainer instructions_;
+};
+
+/// Sequences are defined in event trees but referenced in other constructs.
+using SequencePtr = std::shared_ptr;
+
/// Event Tree representation with MEF constructs.
class EventTree : public Element, private boost::noncopyable {
public:
- /// @param[in] name A unique name for the event tree within the model.
+ using Element::Element;
+
+ /// @param[in] sequence A unique sequence defined in this event tree.
///
- /// @throws InvalidArgument The name is malformed.
- explicit EventTree(std::string name);
+ /// @throws ValidationError The sequence is already defined.
+ void Add(SequencePtr sequence);
+
+ private:
+ /// Unique sequences defined in this event tree.
+ ElementTable sequences_;
};
using EventTreePtr = std::unique_ptr; ///< Unique trees in models.
diff --git a/src/initializer.cc b/src/initializer.cc
index 4726219968..c1effdd586 100644
--- a/src/initializer.cc
+++ b/src/initializer.cc
@@ -308,6 +308,16 @@ CcfGroupPtr Initializer::Register(const xmlpp::Element* ccf_node,
tbd_.emplace_back(ccf_group.get(), ccf_node);
return ccf_group;
}
+
+template <>
+SequencePtr Initializer::Register(const xmlpp::Element* xml_node,
+ const std::string& /*base_path*/,
+ RoleSpecifier /*container_role*/) {
+ SequencePtr sequence = ConstructElement(xml_node);
+ Register(sequence, xml_node);
+ tbd_.emplace_back(sequence.get(), xml_node);
+ return sequence;
+}
/// @}
void Initializer::ProcessInputFile(const std::string& xml_file) {
@@ -414,6 +424,17 @@ void Initializer::Define(const xmlpp::Element* ccf_node, CcfGroup* ccf_group) {
}
}
}
+
+template <>
+void Initializer::Define(const xmlpp::Element* xml_node, Sequence* sequence) {
+ xmlpp::NodeSet xml_instructions =
+ xml_node->find("./*[name() != 'attributes' and name() != 'label']");
+ InstructionContainer instructions;
+ for (const xmlpp::Node* xml_instruction : xml_instructions) {
+ instructions.emplace_back(GetInstruction(XmlElement(xml_instruction)));
+ }
+ sequence->instructions(std::move(instructions));
+}
/// @}
void Initializer::ProcessTbdElements() {
@@ -436,6 +457,10 @@ void Initializer::ProcessTbdElements() {
void Initializer::DefineEventTree(const xmlpp::Element* et_node) {
EventTreePtr event_tree = ConstructElement(et_node);
+ for (const xmlpp::Node* node : et_node->find("./define-sequence")) {
+ event_tree->Add(Register(XmlElement(node), event_tree->name(),
+ RoleSpecifier::kPublic));
+ }
Register(std::move(event_tree), et_node);
}
@@ -578,6 +603,13 @@ void Initializer::ProcessFormula(const xmlpp::Element* formula_node,
}
}
+InstructionPtr Initializer::GetInstruction(const xmlpp::Element* xml_element) {
+ assert(xml_element->get_name() == "collect-expression");
+ const xmlpp::Element* arg_element =
+ XmlElement(xml_element->find("./*").front());
+ return std::make_unique(GetExpression(arg_element, ""));
+}
+
template
struct Initializer::Extractor {
/// Extracts and accumulates expressions
diff --git a/src/initializer.h b/src/initializer.h
index 98ec20120d..2af3b49719 100644
--- a/src/initializer.h
+++ b/src/initializer.h
@@ -264,12 +264,21 @@ class Initializer : private boost::noncopyable {
const std::string& base_path,
Formula* formula);
+ /// Processes Instruction definitions.
+ ///
+ /// @param[in] xml_element The XML element with instruction definitions.
+ ///
+ /// @returns The newly defined instruction.
+ ///
+ /// @throws ValidationError Errors in instruction definitions.
+ InstructionPtr GetInstruction(const xmlpp::Element* xml_element);
+
/// Processes Expression definitions in input file.
///
/// @param[in] expr_element XML expression element containing the definition.
/// @param[in] base_path Series of ancestor containers in the path with dots.
///
- /// @returns Pointer to the newly defined or registered expression.
+ /// @returns The newly defined or registered expression.
///
/// @throws ValidationError There are problems with getting the expression.
ExpressionPtr GetExpression(const xmlpp::Element* expr_element,
@@ -363,7 +372,7 @@ class Initializer : private boost::noncopyable {
/// CCF groups rely on both parameter and basic event registrations.
///
/// Elements are assumed to be unique.
- TbdContainer tbd_;
+ TbdContainer tbd_;
/// Container of defined expressions for later validation due to cycles.
std::vector> expressions_;
diff --git a/src/model.cc b/src/model.cc
index bbef311564..a5cad7b526 100644
--- a/src/model.cc
+++ b/src/model.cc
@@ -39,6 +39,13 @@ void Model::Add(EventTreePtr event_tree) {
event_trees_.insert(std::move(event_tree));
}
+void Model::Add(const SequencePtr& sequence) {
+ if (sequences_.count(sequence->name())) {
+ throw RedefinitionError("Redefinition of sequence " + sequence->name());
+ }
+ sequences_.insert(std::move(sequence));
+}
+
void Model::Add(FaultTreePtr fault_tree) {
if (fault_trees_.count(fault_tree->name())) {
throw RedefinitionError("Redefinition of fault tree " + fault_tree->name());
diff --git a/src/model.h b/src/model.h
index 9ba35db2e5..63d3673152 100644
--- a/src/model.h
+++ b/src/model.h
@@ -60,6 +60,7 @@ class Model : public Element, private boost::noncopyable {
/// @returns Defined constructs in the model.
/// @{
const ElementTable& event_trees() const { return event_trees_; }
+ const ElementTable& sequences() const { return sequences_; }
const ElementTable& fault_trees() const { return fault_trees_; }
const IdTable& parameters() const {
return parameters_.entities_by_id;
@@ -85,6 +86,7 @@ class Model : public Element, private boost::noncopyable {
///
/// @{
void Add(EventTreePtr element);
+ void Add(const SequencePtr& element);
void Add(FaultTreePtr element);
void Add(const ParameterPtr& element);
void Add(const HouseEventPtr& element);
@@ -182,6 +184,7 @@ class Model : public Element, private boost::noncopyable {
/// A collection of defined constructs in the model.
/// @{
ElementTable event_trees_;
+ ElementTable sequences_;
ElementTable fault_trees_;
LookupTable gates_;
LookupTable house_events_;
diff --git a/src/reporter.cc b/src/reporter.cc
index 3c2d11fceb..f24d972ef3 100644
--- a/src/reporter.cc
+++ b/src/reporter.cc
@@ -229,6 +229,7 @@ void Reporter::ReportModelFeatures(const mef::Model& model,
feature("house-events", model.house_events());
feature("ccf-groups", model.ccf_groups());
feature("fault-trees", model.fault_trees());
+ feature("event-trees", model.event_trees());
}
void Reporter::ReportPerformance(const core::RiskAnalysis& risk_an,
diff --git a/tests/initializer_tests.cc b/tests/initializer_tests.cc
index ad9d2a9097..9b77286512 100644
--- a/tests/initializer_tests.cc
+++ b/tests/initializer_tests.cc
@@ -93,7 +93,9 @@ TEST(InitializerTest, CorrectEtaInputs) {
TEST(InitializerTest, IncorrectEtaInputs) {
std::string dir = "./share/scram/input/eta/";
- const char* incorrect_inputs[] = {"doubly_defined_event_tree.xml"};
+ const char* incorrect_inputs[] = {
+ "doubly_defined_event_tree.xml", "doubly_defined_sequence.xml",
+ };
for (const auto& input : incorrect_inputs) {
EXPECT_THROW(Initializer({dir + input}, core::Settings()), ValidationError)
<< " Filename: " << input;
diff --git a/tests/input/eta/doubly_defined_sequence.xml b/tests/input/eta/doubly_defined_sequence.xml
new file mode 100644
index 0000000000..7cac04a6b9
--- /dev/null
+++ b/tests/input/eta/doubly_defined_sequence.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+