1 // Copyright (C) 2006-2023 CEA, EDF
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #include "yacsconfig.h"
21 #include "RuntimeSALOME.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 #include "PythonNode.hxx"
35 #include "KernelBasis.hxx"
36 #include "SALOME_Launcher.hxx"
37 #include "ServiceUnreachable.hxx"
38 #include "SALOME_NamingService_Wrapper.hxx"
39 #include "SALOME_NamingService.hxx"
40 #include "SALOME_ModuleCatalog.hh"
41 #include "SALOMESDS_DataServerManager.hxx"
42 #include "Basics_Utils.hxx"
49 #if defined WIN32 || defined __APPLE__
54 using YACS::YACSLoader;
55 using namespace YACS::ENGINE;
59 // --- use of glibc argp interface for parsing unix-style arguments
61 const char *argp_program_version ="driver V0.1";
62 const char *argp_program_bug_address ="<nepal@nepal.edf.fr>";
63 static char doc[] ="driver -- a SALOME YACS graph executor";
64 static char args_doc[] = "graph.xml";
66 #if defined WIN32 || defined __APPLE__
68 static struct argp_option options[] =
70 {"display", 'd', "level", 0, "Display dot files: 0=never to 3=very often (default 0)"},
71 {"verbose", 'v', 0, 0, "Produce verbose output" },
72 {"stop-on-error", 's', 0, 0, "Stop on first error" },
73 {"dump-on-error", 'e', "file", OPTION_ARG_OPTIONAL, "Stop on first error and dump state"},
74 {"dump-final", 'f', "file", OPTION_ARG_OPTIONAL, "dump final state"},
75 {"dump", 'g', "nbsec", OPTION_ARG_OPTIONAL, "dump state"},
76 {"load-state", 'l', "file", 0, "Load State from a previous partial execution"},
77 {"save-xml-schema", 'x', "file", OPTION_ARG_OPTIONAL, "dump xml schema"},
78 {"shutdown", 't', "level", 0, "Shutdown the schema: 0=no shutdown to 3=full shutdown (default 1)"},
79 {"reset", 'r', "level", 0, "Reset the schema before execution: 0=nothing, 1=reset error nodes to ready state (default 0)"},
80 {"kill-port", 'k', "port", 0, "Kill Salome application running on the specified port if the driver process is killed (with SIGINT or SIGTERM)"},
81 {"init_port", 'i', "value", OPTION_ARG_OPTIONAL, "Initialisation value of a port, specified as bloc.node.port=value."},
82 {"donotsqueeze", 'z', "value", 0, "Desactivate squeeze memory optimization."},
93 std::string dumpErrorFile;
94 std::string finalDump;
96 std::string xmlSchema;
97 std::string loadState;
101 bool squeezeMemory = true;
102 std::list<std::string> init_ports;
111 #if defined WIN32 || defined __APPLE__
116 parse_opt (int key, char *arg, struct argp_state *state)
118 #if defined WIN32 || defined __APPLE__
120 // Get the input argument from argp_parse, which we
121 // know is a pointer to our arguments structure.
122 struct arguments *myArgs = (arguments*)state->input;
127 myArgs->display = atoi(arg);
130 myArgs->shutdown = atoi(arg);
133 myArgs->reset = atoi(arg);
144 myArgs->dumpErrorFile = arg;
146 myArgs->dumpErrorFile = "dumpErrorState.xml";
150 myArgs->finalDump = arg;
152 myArgs->finalDump = "finalDumpState.xml";
156 myArgs->dump = atoi(arg);
161 myArgs->loadState = arg;
165 myArgs->xmlSchema = arg;
167 myArgs->xmlSchema = "saveSchema.xml";
170 myArgs->killPort = atoi(arg);
174 myArgs->init_ports.push_back(std::string(arg));
177 myArgs->squeezeMemory = ! ( bool( atoi(arg) ) );
180 if (state->arg_num >=1) // Too many arguments.
182 myArgs->args[state->arg_num] = arg;
186 if (state->arg_num < 1) // Not enough arguments.
191 return ARGP_ERR_UNKNOWN;
198 #if defined WIN32 || defined __APPLE__
200 static struct argp argp = { options, parse_opt, args_doc, doc };
203 void timer(std::string msg)
205 #if defined WIN32 || defined __APPLE__
208 gettimeofday(&tv,NULL);
209 long t=tv.tv_sec*1000+tv.tv_usec/1000;
211 gettimeofday(&tv,NULL);
212 std::cerr << msg << tv.tv_sec*1000+tv.tv_usec/1000-t0 << " ms" << std::endl;
217 static struct arguments myArgs;
219 void Handler(int theSigId)
224 //if requested save state
225 bool isFinalDump = !myArgs.finalDump.empty();
228 YACS::ENGINE::VisitorSalomeSaveState vst(p);
229 vst.openFileDump(myArgs.finalDump);
233 //if requested shutdown schema
234 if(myArgs.shutdown < 999)
236 p->shutdown(myArgs.shutdown);
241 ostringstream command;
242 command << "killSalomeWithPort.py " << myArgs.killPort;
243 int status = system(command.str().c_str());
245 cerr << "Salome application on port " << myArgs.killPort << " is killed" << endl;
247 cerr << "Error: Can't kill Salome application on port " << myArgs.killPort << endl;
252 void * dumpState(void *arg)
254 thread_st *st = (thread_st*)arg;
255 YACS::StatesForNode state = p->getEffectiveState();
256 while((state != YACS::DONE) && (state != YACS::LOADFAILED) && (state != YACS::EXECFAILED) && (state != YACS::INTERNALERR) && (state != YACS::DISABLED) && (state != YACS::FAILED) && (state != YACS::ERROR)){
262 string cmd = "touch " + st->lockFile;
264 YACS::ENGINE::VisitorSalomeSaveState vst(p);
265 vst.openFileDump(st->dumpFile);
268 cmd = "rm -f " + st->lockFile;
270 state = p->getEffectiveState();
277 typedef void (*sighandler_t)(int);
278 sighandler_t setsig(int sig, sighandler_t handler)
280 struct sigaction context, ocontext;
281 context.sa_handler = handler;
282 sigemptyset(&context.sa_mask);
283 context.sa_flags = 0;
284 if (sigaction(sig, &context, &ocontext) == -1)
286 return ocontext.sa_handler;
290 bool parse_init_port(const std::string& input, std::string& node, std::string& port, std::string& value)
293 size_t pos_eq = input.find('=');
294 if(pos_eq == std::string::npos || pos_eq == input.size())
296 value = input.substr(pos_eq+1);
297 size_t pos_dot = input.rfind('.', pos_eq);
298 if(!pos_dot || pos_dot == std::string::npos || pos_dot >= pos_eq-1)
300 port = input.substr(pos_dot+1, pos_eq-pos_dot-1);
301 node = input.substr(0, pos_dot);
308 KERNEL::getLauncherSA();
311 void shutdownServers()
313 // shutdown data server scopes
316 YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
317 runTime->loadModulCatalog();
318 CORBA::ORB_ptr orb = runTime->getOrb();
321 SALOME_NamingService_Wrapper namingService(orb);
322 CORBA::Object_var objDSM(namingService.Resolve(SALOMESDS::DataServerManager::NAME_IN_NS));
323 SALOME::DataServerManager_var dsm(SALOME::DataServerManager::_narrow(objDSM));
324 if ( !CORBA::is_nil(dsm) )
325 dsm->shutdownScopes();
328 catch(const CORBA::Exception& )
330 // ignore and continue
332 catch(ServiceUnreachable& e)
334 // ignore and continue
338 int main (int argc, char* argv[])
340 // Parse our arguments; every option seen by parse_opt will be reflected in arguments.
341 #if defined WIN32 || defined __APPLE__
343 argp_parse (&argp, argc, argv, 0, 0, &myArgs);
344 std::cerr << "graph = " << myArgs.args[0];
345 std::cerr << " options: display=" << myArgs.display;
346 std::cerr << " verbose="<<myArgs.verbose;
347 std::cerr << " stop-on-error=" << myArgs.stop;
348 std::cerr << " shutdown=" << myArgs.shutdown;
349 std::cerr << " reset=" << myArgs.reset;
351 std::cerr << " kill-port=" << myArgs.killPort;
353 std::cerr << " dumpErrorFile=" << myArgs.dumpErrorFile << std::endl;
355 std::cerr << std::endl;
356 std::list<std::string>::const_iterator it;
357 for(it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
359 std::cerr << (*it) << std::endl;
364 setsig(SIGINT,&Handler);
365 setsig(SIGTERM,&Handler);
371 long flags = RuntimeSALOME::UsePython + RuntimeSALOME::UseCorba + RuntimeSALOME::UseXml + \
372 RuntimeSALOME::UseCpp + RuntimeSALOME::UseSalome;
373 RuntimeSALOME::setRuntime(flags, argc, argv);
375 // Try to load the session catalog if it exists
378 YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
379 runTime->loadModulCatalog();
380 CORBA::ORB_ptr orb = runTime->getOrb();
383 SALOME_NamingService_Wrapper namingService(orb);
384 CORBA::Object_var obj = namingService.Resolve("/Kernel/ModulCatalog");
385 SALOME_ModuleCatalog::ModuleCatalog_var aModuleCatalog = SALOME_ModuleCatalog::ModuleCatalog::_narrow(obj);
386 if (! CORBA::is_nil(aModuleCatalog))
388 CORBA::String_var anIOR = orb->object_to_string( aModuleCatalog );
389 YACS::ENGINE::Catalog* aCatalog = runTime->loadCatalog( "session", anIOR.in() );
390 runTime->addCatalog(aCatalog);
394 catch(ServiceUnreachable& e)
396 //Naming service unreachable don't add catalog
404 timer("Elapsed time before load: ");
405 p=loader.load(myArgs.args[0]);
408 std::cerr << "The imported file is probably not a YACS schema file" << std::endl;
411 if(myArgs.squeezeMemory)
413 std::list<Node *> allNodes = p->getAllRecursiveNodes();
414 for(auto node : allNodes)
416 PythonNode *nodePy = dynamic_cast<PythonNode *>(node);
419 nodePy->setSqueezeStatus( true );
423 // Initialize the ports
424 for(std::list<std::string>::iterator it=myArgs.init_ports.begin(); it != myArgs.init_ports.end(); it++)
426 std::string node, port, value;
427 if(parse_init_port((*it), node, port, value))
429 std::cerr << "Initialization node=" << node
431 << " value=" << value << std::endl;
433 std::string init_state;
434 init_state = p->setInPortValue(node, port, value);
435 if(value.compare(init_state))
437 std::cerr << "Error on initialization:" << init_state << std::endl;
443 std::cerr << "Error on parsing initialization string:" << (*it) << std::endl;
448 //Get the parser logger
449 Logger* logger=p->getLogger("parser");
450 //Print errors logged if any
451 if(!logger->isEmpty())
453 std::cerr << "The imported file has errors" << std::endl;
454 std::cerr << logger->getStr() << std::endl;
456 //Don't execute if there are errors
457 if(logger->hasErrors())
461 std::string report=p->getErrorReport();
462 std::cerr << "The schema is not valid and can not be executed" << std::endl;
463 std::cerr << report << std::endl;
466 Runtime* r=YACS::ENGINE::getRuntime();
467 Dispatcher* disp=Dispatcher::getDispatcher();
473 timer("Elapsed time after load: ");
477 std::string report=p->getErrorReport();
478 std::cerr << "The schema is not valid and can not be executed" << std::endl;
479 std::cerr << report << std::endl;
480 Runtime* r=YACS::ENGINE::getRuntime();
481 Dispatcher* disp=Dispatcher::getDispatcher();
487 timer("Elapsed time after validation: ");
490 LinkInfo info(LinkInfo::ALL_DONT_STOP);
491 p->checkConsistency(info);
492 if(info.areWarningsOrErrors())
494 std::cerr << "The schema is not consistent and can not be executed" << std::endl;
495 std::cerr << info.getGlobalRepr() << std::endl;
496 Runtime* r=YACS::ENGINE::getRuntime();
497 Dispatcher* disp=Dispatcher::getDispatcher();
503 timer("Elapsed time after check consistency: ");
506 bool isXmlSchema = !myArgs.xmlSchema.empty() ;
509 YACS::ENGINE::VisitorSaveSalomeSchema vss(p);
510 vss.openFileSchema(myArgs.xmlSchema);
512 vss.closeFileSchema();
515 bool fromScratch = myArgs.loadState.empty() ;
520 stateParser* rootParser = new stateParser();
521 stateLoader myStateLoader(rootParser, p);
522 myStateLoader.parse(myArgs.loadState);
525 p->resetState(myArgs.reset);
532 if ( !myArgs.dumpErrorFile.empty() )
533 executor.setStopOnError(true, myArgs.dumpErrorFile);
535 executor.setStopOnError(false, myArgs.dumpErrorFile);
539 std::ofstream f("toto");
544 bool isDump = (myArgs.dump != 0);
548 thread_st *st = new thread_st;
549 st->nbsec = myArgs.dump;
550 st->dumpFile = string("dumpState_") + myArgs.args[0];
551 string rootFile = st->dumpFile.substr(0,st->dumpFile.find("."));
552 st->lockFile = rootFile + ".lock";
553 pthread_create(&th,NULL,&dumpState,(void*)st);
555 YACS::ENGINE::LoadObserversPluginIfAny(p,&executor);
556 cerr << "+++++++++++++++++++ start calculation +++++++++++++++++++" << endl;
557 executor.RunW(p,myArgs.display, fromScratch);
558 cerr << "+++++++++++++++++++ end calculation +++++++++++++++++++" << endl;
559 cerr << "Proc state : " << Node::getStateName(p->getEffectiveState()) << endl;
560 timer("Elapsed time after execution: ");
562 // Return 0 if SCHEMA OK
563 // Return 1 for YACS ERROR (EXCEPTION NOT CATCHED)
564 // Return 2 for YACS SCHEMA ERROR/FAILED
565 int return_value = 0;
567 if(p->getEffectiveState() != YACS::DONE)
569 std::string report=p->getErrorReport();
570 std::cerr << "Execution has ended in error" << std::endl;
571 std::cerr << report << std::endl;
577 std::ofstream g("titi");
584 pthread_join(th,NULL);
587 bool isFinalDump = !myArgs.finalDump.empty() ;
590 YACS::ENGINE::VisitorSalomeSaveState vst(p);
591 vst.openFileDump(myArgs.finalDump);
595 if(myArgs.shutdown < 999)
597 p->shutdown(myArgs.shutdown);
601 Runtime* r=YACS::ENGINE::getRuntime();
602 Dispatcher* disp=Dispatcher::getDispatcher();
606 YACS::ENGINE::UnLoadObserversPluginIfAny();
609 catch (YACS::Exception& e)
611 cerr << "Caught a YACS exception" << endl;
612 cerr << e.what() << endl;
613 Runtime* r=YACS::ENGINE::getRuntime();
614 Dispatcher* disp=Dispatcher::getDispatcher();
620 catch (const std::ios_base::failure&)
622 cerr << "Caught an io failure exception" << endl;
625 catch(CORBA::SystemException& ex)
627 cerr << "Caught a CORBA::SystemException.:" << __FILE__ << ":" << __LINE__ << ":" ;
630 CORBA::TypeCode_var tc = tmp.type();
631 const char *p = tc->name();
639 catch(omniORB::fatalException& fe)
641 cerr << "Caught omniORB::fatalException:" << endl;
642 cerr << " file: " << fe.file() << endl;
643 cerr << " line: " << fe.line() << endl;
644 cerr << " mesg: " << fe.errmsg() << endl;
649 cerr << "Caught unknown exception." << endl;