1 // Copyright (C) 2006-2019 CEA/DEN, EDF R&D
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 // Lesser General Public License for more details.
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 #include "DynParaLoop.hxx"
22 #include "InputPort.hxx"
23 #include "OutputPort.hxx"
24 #include "InPropertyPort.hxx"
25 #include "ComposedNode.hxx"
26 #include "Dispatcher.hxx"
27 #include "InputDataStreamPort.hxx"
28 #include "OutputDataStreamPort.hxx"
32 #include "YacsTrace.hxx"
34 using namespace YACS::ENGINE;
37 /*! \class YACS::ENGINE::Node
38 * \brief Base class for all nodes
43 const char Node::SEP_CHAR_IN_PORT[]=".";
46 std::map<int,Node*> Node::idMap;
48 NodeStateNameMap::NodeStateNameMap()
50 insert(make_pair(YACS::READY, "READY"));
51 insert(make_pair(YACS::TOLOAD, "TOLOAD"));
52 insert(make_pair(YACS::LOADED, "LOADED"));
53 insert(make_pair(YACS::TOACTIVATE, "TOACTIVATE"));
54 insert(make_pair(YACS::ACTIVATED, "ACTIVATED"));
55 insert(make_pair(YACS::DESACTIVATED, "DESACTIVATED"));
56 insert(make_pair(YACS::DONE, "DONE"));
57 insert(make_pair(YACS::SUSPENDED, "SUSPENDED"));
58 insert(make_pair(YACS::LOADFAILED, "LOADFAILED"));
59 insert(make_pair(YACS::EXECFAILED, "EXECFAILED"));
60 insert(make_pair(YACS::PAUSE, "PAUSE"));
61 insert(make_pair(YACS::INTERNALERR, "INTERNALERR"));
62 insert(make_pair(YACS::DISABLED, "DISABLED"));
63 insert(make_pair(YACS::FAILED, "FAILED"));
64 insert(make_pair(YACS::ERROR, "ERROR"));
68 Node::Node(const std::string& name):_name(name),_inGate(this),_outGate(this),_father(0),_state(YACS::READY),
69 _implementation(Runtime::RUNTIME_ENGINE_INTERACTION_IMPL_NAME),_modified(1)
71 // Should be protected by lock ??
72 Node::checkValidityOfNodeName(_name);
76 // Every node has an InPropertyPort
77 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
80 Node::Node(const Node& other, ComposedNode *father):_inGate(this),_outGate(this),_name(other._name),_father(father),
81 _state(YACS::READY),_implementation(other._implementation),
82 _propertyMap(other._propertyMap),_modified(1)
87 // Every node has an InPropertyPort
88 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
93 delete _inPropertyPort;
97 * initialisation of all input and output ports and gates, for execution
100 void Node::init(bool start)
104 if(_state == YACS::DISABLED)
106 exDisabledState(); // to refresh propagation of DISABLED state
109 setState(YACS::READY);
113 * This method clones \a this by :
115 * - deep copying nodes, links, ports, types
116 * - containers are either deep copied or shallow copied depending on _isAttachedOnCloning attribute.
117 * - component are either deep copied or shallow copied depending on _isAttachedOnCloning attribute.
119 * So \b this \b method \b clone \b is \b dedicated \b for \b DynParaLoop \b class \b or \b subclasses.
120 * It \b should \b not \b be \b used \b elsewhere, because
121 * _isAttachedOnCloning attribute is an attribute in the engine not for GUI/TUI aspects.
122 * For GUI/TUI manipulation cloneWithoutCompAndContDeepCpy method should be used preferably.
124 * \param [in] father - The new father of the returned clone.
125 * \param [in] editionOnly ignored
127 * \sa cloneWithoutCompAndContDeepCpy
129 Node *Node::clone(ComposedNode *father, bool editionOnly) const
131 Node *ret(simpleClone(father,editionOnly));
132 ret->performDuplicationOfPlacement(*this);
137 * This method clones \a this by :
138 * - deep copying nodes, links, ports, types
139 * - shallow copy containers
140 * - shallow copy components
142 * So this method simply ignores isAttachedOnCloning attribute for both containers and components.
143 * So this method is dedicated for the GUI/TUI users.
145 * \param [in] father - The new father of the returned clone.
146 * \param [in] editionOnly ignored
148 Node *Node::cloneWithoutCompAndContDeepCpy(ComposedNode *father, bool editionOnly) const
150 Node *ret(simpleClone(father,editionOnly));
151 ret->performShallowDuplicationOfPlacement(*this);
155 //! Change the name of the node
157 * raise an exception if the name is already used in the scope of its father
158 * \param name : the new name
160 void Node::setName(const std::string& name)
162 Node::checkValidityOfNodeName(name);
165 if(_father->isNameAlreadyUsed(name))
167 if ( _father->getChildByName(name) != this )
169 std::string what("Name ");
171 what+=" already exists in the scope of "; what+=_father->getName();
172 throw Exception(what);
180 * get the set of all nodes connected to the outGate
183 list<Node *> Node::getOutNodes() const
186 list<InGate *> inGates=_outGate.edSetInGate();
187 for(list<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
188 ret.push_back((*iter)->getNode());
192 bool Node::exIsControlReady() const
194 return _inGate.exIsReady();
197 //! Update the node state
199 * \note : Update the '_state' attribute.
200 * Typically called by 'this->_inGate' when 'this->_inGate' is ready.
202 * Called by InGate::exNotifyFromPrecursor
204 void Node::exUpdateState()
206 if(_state==YACS::DISABLED)return;
207 if(_inGate.exIsReady())
208 setState(YACS::TOACTIVATE);
211 //! Notify this node that its execution has failed
213 * The node goes in FAILED state and
214 * propagate the notification through the outGate port
217 void Node::exFailedState()
219 DEBTRACE( "Node::exFailedState: " << getName() );
220 setState(YACS::FAILED);
221 _outGate.exNotifyFailed();
224 //! Notify this node that it has been disabled
226 * The node goes in DISABLED state and
227 * propagate the notification through the outGate port
230 void Node::exDisabledState()
232 DEBTRACE( "Node::exDisabledState: " << getName() );
233 setState(YACS::DISABLED);
234 _outGate.exNotifyDisabled();
237 InPort *Node::getInPort(const std::string& name) const throw(YACS::Exception)
242 ret=getInputPort(name);
246 ret=getInputDataStreamPort(name);
252 Node::getInPropertyPort() const throw(YACS::Exception)
254 return _inPropertyPort;
258 Node::getInputPort(const std::string& name) const throw(YACS::Exception)
260 if (name == "__InPropertyPort__Node__YACS_")
261 return _inPropertyPort;
264 std::string what("Node::getInputPort : the port with name "); what+=name; what+=" does not exist on the current level";
265 throw Exception(what);
270 * \note: Contrary to getOutputPort method, this method returns the output port at highest level, possible.
271 * That is to say in some ComposedNode, like ForEachLoop or Switch, an outport inside 'this' is seen differently than the true outport.
273 OutPort *Node::getOutPort(const std::string& name) const throw(YACS::Exception)
278 ret=getOutputPort(name);
282 ret=getOutputDataStreamPort(name);
287 std::list<InPort *> Node::getSetOfInPort() const
290 list<InputPort *> data=getSetOfInputPort();
291 ret.insert(ret.end(),data.begin(),data.end());
292 list<InputDataStreamPort *> ds=getSetOfInputDataStreamPort();
293 ret.insert(ret.end(),ds.begin(),ds.end());
297 std::list<OutPort *> Node::getSetOfOutPort() const
300 list<OutputPort *> data=getSetOfOutputPort();
301 ret.insert(ret.end(),data.begin(),data.end());
302 list<OutputDataStreamPort *> ds=getSetOfOutputDataStreamPort();
303 ret.insert(ret.end(),ds.begin(),ds.end());
308 * gets a set of the composed nodes that constitute the ascendancy of this node, starting from root
309 * or from a particular ancestor
310 * \b WARNING : returned set is not sorted !
311 * @param levelToStop composed node which is the oldest ancestor required
312 * @return ascendancy, direct father first in set.
315 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
317 list<ComposedNode *> ret;
318 if(this==levelToStop)
320 for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
325 bool Node::operator>(const Node& other) const
327 const ComposedNode *iter=other._father;
328 while(iter!=0 && iter!=this)
333 bool Node::operator<(const Node& other) const
335 const ComposedNode *iter=_father;
336 while(iter!=0 && iter!=(&other))
338 return iter==(&other);
342 * @return Implementation of node: C++, Python, CORBA...
343 * _implementation is set by a derived class in a Runtime
344 * it normally applies only to elementaryNodes and it is used by Ports to select Data Converters.
345 * Potential problem with Ports attached to composed Nodes...
348 string Node::getImplementation() const
350 return _implementation;
353 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
354 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
356 set<InputPort *> setOfUnitializedInputPort;
357 list<InputPort *> allOfInputPorts=getSetOfInputPort();
358 for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
360 if ( ! (*iter)->edIsInitialized() )
361 setOfUnitializedInputPort.insert(*iter);
363 return setOfUnitializedInputPort;
366 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
367 bool Node::edAreAllInputPortInitialized() const
369 set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
370 return ( setOfUnitializedInputPort.size() == 0);
374 * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
376 void Node::exForwardFailed()
378 _outGate.exNotifyFailed();
382 * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
384 void Node::exForwardFinished()
386 DEBTRACE("Node::exForwardFinished");
387 _outGate.exNotifyDone();
391 * Called typically by ComposedNode to correctly update DF/CF/DS links
393 void Node::edDisconnectAllLinksWithMe()
395 _inGate.edDisconnectAllLinksToMe();
396 _outGate.edDisconnectAllLinksFromMe();
399 Proc *Node::getProc()
403 return _father->getProc();
406 const Proc * Node::getProc() const
410 return _father->getProc();
413 DynParaLoop *Node::getClosestDPLAmongAncestors() const
417 ComposedNode *iter(_father);
420 DynParaLoop *iter2(dynamic_cast<DynParaLoop *>(iter));
429 ComposedNode *Node::getRootNode() const throw(YACS::Exception)
432 throw Exception("No root node");
433 ComposedNode *iter=_father;
436 return (ComposedNode *)iter;
440 * checks validity of ports name, that must not contain a particular character '?'
441 * USAGE NOT CLEAR, not used so far, when are those characters set ?
444 void Node::checkValidityOfPortName(const std::string& name) throw(YACS::Exception)
446 if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
448 string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
449 throw Exception(what);
453 void Node::checkValidityOfNodeName(const std::string& name)
455 if(name.find(ComposedNode::SEP_CHAR_BTW_LEVEL,0)!=string::npos)
457 string what("Node name "); what+=name; what+="not valid because it contains character "; what+=ComposedNode::SEP_CHAR_BTW_LEVEL;
458 throw Exception(what);
463 * @note : Check that 'node1' and 'node2' have exactly the same father
464 * @exception : If 'node1' and 'node2' have NOT exactly the same father
466 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2) throw(YACS::Exception)
468 if(node1!=0 && node2!=0)
470 if(node1->_father==node2->_father)
471 return node1->_father;
473 throw Exception("check failed : nodes have not the same father");
476 const std::string Node::getId() const
478 std::string id=getRootNode()->getName();
479 if(getRootNode() != this)
480 id= id+'.'+ getRootNode()->getChildName(this);
481 string::size_type debut =id.find_first_of('.');
482 while(debut != std::string::npos){
484 debut=id.find_first_of('.',debut);
489 void Node::setProperty(const std::string& name, const std::string& value)
491 DEBTRACE("Node::setProperty " << name << " " << value);
492 _propertyMap[name]=value;
495 std::string Node::getProperty(const std::string& name)
497 std::map<std::string,std::string>::iterator it=_propertyMap.find(name);
499 if(it != _propertyMap.end())
502 return _father->getProperty(name);
507 std::map<std::string,std::string> Node::getProperties()
509 std::map<std::string,std::string> amap=_propertyMap;
512 std::map<std::string,std::string> fatherMap=_father->getProperties();
513 amap.insert(fatherMap.begin(),fatherMap.end());
519 void Node::setProperties(std::map<std::string,std::string> properties)
521 _propertyMap.clear();
522 _propertyMap=properties;
525 //! Return the node state in the context of its father
527 * \return the effective node state
529 * The node state is stored in a private attribute _state.
530 * This state is relative to its father state : a node with a
531 * TOACTIVATE state with a father node in a READY state is not
532 * to activate. Its effective state is only READY.
533 * This method returns the effective state of the node taking
534 * into account that of its father.
536 YACS::StatesForNode Node::getEffectiveState() const
538 if(!_father) //the root node
540 if(_state==YACS::DISABLED)
541 return YACS::DISABLED;
542 return _father->getEffectiveState(this);
545 //! Return the effective state of a node in the context of this one (its father)
547 * \param node: the node which effective state is queried
548 * \return the effective node state
550 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
552 if(node->getState()==YACS::DISABLED)
553 return YACS::DISABLED;
555 YACS::StatesForNode effectiveState=getEffectiveState();
556 switch(effectiveState)
560 case YACS::TOACTIVATE:
563 return YACS::DISABLED;
567 return node->getState();
571 //! Return the color associated to a state
573 * \param state : the node state
574 * \return the associated color
576 std::string Node::getColorState(YACS::StatesForNode state) const
586 case YACS::TOACTIVATE:
588 case YACS::ACTIVATED:
605 //! Dump to the input stream a dot representation of the node
607 * \param os : the input stream
609 void Node::writeDot(std::ostream &os) const
611 os << getId() << "[fillcolor=\"" ;
612 YACS::StatesForNode state=getEffectiveState();
613 os << getColorState(state);
614 os << "\" label=\"" << getImplementation() << "Node:" ;
615 os << getQualifiedName() <<"\"];\n";
618 //! same as Node::getName() in most cases, but differs for children of switch
620 * used by writeDot to distinguish children of switch, by adding a prefix to the name.
621 * prefix is built on case id.
624 std::string Node::getQualifiedName() const
627 return _father->getMyQualifiedName(this);
631 //! return node instance identifiant, unique for each node instance
633 * node instance identifiant is used to check if to nodes pointers refers to the same instance
640 //! Sets the given state for node.
641 /*! It is strongly recommended to use this function if you want to
642 * change the state of the node, instead of direct access to _state field (_state = ...).
644 void Node::setState(YACS::StatesForNode theState)
646 DEBTRACE("Node::setState: " << getName() << " " << theState);
648 // emit notification to all observers registered with the dispatcher on any change of the node's state
652 std::vector<std::pair<std::string,int> > Node::getDPLScopeInfo(ComposedNode *gfn)
654 std::vector< std::pair<std::string,int> > ret;
656 ComposedNode *work(getFather());
657 while(work!=gfn && work!=0)
659 DynParaLoop *workc(dynamic_cast<DynParaLoop *>(work));
662 std::pair<std::string,int> p(gfn->getChildName(workc),workc->getBranchIDOfNode(work2));
666 work=work->getFather();
672 * Method called by the Executor only if the executor is sensitive of scope of DynParaLoop.
673 * This method is virtual and empty because by default nothing is done.
675 void Node::applyDPLScope(ComposedNode *gfn)
679 //! emit notification to all observers registered with the dispatcher
681 * The dispatcher is unique and can be obtained by getDispatcher()
683 void Node::sendEvent(const std::string& event)
685 DEBTRACE("Node::sendEvent " << event);
686 Dispatcher* disp=Dispatcher::getDispatcher();
687 disp->dispatch(this,event);
690 //! emit notification to all observers registered with the dispatcher
692 * The dispatcher is unique and can be obtained by getDispatcher()
694 void Node::sendEvent2(const std::string& event, void *something)
696 Dispatcher* disp=Dispatcher::getDispatcher();
697 disp->dispatch2(this,event,something);
701 * For use only when loading a previously saved execution
704 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
706 node->setState(state);
709 //! indicates if the node is valid (returns 1) or not (returns 0)
711 * This method is useful when editing a schema. It has no meaning in execution.
712 * When a node is edited, its modified method must be called so when isValid is called, its state
713 * is updated (call to edUpdateState) before returning the validity check
719 if(_state > YACS::INVALID)
725 //! update the status of the node
727 * Only useful when editing a schema
728 * Do nothing in base Node : to implement in derived classes
730 void Node::edUpdateState()
732 DEBTRACE("Node::edUpdateState(): " << _modified);
736 //! returns a string that contains an error report if the node is in error
740 std::string Node::getErrorReport()
742 if(getState()==YACS::DISABLED)
743 return "<error node= "+getName()+ "state= DISABLED/>\n";
745 YACS::StatesForNode effectiveState=getEffectiveState();
747 DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
748 if(effectiveState != YACS::INVALID && effectiveState != YACS::ERROR &&
749 effectiveState != YACS::FAILED && effectiveState != YACS::INTERNALERR)
752 std::string report="<error node= " ;
753 report=report + getName() ;
754 switch(effectiveState)
757 report=report+" state= INVALID";
760 report=report+" state= ERROR";
763 report=report+" state= FAILED";
765 case YACS::INTERNALERR:
766 report=report+" state= INTERNALERR";
771 report=report + ">\n" ;
772 report=report+_errorDetails;
773 report=report+"\n</error>";
777 //! returns a string that contains the name of the container log file if it exists
779 * Do nothing here. To subclass
781 std::string Node::getContainerLog()
786 //! Sets Node in modified state and its father if it exists
790 void Node::modified()
792 DEBTRACE("Node::modified() " << getName());
798 //! Put this node into TOLOAD state when possible
802 void Node::ensureLoading()
804 if(_state == YACS::READY)
805 setState(YACS::TOLOAD);
808 //! Return the name of a state
812 std::string Node::getStateName(YACS::StatesForNode state)
814 static NodeStateNameMap nodeStateNameMap;
815 return nodeStateNameMap[state];
818 //! Stop all pending activities of the node
820 * This method should be called when a Proc is finished and must be deleted from the YACS server
822 void Node::shutdown(int level)
827 //! Clean the node in case of not clean exit
829 * This method should be called on a control-C or sigterm
831 void Node::cleanNodes()
835 //! Reset the node state depending on the parameter level
836 void Node::resetState(int level)
838 DEBTRACE("Node::resetState " << getName() << "," << level << "," << _state);
839 if(_state==YACS::ERROR || _state==YACS::FAILED || _state==YACS::ACTIVATED)
841 setState(YACS::READY);
842 InGate* inGate = getInGate();
843 std::list<OutGate*> backlinks = inGate->getBackLinks();
844 for (std::list<OutGate*>::iterator io = backlinks.begin(); io != backlinks.end(); io++)
846 Node* fromNode = (*io)->getNode();
847 if(fromNode->getState() == YACS::DONE)
849 inGate->setPrecursorDone(*io);