## Import libraries
library(psych)

## Function to generate summary statistics for one single measurement value (solumn)
# May return the measurements or print the results
generateSummaryStatistics <- function(measurements, value, returnAllMeasurements = F, printResults = T){
  allMeasurements <- NULL
  for(i in 1:length(measurements)){
    m <- cbind(measurements[[i]], i)
    names(m)[ncol(m)]<-"testrunNumber"
    allMeasurements <- rbind(allMeasurements, m)
  }
  if(printResults){
    cat("Summary Statistics for ", deparse(substitute(measurements)), "$", value, ":\n", sep = "")
    desc <- describe(as.numeric(allMeasurements[[value]]), IQR = T)
    rownames(desc) <- ""
    print(desc[,c("n", "mean", "sd", "median", "min", "max", "range", "IQR")])
  }
  if(returnAllMeasurements)
    return(allMeasurements)
}

## Function to calculate all means for all measurement runs of one measurement value (column)
calculateIndividualMeans <- function(measurements, value){
  allMeans <- NULL
  for(i in 1:length(measurements)){
    allMeans[i] <- mean(as.numeric(measurements[[i]][[value]]))
  }
  cat("Summary Statistics for all individual measurement mean values in ", deparse(substitute(measurements)), "$", value, ":\n", sep = "")
  desc <- describe(allMeans, IQR = T)
  rownames(desc) <- ""
  print(desc[,c("n", "mean", "sd", "median", "min", "max", "range", "IQR")])
  return(allMeans)
}

## Function that plots the individual measurements and the per second mean for one measurement value (column)
# must be provided with a main title, label for the x and y axes for the plot.
# May be provided with a set of markers to add a rug to the top of the graph.
# If a plotFilename is provided, the plot is also exported to this file
plotAllMeasurementsAndMean <- function(measurements, value, main, xlab, ylab, markers, plotFilename = NULL){
  if(!is.null(plotFilename)){
    png(file = plotFilename, width = 2000, height = 1000)
  }
  # Get min and max values
  min <- 0
  max <- 1
  if(is.numeric(min(measurements[[1]][[value]]))){
    min <- min(as.numeric(measurements[[1]][[value]]))
  } else {
    cat("Missing value in min() in ", main)
  }
  if(is.numeric(max(measurements[[1]][[value]]))){
    max <- max(as.numeric(measurements[[1]][[value]]))
  } else {
    cat("Missing value in max() in ", main)
  }
  for(i in 2:length(measurements)){
    potentialMin <- 0
    potentialMax <- 0
    if(is.numeric(min(measurements[[i]][[value]])))
      potentialMin <- min(as.numeric(measurements[[i]][[value]]))
    if(is.numeric(max(measurements[[i]][[value]])))
      potentialMax <- max(as.numeric(measurements[[i]][[value]]))
    if((!is.na(min))&&(!is.na(potentialMin)))
      if(min > potentialMin)
        min <- potentialMin
    if((!is.na(max))&&(!is.na(potentialMax)))
      if(max < potentialMax)
        max <- potentialMax
  }
  # Plot individual measurements
  par("mar" = par("mar")+5, "cex.axis" = 2.2, "cex.main" = 2.5, "cex.lab" = 2.5)
  plot(as.numeric(measurements[[1]][[value]]), type = "S", col="dimgray", main = main, xlab = xlab, ylab = ylab, ylim = c(min, max))
  for(i in 2:length(measurements)){
    points(as.numeric(measurements[[i]][[value]]), type = "S", col="dimgray")
  }
  # add a mean line
  sumOfAllMeasurementValues <- rep(0, nrow(measurements[[1]]))
  for(i in 1:length(measurements)){
    sumOfAllMeasurementValues <- sumOfAllMeasurementValues + as.numeric(measurements[[i]][[value]])
  }
  meanOfAllMeasurementValues <- sumOfAllMeasurementValues / length(measurements)
  points(meanOfAllMeasurementValues[1:nrow(measurements[[1]])], type = "S", col="red", lwd=2)
  
  if(nrow(markers) != 0){
    # Add hlines for the markers
    firstStoptestrunTimestamp <- markers[which(markers$action == "stopTestrun"),][1,1]
    firstStarttestrunTimestamp <- markers[which(markers$action == "startTestrun"),][1,1]
    markerFirstMeasurement <- markers[which((markers$action == "startAction")&(markers$timestamp < firstStoptestrunTimestamp)),]
    markerFirstMeasurement$second <- markerFirstMeasurement$timestamp - firstStarttestrunTimestamp
    rug(markerFirstMeasurement$second, col="blue", lwd = 2, side = 3)
    #abline(v=markerFirstMeasurement$second, col = "blue", lwd = 2)
  }
  if(!is.null(plotFilename)){
    dev.off()
  }
}

##############################
## START OF ANALYSIS SCRIPT ##
##############################
# Import hardware measurements
hw_szen<-read.table("./Szenario/Hardwareauslastung.csv", header=T, quote="\"", sep=",", dec=".", stringsAsFactors = F)
names(hw_szen) <- c("timestamp", "RAM", "Swap", "HDD_read", "HDD_write", "HDD", "NW_sent", "NW_recd", "NW", "CPU")

#Import measurement log
log_szen<-read.table("./Szenario/2018-07-27_StandardUsageScenario_WeSustain_EMS.txt", header=F, sep=";", stringsAsFactors = F, fill=T)
names(log_szen)<-c("timestamp", "action", "name")

#Import baseline measurements
hw_bl<-read.table("./Baseline/Hardware_Baseline.csv", header=T, quote="\"", sep=",", dec=".", stringsAsFactors = F)
names(hw_bl) <- c("timestamp", "RAM", "Swap", "HDD_read", "HDD_write", "HDD", "NW_sent", "NW_recd", "NW", "CPU")

#Import basline log
log_bl<-read.table("./Baseline/2018_07_28_Baseline.txt", header=F, sep=";", stringsAsFactors = F, fill=T)
names(log_bl)<-c("timestamp", "action")

#Convert timestamps to POSIXct
hw_szen$timestamp<-as.POSIXct(hw_szen$timestamp, format="%m/%d/%Y %H:%M:%OS")
log_szen$timestamp<-as.POSIXct(log_szen$timestamp)
hw_bl$timestamp<-as.POSIXct(hw_bl$timestamp, format="%m/%d/%Y %H:%M:%OS")
log_bl$timestamp<-as.POSIXct(log_bl$timestamp)

#Extract the timestamps of the beginning and end of each measurement and baseline
starts_szen <- log_szen[which(log_szen$action == "startTestrun"),]
starts_bl <- log_bl[which(log_bl$action == "startTestrun"),]
ends_szen <- log_szen[which(log_szen$action == "stopTestrun"),]
ends_bl <- log_bl[which(log_bl$action == "stopTestrun"),]

#Calculate the scenario durations
duration_szen <- ends_szen$timestamp - starts_szen$timestamp
duration_bl <- ends_bl$timestamp - starts_bl$timestamp

#Convert network traffic to kilobytes and megabytes
hw_szen$NW_KB<-hw_szen$NW/1000
hw_bl$NW_KB<-hw_bl$NW/1000
hw_szen$NW_MB<-hw_szen$NW_KB/1000
hw_bl$NW_MB<-hw_bl$NW_KB/1000

#Extract all individual measurement time intervals (from start to end timestamps) and add them to a list
performanceMeasurement <- list()
for (i in 1:nrow(starts_szen)) {
  element <- length(performanceMeasurement) + 1
  performanceMeasurement[[element]] <- hw_szen[which((hw_szen$timestamp >= starts_szen$timestamp[i]) & (hw_szen$timestamp <= ends_szen$timestamp[i])),]
  performanceMeasurement[[element]]$second <- round(performanceMeasurement[[element]]$timestamp - performanceMeasurement[[element]]$timestamp[1])
  #check if the intervals overlap
  if ((i > 1) && (starts_szen$timestamp[i] <= ends_szen$timestamp[i - 1]))
    warning(
      "Warning! Start marker of measurement ",
      i,
      " lies " ,
      ends_szen$timestamp[i - 1] - starts_szen$timestamp[i],
      " seconds before the end of the previous measurement!",
      "\n",
      "Start timestamp of measurement ",
      i,
      ": ",
      starts_szen$timestamp[i],
      ", end timestamp of measurement ",
      i - 1,
      ": ",
      ends_szen$timestamp[i - 1]
    )
}

#Extract all individual baseline time intervals (from start to end timestamps) and add them to a list
performanceBaseline <- list()
for (i in 1:nrow(starts_bl)) {
  element <- length(performanceBaseline) + 1
  performanceBaseline[[element]] <- hw_bl[which((hw_bl$timestamp >= starts_bl$timestamp[i]) & (hw_bl$timestamp <= ends_bl$timestamp[i])),]
  performanceBaseline[[element]]$second <- round(performanceBaseline[[element]]$timestamp - performanceBaseline[[element]]$timestamp[1])
  #check if the intervals overlap
  if ((i > 1) && (starts_bl$timestamp[i] <= ends_bl$timestamp[i - 1]))
    warning(
      "Warning! Start marker of baseline ",
      i,
      " lies " ,
      ends_bl$timestamp[i - 1] - starts_bl$timestamp[i],
      " seconds before the end of the previous baseline!",
      "\n",
      "start timestamp of baseline ",
      i,
      ": ",
      starts_bl$timestamp[i],
      ", end timestamp of baseline ",
      i - 1,
      ": ",
      ends_bl$timestamp[i - 1]
    )
}

#generate results and plots
plotAllMeasurementsAndMean(measurements = performanceMeasurement, "NW_KB", main = "Plot of network traffic", xlab = "Time [s]", ylab="Network traffic [KB]\n", plotFilename = "network_traffic_measurements_kb.png", markers = log_szen)
plotAllMeasurementsAndMean(measurements = performanceMeasurement, "NW_MB", main = "Plot of network traffic", xlab = "Time [s]", ylab="Network traffic [MB]\n", plotFilename = "network_traffic_measurements_mb.png", markers = log_szen)

plotAllMeasurementsAndMean(measurements = performanceBaseline, "NW", main = "Plot of network traffic of baseline", xlab = "Time [s]", ylab="Network traffic [Bytes]\n", plotFilename = "network_traffic_baseline_b.png", markers = data.frame())
plotAllMeasurementsAndMean(measurements = performanceBaseline, "NW_KB", main = "Plot of network traffic of baseline", xlab = "Time [s]", ylab="Network traffic [KB]\n", plotFilename = "network_traffic_baseline_kb.png", markers = data.frame())

boxplot(network_means, main = paste("Boxplot of average network traffic of\n 30 measurements (avg. duration: 516.6 sec)"), ylab="Network traffic [kB]", ylim=c(0,25))
boxplot(network_baseline_means, main = paste("Boxplot of average network traffic of 30 baseline\n measurements (avg. duration: 600.2 sec)"), ylab="Network traffic [kB]", ylim=c(0,25))
boxplot(network_means-mean(network_baseline_means), main = paste("Boxplot of corrected average network traffic of\n 30 baseline measurements (avg. duration: 600.2 sec)"), ylab="Network traffic [kB]", ylim=c(0,25))

calculateIndividualMeans(performanceBaseline, "RAM")
calculateIndividualMeans(performanceBaseline, "HDD")
calculateIndividualMeans(performanceBaseline, "NW_KB")
calculateIndividualMeans(performanceBaseline, "CPU")

calculateIndividualMeans(performanceMeasurement, "RAM")
calculateIndividualMeans(performanceMeasurement, "HDD")
calculateIndividualMeans(performanceMeasurement, "NW_KB")
calculateIndividualMeans(performanceMeasurement, "CPU")

#calculate the duration of each measurement and baseline
duration_bl <- ends_bl$timestamp - starts_bl$timestamp
duration_szen <- ends_szen$timestamp - starts_szen$timestamp
duration_bl_sec <- duration_bl*60
duration_szen_sec <- duration_szen*60
