unit uOptSimDis;

{$mode delphi}

interface

uses
  uConectorProblema, usalasdejuego, u_updownload, uConstantesConector,
  uConstantesOptSimDis, uTareas, uNodosDeCalculo, uMsgRetardados,
  uFuncionesAuxiliares, unettopos, unettopostypes, uglobsharedmem,
  uConstantesSimSEE, uInicioYFinal, xmatdefs, uauxiliares, uDataSetGenerico,
  ubuffrw, dateutils, urosx,
  {$IFDEF LINUX}

  {$ELSE}
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Messages,
  Windows;

  {$ENDIF}

const

  multiplicidad_tareas_por_nodo = 1;

type

  { TTareaCalcularRangosPaso }

  TTareaCalcularRangosPaso = class(TTarea)

    sala_: TSalaDeJuego;

    k_Paso_: integer;
    k_estrellaIni_: integer;
    k_estrellaFin_: integer;

    framePaso: TDAOfNReal;
    Auxs_r_Paso: TDAOfDAofNReal;
    Auxs_i_Paso: TDAOfDAOfNInt;


    constructor Crear(codigoMsg: integer; k_Paso: integer; k_estrellaIni: integer;
      k_estrellaFin: integer; sala: TSalaDeJuego;
      freeBufferDatos: boolean = True);


    constructor CrearDesdeMSG(idCom: integer; codMsg: integer; sala: TSalaDeJuego);

    procedure resolverTareaEnLinea; override;
    procedure procesarResultadosEnLinea; override;

  end;

  { TTareaDarPaso }

  TTareaDarPaso = class(TTarea)

    sala_: TSalaDeJuego;

    k_Paso_: integer;

    framePaso: TDAOfNReal;
    Auxs_r_Paso: TDAOfDAofNReal;
    Auxs_i_Paso: TDAOfDAofNInt;

    constructor Crear(codigoMsg: integer; k_Paso: integer;
      sala: TSalaDeJuego; enviarAuxs: boolean);

    constructor CrearDesdeMSG(idCom: integer; codMsg: integer;
      sala: TSalaDeJuego);

    procedure resolverTareaEnLinea; override;

  end;


  TEstadosOptSimDis = (EOSD_inicio_, EOSD_optPaso_, EOSD_sincroPaso_,
    EOSD_dandoPaso_, EOSD_fin_, EOSD_abortado_);

  { Toptsimdis }

  Toptsimdis = class(TConectorProblema)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { private declarations }


    //Id de tareas
    idTareas: integer;
    ultimaComunicacion: TDateTime;
    idMsgTimeOUT: integer;

    procedure cambiarEstado(e: TEstadosOptSimDis);

  public
    { public declarations }

    estadosOptSimDis: TEstadosOptSimDis;
    archiSala: string;
    sala: TSalaDeJuego;

    totalEstrellas: integer;
    nEstrellasCalculadas: integer;
    nMiliSecPorEstrella: NReal;
    nTicsIni: int64;
    nTicsNow: int64;
    frec: int64;

    nSecsTimeOut: integer;

    lstTareasProcesando: TListaDeTareas;

    function bajarSalaProblema: boolean;


    procedure inicializarOpt;
    procedure inicializarSim;

    //********   Master *****


    procedure iniciarResolucion; override;

    procedure setTimeOut(nSecs: integer);


    procedure distribuirRangosPaso(kPaso, nTareas: integer);

    procedure darPaso;

    procedure finOpt;

    procedure msgr_calcularRangoEstrellas(var Msg: TMessage);
      message MSGR_CALCULAR_RANGO_ESTRELLAS;
    procedure msgr_DarPaso(var Msg: TMessage); message MSGR_DAR_PASO;

    procedure msgp_time_out(var Msg: TMessage); message MSGP_TIMEOUT;

    //********   Worker *****

    procedure msgp_calcularRangoEstrellas(var Msg: TMessage);
      message MSGP_CALCULAR_RANGO_ESTRELLAS;
    procedure msgp_DarPaso(var Msg: TMessage); message MSGP_DAR_PASO;

  end;

var

  optsimdis: Toptsimdis;

implementation

{ TTareaDarPaso }

constructor TTareaDarPaso.Crear(codigoMsg: integer; k_Paso: integer;
  sala: TSalaDeJuego; enviarAuxs: boolean);
var
  datos: TBuffWriter;

begin

  self.sala_ := sala;

  self.framePaso := sala.globs.CF.constelacion.fCosto[k_Paso + 1];
  self.Auxs_r_Paso := sala.globs.Auxs_r1;
  self.Auxs_i_Paso := sala.globs.Auxs_i1;

  self.tambuffDatos_ := 0;

  self.tambuffDatos_ := self.tambuffDatos_ + xSizeOf(enviarAuxs) +
    xSizeOf(framePaso) + xSizeOf(k_Paso);

  if enviarAuxs then
  begin
    self.tambuffDatos_ := self.tambuffDatos_ + xSizeOf(Auxs_r_Paso) +
      xSizeOf(Auxs_i_Paso);

    datos := TBuffWriter.Create(self.tambuffDatos_);

    datos.xBoolean(enviarAuxs);
    datos.xInteger(k_Paso);
    datos.xTDAOfNReal(framePaso);
    datos.xTDAOfDAOfNReal(Auxs_r_Paso);
    datos.xTDAOfDAOfNInt(Auxs_i_Paso);

  end
  else
  begin
    datos := TBuffWriter.Create(self.tambuffDatos_);
    datos.xBoolean(enviarAuxs);
    datos.xInteger(k_Paso);
    datos.xTDAOfNReal(framePaso);
  end;

  inherited Crear(codigoMsg, datos.pBuff, datos.tamBuff, True, False, True);

end;

constructor TTareaDarPaso.CrearDesdeMSG(idCom: integer; codMsg: integer;
  sala: TSalaDeJuego);
begin

  self.sala_ := sala;
  inherited CrearDesdeMSG(idCom, codMsg, True, True, True);

end;

procedure TTareaDarPaso.resolverTareaEnLinea;
var
  datos: TBuffReader;
  vinieronAuxs: boolean;
begin

  datos := TBuffReader.Create(self.bufferDatos_, self.tambuffDatos_);
  datos.xBoolean(vinieronAuxs);
  datos.xInteger(self.k_Paso_);
  datos.xTDAOfNReal(self.framePaso);

  self.sala_.setRangoEstrellasCF(0, framePaso, k_Paso_ + 1);
  if vinieronAuxs then
  begin
    datos.xTDAOfDAOfNReal(self.Auxs_r_Paso);
    datos.xTDAOfDAOfNInt(self.Auxs_i_Paso);

    self.sala_.setRangoEstrellasAux_r1(0, Auxs_r_Paso);
    self.sala_.setRangoEstrellasAux_i1(0, Auxs_i_Paso);
  end;

  sala_.globs.Fijar_kPaso(k_Paso_);
  sala_.globs.SwapAuxs;

  sala_.globs.ActualizadorLPD.ActualizarFichasHasta(sala_.globs.FechaInicioDelpaso);
  sala_.PrepararPaso_as;

  writeln('yendo a paso: ', k_Paso_);
  self._resuelta := True;

end;

{ TTareaCalcularRangosPaso }

constructor TTareaCalcularRangosPaso.Crear(codigoMsg: integer;
  k_Paso: integer; k_estrellaIni: integer; k_estrellaFin: integer;
  sala: TSalaDeJuego; freeBufferDatos: boolean);
var
  datos: TBuffWriter;

begin

  self.sala_ := sala;

  k_Paso_ := k_Paso;
  k_estrellaIni_ := k_estrellaIni;
  k_estrellaFin_ := k_estrellaFin;

  tambuffDatos_ := SizeOf(integer) * 3;
  datos := TBuffWriter.Create(tambuffDatos_);

  datos.xInteger(k_Paso_);
  datos.xInteger(k_estrellaIni_);
  datos.xInteger(k_estrellaFin_);

  inherited Crear(codigoMsg, datos.pBuff, datos.tamBuff, True, True, True);

end;

constructor TTareaCalcularRangosPaso.CrearDesdeMSG(idCom: integer;
  codMsg: integer; sala: TSalaDeJuego);
begin

  self.sala_ := sala;
  inherited CrearDesdeMSG(idCom, codMsg, True, True, True);

end;


procedure TTareaCalcularRangosPaso.resolverTareaEnLinea;
var
  datos: TBuffReader;
  datosSalida: TBuffWriter;
  aux_r0: boolean;
  aux_i0: boolean;
  i: integer;
  j: integer;
  prueba: TBuffReader;
  Aux_r: TDAOfDAofNReal;
begin

  datos := TBuffReader.Create(self.bufferDatos_, self.tambuffDatos_);

  datos.xInteger(k_Paso_);
  datos.xInteger(k_estrellaIni_);
  datos.xInteger(k_estrellaFin_);

  writeln('Calculando PASO: ', k_Paso_, ' RANGO: ', k_estrellaIni_, ' ', k_estrellaFin_);

  if sala_.globs.SortearOpt then // Cálculo de los costos de la etapa con sorteos
  begin
    sala_.OptimizacionCronizada_UnPaso_RangoDeEstrellas(
      k_estrellaIni_, k_estrellaFin_, False, False);
  end // fin de la optimización Cronizada
  else
  begin // incio: optimización con valores esperados
    sala_.OptimizacionValorEsperado_UnPaso_RangoDeEstrellas(
      k_estrellaIni_, k_estrellaFin_, False, False);
  end;

  framePaso := sala_.getRangoEstrellasCF(k_estrellaIni_, k_estrellaFin_, k_Paso_);
  Auxs_r_Paso := sala_.getRangoEstrellasAux_r1(k_estrellaIni_, k_estrellaFin_);
  Auxs_i_Paso := sala_.getRangoEstrellasAux_i1(k_estrellaIni_, k_estrellaFin_);

  //for i :=0 to Length(sala_.globs.Auxs_r1)-1 do
  //  vclear(sala_.globs.Auxs_r1[i]);

  //for i :=0 to Length(sala_.globs.CF.constelacion.fCosto[k_Paso_])-1 do
  //  vclear(sala_.globs.CF.constelacion.fCosto[k_Paso_]);

  tambuffResultados_ := xSizeOf(framePaso) + xSizeOf(Auxs_r_Paso) + xSizeOf(Auxs_i_Paso);
  datosSalida := TBuffWriter.Create(tambuffResultados_);
  datosSalida.xTDAOfNReal(framePaso);
  datosSalida.xTDAOfDAOfNReal(Auxs_r_Paso);
  datosSalida.xTDAOfDAOfNInt(Auxs_i_Paso);

  self.bufferResultados_ := datosSalida.pBuff;

  self._resuelta := True;

end;


procedure TTareaCalcularRangosPaso.procesarResultadosEnLinea;
var
  datos: TBuffReader;
  i: integer;

begin

  datos := TBuffReader.Create(self.bufferResultados_, self.tambuffResultados_);

  datos.xTDAOfNReal(framePaso);
  datos.xTDAOfDAOfNReal(Auxs_r_Paso);
  datos.xTDAOfDAOfNInt(Auxs_i_Paso);


  self.sala_.setRangoEstrellasCF(k_estrellaIni_, framePaso, k_Paso_);
  self.sala_.setRangoEstrellasAux_r1(k_estrellaIni_, Auxs_r_Paso);
  self.sala_.setRangoEstrellasAux_i1(k_estrellaIni_, Auxs_i_Paso);

  writeln('Completando PASO: ', k_Paso_, ' RANGO: ', k_estrellaIni_, ' ', k_estrellaFin_);

  datos.Free;

  self._resuelta := True;

end;


{$R *.lfm}

{ Toptsimdis }

procedure Toptsimdis.FormCreate(Sender: TObject);
begin
  inherited Create('optsimdis', 'OPT');
  cambiarEstado(EOSD_inicio_);
  if not ((estado = ED_esperandoTareas_) or (estado = ED_esperandoWks_)) then
  begin
    Salir;
    exit;
  end
  else
  begin
    if not bajarSalaProblema then
    begin
      Salir;
      exit;
    end
    else
    if not soyMaster then
      master.notificar(MSGP_NOTIFICAR_WK_ON);
  end;
end;

procedure Toptsimdis.FormDestroy(Sender: TObject);
begin
  inherited Free;
end;

procedure Toptsimdis.cambiarEstado(e: TEstadosOptSimDis);
begin
  {$IFDEF DEBUG}
  writeln('Cambiando estadoOptSim de ', self.estadosOptSimDis, ' -> ', e);
  {$ENDIF}
  estadosOptSimDis := e;
end;

function Toptsimdis.bajarSalaProblema: boolean;
var
  dir: string;
  dirResultados: string;

begin

  Result := True;

  dir := getDir_Tmp + 'clt_' + IntToStr(problema._nid);
  if not DirectoryExists(dir) then
  begin
    CreateDir(dir);
    bajarCarpeta('clt_carpetas', 'clt_archivos', problema._nid, dir);
  end;

  dirResultados := dir + DirectorySeparator + 'Resultados';
  if not DirectoryExists(dirResultados) then
    CreateDir(dirResultados);


  try
    uInicioYFinal.AlInicio;
    archiSala := dir + DirectorySeparator + problema._sala;
    self.sala := TSalaDeJuego.cargarSala(archiSala, '__principal__', True);
    sala.dirResultadosCorrida := dirResultados;

    if ((problema._tipo = 'OPT') or (problema._tipo = 'OyS')) then
      inicializarOpt;

    if problema._tipo = 'SIM' then
      inicializarSim;

  except
    Result := False;
  end;
end;

procedure Toptsimdis.inicializarOpt;
var
  dir: string;
  dir_sala: string;

  ds: TResultadoQuery;
  rec: TDataRecord;
begin
  // nidProblema | tasa | nCronicas | semilla | maxIter | disponibilidad | sortear |
  // establizarFrameIni | guardarCF
  ds := sql_query('SELECT * from clt_parametrosOpt WHERE nidProblema=' +
    IntToStr(problema._nid));
  rec := ds.Next;
  if rec = nil then
  begin
    Salir;
    exit;
  end
  else
  begin
    sala.globs.TasaDeActualizacion := rec.GetByNameAsFloat('tasa');
    sala.globs.NCronicasOpt := rec.GetByNameAsInt('nCronicas');
    sala.globs.semilla_inicial_opt := rec.GetByNameAsInt('semilla');

    sala.globs.NMAX_ITERACIONESDELPASO_OPT := rec.GetByNameAsInt('maxIter');

    sala.estabilizarInicio := boolean(rec.GetByNameAsInt('establizarFrameIni'));
    sala.globs.SortearOpt := boolean(rec.GetByNameAsInt('sortear'));
    sala.globs.ObligarDisponibilidad_1_Opt :=
      boolean(rec.GetByNameAsInt('disponibilidad'));
  end;



  dir := GetCurrentDir;
  dir_sala := ExtractFileDir(archiSala);
  ChDir(dir_sala);
  sala.inicializarOptimizacion_subproc01;
  if sala.inicializarOptimizacion_subproc02(nil, nil) = 0 then
  begin
    Salir;
    exit; // No hay variables de estado = no hay optimización
  end;

  setSeparadoresGlobales; // para que escriba en global
  sala.globs.ActualizadorLPD.ActualizarFichasHasta(sala.globs.FechaInicioDelpaso);
  sala.PrepararPaso_as;

  ChDir(dir);

end;

procedure Toptsimdis.inicializarSim;
begin

end;

procedure Toptsimdis.iniciarResolucion;
var
  nJugadores: integer;
  i: integer;

  nTicsIni_: int64;
  nTicsEnd_: int64;
  frec: int64;

begin

  QueryPerformanceCounter(nTicsIni_);
  sala.calcularRangoEstrellas(0, 0, False, False);
  QueryPerformanceCounter(nTicsEnd_);
  QueryPerformanceFrequency(frec);

  //TimeOut inicial
  //tiempo estimado que tarda en calcularse un frame por 10
  nSecsTimeOut := trunc(((nTicsEnd_ - nTicsIni_) / frec) *
    sala.globs.CF.nEstrellasPorPuntoT * 10) + 1;
  setTimeOut(nSecsTimeOut);

  nJugadores := nodosNuevos.Count;
  for i := 0 to nJugadores - 1 do
    nodosActivos.Add(nodosNuevos.Get(i));

  nodosNuevos.Clear;

  totalEstrellas := sala.globs.NPasos * sala.globs.CF.nEstrellasPorPuntoT;
  nEstrellasCalculadas := 0;
  nMiliSecPorEstrella := 0;
  QueryPerformanceCounter(nTicsIni);

  writeln('resolviendo paso ', sala.globs.kPaso_, ' de ', sala.globs.NPasos);

  distribuirRangosPaso(sala.globs.kPaso_, nJugadores * multiplicidad_tareas_por_nodo);

end;

procedure Toptsimdis.setTimeOut(nSecs: integer);
var
  fc: TFichaComunicado;
begin

  fc.idNodoOrigen := pm^.idNodoLocal;
  fc.idOrigen := idAplicYo;
  fc.idNodoDestino := pm^.idNodoLocal;
  fc.idDestino := idAplicYo;
  fc.nBytesDatos := 0;
  fc.codigoMsg := MSGP_TIMEOUT;

  idMsgTimeOUT := agendar_msgRetardado(fc, MSGP_TIMEOUT, nSecs, True);

end;

procedure Toptsimdis.distribuirRangosPaso(kPaso, nTareas: integer);
var
  nEstrellas: integer;
  nEstrallasPorRangos: TDAofNInt;
  t: TTareaCalcularRangosPaso;

  estrellaIni: integer;
  i: integer;

begin

  lstTareasProcesando := TListaDeTareas.Create;
  sincronizarNodos;

  nEstrellas := sala.globs.CF.nEstrellasPorPuntoT;
  nEstrallasPorRangos := XdivYrepartirResto(nEstrellas, nTareas);

  estrellaIni := 0;
  for i := 0 to length(nEstrallasPorRangos) - 1 do
  begin

    t := TTareaCalcularRangosPaso.Crear(MSGP_CALCULAR_RANGO_ESTRELLAS,
      sala.globs.kPaso_, estrellaIni,
      estrellaIni + nEstrallasPorRangos[i] - 1, self.sala);
    lstTareasProcesando.agregarTarea(t);
    estrellaIni := estrellaIni + nEstrallasPorRangos[i];

  end;

  lstTareasProcesando.despachar(nodosActivos);

  cambiarEstado(EOSD_optPaso_);

end;

procedure Toptsimdis.darPaso;
var
  i: integer;
  enviarAuxs: boolean;
begin

  lstTareasProcesando := TListaDeTareas.Create;
  enviarAuxs := sincronizarNodos;

  self.sala.globs.Fijar_kPaso(self.sala.globs.kPaso_ - 1);
  if self.sala.globs.kPaso_ > 0 then
  begin
    for i := 0 to nodosActivos.Count - 1 do
      lstTareasProcesando.agregarTarea(TTareaDarPaso.Crear(MSGP_DAR_PASO,
        self.sala.globs.kPaso_, self.sala, enviarAuxs));


    lstTareasProcesando.despachar(nodosActivos);
    cambiarEstado(EOSD_dandoPaso_);

  end
  else
    finOpt;

end;



procedure Toptsimdis.finOpt;
begin

  writeln('guardando CF en: ' + sala.dirResultadosCorrida + DirectorySeparator + 'CF.bin');
  sala.globs.CF.StoreInArchi(sala.dirResultadosCorrida + DirectorySeparator + 'CF2.bin');

end;

procedure Toptsimdis.msgr_calcularRangoEstrellas(var Msg: TMessage);
var
  pfc: PFichaComunicado;
  nodo: TNodoCalculo;
  t: TTarea;
  nEstrellasTarea: integer;
  i: integer;
begin

  ultimaComunicacion := now;
  if levantarMensajesTareas(Msg.wParam, lstTareasProcesando, nodosActivos, nodo, t) then
  begin
    if lstTareasProcesando.tareasResueltas then
    begin

      writeln(lstTareasProcesando.nSecsEnResolver);
      problema.setNSecsToEnd(lstTareasProcesando.nSecsEnResolver *
        (sala.globs.kPaso_ - 1));

      lstTareasProcesando.Free;
      darPaso;
    end

    else
      lstTareasProcesando.despachar(nodo);

    nEstrellasTarea := TTareaCalcularRangosPaso(t).k_estrellaFin_ -
      TTareaCalcularRangosPaso(t).k_estrellaFin_ + 1;
    nEstrellasCalculadas := nEstrellasCalculadas + nEstrellasTarea;

    problema.setAvance(nEstrellasCalculadas / totalEstrellas);

  end;

end;


procedure Toptsimdis.msgr_DarPaso(var Msg: TMessage);
var

  pfc: PFichaComunicado;
  nodo: TNodoCalculo;
  t: TTarea;
  nJugadores: integer;

begin
  ultimaComunicacion := now;
  if levantarMensajesTareas(Msg.wParam, lstTareasProcesando, nodosActivos, nodo, t) then
    if lstTareasProcesando.tareasResueltas then
    begin
      lstTareasProcesando.Free;
      nJugadores := nodosActivos.Count;
      distribuirRangosPaso(sala.globs.kPaso_,
        nJugadores * multiplicidad_tareas_por_nodo);
    end;
end;

procedure Toptsimdis.msgp_calcularRangoEstrellas(var Msg: TMessage);
var
  t: TTareaCalcularRangosPaso;
begin

  t := TTareaCalcularRangosPaso.CrearDesdeMSG(Msg.wParam,
    MSGR_CALCULAR_RANGO_ESTRELLAS, self.sala);
  t.Destruir;

end;

procedure Toptsimdis.msgp_DarPaso(var Msg: TMessage);
var
  t: TTareaDarPaso;
begin

  t := TTareaDarPaso.CrearDesdeMSG(Msg.wParam, MSGR_DAR_PASO, self.sala);
  t.Destruir;

end;

procedure Toptsimdis.msgp_time_out(var Msg: TMessage);
var
  nsecs: integer;
begin

  nsecs := SecondsBetween(ultimaComunicacion, now);
  if nsecs > nSecsTimeOut then
    suspenderProceso;

end;

end.
