{$DEFINE PARTICION_DINAMICA}
{xDEFINE MEDIR_TIEMPOS}
{xDEFINE DEBUG_EJEC}
unit uRobotOrquestadorV2;

interface

uses
  uDespachador, xMatDefs, uRangos, classes, fddp, sysutils, usalasdejuego,
  uMsgsOptDisV2, uTareas, ubuffrw, uconstantes_nettopos, uCosa, uSalasDeJuegoParaEditor,
  uCosaConNombre, uMsgsOrquestadorV2, uMsgsFileManager, uDatosNodo,
  uFileHandler, uDatosNodoOptDisV2, uconstantesSimSEE, uAuxiliares,
  uglobs,
{$IFDEF WINDOWS}
  ipcthrd, Windows, utilidades, Forms,  
{$ELSE}
  timeLib, uEmuladorWinIPC, uWinMsgs,
{$ENDIF}
{$IFDEF NETTOPOS_DLL}
  uimportnettopos,
{$ELSE}
  unettopos,
  unettopostypes,
  uglobsharedmem
{$ENDIF};

const
  timeOutCalcEstrellasPEstrella = 2;//1 estrella lleva 2 msecs
  timeOutActRestoPEstrella = 2;
  maxNIntentosDeRevivirNodos = 3;
  timeOutCargarEInicializarSala = 100000;
  timeOutSetEstadoFuenteMadreUniforme = 100000;

type
  TDAOfDAOfNReal = array of TDAofNReal;

    {Cantidad de estrellas a calcular por cada nodo
    reasigna la cantidad de estrellas para cada nodo de forma que los tiempos
    de calculo de los distintos nodos tiendan a igualarse

    supongamos que tenemos k nodos con sus tiempos de calculo t0...tk, y sus
    cantidades de estrellas n0...nk
    el tiempo que tarda un nodo en calcular su rango de estrellas es proporcional
    a la cantidad de estrellas que tiene asignada, asumamos que es directamente
    proporcional => ti = ni * (1 / vi) (1)
    donde vi es la cantidad de estrellas resueltas por unidad de tiempo

    ahora queremos que t1 = t2 = ... = tk => sustituyendo por (1)
    n0 * (1 / v0) = n1 * (1 / v1) = ... = nk * (1 / vk)

    o lo que es lo mismo n0 * (1 / v0) = ni * (1 / vi) (2) para todo i
    perteneciente a 1...k
    despejando de (2) obtenemos
    ni = n0 * (1 / v0) / (1 / vi) = n0 * vi / v0 (3)

    sabemos que sumatoria(ni) = nEstrellas => sustituyendo por (3)
    n0 + (n0 * v1/v0) + (n0 * v2/v0) + ... + (n0 * vk/v0) = nEstrellas =>
    n0 * (1 + v1/v0 + v2/v0 + ... vk/v0) = nEstrellas =>
    n0 = nEstrellas / (1 + v1/v0 + v2/v0 + ... vk/v0)
    y con esto podemos calcular las cantidades de estrellas que debe asignarse a
    cada nodo
    }

  //El orquestador mantiene una lista de estos con una correspondencia directa
  //con los nodos en el despachador. Es decir, el datosNodo[i] corresponde a la
  //fichaNodo[i]

  TRobotOrquestador = class(TThread)
    public
      sala: TSalaDeJuego;
      Constructor Create(archiSala: String; despachador: TDespachador);
      procedure Execute; override;
      procedure Free;
    private
      ticksIni, ticksFin, frec: Int64;
      invFrec: NReal;
      archiSala: String;
      archiSalaMod: String; //archiSala_Netopos.ese
      despachador: TDespachador;
      particionRango, particionRangoPasoPasado: TListaDeRangos;
      conjuntoDeEjecucion: TConjuntoDeEjecucion;
      timeOutCalcEstrellas, timeOutActRestoEstrellas: Cardinal;
      invLargoTotal: NReal;

{$IFDEF MEDIR_TIEMPOS}
      //Para debug
      lineasTEjecs: TStringList;
      r: String;
      rs: array of String;
{$ENDIF}

{$IFDEF DEBUG_EJEC}
      fOrquestadorV2: TextFile;
{$ENDIF}
      procedure Optimizar;

      procedure CargarSala(archiSala: String);
      procedure transmitirArchivos(archiSala: String; idsNodos: TDAofNCardinal);

      function crearListaTareasCargarEInicializarSala(conjuntoDeEjecucion: TConjuntoDeEjecucion{of TDatosNodoOptDisV2}): TList{of TFichaTarea};
      procedure procesarCargarEInicializarSala(listaTareas: TList{of TFichaTarea});

      //Revisa si algun nodo esta en un paso distinto al que va la simulación,
      //si hay pregunta a algun nodo que haya ejecutado correctamente los valores
      //de glma, glinext y glinextp de su fuente MadreUniforme y se los envía al
      //nodo no sincronizado para que pueda engancharse en la optimización
      procedure chequearPorNodosNoSincronizados;
      procedure getEstadoMadreUniforme(var glma: TDAofNInt; glinext: Integer; glinextp: Integer);

      //pasa por la lista de nodos viendo quienes estan disponibles y quienes no
      //si no hay nodos disponibles le pide al despachador chequearEstadosNodos
      //hasta un maximo de maxNIntentosDeRevivirNodos veces y en caso de no
      //haber nodos disponibles luego de los intentos lanza una excepción
      //Llena particionRango con los rangos que haya que calcular
      procedure particionarRangoEstrellas(conjuntoDeEjecucion: TConjuntoDeEjecucion{of TDatosNodoOptDisV2});
      function crearListaTareasActRestoDarPasoYCalcularRango(conjuntoDeEjecucion: TConjuntoDeEjecucion{of TDatosNodoOptDisV2}): TList{of TFichaTarea};
      procedure procesarActRestoDarPasoYCalcularRango(listaTareas: TList{of TFichaTarea});

//      procedure darPasoFormulario;

      //Guarda en un archivo de logs el tiempo que le llevo resolver el problema
      procedure logTimes;
{$IFDEF MEDIR_TIEMPOS}
      procedure logSubTimes;
{$ENDIF}

      //Retorna la lista de archivos que tienen que estar disponibles para la
      //ejecución de la sala. Crea un archivo de sala modificado con los paths
      //de los archivos modificados para que arranquen de dirbase
      //El archivo de sala modificado se agrega a la lista resultado en el primer
      //lugar
      function procesarPaths(archiSala: String): TStringList;
  end;

implementation

uses
  uOrquestadorV2Main;

Constructor TRobotOrquestador.Create(archiSala: String; despachador: TDespachador);
var
  i: Integer;
  idsNodos: TDAofNCardinal;
begin
  inherited Create(true);
  self.despachador:= despachador;
  self.archiSala:= archiSala;
  self.FreeOnTerminate:= false;

  writeln;
  Writeln('Finalizar Ejecuciones: ' + uMsgsOptDisV2.AppName);
  despachador.finalizarEjecuciones(uMsgsOptDisV2.AppName);
  QueryPerformanceFrequency(frec);
  invFrec:= 1 / frec;
  QueryPerformanceCounter(ticksIni);
  idsNodos:= despachador.idsNodos;
  writeln;
  writeln('Enviar archivos');
  transmitirArchivos(self.archiSala, idsNodos);
  writeln('Fin enviar archivos');
  writeln;
  writeln('Lanzar Optimizadores');
  despachador.lanzarEjecuciones(uMsgsOptDisV2.AppName);
  writeln('Cargar Sala: ' + self.archiSala);
  CargarSala(self.archiSalaMod);
  sala.inicializarOptimizacion(nil);

  conjuntoDeEjecucion:= TConjuntoDeEjecucion.Create;
  for i:= 0 to high(idsNodos) do
    conjuntoDeEjecucion.add(TDatosNodoOptDisV2.Create(idsNodos[i], sala.globs.NPasos, 0, sala.globs.CF.nEstrellasPorPuntoT -1));
  despachador.setDatosNodos(conjuntoDeEjecucion);

  particionRango:= TListaDeRangos.Create(0, sala.globs.CF.nEstrellasPorPuntoT -1);
  particionRangoPasoPasado:= TListaDeRangos.Create(0, sala.globs.CF.nEstrellasPorPuntoT -1);
  timeOutActRestoEstrellas:= sala.globs.CF.nEstrellasPorPuntoT * timeOutActRestoPEstrella;
{$IFDEF PARTICION_DINAMICA}
  invLargoTotal:= 1 / particionRango.largoRango;
  despachador.setTemporizador(TTemporizadorUniOperacionPromedioMovil.Create(conjuntoDeEjecucion, despachador, 6));
{$ENDIF}
{$IFDEF DEBUG_EJEC}
  AssignFile(fOrquestadorV2, getDir_Bin + 'OrquestadorV2.xlt');
  Rewrite(fOrquestadorV2);
{$ENDIF}
  self.Resume;
end;

procedure TRobotOrquestador.Execute;
begin
  optimizar;
end;

procedure TRobotOrquestador.Optimizar;
var
  i: Integer;
  listaTareas: TList;
begin
  writeln;
  writeln('Optimizar');
{$IFDEF WINDOWS}
  OrquestadorV2.PBOpt.Min:= 0;
  OrquestadorV2.PBOpt.Position:= OrquestadorV2.PBOpt.Min;
  OrquestadorV2.PBOpt.Step:= 1;
  OrquestadorV2.PBOpt.Max:= sala.globs.NPasos;
{$ENDIF}

  listaTareas:= crearListaTareasCargarEInicializarSala(conjuntoDeEjecucion);
  despachador.forkAndJoin(listaTareas, timeOutCargarEInicializarSala * conjuntoDeEjecucion.Count);
  procesarCargarEInicializarSala(listaTareas);

{$IFDEF MEDIR_TIEMPOS}  
  r:= 'Paso';
  for i:= 0 to conjuntoDeEjecucion.Count - 1 do
    r:= r + #9 + 'IdNodo' + #9 + 'Velocidad[pesoRel/s]' + #9 + 'pesoRel' + #9 + 'tEjec[ms] + tCom[ms]';
  SetLength(rs, conjuntoDeEjecucion.Count);
  lineasTEjecs:= TStringList.Create;
  lineasTEjecs.Capacity:= sala.globs.NPasos;
  lineasTEjecs.Add(r);
{$ENDIF}  

  try
    while (sala.globs.kPaso_ > 0) and (not sala.globs.abortarSim) do
    begin
      chequearPorNodosNoSincronizados;

      for i:= 0 to particionRango.Count - 1 do
        particionRangoPasoPasado.add(particionRango[i]);
      particionarRangoEstrellas(conjuntoDeEjecucion);
      listaTareas:= crearListaTareasActRestoDarPasoYCalcularRango(conjuntoDeEjecucion);
      despachador.forkAndJoin(listaTareas, (timeOutCalcEstrellas + timeOutActRestoEstrellas) * 3 * conjuntoDeEjecucion.Count);
      procesarActRestoDarPasoYCalcularRango(listaTareas);
      QueryPerformanceCounter(ticksFin);

{$IFDEF MEDIR_TIEMPOS}
      r:= IntToStr(sala.globs.kPaso_);
      for i:= 0 to conjuntoDeEjecucion.Count - 1 do
        r:= r + rs[i];
      lineasTEjecs.Add(r);
{$ENDIF}

      sala.globs.Fijar_kPaso( sala.globs.kPaso_-1 );//sala.darPaso;
{$IFDEF WINDOWS}
      OrquestadorV2.PBOpt.StepIt;
      OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((ticksFin - ticksIni) * invFrec, fffixed, 8, 2);
{$ELSE}
      if sala.globs.kPaso_ mod 10 = 0 then
        writeln('Etapa: ', sala.globs.kPaso_,
                ' Tiempo Total: ', FloatToStrF((ticksFin - ticksIni) * invFrec, ffFixed, 8, 2), 'segs');
{$ENDIF}      
    end; // while del paso
    if not sala.globs.abortarSim then
    begin
      //sala.guardarResultadosOpt(sala.dirResultadosCorrida);
      sala.globs.EstadoDeLaSala:= CES_OPTIMIZACION_TERMINADA;
      QueryPerformanceCounter(ticksFin);      
{$IFDEF WINDOWS}
      OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((ticksFin - ticksIni) * invFrec, fffixed, 8, 2);
{$ELSE}
      writeln('Optimizacion Terminada. Tiempo Total: ', FloatToStrF((ticksFin - ticksIni) * invFrec, ffFixed, 8, 2), 'segs');
{$ENDIF}
      logTimes;
    end
    else
      sala.globs.EstadoDeLaSala:= CES_OPTIMIZACION_ABORTADA;
  finally
{$IFDEF MEDIR_TIEMPOS}
    lineasTEjecs.SaveToFile(getDir_Bin + 'TiemposV2.xlt');
    lineasTEjecs.Free;
{$ENDIF}    

{$IFDEF WINDOWS}
    utilidades.habilitarControles(OrquestadorV2);
//    utilidades.llamarAtencion(OrquestadorV2);
{$ENDIF}
  end;
  //Borro el archivo auxiliar creado
  SysUtils.DeleteFile(archiSalaMod);

  despachador.finalizarEjecuciones(uMsgsOptDisV2.AppName);
  particionRangoPasoPasado.Free;
end;

procedure TRobotOrquestador.Free;
var
  i: Integer;
begin
  sala.Free;
  particionRango.Free;
  for i:= 0 to conjuntoDeEjecucion.Count - 1 do
    TDatosNodo(conjuntoDeEjecucion[i]).Free;
  conjuntoDeEjecucion.Free;
{$IFDEF DEBUG_EJEC}
  CloseFile(fOrquestadorV2);
{$ENDIF}
  inherited Free;
end;

function TRobotOrquestador.crearListaTareasCargarEInicializarSala(conjuntoDeEjecucion: TConjuntoDeEjecucion{of TDatosNodoOptDisV2}): TList{of TFichaTarea};
var
  iNodo: Integer;
  datosNodo: TDatosNodoOptDisV2;
  paramsEntrada: TBuffWriter;
  tarea: TFichaTarea;
  res: TList;
{$IFDEF DEBUG_EJEC}
  dbgLinea: String;
  debugReader: TBuffReader;
  dbgArchiSala: String;
  idbg: Integer;
{$ENDIF}
begin
  res:= TList.Create;
{  if listaDeDatosNodos.nNodosDisponibles > 0 then
    pesoRel:= 1 / listaDeDatosNodos.nNodosDisponibles
  else
    raise Exception.Create('Error: el despachador se quedo sin nodos');}
  for iNodo:= 0 to conjuntoDeEjecucion.Count - 1 do
  begin
    datosNodo:= conjuntoDeEjecucion[iNodo];
    if datosNodo.disponible then
    begin
      paramsEntrada:= TBuffWriter.Create(xSizeOf(archiSalaMod));
      paramsEntrada.xString(archiSalaMod);
      tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_CARGAR_E_INICIALIZAR_SALA,
                                 paramsEntrada, 0, timeOutCargarEInicializarSala, False,
                                 datosNodo.idNodo, true);
      res.Add(tarea);
    end;
  end;
  result:= res;

{$IFDEF DEBUG_EJEC}
  writeln(fOrquestadorV2, 'Cargar e inicializar Sala:');

  for iNodo:= 0 to res.Count - 1 do
  begin
    tarea:= res[iNodo];
    debugReader:= TBuffReader.Create(tarea.parametrosEntrada.pBuff, tarea.comunicado.nBytesDatos);
    debugReader.xString(dbgArchiSala);
    debugReader.Free;

    dbgLinea:= IntToStr(tarea.comunicado.idTarea) + #9 +
               IntToStr(tarea.idNodoEjecutor) + #9 +
               dbgArchiSala;
    Writeln(fOrquestadorV2, dbgLinea);
  end;
{$ENDIF}
end;

procedure TRobotOrquestador.procesarCargarEInicializarSala(listaTareas: TList{of TFichaTarea});
var
  iTarea, codRespuesta, mbResult: Integer;
  tarea: TFichaTarea;
  msjError: String;
  msjErrorAsPChar: PAnsiChar;
{$IFDEF DEBUG_EJEC}
  dbgLinea: String;
{$ENDIF}
begin
{$IFDEF DEBUG_EJEC}
  for iTarea:= 0 to listaTareas.Count - 1 do
  begin
    tarea:= listaTareas[iTarea];
    dbgLinea:= IntToStr(tarea.comunicado.idTarea) + #9 +
               IntToStr(tarea.idNodoEjecutor);
    Writeln(fOrquestadorV2, dbgLinea);
  end;
  Writeln(fOrquestadorV2);
{$ENDIF}
  iTarea:= 0;
  while (iTarea < listaTareas.Count) and not sala.globs.abortarSim do
  begin
    tarea:= listaTareas[iTarea];
    tarea.resultados.xInteger(codRespuesta);
    if tarea.estado = Terminada then
    begin
      if codRespuesta <> 1 then
      begin
        tarea.resultados.xString(msjError);
        msjError:= 'No se pudo cargar la sala en el nodo ' +
                   conjuntoDeEjecucion.getDatosNodo(tarea.idNodoEjecutor).nombre + '.'#13 +
                   'El mensaje de error retornado fue: ' + #13 +
                   msjError + #13 +
                   '¿Desea continuar la ejecución sin este nodo?';
        GetMem(msjErrorAsPChar, length(msjError) + 1);
        StrPCopy(msjErrorAsPChar, msjError);
{$IFDEF WINDOWS}
        mbResult:= Application.MessageBox(msjErrorAsPChar, 'Error Remoto',
                                          MB_YESNO or MB_ICONEXCLAMATION);
{$ELSE}
        mbResult:= MessageBox(msjErrorAsPChar, 'Error Remoto');
{$ENDIF}
        if mbResult = IDYES then
        begin
          despachador.banearNodoPermanentemente(tarea.idNodoEjecutor);
        end
        else if mbResult = IDNO then
        begin
          sala.globs.abortarSim:= true;
        end;
        FreeMem(msjErrorAsPChar, length(msjError) + 1);
      end;
    end
    else
    begin
      msjError:= 'El nodo ' +
                 conjuntoDeEjecucion.getDatosNodo(tarea.idNodoEjecutor).nombre +
                 ' no respondió al intentar cargar la sala.'#13 +
                 '¿Desea continuar la ejecución sin este nodo?';
      GetMem(msjErrorAsPChar, length(msjError) + 1);
      StrPCopy(msjErrorAsPChar, msjError);
{$IFDEF WINDOWS}
        mbResult:= Application.MessageBox(msjErrorAsPChar, 'Error Remoto',
                                          MB_YESNO or MB_ICONEXCLAMATION);
{$ELSE}
        mbResult:= MessageBox(msjErrorAsPChar, 'Error Remoto');
{$ENDIF}
  		if mbResult = IDYES then
      begin
        despachador.banearNodoPermanentemente(tarea.idNodoEjecutor);
			end
		  else if mbResult = IDNO then
      begin
        sala.globs.abortarSim:= true;
			end;
      FreeMem(msjErrorAsPChar, length(msjError) + 1);
    end;
    tarea.Free;
    iTarea:= iTarea + 1;
  end;
  listaTareas.Free;
end;

procedure TRobotOrquestador.chequearPorNodosNoSincronizados;
var
  i: Integer;
  nNodosNoSincronizados: Integer;
  idsNodosNoSincronizados: TDAofNCardinal;

  glma: TDAofNInt;
  glinext, glinextp: Integer;

  parametrosEntrada: TBuffWriter;
  listaTareasSetEstadoMadreUniforme: TList;
begin
  SetLength(idsNodosNoSincronizados, conjuntoDeEjecucion.Count);
  nNodosNoSincronizados:= 0;
  for i:= 0 to conjuntoDeEjecucion.Count - 1 do
  begin
    if TDatosNodoOptDisV2(conjuntoDeEjecucion[i]).pasoEnElQueSeEncuentra <> sala.globs.kPaso_ + 1 then
    begin
      idsNodosNoSincronizados[nNodosNoSincronizados]:= TDatosNodoOptDisV2(conjuntoDeEjecucion[i]).idNodo;
      nNodosNoSincronizados:= nNodosNoSincronizados + 1;
    end;
  end;

  if nNodosNoSincronizados > 0 then
  begin
    if nNodosNoSincronizados <> conjuntoDeEjecucion.Count then
      idsNodosNoSincronizados:= copy(idsNodosNoSincronizados, 0, nNodosNoSincronizados - 1);

    getEstadoMadreUniforme(glma, glinext, glinextp);

    listaTareasSetEstadoMadreUniforme:= TList.Create;
    for i:= 0 to nNodosNoSincronizados - 1 do
    begin
      parametrosEntrada:= TBuffWriter.Create(xSizeOf(glma) + xSizeOf(glinext) + xSizeOf(glinextp));
      parametrosEntrada.xTDAOfNInt(glma);
      parametrosEntrada.xInteger(glinext);
      parametrosEntrada.xInteger(glinextp);

      listaTareasSetEstadoMadreUniforme.Add(TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_SET_ESTADO_MADRE_UNIFORME,
                                            parametrosEntrada, 0, timeOutSetEstadoFuenteMadreUniforme, false, idsNodosNoSincronizados[i], true));
    end;
    despachador.forkAndJoin(listaTareasSetEstadoMadreUniforme, timeOutSetEstadoFuenteMadreUniforme * nNodosNoSincronizados);

    for i:= 0 to listaTareasSetEstadoMadreUniforme.Count - 1 do
    begin
      //Ver que se hace en caso de no haber podido realizar la tarea.
      //En principio no hacer nada está bien, se le aumenta el contador de veces
      //consecutivas que no repsondió al nodo y si no respondió muchas veces se
      //lo banea permanentemente

      TFichaTarea(listaTareasSetEstadoMadreUniforme[i]).Free;
    end;
    listaTareasSetEstadoMadreUniforme.Free;
  end
end;

procedure TRobotOrquestador.getEstadoMadreUniforme(var glma: TDAofNInt; glinext: Integer; glinextp: Integer);
var
  tarea: TFichaTarea;
begin
  tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_GET_ESTADO_MADRE_UNIFORME,
                             NIL, 0, timeOutSetEstadoFuenteMadreUniforme, true, despachador.idNodoLocal, false);
  despachador.forkAndJoin(tarea);

  if tarea.estado = Terminada then
  begin
    tarea.resultados.xTDAOfNInt(glma);
    tarea.resultados.xInteger(glinext);
    tarea.resultados.xInteger(glinextp);
  end
  else
    raise Exception.Create('TRobotOrquestador.getEstadoMadreUniforme: no se pudo ejecutar la tarea');
  tarea.Free;
end;

{$IFDEF PARTICION_DINAMICA}
procedure TRobotOrquestador.particionarRangoEstrellas(conjuntoDeEjecucion: TConjuntoDeEjecucion);
var
  i, nNodosDisponibles: Integer;
  estrellaIni, estrellaFin, n0: Integer;
  nIntentosDeRevivirNodos: Integer;
  viSobreV0: TDAOfNreal;
  sumViSobreV0, invV0: NReal;
begin
{ni = n0 * (1 / v0) / (1 / vi) = n0 * vi / v0
 n0 = nEstrellas / (1 + v1/v0 + v2/v0 + ... vk/v0)}
  particionRango.Count:= 0;
  SetLength(viSobrev0, conjuntoDeEjecucion.Count);
  nIntentosDeRevivirNodos:= 0;
  repeat
    nNodosDisponibles:= 0;
    sumViSobreV0:= 1;
    for i:= 0 to conjuntoDeEjecucion.Count - 1 do
    begin
      if TDatosNodoOptDisV2(conjuntoDeEjecucion[i]).disponible {and
         (TDatosNodoOptDisV2(listaDeDatosNodos[i]).pasoEnElQueSeEncuentra = sala.globs.kPaso_)} then
      begin
        nNodosDisponibles:= nNodosDisponibles + 1;
        if nNodosDisponibles = 1 then
        begin
          invV0:= 1 / TDatosNodoOptDisV2(conjuntoDeEjecucion[i]).velocidad;
          viSobreV0[nNodosDisponibles - 1]:= 1;
        end
        else
        begin
          viSobreV0[nNodosDisponibles - 1]:= TDatosNodoOptDisV2(conjuntoDeEjecucion[i]).velocidad * invV0;
          sumViSobreV0:= sumViSobreV0 + viSobreV0[nNodosDisponibles - 1];
        end;
      end;
    end;
    if nNodosDisponibles = 0 then
    begin
      if nIntentosDeRevivirNodos = maxNIntentosDeRevivirNodos then
        raise Exception.Create('Error: el despachador se quedo sin nodos')
      else
      begin
        sleep(100);
        despachador.checkearEstadoNodos(uMsgsOptDisV2.AppName);
        nIntentosDeRevivirNodos:= nIntentosDeRevivirNodos + 1;
        logError('TRobotOrquestador.particionarRangoEstrellas: El despachador se quedo sin nodos. Intento revivir');
      end;
    end;
  until nNodosDisponibles > 0;

  timeOutCalcEstrellas:= trunc((timeOutCalcEstrellasPEstrella * sala.globs.CF.nEstrellasPorPuntoT * sala.globs.NCronicasOpt * despachador.factorDeAumentoEnTOsPorMultiplesNodos) / nNodosDisponibles);

  estrellaIni:= 0;
  estrellaFin:= trunc(sala.globs.CF.nEstrellasPorPuntoT / sumViSobreV0) - 1;
  particionRango.Add(TRango.Create(estrellaIni, estrellaFin));
  n0:= TRango(particionRango[0]).largoRango;
  for i:= 1 to nNodosDisponibles - 2 do
  begin
    estrellaIni:= estrellaFin + 1;
    estrellaFin:= estrellaIni + trunc(n0 * viSobreV0[i]);
    particionRango.Add(TRango.Create(estrellaIni, estrellaFin));
  end;
  if nNodosDisponibles > 1 then
  begin
    estrellaIni:= estrellaFin + 1;
    estrellaFin:= sala.globs.CF.nEstrellasPorPuntoT - 1;
    particionRango.Add(TRango.Create(estrellaIni, estrellaFin));
  end;
end;
{$ELSE}
procedure TRobotOrquestador.particionarRangoEstrellas(listaDeDatosNodos: TConjuntoDeEjecucion);
var
  i, nNodosDisponibles: Integer;
  nEstrellasPorNodo, nNodosConUnaEstrellaMas: Integer;
  estrellaIni, estrellaFin: Integer;
  nIntentosDeRevivirNodos: Integer;
begin
  particionRango.Count:= 0;
  nIntentosDeRevivirNodos:= 0;
  nNodosDisponibles:= 0;
  repeat
    for i:= 0 to listaDeDatosNodos.Count - 1 do
    begin
      if TDatosNodoOptDisV2(listaDeDatosNodos[i]).disponible {and
         (TDatosNodoOptDisV2(listaDeDatosNodos[i]).pasoEnElQueSeEncuentra = sala.globs.kPaso_)} then
        nNodosDisponibles:= nNodosDisponibles + 1
    end;
    if nNodosDisponibles = 0 then
    begin
      if nIntentosDeRevivirNodos = maxNIntentosDeRevivirNodos then
        raise Exception.Create('Error: el despachador se quedo sin nodos')
      else
      begin
        sleep(100);
        despachador.checkearEstadoNodos(uMsgsOptDisV2.AppName);
        nIntentosDeRevivirNodos:= nIntentosDeRevivirNodos + 1;
        logError('TRobotOrquestador.particionarRangoEstrellas: El despachador se quedo sin nodos. Intento revivir');
      end;
    end;
  until nNodosDisponibles > 0;

  timeOutCalcEstrellas:= trunc((timeOutCalcEstrellasPEstrella * sala.globs.CF.nEstrellasPorPuntoT * sala.globs.NCronicasOpt * despachador.factorDeAumentoEnTOsPorMultiplesNodos) / nNodosDisponibles);
  nEstrellasPorNodo:= sala.globs.CF.nEstrellasPorPuntoT div nNodosDisponibles;
  nNodosConUnaEstrellaMas:= sala.globs.CF.nEstrellasPorPuntoT mod nNodosDisponibles;

  for i:= 0 to nNodosConUnaEstrellaMas - 1 do
  begin
    estrellaIni:= i * (nEstrellasPorNodo + 1);
    estrellaFin:= estrellaIni + nEstrellasPorNodo;
    particionRango.Add(TRango.Create(estrellaIni, estrellaFin));
  end;

  for i:= nNodosConUnaEstrellaMas to nNodosDisponibles - 1 do
  begin
    estrellaIni:= (i * nEstrellasPorNodo) + nNodosConUnaEstrellaMas;
    estrellaFin:= estrellaIni + nEstrellasPorNodo - 1;
    particionRango.add(TRango.Create(estrellaIni, estrellaFin));
  end;
end;
{$ENDIF}

function TRobotOrquestador.crearListaTareasActRestoDarPasoYCalcularRango(conjuntoDeEjecucion: TConjuntoDeEjecucion{of TDatosNodoOptDisV2}): TList{of TFichaTarea};
var
  iParticion, iNodo, iRango: Integer;
  datosNodo: TDatosNodoOptDisV2;
  rangosAActualizar: TListaDeRangos;
  rangoACalcular: TRango;
  nRangos: Integer;
  tamBuf: Cardinal;
//  pesoRel: NReal;
  paramsEntrada: TBuffWriter;
  tarea: TFichaTarea;
  res: TList;

{$IFDEF DEBUG_EJEC}
  dbgLinea: String;
  debugReader: TBuffReader;
  dbgEstIni, dbgEstFin: Integer;
  dbgNRangos: Integer;
  dbgEstrellasIni: TDAofNInt;
  dbgRangos: TDAOfDAOfNReal;
  idbg: Integer;
{$ENDIF}
begin
  res:= TList.Create;
{  if listaDeDatosNodos.nNodosDisponibles > 0 then
    pesoRel:= 1 / listaDeDatosNodos.nNodosDisponibles
  else
    raise Exception.Create('Error: el despachador se quedo sin nodos');}
  iParticion:= 0;
  for iNodo:= 0 to conjuntoDeEjecucion.Count - 1 do
  begin
    datosNodo:= conjuntoDeEjecucion[iNodo];
    if datosNodo.disponible then
    begin
      rangoACalcular:= particionRango[iParticion];

      rangosAActualizar:= datosNodo.rangosCalculados.complemento;
      nRangos:= rangosAActualizar.Count;
      tamBuf:= xSizeOf(nRangos) + xSizeOf(rangoACalcular.minValor) + xSizeOf(rangoACalcular.maxValor);
      for iRango:= 0 to rangosAActualizar.Count - 1 do
      begin
        tamBuf:= tamBuf + xSizeOf(TRango(rangosAActualizar[iRango]).minValor)
                        + xCompressedSizeOf(sala.globs.CF.constelacion.fCosto[sala.globs.kPaso_ + 1],
                                            TRango(rangosAActualizar[iRango]).minValor,
                                            TRango(rangosAActualizar[iRango]).maxValor, uMsgsOptDisV2.tipoCompresionReales);
      end;
      paramsEntrada:= TBuffWriter.Create(tamBuf);
      paramsEntrada.xInteger(nRangos);
      for iRango:= 0 to nRangos - 1 do
      begin
        paramsEntrada.xInteger(TRango(rangosAActualizar[iRango]).minValor);
        paramsEntrada.xCompressedTDAOfNReal(sala.globs.CF.constelacion.fCosto[sala.globs.kPaso_ + 1],
                                            TRango(rangosAActualizar[iRango]).minValor,
                                            TRango(rangosAActualizar[iRango]).maxValor, uMsgsOptDisV2.tipoCompresionReales);
      end;
      paramsEntrada.xInteger(rangoACalcular.minValor);
      paramsEntrada.xInteger(rangoACalcular.maxValor);

      tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_LLENAR_RANGO_DAR_PASO_Y_CALCULAR_RANGO,
                                 paramsEntrada, sala.globs.kpaso_ + 1, timeOutCalcEstrellas + timeOutActRestoEstrellas, true,
                                 datosNodo.idNodo, false);
{$IFDEF PARTICION_DINAMICA}
      tarea.setPesoRelativo(rangoACalcular.largoRango * invLargoTotal, true);
{$ENDIF}
      res.Add(tarea);
      rangosAActualizar.Free;
      iParticion:= iParticion + 1;
    end;
  end;
  result:= res;

{$IFDEF DEBUG_EJEC}
  writeln(fOrquestadorV2, 'Paso a calcular: ' +  IntToStr(sala.globs.kpaso_ - 1));

  for iNodo:= 0 to res.Count - 1 do
  begin
    tarea:= res[iNodo];
    debugReader:= TBuffReader.Create(tarea.parametrosEntrada.pBuff, tarea.comunicado.nBytesDatos);
    debugReader.xInteger(dbgNRangos);
    SetLength(dbgEstrellasIni, dbgNRangos);
    SetLength(dbgRangos, dbgNRangos);
    for idbg:= 0 to dbgNRangos - 1 do
    begin
      debugReader.xInteger(dbgEstrellasIni[idbg]);
      debugReader.xCompressedTDAOfNReal(dbgRangos[idbg], uMsgsOptDisV2.tipoCompresionReales);
    end;
    debugReader.xInteger(dbgEstIni);
    debugReader.xInteger(dbgEstFin);
    debugReader.Free;

    dbgLinea:= IntToStr(tarea.comunicado.idTarea) + #9 +
               IntToStr(tarea.idNodoEjecutor) + #9 +
               IntToStr(dbgEstIni) + #9 +
               IntToStr(dbgEstFin) + #9 +
               IntToStr(dbgNRangos) + #9;
    for idbg:= 0 to dbgNRangos - 1 do
    begin
      dbgLinea:= dbgLinea + 'estrellasIni[' + IntToStr(idbg) + ']= '#9 +
                 IntToStr(dbgEstrellasIni[idbg]) + #9 +
                 uAuxiliares.TDAOfNRealToString(dbgRangos[idbg]) + #9;
    end;
    Writeln(fOrquestadorV2, dbgLinea);
  end;
{$ENDIF}
end;

procedure TRobotOrquestador.procesarActRestoDarPasoYCalcularRango(listaTareas: TList{of TFichaTarea});
var
  iTarea, iNodo, iRango, codRespuesta: Integer;
  tarea: TFichaTarea;
  rango: TRango;
  datosNodo: TDatosNodoOptDisV2;
{$IFDEF DEBUG_EJEC}
  dbgLinea: String;
{$ENDIF}
begin
{$IFDEF DEBUG_EJEC}
  for iTarea:= 0 to listaTareas.Count - 1 do
  begin
    tarea:= listaTareas[iTarea];
    dbgLinea:= IntToStr(tarea.comunicado.idTarea) + #9 +
               IntToStr(tarea.idNodoEjecutor);
    Writeln(fOrquestadorV2, dbgLinea);
  end;
  Writeln(fOrquestadorV2);
{$ENDIF}

  for iNodo:= 0 to conjuntoDeEjecucion.Count - 1 do
    TDatosNodoOptDisV2(conjuntoDeEjecucion[iNodo]).rangosCalculados.Count:= 0;

  for iTarea:= 0 to listaTareas.Count - 1 do
  begin
    tarea:= listaTareas[iTarea];
    rango:= particionRango[iTarea];
    iNodo:= conjuntoDeEjecucion.getINodo(tarea.idNodoEjecutor);
    datosNodo:= conjuntoDeEjecucion[iNodo];
    tarea.resultados.xInteger(codRespuesta);
    tarea.resultados.xInteger(datosNodo.pasoEnElQueSeEncuentra);
    tarea.resultados.xCompressedTDAOfNReal(sala.globs.CF.constelacion.fCosto[sala.globs.kPaso_], rango.minValor, uMsgsOptDisV2.tipoCompresionReales);
    datosNodo.rangosCalculados.add(rango);

{$IFDEF MEDIR_TIEMPOS}
    rs[iNodo]:= #9 + IntToStr(tarea.idNodoEjecutor) + #9 +
                FloatToStr(TDatosNodo(conjuntoDeEjecucion[iNodo]).velocidad) + #9 +
                FloatToStr(tarea.pesoRelativo) + #9 +
                FloatToStrF(tarea.tiempoEjec + tarea.tiempoCom, ffFixed, 8, 5);
{$ENDIF}                

    tarea.Free;
  end;

  for iRango:= 0 to particionRangoPasoPasado.Count - 1 do
    TRango(particionRangoPasoPasado[iRango]).Free;
  particionRangoPasoPasado.Count:= 0;

  listaTareas.Free;
end;

procedure TRobotOrquestador.CargarSala(archiSala: String);
begin
	if Sala <> nil then
  begin
		Sala.Free;
    sala:= NIL;
  end;

  Writeln('Cargando Sala: ' + archiSala);
  try
    sala:= TSalaDeJuego.cargarSala(archiSala, true);
    sala.setDirCorrida(archiSala);

  except
    On E: Exception do
    begin
      Writeln('Error cargando la Sala: ' + E.Message);
      logError('Error cargando la Sala: ' + E.Message);
      if sala <> NIL then
        sala.Free;
      exit;
    end;
  end;
end;

procedure TRobotOrquestador.transmitirArchivos(archiSala: String; idsNodos: TDAofNCardinal);
var
  archivos : TStringList;
  handler: TFileHandler;
begin
  despachador.finalizarEjecuciones(uMsgsFileManager.AppName);
  despachador.lanzarEjecuciones(uMsgsFileManager.AppName);

  archivos:= procesarPaths(archiSala);
  self.archiSalaMod:= archivos[0];

  handler:= TFileHandler.Create;
  handler.enviarArchivosSiNoExisten(idsNodos, archivos);
  handler.Free;
  archivos.Free;

  despachador.finalizarEjecuciones(uMsgsFileManager.AppName);
end;

{procedure TRobotOrquestador.darPasoFormulario;
begin
  OrquestadorV2.PBOpt.StepIt;
  OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((GetTickCount - tIniOpt) * (1/1000), fffixed, 8, 2);
end;}

procedure TRobotOrquestador.logTimes;
var
  i: Integer;
  f: TextFile;
  archLogTimes: String;
  linea: String;
begin
  archLogTimes:= uconstantes_nettopos.dirbase + DirectorySeparator + 'archlogs' + barraDir +
                 'OptDisV2Times-' + IntToStr(conjuntoDeEjecucion.Count) + 'Nodos.xlt';
  if not FileExists(archLogTimes) then
  begin
    AssignFile(f, archLogTimes);
    Rewrite(f);
    write(f, 'Sala', #9, 'Tiempo[secs]');
    for i:= 1 to conjuntoDeEjecucion.Count do
      write(f, #9, 'Nodo' + IntToStr(i));
    writeln(f);
  end
  else
  begin
    AssignFile(f, archLogTimes);
    Append(f);
  end;
  write(f, archiSala, #9, IntToStr(round((ticksFin - ticksIni) * invFrec)));
  linea:= '';
  for i:= 0 to conjuntoDeEjecucion.Count - 1 do
    linea:= linea + #9 + TDatosNodo(conjuntoDeEjecucion[i]).toLinea;
  Writeln(f, linea);

  CloseFile(f);
end;

{$IFDEF MEDIR_TIEMPOS}
procedure TRobotOrquestador.logSubTimes;
const
  baseArchLogTimes = uconstantes.dirbase + DirectorySeparator + 'archlogs' + barraDir + 'OptDisV2SubTimes-';
var
  f: TextFile;
  archLogTimes: String;
  sum: Cardinal;
begin
  archLogTimes:= baseArchLogTimes + IntToStr(conjuntoDeEjecucion.Count) + 'Nodos.xlt';
  if not FileExists(archLogTimes) then
  begin
    AssignFile(f, archLogTimes);
    Rewrite(f);
    writeln(f, 'Sala', #9,
           'tParticionarRangoEstrellas[p.u.]', #9,
           'tCrearListaTareasCalcularRangos[p.u.]', #9,
           'tCalcularRangos[p.u.]', #9,
           'tProcesarCalcularRangos[p.u.]', #9,
           'tCrearListaTareasActRestoYDarPaso[p.u.]', #9,
           'tActRestoYDarPaso[p.u.]', #9,
           'tProcesarDarPaso[p.u.]', #9,
           'tTotal[secs]', #9,
           'tActualizarVelocidades[p.u.]');
  end
  else
  begin
    AssignFile(f, archLogTimes);
    Append(f);
  end;
  sum:= tParticionarRangoEstrellas + tCrearListaTareasCalcularRangos + tCalcularRangos +
        tProcesarCalcularRangos + tCrearListaTareasActRestoYDarPaso + tActRestoYDarPaso + tProcesarDarPaso;
{$IFDEF PARTICION_DINAMICA}
  sum:= sum + tActualizarVelocidades;
{$ENDIF}
  write(f, archiSala, #9,
          tParticionarRangoEstrellas / sum, #9,
          tCrearListaTareasCalcularRangos / sum, #9,
          tCalcularRangos / sum, #9,
          tProcesarCalcularRangos / sum, #9,
          tCrearListaTareasActRestoYDarPaso / sum, #9,
          tActRestoYDarPaso / sum, #9,
          tProcesarDarPaso / sum, #9,
          sum / 1000);
{$IFDEF PARTICION_DINAMICA}
  writeln(f, #9, tActualizarVelocidades / sum);
{$ELSE}
  writeln(f);
{$ENDIF}
  CloseFile(f);
end;
{$ENDIF}

function TRobotOrquestador.procesarPaths(archiSala: String): TStringList;
var
  f, fMod: TextFile;
  res: TStringList;
  posIgual, posPuntoYComa, posPunto: Integer;
  linea, pathPosible: String;
  fileName: String;
begin
  fileName:= ExtractFileName(archiSala);
  posPunto:= pos('.', fileName);
  if posPunto <> 0 then
    fileName:= Copy(fileName, 1, posPunto -1);
  archiSalaMod:= ExtractFilePath(archiSala) + fileName + '_Netopos' + ExtractFileExt(archiSala);
  res:= TStringList.Create;
  res.Add(archiSalaMod);
  AssignFile(f, archiSala);
  AssignFile(fMod, archiSalaMod);
  try
    Reset(f);
    Rewrite(fMod);
    while not Eof(f) do
    begin
      Readln(f, linea);
      posIgual:= pos('= ', linea);
      if posIgual <> 0 then
      begin
        posPuntoYComa:= Pos(';', linea);
        pathPosible:= quitarRaiz(copy(linea, posIgual + 2, posPuntoYComa - 2 - posIgual));
        if FileExists(pathPosible) then
        begin
          if res.IndexOf(pathPosible) = -1 then
            res.Add(pathPosible);
          toNetoposPath(pathPosible);
          Writeln(fMod, copy(linea, 1, posIgual + 1) + pathPosible + ';');
        end
        else
          writeln(fMod, linea);
      end
      else
        Writeln(fMod, linea);
    end;
  finally
    CloseFile(f);
    CloseFile(fMod);
  end;
  result:= res;
end;

{procedure TRobotOrquestador.optimizar;
var
  listaTareas: TList;
  i: Integer;
begin
{$IFDEF MEDIR_TIEMPOS}
{  tAux:= 0; tParticionarRangoEstrellas:= 0; tCrearListaTareasCalcularRangos:= 0;
  tCalcularRangos:= 0; tProcesarCalcularRangos:= 0; tCrearListaTareasActRestoYDarPaso:= 0;
  tActRestoYDarPaso:= 0; tProcesarDarPaso:= 0;
{$IFDEF PARTICION_DINAMICA}
{  tActualizarVelocidades:= 0;
{$ENDIF}
{{$ENDIF}
{
  r:= 'Paso';
  for i:= 0 to conjuntoDeEjecucion.Count - 1 do
    r:= r + #9 + 'IdNodo' + #9 + 'Velocidad[pesoRel/s]' + #9 + 'pesoRel' + #9 + 'tEjec + tCom[ms]';
  SetLength(rs, conjuntoDeEjecucion.Count);
  lineasTEjecs:= TStringList.Create;
  lineasTEjecs.Capacity:= sala.globs.NPasos;
  lineasTEjecs.Add(r);

  self.initFormulario;
  try
    while (sala.globs.kPaso_ > 0) and (not sala.globs.abortarSim) do
    begin
{$IFDEF MEDIR_TIEMPOS}
{      tAux:= GetTickCount;
{$ENDIF}
{      particionarRangoEstrellas(conjuntoDeEjecucion);
{$IFDEF MEDIR_TIEMPOS}
{      tParticionarRangoEstrellas:= tParticionarRangoEstrellas + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{      listaTareas:= crearListaTareasCalcRango;
{$IFDEF MEDIR_TIEMPOS}
{      tCrearListaTareasCalcularRangos:= tCrearListaTareasCalcularRangos + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{      despachador.forkAndJoin(listaTareas, timeOutCalcEstrellas * 2 * conjuntoDeEjecucion.Count);
{$IFDEF MEDIR_TIEMPOS}
{      tCalcularRangos:= tCalcularRangos + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{      procesarCalcEstrellas(listaTareas);
{$IFDEF MEDIR_TIEMPOS}
{      tProcesarCalcularRangos:= tProcesarCalcularRangos + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{{$IFDEF PARTICION_DINAMICA}
{      despachador.temporizador.actualizarVelocidades;
{$IFDEF MEDIR_TIEMPOS}
{      tActualizarVelocidades:= tActualizarVelocidades + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{{$ENDIF}
{      listaTareas:= crearListaTareasActRestoYDarPaso(conjuntoDeEjecucion);
{$IFDEF MEDIR_TIEMPOS}
{      tCrearListaTareasActRestoYDarPaso:= tCrearListaTareasActRestoYDarPaso + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{      despachador.forkAndJoin(listaTareas, timeOutActResto * 2 * conjuntoDeEjecucion.Count);
{$IFDEF MEDIR_TIEMPOS}
{      tActRestoYDarPaso:= tActRestoYDarPaso + GetTickCount - tAux;
      tAux:= GetTickCount;
{$ENDIF}
{      procesarDarPaso(listaTareas);
{$IFDEF MEDIR_TIEMPOS}
{      tProcesarDarPaso:= tProcesarDarPaso + GetTickCount - tAux;
{$ENDIF}
{
  //    PostMessage(handle, uMsgsOrquestadorV2.MSGP_DAR_PASO, 0, 0);
  //    synchronize(self.darPasoFormulario);
      OrquestadorV2.PBOpt.StepIt;
      OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((GetTickCount - tIniOpt) * (1/1000), fffixed, 8, 2);

      r:= IntToStr(sala.globs.kPaso_);
      for i:= 0 to conjuntoDeEjecucion.Count - 1 do
        r:= r + rs[i];
      lineasTEjecs.Add(r);

      //Hago un sorteo para que se cambie la semilla
      fddp.random;
      semillaAleatoria:= fddp.semillaAleatoria;
    end; // while del paso
    sala.guardarResultadosOpt(uconstantes.dirbase);
    OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((GetTickCount - tIniOpt) * (1/1000), fffixed, 8, 2);
    logTimes;

{$IFDEF MEDIR_TIEMPOS}
{    logSubTimes;
{$ENDIF}
{  finally
    lineasTEjecs.SaveToFile('TiemposV2.xlt');
    lineasTEjecs.Free;

    despachador.finalizarEjecuciones(uMsgsOptDisV2.AppName);
    utilidades.habilitarControles(OrquestadorV2);
    utilidades.llamarAtencion(OrquestadorV2);
  end;
end;}

{function TRobotOrquestador.crearListaTareasCalcRango: TList;
var
  tarea: TFichaTarea;
  iParticion: Integer;
  rango: TRango;
  paramsEntrada: TBuffWriter;
  res: TList;
begin
  res:= TList.Create;
  res.Capacity:= particionRango.Count;
  for iParticion:= 0 to particionRango.Count - 1 do
  begin
    rango:= particionRango[iParticion];
    paramsEntrada:= TBuffWriter.Create(xSizeOf(semillaAleatoria) + xSizeOf(rango.minValor) + xSizeOf(rango.maxValor));
    paramsEntrada.xInteger(semillaAleatoria);
    paramsEntrada.xInteger(rango.minValor);
    paramsEntrada.xInteger(rango.maxValor);
    tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_CALCULAR_RANGO_ESTRELLAS,
                               paramsEntrada, sala.globs.kPaso_, timeOutCalcEstrellas, true);
{$IFDEF PARTICION_DINAMICA}
{    tarea.setPesoRelativo(rango.largoRango * invLargoTotal, true);
{$ENDIF}
{    res.Add(tarea);
  end;
  result:= res;
end;

procedure TRobotOrquestador.procesarCalcEstrellas(listaTareas: TList);
var
  iTarea, iNodo: Integer;
  tarea: TFichaTarea;
  rango: TRango;
  valores: TDAOfNReal;
  datosNodo: TDatosNodoOptDisV2;
begin
  for iNodo:= 0 to conjuntoDeEjecuciony.Count - 1 do
    TDatosNodoOptDisV2(conjuntoDeEjecucion[iNodo]).rangosCalculados.Count:= 0;

  for iTarea:= 0 to listaTareas.Count - 1 do
  begin
    tarea:= listaTareas[iTarea];
    rango:= particionRango[iTarea];
    tarea.resultados.xCompressedTDAOfNReal(valores);
    sala.llenarRangoEstrellas(rango.minValor, valores, sala.globs.kPaso_);

    iNodo:= conjuntoDeEjecucion.getINodo(tarea.idNodoEjecutor);

    datosNodo:= conjuntoDeEjecucion[iNodo];
    datosNodo.rangosCalculados.add(rango);

    rs[iNodo]:= #9 + IntToStr(tarea.idNodoEjecutor) + #9 +
                FloatToStr(TDatosNodo(conjuntoDeEjecucion[iNodo]).velocidad) + #9 +
                FloatToStr(tarea.pesoRelativo) + #9 +
                FloatToStrF(tarea.tiempoEjec + tarea.tiempoCom, ffFixed, 8, 5);
    tarea.Free;
  end;
  listaTareas.Free;
end;

function TRobotOrquestador.crearListaTareasActRestoYDarPaso(listaDeDatosNodos: TConjuntoDeEjecucion): TList;
var
  iNodo, iRango: Integer;
  datosNodo: TDatosNodoOptDisV2;
  rangosAActualizar: TListaDeRangos;
  nRangos: Integer;
  estrellasIni: TDAofNInt;
  valores: TDAOfDAOfNReal;
  tamBuf: Cardinal;
//  pesoRel: NReal;
  paramsEntrada: TBuffWriter;
  tarea: TFichaTarea;
  res: TList;
begin
  res:= TList.Create;
  if listaDeDatosNodos.nNodosDisponibles > 0 then
    pesoRel:= 1 / listaDeDatosNodos.nNodosDisponibles
  else
    raise Exception.Create('Error: el despachador se quedo sin nodos');

  for iNodo:= 0 to listaDeDatosNodos.Count - 1 do
  begin
    datosNodo:= listaDeDatosNodos[iNodo];
    if datosNodo.disponible then
    begin
      rangosAActualizar:= datosNodo.rangosCalculados.complemento;
      nRangos:= rangosAActualizar.Count;
      tamBuf:= xSizeOf(nRangos);
      SetLength(estrellasIni, nRangos);
      SetLength(valores, nRangos);
      for iRango:= 0 to rangosAActualizar.Count - 1 do
      begin
        estrellasIni[iRango]:= TRango(rangosAActualizar[iRango]).minValor;
        valores[iRango]:= sala.getRangoEstrellas(TRango(rangosAActualizar[iRango]).minValor, TRango(rangosAActualizar[iRango]).maxValor, sala.globs.kPaso_);
        tamBuf:= tamBuf + xSizeOf(estrellasIni[iRango]) + xCompressedSizeOf(valores[iRango]);
      end;
      paramsEntrada:= TBuffWriter.Create(tamBuf);
      paramsEntrada.xInteger(nRangos);
      for iRango:= 0 to nRangos - 1 do
      begin
        paramsEntrada.xInteger(estrellasIni[iRango]);
        paramsEntrada.xCompressedTDAOfNReal(valores[iRango]);
      end;
      tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_LLENAR_RANGOS_Y_DAR_PASO,
                                 paramsEntrada, sala.globs.kpaso_, timeOutActResto, false,
                                 datosNodo.idNodo);
      res.Add(tarea);
      rangosAActualizar.Free;
    end;
  end;
  result:= res;
end;

procedure TRobotOrquestador.procesarDarPaso(listaTareas: TList);
var
  iTarea, iRango: Integer;
  tarea: TFichaTarea;
  datosNodo: TDatosNodoOptDisV2;
  codRespuesta: Integer;
begin
  for iTarea:= 0 to listaTareas.Count - 1 do
  begin
    tarea:= listaTareas[iTarea];
    if tarea.estado = Terminada then
    begin
      datosNodo:= TDatosNodoOptDisV2(conjuntoDeEjecucion.getDatosNodo(tarea.idNodoEjecutor));
      tarea.resultados.xInteger(codRespuesta);
      if codRespuesta = 1 then
        tarea.resultados.xInteger(datosNodo.pasoEnElQueSeEncuentra);
      tarea.Free;
    end;
  end;

  for iRango:= 0 to particionRango.Count - 1 do
    TRango(particionRango[iRango]).Free;
  listaTareas.Free;
end;}

(*procedure TRobotOrquestador.medirk2k3yt;
const
  nIteraciones = 40;
  nMedidas = 10;
var
  estrellaIni, estrellaFin: Integer;
  i, j: Integer;
  tamBuf: Cardinal;
//  pesoRel: NReal;
  paramsEntrada: TBuffWriter;
  tarea: TFichaTarea;
  res: TList;

  sal: TextFile;

  tEjecSobreTTotalMedio: System.Extended;
  frec64: Int64;
  ticksIni, ticksFin: Int64;
  idNodoLocal: Cardinal;
procedure transferir(idNodo: Cardinal);
var
  k: Integer;
  datosNodo: TDatosNodoOptDisV2;
begin
  datosNodo:= TDatosNodoOptDisV2(conjuntoDeEjecucion.getDatosNodo(idNodo));
  if datosNodo.disponible then
  begin
    tamBuf:= xSizeOf(semillaAleatoria) + xSizeOf(estrellaIni) + xSizeOf(estrellaFin);
    paramsEntrada:= TBuffWriter.Create(tamBuf);
    paramsEntrada.xInteger(semillaAleatoria);
    paramsEntrada.xInteger(estrellaIni);
    paramsEntrada.xInteger(estrellaFin);

    tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_CALCULAR_RANGO_ESTRELLAS,
                               paramsEntrada, sala.globs.kpaso_, timeOutCalcEstrellas, true,
                               datosNodo.idNodo, true);
    res.Add(tarea);
  end;

  despachador.forkAndJoin(res, timeOutCalcEstrellas * conjuntoDeEjecucion.Count);

  for k:= 0 to res.Count - 1 do
  begin
    tarea:= res[k];
//    writeln('tCom= ', tarea.tiempoCom:6:3, ', tEjec= ', tarea.tiempoEjec:6:3, ', tTotal= ', tarea.tiempoCom + tarea.tiempoEjec:6:3);
    tEjecSobreTTotalMedio:= ((tEjecSobreTTotalMedio * (j - 1)) + tarea.tiempoEjec / (tarea.tiempoCom + tarea.tiempoEjec)) / j;
    tarea.free;
  end;
  res.Count:= 0;

  OrquestadorV2.PBOpt.StepIt;
  OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((ticksFin - ticksIni) * invFrec, fffixed, 8, 2);
end;
begin
  idNodoLocal:= getIdNodoLocal;
  OrquestadorV2.PBOpt.Min:= 0;
  OrquestadorV2.PBOpt.Position:= OrquestadorV2.PBOpt.Min;
  OrquestadorV2.PBOpt.Step:= 1;
  OrquestadorV2.PBOpt.Max:= nMedidas * 2 * nIteraciones;

  AssignFile(sal, 'salk2k3yt.txt');
  Rewrite(sal);
  writeln(sal, 'NEstrellas', #9, 'tComLocal[s]', #9, 'tEjecLocal[s]', #9, 'tComRemoto[s]', #9, 'tEjecRemoto[s]');


{$IFNDEF WINDOWS}
  timeLib.QueryPerformanceFrequency(frec64);
{$ELSE}
  Windows.QueryPerformanceFrequency(frec64);
{$ENDIF}
  res:= TList.Create;
  for i:= 1 to nMedidas do
  begin
{    for iNodo:= 0 to conjuntoDeEjecucion.Count - 1 do
    begin
      datosNodo:= conjuntoDeEjecucion[iNodo];
      if datosNodo.disponible then
      begin
        estrellaIni:= 0;
        nrangos:= 1;
        tamBuf:= xSizeOf(nRangos) + xSizeOf(estrellaIni) + xCompressedSizeOf(valores);
        paramsEntrada:= TBuffWriter.Create(tamBuf);
        paramsEntrada.xInteger(nrangos);
        paramsEntrada.xInteger(estrellaIni);
        paramsEntrada.xCompressedTDAOfNReal(valores);

        tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_LLENAR_RANGOS_Y_DAR_PASO,
                                   paramsEntrada, sala.globs.kpaso_, timeOutCalcEstrellas, false,
                                   datosNodo.idNodo);
        res.Add(tarea);
      end;
    end;}

    estrellaIni:= 0;
    estrellaFin:= round(sala.globs.CF.nEstrellasPorPuntoT * (i / nMedidas));

    writeln(i);
    timeOutCalcEstrellas:= timeOutCalcEstrellasPEstrella * (estrellaFin) * sala.globs.NCronicasOpt + timeOutComsCte;

    tEjecSobreTTotalMedio:= 0;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksIni);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksIni);
{$ENDIF}
    for j:= 1 to nIteraciones do
    begin
      transferir(idNodoLocal);
    end;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksFin);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksFin);
{$ENDIF}
//    writeln('tEjecSobreTTotalMedio= ', tEjecSobreTTotalMedio:6:3);
    Writeln(sal, estrellaFin + 1, #9, ((ticksFin - ticksIni) / (frec64 * nIteraciones)) * (1 - tEjecSobreTTotalMedio), #9, ((ticksFin - ticksIni) / (frec64 * nIteraciones)) * tEjecSobreTTotalMedio, #9);

    tEjecSobreTTotalMedio:= 0;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksIni);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksIni);
{$ENDIF}
    for j:= 1 to nIteraciones do
    begin
      transferir(3232235549);
    end;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksFin);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksFin);
{$ENDIF}
    Writeln(sal, ((ticksFin - ticksIni) / (frec64 * nIteraciones)) * (1 - tEjecSobreTTotalMedio), #9, ((ticksFin - ticksIni) / (frec64 * nIteraciones)) * tEjecSobreTTotalMedio);

    sleep(20);
  end;
  res.Free;
//  despachador.finalizarEjecuciones(uMsgsOptDisV2.AppName);
  utilidades.habilitarControles(OrquestadorV2);
  CloseFile(sal);
end;

procedure TRobotOrquestador.medirCk4yCLk5;
const
  maxNEstrellas = 10000;
  nIteraciones = 100;

  tams : array [0..18] of Integer = (2, 10, 20, 30, 40, 50, 60, 80, 100, 200, 300,
                                     400, 500, 600, 800, 1024, 10 * 1024,
                                     100 * 1024, 1024 * 1024);
var
  i, j, nrangos, estrellaIni: Integer;
  valores: TDAOfNReal;
  tamBuf: Cardinal;
//  pesoRel: NReal;
  paramsEntrada: TBuffWriter;
  tarea: TFichaTarea;
  res: TList;

  sal: TextFile;

  frec64: Int64;
  ticksIni, ticksFin: Int64;
  idNodoLocal: Cardinal;
procedure transferir(idNodo: Cardinal);
var
  k: Integer;
  datosNodo: TDatosNodoOptDisV2;
begin
  datosNodo:= TDatosNodoOptDisV2(conjuntoDeEjecucion.getDatosNodo(idNodo));
  if datosNodo.disponible then
  begin
    estrellaIni:= 0;
    nrangos:= 1;
    tamBuf:= xSizeOf(nRangos) + xSizeOf(estrellaIni) + xCompressedSizeOf(valores);
    paramsEntrada:= TBuffWriter.Create(tamBuf);
    paramsEntrada.xInteger(nrangos);
    paramsEntrada.xInteger(estrellaIni);
    paramsEntrada.xCompressedTDAOfNReal(valores);

    tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_LLENAR_RANGOS_Y_DAR_PASO,
                               paramsEntrada, sala.globs.kpaso_, timeOutCalcEstrellas, true,
                               datosNodo.idNodo, true);
    res.Add(tarea);
  end;

  despachador.forkAndJoin(res, timeOutCalcEstrellas * conjuntoDeEjecucion.Count);

  for k:= 0 to res.Count - 1 do
  begin
    tarea:= res[k];
//    writeln('tCom= ', tarea.tiempoCom:6:3, ', tEjec= ', tarea.tiempoEjec:6:3, ', tTotal= ', tarea.tiempoCom + tarea.tiempoEjec:6:3);
    tarea.free;
  end;
  res.Count:= 0;

  OrquestadorV2.PBOpt.StepIt;
  OrquestadorV2.ETiempoTotal.Text:= FloatToStrF((ticksFin - ticksIni) * invFrec, fffixed, 8, 2);
end;
begin
  idNodoLocal:= getIdNodoLocal;
  OrquestadorV2.PBOpt.Min:= 0;
  OrquestadorV2.PBOpt.Position:= OrquestadorV2.PBOpt.Min;
  OrquestadorV2.PBOpt.Step:= 1;
  OrquestadorV2.PBOpt.Max:= length(tams) * nIteraciones * 2;

  AssignFile(sal, 'salCk4yCLk5.txt');
  Rewrite(sal);
  writeln(sal, 'NEstrellas', #9, 'tActualizarLocal[s]', #9, 'tActualizarRemoto[s]');

{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceFrequency(frec64);
{$ELSE}
    Windows.QueryPerformanceFrequency(frec64);
{$ENDIF}
  res:= TList.Create;
  for i:= low(tams) to high(tams) do
  begin
    writeln(tams[i], 'Bytes');
    SetLength(valores, tams[i]);
    for j:= 0 to high(valores) do
      valores[j]:= System.random;
    timeOutCalcEstrellas:= trunc((timeOutComsPorByte * tams[i] + timeOutComsCte) * 2);
{    for iNodo:= 0 to conjuntoDeEjecucion.Count - 1 do
    begin
      datosNodo:= conjuntoDeEjecucion[iNodo];
      if datosNodo.disponible then
      begin
        estrellaIni:= 0;
        nrangos:= 1;
        tamBuf:= xSizeOf(nRangos) + xSizeOf(estrellaIni) + xCompressedSizeOf(valores);
        paramsEntrada:= TBuffWriter.Create(tamBuf);
        paramsEntrada.xInteger(nrangos);
        paramsEntrada.xInteger(estrellaIni);
        paramsEntrada.xCompressedTDAOfNReal(valores);

        tarea:= TFichaTarea.Create(uMsgsOptDisV2.AppName, uMsgsOptDisV2.MSGP_LLENAR_RANGOS_Y_DAR_PASO,
                                   paramsEntrada, sala.globs.kpaso_, timeOutCalcEstrellas, false,
                                   datosNodo.idNodo);
        res.Add(tarea);
      end;
    end;}

{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksIni);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksIni);
{$ENDIF}
    for j:= 1 to nIteraciones do
    begin
      transferir(idNodoLocal);
    end;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksFin);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksFin);
{$ENDIF}
    Write(sal, tams[i], #9, (ticksFin - ticksIni) / (frec64 * nIteraciones), #9);

{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksIni);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksIni);
{$ENDIF}
    for j:= 1 to nIteraciones do
    begin
      transferir(3232235549);
    end;
{$IFNDEF WINDOWS}
    timeLib.QueryPerformanceCounter(ticksFin);
{$ELSE}
    Windows.QueryPerformanceCounter(ticksFin);
{$ENDIF}
    Writeln(sal, (ticksFin - ticksIni) / (frec64 * nIteraciones));

    SetLength(valores, 0);

    sleep(20);
  end;
  res.Free;
//  despachador.finalizarEjecuciones(uMsgsOptDisV2.AppName);
  utilidades.habilitarControles(OrquestadorV2);
  CloseFile(sal);
end;*)


end.
