package iamrescue.communication; import iamrescue.communication.compression.CompressorException; import iamrescue.communication.compression.IByteArrayCompressor; import iamrescue.communication.compression.NullCompressor; import iamrescue.communication.messages.Message; import iamrescue.communication.messages.MessageChannel; import iamrescue.communication.messages.codec.ICommunicationBeliefBaseAdapter; import iamrescue.communication.messages.codec.IMessageCodec; import iamrescue.communication.messages.codec.UnknownMessageFormatException; import iamrescue.communication.util.ByteArray; import java.util.ArrayList; import java.util.HashMap; 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.commons.lang.time.StopWatch; import org.apache.log4j.Logger; import rescuecore2.worldmodel.EntityID; public class InterAgentMessageDecoder implements IDecoder { private static final List<Message> EMPTY_LIST = new FastList<Message>(); private static final Logger LOGGER = Logger .getLogger(InterAgentMessageDecoder.class); private final Map<Byte, IMessageCodec<?>> decoderTable = new HashMap<Byte, IMessageCodec<?>>(); private Map<EntityID, Set<ByteArray>> alreadyReceived = new FastMap<EntityID, Set<ByteArray>>(); private int lastTimeStep = -1; private IByteArrayCompressor compressor = new NullCompressor(); public void setCompressor(IByteArrayCompressor compressor) { this.compressor = compressor; } public IByteArrayCompressor getCompressor() { return compressor; } public List<Message> decode(EntityID senderAgentID, MessageChannel channel, int timestep, byte[] rawData, ICommunicationBeliefBaseAdapter beliefBase) throws UnknownMessageFormatException { if (channel.getOverallFailureProbability() > 0) { // Possibly expecting redundant messages. Need to filter these. if (timestep > lastTimeStep) { for (Entry<EntityID, Set<ByteArray>> entry : alreadyReceived .entrySet()) { entry.getValue().clear(); } lastTimeStep = timestep; } Set<ByteArray> already = alreadyReceived.get(senderAgentID); if (already == null) { already = new FastSet<ByteArray>(); alreadyReceived.put(senderAgentID, already); } ByteArray array = new ByteArray(rawData); if (!already.add(array)) { return EMPTY_LIST; } } BitStream uncompressedData; try { uncompressedData = compressor.decompress(rawData); } catch (CompressorException e) { throw new UnknownMessageFormatException(e); } return decode(senderAgentID, channel, timestep, uncompressedData, beliefBase); } public List<Message> decode(EntityID senderAgentID, MessageChannel channel, int timestep, BitStream uncompressedData, ICommunicationBeliefBaseAdapter beliefBase) { List<Message> result = new ArrayList<Message>(); StopWatch watch = new StopWatch(); if (uncompressedData.available() == 0) { LOGGER.error("No content"); } while (uncompressedData.available() > 0) { byte msgTypeId; try { // because of an unknown problem, messages are received with // trailing zeros. This might result in a problem while // attempting to read the message id from the stream, as there // might be less than 8 trailing zeros left in the stream msgTypeId = uncompressedData.readByte(); } catch (IllegalArgumentException e) { LOGGER.debug("Couldn't read message type id"); LOGGER.debug(uncompressedData); // return all successfully decoded messages return result; } IMessageCodec<?> decoder = decoderTable.get(msgTypeId); if (decoder == null) { LOGGER.error("Message type unknown, " + "no codec registered for messagetype " + msgTypeId); List<Byte> list = new ArrayList<Byte>(); boolean allZero = true; while (uncompressedData.available() > 0) { boolean bit = uncompressedData.readBit(); list.add(bit ? (byte) 1 : (byte) 0); allZero &= !bit; } if (allZero) { LOGGER.warn("Received trailing zeros"); } else { LOGGER.error("Remaining bits " + list); LOGGER.error("Successfully decoded messages " + result); LOGGER.error("Data: " + uncompressedData); } } else try { watch.start(); Message msg = decoder.decode(senderAgentID, channel, timestep, uncompressedData, beliefBase); watch.stop(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Decoded new message " + msg); LOGGER.trace("Decoding of message with type " + msgTypeId + " took " + watch.getTime() + " ms."); } if (watch.getTime() > 100) LOGGER.warn("Decoding of message with type " + msgTypeId + " took " + watch.getTime() + " ms."); result.add(msg); watch.reset(); } catch (Exception e) { LOGGER.error("Error while decoding message of type " + msgTypeId, e); LOGGER.error("Sender: " + senderAgentID); LOGGER.error("Channel: " + channel); LOGGER.error("Timestep: " + timestep); LOGGER.error("Data: " + uncompressedData); break; } } return result; } public void registerCodec(IMessageCodec<?> decoder) { IMessageCodec<?> messageCodec = decoderTable.get(decoder .getMessagePrefix()); if (messageCodec != null && messageCodec != decoder) throw new IllegalArgumentException("Decoder with prefix " + decoder.getMessagePrefix() + " has already been registered. Are all messageprefixes" + " distinct?"); decoderTable.put(decoder.getMessagePrefix(), decoder); LOGGER.info("Registered MessageCodec " + decoder.getClass()); } /* * (non-Javadoc) * * @see * iamrescue.communication.IDecoder#canDecode(rescuecore2.worldmodel.EntityID * , iamrescue.communication.messages.MessageChannel, int, byte[], * iamrescue.communication.messages.codec.ICommunicationBeliefBaseAdapter) */ @Override public boolean canDecode(EntityID senderAgentID, MessageChannel channel, int timestep, byte[] messageContents, ICommunicationBeliefBaseAdapter beliefBase) { return beliefBase.isRescueEntity(senderAgentID); } }