//*********************************************************************************************************************************************************************************** // Fichier MAIN.C // // Contient les fonctions principales... //**********************************************************************************************************************************************************************************/ // Include : #include "main.h" // Prototypes : int8_t init(void); void sigintHandler(int dummy); void lancement_reinit(uint16_t tempo); int8_t analyse_signal(void); int8_t init_signaux(void); // Variables globales : /********************************************************************************************************************************************************************************** Fonction MAIN Fonction principale... **********************************************************************************************************************************************************************************/ int main(int argc, char **argv) { // Initialisation globale : if (init() < 0) { if (bdd.pointeur != NULL) // Utile si l'erreur d'init est déclarée avant l'initialisation de la BDD... { // Mise à jour du flag d'état du CM3P (si l'accès à la BDD est possible...) : mysql_query(bdd.pointeur, "UPDATE TB_Systeme SET Valeur='-1' WHERE Nom=Etat_cm3p "); // Ernegistrement dans l'historique (si possible...) : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CONFIGURATION_INVALIDE_DEMARRAGE_IMPOSSIBLE, 0, 0, NULL); } printf("\n\033[31m----------Initialisation arrêtée - Application non lancée----------\033[0m\n\n"); exit (-1); } // Boucle principale : while (1) { synchro_bdd(); gestion_rx_serial0(); gestion_rx_serial1(); gestion_entrees(); gestion_c_bus(); gestion_g_bus(); gestion_m_bus(); gestion_nmea(); gestion_gnss(); gestion_modem(); gestion_capteurs(); // A priori, plus logique de traiter les capteurs avant les circuits gestion_circuits(); gestion_batteries(); gestion_reservoirs(); gestion_events(); gestion_usb(); gestion_reseaux(); gestion_ecran(); gestion_audio(); gestion_systeme(); supervision(); if (analyse_signal() != 0) break; } arret_programme(); } /********************************************************************************************************************************************************************************** Fonction INIT Fonction d'initialisation, elle-même faisaint appel à des fonctions d'initialisation dédiées... **********************************************************************************************************************************************************************************/ int8_t init(void) { char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; //------------------------------------------------------------------------------------------------------------------------------------------------------- // Init système et hardware //------------------------------------------------------------------------------------------------------------------------------------------------------- // Init des variables de supervision de la boucle principale : supervision_boucle_principale.nb_boucles = 0; supervision_boucle_principale.attente_premieres_mesures = SYSTEME_ATTENTE_DEMARRAGE_COMPTEUR_BOUCLE_PRINCIPALE; // Interception des signaux vers la fonction de traitement dédiée (Refer http://en.cppreference.com/w/c/program/signal) : if (init_signaux() < 0) return(-1); // Intitialisation du mode Debug immédiatement (doit être initialisé avant toute autre action pour être disponible dès le départ...) : // Les paramètres correspondants sont en dur (et non en BDD) car s'ils sont corrompus, on peut perdre accès à tout Log, voire planter le démarrage. // De plus, par expérience, on les modifie assez peu... debug.mode = AFFICHAGE_DEBUG_ECRAN_ET_FICHIER; strcpy(debug.nom_fichier, NOM_FICHIER_DEBUG); if (init_affichage_debug() < 0) return(-1); // Recherche de la version matérielle : if (version_compute_module() < 0) return(-1); // Recherche du package en cours (fichier JSON) et du numéro de package en cours (1.2.3) : strcpy(systeme.package_version, "Inconnue"); // Valeur par défaut struct dirent *dir; // Les types 'dirent' ou 'DIR' sont déclarés dans le header DIR *repertoire; repertoire = opendir(REPERTOIRE_VERSIONS_CURRENT); // 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 '..' if (strstr(dir->d_name, MARQUEUR_FICHIER_JSON) != NULL) // Recherche d'un fichier de type JSON { strcpy(systeme.package_version, dir->d_name); *strstr(systeme.package_version, MARQUEUR_FICHIER_JSON) = 0; // Mise à 0 de la première position du marqueur JSON (donc on coupe la chaine avant le '.json'). sprintf(tmp, "Version de package en cours\t\t\t\t\t\t\t: %s", systeme.package_version); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); // Recherche du n° de la version dans le fichier JSON : char nom_fichier[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL + MAX_LONGUEUR_NOM_FICHIER]; struct Struct_json_version json_version; sprintf(nom_fichier, "%s/%s%s", REPERTOIRE_VERSIONS_CURRENT, systeme.package_version, MARQUEUR_FICHIER_JSON); json_version = parse_json_version(nom_fichier, false); strcpy(systeme.package_no_version, json_version.no_version); // NB : si le parsing du fichier JSON a rencontré une erreur, le N° de version remonté est "ERREUR" sprintf(tmp, "N° de version de package en cours\t\t\t\t\t\t: %s", systeme.package_no_version); if (strstr(systeme.package_no_version, "ERREUR") == NULL) affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); else affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); // Si la lecture du fichier de version a rencontré une erreur break; } } closedir(repertoire); // Fermeture du répertoire } // Mise à jour de la version de logiciel : // Cette fonction est délocalisée dans '_version.c' pour être mise à jour à chaque make tout en évitant de tout recompiler systématiquement. // C'est la raison pour laquelle l'affichage de la version se fait ici (et pas dans la fonction) car on veut la garder aussi épurée que possible. sprintf(systeme.cm3p_soft_version, "%s", maj_version_logiciel()); sprintf(tmp, "Version d'application lancée\t\t\t\t\t\t: %s", systeme.cm3p_soft_version); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); // Initialisation du handler de signal SIGALRM et lancement du timer Linux : // Nécessaire d'être lancé 'tôt' car peut être utilisé dans certaines routines d'init. Dans tous les cas, la construction des timers fait que // les variables globales (initialisées à 0 par le compilateur) sont inactives tant qu'aucun gestionnaire ne les charge. if (init_timer() < 0) return(-1); // Initialisation des pointeur physique du SOC du processeur (GPIO, BSC1, SPI, ...) afin d'accéder directement à ses registres : if (init_memory_map() < 0) return(-1); // Initialisation des accès à la base de données (+ lecture de certains paramètres de configuration): // Cette initialisation est faite très 'tôt' car elle ouvre l'accès au mode de débug notamment qui est utilisé ensuite par les différents programmes d'init. if (init_bdd(BDD_MODE_INIT_RESET) < 0) return(-1); // initialisation des sorties : if (init_sorties() < 0) return(-1); // initialisation des entrées : if (init_entrees() < 0) return(-1); // initialisation des UARTs (les GPIOs doivent être configurées AVANT) : if (init_serial0() < 0) return(-1); if (init_serial1() < 0) return(-1); //--------------------------------------------------------- // Activations effectuées directement dans config.txt mais qu'il faut répéter ici sinon le gestionnaire de sortie va les positionner à 0 par défaut... //--------------------------------------------------------- // Ecran : if (init_ecran() < 0) return(-1); // Audio : if (init_audio() < 0) return(-1); // initialisation des ports USB : if (init_usb() < 0) return(-1); //------------------------------------------------------------------------------------------------------------------------------------------------------- // Init des fonctions haut niveau //------------------------------------------------------------------------------------------------------------------------------------------------------- // initialisation du C_Bus : if (init_c_bus() < 0) return(-1); // initialisation du G_Bus : if (init_g_bus() < 0) return(-1); // initialisation du Gnss : if (init_gnss() < 0) return(-1); // initialisation du M_Bus : if (init_m_bus() < 0) return(-1); // initialisation du Modem : if (init_modem() < 0) return(-1); // initialisation du Wifi : if (init_reseaux() < 0) return(-1); // initialisation des capteurs : if (init_capteurs() < 0) return(-1); // initialisation des circuits : if (init_circuits() < 0) return(-1); // initialisation des niveaux : if (init_reservoirs() < 0) return(-1); // initialisation des batteries : if (init_batteries() < 0) return(-1); // initialisation des évènements : if (init_events() < 0) return(-1); // initialisation du réseau NMEA2000 : if (init_nmea() < 0) return(-1); // initialisation Système : if (init_systeme() < 0) return(-1); // Enregistrement dans l'historique de la bonne fin de l'initialisation : enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_REINIT, 0, 0, NULL); affichage_debug(__FILE__, __LINE__, "Fin d'initialisation de tous les gestionnaires", TYPE_MESSAGE_DEBUG_OK); return(0); } //================================================================================================================================================================== // Fonction : Demande une réinit au Compagnon (qui va la temporiser à réception d'environ 5s). Lance un compteur pour laisser tourner l'application pendant // un certain temps avant d'arrêter la boucle principale. // Reçoit : La valeur de tempo, en 100ms (0 = valeur par défaut) // Retour : Rien //================================================================================================================================================================== void lancement_reinit(uint16_t tempo) { char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; systeme.faucheuse.flag_demande_reinit = TRUE; systeme.faucheuse.id_message_user = enregistrement_message_users(MESSAGE_USERS_CODE_REDEMARRAGE, "", MESSAGE_USERS_ETAT_ALERTE); if (tempo == 0) systeme.faucheuse.compteur = SYSTEME_TEMPO_ATTENTE_REINIT_PAR_DEFAUT; else systeme.faucheuse.compteur = tempo; c_bus_down_commande_reset(); // Demande de réinit (elle est temporisée au niveau du Compagnon pour permettre le traitement des dernières fonctions) enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_DEMANDE_REINIT, 0, 0, NULL); // Enregistrement dans l'historique sprintf(tmp, "\t----------------------------------------------------------------------------"); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); sprintf(tmp, "\t!!!!! Une demande de réinit a été lancée et sera effective dans %i ms !!!!", (systeme.faucheuse.compteur * 100)); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); sprintf(tmp, "\t----------------------------------------------------------------------------"); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); } //================================================================================================================================================================== // Fonction : Analyse la valeur de signal reçu et renvoit une valeur non nulle si le signal est considéré comme devant arrêter l'application (notamment SIGINT qui // est reçu suite à l'appui sur CTRL+C au clavier). Dans tous les cas, la réception du signal est archivé dans l'historique. // Reçoit : Rien // Retour : 0 si pas de signal d'arrêt reçu, -1 si un signal d'arrêt a été reçu //================================================================================================================================================================== int8_t analyse_signal(void) { char commande[MAX_TAILLE_COMMANDE_SHELL]; int8_t retour = 0; if (systeme.faucheuse.flag_demande_reinit == TRUE) { if (systeme.faucheuse.compteur == 0) { systeme.faucheuse.flag_demande_reinit = FALSE; // Utile surtout en debug... maj_message_users(systeme.faucheuse.id_message_user, "", MESSAGE_USERS_ETAT_SUPPRESSION); // Suppression du message juste avant la réinit... sprintf(commande, "sudo sync"); // Permet de s'assurer que tous les fichiers sont bien répliqués sur le disque. commande_shell(commande); return (-1); // On est arrivé au bout... C'est la fin. } } if (systeme.faucheuse.signal_recu == SYSTEME_SIGNAL_AUCUN) return (0); // Cas normal // Cas où on a reçu un signal : switch(systeme.faucheuse.signal_recu) { case SIGINT : { retour = -1; break; } // CTRL+C notamment... case SIGQUIT : { retour = -1; break; } case SIGABRT : { retour = -1; break; } case SIGTERM : { retour = -1; break; } case SIGKILL : { retour = -1; break; } // A priori inutile car on n'aura pas le temps d'arriver ici... default : retour = 0; } // Enregistrement dans l'historique, quel que soit le signal (et s'il nous en laisse l'occasion...) : if (systeme.faucheuse.signal_recu != SIGCHLD) { enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_SIGNAL_RECU, 0, systeme.faucheuse.signal_recu, NULL); } systeme.faucheuse.signal_recu = SYSTEME_SIGNAL_AUCUN; return(retour); } //================================================================================================================================================================== // Fonction : Initialisation des handlers de signaux (vers une seule et même fonction). // Reçoit : Rien // Retour : 0 si pas d'erreur, -1 si erreur //================================================================================================================================================================== int8_t init_signaux(void) { systeme.faucheuse.signal_recu = SYSTEME_SIGNAL_AUCUN; signal(SIGHUP, sigintHandler); // 1 SIGHUP Instruction (HANG UP) - Fin de session signal(SIGINT, sigintHandler); // 2 SIGINT Interruption signal(SIGQUIT, sigintHandler); // 3 SIGQUIT Instruction (QUIT) signal(SIGILL, sigintHandler); // 4 SIGILL Instruction illégale signal(SIGTRAP, sigintHandler); // 5 SIGTRAP Trace trap signal(SIGABRT, sigintHandler); // 6 SIGABRT (ANSI) Instruction (ABORT) signal(SIGIOT, sigintHandler); // 6 SIGIOT (BSD) IOT Trap signal(SIGBUS, sigintHandler); // 7 SIGBUS Bus error signal(SIGFPE, sigintHandler); // 8 SIGFPE Floating-point exception - Exception arithmétique signal(SIGKILL, sigintHandler); // 9 SIGKILL Instruction (KILL) - termine le processus immédiatement signal(SIGUSR1, sigintHandler); // 10 SIGUSR1 Signal utilisateur 1 signal(SIGSEGV, sigintHandler); // 11 SIGSEGV Violation de mémoire signal(SIGUSR2, sigintHandler); // 12 SIGUSR2 Signal utilisateur 2 signal(SIGPIPE, sigintHandler); // 13 SIGPIPE Broken PIPE - Erreur PIPE sans lecteur signal(SIGALRM, sigintHandler); // 14 SIGALRM Alarme horloge signal(SIGTERM, sigintHandler); // 15 SIGTERM Signal de terminaison signal(SIGSTKFLT, sigintHandler); // 16 SIGSTKFLT Stack Fault signal(SIGCHLD, sigintHandler); // 17 SIGCHLD ou SIGCLD modification du statut d'un processus fils signal(SIGCLD, sigintHandler); // 17 SIGCHLD ou SIGCLD modification du statut d'un processus fils signal(SIGCONT, sigintHandler); // 18 SIGCONT Demande de reprise du processus signal(SIGSTOP, sigintHandler); // 19 SIGSTOP Demande de suspension imbloquable signal(SIGTSTP, sigintHandler); // 20 SIGTSTP Demande de suspension depuis le clavier signal(SIGTTIN, sigintHandler); // 21 SIGTTIN lecture terminal en arrière-plan signal(SIGTTOU, sigintHandler); // 22 SIGTTOU écriture terminal en arrière-plan signal(SIGURG, sigintHandler); // 23 SIGURG évènement urgent sur socket signal(SIGXCPU, sigintHandler); // 24 SIGXCPU temps maximum CPU écoulé signal(SIGXFSZ, sigintHandler); // 25 SIGXFSZ taille maximale de fichier atteinte signal(SIGVTALRM, sigintHandler); // 26 SIGVTALRM alarme horloge virtuelle signal(SIGPROF, sigintHandler); // 27 SIGPROF Profiling alarm clock signal(SIGWINCH, sigintHandler); // 28 SIGWINCH changement de taille de fenêtre signal(SIGPOLL, sigintHandler); // 29 SIGPOLL (System V) occurence d'un évènement attendu signal(SIGIO, sigintHandler); // 29 SIGIO (BSD) I/O possible actuellement signal(SIGPWR, sigintHandler); // 30 SIGPWR Power failure restart signal(SIGSYS, sigintHandler); // 31 SIGSYS Erreur d'appel système return(0); } //================================================================================================================================================================== // Fonction : Handler unique de tous les signaux. Récupére la valeur du signal et met à jour la variable globale correspondante. // ATTENTION : il ne faut PAS faire de printf (ou équivalent) ici car il y a des restrictions dans les signals handlers. // En l'occurence, le traitement en fonction du numéro de signal est fait dans la fonction 'analyse_signal()' ci-dessus. // Reçoit : Rien // Retour : Rien //================================================================================================================================================================== void sigintHandler(int32_t signal) { systeme.faucheuse.signal_recu = signal; // Permet de passer l'information de réception du signal à la boucle infinie du MAIN. }