package iamrescue.belief; import iamrescue.agent.ISimulationTimer; import iamrescue.belief.provenance.IProvenanceInformation; import iamrescue.belief.provenance.IProvenanceStore; import iamrescue.belief.provenance.ProvenanceLogEntry; import iamrescue.belief.provenance.ProvenanceStore; import iamrescue.belief.provenance.SensedOrigin; import iamrescue.communication.messages.codec.property.PropertyEncoderStore; import iamrescue.routing.WorldModelConverter; import iamrescue.routing.costs.BlockCache; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javolution.util.FastSet; import org.apache.log4j.Logger; import rescuecore2.config.Config; import rescuecore2.standard.entities.Area; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.standard.entities.StandardEntityURN; import rescuecore2.standard.entities.StandardPropertyURN; import rescuecore2.standard.entities.StandardWorldModel; import rescuecore2.worldmodel.ChangeSet; import rescuecore2.worldmodel.Entity; import rescuecore2.worldmodel.EntityID; import rescuecore2.worldmodel.Property; public class IAMWorldModel extends StandardWorldModel implements IBuildingSearchUtility { // Set this to -1 if no time limit // private static final int PROVENANCE_MAX_ENTRIES = 10; private Map<StandardEntityURN, Collection<StandardEntity>> agentsByType; private IProvenanceStore provenance; private static final Logger LOGGER = Logger.getLogger(IAMWorldModel.class); // private Map<IDPair, EntityID> connectingRoadMap = new FastMap<IDPair, // EntityID>(); private ShortIDIndex shortIndex = null; private ISimulationTimer timer; private ChangeSet lastMergedChanges; private WorldModelConverter converter = null; private BlockCache blockCache; private BuildingSearchUtility buildingUtility; private Config config; private FastSet<EntityID> rescueEntities; private boolean initialisedSearch = false; private StuckMemory stuckMemory; public IAMWorldModel(ISimulationTimer timer, Config config) { super(); this.timer = timer; this.config = config; this.provenance = new ProvenanceStore(); blockCache = new BlockCache(timer); stuckMemory = new StuckMemory(); } public IAMWorldModel(ISimulationTimer timer) { this(timer, new Config()); LOGGER.warn("No configuration specified. Using empty."); } /** * @return the stuckMemory */ public StuckMemory getStuckMemory() { return stuckMemory; } public void setDefaultConverter(WorldModelConverter converter) { this.converter = converter; } /** * @return the blockCache */ public BlockCache getBlockCache() { return blockCache; } /** * @return the converter */ public WorldModelConverter getDefaultConverter() { return converter; } /** * This signals that initial information about the world has been merged. * Can add logic here to set up further information about static beliefs. */ public void postConnect() { buildingUtility = new BuildingSearchUtility(this, config, timer); rescueEntities = new FastSet<EntityID>(); Collection<StandardEntity> entities = getEntitiesOfType( StandardEntityURN.AMBULANCE_CENTRE, StandardEntityURN.AMBULANCE_TEAM, StandardEntityURN.FIRE_BRIGADE, StandardEntityURN.FIRE_STATION, StandardEntityURN.POLICE_FORCE, StandardEntityURN.POLICE_OFFICE); for (StandardEntity standardEntity : entities) { rescueEntities.add(standardEntity.getID()); } stuckMemory = new StuckMemory(); } /** * Checks if ID belongs to one of the rescue agents or centres. * * * @param id * ID to check * @return True iff agent or centre. */ public boolean isRescueEntity(EntityID id) { return rescueEntities.contains(id); } /** * Returns an index converter between EntityIDs and shorts. * * @return the shortIndex */ public ShortIDIndex getShortIndex() { if (shortIndex == null) { buildShortIndex(); } return shortIndex; } public IProvenanceInformation getProvenance(EntityID id, StandardPropertyURN property) { return provenance.getProvenance(id, property.toString()); } /** * Causes the short index to be built (or rebuilt). This should be called * after all initial objects have been received. */ public void buildShortIndex() { shortIndex = new ShortIDIndex(this); } /* * private static class IDPair { private int id1; private int id2; public * IDPair(int id1, int id2) { this.id1 = id1; this.id2 = id2; } * * @Override public int hashCode() { final int prime = 31; int result = 1; * result = prime * result + id1; result = prime * result + id2; return * result; } * * @Override public boolean equals(Object obj) { if (this == obj) return * true; if (obj == null) return false; if (getClass() != obj.getClass()) * return false; IDPair other = (IDPair) obj; if (id1 != other.id1) return * false; if (id2 != other.id2) return false; return true; } public IDPair * getReverse() { return new IDPair(id2, id1); } } */ /* * (non-Javadoc) * * @see * rescuecore2.worldmodel.AbstractWorldModel#merge(rescuecore2.worldmodel * .ChangeSet) */ @Override public void merge(ChangeSet changeSet) { lastMergedChanges = getChanges(changeSet); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Receiving changeSet: " + changeSet + ". New updates: " + lastMergedChanges); } removeOldBlockades(lastMergedChanges, changeSet); super.merge(changeSet); // add provenance information for these updates // Careful: we assume here this method is only ever called by the // AbstractAgent implementation using the // sense updates. Set<EntityID> changedEntities = changeSet.getChangedEntities(); for (EntityID entityID : changedEntities) { for (Property p : changeSet.getChangedProperties(entityID)) { ProvenanceLogEntry entry = new ProvenanceLogEntry(timer .getTime(), SensedOrigin.INSTANCE, p.copy()); provenance.storeProvenance(entityID, entry); } } } /** * Removes blockades that are no longer associated with areas that have just * been seen * * @param lastMergedChanges * @param changeSet */ private void removeOldBlockades(ChangeSet lastMergedChanges, ChangeSet seen) { Set<EntityID> changedEntities = seen.getChangedEntities(); for (EntityID entityID : changedEntities) { Property changedProperty = seen.getChangedProperty(entityID, StandardPropertyURN.BLOCKADES.toString()); if (changedProperty != null && changedProperty.isDefined()) { Set<EntityID> seenBlockades = new FastSet<EntityID>(); seenBlockades.addAll((List<EntityID>) changedProperty .getValue()); List<EntityID> previousBlockades = ((Area) getEntity(entityID)) .getBlockades(); if (previousBlockades != null) { for (EntityID previousBlock : previousBlockades) { if (!seenBlockades.contains(previousBlock)) { // Remove this block LOGGER.debug("Removing block " + previousBlock); removeEntity(previousBlock); lastMergedChanges.entityDeleted(previousBlock); } } } } } } public Map<StandardEntityURN, Collection<StandardEntity>> getRescueAgents() { if (agentsByType == null) { agentsByType = new HashMap<StandardEntityURN, Collection<StandardEntity>>(); agentsByType.put(StandardEntityURN.AMBULANCE_CENTRE, getEntitiesOfType(StandardEntityURN.AMBULANCE_CENTRE)); agentsByType.put(StandardEntityURN.AMBULANCE_TEAM, getEntitiesOfType(StandardEntityURN.AMBULANCE_TEAM)); agentsByType.put(StandardEntityURN.FIRE_BRIGADE, getEntitiesOfType(StandardEntityURN.FIRE_BRIGADE)); agentsByType.put(StandardEntityURN.FIRE_STATION, getEntitiesOfType(StandardEntityURN.FIRE_STATION)); agentsByType.put(StandardEntityURN.POLICE_FORCE, getEntitiesOfType(StandardEntityURN.POLICE_FORCE)); agentsByType.put(StandardEntityURN.POLICE_OFFICE, getEntitiesOfType(StandardEntityURN.POLICE_OFFICE)); } return agentsByType; } /** * @param objectID * @param entry */ public void storeProvenance(EntityID id, ProvenanceLogEntry entry) { provenance.storeProvenance(id, entry); } /** * Takes a sensed changeSet and returns the true changes compared to the * current state of the world model. * * @param sensed * The sensed changes * @return True change set */ public ChangeSet getChanges(ChangeSet sensed) { ChangeSet changes = new ChangeSet(); Set<EntityID> changedEntities = sensed.getChangedEntities(); for (EntityID entityID : changedEntities) { Entity entity = getEntity(entityID); Set<Property> changedProperties = sensed .getChangedProperties(entityID); String urn = sensed.getEntityURN(entityID); if (entity == null) { // Entity did not exist for (Property property : changedProperties) { changes.addChange(entityID, urn, property); } } else { // Entity existed for (Property property : changedProperties) { // Compare to world model Property knownProperty = entity.getProperty(property .getURN()); if (knownProperty.getValue() == null) { if (property.getValue() != null) { changes.addChange(entityID, urn, property); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Property is not null anymore: " + property); } } } else { if (property.getValue() == null) { changes.addChange(entityID, urn, property); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Property is now null: " + property); } } else { if (!checkIfPropertiesEqual(knownProperty, property)) { if (LOGGER.isTraceEnabled()) { LOGGER.trace(knownProperty + " is not equal to " + property); } // For position, check with some margin for // error if (property.getURN().equals( StandardPropertyURN.X.toString()) || property.getURN().equals( StandardPropertyURN.Y .toString())) { int diff = (Integer) property.getValue() - (Integer) knownProperty .getValue(); if (diff < -PropertyEncoderStore.COORD_ACCURACY || diff > PropertyEncoderStore.COORD_ACCURACY) { changes.addChange(entityID, urn, property); } } else { changes.addChange(entityID, urn, property); } } } } } } } return changes; } /** * @param knownProperty * @param newProperty * @return */ public static boolean checkIfPropertiesEqual(Property knownProperty, Property newProperty) { if (knownProperty.isDefined()) { if (!newProperty.isDefined()) { return false; } else { Object knownValue = knownProperty.getValue(); Object newValue = newProperty.getValue(); return checkIfPropertyValuesEqual(knownValue, newValue); } } else { if (!newProperty.isDefined()) { return true; } else { return false; } } } public static boolean checkIfPropertyValuesEqual(Object knownValue, Object newValue) { if (knownValue == null) { return newValue == null; } else { if (newValue == null) { return false; } else { if (!knownValue.equals(newValue)) { if (knownValue instanceof int[] && newValue instanceof int[]) { int[] knownValueList = (int[]) knownValue; int[] newValueList = (int[]) newValue; boolean equal = Arrays.equals(knownValueList, newValueList); return equal; } return false; } else { return true; } } } } /** * @return the lastSensedChanges */ public ChangeSet getLastSensedChanges() { return lastMergedChanges; } /** * @param objectID * @param urn * @return */ public IProvenanceInformation getProvenance(EntityID objectID, String urn) { return provenance.getProvenance(objectID, urn); } /** * Returns the properties about which this model has provenance information. * * @param objectID * The ID in question. * @return Properties that have associated provenance information (empty if * none known or object has been unknown). */ public Collection<String> getProvenanceProperties(EntityID objectID) { return provenance.getKnownProperties(objectID); } /* * (non-Javadoc) * * @see iamrescue.belief.IBuildingSearchUtility#getSafeUnsearchedBuildings() */ @Override public Collection<EntityID> getSafeUnsearchedBuildings() { return buildingUtility.getSafeUnsearchedBuildings(); } /* * (non-Javadoc) * * @see iamrescue.belief.IBuildingSearchUtility#getUnknownBuildings() */ @Override public Collection<EntityID> getUnknownBuildings() { return buildingUtility.getUnknownBuildings(); } @Override public Collection<EntityID> getModulatedSafeUnsearchedBuildings() { // TODO Auto-generated method stub return buildingUtility.getModulatedSafeUnsearchedBuildings(); } @Override public Collection<EntityID> getModulatedUnknownBuildings() { // TODO Auto-generated method stub return buildingUtility.getModulatedUnknownBuildings(); } public boolean initialisedSearch() { // TODO Auto-generated method stub return initialisedSearch; } public void initialiseSearch(EntityID myself) { // TODO Auto-generated method stub buildingUtility.initialiseSearchList(myself); initialisedSearch = true; } public void updateHighPriorityBuildings(Collection<StandardEntity> buildings) { buildingUtility.addHighPriorityBuildings(buildings); } @Override public Collection<EntityID> getSafeHigh() { // TODO Auto-generated method stub return buildingUtility.getSafeHigh(); } @Override public Collection<EntityID> getUnknownHigh() { // TODO Auto-generated method stub return buildingUtility.getUnknownHigh(); } }