{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 UCalibMonitorThread;

interface

uses Windows,Classes,Winsock,UGlobal,SysUtils,Math,Dialogs,Forms;

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

type
  TCalibMonitorThread = class(TThread)
  private
		{ Déclarations privées }
		//fonctionnement en reseau
    CLientSocket : TSocket;
    ServerResponse : Char;
    Etat : integer;//WANTADWIN : demande de connection ADWin
                   //ADWINLOCKED : connection ADWin active
									 //ADWINUNLOCK : fin de connection ADWin
		
		stopThread : boolean;
		initDone : boolean;
		c1 : int64;
    c2 : int64;
    freq : int64;
    time : double;
		function ConnectUDP : TSocket;
		procedure ErrorExit(Msg : string);
		procedure TestEndProcess;
		procedure inputs;
		procedure outputs;
		procedure initADWin;
  protected
		procedure Execute; override;
	public
	  public
    constructor Create(suspended : boolean; priorityLevel : TThreadPriority);
  end;

implementation

uses UCalibMonitor,UInFrame,UOutFrame,UDocometre;

{ 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 TCalibMonitorThread.UpdateCaption;
    begin
      Form1.Caption := 'Mis à jour dans un thread';
		end; }

{ TCalibMonitorThread }

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

procedure TCalibMonitorThread.TestEndProcess;
begin
  Application.ProcessMessages;
	if (CalibMonitorForm.ADwin.Process_Status(num_process) <> 1) then
	begin
		if (CalibMonitorForm.ADwin.Process_Status(num_process) = 0)
		then stopThread := true;
	end;
end;

procedure TCalibMonitorThread.ErrorExit(Msg : string);
begin
  WSACleanup();
  Terminate;
end;

function TCalibMonitorThread.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 TCalibMonitorThread.Create(suspended : boolean; priorityLevel : TThreadPriority);
var	WSVERS : word ;
		AWSAData : TWSAData;
		n : integer;
begin
	inherited Create(suspended);

	c1 := 0;
  c2 := 0;
  time := 0.0;
	QueryPerformanceFrequency(Freq);

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

  //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 TCalibMonitorThread.initADWin;
var i : integer;
    DIOPROG,DIOPROG1,DIOPROG2 : int64;
		tempDIGPROG1,tempDIGPROG2 : int64;
    B1,B2,B3,B4,num,N2 : integer;
begin
  if SystemType = 'ADWINPRO' then
  begin
    CalibMonitorForm.ADWin.Load_Process(rep_racine+'calibration.t' + cpuID + inttostr(Num_Process)) ;

    //single ended ou différentiel
    //uniquement pour ADWinPro
    if DocDOCOMETRE.CTRLEAN.SEDIFF = 'SE'
    then CalibMonitorForm.ADWin.Set_Par(12,0)
    else CalibMonitorForm.ADWin.Set_Par(12,1);

    //Préparation programation des entrées/sorties numériques
    //uniquement pour ADWinPro
    DIOPROG := 0;
    for i := 0 to DocDOCOMETRE.SNUM.Count - 1
    do DIOPROG := DIOPROG + round(power(2,DocDOCOMETRE.SNUM[i].NUMERO-1));
    DIOPROG1 := DIOPROG and 65535;
    DIOPROG2 := DIOPROG shr 16;

    //Sauvegarde la configuration courante
    tempDIGPROG1 := CalibMonitorForm.ADwin.Get_Par(10);
    tempDIGPROG2 := CalibMonitorForm.ADwin.Get_Par(11);
    DIOPROG1 := tempDIGPROG1 or DIOPROG1;
    DIOPROG2 := tempDIGPROG2 or DIOPROG2;

    //Programation des entrées/sorties numériques
    CalibMonitorForm.ADwin.Set_Par(10,DIOPROG1);
    CalibMonitorForm.ADwin.Set_Par(11,DIOPROG2);
    CalibMonitorForm.ADwin.Set_Par(9,1);
  end;

  if SystemType = 'ADWINGOLD'
  then CalibMonitorForm.ADWin.Load_Process(rep_racine+'calibrationGold.t' + cpuID + inttostr(Num_Process));

  if SystemType = 'ADWINPRO II' then
   begin

     CalibMonitorForm.ADWin.Load_Process(rep_racine+'calibrationPro2.t' + cpuID + inttostr(Num_Process)) ;

     B1 := 0;
     B2 := 0;
     B3 := 0;
     B4 := 0;
     for i := 0 to DocDOCOMETRE.SNUM.Count - 1 do
     begin
       num := DocDOCOMETRE.SNUM[i].NUMERO;
       if (num > 24) then B4 := 8;
       if ((num > 16) and (num <= 24)) then B3 := 4;
       if ((num > 8) and (num <= 16)) then B2 := 2;
       if (num <= 8) then B1 := 1;
     end;

     N2 := B4+B3+B2+B1;

     CalibMonitorForm.ADwin.Set_Par(10,N2);
     CalibMonitorForm.ADwin.Set_Par(9,1);

     
   end;

   if SystemType = 'ADWINPRO II Old Cards' then
   begin

     CalibMonitorForm.ADWin.Load_Process(rep_racine+'calibrationPro2OldCards.t' + cpuID + inttostr(Num_Process)) ;

    //single ended ou différentiel
    //uniquement pour ADWinPro
    if DocDOCOMETRE.CTRLEAN.SEDIFF = 'SE'
    then CalibMonitorForm.ADWin.Set_Par(12,0)
    else CalibMonitorForm.ADWin.Set_Par(12,1);

    //Préparation programation des entrées/sorties numériques
    //uniquement pour ADWinPro
    DIOPROG := 0;
    for i := 0 to DocDOCOMETRE.SNUM.Count - 1
    do DIOPROG := DIOPROG + round(power(2,DocDOCOMETRE.SNUM[i].NUMERO-1));
    DIOPROG1 := DIOPROG and 65535;
    DIOPROG2 := DIOPROG shr 16;

    //Sauvegarde la configuration courante
    tempDIGPROG1 := CalibMonitorForm.ADwin.Get_Par(10);
    tempDIGPROG2 := CalibMonitorForm.ADwin.Get_Par(11);
    DIOPROG1 := tempDIGPROG1 or DIOPROG1;
    DIOPROG2 := tempDIGPROG2 or DIOPROG2;

    //Programation des entrées/sorties numériques
    CalibMonitorForm.ADwin.Set_Par(10,DIOPROG1);
    CalibMonitorForm.ADwin.Set_Par(11,DIOPROG2);
    CalibMonitorForm.ADwin.Set_Par(9,1);

     
   end;

	CalibMonitorForm.ADWin.Start_Process(Num_Process);
	QueryPerformanceCounter(c2);
	c1 := c2;
	initDone := true;
end;

procedure TCalibMonitorThread.inputs;
var i : integer;
begin
  for i := 0 to CalibMonitorForm.inForm.ControlCount - 1 do
	begin
		if CalibMonitorForm.inForm.Controls[i].ClassNameIs('TInFrame') then
		begin
			(CalibMonitorForm.inForm.Controls[i] as TInFrame).maj_Chart;
		end;
	end;
end;

procedure TCalibMonitorThread.outputs;
var i : integer;
begin
	for i := 0 to CalibMonitorForm.outForm.ControlCount - 1 do
	begin
		if CalibMonitorForm.outForm.Controls[i].ClassNameIs('TOutFrame') then
		begin
			(CalibMonitorForm.outForm.Controls[i] as TOutFrame).maj_Chart;
			if (CalibMonitorForm.outForm.Controls[i] as TOutFrame).analogChanged
			then (CalibMonitorForm.outForm.Controls[i] as TOutFrame).majAnalogChange;
			if (CalibMonitorForm.outForm.Controls[i] as TOutFrame).ONOFFChanged
			then (CalibMonitorForm.outForm.Controls[i] as TOutFrame).majONOFFChange;
		end;
	end;
end;

procedure TCalibMonitorThread.Execute;
var n  : integer;
begin
	{ Placez le code du thread ici }
	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;
			c1Read := c2Read;}

			if not initDone
			then Synchronize(InitADwin)
			else begin
				QueryPerformanceCounter(c2);
				time := (c2-c1)/Freq;
				if (time > 0.010) then
				begin
					Synchronize(inputs);
					Synchronize(outputs);
					c1 := c2;
				end;
			end;

			Synchronize(TestEndProcess);
			
			if stopThread then
			begin
			  //
			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;

	end;
end;

end.
