unit uAgendaGNL;

{ uAgendaGNL
Define la clases TCargoGNL y TAgendaCargosGNL.

En principio está pensada para la corrida en una sala semanal y se
supone que hay lugar para un Cargo por semana.

La Agenda contiene un array de booleanas con un casillero para cada paso
de tiempo.
Como La Agenda puede haber sido construida para otra sala que sobra la
que se está ejecutando, se debe especificar la fecha del primer casillero
de la Agenda para poder así determinar el sincronismo con la Sala.



Los Cargos pueden ser "De Agenda" (TipoCargo = 1)  o "Spot" (TipoCargo = 2)
El TipoCago = 0 indica que esa semana está libre y que se podría decidir
un cargo Spot.

.............................................
rch@junio_2014
proyecto: ANII-FSE_1_2013_1_10957_OptimizacionAgendas
===========================================

}
interface


uses
  Classes, SysUtils, ucosa, ucosaconnombre, xmatdefs, ufechas, ucombustible;

(* Supondremos que las ventanas de descarga son SEMANALES *)

type

  { TAgendaCargosGNL }

  TAgendaCargosGNL = class(TCosa)

  public

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

    FechaInicioAgenda: TFecha; // Fecha del primer casillero de CargoAgendado
    MesCierre, DiaCierre: integer; // Todos los años se decide ese día
    MesInicio, DiaInicio: integer; // y vale a partir de este dia.
    VGNL_m3: NReal; // volumen del cargo
    precioAgenda: NReal; // Precio de los cargos de Agenda USD/MMBTU @pcs
    precioSpot: NReal; // Precio de los cargos Spot USD/MMBTU @pcs
    precioDesvio: NREal; // Precio recibido por los cargos Desviados USD/MMBTU @pcs
    nPasosPreAviso_CompraSpot: integer; // cantidad de pasos (semanas) de pre-aviso para
    // para requerir una compra Spot.
    nPasosPreAviso_DesvioCargo: integer;
    // cantidad de pasos (semanas) de pre-aviso para
    // para requerir el desvío de un cargo
    CargoAgendado: TDAOfBoolean;  // Vector con los cargos agendados
    (**************************************************************************)

    // variables auxiliares
    rOffset_InicioHorizonteSala: NReal;
    // Tiene el offset en semanas entre desde el inicio
    // del primer casillero de la agenda al primer paso del horizonte
    // de la sala (se calcula en PrepararHorizonte
    kOffset_InicioHorizonteSala: integer;
    // trunc( rOffset_InicioHorizonteSala + 0.5 )-1;


    CostoDesvio_USD, CostoSpot_USD, CostoAgenda_USD: NReal;
    // USD a pagar = Volumen * Precio (en Desvio es PrecioAgenda-PrecioDesvio)

    constructor Create(capa: integer; FechaInicioDeLaAgenda: TFecha;
      NCasillerosDeLaAgenda: integer; MesCierre: integer; DiaCierre: integer;
      MesInicio: integer; DiaInicio: integer; VGNL_m3: NReal;
      PrecioAgenda: NReal; PrecioSpot: NReal; PrecioDesvio: NReal;
      nPasosPreAviso_CompraSpot: integer; nPasosPreAviso_DesvioCargo: integer);


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

    // Se prepara para mantener sincronismo con el horizonte de Optimización o de Simulación
    // La Agenda está pensada para trabajar en salas de paso SEMANAL si se
    // utiliza en salas de paso de tiempo difernete lanza una Excepción.
    procedure PrepararHorizonte_(FechaInicioHorizonte: TFecha; horasDelPaso: NReal);


    // en fución de los precios calcula las variables auxiliares
    // con los costos de los cargos
    procedure CalcularCostos_(ro_kg_por_m3, PCS_J_por_kg: NReal);


    // retorna el ordinal del casillero al que corresponde el paso
    // de la sala teniendo en cuenta la fecha de inicio de los casilleros de la
    // Agenda y la Fecha de inicio del HOrizonte especificado al llamar a Prepararse
    // El resultado es negativo si el valor es anterior a la agenda especificada
    // y será superior a high( CargoAgendado ) si es posterior.
    function kCasilleroAgenda(kPasoSala: integer): integer;

    // retonra TRUE si hay un cargo agendado en el casillero y FALSE en caso
    // contraio. Si el casillero está fuera de la Agenda retonra FALSE
    function HayCargoAgendado(kCasillero: integer): boolean;

    // Retorna un string del tipo 000111101010101
    // con los cargosen líneas de nbinsPorLinea máximo
    function Cargos_AsBoolStr(nbinsPorLinea: integer): string;

    // Fija el array de cargos en base a un string de 0s y 1s
    // ignorando retornos de carro y espacios.
    procedure SetCargos_FromBoolStr(cargosBoolStr: string);

    destructor destroy; override;

  end;


procedure AlInicio;
procedure AlFinal;

implementation

constructor TAgendaCargosGNL.Create(capa: integer; FechaInicioDeLaAgenda: TFecha;
  NCasillerosDeLaAgenda: integer; MesCierre: integer; DiaCierre: integer;
  MesInicio: integer; DiaInicio: integer; VGNL_m3: NReal; PrecioAgenda: NReal;
  PrecioSpot: NReal; PrecioDesvio: NReal; nPasosPreAviso_CompraSpot: integer;
  nPasosPreAviso_DesvioCargo: integer);
begin
  inherited Create(capa);
  Self.FechaInicioAgenda := FechaInicioDeLaAgenda;
  setlength(CargoAgendado, NCasillerosDeLaAgenda);

  self.MesCierre := MesCierre;
  self.DiaCierre := DiaCierre;
  self.MesInicio := MesInicio;
  self.DiaInicio := DiaInicio;

  self.VGNL_m3 := VGNL_m3;
  self.PrecioAgenda := PrecioAgenda;
  self.PrecioSpot := PrecioSpot;
  self.PrecioDesvio := PrecioDesvio;
  self.nPasosPreAviso_CompraSpot := nPasosPreAviso_CompraSpot;
  self.nPasosPreAviso_DesvioCargo := nPasosPreAviso_DesvioCargo;
end;

function TAgendaCargosGNL.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('FechaInicioAgenda', FechaInicioAgenda);
  Result.addCampoDef('MesCierre', MesCierre);
  Result.addCampoDef('DiaCierre', DiaCierre);
  Result.addCampoDef('MesInicio', MesInicio);
  Result.addCampoDef('DiaInicio', DiaInicio);
  Result.addCampoDef('VGNL_m3', VGNL_m3);
  Result.addCampoDef('precioAgenda', precioAgenda);
  Result.addCampoDef('precioSpot', precioSpot);
  Result.addCampoDef('precioDesvio', precioDesvio);
  Result.addCampoDef('nPasosPreAviso_CompraSpot', nPasosPreAviso_CompraSpot);
  Result.addCampoDef('nPasosPreAviso_DesvioCargo', nPasosPreAviso_DesvioCargo);
  Result.addCampoDef('CargoAgendado', CargoAgendado);
end;

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

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



// Se prepara para mantener sincronismo con el horizonte de Optimización o de Simulación
// La Agenda está pensada para trabajar en salas de paso SEMANAL si se
// utiliza en salas de paso de tiempo difernete lanza una Excepción.
procedure TAgendaCargosGNL.PrepararHorizonte_(FechaInicioHorizonte: TFecha;
  horasDelPaso: NReal);

begin
  rOffset_InicioHorizonteSala :=
    (FechaInicioHorizonte.dt - FechaInicioAgenda.dt) * 24.0 / horasDelPaso;
  kOffset_InicioHorizonteSala := round(rOffset_InicioHorizonteSala + 0.5) - 1;
  // El -1 es para tener en cuenta que los pasos de la sala son en base 1 (uno) y que los casilleros
  // de la Agenda son en base 0 (cero)

end;



procedure TAgendaCargosGNL.CalcularCostos_(ro_kg_por_m3, PCS_J_por_kg: NReal);
var
  fconv: NReal;
begin
  fconv := ro_kg_por_m3 * PCS_J_por_kg * MBTU_por_J;
  CostoAgenda_USD := VGNL_m3 * fconv * precioAgenda;
  CostoDesvio_USD := VGNL_m3 * fconv * (precioAgenda - precioDesvio);
  CostoSpot_USD := VGNL_m3 * fconv * PrecioSpot;
end;

// retorna el ordinal del casillero al que corresponde el paso
// de la sala teniendo en cuenta la fecha de inicio de los casilleros de la
// Agenda y la Fecha de inicio del HOrizonte especificado al llamar a Prepararse
// El resultado es negativo si el valor es anterior a la agenda especificada
// y será superior a high( CargoAgendado ) si es posterior.
function TAgendaCargosGNL.kCasilleroAgenda(kPasoSala: integer): integer;
begin
  Result := kPasoSala + kOffset_InicioHorizonteSala;
end;

// retonra TRUE si hay un cargo agendado en el casillero y FALSE en caso
// contraio. Si el casillero está fuera de la Agenda retonra FALSE
function TAgendaCargosGNL.HayCargoAgendado(kCasillero: integer): boolean;
begin
  if (kCasillero < 0) or (kCasillero > high(CargoAgendado)) then
    Result := False
  else
    Result := CargoAgendado[kCasillero];
end;



function TAgendaCargosGNL.Cargos_AsBoolStr(nbinsPorLinea: integer): string;
var
  col, k: integer;
  res: string;
begin
  res := '';
  col := 0;
  for k := 0 to high(CargoAgendado) do
  begin
    if CargoAgendado[k] then
      res := res + '1'
    else
      res := res + '0';
    Inc(col);
    if col = nbinsPorLinea then
    begin
      res := res + #13#10;
      col := 0;
    end;
  end;
  Result := res;
end;

// Fija el array de cargos en base a un string de 0s y 1s
// ignorando retornos de carro y espacios.
procedure TAgendaCargosGNL.SetCargos_FromBoolStr(cargosBoolStr: string);
var
  k: integer;
  cnt: integer;
begin
  setlength(CargoAgendado, length(cargosBoolStr));
  cnt := 0;
  for k := 1 to length(cargosBoolStr) do
  begin
    if (cargosBoolStr[k] = '0') then
    begin
      cargoAgendado[cnt] := False;
      Inc(cnt);
    end
    else if (cargosBoolStr[k] = '1') then
    begin
      cargoAgendado[cnt] := True;
      Inc(cnt);
    end;
  end;
  setlength(CargoAgendado, cnt);
end;

destructor TAgendaCargosGNL.destroy;
begin
  FechaInicioAgenda.Free;
  setlength(CargoAgendado, 0);
  inherited destroy;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TAgendaCargosGNL.ClassName,
    TAgendaCargosGNL);
end;

procedure AlFinal;
begin
end;

end.
