#!/usr/bin/perl

require "ctime.pl";

# This script creates a configuration file for the ABR case study
# for the PTTs.  conf10 is a 2 switch configuration, with two ABR
# control loops:
#
#             video1------.
#                         |
#           _____        _V____        ______
#  LAN1<-->| ABR |----->|switch|----->|ABRCU |<-- LAN2
#          | CU 1|<-----|   1  |<-----| 2b   | 
#           -----        ------        ------
#
# LAN 1 contains only sources and LAN 2 only destinations

# simulation model parameters

# general

$sim_duration=250;
$num_batches=5;

# LAN parameters

$LAN_group_size=10;	# number of workstations per group (2 groups per
		 	# branch LAN
$number_of_lans=2;	# number of LANs
$core_lan=2;		# core LAN
$ns=$LAN_group_size;	# number of workstations

# background load parameters

$bg_burst_length=200;	# cells 

# queue parameters

$lan_q_size="5000";	# cells of a LAN queue (input and output) (want 0 loss)
$lan_rate=235849.05;	# LAN cell rate (cells/second)
$lan_delay=10e-6;	# LAN delay = 2 km

# workstation parameters

# command line parameter in these sims
# $ws_call_rate=2.0;	# calls/second for each workstation
$offered_load=72500000;	# total offered load per LAN source station group
$ws_fl=453125;		# mean filelength for each workstation
$ws_nic_peak=235849.05;	# NIC peak cell rate (100 Mbits/s)
$ws_sockrate=50e6;	# socket load rate (bits/second)
$ws_delay=10e-6;	# constant delay in NIC card
$ws_packet_size=9180;	# packet size

# switch parameters

$s_in_qsize=128;		# input queue size
$s_bg_qsize=1000;		# background traffic queue size
$s_abr_rm_qsize=128;		# ABR RM cell queue
$s_abr_qsize=8192;		# ABR traffic queue size
$s_in_rate=341981.13;		# switch queue service rate (cells/second)
$s_out_rate=341981.13;		# switch queue service rate (cells/second)
$s_out_qsize=128;		# output queue size
$s_cu_delay=0.0005;		# switch<->CU delay
# this is a command line argument
# $s_net_delay=0.005;		# output queue delay, through network (seconds)
$s_int_delay=450e-6;		# internal switch delay (seconds)
$abr_meas_win=2e-3;		# measurement window (seconds)
$abr_target_utilisation=0.95;	# target utilisation
$abr_alpha=0.0625;		# alpha (for load averaging)
$abr_decay_factor=0.25;		# decay factor for VC contribution averaging
$abr_erica_opt="no";		# ERICA + queue control options
$abr_empty_time=200e-6;		# queue emptying time
$abr_a=1.15;			# a
$abr_b=1.05;			# b
$abr_qdlf=0.5;			# 0.5 for WAN and 0.8 for LAN

# ABR control unit parameters

$cu_rm_qsize=128;		# RM queue capacity
# command line parameter
# $cu_ingress_qsize=8000;		# ingress queue capacity
$cu_shared_ingress_size=10;	# shared queue size for the core CU
$cu_ingress_rate=341981.13;	# 145 Mbits/s ingress queue rate
$cu_egress_qsize=128;		# egress queue capacity
$cu_egress_rate=341981.13;	# 145 Mbits/s egress queue rate
# source and destination behaviour parameters
$icr=2358.490;			# Initial Cell Rate
$pcr=341981.13;			# Peak Cell Rate
$mcr=2358.490;			# Minimum Cell Rate
$rif=1;				# Rate Increase Factor
$rdf=0.5;			# Rate Decrease Factor
$nrm=32;			# Nrm
$crm=4;				# Missing RM cell count, CRM
$cdf=1;				# Cutoff Decrease Factor, CDF
$adtf=0.2;			# ADTF, (0.01<ADTF<10.23)
$trm=0.1;			# Trm Max. time between two RM cells
$pni="true";			# PNI
$start_thresh=1;		# Start threshold 
$oor_ta="yes";			# Send turn-arounds out-of-rate when conflict

$trace_length=5000;		# how many points to trace at cu1_to_2

# background traffic

$num_bg_sources = 10;		# number of background source
$bg_peak_utilisation = 1.25;	# peak utilisation

# Markov chain parameters 

$markov_numstates=3;		# number of states in Markov chain
$discrete_timeslot="1.0e-3";	# discrete time-slot duration
$initial_state="0";
				# Markov transition matrix,
				# i.e. prob. of changing from state
				# x to state y at the end of each
				# time slot.
@trans_matrix =       (	0.9,	0.05,	0.05,
			0.05,	0.00,	0.95,
			0.01,	0.99,	0.00);
# note 341981.13 == 145 Mbits/s
@rate_vector =        (	40094, 0.0, 0.0 );	

# formatting constants

$tabwidth=8;
$comment_tab=16;


sub multiple_char_print {
    local($temp_char,$times) = @_;
    for(local($i)=0;$i<$times;$i++) {
	print($temp_char);
    }
}

sub wp {
    local($param,@comment) = @_;
    local($param_string);
 
    # if param is a floating point value, we have to be a bit careful
    # with the printing, coz Perl converts it using %.20g!
 
    if($param =~ /\./) {
        $param_string = sprintf("%-12le",$param);
    } else {
        $param_string = $param;
    }
    local($plen) = length($param_string);
    local($diff);
    print $param_string;

    if ($plen >= $comment_tab) {
	print "\t";
    } else {
	while($plen < $comment_tab) {
	    $diff = $comment_tab - $plen;
	    if ($diff < $tabwidth) {
		&multiple_char_print(" ",$diff);
		$plen = $comment_tab;
	    } else {
		print "\t";
		$plen = $tabwidth * (int($plen/$tabwidth) + 1);
	    }

	}
    }
    print "; ",@comment,"\n";
}

sub bl {
    print "#\n";
}

sub cl {
    print "# ",@_,"\n";
}

sub component {
    print "\\",$_[0],"(",$_[1],")";
    local($i)=2;
    while($_[$i]) {
	print "(",$_[$i++],")";
    }
    print "\n{\n";
}

# write queues and workstations connected to the output of a queue

sub write_conn {
    local(@outputs) = (@_,"end");
    local($line_num) = 0;
    local($x)=0;
    while (@outputs) {
	$comp = shift(@outputs);
	print $comp," ";
	$x += (length($comp) + 1);
	local($left) = ($comment_tab - $x);
	if ($left < (length(@outputs[0]) + 2)) {
	    if($line_num == 0) {
		local($num_tabs)=int($left/$tabwidth) + 1;
		&multiple_char_print("\t",$num_tabs);
		print "; Connectivity";
	    }
	    print "\n";
	    $x=0;
	    $line_num++;
	}
    }
    if ($x>0) {
	print "\n";
    }
}

# write a queue model component.  The output connectivity is
# passed in as a list at the end of the other parameters.

sub q {
    # note that we do not allow for any queue measurements

    local($qname,$qsize,$qrate,$qdelay,@connectivity) = @_;
    &component("queue",$qname);
    &wp($qsize,"Queue capacity (ATM cells)");
    &wp($qrate,"Service rate (cells/second)");
    &wp($qdelay,"Post service delay (seconds)");

    &write_conn(@connectivity);

    &wp(0,"Max. number of window samples");
    &wp(0,"Max. number of occupancy samples");
    &wp(0,"Occupancy trace");
    &wp("no","Occupancy distribution");
    print "}\n";
    &bl;
}

sub priq {
    # note that we do not allow for any queue measurements
    local($subqueue,$subqueue_size,@subq_list,@subq_size_list,@optlist,$qi);
    while($subqueue = shift) {
	$subqueue_size = shift;
	$opttmp = shift;
	push (@subq_list,$subqueue);
	push (@subq_size_list,$subqueue_size);
	push (@opt_list,$opttmp);
    }
    print "\\priqueue";
    for($qi=0;$qi<=$#subq_list;$qi++) {
	print "(",$subq_list[$qi],")";
    }
    print "\n{\n";
    while($subqueue = shift(@subq_list)) {
	print $subqueue,"{\n";
	$subqueue_size = shift subq_size_list;
	&wp($subqueue_size,"queue length");
	&wp(0,"Window sample size");
	&wp(0,"Number of window samples");
	&wp(0,"Queue occupancy trace");
	&wp("no","Queue occupancy distribution");
	# queue options
	print shift opt_list;
	print "}\n";
    }

    &cl("Common parameters:");
    local($qrate,$qdelay,@connectivity) = @_;

    &wp($qrate,"Service rate (cells/second)");
    &wp($qdelay,"Post service delay (seconds)");

    &write_conn(@connectivity);

    print "}\n";
    &bl;
}

# write workstation model component 

sub ws {
    # note that we do not allow for any queue measurements

    local($src_name,$dest_name,$src_out,$src_in,$dest_out,$dest_in,
	  $call_rate,$fl,$src_peak,$dest_peak,$sockrate,
	  $src_delay,$dest_delay) = @_;
    &component("workstationpair",$src_name,$dest_name);
    &wp($src_out,"Source output queue");
    &wp($src_in,"Source input queue");
    &wp($dest_out,"Destination output queue");
    &wp($dest_in,"Destination input queue");
    &wp($call_rate,"Mean call rate (calls/second)");
    &wp("#","mean inter call time = ",(1/$call_rate));
    &wp($fl,"Mean file length (bytes)");
    &wp($src_peak,"Peak source cell rate (cells/second)");
    &wp($dest_peak,"Peak dest cell rate (cells/second)");
    &wp($sockrate,"Send socket buffer load rate (bits/second)");
    &wp("1.0e-5","Time between last tx data pkt and call shutdown");
    &wp("1.0e-5","Time between CLOSE_WAIT and LAST_ACK STATES at dest.");
    &wp(61444,"Source send socket buffer size (bytes)");
    &wp(61444,"Source receive socket buffer size (bytes)");
    &wp(61444,"Destination send socket buffer size (bytes)");
    &wp(61444,"Destination receive socket buffer size (bytes)");
    &wp("0.2","Fast timeout period (seconds) (normally 200ms)");
    &wp("0.5","Slow timeout period (seconds) (normally 500ms)");
    &wp("100.0e+06","Socket read rate");
    &wp("16384","Socket read size (lumpiness of reads)");
    &wp("0","Default sock reading flags (soreceive, see Wright+Stephens)");
    &wp("sync 2","TCPTV_MSL (seconds) min. resolution 0.5 seconds");
    &wp($ws_packet_size,"MTU (bytes)");
    &wp("2.0e-05","Time between call establishment and data sending");
    &wp("1.0e-05","Time between sockbuf read and loader wakeup (s)");
    &wp($src_delay,"Delay in source station ATM card (seconds)");
    &wp($dest_delay,"Delay in destination station ATM card (seconds)");
    &wp("none","Per-call delay distribution");
    &wp("0","No TCP window trace");
    &wp("0","No log for source workstation");
    &wp("0","No log for destination workstation");
    print "}\n";
    &bl;
}


sub route {
    $start = shift;
    $dest = shift;
    print "\\route(",$start,")(",$dest,") {\n";
    while($nextnode = shift) {
	print $nextnode,"\n";
    }
    print "}\n";
}

sub global {
    &cl("Configuration file for TCP simulations");
    &cl("Created by make_3lan program");
    &cl("(c) Sam Manthorpe, 1996");
    print "# ",&ctime(time);
    &bl;

    &wp($sim_duration,"Simulation duration (seconds)");
    &wp(0,"Warm-up time (seconds)");
    &wp($num_batches,"Number of batches");
    &bl;

    &wp(1000,"RNG seeds");
    &wp(666);
    &wp(29);
    &bl;

    &cl("Common parameters");
    &bl;
    &wp(1000,"Output buffer length (cells)");
    &wp(2,"Binary rate shift bits");
    &wp(0,"Stagger factor");
    &bl;
}

# write LAN-x model component information

sub write_lan {
    local($lan_number,$group_size)=@_;
    local($other_end);
    local(@lan_conn);

    &cl("Configuration for LAN ",$lan_number);
    &cl("~~~~~~~~~~~~~~~~~~~~~~~");
    &bl;

    if ($lan_number != $core_lan) {

	# The network model consists of two types of LAN: a core LAN
	# and branch LANs.  A workstation pair has one workstation in the
	# core LAN and one in the branch LAN.
	# For all branch-LANs, there are two groups of workstations: source
	# workstations and destination workstations, each of size
	# $group_size.  For the core LAN, there are $num_lans-1 groups of 
	# workstations, but these are definined implicitly through the 
	# definitions of the workstations in LANs 1 and 3.

	local($ws_i,$src,$dest);

	# write local source workstations for branch LAN

	for($ws_i=0;$ws_i<$group_size;$ws_i++) {
	    $src = "lan".$lan_number."_to_lan".$core_lan."_src_ws".$ws_i;
	    $dest = "lan".$core_lan."_to_lan".$lan_number."_dst_ws".$ws_i;
	    &ws($src,$dest,
		"lan".$lan_number."_q",
		"lan".$lan_number."_q",
		"lan".$core_lan."_q",
		"lan".$core_lan."_q",
		$ws_call_rate,$ws_fl,
		$ws_nic_peak,		# src peak
		$ws_nic_peak,		# dst peak
		$ws_sockrate,		# socket load rate
		$ws_delay,		# src constant delay
		$ws_delay);		# dst constant delay
	    # write the route for these workstations
	    &route("lan".$lan_number."_q",$dest,
		   "cu".$lan_number."_to_".$core_lan."_ingress","*");
	    &route("lan".$core_lan."_q",$src,
		   "cu".$core_lan."_to_".$lan_number."_ingress","*");
	}
    } 
    # ..else we are writing the core LAN, so all the workstations have
    # been defined elsewhere

    # write the LAN queue (shared medium)

    # first work out what the LAN queue is conncted to.  All LAN queues
    # are connected to the ABR control unit.

    if($lan_number == $core_lan) {
	for($other_end=1;$other_end<=$number_of_lans;$other_end++) {
	    if($other_end != $core_lan) {
		push(@lan_conn,"cu".$lan_number."_to_".$other_end."_ingress");
	    }
	}
    } else {
	@lan_conn = ("cu".$lan_number."_to_".$core_lan."_ingress");
    }
    # if this is not a core lan, then the other end _is_ a core lan.
    # if this _is_ a core lan, then all other lans are possible other ends.
    if ($lan_number == $core_lan) {
	# this is a core LAN.
	# loop for each LAN at the other end of a workstation pair
	for($other_end=1;$other_end<=$number_of_lans;$other_end++) {
	    if ($other_end != $core_lan) {
		# for each LAN, add a source and destination pairs
		push(@lan_conn,
		     "lan".$lan_number."_to_lan".$other_end."_dst_ws*");
	    }
	}
    } else {
	push(@lan_conn,"lan".$lan_number."_to_lan".$core_lan."_src_ws*");
    }
    &q("lan".$lan_number."_q",$lan_q_size,$lan_rate,$lan_delay, @lan_conn);
}


# write Poisson background source

sub poisson_source {
    local($source_name,$rate,$connected_to)=@_;
    &component("bgsource",$source_name);
    &wp($connected_to,"Queue to which it is attached");
    &wp($connected_to,"Terminating queue (sunk after this queue)");
    &wp("poisson","Source type");
    &wp($rate,"Mean cell rate (cells/second)");
    print "}\n";
}

sub on_off_source {
    local($source_name,$rate,$burst_length,$peak_rate,$connected_to) = @_;

    &bl;
    &component("bgsource",$source_name);
    &wp($connected_to,"Queue to which it is attached");
    &wp($connected_to,"Terminating queue (sunk after this queue)");
    &wp("onoff","Source type");
    &wp($rate,"Mean cell rate (cells/second)");
    &wp($peak_rate,"Peak cell rate (cells/second)");
    &wp($burst_length,"Mean burst length (cells)");
    print "}\n";
    &bl;
}

sub markov_source {
    &bl;
    local($source_name,$connected_to,$terminating_q)=@_;
    &component("bgsource",$source_name);
    &wp($connected_to,"Queue to which it is attached");
    &wp($terminating_q,"Terminating queue (sunk after this queue)");
    &wp("markov","Source type");
    &wp($markov_numstates,"Number of states");
    &wp($discrete_timeslot,"Discrete time-slot (seconds)");
    &wp($initial_state,"Initial state");
    local($si,$tsi);
    for($si=0;$si<$markov_numstates;$si++) {
	printf("\t%12le",@rate_vector[$si]);
	for($tsi=0;$tsi<$markov_numstates;$tsi++) {
	    printf("\t%12le",
		   @trans_matrix[$tsi+($si * $markov_numstates)]);
	}
	print "\n";
    }
    print "}\n";
    &bl;
}


# write queue information for a switch

sub switch {

    local($sn)=@_;
    local(@outputs,$qi,$abrq,$input_queue,@port_queues);
    &cl("Configuration for switch ",$sn);
    &bl;

    if($srv_mode eq "ubr+") {
	$abrq_opt = "ppd epd ".$epd_thresh."\n";
    } else {
	$abrq_opt = " ";
    }

    @port_queues = ();
    $abrq = "s".$sn."_abr_q1";
    &priq("s".$sn."_bg_q1",$s_bg_qsize," ",
	  $abrq,$s_abr_qsize,$abrq_opt,
	  0,
	  $s_out_rate,$s_int_delay+$s_net_delay,
	  "cu1_to_2_egress");
    push(@port_queues,$abrq);
    $input_queue = "s".$sn."_in_q1";
    &q($input_queue,$s_in_qsize,
       $s_in_rate, $s_int_delay, "s".$sn."_abr_q2");
    push(@port_queues,$input_queue);

    $abrq = "s".$sn."_abr_q2";
    &priq("s".$sn."_bg_q2",$s_bg_qsize," ",
	  $abrq,$s_abr_qsize,$abrq_opt,
	  0,
	  $s_out_rate,$s_int_delay+$s_net_delay,
	  "cu2_to_1_egress");
    push(@port_queues,$abrq);
    $input_queue = "s".$sn."_in_q2";
    &q($input_queue,$s_in_qsize,
       $s_in_rate, $s_int_delay, "s".$sn."_abr_q1");
    push(@port_queues,$input_queue);

    if($srv_mode eq "abr") {
	# now write the ABR switch itself
	&component("abrswitch","switch".$sn);
	# write the ERICA parameters (these are the same for all ports)
	&wp($abr_meas_win,"measurement window size (seconds)");
	&wp($abr_target_utilisation,"target utilisation");
	&wp("yes","load averaging option");
	&wp($abr_alpha,"alpha for load averaging");
	&wp("yes","per-VC cell rate options");
	&wp($abr_decay_factor,"decay factor");
	&wp("yes","immediate fairshare update option");
	&wp("yes","average number of VCs");
	&wp($abr_erica_opt,"ERICA + queue control options");
	if($abr_erica_opt eq "yes") {
	    &wp($abr_empty_time,"target empty time");
	    &wp($abr_a,"a");
	    &wp($abr_b,"b");
	    &wp($abr_qdlf,"qdlf");
	}

	# write the queues that it is connected to
	print "#input\t\toutput\n";
	while($output_queue = shift(@port_queues)) {
	    $input_queue = shift(@port_queues);
	    print $input_queue,"\t",$output_queue,"\n";
	}
	print "end\n}\n\n";
    }
}

sub leaky_bucket {
    # write a leaky bucket model component

    local($lb_name,$connected_to,$lb_inc,$lb_lim,$lb_type) = @_;

    &bl;
    &cl("Leaky bucket for UPC on ",$connected_to);
    &bl;
    &component("leakybucket",$lb_name);
    &wp($connected_to,"Queue to which it is attached");
    &wp($lb_inc,"Increase (seconds)");
    &wp($lb_lim,"Limit (seconds)");
    &wp($lb_type,"Control type");
    print "}\n";
    &bl;
}

# writes the queues required for an ABR contol unit

sub abrcu_queues {
    &cl("Control unit queues");
    &bl;
    local($lan_index,$other_end) = @_;
    local(@outputs,$di);
    local($ingress_q,$egress_q);

    @outputs = "s1_in_q".$lan_index;
    $ingress_q = "cu".$lan_index."_to_".$other_end."_ingress";
    &q($ingress_q,$cu_ingress_qsize,
       $cu_ingress_rate,$s_int_delay+$s_net_delay,
       @outputs);
    $egress_q = "cu".$lan_index."_to_".$other_end."_egress";
    &q($egress_q, $cu_egress_qsize,
       $cu_egress_rate, 0,"lan".$lan_index."_q");
}


# write an ABR control unit

sub abrcu {
    local($cu_num,$egress_num, $abrcu_name, $other_end_name, $alloc_trace2,
	  $er_trace2, $dcr_trace2);
    $cu_num = shift;
    $egress_num = shift;
    $abrcu_name = "cu".$cu_num."_to_".$egress_num;
    $other_end_name = "cu".$egress_num."_to_".$cu_num;
    &component("abrcu",$abrcu_name);
    &wp("cu".$cu_num."_to_".$egress_num."_ingress",
	"Ingress node (forward traffic)");
    &wp("cu".$cu_num."_to_".$egress_num."_egress",
	"Egress node (backwards traffic)");
    &wp($other_end_name,"Other ABR control unit in pair");
    &wp(10e-6,"Turnaround time for RM cells");
    &wp($icr,"Initial cell rate (cells/second)");
    &wp($pcr,"Peak cell rate (cells/second)");
    &wp($mcr,"Minimum cell rate (cells/second)");
    &wp($rif,"Rate Increase Factor");
    &wp($rdf,"Rate Decrease Factor");
    &wp($nrm,"Renegotiation period, Nrm (cells)");
    &wp($crm,"Missing RM cell count, CRM");
    &wp($cdf,"Cutoff decrease factor");
    &wp($adtf,"ACR Decrease Time Factpr (0.01<ADTF<10.23)");
    &wp($trm,"Trm Max. time between two RM cells");
    &wp($pni,"PNI");
    &wp($start_thresh,"Start threshold (ask for PCR if occ > thresh)");
    &wp($oor_ta,"Send turn-arounds out-of-rate in case of conflict");
    $alloc_trace2 = shift;
    &wp($alloc_trace2,"Number of allocation trace points");
    if($alloc_trace2) {
	print $abrcu_name."_alloc_trace.txt\t; Output filename\n";
    }
    $er_trace2 = shift;
    &wp($er_trace2,"Number of explicit rate trace points");
    if($er_trace2) {
	print $abrcu_name."_er_trace.txt\t; Output filename\n";
    }
    $dcr_trace2 = shift;
    &wp($dcr_trace2,"Number of desired cell rate trace points");
    if($dcr_trace2) {
	print $abrcu_name."_dcr_trace.txt\t; Output filename\n";
    }
    print "}\n";
}

# write a pair of ABR control units, attached to LANs
# $left_cu and $right_cu.

sub abr_loop {
    local($left_cu,$right_cu,$alloc_trace,$er_trace,$dcr_trace) = @_;
    &bl;
    &cl("ABR control unit ",$left_cu);
    &bl;
    &abrcu_queues($left_cu,$right_cu);
    if ($srv_mode eq "abr") {
	&abrcu($left_cu,$right_cu,$alloc_trace,$er_trace,$dcr_trace);
    }
    &bl;
    &cl("ABR control unit ",$right_cu);
    &bl;
    &abrcu_queues($right_cu,$left_cu);
    if ($srv_mode eq "abr") {
	&abrcu($right_cu,$left_cu,0,0,0);
    }
}


sub bg_sources {
    local($i,$src_rate,$src_peak);
    $src_rate = $s_out_rate * $bg_utilisation / $num_bg_sources;
    $src_peak = $s_out_rate * $bg_peak_utilisation / $num_bg_sources;
    for($i=0;$i<$num_bg_sources;$i++) {
	&on_off_source("bg_src_".$i, $src_rate, $bg_burst_length,
		       $src_peak,"s1_bg_q2");
    }
}

# Top level.
# Read in command line arguments.

$srv_mode = shift;
if(!($srv_mode =~ /abr|ubr|ubr\+/)) {
    die("Unknown service mode type");
}
if($srv_mode eq "ubr+") {
    $epd_thresh = shift;
    if($epd_thresh eq "half") {
        $epd_thresh = $s_abr_qsize/2;
    }
}
($bg_utilisation,$s_net_delay,$ws_packet_size,$cu_ingress_qsize) = @ARGV;
$ws_call_rate = $offered_load / (8*$LAN_group_size*$ws_fl);

# now start writing the parameters

&global;
for($lan_index=1;$lan_index<=$number_of_lans;$lan_index++) {
    &write_lan($lan_index,$LAN_group_size);
}

&switch(1);

&abr_loop(1,2,$trace_length,$trace_length,$trace_length);

if($num_bg_sources) {
    &bg_sources();
}

&bl;
&cl("EOF");
&bl;


