Salome HOME
Update copyrights 2014.
[modules/yacs.git] / src / engine / Node.cxx
1 // Copyright (C) 2006-2014  CEA/DEN, EDF R&D
2 //
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.
7 //
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.
12 //
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
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19
20 #include "Node.hxx"
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"
28 #include <iostream>
29
30 //#define _DEVDEBUG_
31 #include "YacsTrace.hxx"
32
33 using namespace YACS::ENGINE;
34 using namespace std;
35
36 /*! \class YACS::ENGINE::Node
37  *  \brief Base class for all nodes
38  *
39  * \ingroup Nodes
40  */
41
42 const char Node::SEP_CHAR_IN_PORT[]=".";
43
44 int Node::_total = 0;
45 std::map<int,Node*> Node::idMap;
46
47 NodeStateNameMap::NodeStateNameMap()
48 {
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"));
64 }
65
66
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)
69 {
70   // Should be protected by lock ??
71   _numId = _total++;
72   idMap[_numId]=this;
73
74   // Every node has an InPropertyPort
75   _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
76 }
77
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)
81 {
82   _numId = _total++;
83   idMap[_numId]=this;
84
85   // Every node has an InPropertyPort
86   _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
87 }
88
89 Node::~Node()
90 {
91 }
92
93 /**
94  *  initialisation of all input and output ports and gates, for execution
95  */
96
97 void Node::init(bool start)
98 {
99   _inGate.exReset();
100   _outGate.exReset();
101   if(_state == YACS::DISABLED)
102     {
103       exDisabledState(); // to refresh propagation of DISABLED state 
104       return;
105     }
106   setState(YACS::READY);
107 }
108
109 Node *Node::clone(ComposedNode *father, bool editionOnly) const
110 {
111   Node *ret=simpleClone(father,editionOnly);
112   ret->performDuplicationOfPlacement(*this);
113   return ret;
114 }
115
116 //! Change the name of the node
117 /*!
118  *  raise an exception if the name is already used in the scope of its father 
119  *  \param name : the new name
120  */
121 void Node::setName(const std::string& name)
122 {
123   if(_father)
124     {
125       if(_father->isNameAlreadyUsed(name))
126         {
127           if ( _father->getChildByName(name) != this )
128             {
129               std::string what("Name "); 
130               what+=name;
131               what+=" already exists in the scope of "; what+=_father->getName();
132               throw Exception(what);
133             }
134         }
135     }
136   _name=name;
137 }
138
139 /**
140  *  get the set of all nodes connected to the outGate
141  */
142
143 set<Node *> Node::getOutNodes() const
144 {
145   set<Node *> ret;
146   set<InGate *> inGates=_outGate.edSetInGate();
147   for(set<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
148     ret.insert((*iter)->getNode());
149   return ret;
150 }
151
152 bool Node::exIsControlReady() const
153 {
154   return _inGate.exIsReady();
155 }
156
157 //! Update the node state
158 /*!
159  * \note : Update the '_state' attribute.
160  *          Typically called by 'this->_inGate' when 'this->_inGate' is ready.
161  *
162  *          Called by InGate::exNotifyFromPrecursor 
163  */
164 void Node::exUpdateState()
165 {
166   if(_state==YACS::DISABLED)return;
167   if(_inGate.exIsReady())
168     setState(YACS::TOACTIVATE);
169 }
170
171 //! Notify this node that its execution has failed
172 /*!
173  * The node goes in FAILED state and 
174  * propagate the notification through the outGate port
175  *
176  */
177 void Node::exFailedState()
178 {
179   DEBTRACE( "Node::exFailedState: " << getName() );
180   setState(YACS::FAILED);
181   _outGate.exNotifyFailed();
182 }
183
184 //! Notify this node that it has been disabled
185 /*!
186  * The node goes in DISABLED state and
187  * propagate the notification through the outGate port
188  *
189  */
190 void Node::exDisabledState()
191 {
192   DEBTRACE( "Node::exDisabledState: " << getName() );
193   setState(YACS::DISABLED);
194   _outGate.exNotifyDisabled();
195 }
196
197 InPort *Node::getInPort(const std::string& name) const throw(YACS::Exception)
198 {
199   InPort *ret;
200   try
201     {
202       ret=getInputPort(name);
203     }
204   catch(Exception& e)
205     {
206       ret=getInputDataStreamPort(name);
207     }
208   return ret;
209 }
210
211 InPropertyPort *
212 Node::getInPropertyPort() const throw(YACS::Exception)
213 {
214   return _inPropertyPort;
215 }
216
217 InputPort *
218 Node::getInputPort(const std::string& name) const throw(YACS::Exception)
219 {
220   if (name == "__InPropertyPort__Node__YACS_")
221     return _inPropertyPort;
222   else
223   {
224     std::string what("Node::getInputPort : the port with name "); what+=name; what+=" does not exist on the current level";
225     throw Exception(what);
226   }
227 }
228
229 /*!
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.
232  */
233 OutPort *Node::getOutPort(const std::string& name) const throw(YACS::Exception)
234 {
235   OutPort *ret;
236   try
237     {
238       ret=getOutputPort(name);
239     }
240   catch(Exception& e)
241     {
242       ret=getOutputDataStreamPort(name);
243     }
244   return ret;
245 }
246
247 std::list<InPort *> Node::getSetOfInPort() const
248 {
249   list<InPort *> ret;
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());
254   return ret;
255 }
256
257 std::list<OutPort *> Node::getSetOfOutPort() const
258 {
259   list<OutPort *> ret;
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());
264   return ret;
265 }
266
267 /**
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.
273  */
274
275 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
276 {
277   list<ComposedNode *> ret;
278   if(this==levelToStop)
279     return ret;
280   for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
281       ret.push_back(iter);
282   return ret;
283 }
284
285 bool Node::operator>(const Node& other) const
286 {
287   const ComposedNode *iter=other._father;
288   while(iter!=0 && iter!=this)
289     iter=iter->_father;
290   return iter==this;
291 }
292
293 bool Node::operator<(const Node& other) const
294 {
295   const ComposedNode *iter=_father;
296   while(iter!=0 && iter!=(&other))
297     iter=iter->_father;
298   return iter==(&other);
299 }
300
301 /**
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...
306  */
307
308 string Node::getImplementation() const
309 {
310   return _implementation;
311 }
312
313 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
314 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
315 {
316   set<InputPort *> setOfUnitializedInputPort;
317   list<InputPort *> allOfInputPorts=getSetOfInputPort();
318   for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
319     {
320       if ( ! (*iter)->edIsInitialized() )
321         setOfUnitializedInputPort.insert(*iter);
322     }
323   return setOfUnitializedInputPort;
324 }
325
326 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
327 bool Node::edAreAllInputPortInitialized() const
328 {
329   set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
330   return ( setOfUnitializedInputPort.size() == 0);
331 }
332
333 /*!
334  * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
335  */
336 void Node::exForwardFailed()
337 {
338   _outGate.exNotifyFailed();
339 }
340
341 /*!
342  * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
343  */
344 void Node::exForwardFinished()
345 {
346   DEBTRACE("Node::exForwardFinished");
347   _outGate.exNotifyDone();
348 }
349
350 /*!
351  * Called typically by ComposedNode to correctly update DF/CF/DS links
352  */
353 void Node::edDisconnectAllLinksWithMe()
354 {
355   _inGate.edDisconnectAllLinksToMe();
356   _outGate.edDisconnectAllLinksFromMe();
357 }
358
359 Proc *Node::getProc()
360 {
361   if(!_father)
362     return 0;
363   return _father->getProc();
364 }
365
366 const Proc * Node::getProc() const
367 {
368   if(!_father)
369     return 0;
370   return _father->getProc();
371 }
372
373 ComposedNode *Node::getRootNode() const throw(YACS::Exception)
374 {
375   if(!_father)
376     throw Exception("No root node");
377   ComposedNode *iter=_father;
378   while(iter->_father)
379     iter=iter->_father;
380   return (ComposedNode *)iter;
381 }
382
383 /**
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 ?
386  */
387
388 void Node::checkValidityOfPortName(const std::string& name) throw(YACS::Exception)
389 {
390   if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
391     {
392       string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
393       throw Exception(what);
394     }
395 }
396
397 /**
398  * @note : Check that 'node1' and 'node2' have exactly the same father
399  * @exception : If 'node1' and 'node2' have NOT exactly the same father
400  */
401 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2) throw(YACS::Exception)
402 {
403   if(node1!=0 && node2!=0)
404     {
405       if(node1->_father==node2->_father)
406         return node1->_father;
407     }
408   throw Exception("check failed : nodes have not the same father");
409 }
410
411 const std::string Node::getId() const
412 {
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){
418         id[debut]='_';
419         debut=id.find_first_of('.',debut);
420     }
421     return id;
422 }
423
424 void Node::setProperty(const std::string& name, const std::string& value)
425 {
426     DEBTRACE("Node::setProperty " << name << " " << value);
427     _propertyMap[name]=value;
428 }
429
430 std::string Node::getProperty(const std::string& name)
431 {
432   std::map<std::string,std::string>::iterator it=_propertyMap.find(name);
433
434   if(it != _propertyMap.end())
435     return it->second;
436   else if(_father)
437     return _father->getProperty(name);
438   else
439     return "";
440 }
441
442 std::map<std::string,std::string> Node::getProperties()
443 {
444   std::map<std::string,std::string> amap=_propertyMap;
445   if(_father)
446     {
447       std::map<std::string,std::string> fatherMap=_father->getProperties();
448       amap.insert(fatherMap.begin(),fatherMap.end());
449     }
450
451   return amap;
452 }
453
454 void Node::setProperties(std::map<std::string,std::string> properties)
455 {
456   _propertyMap.clear();
457   _propertyMap=properties;
458 }
459
460 //! Return the node state in the context of its father
461 /*!
462  * \return the effective node state
463  *
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.
470  */
471 YACS::StatesForNode Node::getEffectiveState() const
472 {
473   if(!_father)   //the root node
474     return _state;
475   if(_state==YACS::DISABLED)
476     return YACS::DISABLED;
477   return _father->getEffectiveState(this);
478 }
479
480 //! Return the effective state of a node in the context of this one (its father)
481 /*!
482  * \param node: the node which effective state is queried
483  * \return the effective node state
484  */
485 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
486 {
487   if(node->getState()==YACS::DISABLED)
488     return YACS::DISABLED;
489
490   YACS::StatesForNode effectiveState=getEffectiveState();
491   switch(effectiveState)
492     {
493     case YACS::READY:
494       return YACS::READY;
495     case YACS::TOACTIVATE:
496       return YACS::READY;
497     case YACS::DISABLED:
498       return YACS::DISABLED;
499     case YACS::ERROR:
500       return YACS::FAILED;
501     default:
502       return node->getState();
503     }
504 }
505
506 //! Return the color associated to a state
507 /*!
508  * \param state : the node state
509  * \return the associated color
510  */
511 std::string Node::getColorState(YACS::StatesForNode state) const
512 {
513   switch(state)
514     {
515     case YACS::READY:
516       return "pink";
517     case YACS::TOLOAD:
518       return "magenta";
519     case YACS::LOADED:
520       return "magenta";
521     case YACS::TOACTIVATE:
522       return "purple";
523     case YACS::ACTIVATED:
524       return "blue";
525     case YACS::DONE:
526       return "green";
527     case YACS::ERROR:
528       return "red";
529     case YACS::FAILED:
530       return "orange";
531     case YACS::DISABLED:
532       return "grey";
533     case YACS::PAUSE:
534       return "white";
535     default:
536       return "white";
537     }
538 }
539
540 //! Dump to the input stream a dot representation of the node
541 /*!
542  *  \param os : the input stream
543  */
544 void Node::writeDot(std::ostream &os) const
545 {
546   os << getId() << "[fillcolor=\"" ;
547   YACS::StatesForNode state=getEffectiveState();
548   os << getColorState(state);
549   os << "\" label=\"" << getImplementation() << "Node:" ;
550   os << getQualifiedName() <<"\"];\n";
551 }
552
553 //! same as Node::getName() in most cases, but differs for children of switch
554 /*!
555  *  used by writeDot to distinguish children of switch, by adding a prefix to the name.
556  *  prefix is built on case id.
557  */
558
559 std::string Node::getQualifiedName() const
560 {
561   if(_father)
562     return _father->getMyQualifiedName(this);
563   return getName();
564 }
565
566 //! return node instance identifiant, unique for each node instance 
567 /*!
568  * node instance identifiant is used to check if to nodes pointers refers to the same instance
569  */ 
570 int Node::getNumId()
571 {
572   return _numId;
573 }
574
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 = ...).
578  */
579 void Node::setState(YACS::StatesForNode theState)
580 {
581   DEBTRACE("Node::setState: " << getName() << " " << theState);
582   _state = theState;
583   // emit notification to all observers registered with the dispatcher on any change of the node's state
584   sendEvent("status");
585 }
586
587 //! emit notification to all observers registered with  the dispatcher 
588 /*!
589  * The dispatcher is unique and can be obtained by getDispatcher()
590  */
591 void Node::sendEvent(const std::string& event)
592 {
593   DEBTRACE("Node::sendEvent " << event);
594   Dispatcher* disp=Dispatcher::getDispatcher();
595   disp->dispatch(this,event);
596 }
597
598 /*!
599  *  For use only when loading a previously saved execution
600  */
601
602 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
603 {
604   node->setState(state);
605 }
606
607 //! indicates if the node is valid (returns 1) or not (returns 0)
608 /*!
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
612  */
613 int Node::isValid()
614 {
615   if(_modified)
616     edUpdateState();
617   if(_state > YACS::INVALID)
618     return 1;
619   else
620     return 0;
621 }
622
623 //! update the status of the node
624 /*!
625  * Only useful when editing a schema
626  * Do nothing in base Node : to implement in derived classes
627  */
628 void Node::edUpdateState()
629 {
630   DEBTRACE("Node::edUpdateState(): " << _modified);
631   _modified=0;
632 }
633
634 //! returns a string that contains an error report if the node is in error
635 /*!
636  * 
637  */
638 std::string Node::getErrorReport()
639 {
640   if(getState()==YACS::DISABLED)
641     return "<error node= "+getName()+ "state= DISABLED/>\n";
642
643   YACS::StatesForNode effectiveState=getEffectiveState();
644
645   DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
646   if(effectiveState != YACS::INVALID &&  effectiveState != YACS::ERROR && 
647      effectiveState != YACS::FAILED && effectiveState != YACS::INTERNALERR)
648     return "";
649
650   std::string report="<error node= " ;
651   report=report + getName() ;
652   switch(effectiveState)
653     {
654     case YACS::INVALID:
655       report=report+" state= INVALID";
656       break;
657     case YACS::ERROR:
658       report=report+" state= ERROR";
659       break;
660     case YACS::FAILED:
661       report=report+" state= FAILED";
662       break;
663     case YACS::INTERNALERR:
664       report=report+" state= INTERNALERR";
665       break;
666     default:
667       break;
668     }
669   report=report + ">\n" ;
670   report=report+_errorDetails;
671   report=report+"\n</error>";
672   return report;
673 }
674
675 //! returns a string that contains the name of the container log file if it exists
676 /*!
677  * Do nothing here. To subclass
678  */
679 std::string Node::getContainerLog()
680 {
681   return "";
682 }
683
684 //! Sets Node in modified state and its father if it exists
685 /*!
686  * 
687  */
688 void Node::modified()
689 {
690   DEBTRACE("Node::modified() " << getName());
691   _modified=1;
692   if(_father)
693     _father->modified();
694 }
695
696 //! Put this node into TOLOAD state when possible
697 /*!
698  * 
699  */
700 void Node::ensureLoading()
701 {
702   if(_state == YACS::READY)
703     setState(YACS::TOLOAD);
704 }
705
706 //! Return the name of a state
707 /*!
708  * 
709  */
710 std::string Node::getStateName(YACS::StatesForNode state)
711 {
712   static NodeStateNameMap nodeStateNameMap;
713   return nodeStateNameMap[state];
714 }
715
716 //! Stop all pending activities of the node
717 /*!
718  * This method should be called when a Proc is finished and must be deleted from the YACS server
719  */
720 void Node::shutdown(int level)
721 {
722   if(level==0)return;
723 }
724
725 //! Clean the node in case of not clean exit
726 /*!
727  * This method should be called on a control-C or sigterm
728  */
729 void Node::cleanNodes()
730 {
731 }
732
733 //! Reset the node state depending on the parameter level
734 void Node::resetState(int level)
735 {
736   DEBTRACE("Node::resetState " << getName() << "," << level << "," << _state);
737   if(_state==YACS::ERROR || _state==YACS::FAILED)
738     {
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++)
743         {
744           Node* fromNode = (*io)->getNode();
745           if(fromNode->getState() == YACS::DONE)
746             {
747               inGate->setPrecursorDone(*io);
748             }
749         }
750     }
751 }