server_hdw

server-hardware facility

Description

Although spec usually includes built-in support for specific hardware devices, sometimes it is better if the hardware support is in a separate program. For example, some devices can only be accessed through a vendor library, and it is not always practical or possible to link the spec binary with such a library. The server-hardware facility described in this document allows spec to communicate using a socket protocol with a standalone program.

The hardware type in spec called "server hardware" implements the client side of the protocol. Files provided in the new servers subdirectory of the spec distribution include the server code.

As of spec release 6.06, the server-hardware feature supports 1D (MCA-type) and 2D (image-type) devices.

spec release 6.06 also includes support for having the server started automatically either on the local host or a remote host.

spec release 6.07 supports a standard directory for the server executable file, namely SPECD/servers, where SPECD is the spec auxiliary file directory, normally /usr/local/lib/spec.d.

As of spec release 6.08.07, server prototypes are included for XIA DPP MCA-type devices, Multix ME100 and greateyes image-type devices, and a no-hardware server prototype for testing. See the multix and greateyes help files. The XIA support works with the Ketek DPP2 and should work with devices from any vendor that use the XIA DPP.

spec release 6.09.01 includes Python prototypes for the Dectris Eiger detector, along with a no-hardware Python demo server. See the eiger help file.

Server Invocation

Servers linked with the overhead module included in the spec distribution recognize the following command-line arguments:

-h
Print the usage message.
-p port
Set the TCP port on which the server will listen. If not given, the default port (5000) will be used.
-l logfile
Set the path name for a log file. If unspecified, standard output is used, unless the server is daemonized. See -d below. Log files are opened to append.
-L lockfile
Specifies an alternate lock file. The default lock file is placed in /tmp with a name consisting of the server name appended with a hyphen, the port number and ".lock".
-v verbosity
Sets the verbosity level for logging. If unspecified, no logging is done. Meaningful levels are from 1 to 4. Higher numbers mean more messages.
-d
Daemonize the server. The server will run in the background and not be associated with a control terminal. When daemonized, the server creates a log file, either by the name specified with -l, or a file in /tmp with a name consisting of the server name appended with a hyphen, the port number and ".log". Error messages during start up that would appear on the associated terminal screen are redirected to the log file.
-k
Tells the server to kill an instance of it running with the same name and specified port number. The server then exits. This option is useful for terminating daemonized instances.
-Q
The server will exit when spec quits and the server loses the socket connection.

Additional command-line arguments may be recognized by the device-dependent code.

Lock File and Log File

With a non-zero -v verbosity option, the server prints messages. If the server is running as a daemon, the messages are sent to a log file. Otherwise, if no log file is specified with the -l option, messages are sent to the screen. The default log file when the server is daemonized is placed in /tmp with a name consisting of the server name appended with a hyphen, the port number and ".log", for example, /tmp/server-5000.log.

In order to prevent more than one instance of a particular server running, the server creates a lock file that contains the process ID of the active server. The default lock file is placed in /tmp with a name consisting of the server name appended with a hyphen, the port number and ".lock", for example, /tmp/server-5000.lock. An alternate lock file can be specified with the -L option, although that is not recommended, as every invocation of the server needs to refer to the same lock file in order for the lock file to be effective.

Configuration

The server hardware devices are configured on the Aquisition screen of the configuration editor along the following lines:

MCA (1D) and Image (2D) Acquisition Device Configuration

MCA             DEVICE  ADDR  <>MODE              <>TYPE
 0  YES localhost:5000     0                  Server MCA
...

Image           DEVICE  ADDR  <>MODE              <>TYPE
 0  YES  hostname:5001     0         Server Image Device
...

The DEVICE column identifies the TCP port on which to connect. If the server is on the same computer as spec, use the word "localhost". Otherwise, use a resolvable hostname or IP address for a remote host. If the port number is unspecified, spec will use the default port number of 5000. A string entered into the ADDR column will be passed to the server with the config message, as described below in the Messages section.

When spec initializes hardware, if it can't connect to the specified TCP port, spec will try to start the server if configuration information has been entered on the optional controller parameter screen (accessed with the p command in the configuration editor). The possible parameters are as follows:

Custom Parameters for "Server Image Device"       4/4 configured

NAME                      VALUE
user                      specadm
path                      /usr/local/lib/spec.d/servers
command                   img_server
args                      -v 4 -u image -l /tmp/img.log

The path parameter is needed if the server executable is not in the standard $PATH or in SPECD/servers. If using a remote server, the user parameter specifies a user name for the secure shell connection if the current user is not the appropriate value. Public-key secure-shell authentication needs to be set up properly for the named user to be able to start a remote server from spec. The command parameter is the name of the server and is required. The args parameter specifies arguments to the command. spec will automatically add the -p port and the -d arguments to set the port number and daemonize the server.

When initiating a connection with a configured server, spec will first try to connect to a socket at the specified port number. If there is no port to connect to, spec will start the server named as command. If a path is specifed in the config file, spec will execute the instance of command there. Otherwise, spec will attempt to execute command if it is in $PATH. If not found spec will attempt to execute command in SPECD/servers. On a remote server, spec will not look in $PATH.

xxx_par() Commands In spec

A number of xxx_par() commands are implemented for all server devices. The short hand xxx_par() is used to represent mca_par(), mca_spar() and image_par(). The mca_spar() and image_par() commands require an initial device-number argument.

Commands that are common for both MCA and image-type devices are:

xxx_par("unusable")
Returns 1 if the device is unusable due to being unconfigured or unresponsive. Otherwise returns 0.
xxx_par("responsive")
Returns 1 if the device is responsive. Returns 0 if the device is unconfigured or unresponsive.
xxx_par("controller")
Returns the string SERVER_MCA or SERVER_IMG, as appropriate.
xxx_par("device_id")
Returns the host:port string used to create the socket.
xxx_par("info")

Displays information regarding the device parameters. For MCA devices, it looks like this:

Native type: long
   Channels: 1024
  Max chans: 1024
 Auto clear: On
   Auto run: Off
Soft preset: Off

For image devices, like this:

Native type: ulong
   Max rows: 128
   Max cols: 128
   ROI rows: 65: 0 -> 64
   ROI cols: 128: 0 -> 127
   ROI npts: 8320
 Auto clear: On
   Auto run: Off
Soft preset: Off
xxx_par("address")
Returns the value of the optional ADDR field from the configuration editor, if it has been set.
xxx_par("disable" [, 0|1])
With no arguments, returns nonzero if the device has been disabled by the user, otherwise returns zero. If arg is 1, disables the device. If arg is 0, turns off disabled mode. When the device is disabled, spec will not access the hardware. On start up, and after the standard config macro or the reconfig command is run, disabled mode is off.
xxx_par("auto_run" [, 0|1])
With no arguments, returns nonzero if the device has auto-run mode set, otherwise returns zero. If arg is 1, enables auto-run mode. If arg is 0, turns off auto-run mode. When auto-run mode is set, the device is started and stopped with the counting functions tcount(), mcount(), etc. When not set, the counting functions are ignored, but the device can be controlled with the "run" and "halt" options described below. In addition, the device can be halted with the stop() function and will be halted with ^C. On start up and after the standard config macro or the reconfig command is run, server devices default to auto-run mode off.
xxx_par("soft_preset" [, arg])
With no arguments, returns nonzero if the device has soft-preset mode set, otherwise returns zero. If arg is 1, enables soft-preset mode. If arg is 0, turns off soft-preset mode. When set, and if auto-run mode is enabled (see above), the count-time preset is passed to the server with the run message on the tcount() and mcount() calls. If soft-preset mode is not set, the value set with the xxx_par("preset", value) is sent with the run message. The wait() function will wait until both the timer and the device have counted to their respective presets. When soft-preset mode is not set, but auto-run mode is, the device will be sent a stop message when the timer's preset is reached.
xxx_par("auto_clear" [, 0|1])
With no arguments, returns nonzero if the device has auto-clear mode set, otherwise returns zero. If arg is 1, enables auto-clear mode. If arg is 0, turns off auto-clear mode. When set, spec sends the clear message before acquisition is started. Auto-clear mode is set at start up and after each hardware reconfiguration.
xxx_par("native_type")
Returns one of the strings byte, ubyte, short, ushort, long, ulong, long64, ulong64, float, double or unknown to describe the native data type of the MCA device.
xxx_par("preset" [, arg])
With no arguments, returns the current preset value. Otherwise, sets the preset to arg. The preset is sent with the run message with xxx_par("run") and with the standard counting commands if auto-run mode is set but soft-preset mode is off.
xxx_par("run")
Sends the run message along with a preset value and the count mode to start acquisition.
xxx_par("halt")
Sends the halt message to stop acquisition.
xxx_par("clear")
Sends the clear message.
xxx_par("host")
Returns a string containing the host name on which the server is running, as reported by the server.
xxx_par("pid")
Returns the process ID of the server as reported by the server.
xxx_par("description")
Returns the description string sent by the server.
xxx_par("port")
Returns the port number that spec used to make the socket connection.
xxx_par("exit")
Sends a command to tell the server process to exit, but does not mark the devices as unresponsive. If the server restarts, the socket communication will be reestablished. Commands will continue to be attempted, and will return errors until the server restarts or a reconfig command establishes the server as unresponsive.
xxx_par("kill")
Sends a command to tell the server process to exit. Then sends a SIGTERM signal to the process that the server should catch if it is still running and then exit. Then sends a SIGKILL signal to the process if it is still running which should unconditionally kill it. spec marks the device as unresponsive, and a reconfig command is needed to re-enable communication.
xxx_par("send", cmd)
Sends an arbitrary command to the server. Commands that are not recognized by the server may be recognized by the hardware-dependent prototype code.
xxx_par("read", cmd)
Sends an arbitrary command to the server and returns the reply. Commands that are not recognized by the server may be recognized by the hardware-dependent prototype code.
xxx_par("device_command")
Sends a get message to the server with the contents of "device_command", which can be more than one word. Expects to return a result. A "device_command" is any string that isn't recognized by spec's built-in code. The device-dependent server code should return an error if it also doesn't recognize the command.
xxx_par("device_command", "setting")
Sends a set message to the server followed by the contents of "device_command" and "setting", both of which can be more than one word. A "device_command" is any string that isn't recognized by spec's built-in code. The device-dependent server code should return an error if it also doesn't recognize the command.

For MCA devices, the following standard MCA commands are recognized. As usual, mca_par(...) or mca_spar(a, ...) can be used, where a is the MCA device number.

mca_par("chans"), mca_par("max_chans") or mca_par("max_channels")
All return the number of channels as sent in the response to the config message.
mca_par("chan#")
Sends the read # # message to the server and returns the reply, where # is a single channel number. The device-dependent code may or may not support reading a single channel.
mca_par("chan#", value)

Sends the write # # message to the server followed by a single channel of data. The device-dependent code may or may not support writing a single channel or writing data at all.

Set the contents of channel number # to value.

See the mca help file for additional details.

For image devices, these standard commands are recognized, where a is the image device number:

image_par(a, "rows")
Returns the maximum number of rows available in the returned image array, as sent in the response to the config message.
image_par(a, "cols")
Returns the maximum number of columns available in the returned image array, as sent in the response to the config message.
image_par(a, "roi" [, row_beg [, row_end [, col_beg [, col_end]]]])
With no optional arguments, returns the total number of points in the region of interest. With 1 to 4 arguments, sets the region-of-interest values as indicated.
image_par(a, "row_beg"|"row_end"|"col_beg"|"col_end" [, value])
These all return or set individual limits in the region of interest.

Protocol

A key part of the protocol is that every message sent by spec to the server receives an immediate reply. Each message from spec starts with the characters =: followed by a space, a sequence number, a one-word command message and optional arguments. The server responds to each message from spec with a newline-terminated line of text. The server response starts with the characters @: followed by a space, the sequence number from spec, the number of bytes in the optional response string and a # character. If there is no response string, the number of bytes is zero and the message terminates with a newline. If there is a response string, the number of bytes is the length of the response not including the terminating newline.

For example, the first message from spec will be:

=: 1 hello server_name\n

where server_name is the name of the server. The response from the server will be something like:

@: 1 43#hello back V2 certified.local 16891 Generic\n

The hello message from spec is used to initiate a connection. The correct response from the server is hello back followed by the protocol version number supported by the server, the hostname of the server, the process ID (PID) on the server and a string description provided by the hardware-dependent description() function.

If the server detects an error, the reply begins with the characters !:. The response string after the # is the error message.

The MCA or image data array is transmitted as a binary stream which is initiated and acknowledged using the above message structure. See the detailed description in the next section.

Messages

The messages sent by spec to the server and the expected server responses are described below. The optional address arguments are explained in the Device Addresses section that follows. Most messages generate a call to the device-dependent function (if one exists) as indicated. See the Functions section for more information.

hello server_name

Sent by spec to initiate a connection. The server responds with a message of the form:

hello back Vver hostname pid description

where the elements of the reply are as follows:

ver
Version number of the server protocol.
hostname
The host name of the server.
pid
The process ID of the server.
description
A text string provided by the device-dependent description function in the server.

An environment may have more than one server listening at different port numbers, making it is possible to connect to a different server than the one intended. The server will compare its name to the argument of the hello command and return an error if the names don't match (as of spec release 6.07.01).

goodbye how
Sent before spec disconnects the socket. The argument how will be 0 during a reconfig disconnection. The argument will be 1 when spec exits. If the server is invoked with the -Q flag, the server will also exit in the latter case (as of spec release 6.08.07). There is no server response to the goodbye message, as spec will have shutdown the socket after sending "goodbye".
config [arg]

Sent by spec when running its hardware configuration code at start up and on the reconfig command. The arg string is the contents of the ADDR field from the edconf hardware configuration editor (if the field is not empty). For an MCA device, the server response includes the native data type and the maximum number of points per channel, as in:

long 1024

For an image device, the response contains the native data type, the maximum number of rows and the maximum number of columns in the image, as in:

ushort 512 512

Generates a call of the device-dependent config function in the server.

exit
Sent by spec to tell the server to terminate. After a call to the device-dependent exit function, the server sends an acknowledgment and then terminates.
clear

Sent by spec with the xxx_par("clear") function and automatically if the device is started when "auto_clear" mode is enabled.

Generates a call of the device-dependent clear function in the server.

run preset mode

Sent by spec to start counting by the xxx_par("run") functions or with the counting functions when "auto_run" mode is enabled for the device. If "soft_preset" mode is enabled for the device, preset is the count time in seconds when counting to time, the monitor count preset when counting to monitor and zero when doing a move_cnt.

When using the counting functions with "soft_preset" disabled or with xxx_par("run"), preset is the value set for the device with xxx_par("preset", value). The mode value is as follows:

1   called by mcount()
2   called by tcount()
3   called via move_cnt
4   called by xxx_par("run")

Generates a call of the device-dependent run function in the server.

halt [1|0]

Sent by spec to stop counting if the device is active when the xxx_par("halt") command is entered, when the master timers finish counting (only if "auto_run" mode is enabled for the device), when the stop() function is called, and when ^C is entered at the keyboard. The argument is zero when called from xxx_par("halt") or when the master timer completes. The argument is one when called from stop() or a ^C abort.

Generates a call of the device-dependent halt function in the server.

get_status

Sent by spec to poll the device when it is active to see if it is still active. The server returns nonzero if the device is active, zero if it is idle.

Generates a call of the device-dependent poll function in the server.

get [a=addr] par

Sent by spec from xxx_par(par) to get the value of the indicated parameter par, but only if par is not one of the built-in parameters recognized by spec. The server returns the parameter value in its reply as a string.

Generates a call of the device-dependent pars function in the server with how argument set to 0.

set [a=addr] par value

Sent by spec from xxx_par(par, value) to set the value of the indicated parameter par, but only if par is not one of the built-in parameters recognized by spec. The parameter is sent to the server as a string.

Generates a call of the device-dependent pars function in the server with the how argument set to 1.

read [a=addr] first last

Sent by spec with mca_get(arr [, first [, last]]) to read the MCA data. The server responds with a message that confirms the number of points it will send. The server then sends that many points in binary format using the native data type. After reading the data, spec sends the command xfer_done.

Generates a call of the device-dependent send_data function in the server.

write [a=addr] first last

Sent by spec with mca_put(arr [, first [, last]]) to send data to the MCA. If the server will accept the data, it responds with a message that contains okay npts where npts is the number of points it expects. spec then sends that many points in binary format using the native data type. After sending the data, spec sends the command xfer_done.

Generates a call of the device-dependent receive_data function in the server.

read [a=addr] row_beg row_end col_beg col_end

Sent by spec with image_get(sel, arr) to read the image data. The begin and end rows and columns reflect the current region of interest, either as set by the "roi" option to image_par() or, if not set, the size of the array arr. The server responds with a message that confirms the number of points it will send and the data type. The server then sends that many points in binary format using the indicated data type. After reading the data, spec sends the command xfer_done.

Generates a call of the device-dependent send_data function in the server.

write [a=addr] row_beg row_end col_beg col_end

Sent by spec with image_put(sel, arr) to send data to the image device. the begin and end rows and columns reflect the current region of interest, either as set by the "roi" option to image_par() or, if not set, the size of the array arr. If the server will accept the data, it responds with a message that contains okay npts where npts is the number of points it expects. spec then sends that many points in binary format using the native data type. After sending the data, spec sends the command xfer_done.

Generates a call of the device-dependent receive_data function in the server.

Data Types

The data type given in response to the config command can be as follows:

ubyte 8-bit unsigned integer
ushort 16-bit unsigned integer
ulong 32-bit unsigned integer
ulong64 64-bit unsigned integer
byte 8-bit signed integer
short 16-bit signed integer
long 32-bit signed integer
long64 64-bit signed integer
float 32-bit single precision floating point
double 64-bit double precision floating point

Device Addresses

To accommodate devices that have more than one channel, the get, set, read and write commands may include the a=addr argument after the command. The address is taken from the optional subaddress component of the first argument to the mca_spar(), mca_sget() or mca_sput() functions for MCA devices. Subaddresses are supported for the image_par(), image_get() and image_put() commands as of spec release 6.08.07.

The address argument may be given as "0.0", 0.1, 0.2, ..., where the first digit is the unit number from the config file, and the digit right of the decimal point is the subaddress. The argument can be entered as a number or a string. The syntax "0:0", "0:1", "0:2", ..., is also recognized, where the arguments for each module are strings.

If no subaddress is specified, the command doesn't include the a= word. The server considers no address the same as a=-1.

Currently, spec only includes the address argument for the commands to get and set parameters and to read and write data. Commands to start, stop, clear and poll the hardware apply to the entire unit.

Functions

Each device-dependent code module must initialize a structure containing the indicated functions.

struct  device_funcs {
     int     (*parse_args)(int argc, char **argv);
     char    *(*description)();
     int     (*initialize)(char *err_str, struct data_init *d);
     int     (*config)(char *err_str, char *arg);
     int     (*clear)(char *err_str, int addr);
     int     (*run)(char *err_str, int addr, int mode, double preset);
     int     (*stop)(char *err_str, int addr, int mode);
     int     (*poll)(char *err_str, int addr, int *status);
     int     (*pars)(char *err_str, int addr, int how, char *cmd, char **args, char **reply, int vsize);
     int     (*send_data)(char *err_str, int addr, struct data_block *d);
     int     (*receive_data)(char *err_str, int addr, struct data_block *d);
     int     (*exit)(char *err_str);
     char    *(*usage)();
     void    (*select_poll)();
     int     (*cmd)(char *err_str, int addr, char *cmd, char **args, int vsize);
 };

See the source code for examples of usage.