package rescuecore2.log; import static rescuecore2.misc.EncodingTools.readInt32; import static rescuecore2.misc.EncodingTools.readBytes; import rescuecore2.worldmodel.WorldModel; import rescuecore2.worldmodel.DefaultWorldModel; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.config.Config; import rescuecore2.registry.Registry; import java.util.Map; import java.util.HashMap; import java.util.Set; import java.util.HashSet; import java.io.InputStream; import java.io.IOException; import java.io.ByteArrayInputStream; /** An class for reading kernel logs from a stream. */ public class StreamLogReader extends AbstractLogReader { private int maxTime; private Map<Integer, CommandsRecord> commands; private Map<Integer, UpdatesRecord> updates; private Map<Integer, WorldModel<? extends Entity>> worldModels; private Map<Integer, Map<EntityID, PerceptionRecord>> perception; private Config config; /** Construct a StreamLogReader. @param in The InputStream to read. @param registry The registry to use for reading log entries. @throws LogException If there is a problem reading the log. */ public StreamLogReader(InputStream in, Registry registry) throws LogException { super(registry); commands = new HashMap<Integer, CommandsRecord>(); updates = new HashMap<Integer, UpdatesRecord>(); worldModels = new HashMap<Integer, WorldModel<? extends Entity>>(); perception = new HashMap<Integer, Map<EntityID, PerceptionRecord>>(); try { readLog(in); } catch (IOException e) { throw new LogException(e); } } @Override public Config getConfig() throws LogException { if (config == null) { throw new LogException("No config record found"); } return config; } @Override public int getMaxTimestep() throws LogException { return maxTime; } @Override public WorldModel<? extends Entity> getWorldModel(int time) throws LogException { checkTime(time); WorldModel<? extends Entity> result = worldModels.get(time); if (result == null) { result = DefaultWorldModel.create(); } return result; } @Override public Set<EntityID> getEntitiesWithUpdates(int time) throws LogException { checkTime(time); Map<EntityID, PerceptionRecord> agentData = perception.get(time); Set<EntityID> result = new HashSet<EntityID>(); if (agentData != null) { result.addAll(agentData.keySet()); } return result; } @Override public PerceptionRecord getPerception(int time, EntityID entity) throws LogException { checkTime(time); Map<EntityID, PerceptionRecord> agentData = perception.get(time); if (agentData == null) { return null; } PerceptionRecord result = agentData.get(entity); return result; } @Override public CommandsRecord getCommands(int time) throws LogException { checkTime(time); return commands.get(time); } @Override public UpdatesRecord getUpdates(int time) throws LogException { checkTime(time); return updates.get(time); } private void checkTime(int time) { if (time < 0 || time > maxTime) { throw new IllegalArgumentException("Time is out of range: " + time + " should be between 0 and " + maxTime); } } private void readLog(InputStream in) throws IOException, LogException { Registry.setCurrentRegistry(registry); int id; RecordType type; boolean startFound = false; do { id = readInt32(in); type = RecordType.fromID(id); if (!startFound) { if (type != RecordType.START_OF_LOG) { throw new LogException("Log does not start with correct magic number"); } startFound = true; } readRecord(type, in); } while (type != RecordType.END_OF_LOG); } private void readRecord(RecordType type, InputStream in) throws IOException, LogException { int size = readInt32(in); byte[] data = readBytes(size, in); InputStream d = new ByteArrayInputStream(data); switch (type) { case INITIAL_CONDITIONS: readInitialConditions(d); break; case PERCEPTION: readPerception(d); break; case COMMANDS: readCommands(d); break; case UPDATES: readUpdates(d); break; case CONFIG: readConfig(d); break; case END_OF_LOG: return; default: throw new LogException("Unexpected record type: " + type); } } private void readInitialConditions(InputStream in) throws IOException, LogException { InitialConditionsRecord record = new InitialConditionsRecord(in); worldModels.put(0, record.getWorldModel()); } private void readPerception(InputStream in) throws IOException, LogException { PerceptionRecord record = new PerceptionRecord(in); int time = record.getTime(); Map<EntityID, PerceptionRecord> agentData = perception.get(time); if (agentData == null) { agentData = new HashMap<EntityID, PerceptionRecord>(); perception.put(time, agentData); } agentData.put(record.getEntityID(), record); maxTime = Math.max(time, maxTime); } private void readCommands(InputStream in) throws IOException, LogException { CommandsRecord record = new CommandsRecord(in); commands.put(record.getTime(), record); maxTime = Math.max(record.getTime(), maxTime); } private void readUpdates(InputStream in) throws IOException, LogException { UpdatesRecord record = new UpdatesRecord(in); int time = record.getTime(); updates.put(time, record); // Make the world model for this timestep WorldModel<? extends Entity> newWorld = new DefaultWorldModel<Entity>(Entity.class); WorldModel<? extends Entity> oldWorld = getWorldModel(time - 1); if (oldWorld != null) { Set<Entity> copy = new HashSet<Entity>(); for (Entity next : oldWorld) { copy.add(next.copy()); } newWorld.merge(copy); } newWorld.merge(record.getChangeSet()); worldModels.put(time, newWorld); maxTime = Math.max(time, maxTime); } private void readConfig(InputStream in) throws IOException, LogException { ConfigRecord record = new ConfigRecord(in); config = record.getConfig(); } }