//********************************************************************************************************************************************************************************** // Fichier FONCTIONS.C // // Contient toutes les fonctions générales du projet. //********************************************************************************************************************************************************************************** // Include : #include "main.h" // Prototypes : void date_courante(char* chaine, bool flag_ns, int format); int8_t set_date_heure(struct Struct_date date, struct Struct_heure heure); void delai_dixieme_sec(uint16_t tempo); void delai_nano_sec(unsigned int tempo); uint64_t timestamp_secondes(void); int8_t tri_liste_valeurs_niveaux(struct Struct_valeur_niveau * liste, uint16_t taille_liste); float calcul_niveau(struct Struct_valeur_niveau * liste, uint16_t taille_liste, float valeur); float moyenne_mobile(float old_val, float new_val, uint16_t coefficient); char * conversion_adresse_mac_chaine(uint8_t * adresse_mac, char * chaine, char separateur); uint8_t * conversion_chaine_adresse_mac(char * chaine, uint8_t * adresse_mac); char * conversion_rom_code_chaine(uint8_t * rom_code, char * chaine, char separateur); char * conversion_unique_id_chaine(uint8_t * unique_id, char * chaine); int32_t recherche_caractere_dans_chaine(char * chaine, char car, uint16_t debut); char * chaine_minuscules(char * chaine); // Variables globales : volatile uint16_t tempo_delai_dixieme_sec; //********************************************************************************************************************************************************************************** // Fonction DATE_COURANTE // // Renvoie une chaine représentant la date et l'heure en cours. // En option (flag_ns = TRUE), on renvoie également le nombre de ns //********************************************************************************************************************************************************************************** void date_courante(char* chaine, bool flag_ms, int format) { time_t now; struct tm tm_now; struct timespec tms; char chaine_nb_ns[20]; char chaine_format[50]; switch(format) { case FORMAT_DATE_COURANTE_USER : { strcpy(chaine_format, "%d/%m/%Y %H:%M:%S"); break; } case FORMAT_DATE_COURANTE_MYSQL : { strcpy(chaine_format, "%Y-%m-%d %H:%M:%S"); break; } case FORMAT_DATE_COURANTE_DATE_SEULE : { strcpy(chaine_format, "%d/%m/%Y"); break; } case FORMAT_DATE_COURANTE_HEURE_SEULE : { strcpy(chaine_format, "%H:%M:%S"); break; } case FORMAT_DATE_COURANTE_FICHIER : { strcpy(chaine_format, "%y%m%d_%H%M%S"); break; } case FORMAT_DATE_COURANTE_MODEM : { strcpy(chaine_format, "%y/%m/%d,%H:%M:%S+00"); break; } // yy/MM/dd,hh:mm:ss±zz case FORMAT_DATE_COURANTE_COMPRESSEE: { strcpy(chaine_format, "%y%m%d_%H%M%S"); break; } // "YYMMJJ_hhmmss" case FORMAT_DATE_COURANTE_SYSTEME : default : { strcpy(chaine_format, "%Y-%m-%d_%H:%M:%S"); break; } } now = time (NULL); // lire l'heure courante tm_now = *localtime (&now); // la convertir en heure locale strftime (chaine, MAX_LONGUEUR_DATE_COURANTE, chaine_format, &tm_now); if (flag_ms) { timespec_get(&tms, TIME_UTC); // Permet de récupérer un timestamp en secondes (tms.tv_sec, inutilisé ici) et en nanosecondes (tms.tv_nsec). sprintf(chaine_nb_ns, ".%03i", (int16_t) (tms.tv_nsec / 1000000)); // Formatage de la sous-chaine du nombre de ns (nb : le lien entre les secondes obtenus par DATE et les secondes obtenues par TEMESPEC_GET n'est pas garanti mais ce n'est pas génant pour l'usage) strcat(chaine, chaine_nb_ns); // Ajout des ns à la fin de la chaine de date. } } //********************************************************************************************************************************************************************************** // Fonction : Met à jour la date et heure système avec les valeurs reçues en paramètre // Reçoit : Une structure de type Date // Retour : 0 si la mise à jour s'est bien passée, -1 sinon //**********************************************************************************************************************************************************************************/ int8_t set_date_heure(struct Struct_date date, struct Struct_heure heure) { struct tm newdatetime; time_t newtime; struct timeval newsecus; int8_t result; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; newdatetime.tm_mday = date.jour; // Jour du mois newdatetime.tm_mon = date.mois - 1; // Mois commence à 0 newdatetime.tm_year = date.annee - 1900; // Années depuis 1900 newdatetime.tm_sec = heure.secondes; // Secondes newdatetime.tm_min = heure.minutes; // Minutes newdatetime.tm_hour = heure.heures; // Heures newdatetime.tm_isdst = 0; // 0=heure d'hiver, 1=heure d'été, -1=ne sait pas. newtime = mktime(&newdatetime); newsecus.tv_sec = newtime; newsecus.tv_usec = 0; result = settimeofday(&newsecus,NULL); if(result < 0) { sprintf(tmp, "Mise à jour de la date-heure impossible (demande effectuée pour %02i/%02i/%04i à %02i:%02i:%02i)", date.jour, date.mois, date.annee, heure.heures, heure.minutes, heure.secondes); affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); return (-1); } else { // sprintf(tmp, "Mise à jour de la date-heure effectuée avec succès à %02i/%02i/%04i %02i:%02i:%02i", date.jour, date.mois, date.annee, heure.heures, heure.minutes, heure.secondes); // affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); return (0); } } //********************************************************************************************************************************************************************************** // Fonction DELAI_DIXIEME_SEC // // Met en sommeil l'exécution pendant le nombre de centaines de ms passé en paramètre. // NB : utilise la focntion TIMER de Linux (avec une base de temps calée dans HARDWARE.H à 100ms). //********************************************************************************************************************************************************************************** void delai_dixieme_sec(uint16_t tempo) { tempo_delai_dixieme_sec = tempo; // NB : le type 'volatile' est absolument nécessaire. Sinon, l'optimisation du compilateur va rendre la boucle While infinie... while (tempo_delai_dixieme_sec != 0); } //********************************************************************************************************************************************************************************** // Fonction DELAI_NANO_SEC // // Met en sommeil l'exécution pendant le nombre de ns passé en paramètre. // ATTENTION : le temps de latence est d'environ 65/70µs => toute valeur inféreure donnera cette valeur. // Au-delà, il semble que la courbe est à peu près linéaire : réel = 65/70µs + tempo // Par exemple : 50000 (soit 50µs) donnera environ 120µs. // NB : Pour débug uniquement... //********************************************************************************************************************************************************************************** void delai_nano_sec(unsigned int tempo) { struct timespec tim; tim.tv_sec = 0; tim.tv_nsec = tempo; nanosleep(&tim , (struct timespec *)NULL); } //********************************************************************************************************************************************************************************** // Fonction : Calcule et renvoit le timestamp actuel, en secondes // Reçoit : Rien // Retourne : Le timestamp, en secondes //********************************************************************************************************************************************************************************** uint64_t timestamp_secondes(void) { struct timeval current_time; gettimeofday(¤t_time, NULL); return (current_time.tv_sec); } //********************************************************************************************************************************************************************************** // Fonction : Traite une liste de Valeurs/Niveaux (pour un réservoir ou une batterie par exemple). La première étape est de stocker toutes les valeurs valides de la liste en début // de liste. Dans un second temps, c'est de stocker tous les enregistrements par ordre croissant de 'valeur'. // Reçoit : - le pointeur sur le début de la liste // - la taille de la liste // Retour : 0 si Ok et -1 si erreur. //********************************************************************************************************************************************************************************** int8_t tri_liste_valeurs_niveaux(struct Struct_valeur_niveau * liste, uint16_t taille_liste) { uint16_t i, j; float val; float niv; uint16_t max; // 1. Stockage de toutes les valeurs valides en début de liste : j = 0; for (i = 0; i < taille_liste; i ++) { if ((liste + i)->flag_validite == TRUE) { (liste + j)->valeur = (liste + i)->valeur; (liste + j)->niveau = (liste + i)->niveau; (liste + j)->flag_validite = (liste + i)->flag_validite; j ++; } } // 1 (suite). Parcours la fin de la liste qui n'est plus utile et qu'il faut dévalider : for (; j < taille_liste; j ++) { (liste + j)->valeur = 0; (liste + j)->niveau = 0; (liste + j)->flag_validite = FALSE; } // 2. Tri de la liste : j = 0; // Compteur de sécurité... max = taille_liste * (taille_liste + 1) / 2; // Le nombre maxi d'itération est de n(n+1)/2 boucles dans le cas où la liste initiale est totalement remplie et en sens inverse. for (i = 1; (i < taille_liste) && ((liste + i)->flag_validite == TRUE); i ++) { if ((liste + i)->valeur < (liste + i -1)->valeur) // Si la valeur [i] < valeur [i-1], on les inverse : { val = (liste + i)->valeur; // Stockage temporaire de la valeur [i] niv = (liste + i)->niveau; (liste + i)->valeur = (liste + i - 1)->valeur; // Stockage de la valeur [i-1] dans [i] (liste + i)->niveau = (liste + i - 1)->niveau; (liste + i - 1)->valeur = val; // Stockage de la valeur temporaire dans [i-1] (liste + i - 1)->niveau = niv; i = 0; // La valeur '0' permet de tenir compte du 'i++' qui est fait juste après et donc de bien recommencer à boucle à '1' if ((j ++) > max) return(-1); // Sécurité car on bricole l'indice de boucle... } } return(0); } //********************************************************************************************************************************************************************************** // Fonction : Recherche dans une liste de Valeurs/Niveaux les deux points les plus proches de la 'valeur' reçue en paramètre. On effectue ensuite un calcul d'interpolation // entre ces deux points pour retourner une valeur de niveau entre 0.00 et 100.00%. // IMPORTANT : on suppose que la liste a été triée auparavant par 'valeurs' croissante ! Ce tri est fait dans bdd_init.c, en appelant 'tri_liste_valeurs_niveaux()' // ci-dessus après avoir chargé la liste depuis la BDD. Le tri n'est donc fait d'une seule fois à l'init. // Si 'valeur' < la plus petite valeur de la liste : renvoie le niveau de la plus petite valeur de la liste. // Si 'valeur' > la plus grande valeur de la liste : renvoie le niveau de la plus grande valeur de la liste. // Si 'valeur' = une valeur de la liste : renvoie le niveau de la valeur en question. // Si aucune valeur dans la liste, renvoie toujours 0% // Reçoit : - le pointeur sur le début de la liste // - la taille de la liste // - la valeur actuelle // Retour : la valeur de niveau (entre 0 et 100.00) si Ok et -1 si erreur. //********************************************************************************************************************************************************************************** float calcul_niveau(struct Struct_valeur_niveau * liste, uint16_t taille_liste, float valeur) { uint16_t i; for (i = 0; (i < taille_liste) && ((liste + i)->flag_validite == TRUE); i ++) { if ((liste + i)->valeur == valeur) return((liste + i)->niveau); // Si on tombe exactement sur une valeur de la table, on renvoit immédiatement le niveau. if ((liste + i)->valeur > valeur) // Si on a dépassé la valeur sans tomber dessus, on s'arrête : { if (i == 0) return((liste + i)->niveau); // Si la valeur est inférieure à la plus petite entrée de la table, on renvoit immédiatement le niveau correspondant à cette première entrée. else { if (((liste + i)->niveau == (liste + i - 1)->niveau) || // Cas où les deux niveaux sont identiques... ((liste + i)->valeur == (liste + i - 1)->valeur)) // ... ou les deux valeurs sont identiques (ce qui ne devrait pas arriver car dès la 1ère, on aurait dû arrêter la boucle) { return((liste + i)->niveau); } else { return (((((liste + i)->niveau - (liste + i - 1)->niveau) * (valeur - (liste + i - 1)->valeur)) / ((liste + i)->valeur - (liste + i - 1)->valeur)) + (liste + i - 1)->niveau); } } } } // Cas où on est arrivé au bout de la liste sans trouver de valeur[i] > valeur. // Dans ce cas, on va renvoyer le niveau correspondant à la dernière valeur valide (donc la plus grande puisque la liste est classée). if (i != 0) return((liste + i - 1)->niveau); else return(-1); // Cas où la liste ne contient aucune valeur. On ne peut pas faire d'extrapolation... } //================================================================================================================================================================== // Fonction : Fonction qui ajoute une 'new_val' à une 'old_val' par une moyenne pondérée : l'ancienne valeur est affectée du (coefficient - 1) et la nouvelle // d'un coefficient de 1. // Si le coeff de filtrage = 0 (erreur mais on traite comme si aucun filtrage) ou 1, on applique directement la nouvelle valeur comme valeur de la // mesure. Ces deux correspondent à "pas de filtrage". // Reçoit : l'ancienne valeur, la nouvelle valeur et le coefficient de filtrage à utiliser // Renvoit : l'ancienne valeur mise à jour //================================================================================================================================================================== float moyenne_mobile(float old_val, float new_val, uint16_t coefficient) { if (coefficient <= 1) return(new_val); else return(((old_val * (coefficient - 1)) + new_val) / coefficient); } //********************************************************************************************************************************************************************************** // Fonction : Convertit le tableau de 6 octets reçu en une chaine de type adresse MAC (chaque octet étant représenté par deux caractères ASCII représentant sa valeur en // hexadécimal). Le séparateur utilisé entre chaque couple de caractère est reçu en paramètre (en général, soit ':', soit'-' ou '_') // Reçoit : - le pointeur sur le début du tableau de 6 octets // - le pointeur sur le début de la chaine à remplir // - le caractère séparateur à utiliser // Retour : Le pointeur sur la chaine (donc, a priori, identique à celui reçu). //********************************************************************************************************************************************************************************** char * conversion_adresse_mac_chaine(uint8_t * adresse_mac, char * chaine, char separateur) { sprintf(chaine, "%02X%c%02X%c%02X%c%02X%c%02X%c%02X", *adresse_mac, separateur, *(adresse_mac + 1), separateur, *(adresse_mac + 2), separateur, *(adresse_mac + 3), separateur, *(adresse_mac + 4), separateur, *(adresse_mac + 5)); return(chaine); } //********************************************************************************************************************************************************************************** // Fonction CONVERSION_CHAINE_ADRESSE_MAC // // Convertit une chaine au format 'xx:xx:xx:xx:xx:xx' en un tableau d'adresse MAC //**********************************************************************************************************************************************************************************/ uint8_t * conversion_chaine_adresse_mac(char * chaine, uint8_t * adresse_mac) { uint8_t i; uint8_t clsb, cmsb; uint8_t lsb, msb; for (i = 0; i < TAILLE_ADRESSE_MAC; i ++) { cmsb = chaine[3*i]; if ((cmsb >= '0') && (cmsb <= '9')) msb = cmsb - '0'; else if ((cmsb >= 'A') && (cmsb <= 'F')) msb = cmsb - 'A' + 10; else if ((cmsb >= 'a') && (cmsb <= 'f')) msb = cmsb - 'a' + 10; else msb = 0; clsb = chaine[(3*i)+1]; if ((clsb >= '0') && (clsb <= '9')) lsb = clsb - '0'; else if ((clsb >= 'A') && (clsb <= 'F')) lsb = clsb - 'A' + 10; else if ((clsb >= 'a') && (clsb <= 'f')) lsb = clsb - 'a' + 10; else lsb = 0; *(adresse_mac + i) = (msb << 4) | lsb; } return(adresse_mac); } //********************************************************************************************************************************************************************************** // Fonction : Convertit le tableau de 8 octets reçu en une chaine de type Rom Code (chaque octet étant représenté par deux caractères ASCII représentant sa valeur en // hexadécimal). Le séparateur utilisé entre chaque couple de caractère est reçu en paramètre (en général, soit ':', soit'-' ou '_') // Reçoit : - le pointeur sur le début du tableau de 8 octets // - le pointeur sur le début de la chaine à remplir // - le caractère séparateur à utiliser // Retour : Le pointeur sur la chaine (donc, a priori, identique à celui reçu). //********************************************************************************************************************************************************************************** char * conversion_rom_code_chaine(uint8_t * rom_code, char * chaine, char separateur) { sprintf(chaine, "%02X%c%02X%c%02X%c%02X%c%02X%c%02X%c%02X%c%02X", *rom_code, separateur, *(rom_code + 1), separateur, *(rom_code + 2), separateur, *(rom_code + 3), separateur, *(rom_code + 4), separateur, *(rom_code + 5), separateur, *(rom_code + 6), separateur, *(rom_code + 7)); return(chaine); } //********************************************************************************************************************************************************************************** // Fonction CONVERSION_UNIQUE_ID_CHAINE // // Convertit un tableau contenant un Unique ID (au sens STM32) en une chaine au format 'xx xx xx xx xx xx xx xx xx xx xx xx' //**********************************************************************************************************************************************************************************/ char * conversion_unique_id_chaine(uint8_t * unique_id, char * chaine) { sprintf(chaine, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", *unique_id, *(unique_id + 1), *(unique_id + 2), *(unique_id + 3), *(unique_id + 4), *(unique_id + 5), *(unique_id + 6), *(unique_id + 7), *(unique_id + 8), *(unique_id + 9), *(unique_id + 10), *(unique_id + 11)); return(chaine); } //********************************************************************************************************************************************************************************** // Fonction : Recherche un caractère dans une chaine et retourne sa position s'il le trouve // Reçoit : Un pointeur sur la chaine à analyser, le caractère recherché, la posiiton du début de la recherche (par défaut, 0) et la taille de la chaine // Retour : La position du caractère recherché (entre 0 et taille-1), -1 si le caractère n'est pas trouvé avant le NULL de fin de chaine //**********************************************************************************************************************************************************************************/ int32_t recherche_caractere_dans_chaine(char * chaine, char car, uint16_t debut) { uint16_t i; if (debut >= strlen(chaine)) return (-1); for (i = debut; chaine[i] != 0; i ++) { if (chaine[i] == car) return(i); } return(-1); } //********************************************************************************************************************************************************************************** // Fonction : Convertit une chaine de caractères en minuscules // Reçoit : Un pointeur sur la chaine à convertir // Retour : le pointeur sur la chaine convertie //**********************************************************************************************************************************************************************************/ char * chaine_minuscules(char * chaine) { uint16_t i; if (strlen(chaine) < 64000) // Pour éviter de déborder le 'i' de la boucle... { for (i = 0; chaine[i] != 0; i ++) { if ((chaine[i] >= 'A') && (chaine[i] <= 'Z')) chaine[i] = chaine[i] +32; } } return(chaine); }