Instructions for writing entity and message sets
From RoboCup
Contents |
Instructions for writing entity and message sets
The kernel is written entirely in terms of Entities and Messages. These encode the objects in the world and the actions that agents can take respectively. In order to write a new set of entities or messages there are a number of classes that you need to understand and extend:
- rescuecore2.worldmodel.Entity
- rescuecore2.worldmodel.EntityFactory
- rescuecore2.worldmodel.EntityType
- rescuecore2.worldmodel.Property
- rescuecore2.worldmodel.PropertyType
- rescuecore2.messages.Message
- rescuecore2.messages.MessageFactory
A step-by-step example
It is probably easiest to illustrate the design by example. Suppose we want to add a new type of entity: the helicopter. We will need one new entity type and one new message type so agents can control the helicopter. We'll create a new module for this entity set. See Instructions_for_writing_modules for instructions on how to do create a new module.
The helicopter code
The first thing to do is define some constants for helicopters:
package helicopter;
/**
A set of constants for the helicopter package.
*/
public final class HelicopterConstants {
/**
The helicopter entity type ID.
*/
public static final int HELICOPTER_TYPE_ID = 0x0100;
/**
The helicopter x property type ID.
*/
public static final int HELICOPTER_PROPERTY_X = 0x0100;
/**
The helicopter y property type ID.
*/
public static final int HELICOPTER_PROPERTY_Y = 0x0101;
/**
The helicopter z property type ID.
*/
public static final int HELICOPTER_PROPERTY_Z = 0x0102;
/**
The helicopter fly command ID.
*/
public static final int HELICOPTER_FLY = 0x0100;
}
We can now write our Entity class. Helicopters will have three properties: their x, y and z coordinates which are all integers. We will extend the AbstractEntity class which does a lot of work for us.
package helicopter;
import rescuecore2.worldmodel.AbstractEntity;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.EntityType;
import rescuecore2.worldmodel.SimpleEntityType;
import rescuecore2.worldmodel.PropertyType;
import rescuecore2.worldmodel.SimplePropertyType;
import rescuecore2.worldmodel.properties.IntProperty;
/**
A Helicopter entity.
*/
public class Helicopter extends AbstractEntity {
private static final EntityType HELICOPTER_TYPE = new SimpleEntityType(HelicopterConstants.HELICOPTER_TYPE_ID, "Helicoptor");
// The three property types for helicopters
private static final PropertyType TYPE_X = new SimplePropertyType(HelicopterConstants.HELICOPTER_PROPERTY_X, "Helicopter X");
private static final PropertyType TYPE_Y = new SimplePropertyType(HelicopterConstants.HELICOPTER_PROPERTY_Y, "Helicopter Y");
private static final PropertyType TYPE_Z = new SimplePropertyType(HelicopterConstants.HELICOPTER_PROPERTY_Z, "Helicopter Z");
// The three properties for this instance of Helicopter
private IntProperty x;
private IntProperty y;
private IntProperty z;
/**
Construct a new Helicopter.
*/
public Helicopter(EntityID id) {
super(id, HELICOPTER_TYPE);
x = new IntProperty(TYPE_X);
y = new IntProperty(TYPE_Y);
z = new IntProperty(TYPE_Z);
}
@Override
public Helicopter copyImpl() {
return new Helicopter(getID());
}
/**
Get the x coordinate of this helicopter;
@return The x coordinate.
*/
public int getX() {
return x.getValue();
}
/**
Set the x coordinate of this helicopter.
@param x The new x coordinate.
*/
public void setX(int x) {
this.x.setValue(x);
}
/**
Find out if the x coordinate has been defined.
@return True if the x coordinate has been defined, false otherwise.
*/
public boolean isXDefined() {
return x.isDefined();
}
/**
Undefine the x coordinate.
*/
public void undefineX() {
x.undefine();
}
<same for Y and Z properties>
}
The Fly message is used to move helicopters around:
package helicopter;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.messages.IntComponent;
import rescuecore2.messages.AbstractCommand;
/**
A helicopter "fly" command.
*/
public class Fly extends AbstractCommand {
private IntComponent targetX;
private IntComponent targetY;
private IntComponent targetZ;
/**
Create an empty fly command.
*/
public Fly() {
super("FLY", HelicopterConstants.HELICOPTER_FLY);
init();
}
/**
Construct a fly command.
@param agent The ID of the agent issuing the command.
@param time The time the command was issued.
@param x The x coordinate to move to.
@param y The y coordinate to move to.
@param z The z coordinate to move to.
*/
public Fly(EntityID agent, int time, int x, int y, int z) {
super("FLY", HelicopterConstants.HELICOPTER_FLY, agent, time);
init();
targetX.setValue(x);
targetY.setValue(y);
targetZ.setValue(z);
}
/**
Get the desired x coordinate.
@return The x coordinate.
*/
public int getX() {
return targetX.getValue();
}
/**
Get the desired y coordinate.
@return The y coordinate.
*/
public int getY() {
return targetY.getValue();
}
/**
Get the desired z coordinate.
@return The z coordinate.
*/
public int getZ() {
return targetZ.getValue();
}
private void init() {
targetX = new IntComponent("Destination X");
targetY = new IntComponent("Destination Y");
targetZ = new IntComponent("Destination Z");
addMessageComponent(targetX);
addMessageComponent(targetY);
addMessageComponent(targetZ);
}
}
The HelicopterFactory generates new Helicopter and Fly objects:
package helicopter;
import rescuecore2.worldmodel.EntityFactory;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.Entity;
import rescuecore2.messages.MessageFactory;
import rescuecore2.messages.Message;
import java.io.InputStream;
import java.io.IOException;
/**
The HelicopterFactory implements both EntityFactory and MessageFactory.
*/
public class HelicopterFactory implements EntityFactory, MessageFactory {
/**
Singleton class with a shared instance.
*/
public static final HelicopterFactory INSTANCE = new HelicopterFactory();
/**
Singleton class: private constructor.
*/
private HelicopterFactory() {}
@Override
public Entity makeEntity(int typeID, EntityID id) {
if (typeID == HelicopterConstants.HELICOPTER_TYPE_ID) {
return new Helicopter(id);
}
else {
throw new IllegalArgumentException("Unrecognised entity type: " + typeID);
}
}
@Override
public int[] getKnownEntityTypeIDs() {
return new int[] {HelicopterConstants.HELICOPTER_TYPE_ID};
}
@Override
public Message createMessage(int id, InputStream data) throws IOException {
if (id == HelicopterConstants.HELICOPTER_FLY) {
Message result = new Fly();
result.read(data);
return result;
}
else {
throw new IllegalArgumentException("Unrecognised message type: " + id);
}
}
@Override
public int[] getKnownMessageTypeIDs() {
return new int[] {HelicopterConstants.HELICOPTER_FLY};
}
}
That's it! All the code is done. Now we need to edit the kernel configuration files to ensure that helicopter entities will be recognised in the system. In the boot/kernel.cfg file change the kernel.messages.factories and kernel.entities.factories entries as follows:
kernel.messages.factories: rescuecore2.standard.messages.StandardMessageFactory helicopter.HelicopterFactory kernel.entities.factories: rescuecore2.standard.entities.StandardEntityFactory helicopter.HelicopterFactory
Run ant start-kernel and the kernel should start. Later we will create a basic simulator that will process Fly commands and update the kernel world model.
