# -*- Tcl -*-
# 
# Class for realtime monitoring of link statistics
# Copyright (c) 2002, Hiroyuki Ohsaki.
# All rights reserved.
# 
# $Id: linkmonitor.tcl,v 1.6 2005/11/02 09:17:41 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 linkmonitor.tcl
# $ns init-linkmonitor LINK START INTERVAL

# NOTE: For obtaining the best results, parameters are carefully
# chosen.  In most cases, ``link'' should be one of congested links
# (e.g., the bottleneck link).  ``start'' must be positive, and must
# be large enough for removing warm-up effect from measurement results
# and also for fast convergence.  ``interval'' should be chosen by
# taking account of a trade-off between measurement accuracy and
# computational burden.

# Example:
# ----------------------------------------------------------------
# # include LinkMonitor class
# source linkmonitor.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 LinkMonitor after warming-up
# $ns init-linkmonitor [$ns link $node1 $node2] 10.0
# 
# $ns at 100.0 "exit"
# $ns run
# ----------------------------------------------------------------

Class LinkMonitor

Simulator instproc init-linkmonitor {link start {interval 0.001}} {
  $self instvar lmon_

  set lmon_ [new LinkMonitor $self $link $start $interval]
  return $lmon_
}

# Create and activate a LinkMonitor object.  ``ns'' is the Simulator
# object, ``link'' is the target link for which statistics are
# collected, ``start'' is the warm-up time (i.e., the measurement
# start time), and ``interval'' is the duration between two successive
# measurements.
LinkMonitor instproc init {ns link start interval} {
  $self instvar ns_ link_ start_ interval_ qmon_
  
  # save instance variables
  set ns_ $ns
  set link_ $link
  set start_ $start
  set interval_ $interval
  # create and attach a queue monitor
  set qmon_ [$link init-monitor $ns "" $interval]
  # schedule the first invocation
  $ns at $start "$self hook"
}

LinkMonitor instproc hook {} {
  $self instvar ns_ link_ start_ interval_ qmon_
  
  set now [$ns_ now]
  if { $now == $start_ } {
    # initialize QueueMonitor
    $qmon_ reset
  } else {
    set intv [expr $now - $start_]
    
    # instantenous queue length in packets
    set q [$qmon_ set pkts_]
    # average queue length in packets
    set integ [$qmon_ get-pkts-integrator]
    set qavg [expr ([$integ set sum_] + 0.0) / $intv]
    # average link utilization
    set bw [$link_ bw]
    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]
    }
    # dump all statistics
    puts [format "now=%g q=%g qa=%g utl=%g thr=%g loss=%g" \
	    $now $q $qavg $util $thr $loss]
  }
  # schedule the next invocation
  $ns_ after $interval_ "$self hook"
}

