package lcm.lcm; import java.io.*; import java.util.*; /** * Accumulates received LCM messages in a queue. * <p> * {@link LCM} normally delivers messages asynchronously by invoking the * {@link LCMSubscriber#messageReceived messageReceived} * method on a subscriber as soon as a message is received. This class provides an * alternate way to receive messages by storing them in an internal queue, and then * delivering them to synchronously to the user. * <p> * The aggregator has configurable limits. If too many messages are aggregated * without having been retrieved, then older messages are discarded. */ public class MessageAggregator implements LCMSubscriber { /** * A received message. */ public class Message { /** * The raw data bytes of the message body. */ final public byte[] data; /** * Channel on which the message was received. */ final public String channel; public Message(String channel_, byte[] data_) { data = data_; channel = channel_; } } // Would prefer to use ArrayDequeue for performance reasons, but it's too new // (only since Java 1.6) // Deque<Message> messages = new ArrayDeque<Message>(); LinkedList<Message> messages = new LinkedList<Message>(); long queue_data_size = 0; long max_queue_data_size = 100 * (1 << 20); // 100 megabytes int max_queue_length = Integer.MAX_VALUE; /** * Internal method, called by LCM when a message is received. */ public synchronized void messageReceived(LCM lcm, String channel, LCMDataInputStream dins) { try { byte data[] = new byte[dins.available()]; dins.readFully(data); messages.addLast(new Message(channel, data)); queue_data_size += data.length; while(queue_data_size > max_queue_data_size || messages.size() > max_queue_length) { Message to_remove = messages.removeFirst(); queue_data_size -= to_remove.data.length; } notify(); } catch (IOException xcp) {} } /** * Sets the maximum amount of memory that will be used to store messages. * * This is an alternative way to limit the messages stored by the * aggregator. Messages are discarded oldest-first to ensure that the * total size of unretrieved messages stays under this limit. * * @param val memory limit, in bytes. */ public synchronized void setMaxBufferSize(long val) { max_queue_data_size = val; } /** * Retrieves the maximum amount of memory that will be used to store messages. */ public synchronized long getMaxBufferSize() { return max_queue_data_size; } /** * Sets the maximum number of unretrieved message that will be queued up by * the aggregator. * * Messages are discarded oldest-first to ensure that the number of * unretrieved messages stays under this limit. */ public synchronized void setMaxMessages(int val) { max_queue_length = val; } /** * Retrieves the maximum number of unretrieved message that will be queued * up by the aggregator. */ public synchronized int getMaxMessages() { return max_queue_length; } /** * Attempt to retrieve the next received LCM message. * @param timeout_ms Max # of milliseconds to wait for a message. If 0, * then don't wait. If less than 0, then wait indefinitely. * @return a Message, or null if no message was received. */ public synchronized Message getNextMessage(long timeout_ms) { if(!messages.isEmpty()) { Message m = messages.removeFirst(); queue_data_size -= m.data.length; return m; } if(timeout_ms == 0) return null; try { if(timeout_ms > 0) wait(timeout_ms); else wait(); if(!messages.isEmpty()) { Message m = messages.removeFirst(); queue_data_size -= m.data.length; return m; } } catch (InterruptedException xcp) { } return null; } /** * Retrieves the next message, waiting if necessary. */ public synchronized Message getNextMessage() { return getNextMessage(-1); } /** * Returns the number of received messages waiting to be retrieved. */ public synchronized int numMessagesAvailable() { return messages.size(); } }