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