#
# A simple (but not too realistic) rate-based congestion control
# scheme for Homework 3 in CS268.
#
# $Header: /nfs/jade/faber/CVSROOT//acc-ns/tcl/ex/rc.tcl,v 1.1.1.3 1999/05/19 21:48:32 faber Exp $
#

source timer.tcl

set ns [new Simulator]

proc build_topology { ns which } {
        $ns color 1 red
        $ns color 2 white

	foreach i "0 1 2 3" {
		global n$i
		set tmp [$ns node]
		set n$i $tmp
	}
	$ns duplex-link $n0 $n2 5Mb 2ms DropTail
	$ns duplex-link $n1 $n2 5Mb 2ms DropTail
	$ns duplex-link-op $n0 $n2 orient right-down
	$ns duplex-link-op $n1 $n2 orient right-up

	if { $which == "FIFO" } {
		$ns duplex-link $n2 $n3 1.5Mb 10ms DropTail
	} elseif { $which == "RED" } {
		$ns duplex-link $n2 $n3 1.5Mb 10ms RED
	} else {
		$ns duplex-link $n2 $n3 1.5Mb 10ms FQ
	}
	$ns duplex-link-op $n2 $n3 orient right
	$ns duplex-link-op $n2 $n3 queuePos 0.5
}

Class Agent/Message/Sender -superclass {Agent/Message Timer}
Class Agent/Message/Receiver -superclass Agent/Message

Agent/Message/Sender instproc init {} {
	$self next
	$self instvar cbr_ seq_ udp_
	set udp_ [new Agent/UDP]
	set cbr_ [new Application/Traffic/CBR]
	$cbr_ attach-agent $udp_
	set seq_ 1
	$self sched [$self randomize 0.05]
}

Agent/Message/Sender instproc randomize v {
	return [expr $v * (1.0 + [ns-random] / 2147483647.)]
}

Agent/Message/Sender instproc timeout {} {
	global ns
	$self send "probe [$ns now]"
	$self sched [$self randomize 0.05]
}

Agent/Message/Sender instproc handle msg {
	set type [lindex $msg 0]
	if { $type == "congested" } {
		$self decrease
	} else {
		$self increase
	}
}

Agent/Message/Sender instproc increase {} {
	$self instvar cbr_
	set delta [$cbr_ set interval_]
	set delta [expr 1.0 / (1.0 / $delta + 100)]
	$cbr_ set interval_ $delta
}

Agent/Message/Sender instproc decrease {} {
	$self instvar cbr_
	set delta [$cbr_ set interval_]
	set delta [expr 2 * $delta]
	$cbr_ set interval_ $delta
}

Agent/Message/Receiver instproc init {} {
	$self next
	$self instvar mon_ congested_
	set mon_ [new Agent/LossMonitor]
	$mon_ proc log-loss {} "$self log-loss"
	set congested_ 0
}

Agent/Message/Receiver instproc log-loss {} {
	global ns
	$self instvar congested_
	if !$congested_ {
		$self send "congested 0"
		set congested_ 1
	}
}

Agent/Message/Receiver instproc handle msg {
	$self instvar congested_
	if $congested_ {
		set congested_ 0
	} else {
		$self send "uncongested [lindex $msg 1]"
	}
}

proc build_conn { from to startTime } {
	global ns
	set src [new Agent/Message/Sender]
	set sink [new Agent/Message/Receiver]
	$ns attach-agent $from $src
	$ns attach-agent $to $sink
	$ns connect $src $sink
	$ns attach-agent $from [$src set udp_]
	$ns attach-agent $to [$sink set mon_]
	$ns connect [$src set udp_] [$sink set mon_]
	$ns at $startTime "[$src set cbr_] start"
	return [$src set udp_]
}

set f [open out.tr w]
$ns trace-all $f
set nf [open out.nam w]
$ns namtrace-all $nf

build_topology $ns FIFO

set c1 [build_conn $n0 $n3 0.1]
$c1 set class_ 1
set c2 [build_conn $n1 $n3 0.1]
$c2 set class_ 2

$ns at 5.0 "finish"

proc finish {} {
	global ns f nf
	$ns flush-trace
	close $f
	close $nf

	#XXX
	puts "running nam..."
	exec nam out.nam &
	exit 0
}

$ns run

