1 // Copyright (C) 2006-2013 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.
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 "InputPort.hxx"
22 #include "OutputPort.hxx"
23 #include "InPropertyPort.hxx"
24 #include "ComposedNode.hxx"
25 #include "Dispatcher.hxx"
26 #include "InputDataStreamPort.hxx"
27 #include "OutputDataStreamPort.hxx"
31 #include "YacsTrace.hxx"
33 using namespace YACS::ENGINE;
36 /*! \class YACS::ENGINE::Node
37 * \brief Base class for all nodes
42 const char Node::SEP_CHAR_IN_PORT[]=".";
45 std::map<int,Node*> Node::idMap;
47 NodeStateNameMap::NodeStateNameMap()
49 insert(make_pair(YACS::READY, "READY"));
50 insert(make_pair(YACS::TOLOAD, "TOLOAD"));
51 insert(make_pair(YACS::LOADED, "LOADED"));
52 insert(make_pair(YACS::TOACTIVATE, "TOACTIVATE"));
53 insert(make_pair(YACS::ACTIVATED, "ACTIVATED"));
54 insert(make_pair(YACS::DESACTIVATED, "DESACTIVATED"));
55 insert(make_pair(YACS::DONE, "DONE"));
56 insert(make_pair(YACS::SUSPENDED, "SUSPENDED"));
57 insert(make_pair(YACS::LOADFAILED, "LOADFAILED"));
58 insert(make_pair(YACS::EXECFAILED, "EXECFAILED"));
59 insert(make_pair(YACS::PAUSE, "PAUSE"));
60 insert(make_pair(YACS::INTERNALERR, "INTERNALERR"));
61 insert(make_pair(YACS::DISABLED, "DISABLED"));
62 insert(make_pair(YACS::FAILED, "FAILED"));
63 insert(make_pair(YACS::ERROR, "ERROR"));
67 Node::Node(const std::string& name):_name(name),_inGate(this),_outGate(this),_father(0),_state(YACS::READY),
68 _implementation(Runtime::RUNTIME_ENGINE_INTERACTION_IMPL_NAME),_modified(1)
70 // Should be protected by lock ??
74 // Every node has an InPropertyPort
75 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
78 Node::Node(const Node& other, ComposedNode *father):_inGate(this),_outGate(this),_name(other._name),_father(father),
79 _state(YACS::READY),_implementation(other._implementation),
80 _propertyMap(other._propertyMap),_modified(1)
85 // Every node has an InPropertyPort
86 _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
94 * initialisation of all input and output ports and gates, for execution
97 void Node::init(bool start)
101 if(_state == YACS::DISABLED)
103 exDisabledState(); // to refresh propagation of DISABLED state
106 setState(YACS::READY);
109 Node *Node::clone(ComposedNode *father, bool editionOnly) const
111 Node *ret=simpleClone(father,editionOnly);
112 ret->performDuplicationOfPlacement(*this);
116 //! Change the name of the node
118 * raise an exception if the name is already used in the scope of its father
119 * \param name : the new name
121 void Node::setName(const std::string& name)
125 if(_father->isNameAlreadyUsed(name))
127 if ( _father->getChildByName(name) != this )
129 std::string what("Name ");
131 what+=" already exists in the scope of "; what+=_father->getName();
132 throw Exception(what);
140 * get the set of all nodes connected to the outGate
143 set<Node *> Node::getOutNodes() const
146 set<InGate *> inGates=_outGate.edSetInGate();
147 for(set<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
148 ret.insert((*iter)->getNode());
152 bool Node::exIsControlReady() const
154 return _inGate.exIsReady();
157 //! Update the node state
159 * \note : Update the '_state' attribute.
160 * Typically called by 'this->_inGate' when 'this->_inGate' is ready.
162 * Called by InGate::exNotifyFromPrecursor
164 void Node::exUpdateState()
166 if(_state==YACS::DISABLED)return;
167 if(_inGate.exIsReady())
168 setState(YACS::TOACTIVATE);
171 //! Notify this node that its execution has failed
173 * The node goes in FAILED state and
174 * propagate the notification through the outGate port
177 void Node::exFailedState()
179 DEBTRACE( "Node::exFailedState: " << getName() );
180 setState(YACS::FAILED);
181 _outGate.exNotifyFailed();
184 //! Notify this node that it has been disabled
186 * The node goes in DISABLED state and
187 * propagate the notification through the outGate port
190 void Node::exDisabledState()
192 DEBTRACE( "Node::exDisabledState: " << getName() );
193 setState(YACS::DISABLED);
194 _outGate.exNotifyDisabled();
197 InPort *Node::getInPort(const std::string& name) const throw(YACS::Exception)
202 ret=getInputPort(name);
206 ret=getInputDataStreamPort(name);
212 Node::getInPropertyPort() const throw(YACS::Exception)
214 return _inPropertyPort;
218 Node::getInputPort(const std::string& name) const throw(YACS::Exception)
220 if (name == "__InPropertyPort__Node__YACS_")
221 return _inPropertyPort;
224 std::string what("Node::getInputPort : the port with name "); what+=name; what+=" does not exist on the current level";
225 throw Exception(what);
230 * \note: Contrary to getOutputPort method, this method returns the output port at highest level, possible.
231 * That is to say in some ComposedNode, like ForEachLoop or Switch, an outport inside 'this' is seen differently than the true outport.
233 OutPort *Node::getOutPort(const std::string& name) const throw(YACS::Exception)
238 ret=getOutputPort(name);
242 ret=getOutputDataStreamPort(name);
247 std::list<InPort *> Node::getSetOfInPort() const
250 list<InputPort *> data=getSetOfInputPort();
251 ret.insert(ret.end(),data.begin(),data.end());
252 list<InputDataStreamPort *> ds=getSetOfInputDataStreamPort();
253 ret.insert(ret.end(),ds.begin(),ds.end());
257 std::list<OutPort *> Node::getSetOfOutPort() const
260 list<OutputPort *> data=getSetOfOutputPort();
261 ret.insert(ret.end(),data.begin(),data.end());
262 list<OutputDataStreamPort *> ds=getSetOfOutputDataStreamPort();
263 ret.insert(ret.end(),ds.begin(),ds.end());
268 * gets a set of the composed nodes that constitute the ascendancy of this node, starting from root
269 * or from a particular ancestor
270 * \b WARNING : returned set is not sorted !
271 * @param levelToStop composed node which is the oldest ancestor required
272 * @return ascendancy, direct father first in set.
275 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
277 list<ComposedNode *> ret;
278 if(this==levelToStop)
280 for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
285 bool Node::operator>(const Node& other) const
287 const ComposedNode *iter=other._father;
288 while(iter!=0 && iter!=this)
293 bool Node::operator<(const Node& other) const
295 const ComposedNode *iter=_father;
296 while(iter!=0 && iter!=(&other))
298 return iter==(&other);
302 * @return Implementation of node: C++, Python, CORBA...
303 * _implementation is set by a derived class in a Runtime
304 * it normally applies only to elementaryNodes and it is used by Ports to select Data Converters.
305 * Potential problem with Ports attached to composed Nodes...
308 string Node::getImplementation() const
310 return _implementation;
313 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
314 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
316 set<InputPort *> setOfUnitializedInputPort;
317 list<InputPort *> allOfInputPorts=getSetOfInputPort();
318 for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
320 if ( ! (*iter)->edIsInitialized() )
321 setOfUnitializedInputPort.insert(*iter);
323 return setOfUnitializedInputPort;
326 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
327 bool Node::edAreAllInputPortInitialized() const
329 set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
330 return ( setOfUnitializedInputPort.size() == 0);
334 * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
336 void Node::exForwardFailed()
338 _outGate.exNotifyFailed();
342 * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
344 void Node::exForwardFinished()
346 DEBTRACE("Node::exForwardFinished");
347 _outGate.exNotifyDone();
351 * Called typically by ComposedNode to correctly update DF/CF/DS links
353 void Node::edDisconnectAllLinksWithMe()
355 _inGate.edDisconnectAllLinksToMe();
356 _outGate.edDisconnectAllLinksFromMe();
359 Proc *Node::getProc()
363 return _father->getProc();
366 const Proc * Node::getProc() const
370 return _father->getProc();
373 ComposedNode *Node::getRootNode() const throw(YACS::Exception)
376 throw Exception("No root node");
377 ComposedNode *iter=_father;
380 return (ComposedNode *)iter;
384 * checks validity of ports name, that must not contain a particular character '?'
385 * USAGE NOT CLEAR, not used so far, when are those characters set ?
388 void Node::checkValidityOfPortName(const std::string& name) throw(YACS::Exception)
390 if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
392 string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
393 throw Exception(what);
398 * @note : Check that 'node1' and 'node2' have exactly the same father
399 * @exception : If 'node1' and 'node2' have NOT exactly the same father
401 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2) throw(YACS::Exception)
403 if(node1!=0 && node2!=0)
405 if(node1->_father==node2->_father)
406 return node1->_father;
408 throw Exception("check failed : nodes have not the same father");
411 const std::string Node::getId() const
413 std::string id=getRootNode()->getName();
414 if(getRootNode() != this)
415 id= id+'.'+ getRootNode()->getChildName(this);
416 string::size_type debut =id.find_first_of('.');
417 while(debut != std::string::npos){
419 debut=id.find_first_of('.',debut);
424 void Node::setProperty(const std::string& name, const std::string& value)
426 DEBTRACE("Node::setProperty " << name << " " << value);
427 _propertyMap[name]=value;
430 std::string Node::getProperty(const std::string& name)
432 std::map<std::string,std::string>::iterator it=_propertyMap.find(name);
434 if(it != _propertyMap.end())
437 return _father->getProperty(name);
442 std::map<std::string,std::string> Node::getProperties()
444 std::map<std::string,std::string> amap=_propertyMap;
447 std::map<std::string,std::string> fatherMap=_father->getProperties();
448 amap.insert(fatherMap.begin(),fatherMap.end());
454 void Node::setProperties(std::map<std::string,std::string> properties)
456 _propertyMap.clear();
457 _propertyMap=properties;
460 //! Return the node state in the context of its father
462 * \return the effective node state
464 * The node state is stored in a private attribute _state.
465 * This state is relative to its father state : a node with a
466 * TOACTIVATE state with a father node in a READY state is not
467 * to activate. Its effective state is only READY.
468 * This method returns the effective state of the node taking
469 * into account that of its father.
471 YACS::StatesForNode Node::getEffectiveState() const
473 if(!_father) //the root node
475 if(_state==YACS::DISABLED)
476 return YACS::DISABLED;
477 return _father->getEffectiveState(this);
480 //! Return the effective state of a node in the context of this one (its father)
482 * \param node: the node which effective state is queried
483 * \return the effective node state
485 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
487 if(node->getState()==YACS::DISABLED)
488 return YACS::DISABLED;
490 YACS::StatesForNode effectiveState=getEffectiveState();
491 switch(effectiveState)
495 case YACS::TOACTIVATE:
498 return YACS::DISABLED;
502 return node->getState();
506 //! Return the color associated to a state
508 * \param state : the node state
509 * \return the associated color
511 std::string Node::getColorState(YACS::StatesForNode state) const
521 case YACS::TOACTIVATE:
523 case YACS::ACTIVATED:
540 //! Dump to the input stream a dot representation of the node
542 * \param os : the input stream
544 void Node::writeDot(std::ostream &os) const
546 os << getId() << "[fillcolor=\"" ;
547 YACS::StatesForNode state=getEffectiveState();
548 os << getColorState(state);
549 os << "\" label=\"" << getImplementation() << "Node:" ;
550 os << getQualifiedName() <<"\"];\n";
553 //! same as Node::getName() in most cases, but differs for children of switch
555 * used by writeDot to distinguish children of switch, by adding a prefix to the name.
556 * prefix is built on case id.
559 std::string Node::getQualifiedName() const
562 return _father->getMyQualifiedName(this);
566 //! return node instance identifiant, unique for each node instance
568 * node instance identifiant is used to check if to nodes pointers refers to the same instance
575 //! Sets the given state for node.
576 /*! It is strongly recommended to use this function if you want to
577 * change the state of the node, instead of direct access to _state field (_state = ...).
579 void Node::setState(YACS::StatesForNode theState)
581 DEBTRACE("Node::setState: " << getName() << " " << theState);
583 // emit notification to all observers registered with the dispatcher on any change of the node's state
587 //! emit notification to all observers registered with the dispatcher
589 * The dispatcher is unique and can be obtained by getDispatcher()
591 void Node::sendEvent(const std::string& event)
593 DEBTRACE("Node::sendEvent " << event);
594 Dispatcher* disp=Dispatcher::getDispatcher();
595 disp->dispatch(this,event);
599 * For use only when loading a previously saved execution
602 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
604 node->setState(state);
607 //! indicates if the node is valid (returns 1) or not (returns 0)
609 * This method is useful when editing a schema. It has no meaning in execution.
610 * When a node is edited, its modified method must be called so when isValid is called, its state
611 * is updated (call to edUpdateState) before returning the validity check
617 if(_state > YACS::INVALID)
623 //! update the status of the node
625 * Only useful when editing a schema
626 * Do nothing in base Node : to implement in derived classes
628 void Node::edUpdateState()
630 DEBTRACE("Node::edUpdateState(): " << _modified);
634 //! returns a string that contains an error report if the node is in error
638 std::string Node::getErrorReport()
640 if(getState()==YACS::DISABLED)
641 return "<error node= "+getName()+ "state= DISABLED/>\n";
643 YACS::StatesForNode effectiveState=getEffectiveState();
645 DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
646 if(effectiveState != YACS::INVALID && effectiveState != YACS::ERROR &&
647 effectiveState != YACS::FAILED && effectiveState != YACS::INTERNALERR)
650 std::string report="<error node= " ;
651 report=report + getName() ;
652 switch(effectiveState)
655 report=report+" state= INVALID";
658 report=report+" state= ERROR";
661 report=report+" state= FAILED";
663 case YACS::INTERNALERR:
664 report=report+" state= INTERNALERR";
669 report=report + ">\n" ;
670 report=report+_errorDetails;
671 report=report+"\n</error>";
675 //! returns a string that contains the name of the container log file if it exists
677 * Do nothing here. To subclass
679 std::string Node::getContainerLog()
684 //! Sets Node in modified state and its father if it exists
688 void Node::modified()
690 DEBTRACE("Node::modified() " << getName());
696 //! Put this node into TOLOAD state when possible
700 void Node::ensureLoading()
702 if(_state == YACS::READY)
703 setState(YACS::TOLOAD);
706 //! Return the name of a state
710 std::string Node::getStateName(YACS::StatesForNode state)
712 static NodeStateNameMap nodeStateNameMap;
713 return nodeStateNameMap[state];
716 //! Stop all pending activities of the node
718 * This method should be called when a Proc is finished and must be deleted from the YACS server
720 void Node::shutdown(int level)
725 //! Clean the node in case of not clean exit
727 * This method should be called on a control-C or sigterm
729 void Node::cleanNodes()
733 //! Reset the node state depending on the parameter level
734 void Node::resetState(int level)
736 DEBTRACE("Node::resetState " << getName() << "," << level << "," << _state);
737 if(_state==YACS::ERROR || _state==YACS::FAILED)
739 setState(YACS::READY);
740 InGate* inGate = getInGate();
741 std::list<OutGate*> backlinks = inGate->getBackLinks();
742 for (std::list<OutGate*>::iterator io = backlinks.begin(); io != backlinks.end(); io++)
744 Node* fromNode = (*io)->getNode();
745 if(fromNode->getState() == YACS::DONE)
747 inGate->setPrecursorDone(*io);