package iamrescue.communication.scenario;
import iamrescue.agent.ISimulationTimer;
import iamrescue.communication.CommunicationModule;
import iamrescue.communication.IAMMessagePrioritiser;
import iamrescue.communication.ICommunicationModule;
import iamrescue.communication.IMessagingSchedule;
import iamrescue.communication.ISimulationCommunicationConfiguration;
import iamrescue.communication.messages.Message;
import iamrescue.communication.messages.MessageChannel;
import iamrescue.communication.messages.MessageChannelType;
import iamrescue.communication.messages.codec.ICommunicationBeliefBaseAdapter;
import iamrescue.communication.scenario.scenarios.DefaultCommunicationScenarioDetector;
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.Set;
import java.util.Map.Entry;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
import org.apache.log4j.Logger;
import rescuecore2.connection.Connection;
import rescuecore2.messages.Command;
import rescuecore2.standard.messages.AKSpeak;
import rescuecore2.worldmodel.EntityID;
public class IAMCommunicationModule implements ICommunicationModule {
// Automatically delete all messages beyond this length in each
// channel (starting with
// lowest priority messages)
private static final int MAX_OUTBOX_LENGTH = 50;
private static final Logger LOGGER = Logger
.getLogger(IAMCommunicationModule.class);
private ICommunicationScenarioDetector detector;
private ICommunicationModule communicationModule;
private Collection<MessageChannel> vocalChannels;
private ISimulationTimer timer;
private ISimulationCommunicationConfiguration configuration;
private static final String IGNORE_KEY = "kernel.agents.ignoreuntil";
private List<Message> messagesToOtherTeams = new FastList<Message>();
private List<Message> messagesToOwnTeam = new FastList<Message>();
private int ignoreUntil;
private static final boolean DO_FAULTY_CHANNEL_DETECTION = false;
private static final int FAULTY_CHANNEL_DETECTION_TIME = 4;
//private Set<Integer> silentChannels = new FastSet<Integer>();
// private Set<Integer> previouslySubscribedChannels = new
// FastSet<Integer>();
private static final int UNHEARD_CENTRE_REMOVAL_THRESHOLD = 6;
private Map<EntityID, Integer> unheard = new FastMap<EntityID, Integer>();
private Set<EntityID> centresToIgnore = new FastSet<EntityID>();
private IAMMessagePrioritiser messagePrioritiser;
public IAMCommunicationModule(EntityID id, ISimulationTimer timer,
ICommunicationBeliefBaseAdapter beliefBase,
final ISimulationCommunicationConfiguration configuration,
Connection connection) {
this.detector = new DefaultCommunicationScenarioDetector(configuration);
this.timer = timer;
this.configuration = configuration;
IMessagingSchedule scheduler = detector.getScenario().getScheduler();
CommunicationModule commModule = new CommunicationModule(id, timer,
beliefBase, configuration, connection, scheduler);
commModule.setMaxOutboxSizePerChannel(MAX_OUTBOX_LENGTH);
this.communicationModule = commModule;
this.ignoreUntil = configuration.getConfig().getIntValue(IGNORE_KEY, 3);
this.messagePrioritiser = new IAMMessagePrioritiser();
List<MessageChannel> channels = configuration.getChannels();
/*for (MessageChannel messageChannel : channels) {
silentChannels.add(messageChannel.getChannelNumber());
}*/
}
/**
* @return the configuration
*/
public ISimulationCommunicationConfiguration getConfiguration() {
return configuration;
}
public void enqueueVocalMessage(Message message) {
MessageChannel selected;
if (getVocalChannels().size() > 1) {
LOGGER.warn("More than one vocal channel exist. "
+ "Selecting a random one");
MessageChannel[] channels = getVocalChannels().toArray(
new MessageChannel[0]);
selected = channels[(int) (Math.random() * channels.length)];
} else {
selected = getVocalChannels().iterator().next();
}
communicationModule.enqueueMessage(message, selected);
}
private Collection<MessageChannel> getVocalChannels() {
if (vocalChannels == null) {
vocalChannels = new ArrayList<MessageChannel>();
for (MessageChannel messageChannel : communicationModule
.getChannels()) {
if (messageChannel.getType() == MessageChannelType.VOICE) {
vocalChannels.add(messageChannel);
}
}
}
return vocalChannels;
}
public void enqueueRadioMessageToOtherTeams(Message message) {
messagePrioritiser.updateMessagePriority(message);
messagesToOtherTeams.add(message);
}
public void enqueueRadioMessageToOwnTeam(Message message) {
messagePrioritiser.updateMessagePriority(message);
messagesToOwnTeam.add(message);
}
@Override
public void enqueueMessage(Message message, MessageChannel channel) {
communicationModule.enqueueMessage(message, channel);
}
@Override
public void flushOutbox() {
List<MessageChannel> channelsToSubscribeTo = detector.getScenario()
.getChannelsToSubscribeTo();
// Check if any are silent
/*if (ignoreUntil + FAULTY_CHANNEL_DETECTION_TIME < timer.getTime()) {
List<MessageChannel> finalList = new ArrayList<MessageChannel>(
channelsToSubscribeTo.size());
List<MessageChannel> substituteList = null;
for (MessageChannel messageChannel : channelsToSubscribeTo) {
if (silentChannels.contains(messageChannel.getChannelNumber())) {
LOGGER.warn("Noticed failure of channel " + messageChannel);
// Need to replace this with another channel.
if (substituteList == null) {
substituteList = new ArrayList<MessageChannel>(
configuration.getRadioChannels());
Collections.shuffle(substituteList);
}
Iterator<MessageChannel> iterator = substituteList
.iterator();
while (iterator.hasNext()) {
MessageChannel option = iterator.next();
iterator.remove();
if (!silentChannels.contains(option)
&& (!channelsToSubscribeTo.contains(option))) {
LOGGER.warn("Substituting with " + option);
finalList.add(option);
break;
}
}
} else {
finalList.add(messageChannel);
}
}
channelsToSubscribeTo = finalList;
} */
subscribeToChannels(channelsToSubscribeTo);
Map<MessageChannel, List<Message>> messagesAllocation = detector
.getScenario().distributeMessages(messagesToOwnTeam,
messagesToOtherTeams, timer);
for (Entry<MessageChannel, List<Message>> channelEntry : messagesAllocation
.entrySet()) {
MessageChannel channel = channelEntry.getKey();
List<Message> messages = channelEntry.getValue();
for (Message message : messages) {
communicationModule.enqueueMessage(message, channel);
}
}
communicationModule.flushOutbox();
messagesToOtherTeams.clear();
messagesToOwnTeam.clear();
}
@Override
public Collection<MessageChannel> getChannels() {
return communicationModule.getChannels();
}
@Override
public Collection<Message> getUnreadMessages() {
return communicationModule.getUnreadMessages();
}
@Override
public void hear(Collection<Command> heard) {
communicationModule.hear(heard);
if (LOGGER.isInfoEnabled()) {
Collection<Message> unreadMessages = getUnreadMessages();
StringBuffer sb = new StringBuffer();
sb.append("Contents of inbox:");
for (Message message : unreadMessages) {
sb.append(' ');
sb.append(message.toShortString() + ",t:"
+ message.getTimestepReceived());
}
LOGGER.info(sb.toString());
}
// Did I hear from all my centres?
if (timer.getTime() > ignoreUntil) {
Set<EntityID> unheardCentres = new FastSet<EntityID>();
unheardCentres.addAll(detector.getScenario().getMyCentres());
unheardCentres.remove(configuration.getEntityID());
for (Command c : heard) {
unheardCentres.remove(c.getAgentID());
}
// Now go through unheard centres
if (unheardCentres.size() > 0) {
Map<EntityID, Integer> newUnheardMap = new FastMap<EntityID, Integer>();
for (EntityID id : unheardCentres) {
Integer already = unheard.get(id);
if (already == null) {
already = 0;
}
int newAlready = already + 1;
newUnheardMap.put(id, newAlready);
if (newAlready >= UNHEARD_CENTRE_REMOVAL_THRESHOLD) {
// Need to remove this
centresToIgnore.add(id);
}
if (newAlready > 1) {
LOGGER.warn("Have not heard from centre " + id + " "
+ newAlready + " time steps in a row.");
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Have not heard from centre " + id
+ " this time step.");
}
}
}
unheard = newUnheardMap;
if (centresToIgnore.size() > 0) {
LOGGER.warn("Have not heard some centres ("
+ centresToIgnore + ") in "
+ UNHEARD_CENTRE_REMOVAL_THRESHOLD + " time steps."
+ " Reinitialising team communications.");
// Reinitialise!
detector.getScenario().reinitialiseTeam(centresToIgnore);
// LOGGER.info("New team communication infrastrucutre: " +
// detector.getScenario().get )
}
} else {
// Heard all
if (unheard.size() > 0) {
unheard.clear();
}
}
/*for (Command command : heard) {
if (command instanceof AKSpeak) {
AKSpeak speak = (AKSpeak) command;
int channel = speak.getChannel();
silentChannels.remove(channel);
}
}*/
}
}
@Override
public boolean isRadioCommunicationPossible() {
return communicationModule.isRadioCommunicationPossible();
}
@Override
public void enqueueMessage(Message message, List<MessageChannel> channels) {
communicationModule.enqueueMessage(message, channels);
}
@Override
public void subscribeToChannels(List<MessageChannel> channels) {
// if (timer.getTime() < ignoreUntil) {
communicationModule.subscribeToChannels(channels);
// }
}
public boolean amICentre() {
return detector.getScenario().amICentre();
}
public List<MessageChannel> getChannelsToOwnTeam() {
return detector.getScenario().getChannelsToOwnTeam();
}
}