++ed by:

1 PAUSE user
1 non-PAUSE user.

Andreas K. Hüttel
and 4 contributors


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;

 # (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 );

 # (9)


  •  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 and clear.

  •  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;

    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 to query!

  •  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 );

    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:

 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
     instr  => $lia,
     values => [qw/0.004 1 2 3 5/],
     getter => 'get_amplitude',
     setter => 'set_amplitude',
     cache  => 'cached_amplitude',


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


Writing connections


Data files and plotting