package kernel; import rescuecore2.connection.Connection; import rescuecore2.connection.ConnectionListener; import rescuecore2.messages.Message; import rescuecore2.messages.Command; import rescuecore2.messages.control.SKUpdate; import rescuecore2.messages.control.KSUpdate; import rescuecore2.messages.control.KSCommands; import rescuecore2.messages.control.EntityIDRequest; import rescuecore2.messages.control.EntityIDResponse; import rescuecore2.worldmodel.ChangeSet; import rescuecore2.worldmodel.EntityID; import rescuecore2.log.Logger; import java.util.Collection; import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.ArrayList; /** This class is the kernel interface to a simulator. */ public class SimulatorProxy extends AbstractKernelComponent { private Map<Integer, ChangeSet> updates; private int id; private EntityIDGenerator idGenerator; /** Construct a new simulator. @param name The name of the simulator. @param id The ID of the simulator. @param c The connection this simulator is using. */ public SimulatorProxy(String name, int id, Connection c) { super(name, c); this.id = id; updates = new HashMap<Integer, ChangeSet>(); c.addConnectionListener(new SimulatorConnectionListener()); } /** Get updates from this simulator. This method may block until updates are available. @param time The timestep to get updates for. @return A ChangeSet representing the updates from this simulator. @throws InterruptedException If this thread is interrupted while waiting for updates. */ public ChangeSet getUpdates(int time) throws InterruptedException { ChangeSet result = null; synchronized (updates) { while (result == null) { result = updates.get(time); if (result == null) { updates.wait(1000); } } } return result; } /** Send an update message to this simulator. @param time The simulation time. @param update The updated entities. */ public void sendUpdate(int time, ChangeSet update) { send(new KSUpdate(id, time, update)); } /** Send a set of agent commands to this simulator. @param time The current time. @param commands The agent commands to send. */ public void sendAgentCommands(int time, Collection<? extends Command> commands) { send(new KSCommands(id, time, commands)); } @Override public String toString() { return getName() + " (" + id + "): " + getConnection().toString(); } /** Set the EntityIDGenerator. @param generator The new EntityIDGenerator. */ public void setEntityIDGenerator(EntityIDGenerator generator) { idGenerator = generator; } /** Register an update from the simulator. @param time The timestep of the update. @param changes The set of changes. */ protected void updateReceived(int time, ChangeSet changes) { synchronized (updates) { ChangeSet c = updates.get(time); if (c == null) { c = new ChangeSet(); updates.put(time, c); } c.merge(changes); updates.notifyAll(); } } private class SimulatorConnectionListener implements ConnectionListener { @Override public void messageReceived(Connection connection, Message msg) { Logger.pushLogContext(Kernel.KERNEL_LOG_CONTEXT); try { if (msg instanceof SKUpdate) { SKUpdate update = (SKUpdate)msg; if (update.getSimulatorID() == id) { updateReceived(update.getTime(), update.getChangeSet()); } } if (msg instanceof EntityIDRequest) { EntityIDRequest req = (EntityIDRequest)msg; Logger.debug("Simulator proxy " + id + " received entity ID request: " + msg); if (req.getSimulatorID() == id) { int requestID = req.getRequestID(); int count = req.getCount(); List<EntityID> result = new ArrayList<EntityID>(count); for (int i = 0; i < count; ++i) { result.add(idGenerator.generateID()); } Logger.debug("Simulator proxy " + id + " sending new IDs: " + result); send(new EntityIDResponse(id, requestID, result)); } } } finally { Logger.popLogContext(); } } } }