AFAS::EIS
From Agent Factory
This is lesson 9 of the AF-AgentSpeak language guide.
Contents |
Introduction
The Environment Interface Standard (EIS) is a recently proposed standard for linking agents to some environment. In terms of the standard, an environment is some physical or virtual space that the agents can inhabit. EIS proposes a standardised interface that the agents can use to interact with and control entities embedded within the environment. Additionally, EIS proposes a management interface that allows agents and/or some application to control and configure the overall environment (e.g. starting and stopping the environment, detecting newly created entities, ...).
To facilitate use of EIS environments with Agent Factory, a platform service has been developed. A Platform Service API has subsequently been developed to allow agents built using the Common Language Framework to interact with EIS environments.
This lesson explains how to use the support provided by Agent Factory with AF-AgentSpeak. To this end, the first couple of sections highlight the EIS APIs and describe how agents utilise those APIs, and the latter sections illustrate how to work with EIS through some example EIS environments.
The EIS API
The EIS API defines a set of actions and sensors that facilitate interaction with EIS environments. Given EIS functionality is separated into two functional parts: management functionality, and entity control functionality; we have decomposed the EIS API into 2 separate APIs - one for each functional part.
The EIS Manager API
This API provides support for monitoring and controlling EIS environments. It currently specifies 3 actions:
- setup(?serviceId, ?jarFile): This is the standard configuration action. It takes two arguments: the service identifier that was associated with the EISSerivce (the underlying platform service); and the jar file that contains the environment.
- getFreeEntities: This action queries the EIS interface to return a list of entities that have not been associated with an agent (hence are free). It generates a set of beliefs of the form: freeEntity(?name).
- getEntityType(?entity): This action queries the EIS interface to return the type of the entity specified (by name). It generates a set of beliefs of the form: entityType(?name, ?type).
Additionally, one sensor, called eisSensor is associated with the environment. This sensor generates a belief as to whether or not the API is connected to the EISService (this takes the form eis-state(?connected)). Additionally,it generates beliefs about any environment-level events that occur.
The API can be added to an agent using the following line of code:
module eis -> com.agentfactory.eis.EISManagerModule;
The EIS Control API
This API provides support for controlling entities within an EIS environment. It currently specifies 4 actions:
- setup(?serviceId): sets up the API and links it to the EISService associated with the given service identifier.
- registerAgent: registers the agent with the environment.
- associateWithEntity(?name): links the agent to the entity, which results in the agent receiving perceptions from the entity and being able to control the entity.
- perform(?action): this action sends the specified entity action to the EIS interface, which in turn, processes the action. In order to pass the entity action to the EIS layer, the action converts the AFL term into equivalent EIS structures. This is done automatically by the action (no configuration is needed).
Additionally, one sensor, known as the eisSensor, is included. This sensor generates beliefs based on received events (that are relevant to the associated entity) and by querying the entity for its current perceptions. Most beliefs generated are specific to the application, however, the following common beliefs are generated for all environments:
- registeredAgent(?name): belief that the agent is registered with EIS under the given name.
- associatedWithEntity(?name): belief that the agent is associated with the entity given by the name.
- setup(done): belief that the API is connected to the service.
The API is associated with an agent by adding the following line to the agent program:
module eis -> com.agentfactory.eis.EISControlModule;
EIS Agents
EIS applications typically include a number of agents that are responsible for controlling entities within the EIS environment. The basic structure of such an agent is described in the later subsection below. In addition to these "application agents", Agent Factory also includes an EIS Management Agent, which is responsible for configuring and monitoring the attached EIS environment. This second agent can be seen as an "infrastructure agent" as it is pre-written and does not need to be created by developers working with EIS environments (although the developer does need to give some configuration information). The next subsection introduces this agent and explains its internal workings.
The EIS Management Agent
The default use of the EIS Management API is as part of the EIS Management Agent. This agent is responsible for configuring and monitoring the attached EIS environment. Typically, it is expected that any EIS user will not need to modify this agent, but instead, they will simply use it as part of their deployment. In this section, we give the code for this agent. We do so for two reasons: so that the operation of this agent is well understood, and so as to illustrate how the EIS Manager API is used in practice.
#agent EnvironmentManager
module ams -> com.agentfactory.clf.library.AMSLibrary;
module eis -> com.agentfactory.eis.EISManagerModule;
+initialized : environment(?pid, ?jarfile) <-
ams.setup,
?params = [],
if (parameter(?x)) {
?params = list[ ?x | parameter(?x) ]
}
eis.setup(?pid, ?jarfile, ?params),
eis.getFreeEntities;
+eisState(paused) : ~eis(paused) & eisSupports(start) <-
eis.start;
+newEntity(?entity) : ~design(?entity, ?design) <-
eis.getEntityType(?entity),
foreach(entityType(?entity, ?entityType) & design(?entityType, ?design)) {
ams.create(?entity, ?design),
ams.resume(?entity)
};
+newEntity(?entity) : design(?entity, ?design) <-
ams.create(?entity, ?design),
ams.resume(?entity);
+freeEntity(?entity) : ~design(?entity, ?design) <-
eis.getEntityType(?entity),
foreach(entityType(?entity, ?entityType) & design(?entityType, ?design)) {
ams.create(?entity, ?design),
ams.resume(?entity)
};
+freeEntity(?entity) : design(?entity, ?design) <-
ams.create(?entity, ?design),
ams.resume(?entity);
The main responsibilities of this agent are:
- To configure the EIS Service for the relevant environment.
- To create the agents that will control the entities present in the environment.
The former of these responsibilities is handled via the initial rule, which simply sets up the EIS Manager API and the AMS API, and then queries the environment for any free entities.
The second responsibility involves creating agents to control any of the free entities identified by the free entities query, and also creating agents for any entities that are created as the environment evolves. In reality, these options represent two possible design strategies for developers of EIS environments, and so, must both be supported to ensure compliance.
The creation of agents to control free entities is further complicated by the typing system employed by EIS. This feature of EIS associates a string type with every entity. The manager agent is able to use the EIS Manager API to query the environment as to the entities type. Again, some environments specify types for entities, while others do not (EIS best practice seems to be that you should, but this is not enforced). As a result, the EIS Management Agent must be able to create entities either by name or by type.
The result of this is that there are 4 rules devoted to creating agents to control entities: 2 rules for dealing with free entities, and 2 rules for dealing with new entities. For both cases, one rule deals with creating agents based on the entity name, and one rule deals with creating agents based on the entity design. The rules dealing with the entity by name are written before those dealing with entity type as this gives precedence to name based mappings over type based mappings.
EIS Controller Agents
While the details of the entity controller agent programs will differ depending on the environment, the one common core plan rule is:
+initialized : name(?name) <-
eis.setup(eis),
eis.registerAgent,
eis.associateEntity(?name);
This rule sets up the API, registers the agent with the environment, and links the agent to an entity whose name matches the agents name.
Writing and Deploying an EIS Application
To write an application, you can use any (or no) Java IDE. If you choose to use the Agent Factory Eclipse Plugin, then all you need to do is create an "Agent Factory EIS Project" instead of an "Agent Factory Project", and implement the Main class shown below. The main difference between the two projects are:
- the EIS Project includes a reference to the version of EIS that is packaged with Agent Factory.
- the EIS Project creates an EIS specific Main class.
Should you wish to use a different (or no) IDE, all you need to do is make sure that the AF-Library.jar (downloadable from sourceforge.net) and the relevant EIS jar are included in the classpath.
Deploying an EIS application is actually more straightforwards than deploying basic Agent Factory applications. This is because much of the platform configuration is specified in a pre-written class entitled EISRunConfiguration. To launch an EIS environment, all that you need to do is write the following Main class:
public class Main {
public static void main(String[] args) {
Map<String, String> designs = new HashMap<String, String>();
designs.put("car", "elevator/Basic.aspeak");
new EISRunConfiguration("elevator", designs, "elevatorenv.jar").configure();
}
}
As can be seen, the EISRunConfiguration constructor takes 3 parameters:
- a platform name;
- a Map that associates entity names / types with agent designs; and
- a string specifying the location of the actual EIS environment.
The implementation of this class sets up the Agent Platform, loads the Agent Factory Debugger, and creates and initialise the EIS Management Agent.
Example: The Elevator Environment
The Elevator environment is one of the example environments that are provided as part of the basic EIS deployment. For details of this environment, please read the appropriate documentation. This section does not discuss how to build the best solution for this problem, instead, it focuses on demonstrating how to build an agent that will work an existing EIS environment, and in the process shows how the EIS Controller API is used to control an entities in that environment.
A Basic Elevator Agent
The design of the example agent utilizes two APIs:
- The EIS Control API: This API is used to link the agent to one of the free entities in the environment.
- The Queue API: This API is used to create 2 queues that are used to record button presses in the environment. The 'fButtonQueue' records button presses relating to calling an elevator (i.e. the button you need to press to call the elevator), and 'eButtonQueue' records the internal button presses for the particular elevator that the agent is controlling.
The modules are installed through the following code:
module eis -> com.agentfactory.eis.EISControlModule; module queues -> com.agentfactory.clf.interpreter.QueueAPI;
Next, the initialisation plan is used to link the agent to the free entity with the same name and to createe the two queues:
+initialized : name(?name) <-
eis.setup(eis),
eis.registerAgent,
queues.create(fButtonQueue),
queues.create(eButtonQueue),
eis.associateEntity(?name);
The next two rules record button presses in the corresponding queues:
+fButtonOn(?level, ?dir) : true <-
queues.enqueue(fButtonQueue, fButtonOn(?level, ?dir));
+eButtonOn(?level) : true <-
queues.enqueue(eButtonQueue, eButtonOn(?level));
Finally, the fourth rule implements the core lift control behaviour:
+associatedWithEntity(?name) : true <-
while (true) {
wait(~empty(fButtonQueue)),
foreach( front(fButtonQueue, fButtonOn(?level, ?dir)) ) {
queues.dequeue(fButtonQueue),
if (fButtonOn(?level, ?dir)) {
eis.perform(goto(?level, ?dir)),
wait(atFloor(?level)),
wait(doorState(closed)),
while (~empty(eButtonQueue)) {
foreach( front(eButtonQueue, eButtonOn(?l)) ) {
queues.dequeue(eButtonQueue),
if (?l > ?level) {
eis.perform(goto(?l, up))
} else {
eis.perform(goto(?l, down))
},
wait(atFloor(?l))
}
}
}
}
};
Basically, the agent monitors the fButtonQueue continuously. If an elevator has been called, then the agent takes the head item from the fButtonQueue (which is a recording of a detected elevator call) and checks that the button is still pressed. If it is, then the agent sends the lift to the specified floor. The agent waits until it reaches that floor, and then waits until the doors are closed. Once closed, it checks to see what buttons are pressed, and visits all the corresponding floors. Once there are no more buttons pressed, the lift checks the next external button press.
NOTE: The implementation above is not optimal, but rather is purely an example of how to develop agents for an EIS environment. At an individual level, there is no check on the ordering of the buttons. For example, if the lift is at level 5, and the internal button for level 8 is pressed before the one for level 7, then the lift will visit level 8 before level 7. Similarly, the agent does not work out the best order in which to visit the floors, nor does it collaborate with other elevator agents about which floors to visit.
Deploying the Example
To deploy (and debug) our basic elevator agent, we create the following Debugger Run Configuration:
public class Elevator {
public static void main(String[] args) {
Map<String, String> designs = new HashMap<String, String>();
designs.put("car", "elevator/Elevator.aspeak");
new EISDebugConfiguration("elevator", designs, "elevatorenv.jar").configure();
}
}
The second line of the main method associates entities of type "car" with the "elevator/Elevator.aspeak" agent program (this is the program we wrote in the prevous section). The third line creates and configures an EISDebugConfiguration. When you run the example, you must use the Elevator interface to create a new scenario and when prompted, you should use the GOAL Controller.
Example: The VacWorld Environment
VacWorld is another EIS-based Environment that has been developed internally by the Agent Factory team. The latest version of the environment description and the jar file can be found here. It is basically a 2-dimensional grid world that is inhabited by 4 cleaner agents who are responsible for cleaning up any dirt that they can find. A screenshot of VacWorld can be seen below:
Agents have 3 basic capabilities:
- movement: they can move forwards, left, right, or back either 1 space or multiple spaces.
- cleaning: they can activate their hoover and clean any dirt on the current square on which they are residing
- flashing: they can turn their light on or off
Additionally, agents can perceive their environment, and get beliefs about:
- their current square (whether or not it is dirty)
- the state of the 3 squares in front of them and the squares to their left and right
- the state of their light
- their current location
- their current list of tasks
A Basic VacWorld Agent
Below, we present code for a simple cleaner agent that is able to clean any dirt that the cleaner can see, but which stops when it can see no more dirt. We leave it to you to develop more complex solutions that take advantage of collaboration, memory, or other more advanced behaviours.
#agent VacBot #extends com.agentfactory.eis.EISAgent
#implements #partial @postInitialize <-
.nil;
+location(?x, ?y) : square(here, dust) <-
!clean,
!doMove;
+location(?x, ?y) : ~square(here, dust) <-
!doMove;
+!doMove : name(?name) <-
try {
!move
} recover {
.println(?name + " had problem moving."),
!doMove
};
+!move : square(forward, dust) <-
durative(eis.perform(move(forward)));
+!move : square(left, dust) <-
durative(eis.perform(move(left)));
+!move : square(right, dust) <-
durative(eis.perform(move(right)));
+!move : ~square(forward, obstacle) & ~square(forward, vac) <-
durative(eis.perform(move(forward)));
+!move : ~square(left, obstacle) & ~square(left, vac) <-
durative(eis.perform(move(left)));
+!move : ~square(right, obstacle) & ~square(right, vac) <-
durative(eis.perform(move(right)));
+!move : true <-
try {
durative(eis.perform(move(right)))
} recover {
!move
};
+!clean : true <-
eis.perform(light(on)),
durative(eis.perform(clean)),
eis.perform(light(off));
Deploying VacWorld
The deployment for VacWorld is basically the same as for Elevator World, with the exception that you have to reference the vacworld jar and link each entity in vacworld to an agent program. An example of this is given below:
public class VacWorld {
public static void main(String[] args) {
Map<String, String> designs = new HashMap<String, String>();
designs.put("vacbot", "vacworld/VacBot.aspeak");
new EISDebugConfiguration("vacworld", designs, "ita-2.0.2.jar").configure();
}
}
NOTE: For Eclipse Users, the recommendation is to place the environment jar in the project root as you can reference the jar by name only (as is done in the code above)'
