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