Adding DimensionsTopApplication Startup and Event FunctionsUsing a Control ShellGoTo Top

Using a Control Shell

A control shell is one of the three major components of a blackboard system (along with KSs and the blackboard). The control shell directs the problem-solving process by managing how KSs respond to contributions that are placed on the blackboard by an executing KS and to other events that may be triggered by the application or received from external sources. In this exercise we will use a control shell called the “Agenda Shell.”

GBBopen's Agenda Shell is a generalization of the priority-based scheduling approach that was used in the original Hearsay-II blackboard architecture. The Agenda Shell manages KS definitions, and it initiates and terminates KS activities by:

The Agenda Shell is highly customizable and extensible, and it can be used as the foundation for implementing advanced control mechanisms. We will use only the most basic Agenda Shell capabilities in this Tutorial.


This exercise shows you how to:


Prerequisites

The tutorial-example.lisp file as modified thus far:

  (in-package :gbbopen-user)

  (define-unit-class location ()
    (x y)
    (:dimensional-values
      (x :point x)
      (y :point y))
    (:initial-space-instances (known-world)))

  (defmethod print-instance-slots ((location location) stream)
    (call-next-method)
    (when (and (slot-boundp location 'x)
               (slot-boundp location 'y))
      (format stream " (~s ~s)"
              (x-of location)
              (y-of location))))

Step 1: Load the Agenda Shell

Start up a fresh Common Lisp session and load the :agenda-shell-user module, using the :agenda-shell-user REPL command:

  cl-user> :agenda-shell-user
  ;; Loading <install-dir>/startup.lisp
     ...
  ;; Loading <install-dir>/<platform-dir>/gbbopen/
  ;;            control-shells/agenda-shell-user.fasl
  gbbopen-user>

This loads everything that we've been loading with the :gbbopen-user module and, additionally, GBBopen's Agenda Shell control shell. As with the :gbbopen-user REPL command, the current package in the REPL is set to the :gbbopen-user package.

Step 2: Run the control shell

Now, start the Agenda Shell:

  gbbopen-user> (start-control-shell)
  ;; Control shell 1 started
  ;; No executable KSAs remain, exiting control shell
  ;; Control shell 1 exited: 2 cycles completed
  ;; Run time: 0 seconds
  ;; Elapsed time: 0 seconds
  :quiescence
  gbbopen-user>

What just happened? The Agenda Shell began executing and looked for something to do. However, we have not yet defined any knowledge sources (KSs), so the Agenda Shell indicates that it did not find any executable KSAs in its initial KS-execution cycle. This situation is called quiescence and, by default, the Agenda Shell signals that quiescence has occurred and then continues for an additional KS-execution cycle in case any executable KSAs resulted from the quiescence signal. Again, no executable KSAs were found in cycle 2, so the Agenda Shell exits due to :quiescence.

Note that the Agenda Shell requires that the idle-loop process has been started on CMUCL and that multiprocessing has been started on LispWorks. (An error message will instruct you on what to do if this is not the case.)

Step 3: Define a KS

So let's define a KS!

Edit your tutorial-example.lisp file and add the following function and KS definition to the end of the tutorial-example.lisp file:

  (define-unit-class location ()
    (x y)
    (:dimensional-values
      (x :point x)
      (y :point y))
    (:initial-space-instances (known-world)))

  (defmethod print-instance-slots ((location location) stream)
    (call-next-method)
    (when (and (slot-boundp location 'x)
               (slot-boundp location 'y))
      (format stream " (~s ~s)"
              (x-of location)
              (y-of location))))

  ;;; ====================================================================
  ;;;   Startup KS

  (defun startup-ks-function (ksa)
    (declare (ignore ksa))
    ;; Create an initial location unit instance at (0,0):
    (make-instance 'location :x 0 :y 0))

  (define-ks startup-ks
      :trigger-events ((control-shell-started-event))
      :execution-function 'startup-ks-function)

The function startup-ks-function implements the KS. The Agenda Shell always calls a KS's execution function with a single argument, a ksa unit instance that represents the activation of the KS. For the present, we will ignore the ksa argument. Our simple startup-ks-function creates a location unit instance at the center of the known-world, coordinate (0,0).

The define-ks form defines a KS named startup-ks to the Agenda Shell. The :trigger-events value indicates that the KS should be triggered when an event called control-shell-started-event is signaled. The Agenda Shell signals control-shell-started-event once, when the control shell is started. The :execution-function names the function (that we just defined) that implements the KS.

Compile and load the entire tutorial-example.lisp file directly from the editor buffer (using C-c C-k in SLIME; C-c C-b in ELI).

The Agenda Shell creates a ks unit instance for each KS that we define:

  gbbopen-user> (map-instances-of-class #'print 'ks)
  #<ks startup-ks> 
  nil
  gbbopen-user>
Note that the name of the ks unit instance is the name of the KS.

Step 4: Make the known-world

Before we can run our KS, we must make the known-world space instance:

  gbbopen-user> (make-space-instance '(known-world)
                          :dimensions '((x :ordered) (y :ordered))))
  #<standard-space-instance (known-world)>
  gbbopen-user>

Step 5: Start the control shell

Start the Agenda Shell again:

  gbbopen-user> (start-control-shell)
  ;; Control shell 1 started
  ;; No executable KSAs remain, exiting control shell
  ;; Control shell 1 exited: 3 cycles completed
  ;; Run time: 0 seconds
  ;; Elapsed time: 0 seconds
  :quiescence
  gbbopen-user>

Did it work? Let's describe the blackboard repository:

  gbbopen-user> :dsbb
  
  Space Instance                Contents
  --------------                --------
  known-world                   1 instances (1 location)

  Unit Class                    Instances
  ----------                    ---------
  control-shell                         1 *
  ks                                    1 +
  ksa-queue                             2 +
  location                              1
  ordered-ksa-queue                     1 +
  standard-space-instance               1
                                ---------
                                        7 instances
  gbbopen-user>
The initial location unit instance is there!

Note that some other unit instances have been created by the control shell. There is one ks unit instance, our startup-ks KS, as well as two ksa-queue and one ordered-ksa-queue unit instances. The ordered-ksa-queue is the rating-based queue of pending KSAs (no pending KSAs remain on it) and the two ksa-queue queues are the Agenda Shell's executed KSAs and obviated KSAs queues (both empty). We will discuss these queues in a later exercise.

The ks, ksa-queue, ordered-ksa-queue unit-instance counts are followed by plus signs (+). This indicates that these unit classes have been defined to be retained, meaning that their instances are not deleted by our call to delete-blackboard-repository. The plus sign indicates that the retention attribute will be propagated to all subclasses of those unit classes; retained, but not propagated, would be shown by an asterisk (*).

Let's verify this behavior by calling delete-blackboard-repository and then describe the blackboard repository:

  gbbopen-user> (delete-blackboard-repository)
  t
  gbbopen-user> :dsbb
  There are no space instances in the blackboard repository.

  Unit Class                    Instances
  ----------                    ---------
  control-shell                         1 *
  ks                                    1 +
  ksa-queue                             2 +
  ordered-ksa-queue                     1 +
  gbbopen-user>

Step 6: Display control shell activities

With all but the retained unit instances deleted from the blackboard repository, let's rerun the control shell. However, this time we will ask GBBopen to display more of what the control shell is doing. First, enable display of all control-shell and instance-creation events by evaluating:

  gbbopen-user> (enable-event-printing '(control-shell-event :plus-subevents))
  nil
  gbbopen-user> (enable-event-printing 'instance-created-event)
  nil
  gbbopen-user>
or the shorthand equivalent:
  gbbopen-user> (enable-event-printing '(control-shell-event +))
  nil
  gbbopen-user> (enable-event-printing 'instance-created-event)
  nil
  gbbopen-user>

We will cover events, event printing, and event functions in greater detail in a later exercise.

Start the Agenda Shell once again:

  gbbopen-user> (start-control-shell)
  ;; Control shell 1 started
  => Control-shell-started-event
  => Control-shell-cycle-event
       :cycle 1
  => Instance-created-event
       :instance #<ksa 1 startup-ks 1>
  => Ksa-activated-event
       :instance #<ksa 1 startup-ks 1>
       :cycle 1
  => Ksa-executing-event
       :instance #<ksa 1 startup-ks 1>
       :cycle 1
  => Instance-created-event
       :instance #<location 1 (0 0)>
  => Control-shell-cycle-event
       :cycle 2
  => Quiescence-event
  => Control-shell-cycle-event
       :cycle 3
  ;; No executable KSAs remain, exiting control shell
  ;; Control shell 1 exited: 3 cycles completed
  ;; Run time: 0 seconds
  ;; Elapsed time: 0 seconds
  :quiescence
  gbbopen-user>

Step 7: Control-shell stepping

Let's rerun the control shell once again, but this time we will enable control-shell stepping. Again, delete the blackboard repository:

  gbbopen-user> (delete-blackboard-repository)
  t
  gbbopen-user>
and disable the event printing that we enabled in the last step:
  gbbopen-user> (disable-event-printing)
  nil
  gbbopen-user>

Now start the Agenda Shell, this time with stepping enabled:

  gbbopen-user> (start-control-shell :stepping 't)
  ;; Control shell 1 started
  >> CS Step (cycle 1):
     About to process event #<control-shell-started-event>... [? entered]
  Stepping commands (follow with <Return>):
     d       Disable this kind of stepping (:process-event)
     e       Enable another kind of stepping
     f       Evaluate a form
     h or ?  Help (this text)
     q       Quit (disable all stepping and continue)
     s       Show enabled stepping kinds
     x       Exit control shell
     =       Describe the object of interest (bound to ==)
     +       Enable all stepping
     -       Disable all stepping
     <Space> Continue (resume processing)
  >> CS Step (cycle 1):
     About to process event #<control-shell-started-event>... [<Return> entered]
  >> CS Step (cycle 1):
     About to activate KS startup-ks on
       control-shell-started-event... [<Return> entered]
  >> CS Step (cycle 1):
     About to execute KSA #<ksa 1 startup-ks 1>... [<Return> entered]
  << KSA 1 returned: (#<location 1 (0 0)>)
  >> CS Step (cycle 2):
     About to signal quiescence... [<Return> entered]
  >> CS Step (cycle 3):
     About to signal quiescence... [<Return> entered]
  ;; No executable KSAs remain, exiting control shell
  ;; Control shell 1 exited: 3 cycles completed
  ;; Run time: 0 seconds
  ;; Elapsed time: 1 minute, 6 seconds
  :quiescence
  gbbopen-user>


The GBBopen Project


Adding DimensionsTopApplication Startup and Event FunctionsUsing a Control ShellGoTo Top