//(GRIM)REEPER Implements Macros to Read EPICS Events and Plot Everything in ROOT //Ken Livingston (kliv@jlab.org) March 2017. // //A collection of functions to deal with EPICS scans. // ... and a simple, optional, GUI based on TButtons // //To use this, load the library, then load your own macro to use the library: // >root grLoad.C myFuncs.C #include #include "TROOT.h" #include "TRootCanvas.h" #include "TCanvas.h" #include "TColor.h" #include "TFrame.h" #include "TGraph.h" #include "TMultiGraph.h" #include "TLine.h" #include "TLatex.h" #include "TMath.h" #include "TStyle.h" #include "Riostream.h" #include "TRandom.h" #include "TSystem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum E_GR_PV {Time}; //(Because I want to refer to Time enum E_GR_FIT {GR_NOFIT,GR_FIT,GR_FITRANGE}; enum E_GR_STATUS {GR_STOPPED,GR_STARTED,GR_PAUSED}; enum E_GR_AUTO {GR_AUTO,GR_MANUAL,GR_TOGGLE}; enum E_GR_GUI {GR_NOGUI,GR_SMALLGUI,GR_BIGGUI}; const char* defaultFormats[] = {"gif","C",NULL}; //default formats for saving canvases const char defaultOutdir[] = "./epics_gr"; //default dir const char* onOffList[] = {"#bullet",""}; const int goodColors[] = {1,2,4,6,8,9,28,30,38,46,48}; //colours and markers that are OK on white bg. const int goodMarkers[] = {20,21,22,23,29}; class GrimReeper : public TObject{ private: public: // GrimReeper(const char* myname="gr" ); GrimReeper(); virtual ~GrimReeper(){}; void init(); int addPV(const char *pvstring); void printPVs(); void PVScanStep(); void printGraphs(); void startScanning(); void stopScanning(); void pauseScanning(); void resumeScanning(); void makeGraph(int yPV, int xPV, const char* pvfit = "", double rangemin = 0.0, double rangemax = 0.0, int canv = -1, int pad = 1); void makeGraph(const char *yPVName, const char *xPVName, const char* pvfit = "", double rangemin = 0.0, double rangemax = 0.0, int canv = -1, int pad = 1 ); void updateGraph(int g); void drawGraph(int g); void drawGraphs(); void updateMultiGraph(int g); void updateMultiGraphs(); void makeMultiGraph(const char *title, const char *g0, const char *g1=NULL,const char *g2=NULL,const char *g3=NULL,const char *g4=NULL,const char *g5=NULL,const char *g6=NULL,const char *g7=NULL,const char *g8=NULL,const char *g9=NULL); void makeMultiGraph(const char *title, int g0, int g1=-1,int g2=-1,int g3=-1,int g4=-1,int g5=-1,int g6=-1,int g7=-1,int g8=-1,int g9=-1); void makeMultiGraph(const char *title, int *mg); void updateGraphList(); void updatePvList(); void updateMultiList(); void updateGUI(); void updateStartButton(); void updatePauseButton(); void updateStatus(); void writePV(const char *pv_name, double pv_value); void changePeriod(int t); void setGraphAuto(int g, int a); void setMultiAuto(int g, int a); int getGraphAuto(int g); int getMultiAuto(int g); int GetAuto(TObject *gr); void SetAuto(TObject *gr); // *TOGGLE* *GETTER=GetAutohhxs void resetGrim(); void exitGrim(); void saveAll(); void setGuiToggle(); void toggleGUI(); void makeControlGUI(int guisize=GR_BIGGUI); void waveToArray(const char *pv, double *array, int n,int add=0); void waveToTH1D(const char *pv,TH1D *h,int add=0); void waveToTH2DRow(const char *pv,TH2D *h, double y,int add=0); void setScan(int pvindex,double start, double stop, double step); void setScan(const char *pvname,double start, double stop, double step); void setPeriod(int period){scanPeriod=period;}; void setVerbosity(int verbosity){verbose=verbosity;}; void setConfirmScan(int confirm){confirmScan=confirm;}; void setUserRead(const char *read){userRead=read;}; void setUserWrite(const char *write){userWrite=write;}; void setUserInit(const char *init){userInit=init;}; void setUserEnd(const char *end){userEnd=end;}; void setSaveFormats(const char **formats){saveFormats=(char **)formats;}; void setSaveDir(const char *dir){outdir=(char *)dir;}; int getPoints(){return nPoints;}; void setPoints(int n){nPointsInScan=n;}; void setSaveAndExit(int n){saveAndExit=n;}; double **getPVData(){return pvData;}; int confirmDialog(const char *title, char *message, EMsgBoxIcon icon = kMBIconExclamation, Int_t buttons = kMBCancel+kMBOk); void setMAXPV(int maxpv){MAXPV=maxpv;}; void setMAXDATA(int data){MAXDATA=data;}; void setOptFit(int opt); void setGraphsPerCanvas(int g){perCanvas=g;}; //functions for asimulated ioc void killSimIOC(); void writeTestPVs(); void simIOC(int m=0); void setLogCommand(const char *logCom){logCommand=logCom;}; //All these class data members should really start with "f" to keep in line with ROOT coding convention. Too late! //Change any of these before calling adding any PVs, or calling init() int MAXPV; int MAXDATA; int MAXGRAPH; int MAXMULTI; int MAXADDM; int MAXCANV; int PERCANVAS; int MAXFORM; int CANVH; int CANVW; int GUIH; int GUIW; int MCANVH; int MCANVW; int XPADD; int YPADD; int verbose; const char *REEPER; //the env variable const char *outdir; //directory for output and saves char **saveFormats; //list of formats to be used for saving graphs etc char **onOff; //char strings to represent on and off char fitopt[20]; //options to be passed to fitting //Arrays to hold all the data TString *pvNames; //hold the PV names double **pvData; //pointers to the data arrays double *pvIntegral; //keep running integrals TFormula **pvFormula; //for dummy PVs which are functions of previously defined PVs. int **pvFormPV; //incices of PVs in a formula - up to 10; int *pvFormN; //no of incices of PVs in a formula - up to 10; char **pvMod; //Flag to refer to the previous reading of a PV. TGraph **pvGraph; //all the graphs int *graphAuto; //autoscale stuff double *graphXfirst; double *graphXlast; double *graphYfirst; double *graphYlast; double *multiXfirst; double *multiXlast; double *multiYfirst; double *multiYlast; TCanvas **grCanvas; //canvases for each graph TCanvas **gCanv; // int nCanv; // int *grPad; //pad index for each graph int perCanvas; //flag to do a canvas per graph TMultiGraph **pvMultiGraph; //pointers to all the multigraphs int *multiAuto; //autoscale flag int **multiIndices; //hold indices of graphs in multigraphs TCanvas **multiCanvas; //canvases for each multigraph int *pvGraphX; //hold the indices of the data sets used for each graph int *pvGraphY; TString *grFits; //hold fit and ranges for graphs TF1 **grTF1; //The actual fits double *grFitMin; double *grFitMax; int *grFitDo; TCanvas *grimCanvas ; //a main canvas for the controlGUI. TButton *startButton; TButton *pauseButton; TButton *saveButton; TButton *exitButton; TButton *tIncButton; TButton *tDecButton; TButton *detailsButton; int nPV; //no of loaded PVs int scanPeriod; //default to 1s int scanTime; //time counter int nPoints; //no of reads int nPointsInScan; //end scan after this, if non-zero int saveAndExit; //flag to save all the stuff before exiting int nGraphs; //no of graphs int nCanvas; //no of canvases int nMulti; //no of mulit graphs int nMultiCanvas; //no of multi canvases int refreshRate; //how often to refresh the graphs (every 2 points default) int fitAfter; //how many points required before trying a fit. int confirmScan; //should ask for confirmation before doing a scan with caput commands. int scanStatus; int guiToggle; // char className[20]; char commandString[128]; //cainfo command char getCommandString[256]; //caget command char putCommandString[128]; //caput command TString returnString; //general return string TLegend *legend; TTimer *pvTimer; //to call the PV read (and possibly write) periodically TTimer *guiTimer; TTimeStamp sysTime; //to get precise times. double startTime; double now; int scanPVIndex; //index of PV to be scanned. Defaults to scanning on time (ie periodic readback only) const char *userRead; //custom read function called when epics readback is done. const char *userWrite; //custom write function called at the start of each step. const char *userInit; //custom init called to do initial positions for user scan const char *userEnd; //custom init called to do initial positions for user scan const char *logCommand; double scanStart; //defaults for stepping on PV for the scan double scanStop; double scanStep; double scanVal; //current value int isInit; //flag if initialised //Stuff for the optional GUI int isGUI; //flag if GUI running int maxlines; TPad *titlepad1; TPad *titlepad2; TPad *titlepad3; TPad *pvpad; TPad *graphpad; TPad *multipad; TPad *buttonpad; TPaveText *pvtext; TPaveText *graphtext; TPaveText *multitext; TPaveText *statustext; TPaveText *pvlabel; TPaveText *graphlabel; TPaveText *multilabel; TPaveText *statuslabel; int textPixels; int textLines ; //some variables for a simulated ioc int updatePeriod; TTimer *iocTimer; //to push values to the simIOC TRandom *klrand; int testTime; //time counter for simIOC int mode; double AMO_SCALERS[100]; ClassDef(GrimReeper, 0) }; //This needs to be a global to allow the very crude way of using buttons! GrimReeper *GR; GrimReeper::GrimReeper(){ MAXPV = 50; //Max no of PVs MAXDATA = 10000; //Max no of data points per PV MAXGRAPH = 100; //Max no of graphs to draw MAXMULTI = 20; //Max no of canvases to mults to draw MAXADDM = 10; //Max no of graphs to add to a multigraph MAXCANV = 20; //Max no of canvases to use MAXFORM = 10; //Max no of PVs that can be referred to in a formula CANVH = 600; //come canvas dimensions CANVW = 800; GUIH = 900; GUIW = 820; MCANVH = 600; MCANVW = 800; XPADD = 10; YPADD = 40; PERCANVAS = 5; //no of graphs per big canvas scanPeriod = 1; //default to 1s verbose = 1; //default to verbose userRead = NULL; userWrite = NULL; userInit = NULL; userEnd = NULL; logCommand = NULL; scanStart = 0.0; //defaults for stepping on PV for the scan scanStop = 0.0; scanStep = 0.0; scanVal = 0.0; nPointsInScan = 0; //end scan after this number saveAndExit = 0; // perCanvas = PERCANVAS; isInit = 0; //flag if initialised //Stuff for the optional GUI isGUI = 0; //flag if GUI running maxlines = 20; textPixels = 10; textLines = 20; sprintf(fitopt,""); outdir = (char *)defaultOutdir; saveFormats = (char **)defaultFormats; onOff = (char **)onOffList; scanPVIndex = Time; pvTimer = NULL; guiTimer = NULL; iocTimer = NULL; klrand = NULL; //init of simulation data; updatePeriod = 1; testTime = 0; //time counter for simIOC mode = 0; //sprintf(className,"%s",myname); REEPER = gSystem->Getenv("REEPER"); GR=(GrimReeper *)this; //to make the finctions available as button methods (gr->Exit() etc) } void GrimReeper::init(){ // initialise char namestring[128]; char titlestring[128]; char timerfunc[128]; pvNames = new TString[MAXPV]; //pv names pvFormula = new TFormula*[MAXPV]; //for dummy PVs which are functions of previously defined PVs. pvFormPV = new int*[MAXPV]; //allow up to MAXFORM PVs in a formula [0] pvMod = new char*[MAXPV]; for(int n=0;nPVScanStep()",className); pvTimer = new TTimer("GR->PVScanStep()",1000*scanPeriod); //create timer with the default time gStyle->SetMarkerSize(1.5); //set dome default styles gStyle->SetMarkerStyle(20); gStyle->SetPadBorderMode(0); setOptFit(1); fprintf(stdout, "\nInitialised\n\n"); isInit = 1; //flag that init was called } int GrimReeper::addPV(const char *pvstring){ //Add a PV to the list for plotting char pvname[128]=""; //pv char tstring[128]=""; //for temporary use char tstringf[128]=""; //for temporary use char fname[128]=""; //formula char checkString[128]; char comString[128]; char namestring[128]; char titlestring[128]; char conststring[128]; char *bracket; char *dptr; char *dptr2; char *sptr; int index; char indstring[10]; char formname[20]; int npar=0; char parstr[2]; if(!isInit) init(); //call init if required pvFormN[nPV]=0; //assume not a formula until we know better pvFormula[nPV]=NULL; strcpy(fname,pvstring); if(strstr(fname,"[")){ //string has "[" - meaning it's a formula //Now it get slightly tricky. - 3 stages: //Stage 1. Hunt for modifiers and store and remove eg. "[2]/[3-]*[1+]" -> "[2]/[3]*[1]" //The +/- are saved and at the point of application of the formula //determine whether to unse the derivateive or integral instead of the PV value. dptr=fname; dptr2=pvname; strcpy(fname,pvstring); strcpy(pvname,pvstring); while(bracket=strstr(dptr,"]")){ //search for all +/- if((*(bracket-1)=='+')|| (*(bracket-1)=='-')){ //check if theres a + or - behind the ] fprintf(stderr,"bracket = %c\n",*(bracket-1)); // - means use the prev point [n-1] in the formula sscanf(bracket-2,"%d",&index); // + means use integral of all prev points [Sigma n] pvMod[nPV][pvFormN[nPV]]=*(bracket-1); // save this to know how to use this PV in the formual sprintf(bracket-1,"%s",bracket); } else{ sscanf(bracket-1,"%d",&index); //get the index pvMod[nPV][pvFormN[nPV]]=0; //no modifications in formua just [n] } pvFormPV[nPV][pvFormN[nPV]]=index; //save the index dptr=bracket+2; //move the destination strin ptr pvFormN[nPV]++; //increment the no of PV used in this calculation } strcpy(pvname,fname); //Stage 2. now the string has all the [+],[-]s stripped out sptr=pvname; //eg [2]/[3-]*[1+] -> [2]/[3]*[1] pvFormN[nPV]=0; while(bracket=strstr(sptr,"[")){ //replace the indices with the proper pv names to make titles. strcpy(tstring,bracket+3); sscanf(bracket+1,"%d",&index); sprintf(bracket,""); if(pvMod[nPV][pvFormN[nPV]]=='-'){ sprintf(bracket,"#Delta%s%s",pvNames[pvFormPV[nPV][pvFormN[nPV]]].Data(),tstring); } else if(pvMod[nPV][pvFormN[nPV]]=='+'){ sprintf(bracket,"#Sigma%s%s",pvNames[pvFormPV[nPV][pvFormN[nPV]]].Data(),tstring); } else{ sprintf(bracket,"%s%s",pvNames[pvFormPV[nPV][pvFormN[nPV]]].Data(),tstring); } sptr=bracket+strlen(pvNames[pvFormPV[nPV][pvFormN[nPV]]].Data())+1; fprintf(stderr,"pvname = %s\n",pvname); pvFormN[nPV]++; } npar=0; bracket=fname; while(bracket=strstr(bracket,"[")){ //stage 3 replace all the [n] with contiguous parameter indices sprintf(parstr,"%d",npar++); //eg [2]/[3-]*[1+] -> [0]/[1]*[2] strncpy(bracket+1,parstr,1); bracket++; fprintf(stderr,"fname = %s\n",fname); } fprintf(stderr,"Translating Formula to dummy PV: %s -> %s -> %s\n", pvstring, pvname,fname ); sprintf(formname,"pv%d_form",nPV); pvFormula[nPV] = new TFormula(formname,fname); } else{ sscanf(pvstring, "%s", pvname); //scan PV into new string to strip any whitespace before/after PV name sprintf(checkString,"cainfo %s | grep State:",pvname); //print the cainfo command to figure if the channel exists returnString = gSystem->GetFromPipe(checkString); //capture the result from the command if(strstr(returnString.Data(),"never")){ //return -1 if pv not found from chan access fprintf(stdout,"WARNING, PV not found: %s\n",pvname); return -1; } } pvNames[nPV] = pvname; //save the name pvData[nPV] = new Double_t[MAXDATA]; //create an array for it for(int p = 0;pGetName(),grFits[n].Data(), grFitMin[n], grFitMax[n], pvGraph[n]->GetN()); } else{ fprintf(stdout,"%-2d\t%-50s%10s\t\t\t\t%2d\n",n,pvGraph[n]->GetName(),grFits[n].Data(), pvGraph[n]->GetN()); } } fprintf(stdout,"\n"); } void GrimReeper::PVScanStep(){ //This is called by the timer char command[128]; char warning[500]; int ret; char *data, *eptr; int n = 1; int par=-1; int skip=0; if(verbose)fprintf(stdout,"Doing step number %d: ", nPoints); sysTime.Set(); // now=(double)sysTime.GetSec()+double(sysTime.GetNanoSec())*10e-9; now=sysTime.AsDouble(); // now=(double)((10000000000*sysTime.GetSec())+sysTime.GetNanoSec())/10e9; // pvData[Time][nPoints] = (double)scanTime; //save the time as pv0 pvData[Time][nPoints] = now-startTime; //save the time as pv0 scanTime += scanPeriod; //increment the timer if(verbose)fprintf(stdout,"%s\n",getCommandString); returnString = gSystem->GetFromPipe(getCommandString); //read if(verbose)fprintf(stdout,"%s\n",returnString.Data()); //fead in all the PVs data=(char *)returnString.Data(); do { //loop over in this old fashioned way if(pvFormN[n]==0){ //if not a formula pvData[n][nPoints]=strtod(data, &eptr); pvIntegral[n]+=pvData[n][nPoints]; //add to the integral if(verbose)fprintf(stdout,"%-50s\t%g\n",pvNames[n].Data(),pvData[n][nPoints]); data = eptr; } n++; } while ((*eptr)&&(n0){ //if it's a formula skip=0; for(int i=0;i 1 point read if(nPoints<=1){ //skip 1st point if any parameter uses '-' skip=1; break; } pvFormula[n]->SetParameter(i,pvData[par][nPoints]-pvData[par][nPoints-1]); //Diff wth prev point. } else if(pvMod[n][i]=='+'){ pvFormula[n]->SetParameter(i,pvIntegral[par]); //Integral } else{ pvFormula[n]->SetParameter(i,pvData[par][nPoints]); //set the parameter to use current reading for that PV. } } if(!skip)pvData[n][nPoints]=pvFormula[n]->Eval(1); //evaluate } } if(userRead){ gROOT->ProcessLine(userRead); } nPoints++; if(isGUI)updateStatus(); if(nPoints>=MAXDATA){ stopScanning(); sprintf(warning,"The limit of %d sets of readings has been reached.", MAXDATA); confirmDialog("Message from the GRIM REEPER",warning,kMBIconStop,kMBOk); updateStartButton(); updatePauseButton(); return; } //if it's writing a PV, do the next step if(scanPVIndex){ scanVal+=scanStep; if(scanVal<=scanStop){ sprintf(command,"caput -t %s %g > /dev/null", pvNames[scanPVIndex].Data(), scanVal); //go to the next position gSystem->Exec(command); } else{ scanVal-=scanStep; stopScanning(); } } if(userWrite){ gROOT->ProcessLine(userWrite); } if((nPoints%refreshRate==0)||(nPoints==1)) drawGraphs(); //try updating if((nPointsInScan)&&(nPoints>=nPointsInScan)){ //if scan completed stopScanning(); } } void GrimReeper::startScanning(){ char command[128]; char warning[500]; int ret; if(scanStatus==GR_STARTED){ fprintf(stdout,"WARNING: The scan is already started\n"); return; //If it's already started return } if(scanStatus==GR_STOPPED){ if(nPV<=1){ fprintf(stderr,"\nWARNING, there are no PVs defined. Scanning is meaningless!\n"); return; } if(nPoints>0){ sprintf(warning,"WARNING. This will clear all graphs and restart any scans.\nSave first if you need to.\n"); //make a big string fprintf(stdout,"%s",warning); //print on stdout, and open confim dialogue ret=confirmDialog("Message from the GRIM REEPER",warning,kMBIconExclamation,kMBCancel+kMBOk); if(ret!=kMBOk) return; //Don't start unless OK else resetGrim(); } if(scanPVIndex){ //if time is not the control variable, init the control variable before starting scan if(confirmScan){ //if cautious, ask for confirmation sprintf(warning,"SCANNING: About to start scan which will cause values to be written to a PV as follows:\n"); //make a big string sprintf(warning,"%sPV name: %s\n",warning,pvNames[scanPVIndex].Data()); sprintf(warning,"%sStart: %g\n",warning,scanStart); sprintf(warning,"%sStop: %g\n",warning,scanStop); sprintf(warning,"%sStep: %g\n",warning,scanStep); sprintf(warning,"%sPeriod(s): %d ( = time to wait between steps)\n",warning,scanPeriod); sprintf(warning,"%sARE YOU REALLY SURE YOU WANT TO DO THIS ? \n",warning); //fprintf(stdout,"%s",warning); //print on stdout, and open confim dialogue ret=confirmDialog("Message from the GRIM REEPER",warning,kMBIconExclamation,kMBCancel+kMBOk); if(ret!=kMBOk) return; //Don't start unless OK } for(int n=0;n /dev/null", pvNames[scanPVIndex].Data(), scanStart); //go to the starting poition sprintf(command,"caput %s %g", pvNames[scanPVIndex].Data(), scanStart); //go to the starting poition nPointsInScan=0; //override any limit to the no of steps, since this might stop the scan gSystem->Exec(command); scanVal=scanStart; //save this for the next steps } if(userWrite){ //The user has some proceedure to write EPICS. Warn the user! if(confirmScan){ //if cautious, ask for confirmation sprintf(warning,"WARNING form the GRIM REEPER\n"); //make a big string sprintf(warning,"%sIt looks like you are about to write to EPICS PVs with your own custom procedure\n",warning); sprintf(warning,"%sARE YOU REALLY SURE YOU WANT TO DO THIS ? \n",warning); fprintf(stdout,"%s",warning); //print on stdout, and open confim dialogue ret=confirmDialog("Message from the GRIM REEPER",warning,kMBIconExclamation,kMBDismiss+kMBOk); if(ret!=kMBOk) return; //Don't start unless OK } if(userInit){ //if init function, call before starting gROOT->ProcessLine(userInit); } } } sysTime.Set(); // startTime=(double)sysTime.GetSec()+double(sysTime.GetNanoSec())*10e-9; startTime=sysTime.AsDouble(); pvTimer->Start(scanPeriod*1000); //start the timer which calls the scan step function scanStatus = GR_STARTED; if(isGUI)updateGUI(); } void GrimReeper::stopScanning(){ if(pvTimer){ pvTimer->Stop(); fprintf(stdout,"\nStopped any scans\n"); scanStatus = GR_STOPPED; if(isGUI)updateGUI(); if(userEnd){ //if end function fprintf(stdout,"\nCalling %s\n",userEnd); gROOT->ProcessLine(userEnd); } if(saveAndExit){ saveAll(); exitGrim(); } } } void GrimReeper::pauseScanning(){ if(pvTimer){ pvTimer->Stop(); fprintf(stdout,"\nPaused any scans\n"); scanStatus = GR_PAUSED; if(isGUI)updateGUI(); } } void GrimReeper::resumeScanning(){ if(pvTimer){ pvTimer->Start(); fprintf(stdout,"\nResumed any scans\n"); scanStatus = GR_STARTED; if(isGUI)updateGUI(); } } void GrimReeper::makeGraph(const char *yPVName, const char *xPVName, const char* pvfit, double rangemin, double rangemax, int canv, int pad) { int x=-1,y=-1; for(int n=0;n=0)&&(y=0)&&(x=nPV)||(yPV>=nPV)||(xPV==yPV)){ fprintf(stderr,"ERROR: plotting set[%d] vs set[%d] is not possible. Available sets are:",yPV,xPV); printPVs(); fprintf(stderr,"Select 2 different sets from the above list"); return; } for(int n=0; nSetMarkerColor(goodColors[nGraphs%11]); pvGraph[nGraphs]->SetMarkerStyle(goodMarkers[nGraphs%5]); pvGraphX[nGraphs] = xPV; pvGraphY[nGraphs] = yPV; sprintf(titlestring,"pvGraph[%d]: %s v %s", nGraphs, pvNames[pvGraphY[nGraphs]].Data(),pvNames[pvGraphX[nGraphs]].Data()); sprintf(namestring,"graph%d_%s_v_%s", nGraphs, pvNames[pvGraphY[nGraphs]].Data(),pvNames[pvGraphX[nGraphs]].Data()); fprintf(stderr,"%s %s %s\n", titlestring,namestring, pvfit); pvGraph[nGraphs]->SetTitle(titlestring); pvGraph[nGraphs]->SetName(namestring); //store the fit for the graph //fprintf(stderr,"pvfitstr = %s\n",pvfit); if(strlen(pvfit)>2){ grFits[nGraphs].Append(pvfit); grFitMin[nGraphs] = rangemin; grFitMax[nGraphs] = rangemax; sprintf(grString,"grTF1_%d",nGraphs), grTF1[nGraphs] = new TF1(grString,pvfit,rangemin,rangemax); if(TMath::Abs(rangemax-rangemin)<0.000001){ //tiny, so don't use fit range grFitDo[nGraphs] = GR_FIT; } else{ grFitDo[nGraphs] = GR_FITRANGE; } } else{ grFitDo[nGraphs]=GR_NOFIT; grFits[nGraphs].Append(pvfit); } //make title and name for the graph sprintf(titlestring,"%s v %s", pvNames[pvGraphY[nGraphs]].Data(),pvNames[pvGraphX[nGraphs]].Data()); sprintf(namestring,"Graph%d_%s_v_%s", nGraphs, pvNames[pvGraphY[nGraphs]].Data(),pvNames[pvGraphX[nGraphs]].Data()); xcoord = GUIW + 20 + (XPADD*nGraphs); ycoord = 30+YPADD*nGraphs; if(canv==-1){ //make a new canvas for single graph sprintf(cnamestring,"grCanvas_%d",nCanv); gCanv[nCanv]=new TCanvas(cnamestring,titlestring,xcoord,ycoord,CANVW,0.6*CANVH); gCanv[nCanv]->Divide(1,1,0.001,0.001); grCanvas[nGraphs]=gCanv[nCanv]; grCanvas[nGraphs]->GetPad(1)->SetBorderMode(0); nCanv++; } else{ if(gCanv[canv]==NULL){ sprintf(cnamestring,"grCanvas_%d",nCanv); gCanv[canv]=new TCanvas(cnamestring,titlestring,xcoord,ycoord,CANVW,perCanvas*0.6*CANVH); nCanv++; gCanv[canv]->Divide(1,perCanvas); } grCanvas[nGraphs]=gCanv[canv]; } grPad[nGraphs]=pad; grCanvas[nGraphs]->cd(grPad[nGraphs]); grCanvas[nGraphs]->GetPad(grPad[nGraphs])->SetName(namestring); if(nGraphs==0){ cl = (grCanvas[nGraphs]->GetPad(1))->IsA(); //cl->SetContextMenuTitle("GrimReeper"); l = cl->GetMenuList(); //l->RemoveAll(); mi = new TClassMenuItem(TClassMenuItem::kPopupUserFunction,cl,"toggle autoscale","SetAuto",GR,"TObject *",0); l->AddFirst(mi); } graphAuto[nGraphs]=GR_AUTO; //default to auto scaling graphXfirst[nGraphs] = 0.0; graphXlast[nGraphs] = 0.0; graphYfirst[nGraphs] = 0.0; graphYlast[nGraphs] = 0.0; sprintf(namestring,"%s_v_%s", pvNames[pvGraphY[nGraphs]].Data(),pvNames[pvGraphX[nGraphs]].Data()); fprintf(stdout,"Added graph %s\n",namestring); pvGraph[nGraphs]->Draw("AL"); nGraphs++; if(isGUI==GR_BIGGUI){ updateGraphList(); } } //This gets called from the graph menu, passing the object associated with the menu. void GrimReeper::SetAuto(TObject *gr){ int g; if(strstr(gr->GetName(),"Multi")){ //fprintf(stderr,"MULTITOGGLE %s\n",gr->GetName()); sscanf(gr->GetName(),"MultiGraphCanvas%d",&g); setMultiAuto(g,GR_TOGGLE); } else{ sscanf(gr->GetName(),"Graph%d_",&g); setGraphAuto(g,GR_TOGGLE); } } //This gets called from the graph menu, passing the object associated with the menu. int GrimReeper::GetAuto(TObject *gr){ int g; if(strstr(gr->GetName(),"Multi")){ //fprintf(stderr,"MULTITOGGLE %s\n",gr->GetName()); sscanf(gr->GetName(),"MultiGraphCanvas%d",&g); return getMultiAuto(g); } else{ sscanf(gr->GetName(),"Graph%d_",&g); return getGraphAuto(g); } } void GrimReeper::setGraphAuto(int g, int a){ if(a==GR_TOGGLE)graphAuto[g]=!graphAuto[g]; else graphAuto[g]=a; if(isGUI==GR_BIGGUI)updateGraphList(); } int GrimReeper::getGraphAuto(int g){ return graphAuto[g]; } void GrimReeper::setMultiAuto(int g, int a){ double xf,xl; if(a==GR_TOGGLE)multiAuto[g]=!multiAuto[g]; else multiAuto[g]=a; if(multiAuto[g]==GR_MANUAL){ xf = pvMultiGraph[g]->GetXaxis()->GetBinCenter(pvMultiGraph[g]->GetXaxis()->GetFirst()); xl = pvMultiGraph[g]->GetXaxis()->GetBinCenter(pvMultiGraph[g]->GetXaxis()->GetLast()); multiXfirst[g]=xf; multiXlast[g]=xl; } if(isGUI==GR_BIGGUI)updateMultiList(); } int GrimReeper::getMultiAuto(int g){ return multiAuto[g]; } void GrimReeper::updateGraph(int g){ double xf=0.0,xl=0.0; if(nPoints==1){ //if its 1st point, replace the dummy 0,0 point with the real one. pvGraph[g]->SetPoint(0,pvData[pvGraphX[g]][0],pvData[pvGraphY[g]][0]); } if(graphAuto[g]==GR_MANUAL){ // xf = pvGraph[g]->GetXaxis()->GetBinLowEdge(pvGraph[g]->GetXaxis()->GetFirst()); // xl = pvGraph[g]->GetXaxis()->GetBinUpEdge(pvGraph[g]->GetXaxis()->GetLast()); xf = pvGraph[g]->GetXaxis()->GetBinCenter(pvGraph[g]->GetXaxis()->GetFirst()); xl = pvGraph[g]->GetXaxis()->GetBinCenter(pvGraph[g]->GetXaxis()->GetLast()); graphXfirst[g]=xf; graphXlast[g]=xl; // if((TMath::Abs((xf-graphXfirst[g])/graphXfirst[g]))>0.2)graphXfirst[g]=xf; // if((TMath::Abs((xl-graphXlast[g])/graphXlast[g]))>0.2)graphXlast[g]=xl; graphYfirst[g]=pvGraph[g]->GetHistogram()->GetMinimum(); graphYlast[g]=pvGraph[g]->GetHistogram()->GetMaximum(); } if(nPoints>pvGraph[g]->GetN()){ for(int p=pvGraph[g]->GetN();pSetPoint(p,pvData[pvGraphX[g]][p],pvData[pvGraphY[g]][p]); } } if(graphAuto[g]==GR_MANUAL) pvGraph[g]->GetXaxis()->SetRangeUser(xf,xl); } void GrimReeper::drawGraphs(){ double xf,xl; for(int m=0;mGetXaxis()->GetBinCenter(pvMultiGraph[m]->GetXaxis()->GetFirst()); xl = pvMultiGraph[m]->GetXaxis()->GetBinCenter(pvMultiGraph[m]->GetXaxis()->GetLast()); multiXfirst[m]=xf; multiXlast[m]=xl; } } for(int g=0;gcd(grPad[g]); if((grFitDo[g]==GR_NOFIT)&&(strlen(grFits[g].Data())>0)){ strcat(dopt,grFits[g].Data()); } if(graphAuto[g]==GR_MANUAL){ pvGraph[g]->GetXaxis()->SetRangeUser(graphXfirst[g],graphXlast[g]); pvGraph[g]->GetHistogram()->SetMinimum(graphYfirst[g]); pvGraph[g]->GetHistogram()->SetMaximum(graphYlast[g]); } else{ pvGraph[g]->GetXaxis()->SetRange(0.0,0.0); pvGraph[g]->GetYaxis()->UnZoom(); } pvGraph[g]->Draw(dopt); if(pvGraphX[g]==Time)pvGraph[g]->GetHistogram()->GetXaxis()->SetTitle("Time (s)"); else pvGraph[g]->GetHistogram()->GetXaxis()->SetTitle(pvNames[pvGraphX[g]].Data()); pvGraph[g]->GetHistogram()->GetYaxis()->SetTitle(pvNames[pvGraphY[g]].Data()); ((TRootCanvas*)grCanvas[g]->GetCanvasImp())->DontCallClose(); //don't allow closing the graph canvas pvGraph[g]->Draw(dopt); if((grFitDo[g])&&(nPoints>10)){ //only apply fits after 10 points sprintf(fopt,"q%s",fitopt); if(grFitDo[g]==GR_FIT){ // pvGraph[g]->Fit(grFits[g].Data(),fopt); pvGraph[g]->Fit(grTF1[g],fopt); } else{ sprintf(fopt,"q%sR",fitopt); //pvGraph[g]->Fit(grFits[g].Data(),fopt,"", grFitMin[g],grFitMax[g]); pvGraph[g]->Fit(grTF1[g],fopt,""); } } grCanvas[g]->Update(); } void GrimReeper::makeMultiGraph(const char *title, const char *g0,const char *g1,const char *g2,const char *g3,const char *g4,const char *g5,const char *g6,const char *g7,const char *g8,const char *g9){ const char *mc[10]; mc[0]=g0;mc[1]=g1;mc[2]=g2;mc[3]=g3;mc[4]=g4;mc[5]=g5;mc[6]=g6;mc[7]=g7;mc[8]=g8;mc[9]=g9; int mg[10]; int match=-1; int mcount=0; for(int n=0;n<10;n++){ fprintf(stderr,"making multi %d, %s\n",n,mc[n]); mg[n]=-1; } int g=0; while(mc[g]!=NULL){ match=-1; for(int n=0;nGetName(),"_")+1,strcmp(mc[g],strstr(pvGraph[n]->GetName(),"_")+1)); if(strcmp(mc[g],strstr(pvGraph[n]->GetName(),"_")+1)==0){ match=n; break; } } if(match==-1){ fprintf(stderr, "Error: makeMultiGraph(\"%s\",....) graph \"%s\" does not exist\n",title,mc[g]); } else{ mg[mcount++]=match; } g++; } if(mcount>1){ makeMultiGraph(title,mg); } } void GrimReeper::makeMultiGraph(const char *title, int g0,int g1,int g2,int g3,int g4,int g5,int g6,int g7,int g8,int g9){ //This one gets called by hand - allowing up to 10 graphs on the command line //Just fills an array and calls the proper version of makeMultiGraph int mg[10]; mg[0]=g0;mg[1]=g1;mg[2]=g2;mg[3]=g3;mg[4]=g4;mg[5]=g5;mg[6]=g6;mg[7]=g7;mg[8]=g8;mg[9]=g9; makeMultiGraph(title,mg); } void GrimReeper::makeMultiGraph(const char *title,int *mg){ char namestring[128]; char titlestring[128]; int mcount=0; TClass *cl; TList*l; TClassMenuItem* mi; legend = new TLegend(0.1,0.7,0.6,0.90); //Create a legend sprintf(titlestring,"pvMultiGraph[%d]: %s",nMulti,title); //make name and titles sprintf(namestring,"MultiGraph%d",nMulti); pvMultiGraph[nMulti] = new TMultiGraph(namestring,titlestring); //new multigraph for(int n=0; n<10; n++){ //init the list of indices multiIndices[nMulti][n]=-1; } for(int g=0;g<10;g++){ //loop over the requested graphs if(mg[g]==-1) break; if(!pvGraph[mg[g]]){ //check they exist fprintf(stdout,"Warning: pvGraph[%d] doesn't exist\n",mg[g]); } else{ pvMultiGraph[nMulti]->Add(pvGraph[mg[g]]); //add graph, save index and add to legend multiIndices[nMulti][mcount++]=mg[g]; legend->AddEntry(pvGraph[mg[g]],pvNames[pvGraphY[mg[g]]],"pl"); } } multiAuto[nMulti] = GR_AUTO; //default to auto scaling multiXfirst[nMulti] = 0.0; multiXlast[nMulti] = 0.0; multiYfirst[nMulti] = 0.0; multiYlast[nMulti] = 0.0; sprintf(namestring,"MultiGraphCanvas%d",nMulti); //name and create canvas multiCanvas[nMulti] = new TCanvas(namestring,titlestring,1250+CANVW+2*XPADD*nMulti,2*YPADD*(nMulti),MCANVW,MCANVH); multiCanvas[nMulti]->Divide(1,1,0.001,0.001); multiCanvas[nMulti]->cd(1); ((TRootCanvas*)multiCanvas[nMulti]->GetCanvasImp())->DontCallClose(); //don't allow closing the graph canvas pvMultiGraph[nMulti]->Draw("ap"); if(pvGraphX[mg[0]]==Time) pvMultiGraph[nMulti]->GetXaxis()->SetTitle("Time (s)"); //If time, give x axis label units of s else pvMultiGraph[nMulti]->GetXaxis()->SetTitle(pvNames[pvGraphX[mg[0]]]); //else just pv name legend->Draw(); multiCanvas[nMulti]->Modified(); //force the drawing before coloring the canvas multiCanvas[nMulti]->Update(); //to keep the actual graph white multiCanvas[nMulti]->SetFillColor(20+(3*nMulti)); multiCanvas[nMulti]->GetPad(1)->SetBorderMode(0); multiCanvas[nMulti]->GetPad(1)->SetFillColor(20+(3*nMulti)); multiCanvas[nMulti]->Modified(); multiCanvas[nMulti]->Update(); nMulti++; if(isGUI==GR_BIGGUI) updateMultiList(); //if there's a GUI update the list. } void GrimReeper::updateMultiGraphs(){ for(int g=0;gGetListOfGraphs(); TIter next(l); xmin = ((TGraph*)l->First())->GetXaxis()->GetXmin(); xmax = ((TGraph*)l->First())->GetXaxis()->GetXmax(); ymin = ((TGraph*)l->First())->GetYaxis()->GetXmin(); ymax = ((TGraph*)l->First())->GetYaxis()->GetXmax(); while((g=(TGraph*)next())){ tmin=g->GetXaxis()->GetXmin(); tmax=g->GetXaxis()->GetXmax(); umin=g->GetYaxis()->GetXmin(); umax=g->GetYaxis()->GetXmax(); if(tminxmax)xmax=tmax; if(uminymax)ymax=umax; } pvMultiGraph[m]->GetXaxis()->SetLimits(xmin,xmax); if(multiAuto[m]==GR_AUTO)pvMultiGraph[m]->GetXaxis()->SetRangeUser(xmin,xmax); else pvMultiGraph[m]->GetXaxis()->SetRangeUser(multiXfirst[m],multiXlast[m]); pvMultiGraph[m]->GetYaxis()->SetLimits(ymin,ymax); if(nPoints<10)pvMultiGraph[m]->GetYaxis()->SetRangeUser(ymin,ymax); //autoscale on the basis of the 1st 10 points if(multiAuto[m]==GR_AUTO)pvMultiGraph[m]->GetYaxis()->SetRangeUser(ymin,ymax); multiCanvas[m]->cd(1); gPad->Modified(); multiCanvas[m]->Update(); } void GrimReeper::changePeriod(int t){ scanPeriod += t; if(scanPeriod<1)scanPeriod=1; pvTimer->SetTime(1000*scanPeriod); if(isGUI)updateGUI(); } void GrimReeper::saveAll(){ //save data in column format and save canvases in some sort of format FILE *fp; char filename[200]; char rmlink[200]; char mklink[200]; char command[200]; char warning[500]; int ret; TString date = gSystem->GetFromPipe("date '+%d_%m_%y-%H_%M'"); //get date for timestamping files sprintf(filename,"%s/gr_save_%s.txt",outdir,date.Data()); sprintf(rmlink,"cd %s; /bin/rm -f *_latest.*",outdir); sprintf(mklink,"cd %s;ln -s gr_save_%s.txt gr_save_latest.txt",outdir,date.Data()); if(isGUI){ //if there's a GUI ask for confirmation sprintf(warning,"Save all data.\n"); //make a big string sprintf(warning,"%sAll PV data will be saved in columns in file: %s \n",warning,filename); sprintf(warning,"%s and graph canvases as %s/_%s. \n",warning,outdir,date.Data()); if(logCommand){ sprintf(warning,"%s The following command will also be issued to save information to the log book:\n",warning); sprintf(warning,"%s %s\n",warning,logCommand); } fprintf(stdout,"%s\n",warning); //print on stdout, and open confim dialogue ret=confirmDialog("Message from the GRIM REEPER",warning,kMBIconQuestion,kMBCancel+kMBOk); if(ret!=kMBOk) return; //Don't startsave unless OK } sprintf(command,"mkdir -p %s",outdir); //make the output directory if it doesn't exist gSystem->Exec(command); gSystem->Exec(rmlink); //remove any previous link file fp = fopen(filename,"w"); for(int n=0;nExec(mklink); //remove any previous link filemake a new link for(int g=0;gGetName(),date.Data(),saveFormats[f]); sprintf(mklink,"cd %s;ln -s %s_%s.%s %s_latest.%s ",outdir,gCanv[g]->GetName(),date.Data(),saveFormats[f],gCanv[g]->GetName(),saveFormats[f]); fprintf(stderr,"Saving %s\n",filename); gCanv[g]->SaveAs(filename); gSystem->Exec(mklink); //make a new link file f++; } } if(logCommand){ gSystem->Exec(logCommand); } } void GrimReeper::resetGrim(){ nPoints = 0; //reset npoints scanTime=0.0; for(int g=0;gSet(1); pvGraph[g]->SetPoint(0,0.0,0.0); if(pvGraph[g]->GetListOfFunctions()->First()) delete pvGraph[g]->GetListOfFunctions()->First(); } if(isGUI)updateGUI(); } int GrimReeper::confirmDialog(const char *title, char *message, EMsgBoxIcon icon, Int_t buttons){ TGMsgBox *box; int ret=0; //get confirmation from dialog box if isGUI, otherwise from root CINT. if(isGUI){ box = new TGMsgBox(gClient->GetRoot(),0,title, message,icon,buttons,&ret,0,0); } else{ fprintf(stderr,"** %s **\n",title); if(buttons==kMBOk){ //no confirm reqd fprintf(stderr,"%s\n",message); } else{ fprintf(stderr,"%s (y/n)?",message); //a fudge to allow CINT to get response from the command line (return 4 if OK, since kMBOk = 4). sscanf((gSystem->GetFromPipe("read p;echo $p|awk '{if($1==\"y\"){print 4}else{print 0}}'")).Data(),"%d",&ret); } } return ret; } void GrimReeper::exitGrim(){ stopScanning(); killSimIOC(); exit(0); } void GrimReeper::setScan(int pvindex,double start, double stop, double step){ scanPVIndex = pvindex; scanStart = start; scanStop = stop; scanStep = step; fprintf(stdout,"Scanning %s, %lg ---> %lg, step = %lg\n\n",pvNames[scanPVIndex].Data(),scanStart,scanStop,scanStep); } void GrimReeper::setScan(const char *pvname,double start, double stop, double step){ scanPVIndex = Time; for(int n=1;n %lg, step = %lg\n\n",pvNames[scanPVIndex].Data(),scanStart,scanStop,scanStep); } //Functions for the optional GUI //A simple GUI with only TButtons. However, added functions are on the ContextMenus (RH Mouse button) void GrimReeper::makeControlGUI(int guisize){ char string[200]; char method[200]; TClass *cl; TList*l; TClassMenuItem* mi; if(!guiTimer) guiTimer = new TTimer("GR->toggleGUI()",200); //create timer guiTimer->Start(); gStyle->SetPadBorderMode(1); gStyle->SetPadBorderSize(2); //if(!grimCanvas){ if(1){ if(guisize==GR_SMALLGUI){ grimCanvas = new TCanvas("grimCanvas","grimCanvas", 10, 10, GUIW, 0.45*GUIH); ((TRootCanvas*)grimCanvas->GetCanvasImp())->DontCallClose(); //don't allow closing the canvas titlepad1 = new TPad( "titlepad1", "titlepad1", 0, 0.33, 0.2, 1.0, 0);titlepad1->Draw(); titlepad2 = new TPad( "titlepad2", "titlepad2", 0.2, 0.33, 0.8, 1.0, 0);titlepad2->Draw(); titlepad3 = new TPad( "titlepad3", "titlepad3", 0.8, 0.33, 1.0, 1.0, 0);titlepad3->Draw(); buttonpad = new TPad( "buttonpad", "buttonpad", 0, 0.0, 1.0, 0.33, 0);buttonpad->SetFillColor(30);buttonpad->Draw(); } else{ grimCanvas = new TCanvas("grimCanvas","grimCanvas", 10, 10, GUIW, GUIH); ((TRootCanvas*)grimCanvas->GetCanvasImp())->DontCallClose(); //don't allow closing the canvas titlepad1 = new TPad( "titlepad1", "titlepad1", 0, 0.7, 0.2, 1.0, 0);titlepad1->Draw(); titlepad2 = new TPad( "titlepad2", "titlepad2", 0.2, 0.7, 0.8, 1.0, 0);titlepad2->Draw(); titlepad3 = new TPad( "titlepad3", "titlepad3", 0.8, 0.7, 1.0, 1.0, 0);titlepad3->Draw(); buttonpad = new TPad( "buttonpad", "buttonpad", 0, 0.55, 1.0, 0.7, 0);buttonpad->SetFillColor(30);buttonpad->Draw(); pvpad = new TPad( "pvpad", "pvpad", 0, 0.15, 0.3, 0.55, 0);pvpad->SetFillColor(30);pvpad->Draw(); graphpad = new TPad( "graphpad", "graphpad", 0.3, 0.15, 1.0, 0.55, 0);graphpad->SetFillColor(30);graphpad->Draw(); multipad = new TPad( "multipad", "multipad", 0, 0.00, 1.0, 0.15, 0);multipad->SetFillColor(30);multipad->Draw(); } titlepad1->cd(); sprintf(string,"%s/Grim_Reaper.png",REEPER); TImage *img1 = TImage::Open(string); img1->Draw(); img1->SetConstRatio(kTRUE); img1->SetEditable(kTRUE); titlepad1->SetEditable(kFALSE); titlepad2->cd(); sprintf(string,"%s/welcome2.png",REEPER); TImage *img2 = TImage::Open(string); img2->Draw(); img2->SetConstRatio(kFALSE); img2->SetEditable(kTRUE); titlepad2->SetEditable(kFALSE); titlepad3->cd(); sprintf(string,"%s/EPICS_Logo-192x192.png",REEPER); TImage *img3 = TImage::Open(string); img3->Draw(); img3->SetConstRatio(kTRUE); img3->SetEditable(kTRUE); titlepad3->SetEditable(kFALSE); if(nGraphs > textLines) textLines = nGraphs; if(nPV > textLines) textLines = nPV; textPixels = (GUIH*0.4*0.87)/textLines; buttonpad->SetFillColor(30); buttonpad->SetBorderMode(1); buttonpad->cd(); //sprintf(method,"%s->startScanning()",className); startButton = new TButton("Start","GR->startScanning()",0.02,0.75,0.15,0.9); startButton->Draw(); updateStartButton(); buttonpad->cd(); //sprintf(method,"%s->pauseScanning()",className); pauseButton = new TButton("Pause","GR->pauseScanning()",0.02,0.60,0.15,0.75); pauseButton->Draw(); updatePauseButton(); buttonpad->cd(); //sprintf(method,"%s->saveAll()",className); saveButton = new TButton("Save","GR->saveAll()",0.02,0.45,0.15,0.60); saveButton->Draw(); buttonpad->cd(); //sprintf(method,"%s->exitGrim()",className); exitButton = new TButton("Exit","GR->exitGrim()",0.02,0.3,0.15,0.45); exitButton->Draw(); buttonpad->cd(); //sprintf(method,"%s->setGuiToggle()",className); detailsButton = new TButton("Detail (show/hide)","GR->setGuiToggle()",0.02,0.05,0.15,0.2); detailsButton->Draw(); //sprintf(method,"%s->changePeriod(-1)",className); tDecButton = new TButton("<","GR->changePeriod(-1)",0.36,0.6,0.39,0.72); tDecButton->SetBorderMode(0);tDecButton->SetBorderSize(0);tDecButton->Draw(); //sprintf(method,"%s->changePeriod(1)",className); tIncButton = new TButton(">","GR->changePeriod(1)",0.39,0.6,0.42,0.72); tIncButton->SetBorderMode(0);tIncButton->SetBorderSize(0);tIncButton->Draw(); statuslabel=new TPaveText(0.18,0.1,0.36,0.9,"NB"); statuslabel->SetTextFont(103); statuslabel->SetFillColor(30); statuslabel->SetTextAlign(32); statuslabel->SetTextSize(textPixels); statuslabel->AddText("nPoints:"); statuslabel->AddText("Period(s):"); statuslabel->AddText("Scanning:"); statuslabel->AddText("Init/End funcs:"); statuslabel->AddText("Read/Write funcs:"); statuslabel->Draw(); statustext=new TPaveText(0.42,0.1,0.98,0.9,"NB"); statustext->SetTextFont(83); statustext->SetFillColor(30); statustext->SetTextAlign(12); statustext->SetTextSize(textPixels); statustext->Draw(); buttonpad->SetEditable(kFALSE); updateStatus(); if(guisize==GR_BIGGUI){ //pv info pad pvpad->cd(); //if(nPV>maxlines)maxlines=nPV; pvlabel=new TPaveText(0.02,0.90,0.98,0.98,"NB"); pvlabel->SetTextFont(103); pvlabel->SetFillColor(30); pvlabel->SetTextAlign(12); pvlabel->SetTextSize(textPixels); pvlabel->AddText(" PV"); pvlabel->Draw(); pvtext=new TPaveText(0.02,0.02,0.98,0.89,"NB"); pvtext->SetFillColor(30); pvtext->SetTextFont(83); pvtext->SetTextAlign(12); pvtext->SetTextSize(textPixels); updatePvList(); //graph info pad graphpad->cd(); graphlabel=new TPaveText(0.02,0.90,0.90,0.98,"NB"); graphlabel->SetTextFont(103); graphlabel->SetFillColor(30); graphlabel->SetTextAlign(12); graphlabel->SetTextSize(textPixels); graphlabel->AddText(" Graph yPV,xPV Autoscale"); graphlabel->Draw(); graphtext=new TPaveText(0.02,0.02,0.90,0.89,"NB"); graphtext->SetFillColor(30); graphtext->SetTextFont(83); graphtext->SetTextAlign(12); graphtext->SetTextSize(textPixels); //graphtext->AddText(""); updateGraphList(); //multi info pad multipad->cd(); multilabel=new TPaveText(0.00,0.78,0.986,0.96,"NB"); multilabel->SetTextFont(103); multilabel->SetFillColor(30); multilabel->SetTextAlign(12); multilabel->SetTextSize(textPixels); multilabel->AddText(" Multi name Graph indices Autoscale"); multilabel->Draw(); multitext=new TPaveText(0.00,0.04,0.986,0.76,"NB"); multitext->SetFillColor(30); multitext->SetTextFont(83); multitext->SetTextAlign(12); multitext->SetTextSize(textPixels); updateMultiList(); } //as soon as pads are made, set the TTextLabel context menu to have extra items for addPV, makeGraph and makeMultiGraph cl = statustext->IsA(); cl->SetContextMenuTitle("GrimReeper"); l = cl->GetMenuList(); l->RemoveAll(); //mi = new TClassMenuItem(TClassMenuItem::kPopupUserFunction,cl,"Make new multigraph","GR->makeMultiGraph",0,"char *title, int, int, int, int, int, int, int, int, int, int"); mi = new TClassMenuItem(TClassMenuItem::kPopupUserFunction,cl,"Make new multigraph","makeMultiGraph",GR,"char *title, int, int, int, int, int, int, int, int, int, int"); l->AddFirst(mi); mi = new TClassMenuItem(TClassMenuItem::kPopupUserFunction,cl,"Make new graph","makeGraph",GR,"int, int, const char*, double, double"); l->AddFirst(mi); mi = new TClassMenuItem(TClassMenuItem::kPopupUserFunction,cl,"Add new PV","addPV",GR,"char *"); l->AddFirst(mi); grimCanvas->cd(); // isGUI = guisize; guiToggle = 0; //add a special popup menu to the toolbar with some info TRootCanvas *imp = (TRootCanvas*)grimCanvas->GetCanvasImp(); TGMenuBar *bar = imp->GetMenuBar(); TGPopupMenu *popup = bar->AddPopup("GrimReeper"); popup->AddEntry("Use the Contex Menu (RH mouse) to access the extra Grim Reeper functions:", 1); popup->AddEntry("AddPV(), makeGraph(), makeMultiGraph() functions from the text area below", 1); popup->AddEntry("Autoscaling() from any of the graph Canvases", 1); popup->AddSeparator(); popup->AddEntry("See manual at http://nuclear.gla.ac.uk/~kl/epics/grimReeper/grimReeper.html", 1); bar->MapSubwindows(); bar->Layout(); } } void GrimReeper::updatePvList(){ char string[100]; //pv info pad pvpad->SetEditable(kTRUE); pvpad->cd(); pvtext->Clear(); for(int n=0;nAddText(string); } pvtext->Draw(); pvpad->SetEditable(kFALSE); } void GrimReeper::updateGraphList(){ char string[100]; char fstring[100]; //graph info pad graphpad->SetEditable(kTRUE); graphpad->cd(); graphtext->Clear(); for(int n=0;nGetName(),"_")+1,pvGraphY[n],pvGraphX[n],onOff[graphAuto[n]]); } else{ sprintf(string,""); } graphtext->AddText(string); } graphtext->Draw(); graphpad->SetEditable(kFALSE); if(nGraphs>maxlines){ maxlines=nGraphs; updatePvList(); //to keep the spacing the same in both boxes. } } void GrimReeper::updateMultiList(){ char string[100]; char multistring[50]=""; int l=0; //multi info pad multipad->SetEditable(kTRUE); multipad->cd(); multitext->Clear(); if(nMulti){ for(int n=0;n<(int)(maxlines/4);n++){ if(n0)strcat(multistring,","); sprintf(multistring,"%s%d",multistring,multiIndices[n][l]); l++; } sprintf(string,"%d %-38s%-42s%s",n,strstr(multiCanvas[n]->GetTitle(),":")+2,multistring,onOff[multiAuto[n]]); } else sprintf(string,""); multitext->AddText(string); } multitext->Draw(); } multipad->SetEditable(kFALSE); grimCanvas->cd(); multipad->Update(); grimCanvas->Update(); } void GrimReeper::updateGUI(){ updateStatus(); updateStartButton(); updatePauseButton(); if(isGUI==GR_BIGGUI){ if(nGraphs > textLines) textLines = nGraphs; if(nPV > textLines) textLines = nPV; textPixels = (GUIH*0.4*0.87)/textLines; } } void GrimReeper::updateStartButton(){ buttonpad->cd(); if((scanStatus==GR_STARTED)||(scanStatus==GR_PAUSED)){ startButton->SetMethod("GR->stopScanning()"); startButton->SetTitle("Stop"); startButton->SetFillColor(kRed); startButton->Draw(); } else{ startButton->SetMethod("GR->startScanning()"); startButton->SetTitle("Start"); startButton->SetFillColor(kGreen); startButton->Draw(); } buttonpad->Modified(); startButton->Modified(); startButton->Update(); } void GrimReeper::updatePauseButton(){ buttonpad->cd(); if(scanStatus==GR_STARTED){ pauseButton->SetMethod("GR->pauseScanning()"); pauseButton->SetTitle("Pause"); pauseButton->SetFillColor(50); pauseButton->Draw(); } else if (scanStatus==GR_PAUSED){ pauseButton->SetMethod("GR->resumeScanning()"); pauseButton->SetTitle("Resume"); pauseButton->SetFillColor(5); pauseButton->Draw(); } else{ pauseButton->SetMethod(""); pauseButton->SetTitle("pause"); pauseButton->SetFillColor(19); pauseButton->Draw(); } pauseButton->Modified(); pauseButton->Update(); } void GrimReeper::updateStatus(){ buttonpad->cd(); char string[100]; char read[100]; char write[100]; statustext->Clear(); sprintf(string,"%-6d", nPoints); statustext->AddText(string); sprintf(string,"%-6d", scanPeriod); statustext->AddText(string); if(scanPVIndex){ sprintf(string,"%s (%5.2f-%5.2f,step %4.2f) %5.2f", pvNames[scanPVIndex].Data(),scanStart,scanStop,scanStep,scanVal ); } else{ sprintf(string,"------"); } statustext->AddText(string); if(userInit==NULL){ sprintf(read,"------"); } else{ sprintf(read,"%s",userInit); } if(userEnd==NULL){ sprintf(write,"------"); } else{ sprintf(write,"%s",userEnd); } sprintf(string,"%s/%s",read,write); statustext->AddText(string); if(userRead==NULL){ sprintf(read,"------"); } else{ sprintf(read,"%s",userRead); } if(userWrite==NULL){ sprintf(write,"------"); } else{ sprintf(write,"%s",userWrite); } sprintf(string,"%s/%s",read,write); statustext->AddText(string); buttonpad->Modified(); buttonpad->Update(); grimCanvas->cd(); buttonpad->Update(); } void GrimReeper::setGuiToggle(){ guiToggle=1; } void GrimReeper::toggleGUI(){ if(!guiToggle) return; int newsize=0; if(isGUI==GR_BIGGUI) newsize=GR_SMALLGUI; else if (isGUI==GR_SMALLGUI) newsize=GR_BIGGUI; makeControlGUI(newsize); } //Some functions to read waveforms into doubles. //get a waveform into an array of doubles, void GrimReeper::waveToArray(const char *pv, double *array, int n, int add ){ int cnt = 0; char command[200]; TString response; char *data, *eptr; int elem; sprintf(command,"caget -t %s",pv); //caget to read waveform: "n a1 a2 .......an" response = gSystem->GetFromPipe(command); data=(char *)response.Data(); elem=strtod(data, &eptr); //read the no of elements in the waveform data=eptr; do { //loop over in this old fashioned way if(add){ array[cnt++]+=strtod(data, &eptr); } else{ array[cnt++]=strtod(data, &eptr); } data = eptr; } while ((*eptr)&&(cntGetArray()+1,h->GetNbinsX(),add); } //get the waveform directly into a row of a TH2D void GrimReeper::waveToTH2DRow(const char *pv,TH2D *h, double y, int add){ h->Fill(h->GetXaxis()->GetBinCenter(0),y); //seem to need something to force it to realise it's modified //fprintf(stderr,"%d\n",h->GetYaxis()->FindBin(y)*(h->GetNbinsX()+2)); waveToArray(pv, h->GetArray()+h->GetYaxis()->FindBin(y)*(h->GetNbinsX()+2)+1,h->GetNbinsX(),add); } void GrimReeper::simIOC(int m){ //run up the test IOC char command[200]; mode = m; killSimIOC(); //kill the running one you forgot about sprintf(command,"xterm -e \"cd %s;softIoc st.cmd_reeper\"&",REEPER); gSystem->Exec(command); iocTimer = new TTimer("GR->writeTestPVs()",1000*updatePeriod); //call this function iocTimer->Start(); fprintf(stdout, "\nAttempted to start testIOC in a separate xterm (softIoc,caput etc must be in the PATH)\n\n"); fprintf(stdout, "The following, correlated, test PVs are being updated every %ds:\n",updatePeriod); fprintf(stdout, "PV23004\ncolliX\nPMT1\nFcup\nXsetting\nYsetting\nYsetting\nBigScaler\n"); fprintf(stdout, "(Names chosen to avoid conflict with real PVs)\n\n"); fprintf(stdout, "To stop this process type killTestIOC() at the ROOT prompt, or exit in the xterm with the ioc\n\n"); gSystem->Sleep(2000); //wait 2s before we try to access it } void GrimReeper::killSimIOC(){ if(iocTimer)iocTimer->Stop(); //stop the timer and kill the ioc gSystem->Exec("kill -9 `ps x | grep soft | grep reeper | grep -v xterm | awk '{print $1;exit}'`"); fprintf(stdout, "Killed testIOC \n\n"); } void GrimReeper::writeTestPVs(){ static double PV23004,colliX,PMT1,Fcup,Xsetting,Ysetting,BigScaler; double x; char command[1028]; double amp; if(!klrand) klrand = new TRandom(); if(mode==1){ //called this way if scanning sscanf((gSystem->GetFromPipe("caget -t colliX")).Data(),"%lg",&colliX); } else{ colliX=klrand->Uniform(-4,4); //colliX=klrand->Gaus(0.6,1.0); sprintf(command,"caput -t colliX %g > /dev/null", colliX); gSystem->Exec(command); } if(mode==2){ //xy scan demo mode //read in the x and y sscanf((gSystem->GetFromPipe("caget -t Xsetting")).Data(),"%lg",&Xsetting); sscanf((gSystem->GetFromPipe("caget -t Ysetting")).Data(),"%lg",&Ysetting); } else{ Xsetting=4.0+klrand->Uniform(0,4); sprintf(command,"caput -t Xsetting %g > /dev/null", Xsetting); gSystem->Exec(command); Ysetting=3.0+klrand->Uniform(0,3); sprintf(command,"caput -t Ysetting %g > /dev/null", Ysetting); gSystem->Exec(command); } //full up the sum scalers according to some alorgithm amp=TMath::Gaus(Xsetting,4,1.0)*TMath::Gaus(Ysetting,3,1.0); sprintf(command,"caput -a -t AMO_SCALERS 100"); for(int n=0;n<100;n++){ AMO_SCALERS[n]=amp*klrand->Gaus(1,0.01)/(n+1.0); sprintf(command,"%s %lf",command,AMO_SCALERS[n]); } sprintf(command,"%s > /dev/null", command); gSystem->Exec(command); sprintf(command,"caput -t BigScaler %g > /dev/null",amp*klrand->Gaus(1,0.01)); gSystem->Exec(command); // Fcup = 1000.0*TMath::Gaus(colliX,0.6,2.0); Fcup = klrand->Gaus(100,20.0); sprintf(command,"caput -t Fcup %g > /dev/null", Fcup); gSystem->Exec(command); PV23004 = Fcup*klrand->Uniform(100.0,200.0); sprintf(command,"caput -t PV23004 %g > /dev/null", PV23004); gSystem->Exec(command); PMT1 = Fcup*20000.0*TMath::Gaus(colliX,0.6,1.0); //PMT1 *= klrand->Gaus(1,0.1); sprintf(command,"caput -t PMT1 %g > /dev/null", PMT1); gSystem->Exec(command); testTime++; } void GrimReeper::writePV(const char *pv_name, double pv_value){ char command[100]; sprintf(command,"caput -t %s %g > /dev/null", pv_name, pv_value); gSystem->Exec(command); } void GrimReeper::setOptFit(int opt){ gStyle->SetOptFit(opt); }