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