T. Meyer ROD Software Workshop July 2002 Scripting and Interpreted Languages Tom Meyer Iowa State University
T. Meyer ROD Software Workshop July 2002 Where to Use Use for middle layer(s), where easy and rapid reconfiguration is necessary. I don’t think we need to look further than CINT or Python for an interpreted language.
T. Meyer ROD Software Workshop July 2002 A Test Case On a Friday morning, I asked an undergraduate student to code a test case of a layer of Python code sandwiched between two layers of C++ code. By that evening, he had a working example. By the next day, he had a demo version interfaced to RodModule. His demo prompts the user for ROD parameters and a data string to be passed to the ECHO primitive. The hardest part was understanding the poorly written documentation. However, he is a VERY capable student with a lot of Python experience. Nevertheless, this convinced me that this approach is very feasible. I’m pretty sure CINT would be comparably easy to interface, but no test case has been done (volunteers?).
T. Meyer ROD Software Workshop July 2002 py_demo: Source Files py_demo.cxx - The main routine ParamGetter.h - C++ header file for ParamGetter class ParamGetter.cxx – The implementation of ParamGetter class py_demo.py – The Python source
T. Meyer ROD Software Workshop July 2002 py_demo.cxx (1) // py_demo.cxx // This demo calls a python script from C++ #include "py_demo/ParamGetter.h" // My code to deal with returned params #include #include "RodPrimitive.h" #include "primParams.h" #include "RodModule.h" #include "../VmeInterface/RCCVmeInterface.h" using namespace SctPixelRod; int main(int argc, char **argv) { RCCVmeInterface *vme = new RCCVmeInterface(); // Initialize my ParamGetter class. See the code in the py_demo directory. // What this does is execute the function GetParams from the python script // echo_params.py. We can then use p's methods to get the returned params. ParamGetter p("echo_params", argc, argv); // Get five long ints from the dictionary returned by GetParams, and // deal with them appropriately. UINT32 baseaddr = p.GetLongParam("BaseAddress"); UINT32 mapsize = p.GetLongParam("MapSize"); UINT32 slot = p.GetLongParam("SlotNum"); UINT32 numslaves = p.GetLongParam("NumSlaves"); UINT32 body = p.GetLongParam("ByteToEcho"); RodModule *rod = new RodModule(baseaddr, mapsize, *vme, slot, numslaves); // Create a RodPrimitive based on the parameters we got. RodPrimitive *rp = new RodPrimitive(4,1,ECHO,&body);
T. Meyer ROD Software Workshop July 2002 py_demo.cxx (2) // Create a RodPrimList with rp as its only element. RodPrimList *pl = new RodPrimList(1); pl->insert(pl->begin(), *rp); // Build PrimList message. pl->bufferBuild(); // Now send it. rod->sendPrimList(MASTER_INBUFF, *pl); // Wait for the PrimList to finish executing. while (rod->primHandler != PRIM_EXECUTING && rod->primHandler != PRIM_WAITING) {} // Deal with the output from the Rod. RodOutList *outlist = rod->getOutList(); cout << "outLength=" << outList[0] << ", outIndex=" << outList[1] << ", outNumPrims=" << outList[2] << endl << "primLength=" << outList[3] << ", primIndex=" << outList[4] << ", primId=" << outList[5] << endl; cout << "data: " << endl; for (int i=0; i<outList[3]-3; i++) cout << outList[6+i] << endl; // Clear the PrimList and free the memory we used. primList->clear(); delete vme, rod, pl, rp, outlist; return 0; }
T. Meyer ROD Software Workshop July 2002 py_demo.py (1) class App: # App constructor def __init__(self, master): from Tkinter import * # Create some string variables to hold the input parameters. self.base=StringVar(master) self.byte=StringVar(master) self.maps=StringVar(master) self.slot=StringVar(master) self.slvs=StringVar(master) # Create a tk frame that will hold each element of our gui. self.frame = Frame(master) self.frame.pack() # Now create a subframe of frame that holds the entry box for # the base address parameter. self.subframe1 = Frame(self.frame) self.ent1 = Entry(self.subframe1, textvar=self.base) self.ent1.pack(side=RIGHT) self.lab1 = Label(self.subframe1, text='Base address:') self.lab1.pack(side=LEFT) self.subframe1.pack() # And again for the map size parameter. self.subframe2 = Frame(self.frame) self.ent2 = Entry(self.subframe2, textvar=self.maps) self.ent2.pack(side=RIGHT) self.lab2 = Label(self.subframe2, text='Map size:') self.lab2.pack(side=LEFT) self.subframe2.pack()
T. Meyer ROD Software Workshop July 2002 py_demo.py (2) # And again for the slot number. self.subframe3 = Frame(self.frame) self.ent3 = Entry(self.subframe3, textvar=self.slot) self.ent3.pack(side=RIGHT) self.lab3 = Label(self.subframe3, text='Slot number:') self.lab3.pack(side=LEFT) self.subframe3.pack() # Still another for the number of slaves. self.subframe4 = Frame(self.frame) self.ent4 = Entry(self.subframe4, textvar=self.slvs) self.ent4.pack(side=RIGHT) self.lab4 = Label(self.subframe4, text='Number of slaves:') self.lab4.pack(side=LEFT) self.subframe4.pack() # Yet another for the byte to actually echo. self.subframe5 = Frame(self.frame) self.ent5 = Entry(self.subframe5, textvar=self.byte) self.ent5.pack(side=RIGHT) self.lab5 = Label(self.subframe5, text='Byte to echo:') self.lab5.pack(side=LEFT) self.subframe5.pack() # At the bottom, put in a button to click when you're done # inputting stuff. When clicked, it will call the command # self.done(). self.donebutton=Button(self.frame,text="Done", command=self.done) self.donebutton.pack()
T. Meyer ROD Software Workshop July 2002 py_demo.py (3) # When the Done button is clicked, this function is called and # the frame is closed. def done(self): self.frame.quit() # After the frame has been closed, this function can be called to # get the values input by the user. It returns a dictionary. def get_vals(self): return { 'BaseAddress' : int(self.base.get()), 'MapSize' : int(self.maps.get()), 'SlotNum' : int(self.slot.get()), 'NumSlaves' : int(self.slvs.get()), 'ByteToEcho' : int(self.byte.get()) } # The function name "GetParams" is hard-wired into the ParamGetter # C++ class. This is the function that will be called when the class # is initialized. def GetParams(): import Tkinter # Create a root window for Tk. root = Tkinter.Tk() # Create an App within the Tk root window. app = App(root) # Give it a name. root.title("Echo parameters") # Start the Tk event loop. root.mainloop() # The user terminates the event loop by clicking the Done button. # Now return the dictionary to C++ to deal with as it pleases. return app.get_vals()
T. Meyer ROD Software Workshop July 2002 ParamGetter.h #ifndef PARAMGETTER_H #define PARAMGETTER_H #include #include // Standard Python include class ParamGetter { private: PyObject *pName, *pModule, *pDict, *pFunc; PyObject *pArgs, *pValue; void Crash(string message); public: ParamGetter(string module_name, int argc, char **argv); ~ParamGetter(); long GetLongParam(string param_name); // Get a long int from the dictionary returned by a // python script. string GetStringParam(string param_name); // Get a std::string from the dictionary returned by a // python script. }; #endif
T. Meyer ROD Software Workshop July 2002 ParamGetter.cxx (1) #include "ParamGetter.h" // Functions to get parameters returned in a dictionary from a python script void ParamGetter::Crash(string message) { // Exit neatly so that core isn't dumped. You always have to do a DECREF // because of the way Python handles memory. cerr << message << endl; PyErr_Print(); Py_XDECREF(pValue); // Free all the memory Py_XDECREF(pArgs); // that we did not Py_XDECREF(pModule); // "borrow" from Py_XDECREF(pName); // Python Py_Finalize(); } ParamGetter::ParamGetter(string module_name, int argc, char **argv) { Py_Initialize(); // Start the interpreter PySys_SetArgv(argc,argv); // Set the arguments correctly }
T. Meyer ROD Software Workshop July 2002 ParamGetter.cxx (2) pName = PyString_FromString(module_name.c_str()); pModule = PyImport_Import(pName); //Import the python module if (!pModule) { Crash("Unable to load module."); return; } pDict = PyModule_GetDict(pModule); // Get a dictionary containing all the objects in the module pFunc = PyDict_GetItemString(pDict, "GetParams"); // Grab a pointer to the function GetParams pArgs = PyTuple_New(0); // We don't need to pass it any arguments if (!pFunc) { Crash("Unable to load function."); return; } if (!PyCallable_Check(pFunc)) { Crash("Unable to call function."); return; } pValue = PyObject_CallObject(pFunc, pArgs); // Call the function! if (!pValue) { Crash("Call failed."); return; }