unit uArcoConSalidaProgramable;

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

interface

uses
  Classes,
  uEstados,
  SysUtils, xmatdefs, usimplex, umipsimplex, uglobs,
  uNodos,
  uArcos,
  ucosa, uCosaConNombre,
  ufechas, uconstantesSimSEE,
  uFuentesAleatorias,
  uFichasLPD;

resourcestring
  rsArcoSalidaProgramable = 'Arco con salida programable.';

type

  { TFichaArcoConSalidaProgramable }

  TFichaArcoConSalidaProgramable = class(TFichaArco)
  public
    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)

    NPasosDePreAviso: integer; // anticipación a la desconexión
    NPasosDeDesconexion: integer; // tiempo que permanece desconectado
    NPasosAntesNuevaProg: integer; // tiempo luego de volver de una desconexión
    // que debe transcurrir antes de aceptar una nueva programación.
    CostoPorDesconexion: NReal; // [USD] por mandar a desconectar.
    (**************************************************************************)

    constructor Create(capa: integer; fecha_: TFecha; periodicidad_: TPeriodicidad;
      rendimiento, peaje, PMax: TDAofNReal;
      NPasosDePreAviso_, NPasosDeDesconexion_, NPasosAntesNuevaProg_: integer;
      CostoPorDesconexion_: NREal; PagoPorDisponibilidad_USD_MWh: NReal;
      flg_usarFuentePmax:Boolean; fuentePmax: TFuenteAleatoria; bornePmax:String);

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;
    destructor destroy; override;
  end;

  { TArcoConSalidaProgramable }

  TArcoConSalidaProgramable = class(TArco)

  public
    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)

    X_Desc_Ini: integer; // valor inicial de la variable de estado.
    (**************************************************************************)

    // Variable de estado
    X_Desc, Xs_Desc: integer;
    dCF: NReal;

    constructor Create(capa: integer; nombre: string;
      lpdUnidades, lpd: TFichasLPD; Entrada, Salida: TNodo;
  X_Desc_Ini: integer; xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    procedure PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs); override;
    class function DescClase: string; override;

    procedure prepararPaso_ps; override;

    procedure opt_nvers(var ivar, ivae, ires: integer); override;
    procedure opt_cargue(s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_leerSolucion(s: TSimplex); override;


    procedure optx_nvxs(var ixr, ixd, iauxNReal, iauxInt: integer); override;
    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados); override;

    procedure PosicionarseEnEstrellita; override;
    procedure ActualizarEstadoGlobal(flg_Xs: boolean); override;
    procedure EvolucionarEstado; override;
    procedure Sim_Cronica_Inicio; override;


    function getNombreVar(ivar: integer; var nombre: string): boolean; override;

    procedure dump_Variables(var f: TextFile; charIndentacion: char); override;

    procedure PubliVars; override;
    destructor destroy; override;

    // esta función hace el TypeCast por comodidad
    function fpa: TFichaArcoConSalidaProgramable;

    {$IFDEF BOSTA}
    procedure AfterInstantiation; override;
    {$ENDIF}
  end;

procedure AlInicio;
procedure AlFinal;

implementation


//-------------------
// Métodos de TArco
//===================

constructor TArcoConSalidaProgramable.Create(capa: integer; nombre: string;
  lpdUnidades, lpd: TFichasLPD;
  Entrada, Salida: TNodo; X_Desc_Ini: integer;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );
begin
  inherited Create(capa, nombre,lpdUnidades, lpd, Entrada, Salida, nil, '', nil, 0,
    xFuenteIdxP, xBorneIdxP );
  self.X_Desc_Ini := X_Desc_Ini;
end;

function TArcoConSalidaProgramable.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('X_Desc_Ini', self.X_Desc_Ini);
end;

procedure TArcoConSalidaProgramable.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
end;

procedure TArcoConSalidaProgramable.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  lpd.Propietario := self;
end;


procedure TArcoConSalidaProgramable.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
end;

class function TArcoConSalidaProgramable.DescClase: string;
begin
  Result := rsArcoSalidaProgramable;
end;



procedure TArcoConSalidaProgramable.prepararPaso_ps;
var
  dx: integer;
begin
  if X_Desc < 0 then
    dx := 1
  else if X_Desc > 1 then
    dx := -1
  else if X_Desc = 1 then
    dx := -(fpa.NPasosDeDesconexion + fpa.NPasosAntesNuevaProg)
  else // X_Desc = 0
    dx := fpa.NPasosDePreAviso;
  dCF := globs.CF.deltaCosto_vxd_continuo(ixd, globs.kPaso_Opt + 1, dx) * globs.fActPaso;
end;


procedure TArcoConSalidaProgramable.opt_nvers(var ivar, ivae, ires: integer);
begin
  if (X_Desc >= -fpa.NPasosAntesNuevaProg) and (X_Desc <> 1) then
  begin
    Self.ivar := ivar;
    ivar := ivar + globs.NPostes;
    if X_Desc = 0 then
    begin
      Inc(ivar);
      self.ivae := ivae;
      Inc(ivae);
    end;
  end;
end;


procedure TArcoConSalidaProgramable.optx_nvxs(
  var ixr, ixd, iauxNReal, iauxInt: integer);
begin
  Self.ixd := ixd;
  ixd := ixd + 1;
end;

procedure TArcoConSalidaProgramable.optx_RegistrarVariablesDeEstado(
  adminEstados: TAdminEstados);
begin
  adminEstados.Registrar_Discreta(
    ixd,
    fpa.NPasosDeDesconexion + fpa.NPasosDePreAviso + fpa.NPasosAntesNuevaProg + 1,
    Self.Nombre + '_XDesc', 'pasos');
end;

procedure TArcoConSalidaProgramable.PosicionarseEnEstrellita;
begin
  X_Desc := globs.CF.xd[ixd] - (fpa.NPasosDeDesconexion + fpa.NPasosAntesNuevaProg);
end;

procedure TArcoConSalidaProgramable.ActualizarEstadoGlobal(flg_Xs: boolean);
begin
  globs.CF.xd[ixd] := X_Desc + fpa.NPasosDeDesconexion + fpa.NPasosAntesNuevaProg;
end;

procedure TArcoConSalidaProgramable.EvolucionarEstado;
begin
  X_Desc := Xs_Desc;
end;

procedure TArcoConSalidaProgramable.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
  X_Desc := X_Desc_Ini;
end;


procedure TArcoConSalidaProgramable.opt_cargue(s: TSimplex);
var
  ibaseResSal, ibaseResEnt: integer;
  iposte: integer;
begin
  if (X_Desc >= -fpa.NPasosAntesNuevaProg) and (X_Desc <> 1) then
  begin
    // Tenemos que aportar a las restricciones de
    // demanda de los nodos de
    ibaseResEnt := NodoA.ires;
    ibaseResSal := NodoB.ires;
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.pon_e(ibaseResEnt + iposte, ivar + iposte, -1);
      s.pon_e(ibaseResSal + iposte, ivar + iposte, pa.rendimiento[iposte]);
      // Ahora agregamos a la función de UTILIDAD (-costo)
      s.pon_e(s.nf, ivar + iposte, -pa.peaje[iposte] * globs.durpos[iposte]);
    end;

    if X_Desc = 0 then
      // estoy en condiciones de dar el pre-aviso.
      s.pon_e(s.nf, ivar + globs.NPostes, -(dCF + fpa.CostoPorDesconexion))
    else
      s.acum_e(s.nf, s.nc, -dCF);
  end;
end;

procedure TArcoConSalidaProgramable.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if (X_Desc >= -fpa.NPasosAntesNuevaProg) and (X_Desc <> 1) then
  begin
    // Le fijamos como cota máxima PMax a la potencia en todos los postes
    for iposte := 0 to globs.NPostes - 1 do
      s.cota_sup_set(ivar + iposte, pa.PMax[iposte]);

    if X_Desc = 0 then
      TMIPSimplex(s).set_Entera(ivae, ivar + globs.NPostes, 1);
  end;
end;

procedure TArcoConSalidaProgramable.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m: NReal;
  P: NReal;

begin
  CostoDirectoDelPaso := 0;

  vclear(P_Entrante);
  vclear(P_NodoA);
  vclear(P_NodoB);
  vclear(costo_congestion);

  if (X_Desc >= -fpa.NPasosAntesNuevaProg) and (X_Desc <> 1) then
  begin

    // recuperamos los valores de Potencia despachada
    for iposte := 0 to globs.NPostes - 1 do
    begin
      P := s.xval(ivar + iposte);
      P_Entrante[iposte] := P;
      if pa.flg_SumarPeajeAlCDP then
      begin
        m := P * pa.peaje[iposte] * globs.DurPos[iposte];
        costo[iposte] := m;
        CostoDirectoDelPaso := CostoDirectoDelPaso + m;
      end;
      costo_congestion[iposte] := -s.xmult(ivar + iposte);
      P_NodoA[iposte] := -P;
      P_NodoB[iposte] := P * pa.rendimiento[iposte];
    end;

    if X_Desc = 0 then
    begin
      if trunc(s.xval(ivar + globs.NPostes) + 0.2) = 1 then
      begin
        Xs_Desc := fpa.NPasosDePreAviso;
        CostoDirectoDelPaso := CostoDirectoDelPaso + fpa.CostoPorDesconexion;
      end
      else
        Xs_Desc := 0; // continuo sin tomar desición
    end
    else
    begin
      if X_Desc < 0 then
        Xs_Desc := X_Desc + 1
      else
        Xs_Desc := X_Desc - 1;
    end;
  end
  else
  begin
    // estoy en la zona de desconexión.
    if X_Desc < 0 then
      Xs_Desc := X_Desc + 1
    else // XDesc = 1
      Xs_Desc := -(fpa.NPasosDeDesconexion + fpa.NPasosAntesNuevaProg);
  end;

end;

function TArcoConSalidaProgramable.getNombreVar(ivar: integer;
  var nombre: string): boolean;
begin
  if (ivar >= self.ivar) and (ivar < self.ivar + globs.NPostes) then
  begin
    nombre := self.Nombre + '_P[MW]' + IntToStr(ivar - self.ivar + 1);
    Result := True;
  end
  else
    Result := False;
end;


procedure TArcoConSalidaProgramable.dump_Variables(var f: TextFile;
  charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f);
end;

procedure TArcoConSalidaProgramable.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('P', '[MW]', 6, 1, P_Entrante, True, True);
  PublicarVariableNI('X_Desc', 'pasos', X_Desc, True);
  PublicarVariableNR('dCF', 'USD', 12, 2, dCF, True);
end;

destructor TArcoConSalidaProgramable.destroy;
begin
  inherited destroy;
end;

function TArcoConSalidaProgramable.fpa: TFichaArcoConSalidaProgramable;
begin
  Result := pa as TFichaArcoConSalidaProgramable;
end;


{$IFDEF BOSTA}
procedure TArcoConSalidaProgramable.AfterInstantiation;
begin
  inherited AfterInstantiation;
  lpd.Propietario := self;
end;

{$ENDIF}

//---------------------
//Metodos de TFichaArco
//=====================

constructor TFichaArcoConSalidaProgramable.Create(capa: integer;
  fecha_: TFecha; periodicidad_: TPeriodicidad; rendimiento, peaje,
  PMax: TDAofNReal; NPasosDePreAviso_, NPasosDeDesconexion_,
  NPasosAntesNuevaProg_: integer; CostoPorDesconexion_: NREal;
  PagoPorDisponibilidad_USD_MWh: NReal; flg_usarFuentePmax: Boolean;
  fuentePmax: TFuenteAleatoria; bornePmax: String);
begin
  inherited Create(capa, fecha_, periodicidad_,
    Rendimiento,
    peaje,
    PMax,
    1, // fd
    0, // tRepHoras
    True, // ConsidearrPeajeEnElDespacho
    True, // SumarPeajeAlCDP: boolean;
    1.0, //FactorPeajeCDP: NReal);
    PagoPorDisponibilidad_USD_MWh,// pago por MW-h puesto a disposición
    nil, False, 0,flg_usarFuentePmax,
    fuentePmax,bornePmax);
  NPasosDePreAviso := NPasosDePreAviso_;
  NPasosDeDesconexion := NPasosDeDesconexion_;
  NPasosAntesNuevaProg := NPasosAntesNuevaProg_;
  CostoPorDesconexion := CostoPorDesconexion_;
end;

function TFichaArcoConSalidaProgramable.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('NPasosDePreAviso', NPasosDePreAviso);
  Result.addCampoDef('NPasosDeDesconexion', NPasosDeDesconexion);
  Result.addCampoDef('NPasosAntesNuevaProg', NPasosAntesNuevaProg);
  Result.addCampoDef('CostoPorDesconexion', CostoPorDesconexion);
end;

procedure TFichaArcoConSalidaProgramable.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
end;

procedure TFichaArcoConSalidaProgramable.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
end;



destructor TFichaArcoConSalidaProgramable.destroy;
begin
  inherited destroy;
end;



procedure AlInicio;
begin
  registrarClaseDeCosa(TArcoConSalidaProgramable.ClassName, TArcoConSalidaProgramable);
  registrarClaseDeCosa(TFichaArcoConSalidaProgramable.ClassName,
    TFichaArcoConSalidaProgramable);
end;

procedure AlFinal;
begin
end;

end.
