Using the SWIG generated Tcl wrappers and the tcl control module wrapper you can also write your slow controls drivers in pure Tcl. This section describes:
How to write a slow controls driver in Tcl
How to use the Module create
command
to hook an instance of your driver into the slow controls
framework.
Before continuing with a detailed discussion of pure Tcl drivers, it is important to understand Tcl command ensembles. A command ensemble is a command that has subcommands. Traditionally, the subcommand is the second keyword on the command line. The Tcl string command is an example ofa command ensemble that is part of the core lanaguage.
All Tcl slow controls drivers must be Tcl command ensembles that implement some pre-defined subcommands. When writing Tcl drivers, it is useful to have an ensemble generator. An ensemble generator is a mechanism that generates command ensembles that follow the form of some template. For example, all Tk user interface creation commands (e.g. canvas) are ensemble generators. The widget they create becomes a Tcl command that at least has the config and cget subcommands.
Tcl object oriented extensions such as IncrTcl, snit and as we transition to Tcl6.0, the Tcl6.0 tcloo object system. Can serve as generators.
For example, in instances of an incrTcl class, public methods of that class are ensemble subcommands for the name of the object. Similarly, for objects generated by snit::type class commands, the methods of that type are ensemble methods of the generated command.
The remainder of this section describes a sample Tcl driver written in Tcl that is similar in functionality to the sample driver written in C++ described in the previous section. We'll use this sample driver as a mechanism to describe and discuss the interfaces between the sample driver and the framework.
Note that sample drivers perform CCUSB operations via the SWIG wrappers for the CCCUSB class. They can generate and perform lists as well by using the SWIG wrapping of the CCUSBReadoutList class. This section assumes you are familiar with those wrappers.
We are going to approach the description of the driver by first showing a skeletal snit::type and then filling in the body of each method. Finally we'll show some script fragments that show how to create instances of the type and how to integrate those instances into the slow controls framework.
The example below is a snit driver with all of the executable code stripped out. This shows the general structure of the driverand its methods.
Example 5-22. CCUSB Tcl slow controls driver skeleton
package require cccusb snit::type SampleDriver { option -anint -default 0 -configuremethod _CheckInt option -anintlist -default 0 -configuremethod _CheckIntList option -abool -default true -configuremethod _CheckBool construtor args {} method Initialize crate {} method Update crate {} method Set {crate what value} {} method Get {crate what} {} method _CheckInt {name value} {} method _CheckIntList {name value} {} method _CheckBool {name value} {} }
It is no accident that there is a close parallel between the names of the methods for the Tcl driver and those of a C++ class that implements a slow control driver.
CCCUSB
driver class. In order
to successfully package require this,
the lib subdirectory of the NSCLDAQ
installation must be part of the package search path.
Some drivers may want to block up several CCUSB operations
into a list. To do this will also require a
package require cccusbreadoutlist
command to incorporate the SWIG wrapper for the
CCCUSBReadoutList
snit::type creates an ensemble generating command (in this case SampleDriver) with subcommands defined by the method definitions within the body of the type. The generated command ensembles also automatically have configuration management commands; configure which configures an object's option database, and cget which obtains information about that database.
options
array (indexed by option name).
This section of code defines three options much like the
C++ sample driver does. The -default
option
provides a default value for the option, effectively initializing
that element of the options
array.
The -configuremethod
option provides a
method that is automatically called when an option is
configured via the configure sub-command.
We will use these methods to validate a proposed new
value for the option.
Note that snit::type does not have any concept of a private method. By NSCLDAQ convention, however, methods with names that begin with _ should be considered internal and not called by external clients.
crate
parameter is a
SWIG wrapped CCCUSB
controller that
can be used by the driver to perform CAMAC operations.
crate
is a SWIG wrapped
CCCUSB
object and allows the
ensemble to perform CAMAC operations. The driver is suppoed
define a set of named parameters the client can modify.
what
identifies which parameter the
client wants to modify and value
provides
the new proposed value for the parameter.
On success the method should return OK. The simplest way to report an error is with the Tcl error command. The error text becomes the string after the ERROR text of an error return.
crate
is a SWIG encapsulated
CCCUSB
object that can be used to
perform CAMAC operations.
Drivers are expected to define a set of named parameters
that can be read. These need not be the same as the set
that can be written. what
should be
the name of one of these defined parameters. On success, the
driver should return the value of that parameter. On failure
the driver should either execute an error comman
or return a string of the form
ERROR -Error message
If the driver does use error to indicate an error condition, the string passed to that command will be concatenated to ERROR - and returned to the client.
Let's now look at the implementations of these methods:
construction.
Construction should be used to initialize the driver's internal
state. Furthermore, the configurelist
predefined method can be used to process the configuration arguments
passed to the constructor.
Example 5-23. CCUSB Tcl slow controls driver construction
construtor args { $self configurelist $args }
Initialization.
Since we have no hardware we have no one-type initialization
of that hardware. Note that if we did have to initialize
hardware the crate
parameter can be
used to perform the CAMAC operations needed to do that
initialization.
Update. We don't have anything to do on Update. In this case it's best to make Update signal and error because clients should be written not to use it for this device.
Example 5-25. CCUSB Tcl Slow Controls Update
method Update crate { error "This device does not support 'Update'" }
In this case we use the error command to signal the error. The Tcl module will catch this error and turn it into the string ERROR - This device does not support 'Update'. We could have also directly returned that string.
If the Update method was legal for this driver, and it executed successfully, we should return the string that we want the client to get. This is normally OK indicating success.
Set.
In our case, Set will treat
what
like a configuration
parameter name and value
like
the value to use to set the parameter.
We are just going to do a $self configure
and let this produce an error if there are problems
(illegal configuration options, illegal values).
If we succeed, we'll return OK to indicate
that.
Normally we'd need to use the crate
parameter
to perform actual CAMAC operations.
Example 5-26. CCUSB Tcl Slow controls driver Set
method Set {crate what value} { $self configure $what $value return "OK" }
Get.
The Get will treat the what
parameter as the name of a configuration option. If that option
exists it will be returned, otherwise an error message will be
generated.
Example 5-27. CCUSB Tcl Slow Control driver Get
method Get {crate what} { if {[array names options $what] ne ""} { return $options($what) } else { error "No such parameter '$what'" } }
Note that in snit::types, the options
array contains the configuration options. The Tcl command
array names returns the names of array indices
that match a glob pattern. We can get fooled into not emitting the
correct error message if, for example, we are asked to get
*anint. In that case there will be a match with
-anint, and the error produced will be:
ERROR - can't read "options(*anint)": no such element in array
Option value checking.
In our C++ driver we used configuration constraints to enforce
correctness in the form and values of the configuration options.
Tcl rivers are not able to take advantage of the capabilities of
a CConfigurableObject
. Therefore,
the onus of ensuring that options are correct is placed on
the driver.
snit::type provides the ability to intercept
the configure (and for that matter the cget)
operations on an option by option basis. This is done using the
-configuremethod
when defining an option.
Configure methods are responsible for updating (or
not) the appropriate element of the
options
array and can therefore
reject proposed changes.
Configure methods receive as parameters the name of
the option being configured and the new proposed value.
This allows options to share configuremethods.
We are going to further share code by having a helper function
that will throw an error if a string is not an integer. This
helper will be used by both _CheckInt
and
_CheckIntList
. For now we will assume
that _CheckIntList
needs to see a 16
element list. If this method needs to validate several lists
that are of different lengths, we would define a variable
for the object that would be an array indexed by option name whose
values would be the lengths of the list.
Example 5-28. CCUSB Slow controls Tcl driver option validation
method _CheckInt {name value} { $self _IsInt $value set options($name) $value } method _CheckIntList {name value} { if {[string is list $value]} { if {[llength $value] == 16} { foreach element $value { $self _IsInt $element } set options($name) $value } else { error "$name option expects a list exactly 16 elements long" } } else { error "$name must be a valid Tcl list" } } method _CheckBool {name value} { if {[string is boolean -strict $value]} { set options($name) $value } else { error "$name must be a boolean parameter, was: $value" } } method _IsInt {value} { if {![string is integer -strict $value]} { error "Configuration value $value must be an integer and is not" } }
Once a driver class/type has been created it can be used in the slow controls configuration script by
Creating an instance of the class/type.
Configuring any options for the instance
Wrapping the object in an tcl driver type by specifying it's
name as the -ensemble
option of the
object.