//********************************************************************************************************************************************************************************** // Fichier MODEM.C // // Contient toutes les fonctions liées à la gestion du Modem SIM7070G, y compris GNSS //**********************************************************************************************************************************************************************************/ // #define __DEBUG_MODEM //Décommenter cette ligne pour avoir les messages de debug du MODEM // #define __DEBUG_MODEM_GNSS //Décommenter cette ligne pour avoir les messages de debug du MODEM en mode GNSS // #define __DEBUG_MODEM_HTTP_GET // Décommenter cette ligne pour avoir les messages de debug du MODEM en mode HTTP GET #define __DEBUG_MODEM_FTP_UPLOAD //Décommenter cette ligne pour avoir les messages de debug du MODEM en mode FTP #define __DEBUG_MODEM_FTP_DOWNLOAD //Décommenter cette ligne pour avoir les messages de debug du MODEM en mode DOWNLOAD de fichiers sur le serveur // Includes : #include "main.h" // Prototypes : int8_t init_modem(void); void gestion_modem(void); // Variables globales : struct Struct_modem modem; /********************************************************************************************************************************************************************************** Fonction INIT_MODEM Fonction d'initialisation du MODEM **********************************************************************************************************************************************************************************/ int8_t init_modem(void) { if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) { modem.consigne = ON; // Etat demandé pour le modem. // 'mode.etat' est récupéré de la base de données au démarrage. Il sera ensuite mis à jour par le gestionnaire après démarrage. // 'modem.etat_presence_sim' est récupéré de la base de données au démarrage. Il sera ensuite mis à jour par le gestionnaire après démarrage. modem.etat_liaison = OFF; // Par défaut, la liaison série n'est PAS établie avec le modem modem.etat_4g_lte = OFF; // Par défaut, la liaison 4G-LTE n'est PAS disponible gnss.etat = OFF; // Par défaut. modem.compteur_periode_gnss = 15; // Permet que plusieurs (a priori, 15) boucles de surveillance modem soient faites avant de déclencher la première acquisition GNSS modem.compteur_periode_download = modem.compteur_periode_gnss + 10; // Permet que le premier Download soit fait avant tout upload (utile si on est bloqué sur les upload...) modem.compteur_periode_upload = modem.compteur_periode_download + 10; // Permet que la première recherche d'upload se fasse après le premier download si nécessaire // ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_MOD_WKUP, ON); // Activation immédiate de la sortie DTR qui n'est pas utilisée mais qui doit être positionnée à 1 pour pouvoir fonctionner modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_DEBUT_IMPULSION_RESET; // Déclenche systématiquement une impulsion >12s de reset sur POWER_KEY modem.sequenceur.compteur = MODEM_TEMPO_INIT; // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation du MODEM\t\t: OK", TYPE_MESSAGE_DEBUG_OK); } else { // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation du Modem\t\t: Devalidé car non valide sur cette version de ShipBase", TYPE_MESSAGE_DEBUG_WARNING); } return(0); } //================================================================================================================================================================== // Fonction : Appelée en tâche de fond, cette fonction gère les fonctions du modem SIM7070G dans son ensemble. Il s'appuie sur les fonctions M-Bus pour y accéder. // Reçoit : Rien // Renvoit : Rien //================================================================================================================================================================== void gestion_modem(void) { uint16_t i; uint32_t t; uint8_t s, m; int32_t type; int32_t taille; char nom[MAX_LONGUEUR_NOM_FICHIER]; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; if (systeme.generation_shipbase != GENERATION_SHIPBASE_V1) return; switch(modem.sequenceur.etat) { //========================================================================================================================================================= // INITIALISATION : //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_INIT : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { //------------------------------------------------------------------- // RAZ du M-Bus. Utile notamment dans le cas d'un overflow qui aurait déclenché un reset du modem : m_bus.sequenceur.compteur = 0; m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_INIT; //------------------------------------------------------------------- // Vérification de l'état actuel du modem et comparaison au demandé : // Mise à jour de l'état actuel du modem : if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_STATUS_MOD, FALSE) == 0) modem.etat = OFF; // 0 est l'adresse de ShipBase else modem.etat = ON; // Check si la consigne est différente de l'état demandé : if (modem.etat == modem.consigne) modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT_LIAISON; // Si non, on passe directemetn au repos else modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT; // Si oui, on passe par une phase de changement d'état (identique que ce soit pour passer à On ou à Off) #ifdef __DEBUG_MODEM sprintf(tmp, "L'entrée Status du modem SIM7070G est à %i et sa consigne à %i. le séquenceur passe à l'état %i", modem.etat, modem.consigne, modem.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif modem.etat_liaison = OFF; // Par défaut, la liaison n'est PAS établie (permet de bien vérifier que l'autobaud fonctionne) } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_PWKEY_MOD, ON); // Activation immédiate de la sortie PowerKey. 0 est l'adresse de ShipBase modem.sequenceur.compteur = MODEM_TEMPO_IMPULSION_CHANGEMENT_ETAT; // Lancement de la durée de l'impulsion #ifdef __DEBUG_MODEM sprintf(tmp, "Envoi d'une impulsion sur PowerKey du modem de %ims", (modem.sequenceur.compteur * 100)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_IMPULSION; // Passage à l'attente de la fin de l'impulsion break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_IMPULSION : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) // L'impulsion est terminée { ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_PWKEY_MOD, OFF); // Désactivation immédiate de la sortie PowerKey. 0 est l'adresse de ShipBase #ifdef __DEBUG_MODEM sprintf(tmp, "Fin de l'impulsion sur PowerKey du modem. Attente du changement de son Status"); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif modem.repetitions = 0; modem.sequenceur.compteur = MODEM_TEMPO_ATTENTE_CHANGEMENT_ETAT; // Chargement d'un quantum de temps avant de venir relire l'état du Status du modem (ce quatum n'est pas critique, c'est le temps total de relecture qui est important) modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_ATTENTE_STATUS; // Passage à l'attente de la mise à jour de Status } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_ATTENTE_STATUS : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_STATUS_MOD, FALSE) != modem.consigne) // Si le modem n'est pas encore dans l'état souhaité : 0 est l'adresse de ShipBase { if (++ modem.repetitions >= MODEM_MAX_REPETITIONS_TEMPO_ATTENTE_CHANGEMENT_ETAT) // Vérification que la durée maximale d'attente n'est pas atteinte. Si oui : { #ifdef __DEBUG_MODEM sprintf(tmp, "Le Status du modem SIM7070G n'est toujours pas à la consigne malgré une attente de %i ms. Une nouvelle impulsion de PowerKey va être envoyée\n", (modem.repetitions * MODEM_TEMPO_ATTENTE_CHANGEMENT_ETAT * 100)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); #endif // Enregistrement dans l'historique : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_CHANGEMENT_ETAT, 0, modem.consigne, NULL); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT; // Relance d'un cycle avec renvoit d'une impulsion } else modem.sequenceur.compteur = MODEM_TEMPO_ATTENTE_CHANGEMENT_ETAT; // Si non, nouveau quantum d'attente lancé } else // Si le modem est dans l'état souhaité : { #ifdef __DEBUG_MODEM sprintf(tmp, "L'entrée Status du modem SIM7070G est passée à %i, comme demandé, après %ims.", modem.etat, ((++ modem.repetitions) * M_BUS_TEMPO_POST_IMPULSION_ACTIVATION_MODEM * 100)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif modem.etat = modem.consigne; modem.repetitions = 0; // Utile pour vérifier que la liaison est bien établie ensuite. // Enregistrement dans l'historique : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_ETAT, 0, modem.etat, NULL); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT_LIAISON; } } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_INIT_LIAISON : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_SIM_SW, FALSE) == 0) modem.etat_presence_sim = OFF; // Lecture du switch de présence de la carte SIM. 0 est l'adresse de ShipBase else modem.etat_presence_sim = ON; if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_STATUS_MOD, FALSE) == 0) modem.etat = OFF; // Lecture de l'état actuel du modem. 0 est l'adresse de ShipBase else modem.etat = ON; //-------------------------------------------------------------------------------------------- // Préparation des commandes lorsque le modem est ON : if (modem.etat == ON) { //===================================== // Commandes liées à la liaison série : // m_bus_down_commande_libre("AT+IPR=?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne la vitesse de communication // m_bus_down_commande_libre("AT+IPR=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini la vitesse de communication (0=AUTOBAUD). Attention : la modif est conservée après Reset donc la seule façon de revenir en arrière est d'envoyer une nouvelle commande IPR à la bonne vitesse !!!! // m_bus_down_commande_libre("AT+ICF=?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le format (nb de bits, parité et nb de stops) de la communication // m_bus_down_commande_libre("AT+ICF=3", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le format (nb de bits, parité et nb de stops) de communication (défaut=3: 8N1). Attention : la modif est conservée après Reset donc la seule façon de revenir en arrière est d'envoyer une nouvelle commande IPR à la bonne vitesse !!!! m_bus_down_commande_libre("ATE0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Echo OFF } modem.sequenceur.compteur = MODEM_TEMPO_CHECK_INIT_LIAISON; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_COMMANDES_INIT; break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_COMMANDES_INIT : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { if (modem.etat_liaison == OFF) // Si la liaison n'est pas encore établie : { if (++ modem.repetitions >= MODEM_MAX_REPETITIONS_TEMPO_CHECK_INIT_LIAISON) // Vérification que la durée maximale d'attente n'est pas atteinte. Si oui : { #ifdef __DEBUG_MODEM sprintf(tmp, "La liaison avec le modem SIM7070G n'est toujours étabie malgré une attente de %i ms. Une nouvelle impulsion de PowerKey va être envoyée\n", (modem.repetitions * MODEM_TEMPO_CHECK_INIT_LIAISON * 100)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); #endif // Enregistrement dans l'historique : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_ETABLISSEMENT_LIAISON, 0, 0, NULL); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT; // Relance d'un cycle avec renvoit d'une impulsion } else modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT_LIAISON; // Si non, envoi d'une nouvelle commande et nouveau quantum d'attente lancé break; } else ; // Si la liaison est établie, on poursuit sur cet état de séquenceur //-------------------------------------------------------------------------------------------- // Préparation des commandes lorsque le modem est ON : if (modem.etat == ON) { //===================================== // Commandes liées à la liaison série : m_bus_down_commande_libre("AT+IFC=2,2", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le contrôle du flux dans le sens modem => UC puis UC=>modem (0:pas de ctrl de flux, 1=Xon-Xoff, 2=hardware) // m_bus_down_commande_libre("AT+IFC?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne l'état actuel du contrôle de flux m_bus_down_commande_libre("AT+CFGRI=1", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le fonctionnement de la ligne RI (0: Off=default, 1:TCPIP, FGTP and URC, 2:only TCPIP). Important pour le traitement FTP. //========================================== // Commandes de lecture de la configuation : // m_bus_down_commande_libre("AT+CGMI", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le nom du fabricant du modem. En l'occurrence, 'SIMCOM_Ltd'. NB : supprimé car info visible dans CGMR // m_bus_down_commande_libre("AT+CGMM", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le nom du modèle du modem. En l'occurrence, 'SIMCOM_SIM7070'. NB : supprimé car info visible dans CGMR m_bus_down_commande_libre("AT+CGMR", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne la version de soft du modem. En septembre 2020, 'Revision:1951B04SIM7070'. m_bus_down_commande_libre("AT+CGSN", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le n° d'IMEI (International mobile equipment identity) du modem (et pas de la carte SIM). Exemple : 869777046749118 //========================================== // Mise à l'heure du modem : char dt_tmp[MAX_LONGUEUR_DATE_COURANTE]; date_courante(dt_tmp, FALSE, FORMAT_DATE_COURANTE_MODEM); sprintf(tmp, "AT+CCLK=\"%s\"", dt_tmp); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Configuration de la date et de l'heure du modem //=========================== // Commandes d'init du GNSS : // Attention : a priori, la configuration du GNSS doit se faire avant de l'avoir activé // m_bus_down_commande_libre("AT+SGNSCFG=?", M_BUS_PROTOCOLE_NORMAL, 0, -1); m_bus_down_commande_libre("AT+SGNSCFG=\"MODE\",0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // 0: Start GPS and GLONASS constellation, // 1: Start GPS and GALILEO constellation, // 2: Start GPS and BEIDOU constellation, // 3: Start GPS and QZSS constellation. // m_bus_down_commande_libre("AT+SGNSCFG=\"ADSS\",0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // 0: Do not delete any data. Perform hot start if the conditions are permitted after starting GNSS, // 1: Delete some related data. Perform warm start if the conditions are permitted after starting GNSS, // 2: Delete all assistance data except almanac data. Enforce cold start after starting GNSS, // 3: Delete all assistance data except almanac and sv health data. Enforce xtra cold start after starting GNSS, 4: Delete all assistance data. Enforce reset start after starting GNSS. // Attention : la commande 'AT+SGNSCFG=\"ADSS\",0' est acceptée mais génère ensuite des ERRORS dès qu'on essaie d'activer l'alimentation du GNSS (CGNSPWR=1)...!! // m_bus_down_commande_libre("AT+SGNSCFG?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // m_bus_down_commande_libre("AT+CGNSPWR?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Demande de l'état actuel de la commande } //----------------------------------------------------------------------------------- // Préparation des commandes lorsque le modem est ON et dispose d'une carte SIM : if ((modem.etat == ON) && (modem.etat_presence_sim == ON)) { //========================================== // Commandes de lecture de la configuation : m_bus_down_commande_libre("AT+CPIN?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne l'état de la carte SIM. En l'occurence, 'READY' indiquant qu'elle n'attend ni user ni pwd. m_bus_down_commande_libre("AT+CIMI", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le n° d'IMSI (International Mobile Subscriber Identity - code utilisé par l'opérateur pour identifier la carte SIM sur le réseau). Exemple : 204080811667681 m_bus_down_commande_libre("AT+CCID", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le n° d'ICCID (Integrated Circuit Card ID - numéro de série de la carte SIM). Exemple : 8931080119091427723 (attention : il y a souvent un 'f' à la fin....) // m_bus_down_commande_libre("AT+CNUM", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le n° de téléphone associé à la carte. ATTENTION : cette commande ne fonctionne pas sur la plupart des cartes SIM... //======================================== // Commandes d'init des SMS : m_bus_down_commande_libre("AT+CMGF=1", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le mode de traitement des SMS en PDU (0=default) ou TEXTE (1) // m_bus_down_commande_libre("AT+CMGF?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Reli le mode traitement des SMS en cours // m_bus_down_commande_libre("AT+CSMS?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le type de SMS qui sont acceptés (par défaut, tous). // m_bus_down_commande_libre("AT+CSCA?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le Service Center Address (SCA) soit le numéro de téléphone servant de serveur de SMS. A priori : +31653131313 // m_bus_down_commande_libre("AT+CPMS?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne l'état de la mémoire dédiée aux SMS. Exemple de réponse : '+CPMS: "SM",0,25,"SM",0,25,"SM",0,25' soit 0 présents et 25 de capacité dans les 3 zones (qui n'en n'est qu'une si je comprends bien...) // m_bus_down_commande_libre("AT+CNMI=0,0?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Positionne le New SMS Message Indications : mode=0 pas d'envoi de code non sollicité, mt=0 No SMS-DELIVER indications are routed to the TE. //======================================== // Commandes d'init de la liaison 4G-LTE : modem.etat_4g_lte = OFF; // m_bus_down_commande_libre("AT+CGATT?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Demande de l'état d'attachement au réseau GPRS (0: detached, 1:attached) // m_bus_down_commande_libre("AT+CFUN?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Permet de visualiser le mode de fonctionnement (0: Minimum, 1:Normal, 4:Avion, 6: Reset, 7:Offline). Default=1. // m_bus_down_commande_libre("AT+CNACT?", M_BUS_PROTOCOLE_NORMAL, 0, -1); m_bus_down_commande_libre("AT+CNACT=0,2", M_BUS_PROTOCOLE_NORMAL, MODEM_TIME_OUT_ATTENTE_FIN_INIT, 0); // Activation du PDP #0 (ils vont de 0 à 3) en mode AUTOACTIVE (nécessaire dans le cas où l'activation n'est pas possible immédiatement ; en particulier lors de la première mise en service de la carte SIM). // m_bus_down_commande_libre("AT+CNACT?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Relecture de l'état des PDP modem.sequenceur.compteur_time_out = MODEM_TIME_OUT_ATTENTE_FIN_INIT; // Permet de s'assurer qu'on reçoit bien le "+APP PDP: 0,ACTIVE" ou "+APP PDP: 0,DEACTIVE" dans un délai acceptable (cf explications sur le define de la constante dans 'modem.h') } else modem.sequenceur.compteur_time_out = 0; // Dans le cas où on n'a pas de carte SIM (ou pas de modem actif), il ne faut pas lancer le time-out car personne ne le remettra à 0 par anticipation (fonctionnement normal à la réception de "+APP PDP: 0,ACTIVE" par exemple). modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_ATTENTE_FIN_INIT; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_ATTENTE_FIN_INIT : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if ((m_bus.down.ptr_debut_buffer_cdes == m_bus.down.ptr_fin_buffer_cdes) && // Attente que toutes les commandes dans le buffer de cdes soient traitées... (m_bus.down.flag_attente_reponse == FALSE) && // ... et que la commande en cours ait fini d'être émise (tout cela n'est pas obligatoire mais permet d'évitre d'empiler trop de commandes) (modem.sequenceur.compteur_time_out == 0)) { modem.sequenceur.compteur = MODEM_PERIODE_SURVEILLANCE_MODEM; // Permet de temporiser un peu la première demande modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; } break; } //========================================================================================================================================================= // Tache de Fond : //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_REPOS : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { modem.sequenceur.compteur = MODEM_PERIODE_SURVEILLANCE_MODEM; // Relance du timer //------------------------------------------------------------------- // Présence de la carte SIM : if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_SIM_SW, FALSE) == 0) // Lecture du switch de présence de la carte SIM. 0 est l'adresse de ShipBase { modem.etat_presence_sim = OFF; modem.etat_4g_lte = OFF; } else modem.etat_presence_sim = ON; //------------------------------------------------------------------- // Surveillance de l'état du modem : if (lecture_entree(TYPE_ENTREES_GPIO, 0, CM3P_DI_STATUS_MOD, FALSE) == 0) // Lecture de l'état actuel du modem. 0 est l'adresse de ShipBase { modem.etat = OFF; modem.etat_4g_lte = OFF; } else modem.etat = ON; //------------------------------------------------------------------- // Détection d'un changement d'état non sollicité du modem : if (modem.etat != modem.consigne) // On vérifie que l'état actuel correspond bien à la consigne demandée (changement de consigne ou changement d'état non sollicité) { // Enregistrement dans l'historique : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_ETAT_NON_SOLLICITE, 0, modem.etat, NULL); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT; // Lancement d'une séquence d'activation selon la consigne demandée break; // Pas la peine de traiter la suite... } #ifdef __DEBUG_MODEM sprintf(tmp, "Surveillance du modem : présence SIM= %i, état_4G= %i, niveau RSRP=%i dbm, état_gnss=%i, compteur GNSS= %i, compteur Upload= %i, compteur Download= %i", modem.etat_presence_sim, modem.etat_4g_lte, entrees.predefinie.compagnon.rsrp, gnss.etat, modem.compteur_periode_gnss, modem.compteur_periode_upload, modem.compteur_periode_download); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif //------------------------------------------------------------------- // GNSS : if (modem.etat == ON) { if (modem.compteur_periode_gnss > 0) modem.compteur_periode_gnss --; if (modem.compteur_periode_gnss == 0) { if (modem.etat_4g_lte == ON) modem.compteur_periode_gnss = ((MODEM_GNSS_PERIODE_ACQUISITION_AVEC_4G_LTE * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); // Le x10 = x1000ms / 100ms et provient du fait que la période de check est en secondes alors que MODEM_PERIODE_SURVEILLANCE_MODEM est en 100ms else modem.compteur_periode_gnss = ((MODEM_GNSS_PERIODE_ACQUISITION_SANS_4G_LTE * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); // Le x10 = x1000ms / 100ms et provient du fait que la période de check est en secondes alors que MODEM_PERIODE_SURVEILLANCE_MODEM est en 100ms modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_GNSS_ACTIVATION; break; // Permet de ne pas traiter les tâches suivantes si on lance un cycle GNSS } } //------------------------------------------------------------------- // Vérification si des fichiers doivent être uploadés et upload : if (modem.etat_4g_lte == ON) { if (modem.compteur_periode_upload > 0) modem.compteur_periode_upload --; if (modem.compteur_periode_upload == 0) { #ifdef __DEBUG_MODEM_FTP_UPLOAD affichage_debug(__FILE__, __LINE__, "---------------------Début accès FTP PUT---------------------------------------", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif strcpy(upload.nom_fichier_source, ""); struct dirent *dir; // Les types 'dirent' ou 'DIR' sont déclarés dans le header DIR *repertoire = opendir(REPERTOIRE_SERVER_UPLOAD); // Ouverture du répertoire if (repertoire != NULL) { while ((dir = readdir(repertoire)) != NULL) // Lecture de tous les fichiers du répertoire pour rechercher le plus ancien : { if (strcmp(dir->d_name, ".") == 0) continue; // On ne traite pas le répertoire '.' ... if (strcmp(dir->d_name, "..") == 0) continue; // ... ni le répertoire '..' if (strcmp(upload.nom_fichier_source, "") == 0) strcpy(upload.nom_fichier_source, dir->d_name); // Cas où c'est le premier fichier valide trouvé else if (strcmp(upload.nom_fichier_source, dir->d_name) > 0) strcpy(upload.nom_fichier_source, dir->d_name); // Si ce n'est pas le premier fichier, on garde le plus ancien (par son nom, ATTENTION). } closedir(repertoire); // Fermeture du répertoire } // Préparation de l'upload quand il y a un fichier disponible : if (strcmp(upload.nom_fichier_source, "") != 0) { sprintf(upload.nom_repertoire_source, "%s/", REPERTOIRE_SERVER_UPLOAD); sprintf(upload.nom_repertoire_destination, "%s/%s%s/", REPERTOIRE_SERVEUR, conversion_adresse_mac_chaine(systeme.modules_physiques[0].adresse_mac, tmp, '_'), REPERTOIRE_UPLOAD_DESTINATION); strcpy(upload.nom_fichier_destination, upload.nom_fichier_source); // Par défaut, le nom est conservé lors du transfert modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT; break; // Si on part sur du FTP PUT, on ne traite pas les fonctions de tâche de fond suivantes } else // On ne relance la tempo que lorsque'on n'a plus de fichier à envoyer (on en envoi donc 1 à chaque MODEM_PERIODE_SURVEILLANCE_MODEM tant qu'il y en a - et si un période GNSS n'est pas arrivée entre temps). Cela évite d'attendre trop longtemps quand on a du retard d'envoi... { modem.compteur_periode_upload = ((MODEM_UPLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); } } } //---------------------------------------------------------------------------- // Vérification si des fichiers doivent être downloadés et download : if ((modem.etat_4g_lte == ON) && (systeme.flag_new_download == FALSE)) // Tant que le chargement précédent n'est pas terminé, on ne regarde pas si un autre doit être fait... { if (modem.compteur_periode_download > 0) modem.compteur_periode_download --; if (modem.compteur_periode_download == 0) { modem.compteur_periode_download = ((MODEM_DOWNLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); // Le x10 = x1000ms / 100ms et provient du fait que la période de check est en secondes alors que MODEM_PERIODE_SURVEILLANCE_MODEM est en 100ms sprintf(download.nom_repertoire_source, "%s/%s%s/", REPERTOIRE_SERVEUR, conversion_adresse_mac_chaine(systeme.modules_physiques[0].adresse_mac, tmp, '_'), REPERTOIRE_DOWNLOAD_SOURCE); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_LANCEMENT; break; } } //------------------------------------------------------------------- // Suivi du modem en tâche de fond : if (modem.etat_presence_sim == ON) { // Demandes d'infos sur le réseau 4G-LTE : m_bus_down_commande_libre("AT+CPSI?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Demande des informations de connexions au réseau m_bus_down_commande_libre("AT+CGNAPN", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Retourne le nom du réseau APN. En l'occurence "fixedip.m2m" ou " matooma.m2m" m_bus_down_commande_libre("AT+CNACT?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Relecture de l'état des PDP // Lecture des SMS : m_bus_down_commande_libre("AT+CMGL=\"REC UNREAD\", 1", M_BUS_PROTOCOLE_NORMAL, 200, -1); // Demande la liste des SMS Reçus et Non Lus, et 0= on les passe en 'lus' (default) ou 1= on ne change pas leur état } else { // m_bus_down_commande_libre("AT+CCLK?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Lecture de la date et de l'heure du modem (NB : le retour n'est pas traité...! Sert uniquement à l'analyseur) } } break; } //========================================================================================================================================================= // GNSS : //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_GNSS_ACTIVATION: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { #ifdef __DEBUG_MODEM_GNSS affichage_debug(__FILE__, __LINE__, "---------------------Début acquisition GNSS---------------------------------------", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif // Coupure de la connexion 4G car le GNSS ne peut fonctionner simultanément : modem.etat_4g_lte = OFF; m_bus_down_commande_libre("AT+CFUN=4", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Turn Off RF modem part (permet au GNSS de fonctionner) modem.compteur_cycles_gnss = 0; modem.compteur_cycles_valides_gnss = 0; modem.sequenceur.compteur = MODEM_GNSS_TEMPO_ATTENTE_COUPURE_4G_LTE; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_COUPURE_4G_LTE; break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_COUPURE_4G_LTE: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { m_bus_down_commande_libre("AT+CGNSPWR=1", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Turn On GNSS power supply modem.sequenceur.compteur = MODEM_GNSS_TEMPO_ATTENTE_ACTIVATION; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_LECTURE; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_LECTURE: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { #ifdef __DEBUG_MODEM_GNSS sprintf(tmp, "Lancement du cycle GNSS n° %3i (%2i sont déjà valides)", modem.compteur_cycles_gnss, modem.compteur_cycles_valides_gnss); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif m_bus_down_commande_libre("AT+CGNSINF", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Lecture des infos de localisation modem.sequenceur.compteur_time_out = MODEM_GNSS_TIME_OUT_ATTENTE_LECTURE; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_GNSS_LECTURE; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_GNSS_LECTURE: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur_time_out == 0) { //======================================= // Fin de la période d'acquisition GNSS : if (modem.compteur_cycles_valides_gnss >= MODEM_GNSS_NOMBRE_CYCLES_VALIDES) { gnss.etat = ON; // SDIR #107 : c_bus_down_commande_config_date_heure(); // Mise à jour de la date/heure du Compagnon (toujours en UTC) // Fin SDIR m_bus_down_commande_libre("AT+CGNSPWR=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Turn Off GNSS power supply sprintf(tmp, "L'acquisition GNSS vient de se terminer avec %i cycles valides sur %i cycles au total", modem.compteur_cycles_valides_gnss, (modem.compteur_cycles_gnss+1)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); // Réactivation de la connexion 4G : m_bus_down_commande_libre("AT+CFUN=1,1", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Reset soft du modem, obligatoire après la coupure au début de la période GNSS modem.sequenceur.compteur = MODEM_TEMPO_APRES_RESET_SOFT; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT; } else { //=================================== // Nouveau cycle d'acquisition GNSS : if (++ modem.compteur_cycles_gnss <= MODEM_GNSS_MAX_NOMBRE_CYCLES) { modem.sequenceur.compteur = MODEM_GNSS_TEMPO_ENTRE_LECTURES; // Petite temporisation pour éviter d'envoyer les commandes en rafale... modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_LECTURE; } //=========================================== // Abandon de la période d'acquisition GNSS : else { sprintf(tmp, "Abandon de l'acquisition GNSS car %i cycles au total (pour %i valides)", modem.compteur_cycles_gnss, modem.compteur_cycles_valides_gnss); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); gnss.etat = OFF; enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_GNSS, 0, modem.compteur_cycles_valides_gnss, NULL); m_bus_down_commande_libre("AT+CGNSPWR=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Turn Off GNSS power supply // Réactivation de la connexion 4G : m_bus_down_commande_libre("AT+CFUN=1,1", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Reset soft du modem, obligatoire après la coupure au début de la période GNSS modem.sequenceur.compteur = MODEM_TEMPO_APRES_RESET_SOFT; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT; } } } break; } //========================================================================================================================================================= // FTP GET : // Cette séquence consiste à : // - créer un fichier '__list_download_serveur.txt' dans le répertoire local 'download' // - le remplir avec le retour du serveur sur son répertoire 'download' (format texte du type 'ls -l) // - établir la liste des fichiers à télécharger (ceux présents sur le serveur et pas localement OU ceux présents sur le serveur et localement mais // avec des tailles différentes) et des fichiers à supprimer localement (présents localement mais plus présents sur le serveur). // - supprimer localement les fichiers à supprimer // - télécharger les fichiers à télécharger. // NB : l'historique contient toutes les opérations de téléchargement et de suppression. C'est la seule trace après ces opérations. //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_LANCEMENT: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD affichage_debug(__FILE__, __LINE__, "---------------------Début accès FTP GET---------------------------------------", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif // Ouverture du fichier à écrire localement : sprintf(tmp, "%s/%s", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); download.ptr_fichier_destination = fopen(tmp, "w"); // Ouverture du fichier en "écriture"... if (download.ptr_fichier_destination == NULL) { sprintf(tmp, "L'ouverture du fichier de LIST '%s/%s' n'est pas possible. La liste du répertoire download du serveur est abandonnée...", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_LIST_NOK, 0, 0, tmp); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } else { fclose(download.ptr_fichier_destination); // Fermeture du fichier download.taille_fichier_local = 0; download.no_paquet = 0; } m_bus_down_commande_libre("AT+FTPCID=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Bearer profile identifier refer to AT+CNACT sprintf(tmp, "AT+FTPSERV=\"%s\"", SERVEUR_FTP_ADRESSE); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini l'adresse du serveur FTP à joindre sprintf(tmp, "AT+FTPUN=\"%s\"", SERVEUR_FTP_USER); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom d'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPPW=\"%s\"", SERVEUR_FTP_PASSWORD); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le pwd de l'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPGETPATH=\"%s\"", download.nom_repertoire_source); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le path du répertoire à lister m_bus_down_commande_libre("AT+FTPLIST=1", M_BUS_PROTOCOLE_NORMAL, MODEM_DOWNLOAD_TIME_OUT_ATTENTE_INFO, 0); download.nb_fichiers_liste = 0; // Permet de démarrer le cycle à jour download.fichier_en_cours = -1; // Marqueur pour indiquer qu'aucun fichier n'a été téléchargé pour le moment download.taille_buffer_rx = 0; // Est nécessaire pour que la fonction M_COMMANDE_FTPLIST_1 de m_commandes.c focntionne correctement au démarrage download.taille_buffer_tx = MODEM_MAX_TAILLE_TRAME_FTPLIST; // Idem. download.status_get = -1; // Idem. modem.sequenceur.compteur = MODEM_DOWNLOAD_TIME_OUT_ATTENTE_INFO; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_INFORMATION; break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_INFORMATION: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { switch(download.status_get) { case 1: // Cas normal, l'échange peut continuer : { download.taille_buffer_tx = MODEM_MAX_TAILLE_TRAME_FTPLIST; sprintf(tmp, "AT+FTPLIST=2,%i", download.taille_buffer_tx); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, MODEM_DOWNLOAD_TIME_OUT_ATTENTE_LISTE, 0); modem.sequenceur.compteur = MODEM_DOWNLOAD_TIME_OUT_ATTENTE_LISTE; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_LISTE; break; } case -1 : // Aucun retour n'a été reçu à la fin du time-out : { sprintf(tmp, "Aucun retour de la fonction LIST à l'expiration du time-out. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } case 0 : // Aucune donnée n'est dispo. Le transfert est terminé : { sprintf(tmp, "Retour de la fonction LIST indiquant qu'aucune données n'est disponible. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } default : // Si > 1, erreur de GET { sprintf(tmp, "Retour en erreur de la fonction LIST (code d'erreur = %i). Le transfert est arrêté.", download.status_get); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } } } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_LISTE: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { switch(download.status_get) { case 1: // Cas normal, l'échange peut continuer immédiatement : { if (download.taille_buffer_rx > 0) // On passe ici plusieurs fois en fin de transfert en attente de 'FTPGET: 1,0' et dans ce cas, ce ne sont pas de 'vrais' blocs qui sont reçus... { if ((download.no_paquet ++) >= MODEM_MAX_NOMBRE_PAQUETS_LIST) { sprintf(tmp, "Le nombre de paquets reçus par la fonction LIST est trop important (nombre = %i). Le transfert est arrêté.", download.no_paquet); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; } #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Réception du paquet n° %4i avec %4i octets (total reçu = %8i octets)", download.no_paquet, download.taille_buffer_rx, download.taille_fichier_local); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif } modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_INFORMATION; // NB : on ne recharge pas le compteur car on veut que la prochaine commande FTPLIST=2,xxx soit faite dès que possible. // NB 2 : on ne peut pas faire tout à fait comme en mode GET car on ne connait pas à l'avance la taille des données à recevoir... break; } case 0 : // Plus aucune donnée n'est dispo. Le transfert est terminé : { // On s'assure que la libération du protocole M-Bus a bien été faite (on risque sinon de rester bloqué en mode FLUX_BINAIRE) : m_bus.down.flag_attente_reponse = FALSE; // Libération du séquenceur d'émission pour passer à la commande suivante m_bus.protocole_tx.repetitions = 0; // RAZ du nombre de répétitions pour usage futur m_bus.protocole_tx.sequenceur.compteur_time_out = 0; // RAZ du time-out pour éviter tout déclenchement a postériori. // Listing des fichiers locaux : struct dirent *dir; // Les types 'dirent' ou 'DIR' sont déclarés dans le header FILE *ptr_fic; char nom_fic[MAX_LONGUEUR_NOM_FICHIER]; DIR *repertoire = opendir(REPERTOIRE_SERVER_DOWNLOAD); // Ouverture du répertoire if (repertoire != NULL) { while ((dir = readdir(repertoire)) != NULL) // Lecture de tous les fichiers du répertoire : { if (strcmp(dir->d_name, ".") == 0) continue; // On ne traite pas le répertoire '.' ... if (strcmp(dir->d_name, "..") == 0) continue; // ... ni le répertoire '..' strcpy(download.liste_fichiers[download.nb_fichiers_liste].nom, dir->d_name); sprintf(nom_fic, "%s/%s", REPERTOIRE_SERVER_DOWNLOAD, download.liste_fichiers[download.nb_fichiers_liste].nom); ptr_fic=fopen(nom_fic,"rb"); if(ptr_fic) { fseek (ptr_fic, 0, SEEK_END); // Place le curseur à la fin du fichier download.liste_fichiers[download.nb_fichiers_liste].taille_locale = ftell(ptr_fic); // Lit la position du curseur, donc la taille... fclose (ptr_fic); } char * p; p = strstr(download.liste_fichiers[download.nb_fichiers_liste].nom, MARQUEUR_FICHIER_DONE); // Si le fichier local contient l'extension '.DONE'... if (p != NULL) *p = 0; // ... on supprime le suffixe. Cela permet de ne pas recharger un fichier traité, sauf si sa taille a changée. download.liste_fichiers[download.nb_fichiers_liste].taille_serveur = 0; // Par défaut. download.liste_fichiers[download.nb_fichiers_liste].flag_a_telecharger = FALSE; // Par défaut (si on ne trouve pas de fichier identique sur le serveur) : on ne télécharge pas le fichier... download.liste_fichiers[download.nb_fichiers_liste].flag_a_supprimer = TRUE; // ... et on le supprime à la fin. // Cas particulier du fichier contenant le listing du serveur et qu'on conserve (alors qu'il n'est pas présent sur le serveur) : if (strcmp(dir->d_name, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR) == 0) download.liste_fichiers[download.nb_fichiers_liste].flag_a_supprimer = FALSE; if (++ download.nb_fichiers_liste >= MAX_NOMBRE_FICHIERS_DOWNLOAD) // Cas où le nombre de fichiers est trop important dans le épertoire pour pouvoir être traité... { enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_LOCAUX_TROP_ELEVE, 0, MAX_NOMBRE_FICHIERS_DOWNLOAD, NULL); break; } } closedir(repertoire); // Fermeture du répertoire } // Ouverture du fichier de LIST : sprintf(tmp, "%s/%s", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); download.ptr_fichier_destination = fopen(tmp, "r"); // Ouverture du fichier en "lecture"... if (download.ptr_fichier_destination == NULL) { sprintf(tmp, "L'ouverture du fichier de LIST '%s/%s' n'est pas possible en lecture. L'utilisation de LIST est abandonnée...", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_LIST_NOK, 0, 0, tmp); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } else { while (fscanf(download.ptr_fichier_destination,"%*s %d %*s %*s %d %*s %*s %*s %s", &type, &taille, nom) != EOF) { if (type != 1) continue; // Seul les lignes avec 'type=1' correspondent à des fichiers (2= sous-répertoire, 3= répertoire actuel (.) et 4= répertoire parent (..)) if (taille == 0) // Si le fichier est de taille nulle, on ne stocke pas cette entrée (on ne télécharge pas des fichiers vides...) : { sprintf(tmp, "Le fichier [%s] a une taille de 0 octets => le fichier ne pourra pas être téléchargé...\n", nom); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); continue; } else if (taille > MAX_TAILLE_FICHIER_DOWNLOAD) // Si le fichier est trop gros, on ne stocke pas cette entrée car on ne pourra pas télécharger un tel fichier : { sprintf(tmp, "Le fichier [%s] a une taille de %i octets => le fichier est trop gros (max = %i octets) et ne pourra pas être téléchargé...\n", nom, taille, MAX_TAILLE_FICHIER_DOWNLOAD); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); continue; } else // Si c'est un fichier "normal", on vérifie s'il existe localement ou pas pour savoir s'il va falloir le télécharger { for (i = 0; i < download.nb_fichiers_liste; i++) // Boucle sur tous les fichiers locaux : { if (strcmp(nom, download.liste_fichiers[i].nom) == 0) // Cas où le fichier existe localement ET sur le serveur : { download.liste_fichiers[i].taille_serveur = taille; if (download.liste_fichiers[i].taille_locale == taille) // Si les tailles sont identiques : { download.liste_fichiers[i].flag_a_supprimer = FALSE; // On va conserver le fichier local (et ne pas télécharger celui du serveur, tel que prévu par défaut) } else // Si les tailles sont différentes : { download.liste_fichiers[i].flag_a_telecharger = TRUE; // on va télécharger la version du serveur (et la version locale sera supprimée, prévu par défaut). } break; } } if (i == download.nb_fichiers_liste) // Si on ne l'a pas trouvé dans la liste, c'est qu'il existe sur le serveur mais PAS localement : { strcpy(download.liste_fichiers[download.nb_fichiers_liste].nom, nom); // On mémorise son nom download.liste_fichiers[download.nb_fichiers_liste].taille_serveur = taille; // ... et sa taille download.liste_fichiers[download.nb_fichiers_liste].taille_locale = 0; download.liste_fichiers[download.nb_fichiers_liste].flag_a_telecharger = TRUE; // Et on mentionne qu'il doit être chargé depuis le serveur download.liste_fichiers[download.nb_fichiers_liste].flag_a_supprimer = FALSE; // .. et qu'on n'a pas à le supprimer localement (en même temps, il n'existe pas encore...). if (++ download.nb_fichiers_liste >= MAX_NOMBRE_FICHIERS_DOWNLOAD) // Cas où le nombre de fichiers est trop important dans le répertoire distant pour pouvoir être traité... { enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_SERVEUR_TROP_ELEVE, 0, MAX_NOMBRE_FICHIERS_DOWNLOAD, NULL); break; } } } } fclose(download.ptr_fichier_destination); // Fermeture du fichier } #ifdef __DEBUG_MODEM_FTP_DOWNLOAD char chaine_tmp[50]; sprintf(tmp, "Synthèse de l'état des répertoires de download, local et distant AVANT traitement :"); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); for (i = 0; i < download.nb_fichiers_liste; i++) // Boucle sur tous les fichiers locaux pour supprimer ceux qui doivent l'être { sprintf(tmp, "\t%-25s\t", download.liste_fichiers[i].nom); if (download.liste_fichiers[i].taille_locale != 0) sprintf(chaine_tmp, "\tlocal = %8i octets", download.liste_fichiers[i].taille_locale); else sprintf(chaine_tmp, "\t......................."); strcat(tmp, chaine_tmp); if (download.liste_fichiers[i].taille_serveur != 0) sprintf(chaine_tmp, "\t\tserveur = %8i octets", download.liste_fichiers[i].taille_serveur); else sprintf(chaine_tmp, "\t\t........................."); strcat(tmp, chaine_tmp); if (download.liste_fichiers[i].flag_a_supprimer == TRUE) sprintf(chaine_tmp, "\tLocal à supprimer"); else sprintf(chaine_tmp, "\t................."); strcat(tmp, chaine_tmp); if (download.liste_fichiers[i].flag_a_telecharger == TRUE) sprintf(chaine_tmp, "\tServeur à télécharger"); else sprintf(chaine_tmp, "\t....................."); strcat(tmp, chaine_tmp); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); } #endif // Suppression des fichiers locaux qui doivent l'être : for (i = 0; i < download.nb_fichiers_liste; i++) // Boucle sur tous les fichiers locaux pour supprimer ceux qui doivent l'être { if (download.liste_fichiers[i].flag_a_supprimer == TRUE) { sprintf(tmp, "%s/%s", REPERTOIRE_SERVER_DOWNLOAD, download.liste_fichiers[i].nom); remove(tmp); strcat(tmp, MARQUEUR_FICHIER_DONE); remove(tmp); } } modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_RECHERCHE_FICHIER; // Poursuite avec la recherche d'un fichier à récupérer break; } case -1 : // Aucun retour n'a été reçu à la fin du time-out : { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Aucun retour de la fonction LIST à l'expiration du time-out. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } default : // Si > 1, erreur de GET. On va attendre qu'un retour de status_get=1 revienne (cas le plus fréquent) { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Retour en erreur de la fonction LIST (code d'erreur = %i). Le transfert est arrêté.", download.status_get); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } } } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_RECHERCHE_FICHIER: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { // Recherche dans la liste des fichiers, le prochain à télécharger du serveur : download.taille_fichier_serveur = 0; for (i = 0; i < download.nb_fichiers_liste; i++) { if (download.liste_fichiers[i].flag_a_telecharger == TRUE) { strcpy(download.nom_fichier_source, download.liste_fichiers[i].nom); download.taille_fichier_serveur = download.liste_fichiers[i].taille_serveur; download.fichier_en_cours = i; // Permettra de mettre son flag à 0 si le téléchargement est bien allé à son terme (et, indirectement car != -1, permettra de savoir qu'un moins un téléchargement a été réalisé et informer 'systeme.c') break; // On s'arrête dès qu'on a trouvé un fichier à télécharger } } if (download.taille_fichier_serveur > 0) // Si la taille est > 0, c'est qu'un fichier à télécharger a été trouvé { sprintf(download.nom_repertoire_source, "%s/%s%s/", REPERTOIRE_SERVEUR, conversion_adresse_mac_chaine(systeme.modules_physiques[0].adresse_mac, tmp, '_'), REPERTOIRE_DOWNLOAD_SOURCE); sprintf(download.nom_repertoire_destination, "%s/", REPERTOIRE_SERVER_DOWNLOAD); strcpy(download.nom_fichier_destination, download.nom_fichier_source); // Par défaut, le nom est conservé lors du transfert modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_DEBUT_FICHIER; } else // Sinon, c'est qu'il n'y a plus de fichier à télécharger { systeme.flag_new_download = TRUE; // Le principe retenu est qu'après chaque séquence de GET, on alerte gestion_systeme() pour qu'il vérifie si des chargements de code ne sont pas nécessaires. modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_DEBUT_FICHIER: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Connexion FTP pour download du fichier '%s%s' vers le fichier '%s%s'", download.nom_repertoire_source, download.nom_fichier_source, download.nom_repertoire_destination, download.nom_fichier_destination); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif download.secondes_debut = timestamp_secondes(); // Stockage du timestamp de départ // Ouverture du fichier à écrire localement : sprintf(tmp, "%s%s", download.nom_repertoire_destination, download.nom_fichier_destination); download.ptr_fichier_destination = fopen(tmp, "wb"); // Ouverture du fichier en "écriture"... if (download.ptr_fichier_destination == NULL) { sprintf(tmp, "L'ouverture du fichier '%s%s' n'est pas possible. Le download du fichier est abandonné...", download.nom_repertoire_destination, download.nom_fichier_destination); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_NOK, 0, 0, tmp); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } else { fclose(download.ptr_fichier_destination); // Fermeture du fichier download.taille_fichier_local = 0; download.no_paquet = 0; // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_OK, 0, 0, tmp); } m_bus_down_commande_libre("AT+FTPCID=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Bearer profile identifier refer to AT+CNACT sprintf(tmp, "AT+FTPSERV=\"%s\"", SERVEUR_FTP_ADRESSE); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini l'adresse du serveur FTP à joindre sprintf(tmp, "AT+FTPUN=\"%s\"", SERVEUR_FTP_USER); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom d'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPPW=\"%s\"", SERVEUR_FTP_PASSWORD); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le pwd de l'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPGETNAME=\"%s\"", download.nom_fichier_source); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom du fichier à downloader sur le serveur FTP sprintf(tmp, "AT+FTPGETPATH=\"%s\"", download.nom_repertoire_source); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le path du répertoire sur lequel chercher le fichier m_bus_down_commande_libre("AT+FTPGET=1", M_BUS_PROTOCOLE_NORMAL, MODEM_DOWNLOAD_TIME_OUT_ATTENTE_DISPO_DATA, 0); download.taille_buffer_rx = 0; // Est nécessaire pour que la fonction M_COMMANDE_FTPGET_1 de m_commandes.c focntionne correctement au démarrage download.taille_buffer_tx = MODEM_MAX_TAILLE_TRAME_FTPGET; // Idem. download.status_get = -1; // Idem. modem.sequenceur.compteur = MODEM_DOWNLOAD_TIME_OUT_ATTENTE_DISPO_DATA; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_DISPO_DATA; break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_DISPO_DATA: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { switch(download.status_get) { case 1: // Cas normal, l'échange peut continuer : { download.taille_buffer_tx = MODEM_MAX_TAILLE_TRAME_FTPGET; sprintf(tmp, "AT+FTPGET=2,%i", download.taille_buffer_tx); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, MODEM_DOWNLOAD_TIME_OUT_ATTENTE_RECEPTION_DATA, 0); modem.sequenceur.compteur = MODEM_DOWNLOAD_TIME_OUT_ATTENTE_RECEPTION_DATA; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_RECEPTION_DATA; break; } case -1 : // Aucun retour n'a été reçu à la fin du time-out : { sprintf(tmp, "Aucun retour de la fonction GET à l'expiration du time-out. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } case 0 : // Plus aucune donnée n'est dispo. Le transfert est terminé : { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Retour de la fonction GET indiquant qu'aucune données n'est plus disponible. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } default : // Si > 1, erreur de GET. Retour au repos dans le doute. { sprintf(tmp, "Retour en erreur de la fonction GET (code d'erreur = %i). Le transfert est arrêté.", download.status_get); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } } } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_RECEPTION_DATA: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { switch(download.status_get) { case 1: // Cas normal, l'échange peut continuer immédiatement : { download.secondes_fin = timestamp_secondes(); if (download.secondes_fin == download.secondes_debut) download.secondes_fin = download.secondes_debut + 1; // évite les divisions par 0... if (download.taille_buffer_rx > 0) // On passe ici plusieurs fois en fin de transfert en attente de 'FTPGET: 1,0' et dans ce cas, ce ne sont pas de 'vrais' blocs qui sont reçus... { download.no_paquet ++; #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Réception du paquet n° %4i avec %4i octets (total reçu = %8i octets en %4i secondes, soit %5i octets/secondes)", download.no_paquet, download.taille_buffer_rx, download.taille_fichier_local, (uint32_t)(download.secondes_fin - download.secondes_debut), download.taille_fichier_local/(uint32_t)(download.secondes_fin - download.secondes_debut)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif } if (download.taille_fichier_local < download.taille_fichier_serveur) modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_DISPO_DATA; // Cas où le chargement n'est pas terminé, on relance une commande FTPGET=2,1024 pour demander des données supplémentaires. else modem.sequenceur.compteur = MODEM_DOWNLOAD_TIME_OUT_ATTENTE_RECEPTION_DATA; // Cas où on a déjà récupéré toutes les data. Dans ce cas, on réarme juste le time-out puisqu'on devrait recevoir d'ici quelques secondes (8-15s souvent) une réponse 'FTPGET: 1,0' indiquant la fin de l'échange. break; } case 0 : // Plus aucune donnée n'est dispo. Le transfert est terminé : { // On s'assure que la libération du protocole M-Bus a bien été faite (on risque sinon de rester bloqué en mode FLUX_BINAIRE) : m_bus.down.flag_attente_reponse = FALSE; // Libération du séquenceur d'émission pour passer à la commande suivante m_bus.protocole_tx.repetitions = 0; // RAZ du nombre de répétitions pour usage futur m_bus.protocole_tx.sequenceur.compteur_time_out = 0; // RAZ du time-out pour éviter tout déclenchement a postériori. if (download.taille_fichier_local != download.taille_fichier_serveur) // Erreur de téléchargement : { sprintf(tmp, "L'ouverture du fichier '%s%s' indique une taille [%i] différente du fichier sur le serveur [%i]. Le fichier local va être supprimé et le download abandonné...", download.nom_repertoire_destination, download.nom_fichier_destination, download.taille_fichier_local, download.taille_fichier_serveur); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_NOK, 0, 0, tmp); // Suppression du fichier : sprintf(tmp, "%s%s", download.nom_repertoire_destination, download.nom_fichier_destination); remove(tmp); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; } else // Téléchargement Ok : { download.secondes_fin = timestamp_secondes(); if (download.secondes_fin == download.secondes_debut) download.secondes_fin = download.secondes_debut + 1; // évite les divisions par 0... download.liste_fichiers[download.fichier_en_cours].flag_a_telecharger = FALSE; // Enregistrement dans l'historique : sprintf(tmp, "%s", download.nom_fichier_destination); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_OK, 0, (uint16_t)(download.secondes_fin - download.secondes_debut), tmp); t = (uint32_t)(download.secondes_fin - download.secondes_debut); m = (uint8_t)(t / 60); s = (uint8_t)(t % 60); sprintf(tmp, "Le téléchargement du fichier [%s] est terminé en %2i minutes et %2i secondes (soit %i octets à %5i octets/secondes)", download.nom_fichier_source, m, s, download.taille_fichier_local, (download.taille_fichier_local / (uint32_t)(download.secondes_fin - download.secondes_debut))); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); } modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_GET_RECHERCHE_FICHIER; break; } case -1 : // Aucun retour n'a été reçu à la fin du time-out : { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Aucun retour de la fonction GET à l'expiration du time-out. Le transfert est arrêté."); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } default : // Si > 1, erreur de GET. On va attendre qu'un retour de status_get=1 revienne (cas le plus fréquent) { #ifdef __DEBUG_MODEM_FTP_DOWNLOAD sprintf(tmp, "Retour en erreur de la fonction GET (code d'erreur = %i). Le transfert est arrêté.", download.status_get); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); #endif modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } } } break; } //========================================================================================================================================================= // FTP PUT : //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { #ifdef __DEBUG_MODEM_FTP_UPLOAD sprintf(tmp, "Connexion FTP pour upload du fichier '%s%s' vers le fichier '%s%s'", upload.nom_repertoire_source, upload.nom_fichier_source, upload.nom_repertoire_destination, upload.nom_fichier_destination); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif // Ouverture du fichier à lire : // (NB : cette ouverture n'est pas utile à ce moment-là mais il est inutile d'engager une ouverture de session FTP et tout le reste si le fichier n'est pas accessible...) : sprintf(tmp, "%s%s", upload.nom_repertoire_source, upload.nom_fichier_source); upload.ptr_fichier_source = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... if (upload.ptr_fichier_source == NULL) { sprintf(tmp, "L'ouverture du fichier '%s%s' n'est pas possible. L'upload du fichier est abandonné...", upload.nom_repertoire_source, upload.nom_fichier_source); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK, 0, 0, tmp); // RAZ de la tempo d'UPLOAD pour éviter de boucler sur une situation d'erreur (on refera une tendtative mais au prochain cycle) : modem.compteur_periode_upload = ((MODEM_UPLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } else { fseek(upload.ptr_fichier_source, 0, SEEK_END); // Aller à la fin du fichier upload.taille_theorique_fichier = ftell(upload.ptr_fichier_source); // Recherche de la position du curseur (donc la taille du fichier) fclose(upload.ptr_fichier_source); // Fermeture du fichier if ((upload.taille_theorique_fichier == 0) || (upload.taille_theorique_fichier > MAX_TAILLE_FICHIER_UPLOAD)) { if (upload.taille_theorique_fichier == 0) sprintf(tmp, "L'ouverture du fichier '%s%s' indique une taille nulle. Le fichier va être supprimé et l'upload abandonné...", upload.nom_repertoire_source, upload.nom_fichier_source); else sprintf(tmp, "L'ouverture du fichier '%s%s' indique une taille trop importante (%i octets pour max = %i octets). Le fichier va être supprimé et l'upload abandonné...", upload.nom_repertoire_source, upload.nom_fichier_source, upload.taille_theorique_fichier, MAX_TAILLE_FICHIER_UPLOAD); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK, 0, upload.taille_theorique_fichier, tmp); // Suppression du fichier s'il a une taille nulle ou trop importante : sprintf(tmp, "%s%s", upload.nom_repertoire_source, upload.nom_fichier_source); remove(tmp); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_OK, 0, upload.taille_theorique_fichier, tmp); } upload.secondes_debut = timestamp_secondes(); // Stockage du timestamp de départ // m_bus_down_commande_libre("AT+FTPTYPE?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Indique le type de données transmises (default = "I" = Binary, "A" = Ascii) // m_bus_down_commande_libre("AT+FTPMODE?", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Indique dans quel FTP Mode on est (default = 1 = passive, 0 = active). En pratique, Active ne semble pas accepté... m_bus_down_commande_libre("AT+FTPCID=0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Bearer profile identifier refer to AT+CNACT sprintf(tmp, "AT+FTPSERV=\"%s\"", SERVEUR_FTP_ADRESSE); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini l'adresse du serveur FTP à joindre sprintf(tmp, "AT+FTPUN=\"%s\"", SERVEUR_FTP_USER); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom d'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPPW=\"%s\"", SERVEUR_FTP_PASSWORD); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le pwd de l'utilisateur sur le serveur FTP sprintf(tmp, "AT+FTPPUTNAME=\"%s\"", upload.nom_fichier_destination); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom du fichier à charger sur le serveur sprintf(tmp, "AT+FTPPUTPATH=\"%s\"", upload.nom_repertoire_destination); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le path du fichier à charger sur le serveur m_bus_down_commande_libre("AT+FTPPUT=1", M_BUS_PROTOCOLE_NORMAL, MODEM_UPLOAD_TEMPO_ATTENTE_OUVERTURE_SESSION, 0); // Ouverture de la session FTP. upload.ptr_debut = 0; // Début du fichier qui va être lu puis uploadé upload.ptr_fin = 0; // Fin du fichier qui va être lu puis uploadé upload.no_paquet = 0; modem.max_taille_ftp_put = 0; // Permet de vérifier ensuite qu'on a bien reçu une réponse à FTPPUT:1 avec la taille maxi à émettre. upload.taille_fichier_local = 0; upload.taille_fichier_serveur = 0; upload.code_erreur_ftpput_data = M_BUS_ERREUR_AUCUNE; upload.nb_repetitions_total = 0; modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_ATTENTE_OUVERTURE_SESSION; // Cette tempo sera RAZ par la réception d'une réponse '+FTPPUT: 1,1,xxxx'. C'est donc plutôt un time-out... modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_OUVERTURE_SESSION; break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_OUVERTURE_SESSION: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { if ((upload.code_erreur_ftpput_data == M_BUS_ERREUR_AUCUNE) || (upload.code_erreur_ftpput_data == M_BUS_ERREUR_OK)) { if (modem.max_taille_ftp_put == 0) // Si la taille maxi du buffer d'émission n'a pas encore été mis à jour au moins une fois, on va juste demander la fermeture du port car on ne sait pas comment le traiter. { modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FIN_TRANSFERT; break; } upload.nb_repetitions = 0; upload.ptr_debut = upload.ptr_fin; upload.no_paquet ++; // Ouverture du fichier à lire : sprintf(tmp, "%s%s", upload.nom_repertoire_source, upload.nom_fichier_source); upload.ptr_fichier_source = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... if (upload.ptr_fichier_source == NULL) { sprintf(tmp, "L'ouverture du fichier '%s%s' n'est pas possible. L'upload du fichier est abandonné...", upload.nom_repertoire_source, upload.nom_fichier_source); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } fseek(upload.ptr_fichier_source, upload.ptr_debut, SEEK_SET); // Positionne le curseur à 'ptr_debut' du début du fichier upload.lg_buffer = 0; int16_t c; // Ce n'est pas intuitif, mais 'fgetc' retourne un entier (soit 0-255 si un caractère a été lu, soit -1=EOF si erreur) do { c = fgetc(upload.ptr_fichier_source); if (c != EOF) upload.buffer[upload.lg_buffer ++] = (uint8_t) c; else break; } while ((upload.lg_buffer < (MAX_TAILLE_BUFFER_UPLOAD - 2)) && (upload.lg_buffer < (modem.max_taille_ftp_put - 2))); fclose(upload.ptr_fichier_source); // Fermeture du fichier upload.taille_fichier_local += upload.lg_buffer; // Mise à jour de la taille du fichier en cours de transmission if (upload.lg_buffer == 0) // Si le buffer d'émission est vide, c'est qu'on est arrivés à la fin du fichier et on va juste demander la fermeture du port. { upload.secondes_fin = timestamp_secondes(); // Stockage du timestamp de fin if (upload.secondes_fin == upload.secondes_debut) upload.secondes_fin ++; // Permet d'éviter les divisions par 0.... sprintf(tmp, "Le fichier [%s] a été uploadé complètement : %i paquets transférés (dont %i répétés), pour un total de %i octets, en %i secondes (soit %i octets/s) ", upload.nom_fichier_source, upload.no_paquet, upload.nb_repetitions_total, upload.taille_fichier_local, (uint32_t)(upload.secondes_fin - upload.secondes_debut), (uint32_t)(upload.taille_fichier_local/(uint32_t)(upload.secondes_fin - upload.secondes_debut))); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FIN_TRANSFERT; break; } else { #ifdef __DEBUG_MODEM_FTP_UPLOAD sprintf(tmp, "Demande d'émission du paquet n° %i avec %i octets (total transmis = %i octets, soit %.1f%%)", upload.no_paquet, upload.lg_buffer, upload.taille_fichier_local, (float) ((float)(upload.taille_fichier_local * 100)/ (float)upload.taille_theorique_fichier)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); #endif } upload.ptr_fin = upload.ptr_debut + upload.lg_buffer; // Mise à jour du ptr_fin } else { upload.code_erreur_ftpput_data = M_BUS_ERREUR_AUCUNE; upload.nb_repetitions_total ++; if (++upload.nb_repetitions >= MODEM_MAX_REPETITIONS_PAQUET_UPLOAD) // Si on a déjà répété plusieurs fois ce bloc, on fini par arrêter... { sprintf(tmp, "L'émission du paquet n° %i a été répétée %i fois. L'upload est arrêté.", upload.no_paquet, upload.nb_repetitions); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FIN_TRANSFERT; break; } else // Sinon, on répête une fois de plus { sprintf(tmp, "L'émission du paquet n° %i a rencontré une erreur. Il est réémis.", upload.no_paquet); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); } } sprintf(tmp, "AT+FTPPUT=2,%i", upload.lg_buffer); // Demande d'envoi de N octets, correspondant à la longueur du buffer chargé m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // NB : la réponse du modem ne comprenant pas de CR/LF/OK/CR/LF, il faut dire au protocole de ne PAS attendre de réponse. C'est ici qu'on gère l'attente avant de poursuivre. modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_ATTENTE_ACCEPTATION_DONNEES; // Cette tempo est plus un time-out car la tempo sera remise à 0 par la réception de '+FTPPUT: 2,1358' modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_ACCEPTATION_DONNEES; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_ACCEPTATION_DONNEES: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { upload.code_erreur_ftpput_data = M_BUS_ERREUR_AUCUNE; m_bus_down_commande_libre_binaire(M_COMMANDE_FTPPUT_ENVOI_DATA, (char *)upload.buffer, upload.lg_buffer, M_BUS_PROTOCOLE_DATA_BINAIRE, MODEM_UPLOAD_TIME_OUT_ENVOI_DONNEES, 0); // Envoi des N octets. modem.sequenceur.compteur = MODEM_UPLOAD_TIME_OUT_ENVOI_DONNEES; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_OUVERTURE_SESSION; // Retour vers l'envoi du paquet suivant. } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FIN_TRANSFERT: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { m_bus_down_commande_libre("AT+FTPPUT=2,0", M_BUS_PROTOCOLE_NORMAL, 0, -1); // Demande de fermeture de la session FTP modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_ATTENTE_FIN_TRANSFERT; // Cette tempo est en fait un time-out car dès qu'on recevra le retour 'AT+FTTPUT: 1,0', la tempo sera forcée à 0 par 'm_commandes.c'. modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT_CHECK; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT_CHECK: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { upload.status_get_size = 0; // Valeur par défaut (pas d'erreur) sprintf(tmp, "AT+FTPGETNAME=\"%s\"", upload.nom_fichier_destination); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le nom du fichier à charger sur le serveur sprintf(tmp, "AT+FTPGETPATH=\"%s\"", upload.nom_repertoire_destination); m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Défini le path du fichier à charger sur le serveur m_bus_down_commande_libre("AT+FTPSIZE", M_BUS_PROTOCOLE_NORMAL, MODEM_UPLOAD_TIME_OUT_ATTENTE_GETSIZE, -1); // Demande de la taille du fichier sur le serveur. NB : la réponse du modem comprenant un CR/LF/OK/CR/LF situé AVANT la réponse proprement dite, il faut dire au protocole de ne PAS attendre de réponse. C'est ici qu'on gère l'attente avant de poursuivre. modem.sequenceur.compteur = MODEM_UPLOAD_TIME_OUT_ATTENTE_GETSIZE; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_GETSIZE; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_GETSIZE: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { if (upload.status_get_size == 0) // Le code de retour doit être nul (2ième paramètre de retour de la réponse à FTPSIZE) { if (upload.taille_fichier_serveur != upload.taille_fichier_local) { sprintf(tmp, "La taille du fichier sur le serveur FTP (%i) est différente de la taille du fichier uploadé (%i). La transmission est relancée au début...", upload.taille_fichier_serveur, upload.taille_fichier_local); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK, 0, 0, tmp); } else { sprintf(tmp, "L'upload du fichier [%s] a été vérifié (la taille du fichier sur le serveur FTP est identique à la taille du fichier uploadé, soit %i octets). Le fichier source est donc supprimé.", upload.nom_fichier_source, upload.taille_fichier_serveur); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); // Suppression du fichier puisqu'il a été correctement uploadé : if (upload.taille_fichier_serveur != 0) // Ne devrait pas arriver mais permet de ne pas effacer un fichier si sa taille est nulle { // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_FIN_OK, 0, upload.taille_fichier_serveur, tmp); sprintf(tmp, "%s%s", upload.nom_repertoire_source, upload.nom_fichier_source); remove(tmp); } else // Si la taille du fichier est détectée nulle, on enregistre juste la fin du transfert en erreur (cela laisse une chance de l'uploader à nouveau lors du prochain transfert) { // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK, 0, upload.taille_fichier_serveur, tmp); } } } else { sprintf(tmp, "La taille du fichier distant n'a pas pu être établie (retour de FTPSIZE avec code d'erreur : %i). L'upload est abandonné et le fichier local n'est PAS supprimé...", upload.status_get_size); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Enregistrement dans l'historique : sprintf(tmp, "%s", upload.nom_fichier_source); enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK, 0, 0, tmp); // RAZ de la tempo d'UPLOAD pour éviter de boucler sur une situation d'erreur (on refera une tendtative mais au prochain cycle) : modem.compteur_periode_upload = ((MODEM_UPLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM); } modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FERMETURE_SESSION; // Même en cas d'erreur, on revient au repos. Le fichier sera identifié à nouveau et rechargé plus tard. } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FERMETURE_SESSION: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; } break; } //========================================================================================================================================================= // Reset du Modem : //========================================================================================================================================================= //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_DEBUT_IMPULSION_RESET: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_PWKEY_MOD, ON); // 0 est l'adresse de ShipBase #ifdef __DEBUG_MODEM affichage_debug(__FILE__, __LINE__, "Début de l'impulsion de reset du modem SIM7070G", TYPE_MESSAGE_DEBUG_WARNING); #endif modem.sequenceur.compteur = MODEM_TEMPO_IMPULSION_RESET; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_FIN_IMPULSION_RESET; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_MODEM_FIN_IMPULSION_RESET: //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (modem.sequenceur.compteur == 0) { ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_PWKEY_MOD, OFF); // 0 est l'adresse de ShipBase #ifdef __DEBUG_MODEM affichage_debug(__FILE__, __LINE__, "Fin de l'impulsion de reset du modem SIM7070G", TYPE_MESSAGE_DEBUG_WARNING); #endif modem.sequenceur.compteur = MODEM_TEMPO_ATTENTE_RESET; modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_INIT; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- default : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { sprintf(tmp, "L'état du séquenceur Modem est inconnu [%i]. Il est repositionné au REPOS", modem.sequenceur.etat); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_REPOS; break; } } }