OAA 1 Lisp Developer's Notes

Contents:

Overview

This document informally describes the basics of making a Lisp program into an OAA agent, which normally requires only a small effort. Four things need to be done: declare solvables; define the required application-specific functions; write code to load the required foreign functions; and write code to initialize the agent (including registering with a Facilitator).

Declaring Solvables

Writing Application-specific Functions

Complete the oaa callback functions idle, do-event, app-init, and app-done. They must be defined in package 'CL-USER.

The application-specific functions are generally quite simple.

DO-EVENT

    (defun cl-user::DO-EVENT (KS Event) ...)
Do-event is normally defined as a "cond" expression with a case for each solvable provided by the agent. Each case should call whatever functions implement that solvable.

The Event is passed to do-event as a single Prolog-formatted string, which is precisely the event as received from the facilitator. In decomposing this string, you may wish to use agentlib function calls (functor Event), (list-len ArgList), (nthelt ArgList), (Term ArgList), or other utility functions that are defined in agentlib.lisp.

Remember that do-event (normally) needs to return a string that represents, in Prolog syntax, a list of solutions (0 or more) that would result from treating the event as a Prolog goal. Note that this means that each solution in the list is unifiable with the goal. This can sometimes be trivial, but sometimes can involve a fair amount of work.

There are 2 non-string values that may be returned from do-event in certain situations:

  1. The list (:delay-event DelayIndex) may be returned to indicate that solutions are not immediately available; that is, the event that returns the solutions will be delayed. DelayIndex should be obtained by developer code, by calling (delay-solution). This call causes library code to store an entry in a table, saving information needed to generate the delayed event. When the solutions are known, developer code must call (return-delayed-solutions DelayIndex Solutions), where Solutions is the Prolog-formatted list of solutions, as described above (or t or nil -- see comments for return-delayed-solutions). IMPORTANT: the call to delay-solution must be made in the same thread in which do-event is called.
  2. The list (:delay-process func args) may be returned to indicate that solutions should be obtained by creating a new process that applies func to args (this application must return the Prolog-formatted solutions list). When this option is used, library code does the rest (that is, it is not necessary for developer code to call return-delayed-solutions).

IDLE, APP-INIT, and APP-DONE

    (defun cl-user::IDLE () ...)
    (defun cl-user::APP-INIT () ...)
    (defun cl-user::APP-DONE () ...)
Idle is called whenever the OAA event queue is checked and found empty; app-init is called from oaa-go, and app-done is called when the 'halt' event is received from the facilitator. These functions can do as little or as much as you wish. For most applications, idle need not do anything, so it is sufficient to give it a definition that just returns nil. App-done should normally call (lcl:quit) after doing whatever cleanup is appropriate for the application.

If do-event, idle, app-init, or app-done is missing, a warning message is printed when oaa-setup is called. Also, for do-event, a warning is printed every time an attempt is made to call it.

Loading Agentlib

Load agentlib.lisp (or the equivalent compiled version) into the Lisp environment along with the Lisp application.

Load the required application-specific functions (see above) into the 'oaa package.

Call

    (OAA-LOAD path)
This loads the appropriate foreign function libraries. path is an optional string argument that tells what directory to load the .o or .so files from. Currently the default value of path is set for the directory structure at SRI AIC, so on other systems it is necessary to specify a path.

In the case of Lucid/Liquid and Allegro on a non-System V platform, the following are loaded:

agentlib.o, tcp.o, and required functions from libc.a and libm.a
In Allegro, running on a System V platform (such as Solaris), the following are loaded:
agentlib.so, and required functions from libc.so and libm.so

Initializing the Agent

After loading, the steps in starting an agent are these: Note that OAA-LOOP creates a new Lisp process, named "OAA Event Server".

If the default parameters are acceptable, calling OAA-GO provides the sequence of calls (OAA-LOAD - OAA-SETUP - APP-INIT - OAA-LOOP)

Notes on Implementation Strategy

The general strategy is to maximize the use of agentlib.c. Thus we have used the foreign function interface (FFI) as much as possible to provide direct mappings from Lisp functions to C functions of the same name. In every case where we use a foreign function from agentlib.c, there are 2 functions in the Lisp code - the foreign function and a wrapper function. The wrapper function has the same name (or close) to the C function, and is the one that is for use by the Lisp programmer. Foreign functions are always named with the prefix "ff_", and are not to be used directly

For example, ff_functor is the Lisp foreign function that actually calls the C function "Functor". Functor is also the name of the Lisp wrapper function.

It has been necessary to rewrite or partially rewrite some important functions in Lisp, however, such as:

    OAA-LOOP
    INTERPRET
and others. the main reason for these rewrites is so that things which the OAA application developer is required to define, such as functions "idle" and "do-event" can be defined entirely in Lisp. In most cases, the Lisp definitions of these functions still rely heavily on calls to other functions in agentlib.c via the FFI.

Functions which are defined here but have no equivalent in agentlib.c:

    OAA-SETUP

See also: comments and documentation related to the OAA C library.


Back to OAA 1 Lisp Library Overview