ACCUEIL | INTRODUCTION | I) L’ORIGINE DU PROJET | II) LE BESOIN DU CLIENT | III) L’ORGANISATION DU TRAVAIL | IV) LA SOLUTION TECHNIQUE | IV.1) PARTIE LOGICIELLE | IV.2) PARTIE MATERIELLE | BILAN || BIBLIOGRAPHIE | DOCUMENTS | NOTES D'APPLICATION


PARTIE LOGICIELLE

La partie logicielle est composée de trois phases :

  1. Envoi du fichier depuis le PC sur le Port Série USB.
  2. Transmission/Sécurisation à travers les cartes électroniques avec l'embrouillage filaire.
  3. Réception du fichier sur le serveur grâce au Port Série USB.


Figure 10 - Système complet


PHASE 1 : ENVOI

La phase d'envoi est exécutée par un programme rédigé en langage C.
Dans un premier temps, ce dernier va générer le répertoire principal qui permettra à l'utilisateur de réaliser ses transferts de données. Ainsi, l’arborescence suivante; qui peut être complètement modifiée par l'utilisateur; est créée : "D:/Répertoire/Transfert/" et "D:/Répertoire/Archive/". L'utilisateur n'a plus qu'à déposer ses fichiers dans le dossier "Transfert" et le programme va s'occuper de l'envoyer sur le port série de l’ordinateur. Une fois le fichier transféré au serveur, il est automatiquement supprimé du dossier "Transfert". C'est pourquoi, une sauvegarde est créée dans le dossier "Archive" pour éviter toutes pertes. En outre, un fichier "Journal_de_Transfert.txt" dont la fonction est de dresser l'historique des fichiers transférés est également créé et mis à jour. A chaque transfert sera donc renseigné, la date et l'heure à laquelle le fichier a été transféré ainsi que son nom et sa taille.
L'ensemble de ces étapes est illustré sur la figure 11 ci-dessous.


Figure 11 - Application en langage C

Par la suite, le fichier déposé dans le dossier "Transfert" est envoyé sur le Port Série USB de l'ordinateur. Le programme génère donc une trame, et c'est cette trame qui est envoyée. Cette dernière est composée de cinq parties différentes. En effet, comme nous pouvons le voir sur la figure 8, la première information à être envoyée est le nom du fichier. Pour cela, la taille de la chaîne de caractère est envoyée en premier pour indiquer au programme de réception quelle est la longueur exacte du nom qu'il doit s'attendre à acquérir. Puis le nom du fichier est transmis. Le même procédé est ensuite utilisé pour envoyer le contenu du fichier. La taille du fichier est transmise, puis le contenu du fichier. Pour finir, un checksum, calculé préalablement, est envoyé. Ce dernier permettra à la réception de vérifier qu'il n'y ait aucunes altérations entre les données envoyées et les données reçues.
Le calcul du checksum utilisé consiste à appliquer un OU-Exclusif sur la valeur de chacun des caractères composant le fichier (voir le programme ci-dessous).

unsigned long checksum(char* nom_fichier)
{
    FILE* file;
    unsigned char buffer[4096];
    unsigned int code = 0;
    size_t longueur;
    size_t i;

    if (NULL == (file = fopen(nom_fichier, "rb")))
    {
        printf("Impossible d'ouvrir le fichier %s.\n", nom_fichier);
        return -1;
    }

    longueur = fread(buffer, sizeof(char), sizeof(buffer), file);

    for (i = 0 ; i < longueur ; i++)
    {
        code ^= (unsigned int)(buffer[i]);
    }

    return code;
}


Figure 12 - Principe de Fonctionnement du Programme

Pour envoyer les différentes informations sur le port série de l'ordinateur, le programme utilise la fonction "WriteFile" de la librairie "Windows.h". Voici ci-dessous un exemple avec l'envoi de la taille du nom du fichier.

taille_nom_fichier = (unsigned long)strlen(nom_fichier);
WriteFile(serial_port, (void *)&taille_nom_fichier, sizeof(unsigned long), &nb_octets, NULL);
if (nb_octets != sizeof(unsigned long))
{
    fprintf(stderr, "Erreur : La taille du nom de fichier a ete perdue.\n");
    CloseHandle(serial_port);
    exit(0);
}

La fonction prend en compte les paramètres suivants :
- serial_port : du type HANDLE, il permet de définir le numéro du port USB vers lequel le programme va envoyer les données (exemple : "COM4").
- taille_nom_fichier : il s'agit d'un entier et ici, il contient le nombre de caractères composant le nom du fichier. C'est donc la donnée qui va être envoyé sur "serial_port".
- sizeof(unsigned long) : il s'agit d'un entier et il indique le nombre d'octets qui vont être écrits sur "serial_port".
- nb_octets : il s'agit d'un entier et il indique le nombre d'octets qui ont été effectivement écrits sur "serial_port".
- NULL : Valeur NULL car le fichier n'est pas ouvert avec "FILE_FLAG_OVERLAPPED". Il n'y a donc pas besoin d'utiliser un pointeur sur une structure "OVERLAPPED".


PHASE 2 : TRANSMISSION/SÉCURISATION

> TRANSMISSION

Tout d'abord, avant de réaliser l'envoi de la donnée, il est primordial de configurer le micro-contrôleur. Il faut donc débuter par la partie entrée/sortie du bus parallèle, puis par la partie USB qui s'occupe de la réception de la donnée à envoyer.
Une partie du code de configuration est exposée ci-dessous.

void config_PIC(void){
    TRIS_LED_USB_DEVICE_STATUS=0;   // Set LED_USB_DEVICE_STATUS as output
    TRIS_PULSE =0;                  // Set PULSE as output    
    TRIS_DATA = 0x00;               // Set DATA as output

    DATA = 0x00;                    // Init the DATA bus
    PULSE = 0;                      // Init the PULSE

#if defined _18F45K50

    ANSELD = 0x00;                  // Set all pin in portD in digital mode
    ANSELC = 0;                     // Set RC0 pin in portC in digital mode
#endif
}
void APP_DeviceCDCBasicDemoInitialize(){    
    CDCInitEP();

    line_coding.bCharFormat = 0;
    line_coding.bDataBits = 8;
    line_coding.bParityType = 0;
    line_coding.dwDTERate = 19200;    
}

Une fois que la communication USB a été configurée, l'envoi des données peut débuter. Le protocole créé à ces fins est présenté ci-dessous.

Le principe de transmission est le suivant :
- écoute sur le port USB : si une donnée est reçue, elle est envoyée à un buffer grâce à la fonction "readBuffer".
- envoie d'un "pulse" : permet d'indiquer à quel moment le micro-contrôleur de réception doit lire la donnée.
- incrémentation d'une variable : permet de vérifier que la donnée a bien été envoyée.
- vérification de l'état de la communication USB : si cette dernière est prête pour l'envoi de donnée, le caractère "#" est affiché sur le terminal.

void APP_DeviceCDCBasicDemoTasks(){
        uint8_t i;
        uint8_t numBytesRead;

        /* 
         * getsUSBUSART copies a string of BYTEs received through USB CDC Bulk OUT
           endpoint to a user's specified location. It is a non-blocking function.
           It does not wait for data if there is no data available. Instead it
           returns '0' to notify the caller that there is no data available.
         */

        numBytesRead = getsUSBUSART(readBuffer, sizeof(readBuffer));
        if(numBytesRead > 0){
            for(i=0; i<numBytesRead; i++){
                /* Send data*/
                DATA = readBuffer[i];

                /* Pulse's start */
                __delay_us(63);
                PULSE = 1;
                __delay_us(63);
                PULSE = 0;
                /* Pulse's end */                   
            }
            numBuffSend++;
        }
        if (USBUSARTIsTxTrfReady()){
            if(numBuffSend){
                putrsUSBUSART("#");         //validate the transmission by printing this symbole into the terminal
                numBuffSend--;
            }
        }    
    /*
     CDCTxService handles device-to-host transaction(s). This function
     should be called once per Main Program loop after the device reaches
     the configured state.    
     */
    CDCTxService();
}

La donnée a été envoyée depuis l'ordinateur (réseau externe), sur le micro-contrôleur d’émission. Il faut ensuite s'assurer que cette dernière soit bien reçue sur le micro-contrôleur de réception, après passage du bus parallèle. Ici, la configuration des ports du micro-contrôleur ainsi que la configuration de la communication USB doivent être obligatoirement réalisées.
Une fois que la partie réception est prête, la donnée est envoyée à l'ordinateur (réseau interne).

void config_PIC(void){
    TRIS_LED_USB_DEVICE_STATUS=0;   // Set LED_USB_DEVICE_STATUS as output
    TRIS_LED_ROUGE = 0;             // Set LED_ROUGE as output
    TRIS_PULSE = 1;                 // Set PULSE as input    
    TRIS_DATA = 0xFF;               // Set DATA as input

    ANSELD = 0X00;                  //Set all pin in portD in digital mode
    ANSELC = 0;                     //Set all pin in portC in digital mode    
}
void APP_DeviceCDCBasicDemoInitialize(){
    CDCInitEP();
    line_coding.bCharFormat = 0;
    line_coding.bDataBits = 8;
    line_coding.bParityType = 0;
    line_coding.dwDTERate = 19200;    
}

Une fois que les configurations sont réalisées, la donnée est prête à être acquise. Pour cela, le programme va avoir recourt à un double buffer.


Figure 13 - Méthode du double buffer

La méthode du double buffer se décompose ainsi :
- le nombre d'octets est initialisé à 0 et la taille des paquets à recevoir à 63.
- dans l'état "R0", il y a deux configurations :
  • réception des données à chaque coup d'horloge ou lors d'une interruption. Dans ce cas, la donnée est stockée dans le buffer1 et est incrémentée du nombre d'octets.
  • deux situations possibles :
    -> le buffer est plein : Buffer1 est envoyé et le nombre d'octets est réinitialisé.
    -> le buffer n'est pas plein et un timeout est reçu : Buffer1 envoyé et le nombre d'octets est réinitialisé.
    Après avoir envoyé le Buffer1, le nombre d'octets est mis à zéro et l'état du système passe à "R1". Cet état permet de recevoir les données dans le Buffer2. Ce dernier sera ensuite envoyé en suivant le même principe que Buffer1.

La phase de transmission ayant été réalisée, il reste à étudier l'aspect sécurité des données envoyées.

> SÉCURISATION (CÂBLAGE EMBROUILLÉ)

L'une des demandes du client était de réaliser un système de sécurisation par câblage embrouillé, capable de "crypter" les données, et dont la combinaison pouvait être modifiable.
La solution consiste donc à connecter les ports de données de chacun des micro-contrôleurs entre eux, de façon prédéfinie. La donnée reçue sur le micro-contrôleur de réception est ensuite reconstruite grâce à une table de correspondance.
Par exemple, si l'utilisateur décide d'envoyer "010", il recevra "001" dans cette configuration.


Figure 14 - Maillage Filaire

Voici un exemple d'embrouillage filaire sur trois fils du bus parallèle (figure 14).
De cette façon, si la table de correspondance n'est pas connue, la donnée ne peut être décryptée.


PHASE 3 : RÉCEPTION

La phase de réception est également exécutée par un programme rédigé en langage C.
Dans un premier temps, ce dernier va générer le dossier de réception dans lequel les fichiers transférés par le programme d'envoi seront stockés. Ainsi, l’arborescence suivante; qui peut être complètement modifiée par l'utilisateur; est créée : "D:/Réception/". De ce côté de l'application, l'utilisateur n'a aucune action à réaliser. Le programme s'occupe du transfert de données. En effet, ce dernier va créer un fichier temporaire vide qui sera renommé en fonction de l'avancement du transfert. Par exemple, pour le fichier "test.txt", le programme va créer dans le dossier de réception le fichier "test.txt_xx%". Ici, "xx" représente l'avancement du transfert et sa valeur est un entier compris entre 0 et 100. Une fois le transfert effectué à 100%, le fichier temporaire va être supprimé. A noter que le fichier transféré va apparaître dans le dossier de réception au début du transfert mais ce dernier ne pourra être ouvert qu'une fois le transfert terminé.

En outre, comme présenté sur la figure 12, la trame envoyé par le programme d'envoi est composée de cinq parties.
Comme pour la partie d'émission, le programme de réception va acquérir chaque partie une par une. Pour se faire, le programme utilise la fonction "ReadFile" de la libraire "Windows.h". Voici ci-dessous un exemple avec la réception de la taille du nom du fichier.

ReadFile(serial_port, (void *)&taille_nom_fichier, sizeof(unsigned long), &octets_recus, NULL);
if (octets_recus != sizeof(unsigned long))
{
    fprintf(stderr, "Erreur : Impossible d'obtenir la taille du nom de fichier.\n");
        CloseHandle(serial_port);
    exit(0);
}
La fonction prend en compte les paramètres suivants :
- serial_port : du type HANDLE, il permet de définir le numéro du port USB sur lequel le programme va recevoir les données (exemple : "COM3").
- taille_nom_fichier : il s'agit d'un entier et ici, il contient le nombre de caractères composant le nom du fichier. Cette variable reçoit donc la valeur envoyée sur serial_port.
- sizeof(unsigned long) : il s'agit d'un entier et il indique le nombre d'octets qui vont être reçus sur "serial_port".
- octets_recus : il s'agit d'un entier et il indique le nombre d'octets qui ont été effectivement reçus sur "serial_port".
- NULL : Valeur NULL car le fichier n'est pas ouvert avec "FILE_FLAG_OVERLAPPED". Il n'y a donc pas besoin d'utiliser un pointeur sur une structure "OVERLAPPED".

Par la suite, une fois que le transfert du fichier c'est parfaitement déroulé, le programme va calculer le checksum de ce dernier. La méthode de calcul est exactement la même que pour le programme d'envoi.
Une fois calculé, ce checksum va être comparé à celui calculé par le programme d'envoi et qui a été envoyé au programme de réception. Si il n'y a pas correspondance, alors le fichier a été altéré ou corrompu lors du transfert. Il est donc automatiquement effacé.


<< Revenir à IV) LA SOLUTION TECHNIQUE | Chapitre Suivant : IV.2) PARTIE MATERIELLE >>

Application_C.JPG

Principe_de_Fonctionnement_du_Programme.JPG

reception.jpg

MaillageFilaire.jpg

Système_complet.jpg