Salome HOME
[EDF28974] : Move to python implementation of driver and integrate monitoring into...
[modules/yacs.git] / src / engine / Node.cxx
1 // Copyright (C) 2006-2024  CEA, EDF
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 "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"
29 #include <iostream>
30 #include <fstream>
31
32 //#define _DEVDEBUG_
33 #include "YacsTrace.hxx"
34
35 using namespace YACS::ENGINE;
36 using namespace std;
37
38 /*! \class YACS::ENGINE::Node
39  *  \brief Base class for all nodes
40  *
41  * \ingroup Nodes
42  */
43
44 const char Node::SEP_CHAR_IN_PORT[]=".";
45
46 int Node::_total = 0;
47 std::map<int,Node*> Node::idMap;
48
49 NodeStateNameMap::NodeStateNameMap()
50 {
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"));
66 }
67
68
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)
71 {
72   // Should be protected by lock ??
73   Node::checkValidityOfNodeName(_name);
74   _numId = _total++;
75   idMap[_numId]=this;
76
77   // Every node has an InPropertyPort
78   _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
79 }
80
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)
84 {
85   _numId = _total++;
86   idMap[_numId]=this;
87
88   // Every node has an InPropertyPort
89   _inPropertyPort = new InPropertyPort("__InPropertyPort__Node__YACS_", this, Runtime::_tc_propvec);
90   _eventReceiver=const_cast<Node *>(&other);
91 }
92
93 Node::~Node()
94 {
95   delete _inPropertyPort;
96 }
97
98 /**
99  *  initialisation of all input and output ports and gates, for execution
100  */
101
102 void Node::init(bool start)
103 {
104   _inGate.exReset();
105   _outGate.exReset();
106   if(_state == YACS::DISABLED)
107     {
108       exDisabledState(); // to refresh propagation of DISABLED state 
109       return;
110     }
111   setState(YACS::READY);
112 }
113
114 /*!
115  * This method clones \a this by :
116  *
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.
120  *
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.
125  *
126  * \param [in] father - The new father of the returned clone.
127  * \param [in] editionOnly ignored
128  *
129  * \sa cloneWithoutCompAndContDeepCpy
130  */
131 Node *Node::clone(ComposedNode *father, bool editionOnly) const
132 {
133   Node *ret(simpleClone(father,editionOnly));
134   ret->performDuplicationOfPlacement(*this);
135   return ret;
136 }
137
138 /*!
139  * This method clones \a this by :
140  * - deep copying nodes, links, ports, types
141  * - shallow copy containers
142  * - shallow copy components
143  *
144  * So this method simply ignores isAttachedOnCloning attribute for both containers and components.
145  * So this method is dedicated for the GUI/TUI users.
146  *
147  * \param [in] father - The new father of the returned clone.
148  * \param [in] editionOnly ignored
149  */
150 Node *Node::cloneWithoutCompAndContDeepCpy(ComposedNode *father, bool editionOnly) const
151 {
152   Node *ret(simpleClone(father,editionOnly));
153   ret->performShallowDuplicationOfPlacement(*this);
154   return ret;
155 }
156
157 //! Change the name of the node
158 /*!
159  *  raise an exception if the name is already used in the scope of its father 
160  *  \param name : the new name
161  */
162 void Node::setName(const std::string& name)
163 {
164   Node::checkValidityOfNodeName(name);
165   if(_father)
166     {
167       if(_father->isNameAlreadyUsed(name))
168         {
169           if ( _father->getChildByName(name) != this )
170             {
171               std::string what("Name "); 
172               what+=name;
173               what+=" already exists in the scope of "; what+=_father->getName();
174               throw Exception(what);
175             }
176         }
177     }
178   _name=name;
179 }
180
181 /**
182  *  get the set of all nodes connected to the outGate
183  */
184
185 list<Node *> Node::getOutNodes() const
186 {
187   list<Node *> ret;
188   list<InGate *> inGates=_outGate.edSetInGate();
189   for(list<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
190     ret.push_back((*iter)->getNode());
191   return ret;
192 }
193
194 bool Node::exIsControlReady() const
195 {
196   return _inGate.exIsReady();
197 }
198
199 //! Update the node state
200 /*!
201  * \note : Update the '_state' attribute.
202  *          Typically called by 'this->_inGate' when 'this->_inGate' is ready.
203  *
204  *          Called by InGate::exNotifyFromPrecursor 
205  */
206 void Node::exUpdateState()
207 {
208   if(_state==YACS::DISABLED)return;
209   if(_inGate.exIsReady())
210     setState(YACS::TOACTIVATE);
211 }
212
213 //! Notify this node that its execution has failed
214 /*!
215  * The node goes in FAILED state and 
216  * propagate the notification through the outGate port
217  *
218  */
219 void Node::exFailedState()
220 {
221   DEBTRACE( "Node::exFailedState: " << getName() );
222   setState(YACS::FAILED);
223   _outGate.exNotifyFailed();
224 }
225
226 //! Notify this node that it has been disabled
227 /*!
228  * The node goes in DISABLED state and
229  * propagate the notification through the outGate port
230  *
231  */
232 void Node::exDisabledState()
233 {
234   DEBTRACE( "Node::exDisabledState: " << getName() );
235   setState(YACS::DISABLED);
236   _outGate.exNotifyDisabled();
237 }
238
239 InPort *Node::getInPort(const std::string& name) const
240 {
241   InPort *ret;
242   try
243     {
244       ret=getInputPort(name);
245     }
246   catch(Exception& e)
247     {
248       ret=getInputDataStreamPort(name);
249     }
250   return ret;
251 }
252
253 InPropertyPort *
254 Node::getInPropertyPort() const
255 {
256   return _inPropertyPort;
257 }
258
259 InputPort *
260 Node::getInputPort(const std::string& name) const
261 {
262   if (name == "__InPropertyPort__Node__YACS_")
263     return _inPropertyPort;
264   else
265   {
266     std::string what("Node::getInputPort : the port with name "); what+=name; what+=" does not exist on the current level";
267     throw Exception(what);
268   }
269 }
270
271 /*!
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.
274  */
275 OutPort *Node::getOutPort(const std::string& name) const
276 {
277   OutPort *ret;
278   try
279     {
280       ret=getOutputPort(name);
281     }
282   catch(Exception& e)
283     {
284       ret=getOutputDataStreamPort(name);
285     }
286   return ret;
287 }
288
289 std::list<InPort *> Node::getSetOfInPort() const
290 {
291   list<InPort *> ret;
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());
296   return ret;
297 }
298
299 std::list<OutPort *> Node::getSetOfOutPort() const
300 {
301   list<OutPort *> ret;
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());
306   return ret;
307 }
308
309 /**
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.
315  */
316
317 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
318 {
319   list<ComposedNode *> ret;
320   if(this==levelToStop)
321     return ret;
322   for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
323       ret.push_back(iter);
324   return ret;
325 }
326
327 bool Node::operator>(const Node& other) const
328 {
329   const ComposedNode *iter=other._father;
330   while(iter!=0 && iter!=this)
331     iter=iter->_father;
332   return iter==this;
333 }
334
335 bool Node::operator<(const Node& other) const
336 {
337   const ComposedNode *iter=_father;
338   while(iter!=0 && iter!=(&other))
339     iter=iter->_father;
340   return iter==(&other);
341 }
342
343 /**
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...
348  */
349
350 string Node::getImplementation() const
351 {
352   return _implementation;
353 }
354
355 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
356 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
357 {
358   set<InputPort *> setOfUnitializedInputPort;
359   list<InputPort *> allOfInputPorts=getSetOfInputPort();
360   for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
361     {
362       if ( ! (*iter)->edIsInitialized() )
363         setOfUnitializedInputPort.insert(*iter);
364     }
365   return setOfUnitializedInputPort;
366 }
367
368 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
369 bool Node::edAreAllInputPortInitialized() const
370 {
371   set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
372   return ( setOfUnitializedInputPort.size() == 0);
373 }
374
375 /*!
376  * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
377  */
378 void Node::exForwardFailed()
379 {
380   _outGate.exNotifyFailed();
381 }
382
383 /*!
384  * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
385  */
386 void Node::exForwardFinished()
387 {
388   DEBTRACE("Node::exForwardFinished");
389   _outGate.exNotifyDone();
390 }
391
392 /*!
393  * Called typically by ComposedNode to correctly update DF/CF/DS links
394  */
395 void Node::edDisconnectAllLinksWithMe()
396 {
397   _inGate.edDisconnectAllLinksToMe();
398   _outGate.edDisconnectAllLinksFromMe();
399 }
400
401 Proc *Node::getProc()
402 {
403   if(!_father)
404     return 0;
405   return _father->getProc();
406 }
407
408 const Proc * Node::getProc() const
409 {
410   if(!_father)
411     return 0;
412   return _father->getProc();
413 }
414
415 DynParaLoop *Node::getClosestDPLAmongAncestors() const
416 {
417   if(!_father)
418     return NULL;
419   ComposedNode *iter(_father);
420   do
421     {
422       DynParaLoop *iter2(dynamic_cast<DynParaLoop *>(iter));
423       if(iter2)
424         return iter2;
425       iter=iter->_father;
426     }
427   while(iter);
428   return NULL;
429 }
430
431 ComposedNode *Node::getRootNode() const
432 {
433   if(!_father)
434     throw Exception("No root node");
435   ComposedNode *iter=_father;
436   while(iter->_father)
437     iter=iter->_father;
438   return (ComposedNode *)iter;
439 }
440
441 /**
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 ?
444  */
445
446 void Node::checkValidityOfPortName(const std::string& name)
447 {
448   if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
449     {
450       string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
451       throw Exception(what);
452     }
453 }
454
455 void Node::checkValidityOfNodeName(const std::string& name)
456 {
457   if(name.find(ComposedNode::SEP_CHAR_BTW_LEVEL,0)!=string::npos)
458     {
459       string what("Node name "); what+=name; what+="not valid because it contains character "; what+=ComposedNode::SEP_CHAR_BTW_LEVEL;
460       throw Exception(what);
461     }
462 }
463
464 /**
465  * @note : Check that 'node1' and 'node2' have exactly the same father
466  * @exception : If 'node1' and 'node2' have NOT exactly the same father
467  */
468 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2)
469 {
470   if(node1!=0 && node2!=0)
471     {
472       if(node1->_father==node2->_father)
473         return node1->_father;
474     }
475   throw Exception("check failed : nodes have not the same father");
476 }
477
478 const std::string Node::getId() const
479 {
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){
485         id[debut]='_';
486         debut=id.find_first_of('.',debut);
487     }
488     return id;
489 }
490
491 void Node::setProperty(const std::string& name, const std::string& value)
492 {
493     DEBTRACE("Node::setProperty " << name << " " << value);
494     _propertyMap[name]=value;
495 }
496
497 std::string Node::getProperty(const std::string& name)
498 {
499   std::map<std::string,std::string>::iterator it=_propertyMap.find(name);
500
501   if(it != _propertyMap.end())
502     return it->second;
503   else if(_father)
504     return _father->getProperty(name);
505   else
506     return "";
507 }
508
509 std::map<std::string,std::string> Node::getProperties()
510 {
511   std::map<std::string,std::string> amap=_propertyMap;
512   if(_father)
513     {
514       std::map<std::string,std::string> fatherMap=_father->getProperties();
515       amap.insert(fatherMap.begin(),fatherMap.end());
516     }
517
518   return amap;
519 }
520
521 void Node::setProperties(std::map<std::string,std::string> properties)
522 {
523   _propertyMap.clear();
524   _propertyMap=properties;
525 }
526
527 //! Return the node state in the context of its father
528 /*!
529  * \return the effective node state
530  *
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.
537  */
538 YACS::StatesForNode Node::getEffectiveState() const
539 {
540   if(!_father)   //the root node
541     return _state;
542   if(_state==YACS::DISABLED)
543     return YACS::DISABLED;
544   return _father->getEffectiveState(this);
545 }
546
547 //! Return the effective state of a node in the context of this one (its father)
548 /*!
549  * \param node: the node which effective state is queried
550  * \return the effective node state
551  */
552 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
553 {
554   if(node->getState()==YACS::DISABLED)
555     return YACS::DISABLED;
556
557   YACS::StatesForNode effectiveState=getEffectiveState();
558   switch(effectiveState)
559     {
560     case YACS::READY:
561       return YACS::READY;
562     case YACS::TOACTIVATE:
563       return YACS::READY;
564     case YACS::DISABLED:
565       return YACS::DISABLED;
566     case YACS::ERROR:
567       return YACS::FAILED;
568     default:
569       return node->getState();
570     }
571 }
572
573 //! Return the color associated to a state
574 /*!
575  * \param state : the node state
576  * \return the associated color
577  */
578 std::string Node::getColorState(YACS::StatesForNode state) const
579 {
580   switch(state)
581     {
582     case YACS::READY:
583       return "pink";
584     case YACS::TOLOAD:
585       return "magenta";
586     case YACS::LOADED:
587       return "magenta";
588     case YACS::TOACTIVATE:
589       return "purple";
590     case YACS::ACTIVATED:
591       return "blue";
592     case YACS::DONE:
593       return "green";
594     case YACS::ERROR:
595       return "red";
596     case YACS::FAILED:
597       return "orange";
598     case YACS::DISABLED:
599       return "grey";
600     case YACS::PAUSE:
601       return "white";
602     default:
603       return "white";
604     }
605 }
606
607 //! Dump to the input stream a dot representation of the node
608 /*!
609  *  \param os : the input stream
610  */
611 void Node::writeDot(std::ostream &os) const
612 {
613   os << getId() << "[fillcolor=\"" ;
614   YACS::StatesForNode state=getEffectiveState();
615   os << getColorState(state);
616   os << "\" label=\"" << getImplementation() << "Node:" ;
617   os << getQualifiedName() <<"\"];\n";
618 }
619
620 void Node::writeDotInFile(const std::string& fileName) const
621 {
622   std::ofstream f(fileName);
623   this->writeDot(f);
624 }
625
626 //! same as Node::getName() in most cases, but differs for children of switch
627 /*!
628  *  used by writeDot to distinguish children of switch, by adding a prefix to the name.
629  *  prefix is built on case id.
630  */
631
632 std::string Node::getQualifiedName() const
633 {
634   if(_father)
635     return _father->getMyQualifiedName(this);
636   return getName();
637 }
638
639 //! return node instance identifiant, unique for each node instance 
640 /*!
641  * node instance identifiant is used to check if to nodes pointers refers to the same instance
642  */ 
643 int Node::getNumId()
644 {
645   return _numId;
646 }
647
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 = ...).
651  */
652 void Node::setState(YACS::StatesForNode theState)
653 {
654   DEBTRACE("Node::setState: " << getName() << " " << theState);
655   _state = theState;
656   // emit notification to all observers registered with the dispatcher on any change of the node's state
657   sendEvent("status");
658 }
659
660 std::vector<std::pair<std::string,int> > Node::getDPLScopeInfo(ComposedNode *gfn)
661
662   std::vector< std::pair<std::string,int> > ret;
663   Node *work2(this);
664   ComposedNode *work(getFather());
665   while(work!=gfn && work!=0)
666     {
667       DynParaLoop *workc(dynamic_cast<DynParaLoop *>(work));
668       if(workc)
669         {
670           std::pair<std::string,int> p(gfn->getChildName(workc),workc->getBranchIDOfNode(work2));
671           ret.push_back(p);
672         }
673       work2=work;
674       work=work->getFather();
675     }
676   return ret;
677 }
678
679 /*!
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.
682  */
683 void Node::applyDPLScope(ComposedNode *gfn)
684 {
685 }
686
687 //! emit notification to all observers registered with  the dispatcher 
688 /*!
689  * The dispatcher is unique and can be obtained by getDispatcher()
690  */
691 void Node::sendEvent(const std::string& event)
692 {
693   DEBTRACE("Node::sendEvent " << event);
694   Dispatcher *disp(Dispatcher::getDispatcher());
695   if(!_eventReceiver)
696     {
697       disp->dispatch(this,event);
698     }
699   else
700     {
701       disp->dispatchFromClone(_eventReceiver,event,this);
702     }
703 }
704
705 //! emit notification to all observers registered with  the dispatcher 
706 /*!
707  * The dispatcher is unique and can be obtained by getDispatcher()
708  */
709 void Node::sendEvent2(const std::string& event, void *something)
710 {
711   Dispatcher* disp=Dispatcher::getDispatcher();
712   disp->dispatch2(this,event,something);
713 }
714
715 /*!
716  *  For use only when loading a previously saved execution
717  */
718
719 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
720 {
721   node->setState(state);
722 }
723
724 //! indicates if the node is valid (returns 1) or not (returns 0)
725 /*!
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
729  */
730 int Node::isValid()
731 {
732   if(_modified)
733     edUpdateState();
734   if(_state > YACS::INVALID)
735     return 1;
736   else
737     return 0;
738 }
739
740 //! update the status of the node
741 /*!
742  * Only useful when editing a schema
743  * Do nothing in base Node : to implement in derived classes
744  */
745 void Node::edUpdateState()
746 {
747   DEBTRACE("Node::edUpdateState(): " << _modified);
748   _modified=0;
749 }
750
751 //! returns a string that contains an error report if the node is in error
752 /*!
753  * 
754  */
755 std::string Node::getErrorReport()
756 {
757   if(getState()==YACS::DISABLED)
758     return "<error node= "+getName()+ "state= DISABLED/>\n";
759
760   YACS::StatesForNode effectiveState=getEffectiveState();
761
762   DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
763   if(effectiveState != YACS::INVALID &&  effectiveState != YACS::ERROR && 
764      effectiveState != YACS::FAILED && effectiveState != YACS::INTERNALERR)
765     return "";
766
767   std::string report="<error node= " ;
768   report=report + getName() ;
769   switch(effectiveState)
770     {
771     case YACS::INVALID:
772       report=report+" state= INVALID";
773       break;
774     case YACS::ERROR:
775       report=report+" state= ERROR";
776       break;
777     case YACS::FAILED:
778       report=report+" state= FAILED";
779       break;
780     case YACS::INTERNALERR:
781       report=report+" state= INTERNALERR";
782       break;
783     default:
784       break;
785     }
786   report=report + ">\n" ;
787   report=report+_errorDetails;
788   report=report+"\n</error>";
789   return report;
790 }
791
792 //! returns a string that contains the name of the container log file if it exists
793 /*!
794  * Do nothing here. To subclass
795  */
796 std::string Node::getContainerLog()
797 {
798   return "";
799 }
800
801 //! Sets Node in modified state and its father if it exists
802 /*!
803  * 
804  */
805 void Node::modified()
806 {
807   DEBTRACE("Node::modified() " << getName());
808   _modified=1;
809   if(_father)
810     _father->modified();
811 }
812
813 //! Put this node into TOLOAD state when possible
814 /*!
815  * 
816  */
817 void Node::ensureLoading()
818 {
819   if(_state == YACS::READY)
820     setState(YACS::TOLOAD);
821 }
822
823 //! Return the name of a state
824 /*!
825  * 
826  */
827 std::string Node::getStateName(YACS::StatesForNode state)
828 {
829   static NodeStateNameMap nodeStateNameMap;
830   return nodeStateNameMap[state];
831 }
832
833 //! Stop all pending activities of the node
834 /*!
835  * This method should be called when a Proc is finished and must be deleted from the YACS server
836  */
837 void Node::shutdown(int level)
838 {
839   if(level==0)return;
840 }
841
842 //! Clean the node in case of not clean exit
843 /*!
844  * This method should be called on a control-C or sigterm
845  */
846 void Node::cleanNodes()
847 {
848 }
849
850 //! Reset the node state depending on the parameter level
851 void Node::resetState(int level)
852 {
853   DEBTRACE("Node::resetState " << getName() << "," << level << "," << _state);
854   if(_state==YACS::ERROR || _state==YACS::FAILED || _state==YACS::ACTIVATED)
855     {
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++)
860         {
861           Node* fromNode = (*io)->getNode();
862           if(fromNode->getState() == YACS::DONE)
863             {
864               inGate->setPrecursorDone(*io);
865             }
866         }
867     }
868 }