The Basic AgeS Classes
The ages package hierarchy
- agent (package)
- effectors (package)
- CameraEffector.java
- DigitalOutEffector.java
- GripperEffector.java
- HeadMotorEffector.java
- MotorEffector.java
- SimEffector.java
- sensors (package)
- AnalogPortSensor.java
- AuxPortSensor.java
- BatterySensor.java
- BumperSensor.java
- CameraSensor.java
- CompassSensor.java
- DigitalInSensor.java
- IRSensor.java
- MotorSensor.java
- PositionSensor.java
- SonarSensor.java
- VelocitySensor.java
- pioneer (package)
- effectors (package)
- PioneerCameraEffector.java
- PioneerDigitalOutEffector.java
- PioneerMotorEffector.java
- sensors (package)
PioneerBatterySensor.java
- PioneerBumperSensor.java
- PioneerCameraSensor.java
- PioneerCompassSensor.java
- PioneerDigitalInSensor.java
- PioneerMotorSensor.java
- PioneerSonarSensor.java
- PioneerAgent.java
- PioneerInterface.java
- Agent.java
- Agent.java
- BasicEffector.java
- BasicSensor.java
- Brain.java
- Effector.java
- Sensor.java
Agent.java
This class defines the basic agent model, which consists of sensors,
effectors, and a brain. Agents are created by AgeS servers that know
how to fill in information about sensors and effectors. Brains,
on the other hand, are created by users and must be derived from
Brain.java. Agents have their own thread that updates them
periodically. This thread will also wake up the Brain's update
thread and consequently causes the Brain to update. Note that
the updateAgent function is not implemented in the base class.
package agent;
import agent.Brain;
import java.util.*;
abstract public class Agent extends Observable implements Runnable, Observer {
protected Vector mySensors; // the sensors of the agent
protected Vector myEffectors; // the effectors of the agent
protected Brain myBrain; // the brain of the agent
protected int update_rate; // the frequency with which updateAgent is called
protected Thread t; // the thread used for periodic updates
// dummy constructor
public Agent() {
this(100,null); // default update is every 100 msec.
}
public Agent(Brain myBrain) {
this(100,myBrain); // default update is every 100 msec.
}
// this just creates the data structures...
public Agent(int update_rate,Brain myBrain) {
this.update_rate = update_rate; // store the update rate
mySensors = new Vector(); // create vectors to hold sensors
myEffectors = new Vector(); // and effectors
this.myBrain = myBrain; // and store the brain
}
// this will have to be overwritten in each particular agent...
// it initializes the particular sensors, effectors, and brain
abstract protected void initializeSEB();
// need to start the agent thread explicitly
public void startAgent(Brain br) {
myBrain = br;
startAgent();
}
// this will start the agent thread, and in turn start the brain thread...
public void startAgent() {
initializeSEB();
t = new Thread(this,"Agent_thread");
t.start();
}
// the run-method, which will periodically update the brain
// and the agent itself if necessary
public void run() {
while (true) {
try {
Thread.sleep(update_rate);
} catch (InterruptedException e) { }
myBrain.updateBrain();
updateAgent(); //must be overwritten by the specific agent
// this notifies the observer of this agent...
setChanged();
notifyObservers(); //from notifyObservers(value)
}
}
// this could be used to implement the various
// sensors and effectors...
// if the agent itself needs to observe their states, for example
public void update(Observable obj, Object observed) {
}
// overwrite this method for period tasks within the agent class
public void updateAgent() {
}
}
Brain.java
This class defines the container class for agent control systems,
i.e., for the implementation of agent architectures. In here,
use will put the control code that needs to be executed
periodically. Users need to implement the udateMyself
method in their derived Brain class. This method is called
periodically by the Agent. Independent of this periodic update,
brains get "notified" when their sensors or effectors pick up
new values (in which case their update method will be
called). Note that this mechanisms requires "polling mode" on
the sensor side (and should not be used for extensive
processing)--see the section on sensors.
abstract public class Brain extends Observable implements Observer, Runnable {
protected Thread t; // the thread for brain update
protected Vector mySensors; // the vector to hold sensors
protected Vector myEffectors; // the vector to the effectors
public Agent robot; // the reference to the agent of which this is the brain
// just initialize the necessary parts
public Brain() {
t = new Thread(this,"Brain-thread");
}
// this really intializes the brain, it registers all the sensors
// and effectors, should be called after the respective function is called
// in the derived brain class...
public void initializeBrain(Vector mySensors, Vector myEffectors) {
this.mySensors = mySensors;
this.myEffectors = myEffectors;
// register the brain as observer to sensors and effectors
for (int i = 0; i < mySensors.size(); i++)
((Observable)mySensors.elementAt(i)).addObserver(this);
for (int i = 0; i < myEffectors.size(); i++)
((Observable)myEffectors.elementAt(i)).addObserver(this);
t.start();
}
// this just notifies the thread to do the update
public synchronized void updateBrain() {
notify();
}
// this will be called when sensors and effectors change,
// has to be implemented, since this class implements observer
void update(Object obj, Object arg) {
}
// has to be implemented: the actual brain update
abstract protected void updateMyself();
// run forever and send a command to the agent
// when you are notified, notify observer if error
// occurred...
public void run() {
while (true) {
try {
synchronized(this) {
wait();
}
} catch (InterruptedException e) { } // do not react to interruptions
updateMyself(); // this will update the brain...
setChanged(); // notify anybody watching the brain
notifyObservers();
}
}
}
Sensor.java and BasicSensor.java
These classes define the basic sensor interface to the agent. Every
sensor has to implement the Sensor interface for it to qualify as a
sensor, and to extend BasicSensor to be able to poll sensory
information automatically. Use getData() to get the latest data for
this sensor (and note that the format for this data will depend on
what kind of sensor it is). There are several subinterfaces defined
for different kinds of sensors, such as
public interface Sensor {
}
abstract public class BasicSensor extends Observable implements Sensor, Runnable, Serializable {
private int pollingRate = 500; // the rate in millisec. at which the sensor is polled
private boolean polling; // polling mode
protected AgeSServer server;
private Thread t; // the thread for the sensor polling
private boolean threadSuspended=true; // keeps track of whether polling is active
protected Object data = null; // the string representation of the return value
public BasicSensor() {
this(true);
}
public BasicSensor(boolean polling) {
this.polling = polling;
}
public void setPollingRate(int rate) {
pollingRate = rate;
}
public int getPollingRate() {
return pollingRate;
}
// This needs to be called before anything is done with the sensor. The function is defined
// in order to allow sensors to be initialized serverside, but run clientside
public void startSensor() {
t = new Thread(this,"Sensor-thread");
t.start();
}
// stop polling mode
public synchronized void deactivateSensor() {
threadSuspended = true;
notify();
}
// start polling mode
public synchronized void activateSensor() {
threadSuspended = false;
notify();
}
// run forever and poll the sensor at the given rate
public void run() {
Object temp=null;
while (true) {
// wait till you are activated
//System.err.println( "here" );
try {
synchronized(this) {
while (threadSuspended)
wait();
}
} catch (InterruptedException e) {} // do not react to interruptions
// System.err.println( "here2" );
try {
temp = getData();
} catch (Exception e) {
System.err.println( e );
}
if (!data.equals(temp)) {
setChanged();
notifyObservers();
data = temp;
}
// now go to sleep
try {
t.sleep(pollingRate);
} catch (InterruptedException e) {} // do not react to interruptions
}
}
// this returns the data of this sensor, has to be implemented by the derived classes
// and will eventually get information directly from the server
abstract public Object getData();
}
BasicEffector.java
These class defines the basic effector interface. The mechanism here
is analogous to the sensor interface, except that effectors do not
need to poll. Instead, their thread is used to send the command
(i.e., it will contact the server, send the command and wait for the
server's acknowledgment--during this process no new commands can be sent);
public interface Effector {
}
abstract public class BasicEffector extends Observable implements Effector, Serializable, Runnable {
private Object command; // the command string for the robot
private boolean simulation; // real or simulated sensor
protected AgeSServer server;
private Thread t; // the thread for the sensor polling
private boolean threadSuspended=false; // keeps track of whether polling is active
// THIS SHOULD BE MOVED TO INDIVIDUAL EFFECTORS - e.g. Trilobot
private boolean sentCommand = true;
// no client
public BasicEffector() {
this(true);
}
public BasicEffector(boolean simulation) {
this.simulation = simulation;
}
// This should be called on Brain start.
public void startEffector() {
t = new Thread(this,"Effector-thread");
t.start();
Thread.yield();
}
////////////////////////////////////////////
// activate command
//////////////////////////////////////////
public synchronized void activateEffector(Object command) {
while (!sentCommand) {
try {
notifyAll(); //System.err.println( "Notified in activate" );
wait();
} catch(InterruptedException e) {}
}
sentCommand = false;
this.command = command;
//System.err.println( "Activated command: " + command);
notifyAll();
//System.err.println( "Notified at end of activate" );
}
abstract public void sendCommand();
// run forever and send a command to the agent
// when you are notified, notify observer if error
// occurred...
public void run() {
Object temp = null;
synchronized(this) {
while (true) {
try {
wait();
} catch (InterruptedException e) {
System.err.println( "Interupted"); } // do not react to interruptions
if (simulation)
System.out.println("NOT YET IMPLEMENTED");
else
sendCommand();
// notify observer if error occurred
//synchronized(this) {
sentCommand = true;
notifyAll();
}
}
}
}
The Pioneer Setup
There is a general PioneerInterface defined for the Pionner Server,
which specifies the functionality required of any server that can
serve a Pioneer robot.
public interface PioneerInterface extends Remote {
public short getMotor() throws RemoteException;
public short getXPos() throws RemoteException;
public short getYPos() throws RemoteException;
public short getTh() throws RemoteException;
public short getLeftVelocity() throws RemoteException;
public short getRightVelocity() throws RemoteException;
public short getBattery() throws RemoteException;
public short getSB() throws RemoteException;
public short getControl() throws RemoteException;
public short getPTU() throws RemoteException;
public short getCompass() throws RemoteException;
public short getSonar(int w) throws RemoteException;
public short getTimer() throws RemoteException;
public short getAnalog() throws RemoteException;
public short getDigin() throws RemoteException;
public short getDigout() throws RemoteException;
public short getChecksum() throws RemoteException;
public void updateRobot() throws RemoteException;
public void sendClose() throws RemoteException;
public void sendSeto() throws RemoteException;
public void sendStop() throws RemoteException;
public void sendEstop() throws RemoteException;
public void sendStep() throws RemoteException;
public void sendPlaylist() throws RemoteException;
public void sendPolling(byte stringBuffer[]) throws RemoteException;
public void sendSay(byte stringBuffer[] ) throws RemoteException;
public void sendTty2(byte stringBuffer[] ) throws RemoteException;
public void sendSeta(short arg) throws RemoteException;
public void sendSetv(short arg) throws RemoteException;
public void sendSetrv(short arg) throws RemoteException;
public void sendVel(short arg) throws RemoteException;
public void sendHead(short arg) throws RemoteException;
public void sendDhead(short arg) throws RemoteException;
public void sendConfig(short arg) throws RemoteException;
public void sendEncoder(short arg) throws RemoteException;
public void sendRvel(short arg) throws RemoteException;
public void sendDchead(short arg) throws RemoteException;
public void sendSetra(short arg) throws RemoteException;
public void sendSonar(short arg) throws RemoteException;
public void sendDigout(short arg) throws RemoteException;
public void sendVel2(short arg) throws RemoteException;
public void sendGripper(short arg) throws RemoteException;
public void sendAdsel(short arg) throws RemoteException;
public void sendGripperval(short arg) throws RemoteException;
public void sendGriprequest(short arg) throws RemoteException;
public void sendPtupos(short arg) throws RemoteException;
public void sendGetaux(short arg) throws RemoteException;
public void sendBump_stall(short arg) throws RemoteException;
public void sendTcm2(short arg) throws RemoteException;
public void sendE_stall(short arg) throws RemoteException;
public void sendEnable(short arg) throws RemoteException;
}
However, for our purposes, it suffices to know what kinds of sensors
and effectors a Pioneer agent has, as we are only accessing them
through the agent.
PioneerAgent.java
public class PioneerAgent extends Agent implements Serializable {
// this will do all the communication
String ipAddress = "";
private int pollingRate;
private boolean polling;
///////////////
// Sensors
///////////////
// public AnalogPorts myAnalogPorts;
// public AuxPortSensor myAuxPortSensor;
public PioneerBatterySensor myBatterySensor;
public PioneerBumperSensor myBumperSensor;
public PioneerCompassSensor myCompassSensor;
public PioneerDigitalInSensor myDigitalInSensor;
//public DigitalOut myDigitalOut;
public PioneerMotorSensor myMotorSensor;
// public Position myPosition;
public PioneerSonarSensor mySonarSensor;
public CameraSensor myCameraSensor;
// public SoundIn mySoundIn;
// public Velocity myVelocity;
///////////////////
// Effectors
//////////////////
//public AnalogEffector myAnalogEffector;
//public AuxPortEffector myAuxPortEffector;
//public CameraMotor myCameraMotor;
//public SoundOut mySoundOut;
public PioneerMotorEffector myMotorEffector;
public PioneerDigitalOutEffector myDigitalOutEffector;
public CameraEffector myCameraEffector;
// public EStop myEStop;
// default is always simulation mode
public PioneerAgent() {
this(true,true,100,null); // no brain, can happen :)
}
public PioneerAgent(Brain myBrain) {
this(true,true,100,myBrain);
}
public PioneerAgent(boolean simulation,Brain myBrain) {
this(simulation,true,100,myBrain);
}
public PioneerAgent(boolean simulation,boolean polling,int pollingRate,Brain myBrain) {
super(pollingRate,myBrain); // same polling rate for brain for now...
this.simulation = simulation;
this.polling = polling;
this.pollingRate = pollingRate;
}
public void initializeSensors(Vector v) {
mySensors = v;
}
public void initializeEffectors(Vector v) {
myEffectors = v;
}
// this is the initialization procedure that must be implemented
protected void initializeSEB() {
if (myBrain != null)
myBrain.initializeBrain(mySensors,myEffectors);
}
}
This page is maintained by:
Matthias Scheutz
Copyright © Matthias Scheutz, 2003
University of Notre Dame
All rights reserved.
Last revised on February 10, 2003