Salome HOME
[EDF29576] : ready for clean SIGINT implementation
[modules/yacs.git] / src / yacsloader / driver_internal.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2024  CEA, EDF
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20
21 import salome
22 import logging
23
24 my_runtime_yacs = None
25
26 def initializeSALOME():
27     import SALOMERuntime
28     global my_runtime_yacs
29     if my_runtime_yacs:
30       return
31     salome.salome_init()
32     flags = SALOMERuntime.RuntimeSALOME.UsePython + SALOMERuntime.RuntimeSALOME.UseCorba + SALOMERuntime.RuntimeSALOME.UseXml + SALOMERuntime.RuntimeSALOME.UseCpp + SALOMERuntime.RuntimeSALOME.UseSalome
33     SALOMERuntime.RuntimeSALOME.setRuntime( flags )
34     my_runtime_yacs = SALOMERuntime.getSALOMERuntime()
35     anIOR = salome.orb.object_to_string ( salome.modulcat )
36     aCatalog = my_runtime_yacs.loadCatalog( "session", anIOR )
37     my_runtime_yacs.addCatalog( aCatalog )
38
39 def SALOMEInitializationNeeded(func):
40     def decaratedFunc(*args,**kwargs):
41       initializeSALOME()
42       return func(*args,**kwargs)
43     return decaratedFunc
44
45 @SALOMEInitializationNeeded
46 def loadGraph( xmlFileName ):
47     """
48     Args:
49     -----
50     xmlFileName : XML file containing YACS schema
51
52     Returns
53     -------
54
55     SALOMERuntime.SalomeProc : YACS graph instance
56     """
57     import loader
58     l=loader.YACSLoader()
59     p=l.load( xmlFileName )
60     return p
61
62 def patchGraph( proc, squeezeMemory, initPorts, xmlSchema, loadStateXmlFile, reset, display):
63     """
64     Args:
65     -----
66
67     proc ( SALOMERuntime.SalomeProc ) : YACS Proc instance to be evaluated
68     squeezeMemory ( bool ) : squeezememory to be activated
69     initPorts (list<string>) : list of bloc.node.port=value.
70     xmlSchema (string) :
71     loadStateXmlFile (string) : file if any of state to be loaded inside proc
72     reset (int) : 
73     display (int) :
74     """
75     import SALOMERuntime
76     import loader
77     def parse_init_port(input):
78       """
79       Returns
80       -------
81       node, port, value
82       """
83       node_port, value = input.split("=")
84       nodePortSpl = node_port.split(".")
85       port = nodePortSpl[-1]
86       node = ".".join( nodePortSpl[:-1] )
87       return node,port,value
88        
89     if squeezeMemory:
90       logging.info("SqueezeMemory requested -> update proc")
91       allNodes = proc.getAllRecursiveNodes()
92       for node in allNodes:
93         if isinstance(proc,SALOMERuntime.PythonNode):
94           node.setSqueezeStatus( True )
95     #
96     for initPort in initPorts:
97         node,port,value = parse_init_port(initPort)
98         init_state = proc.setInPortValue(node, port, value)
99         if init_state != value:
100           raise RuntimeError(f"Error on initialization of {initPort}")
101     #
102     if xmlSchema:
103       SALOMERuntime.VisitorSaveSalomeSchemaUnsafe(proc,xmlSchema)
104       pass
105     #
106     if loadStateXmlFile:
107       loader.loadState( proc, loadStateXmlFile )
108       if reset > 0:
109         proc.resetState(reset)
110         proc.exUpdateState()
111     #
112     if display > 0:
113        proc.writeDotInFile("toto")
114          
115 @SALOMEInitializationNeeded
116 def prepareExecution(proc, isStop, dumpErrorFile):
117   """
118   Returns
119   -------
120
121   pilot.ExecutorSwig : Instance of executor
122   """
123   import pilot
124   ex=pilot.ExecutorSwig()
125   if isStop:
126     logging.info(f"Stop has been activated with {dumpErrorFile}")
127     ex.setStopOnError( dumpErrorFile!="", dumpErrorFile )
128   return ex
129
130 @SALOMEInitializationNeeded
131 def executeGraph( executor, xmlfilename, proc, dump, finalDump, display, shutdown ):
132     """
133     Args:
134     -----
135   
136     executor (pilot.ExecutorSwig) : Executor in charge of evaluation.
137     proc ( SALOMERuntime.SalomeProc ) : YACS Proc instance to be evaluated
138     xmlfilename (string)
139     dump (int) : time interval between 2 dump state
140     finalDump ( string ) : filename containing final result of graph, if any.
141     display (int) :
142     shutdown (int) : shutdown level
143     """
144     import signal
145     import SALOMERuntime
146     def handler(signum, frame):
147       print('Signal handler called with signal', signum)
148       proc.cleanNodes()
149       if finalDump:
150         SALOMERuntime.schemaSaveStateUnsafe( proc, finalDump )
151       if shutdown < 999:
152         print(100*"shutdown")
153         proc.shutdown(shutdown)
154     
155     signal.signal(signal.SIGINT, handler)
156     signal.signal(signal.SIGTERM, handler)
157     dump_thread = None
158     import pilot
159     import os
160
161     if dump != 0:
162        dumpFile = "dumpState_{}".format( os.path.basename(xmlfilename) )
163        lockFile = "{}.lock".format( os.path.splitext( os.path.basename(xmlfilename) )[0] )
164        dump_thread = SALOMERuntime.ThreadDumpState(proc,dump,dumpFile,lockFile)
165        dump_thread.start()
166
167     executor.RunPy(proc,display,isPyThread=True,fromscratch=True) # same as RunW but releasing GIL
168     if proc.getEffectiveState() != pilot.DONE:
169       raise RuntimeError( proc.getErrorReport() )
170     #
171     if display > 0:
172        proc.writeDotInFile("titi")
173     #
174     if dump_thread:
175        dump_thread.join()
176     #
177     if finalDump:
178       logging.info(f"Final dump requested : {finalDump}")
179       SALOMERuntime.schemaSaveStateUnsafe( proc, finalDump )
180
181 @SALOMEInitializationNeeded
182 def destroyElementsGeneratedByExecutionOfGraph( proc, shutdown ):
183     """
184     Args:
185     -----
186
187     shutdown (int) : shutdown level
188
189     """
190     if shutdown < 999:
191       proc.shutdown(shutdown)
192     salome.dsm.shutdownScopes()
193     my_runtime_yacs.fini( False )
194
195 if __name__ == "__main__":
196     from salome_utils import positionVerbosityOfLoggerRegardingState,setVerboseLevel,setVerbose
197     import argparse
198     parser = argparse.ArgumentParser()
199     parser.add_argument('xmlfilename',help = "XML file containing YACS schema to be executed")
200     parser.add_argument("-d", "--display", dest = "display", type=int, default=[0], nargs=1, help="Display dot files: 0=never to 3=very often")
201     parser.add_argument("-v", "--verbose", dest = "verbose",help="Produce verbose output", action='store_true')
202     parser.add_argument("-s","--stop-on-error",dest="stop",help="Stop on first error", action='store_true')
203     parser.add_argument("-e","--dump-on-error",dest="dumpErrorFile", type=str, const='dumpErrorState.xml', default="", nargs='?', help="Stop on first error and dump state")
204     parser.add_argument("-g","--dump",dest="dump", type=int, const=60, default=0, nargs='?', help="dump state")
205     parser.add_argument("-kt", "--kerneltrace", dest = "kerneltrace",help="Produce verbose of SALOME/KERNEL", action='store_true')
206     parser.add_argument("-f","--dump-final", dest ="finalDump", type=str, const='finalDumpState.xml', default="", nargs='?', help="dump final state")
207     parser.add_argument("-l","--load-state", dest="loadState", type=str, default="", help="Load State from a previous partial execution")
208     parser.add_argument("-x","--save-xml-schema", dest="saveXMLSchema", type=str, const="saveSchema.xml", nargs='?', default="", help = "dump xml schema")
209     parser.add_argument("-t","--shutdown", dest = 'shutdown', type=int , default=1, help="Shutdown the schema: 0=no shutdown to 3=full shutdown")
210     parser.add_argument("-r","--reset", dest = "reset", type=int , default = 0, help="Reset the schema before execution: 0=nothing, 1=reset error nodes to ready state")
211     parser.add_argument("-i","--init-port", dest = 'init_port', type=str, default ="", help="Initialisation value of a port, specified as bloc.node.port=value.")
212     parser.add_argument("-z","--donotsqueeze", dest = "donotsqueeze", help = "Desactivate squeeze memory optimization.", action='store_true')
213     args = parser.parse_args()
214     args.display = args.display[0]
215     #
216     if args.verbose:
217       setVerbose( args.kerneltrace )
218       setVerboseLevel(logging.INFO)
219       positionVerbosityOfLoggerRegardingState()
220     #
221     proc = loadGraph( args.xmlfilename )
222     patchGraph( proc, not args.donotsqueeze, [elt for elt in args.init_port.split(",") if elt !=""], args.saveXMLSchema, args.loadState, args.reset, args.display)
223     executor = prepareExecution( proc, args.stop, args.dumpErrorFile)
224     #
225     executeGraph( executor, args.xmlfilename, proc, args.dump, args.finalDump, args.display, args.shutdown)
226     #
227     destroyElementsGeneratedByExecutionOfGraph( proc, args.shutdown )