nscl_logo_small.gif (2463 bytes)

Accessing Tcl Variables from within SpecTcl code

HH00706_.wmf (6530 bytes)

SpecTcl Home  General Information User Guide Programmer's Guide Obtaining and Installing

In many other histogramming systems, programs provide for a set of arbitrary user parameters. These may be floating point or integer parameters or what have you. Usually these are presented to the user as an array of integers, floats booleans or whatever. This makes keeping track of things like the usage of these array elements a burden on the experimenter. SpecTcl's approach is different.

Since SpecTcl contains an embedded Tcl/Tk interpreter, and since this interpreter allows you to create named variables and arrays, we take a different approach. Instead of creating artificial arrays of integers and reals which you have to keep track of, we reccomend instead creating Tcl variables and accessing them directly from within your code.

To do this you must:

Create tcl variables and give them initial values.
Bind these tcl variables to C/C++ variables in your unpacker.

The remaining parts of this page will assume that you are trying to produce a calibrated parameter where the calibration is a simple linear function of one of the raw parameters.

Sample code will be given to show how this can be implemented.

Creating tcl variable at startup time.

The Tcl set command is used to create a variable and assign it a value. The form of this command is:

set variablename value

SpecTcl has two initialization phases. The first, runs SpecTclInit.tcl at startup time, prior to hooking together the analysis system. The second, SpecTclRC.tcl is run after all initialization is complete. Typically, the binding between Tcl and program variables will be done in the Unpacker's OnAttach() member function. It is therefore a good idea to at least create these variables and give them initial default values in a user SpecTclInit.tcl (located in the user's home directory). This ensures that the Tcl variable exists at OnAttach() time. In our example the following lines would be added to the ~/SpecTclInit.tcl file:

set slope      1.0        ;# Initialize to 1-1 calibration.
set crossover  0.0        ;# so m = 1.0, b = 0.0
Top

Binding a tcl variable to a C/C++ variable.

The class CTCLVariable is an interface to TCL variables. The key member functions you will need are:

Name/Type Arguments Description
CTCLVariable (CTCLInterpreter* pInterp,
std::string rsVariable,
Bool_t fTracing )
Constructor. The parameters are as follows:
  • pInterp - pointer to the interpreter object on which the variable is defined, use the global variable: gpInterpreter.
  • rsVariable - the name of the tcl Variable, e.g. std::string("slope").
  • fTracing - The CTCLVariable operator() is called when the variable value is changed if this flag is kfTRUE. This is intended for code which sublcasses CTCLVariable and overrides operator(). For this application, typically, you can supply kfFALSE.
int Link (void* pVariable,
int Type)
Links the TCL Variable to a program variable. When Tcl does a set for that variable, your variable is modifed. When Tcl substitutes for the variable, the value of your variable is consulted. If successful, the return value is TCL_OK. The parameters are:
  • void* pVariable - Points to your variable.
  • int Type - Tells TCL the type of variable you are using. This can be one of:

    TCL_LINK_INT - Int_t variable.
    TCL_LINK_DOUBLE - DFloat_t variable.
    TCL_LINK_BOOLEAN - Bool_t variable.
    TCL_LINK_STRING - char[] variable, must be 'large' or use this with tracing to limit the string size prior to setting your variable.
    TCL_LINK_READONLY - can be or'ed with one of the others to disable the Tcl set command for that variable.

void Unlink () Remove the current variable linkage.

Top

Sample Code

The modifications requires will all take place within UserCode.cpp They involve:

Adding member variables m_dSlope and m_dCrossover to the unpacker class, along with appropriate CTCLVariable member functions as well.
Connecing m_dSlope and m_dCrossover to the the Tcl varaibles named "slope" and "crossover" respectively.in OnAttach()
Using the member variables in computation of a calibrated pseudo parameter in operator()

The complete, modified UserCode.cpp is available here.

Adding member variables:

Four member variables will be added. One for each of the C/C++ variables and one for each of the corresponding Tcl/Tk variables:

 

#include <TCLVariable.h>
#include <Globals.h>
#include <assert.h>
...

class CTestUnpacker : public CEventUnpacker
{
private:                      // Member variables.
   DFloat_t m_dSlope;         // Calibration slope.
   DFloat_t m_dCCrossover     // Calibration zero crossing.

   CTCLVariable* m_pSlope;      // Pointer to TCL Variable for slope.
   CTCLVariabel* m_pCrossover;  // Pointer to TCL Variable for crossover.
public:
   CTestUnpacker() :            // Constructor must be done this way because
       m_pSlope(0),             // The TCL interpreter is not yet up at the time
       m_pCrossover(0)          // of construction.
   {}
   ~CTestUnpacker() {            // Delete the CTCL Variable at destruction time.
       if(m_pSlope)     m_pSlope->Unlink();
       if(m_pCrossover) m_pCrossover->Unlink();
       delete m_pSlope;
       delete m_pCrossover;
    }
   UInt_t operator()(const Address_t pEvent,
                     CEvent& rEvent,
   CAnalyzer& rAnalyzer,
   CBufferDecoder& rDecoder);
   virtual void OnAttach(CAnalyzer& rAnalyzer);
   virtual void OnDetach(CAnalyzer& rAnalyzer);
};

Modifying OnAttach().

The text below shows the modified version of OnAttach.

 

void
CTestUnpacker::OnAttach(CAnalyzer& rAnalyzer)
{
   CEventUnpacker::OnAttach(rAnalyzer);

   // Create the TCL variable objects:

   m_pSlope = new CTCLVariable(gpInterpreter, std::string("slope"),
                               kfFALSE);
   m_pCrossover = new CTCLVariable(gpInterpreter, std::string("crossover"),
                                   kfFALSE);

   // Now link these variables to our variables Any failure results in a
   // crash of the program.

   assert(m_pSlope->Link((void*)&m_dSlope,
                         TCL_LINK_DOUBLE) == TCL_OK);
   assert(m_pCrossover->Link((void*)&m_dCrossover, 
                              TCL_LINK_DOUBLE) == TCL_OK);
}

 

Modifications to operator()

This member contains the unpacking code. We assume that the parameter with id 1 will be calibrated into an element of the rEvent array indexed by pIdx:

...
DFloat_t calib = (DFloat_t)rEvent[1]; // Compute calibrated var.
calib = calib*m_dSlope + m_dCrossover;
rEvent[pIdx] = (Int_t)(calib + 0.5); // Round it into integer. 
...

 

Top

SpecTcl Home  General Information User Guide Programmer's Guide Obtaining and Installing


Last Modified: October 28, 2003 by: fox@nscl.msu.edu
© Copyright NSCL 1999, All rights reserved