Salome HOME
Merge branch 'omu/py2yacs'
[modules/yacs.git] / src / yacsloader / driver.cxx
1 // Copyright (C) 2006-2016  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 "yacsconfig.h"
21 #include "RuntimeSALOME.hxx"
22 #include "Proc.hxx"
23 #include "Logger.hxx"
24 #include "Exception.hxx"
25 #include "Executor.hxx"
26 #include "parsers.hxx"
27 #include "VisitorSaveState.hxx"
28 #include "VisitorSaveSalomeSchema.hxx"
29 #include "LoadState.hxx"
30 #include "Dispatcher.hxx"
31 #include "LinkInfo.hxx"
32
33 #ifdef SALOME_KERNEL
34 #include "SALOME_NamingService.hxx"
35 #include "SALOME_ModuleCatalog.hh"
36 #include "Basics_Utils.hxx"
37 #endif
38
39 #include <iostream>
40 #include <fstream>
41 #include <signal.h>
42 #include <list>
43
44 #if defined WIN32 || defined __APPLE__
45 #else
46 #include <argp.h>
47 #endif
48
49 using YACS::YACSLoader;
50 using namespace YACS::ENGINE;
51 using namespace std;
52
53
54 // --- use of glibc argp interface for parsing unix-style arguments
55
56 const char *argp_program_version ="driver V0.1";
57 const char *argp_program_bug_address ="<nepal@nepal.edf.fr>";
58 static char doc[] ="driver -- a SALOME YACS graph executor";
59 static char args_doc[] = "graph.xml";
60
61 #if defined WIN32 || defined __APPLE__
62 #else
63 static struct argp_option options[] =
64   {
65     {"display",         'd', "level", 0,                   "Display dot files: 0=never to 3=very often (default 0)"},
66     {"verbose",         'v', 0,       0,                   "Produce verbose output" },
67     {"stop-on-error",   's', 0,       0,                   "Stop on first error" },
68     {"dump-on-error",   'e', "file",  OPTION_ARG_OPTIONAL, "Stop on first error and dump state"},
69     {"dump-final",      'f', "file",  OPTION_ARG_OPTIONAL, "dump final state"},
70     {"dump",            'g', "nbsec", OPTION_ARG_OPTIONAL, "dump state"},
71     {"load-state",      'l', "file",  0,                   "Load State from a previous partial execution"},
72     {"save-xml-schema", 'x', "file",  OPTION_ARG_OPTIONAL, "dump xml schema"},
73     {"shutdown",        't', "level", 0,                   "Shutdown the schema: 0=no shutdown to 3=full shutdown (default 1)"},
74     {"reset",           'r', "level", 0,                   "Reset the schema before execution: 0=nothing, 1=reset error nodes to ready state (default 0)"},
75     {"kill-port",       'k', "port",  0,                   "Kill Salome application running on the specified port if the driver process is killed (with SIGINT or SIGTERM)"},
76     {"init_port",       'i', "value", OPTION_ARG_OPTIONAL, "Initialisation value of a port, specified as bloc.node.port=value."},
77     { 0 }
78   };
79 #endif
80
81 struct arguments
82 {
83   char *args[1];
84   int display;
85   int verbose;
86   int stop;
87   char *dumpErrorFile;
88   char *finalDump;
89   int dump;
90   char *xmlSchema;
91   char *loadState;
92   int shutdown;
93   int reset;
94   int killPort;
95   std::list<std::string> init_ports;
96 };
97
98 typedef struct {
99   int nbsec;
100   string dumpFile;
101   string lockFile;
102 } thread_st;
103
104 #ifndef WIN32
105 #include <dlfcn.h>
106 #include <stdlib.h>
107 #endif
108
109 std::string LoadedDriverPluginLibrary;
110 void *HandleOnLoadedPlugin=0;
111 void (*DefineCustomObservers)(YACS::ENGINE::Dispatcher *, YACS::ENGINE::ComposedNode *, YACS::ENGINE::Executor *)=0;
112 void (*CleanUpObservers) ()=0;
113
114 void LoadObserversPluginIfAny(YACS::ENGINE::ComposedNode *rootNode, YACS::ENGINE::Executor *executor)
115 {
116   static const char SYMBOLE_NAME_1[]="DefineCustomObservers";
117   static const char SYMBOLE_NAME_2[]="CleanUpObservers";
118 #ifndef WIN32
119   Dispatcher *disp(Dispatcher::getDispatcher());
120   if(!disp)
121     throw YACS::Exception("Internal error ! No dispatcher !");
122   char *yacsDriverPluginPath(getenv("YACS_DRIVER_PLUGIN_PATH"));
123   if(!yacsDriverPluginPath)
124     return ;
125   void *handle(dlopen(yacsDriverPluginPath, RTLD_LAZY | RTLD_GLOBAL));
126   if(!handle)
127     {
128       std::string message(dlerror());
129       std::ostringstream oss; oss << "Error during load of \"" << yacsDriverPluginPath << "\" defined by the YACS_DRIVER_PLUGIN_PATH env var : " << message;
130       throw YACS::Exception(oss.str());
131     }
132   DefineCustomObservers=(void (*)(YACS::ENGINE::Dispatcher *, YACS::ENGINE::ComposedNode *, YACS::ENGINE::Executor *))(dlsym(handle,SYMBOLE_NAME_1));
133   if(!DefineCustomObservers)
134     {
135       std::ostringstream oss; oss << "Error during load of \"" << yacsDriverPluginPath << "\" ! Library has been correctly loaded but symbol " << SYMBOLE_NAME_1 << " does not exists !";
136       throw YACS::Exception(oss.str());
137     }
138   CleanUpObservers=(void (*)())(dlsym(handle,SYMBOLE_NAME_2));
139   if(!CleanUpObservers)
140     {
141       std::ostringstream oss; oss << "Error during load of \"" << yacsDriverPluginPath << "\" ! Library has been correctly loaded but symbol " << SYMBOLE_NAME_2 << " does not exists !";
142       throw YACS::Exception(oss.str());
143     }
144   HandleOnLoadedPlugin=handle;
145   DefineCustomObservers(disp,rootNode,executor);
146 #endif
147 }
148
149 void UnLoadObserversPluginIfAny()
150 {
151 #ifndef WIN32
152   if(HandleOnLoadedPlugin)
153     {
154       CleanUpObservers();
155       dlclose(HandleOnLoadedPlugin);
156     }
157 #endif
158 }
159
160 #if defined WIN32 || defined __APPLE__
161 static int
162 #else
163 static error_t
164 #endif
165 parse_opt (int key, char *arg, struct argp_state *state)
166 {
167 #if defined WIN32 || defined __APPLE__
168 #else
169   // Get the input argument from argp_parse, which we
170   // know is a pointer to our arguments structure. 
171   struct arguments *myArgs = (arguments*)state->input;
172   
173   switch (key)
174     {
175     case 'd':
176       myArgs->display = atoi(arg);
177       break;
178     case 't':
179       myArgs->shutdown = atoi(arg);
180       break;
181     case 'r':
182       myArgs->reset = atoi(arg);
183       break;
184     case 'v':
185       myArgs->verbose = 1;
186       break;
187     case 's':
188       myArgs->stop = 1;
189       break;
190     case 'e':
191       myArgs->stop = 1;
192       if (arg)
193         myArgs->dumpErrorFile = arg;
194       else
195         myArgs->dumpErrorFile = (char *)"dumpErrorState.xml";
196       break;
197     case 'f':
198       if (arg)
199         myArgs->finalDump = arg;
200       else
201         myArgs->finalDump = (char *)"finalDumpState.xml";
202       break;      
203     case 'g':
204       if (arg)
205         myArgs->dump = atoi(arg);
206       else
207         myArgs->dump = 60;
208       break;      
209     case 'l':
210       myArgs->loadState = arg;
211       break;
212     case 'x':
213       if (arg)
214         myArgs->xmlSchema = arg;
215       else
216         myArgs->xmlSchema = (char *)"saveSchema.xml";
217       break;      
218     case 'k':
219       myArgs->killPort = atoi(arg);
220       break;
221     case 'i':
222       if (arg)
223         myArgs->init_ports.push_back(std::string(arg));
224       break;
225
226     case ARGP_KEY_ARG:
227       if (state->arg_num >=1) // Too many arguments.
228         argp_usage (state);
229       myArgs->args[state->arg_num] = arg;
230       break;
231       
232     case ARGP_KEY_END:
233       if (state->arg_num < 1) // Not enough arguments.
234         argp_usage (state);
235       break;
236      
237     default:
238       return ARGP_ERR_UNKNOWN;
239     }
240 #endif
241   return 0;
242 }
243
244 // Our argp parser.
245 #if defined WIN32 || defined __APPLE__
246 #else
247 static struct argp argp = { options, parse_opt, args_doc, doc };
248 #endif
249
250 void timer(std::string msg)
251 {
252 #if defined WIN32 || defined __APPLE__
253 #else
254   struct timeval tv;
255   gettimeofday(&tv,NULL);
256   long t=tv.tv_sec*1000+tv.tv_usec/1000;
257   static long t0=t;
258   gettimeofday(&tv,NULL);
259   std::cerr << msg << tv.tv_sec*1000+tv.tv_usec/1000-t0 << " ms" << std::endl;
260 #endif
261 }
262
263 Proc* p=0;
264 static struct arguments myArgs;
265
266 void Handler(int theSigId)
267 {
268   if(p)
269     {
270       p->cleanNodes();
271       //if requested save state
272       bool isFinalDump = (strlen(myArgs.finalDump) != 0);
273       if (isFinalDump)
274         {
275           YACS::ENGINE::VisitorSaveState vst(p);
276           vst.openFileDump(myArgs.finalDump);
277           p->accept(&vst);
278           vst.closeFileDump();
279         }
280       //if requested shutdown schema
281       if(myArgs.shutdown < 999)
282         {
283           p->shutdown(myArgs.shutdown);
284         }
285     }
286   if (myArgs.killPort)
287     {
288       ostringstream command;
289       command << "killSalomeWithPort.py " << myArgs.killPort;
290       int status = system(command.str().c_str());
291       if (status == 0)
292         cerr << "Salome application on port " << myArgs.killPort << " is killed" << endl;
293       else
294         cerr << "Error: Can't kill Salome application on port " << myArgs.killPort << endl;
295     }
296   _exit(1);
297 }
298
299 void * dumpState(void *arg)
300 {
301   thread_st *st = (thread_st*)arg;
302   YACS::StatesForNode state = p->getEffectiveState();
303   while((state != YACS::DONE) && (state != YACS::LOADFAILED) && (state != YACS::EXECFAILED) && (state != YACS::INTERNALERR) && (state != YACS::DISABLED) && (state != YACS::FAILED) && (state != YACS::ERROR)){
304 #ifdef WIN32
305     Sleep(st->nbsec);
306 #else 
307     sleep(st->nbsec);
308 #endif
309     string cmd = "touch " + st->lockFile;
310     system(cmd.c_str());
311     YACS::ENGINE::VisitorSaveState vst(p);
312     vst.openFileDump(st->dumpFile);
313     p->accept(&vst);
314     vst.closeFileDump();
315     cmd = "rm -f " + st->lockFile;
316     system(cmd.c_str());
317     state = p->getEffectiveState();
318   }
319   delete st;
320   return NULL;
321 }
322
323 #ifndef WIN32
324 typedef void (*sighandler_t)(int);
325 sighandler_t setsig(int sig, sighandler_t handler)
326 {
327   struct sigaction context, ocontext;
328   context.sa_handler = handler;
329   sigemptyset(&context.sa_mask);
330   context.sa_flags = 0;
331   if (sigaction(sig, &context, &ocontext) == -1)
332     return SIG_ERR;
333   return ocontext.sa_handler;
334 }
335 #endif
336
337 bool parse_init_port(const std::string& input, std::string& node, std::string& port, std::string& value)
338 {
339   bool ok = true;
340   size_t pos_eq = input.find('=');
341   if(pos_eq == std::string::npos || pos_eq == input.size())
342     return false;
343   value = input.substr(pos_eq+1);
344   size_t pos_dot = input.rfind('.', pos_eq);
345   if(!pos_dot || pos_dot == std::string::npos || pos_dot >= pos_eq-1)
346     return false;
347   port = input.substr(pos_dot+1, pos_eq-pos_dot-1);
348   node = input.substr(0, pos_dot);
349   return true;
350 }
351
352 int main (int argc, char* argv[])
353 {
354      
355   // Default values.
356   myArgs.display = 0;
357   myArgs.verbose = 0;
358   myArgs.stop = 0;
359   myArgs.dumpErrorFile= (char *)"";
360   myArgs.finalDump = (char *)"";
361   myArgs.dump = 0;
362   myArgs.loadState = (char *)"";
363   myArgs.xmlSchema = (char *)"";
364   myArgs.shutdown = 1;
365   myArgs.reset = 0;
366   myArgs.killPort = 0;
367   myArgs.init_ports.clear();
368
369   // Parse our arguments; every option seen by parse_opt will be reflected in arguments.
370 #if defined WIN32 || defined __APPLE__
371 #else
372   argp_parse (&argp, argc, argv, 0, 0, &myArgs);
373   std::cerr << "graph = " << myArgs.args[0];
374   std::cerr << " options: display=" << myArgs.display;
375   std::cerr << " verbose="<<myArgs.verbose;
376   std::cerr << " stop-on-error=" << myArgs.stop;
377   std::cerr << " shutdown=" << myArgs.shutdown;
378   std::cerr << " reset=" << myArgs.reset;
379   if (myArgs.killPort)
380     std::cerr << " kill-port=" << myArgs.killPort;
381   if (myArgs.stop)
382     std::cerr << " dumpErrorFile=" << myArgs.dumpErrorFile << std::endl;
383   else
384     std::cerr << std::endl;
385   std::list<std::string>::const_iterator it;
386   for(it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
387   {
388     std::cerr << (*it) << std::endl;
389   }
390 #endif
391
392 #ifndef WIN32
393   setsig(SIGINT,&Handler);
394   setsig(SIGTERM,&Handler);
395 #endif
396
397   timer("Starting ");
398   long flags = RuntimeSALOME::UsePython + RuntimeSALOME::UseCorba + RuntimeSALOME::UseXml + \
399                RuntimeSALOME::UseCpp + RuntimeSALOME::UseSalome;
400   RuntimeSALOME::setRuntime(flags, argc, argv);
401
402   // Try to load the session catalog if it exists
403   try
404     {
405       YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
406       CORBA::ORB_ptr orb = runTime->getOrb();
407       if (orb)
408         {
409           SALOME_NamingService namingService(orb);
410           CORBA::Object_var obj = namingService.Resolve("/Kernel/ModulCatalog");
411           SALOME_ModuleCatalog::ModuleCatalog_var aModuleCatalog = SALOME_ModuleCatalog::ModuleCatalog::_narrow(obj);
412           if (! CORBA::is_nil(aModuleCatalog))
413             {
414               CORBA::String_var anIOR = orb->object_to_string( aModuleCatalog );
415               YACS::ENGINE::Catalog* aCatalog = runTime->loadCatalog( "session", anIOR.in() );
416               runTime->addCatalog(aCatalog);
417             }
418         }
419     }
420   catch(ServiceUnreachable& e)
421     {
422       //Naming service unreachable don't add catalog
423     }
424
425   YACSLoader loader;
426   Executor executor;
427
428   try
429     {
430       timer("Elapsed time before load: ");
431       p=loader.load(myArgs.args[0]);
432       if(p==0)
433         {
434           std::cerr << "The imported file is probably not a YACS schema file" << std::endl;
435           return 1;
436         }
437       // Initialize the ports
438       for(std::list<std::string>::iterator it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
439       {
440         std::string node, port, value;
441         if(parse_init_port((*it), node, port, value))
442         {
443           std::cerr << "Initialization node=" << node
444                     << " port=" << port
445                     << " value=" << value << std::endl;
446
447           std::string init_state;
448           init_state = p->setInPortValue(node, port, value);
449           if(value.compare(init_state))
450           {
451             std::cerr << "Error on initialization:" << init_state << std::endl;
452             return 1;
453           }
454         }
455         else
456         {
457           std::cerr << "Error on parsing initialization string:" << (*it) << std::endl;
458           return 1;
459         }
460       }
461       
462       //Get the parser logger
463       Logger* logger=p->getLogger("parser");
464       //Print errors logged if any
465       if(!logger->isEmpty())
466         {
467           std::cerr << "The imported file has errors" << std::endl;
468           std::cerr << logger->getStr() << std::endl;
469         }
470       //Don't execute if there are errors
471       if(logger->hasErrors())
472         {
473           if(!p->isValid())
474             {
475               std::string report=p->getErrorReport();
476               std::cerr << "The schema is not valid and can not be executed" << std::endl;
477               std::cerr << report << std::endl;
478             }
479           delete p;
480           Runtime* r=YACS::ENGINE::getRuntime();
481           Dispatcher* disp=Dispatcher::getDispatcher();
482           r->fini();
483           delete r;
484           delete disp;
485           return 1;
486         }
487       timer("Elapsed time after load: ");
488
489       if(!p->isValid())
490         {
491           std::string report=p->getErrorReport();
492           std::cerr << "The schema is not valid and can not be executed" << std::endl;
493           std::cerr << report << std::endl;
494           Runtime* r=YACS::ENGINE::getRuntime();
495           Dispatcher* disp=Dispatcher::getDispatcher();
496           r->fini();
497           delete r;
498           delete disp;
499           return 1;
500         }
501       timer("Elapsed time after validation: ");
502
503       // Check consistency
504       LinkInfo info(LinkInfo::ALL_DONT_STOP);
505       p->checkConsistency(info);
506       if(info.areWarningsOrErrors())
507         {
508           std::cerr << "The schema is not consistent and can not be executed" << std::endl;
509           std::cerr << info.getGlobalRepr() << std::endl;
510           Runtime* r=YACS::ENGINE::getRuntime();
511           Dispatcher* disp=Dispatcher::getDispatcher();
512           r->fini();
513           delete r;
514           delete disp;
515           return 1;
516         }
517       timer("Elapsed time after check consistency: ");
518
519       //execution
520       bool isXmlSchema = (strlen(myArgs.xmlSchema) != 0);
521       if (isXmlSchema)
522       {
523         YACS::ENGINE::VisitorSaveSalomeSchema vss(p);
524         vss.openFileSchema(myArgs.xmlSchema);
525         p->accept(&vss);
526         vss.closeFileSchema();
527       }
528
529       bool fromScratch = (strlen(myArgs.loadState) == 0);
530       if (!fromScratch)
531         {
532           p->init();
533           p->exUpdateState();
534           stateParser* rootParser = new stateParser();
535           stateLoader myStateLoader(rootParser, p);
536           myStateLoader.parse(myArgs.loadState);
537           if(myArgs.reset>0)
538             {
539               p->resetState(myArgs.reset);
540               p->exUpdateState();
541             }
542         }
543
544       if (myArgs.stop)
545         if (strlen(myArgs.dumpErrorFile) >0)
546           executor.setStopOnError(true, myArgs.dumpErrorFile);
547         else
548           executor.setStopOnError(false, myArgs.dumpErrorFile);
549
550       if(myArgs.display>0)
551         {
552           std::ofstream f("toto");
553           p->writeDot(f);
554           f.close();
555         }
556
557       bool isDump = (myArgs.dump != 0);
558       pthread_t th;
559       if (isDump)
560         {
561           thread_st *st = new thread_st;
562           st->nbsec = myArgs.dump;
563           st->dumpFile = string("dumpState_") + myArgs.args[0];
564           string rootFile = st->dumpFile.substr(0,st->dumpFile.find("."));
565           st->lockFile = rootFile + ".lock";
566           pthread_create(&th,NULL,&dumpState,(void*)st);
567         }
568       LoadObserversPluginIfAny(p,&executor);
569       cerr << "+++++++++++++++++++ start calculation +++++++++++++++++++" << endl;
570       executor.RunW(p,myArgs.display, fromScratch);
571       cerr << "+++++++++++++++++++  end calculation  +++++++++++++++++++" << endl;
572       cerr << "Proc state : " << Node::getStateName(p->getEffectiveState()) << endl;
573       timer("Elapsed time after execution: ");
574
575       // Return 0 if SCHEMA OK
576       // Return 1 for YACS ERROR (EXCEPTION NOT CATCHED)
577       // Return 2 for YACS SCHEMA ERROR/FAILED
578       int return_value = 0;
579
580       if(p->getEffectiveState() != YACS::DONE)
581         {
582           std::string report=p->getErrorReport();
583           std::cerr << "Execution has ended in error" << std::endl;
584           std::cerr << report << std::endl;
585           return_value = 2;
586         }
587
588       if(myArgs.display>0)
589         {
590           std::ofstream g("titi");
591           p->writeDot(g);
592           g.close();
593         }
594
595       if (isDump)
596         {
597           pthread_join(th,NULL);
598         }
599
600       bool isFinalDump = (strlen(myArgs.finalDump) != 0);
601       if (isFinalDump)
602         {
603           YACS::ENGINE::VisitorSaveState vst(p);
604           vst.openFileDump(myArgs.finalDump);
605           p->accept(&vst);
606           vst.closeFileDump();
607         }
608       if(myArgs.shutdown < 999)
609         {
610           p->shutdown(myArgs.shutdown);
611         }
612       delete p;
613       Runtime* r=YACS::ENGINE::getRuntime();
614       Dispatcher* disp=Dispatcher::getDispatcher();
615       r->fini();
616       delete r;
617       delete disp;
618       UnLoadObserversPluginIfAny();
619       return return_value;
620     }
621   catch (YACS::Exception& e)
622     {
623       cerr << "Caught a YACS exception" << endl;
624       cerr << e.what() << endl;
625       Runtime* r=YACS::ENGINE::getRuntime();
626       Dispatcher* disp=Dispatcher::getDispatcher();
627       r->fini();
628       delete r;
629       delete disp;
630       return 1;
631     }
632   catch (const std::ios_base::failure&)
633     {
634       cerr << "Caught an io failure exception" << endl;
635       return 1;
636     }
637   catch(CORBA::SystemException& ex) 
638     {
639       cerr << "Caught a CORBA::SystemException.:" << __FILE__ << ":" << __LINE__ << ":" ;
640       CORBA::Any tmp;
641       tmp <<= ex;
642       CORBA::TypeCode_var tc = tmp.type();
643       const char *p = tc->name();
644       if ( *p != '\0' ) 
645         cerr <<p;
646       else  
647         cerr  << tc->id();
648       cerr << endl;
649       return 1;
650     }
651   catch(omniORB::fatalException& fe) 
652     {
653       cerr << "Caught omniORB::fatalException:" << endl;
654       cerr << "  file: " << fe.file() << endl;
655       cerr << "  line: " << fe.line() << endl;
656       cerr << "  mesg: " << fe.errmsg() << endl;
657       return 1;
658     }
659   catch(...) 
660     {
661       cerr << "Caught unknown exception." << endl;
662       return 1;
663     }
664 }
665