la prof nous a file un code qui permet d'avoir le clavier non blocant,
et qui rentre dans le code lorsque l'on recoit des données sur le port serie, le clavier ou lors du TimeOut.
le probleme est que le code que nous utilisons doit marchait lorsque l'on simule le oport serie
avec 3 appli en parallele, apparemment, le SELECT considere que l'on recoit des datas
lorsque le fichier est ouvert,
comment y remedier selon vous, merci.
ci-apres le code complet de mon projet
- Code: Tout sélectionner
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <termios.h>
#include <stdlib.h>
#define max(a,b) (a>b ? a : b)
struct termios KbdTermiosNew={0};
struct termios KbdTermiosOld={0};
struct termios ComTermiosNew={0};
struct termios ComTermiosOld={0};
fd_set Readfds={0};
fd_set ReadfdsInstantane={0};
int hPortMax=0;
int NoDescripteurLiaisonSerie;
int NoDescripteurLiaisonEmission ;
int NoDescripteurLiaisonReception;
unsigned char TJeton[]={0xE0,0x00, 0xE0, 0x70};
int NumMach; // Numero de la machine
// A utiliser pour simuler les ports COM par des fichiers
int TacInitLiaison(int NumMachine, int Prec, int Suiv)
{
int Erreur=0;
char Nom1[] = "ttyS0_0",
Nom2[] = "ttyS0_0",
Tempo[3];
// format du fichier : 'ttyS' + Poste qui ecrit + '_' + Poste qui lit
// ex : ttyS1_2 : fichier ecrit par la machine 1 et lue par la 2
// Entier -> Caractere pour faire le nom du fichier
sprintf(Tempo,"%d",Prec);
Nom1[4] = Tempo[0];
sprintf(Tempo,"%d",Suiv);
Nom2[6] = Tempo[0];
sprintf(Tempo,"%d",NumMachine);
Nom1[6] = Tempo[0];
Nom2[4] = Tempo[0];
printf("\n%s\n%s\n",Nom1,Nom2);
// COM2
NoDescripteurLiaisonReception = open(Nom1, O_NOCTTY | O_NONBLOCK | O_RDONLY | O_CREAT|O_TRUNC,S_IRWXU);
printf("NoDescripteurLiaisonReception=%d\n",NoDescripteurLiaisonReception);
// SI l'ouverture s'est mal passee
if (NoDescripteurLiaisonReception==-1) {
// Afficher le message UNIX correspondant au errno
perror("Ouverture liaison serie");
Erreur = 1;
}
// COM1
NoDescripteurLiaisonEmission = open(Nom2, O_NOCTTY | O_NONBLOCK | O_WRONLY | O_CREAT | O_TRUNC| O_APPEND,S_IRWXU);
printf("NoDescripteurLiaisonEmission=%d\n",NoDescripteurLiaisonEmission);
// SI l'ouverture s'est mal passee
if (NoDescripteurLiaisonEmission==-1) {
// Afficher le message UNIX correspondant au errno
perror("Ouverture liaison serie");
Erreur = 1;
}
return Erreur;
}
int TacNettoyage(void)
{
// Remettre les vieux attributs de l'entree standard
tcsetattr(0,TCSANOW,&KbdTermiosOld);
// close ttty
if (close (NoDescripteurLiaisonEmission)) {
// Afficher le message UNIX correspondant au errno
perror("Fermeture liaison serie");
}
if (close (NoDescripteurLiaisonReception)) {
// Afficher le message UNIX correspondant au errno
perror("Fermeture liaison serie");
}
}
//**********************************************************************************//
// Cette procedure extrait l'adresse dest (1) ou src (2) codé sur 3 bits de la trame//
//**********************************************************************************//
int ExtraireAdresse(char Tab[35], int Addr)
{
char Cadr = Tab[0];
int Ret=0;
if (Addr == 1)
Ret = (int) Cadr>>5; // Adresse destinataire
else
Ret = (int) (Cadr<<3)>>2; // Adresse source
return(Ret);
}
//**********************************************************************************//
// Cette fonction extrait le CRC de la trame //
//**********************************************************************************//
int ExtraireCRC(unsigned char trame[35])
{
unsigned char Tempo;
int iCRC, // Index du CRC dans la trame
Ret;
// recuperation du nombre d'octet du message
Tempo = trame[1]<<3;
Tempo = Tempo>>3;
iCRC = 2 + (int)Tempo;
// creation d'un entier à partir des 2 octets de CRC
Ret = (int)trame[iCRC];
Ret = (Ret << 8) + (int)trame[iCRC+1];
return(Ret);
}
//**********************************************************************************//
// Cette fonction calcul un CRC sur la trame //
//**********************************************************************************//
int CRC(unsigned char trame[35], int lgT)
{
int i,Ret = 0;
unsigned char parite1=0, parite2=0, temp, c;
for(i=0;i<lgT;i++)
{
c = parite1 ^ trame[i]; // parite bit à bit
temp = (parite2 & 0x01)<<7; // recup du LSB et repositionnement en MSB
parite2 = parite2 >> 1; // decalage a droite de 1 bit
parite2 = parite2 + temp;// reinjection du premier bit
parite2 = parite2 ^ trame[i]; // parite bit à bit
}
// creation d'un entier à partir des 2 octets de CRC
Ret = (int)parite1;
Ret = (Ret << 8) + (int)parite2;
return(Ret);
}
//**********************************************************************************//
// Cette fonction verifie la trame par rapport à son CRC //
//**********************************************************************************//
int CRC_OK(unsigned char trame[35], int lgT)
{
return (ExtraireCRC(trame) == CRC(trame,lgT-2));
}
//**********************************************************************************//
// Cette fonction retourne 1 si l'appli est maitre et 0 dans le cas contraire
//**********************************************************************************//
int AppliMaitre()
{
return(NumMach == 1);
}
//**********************************************************************************//
// Cette fonction retourne 1 si l'appli est la source du message et 0 dans le cas contraire
//**********************************************************************************//
int SourceMoi(unsigned char trame[35])
{
return(ExtraireAdresse(trame,2) == NumMach);
}
//**********************************************************************************//
// Cette fonction retourne 1 s'il s'agit d'un message de broadcast et 0 dans le cas contraire
//**********************************************************************************//
int DestinataireMoi(unsigned char trame[35])
{
return(ExtraireAdresse(trame,1) == NumMach);
}
//**********************************************************************************//
// Cette fonction retourne 1 s'il s'agit d'un message de broadcast et 0 dans le cas contraire
//**********************************************************************************//
int DestinataireTous(unsigned char trame[35])
{
return(ExtraireAdresse(trame,1) == 0);
}
//**********************************************************************************//
// Cette fonction envoie le jeton
//**********************************************************************************//
void EnvoyerJeton(void)
{
write(NoDescripteurLiaisonEmission,TJeton,4);
}
//**********************************************************************************//
// Cette fonction renvoir 1 si la trame est une trame de confirmation et 0 dans le cas contraire
//**********************************************************************************//
int TrameDeConfirmation(unsigned char trame[35], int LgT)
{
return(((trame[0] & 0x03)==0) && (LgT == 3));
}
//**********************************************************************************//
/* Cette fonction permet de traiter une trame
Retour de la fonction :
-----------------------
-1 : Erreur CRC
0 : Message recu et valide
1 : Message a retransferrer, le destinataire est un autre
*/
//**********************************************************************************//
int TraiterTrame(unsigned char TRAME[35],int Lg)
{
int i, Ret=1;
if ( (ExtraireAdresse(TRAME,1)) == NumMach )
{
// la trame est pour ce poste : l'afficher si le crc est bon
if (ExtraireCRC(TRAME) == CRC(TRAME,Lg-2))
Ret = 0; // CRC OK
else
Ret = -1; // Erreur CRC
}
else
{
// la trame n'est pas pour ce poste, la rexpedier
write(NoDescripteurLiaisonEmission,TRAME,Lg);
}
}
//**********************************************************************************//
// Cette fonction permet d'afficher le message d'une trame
//**********************************************************************************//
void AfficherMessage(unsigned char TRAME[35],int Lg)
{
int i;
for (i=2;i<Lg-4;i++) printf("%c",TRAME[i]);
}
//**********************************************************************************//
// Cette fonction permet d'envoyer un message de 31 caracteres max, elle
// retourne le nombre de caractere restant a afficher
//**********************************************************************************//
int EnvoyerMessage(char Message[255], int LgMsg, char CNumMachine)
{
char CTempAtoi[2] = "0",
carac,
TRAME[35];
int LgTransmit,
i,
iCRC1,
iCRC2;
// Codage de l'adresse d'expedition : 3 bits de poids fort
CTempAtoi[0] = Message[0];
carac = (char) atoi(CTempAtoi);
// verification que la machine cible est autre que l'expediteur
if (carac != CNumMachine)
{
carac = carac<<5;
// On rajoute les 3 bits de sources pas une operation logique OU
TRAME[0] = carac | CNumMachine;
// le decalage de 2 vient du formatage du message : 1:Message !
if (LgMsg>31+2) // trame trop longue
{
LgMsg -= 31;
LgTransmit = 31;
}
else
{
LgTransmit = LgMsg-2;
LgMsg = 0;
}
TRAME[1] = (char)LgTransmit;
// recopie du bout de message a transmettre
for (i=0;i<LgTransmit;i++) TRAME[i+2] = Message[i];
// Calcul et insertion du CRC dans les 2 derniers octets
iCRC1 = CRC(TRAME,LgTransmit+2);
iCRC2 = iCRC1-((iCRC1>>8)<<8);
iCRC1 -= iCRC2;
TRAME[LgTransmit] = (char)iCRC1;
TRAME[LgTransmit+1] = (char)iCRC2;
// Envoi de la trame
write(NoDescripteurLiaisonEmission,TRAME,LgTransmit);
// Decalage des datas qui reste a envoyer
for(i=LgTransmit; i<LgTransmit+LgMsg;i++) Message[i-LgTransmit+2] = Message[i+2];
}
else
LgMsg = 0; // destinataire = source > annulation !
return(LgMsg);
}
//**********************************************************************************//
// Cette fonction renvoie une trame
//**********************************************************************************//
void RenvoyerTrame(char Message[255], int LgMsg)
{
write(NoDescripteurLiaisonEmission,Message,LgMsg);
}
//**********************************************************************************//
// Cette fonction retourne une trame de confirmation
//**********************************************************************************//
void EnvoyerConfirmation(char Message[255])
{
char TRAME[3];
int iCRC1,
iCRC2;
// L'adresse de source devient celle de destination
TRAME[0] = Message[0] << 3;
// Codage de l'adresse de source
TRAME[0] = TRAME[0] | NumMach;
// Calcul et insertion du CRC dans les 2 derniers octets
iCRC1 = CRC(TRAME,1);
iCRC2 = iCRC1-((iCRC1>>8)<<8);
iCRC1 -= iCRC2;
TRAME[1] = (char)iCRC1;
TRAME[2] = (char)iCRC2;
// Envoi de la trame
write(NoDescripteurLiaisonEmission,TRAME,3);
}
//**********************************************************************************//
// Cette fonction retourne 1 si la trame est un jeton et 0 dans le cas contraire
//**********************************************************************************//
int IsJeton(unsigned char TRAME[35],int Lg)
{
int Ret = 1, i;
if (Lg == 4)
{
for (i=0; i<4; i++)
{
if (TRAME[i] != TJeton[i]) Ret = 0;
}
}
else
Ret = 0;
return(Ret);
}
//**********************************************************************************//
// Cette fonction retourne 1 si la trame est une trame d'erreur
//**********************************************************************************//
int IsTrameErreur(unsigned char TRAME[35],int Lg)
{
int Ret = 1, i;
if ( (Lg != 4) || ((TRAME[i] & 0x02) == 0 ) )
{
Ret = 0;
}
return(Ret);
}
//**********************************************************************************//
// Programme principal
//**********************************************************************************//
main(int argc, char * argv[])
{
int Arret=0;
int ValMiniMs = 2000;
struct timeval Timeout;
int NMach, Prec, Suiv;
int Lg,
LgC = 0, // longueur du message a envoyer
i, j = 0,
iCRC1,
iCRC2,
EnCoursTransmission = 0;
char CLAVIER[255], CNumMach;
int Data_E = 0, // data enable : donnee a envoyer
AttenteReponse = 0, // en attente de reponse
TrameDejaReemise = 0;
unsigned char TRAME[35];
printf("Simulation Reseau Serie\n");
// ************ Demande de la config de l'appli **********************************
printf("\nIdentification du poste\n----------------------\n\n\tNumero de la machine : ");
fflush(stdin);
scanf("%d",&NumMach);
fflush(stdin);
printf("\n\tNombre de machine totale : ");
scanf("%d",&NMach);
fflush(stdin);
// recherche du poste suivant sur le reseau
if (NumMach == NMach) Suiv = 1; else Suiv = NumMach + 1;
// Recherche du poste precedent sur le reseau
if (NumMach == 1) Prec = NMach; else Prec = NumMach - 1;
// codage de l'adresse de la machine sur les 3 bits suivants : ...XXX..
CNumMach = (char) NumMach;
CNumMach = CNumMach<<5; // decalage de 5 bits vers la gauche pour supprimer les 5 bits de debut
CNumMach = CNumMach>>3; // decalage de 3 bits vers la droite pour respecter le format
//************************************************************************************
/* L'entree standard est 0 (stdin) */
/* Passer l'entree standard en mode non bloquant */
fcntl(0,F_SETFL,fcntl(0,F_GETFL) | O_NONBLOCK);
/* Sauvegarder les vieux attributs de l'entree standard */
tcgetattr(0,&KbdTermiosOld);
KbdTermiosNew=KbdTermiosOld;
/* Passer l'entree standard en mode canonique */
KbdTermiosNew.c_lflag |= ICANON;
// Appliquer maitenenant nos attributs
tcsetattr(0,TCSANOW,&KbdTermiosNew);
if (TacInitLiaison(NumMach,Prec,Suiv))
{
printf("Erreur initialisation\n");
exit (1);
}
// Positionner le bit correspondant au descripteur de la liaison serie
// dans la table bit du select
FD_SET(NoDescripteurLiaisonReception,&Readfds);
// L'entree standard est 0 (stdin)
// Positionner le bit correspondant au descripteur de la l'entree standard
// dans la table bit du select
FD_SET(0,&Readfds);
hPortMax = max(hPortMax,NoDescripteurLiaisonReception)+1;
printf("hPortMax=%d\n",hPortMax);
while (!Arret)
{
// Reinitialiser la table du select
memcpy(&ReadfdsInstantane,&Readfds,sizeof(ReadfdsInstantane));
Timeout.tv_sec = (ValMiniMs / 1000);
Timeout.tv_usec= (ValMiniMs % 1000) * 1000;
// SI au moins un descripteur parle
if (select(hPortMax,&ReadfdsInstantane,0,0,&Timeout) > 0)
{
printf("%d\n",j);
j = (j++)%100;
// SI data clavier
if (FD_ISSET(0,&ReadfdsInstantane))
{
// !! ** changer le systeme de saisie du clavier pour ne pas perdre de data ** !! //
// faire un systeme pour rajouter le new buffer a un ancien non envoyé
LgC=read(0,CLAVIER,sizeof(CLAVIER)-2);
Data_E = (LgC > 0);// and (NumMach != atoi(CLAVIER[0]);
}
Lg = 0;
// SI data serie
if (FD_ISSET(NoDescripteurLiaisonReception,&ReadfdsInstantane))
{
// Lire la liaison serie
Lg=read(NoDescripteurLiaisonReception,TRAME,sizeof(TRAME));
if (Lg>0)
{
if (AppliMaitre())
{
// reinitialisation du timeout principal
}
if (CRC_OK(TRAME, Lg))
{
if (AttenteReponse)
{
// source = moi
if (SourceMoi(TRAME))
{
// destinataire different de tous
if (!DestinataireTous(TRAME))
{
Data_E = 0;
EnvoyerJeton();
}
AttenteReponse = 0;
}
else
{
// source = pas moi
if (TrameDeConfirmation(TRAME, Lg))
{
// data_e = 0
if (!Data_E)
{
EnvoyerJeton();
AttenteReponse = 0;
}
}
else
{
if (!TrameDejaReemise)
{
EnvoyerJeton();
AttenteReponse = 0;
}
}
}
}
else
{ // pas en attente d'une reponse
if (IsJeton(TRAME,Lg))
{// la trame est le jeton
if (Data_E)
{// il y a des donnée à emettre
// envoi de 31 caracteres max
LgC = EnvoyerMessage(CLAVIER, LgC, CNumMach);
// positionemment de l'appli en mode attente de reponse
AttenteReponse = 1;
// reste il des datas à emettre
if ( LgC == 0 ) Data_E = 0;
// reinitialisation du timeout ici
}
else
{// il n'y a pas de donnée à emettre
EnvoyerJeton();
}
}
else
{// la trame n'est pas le jeton
if (IsTrameErreur(TRAME, Lg))
{
// il s'agit d'une trame d'erreur
if (SourceMoi(TRAME))
{
EnvoyerJeton();
}
else
{
RenvoyerTrame(TRAME, Lg);
}
}
else
{
// il ne s'agit pas d'une trame d'erreur
if (DestinataireMoi(TRAME))
{
AfficherMessage(TRAME, Lg);
EnvoyerConfirmation(TRAME);
}
else
{
if (DestinataireTous(TRAME))
{
AfficherMessage(TRAME, Lg);
RenvoyerTrame(TRAME, Lg);
}
}
}
}
}
}
}
}
if (Lg == 0)
{// Il n'y a pas eu de données sur le port série
if (AttenteReponse)
{
// Appli dans l'attente d'une reponse
}
else
{ // Appli dans l'attente d'aucune reponse
}
}
}
}
TacNettoyage();
}