package mde.jini.service;
import mde.lego.*;
import java.io.*;
import java.rmi.*;
import java.rmi.activation.*;
import java.rmi.server.*;
import java.util.*;
import net.jini.core.event.*;
import net.jini.core.lease.*;
import net.jini.discovery.*;
import net.jini.core.lookup.*;
/**
* This class handles the low-level communication between the tanks
* and the host. It comes up in the sign-up state. In that state,
* the tank daemon sends out a broadcast message, inviting tanks
* to sign up for a game. Tanks respond by sending back their
* particular IDs. After the specified number of tanks have signed
* up, the tank daemon switches to the run state. In that state,
* it uses a slotted protocol to communicate with the tanks. For each
* slot, the tank daemon queries all tank proxies for commands to
* send to the tanks. For each tank, the command is sent (either a
* real command, or a dummy status command), and the tank hands back
* status. That status is forwarded back to the tank proxy. If there
* is no status feedback from the tank in a specified number of
* slots, that tank is presumed missing, and the run state will be
* aborted. The tank daemon goes back into sign-up state until the
* specified number of tanks are registered again.
*/
public class TankDaemonImpl extends Thread implements TankDaemon,
TankCommands,
DataReceiver,
DiscoveryListener {
private ActivationDesc desc;
private Brick brick;
private TankProxy[] tankProxy;
private String[] cmdlist = {"FORWARD", "BACKWARD", "LEFT", "RIGHT", "STOP", "FIRE", "STATUS", "HELLO"};
private Hashtable statuslist = new Hashtable();
private transient LookupDiscovery discovery;
private String[] groups;
private TankExpiredListener listener;
private static final int INTER_MESSAGE_DELAY = 200;
private ByteArrayOutputStream status_buf;
private final int STATUS_BYTE = 0;
private final int LEFT_BYTE = 1;
private final int RIGHT_BYTE = 2;
/** Create a new tank daemon instance */
public TankDaemonImpl(ActivationID id, MarshalledObject data) throws RemoteException {
Activatable.exportObject(this, id, 0);
try {
TankDaemonConfiguration config = (TankDaemonConfiguration) data.get();
tankProxy = new TankProxy[config.getPlayerCount()];
System.out.println("TankDaemonImpl(): seting up for " + config.getPlayerCount() + " players");
groups = config.getGroups();
} catch(Exception e) {
System.out.println("TankDaemonImpl(): exception caught retrieving activation data: " + e);
}
brick = new Brick();
brick.addDataReceiver(this);
status_buf = new ByteArrayOutputStream();
try {listener = new TankExpiredListener();}
catch(Exception e) {e.printStackTrace();}
statuslist.put(new Byte((byte)0), "OK");
statuslist.put(new Byte((byte)1), "OBSTRUCTED");
statuslist.put(new Byte((byte)2), "HIT");
setupTankActivation();
// Setup listener for Jini discovery events
try {
discovery = new LookupDiscovery(groups);
discovery.addDiscoveryListener(this);
} catch(IOException e) {
e.printStackTrace();
}
}
private void echoReceived(byte msg) {
int cmd = (msg & CMD_MASK) >> 3;
if (cmd < 0 || cmd >= cmdlist.length) {
// System.out.println("invalid cmd, msg was " + toHex(msg));
return;
}
if (cmd != HELLO && cmd != STATUS) {
System.out.println("TankDaemonImpl.echoReceived(): " + cmdlist[cmd]);
}
}
private void statusReceived(byte[] buf) {
byte id = (byte)((buf[STATUS_BYTE] & ID_MASK) - 1);
byte status;
if (id < 0 || id >= tankProxy.length) {
// System.out.println("invalid ID: " + id + ", complete msg was " + toHex(msg));
return;
}
synchronized (this) {
if (tankProxy[id] == null) {
tankProxy[id] = startTankProxy(id, groups);
System.out.println("created new tank proxy with ID " + id);
}
status = (byte)((buf[STATUS_BYTE] & STATUS_MASK) >> 3);
TankEvent te = new TankEvent(this, id, status,
(buf.length == 3 ) ? buf[LEFT_BYTE] : -1,
(buf.length == 3 ) ? buf[RIGHT_BYTE] : -1);
System.out.println(te);
try {tankProxy[id].updateStatus(te);}
catch (Exception e) {e.printStackTrace();}
}
if (status != 0) {
System.out.println("status for tank " + (id + 1)
+ " is " + statuslist.get(new Byte(status)));
}
}
public void dataAvailable(byte msg) {
// System.out.println("TankDaemonImpl.dataAvailable(): received " + toHex(msg));
if ((msg & ECHO_BIT) != 0) {
echoReceived(msg);
if (status_buf.size() > 0) statusReceived(status_buf.toByteArray());
status_buf.reset();
} else {
status_buf.write(msg);
}
}
/** Start the tank daemon thread */
public void run() {
long[] last_contact = new long[tankProxy.length + 1];
long last;
boolean idle;
byte msg;
int i, id, lru;
try {
for (;;) {
idle = true;
for (i=0; i= 0) synchronized (this) {
tankProxy[id].shutdown();
tankProxy[id] = null;
}
}
}
}