# -*- Tcl -*-
# 
# Class for obtaining all link statistics.
# Copyright (c) 2002, Hiroyuki Ohsaki.
# All rights reserved.
# 
# $Id: linkstats.tcl,v 1.4 2005/11/02 09:17:44 oosaki Exp $
# 

# This code is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY.  No author or distributor
# accepts responsibility to anyone for the consequences of using it
# or for whether it serves any particular purpose or works at all,
# unless he says so in writing.  Refer to the GNU Emacs General Public
# License for full details.

# Everyone is granted permission to copy, modify and redistribute
# this code, but only under the conditions described in the
# GNU Emacs General Public License.   A copy of this license is
# supposed to have been given to you along with GNU Emacs so you
# can know your rights and responsibilities.  It should be in a
# file named COPYING.  Among other things, the copyright notice
# and this notice must be preserved on all copies.

# Usage:
# source linkstats.tcl
# $ns init-linkstats START (after defining all links)
# $ns show-linkstats (before ``exit'' would be good)

# Example:
# ----------------------------------------------------------------
# # include LinkStats class
# source linkstats.tcl
# 
# # create a Simulator object
# set ns [new Simulator]
# 
# # source and destination nodes
# set node1 [$ns node]
# set node2 [$ns node]
# 
# # create a link between node1 and node2
# $ns duplex-link $node1 $node2 1.5Mb 10ms DropTail
# $ns queue-limit $node1 $node2 50
# 
# # create TCP connections and FTP agents
# for { set i 1 } { $i <= 10 } { incr i } {
#     set tcp($i) [$ns create-connection TCP/Reno $node1 TCPSink $node2 0]
#     set ftp($i) [$tcp($i) attach-app FTP]
#     $ns at 0 "$ftp($i) start"
# }
# 
# # activate LinkStats after warming-up
# $ns init-linkstats 10.0
# 
# proc finish {} {
#     global ns
#     $ns show-linkstats
#     exit 0
# }
# 
# $ns at 100.0 "finish"
# $ns run
# ----------------------------------------------------------------

Class LinkStats

# create LinkStats object
Simulator instproc init-linkstats {start {interval 0.001}} {
  $self instvar lstat_
  
  set lstat_ [new LinkStats $self $start $interval]
  return $lstat_
}

# display all statistics via invoking LinkStats::show
Simulator instproc show-linkstats {} {
  $self instvar lstat_
  
  $lstat_ show
}

LinkStats instproc init {ns start interval} {
  $self instvar ns_ start_ interval_ qmtbl_
  
  # save instance variables
  set ns_ $ns
  set start_ $start
  set interval_ $interval
  # create queue monitor for all links
  # FIXME: this is too slow; should use `link_' instead
  foreach n1 [$ns all-nodes-list] {
    foreach n2 [$ns all-nodes-list] {
      set link [$ns link $n1 $n2]
      if { $link != "" } {
        set qmtbl_($n1,$n2) [$link init-monitor $ns "" $interval]
        $ns at $start "$qmtbl_($n1,$n2) reset"
      }
    }
  }
}

LinkStats instproc show {} {
  $self instvar ns_ start_ interval_ qmtbl_
  
  set intv [expr [$ns_ now] - $start_]
  puts "**** Link Statistics ****"
  puts "Warm-Up Time\t$start_"
  puts "Simulation Time\t[$ns_ now]"
  puts "Duration\t$intv"
  puts "Intg. Interval\t$interval_"
  foreach n1 [$ns_ all-nodes-list] {
    foreach n2 [$ns_ all-nodes-list] {
      set link [$ns_ link $n1 $n2]
      if { $link != "" } {
        set qmon $qmtbl_($n1,$n2)
        # average queue length in packets
        set integ [$qmon get-pkts-integrator]
        set qavg [expr [$integ set sum_] / $intv]
        # average link utilization
        set bw [$link bw]
        set ba [$qmon set barrivals_]
        set bs [$qmon set bdepartures_]
        set util [expr ($bs + 0.0) * 8 / ($bw * $intv)]
        # average throughput in bit/sec
        set thr [expr ($bs + 0.0) * 8 / $intv]
        # packet loss probability
        set pd [$qmon set pdrops_]
        set pa [$qmon set parrivals_]
	set loss 0
        if { $pa > 0 } {
          set loss [expr ($pd + 0.0) / $pa]
        }
        
        puts "\n---- Node [$n1 id] -> Node [$n2 id] ----"
        puts "Bandwidth\t$bw"
        puts "Prop Delay\t[$link delay]"
        puts "Queue Limit\t[$link qsize]"
        puts "Pkts Rcvd\t$pa"
        puts "Pkts Sent\t[$qmon set pdepartures_]"
        puts "Pkts Lost\t$pd"
        puts "Pkt Loss Pr\t$loss"
        puts "Avg Queue\t$qavg"
        puts "Bytes Rcvd\t$ba"
        puts "Bytes Sent\t$bs"
        puts "Bytes Lost\t[$qmon set bdrops_]"
        puts "Link Util\t$util"
        puts "Throughput\t$thr"
      }
    }
  }
}

