// Sim_unidir_channel.nw -- a unidirectional message passing channel using
// the simjava package in Java
// (c) 1997 by Felix Gaertner <theedge@rbg.informatik.tu-darmstadt.de>
// NOTE: DO NOT EDIT THIS FILE. See Sim_unidir_channel.nw instead.

package simdistalg;

import eduni.simjava.*;
import eduni.simanim.*;
import java.util.*;

/**
 * This is a class to simulate a unidirectional message passing
 * channel within the <tt>simjava</tt> simulation environment that
 * can be faulty. It is used together with it`s companion class
 * <code>Sim_node</code>.
 * @see Sim_pos_entity
 * @see Sim_node
 * @see Sim_message_info
 * @see Sim_faulty_channel
 * @author Felix G&auml;rtner
 */
public class Sim_unidir_channel extends Sim_pos_entity
    implements Sim_faulty_channel {
  // private members:
  /**
   * The rate by which messages are lost in percent (default 0). For
   * example, with a rate of 0.5, 50 percent of the messages that
   * are about to be forwarded by the channel will be lost.
   */
  private float loss_rate = 0;
  /**
   * The rate by which messages are duplicated in percent (default 0).
   * For example, with a rate of 0.33, one third of the messages sent
   * to the channel will be duplicated.
   */
  private float duplicate_rate = 0;
  /**
   * The rate by which messages are reordered provided that there is more
   * than one message in the channel (default 0). For example, a rate of
   * 0.2 will result in reordering faults in 20 percent of the time where
   * more than one message is in the channel.
   */
  private float reordering_rate = 0;
  /**
   * the average delay of a message in simulation time units (default 10).
   * Delay is in the interval [<code>delay - delay_minus</code>,
   * <code>delay + delay_plus</code>] (default [5,15]).
   */
  private int delay = 10;
  /**
   * number of time units that messages can take longer (default 5)
   */
  private int delay_plus = 5;
  /**
   * number of time units that messages can be faster (default 5)
   */
  private int delay_minus = 5;
  /**
   * Hold node and port names that the channel is connected to. Needed
   * because linking ports is done outside of constructors.
   */
  private String inport_nodename;
  private String inport_portname;
  private String outport_nodename;
  private String outport_portname;
  private Sim_port inport;
  private Sim_port outport;
  private void display_params(String q, String s, String state) {
    sim_trace(1, "P " + q + " " + s + " " + state);
  }
  private Vector queue = new Vector(); // message queue
  private boolean broken = false;
  private String state() {
    if (broken) return "broken";
    else return "normal";
  }
  // access methods:
  public float get_loss_rate() {
    return loss_rate;
  }
  /**
   * rates below 0 or above 1 are treated as 0 and 1 respectively.
   */
  public void set_loss_rate(float rate) {
    if (rate < 0.0) rate = 0;
    if (rate > 1.0) rate = 1;
    loss_rate = rate;
  }
  public float get_duplicate_rate() {
    return duplicate_rate;
  }
  /**
   * rates below 0 or above 1 are treated as 0 and 1 respectively.
   */
  public void set_duplicate_rate(float rate) {
    if (rate < 0.0) rate = 0;
    if (rate > 1.0) rate = 1;
    duplicate_rate = rate;
  }
  public float get_reordering_rate() {
    return reordering_rate;
  }
  /**
   * rates below 0 or above 1 are treated as 0 and 1 respectively.
   */
  public void set_reordering_rate(float rate) {
    if (rate < 0.0) rate = 0;
    if (rate > 1.0) rate = 1;
    reordering_rate = rate;
  }
  public int get_delay() {
    return delay;
  }
  /**
   * A value below 0 is treated as zero.
   */
  public void set_delay(int del) {
    if (del<0) del = 0;
    delay = del;
  }
  public int get_delay_plus() {
    return delay_plus;
  }
  /**
   * A value below 0 is treated as zero.
   */
  public void set_delay_plus(int del) {
    if (del<0) del = 0;
    delay_plus = del;
  }
  public int get_delay_minus() {
    return delay_minus;
  }
  /**
   * A value below 0 is treated as zero.
   */
  public void set_delay_minus(int del) {
    if (del<0) del = 0;
    delay_minus = del;
  }
  // constructors:
  /**
   * Create a new channel using default values.
   * @param in_nodename Name of the node that issued messages into the channel
   * @param in_portname Name of the port that connects to the channel's inport
   * @param out_nodename Name of the node that receives messages over the channel
   * @param out_portname Name of the port that connects to the channel's outport
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
                        String out_nodename, String out_portname) {
    super(in_nodename + "YY" + out_nodename);
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new port `" + inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin");
    add_port(inport);
    debug("creating new port `" + inport_nodename + "YY" + outport_nodename + "ZZout"
        + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout");
    add_port(outport);
  }
  /**
   * Create a channel using special values for private members.
   * @param message_loss_rate rate by which messages are lost (in percent)
   * @param message_duplicate_rate rate by which messages are duplicated (in percent)
   * @param message_reordering_rate rate by which messages are reordered (provided there is more than one message in the channel)
   * @param message_delay average delay of a message (in simulation time units)
   * @param message_delay_plus number of units by which messages can be slower
   * @param message_delay_minus number of units by which messages can be faster
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
      String out_nodename, String out_portname,
      float message_loss_rate,
      float message_duplicate_rate,
      float message_reordering_rate,
      int message_delay,
      int message_delay_plus,
      int message_delay_minus) {
    super(in_nodename + "YY" + out_nodename);
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new port `" + inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin");
    add_port(inport);
    debug("creating new port `" + inport_nodename + "YY" + outport_nodename + "ZZout"
        + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout");
    add_port(outport);
    set_loss_rate(message_loss_rate);
    set_duplicate_rate(message_duplicate_rate);
    set_reordering_rate(message_reordering_rate);
    set_delay(message_delay);
    set_delay_plus(message_delay_plus);
    set_delay_minus(message_delay_minus);
  }
  /**
   * Create an animated channel with default values.
   * @param x x-coordinate of channel in simulation
   * @param y y-coordinate of channel in simulation
   * @param inport_side side of the ``in'' port of the channel
   *                    (use values <code>Anim_port.RIGHT</code> etc.)
   * @param outport_side side of the ``out'' port of the channel
   * @param stack_side side at which to display the message stack
   * @see eduni.simanim.Anim_port
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
      String out_nodename, String out_portname, int x, int y,
      int inport_side, int outport_side, int stack_side) {
    super(in_nodename + "YY" + out_nodename, "channel", x, y);
    debug("creating new animated node `" + in_nodename + "YY" + out_nodename + "' (reference is " + this + ")");
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin", "in-port",
        inport_side, 8);
    add_port(inport);
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZout" + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout", "out-port",
        outport_side, 8);
    add_port(outport);
    add_param(new Anim_param("q", Anim_param.VALUE, "", 0, 0));
    add_param(new Anim_param("s", Anim_param.VALUE, "", 8, 16));
    add_param(new Anim_param("state", Anim_param.STATE, "normal", 0, 0));
  }
  /**
   * Create an animated channel using special values for private members.
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
      String out_nodename, String out_portname, int x, int y,
      int inport_side, int outport_side,
      float message_loss_rate,
      float message_duplicate_rate,
      float message_reordering_rate,
      int message_delay,
      int message_delay_plus,
      int message_delay_minus,
      int side) {
    super(in_nodename + "YY" + out_nodename, "channel", x, y);
    debug("creating new animated node `" + in_nodename + "YY" + out_nodename + "' (reference is " + this + ")");
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin", "in-port",
        inport_side, 8);
    add_port(inport);
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZout" + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout", "out-port",
        outport_side, 8);
    add_port(outport);
    add_param(new Anim_param("q", Anim_param.VALUE, "", 0, 0));
    add_param(new Anim_param("s", Anim_param.VALUE, "", 8, 16));
    add_param(new Anim_param("state", Anim_param.STATE, "normal", 0, 0));
    set_loss_rate(message_loss_rate);
    set_duplicate_rate(message_duplicate_rate);
    set_reordering_rate(message_reordering_rate);
    set_delay(message_delay);
    set_delay_plus(message_delay_plus);
    set_delay_minus(message_delay_minus);
  }
  /**
   * Create an animated channel with default values and <code>Sim_node</code>s
   * as connected entities.
   * @see Sim_node
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
      String out_nodename, String out_portname,
      int side) {
    super(in_nodename + "YY" + out_nodename, "channel",
      center_pos(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos()) +
       channel_delta_x(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()),
      center_pos(
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()) +
       channel_delta_y(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()));
    // do calculations again
    int x1 = Sim_pos_entity.name_to_node(in_nodename).get_x_pos();
    int y1 = Sim_pos_entity.name_to_node(in_nodename).get_y_pos();
    int x2 = Sim_pos_entity.name_to_node(out_nodename).get_x_pos();
    int y2 = Sim_pos_entity.name_to_node(out_nodename).get_y_pos();
    int my_x = center_pos(x1, x2) + channel_delta_x(x1,y1,x2,y2);
    int my_y = center_pos(y1, y2) + channel_delta_y(x1,y1,x2,y2);

    debug("placing channel at " + my_x + "," + my_y);

    debug("creating new animated node `" + in_nodename + "YY" + out_nodename +
        "'; reference=" + this);
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    int inport_side = determine_side(my_x, my_y, x1, y1);
    int outport_side = determine_side(my_x, my_y, x2, y2);
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin", "in-port",
        inport_side, 8);
    add_port(inport);
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZout" + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout", "out-port",
        outport_side, 8);
    add_port(outport);
    add_param(new Anim_param("q", Anim_param.VALUE, "", 0, 0));
    add_param(new Anim_param("s", Anim_param.VALUE, "", 8, 16));
    add_param(new Anim_param("state", Anim_param.STATE, "normal", 0, 0));
  }
  /**
   * Create an animated channel using special values for private members
   * and <code>Sim_node</code>s as connected entities.
   * @see Sim_node
   */
  public Sim_unidir_channel(String in_nodename, String in_portname,
      String out_nodename, String out_portname,
      float message_loss_rate,
      float message_duplicate_rate,
      float message_reordering_rate,
      int message_delay,
      int message_delay_plus,
      int message_delay_minus,
      int side) {
    super(in_nodename + "YY" + out_nodename, "channel",
      center_pos(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos()) +
       channel_delta_x(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()),
      center_pos(
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()) +
       channel_delta_y(
         Sim_pos_entity.name_to_node(in_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(in_nodename).get_y_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_x_pos(),
         Sim_pos_entity.name_to_node(out_nodename).get_y_pos()));
    // do calculations again
    int x1 = Sim_pos_entity.name_to_node(in_nodename).get_x_pos();
    int y1 = Sim_pos_entity.name_to_node(in_nodename).get_y_pos();
    int x2 = Sim_pos_entity.name_to_node(out_nodename).get_x_pos();
    int y2 = Sim_pos_entity.name_to_node(out_nodename).get_y_pos();
    int my_x = center_pos(x1, x2) + channel_delta_x(x1,y1,x2,y2);
    int my_y = center_pos(y1, y2) + channel_delta_y(x1,y1,x2,y2);

    debug("placing channel at " + my_x + "," + my_y);

    debug("creating new animated node `" + in_nodename + "YY" + out_nodename +
        "'; reference=" + this);
    inport_nodename = in_nodename;
    inport_portname = in_portname;
    outport_nodename = out_nodename;
    outport_portname = out_portname;
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZin" + "'");
    int inport_side = determine_side(my_x, my_y, x1, y1);
    int outport_side = determine_side(my_x, my_y, x2, y2);
    inport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZin", "in-port",
        inport_side, 8);
    add_port(inport);
    debug("creating new animated port `" +
        inport_nodename + "YY" + outport_nodename + "ZZout" + "'");
    outport = new Sim_port(inport_nodename + "YY" + outport_nodename + "ZZout", "out-port",
        outport_side, 8);
    add_port(outport);
    add_param(new Anim_param("q", Anim_param.VALUE, "", 0, 0));
    add_param(new Anim_param("s", Anim_param.VALUE, "", 8, 16));
    add_param(new Anim_param("state", Anim_param.STATE, "normal", 0, 0));
    set_loss_rate(message_loss_rate);
    set_duplicate_rate(message_duplicate_rate);
    set_reordering_rate(message_reordering_rate);
    set_delay(message_delay);
    set_delay_plus(message_delay_plus);
    set_delay_minus(message_delay_minus);
  }
  // public methods:
  /**
   * Connect the ports of the channel to it's neighbours. (Call only
   * when all ports have been created.)
   */
  public void connect() {
    debug("linking port `" + inport_portname + "' on node `" +
        inport_nodename + "' to port `" +
        inport_nodename + "YY" + outport_nodename + "ZZin" + "' on node `" +
        inport_nodename + "YY" + outport_nodename);
    Sim_system.link_ports( inport_nodename, inport_portname,
        inport_nodename + "YY" + outport_nodename, inport_nodename + "YY" + outport_nodename + "ZZin");
    debug("linking port `" + outport_portname + "' on node `" +
        outport_nodename + "' to port `" +
        inport_nodename + "YY" + outport_nodename + "ZZout" + "' on node `" +
        inport_nodename + "YY" + outport_nodename);
    Sim_system.link_ports( outport_nodename, outport_portname,
        inport_nodename + "YY" + outport_nodename, inport_nodename + "YY" + outport_nodename + "ZZout");
  }
  public void down() {
    debug("going down");
    broken = true;
  }
  public void up() {
    debug("going up");
    broken = false;
  }
  public void reset() {
    debug("resetting the channel");
    broken = false;
    queue.removeAllElements();
    queue.addElement(null);
  }
  public void perturb() {
    debug("perturbing channel state...");
    // TODO
  }
  // private methods
  public void body() {
    String transient_state = state();
    Sim_normal_obj msg_delay =
      new Sim_normal_obj("message delay", delay,
          Math.min(delay_plus, delay_minus), 1);
    String q = "."; // message queue as a [[String]]
    String q_size = "0"; // message queue size as a [[String]]
    Sim_uniform_obj fault_generator =
      new Sim_uniform_obj("fault generator", 0.0, 1.0, 1);
    queue.addElement(null); // add one dummy element

    while (true) {
      Sim_event ev = new Sim_event();
      debug("in channel (ref=" + this + ") main loop (broken=" + broken + ")");
      transient_state = state();
      if (queue.size() <= 1) {
        q = ".";
      } else { // size > 1
        Sim_event e;
        Sim_message_info info;
        q = "";
        for (int c = 1; c < queue.size(); c++) {
          e = (Sim_event) queue.elementAt(c-1);
          info = (Sim_message_info) e.get_data();
          debug("accessing " + (c-1) + " of queue = " +
              info.get_data() + e +" (size=" + queue.size() + ")");
          String data_string = info.get_sdata();
          if (info.get_sdata() == null) {
            data_string = "?";
          }
          q = q + "<" + c + "," + data_string + ">";
        }
      }
      q_size = "" + (queue.size()-1);
      display_params(q, q_size, transient_state);
      if (sim_waiting() > 0) {
        // get first event
        sim_select(Sim_system.SIM_ANY, ev);
        debug("queuing message: " + ev.get_data() + "(at pos. " +
            (queue.size()-1) + ")");
        queue.insertElementAt(ev, queue.size()-1 );
        if (!broken) {
          if (queue.size() <= 1) {
            q = ".";
          } else { // size > 1
            Sim_event e;
            Sim_message_info info;
            q = "";
            for (int c = 1; c < queue.size(); c++) {
              e = (Sim_event) queue.elementAt(c-1);
              info = (Sim_message_info) e.get_data();
              debug("accessing " + (c-1) + " of queue = " +
                  info.get_data() + e +" (size=" + queue.size() + ")");
              String data_string = info.get_sdata();
              if (info.get_sdata() == null) {
                data_string = "?";
              }
              q = q + "<" + c + "," + data_string + ">";
            }
          }
          q_size = "" + (queue.size()-1);
          if (fault_generator.sample() < duplicate_rate) {
            queue.insertElementAt( ev.clone(), queue.size()-1);
            if (queue.size() <= 1) {
              q = ".";
            } else { // size > 1
              Sim_event e;
              Sim_message_info info;
              q = "";
              for (int c = 1; c < queue.size(); c++) {
                e = (Sim_event) queue.elementAt(c-1);
                info = (Sim_message_info) e.get_data();
                debug("accessing " + (c-1) + " of queue = " +
                    info.get_data() + e +" (size=" + queue.size() + ")");
                String data_string = info.get_sdata();
                if (info.get_sdata() == null) {
                  data_string = "?";
                }
                q = q + "<" + c + "," + data_string + ">";
              }
            }
            q_size = ".";
            transient_state = "duplicate";
            debug("Duplicated message");
          }
          display_params(q, q_size, transient_state);
        }
      }
      if (!broken) {
        if ((queue.size() >= 3) && (fault_generator.sample() < reordering_rate)) {
          // select position in queue in 0..real_size-1
          int sel = (int)(fault_generator.sample()*(queue.size() - 2));
          debug("position " + sel + " selected for reordering");
          Object tmp = queue.elementAt(sel);
          queue.setElementAt(queue.elementAt(sel+1), sel);
          queue.setElementAt(tmp, sel + 1);
          if (queue.size() <= 1) {
            q = ".";
          } else { // size > 1
            Sim_event e;
            Sim_message_info info;
            q = "";
            for (int c = 1; c < queue.size(); c++) {
              e = (Sim_event) queue.elementAt(c-1);
              info = (Sim_message_info) e.get_data();
              debug("accessing " + (c-1) + " of queue = " +
                  info.get_data() + e +" (size=" + queue.size() + ")");
              String data_string = info.get_sdata();
              if (info.get_sdata() == null) {
                data_string = "?";
              }
              q = q + "<" + c + "," + data_string + ">";
            }
          }
          q_size = ".";
          transient_state = "reorder";
          display_params(q, q_size, transient_state);
          debug("reordered message at positions " + sel + " and " + (sel+1));
        }
      }
      if ((queue.size() > 1) && (!broken)) {
        ev = (Sim_event) queue.elementAt(0);
        double current_delay = Sim_system.clock() - ev.event_time();
        debug("delay of msg currently " + current_delay);
        // get a new delay sample and look if it's okay
        double proposed_delay = msg_delay.sample();
        proposed_delay = Math.max(delay-delay_minus, proposed_delay);
        proposed_delay = Math.min(delay+delay_plus, proposed_delay);
        debug("proposed delay is " + proposed_delay);
        if (current_delay >= proposed_delay) {
          debug("about to send " + ev);
          queue.removeElementAt(0);
          if (queue.size() <= 1) {
            q = ".";
          } else { // size > 1
            Sim_event e;
            Sim_message_info info;
            q = "";
            for (int c = 1; c < queue.size(); c++) {
              e = (Sim_event) queue.elementAt(c-1);
              info = (Sim_message_info) e.get_data();
              debug("accessing " + (c-1) + " of queue = " +
                  info.get_data() + e +" (size=" + queue.size() + ")");
              String data_string = info.get_sdata();
              if (info.get_sdata() == null) {
                data_string = "?";
              }
              q = q + "<" + c + "," + data_string + ">";
            }
          }
          display_params(q, q_size, transient_state);
          if (!(fault_generator.sample() < loss_rate)) {
            sim_schedule(outport, 0.0, ev.get_tag(), ev.get_data());
            sim_trace(1, "S " + inport_nodename + "YY" + outport_nodename + "ZZout" + " " +
                "<" + ((Sim_message_info)ev.get_data()).get_sdata() + ">");
          } else {
            transient_state = "loose";
            q_size = ".";
            display_params(q, q_size, transient_state);
            debug("lost a message");
          }
        }
      }
      // how often sample input channels
      sim_hold(1.0);
    }
  }
  private final static int channel_delta = 10;
  public static int center_pos(int x1, int x2) {
    return (x1 + x2) / 2;
  }
  public static int determine_side(int own_x, int own_y,
      int other_x, int other_y) {
    //System.out.println("det_side(" + own_x + "," + own_y + ","
    //    + other_x + "," + other_y + ") called");
    int delta_x = other_x - own_x;
    int delta_y = other_y - own_y;
    if (Math.abs(delta_x) < Math.abs(delta_y)) {
      if (delta_y <= 0) {
        //System.out.println("TOP");
        return Anim_port.TOP;
      } else { // delta_y > 0
        //System.out.println("BOTTOM");
        return Anim_port.BOTTOM;
      }
    } else { // (Math.abs(delta_x) >= Math.abs(delta_y))
      if (delta_x <= 0) {
        //System.out.println("LEFT");
        return Anim_port.LEFT;
      } else { // delta_x > 0
        //System.out.println("RIGHT");
        return Anim_port.RIGHT;
      }
    }
  }
  public static int channel_delta_x(int from_x, int from_y,
      int to_x, int to_y) {
    int ret = 0; // return value
    int pointing_to = determine_side(from_x, from_y, to_x, to_y);
    switch (pointing_to) {
      case Anim_port.LEFT:
      case Anim_port.BOTTOM:
        ret = channel_delta;
        break;
      case Anim_port.RIGHT:
      case Anim_port.TOP:
        ret = - channel_delta;
        break;
     }
     return ret;
  }
  public static int channel_delta_y(int from_x, int from_y,
      int to_x, int to_y) {
    int ret = 0; // return value
    int pointing_to = determine_side(from_x, from_y, to_x, to_y);
    switch (pointing_to) {
      case Anim_port.LEFT:
      case Anim_port.TOP:
        ret = channel_delta;
        break;
      case Anim_port.RIGHT:
      case Anim_port.BOTTOM:
        ret = - channel_delta;
        break;
     }
     return ret;
  }
  public void debug(String msg) {
    // System.out.println("<" + inport_nodename + "YY" + outport_nodename + ">:" + msg);
  }
}

