/*
* Copyright (c) 1991,1993 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Systems
* Engineering Group at Lawrence Berkeley Laboratory.
* 4. Neither the name of the University nor of the Laboratory may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#) $Header: /cvsroot/nsnam/nam-1/edge.cc,v 1.54 2003/10/11 22:56:49 xuanc Exp $ (LBL)
*/
#include <tclcl.h>
#include "config.h"
#include "sincos.h"
#include "edge.h"
#include "queuehandle.h"
#include "node.h"
#include "packet.h"
#include "view.h"
#include "netview.h"
#include "psview.h"
#include "transform.h"
#include "paint.h"
#include "monitor.h"
#include "agent.h"
#include "lossmodel.h"
//----------------------------------------------------------------------
// Wrapper for tcl creation of Edges (links)
//----------------------------------------------------------------------
static class EdgeClass : public TclClass {
public:
EdgeClass() : TclClass("Edge") {}
TclObject * create(int argc, const char*const* argv) {
// set <Edge> [new Node node_id type size]
Edge * edge;
double bandwidth, delay, length, angle, label_size;
bandwidth = strtod(argv[4], NULL);
delay = strtod(argv[5], NULL);
length = strtod(argv[6], NULL);
angle = strtod(argv[7], NULL);
label_size = strtod(argv[8], NULL);
edge = new Edge(label_size, bandwidth, delay, length, angle);
return edge;
}
} class_edge;
//----------------------------------------------------------------------
// Edge::Edge(double ps, double bw, double delay,
// double length, double angle)
// - Creates an edge that is not attached to any nodes.
// Used by nam editor when loading in an existing enam file.
// - With this constructor, the nodes need to be added
// to this edge using Edge::attachNodes()
//----------------------------------------------------------------------
Edge::Edge(double ps, double bw, double delay,
double length, double angle) :
Animation(0,0) {
psize_ = ps;
angle_ = angle;
bandwidth_ = bw;
delay_ = delay;
length_ = length;
state_ = UP;
paint_ = Paint::instance()->thick();
setupDefaults();
}
//----------------------------------------------------------------------
// Edge::Edge(Node* src, Node* dst, double ps, double bw,
// double delay, double length, double angle)
// - creates an edge which is a one way network link
// - the edge is automaticall attached to nodes src and dst
//----------------------------------------------------------------------
Edge::Edge(Node * src, Node * dst, double ps, double bw,
double _delay, double length, double angle) :
Animation(0,0),
psize_(ps),
angle_(angle),
bandwidth_(bw),
delay_(_delay),
length_(length) {
setupDefaults();
src_ = src->num();
dst_ = dst->num();
start_ = src;
neighbor_ = dst;
paint_ = Paint::instance()->thick();
if (length_ == 0) {
length_ = delay_;
}
}
//----------------------------------------------------------------------
// Edge::Edge(Node* src, Node* dst, double ps, double bw,
// double delay, double length, double angle, int ws)
// - creates an edge for wireless
//----------------------------------------------------------------------
Edge::Edge(Node* src, Node* dst, double ps,
double bw, double _delay, double length, double angle, int ws) :
Animation(0,0),
psize_(ps),
angle_(angle),
bandwidth_(bw),
delay_(_delay),
length_(length),
wireless_(ws)
{
setupDefaults();
src_ = src->num();
dst_ = dst->num();
start_ = src;
neighbor_ = dst;
paint_ = Paint::instance()->thick();
if (length_ == 0) {
length_ = delay_;
}
}
//----------------------------------------------------------------------
// void
// Edge::setupDefaults()
//----------------------------------------------------------------------
void
Edge::setupDefaults() {
x0_ = 0.0;
y0_ = 0.0;
state_ = UP;
packets_ = NULL;
no_of_packets_ = 0;
last_packet_ = NULL;
marked_ = 0;
visible_ = 1;
dlabel_ = NULL;
dcolor_ = NULL;
direction_ = 0;
used_ = 0;
// Set initial (x0,y0), (x1,y1) so that they reflect the current
// angle. This enables netmodel to share the same placeEdge() and
// placeAgent() with AutoNetModel.
SINCOSPI(angle_, &x1_, &y1_);
lossmodel_ = NULL;
queue_handle_ = NULL;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
Edge::~Edge() {
if (queue_handle_) {
delete queue_handle_;
}
}
//----------------------------------------------------------------------
// void
// Edge::attachNodes(Node * source, Node * destination)
//----------------------------------------------------------------------
void
Edge::attachNodes(Node * source, Node * destination) {
src_ = source->num();
dst_ = destination->num();
start_ = source;
neighbor_ = destination;
if (length_ == 0) {
length_ = delay_;
}
start_->add_link(this);
}
//----------------------------------------------------------------------
// void
// Edge::addLossModel(LossModel * lossmodel)
//----------------------------------------------------------------------
void
Edge::addLossModel(LossModel * lossmodel) {
if (!lossmodel_) {
lossmodel_ = lossmodel;
lossmodel_->attachTo(this);
lossmodel_->place();
}
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
LossModel *
Edge::getLossModel() {
return lossmodel_;
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
Edge::clearLossModel() {
// Delete previous loss model if it exists
if (lossmodel_) {
lossmodel_ = NULL;
}
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
Edge::addQueueHandle(QueueHandle * q_handle) {
queue_handle_ = q_handle;
queue_handle_->place();
}
//----------------------------------------------------------------------
//----------------------------------------------------------------------
float Edge::distance(float x, float y) const
{
// TODO: Look it up when back home...
matrix_.imap(x, y);
return y;
}
//----------------------------------------------------------------------
// inline double
// Edge::delay() const
// - delay_ is stored in milliseconds
// - this function returns the delay in seconds
//
// - look at tcl/netModel.tcl layout_link and layout_lan
// for the conversion to milliseconds
//
// - delay_ is kept in milliseconds so that Edge::info()
// is displayed in millseconds and so that delay can be
// entered in milliseconds by the nam editor
//
//----------------------------------------------------------------------
double
Edge::delay() const {
return (delay_/1000.0);
}
void Edge::init_color(const char *clr)
{
color(clr);
dcolor(clr);
oldPaint_ = paint_;
}
void Edge::set_down(char *color)
{
// If current color is down, don't change it again.
// Assuming only one down color. User can change this behavior
// by adding tcl code for link-up and link-down events.
if (state_ == UP) {
int pno = Paint::instance()->lookup(color, 3);
oldPaint_ = paint_;
paint_ = pno;
state_ = DOWN;
}
}
void Edge::set_up()
{
if (state_ == DOWN) {
state_ = UP;
toggle_color();
}
}
void Edge::place(double x0, double y0, double x1, double y1)
{
x0_ = x0;
y0_ = y0;
x1_ = x1;
y1_ = y1;
double dx = x1 - x0;
double dy = y1 - y0;
/*XXX*/
// delay should not be equal to edge's position
// delay_ = sqrt(dx * dx + dy * dy);
matrix_.clear();
matrix_.rotate((180 / M_PI) * atan2(dy, dx));
matrix_.translate(x0, y0);
if (x1>x0) {
bb_.xmin = x0; bb_.xmax = x1;
} else {
bb_.xmin = x1; bb_.xmax = x0;
}
if (y1>y0) {
bb_.ymin = y0; bb_.ymax = y1;
} else {
bb_.ymin = y1; bb_.ymax = y0;
}
eb_.xmin = -0.1 * start_->size();
eb_.xmax = sqrt(dx*dx+dy*dy) + 0.1*neighbor_->size();
eb_.ymin = -0.1 * start_->size();
eb_.ymax = 0.1 * start_->size();
if (queue_handle_) {
queue_handle_->place();
}
if (lossmodel_) {
lossmodel_->place();
}
}
void Edge::AddPacket(Packet *p)
{
p->next(packets_);
if (packets_!=NULL)
packets_->prev(p);
p->prev(NULL);
packets_=p;
if (last_packet_==NULL)
last_packet_=p;
no_of_packets_++;
#define PARANOID
#ifdef PARANOID
int ctr=0;
for(Packet *tp=packets_;tp!=NULL;tp=tp->next()) {
ctr++;
if (ctr>no_of_packets_)
abort();
}
if (last_packet_->next()!=NULL)
abort();
#ifdef DEBUG
printf("AddPacket: %d->%d OK\n", src_, dst_);
#endif
#endif
#undef PARANOID
}
void Edge::arrive_packet(Packet *p, double atime)
{
/*Trigger any arrival event at the node*/
neighbor_->arrive_packet(p, this, atime);
}
void Edge::DeletePacket(Packet *p)
{
/*Trigger any deletion event at the node*/
if (neighbor_) neighbor_->delete_packet(p);
else return ;
no_of_packets_--;
if (last_packet_==p) {
/*Normal case - it's the last packet*/
last_packet_ = p->prev();
if (packets_!=p)
p->prev()->next(NULL);
else
packets_=NULL;
} else {
/*Abnormal case - need to search the list*/
Packet *tp;
tp=packets_;
while((tp!=p)&&(tp!=NULL)) {
tp=tp->next();
}
if (tp==p) {
if (tp==packets_)
/*it was the first packet*/
packets_=tp->next();
else
tp->prev()->next(tp->next());
if (tp==last_packet_) {
/*this shouldn't ever happen*/
fprintf(stderr, "**it happened\n");
last_packet_=tp->prev();
} else
tp->next()->prev(tp->prev());
}
}
#define PARANOID
#ifdef PARANOID
int ctr=0;
for(Packet *tp=packets_;tp!=NULL;tp=tp->next()) {
ctr++;
if (ctr>no_of_packets_) abort();
}
if ((last_packet_!=NULL)&&(last_packet_->next()!=NULL)) abort();
#ifdef DEBUG
printf("DeletePacket: %d->%d OK\n", src_, dst_);
#endif
#endif
#undef PARANOID
}
void Edge::dlabel(const char* name)
{
if (name[0] == 0) {
if (dlabel_) {
delete []dlabel_;
dlabel_ = 0;
}
return;
}
if (dlabel_)
delete []dlabel_;
dlabel_ = new char[strlen(name) + 1];
strcpy(dlabel_, name);
}
void Edge::dcolor(const char* name)
{
if (name[0] == 0) {
if (dcolor_) {
delete []dcolor_;
dcolor_ = 0;
}
return;
}
if (dcolor_)
delete []dcolor_;
dcolor_ = new char[strlen(name) + 1];
strcpy(dcolor_, name);
}
void Edge::direction(const char* name)
{
if (name[0] == 0) {
if (direction_)
direction_ = 0;
return;
}
if (!strcmp(name, "SOUTH")) direction_ = 1;
else if (!strcmp(name, "NORTH")) direction_ = 2;
else if (!strcmp(name, "EAST")) direction_ = 3;
else if (!strcmp(name, "WEST")) direction_ = 4;
else direction_ = 2;
}
void Edge::draw(View* view, double now) {
if (visible()) {
view->line(x0_, y0_, x1_, y1_, paint_);
}
if (monitor_!=NULL)
monitor_->draw(view, (x0_+x1_)/2, (y0_+y1_)/2);
if (dlabel_ == 0)
return;
// Draw dynamic label
switch (direction_) {
case 0:
view->string((x0_+x1_)/2.0, (y0_+y1_)/2.0+psize_*1.5,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
case 1:
view->string((x0_+x1_)/2.0, (y0_+y1_)/2.0-psize_*1.5,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
case 2:
view->string((x0_+x1_)/2.0, (y0_+y1_)/2.0+psize_*1.5,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
case 3:
view->string((x0_+x1_)/2.0-psize_*1.5, (y0_+y1_)/2.0,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
case 4:
view->string((x0_+x1_)/2.0+psize_*1.5, (y0_+y1_)/2.0,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
default:
view->string((x0_+x1_)/2.0, (y0_+y1_)/2.0+psize_*1.5,
psize_*2.3, dlabel_, direction_, dcolor_);
break;
}
}
void Edge::reset(double)
{
// paint_ = Paint::instance()->thick();
no_of_packets_=0;
packets_=NULL;
last_packet_=NULL;
}
int Edge::inside(double, float px, float py) const
{
// matrix_.imap(px, py);
// return eb_.inside(px, py);
return inside(px, py);
}
int Edge::inside(float px, float py) const
{
matrix_.imap(px, py);
return eb_.inside(px, py);
}
const char* Edge::info() const
{
static char text[128];
sprintf(text, "Link: %d<->%d\n Bandwidth: %g Mbits/sec\n Delay: %g ms\n",
src_, dst_, bandwidth_, delay_);
return (text);
}
const char* Edge::property() {
rgb *color;
color=Paint::instance()->paint_to_rgb(paint_);
static char text[256];
char *p;
// object type and id
p = text;
if (!wireless_) {
sprintf(text, "{\"Link %d-%d\" title title \"Link %d-%d\"} ",
src_, dst_, src_, dst_);
} else {
sprintf(text, "{\"Wireless Link %d-%d\" title title \"Wireless Link %d-%d\"} ",
src_, dst_, src_, dst_);
}
p = &text[strlen(text)];
sprintf(p, "{Color color_ color %s} ", color->colorname); // color & value
// label
p = &text[strlen(text)];
if (dlabel_) {
sprintf(p, "{Label dlabel_ text %s} ", dlabel_);
} else {
sprintf(p, "{Label dlabel_ text } ");
}
//p = &text[strlen(text)];
//sprintf(p, "{LABEL-DIRECTION %d} ", direction_); // label-direction $ value
// Link Bandwidth
p = &text[strlen(text)];
sprintf(p, "{\"Bandwidth (Mbits per second)\" bandwidth_ text %-f} ", bandwidth_);
// Link Delay
p = &text[strlen(text)];
sprintf(p, "{\"Delay (ms)\" delay_ text %-f} ", delay_);
return(text);
}
const char* Edge::getname() const
{
static char text[128];
sprintf(text, "l %d %d",
src_, dst_);
return (text);
}
void Edge::monitor(Monitor *m, double /*now*/, char *result, int /*len*/)
{
monitor_=m;
sprintf(result, "link %d-%d:\n bw: %g Mbits/sec\n delay: %g ms",
src_, dst_, bandwidth_, delay_);
return;
}
int Edge::save(FILE *file)
{
rgb *color;
color=Paint::instance()->paint_to_rgb(paint_);
char state[10];
double angle,length;
float dx,dy;
//Edges in the tracefile are bidirectional, so don't need to save
//both unidirectional edges
if (src_>dst_) return 0;
if (state_==UP)
strcpy(state, " -S UP");
else if (state_==DOWN)
strcpy(state, " -S DOWN");
else
state[0]='\0';
if (angle_<0)
angle=180*(angle_+2*M_PI)/M_PI;
else
angle=180*(angle_/M_PI);
dx=x0_-x1_;
dy=y0_-y1_;
length=sqrt(dx*dx+dy*dy);
// printf("%d->%d, angle=%f (%fdeg)\n", src_, dst_, angle_, angle);
fprintf(file, "l -t * -s %d -d %d -r %f -D %f -c %s -o %.1fdeg -l %f%s\n",
src_, dst_, bandwidth_, delay_, color->colorname,angle, length, state);
return 0;
}
int Edge::saveAsEnam(FILE *file)
{
rgb *color;
color=Paint::instance()->paint_to_rgb(paint_);
char state[10];
double angle,length;
float dx,dy;
// XXX Should never set edge size outside scale_estimate()!!!!
// psize_ = 25.0;
// Edges in the tracefile are bidirectional, so don't need to save
// both unidirectional edges
if (src_>dst_) return 0;
if (state_==UP)
strcpy(state, " -S UP");
else if (state_==DOWN)
strcpy(state, " -S DOWN");
else
state[0]='\0';
if (angle_<0)
angle=180*(angle_+2*M_PI)/M_PI;
else
angle=180*(angle_/M_PI);
dx=x0_-x1_;
dy=y0_-y1_;
length=sqrt(dx*dx+dy*dy);
// printf("%d->%d, angle=%f (%fdeg)\n", src_, dst_, angle_, angle);
fprintf(file, "##l -t * -s %d -d %d -r %f -D %f -c %s -o %.1fdeg -l %f%s -b %s\n",
src_, dst_, bandwidth_, delay_, color->colorname,angle, length, state, dlabel_);
return 0;
}
//----------------------------------------------------------------------
// int
// Edge::writeNsScript(FILE *file)
// - Edges in the tracefile are bidirectional, so don't need to save
// both unidirectional edges
//----------------------------------------------------------------------
int
Edge::writeNsScript(FILE *file) {
rgb *color;
double angle,length;
float dx,dy;
color = Paint::instance()->paint_to_rgb(paint_);
if (angle_ < 0) {
angle = 180 * (angle_ + 2 * M_PI) / M_PI;
} else {
angle = 180 * (angle_ / M_PI);
}
dx = x0_ - x1_;
dy = y0_ - y1_;
length = sqrt(dx * dx + dy * dy);
// Create the link
if (queue_handle_) {
//fprintf(file, "$ns simplex-link $node(%d) $node(%d) %f %f DropTail\n",
fprintf(file, "$ns simplex-link $node(%d) $node(%d) %fMb %fms %s\n",
src_, dst_, bandwidth_, delay_, queue_handle_->name());
} else {
fprintf(file, "$ns simplex-link $node(%d) $node(%d) %fMb %fms DropTail\n",
src_, dst_, bandwidth_, delay_);
}
// Add a packet queue
fprintf(file, "$ns simplex-link-op $node(%d) $node(%d) queuePos 0.5\n",
src_, dst_);
// Set different options for the link
// - Color
fprintf(file, "$ns simplex-link-op $node(%d) $node(%d) color %s\n",
src_, dst_, color->colorname);
// - Orientation
fprintf(file, "$ns simplex-link-op $node(%d) $node(%d) orient %.1fdeg\n",
src_, dst_, angle);
if (dlabel_){
// - label
fprintf(file, "$ns simplex-link-op $node(%d) $node(%d) label %s\n",
src_, dst_, dlabel_);
}
if (direction_!=0) {
// - label orientation
fprintf(file, "$ns simplex-link-op $node(%d) $node(%d) label-at %d\n",
src_, dst_, direction_);
}
if (queue_handle_) {
queue_handle_->writeNsScript(file);
}
fprintf(file, "\n");
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1