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