Package rinde.sim.core.model.communication

Source Code of rinde.sim.core.model.communication.CommunicationModel$Rectangle

package rinde.sim.core.model.communication;

import static com.google.common.base.Preconditions.checkArgument;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.math3.random.RandomGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import rinde.sim.core.TickListener;
import rinde.sim.core.TimeLapse;
import rinde.sim.core.graph.Point;
import rinde.sim.core.model.Model;
import rinde.sim.core.model.road.RoadUser;

import com.google.common.base.Predicate;

/**
* The communication model. Messages are send at the end of a current tick.
* @author Bartosz Michalik <bartosz.michalik@cs.kuleuven.be>
* @author Rinde van Lon <rinde.vanlon@cs.kuleuven.be>
* @since 2.0
*/
public class CommunicationModel implements Model<CommunicationUser>,
    TickListener, CommunicationAPI {

  protected static final Logger LOGGER = LoggerFactory
      .getLogger(CommunicationModel.class);

  protected final Set<CommunicationUser> users;
  protected List<Entry<CommunicationUser, Message>> sendQueue;
  protected RandomGenerator generator;
  private final boolean ignoreDistances;

  /**
   * Constructs the communication model.
   * @param pGenerator the random number generator that is used for reliability
   *          computations
   * @param pIgnoreDistances when <code>true</code> the distances constrains are
   *          ignored.
   */
  public CommunicationModel(RandomGenerator pGenerator, boolean pIgnoreDistances) {
    checkArgument(pGenerator != null, "generator can not be null");
    users = new LinkedHashSet<CommunicationUser>();
    sendQueue = new LinkedList<Entry<CommunicationUser, Message>>();
    generator = pGenerator;
    ignoreDistances = pIgnoreDistances;
  }

  /**
   * Construct the communication model that respects the distance constrains
   * @param pGenerator the random number generator that is used for reliability
   *          computations
   */
  public CommunicationModel(RandomGenerator pGenerator) {
    this(pGenerator, false);
  }

  /**
   * Register communication user {@link CommunicationUser}. Communication user
   * is registered only when it is also {@link RoadUser}. This is required as
   * communication model depends on elements positions.
   */
  @Override
  public boolean register(CommunicationUser element) {
    if (element == null) {
      throw new IllegalArgumentException("element can not be null");
    }
    boolean result = users.add(element);
    if (!result) {
      return false;
    }
    // callback
    try {
      element.setCommunicationAPI(this);
    } catch (Exception e) {
      // if you miss-behave you don't deserve to use our infrastructure :D
      LOGGER
          .warn("callback for the communication user failed. Unregistering", e);
      users.remove(element);
      return false;
    }
    return true;
  }

  @Override
  public boolean unregister(CommunicationUser element) {
    if (element == null) {
      return false;
    }
    List<Entry<CommunicationUser, Message>> toRemove = new LinkedList<Entry<CommunicationUser, Message>>();
    for (Entry<CommunicationUser, Message> e : sendQueue) {
      if (element.equals(e.getKey())
          || element.equals(e.getValue().getSender())) {
        toRemove.add(e);
      }
    }
    sendQueue.removeAll(toRemove);

    return users.remove(element);
  }

  @Override
  public Class<CommunicationUser> getSupportedType() {
    return CommunicationUser.class;
  }

  @Override
  public void tick(TimeLapse tl) {
    // empty implementation
  }

  @Override
  public void afterTick(TimeLapse tl) {
    long timeMillis = System.currentTimeMillis();
    List<Entry<CommunicationUser, Message>> cache = sendQueue;
    sendQueue = new LinkedList<Entry<CommunicationUser, Message>>();
    for (Entry<CommunicationUser, Message> e : cache) {
      try {
        e.getKey().receive(e.getValue());
        // TODO [bm] add msg delivered event
      } catch (Exception e1) {
        LOGGER.warn("unexpected exception while passing message", e1);
      }
    }
    if (LOGGER.isDebugEnabled()) {
      timeMillis = (System.currentTimeMillis() - timeMillis);
      LOGGER.debug("broadcast lasted for:" + timeMillis);
    }
  }

  @Override
  public void send(CommunicationUser recipient, Message message) {
    if (!users.contains(recipient)) {
      // TODO [bm] implement dropped message EVENT
      return;
    }

    if (new CanCommunicate(message.sender).apply(recipient)) {
      sendQueue.add(SimpleEntry.entry(recipient, message));
    } else {
      // TODO [bm] implement dropped message EVENT
      return;
    }

  }

  @Override
  public void broadcast(Message message) {
    broadcast(message, new CanCommunicate(message.sender));
  }

  @Override
  public void broadcast(Message message, Class<? extends CommunicationUser> type) {
    broadcast(message, new CanCommunicate(message.sender, type));

  }

  private void broadcast(Message message, Predicate<CommunicationUser> predicate) {
    if (!users.contains(message.sender)) {
      return;
    }
    HashSet<CommunicationUser> uSet = new HashSet<CommunicationUser>(
        users.size() / 2);

    for (CommunicationUser u : users) {
      if (predicate.apply(u)) {
        uSet.add(u);
      }
    }

    for (CommunicationUser u : uSet) {
      try {
        sendQueue.add(SimpleEntry.entry(u, message.clone()));
      } catch (CloneNotSupportedException e) {
        LOGGER.error("clonning exception for message", e);
      }
    }
  }

  /**
   * Check if an message from a given sender can be deliver to recipient
   * @see CanCommunicate#apply(CommunicationUser)
   * @author Bartosz Michalik <bartosz.michalik@cs.kuleuven.be>
   * @since 2.0
   */
  class CanCommunicate implements Predicate<CommunicationUser> {

    private final Class<? extends CommunicationUser> clazz;
    private final CommunicationUser sender;
    private Rectangle rec;

    public CanCommunicate(CommunicationUser sender,
        Class<? extends CommunicationUser> clazz) {
      this.sender = sender;
      this.clazz = clazz;
      if (sender.getPosition() != null) {
        rec = new Rectangle(sender.getPosition(), sender.getRadius());
      }
    }

    public CanCommunicate(CommunicationUser sender) {
      this(sender, null);
    }

    @Override
    public boolean apply(CommunicationUser input) {
      if (input == null || rec == null) {
        return false;
      }
      if (clazz != null && !clazz.equals(input.getClass())) {
        return false;
      }
      if (input.equals(sender)) {
        return false;
      }
      final Point iPos = input.getPosition();
      if (!ignoreDistances && !rec.contains(iPos)) {
        return false;
      }
      double prob = input.getReliability() * sender.getReliability();
      double minRadius = Math.min(input.getRadius(), sender.getRadius());
      double rand = generator.nextDouble();
      Point sPos = sender.getPosition();
      return prob > rand
          && (ignoreDistances ? true : Point.distance(sPos, iPos) <= minRadius);
    }
  }

  private static class Rectangle {
    private final double y1;
    private final double x1;
    private final double y0;
    private final double x0;

    public Rectangle(Point p, double radius) {
      x0 = p.x - radius;
      y0 = p.y - radius;
      x1 = p.x + radius;
      y1 = p.y + radius;
    }

    public boolean contains(Point p) {
      if (p == null) {
        return false;
      }
      if (p.x < x0 || p.x > x1) {
        return false;
      }
      if (p.y < y0 || p.y > y1) {
        return false;
      }
      return true;
    }
  }

  protected static class SimpleEntry<K, V> implements Entry<K, V> {

    private final V value;
    private final K key;

    public SimpleEntry(K key, V value) {
      this.key = key;
      this.value = value;
    }

    @Override
    public K getKey() {
      return key;
    }

    @Override
    public V getValue() {
      return value;
    }

    @Override
    public V setValue(V value) {
      return null;
    }

    static <V, K> Entry<V, K> entry(V v, K k) {
      return new SimpleEntry<V, K>(v, k);
    }

  }

}
TOP

Related Classes of rinde.sim.core.model.communication.CommunicationModel$Rectangle

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.