NAME

Lab::Measurement::Developer - Lab::Measurement developer tutorial

VERSION

version 3.800

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

Now 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.

git pre-commit hook: run tests for each commit

The file xt/pre-commit.pl provides a git pre-commit hook.

The hook is enabled by creating a link to .git/hooks/pre-commit:

ln -s xt/pre-commit.pl .git/hooks/pre-commit

The hook performs the following actions before a commit is finished:

  • Run perltidy with our configuration perltityrc on all changed files.

  • Run all tests in t/ with prove.

  • Run Perl::Critic tests in xt/critic.

  • Run xt/pod-manual-coverage.t, which ensures that Lab::Measurement::Manual contains links to all modules in the distribution.

If a tests fails, the commit is stopped.

Roles for SCPI instruments

TODO

Writing connections

TODO

Data files and plotting

TODO

COPYRIGHT AND LICENSE

This software is copyright (c) 2021 by the Lab::Measurement team; in detail:

Copyright 2017-2018  Andreas K. Huettel, Simon Reinhardt

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.