INS-DAS-9

Implementation
of the
Data Acqusition Server

Guy Rixon

Issue 1.3; 22nd May 1995




Royal Greenwich Observatory,
Madingley Road,
Cambridge CB3 0HJ

Telephone (01223) 374000
Fax (01223) 374700
Internet G.Rixon@ast.cam.ac.uk

1 Introduction

1.1 Purpose of this document

The coding, functions and data structures of the Data Acquisition Server, as implemented in 1995 for the INT data-acquisition system, are described below for the benefit of engineers intending to test or modify the server. The information presented here refers entirely to internal details. If you intend to write software that interacts with the server, please refer instead to document INS-DAS-8 which describes and defines the server's external interfaces and states the behaviours which may be relied upon.

This is not a specification document, but post hoc description. If the description does not match the software, then this document should be corrected.

1.2 Scope of the software

The Data Acquisition server is responsible for the recording of images and headers as FITS files on disc. Its inputs, which are described in detail in INS-DAS-8 are pixels from a FOX rack, files of FITS-header packets from disk and DRAMA messages. Control of detectors, co-ordination of operations and archiving of data are external to the data-acquisition server. The server is implemented as a single DRAMA task and this document describes that task.

1.3 Sub-system context

The position of the server in an observing system is described in INS-PF-4 for the specific case of the INT PFC. For convenience, I have abstracted a context diagram for the system in Figure 1.

The external interfaces are all described in INS-DAS-8 except for two data-flows to and from disk that are private to the DA server and should not be interfered with by other software.

`Channel settings' between the sub-system and the system disk

represents the server storing and retrieving the last settings specified on each of its eight channels, in order that the settings remain available when the server is restarted. The files of settings are visible to other programs, but are considered private to the server.

`Run number' between the sub-system and the system disk represents the server's memory of which run numbers have been allocated. In order that run numbers be continuous between observing sessions and across upgrades to the observing system, the run-number file needs to be copied between successive installations of the DA server.

It may be helpful to remember that the server is usable with many combinations of instruments, detectors and telescope. The emphasis in the design is on generality. Most of the intelligence required to usefully record observations is supplied by the programs that invoke actions on the DA server.

1.4 Using this document

The DA server has a very large degree of concurrency - eight parallel channels with interleaved commands on each and everything interruptible - and this makes it rather more complex than a program that `just writes pixels into a file'. To understand the program, it is necessary first to know what it is trying to do, then to understand the nature of the data processed and finally to examine the multi-threading constructs that allow the performance to be acceptable.

The interfaces and guaranteed behaviour are listed in INS-DAS-8. This is best and briefest guide to the function of the program. Further details can be found in the descriptions below of individual components, but these descriptions presume an understanding of the data and threading structures.

The principle data-structures are described in section 3 and the threading in section 4. It may be helpful to review the DITS documentation before reading section 4.

Section 2 describes certain standards used in the production of this sub-system. In particular, the naming convention for data objects is introduced.

The bulk of the text, contained in section 5, is descriptions of the components. The descriptions are copied directly from the prologue comments in the source files. These sections are the most authoritative description of the sub-system.

2 Sub-system standards, conventions and procedures

2.1 Design standards

The code for the server is designed as series of `components', as defined in INT-PF-3, each component being a distinct and visible item under version control. Apart from the version-control conventions in INT-PF-3, there are no formal standards for design.

2.2 Documentation standards

Technical documentation is produced in line with the ESA standards for software engineering. Such documents are produced in Interleaf or ASCII formats, and are listed in the general catalogue of ING documentation.

Observer's manuals for hard copy are produced in LaTeX format and may later be converted to HTML. To allow this conversion, the manuals must not contain any control sequences that are recognized by TeX but not LaTeX.

On-line help for use while observing is produced in HTML (or one of the later HTML derivatives such as HTML+). These hyperdocuments are not suitable for use as a printed manual.

2.3 Naming conventions

The data-acquisition sub-system conforms to the naming standards for the INT PFC project.

z Names of compiled programs are always in lower case throughout.

z Names of variables and functions are mainly in lower case with upper case letters inserted where words have been concatenated, e.g. dasCountPixel(). The first word in the concatenation does not have a capital letter.

z Names of data types have the suffix _t; names of global variables have the suffix _g; names of non-global variables declared outside any function in their component (`local' variables) have the suffix _l.

z Constants, particularly those declared as macros for the C preprocessor are named in upper case with underscores separating words.

z Names of source-files are in mixed case, normally following the name of the principal function within the component.

z Components specific to the sub-system have names with the prefix das.

z Names of actions and parameters, and the name of the server task itself, are entirely in upper case. This is necessary to make the external interfaces usable from ADAM programs.

2.4 Programming standards

All C code is compilable by ANSI compilers. The C programming standards of Starlink are loosely followed.

Where possible, library calls are limited to ANSI and POSIX functions. However, the DA server is inherently non-portable as its main function is call a Solaris-specific device-driver. The other main deviation from ANSI/POSIX compliance is the use of Solaris 2's threads facility, which is virtually essential to achieve the required performance. It is hoped that the server could easily be re-worked to use a POSIX threads library if one became available.

The sub-system is built by a makefile which can be run under control of the bom utility, as described in INT-PF-3. A suitable bill of materials is supplied. All components (except documents) are tagged for this system.

2.5 Development tools

The compiler and debugger from SPARCworks Professional C were used to develop the system. To cope with the multi-threading, the IMPacT thread-aware debugger was used. Some use was made of lint in the form distributed with Solaris 2.

2.6 Infrastructure for the installed sub-system

The DA sub-system can only run with Solaris 2; Solaris 1 and other Unices do not provide the necessary support for multi-threading. In future versions, it may be possible to use the p-threads interface to the Solaris kernel, which is portable.

For the server to execute sensibly, a FOX interface card must be installed in the S-bus of the host computer. The device driver for the FOX interface must be loaded into the kernel and the driver's interface must be visible to the server as special files /dev/fox0xx where xx is 00, 01, 02, 03, 04, 05, 06, 07 and 80. The leading zero in these filenames indicates the first FOX-interface card in the S-bus; additional cards would show up as /dev/fox1xx, but this version of the DA server does not support that naming.

To build and maintain versions of the sub-system, the bom utility is required. This utility is described in INT-PF-3. To run bom, a PERL interpreter must be installed as /usr/local/bin/perl.

The DA server is a DRAMA program. DRAMA must be installed on the host computer to build and maintain the DA sub-system.

3 Data structures

The DA server makes extensive use of static data-structures. Some of these structures are routinely backed up to disc and are reloaded from disc during initialization. This section describes the principle structures.

3.1 The `settings' (dasSetting_t)

The majority of persistently-stored data result from control-settings imposed on the DA server though the command interface. As a result, the structure in which these data live is called `the settings'. The principle data-object is an array of dasSetting_t with one element for each of eight channels.

Whenever the settings are changed they are written to file in their entirety. The RESET action (which is routinely run when the server loads) reads back the settings. Hence, barring faults, the settings are not at all volatile. Access to the settings is managed by the component dasSettings.c.

For each channel, an element of type dasSettings_t stores the data

.channel the number of the channel.

.obsdata name of directory in which observations are kept.

.format readout format (type SCCD_format_T).

.nPixels number of pixels in the readout;

.nStrips number of readout strips;

.strips[] array of readout strips;

.headcode pixel headcode;

.foxFd file-descriptor for the FOX driver;

.fitsFileName name of the current FITS file;

.packets list of header packets to collect (space separated);

.fitsHeaderLen length of FITS header in bytes;

.readout address of the readout buffer;

.image address of the (output) image buffer.

3.1.1 Readout strips (dasRoStrip_t)

The mapping between pixel number in the readout and address in the final image-buffer is stored as instructions for mapping strips of pixels. Each strip is a set of pixels contiguous in the x-direction of the original frame; that is a strip is one row of the CCD or the part of that row contained within one readout-window. The pixels in a strip are at contiguous addresses in the readout buffer but rotation and reflection may separate them in the final image.

Strips are described by structures of type dasRoStrip_t with the members

.n number of pixels in the strip;

.o0 address offset to reach the first pixel;

.o1 address offset to move on one pixel.

The .o0 offset moves a pointer from the first pixel of the image to the first pixel of the current strip. The .o1 offset moves the pointer to the next pixel in the strip; that is, one applies .o1 (.n - 1) times to visit all the pixels in a strip.

An array of dasRoStrip_t is included for each channel in the `settings' (q.v.). The greatest number of strips needed to describe a readout is the y-size of the frame times the greatest number of windows.

3.1.2 Format structures (SCCD_format_T)

Structures of type SCCD_format_T, as discussed in INS-DAS-8, define the form of an image from a CCD. The DA server receives such a structure in the argument list of the FORMAT action and this data is passed into the settings. This structured type is defined in sccd.h.

3.1.3 Pixel headcodes

Each pixel arriving at the FOX carries a headcode that determines which channel receives the pixel. The headcode is an unsigned byte (all valid headcodes have the top bit set).

3.1.4 File descriptor for the FOX

The FOX is accessed through a series of `minor devices', one per channel, as described in INS-DAS-8. At initialization, each minor device yields a file descriptor from open(), and is kept open until the DA server is shut down or is reset.

3.1.5 File name for the FITS file

Each channel has the use of one FITS file at a time and the name of that file is stored here. The name is an absolute path-name: it includes the fully-expanded value of settings.obsdata. The last level of the name conforms to the convention stated in INS-DAS-8: the file is named rn where n is the run number. While the file is being constructed, it is not given the file-name extension .fits that INS-DAS-8 requires; this hides the file from the archiving system until the file is complete. In fact, the final name of the file, including the .fits extension, is never stored in the settings.

3.1.6 The name of the data partition

By design, all observations go into `the same' disk partition. If disk partitions could be indefinitely large, this would be absolutely true and the partition name could be a compile-time constant. In practice, the partitions are finite and the server changes between partitions as disk fill up. The name of the directory in which observations are kept is stored as a static character string.

3.2 Progress data (dasRun_t)

Data on the progress of an acquisition are stored for all channels. These data are only accessible as scalars through the component dasProgress.c. Access to these data is protected by mutexes - hence the need to use a standard access-function. For each channel, there is an object of type dasRun_t with the members:

.run the run number;

.readout the percentage completion of the readout in the current run;

.header the percentage completion of the header for the current run;

.disposal the disposal option chosen for the current run.

.headerReady true when the HEADERn action ahs completed.

.imageReady true when the READOUTn action has completed.

.fileReleased true when the DISPOSEn action has completed.

.validFits true when the RUNn action has completed.

.foxStatus final status of the read() from the FOX.

.imageStatus final status of the image reconstruction.

.headerStatus final status of header collection.

This data type is public, since it maps directly onto the structured parameters promised in INS-DAS-8. However, only the members from .run to .disposal inclusive are guaranteed to be stable between versions.

The members .xxxReady are used as flags that the longer-lived actions have finished: these members are boolean integers. The member .validFits indicates when the FITS file is complete enough to satisfy the FITS standard. In practice, this means that the keywords SIMPLE, BITPIX, NAXIS, NAXIS1, NAXIS2, BZERO, BSCALE and END are present in the header and the image-area has been extended to the image size indicated. This completeness is achieved at the end of the RUNn action; the RUN keyword is also set in this action, but the image is not zeroed. The members .xxxStatus indicate the final stati returned by worker threads in the major actions.

For each channel n, the progress data are mirrored in the parameter RUNn: the (structured) parameter is set by a direct copy of the C structure.

3.3 The readout buffer and pixel values

Pixels arriving from the CCD are written by the FOX device-driver directly into a buffer declared on the heap. A fresh buffer is created for each readout and is freed after the observation has been stored. Each buffer is made large enough to hold all the pixels in the readout, assuming two bytes per pixel; the buffers are aligned in memory on 16-byte boundaries as required by the device driver. A readout fills its buffer contiguously starting at the lowest address.

Pixels in the readout buffer are taken to be 16-bit unsigned integers: unsigned short in C. This means that in address calculations on the buffer, using pointers to unsigned short, the address increment is two bytes.

3.4 The FITS files; FITS-header `packets'

The FITS files produced by the DA server are standard disk-FITS using integers with 16 bits per pixel. To represent a data range of 0..65565 in standard FITS, 32676 is subtracted from each readout pixel and BZERO is set to -32676. BSCALE is always set to 1.0.

The form and length of the FITS headers is not set by the DA server. Instead, the server copies in pre-formatted `packets' of descriptors supplied by other programs. Packets are disk files containing FITS descriptors. These packets are assumed

z to contain only valid and complete FITS descriptors;

z not to duplicate FITS keywords;

z not to include any of the following keywords: SIMPLE, BITPIX, NAXIS, NAXIS1, NAXIS2, BZERO, BSCALE, RUN, END;

z to present the descriptors in the order specified in the user requirements.

The DA server provides the mandatory descriptors at the start of the header and follows these immediately with the RUN keyword which denotes the run number; the server also manages the placement of the END keyword. Where header packets have been specified (it is valid to run with no packet collection), they are added to the header in the order given.

3.5 Magic numbers and parametric constants

Some numeric and textual constants are fundamental to the design and are written directly into the code.

z There are eight channels and the channel numbers are zero to eight inclusive. These channel numbers are also know as `minor-device numbers'. There is a ninth minor device, number 80h, which can by used to monitor all channels.

z The FOX device-driver is accessed through the special files /dev/fox0nn where nn is the hexidecimal number of the minor device.

z All buffers for file-name are 256 characters long.

z Buffers for action names are 20 characters long.

z FITS descriptors are assumed to be 80 characters long.

z The name by which the server logs into the message net is DAS.

z The constant 32767 is used in converting pixel values.

z All pixels take two bytes of storage.

Some constants are represented by symbols defined in dasInternal.h.

FITS_BLOCK_SIZE is the length of logical blocks in the FITS files.

DAS_OBDATA_LEN is the maximum length of the member settings.obsdata.

MANDATORY_FITS is the number of FITS descriptors generated by the DA server (i.e. not taken from packets), including RUN and END.

4 Processes, threads and actions

The DA server is contained within a single Unix process. Inside the process, pre-emptive multitasking is provided by the Solaris threads facility and cooperative multitasking by DITS actions. The inputs and outputs of the process are the boundary of the sub-system, as discussed in section 1.

4.1 Threads

In principle, the server could execute within one thread. However, since the acquisition of image and header require parallel I/O operations, it is convenient to use several threads to serve each detector channel. While an acquisition is active on a channel, one thread is reading pixels from the FOX, a second thread is arranging the pixel into an image and a third is reading FITS-header packets into the observation file. When multiple channels are active, there are multiple copies of these threads executing the same code; the code, therefore, must be re-entrant. The initial thread (the one in which the server starts execution) creates the worker threads and monitors their progress; it is this initial thread that executes the fixed part of the task and the task's DITS actions. The code for the worker threads, and the code that creates them, is in dasReadout.c and dasHeader.c .

Figure 2 summarises the data flows between the threads.

The worker threads are passed an augmented copy of the settings at creation; each copy is private to the thread in question. The worker threads update the progress structures by calls to a data-hiding component, dasProgress.c; these accesses are serialized by mutex semaphores. The DRAMA thread reads from the progress structures when the actions within it are rescheduled. The worker threads write to a FITS file that has been opened for them by the DRAMA thread. The FOX-reading thread and the image-reconstruction thread share the readout buffer. The image thread reads from this buffer only the pixel locations that the FOX thread has filled in the current readout; it identifies these pixels by enquiry calls to the FOX-hardware interface. When a worker thread has finished its processing it terminates. Before exiting, it calls dasProgress.c to log its final status; a deliberate side-effect of this call is that relevant actions are scheduled, including the parent of the worker thread. When the DRAMA thread executes it will, in due course, enter the parent action which can react to the death of the worker thread.

4.2 Actions

The DITS actions are used to give separate contexts of execution to the commands sent to the DA server. Only one copy of any action can be active at once, and actions cannot pre-empt one another. The code for actions need not be re-entrant. To avoid contention for the use of actions between channels, most actions are declared as sets of eight distinguished by a numeral from 0 to 7 at the end of the action's name. The DITS kernel treats these actions as entirely separate, even though they execute the same code. Since there is only one DRAMA thread, there are no problems with re-entrancy.

Several actions, notably those that impose settings on the server, complete in one phase. The principle actions READOUTn and HEADERn create the worker threads referred to above and return to the fixed part while those threads are active. These actions are rescheduled periodically in order to report the progress of the worker threads. The set of actions are defined in dasMain.c and the code to implement them is arranged in other files; each action has one component.

The actions for the DA server are the following:

OBSDATA sets the disk partition in which the observations are stored.

HEADCODEn maps pixel headcodes to channel numbers.

FORMATn sets the readout format.

PACKETSn set the list of header packets to be collected.

RUNn opens a FITS file for an observation and allocates a run number.

READOUTn receives and stores a readout. The FOX and image threads are children of these actions.

HEADERn constructs the FITS header. The header threads are children of these actions.

DISPOSEn disposes of a complete observation.

PROMOTE promotes to a full run an observation previously demoted to a scratch file.

RESET resets the server and its hardware.

WATCHFOX watches all channels for unusual events.

Where the action names end in n, there are eight of each action with n = 0..7, one action for each channel. The other actions apply to all channels. All the actions except MONITOR are part of the command set for use by other programs. The MONITOR action is started by the server itself when the server is initialized and runs periodically whenever the server is loaded.

Figure 3 shows data-flow between actions. These flows are all passed through the settings or the progress data.

All flows implicitly pass through stores although these are not shown in the figure; the receiving action must call dasSettingsGet() or dasProgressGet() to read the data.

The flows involving the progress structure also involve the worker threads, and the data structures have to be protected against concurrent access. The access functions in dasProgress.c (which are the only way to get these data structures) provide mutual-exclusion semaphores for this purpose. The normal update sequence is (a) lock the mutex; (b) get a copy of the data structure; (c) modify the structure; (d) write the structure back; (e) unlock the mutex. Steps a, b, d and e are separate calls into dasProgress.c. Similar protection is provided for the settings by dasSettings.c.

5 Reconstructing images

During a CCD readout, the DA server receives a 1-dimensional array of pixels from which it must generate a 2-dimensional image. The process of reconstruction takes account of the following factors.

z Binning: binning factors reduce the effective size of the CCD, of the output image, and of the readout windows.

z Windowing: when windows are enabled, only the pixels of the CCD that fall inside a window are read out. Also, the readout is in the form of a raster scan over the entire CCD, and rows of windows may be interleaved where those windows intersect the same CCD rows. Inserting `line-breaks' for an un-windowed frame is a special case of windowing.

z Window packing: if an image is reconstructed with the windows in their original place on the CCD, there will usually be areas of un-exposed pixels between windows. The DA server packs the windows so as to minimize these gaps.

z Reflection and rotation: in order to present the final image in and orientation that suit the observer, an arbitrary sequence of 90-degree rotations and reflections in the axes may be required.

The stages of reconstruction are applied in order. The arrangement of pixel-values on the CCD is denoted as the `original frame'. Binning, windowing, line-breaking and packing give an `intermediate frame'. Rotation and reflection give the `final' or `output' frame.

In principle, it is possible to compute these transforms for each pixel as it arrives in the readout buffer. In practice, this is found to take slightly too long

on the current generation of computers. Instead, the DA server stores a mapping between pixel number in the readout buffer and address offset in the output frame. Address offsets are four-byte integers, so it is impractical to store an offset for each output pixel: the address map would be twice the size of the image! The raster-scan nature of the CCD readout means that pixels always arrive in contiguous strips, each strip being one row of a window or one row of the original frame.



The DA server stores address-mapping data for each strip:

z the address offset o0 leading from the first pixel of the frame to the first pixel of the strip;

z the address offset o1 between pixels in the strip;

z the number of pixels in the strip.

Applying rotation and reflection to address offsets is conceptually difficult. As an intermediate stage, (x,y) vectors v0 and v1 are derived in the coordinates of the intermediate frame that express the same offsets as o0 and o1. These vectors are transformed (a standard matrix-operation) into the frame of the output image before being converted to address offsets.

Reconstructing the output image from the strips is simple. If a pixel is found to be pixel p of strip s, its address O in the output image of base address B is

O = B + o0(s) + [ p  o1(s)]

where p is counted from 0 for the first pixel in the strip. In fact, since we know that each strip is contiguous in the readout buffer, we can calculate O for the first pixel in the strip and then add o1 before each of the other pixels of that strip.

Predicting the readout strips is considerably more involved. The derivation follows the order of operations given at the head of this section.

First, the CCD size and windows sizes are divided by the binning factors. Any remainders in this division are discarded: the sizes are rounded down to the nearest binned pixel.

Secondly, the windows are ranked in order of increasing x-origin, since this is the order in which they generate strips if they intersect the same row. Windows with zero size in one or both axes are ignored.

The windows are packed to give the smallest possible intermediate frame. This is done by checking each column outward from the origin: if the column does not intersect any window, then it is eliminated by moving all windows with origins at higher x one unit toward the origin and reducing the frame's x-size by one pixel. This process is repeated in the y direction.

A transformation matrix is constructed to account for the reflections and rotations. The initial matrix is the identity matrix. Each 90-degree rotation and each reflection in an axis is represented by multiplying in the standard matrix for that transformation. After that, further correction is needed, since all pixels in the output frame must have positive coordinates. That is, a shift is needed for each reflection or rotation:

z after a rotation of +90 degrees (anti-clockwise), shift to positive x by one frame-width;

z after a rotation of -90 degrees (clockwise), shift to positive y by one frame height;

z after a reflection in x (y coordinates go negative), shift to positive y by one frame-height;

z after a reflection in y (x coordinates go negative), shift to positive x by one frame-width;

(In this prescription, the frame height and width are, of course, those of the transformed frame.) This sequence gives the following matrices for each transformation:



The rotation/reflection matrices can be multiplied together to give a single transformation to be applied to all vectors. The apparently equivalent operation on the translation matrices, summing them to give a single translation, is not geometrically valid; that is, one cannot permute the order of rotations, reflections and translations. To recover the single translation needed after the reflections and rotations, we consider the test points

(1,0) and (0,1): where one or both of these points has a negative co-ordinate we have a translation by the frame size, and otherwise no translation. The translations always increase co-ordinates of the vectors to which they are applied.

The rows of the original frame are considered in order of increasing y. If windows are disabled, the row yields one strip, with

v0 = ( 0, y )

where X is the x-size

of the frame. If windows are enabled, the windows are checked in their ranked order to see if they intersect the row. Each such window w yields a strip with

v0 = ( x(w), y  X )

where x(w) is the x-origin of the window. In both cases, v1 = (+1, 0) for all strips generated, since the readout scans towards positive x inside each strip.

The vectors are transformed into the frame of the output image:

v0' = T v0

v1' = T v1

and then the address offsets are derived:

o0 = (1,X)  v0'

o1 = (1,X)  v1'

where X is the x-size of the output frame (i.e. it is the x-size of the intermediate frame if there is an even number of reflections and the y-size of that frame if there is an odd number). Note that by deriving the transformation-matrix T before generating the strips, we need store the four vectors only for the strip currently being worked on.

6 The components

6.1 Component list

dasServerMain.c Contains main(). Sets up action map and parameter system; initializes the FOX.

das.h Defines structured types and constants for the benefit of programs outside the sub-system.

dasErr.msg Defines condition codes. Compiles to dasErr.h and dasErr_msgt.h, both of which may be needed by external programs.

dasInternal.h Defines, types and constants that are private to the sub-system. Global variables are declared external here but not defined.

dasObsdata.c Implements the OBSDATA action. Sets the data partition in which the observations are stored.

dasRun.c Implements the RUNn actions. Handles the allocation of run numbers. Opens the FITS files for observations.

dasFormat.c Implements the actions FORMATn.

dasPackets.c Implements the actions PACKETSn.

dasHeadcode.c Implements the actions HEADCODEn.

dasReadout.c Implements the actions READOUTn. Initiates the threads that read from the FOX and write pixels to the FITS file. The code for these threads is included.

dasHeader.c Implements the actions HEADERn. Initiates the threads that read in packets from disk and write FITS descriptors into the FITS file. The code for these threads is included. Handles the construction of the descriptors that only the DAS can provide.

dasDispose.c Implements the actions DIISPOSEn. Disposes of the FITS files at the end of each run.

dasPromote.c Implements the action PROMOTE.

dasReset.c Implements the RESET action. Completely resets and initializes the FOX.

dasWatchFox.c Implements the WATCHFOX action. Polls the FOX for exceptional events.

dasSettings.c Manages the storage of DAS settings.

dasProgress.c Manages the storage and sharing of the progress on each channel.

cio.c wrap-ups for system calls: these add error reporting to I/O functions.

das.mk Makefile. Builds the executable program `das'.

das.bom Bill of materials. Defines the version of the executable program `das'.

6.2 Component relationships

The C-code components form four layers of a hierarchy.

(i) dasServerMain() contains main() and starts the server. This component runs the event loop from which all other processing is invoked.

(ii) The action-implementation components (dasObsdata.c etc.) each contain one public function that is invoked by main() at initialization. This function enables the action (where the action is channel specific, each call enables it for one channel) and does any other initialization required by the component. Other functions in the component are obey and kick handlers and are called by main() in response to action requests. Two of these components, dasReadout.c and dasHeader.c, also contain functions to be executed by worker threads.

(iii) dasSettings.c and dasProgress.c each contain an initialization function called by main() and other public functions called by the action-implementation routines.

(iv) cio.c contains context-independent functions for manipulation of files. These functions are called by all the other layers.

6.3 dasServerMain.c v1.2

TYPE:
C source code: main() function.

PURPOSE:
To initate the DRAMA interface to the DA server as defined in INS-DAS-8.

FUNCTION:
This component defines the DA server as a DITS task. DITS and SDP are
initialized and the task specific facilities are progressively enabled.
When all facilities are enabled, the event loop is entered. The
task leaves the event loop after the EXIT action is invoked, and then
exits.

SUBORDINATES:
Called: dasWatchFoxInit(), dasResetInit(), dasProgressInit(),
dasSettingsInit(), dasOsdataInit(), dasHeadcodeInit(), dasFormatInit(),
dasPacketsInit(), dasReadoutInit(), dasHeaderInit(), dasDisposeInit(),
dasRunInit(), dasPromoteInit().

DEPENDENCIES:
Only one copy of this program can run on any given SPARCstation.

INTERFACES:
Call from C:
(int) = main();

See INS-DAS-8 for a list of interfaces provided by the running server.

RESOURCES:
The size of the main message-buffer is set to 20000 bytes. This figure is
arbitrary and may prove to be too high.

REFERENCES:
"Interfaces to the DA server"
RGO/ING document INS-DAS-8.

PROCESSING:
The server logs in to the message net as `DAS'. Because this name is
fixed (it is specified exactly in INS-DAS-8) there can only be one copy of
the server logged in to any one computer.

The parameter system is established in three steps. First, the root of the
system is created by SdpInit(). Second, the parameter system is connected
to DITS (and hence to the message net) by DitsPutParSys(); in this call,
the addresses of service functions SdpGet() and SdpSet() are passed to DITS.
Finally, monitoring of parameters is enabled by DitsPutParamMon(); this
call passes to DITS the addresses of service functions DitsMonitorMsg(),
DitsMonitorDisconect() and DitsMonitorTidy(). The parameter system has
no parameters at this stage: each subsequent initialization routine adds
the parameters it needs.

6.4 das.h v1.1

TYPE:
C source-code: include file.

PURPOSE:
To define the type dasRun_t, as specified in INS-DAS-8.

FUNCTION:
A definition of dasRun_t is prsented tat includes all fields specified
in INS-DAS-8.

SUBORDINATES:
None.

REFERENCES:
"Interfaces to the DA server"
RGO/ING document INS-DAS-8.

DATA:
The members of dasRun_t from run down to and including disposal are
required by INS-DAS-8; code that includes das.h may assume that these
members will not be altered or withdrawn. The other members are present
for the benefit of the DA server itself and may change without warning.

6.5 dasInternal.h v1.1

TYPE:
C source-code: include file.

PURPOSE:
To define types and constants needed within the DA server.

FUNCTION:
Definitions are provided for
DAS_OBSDATA_LEN
FITS_BLOCK_SIZE
MANDATORY_FITS
dasRoStrip_t
dasSettings_t

SUBORDINATES:
Included: sccd,h.

REFERENCES:
"Implementation of the DA server"
ING/RGO document INS-DAS-9.

DATA:
The usage of dasRoStrip_t and dasSettings_t is described in INS-DAS-9.

6.6 dasErr.msg v2.1

TYPE:
Source file for MESSGEN.

PURPOSE:
To define condition codes concerning the DA server.

FUNCTION:
Condition codes are defined at severities WARNING and ERROR.

SUBORDINATES:
None.

6.7 dasObsdata.c v1.1

TYPE:
C source code: one public and one private function.

PURPOSE:
To set and record the directory in which observations are stored.

FUNCTION:
This function implements the action OBSDATA.

dasObsdataInit() enables the action by adding it to the task's action map.
When dasObsdataInit() has completed successfully, each invocation of the
action calls dasObsdata().

dasObsdata() updates name of the data-directory in the settings and completes
in one phase. The name of the directory is taken from the argument list.

SUBORDINATES:
Called: dasSettingsLock(), dasSettingsGet(), dasSettingsPut(),
dasSettingsWrite(), dasSettingsUnlock().

Included: dasInternal.h.

DEPENDENCIES:
dasSettingsInit() must have completed successfully before dasObsdataInit()
is called.

INTERFACES:
Call from C:
(void) = dasObsdataInit( StatusType *status );

6.8 dasHeadcode.c v1.1

TYPE:
C source-code: one public function and one private function.

PURPOSE:
To set the mapping between pixel headcode and channel-number for one
DAS channel.

FUNCTION:
This component implements the actions HEADCODEn.

dasHeadcodeInit() enables the action for one channel by adding to the task's
map of actions. Subsequently, each invocation of the action causes
dasHeadcode() to be called.

dasHeadcode() read the current settings for the given channel. The new
headcode is read from the argument list and added to the settings, which are
then written back to file. The action completes in one phase.

SUBORDINATES:
Included: dasInternal.h

Called: dasSettingsLock(), dasSettingsGet(), dasSettingsPut(),
dasSettingsWrite(), dasSettingsUnlock().

DEPENDENCIES:
dasSettingsInit() must have completed successfully before dasHeadcodeInit()
is called.

INTERFACES:
Call from C:
(void) = dasHeadcodeInit( StatusType *status );

Invoke the action as HEADCODEn (n = 0..7). Pass an argument list
containing Argument1, an integer, holding the headcode.

PROCESSING:
If there are problems with the argument list (the only likely failure), the
call to dasSettingsPut() will return without action when it sees bad status.
The call to dasSettingsUnlock() must not fail in a similar way and so is
passed guaranteed good status.

Since ArgGetc() returns a signed char and the headcode is an unsigned char,
ArgGeti is used to extract the headcode.

6.9 dasPackets.c v1.1

TYPE:
C source-code: one public function and one private function.

PURPOSE:
To set the list of FITS-header packets to be collected for one channel.

FUNCTION:
This component implements the actions PACKETSn.

dasPacketsInit() enables the action for one channel by adding to the task's
map of actions. Subsequently, each invocation of the action causes
dasPackets() to be called.

dasPackets() read the current settings for the given channel. The new list
of packets is read from the argument list and added to the settings, which are
then written back to file. The action completes in one phase.

SUBORDINATES:
Called: dasSettingsLock(), dasSettingsGet(), dasSettingsPut(),
dasSettingsWrite(), dasSettingsUnlock().

DEPENDENCIES:
dasSettingsInit() must have completed successfully before dasPacketsInit() is
called.

INTERFACES:
Call from C:
(void) = dasPacketsInit( int channel, StatusType *status );

Invoke the action as PACKETSn (n = 0..7). Pass an argument list containing
Argument1, a string holding a space-separated list of packet-file names
(see INS-DAS-9 for the required form of naming) and Argument2, an integer,
the total number of descriptors in the packets listed by Argument1.

The length of the FITS header, in FITS descriptors (each 80 bytes) is
the number of descriptors in the packets plus the constant MANDATORY_FITS
(defined in dasInternal.h, this covers keywords SIMPLE down to BSCALE) plus
one for the keyword RUN plus one for the KEYWORD END. This value is written
into the settings. Note that the length recorded is the used length of the
header: in the FITS file, this length will be rounded up to a whole number
of FITS blocks.

PROCESSING:
If there are problems with the argument list (the only likely failure), the
call to dasSettingsPut() will return without action when it sees bad status.
The call to dasSettingsUnlock() must not fail in a similar way and so is
passed guaranteed good status.

The packet list is read as a character string. The entry for packets in the
settings structure holds 255 characters which should be plenty for normal
lists. Any extra characters are discarded silently.

6.10 dasFormat.c v2.1

TYPE:
C source code: one public and nine private functions.

PURPOSE:
To implement the FORMATn action for one channel: to set up a new
readout-format for that channel.

FUNCTION:
This component implements the actions FORMATn (n = 0..7).

dasFormatInit enables the action for one channel by adding the action
to the task's action map. When dasFormatInit() has completed successfully,
each invocation of the action calls dasFormat(). dasFormatInit() invokes
the action itself: an initial format is set for the channel as soon as the
event loop starts.

dasFormat() establishes the format for one channel. A structure of type
SCCD_format_T is taken as input and processed into descriptions of readout
strips as explained in INS-DAS-9. If a suitable structure is present in the
action's argument list, that structure is used; otherwise, the format from
the `settings' database is reprocessed. The new format is written back to
the settings database in both raw and processed form.

SUBORDINATES:
Included: dasInternal.h, dasErr.h, sccd.h

Called: dasSettingsLock(), dasSettingGet(), dasSettingsPut(),
dasSettingsWrite(), dasSettingsUnlock().

DEPENDENCIES:
For any given channel, dasSettingsInit() must be called before
dasFormatInit().

INTERFACES:
Call from C:
(void) = dasFormatInit( int channel, StatusType *status );

Invoke the action as FORMATn (n = 0..7). Optionally, pass an argument
list including a structure of type SCCD_format_T as defined in sccd.h.

RESOURCES:
This component allocates memory on the heap for the array of readout strips.
The amount of memory is 12 bytes (3 *sizeof(int)) times the number of strips.
Thus, the greatest size of this memory area is 48 bytes times the y-size of
the CCD.

REFERENCES:
"Implementation of the DA Server"
ING/RGO document INS-DAS-9.

PROCESSING:
This component precomputes address maps as used by the READOUTn action: the
final result of FORMATn is an array of readout strips as defined in
INS-DAS-9 where the mathematics of the calculation are explained.
A by-product of the strip extraction is the calculation of the size of the
output frame and a tranformation of the readout windows to show their location
in this frame. The order of the calcaltion is critical: the steps are as
follows.
1. Rank the windows (if windows are in use) in order of increasing
x-origin. Calculate the size of the intermediate frame.
2. Adjust the sizes of the windows and intermediate frame to
account for the binning factors.
3. Remove waste space between the windows: adjust the window origins
and the size of the intermediate frame.
4. Compute matrices to contain the effect of rotations and reflections
between the intermediate and output frames.
5. Determine how many readout strips are expected and allocate space
for them on the heap.
6. Compute the parameters of the readout strips allowing for
rotation and reflection.
7. Transform the windows and image size into the coordinate system of
the output frame.

The number of readout strips is the sum of the y-sizes of the windows or
the y-size of the image if windows are disabled.

DATA:
The format structure and the number of readout strips live in a settings
structure. The strips themselves are stored in an array of varying, and
potentially large, size which is allocated on the heap. The address of this
array is kept in the settings structure itself.

On entry, the format structure holds the formmating applied at the CCD,
given in members CCD_SIZE, WIN, BIN_FACTOR and WINDOWS_ENABLED. These
members are not changed in this component. Member TRANSFORM states how the
DA server should transform the image. Member IMAGE_SIZE is set by this
component to the final size of the output image. Member WINP (processed
windows) is set by this component to indicate where the windows of member
WIN ended up in the output frame.

6.11 dasRun.c v1.1

TYPE:
C source-code: one public and three private functions

PURPOSE:
To prepare a FITS file to receive an observation.

FUNCTION:
This component implements the actions RUNn (n = 0..7).

dasRunInit() enables the action for one channel by adding to the task's
action map. It also initializes the run number in the progress record.
When dasRunInit() has completed successfully, each invocation of RUNn for
the given channel causes dasRun() to be called.

dasRun() executes RUNn in one phase. A run number is allocated to the
channel and a FITS file for that run is opened. The run number is written
to the progress record and the file name for the FITS file to the
settings. The FITS file is made valid FITS by (a) adding the mandatory
descriptors plus keywords RUN and END; (b) extending the file to its full
length. The file is marked as valid FITS in the progress record.

SUBORDINATES:
Called: dasSettingsLock(), dasSettingsGet(), dasSettingsPut,
dasSettingsWrite(), dasSettingsUnlock(), cioCreat(), cioOpen(), cioLseek(),
cioRead(), cioWrite(), dasProgressLock(), dasProgressGet(),
dasProgressPut(), dasProgressUnlock().

Included: dasInternal.h, das.h.

DEPENDENCIES:
For any given channel, dasSettingsInit() and dasProgressInit() must have
completed before dasRunInit() is called.

This action does not write any data into the FITS file and so the file is
not valid FITS until HEADERn and READOUTn have executed.

The environment variable OBSSYS must be defined to be the root directory
of the installed observing system (see INS-DAS-8) at the time that
dasReadRunNumber() is executed.

Before RUNn is invoked, the length of the FITS header should have been
defined by PACKETSn.

INTERFACES:
Call from C:
(void) = dasRunInit( int channel, StatusType *status );

Invoke the action as RUNn (n = 0..7). No arguments are needed.

This component reads and writes the file in which the last-used
run-number is recorded. See `Processing' and `Data' below.

REFERENCES:
"Interfaces to the data-acquisition server"
RGO/ING document INS-DAS-8.

"Implementation of the data-acquisition server"
RGO/ING document INS-DAS-9.

PROCESSING:
FITS files are named in the convention specified in INS-DAS-8: the
file is r<n>.fits, where <n> is the run number, and lives in a directory
specified by the .obsdata member of the settings structure. Until the
file is completed (by READOUTn, HEADERn and DISPOSEn), it is not given the
name-extension .fits: this hides it from the archiving system.

In principle, the next run number after the last one used should never
correspond to an existing file. However, mistakes happen and this component
always checks, using access(), that it is not about to over-write a
previous observation. If there is already a file with the chosen run number,
dasRun() moves on to the next number and tries again. It repeats this until
an unused number is found. Note that this check won't detect a file with
the same number in a different partition.

Most of the access to the files is through the cio functions (q.v). These
wrap up system calls and provide error reporting to DRAMA standards.

In order to make the file valid FITS as soon as possible, it must be expanded
to its full size. This is done here by writing to the last byte of the image
area. The size of the file is the size of the FITS header, rounded up to a
whole number of FITS blocks, plus two bytes for each pixel.

DATA:
The server records the last-allocated run-number in a disk file. The file
just contains a binary dump of the integer that is the run number. This file
is read when the server initializes and written each time a run number is
allocated. While the server is running, the last-used run-number is kept
in a local variable of this component.

The name of the run-number file is generated when dasReadRunNumber() executes
and is then cached in a local variable for the use of dasWriteRunNumber().

6.12 dasReadout.c v1.2

TYPE:
C source-code: 1 public and five private functions.

PURPOSE:
To capture and record a CCD readout.

FUNCTION:
This component implements the actions READOUTn (n = 0..7).

dasReadoutInit() enabled the action for one channel by adding the action to
the calling task's action map. This call alsoopens a channel to the FOX and
stores the file descriptor for the channel in the settings. When
dasReadoutInit() has completed successfully, each new invocation of the action
calls dasReadoutO().

dasReadoutO() executes the first phase of an Obety transaction. A buffer
for the readout is allocated on the heap. A thread is created, with
dasReadFox() as its starting function, to read pixels into the readout buffer.
The target FITS file, the name of which is taken from the settings, is
opened and its image area is mapped into memory. A thread is created, with
dasReconstructImage() as its starting function, to copy pixels from the
readout buffer into the FITS file. The Obey handler for the next entry to the
action is changed to dasReadout().

dasReadoutM() checks for completeion of failure of the image capture. The
stati of the worker threads (read from the progress record) are checked; if
one thread has finished with an error, the other threads is killed and the
action aborts. If the image thread has completed successfully, the action
ends with good status. If the fox thread has finished but the image thread
is still running, the action is rescheduled for another entry. When the
action completes or fails, the Obey handler is changed to dasReadoutO().

dasReadoutK() executes a `kick' of the action. Both worker threads are
killed and the action ends with an error status.

dasReadFox() reads pixels from the FOX into the readout buffer. When the
read completes, the final status is determined and written into the progress
data.

dasReconstructImage() maps pixels from the readout buffer to the FITS file
according to the algorithm described in INS-DAS-9. The pixels are converted
from unsigned short to signed short with a 32K offset.

SUBORDINATES:
Included: das.h, dasInternal.h, dasErr.h, rgofox.h, cio.h

Called: dasProgressLock(), dasProgressGet(), dasProgressPut(),
dasProgressUnlock(), dasSettingsLock(), dasSettingsGet(),
dasSettingsPut(), dasSettingsUnlock(), cioOpen().

DEPENDENCIES:
dasSettingsInit() must have completed successfully before dasReadoutInit
is called. For any given channel, RUNn must have completed before
READOUTn is invoked (if RUNn has not completed, READOUTn will crash when
accessing the FITS file).

INTERFACES:
Call from C:
(void) = dasReadoutInit( int channel, StatusType *status );

Invoke the action as READOUTn (n = 0..7); no arguments are required.

This component updates the progress and settings structures.

RESOURCES:
The readout buffers contains two bytes per pixel in the readout. In
full-frame mode, each buffer may require 8MB or more of heap memory.

The image area of the FITS file is mapped into memory as a single segment.
This area may be 8MB or larger in full-frame mode.

REFERENCES:
"Implementation of the DA server"
RGO/ING document INS-DAS-9.

PROCESSING:
This action serves channels 0 to 7 inclusive. The channel number is found
by reading the DITS action-code.

The reconstruction of the image uses the `strips' algorithm described in
INS-DAS-9. The code for the image-reconstruction thread is a counted loop
over a known number of strips. Before processing each strip, the thread
polls the FOX util all the pixels needed for the strip have arrived. The
polling period is 1 second until the first pixel of the readout arrives and
100 ms threafter.

When killing worker threads, the parent thread must ensure that neither
worker holds the mutexes associated with the progress and settings data.
The parent acquires both mutexes itself before calling thr_kill().

DATA:
The memory for the readout buffer is allocated on the heap. Only this
component and its children need to see this, so the pointer is local.

6.13 dasHeader.c v1.1

TYPE:
C source-code: one public and four private functions.

PURPOSE:
To assemble a FITS header for an observation.

FUNCTION:
This component implements the actions HEADERn (n = 0..7).

dasHeaderInit() enables the action for one channel by adding the action to
the tasks's action map. When dasHeaderInit() has completed successfully,
each subsequent invocation of the action calls dasHeaderO().

dasHeaderO() executes the first stage of HEADERn. It recovers the current
settings, creates a worker thread, and passes the threads the settings as
its orders. The next entry to the action in obey context calls
dasHeaderM().

dasHeaderM() handles the normal end of HEADERn, when the worker thread
exits normally. The final status of the thread is obtained and becomes the
final status of the action.

dasHeaderK() handles the kick context. The worker thread is killed and
the action ends with status DAS__HEADERABORTED.

dasMakeHeader() is the entry point for the worker thread. The required
packets of FITS-descriptors are copied into the FITS file and capped
with the END descriptor.

SUBORDINATES:
Included: das.h, dasErr.h, dasInternal.h

Called: cioWrite(), cioLseek(), dasProgressLock(), dasProgressGet(),
dasProgressPut(), dasProgressUnlock(), dasSettingsLock(), dasSettingsGet(),
dasSettingsUnlock().

DEPENDENCIES:
dasSettingsInit() and dasProgressInit() must have completed successfully
before dasHeaderInit is called.

INTERFACES:
Call from C:
(void) = dasHeaderInit( int channel, StatusType *status );

Invoke the action as HEADERn (n = 0..7). Optionally, pass an argument
of any kind; if any argument is present, only the mandatory
FITS-descriptors are collected and the header packets are not used.

PROCESSING:
This action serves channels 0 to 7 inclusive. The channel number is found
by reading the DITS action-code.

6.14 dasDispose.c

TYPE:
C source-code: one public and two private functions.

PURPOSE:
To dispose of a FITS file after that file is completed.

FUNCTION:
This component implements the DISPOSEn actions as defined in INS-DAS-8.

dasDisposeInit() enables the action DISPOSEn (n = 0..7) for one channel by
adding the action to the task's action-map. When dasDisposeInit() has
completed successfully, each invocation of the action calls dasDisposeO().

dasDisposeO() checks the progress structure for the channel. When the
file is finished, or when it will never be finished because of errors,
dasDisposeO() executes the disposal option given in the argument list.
The normal disposal is to rename the file and to make it visible to the
archiving system. The alternatives are conversion to a scratch file by
renaming, or deletion.

dasDisposeK() aborts DISPOSEn. It causes the action to complete with bad
status and has no effect on the FITS file.

SUBORDINATES:
Included: das.h, dasErr.h, dasInternal.h

Called: dasSettingsLock(), dasSettingsGet(), dasSettingsUnlock(),
dasProgressLock(), dasProgressGet(), dasProgressPut(), dasProgressUnlock().

DEPENDENCIES:
dasSettingsInit() and dasProgressInit() must have completed successfully
before dasDisposeInit() is called.

INTERFACES:
Call from C:
(void) = dasDisposeInit( int channel, StatusType *status );

Invoke the action as DISPOSEn (n = 0..7). Pass an argument list containing
a string, Argument1, in which the first character is A for archive, S for
scratch or D for delete (upper case is required).

REFERENCES:
"Interfaces to the DA server"
RGO/ING document INS_DAS-8.

6.15 dasPromote.c v1.1

TYPE:
C source-code: one public function and one private function.

PURPOSE:
To promote an observation from a scratch file to an archivable run.

FUNCTION:
This component implements the action PROMOTE.

dasPromoteInit() enables the action by adding it to the tasks map of actions.
When dasPromoteInit() has completed successfully, each invocation of the
action calls dasPromote().

dasPromote() operates on a scratch file whose number is given in the action's
argument-list. The scratch file is located, its run number is retrieved, and
the file is renamed in situ to the naming convention specified in INT-DAS-8.

SUBORDINATES:
Included: dasInternal.h

Called: dasSettingsLock(), dasSettingsGet(), dasSettingsUnlock().

DEPENDENCIES:
dasPromoteInit() should not be called until dasSettingsInit() has completed
successfully.

INTERFACES:
Call from C:
(void) = dasPromoteInit( StatusType *status );

REFERENCES:
"Interfaces to the data-acquisition server"
ING/RGO document INS-DAS-8.

PROCESSING:
The name of the scratch file is assumed to be snnn.fits where nnn is the
number of the file (not the run number). This file is looked for in
the directory specified in settings.obsdata.

The run number is found by searching the FITS header for the keyword
RUN and extracting the value field of this descriptor as an integer. If
The RUN keyword is not found, the file is not promoted.

6.16 dasReset.c 1.1

TYPE:
C source code: one public function and one private function.

PURPOSE:
To reset the FOX.

FUNCTION:
This component implements the action RESET.

dasResetInit() enables the action by adding it to the task's map of actions.
When dasResetInit() has completed successfully, each invocation of the
action calls dasReset(). DasResetInit() arranges for the action to be
invoked once as soon as the event loop is started.

dasReset() resets the FOX.

SUBORDINATES:
Included: rgofox.h

DEPENDENCIES:
dasResetInit() should not be called until dasSettingsInit() and
dasProgressInit() have completed successfully.

INTERFACES:
Call from C:
(void) = dasResetInit( StatusType *status );

Invoke the action as RESET. No arguments are required.

6.17 daswatchFox.c v1.1

TYPE:
C source-code: one public function and one private function.

PURPOSE:
To monitor the health of the FOX.

FUNCTION:
This component implements the WATCHFOX action.

dasWatchFoxInit() enables the action by addingit to the task's action map.
The action is invoked by dasWatchFoxInit() to start as sonn as the event
loop is started. When dasWatchFoxInit() has completed successfully, each
entry to the action calls dasWatchFox().

dasWatchFox() polls all eight channels of the FOX for errors and posts its
findings to the parameters READYn (n = 0..7). If a FIFO overflow is
detected, dasWatchFox() invokes the RESET action. The action is continually
actie and cannot be invoked by other tasks.

SUBORDINATES:
Included: rgofox.h

Called: cioOpen()

INTERFACES:
Call from C:
(void) = dasWatchFoxInit( StatusType *status );

DATA:
The file descriptor for the enquiry channel to the FOX is stored statically
and locally: foxFd. The name of the FOX-access device is hard-coded as
/dev/fox080 - i.e. access device 0, minor device 80h.

6.18 dasSettings.c v12.2

TYPE:
C source-code: five public functions.

PURPOSE:
To manage the record of settings in the DA server.

FUNCTION:
The DA server has several control settings that influence the way in which
the detector channels operate. These settings are held in memory for the
benefit of the code executing each action. The settings are frequently
written to disk and the server can read back the last settings stored for use
as defaults. One component of the settings, the readout format, is made
available to other tasks through the DRAMA parameters FORMATn.

Settings are held and accessed independently for each for eight channels.
Access the settings for can be made MT-safe by the use of a mutex.

This component provides an interface to the storage of settings. The
access operations are:
dasSettingsInit - initialize settings (read them back from file if
possible, otherwise generate defaults);
dasSettingsLock - lock a mutex to protect access to settings;
dasSettingsGet - copy settings to local buffer;
dasSettingsPut - update settings from local buffer;
dasSettingsUnlock - unlock a mutex after access to settings.

SUBORDINATES:
Included: dasInternal.h

Linked: cioOpen(), cioLseek(), cioRead(), cioWrite().

DEPENDENCIES:
For any given channel, the Init function must be called before any of the
others. Failure to do this may crash the calling program.

The environment variable OBSSYS should be set before initializing the
settings.

Passing an invalid channel number or a null pointer for a settings structure
will cause the program to abort. This restriction does not apply when
*status is non-zero on entry.

INTERFACES:
Call from C:
(void) = dasSettingsInit( int channel, SdsIdType pSys,
StatusType *status );
(void) = dasSettingsLock( int channel, StatusType *status );
(void) = dasSettingsGet( int channel,
dasSettings_t *buffer, StatusType *status );
(void) = dasSettingsPut( int channel,
dasSettings_t *buffer, StatusType *status );
(void) = dasSettingsUnlock( int channel, StatusType *status );

dasSettings_t is defined in dasInternal.h. Channel must be from 0 to 7
inclusive.

This component reads and writes the files $OBSSYS/etc/das_settingsn.dat
(n = 0..7), which are private to the DA server.

The DRAMA parmameters FORMATn, defined in INS-DAS-8, are maintained by this
component.

REFERENCES:
"Interfaces to the DA server",
ING/RGO document INS-DAS-8.

PROCESSING:
All the processing is protected by a mutex. This is a memory structure
(i.e. not a part of the file-system and not visible to other processes)
and is initialized by dasSettingsInit().

When dasSettingsInit() is called, it judges the validity of the settings
file by its length: the file should be the same size as a dasSettings_t.

If dasSettingsPut() fails to write to the settings file, it closes the file
and sets its FD to -1. While the FD remains set to -1 (i.e. until the
next successful call to dasSettingsInit()), dasSettingsPut() does not
write anything and does not report errors. This prevents the same error
being reported many times.

Most of the public routines return without action if status is bad on entry.
dasSettingsUnlock() is different: it always unlocks the settings if the
channel number is valid and if the settings have been initialized; otherwise,
it returns without action and with no error reports.

DATA:
Persistent data for the eight channels are stored in static arrays.
- The mutexes are mutex_l[] and are initially unlocked.
- The flags for successful initialization are initialized_l[].
- The descriptors for the settings files are fd_l[].
- The SDS ids for the format parameters are format_l[].
- The settings themselves are settings_l[].
All these data are initialized by dasSettingsInit().

Local buffers for the names of the settings files are limited to
255 characters. If the directory name indicated by OBSSYS is long enough to
cause an overflow, the program is terminated.

6.19 dasProgress.c v1.2

TYPE:
C source-code: five public functions and one private function.

PURPOSE:
To report progress in data acquisitions.

FUNCTION:
This component allows progress data to travel between threads, actions and
tasks: the data are passed through DRAMA parameters. In this context,
`progress data' means structures of type dasRun_t as defined in das.h and
described in INS-DAS-8.

dasProgressInit() initializes the data for one channel. It defines
the parameter, puts in initial values for the data and sets up a mutex to
protect the parameter against simultaneous access within the parent task.

dasProgressLock() and dasProgressUnlock() manipulate the mutex for a
channel's progress data.

dasProgressGet() returns the current set of progress data for one channel.

dasProgressPut() updates the progress data for one channel and notifies
the parameter system that the data have changed: the changes are propagated
automatically to monitoring tasks. Certain events - status changes in the
worker threads - cause signals to be sent to actions.
- READOUTn is signalled on a status change in the FOX or image thread.
- READOUTn is signalled when the image is complete.
- HEADERn is signalled on a status change in the header thread.
- HEADERn is signalled when the header is complete.
- DISPOSEn is signalled whenever READOUTn or HEADERn are signalled.

SUBORDINATES:
Included: das.h

DEPENDENCIES:
The parameter system SDP must have been initialized before any calls to
dasProgressInit(). For any given channel, the Init function must be
called before any of the other functions.

The functions in this component will crash if the channel number is out of
range or if a null pointer is passed for the progress structure. This
component is multi-thread-safe but should not be used in signal handlers.

INTERFACES:
Call from C:
(void) = dasProgressInit( int channel, SdsIdType pSys,
StatusType *status );
(void) = dasProgressGet( int channel, dasRun_t *progress,
StatusType *status );
(void) = dasProgressPut( int channel, dasRun_t *progress,
StatusType *status );
(void) = dasProgressLock( int channel, StatusType *status );
(void) = dasProgressUnlock( int channel, StatusType *status );

In all these calls, channel is a channel number from 0 to 7 inclusive.
pSys is the identifier of the parameter system.

The DRAMA parameters RUNn (n = 0..7) are manipulated: see INS-DAS-8.

REFERENCES:
"Interfaces to the DA server"
ING/RGO document INS-DAS-8.

PROCESSING:
In dasProgressInit(), sdsDasRun_t() is called to make an SDS structure.
This function is assumed to generate a structure compatible with dasRun_t.

6.20 cio.c v1.1

TYPE:
C source-code: multiple public functions.

PURPOSE:
To add error-handling facilities to basic I/O calls.

FUNCTION:
The system calls open(), creat(), read(), write() lseek() and close()
are wrapped in layers that provide status-handling and error reporting.
The wrappers do not change the function of their system calls when there is
no error.

In this version, the errors are reported to standard output. Each function
has a status argument; if status is not zero on entry, the function returns
without action. The one exception to this is cioClose(), which will still
close a file if the status on entry is bad but the file descriptor is valid.

SUBORDINATES:
None.

DEPENDENCIES:
Because it uses errno, this component is not necessarily reentrant.
If compiled on Solaris 2 with the _REENTRANT flag set, the component is
then reentrant.

INTERFACES:
Call from C:
(int fileDescriptor) = cioCreat( pathName, mode, status );
(int fileDescriptor) = cioOpen( pathName, flags, mode, status );
(pos_t position) = cioLseek( fileDescriptor, offset, whence, status );
(ssize_t nWritten) = cioWrite( fileDescriptor, buffer, nBytes, status );
(ssize_t nRead) = cioRead( fileDescriptor, buffer, nBytes, status );
(int check) = cioClose( fileDescriptor, status );

All the arguments and returned values above are as for the basic system calls
except status (type: StatusType*) which is the inherited status.

Appendix A. Document history

Issue 1.1 20/04/95 First formal issue. Compiled after the code was written and in time for the first inspections.

Issue 1.2 24/04/95 The expression for v0 (non-windowed case) was corrected. The range of channel numbers in section 4.2 was corrected to 0..7.

Issue 1.3 22/05/95 The descriptions of dasServerMain.c, dasReadout.c, dasFormat.c, dasProgress.c and dasSettings.c were updated. The discussion of co-ordinates was corrected.