3 ADAM Message System Extension

 3.1 The Message System
 3.2 The Tcl/Tk Interface

The Adam message system extensions provide access to the message system at the level of the ams library and a fairly detailed knowledge of the Adam message system and how it is used by Adam tasks is required. Therefore, this section describes a set of Tcl procedures that use the message system interface to provide a higher level interface that is suitable for controlling Adam tasks from a graphical user interface. The low-level commands are described in appendix C.

Some of the features of Adam tasks described here appeared in release 3.1 and so you must ensure that any tasks to be used with Tcl have been linked with Adam 3.1 or later.

3.1 The Message System

Command languages (such as ICL or Tcl/Tk) control Adam tasks by sending them Adam messages and responding to any messages from the task The messages from the task request such things as new values for parameters or contain text to displayed to the user. This section describes what sort of messages tasks respond to and what replies can be expected; it concentrates on data reduction tasks and ignores some of the more complicated behavour of instrument control tasks.

3.1.1 The OBEY message

An adam task supports one or more commands, normally referred to as actions, (e.g. the KAPPA task supports “add”, “cadd”, “stats” etc.) and is instructed to execute one of them by sending it an “OBEY” message. The message contains the action name (e.g. “add”) and a command line which can contain parameter values and any of the keywords that modify the behaviour of the parameter system (e.g. “in1=test accept”). If the task recognises the action name3 it responds with an actstart message; the message name field of the message will be the action name and the status field will be DTASK__ACTSTART. The task then starts executing the command.

In the simplest possible case, where the action writes no text and does not need to prompt for any parameter values, the task completes the action and sends an endmsg message to the command language. The message name is again the action name and the status will be DTASK__ACTCOMPLETE. The task is now ready to execute another action.

If, as usually happens, the task does want to display some text, it will send one or more inform messages; the text to display will be in the value field of the message. Note that the message system makes no distinction between text and error reports, however, the adam error reporting system always prefixes the first line of an error message with two exclamation marks and subsequent lines with a single exclamation mark. If the task needs a value for a parameter it will send a paramreq message; the value field will contain the parameter name, prompt, default value etc.. The command language must reply with a paramrep message with the new parameter value in the value field.

The only other message that a task might send as a result of an “OBEY” is a sync message; the purpose of this message is to instruct the command language to ensure that all output has been actually written to the screen for the user to read—for example, instructions on how to perform some interaction with a graphics device. In the context of a GUI, where updates to the screen are performed asynchronously whenever the GUI process is idle, the message probably has no useful function. However, a synchrep message must be sent to acknowledge the sync message (the adamtask library described below does this automatically).

If an error occurs during the execution of a command, the status field of the final endmsg message will be the final exit status of the action; ie. something other than DTASK__ACTCOMPLETE. If the action cannot even be started (if, for example, the action name isn’t recognised) there will be no actstart message and the actcomplete message will contain an error status (eg. SUBPAR__NOACT).

3.1.2 The SET message

To set a parameter, you send a “SET” message with the message name set to the parameter name in the form action:parameter (eg. stats:ndf) and the value field to the desired parameter value. The task will respond with a setresponse message with a status field set to SAI__OK if the parameter was sucessfully set. If not, it will send one of more inform messages containing error messages followed by a setresponse message with a status other than SAI__OK (for example, SUBPAR__NOPAR if the parameter didn’t exist). When the task executes the coresponding action it will behave exactly as if the parameter had been set with a command line argument in the “OBEY” message.

3.1.3 The GET message

To get a parameter value, send a “GET” message with the parameter in the message name field; the task will respond with a getresponse message with the name set to the parameter name and with the parameter’s value in the value field. Errors are handled in the same way as for a “SET”. The “GET” message is only really useful if the task is running with its type set to “I”, otherwise all of an actions parameters are annulled (set to have no value) when an action completes.

3.1.4 The CONTROL message

A “CONTROL” message instructs a task to perform one of the pre-defined actions that all Adam tasks support. There are two control actions recognised:

default
instructs the task to change to a new default directory. The value field of the message should be set to the new directory. The task will respond with a controlresponse message with the value field set to the new directory. If a “CONTROL” message with the action set to “default” has a blank value field the value field in the setresponse message will be set to the current default directory.
par_reset
sets all of the tasks parameters to the state they were in when the task was first loaded. The task responds with a controlresponse message.
3.1.5 The CANCEL message

The final type of message is the “CANCEL” message which is similar to an “OBEY” message but data reduction tasks do not support any “CANCEL” actions.

3.2 The Tcl/Tk Interface

The interface is loaded by reading the file /star/lib/startcl/adamtask.tcl with the tcl command:

  source /star/lib/startcl/adamtask.tcl

This defines a procedure called adamtask which starts an Adam task. It takes two arguments: a name to be used to refer to the task in later Tcl commands, and, optionally, the name of the executable file to run. if the second argument is omitted, the name must be the name, as known to the Adam message system, of a task already loaded. In addition it creates a new Tcl command with the same name as the task which can then be used to communicate with the task.

For example:

  adamtask kappa /star/bin/kappa/kappa_mon

will start the KAPPA monolith and create a Tcl command called kappa which can be used to send messages to the task. The first argument to this new command is the type of Adam message to send to the task and is one of set, get, obey, cancel, paramreply or syncrep.

The format of the command to send a task an obey message is:

  <taskname> obey <action> <command-line>

So, for example, we can instruct KAPPA to execute the add command with:

  kappa obey add "in1=image1 in2=image2 out=result"

However, we don’t receive any notification of when the obey has completed.

The action to be taken when a message of a particular type is sent by the task is specified by additional arguments consisting of a message type with a hyphen prepended followed by a Tcl command to be executed when the message is received. The command is executed in global scope in the same way as widget and X event bindings. So, for example:

  kappa obey add "in1=image1 in2=image2 out=result" \
      -endmsg {puts "add finished"}

Will print “add finished” on the terminal when the obey has completed.

The various fields in the message can be inserted in the command to be executed wherever the percent character appears (similar to the substitutions performed when widget bindings are invoked). For example:

  kappa obey stats "ndf=result" -inform {puts %V}

will print the messages output by the stats command on the terminal. The following tokens are recognised for all message types:



token replaced by


%C Message context
%T Task name
%N Message name
%P Path
%M Message id
%S Status
%V Message value
%R Reply Token
%% %


In the case of a paramreq message the value is in the form of a Tcl list and the following tokens are replaced by the appropriate element of the list:


token replaced by


%n parameter name
%p prompt string
%d parameter default
%h help text
%e error message


Dealing with a paramreq message is more complicated than any other message type because it demands that a reply is sent to the task before it will continue. The format of the command to send a paramreply message is:

  <task-name> paramreply <reply-token> <reply>

The <reply-token> parameter is used by the message system to route the message back to the task that made the parameter request and can be extracted from the paramreq message.

About the simplest example is:

  kappa obey cadd "in=image1 out=result" \
      -paramreq {kappa paramreply %R !}

This will reply with “!” whenever any parameter is prompted for. A more realistic example is:

  proc prompt {task reptok param string default} {
  #+
  # Handles a prompt request from a task.
  #-
  
  # Create dialog box to display the prompt and receive the user’s reply.
      toplevel .${task}_prompt
      wm title .${task}_prompt "Parameter prompt from ${task}"
      wm transient .${task}_prompt .
  
  # Create the dialog box layout.
      label .${task}_prompt.label -bd 5 -text \
          "$task requires a value for $param"
      label .${task}_prompt.prompt -text $string -bd 5
      entry .${task}_prompt.entry -bd 2 -relief sunken
      .${task}_prompt.entry insert end $default
      button .${task}_prompt.ok -text OK -command "
          $task paramreply $reptok \[.${task}_prompt.entry get\]
          destroy .${task}_prompt
      "
      pack .${task}_prompt.label
      pack .${task}_prompt.ok -side bottom -pady 10
      pack .${task}_prompt.prompt .${task}_prompt.entry -side left
  }
  kappa obey cadd "in=image1 out=result" \
      -paramreq {kappa prompt %T %R %n %p %d}

Default handlers are provided for paramreq, inform and sync messages. The handler for paramreq messages is the one listed above. The handler for inform messages creates a dialog box (one for each task) containing a scrolling text widget and inserts the message into it. These ensure that, by default, important messages are not ignored; they are adequate during development but for production systems bespoke inform message handlers will almost certainly be required. The handler for sync messages calls the Tcl update command and then sends a syncrep message to the task.

If you want to ignore a message type then specify an empty string as the handler procedure. For example:

  kappa obey stats "ndf=result logfile=stats.log" -inform ""

will run “stats” and log the output to a file instead of displaying them.

The format of the remaining command options that send messages to a task are:

cancel
action command-line
set
parameter-name parameter-value
get
parameter-name

There are some additional command options that don’t actually send any messages to the task:

kill
kills the adam task (provided that the task was loaded by the adamtask procedure) and deletes the task procedure.
forget
deletes the task procedure and removes the task from the list of known tasks so that it doesn’t get killed when the Tcl application exits.
path
returns 1 if a message system path to the task has been established and 0 if it hasn’t. This can be used to wait for a task to be loaded before sending it any messages. For example:
  adamtask kappa /star/bin/kappa/kappa_mon
  set count 0
  while {[kappa path] == 0} {
      after 100
      incr count
      if {$count > 100} {
         tk_dialog .loadError $title \
           "Timed out waiting for task kappa to start" error 0 OK
         kappa kill
      }
  }

loads KAPPA and tries 100 times at 100 millisecond intervals to establish a message system path.

All the task commands return immediately the appropriate message has been sent; they do not wait for the task to acknowledge the receipt of the message. The following code illustrates how to wait for an action (in this case an obey) to be completed:

  kappa obey stats -endmsg {set done 1}
  tkwait variable done

Note that the set command is executed in global scope so in this case there is no need to declare done to be global. If done were to be set in a procedure as in:

  procedure setdone {} {
       global done
       set done 1
  }
  kappa obey stats -endmsg setdone

then a global command is required. While the tkwait command is waiting, widget and X event bindings can still be triggered and adam messages received.

If a large number of adam messages are sent without waiting for an acknowledgement there is a risk of filling up the buffers in the message system and causing the system to deadlock. In order to avoid this happening you should:

3Tasks that contain only one command ignore the action name and execute the command regardless