NAME
Lab::Moose::Developer - Developer tutorial.
Writing drivers
The driver infrastructure is based on Moose. For Moose basics see e.g.
The Modern Perl book by chromatic, available online for free, contains a chapter on Moose.
For full details, see the very well written Moose::Manual.
Code example
We start with a section of code from the Lab::Moose::Instrument::SR830 LIA driver and discuss the most important steps. This explains the most important parts of a driver, i.e. initialization, getters, setters, caching, ...
package Lab::Moose::Instrument::SR830;
use 5.010;
use Moose;
use MooseX::Params::Validate;
use Lab::Moose::Instrument qw/validated_getter validated_setter/;
use Lab::Moose::Instrument::Cache;
use Carp;
use namespace::autoclean;
our $VERSION = '3.520';
# (1)
extends 'Lab::Moose::Instrument';
# (2)
with qw/Lab::Moose::Instrument::Common/;
# (3)
sub BUILD {
my $self = shift;
$self->clear();
$self->cls();
}
# (4)
cache amplitude => (getter => 'get_amplitude');
# (5)
sub get_amplitude {
my ($self, %args) = validated_getter(\@_);
# (6)
return $self->cached_amplitude(
$self->query( command => 'SLVL?', %args ) );
}
sub set_amplitude {
# (7)
my ( $self, $value, %args ) = validated_setter(
\@_,
value => { isa => 'Num' }
);
# (8)
$self->write( command => "SLVL $value", %args );
$self->cached_amplitude($value);
}
# (9)
__PACKAGE__->meta()->make_immutable();
1;
Explanations
-
extends 'Lab::Moose::Instrument';
All drivers inherit from Lab::Moose::Instrument. This base class provides the access to the underlying connection via methods like
write
,query
andclear
. -
with qw/Lab::Moose::Instrument::Common/;
The Lab::Moose::Instrument::Common role contains methods for GPIB common commands like *IDN, *RST, *CLS, ...
-
sub BUILD { my $self = shift; $self->clear(); $self->cls(); }
This
BUILD
method (see Moose::Manual::Construction) executes a device clear and clears the error status. -
cache amplitude => (getter => 'get_amplitude');
Accessing instruments settings by querying them from the device can be slow. It is mutch faster if the setters and getters store the setting in an object attribute (See Moose::Manual::Attributes). This line creates an attribute for storing the amplitude value of the LIA's reference output.
The attribute can then be accessed with the
cached_amplitude
method.If the getter is called while the attributes is unset, the
get_amplitude
method will be called under the hood to initialize the attribute.What if the initialization should call the get_amplitude method with additional arguments, say
timeout => 10
? This can be done by overwriting the builder method of the attribute in your driver:sub cached_amplitude_builder { my $self = shift; return $self->get_amplitude( timeout => 10 ); }
See Lab::Moose::Instrument::Cache for full details.
-
sub get_amplitude { my ($self, %args) = validated_getter(\@_);
We come to the getter function, which queries the amplitude from the LIA. The
validated_getter
function (from Lab::Moose::Instrument) allows the user to pass additional options, like a timeout, to the underlying connection:my $amplitude = $self->get_amplitude(timeout => 10); my $amplitude = $self->get_amplitude(); # Use default timeout.
-
return $self->cached_amplitude( $self->query( command => 'SLVL?', %args ) );
We read the amplitude from the instrument and store it's value into the cache. Don't forget the
%args
argument toquery
! -
sub set_amplitude { my ( $self, $value, %args ) = validated_setter( \@_, value => { isa => 'Num' } );
The
validated_setter
function parses the arguments of the setter method. We require that the value argument is a number. -
$self->write( command => "SLVL $value", %args ); $self->cached_amplitude($value);
We pass the new amplitude to the instrument and update the cache.
-
__PACKAGE__->meta()->make_immutable();
Every Moose class shell end this way. See Moose::Manual::BestPractices.
Building automated instrument tests
Lab::Moose has extensive support to build automated tests for instrument drivers and this is what enables long-term maintainability of the code base.
If you are new to automated testing with Perl, consider the Modern Perl book, which covers all of the basics.
A test file t/Moose/Instrument/SR830.t for the above driver could look like this:
#!perl
use warnings;
use strict;
use lib 't';
use Lab::Test import => [qw/set_get_test/];
use Moose::Instrument::MockTest qw/mock_instrument/;
use MooseX::Params::Validate;
use File::Spec::Functions 'catfile';
my $log_file = catfile(qw/t Moose Instrument SR830.yml/);
my $lia = mock_instrument(
type => 'SR830',
log_file => $log_file,
);
isa_ok( $lia, 'Lab::Moose::Instrument::SR830' );
$lia->rst( timeout => 10 );
# Test the amplitude getter and setter
set_get_test(
instr => $lia,
values => [qw/0.004 1 2 3 5/],
getter => 'get_amplitude',
setter => 'set_amplitude',
cache => 'cached_amplitude',
);
$lia->rst();
done_testing();
First thing, we need to run the test with the real instrument, to create the connection log file t/Moose/Instrument/SR830.yml:
$ perl -Ilib t/Moose/Instrument/SR830.t --connection=LinuxGPIB --connection_options='{pad: 1}'
where the argument of --connection_options
is given as a YAML hash.
You can always print a help screen of command line options supported by the test:
$ perl -Ilib t/Moose/Instrument/SR830.t --help
Know with the connection log in place, you can run the test without the real instrument connected:
$ prove -lv t/Moose/Instrument/SR830.t
If set_get_test
is not enough for you, take a look at the other test routines in t/Lab/Test.pm which offer
Floating point number comparison (absolute error, relative error, ...)
File comparison, including basic filtering options.
Roles for SCPI instruments
TODO
Writing connections
TODO
Data files and plotting
TODO