Agent.java 12.9 KB
Newer Older
Juon Kawakami's avatar
init  
Juon Kawakami committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377

/*
 * 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.
   <p>Agent implementations should provide at least one of the following three constructors:
   <ol><li>A no-arg constructor - e.g. MyAgent()
   <li>A String[] constructor - e.g. MyAgent(String[] args)
   <li>A constructor that takes one or more String arguments - e.g. MyAgent(String arg1, String arg2)
   </ol>
   The reason for this is that the AgentSystem allows arguments to be passed to the Agent via the command line. When creating an instance of the agent it first looks for any constructor that accepts the right number of String arguments, followed by the String[] constructor. Failing that, the no-arg constructor will be used.
   <p>
   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<knowledge.length;++i) {
			memory.add(knowledge[i],0,RescueConstants.SOURCE_INITIAL);
		}
                type = me().getType();
		if(debug){
			DebugWriter.logInitialObjects(this,memory.getAllObjects());
			memory.addMemoryListener(new DebugMemoryListener(this));
		}
    }

    /**
       Construct a new Memory object for use by this Agent. This method allows Agents to customise their choice of Memory object. The default implementation returns a {@link HashMemory}.
       @return A new Memory object
	*/
    protected Memory generateMemory() {
		return new HashMemory();
    }

    /**
       Called after a KA_SENSE is received
	*/
    protected abstract void sense();

    /**
       Called after a KA_HEAR is received
       @param from The agent that sent the message
       @param msg The message body
	   @param channel The channel that this message was received on
    */
    protected void hear(int from, byte[] msg, byte channel){}
	//    protected boolean hearTell(int from, byte[] msg){return false;}
	//    protected boolean hearSay(int from, byte[] msg){return false;}
	//	protected boolean willListenHear(int from) {return false;}
	//	protected boolean willListenHearSay(int from) {return false;}
	//	protected boolean willListenHearTell(int from) {return false;}

    /**
       How many messages can this agent send per timestep?
       @return The maximum number of messages this agent can send per timestep
	*/
	//    protected final int getMaxSend() {
		//		return sendMax;
	//    }

    /**
       How many messages can this agent receive per timestep?
       @return The maximum number of messages this agent can receive per timestep
	*/
	//    protected final int getMaxReceive() {
	//		return receiveMax;
	//    }

    /**
       Send an AK_SAY message to the kernel. If this agent has already send too many messages this timestep then this will be silently ignored
       @param message The message
	*/
    protected final void say(byte[] message) {
		//		if (numSent < sendMax) {
		appendCommand(Command.SAY(id,timeStep,message,message.length));
		//			++numSent;
		//		}
    }

    /**
       Send an AK_TELL message to the kernel. If this agent has already send too many messages this timestep then this will be silently ignored
       @param message The message
	*/
    protected final void tell(byte[] message, byte channel) {
		//		if (numSent < sendMax) {
		appendCommand(Command.TELL(id,timeStep,message,message.length,channel));
		//			++numSent;
		//		}
    }
}