/* * 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 #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 [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; }