unit uoddface_pam;

{$mode delphi}
interface

uses
  Classes, SysUtils,
  uoddface,
  xmatdefs,
  matreal,
  uauxiliares,
  uactores,
  ufechas,
  usalasdejuego,
  uDataSetGenerico,
  urosx,
  uInicioYFinal,
  uconstantessimsee;


(*

===========================================================
      P A M  Optimizador del Plan Anual de Mantenimiento
============================================================

Se trata de determinar el PAM (Plan Anual de Mantenimientos) de las centrales
de generación.
Para ello se debe crear la Sala SimSEE que con el sistema a optimizar.
Utilizando la aplicación oddface_prepare se debe crear el problema de optmización,
subir la sala y editar en la información del problema el listado de ordenes de mantenimiento
indicando el nombre de la central (tiene que coincidir con el Actor SimSEE)
y indicar la cantidad de unidades a poner en mantenimiento, la cantidad de días
que durará el mantenimiento y una fecha de antes de la cual NO se debe iniciar el
mantenimieno y una fecha antes de la cual SE debe iniciar el mantenimiento.

El parámetro de otimización, en cada orden es la fecha de inicio del mantenimiento
dentro de la ventana definida como posible para su inicio.

*)


type

  { TPAM_OrdenDeMantenimiento }

  TPAM_OrdenDeMantenimiento = class
    nid: Integer;
    nombre: String; // Nombre de la máquina en SimSEE
    unidades_a_mantener: Integer; // Unidades a poner en  mantenimiento
    dias_parada: Integer;
    fecha_inicio: TFecha; // Primer fecha a considerar
    fecha_fin: TFecha; // Última fecha a considerar
    {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
    nid_enganche: Integer;
    lugar_enganche: Integer;
    ordenes_enganchadas: TList;

    procedure OrdenarOrdenesEnganchadas();
    {$ENDIF}

    class function ReadFromRec( var r: TDataRecord ): TPAM_OrdenDeMantenimiento;
    procedure Free;
  end;

  { TPAM_OrdenesDeMantenimiento }

  TPAM_OrdenesDeMantenimiento = class
    lst: TList;
  private
    function getOrden( index: integer ): TPAM_OrdenDeMantenimiento;
    procedure setOrden( index: integer; value: TPAM_OrdenDeMantenimiento );
    function getOrdenByNid( nid: integer ): TPAM_OrdenDeMantenimiento;

  public
    class function ReadFromDB(dbconx: TDBrosxCon; nid_problema: integer
      ): TPAM_OrdenesDeMantenimiento;
    procedure Free;
    property orden[ index: integer ]: TPAM_OrdenDeMantenimiento
             read getOrden write setOrden; Default;

  end;


  TSimCostos_archi = class
    tasa: NReal; // [p.u.] tasa de descuento en por unidad
    cad_VE: NReal; // [MUSD] valor esperado del CAD
    cad_VaR5pe: NReal; // [MUSD] VaR(5%) del CAD
    cad_CVaR5pe: NReal; // [MUSD] CVaR(5%) del CAD
    cad_VE_aux: NReal; // [MUSD] valor esperado del CAD considerando CF_aux al final
    cad_VaR5pe_aux: NReal; // [MUSD] VaR(5%) del CAD considerando CF_aux al final
    vcad: TDAOfNReal; // [MUSD] vector con los CAD de cada crónica
    vcad_aux: TDAOfNReal; // [MUSD] vector de los CAD de cada crónica, considerando CF_aux al final
    vcdp: TDAOfNReal; // [MUSD] vector con los costos directos del paso (CDP) acumulados de cada crónica.
    vcffja: TDAOfNReal; // [MUSD] vector con el Costo Futuro de Fin de Juego Actualizado de cad crónica.
    vcffja_aux: TDAOfNReal; // [MUSD] vector con el Costo Futuro (auxiliar) de Fin de Juego Actualizado de cad crónica.
    vudp: TDAOfNReal; // [MUSD] vector con las utilidades directas del paso (UDP) acumuladas de cada crónica.
    constructor CreateRead( archi_res: string; NCronicas: integer );
    procedure Free;
  end;


  { TPAM_Problema }

  TPAM_Problema = class( TProblema )
    CarpetaSala: string;
    NombreSala: string;

    Ordenes: TPAM_OrdenesDeMantenimiento;
    {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
    TodasOrdenes: TPAM_OrdenesDeMantenimiento;
    {$ENDIF}

    constructor Create(dbconx: TDBrosxCon; recProblema: TDataRecord; idEjecutor_: integer; tmp_rundir_: string );
    class function CreateFromDB(dbconx: TDBrosxCon; nid_problema: integer;
      idEjecutor_: integer; tmp_rundir_: string): TPAM_Problema;
    function evaluar_( Individuo: TIndividuo; SemillaAleatoria: integer ): boolean; override;
    destructor destroy; override;

    function RunOptSim(sala: string; NCronicas, semillaAleatoria_: integer;
      Escenario: string): TSimCostos_archi;

    // Crea la sala y retorna el camino completo a la misma.
     function CrearSalaSimSEE(var Escenario: string; individuo: TIndividuo): string;
     function BajarCrearSalaSimSEE(nid: integer; out aIndividuo: TIndividuo): string;

     //
     function BajarIndividuosOrdenados(dbconx: TDBrosxCon; nid_problema: integer): TList;
     procedure ImprimirResultados(ListaIndividuos: TList);

  end;




implementation


(** Constructor de un problema tipo PAM **)
constructor TPAM_Problema.Create(dbconx: TDBrosxCon; recProblema: TDataRecord;
  idEjecutor_: integer; tmp_rundir_: string);

var
  kEtapa: integer; // Ordinal de la etapa de decisión
  kOrden: integer; // Ordinal de la Orden (del listado especificado en el problema)
  (*** En la forma de codificación del problema que se implementó, cada parámetro
  de optimización será el tiempo en el cual se comienza la realización de la orden.
  Como en problema se resuelve en una ventana de tiempo que se dividió en etapas,
  el parámetro será el ordinal de la etapa en que comienza la tarea. ***)

  aOrden: TPAM_OrdenDeMantenimiento; // Auxiliar para ir recorriendo las órdenes.

  rDias: double; // Distancia en días para calcular k_Min y k_Max
  k_Min, k_Max: integer; // Ordinales mínimo y máximo de la etapa en que puede comenzar la orden analizada
  {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
  fechaFinAux: TDateTime;
  kTodasOrden, lugar_enganche: Integer;
  ordenEnTodasOrdenes, ordenEnOrdenes, aOrdenAux: TPAM_OrdenDeMantenimiento;
  {$ENDIF}


begin
  // LLamamos al método de la clase ancestral para que se cree lo que se tenga que crear.
  inherited Create( dbconx,  recProblema, idEjecutor_, tmp_rundir_ );


// calculamos carpeta de la sala y nombre
//  CarpetaSala := extractFilePath( ArchiSala );
  CarpetaSala:= tmp_rundir_;
  NombreSala:= extractFileName( ArchiSala );
// acomodamos el directorio de la sala
  ArchiSala:= tmp_rundir_ + DirectorySeparator + NombreSala;
  if ( pos( '.ese', NombreSala ) > 0 ) then
     delete( NombreSala, pos( '.ese', NombreSala ), length( NombreSala ) - pos( '.ese', NombreSala ) + 1 );


// cargamos las ordenes de mantenimiento a optimizar
  {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
  TodasOrdenes:= TPAM_OrdenesDeMantenimiento.ReadFromDB( dbconx, nid_Problema);
  Ordenes:= TPAM_OrdenesDeMantenimiento.Create;
  Ordenes.lst:= TList.Create;
  for kTodasOrden:= 0 to TodasOrdenes.lst.count-1 do
  begin
      aOrden:= TodasOrdenes[kTodasOrden];
      if aOrden.nid_enganche<0 then
      begin
         aOrden.ordenes_enganchadas := TList.Create;
         Ordenes.lst.Add(aOrden);
      end;
  end;
  {$ELSE}
  Ordenes:= TPAM_OrdenesDeMantenimiento.ReadFromDB( dbconx, nid_Problema );
  {$ENDIF}

  if ordenes = nil then
     raise Exception.Create('No pude leer archivo del problema.');

  // Bien, ahora que tenemos las ordenes  definimos los parámetros
  // Los problemas tienen juegos de parámetros Enteros y Reales.
  // En este caso solo usaremos los enteroes. (DescriptoresE).
  setlength( DescriptoresE, ordenes.lst.count );
  setlength( DescriptoresR, 0 );

  {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
  for kTodasOrden:= 0 to TodasOrdenes.lst.count-1 do
  begin
    aorden:= TodasOrdenes[kTodasOrden];
    aOrdenAux:=aOrden;
    lugar_enganche:=0;
    while (aOrdenAux.nid_enganche>=0) do
    begin
     ordenEnTodasOrdenes:=TodasOrdenes.getOrdenByNid(aOrdenAux.nid_enganche);
     ordenEnOrdenes:=Ordenes.getOrdenByNid(aOrdenAux.nid_enganche);
     if (ordenEnOrdenes<>nil) then
     begin
        ordenEnOrdenes.ordenes_enganchadas.Add(aOrden);
        aOrden.lugar_enganche:=lugar_enganche;
        Break;
     end
     else if (ordenEnTodasOrdenes<>nil) then
     begin
        aOrdenAux:=ordenEnTodasOrdenes;
        inc(lugar_enganche);
     end
     else
         Break;
    end;
  end;
  {$ENDIF}

   for kOrden:= 0 to ordenes.lst.count-1 do
   begin
      aOrden:= ordenes[kOrden];
      {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
      aOrden.OrdenarOrdenesEnganchadas();
      {$ENDIF}
      // determinamos la primer etapa posible.
      // rdias = dias desde la primer fecha de inicio según la especificación global del
      // problema y la primer fecha de de inicio especificada en la orden.
      rdias:= aOrden.fecha_inicio.dt - fecha_primer_etapa.dt;
      if ( rdias < 0 ) then // mmm no está bueno el mantenimiento puede empezar antes
        k_min:= 0 // Limita la caja del problema
      else
        k_min:= trunc( (rdias +0.9) / dias_por_etapa ); // limita la caja de la orden
      if k_min > self.n_etapas then
         raise Exception.Create('Problema Infactible, una de las ordenes no puede iniciarse en la ventana del problema ');


      // ahora determinamos la última etapa posible
      // rdias = dias desde primer fecha según problema y última fecha según la orden.
      {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
      fechaFinAux:=aOrden.fecha_fin.dt;
      if aOrden.ordenes_enganchadas<>nil then
        for kTodasOrden:=0 to aOrden.ordenes_enganchadas.Count-1 do
        begin
           ordenEnTodasOrdenes:=aorden.ordenes_enganchadas[kTodasOrden];
           fechaFinAux:=fechaFinAux+ordenEnTodasOrdenes.dias_parada;
        end;
      rdias:= fechaFinAux - fecha_primer_etapa.dt;
      {$ELSE}
      rdias:= aOrden.fecha_fin.dt - fecha_primer_etapa.dt;
      {$ENDIF}
      if ( rdias < 0 ) then
      begin
        k_max:= 0; // mmm no es posible solucionar este problema. ERROR!!! INFACTIBLE DE PLANO!.
        raise Exception.Create('Problema Infactible, una de las ordenes no puede iniciarse en la ventana del problema ');
      end
      else
        k_max:= trunc( (rdias +0.9) / dias_por_etapa );
      if k_max > self.n_etapas then k_max:= n_etapas;

      DefinirParametroEntero(
        kOrden,
        'e_'+aOrden.Nombre+'_'+IntToStr( aOrden.nid ),
        k_min, k_max  );
   end;

  CalcularLargosADN;
end;


class function TPAM_Problema.CreateFromDB(dbconx: TDBrosxCon; nid_problema: integer;  idEjecutor_: integer; tmp_rundir_: string ): TPAM_Problema;
var
  ds: TResultadoQuery;
  r: TDataRecord;
  ap: TPAM_Problema;
begin
  ds:= dbconx.sql_query( 'SELECT * FROM ofe_problemas WHERE nid = '+IntToStr( nid_problema ) +' LIMIT 1 ');
  ds.next;
  r:= ds.getFilaActiva;
  if r <> nil then
    ap:= TPAM_Problema.Create(dbconx, r, idEjecutor_, tmp_rundir_ )
  else
    ap:= nil;
  ds.Free;
  result:= ap;
end;


function nextpal_sep(var r: string; sep: string ): string;
var
  res: string;
  i:   integer;
begin
  r := trim(r);
  i := pos( sep, r);
  if i > 0 then
  begin
    res := trim(copy(r, 1, i - 1));
    Delete(r, 1, i);
  end
  else
  begin
    res := r;
    r   := '';
  end;
  Result := res;
end;


function nextpal_tab(var r: string): string;
begin
  result:= nextpal_sep( r, #9 );
end;





procedure readln_vect( var f: textfile; var v: TDAOfNreal; N: integer );
var
  r: string;
  pal: string;
  k: integer;
begin
  setlength( v, N );
  system.readln( f, r );
  pal:= nextpal_tab( r ); // caption
  for k:= 0 to high( v ) do
  begin
    pal:= nextpal_tab( r );
    v[k]:= StrToFloat( pal );
  end;
end;

{$IFDEF MANTENIMIENTOS_ENGANCHADOS}
function OrdenDeOrdenesEnganchadas(Item1, Item2: Pointer): Integer;
var
  o1, o2: TPAM_OrdenDeMantenimiento;
begin
  o1:=TPAM_OrdenDeMantenimiento(Item1);
  o2:=TPAM_OrdenDeMantenimiento(Item2);
  if (o1.lugar_enganche > o2.lugar_enganche) then
    Result:=1
  else if (o1.lugar_enganche = o2.lugar_enganche) then
    Result:=0
  else
    Result:=-1;
end;



procedure TPAM_OrdenDeMantenimiento.OrdenarOrdenesEnganchadas();
begin
   Self.ordenes_enganchadas.Sort(@OrdenDeOrdenesEnganchadas);
end;
{$ENDIF}

class function TPAM_OrdenDeMantenimiento.ReadFromRec( var r: TDataRecord ): TPAM_OrdenDeMantenimiento;
var
 res: TPAM_OrdenDeMantenimiento;
begin
  res:= TPAM_OrdenDeMantenimiento.Create;
  res.nid:= r.GetByNameAsInt( 'nid' );
  res.nombre:= r.GetByNameAsString( 'nombre' );
  res.unidades_a_mantener:= r.GetByNameAsInt( 'unidades_a_mantener' );
  res.dias_parada:= r.GetByNameAsInt( 'dias_parada' );
  res.fecha_inicio:= TFecha.Create_ISOStr( r.GetByNameAsString( 'fecha_inicio' ) );
  res.fecha_fin:= TFecha.Create_ISOStr( r.GetByNameAsString( 'fecha_fin' ) );
  {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
  res.nid_enganche:= r.GetByNameAsInt( 'nid_enganche' );
  {$ENDIF}
  result:= res;
end;



procedure TPAM_OrdenDeMantenimiento.Free;
begin
  fecha_inicio.Free;
  fecha_fin.Free;
  inherited Free;
end;



class function TPAM_OrdenesDeMantenimiento.ReadFromDB(
  dbconx: TDBrosxCon; nid_problema: integer ): TPAM_OrdenesDeMantenimiento;
var
  aTecno: TPAM_OrdenDeMantenimiento;
  res: TPAM_OrdenesDeMantenimiento;
  sql: string;
  ds: TResultadoQuery;
  rec: TDataRecord;

begin
  res:= TPAM_OrdenesDeMantenimiento.Create;
  res.lst:= TList.Create;

  sql := 'SELECT * FROM ofe_PAM_ordenes WHERE nid_problema = ' + IntToStr(NID_Problema) +' AND activa = 1 ORDER BY nid ';
  ds:= dbconx.sql_query(sql);
  ds.first;
  rec := ds.getFilaActiva;
  while rec <> nil do
  begin
    aTecno:= TPAM_OrdenDeMantenimiento.ReadFromRec( rec );
    res.lst.add( aTecno );
    ds.Next;
    rec:= ds.getFilaActiva;
  end;

  ds.Free;
  result:= res;
end;



procedure TPAM_OrdenesDeMantenimiento.Free;
var
  k: integer;
begin
  for k:= 0 to lst.count-1 do
    orden[k].Free;
  lst.Free;
  inherited Free;
end;

function TPAM_OrdenesDeMantenimiento.getOrden( index: integer ): TPAM_OrdenDeMantenimiento;
begin
  result:= lst.items[ index ];
end;

procedure TPAM_OrdenesDeMantenimiento.setOrden( index: integer; value: TPAM_OrdenDeMantenimiento );
begin
  lst.items[ index ]:= value;
end;

function TPAM_OrdenesDeMantenimiento.getOrdenByNid(nid: integer
  ): TPAM_OrdenDeMantenimiento;
var
  kOrden: Integer;
  aordenAux: TPAM_OrdenDeMantenimiento;
begin
  Result:=nil;
  for kOrden:= 0 to Self.lst.count-1 do
  begin
    aordenAux:= Self[kOrden];
    if aordenAux.nid=nid then
        Result:=aOrdenAux;
  end;
end;



constructor TSimCostos_archi.CreateRead( archi_res: string; NCronicas: integer );
var
  f: textfile;
begin
  inherited Create;

  assign( f, archi_res );
  {$I-}
  reset( f );
  {$I+}
  if ioresult <> 0 then
   raise Exception.Create( 'No encontré archivo: '+archi_res);

  system.readln( f, tasa );
  system.readln( f, cad_VE);
  system.readln( f, cad_VaR5pe );
  system.readln( f, cad_CVaR5pe);
  system.readln( f, cad_VE_aux );
  system.readln( f, cad_VaR5pe_aux );

  readln_vect( f, vcad, NCronicas );
  readln_vect( f, vcad_aux, NCronicas );
  readln_vect( f, vcdp, NCronicas );
  readln_vect( f, vcffja, NCronicas );
  readln_vect( f, vcffja_aux, NCronicas );
  readln_vect(f, vudp, NCronicas);

  closefile( f );
end;

procedure TSimCostos_archi.Free;
begin
  setlength( vcad, 0 );
  setlength( vcad_aux, 0 );
  setlength( vcdp, 0 );
  setlength( vcffja, 0 );
  setlength( vcffja_aux, 0 );
  inherited Free;
end;


function TPAM_Problema.CrearSalaSimSEE(var Escenario: string; individuo: TIndividuo ): string;
var
  sala: TSalaDejuego;
  kEtapa, kOrden, kTodasOrden: integer;
  actor, actorAux: TActor;
  Orden, OrdenAux: TPAM_OrdenDeMantenimiento;
  nUnidades: TDAofNInt;
  fecha: TFecha;
  archiSalaModificada: string;

{$IFDEF DEBUG_CrearSalaSimSEE}
  s: string;
{$ENDIF}

begin
  uInicioYFinal.AlInicio;

  // cargamos la sala de juegos_base

  chdir(carpetaSala);

  sala:= usalasdejuego.cargarSala( 0, archiSala, '__principal__', true );
  Escenario := sala.EscenarioActivo.nombre;
  setlength( nUnidades, 1 );

(****

 Una posible es que los parámeros sean, indicar de cada
 cuantas unidades se quieren poner en mantenimiento en la ventana de tiempo
 y que el itente ubicarlas
 por ej,
 Mantener( 2 unidades de Salto grande sacandolas 15 días en la ventana 2013-01-01 a 2014-01-01 )

 Para mentener las 7 unidades de uruguay la orden sería poner varios mantenimientos
 para la misma central y que entonces pueda agruparlas o no.

 Las CTR, por ejemplo podría ser
 mantener( ctr, 1, 45d, fecha1, fecha2 )
 mantener( ctr, 1, 30d, fecha1, fecha2 )


 ***)

  for kOrden:= 0 to ordenes.lst.count - 1 do
  begin
    orden:= ordenes[kOrden];
    actor:= sala.ListaActores.find( Orden.Nombre ) as TActor;

    {$IFDEF DEBUG_CrearSalaSimSEE}
    s:= IntToStr( kOrden );
    {$ENDIF}

    kEtapa:= Individuo.XE.e( kOrden + 1 );
    fecha:= TFecha.Create_Clone( fecha_primer_etapa );
    fecha.addDias( kEtapa * dias_por_etapa );
    nUnidades[0]:= - Orden.unidades_a_mantener;

    actor.lpdUnidades.delta_unidades_( fecha, nUnidades );

    {$IFDEF DEBUG_CrearSalaSimSEE}
    s:= s+', '+Orden.Nombre+ ',du: '+ IntToStr( Orden.unidades_a_mantener )+', '+fecha.AsISOStr+', '+ IntToStr( nUnidades[0] );
    {$ENDIF}

//    kEtapa := kEtapa + trunc( orden.dias_parada / dias_por_etapa +0.2 );
    fecha:= TFecha.Create_Clone( fecha_primer_etapa );
//    fecha.addDias( kEtapa * dias_por_etapa );
    fecha.addDias( kEtapa * dias_por_etapa +  orden.dias_parada );
    nUnidades[0]:= Orden.unidades_a_mantener;
    actor.lpdUnidades.delta_unidades_( fecha, nUnidades );

    {$IFDEF DEBUG_CrearSalaSimSEE}
     s:= s+', '+fecha.AsISOStr+', '+ IntToStr( nUnidades[0] );
     writeln( s );
    {$ENDIF}

    {$IFDEF MANTENIMIENTOS_ENGANCHADOS}
    if orden.ordenes_enganchadas<>nil then
       for kTodasOrden:= 0 to orden.ordenes_enganchadas.count - 1 do
       begin
          ordenAux:= TPAM_OrdenDeMantenimiento(orden.ordenes_enganchadas[kTodasOrden]);
          actorAux:= sala.ListaActores.find( ordenAux.Nombre ) as TActor;

          nUnidades[0]:= - ordenAux.unidades_a_mantener;
          actorAux.lpdUnidades.delta_unidades_( fecha, nUnidades );

          fecha.addDias(ordenAux.dias_parada );
          nUnidades[0]:= ordenAux.unidades_a_mantener;
          actorAux.lpdUnidades.delta_unidades_( fecha, nUnidades );
       end;
    {$ENDIF}
  end;

  archiSalaModificada:= getDir_run + self.NombreSala + '_oddface_.ese';
  sala.WriteToArchi( archiSalaModificada );
  sala.Free;

  uInicioYFinal.AlFinal;

  result:= archiSalaModificada;
end;

function TPAM_Problema.BajarCrearSalaSimSEE(nid: integer; out aIndividuo: TIndividuo): string;
var
  Escenario: string;
begin
  aIndividuo:= LeerIndividuo( nid );
  decodificar_adn( aIndividuo );
  result:= CrearSalaSimSEE( Escenario, aIndividuo );
end;

function TPAM_Problema.BajarIndividuosOrdenados(dbconx: TDBrosxCon; nid_problema: integer): TList;
var
  sql: String;
  ds: TResultadoQuery;
  rec: TDataRecord;
begin
  Result:=TList.Create;

  sql := 'SELECT * FROM ofe_individuos_' + IntToStr(NID_Problema) +' ORDER BY f_objetivo ';
  ds:= dbconx.sql_query(sql);
  ds.first;
  rec := ds.getFilaActiva;
  while rec <> nil do
  begin
    Result.Add(TIndividuo.CreateFromRec(dbconx, self, rec));
    ds.Next;
    rec:= ds.getFilaActiva;
  end;

  ds.Free;
end;

procedure TPAM_Problema.ImprimirResultados(ListaIndividuos: TList);
var
  kIndice, kOrden, kdia, kdiaperiodo: Integer;
  Texto: Text;
  kEtapa: NEntero;
  aIndividuo: TIndividuo;
  fechasIni, fechasFin: TDAOfDateTime;
  nidsIndividuos: TDAofNInt;
  fObjetivos: TDAofNReal;
  fecha, fechaAux: TFecha;
  encontre: Boolean;
begin
  setSeparadoresGlobales_FechaUruguaya_V45_();

  Assign(Texto, tmp_rundir+'/salida.csv');
  Rewrite(Texto);

  SetLength(nidsIndividuos, ListaIndividuos.Count*Ordenes.lst.Count);
  SetLength(fechasIni, ListaIndividuos.Count*Ordenes.lst.Count);
  SetLength(fechasFin, ListaIndividuos.Count*Ordenes.lst.Count);
  SetLength(fObjetivos, ListaIndividuos.Count*Ordenes.lst.Count);

  //write(Texto, 'Nid Individuo');
  //write(Texto, #9, 'Fecha Inicial');
  //write(Texto, #9, 'Fecha Final');
  //write(Texto, #9, 'Fecha Medio Periodo');
  write(Texto, 'Fecha');
  writeln(Texto, #9, 'F Objetivo');

  {for kIndice:=0 to ListaIndividuos.Count-1 do
  begin
    aIndividuo := TIndividuo(ListaIndividuos[kIndice]);
    self.decodificar_adn(aIndividuo);

    for korden:=1 to aIndividuo.XE.n do
    begin
      kEtapa:= aIndividuo.XE.e(kOrden);

      fecha:= TFecha.Create_Clone( fecha_primer_etapa );
      fecha.addDias( kEtapa * dias_por_etapa );
      fechasIni[kIndice*kOrden]:= fecha.dt;

      fecha.addDias(Ordenes.getOrden(kOrden-1).dias_parada);
      fechasFin[kIndice*kOrden]:= fecha.dt;

      fObjetivos[kIndice*kOrden]:=aIndividuo.f_objetivo;
      nidsIndividuos[kIndice*kOrden]:=aIndividuo.nid;
      fecha.Free;

      for kdia:=0 to Ordenes.getOrden(kOrden-1).dias_parada-1 do
      begin
        fecha:= TFecha.Create_Clone( fecha_primer_etapa );
        fecha.addDias( kEtapa * dias_por_etapa );
        fecha.addDias( kdia );
        //write(Texto, nidsIndividuos[kindice*kOrden]);
        //write(Texto, #9, DateToStr(fechasIni[kindice*kOrden]));
        //write(Texto, #9, DateToStr(fechasFin[kindice*kOrden]));
        //write(Texto, #9, DateToStr((fechasIni[kindice*kOrden]+fechasFin[kindice*kOrden])/2));
        write(Texto, DateToStr(fecha.dt));
        writeln(Texto, #9, 1/fObjetivos[kindice*kOrden]);
        fecha.free;
      end;
    end;

  end; }
  fechaAux:= TFecha.Create_Clone( fecha_primer_etapa );

  for kdiaperiodo:=0 to (dias_por_etapa*n_etapas)-1 do
  begin
    encontre:=False;

    kIndice:=0;
    while (kIndice<ListaIndividuos.Count) and not(encontre) do
    begin
      aIndividuo := TIndividuo(ListaIndividuos[kIndice]);
      self.decodificar_adn(aIndividuo);
      korden:=1;
      while (korden<=aIndividuo.XE.n) and not(encontre) do
      begin
        kEtapa:= aIndividuo.XE.e(kOrden);

        fObjetivos[kIndice*kOrden]:=aIndividuo.f_objetivo;
        nidsIndividuos[kIndice*kOrden]:=aIndividuo.nid;
        kdia:=0;
        while (kdia<Ordenes.getOrden(kOrden-1).dias_parada) and not(encontre) do
        begin
          fecha:= TFecha.Create_Clone( fecha_primer_etapa );
          fecha.addDias( kEtapa * dias_por_etapa );
          fecha.addDias( kdia );

          if fecha.dt=fechaAux.dt then
          begin
             write(Texto, DateToStr(fecha.dt));
             writeln(Texto, #9, 1/fObjetivos[kindice*kOrden]);
             encontre:=TRUE;
          end;
          fecha.free;
          inc(kdia);
        end;
        inc(korden);
      end;
      inc(kIndice);
    end;
    fechaAux.addDias( 1 );
  end;
  CloseFile(Texto);
end;



// retorna NIL si falla algo
function TPAM_Problema.RunOptSim( sala: string; NCronicas, semillaAleatoria_: integer ;
  Escenario: string): TSimCostos_archi;
var
  cmd:    string;
  params: array of string;
  archi_simcosto: string;
begin

  if tmp_rundir = '' then
    archi_simcosto:= getDir_run + NombreSala + DirectorySeparator
  else
    archi_simcosto := getDir_run;

 limpiarCarpeta( archi_simcosto, 'simres_*' );
 limpiarCarpeta( archi_simcosto, 'simcosto_*' );
 limpiarCarpeta( archi_simcosto, 'estado_fin_cron_*' );


  setlength(params, 3);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + IntToStr( idEjecutor );
  params[2] := 'semilla='+ IntToStr( semillaAleatoria_ );

{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdopt';
{$ELSE}
  cmd := getDir_bin + 'cmdopt.exe';
{$ENDIF}
  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdopt');
{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdsim';
{$ELSE}
  cmd := getDir_bin + 'cmdsim.exe';
{$ENDIF}
  setlength(params, 4);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + intToStr( idEjecutor );
  params[2] := 'semilla='+ IntToStr( semillaAleatoria_ );
  params[3] := 'NCronicasSim=' + IntToStr( NCronicas );

  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdsim');
  setlength(params, 0);


  try
    if tmp_rundir = '' then
      archi_simcosto:= getDir_run + NombreSala + DirectorySeparator
    else
      archi_simcosto := getDir_run;

    archi_simcosto:= archi_simcosto+ 'simcosto_' + IntToStr( semillaAleatoria_ ) + 'x'+IntToStr( NCronicas )+'_' + Escenario + '.xlt';
    result:= TSimCostos_archi.CreateRead( archi_simcosto, NCronicas );
  except
    result:= nil;
    raise Exception.Create('Error al leer archivo de resultados uplanes.425: ' + archi_simcosto );
  end;

end;

function TPAM_Problema.evaluar_( Individuo: TIndividuo; SemillaAleatoria: integer  ): boolean;
var
  archiSalaModificada: string;
  res_SimCostos: TSimCostos_archi;
  Escenario: string;
begin
  archiSalaModificada:= crearSalaSimsee(Escenario, Individuo );
  result:= true;
  res_SimCostos:= RunOptSim( archiSalaModificada, NCronicasCronicasPorVez, SemillaAleatoria, Escenario);
  if res_simCostos <> nil then
  begin
  Individuo.f_histo:= TVectR.Create_FromDAofR( res_SimCostos.vcad );
  Individuo.f_VE:= res_SimCostos.cad_VE;
  Individuo.f_VaR:= Individuo.f_histo.pe_VaR(  pe_var );
  Individuo.f_CVaR:= Individuo.f_histo.pe_CVaR(  pe_var );
  Individuo.f_MIN:= individuo.f_histo.e(1);
  Individuo.f_MAX:= individuo.f_histo.e( individuo.f_histo.n );
  Individuo.f_Objetivo:= ro_VE* Individuo.f_VE + ro_VaR * Individuo.f_VaR + ro_CVaR * Individuo.f_CVaR;
  res_SimCostos.Free;
  end
  else
    raise Exception.Create('Falló la simulación');
end;


destructor TPAM_Problema.destroy;
begin
  Ordenes.Free;
  inherited destroy;
end;

end.

