{Copyright or © or Copr. :

CNRS - Laboratoire Mouvement & Perception
franck.buloup@univmed.fr

This software is a computer program whose purpose is to highly simplify  
the use of ADwinPro realtime systems when you develop experimental
protocols  on life's sciences area.

This software is governed by the CeCILL  license under French law and
abiding by the rules of distribution of free software.  You can  use, 
modify and/ or redistribute the software under the terms of the CeCILL
license as circulated by CEA, CNRS and INRIA at the following URL
"http://www.cecill.info". 

As a counterpart to the access to the source code and  rights to copy,
modify and redistribute granted by the license, users are provided only
with a limited warranty  and the software's author,  the holder of the
economic rights,  and the successive licensors  have only  limited
liability. 

In this respect, the user's attention is drawn to the risks associated
with loading,  using,  modifying and/or developing or reproducing the
software by the user in light of its specific status of free software,
that may mean  that it is complicated to manipulate,  and  that  also
therefore means  that it is reserved for developers  and  experienced
professionals having in-depth computer knowledge. Users are therefore
encouraged to load and test the software's suitability as regards their
requirements in conditions enabling the security of their systems and/or 
data to be ensured and,  more generally, to use and operate it in the 
same conditions as regards security. 

The fact that you are presently reading this means that you have had
knowledge of the CeCILL license and that you accept its terms.}
unit URecupGeneThread;

interface

uses Windows, Classes, SysUtils, Contnrs, XMLClasses, UGlobal, StimuliClass,
     DocoFastLineSeriesClass, Graphics, Dialogs, Winsock;

const WANTADWIN   = 0;
      ADWINLOCKED = 1;
      ADWINUNLOCK = 2;
      YES         = '1';
      NO          = '0';
      MAX_SIGNAL_POINTS = 1000000;

type
  TRecupGeneThread = class(TThread)
  private
    { Déclarations privées }
    real_time : double;
    numDataTrsfrtKPC : array of integer;
    numDataTrsfrtPCK : array of integer;
    nbDataTrsfrt     : array[1..80] of integer;
    numDataEnreg     : array[1..80] of boolean;
    numDataVisu      : array[1..80] of boolean;
    memoryBuffers    : array[0..80] of TMemoryStream;//Commence 0 pour gérer simplement
                                                     //les graphes temporel
    memoryBuffersStim: array[0..80] of TMemoryStream;//Pour le generation de signal
    saveFiles        : array[1..80] of TFileStream;
    signal           : array[1..MAX_SIGNAL_POINTS] of single;
    stopThread : boolean;

    //Calcul tps execution
    c1 : int64;
    c2 : int64;
    c1Read : int64;
    c2Read : int64;
    freq : int64;
    time : double;
    perfFile : TStringList;

    //fonctionnement en reseau
    CLientSocket : TSocket;
    ServerResponse : Char;
    Etat : integer;//WANTADWIN : demande de connection ADWin
                   //ADWINLOCKED : connection ADWin active
                   //ADWINUNLOCK : fin de connection ADWin

    indexNumSession : integer;

    procedure Recuperation;
    procedure Generation;
    procedure Affichage;
    procedure TestEndProcess;
    procedure ErrorExit(Msg : string);
    function ConnectUDP : TSocket;
  protected
    procedure Execute; override;
  public
    constructor Create(suspended : boolean; priorityLevel : TThreadPriority;
                       lstTsfrtKPC,lstTsfrtPCK : TStringList);
  end;

implementation

uses UExecute;

{ Important : les méthodes et propriétés des objets de la VCL peuvent uniquement
  être utilisés dans une méthode appelée en utilisant Synchronize, comme :

      Synchronize(UpdateCaption);

  où UpdateCaption serait de la forme 

    procedure TRecupGeneThread.UpdateCaption;
    begin
      Form1.Caption := 'Mis à jour dans un thread';
    end; }

{ TRecupGeneThread }

//fonctionnement en reseau
var CONNECT    : Char = 'C';
    ACTIVATE   : Char = 'A';
    LOCK       : Char = 'L';
    ENDLOCK    : Char = 'E';
    INACTIVATE : Char = 'I';
    DISCONNECT : Char = 'D';

procedure TRecupGeneThread.TestEndProcess;
begin
  if (ExecuteForm.ADwin.Process_Status(num_process) <> 1) then
  begin
    if (ExecuteForm.ADwin.Process_Status(num_process) = 0)
    then stopThread := true;
  end;
end;

procedure TRecupGeneThread.ErrorExit(Msg : string);
begin
  ExecuteForm.BootLabel.Caption := Msg;
  WSACleanup();
  stopThread := true;
  Terminate;
end;

function TRecupGeneThread.ConnectUDP : TSocket;
var sin : TSockAddrIn;
    s : TSocket;
    ppe : PProtoEnt;
    iMode : integer;
begin
  sin.sin_family := AF_INET;
  sin.sin_addr.S_addr := inet_addr(PAnsiChar(IPServeur));
  sin.sin_port := strtoint(Port);

  ppe := getprotobyname(PChar('UDP'));

  s := socket(PF_INET,SOCK_DGRAM,ppe.p_proto);
  if (s = INVALID_SOCKET)
  then ErrorExit('Impossible de créer le socket ' + inttostr(getLastError()));

  if (Winsock.connect(s,sin,sizeof(sin)) = SOCKET_ERROR)
  then ErrorExit('Impossible de connecter le socket ' + inttostr(getLastError()));

  //-------------------------
  // Set the socket I/O mode: In this case FIONBIO
  // enables or disables the blocking mode for the
  // socket based on the numerical value of iMode.
  // If iMode = 0, blocking is enabled;
  // If iMode != 0, non-blocking mode is enabled.
  iMode := 0;
  ioctlsocket(s, FIONBIO, iMode);

  Result := s;
end;

constructor TRecupGeneThread.Create(suspended : boolean; priorityLevel : TThreadPriority;
                                    lstTsfrtKPC,lstTsfrtPCK : TStringList);
var InfoCanaux : TInfoCanaux;
    i,j,k,index : integer;
    NomCanal,FileName : string;
    stimFileNames : TStringList;
    stimuli : TStimuli;
    stimulus : TStimulus;
    tmpSingle : single;
    WSVERS : word ;
    AWSAData : TWSAData;
    n : integer;
begin
  inherited Create(suspended);
  c1 := 0;

  QueryPerformanceFrequency(Freq);
  perfFile := TStringList.Create;
  perfFile.Add(DateTimeToStr(Now));
  perfFile.Add(' ');

  freeOnTerminate := true;
  stopThread := false;
  Self.Priority := priorityLevel;

  //Dim des tableaux dyn trsfrt PC K (stims) et K PC
  setLength(numDataTrsfrtKPC,lstTsfrtKPC.Count);
  setLength(numDataTrsfrtPCK,lstTsfrtPCK.Count);


  //init des "fichiers" memoires et physiques
  for i := 1 to 80 do
  begin
    memoryBuffers[i] := nil;
    memoryBuffersStim[i] := nil;
    saveFiles[i] := nil;
    nbDataTrsfrt[i] := -1;
  end;

  InfoCanaux := ExecuteForm.DocDOCOMETRE.RendreListeInfoCanaux;
  if ExecuteForm.mode = 'EXEC'
  then indexNumSession := ExecuteForm.sessionList[ExecuteForm.numSession];

  //pour ts les trsfrt K PC
  for i := 0 to lstTsfrtKPC.Count - 1 do
  begin

    //recup nom canal - num data trsfrt
    NomCanal := lstTsfrtKPC[i];
    index := InfoCanaux.Nom.IndexOf(NomCanal);
    numDataTrsfrtKPC[i] := strtoint(InfoCanaux.NumCanal[index]);

    //init tampon memoire de recup
    memoryBuffers[numDataTrsfrtKPC[i]] := TMemoryStream.Create;

    //Parcours des canaux en enregistrement
    for j := 0 to  ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXENREG.Count - 1 do
    begin
      if nomCanal = ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXENREG[j].NOMCANAL then
      begin
        //init tableau enreg
        numDataEnreg[numDataTrsfrtKPC[i]] := true;

        //Formation du nom de fichier de sauvegarde
        FileName := rep_manip;

        if ExecuteForm.mode = 'TEST'
        then FileName := FileName + 'TEST_' + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '\'
        else FileName := FileName + ExecuteForm.ASujet.NOM + '\';

        ForceDirectories(FileName);

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMCANAL = 1
        then FileName := FileName + NomCanal + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMST = 1
        then FileName := FileName + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMSESSION = 1 then
        begin
          if ExecuteForm.mode = 'TEST'
          then FileName := FileName + 'S0_'
          else FileName := FileName + ExecuteForm.ASujet.SESSIONS[indexNumSession].NOM + '_'
        end;

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMSESSION = 1
        then FileName := FileName + 'S' + inttostr(indexNumSession + 1) + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMESSAI = 1
        then FileName := FileName + 'E' + inttostr(ExecuteForm.numEssai + 1);

        if FileName[length(FileName)] = '_'
        then delete(FileName,length(FileName),1);

        FileName := FileName + '.sau';

        //init tableau fichier de sauvegarde
        DeleteFile(FileName);
        saveFiles[numDataTrsfrtKPC[i]] := TFileStream.Create(FileName,fmCreate);

      end;
    end;

    //Parcours des canaux en visualisation
    for j := 0 to  ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXVISU.Count - 1 do
    begin
      if nomCanal = ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXVISU[j].NOMCANAL
      then numDataVisu[numDataTrsfrtKPC[i]] := true;
    end;

  end;

  //pour ts les trsfrt PC K (stims)
  for i := 0 to lstTsfrtPCK.Count - 1 do
  begin

    //recup nom canal - num data trsfrt
    NomCanal := lstTsfrtPCK[i];
    index := InfoCanaux.Nom.IndexOf(NomCanal);
    numDataTrsfrtPCK[i] := strtoint(InfoCanaux.NumCanal[InfoCanaux.Nom.IndexOf(NomCanal)]);

    //recup nom fichiers stim
    stimFileNames := TStringList.Create;
    for j := 0 to ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].BLOC.Count - 1 
    do ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].BLOC[j].GetStimFileNames(stimFileNames);

    //pour ts les fichiers stim
    for j := 0 to stimFileNames.Count - 1 do
    begin
      if(FileExists(stimFileNames[j])) then
      begin
        stimuli := TSTimuli.Create(stimFileNames[j]);
        //si ce canal appartient à ce stim, init memorystream
        //lire les données, transtype en single pour keithley
        //init data fifo
        stimulus := stimuli.Has(NomCanal);
        if stimulus <> nil then
        begin
          memoryBuffersStim[numDataTrsfrtPCK[i]] := TMemoryStream.Create;
          for k := 0 to high(stimulus.Values) do
          begin
            tmpSingle := stimulus.GetValue;
            memoryBuffersStim[numDataTrsfrtPCK[i]].Write(tmpSingle,4);
          end;
          //Copie du stim entier ds memoryBuffers pour l'affichage
          memoryBuffers[numDataTrsfrtPCK[i]] := TMemoryStream.Create;
          memoryBuffers[numDataTrsfrtPCK[i]].CopyFrom(memoryBuffersStim[numDataTrsfrtPCK[i]],0);
          memoryBuffers[numDataTrsfrtPCK[i]].Position := 0;
          //Copie d'une partie du stim (taille tampon) ds le keithley
          memoryBuffersStim[numDataTrsfrtPCK[i]].Position := 0;
          memoryBuffersStim[numDataTrsfrtPCK[i]].read(signal[1],strtoint(InfoCanaux.TailleTampon[index])*4);
          ExecuteForm.ADwin.Fifo_Clear(numDataTrsfrtPCK[i]);
          ExecuteForm.ADwin.SetFifo_Float(numDataTrsfrtPCK[i],signal[1],strtoint(InfoCanaux.TailleTampon[index]));
        end;
        stimuli.FreeValues;
        stimuli.Free;
      end else
      begin
        showmessage('Le fichier de stimuli suivant : ' + stimFileNames[j] + ' n''existe pas !');
        Self.Terminate;
      end;
    end;
    
    stimFileNames.Free;

    //Parcours des canaux en enregistrement
    for j := 0 to  ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXENREG.Count - 1 do
    begin
      if nomCanal = ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXENREG[j].NOMCANAL then
      begin
        //init tableau enreg
        numDataEnreg[numDataTrsfrtPCK[i]] := true;

        //Formation du nom de fichier de sauvegarde
        FileName := rep_manip;

        if ExecuteForm.mode = 'TEST'
        then FileName := FileName + 'TEST_' + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '\'
        else FileName := FileName + ExecuteForm.ASujet.NOM + '\';

        ForceDirectories(FileName);

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMCANAL = 1
        then FileName := FileName + NomCanal + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMST = 1
        then FileName := FileName + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMSESSION = 1
        then FileName := FileName + ExecuteForm.ASujet.SESSIONS[indexNumSession].NOM + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMSESSION = 1
        then FileName := FileName + 'S' + inttostr(indexNumSession + 1) + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMESSAI = 1
        then FileName := FileName + 'E' + inttostr(ExecuteForm.numEssai + 1);

        if FileName[length(FileName)] = '_'
        then delete(FileName,length(FileName),1);

        FileName := FileName + '.sau';

        //init tableau fichier de sauvegarde
        DeleteFile(FileName);
        saveFiles[numDataTrsfrtPCK[i]] := TFileStream.Create(FileName,fmCreate);

      end;
    end;

    //Parcours des canaux en visualisation
    for j := 0 to  ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXVISU.Count - 1 do
    begin
      if nomCanal = ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].LISTECANAUXVISU[j].NOMCANAL
      then numDataVisu[numDataTrsfrtPCK[i]] := true;
    end;

  end;

  InfoCanaux.Free;

  //Fonctionnement en réseau
  if EnReseau then
  begin
    Etat := WANTADWIN;

    //Load winsock DLL
    WSVERS := MAKEWORD(2,2);
    if WSAStartup(WSVERS,AWSAData) > 0
    then ErrorExit('Erreur au chargement de la dll winsock');

    //Configuration server socket UDP
    ClientSocket := connectUDP;

    if send(ClientSocket,CONNECT,sizeof(CONNECT),0) = SOCKET_ERROR
    then ErrorExit('Erreur d''emission ' + inttostr(getLastError()));
    n := recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0);
    if n = SOCKET_ERROR
    then ErrorExit('Erreur de reception ' + inttostr(getLastError()))
    else if ServerResponse = YES then
         begin
           send(ClientSocket,ACTIVATE,sizeof(ACTIVATE),0);
           n := recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0);
           if n = SOCKET_ERROR
           then ErrorExit('Erreur de reception ' + inttostr(getLastError()));
         end;
    
  end else Etat := ADWINLOCKED;
  
end;

procedure TRecupGeneThread.Recuperation;
var i,n,nbdata,par : integer;
begin
  real_time := ExecuteForm.ADwin.Get_FPar(num_process);
  ExecuteForm.TimeLabel.Caption := 'Temps : ' + formatFloat('0.000000',real_time);
  ExecuteForm.TimeLabel.Refresh;
  //rajouter aussi la mise a jour   %occupation CPU
  ExecuteForm.CPUGauge.Progress := ExecuteForm.ADwin.Workload(0);
  ExecuteForm.MemProgGauge.Progress := 100-trunc(100.0*ExecuteForm.ADwin.Free_Mem(1) / ExecuteForm.MemProgMax);
  ExecuteForm.MemDataGauge.Progress :=  100-trunc(100.0*ExecuteForm.ADwin.Free_Mem(3) / ExecuteForm.MemDataMax);
  ExecuteForm.CPUGauge.Refresh;
  ExecuteForm.MemProgGauge.Refresh;
  ExecuteForm.MemDataGauge.Refresh;

  //gerer les perte de signal en testant les pars du keithley

  for i := 0 to high(numDataTrsfrtKPC) do
  begin
    n := numDataTrsfrtKPC[i];
    //recuperation
    nbdata := ExecuteForm.ADwin.Fifo_Full(n);
    if nbdata > 0 then
    begin
      perfFile.Add('Recup Num data : ' + inttostr(n) + ' - Tampon : ' + inttostr(nbData));
      ExecuteForm.ADwin.GetFifo_Float(n,signal[1],nbdata);
      memoryBuffers[n].Write(signal[1],nbdata*4);
      memoryBuffers[n].Position := 0;

      //enregistrement
      if numDataEnreg[n]
      then memoryBuffers[n].SaveToStream(saveFiles[n]);
      //then saveFiles[n].Write(signal[1],nbdata*4);
      //memoryBuffers[n].SetSize(0);

      par := ExecuteForm.ADwin.Get_Par(n);
      if par > 0 then
      begin        
        ExecuteForm.Panel2.Color := clRed;
        perfFile.Add('Perte de données Recup sur Num data : ' + inttostr(n) + ' à ' + floattostr(real_time));
        ExecuteForm.ADwin.Set_Par(n,0);
      end;

    end;

  end;

end;

procedure TRecupGeneThread.Generation;
var i,n,nbdata,par : integer;
    noMoreData : boolean;
begin

  for i := 0 to high(numDataTrsfrtPCK) do
  begin
    noMoreData := false;
    n := numDataTrsfrtPCK[i];
    //remplir fifo
    nbdata := ExecuteForm.ADwin.Fifo_Empty(n);
    if nbdata > 0 then
    begin
      //Attention on utilise un memoryBufferStims ici
      //au lieu du memoryBuffers. Ceci afin de permettre un
      //mecanisme d'affichage identique aux recupération
      if memoryBuffersStim[n].Position < memoryBuffersStim[n].Size then
      begin
        nbDataTrsfrt[n] := nbdata;
        perfFile.Add('Gene Num data : ' + inttostr(n) + ' - Tampon : ' + inttostr(nbData));
        memoryBuffersStim[n].read(signal[1],nbdata*4);
        ExecuteForm.ADwin.SetFifo_Float(n,signal[1],nbdata);
      end else
      begin
        noMoreData := true;
        perfFile.Add('Plus de donnée à transférer');
      end;

      if not noMoreData then
      begin
        par := ExecuteForm.ADwin.Get_Par(n);
        if par > 0 then
        begin
          ExecuteForm.Panel2.Color := clRed;
          perfFile.Add('Perte de données Gene sur Num data : ' + inttostr(n) + ' à ' + floattostr(real_time));
          ExecuteForm.ADwin.Set_Par(n,0);
        end;
      end;
    end;

    //ne rien faire si enregistrement -> fait à la fin du process

  end;
end;

procedure TRecupGeneThread.Affichage;
var i,n : integer;
    ADFLSerie : TDocoFastLineSeries;
begin

  for i := 0 to ExecuteForm.ListeDocoFastLineSeries.Count - 1 do
  begin
    ADFLSerie :=
    ExecuteForm.ListeDocoFastLineSeries.Items[i] as TDocoFastLineSeries;
    ADFLSerie.majSerie(memoryBuffers[ADFLSerie.numDataAbsc],
                       memoryBuffers[ADFLSerie.numDataOrdo],
                       nbDataTrsfrt[ADFLSerie.numDataAbsc],
                       nbDataTrsfrt[ADFLSerie.numDataOrdo]);
    ADFLSerie.ParentChart.Refresh;
  end;

  for i := 0 to high(numDataTrsfrtKPC) do
  begin

    n := numDataTrsfrtKPC[i];

    memoryBuffers[n].SetSize(0);

  end;


            {
  for i := 0 to high(numDataTrsfrtPCK) do
  begin

    n := numDataTrsfrtPCK[i];

    if numDataVisu[n] then
    begin
      //il faut aussi un memory buffer pour affichage stim
      //=> un memoryBufferStims pour la generation qu'on remet jamais
      //position à zero et un memoryBuffers pour affichage avec remise
      //à zero position ... ?
      if memoryBuffers[n].Size > 0 then
      begin

      end;
    end;

   //memoryBuffers[n].SetSize(0);

  end;       }
  

end;

procedure TRecupGeneThread.Execute;
var i,n : integer;
    FileName : string;
begin
  while not Terminated do
  begin

    if EnReseau then
    begin
      if Etat = WANTADWIN then
      begin
        send(ClientSocket,LOCK,sizeof(LOCK),0);//Demande de Lock sur ADWin
        if (recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0) = SOCKET_ERROR)
        then ErrorExit('Erreur de reception ' + inttostr(getLastError()));
        if ServerResponse = YES then Etat := ADWINLOCKED;//Lock ADwin possible
      end;
    end;//Si pas fonctionnement reseau Etat est initialisé à 1 dès le début

    if Etat = ADWINLOCKED then
    begin

      QueryPerformanceCounter(c2Read);
      if c1Read = 0 then c1Read := c2Read;
      time := (c2Read-c1Read)/Freq;
      perfFile.Add('Temps entre 2 Dialogues ADwin : ' + floattostr(time));
      perfFile.Add(' ');
      c1Read := c2Read;
      
      QueryPerformanceCounter(c1);
      Synchronize(Recuperation);
      QueryPerformanceCounter(c2);
      time := (c2-c1)/Freq;
      perfFile.Add('Temps recup : ' + floattostr(time));
      perfFile.Add(' ');

      QueryPerformanceCounter(c1);
      Synchronize(Generation);
      QueryPerformanceCounter(c2);
      time := (c2-c1)/Freq;
      perfFile.Add('Temps gene : ' + floattostr(time));
      perfFile.Add(' ');

      QueryPerformanceCounter(c1);
      Synchronize(Affichage);
      QueryPerformanceCounter(c2);
      time := (c2-c1)/Freq;
      perfFile.Add('Temps affiche : ' + floattostr(time));
      perfFile.Add('****************');
      perfFile.Add(' ');

      Synchronize(TestEndProcess);

      if stopThread then
      begin
        QueryPerformanceCounter(c1);
        Synchronize(Recuperation);
        QueryPerformanceCounter(c2);
        time := (c2-c1)/Freq;
        perfFile.Add('Temps recup : ' + floattostr(time));
        perfFile.Add(' ');

        QueryPerformanceCounter(c1);
        Synchronize(Generation);
        QueryPerformanceCounter(c2);
        time := (c2-c1)/Freq;
        perfFile.Add('Temps gene : ' + floattostr(time));
        perfFile.Add(' ');

        QueryPerformanceCounter(c1);
        Synchronize(Affichage);
        QueryPerformanceCounter(c2);
        time := (c2-c1)/Freq;
        perfFile.Add('Temps affiche : ' + floattostr(time));
        perfFile.Add(' ');

        FileName := rep_manip;

        if ExecuteForm.mode = 'TEST'
        then FileName := FileName + 'TEST_' + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '\'
        else FileName := FileName + ExecuteForm.ASujet.NOM + '\';

        FileName := FileName + 'perfFile_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMST = 1
        then FileName := FileName + ExecuteForm.DocDOCOMETRE.ST[ExecuteForm.numST].NOM + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNOMSESSION = 1
        then if ExecuteForm.mode = 'TEST' then FileName := FileName + ExecuteForm.ASujet.SESSIONS[indexNumSession].NOM + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMSESSION = 1
        then FileName := FileName + 'S' + inttostr(indexNumSession + 1) + '_';

        if ExecuteForm.DocDOCOMETRE.FORMATNOMFICHIERSSAVE.UseNUMESSAI = 1
        then FileName := FileName + 'E' + inttostr(ExecuteForm.numEssai + 1);

        if FileName[length(FileName)] = '_'
        then delete(FileName,length(FileName),1);

        FileName := FileName + '.txt';
        perfFile.SaveToFile(FileName);
        perfFile.Free;

        //liberation memoires trsfrt K PC
        for i := 0 to high(numDataTrsfrtKPC) do
        begin
          n := numDataTrsfrtKPC[i];
          //memoires
          memoryBuffers[n].Free;
          memoryBuffers[n] := nil;
          //enregistrements
          if numDataEnreg[n] then saveFiles[n].Free;
          saveFiles[n] := nil;
        end;

        //liberation memoires trsfrt PC K
        for i := 0 to high(numDataTrsfrtPCK) do
        begin
          n := numDataTrsfrtPCK[i];
          //enregistrement
          if numDataEnreg[n] then
          begin
            //memorystream ds savefile ....
            memoryBuffersStim[n].SaveToStream(saveFiles[n]);
            saveFiles[n].Free;
            saveFiles[n] := nil;
          end;
          //memoire
          memoryBuffers[n].Free;
          memoryBuffers[n] := nil;
          memoryBuffersStim[n].Free;
          memoryBuffersStim[n] := nil;
        end;
        
      end;

      if EnReseau then Etat := ADWINUNLOCK;

    end;

    if EnReseau and (Etat = ADWINUNLOCK) then
    begin
      send(ClientSocket,ENDLOCK,sizeof(ENDLOCK),0);//Demande de UnLock sur ADWin
      n := recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0);
      if n = SOCKET_ERROR
      then ErrorExit('Erreur de reception ' + inttostr(getLastError()));
      if ServerResponse = YES then Etat := WANTADWIN;
    end;

    if stopThread then
    begin
      if EnReseau then
      begin
        send(ClientSocket,INACTIVATE,sizeof(INACTIVATE),0);
        n := recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0);
        if n = SOCKET_ERROR
        then ErrorExit('Erreur de reception ' + inttostr(getLastError()));

        if ServerResponse = YES then
        begin
          send(ClientSocket,DISCONNECT,sizeof(DISCONNECT),0);
          n := recv(ClientSocket,ServerResponse,sizeof(ServerResponse),0);
          if n = SOCKET_ERROR
          then ErrorExit('Erreur de reception ' + inttostr(getLastError()));
          if ServerResponse = YES then
          begin
            shutdown(ClientSocket,1);
            if (closesocket(ClientSocket) <> 0)
            then showmessage('Impossible liberer socket ' + inttostr(getLastError()));
            WSACleanup();
          end;
        end;
      end;
      Terminate;
    end;

    if (GetAsyncKeyState(VK_Escape) AND 32768)=32768 then ExecuteForm.ADwin.Stop_Process(num_process); 

  end;
end;

end.
