#PODNAME: Lab::Measurement::Developer #ABSTRACT: Lab::Measurement developer tutorial __END__ =pod =encoding UTF-8 =head1 NAME Lab::Measurement::Developer - Lab::Measurement developer tutorial =head1 VERSION version 3.931 =head1 Writing drivers The driver infrastructure is based on L. For Moose basics see e.g. =over =item * The L by chromatic, available online for free, contains a chapter on Moose. =item * For full details, see the very well written L. =back =head2 Code example We start with a section of code from the L 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; =head2 Explanations =over =item extends 'Lab::Moose::Instrument'; All drivers inherit from L. This base class provides the access to the underlying connection via methods like C, C and C. =item with qw/Lab::Moose::Instrument::Common/; The L role contains methods for GPIB common commands like I<*IDN>, I<*RST>, I<*CLS>, ... =item sub BUILD { my $self = shift; $self->clear(); $self->cls(); } This C method (see L) executes a device clear and clears the error status. =item 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 L). This line creates an attribute for storing the amplitude value of the LIA's reference output. The attribute can then be accessed with the C method. If the getter is called while the attributes is unset, the C 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 C<< 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 L for full details. =item sub get_amplitude { my ($self, %args) = validated_getter(\@_); We come to the getter function, which queries the amplitude from the LIA. The C function (from L) 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. =item 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 C<%args> argument to C! =item sub set_amplitude { my ( $self, $value, %args ) = validated_setter( \@_, value => { isa => 'Num' } ); The C function parses the arguments of the setter method. We require that the value argument is a number. =item $self->write( command => "SLVL $value", %args ); $self->cached_amplitude($value); We pass the new amplitude to the instrument and update the cache. =item __PACKAGE__->meta()->make_immutable(); Every Moose class shell end this way. See L. =back =head2 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 L, which covers all of the basics. A test file F 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 I instrument, to create the connection log file F: C<< $ perl -Ilib t/Moose/Instrument/SR830.t --connection=LinuxGPIB --connection-options='{pad: 1}' >> where the argument of C<--connection-options> is given as a YAML hash. You can always print a help screen of command line options supported by the test: C<< $ 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: C<< $ prove -lv t/Moose/Instrument/SR830.t >> If C is not enough for you, take a look at the other test routines in F which offer =over =item * Floating point number comparison (absolute error, relative error, ...) =item * File comparison, including basic filtering options. =back =head2 git pre-commit hook: run tests for each commit The file F provides a L. The hook is enabled by creating a link to F<.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: =over =item * Run perltidy with our configuration F on all changed files. =item * Run all tests in F with B. =item * Run Perl::Critic tests in F. =item * Run F, which ensures that L contains links to all modules in the distribution. =back If a tests fails, the commit is stopped. =head2 Roles for SCPI instruments TODO =head2 Writing connections TODO =head1 Data files and plotting TODO =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2025 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. =cut