classdef EnvironmentBasis < rl.env.MATLABEnvironment
    %ENVIRONMENTBASIS: Template for defining custom environment in MATLAB.

    %% Properties (set properties' attributes accordingly)
    properties

        eta             =       [];
        Batt_P          =       [];
        Batt_cap        =       [];
        PV_Peak         =       [];
        PV_gen_time     =       [];
        Load_time       =       [];
        Price_time      =       [];
        Compense_time   =       [];
        Faktor_time     =       [];
    end
    properties
        % Initialize system state;

        State = [0;0;0;0;0;1]
    end

    properties(Access = protected)
        % Initialize internal flag to indicate episode termination()
        IsDone = false;
    end

    %% Necessary Methods
    methods
        % Contructor method creates an instance of the environment
        % Change class name and constructor name accordingly


        function this = EnvironmentBasis()
            %           Initialize Observation settings
            ObservationInfo = rlNumericSpec([5 1]);
            ObservationInfo.Name = 'Observation';
            ObservationInfo.Description = 'Load_obs, PV_gen_obs, batt_obs, Price_obs, Compense_obs';

            % Initialize Action settings
            ActionInfo = rlFiniteSetSpec([10 11 20 21 30 31 40 41 42 50 51 60 61 70 71 72]);
            ActionInfo.Name = 'Betriebsmodus';

            % The following line implements built-in functions of RL env
            this = this@rl.env.MATLABEnvironment(ObservationInfo,ActionInfo);
            %             updateActionInfo(this);
        end

        % Apply system dynamics and simulates the environment with the
        % given action for one step.
        function [Observation,Reward,IsDone,LoggedSignals] = step(this,Action)

            % Werte aus Zeitreihen
            Load_sys                = this.Load_time(this.State(6));         % Last in kWh
            PV_gen              = this.PV_gen_time(this.State(6));       % PV Erzeugung in kWh

            % in diesem ersten Beispiel sind der Strompreis und die EEG-Vergütung
            % Konstant
            Price    = this.Price_time;
            Compense  = this.Compense_time;

            % Es wird eine sehr gute (optimale) Prognose angenommen. Daher werden
            % an den Agent die Lasten, PV-Einspeisung, Strompreise und Vergütungen
            % für die folge Stunde (für welche die Entschiedungen getroffen werden)
            % weiter gegeben

            if this.State(6) < length(this.PV_gen_time)

                Load_obs            = this.Load_time(this.State(6)+1);
                PV_gen_obs          = this.PV_gen_time(this.State(6)+1);
            else
                Load_obs            = 0;
                PV_gen_obs          = 0;
            end
            Price_obs    = this.Price_time;
            Compense_obs  = this.Compense_time;

            % Als Observation wird die relative Beladung des Speichers übergeben.
            % Alternativ könnten die Beladung und die Kapazität als einzelne Werte
            % übergeben werden. Die multiplikation ist notwendig damit im weiteren
            % Verlauf die Beladung übergeben wird.
            Batt_stor = this.State(3)*this.Batt_cap;

            %% Algorithmen für die Betriebsmodi
            left_gen = PV_gen;
            % Die folgenden Werte sind Input für die Reward Funktion, werden aber nicht
            % in allen Betriebsmodi generiert.
            Batt_in = 0;
            Batt_out = 0;
            Grid_Load = 0;
            Grid_to_Batt = 0;
            Batt_to_Grid = 0;
            PV_to_Grid = 0;


            % Modus 1.0
            if Action ==10
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                            % Lastdeckung durch PV
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);        % Lastdeckung Batterie
                Grid_Load = Load_sys;                                                               % Lastdeckung durch Netzbezug
                [left_gen, Batt_stor, Batt_in] = function_charge_PV(this, left_gen, Batt_stor); % Beladen der Batterie mit PV-Strom
                PV_to_Grid = left_gen;                                                          % Einspeisung PV-Strom ins Netz
                % Modus 1.1
            elseif Action == 11
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                [left_gen, Batt_stor, Batt_in] = function_charge_batt(this, left_gen, Batt_stor);       % Beladen der Batterie mit PV-Strom
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Grid_to_Batt] = function_charge_Grid(this, Batt_stor, Batt_out, Batt_in);   % Beldaden der Batterie aus dem Netz
                % Modus 2.0
            elseif Action ==20
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                            % Lastdeckung durch PV
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);        % Lastdeckung durch den Speicher
                Grid_Load = Load_sys;                                                               % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                          % Einspeisung PV-Strom ins Netz
                % Modus 2.1
            elseif Action ==21
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor,Batt_out,Batt_in);   % Einspeisung der Batterie ins Netz
                % Modus 3.0
            elseif Action == 30
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                        % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                           % Lastdeckung durch Netzbezug
                [left_gen, Batt_stor, Batt_in] = function_charge_batt(this, left_gen, Batt_stor);           % Beladen der Batterie mit PV-Strom
                PV_to_Grid = left_gen;                                                                      % Einspeisung PV-Strom ins Netz
                % Modus 3.1
            elseif Action == 31
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV-Strom
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                [left_gen, Batt_stor, Batt_in] = function_charge_batt(this, left_gen, Batt_stor);       % Beladen der Batterie mit PV-Strom
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                % Modus 4.0
            elseif Action == 40
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch das Netz
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                % Modus 4.1
            elseif Action == 41
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Grid_to_Batt] = function_charge_Grid(this, Batt_stor, Batt_out, Batt_in);   % Beldaden der Batterie aus dem Netz
                Batt_to_Grid = 0;
                % Modus 4.2
            elseif Action == 42
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor,Batt_out,Batt_in);   % Einspeisung der Batterie ins Netz
                % Modus 5.0
            elseif Action == 50
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                % Modus 5.1
            elseif Action == 51
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen);                                    % Lastdeckung durch PV
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor,Batt_out,Batt_in);   % Einspeisung der Batterie ins Netz
                % Modus 6.0
            elseif Action == 60
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                % Modus 6.1
            elseif Action == 61
                [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor);                % Lastdeckung durch den Speicher
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor,Batt_out,Batt_in);   % Einspeisung der Batterie ins Netz
                % Modus 7.0
            elseif Action == 70
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                % Modus 7.1
            elseif Action == 71
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Grid_to_Batt] = function_charge_Grid(this, Batt_stor, Batt_out, Batt_in);   % Beldaden der Batterie aus dem Netz
                % Modus 7.2
            elseif Action == 72
                Grid_Load = Load_sys;                                                                       % Lastdeckung durch Netzbezug
                PV_to_Grid = left_gen;                                                                  % Einspeisung PV-Strom ins Netz
                [Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor,Batt_out,Batt_in);   % Einspeisung der Batterie ins Netz
            else
            end
            %% Reward Function
            Reward = getReward(Batt_in,Batt_out,Grid_Load,Grid_to_Batt,Batt_to_Grid,PV_to_Grid,Price,Compense);

            %% Speicherbeladung
            Batt_obs = Batt_stor;
            % Ausreichend für den Basisfall ohne mobilen Speicher. Es wird
            % vermutet das die Absolute Kapazität des Speichers nicht
            % relevant ist. Bei integration eines mobilen Speichers sollte
            % dieser Ansatz erneut geprüft werden.


            LoggedSignals = [];
            if this.State(6) < length(this.PV_gen_time)
                this.State(6) = this.State(6)+1;
            else
            end
            % Erzeuge das Observation Array

            Observation = [Load_obs;PV_gen_obs;Batt_obs;Price_obs;Compense_obs];
            % Update system states
            this.State = [Observation, this.State(6)];


            IsDone = this.State(6) == length(this.PV_gen_time);
%             this.IsDone = IsDone;
%             notifyEnvUpdated(this);
        end

        % Reset environment to initial state and output initial observation
        % 'Load_obs, PV_gen_obs, batt_obs, Price_obs,Compense_obs
        function InitialObservation = reset(this)
            Load_obs0 = this.Load_time(1);
            PV_obs0 = this.PV_gen_time(1);
            batt_obs0 = 0;
            Price_obs0 = 0;
            Compense_obs0 = 0;
            timestep0  = 1;

            InitialObservation = [Load_obs0;PV_obs0;batt_obs0;Price_obs0;Compense_obs0];
            this.State = [InitialObservation; timestep0];

            notifyEnvUpdated(this);
        end
    end
%% Optional Methods (set methods' attributes accordingly)
    methods
        % Helper methods to create the environment

        %% Lastdeckung durch PV
        function [Load_sys, left_gen] = function_load_PV(Load_sys, left_gen)
%           PV_Load = min(Load_sys,left_gen);
            Load_sys = max([Load_sys-min(Load_sys,left_gen) 0]);
            left_gen = max(left_gen-Load_sys,0);
        end
        % Lastdeckung durch Batterie
        function [Load_sys, Batt_stor, Batt_out] = function_load_batt(this, Load_sys, Batt_stor)
            Batt_out = min([this.Batt_P Batt_stor Load_sys]);
            Batt_stor = Batt_stor - Batt_out;
            Load_sys = load_PV - Batt_out;
        end
        % Beladen der Batterie mit PV-Strom
        function [left_gen, Batt_stor, Batt_in] = function_charge_PV(this, left_gen, Batt_stor)
            temp1 = Batt_stor+(left_gen*this.eta);
            temp2 = Batt_stor+this.Batt_P;
            temp3 = left_gen*this.eta;
            temp4 = this.Batt_cap-Batt_stor;
            Batt_in = min([temp3 this.Batt_P temp4]);
            Batt_stor = min([this.Batt_cap temp1 temp2]);
            left_gen = left_gen-(Batt_in/this.eta);
        end
        % Beladen der Batterie aus dem Netz
        function [Batt_stor, Grid_to_Batt] = function_charge_Grid(this, Batt_stor, Batt_out, Batt_in)
            temp1 = this.Batt_P-Batt_out-Batt_in;
            temp2 = this.Batt_cap - Batt_stor;
            Grid_to_Batt = min([temp1 temp2]);
            Batt_stor = Batt_stor+(this.eta*Grid_to_Batt);
        end
        % Endladen der Batterie ins Netz
        function[Batt_stor, Batt_to_Grid] = function_decharge_Grid(this, Batt_stor)
            temp1 = this.Batt_P-Batt_in;
            temp2 = this.Batt_cap - Batt_stor;
            Batt_to_Grid = min([temp1 temp2]);
            Batt_stor = Batt_stor+(this.eta*Batt_grid);
        end
        %% Reward function
        function Reward = getReward(Batt_in,Batt_out,Grid_Load,Grid_to_Batt,Batt_to_Grid,PV_to_Grid,Price,Compense)
            Reward = (Batt_in+Grid_Load+Grid_to_Batt)*Price + (Batt_out+Batt_to_Grid+PV_to_Grid)*Compense;
        end
    end
    methods(Access = protected)
        % (optional) update visualization everytime the environment is updated
        % (notifyEnvUpdated is called)
%         function envUpdatedCallback(this)
%         end
    end
end




