The Basic AgeS Classes

The ages package hierarchy

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