#!/usr/bin/perl

# TIP: Set your tabwidth to 4
#
# This script was written by Mark Stephan during a job as a student assistant
# in April till August 2010 under supervision of Michael Iedema (<michael@askozia.com>).
# Bugs, questions and reports to <mark.stephan@askozia.com>
# PS: This is version 1.0 - it is not structured perfectly yet
#
# This script follows the following procedure:
# 0. Introduction (Module-loading, parse parameters, check needed files, ...)
#	a) Load modules
#	b) Parse parameters
#	c) Check dependencies
#	d) Calculate needed test-time
# 1. Get Infos from Askozia-Box, edit configfile und update Askozia-Box
#	a) Download translation table
#   b) Get Configfile
#	c) Edit it
#	d) Re-Upload it
# 2. Generate Users.csv-File for sipp
#	a) For two-party-tests
#	b) For "conference rooms test" 
#	c) For "conference participants test"
# 3. Execute, interpret and plot Performance-Tests
#   a) Prepare watchdog
#	b) Execute two-party-tests
#	c) Execute "conference rooms test"
#	d) Execute "conference participants test" 
# 4. Restore old Askozia-Configuration


#######################
### 0. INTRODUCTION ###
#######################

### 0.a) load needed modules ###
use warnings;					# Activate Warnings
use strict;						# Activate strict code-control
use Cwd;						# For determing the current working path
use File::Path;					# For creating pathes
use File::Spec;					# For assimilate relative and absolut pathes to the operating system
use Getopt::Long;				# For parsing command-line-parameters
use HTTP::Request::Common;		# For executing HTTP-POST-Requests
use IO::Pipe;					# IPC between parent and child (fork) with autoflush
use IO::Select;
use LWP;						# For executing HTTP-POST-Requests

# install signal handler for killing all sipp-processes before being terminated
$SIG{'INT'} = 'term_handler';	# Catch "STRG-C" for deregister all registered users

### 0.b) Parse parameters ###
my $debug = 0;								# Debug-Mode? (0=off, 1=on)
my $save_debug;								# Save debug-msgs in this file? (undef = no saving)
my $help;									# Print help an exit? (defined = yes)
my $xml_configfile;							# Name for saving the original AskoziaPBX-XML-Configfile
my $term_handler_called;					# Was the TERM-Signal (STRG+C) sent? --> cancel all tests
my $testname = "";							# Name of the test (needed for creating subdir with result-files)
chomp(my $gnuplot_exe = `which gnuplot`);	# Path to the gnuplot executable for drawing graphs at the end of the test
my @codecs_to_draw;							# List of codecs that should be drawed as source codecs in the codecs.png
my @PBXs_to_compare;						# List of PBX names which translation table results should be compared with these ones

# Askozia-Settings
my $ask_ip			= "10.10.10.1";					# IP of the askozia-box
my $ask_port		= 80;							# Webport of the askozia-box
my $ask_conf_page	= "system_backup.php";			# PHP-Page for down-/uploading the configuration-file
my $ask_reboot_page	= "system_reboot.php";
my $ask_realm		= "Web Server Authentication";	# Realm for authentification
my $ask_user		= "admin";						# User for down-/uploading the configuration-file
my $ask_pw			= "askozia";					# Passwort for the user to login
my $reboot_time		= 15;							# Time for completing a reboot after AskoziaPBX pings back again
my $translation_table = "translation-table";		# Filename for saving the translation table
my $restore			= "old-config";					# Which configuration shell the AskoziaPBX have AFTER the test?
													# (old-config, keep, factory-defaults)

# Perfomance-Test-Settings
my $pause = 60;
my $max_two_party;						# How many calls shell be executed for the two-party-test? (0/undef = no twoway test)
my $max_conf_participants;				# How many participants maximal in the "conference participants test"? (0/undef = no test)
my $conf_participants_no_of_rooms = 1;	# How many rooms shell exist in the "conference participants test"? (developers parameter)
my $max_conf_rooms;						# How many rooms shell exist in the "conference rooms test" maximal? ()
my $conf_rooms_no_of_participants = 3;	# Which size should the conference rooms in the "conference rooms test" have? (dev parameter)
my $save_sipp_log;						# Shell the sipp-output be saved in files? (extrem-debugging because very much data)
my $local_ip;							# The IP adress of the test-computer

# Sipp-Settings
my $sipp_exe;																		# Name of the sipp-executable
my $users_two_party_file			= "Users_two-party.csv";					# Name of the CSV-Injection-File for sipp for two-party-test
my $users_conf_rooms_file			= "Users_conf-rooms.csv";					# CSV-Injection-File for sipp for "conference rooms test" 
my $users_conf_participants_file	= "Users_conf-participants.csv";			# CSV-Injection-File for sipp for "conference participants test"
my $reg_scen						= File::Spec->rel2abs ("Register.xml"	, cwd()."/PERF_TEST_FILES");	# REGISTER-scenario
my $der_scen						= File::Spec->rel2abs ("Deregister.xml"	, cwd()."/PERF_TEST_FILES");	# De-REGISTER-scenario
my $inv_scen						= File::Spec->rel2abs ("Invite.xml"		, cwd()."/PERF_TEST_FILES");	# INVITE-scenario
my $acc_scen						= File::Spec->rel2abs ("Accept.xml"		, cwd()."/PERF_TEST_FILES");	# ACCEPT-scenario for listening invited users
my $sip_src_port			= 5061;												# SIPP-Source-Port of the testcomputer
																				# (should be != 5060 because blocked by running softphones)
my $sip_dst_port			= 5062;												# SIPP-Destination-Port of the testcomputer (for two-party-tests only)
my $rtp_src_port			= 6020;												# RTP-Source-Port of the testcomputer
my $rtp_dst_port			= 6030;												# RTP-Destination-Port of the testcomputer (for two-party-tests only)

# Template-Text for the xml-configfile for a sipphone
my $conffile_text_usertext =
"		<phone>
			<extension>_userno_</extension>
			<callerid>Testuser _userno_</callerid>
			<manualattributes>cXVhbGlmeT1ubw==</manualattributes>
			<codec>alaw</codec>
			<secret>_userno_</secret>
			<uniqid>SIP-PHONE-_userno_</uniqid>
			<language>en-us</language>
			<ringlength>indefinitely</ringlength>
			<natmode>yes</natmode>
			<dtmfmode>auto</dtmfmode>
		</phone>
";

# Template-Text for the xml-configfile for a conference-room
my $conffile_conftext = 
"		<room>	<number>_roomno_</number>
			<name>Default Conference</name>
			<uniqid>CONFERENCE-ROOM-_roomno_</uniqid> </room>
";

# use "./PERF_TEST_FILES/sipp" if existing, otherwise the installed version for default executables, is overwritten if specified by command line parameter
(-f File::Spec->rel2abs ("sipp", cwd()."/PERF_TEST_FILES"))	? ($sipp_exe = "PERF_TEST_FILES/sipp")
															: (($sipp_exe = `which sipp`) && chomp $sipp_exe);

# parse command line parameters
# for help have a look at (a) help-output of this script or (b) declaration of this variables
Getopt::Long::GetOptions (
	"askozia-ip=s"						=> \$ask_ip, 
	"askozia-port=s"					=> \$ask_port,
	"askozia-confpage=s"				=> \$ask_conf_page,
	"askozia-realm=s"					=> \$ask_realm,
	"askozia-user=s"					=> \$ask_user,
	"askozia-pw=s"						=> \$ask_pw,
	"reboot-time=i"						=> \$reboot_time,
	"save-sipp-log"						=> \$save_sipp_log,
	"local-ip=s"						=> \$local_ip,
	"pause=i"							=> \$pause,
	"max-two-party-test=i"				=> \$max_two_party,
	"max-conf-participants-test=i"		=> \$max_conf_participants,
	"max-conf-rooms-test=i"				=> \$max_conf_rooms,
	"conf-participants_no_of_rooms=i"	=> \$conf_participants_no_of_rooms,
	"conf-rooms-no-of-participants=i"	=> \$conf_rooms_no_of_participants,
	"sipp-path=s"						=> \$sipp_exe,
	"gnuplot-path=s"					=> \$gnuplot_exe,
	"users-two-party-file=s"			=> \$users_two_party_file,
	"users-conf-rooms-file=s"			=> \$users_conf_rooms_file,
	"users-conf-participants-file=s"	=> \$users_conf_participants_file,
	"reg-scen=s"						=> \$reg_scen,
	"dereg-scen=s"						=> \$der_scen,
	"inv-scen=s"						=> \$inv_scen,
	"acc-scen=s"						=> \$acc_scen,
	"sip-src-port=i"					=> \$sip_src_port,
	"sip-dst-port=i"					=> \$sip_dst_port,
	"rtp-src-port=i"					=> \$rtp_src_port,
	"rtp-dst-port=i"					=> \$rtp_dst_port,
	"debug"								=> \$debug,
	"help"								=> \$help,
	"testname=s"						=> \$testname,
	"translation-table=s"				=> \$translation_table,
	"draw-codec=s"						=> \@codecs_to_draw,
	"compare-PBX-with=s"				=> \@PBXs_to_compare,
	"restore=s"							=> \$restore
);

# ensure that lists have one entry per array index
@codecs_to_draw		= split (/,/, join (',', @codecs_to_draw));
@PBXs_to_compare	= split (/,/, join (',', @PBXs_to_compare));

### 0.c) Check depedencies and settings ###
print_help() if (defined $help);
mydie ("No local ip address specified.") unless (defined $local_ip);
mydie ("No test specified (no two-party tests and no conference tests were entered).")
	if ((not defined ($max_two_party)) && (not defined ($max_conf_participants)) && (not defined ($max_conf_rooms)));
warn ("WARN: No gnuplot executable specified, cannot create graphs at the end!") unless ($gnuplot_exe);
# am I root? Necessary for SIPP
(mydie ("Must be root because sipp reserves ports.\n")) if (`id -u` != 0);

# the following lines are necessary to avoid problems if used at different operating systems (lin, win, mac, ...)
chomp(my $timestamp				= `date +"%Y-%m-%d_%H-%M"`);
my $testpath					= File::Spec->rel2abs ("$testname"."_$timestamp", cwd()."/results");# determine subdir-path to save all results
(File::Path::mkpath ($testpath)) unless (-e $testpath);												# create subdir
system ("chmod", "777", "$testpath");
$translation_table				= File::Spec->rel2abs ($translation_table, $testpath);
$users_two_party_file			= File::Spec->rel2abs ($users_two_party_file, $testpath);			# determine path of the csv-file for two-party tests
$users_conf_rooms_file			= File::Spec->rel2abs ($users_conf_rooms_file, $testpath);			# determine complete path of the csv-file for fixed rooms
$users_conf_participants_file	= File::Spec->rel2abs ($users_conf_participants_file, $testpath);	# determine complete path of the csv-file for fixed calls
($save_debug					= File::Spec->rel2abs ("debug.log", $testpath))	if (defined $debug);# file to save the debug-msgs
($save_sipp_log					= File::Spec->rel2abs ("sipp.log", $testpath))	if (defined $save_sipp_log);	# file to save sipp-output
($gnuplot_exe					= File::Spec->rel2abs ($gnuplot_exe, cwd()))	if ($gnuplot_exe);	# determine complete path of the gnuplot-executable
$sipp_exe						= File::Spec->rel2abs ($sipp_exe, cwd());							# determine complete path of the sipp-executable
$reg_scen						= File::Spec->rel2abs ($reg_scen, cwd());							# determine complete path of the register-scenario
$der_scen						= File::Spec->rel2abs ($der_scen, cwd());							# determine complete path of the deregister-scenario
$inv_scen						= File::Spec->rel2abs ($inv_scen, cwd());							# determine complete path of the invite-scenario
$acc_scen						= File::Spec->rel2abs ($acc_scen, cwd());							# determine complete path of the accept-scenario

# print debug messages for debugging :-)
debug ("Settings for this performance-test:");
(defined $ask_ip)						? debug ("ask_ip is '$ask_ip'")									: debug ("ask_ip is not defined.");
(defined $ask_port)						? debug ("ask_port is '$ask_port'")								: debug ("ask_port is not defined.");
(defined $ask_conf_page)				? debug ("ask_conf_page is '$ask_conf_page'")					: debug ("ask_conf_page is not defined.");
(defined $ask_realm)					? debug ("ask_realm is '$ask_realm'")							: debug ("ask_realm is not defined.");
(defined $ask_user)						? debug ("ask_user is '$ask_user'")								: debug ("ask_user is not defined.");
(defined $ask_pw)						? debug ("ask_pw is '$ask_pw'")									: debug ("ask_pw is not defined.");
(defined $reboot_time)					? debug ("reboot_time is '$reboot_time'")						: debug ("reboot_time is not defined.");
(defined $save_sipp_log)				? debug ("save_sipp_log is '$save_sipp_log'")					: debug ("save_sipp_log is not defined.");
(defined $local_ip)						? debug ("local_ip is '$local_ip'")								: debug ("local_ip is not defined.");
(defined $max_two_party)				? debug ("max_two_party is '$max_two_party'")					: debug ("max_two_party is not defined.");
(defined $pause)						? debug ("pause is '$pause'")									: debug ("pause is not defined.");
(defined $max_conf_participants)		? debug ("max_conf_participant is '$max_conf_participants'")	: debug ("conf_calls_room is not defined.");
(defined $conf_participants_no_of_rooms)? debug ("conf_participants_no_of_rooms is '$conf_participants_no_of_rooms'")
										: debug ("conf_participants_no_of_rooms is not defined.");
(defined $max_conf_rooms)				? debug ("max_conf_rooms is '$max_conf_rooms'")					: debug ("max_conf_rooms is not defined.");
(defined $conf_rooms_no_of_participants)? debug ("conf_rooms_no_of_participants is '$conf_rooms_no_of_participants'")
										: debug ("conf_rooms_no_of_participants is not defined.");
(defined $sipp_exe)						? debug ("sipp_exe is '$sipp_exe'")								: debug ("sipp is not defined.");
(defined $gnuplot_exe)					? debug ("gnuplot_exe is '$gnuplot_exe'")						: debug ("gnuplot_exe is not defined.");
(defined $users_two_party_file)			? debug ("users_two_party_file is '$users_two_party_file'")		: debug ("users_file is not defined.");
(defined $users_conf_rooms_file)		? debug ("users_conf_rooms_file is '$users_conf_rooms_file'")	: debug ("users_conf_rooms_file is not defined.");
(defined $users_conf_participants_file)	? debug ("users_conf_participants_file is '$users_conf_participants_file'")
										: debug ("users_conf_participants_file is not defined.");
(defined $reg_scen)						? debug ("reg_scen is '$reg_scen'")								: debug ("reg_scen is not defined.");
(defined $der_scen)						? debug ("der_scen is '$der_scen'")								: debug ("der_scen is not defined.");
(defined $inv_scen)						? debug ("inv_scen is '$inv_scen'")								: debug ("inv_scen is not defined.");
(defined $acc_scen)						? debug ("acc_scen is '$acc_scen'")								: debug ("acc_scen is not defined.");
(defined $sip_src_port)					? debug ("sip_src_port is '$sip_src_port'")						: debug ("sip_src_port is not defined.");
(defined $sip_dst_port)					? debug ("sip_dst_port is '$sip_dst_port'")						: debug ("sip_dst_port is not defined.");
(defined $rtp_src_port)					? debug ("rtp_src_port is '$rtp_src_port'")						: debug ("rtp_src_port is not defined.");
(defined $rtp_dst_port)					? debug ("rtp_dst_port is '$rtp_dst_port'")						: debug ("rtp_dst_port is not defined.");
(defined $debug)						? debug ("debug is '$debug'")									: debug ("debug is not defined.");
(defined $help)							? debug ("help is '$help'")										: debug ("help is not defined.");
(defined $testname)						? debug ("testname is '$testname'")								: debug ("testname is not defined.");
(defined $testpath)						? debug ("testpath is '$testpath'")								: debug ("testpath is not defined.");
(defined $restore)						? debug ("restore is '$restore'")								: debug ("restore is not defined.");
debug ();
if (@PBXs_to_compare)
{
	debug ("PBXs_to_compare is:");
	debug (\@PBXs_to_compare);
}

### 0.d) Calculate needed testtime ###
my $sum_time = 0;
my $no_of_reboots = 2;
if (defined $max_two_party)
{
	$no_of_reboots++;
	$sum_time += ($max_two_party + 2) * $pause;
}
debug ("Now: $sum_time");
if (defined $max_conf_rooms)
{
	$no_of_reboots++;
	$sum_time += ($max_conf_rooms * $conf_rooms_no_of_participants + 2) * $pause;
}
debug ("Now: $sum_time");
if (defined $max_conf_participants)
{
	$no_of_reboots++;
	$sum_time += ($max_conf_participants * $conf_participants_no_of_rooms + 2) * $pause;
}
debug ("Now: $sum_time");
$sum_time += $no_of_reboots * $reboot_time;
debug ("Sum-Time is now '$sum_time'");

# convert to readable time
my ($sec, $min, $hour);
$hour = int ($sum_time/3600);
$min = int ($sum_time/60 - $hour*60);
$sec = int ($sum_time - $hour*3600 - $min*60);
my $text = "This test is going to need about ";
($text .= int($hour)." hours, ") if ($hour);
($text .= int($min)." minutes and ") if ($min);
$text .= int($sec)." seconds.";
print $text."\n";
debug ($text);

#########################################################
### 1. GET INFOS FROM ASKOZIA-BOX AND EDIT CONFIGFILE ###
#########################################################
debug ("### CHAPTER 1 ###");

### 1.a) Download translation table ###
my $ua = new LWP::UserAgent;
$ua->credentials ("$ask_ip:$ask_port", $ask_realm, "$ask_user" => "$ask_pw");
my $res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27core+show+translation+recalc+10%27");

debug ("Status: '". $res->status_line ."'");
(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
if ($res->is_success)
{
	my $table_ref = interpret_translation_table ($res->decoded_content);
	draw_translation_table ($testname, $testpath, $table_ref, \@codecs_to_draw, \@PBXs_to_compare);
	open (TRANSLATION, ">$translation_table.csv") || mydie ("Could not open '$translation_table.csv' for writing: $!");
	for my $row (@$table_ref)
	{
		(print TRANSLATION "$_\t") for (@$row);
		print TRANSLATION "\n";
	}
	close TRANSLATION;
	system ("chmod", "666", "$translation_table.csv");
}
else
{
	warn ("No success in downloading translation table.");
}

### 1.b) Execute POST-Request and download file ###
debug ("Begin downloading configfile.");
$res = $ua->request (POST "http://$ask_ip:$ask_port/$ask_conf_page", "Content-Type" => "multipart/form-data", Content => [ Download => "1"]);
debug ("Ready downloading configfile. Status: ". $res->status_line);

# Was the download successful AND the POST-Request correct?
if ($res->is_success && $res->filename ne "system_backup.php")
{
	debug ("Begin saving original configfile.");
	$xml_configfile = File::Spec->rel2abs ($res->filename, $testpath);
	open (CONFFILE, ">", $xml_configfile) || mydie ("Could not open '". $xml_configfile ."' for writing: $!");
	print CONFFILE $res->decoded_content;
	close CONFFILE;
	system ("chmod", "666", "$xml_configfile");
	debug ("Ready saving original configfile '". $xml_configfile ."'");
}

# Was the download successful, but we got only the "system_backup.php"-results?
elsif ($res->is_success)
{
	debug ("Download successful, but we got '". $res->filename ."'");
	mydie ("Error in POST-Request-Parameters: The AskoziaPBX did not send the configfile.");
}

# The download was completely NOT successful
else
{
	debug ("Download failed.");
	
	# Try to print the HTML-Error to a file
	my $html_file = File::Spec->rel2abs ("HTML-Download-Error.html", $testpath);
	my $open_result = open (CONFERROR, ">", $html_file);
	if ($open_result)
	{
		print CONFERROR $res->error_as_HTML;
		close CONFERROR;
		system ("chmod", "666", "$html_file");
		mydie ("Could not download xml-configfile from askozia-box: ". $res->status_line ."\nSaved output in file '$html_file'");
	}

	# Can't print the HTML-Error to a file, so print it at stdout
	else
	{
		print $res->error_as_HTML;
		warn "Could not open '$html_file' for printing HTML-Error: $!\n";
		mydie ("Could not download xml-configfile from askozia-box: ". $res->status_line);
	}
}

### 1.c) Edit Configfile ###
# calculate needed users and conference rooms
my ($users_conf, $rooms_conf) = create_conf_contents
	($conffile_text_usertext, $conffile_conftext, $max_two_party, $max_conf_participants, $conf_participants_no_of_rooms, $max_conf_rooms, $conf_rooms_no_of_participants);

# Print calculated values in new configfile
my @orig_conffile = split (/\n/, $res->decoded_content);
open (EDITED_CONFFILE, ">", $xml_configfile."_edited") || mydie ("Could not open file '". $xml_configfile ."_edited' for writing: $!");
my ($active_paragraph, $sip_printed, $conf_printed);
for my $line (@orig_conffile)
{
	# Begin of sip phones paragraph?
	if ($line =~ /<sip>/)
	{
		print EDITED_CONFFILE "\t<sip>\n";
		$active_paragraph = "sip";
	}

	# End of sip phones paragraph?
	elsif ((defined $active_paragraph) && ($active_paragraph eq "sip") && ($line =~ m+</sip>+))
	{
		print EDITED_CONFFILE $users_conf;
		print EDITED_CONFFILE "\t</sip>\n";
		$sip_printed = 1;
		undef $active_paragraph;
	}
	
	# Begin of conferencing paragraph?
	elsif ($line =~ /<conferencing>/)
	{
		print EDITED_CONFFILE "\t<conferencing>\n";
		$active_paragraph = "conf";
	}

	# End of conferencing paragraph?
	elsif ((defined $active_paragraph) && ($active_paragraph eq "conf") && ($line =~ m+</conferencing>+))
	{
		print EDITED_CONFFILE $rooms_conf;
		print EDITED_CONFFILE "\t</conferencing>\n";
		$conf_printed = 1;
		undef $active_paragraph;
	}

	# End of complete configuration file?
	elsif ($line =~ m+</askoziapbx>+)
	{
		# Were no sip phones found until now?
		unless (defined $sip_printed)
		{
			# Add the needed sip phones paragraph
			print EDITED_CONFFILE "\t<sip>\n";
			print EDITED_CONFFILE $users_conf;
			print EDITED_CONFFILE "\t</sip>\n";
		}
		
		# Were no conference rooms found until now?
		unless (defined $conf_printed)
		{
			# Add the needed conference rooms paragraph
			print EDITED_CONFFILE "\t<conferencing>\n";
			print EDITED_CONFFILE $rooms_conf;
			print EDITED_CONFFILE "\t</conferencing>\n";
		}
		print EDITED_CONFFILE "</askoziapbx>\n";
	}

#	else
	elsif (not defined $active_paragraph)
	{
		print EDITED_CONFFILE "$line\n";
	}
}
close EDITED_CONFFILE;
system ("chmod", "666", "$xml_configfile"."_edited");

### 1.d) Re-Upload edited configfile ###
debug ("Begin uploading configfile '". $xml_configfile ."_edited'");
$res = $ua->request (POST "http://$ask_ip:$ask_port/$ask_conf_page", "Content-Type" => "multipart/form-data",
	Content => [ Restore => "1", conffile => [ $xml_configfile."_edited" ] ]);
debug ("Status: '". $res->status_line ."'");
reboot_askozia ($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);

#############################################
### 2. Generate 'Users.csv'-file for sipp ###
#############################################
debug ("### CHAPTER 2 ###");
debug ("Creating new csv-files.");

### 2.a) Generate Users-File for two-party-tests
if ($max_two_party)
{
	debug ("Generating users for two-party-call.");
	open (CSV, ">", $users_two_party_file) || mydie ("Could not open Users-two-party-File '$users_two_party_file' for writing: $!");
	debug ("Opened two-party-csv-file, filling with:");
	print CSV "SEQUENTIAL\n";

	for (my $cur_call = 1; $cur_call <= 2*$max_two_party; $cur_call += 2)
	{
		my $userA = sprintf ("%03d", $cur_call);
		my $userB = sprintf ("%03d", $cur_call+1);
		my $printtext =	"$userA;[authentication username=$userA password=$userA];".
						"$userB;[authentication username=$userB password=$userB]";
		print CSV $printtext."\n";
		debug ("two-party-File: ". $printtext);
	}

	close CSV;
	system ("chmod", "666", "$users_two_party_file");
	debug ("Ready generating users for two-party-call.");
}

### 2.b) Generate Users-File for "conference participants test" 
if ($max_conf_participants)
{
	debug ("Generating users for 'conference participants test'");
	open (CSV, ">", $users_conf_participants_file) || mydie ("Could not open Users-Conference-Room-File '$users_conf_participants_file' for writing: $!");
	debug ("Opened Users-Conference-Room-File, filling with:");
	print CSV "SEQUENTIAL\n";

	for (my $cur_call = 0; $cur_call <= $max_conf_participants-1; $cur_call++)
	{
		for (my $cur_room=0; $cur_room <= $conf_participants_no_of_rooms-1; $cur_room++)
		{
			my $userno = $cur_call * $conf_participants_no_of_rooms + $cur_room + 1;
			my $roomno = 2000 + $cur_room;
			my $user = sprintf ("%03d", $userno);
			my $printtext = "$user;[authentication username=$user password=$user];". $roomno;
			print CSV $printtext."\n";
			debug ("Conf-File 'participants test': ". $printtext);
		}
	}

	close CSV;
	system ("chmod", "666", "$users_conf_participants_file");
	debug ("Ready generating users for 'conference participants test'");
}

### 2.c) Generate Users-File for "conference rooms test" 
if ($max_conf_rooms)
{
	debug ("Generating users for 'conference rooms test'");
	open (CSV, ">", $users_conf_rooms_file) || mydie ("Could not open Users-Conference-Call-File '$users_conf_rooms_file' for writing: $!");
	debug ("Opened Users-Conference-Call-File, filling with:");
	print CSV "SEQUENTIAL\n";

	for (my $cur_room=0; $cur_room <= $conf_rooms_no_of_participants-1; $cur_room++)
	{
		my $roomno = 2000 + $cur_room;
		for (my $cur_call=0; $cur_call <= $max_conf_rooms-1; $cur_call++)
		{
			my $userno = $cur_room * $max_conf_rooms + $cur_call + 1;
			my $user = sprintf ("%03d", $userno);
			my $printtext = "$user;[authentication username=$user password=$user];". $roomno;
			print CSV $printtext."\n";
			debug ("Conf-File 'rooms test': ". $printtext);
		}
	}

	close CSV;
	system ("chmod", "666", "$users_conf_rooms_file");
	debug ("Ready generating users for 'conference rooms test'");
}

####################################
### 3. Execute performance-tests ###
####################################
debug ("### CHAPTER 3 ###");

### 3.a) Prepare watchdog

my $pipe = new IO::Pipe;
my $pid = fork();

# This is the Test-Process.
if ($pid == 0)
{
	$pipe->writer();
	$pipe->autoflush();
	print "Test-Process started.\n";
}

# This is the watchdog process
elsif ($pid > 0)
{
	print "Watchdog-Process started.\n";
	$pipe->reader();
	my $s = IO::Select->new;
	$s->add(\*$pipe);

	my $pause = 0;
	my $users = 0;
	my $counter = 0;
	my $watchdog_active = 0;

	while (1)
	{
		# Is there a message in the buffer?
		if ($s->can_read(0))
		{
			# Get message and interpret it
			# here is enough space for your own commands :-)
			my $msg = <$pipe>;
			(next) if ((not defined $msg) || ($msg =~ /^(\\n)*$/));
			chomp ($msg);
			debug ("WATCHDOG: Received message '$msg'");
			if		($msg eq "Tests finished.")			{ debug ("WATCHDOG: Accepted finishing tests.")	&& exit(0);					}
			elsif	($msg =~ /^set pause '(\d+)'$/)		{ debug ("WATCHDOG: Pause set to '$1'.")		&& ($pause = $1);			}
			elsif	($msg =~ /^set users '(\d+)'$/)		{ debug ("WATCHDOG: Users set to '$1'.")		&& ($users = $1);			}
			elsif	($msg eq "watchdog start")			{ debug ("WATCHDOG: Started.")					&& ($watchdog_active = 1);	}
			elsif	($msg eq "watchdog stop")			{ debug ("WATCHDOG: Stopped.")					&& ($watchdog_active = 0);	}

			$counter = 0;
		}

		# There is no message in the buffer
		elsif ($watchdog_active)
		{
			# Increment the counter and look if the time has come...
			# pause: Your pausetime has to be considered - if it is 5 minutes, the test may not be cancelled after 10 secs
			# current_calls: if you have 50 calls to be established, you need some extra time for register and deregister
			my $max_time = 5 + ($users+2) * $pause;
			debug ("WATCHDOG: Counter = '$counter', pause = '$pause', users = '$users' => Max = $max_time");
			if ($counter++ >= $max_time)
			{
				# Slap the testprocess with an INT-signal
				system ("kill", "-INT", "$pid");
				print "Sent INT-Signal to Test-Process.\n";
				debug ("Sent INT-Signal to Test-Process.");
			}
			sleep (1);
		}
		sleep (0.1);
	}
	exit (0);
}

# Fork does not want to clone the process :-(
else
{
	mydie ("Could not fork: $!");
}

### 3.b) Execute two-party-tests
if ($max_two_party)
{
	# 0: Prepare test
	# 1: Register all users
	# 2: Start sipp process for accepting users on local testcomputer
	# 3: Start watchdog
	# 4: Execute tests

	debug ("Starting two-party-tests...");
	print $pipe "set pause '$pause'\n";

	# prepare commands for sipp to ensure that the "debug"-printed commands are REALLY the commands that were executed
	debug ("Preparing commands");
	my $call_duration = ($max_two_party+2) * $pause;
	my $constant = "'$sipp_exe' -aa -inf '$users_two_party_file' -m $max_two_party -i $local_ip ";
	my $reg_cmd = $constant . "-p $sip_dst_port -mp $rtp_dst_port -sf '$reg_scen' -r 3 $ask_ip 2>&1";
	my $acc_cmd = $constant . "-p $sip_dst_port -mp $rtp_dst_port -sf '$acc_scen' -bg $ask_ip 2>&1 &";
	my $inv_cmd = $constant . "-p $sip_src_port -mp $rtp_src_port -sf '$inv_scen' -r 1 -rp ". $pause ."s -d ". $call_duration ."s $ask_ip 2>&1";
	my $der_cmd = $constant . "-p $sip_dst_port -mp $rtp_dst_port -sf '$der_scen' -r 3 $ask_ip 2>&1";
	
	# Register all users that are being called
	debug ("Beginning Register:");
	debug ("'$reg_cmd'");
	my @testresult;
	push (@testresult, "#"x30 ." REGISTER ". "#"x30 ."\n" . `$reg_cmd`);

	# Start sipp accept process on testcomputer for all users that are being called
	debug ("Beginning Accept (no messages here):");
	debug ("'$acc_cmd'");
	`$acc_cmd`;
	
	# Start QStat-Recording on AskoziaPBX
	debug ("Starting QStat-Recording");
	sleep (10);
#	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27core+set+debug+1000%27");
#	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27core+set+verbose+1000%27");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27qstat+start+two-party%27");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if ($res->decoded_content ne "[qstat] started\n")
	{
		my $text = "Could not start QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}

	# give the AskoziaPBX enough time to register, accept, invite and deregister all users
	print $pipe "set users '$max_two_party'\n";
	sleep (1);
	print $pipe "watchdog start\n";

	# START CALLS #
	debug ("Beginning Invite:");
	debug ("'$inv_cmd'");
	push (@testresult, "#"x30 ." STARTING CALL ". "#"x30 ."\n". `$inv_cmd`);
	print $pipe "watchdog stop\n";

	# deregister called user to avoid the askoziaPBX to send "OPTIONS" messages
	debug ("Beginning Deregister");
	debug ("'$der_cmd'");
	push (@testresult, "#"x30 ." DEREGISTER ". "#"x30 ."\n". `$der_cmd`);

	# Save the sipp-log-output in a logfile if wanted
	if (defined $save_sipp_log)
	{
		debug ("Saving sipp log in file '$save_sipp_log'");
		open (LOG, ">>", $save_sipp_log) || mydie ("Could not open logfile '$save_sipp_log' for writing: $!\n");
		(print LOG $_) for (@testresult);
		close LOG;
		system ("chmod", "666", "$save_sipp_log");
		debug ("Ready saving sipp log.");
	}
	debug ("Ready with two-party tests, cleaning up now");

	# Cleaning up and stop qstat recording on AskoziaPBX
	undef $term_handler_called;
	debug ("Try to stop QStat-Recordings");
	$res = $ua->request (GET "http://$ask_ip/cgi-bin/ajax.cgi?exec_ami='qstat+stop'");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if (not($res->decoded_content =~ /^\[qstat\] stopped/) && $res->decoded_content ne "")
	{
		my $text = "Could not stop QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}
	
	# Download QStat-Data (automatic save in file)
	download_qstat_data ($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);
	
	# Interpret QStat-Data
	interpret_data ("two-party", $pause, $max_two_party);
	
	# Reboot AskoziaPBX
	reboot_askozia($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);
	debug ("Finished two-party-tests");
}

### 3.c) Execute conference rooms test
if ($max_conf_rooms)
{
	debug ("Starting conference rooms test");
	print $pipe "set pause '$pause'\n";
	
	# Start QStat-Recording on AskoziaPBX
	debug ("Starting QStat-Recording");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27qstat+start+conf-rooms%27");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if ($res->decoded_content ne "[qstat] started\n")
	{
		my $text = "Could not start QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}
	sleep (1);

	my $max_users = $max_conf_rooms * $conf_rooms_no_of_participants;
	print $pipe "set users '$max_users'\n";
	debug ("Max users is '$max_users'");
	sleep (1);
	my $call_duration = ($max_users+2) * $pause;
	debug ("Call duration is '$call_duration'");
	my $inv_cmd = "'$sipp_exe' --trace_err -r 1 -rp ". $pause ."s -aa -m $max_users -d ". $call_duration ."s".
		" -inf '$users_conf_rooms_file' -i $local_ip -p $sip_src_port -mp $rtp_src_port -sf '$inv_scen' $ask_ip 2>&1";
	print $pipe "watchdog start\n";

	# START CALLS #
	debug ("Beginning Invite with command '$inv_cmd'");
	my @testresult;
	push (@testresult, "#"x30 ." STARTING CALL ". "#"x30 ."\n". `$inv_cmd`);

	# Save the sipp-log-output in a logfile if wanted
	if (defined $save_sipp_log)
	{
		debug ("Saving sipp-log in file '$save_sipp_log'");
		open (LOG, ">", $save_sipp_log) || mydie ("Could not open logfile '$save_sipp_log' for writing: $!\n");
		(print LOG $_) for (@testresult);
		close LOG;
		system ("chmod", "666", "$save_sipp_log");
		debug ("Ready saving sipp-log.");
	}
	debug ("Ready with conf call tests, cleaning up now");

	# Cleaning up and stop QStat-Recording on AskoziaPBX
	undef $term_handler_called;
	print $pipe "watchdog stop\n";
	debug ("Try to stop QStat-Recordings");
	$res = $ua->request (GET "http://$ask_ip/cgi-bin/ajax.cgi?exec_ami='qstat+stop'");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if (not($res->decoded_content =~ /^\[qstat\] stopped/) && $res->decoded_content ne "")
	{
		my $text = "Could not stop QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}

	# Download QStat-Data
	download_qstat_data ($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);

	# Interpret QStat-Data
	interpret_data ("conf-rooms", $pause, $max_conf_rooms, $conf_rooms_no_of_participants);

	# Reboot AskoziaPBX
	reboot_askozia($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);
	debug ("Finished conference rooms test");
}

### 3.d) Execute conference participants test
if ($max_conf_participants)
{
	debug ("Starting 'conference participants test'");
	print $pipe "set pause '$pause'\n";
	
	# Start QStat-Recording on AskoziaPBX
	debug ("Starting QStat-Recording");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_ami=%27qstat+start+conf-participants%27");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if ($res->decoded_content ne "[qstat] started\n")
	{
		my $text = "Could not start QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}
	sleep (1);

	# Prepare test
	my $max_users = $max_conf_participants * $conf_participants_no_of_rooms;
	print $pipe "set users '$max_users'\n";
	sleep (1);
	print $pipe "watchdog start\n";
	my $call_duration = ($max_users+2) * $pause;
	my $inv_cmd = "'$sipp_exe' --trace_err -r 1 -rp ". $pause ."s -aa -m $max_users -d ". $call_duration ."s".
		" -inf '$users_conf_participants_file' -i $local_ip -p $sip_src_port -mp $rtp_src_port -sf '$inv_scen' $ask_ip 2>&1";

	# START CALLS #
	debug ("Beginning Invite with command '$inv_cmd'");
	my @testresult;
	push (@testresult, "#"x30 ." STARTING CALL ". "#"x30 ."\n". `$inv_cmd`);

	# Save the sipp-log-output in a logfile if wanted
	if (defined $save_sipp_log)
	{
		debug ("Saving sipp-log in file '$save_sipp_log'");
		open (LOG, ">>", $save_sipp_log) || mydie ("Could not open logfile '$save_sipp_log' for writing: $!\n");
		(print LOG $_) for (@testresult);
		close LOG;
		system ("chmod", "666", "$save_sipp_log");
		debug ("Ready saving sipp-log.");
	}
	debug ("Ready with conf room tests, cleaning up now");
	
	# Cleaning up and stop QStat-Recording on AskoziaPBX
	print $pipe "watchdog stop\n";
	debug ("Try to stop QStat-Recordings");
	$res = $ua->request (GET "http://$ask_ip/cgi-bin/ajax.cgi?exec_ami='qstat+stop'");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	if (not($res->decoded_content =~ /^\[qstat\] stopped/) && $res->decoded_content ne "")
	{
		my $text = "Could not stop QStat-recording:";
		($text .= "$_\n") for (split ("\n", $res->decoded_content));
		mydie ("$text\n");
	}

	# Download QStat-Data
	download_qstat_data ($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);
	
	# Interpret QStat-Data
	interpret_data ("conf-participants", $pause, $max_conf_participants, $conf_participants_no_of_rooms);

	# Reboot AskoziaPBX
	reboot_askozia($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);
	debug ("Finished conference participants test");
}

# End watchdog
print $pipe "Tests finished.\n";

###############################################
### 4. Restore old AskoziaPBX-Configuration ###
###############################################
debug ("### CHAPTER 5 ###");

# Shell the old configuration BEFORE the test should be restored?
if ($restore eq "old-config")
{
	debug ("The old configuration should be restored. Beginning...");
	$res = $ua->request (POST "http://$ask_ip:$ask_port/$ask_conf_page", "Content-Type" => "multipart/form-data",
		Content => [ Restore => "1", conffile => [ $xml_configfile ] ]);
	debug ("Status: '". $res->status_line ."'");
	debug ("The old configuration was restored.");
}

# Should the AskoziaPBX should be reset to factory-defaults?
elsif($restore eq "factory-defaults")
{
	debug ("The AskoziaPBX should be reset to factory-defaults.");
	$res = $ua->request (POST "http://$ask_ip:$ask_port/system_defaults.php", Content => [ Yes => "1" ]);
	debug ("Status: '". $res->status_line ."'");
	debug ("Factory-Defaults were restored.");
}

### END ###
system ("killall", "-9", "sipp");
print ("### END OF TEST ###");
exit (0);

#################
### FUNCTIONS ###
#################


########################
# CREATE_CONF_CONTENTS #
########################
# Calculates from the number of twoway- and conference-tests
# how many users and conference-rooms shell be added to the AskoziaPBX
#
# Takes following needed arguments:
# 1. Configfile-Usertext: Template-Text for the xml-configfile for sipphone
# 2. Confilfile-Roomtext: Template-text for the xml-configfile for conference-rooms
# 3. Twoway-Calls: The maximum number of the twoway-calls of all performance-tests
# 4. Conf-Calls-Room: The maximum number of conference-calls of all performance-tests with fixed number of rooms
# 5. Conf-Rooms-Room: The maximum number of conferencing-rooms of all performance-tests with fixed number of rooms
# 6. Conf-Calls-Call: The maximum number of conference-calls of all performance-tests with fixed number of calls
# 7. Conf-Rooms-Call: The maximum number of conferencing-rooms of all performance-tests with fixed number of calls
# Returns an array with following contents:
# 1. Needed Users for the AskoziaPBX
# 2. Needed Conference-Rooms for the AskoziaPBX
sub create_conf_contents
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $conffile_text_user					= shift || mydie ("No 'configfile-usertext'-argument specified in function '$this_function'!");
	(ref $conffile_text_user)				&& mydie ("'Configfile-usertext'-argument has to be scalar in function '$this_function'!");
	(defined $conffile_text_user)			? debug ("conffile_text_user is '\n$conffile_text_user'")	: debug ("conffile_text_user is not defined.");
	
	my $conffile_text_room					= shift || mydie ("No 'configfile-roomtext'-argument specified in function '$this_function'!");
	(ref $conffile_text_room)				&& mydie ("'Configfile-roomtext'-argument has to be scalar in function '$this_function'!");
	(defined $conffile_text_room)			? debug ("conffile_text_room is '\n$conffile_text_room'")	: debug ("conffile_text_room is not defined.");
	
	my $max_two_party						= shift;
	(ref $max_two_party)					&& mydie ("'Twoway_Calls'-argument has to be scalar in function '$this_function'!");
	(defined $max_two_party)				? debug ("twoway_calls is '$max_two_party'")				: debug ("twoway_calls is not defined.");
	
	my $max_conf_participants				= shift;
	(ref $max_conf_participants)			&& mydie ("'Conf_calls_room'-argument has to be scalar in function '$this_function'!");
	(defined $max_conf_participants)		? debug ("conf_calls_room is '$max_conf_participants'")		: debug ("conf_calls_room is not defined.");
	
	my $conf_participants_no_of_rooms		= shift;
	(ref $conf_participants_no_of_rooms)	&& mydie ("'Conf_rooms_room'-argument has to be scalar in function '$this_function'");
	(defined $conf_participants_no_of_rooms)? debug ("conf_rooms_room is '$conf_participants_no_of_rooms'")		: debug ("conf_rooms_room is not defined.");
	
	my $max_conf_rooms						= shift;
	(ref $max_conf_rooms)					&& mydie ("'Conf_calls_call'-argument has to be scalar in function '$this_function'!");
	(defined $max_conf_rooms)				? debug ("conf_calls_call is '$max_conf_rooms'")		: debug ("conf_calls_call is not defined.");
	
	my $conf_rooms_no_of_participants		= shift;
	(ref $conf_rooms_no_of_participants)	&& mydie ("'Conf_rooms_call'-argument has to be scalar in function '$this_function'");
	(defined $conf_rooms_no_of_participants)? debug ("conf_rooms_call is '$conf_rooms_no_of_participants'")		: debug ("conf_rooms_call is not defined.");
	
	my $needed_users = 0;
	my $needed_rooms = 0;
	my $new_usertext = "";
	my $new_roomtext = "";

	# MAIN

	# determine the maximum number of users needed
	if (defined ($max_two_party))
	{
		$needed_users = 2 * $max_two_party;
	}
	if (defined ($max_conf_participants))
	{
		($conf_participants_no_of_rooms = 1) unless (defined $conf_participants_no_of_rooms);
		($needed_users = $max_conf_participants * $conf_participants_no_of_rooms) if ($needed_users < ($max_conf_participants * $conf_participants_no_of_rooms));
	}
	if (defined ($max_conf_rooms))
	{
		($conf_rooms_no_of_participants = 1) unless (defined $conf_rooms_no_of_participants);
		($needed_users = $max_conf_rooms * $conf_rooms_no_of_participants) if ($needed_users < ($max_conf_rooms * $conf_rooms_no_of_participants));
	}

	# determine the maximum number of rooms needed
	($needed_rooms = $conf_participants_no_of_rooms) if (defined $conf_participants_no_of_rooms);
	($needed_rooms = $conf_rooms_no_of_participants) if ((defined $conf_rooms_no_of_participants) && ($conf_rooms_no_of_participants > $needed_rooms));

	# Create configfile-text for users
	for my $userno (1..$needed_users)
	{
		$new_usertext .= $conffile_text_user;
		my $usertext = sprintf ("%03d", $userno);
		$new_usertext =~ s/_userno_/$usertext/g;
	}

	# Create configfile-text for conference-rooms
	for my $roomno (2000..2000+$needed_rooms-1)
	{
		$new_roomtext .= $conffile_text_room;
		$new_roomtext =~ s/_roomno_/$roomno/g;
	}

	debug ("New roomtext is '\n$new_roomtext'");
	debug ("### END FUNCTION ###");
	return ($new_usertext, $new_roomtext);
}

################
# TERM_HANDLER #
################
# this function is the reaction of sending the TERM-Signal to this script
# (STRG+C)
sub term_handler
{
	system ("killall", "-9", "sipp");
	(print $pipe "watchdog stop\n") if (defined $pipe);
	$term_handler_called = 1;
}

#########
# DEBUG #
#########
# Prints textmessage to stdout if $debug is set
#
# Takes following optional arguments:
# 1. Textmessage to print
BEGIN {
my $debug_indent = 0;
sub debug
{
	(return 1) unless $debug;

	my $message = shift || "";
	if (ref ($message) eq "ARRAY")
	{
		my $printtext = "";
		($printtext .= "DEBUG: - '$_'\n") for (@$message);
	}

	($debug_indent++) if ($message =~ /^### FUNCTION:/);
	
	print "DEBUG: " .(" "x(2*$debug_indent)). "$message\n";
	if (defined $save_debug)
	{
		open (DEBUG, ">>", $save_debug) || mydie ("Could not open '$save_debug' for appending: $!\n");
		print DEBUG " "x(2*$debug_indent) . $message ."\n";
		close DEBUG;
		system ("chmod", "666", "$save_debug");
	}

	($debug_indent--) if ($message eq "### END FUNCTION ###");
	return 1;
}}

#######################
# DOWNLOAD_QSTAT_DATA #
#######################
# downloads all existing QStat-Files from AskoziaPBX
sub download_qstat_data
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $ask_ip					= shift || mydie ("No 'Askozia-IP'-argument specified in function '$this_function'!");
	(ref $ask_ip)				&& mydie ("'Askozia-IP'-argument has to be scalar in function '$this_function'!");
	(defined $ask_ip)			? debug ("ask_ip is '$ask_ip'")	: debug ("ask_ip is not defined.");
	
	my $ask_port				= shift || mydie ("No 'Askozia-Port'-argument specified in function '$this_function'!");
	(ref $ask_port)				&& mydie ("'Askozia-Port'-argument has to be scalar in function '$this_function'!");
	(defined $ask_port)			? debug ("ask_port is '$ask_port'")	: debug ("ask_port is not defined.");
	
	my $ask_user				= shift || mydie ("No 'Askozia-User'-argument specified in function '$this_function'!");
	(ref $ask_user)				&& mydie ("'Askozia-User'-argument has to be scalar in function '$this_function'!");
	(defined $ask_user)			? debug ("ask_user is '$ask_user'")	: debug ("ask_user is not defined.");
	
	my $ask_pw					= shift || "";
	(ref $ask_pw)				&& mydie ("'Askozia-Password'-argument has to be scalar in function '$this_function'!");
	(defined $ask_pw)			? debug ("ask_pw is '$ask_pw'")				: debug ("ask_pw is not defined.");
	
	my $ask_realm				= shift || mydie ("No 'Askozia-Realm'-argument specified in function '$this_function'!");
	(ref $ask_realm)			&& mydie ("'Askozia-Realm'-argument has to be scalar in function '$this_function'!");
	(defined $ask_realm)		? debug ("ask_realm is '$ask_realm'")				: debug ("ask_realm is not defined.");
	
	### Download all qstat-files ###
	# get qstat-php-file from AskoziaPBX to determine the existing file(name)s
	debug ("Beginning with download of qstat-page");
	my $ua = new LWP::UserAgent;
	$ua->credentials ("$ask_ip:$ask_port", $ask_realm, "$ask_user" => "$ask_pw");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_shell=cat+/var/asterisk/log/qstat/*");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	
	# Success of executing request
	if ($res->is_success)
	{
		debug ("Filter relevant qstat-data");
		
		my $filename;
		my @qstat_data;
		for my $line (split ("\n", $res->decoded_content))
		{
			# Comment line with evtl. relevant information
			if ($line =~ /^#/)
			{
				if ($line =~ /description: (.*)$/)	{ $filename = $1; }
			}
	
			# No empty line: a line with really measured values
			elsif ($line ne "")
			{
	       		my @lineparts = split (/ /, $line);
				push (@qstat_data, $lineparts[0] ."\t". $lineparts[2] ."\t". $lineparts[9]);
			}
		}

		# Copy contents into file
		$filename = File::Spec->rel2abs ("$filename.qstat", $testpath);
		open (QSTAT, ">$filename") || mydie ("Could not open file '$filename' for writing: $!\n");
		(print QSTAT $_."\n") for (@qstat_data);
		close QSTAT;
		system ("chmod", "666", "$filename");

		# Debug messages
		debug ("QStat-Data is now:");
		(debug ("QStat: $_")) for (@qstat_data);
		debug ("### END FUNCTION ###");
		return \@qstat_data;
	}
		
	# No success of executing request
	else
	{
		warn ("No success in executing qstat-request.");
		debug ("### END FUNCTION ###");
		return undef;
	}
}

##################
# REBOOT_ASKOZIA #
##################
# reboots the AskoziaPBX and waits until it is completely restarted
sub reboot_askozia
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $ask_ip					= shift || mydie ("No 'Askozia-IP'-argument specified in function '$this_function'!");
	(ref $ask_ip)				&& mydie ("'Askozia-IP'-argument has to be scalar in function '$this_function'!");
	(defined $ask_ip)			? debug ("ask_ip is '$ask_ip'") : debug ("ask_ip is not defined.");
	
	my $ask_port				= shift || mydie ("No 'Askozia-Port'-argument specified in function '$this_function'!");
	(ref $ask_port)				&& mydie ("'Askozia-Port'-argument has to be scalar in function '$this_function'!");
	(defined $ask_port)			? debug ("ask_port is '$ask_port'")	: debug ("ask_port is not defined.");
	
	my $ask_user				= shift || mydie ("No 'Askozia-User'-argument specified in function '$this_function'!");
	(ref $ask_user)				&& mydie ("'Askozia-User'-argument has to be scalar in function '$this_function'!");
	(defined $ask_user)			? debug ("ask_user is '$ask_user'") : debug ("ask_user is not defined.");
	
	my $ask_pw					= shift || "";
	(ref $ask_pw)				&& mydie ("'Askozia-Password'-argument has to be scalar in function '$this_function'!");
	(defined $ask_pw)			? debug ("ask_pw is '$ask_pw'") : debug ("ask_pw is not defined.");
	
	my $ask_realm				= shift || mydie ("No 'Askozia-Realm'-argument specified in function '$this_function'!");
	(ref $ask_realm)			&& mydie ("'Askozia-Realm'-argument has to be scalar in function '$this_function'!");
	(defined $ask_realm)		? debug ("ask_realm is '$ask_realm'") : debug ("ask_realm is not defined.");
	
	my $ua = new LWP::UserAgent;
	$ua->credentials ("$ask_ip:$ask_port", $ask_realm, "$ask_user" => "$ask_pw");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_shell=reboot");
	
	debug ("Wait for the AskoziaPBX to be rebooted");
	my $success = 0;
	do
	{
		sleep (1);
		$success = system ("ping -c 1 -w 1 $ask_ip > /dev/null");
	} while (not $success);
	
	debug ("AskoziaPBX is rebooting now");
	do
	{
		sleep (1);
		$success = system ("ping -c 1 -w 1 $ask_ip > /dev/null");
	} while ($success);
	
	debug ("AskoziaPBX is online - wait '$reboot_time' seconds for completing its boot sequence...");
	sleep ($reboot_time);
	debug ("### END FUNCTION ###");
}

##################
# INTERPRET_DATA #
##################
# takes a file and creates the mittelwert-fields
sub interpret_data
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	# get parameters
	my $type			= shift || mydie ("No 'Type'-argument specified in function '$this_function'!");
	(ref $type)			&& mydie ("'Type'-argument has to be scalar in function '$this_function'!");
	(defined $type)		? debug ("Type is '$type'") : debug ("Type is not defined.");

	my $pause			= shift;
	(ref $pause)		&& mydie ("'Pause'-argument has to be scalar in function '$this_function'!");
	(defined $pause)	? debug ("Pause is '$pause'") : debug ("Pause is not defined.");

	my $calls			= shift || mydie ("No 'Calls'-argument specified in function '$this_function'!");
	(ref $calls)		&& mydie ("'Calls'-argument has to be scalar in function '$this_function'!");
	(defined $calls)	? debug ("Calls is '$calls'") : debug ("Calls is not defined.");

	my $rooms			= shift || 1;
	(ref $rooms)		&& mydie ("'Rooms'-argument has to be scalar in function '$this_function'!");
	(defined $rooms)	? debug ("Rooms is '$rooms'") : debug ("Rooms is not defined.");

	# get file contents
	my @old_file;
	my $qstat_file = File::Spec->rel2abs ("$type.qstat", $testpath);
	open (OLD_FILE, "<", $qstat_file) || mydie ("Cannot open $qstat_file for reading: $!");
	@old_file = <OLD_FILE>;
	close OLD_FILE;

	# copy old file contents in new array incl. splitting every line in a second array
	# hint: work_file is an array of array-references
	#       each reference in the work_file-array contains columns: "Time", "Active Calls", "CPU load"
	#       in the first lines are additional columns: "active calls", "mean-value of all cpu-load-values of this number of calls"
	my @work_file;
	for my $line (@old_file)
	{
		chomp $line;
		my @lineparts = split ("\t", $line);
		push (@work_file, \@lineparts);
		debug ("Data: ". join(" | ", @lineparts));
	}

	debug ("Interpreting slow-and-steadily building-up test");
	my $cur_index;
	my $cur_call = 1;
	my @temp_mean_values;
	my @mean_values;

	# add "0,0" at begin
	push (@{$work_file[0]}, "0", "0");
	
	# go through complete array of measured values
	my $index = 0;
	while ($index < @work_file)
	{
		my @values = @{$work_file[$index]};
		debug ("Index is '$index', looking at value '". $values[2] ."'");

		# a value of the current call
		if ($values[1] == $cur_call)
		{
			push (@temp_mean_values, $values[2]);
			debug ("Adding for call '$cur_call' value '$values[2]'");
		}

		# a value that does not belong to the current call
		elsif (@temp_mean_values && $values[1] != $cur_call)
		{
			debug ("No value of call '$cur_call', calculating mean-value");
			my $line = $work_file[$cur_call];
			my @sorted_mean_values = sort {$a <=> $b} (@temp_mean_values);
			debug ("Sorted mean values is:");
			(debug ("- '$_'")) for (@sorted_mean_values);
#			my $me = find_median (@sorted_mean_values);
			
			my (@tmp_inter_1, @tmp_inter_2);
			if (@sorted_mean_values % 2)
			{
				# odd number of array elements
				my $middle = $#sorted_mean_values/2;
				@tmp_inter_1 = @sorted_mean_values[0..$middle];
				@tmp_inter_2 = @sorted_mean_values[$middle..$#sorted_mean_values];
			}
			else
			{
				# even number of array elements
				my $middle = @sorted_mean_values/2;
				@tmp_inter_1 = @sorted_mean_values[0..$middle-1];
				@tmp_inter_2 = @sorted_mean_values[$middle..$#sorted_mean_values];
			}
			my $inter_start = find_median (\@tmp_inter_1);
			debug ("Inter-Start is '$inter_start'");
			my $inter_end = find_median (\@tmp_inter_2);
			debug ("Inter-End is '$inter_end'");
			my @inter_values;
			for (@sorted_mean_values)
			{
				debug ("Looking at '$_'");
				push (@inter_values, $_) if ($_ >= $inter_start && $_ <= $inter_end);
			}

			my $sum = 0;
			($sum += $_) for (@inter_values);
			my $mean_value = $sum/@inter_values;
			debug ("Mean value is '$mean_value'");
			$mean_value = 100 - $mean_value; # convert from idle time to cpu load
			my $last_mean_value = $work_file[$cur_call-1]->[4];
			debug ("Current mean-value is '$mean_value', the last value was '$last_mean_value'"); 
#			($mean_value >= $last_mean_value - 10)	? push (@$line, $cur_call/$rooms, $mean_value)
#			($mean_value >= $last_mean_value)		? push (@$line, $cur_call/$rooms, $mean_value)
#													: last;
			push (@$line, $cur_call/$rooms, $mean_value);
			
			undef @temp_mean_values;
			$cur_call++;
			if ($values[1] == $cur_call)
			{
				$index--;
				debug ("The current value belongs to '$cur_call' already, decrementing index: Index is now '$index'");
			}
		}
		$index++;
	}

	if (@temp_mean_values)
	{
		debug ("End of measured values, adding last mean-values");
		my $sum = 0;
		($sum += $_) for (@temp_mean_values);
		my $line = $work_file[$cur_call];
		my $last_mean_value = $work_file[$cur_call-1]->[4];
		my $mean_value = 100 - ($sum/@temp_mean_values);
#		push (@$line, $cur_call/$rooms, $mean_value) if ($mean_value >= $last_mean_value - 10);
#		push (@$line, $cur_call/$rooms, $mean_value) if ($mean_value >= $last_mean_value);
		push (@$line, $cur_call/$rooms, $mean_value);# if ($mean_value >= $last_mean_value);
	}

	my @new_file;
	(push (@new_file, join ("\t", @$_))) for (@work_file);
	(debug ("New Data: $_")) for (@new_file);

	# write new file contents in new file
	open (NEW_FILE, ">", $qstat_file .".csv") || mydie ("Cannot open $qstat_file.csv for writing: $!");
	(print NEW_FILE $_."\n") for (@new_file);
	close NEW_FILE;
	system ("chmod", "666", "$qstat_file");

	### CREATE GNUPLOT SCRIPTFILE ###

	# Determine text for x-axis
	my $x_text = "Unknown";
	if		($type eq "conf-participants")	{ ($rooms == 1) ? ($x_text = "Number of concurrent participants of a conference")
															: ($x_text = "Number of participants per conference room ($rooms existing rooms)"); }
	elsif	($type eq "conf-rooms")			{ $x_text = "Number of parallel conference rooms ($rooms participants in each room)"; }
	elsif	($type eq "two-party")			{ $x_text = "Number of parallel calls (twoleg calls)"; }

	# Determine Platform and version
	my ($platform, $version) = get_sut_data ($ask_ip, $ask_port, $ask_user, $ask_pw, $ask_realm);

	my $gnuplot_cmd = "# Type: $type\n";
	$gnuplot_cmd .= "set terminal png\n";
	$gnuplot_cmd .= "set output '$qstat_file.png'\n";
	
	$gnuplot_cmd .= "set title 'Performance test of \"$testname\" appliance'\n";
	$gnuplot_cmd .= "set xlabel '$x_text'\n";
	$gnuplot_cmd .= "set ylabel 'CPU load in %'\n";
	$gnuplot_cmd .= "set xrange [0:$calls]\n";
	$gnuplot_cmd .= "set yrange [0:100]\n";
	($gnuplot_cmd .= "set xtics 1\n") if ($calls <= 15);
	$gnuplot_cmd .= "set border 3\n";
	$gnuplot_cmd .= "set xtics axis\n";
	$gnuplot_cmd .= "set ytics axis\n";
	$gnuplot_cmd .= "set xzeroaxis lw 4\n";
	$gnuplot_cmd .= "set yzeroaxis lw 6\n";
	$gnuplot_cmd .= "unset grid\n";
	$gnuplot_cmd .= "unset key\n";
	$gnuplot_cmd .= "set pointsize 1.5\n";

	$gnuplot_cmd .= "set label 'Askozia". chr(174) ."PBX Software Version: $version' at 0.1,95.0\n";
	$gnuplot_cmd .= "set label 'Used Codecs: G711 A-Law' at 0.1,90.0\n";
	$gnuplot_cmd .= "set label 'Transcoding used: no' at 0.1, 85.0\n";
	$gnuplot_cmd .= "set label 'All participants: VoIP' at 0.1, 80.0\n";
	$gnuplot_cmd .= "set label 'Date: $timestamp' at 0.1, 75.0\n";

	(defined $pause)	? ($gnuplot_cmd .= "plot '$qstat_file.csv' using 4:5 with points, '$qstat_file.csv' using 4:5 smooth bezier with line lw 2\n")
#	(defined $pause)	? ($gnuplot_cmd .= "f(x)=a*x**3+b*x**2+c*x+d\n".
#							"fit f(x) '$qstat_file.csv' using 4:5 via a,b,c,d\n".
#							"plot f(x) with line lw 2\n")
#							"plot '$qstat_file.csv' using 4:5 with line lw 2, f(x) with line lw 2\n")
						: ($gnuplot_cmd .= "plot '$qstat_file.csv' using 4:5 with line lw 2\n");

	debug ("Gnuplot-Command is '$gnuplot_cmd'");
	open (GNU_FILE, ">", $qstat_file .".gplt") || mydie ("Cannot open ". $qstat_file .".gplt for writing: $!");
	print GNU_FILE $gnuplot_cmd;
	close GNU_FILE;
	system ("chmod", "666", "$qstat_file.gplt");
	system ("$gnuplot_exe", "$qstat_file.gplt");

	debug ("### END FUNCTION ###");
}

################
# GET_SUT_DATA #
################
sub get_sut_data
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $ask_ip					= shift || mydie ("No 'Askozia-IP'-argument specified in function '$this_function'!");
	(ref $ask_ip)				&& mydie ("'Askozia-IP'-argument has to be scalar in function '$this_function'!");
	(defined $ask_ip)			? debug ("ask_ip is '$ask_ip'") : debug ("ask_ip is not defined.");
	
	my $ask_port				= shift || mydie ("No 'Askozia-Port'-argument specified in function '$this_function'!");
	(ref $ask_port)				&& mydie ("'Askozia-Port'-argument has to be scalar in function '$this_function'!");
	(defined $ask_port)			? debug ("ask_port is '$ask_port'")	: debug ("ask_port is not defined.");
	
	my $ask_user				= shift || mydie ("No 'Askozia-User'-argument specified in function '$this_function'!");
	(ref $ask_user)				&& mydie ("'Askozia-User'-argument has to be scalar in function '$this_function'!");
	(defined $ask_user)			? debug ("ask_user is '$ask_user'") : debug ("ask_user is not defined.");
	
	my $ask_pw					= shift || "";
	(ref $ask_pw)				&& mydie ("'Askozia-Password'-argument has to be scalar in function '$this_function'!");
	(defined $ask_pw)			? debug ("ask_pw is '$ask_pw'") : debug ("ask_pw is not defined.");
	
	my $ask_realm				= shift || mydie ("No 'Askozia-Realm'-argument specified in function '$this_function'!");
	(ref $ask_realm)			&& mydie ("'Askozia-Realm'-argument has to be scalar in function '$this_function'!");
	(defined $ask_realm)		? debug ("ask_realm is '$ask_realm'") : debug ("ask_realm is not defined.");

	# Get Askozia-Version
	debug ("Try to get SUT-Version");
	my $ua = new LWP::UserAgent;
	$ua->credentials ("$ask_ip:$ask_port", $ask_realm, "$ask_user" => "$ask_pw");
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_shell=cat%20/etc/version");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	chomp (my $version = $res->decoded_content);

	# Get Askozia-Platform
	$res = $ua->request (GET "http://$ask_ip:$ask_port/cgi-bin/ajax.cgi?exec_shell=cat%20/etc/platform");
	debug ("Status: '". $res->status_line ."'");
	(debug ("Answer: '$_'")) for (split ("\n", $res->decoded_content));
	chomp (my $platform = $res->decoded_content);

	debug ("### END FUNCTION ###");
	return ($platform, $version);
}

##########################
# DRAW_TRANSLATION_TABLE #
##########################
# This script creates a 3-dimensional hash called "data" including ALL needed data for creating the gnuplot data files.
# structure of "data": %data{source codec}{destination codec}{PBX} = <value in milliseconds>
sub draw_translation_table
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $testname						= shift || mydie ("No 'Testpath'-argument specified in function '$this_function'!");
	(ref ($testname))					&& mydie ("'Testpath'-argument has to be scalar in function '$this_function'!");
	(defined $testname)					? debug ("testname is '$testname'") : debug ("testname is not defined.");

	my $testpath						= shift || mydie ("No 'Testpath'-argument specified in function '$this_function'!");
	(ref ($testpath))					&& mydie ("'Testpath'-argument has to be scalar in function '$this_function'!");
	(defined $testpath)					? debug ("testpath is '$testpath'") : debug ("testpath is not defined.");
	
	my $table_ref						= shift || mydie ("No 'Tableref'-argument specified in function '$this_function'!");
	(ref ($table_ref) eq "ARRAY")		|| mydie ("'Tableref'-argument has to be array-ref in function '$this_function'!");
	(defined $table_ref)				? debug ("table_ref is '$table_ref'") : debug ("table_ref is not defined.");

	my $codeclist_ref					= shift || mydie ("No 'Codeclist_Ref'-argument specified in function '$this_function'!");
	(ref ($codeclist_ref) eq "ARRAY")	|| mydie ("'Codeclist_Ref'-argument has to be array-ref in function '$this_function'!");
	(defined $codeclist_ref)			? debug ("codeclist_ref is '$codeclist_ref'") : debug ("codeclist_ref is not defined.");

	my $PBXlist_ref						= shift || mydie ("No 'PBXlist_Ref'-argument specified in function '$this_function'!");
	(ref ($PBXlist_ref) eq "ARRAY")		|| mydie ("'PBXlist_Ref'-argument has to be array-ref in function '$this_function'!");
	(defined $PBXlist_ref)				? debug ("PBXlist_ref is '$PBXlist_ref'") : debug ("PBXlist_ref is not defined.");

	# Print all codecs if no specific codec(s) was/were specified
	my @table = @$table_ref;
	unless (@$codeclist_ref)
	{
		$codeclist_ref = $table[0];
		shift @$codeclist_ref; # i don't want to have an empty entry here, so i have to subtract 1 to the indizes later
		debug ("Codeclist is now:");
		debug ("- '$_'") for (@$codeclist_ref);
	}

	# data is a hash_ref: $data->{src-codec}->{dst-codec}->{PBX-name} = timing-value
	my $data = {};
	
	# for every diagram that should be drawed
	for my $src_codec (@$codeclist_ref)
	{
		debug ();
		debug ("### Working at source codec '$src_codec'");
		$data->{$src_codec} = {};

		# collect my own PBX's data
		debug ("Adding my own data (PBX '$testname'):");
		for my $row_index (1..@$table_ref-1)
		{
			my $row = $table_ref->[$row_index];
			my $row_codec = $row->[0];

			if ($src_codec eq $row_codec)
			{
				debug ("Found the correct line");
				for my $dst_codec_index (0..@$table_ref-2)
				{
					my $dst_codec = $table_ref->[0]->[$dst_codec_index];
					my $value = $row->[$dst_codec_index+1];

					unless ($src_codec eq $dst_codec) # adding the current source codec would be non-sense
					{
						debug ("Adding value '$value' for destination codec '$dst_codec'");
						$data->{$src_codec}->{$dst_codec} = {};
						$data->{$src_codec}->{$dst_codec}->{$testname} = $value;
					}
				}
			}
		}
	}

	# collect foreign PBX's data
	for my $PBX (@$PBXlist_ref)
	{
		my $PBX_dir = $PBX;
		$PBX =~ s/_\d\d\d\d-\d\d-\d\d_\d\d-\d\d$//;
		debug ("Adding data for PBX '$PBX':");

		my $translation_table_file = File::Spec->rel2abs ("translation-table.csv", "results/$PBX_dir");
		debug ("Opening translation table '$translation_table_file'");
		if (open (TRANSLATION_TABLE, "<$translation_table_file"))
		{
			my @foreign_table = ();
			while (my $line = <TRANSLATION_TABLE>)
			{
				debug ("Read line '$line'");
				my @splitted_line = split (/[\s;]+/, $line);
				push (@foreign_table, \@splitted_line);
			}
			close TRANSLATION_TABLE;

			for my $src_codec (@$codeclist_ref)
			{
				debug ();
				debug ("### Working at source codec '$src_codec' for PBX '$PBX':");
				for my $row_index (1..@foreign_table-1)
				{
					my $row = $foreign_table[$row_index];
					my $row_codec = $row->[0];
		
					if ($src_codec eq $row_codec)
					{
						debug ("Found the correct line");
						for my $dst_codec_index (0..@foreign_table-2)
						{
							my $dst_codec = $foreign_table[0]->[$dst_codec_index];
							my $value = $row->[$dst_codec_index+1];
		
							unless ($src_codec eq $dst_codec) # adding the current source codec would be non-sense
							{
								debug ("Adding value '$value' for destination codec '$dst_codec'");
								$data->{$src_codec}->{$dst_codec}->{$PBX} = $value;
							}
						}
					}
				}
			}
		}
		else
		{
			warn "Could not open '$translation_table_file' for reading: $!\n";
			warn "Cannot add translation timing for PBX '$PBX'\n";
		}
	}
	
	use Data::Dumper;
	debug (Data::Dumper::Dumper ($data));
	#########################################################################################
	#	NOW, we have all needed data (my own and foreign PBX's timing) collected in %data ! #
	#	hint: %$data contains ALL available timing information, even the ones that should	#
	#	not be printed!																		#
	#########################################################################################

	# prepare gnuplot file
	# find out how many rows and columns are needed
	my $graph_columns = 0;
	my $graph_rows = 0;
	($graph_columns++) while ($graph_columns * $graph_columns < @$codeclist_ref);
	($graph_columns * ($graph_columns-1) < @$codeclist_ref)	? ($graph_rows = $graph_columns)
															: ($graph_rows = $graph_columns - 1);
	my $single_width = 1/$graph_columns;
	my $single_height = 1/$graph_rows;
	debug ("Graph rows is '$graph_rows', graph columns is '$graph_columns'");

	# print general settings for output
	my $gnuplot_cmd = "# gnuplot file for printing codecs\n";
	$gnuplot_cmd .= "f(x)=50\n"; # red line for maximum milliseconds mark
	$gnuplot_cmd .= "set border 15\n";
	$gnuplot_cmd .= "set grid\n";
	$gnuplot_cmd .= "set size $single_width,$single_height\n";
	$gnuplot_cmd .= "unset mxtics\n";
	$gnuplot_cmd .= "set xtics axis\n";
	$gnuplot_cmd .= "set ytics axis\n";
	$gnuplot_cmd .= "set xtics rotate 90\n";
	$gnuplot_cmd .= "set format x ''\n";
	$gnuplot_cmd .= "set xrange [0:$#table]\n";
	$gnuplot_cmd .= "set xlabel ' ' 0,-1\n";
	$gnuplot_cmd .= "set ylabel 'conversion time in ms'\n";
	$gnuplot_cmd .= "set yrange [0:(1.5*ymax)]\n";
	$gnuplot_cmd .= "set style fill solid border -1\n";
	$gnuplot_cmd .= "set terminal png size ". ($graph_columns*640) .",". ($graph_rows*480) ."\n";
	$gnuplot_cmd .= "set output '". File::Spec->rel2abs ('codecs.png', $testpath) ."'\n";
	$gnuplot_cmd .= "set multiplot\n";

	# Plot every single graph
	my $y_max = 0;
	my @codecs_to_draw = sort {lc($a) cmp lc($b)} (@$codeclist_ref);
	debug ("Codecs to draw are '@codecs_to_draw'");
	for my $graph_row_index (0..$graph_rows-1)
	{
		for my $graph_column_index (0..$graph_columns-1)
		{
			my $src_codec_index = $graph_row_index * $graph_columns + $graph_column_index;
			my $src_codec = $codecs_to_draw[$src_codec_index];
			last unless defined $src_codec; # index to high: no data anymore
			debug ("Source codec index is '$src_codec_index'");
			debug ("Working at source codec '$src_codec'");
		
			my $src_codec_file = File::Spec->rel2abs ("$src_codec.csv", $testpath);
			open (SRC_CODEC, ">$src_codec_file") || mydie ("Could not open file '$src_codec_file' for writing: $!");

			my @PBXs;
			my $counter = 1;
			for my $dst_codec (@codecs_to_draw)
			{
				# printing the source codec is non-sense:
				unless ($src_codec eq $dst_codec)
				{
					my $values = "";
#					@PBXs = sort {lc($a) cmp lc($b)} (keys %{$data->{$src_codec}->{$dst_codec}});
					@PBXs = ($testname, @$PBXlist_ref);
					debug ("PBXs is '@PBXs'");
					
					for my $PBX_index (0..$#PBXs)
					{
						my $PBX = $PBXs[$PBX_index];
						$values .= ($counter + $PBX_index/(2+@PBXs)) ."\t";

						if (defined $data->{$src_codec}->{$dst_codec}->{$PBX})
						{
							my $new_value = $data->{$src_codec}->{$dst_codec}->{$PBX};
#							($new_value == 0)	? ($values .= "2")
#												: ($values .= $new_value);
							$values .= $new_value;
							($y_max = $new_value) if ($y_max < $new_value);
						}
						else
						{
							$values .= "0";
						}
						$values .= "\t";
					}
					debug ("Printing line '$dst_codec\t$values'");
					print SRC_CODEC "$dst_codec\t$values\n";
					$counter++;
				}
			}
			close SRC_CODEC;
			system ("chmod", "666", "$src_codec_file");
			
			$gnuplot_cmd .= "\n# Source codec '$src_codec'\n";
			$gnuplot_cmd .= "set origin ". sprintf ("%.2f,%.2f", $graph_column_index * $single_width, 1 - $single_height*(1+$graph_row_index)) ."\n";
			$gnuplot_cmd .= "set boxwidth ". (1/(2+@PBXs)) ."\n";
			$gnuplot_cmd .= "set title 'Converting from \"$src_codec\" to ...'\n";
			$gnuplot_cmd .= "plot f(x) with line lc 1 lw 3 title 'Max. translation time', ";
			my @PBX_labels = ($testname, @$PBXlist_ref);

			for ($counter=0; $counter<@PBXs; $counter++)
			{
				my $x_index = 2*$counter+2;
				my $y_index = $x_index+1;
				($gnuplot_cmd .= ", \\\n") unless ($counter == 0);
				$gnuplot_cmd .= "'$src_codec_file' using $x_index:$y_index";
				($gnuplot_cmd .= ":xticlabels(1)") if ($counter == 0);
				$gnuplot_cmd .= " with boxes lc ". ($counter+2) ." title '". $PBXs[$counter] ."'";
			}
			$gnuplot_cmd .= "\n";
		}
	}

	$gnuplot_cmd .= "unset multiplot\n";
	debug ("Y-max is '$y_max'");
	
	# Export gnuplot command in 'codecs.gplt' file and execute it
	my $filename = File::Spec->rel2abs ("codecs.gplt", $testpath);
	debug ("Filename is '$filename'");
	open (GNUPLOT, ">$filename") || mydie ("Could not open '$filename' for writing: $!");
	print GNUPLOT "ymax=$y_max\n";
	print GNUPLOT $gnuplot_cmd;
	close GNUPLOT;
	system ("$gnuplot_exe", "$filename");
	system ("chmod", "666", "$filename");

	my $graphs_picture = File::Spec->rel2abs ('codecs.png', $testpath);
	system ("chmod", "666", "$graphs_picture") if (-e $graphs_picture);
	
	debug ("### END FUNCTION ###");
	return;
}

###############################
# INTERPRET_TRANSLATION_TABLE #
###############################
sub interpret_translation_table
{
	# INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	my $table_string		= shift || mydie ("No 'Table'-argument specified in function '$this_function'!");
	(ref $table_string)		&& mydie ("'Table'-argument has to be scalar in function '$this_function'!");
	(defined $table_string)	? debug ("table_string is '$table_string'") : debug ("table_string is not defined.");

	# format passed string to a 2-dimensional array
	my @temp = split (/\n/, $table_string);
	my @table;
	for my $index (5..$#temp)
	{
		my @temp2 = split (/\s+/, $temp[$index]);
		(shift @temp2) unless ($index == 5);
		push (@table, \@temp2);
	}

	# find indizes of lines that contain no information (no milliseconds number) --> no supported codec
	my $row_index = 1;
	while ($row_index < @table)
	{
		my $row = $table[$row_index];
		my $line = join (" ", @$row);

		# Is there a valid content in this codec?
		if ($line =~ / \d+ /)
		{
			debug ("Line '$line' is a valid codec.");
			# Convert microseconds to milliseconds
			for my $element (@$row)
			{
				if ($element =~ /^\d+$/)
				{
					$element = int ($element / 1000 + 0.5);
					($element = 1) if ($element == 0);
				}
				elsif ($element eq "-")
				{
					$element = 0;
				}
			}
		}
		else
		{
			debug ("Line '$line' is not a valid codec.");
			# There is no valid content for this codec: Delete it
			splice (@table, $row_index, 1);	# delete row
			for my $temp_row (@table)		# delete column
			{
				splice (@$temp_row, $row_index, 1);
			}
			$row_index--;
		}
		$row_index++;
	}

	debug ("Translation table is now:");
	for my $line (@table)
	{
		my $debug_text = "- '";
		for my $column (@$line)
		{
			$debug_text .= "$column | ";
		}
		debug ("$debug_text'");
	}

	debug ("### END FUNCTION ###");
	return (\@table);
}

##############
# PRINT_HELP #
##############
sub print_help
{
print <<EOT;

#
#	HINT: THIS HELP IS DEPRECATED, PLEASE HAVE A LOOK AT THE DOCUMENTATION IN SVN !
#

PERF_TEST

This script executes sip-performance-tests. Developed for AskoziaPBX-solutions.

Written by Mark Stephan <mark.stephan\@askozia.com> in Summer 2010.
Bugs, reports and questions are welcome.

USAGE: 'perl PERF_TEST <options>'

OPTIONS:
	askozia-ip=<string>             The IP-Adress of the AskoziaPBX (std: "10.10.10.1")
	askozia-port=<string>           The HTTP-Port of the Askozia-PBX (std: "80")
	askozia-confpage=<string>       The Configuration-PHP-Page of the Askozia-PBX (std:
	askozia-qstatpage=<string>      The QStat-PHP-Page of the AskoziaPBX (std: "debug_qstat.php")
	askozia-realm=<string>          The Authentication-Realm of the AskoziaPBX (std: "Web Server Authentication")
	askozia-user=<string>           Name of webfrontent-root-user of the AskoziaPBX (std: "admin")
	askozia-pw=<string>             Password for root (std: "askozia")
	save-sipp-log=<string>          Shell the sipp-output be saved in a file?
	                                Set this variable to the filename for the output (std: undef = no saving)
	local-ip=<string>               The local IP of the testcomputer (std: "10.10.10.2")
	save-users                      Shell the automatic generated Users-files (Users_*.csv) NOT be deleted?
	save-debug=<string>             Shell the debugmessages be saved in a file?
	                                Set this variable to the filename for the output (std: undef = no saving)
	two-party-calls=<number>             Number of two-party-calls that shell be executed
	two-party-pause=<number>             Important option:
	                                Defined: Slow-and-steadily building-up-tests:
	                                         There are single tests for every number of two-party-calls from 1 to <two-party-calls>
	                                         Between every tests are <two-party-pause> seconds waittime for calming down the AskoziaPBX.
	                                Undefined: One test with <two-party-calls> number of parallel calls.
	conf-calls-room=<number>        Number of calls for conference-test with fixed number of rooms.
	                                (Fixed number of rooms: in every test exist the same number of rooms,
	                                e.g. 1 call:  3 conf-rooms with 1 user each
	                                     2 calls: 3 conf-rooms with 2 users each
	                                     3 calls: 3 conf-rooms with 3 users each
	conf-rooms-room=<number>        Number of conference-rooms for the conference-test with fixed number of rooms.
	conf-pause-room=<number>        The same meaning as "two-party-pause", only for conference-test with fixed number of rooms.
	conf-calls-call=<number>        Number of calls for the conference-test with fixed number of calls
	                                (Fixed number of calls: in every test exist the same number of participants in each conf-room)	
	conf-rooms-call=<number>        Maximum number of existing conference-rooms in conference-tests with fixed calls
	conf-pause-call=<number>        The same meaning as "two-party-pause", only for conference-test with fixed number of calls
	sipp-exe=<string>               Path to the sipp-executable (std: ./PERF_TEST_FILES/sipp if existing, else 'which sipp') 
	users-two-party-file=<string>        Path where the automatic generated users-injection-file for sipp for two-party-tests shell be saved
	users-conf-room-file=<string>   Path where the automatic generated users-injection-file for sipp for conf-tests with fixed rooms shell be saved
	users-conf-call-file=<string>   Path where the automatic generated users-injection-file for sipp for conf-tests with fixed calls shell be saved
	reg-scen=<string>               Path to the register-scenario for sipp (std: './PERF_TEST_FILES/Register.xml')
	dereg-scen=<string>             Path to the deregister-scenario for sipp (std: './PERF_TEST_FILES/Deregister.xml')
	inv-scen=<string>               Path to the invite-scenario...
	acc-scen=<string>               Path to the accept-scenario...
	sip-src-port=<number>           SIP-Source-Port of the testserver (std: 5061)
	sip-dst-port=<number>           SIP-Dest-Port of the testserver (for two-party-calls only, receive port of the accepting user, std: 5062)
	rtp-src-port=<number>           RTP-Source-Port of the testserver (std: 6020)
	rtp-dst-port=<number>           RTP-Dest-port of the testserver (for two-party-calls only, receive port of the accepting user, std: 6030)
	debug                           Print debugmessages (necessary for save-debug)
	help                            Print this help and exit
	testname                        Name of this test (used for making subdirectory in 'results', std: undef)
	restore=<string>                Shell the configuration of the AskoziaPBX be restored after the test?
	                                (none, factory-defaults, old-config, std: old-config)

EOT

exit (0);
}


sub find_median
{
    # INIT
	my $this_function = (caller(0))[3];
	debug();
	debug ("### FUNCTION: '$this_function' ###");

	(my $array_ref = shift) || (mydie ("No 'Array-Ref'-argument specified in function '$this_function'!"));
	(ref ($array_ref) eq "ARRAY") ||
		(mydie ("'Array-Ref'-argument has to be array-reference in function '$this_function', it is '". ref($array_ref) ."' and '$array_ref''!"));
	my @array = @$array_ref;
	debug ("Array is:");
	(debug ("- '$_'")) for (@array);

	my $return;
	(@array % 2)	? ($return = $array[$#array/2])
					: ($return = ($array[$#array/2]+$array[$#array/2+1])/2);

	debug ("Returning '$return'");
	debug ("### END FUNCTION ###");
	return $return;
}

sub mydie
{
	my $message = (shift || "");
	(print $pipe "Tests finished.") if (defined $pipe);
	debug ("FATAL: $message");
	die "FATAL: $message\n";
}
