Bonjour,

Je travaille dans le domaine des transaction électronique et je cherche à résoudre un problème de communication entre un terminal de paiement et une caisse.
Cette communication se fait au travers soit d'un port COM soit au travers d'un port USB et fonctionne la plupart du temps. Sauf que parfois et de façon appartement aléatoire le PC semble devenir incapable de recevoir la reponse du terminal ( Je tiens à préciser que je travaille sur le terminal et sur le PC , car j'ai participer au développement de l'application du terminal et de la DLL PC qui gère les coms ).

Voici le code utiliser :

- Pour ouvrir le port

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
int OpenCom (unsigned char Com,
			 unsigned long Baud,
			 unsigned char Parity,
			 unsigned char StopBits,
			 unsigned char DataBits)
{
 
	int     Retour;
	BOOL	fRet;
	DCB     dcb ;
	DWORD   cr;
	char    NomEvenement[15];
 
	Retour=CZT_ERREUR;
	if ((Com<0) || (Com>20)) return Retour;
	sprintf(NomEvenement,"%s","EVENT_EPOS");
	EvenementCom = CreateEvent(0,TRUE,FALSE,NomEvenement);
 
	if (!(cr=GetLastError()))
	{
	  Overlapped.hEvent     = EvenementCom;
	  Overlapped.Offset     = 0;
	  Overlapped.OffsetHigh = 0;
 
      idComDev[Com]=CreateFile( (LPCTSTR)tCom[Com],
							  GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE ,
			                  0,                    // exclusive access
			                  NULL,                 // no security attrs
			                  OPEN_EXISTING,
			                  FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, // overlapped I/O
			                  NULL );
 
	  if ( idComDev[Com] != INVALID_HANDLE_VALUE ) {
		// get any early notifications
 
		// setup device buffers
		SetupComm( idComDev[Com] , 4096, 4096 ) ;
 
		// purge any information in the buffer
		PurgeComm( idComDev[Com], PURGE_TXABORT | PURGE_RXABORT |
                                  PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
 
		// set up for overlapped I/O
	    CommTimeOuts[Com].ReadIntervalTimeout			= 0 ;
	    CommTimeOuts[Com].ReadTotalTimeoutMultiplier	= 0 ;
	    CommTimeOuts[Com].ReadTotalTimeoutConstant		= 1000 ;
	    CommTimeOuts[Com].WriteTotalTimeoutMultiplier	= 0;
	    CommTimeOuts[Com].WriteTotalTimeoutConstant		= 0;
 
	    fRet=SetCommTimeouts( idComDev[Com], &CommTimeOuts[Com] ) ;
 
		if (Com<8)
		{
		  dcb.DCBlength = sizeof( DCB ) ;
 
  	      fRet=GetCommState( idComDev[Com], &dcb ) ;
 
		  dcb.BaudRate          = Baud;
		  dcb.ByteSize          = DataBits ;
		  dcb.Parity	          = Parity;
		  dcb.StopBits          = StopBits;
		  dcb.fOutxCtsFlow      = FALSE;
		  dcb.fOutxDsrFlow      = FALSE;
		  dcb.fDtrControl       = DTR_CONTROL_DISABLE;
		  dcb.fTXContinueOnXoff = TRUE;
		  dcb.fOutX             = FALSE;				//Enable output X-ON/X-OFF        
		  dcb.fInX              = FALSE;				// Enable input X-ON/X-OFF         
		  dcb.fRtsControl       = RTS_CONTROL_DISABLE;
 
		  // setup hardware flow control
  	      fRet=SetCommState( idComDev[Com], &dcb );
		}
	    else
		{
		  // Configuration ....
		  // Omit the call to SetupComm to use the default queue sizes....???...
  	      fRet=GetCommState( idComDev[Com], &dcb ) ;
		  dcb.ByteSize        = 8;
		  // setup hardware flow control
  	      fRet=SetCommState( idComDev[Com], &dcb );
		}
		if (fRet==TRUE) 
		{	
			Retour=CZT_OK;
		}
		else 
		{
			ConcertLog_AddEvent("fRet incorrect");
			CloseHandle(EvenementCom); 
		}
	  }
	  else
	  {
	    CloseHandle(EvenementCom); 
		ConcertLog_AddEvent("Invalid handle");
	  }
 
	}
	else
	{
		ConcertLog_AddEvent("Get last error found");
	}
 
 
	return Retour;
}
- La fonction de lecture
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
unsigned short PCharIn(unsigned char Com,unsigned char *CarLu,unsigned long Timeout)
{
  unsigned short Retour;
  DWORD			 nBytesRead;
  DWORD          ret=0;
 
  CommTimeOuts[Com].ReadTotalTimeoutConstant =  Timeout;
  SetCommTimeouts( idComDev[Com], &CommTimeOuts[Com] ) ;
 
  Overlapped.hEvent     = EvenementCom;
  Overlapped.Offset     = 0;
  Overlapped.OffsetHigh = 0;
  ReadFile(idComDev[Com],CarLu,1,&nBytesRead , &Overlapped);
  do
  {
 
  }
  while ((ret=WaitForSingleObject(EvenementCom,100)) != WAIT_OBJECT_0);
 
  if (GetOverlappedResult(idComDev[Com],&Overlapped,&nBytesRead,TRUE)==TRUE)
  {
    if (nBytesRead==0)
	{
		Retour = CZT_TIMEOUT;
	}
	else
	{
		Retour=CZT_OK;
	}
  }
  else
  {
	 Retour=CZT_ERREUR;
  }
  ResetEvent(EvenementCom);
  return(Retour);
}
et la fonction d'écriture

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned short PCharOut(unsigned char Com,unsigned char CarEcrit)
{
  unsigned short	Retour;
  DWORD				nBytesWritten;
 
  Overlapped.hEvent     = EvenementCom;
  Overlapped.Offset     = 0;
  Overlapped.OffsetHigh = 0;
 
  WriteFile(idComDev[Com], &CarEcrit, 1, &nBytesWritten, &Overlapped);
  while (WaitForSingleObject(EvenementCom,100) != WAIT_OBJECT_0)
  if (GetOverlappedResult(idComDev[Com],&Overlapped,&nBytesWritten,TRUE)==TRUE)
  {
	  Retour=CZT_OK;
  }
  else
  {
    PurgeComm( idComDev[Com], PURGE_TXABORT | PURGE_TXCLEAR ) ;
    Retour = CZT_ERREUR;
  }
  ResetEvent(EvenementCom);
  return(Retour);
}

De façon simplifiée, le protocole entre les deux fonctionne ainsi :

Émission d'une requête par la caisse :
Caisse ----> Terminal : ENQ
Caisse <---- Terminal : ACK
Caisse ------> Terminal : Requete
Caisse <---- Terminal : ACK
Caisse ----> Terminal : EOT

À ce moment le terminal fait son travail et la caisse se met dans un delay d'attente de 90s

Normalement , la réponse du terminal doit fonctionner comme ça
Caisse <---- Terminal : ENQ
Caisse ----> Terminal : QCK
Caisse <----- Terminal : Reponse
Caisse -----> Terminal : ACK
Caisse <---- Terminal : EOT

Tous fonctionne dans plus de 90% des cas , sauf parfois ou le terminal envoie le ENQ mais la DLL ne semble pas le recevoir ( La logique est d'envoyer un ENQ puis au bout de 600 ms en absence
de réponse envoyer un EOT , attendre un peu puis recommence la séquence et cela 3 fois) , ce qui finit par provoquer un échec.

Le pire c'est que quelque seconde après si la caisse tente d'envoyer une nouvelle requête , je trouve tous la séquence dans le buffer de réponse ( ENQ , EOT , ENQ, EOT, ENQ, EOT)

Après des jours de rechercher et d'analyse de log sans le moindre résultat , j’espère que quelqu'un pourra m'aider a résoudre ce casse tête

Il semble que pour une raison ou une autre mes octets venant du terminal soit bloquer avant d'arriver a la DLL et cela de façon aléatoire. Pour information , si on tente une nouvelle requête sans attendre après un échec , elle choura aussi mais cette fois dans l'autre sens , la DLL indiquant l'envoie et le terminal la non réception. Ce qui me fait penser que c'est peut etre lier au driver Windows ou a un problème Hardware

Est-ce que quelqu'un aurait déjà vu un truc semblable ou aurez une piste ou une idée car la moi je commence a manquer d’idée

Merci D'avance