// SPDX-License-Identifier: GPL-3.0-or-later

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright (C) 2013 - 2024, nymea GmbH
* Copyright (C) 2024 - 2025, chargebyte austria GmbH
*
* This file is part of nymea-energy-plugin-nymea.
*
* nymea-energy-plugin-nymea.s 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.
*
* nymea-energy-plugin-nymea.s 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 nymea-energy-plugin-nymea. If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "simulation.h"

#include <hardware/electricity.h>
#include <servers/mocktcpserver.h>
#include <experiences/experiencemanager.h>
#include <experiences/experienceplugin.h>

using namespace nymeaserver;

#include "../../../energyplugin/smartchargingmanager.h"
#include "../../mocks/spotmarketprovider/spotmarketdataprovidermock.h"

#include <QHash>
#include <QtMath>
#include <QtGlobal>
#include <QProcess>
#include <QDateTime>
#include <QSignalSpy>
#include <QProcessEnvironment>

#include <nymeacore.h>

#include "simulationtestpoint.h"

void Simulation::run_data()
{
    // Simulation infos
    QTest::addColumn<QString>("simulationName");
    QTest::addColumn<QString>("simulationTitle");
    QTest::addColumn<QString>("databaseName");
    QTest::addColumn<QDateTime>("simulationStart");
    QTest::addColumn<ChargerPlugEvents>("plugEvents");
    QTest::addColumn<int>("simulationHours");
    QTest::addColumn<EnergyLogs::SampleRate>("sampleRate");
    QTest::addColumn<double>("productionScaling");
    QTest::addColumn<int>("detailsStepStart");
    QTest::addColumn<int>("detailsStepStop");
    QTest::addColumn<DetailsStepList>("detailsStepList");

    // Houshold info
    QTest::addColumn<int>("phasePowerLimit");
    QTest::addColumn<bool>("spotMarketEnabled");
    QTest::addColumn<QString>("spotMarketResourceData");
    QTest::addColumn<double>("acquisitionTolerance");
    QTest::addColumn<double>("batteryLevelConsideration");

    // ChargingConfiguration
    QTest::addColumn<double>("targetPercentage");
    QTest::addColumn<QDateTime>("targetDateTime");
    QTest::addColumn<QString>("chargingMode");
    QTest::addColumn<int>("carBatteryLevel");
    QTest::addColumn<int>("dailySpotMarketPercentage");

    // Car information and states
    QTest::addColumn<int>("carCapacity");
    QTest::addColumn<int>("carMinChargingCurrent");
    QTest::addColumn<int>("carPhaseCount");

    // Energy storage information and states
    QTest::addColumn<bool>("energyStorageAvailable");
    QTest::addColumn<int>("energyStorageCapacity");
    QTest::addColumn<double>("energyStorageMaxChargingPower");
    QTest::addColumn<double>("energyStorageMaxDischargingPower");
    QTest::addColumn<double>("energyStorageInitialBatteyLevel");

    // Charger initial states
    QTest::addColumn<bool>("chargerConnected");
    QTest::addColumn<bool>("chargerPower");
    QTest::addColumn<QString>("chargerPhases");
    QTest::addColumn<bool>("canSwitchPhaseCount");
    QTest::addColumn<int>("chargerMaxChargingCurrent");
    QTest::addColumn<int>("chargerMaxChargingCurrentMaxValue");

    QTest::addColumn<SimulationIterationTest>("iterationTest");

    bool runAllSimulations = true;

    bool runSpotmarketSimulation = runAllSimulations;
    bool run1PhaseSimulations = true;
    bool run2PhaseSimulations = runAllSimulations;
    bool run3PhaseSimulations = runAllSimulations;
    bool runPhaseSwitchingSimulations = runAllSimulations;

    // Simulations
    if (runSpotmarketSimulation)
        QTest::newRow("Spotmarket only")

            /* Simulation info */

            << "simulation-spotmarket-only-1-phase-16A" // simulationName
            << "Simulation (1 phase, charger 16A max, only spot market)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0,0,0)) // simulationStart (UTC)

            << ChargerPlugEvents( { ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(7,0,0)), false),
                                    ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(17,30,0)), true),
                                    ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(7,0,0)), false),
                                    ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(17,0,0)), true)
               }) // pluggedInTime (UTC)

            << 48 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 0.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << true // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEco" // chargingMode
            << 20 // carBatteryLevel
            << 20 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 20)
                       }
                   },
                   {
                       250, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                       }
                   },
                   {
                       500, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                       }
                   },
                   {
                       1440, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40) // Should have charged 20% in one day
                       }
                   },
                   {
                       2800, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                       }
                   },
                   {
                       2880, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 60) // Should have charged 20% in one day
                       }
                   }
               } );

    if (runSpotmarketSimulation)
        QTest::newRow("Spotmarket and PV")

            /* Simulation info */

            << "simulation-spotmarket-and-pv-1-phase-16A" // simulationName
            << "Simulation (1 phase, charger 16A max, spot market and PV)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents()

            << 48 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 0.35 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << true // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEco" // chargingMode
            << 20 // carBatteryLevel
            << 20 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 20)
                       }
                   },
                   {
                       250, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       500, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       580, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       750, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       780, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       810, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       2000, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 47) // Should be at least 20% more than the day before...
                       }
                   },
                   {
                       2250, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                       }
                   },
                   {
                       2750, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                       }
                   }
               } );

    if (runSpotmarketSimulation)
        QTest::newRow("Spotmarket with target time")

            /* Simulation info */

            << "simulation-spotmarket-only-with-targettime-1-phase-16A" // simulationName
            << "Simulation (1 phase, charger 16A max, only spot market with target time)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // Car plug events

            << 48 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 0.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << true // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 20 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 20)
                       }
                   },
                   {
                       700, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1200, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       1400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1550, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       1700, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       2760, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );


    if (runSpotmarketSimulation)
        QTest::newRow("Spotmarket only with target time")

            /* Simulation info */

            << "simulation-spotmarket-only-with-targettime-1-day-1-phase-16A" // simulationName
            << "Simulation (1 phase, charger 16A max, only spot market with target time single day)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0, 0, 0)) // simulationStart (UTC)
            << ChargerPlugEvents()

            << 32 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 0.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << true // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 15), QTime(07, 0, 0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 50 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 50)
                       }
                   },
                   {
                       250, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 61)
                       }
                   },
                   {
                       500, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                       }
                   },
                   {
                       1400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                       }
                   }
               });

    if (run1PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-1-phase-32A" // simulationName
            << "Simulation (1 phase, charger 32A max, target 22:00 100%" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 20.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 32 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       163, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       170, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 9),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       444, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 7) ,
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       520, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6) ,
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 80)
                       }
                   },
                   {
                       700, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6) ,
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 85)
                       }
                   },
                   {
                       900, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 31) ,
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 88)
                       }
                   },
                   {
                       960, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 31) ,
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run1PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-1-phase-16A" // simulationName
            << "Simulation (1 phase, charger 16A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(0,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents( { ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)), true) }) // pluggedInTime (UTC)
            << 24 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 20.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 50 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 1  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 50)
                       }
                   },
                   {
                       452, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 50)
                       }
                   },
                   {
                       467, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 9),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 51)
                       }
                   },
                   {
                       750, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       830, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       841, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1000, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       1300, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1440, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run2PhaseSimulations)
        QTest::newRow("Kostal")
            /* Simulation info */
            << "simulation-kostal-2-phase-16A" // simulationName
            << "Simulation (2 phase, charger 16A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents( { ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(8,0,0)), true) }) // pluggedInTime (UTC)
            << 24 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 1.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 2  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       550, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       600, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 9),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 48)
                       }
                   },
                   {
                       820, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       823, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       847, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 82)
                       }
                   },
                   {
                       1050, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run2PhaseSimulations)
        QTest::newRow("Kostal")
            /* Simulation info */
            << "simulation-kostal-2-phase-16A-away-2-hours" // simulationName
            << "Simulation (2 phase, charger 16A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-08-12-kostal-energylogs.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(0,0,0)) // simulationStart (UTC)

            << ChargerPlugEvents( {
                                    ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(12,0,0)), false),
                                    ChargerPlugEvent(EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(14,00,0)), true, 10)
               }) // pluggedInTime (UTC)
            << 24 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 1.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 8, 14), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 2  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       550, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       600, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 9),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 48)
                       }
                   },
                   {
                       1300, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       1440, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run2PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-2-phase-16A" // simulationName
            << "Simulation (2 phase, charger 16A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 20.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 2  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       850, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       900, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       960, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run2PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-2-phase-32A" // simulationName
            << "Simulation (2 phase, charger 32A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 20.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList() // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 2  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 32 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       100, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       300, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 10),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 61)
                       }
                   },
                   {
                       470, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       586, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       800, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       950, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 31),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 99)
                       }
                   },
                   {
                       960, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (run3PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-3-phase-16A" // simulationName
            << "Simulation (3 phase, charger 16A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 40.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList({200, 300, 400, 500}) // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 3  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       200, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 11),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 57)
                       }
                   },
                   {
                       300, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 13),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 82)
                       }
                   },
                   {
                       400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 11),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   },
                   {
                       700, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );


    if (run3PhaseSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-3-phase-32A" // simulationName
            << "Simulation (3 phase, charger 32A max, target 22:00 100%)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 20.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList({ 480, 500, 600, 950, 960 }) // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEcoWithTargetTime" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 3  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << false // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 32 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       100, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       200, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       300, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true)
                       }
                   },
                   {
                       480, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       500, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 83)
                       }
                   },
                   {
                       600, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false)
                       }
                   },
                   {
                       950, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 99) // Finish 10 min early
                       }
                   },
                   {
                       960, { // 22:00
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   }
               } );

    if (runPhaseSwitchingSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-phase-switching-16A" // simulationName
            << "Simulation (phase switching, charger 16A max, surplus only)" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(6,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 18 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 45.0 // productionScaling
            << 0 // detailsStepStart
            << 0 // detailsStepStop
            << DetailsStepList({80, 150, 310, 400, 470}) // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEco" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 3  // carPhaseCount

            /* Energy storage */
            << false // energyStorageAvailable
            << 0 // energyStorageCapacity
            << 0.0 // energyStorageMaxChargingPower
            << 0.0 // energyStorageMaxDischargingPower
            << 50.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << true // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       80, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   },
                   {
                       150, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 10),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 50)
                       }
                   },
                   {
                       310, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 16),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 91)
                       }
                   },
                   {
                       400, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 13),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   },
                   {
                       470, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 9),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, true),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 100)
                       }
                   },
               } );

    if (runPhaseSwitchingSimulations)
        QTest::newRow("Default")
            /* Simulation info */
            << "simulation-energy-storage-phase-switching" // simulationName
            << "Simulation energy storage, phase switching" // simulationTitle
            << ":/databases/2022-06-28-energylogs-micha.sqlite" // databaseName
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(0,0,0)) // simulationStart (UTC)
            << ChargerPlugEvents() // pluggedInTime (UTC)
            << 36 // simulationHours
            << EnergyLogs::SampleRate1Min
            << 45.0 // productionScaling
            << 0 // detailsStepStart
            << 10 // detailsStepStop
            << DetailsStepList({}) // detailsStepList

            /* Houshold info */
            << 32 // phase limit (A)
            << false // spotMarketEnabled
            << ":/resources/dataset-1.json" // spotMarketResourceData
            << 0.5 // acquisitionTolerance
            << 0.9 // batteryLevelConsideration

            /* Charging configuration */
            << 100.0 // targetPercentage
            << EnergyTestBase::utcDateTime(QDate(2022, 6, 27), QTime(22,0,0)) // targetDateTime
            << "ChargingModeEco" // chargingMode
            << 40 // carBatteryLevel
            << 0 // dailySpotMarketPercentage

            /* Car settings */
            << 50 // carCapacity
            << 6  // carMinChargingCurrent
            << 3  // carPhaseCount

            /* Energy storage */
            << true // energyStorageAvailable
            << 12 // energyStorageCapacity
            << 5000.0 // energyStorageMaxChargingPower
            << 5000.0 // energyStorageMaxDischargingPower
            << 10.0 // energyStorageInitialBatteyLevel

            << true // chargerConnected
            << false // chargerPower
            << "ABC" // chargerPhases
            << true // canSwitchPhaseCount
            << 6 // chargerMaxChargingCurrent
            << 16 //chargerMaxChargingCurrentMaxValue

            << SimulationIterationTest ( {
                   {
                       0, {
                           SimulationTestPoint(SimulationTestPoint::TestTypeMaxChargingCurrent, 6),
                           SimulationTestPoint(SimulationTestPoint::TestTypeCharging, false),
                           SimulationTestPoint(SimulationTestPoint::TestTypeStateOfCharge, 40)
                       }
                   }
               } );
}

void Simulation::run()
{
    QFETCH(QString, simulationName);
    QFETCH(QString, simulationTitle);
    QFETCH(QString, databaseName);
    QFETCH(QDateTime, simulationStart);
    QFETCH(ChargerPlugEvents, plugEvents);
    QFETCH(int, simulationHours);
    QFETCH(EnergyLogs::SampleRate, sampleRate);
    QFETCH(double, productionScaling);
    QFETCH(int, detailsStepStart);
    QFETCH(int, detailsStepStop);
    QFETCH(DetailsStepList, detailsStepList);

    QFETCH(int, phasePowerLimit);
    QFETCH(bool, spotMarketEnabled);
    QFETCH(QString, spotMarketResourceData);
    QFETCH(double, acquisitionTolerance);
    QFETCH(double, batteryLevelConsideration);

    QFETCH(double, targetPercentage);
    QFETCH(QDateTime, targetDateTime);
    QFETCH(QString, chargingMode);
    QFETCH(int, carBatteryLevel);
    QFETCH(int, dailySpotMarketPercentage);

    QFETCH(int, carCapacity);
    QFETCH(int, carMinChargingCurrent);
    QFETCH(int, carPhaseCount);

    QFETCH(bool, energyStorageAvailable);
    QFETCH(int, energyStorageCapacity);
    QFETCH(double, energyStorageMaxChargingPower);
    QFETCH(double, energyStorageMaxDischargingPower);
    QFETCH(double, energyStorageInitialBatteyLevel);

    QFETCH(bool, chargerConnected);
    QFETCH(bool, chargerPower);
    QFETCH(QString, chargerPhases);
    QFETCH(bool, canSwitchPhaseCount);
    QFETCH(int, chargerMaxChargingCurrent);
    QFETCH(int, chargerMaxChargingCurrentMaxValue);

    QFETCH(SimulationIterationTest, iterationTest);


    QStringList loggingDefaultList = {
        "*.debug=false",
        "Application.debug=true",
        "LogEngine.info=false",
        "Simulation.debug=true",
        "Experiences.debug=false",
        "NymeaEnergy.debug=false",
        "EnergyMocks.debug=false",
        "DBus.warning=false",
    };
    QString loggingRulesDefault = loggingDefaultList.join("\n");

    QStringList loggingDetailsList = {
        "*.debug=false",
        "Application.debug=true",
        "LogEngine.info=false",
        "Simulation.debug=true",
        "Experiences.debug=false",
        "NymeaEnergy.debug=true",
        "EnergyMocks.debug=false",
        "DBus.warning=false",
    };
    QString loggingRulesDetails = loggingDetailsList.join("\n");

    QStringList availableChargingModes;
    availableChargingModes << "ChargingModeNormal";
    availableChargingModes << "ChargingModeEco";
    availableChargingModes << "ChargingModeEcoWithTargetTime";
    QVERIFY2(availableChargingModes.contains(chargingMode), "Unknown charging mode passed to the simulation. Please compair the list with the ChargingMode enum.");

    if (canSwitchPhaseCount)
        QVERIFY2(chargerPhases == "ABC", "If the charger supports phase count switching all 3 phases must be connected.");

    cleanupTestCase();
    m_energyLogDbFilePath = databaseName;
    initTestCase(loggingRulesDefault);

    // Print simulation init details
    QLoggingCategory::setFilterRules(loggingRulesDefault);

    QVariant response; QVariantMap params;
    QNetworkReply *reply = nullptr;
    QSignalSpy packetSpy(m_mockTcpServer, &MockTcpServer::outgoingData);;

    Electricity::Phases chargerPhasesConverted = Electricity::convertPhasesFromString(chargerPhases);

    // Set phase power limit
    response = injectAndWait("NymeaEnergy.SetPhasePowerLimit", QVariantMap({{"phasePowerLimit", phasePowerLimit}}));
    QVERIFY(response.toMap().value("params").toMap().value("energyError").toString() == "EnergyErrorNoError");

    // Add mock spotmarket provider
    SpotMarketManager *spotMarketManager = m_experiencePlugin->spotMarketManager();
    SpotMarketDataProviderMock *mockProvider = new SpotMarketDataProviderMock(nullptr, this);
    QVERIFY(mockProvider->prepareResourceData(spotMarketResourceData, simulationStart.toUTC()));
    QVERIFY(spotMarketManager->registerProvider(mockProvider));
    QVERIFY(spotMarketManager->changeProvider(mockProvider->providerId()));

    // Enabke/disable spot market
    response = injectAndWait("NymeaEnergy.SetSpotMarketConfiguration", QVariantMap({ {"enabled", spotMarketEnabled }, {"providerId", mockProvider->providerId()} }));
    QCOMPARE(response.toMap().value("params").toMap().value("energyError").toString(), "EnergyErrorNoError");
    QCOMPARE(m_experiencePlugin->spotMarketManager()->enabled(), spotMarketEnabled);

    // Set initial acquisition tolerance
    params.clear(); response.clear();
    params.insert("acquisitionTolerance", acquisitionTolerance);
    response = injectAndWait("NymeaEnergy.SetAcquisitionTolerance", params);
    verifyEnergyError(response);

    // Set battery level consideration
    params.clear(); response.clear();
    params.insert("batteryLevelConsideration", batteryLevelConsideration);
    response = injectAndWait("NymeaEnergy.SetBatteryLevelConsideration", params);
    verifyEnergyError(response);

    // Add mock meter
    QUuid meterThingId = addMeter();
    QVERIFY2(!meterThingId.isNull(), "Did not receive valid ThingId");
    if (packetSpy.count() == 0) packetSpy.wait();
    checkNotification(packetSpy, "Integrations.ThingAdded");
    packetSpy.clear();

    // Set it as root meter
    m_experiencePlugin->energyManager()->setRootMeter(meterThingId);

    // Make sure this is our root meter now
    response = injectAndWait("Energy.GetRootMeter");
    QCOMPARE(response.toMap().value("params").toMap().value("rootMeterThingId").toUuid(), meterThingId);
    packetSpy.clear();

    // Add the charger
    QUuid evChargerId;
    if (canSwitchPhaseCount) {
        evChargerId = addChargerWithPhaseCountSwitching(chargerPhases, chargerMaxChargingCurrentMaxValue);
    } else {
        evChargerId = addCharger(chargerPhases, chargerMaxChargingCurrentMaxValue);
    }

    QVERIFY2(!evChargerId.isNull(), "Did not receive valid ThingId");
    if (packetSpy.count() == 0) packetSpy.wait();
    checkNotification(packetSpy, "Integrations.ThingAdded");

    // Add the car
    QUuid carThingId = addCar();
    QVERIFY2(!carThingId.isNull(), "Did not receive valid ThingId");
    if (packetSpy.count() == 0) packetSpy.wait();
    checkNotification(packetSpy, "Integrations.ThingAdded");

    // Add energy storage if available
    QUuid energyStorageThingId;
    if (energyStorageAvailable) {
        energyStorageThingId = addEnergyStorage(energyStorageCapacity, energyStorageMaxChargingPower, energyStorageMaxDischargingPower);
        QVERIFY2(!energyStorageThingId.isNull(), "Did not receive valid ThingId");
        if (packetSpy.count() == 0) packetSpy.wait();
        checkNotification(packetSpy, "Integrations.ThingAdded");
    }

    // ==============================================================================
    // Set states of car and charger

    Thing *carThing = NymeaCore::instance()->thingManager()->findConfiguredThing(carThingId);
    QVERIFY2(carThing != nullptr, "Failed to find car thing");
    carThing->setSettingValue(carThing->thingClass().settingsTypes().findByName("phaseCount").id(), carPhaseCount);
    carThing->setSettingValue(carThing->thingClass().settingsTypes().findByName("capacity").id(), carCapacity);
    carThing->setSettingValue(carThing->thingClass().settingsTypes().findByName("minChargingCurrent").id(), carMinChargingCurrent);
    carThing->setStateValue("batteryLevel", carBatteryLevel);

    Thing *chargerThing = NymeaCore::instance()->thingManager()->findConfiguredThing(evChargerId);
    QVERIFY2(chargerThing != nullptr, "Failed to find charger thing");
    chargerThing->setStateValue("connected", chargerConnected);
    chargerThing->setStateValue("power", chargerPower);
    chargerThing->setStateValue("maxChargingCurrent", chargerMaxChargingCurrent);
    chargerThing->setStateValue("maxChargingCurrentMaxValue", chargerMaxChargingCurrentMaxValue);
    chargerThing->setStateValue("pluggedIn", true); // Initially always plugged in, the rest can be handeld using the plug events

    // This will update all internal states not set directly
    updateChargerMeter(chargerThing);

    Thing *meterThing = NymeaCore::instance()->thingManager()->findConfiguredThing(meterThingId);
    QVERIFY2(meterThing != nullptr, "Failed to find meter thing");
    meterThing->setStateValue("connected", true);

    // Energy storage states
    Thing *energyStorageThing = nullptr;
    if (energyStorageAvailable) {
        energyStorageThing = NymeaCore::instance()->thingManager()->findConfiguredThing(energyStorageThingId);
        energyStorageThing->setStateValue("currentPower", 0);
        energyStorageThing->setStateValue("batteryLevel", energyStorageInitialBatteyLevel);
        energyStorageThing->setProperty("preciseBatteryLevel", energyStorageInitialBatteyLevel * 1.0); // For precise runtime calculations
        printStates(energyStorageThing);
    }

    //    printStates(chargerThing);
    //    printStates(carThing);
    //    printStates(meterThing);

    // Set charging configuration with our charger and car, this should trigger the evaluation
    QVariantMap chargingConfigurationMap;
    chargingConfigurationMap.insert("evChargerId", evChargerId);
    chargingConfigurationMap.insert("assignedCarId", carThingId);
    chargingConfigurationMap.insert("chargingMode", chargingMode);
    chargingConfigurationMap.insert("endDateTime", targetDateTime.toMSecsSinceEpoch() / 1000);
    chargingConfigurationMap.insert("targetPercentage", targetPercentage);
    chargingConfigurationMap.insert("dailySpotMarketPercentage", dailySpotMarketPercentage);
    response = injectAndWait("NymeaEnergy.SetChargingConfiguration", QVariantMap({{"chargingConfiguration", chargingConfigurationMap}}));
    QCOMPARE(response.toMap().value("status").toString(), QString("success"));
    QCOMPARE(response.toMap().value("params").toMap().value("energyError").toString(), QString("EnergyErrorNoError"));

    uint effectivePhaseCount;
    if (canSwitchPhaseCount) {
        effectivePhaseCount = chargerThing->stateValue("phaseCount").toUInt();
    } else {
        effectivePhaseCount = qMin((uint)carPhaseCount, Electricity::getPhaseCount(chargerPhasesConverted));
    }

    QString usedPhases;
    if (effectivePhaseCount >= 1)
        usedPhases.append("A");

    if (effectivePhaseCount >= 2)
        usedPhases.append("B");

    if (effectivePhaseCount >= 3)
        usedPhases.append("C");

    chargerThing->setStateValue("usedPhases", usedPhases);


    // Note: we want to have the limit negative, so we see better where the limit is and why the chaging stopped
    double aquisitionToleranceLimit = -(effectivePhaseCount * carMinChargingCurrent * 230.0 * acquisitionTolerance);

    // Simulation output
    QDir simulationBaseDir = QDir(QDir::currentPath() + QDir::separator() + "simulations");

    QDir workspaceDir = QDir(simulationBaseDir.absolutePath() + QDir::separator() + "workspace");
    if (!workspaceDir.exists()) {
        //QVERIFY2(outputDir.removeRecursively(), "Failed to cleanup output dir");
        QVERIFY2(workspaceDir.mkpath(workspaceDir.path()), "Failed to create results dir");
    }

    QDir outputDir = QDir(workspaceDir.absolutePath() + QDir::separator() + simulationName);
    if (!outputDir.exists()) {
        // QVERIFY2(outputDir.removeRecursively(), "Failed to cleanup output dir");
        QVERIFY2(outputDir.mkpath(outputDir.path()), "Failed to create output dir");
    }

    QDir resultsDir = QDir(simulationBaseDir.absolutePath() + QDir::separator() + "results");
    if (!resultsDir.exists()) {
        //QVERIFY2(outputDir.removeRecursively(), "Failed to cleanup output dir");
        QVERIFY2(resultsDir.mkpath(resultsDir.path()), "Failed to create results dir");
    }

    QFile gnuplotLogFile(outputDir.path() + QDir::separator() + "simulation.csv");
    QVERIFY2(gnuplotLogFile.open(QIODevice::ReadWrite | QIODevice::Truncate), QString("Failed to open logfile for gnuplot" + gnuplotLogFile.fileName() + ": " + gnuplotLogFile.errorString()).toUtf8());
    QTextStream gnuplotLogFileStream(&gnuplotLogFile);

    QFile gnuplotOriginalLogFile(outputDir.path() + QDir::separator() + "original.csv");
    QVERIFY2(gnuplotOriginalLogFile.open(QIODevice::ReadWrite | QIODevice::Truncate), "Failed to open logfile for gnuplot");
    QTextStream gnuplotOriginalLogFileStream(&gnuplotOriginalLogFile);


    // ==============================================================================
    // All set up, lets start simulating

    QDateTime simulationEnd = simulationStart.addSecs(simulationHours * 3600);

    PowerBalanceLogEntries powerBalanceLogs = m_experiencePlugin->energyManager()->logs()->powerBalanceLogs(sampleRate, simulationStart, simulationEnd);
    qCDebug(dcSimulation()) << "Simulation start" << simulationStart;
    qCDebug(dcSimulation()) << "Simulation end" << simulationEnd;
    qCDebug(dcSimulation()) << "Loaded" << powerBalanceLogs.count() << "log entries for that day";

    // Simulation helpers
    int simulationProgress = 0;

    // Disable init details
    QLoggingCategory::setFilterRules(loggingRulesDefault);

    // Run the simulation
    for (int i = 0; i < powerBalanceLogs.count(); i++) {

        bool debugEnabled = (i >= detailsStepStart && i <= detailsStepStop) || detailsStepList.contains(i);
        const PowerBalanceLogEntry entry = powerBalanceLogs.at(i);
        QDateTime currentDateTime = entry.timestamp().toUTC();

        // Calculate progress
        if (debugEnabled) {
            qCDebug(dcSimulation()) << "###############################################################################################################";
            qCDebug(dcSimulation()) << "Step" << i << ":" << currentDateTime.toUTC().toString("yyyy.MM.dd hh:mm");
        }

        effectivePhaseCount = chargerThing->stateValue("phaseCount").toUInt();
        usedPhases = chargerThing->stateValue("usedPhases").toString();

        // Update mocked spotmarket data provider
        mockProvider->setCurrentDataTime(currentDateTime.toUTC());

        // Print simulation progress
        double simulationProgressPrecise = i * 100 / powerBalanceLogs.count();
        if (simulationProgress != qRound(simulationProgressPrecise)) {
            simulationProgress = qRound(simulationProgressPrecise);
            if (simulationProgress % 10 == 0) {
                qCDebug(dcSimulation()) << simulationName << currentDateTime.toUTC().toString("hh:mm") << simulationProgress << "% (" << i << ")";
            }
        }

        // Enable logs in the interesting simulation steps
        if (debugEnabled) {
            QLoggingCategory::setFilterRules(loggingRulesDetails);
        } else {
            QLoggingCategory::setFilterRules(loggingRulesDefault);
        }

        // Get the current situation befor running the simulation step
        double totalProduction = entry.production() * productionScaling;
        double totalProductionDifference = totalProduction - entry.production();
        double scaledCurrentPower = entry.acquisition() + totalProductionDifference;
        //qCDebug(dcSimulation()) << "Scale production using" << productionScaling << entry.production() << "-->" << totalProduction << totalProductionDifference;
        //qCDebug(dcSimulation()) << "Scale aquisition using" << entry.acquisition() << "-->" << scaledCurrentPower;

        // Distribute the load before battery and charger on 3 phases
        double phaseLoad = scaledCurrentPower / 3;

        // -------------------- Charger

        // Handle plug events
        foreach(const ChargerPlugEvent &plugEvent, plugEvents) {
            if (plugEvent.dateTime.date() ==  currentDateTime.toUTC().date() &&
                plugEvent.dateTime.time().hour() ==  currentDateTime.toUTC().time().hour() &&
                plugEvent.dateTime.time().minute() ==  currentDateTime.toUTC().time().minute()) {

                // Plug event
                chargerThing->setStateValue("pluggedIn", plugEvent.pluggedIn);
                if (plugEvent.percentageUsed != 0) {
                    double batteryLevel = carThing->stateValue("batteryLevel").toDouble();
                    batteryLevel -= plugEvent.percentageUsed;
                    if (batteryLevel < 0) {
                        batteryLevel = 0;
                    }
                    qCDebug(dcSimulation()) << "-->" << currentDateTime.toUTC().toString("hh:mm") << "New car battery level" << batteryLevel << "(old:" << carThing->stateValue("batteryLevel").toDouble() << ")";
                    carThing->setStateValue("batteryLevel", batteryLevel);
                }
                qCDebug(dcSimulation()) << "-->" << currentDateTime.toUTC().toString("hh:mm") << "Car has been" << (plugEvent.pluggedIn ? "plugged in" : "unplugged");
            }
        }

        // Let the charger set all power, voltage etc....
        updateChargerMeter(chargerThing);
        double chargerCurrentPower = chargerThing->stateValue("currentPower").toDouble();

        double totalConsumption = chargerCurrentPower + entry.consumption();

        // Totals before battery
        double totalCurrentPower = totalConsumption + totalProduction;

        // All producers and all consumers have been summed up, charge / discharge the battery and create a final total balance

        // -------------------- Energy storage

        double energyStorageCurrentPower = 0;
        uint energyStorageBatteryLevel = 0;

        // All consumers should have what they get, put the rest into or from the storage within limits

        if (energyStorageAvailable) {

            // Calculate the new battery level depending on the previouse step.

            double esCurrentPower = energyStorageThing->stateValue("currentPower").toDouble();
            double esBatteryLevel = energyStorageThing->property("preciseBatteryLevel").toDouble();
            double esCapacity = energyStorageThing->stateValue("capacity").toDouble();

            // Let's caclulate the new percentage depending on the rate of the last minute...

            // We charged/discharged the last minute with energyStorageCurrentPower W
            double energyChargedDischargedkWh = esCurrentPower * 60 / 60 / 60 / 1000;
            double addedPercentage = energyChargedDischargedkWh * 100.0 / esCapacity;
            double newBatteryLevel = esBatteryLevel + addedPercentage;

            if (totalCurrentPower < 0 && newBatteryLevel < 100) {
                energyStorageCurrentPower = qMin(energyStorageMaxChargingPower, -totalCurrentPower);
                energyStorageBatteryLevel = newBatteryLevel;
                energyStorageThing->setProperty("preciseBatteryLevel", newBatteryLevel);
                setEnergyStorageStates(energyStorageBatteryLevel, energyStorageCurrentPower);
            } else if (totalCurrentPower > 0 && newBatteryLevel > 0) {
                energyStorageCurrentPower = - qMin(energyStorageMaxDischargingPower, totalCurrentPower);
                energyStorageBatteryLevel = newBatteryLevel;
                energyStorageThing->setProperty("preciseBatteryLevel", newBatteryLevel);
                setEnergyStorageStates(energyStorageBatteryLevel, energyStorageCurrentPower);
            } else {
                energyStorageBatteryLevel = newBatteryLevel;
                energyStorageThing->setProperty("preciseBatteryLevel", newBatteryLevel);
                setEnergyStorageStates(energyStorageBatteryLevel, energyStorageCurrentPower);
            }

            //qCDebug(dcSimulation()) << "Energy storage charged with" << energyStorageCurrentPower << "W" << addedPercentage << "% added to total" << energyStorageBatteryLevel << "%";
        }

        // -------------------- Meter

        totalCurrentPower += energyStorageCurrentPower;
        phaseLoad += energyStorageCurrentPower / 3;

        // Add the charger power in the aproperiate phase
        QVariantMap phases = QVariantMap({ {"A", phaseLoad + chargerThing->stateValue("currentPowerPhaseA").toDouble()},
                                          {"B", phaseLoad + chargerThing->stateValue("currentPowerPhaseB").toDouble()},
                                          {"C", phaseLoad + chargerThing->stateValue("currentPowerPhaseC").toDouble()} });

        reply = setMeterStates(phases, true);
        QSignalSpy setMeterStatesReplySpy(reply, &QNetworkReply::finished);
        if (setMeterStatesReplySpy.count() == 0) setMeterStatesReplySpy.wait();
        QCOMPARE(reply->error(), QNetworkReply::NoError);

        // -------------------- Run charging manager update

        // Set charger information and pass them to the logic
        ThingPowerLogEntry chargerPowerEntry(currentDateTime.toUTC(), chargerThing->id(), chargerThing->stateValue("currentPower").toDouble(), 0, 0);
        m_experiencePlugin->smartChargingManager()->simulationCallUpdateManualSoCsWithMeter(sampleRate, chargerPowerEntry);

        // Update smart charging manager with the current root meter and charger situation
        m_experiencePlugin->smartChargingManager()->simulationCallUpdate(currentDateTime.toUTC());

        // Fetch information after simulation iteration
        double carBatteryPercentage = carThing->stateValue("batteryLevel").toDouble();
        int maxChargingCurrent = chargerThing->stateValue("maxChargingCurrent").toInt();
        chargerPower = chargerThing->stateValue("power").toBool();
        chargerCurrentPower = chargerThing->stateValue("currentPower").toDouble();


        if (debugEnabled | iterationTest.contains(i)) {
            qCDebug(dcSimulation()) << "Step" << i;
            qCDebug(dcSimulation()) << "- Total power:" << totalCurrentPower << "Production:" << totalProduction << "Consumption:" << totalConsumption;
            if (energyStorageAvailable) {
                qCDebug(dcSimulation()) << "- Energy storage:" << energyStorageCurrentPower << energyStorageThing->property("preciseBatteryLevel").toDouble() << "%";
            }
            qCDebug(dcSimulation()) << "- Meter phases: A:" << meterThing->stateValue("currentPowerPhaseA").toDouble() << "W | B:"
                                    << meterThing->stateValue("currentPowerPhaseB").toDouble() << "W | C:" << meterThing->stateValue("currentPowerPhaseC").toDouble() << "W";
            qCDebug(dcSimulation()) << "- Charger:" << chargerCurrentPower << "[W] (" << maxChargingCurrent << "[A]" << (chargerCurrentPower ? "On)" : "Off )") << effectivePhaseCount << usedPhases;
            qCDebug(dcSimulation()) << "- Charger phases: A:" << chargerThing->stateValue("currentPowerPhaseA").toDouble() << "W | B:"
                                    << chargerThing->stateValue("currentPowerPhaseB").toDouble() << "W | C:" << chargerThing->stateValue("currentPowerPhaseC").toDouble() << "W";
            qCDebug(dcSimulation()) << "- Car battery:" << carBatteryPercentage;
            qCDebug(dcSimulation()) << "--------------------------------";

            //            printStates(meterThing);
            //            printStates(chargerThing);
            //            printStates(carThing);

        }

        // Verify test points
        foreach(const SimulationTestPoint &testPoint, iterationTest.value(i)) {
            switch(testPoint.testType()) {
            case SimulationTestPoint::TestTypeCharging:
                QVERIFY2(chargerPower == testPoint.expectedValue().toBool(),
                         qPrintable(QString("Simulation: %1 - %2 Step: %3 expected \"%4\" from the testpoint but is actually \"%5\"")
                                        .arg(simulationName)
                                        .arg(simulationTitle)
                                        .arg(i)
                                        .arg(testPoint.expectedValue().toBool() ? "true" : "false")
                                        .arg(chargerPower ? "true" : "false")));

                break;
            case SimulationTestPoint::TestTypeMaxChargingCurrent:
                QVERIFY2(maxChargingCurrent == testPoint.expectedValue().toInt(),
                         qPrintable(QString("Simulation: %1 - %2 Step: %3 expected \"%4\" from the testpoint but is actually \"%5\"")
                                        .arg(simulationName)
                                        .arg(simulationTitle)
                                        .arg(i)
                                        .arg(testPoint.expectedValue().toInt())
                                        .arg(maxChargingCurrent)));

                break;
            case SimulationTestPoint::TestTypeStateOfCharge:
                QVERIFY2(qFuzzyCompare(carBatteryPercentage, testPoint.expectedValue().toDouble()),
                         qPrintable(QString("Simulation: %1 - %2 Step: %3 expected \"%4\" from the testpoint but is actually \"%5\"")
                                        .arg(simulationName)
                                        .arg(simulationTitle)
                                        .arg(i)
                                        .arg(testPoint.expectedValue().toDouble())
                                        .arg(carBatteryPercentage)));

                break;
            }
        }

        // -------------------- Data logging

        // Log the simulation data
        gnuplotLogFileStream << currentDateTime.toMSecsSinceEpoch() / 1000 << ", " << // 1
            totalCurrentPower << ", " << // 2
            totalProduction << ", " << // 3
            totalConsumption  << ", " << // 4
            chargerCurrentPower << ", " << // 5
            maxChargingCurrent << ", " << // 6
            (chargerPower ? "1" : "0") << ", " << // 7
            chargerThing->state("maxChargingCurrent").minValue().toDouble() * 230 * effectivePhaseCount << ", " << // 8
            chargerThing->state("maxChargingCurrent").maxValue().toDouble() * 230 * effectivePhaseCount << ", " << // 9
            phasePowerLimit * 230 * effectivePhaseCount << ", " << // 10
            carBatteryPercentage << ", " << // 11
            i << ", " << // 12
            aquisitionToleranceLimit << ", " << // 13
            (chargerThing->stateValue("pluggedIn").toBool() ? 10 : 0 ) << ", "; // 14
        if (spotMarketEnabled) {
            const ScoreEntries weightedEntries =  spotMarketManager->weightedScoreEntries(currentDateTime.date());
            const ScoreEntry currentScore = weightedEntries.getScoreEntry(currentDateTime.toUTC());
            QVERIFY(!currentScore.isNull());
            gnuplotLogFileStream << currentScore.weighting() * 100 << ", "; // 15
            gnuplotLogFileStream << currentScore.value() / 10.0 << ", "; // 16 Price
        } else {
            gnuplotLogFileStream << 0 << ", "; // 15
            gnuplotLogFileStream << 0 << ", "; // 16
        }
        if (energyStorageAvailable) {
            gnuplotLogFileStream << energyStorageCurrentPower << ", "; // 17
            gnuplotLogFileStream << energyStorageBatteryLevel << ", "; // 18
        } else {
            gnuplotLogFileStream << 0 << ", "; // 17
            gnuplotLogFileStream << 0 << ", "; // 18
        }
        gnuplotLogFileStream << "\n";



        // Log Unchanged for raw data analysis
        gnuplotOriginalLogFileStream << currentDateTime.toMSecsSinceEpoch() / 1000 << ", " << // 1
            scaledCurrentPower << ", " << // 2
            totalProduction << ", " << // 3
            entry.consumption() << ", " << // 4
            phasePowerLimit * 230 * effectivePhaseCount << ", " << // 5
            i << ", " << // 6
            "\n";
    }

    gnuplotLogFile.close();
    gnuplotOriginalLogFile.close();

    // Draw original data
    QStringList scriptLines;
    QStringList plotLines;

    // Plot with: 1h = 200 px
    int height = 800;
    int width = simulationHours * 200;

    QString originalImageName =  simulationName + "-00-original.png";
    QString simulationImageName =  simulationName + "-01.png";

    scriptLines.append("set term png size " + QString::number(width) + "," + QString::number(height));
    scriptLines.append("set output '" + originalImageName + "'");
    scriptLines.append("set datafile separator ','");
    scriptLines.append(plotOriginalData(powerBalanceLogs.count()));

    if (spotMarketEnabled) {
        scriptLines.append("set term png size " + QString::number(width) + "," + QString::number(height * 2));
        scriptLines.append("set output '" + simulationImageName + "'");
        scriptLines.append("set datafile separator ','");

        scriptLines.append("set multiplot layout 2,1");

        scriptLines.append("set size 1,0.8");
        scriptLines.append("set origin 0,0.2");
        scriptLines.append(plotSimulation(simulationTitle, powerBalanceLogs.count()));

        scriptLines.append("set size 1,0.2");
        scriptLines.append("set origin 0,0");
        scriptLines.append(plotSpotMarketData(powerBalanceLogs.count()));

        scriptLines.append("unset multiplot");

    } else {
        scriptLines.append("set term png size " + QString::number(width) + "," + QString::number(height));
        scriptLines.append("set output '" + simulationImageName + "'");
        scriptLines.append("set datafile separator ','");
        scriptLines.append(plotSimulation(simulationTitle, powerBalanceLogs.count()));
    }

    // Write the gnuplot script
    QFile gnuplotScript(outputDir.path() + QDir::separator() + "script.gnuplot");
    QVERIFY2(gnuplotScript.open(QIODevice::ReadWrite | QIODevice::Truncate),
             QString("Failed to open script file for gnuplot" + gnuplotScript.fileName() + ": " + gnuplotScript.errorString()).toUtf8());
    QTextStream scriptStream(&gnuplotScript);
    foreach (const QString &line, scriptLines)
        scriptStream << line << "\n";

    gnuplotScript.close();


    // Write the executable gnuplot script

    scriptLines.clear();

    scriptLines.append("set terminal wxt 1 persist");
    scriptLines.append("set datafile separator ','");

    if (spotMarketEnabled) {

        scriptLines.append("set multiplot layout 2,1");

        scriptLines.append("set size 1,0.8");
        scriptLines.append("set origin 0,0.2");
        scriptLines.append(plotSimulation(simulationTitle, powerBalanceLogs.count()));

        scriptLines.append("set size 1,0.2");
        scriptLines.append("set origin 0,0");
        scriptLines.append(plotSpotMarketData(powerBalanceLogs.count()));

        scriptLines.append("unset multiplot");

    } else {
        scriptLines.append(plotSimulation(simulationTitle, powerBalanceLogs.count()));
    }

    QString executableScriptName =  simulationName + ".gnuplot";
    QFile executableGnuplotScript(outputDir.path() + QDir::separator() + executableScriptName);
    QVERIFY2(executableGnuplotScript.open(QIODevice::ReadWrite | QIODevice::Truncate),
             QString("Failed to open logfile for gnuplot" + executableGnuplotScript.fileName() + ": " + executableGnuplotScript.errorString()).toUtf8());
    QTextStream executableScriptStream(&executableGnuplotScript);
    foreach (const QString &line, scriptLines)
        executableScriptStream << line << "\n";

    executableGnuplotScript.close();



    QProcess gnuplotProcess;
    //gnuplotProcess.setEnvironment(QProcessEnvironment::systemEnvironment().toStringList());
    gnuplotProcess.setProcessChannelMode(QProcess::MergedChannels);
    gnuplotProcess.setWorkingDirectory(outputDir.path());
    gnuplotProcess.start("gnuplot", { "-c", "script.gnuplot"});
    gnuplotProcess.waitForFinished();
    qCDebug(dcSimulation()) << "gnuplot finished" << gnuplotProcess.arguments() << gnuplotProcess.workingDirectory() << gnuplotProcess.exitCode() << gnuplotProcess.exitStatus();
    if (gnuplotProcess.exitCode() != 0) {
        qCDebug(dcSimulation()) << "error plotting data:\n" << qUtf8Printable(gnuplotProcess.readAll());
        QVERIFY2(false, "plot process finished with error");
    }

    // Copy resulting images to the simulations
    QFile::copy(outputDir.path() + QDir::separator() + originalImageName, resultsDir.path() + QDir::separator() + originalImageName);
    QFile::copy(outputDir.path() + QDir::separator() + simulationImageName, resultsDir.path() + QDir::separator() + simulationImageName);
}

void Simulation::printStates(Thing *thing)
{
    qCDebug(dcSimulation()) << "Thing states for" << thing->name();
    foreach (const StateType &stateType, thing->thingClass().stateTypes()) {
        qCDebug(dcSimulation()) << "-->" << stateType.name() << thing->stateValue(stateType.id());
    }
}

void Simulation::updateChargerMeter(Thing *thing)
{
    Action updateChargerAction(thing->thingClass().actionTypes().findByName("update").id(), thing->id());
    NymeaCore::instance()->thingManager()->executeAction(updateChargerAction);
}

QStringList Simulation::plotOriginalData(int powerBalanceCount)
{
    QStringList scriptLines;
    scriptLines.append("set title 'Original energy data'");
    scriptLines.append("set grid");

    scriptLines.append("set timefmt '%s'");
    scriptLines.append("set xdata time");
    scriptLines.append("set xtics 3600");
    scriptLines.append("set format x '%H:%M'");

    scriptLines.append("set xlabel 'Time'");
    scriptLines.append("set ylabel '[W]'");

    scriptLines.append("set style fill transparent solid 0.3");

    scriptLines.append("set x2tics 100");
    scriptLines.append("set xtics nomirror");
    scriptLines.append("set x2label 'iterations'");
    scriptLines.append("set x2range [0:" + QString::number(powerBalanceCount) + "]");

    QStringList plotLines;
    plotLines.append("'original.csv' using 1:3 with boxes lt rgb '#7F75C23A' title 'Production'");
    plotLines.append("'original.csv' using 1:4 with boxes lt rgb '#7F3590F3' title 'Consumption'");
    plotLines.append("'original.csv' using 1:5 with line lt rgb 'orange' title 'House limit'");
    plotLines.append("'original.csv' using 1:2 with line lt rgb 'red' title 'Meter'");
    scriptLines.append("plot \\\n" + plotLines.join(", \\\n"));
    scriptLines.append("");
    return scriptLines;
}

QStringList Simulation::plotSimulation(const QString &title, int powerBalanceCount)
{
    QStringList scriptLines;
    scriptLines.append("set title '" + title + "'");
    scriptLines.append("set grid");

    scriptLines.append("set timefmt '%s'");
    scriptLines.append("set xdata time");
    scriptLines.append("set format x '%H:%M'");
    scriptLines.append("set xtics 3600");

    scriptLines.append("set xlabel 'time'");
    scriptLines.append("set ylabel '[W]'");

    scriptLines.append("set x2tics 100");
    scriptLines.append("set xtics nomirror");
    scriptLines.append("set x2label 'iterations'");
    scriptLines.append("set x2range [0:" + QString::number(powerBalanceCount) + "]");

    scriptLines.append("set style fill transparent solid 0.3");

    scriptLines.append("set y2tics 10");
    scriptLines.append("set ytics nomirror");
    scriptLines.append("set y2label '[\%]'");
    scriptLines.append("set y2range [0:100]");

    QStringList plotLines;
    plotLines.append("'simulation.csv' using 1:9 title 'Charger range' w filledcurves x1 lc rgb '#fff0f0f0'");
    plotLines.append("'simulation.csv' using 1:8 notitle w filledcurves x1 lc rgb '#ffffffff'");

    plotLines.append("'simulation.csv' using 1:3 with boxes lt rgb '#0A75C23A' title 'Production'");
    plotLines.append("'simulation.csv' using 1:4 with boxes lt rgb '#7F3590F3' title 'Consumption'");
    plotLines.append("'simulation.csv' using 1:17 with boxes lt rgb '#7FA020F0' title 'Energy storage'");
    plotLines.append("'simulation.csv' using 1:5 with boxes lt rgb '#7FF3DE8A' title 'Charger'");

    //    plotLines.append("'simulation.csv' using 1:9 with line lt rgb '#EABC01' title 'Charger max'");
    //    plotLines.append("'simulation.csv' using 1:8 with line lt rgb '#F6CAAF' title 'Charger min'");

    plotLines.append("'simulation.csv' using 1:13 with line lt rgb 'green' title 'Acquisition Limit'");
    plotLines.append("'simulation.csv' using 1:11 with line lt rgb 'black' axes x1y2 title 'Battery [\%]'");
    plotLines.append("'simulation.csv' using 1:18 with line lt rgb 'purple ' axes x1y2 title 'Energy storage [\%]'");
    plotLines.append("'simulation.csv' using 1:14 with line lt rgb 'purple' axes x1y2 title 'Car plugged in into charger'");
    plotLines.append("'simulation.csv' using 1:10 with line lt rgb 'orange' title 'House limit'");
    plotLines.append("'simulation.csv' using 1:2 with line lt rgb 'red' title 'Meter'");

    scriptLines.append("plot \\\n" + plotLines.join(", \\\n"));
    scriptLines.append("");
    return scriptLines;
}

QStringList Simulation::plotSpotMarketData(int powerBalanceCount)
{
    QStringList scriptLines;
    scriptLines.append("set title 'Spot maket data'");
    scriptLines.append("set grid");

    scriptLines.append("set timefmt '%s'");
    scriptLines.append("set xdata time");
    scriptLines.append("set format x '%H:%M'");
    scriptLines.append("set xtics 3600");

    scriptLines.append("set xlabel 'Time'");
    scriptLines.append("set ylabel 'Price [Cent/kWh]'");
    scriptLines.append("set x2tics 100");
    scriptLines.append("set xtics nomirror");
    scriptLines.append("set x2label 'iterations'");
    scriptLines.append("set x2range [0:" + QString::number(powerBalanceCount) + "]");

    scriptLines.append("set y2tics");
    scriptLines.append("set ytics nomirror");
    scriptLines.append("set y2label '[\%]'");
    scriptLines.append("set y2range [0:100]");

    QStringList plotLines;
    plotLines.append("'simulation.csv' using 1:15 with boxes fs solid lt rgb '#fff0f0f0' axes x1y2 title 'Spotmarket scoring [%]'");
    plotLines.append("'simulation.csv' using 1:16 with line lt rgb 'black' axes x1y1 title 'Price [Cent/kWh]'");
    scriptLines.append("plot \\\n" + plotLines.join(", \\\n"));
    scriptLines.append("");
    return scriptLines;
}


QTEST_MAIN(Simulation)
