Next: Log Files
Up: ORAC-DR - Programmer's Guide
Previous: Primitives
In order to write a valid primitive certain steps must
be adhered to in order to ensure that subsequent primitives
have the correct information.
It assumes knowledge of the following Perl concepts:
using Perl objects, lexical variables, Perl data
structures.
Here is an example primitive showing the basic principles:
1 =head1 NAME
2
3 _PRIMITIVE_NAME_ - short description
4
5 =head1 DESCRIPTION
6
7 Long description
8
9 =head1 ARGUMENTS
10
11 =over 4
12
13 =item ARG1
14
15 Description of possible values of ARG1
16
17 etc...
18
19 =back
20
21 =head1 TASKS
22
23 List of external tasks required by the primitive
24
25 =head1 OUTPUT FILES
26
27 Output suffix for the display system.
28
29 etc, AUTHORS, COPYRIGHT.....
30
31 =cut
32
33 # Read arguments or use default values
34 my $arg1 = ( exists $_PRIMITIVE_NAME{ARG1} ?
35 $_PRIMITIVE_NAME{ARG1} : 5);
36
37 # Loop over all sub frames
38 foreach my $i (1..$Frm->nfiles) {
39
40 # Get the input and output filename
41 my ($in, $out) = $Frm->inout('_sfx', $i);
42
43 # Read some value from the frame FITS header
44 my $value = $Frm->hdr('KEYWORD');
45
46 # Combine the in, out and value into options for the task
47 # DEPENDS ON ALGORITHM ENGINE
48 my $options = "IN=$in OUT=$out SWITCH=$value";
49
50 # Run the algorithm engine
51 $Mon{'task'}->obeyw('TASK',$options);
52
53 # Retrieve an answer from a parameter
54 ($ORAC_STATUS, $result) = $Mon{'task'}->get('TASK','PARAMETER');
55
56 # Print the result
57 orac_print "Result from primitive $ORAC_PRIMITIVE = $result\n";
58
59 # Update the frame object so that the next primitive
60 # gets the correct input file name
61 $Frm->file($i, $out);
62
63 }
64
65 # Ask the display system to display the frame
66 $Display->display_data($Frm) if defined $Display
67
The following should be noted:
- Primitives are written in Perl and all variables are lexicals
(using my).
- As for recipes, the first few lines (1-31) are the
documentation for the primitive in pod format. In addition to the
fields used for recipe headers, three new fields are required for
primitives, ARGUMENTS to describe the configuration options,
TASKS to list external dependencies and OUTPUT FILES
to list the suffix of any output data files (for use with the display
system) and any log files that may be written.
- Supplied arguments can be read from the _PRIMITIVE_NAME_
hash. Lines 33-35 check for the existence of the ARG1
key in the hash and read it if it exists else a default value is
copied in. Note the use of exists rather than
defined for checking hash contents. If defined is
used the key would automatically be created in the hash and set to a value of
undef whereas exists simply looks for the key in the
hash without creating it. In general, this is the more correct
behaviour as it can distinguish between the key not being there at all
(i.e. never set) and the key being set explicitly to
undef5.
In some current primitives the following may
be found for reading arguments:
my $arg1 = ( $_PRIMITIVE_NAME_{ARG1} || $default);
This works in most normal cases but will fail if the value of the
argument is desired to be `0' since that evaluates to false and will
cause the default to be returned rather than a `0'. This construct
also causes the key to be created even if no argument was ever
supplied (known as auto-vivification).
- Line 38 starts a loop over all the sub-frames present in the
frame. The need for such a loop depends on the individual instrument.
UFTI, for example, only ever creates a single data frame per disk file
and so will not require the loop. SCUBA can take data for multiple
wavelengths, and MICHELLE can store multiple integrations per data
file, so this loop would ensure that each wavelength/integration is
processed in turn.
- Line 41 retrieves the name of the current input file and
supplies a valid output filename based on the supplied suffix,
``_sfx''. Note that this command accepts a number as an
optional argument. This can be used to connect the file name with
the sub-frame (a Frame object can store multiple current filenames).
- Line 44 simply retrieves a value from the header. The
hdr() method can also be used to set header values (but does
not change the header on disk).
- Lines 46-51 send a message to an algorithm engine using a
messaging object stored in the %Mon hash. The options
string depends on the task at the other end of the message bus and
will therefore need to be changed if the algorithm engine is changed.
An important point is that error checking code is automatically
inserted into the recipe when ->obeyw is found in a line
that is not preceeded by an equals sign or a comment. This means that
line 51 is translated to:
{
my $OBEYW_STATUS = $Mon{'task'}->obeyw('TASK', $options);
if ($OBEYW_STATUS != ORAC__OK) {
< ERROR MESSAGE CONSTRUCTED AND PRINTED WITH orac_err>
return $OBEYW_STATUS;
}
}
such that the recipe is aborted and an error message printed. Note
that this block runs in its own scope to prevent warnings from Perl
concerning the masking of previous OBEYW_STATUS variables.
If automatic checking is not required, simply check the return
status. If the parser finds an equals sign before the obeyw
the line will not be re-written:
my $status = $Mon{'kappa_mon'}->obeyw("stats","ndf=$in");
- Line 54 retrieves a parameter value from the external task.
This example also makes use of the automatic status checking within
ORAC-DR. Whenever the parser sees the special ORAC_STATUS
variable in a primitive, code is automatically added after this line to
check the value of ORAC_STATUS and compare it with
ORAC__OK. If the status is not good, the recipe aborts and an
error message is printed. This saves the primitive writer from having
to worry about status checking.
- Line 57 makes use of the orac_print command to send a
message to the user. The orac_print command is written to
send the message to multiple output filehandles as defined by the user
with the -log switch to oracdr.
- The final task in the loop (line 61) is to update the file name
stored in the frame object. On exit from each primitive the frame
and group objects must contain the filenames that should be used by
subsequent primitives. This step is vital, and without it subsequent
primitives will use the wrong input file names.
- The final step is to display the reduced frames. The
display_data method will ask for the current frame to be
displayed. Note that the display sub-system will only display
the data frame if the user has requested this by configuring the
display system (using, for example, the oracdisp command)
accordingly. The check to make sure the display object is initialised
is required in case the user has turned off the display system.
Next: Log Files
Up: ORAC-DR - Programmer's Guide
Previous: Primitives
ORAC-DR -- Programmer's Guide
Starlink User Note 233
Tim Jenness, Frossie Economou, Brad Cavanagh
Joint Astronomy Centre, Hilo, Hawaii
June 2004
E-mail:ussc@star.rl.ac.uk
Copyright © 2004 Particle Physics and Astronomy Research Council