package iamrescue.communication.scenario.scenarios;
import iamrescue.communication.messages.MessageChannel;
import iamrescue.communication.messages.MessageChannelType;
import iamrescue.communication.scenario.ChannelDividerUtil;
import iamrescue.util.comparators.EntityIDComparator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Map.Entry;
import javolution.util.FastMap;
import javolution.util.FastSet;
import org.apache.log4j.Logger;
import rescuecore2.standard.entities.AmbulanceTeam;
import rescuecore2.standard.entities.FireBrigade;
import rescuecore2.standard.entities.FireStation;
import rescuecore2.standard.entities.PoliceForce;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.worldmodel.EntityID;
public class IAMTeamCommunicationConfiguration {
public static final int RELATIVE_CENTRE_BANDIWDTH = 5;
private static Logger LOGGER = Logger
.getLogger(IAMTeamCommunicationConfiguration.class);
private List<MessageChannel> mainCommunicationChannels;
private List<MessageChannel> overflowChannels;
private Map<EntityID, MessageChannel> outputMainChannels;
private Map<EntityID, MessageChannel> outputOverflowChannels;
private Map<MessageChannel, List<EntityID>> channelSenders;
private Map<EntityID, List<MessageChannel>> subscribedChannels;
private Set<EntityID> centres;
private Set<EntityID> platoons;
private ConfigInfo configInfo;
private Set<EntityID> ignoredEntities = new FastSet<EntityID>();
private IAMTeamCommunicationConfiguration(ConfigInfo configInfo) {
this.configInfo = configInfo;
}
public void setIgnoredEntities(Collection<EntityID> ignored) {
ignoredEntities.clear();
ignoredEntities.addAll(ignored);
}
public Map<EntityID, MessageChannel> getOutputMainChannels() {
return outputMainChannels;
}
public Map<EntityID, MessageChannel> getOutputOverflowChannels() {
return outputOverflowChannels;
}
public Map<MessageChannel, List<EntityID>> getChannelSenders() {
return channelSenders;
}
public Map<EntityID, List<MessageChannel>> getSubscribedChannels() {
return subscribedChannels;
}
public Set<EntityID> getCentres() {
return centres;
}
public Set<EntityID> getPlatoons() {
return platoons;
}
public List<MessageChannel> getMainCommunicationChannels() {
return mainCommunicationChannels;
}
public List<MessageChannel> getOverflowChannels() {
return overflowChannels;
}
public static IAMTeamCommunicationConfiguration createTeam(
List<MessageChannel> mainChannels,
List<MessageChannel> overflowChannels,
List<MessageChannel> highPriorityTeamsToListen,
List<MessageChannel> lowPriorityTeamsToListen,
List<MessageChannel> veryLowPriorityTeamsToListen,
Collection<StandardEntity> entities, int maxPlatoonChannels,
int maxCentreChannels) {
ConfigInfo configInfo = new ConfigInfo(mainChannels, overflowChannels,
highPriorityTeamsToListen, lowPriorityTeamsToListen,
veryLowPriorityTeamsToListen, entities, maxPlatoonChannels,
maxCentreChannels);
IAMTeamCommunicationConfiguration config = new IAMTeamCommunicationConfiguration(
configInfo);
config.initialise();
return config;
}
public void reinitialise(Collection<EntityID> ignoredEntities) {
setIgnoredEntities(ignoredEntities);
initialise();
}
private void initialise() {
List<MessageChannel> mainChannels = configInfo.getMainChannels();
List<MessageChannel> overflowChannels = configInfo
.getOverflowChannels();
List<MessageChannel> highPriorityTeamsToListen = configInfo
.getHighPriorityTeamsToListen();
List<MessageChannel> lowPriorityTeamsToListen = configInfo
.getLowPriorityTeamsToListen();
List<MessageChannel> veryLowPriorityTeamsToListen = configInfo
.getVeryLowPriorityTeamsToListen();
int maxPlatoonChannels = configInfo.getMaxPlatoonChannels();
int maxCentreChannels = configInfo.getMaxCentreChannels();
Collection<StandardEntity> entities = new ArrayList<StandardEntity>();
for (StandardEntity se : configInfo.getEntities()) {
if (!ignoredEntities.contains(se.getID())) {
entities.add(se);
}
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Starting team configuration with main channels "
+ mainChannels + ", overflow channels: " + overflowChannels
+ ", high priority " + highPriorityTeamsToListen + ", low "
+ lowPriorityTeamsToListen + ", very low: "
+ veryLowPriorityTeamsToListen + ", max platoon: "
+ maxPlatoonChannels + ", maxCentre: " + maxCentreChannels
+ ", entities: " + entities);
}
this.mainCommunicationChannels = mainChannels;
this.centres = new FastSet<EntityID>();
this.subscribedChannels = new FastMap<EntityID, List<MessageChannel>>();
this.outputMainChannels = new FastMap<EntityID, MessageChannel>();
this.outputOverflowChannels = new FastMap<EntityID, MessageChannel>();
this.overflowChannels = overflowChannels;
List<StandardEntity> platoons = new ArrayList<StandardEntity>();
List<StandardEntity> centres = new ArrayList<StandardEntity>();
for (StandardEntity entity : entities) {
if (entity instanceof FireBrigade || entity instanceof PoliceForce
|| entity instanceof AmbulanceTeam) {
platoons.add(entity);
} else {
centres.add(entity);
}
}
Collections.sort(platoons, new EntityIDComparator());
Collections.sort(centres, new EntityIDComparator());
// How many normal platoon agents need to additionally listen to other
// teams.
int needToListen = 0;
int channelsEach = 1;
List<MessageChannel> channelsToListenTo = new ArrayList<MessageChannel>();
channelsToListenTo.addAll(highPriorityTeamsToListen);
channelsToListenTo.addAll(mainChannels);
channelsToListenTo.addAll(overflowChannels);
channelsToListenTo.addAll(lowPriorityTeamsToListen);
channelsToListenTo.addAll(veryLowPriorityTeamsToListen);
if (channelsToListenTo.size() > maxPlatoonChannels) {
// Only do centres if they are required.
if (centres.size() > 0) {
// There are centres
// First do high priority channels
int index = 0;
for (int i = 0; i < centres.size(); i++) {
List<MessageChannel> toSubscribe = new ArrayList<MessageChannel>();
while (toSubscribe.size() < maxCentreChannels
&& index < channelsToListenTo.size()) {
toSubscribe.add(channelsToListenTo.get(index++));
}
if (toSubscribe.size() > 0) {
if (toSubscribe.size() < maxCentreChannels) {
// Fill up with own channels if still space
Iterator<MessageChannel> iterator = mainChannels
.iterator();
while (iterator.hasNext()
&& toSubscribe.size() < maxCentreChannels) {
MessageChannel next = iterator.next();
if (!toSubscribe.contains(next)) {
toSubscribe.add(next);
}
}
}
EntityID id = centres.get(i).getID();
this.centres.add(id);
this.subscribedChannels.put(id, toSubscribe);
}
}
List<StandardEntity> newCentres = new ArrayList<StandardEntity>();
for (StandardEntity se : centres) {
if (this.centres.contains(se.getID())) {
newCentres.add(se);
}
}
/*for (StandardEntity se : centres) {
if (!newCentres.contains(se)) {
newCentres.add(se);
this.centres.add(se.getID());
this.subscribedChannels.put(se.getID(), mainChannels);
}
}*/
centres = newCentres;
int nextGoal = highPriorityTeamsToListen.size();
if (index < nextGoal) {
LOGGER.error("Could not get centres to listen "
+ "to all high priority channels.");
this.overflowChannels = new ArrayList<MessageChannel>();
} else {
nextGoal += mainChannels.size();
if (index < nextGoal) {
LOGGER.warn("Centre is not listening to all its own "
+ "team's channels. This can lead to "
+ "duplicate messages.");
this.overflowChannels = new ArrayList<MessageChannel>();
} else {
nextGoal += overflowChannels.size();
if (index < nextGoal) {
// Could not add all overflow channels
index -= (nextGoal - overflowChannels.size());
// index should now point to last overflow channel
// that
// was added
this.overflowChannels = new ArrayList<MessageChannel>();
for (int i = 0; i < index; i++) {
this.overflowChannels.add(overflowChannels
.get(i));
}
LOGGER.info("Added only " + index
+ " overflow channels.");
}
// No need to be more verbose now.
}
}
} else {
// No centres
// Assign several agents to just listen on the other channels
needToListen = highPriorityTeamsToListen.size();
channelsEach = 1;
if (needToListen > platoons.size()) {
// If too few agents, make one of them listen to all
channelsEach = needToListen;
needToListen = 1;
}
centres = new ArrayList<StandardEntity>();
// int counter = 0;
for (int i = 0; i < needToListen; i++) {
StandardEntity newCentre = platoons.remove(0);
centres.add(newCentre);
}
// No overflow channels in this case
overflowChannels = new ArrayList<MessageChannel>();
}
}
// Add any remaining centres to platoons
boolean added = false;
for (StandardEntity se : entities) {
if (!centres.contains(se)) {
if (!platoons.contains(se)) {
// Add to platoons
platoons.add(se);
added = true;
}
}
}
if (added) {
Collections.sort(platoons, new EntityIDComparator());
}
// Now work out assignments for main channel.
Map<MessageChannel, List<EntityID>> divideAgents = ChannelDividerUtil
.divideAgents(mainChannels, ChannelDividerUtil
.convertToIDs(platoons), ChannelDividerUtil
.convertToIDs(centres), RELATIVE_CENTRE_BANDIWDTH);
// Store this
for (Entry<MessageChannel, List<EntityID>> entry : divideAgents
.entrySet()) {
MessageChannel channel = entry.getKey();
List<EntityID> listeners = entry.getValue();
for (EntityID entityID : listeners) {
this.outputMainChannels.put(entityID, channel);
}
}
// Ensure centres are listening to their own allocated channels where
// possible
if (needToListen == 0) {
// Only do for real centres, otherwise next if statement takes care
// of this.
Map<EntityID, MessageChannel> needed = new FastMap<EntityID, MessageChannel>();
Map<EntityID, Set<MessageChannel>> surplus = new FastMap<EntityID, Set<MessageChannel>>();
for (EntityID centreID : this.centres) {
surplus.put(centreID, new FastSet<MessageChannel>());
MessageChannel messageChannel = this.outputMainChannels
.get(centreID);
needed.put(centreID, messageChannel);
Set<MessageChannel> mainCommsChannels = new FastSet<MessageChannel>();
mainCommsChannels.addAll(this.getMainCommunicationChannels());
boolean ok = false;
for (MessageChannel already : this.getSubscribedChannels().get(
centreID)) {
if (already.equals(messageChannel)) {
ok = true;
} else if (mainCommsChannels.contains(already)) {
surplus.get(centreID).add(already);
}
}
if (ok) {
needed.remove(centreID);
}
}
// Now swap
for (EntityID centreID : this.centres) {
MessageChannel neededChannel = needed.get(centreID);
if (neededChannel != null) {
for (EntityID otherCentreID : this.centres) {
Set<MessageChannel> otherSurplus = surplus
.get(otherCentreID);
MessageChannel toSwap = null;
if (otherSurplus.contains(neededChannel)) {
for (MessageChannel unneeded : surplus
.get(centreID)) {
if (!otherSurplus.contains(unneeded)
&& ((needed.get(otherCentreID) != null && needed
.get(otherCentreID).equals(
unneeded)) || !this
.getOutputMainChannels().get(
otherCentreID).equals(
unneeded))) {
toSwap = unneeded;
break;
}
}
if (toSwap != null) {
this.getSubscribedChannels().get(centreID).add(
neededChannel);
this.getSubscribedChannels().get(centreID)
.remove(toSwap);
this.getSubscribedChannels().get(otherCentreID)
.add(toSwap);
this.getSubscribedChannels().get(otherCentreID)
.remove(neededChannel);
needed.remove(centreID);
}
}
}
}
}
}
// Next, assign platoon centres (if any) to listen to other channels and
// their own
if (needToListen > 0) {
int counter = 0;
// Yes, there are platoon centres
for (int i = 0; i < centres.size(); i++) {
List<MessageChannel> listenTo = new ArrayList<MessageChannel>();
while (listenTo.size() < maxPlatoonChannels
&& counter < highPriorityTeamsToListen.size()
&& listenTo.size() < channelsEach) {
listenTo.add(highPriorityTeamsToListen.get(counter++));
}
if (listenTo.size() > 0 && listenTo.size() < maxPlatoonChannels) {
MessageChannel myChannel = this.outputMainChannels
.get(centres.get(i).getID());
// Add own channel if possible (for error detection)
listenTo.add(myChannel);
if (listenTo.size() < maxPlatoonChannels) {
List<MessageChannel> remaining = new ArrayList<MessageChannel>(
mainChannels);
Collections.shuffle(remaining, new Random(1234));
counter = 0;
while (listenTo.size() < maxPlatoonChannels) {
if (counter < remaining.size()) {
MessageChannel channel = remaining
.get(counter++);
if (channel.equals(myChannel)) {
// Already subscribed
continue;
} else {
// Add channel
listenTo.add(channel);
}
} else {
break;
}
}
}
}
this.centres.add(centres.get(i).getID());
this.subscribedChannels.put(centres.get(i).getID(), listenTo);
}
} // Done allocating channels to platoon centres
// Work out subscribed channels to listen to for everyone else
for (int i = 0; i < platoons.size(); i++) {
List<MessageChannel> list;
StandardEntity se = platoons.get(i);
if (maxPlatoonChannels >= mainChannels.size()) {
list = new ArrayList<MessageChannel>(mainChannels);
} else {
list = new ArrayList<MessageChannel>();
list.add(this.outputMainChannels.get(se.getID()));
int counter = 0;
List<MessageChannel> channels = new ArrayList<MessageChannel>();
channels.addAll(mainChannels);
Collections
.shuffle(channels, new Random(se.getID().getValue()));
for (int j = 1; j < maxPlatoonChannels; j++) {
MessageChannel messageChannel = channels.get(counter++);
if (messageChannel.equals(list.get(0))) {
j--;
continue;
}
list.add(messageChannel);
}
}
int counter = 0;
while (list.size() < maxPlatoonChannels) {
if (counter < highPriorityTeamsToListen.size()) {
list.add(highPriorityTeamsToListen.get(counter++));
} else {
int index = counter - highPriorityTeamsToListen.size();
if (index < lowPriorityTeamsToListen.size()) {
list.add(lowPriorityTeamsToListen.get(index));
counter++;
} else {
index = counter - highPriorityTeamsToListen.size()
- lowPriorityTeamsToListen.size();
if (index < veryLowPriorityTeamsToListen.size()) {
list.add(veryLowPriorityTeamsToListen.get(index));
counter++;
} else {
break;
}
}
}
}
this.subscribedChannels.put(se.getID(), list);
}
// Then, work out overflow channel allocations
Map<MessageChannel, List<EntityID>> overflowAssignment = ChannelDividerUtil
.divideAgents(this.overflowChannels, ChannelDividerUtil
.convertToIDs(platoons), new ArrayList<EntityID>(), 1);
// Save all in scenario
for (Entry<MessageChannel, List<EntityID>> entry : overflowAssignment
.entrySet()) {
MessageChannel channel = entry.getKey();
List<EntityID> listeners = entry.getValue();
for (EntityID entityID : listeners) {
this.outputOverflowChannels.put(entityID, channel);
}
}
this.platoons = new FastSet<EntityID>();
this.platoons.addAll(ChannelDividerUtil.convertToIDs(platoons));
this.updateChannelSenders();
}
/**
*
*/
private void updateChannelSenders() {
channelSenders = new FastMap<MessageChannel, List<EntityID>>();
for (int i = 0; i < 2; i++) {
Set<Entry<EntityID, MessageChannel>> entrySet = (i == 0) ? outputMainChannels
.entrySet()
: outputOverflowChannels.entrySet();
for (Entry<EntityID, MessageChannel> entry : entrySet) {
MessageChannel channel = entry.getValue();
EntityID sender = entry.getKey();
List<EntityID> list = channelSenders.get(channel);
if (list == null) {
list = new ArrayList<EntityID>();
channelSenders.put(channel, list);
}
list.add(sender);
}
}
}
public String toVerboseString() {
StringBuffer sb = new StringBuffer("Allocation: ");
sb.append('\n');
sb.append("\nPlatoons: ");
sb.append(platoons.toString());
sb.append('\n');
sb.append("\nCentres: ");
sb.append(centres.toString());
sb.append('\n');
sb.append("\nAllocation:");
sb.append('\n');
for (EntityID id : centres) {
MessageChannel overflow = outputOverflowChannels.get(id);
String overflowStr = overflow == null ? "none" : overflow
.getChannelNumber()
+ "";
sb.append("Centre " + id + " -> main: "
+ outputMainChannels.get(id).getChannelNumber()
+ ", overflow: " + overflowStr + "\n");
sb.append("Centre " + id + " <-");
List<MessageChannel> list = subscribedChannels.get(id);
for (MessageChannel messageChannel : list) {
sb.append(" " + messageChannel.getChannelNumber());
}
sb.append("\n");
}
for (EntityID id : platoons) {
MessageChannel overflow = outputOverflowChannels.get(id);
String overflowStr = overflow == null ? "none" : overflow
.getChannelNumber()
+ "";
sb.append("Platoon " + id + " -> main: "
+ outputMainChannels.get(id).getChannelNumber()
+ ", overflow: " + overflowStr + "\n");
sb.append("Platoon " + id + " <-");
List<MessageChannel> list = subscribedChannels.get(id);
for (MessageChannel messageChannel : list) {
sb.append(" " + messageChannel.getChannelNumber());
}
sb.append("\n");
}
sb.append("\nChannels:\n");
sb.append("\nMain Channels:\n");
for (MessageChannel channel : mainCommunicationChannels) {
sb.append(channel.getChannelNumber() + " <- "
+ channelSenders.get(channel) + "\n");
}
sb.append("\nOverflow Channels:\n");
for (MessageChannel channel : overflowChannels) {
sb.append("Channel " + channel.getChannelNumber() + " <- "
+ channelSenders.get(channel) + "\n");
}
return sb.toString();
}
public static void main(String[] args) {
List<StandardEntity> agents = new ArrayList<StandardEntity>();
agents.add(new FireBrigade(new EntityID(1)));
agents.add(new FireBrigade(new EntityID(2)));
agents.add(new FireBrigade(new EntityID(3)));
agents.add(new FireBrigade(new EntityID(4)));
agents.add(new FireStation(new EntityID(6)));
agents.add(new FireStation(new EntityID(5)));
agents.add(new FireStation(new EntityID(7)));
// MessageChannelConfig
MessageChannel mc1 = new MessageChannel(1, MessageChannelType.RADIO);
mc1.setBandwidth(1000);
mc1.setInputDropoutProbability(0.5);
MessageChannel mc2 = new MessageChannel(2, MessageChannelType.RADIO);
mc2.setBandwidth(900);
MessageChannel mc3 = new MessageChannel(3, MessageChannelType.RADIO);
mc3.setBandwidth(800);
MessageChannel other1 = new MessageChannel(4, MessageChannelType.RADIO);
other1.setBandwidth(800);
MessageChannel other2 = new MessageChannel(5, MessageChannelType.RADIO);
other2.setBandwidth(750);
MessageChannel other3 = new MessageChannel(6, MessageChannelType.RADIO);
other3.setBandwidth(800);
MessageChannel other4 = new MessageChannel(7, MessageChannelType.RADIO);
other4.setBandwidth(750);
List<MessageChannel> main = new ArrayList<MessageChannel>();
main.add(mc1);
main.add(mc2);
List<MessageChannel> overflow = new ArrayList<MessageChannel>();
overflow.add(mc3);
List<MessageChannel> other = new ArrayList<MessageChannel>();
other.add(other1);
other.add(other2);
List<MessageChannel> yetother = new ArrayList<MessageChannel>();
yetother.add(other3);
yetother.add(other4);
IAMTeamCommunicationConfiguration team = IAMTeamCommunicationConfiguration
.createTeam(main, overflow, other, yetother,
new ArrayList<MessageChannel>(), agents, 1, 3);
System.out.println(team.toVerboseString());
team.reinitialise(Collections.singleton(new EntityID(7)));
System.out.println(team.toVerboseString());
}
private static class ConfigInfo {
private List<MessageChannel> mainChannels;
private List<MessageChannel> overflowChannels;
private List<MessageChannel> highPriorityTeamsToListen;
private List<MessageChannel> lowPriorityTeamsToListen;
private List<MessageChannel> veryLowPriorityTeamsToListen;
private Collection<StandardEntity> entities;
private int maxPlatoonChannels;
private int maxCentreChannels;
public ConfigInfo(List<MessageChannel> mainChannels,
List<MessageChannel> overflowChannels,
List<MessageChannel> highPriorityTeamsToListen,
List<MessageChannel> lowPriorityTeamsToListen,
List<MessageChannel> veryLowPriorityTeamsToListen,
Collection<StandardEntity> entities, int maxPlatoonChannels,
int maxCentreChannels) {
this.mainChannels = mainChannels;
this.overflowChannels = overflowChannels;
this.highPriorityTeamsToListen = highPriorityTeamsToListen;
this.lowPriorityTeamsToListen = lowPriorityTeamsToListen;
this.veryLowPriorityTeamsToListen = veryLowPriorityTeamsToListen;
this.entities = entities;
this.maxPlatoonChannels = maxPlatoonChannels;
this.maxCentreChannels = maxCentreChannels;
}
/**
* @return the mainChannels
*/
public List<MessageChannel> getMainChannels() {
return Collections.unmodifiableList(mainChannels);
}
/**
* @return the overflowChannels
*/
public List<MessageChannel> getOverflowChannels() {
return Collections.unmodifiableList(overflowChannels);
}
/**
* @return the highPriorityTeamsToListen
*/
public List<MessageChannel> getHighPriorityTeamsToListen() {
return Collections.unmodifiableList(highPriorityTeamsToListen);
}
/**
* @return the lowPriorityTeamsToListen
*/
public List<MessageChannel> getLowPriorityTeamsToListen() {
return Collections.unmodifiableList(lowPriorityTeamsToListen);
}
/**
* @return the veryLowPriorityTeamsToListen
*/
public List<MessageChannel> getVeryLowPriorityTeamsToListen() {
return Collections.unmodifiableList(veryLowPriorityTeamsToListen);
}
/**
* @return the entities
*/
public Collection<StandardEntity> getEntities() {
return Collections.unmodifiableCollection(entities);
}
/**
* @return the maxPlatoonChannels
*/
public int getMaxPlatoonChannels() {
return maxPlatoonChannels;
}
/**
* @return the maxCentreChannels
*/
public int getMaxCentreChannels() {
return maxCentreChannels;
}
}
}