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 @@ + + + + + + + + + + + + + + + + + + + + + + + +