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