unit uConectorProblema;


{$mode delphi}

interface

uses

  Classes, SysUtils, dateutils,
{$IFDEF LINUX}
   uwinmsgs, Unix, BaseUnix, pthreads,
{$ELSE}
   Controls, Graphics, Dialogs,
   windows, Forms, FileUtil,
{$ENDIF}

  unettopos, uglobsharedmem, unettopostypes, uconstantes_nettopos,
  uTareas,
  uFuncionesAuxiliares, uConstantesConector,
  xmatdefs, uConstantesSimSEE, uDataSetGenerico, uMsgRetardados, urosx,
  uNodosDeCalculo;

const

  //Cantidad de Wk minimo para comenzar la resolucion
  nWksParaEmpezar=1;
  MAX_FALLOS_NODO = 1;

type

  arrAplicaiones = array of TFichaAplicacion;

  TEstadosProblema = (ED_iniciando_, ED_esperandoWks_, ED_esperandoTareas_, ED_procesando_,
                          ED_sincronizandoPaso_, ED_sincronizandoCompleto_,
                          ED_finalizado_, ED_abortado_);


  //nid | usuario | clave | sala | dt_creacion | activa | avance | nSecsToEnd |
  //dt_actualizacion | tipo | estado | master | maxWks | descripcion
  //TProblemasCltr Problemas Cluster
  TProblemasCltr=class

    _nid: integer;
    _usuario, _llave: string;
    _dt_creacion: string;
    _dt_actualizacion: string;
    _activa: Integer;
    _avance: Double;
    _nSecsToEnd: Integer;
    _descripcion:string;
    _tipo: string;
    _estado: Integer;
    _maxWks: integer;
    _master:Integer;
    _sala: string;

    constructor Create(nid: integer; usuario, llave: string; dt_creacion: string;
                       dt_actualizacion: string; activa: Integer; avance: Double;
                       nSecsToEnd: Integer; descripcion:string; tipo: string; estado: Integer;
                       maxWks: integer; master:Integer; sala:string);

    //Estados del problema:
    // -2 : Problema Cancelado
    // -1 : Problema abortado
    //  0 : Problema sin Master
    //  1 : Problema con Master
    //  2 : Problema en progreso
    //  3 : Problema finalizado

    function getEstado:Boolean;
    function setEstado (activa: Integer; avance: Double; nSecsToEnd: Integer;
                        maxWks: integer):Integer;

    function setAvance (avance: NReal): Boolean;
    function setNSecsToEnd (nSecs: NReal): Boolean;

end;

  { TConectorProblema }
{$IFDEF LINUX}
   TConectorProblema = class(TComponent)
{$ELSE}
  TConectorProblema = class(TForm)

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

{$ENDIF}
   public

    mi_nid:Integer;
    miNombre:string;
//    idNodoLocal: Cardinal;
//    idTopoLocal: Cardinal;
    capataz: Boolean;

    registradoEnNettopos: Boolean;

    problema: TProblemasCltr;

    soyMaster:Boolean;
    master:TNodoCalculo;

    estado: TEstadosProblema;

    hayNuevosNodos: Boolean;

    //todos los nodos activos
    nodosActivos: TListaDeNodosCalculo;
    //nodos nuevos
    nodosNuevos: TListaDeNodosCalculo;
    //nodos caidos
    nodosOffLine: TListaDeNodosCalculo;

    //Obtener problema activo del tipoProproblema de la base de datos.
    function getProblema(tipoProblema: string):TProblemasCltr;

    //Registra a la aplicación para jugar en la distribucion de la solucion
    //del problema
    function registrarJugador: Boolean;

    //intento postularme como master del juego
    //actualiza el master del problema si este es igual a 0.
    function postularmeMaster:Boolean;

    //Obtengo el master del problema de la base de datos.
    function getMasterFromProblema: TNodoCalculo;

    //Seteo el master en el servidor.
    function setProblemaMaster:Boolean;

    //El problema queda sim master asignado
    function resetProblemaMaster:Boolean;

    //Desregistra la aplicacion
    function desRegistrarJugador:Boolean;

   private

     msgSalirDespachado: Boolean;
     procedure cambiarEstado(e: TEstadosProblema);

   public

    { public declarations }
     {$IFDEF LINUX}
      recolectar:Boolean;
      SActionRec_: SigActionRec;

    {$ENDIF}

    constructor Create (nombre: string; tipoProblema: string);

    //Esta funcion se ejecuta si hay un problema para resolver
    function ingresarAlJuego:Boolean;

    //inicializa nettopos para volver a la aplicacion candidata a recibir mensajes
    //para evitar nombres duplicados el nombre se registra como nombre+id
    //se guarda el string en la variable miNombre para facil acceso
    //Ademas intenta lanzar un topo en caso de que no haya u obtiene el id del
    //topo local
    function iniciarNettopos (nombreAplic: PChar; idAplic: cardinal):Boolean;

    procedure notificarSalidaAlTopoLocal;
    function levantarNotificarcion (idCom: Integer):TNodoCalculo;
    procedure finalizarNettopos;

    //Obtiene de la base de datos los topos participes del problema
    function getToposProblema: arrAplicaiones;

    procedure Salir;
    procedure Free;


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

    procedure iniciarResolucion;virtual;
    procedure resolverPaso;virtual;
    procedure sincronizarPaso;virtual;
    procedure sincronizarCompleto;virtual;
    procedure reset;virtual;

    //Envia un mensaje a todos los topos del problema para que bajen los workers
    //y luego se bajen los propios topos
    procedure suspenderProceso;

    //Levanta el mensaje y busca en "lstTareas" la tarea correspondiente
    //si no encuentra busca el nodo que contesto la tarea en lstNodos
    function levantarMensajesTareas (idCom: Integer; lstTareas: TListaDeTareas;
                                     lstNodos:TListaDeNodosCalculo;
                                     var nodoRemitente: TNodoCalculo;
                                     var t:TTarea): Boolean;


    //si hay nodos nuevos los agrega a la lista de nodos activos
    //a los nodos con nFallos> MAX_FALLOS_NODO los mueve a nodosOffLine
    //true si modifico algo
    function sincronizarNodos: Boolean;

    procedure msgp_wkDisponible (var Msg: TMessage); message MSGP_NOTIFICAR_WK_ON;
    procedure msgp_wkNoDisponible (var Msg: TMessage); message MSGP_NOTIFICAR_WK_OFF;

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

    procedure msgp_TareasSinResolver (var Msg: TMessage); message MSGP_NOTIFICAR_TAREAS_SIN_RESOLVER;

    //indica al master que el wk esta disponible.
    procedure notificarMaster_WkOn;

    //indica al master que el wk NO esta disponible.
    procedure notificarMaster_WkOff;


  end;

var
  ConectorProblema: TConectorProblema;

implementation
{$IFDEF LINUX}
var
  SActionRec: SigActionRec;

{ Procedimientos agregados para el manejo de señales
del sistema.}
procedure Handler(Sig : Integer); cdecl;
begin
  case Sig of
    SIGALRM:
    begin
      writeln('SIGALRM');
      msgsRetardados.Start;
      exit;
    end;
    SIGINT{, SIGTERM} :
    begin
      writeln('--- recibi SIGINT o SIGTERM ---' );
      conectorProblema.recolectar:= false;
      exit;
    end;
    //  SIGIO: writeln('SIGIO');
  end; { case }
end;

procedure InstallHandlers;
begin
//  writeln('Instalo Handlers, ThreadId= ', GetThreadId);
  with SActionRec do
  begin
    sa_handler:= SigActionHandler(@Handler);
    fpsigemptyset(sa_mask);
    FpSigAddSet(sa_mask, SIGALRM);
    sa_flags:= 0;
  end; { with }
  fpsigaction(SIGINT, @SActionRec, nil);
  fpsigaction(SIGTERM, @SActionRec, nil);
  fpsigaction(SIGALRM, @SActionRec, nil);


end;
{$ENDIF}

{ TProblemasCltr }

constructor TProblemasCltr.Create(nid: integer; usuario, llave: string;
  dt_creacion: string; dt_actualizacion: string; activa: Integer;
  avance: Double; nSecsToEnd: Integer; descripcion: string; tipo: string;
  estado: Integer; maxWks: integer; master: Integer; sala: string);
begin

    _nid:= nid;
    _usuario:= usuario;
    _llave:= llave;
    _dt_creacion:= dt_creacion;
    _dt_actualizacion:= dt_actualizacion;
    _activa:= activa;
    _avance:=avance;
    _nSecsToEnd:=nSecsToEnd;
    _descripcion:= descripcion;
    _tipo:= tipo;
    _estado:=estado;
    _maxWks:=maxWks;
    _master:=master;
    _sala:=sala;

end;

function TProblemasCltr.getEstado: Boolean;
var

  ds: TResultadoQuery;
  rec: TDataRecord;

begin


 result:= true;
 ds:= sql_query('SELECT estado, activa, avance, nSecsToEnd, maxWks from clt_problemas WHERE nid='+IntToStr(self._nid) );
 rec := ds.next;
 if rec = nil then
   result:=false
 else
  begin
      self._estado := rec.GetByNameAsInt('estado');
      self._activa := rec.GetByNameAsInt('activa');
      self._avance := rec.GetByNameAsFloat('avance');
      self._nSecsToEnd := rec.GetByNameAsInt('nSecsToEnd');
      self._nSecsToEnd := rec.GetByNameAsInt('maxWks');
      result:=true;
  end;

 ds.Free;

end;

function TProblemasCltr.setEstado(activa: Integer; avance: Double;
  nSecsToEnd: Integer; maxWks: integer): Integer;
begin

end;

function TProblemasCltr.setAvance(avance: NReal): Boolean;
begin
   result:=sql_exec('UPDATE clt_problemas SET (avance, dt_actualizacion) VALUES ('+
                    FloatToStr(avance)+', now() ) WHERE nid='+IntToStr(_nid));

end;

function TProblemasCltr.setNSecsToEnd(nSecs: NReal): Boolean;
begin
  result:=sql_exec('UPDATE clt_problemas SET (nSecsToEnd, dt_actualizacion) VALUES ('+
                    FloatToStr(nSecs)+', now() ) WHERE nid='+IntToStr(_nid));

end;

{$R *.lfm}

{ TConectorProblema }

{$IFNDEF LINUX}
procedure TConectorProblema.FormCreate(Sender: TObject);
var
  p: TProblemasCltr;
begin

  miNombre:='conectorPrueba';
  problema := getProblema('OPT');
  if (problema <> nil) then
   begin
     if not ingresarAlJuego then
       Salir;
   end
  else
    Salir;

end;

procedure TConectorProblema.FormDestroy(Sender: TObject);
begin
 self.Free;
end;
{$ENDIF}

function TConectorProblema.getProblema(tipoProblema: string): TProblemasCltr;
var
  ds: TResultadoQuery;
  rec: TDataRecord;
  p: TProblemasCltr;

  nid: Integer;
  activa: Integer;
  descripcion: String;
  tipo: String;
  estado: Integer;
  nid_packSala: integer;
  cnt_terminar: Integer;
  nid_master: Integer;
  nombreSala: String;

begin

 ds:= sql_query('SELECT * from clt_problemas WHERE activa=1 AND tipo='''+tipoProblema+''' LIMIT 1');
 rec := ds.next;
 if rec = nil then
  result:= nil
 else
  begin

     DateSeparator:='-';
     p := TProblemasCltr.Create (rec.GetByNameAsInt('nid'),
                                ' ', ' ',
                                rec.GetByNameAsString('dt_creacion'),
                                rec.GetByNameAsString('dt_actualizacion'),
                                rec.GetByNameAsInt('activa'),
                                rec.GetByNameAsFloat('avance'),
                                rec.GetByNameAsInt('nSecsToEnd'),
                                rec.GetByNameAsString('descripcion'),
                                rec.GetByNameAsString('tipo'),
                                rec.GetByNameAsInt('estado'),
                                rec.GetByNameAsInt('maxWks'),
                                rec.GetByNameAsInt('master'),
                                rec.GetByNameAsString('sala'));
     result:=p;
  end;

 ds.Free;

end;

function TConectorProblema.registrarJugador: Boolean;
var
 ds: TResultadoQuery;
 rec: TDataRecord;

begin

 result:=sql_exec('INSERT INTO clt_aplics_problema (nodo, topo, idaplic, nid_problema, '+
                  'nombre, estado, dt_creacion, dt_ultimo_contacto) VALUES '+
                  '('''+CardinalToIP4Str(pm^.idNodoLocal)+''','+IntToStr(pm^.idTopoLocal)+
                  ','+IntToStr(idAplicYo)+','+IntToStr(problema._nid)+
                  ','''+miNombre+''',1, now(), now() )');

 ds:= sql_query('SELECT nid FROM clt_aplics_problema WHERE nodo='''+CardinalToIP4Str(pm^.idNodoLocal)+
                ''' AND idaplic='+IntToStr(idAplicYo));
 rec := ds.next;
 if rec = nil then
  begin
   //irse
  end
 else
    mi_nid := rec.GetByNameAsInt('nid');

 ds.Free;

end;

function TConectorProblema.postularmeMaster:Boolean;
begin
 result:=sql_exec('UPDATE clt_problemas SET master='+IntToStr(mi_nid)+
                ' WHERE nid='+IntToStr(problema._nid)+' AND master=-1');
end;

function TConectorProblema.getMasterFromProblema: TNodoCalculo;
var
  ds: TResultadoQuery;
  rec: TDataRecord;
  nidMaster:Integer;
  nid: Integer;
  nodo: Cardinal;
  topo: Integer;
  idaplic: Integer;
  nid_problema_: Integer;
  nombre: String;
  estado_: Integer;
  soy_master: Boolean;
begin
 result := nil;
 ds:= sql_query('SELECT master FROM clt_problemas WHERE nid='+IntToStr(problema._nid));
 rec := ds.next;
 if rec = nil then
  begin
   result:= nil;
   exit;
  end
 else
   begin
    nidMaster:=rec.GetByNameAsInt('master');
    ds:= sql_query('SELECT * FROM clt_aplics_problema WHERE nid='+IntToStr(nidMaster));
    rec := ds.next;
    if rec = nil then
      begin
        resetProblemaMaster;
      end
    else
      begin
        nid:=rec.GetByNameAsInt('nid');
        nodo:= IP4StrToCardinal(rec.GetByNameAsString('nodo'));
        topo:= rec.GetByNameAsInt('topo');
        idaplic:= rec.GetByNameAsInt('idaplic');
        nid_problema_:= rec.GetByNameAsInt('nid_problema');
        nombre:= rec.GetByNameAsString('nombre');
        estado_:= rec.GetByNameAsInt('estado');
        if rec.GetByNameAsInt('estado') = 1 then
          soy_master:=True
        else
          soy_master:=False;

        Result:=TNodoCalculo.Crear(nid, nodo, topo, idaplic, nid_problema_,
                               soy_master, nombre, estado_);



      end;
   end;

 ds.Free;

end;

function TConectorProblema.setProblemaMaster: Boolean;
begin
 result:=sql_exec('UPDATE clt_aplics_problema SET soy_master=1 WHERE nid='+
                  IntToStr(mi_nid));
end;

function TConectorProblema.resetProblemaMaster: Boolean;
begin
 result:=sql_exec('UPDATE clt_problemas SET master=-1 WHERE nid='+
                  IntToStr(problema._nid));
end;

function TConectorProblema.desRegistrarJugador: Boolean;
begin
   result:=sql_exec('DELETE FROM clt_aplics_problema WHERE nid='+IntToStr(mi_nid));
end;

procedure TConectorProblema.cambiarEstado(e: TEstadosProblema);
begin
{$IFDEF DEBUG}
writeln('Cambiando estado de ',estado,' -> ',e );
{$ENDIF}
estado:= e;
end;

constructor TConectorProblema.Create(nombre: string; tipoProblema: string);
begin


  inherited Create(nil);
  msgSalirDespachado:=False;
  cambiarEstado(ED_iniciando_);
  capataz:=False;
  miNombre:=nombre;

  //Busco problema en la db, si no hay me voy
  problema := getProblema(tipoProblema);
  if (problema = nil) then
   begin
     //writeln('Fue Salir');
     Salir;
     Exit;
   end;
         writeln('Fue Salir');
   nodosActivos:= TListaDeNodosCalculo.Create;
   nodosNuevos := TListaDeNodosCalculo.Create;
   nodosOffLine:= TListaDeNodosCalculo.Create;
   hayNuevosNodos := False;
  if not ingresarAlJuego then
    begin
      Salir;
      Exit;
    end;

  //Manejo de señales de linux
  {$IFDEF LINUX}
    InstallHandlers;
    FpsigEmptySet(sigs);
    FpSigAddSet(sigs, SIGALRM);
    pthread_sigmask(SIG_SETMASK, @sigs, nil);
    recolectar:= true;
    msgsRetardados:= TMensajesRetardados.Create;
  {$ENDIF}

  if soyMaster then
   begin
    cambiarEstado(ED_esperandoWks_);
   end
  else
   begin
     cambiarEstado(ED_esperandoTareas_);
   end;
end;

procedure TConectorProblema.finalizarNettopos;
begin

  desregistrarAplicacion(idAplicYo);
  finalizar;

end;

function TConectorProblema.getToposProblema: arrAplicaiones;
var
  ds: TResultadoQuery;
  rec: TDataRecord;
  nTopos: Integer;
  topos: arrAplicaiones;
  i: Integer;
begin

 ds:= sql_query('SELECT DISTINCT topo FROM clt_aplics_problema where nid_problema='+IntToStr(self.problema._nid) );
 nTopos:=ds.nrows;
 rec := ds.next;
 if nTopos > 0 then
  begin
   SetLength(topos, nTopos);
   for i:= 0 to nTopos-1 do
    begin
      topos[i].idNodo := IP4StrToCardinal(rec.GetByNameAsString('nodo'));
      topos[i].idAplic := rec.GetByNameAsInt('topo');
      topos[i].nombreAplic := rec.GetByNameAsString('nombre');

      rec:= ds.next;
    end;
   ds.Free;
  end;

 Result := topos;

end;

procedure TConectorProblema.Salir;
begin
  if not msgSalirDespachado then
   begin
    msgSalirDespachado:=True;
    {$IFDEF LINUX}
       ColaDeMensajes.PostMessage(FpGetpid ,WM_CLOSE, 0, 0);
    {$ELSE}
       PostMessage(HANDLE, WM_CLOSE, 0, 0);
    {$ENDIF}
   end;
end;

procedure TConectorProblema.iniciarResolucion;
begin

end;

procedure TConectorProblema.resolverPaso;
begin
  cambiarEstado(ED_procesando_);

end;

procedure TConectorProblema.sincronizarPaso;
begin
  cambiarEstado(ED_sincronizandoPaso_);

end;

procedure TConectorProblema.sincronizarCompleto;
begin
  cambiarEstado(ED_sincronizandoCompleto_);

end;

procedure TConectorProblema.reset;
begin

end;

procedure TConectorProblema.suspenderProceso;
var
  arrTopos: arrAplicaiones;
  fc: TFichaComunicado;
  i: Integer;
begin

  arrTopos:= getToposProblema;
  fc.idNodoOrigen := pm^.idNodoLocal;
  fc.idOrigen:=idAplicYo;
  fc.codigoMsg:=WM_CLOSE;
  fc.idTarea:=0;
  fc.nBytesDatos:=0;

  for i := 0 to Length(arrTopos)-1 do
   begin
     fc.idNodoDestino:=arrTopos[i].idNodo;
     fc.idDestino:=arrTopos[i].idAplic;
     comunicar(@fc, nil, timeOutComsCte);
   end;

end;

procedure TConectorProblema.notificarMaster_WkOn;
begin
  master.notificar(MSGP_NOTIFICAR_WK_ON);
end;

function TConectorProblema.iniciarNettopos(nombreAplic: PChar; idAplic: cardinal
  ): Boolean;
var
  id:Cardinal;
  idTopoLocal: Cardinal;
begin

  miNombre:=nombreAplic+inttostr(idAplic);
  result:=false;
  if inicializar then
    if registrarAplicacion(PChar(miNombre), idAplic) > 0 then
       begin
        id:=getIdAplicacion(0, uconstantes_nettopos.AppName_Topo);
        writeln('A LANZAR TOPO, topoID: ', id);
        capataz:=getOLanzarTopoLocal(idTopoLocal);
        writeln('LANZO TOPO: ',capataz);
        if idTopoLocal > 0 then
         result:=true
        else
         begin
          desregistrarAplicacion(idAplic);
          finalizar;
         end;
       end
    else
       finalizar;
end;

procedure TConectorProblema.notificarSalidaAlTopoLocal;
var
   fc: TFichaComunicado;
begin

   fc.idNodoOrigen:=pm^.idNodoLocal;
   fc.idOrigen:=idAplicYo;
   fc.idNodoDestino:=pm^.idNodoLocal;
   fc.idDestino:=pm^.idTopoLocal;

   fc.codigoMsg:=MSGP_APLIC_CLOSING;

   fc.nBytesDatos:=0;

   comunicar(@fc, nil, timeOutComsCte);


end;

function TConectorProblema.levantarNotificarcion(idCom: Integer): TNodoCalculo;
var
  fc_e: TFichaComunicado;
  i: Integer;
  j:TNodoCalculo;
begin
  result := nil;
  if leerFichaComunicado (idCom, @fc_e) > 0 then
   if levantarDatosComunicado(idCom, nil, 0) > 0 then
      result := nodosActivos.Get(nodosActivos.GetIndexByIdAplic(fc_e.idNodoOrigen, fc_e.idOrigen));

end;

function TConectorProblema.ingresarAlJuego: Boolean;
var
 fc: TFichaComunicado;
begin

  result:=True;
  {$IFDEF LINUX}
     registradoEnNettopos := iniciarNettopos(pchar(miNombre), FpGetpid);
  {$ELSE}
     registradoEnNettopos := iniciarNettopos(pchar(miNombre), Handle);
  {$ENDIF}

  if registradoEnNettopos=False then
   begin
    result:=false;
    exit;
   end;

  if registrarJugador then
   begin
     if problema._master = -1 then //no hay master
       begin
         if postularmeMaster then //me postulo y espero a ver que pasa
           begin
            master := getMasterFromProblema;
            if master= nil then
             begin
              result:= false;
              exit;
             end;
            if ((master._idaplic = idAplicYo) AND (master._idnodo=pm^.idNodoLocal)) then
              begin
               soyMaster:=True;
               if not setProblemaMaster then
                 begin
                  result:= false;
                 end;
              end
            else
              soyMaster:=False;
            problema._master:= master._nid;
          end
         else
           begin
             result:= false;
           end;
       end
     else
       begin
         soyMaster:= False;
         master := getMasterFromProblema;
       end;
   end
  else
    begin
      result := false;
    end;
end;


procedure TConectorProblema.msgp_wkDisponible(var Msg: TMessage);
var
  fc_e: TFichaComunicado;
  j:TNodoCalculo;
begin

  if leerFichaComunicado (msg.wParam, @fc_e) > 0 then
   if levantarDatosComunicado(msg.wParam, nil, 0) > 0 then
    begin
      j := TNodoCalculo.createFromServer(fc_e.idNodoOrigen, fc_e.idOrigen);
      if j <> nil then
        begin
           nodosNuevos.Add(j);
           if ((estado=ED_esperandoWks_) AND (nodosNuevos.Count>=nWksParaEmpezar)) then
            begin
              iniciarResolucion;
              cambiarEstado(ED_procesando_);
            end;
        end
    end
   else
    Salir
  else
   Salir;

end;

procedure TConectorProblema.notificarMaster_WkOff;
begin
  master.notificar(MSGP_NOTIFICAR_WK_OFF);
end;

procedure TConectorProblema.msgp_wkNoDisponible(var Msg: TMessage);
var
  fc: TFichaComunicado;
  j:TNodoCalculo;
  i:Integer;
begin

  if leerFichaComunicado (msg.wParam, @fc) > 0 then
   if levantarDatosComunicado(msg.wParam, nil, 0) > 0 then
     begin
       i:= nodosActivos.GetIndexByIdAplic(fc.idNodoOrigen, fc.idOrigen);
       j:=nodosActivos.Get(i);
       nodosOffLine.Add(j);

       nodosActivos.Delete(i);

     end;

end;

procedure TConectorProblema.msgp_TareasSinResolver(var Msg: TMessage);
begin

end;

procedure TConectorProblema.Free;
begin

  desRegistrarJugador;

  if registradoEnNettopos then
   begin
     notificarSalidaAlTopoLocal;
     if not soyMaster then
       master.notificar(MSGP_NOTIFICAR_WK_OFF)
     else
       begin
         resetProblemaMaster;
         //VER
       end;
     finalizarNettopos;
   end;
end;

function TConectorProblema.levantarMensajesTareas(idCom: Integer;
  lstTareas: TListaDeTareas; lstNodos: TListaDeNodosCalculo;
  var nodoRemitente: TNodoCalculo; var t: TTarea): Boolean;
var

 fc:TFichaComunicado;
 iNodo: Integer;

begin

   result:=true;
   nodoRemitente:= nil;
   if leerFichaComunicado (idCom, @fc) > 0 then
    begin
      lstTareas.obtenerTarea(fc.idTarea, t);
      if ((t <> nil) or (t._resuelta=true))  then
       begin
         nodoRemitente:= t._nodo;
         if t.procesarEnLinea_ then
           begin
             t.bufferResultados_:=fc.pdatos;
             t.tambuffResultados_:=fc.nBytesDatos;
             t.procesarResultadosEnLinea;
             levantarDatosComunicado(idCom, nil, 0);
             t.bufferResultados_:=nil;
             t.tambuffResultados_:=0;
           end
         else
           begin
             if fc.nBytesDatos<>0 then
               levantarDatosComunicado(idCom, t.bufferResultados_, fc.nBytesDatos)
             else
               levantarDatosComunicado(idCom, nil, 0);
             t._resuelta:=true;
           end;
       end
      else
       begin
         result :=false;
         iNodo:=lstNodos.GetIndexByIdAplic(fc.idNodoOrigen, fc.idOrigen);
         if iNodo <> -1 then
           begin
             nodoRemitente := lstNodos.Get(iNodo);
             nodoRemitente.incFallos;
           end;
       end;
    end;
end;

function TConectorProblema.sincronizarNodos: Boolean;
var
  i: Integer;
  nodo: TNodoCalculo;
begin

   result := false;
   if nodosActivos.Count > 0 then
     for i := 0 to nodosActivos.Count -1 do
       if nodosActivos.Get(i).nFallos >= MAX_FALLOS_NODO then
        begin
          nodo := nodosActivos.Get(i);
          nodosOffLine.Add(nodo);
          nodo := nil;
          result:=true;
        end;
     nodosActivos.Pack;

   if nodosNuevos.Count > 0 then
     begin
       for i := 0 to nodosNuevos.Count -1 do
        nodosOffLine.Add(nodosActivos.Get(i));
       result:=true;
       nodosOffLine.Clear;
     end;

end;


end.
