Scripting | Overview | Construction | Examples | Development |
This document is meant to offer a description of how our scripting system
functions, both from a usage and development standpoint. We will start with an
overview of the structure of the scripting system, and how it behaves.
Following this we will cover how scripts are defined and used, along with a
brief list of many of the existing scripting components. The last part
of the paper will go over the technical details needed to extend the scripting
system to suit your needs (if it doesn't already).
The source of local script definitions is indicated by the SCRIPTS tag
which can be placed in the simulator.cfg file (e.g.
SCRIPTS: scripts.cfg). The scripts file is read once upon start-up,
and all scripts are imported and installed into the current simulation session.
On the agent side in JAF is a Scripter component, which reads from a similar
file located in the extended entry data file (indicated by the
ScriptData property). This file is read on agent startup, and the
information automatically transferred to the simulator when connected. While
the config file is read just once, scripts can be added (or removed) at any
time by other components in the agent. So, if you want dynamic scripting to
occur, just add that capability to your problem solver. The submission format
(for Scripter.addScript) is the same as the normal script text file, just
stuffed into a StringBuffer.
Grammar event scripting is handled in a similar manner as regular
agent event scripting. The current objective TAEMS structure is first
searched for Reactions. As reactions are found, their
activation traits and reactions are encoded into the simulator
scripting format, and are then added with the Scripter component as
any script would be. Thus, dynamic task structures (as discussed here) are accomplished by
converting them to the simulator scripting format. The reactions to
these scripts are handled by the simulator, which communicates to the
agent the ID of a reaction when it is to take place. When the ID is
received, task structure regeneration should take place as normal.
Reaction conversion, addition and detection are all handled
automatically by the AsggOrgDesign component.
Once in the simulation environment, scripts are periodically checked
to determine if they should be started. Right now, scripts are
checked during each of three defined phases: PERIODIC, PREEVENTS, and
POSTEVENTS. These correspond to the time when the simulator is
paused, before the event queue is processed and after the event queue
is processed, respectively. If a script's activation requirements are
found to be true, the script is immediately activated.
Thus, each script is composed of a number of parts which say when it should
fire, and other parts that specify what should happen when it does fire. While
simple in construction, this technique actually allows for quite a bit of
flexibility and versatility, when combined with a rich set of Assertions and
Reactions. It is not only the type of element in the script which has
bearing on its behavior, however. As seen above, we used general classes of
component (Time and ExecuteEventActive) and specialized them to our needs by
adding instance variables (> 5 and method-4). All the components, Script,
Assertion and Reaction, are customizable in this way. Depending on their
structure and function, they may take external data given at runtime which
affects the way they will behave. You can think of this external data as being
parameters to a function, or items in a configuration file. In generating our
scripting components, we have tried to enforce a convention that configuration
parameters be enumerated and described in the header comments on the object in
question, so if you look at the API documents for the scripting objects, you
should be able to determine how each object may be customized to your needs.
The file format used both in the configuration files and as a parameter to the
Scripter's addScript function is fairly simple. Each element in the file is
prepended with a type name, indicating what sort of item is defined on that
line. A Script line will begin with "Script", an Assertion with "Assertion",
and so forth. Individual items in a line are separated by commas (",").
A script is started by specifying a Script class name (e.g.
AndScript, OrScript) to give the script assertion checking properties.
The script may be given a textual name, which can be used to identify
it later if needed, and any parameters the designer chooses to
specify. Here's an example:
Script, AndScript, My First Script, Fire:10
Following the script definition is a list of one or more assertions, one per
line. Each assertion is specified using its class name, followed by parameters
encoded in the same format as was used in the Script line. Here's a couple
examples:
Assertion, Time, Op: >, Value:5
Reactions are specified in the same way as Assertions - a class name followed
by data. Here's the simple QuitSim reaction we used above (it uses no
external parameters):
Reaction, QuitSim
Look at this link to see an example script
config file. Note this file isn't meant to do anything constructive;
it just serves as sort of a tangible example of what a script config
file might look like.
Scripts
Assertions
Reactions
As you might have deduced from the structure given above, the
scripting mechanism was designed to be very modular and extensible,
thus allowing users to develop new script objects if the existing set
fail to meet a particular need. Any of the three scripting object
types can be easily derived to produce new behaviors or functionality
by following the construction guidelines that will be covered in this
section.
All three types of objects share a few features in common. During
construction, they are passed the raw textual description of the
parameters, exactly as encoded in the configuration file. This data
is parsed by a common method, and the resulting data stored in a
Hashtable accessible by the getData() function in the object
(parameters are stored in String form, keyed by their property names).
This process is done automatically. Each object also has an init()
method, called just after construction, during which parameter
conversion is typically performed (e.g. converting the String data in
the Hashtable to a more suitable representation). The JAF function
agent.simplest.State#reTypeProperty() may be useful here. As stated
earlier, we expect that users defining new scripting objects will
enumerate and describe any parameters used by their objects in the
object's header comments, using the Javadoc comment style.
Script has essentially two functions which are important to customize:
pulse() and reset(). The first, pulse, is called by the script engine
periodically to allow the Script to determine if it should fire.
Thus, the primary purpose of pulse is to check its assertions and
determine if the script should fire. A script may fire with the
start() method. The reset method is responsible for resetting any
internal structures maintained by the Script back to their original
values when it is reset.
The Assertion structure is very simple. The only method of interest here is
check(). The check() method should determine, by whatever means necessary,
weather or not the assertion is true. If the assertion is false, or an error
occurs, the check function should return false.
As with Assertion, Reaction also has just one method to define in derived
classes: realize(). This method is responsible for producing the behaviors
defined by the Reaction. Realize should return true if the action was
successfully performed, or false if an error occurred.
The structure of the objects as shown is quite simple. The only complicated
part of creating new objects is how to hook your Assertions and Reactions into
the simulation environment - you may need to know how you muddle around in the
guts of the simulator to achieve your goals. This may be quite daunting to
users, especially if you do not have the simulator source code available to
you (but feel free to write us if you need guidance with your object). One
alternative is to make use of a preexisting object to produce your
behavior. For instance, the GrammarEvent object is a trivial subclass of
SendMessage which just fills in the appropriate parameters with the data needed
to send a GrammarEvent. For some types of objects this may be the easiest
method of construction.
Simulation Scripting
The simulator, and the agents connected to it to a certain extent, support a
wide range of scripting options. A script is meant to help both the scenario
designer and tester easily simulate sophisticated behaviors and responses,
as well as automate the execution of those scenarios. With the scripting
services provided, you may do something as mundane as automatically start the
simulation once certain agents have connected, or as complicated as remotely
modify the internal beliefs of a specific agent.
Scripting Environment Overview
All script activity is controlled by the simulation controller. Scripts are
gathered from two locations: a local configuration file and from any
connecting agent. The structure for the scripts (defined in the next section)
is the same, regardless of the source.
Script Construction
Each script in the simulation environment is composed of three parts:
Lets look at a simple script to see how these work in practice:
This script is an instance of AndScript, which specifies that the results of
the assertions are ANDed together to determine if the script should fire. In
plain terms, this means that all the assertions must be true for a reaction to
be produced. The script has two assertions: one that says that the current
time must be greater than five, and another that says that method-4 must be
active. So, we now know that this script will fire any time after pulse five
when method-4 is being executed. The result of this script, as specified with
the QuitSim Reaction, is fairly straightforward - the simulation will end. One
might use this kind of script when testing some sort of behavioral technique or
gathering data. The designer in this case knew that the quality being studied
or quantified would have taken place by this time, so the simulator can
automatically quit.
Assertion, ExecuteEventActive, Method:method-4
Some Example Objects
In this section, we'll briefly go over some of the scripting objects
which have already been written. Many of the existing objects are
covered here, but not all. For a more complete listing, and more details on
an object's behavior and parameters, see the API documentation for the MASS
simulator.
Developing New Script Objects
Note that you only have to read this section if you've gone over all
the above material and the complete list of existing scripting
objects, and decided that you still can't create a script that meets
your needs.