/* * Last change: $Date: 2004/07/11 22:26:27 $ * $Revision: 1.30 $ * * Copyright (c) 2004, The Black Sheep, Department of Computer Science, The University of Auckland * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of The Black Sheep, The Department of Computer Science or The University of Auckland nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package rescuecore; import java.io.*; import rescuecore.debug.*; import rescuecore.commands.*; /** This is the base class for all agents. This class handles messages from the server, provides a memory of the simulation environment and convenience methods for path planning etc. This class also enforces the message limits imposed by the robocup rescue rules.
Agent implementations should provide at least one of the following three constructors:
For example, assuming we have the three constructors mentioned above, if the command line provides two arguments then the AgentSystem will use the MyAgent(String arg1, String arg2) constructor. If only one argument is provided then the MyAgent(String[] args) constructor is used.
*/
public abstract class Agent extends RescueComponent {
private int[] agentTypes;
protected int type;
protected int id;
protected int timeStep;
protected Memory memory;
/*
private int numReceived, numSent;
private int sendMax,receiveMax;
*/
private int tempID;
private volatile boolean running;
private static int NEXT_ID = 0;
// private LogWriter logWriter;
// private File logFile;
// private int lastLogTime = -1;
protected boolean debug = false;
/**
Create a new agent of a particular type.
@param types The entity types this agent wants.
*/
protected Agent(int... types) {
this.agentTypes = types;
id = -1;
timeStep = -1;
this.type = -1;
// numReceived = 0;
// numSent = 0;
tempID = ++NEXT_ID;
// tempID = (int)(Math.random()*Integer.MAX_VALUE);
}
public final int getComponentType() {
return RescueConstants.COMPONENT_TYPE_AGENT;
}
public final Command generateConnectCommand() {
return new AKConnect(0,tempID,getClass().getName(),agentTypes);
}
protected void appendCommand(Command c){
super.appendCommand(c);
if(debug)
logObject(c);
}
public final boolean handleConnectOK(Command c) {
KAConnectOK ok = (KAConnectOK)c;
int requestID = ok.getRequestID();
if (requestID==tempID) {
id = ok.getAgentID();
System.out.println("Connect succeeded for "+tempID+". Kernel assigned id:"+id);
try {
RescueObject[] knowledge = ok.getKnowledge();
// Initialise
initialise(knowledge);
// Send AK_ACKNOWLEDGE
RescueMessage ack = new RescueMessage();
ack.append(new AKAcknowledge(requestID, id));
sendMessage(ack);
}
catch (Exception e) {
System.out.println(e);
e.printStackTrace();
}
timeStep = 0;
running = true;
return true;
}
else {
System.out.println("Received a KA_CONNECT_OK for agent "+requestID+", but I'm listening for a reply for "+tempID);
}
return false;
}
public final String handleConnectError(Command c) {
KAConnectError error = (KAConnectError)c;
int requestID = error.getRequestID();
String reason = error.getReason();
if (requestID==tempID)
return reason;
else
System.out.println("Received a KA_CONNECT_ERROR ("+reason+") for agent "+requestID+", but I'm listening for a reply for "+tempID);
return null;
}
public boolean isRunning() {
return running;
}
public void shutdown() {
running = false;
}
public final void handleMessage(Command c) {
// System.out.println("Handling "+c);
switch (c.getType()) {
case RescueConstants.KA_SENSE:
handleSense((KASense)c);
break;
case RescueConstants.KA_HEAR:
case RescueConstants.KA_HEAR_SAY:
case RescueConstants.KA_HEAR_TELL:
handleHear(c);
break;
case RescueConstants.KA_CONNECT_OK:
if (running) {
// Someone obviously didn't get our AK_ACKNOWLEDGE
KAConnectOK ok = (KAConnectOK)c;
int requestID = ok.getRequestID();
System.out.println(this+" just received a KA_CONNECT_OK to "+requestID+" - my tempID is "+tempID);
if (requestID==tempID) {
int newID = ok.getAgentID();
System.out.println("Old ID: "+id+", new ID: "+newID);
id = newID;
RescueMessage ack = new RescueMessage();
ack.append(new AKAcknowledge(requestID, id));
sendMessage(ack);
}
}
break;
default:
handleOtherMessage(c);
break;
}
logObject(c);
}
protected void handleOtherMessage(Command c) {
System.out.println("Timestep "+timeStep+": "+this+" received a weird command: "+Handy.getCommandTypeName(c.getType()));
}
/**
Handle a KA_SENSE message
@param c The KA_SENSE Command object
*/
private void handleSense(Command c) {
// System.out.println("Last timestep ("+timeStep+") "+this+" sent "+numSent+" messages and received "+numReceived);
KASense sense = (KASense)c;
// numSent = numReceived = 0;
try {
int newTimeStep = sense.getTime();
if (newTimeStep < timeStep) System.err.println(this+" just moved back in time! It was timestep "+timeStep+" and now it's timestep "+newTimeStep);
if (newTimeStep > timeStep+1) System.err.println(this+" just skipped ahead in time! It was timestep "+timeStep+" and now it's timestep "+newTimeStep);
timeStep = newTimeStep;
memory.update(sense); // Update the memory
}
catch (Exception e) {
e.printStackTrace();
}
// long start = System.currentTimeMillis();
sense();
// long end = System.currentTimeMillis();
// System.out.println("Sense took "+(end-start)+"ms for "+this);
flushCommands();
// Flush the log
DebugWriter.flush(this);
}
/**
Handle a KA_HEAR (or KA_HEAR_SAY, or KA_HEAR_TELL) message
@param hear The KA_HEAR Command object.
@see RescueConstants#KA_HEAR
@see RescueConstants#KA_HEAR_SAY
@see RescueConstants#KA_HEAR_TELL
*/
private void handleHear(Command c) {
KAHear hear = (KAHear)c;
int toID = hear.getToID();
int fromID = hear.getFromID();
int length = hear.getLength();
byte[] msg = hear.getData();
byte channel = hear.getChannel();
// System.out.println(Handy.getCommandTypeName(type)+" received by "+id+" from "+fromID+" - have already accepted "+numReceived+" messages of "+receiveMax+" this timestep");
// if (willListenHear(fromID) && canListen()) {
// System.out.println("Hear from "+fromID+" to "+toID+" on channel "+channel);
hear(fromID,msg,channel);
// ++numReceived;
// }
}
/*
private boolean canListen() {
if (numReceived < receiveMax) return true;
System.err.println("WARNING: "+this+" tried to receive too many messages in timestep "+timeStep+" (maximum="+receiveMax+")");
return false;
}
*/
private RescueObject me() {
return memory.lookup(id);
}
public String toString() {
if (!running) return "Unconnected agent. Temporary ID: "+tempID;
return Handy.getTypeName(type)+" ("+id+")";
}
/**
Get this agents Memory
@return The agents Memory
*/
public final Memory getMemory() {
return memory;
}
/**
Get the type of RescueObject that this agent represents
@see RescueConstants#TYPE_CIVILIAN
@see RescueConstants#TYPE_CAR
@see RescueConstants#TYPE_FIRE_BRIGADE
@see RescueConstants#TYPE_FIRE_STATION
@see RescueConstants#TYPE_POLICE_FORCE
@see RescueConstants#TYPE_POLICE_OFFICE
@see RescueConstants#TYPE_AMBULANCE_TEAM
@see RescueConstants#TYPE_AMBULANCE_CENTER
*/
public final int getType() {
return type;
}
/**
Get this agent's unique id, assigned by the kernel
*/
public final int getID() {
return id;
}
/**
Enable debugging
@param file The File to log information to.
*/
protected void enableDebug(String target) {
debug = true;
DebugWriter.register(this,this.toString(),target);
}
/**
Disable debugging
*/
protected void disableDebug() {
debug = false;
}
/**
* Writes an Update to a RescueObject to the log.
**/
// public final void logUpdate(rescuecore.debug.Update upd){
// if (debug) logWriter.addUpdate(upd, id, timeStep);
// }
/**
* Writes a transient Object to the log.
**/
public final void logObject(Object obj){
// if (debug) logWriter.addObject(obj,id,timeStep);
if (debug) DebugWriter.logUserObject(this,obj,timeStep);
}
/**
Initialise this agent. Subclasses that override this method should invoke super.initialise(knowledge,self) at some point.
@param knowledge This agent's knowledge of the world
@param self The RescueObject describing this agent
*/
protected void initialise(RescueObject[] knowledge) {
memory = generateMemory();
for (int i=0;i