Eisification

EISifying an Environment

Implementing an EIS interface for an environment is called EISifying (pronounce as: ahys-uh-fahy). EIS provides a specific set of methods that facilitate the interaction of a software agent with an environment, each of which needs to be implemented in order to make sure that an environment is EIS-enabled. Releases of EIS also provide a default implementation. Although you do not need to use it, we advise you to do so. It makes life a lot easier. The specification of actions and percepts in the EIS interface can be done using annotations since EIS v0.3, using the EIS2Java package that has been integrated into this release.

This page provides an implementation guide for EISifying an environment. If you use EIS, we ask you to contribute your environment to eishub so others can use your work as well. You can develop your EIS interface in a dedicated repository on eishub that we can make available for you; please contact @eishub/the-eis-team if you have any questions.


Notes:

  1. The guide below applies to EIS v0.2 and does not yet make use of the annotations feature. We still need to update it to v0.3, but the guide below can be used with v0.3 as well. When using EIS v0.3 the preferred approach is to extend AbstractEnvironment instead of EIDefaultImpl. See for an example of how to use EIS2Java the BWEnvironment class on eishub.

  2. The guidelines below are not intended to replace the EIS Guide available on the release page. The EIS Guide provides useful guidelines and detailed information about, e.g., generating EIS exceptions.

  3. Below, we assume that the code base for the environment is written in Java; if that is not the case, the environment needs to be wrapped.


Get the Environment Interface Jar

For developing an interface for your environment, you need the interface. You can find releases of the interface here or in the maven repository on eishub. Add the jar as a dependency to your environment project. This should something similar to this;

<dependencies>
    <dependency>
        <groupId>eishub</groupId>
        <artifactId>eis</artifactId>
        <version>0.4</version>
    </dependency>

    ...

</dependencies>

Maven is able to download all of your dependencies automatically. Most clients offer the option 'Download Sources', which will download the JAR files of all the dependencies known. Keep in mind that EIS is not hosted in the central Maven repository.

Creating the TestInterface class

The first step is to create a class called TestInterface that extends AbstractEnvironment as follows. Of course, you can change the name of this class if you like; but remember to also then modify TestInterface whenever you copy code snippets listed below. We kindly ask you to not use something like Environment in the name of your classes, this will prevent us from having long and unclear naming.

Insert the following code into the TestInterface class:

import eis.*;

public class TestInterface extends AbstractEnvironment [implements XXX] {

    variables ...

    constructor ...

    /* Support functions */
    ...

    /* EIS v0.4 interface functions */
    ...

}

The variables refer to private variables of the interface that typically manage parts of the environment. The constructor of the interface can perform part of the setup of an environment depending on the environment.

Note: The constructor can set up part of the environment but should not call the setStatemethod of EIS. If you call setState in the constructor, the selected state will not be passed properly to the listeners of an agent platform because these can be subscribed to the environment only after it has been constructed. Setting the state of the environment should be done by means of the init method.

Percepts

As of EIS v0.4 Percepts can be handled easily using EIS2Java. EIS2Java enables you to use annotations to define your Percepts. To improve readibility and maintainability you might want to create a seperate Entity class. The Entity should be able to access to control the Environment, so you will need to hook them to eachother somewhere. In the snippet below the Environment itself was provided to the constructor of the Entity.

public class Entity {

    private <classname> model;

    [other variables]

    /**
     * Constructor method.
     */
    public Entity(<classname> model) { this.model = model; }

    [percepts/actions/other methods here]
}

If we now want to send information from the contrallable Entity through Percepts, we need to define a new Percept. Every cycle of the Agent platform, all the information will be gathered and send through Percepts.

/**
 * Percept: Example, Sends a bunch of numbers.
 */
@AsPercept(name = "nums", multiplePercepts = true, multipleArguments = true)
public List<List<Integer>> nums() {

    // Resulting list.
    List<List<Integer>> list = new ArrayList<List<Integer>>();

    [ ... fill the lists with information ...]

    // Return the list with information
    return list;
} 

Caution: Be really carefull when you add data to your lists. Always make sure NullPointerException can't occur (by testing the values before adding them for instance). Uncautious use can cause your Interface to break.

The Annotation AsPercept works as follows;

  • name defines the name of the percept.
  • multiplePercepts indicates that multiple Percepts will be generated within the method.
  • multipleArguments indicates that the Percept(s) will have multiple arguments.

The last 2 options are false by default. The snippet above could result in multiple percepts all with multiple arguments. To visualize this a bit;

nums( 1, 3 )
nums( 6, 8 )
nums( 6, 0 )
.. 

You can also choose to send only a single value, in that case you will need to change the signature of the method accordingly to return only an intString, etc. These lists will be converted as needed by EIS2Java, which supports most common datatypes.

The getState Method

You must call setState(EnvironmentState.XX) to switch to another running state. Failure to do so may cause an agent platform to assume that an environment is in a different state than it actually is.

In some environments the environment state can only be polled, but no event/notification occurs when the state changes. In order to make such an environment work with an agent platform, you will have to modify the environment code. Overriding getState() to do the polling will not work as an agent platform that does not poll the state only reacts to state change events.

Actions

As of EIS v0.4 you can specify actions available to the Environment using Annotations. Again, it might be useful to put the Actions, together with the Perepts, in a seperate Entity class. An example is shown below;

/**
 * Action: Example, provides the ability to execute an action.
 */ 
@AsAction(name = "doSomething")
public void doSomething() {
    model.executeAssociatedAction();
}

To supply arguments you can specify them in the signature of your method.

Caution: Make sure to verify all conditions to execute an action are met.

Throwing Exceptions from an Action

  1. Do not throw any RuntimeException (eg, NullpointerExceptionThreadDeathException, etc), because otherwise EIS will throw an AssertionError. Therefore catch all RuntimeExceptionswith this wrapper

    public void doSomething() { try { .... } catch (RuntimeException e) { throw new ActException(ActException.FAILURE, "internal error while executing action " + action.toProlog(), e); } }

  2. When you throw ActException(), make sure you set type="FAILURE". Any other value will cause EIS to throw another Exception.

Normally, RuntimeExceptions are rare and do not deserve special attention. However RuntimeExceptions are likely to occur during takedown of the environment, particularly when the user kills the environment hard with its close button.

Environment management functionality

You need to @Override the start()pause()kill() and init() methods. Keep in mind that a GUI for the environment itself may also provide buttons for starting/running, pausing, terminating/killing, and/or initializing. The following code takes this into account:

...
/* support functions */
...

void doPause() {
    ...
    setState(EnvironmentState.PAUSED);
}
...
/* EIS 0.3 interface functions */
...
/**
 * {@inheritDoc}
 */

@Override
public void pause() throws ManagementException {
    doPause();
}
...

Events like pausing and starting an environment can also be generated because of user interaction with an environment, it is important to ensure these events are generated whenever the environment is actually started, paused, or e.g. killed. Only in this case an agent platform is able to correctly report the status of the environment. The code above deals with this by calling the function doPause() in the setState method. The doPause() method is assumed to do everything needed to pause the environment, whether pausing is triggered via EIS method calls or via user interaction with buttons on the environment's GUI.

Environment Initialization

The init method takes a set of key-value pairs. EIS enforces the initialization to contain a list of key-value pairs where

  • a key is a String
  • a value can be any eis.iilang.Parameter.

We recommend to define an InitKey enumeration, like this:

enum InitKey {
    FILE, UNKNOWN;
    static InitKey toKey(String key) {
        try {
            return valueOf(key.toUpperCase());
    } catch (Exception ex) {
        return UNKNOWN;
    }
}

and use this code fragment to determine how to unwrap the keys in the init() method

switch (InitKey.toKey(key)) {
case FILE:
    if (!(p instanceof Identifier)) {
    throw new ManagementException("...")
.....
break;
default:
    throw new ManagementException("Init key " + key + " unknown.");
}

Parsing EIS Parameters

We advise that you use the following syntax for EIS Parameters (IdentifierNumeralFunctionParameterList): For the Numeral, we roughly followed the Java Double convention and the recommended regular expression for it (see [http://java.sun.com/j2se/1.5.0/docs/api]). The differences with Java Double specifications are:

  • we do not accept the "NaN", "Infinity".
  • we do not accept the float and double suffixes.
  • Optional leading "whitespace" is not accepted.
  • Hexadecimal notation is not accepted.

Note the "ANYTHING NOT alternatives" notation which means that any terminal not equal to one of given alternatives is acceptable. '\n' is the newline character. '\r' is the carriage return character.

Parameter ::= Identifier | Numeral | Function | ParameterList
parameters ::= Parameter ["," parameters]

Identifier ::= ID | path 
ID ::= ("a".."z"|"A".."Z"|"_") {"a".."z"|"A".."Z"|"_"|"0".."9"}
path ::= '"' {ANYTHING NOT '\n' | '\r' | '"' } '"'

Numeral ::= ( ["+" | "-"] digits '.'? digits? exp? ) | ( '.' digits exp? ) 
exp ::= ("e" | "E") ["+" | "-"] digits
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
digits ::= digit {digit}

Function ::= ID "(" parameters ")"

ParameterList ::= "[" [parameters] "]"

When a path is converted into an Identifier, the initial and final double quote (") are removed. Numerals are attempted to parse with the Java Integer.valueOf() parser. If that succeeds the Numeral is set to the parsed integer. Otherwise the Numeral is parsed using Double.valueOf()TODO: SEEMS WE HAVE AN ISSUE HERE WE SHOULD LOOK AT

Typical initialization sequence

Most environments offer a panel to adjust all kind of settings, like the number of floors and cars (entities), properties of a car, simulation speed, etc. This typically results in the following mechanism:

  • The init method passes the preferred values to the environment.
  • The environment stores the values.
  • The environment then checks if the init method provided all values. If so, it proceeds with environment initialization; otherwise, the environment fetches default values for the missing settings.
  • The environment performs the initialization. It also creates the entities in the environment and sets up necessary connections.
  • By default, it is advised to start an environment in paused mode.

Register Entities

Whenever an entity such as a bot in a game or gripper in, e.g., the Blocks World, is made available by the environment, this entity must be registered! You can use the following code to do so:

try {
this.addEntity(<INSERT AN ENTITY NAME HERE>);
} catch (EntityException e) {
    ....
}

Release Version of EIS

The method requiredVersion should be implemented as follows (assuming that EIS v0.4 is used):

public String requiredVersion() {
return "0.4";
}

Environment Name

Implement the toString method to ensure the proper name is provided for your environment.

/**
 * Provides name of the environment.
 * 
 * @return name of the environment.
 */
@Override
public String toString() {
return "<PROVIDE ENVIRONMENT NAME HERE>";
}

Terminating an Environment

An environment can typically be terminated in a number of different ways:

  1. A user can close the environment GUI (which sometimes means the environment itself should be terminated as well).

  2. A user can press or select a Quit button or menu item provided in the environment GUI.

  3. The agent platform can request termination using the kill() method.

Your code may still have bugs but normally exceptions will be caught in EIS, wrapped and passed back to the platform manager to report it to the user. More troublesome (for the user) is when the environment management functions (init, start, stop, pause, kill) have bugs, particularly when the environment can not be closed anymore. It really helps the user if you can make these robust even after some detail inside your environment failed.