]> SALOME platform Git repositories - modules/yacs.git/blob - src/engine/Node.cxx
Salome HOME
merge from branch DEV tag mergeto_trunk_04apr08
[modules/yacs.git] / src / engine / Node.cxx
1 #include "Node.hxx"
2 #include "InputPort.hxx"
3 #include "OutputPort.hxx"
4 #include "ComposedNode.hxx"
5 #include "Dispatcher.hxx"
6 #include "InputDataStreamPort.hxx"
7 #include "OutputDataStreamPort.hxx"
8 #include <iostream>
9
10 //#define _DEVDEBUG_
11 #include "YacsTrace.hxx"
12
13 using namespace YACS::ENGINE;
14 using namespace std;
15
16 const char Node::SEP_CHAR_IN_PORT[]=".";
17
18 int Node::_total = 0;
19 std::map<int,Node*> Node::idMap;
20
21 Node::Node(const std::string& name):_name(name),_inGate(this),_outGate(this),_father(0),_state(YACS::READY),
22                                     _implementation(Runtime::RUNTIME_ENGINE_INTERACTION_IMPL_NAME),_modified(1)
23 {
24   // Should be protected by lock ??
25   _numId = _total++;
26   idMap[_numId]=this;
27 }
28
29 Node::Node(const Node& other, ComposedNode *father):_inGate(this),_outGate(this),_name(other._name),_father(father),
30                                                    _state(YACS::READY),_implementation(other._implementation),
31                                                     _propertyMap(other._propertyMap),_modified(other._modified)
32 {
33   _numId = _total++;
34   idMap[_numId]=this;
35 }
36
37 Node::~Node()
38 {
39 }
40
41 /**
42  *  initialisation of all input and output ports and gates, for execution
43  */
44
45 void Node::init(bool start)
46 {
47   _inGate.exReset();
48   _outGate.exReset();
49   if(_state == YACS::DISABLED)
50     {
51       exDisabledState(); // to refresh propagation of DISABLED state 
52       return;
53     }
54   setState(YACS::READY);
55 }
56
57 Node *Node::clone(ComposedNode *father, bool editionOnly) const
58 {
59   Node *ret=simpleClone(father,editionOnly);
60   ret->performDuplicationOfPlacement(*this);
61   return ret;
62 }
63
64 //! Change the name of the node
65 /*!
66  *  raise an exception if the name is already used in the scope of its father 
67  *  \param name : the new name
68  */
69 void Node::setName(const std::string& name)
70 {
71   if(_father)
72     {
73       if(_father->isNameAlreadyUsed(name))
74         {
75           if ( _father->getChildByName(name) != this )
76             {
77               std::string what("Name "); 
78               what+=name;
79               what+=" already exists in the scope of "; what+=_father->getName();
80               throw Exception(what);
81             }
82         }
83     }
84   _name=name;
85 }
86
87 /**
88  *  get the set of all nodes connected to the outGate
89  */
90
91 set<Node *> Node::getOutNodes() const
92 {
93   set<Node *> ret;
94   set<InGate *> inGates=_outGate.edSetInGate();
95   for(set<InGate *>::iterator iter=inGates.begin();iter!=inGates.end();iter++)
96     ret.insert((*iter)->getNode());
97   return ret;
98 }
99
100 bool Node::exIsControlReady() const
101 {
102   return _inGate.exIsReady();
103 }
104
105 //! Update the node state
106 /*!
107  * \note : Update the '_state' attribute.
108  *          Typically called by 'this->_inGate' when 'this->_inGate' is ready.
109  *
110  *          Called by InGate::exNotifyFromPrecursor 
111  */
112 void Node::exUpdateState()
113 {
114   if(_state==YACS::DISABLED)return;
115   if(_inGate.exIsReady())
116     setState(YACS::TOACTIVATE);
117 }
118
119 //! Notify this node that its execution has failed
120 /*!
121  * The node goes in FAILED state and 
122  * propagate the notification through the outGate port
123  *
124  */
125 void Node::exFailedState()
126 {
127   DEBTRACE( "Node::exFailedState: " << getName() );
128   setState(YACS::FAILED);
129   _outGate.exNotifyFailed();
130 }
131
132 //! Notify this node that it has been disabled
133 /*!
134  * The node goes in DISABLED state and
135  * propagate the notification through the outGate port
136  *
137  */
138 void Node::exDisabledState()
139 {
140   DEBTRACE( "Node::exDisabledState: " << getName() );
141   setState(YACS::DISABLED);
142   _outGate.exNotifyDisabled();
143 }
144
145 InPort *Node::getInPort(const std::string& name) const throw(Exception)
146 {
147   InPort *ret;
148   try
149     {
150       ret=getInputPort(name);
151     }
152   catch(Exception& e)
153     {
154       ret=getInputDataStreamPort(name);
155     }
156   return ret;
157 }
158
159 /*!
160  * \note: Contrary to getOutputPort method, this method returns the output port at highest level, possible.
161  *        That is to say in some ComposedNode, like ForEachLoop or Switch, an outport inside 'this' is seen differently than the true outport.
162  */
163 OutPort *Node::getOutPort(const std::string& name) const throw(Exception)
164 {
165   OutPort *ret;
166   try
167     {
168       ret=getOutputPort(name);
169     }
170   catch(Exception& e)
171     {
172       ret=getOutputDataStreamPort(name);
173     }
174   return ret;
175 }
176
177 std::list<InPort *> Node::getSetOfInPort() const
178 {
179   list<InPort *> ret;
180   list<InputPort *> data=getSetOfInputPort();
181   ret.insert(ret.end(),data.begin(),data.end());
182   list<InputDataStreamPort *> ds=getSetOfInputDataStreamPort();
183   ret.insert(ret.end(),ds.begin(),ds.end());
184   return ret;
185 }
186
187 std::list<OutPort *> Node::getSetOfOutPort() const
188 {
189   list<OutPort *> ret;
190   list<OutputPort *> data=getSetOfOutputPort();
191   ret.insert(ret.end(),data.begin(),data.end());
192   list<OutputDataStreamPort *> ds=getSetOfOutputDataStreamPort();
193   ret.insert(ret.end(),ds.begin(),ds.end());
194   return ret;
195 }
196
197 /**
198  * gets a set of the composed nodes that constitute the ascendancy of this node, starting from root
199  * or from a particular ancestor
200  * \b WARNING : returned set is not sorted !
201  * @param  levelToStop   composed node which is the oldest ancestor required
202  * @return               ascendancy, direct father first in set.
203  */
204
205 std::list<ComposedNode *> Node::getAllAscendanceOf(ComposedNode *levelToStop) const
206 {
207   list<ComposedNode *> ret;
208   if(this==levelToStop)
209     return ret;
210   for(ComposedNode *iter=_father;iter!=levelToStop && iter!=0; iter=iter->_father)
211       ret.push_back(iter);
212   return ret;
213 }
214
215 bool Node::operator>(const Node& other) const
216 {
217   const ComposedNode *iter=other._father;
218   while(iter!=0 && iter!=this)
219     iter=iter->_father;
220   return iter==this;
221 }
222
223 bool Node::operator<(const Node& other) const
224 {
225   const ComposedNode *iter=_father;
226   while(iter!=0 && iter!=(&other))
227     iter=iter->_father;
228   return iter==(&other);
229 }
230
231 /**
232  *  @return Implementation of node: C++, Python, CORBA...
233  *  _implementation is set by a derived class in a Runtime
234  *  it normally applies only to elementaryNodes and it is used by Ports to select Data Converters.
235  *  Potential problem with Ports attached to composed Nodes...
236  */
237
238 string Node::getImplementation() const
239 {
240   return _implementation;
241 }
242
243 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
244 set<InputPort *> Node::edGetSetOfUnitializedInputPort() const
245 {
246   set<InputPort *> setOfUnitializedInputPort;
247   list<InputPort *> allOfInputPorts=getSetOfInputPort();
248   for(list<InputPort *>::const_iterator iter=allOfInputPorts.begin();iter!=allOfInputPorts.end();iter++)
249     {
250       if ( ! (*iter)->edIsInitialized() )
251         setOfUnitializedInputPort.insert(*iter);
252     }
253   return setOfUnitializedInputPort;
254 }
255
256 //! Becomes deprecated soon. Replaced by ComposedNode::CheckConsistency.
257 bool Node::edAreAllInputPortInitialized() const
258 {
259   set<InputPort *> setOfUnitializedInputPort = edGetSetOfUnitializedInputPort();
260   return ( setOfUnitializedInputPort.size() == 0);
261 }
262
263 /*!
264  * Called typically by Bloc to notify failure on potentially next nodes on the same scope of 'this'
265  */
266 void Node::exForwardFailed()
267 {
268   _outGate.exNotifyFailed();
269 }
270
271 /*!
272  * Called typically by Bloc to activate potentially next nodes on the same scope of 'this'
273  */
274 void Node::exForwardFinished()
275 {
276   _outGate.exNotifyDone();
277 }
278
279 /*!
280  * Called typically by ComposedNode to correctly update DF/CF/DS links
281  */
282 void Node::edDisconnectAllLinksWithMe()
283 {
284   _inGate.edDisconnectAllLinksToMe();
285   _outGate.edDisconnectAllLinksFromMe();
286 }
287
288 ComposedNode *Node::getRootNode() const throw(Exception)
289 {
290   if(!_father)
291     throw Exception("No root node");
292   ComposedNode *iter=_father;
293   while(iter->_father)
294     iter=iter->_father;
295   return (ComposedNode *)iter;
296 }
297
298 /**
299  * checks validity of ports name, that must not contain a particular character '?'
300  * USAGE NOT CLEAR, not used so far, when are those characters set ?
301  */
302
303 void Node::checkValidityOfPortName(const std::string& name) throw(Exception)
304 {
305   if(name.find(SEP_CHAR_IN_PORT, 0 )!=string::npos)
306     {
307       string what("Port name "); what+=name; what+="not valid because it contains character "; what+=SEP_CHAR_IN_PORT;
308       throw Exception(what);
309     }
310 }
311
312 /**
313  * @note : Check that 'node1' and 'node2' have exactly the same father
314  * @exception : If 'node1' and 'node2' have NOT exactly the same father
315  */
316 ComposedNode *Node::checkHavingCommonFather(Node *node1, Node *node2) throw(Exception)
317 {
318   if(node1!=0 && node2!=0)
319     {
320       if(node1->_father==node2->_father)
321         return node1->_father;
322     }
323   throw Exception("check failed : nodes have not the same father");
324 }
325
326 const std::string Node::getId() const
327 {
328     std::string id=getRootNode()->getName();
329     if(getRootNode() != this)
330       id= id+'.'+ getRootNode()->getChildName(this);
331     string::size_type debut =id.find_first_of('.');
332     while(debut != std::string::npos){
333         id[debut]='_';
334         debut=id.find_first_of('.',debut);
335     }
336     return id;
337 }
338
339 void Node::setProperty(const std::string& name, const std::string& value)
340 {
341     _propertyMap[name]=value;
342 }
343
344 //! Return the node state in the context of its father
345 /*!
346  * \return the effective node state
347  *
348  * The node state is stored in a private attribute _state.
349  * This state is relative to its father state : a node with a
350  * TOACTIVATE state with a father node in a READY state is not
351  * to activate. Its effective state is only READY.
352  * This method returns the effective state of the node taking
353  * into account that of its father.
354  */
355 YACS::StatesForNode Node::getEffectiveState() const
356 {
357   if(!_father)   //the root node
358     return _state;
359   if(_state==YACS::DISABLED)
360     return YACS::DISABLED;
361   return _father->getEffectiveState(this);
362 }
363
364 //! Return the effective state of a node in the context of this one (its father)
365 /*!
366  * \param node: the node which effective state is queried
367  * \return the effective node state
368  */
369 YACS::StatesForNode Node::getEffectiveState(const Node* node) const
370 {
371   if(node->getState()==YACS::DISABLED)
372     return YACS::DISABLED;
373
374   YACS::StatesForNode effectiveState=getEffectiveState();
375   switch(effectiveState)
376     {
377     case YACS::READY:
378       return YACS::READY;
379     case YACS::TOACTIVATE:
380       return YACS::READY;
381     case YACS::DISABLED:
382       return YACS::DISABLED;
383     case YACS::ERROR:
384       return YACS::FAILED;
385     default:
386       return node->getState();
387     }
388 }
389
390 //! Return the color associated to a state
391 /*!
392  * \param state : the node state
393  * \return the associated color
394  */
395 std::string Node::getColorState(YACS::StatesForNode state) const
396 {
397   switch(state)
398     {
399     case YACS::READY:
400       return "pink";
401     case YACS::TOLOAD:
402       return "magenta";
403     case YACS::LOADED:
404       return "magenta";
405     case YACS::TOACTIVATE:
406       return "purple";
407     case YACS::ACTIVATED:
408       return "blue";
409     case YACS::DONE:
410       return "green";
411     case YACS::ERROR:
412       return "red";
413     case YACS::FAILED:
414       return "orange";
415     case YACS::DISABLED:
416       return "grey";
417     case YACS::PAUSE:
418       return "white";
419     default:
420       return "white";
421     }
422 }
423
424 //! Dump to the input stream a dot representation of the node
425 /*!
426  *  \param os : the input stream
427  */
428 void Node::writeDot(std::ostream &os) const
429 {
430   os << getId() << "[fillcolor=\"" ;
431   YACS::StatesForNode state=getEffectiveState();
432   os << getColorState(state);
433   os << "\" label=\"" << getImplementation() << "Node:" ;
434   os << getQualifiedName() <<"\"];\n";
435 }
436
437 //! same as Node::getName() in most cases, but differs for children of switch
438 /*!
439  *  used by writeDot to distinguish children of switch, by adding a prefix to the name.
440  *  prefix is built on case id.
441  */
442
443 std::string Node::getQualifiedName() const
444 {
445   if(_father)
446     return _father->getMyQualifiedName(this);
447   return getName();
448 }
449
450 //! return node instance identifiant, unique for each node instance 
451 /*!
452  * node instance identifiant is used to check if to nodes pointers refers to the same instance
453  */ 
454 int Node::getNumId()
455 {
456   return _numId;
457 }
458
459 //! Sets the given state for node.
460 /*! It is strongly recommended to use this function if you want to
461  *  change the state of the node, instead of direct access to _state field (_state = ...).
462  */
463 void Node::setState(YACS::StatesForNode theState)
464 {
465   DEBTRACE("Node::setState: " << getName() << " " << theState);
466   _state = theState;
467   // emit notification to all observers registered with the dispatcher on any change of the node's state
468   sendEvent("status");
469 }
470
471 //! emit notification to all observers registered with  the dispatcher 
472 /*!
473  * The dispatcher is unique and can be obtained by getDispatcher()
474  */
475 void Node::sendEvent(const std::string& event)
476 {
477   DEBTRACE("Node::sendEvent " << event);
478   Dispatcher* disp=Dispatcher::getDispatcher();
479   disp->dispatch(this,event);
480 }
481
482 /*!
483  *  For use only when loading a previously saved execution
484  */
485
486 void YACS::ENGINE::StateLoader(Node* node, YACS::StatesForNode state)
487 {
488   node->setState(state);
489 }
490
491 //! indicates if the node is valid (returns 1) or not (returns 0)
492 /*!
493  * This method is useful when editing a schema. It has no meaning in execution.
494  * When a node is edited, its modified method must be called so when isValid is called, its state
495  * is updated (call to edUpdateState) before returning the validity check
496  */
497 int Node::isValid()
498 {
499   if(_modified)
500     edUpdateState();
501   if(_state > YACS::INVALID)
502     return 1;
503   else
504     return 0;
505 }
506
507 //! update the status of the node
508 /*!
509  * Only useful when editing a schema
510  * Do nothing in base Node : to implement in derived classes
511  */
512 void Node::edUpdateState()
513 {
514   DEBTRACE("Node::edUpdateState(): " << _modified);
515   _modified=0;
516 }
517
518 //! returns a string that contains an error report if the node is in error
519 /*!
520  * 
521  */
522 std::string Node::getErrorReport()
523 {
524   if(getState()==YACS::DISABLED)
525     return "<error node= "+getName()+ "state= DISABLED/>\n";
526
527   YACS::StatesForNode effectiveState=getEffectiveState();
528
529   DEBTRACE("Node::getErrorReport: " << getName() << " " << effectiveState << " " << _errorDetails);
530   if(effectiveState == YACS::READY || effectiveState == YACS::DONE)
531     return "";
532
533   std::string report="<error node= " ;
534   report=report + getName() ;
535   switch(effectiveState)
536     {
537     case YACS::INVALID:
538       report=report+" state= INVALID";
539       break;
540     case YACS::ERROR:
541       report=report+" state= ERROR";
542       break;
543     case YACS::FAILED:
544       report=report+" state= FAILED";
545       break;
546     default:
547       break;
548     }
549   report=report + ">\n" ;
550   report=report+_errorDetails;
551   report=report+"\n</error>";
552   return report;
553 }
554
555 //! returns a string that contains the name of the container log file if it exists
556 /*!
557  * Do nothing here. To subclass
558  */
559 std::string Node::getContainerLog()
560 {
561   return "";
562 }
563
564 //! Sets Node in modified state and its father if it exists
565 /*!
566  * 
567  */
568 void Node::modified()
569 {
570   DEBTRACE("Node::modified() " << getName());
571   _modified=1;
572   if(_father)
573     _father->modified();
574 }
575
576 //! Put this node into TOLOAD state when possible
577 /*!
578  * 
579  */
580 void Node::ensureLoading()
581 {
582   if(_state == YACS::READY)
583     setState(YACS::TOLOAD);
584 }