1 // Copyright (C) 2006-2024 CEA, EDF
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"
33 #include "YacsTrace.hxx"
35 using namespace YACS::ENGINE;
38 /*! \class YACS::ENGINE::Node
39 * \brief Base class for all nodes
44 const char Node::SEP_CHAR_IN_PORT[]=".";
47 std::map<int,Node*> Node::idMap;
49 NodeStateNameMap::NodeStateNameMap()
51 insert(make_pair(YACS::READY, "READY"));
52 insert(make_pair(YACS::TOLOAD, "TOLOAD"));
53 insert(make_pair(YACS::LOADED, "LOADED"));
54 insert(make_pair(YACS::TOACTIVATE, "TOACTIVATE"));
55 insert(make_pair(YACS::ACTIVATED, "ACTIVATED"));
56 insert(make_pair(YACS::DESACTIVATED, "DESACTIVATED"));
57 insert(make_pair(YACS::DONE, "DONE"));
58 insert(make_pair(YACS::SUSPENDED, "SUSPENDED"));
59 insert(make_pair(YACS::LOADFAILED, "LOADFAILED"));
60 insert(make_pair(YACS::EXECFAILED, "EXECFAILED"));
61 insert(make_pair(YACS::PAUSE, "PAUSE"));
62 insert(make_pair(YACS::INTERNALERR, "INTERNALERR"));
63 insert(make_pair(YACS::DISABLED, "DISABLED"));
64 insert(make_pair(YACS::FAILED, "FAILED"));
65 insert(make_pair(YACS::ERROR, "ERROR"));
69 Node::Node(const std::string& name):_name(name),_inGate(this),_outGate(this),_father(0),_state(YACS::READY),
70 _implementation(Runtime::RUNTIME_ENGINE_INTERACTION_IMPL_NAME),_modified(1)
72 // Should be protected by lock ??
73 Node::checkValidityOfNodeName(_name);
77 // Every node has an InPropertyPort
78 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
81 Node::Node(const Node& other, ComposedNode *father):_inGate(this),_outGate(this),_name(other._name),_father(father),
82 _state(YACS::READY),_implementation(other._implementation),
83 _propertyMap(other._propertyMap),_modified(1)
88 // Every node has an InPropertyPort
89 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
90 _eventReceiver=const_cast<Node *>(&other);
95 delete _inPropertyPort;
99 * initialisation of all input and output ports and gates, for execution
102 void Node::init(bool start)
106 if(_state == YACS::DISABLED)
108 exDisabledState(); // to refresh propagation of DISABLED state
111 setState(YACS::READY);
115 * This method clones \a this by :
117 * - deep copying nodes, links, ports, types
118 * - containers are either deep copied or shallow copied depending on _isAttachedOnCloning attribute.
119 * - component are either deep copied or shallow copied depending on _isAttachedOnCloning attribute.
121 * So \b this \b method \b clone \b is \b dedicated \b for \b DynParaLoop \b class \b or \b subclasses.
122 * It \b should \b not \b be \b used \b elsewhere, because
123 * _isAttachedOnCloning attribute is an attribute in the engine not for GUI/TUI aspects.
124 * For GUI/TUI manipulation cloneWithoutCompAndContDeepCpy method should be used preferably.
126 * \param [in] father - The new father of the returned clone.
127 * \param [in] editionOnly ignored
129 * \sa cloneWithoutCompAndContDeepCpy
131 Node *Node::clone(ComposedNode *father, bool editionOnly) const
133 Node *ret(simpleClone(father,editionOnly));
134 ret->performDuplicationOfPlacement(*this);
139 * This method clones \a this by :
140 * - deep copying nodes, links, ports, types
141 * - shallow copy containers
142 * - shallow copy components
144 * So this method simply ignores isAttachedOnCloning attribute for both containers and components.
145 * So this method is dedicated for the GUI/TUI users.
147 * \param [in] father - The new father of the returned clone.
148 * \param [in] editionOnly ignored
150 Node *Node::cloneWithoutCompAndContDeepCpy(ComposedNode *father, bool editionOnly) const
152 Node *ret(simpleClone(father,editionOnly));
153 ret->performShallowDuplicationOfPlacement(*this);
157 //! Change the name of the node
159 * raise an exception if the name is already used in the scope of its father
160 * \param name : the new name
162 void Node::setName(const std::string& name)
164 Node::checkValidityOfNodeName(name);
167 if(_father->isNameAlreadyUsed(name))
169 if ( _father->getChildByName(name) != this )
171 std::string what("Name ");
173 what+=" already exists in the scope of "; what+=_father->getName();
174 throw Exception(what);
182 * get the set of all nodes connected to the outGate
185 list<Node *> Node::getOutNodes() const
188 list<InGate *> inGates=_outGate.edSetInGate();
189 for(list<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
190 ret.push_back((*iter)->getNode());
194 bool Node::exIsControlReady() const
196 return _inGate.exIsReady();
199 //! Update the node state
201 * \note : Update the '_state' attribute.
202 * Typically called by 'this->_inGate' when 'this->_inGate' is ready.
204 * Called by InGate::exNotifyFromPrecursor
206 void Node::exUpdateState()
208 if(_state==YACS::DISABLED)return;
209 if(_inGate.exIsReady())
210 setState(YACS::TOACTIVATE);
213 //! Notify this node that its execution has failed
215 * The node goes in FAILED state and
216 * propagate the notification through the outGate port
219 void Node::exFailedState()
221 DEBTRACE( "Node::exFailedState: " << getName() );
222 setState(YACS::FAILED);
223 _outGate.exNotifyFailed();
226 //! Notify this node that it has been disabled
228 * The node goes in DISABLED state and
229 * propagate the notification through the outGate port
232 void Node::exDisabledState()
234 DEBTRACE( "Node::exDisabledState: " << getName() );
235 setState(YACS::DISABLED);
236 _outGate.exNotifyDisabled();
239 InPort *Node::getInPort(const std::string& name) const
244 ret=getInputPort(name);
248 ret=getInputDataStreamPort(name);
254 Node::getInPropertyPort() const
256 return _inPropertyPort;
260 Node::getInputPort(const std::string& name) const
262 if (name == "__InPropertyPort__Node__YACS_")
263 return _inPropertyPort;
266 std::string what("Node::getInputPort : the port with name "); what+=name; what+=" does not exist on the current level";
267 throw Exception(what);
272 * \note: Contrary to getOutputPort method, this method returns the output port at highest level, possible.
273 * That is to say in some ComposedNode, like ForEachLoop or Switch, an outport inside 'this' is seen differently than the true outport.
275 OutPort *Node::getOutPort(const std::string& name) const
280 ret=getOutputPort(name);
284 ret=getOutputDataStreamPort(name);
289 std::list<InPort *> Node::getSetOfInPort() const
292 list<InputPort *> data=getSetOfInputPort();
293 ret.insert(ret.end(),data.begin(),data.end());
294 list<InputDataStreamPort *> ds=getSetOfInputDataStreamPort();
295 ret.insert(ret.end(),ds.begin(),ds.end());
299 std::list<OutPort *> Node::getSetOfOutPort() const
302 list<OutputPort *> data=getSetOfOutputPort();
303 ret.insert(ret.end(),data.begin(),data.end());
304 list<OutputDataStreamPort *> ds=getSetOfOutputDataStreamPort();
305 ret.insert(ret.end(),ds.begin(),ds.end());
310 * gets a set of the composed nodes that constitute the ascendancy of this node, starting from root
311 * or from a particular ancestor
312 * \b WARNING : returned set is not sorted !
313 * @param levelToStop composed node which is the oldest ancestor required
314 * @return ascendancy, direct father first in set.
317 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
319 list<ComposedNode *> ret;
320 if(this==levelToStop)
322 for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
327 bool Node::operator>(const Node& other) const
329 const ComposedNode *iter=other._father;
330 while(iter!=0 && iter!=this)
335 bool Node::operator<(const Node& other) const
337 const ComposedNode *iter=_father;
338 while(iter!=0 && iter!=(&other))
340 return iter==(&other);
344 * @return Implementation of node: C++, Python, CORBA...
345 * _implementation is set by a derived class in a Runtime
346 * it normally applies only to elementaryNodes and it is used by Ports to select Data Converters.
347 * Potential problem with Ports attached to composed Nodes...
350 string Node::getImplementation() const
352 return _implementation;
355 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
356 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
358 set<InputPort *> setOfUnitializedInputPort;
359 list<InputPort *> allOfInputPorts=getSetOfInputPort();
360 for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
362 if ( ! (*iter)->edIsInitialized() )
363 setOfUnitializedInputPort.insert(*iter);
365 return setOfUnitializedInputPort;
368 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
369 bool Node::edAreAllInputPortInitialized() const
371 set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
372 return ( setOfUnitializedInputPort.size() == 0);
376 * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
378 void Node::exForwardFailed()
380 _outGate.exNotifyFailed();
384 * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
386 void Node::exForwardFinished()
388 DEBTRACE("Node::exForwardFinished");
389 _outGate.exNotifyDone();
393 * Called typically by ComposedNode to correctly update DF/CF/DS links
395 void Node::edDisconnectAllLinksWithMe()
397 _inGate.edDisconnectAllLinksToMe();
398 _outGate.edDisconnectAllLinksFromMe();
401 Proc *Node::getProc()
405 return _father->getProc();
408 const Proc * Node::getProc() const
412 return _father->getProc();
415 DynParaLoop *Node::getClosestDPLAmongAncestors() const
419 ComposedNode *iter(_father);
422 DynParaLoop *iter2(dynamic_cast<DynParaLoop *>(iter));
431 ComposedNode *Node::getRootNode() const
434 throw Exception("No root node");
435 ComposedNode *iter=_father;
438 return (ComposedNode *)iter;
442 * checks validity of ports name, that must not contain a particular character '?'
443 * USAGE NOT CLEAR, not used so far, when are those characters set ?
446 void Node::checkValidityOfPortName(const std::string& name)
448 if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
450 string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
451 throw Exception(what);
455 void Node::checkValidityOfNodeName(const std::string& name)
457 if(name.find(ComposedNode::SEP_CHAR_BTW_LEVEL,0)!=string::npos)
459 string what("Node name "); what+=name; what+="not valid because it contains character "; what+=ComposedNode::SEP_CHAR_BTW_LEVEL;
460 throw Exception(what);
465 * @note : Check that 'node1' and 'node2' have exactly the same father
466 * @exception : If 'node1' and 'node2' have NOT exactly the same father
468 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2)
470 if(node1!=0 && node2!=0)
472 if(node1->_father==node2->_father)
473 return node1->_father;
475 throw Exception("check failed : nodes have not the same father");
478 const std::string Node::getId() const
480 std::string id=getRootNode()->getName();
481 if(getRootNode() != this)
482 id= id+'.'+ getRootNode()->getChildName(this);
483 string::size_type debut =id.find_first_of('.');
484 while(debut != std::string::npos){
486 debut=id.find_first_of('.',debut);
491 void Node::setProperty(const std::string& name, const std::string& value)
493 DEBTRACE("Node::setProperty " << name << " " << value);
494 _propertyMap[name]=value;
497 std::string Node::getProperty(const std::string& name)
499 std::map<std::string,std::string>::iterator it=_propertyMap.find(name);
501 if(it != _propertyMap.end())
504 return _father->getProperty(name);
509 std::map<std::string,std::string> Node::getProperties()
511 std::map<std::string,std::string> amap=_propertyMap;
514 std::map<std::string,std::string> fatherMap=_father->getProperties();
515 amap.insert(fatherMap.begin(),fatherMap.end());
521 void Node::setProperties(std::map<std::string,std::string> properties)
523 _propertyMap.clear();
524 _propertyMap=properties;
527 //! Return the node state in the context of its father
529 * \return the effective node state
531 * The node state is stored in a private attribute _state.
532 * This state is relative to its father state : a node with a
533 * TOACTIVATE state with a father node in a READY state is not
534 * to activate. Its effective state is only READY.
535 * This method returns the effective state of the node taking
536 * into account that of its father.
538 YACS::StatesForNode Node::getEffectiveState() const
540 if(!_father) //the root node
542 if(_state==YACS::DISABLED)
543 return YACS::DISABLED;
544 return _father->getEffectiveState(this);
547 //! Return the effective state of a node in the context of this one (its father)
549 * \param node: the node which effective state is queried
550 * \return the effective node state
552 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
554 if(node->getState()==YACS::DISABLED)
555 return YACS::DISABLED;
557 YACS::StatesForNode effectiveState=getEffectiveState();
558 switch(effectiveState)
562 case YACS::TOACTIVATE:
565 return YACS::DISABLED;
569 return node->getState();
573 //! Return the color associated to a state
575 * \param state : the node state
576 * \return the associated color
578 std::string Node::getColorState(YACS::StatesForNode state) const
588 case YACS::TOACTIVATE:
590 case YACS::ACTIVATED:
607 //! Dump to the input stream a dot representation of the node
609 * \param os : the input stream
611 void Node::writeDot(std::ostream &os) const
613 os << getId() << "[fillcolor=\"" ;
614 YACS::StatesForNode state=getEffectiveState();
615 os << getColorState(state);
616 os << "\" label=\"" << getImplementation() << "Node:" ;
617 os << getQualifiedName() <<"\"];\n";
620 void Node::writeDotInFile(const std::string& fileName) const
622 std::ofstream f(fileName);
626 //! same as Node::getName() in most cases, but differs for children of switch
628 * used by writeDot to distinguish children of switch, by adding a prefix to the name.
629 * prefix is built on case id.
632 std::string Node::getQualifiedName() const
635 return _father->getMyQualifiedName(this);
639 //! return node instance identifiant, unique for each node instance
641 * node instance identifiant is used to check if to nodes pointers refers to the same instance
648 //! Sets the given state for node.
649 /*! It is strongly recommended to use this function if you want to
650 * change the state of the node, instead of direct access to _state field (_state = ...).
652 void Node::setState(YACS::StatesForNode theState)
654 DEBTRACE("Node::setState: " << getName() << " " << theState);
656 // emit notification to all observers registered with the dispatcher on any change of the node's state
660 std::vector<std::pair<std::string,int> > Node::getDPLScopeInfo(ComposedNode *gfn)
662 std::vector< std::pair<std::string,int> > ret;
664 ComposedNode *work(getFather());
665 while(work!=gfn && work!=0)
667 DynParaLoop *workc(dynamic_cast<DynParaLoop *>(work));
670 std::pair<std::string,int> p(gfn->getChildName(workc),workc->getBranchIDOfNode(work2));
674 work=work->getFather();
680 * Method called by the Executor only if the executor is sensitive of scope of DynParaLoop.
681 * This method is virtual and empty because by default nothing is done.
683 void Node::applyDPLScope(ComposedNode *gfn)
687 //! emit notification to all observers registered with the dispatcher
689 * The dispatcher is unique and can be obtained by getDispatcher()
691 void Node::sendEvent(const std::string& event)
693 DEBTRACE("Node::sendEvent " << event);
694 Dispatcher *disp(Dispatcher::getDispatcher());
697 disp->dispatch(this,event);
701 disp->dispatchFromClone(_eventReceiver,event,this);
705 //! emit notification to all observers registered with the dispatcher
707 * The dispatcher is unique and can be obtained by getDispatcher()
709 void Node::sendEvent2(const std::string& event, void *something)
711 Dispatcher* disp=Dispatcher::getDispatcher();
712 disp->dispatch2(this,event,something);
716 * For use only when loading a previously saved execution
719 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
721 node->setState(state);
724 //! indicates if the node is valid (returns 1) or not (returns 0)
726 * This method is useful when editing a schema. It has no meaning in execution.
727 * When a node is edited, its modified method must be called so when isValid is called, its state
728 * is updated (call to edUpdateState) before returning the validity check
734 if(_state > YACS::INVALID)
740 //! update the status of the node
742 * Only useful when editing a schema
743 * Do nothing in base Node : to implement in derived classes
745 void Node::edUpdateState()
747 DEBTRACE("Node::edUpdateState(): " << _modified);
751 //! returns a string that contains an error report if the node is in error
755 std::string Node::getErrorReport()
757 if(getState()==YACS::DISABLED)
758 return "<error node= "+getName()+ "state= DISABLED/>\n";
760 YACS::StatesForNode effectiveState=getEffectiveState();
762 DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
763 if(effectiveState != YACS::INVALID && effectiveState != YACS::ERROR &&
764 effectiveState != YACS::FAILED && effectiveState != YACS::INTERNALERR)
767 std::string report="<error node= " ;
768 report=report + getName() ;
769 switch(effectiveState)
772 report=report+" state= INVALID";
775 report=report+" state= ERROR";
778 report=report+" state= FAILED";
780 case YACS::INTERNALERR:
781 report=report+" state= INTERNALERR";
786 report=report + ">\n" ;
787 report=report+_errorDetails;
788 report=report+"\n</error>";
792 //! returns a string that contains the name of the container log file if it exists
794 * Do nothing here. To subclass
796 std::string Node::getContainerLog()
801 //! Sets Node in modified state and its father if it exists
805 void Node::modified()
807 DEBTRACE("Node::modified() " << getName());
813 //! Put this node into TOLOAD state when possible
817 void Node::ensureLoading()
819 if(_state == YACS::READY)
820 setState(YACS::TOLOAD);
823 //! Return the name of a state
827 std::string Node::getStateName(YACS::StatesForNode state)
829 static NodeStateNameMap nodeStateNameMap;
830 return nodeStateNameMap[state];
833 //! Stop all pending activities of the node
835 * This method should be called when a Proc is finished and must be deleted from the YACS server
837 void Node::shutdown(int level)
842 //! Clean the node in case of not clean exit
844 * This method should be called on a control-C or sigterm
846 void Node::cleanNodes()
850 //! Reset the node state depending on the parameter level
851 void Node::resetState(int level)
853 DEBTRACE("Node::resetState " << getName() << "," << level << "," << _state);
854 if(_state==YACS::ERROR || _state==YACS::FAILED || _state==YACS::ACTIVATED)
856 setState(YACS::READY);
857 InGate* inGate = getInGate();
858 std::list<OutGate*> backlinks = inGate->getBackLinks();
859 for (std::list<OutGate*>::iterator io = backlinks.begin(); io != backlinks.end(); io++)
861 Node* fromNode = (*io)->getNode();
862 if(fromNode->getState() == YACS::DONE)
864 inGate->setPrecursorDone(*io);