CharafeddineMechalikh/PureEdgeSim

View on GitHub
PureEdgeSim/com/mechalikh/pureedgesim/simulationmanager/SimLog.java

Summary

Maintainability
F
4 days
Test Coverage
/**
 *     PureEdgeSim:  A Simulation Framework for Performance Evaluation of Cloud, Edge and Mist Computing Environments 
 *
 *     This file is part of PureEdgeSim Project.
 *
 *     PureEdgeSim is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     PureEdgeSim is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with PureEdgeSim. If not, see <http://www.gnu.org/licenses/>.
 *     
 *     @author Mechalikh
 **/
package com.mechalikh.pureedgesim.simulationmanager;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

import com.mechalikh.pureedgesim.datacentersmanager.ComputingNode;
import com.mechalikh.pureedgesim.energy.EnergyModelNetworkLink;
import com.mechalikh.pureedgesim.network.NetworkLink;
import com.mechalikh.pureedgesim.network.TransferProgress;
import com.mechalikh.pureedgesim.scenariomanager.SimulationParameters; 
import com.mechalikh.pureedgesim.taskgenerator.Task;

public class SimLog {
    public static final int NO_TIME = 0;
    public static final int SAME_LINE = 1;
    public static final int DEFAULT = 2;
    protected ArrayList<String> resultsList = new ArrayList<String>();
    protected DecimalFormat decimalFormat;
    protected List<String> log = new ArrayList<String>();
    protected String currentOrchArchitecture;
    protected String currentOrchAlgorithm;
    protected int currentEdgeDevicesCount;
    protected String simStartTime;
    protected SimulationManager simulationManager;
    protected boolean isFirstIteration;

    // Tasks execution results
    protected int generatedTasksCount = 0;
    protected int tasksSent = 0;
    protected int tasksFailed = 0;
    protected int tasksFailedLatency = 0;
    protected int tasksFailedMobility = 0;
    protected int tasksFailedRessourcesUnavailable = 0;
    protected int tasksFailedBeacauseDeviceDead = 0;
    protected int notGeneratedBecDeviceDead = 0;
    protected Double totalExecutionTime = 0.0;
    protected Double totalWaitingTime = 0.0;
    protected int executedTasksCount = 0;
    protected int tasksExecutedOnCloud = 0;
    protected int tasksExecutedOnEdge = 0;
    protected int tasksExecutedOnMist = 0;
    protected int tasksFailedCloud = 0;
    protected int tasksFailedEdge = 0;
    protected int tasksFailedMist = 0;

    // Network utilization
    protected Double totalLanUsage = 0.0;
    protected Double totalManUsage = 0.0;
    protected Double totalWanUsage = 0.0;
    protected Double totalBandwidth = 0.0;
    protected int transfersCount = 0;
    protected Double containersLanUsage = 0.0;
    protected Double containersWanUsage = 0.0;
    protected Double containersManUsage = 0.0;
    protected Double totalTraffic = 0.0;

    public SimLog(String startTime, boolean isFirstIteration) {
        this.setSimStartTime(startTime);
        this.isFirstIteration = isFirstIteration;

        // Use this format for all numbers
        DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.GERMAN);
        otherSymbols.setDecimalSeparator('.'); // use the dot "." as separation symbole, since the comma ","
                                                // is used in csv files as a separator
        decimalFormat = new DecimalFormat("######.####", otherSymbols);

        if (isFirstIteration) {
            // Add the CSV file header
            resultsList.add("Orchestration architecture,Orchestration algorithm,Edge devices count,"
                    + "Total tasks execution delay (s),Average execution delay (s),Total tasks waiting time (s),"
                    + "Average waiting time (s),Number of generated tasks,Tasks successfully executed,"
                    + "Task not executed (No resources available or long waiting time),Tasks failed (delay),Tasks failed (device dead),"
                    + "Tasks failed (mobility),Tasks not generated due to the death of devices,Total tasks executed (Cloud),"
                    + "Tasks successfully executed (Cloud),Total tasks executed (Edge),Tasks successfully executed (Edge),"
                    + "Total tasks executed (Mist),Tasks successfully executed (Mist),"
                    + "Network usage (s),Wan usage (s),Lan usage (s), Total network traffic (MBytes), Containers wan usage (s), Containers lan usage (s),Average bandwidth per task (Mbps),Average CPU usage (%),"
                    + "Average CPU usage (Cloud) (%),Average CPU usage (Edge) (%),Average CPU usage (Mist) (%),"
                    + "Energy consumption of computing nodes (Wh),Average energy consumption (Wh/Computing node),Cloud energy consumption (Wh),"
                    + "Average Cloud energy consumption (Wh/Data center),Edge energy consumption (Wh),Average Edge energy consumption (Wh/Data center),"
                    + "Mist energy consumption (Wh),Average Mist energy consumption (Wh/Device),"
                    + "WAN energy consumption (Wh), MAN energy consumption (Wh), LAN energy consumption (Wh),"
                    + "WiFi energy consumption (Wh), LTE energy consumption (Wh), Ethernet energy consumption (Wh),"
                    + "Dead devices count,Average remaining power (Wh),Average remaining power (%), First edge device death time (s),"
                    + "List of remaining power (%) (only battery powered devices / 0 = dead),List of the time when each device died (s)");
        }
    }

    public void showIterationResults(List<Task> finishedTasks) {
        printTasksRelatedResults();
        printNetworkRelatedResults();
        printCPUUtilizationResults();
        printPowerConsumptionResults(finishedTasks);
        StringBuilder s = new StringBuilder("\n");
        for (String value : log) {
            s.append(value).append("\n");
        }
        System.out.print(s);
        // update the log
        saveLog();
    }

    protected void printCPUUtilizationResults() {
        double averageCpuUtilization = 0;
        double averageCloudCpuUtilization = getCpuUtilizationForNodeType(SimulationParameters.TYPES.CLOUD);
        double averageEdgeCpuUtilization = getCpuUtilizationForNodeType(SimulationParameters.TYPES.EDGE_DATACENTER);
        double averageMistCpuUtilization = getCpuUtilizationForNodeType(SimulationParameters.TYPES.EDGE_DEVICE);

        int totalNodes = simulationManager.getDataCentersManager()
                .getComputingNodesGenerator().getMistOnlyListSensorsExcluded().size() + SimulationParameters.numberOfEdgeDataCenters
                + SimulationParameters.numberOfCloudDataCenters;
        if (totalNodes > 0) {
            averageCpuUtilization = (averageCloudCpuUtilization + averageMistCpuUtilization + averageEdgeCpuUtilization)
                    / totalNodes;
        }

        averageCloudCpuUtilization /= SimulationParameters.numberOfCloudDataCenters;
        averageEdgeCpuUtilization /= SimulationParameters.numberOfEdgeDataCenters;
        averageMistCpuUtilization /= simulationManager.getDataCentersManager().getComputingNodesGenerator().getMistOnlyListSensorsExcluded().size();

        print("Average CPU utilization                                                 :"
                + padLeftSpaces(decimalFormat.format(averageCpuUtilization), 20) + " %%");
        print("Average CPU utilization per level                                       :Cloud= "
                + padLeftSpaces(decimalFormat.format(averageCloudCpuUtilization), 13) + " %%");
        print("                                                                          Edge= "
                + padLeftSpaces(decimalFormat.format(averageEdgeCpuUtilization), 13) + " %%");
        print("                                                                          Mist= "
                + padLeftSpaces(decimalFormat.format(averageMistCpuUtilization), 13) + " %%");

        resultsList.set(resultsList.size() - 1,
                resultsList.get(resultsList.size() - 1) + decimalFormat.format(averageCpuUtilization) + ","
                        + decimalFormat.format(averageCloudCpuUtilization) + ","
                        + decimalFormat.format(averageEdgeCpuUtilization) + ","
                        + decimalFormat.format(averageMistCpuUtilization) + ",");
    }



    private double getCpuUtilizationForNodeType(SimulationParameters.TYPES nodeType) {
        List<ComputingNode> nodes = getNodesByType(nodeType);
        return getCpuUtilization(nodes);
    }

    public List<ComputingNode> getNodesByType(SimulationParameters.TYPES nodeType) {
        return simulationManager.getDataCentersManager().getComputingNodesGenerator()
                .getAllNodesList()
                .stream()
                .filter(node -> node.getType().equals(nodeType))
                .collect(Collectors.toList());
    }

    protected double getCpuUtilization(List<ComputingNode> list) {
        double averageCpuUtilization = 0;
        for (ComputingNode node : list) {
            averageCpuUtilization += node.getAvgCpuUtilization();
        }
        return averageCpuUtilization;
    }

    public void printTasksRelatedResults() {
        print(getClass().getSimpleName() + " - Printing iteration output...");
        print("------------------------------------------------------- OUTPUT -------------------------------------------------------");
        print("");
        print("Tasks not sent because device died (low energy)                         :"
                + padLeftSpaces(decimalFormat.format(notGeneratedBecDeviceDead / generatedTasksCount), 20) + " %% ("
                + notGeneratedBecDeviceDead + " tasks)");
        print("Tasks sent from edge devices                                            :"
                + padLeftSpaces("" + decimalFormat.format(((double) tasksSent * 100) / ((double) generatedTasksCount)),
                        20)
                + " %% (" + tasksSent + " among " + generatedTasksCount + " generated tasks)");

        print("-------------------------------------All values below are based on the sent tasks-------------------------------------");
        print("Total tasks execution time                                              :"
                + padLeftSpaces(decimalFormat.format(totalExecutionTime), 20) + " seconds");
        print("Average task execution time                                             :"
                + padLeftSpaces(decimalFormat.format(totalExecutionTime / executedTasksCount), 20) + " seconds");
        print("Total waiting time (from submitting the tasks to when execution started):"
                + padLeftSpaces(decimalFormat.format(totalWaitingTime), 20) + " seconds");
        print("Average task waiting time                                               :"
                + padLeftSpaces(decimalFormat.format(totalWaitingTime / executedTasksCount), 20) + " seconds");
        print("Tasks successfully executed                                             :"
                + padLeftSpaces("" + decimalFormat.format((double) (tasksSent - tasksFailed) * 100 / tasksSent), 20)
                + " %% (" + (tasksSent - tasksFailed) + " among " + tasksSent + " sent tasks)");

        print("Tasks failures");
        print("                              Not executed due to resource unavailablity:"
                + padLeftSpaces(decimalFormat.format((double) tasksFailedRessourcesUnavailable * 100 / tasksSent), 20)
                + " %% (" + tasksFailedRessourcesUnavailable + " tasks)");
        print("                                   Executed but failed due to high delay:"
                + padLeftSpaces(decimalFormat.format((double) tasksFailedLatency * 100 / tasksSent), 20) + " %% ("
                + tasksFailedLatency + " tasks from " + tasksSent + " successfully sent tasks)");
        print("               Tasks execution results not returned due to devices death:"
                + padLeftSpaces(decimalFormat.format((double) tasksFailedBeacauseDeviceDead * 100 / tasksSent), 20)
                + " %% (" + tasksFailedBeacauseDeviceDead + " tasks)");
        print("            Tasks execution results not returned due to devices mobility:"
                + padLeftSpaces(decimalFormat.format((double) tasksFailedMobility * 100 / tasksSent), 20) + " %% ("
                + tasksFailedMobility + " tasks)");

        print("Tasks executed on each level                                            :" + "Cloud= "
                + padLeftSpaces("" + tasksExecutedOnCloud, 13) + " tasks (where "
                + (tasksExecutedOnCloud - tasksFailedCloud) + " were successfully executed )");
        print("                                                                         " + " Edge="
                + padLeftSpaces("" + tasksExecutedOnEdge, 14) + " tasks (where "
                + (tasksExecutedOnEdge - tasksFailedEdge) + " were successfully executed )");
        print("                                                                         " + " Mist="
                + padLeftSpaces("" + tasksExecutedOnMist, 14) + " tasks (where "
                + (tasksExecutedOnMist - tasksFailedMist) + " were successfully executed )");

        resultsList.add(currentOrchArchitecture + "," + currentOrchAlgorithm + "," + currentEdgeDevicesCount + ","
                + decimalFormat.format(totalExecutionTime) + ","
                + decimalFormat.format(totalExecutionTime / executedTasksCount) + ","
                + decimalFormat.format(totalWaitingTime) + ","
                + decimalFormat.format(totalWaitingTime / executedTasksCount) + "," + generatedTasksCount + ","
                + (tasksSent - tasksFailed) + "," + tasksFailedRessourcesUnavailable + "," + tasksFailedLatency + ","
                + tasksFailedBeacauseDeviceDead + "," + tasksFailedMobility + "," + notGeneratedBecDeviceDead + ","
                + tasksExecutedOnCloud + "," + (tasksExecutedOnCloud - tasksFailedCloud) + "," + tasksExecutedOnEdge
                + "," + (tasksExecutedOnEdge - tasksFailedEdge) + "," + tasksExecutedOnMist + ","
                + (tasksExecutedOnMist - tasksFailedMist) + ",");
    }

    public void printNetworkRelatedResults() {
        print("Network usage                                                           :"
                + padLeftSpaces(decimalFormat.format(totalLanUsage + totalManUsage + totalWanUsage), 20)
                + " seconds (The total traffic: " + decimalFormat.format(totalTraffic) + " (MBytes) )");
        print("                                                                         " + "  Wan="
                + padLeftSpaces(decimalFormat.format(totalWanUsage), 14) + " seconds ("
                + decimalFormat.format(totalWanUsage * 100 / (totalLanUsage + totalManUsage + totalWanUsage))
                + " %% of total usage, WAN used when downloading containers="
                + decimalFormat.format(totalWanUsage == 0 ? 0 : containersWanUsage * 100 / totalWanUsage)
                + " %% of WAN usage )");
        print("                                                                         " + "  Man="
                + padLeftSpaces(decimalFormat.format(totalManUsage), 14) + " seconds ("
                + decimalFormat.format(totalManUsage * 100 / (totalLanUsage + totalManUsage + totalWanUsage))
                + " %% of total usage, MAN used when downloading containers="
                + decimalFormat.format(totalManUsage == 0 ? 0 : containersManUsage * 100 / totalManUsage)
                + " %% of MAN usage )");
        print("                                                                         " + "  Lan="
                + padLeftSpaces(decimalFormat.format(totalLanUsage), 14) + " seconds ("
                + decimalFormat.format(totalLanUsage * 100 / (totalLanUsage + totalManUsage + totalWanUsage))
                + " %% of total usage, LAN used when downloading containers="
                + decimalFormat.format(containersLanUsage * 100 / totalLanUsage) + " %% of LAN usage )");
        print("Average transfer speed                                                  :"
                + padLeftSpaces(decimalFormat.format(totalBandwidth / transfersCount), 20) + " Mbps  ");
        // Add these values to the las item of the results list
        resultsList.set(resultsList.size() - 1,
                resultsList.get(resultsList.size() - 1) + totalLanUsage + "," + totalWanUsage + "," + totalLanUsage
                        + "," + totalTraffic + "," + containersWanUsage + "," + containersLanUsage + ","
                        + (totalBandwidth / transfersCount) + ",");
    }

    public void printPowerConsumptionResults(List<Task> finishedTasks) {
        int deadEdgeDevicesCount = 0;
        double energyConsumption = 0;
        double cloudEnConsumption = 0;
        double mistEnConsumption = 0;
        double edgeEnConsumption = 0;
        double averageRemainingPowerWh = 0;
        double averageRemainingPower = 0;
        List<Double> remainingPower = new ArrayList<>();
        double firstDeviceDeathTime = -1; // -1 means invalid
        List<Double> devicesDeathTime = new ArrayList<>();
        int batteryPoweredDevicesCount = 0;
        int aliveBatteryPoweredDevicesCount = 0;

        double wan = simulationManager.getDataCentersManager().getTopology().getWanLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        double man = simulationManager.getDataCentersManager().getTopology().getManLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        double lan = simulationManager.getDataCentersManager().getTopology().getLanLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        double fourG = simulationManager.getDataCentersManager().getTopology().get4gLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        double eth = simulationManager.getDataCentersManager().getTopology().getEthernetLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        double wifi = simulationManager.getDataCentersManager().getTopology().getWifiLinks().stream()
                .map(NetworkLink::getEnergyModel)
                .collect(Collectors.summingDouble(EnergyModelNetworkLink::getTotalEnergyConsumption));

        for (ComputingNode node : simulationManager.getDataCentersManager().getComputingNodesGenerator()
                .getCloudOnlyList())
            cloudEnConsumption = node.getEnergyModel().getTotalEnergyConsumption();

        for (ComputingNode node : simulationManager.getDataCentersManager().getComputingNodesGenerator()
                .getEdgeOnlyList())
            edgeEnConsumption += node.getEnergyModel().getTotalEnergyConsumption();

        for (ComputingNode node : simulationManager.getDataCentersManager().getComputingNodesGenerator()
                .getMistOnlyList()) {
            ComputingNode edgeDevice = node;
            mistEnConsumption += edgeDevice.getEnergyModel().getTotalEnergyConsumption();

            if (edgeDevice.isDead()) {
                devicesDeathTime.add(edgeDevice.getDeathTime());
                remainingPower.add(0.0);
                deadEdgeDevicesCount++;
                if (firstDeviceDeathTime == -1)
                    firstDeviceDeathTime = edgeDevice.getDeathTime();
                else if (firstDeviceDeathTime > edgeDevice.getDeathTime())
                    firstDeviceDeathTime = edgeDevice.getDeathTime();
            } else {
                if (edgeDevice.getEnergyModel().isBatteryPowered()) {
                    averageRemainingPowerWh += edgeDevice.getEnergyModel().getBatteryLevelWattHour();
                    averageRemainingPower += edgeDevice.getEnergyModel().getBatteryLevelPercentage();
                    remainingPower.add(edgeDevice.getEnergyModel().getBatteryLevelPercentage());
                    aliveBatteryPoweredDevicesCount++;
                }
            }

        }
        batteryPoweredDevicesCount = aliveBatteryPoweredDevicesCount + deadEdgeDevicesCount;
        // escape from devision by 0
        if (aliveBatteryPoweredDevicesCount == 0)
            aliveBatteryPoweredDevicesCount = 1;
        energyConsumption = cloudEnConsumption + edgeEnConsumption + mistEnConsumption;
        averageRemainingPower = averageRemainingPower / (double) aliveBatteryPoweredDevicesCount;
        averageRemainingPowerWh = averageRemainingPowerWh / (double) aliveBatteryPoweredDevicesCount;
        double averageCloudEnConsumption = cloudEnConsumption / SimulationParameters.numberOfCloudDataCenters;
        double averageEdgeEnConsumption = edgeEnConsumption / SimulationParameters.numberOfEdgeDataCenters;
        double averageMistEnConsumption = mistEnConsumption / simulationManager.getScenario().getDevicesCount();

        print("Energy consumption                                                      :"
                + padLeftSpaces(decimalFormat.format(energyConsumption), 20) + " Wh (Average: "
                + decimalFormat.format(energyConsumption
                        / (SimulationParameters.numberOfEdgeDataCenters + SimulationParameters.numberOfCloudDataCenters
                                + simulationManager.getScenario().getDevicesCount()))
                + " Wh/data center(or device))");
        print("                                                                        :" + padLeftSpaces("", 19)
                + "     (Average: " + decimalFormat.format(energyConsumption / (double) finishedTasks.size())
                + " Wh/task)");
        print("Energy Consumption per level                                            :Cloud= "
                + padLeftSpaces(decimalFormat.format(cloudEnConsumption), 13) + " Wh (Average: "
                + decimalFormat.format(cloudEnConsumption / SimulationParameters.numberOfCloudDataCenters)
                + " Wh/data center)");
        print("                                                                          Edge="
                + padLeftSpaces(decimalFormat.format(edgeEnConsumption), 14) + " Wh (Average: "
                + decimalFormat.format(edgeEnConsumption / SimulationParameters.numberOfEdgeDataCenters)
                + " Wh/data center)");
        print("                                                                          Mist="
                + padLeftSpaces(decimalFormat.format(mistEnConsumption), 14) + " Wh (Average: "
                + decimalFormat.format(mistEnConsumption / currentEdgeDevicesCount) + " Wh/edge device)");

        print("Energy Consumption per network                                          :  WAN="
                + padLeftSpaces(decimalFormat.format(wan), 14) + " Wh");
        print("                                                                           MAN="
                + padLeftSpaces(decimalFormat.format(man), 14) + " Wh ");
        print("                                                                           LAN="
                + padLeftSpaces(decimalFormat.format(lan), 14) + " Wh ");

        print("Energy Consumption per technology                                       : WiFi="
                + padLeftSpaces(decimalFormat.format(wifi), 14) + " Wh");
        print("                                                                      Cellular="
                + padLeftSpaces(decimalFormat.format(fourG), 14) + " Wh ");
        print("                                                                      Ethernet="
                + padLeftSpaces(decimalFormat.format(eth), 14) + " Wh ");

        print("Dead edge devices due to battery drain                                  :"
                + padLeftSpaces(decimalFormat.format(deadEdgeDevicesCount), 20) + " devices (Among "
                + batteryPoweredDevicesCount + " devices with batteries ("
                + decimalFormat.format(((double) deadEdgeDevicesCount) * 100 / (double) batteryPoweredDevicesCount)
                + " %%))");
        print("Average remaining power (devices with batteries that are still alive)   :"
                + padLeftSpaces(decimalFormat.format(averageRemainingPowerWh), 20) + " Wh (Average: "
                + decimalFormat.format(averageRemainingPower) + " %%)");
        if (firstDeviceDeathTime != -1)
            print("First device died at                                                    :"
                    + padLeftSpaces("" + firstDeviceDeathTime, 20) + " seconds");

        // Add these values to the las item of the results list
        resultsList.set(resultsList.size() - 1, resultsList.get(resultsList.size() - 1)
                + decimalFormat.format(energyConsumption) + ","
                + decimalFormat.format(energyConsumption
                        / (SimulationParameters.numberOfEdgeDataCenters + SimulationParameters.numberOfCloudDataCenters
                                + simulationManager.getScenario().getDevicesCount()))
                + "," + decimalFormat.format(cloudEnConsumption) + "," + decimalFormat.format(averageCloudEnConsumption)
                + "," + decimalFormat.format(edgeEnConsumption) + "," + decimalFormat.format(averageEdgeEnConsumption)
                + "," + decimalFormat.format(mistEnConsumption) + "," + decimalFormat.format(averageMistEnConsumption)
                + "," + decimalFormat.format(wan) + "," + decimalFormat.format(man) + "," + decimalFormat.format(lan)
                + "," + decimalFormat.format(wifi) + "," + decimalFormat.format(fourG) + "," + decimalFormat.format(eth)
                + "," + decimalFormat.format(deadEdgeDevicesCount) + "," + decimalFormat.format(averageRemainingPowerWh)
                + "," + decimalFormat.format(averageRemainingPower) + "," + firstDeviceDeathTime + ","
                + remainingPower.toString().replace(",", "-") + "," + devicesDeathTime.toString().replace(",", "-"));
    }

    public String padLeftSpaces(String str, int n) {
        return String.format("%1$" + n + "s", str);
    }

    public void cleanOutputFolder() throws IOException {
        // Clean the folder where the results files will be saved
        if (isFirstIteration) {
            print(getClass().getSimpleName() + " - Cleaning the outputfolder...");
            isFirstIteration = false;
            Path dir = new File(SimulationParameters.outputFolder).toPath();
            deleteDirectory(dir);
        }
    }

    protected void deleteDirectory(Path path) throws IOException {
        if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS)) {
            try (DirectoryStream<Path> entries = Files.newDirectoryStream(path)) {
                for (Path entry : entries) {
                    deleteDirectory(entry);
                }
            }
        }
        try {
            Files.delete(path);
        } catch (Exception e) {
            print(getClass().getSimpleName() + " - Could not delete file/folder: " + path.toString());
        }
    }

    public void saveLog() {
        // writing results in csv file
        writeFile(getFileName(".csv"), getResultsList());

        if (!SimulationParameters.saveLog) {
            println("%s - No log saving", getClass().getSimpleName());
            return;
        }

        writeFile(getFileName(".txt"), log);

    }

    protected List<String> getResultsList() {
        return this.resultsList;
    }

    public void writeFile(String fileName, List<String> Lines) {
        try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(fileName, true))) {
            for (String str : Lines) {
                bufferedWriter.append(str);
                bufferedWriter.newLine();
            }
            Lines.clear();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getFileName(String extension) {
        String outputFilesName = SimulationParameters.outputFolder + "/" + simStartTime;
        new File(outputFilesName).mkdirs();
        if (SimulationParameters.parallelism_enabled)
            outputFilesName += "/Parallel_simulation_" + simulationManager.getSimulationId();
        else
            outputFilesName += "/Sequential_simulation";

        return outputFilesName + extension;
    }

    public void print(int flag, String newLine, Object... args) {
        if (args != null)
            newLine = String.format(newLine, args);

        if (simulationManager == null) {
            System.out.format("    0.0 : %s \n", newLine, args);
        } else {
            switch (flag) {
            case DEFAULT:

                newLine = padLeftSpaces(decimalFormat.format(simulationManager.getSimulation().clock()), 7) + " (s) : "
                        + newLine;
                log.add(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date()) + " - simulation time "
                        + newLine);
                break;
            case NO_TIME:
                log.add(newLine);
                break;
            case SAME_LINE:
                log.set(log.size() - 1, log.get(log.size() - 1) + String.format(newLine, args));
                break;
            default:
                break;
            }
        }
    }

    public void print(String line, Object... args) {
        print(DEFAULT, line, args);
    }

    public static void println(String line, Object... args) {
        System.out.format(line + "\n", args);
    }

    public void deepLog(String line, Object... args) {
        if (SimulationParameters.deepLoggingEnabled) {
            print(DEFAULT, line, args);
            System.out.format(line, args);
        }
    }

    public void deepLog(int flag, String line, Object... args) {
        if (SimulationParameters.deepLoggingEnabled) {
            print(flag, line, args);
        }
    }

    public void printWithoutTime(String line, Object... args) {
        print(NO_TIME, line, args);
    }

    public void printSameLine(String line, String color) {
        if ("red".equalsIgnoreCase(color))
            System.err.print(line);
        else
            System.out.print(line);
    }

    public void printSameLine(String line) {
        System.out.print(line);
    }

    public int getGeneratedTasks() {
        return generatedTasksCount;
    }

    public void setGeneratedTasks(int generatedTasks) {
        this.generatedTasksCount = generatedTasks;
    }

    public void setCurrentOrchPolicy(String currentOrchPolicy) {
        this.currentOrchArchitecture = currentOrchPolicy;
    }

    public void initialize(SimulationManager simulationManager, int dev, int alg, int arch) {
        this.currentEdgeDevicesCount = dev;
        this.currentOrchAlgorithm = SimulationParameters.orchestrationAlgorithms[alg];
        this.currentOrchArchitecture = SimulationParameters.orchestrationArchitectures[arch];
        this.simulationManager = simulationManager;
    }

    public String getSimStartTime() {
        return simStartTime;
    }

    public void setSimStartTime(String simStartTime) {
        this.simStartTime = simStartTime;
    }

    public void incrementTasksSent() {
        this.tasksSent++;
    }

    public void incrementTasksFailed(Task task) {
        this.tasksFailed++;
        if (task.getOffloadingDestination() == null)
            return;
        SimulationParameters.TYPES type = task.getOffloadingDestination().getType();

        if (type == SimulationParameters.TYPES.CLOUD) {
            this.tasksFailedCloud++;
        } else if (type == SimulationParameters.TYPES.EDGE_DATACENTER) {
            this.tasksFailedEdge++;
        } else if (type == SimulationParameters.TYPES.EDGE_DEVICE) {
            this.tasksFailedMist++;
        }
    }

    public void incrementFailedBeacauseDeviceDead(Task task) {
        this.tasksFailedBeacauseDeviceDead++;
        incrementTasksFailed(task);
    }

    public void incrementNotGeneratedBeacuseDeviceDead() {
        this.notGeneratedBecDeviceDead++;
    }

    public void incrementTasksFailedLatency(Task task) {
        this.tasksFailedLatency++;
        incrementTasksFailed(task);
    }

    public void incrementTasksFailedMobility(Task task) {
        this.tasksFailedMobility++;
        incrementTasksFailed(task);
    }

    public void incrementTasksFailedLackOfRessources(Task task) {
        this.tasksFailedRessourcesUnavailable++;
        incrementTasksFailed(task);
    }

    public void getTasksExecutionInfos(Task task) {
        this.totalExecutionTime += task.getActualCpuTime();
        this.totalWaitingTime += task.getWatingTime();
        this.executedTasksCount++;
    }

    public void taskSentFromOrchToDest(Task task) {
        SimulationParameters.TYPES type = task.getOffloadingDestination().getType();
        if (type == SimulationParameters.TYPES.CLOUD) {
            this.tasksExecutedOnCloud++;
        } else if (type == SimulationParameters.TYPES.EDGE_DATACENTER) {
            this.tasksExecutedOnEdge++;
        } else if (type == SimulationParameters.TYPES.EDGE_DEVICE) {
            this.tasksExecutedOnMist++;
        }
    }

    public void updateNetworkUsage(TransferProgress transfer) {
        this.totalLanUsage += transfer.getLanNetworkUsage();
        this.totalManUsage += transfer.getManNetworkUsage();
        this.totalWanUsage += transfer.getWanNetworkUsage();
        this.totalBandwidth += transfer.getAverageBandwidth() / 1000000; // bits/s to Mbits/s
        this.totalTraffic += transfer.getFileSize() / 8000000; // bits to Mbytes

        if (transfer.getTransferType() == TransferProgress.Type.CONTAINER) {
            this.containersLanUsage += transfer.getLanNetworkUsage();
            this.containersWanUsage += transfer.getWanNetworkUsage();
            this.containersManUsage += transfer.getManNetworkUsage();
        }
        this.transfersCount++;

    }

}