Related Documentation:
In a previous example we created a simulation object called PulseGen{} for use in GENESIS simulations as a callable object. Here we implement the necessary bindings to make the object callable from Perl using SWIG. This will allow it to easily be used in the Perl based applications of GENESIS such as the G-Shell and SSP.
The PulseGen{} implementation currently resides in the Experiment component in the files:
pulsegen.c
experiment/pulsegen.h |
Experiment has two files where SWIG bindings are implemented:
glue/swig/perl/experiment.i
glue/swig/perl/Experiment.pm |
experiment.i is a SWIG interface file which generates C code that can be compiled and linked against Perl headers and libraries.
Experiment.pm is a Perl module that contains higher level function calls to the previously mentioned interface.
Here, we show how to add bindings to these files to make the PulseGen{} object callable.
First we need to include our experiment/pulsegen.h header in the interface file in two places. At the top of the file with the other includes we add:
#include "experiment/pulsegen.h"
|
For the module declaration we add it near the end like this:
%include "experiment/pulsegen.h"
|
Next we need to declare a couple of functions for retrieving object data, and for performing a step in a simulation. These functions are considered to be low level functions.
The following function returns a pointer to a simobj_PulseGen data structure (defined in experiment/pulsegen.h) so that the calling process can get access to the object’s data:
void * pulse_gen_get_driver_data(struct simobj_PulseGen *ppg)
{ return((void *)ppg); } |
This function will be called when performing a step in a simulation:
void * pulse_gen_get_driver_method(struct simobj_PulseGen *ppg)
{ return((void *)PulseGenSingleStep); } |
In this section we define higher level functions that make use of the functions we defined in the SWIG interface file. These functions will be defined within a Perl module and be callable by other Perl programs.
In our implementation files for our simulation object, pulsegen.c and experiment/pulsegen.h, we have a set of functions required for running a simulation:
struct simobj_PulseGen * PulseGenNew(char *pcName);
int PulseGenFinish(struct simobj_PulseGen *ppg); int PulseGenAddInput(struct simobj_PulseGen *ppg, void *pvInput); int PulseGenAddVariable(struct simobj_PulseGen *ppg, void *pvOutput); int PulseGenReset(struct simobj_PulseGen *ppg); int PulseGenSingleStep(struct simobj_PulseGen *ppg, double dTime); int PulseGenSetFields ( struct simobj_PulseGen *ppg, double dLevel1, double dWidth1, double dDelay1, double dLevel2, double dWidth2, double dDelay2, int iTriggerMode, double *pdPulseOut ); |
In our Perl module we want to create a 1-to-1 correspondence for each function. The file glue/swig/perl/Experiment.pm contains declarations for packages along with their accompanying functions.
We declare a new package and then, using an existing working example for PerfectClamp{}, we create identical calls to our PulseGen{}, with some changes made to accommodate the PulseGen{} parameters. We are going to define a function for the following actions: add, finish, get_driver, get_time_step, initiate, new, report, and step:
This is our package declaration, it precedes our set of functions:
package Experiment::PulseGen;
BEGIN { our @ISA = qw(Experiment::Glue); } |
The add function adds a variable address to the PulseGen{} structure so that it knows where to write its output. We perform a call to PulseGenAddVariable which we created in our simulation object:
sub add
{ my $self = shift; my $options = shift; my $backend = $self->backend(); my $name = $options->{service_request}->{component_name} . "__" . $options->{service_request}->{field}; $name =~ s/\//____/g; my $result = $backend->PulseGenAddVariable($options->{address}); return $result; } |
The finish function performs a call to PulseGenFinish from the simulation object:
sub finish
{ my $self = shift; # close files, free memory my $backend = $self->backend(); $backend->PulseGenFinish(); } |
The get_driver function simply makes calls to pulse_gen_get_driver_data and pulse_gen_get_driver_method to give the Perl bindings access to the simulation object.
sub get_driver
{ my $self = shift; my $result = { data => $self->{backend}->pulse_gen_get_driver_data(), method => $self->{backend}->pulse_gen_get_driver_method(), }; return $result; } |
The get_time_step function is left in for consistency, it performs no operations.
sub get_time_step
{ my $self = shift; return undef; } |
The initiate function is left in for consistency, it performs no operations.
sub initiate
{ my $self = shift; } |
The new function is the most important. It allocates and sets up a new PulseGen object. We create a new object via a call to PulseGenNew from our simulation object. We then set up the parameters PulseGen needs by checking for them in the options hash: width1, level1, delay1, width2, level2, delay2, baselevel, and triggermode. If all are present then a call is made to PulseGenSetFields, which sets all of the parameters for the simulation object:
sub new
{ my $package = shift; my $options = shift; my $self = { %$options, }; bless $self, $package; if (!defined $self->{name}) { $self->{name} = "a pulsegen"; } $self->{backend} = SwiggableExperiment::PulseGenNew($self->{name}); if (!defined $self->{backend}) { return undef; } # # Here we check for all of our needed variables, we die if even # one is missing since we need it to continue creating the object. # if (!defined $options->{width1}) { return "Experiment::PulseGen constructor: width1 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{level1}) { return "Experiment::PulseGen constructor: level1 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{delay1}) { return "Experiment::PulseGen constructor: delay1 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{level2}) { return "Experiment::PulseGen constructor: level2 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{width2}) { return "Experiment::PulseGen constructor: width2 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{delay2}) { return "Experiment::PulseGen constructor: delay2 is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{baselevel}) { return "Experiment::PulseGen constructor: baselevel is not defined, cannot construct pulsegen object"; } elsif(!defined $options->{triggermode}) { return "Experiment::PulseGen constructor: triggermode is not defined, cannot construct pulsegen object"; } else { my $backend = $self->backend(); $backend->PulseGenSetFields($options->{level1}, $options->{width1}, $options->{delay1}, $options->{level2}, $options->{width2}, $options->{delay2}, $options->{baselevel}, $options->{triggermode}); } return $self; } |
The report function is left in for consistency, it performs no operations.
sub report
{ my $self = shift; #t nothing I guess ? } |
The step function simply performs a given number of simulation steps. A call is made to PulseGenSingleStep from our simulation object:
sub step
{ my $self = shift; my $scheduler = shift; my $options = shift; my $backend = $self->backend(); my $result = $backend->PulseGenSingleStep($options->{steps}); return $result; } |
With this set of declarations, we are now able to utilize the PulseGen{} simulation object from a set of Perl functions via SWIG.