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