From 8fe204361bdcc78094d4f91f51e589bcacc40b8a Mon Sep 17 00:00:00 2001 From: Pierre Date: Thu, 13 Aug 2026 10:33:28 +0200 Subject: [PATCH] Import initial du projet shipheart_appli --- .gitignore | 4 + make/mak.sh | 32 + make/makefile | 46 + sources/_version.c | 27 + sources/audio.c | 341 ++++ sources/audio.h | 28 + sources/batteries.c | 706 ++++++++ sources/batteries.h | 59 + sources/bdd.h | 249 +++ sources/bdd_admin.c | 406 +++++ sources/bdd_fonctions.c | 1567 +++++++++++++++++ sources/bdd_init.c | 3605 +++++++++++++++++++++++++++++++++++++++ sources/bdd_synchro.c | 1070 ++++++++++++ sources/c_bus.c | 548 ++++++ sources/c_bus.h | 49 + sources/c_commandes.c | 3329 ++++++++++++++++++++++++++++++++++++ sources/c_commandes.h | 89 + sources/capteurs.c | 747 ++++++++ sources/capteurs.h | 96 ++ sources/circuits.c | 479 ++++++ sources/circuits.h | 119 ++ sources/debug.c | 1 + sources/debug.h | 1 + sources/ecran.c | 229 +++ sources/ecran.h | 47 + sources/entrees.c | 884 ++++++++++ sources/entrees.h | 129 ++ sources/events.c | 568 ++++++ sources/events.h | 72 + sources/fonctions.c | 418 +++++ sources/fonctions.h | 33 + sources/g_bus.c | 461 +++++ sources/g_bus.h | 57 + sources/g_commandes.c | 728 ++++++++ sources/g_commandes.h | 68 + sources/gnss.c | 225 +++ sources/gnss.h | 26 + sources/hardware.c | 312 ++++ sources/hardware.h | 1 + sources/localisation.h | 95 ++ sources/m_bus.c | 415 +++++ sources/m_bus.h | 57 + sources/m_commandes.c | 1937 +++++++++++++++++++++ sources/m_commandes.h | 68 + sources/main.c | 387 +++++ sources/main.h | 1600 +++++++++++++++++ sources/modem.c | 1603 +++++++++++++++++ sources/modem.h | 145 ++ sources/nmea.c | 3083 +++++++++++++++++++++++++++++++++ sources/nmea.h | 72 + sources/reseaux.c | 1080 ++++++++++++ sources/reseaux.h | 72 + sources/reservoirs.c | 258 +++ sources/reservoirs.h | 31 + sources/serial.c | 610 +++++++ sources/serial.h | 1 + sources/sorties.c | 372 ++++ sources/sorties.h | 46 + sources/systeme.c | 2113 +++++++++++++++++++++++ sources/systeme.h | 171 ++ sources/usb.c | 238 +++ sources/usb.h | 29 + 62 files changed, 32339 insertions(+) create mode 100644 .gitignore create mode 100755 make/mak.sh create mode 100644 make/makefile create mode 100644 sources/_version.c create mode 100644 sources/audio.c create mode 100644 sources/audio.h create mode 100644 sources/batteries.c create mode 100644 sources/batteries.h create mode 100644 sources/bdd.h create mode 100644 sources/bdd_admin.c create mode 100644 sources/bdd_fonctions.c create mode 100644 sources/bdd_init.c create mode 100644 sources/bdd_synchro.c create mode 100644 sources/c_bus.c create mode 100644 sources/c_bus.h create mode 100644 sources/c_commandes.c create mode 100644 sources/c_commandes.h create mode 100644 sources/capteurs.c create mode 100644 sources/capteurs.h create mode 100644 sources/circuits.c create mode 100644 sources/circuits.h create mode 100644 sources/debug.c create mode 100644 sources/debug.h create mode 100644 sources/ecran.c create mode 100644 sources/ecran.h create mode 100644 sources/entrees.c create mode 100644 sources/entrees.h create mode 100644 sources/events.c create mode 100644 sources/events.h create mode 100644 sources/fonctions.c create mode 100644 sources/fonctions.h create mode 100644 sources/g_bus.c create mode 100644 sources/g_bus.h create mode 100644 sources/g_commandes.c create mode 100644 sources/g_commandes.h create mode 100644 sources/gnss.c create mode 100644 sources/gnss.h create mode 100644 sources/hardware.c create mode 100644 sources/hardware.h create mode 100644 sources/localisation.h create mode 100644 sources/m_bus.c create mode 100644 sources/m_bus.h create mode 100644 sources/m_commandes.c create mode 100644 sources/m_commandes.h create mode 100644 sources/main.c create mode 100644 sources/main.h create mode 100644 sources/modem.c create mode 100644 sources/modem.h create mode 100644 sources/nmea.c create mode 100644 sources/nmea.h create mode 100644 sources/reseaux.c create mode 100644 sources/reseaux.h create mode 100644 sources/reservoirs.c create mode 100644 sources/reservoirs.h create mode 100644 sources/serial.c create mode 100644 sources/serial.h create mode 100644 sources/sorties.c create mode 100644 sources/sorties.h create mode 100644 sources/systeme.c create mode 100644 sources/systeme.h create mode 100644 sources/usb.c create mode 100644 sources/usb.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f21c9d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +objets/ +bin/ +versions/ +sources/_version.txt diff --git a/make/mak.sh b/make/mak.sh new file mode 100755 index 0000000..a23a9e4 --- /dev/null +++ b/make/mak.sh @@ -0,0 +1,32 @@ +#!/bin/bash +################################################################################################# +# Informations : +# 1. Ce fichier doit être exécutable : sudo chmod +x +# 2. il peut être lancé manuellement par : ./ +# 3. les sorties standard et d'erreur sont redirigées vers un fichier dans le répertoire UPLOAD (donc il sera uploadé à la prochaine connexion puis supprimé localement) +################################################################################################# +# Variables globales : +DATEVERSION=$(date +"%y%m%d_%H%M%S") +#============================================================================================================== +# Met à jour le fichier '_version.txt' dans le répertoire ../sources avec le 'datecode'. Celui-ci sera +# utilisé lors de la compilation/link de l'application comme version du logiciel. +echo "#define VERSION_LOGICIEL \""$DATEVERSION"\"" > ../sources/_version.txt +#============================================================================================================== +# Lancement du makefile : +make +#============================================================================================================== +# Sauvegarde de la version : +cp "../bin/shipheart_appli" "../versions/shipheart_appli_"$DATEVERSION +#============================================================================================================== +# Déploiement vers le CM5 (nécessaire depuis le passage en cross-compilation WSL2 - +# auparavant la compilation avait lieu directement sur le CM5, donc le binaire y +# était déjà présent). Utilise l'alias SSH 'cm5' défini dans ~/.ssh/config. +# NB : shipheart_appli tourne en permanence sur le CM5 - une écriture directe sur +# le fichier cible échoue ("Text file busy"). On écrit donc dans un fichier +# temporaire, puis on renomme (opération atomique, fonctionne même sur un +# exécutable en cours d'utilisation). +scp -q "../bin/shipheart_appli" cm5:/home/koriolan/shipheart/versions/current/shipheart_appli.new +ssh cm5 "mv /home/koriolan/shipheart/versions/current/shipheart_appli.new /home/koriolan/shipheart/versions/current/shipheart_appli" +#============================================================================================================== +# Fin : +exit 0 diff --git a/make/makefile b/make/makefile new file mode 100644 index 0000000..7306a60 --- /dev/null +++ b/make/makefile @@ -0,0 +1,46 @@ +# Choix du compilateur croisé (cible aarch64/CM5, exécuté depuis WSL2 x86_64) : +CC=aarch64-linux-gnu-gcc + +# Recherche pkg-config restreinte aux .pc arm64 uniquement (PKG_CONFIG_LIBDIR +# écrase le chemin de recherche par défaut, pour ne jamais mélanger avec les +# .pc x86_64 de l'hôte). mariadb_config est abandonné : ce script n'est pas +# conscient du multiarch et renverrait les chemins x86_64 de l'hôte. +PKGCONFIG = PKG_CONFIG_PATH= PKG_CONFIG_LIBDIR=/usr/lib/aarch64-linux-gnu/pkgconfig pkg-config + +# Options de compilation : +# PRODUCTION : +CFLAGS = -O3 -Wall $(shell $(PKGCONFIG) --cflags mariadb alsa libssl libcrypto json-c libgpiod) +# DEBUG : +#CFLAGS = -O3 -Wall $(shell $(PKGCONFIG) --cflags mariadb alsa libssl libcrypto json-c libgpiod) -gstabs +# Possibilité d'utiliser aussi '-Wunused-variable' si besoin... Voire 'Wall' si on veut se faire mal...! + +# Options de link pour accéder à MariaDB, Alsa, OpenSSL, json-c et GPIO : +LIBS = $(shell $(PKGCONFIG) --libs mariadb alsa libssl libcrypto json-c libgpiod) -lm + +# Localisation des répertoires : +SRCDIR=../sources +OBJDIR=../objets +EXEDIR=../bin + +# Nom de l'éxécutable généré : +EXECUTABLE=shipheart_appli +#------------------------------------------------------------------------------------------------------------ +# !!!!!!!!! .............................NE DOIT PAS ETRE MODIFIE EN DESSOUS.......................!!!!!!!!!! +#------------------------------------------------------------------------------------------------------------ +SRC= $(wildcard $(SRCDIR)/*.c) +DEPS= $(wildcard $(SRCDIR)/*.h) +OBJ = $(subst $(SRCDIR),$(OBJDIR), $(patsubst %.c, %.o, $(SRC)) ) +# Règle de plus haut niveau : +all: $(EXEDIR)/$(EXECUTABLE) +# Linker (dépendance de l'exécutable vs les fichiers Objet) : +$(EXEDIR)/$(EXECUTABLE): $(OBJ) + $(CC) -o $@ $^ $(LIBS) +# Compilateur (dépendance des fichiers Objet vs leur fichier Source et TOUS les Headers) : +$(OBJDIR)/%.o: $(SRCDIR)/%.c $(DEPS) + $(CC) -o $@ -c $< $(CFLAGS) +# Cas particulier du fichier '_version.c' qui dépend, en plus, du fichier '_version.txt' : +# cf fichier '_version.c' pour détails. +$(OBJDIR)/_version.o: $(SRCDIR)/_version.c $(SRCDIR)/_version.txt + $(CC) -o $(OBJDIR)/_version.o -c $(SRCDIR)/_version.c $(CFLAGS) +# NB : Les focntions de type Clean ou Mrproper ne sont pas laissées car +# on part du principe qu'on garde les fichiers objets dans le répertoire dédié... diff --git a/sources/_version.c b/sources/_version.c new file mode 100644 index 0000000..b15f128 --- /dev/null +++ b/sources/_version.c @@ -0,0 +1,27 @@ +//********************************************************************************************************************************************************************************** +// Fichier _VERSION.C +// +// Contient le minimum pour effectuer le lien entre le '#define VERSION_LOGICIEL' situé dans le fichier '_version.txt' et l'application. +// Pour cela, le fichier '_version.txt' est mis à jour par le fichier 'mak.sh' avant de lancer le make proprement dit. Ainsi, ce fichier txt dispose, avant tout make, +// d'un define mis à jour avec le date-code du moment. +// Le choix d'un fichier '.txt' plutôt qu'un '.h' traditionnel est uniquement pour que sa mise à jour ne génère pas une recompilation de tous les fichiers '.c' comme cela est +// (fort justement) demandé dans le makefile. Donc tous les '.c' (yc _version.c même si ça n'a pas beaucoup d'intérêt) sont mis à jour lors de la modif de n'importe quel '.h'. +// Et seul 'version.c' est mis en jour, en plus, à chaque changement de '_version.txt' ; soit à chaque lancement si le fichier bash fait bien son boulot... +// +// La fonction se contente de renvoyer le pointeur sur la chaine définie dans '_version.txt' afin d'avoir le minimum de lien vec le reste de l'application. +// +//**********************************************************************************************************************************************************************************/ + +#include "_version.txt" // Attention : ce fichier n'est inclus qu'ici (pour ne pas tout recompiler à chaque fois...!). Et il est mis à jour automatiquement par le fichier 'mak.sh'. + + +//================================================================================================================================================================== +// Fonction : Retourne la chaine de caractère correspondant au date-code actuel mis à jour dans le fichier '_version.txt' par un fichier bash. +// Reçoit : Rien +// Renvoit : Le pointeur sur la chaine de version +//================================================================================================================================================================== +char * maj_version_logiciel(void) +{ + + return (VERSION_LOGICIEL); +} diff --git a/sources/audio.c b/sources/audio.c new file mode 100644 index 0000000..805af8c --- /dev/null +++ b/sources/audio.c @@ -0,0 +1,341 @@ +// ********************************************************************************************************************************************************************************** +// Fichier AUDIO.C +// +// Contient toutes les fonctions liées à l'audio. +// ********************************************************************************************************************************************************************************** + +#define __DEBUG_AUDIO //Décommenter cette ligne pour avoir les messages de debug concernant l'audio + + +// Include : +#include "main.h" + +// Prototypes : +void gestion_audio(void); +int8_t init_audio(void); +int8_t emission_bip(char * nom_fichier, uint16_t nb_repetitions, uint16_t periode_boucles, uint16_t nb_boucles); +int8_t lecture_mp3(char * nom_fichier, uint16_t nb_repetitions); +int8_t mise_a_jour_etat_jack(); +int8_t set_volume(float volume, bool echo); +int8_t alimentation_ampli_audio(void); + + +// Variables globales : +struct Struct_audio audio; + + +//********************************************************************************************************************************************************************************** +// Fonction : Gestion de l'audio en tâche de fond +// Reçoit : Rien +// Retourne : Rien +//********************************************************************************************************************************************************************************** +void gestion_audio(void) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + switch(audio.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_AUDIO_INIT : + { + if (audio.sequenceur.compteur == 0) + { + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_AUDIO_REPOS : + { + if (audio.sequenceur.compteur == 0) + { + audio.sequenceur.compteur = AUDIO_PERIODE_SURVEILLANCE; + mise_a_jour_etat_jack(); + } + + break; + } + + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE : + { + mise_a_jour_etat_jack(); // Pour que la mise à jour se fasse quand même de temps en temps... + + lecture_mp3(audio.boucle.nom_fichier, audio.boucle.nb_repetitions); + audio.sequenceur.compteur = audio.boucle.periode_boucles; + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_TEMPO_BOUCLE; + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_AUDIO_TEMPO_BOUCLE : + { + if (audio.sequenceur.compteur == 0) + { + if (audio.boucle.compteur_boucles > 0) + { + audio.boucle.compteur_boucles --; + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE; + } + else + { + audio.boucle.nom_fichier[0] = 0; + audio.boucle.nb_repetitions = 0; + audio.boucle.periode_boucles = 0; + audio.boucle.nb_boucles = 0; + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS; + } + } + break; + } + + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Audio est inconnu [%i]. Il est repositionné au REPOS", audio.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS; + break; + } + } +} + + +//================================================================================================================================================================== +// Fonction : Initialise les fonctions audio +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t init_audio(void) +{ + + // Ajustement des variables selon la génération de ShipBase en cours : + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + audio.sortie_gpio_cm_do_audio_sd = CM3P_DO_AUDIO_SD; + audio.entree_gpio_cm_di_jack_detect = 0; // Non disponible sur V1 + } + else + { + audio.sortie_gpio_cm_do_audio_sd = CM4_DO_AUDIO_SD; + audio.entree_gpio_cm_di_jack_detect = CM4_DI_JACK_DETECT; + } + + // Mise à jour du niveau de son (sans émission de bip) : +// if (set_volume(audio.consigne_volume, AUDIO_SET_VOLUME_ECHO_OFF) == -1) return(-1); // La valeur a été récupérée dans la BDD par 'init_bdd()' + set_volume(audio.consigne_volume, AUDIO_SET_VOLUME_ECHO_OFF); // Mise à jour du volume au niveau matériel. NB : l'alimentation de l'ampli est faite en fin de 'set_volume()' et tient compte de l'état de 'Fonction_jack' + + // Lancement musique d'accueil : + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) lecture_mp3("netflix.mp3", 0); + else lecture_mp3("battement_coeur.mp3", 0); + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation audio\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Demande l'émission d'un bip qui peut être complexe (un ou plusieurs bips, répétés un certain nombre de fois à un intervalle défini). A noter que cette +// fonction permet aussi d'arrêter la séquence par anticipation si besoin. +// Reçoit : Le nom du fichier MP3 à utiliser comme son de base (ou "Off" pour stopper), le nombre de répétitions du son de base (joués à la suite les uns des autres), +// la tempo d'attente entre chaque boucles et le nombre de boucles. +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t emission_bip(char * nom_fichier, uint16_t nb_repetitions, uint16_t periode_boucles, uint16_t nb_boucles) +{ +FILE *file; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Détection d'une commande d'arrêt de lecture : + // Nb : le nom spécifique "Off" est traité de façon insensible à la casse (donc "OFF", "Off", "off" et tous les autres intermédiaires fonctionnent) + if ( (strlen(nom_fichier) == 3) + && (tolower(nom_fichier[0]) == 'o') + && (tolower(nom_fichier[1]) == 'f') + && (tolower(nom_fichier[2]) == 'f') ) + { + audio.boucle.nom_fichier[0] = 0; + audio.boucle.nb_repetitions = 0; + audio.boucle.periode_boucles = 0; + audio.boucle.nb_boucles = 0; + audio.boucle.compteur_boucles = 0; + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_REPOS; + return (0); + } + + // Test du fichier : + sprintf(tmp, "%s/%s", REPERTOIRE_CONFIG_SONS, nom_fichier); // Chemin complet du fichier dont le nom a été reçu + file = fopen(tmp, "r"); // Tentative d'ouverture en lecture du fichier + if (file == NULL) + { + sprintf(tmp, "Le fichier audio à lire [%s] n'existe pas dans le répertoire SONS [%s]. La lecture n'est pas lancée...", nom_fichier, REPERTOIRE_CONFIG_SONS); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); // Si erreur d'ouverture, sortie en erreur (et, surtout, pas de lancementd e lecture) + } + else fclose(file); // Si pas d'erreur, on referme immédiatement le fichier avantd e continuer... + + // Cas où le fichier existe bien : + strcpy(audio.boucle.nom_fichier, nom_fichier); + audio.boucle.nb_repetitions = nb_repetitions; + audio.boucle.periode_boucles = periode_boucles; + audio.boucle.nb_boucles = nb_boucles; + audio.boucle.compteur_boucles = nb_boucles; + audio.sequenceur.etat = ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE; + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Lance le son correspondant au fichier MP3 passé en paramètre. +// NB : la fonction lance un appel système non bloquant. Le fichier doit être dans REPERTOIRE_CONFIG_SONS. +// Reçoit : Nom complet du fichier (depuis REPERTOIRE_CONFIG_SONS) +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t lecture_mp3(char * nom_fichier, uint16_t nb_repetitions) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +char loop[15]; + + if (nb_repetitions > 0) sprintf(loop, "--loop %i", nb_repetitions); + else loop[0] = 0; + + sprintf(tmp, "sudo mpg123 %s %s/'%s' > /dev/null 2>&1 &", loop , REPERTOIRE_CONFIG_SONS, nom_fichier); // Attention : le '&' en fin de commande est nécessaire pour que l'appli n'attende pas la fin de la commande, donc de la lecture du morceau...! + system(tmp); +//printf("\tLancement lecture du fichier MP3 via la commande : [%s]\n", tmp); + + return(0); +} + + + +//================================================================================================================================================================== +// Fonction : Effectue une lecture matérielle pour définir si une prise jack est connectée ou pas sur la sortie audio de ShipBase +// NB : valable uniquement sur V2. +// NB 2 : cd SDIR #232 +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t mise_a_jour_etat_jack() +{ +bool flag; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + if (audio.entree_gpio_cm_di_jack_detect != 0) // Les V1 ne sont pas concernées... + { +// flag = (bool) lecture_entree(TYPE_ENTREES_GPIO, 0, audio.entree_gpio_cm_di_jack_detect, FALSE); // 0 = adresse ShipBase + flag = TRUE; // SDIR #232 + + if (flag != audio.etat_presence_jack) + { + audio.etat_presence_jack = flag; + alimentation_ampli_audio(); +#ifdef __DEBUG_AUDIO + sprintf(tmp, "La présence d'une prise jack sur la sortie audio a changé d'état. Elle est maintenant : %s. NB : Fonction jack = %i", audio.etat_presence_jack ? "Présente" : "Absente", audio.fonction_jack); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + } + + return (0); +} + + +//================================================================================================================================================================== +// Fonction : Met à jour le niveau de volume du son de la carte. La consigne est celle reçue en paramètre et la fonction met à jour les paramètres de AlsaMixer. +// Dans le cas où la consigne est à 0, la sortie de commande de l'ampli est carrément coupée. +// Reçoit : Le niveau de volume attendu (de 0 à 100%), en centièmes de pourcent. +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t set_volume(float volume, bool echo) +{ +long min, max; +snd_mixer_t *handle; +snd_mixer_selem_id_t *sid; +const char *card = "default"; +const char *selem_name = "PCM"; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Vérification de la nouvelle valeur de consigne : + if ((volume < AUDIO_CONSIGNE_MIN) || (volume > AUDIO_CONSIGNE_MAX)) + { + sprintf(tmp, "La nouvelle consigne de volume audio n'est pas dans la plage autorisée : %.2f pour [%i ; %i%%]. La commande est ignorée...", volume, AUDIO_CONSIGNE_MIN, AUDIO_CONSIGNE_MAX); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + // Mise à jour de la nouvelle valeur : + audio.consigne_volume = volume; + + // Envoi vers le driver : + snd_mixer_open(&handle, 0); + snd_mixer_attach(handle, card); + snd_mixer_selem_register(handle, NULL, NULL); + snd_mixer_load(handle); + snd_mixer_selem_id_alloca(&sid); + snd_mixer_selem_id_set_index(sid, 0); + snd_mixer_selem_id_set_name(sid, selem_name); + snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid); + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100); + snd_mixer_close(handle); + + // Activation ampli audio : + alimentation_ampli_audio(); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_VOLUME_CHANGEMENT_CONSIGNE, 0, audio.consigne_volume, NULL); + +#ifdef __DEBUG_AUDIO + sprintf(tmp, "Nouvelle consigne de volume = %.2f %%", audio.consigne_volume); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Bip de mise à jour s'il est demandé : +// if (echo) lecture_mp3("button-8.mp3", 0); + if (echo) lecture_mp3("beep-07.mp3", 0); + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Active ou désactive l'alimentation de l'ampli audio (alimentant le HP intégré) selon la présence ou non d'une prise jack sur la sortie AUDIO +// et selon le paramètre de configuration 'Fonction_jack' +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t alimentation_ampli_audio(void) +{ +#ifdef __DEBUG_AUDIO + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +#endif + + if ( ((audio.etat_presence_jack == TRUE) && (audio.fonction_jack == AUDIO_FONCTION_JACK_HP_OFF)) || // Si une fiche jack est présente sur la sortie Audio ET que le mode de fonctionnement est de couper le HP dans ce cas... + (audio.consigne_volume == 0) ) // ... OU le volume est à 0 + { + // Coupure de l'alimentation de la sortie audio : + ecriture_sortie(TYPE_SORTIE_GPIO, 0, audio.sortie_gpio_cm_do_audio_sd, OFF); // 0 est l'adresse de ShipBase +#ifdef __DEBUG_AUDIO + sprintf(tmp, "L'alimentation de l'ampli audio est passée à OFF car Présence Jack = %s et Fonction Jack = %i", (audio.etat_presence_jack ? "Présente" : "Absente"), audio.fonction_jack); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else // Dans tous les autres cas, l'ampli est alimenté. + { + // Alimentation de la sortie audio : + ecriture_sortie(TYPE_SORTIE_GPIO, 0, audio.sortie_gpio_cm_do_audio_sd, ON); // 0 est l'adresse de ShipBase +#ifdef __DEBUG_AUDIO + sprintf(tmp, "L'alimentation de l'ampli audio est passée à ON car Présence Jack = %s et Fonction Jack = %i", (audio.etat_presence_jack ? "Présente" : "Absente"), audio.fonction_jack); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + return(0); +} \ No newline at end of file diff --git a/sources/audio.h b/sources/audio.h new file mode 100644 index 0000000..e164d95 --- /dev/null +++ b/sources/audio.h @@ -0,0 +1,28 @@ +//********************************************************************************************************************************************************************************** +// Fichier AUDIO.H +// +// Contient tous les define liés à l'audio... +//**********************************************************************************************************************************************************************************/ + + + // Maximums : +#define AUDIO_CONSIGNE_MAX 100 +#define AUDIO_CONSIGNE_MIN 0 + + + // Timings : +#define AUDIO_PERIODE_SURVEILLANCE 5 // En x 100ms + + // Définitions : +#define AUDIO_SET_VOLUME_ECHO_OFF 0 // Indique que le changement de niveau se fera sans émettre de son +#define AUDIO_SET_VOLUME_ECHO_ON 1 // Indique que le changement de niveau se fera en émettant un son après la mise à jour + + // Fonctions Jack : +#define AUDIO_FONCTION_JACK_HP_OFF 0 // le HP est coupé quand une prise jack est présente +#define AUDIO_FONCTION_JACK_HP_ON 1 // Le HP est maintenu On quand une prise jack est présente + + // Etat séquenceur Audio : +#define ETAT_SEQUENCEUR_AUDIO_INIT 0 +#define ETAT_SEQUENCEUR_AUDIO_REPOS 1 +#define ETAT_SEQUENCEUR_AUDIO_DEBUT_BOUCLE 2 +#define ETAT_SEQUENCEUR_AUDIO_TEMPO_BOUCLE 3 diff --git a/sources/batteries.c b/sources/batteries.c new file mode 100644 index 0000000..72c066c --- /dev/null +++ b/sources/batteries.c @@ -0,0 +1,706 @@ +//********************************************************************************************************************************************************************************** +// Fichier BATTERIES.C +// +// Contient toutes les fonctions liées aux batteries de ShipHeart +//**********************************************************************************************************************************************************************************/ + +//#define __DEBUG_BATTERIES + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_batteries(void); +void gestion_batteries(void); +int16_t batterie_cle_primaire(uint16_t cle_primaire); +int16_t variable_batterie_cle_primaire(uint16_t cle_primaire); + + +// Variables globales : +struct Struct_batteries batteries; + + +/********************************************************************************************************************************************************************************** + Fonction : Initialise les différents paramètres des batteries au démarrage de l'application + Reçoit : Rien + Retour : -1 si erreur, 0 sinon. +**********************************************************************************************************************************************************************************/ +int8_t init_batteries(void) +{ + //------------------------------------------------------------------------------------------------------ + // Init des variables générales : + + batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_INIT; + batteries.sequenceur.compteur = BATTERIES_TEMPO_INIT; + + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation des batteries\t: OK", TYPE_MESSAGE_DEBUG_OK); + return(0); + +} + + +/********************************************************************************************************************************************************************************** + Fonction : Fonction de plus haut niveau de gestion batteries + Reçoit : Rien + Retour : Rien +**********************************************************************************************************************************************************************************/ +void gestion_batteries(void) +{ +uint16_t i, j; +float soc; +float tension_a_vide; +float resistance_interne; +float efficacite; +float delta_energie; +float capacite_reelle; +float duree_decharge_corrigee; +float courant_differentiel_corrige; +bool flag_erreur; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (!systeme.flag_cm3p_ready) return; // Evite que le traitement soit fait au milieu d'une reconfiguration... + + switch(batteries.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_BATTERIES_INIT : + { + if ((systeme.flag_cm3p_ready) && // Permet de s'assurer que le cycle d'init système est terminé. En particulier, on est sûr que 'init_bdd()' puis 'annuaire_k_bus()' ont été traités. + (batteries.sequenceur.compteur == 0)) + { + +affichage_debug(__FILE__, __LINE__, "------------------BATTEREIES INIT", TYPE_MESSAGE_DEBUG_WARNING); + batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_BATTERIES_REPOS : + { + if (batteries.sequenceur.compteur == 0) + { + batteries.sequenceur.compteur = BATTERIES_PERIODE_MESURES; +#ifdef __DEBUG_BATTERIES +batteries.sequenceur.compteur = 10; +// Valeur passée de 300ms à 1s pour les tests (cf batteries.h) +#endif + for (i = 0; i < batteries.nb; i ++) + { + if (batteries.batteries[i].flag_validite == FALSE) continue; + + flag_erreur = FALSE; + + // Acquisition des dernières valeurs et mémorisation du nouveau niveau : + switch(batteries.batteries[i].type_entrees) + { + //-------------------------------------------------------------------------- + case TYPE_ENTREES_VOLTAGE : + { + // Récupération de l'état de l'entrées Voltage configurée sur cette batterie et calcul de la nouvelle moyenne de tension à partir de l'ancienne et de la valeur lue : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = (((batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (batteries.batteries[i].tension_coeff_filtrage - 1))) + entrees.volt.entrees[batteries.batteries[i].no_entrees].valeur) / batteries.batteries[i].tension_coeff_filtrage; + + break; + } + + //-------------------------------------------------------------------------- + case TYPE_ENTREES_PREDEFINIES : + { + // Récupération de l'état de l'entrées Prédéfinie configurée sur cette batterie et calcul de la nouvelle moyenne de tension à partir de l'ancienne et de la valeur lue : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = (((batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (batteries.batteries[i].tension_coeff_filtrage - 1))) + entrees.predefinies.entrees[batteries.batteries[i].no_entrees].valeur) / batteries.batteries[i].tension_coeff_filtrage; + + break; + } + + //-------------------------------------------------------------------------- + case TYPE_ENTREES_IBS : + { + break; + } + + default : + { + sprintf(tmp, "Le 'Type_entrées' de la batterie #%i est invalide [%i]. La batterie est dévalidée", (i+1), batteries.batteries[i].type_entrees); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + batteries.batteries[i].flag_validite = FALSE; + flag_erreur = TRUE; + break; + } + } + + // Traitement : + if (flag_erreur == FALSE) + { + + //------------------------------------- + // Tension : + //------------------------------------- + // Calcul de l'impact de Gain_tension sur la tension courante : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante *= batteries.batteries[i].tension_gain; + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante; + + + //------------------------------------- + // Courant de décharge : + //------------------------------------- + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante = 0; + + for (j = 0; j < circuits.nb; j ++) + { + if (! circuits.circuits[j].flag_validite) continue; + if (circuits.circuits[j].id_batterie != batteries.batteries[i].cle_primaire) continue; + + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante -= circuits.circuits[j].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; // Cumul du courant dans chaque circuit. En négatif car le courant de chaque circuit est comptabilisé de façon positive alors que le courant de décharge est forcément négatif. + } + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; + + + //------------------------------------- + // Puissance : + //------------------------------------- + // SDIR #199 : suppression du (-1) dans le produit (tension x courant) afin de laisser la puissance consommée toujours négative : + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante; + + + + //================================================ + // Entrée de type Entrée Prédéfinie ou Voltage : + //================================================ + if (batteries.batteries[i].type_entrees != TYPE_ENTREES_IBS) + { + //------------------------------------- + // SOC : + //------------------------------------- + // Calcul du SOC : + soc = calcul_niveau(batteries.batteries[i].points_tension_soc, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante); + if (soc >= 0) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = soc; + else + { + sprintf(tmp, "Erreur de calcul de SOC de la batterie #%i avec tension=%.3fV. La valeur lue n'est pas exploitée.\n", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; + + + //------------------------------------- + // Mise à jour BDD : + //------------------------------------- + // Mise à jour dans la BDD si les valeurs ont suffisemment évolué : +// if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TENSION].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOC].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_PUISSANCE].marge)) + if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) > 0)) + { + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + +/*#ifdef __DEBUG_BATTERIES +sprintf(tmp, "Batterie #%i : Tension = %.3fV, courant de décharge = %.3fA (donc puissance = %.3fW) et SOC = %.2f%% (coeff filtrage tension = %i)", + i, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante, + (batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante * (-1) * batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante), + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, + batteries.batteries[i].tension_coeff_filtrage); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif +*/ + } + + //================================================ + // Entrée de type IBS : + //================================================ + else + { + //------------------------------------- + // Température : + //------------------------------------- + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante; + + //------------------------------------- + // Courant différentiel : + //------------------------------------- + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante; + + //------------------------------------- + // Calcul du courant de charge : + //------------------------------------- + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante < 0) // Normalement, impossible car cela induirait un courantd e charge négatif... + { +#ifdef __DEBUG_BATTERIES +// sprintf(tmp, "Batterie #%i : Le courant de charge est calculé avec une valeur négative [%.3f A], ce qui n'est pas possible. Il est ramené à 0 mA", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante = 0; // Par défaut, on écrête... + } + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante; + + + + //------------------------------------- + // Calcul du SOC : + //------------------------------------- + + // Calcul de la résistance interne en fonction de la température : + resistance_interne = calcul_niveau(batteries.batteries[i].points_temperature_resistance_interne, MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante); + if (resistance_interne != -1) resistance_interne = resistance_interne * batteries.batteries[i].resistance_interne / 1000; // Le '/1000' permet de passer de valeurs de résistance interne en mOhms à Ohms. + else resistance_interne = batteries.batteries[i].resistance_interne / 1000; // Le '/1000' permet de passer de valeurs de résistance interne en mOhms à Ohms. +#ifdef __DEBUG_BATTERIES +// sprintf(tmp, "Valeur brute de résistance interne = %.2f mOhms, température batterie = %.2f °C, soit résistance interne corrigée = %.2f mOhms", batteries.batteries[i].resistance_interne, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante, (resistance_interne * 1000)); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + // Estimation de la tension à vide (en tenant compte de la chute de tension due à la résistance interne de la batterie) : + tension_a_vide = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante - (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante * resistance_interne); + + if ((batteries.batteries[i].sequenceur.etat != ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE) && // Le calcul de l'EOD, et, in fine, du SOC n'est pas effectué tant qu'on est à l'état de charge nulle... + (batteries.batteries[i].sequenceur.etat != ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE)) // ... ou de pleine charge. + { + // Calcul de l'EOD : + // Calculs fonction d'une charge ou d'une décharge : + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante < 0) // Il est important de ne PAS prendre la valeur 0 (sinon le calcul de 'duree_decharge_corrigee' va planter via division par 0) + { + efficacite = 1 / batteries.batteries[i].efficacite_decharge; + + // Effet Peukert : + duree_decharge_corrigee = batteries.batteries[i].heures_capacite * powf(((batteries.batteries[i].capacite / batteries.batteries[i].heures_capacite) / (-batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante)), batteries.batteries[i].coeff_peukert); // Le signe - devant le courant permet d'avoir une valeur positive (sinon POWF va générer une erreur) + courant_differentiel_corrige = -(float)batteries.batteries[i].capacite / duree_decharge_corrigee; // Le signe '-' permet de revenir à un courant négatif en cas de décharge + } + else + { + efficacite = batteries.batteries[i].efficacite_charge; + courant_differentiel_corrige = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante; // Pas de correction de Peukert en charge... + } + + // Calculs intermédiaires (facilite la lecture et le debug) : + delta_energie = courant_differentiel_corrige * efficacite * (BATTERIES_PERIODE_MESURES * 0.1 / 3600); // La dernière () permet de passer en heures. +// SDIR #337 : capacite_reelle = batteries.batteries[i].capacite * batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante / 100; // Le '100' permet de tenir compte du fait que le SOH est en pourcents + capacite_reelle = batteries.batteries[i].capacite; // SDIR #337 + + // Calcul de l'EOD proprement dit : + batteries.batteries[i].eod += delta_energie; + if (batteries.batteries[i].eod > 0) batteries.batteries[i].eod = 0; // SDIR #337 +//printf("Courant diff = %.3f, efficacité = %.3f, Delta T = %.6f secondes donc Delta Energie = %.6fAh => energie_dechargee = %.6fAh. Capacite_reelle = %.2fAh\n", batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, efficacite, (BATTERIES_PERIODE_MESURES * 0.1 / 3600), delta_energie, batteries.batteries[i].eod, capacite_reelle); + + // Mise à jour du SOC à partir de l'EOD : + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = (1 + (batteries.batteries[i].eod / capacite_reelle)) * 100; // EOD est négative par nature. Le 'x100' permet de revenir d'une fraction en pourcents + } + else + { + efficacite = 0; // Permet d'éviter un warning losqu'il n'est pas calculé (état de pleine charge ou de charge nulle) mais utilisé par le message de debug + courant_differentiel_corrige = 0; // Idem + } + + //-------------------------------- + // Mode repos : + //-------------------------------- +/* if (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) < batteries.batteries[i].courant_repos) + { + soc = calcul_niveau(batteries.batteries[i].points_tension_soc, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE, tension_a_vide); + if (soc >= 0) + { +// batteries.batteries[i].soc_coeff_filtrage = 50; +// float old = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; +// float new; +// new = moyenne_mobile(old, soc, batteries.batteries[i].soc_coeff_filtrage); +// sprintf(tmp, "Old = %.3f%%, Add=%.3f%%, New = %.3f%% et le coeff %i. ", old, soc, new, batteries.batteries[i].soc_coeff_filtrage); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = moyenne_mobile(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, soc, batteries.batteries[i].soc_coeff_filtrage); +sprintf(tmp, "La batterie #%i est en mode REPOS. Son nouveau SOC = %.1f%%, il a été mis à jour via moyenne mobile avec la nouvelle valeur %.1f%% et le coeff %i. ", i, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, soc, batteries.batteries[i].soc_coeff_filtrage); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + // Sinon, on n'utilise pas la valeur calculée car il ne sert à rien de corriger avec une valeur erronée... + } +*/ + //-------------------------------- + // Mode Décharge : + //-------------------------------- + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante < 0) + { + // Vérification du passage en "charge nulle" : + switch(batteries.batteries[i].sequenceur.etat) + { + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_NORMAL: + { + if (tension_a_vide <= batteries.batteries[i].tension_charge_nulle) + { + batteries.batteries[i].sequenceur.compteur = batteries.batteries[i].temporisation_charge_nulle; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE; + } + break; + } + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE: + { + if (tension_a_vide > batteries.batteries[i].tension_charge_nulle) // Cas où on n'a pas eu le temps d'attendre la tempo de confirmation avant remontée de la tension. + { + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + + if (batteries.batteries[i].sequenceur.compteur == 0) // Cas où le passage à charge nulle est confirmé : + { + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = -(batteries.batteries[i].eod * 100) / batteries.batteries[i].capacite; + sprintf(tmp, "La Batterie [%i] est confirmée en CHARGE NULLE. EOD = %.3fAh et Capacité = %.2fAh donc SOH = %.3f%%", (i+1), batteries.batteries[i].eod, batteries.batteries[i].capacite, batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE; + } + break; + } + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE: + { + if (tension_a_vide > (batteries.batteries[i].tension_charge_nulle + BATTERIES_HYSTERESIS_CHARGE_NULLE)) + { + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + + break; + } + //...................................... + default : + { +#ifdef __DEBUG_BATTERIES + sprintf(tmp, "L'état du séquenceur de la Batterie [%i] est inconnu pour ce mode [%i]. Il est repositionné à NORMAL", (i+1), batteries.batteries[i].sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + } + } + + //-------------------------------- + // Mode Charge : + //-------------------------------- + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > 0) + { + switch(batteries.batteries[i].sequenceur.etat) + { + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_NORMAL: + { + // Détection d'un passage à l'état "Pleine charge" : + if ((tension_a_vide >= batteries.batteries[i].tension_pleine_charge) && + (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante <= batteries.batteries[i].courant_pleine_charge)) + { +#ifdef __DEBUG_BATTERIES + sprintf(tmp, "Les conditions du mode PLEINE CHARGE ont été détectées sur la Batterie [%i]. La temporisation de vérification (%is) est lancée ", (i+1), batteries.batteries[i].temporisation_pleine_charge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + batteries.batteries[i].sequenceur.compteur = batteries.batteries[i].temporisation_pleine_charge; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE; + } + break; + } + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE: + { + // Détection de sortie de l'état "Pleine charge" avant d'avoir été confirmé : + if ((tension_a_vide < batteries.batteries[i].tension_pleine_charge) || // Si la tension est redescendue... + (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > batteries.batteries[i].courant_pleine_charge)) // ... ou le courant remonté avant l'expiration de la tempo : + { + sprintf(tmp, "Les conditions du mode PLEINE CHARGE ont disparu sur la Batterie [%i]. La batterie est repositionnée en mode NORMAL ", (i+1)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + + // Confirmation du passage à l'état "Pleine charge" : + if (batteries.batteries[i].sequenceur.compteur == 0) // Cas où le passage à pleine charge est confirmé : + { + sprintf(tmp, "La Batterie [%i] est confirmée en PLEINE CHARGE ", (i+1)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; + batteries.batteries[i].eod = 0; +// batteries.batteries[i].tension_pleine_charge = tension_a_vide - BATTERIES_DELTA_TENSION_PLEINE_CHARGE_AUTO; // Ajustement auto de la tension pleine charge + + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE; + } + break; + } + //...................................... + case ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE: + { + // Détection de sortie de l'état "Pleine charge" : + if ((tension_a_vide < batteries.batteries[i].tension_pleine_charge) || // Si la tension est redescendue... + (batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante > batteries.batteries[i].courant_pleine_charge)) // ... ou le courant remonté : + { + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + +// Etat "pleine charge" : +// batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; +// batteries.batteries[i].eod = 0; + + break; + } + //...................................... + default : + { +#ifdef __DEBUG_BATTERIES + sprintf(tmp, "L'état du séquenceur de la Batterie [%i] est inconnu pour ce mode [%i]. Il est repositionné à NORMAL", (i+1), batteries.batteries[i].sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + batteries.batteries[i].sequenceur.compteur = 0; + batteries.batteries[i].sequenceur.etat = ETAT_SEQUENCEUR_BATTERIE_NORMAL; + break; + } + } + } + + // Vérification du SOC : + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante > 100) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante < 0) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 0; + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; + + + //-------------------------------------------- + // Calcul des nombres de cycles : + //-------------------------------------------- + if (batteries.batteries[i].flag_increment_cycles == 0) // Le compteur est armé et n'a pas encore été incrémenté : + { + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante <= BATTERIES_SEUIL_SOC_BAS_NOMBRE_CYCLES) + { + batteries.batteries[i].flag_increment_cycles = 1; // Blocage du flag jusqu'au prochain passage par 100% + batteries.batteries[i].nombre_cycles ++; // Incrémentation du compteur + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + } + else // Le compteur "Nombre_cylces" a été incrémenté, on attend maintenant de revenir à 100% pour le réarmer : + { + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 100) + { + batteries.batteries[i].flag_increment_cycles = 0; // Réarmement du flag pour le prochain cycle + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + } + + //-------------------------------------------- + // Calcul du nombre de décharges complètes : + //-------------------------------------------- + if (batteries.batteries[i].flag_increment_decharges_completes == 0) // Le compteur est armé et n'a pas encore été incrémenté : + { + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 0) + { + batteries.batteries[i].flag_increment_decharges_completes = 1; // Blocage du flag jusqu'au prochain passage par 100% + batteries.batteries[i].nombre_decharges_completes ++; // Incrémentation du compteur + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + } + else // Le compteur "Nombre_cylces" a été incrémenté, on attend maintenant de revenir à 100% pour le réarmer : + { + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante == 100) + { + batteries.batteries[i].flag_increment_decharges_completes = 0; // Réarmement du flag pour le prochain cycle + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + } + + + + //------------------------------------- + // Calcul du SOH : + //------------------------------------- + // Vérification du SOH : + // if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante > 100) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = 100; + if (batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante < BATTERIES_SEUIL_MINI_SOH) batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = BATTERIES_SEUIL_MINI_SOH; + + // Mise à jour du cumul : + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante; + + + + + +//------------------------------------- +// Calcul de l'autonomie : +//------------------------------------- + +// Mise à jour du cumul : +batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].cumul += batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante; + + + + //------------------------------------- + // Mise à jour BDD : + //------------------------------------- + // Mise à jour dans la BDD si les valeurs ont suffisemment évolué : +// if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TEMPERATURE].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_TENSION].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_CHARGE].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOH].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_SOC].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_AUTONOMIE].marge) || +// (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) >= batteries.variables[BATTERIE_VARIABLE_PUISSANCE].marge)) + if ((fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante) > 0) || + (fabs(batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd - batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante) > 0)) + { + batteries.batteries[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + +#ifdef __DEBUG_BATTERIES +char texte_etat[200]; + +if (++ batteries.compteur_affichage_debug == 6) // Comme BATTERIES_PERIODE_MESURES = 300ms, cela donne un affichage toutes les 1.8s +{ + batteries.compteur_affichage_debug = 0; + + switch(batteries.batteries[i].sequenceur.etat) + { + case ETAT_SEQUENCEUR_BATTERIE_NORMAL: { strcpy(texte_etat, "NORMAL"); break; } + case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE: { strcpy(texte_etat, "ATTENTE_CONFIRMATION_CHARGE_NULLE"); break; } + case ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE: { strcpy(texte_etat, "CHARGE_NULLE"); break; } + case ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE: { strcpy(texte_etat, "ATTENTE_CONFIRMATION_PLEINE_CHARGE"); break; } + case ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE: { strcpy(texte_etat, "PLEINE_CHARGE"); break; } + default: { strcpy(texte_etat, "Inconnu"); break; } + } + + sprintf(tmp, "Batterie #%i : Etat = %s, Température = %.2f°C, Tension = %.3fV, Courant diff = %.3fA, courant charge = %.3fA, Courant décharge = %.3fA, SOH = %.2f%%, SOC = %.2f%%, Autonomie = %.2fh et puissance = %.3fW. Tension à vide = %.3fV, résistance interne corrigée = %.2fmOhms, efficacité = %.3f, courant_differentiel_corrigé = %.3fA, capacité = %.2fAh, EOD (énergie déchargée depuis démarrage) = %.5fAh", i, + texte_etat, + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante, + tension_a_vide, + (resistance_interne * 1000), // Pour passer de Ohms à mOhms + efficacite, + courant_differentiel_corrige, + batteries.batteries[i].capacite, + batteries.batteries[i].eod + ); + +/*sprintf(tmp, "Batterie #%i : Etat = %s, Tension = %.3fV, Courant diff = %.3fA. Tension à vide = %.3fV, efficacité = %.3f, courant_differentiel_corrigé = %.3fA, capacité = %.2fAh, duree_decharge_corrigee = %.2fh, EOD = %.5fAh", i, + texte_etat, + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante, + tension_a_vide, + efficacite, + courant_differentiel_corrige, + batteries.batteries[i].capacite, + duree_decharge_corrigee, + batteries.batteries[i].eod + ); +*/ + + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +} +#endif + } // Fin du traitement pour batterie sur IBS + + batteries.batteries[i].cumuls_nb_val ++; // MàJ du nombre d'échantillons dans le cumul + + + // Mise à jour des valeurs BDD des variables : + if (batteries.batteries[i].flag_dde_maj_bdd) + { + for (j = 0; j < MAX_NOMBRE_VARIABLES_BATTERIES; j ++) batteries.batteries[i].variables[j].valeur_bdd = batteries.batteries[i].variables[j].valeur_courante; + } + } + + } + + } + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Batteries est inconnu [%i]. Il est repositionné au REPOS", batteries.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + batteries.sequenceur.etat = ETAT_SEQUENCEUR_BATTERIES_REPOS; + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Recherche la batterie dont la Clé_primaire est reçue en paramètre +// Reçoit : La Cle_primaire de la batterie à rechercher +// Retourne : le numéro de batterie correspondant (ou -1 si aucun circuit valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_BATTERIES - 1. +//********************************************************************************************************************************************************************************** +int16_t batterie_cle_primaire(uint16_t cle_primaire) +{ +uint16_t i; + + + if (cle_primaire == 0) return (-1); // Inutile de chercher si le n° à chercher est nul... + + for (i = 0; i < batteries.nb; i ++) + { + if (batteries.batteries[i].flag_validite == FALSE) continue; + if (batteries.batteries[i].cle_primaire == cle_primaire) return(i); + } + return (-1); // Si on est arrivé au bout de la boucle, on renvoi le code de 'non trouvé'. +} + + +//********************************************************************************************************************************************************************************** +// Fonction VARIABLE_BATTERIE_CLE_PRIMAIRE +// Reçoit en paramètre : +// - La Cle_primaire de la variable à rechercher +// Retourne : +// - le numéro de variable correspondant (ou -1 si aucune variable valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_VARIABLES_BATTERIES-1. +//********************************************************************************************************************************************************************************** +int16_t variable_batterie_cle_primaire(uint16_t cle_primaire) +{ +uint16_t i; + + for (i=0; i 0 car sinon la capacité réelle sera nulle ou négative et plantera le calcul. +#define BATTERIES_SEUIL_SOC_BAS_NOMBRE_CYCLES 60 // En %. Défini la valeur en-dessous de laquelle le compteur 'Nombre_cycles' est incrémenté (NB : il faut ensuite repasser par un SOC=100% avant de compter un nouveau cycle) + + + // Variables de la batterie : +#define BATTERIE_VARIABLE_TEMPERATURE 0 +#define BATTERIE_VARIABLE_TENSION 1 +#define BATTERIE_VARIABLE_COURANT_DIFFERENTIEL 2 +#define BATTERIE_VARIABLE_COURANT_CHARGE 3 +#define BATTERIE_VARIABLE_COURANT_DECHARGE 4 +#define BATTERIE_VARIABLE_SOH 5 +#define BATTERIE_VARIABLE_SOC 6 +#define BATTERIE_VARIABLE_AUTONOMIE 7 +#define BATTERIE_VARIABLE_PUISSANCE 8 +#define BATTERIE_VARIABLE_ENERGIE 9 +// NB : ces valeurs doivent rester strictement inférieures à MAX_NOMBRE_VARIABLES_BATTERIES + + + // Commandes de la batterie : +#define BATTERIE_COMMMANDE_FORCAGE_PLEINE_CHARGE 1 +#define BATTERIE_COMMMANDE_RESET_SOH 2 +#define BATTERIE_COMMMANDE_RESET_COMPTEURS 3 + + + // Etats du séquenceur d'une batterie : +#define ETAT_SEQUENCEUR_BATTERIE_NORMAL 0 +#define ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_CHARGE_NULLE 1 +#define ETAT_SEQUENCEUR_BATTERIE_CHARGE_NULLE 2 + +#define ETAT_SEQUENCEUR_BATTERIE_ATTENTE_CONFIRMATION_PLEINE_CHARGE 10 +#define ETAT_SEQUENCEUR_BATTERIE_PLEINE_CHARGE 11 + + + // Etats du séquenceur Batteries : +#define ETAT_SEQUENCEUR_BATTERIES_INIT 0 +#define ETAT_SEQUENCEUR_BATTERIES_REPOS 1 diff --git a/sources/bdd.h b/sources/bdd.h new file mode 100644 index 0000000..7cc77b4 --- /dev/null +++ b/sources/bdd.h @@ -0,0 +1,249 @@ +/********************************************************************************************************************************************************************************** + Fichier GESTION_BDD.H + Contient toutes les définitions liées à la gestion de la BDD +**********************************************************************************************************************************************************************************/ + // Maximums : +#define MAX_LONGUEUR_REQUETE_MYSQL 3500 +#define MAX_LONGUEUR_SOUS_REQUETE_MYSQL 1000 +#define MAX_NOMBRE_CHAMPS_INTERNES 300 // Utilisé pour dimensionner les tableaux de pointeurs de décodage (exemple : champs 'Entrees' des Réservoirs, Batteries ou Capteurs) +#define MAX_LONGUEUR_NOM_FICHIER_SAUVEGARDE_BDD 150 +#define MAX_LONGUEUR_OPTION_COMMANDE_SAUVEGARDE_BDD 300 +#define MAX_LONGUEUR_NOM_TABLE 50 +#define MAX_NOMBRE_TABLES_FRAGMENTEES 20 +#define MAX_LONGUEUR_NOM_CHAMP_BDD 80 + + // Valeurs fonctionnelles : +#define MAX_TENTATIVES_CONNEXION_BDD 30 // Au démarrage, la connexion à la BDD sera tentée autant de fois, avec une attente de 1s entre chaque essais (permet à tous les services d'être prêts au démarrage) +#define PERIODE_CHECK_SYNCHRO_WEB 10 // x 10ms +#define PERIODE_MAJ_BDD 10 // x 100ms +#define PERIODE_ENREGISTREMENT_MESURES 150 // x 100ms + + + + // Types d'init de la BDD : +#define BDD_MODE_INIT_RESET 0 // Au démarrage après reset +#define BDD_MODE_INIT_RELOAD 1 // Uniquement un rechargement des paramètres de config sur un système déjà démarré + + // Types d'enrehistrement de mesures : +#define BDD_ENREGISTREMENT_MESURE_INIT 0 // Au démarrage après reset. Les valeurs enregistrées sont toutes à NULL avec date de dernier Heart-Beat connu +#define BDD_ENREGISTREMENT_MESURE_NORMAL 1 // Enregistrement normal (date en cours et valeur courante) +#define BDD_ENREGISTREMENT_MESURE_PONCTUEL 2 // Enregistrement d'une valeur poctuelle (notamment capteur configuré en mode MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + + // Types de demandes de mise à jour dans la BDD : +#define BDD_TYPE_MAJ_AUCUNE 0 +#define BDD_TYPE_MAJ_COURANTE 1 +#define BDD_TYPE_MAJ_CONFIG 2 + + // Typde d'erreur K-Bus : +#define DEFAUT_K_BUS_AUCUN 0 +#define DEFAUT_K_BUS_MINEUR 1 +#define DEFAUT_K_BUS_MAJEUR 2 + + // Types d'archivage : +// Nb : les valeurs 0 et 1 étaient historiqueemnt utilisées et on n'a pas tout redécalé pour des raisons de simplicité... +// D'autant que la valeur 0 est particulière pour les enregistrments de TB_Mesures_circuits... +#define BDD_TYPE_ARCHIVAGE_1_HEURE 2 // L'archivage exécutera d'abord un Type 3 (5 minutes) puis un Type 2 (1h) dans la foulée +#define BDD_TYPE_ARCHIVAGE_5_MINUTES 3 // Uniquement un Type 3 +#define BDD_TYPE_ARCHIVAGE_PURGE_SEULE 10 +#define BDD_TYPE_ARCHIVAGE_1_HEURE_ET_PURGE 12 // L'archivage exécutera d'abord un Type 3 (5 minutes) puis un Type 2 (1h) dans la foulée puis une purge [ce type d'appel est déconseillé car il cumule les 3 traitements !] +#define BDD_TYPE_ARCHIVAGE_5_MINUTES_ET_PURGE 13 // L'archivage exécutera d'abord un Type 3 (5 minutes) puis une purge dans la foulée + + + // Modes enregistrement des mesures +#define MODE_ENREGISTREMENT_MESURES_AUCUN 0 // Aucun enregistrement de mesures +#define MODE_ENREGISTREMENT_MESURES_PERIODIQUE 1 // Enregistrement des mesures périodique avec intégration sur la période et création de nouvelles mesures uniquement lorsque le seuil de marge est dépassé +#define MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT 2 // Enregistrement d'une mesure sans cumul, chaque fois que le seuil de marge est dépassé + + + // Modes enregistrement de l'historique +#define MODE_ENREGISTREMENT_HISTORIQUE_AUCUN 0 // Les changements d'état ne sont pas enregistrés dans l'historique +#define MODE_ENREGISTREMENT_HISTORIQUE_NORMAL 1 // Les changements d'état dont enregistrés dans l'historique + + + // Type mesure : +#define SOURCE_MESURE_CIRCUIT 1 +#define SOURCE_MESURE_CAPTEUR 2 +#define SOURCE_MESURE_RESERVOIR 3 +#define SOURCE_MESURE_BATTERIE 4 + + // Etat fichier download: +#define ETAT_FICHIER_DOWNLOAD_LISTE 0 +#define ETAT_FICHIER_DOWNLOAD_TELECHARGE 1 +#define ETAT_FICHIER_DOWNLOAD_VERIFIE 2 +#define ETAT_FICHIER_DOWNLOAD_UPLOADE_COMPAGNON 3 +#define ETAT_FICHIER_DOWNLOAD_UPLOADE_EXTENSION 4 +#define ETAT_FICHIER_DOWNLOAD_INSTALLE_EXECUTE 5 +#define ETAT_FICHIER_DOWNLOAD_ARCHIVE 6 +#define ETAT_FICHIER_DOWNLOAD_EFFACE 7 + + + // Codes prédéfinis de messages aux users : +#define MESSAGE_USERS_CODE_AUCUN 0 +#define MESSAGE_USERS_CODE_FIN_MINUTEUR 1 // Paramètres = 'compteur_initial' au format 'hh:mm:ss' +#define MESSAGE_USERS_CODE_REVEIL 2 // Paramètres = 'condition_horaire' au format '[jour_semaine] @ [hh:mm]' +#define MESSAGE_USERS_CODE_MAJ_EN_COURS 3 // Paramètres = 'id_module';'avancement_en_%' (id_module: -1=démarrage, 0-N=Id_module en cours, -2=fin) +#define MESSAGE_USERS_CODE_REDEMARRAGE 4 // Paramètres = 'temps_restant' en ms ou rien +#define MESSAGE_USERS_CODE_NOUVELLE_VERSION_DETECTEE 5 // Paramètres = 'ancienne version';'nouvelle version' +#define MESSAGE_USERS_CODE_DEFAUT_CONFIG_K_BUS 6 // Paramètres = 'adresse_k_bus du premier module Nok';'Type_module_physique à cette adresse (yc -1 pour type inconnu)';'Type module configuré'. + // Il peut donc y avoir : - Physique inconnu (pas de module physique à cette adresse) + config d'un type connu => vérifier K-bus entre module (@Adr-1) et module (@Adr) + // - Physique connu + config d'un autre type ==> Il est probable qu'un/des modules aient été ajoutés/enlevés/déplacés ==> Aller dans config-K-Bus +#define MESSAGE_USERS_CODE_DECLENCHEMENT_EVENT 7 // Paramètres = 'id_event' + + + // Etat du message users : +#define MESSAGE_USERS_ETAT_SUPPRESSION -1 +#define MESSAGE_USERS_ETAT_INFO 0 +#define MESSAGE_USERS_ETAT_ALERTE 1 +#define MESSAGE_USERS_ETAT_URGENT 2 + + + + // Historique : + // Valeur principale (Système, Circuits, Capteurs, ... ne sert qu'à router sur la bonne table : TB_Historique_systeme, TB_Historique_circuits, etc...) + // Event en uint8_t (valeur par défaut = 0) + // ID en int (cle primaire de l'élément concerné) + // Valeur en int (valeur par défaut = 0) + // Complément en texte + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_SYSTEME 0 + #define SYSTEME_REINIT 1 // Réinitialisation de l'application + #define SYSTEME_ERREUR_MYSQL 2 + #define SYSTEME_CONNEXION_USER 3 + #define SYSTEME_REBOOT 4 // Reboot du système + #define SYSTEME_ARRET 5 + #define SYSTEME_TIME_OUT_CONNEXION 6 + #define SYSTEME_DECONNEXION_USER 7 + #define SYSTEME_CONFIGURATION_INVALIDE_DEMARRAGE_IMPOSSIBLE 8 + #define SYSTEME_SIGNAL_RECU 9 // La valeur du signal reçu est stocké dans Valeur + #define SYSTEME_DEMANDE_REINIT 10 + #define SYSTEME_CHANGEMENT_VEILLE 11 // La nouvelle valeur de 'veille' est stockée dans Valeur + #define SYSTEME_ERREUR_SAUVEGARDE_BDD 12 // Le message d'info sur l'erreur est stocké dans le complément. + + #define SYSTEME_MODEM_DEMANDE_ACTIVATION 20 // La Valeur indique dans quel sens (On ou Off) + #define SYSTEME_MODEM_CHANGEMENT_ETAT 21 // La Valeur indique dans quel sens (On ou Off) + #define SYSTEME_MODEM_ERREUR_CHANGEMENT_ETAT 22 // La Valeur indique dans quel sens (On ou Off) le changement était demandé et a échoué + #define SYSTEME_MODEM_ERREUR_ETABLISSEMENT_LIAISON 23 + #define SYSTEME_MODEM_CHANGEMENT_ETAT_NON_SOLLICITE 24 // La Valeur indique dans quel sens (On ou Off) le modem se trouve alors qu'il ne devrait pas + #define SYSTEME_MODEM_CHANGEMENT_REVISION_FIRMWARE 25 // La valeur de révision est stockée dans Complément + #define SYSTEME_MODEM_CHANGEMENT_IMEI 26 // Le n° est stocké dans Complément + #define SYSTEME_MODEM_CHANGEMENT_IMSI 27 // Le n° est stocké dans Complément + #define SYSTEME_MODEM_CHANGEMENT_ICCID 28 // Le n° est stocké dans Complément + #define SYSTEME_MODEM_CHANGEMENT_NO_TELEPHONE 29 // Le n° est stocké dans Complément + #define SYSTEME_MODEM_CHANGEMENT_APN 30 // Le n° est stocké dans Complément + #define SYSTEME_MODEM_ERREUR_FTP 31 // Le code d'erreur reçu du modem est stocké dans Valeur et la réponse complète du modem dans Complement + #define SYSTEME_MODEM_ERREUR_GNSS 32 // Le cycle d'acquisition n'a pas pu être réalisé complètement. Le nombre de cycles valides est indiqué dans Valeur. + #define SYSTEME_MODEM_ERREUR_M_BUS_PLEIN 33 // Le nombre de refus de nouvelle commande dans le buffer de commandes est trop important. Le nb de refus consécutifs est dans Valeur + #define SYSTEME_MODEM_PERTE_LIAISON_4G_LTE 34 + #define SYSTEME_MODEM_ETABLISSEMENT_LIAISON_4G_LTE 35 + + #define SYSTEME_MODULES_NOUVEAU_MODULE 40 // L'adresse K-Bus où est apparu le module est dans 'id'. Le Type de module est dans Valeur. L'adresse MAC est dans Complément + #define SYSTEME_MODULES_SUPPRESSION_MODULE 41 // L'adresse K-Bus où était le module est dans 'id'. Le Type de module est dans Valeur. L'adresse MAC est dans Complément + + #define SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_OK 50 // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur + #define SYSTEME_FTP_UPLOAD_FICHIER_DEBUT_NOK 51 // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur + #define SYSTEME_FTP_UPLOAD_FICHIER_FIN_OK 52 // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur + #define SYSTEME_FTP_UPLOAD_FICHIER_FIN_NOK 53 // Le nom du fichier source et du fichier destination sont stockés dans complément + + #define SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_OK 60 // Le nom du fichier source et du fichier destination sont stockés dans complément. + #define SYSTEME_FTP_DOWNLOAD_FICHIER_DEBUT_NOK 61 // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur + #define SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_OK 62 // Le nom du fichier source et du fichier destination sont stockés dans complément. + #define SYSTEME_FTP_DOWNLOAD_FICHIER_FIN_NOK 63 // Le nom du fichier source et du fichier destination sont stockés dans complément. La taille dans valeur + #define SYSTEME_FTP_DOWNLOAD_LIST_NOK 64 // Le nom du fichier de LIST est stocké dans complément. + #define SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_LOCAUX_TROP_ELEVE 65 // Le nombre maxi admissible est stocké dans Valeur + #define SYSTEME_FTP_DOWNLOAD_NOMBRE_FICHIERS_SERVEUR_TROP_ELEVE 66 // Le nombre maxi admissible est stocké dans Valeur + + #define SYSTEME_EXECUTION_BASH_DEBUT_OK 70 // Le nom du fichier utilisé pour l'upload est dans le complément. + #define SYSTEME_EXECUTION_BASH_DEBUT_NOK 71 // Le nom du fichier utilisé pour l'upload est dans le complément. + #define SYSTEME_EXECUTION_BASH_FIN_OK 72 // Le nom du fichier utilisé pour l'upload est dans le complément. + #define SYSTEME_EXECUTION_BASH_FIN_NOK 73 // Le nom du fichier utilisé pour l'upload est dans le complément. + #define SYSTEME_COPIE_ARCHIVE_A_VALIDER_OK 74 // Le nom du fichier est dans le complément. + #define SYSTEME_COPIE_ARCHIVE_A_VALIDER_NOK 75 // Le nom du fichier est dans le complément. + #define SYSTEME_COPIE_ARCHIVE_A_CHARGER_OK 76 // Le nom du fichier est dans le complément. + #define SYSTEME_COPIE_ARCHIVE_A_CHARGER_NOK 77 // Le nom du fichier est dans le complément. + + #define SYSTEME_CHARGEMENT_CODE_DEBUT_OK 80 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_DEBUT_NOK 81 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_ERASE_FLASH_NOK 82 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK 83 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_CRC_FLASH_NOK 84 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_FIN_OK 85 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + #define SYSTEME_CHARGEMENT_CODE_FIN_NOK 86 // Le nom du fichier utilisé pour l'upload est dans le complément. Le n° de la carte dans ID + + + + #define SYSTEME_UART_BUFFER_RX_PLEIN 100 // Valeur précise quel UART + #define SYSTEME_BUS_BUFFER_COMMANDES_PLEIN 101 // Valeur précise quel bus ('M' ou 'C') + + #define SYSTEME_BACKLIGHT_CHANGEMENT_CONSIGNE 110 // La valeur indique la nouvelle consigne + #define SYSTEME_VOLUME_CHANGEMENT_CONSIGNE 111 // La valeur indique la nouvelle consigne + #define SYSTEME_LEDS_CHANGEMENT_CONSIGNE 112 // La valeur indique la nouvelle consigne + + #define SYSTEME_WIFI_MISE_A_JOUR_CONFIG 130 // Les informations sur la nouvelle config sont stockées dans Complément + + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_CIRCUITS 1 + #define CIRCUIT_CHANGEMENT_CONSIGNE 1 // La nouvelle valeur de consigne est stockée dans Valeur. De plus, Complément comprend 'origine_consigne/id_origine_consigne'. + + #define CIRCUIT_OK 10 // La valeur de limitation (en mA) est stockée dans Valeur + #define CIRCUIT_LIMITATION 11 // La valeur de limitation (en mA) est stockée dans Valeur + #define CIRCUIT_PROTECTION 12 // La valeur de limitation (en mA) est stockée dans Valeur + #define CIRCUIT_DISJONCTE 13 // La valeur de limitation (en mA) est stockée dans Valeur + #define CIRCUIT_CC_BATTERIE 14 // La valeur de limitation (en mA) est stockée dans Valeur + #define CIRCUIT_DELESTAGE 15 // La valeur de limitation (en mA) est stockée dans Valeur + + #define CIRCUIT_CREATION 20 + #define CIRCUIT_MODIFICATION 21 + #define CIRCUIT_SUPPRESSION 22 + + #define CIRCUIT_CONFIGURATION_INVALIDE 30 // L'ID précise la clé primaire. Valeur contient l'ID_Carte actuellement configuré + + #define CIRCUIT_EXPIRATION_TIME_OUT 40 // La valeur initiale du time-out est stockée dans Valeur + + #define CIRCUIT_ACTION_EVENT 50 // L'ID précise la clé primaire. Valeur contient la clé_primaire de l'event qui a déclenché l'action sur le circuit + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_CAPTEURS 2 + #define CAPTEUR_PASSAGE_OK 1 // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement + #define CAPTEUR_PASSAGE_ALERTE 2 // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement + #define CAPTEUR_PASSAGE_ALARME 3 // L'ID précise la clé primaire. VAleur contient la valeur du capteur au moment du changement + + #define CAPTEUR_OW_DEVICE_OK 40 // L'ID précise la clé primaire. Complément contient le Rom_code du device One_wire + #define CAPTEUR_OW_DEVICE_NOK 41 // L'ID précise la clé primaire. Complément contient le Rom_code du device One_wire + #define CAPTEUR_OW_BUS_NOK 42 // L'ID précise la clé primaire. Complément contient le Rom_code du device One_wire + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_RESERVOIRS 3 + #define RESERVOIR_PASSAGE_NIVEAU_NORMAL 1 // L'ID précise le Réservoir. La valeur indique l'ancien état + #define RESERVOIR_PASSAGE_NIVEAU_ALERTE 2 // L'ID précise le Réservoir. La valeur indique l'ancien état + #define RESERVOIR_PASSAGE_NIVEAU_ALARME 3 // L'ID précise le Réservoir. La valeur indique l'ancien état + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_BATTERIES 4 + #define BATTERIE_ALARME_TEMPERATURE (BATTERIE_VARIABLE_TEMPERATURE + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_TENSION (BATTERIE_VARIABLE_TENSION + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_COURANT_DIFFERENTIEL (BATTERIE_VARIABLE_COURANT_DIFFERENTIEL + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_COURANT_CHARGE (BATTERIE_VARIABLE_COURANT_CHARGE + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_COURANT_DECHARGE (BATTERIE_VARIABLE_COURANT_DECHARGE + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_SOH (BATTERIE_VARIABLE_SOH + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_SOC (BATTERIE_VARIABLE_SOC + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_AUTONOMIE (BATTERIE_VARIABLE_AUTONOMIE + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + #define BATTERIE_ALARME_PUISSANCE (BATTERIE_VARIABLE_PUISSANCE + 1) // L'ID précise la batterie. La valeur indique l'état : 0=Ok, 1=Défaut seuil bas, 2=Défaut seuil haut, 3=Défaut seuil haut ET seuil bas, 99=Défaut calcul ou capteur (état inconnu) + + + #define BATTERIE_RX_CDE_FORCAGE_PLEINE_CHARGE 11 // L'ID précise la batterie. + #define BATTERIE_RX_CDE_RESET_SOH 12 // L'ID précise la batterie. + #define BATTERIE_RX_CDE_RESET_COMPTEURS 13 // L'ID précise la batterie. + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_USB 5 + #define USB_CHANGEMENT_CONSIGNE 1 // La valeur indique le nouvel état. L'ID contient le n° du port (1 pour ports Avant ou 4 pour port Arrière) + + #define USB_PASSAGE_OK 10 // L'ID contient le n° du port (1 à 3 ou 4 pour arrière) + #define USB_PASSAGE_DEFAUT 11 // L'ID contient le n° du port (1 à 3 ou 4 pour arrière) + +//------------------------------------------------------------- +#define TYPE_SOURCE_HISTORIQUE_EVENTS 6 + #define EVENT_DECLENCHEMENT 1 // L'ID contient la cle_primaire de l'event + \ No newline at end of file diff --git a/sources/bdd_admin.c b/sources/bdd_admin.c new file mode 100644 index 0000000..d1bf609 --- /dev/null +++ b/sources/bdd_admin.c @@ -0,0 +1,406 @@ +//********************************************************************************************************************************************************************************** +// Fichier BDD_ADMIN.C +// +// Contient les fonctions de supervision de la BDD (archivage, sauvegard,...) +// +//********************************************************************************************************************************************************************************** + +// #define __DEBUG_ARCHIVAGE //Décommenter cette ligne pour avoir les messages de debug concernant les fonctions d'archivage + +// Include : +#include "main.h" + +// Prototypes : +int32_t sauvegarde_bdd(void); +int8_t archivage_bdd(char * chaine_date_fin, uint8_t type); + + +// Variables globales : + + + +//================================================================================================================================================================== +// Fonction : Cette fonction génère un fichier de sauvegarde de la base de données. +// Le fichier est stocké dans le répertoire d'upload pour pouvoir être remonté au serveur Koriolan via FTP ensuite (cette remontée ne fait pas partie de la présente fonction) +// La base de données est sauvegardée en totalité (tous les enregistrements de toutes les tables) SAUF : +// - les tables TB_Mesures_xxx : seuls les enregistrements dont le champ No_paquet est identique au No_paquet de TB_Systeme sont remontés. +// - la table TB_Historique : seuls les enregistrements dont le champ No_paquet est identique au No_paquet de TB_Systeme sont remontés. +// Le champ No_paquet de TB_Systeme est incrémenté dès la fin de l'archive (afin que les nouveaux enregistrements soient tagués avec ce nouveau n°) +// Reçoit : Rien +// Renvoit : 0 si pas d'erreur. Valeur de retour du premier appel 'system()' qui est différent de 0. +//================================================================================================================================================================== +int32_t sauvegarde_bdd(void) +{ +int32_t retour_commande; +char str_date[MAX_LONGUEUR_DATE_COURANTE]; +char nom_fichier_sauvegarde[MAX_LONGUEUR_NOM_FICHIER_SAUVEGARDE_BDD]; +FILE * ptr_fichier_sauvegarde; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +char commande[MAX_TAILLE_COMMANDE_SHELL]; +char options[MAX_LONGUEUR_OPTION_COMMANDE_SAUVEGARDE_BDD]; +char filtre_table[MAX_LONGUEUR_OPTION_COMMANDE_SAUVEGARDE_BDD]; +char param[MAX_LONGUEUR_OPTION_COMMANDE_SAUVEGARDE_BDD]; +struct { + char nom[MAX_LONGUEUR_NOM_TABLE]; // Nom de la table (par exemple, TB_Mesures_capteurs') +} tables_fragmentees[MAX_NOMBRE_TABLES_FRAGMENTEES]; +uint8_t nb_tables_fragmentees; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +uint16_t i; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + sprintf(tmp, "Début de sauvegarde de la BDD avec n° de paquet = %i", systeme.no_paquet); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + // SDIR # 202 (début) : + // Vérification qu'une phase d'archivage n'est pas en cours (si c'est le cas, on va décaler la sauvagarde pour éviter de surcharger inutilement le système) : + if (get_pid_numbers_from_name(NOM_FICHIER_ARCHIVAGE, "") > 0) // Il y a au moins 1 processus d'archivage en cours... + { + systeme.compteur_sauvegarde_bdd = SYSTEME_TEMPO_REPORT_SAUVEGARDE; // Dans ce cas, on relance une tempo courte (5s typique) avant de refaire une tentative. + + sprintf(tmp, "Un processus [%s] a été détecté en cours d'exécution. La sauvegarde est temporisée pour %i secondes", NOM_FICHIER_ARCHIVAGE, systeme.compteur_sauvegarde_bdd); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return (-1); + } + // SDIR # 202 (fin) + + + + retour_commande = 0; + + // Calcul de la date et de l'heure, au bon format : + date_courante(str_date, DATE_COURANTE_SANS_MS, FORMAT_DATE_COURANTE_FICHIER); + + // Nom du fichier de sauvegarde : +// sprintf(nom_fichier_sauvegarde, "%s/%s_%s.sql", REPERTOIRE_SERVER_UPLOAD, NOM_FICHIER_SAUVEGARDE_BDD, str_date); // SDIR #298 + sprintf(nom_fichier_sauvegarde, // SDIR #298 + "%s/%02X%02X%02X%02X%02X%02X_%08i_%s.sql", // Le '%08i' permet de forcer le n° de paquet avec 8 chiffres commençant par 0. Sinon, '10' est trié avant '9' par exemple. 8 chiffres = 1 fichier par minutes pendant 190 ans... + REPERTOIRE_SERVER_UPLOAD, + systeme.modules_physiques[0].adresse_mac[0], systeme.modules_physiques[0].adresse_mac[1], systeme.modules_physiques[0].adresse_mac[2], + systeme.modules_physiques[0].adresse_mac[3], systeme.modules_physiques[0].adresse_mac[4], systeme.modules_physiques[0].adresse_mac[5], + systeme.no_paquet, str_date); // SDIR #298 + + // Options de la commande de sauvegarde (utilisée sur la commande 'mysqldump' : + sprintf(options, "--single-transaction --no-create-info --compact --complete-insert"); +// sprintf(options, "--single-transaction --skip-lock-tables --no-create-info --compact --complete-insert"); // SDIR # 202 + + + do // Fake loop... + { + //------------------------------------------------------------------------------------------- + // Remplissage de l'entête du fichier de sauvegarde (SDIR #292) : + //------------------------------------------------------------------------------------------- + ptr_fichier_sauvegarde = fopen(nom_fichier_sauvegarde, "w+"); // Ouverture du flux en lecture/écriture. Si le fichier existe, il sera alors écrasé et la position courante est fixée au début du fichier. Sinon le fichier sera créé. + if (ptr_fichier_sauvegarde == NULL) + { + printf("Ouverture du fichier de sauvegarde [ %s ] impossible. La sauvegarde est arrêtée !\n", nom_fichier_sauvegarde); + retour_commande = -1; + break; + } + + // Attention : le format '# [nom_de_champ=valeur_du_champ]' est utilisé par la fonction 'upload_code.php' sur le serveur... + // Il est donc fortement conseillé de ne PAS modifier le format de ces trames d'entête !!! + fprintf(ptr_fichier_sauvegarde, + "# [Adresse_mac=%02X:%02X:%02X:%02X:%02X:%02X]\n", + systeme.modules_physiques[0].adresse_mac[0], systeme.modules_physiques[0].adresse_mac[1], systeme.modules_physiques[0].adresse_mac[2], + systeme.modules_physiques[0].adresse_mac[3], systeme.modules_physiques[0].adresse_mac[4], systeme.modules_physiques[0].adresse_mac[5]); + fprintf(ptr_fichier_sauvegarde, "# [Nom_du_bateau=%s]\n", systeme.nom_bateau); + fprintf(ptr_fichier_sauvegarde, "# [No_paquet=%i]\n", systeme.no_paquet); + fprintf(ptr_fichier_sauvegarde, "# [Date_fichier=%s]\n", str_date); + fprintf(ptr_fichier_sauvegarde, "# [Version_soft_no_package=%s]\n", systeme.package_no_version); + + fclose(ptr_fichier_sauvegarde); + + // Fin de SDIR #292 + + + //------------------------------------------------------------------------------------------- + // Recherche des tables fragmentées : TB_Mesures* et TB_Historique + //------------------------------------------------------------------------------------------- + nb_tables_fragmentees = 0; + + // Tables de mesures : + sprintf(requete, "SHOW TABLES FROM shipheart LIKE 'TB_Mesures%%'"); + mysql_query(bdd.pointeur, requete); + result = mysql_store_result(bdd.pointeur); + while((row = mysql_fetch_row(result))) + { + strcpy(tables_fragmentees[nb_tables_fragmentees ++].nom, row[0]); + } + mysql_free_result(result); + + // Table historique : + strcpy(tables_fragmentees[nb_tables_fragmentees ++].nom, "TB_Historique"); + + // Vérification du nombre de tables max : + if (nb_tables_fragmentees > MAX_NOMBRE_TABLES_FRAGMENTEES) + { + sprintf(tmp, "Le nombre de tables fragmentées (qui sont sauvegardées par fragment) est supérieur au nombre maxi (%i tables trouvées pour maxi = %i). La sauvegarde de la BDD est arrêté.", nb_tables_fragmentees, MAX_NOMBRE_TABLES_FRAGMENTEES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + retour_commande = -1; + break; + } + +//printf("Le nombre de tables fragmentées trouvé est : %i \n", nb_tables_fragmentees); +//for (i = 0; i < nb_tables_fragmentees; i ++) printf("\tTable #%i: [%s]\n", i, tables_fragmentees[i].nom); + + //------------------------------------------------------------------------------------------- + // Sauvegarde de toutes les tables, sauf TB_Mesures* et TB_Historique : + //------------------------------------------------------------------------------------------- + + // Début de la commande de dump: + sprintf(commande, "mysqldump %s -u %s --password=%s %s ", options, BDD_USER, BDD_PASSWORD, BDD_DATABASE); + + // Suppression des tables fragmentées du dump : + for (i = 0; i < nb_tables_fragmentees; i ++) + { + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wformat-overflow" + sprintf(param, "--ignore-table=%s.%s ", BDD_DATABASE, tables_fragmentees[i].nom); // Warning connu mais non justifié : tables_fragmentees[].nom : 50 car et param : 300 car + #pragma GCC diagnostic pop + + strcat(commande, param); + } + + // Fin de la commande : + sprintf(param, ">> %s ", nom_fichier_sauvegarde); + strcat(commande, param); + +// printf("Commande shell = [%s]\n", commande); + + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande de sauvegarde de toutes les tables non fragmentées [%s] a échoué avec le code %i", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + + //--------------------------------------------------------------------- + // Sauvegarde des derniers enregistrements des tables fragmentées : + //--------------------------------------------------------------------- + for (i = 0; i < nb_tables_fragmentees; i ++) + { + // Ajout au fichier de sauvegarde des enregistrements de la table dont le No_paquet est égal à celui de TB_Systeme.No_paquet : +// NB : la requête pourrait être complétée d'un 'ORDER BY Date' mais cela risque de prendre du temps et ne semble pas nécessaire... + + // Suppression des 'Type>0' pour TB_Mesures_circuits : + if (strstr(tables_fragmentees[i].nom, "TB_Mesures_circuits") != NULL) strcpy(filtre_table, " AND Type=0"); + else strcpy(filtre_table, ""); + sprintf(commande, "mysqldump %s -u %s --password=%s %s %s --where=\"No_paquet=%i %s \" >> %s", options, BDD_USER, BDD_PASSWORD, BDD_DATABASE, tables_fragmentees[i].nom, systeme.no_paquet, filtre_table, nom_fichier_sauvegarde); +//printf("Commande shell = [%s]\n", commande); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande de sauvegarde de la table fragmentée %s [%s] a échoué avec le code %i", tables_fragmentees[i].nom, commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; // Sortie de la boucle FOR + } + } + if (retour_commande != 0) break; + + //--------------------------------------------------------------------- + // Compression du fichier de sauvegarde : + //--------------------------------------------------------------------- + // SDIR #170 : ajout du paramètre '-k' dans la commande de compresion afin de conserver le fichier SQL initial (sinon, il n'est pas conservé par GZIP) + sprintf(commande, "gzip %s -k -S .%s", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD); +// sprintf(tmp, "Commande de compression = [%s]\n", commande); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande de compression du fichier de sauvegarde [%s] a échoué avec le code %i", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + //--------------------------------------------------------------------- + // Encryption du fichier de sauvegarde : + //--------------------------------------------------------------------- + sprintf(commande, "openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -md sha256 -in %s.%s -out %s.%s.%s -pass pass:%s", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD, nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD, SUFFIXE_NOM_FICHIER_ENCRYPTE_SAUVEGARDE_BDD, BDD_PASSWORD); +// sprintf(tmp, "Commande d'encryptage = [%s]\n", commande); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande d'encryption du fichier de sauvegarde [%s] a échoué avec le code %i", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + // SDIR #170 : cf jusqu'à la fin de la fake loop... + //--------------------------------------------------------------------- + // Décryptage du fichier final : + //--------------------------------------------------------------------- + sprintf(commande, "openssl enc -aes-256-cbc -pbkdf2 -iter 10000 -md sha256 -d -in %s.%s.%s -out %s/%s.sql.%s -pass pass:Mp7Hgf12bV9zm", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD, SUFFIXE_NOM_FICHIER_ENCRYPTE_SAUVEGARDE_BDD, REPERTOIRE_TMP_BDD, NOM_FICHIER_CHECK_SAUVEGARDE, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD); +// sprintf(tmp, "Commande de décryptage temporaire = [%s]\n", commande); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande de décrytage temporaire du fichier de sauvegarde [%s] a échoué avec le code %i", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + //--------------------------------------------------------------------- + // Décompression du fichier temporaire : + //--------------------------------------------------------------------- + sprintf(commande, "gzip -d -f %s/%s.sql.%s", REPERTOIRE_TMP_BDD, NOM_FICHIER_CHECK_SAUVEGARDE, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD); +// sprintf(tmp, "Commande de décompression temporaire = [%s]\n", commande); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La commande de décompression temporaire [%s] a échoué avec le code %i", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + //--------------------------------------------------------------------- + // Comparaison du fichier temporaire avec le fichier inital : + //--------------------------------------------------------------------- + sprintf(commande, "diff -q %s %s/%s.sql", nom_fichier_sauvegarde, REPERTOIRE_TMP_BDD, NOM_FICHIER_CHECK_SAUVEGARDE); +//sprintf(commande, "diff -q %s %s/%s_test.sql", nom_fichier_sauvegarde, REPERTOIRE_TMP_BDD, NOM_FICHIER_CHECK_SAUVEGARDE); + +// sprintf(tmp, "Commande de comparaison du fichier temporaire avec le fchier inital = [%s]\n", commande); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + retour_commande = commande_shell(commande); + if (retour_commande != 0) + { + sprintf(tmp, "La comparaison du fichier temporaire avec le fichier inital [%s] indique deux fichiers différents [%i]", commande, retour_commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + + } while(0); + + + //--------------------------------------------------------------------- + // Vérification que la sauvegarde s'est bien passée et retour : + //--------------------------------------------------------------------- + if (retour_commande != 0) // Cas où au moins l'une des opérations de sauvegarde ne s'est pas bien passée + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_ERREUR_SAUVEGARDE_BDD, 0, 0, tmp); + + // Suppression du fichier qui est incomplet, mais aussi du fichier compressé et du fichier encrypté car on ne sait pas où on s'est arrêté) : + sprintf(commande, "%s", nom_fichier_sauvegarde); + remove(commande); + sprintf(commande, "%s.%s", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD); + remove(commande); + sprintf(commande, "%s.%s.%s", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD, SUFFIXE_NOM_FICHIER_ENCRYPTE_SAUVEGARDE_BDD); + remove(commande); + + sprintf(tmp, "La sauvegarde de la BDD est abandonnée. Les fichiers créés ont été supprimés et les indicateurs de sauvegarde dans TB_Systeme ont été remis à leur valeur d'origine."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + // NB : en pratique, c'est le fait de ne PAS incrémenter le n° de paquet dans TB_Systeme qui permettra de reprendre les enregistrements qui étaient prévus dans cet archive dans la prochaine. + + return(retour_commande); + } + else // Cas normal, sans erreur : + { + // Incrémentation du No_paquet : + systeme.no_paquet ++; + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='No_paquet' ", systeme.no_paquet); +// printf("requête = [%s]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Supression du fichier SQL (qui a été volontairement conservé via le paramètre '-k' de GZIP)... + // ... et du fichier compressé (openssl ne peut pas "écraser" la source si elle est dans le même répertoire ; on le fait donc manuellement)... + // ... si tout s'est bien passé : + sprintf(commande, "%s", nom_fichier_sauvegarde); + remove(commande); + sprintf(commande, "%s.%s", nom_fichier_sauvegarde, SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD); + remove(commande); + } + + affichage_debug(__FILE__, __LINE__, "Fin de sauvegarde de la BDD", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + return(0); +} + + + +//********************************************************************************************************************************************************************************** +// Fonction ARCHIVAGE_BDD +// +// Fonction : Lance l'utilitaire 'archivage.php' situé dans '/utils' +// Reçoit : la chaine de date de fin de période à utiliser et le Type d'archivage à réaliser (de 0 à 3 ou 10 - cf bdd.h : BDD_TYPE_ARCHIVAGE_xxxx) +// Retour : -1 en cas d'erreur, 0 si Ok +//********************************************************************************************************************************************************************************** +int8_t archivage_bdd(char * chaine_date_fin, uint8_t type) +{ +char commande[MAX_TAILLE_COMMANDE_SHELL]; +time_t now; +struct tm maintenant; +int32_t pid_nb; +char suffixe_fichier_log[MAX_LONGUEUR_NOM_FICHIER]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Calcul de la date courante pour nommer le fichier de logs : + time(&now); + maintenant = *localtime(&now); + + // Vérification des paramètres & préparation du suffixe de fichier de logs : + switch(type) + { + case BDD_TYPE_ARCHIVAGE_1_HEURE : + { + sprintf(suffixe_fichier_log, "_h%02i", maintenant.tm_hour); + break; + } + case BDD_TYPE_ARCHIVAGE_5_MINUTES : + { + sprintf(suffixe_fichier_log, "_min%02i", maintenant.tm_min); + break; + } + case BDD_TYPE_ARCHIVAGE_PURGE_SEULE : + { + sprintf(suffixe_fichier_log, "_purge_demarrage"); // Cet appel n'étant fait qu'au démarrage (et donc à des heures/min non définies, on prend un nom générique qui sera donc écrasé à chaque démarrage) + break; + } + case BDD_TYPE_ARCHIVAGE_1_HEURE_ET_PURGE : + case BDD_TYPE_ARCHIVAGE_5_MINUTES_ET_PURGE : + { + sprintf(suffixe_fichier_log, "_purge_%02i_%02i", maintenant.tm_hour, maintenant.tm_min); + break; + } + default : return (-1); + } + + // Récupération du nombre de process incluant 'archivage.php' : + pid_nb = get_pid_numbers_from_name(NOM_FICHIER_ARCHIVAGE, ""); + + // Archivage si aucun processus équivalent n'est déjà en cours : + if (pid_nb <= 0) // Il ny a aucun processus d'archivage en cours... + { + sprintf(commande, "sudo nice -n 18 php %s/%s \"%s\" %i >%s/%s%s.txt &", REPERTOIRE_UTILS, NOM_FICHIER_ARCHIVAGE, chaine_date_fin, type, REPERTOIRE_LOGS, PREFIXE_NOM_FICHIER_ARCHIVAGE_LOG, suffixe_fichier_log); // AVEC '&' de terminaison car c'était bien l'objectif, de faire exécuter cette tâche en tâche de fond...! + commande_shell(commande); + + if (pid_nb == 0) + { + sprintf(tmp, "La commande d'archivage [%s] vient d'être lancée en tâche de fond car aucun processus d'archivage n'a été détecté en cours d'exécution", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + else + { + sprintf(tmp, "Le retour de recherche de processus d'archivage en cours a détecté une erreur (%i). L'archivage demandé est exécuté quand même...", pid_nb); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + else // Il y a déjà un processus en cours : + { + sprintf(tmp, "Au moins un processus d'archivage a été détecté en cours d'exécution (nb = %i). L'archivage demandé n'est donc pas exécuté...", pid_nb); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + // NB : dans ce cas, on ne met pas à jour 'systeme.jour_dernier_archivage' afin qu'une nouvelle tentative soit effectuée dans 5 minutes... + } + + + //--------------------------------------- RETOUR ------------------------------------ + return(0); +} diff --git a/sources/bdd_fonctions.c b/sources/bdd_fonctions.c new file mode 100644 index 0000000..dd76533 --- /dev/null +++ b/sources/bdd_fonctions.c @@ -0,0 +1,1567 @@ +//********************************************************************************************************************************************************************************** +// Fichier BDD_FONCTIONS.C +// +// Contient les fonctions principales de gestion de la BDD +// +//********************************************************************************************************************************************************************************** + +// #define __DEBUG_INIT //Décommenter cette ligne pour avoir les messages de debug concernant l'init +// #define __DEBUG_BDD_MESURES //Décommenter cette ligne pour avoir les messages de debug concernant l'init + + + +// Include : +#include "main.h" + +// Prototypes : +int8_t init_annuaire_nmea(void); +int8_t enregistrement_date_pgn_nmea(uint16_t rang); +int8_t creation_pgn_field_nmea(uint16_t rang, uint8_t field, char * code_unite, uint8_t precision_accueil, uint8_t precision_mesures, char * nom); +int8_t enregistrement_pgn_field_nmea(uint16_t rang, uint8_t field, float val); +int8_t suppression_pgn_nmea(uint16_t rang); +void maj_etat_cm3p(uint8_t etat); +int8_t enregistrement_annuaire_k_bus(void); +int8_t enregistrement_annuaire_ow(uint8_t nb_devices); +void enregistrement_eeprom(uint8_t adresse_module, uint16_t longueur); +void enregistrement_historique(uint8_t type_source, uint8_t type, uint16_t id_source, float valeur, char * complement); +void enregistrement_mesures(uint8_t mode); +void enregistrement_mesure(uint8_t type, uint16_t no, uint16_t no_variable, char str_date[MAX_LONGUEUR_DATE_COURANTE], uint8_t mode); +int32_t enregistrement_message_users(int32_t code, char * parametres, int8_t etat_demande); +int8_t maj_message_users(uint32_t id, char * parametres, int8_t etat_demande); +int8_t clear_scan_wifi_sta(void); +int8_t enregistrement_scan_wifi_sta(char * bssid, char * nom, uint8_t canal, uint8_t qualite); +int8_t nom_capteur_cle_primaire(uint16_t id, char * nom); +int8_t nom_circuit_cle_primaire(uint16_t id, char * nom); + +// Variables globales : + + + + + +//********************************************************************************************************************************************************************************** +// Fonction : 1. Supprime de la BDD les PGN n'ayant aucun Field Actif (dès qu'un PGN a au moins un Field Actif, tous les enregistrement de ce PGN sont conservés car, dans tous les cas, +// le Compagnon gère le PGN dans son ensemble...) +// 2. RAZ de l'annuaire stocké en RAM +// 3. Init de l'annuaire avec les PGN/Field restant en BDD +// Reçoit : Rien +// Renvoit : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t init_annuaire_nmea(void) +{ +uint16_t i, j; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; + + // Suppression de tous les PGN qui n'ont aucun Field actif : + sprintf(requete, "DELETE FROM TB_Nmea2000 WHERE Pgn NOT IN (SELECT Pgn FROM TB_Nmea2000 WHERE Actif=1) "); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // RAZ dde l'annuaire : + for (i = 0; i < NMEA_MAX_NOMBRE_PGN; i ++) nmea.annuaire[i].flag_validite = false; + + // Enregistrement dans l'annuaire des PGNs restant en BDD : + i = 0; + sprintf(requete, "SELECT Pgn FROM TB_Nmea2000 GROUP BY Pgn "); +// printf("Requete = [%s]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + nmea.annuaire[i].pgn = atoi(row[0]); + nmea.annuaire[i].flag_validite = true; // Marque cette position d'annuaire comme Utilisée + nmea.annuaire[i].flag_presence = false; // Permet de savoir qu'on n'a pas encore eu de retour du Compagnon et que la première fois que ça arrivera, il faudra configurer le PGN sur le Compagnon + nmea.annuaire[i].actif = 1; // Puisque seuls les PGN ayant au moins un Field Actif ont été conservés... + nmea.annuaire[i].priority = 0; // Par défaut + nmea.annuaire[i].source = 0; // Par défaut + nmea.annuaire[i].lg_data = 0; // Par défaut + + // Recherche des infos du PGN dans le catalogue : + for (j = 0; ((j < NMEA_MAX_NOMBRE_PGN) && (catalogue_pgns[j].pgn != 0)); j ++) + { + if (catalogue_pgns[j].pgn == nmea.annuaire[i].pgn) // Le PGN a été trouvé dans le catalogue (ce qui doit toujours être le cas car pour que le PGN soit stocké en BDD, c'est qu'il a été détecté dans le catalogue) + { + nmea.annuaire[i].type = catalogue_pgns[j].type; + nmea.annuaire[i].periode_mini = catalogue_pgns[j].periode_mini; + + break; // Sortie de la boucle du catalogue puisqu'on a trouvé l'entrée. + } + } + + // c_bus_down_commande_config_pgn_nmea(nmea.nb_entrees_annuaire); + // NB : on n'émet pas de commande de config car rien ne dit que le Compagnon a déjà détecté ce PGN depuis son reset. Et si ce n'est pas le cas, il ne saura pas traiter cette commande + // (puisqu'il n'aura pas le PGN dans son annuaire). On attend donc de recevoir la première connexion de ce PGN de la part du Compagnon pour luyi envoyer la commande de config correspondante, + // dans 'nmea.c', fonction 'traitement_pgn_compagnon()' + + i ++; + } + mysql_free_result(result); //Libération du jeu de résultat + + return (0); +} + +//********************************************************************************************************************************************************************************** +// Fonction : Mise à jour de la dernière date de réception du PGN (concerne tous les Fields du PGN) +// Reçoit : Le rang du PGN +// Renvoit : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t enregistrement_date_pgn_nmea(uint16_t rang) +{ +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + if (rang >= NMEA_MAX_NOMBRE_PGN) return (-1); + if (!nmea.annuaire[rang].flag_validite) return (-1); + + sprintf(requete, "UPDATE TB_Nmea2000 SET Date_maj=CURRENT_TIMESTAMP() WHERE Pgn=%i", nmea.annuaire[rang].pgn); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + return (0); +} + +//********************************************************************************************************************************************************************************** +// Fonction : Création du field du PGN dans la BDD +// Reçoit : Le rang du PGN, le n° de field et son nom +// Renvoit : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t creation_pgn_field_nmea(uint16_t rang, uint8_t field, char * code_unite, uint8_t precision_accueil, uint8_t precision_mesures, char * nom) +{ +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +uint16_t id_variable; + + + if (rang >= NMEA_MAX_NOMBRE_PGN) return (-1); + if (!nmea.annuaire[rang].flag_validite) return (-1); + + // Recherche de la clé primaire de la variable à utiliser pour ce Field : + sprintf(requete, "SELECT Cle_primaire FROM TB_Variables WHERE Type=%i AND Code='%s' ", SOURCE_MESURE_CAPTEUR, code_unite); + //printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + row = mysql_fetch_row(result); + id_variable = atoi(row[0]); + mysql_free_result(result); //Libération du jeu de résultat + + // Création du Field dans la BDD : + sprintf(requete, "REPLACE INTO TB_Nmea2000 (Pgn, Field, Nom, Actif, Valeur, ID_Variable, Precision_affichage_accueil, Precision_affichage_mesures, Date_maj) VALUES (%i, %i, '%s', 0, 0, %i, %i, %i, CURRENT_TIMESTAMP()) ", + nmea.annuaire[rang].pgn, field, nom, id_variable, precision_accueil, precision_mesures); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + return (0); +} + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistrement de la valeur du field du PGN +// Reçoit : Le rang du PGN, le n° de Field et la Valeur +// Renvoit : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t enregistrement_pgn_field_nmea(uint16_t rang, uint8_t field, float val) +{ +uint16_t i; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + if (rang >= NMEA_MAX_NOMBRE_PGN) return (-1); + if (!nmea.annuaire[rang].flag_validite) return (-1); + + sprintf(requete, "UPDATE TB_Nmea2000 SET Valeur=%f, Date_maj=CURRENT_TIMESTAMP() WHERE Pgn=%i AND Field=%i ", val, nmea.annuaire[rang].pgn, field); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de la valeur dans la ou les entrées NMEA correspondante (s'il y en a) : + for (i=0; i < entrees.nmea.nb; i++) + { + if (!entrees.nmea.entrees[i].flag_validite) continue; + if (entrees.nmea.entrees[i].pgn != nmea.annuaire[rang].pgn) continue; + if (entrees.nmea.entrees[i].field != field) continue; + + entrees.nmea.entrees[i].valeur = val; + } + + return (0); +} + +//********************************************************************************************************************************************************************************** +// Fonction : Suppression de toutes les entrées du PGN (éventuellement, plusieurs fields) de TB_NMEA2000 +// Reçoit : Le rang du PGN +// Renvoit : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t suppression_pgn_nmea(uint16_t rang) +{ + + + return (0); +} + + + + + +//********************************************************************************************************************************************************************************** +// Fonction : Met à jour le flag d'état du CM3P et enregistre dans la BDD cet état +// Reçoit : l'état du CM3P : ON ou OFF +// Renvoit : Rien +//********************************************************************************************************************************************************************************** +void maj_etat_cm3p(uint8_t etat) +{ +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + if (etat == OFF) + { + // Mise à jour du flag interne : + systeme.flag_cm3p_ready = FALSE; + + // Mise à jour dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='0' WHERE Nom='Etat_cm3p' "); // Indication d'init en cours pour la partie web/écran + mysql_query(bdd.pointeur, requete); + + // Coupure de la sortie correspondante vers CM3+/CM4 : + ecriture_sortie(TYPE_SORTIE_GPIO, 0, systeme.sortie_gpio_cm_do_to_uc, OFF); // Coupure de la sortie CM3P_READY vers le Compagnon. 0 est l'adresse de ShipBase + + // Info de debug : + affichage_debug(__FILE__, __LINE__, "L'état de CM3P est passé à\t\t: OFF", TYPE_MESSAGE_DEBUG_WARNING); + } + else if (etat == ON) + { + // Mise à jour du flag interne : + systeme.flag_cm3p_ready = TRUE; + + // Mise à jour dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='1' WHERE Nom='Etat_cm3p' "); + mysql_query(bdd.pointeur, requete); + + // Activation de la sortie correspondante vers CM3+/CM4 : + ecriture_sortie(TYPE_SORTIE_GPIO, 0, systeme.sortie_gpio_cm_do_to_uc, ON); // Activation de la sortie CM3P_READY vers le Compagnon. 0 est l'adresse de ShipBase + + // Info de debug : + affichage_debug(__FILE__, __LINE__, "L'état de CM3P est repassé à\t\t: ON", TYPE_MESSAGE_DEBUG_OK); + } + else + { + sprintf(tmp, "La valeur d'état demandée dans 'maj_etat_cm3p' n'est pas valide (%i). La demande est ignorée...", etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistre dans la BDD les cartes présentes dans le système +// Reçoit : Rien +// Renvoit : 0 si le traitement n'a détecté aucun problème pour démarrer, -1 si au moins une erreur a été détectée. +//********************************************************************************************************************************************************************************** +int8_t enregistrement_annuaire_k_bus(void) +{ +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +uint8_t defaut_config_k_bus; +uint8_t adresse_defaut_config_k_bus; +int8_t type_module_defaut_config_k_bus; +char chaine_adresse_mac[TAILLE_ADRESSE_MAC * 3]; // On a besoin de 3 caractères par octet (xx:) sauf pour le dernier (xx) + le NULL de fin +char chaine_unique_id[MAX_TAILLE_UNIQUE_ID * 3]; // On a besoin de 3 caractères par octet (xx ) sauf pour le dernier (xx) + le NULL de fin +uint16_t i; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + +affichage_debug(__FILE__, __LINE__, "------------------ANNUAIRE K BUS INIT", TYPE_MESSAGE_DEBUG_WARNING); + + + // Remarques sur Création/Suppression, validité et présence : + + // Un circuit (ou une batterie ou un capteur ou un réservoir) est créé ou supprimé exclusivement par l'utilisateur. En particulier, il n'est JAMAIS supprimé automatiquement. + // Dans le cas d'une suppression (par l'utilisateur donc...), l'entrée du circuit dans TB_Circuits (ou de la batterie dans TB_Batteries ou du capteur dans TB_Capteurs, ...) est + // supprimée purement et simplement (DELETE). + + // Plutôt que supprimer un circuit (ou une batterie ou un capteur ou un réservoir), l'utilisateur peut le positionner à 'non affecté'. En général, de façon temporaire mais pas forcément. + // Dans ce cas, le champ 'Adresse_module' du circuit (ou de la batterie ou du capteur ou un réservoir) est positionné à '-1'. + // Note : a priori, seuls les circuits utilisent actuellement cette option (par exemple, pour intervertir 2 circuits sur un module SP8DC plein) + // mais le principe est identique pour les capteurs, les batteries et les réservoirs et pourrait être utilisé si besoin. + + // Le champ 'validite' a été supprimé des tables de circuits (batterie, capteur et réservoir) car il prétait à confusion avec le 'flag_validite' utilisé dans les structures 'circuits ('batteries', + // 'capteurs' et 'reservoirs). Ce dernier est juste une indication pour mettre de côté les entrées du tableau de structure qui ne sont pas utilisées (comme le tableau a une taille maxi fixe, il y a forcément + // de nombreuses entrées inutilisées). Les tables comme TB_Circuits (TB_Batteries, TB_Capteurs ou TB_Reservoirs) utilisaient le champ Validité pour une fonction de dévalidation manuelle (souvent temporaire). + // Les deux fonctions étaient donc très différentes et de nombreuses confusions ont eu lieu. Pour régler ce problème, les tables n'ont plus de champ Validité. Chaque entrée de la table est valide. On peut + // juste positionner le circuit (ou la batterie ou le capteur ou le réservoir) à 'non affecté' comme indiqué ci-dessus via 'Adresse_module'=-1 ou le supprimer purement et simplement. + // Enfin, l'Etat_systeme (cf commentaires ci-dessous) permet de savoir en temps réel si le ciruit est opérationnel (config Ok et module présent et du bon type) ou non. + + // Enfin, dans le cas d'un circuit (ou une batterie ou un capteur ou un réservoir) existant (s'il est supprimé, on n'en parle pas...) et valide (donc affecté à un module d'adresse >=0), + // il est important de savoir si le circui est bien opérationnel. Cette condition est définie par 2 niveaux de tests : + // 1. sa configuration est valide : Cette vérification consiste à tester la validité de chacun de ses paramètres propres (par exemple, un + // réservoir ne peut pas avoir plus d'une entrée) mais aussi la compatibilité de ses paramètres avec le type de module auquel il est affecté (par exemple, un circuit ne peut pas avoir un + // no_circuit=5 sur un SP4AC qui ne comprend que 4 circuits). Ces tests de conformité vs le module prennent pour référence TB_Modules_config et sont donc indépendants de l'état physique + // du réseau K-Bus. + // 2. le module auquel il est affecté est bien présent : l'objet est alors de vérifier qu'un module physique est bien présent à l'adresse prévue en config ET du même type que celui indiqué + // dans TB_Modules_config. + // Une réponse positive à ces 2 tests permet d'affirmer que le circuit (ou la batterie ou le capteur ou le réservoir) est bien opérationnel. Dans ce cas, son champ 'Etat_systeme' est positionné à 0. + // Les test de configuration (1) est effectué dans 'bdd_init.c'. S'il échoue, Etat_systeme est positionné à -1. + // La vérification de la présence du module (2) est fait ci-dessous, après avoir reçu l'annuaire K-Bus du compagnon. S'il échoue, Etat_systeme est positionné à -2. + // Par principe, le niveau 2 est "prioritaire" sur le niveau 1 (si les deux tests sont négatifs, Etat_systeme = -2). + // Lorsque Etat_systeme != 0, le circuit (ou la batterie ou le capteur ou le réservoir) est dévalidé dans l'Appli (flag_validite=FALSE). Dans la BDD, seul le champ Etat_systeme < 0 le distingue d'un + + + + //----------------------------------------------------------------------------------------- + // TRAITEMENT DES MODULES PHYSIQUES + // Tous les flags Presence de TB_Modules_physiques ont été mis à 0 dans 'bdd_init()'. + // Tous les flag_presence de 'systeme.modules_physique' ont été mis à FALSE dans 'bdd_init()' puis à FALSE / TRUE dans 'c_bus_down_reponse_annuaire_k_bus()'. + // On parcours ici tous les modules trouvés par le Compagnon pour : + // - vérifier si le module est nouveau (adresse MAC pas encore dans TB_Modules_physiques, peu importe l'adresse K-bus) : si nouveau, enregistrement dans l'historique + // - mettre à jour les données fraichement récupérées par le Compagnon dans TB_Modules_physiques si le module était connu. Sinon, idem mais en création. + // NB : cette maj se fait à l'adresse K-Bus actuelle (le module a donc pu bouger). + // - vérifier les modules qui ont "disparus" et enregistrement dans l'historique. + // - suppression de TB_Modules_physiques des modules diparus + // - afficher les infos de debug correspondantes (notamment la liste des modules présents et leurs caractéristiques) + // A la fin de cette phase, TB_Modules_physiques est à jour avec seulement les lignes de modules physiquement présents. + // Dit autrement, le champ Présence de TB_Modules_physiques est toujours = 1 un fois l'init terminée. + // NB : même si cela ne serait pas normal (et probablement 'impossible' au niveau du Compagnon), il peut techniquement y avoir des "trous" dans les adresses K-Bus. + //----------------------------------------------------------------------------------------- + +#ifdef __DEBUG_INIT + affichage_debug(__FILE__, __LINE__, "\t\tSynthèse des cartes après traitement de l'annuaire :", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (!systeme.modules_physiques[i].flag_presence) continue; // On ne traite que les cartes qui sont présentes physiquement. + + conversion_adresse_mac_chaine(systeme.modules_physiques[i].adresse_mac, chaine_adresse_mac, ':'); + conversion_unique_id_chaine(systeme.modules_physiques[i].hardware_unique_id, chaine_unique_id); + + // Vérification si le module est nouveau à bord : + sprintf(requete, "SELECT COUNT(*) AS Nb FROM TB_Modules_physiques WHERE Adresse_mac='%s' ", chaine_adresse_mac); + // printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (atoi(row[0]) == 0) + { + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODULES_NOUVEAU_MODULE, i, systeme.modules_physiques[i].type, chaine_adresse_mac); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + // Mise à jour de la BDD avec les valeurs lues dans le module physique : + sprintf(requete, "REPLACE INTO TB_Modules_physiques \ + (Adresse_k_bus, Adresse_mac, Presence, Type, Flag_r_term, Hardware_version, Cpu_unique_id, Cpu_flash_size, Cpu_package, Software_type, Software_version, Software_crc) VALUES \ + (%i, '%s', %i, %i, %i, %i, '%s', %i, %i, '%s', '%s', '%08X')", + i, chaine_adresse_mac, 1, systeme.modules_physiques[i].type, systeme.modules_physiques[i].resistance_terminaison, systeme.modules_physiques[i].hardware_version, + chaine_unique_id, systeme.modules_physiques[i].hardware_flash_size, systeme.modules_physiques[i].hardware_package, systeme.modules_physiques[i].soft_type, + systeme.modules_physiques[i].soft_version, systeme.modules_physiques[i].soft_crc); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + +#ifdef __DEBUG_INIT + // Affichage des infos de la carte : + sprintf(tmp, "Carte n° %2i : ", i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + + printf("\t%-40s: %s\n", "Adresse MAC", conversion_adresse_mac_chaine(systeme.modules_physiques[i].adresse_mac, chaine_adresse_mac, ':')); + printf("\t%-40s: %s\n", "Type de carte", nom_type_carte(systeme.modules_physiques[i].type)); + printf("\t%-40s: %s\n", "ID unique CPU", conversion_unique_id_chaine(systeme.modules_physiques[i].hardware_unique_id, chaine_unique_id)); + printf("\t%-40s: %i\n", "Taille de la flash", systeme.modules_physiques[i].hardware_flash_size); + printf("\t%-40s: %i\n", "Type de package de la CPU", systeme.modules_physiques[i].hardware_package); + printf("\t%-40s: %i\n", "Version materielle", systeme.modules_physiques[i].hardware_version); + printf("\t%-40s: %s\n", "Type de logiciel", systeme.modules_physiques[i].soft_type); + printf("\t%-40s: %s\n", "Version de logiciel", systeme.modules_physiques[i].soft_version); + printf("\t%-40s: %08Xh\n","CRC du logiciel", systeme.modules_physiques[i].soft_crc); + + printf("\n"); +#endif + } + + // Enregistrement dans l'historique des modules ayant "disparus" : + sprintf(requete, "SELECT Adresse_k_bus, Adresse_mac, Type FROM TB_Modules_physiques WHERE Presence=0 "); +// printf("Requete = [%s]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODULES_SUPPRESSION_MODULE, atoi(row[0]), atoi(row[2]), row[1]); + } + mysql_free_result(result); //Libération du jeu de résultat + + // Suppression des entrées de TB_Modules_physiques qui ne sont pas présents sur le bus : + sprintf(requete, "DELETE FROM TB_Modules_physiques WHERE Presence=0; "); + mysql_query(bdd.pointeur, requete); + + + //----------------------------------------------------------------------------------------- + // TRAITEMENT DES MODULES EN CONFIGURATION + // Tous les flags Presence de TB_Modules_config ont été mis à 0 dans 'bdd_init()'. + // Tous les flag_presence de 'systeme.modules' sont mis à FALSE dans 'bdd_init()'. + // La comparaison, pour une adresse k_bus donnée, entre l'entrée dans 'modules_config' et 'modules_physiques' permet de + // positionner le flag_presence de 'modules_config'. Après ce traitement, chaque entrée de 'modules_config' a donc : + // - flag_validite = FALSE si absente de TB_Modules_config, + // TRUE si présente dans TB_Modules_config + // - flag_presence = TRUE si un module du même type qu'attendu est physiquement présent à cette adresse. + // FALSE sinon (dans ce cas, un defaut_k_bus est positionné SI le module est Affecté - Si le module n'est Affecté à aucun circuit (ou batterie ou capteur), on ne considère pas cette différence comme une erreur) + //----------------------------------------------------------------------------------------- + + // Valeurs par défaut : + defaut_config_k_bus = DEFAUT_K_BUS_AUCUN; // Par défaut + adresse_defaut_config_k_bus = MAX_NOMBRE_MODULES; // On cherche une erreur AVANT ce maxi + type_module_defaut_config_k_bus = TYPE_CARTE_INCONNU; // Par défaut + + // Mise à jour du flag Presence dans TB_Modules_config et dans la structure 'modules_config' : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (!systeme.modules_config[i].flag_validite) continue; + + //============================================================================================================================ + // Cas normal : un module physique est présent à l'adresse du module en config ET il est du même type + if ((systeme.modules_physiques[i].flag_presence) && // Si un module est physiquepment présent à cette adresse... + (systeme.modules_physiques[i].type == systeme.modules_config[i].type) ) // ... et du même type que celui attendu en config : + { + systeme.modules_config[i].flag_presence = TRUE; // on positionne le module de config à PRESENT + + // Mise à jour du flag PRESENCE dans la BDD : + sprintf(requete, "UPDATE TB_Modules_config SET Presence=1 WHERE Adresse_k_bus=%i ", i); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + } + + //============================================================================================================================ + // Cas d'erreur : aucun module physique n'est présent à cette adresse OU un module physique est présent mais pas du bon type : + else + { + //------------------------------------------------------------------------------------------------------------------------- + // Cas d'une erreur majeure : le module en config est affecté à un ou plusieurs éléments ; ils ne seront donc pas accessibles... + if (systeme.modules_config[i].flag_affecte) + { + defaut_config_k_bus = DEFAUT_K_BUS_MAJEUR; + + // Enregistrement de la position de l'erreur si elle est AVANT la précédente : + if (i < adresse_defaut_config_k_bus) + { + adresse_defaut_config_k_bus = i; + type_module_defaut_config_k_bus = systeme.modules_config[i].type; + } + } + + //------------------------------------------------------------------------------------------------------------------------- + // Cas d'une erreur mineure : aucun élément n'est affecté au module en erreur donc cette incohérence n'aura pas d'effet... + else + { + if (defaut_config_k_bus == DEFAUT_K_BUS_AUCUN) defaut_config_k_bus = DEFAUT_K_BUS_MINEUR; + + // Enregistrement de la position de l'erreur si elle est AVANT la précédente : + if (i < adresse_defaut_config_k_bus) + { + adresse_defaut_config_k_bus = i; + type_module_defaut_config_k_bus = systeme.modules_config[i].type; + } + } + } + } + + //------------------------ + // Message de défaut K-bus : + //------------------------ + + // Suppression des messages précédents de ce type : + sprintf(requete, "DELETE FROM TB_Messages_users WHERE Code=%i ", MESSAGE_USERS_CODE_DEFAUT_CONFIG_K_BUS); + mysql_query(bdd.pointeur, requete); + + // Création du nouveau si un défaut est présent : + if (adresse_defaut_config_k_bus < MAX_NOMBRE_MODULES) + { + sprintf(tmp, "%i;%i;%i", adresse_defaut_config_k_bus, systeme.modules_physiques[adresse_defaut_config_k_bus].type, type_module_defaut_config_k_bus); // Paramètres = 'adresse_k_bus du premier module Nok';'Type_module_physique à cette adresse (yc -1 pour type inconnu)';'Type module configuré'. + enregistrement_message_users(MESSAGE_USERS_CODE_DEFAUT_CONFIG_K_BUS, tmp, MESSAGE_USERS_ETAT_URGENT); + } + + // Mise à jour du flag de défaut dans TB_Systeme : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Defaut_config_k_bus' ", ((adresse_defaut_config_k_bus < MAX_NOMBRE_MODULES) ? 1 : 0)); + mysql_query(bdd.pointeur, requete); + + + + //----------------------------------------------------------------------------------------- + // VERIFICATION DE LA CONFIGURATION EN COURS DES CIRCUITS (ou BATTERIES/CAPTEURS/RESERVOIRS) + //----------------------------------------------------------------------------------------- + // Tous les flag Presence de TB_Circuits ont été mis à 0 dans 'bdd_init()'. + // Tous les flag_presence de 'circuits.circuits[]' sont mis à FALSE dans 'bdd_init()' + // Les traitements suivants sont faits selon le même modèle : + // - parcours de tous les circuits valides + // - recherche du module sur lequel le circuit est affecté + // - Vérification que : + // - il y a bien un module présent à cette adresse, + // - le module physiquement présent correspond au type de module attendu par le circuit + // - le module comprend biens les entrées/sorties sur lesquelles le circuit est configuré + // Si le résultat est positif, le champ 'TB_Circuits.Presence' et le flag 'circuits.circuits[].flag_presence' sont passés à TRUE. + // Sinon, le champ 'TB_Circuits.Presence' et le flag 'circuits.circuits[].flag_presence' sont laissés à FALSE. + // Dans ce cas (pas présent), le flag 'circuits.circuits[].flag_validite' est aussi passé à FALSE ain de simplifier le traitement mais + // 'TB_Circuits.Presence' est laissé à TRUE ! Il s'agit juste de simplifier le traitement ultérieur par l'appli. + + //------------------------ + // Circuits : + //------------------------ + + // Recherche des circuits affectés à un module absent (ou un module du mauvais type) : + sprintf(requete, "SELECT Cle_primaire FROM TB_Circuits WHERE Adresse_module NOT IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE Presence=1)"); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + circuits.circuits[circuit_cle_primaire(atoi(row[0]))].etat_systeme = ERREUR_SYSTEME_RESEAU; // Si une erreur systeme de type config est déjà détectée sur ce circuit, elle est "écrasée" par l'Erreur_système_réseau qui est considérée comme plus grave... + } + mysql_free_result(result); //Libération du jeu de résultat + + // Traitement des circuits en en erreur systeme (config ou reseau) : + for (i = 0; i < circuits.nb; i ++) // Parcours de tous les circuits + { + if (!circuits.circuits[i].flag_validite) continue; // On ne traite que ceux qui ont une configuration dans la BDD (éventuellement non valide si Etat_systeme = Erreur_systeme_config) + + if (circuits.circuits[i].etat_systeme != ERREUR_SYSTEME_AUCUNE) + { + // Désactivation du circuit pour l'exécution en cours : + circuits.circuits[i].flag_validite = FALSE; + + // Mise à jour de Etat_systeme dans la BDD : + sprintf(requete, "UPDATE TB_Circuits SET Etat_systeme=%i WHERE Cle_primaire=%i", circuits.circuits[i].etat_systeme, circuits.circuits[i].cle_primaire); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Message de debug : + if (circuits.circuits[i].etat_systeme == ERREUR_SYSTEME_RESEAU) + { + sprintf(tmp, "Le module auquel est affecté le circuit # %i (cle_primaire = %i) n'est pas disponible à l'adresse %i. Le circuit est dévalidé...", i, circuits.circuits[i].cle_primaire, circuits.circuits[i].adresse_module); + } + else + { + sprintf(tmp, "La configuration du circuit # %i (cle_primaire = %i) présente des incohérences rendant son exploitation impossible. Le circuit est dévalidé...", i, circuits.circuits[i].cle_primaire); + } + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + + //------------------------ + // Batteries : + //------------------------ + + // Recherche des batteries affectées à un module absent (ou un module du mauvais type) : + sprintf(requete, "SELECT Cle_primaire FROM TB_Batteries WHERE Adresse_module NOT IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE Presence=1)"); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + batteries.batteries[batterie_cle_primaire(atoi(row[0]))].etat_systeme = ERREUR_SYSTEME_RESEAU; // Si une erreur systeme de type config est déjà détectée sur cette batterie, elle est "écrasée" par l'Erreur_système_réseau qui est considérée comme plus grave... + } + mysql_free_result(result); //Libération du jeu de résultat + + // Traitement des batteries en erreur systeme (config ou reseau) : + for (i = 0; i < batteries.nb; i ++) // Parcours de toutes les batteries + { + if (!batteries.batteries[i].flag_validite) continue; // On ne traite que celles qui ont une configuration dans la BDD (éventuellement non valide si Etat_systeme = Erreur_systeme_config) + + if (batteries.batteries[i].etat_systeme != ERREUR_SYSTEME_AUCUNE) + { + // Désactivation de la batterie pour l'exécution en cours : + batteries.batteries[i].flag_validite = FALSE; + + // Mise à jour de Etat_systeme dans la BDD : + sprintf(requete, "UPDATE TB_Batteries SET Etat_systeme=%i WHERE Cle_primaire=%i", batteries.batteries[i].etat_systeme, batteries.batteries[i].cle_primaire); +printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Message de debug : + if (batteries.batteries[i].etat_systeme == ERREUR_SYSTEME_RESEAU) + { + sprintf(tmp, "Le module auquel est affectée la batterie # %i (cle_primaire = %i) n'est pas disponible à l'adresse %i. La batterie est dévalidée...", i, batteries.batteries[i].cle_primaire, batteries.batteries[i].adresse_module); + } + else + { + sprintf(tmp, "La configuration de la batterie # %i (cle_primaire = %i) présente des incohérences rendant son exploitation impossible. La batterie est dévalidée...", i, batteries.batteries[i].cle_primaire); + } + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + + //------------------------ + // Capteurs : + //------------------------ + + // Recherche des capteurs affectés à un module absent (ou un module du mauvais type) : + sprintf(requete, "SELECT Cle_primaire FROM TB_Capteurs WHERE Adresse_module NOT IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE Presence=1)"); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + capteurs.capteurs[capteur_cle_primaire(atoi(row[0]))].etat_systeme = ERREUR_SYSTEME_RESEAU; // Si une erreur systeme de type config est déjà détectée sur ce capteur, elle est "écrasée" par l'Erreur_système_réseau qui est considérée comme plus grave... + } + mysql_free_result(result); //Libération du jeu de résultat + + // Traitement des capteurs en erreur systeme (config ou reseau) : + for (i = 0; i < capteurs.nb; i ++) // Parcours de tous les capteurs + { + if (!capteurs.capteurs[i].flag_validite) continue; // On ne traite que ceux qui ont une configuration dans la BDD (éventuellement non valide si Etat_systeme = Erreur_systeme_config) + + if (capteurs.capteurs[i].etat_systeme != ERREUR_SYSTEME_AUCUNE) + { + // Désactivation du capteurs pour l'exécution en cours : + capteurs.capteurs[i].flag_validite = FALSE; + + // Mise à jour de Etat_systeme dans la BDD : + sprintf(requete, "UPDATE TB_Capteurs SET Etat_systeme=%i WHERE Cle_primaire=%i", capteurs.capteurs[i].etat_systeme, capteurs.capteurs[i].cle_primaire); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Message de debug : + if (capteurs.capteurs[i].etat_systeme == ERREUR_SYSTEME_RESEAU) + { + sprintf(tmp, "Le module auquel est affecté le capteurs # %i (cle_primaire = %i) n'est pas disponible à l'adresse %i. Le capteurs est dévalidé...", i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].adresse_module); + } + else + { + sprintf(tmp, "La configuration du capteurs # %i (cle_primaire = %i) présente des incohérences rendant son exploitation impossible. Le capteurs est dévalidé...", i, capteurs.capteurs[i].cle_primaire); + } + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + + //------------------------ + // Réservoirs : + //------------------------ + + // Recherche des réservoirs affectés à un module absent (ou un module du mauvais type) : + sprintf(requete, "SELECT Cle_primaire FROM TB_Reservoirs WHERE Adresse_module NOT IN (SELECT Adresse_k_bus FROM TB_Modules_config WHERE Presence=1)"); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + reservoirs.reservoirs[reservoir_cle_primaire(atoi(row[0]))].etat_systeme = ERREUR_SYSTEME_RESEAU; // Si une erreur systeme de type config est déjà détectée sur ce réservoir, elle est "écrasée" par l'Erreur_système_réseau qui est considérée comme plus grave... + } + mysql_free_result(result); //Libération du jeu de résultat + + // Traitement des réservoirs en erreur systeme (config ou reseau) : + for (i = 0; i < reservoirs.nb; i ++) // Parcours de tous les réservoirs + { + if (!reservoirs.reservoirs[i].flag_validite) continue; // On ne traite que ceux qui ont une configuration dans la BDD (éventuellement non valide si Etat_systeme = Erreur_systeme_config) + + if (reservoirs.reservoirs[i].etat_systeme != ERREUR_SYSTEME_AUCUNE) + { + // Désactivation du réservoir pour l'exécution en cours : + reservoirs.reservoirs[i].flag_validite = FALSE; + + // Mise à jour de Etat_systeme dans la BDD : + sprintf(requete, "UPDATE TB_Reservoirs SET Etat_systeme=%i WHERE Cle_primaire=%i", reservoirs.reservoirs[i].etat_systeme, reservoirs.reservoirs[i].cle_primaire); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Message de debug : + if (reservoirs.reservoirs[i].etat_systeme == ERREUR_SYSTEME_RESEAU) + { + sprintf(tmp, "Le module auquel est affecté le réservoirs # %i (cle_primaire = %i) n'est pas disponible à l'adresse %i. Le réservoirs est dévalidé...", i, reservoirs.reservoirs[i].cle_primaire, reservoirs.reservoirs[i].adresse_module); + } + else + { + sprintf(tmp, "La configuration du réservoirs # %i (cle_primaire = %i) présente des incohérences rendant son exploitation impossible. Le réservoirs est dévalidé...", i, reservoirs.reservoirs[i].cle_primaire); + } + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + + + return(0); +} + + + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistre dans la BDD les devices OW présents dans le système +// Lors de 'bdd_init()', toutes les entrées de TB_One_wire sont positionnées à Validite=0 SAUF celles utilisées par un Capteur. Dans ce cas, Validite=1. +// L'appel de 'enregistrement_annuaire_ow()' a été fait depuis la fonction 'c_bus_down_reponse_annuaire_ow()' dans 'c_commande.c'. Celle-ci a, avant l'appel, positionné +// les 'flag_présence' et les 'rom_code[]' avec les infos reçues du Compagnon. +// Reçoit : Le nombre de devices renvoyés par le Compagnon. En pratique, on ne se sert que du cas particulier où 'nb_devices=FFh' qui indique une erreur matérielle du bus. +// Renvoit : 0 si le traitement n'a détecté aucun problème pour démarrer, -1 si au moins une erreur a été détectée. +//********************************************************************************************************************************************************************************** +int8_t enregistrement_annuaire_ow(uint8_t nb_devices) +{ +uint16_t i, j; +uint16_t nb; +float valeur_initiale; +int8_t etat_initial; +uint8_t no_entree; +int8_t etat_systeme; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Mise à jour de TB_One_wire pour dévalider le flag de présence et d'affectation de toutes les entrées dans la BDD, par défaut : + sprintf(requete, "UPDATE TB_One_wire SET Presence=0, Affecte=0 "); // SDIR 330 + mysql_query(bdd.pointeur, requete); + + //======================================================================================== + // Cas où le Compagnon a rencontré une erreur matérielle lors de la découverte réseau OW : + if (nb_devices == 0xFF) + { + sprintf(tmp, "Le bus OW est détecté en erreur matérielle (%i). Aucun device n'a pu être identifié sur le bus.", entrees.ow.defaut_bus); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + sprintf(requete, "UPDATE TB_One_wire SET Etat=%i", OW_ETAT_BUS_NOK); + mysql_query(bdd.pointeur, requete); + + nb_devices = 0; // Permet de mettre à jour en conséquence les différentes structures en aval. Notamment en cas de réception d'un annuaire SANS synchro de la BDD (périodique) +// return(0); + } + else // S'il n'y a pas d'erreur matérielle, on remet à jour les éventuelles erreurs de bus : + { + sprintf(requete, "UPDATE TB_One_wire SET Etat=%i WHERE Etat=%i ", OW_ETAT_DEVICE_OK, OW_ETAT_BUS_NOK); + mysql_query(bdd.pointeur, requete); + } + + //======================================================================================== + // Mise à jour de TB_One_wire et complément du tableau 'entrees.ow.devices[]' pour les devices qui étaient déjà existants : + entrees.ow.nb_devices = nb_devices; + + for (i = 0; i < nb_devices; i ++) + { + // Recherche des devices déjà enregistrés dans la BDD avec ce ROM-Code : + nb = 0; + valeur_initiale = 0; + etat_initial = OW_ETAT_DEVICE_OK; + + sprintf(requete, "SELECT Valeur, Etat FROM TB_One_wire WHERE Rom_code='%s' ", entrees.ow.devices[i].rom_code); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (nb == 0) + { + valeur_initiale = atof(row[0]); // On ne conserve que la valeur de la première entrée trouvée (dans le cas où il y en a > 1, les entrées > 1 sont ensuite dévalidées...) + etat_initial = atoi(row[1]); // Idem pour l'état. + } + nb ++; + } + mysql_free_result(result); +// printf("Requête de comptage des ROM-Codes = [ %s; ] et le résultat est : [%i]\n", requete, nb); + + + switch(nb) + { + //---------------------------------------------------------------------------------------------------- + // Cas d'un nouveau device : + case 0 : + { + // Création de la requête d'insertion : + sprintf(requete, "INSERT INTO TB_One_wire (Affecte, Presence, Rom_code) VALUES (0, 1, '%s')", entrees.ow.devices[i].rom_code); // S'il est nouveau, c'est qu'il n'est affecté à aucun capteur. Par contre, il est présent... + mysql_query(bdd.pointeur, requete); +// printf("Requête d'insertion du ROM-Code = [ %s; ]\n", requete); + + // Info débug : + sprintf(tmp, "Le device OW [%s] vient d'être détecté pour la première fois sur le bus. Il vient d'être ajouté à la table TB_One_wire et il est stocké localement dans les devices sous le n° %i.", entrees.ow.devices[i].rom_code, i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + break; + } + + //---------------------------------------------------------------------------------------------------- + // Cas d'un device existant : + case 1 : + { + // Mise à jour du flag de présence : + sprintf(requete, "UPDATE TB_One_wire SET Presence=1 WHERE Rom_code='%s' ", entrees.ow.devices[i].rom_code); + mysql_query(bdd.pointeur, requete); +// printf("Requête de réactivation du ROM-Code = [ %s; ]\n", requete); + + // Info debug : +#ifdef __DEBUG_INIT + sprintf(tmp, "Le device OW [%s] est détecté présent sur le bus et est stocké localement sous le n° %i. Sa valeur initiale est %.2f °C", entrees.ow.devices[i].rom_code, i, valeur_initiale); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + //---------------------------------------------------------------------------------------------------- + // Cas d'un device apparaissant plusieurs fois dans la table : Ce cas n'est techniqueemnt pas possible puisque le Rom-Code est une clé primaire. + } + + // Mise à jour de la valeur en cours : + entrees.ow.devices[i].valeur = valeur_initiale; + entrees.ow.devices[i].etat = etat_initial; + entrees.ow.devices[i].no_entree = -1; // Par défaut, on considère qu'aucune entrée n'est attrinuée à ce device. + } + + + //======================================================================================== + // Vérification des capteurs utilisant les entrées OW : + for (i = 0; i < capteurs.nb; i ++) // Parcours de tous les capteurs + { + if (!capteurs.capteurs[i].flag_validite) continue; // On ne traite que ceux qui ont une configuration valide (donc Etat_systeme = 0 => config:Ok et module:Ok) + + if (capteurs.capteurs[i].type_entrees != TYPE_ENTREES_OW) continue; // Affectés à des entrées OW + +//printf("Capteur # %i : type_entrees=%i, id_entrees=%i, cle primaire=%i\n", i, capteurs.capteurs[i].type_entrees, capteurs.capteurs[i].no_entrees[0], capteurs.capteurs[i].cle_primaire); + + + no_entree = capteurs.capteurs[i].no_entrees[0]; // Simplifie le traitement et la lecture. No_entree est maintenant le n° d'entrée OW affectée à ce capteur. + etat_systeme = ERREUR_SYSTEME_RESEAU; // Valeur par défaut + + entrees.ow.entrees[no_entree].flag_validite = FALSE; // Mise à FALSE temporaire de la validité de l'entrée OW (pour le cas où on ne trouve pas de match ensuite) + for (j = 0; j < nb_devices; j ++) + { + if (strstr(entrees.ow.entrees[no_entree].rom_code, entrees.ow.devices[j].rom_code) != NULL) + { + etat_systeme = ERREUR_SYSTEME_AUCUNE; + entrees.ow.entrees[no_entree].flag_validite = TRUE; // Remise à TRUE de la validité de l'entrée OW + entrees.ow.devices[j].no_entree = no_entree; // Créé le lien entre 'entrees.ow.devices[]' et 'entrees.ow.entrees[]' + + // Mise à jour du flag Affecté du Device dans la BDD : + sprintf(requete, "UPDATE TB_One_wire SET Affecte=1 WHERE Rom_code='%s' ", entrees.ow.entrees[no_entree].rom_code); + mysql_query(bdd.pointeur, requete); +// printf("Requête de réactivation du ROM-Code = [ %s; ]\n", requete); + break; + } + } + + // Mise à jour de l'état du Capteur dans la BDD : + if (capteurs.capteurs[i].etat_systeme != etat_systeme) + { + capteurs.capteurs[i].etat_systeme = etat_systeme; + capteurs.capteurs[i].flag_dde_maj_bdd = true; + } + + // Note : on ne modifie pas le caractère "Affecté" du device dans TB_One_wire si le device a disparu. + // En effet, s'il était affecté à la précédente synchro c'est que c'était le cas... + // On est ici probablement dans le cas où le device a "disparu" en cours de fonctionnement. + + + // Conclusion : on sort ici SOIT avec Capteur ET Entrée_ow validés, SOIT Capteur ET Entrée_ow dévalidés (toujours les deux). + } + + + //======================================================================================== + // Supression de tous les devices enregistrés dans TB_One_wire qui sont pas présents et auquel aucun capteur n'est lié : + sprintf(requete, "DELETE FROM TB_One_wire WHERE Affecte=0 AND Presence=0 "); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + //======================================================================================== + if (nb_devices == 0) + { + sprintf(tmp, "Aucun device OW n'est présent actuellement"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + + entrees.ow.flag_maj_annuaire_en_cours = FALSE; // utilisé par Système.c pour s'ssurer que la mise à jour de l'annuaire est terminé + + return(0); +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistre dans la BDD le contenu de l'EEPROM de la carte +// Reçoit : Le numéro de carte et le nombre d'octets d'EEPROM à stocker dans la BDD +// Renvoit : Rien +//********************************************************************************************************************************************************************************** +void enregistrement_eeprom(uint8_t adresse_module, uint16_t longueur) +{ +uint16_t i; +char chaine_eeprom[(MAX_TAILLE_EEPROM * 2) + 1] = ""; // Chaque octet est codé sur 2 caractères hexa + null de fin +char chaine_octet[3]; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (systeme.modules_config[adresse_module].flag_presence == FALSE) return; // On ne taite que les cartes présentes + + if (longueur > MAX_TAILLE_EEPROM) + { + sprintf(tmp, "La taille d'EEPROM à stocker dans la BDD (TB_Modules_physiques) est supérieure au maxi autorisé (%i octets demandés pour une taille maxi de %i octets)", longueur, MAX_TAILLE_EEPROM); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return; + } + + // Codage de chaque octet en 2 caractères hexa : + for (i = 0; i < longueur; i ++) + { + sprintf(chaine_octet, "%02X", systeme.modules_physiques[adresse_module].eeprom[i]); // Codage d'un octet + strcat(chaine_eeprom, chaine_octet); // Ajout à la chaine + } + + // Enregistrement des nouvelles valeurs dans la BDD : + sprintf(requete, "UPDATE TB_Modules_physiques SET Eeprom='%s' WHERE Adresse_k_bus='%i'", chaine_eeprom, adresse_module); + mysql_query(bdd.pointeur, requete); + +// Debug : +// sprintf(tmp, "La requête de mise à jour de l'EEPROM est [%.3000s]", requete); // NB : le '.3000' permet de limiter le nombre de caractère transférés et évite un warning pour cause de taille de buffers... +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +// Fin de debug + +} + +//********************************************************************************************************************************************************************************** +// Fonction ENREGISTREMENT_HISTORIQUE +// +// Enregistre un évènement dans la table historique +//********************************************************************************************************************************************************************************** +void enregistrement_historique(uint8_t type_source, uint8_t type, uint16_t id_source, float valeur, char * complement) + +{ +bool flag_enregistrement; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + // Valeurs par défaut : + flag_enregistrement = TRUE; + if (complement == NULL) complement = ""; // Sinon le printf suivant va coller une valeur '(null)' qui sera stockée comme une chaine... + + // Calcul des paramètres des requêtes selon le type_source d'objet concerné : + switch(type_source) + { + //------------------------------------ + case TYPE_SOURCE_HISTORIQUE_BATTERIES : + { + if (batteries.batteries[batterie_cle_primaire(id_source)].mode_enregistrement_historique == MODE_ENREGISTREMENT_HISTORIQUE_AUCUN) flag_enregistrement = FALSE; + break; + } + + //------------------------------------ + case TYPE_SOURCE_HISTORIQUE_CAPTEURS : + { + if (capteurs.capteurs[capteur_cle_primaire(id_source)].mode_enregistrement_historique == MODE_ENREGISTREMENT_HISTORIQUE_AUCUN) flag_enregistrement = FALSE; + break; + } + + //------------------------------------ + case TYPE_SOURCE_HISTORIQUE_CIRCUITS : + { + if (circuits.circuits[circuit_cle_primaire(id_source)].mode_enregistrement_historique == MODE_ENREGISTREMENT_HISTORIQUE_AUCUN) flag_enregistrement = FALSE; + break; + } + + //------------------------------------ + case TYPE_SOURCE_HISTORIQUE_RESERVOIRS : + { + if (reservoirs.reservoirs[reservoir_cle_primaire(id_source)].mode_enregistrement_historique == MODE_ENREGISTREMENT_HISTORIQUE_AUCUN) flag_enregistrement = FALSE; + break; + } + + //------------------------------------ + case TYPE_SOURCE_HISTORIQUE_SYSTEME : + case TYPE_SOURCE_HISTORIQUE_USB : + { + break; + } + + //------------------------------------ + default : + { + flag_enregistrement = FALSE; + break; + } + } + + // Enregistrement si cela est autorisé : + if (flag_enregistrement) + { + sprintf(requete, "INSERT INTO TB_Historique (Type_source, ID_Source, Type, Valeur, Complement, No_paquet) VALUES (%i, %i, %i, %.4f, '%s', %i)", type_source, id_source, type, valeur, complement, systeme.no_paquet); +// printf("Requete d'enregistrement dans l'historique = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : A intervalle régulier, enregistre la valeur moyenne depuis le dernier enregistrement de chaque batterie, capteur, circuit et réservoir valide (et dont le flag +// de mesure est VRAI) dans la table TB_Mesures_xxx correspondante. L'enregistrement d'une nouvelle valeur n'a lieu que si la nouvelle valeur est suffisement eloignée +// de la précédente valeur enregistrée (utilisation de la valeur de Marge). +// Cette fonction est également appelée en moe Init. Dans ce cas, il s'agit de crééer une mesure à valeur NULL pour chaque variable de chaque batterie, capteur, +// circuit et réservoir datée de la dernière valeur connue de Heart_beat. Cela permet d'enregistrer sur chaque variable la date à partir de laquelle on a perdu les mesures. +// Reçu : Le mode (BDD_ENREGISTREMENT_MESURE_INIT=0 ou BDD_ENREGISTREMENT_MESURE_NORMAL=1) +// Retour : Rien +//********************************************************************************************************************************************************************************** +void enregistrement_mesures(uint8_t mode) +{ +int i; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char str_date[MAX_LONGUEUR_DATE_COURANTE]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if ((bdd.compteur_enregistrement_mesures == 0) || (mode == BDD_ENREGISTREMENT_MESURE_INIT)) + { + // Rechargement du timer : + bdd.compteur_enregistrement_mesures = PERIODE_ENREGISTREMENT_MESURES; + + //--------------------------------------------- + //Calcul de la date et de l'heure des mesures : + //--------------------------------------------- + // On ne fait le calcul qu'une fois et, surtout, tous les enregistrements de cette boucle auront le même horodatage : + if (mode == BDD_ENREGISTREMENT_MESURE_NORMAL) date_courante(str_date, DATE_COURANTE_SANS_MS, FORMAT_DATE_COURANTE_MYSQL); + else + { + // Dans le cas d'une init, on va chercher la date du dernier Heart-Beat qui sera utilisée pour générer une mesure NULL à cette date sur tous les canaux de mesure. + sprintf(requete, "SELECT Date_maj FROM TB_Systeme WHERE Nom='Heart_beat' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + strcpy(str_date, row[0]); // Copie de la dernière date de MAJ du Heart-Beat comme date des mesures à créer + } + mysql_free_result(result); //Libération du jeu de résultat + } + + //------------------------------------ + // Boucle sur toutes les batteries : + //------------------------------------ + for (i=0; icle_primaire, str_date); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + variables->last_mesure = FLT_MAX; // Initialisation à la valeur maximale d'un float pour être sûr que le prochain appel générera un enregistrement... + + sprintf(requete, "INSERT INTO %s (ID, ID_Variable, Valeur, Date, No_paquet) VALUES ('%i', '%i', NULL, '%s', '%i')", nom_table, cle_primaire, variable->cle_primaire, str_date, systeme.no_paquet); +// printf("Requete d'enregistement mesure = %s\n", requete); + mysql_query(bdd.pointeur, requete); // Enregistrement dans la BDD + + break; + } + + //--------------------------------------------------------------------------- + case BDD_ENREGISTREMENT_MESURE_NORMAL : + //--------------------------------------------------------------------------- + { + valeur = (variables->cumul / cumuls_nb_val); // Calcul de la valeur moyenne + variables->cumul = 0; // RAZ du cumul + + if (fabs(variables->last_mesure - valeur) >= variable->marge) // On n'enregistre une nouvelle valeur que si la moyenne calculée depuis le dernier enregistrement a dévié de plus que [marge] + { +#ifdef __DEBUG_BDD_MESURES + sprintf(tmp, "Enregistrement d'une nouvelle valeur de mesure dans %s, pour la clé_primaire %i, sur variable %i, avec ancienne valeur = %.3f et nouvelle valeur = %.3f (delta = %.3f pour marge = %.3f)", nom_table, cle_primaire, variable->cle_primaire, variables->last_mesure, valeur, fabs(variables->last_mesure - valeur), variable->marge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + variables->last_mesure = valeur; // Mémorisation de la dernière valeur enregistrée dans mesures pour usage ultérieur. + + sprintf(requete, "INSERT INTO %s (ID, ID_Variable, Valeur, Date, No_paquet) VALUES ('%i', '%i', '%f', '%s', '%i')", nom_table, cle_primaire, variable->cle_primaire, valeur, str_date, systeme.no_paquet); +// printf("Requete d'enregistement mesure = %s\n", requete); + mysql_query(bdd.pointeur, requete); // Enregistrement dans la BDD + } + else + { +#ifdef __DEBUG_BDD_MESURES + sprintf(tmp, "Annulation de l'enregistrement d'une nouvelle valeur de mesure dans %s, pour la clé_primaire %i, sur variable %i, avec ancienne valeur = %.3f et nouvelle valeur = %.3f (delta = %.3f pour marge = %.3f)", nom_table, cle_primaire, variable->cle_primaire, variables->last_mesure, valeur, fabs(variables->last_mesure - valeur), variable->marge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + + break; + } + + //--------------------------------------------------------------------------- + case BDD_ENREGISTREMENT_MESURE_PONCTUEL : + //--------------------------------------------------------------------------- + { + valeur = variables->valeur_courante; + + if (fabs(variables->last_mesure - valeur) >= variable->marge) // On n'enregistre une nouvelle valeur que si la nouvelle valeur a dévié de plus que [marge] depuis le dernier enregistrement de mesure + { + date_courante(str_date_ponctuelle, DATE_COURANTE_SANS_MS, FORMAT_DATE_COURANTE_MYSQL); + +#ifdef __DEBUG_BDD_MESURES + sprintf(tmp, "Enregistrement d'une nouvelle valeur de mesure ponctuelle dans %s, pour la clé_primaire %i, sur variable %i, avec ancienne valeur = %.3f et nouvelle valeur = %.3f (delta = %.3f pour marge = %.3f)", nom_table, cle_primaire, variable->cle_primaire, variables->last_mesure, valeur, fabs(variables->last_mesure - valeur), variable->marge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + variables->last_mesure = valeur; // Mémorisation de la dernière valeur enregistrée dans mesures pour usage ultérieur. + + sprintf(requete, "INSERT INTO %s (ID, ID_Variable, Valeur, Date, No_paquet) VALUES ('%i', '%i', '%f', '%s', '%i')", nom_table, cle_primaire, variable->cle_primaire, valeur, str_date_ponctuelle, systeme.no_paquet); +// printf("Requete d'enregistement mesure = %s\n", requete); + mysql_query(bdd.pointeur, requete); // Enregistrement dans la BDD + } + + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistre dans la BDD un nouveau message utilisateur +// Reçoit : le code du message, les paramètres du message s'il y en a et son niveau d'affichage +// Retour : -1 si erreur, l'ID du message créé sinon. +//********************************************************************************************************************************************************************************** +int32_t enregistrement_message_users(int32_t code, char * parametres, int8_t niveau) +{ +int32_t id = -1; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + if ((niveau != MESSAGE_USERS_ETAT_INFO) && + (niveau != MESSAGE_USERS_ETAT_ALERTE) && + (niveau != MESSAGE_USERS_ETAT_URGENT)) return(-1); + + // Préparation de la requête de création du message : + sprintf(requete, "INSERT INTO TB_Messages_users (Code, Parametres, Niveau) VALUES ('%i', '%s', '%i')", code, parametres, niveau); +// printf("Requete d'enregistement nouveau message users = %s\n", requete); + mysql_query(bdd.pointeur, requete); + + // Récupération de la clé_primaire du message créé : + sprintf(requete, "SELECT Cle_primaire FROM TB_Messages_users ORDER BY Cle_primaire DESC LIMIT 1"); +// printf("Requete d'enregistement d'un message user = %s\n", requete); + mysql_query(bdd.pointeur, requete); + result = mysql_store_result(bdd.pointeur); + row = mysql_fetch_row(result); + id = atoi(row[0]); + mysql_free_result(result); + + return(id); +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Mise à jour dans la BDD d'un message utilisateur existant +// Reçoit : l'ID du message, les paramètres du message s'il y en a et l'état demandé. +// Si le champ 'parametres' est une chaine vide, le champ n'est pas mis à jour (utiliser un espace pour l'effacer...) +// Si 'niveau' a la valeur de suppression (-1) la requête consiste juste à mettre son champ Validité à 0 (paramètres est ignoré) +// Nb : le Code du message ne peut pas être modifié une fois créé +// Retour : -1 si erreur, 0 sinon +//********************************************************************************************************************************************************************************** +int8_t maj_message_users(uint32_t id, char * parametres, int8_t niveau) +{ +#define MAX_LG_PARAMETRES 1000 +uint32_t l; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +char update_parametres[MAX_LG_PARAMETRES+20] = ""; // Le '20' correspond au texte fixe dans ", Parametres='%s' " +char update_niveau[25] = ""; // '25' correspond à la taille maxi de ", Niveau=%i " +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + l = strlen(parametres); + if (l > MAX_LG_PARAMETRES) + { + sprintf(tmp, "Les [parametres] reçus ont une longueur trop importante (%i pour maxi = %i)", l, MAX_LG_PARAMETRES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + if (l > 0) sprintf(update_parametres, ", Parametres='%s' ", parametres); + + if ((niveau == MESSAGE_USERS_ETAT_INFO) || + (niveau == MESSAGE_USERS_ETAT_ALERTE) || + (niveau == MESSAGE_USERS_ETAT_URGENT)) // Mise à jour d'un message : + { + sprintf(update_niveau, ", Niveau=%i ", niveau); + + // Requête de mise à jour : + sprintf(requete, "UPDATE TB_Messages_users SET Cle_primaire=%i %s %s WHERE Cle_primaire=%i", id, update_parametres, update_niveau, id); // NB : l'update de Cle_primaire ne sert à rien mais permet de gérer simplement les virgules... +// printf("Requete de mise à jour de message users = %s\n", requete); + mysql_query(bdd.pointeur, requete); + } + else // Suppression d'un message : + { + // Requête de mise à jour : + sprintf(requete, "DELETE FROM TB_Messages_users WHERE Cle_primaire=%i", id); // Changement de Dévalidation à Suppression au passage à V1.4 pour éviter le remplissage infi de TB_Messages_users. +// printf("Requete de mise à jour de message users = %s\n", requete); + mysql_query(bdd.pointeur, requete); + } + + return(0); +} + + + +//********************************************************************************************************************************************************************************** +// Fonction : Efface dans la BDD les entrées de TB_Wifi_sta qui n'ont jamais été connectées (les autres sont conservées jusqu'à un effacement manuel) +// Reçoit : Rien +// Retour : -1 si erreur, 0 sinon +//********************************************************************************************************************************************************************************** +int8_t clear_scan_wifi_sta(void) +{ +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + // Suppression de tous les réseaux non connus : + sprintf(requete, "DELETE FROM TB_Wifi_sta WHERE Connu!=1"); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Raz du flag de présence de tous les réseaux : + sprintf(requete, "UPDATE TB_Wifi_sta SET Presence=0"); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + return(0); +} + + + +//********************************************************************************************************************************************************************************** +// Fonction : Enregistre dans la BDD un enregistrement du scan des réseaux Wifi en mode STA +// Reçoit : Rien +// Retour : -1 si erreur, 0 sinon +//********************************************************************************************************************************************************************************** +int8_t enregistrement_scan_wifi_sta(char * bssid, char * nom, uint8_t canal, uint8_t qualite) +{ +bool flag_existe = FALSE; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + // Recherche si le Bssid existe déjà dans la BDD : + // NB : on est obligé de passer par cette phase intermédiaire cer le REPLACE envisagé au départ (UPDATE ou INSERT) efface le Psk lorsque le réseau existe déjà. + sprintf(requete, "SELECT COUNT(*) FROM TB_Wifi_sta WHERE Bssid='%s' ", bssid); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (atoi(row[0]) > 0) flag_existe = TRUE; + } + mysql_free_result(result); //Libération du jeu de résultat + + + if (flag_existe) + { + sprintf(requete, "UPDATE TB_Wifi_sta SET Nom='%s', Canal=%i, Qualite=%i, Presence=1 WHERE Bssid='%s' ", nom, canal, qualite, bssid); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + } + else + { + sprintf(requete, "INSERT INTO TB_Wifi_sta (Bssid, Nom, Canal, Qualite, Presence) VALUES ('%s', '%s', %i, %i, 1)", bssid, nom, canal, qualite); +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + } + + return(0); +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Recherche le nom d'un capteur dans la BDD (a priori, utilisé en debug ou pour certaines fonctions de mise à jour) +// Reçoit : La clé primaire du capteur et le pointeur de la chaine où déposer le nom +// Retour : -1 si erreur, 0 sinon +//********************************************************************************************************************************************************************************** +int8_t nom_capteur_cle_primaire(uint16_t id, char * nom) +{ +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + nom[0] = 0; // Valeur "" par défaut + + sprintf(requete, "SELECT Nom FROM TB_Capteurs WHERE Cle_primaire=%i ", id); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + strcpy(nom, row[0]); + } + mysql_free_result(result); //Libération du jeu de résultat + + if (nom[0] == 0) return(-1); + else return(0); +} + +//********************************************************************************************************************************************************************************** +// Fonction : Recherche le nom d'un circuit dans la BDD (a priori, utilisé en debug ou pour certaines fonctions de mise à jour) +// Reçoit : La clé primaire du circuit et le pointeur de la chaine où déposer le nom +// Retour : -1 si erreur, 0 sinon +//********************************************************************************************************************************************************************************** +int8_t nom_circuit_cle_primaire(uint16_t id, char * nom) +{ +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + nom[0] = 0; // Valeur "" par défaut + + sprintf(requete, "SELECT Nom FROM TB_Circuits WHERE Cle_primaire=%i ", id); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + strcpy(nom, row[0]); + } + mysql_free_result(result); //Libération du jeu de résultat + + if (nom[0] == 0) return(-1); + else return(0); +} + + + + + diff --git a/sources/bdd_init.c b/sources/bdd_init.c new file mode 100644 index 0000000..ce2a3eb --- /dev/null +++ b/sources/bdd_init.c @@ -0,0 +1,3605 @@ +//********************************************************************************************************************************************************************************** +// Fichier BDD_INIT.C +// +// Contient la fonction d'initialisation de la BDD depuis l'appl +// +//********************************************************************************************************************************************************************************** + +// #define __DEBUG_PATCH_1_4_10 // Si décommenté, les infos de traitement du patch V1.4.10 seront affichées +#define __DEBUG_PATCH_3_0_13 // Si décommenté, les infos de traitement du patch V3.0.13 seront affichées + +// Include : +#include "main.h" + +// Prototypes : +int8_t init_bdd(uint8_t mode_init); + + +// Variables globales : +struct Struct_bdd bdd; + + + +//********************************************************************************************************************************************************************************** +// Fonction INIT_BDD +// +// Fonction initialisant l'accès à la base de données... +//********************************************************************************************************************************************************************************** +int8_t init_bdd(uint8_t mode_init) +{ +uint8_t val_u8; +int8_t val_8; +int16_t val_16; +uint32_t val_u32; +uint16_t i, j, k, m; // On évite la lettre 'l' qu'on confond avec le chiffre '1'... +bool flag_erreur; +char *ptr[MAX_NOMBRE_CHAMPS_INTERNES]; +char *ptr_val; +char *ptr_niv; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; + + + + +affichage_debug(__FILE__, __LINE__, "------------------BDD_INIT", TYPE_MESSAGE_DEBUG_WARNING); + + +// ******************************************************************************************************************************************************** +// INIT DE L'ACCES A LA BDD +// ******************************************************************************************************************************************************** + + if (mode_init == BDD_MODE_INIT_RESET) + { + //initialise l'objet MYSQL pour connections ultérieures: + bdd.pointeur = mysql_init(NULL); + if(bdd.pointeur == NULL) // Cas où l'initialisation n'a pas été possible : + { + sprintf(tmp,"Initialisation de la Base de Données impossible....!"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); // Arrêt de l'initialisation... + } + + // Définition du Charset en UTF-8 : + if (mysql_options(bdd.pointeur, MYSQL_SET_CHARSET_NAME, "utf8") != 0) + { + sprintf(tmp,"Changement du charset de la base de données en UTF-8 IMPOSSIBLE....!"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + //Connection à la BDD : + for (i=0; i < MAX_TENTATIVES_CONNEXION_BDD; i++) + { + if(mysql_real_connect(bdd.pointeur, BDD_SERVER, BDD_USER, BDD_PASSWORD, BDD_DATABASE, 0, NULL, 0) != NULL) break; + delai_dixieme_sec(10); // 1s environ + } + if (i == MAX_TENTATIVES_CONNEXION_BDD) + { + sprintf(tmp,"ERREUR de connexion à la Base de Données ....!"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + else // Cas normal : + { + sprintf(tmp, "Connexion à la base de données [après %i tentative(s)]\t\t\t: OK", (i+1)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + } + + +// ******************************************************************************************************************************************************** +// SYSTEME +// ******************************************************************************************************************************************************** + + maj_etat_cm3p(OFF); // C'est ici que la mise à jour est "au plus tôt"... => Maj du flag, de la BDD et de la sortie physique + + if (mode_init == BDD_MODE_INIT_RESET) + { + // Vidage de la table TB_Messages_users (SDIR #140) : +// sprintf(requete, "TRUNCATE TB_Messages_users "); // SDIR #156 (mise en commentaires) + sprintf(requete, "DELETE FROM TB_Messages_users WHERE Code!=%i ", MESSAGE_USERS_CODE_NOUVELLE_VERSION_DETECTEE); // SDIR #156 (nouvelle ligne) + mysql_query(bdd.pointeur, requete); + + // Mise à jour de la version de Compute Module : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Version_cm' ", systeme.cm.chaine_version); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de la version de package : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Version_soft_package' ", systeme.package_version); + mysql_query(bdd.pointeur, requete); + + // Mise à jour du numéro de la version de package : + // Lecture du numéro de version AVANT mise à jour (SDIR #156) : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Nom='Version_soft_no_package' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], systeme.package_no_version) != 0) // Si le n° de version actuel n'est pas celui stocké en base de données : + { + sprintf(tmp, "%s;%s", row[0], systeme.package_no_version); // Paramètres = 'ancienne version';'nouvelle version' + enregistrement_message_users(MESSAGE_USERS_CODE_NOUVELLE_VERSION_DETECTEE, tmp, MESSAGE_USERS_ETAT_INFO); + + sprintf(tmp, "Une nouvelle version de package a été installée [Ancienne version = %s et Nouvelle version = %s] ",row[0], systeme.package_no_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + // Mise à jour de la nouvelle version dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Version_soft_no_package' ", systeme.package_no_version); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de la version de l'application : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Version_soft_application_cm3p' ", systeme.cm3p_soft_version); + mysql_query(bdd.pointeur, requete); + } + + // Mise à jour du flag de veille : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='0' WHERE Nom='Veille' "); + mysql_query(bdd.pointeur, requete); + + // Lecture de tous les paramètres système : + sprintf(requete, "SELECT Nom, Valeur FROM TB_Systeme "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Etat_modem") == 0) modem.etat = atoi(row[1]); + else if (strcmp(row[0], "Presence_jack") == 0) audio.etat_presence_jack = atoi(row[1]); + else if (strcmp(row[0], "Presence_sim") == 0) modem.etat_presence_sim = atoi(row[1]); + else if (strcmp(row[0], "No_paquet") == 0) systeme.no_paquet = atoi(row[1]); +// Les adresses IP sont systématiquement réinitialisées à ADRESSE_IP_INCONNUE dans l'init de 'systeme.c' : +// else if (strcmp(row[0], "Adresse_ip_ethernet") == 0) strcpy(systeme.adresse_ip_ethernet, row[1]); +// else if (strcmp(row[0], "Adresse_ip_wifi_sta") == 0) strcpy(systeme.adresse_ip_wifi_sta, row[1]); +// else if (strcmp(row[0], "Adresse_ip_wifi_ap") == 0) strcpy(systeme.adresse_ip_wifi_ao, row[1]); + else ; + } + mysql_free_result(result); //Libération du jeu de résultat + + // Lecture de tous les paramètres de configuration : + sprintf(requete, "SELECT Nom, Valeur FROM TB_Configuration "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Nom_du_bateau") == 0) strcpy(systeme.nom_bateau, row[1]); + else if (strcmp(row[0], "Mode_validation_maj") == 0) systeme.mode_validation_maj = atoi(row[1]); + else if (strcmp(row[0], "Mode_maj_firmwares") == 0) systeme.mode_maj_firmwares = atoi(row[1]); + else if (strcmp(row[0], "Fuseau_horaire") == 0) systeme.fuseau_horaire = atoi(row[1]); + else if (strcmp(row[0], "Heure_ete") == 0) systeme.heure_ete = atoi(row[1]); + else if (strcmp(row[0], "Consigne_leds") == 0) ecran.consigne_leds = atof(row[1]); + else if (strcmp(row[0], "Validation_leds") == 0) ecran.validation_leds = atoi(row[1]); + else if (strcmp(row[0], "Consigne_backlight") == 0) ecran.consigne_backlight = atof(row[1]); + else if (strcmp(row[0], "Consigne_volume") == 0) audio.consigne_volume = atof(row[1]); + else if (strcmp(row[0], "Fonction_jack") == 0) audio.fonction_jack = atoi(row[1]); + else if (strcmp(row[0], "Periode_sauvegarde_bdd") == 0) systeme.periode_sauvegarde_bdd = atoi(row[1]); + else if (strcmp(row[0], "Periode_connexion_ship2net") == 0) reseaux.periode_connexion_ship2net = atoi(row[1]); + else if (strcmp(row[0], "Wifi_sta_cde") == 0) reseaux.wifi.sta.cde = atoi(row[1]); + else if (strcmp(row[0], "Wifi_ap_cde") == 0) reseaux.wifi.ap.cde = atoi(row[1]); + else if (strcmp(row[0], "Wifi_sta_ssid") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_STA_TAILLE_SSID) strcpy(reseaux.wifi.sta.ssid, row[1]); + } + else if (strcmp(row[0], "Wifi_sta_psk") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_STA_TAILLE_PSK) strcpy(reseaux.wifi.sta.psk, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_ssid") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_AP_TAILLE_SSID) strcpy(reseaux.wifi.ap.ssid, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_pwd") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_AP_TAILLE_PWD) strcpy(reseaux.wifi.ap.pwd, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_canal") == 0) reseaux.wifi.ap.canal = atoi(row[1]); + else if (strcmp(row[0], "Periode_check_adresses_ip") == 0) reseaux.periode_check_adresses_ip = atoi(row[1]); + else if (strcmp(row[0], "Periode_scan_wifi_sta") == 0) reseaux.periode_scan_sta = atoi(row[1]); + + else if (strcmp(row[0], "Acces_webgl_1_validation") == 0) reseaux.webgl_new[0].validite = atoi(row[1]); + else if (strcmp(row[0], "Acces_webgl_1_parametres") == 0) + { + if (strlen(row[1]) < MAX_LONGUEUR_PARAMETRES_WEBGL) strcpy(reseaux.webgl_new[0].parametres, row[1]); + } + else if (strcmp(row[0], "Acces_webgl_2_validation") == 0) reseaux.webgl_new[1].validite = atoi(row[1]); + else if (strcmp(row[0], "Acces_webgl_2_parametres") == 0) + { + if (strlen(row[1]) < MAX_LONGUEUR_PARAMETRES_WEBGL) strcpy(reseaux.webgl_new[1].parametres, row[1]); + } + else if (strcmp(row[0], "Acces_webgl_3_validation") == 0) reseaux.webgl_new[2].validite = atoi(row[1]); + else if (strcmp(row[0], "Acces_webgl_3_parametres") == 0) + { + if (strlen(row[1]) < MAX_LONGUEUR_PARAMETRES_WEBGL) strcpy(reseaux.webgl_new[2].parametres, row[1]); + } + else if (strcmp(row[0], "Courant_masses_maxi_sp8dc") == 0) // SDIR #318 + { + systeme.courant_masses_maxi_sp8dc = atof(row[1]); + if (systeme.courant_masses_maxi_sp8dc > MAX_COURANT_MASSES_SP8DC) systeme.courant_masses_maxi_sp8dc = MAX_COURANT_MASSES_SP8DC; + } + + else ; + } + mysql_free_result(result); //Libération du jeu de résultat + + // Dévalidation par défaut de l'adresse IP du port Ethernet : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_ethernet' ", ADRESSE_IP_INCONNUE); + mysql_query(bdd.pointeur, requete); + + // Dévalidation par défaut de l'adresse IP du port Wifi_sta : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_wifi_sta' ", ADRESSE_IP_INCONNUE); + mysql_query(bdd.pointeur, requete); + + // Dévalidation par défaut de l'adresse IP du port Wifi_ap : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_wifi_ap' ", ADRESSE_IP_INCONNUE); + mysql_query(bdd.pointeur, requete); + + // RAZ de la liste existante des entrées AUX (utile lors d'une reconfiguration) : + entrees.aux.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_AUXILIAIRES; i ++) entrees.aux.entrees[i].flag_validite = FALSE; + + // RAZ de la liste existante des entrées LEVEL SENSORS (utile lors d'une reconfiguration) : + entrees.level_sensor.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i ++) entrees.level_sensor.entrees[i].flag_validite = FALSE; + + // RAZ de la liste existante des entrées VOLTAGE (utile lors d'une reconfiguration) : + entrees.volt.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_VOLTAGE; i ++) entrees.volt.entrees[i].flag_validite = FALSE; + + // RAZ de la liste existante des entrées OW (utile lors d'une reconfiguration) : + entrees.ow.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_OW; i ++) entrees.ow.entrees[i].flag_validite = FALSE; + + // RAZ de la liste existante des entrées NMEA (utile lors d'une reconfiguration) : + entrees.nmea.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_NMEA; i ++) entrees.nmea.entrees[i].flag_validite = FALSE; + + // RAZ de la liste existante des entrées PREDEFINIES (utile lors d'une reconfiguration) : + entrees.predefinies.nb = 0; + for (i = 0; i < MAX_NOMBRE_ENTREES_PREDEFINIES; i ++) entrees.predefinies.entrees[i].flag_validite = FALSE; + + // Les entrées prédéfinies suivantes sont créées automatiquement car elles sont utilisées par des fonctions de base. + // Il faut noter qu'il est par ailleurs possible que ces entrées soient utilisées par d'autres équipements (capteurs et/ou batteries) + // comme n'importe quelle entrée prédéfinie. Dans ce cas, la même entrée physique sera routée vers plusieurs entrées prédéfinies par + // la 'commande c_bus_up_commande_etat_carte()' sans que cela ne pose de problème. + + // Création de l'entrée prédéfinie IN_V (qui est utilisée quoi qu'il arrive) : + entrees.predefinies.entrees[entrees.predefinies.nb].flag_validite = TRUE; + entrees.predefinies.entrees[entrees.predefinies.nb].module = 0; + entrees.predefinies.entrees[entrees.predefinies.nb].no = ENTREE_PREDEFINIE_IN_V; + entrees.predefinies.nb ++; + + // Création de l'entrée prédéfinie IN_I (qui est utilisée quoi qu'il arrive) : + entrees.predefinies.entrees[entrees.predefinies.nb].flag_validite = TRUE; + entrees.predefinies.entrees[entrees.predefinies.nb].module = 0; + entrees.predefinies.entrees[entrees.predefinies.nb].no = ENTREE_PREDEFINIE_IN_I; + entrees.predefinies.nb ++; + + // Création de l'entrée prédéfinie USB_V (qui est utilisée quoi qu'il arrive) : + entrees.predefinies.entrees[entrees.predefinies.nb].flag_validite = TRUE; + entrees.predefinies.entrees[entrees.predefinies.nb].module = 0; + entrees.predefinies.entrees[entrees.predefinies.nb].no = ENTREE_PREDEFINIE_USB_I; + entrees.predefinies.nb ++; + + +// ******************************************************************************************************************************************************** +// MODULES +// +// NB : Une partie importante de la configuration des modules se fait dans la fonction 'enregistrement_annuaire_k_bus()' appelée +// après avoir récupéré la commande ANNUAIRE auprès du Compagnon. Cf Commentaire dans 'enregistrement_annuaire_k_bus()' pour plus d'explications. +// ******************************************************************************************************************************************************** + + // SDIR #300 : + // Mise à jour depuis la BDD de l'adresse MAC de ShipBase en attendant qu'elle soit remise à jour via le retour de 'annuaire_k_bus' : + for (i=0; i= 3.0.13 : + // Supprimé en 06/2026, au passage à la V4.0.0. + // Fin du patch + //------------------------------------------------------------- + + + // Init par défaut de toutes les variables de circuits de l'appli : + for (i = 0; i < MAX_NOMBRE_CIRCUITS; i ++) + { + circuits.circuits[i].flag_validite = FALSE; // Valeur par défaut + circuits.circuits[i].etat_systeme = ERREUR_SYSTEME_AUCUNE; // Valeur par défaut + } + + // Mise à jour des IDs de variables utilisées par les circuits : + sprintf(requete, "SELECT Code, Cle_primaire, Marge FROM TB_Variables WHERE Code LIKE 'Circ_%%' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Circ_consigne") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_CONSIGNE].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_CONSIGNE].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_tension") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_TENSION].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_TENSION].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_courant") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_COURANT].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_COURANT].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_frequence") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_FREQUENCE].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_FREQUENCE].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_puissance") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_PUISSANCE].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_PUISSANCE].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_energie") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_ENERGIE].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_ENERGIE].marge = atof(row[2]); + } + else if (strcmp(row[0], "Circ_tension_secteur") == 0) + { + circuits.variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].cle_primaire = atoi(row[1]); + circuits.variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].marge = atof(row[2]); + } + else ; + } + mysql_free_result(result); //Libération du jeu de résultat + + + // Mise à 0 de Etat_systeme de toutes les batteries par défaut. + // La remise à une autre valeur sera éventuellement faite : + // - en fin de cette fonction si la configuration n'est pas cohérente et ne peut être exploitée + // - dans 'bdd_fonctions.c', dans 'enregistrement_annuaire_k_bus()', si le module auquel est affecté cette batterie n'est pas pas présent (module physique absent ou pas du bon type) + sprintf(requete, "UPDATE TB_Circuits SET Etat_systeme=0"); + mysql_query(bdd.pointeur, requete); + + + // Récupération de la configuration des circuits : + i = 0; + sprintf(requete, "SELECT Cle_primaire, Adresse_module, No_circuit, ID_Circuit_esclave, Mode_commande, Etat, Mode_test, Consigne, \ + Tension, Intensite, Frequence, Tension_coeff_filtrage, Intensite_coeff_filtrage, Frequence_coeff_filtrage, \ + Intensite_seuil_haut, Intensite_seuil_haut_tempo, Step_time, T_on, T_off, Time_out, Validation_cdes_events, \ + Mode_enregistrement_mesures, Mode_enregistrement_historique \ + FROM TB_Circuits ORDER BY Adresse_module, No_circuit"); // On ne récupère PAS les circuits NON AFFECTES (non valides) +// printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + // Vérification que le nombre de circuits n'est pas trop important : + if (i >= MAX_NOMBRE_CIRCUITS) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de circuits valides dans la Base de Données (TB_Circuits) est trop important (MAX = %i)...!", MAX_NOMBRE_CIRCUITS); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + +// Traiter ce cas avec un message d'alerte user ???? + + break; // On arrête l'init des circuits mais on continue l'initialisation... + } + + // Stokage des valeurs lues : + circuits.circuits[i].flag_validite = TRUE; // Valeur par défaut + + j = 0; // Permet l'insertion plus facile d'un champs par la suite..! Ne pas oublier de l'incrémenter correctement à chaque étape !! + + //--------------------------------------- + // Clé primaire : + //--------------------------------------- + circuits.circuits[i].cle_primaire = atoi(row[j ++]); // ATTENTION : dans les lignes suivantes, on alterne entre 'atoi()' et 'atof()' en fonction du type de variable. Prendre garde lors des copier/coller !! + + //--------------------------------------- + // Adresse de module : + //--------------------------------------- + val_16 = atoi(row[j ++]); // On prend en compte les circuits 'non affectés' qui arrivent avec un numéro de module = -1 + + if ((val_16 < 0) || (val_16 >= MAX_NOMBRE_MODULES)) + { + // Message d'erreur : + if (val_16 < 0) + { + sprintf(tmp, "Le circuit n° %i (Cle_primaire = %i) dans TB_Circuits est Non Affecté (Adresse_module = %i). Le circuit est dévalidé.", i, circuits.circuits[i].cle_primaire, val_16); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le numéro de module du circuit n° %i (Cle_primaire = %i) dans TB_Circuits est trop élevé (%i, MAX = %i). Le circuit est dévalidé.", i, circuits.circuits[i].cle_primaire, val_16, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + circuits.circuits[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + else circuits.circuits[i].adresse_module = val_16; + + //--------------------------------------- + // N° de circuit sur le module : + //--------------------------------------- + circuits.circuits[i].no_circuit = atoi(row[j ++]); + + //--------------------------------------- + // ID Circuit esclave : + //--------------------------------------- + circuits.circuits[i].id_circuit_esclave = atoi(row[j ++]); // NB : on ne peut pas calculer ici le 'no_circuit_esclave' car toutes les clés primaires n'ont pas été enregistrées... + + //--------------------------------------- + // Mode de commande : + //--------------------------------------- + circuits.circuits[i].mode_cde = atoi(row[j ++]); + + //--------------------------------------- + // Etat : + //--------------------------------------- + circuits.circuits[i].etat = atoi(row[j ++]); + + //--------------------------------------- + // Mode test : + //--------------------------------------- + circuits.circuits[i].mode_test = atoi(row[j ++]); + + //--------------------------------------- + // Consigne : + //--------------------------------------- + circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_bdd = atoi(row[j ++]); + + //--------------------------------------- + // Tension : + //--------------------------------------- + if (systeme.modules_config[circuits.circuits[i].adresse_module].type != TYPE_CARTE_SP4AC) circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_bdd = atof(row[j ++]); + else circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_bdd = atof(row[j ++]); + + //--------------------------------------- + // Intensité : + //--------------------------------------- + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_bdd = atof(row[j ++]); + + //--------------------------------------- + // Fréquence : + //--------------------------------------- + circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_bdd = atof(row[j ++]); + + //--------------------------------------- + // Tension coeff filtrage : + //--------------------------------------- + circuits.circuits[i].tension_coeff_filtrage = atoi(row[j ++]); + + //--------------------------------------- + // Intensité coeff filtrage : + //--------------------------------------- + circuits.circuits[i].intensite_coeff_filtrage = atoi(row[j ++]); + + //--------------------------------------- + // Fréquence coeff filtrage : + //--------------------------------------- + circuits.circuits[i].frequence_coeff_filtrage = atoi(row[j ++]); + + //--------------------------------------- + // Intensité seuil haut : + //--------------------------------------- + circuits.circuits[i].intensite_seuil_haut = atof(row[j ++]); + + //--------------------------------------- + // Intensité seuil haut tempo : + //--------------------------------------- + circuits.circuits[i].intensite_seuil_haut_tempo = atoi(row[j ++]); + + //--------------------------------------- + // Step Time : + //--------------------------------------- + circuits.circuits[i].step_time = atoi(row[j ++]); + + //--------------------------------------- + // Ton : + //--------------------------------------- + circuits.circuits[i].t_on = atoi(row[j ++]); + + //--------------------------------------- + // Toff : + //--------------------------------------- + circuits.circuits[i].t_off = atoi(row[j ++]); + + //--------------------------------------- + // Time-out : + //--------------------------------------- + circuits.circuits[i].time_out = atoi(row[j ++]); + + //--------------------------------------- + // Validation Commandes Events : + //--------------------------------------- + val_8 = atoi(row[j ++]); + + if ((val_8 != CIRCUIT_VALIDATION_COMMANDES_EVENTS_OFF) && (val_8 != CIRCUIT_VALIDATION_COMMANDES_EVENTS_ON)) + { + sprintf(tmp, "La validation des commandes par évènements du circuit n° %i (Cle_primaire = %i) dans la Base de Données (TB_Circuits) est hors limite (%i pour 0 ou 1). Le circuit est laissé valide MAIS son Validation_cdes_event est arbitrairement fixé à %i.", i, circuits.circuits[i].cle_primaire, val_8, CIRCUIT_VALIDATION_COMMANDES_EVENTS_ON); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + val_8 = CIRCUIT_VALIDATION_COMMANDES_EVENTS_ON; + } + circuits.circuits[i].validation_cdes_events = val_8; + + //--------------------------------------- + // Enregistrement mesures : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + { + sprintf(tmp, "Le mode d'enregistrement des mesures du circuit n° %i (Cle_primaire = %i) dans la Base de Données (TB_Circuits) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement des mesures est arbitrairement fixé à %i.", i, circuits.circuits[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT, MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + val_u8 = MODE_ENREGISTREMENT_MESURES_PERIODIQUE; // Valeur arbitraire correspondant à aucun enregistrement des mesures. NB : on considère que ce n'est pas une raison pour dévalider le capteur mais on pourrait tout aussi bien le faire ici... + } + circuits.circuits[i].mode_enregistrement_mesures = val_u8; + + //--------------------------------------- + // Enregistrement historique : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_HISTORIQUE_NORMAL) + { + sprintf(tmp, "Le mode d'enregistrement de l'historique du circuit n° %i (Cle_primaire = %i) dans la Base de Données (TB_Circuits) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement d'historique est arbitrairement fixé à %i.", i, circuits.circuits[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_HISTORIQUE_NORMAL, MODE_ENREGISTREMENT_HISTORIQUE_NORMAL); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + val_u8 = MODE_ENREGISTREMENT_HISTORIQUE_NORMAL; + } + circuits.circuits[i].mode_enregistrement_historique = val_u8; + + + //=================================================================================================== + // Vérification de la cohérence de la configuration du circuit avec le module auquel il est affecté : + //=================================================================================================== + flag_erreur = false; + switch (systeme.modules_config[circuits.circuits[i].adresse_module].type) + { + //----------------------- + case TYPE_CARTE_SHIPBASE: + { + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + // Cas particulier du port USB arrière absent des V1 et présent dans la config usine commune aux 2 versions : + if (circuits.circuits[i].no_circuit == SB_CIRCUIT_NO_USB_ARRIERE) + { + sprintf(requete, "DELETE FROM TB_Circuits WHERE Cle_primaire='%i' ", circuits.circuits[i].cle_primaire); // Dans le cas d'une V1 avec le port USB arrière configuré (exemple de Factiry reset), on supprime purement et simplement l'entrée pour les fois suivantes + mysql_query(bdd.pointeur, requete); + + sprintf(tmp, "La configuration contenait un port USB Arrière alors que l'on est sur une V1. Le circuit est supprimé de TB_Circuits (clé primaire = %i) et dévalidé pour l'exécution en cours.", circuits.circuits[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + +// circuits.circuits[i].flag_validite = FALSE; // Le circuit est dévalidé pour cette exécution là (plus simple) + circuits.circuits[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + } + + // Test du n° de circuit : + if (circuits.circuits[i].no_circuit >= MAX_CIRCUITS_PAR_MODULE_SHIPBASE_V2) + { + printf("N° de circuit = %i alors que MAX ShipBase = 2\n", circuits.circuits[i].no_circuit); + flag_erreur = true; + break; + } + + // Cas particulier des ports USB sur ShipBase : + if (circuits.circuits[i].no_circuit == SB_CIRCUIT_NO_USB_AVANT) usb.no_circuit_usb_avant = i; + else if (circuits.circuits[i].no_circuit == SB_CIRCUIT_NO_USB_ARRIERE) usb.no_circuit_usb_arriere = i; + + // Forçage des paramètres qui n'ont qu'une valeur admissible (plus rapide...) : + circuits.circuits[i].id_circuit_esclave = 0; + circuits.circuits[i].mode_cde = CIRCUITS_MODE_COMMANDE_ON_OFF; + + break; + } + + //----------------------- + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + { + if (circuits.circuits[i].no_circuit >= MAX_CIRCUITS_PAR_MODULE_SP8XX) + { +printf("N° de circuit = %i alors que MAX SP8XXX = 7\n", circuits.circuits[i].no_circuit); + flag_erreur = true; + break; + } + + break; + } + + //----------------------- + case TYPE_CARTE_SP4AC: + { + if (circuits.circuits[i].no_circuit >= MAX_CIRCUITS_PAR_MODULE_SP4AC) + { +printf("N° de circuit = %i alors que MAX ShipBase = 3\n", circuits.circuits[i].no_circuit); + flag_erreur = true; + break; + } + + // Forçage des paramètres qui n'ont qu'une valeur admissible (plus rapide...) : + circuits.circuits[i].id_circuit_esclave = 0; + + break; + } + + //----------------------- + case TYPE_CARTE_SBS: + case TYPE_CARTE_INCONNU: + default: + { +printf("Ciruit affecté à un module de type %i \n", systeme.modules_config[circuits.circuits[i].adresse_module].type); + flag_erreur = true; + break; + } + } + + if (flag_erreur) + { + sprintf(tmp, "Le circuit n° %i (Cle_primaire = %i) dans TB_Circuits contient des paramètres non compatibles avec le module auquel il est affecté. Le circuit est dévalidé.", i, circuits.circuits[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + +// circuits.circuits[i].flag_validite = FALSE; // Le circuit est dévalidé pour cette exécution là (plus simple) + circuits.circuits[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + + //================================================================ + // Traitements non directement liés au retour de la requête en BDD : + //================================================================ + + // ID Batterie : + circuits.circuits[i].id_batterie = systeme.modules_config[circuits.circuits[i].adresse_module].id_batterie; + + + + //================================================================ + // Mise à jour des valeurs courantes (SDIR #188) : + //================================================================ + + // Initialisation des valeurs courantes à partir des valeurs de BDD (évite de repartir à 0 mais plutôt de la dernière valeur connue) : + circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_bdd; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_bdd; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_bdd; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_bdd; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_bdd * circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_bdd; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_bdd; + // SDIR #174 : +// circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante; +// circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].cumul = 0; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].cumul = 0; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].cumul = 0; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].cumul = 0; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_PUISSANCE].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_PUISSANCE].cumul = 0; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].last_mesure = circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].cumul = 0; + circuits.circuits[i].cumuls_nb_val = 0; + + +/*printf("Circuit %i : ----------------------------------------------------------\n", i); + printf("Clé=%i, adresse_module=%i, No_circuit=%i, ID_Circuit_esclave=%i, Mode_cde=%i, Etat=%i, Mode_test=%i, Consigne=%.2f, \ + Tension=%.3f, Intensité=%.3f, Fréq=%.3f, Tension_filtrage=%i, Intensite_filtrage=%i, Freq_filtrage=%i, \ + Intensité_seuil_haut=%.3f, Intensité_seuil_hau_tempo=%i, Step_time=%i, T_on=%i, Toff=%i, Time_out=%i, Mode_mesures=%i, Mode_historique=%i \n", + circuits.circuits[i].cle_primaire, circuits.circuits[i].adresse_module, circuits.circuits[i].no_circuit, + circuits.circuits[i].id_circuit_esclave, circuits.circuits[i].mode_cde, circuits.circuits[i].etat, circuits.circuits[i].mode_test, + circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_bdd, circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_bdd, + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_bdd, circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_bdd, + circuits.circuits[i].tension_coeff_filtrage, circuits.circuits[i].intensite_coeff_filtrage, circuits.circuits[i].frequence_coeff_filtrage, + circuits.circuits[i].intensite_seuil_haut, circuits.circuits[i].intensite_seuil_haut_tempo, circuits.circuits[i].step_time, circuits.circuits[i].t_on, + circuits.circuits[i].t_off, circuits.circuits[i].time_out, circuits.circuits[i].mode_enregistrement_mesures, circuits.circuits[i].mode_enregistrement_historique); +*/ + + i++; // Circuit suivant + } + mysql_free_result(result); //Libération du jeu de résultat + + // Mise à jour des paramètres nécessitant d'avoir tous les circuits configurés : + circuits.nb = i; + for (i = 0; i < circuits.nb; i ++) + { + if (! circuits.circuits[i].flag_validite) continue; + + circuits.circuits[i].no_circuit_esclave = circuit_cle_primaire(circuits.circuits[i].id_circuit_esclave); + } + + + + +// ******************************************************************************************************************************************************** +// CAPTEURS +// ******************************************************************************************************************************************************** + + // Init par défaut de toutes les variables de capteurs de l'appli : + for (i = 0; i < MAX_NOMBRE_CAPTEURS; i ++) + { + capteurs.capteurs[i].flag_validite = FALSE; // Valeur par défaut + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_AUCUNE; // Valeur par défaut + capteurs.capteurs[i].nb_entrees = 0; + for (j = 0; j < MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR; j ++) capteurs.capteurs[i].conditions.conditions[j].flag_validite = FALSE; + } + + // Mise à jour des IDs de variables utilisées par les capteurs : + sprintf(requete, "SELECT Code, Cle_primaire, Marge FROM TB_Variables WHERE Code LIKE 'Capt_%%' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Capt_sans_unite") == 0) { capteurs.variables[CAPTEUR_VARIABLE_SANS_UNITE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_SANS_UNITE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_tension_v") == 0) { capteurs.variables[CAPTEUR_VARIABLE_TENSION_V].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_TENSION_V].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_tension_mv") == 0) { capteurs.variables[CAPTEUR_VARIABLE_TENSION_MV].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_TENSION_MV].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_courant_a") == 0) { capteurs.variables[CAPTEUR_VARIABLE_COURANT_A].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_COURANT_A].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_courant_ma") == 0) { capteurs.variables[CAPTEUR_VARIABLE_COURANT_MA].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_COURANT_MA].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_puissance_w") == 0) { capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_W].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_W].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_puissance_va") == 0) { capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_VA].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_VA].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_puissance_db") == 0) { capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_DB].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_PUISSANCE_DB].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_frequence") == 0) { capteurs.variables[CAPTEUR_VARIABLE_FREQUENCE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_FREQUENCE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_resistance") == 0) { capteurs.variables[CAPTEUR_VARIABLE_RESISTANCE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_RESISTANCE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_temperature") == 0) { capteurs.variables[CAPTEUR_VARIABLE_TEMPERATURE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_TEMPERATURE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_pression_atm") == 0) { capteurs.variables[CAPTEUR_VARIABLE_PRESSION_ATM].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_PRESSION_ATM].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_humidite") == 0) { capteurs.variables[CAPTEUR_VARIABLE_HUMIDITE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_HUMIDITE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_acceleration") == 0) { capteurs.variables[CAPTEUR_VARIABLE_ACCELERATION].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_ACCELERATION].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_position") == 0) { capteurs.variables[CAPTEUR_VARIABLE_POSITION].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_POSITION].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_pourcentage") == 0) { capteurs.variables[CAPTEUR_VARIABLE_POURCENTAGE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_POURCENTAGE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_distance_m") == 0) { capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_M].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_M].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_distance_km") == 0) { capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_KM].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_KM].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_distance_nq") == 0) { capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_NQ].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_DISTANCE_NQ].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_vitesse_kts") == 0) { capteurs.variables[CAPTEUR_VARIABLE_VITESSE_KTS].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_VITESSE_KTS].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_vitesse_kmh") == 0) { capteurs.variables[CAPTEUR_VARIABLE_VITESSE_KMH].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_VITESSE_KMH].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_angle_degres") == 0) { capteurs.variables[CAPTEUR_VARIABLE_ANGLE_DEGRES].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_ANGLE_DEGRES].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_latitude") == 0) { capteurs.variables[CAPTEUR_VARIABLE_LATITUDE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_LATITUDE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_longitude") == 0) { capteurs.variables[CAPTEUR_VARIABLE_LONGITUDE].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_LONGITUDE].marge = atof(row[2]); } + else if (strcmp(row[0], "Capt_volume") == 0) { capteurs.variables[CAPTEUR_VARIABLE_VOLUME].cle_primaire = atoi(row[1]); capteurs.variables[CAPTEUR_VARIABLE_VOLUME].marge = atof(row[2]); } + else ; + } + mysql_free_result(result); //Libération du jeu de résultat + + + // Mise à 0 de Etat_systeme de tous les capteurs par défaut. + // La remise à une autre valeur sera éventuellement faite : + // - en fin de cette fonction si la configuration n'est pas cohérente et ne peut être exploitée + // - dans 'bdd_fonctions.c', dans 'enregistrement_annuaire_k_bus()', si le module auquel est affecté ce capteur n'est pas pas présent (module physique absent ou pas du bon type) + sprintf(requete, "UPDATE TB_Capteurs SET Etat_systeme=0"); + mysql_query(bdd.pointeur, requete); + + + // Récupération de la configuration des capteurs : + i = 0; + sprintf(requete, "SELECT Cle_primaire, Adresse_module, Type_entrees, Entrees, Mode_entrees, ID_Variable, Etat, Decalage, Gain, Valeur, \ + Valeur_coeff_filtrage, Mode_enregistrement_mesures, Mode_enregistrement_historique \ + FROM TB_Capteurs ORDER BY Type_entrees, Cle_primaire"); +printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + // Vérification que le nombre de capteur n'est pas trop important : + if (i == MAX_NOMBRE_CAPTEURS) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de capteurs valides dans la Base de Données (TB_Capteurs) est trop important (MAX = %i)...!", MAX_NOMBRE_CAPTEURS); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); +// return(-1); +// Traiter ce cas avec un message d'alerte user ???? +break; // On arrête l'init des capteurs là mais on continue l'initialisation... + } + + // Stockage des valeurs lues : + j = 0; // Permet l'insertion plus facile d'un champs par la suite..! Ne pas oublier de l'incrémenter correctement à chaque étape !! + + capteurs.capteurs[i].flag_validite = TRUE; // On en est sûr puisque le filtre SQL est basé là-dessus... + + //--------------------------------------- + // Clé primaire : + //--------------------------------------- + capteurs.capteurs[i].cle_primaire = atoi(row[j ++]); // ATTENTION : dans les lignes suivantes, on alterne entre 'atoi()' et 'atof()' en fonction du type de variable. Prendre garde lors des copier/coller !! + + //--------------------------------------- + // Adresse de module : + //--------------------------------------- + val_16 = atoi(row[j ++]); // On prend en compte les capteur 'non affectés' qui arrivent avec un numéro de module = -1 (pas encore utilisé en 06/2026) + + if ((val_16 < 0) || (val_16 >= MAX_NOMBRE_MODULES)) + { + // Message d'erreur : + if (val_16 < 0) + { + sprintf(tmp, "Le capteur n° %i (Cle_primaire = %i) dans TB_Capteurs est Non Affecté (Adresse_module = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, val_16); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le numéro de module du capteur n° %i (Cle_primaire = %i) dans TB_Capteurs est trop élevé (%i, MAX = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, val_16, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + else capteurs.capteurs[i].adresse_module = val_16; + + //--------------------------------------- + // Type d'entrées : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + switch(val_u8) + { + case TYPE_ENTREES_AUXILIAIRES : + case TYPE_ENTREES_LEVEL_SENSOR : + case TYPE_ENTREES_VOLTAGE : + case TYPE_ENTREES_OW : + case TYPE_ENTREES_PREDEFINIES : +// case TYPE_ENTREES_IBS : + case TYPE_ENTREES_NMEA : +// case TYPE_ENTREES_CAN : + case TYPE_ENTREES_CALCULEE_BOOLEEN : +// case TYPE_ENTREES_CALCULEE_NUMERIQUE : + { + capteurs.capteurs[i].type_entrees = val_u8; + break; + } + default : + { + sprintf(tmp, "Le capteur n° %i (clé primaire = %i) dans la Base de Données (TB_Capteurs) a une configuration de 'Type_entrees' invalide (valeur = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + } + + //--------------------------------------- + // Entrées : + //--------------------------------------- + if (row[j][0] != 0) // Si le champ "Entrees" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' ATTENTION : si aucun délimiteur trouvé, la fonction renvoit la chaine entière (et pas NULL...) + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + // Calcul du nombre maxi d'entrées, selon le type de capteur : + uint16_t max_entrees = MAX_NOMBRE_ENTREES_PAR_CAPTEUR; + if (capteurs.capteurs[i].type_entrees == TYPE_ENTREES_CALCULEE_BOOLEEN) max_entrees = MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR; + + // Check du nombre d'entrées trouvées : + if (k > max_entrees) + { + // Message d'erreur : + sprintf(tmp, "Le nombre d'entrées du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est trop important (MAX = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, max_entrees); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + // Traitement des entrées trouvées selon la valeur de 'type_entrees' : + switch(capteurs.capteurs[i].type_entrees) + { + //.................................................................................................................... + case TYPE_ENTREES_AUXILIAIRES : + { + for (m = 0; m < k; m ++) // Nb : on a déjà vérifié que k < MAX_NOMBRE_ENTREES_PAR_CAPTEUR + { + val_u8 = atoi(ptr[m]); + + // Mise à jour des variables d'entrées : + if (entrees.aux.nb < MAX_NOMBRE_ENTREES_AUXILIAIRES) + { + // Mise à jour de l'entrée correspondante : + entrees.aux.entrees[entrees.aux.nb].flag_validite = TRUE; + entrees.aux.entrees[entrees.aux.nb].module = capteurs.capteurs[i].adresse_module; + entrees.aux.entrees[entrees.aux.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[m] = entrees.aux.nb; + + entrees.aux.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées AUX est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_AUXILIAIRES, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + break; // Sort de la boucle FOR + } + + } + + if (m < k) // Une erreur a été détectée dans la boucle des numéros d'entrées (le message d'erreur a été émis ci-dessus) + { + i ++; + continue; + } + + capteurs.capteurs[i].nb_entrees = m; + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_LEVEL_SENSOR : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (%i pour 1 autorisé). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée + + // Mise à jour des variables d'entrées : + if (entrees.level_sensor.nb < MAX_NOMBRE_ENTREES_LEVEL_SENSOR) + { + // Mise à jour de l'entrée correspondante : + entrees.level_sensor.entrees[entrees.level_sensor.nb].flag_validite = TRUE; + entrees.level_sensor.entrees[entrees.level_sensor.nb].module = capteurs.capteurs[i].adresse_module; + entrees.level_sensor.entrees[entrees.level_sensor.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[0] = entrees.level_sensor.nb; + capteurs.capteurs[i].nb_entrees = 1; + + entrees.level_sensor.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées LEVEL SENSOR est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_LEVEL_SENSOR, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_VOLTAGE : + { + if (k != 1) + { + sprintf(tmp, "Le nombre de paramètres 'Entrees' du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (%i pour 1 autorisé). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée + + // Mise à jour des variables d'entrées : + if (entrees.volt.nb < MAX_NOMBRE_ENTREES_VOLTAGE) + { + // Mise à jour de l'entrée correspondante : + entrees.volt.entrees[entrees.volt.nb].flag_validite = TRUE; + entrees.volt.entrees[entrees.volt.nb].module = capteurs.capteurs[i].adresse_module; + entrees.volt.entrees[entrees.volt.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[0] = entrees.volt.nb; + capteurs.capteurs[i].nb_entrees = 1; + + entrees.volt.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées VOLTAGE est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_VOLTAGE, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_OW : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (%i pour 1 autorisé). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + // val_u8 = atoi(ptr[0]); // On ne peut pas calculer ici le n° d'entre OW car elle n'est pas encore connue... On stocke la clé_primaire de TB_One_wire et on fera le lien avec le n° d'entrée à la volée. + + // Mise à jour des variables d'entrées : + if (entrees.ow.nb < MAX_NOMBRE_ENTREES_OW) + { + if (strlen(ptr[0]) < (TAILLE_ROM_CODE_OW * 3)) + { + // Mise à jour de l'entrée correspondante : + entrees.ow.entrees[entrees.ow.nb].flag_validite = TRUE; + strcpy(entrees.ow.entrees[entrees.ow.nb].rom_code, ptr[0]); + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[0] = entrees.ow.nb; + capteurs.capteurs[i].nb_entrees = 1; + + entrees.ow.nb ++; + } + else + { + sprintf(tmp, "Le champ Entrees du capteur n° %i (Cle_primaire = %i) est trop important (max = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, (TAILLE_ROM_CODE_OW * 3)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + } + else + { + sprintf(tmp, "Le nombre d'entrées OW est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_OW, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + + //.................................................................................................................... + case TYPE_ENTREES_PREDEFINIES : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (%i pour 1 autorisé). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée prédéfinie (cf liste dans .id_entrees.h')) + + // Mise à jour des variables d'entrées : + if (entrees.predefinies.nb < MAX_NOMBRE_ENTREES_PREDEFINIES) + { + // Mise à jour de l'entrée correspondante : + entrees.predefinies.entrees[entrees.predefinies.nb].flag_validite = TRUE; + entrees.predefinies.entrees[entrees.predefinies.nb].module = capteurs.capteurs[i].adresse_module; + entrees.predefinies.entrees[entrees.predefinies.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[0] = entrees.predefinies.nb; + capteurs.capteurs[i].nb_entrees = 1; + + entrees.predefinies.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées PREDEFINIES est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_PREDEFINIES, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + // case TYPE_ENTREES_IBS : + + + + //.................................................................................................................... + case TYPE_ENTREES_NMEA : + { + if (k != 1) + { + sprintf(tmp, "Le nombre de paramètres 'Entrees' du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (%i pour 1 autorisé). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + //------------------- + // PGN : + ptr_val = strtok (ptr[0],":"); // Extraction du premier segment avant le ':' + if (ptr_val == NULL) break; + val_u32 = atoi(ptr_val); + + //------------------- + // Field : + ptr_val = strtok (NULL, ":"); // Extraction du deuxieme segment après le ':' + if (ptr_val == NULL) break; + val_u8 = atoi(ptr_val); + + // Mise à jour des variables d'entrées : + if (entrees.nmea.nb < MAX_NOMBRE_ENTREES_NMEA) + { + // Mise à jour de l'entrée correspondante : + entrees.nmea.entrees[entrees.nmea.nb].flag_validite = TRUE; + entrees.nmea.entrees[entrees.nmea.nb].module = capteurs.capteurs[i].adresse_module; + entrees.nmea.entrees[entrees.nmea.nb].pgn = val_u32; + entrees.nmea.entrees[entrees.nmea.nb].field = val_u8; + + // Mise à jour du n° d'entrée du capteur : + capteurs.capteurs[i].no_entrees[0] = entrees.nmea.nb; + capteurs.capteurs[i].nb_entrees = 1; + + entrees.nmea.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées NMEA est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_NMEA, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_CAN : + { +capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; +i ++; +continue; + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_CALCULEE_BOOLEEN : + { + // liste de conditions [validite/type_condition/type_source:id_source/id_variable/test/seuil], séparées par des [;] si plusieurs conditions. + // A noter que le séparateur entre [type_source] et [id_source] n'est pas un '/' mais un ':'. Cela permet avec une simple requête de retrouver ces deux champs (ou un seul des deux) + // Avec : + // [type_condition] : 0=condition normale, >0=début de groupe de conditions avec l\'opérateur suivant : 1=Ou, 2=Et, 3=Ou Exclusif, 10=Plus, 11=Moins, 12=Multiplié, 13=Divisé, <0=fin de groupe de conditions (même valeurs que début mais en négatif) ; + // [type_source] : 0=Non défini, 1=circuit, 2=capteur, 3=réservoir, 4=batterie ; + // [id_source] : Clé primaire dans TB_[Source] ; + // [id_variable] : Clé primaire dans TB_Variables indiquant la variable concernée dans ID_Source ; + // [test] : 0=Non défini, 1=strictement inférieure au seuil, 2=inférieure ou égale au seuil, 3=égale au seuil, 4=supérieure ou égale au seuil, 5= strictement supérieure au seuil ; + // [seuil] : valeur, exprimée en unités de l\'ID_Variable. + + capteurs.capteurs[i].nb_entrees = 0; + + for (m = 0; m < k; m ++) // Nb : on a déjà vérifié que k <= MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR + { + //------------------- + // Validite : + ptr_val = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].flag_validite = atoi(ptr_val); + + //------------------- + // Type condition : + ptr_val = strtok (NULL, "/"); // Extraction du deuxieme segment après le premier '/' + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].type_condition = atoi(ptr_val); + + //------------------- + // Type source : + ptr_val = strtok (NULL, ":"); // Extraction du 3ème segment après le 2ème '/' et avant le ':'. ATTENTION : séparateur = ':' !! + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].type_source = atoi(ptr_val); + + //------------------- + // ID Source : + ptr_val = strtok (NULL, "/"); // Extraction du 4ème segment après le ':' et avant le '/' suivant + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].id_source = atoi(ptr_val); + + //------------------- + // ID Variable : + ptr_val = strtok (NULL, "/"); // Extraction du 5ème segment après le '/' et avant le '/' suivant + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].id_variable = atoi(ptr_val); + + //------------------- + // Test : + ptr_val = strtok (NULL, "/"); // Extraction du 6ème segment après le '/' et avant le '/' suivant + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].test = atoi(ptr_val); + + //------------------- + // Seuil : + ptr_val = strtok (NULL, "/"); // Extraction du 7ème segment après le dernier '/' + if (ptr_val == NULL) break; + capteurs.capteurs[i].conditions.conditions[capteurs.capteurs[i].nb_entrees].seuil = atof(ptr_val); + + // Mise à jour du nombre de conditions et test de dépassement : + capteurs.capteurs[i].nb_entrees ++; + + if (capteurs.capteurs[i].nb_entrees >= MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR) + { + sprintf(tmp, "Le nombre de conditions est trop important (max = %i). Le capteur n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR, i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].flag_validite = FALSE; // Le capteur est dévalidé car sa configuration ne sera pas exploitable... + break; // Sort de la boucle FOR + } + } + + if (m < k) // Une erreur a été détectée dans la boucle des numéros d'entrées (le message d'erreur a été émis ci-dessus) + { + sprintf(tmp, "Le capteur n° %i (clé primaire = %i) dans la Base de Données (TB_Capteurs) a un champ 'Entrees' dont la structure est incorrecte. Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + break; // Le champs 'Entrees' est systématiquement égal à "0" + } + + //.................................................................................................................... + case TYPE_ENTREES_CALCULEE_NUMERIQUE : + { +capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; +i ++; +continue; + break; + } + + //.................................................................................................................... + default : ; // Ne devrait pas arriver car testé au § 'Type_entrees'.... + } + + } + else // Si le champ "Entrees" est vide : + { + sprintf(tmp, "Le capteur n° %i (clé primaire = %i) dans la Base de Données (TB_Capteurs) a un champ 'Entrees' vide. Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + j ++; + + //--------------------------------------- + // Mode entrées : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + + switch(capteurs.capteurs[i].type_entrees) + { + //.................................................................................................................... + case TYPE_ENTREES_AUXILIAIRES : + { + if ((val_u8 != 0) && (val_u8 != 1)) + { + // Message d'erreur : + sprintf(tmp, "Le mode de fonctionnement des entrées AUX du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (valeur = %i). Le mode NORMAL (entrées Non inversées) est défini par défaut et la configuration se poursuit.", i, capteurs.capteurs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + val_u8 = 0; + } + + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) + { + entrees.aux.entrees[capteurs.capteurs[i].no_entrees[k]].logique = val_u8; // On met à jour la logique dans les entrées AUX (inutile de disposer de l'info dans le capteur) + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_LEVEL_SENSOR : + { + if ((val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_0_10V) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_500R) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_4_20MA) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_AUTO_TEST) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_MASSE) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_PLUS) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_FULL)) + { + // Message d'erreur : + sprintf(tmp, "Le mode de fonctionnement de l'entrée Level Sensor du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est incorrect (valeur = %i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + entrees.level_sensor.entrees[capteurs.capteurs[i].no_entrees[0]].flag_validite = FALSE; // L'entrée est dévalidée car non utilisable + + i ++; + continue; + } + + entrees.level_sensor.entrees[capteurs.capteurs[i].no_entrees[0]].mode = val_u8; // On met à jour le mode dans l'entre LEVEL SENSOR (inutile de disposer de l'info dans le capteur) + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_VOLTAGE : + case TYPE_ENTREES_OW : + case TYPE_ENTREES_PREDEFINIES : +// case TYPE_ENTREES_IBS : + case TYPE_ENTREES_NMEA : + case TYPE_ENTREES_CAN : + case TYPE_ENTREES_CALCULEE_BOOLEEN : + case TYPE_ENTREES_CALCULEE_NUMERIQUE : + default : // Ne devrait pas arriver car testé au § 'Type_entrees'.... + { + break; + } + } + + //--------------------------------------- + // ID variable : + //--------------------------------------- + capteurs.capteurs[i].id_variable = atoi(row[j ++]); + + // Recherche du n° de variable correspondant : + val_16 = variable_capteur_cle_primaire(capteurs.capteurs[i].id_variable); + if (val_16 >= 0) capteurs.capteurs[i].no_variable = val_16; + else + { + capteurs.capteurs[i].flag_validite = FALSE; + sprintf(tmp, "L'ID de variable du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) ne correspond à aucune variable connue (ID_Variable=%i). Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].id_variable); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + //--------------------------------------- + // Etat : + //--------------------------------------- + capteurs.capteurs[i].etat = atoi(row[j ++]); + + //--------------------------------------- + // Offset : + //--------------------------------------- + capteurs.capteurs[i].offset = atof(row[j ++]); + + //--------------------------------------- + // Gain : + //--------------------------------------- + capteurs.capteurs[i].gain = atof(row[j ++]); + if (capteurs.capteurs[i].gain == 0) + { + capteurs.capteurs[i].gain = 1; + sprintf(tmp, "Le gain du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est égal à 0, ce qui n'est pas autorisé. Le capteur est laissé valide MAIS son gain est arbitrairement fixé à %.3f.", i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].gain); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + //--------------------------------------- + // Valeur : + //--------------------------------------- + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_bdd = atof(row[j ++]); + + //--------------------------------------- + // Coeff filtrage : + //--------------------------------------- + val_u8 = atoi(row[j ++]); // Coeff filtrage + if (val_u8 < 1) + { + val_u8 = 1; // Valeur arbitraire correspondant à aucun filtrage. NB : on considère que ce n'est pas une raison pour dévalider le capteur mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le coefficent de filtrage du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est hors limite (0 MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + { + val_u8 = MODE_ENREGISTREMENT_MESURES_PERIODIQUE; // Valeur arbitraire correspondant à aucun enregistrement des mesures. NB : on considère que ce n'est pas une raison pour dévalider le capteur mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le mode d'enregistrement des mesures du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est hors limite (%i pour 0<= n <= %i). Le capteur est laissé valide MAIS son mode d'enregistrement des mesures est arbitrairement fixé à %i.", i, capteurs.capteurs[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + capteurs.capteurs[i].mode_enregistrement_mesures = val_u8; + + //--------------------------------------- + // Enregistrement historique : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_HISTORIQUE_NORMAL) + { + val_u8 = MODE_ENREGISTREMENT_HISTORIQUE_NORMAL; + sprintf(tmp, "Le mode d'enregistrement de l'historique du capteur n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est hors limite (%i pour 0<= n <= %i). Le capteur est laissé valide MAIS son mode d'enregistrement d'historique est arbitrairement fixé à %i.", i, capteurs.capteurs[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_HISTORIQUE_NORMAL, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + capteurs.capteurs[i].mode_enregistrement_historique = val_u8; + + + + + + //=================================================================================================== + // Vérification de la cohérence de la configuration du capteur : + //=================================================================================================== + flag_erreur = false; + switch (systeme.modules_config[capteurs.capteurs[i].adresse_module].type) + { + //----------------------------------------------------------------------------------------------- + case TYPE_CARTE_SHIPBASE: + { + switch(capteurs.capteurs[i].type_entrees) + { + //............................. + case TYPE_ENTREES_AUXILIAIRES : + { + // Vérification que les n° d'entrées AUX sont compatibles avec un ShipBase : + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if ((entrees.aux.entrees[capteurs.capteurs[i].no_entrees[j]].no < 1) || + (entrees.aux.entrees[capteurs.capteurs[i].no_entrees[j]].no > 32)) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.aux.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //............................. + case TYPE_ENTREES_LEVEL_SENSOR : + { + // Vérification que le ou les n° d'entrées LEVEL SENSOR sont compatibles avec un ShipBase : + // NB : a priori, une seule entrée par capteur mais permet de garder la même structure que pour les entrées AUX... + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if ((entrees.level_sensor.entrees[capteurs.capteurs[i].no_entrees[j]].no < 1) || + (entrees.level_sensor.entrees[capteurs.capteurs[i].no_entrees[j]].no > 8)) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.level_sensor.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //............................. + case TYPE_ENTREES_VOLTAGE : + { + // Vérification que le ou les n° d'entrées VOLT sont compatibles avec un ShipBase : + // NB : a priori, une seule entrée par capteur mais permet de garder la même structure que pour les entrées AUX... + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if ((entrees.volt.entrees[capteurs.capteurs[i].no_entrees[j]].no < 1) || + (entrees.volt.entrees[capteurs.capteurs[i].no_entrees[j]].no > 4)) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.volt.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //............................. + case TYPE_ENTREES_OW : + { + break; // Sera traité ultérieurement par 'enregistrement_annuaire_ow()' + } + + //............................. + case TYPE_ENTREES_PREDEFINIES : + { + // Vérification que le n° d'entrée prédéfinie est compatible avec un ShipBase : + // NB : a priori, une seule entrée par capteur mais permet de garder la même structure que pour les entrées AUX... + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if ((entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_PAT) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_HUM) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_TEMP) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_LAT) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_LON) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_IN_V) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_IN_I) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_USB_I) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_ACC_XYZ) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_ACC_X) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_ACC_Y) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_ACC_Z) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_ALT) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_SOG) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_COG) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_SIV) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_HPA) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_VPA) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_RSRP) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_RSSI) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_SINR) ) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.volt.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //............................. + case TYPE_ENTREES_IBS : + { +flag_erreur = true; + break; + } + + //............................. + case TYPE_ENTREES_NMEA : + { + break; + } + + //............................. + case TYPE_ENTREES_CAN : + { +flag_erreur = true; + break; + } + + //............................. + case TYPE_ENTREES_CALCULEE_BOOLEEN : + { + break; + } + + //............................. + case TYPE_ENTREES_CALCULEE_NUMERIQUE : + { +flag_erreur = true; + break; + } + + //............................. + default : + { + flag_erreur = true; + break; + } + + } + + break; + } + + //----------------------------------------------------------------------------------------------- + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + case TYPE_CARTE_SBS : + { + switch(capteurs.capteurs[i].type_entrees) + { + //............................. + case TYPE_ENTREES_PREDEFINIES : + { + // Vérification que le n° d'entrée prédéfinie est compatible avec ce type de modules : + // NB : a priori, une seule entrée par capteur mais permet de garder la même structure que pour les entrées AUX... + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if ((entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_TEMP) && + (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_IN_V) ) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.volt.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //.......................... + default : + { + flag_erreur = true; + break; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------- + case TYPE_CARTE_SP4AC: + { + switch(capteurs.capteurs[i].type_entrees) + { + //............................. + case TYPE_ENTREES_PREDEFINIES : + { + // Vérification que le n° d'entrée prédéfinie est compatible avec ce type de modules : + // NB : a priori, une seule entrée par capteur mais permet de garder la même structure que pour les entrées AUX... + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if (entrees.predefinies.entrees[capteurs.capteurs[i].no_entrees[j]].no != ENTREE_PREDEFINIE_TEMP) + { + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) entrees.volt.entrees[capteurs.capteurs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //.......................... + default : + { + flag_erreur = true; + break; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------- + case TYPE_CARTE_INCONNU: + default: + { + flag_erreur = true; + break; + } + } + + if (flag_erreur) + { + sprintf(tmp, "Le circuit n° %i (Cle_primaire = %i) dans TB_Capteurs contient des paramètres non compatibles avec le module auquel il est affecté. Le capteur est dévalidé.", i, capteurs.capteurs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + capteurs.capteurs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + //================================================================ + // Traitements non directement liés au retour de la requête en BDD : + //================================================================ + + + + + //------------------------------------------------ + // Mise à jour des valeurs courantes (SDIR #188) : + //------------------------------------------------ + // Initilisation des valeurs courantes à partir des valeurs de BDD (évite de repartir à 0 mais plutôt de la dernière valeur connue) : + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante = capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_bdd; + // SDIR #174 : + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].last_mesure = capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante; + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].cumul = 0; + capteurs.capteurs[i].cumuls_nb_val = 0; + +/* +// DEBUG : +printf("Capteur %i : ----------------------------------------------------------\n", i); +printf("Clé=%i, Module entrées=%i, Type d'entrées=%i, No_entrée=%i, ID_Variable=%i, No variable=%i, Offset=%.3f, Gain=%.3f, \ + Valeur=%.3f, Coeff_filtrage=%i, mesures=%i, historique=%i \n", + capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].adresse_module, capteurs.capteurs[i].type_entrees, capteurs.capteurs[i].no_entrees[0], + capteurs.capteurs[i].id_variable, capteurs.capteurs[i].no_variable, capteurs.capteurs[i].offset, + capteurs.capteurs[i].gain, capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante, capteurs.capteurs[i].valeur_coeff_filtrage, + capteurs.capteurs[i].mode_enregistrement_mesures, capteurs.capteurs[i].mode_enregistrement_historique ); +*/ +/*if (capteurs.capteurs[i].type_entrees == TYPE_ENTREES_CALCULEE_BOOLEEN) +{ + printf("Conditions du capteur calculé :\n"); + for (k = 0; k < capteurs.capteurs[i].nb_entrees; k ++) + { + printf("%i : validite=%i, type_condition=%i, type_source=%i, id_source=%i, id_variable=%i, test=%i et seuil=%.2f\n", k, + capteurs.capteurs[i].conditions.conditions[k].flag_validite, capteurs.capteurs[i].conditions.conditions[k].type_condition, + capteurs.capteurs[i].conditions.conditions[k].type_source, capteurs.capteurs[i].conditions.conditions[k].id_source, + capteurs.capteurs[i].conditions.conditions[k].id_variable, capteurs.capteurs[i].conditions.conditions[k].test, + capteurs.capteurs[i].conditions.conditions[k].seuil ); + } +}*/ + + + + i++; + } + mysql_free_result(result); //Libération du jeu de résultat + + capteurs.nb = i; + + + // SDIR #198 : + if (mode_init == BDD_MODE_INIT_RELOAD) capteurs.sequenceur.etat = ETAT_SEQUENCEUR_CAPTEURS_INIT; // Permet de s'assurer qu'on remet toutes les variables d'aplomb. Notamment si ajout/suppression de capteur car les n° sont alors reattribués et il faut que toutes les variables globales soient également reintialisées. + + + + + + +// ******************************************************************************************************************************************************** +// BATTERIES +// ******************************************************************************************************************************************************** + + // RAZ de la liste existante (utile lors d'une reconfiguration) : + for (i = 0; i < MAX_NOMBRE_BATTERIES; i ++) + { + batteries.batteries[i].flag_validite = FALSE; // Valeur par défaut + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_AUCUNE; // Valeur par défaut + } + + // Mise à jour des IDs de variables utilisées par les batteries : + sprintf(requete, "SELECT Code, Cle_primaire, Marge FROM TB_Variables WHERE Code LIKE 'Batt_%%' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Batt_temperature") == 0) { batteries.variables[BATTERIE_VARIABLE_TEMPERATURE].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_TEMPERATURE].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_tension") == 0) { batteries.variables[BATTERIE_VARIABLE_TENSION].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_TENSION].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_courant_differentiel") == 0) { batteries.variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_courant_charge") == 0) { batteries.variables[BATTERIE_VARIABLE_COURANT_CHARGE].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_COURANT_CHARGE].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_courant_decharge") == 0) { batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_COURANT_DECHARGE].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_soh") == 0) { batteries.variables[BATTERIE_VARIABLE_SOH].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_SOH].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_soc") == 0) { batteries.variables[BATTERIE_VARIABLE_SOC].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_SOC].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_autonomie") == 0) { batteries.variables[BATTERIE_VARIABLE_AUTONOMIE].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_AUTONOMIE].marge = atof(row[2]); } + else if (strcmp(row[0], "Batt_puissance") == 0) { batteries.variables[BATTERIE_VARIABLE_PUISSANCE].cle_primaire = atoi(row[1]); batteries.variables[BATTERIE_VARIABLE_PUISSANCE].marge = atof(row[2]); } + else ; + } + mysql_free_result(result); //Libération du jeu de résultat + + // Mise à 0 de Etat_systeme de toutes les batteries par défaut. + // La remise à une autre valeur sera éventuellement faite : + // - en fin de cette fonction si la configuration n'est pas cohérente et ne peut être exploitée + // - dans 'bdd_fonctions.c', dans 'enregistrement_annuaire_k_bus()', si le module auquel est affecté cette batterie n'est pas pas présent (module physique absent ou pas du bon type) + sprintf(requete, "UPDATE TB_Batteries SET Etat_systeme=0"); + mysql_query(bdd.pointeur, requete); + + // Récupération de la configuration des batteries : + i = 0; + sprintf(requete, "SELECT Cle_primaire, Adresse_module, Type_entrees, Entrees, Type, Tension_nominale, Capacite_1, Coeff_peukert, \ + Tension_pleine_charge, Courant_pleine_charge, Temporisation_pleine_charge, \ + Tension_charge_nulle, Temporisation_charge_nulle, Resistance_interne, Efficacite_charge, \ + Efficacite_decharge, Courant_repos, Nombre_cycles, Flag_increment_cycles, \ + Nombre_decharges_completes, Flag_increment_decharges_completes, \ + Temperature, Temperature_coeff_filtrage, \ + Tension, Tension_coeff_filtrage, Tension_gain, \ + Courant_differentiel, Courant_differentiel_coeff_filtrage, Courant_charge, Courant_decharge, \ + Soh, Soc, Soc_coeff_filtrage, Eod, Autonomie, \ + Liste_tension_soc, Liste_temperature_resistance_interne, Mode_enregistrement_mesures, Mode_enregistrement_historique \ + FROM TB_Batteries ORDER BY Type_entrees, Cle_primaire"); + //printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + // Vérification que le nombre de batteries n'est pas trop important : + if (i == MAX_NOMBRE_BATTERIES) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de batteries valides dans la Base de Données (TB_Batteries) est trop important (MAX = %i)...!", MAX_NOMBRE_BATTERIES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); +// return(-1); +// Traiter ce cas avec un message d'alerte user ???? +break; // On arrête l'init des batteries mais on continue l'initialisation... + } + + // Stockage des valeurs lues : + batteries.batteries[i].flag_validite = TRUE; // Valeur par défaut + + j = 0; // Permet l'insertion plus facile d'un champs par la suite..! Ne pas oublier de l'incrémenter correctement à chaque étape !! + + //--------------------------------------- + // Clé primaire : + //--------------------------------------- + batteries.batteries[i].cle_primaire = atoi(row[j ++]); // ATTENTION : dans les lignes suivantes, on alterne entre 'atoi()' et 'atof()' en fonction du type de variable. Prendre garde lors des copier/coller !! + + //--------------------------------------- + // Adresse de module : + //--------------------------------------- + val_16 = atoi(row[j ++]); // On prend en compte les batteries 'non affectées' qui arrivent avec un numéro de module = -1 (pas encore utilisé en 06/2026) + + if ((val_16 < 0) || (val_16 >= MAX_NOMBRE_MODULES)) + { + // Message d'erreur : + if (val_16 < 0) + { + sprintf(tmp, "La batterie n° %i (Cle_primaire = %i) dans TB_Batteries est Non Affectée (Adresse_module = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, val_16); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le numéro de module de la batterie n° %i (Cle_primaire = %i) dans TB_Batteries est trop élevé (%i, MAX = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, val_16, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + else batteries.batteries[i].adresse_module = val_16; + + //--------------------------------------- + // Type d'entrées : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + switch(val_u8) + { + case TYPE_ENTREES_VOLTAGE : + case TYPE_ENTREES_PREDEFINIES : + case TYPE_ENTREES_IBS : + { + batteries.batteries[i].type_entrees = val_u8; + break; + } + default : + { + sprintf(tmp, "La batterie n° %i (clé primaire = %i) dans TB_Batteries a une configuration de 'Type_entrees' invalide (valeur = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + } + + //--------------------------------------- + // Entrées : + //--------------------------------------- + if (row[j][0] != 0) // Si le champ "Entrees" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + // Check du nombre d'entrées trouvées : + if (k > MAX_NOMBRE_ENTREES_PAR_BATTERIE) + { + // Message d'erreur : + sprintf(tmp, "Le nombre d'entrées de la batterie n° %i (Cle_primaire = %i) dans TB_Batteries est trop important (MAX = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, MAX_NOMBRE_ENTREES_PAR_BATTERIE); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + // Traitement des entrées trouvées selon la valeur de 'type_entrees' : + switch(batteries.batteries[i].type_entrees) + { + //.................................................................................................................... + case TYPE_ENTREES_VOLTAGE : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' de la batterie n° %i (Cle_primaire = %i) dans TB_Batteries est incorrect (%i pour 1 autorisé). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée + + // Mise à jour des variables d'entrées : + if (entrees.volt.nb < MAX_NOMBRE_ENTREES_VOLTAGE) // Si le nombre d'entrées Voltage n'est pas déjà au maxi... + { + // Mise à jour de l'entrée correspondante : + entrees.volt.entrees[entrees.volt.nb].flag_validite = TRUE; + entrees.volt.entrees[entrees.volt.nb].module = batteries.batteries[i].adresse_module; + entrees.volt.entrees[entrees.volt.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + batteries.batteries[i].no_entrees = entrees.volt.nb; + + entrees.volt.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées VOLTAGE est trop important (max = %i). La batterie n° %i (Cle_primaire = %i) est dévalidée.", MAX_NOMBRE_ENTREES_VOLTAGE, i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_PREDEFINIES : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' de la batterie n° %i (Cle_primaire = %i) dans TB_Batteries est incorrect (%i pour 1 autorisé). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée + + // Mise à jour des variables d'entrées : + if (entrees.predefinies.nb < MAX_NOMBRE_ENTREES_PREDEFINIES) + { + // Mise à jour de l'entrée correspondante : + entrees.predefinies.entrees[entrees.predefinies.nb].flag_validite = TRUE; + entrees.predefinies.entrees[entrees.predefinies.nb].module = batteries.batteries[i].adresse_module; + entrees.predefinies.entrees[entrees.predefinies.nb].no = val_u8; + + // Mise à jour du n° d'entrée du capteur : + batteries.batteries[i].no_entrees = entrees.predefinies.nb; + + entrees.predefinies.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées PREDEFINIES est trop important (max = %i). La batterie n° %i (Cle_primaire = %i) est dévalidée.", MAX_NOMBRE_ENTREES_PREDEFINIES, i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_IBS : + { + break; // Le champ Entrees n'est pas utilisé avec ce type_entrees (on n'utilise que 'adresse_module') + } + + + //.................................................................................................................... + default : ; // Ne devrait pas arriver car testé au § 'Type_entrees'.... + } + } + else // Si le champ "Entrees" est vide : + { + sprintf(tmp, "La batterie n° %i (clé primaire = %i) dans la Base de Données (TB_Batteries) a un champ 'Entrees' vide. La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + j ++; + + //--------------------------------------- + // Type : + //--------------------------------------- + batteries.batteries[i].type = atoi(row[j ++]); + + //--------------------------------------- + // Tension nominale : + //--------------------------------------- + batteries.batteries[i].tension_nominale = atoi(row[j ++]); + + //--------------------------------------- + // Capacité_1 : + //--------------------------------------- + flag_erreur = false; + if (row[j][0] != 0) // Si le champ "Capacité_1" n'est pas vide : + { + // Récupération de la liste : + k = 0; + ptr[k] = strtok (row[j],"/"); // Extraction du premier segment avant le premier '/' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, "/"); // Extraction du segment suivant le prochain '/' + } + + if (k == 2) // Format valide : + { + batteries.batteries[i].capacite = atof(ptr[0]); + batteries.batteries[i].heures_capacite = atof(ptr[1]); + if ((batteries.batteries[i].capacite <= 0) || (batteries.batteries[i].heures_capacite <= 0)) flag_erreur = true; + } + else flag_erreur = true; // Si le format est invalide + } + else flag_erreur = true; + j ++; + + if (flag_erreur) + { + batteries.batteries[i].capacite = 0; + batteries.batteries[i].heures_capacite = 0; + } + if (batteries.batteries[i].capacite == 0) //On ne peut pas fonctionner avec une capacité nulle... + { + batteries.batteries[i].capacite = 100; + sprintf(tmp, "La capacité de la batterie n° %i (clé primaire = %i) dans la Base de Données (TB_Batteries) a une valeur nulle, ce qui n'est pas acceptable pour fonctionner. La valeur est arbitrairement fixée à %.2fAh.", i, batteries.batteries[i].cle_primaire, batteries.batteries[i].capacite); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + if (batteries.batteries[i].heures_capacite == 0) //On ne peut pas fonctionner avec une durée de capacité nulle... + { + batteries.batteries[i].heures_capacite = 20; + sprintf(tmp, "La durée de capacité de la batterie n° %i (clé primaire = %i) dans la Base de Données (TB_Batteries) a une valeur nulle, ce qui n'est pas acceptable pour fonctionner. La valeur est arbitrairement fixée à %.2fh.", i, batteries.batteries[i].cle_primaire, batteries.batteries[i].heures_capacite); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + //--------------------------------------- + // Coeff de Peukert : + //--------------------------------------- + batteries.batteries[i].coeff_peukert = atof(row[j ++]); + + //--------------------------------------- + // Tension pleine charge : + //--------------------------------------- + batteries.batteries[i].tension_pleine_charge = atof(row[j ++]); + + //--------------------------------------- + // Courant_pleine_charge : + //--------------------------------------- + batteries.batteries[i].courant_pleine_charge = atof(row[j ++]); + + //--------------------------------------- + // Tempo pleine charge : + //--------------------------------------- + batteries.batteries[i].temporisation_pleine_charge = atoi(row[j ++]); + + //--------------------------------------- + // Tension charge nulle : + //--------------------------------------- + batteries.batteries[i].tension_charge_nulle = atof(row[j ++]); + + //--------------------------------------- + // Tempo charge nulle : + //--------------------------------------- + batteries.batteries[i].temporisation_charge_nulle = atoi(row[j ++]); + + //--------------------------------------- + // Résistance interne : + //--------------------------------------- + batteries.batteries[i].resistance_interne = atof(row[j ++]); + + //--------------------------------------- + // Efficacité charge : + //--------------------------------------- + batteries.batteries[i].efficacite_charge = atof(row[j ++]); + if (batteries.batteries[i].efficacite_charge == 0) //On ne peut pas fonctionner avec une efficacité nulle... + { + batteries.batteries[i].efficacite_charge = 1; + sprintf(tmp, "L'éfficacité de charge de la batterie n° %i (clé primaire = %i) dans la Base de Données (TB_Batteries) a une valeur nulle, ce qui n'est pas acceptable pour fonctionner. La valeur est arbitrairement fixée à %.2f Ah.", i, batteries.batteries[i].cle_primaire, batteries.batteries[i].efficacite_charge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + //--------------------------------------- + // Efficacité décharge : + //--------------------------------------- + batteries.batteries[i].efficacite_decharge = atof(row[j ++]); + if (batteries.batteries[i].efficacite_decharge == 0) //On ne peut pas fonctionner avec une efficacité nulle... + { + batteries.batteries[i].efficacite_decharge = 1; + sprintf(tmp, "L'éfficacité de charge de la batterie n° %i (clé primaire = %i) dans la Base de Données (TB_Batteries) a une valeur nulle, ce qui n'est pas acceptable pour fonctionner. La valeur est arbitrairement fixée à %.2f Ah.", i, batteries.batteries[i].cle_primaire, batteries.batteries[i].efficacite_decharge); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + //--------------------------------------- + // Courant de repos : + //--------------------------------------- + batteries.batteries[i].courant_repos = atof(row[j ++]); + + //--------------------------------------- + // Nombre de cycles : + //--------------------------------------- + batteries.batteries[i].nombre_cycles = atoi(row[j ++]); + + //--------------------------------------- + // Flag incrément cycles : + //--------------------------------------- + batteries.batteries[i].flag_increment_cycles = atoi(row[j ++]); + + //--------------------------------------- + // Nombre décharges complètes : + //--------------------------------------- + batteries.batteries[i].nombre_decharges_completes = atoi(row[j ++]); + + //--------------------------------------- + // Flag incrément décharges complètes : + //--------------------------------------- + batteries.batteries[i].flag_increment_decharges_completes = atoi(row[j ++]); + + //--------------------------------------- + // Température : + //--------------------------------------- + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd = atof(row[j ++]); + + val_u8 = atoi(row[j ++]); // Coeff filtrage de température + if (val_u8 < 1) + { + val_u8 = 1; // Valeur arbitraire correspondant à aucun filtrage. NB : on considère que ce n'est pas une raison pour dévalider le réservoir mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le coefficent de filtrage de la température de la batterie n° %i (Cle_primaire = %i) dans la Base de Données (TB_Batteries) est hors limite (0 MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de points dans la liste Tension/SOC de la batterie n° %i (Cle_primaire = %i) dans la Base de Données (TB_Batteries) est trop important (MAX = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + for (m = 0; m < k; m ++) + { + ptr_val = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + ptr_niv = strtok (NULL, "/"); // Extraction du second segment après le '/' + + if ((ptr_val != NULL) && (ptr_niv != NULL)) + { + batteries.batteries[i].points_tension_soc[m].valeur = atof(ptr_val); + batteries.batteries[i].points_tension_soc[m].niveau = atof(ptr_niv); + + if (batteries.batteries[i].points_tension_soc[m].niveau <= 100) batteries.batteries[i].points_tension_soc[m].flag_validite = TRUE; + } + } + + if (tri_liste_valeurs_niveaux(batteries.batteries[i].points_tension_soc, MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE) == -1) + { + sprintf(tmp, "Le tri de la liste Valeurs/Niveaux de la batterie n° %i (Cle_primaire = %i) ne s'est pas effectué normalement. Le démarrage se fait avec la liste en l'état mais peu provoquer des dysfonctionnements.", i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + j ++; + + //--------------------------------------- + // Points Température/Résistance_interne : + //--------------------------------------- + // RAZ de la liste existante (utile lors d'une reconfiguration) : + for (m = 0; m < MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE; m ++) batteries.batteries[i].points_temperature_resistance_interne[m].flag_validite = FALSE; + + if (row[j][0] != 0) // Si le champ "Liste_temperature_resistance_interne" n'est pas vide : + { + // Récupération de la nouvelle liste : + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + if (k > MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de points dans la liste Température/Résistance_interne de la batterie n° %i (Cle_primaire = %i) dans la Base de Données (TB_Batteries) est trop important (MAX = %i). La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire, MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + for (m = 0; m < k; m ++) + { + ptr_val = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + ptr_niv = strtok (NULL, "/"); // Extraction du second segment après le '/' + + if ((ptr_val != NULL) && (ptr_niv != NULL)) + { + batteries.batteries[i].points_temperature_resistance_interne[m].valeur = atof(ptr_val); + batteries.batteries[i].points_temperature_resistance_interne[m].niveau = atof(ptr_niv); + + batteries.batteries[i].points_temperature_resistance_interne[m].flag_validite = TRUE; + } + } + + if (tri_liste_valeurs_niveaux(batteries.batteries[i].points_temperature_resistance_interne, MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE) == -1) + { + sprintf(tmp, "Le tri de la liste Température/Résistance_interne de la batterie n° %i (Cle_primaire = %i) ne s'est pas effectué normalement. Le démarrage se fait avec la liste en l'état mais peu provoquer des dysfonctionnements.", i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + j ++; + + //--------------------------------------- + // Enregistrement mesures : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + { + val_u8 = MODE_ENREGISTREMENT_MESURES_PERIODIQUE; // Valeur arbitraire correspondant à aucun enregistrement des mesures. NB : on considère que ce n'est pas une raison pour dévalider le capteur mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le mode d'enregistrement des mesures de la batterie n° %i (Cle_primaire = %i) dans la Base de Données (TB_Batteries) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement des mesures est arbitrairement fixé à %i.", i, batteries.batteries[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + batteries.batteries[i].mode_enregistrement_mesures = val_u8; + + //--------------------------------------- + // Enregistrement historique : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_HISTORIQUE_NORMAL) + { + val_u8 = MODE_ENREGISTREMENT_HISTORIQUE_NORMAL; + sprintf(tmp, "Le mode d'enregistrement de l'historique de la batterie n° %i (Cle_primaire = %i) dans la Base de Données (TB_Batteries) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement d'historique est arbitrairement fixé à %i.", i, batteries.batteries[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_HISTORIQUE_NORMAL, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + batteries.batteries[i].mode_enregistrement_historique = val_u8; + + + //=================================================================================================== + // Vérification de la cohérence de la configuration de la batterie : + //=================================================================================================== + flag_erreur = false; + switch (systeme.modules_config[batteries.batteries[i].adresse_module].type) + { + //----------------------- + case TYPE_CARTE_SHIPBASE: + { + switch(batteries.batteries[i].type_entrees) + { + case TYPE_ENTREES_VOLTAGE : + { + // Vérification que le n° d'entrée VOLT est compatible avec un ShipBase : + if ((entrees.volt.entrees[batteries.batteries[i].no_entrees].no < 1) || + (entrees.volt.entrees[batteries.batteries[i].no_entrees].no > 4)) flag_erreur = true; + break; + } + + case TYPE_ENTREES_PREDEFINIES : + { + // Vérification que le n° d'entrée prédéfinie est compatible avec un ShipBase : + if (entrees.predefinies.entrees[batteries.batteries[i].no_entrees].no != ENTREE_PREDEFINIE_IN_V) flag_erreur = true; + break; + } + + case TYPE_ENTREES_IBS : + default : + { + flag_erreur = true; + break; + } + } + + break; + } + + //----------------------- + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + { + switch(batteries.batteries[i].type_entrees) + { + case TYPE_ENTREES_PREDEFINIES : + { + // Vérification que le n° d'entrée prédéfinie est compatible avec un ShipPowerxDC : + if (entrees.predefinies.entrees[batteries.batteries[i].no_entrees].no != ENTREE_PREDEFINIE_IN_V) flag_erreur = true; + break; + } + + case TYPE_ENTREES_VOLTAGE : + case TYPE_ENTREES_IBS : + default : + { + flag_erreur = true; + break; + } + } + + break; + } + + //----------------------- + case TYPE_CARTE_SBS: + { + switch(batteries.batteries[i].type_entrees) + { + case TYPE_ENTREES_IBS : break; + + case TYPE_ENTREES_PREDEFINIES : + case TYPE_ENTREES_VOLTAGE : + default : + { + flag_erreur = true; + break; + } + } + break; + } + + //----------------------- + case TYPE_CARTE_SP4AC: + case TYPE_CARTE_INCONNU: + default: + { + flag_erreur = true; + break; + } + } + + if (flag_erreur) + { + sprintf(tmp, "La batterie n° %i (Cle_primaire = %i) dans TB_Batteries contient des paramètres non compatibles avec le module auquel elle est affectée. La batterie est dévalidée.", i, batteries.batteries[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + batteries.batteries[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + //================================================================ + // Traitements non directement liés au retour de la requête en BDD : + //================================================================ + + // Puissance : + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante = 0; // Valeur par défaut + + + + //------------------------------------------------ + // Mise à jour des valeurs courantes (SDIR #188) : + //------------------------------------------------ + // Initilisation des valeurs courantes à partir des valeurs de BDD (évite de repartir à 0 mais plutôt de la dernière valeur connue) : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_bdd; + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante = batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_bdd; + // SDIR #174 : + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_CHARGE].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DECHARGE].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOC].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_AUTONOMIE].cumul = 0; + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].last_mesure = batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].valeur_courante; + batteries.batteries[i].variables[BATTERIE_VARIABLE_PUISSANCE].cumul = 0; + batteries.batteries[i].cumuls_nb_val = 0; + +// DEBUG : +/* printf("Batterie %i : ----------------------------------------------------------\n", i); + printf("Clé=%i, Adresse_module=%i, Type d'entrées=%i, No_Entrée=%i, Capacite=%.2fAh, Tension_gain=%.3f, Soh=%.2f %%, mesures=%i, historique=%i \n", + batteries.batteries[i].cle_primaire, batteries.batteries[i].adresse_module, batteries.batteries[i].type_entrees, batteries.batteries[i].no_entrees, + batteries.batteries[i].capacite, batteries.batteries[i].tension_gain, + batteries.batteries[i].variables[BATTERIE_VARIABLE_SOH].valeur_bdd, + batteries.batteries[i].mode_enregistrement_mesures, batteries.batteries[i].mode_enregistrement_historique ); +*/ +/*printf("Valeurs/Niveaux :"); +for (j=0; j= MAX_NOMBRE_MODULES)) + { + // Message d'erreur : + if (val_16 < 0) + { + sprintf(tmp, "Le réservoir n° %i (Cle_primaire = %i) dans TB_Reservoirs est Non Affecté (Adresse_module = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, val_16); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le numéro de module du réservoir n° %i (Cle_primaire = %i) dans TB_Circuits est trop élevé (%i, MAX = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, val_16, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + else reservoirs.reservoirs[i].adresse_module = val_16; + + //--------------------------------------- + // Type d'entrées : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + switch(val_u8) + { + case TYPE_ENTREES_AUXILIAIRES : + case TYPE_ENTREES_LEVEL_SENSOR : + { + reservoirs.reservoirs[i].type_entrees = val_u8; + break; + } + default : + { + sprintf(tmp, "Le réservoir n° %i (clé primaire = %i) dans la Base de Données (TB_Reservoirs) a une configuration de 'Type_entrees' invalide (valeur = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + } + + //--------------------------------------- + // Entrées : + //--------------------------------------- + if (row[j][0] != 0) // Si le champ "Entrees" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + // Check du nombre d'entrées trouvées : + if (k > MAX_NOMBRE_ENTREES_PAR_RESERVOIR) + { + // Message d'erreur : + sprintf(tmp, "Le nombre d'entrées du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est trop important (MAX = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, MAX_NOMBRE_ENTREES_PAR_RESERVOIR); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + // Traitement des entrées trouvées selon la valeur de 'type_entrees' : + switch(reservoirs.reservoirs[i].type_entrees) + { + //.................................................................................................................... + case TYPE_ENTREES_AUXILIAIRES : + { + for (m = 0; m < k; m ++) // Nb : on a déjà vérifié que k < MAX_NOMBRE_ENTREES_PAR_CAPTEUR + { + val_u8 = atoi(ptr[m]); + + // Mise à jour des variables d'entrées : + if (entrees.aux.nb < MAX_NOMBRE_ENTREES_AUXILIAIRES) + { + // Mise à jour de l'entrée correspondante : + entrees.aux.entrees[entrees.aux.nb].flag_validite = TRUE; + entrees.aux.entrees[entrees.aux.nb].module = reservoirs.reservoirs[i].adresse_module; + entrees.aux.entrees[entrees.aux.nb].no = val_u8; + + // Mise à jour du n° d'entrée du réservoir : + reservoirs.reservoirs[i].no_entrees[m] = entrees.aux.nb; + + entrees.aux.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées AUX est trop important (max = %i). Le réservoir n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_AUXILIAIRES, i, reservoirs.reservoirs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + break; // Sort de la boucle FOR + } + } + + if (m < k) // Une erreur a été détectée dans la boucle des numéros d'entrées (le message d'erreur a été émis ci-dessus) + { + i ++; + continue; + } + + reservoirs.reservoirs[i].nb_entrees = m; + + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_LEVEL_SENSOR : + { + if (k != 1) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de paramètres 'Entrees' du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est incorrect (%i pour 1 autorisé). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, k); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + val_u8 = atoi(ptr[0]); // N° de l'entrée + + // Mise à jour des variables d'entrées : + if (entrees.level_sensor.nb < MAX_NOMBRE_ENTREES_LEVEL_SENSOR) + { + // Mise à jour de l'entrée correspondante : + entrees.level_sensor.entrees[entrees.level_sensor.nb].flag_validite = TRUE; + entrees.level_sensor.entrees[entrees.level_sensor.nb].module = reservoirs.reservoirs[i].adresse_module; + entrees.level_sensor.entrees[entrees.level_sensor.nb].no = val_u8; + + // Mise à jour du n° d'entrée du réservoir : + reservoirs.reservoirs[i].no_entrees[0] = entrees.level_sensor.nb; + reservoirs.reservoirs[i].nb_entrees = 1; + + entrees.level_sensor.nb ++; + } + else + { + sprintf(tmp, "Le nombre d'entrées LEVEL SENSOR est trop important (max = %i). Le réservoir n° %i (Cle_primaire = %i) est dévalidé.", MAX_NOMBRE_ENTREES_LEVEL_SENSOR, i, reservoirs.reservoirs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + break; + } + + //.................................................................................................................... + default : ; // Ne devrait pas arriver car testé au § 'Type_entrees'.... + } + } + else // Si le champ "Entrees" est vide : + { + sprintf(tmp, "Le réservoir n° %i (clé primaire = %i) dans la Base de Données (TB_Reservoirs) a un champ 'Entrees' vide. Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + j ++; + + //--------------------------------------- + // Mode entrées : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + + switch(reservoirs.reservoirs[i].type_entrees) + { + //.................................................................................................................... + case TYPE_ENTREES_AUXILIAIRES : + { + if ((val_u8 != 0) && (val_u8 != 1)) + { + // Message d'erreur : + sprintf(tmp, "Le mode de fonctionnement des entrées AUX du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est incorrect (valeur = %i). Le mode NORMAL (entrées Non inversées) est défini par défaut et la configuration se poursuit.", i, reservoirs.reservoirs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + val_u8 = 0; + } + + for (k = 0; k < reservoirs.reservoirs[i].nb_entrees; k ++) + { + entrees.aux.entrees[reservoirs.reservoirs[i].no_entrees[k]].logique = val_u8; // On met à jour la logique dans les entrées AUX (inutile de disposer de l'info dans le réservoir) + } + break; + } + + //.................................................................................................................... + case TYPE_ENTREES_LEVEL_SENSOR : + { + if ((val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_0_10V) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_500R) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_4_20MA) && + (val_u8 != ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_AUTO_TEST) ) // SDIR #319 + { + // Message d'erreur : + sprintf(tmp, "Le mode de fonctionnement de l'entrée Level Sensor du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est incorrect (valeur = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + entrees.level_sensor.entrees[reservoirs.reservoirs[i].no_entrees[0]].flag_validite = FALSE; // L'entrée est dévalidée car non utilisable + + i ++; + continue; + } + + entrees.level_sensor.entrees[reservoirs.reservoirs[i].no_entrees[0]].mode = val_u8; // On met à jour le mode dans l'entre LEVEL SENSOR (inutile de disposer de l'info dans le capteur) + + break; + } + + //.................................................................................................................... + default : // Pour les autres Type_entrees, le champ Mode_entrees n'est pas utilisé... + { + break; + } + } + + //--------------------------------------- + // Etat_entrees : + //--------------------------------------- + reservoirs.reservoirs[i].etat_entrees = atoi(row[j ++]); + + //--------------------------------------- + // Liste Valeurs/Niveaux : + //--------------------------------------- + // RAZ de la liste existante (utile lors d'une reconfiguration) : + for (m = 0; m < MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS; m ++) reservoirs.reservoirs[i].liste_valeurs_niveaux[m].flag_validite = FALSE; + + if (row[j][0] != 0) // Si le champ "Liste_valeurs_niveaux" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + if (k > MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de points dans la liste Valeurs/Niveaux du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est trop important (MAX = %i). Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire, MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + continue; + } + + for (m = 0; m < k; m ++) + { + ptr_val = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + ptr_niv = strtok (NULL, "/"); // Extraction du second segment après le '/' + + if ((ptr_val != NULL) && (ptr_niv != NULL)) + { + reservoirs.reservoirs[i].liste_valeurs_niveaux[m].valeur = atof(ptr_val); + reservoirs.reservoirs[i].liste_valeurs_niveaux[m].niveau = atof(ptr_niv); + + if (reservoirs.reservoirs[i].liste_valeurs_niveaux[m].niveau <= 100000) reservoirs.reservoirs[i].liste_valeurs_niveaux[m].flag_validite = TRUE; + } + } + + if (tri_liste_valeurs_niveaux(reservoirs.reservoirs[i].liste_valeurs_niveaux, MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS) == -1) + { + sprintf(tmp, "Le tri de la liste Valeurs/Niveaux du réservoir n° %i (Cle_primaire = %i) ne s'est pas effectué normalement. Le démarrage se fait avec la liste en l'état mais peu provoquer des dysfonctionnements.", i, reservoirs.reservoirs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + j ++; + + //--------------------------------------- + // Niveau : + //--------------------------------------- + reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].valeur_bdd = atof(row[j ++]); + + //--------------------------------------- + // Niveau Coeff filtrage : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 < 1) + { + val_u8 = 1; // Valeur arbitraire correspondant à aucun filtrage. NB : on considère que ce n'est pas une raison pour dévalider le réservoir mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le coefficent de filtrage du réservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Capteurs) est hors limite (0 MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + { + val_u8 = MODE_ENREGISTREMENT_MESURES_PERIODIQUE; // Valeur arbitraire correspondant à aucun enregistrement des mesures. NB : on considère que ce n'est pas une raison pour dévalider le capteur mais on pourrait tout aussi bien le faire ici... + sprintf(tmp, "Le mode d'enregistrement des mesures du reservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Reservoirs) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement des mesures est arbitrairement fixé à %i.", i, reservoirs.reservoirs[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + reservoirs.reservoirs[i].mode_enregistrement_mesures = val_u8; + + //--------------------------------------- + // Enregistrement historique : + //--------------------------------------- + val_u8 = atoi(row[j ++]); + if (val_u8 > MODE_ENREGISTREMENT_HISTORIQUE_NORMAL) + { + val_u8 = MODE_ENREGISTREMENT_HISTORIQUE_NORMAL; + sprintf(tmp, "Le mode d'enregistrement de l'historique du reservoir n° %i (Cle_primaire = %i) dans la Base de Données (TB_Circuits) est hors limite (%i pour 0<= n <= %i). Le circuit est laissé valide MAIS son mode d'enregistrement d'historique est arbitrairement fixé à %i.", i, reservoirs.reservoirs[i].cle_primaire, val_u8, MODE_ENREGISTREMENT_HISTORIQUE_NORMAL, val_u8); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + reservoirs.reservoirs[i].mode_enregistrement_historique = val_u8; + + + + + + + + + + + //=================================================================================================== + // Vérification de la cohérence de la configuration de la batterie : + //=================================================================================================== + flag_erreur = false; + switch (systeme.modules_config[reservoirs.reservoirs[i].adresse_module].type) + { + //----------------------- + case TYPE_CARTE_SHIPBASE: + { + switch(reservoirs.reservoirs[i].type_entrees) + { + //----------------------------- + case TYPE_ENTREES_AUXILIAIRES : + { + // Vérification que les n° d'entrées AUX sont compatibles avec un ShipBase : + for (j = 0; j < reservoirs.reservoirs[i].nb_entrees; j ++) + { + if ((entrees.aux.entrees[reservoirs.reservoirs[i].no_entrees[j]].no < 1) || + (entrees.aux.entrees[reservoirs.reservoirs[i].no_entrees[j]].no > 32)) + { + for (k = 0; k < reservoirs.reservoirs[i].nb_entrees; k ++) entrees.aux.entrees[reservoirs.reservoirs[i].no_entrees[k]].flag_validite = false; // Dévalidation des entrées qui ne seront plus utiles.... + flag_erreur = true; + break; + } + } + + break; + } + + //------------------------------ + case TYPE_ENTREES_LEVEL_SENSOR : + { + j = reservoirs.reservoirs[i].no_entrees[0]; // Simplifie le traitement et la lecture + + if ((entrees.level_sensor.entrees[j].no < 1) || + (entrees.level_sensor.entrees[j].no > 8)) + { + entrees.level_sensor.entrees[j].flag_validite = false; // Si le réservoir n'est pas opérationnel, inutile de traiter l'entrée correspondante. + flag_erreur = true; + } + + break; + } + + //------------------------------ + default : + { + flag_erreur = true; + break; + } + } + + break; + } + + //----------------------- + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + case TYPE_CARTE_SBS: + case TYPE_CARTE_SP4AC: + case TYPE_CARTE_INCONNU: + default: + { + flag_erreur = true; + break; + } + } + + if (flag_erreur) + { + sprintf(tmp, "Le réservoir n° %i (Cle_primaire = %i) dans TB_Reservoirs contient des paramètres non compatibles avec le module auquel il est affecté. Le réservoir est dévalidé.", i, reservoirs.reservoirs[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reservoirs.reservoirs[i].etat_systeme = ERREUR_SYSTEME_CONFIG; + i ++; + continue; + } + + //================================================================ + // Traitements non directement liés au retour de la requête en BDD : + //================================================================ + + //------------------------------------------------ + // Mise à jour des valeurs courantes (SDIR #188) : + //------------------------------------------------ + // Initilisation des valeurs courantes à partir des valeurs de BDD (évite de repartir à 0 mais plutôt de la dernière valeur connue) : + reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].valeur_courante = reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].valeur_bdd; + // SDIR #174 : + reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].last_mesure = reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].valeur_courante; + reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].cumul = 0; + reservoirs.reservoirs[i].cumuls_nb_val = 0; + + +/*printf("Réservoir %i : ----------------------------------------------------------\n", i); +printf("Clé=%i, Type d'entrées=%i, No d'entrée 1=%i, No d'entrée 2=%i, No d'entrée 3=%i, No d'entrée 4=%i, Niveau=%.2f, Niveau_coeff_filtrage=%i, Niveau_seuil_Haut=%.3f, Niveau_seuil_Bas=%.3f, \ + Logique=%i, mesures=%i, historique=%i \n", + reservoirs.reservoirs[i].cle_primaire, reservoirs.reservoirs[i].type_entrees, reservoirs.reservoirs[i].no_entrees[0], reservoirs.reservoirs[i].no_entrees[1], reservoirs.reservoirs[i].no_entrees[2], + reservoirs.reservoirs[i].no_entrees[3], reservoirs.reservoirs[i].variables[RESERVOIR_VARIABLE_NIVEAU].valeur_courante, + reservoirs.reservoirs[i].niveau_coeff_filtrage, reservoirs.reservoirs[i].niveau_seuil_haut, reservoirs.reservoirs[i].niveau_seuil_bas, + reservoirs.reservoirs[i].logique, reservoirs.reservoirs[i].mode_enregistrement_mesures, reservoirs.reservoirs[i].mode_enregistrement_historique ); +printf("Valeurs/Niveaux :"); +for (j=0; j EVENTS_TYPE_TEST_STRICT_SUPERIEUR)) events.events[i].condition_test.flag_validite = FALSE; + + //--------------------------------------- + // Seuil : + //--------------------------------------- + events.events[i].condition_test.seuil = atof(row[j ++]); + + //--------------------------------------- + // Tempo : + //--------------------------------------- + events.events[i].tempo = atoi(row[j ++]); + + //--------------------------------------- + // Conditions horaires : + //--------------------------------------- + if (row[j][0] != 0) // Si le champ "Conditions_horaires" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + // Check du nombre de conditions trouvées : + if (k > MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT) + { + // Message d'erreur : + sprintf(tmp, "Le nombre de conditions horaires de l'évènement n° %i (Cle_primaire = %i) dans la Base de Données (TB_Events) est trop important (MAX = %i). Les conditions supérieures à %i sont ignorées.", i, events.events[i].cle_primaire, MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT, MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + k = MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT; // Limite du nombre de conditions au maxi autorisé + } + + // Traitement des conditions : + char * ptr_jour, *ptr_heure, *ptr_minute; + for (m = 0; m < k; m ++) // Nb : on a déjà vérifié que k <= MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT + { + ptr_jour = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + ptr_heure = strtok (NULL, "/"); // Extraction du deuxieme segment après le premier '/' + ptr_minute = strtok (NULL, "/"); // Extraction du troisème segment après le second '/' + + // Extraction des données de la condition et vérification de leurs validité : + if ((ptr_jour != NULL) && (ptr_heure != NULL) && (ptr_minute != NULL)) + { + events.events[i].conditions_horaires.conditions[m].jour = atoi(ptr_jour); + events.events[i].conditions_horaires.conditions[m].heure = atoi(ptr_heure); + events.events[i].conditions_horaires.conditions[m].minute = atoi(ptr_minute); + + if ((events.events[i].conditions_horaires.conditions[m].jour >= JOUR_SEMAINE_LUNDI) && + (events.events[i].conditions_horaires.conditions[m].jour <= JOUR_SEMAINE_TOUS_LES_JOURS) && + (events.events[i].conditions_horaires.conditions[m].heure <= (24 + HEURE_SPECIALE_DEBUT_NUIT)) && + (events.events[i].conditions_horaires.conditions[m].minute <= 59) ) + { + events.events[i].conditions_horaires.conditions[m].flag_validite = TRUE; + events.events[i].conditions_horaires.nb ++; + } + } + + // Message d'info si la condition n'est pas valide : + if (!events.events[i].conditions_horaires.conditions[m].flag_validite) + { + sprintf(tmp, "La condition horaire #%i de l'évènement n° %i (Cle_primaire = %i) dans la Base de Données (TB_Events) a une structure invalide, elle est dévalidé.", m, i, events.events[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + } + else ; // Si le champ "Conditions_horaires" est vide : le champ 'events.events[i].nb_conditions_horaires' a été mis à 0 par défaut à l'init. + j ++; + + //--------------------------------------- + // Alarme_son : + //--------------------------------------- + events.events[i].alarme_son = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_led : + //--------------------------------------- + events.events[i].alarme_led = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_soutien : + //--------------------------------------- + events.events[i].alarme_soutien = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_source : + //--------------------------------------- + events.events[i].alarme_source = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_message : + //--------------------------------------- + events.events[i].alarme_message = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_sms : + //--------------------------------------- + events.events[i].alarme_sms = atoi(row[j ++]); + + //--------------------------------------- + // Alarme_email : + //--------------------------------------- + events.events[i].alarme_email = atoi(row[j ++]); + + //--------------------------------------- + // Action circuits : + //--------------------------------------- + if (row[j][0] != 0) // Si le champ "Action_circuits" n'est pas vide : + { + k = 0; + ptr[k] = strtok (row[j],";"); // Extraction du premier segment avant le premier ';' + while ((ptr[k] != NULL) && (k < MAX_NOMBRE_CHAMPS_INTERNES)) + { + k ++; + ptr[k] = strtok (NULL, ";"); // Extraction du segment suivant le prochain ';' + } + + // Check du nombre d'actions trouvées : + if (k > MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT) + { + // Message d'erreur : + sprintf(tmp, "Le nombre d'action sur circuits de l'évènement n° %i (Cle_primaire = %i) dans la Base de Données (TB_Events) est trop important (MAX = %i). Les actions supérieures à %i sont ignorées.", i, events.events[i].cle_primaire, MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT, MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + k = MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT; // Limite du nombre d'actions au maxi autorisé + } + + // Traitement des actions : + char * ptr_circuit, *ptr_action; + for (m = 0; m < k; m ++) // Nb : on a déjà vérifié que k <= MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT + { + ptr_circuit = strtok (ptr[m],"/"); // Extraction du premier segment avant le '/' + ptr_action = strtok (NULL, "/"); // Extraction du deuxieme segment, après le '/' + + // Extraction des données de l'action et vérification de leurs validité : + if ((ptr_circuit != NULL) && (ptr_action != NULL)) + { + val_16 = circuit_cle_primaire(atoi(ptr_circuit)); + val_8 = atoi(ptr_action); + + if ((val_16 >= 0) && + (val_8 >= EVENTS_TYPE_ACTION_CIRCUIT_TOGGLE) && + (val_8 <= MAX_CONSIGNE_CIRCUIT)) + { + events.events[i].action_circuits.action[m].no_circuit = val_16; // Mémorisation du n° de circuit + events.events[i].action_circuits.action[m].action = val_8; // Mémorisation de l'action + events.events[i].action_circuits.action[m].flag_validite = TRUE; + events.events[i].action_circuits.nb ++; + } + } + + // Message d'info si l'action n'est pas valide : + if (!events.events[i].action_circuits.action[m].flag_validite) + { + sprintf(tmp, "L'action #%i de l'évènement n° %i (Cle_primaire = %i) dans la Base de Données (TB_Events) a une structure invalide ; elle est dévalidé.", m, i, events.events[i].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + } + else ; // Si le champ "Conditions_horaires" est vide : le champ 'events.events[i].nb_conditions_horaires' a été mis à 0 par défaut à l'init. + j ++; + +/* +// Debug : +printf("Evènement %i : ----------------------------------------------------------\n", i); +printf("Clé=%i, Condition_test_flag_validite=%i, Type source=%i, No source=%i, No_variable=%i, Test=%i, Seuil=%f, Tempo=%i, Alarme_son=%i, Alarme_led=%i, Alarme_soutien=%i, \ + Alarme_source=%i, Alarme_SMS=%i, Alarme_email=%i, Alarme_message=%i \n", + events.events[i].cle_primaire, events.events[i].condition_test.flag_validite, events.events[i].condition_test.type_source, events.events[i].condition_test.no_source, + events.events[i].condition_test.no_variable, events.events[i].condition_test.test, events.events[i].condition_test.seuil, events.events[i].tempo, events.events[i].alarme_son, + events.events[i].alarme_led, events.events[i].alarme_soutien, events.events[i].alarme_source, events.events[i].alarme_sms, events.events[i].alarme_email, + events.events[i].alarme_message ); +printf("Conditions horaires :"); +for (j=0; j= MAX_LONGUEUR_NOM_FICHIER) + { + sprintf(tmp, "Le nom de fichier reçu via le champ New_bip est trop long (%li caractères pour %i max). La demande est rejetée...", strlen(ptr[0]), MAX_LONGUEUR_NOM_FICHIER); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + else emission_bip(ptr[0], atoi(ptr[1]), atoi(ptr[2]), atoi(ptr[3])); // NB : si 'atoi()' rencontre une erreur, il retourne '0' qui sera traité sans problème par 'emission_bip()' + } + + // RAZ de la demande dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='' WHERE Nom='New_bip' "); + mysql_query(bdd.pointeur, requete); + } + //--------------------------------------------------------------- + if (strstr(row[0], "Consigne_modem") != NULL) + { + val_8 = atoi(row[1]); + if ((val_8 == 0) || (val_8 == 1)) + { + modem.consigne = val_8; + + // RAZ de la demande dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='-1' WHERE Nom='Consigne_modem' "); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_DEMANDE_ACTIVATION, 0, modem.consigne, NULL); + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Veille") != NULL) + { + val_u8 = atoi(row[1]); + if ((val_u8 != systeme.veille) && (val_u8 <= SYSTEME_VEILLE_PROFONDE)) + { + systeme.veille = val_u8; + + if (systeme.veille == 0) set_luminosite_ecran(ecran.consigne_backlight); + else set_luminosite_ecran(0); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHANGEMENT_VEILLE, 0, systeme.veille, NULL); + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Demande_decouverte_ow") != NULL) + { + val_u8 = atoi(row[1]); + if (val_u8 == 1) + { + c_bus_down_commande_config_ow(); // Envoi de la demande au Compagnon. + // NB : à la fin de la découverte réseau, le Compagnon va remonter une commande 'c_bus_up_commande_etat_entrees_ow' avec un code + // particulier (code_reinit) qui permettra automatiquement au CM4, et de façon synchrone, d'émettre une commande 'c_bus_down_reponse_annuaire_ow()' + + // RAZ de la demande dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='0' WHERE Nom='Demande_decouverte_ow' "); + mysql_query(bdd.pointeur, requete); + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Demande_connexion_serveur") != NULL) + { + val_u8 = atoi(row[1]); + switch (val_u8) + { + default: + case MODEM_DEMANDE_UTILISATEUR_AUCUNE: break; + + case MODEM_DEMANDE_UTILISATEUR_DOWNLOAD: + { + if (modem.compteur_periode_download > MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI) modem.compteur_periode_download = MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI; + break; + } + case MODEM_DEMANDE_UTILISATEUR_UPLOAD: + { + if (modem.compteur_periode_upload > MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI) modem.compteur_periode_upload = MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI; + break; + } + case MODEM_DEMANDE_UTILISATEUR_DOWNLOAD_UPLOAD: + { + if (modem.compteur_periode_download > MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI) modem.compteur_periode_download = MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI; + if (modem.compteur_periode_upload > MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI) modem.compteur_periode_upload = MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI + 1; // A priori, on force l'Upload à passer après le download. + break; + } + } + + if (val_u8 != MODEM_DEMANDE_UTILISATEUR_AUCUNE) // RAZ de la demande dans la BDD : + { + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Demande_connexion_serveur' ", MODEM_DEMANDE_UTILISATEUR_AUCUNE); + mysql_query(bdd.pointeur, requete); + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Demande_reboot") != NULL) + { + if (strcmp(row[1], "Reboot") == 0) + { + // RAZ du flag dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='' WHERE Nom='Demande_reboot' "); + mysql_query(bdd.pointeur, requete); + + // Lancement de la réinit : + lancement_reinit(0); // La valeur 0 indique d'utiliser la valeur par défaut + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Aube_nautique") != NULL) + { + if ((strlen(row[1]) == 5) && (row[1][2] == ':')) // Doit être de la forme "hh:mm" + { + tmp[0] = row[1][0]; + tmp[1] = row[1][1]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_AUBE_NAUTIQUE].heures = atoi(tmp); + tmp[0] = row[1][3]; + tmp[1] = row[1][4]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_AUBE_NAUTIQUE].minutes = atoi(tmp); + systeme.heures_speciales[HEURE_SPECIALE_AUBE_NAUTIQUE].secondes = 0; + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Lever_soleil") != NULL) + { + if ((strlen(row[1]) == 5) && (row[1][2] == ':')) // Doit être de la forme "hh:mm" + { + tmp[0] = row[1][0]; + tmp[1] = row[1][1]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_LEVER_SOLEIL].heures = atoi(tmp); + tmp[0] = row[1][3]; + tmp[1] = row[1][4]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_LEVER_SOLEIL].minutes = atoi(tmp); + systeme.heures_speciales[HEURE_SPECIALE_LEVER_SOLEIL].secondes = 0; + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Zenith") != NULL) + { + if ((strlen(row[1]) == 5) && (row[1][2] == ':')) // Doit être de la forme "hh:mm" + { + tmp[0] = row[1][0]; + tmp[1] = row[1][1]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_ZENITH].heures = atoi(tmp); + tmp[0] = row[1][3]; + tmp[1] = row[1][4]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_ZENITH].minutes = atoi(tmp); + systeme.heures_speciales[HEURE_SPECIALE_ZENITH].secondes = 0; + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Coucher_soleil") != NULL) + { + if ((strlen(row[1]) == 5) && (row[1][2] == ':')) // Doit être de la forme "hh:mm" + { + tmp[0] = row[1][0]; + tmp[1] = row[1][1]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_COUCHER_SOLEIL].heures = atoi(tmp); + tmp[0] = row[1][3]; + tmp[1] = row[1][4]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_COUCHER_SOLEIL].minutes = atoi(tmp); + systeme.heures_speciales[HEURE_SPECIALE_COUCHER_SOLEIL].secondes = 0; + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Crepuscule_nautique") != NULL) + { + if ((strlen(row[1]) == 5) && (row[1][2] == ':')) // Doit être de la forme "hh:mm" + { + tmp[0] = row[1][0]; + tmp[1] = row[1][1]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_CREPUSCULE_NAUTIQUE].heures = atoi(tmp); + tmp[0] = row[1][3]; + tmp[1] = row[1][4]; + tmp[2] = 0; + systeme.heures_speciales[HEURE_SPECIALE_CREPUSCULE_NAUTIQUE].minutes = atoi(tmp); + systeme.heures_speciales[HEURE_SPECIALE_CREPUSCULE_NAUTIQUE].secondes = 0; + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Demande_maj_nmea") != NULL) + { + val_u8 = atoi(row[1]); + if (val_u8 == 1) + { + flag_demande_maj_nmea = TRUE; // Flag traité en fin de cette fonction... + + // RAZ de la demande dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='0' WHERE Nom='Demande_maj_nmea' "); + mysql_query(bdd.pointeur, requete); + } + } + + } + mysql_free_result(result); //Libération du jeu de résultat + + // Recherche des nouveaux paramètres de config reçus depuis le web : + sprintf(requete, "SELECT Nom, Valeur FROM TB_Configuration "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + //--------------------------------------------------------------- + if (strstr(row[0], "Fuseau_horaire") != NULL) + { + val_8 = atoi(row[1]); + if (val_8 != systeme.fuseau_horaire) + { + if ((val_8 >= -14) && (val_8 <= 12)) systeme.fuseau_horaire = val_8; + } + } + //--------------------------------------------------------------- + if (strstr(row[0], "Heure_ete") != NULL) + { + val_u8 = atoi(row[1]); + if (val_u8 != systeme.heure_ete) + { + if ((val_u8 == 0) || (val_u8 == 1)) systeme.heure_ete = val_u8; + } + } + //--------------------------------------------------------------- + if (strstr(row[0], "Validation_leds") != NULL) + { + val_u16 = atoi(row[1]); + if ((val_u16 != ecran.validation_leds) && (val_u16 <= 1)) + { + ecran.validation_leds = val_u16; + + if (systeme.veille == SYSTEME_VEILLE_AUCUNE) + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(ecran.consigne_backlight, 0); + else c_bus_down_commande_cde_backlight(ecran.consigne_backlight, ecran.consigne_leds); + } + else + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(0, 0); + else c_bus_down_commande_cde_backlight(0, ecran.consigne_leds); + } + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_LEDS_CHANGEMENT_CONSIGNE, 0, ecran.consigne_leds, NULL); + } + } + //--------------------------------------------------------------- + if (strstr(row[0], "Consigne_leds") != NULL) + { + val_f = atof(row[1]); + if ((val_f != ecran.consigne_leds) && (val_f >=LEDS_CONSIGNE_MIN) && (val_f <= LEDS_CONSIGNE_MAX)) + { + ecran.consigne_leds = val_f; + + if (systeme.veille == SYSTEME_VEILLE_AUCUNE) + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(ecran.consigne_backlight, 0); + else c_bus_down_commande_cde_backlight(ecran.consigne_backlight, ecran.consigne_leds); + } + else + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(0, 0); + else c_bus_down_commande_cde_backlight(0, ecran.consigne_leds); + } + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_LEDS_CHANGEMENT_CONSIGNE, 0, ecran.consigne_leds, NULL); + } + } + //--------------------------------------------------------------- + if (strstr(row[0], "Consigne_backlight") != NULL) + { + val_f = atof(row[1]); + if ((val_f != ecran.consigne_backlight) && (val_f >=ECRAN_CONSIGNE_MIN) && (val_f <= ECRAN_CONSIGNE_MAX)) + { + ecran.consigne_backlight = val_f; + + if (systeme.veille == SYSTEME_VEILLE_AUCUNE) set_luminosite_ecran(ecran.consigne_backlight); // Mise à jour de la luminosité de l'écran avec cette nouvelle valeur + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_BACKLIGHT_CHANGEMENT_CONSIGNE, 0, ecran.consigne_backlight, NULL); + } + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Consigne_volume") != NULL) + { + val_f = atof(row[1]); + if (val_f != audio.consigne_volume) set_volume(val_f, AUDIO_SET_VOLUME_ECHO_ON); // Mise à jour de la consigne de volume (les tests de validité et la mise à jour du volume se font dans la fonction...) + } + //--------------------------------------------------------------- + else if (strstr(row[0], "Wifi_new_config") != NULL) + { + val_8 = atoi(row[1]); + if (val_8 == 1) flag_synchro_wifi = TRUE; + else if (val_8 == 2) flag_scan_wifi_sta = TRUE; + //reseaux.wifi_new.sta.flag_dde_scan = TRUE; + } + + } + mysql_free_result(result); //Libération du jeu de résultat + + + // Mise à jour de la config du Wifi : + if (flag_synchro_wifi) + { + // Récupération des valeurs de la BDD : + sprintf(requete, "SELECT Nom, Valeur FROM TB_Configuration WHERE (Nom='Wifi_sta_cde' OR Nom='BSSID_Wifi_sta' OR Nom='Wifi_ap_cde' OR Nom='Wifi_ap_ssid' OR Nom='Wifi_ap_pwd' OR Nom='Wifi_ap_canal') "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], "Wifi_sta_cde") == 0) reseaux.wifi_new.sta.cde = atoi(row[1]); + else if (strcmp(row[0], "Wifi_ap_cde") == 0) reseaux.wifi_new.ap.cde = atoi(row[1]); + else if (strcmp(row[0], "BSSID_Wifi_sta") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_STA_TAILLE_BSSID) strcpy(reseaux.wifi_new.sta.bssid, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_ssid") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_AP_TAILLE_SSID) strcpy(reseaux.wifi_new.ap.ssid, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_pwd") == 0) + { + if (strlen(row[1]) < MAX_RESEAUX_WIFI_AP_TAILLE_PWD) strcpy(reseaux.wifi_new.ap.pwd, row[1]); + } + else if (strcmp(row[0], "Wifi_ap_canal") == 0) reseaux.wifi_new.ap.canal = atoi(row[1]); + } + + // Mise à jour des infos du réseau STA en cours d'utilisation : + if (reseaux.wifi_new.sta.cde == ON) // Le mode STA est ON (sinon, pas la peine d'aller chercher des infos complémentaires qui ne seront pas utilisées) + { + if (reseaux.wifi_new.sta.bssid[0] != 0) // Si 'BSSID_Wifi_sta' n'est pas vide : + { + sprintf(requete, "SELECT Nom, Psk FROM TB_Wifi_sta WHERE Bssid='%s' ", reseaux.wifi_new.sta.bssid); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strlen(row[0]) < MAX_RESEAUX_WIFI_STA_TAILLE_SSID) strcpy(reseaux.wifi_new.sta.ssid, row[0]); + else strcpy(reseaux.wifi_new.sta.ssid, ""); + + if (strlen(row[1]) < MAX_RESEAUX_WIFI_STA_TAILLE_PSK) strcpy(reseaux.wifi_new.sta.psk, row[1]); + else strcpy(reseaux.wifi_new.sta.psk, ""); + } + } + else // Si 'BSSID_Wifi_sta' est vide : + { + strcpy(reseaux.wifi_new.sta.ssid, ""); // ... on passe le SSID et le PSK à vide également + strcpy(reseaux.wifi_new.sta.psk, ""); + } + } + + // Enregistrement dans l'historique : + sprintf(tmp, "Sta_cde=%i, Sta_ssid=%s, Sta_psk=%s, Ap_cde=%i, Ap_ssid=%s, Ap_pwd=%s, Ap_canal=%i", reseaux.wifi_new.sta.cde, reseaux.wifi_new.sta.ssid, reseaux.wifi_new.sta.psk, reseaux.wifi_new.ap.cde, reseaux.wifi_new.ap.ssid, reseaux.wifi_new.ap.pwd, reseaux.wifi_new.ap.canal); + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_WIFI_MISE_A_JOUR_CONFIG, 0, 0, tmp); + + // Positionnement du flag pour prise en compte par le gestionnaire de réseaux : + reseaux.wifi_new.flag_new_config = TRUE; + } + + // Lancement du scan Wifi STA : + if (flag_scan_wifi_sta) reseaux.wifi_new.sta.flag_dde_scan = TRUE; + + // Remise au repos du flag de synchro Wifi de la BDD : + if ((flag_synchro_wifi) || (flag_scan_wifi_sta)) + { + sprintf(requete, "UPDATE TB_Configuration SET Valeur=-1 WHERE Nom='Wifi_new_config' "); + mysql_query(bdd.pointeur, requete); + } + + // Mise à jour de la présence d'une fiche jack dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Presence_jack' ", audio.etat_presence_jack); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'adresse IP du port Ethernet : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_ethernet' ", reseaux.ethernet.adresse_ip); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'adresse IP du port Wifi_sta : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_wifi_sta' ", reseaux.wifi.sta.adresse_ip); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'adresse IP du port Wifi_ap : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Adresse_ip_wifi_ap' ", reseaux.wifi.ap.adresse_ip); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'état du modem dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Etat_modem' ", modem.etat); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de la présence de la carte SIM dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Presence_sim' ", modem.etat_presence_sim); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'état de la 4G-LTE dans la BDD : + if (modem.etat_4g_lte == OFF) sprintf(requete, "UPDATE TB_Systeme SET Valeur=0 WHERE Nom='Etat_4g_lte' "); + else + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_RSRP); // 0 est l'adresse de ShipBase + if (val_16 < 0) sprintf(requete, "UPDATE TB_Systeme SET Valeur=0 WHERE Nom='Etat_4g_lte' "); // Si l'entrée n'existe pas (retour de -1), on considère la valeur à 0 + else + { + if (entrees.predefinies.entrees[val_16].valeur <= -112) sprintf(requete, "UPDATE TB_Systeme SET Valeur=1 WHERE Nom='Etat_4g_lte' "); + else if (entrees.predefinies.entrees[val_16].valeur <= -103) sprintf(requete, "UPDATE TB_Systeme SET Valeur=2 WHERE Nom='Etat_4g_lte' "); + else if (entrees.predefinies.entrees[val_16].valeur <= -85) sprintf(requete, "UPDATE TB_Systeme SET Valeur=3 WHERE Nom='Etat_4g_lte' "); + else sprintf(requete, "UPDATE TB_Systeme SET Valeur=4 WHERE Nom='Etat_4g_lte' "); + } + } + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'état du GNSS dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Etat_gnss' ", gnss.etat); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'état du bus OW : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Defaut_bus_ow' ", entrees.ow.defaut_bus); + mysql_query(bdd.pointeur, requete); + + // Mise à jour de l'état des ports USB : + for (i = 0; i < USB_MAX_NOMBRE_PORTS; i ++) + { + if (usb.ports[i].flag_validite == FALSE) continue; + if (i < 3) sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Defaut_port_usb_%i' ", usb.ports[i].defaut, (i+1)); + else sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Defaut_port_usb_arriere' ", usb.ports[i].defaut); + mysql_query(bdd.pointeur, requete); + } + + + +// ******************************************************************************************************************************************************** +// MODULES +// ******************************************************************************************************************************************************** + + //------------------------------------------------------------------------------------------ + // Traitement des nouvelles demandes de mise à jour de l'EEPROM des modules : + int8_t maj_eeprom_modules[MAX_NOMBRE_MODULES]; + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) maj_eeprom_modules[i] = C_BUS_CONFIG_AUCUNE; + + sprintf(requete, "SELECT Adresse_k_bus, New_eeprom FROM TB_Modules_physiques WHERE Presence=1 AND New_eeprom!='-1' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + val_u8 = (uint8_t) atoi(row[0]); // Adresse K-Bus + maj_eeprom_modules[val_u8] = (int8_t) atoi(row[1]); // Le flag de maj contient le mode de mise à jour demandé (-1=Rien, 1=write normal, 2=write config usine) + + // Remise à -1 (repos), la valeur de New_eeprom dans la BDD : + sprintf(requete, "UPDATE TB_Modules_physiques SET New_eeprom='-1' WHERE Adresse_k_bus='%i'", val_u8); + mysql_query(bdd.pointeur, requete); + } + mysql_free_result(result); //Libération du jeu de résultat + + //------------------------------------------------------------------------------------------ + // Traitement des nouvelles commandes de modules : + sprintf(requete, "SELECT Adresse_k_bus, New_cde FROM TB_Modules_config WHERE Presence=1 AND New_cde!='' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + val_u8 = (uint8_t) atoi(row[0]); // Adresse K-Bus + + i = 0; + ptr[i] = strtok (row[1],"/"); // Extraction du premier segment avant le premier '/' ATTENTION : si aucun délimiteur trouvé, la fonction renvoit la chaine entière (et pas NULL...) + while ((ptr[i] != NULL) && (i < MAX_NOMBRE_CHAMPS_INTERNES)) + { + i ++; + ptr[i] = strtok (NULL, "/"); // Extraction du segment suivant le prochain '/' + } + + // Check du nombre de champs trouvés : + if (i != 2) + { + sprintf(tmp, "La demande de New_cde sur le module @%i n'a pas un format acceptable (%i segments pour 2 attendus). La demande est rejetée...", val_u8, i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + else c_bus_down_commande_cde_carte(val_u8, (uint8_t) atoi(ptr[0]), (uint8_t) atoi(ptr[1])); // On ne fait pas plus de vérification ici car elles seront faites dans la fonction qui, si besoin, rejettera la demande. + + sprintf(requete, "UPDATE TB_Modules_config SET New_cde='' WHERE Adresse_k_bus='%i'", val_u8); + mysql_query(bdd.pointeur, requete); // Remise à -1 (repos), la valeur de Cde dans la BDD + } + mysql_free_result(result); //Libération du jeu de résultat + + + //------------------------------------------------------------------------------------------ + // Mise à jour des modules : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_config[i].flag_presence == FALSE) continue; // On ne taite que les modules présents + + // Mise à jour de l'EEPROM du module si besoin : + if (maj_eeprom_modules[i] > C_BUS_CONFIG_AUCUNE) // Si la demande n'est pas au moins un 'Read', 'Write normal' ou 'write config usine', on ne fait rien + { + // Recherche des valeurs de l'EEPROM stockées dans la BDD (et qui ont probablement été modifiées par QT puisqu'on demande une action de maj) + // pour mise à jour de l'image stockée dans 'systeme.modules[].eeprom[]' : + sprintf(requete, "SELECT Eeprom FROM TB_Modules_physiques WHERE Adresse_k_bus='%i'", i); // NB : le contenu de l'EEPROM est stocké dans le module PHYSIQUE ! +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (row[0] == NULL) break; // Si le champ EEPROM est NULL, on ne fait rien + + k = 0; + for (j = 0; (j < MAX_TAILLE_EEPROM); j ++) // On traite au maxi MAX_TAILLE_EEPROM + { + if (row[0][k] == 0) break; // On s'arrête de traiter les octets quand on atteint le NULL de fin de chaine (le stockage en VARCHAR permet de ne pas aller au bout de la taille maximale). + tmp[0] = row[0][k ++]; + tmp[1] = row[0][k ++]; + tmp[2] = 0; + + systeme.modules_physiques[i].eeprom[j] = (uint8_t) strtol(tmp, NULL, 16); // L'utilisation de 'strtol' permet de passer de l'hexa dans la chaine... + } + + // Envoi vers le module des ces nouvelles valeurs (en tenant compte du mode demandé dans 'New_eeprom') : + c_bus_down_commande_config_carte(i, maj_eeprom_modules[i]); + } + mysql_free_result(result); //Libération du jeu de résultat + } + + // Mise à jour de l'état du module : + sprintf(requete, "UPDATE TB_Modules_config SET Mode_test=%i, Tension_etat=%i, Origine_reset=%i, Courant_masses=%.3f WHERE Adresse_k_bus=%i ", + systeme.modules_config[i].mode_test, + systeme.modules_config[i].alim_etat, + systeme.modules_config[i].origine_reset, + systeme.modules_config[i].courant_masses, // SDIR #318 + i); +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + + // Supervision des défauts d'alimentation et du mode test des modules : + if (systeme.modules_config[i].alim_etat == CIRCUITS_TENSION_CARTE_OK) // Alim du module = OK + { + if (systeme.modules_config[i].mode_test == OFF) // Module en mode Normal + { + systeme.modules_config[i].etat_commande_carte = CIRCUITS_ETAT_COMMANDE_CARTE_OK; + } + else // Module en mode Test + { + systeme.modules_config[i].etat_commande_carte = CIRCUITS_ETAT_COMMANDE_CARTE_TEST; + } + } + else // Alim du module = UNDERVOLTAGE + { + systeme.modules_config[i].etat_commande_carte = CIRCUITS_ETAT_COMMANDE_CARTE_UNDERVOLTAGE; + } + } + + +// ******************************************************************************************************************************************************** +// CIRCUITS +// ******************************************************************************************************************************************************** + + //------------------------------------------- + // Traitement des nouvelles consignes : + sprintf(requete, "SELECT Cle_primaire, New_consigne, Consigne FROM TB_Circuits WHERE New_consigne!=-1 "); + mysql_query(bdd.pointeur, requete); + result = mysql_store_result(bdd.pointeur); + while((row = mysql_fetch_row(result))) + { + i = atoi(row[0]); // Clé primaire du circuit + int16_t no_circuit = circuit_cle_primaire(i); // No du circuit + + j = atoi(row[1]); // New Consigne + k = atoi(row[2]); // Consigne actuelle + + bool flag_consigne_valide = false; + + if (no_circuit >= 0) // Si le n° de circuit n'a pu être trouvé, on ne traite pas la valeur. + { + switch(systeme.modules_config[circuits.circuits[no_circuit].adresse_module].type) // Recherche du type de module : + { + case TYPE_CARTE_SHIPBASE : { if ((j == OFF) || (j == MAX_CONSIGNE_CIRCUIT)) flag_consigne_valide = TRUE; break; } + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : { if ((j >= OFF) && (j <= MAX_CONSIGNE_CIRCUIT)) flag_consigne_valide = TRUE; break; } + case TYPE_CARTE_SP4AC : { if ((j == OFF) || (j == MAX_CONSIGNE_CIRCUIT)) flag_consigne_valide = TRUE; break; } // SDIR #79 + default : ; + } + + if (flag_consigne_valide) // Vérifie que la nouvelle valeur de consigne est valide. + { + if ((uint16_t)circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante != j) // On ne traite que si la nouvelle consigne est différente de l'ancienne : + { + k = j; // SDIR #79 - Permet la mise à jour dans la BDD dans tous les cas (yc les cas d'erreur) + + changement_consigne_circuit(no_circuit, j, CIRCUITS_ORIGINE_CONSIGNE_SHIPBASE, 0); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, CIRCUIT_CHANGEMENT_CONSIGNE, i, j, NULL); // Enregistrement dans l'historique + } + } + } + + sprintf(requete, "UPDATE TB_Circuits SET New_consigne='-1', Consigne=%i WHERE Cle_primaire=%i ", k, i); // SDIR #79 - RAZ de la demande et mise à jour de la consigne dans la BDD +//printf("Requete = [ %s; ]\n", requete); + mysql_query(bdd.pointeur, requete); + } + mysql_free_result(result); + + //------------------------------------------------------------- + // Traitement des nouvelles données, autre que les consignes : + sprintf(requete, "SELECT Cle_primaire, Validation_cdes_events FROM TB_Circuits "); + mysql_query(bdd.pointeur, requete); + result = mysql_store_result(bdd.pointeur); + while((row = mysql_fetch_row(result))) + { + i = atoi(row[0]); // Clé primaire du circuit + int16_t no_circuit = circuit_cle_primaire(i); // No du circuit + + // Mise à jour du flag de validation des commandes par events : + if (no_circuit >= 0) circuits.circuits[no_circuit].validation_cdes_events = atoi(row[1]); // On fait la mise à jour dans tous les cas (y compris si la valeur n'a pas changée) car c'est plus simple... + } + mysql_free_result(result); + + //-------------------------------------------------- + // Mise à jour de l'état des circuits vers la BDD : + for (i = 0; i < circuits.nb; i ++) + { + if (!circuits.circuits[i].flag_dde_maj_bdd) continue; + + circuits.circuits[i].flag_dde_maj_bdd = FALSE; // Reset du flag une fois la mise à jour faite dans la BDD + + if (circuits.circuits[i].adresse_module != 0) circuits.circuits[i].etat_commande_carte = systeme.modules_config[circuits.circuits[i].adresse_module].etat_commande_carte; + else circuits.circuits[i].etat_commande_carte = CIRCUITS_ETAT_COMMANDE_CARTE_OK; + + sprintf(requete, "UPDATE TB_Circuits SET Etat='%i', Origine_consigne=%i, ID_Origine_consigne=%i, Consigne='%f', Mode_test=%i, Etat_commande_carte=%i, \ + Tension=%f, Intensite=%f, Frequence=%f WHERE Cle_primaire='%i'", + circuits.circuits[i].etat, circuits.circuits[i].origine_consigne, circuits.circuits[i].id_origine_consigne, circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante, + circuits.circuits[i].mode_test, circuits.circuits[i].etat_commande_carte, + (systeme.modules_config[circuits.circuits[i].adresse_module].type != TYPE_CARTE_SP4AC) ? circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante : circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante, + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante, circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante, + circuits.circuits[i].cle_primaire); +//printf("Requete sur circuit n° %i = [ %s; ]\n", i, requete); + mysql_query(bdd.pointeur, requete); + } + + +// ******************************************************************************************************************************************************** +// CAPTEURS +// ******************************************************************************************************************************************************** + + // Mise à jour des valeurs dans la BDD quand une demande a été formulée : + for (i=0; i= 0) // Si le n° de batterie n'a pu être trouvé, on ne traite pas la valeur. + { + switch(j) // Recherche du type de commande : + { + //----------------------------------------------- + case BATTERIE_COMMMANDE_FORCAGE_PLEINE_CHARGE : + { + batteries.batteries[no_batterie].variables[BATTERIE_VARIABLE_SOC].valeur_courante = 100; + batteries.batteries[no_batterie].eod = 0; + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_BATTERIES, BATTERIE_RX_CDE_FORCAGE_PLEINE_CHARGE, i, 0, NULL); + + break; + } + + //----------------------------------------------- + case BATTERIE_COMMMANDE_RESET_SOH : + { + batteries.batteries[no_batterie].variables[BATTERIE_VARIABLE_SOH].valeur_courante = 100; + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_BATTERIES, BATTERIE_RX_CDE_RESET_SOH, i, 0, NULL); + + break; + } + + //----------------------------------------------- + case BATTERIE_COMMMANDE_RESET_COMPTEURS : + { + batteries.batteries[no_batterie].nombre_cycles = 0; + batteries.batteries[no_batterie].nombre_decharges_completes = 0; + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_BATTERIES, BATTERIE_RX_CDE_RESET_COMPTEURS, i, 0, NULL); + + break; + } + + //----------------------------------------------- + default : ; + } + + batteries.batteries[no_batterie].flag_dde_maj_bdd = TRUE; // Positionné même dans le cas d'une commande non valide (puisqu'on n'aura rien fait dans ce cas) + } + + sprintf(requete, "UPDATE TB_Batteries SET New_cde='-1' WHERE Cle_primaire=%i ", i); +//printf("Requete = [%s]\n", requete); + mysql_query(bdd.pointeur, requete); + } + mysql_free_result(result); + + + + // Mise à jour des valeurs dans la BDD quand une demande a été formulée : + for (i=0; i C_BUS_PROTOCOLE_TX_MAX_REPETITIONS) // Si le nombre maxi de répétitions a été dépassé, on laisse tomber la commande + { + c_bus.protocole_tx.repetitions = 0; // RAZ du compteur pour utilisation ultérieure + break; // Sortie du séquenceur + } + } + else break; // Tant qu'on est en attente d'une réponse et que le time-out n'est pas expiré, on ne traite aucun envoi en mode Down + } + else // Sinon, recherche d'une nouvelle commande à émettre en mode Down : + { + if (c_bus.down.ptr_debut_buffer_cdes != c_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission... + { + // ATTENTION : gérer les priorités ...! + + if (++ c_bus.down.ptr_debut_buffer_cdes == C_BUS_MAX_TAILLE_BUFFER_CDES) c_bus.down.ptr_debut_buffer_cdes = 0; +#ifdef __DEBUG_C_BUS + c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde[c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].lg_cde] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. + affichage_debug_bus(TYPE_BUS_DEBUG_C_BUS, "Tx", (char *) c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_C_BUS); +#endif + } + else break; // Il n'y a pas de nouvelle commande en attente d'émission + } + } + + //..................................... + // Préparation de la trame d'émission : + lg_tx_buffer = 0; + + tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = ASCII_SOH; + cs = ASCII_SOH; // Initialisation du CS calculé + tx_buffer[lg_tx_buffer ++] = sens; + cs += sens; // Rechargement du CS + tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = ASCII_STX; + cs += ASCII_STX; // Rechargement du CS + + if (sens == 'D') + { + for (i=0; (i < c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < C_BUS_MAX_TAILLE_BUFFER_TX); i++) + { + c = c_bus.down.buffer_cdes[c_bus.down.ptr_debut_buffer_cdes].cde[i]; + if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = c; + cs += c; // Rechargement du CS + } + } + else + { + for (i=0; (i < c_bus.up.reponse.lg_reponse) && (lg_tx_buffer < C_BUS_MAX_TAILLE_BUFFER_TX); i++) + { + c = c_bus.up.reponse.reponse[i]; + if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = c; + cs += c; // Rechargement du CS + } + } + + tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = ASCII_ETX; + cs += ASCII_ETX; // Rechargement du CS + + c = (uint8_t)(cs & 0x00FF); // LSB du CS + if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = c; + + c = (uint8_t)(cs >> 8); // MSB du CS + if (c == ASCII_DLE) tx_buffer[lg_tx_buffer ++] = ASCII_DLE; + tx_buffer[lg_tx_buffer ++] = c; + + //............................. + // Envoi au driver d'émission : + write(config_serial1.filestream, tx_buffer, lg_tx_buffer); + + //.......................................................... + // Mise à jour des paramètres après l'émission de la trame : + if (sens == 'D') // S'il s'agissait d'une commande émise en mode Down, on lance un time-out et on indique qu'on attend la réponse : + { + c_bus.down.flag_attente_reponse = TRUE; + c_bus.protocole_tx.sequenceur.compteur_time_out = C_BUS_TIME_OUT_REPONSE_TX; + } + else ; // S'il s'agissait d'une réponse à une commande reçue en mode Up, on revient immédiatement au repos (la gestion des répétitions des commandes est de la responsabilité du maitre) + + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur C-Bus.Protocole_Tx est inconnu [%i]. Il est repositionné au REPOS", c_bus.protocole_tx.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + c_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_REPOS; + break; + } + } +} + + +/***************************************************************************************************************************************************************** + Fonction : Incrémente le pointeur du buffer de commandes en attente d'émission vers C-Bus + La fonction gère : + - le rebouclage en fin de buffer (buffer circulaire) + - le dépassement (quand la fin rattrape le début) en émettant un message de debug et en choissant : + . soit d'écraser la commande la plus ancienne si elle est de priorité inférieure (strictement) + . soit refuser l'ajout en laissant le pointeur de fin du buffer à sa position et en renvoyant une erreur. + Reçoit : La priorité de la commande à ajouter (permet d'arbitrer dans la cas où le buffer est plein) + Renvoit : 0 si une position disponible a été trouvée, -1 si aucune position n'est disponible. +******************************************************************************************************************************************************************/ +int8_t c_bus_next_write_buffer_cdes(uint8_t priorite) +{ +int8_t retour = 0; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Incrémentation : + if (++ c_bus.down.ptr_fin_buffer_cdes == C_BUS_MAX_TAILLE_BUFFER_CDES) c_bus.down.ptr_fin_buffer_cdes = 0; + + // Vérification si buffer plein : + if (c_bus.down.ptr_fin_buffer_cdes == c_bus.down.ptr_debut_buffer_cdes) + { + if (c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes].priorite >= priorite) + { + // Décrémentation du pointeur puisqu'on ne va pas accepter la nouvelle commande qui n'a pas une priorité suffisante : + if (c_bus.down.ptr_fin_buffer_cdes == 0) c_bus.down.ptr_fin_buffer_cdes = (C_BUS_MAX_TAILLE_BUFFER_CDES - 1); + else c_bus.down.ptr_fin_buffer_cdes --; + + // Message de débug : + sprintf(tmp, "Buffer de commandes C-Bus plein. La demande de stockage d'une nouvelle commande a été rejetée..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_BUS_BUFFER_COMMANDES_PLEIN, (uint32_t) 'C', 0, NULL); + + retour = -1; + } + else // Dans ce cas, la nouvelle commande est plus prioritaire (strictement) à la commande existante dans le buffer et elle va la remplacer. + { + // Message de débug : + sprintf(tmp, "Buffer de commandes C-Bus plein. La demande de stockage d'une nouvelle commande a écrasée la commande la plus ancienne qui était de priorité inférieure..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + + return (retour); +} + + + diff --git a/sources/c_bus.h b/sources/c_bus.h new file mode 100644 index 0000000..e1d7f83 --- /dev/null +++ b/sources/c_bus.h @@ -0,0 +1,49 @@ +/********************************************************************************************************************************************************************************** + + Fichier C_BUS.H + + Contient tous les define liés au C_BUS + +**********************************************************************************************************************************************************************************/ + + // Maximums : +#define C_BUS_MAX_TAILLE_BUFFER_CDES 100 // Taille du buffer de commandes (donc on stocke plusieurs commandes entières !) C-bus en mode UP +#define C_BUS_PROTOCOLE_TX_MAX_REPETITIONS 3 // Nombre de répétitions des commandes émises sur C_Bus +#define C_BUS_MAX_TAILLE_BUFFER_TX 550 +#define C_BUS_MAX_TAILLE_CDE 500 +#define C_BUS_MAX_TAILLE_REPONSE 500 + + + // Tempos : +#define C_BUS_TEMPO_INIT 2 // En x 100ms +#define C_BUS_PROTOCOLE_RX_TEMPO_INIT 10 // en x 100ms +#define C_BUS_PROTOCOLE_TX_TEMPO_INIT 10 // en x 100ms +#define C_BUS_TIME_OUT_REPONSE_TX 2 // En x 100ms + + // Priorité des commandes : +#define PRIORITE_CDE_C_BUS_FAIBLE 0 +#define PRIORITE_CDE_C_BUS_HAUTE 1 + + + // Etats du séquenceur C-Bus : +#define ETAT_SEQUENCEUR_C_BUS_INIT 0 +#define ETAT_SEQUENCEUR_C_BUS_REPOS 1 + + // Etats séquenceur du Protocole_Rx C-bus : +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_INIT 0 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_REPOS 1 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_SENS 2 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_STX 3 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_DATA 4 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ETX 5 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_CS 6 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_NACK 7 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_RX_ERREUR 10 + + // Etats séquenceur du Protocole_Tx C-bus : +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_INIT 0 +#define ETAT_SEQUENCEUR_C_BUS_PROTOCOLE_TX_REPOS 1 + + + + diff --git a/sources/c_commandes.c b/sources/c_commandes.c new file mode 100644 index 0000000..1fa9a6b --- /dev/null +++ b/sources/c_commandes.c @@ -0,0 +1,3329 @@ +//********************************************************************************************************************************************************************************** +// Fichier C_COMMANDES_.C +// +// Contient toutes les fonctions liées aux commandes envoyées et reçues sur C_Bus +//**********************************************************************************************************************************************************************************/ + +// #define __DEBUG_COMMANDES_C_BUS + +// Includes : +#include "main.h" + +// Prototypes : + // Commandes Down : +int8_t c_bus_down_commande_config_date_heure(void); +int8_t c_bus_down_commande_config_carte(uint8_t adresse_module, uint8_t mode); +int8_t c_bus_down_commande_config_circuits(uint8_t adresse_module); +int8_t c_bus_down_commande_config_level_sensors(void); +int8_t c_bus_down_commande_config_ow(void); +int8_t c_bus_down_commande_config_batteries(uint8_t adresse_module); +int8_t c_bus_down_commande_config_pgn_nmea(uint16_t rang); + +int8_t c_bus_down_commande_reset(void); +int8_t c_bus_down_commande_cde_backlight(float consigne_ecran, float consigne_leds); +int8_t c_bus_down_commande_cde_circuits(uint8_t adresse_module); +int8_t c_bus_down_commande_cde_carte(uint8_t adresse_module, uint8_t cde, uint8_t consigne); + +int8_t c_bus_down_commande_lecture_date_heure(void); +int8_t c_bus_down_commande_lecture_id_carte(uint8_t adresse_module); + +int8_t c_bus_down_commande_annuaire_k_bus(void); +int8_t c_bus_down_commande_chargement_code(uint8_t mode, uint8_t adresse_module); +int8_t c_bus_down_commande_annuaire_ow(void); + + + // Réponses Down : +int8_t c_bus_down_reponse_config_date_heure(void); + +int8_t c_bus_down_reponse_cde_backlight(void); + +int8_t c_bus_down_reponse_lecture_date_heure(void); +int8_t c_bus_down_reponse_lecture_id_carte(void); + +int8_t c_bus_down_reponse_annuaire_k_bus(void); +int8_t c_bus_down_reponse_chargement_code(void); +int8_t c_bus_down_reponse_annuaire_ow(void); + +int8_t c_bus_down_reponse_generique(void); +int8_t c_bus_down_recherche_reponse(void); + + + // Commandes UP : +uint8_t c_bus_up_commande_etat_carte(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_circuits(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_entrees_aux(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_level_sensors(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_entrees_volt(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_entrees_ow(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_entrees_batteries(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_commande_etat_pgn_nmea(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); + +uint8_t c_bus_up_commande_config_carte(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); + +uint8_t c_bus_up_commande_inconnue(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +uint8_t c_bus_up_recherche_commande(void); + + + + +// Variables globales : + + + + +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** +// DOWN - Commandes +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** + + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de configurer la date et l'heure courante stockée dans le RTC de la carte. +// Reçoit : Le numéro de carte concernée (0=Compagnon, >0= adresse de la carte sur K-Bus) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_date_heure(void) +{ +time_t timestamp; +struct tm * t; +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + timestamp = time(NULL); + t = localtime(×tamp); + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_DATE_HEURE; + buffer_cde->cde[buffer_cde->lg_cde ++] = t->tm_mday; + buffer_cde->cde[buffer_cde->lg_cde ++] = (t->tm_mon)+1; // tm_mon va de 0 à 11 + buffer_cde->cde[buffer_cde->lg_cde ++] = t->tm_year; // Nombre d'années écoulées depuis 1900 + buffer_cde->cde[buffer_cde->lg_cde ++] = t->tm_hour; + buffer_cde->cde[buffer_cde->lg_cde ++] = t->tm_min; + buffer_cde->cde[buffer_cde->lg_cde ++] = t->tm_sec; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au CM3P de configurer une carte ou le Compagnon +// Reçoit : Le numéro de carte concernée (0=Compagnon, >0= adresse de la carte sur K-Bus) et le Mode d'envoi de la commande (0= read, 1= write) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_carte(uint8_t adresse_module, uint8_t mode) +{ +int16_t val; +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + if (systeme.modules_config[adresse_module].flag_presence == FALSE) return(-1); + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_CARTE; + + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SHIPBASE : // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage tension + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage courant d'entrée + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage courant USB + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage température + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage pression atmosphérique + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage humidité + + break; + } + case TYPE_CARTE_SP812 : // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage tension + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage température + + break; + } + case TYPE_CARTE_SP8DC : // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage tension + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage température + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (systeme.courant_masses_maxi_sp8dc * 10); // Courant de masse maxi. Passage de A à dixièmes d'A // SDIR #138 + + break; + } + case TYPE_CARTE_SBS : + { + // Attention : dans les lignes ci-dessous, on utilise 'modules_physiques' au lieu de 'modules' car les infos d'EEPROM sont relatives au module PHYSIQUE + + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage température shunt + if ((mode == C_BUS_CONFIG_WRITE_COURANTE) || (mode == C_BUS_CONFIG_WRITE_USINE)) + { + val = (int16_t) (((uint16_t)systeme.modules_physiques[adresse_module].eeprom[1] << 8) + (uint16_t) systeme.modules_physiques[adresse_module].eeprom[0]); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (val & 0x00FF); // LSB de l'offset + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) ((val >> 8) & 0x00FF); // MSB de l'offset + + val = (int16_t) (((uint16_t)systeme.modules_physiques[adresse_module].eeprom[3] << 8) + (uint16_t) systeme.modules_physiques[adresse_module].eeprom[2]); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (val & 0x00FF); // LSB du gain + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) ((val >> 8) & 0x00FF); // MSB du gain + } + if (mode == C_BUS_CONFIG_WRITE_USINE) + { + val = (int16_t) (((uint16_t)systeme.modules_physiques[adresse_module].eeprom[5] << 8) + (uint16_t) systeme.modules_physiques[adresse_module].eeprom[4]); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (val & 0x00FF); // LSB de l'offset usine + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) ((val >> 8) & 0x00FF); // MSB de l'offset usine + + val = (int16_t) (((uint16_t)systeme.modules_physiques[adresse_module].eeprom[7] << 8) + (uint16_t) systeme.modules_physiques[adresse_module].eeprom[6]); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (val & 0x00FF); // LSB du gain usine + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) ((val >> 8) & 0x00FF); // MSB du gain usine + } + break; + } + case TYPE_CARTE_SP4AC : + { + buffer_cde->cde[buffer_cde->lg_cde ++] = 4; // Coeff filtrage température module + break; + } + + default : return(-1); + } + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de configurer les circuits de puissance d'une carte extension +// Reçoit : Le numéro de carte dont les circuits sont configurés (>0, adresse de la carte sur K-Bus) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_circuits(uint8_t adresse_module) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; +uint16_t i; +struct { + uint8_t validite; + uint8_t type; + uint8_t tension_coeff_filtrage; + uint8_t intensite_coeff_filtrage; + uint8_t frequence_coeff_filtrage; + uint16_t intensite_seuil_haut; + uint16_t intensite_seuil_haut_tempo; + uint8_t step_time; +} tmp_circuits[MAX_NOMBRE_CIRCUITS]; + + + for (i = 0; i < MAX_NOMBRE_CIRCUITS; i ++) // Valeurs par défaut + { + tmp_circuits[i].validite = FALSE; + tmp_circuits[i].type = 0; + tmp_circuits[i].tension_coeff_filtrage = 1; + tmp_circuits[i].intensite_coeff_filtrage = 1; + tmp_circuits[i].frequence_coeff_filtrage = 1; + tmp_circuits[i].intensite_seuil_haut = 0; + tmp_circuits[i].intensite_seuil_haut_tempo = 0; + tmp_circuits[i].step_time = 50; + } + + + // Vérification du paramètre reçu : + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + + if (systeme.modules_config[adresse_module].flag_presence == FALSE) return(-1); + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module) continue; + if (circuits.circuits[i].no_circuit >= SP8XX_NOMBRE_CIRCUITS) return(-1); // Cas d'erreur qui ne devrait pas arriver après les tests d'initialisation... + + tmp_circuits[circuits.circuits[i].no_circuit].validite = circuits.circuits[i].flag_validite; + + switch(circuits.circuits[i].mode_cde) + { + case CIRCUITS_MODE_COMMANDE_ON_OFF: { tmp_circuits[circuits.circuits[i].no_circuit].type = SP8XX_TYPE_CIRCUIT_ON_OFF; break; } + case CIRCUITS_MODE_COMMANDE_ON_OFF_SOFT: { tmp_circuits[circuits.circuits[i].no_circuit].type = SP8XX_TYPE_CIRCUIT_ON_OFF_SOFT; break; } + case CIRCUITS_MODE_COMMANDE_VARIABLE_SOFT: { tmp_circuits[circuits.circuits[i].no_circuit].type = SP8XX_TYPE_CIRCUIT_VARIABLE_SOFT; break; } + case CIRCUITS_MODE_COMMANDE_CADENCE: { tmp_circuits[circuits.circuits[i].no_circuit].type = SP8XX_TYPE_CIRCUIT_ON_OFF; break; } + default: { tmp_circuits[circuits.circuits[i].no_circuit].type = SP8XX_TYPE_CIRCUIT_ON_OFF; break; } + } + + tmp_circuits[circuits.circuits[i].no_circuit].intensite_coeff_filtrage = circuits.circuits[i].intensite_coeff_filtrage; + tmp_circuits[circuits.circuits[i].no_circuit].intensite_seuil_haut = 0; // circuits.circuits[i].intensite_seuil_haut; // Modif du 09/07/2022 pour que le traitement de LIMITATION soit dorénavant fait au niveau de l'appli et plus du ShipPower8xx. + tmp_circuits[circuits.circuits[i].no_circuit].intensite_seuil_haut_tempo = circuits.circuits[i].intensite_seuil_haut_tempo; + tmp_circuits[circuits.circuits[i].no_circuit].step_time = circuits.circuits[i].step_time; + + if (circuits.circuits[i].no_circuit_esclave >= 0) // Cas où le circuit a un esclave + { + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].type = tmp_circuits[circuits.circuits[i].no_circuit].type; // Utile si le traitement de l'esclave a déjà été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].mode_cde = circuits.circuits[i].mode_cde; // Utile si le traitement de l'esclave n'a pas encore été fait + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].intensite_coeff_filtrage = circuits.circuits[i].intensite_coeff_filtrage; // Utile si le traitement de l'esclave a déjà été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].intensite_coeff_filtrage = circuits.circuits[i].intensite_coeff_filtrage; // Utile si le traitement de l'esclave n'a pas encore été fait + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].intensite_seuil_haut_tempo = circuits.circuits[i].intensite_seuil_haut_tempo; // Utile si le traitement de l'esclave a déjà été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].intensite_seuil_haut_tempo = circuits.circuits[i].intensite_seuil_haut_tempo; // Utile si le traitement de l'esclave n'a pas encore été fait + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].step_time = circuits.circuits[i].step_time; // Utile si le traitement de l'esclave a déjà été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].step_time = circuits.circuits[i].step_time; // Utile si le traitement de l'esclave n'a pas encore été fait + } + + } + + break; + } + + case TYPE_CARTE_SP4AC : + { + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module) continue; + if (circuits.circuits[i].no_circuit >= SP4AC_NOMBRE_CIRCUITS) return(-1); // Cas d'erreur qui ne devrait pas arriver après les tests d'initialisation... + + tmp_circuits[circuits.circuits[i].no_circuit].validite = circuits.circuits[i].flag_validite; + tmp_circuits[circuits.circuits[i].no_circuit].tension_coeff_filtrage = circuits.circuits[i].tension_coeff_filtrage; + tmp_circuits[circuits.circuits[i].no_circuit].intensite_coeff_filtrage = circuits.circuits[i].intensite_coeff_filtrage; + tmp_circuits[circuits.circuits[i].no_circuit].frequence_coeff_filtrage = circuits.circuits[i].frequence_coeff_filtrage; + } + + break; + } + + default : return(-1); + } + + + // Préparation de la commande : + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_CIRCUITS; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + for (i = 0; i < SP8XX_NOMBRE_CIRCUITS; i ++) // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].validite; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].type; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].intensite_coeff_filtrage; + + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(tmp_circuits[i].intensite_seuil_haut & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(tmp_circuits[i].intensite_seuil_haut >> 8); // MSB + + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(tmp_circuits[i].intensite_seuil_haut_tempo & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(tmp_circuits[i].intensite_seuil_haut_tempo >> 8); // MSB + + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].step_time; + +#ifdef __DEBUG_COMMANDES_C_BUS +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +sprintf(tmp, "Commande de config des circuits sur carte %i, circuit %i : val=%i, type=%i, coeff filtrage intensité=%i, seuil limitation=%i, tempo_limitation=%i", + adresse_module, i, tmp_circuits[i].validite, tmp_circuits[i].type, tmp_circuits[i].intensite_coeff_filtrage, tmp_circuits[i].intensite_seuil_haut, tmp_circuits[i].intensite_seuil_haut_tempo); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + break; + } + + case TYPE_CARTE_SP4AC : + { + for (i = 0; i < SP4AC_NOMBRE_CIRCUITS; i ++) // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].validite; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].tension_coeff_filtrage; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].intensite_coeff_filtrage; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].frequence_coeff_filtrage; + +#ifdef __DEBUG_COMMANDES_C_BUS +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +sprintf(tmp, "Commande de config des circuits sur carte %i, circuit %i : val=%i, coeff filtrage tension=%i, coeff filtrage intensité=%i, coeff filtrage fréquence=%i,", + adresse_module, i, tmp_circuits[i].validite, tmp_circuits[i].tension_coeff_filtrage, tmp_circuits[i].intensite_coeff_filtrage, tmp_circuits[i].frequence_coeff_filtrage); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + break; + } + default :; + } + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de configurer les capteurs de niveaux +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_level_sensors(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; +uint16_t i; +struct { + uint8_t validite; + uint8_t mode; + uint8_t coeff_filtrage; +} sensors[MAX_NOMBRE_ENTREES_LEVEL_SENSOR]; + + + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + for (i = 0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) // Mise à jour des valeurs par défaut. Nécessaire car ensiuite on ne rempli que les positions des capteurs VALIDES + { + sensors[i].validite = FALSE; + sensors[i].mode = ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_NON_DEFINI; + sensors[i].coeff_filtrage = 1; + } + + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_LEVEL_SENSORS; + + for (i = 0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) // Calcule, pour chaque capteur, les paramètres de config à adresser au Compagnon : + { + if (entrees.level_sensor.entrees[i].flag_validite != FALSE) + { + sensors[entrees.level_sensor.entrees[i].no - 1].validite = entrees.level_sensor.entrees[i].flag_validite; + sensors[entrees.level_sensor.entrees[i].no - 1].mode = entrees.level_sensor.entrees[i].mode; + sensors[entrees.level_sensor.entrees[i].no - 1].coeff_filtrage = 4; + } + } + + for (i = 0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) // Ecriture dans le buffer de la consigne pour chaque capteur : + { + buffer_cde->cde[buffer_cde->lg_cde ++] = sensors[i].validite; + buffer_cde->cde[buffer_cde->lg_cde ++] = sensors[i].mode; + buffer_cde->cde[buffer_cde->lg_cde ++] = sensors[i].coeff_filtrage; +//printf("Config level sensor # %i : validite=%i, mode=%i, coeff_filtrage=%i\n", (i+1), sensors[i].validite, sensors[i].mode, sensors[i].coeff_filtrage); + } + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au CM3+ de demander au Compagnon une nouvelle reconnaissance réseau OW +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_ow(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_OW; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de configurer les IBS connectés sur K-Bus +// Reçoit : Le numéro de carte dont les circuits sont configurés (>0, adresse de la carte sur K-Bus) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_batteries(uint8_t adresse_module) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; +uint8_t type_batterie; +uint8_t temperature_coeff_filtrage; +uint8_t tension_coeff_filtrage; +uint8_t intensite_coeff_filtrage; + + // Vérification du paramètre reçu : + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + + if (systeme.modules_config[adresse_module].flag_presence == FALSE) return(-1); + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SBS : + { +type_batterie = 0; +temperature_coeff_filtrage = 50; // Il est conseillé de filtrer fortement car les mesures par NTC sont assez fluctuantes... +tension_coeff_filtrage = 20; +intensite_coeff_filtrage = 10; + + break; + } + default : return(-1); + } + + + // Préparation de la commande : + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_BATTERIES; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SBS : + { + + buffer_cde->cde[buffer_cde->lg_cde ++] = type_batterie; + buffer_cde->cde[buffer_cde->lg_cde ++] = temperature_coeff_filtrage; + buffer_cde->cde[buffer_cde->lg_cde ++] = tension_coeff_filtrage; + buffer_cde->cde[buffer_cde->lg_cde ++] = intensite_coeff_filtrage; + +#ifdef __DEBUG_COMMANDES_C_BUS +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +sprintf(tmp, "Commande de config de l'IBS sur carte %i : type batterie=%i, coeff filtrage temperature=%i, coeff filtrage tension=%i, , coeff filtrage intensite=%i", + adresse_module, type_batterie, temperature_coeff_filtrage, tension_coeff_filtrage, intensite_coeff_filtrage); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + default :; + } + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet à l'Appli de configurer un PGN NMEA2000 +// Reçoit : Le rang du PGN dans l'annuaire 'nmea.annuaire[]' +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_config_pgn_nmea(uint16_t rang) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + + // Vérification du paramètre reçu : + if (rang >= NMEA_MAX_NOMBRE_PGN) return (-1); + if (!nmea.annuaire[rang].flag_validite) return (-1); + if ((nmea.annuaire[rang].type < NMEA_PGN_TYPE_SYSTEM_SINGLE_FRAME) || (nmea.annuaire[rang].type > NMEA_PGN_TYPE_PROPRIETARY_FAST_PACKET)) return (-1); + + + // Préparation de la commande : + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + +//printf("Commande c_bus_down_commande_config_pgn_nmea avec PGN=%i, Actif=%i, Type=%i, Periode_mini=%i \n", nmea.annuaire[rang].pgn, nmea.annuaire[rang].actif, nmea.annuaire[rang].type, nmea.annuaire[rang].periode_mini); + + // Code de la commande : + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CONFIG_PGN_NMEA; + + // PGN : + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(nmea.annuaire[rang].pgn & 0x000000FF); // LLSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)((nmea.annuaire[rang].pgn >> 8) & 0x000000FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)((nmea.annuaire[rang].pgn >> 16) & 0x000000FF); // MSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)((nmea.annuaire[rang].pgn >> 24) & 0x000000FF); // MMSB + + // Actif : + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) nmea.annuaire[rang].actif; + + // Type : + buffer_cde->cde[buffer_cde->lg_cde ++] = nmea.annuaire[rang].type; + + // Periode_mini : + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(nmea.annuaire[rang].periode_mini & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(nmea.annuaire[rang].periode_mini >> 8); // MSB + + // Priorité de la commande + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Fonction de demande de réinitialisation du Compagnon, donc du système. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_reset(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_RESET; + buffer_cde->cde[buffer_cde->lg_cde ++] = 'R'; + buffer_cde->cde[buffer_cde->lg_cde ++] = 'E'; + buffer_cde->cde[buffer_cde->lg_cde ++] = 'S'; + buffer_cde->cde[buffer_cde->lg_cde ++] = 'E'; + buffer_cde->cde[buffer_cde->lg_cde ++] = 'T'; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de mettre à jour la valeur de backlight à utiliser pour l'écran tactile. C'est en effet le CM3+ qui gère l'écran +// mais le pilotage de la ligne PWM commandant le backlight est délégué au Compagnon. +// Cette commande a été étendue en juillet 2021 aux LEDS afin de piloter leur luminosité indépendamment de l'écran. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_cde_backlight(float consigne_ecran, float consigne_leds) +{ +uint8_t priorite; +uint16_t consigne; +struct Struct_commande_c_bus *buffer_cde; + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CDE_BACKLIGHT; + + consigne = (uint16_t) (consigne_ecran * 100); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(consigne & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(consigne >> 8); // MSB + + consigne = (uint16_t) (consigne_leds * 100); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(consigne & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(consigne >> 8); // MSB + + buffer_cde->priorite = priorite; + + +#ifdef __DEBUG_COMMANDES_C_BUS + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Commande backlight envoyée : consigne écran = %.2f, consigne leds = %.2f | consigne écran = %.2f, consigne leds = %.2f", consigne_ecran, consigne_leds, ecran.consigne_backlight, ecran.consigne_leds); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au master de commander un ou plusieurs circuits de puissance. +// Reçoit : Le numéro de carte dont les circuits sont commandés (>0, adresse de la carte sur K-Bus) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_cde_circuits(uint8_t adresse_module) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; +uint16_t i; +struct { + uint8_t consigne; + uint8_t mode; +} tmp_circuits[MAX_NOMBRE_CIRCUITS]; + + + for (i = 0; i < MAX_NOMBRE_CIRCUITS; i ++) // Valeurs par défaut + { + tmp_circuits[i].consigne = 0; + tmp_circuits[i].mode = SP8XX_MODE_CDE_CIRCUIT_AUTO; + } + + + // Vérification du paramètre reçu : + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + + if (systeme.modules_config[adresse_module].flag_presence == FALSE) return(-1); + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module) continue; + if (circuits.circuits[i].no_circuit >= SP8XX_NOMBRE_CIRCUITS) continue; + + tmp_circuits[circuits.circuits[i].no_circuit].consigne = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante; + // Attention : circuits.circuits[i].mode_cde est DIFFERENT de tmp_circuits[].mode !! Le premier est 0=On-Off, 1=On-Off soft, 2=Variable soft et 3=Cadencé. Le second est 0=TOR, 1=variable, 2= variable soft + // Il faut donc laisser la valeur envoyée via C-Bus à AUTO, ce qui permet d'utiliser le type de Circuit. + + if (circuits.circuits[i].no_circuit_esclave >= 0) // Cas où le circuit a un esclave + { + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].consigne = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante; // Utile si le traitement de l'esclave a déjà été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante; // Utile si le traitement de l'esclave n'a pas encore été fait + } + } + + break; + } + + case TYPE_CARTE_SP4AC : + { + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module) continue; + if (circuits.circuits[i].no_circuit >= SP4AC_NOMBRE_CIRCUITS) continue; + + tmp_circuits[circuits.circuits[i].no_circuit].consigne = circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante; + } + + break; + } + default : return(-1); + } + + + // Préparation de la commande : + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CDE_CIRCUITS; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + for (i = 0; i < SP8XX_NOMBRE_CIRCUITS; i ++) // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].consigne; + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].mode; + +#ifdef __DEBUG_COMMANDES_C_BUS + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Commande des circuits sur carte %i, circuit %i : consigne=%i, mode=%i", adresse_module, (i+1), tmp_circuits[i].consigne, tmp_circuits[i].mode); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + break; + } + + case TYPE_CARTE_SP4AC : + { + for (i = 0; i < SP4AC_NOMBRE_CIRCUITS; i ++) // + { + buffer_cde->cde[buffer_cde->lg_cde ++] = tmp_circuits[i].consigne; + +#ifdef __DEBUG_COMMANDES_C_BUS +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +sprintf(tmp, "Commande des circuits sur carte %i, circuit %i : consigne=%i", adresse_module, (i+1), tmp_circuits[i].consigne); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + break; + } + + default :; + } + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au CM3+ de commander le Compagnon ou une carte sur le K-Bus +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_cde_carte(uint8_t adresse_module, uint8_t cde, uint8_t consigne) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + // Vérification des paramètres : + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + + switch(cde) + { + case C_BUS_COMMANDE_CDE_CARTE_LOCALISATION : // Localisation + { + if ((consigne != 0) && (consigne != 1)) return(-1); + break; + } + case C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME : // Info sur l'état du système : + { + if (consigne > C_BUS_COMMANDE_CDE_CARTE_MAX_CONSIGNE) return (-1); + break; + } + default : return(-1); + } + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CDE_CARTE; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + buffer_cde->cde[buffer_cde->lg_cde ++] = cde; + buffer_cde->cde[buffer_cde->lg_cde ++] = consigne; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au CM3+ lire la date et l'heure du Compagnon. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_lecture_date_heure(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_LECTURE_DATE_HEURE; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Fonction qui permet au CM3+ lire les paramètres propres au Compagnon ou à une carte sur le K-Bus +// Reçoit : Le numéro de carte dont les IDs sont demandés (0=Compagnon, >0=adresse de la carte sur K-Bus) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_lecture_id_carte(uint8_t adresse_module) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + if (adresse_module != 0) // La lecture de l'ID du Compagnon est toujours possible + { + if (adresse_module >= MAX_NOMBRE_MODULES) return(-1); + if (systeme.modules_physiques[adresse_module].flag_presence == FALSE) return(-1); // Pour les autres cartes, il faut qu'elles aient préalablment été détectées (par une commande annuaire par exemple) + } + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_LECTURE_ID_CARTE; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Cette commande permet au CM3+ de demander au Compagnon la liste des cartes présentes dans le système en publiant leur adresse réseau et leur adresse MAC (y compris celle du Compagnon) +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_annuaire_k_bus(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_ANNUAIRE_K_BUS; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Permet au CM3+ de charger le code de l'application dans le Compagnon ou dans une carte sur K-Bus +// Reçoit : Le mode de la commande à envoyer (0: effacement de la flash, 1: chargement du bloc suivant, 2: vérification CRC, 3: activation de la MAJ) +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_chargement_code(uint8_t mode, uint8_t adresse_module) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; +FILE * ptr_fic; +uint16_t i; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (mode > CHARGEMENT_CODE_MODE_VALIDATION_VERSION) return (-1); // Mode invalide... + + chargement_code.flag_reponse = FALSE; // Permettra d'activer ce flag à la réception et d'informer ainsi le séquenceur d'upload qui gère tout l'échange. + priorite = PRIORITE_CDE_C_BUS_FAIBLE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_CHARGEMENT_CODE; + buffer_cde->cde[buffer_cde->lg_cde ++] = mode; + buffer_cde->cde[buffer_cde->lg_cde ++] = adresse_module; + + switch(mode) + { + //========================================================================== + // Mode 0 : Effacement de la flash + case CHARGEMENT_CODE_MODE_EFFACEMENT_FLASH: + { + break; + } + + //========================================================================== + // Mode 1 : Chargement d'un bloc de data + case CHARGEMENT_CODE_MODE_CHARGEMENT_BLOC: + { + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)chargement_code.no_bloc; + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(chargement_code.no_bloc >> 8); + + if ((chargement_code.nb_transmis + MAX_TAILLE_BLOC_DATA_CHARGEMENT_CODE) <= chargement_code.taille_fichier) chargement_code.taille_bloc = MAX_TAILLE_BLOC_DATA_CHARGEMENT_CODE; + else chargement_code.taille_bloc = (uint16_t) (chargement_code.taille_fichier - chargement_code.nb_transmis); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)chargement_code.taille_bloc; + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(chargement_code.taille_bloc >> 8); + + sprintf(tmp, "%s/%s", REPERTOIRE_VERSIONS_CURRENT, chargement_code.nom_fichier[chargement_code.adresse_module]); + ptr_fic = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... + if (ptr_fic != NULL) + { + fseek (ptr_fic, chargement_code.nb_transmis, SEEK_SET); // Place le curseur à la prochaine position du fichier à transmettre + for (i = 0; i < chargement_code.taille_bloc; i ++) buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)fgetc(ptr_fic); + } + else + { + sprintf(tmp, "L'ouverture du fichier à charger [%s/%s] n'est pas possible. Le chargement du code est abandonné...", REPERTOIRE_VERSIONS_CURRENT, chargement_code.nom_fichier[chargement_code.adresse_module]); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + fclose(ptr_fic); // Fermeture du fichier + + chargement_code.nb_transmis += chargement_code.taille_bloc; + + break; + } + + //========================================================================== + // Mode 2 : Vérification CRC + case CHARGEMENT_CODE_MODE_CHECK_CRC: + { + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) chargement_code.crc_fichier; + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (chargement_code.crc_fichier >> 8); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (chargement_code.crc_fichier >> 16); + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t) (chargement_code.crc_fichier >> 24); + + break; + } + + //========================================================================== + // Mode 3 : Validation de la nouvelle version + case CHARGEMENT_CODE_MODE_VALIDATION_VERSION: + { + break; + } + + //========================================================================== + default: + { + sprintf(tmp, "Commande 'c_bus_down_commande_chargement_code()' appelée avec un mode inconnu (%i)", mode); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + break; + } + } + + + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au CM3+ de demander au Compagnon l'annuaire des devices présents sur son bus OW +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +//================================================================================================================================================================== +int8_t c_bus_down_commande_annuaire_ow(void) +{ +uint8_t priorite; +struct Struct_commande_c_bus *buffer_cde; + + priorite = PRIORITE_CDE_C_BUS_HAUTE; + + if (c_bus_next_write_buffer_cdes(priorite) != -1) + { + buffer_cde = &c_bus.down.buffer_cdes[c_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + + buffer_cde->lg_cde = 0; + + buffer_cde->cde[buffer_cde->lg_cde ++] = C_BUS_DOWN_COMMANDE_ANNUAIRE_OW; + + buffer_cde->priorite = priorite; + + return(0); + } + else return (-1); +} + + + +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** +// DOWN - Réponses +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** + + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_config_date_heure(void) +{ +uint16_t ptr_reponse; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + //------------------------------------------------------------- + // Analyse de la réponse ; + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + ptr_reponse += 6; // + + if (ptr_reponse != c_bus.down.reponse.lg_reponse) + { + sprintf(tmp, "La longueur de réponse reçue sur C-Bus down n'est pas conforme au décodage réalisé (décodés = %i et reçus = %i)", ptr_reponse, c_bus.down.reponse.lg_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + + + + return(0); +} + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_cde_backlight(void) +{ +uint16_t ptr_reponse; +uint16_t consigne_backlight; +uint16_t consigne_leds; + + + //------------------------------------------------------------- + // Analyse de la réponse ; + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + consigne_backlight = (uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]; // Relecture de la valeur de consigne + consigne_backlight |= (((uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 8); + + consigne_leds = (uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]; // Relecture de la valeur de consigne + consigne_leds |= (((uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 8); + + + // NB : la vérification ci-dessous ne fonctionne pas bien quand la consigne de LEDs et de Backlight sont modifiées simultanément. + // En effet, on met à jour la consigne_leds puis, juste après, la consigne_backlight. + // La première maj génére une commande portant uniquement la nouvelle valeur de leds (et donc l'ancienne de backlight). La réponse revient donc avec l'ancienne valeur de backlight qui + // est comparée avec la valeur en cours de backlight qui, elle, est la nouvelle => erreur => répétition. Au final, la commande générée par le changement de backlight finit par être envoyée et tout est en ordre. + // Pour éviter ces répétitions inutile, on n'émet pas d'erreur en cas de retour non identique. Mais on garde les messages d'erreur pour ne pas oublier... +/* char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + if (systeme.veille == SYSTEME_VEILLE_AUCUNE) // On ne fait le test de relecture de la valeur reçue que si on n'est pas en veille (sinon les valeurs demandées peuvent différer de la consigne) + { + if (((uint16_t)(ecran.consigne_backlight * 100)) != consigne_backlight) + { + sprintf(tmp, "La valeur de backlight reçue du Compagnon est différente de la consigne envoyée [valeur envoyée = %.2f et valeur reçue = %.2f]", ecran.consigne_backlight, (float)consigne_backlight / 100); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + +// return(-1); + return(0); + } + + if (((uint16_t)(ecran.consigne_leds * 100)) != consigne_leds) + { + sprintf(tmp, "La valeur de consigne LEDs reçue du Compagnon est différente de la consigne envoyée [valeur envoyée = %.2f et valeur reçue = %.2f]", ecran.consigne_leds, (float)consigne_leds / 100); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + +// return(-1); + return(0); + } + } +*/ + + if (ptr_reponse != c_bus.down.reponse.lg_reponse) return(-1); // Cas où le nombre d'octets reçus n'est pas correct... + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + + + return(0); +} + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_lecture_date_heure(void) +{ +uint16_t ptr_reponse; +struct Struct_date date; +struct Struct_heure heure; + + //------------------------------------------------------------- + // Analyse de la réponse ; + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != C_BUS_ERREUR_AUCUNE) return(-1); // Code d'erreur qui doit être à 0 + + date.jour = c_bus.down.reponse.reponse[ptr_reponse ++]; // Jour + date.mois = c_bus.down.reponse.reponse[ptr_reponse ++]; // Mois + date.annee = c_bus.down.reponse.reponse[ptr_reponse ++]; // Année + + heure.heures = c_bus.down.reponse.reponse[ptr_reponse ++]; // Heures + heure.minutes = c_bus.down.reponse.reponse[ptr_reponse ++]; // Minutes + heure.secondes = c_bus.down.reponse.reponse[ptr_reponse ++]; // Secondes + + if (ptr_reponse != c_bus.down.reponse.lg_reponse) return (-1); // Cas où on a reçu trop d'octets... + + if ((date.jour == 0) || (date.jour > 31)) return(-1); // On ne contrôle pas les jours vs mois/années bisextiles car ce sera fait par la fonction de mise à l'heure + if ((date.mois == 0) || (date.mois > 12)) return(-1); + if (date.annee < 120) return(-1); // La valeur reçue correspond au nombres d'années depuis 1900 donc on suppose qu'on est au moins en 2020... + if (heure.heures > 23) return(-1); + if (heure.minutes > 59) return(-1); + if (heure.secondes > 59) return(-1); + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + date.annee += 1900; + set_date_heure(date, heure); // Mise à jour de la date/heure système à partir de l'heure reçue du Compagnon qui, elle, est sauvegardée (toujours en UTC !) + +//#ifdef __DEBUG_COMMANDES_C_BUS + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Réception de la date-heure du Compagnon 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); +//#endif + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_lecture_id_carte(void) +{ +uint16_t ptr_reponse; +uint8_t i; +uint8_t adresse_module; +uint8_t type_carte; +uint8_t version_hard; +uint8_t unique_id[MAX_TAILLE_UNIQUE_ID]; +uint16_t flash_size; +uint16_t package; +uint8_t lg_type_soft; +char type_soft[MAX_TAILLE_TYPE_SOFT]; +uint8_t lg_version_soft; +char version_soft[MAX_TAILLE_VERSION_SOFT]; +uint32_t crc; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + //------------------------------------------------------------- + // Analyse de la réponse : + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + adresse_module = c_bus.down.reponse.reponse[ptr_reponse ++]; // Numéro de carte + if (adresse_module >= MAX_NOMBRE_MODULES) + { + sprintf(tmp, "Le numéro de carte reçu sur C-Bus dans la réponse à la commande LECTURE_ID_CARTE [valeur = %i] n'est pas valide [devrait être entre 0 et %i]", adresse_module, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + type_carte = c_bus.down.reponse.reponse[ptr_reponse ++]; // Type de carte + switch(type_carte) + { + case TYPE_CARTE_SHIPBASE : + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SBS : + case TYPE_CARTE_SP4AC : + case TYPE_CARTE_SP8DC : + break; + default : + { + sprintf(tmp, "Le TYPE_CARTE reçu sur C-Bus dans la réponse à la commande LECTURE_ID_CARTE [valeur = %i] n'est pas valide", type_carte); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + version_hard = c_bus.down.reponse.reponse[ptr_reponse ++]; // Version hard de la carte + + for (i = 0; i < MAX_TAILLE_UNIQUE_ID; i ++) unique_id[i] = c_bus.down.reponse.reponse[ptr_reponse ++]; // Unique ID + + flash_size = (uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]; // Taille de la flash (LSB) + flash_size |= (((uint16_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 8); // MSB + + package = c_bus.down.reponse.reponse[ptr_reponse ++]; // Boitier du µC + + lg_type_soft = c_bus.down.reponse.reponse[ptr_reponse ++]; // Longueur de la chaine 'Type soft' + if (lg_type_soft >= MAX_TAILLE_TYPE_SOFT) + { + sprintf(tmp, "La longueur de Type Soft reçue sur C-Bus dans la réponse à la commande LECTURE_ID_CARTE est trop importante (reçu = %i, maxi = %i)", lg_type_soft, MAX_TAILLE_TYPE_SOFT); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + for (i = 0; i < lg_type_soft; i ++) type_soft[i] = c_bus.down.reponse.reponse[ptr_reponse ++]; // Type soft + + lg_version_soft = c_bus.down.reponse.reponse[ptr_reponse ++]; // Longueur de la chaine 'Version soft' + if (lg_version_soft >= MAX_TAILLE_VERSION_SOFT) + { + sprintf(tmp, "La longueur de Version Soft reçue sur C-Bus dans la réponse à la commande LECTURE_ID_CARTE est trop importante (reçu = %i, maxi = %i)", lg_version_soft, MAX_TAILLE_VERSION_SOFT); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + for (i = 0; i < lg_version_soft; i ++) version_soft[i] = c_bus.down.reponse.reponse[ptr_reponse ++]; // Version soft + + crc = (uint32_t) c_bus.down.reponse.reponse[ptr_reponse ++]; // CRC (LSB) + crc |= (((uint32_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 8); + crc |= (((uint32_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 16); + crc |= (((uint32_t) c_bus.down.reponse.reponse[ptr_reponse ++]) << 24); // MSB + + + if (ptr_reponse != c_bus.down.reponse.lg_reponse) + { + sprintf(tmp, "La longueur de réponse reçue sur C-Bus down n'est pas conforme au décodage réalisé (décodés = %i et reçus = %i)", ptr_reponse, c_bus.down.reponse.lg_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + +// systeme.modules_physiques[adresse_module].flag_validite = TRUE; // On considère que si on a reçu une réponse valide à une commande de lecture ID, la carte est valide. + systeme.modules_physiques[adresse_module].type = type_carte; + systeme.modules_physiques[adresse_module].hardware_version = version_hard; + for (i = 0; i < MAX_TAILLE_UNIQUE_ID; i ++) systeme.modules_physiques[adresse_module].hardware_unique_id[i] = unique_id[i]; + systeme.modules_physiques[adresse_module].hardware_flash_size = flash_size; + systeme.modules_physiques[adresse_module].hardware_package = package; + for (i = 0; i < lg_type_soft; i ++) systeme.modules_physiques[adresse_module].soft_type[i] = type_soft[i]; + for (i = 0; i < lg_version_soft; i ++) systeme.modules_physiques[adresse_module].soft_version[i] = version_soft[i]; + systeme.modules_physiques[adresse_module].soft_crc = crc; + + + +write(config_serial1.filestream, "AA", 1); + +#ifdef __DEBUG_COMMANDES_C_BUS +sprintf(tmp, "ID Carte reçu : \n\t no carte \t= %i \n\t type carte \t= %i \n\t version hard \t= %i \n\t flash size \t= %iko \n\t package \t= %i \n\t unique ID \t= %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X \n\t type soft \t= %s \n\t version soft \t= %s \n\t CRC \t\t= %08X", + adresse_module, systeme.modules_physiques[adresse_module].type, systeme.modules_physiques[adresse_module].hardware_version, + systeme.modules_physiques[adresse_module].hardware_flash_size, systeme.modules_physiques[adresse_module].hardware_package, + systeme.modules_physiques[adresse_module].hardware_unique_id[0], systeme.modules_physiques[adresse_module].hardware_unique_id[1], systeme.modules_physiques[adresse_module].hardware_unique_id[2], + systeme.modules_physiques[adresse_module].hardware_unique_id[3], systeme.modules_physiques[adresse_module].hardware_unique_id[4], systeme.modules_physiques[adresse_module].hardware_unique_id[5], + systeme.modules_physiques[adresse_module].hardware_unique_id[6], systeme.modules_physiques[adresse_module].hardware_unique_id[7], systeme.modules_physiques[adresse_module].hardware_unique_id[8], + systeme.modules_physiques[adresse_module].hardware_unique_id[9], systeme.modules_physiques[adresse_module].hardware_unique_id[10], systeme.modules_physiques[adresse_module].hardware_unique_id[11], + systeme.modules_physiques[adresse_module].soft_type, systeme.modules_physiques[adresse_module].soft_version, systeme.modules_physiques[adresse_module].soft_crc); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + return(0); +} + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_annuaire_k_bus(void) +{ +uint16_t ptr_reponse; +uint16_t i, j; +uint8_t adresse_module; +uint8_t r_term; +uint8_t no_champ; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +struct { + uint8_t flag_presence; + uint8_t adresse_bus; + uint8_t adresse_mac[TAILLE_ADRESSE_MAC]; + uint8_t flag_r_term; +} cartes[MAX_NOMBRE_MODULES]; + +#define C_BUS_DOWN_REPONSE_ANNUAIRE_K_BUS_SANS_R_TERM 0 // Ancienne version de commande, sans le champ R_Term +#define C_BUS_DOWN_REPONSE_ANNUAIRE_K_BUS_AVEC_R_TERM 1 // Version de commande avec R_Term (depuis V2.0.0). + + + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + cartes[i].flag_presence = FALSE; + cartes[i].flag_r_term = FALSE; + } + + //------------------------------------------------------------- + // Analyse de la réponse : + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + // Détermination du format de réponse pour s'adapter + if ((c_bus.down.reponse.lg_reponse - 10) % 8 == 0) systeme.flag_r_term_etat_carte = false; // Cas pour les Versions >= 2.0.0 + else systeme.flag_r_term_etat_carte = true; // Compatibilité avec les versions < 2.0.0 + +#ifdef __DEBUG_COMMANDES_C_BUS + sprintf(tmp, "Réponse Annuaire reçue au format %s", systeme.flag_r_term_etat_carte ? "SANS R_Term" : "AVEC R_Term"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + no_champ = 0; + adresse_module = 0; + while (ptr_reponse < c_bus.down.reponse.lg_reponse) + { + switch(no_champ) + { + case 0: // N° de carte : + { + adresse_module = c_bus.down.reponse.reponse[ptr_reponse ++]; + if (adresse_module >= MAX_NOMBRE_MODULES) + { + sprintf(tmp, "Le numéro de carte reçu est trop élevé (reçu = %i et maxi = %i)", adresse_module, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + else cartes[adresse_module].flag_presence = TRUE; + no_champ ++; + break; + } + + case 1: // Résistance de terminaison : + { + // Si la réponse est de l'ancien format, on force l'avancement d'un octet de l'analyse de la réponse pour sauter R_Term sans changer l'algo : + if (systeme.flag_r_term_etat_carte) + { + no_champ ++; + break; + } + + r_term = c_bus.down.reponse.reponse[ptr_reponse ++]; + if ((r_term != 0) && (r_term != 1)) + { + sprintf(tmp, "L'état de Résistance de Terminaison de la carte %i n'est pas valide (reçu = %i)", adresse_module, r_term); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + else cartes[adresse_module].flag_r_term = r_term; + no_champ ++; + break; + } + + case 2: // Adresse MAC #1 à #5 : + case 3: + case 4: + case 5: + case 6: + { + cartes[adresse_module].adresse_mac[no_champ - 2] = c_bus.down.reponse.reponse[ptr_reponse ++]; + no_champ ++; + break; + } + case 7: // Adresse MAC #6 + { + cartes[adresse_module].adresse_mac[no_champ - 2] = c_bus.down.reponse.reponse[ptr_reponse ++]; + no_champ = 0; + break; + } + } + + } + + if (no_champ != 0) // Si la trame est conforme, on doit arriver au bout en pointant sur un no_champ = 0 + { + sprintf(tmp, "La longueur de réponse reçue sur C-Bus down n'est pas conforme au décodage réalisé (décodés = %i et no_champ = %i)", ptr_reponse, no_champ); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + systeme.modules_physiques[i].flag_presence = cartes[i].flag_presence; // On met tous les flags de présence à jour (TRUE ou FALSE). NB : La mise à FALSE est normalement déjà faite dans 'bdd_init()', même en cas de reconfig + + if (cartes[i].flag_presence == FALSE) continue; // Si le module n'est pas présent, il est inutile de stocker les infos du module + + for (j = 0; j < TAILLE_ADRESSE_MAC; j ++) systeme.modules_physiques[i].adresse_mac[j] = cartes[i].adresse_mac[j]; + systeme.modules_physiques[i].resistance_terminaison = cartes[i].flag_r_term; // NB : dans le cas d'un retour C_BUS_DOWN_REPONSE_ANNUAIRE_K_BUS_SANS_R_TERM, toutes les valeurs seront à FALSE tant que le Compagnon n'aura pas été mis à jour... + } + + +#ifdef __DEBUG_COMMANDES_C_BUS +char chaine[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +char chaine_adresse_mac[30]; + sprintf(tmp, "Réponse Annuaire reçue de la part du Compagnon : "); + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; + + sprintf(chaine, "\n\tCarte à l'adresse %i\t: adresse MAC = %s, R_term = %s", i, conversion_adresse_mac_chaine(systeme.modules_physiques[i].adresse_mac, chaine_adresse_mac, ':'), (systeme.modules_physiques[i].resistance_terminaison ? "OUI" : "Non")); + strcat(tmp, chaine); + } + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + return(0); +} + + + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_chargement_code(void) +{ +uint16_t ptr_reponse; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +uint8_t ret; + + //------------------------------------------------------------- + // Analyse de la réponse : + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + ret = c_bus.down.reponse.reponse[ptr_reponse ++]; + + chargement_code.status_reponse = ret; + + if (ret == 0) + { + chargement_code.flag_reponse = TRUE; + + return(0); + } + else + { + sprintf(tmp, "Le retour de commande de chargement de bloc indique une erreur (%i). L'info d'erreur est descendue au protocole afin qu'il répète la commande", ret); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); // Dans ce cas, le protocole va répéter la commande autantd e fois qu'il en a le droit... Si aucun de ces essais n'est concluant, il y aura déclenchement du type-out vers 'chargement_code' + } +} + + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_annuaire_ow(void) +{ +uint16_t ptr_reponse; +uint16_t i; +uint8_t nb_devices, nb_devices_recu; +uint8_t no_champ; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +struct { + uint8_t rom_code[TAILLE_ROM_CODE_OW]; +} devices[MAX_NOMBRE_DEVICES_OW]; + + + //------------------------------------------------------------- + // Analyse de la réponse : + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + nb_devices_recu = c_bus.down.reponse.reponse[ptr_reponse ++]; // Nombre de devices + + // Cas d'une erreur matérielle sur le bus OW : + if (nb_devices_recu == 0xFF) // Cas d'une anomalie sur le bus OW qui empêche de faire la reconnaissance réseau + { + entrees.ow.defaut_bus = OW_DEFAUT_BUS_COURT_CIRCUIT; + enregistrement_annuaire_ow(0xFF); // L'appel de la fonction avec FFh en paramètre va permettre de mettre à jour TB_One_wire avec l'info de défaut du bus + + return(0); // Fin sans erreur (au niveau protocole). Il est inutile d'aller plus loin dans le traitement. + } + + // Cas normal : + if (nb_devices_recu > MAX_NOMBRE_DEVICES_OW) return(-1); + + no_champ = 0; + nb_devices = 0; + while (ptr_reponse < c_bus.down.reponse.lg_reponse) + { + switch(no_champ) + { + case 0: // N° de device + { + i = c_bus.down.reponse.reponse[ptr_reponse ++]; // Ce n° n'est pas utilisé en pratique. Les valeurs reçues sont stockées par ordre d'arrivée dans 'devices[]'. + + // Check du nombre de devices reçus : + if (nb_devices >= MAX_NOMBRE_DEVICES_OW) + { + sprintf(tmp, "Le nombre de devices reçu est trop élevé (maxi = %i). La commande est rejetée", MAX_NOMBRE_ENTREES_OW); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + entrees.ow.defaut_bus = OW_DEFAUT_BUS_TROP_DEVICES; + enregistrement_annuaire_ow(0xFF); // L'appel de la fonction avec FFh en paramètre va permettre de mettre à jour TB_One_wire avec l'info de défaut du bus + + return(-1); + } + + no_champ ++; + break; + } + case 1: // ROM Code #1 à #7 : + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + { + devices[nb_devices].rom_code[no_champ - 1] = c_bus.down.reponse.reponse[ptr_reponse ++]; + no_champ ++; + break; + } + case 8: // ROM Code #8 + { + devices[nb_devices].rom_code[no_champ - 1] = c_bus.down.reponse.reponse[ptr_reponse ++]; + no_champ = 0; + nb_devices ++; + break; + } + } + } + + if (no_champ != 0) // Si la trame est conforme, on doit arriver au bout en pointant sur un no_champ = 0 + { + sprintf(tmp, "La longueur de réponse reçue sur C-Bus down n'est pas conforme au décodage réalisé (décodés = %i et no_champ = %i)", ptr_reponse, no_champ); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + if (nb_devices != nb_devices_recu) // Si la trame est conforme, la valeur de nb_devices reçue doit être égale au nombre réellement reçus + { + sprintf(tmp, "Le nombre de devices reçus dans l'annuaire ne correspond pas au nombre indiqué en début de trame (décodés = %i et nombre reçu = %i)", nb_devices, nb_devices_recu); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + for (i = 0; i < nb_devices; i ++) + { + conversion_rom_code_chaine(devices[i].rom_code, entrees.ow.devices[i].rom_code, ':'); + } + + entrees.ow.defaut_bus = OW_DEFAUT_BUS_AUCUN; // Si on a reçu des devices, il ne peut pas y avoir de défat bus... + enregistrement_annuaire_ow(nb_devices); + +#ifdef __DEBUG_COMMANDES_C_BUS +char chaine[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + +sprintf(tmp, "Réponse Annuaire OW reçue de la part du Compagnon : "); + +for (i = 0; i < nb_devices; i ++) +{ + sprintf(chaine, "\n\tDevice OW n° %i avec le ROM Code = %s ", i, entrees.ow.devices[i].rom_code); + strcat(tmp, chaine); +} +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + return(0); +} + + +//------------------------------------------------------------------------------------------------------------------------------------ + + +//================================================================================================================================================================== +// Fonction : Est appelé après réception d'une réponse à une commande et détection du code commande valide en premier caractère. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_reponse_generique(void) +{ +uint16_t ptr_reponse; + + //------------------------------------------------------------- + // Analyse de la réponse ; + ptr_reponse = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + + if (c_bus.down.reponse.reponse[ptr_reponse ++] != 0) return(-1); // Code d'erreur qui doit être à 0 + + if (ptr_reponse != c_bus.down.reponse.lg_reponse) return (-1); // Cas où on a reçu trop d'octets... + + + //------------------------------------------------------------- + // Traitement de la réponse si aucune erreur n'a été détectée : + + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Appelée lorsqu'une réponse a été reçue, pour déterminer le code de la commande commande concernée. +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a détecté une ou pluisieurs erreurs. +//================================================================================================================================================================== +int8_t c_bus_down_recherche_reponse(void) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + switch(c_bus.down.reponse.reponse[0]) + { + //--------------------------------------------- + case C_BUS_DOWN_COMMANDE_CONFIG_DATE_HEURE : { return (c_bus_down_reponse_config_date_heure()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_CARTE : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_CIRCUITS : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_LEVEL_SENSORS : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_OW : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_BATTERIES : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CONFIG_PGN_NMEA : { return (c_bus_down_reponse_generique()); break; } + + case C_BUS_DOWN_COMMANDE_RESET : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CDE_BACKLIGHT : { return (c_bus_down_reponse_cde_backlight()); break; } + case C_BUS_DOWN_COMMANDE_CDE_CIRCUITS : { return (c_bus_down_reponse_generique()); break; } + case C_BUS_DOWN_COMMANDE_CDE_CARTE : { return (c_bus_down_reponse_generique()); break; } + + case C_BUS_DOWN_COMMANDE_LECTURE_DATE_HEURE : { return (c_bus_down_reponse_lecture_date_heure()); break; } + case C_BUS_DOWN_COMMANDE_LECTURE_ID_CARTE : { return (c_bus_down_reponse_lecture_id_carte()); break; } + + case C_BUS_DOWN_COMMANDE_ANNUAIRE_K_BUS : { return (c_bus_down_reponse_annuaire_k_bus()); break; } + case C_BUS_DOWN_COMMANDE_CHARGEMENT_CODE : { return (c_bus_down_reponse_chargement_code()); break; } + case C_BUS_DOWN_COMMANDE_ANNUAIRE_OW : { return (c_bus_down_reponse_annuaire_ow()); break; } + + default : + { + sprintf(tmp, "La réponse reçue sur C-Bus Down comprend un code commande inconnu [%i]", c_bus.down.reponse.reponse[0]); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + return (-1); +} + + + + +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** +// UP +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter son état ou celui d’une carte extension au CM3+ +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_carte(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t i; +uint16_t ptr_cde; +uint8_t lsb, msb, lmsb, mlsb; +uint8_t adresse_module; +uint8_t alim_etat = 0; +uint8_t alim_cde = 0; +float in_v = 0; // Utilisé aussi pour alim_valeur ; on évite de créer deux variables pour la même valeur. +float in_i = 0; +float masses_i = 0; // SDIR #138 +float usb_i = 0; +float temp = 0; +float hum = 0; +float pat = 0; +float acc_x = 0; +float acc_y = 0; +float acc_z = 0; +uint8_t orig; +uint8_t mode_test = 0; // sinon warning à la compilation +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + adresse_module = commande->cde[ptr_cde ++]; // + +// if ((adresse_module >= MAX_NOMBRE_MODULES) || (systeme.modules_config[adresse_module].flag_validite == FALSE)) + if (adresse_module >= MAX_NOMBRE_MODULES) // Le fait de recevoir un état carte d'un module non valide n'est pas une erreur en soit (le module peut être présent mais pas utilisé par la config) + { + sprintf(tmp, "Erreur : n° de carte = %i (Min = 0 et Max = %i ou carte dévalidée)", adresse_module, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_ERREUR_NO_CARTE; + break; + } + + if (!systeme.modules_config[adresse_module].flag_validite) break; // Dans ce cas, on ne déclenche pas une erreur mais il est inutile d'effectuer le traitement (d'autant qu'on ne connait pas la nature du module donc on arrivera probablement à une erreur de format...) + + + if (adresse_module == 0) // Compagnon : <état nmea> + { + lsb = 0; // Permet d'éviter un warning que je ne comprends pas... + + //------------------------------------------------------------- + // Etat de l'alimentation : + //------------------------------------------------------------- + alim_etat = commande->cde[ptr_cde ++]; + + //------------------------------------------------------------- + // Tension entrée ShipBase : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + in_v = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mV à V + + //------------------------------------------------------------- + // Courant ShipBase : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + in_i = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mA à A + + //------------------------------------------------------------- + // Courant USB : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + usb_i = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mA à A + if (usb_i < 0.010) usb_i = 0; // Plancher sur le courant dans USB pour éviter de trainer des valeurs très faibles non significatives. + + //------------------------------------------------------------- + // Température : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temp = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 100; // La valeur reçue est en centièmes de °C + if ((temp <= TEMPERATURE_VALEUR_MINI) || (temp >= TEMPERATURE_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Pression : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + lmsb = commande->cde[ptr_cde ++]; + mlsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + pat = ((float)((((uint32_t) msb) << 24) | (((uint32_t) mlsb) << 16) | (((uint32_t) lmsb) << 8) | ((uint32_t) lsb))) / 100; // La valeur reçue est en Pa + if ((pat < PRESSION_ATM_VALEUR_MINI) || (pat > PRESSION_ATM_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Humidité : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + hum = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 100; // La valeur reçue est en centièmes de % + if ((hum < HUMIDITE_VALEUR_MINI) || (hum > HUMIDITE_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Accélération X : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + acc_x = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 1000; // La valeur reçue est en mg + if ((acc_x <= ACCELERATION_VALEUR_MINI) || (acc_x >= ACCELERATION_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Accélération Y : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + acc_y = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 1000; // La valeur reçue est en mg + if ((acc_y <= ACCELERATION_VALEUR_MINI) || (acc_y >= ACCELERATION_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Accélération Z : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + acc_z = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 1000; // La valeur reçue est en mg + if ((acc_z <= ACCELERATION_VALEUR_MINI) || (acc_z >= ACCELERATION_VALEUR_MAXI)) // Test de validité + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Origine de la commande : + //------------------------------------------------------------- + orig = commande->cde[ptr_cde ++]; + +//etat_nmea = commande->cde[ptr_cde ++]; + +// Debug : +// printf("Etat du Compagnon reçu : Etat alim = %i, Tension = %6.3f V, Courant=%6.3f A, Courant USB=%5.3f A, Température = %5.2f °C, Humidité=%5.2f %%RH, Pression=%7.2f hPa, Origine reset = %i \n", alim_etat, in_v, in_i, usb_i, temp, hum, pat, orig); + + + } + else // Autre carte : + { + lsb = 0; // Permet d'éviter un warning que je ne comprends pas... + + // Paramètres spécifiques à certaines cartes : + switch(systeme.modules_config[adresse_module].type) + { + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP812 : + { + //------------------------------------------------------------- + // Etat de l'alimentation : + //------------------------------------------------------------- + alim_etat = commande->cde[ptr_cde ++]; + + //------------------------------------------------------------- + // Tension d'alimentation : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + in_v = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mV à V + + //------------------------------------------------------------- + // Température : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temp = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 100; // La valeur reçue est en centièmes de °C + if ((temp <= TEMPERATURE_VALEUR_MINI) || (temp >= TEMPERATURE_VALEUR_MAXI)) // Test de validité + { +sprintf(tmp, "Erreur sur carte %i : Temp = %f (Min = %f et Max = %f)", adresse_module, temp, TEMPERATURE_VALEUR_MINI, TEMPERATURE_VALEUR_MAXI); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + //------------------------------------------------------------- + // Mode test : + //------------------------------------------------------------- + mode_test = commande->cde[ptr_cde ++]; + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP8DC : + { + //------------------------------------------------------------- + // Etat de l'alimentation : + //------------------------------------------------------------- + alim_etat = commande->cde[ptr_cde ++]; + + //------------------------------------------------------------- + // Tension d'alimentation : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + in_v = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mV à V + + //------------------------------------------------------------- + // Température : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temp = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 100; // La valeur reçue est en centièmes de °C + if ((temp <= TEMPERATURE_VALEUR_MINI) || (temp >= TEMPERATURE_VALEUR_MAXI)) // Test de validité + { +sprintf(tmp, "Erreur sur carte %i : Temp = %f (Min = %f et Max = %f)", adresse_module, temp, TEMPERATURE_VALEUR_MINI, TEMPERATURE_VALEUR_MAXI); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + // SDIR #138 : Début + //------------------------------------------------------------- + // Courant de masses : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + masses_i = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mA à A + // SDIR #138 : Fin + + //------------------------------------------------------------- + // Mode test : + //------------------------------------------------------------- + mode_test = commande->cde[ptr_cde ++]; + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SBS : + { + //------------------------------------------------------------- + // Cde alimentation 5V2 : + //------------------------------------------------------------- + alim_cde = commande->cde[ptr_cde ++]; + + //------------------------------------------------------------- + // Etat de la tension d'alimentation : + //------------------------------------------------------------- + alim_etat = commande->cde[ptr_cde ++]; + + //------------------------------------------------------------- + // Valeur de tension d'elimentation : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + in_v = ((float)((((uint16_t) msb) << 8) | ((uint16_t) lsb))) / 1000; // Passage de mV à V + + //------------------------------------------------------------- + // Température : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temp = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 100; // La valeur reçue est en centièmes de °C + if ((temp <= TEMPERATURE_VALEUR_MINI) || (temp >= TEMPERATURE_VALEUR_MAXI)) // Test de validité + { +sprintf(tmp, "Erreur sur carte %i : Temp = %f (Min = %f et Max = %f)", adresse_module, temp, TEMPERATURE_VALEUR_MINI, TEMPERATURE_VALEUR_MAXI); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP4AC : + { + //------------------------------------------------------------- + // Température : + //------------------------------------------------------------- + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temp = ((float)((int16_t) ((((uint16_t) msb) << 8) | ((uint16_t) lsb)))) / 100; // La valeur reçue est en centièmes de °C + if ((temp <= TEMPERATURE_VALEUR_MINI) || (temp >= TEMPERATURE_VALEUR_MAXI)) // Test de validité + { +sprintf(tmp, "Erreur sur carte %i : Temp = %f (Min = %f et Max = %f)", adresse_module, temp, TEMPERATURE_VALEUR_MINI, TEMPERATURE_VALEUR_MAXI); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + break; + } + + //-------------------------------------------------------------------------------- + default: break; + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + // Paramètres toujours présents en fin de commande : + if(systeme.flag_r_term_etat_carte) ptr_cde ++; // Conservé uniquement pour des raisons de compatibilité au moment des mises à jour > 2.0.0 + orig = commande->cde[ptr_cde ++]; + +// DEBUG : +//printf("Etat de la carte %i reçu : Etat alim = %i, Tension = %6.3f V, Température = %5.2f °C, Mode test = %i, Origine reset = %i, Courant de masses = %.3f A (ptr_cde=%i et lg_cde=%i)\n", +// adresse_module, alim_etat, in_v, temp, mode_test, orig, masses_i, ptr_cde, commande->lg_cde); + + } + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, retour NOK avec code d'erreur. + { + if (systeme.modules_config[adresse_module].flag_validite) // Le fait de recevoir un Etat Carte d'un module non valide n'est pas une erreur. Mais ne nécessite pas d'être traité pour autant... + { +// Version corrigée en altitude : +// uint8_t coeff_correction_pat +// coeff_correction_pat = 79 + (0.002888888 * entrees.predefinies[adresse_module].temp); // Avec 'entrees.predefinies[adresse_module].temp' exprimée en centièmes de °C +// entrees.predefinies[adresse_module].pat = pat + (entrees.predefinies[adresse_module].alt / coeff_correction_pat); // Compensation d'environ 1 hpa (soit 100pa) / 8m d'altitude +// Version non corrigée en altitude : +// entrees.predefinies[adresse_module].pat = pat; +// printf("Pression non corrigée = %i, altitude = %i, température=%i, coeff correcteur = %i, pression corrigée = %i\n", pat, entrees.predefinies[adresse_module].alt, entrees.predefinies[adresse_module].temp, entrees.predefinies[adresse_module].coeff_correction_pat, entrees.predefinies[adresse_module].pat); + + // Mise à jour des entrées prédéfinies : + for (i = 0; i < entrees.predefinies.nb; i ++) + { + if (!entrees.predefinies.entrees[i].flag_validite) continue; // On ne traite que les entrées prédéfinies valides... + if (entrees.predefinies.entrees[i].module != adresse_module) continue; //... et celles affectées au module en cours. + + switch(entrees.predefinies.entrees[i].no) + { + case ENTREE_PREDEFINIE_PAT: { entrees.predefinies.entrees[i].valeur = pat; break; } + case ENTREE_PREDEFINIE_HUM: { entrees.predefinies.entrees[i].valeur = hum; break; } + case ENTREE_PREDEFINIE_TEMP: { entrees.predefinies.entrees[i].valeur = temp; break; } + case ENTREE_PREDEFINIE_IN_V: { entrees.predefinies.entrees[i].valeur = in_v; break; } + case ENTREE_PREDEFINIE_IN_I: { entrees.predefinies.entrees[i].valeur = in_i; break; } + case ENTREE_PREDEFINIE_USB_I: { entrees.predefinies.entrees[i].valeur = usb_i; break; } + case ENTREE_PREDEFINIE_ACC_XYZ: // NB : On n'a PAS reçu la valeur de ACC_XYZ. Mais si on vient de recevoir l'état de ShipBase, et si l'entrée ACC_XYZ est valide, alors on calcule ACC_XYZ à partir des accélérations X, Y et Z que l'on vient de recevoir de ShipBase. + { + entrees.predefinies.entrees[i].valeur = sqrt(carre(acc_x) + carre(acc_y) + carre(acc_z)) - 1; // La soustraction de 1g permet d'avoir une valeur moyenne des chocs = 0 + break; + } + case ENTREE_PREDEFINIE_ACC_X: { entrees.predefinies.entrees[i].valeur = acc_x; break; } + case ENTREE_PREDEFINIE_ACC_Y: { entrees.predefinies.entrees[i].valeur = acc_y; break; } + case ENTREE_PREDEFINIE_ACC_Z: { entrees.predefinies.entrees[i].valeur = acc_z; break; } + } + } + + // Paramètres spécifiques à certaines cartes : + switch(systeme.modules_config[adresse_module].type) + { + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SHIPBASE : + { + // Etat de la tension d'alimentation : + systeme.modules_config[adresse_module].alim_etat = alim_etat; + + // Valeur de tension d'alimentation : + systeme.modules_config[adresse_module].alim_valeur = in_v; + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP812 : + { + // Etat de la tension d'alimentation : + systeme.modules_config[adresse_module].alim_etat = alim_etat; + + // Valeur de tension d'alimentation : + systeme.modules_config[adresse_module].alim_valeur = in_v; + + // Mode test : + systeme.modules_config[adresse_module].mode_test = mode_test; + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP8DC : + { + // Etat de la tension d'alimentation : + systeme.modules_config[adresse_module].alim_etat = alim_etat; + + // Valeur de tension d'alimentation : + systeme.modules_config[adresse_module].alim_valeur = in_v; + + // SDIR #318 : Début + // Etat du courant de masses : + systeme.modules_config[adresse_module].courant_masses = masses_i; + // SDIR #318 : Fin + + // Mode test : + systeme.modules_config[adresse_module].mode_test = mode_test; + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SBS : + { + // Cde alimentation 5V2 : + systeme.modules_config[adresse_module].alim_cde = alim_cde; + + // Etat de la tension d'alimentation : + systeme.modules_config[adresse_module].alim_etat = alim_etat; + + // Valeur de tension d'alimentation : + // Attention : sur les SBS, la tension mesurée au niveau de la carte est le 5V. + // Pour avoir la tension batterie, il faut utiliser l'état batterie (et non état carte comme ici). + systeme.modules_config[adresse_module].alim_valeur = in_v; + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP4AC : + { + + break; + } + + //-------------------------------------------------------------------------------- + default: break; + } + + // Origine du reset : + systeme.modules_config[adresse_module].origine_reset = orig; + } + } + else + { + sprintf(tmp, "La commande C_BUS_UP_COMMANDE_ETAT_CARTE reçue sur C-Bus Up de la carte %i n'est pas conforme (erreur = %i)", adresse_module, erreur); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_CARTE; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter au CM3+ l’état des Circuits +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_circuits(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t lsb, msb; +uint8_t adresse_module; +struct { + uint8_t consigne; + uint8_t etat; + uint16_t tension; + uint16_t intensite; + uint16_t frequence; + uint8_t mode_test; +} tmp_circuits[MAX_NOMBRE_CIRCUITS]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + adresse_module = commande->cde[ptr_cde ++]; // + if ((adresse_module >= MAX_NOMBRE_MODULES) || (systeme.modules_config[adresse_module].flag_validite == FALSE)) + { + erreur = C_BUS_ERREUR_NO_CARTE; + break; + } + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + { + for (i = 0; i < SP8XX_NOMBRE_CIRCUITS; i++) // <état 1> + { + tmp_circuits[i].consigne = commande->cde[ptr_cde ++]; // Consigne + + tmp_circuits[i].etat = commande->cde[ptr_cde ++]; // Etat + + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + tmp_circuits[i].intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); // Intensité + + tmp_circuits[i].mode_test = commande->cde[ptr_cde ++]; // Mode test + } + + break; + } + + case TYPE_CARTE_SP4AC: + { + for (i = 0; i < SP4AC_NOMBRE_CIRCUITS; i++) // <état> + { + tmp_circuits[i].consigne = commande->cde[ptr_cde ++]; // Consigne + + tmp_circuits[i].etat = commande->cde[ptr_cde ++]; // Etat + + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + tmp_circuits[i].tension = (((uint16_t) msb) << 8) | ((uint16_t) lsb); // Tension (en dixièmes de V) + + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + tmp_circuits[i].intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); // Intensité (en mA) + + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + tmp_circuits[i].frequence = (((uint16_t) msb) << 8) | ((uint16_t) lsb); // Fréquence (en centièmes de Hz) + } + + break; + } + default: erreur = C_BUS_ERREUR_NO_CARTE; + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== +// printf("Commande état circuit reçue pour la carte %i et erreur = %i", adresse_module, erreur); + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + { +// char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +// sprintf(tmp, "Etat circuit reçu sur C_Bus pour la carte #%i : ", adresse_module); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On ne traite que les circuits valides... NB : On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module) continue; // ... et ceux piloté par cette carte... + if (circuits.circuits[i].no_circuit >= SP8XX_NOMBRE_CIRCUITS) continue; // ... et dont le n° de circuit est valide pour cette carte + + // Mise à jour de l'historique si besoin : + if ((circuits.circuits[i].etat != ETAT_CIRCUIT_LIMITATION) && // Si on est déjà en Limitation, on ne traite pas les retours du SP8XX (l'état sera RAZ lors du prochain changement de consigne). + (circuits.circuits[i].etat != ETAT_CIRCUIT_DELESTE)) // Idem pour le Delestage (Limitation et Delestage sont gérés par l'appli) + { + if (tmp_circuits[circuits.circuits[i].no_circuit].etat != circuits.circuits[i].etat) + { + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, (CIRCUIT_OK + tmp_circuits[circuits.circuits[i].no_circuit].etat), circuits.circuits[i].cle_primaire, circuits.circuits[i].intensite_seuil_haut, NULL); + circuits.circuits[i].etat = tmp_circuits[circuits.circuits[i].no_circuit].etat; + } + } + if ((!circuits.circuits[i].flag_changement_consigne_en_cours) && // SDIR #179 + (circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante != tmp_circuits[circuits.circuits[i].no_circuit].consigne)) + { + circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante = tmp_circuits[circuits.circuits[i].no_circuit].consigne; + char date_vide[MAX_LONGUEUR_DATE_COURANTE] = ""; // Evite un warning depuis GCC14 + enregistrement_mesure(SOURCE_MESURE_CIRCUIT, i, CIRCUIT_VARIABLE_CONSIGNE, date_vide, BDD_ENREGISTREMENT_MESURE_PONCTUEL); + } + + circuits.circuits[i].mode_test = tmp_circuits[circuits.circuits[i].no_circuit].mode_test; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = ((float)tmp_circuits[circuits.circuits[i].no_circuit].intensite) / 1000; // Cas normal. Le /1000 permet de passer de la valeur reçue en mA à des A + if (circuits.circuits[i].no_circuit_esclave >= 0) // Cas où le circuit a un esclave + { + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante += ((float)tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].intensite) / 1000; // Ne fonctionne que parce que l'esclave est FORCEMENT sur la même carte !! + tmp_circuits[circuits.circuits[circuits.circuits[i].no_circuit_esclave].no_circuit].intensite = 0; // Utile si le traitement de l'esclave n'a pas encore été fait + circuits.circuits[circuits.circuits[i].no_circuit_esclave].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = 0; // Utile si le traitement de l'esclave a déjà été fait + } + + if (circuits.circuits[i].flag_changement_consigne_en_cours) circuits.circuits[i].flag_changement_consigne_en_cours = false; // RAZ pour prochains retours d'état (SDIR #179) +// DEBUG : +// printf("module # %i | circuit #%i : courant = %i mA, soit %.3f A\n", +// circuits.circuits[i].adresse_module, circuits.circuits[i].no_circuit, tmp_circuits[circuits.circuits[i].no_circuit].intensite, +// circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante); + } + + break; + } + case TYPE_CARTE_SP4AC: + { +// sprintf(tmp, "Etat circuit reçu sur C_Bus pour la carte #%i :", adresse_module); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + for (i = 0; i < circuits.nb; i ++) + { + if (circuits.circuits[i].flag_validite == FALSE) continue; // On ne traite que les circuits valides... NB : On teste le flag de Validité (plutôt que Disponible) car on est sûr que la carte est présente (donc si le circuit est valide, il est disponible) + if (circuits.circuits[i].adresse_module != adresse_module ) continue; // ... et ceux piloté par cette carte... + if (circuits.circuits[i].no_circuit >= SP4AC_NOMBRE_CIRCUITS) continue; // ... et dont le n° de circuit est valide pour cette carte + + // Mise à jour de l'historique si besoin : + if (tmp_circuits[circuits.circuits[i].no_circuit].etat != circuits.circuits[i].etat) + { +// enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, (CIRCUIT_OK + tmp_circuits[circuits.circuits[i].no_circuit].etat), circuits.circuits[i].cle_primaire, circuits.circuits[i].intensite_seuil_haut, NULL); +// A VERIFIER !!!! + circuits.circuits[i].etat = tmp_circuits[circuits.circuits[i].no_circuit].etat; + } + + circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante = tmp_circuits[circuits.circuits[i].no_circuit].consigne; + circuits.circuits[i].etat = tmp_circuits[circuits.circuits[i].no_circuit].etat; + circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante = ((float)tmp_circuits[circuits.circuits[i].no_circuit].tension) / 10; // Pour passer de dixèmes de Volts à des Volts + circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = ((float)tmp_circuits[circuits.circuits[i].no_circuit].intensite) / 1000; // Pour passer de mA à A + circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante = ((float)tmp_circuits[circuits.circuits[i].no_circuit].frequence) / 100; // Pour passer de centièmes de Hz à des Hertz +// DEBUG : +// printf("%i : consigne = %.0f, etat = %i, tension = %.1fV, intensite = %.3fA, frequence = %.2fHz\n", +// circuits.circuits[i].no_circuit, circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante, circuits.circuits[i].etat, +// circuits.circuits[i].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante, circuits.circuits[i].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante, +// circuits.circuits[i].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante); + } + + break; + } + + + default: erreur = C_BUS_ERREUR_NO_CARTE; + } + } + + + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_CIRCUITS; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter au CM3+ l’état des entrées Auxiliaires. +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_entrees_aux(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t val[MAX_NOMBRE_ENTREES_AUXILIAIRES]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + for (i=0; i < MAX_NOMBRE_ENTREES_AUXILIAIRES; i++) + { + // Lecture de la valeur de l'entrée en cours : + val[i] = commande->cde[ptr_cde ++]; + + // Contrôle de la valeur reçue : + if ((val[i] != ON) && (val[i] != OFF)) + { + erreur = C_BUS_ERREUR_VALEUR_CONSIGNE; + break; + } + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + +/* +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +affichage_debug(__FILE__, __LINE__, "---------------------------------------------------------------------", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +for (i=0; i < MAX_NOMBRE_ENTREES_AUXILIAIRES; i++) +{ +sprintf(tmp, "Etat AUX #%i : %s", (i+1), val[i] ? "=== On ===" : "Off"); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +} +affichage_debug(__FILE__, __LINE__, " ", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +*/ + + for (i=0; i < MAX_NOMBRE_ENTREES_AUXILIAIRES; i++) + { + if (!entrees.aux.entrees[i].flag_validite) continue; + + // NB : Le test 1 <= no d'entrées <= 32 a été fait dans 'enregistrement_annuaire_k_bus()'... + + if (entrees.aux.entrees[i].logique == ENTREE_AUX_LOGIQUE_NORMALE) + { + if (val[entrees.aux.entrees[i].no - 1] == 0) entrees.aux.entrees[i].valeur = OFF; + else entrees.aux.entrees[i].valeur = ON; + } + else + { + if (val[entrees.aux.entrees[i].no - 1] == 0) entrees.aux.entrees[i].valeur = ON; + else entrees.aux.entrees[i].valeur = OFF; + } + } + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_ENTREES_AUX; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Permet de recevoir l'état des Level Sensors gérés par le Compagnon +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_level_sensors(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t lsb, msb; +int16_t val[MAX_NOMBRE_ENTREES_LEVEL_SENSOR]; // Important que ce soit un 'int' signé car les valeurs de retour en mode défaut d'isolement peuvent être négative.... + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + for (i=0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) + { + // Lecture de la valeur de l'entrée en cours : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + val[i] = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + // Contrôle de la valeur reçue : +// ????? en fonction du mode du capteur ?? + + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + for (i=0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) + { + if (!entrees.level_sensor.entrees[i].flag_validite) continue; + + // NB : Le test 1 <= no d'entrées <= 8 a été fait dans 'enregistrement_annuaire_k_bus()'... + + entrees.level_sensor.entrees[i].valeur = val[entrees.level_sensor.entrees[i].no - 1]; // NB : Valeur en mV si 0-10V, en µA si 4-20mA, en Ohms si 500R, en dixième de mA si Defaut d'isolement (+ ou -) + + switch(entrees.level_sensor.entrees[i].mode) + { + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_0_10V : // Valeur en mV si 0-10V => V + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_4_20MA : // Valeur en µA si 4-20mA => mA + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_AUTO_TEST : // Valeur en mV si AUTO TEST => V (valeur entre 0 et 10V) + { + entrees.level_sensor.entrees[i].valeur /= 1000; + break; + } + + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_MASSE : // Valeur en dixièmes de mA => mA + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_PLUS : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_FULL : + { + entrees.level_sensor.entrees[i].valeur /= 10; + break; + } + + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_500R : + default: + break; + } + } + +// printf("\nEtat des Level Sensors : "); +// for (i=0; i < MAX_NOMBRE_ENTREES_LEVEL_SENSOR; i++) printf("%i : %.3f\t", (i+1), entrees.level_sensor.entrees[i].valeur); +// printf("\n"); + + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_LEVEL_SENSORS; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Permet de recevoir l'état des 4 Entrées ANA gérés par le Compagnon +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_entrees_volt(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t lsb, msb; +uint16_t val[MAX_NOMBRE_ENTREES_VOLTAGE]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + for (i=0; i < MAX_NOMBRE_ENTREES_VOLTAGE; i++) + { + // Lecture de la valeur de l'entrée en cours : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + val[i] = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + // Contrôle de la valeur reçue : + // if (val[i] > 32000) erreur = C_BUS_VALEUR_HORS_DOMAINE; // On considère que la valeur ne peut dépasser 32V + + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + for (i=0; i < MAX_NOMBRE_ENTREES_VOLTAGE; i++) + { + if (!entrees.volt.entrees[i].flag_validite) continue; + + // NB : Le test 1 <= no d'entrées <= 4 a été fait dans 'enregistrement_annuaire_k_bus()'... + + entrees.volt.entrees[i].valeur = val[entrees.volt.entrees[i].no - 1]; // NB : Valeur en mV + entrees.volt.entrees[i].valeur /= 1000; // Passage de mV en V + } + +// printf("\nEtat des Entrées Voltage : "); +// for (i=0; i < MAX_NOMBRE_ENTREES_VOLTAGE; i++) printf("%i : %.3f V\t", (i+1), entrees.volt.entrees[i].valeur); +// printf("\n"); + + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_ENTREES_VOLT; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Permet de recevoir l'état des entrées OW gérés par le Compagnon +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_entrees_ow(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t nb; +uint8_t lsb, msb; +uint16_t x; +uint8_t defaut_bus; +uint8_t no_entree; +float val[MAX_NOMBRE_ENTREES_OW]; // SDIR # 176 : passage en float +int8_t etat[MAX_NOMBRE_ENTREES_OW]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + defaut_bus = ON; // Par défaut. RAZ dès qu'on trouve au moins un device qui a pu faire sa mesure (donc qu'il n'y avait pas de défaut bus...) + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Fake loop pour permettre la sortie par simple break en cas d'erreur. + { + nb = commande->cde[ptr_cde ++]; + + if (nb > MAX_NOMBRE_ENTREES_OW) + { + erreur = C_BUS_VALEUR_HORS_DOMAINE; + break; + } + + for (i=0; i < nb; i++) + { + // Lecture de la valeur de l'entrée en cours : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + x = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + if (x == DEVICE_OW_VALEUR_ERREUR_ETAT_GENERAL_NOK) etat[i] = OW_ETAT_BUS_NOK; // SDIR #178 + else + { + defaut_bus = OFF; + if (x == DEVICE_OW_VALEUR_ERREUR_ETAT_DEVICE_NOK) etat[i] = OW_ETAT_DEVICE_NOK; // SDIR #178 + else + { + etat[i] = OW_ETAT_DEVICE_OK; // SDIR #178 + val[i] = ((float) ((int16_t) x)) / 100; // Le passage de UINT16_T à INT16_T avant FLOAT est "obligatoire" pour que les valeurs négatives soient correctement converties. Conversion en float et passage de centièmes de °C à °C. // SDIR # 176 + } + } + + + // Contrôle de la valeur reçue : + + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + // Mise à jour du premier flag, indiquant qu'on a reçu les premières valeurs du Compagnon : + if (!entrees.ow.flag_premieres_valeurs_recues) entrees.ow.flag_premieres_valeurs_recues = TRUE; + + if (nb > 0) // Si 'nb' != 0, c'est qu'il s'agit d'un retour normal des valeurs d'entrées OW + { + for (i=0; i < nb; i++) + { + // Mise à jour du device (qui existe toujours - si NB reste raisonnable évidemment) : + if ((entrees.ow.devices[i].valeur != val[i]) || + (entrees.ow.devices[i].etat != etat[i])) + { + entrees.ow.devices[i].valeur = val[i]; + entrees.ow.devices[i].etat = etat[i]; + entrees.ow.devices[i].flag_dde_maj_bdd = TRUE; + + } + + // Mise à jour de l'entrée OW s'il y en a une de valide sur ce device : + if (entrees.ow.devices[i].no_entree == -1) continue; // Indique qu'aucune entrée n'est affectée à ce device... + + no_entree = entrees.ow.devices[i].no_entree; // Simplifie la lecture. Lien entre le device et l'entrée si elle existe + + if (!entrees.ow.entrees[no_entree].flag_validite) continue; // ne devrait pas arriver puisque le device est affecté à l'entrée... + + if ((entrees.ow.entrees[no_entree].valeur != val[i]) && + (etat[i] == OW_ETAT_DEVICE_OK)) // SDIR #178 + { + // SDIR # 176 : modification des unités et donc des CAST associés. + if (( (val[i] != 0) || // Si la nouvelle valeur = 0 ET + (abs(entrees.ow.entrees[no_entree].valeur) < TEMPERATURE_DELTA_ZERO_MAXI) ) && // Si l'ancienne n'était pas comprise entre -2°C et +2°C, on jette la valeur qui ressemble à une erreur ! + (val[i] < TEMPERATURE_VALEUR_MAXI) && // ... et si la nouvelle valeur est < +125°C + (val[i] > TEMPERATURE_VALEUR_MINI)) // ... et si la nouvelle valeur est > -40°C + { + entrees.ow.entrees[no_entree].valeur = val[i]; + } + else + { + sprintf(tmp, "Valeur d'entrée OW rejetée : ancienne valeur = %.3f °C et nouvelle reçue = %.3f °C ", entrees.ow.entrees[no_entree].valeur, val[i]); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + // SDIR #178 [début] : + if (etat[i] != entrees.ow.entrees[no_entree].etat) // Mise à jour de l'état s'il a évolué : + { + if (etat[i] == OW_ETAT_DEVICE_OK) // Si le nouvel état n'est pas en erreur : + { + sprintf(tmp, "Le device OW #%i n'est plus en erreur. ", i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + else + { + sprintf(tmp, "Le device OW #%i vient d'être détecté en erreur. ", i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + entrees.ow.entrees[no_entree].etat = etat[i]; // Mémorisation du nouvel état dans tous les cas + } + // SDIR #178 [fin] + + entrees.ow.defaut_bus = defaut_bus; + } + +// printf("\nEtat des %i devices OW : \n", nb); +// for (i=0; i < nb; i++) printf("\t%i [Rom Code = %s] = %.2f °C - Etat = %s \n", i, entrees.ow.devices[i].rom_code, entrees.ow.devices[i].valeur, ((entrees.ow.devices[i].etat == 0) ? "Ok" : ((entrees.ow.devices[i].etat == OW_ETAT_DEVICE_NOK) ? "Erreur de device" : "Erreur générale OW"))); +// printf("\n"); + + } + else c_bus_down_commande_annuaire_ow(); // Si 'nb' = 0, c'est l'info qu'une découverte réseau OW vient de se terminer et qu'il faut demander un nouvel état d'annuaire OW. + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_ENTREES_OW; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter au CM3+ l’état des IBS +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_entrees_batteries(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t llsb, lsb, msb, mmsb; +uint8_t adresse_module; +int16_t temperature; +uint16_t tension; +int32_t courant_differentiel; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + adresse_module = commande->cde[ptr_cde ++]; // + + if ((adresse_module >= MAX_NOMBRE_MODULES) || (systeme.modules_config[adresse_module].flag_validite == FALSE)) + { + erreur = C_BUS_ERREUR_NO_CARTE; + break; + } + + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SBS : + { + // Température de la batteries : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + temperature = (int16_t) ((uint16_t) lsb | ((uint16_t) msb << 8)); + + // Tension de la batterie : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + tension = (uint16_t) ((uint16_t) lsb | ((uint16_t) msb << 8)); + + // Intensité du courant différentiel dans la batterie : + llsb =commande->cde[ptr_cde ++]; + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + mmsb = commande->cde[ptr_cde ++]; + courant_differentiel = (int32_t) ((uint32_t) llsb | ((uint32_t) lsb << 8) | ((uint32_t) msb << 16) | ((uint32_t) mmsb << 24)); + + break; + } + default: erreur = C_BUS_ERREUR_NO_CARTE; + } + + if (erreur != C_BUS_ERREUR_AUCUNE) break; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== +// printf("Commande état batterie reçue pour la carte %i et erreur = %i", adresse_module, erreur); + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, prise en compte de l'état des entrées : + { + switch(systeme.modules_config[adresse_module].type) + { + case TYPE_CARTE_SBS : + { + for (i = 0; i < batteries.nb; i ++) + { + if (batteries.batteries[i].flag_validite == FALSE) continue; // On ne traite que les batteries valides... + if (batteries.batteries[i].adresse_module != adresse_module ) continue; // ... et ceux piloté par cette carte... + +// Actuellement, une sonde non branchée renvoie une temprature d'erreur (BATTERIES_NTC_TEMPERATURE_ERREUR = -50°C dans 'batteries.h'). +// Si besoin, comparer '((float)temperature)/100' à cette constante et activer un indicateur d'erreur. Surtout si la tension est simultanéement à 0. +// Idem si l'on veut ne pas prendre en compte ces valeurs d'erreur (garder l'ancienne) mais pas fait pour le moment car il semble plus utile d'alerter par une valeur ANORMALE. D'autant +// que cela pose la question du démarrage initial sans sonde : quelle valeur 'garder'...? + batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante = ((float)temperature)/100; // La valeur entière est en centièmes de °C + batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante = ((float)tension)/1000; // La valeur entière est en mV + batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante = ((float)courant_differentiel)/1000; // La valeur entière est en mA + +// printf("Etat batterie #%i reçu pour la carte %i : température = %.2f °C, tension = %.3f V et courant diff = %.3f A \n", i, adresse_module, batteries.batteries[i].variables[BATTERIE_VARIABLE_TEMPERATURE].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_TENSION].valeur_courante, batteries.batteries[i].variables[BATTERIE_VARIABLE_COURANT_DIFFERENTIEL].valeur_courante); + + } + + break; + } + default: erreur = C_BUS_ERREUR_NO_CARTE; + } + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_ENTREES_BATTERIES; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter au CM4 l’état d'un PGN NMEA2000 +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_etat_pgn_nmea(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint16_t i; +uint8_t llsb, lsb, msb, mmsb; +struct Struct_pgn_nmea pgn; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + // N° de PGN : + llsb =commande->cde[ptr_cde ++]; + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + mmsb = commande->cde[ptr_cde ++]; + pgn.pgn = (uint32_t) llsb | ((uint32_t) lsb << 8) | ((uint32_t) msb << 16) | ((uint32_t) mmsb << 24); + + // Source : + pgn.source = commande->cde[ptr_cde ++]; + + // Priorite : + pgn.priority = commande->cde[ptr_cde ++]; + if (pgn.priority > 7) + { + erreur = C_BUS_ERREUR_PRIORITE; + break; + } + + // Timestamp : + llsb =commande->cde[ptr_cde ++]; + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + mmsb = commande->cde[ptr_cde ++]; + pgn.timestamp = (uint32_t) llsb | ((uint32_t) lsb << 8) | ((uint32_t) msb << 16) | ((uint32_t) mmsb << 24); + + // Longueur Data : + pgn.lg_data = commande->cde[ptr_cde ++]; + + if (pgn.lg_data > NMEA_MAX_TAILLE_DATA_PGN) + { + erreur = C_BUS_ERREUR_VALEUR_TAILLE; + break; + } + + // Data : + for (i = 0; i < pgn.lg_data; i ++) pgn.data[i] = commande->cde[ptr_cde ++]; + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + if (erreur == C_BUS_ERREUR_AUCUNE) + { + pgn.flag_presence = true; + + traitement_pgn_compagnon(pgn); // NB : pgn.actif n'est pas initialisé à ce stade car l'info vient de la BDD... + } +else printf("erreur %i dans réception de commande 'c_bus_up_commande_etat_pgn_nmea()' \n", erreur); + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_PGN_NMEA; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//================================================================================================================================================================== +// Fonction : Cette commande permet au Compagnon de remonter sa config ou celle d’une carte extension au CM3+ +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : 0 si pas d'erreur, [code d'erreur (>0)]> si erreur +//================================================================================================================================================================== +uint8_t c_bus_up_commande_config_carte(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +int8_t erreur; +uint16_t ptr_cde; +uint8_t lsb, msb; +uint8_t adresse_module; +uint16_t ptr_eeprom; +int16_t offset_intensite = 0; // Signé...! +int16_t gain_intensite = 0; // Signé...! +int16_t offset_usine_intensite = 0; // Signé...! +int16_t gain_usine_intensite = 0; // Signé...! +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Initialisation du traitement de la commande : + ptr_cde = 1; // La valeur 1 permet de sauter le code commande qui a été déjà testé avant d'appeler la fonction + reponse->lg_reponse = 0; + erreur = C_BUS_ERREUR_AUCUNE; + + //=========================================================================================================================== + // Traitement de tous les paramètres de la commande : + //=========================================================================================================================== + + do // Boucle factice pour permettre la sortie par simple break en cas d'erreur. + { + adresse_module = commande->cde[ptr_cde ++]; // + + if ((adresse_module >= MAX_NOMBRE_MODULES) || (systeme.modules_config[adresse_module].flag_validite == FALSE)) + { + sprintf(tmp, "Erreur : n° de carte = %i (Min = 0 et Max = %i ou carte dévalidée)", adresse_module, MAX_NOMBRE_MODULES); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + erreur = C_BUS_ERREUR_NO_CARTE; + break; + } + + + // Compagnon : + if (adresse_module == 0) + { + + } + + // Autre carte : + else + { + switch(systeme.modules_config[adresse_module].type) + { + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SBS : + { + // Offset intensité : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + offset_intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + // Gain intensité : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + gain_intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + // Offset usine intensité : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + offset_usine_intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + // Gain usine intensité : + lsb = commande->cde[ptr_cde ++]; + msb = commande->cde[ptr_cde ++]; + gain_usine_intensite = (((uint16_t) msb) << 8) | ((uint16_t) lsb); + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP4AC : + { + + break; + } + + //-------------------------------------------------------------------------------- + default: break; + } + } + + //---------------------------------------------------------------------- + // Vérification de la longueur de commande : + //---------------------------------------------------------------------- + + if (ptr_cde != commande->lg_cde) erreur = C_BUS_ERREUR_FORMAT_COMMANDE; + + } while(0); + + + //=========================================================================================================================== + // Traitement de la commande et préparation de la réponse : + //=========================================================================================================================== + + if (erreur == C_BUS_ERREUR_AUCUNE) // Si une erreur a déjà été trouvée, retour NOK avec code d'erreur. + { + // Compagnon : + if (adresse_module == 0) + { + + } + // Autres cartes : + else + { + switch(systeme.modules_config[adresse_module].type) + { + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SBS : + { + ptr_eeprom = 0; + + // Attention : dans les lignes ci-dessous, on utilise 'modules_physiques' au lieu de 'modules' car les infos d'EEPROM sont relatives au module PHYSIQUE. + + // Offset de courant : + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) (offset_intensite & 0x00FF); // LSB de l'offset + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) ((offset_intensite >> 8) & 0x00FF); // MSB de l'offset + + // Gain de courant : + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) (gain_intensite & 0x00FF); // LSB du gain + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) ((gain_intensite >> 8) & 0x00FF); // MSB du gain + + // Offset usine de courant : + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) (offset_usine_intensite & 0x00FF); // LSB de l'offset usine + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) ((offset_usine_intensite >> 8) & 0x00FF); // MSB de l'offset usine + + // Gain usine de courant : + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) (gain_usine_intensite & 0x00FF); // LSB du gain usine + systeme.modules_physiques[adresse_module].eeprom[ptr_eeprom ++] = (uint8_t) ((gain_usine_intensite >> 8) & 0x00FF); // MSB du gain usine + + enregistrement_eeprom(adresse_module, ptr_eeprom); // Mise à jour des nouvelles valeurs dans la BDD + +// DEBUG : +//printf("Config de la carte %i reçue : Offset intensité = %5i, Gain intensité = %5i, Offset intensité usine = %5i, Gain intensité usine = %5i \n", adresse_module, offset_intensite, gain_intensite, offset_usine_intensite, gain_usine_intensite); + + break; + } + + //-------------------------------------------------------------------------------- + case TYPE_CARTE_SP4AC : + { + + break; + } + + //-------------------------------------------------------------------------------- + default: break; + } + } + } + else + { + sprintf(tmp, "La commande C_BUS_UP_COMMANDE_CONFIG_CARTE reçue sur C-Bus Up n'est pas conforme (erreur = %i)", erreur); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + reponse->reponse[reponse->lg_reponse ++] = C_BUS_UP_COMMANDE_ETAT_CARTE; // La trame de retour comprend toujours le code commande en tête. + reponse->reponse[reponse->lg_reponse ++] = erreur; + + reponse->flag_ready = TRUE; + return (erreur); +} + + +//------------------------------------------------------------------------------------------------------------------------------------ + +//================================================================================================================================================================== +// Fonction : Fonction appelée lorsque le code de la commande reçu est inconnu. +// Reçoit : Le pointeur sur la structure de commande qui permet sa lecture mais aussi la mise à jour du buffer de réponse et sa longueur. +// Renvoit : Systématiquement code d'erreur C_BUS_ERREUR_COMMANDE_INCONNUE +//================================================================================================================================================================== +uint8_t c_bus_up_commande_inconnue(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Message de debug : + sprintf(tmp, "Commande inconnue reçue sur C_Bus [code=%i]...", c_bus.up.cde.cde[0]); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + // Préparation de la réponse d'erreur : + reponse->lg_reponse = 0; + reponse->reponse[reponse->lg_reponse ++] = c_bus.up.cde.cde[0]; // La trame de retour comprend toujours le code commande en tête... + reponse->reponse[reponse->lg_reponse ++] = C_BUS_ERREUR_COMMANDE_INCONNUE; // ... suivi du code d'erreur. + + reponse->flag_ready = TRUE; + return (C_BUS_ERREUR_COMMANDE_INCONNUE); +} + + +//================================================================================================================================================================== +// Fonction : En fonction du premier octet de la commande reçue sur C-Bus, appelle la fonction correspondante. +// Si le code reçu ne correspond à aucune commande connue, une fonction particulière est appelée pour formatter une réponse d'erreur au Compagnon. +// Reçoit : Rien +// Renvoit : 0 si pas d'erreur (commande connue et réponse acceptée), code d'errer > 0 sinon +//================================================================================================================================================================== +uint8_t c_bus_up_recherche_commande(void) +{ + + switch(c_bus.up.cde.cde[0]) + { + case C_BUS_UP_COMMANDE_ETAT_CARTE : { return(c_bus_up_commande_etat_carte(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_CIRCUITS : { return(c_bus_up_commande_etat_circuits(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_ENTREES_AUX : { return(c_bus_up_commande_etat_entrees_aux(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_LEVEL_SENSORS : { return(c_bus_up_commande_etat_level_sensors(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_ENTREES_VOLT : { return(c_bus_up_commande_etat_entrees_volt(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_ENTREES_OW : { return(c_bus_up_commande_etat_entrees_ow(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_ENTREES_BATTERIES : { return(c_bus_up_commande_etat_entrees_batteries(&c_bus.up.cde, &c_bus.up.reponse)); break; } + case C_BUS_UP_COMMANDE_ETAT_PGN_NMEA : { return(c_bus_up_commande_etat_pgn_nmea(&c_bus.up.cde, &c_bus.up.reponse)); break; } + + case C_BUS_UP_COMMANDE_CONFIG_CARTE : { return(c_bus_up_commande_config_carte(&c_bus.up.cde, &c_bus.up.reponse)); break; } + + default : { return (c_bus_up_commande_inconnue(&c_bus.up.cde, &c_bus.up.reponse)); break; } + } +} \ No newline at end of file diff --git a/sources/c_commandes.h b/sources/c_commandes.h new file mode 100644 index 0000000..7fda82a --- /dev/null +++ b/sources/c_commandes.h @@ -0,0 +1,89 @@ +//********************************************************************************************************************************************************************************** +// Fichier C_COMMANDES_.H +// +// Contient toutes définitions de constantes liées au commandes sur C_Bus +//**********************************************************************************************************************************************************************************/ + + + // Codes commande Down : +#define C_BUS_DOWN_COMMANDE_CONFIG_DATE_HEURE 0x01 +#define C_BUS_DOWN_COMMANDE_CONFIG_CARTE 0x02 +#define C_BUS_DOWN_COMMANDE_CONFIG_CIRCUITS 0x03 +#define C_BUS_DOWN_COMMANDE_CONFIG_LEVEL_SENSORS 0x04 +#define C_BUS_DOWN_COMMANDE_CONFIG_OW 0x05 +#define C_BUS_DOWN_COMMANDE_CONFIG_BATTERIES 0x06 +#define C_BUS_DOWN_COMMANDE_CONFIG_PGN_NMEA 0x07 + +#define C_BUS_DOWN_COMMANDE_RESET 0x10 +#define C_BUS_DOWN_COMMANDE_CDE_BACKLIGHT 0x11 +#define C_BUS_DOWN_COMMANDE_CDE_CIRCUITS 0x12 +// C_BUS_DOWN_COMMANDE_CDE_SORTIES_AUX 0x13 Plus utilisé depuis la suppression des sorties Aux de ShipBase +#define C_BUS_DOWN_COMMANDE_CDE_CARTE 0x14 + +#define C_BUS_DOWN_COMMANDE_LECTURE_DATE_HEURE 0x20 +#define C_BUS_DOWN_COMMANDE_LECTURE_ID_CARTE 0x21 + +#define C_BUS_DOWN_COMMANDE_ANNUAIRE_K_BUS 0x32 +#define C_BUS_DOWN_COMMANDE_CHARGEMENT_CODE 0x33 +#define C_BUS_DOWN_COMMANDE_ANNUAIRE_OW 0x34 + + + // Codes commande Up : +#define C_BUS_UP_COMMANDE_ETAT_CARTE 0x80 +#define C_BUS_UP_COMMANDE_ETAT_CIRCUITS 0x81 +#define C_BUS_UP_COMMANDE_ETAT_ENTREES_AUX 0x82 +#define C_BUS_UP_COMMANDE_ETAT_LEVEL_SENSORS 0x83 +#define C_BUS_UP_COMMANDE_ETAT_ENTREES_VOLT 0x84 +#define C_BUS_UP_COMMANDE_ETAT_ENTREES_OW 0x85 +#define C_BUS_UP_COMMANDE_ETAT_ENTREES_BATTERIES 0x86 +#define C_BUS_UP_COMMANDE_ETAT_PGN_NMEA 0x87 + +#define C_BUS_UP_COMMANDE_CONFIG_CARTE 0x90 + + // Cdes cartes : +#define C_BUS_COMMANDE_CDE_CARTE_LOCALISATION 1 +#define C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME 2 // Uniquement destiné à ShipBase. La consigne porte la valeur de l'état (0=Normal, 1=Alerte, 2=Alarme) + +#define C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME_NORMAL 0 +#define C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME_ALERTE 1 +#define C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME_ALARME 2 +#define C_BUS_COMMANDE_CDE_CARTE_MAX_CONSIGNE C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME_ALARME // Valeur maximale de la consigne d'une commande carte (2=ALARME) + + // Commandes de config : +#define C_BUS_CONFIG_AUCUNE -1 +#define C_BUS_CONFIG_LECTURE_SEULE 0 +#define C_BUS_CONFIG_WRITE_COURANTE 1 +#define C_BUS_CONFIG_WRITE_USINE 2 + + // Codes d'erreur : +#define C_BUS_ERREUR_AUCUNE 0 +#define C_BUS_ERREUR_COMMANDE_INCONNUE 1 +#define C_BUS_ERREUR_FORMAT_COMMANDE 2 +#define C_BUS_ERREUR_PARAMETRE_INCONNU 3 +#define C_BUS_ERREUR_LONGUEUR_PARAMETRE 4 +#define C_BUS_ERREUR_ADRESSE_MAC 5 +#define C_BUS_ERREUR_ADRESSE_BUS 6 +#define C_BUS_ERREUR_FORMAT_DATE 7 +#define C_BUS_ERREUR_FORMAT_HEURE 8 +#define C_BUS_ERREUR 9 +#define C_BUS_ERREUR_NUMERO_CIRCUIT 10 +#define C_BUS_ERREUR_VALEUR_CONSIGNE 11 +#define C_BUS_ERREUR_MODE_COMMANDE 12 +#define C_BUS_ERREUR_TYPE_CIRCUIT 13 +#define C_BUS_ERREUR_COMPATIBILITE_MODE_TYPE 14 +#define C_BUS_ERREUR_VALEUR_VALIDITE 15 +#define C_BUS_ERREUR_VALEUR_SEUIL 16 +#define C_BUS_ERREUR_VALEUR_FILTRAGE 17 +#define C_BUS_ERREUR_RTC 18 +#define C_BUS_ERREUR_NO_CARTE 19 +#define C_BUS_ERREUR_CONSIGNE_BACKLIGHT 20 +#define C_BUS_VALEUR_HORS_DOMAINE 21 +#define C_BUS_ERREUR_BLOCS 22 +#define C_BUS_ERREUR_VALEUR_TAILLE 23 +#define C_BUS_ERREUR_FLASH 24 +#define C_BUS_ERREUR_CRC 25 +#define C_BUS_ERREUR_PRIORITE 26 + + + + diff --git a/sources/capteurs.c b/sources/capteurs.c new file mode 100644 index 0000000..9d153b9 --- /dev/null +++ b/sources/capteurs.c @@ -0,0 +1,747 @@ +//********************************************************************************************************************************************************************************** +// Fichier CAPTEURS.C +// +// Contient toutes les fonctions liées aux capteurs, associé à la table TB_Capteurs de la BDD (créé en janvier 2017) +//********************************************************************************************************************************************************************************** + +//#define __DEBUG_CAPTEURS // Décommenter cette ligne pour que les infos de debug soient affichées +//#define __DEBUG_CAPTEURS_CALCULES // Décommenter cette ligne pour que les infos de debug soient affichées +//#define __DEBUG_CAPTEURS_ALERTES_ALARMES // Décommenter cette ligne pour que les infos de debug soient affichées + +// Include : +#include "main.h" + +// Prototypes : +void gestion_capteurs(void); +int8_t init_capteurs(void); +bool test_condition_capteur_booleen(uint16_t no_capteur, uint16_t no_condition); +bool valeur_neutre_operation_booleene(uint8_t operation); +int16_t capteur_cle_primaire(uint16_t cle_primaire); +int16_t variable_capteur_cle_primaire(uint16_t cle_primaire); + +// Variables globales : +struct Struct_capteurs capteurs; + + + + +//********************************************************************************************************************************************************************************** +// Fonction INIT_CAPTEURS +// +//********************************************************************************************************************************************************************************** +int8_t init_capteurs(void) +{ + + capteurs.sequenceur.etat = ETAT_SEQUENCEUR_CAPTEURS_INIT; + capteurs.sequenceur.compteur = CAPTEURS_TEMPO_INIT; + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation des capteurs\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + return(0); +} + + +//********************************************************************************************************************************************************************************** +// Fonction GESTION_CAPTEURS +// +//********************************************************************************************************************************************************************************** +void gestion_capteurs(void) +{ +uint16_t i, j; +float val; +int16_t no; +bool flag_erreur; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (!systeme.flag_cm3p_ready) return; // Evite que le traitement soit fait au milieu d'une reconfiguration... + + switch(capteurs.sequenceur.etat) + { + //-------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CAPTEURS_INIT : + //-------------------------------------------------------------------------------------------------------------- + { + if ((systeme.flag_cm3p_ready) && // Permet de s'assurer que le cycle d'init système est terminé. En particulier, on est sûr que 'init_bdd()' puis 'annuaire_k_bus()' ont été traités. + (capteurs.sequenceur.compteur == 0)) + { + +affichage_debug(__FILE__, __LINE__, "------------------CAPTEURS INIT", TYPE_MESSAGE_DEBUG_WARNING); + + for (i=0; i>= 1; + } + + break; + } + + //================================== + case TYPE_ENTREES_LEVEL_SENSOR : + { + entrees.level_sensor.entrees[no].valeur = val; // NB : si le capteur est valide, l'entrée correspondante l'est aussi (traité dans 'enregistrement_annuaire_k_bus()') + break; + } + + //================================== + case TYPE_ENTREES_VOLTAGE : + { + entrees.volt.entrees[no].valeur = val; // NB : si le capteur est valide, l'entrée correspondante l'est aussi (traité dans 'enregistrement_annuaire_k_bus()') + break; + } + + //================================== + case TYPE_ENTREES_OW : + { + // L'init se fait dans 'c_bus_down_reponse_annuaire_ow()' car on n'est pas sûr d'avoir l'annuaire OW disponible à ce stade. + break; + } + + //================================== + case TYPE_ENTREES_PREDEFINIES : + { + entrees.predefinies.entrees[no].valeur = val; // NB : si le capteur est valide, l'entrée correspondante l'est aussi (traité dans 'enregistrement_annuaire_k_bus()') + break; + } + +/* //================================== + case TYPE_ENTREES_IBS : + { + break; + } +*/ + //================================== + case TYPE_ENTREES_NMEA : + { + entrees.nmea.entrees[no].valeur = val; // NB : si le capteur est valide, l'entrée correspondante l'est aussi (traité dans 'enregistrement_annuaire_k_bus()') + break; + } + + //================================== +/* case TYPE_ENTREES_CAN : + { + break; + } +*/ + //================================== + case TYPE_ENTREES_CALCULEE_BOOLEEN : + { + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + if (capteurs.capteurs[i].conditions.conditions[j].type_condition == CAPTEUR_CONDITION_GROUPE_NORMAL) + { + // Mise à jour du n° de source à partir de l'ID_Source : + // Mise à jour du n° de variable à partir de l'ID_Variable : + int16_t no_source; + int16_t no_variable; + switch(capteurs.capteurs[i].conditions.conditions[j].type_source) + { + case SOURCE_MESURE_CIRCUIT: + { + no_source = circuit_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_source); + no_variable = variable_circuit_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_variable); + break; + } + case SOURCE_MESURE_CAPTEUR: + { + no_source = capteur_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_source); + no_variable = 0; // Un capteur n'a qu'un n° de variable... + break; + } + case SOURCE_MESURE_RESERVOIR: + { + no_source = reservoir_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_source); + no_variable = variable_reservoir_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_variable); + break; + } + case SOURCE_MESURE_BATTERIE: + { + no_source = batterie_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_source); + no_variable = variable_batterie_cle_primaire(capteurs.capteurs[i].conditions.conditions[j].id_variable); + break; + } + default: + { + no_source = -1; + break; + } + } + + if (no_source < 0) // Si on n'a pas trouvé d'équipement valide correspondant à l'ID reçu, on est obligé de dévalider complètement de capteur car l'équation peut ne plus rien vouloir dire... + { + sprintf(tmp, "L'ID_Source de la condition #%i du capteur #%i (clé primaire = %i) est invalide [%i]. Le capteur est dévalidé complètement car l'équation pourrait devenir instable", j, i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].conditions.conditions[j].id_source); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + capteurs.capteurs[i].flag_validite = FALSE; + break; + } + else capteurs.capteurs[i].conditions.conditions[j].no_source = no_source; + + if (no_variable < 0) // Si on n'a pas trouvé de variable valide correspondant à l'ID reçu, on est obligé de dévalider complètement de capteur car l'équation peut ne plus rien vouloir dire... + { + sprintf(tmp, "L'ID_Variable de la condition #%i du capteur #%i (clé primaire = %i) est invalide [%i]. Le capteur est dévalidé complètement car l'équation pourrait devenir instable", j, i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].conditions.conditions[j].id_variable); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + capteurs.capteurs[i].flag_validite = FALSE; + break; + } + else capteurs.capteurs[i].conditions.conditions[j].no_variable = no_variable; + } + } + + + break; + } + +/* //================================== + case TYPE_ENTREES_CALCULEE_NUMERIQUE : + { + break; + } +*/ + + //================================== + default: + { + sprintf(tmp, "Le 'Type_entrées' du capteur #%i est invalide [%i]. Le capteur est dévalidé", i, capteurs.capteurs[i].type_entrees); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + capteurs.capteurs[i].flag_validite = FALSE; + break; + } + } + } + + capteurs.sequenceur.etat = ETAT_SEQUENCEUR_CAPTEURS_REPOS; + } + + break; + } + + //-------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CAPTEURS_REPOS : + //-------------------------------------------------------------------------------------------------------------- + { + if (capteurs.sequenceur.compteur == 0) + { + capteurs.sequenceur.compteur = CAPTEURS_PERIODE_INTEGRATION_VALEUR; + + for (i = 0; i < capteurs.nb; i ++) + { + if (capteurs.capteurs[i].flag_validite == FALSE) continue; + + flag_erreur = FALSE; + val = 0; + + //------------------------------------- + // Recherche de la valeur du capteur : + //------------------------------------- + +// ATTENTION : ne doit pas fonctionner si le nb d'entrées est > 1 !!!! + no = capteurs.capteurs[i].no_entrees[0]; // Facilite le traitement et la lecture ensuite. + + switch(capteurs.capteurs[i].type_entrees) + { + //================================== + case TYPE_ENTREES_AUXILIAIRES : + { + uint32_t val_u32 = 0; + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + val_u32 |= ((uint32_t)(entrees.aux.entrees[capteurs.capteurs[i].no_entrees[j]].valeur) ) << j; + } + + val = (float) val_u32; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + break; + } + + //================================== + case TYPE_ENTREES_LEVEL_SENSOR : + { + switch(entrees.level_sensor.entrees[no].mode) + { + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_MASSE : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_PLUS : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_DEFAUT_ISOLEMENT_FULL : + { + val = entrees.level_sensor.entrees[no].valeur; + if (fabs(val) < 0.8) val = 0; // Seuil (en valeur absolue), en-dessous duquel on considère la valeur comme non significative donc nulle. Evite les questions...! +// if (val < -99.9) val = -99.9; // Il est inutile d'afficher des valeurs supérieures qui risquent d'affoler pour rien puisque c'est une projection qui est faite et pas un courant réel. +//printf("Valeur lue sur détection de courant de défaut = %.3f\n", val); + break; + } + + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_NON_DEFINI : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_0_10V : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_500R : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_4_20MA : + case ENTREE_LEVEL_SENSOR_MODE_FONCTIONNEMENT_AUTO_TEST : + default : + { + val = entrees.level_sensor.entrees[no].valeur; + break; + } + } + + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + break; + } + + //================================== + case TYPE_ENTREES_VOLTAGE : + { + val = entrees.volt.entrees[no].valeur; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + break; + } + + //================================== + case TYPE_ENTREES_OW : + { + // NB : on sait que l'entrée est valide car le test a été fait dans 'enregistrement_annuaire_ow()'... Inutile de retester à chaque boucle. + + // Vérification qu'on a déjà reçu des valeurs OW depuis le Companon : + if (!entrees.ow.flag_premieres_valeurs_recues) + { + flag_erreur = TRUE; + break; + } + + // SDIR # 176 : + val = entrees.ow.entrees[no].valeur; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + // SDIR #178 [début] : + if (entrees.ow.entrees[no].etat != OW_ETAT_DEVICE_OK) // Si le capteur OW est NOK : + { + flag_erreur = TRUE; // Il est inutile de traiter de nouvelles valeurs de capteurs si le capteur est défaillant. + + if (capteurs.capteurs[i].etat != CAPTEUR_ETAT_ALARME) // Détection d'un changement d'état. + { + capteurs.capteurs[i].etat = CAPTEUR_ETAT_ALARME; // Mise à jour du nouvel état du capteur + capteurs.capteurs[i].flag_dde_maj_bdd = TRUE; + + if (entrees.ow.entrees[no].etat == OW_ETAT_BUS_NOK) // Différentiation entre erreur globale du bus OW : + { + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CAPTEURS, CAPTEUR_OW_BUS_NOK, capteurs.capteurs[i].cle_primaire, 0, entrees.ow.entrees[no].rom_code); + } + else // ... et erreur du capteur seulement : + { + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CAPTEURS, CAPTEUR_OW_DEVICE_NOK, capteurs.capteurs[i].cle_primaire, 0, entrees.ow.entrees[no].rom_code); + } + } + } + else // Si le capteur OW est Ok : + { + if (capteurs.capteurs[i].etat != CAPTEUR_ETAT_NORMAL) // Détection d'un changement d'état + { + capteurs.capteurs[i].etat = CAPTEUR_ETAT_NORMAL; // Mise à jour du nouvel état du capteur (il sera peut-être modifié ensuite si une alerte ou une alarme est détectée) + capteurs.capteurs[i].flag_dde_maj_bdd = TRUE; + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CAPTEURS, CAPTEUR_OW_DEVICE_OK, capteurs.capteurs[i].cle_primaire, 0, entrees.ow.entrees[no].rom_code); + } + } + // SDIR #178 [fin] + + + break; + } + + //================================== + case TYPE_ENTREES_PREDEFINIES : + { + val = entrees.predefinies.entrees[no].valeur; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + break; + } + +/* //================================== + case TYPE_ENTREES_IBS : + { + break; + } +*/ + //================================== + case TYPE_ENTREES_NMEA : + { + val = entrees.nmea.entrees[no].valeur; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 + + break; + } + + //================================== +/* case TYPE_ENTREES_CAN : + { + break; + } +*/ + //================================== + case TYPE_ENTREES_CALCULEE_BOOLEEN : + { + struct Struct_calcul_capteur_booleen calcul_booleen; + calcul_booleen.niveau = -1; + + +#ifdef __DEBUG_CAPTEURS_CALCULES + sprintf(tmp, "Capteur calculé #%i : ---------------------------------", i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + for (j = 0; j < capteurs.capteurs[i].nb_entrees; j ++) + { + //---------------------------------------------------------------------------------------- + // Condition de début de groupe '[' : + if (capteurs.capteurs[i].conditions.conditions[j].type_condition > CAPTEUR_CONDITION_GROUPE_NORMAL) + { + calcul_booleen.niveau ++; + + calcul_booleen.parentheses[calcul_booleen.niveau].operation = capteurs.capteurs[i].conditions.conditions[j].type_condition; + calcul_booleen.parentheses[calcul_booleen.niveau].resultat = valeur_neutre_operation_booleene(calcul_booleen.parentheses[calcul_booleen.niveau].operation); +#ifdef __DEBUG_CAPTEURS_CALCULES +char ch[100] = ""; +uint8_t k; + +for (k=0; k 0) + { + calcul_booleen.niveau --; + + if (calcul_booleen.parentheses[calcul_booleen.niveau].operation == CAPTEUR_CONDITION_GROUPE_OU) + { + calcul_booleen.parentheses[calcul_booleen.niveau].resultat |= calcul_booleen.parentheses[calcul_booleen.niveau + 1].resultat; + } + else + { + calcul_booleen.parentheses[calcul_booleen.niveau].resultat &= calcul_booleen.parentheses[calcul_booleen.niveau + 1].resultat; + } +#ifdef __DEBUG_CAPTEURS_CALCULES +char ch[100] = ""; +uint8_t k; + +for (k=0; k<(calcul_booleen.niveau+1); k++) strcat(ch, "\t"); +sprintf(tmp, "#%i : %s] %s (%s)", + j, ch, + (calcul_booleen.parentheses[calcul_booleen.niveau].operation == CAPTEUR_CONDITION_GROUPE_OU) ? "OU" : "ET", + (calcul_booleen.parentheses[calcul_booleen.niveau + 1].resultat) ? "TRUE" : "FALSE"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else + { + val = (float) calcul_booleen.parentheses[calcul_booleen.niveau].resultat; + capteurs.capteurs[i].valeur_brute = val; // SDIR #135 +#ifdef __DEBUG_CAPTEURS_CALCULES +sprintf(tmp, "#%i : ] Résultat = %s", j, (val == 0) ? "FALSE" : "TRUE"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + continue; + } + } + + + break; + } + +/* //================================== + case TYPE_ENTREES_CALCULEE_NUMERIQUE : + { + break; + } +*/ + + //================================== + default: flag_erreur = TRUE; // A priori, pas possible car testé au moment de l'init... + } + + + //------------------------------------ + // Traitement de la valeur récupérée : + //------------------------------------ + if (flag_erreur == FALSE) + { + // Correction : + val = (val * capteurs.capteurs[i].gain) + capteurs.capteurs[i].offset ; + + // Calcul de la nouvelle moyenne de valeur courante à partir de l'ancienne et de la valeur lue : + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante = (((capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante * (capteurs.capteurs[i].valeur_coeff_filtrage - 1))) + val) / capteurs.capteurs[i].valeur_coeff_filtrage; + + // Mise à jour du cumul : + capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].cumul += capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante; + capteurs.capteurs[i].cumuls_nb_val ++; // MàJ du nombre d'échantillons dans le cumul + + // Traitement si la valeur a suffisemment évolué : +// if (fabs(capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_bdd - capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante) >= capteurs.variables[capteurs.capteurs[i].no_variable].marge) + if (fabs(capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_bdd - capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante) > 0) + { +#ifdef __DEBUG_CAPTEURS +sprintf(tmp, "Capteur #%i (cle primaire = %i) : changement détecté avec valeur_courante=%.3f et valeur_bdd=%.3f - Marge=%.3f ", i, capteurs.capteurs[i].cle_primaire, capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante, capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_bdd, capteurs.variables[capteurs.capteurs[i].no_variable].marge); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + capteurs.capteurs[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + + + // Enregistrement de la mesure si le capteur est configuré en mode MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT : + if (capteurs.capteurs[i].mode_enregistrement_mesures == MODE_ENREGISTREMENT_MESURES_CHANGEMENT_ETAT) + { + char date_vide[MAX_LONGUEUR_DATE_COURANTE] = ""; // Evite un warning depuis GCC14 + enregistrement_mesure(SOURCE_MESURE_CAPTEUR, i, capteurs.capteurs[i].no_variable, date_vide, BDD_ENREGISTREMENT_MESURE_PONCTUEL); + } + + } // Fin de traitement si la valeur a évolué. + +#ifdef __DEBUG_CAPTEURS + sprintf(tmp, "Capteur #%i : Valeur=%.3f ", i, capteurs.capteurs[i].variables[CAPTEUR_VARIABLE_VALEUR].valeur_courante); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + // Mise à jour des valeurs BDD des variables : + if (capteurs.capteurs[i].flag_dde_maj_bdd) + { + for (j = 0; j < MAX_NOMBRE_VARIABLES_PAR_CAPTEUR; j ++) capteurs.capteurs[i].variables[j].valeur_bdd = capteurs.capteurs[i].variables[j].valeur_courante; + } + } // Fin du traitement sans erreur. + } + } + + break; + } + + + //-------------------------------------------------------------------------------------------------------------- + default : + //-------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "L'état du séquenceur Capteurs est inconnu [%i]. Il est repositionné au REPOS", capteurs.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + capteurs.sequenceur.etat = ETAT_SEQUENCEUR_CAPTEURS_REPOS; + break; + } + } +} + + + +//********************************************************************************************************************************************************************************** +// Fonction : Teste si la condition d'un capteur calculé booléen est vraie ou fausse +// Reçoit : Le numéro du capteur et le numéro de la condition du capteur +// Retourne : Vrai ou Faux +//********************************************************************************************************************************************************************************** +bool test_condition_capteur_booleen(uint16_t no_capteur, uint16_t no_condition) +{ +float valeur; + + + // Si la condition n'est pas une condition normale, on retourne directement FALSE : + if (capteurs.capteurs[no_capteur].conditions.conditions[no_condition].type_condition != CAPTEUR_CONDITION_GROUPE_NORMAL) return(false); + + //================================================== + // Recherche de la valeur en cours de la source : + switch(capteurs.capteurs[no_capteur].conditions.conditions[no_condition].type_source) + { + case SOURCE_MESURE_CIRCUIT: + { + valeur = circuits.circuits[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_source].variables[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_CAPTEUR: + { + valeur = capteurs.capteurs[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_source].variables[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_RESERVOIR: + { + valeur = reservoirs.reservoirs[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_source].variables[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_BATTERIE: + { + valeur = batteries.batteries[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_source].variables[capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_variable].valeur_courante; + break; + } + default: return(false); // Ne devrait pas arriver puisque testé à l'init... + } +#ifdef __DEBUG_CAPTEURS_CALCULES +/*char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Test de la condition #%i du capteur #%i : Type_source=%i, N° source=%i, N° de variable=%i, Valeur= %.2f, Condition de test=%i", + no_condition, no_capteur, + capteurs.capteurs[no_capteur].conditions.conditions[no_condition].type_source, + capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_source, + capteurs.capteurs[no_capteur].conditions.conditions[no_condition].no_variable, + valeur, + capteurs.capteurs[no_capteur].conditions.conditions[no_condition].test); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +*/ +#endif + + //================================================== + // Test selon le type de test de l'évènement : + switch(capteurs.capteurs[no_capteur].conditions.conditions[no_condition].test) + { + //------------------------------------- + case EVENTS_TYPE_TEST_STRICT_INFERIEUR: + { + return (valeur < capteurs.capteurs[no_capteur].conditions.conditions[no_condition].seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_INFERIEUR_OU_EGAL: + { + return (valeur <= capteurs.capteurs[no_capteur].conditions.conditions[no_condition].seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_EGAL: + { + return (fabs(valeur - capteurs.capteurs[no_capteur].conditions.conditions[no_condition].seuil) < FLT_MIN); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_SUPERIEUR_OU_EGAL: + { + return (valeur >= capteurs.capteurs[no_capteur].conditions.conditions[no_condition].seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_STRICT_SUPERIEUR: + { + return (valeur > capteurs.capteurs[no_capteur].conditions.conditions[no_condition].seuil); + break; + } + + //------------------------------------- + default: + case EVENTS_TYPE_TEST_NON_DEFINI : + { + return (false); + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Renvoit une valeur booléenne neutre par rapport à une opération (0 si OU , 1 si ET) +// Reçoit : l'opération concernée +// Retourne : Vrai ou Faux +//********************************************************************************************************************************************************************************** +bool valeur_neutre_operation_booleene(uint8_t operation) +{ + + if (operation == CAPTEUR_CONDITION_GROUPE_ET) return(true); + else return(false); +} + + +//********************************************************************************************************************************************************************************** +// Fonction CAPTEUR_CLE_PRIMAIRE +// Reçoit en paramètre : +// - La Cle_primaire du capteur à rechercher +// Retourne : +// - le numéro de capteur correspondant (ou -1 si aucun capteur valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_CAPTEURS-1. +//********************************************************************************************************************************************************************************** +int16_t capteur_cle_primaire(uint16_t cle_primaire) +{ +uint16_t i; + + for (i=0; i sinon une erreur est levée à l'init de l'appli et le démarrage est arrêté) +#define MAX_NOMBRE_ENTREES_PAR_CAPTEUR 4 // Indique le nombre max d'entrées physiques pour un même capteur. A priori, Ok jusqu'à 32 car utilisation d'uint32_t pour l'analyse dans 'capteurs.c'. +#define MAX_NOMBRE_VARIABLES_CAPTEURS 25 +#define MAX_NOMBRE_VARIABLES_PAR_CAPTEUR 1 +#define MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR 50 // Nombre maxi de conditions pour un capteur de type calculé. Cf équivalent dans 'main.qml'. Note : les conditions de début et de fin de groupe compte dans ce total + + + // Valeurs extrêmes (NB : ces valeurs sont dupliquées dans 'archivage.php'... Penser à les mettre à jour si modification ici). +#define TEMPERATURE_VALEUR_MINI (-40.00) // En °C Plage mini du DS18B20 = -55°C mais jugé trop faible pour l'application +#define TEMPERATURE_VALEUR_MAXI 125.00 // En °C Plage maxi du DS18B20 +#define TEMPERATURE_DELTA_ZERO_MAXI 2.00 // En °C Delta maxi (en valeur absolue) avec la valeur précédente pour qu'une nouvelle valeur = 0 soit prise en compte +#define PRESSION_ATM_VALEUR_MINI 900.00 // En hPa Pression Niveau de la Mer minimale (mondiale, toute période) : 870 hPa, au large des Philippines, près du centre du typhon Tip, le 12 octobre 1979. = +#define PRESSION_ATM_VALEUR_MAXI 1080.00 // En hPa Pression Niveau de la Mer maximum (mondiale, toute période) : 1086,8 hPa, à Tosontsengel (Mongolie), le 20 janvier 2010. +#define HUMIDITE_VALEUR_MINI 0.00 // En %HR +#define HUMIDITE_VALEUR_MAXI 100.00 // En %HR +#define ACCELERATION_VALEUR_MINI -16.00 // En 'g' +#define ACCELERATION_VALEUR_MAXI +16.00 // En 'g' + + + // Valeurs d'erreur : +#define POSITION_VALEUR_ERRONNEE 200000000 // Valeur hors limite de latitude (-90 000 000 à +90 000 000) et longitude (-180 000 000 à +180 000 000) +#define TEMPERATURE_VALEUR_ERRONEE 20000 // Soit 200°C qui hors du domaine possible + + + // Timings : +#define CAPTEURS_TEMPO_INIT 100 // En x 10ms +#define CAPTEURS_PERIODE_INTEGRATION_VALEUR 10 // En x 10ms + + + // Variables du capteur : +#define CAPTEUR_VARIABLE_VALEUR 0 +// NB : ces valeurs doivent rester strictement inférieures à MAX_NOMBRE_VARIABLES_PAR_RESERVOIR + + + // Conditions d'un capteur calculé booléen : +#define CAPTEUR_CONDITION_GROUPE_NORMAL 0 +#define CAPTEUR_CONDITION_GROUPE_OU 1 +#define CAPTEUR_CONDITION_GROUPE_ET 2 +#define CAPTEUR_CONDITION_GROUPE_OU_EXCLUSIF 3 +#define CAPTEUR_CONDITION_GROUPE_PLUS 10 +#define CAPTEUR_CONDITION_GROUPE_MOINS 11 +#define CAPTEUR_CONDITION_GROUPE_MULTIPLIE 12 +#define CAPTEUR_CONDITION_GROUPE_DIVISE 13 + + + // Etat d'un capteur +#define CAPTEUR_ETAT_NORMAL 0 +#define CAPTEUR_ETAT_ALERTE 1 +#define CAPTEUR_ETAT_ALARME 2 + + + + // Variables du capteur : +#define CAPTEUR_VARIABLE_SANS_UNITE 0 +#define CAPTEUR_VARIABLE_TENSION_V 1 +#define CAPTEUR_VARIABLE_TENSION_MV 2 +#define CAPTEUR_VARIABLE_COURANT_A 3 +#define CAPTEUR_VARIABLE_COURANT_MA 4 +#define CAPTEUR_VARIABLE_PUISSANCE_W 5 +#define CAPTEUR_VARIABLE_PUISSANCE_VA 6 +#define CAPTEUR_VARIABLE_PUISSANCE_DB 7 +#define CAPTEUR_VARIABLE_FREQUENCE 8 +#define CAPTEUR_VARIABLE_RESISTANCE 9 +#define CAPTEUR_VARIABLE_TEMPERATURE 10 +#define CAPTEUR_VARIABLE_PRESSION_ATM 11 +#define CAPTEUR_VARIABLE_HUMIDITE 12 +#define CAPTEUR_VARIABLE_ACCELERATION 13 +#define CAPTEUR_VARIABLE_POSITION 14 +#define CAPTEUR_VARIABLE_POURCENTAGE 15 +#define CAPTEUR_VARIABLE_DISTANCE_M 16 +#define CAPTEUR_VARIABLE_DISTANCE_KM 17 +#define CAPTEUR_VARIABLE_DISTANCE_NQ 18 +#define CAPTEUR_VARIABLE_VITESSE_KTS 19 +#define CAPTEUR_VARIABLE_VITESSE_KMH 20 +#define CAPTEUR_VARIABLE_ANGLE_DEGRES 21 +#define CAPTEUR_VARIABLE_LATITUDE 22 +#define CAPTEUR_VARIABLE_LONGITUDE 23 +#define CAPTEUR_VARIABLE_VOLUME 24 +// ATTENTION : ces valeurs doivent rester strictement inférieures à MAX_NOMBRE_VARIABLES_CAPTEURS (augmenter la valeur en haut de ce fichier ci besoin...) + + + + // Etats des séquenceur : +#define ETAT_SEQUENCEUR_CAPTEURS_INIT 0 +#define ETAT_SEQUENCEUR_CAPTEURS_REPOS 1 + + + + diff --git a/sources/circuits.c b/sources/circuits.c new file mode 100644 index 0000000..7ed0ce7 --- /dev/null +++ b/sources/circuits.c @@ -0,0 +1,479 @@ +//********************************************************************************************************************************************************************************** +// Fichier CIRCUITS.C +// +// Contient toutes les fonctions liées aux circuits +//********************************************************************************************************************************************************************************** + +// #define __DEBUG_CIRCUITS // Décommenter cette ligne pour que les infos de debug soient affichées + +// Include : +#include "main.h" + +// Prototypes : +void gestion_circuits(void); +void gestion_circuit(uint16_t no_circuit); +int8_t changement_consigne_circuit(uint16_t no_circuit, uint8_t consigne, uint8_t origine_consigne, uint16_t id_origine_consigne); +int8_t init_circuits(void); +int16_t circuit_cle_primaire(uint16_t cle_primaire); +int16_t variable_circuit_cle_primaire(uint16_t cle_primaire); + +// Variables globales : +struct Struct_circuits circuits; + + +//********************************************************************************************************************************************************************************** +// Fonction GESTION_CIRCUITS +// +//********************************************************************************************************************************************************************************** +void gestion_circuits(void) +{ +uint16_t i, j; +float courant_total; +uint16_t circuit_maxi; +float courant_maxi; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (!systeme.flag_cm3p_ready) return; // Evite que le traitement soit fait au milieu d'une reconfiguration... + + switch(circuits.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_CIRCUITS_INIT : + { + if ((systeme.flag_cm3p_ready) && // Permet de s'assurer que le cycle d'init système est terminé. En particulier, on est sûr que 'init_bdd()' puis 'annuaire_k_bus()' ont été traités. + (circuits.sequenceur.compteur == 0)) + { + +affichage_debug(__FILE__, __LINE__, "------------------CIRCUITS INIT", TYPE_MESSAGE_DEBUG_WARNING); + + circuits.sequenceur.etat = ETAT_SEQUENCEUR_CIRCUITS_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_CIRCUITS_REPOS : + { + if (circuits.sequenceur.compteur == 0) + { + circuits.sequenceur.compteur = CIRCUITS_PERIODE_MISE_A_JOUR; + + //------------------------------------------------ + // Appel de Gestion_circuit pour chaque circuit : + for (i = 0; i < circuits.nb; i ++) + { + if (! circuits.circuits[i].flag_validite) continue; + +// sprintf(tmp, "Circuit #%i : Consigne = %.2f ", i, circuits.circuits[i].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + gestion_circuit(i); + circuits.circuits[i].flag_dde_maj_bdd = TRUE; // On ne vérifie pas s'il y a eu évolution car ce serait un peu compliqué + } + + //----------------------------------------- + // Supervision du courant total par carte : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_config[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + if (systeme.modules_config[i].courant_maxi == 0) continue; // SI le seuil = 0, c'est que la fonction est dévalidée. + + switch(systeme.modules_config[i].type) + { + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP8DC : + { + circuit_maxi = MAX_NOMBRE_CIRCUITS; // Valeur 'impossible' par défaut + courant_maxi = 0; + courant_total = 0; + + // Recherche de tous les circuits de la carte : + for (j=0; j= SP8XX_NOMBRE_CIRCUITS) continue; // ... et dont le n° de circuit est valide pour cette carte + + // Calcul du courant total de la carte : + courant_total += circuits.circuits[j].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + + // Recehrche du circuit consommant le plus sur la carte (utile si on déclenche) : + if (circuits.circuits[j].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante > courant_maxi) + { + courant_maxi = circuits.circuits[j].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + circuit_maxi = j; + } + } + + // Action selon le dépassement ou non du seuil : + if (courant_total <= systeme.modules_config[i].courant_maxi) systeme.modules_config[i].courant_maxi_compteur = systeme.modules_config[i].courant_maxi_tempo; // Réarmement du compteur dès qu'on passe en dessous du seuil + else if (systeme.modules_config[i].courant_maxi_compteur == 0) // On dép&sse le seuil ET la tempo est expirée sans qu'on soit repassés sous le seuil : + { + if (circuit_maxi < MAX_NOMBRE_CIRCUITS) // Cas normal où on a trouvé le circuit qui consomme le plus sur la carte : + { + circuits.circuits[circuit_maxi].etat = ETAT_CIRCUIT_DELESTE; + + changement_consigne_circuit(circuit_maxi, OFF, CIRCUITS_ORIGINE_CONSIGNE_DEFAUT, 0); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, CIRCUIT_DELESTAGE, circuits.circuits[circuit_maxi].cle_primaire, systeme.modules_config[i].courant_maxi, NULL); // Enregistrement dans l'historique + + sprintf(tmp, "/!\\ Carte %i : Le courant total (%.3fA) est resté supérieur au seuil MAXI (%.3fA) pendant %i secondes consécutives. Le circuit %i est délesté car sa consommation est la plus élevée de la carte (%.3fA soit %.2f%% de la carte)", i, courant_total, systeme.modules_config[i].courant_maxi, systeme.modules_config[i].courant_maxi_tempo, circuit_maxi, courant_maxi, (courant_total != 0) ? ((courant_maxi*100)/courant_total) : 0); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + systeme.modules_config[i].courant_maxi_compteur = systeme.modules_config[i].courant_maxi_tempo; // Permet notamment d'attendre que la prise en compte du délestage du circuit le plus consommateur soit effective... + } + else // A priori, ne doit pas arriver car cela voudriat dire que'on a trouvé aucun circuit > 0A alors que le courant total dépasse 50A (par exemple)... + { + sprintf(tmp, "La détection de courant total a rencontér une erreur... La détection est dévalidée"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + break; + } + default : ; + } + } + + } + + break; + } + + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Circuits est inconnu [%i]. Il est repositionné au REPOS", circuits.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + circuits.sequenceur.etat = ETAT_SEQUENCEUR_CIRCUITS_REPOS; + break; + } + } +} + + + +//********************************************************************************************************************************************************************************** +// Fonction GESTION_CIRCUIT +// Séquenceur dédié à chaque circuit +// Reçoit : le n° du circuit +// Retour : Rien +//********************************************************************************************************************************************************************************** +void gestion_circuit(uint16_t no_circuit) +{ +int16_t no_entree_in_i; +int16_t no_entree_usb_i; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + //============================================================================================== + // Mise à jour des valeurs obtenues par un autre biais que 'c_bus_up_commande_etat_circuits()' : + //============================================================================================== + switch(systeme.modules_config[circuits.circuits[no_circuit].adresse_module].type) + { + //------------------------ + case TYPE_CARTE_SHIPBASE: + //------------------------ + { + // Courant USB : + // NB : la notion de valeur plancher du courant USB est traitée dans 'commandes.c', vers la ligne 1800... + float courant_usb; + float in_i; + float usb_i; + + no_entree_in_i = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_IN_I); + if ((no_entree_in_i >=0) && (no_entree_in_i < MAX_NOMBRE_ENTREES_PREDEFINIES)) in_i = entrees.predefinies.entrees[no_entree_in_i].valeur; + else in_i = 0; + + no_entree_usb_i = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_USB_I); + if ((no_entree_usb_i >=0) && (no_entree_usb_i < MAX_NOMBRE_ENTREES_PREDEFINIES)) usb_i = entrees.predefinies.entrees[no_entree_usb_i].valeur; + else usb_i = 0; + + if (systeme.modules_config[circuits.circuits[no_circuit].adresse_module].alim_valeur != 0) courant_usb = (usb_i * 5) / systeme.modules_config[circuits.circuits[no_circuit].adresse_module].alim_valeur; // Il est important de ramner le courant USB mesuré sur 5V sur la tension d'alimentation de ShipBase afin de l'extraire du courant de ShipBase. + else courant_usb = 0; + + if (in_i <= courant_usb) + { + courant_usb = 0; +#ifdef __DEBUG_CIRCUITS + sprintf(tmp, "/!\\ Le courant USB (compensé en tension) [%.2f A] est supérieur ou égal au courant de ShipBase [%.2f A]. Le courant USB n'est pas pris en compte du tout (ramené à 0A)...", courant_usb, in_i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + + // Calculs spécifiques en fonction du "circuit" de Shipbase : + switch(circuits.circuits[no_circuit].no_circuit) + { + //---------------------------- + case SB_CIRCUIT_NO_SHIPHEART : // Circuit correspondant à ShipBase sans les ports USB (appelé 'ShipHeart' dans la BDD) + { + // Tension : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante = systeme.modules_config[circuits.circuits[no_circuit].adresse_module].alim_valeur; + + // Courant : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = in_i - courant_usb; +#ifdef __DEBUG_CIRCUITS + sprintf(tmp, "Courant total ShipBase = [%.2f A], courant de ShipBase uniquement [%.2f A], courant USB (compensé en tension) = [%.2f A]", in_i, circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante, courant_usb); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + // Puissance : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante * circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + + break; + } + + //----------------------------- + case SB_CIRCUIT_NO_USB_AVANT : // Circuit correspondant aux ports USB seulement (appelé 'ShipHeart - USB' dans la BDD) + { + // Tension : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante = 5; // Attention : on applique la règle mais c'est une valeur théorique... + + // Courant : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante = usb_i; // Attention : c'est un courant "réel" sur 5V (non compensé en tension) + + // Puissance : + if (courant_usb == 0) circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = 0; + else circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = usb_i * 5; // Calculé directemenr depuis le courant mesuré sur 5V. + + break; + } + + //------------------------------ + case SB_CIRCUIT_NO_USB_ARRIERE : // Circuit correspondant aux ports USB seulement (appelé 'ShipHeart - USB arrière' dans la BDD) + { + // Tension : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante = 5; // Attention : on applique la règle mais c'est une valeur théorique... + + break; + } + default : break; + } + + break; + } + + //------------------------ + case TYPE_CARTE_SP812: + case TYPE_CARTE_SP8DC: + //------------------------ + { + // Tension : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante = systeme.modules_config[circuits.circuits[no_circuit].adresse_module].alim_valeur; + + // Puissance : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante * circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + + break; + } + + //------------------------ + case TYPE_CARTE_SP4AC: + //------------------------ + { + // Puissance : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante = circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante * circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + + break; + } + + //------------------------ + default: ; + //------------------------ + } + + //==================================== + // Intégration des cumuls du circuit : + //==================================== + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].cumul += circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION].valeur_courante; + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].cumul += circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante; + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_FREQUENCE].cumul += circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_FREQUENCE].valeur_courante; + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].cumul += circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_PUISSANCE].valeur_courante; + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].cumul += circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_TENSION_SECTEUR].valeur_courante; + circuits.circuits[no_circuit].cumuls_nb_val ++; // MàJ du nombre d'échantillons dans le cumul + + + //=========================================== + // Vérification de l'expiration du time-out : + //=========================================== + if ((circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante > 0) && + (circuits.circuits[no_circuit].time_out > 0) && + (circuits.circuits[no_circuit].compteur_time_out == 0)) + { + changement_consigne_circuit(no_circuit, OFF, CIRCUITS_ORIGINE_CONSIGNE_TIME_OUT, 0); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, CIRCUIT_EXPIRATION_TIME_OUT, circuits.circuits[no_circuit].cle_primaire, circuits.circuits[no_circuit].time_out, NULL); // Enregistrement dans l'historique + } + + //======================================== + // Surveillance du seuil haut de courant : + //======================================== + switch(circuits.circuits[no_circuit].sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_CIRCUIT_INIT : + { + circuits.circuits[no_circuit].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUITS_REPOS; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_CIRCUIT_REPOS : + { + // Recherche d'un dépassement de seuil de courant : + if ((circuits.circuits[no_circuit].intensite_seuil_haut > 0) && + (circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante > circuits.circuits[no_circuit].intensite_seuil_haut)) + { + circuits.circuits[no_circuit].sequenceur.compteur = circuits.circuits[no_circuit].intensite_seuil_haut_tempo / 10; // Le compteur est décrémenté toutes les 100ms. + circuits.circuits[no_circuit].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUIT_CHECK_LIMITATION; + break; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_CIRCUIT_CHECK_LIMITATION : + { + // Test de disparition du défaut avant expiration de la tempo : + if (circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_COURANT].valeur_courante <= circuits.circuits[no_circuit].intensite_seuil_haut) + { + circuits.circuits[no_circuit].sequenceur.compteur = 0; // RAZ pour usage ultérieur + circuits.circuits[no_circuit].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUIT_REPOS; + break; + } + + // Expiration de la tempo de filtrage : + if (circuits.circuits[no_circuit].sequenceur.compteur == 0) + { + changement_consigne_circuit(no_circuit, OFF, CIRCUITS_ORIGINE_CONSIGNE_DEFAUT, 0); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, CIRCUIT_LIMITATION, circuits.circuits[no_circuit].cle_primaire, circuits.circuits[no_circuit].intensite_seuil_haut, NULL); // Enregistrement dans l'historique + + circuits.circuits[no_circuit].etat = ETAT_CIRCUIT_LIMITATION; + circuits.circuits[no_circuit].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUIT_REPOS; + break; + } + + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur du circuit %i est inconnu [%i]. Il est repositionné au REPOS", no_circuit+1, circuits.circuits[no_circuit].sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + circuits.circuits[no_circuit].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUIT_REPOS; + break; + } + } + +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Effectue toutes les actions nécessaires lors d'un changement de consigne du circuit +// Reçoit : Le n° de circuit, la valeur de consigne, l'origine de la consigne et l'ID de cette origine +// Retourne : 0 si aucune erreur. -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t changement_consigne_circuit(uint16_t no_circuit, uint8_t consigne, uint8_t origine_consigne, uint16_t id_origine_consigne) +{ + if (no_circuit > MAX_NOMBRE_CIRCUITS) return (-1); + if (consigne > MAX_CONSIGNE_CIRCUIT) return(-1); + + // Mise à jour des variables du circuit : + circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante = consigne; + circuits.circuits[no_circuit].origine_consigne = origine_consigne; + circuits.circuits[no_circuit].id_origine_consigne = id_origine_consigne; + + // Emission d'une commande vers le Compagnon que si le circuit n'est pas sur ShipBase : + if (circuits.circuits[no_circuit].adresse_module != 0) + { + circuits.circuits[no_circuit].flag_changement_consigne_en_cours = true; // SDIR #179 + c_bus_down_commande_cde_circuits(circuits.circuits[no_circuit].adresse_module); // Consigne du circuit envoyée au Compagnon + } + + if (consigne > 0) + { + // RAZ du flag d'état du circuit à chaque fois qu'on l'active avec une consigne > 0 : + circuits.circuits[no_circuit].etat = ETAT_CIRCUIT_OK; + + // Armement du time-out, si le circuit est ON et qu'un time-out est configuré dessus : + if (circuits.circuits[no_circuit].time_out > 0) + { + circuits.circuits[no_circuit].compteur_time_out = (circuits.circuits[no_circuit].time_out * 10); // Réarmement du time-out si le circuit est ON. NB : le time-out est exprimé en secondes mais, pour gagner en précision sur les petites durées, on a passé le décomptage en 100ms dans 'hardware.c' d'où le x10... + } + } + + // Enregistrement du changement de consigne dans les mesures (qui ne sont faites que sur changement d'état pour les consignes) : + char date_vide[MAX_LONGUEUR_DATE_COURANTE] = ""; // Evite un warning depuis GCC14 + enregistrement_mesure(SOURCE_MESURE_CIRCUIT, no_circuit, CIRCUIT_VARIABLE_CONSIGNE, date_vide, BDD_ENREGISTREMENT_MESURE_PONCTUEL); + + return(0); +} + + + + +//********************************************************************************************************************************************************************************** +// Fonction INIT_CIRCUITS +// +//********************************************************************************************************************************************************************************** +int8_t init_circuits(void) +{ +uint16_t i; + + circuits.sequenceur.etat = ETAT_SEQUENCEUR_CIRCUITS_INIT; + circuits.sequenceur.compteur = CIRCUITS_TEMPO_INIT; + + for (i=0; i\t"); break; } case TYPE_BUS_DEBUG_M_BUS : { strcpy(nom_bus, "M-Bus"); if (strstr(sens, "Rx") != NULL) { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_CYAN); strcpy(prefixe_commande, "<\t\t"); } else { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_PURPLE); strcpy(prefixe_commande, ">\t"); } strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } default : { strcpy(nom_bus, "Unknown"); strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_RED); strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } } // Génération de la chaine complète pour l'écran : if ((output == AFFICHAGE_DEBUG_ECRAN) || (output == AFFICHAGE_DEBUG_ECRAN_ET_FICHIER)) printf("[%s] [%5s] %s %s%s %s \n", date_formatee, nom_bus, prefixe_couleur, prefixe_commande, commande, suffixe_couleur); // Génération de la chaine complète pour le fichier : if ((output == AFFICHAGE_DEBUG_FICHIER) || (output == AFFICHAGE_DEBUG_ECRAN_ET_FICHIER)) { sprintf(chaine, "[%s] [%5s] %s%s", date_formatee, nom_bus, prefixe_commande, commande); append_chaine_fichier(chaine, NOM_FICHIER_DEBUG_BUS); } } // ***************************************************************************************************************************************************************** // Fonction : Envoi le message de debug reçu vers l'écran et/ou le fichier de log après l'avoir formaté et horodaté. // Reçoit : Le fichier d'origine de la demande, la ligne où la demande a été faite dans ce fichier, le contenu du message à afficher et le type (donc sa couleur à l'écran). // Renvoit : Rien // ****************************************************************************************************************************************************************** void affichage_debug(char *fichier, int ligne, char *message, char type) { char chaine[MAX_LONGUEUR_LIGNE_FICHIER_LOG]; if (debug.mode == AFFICHAGE_DEBUG_AUCUN) return; // Affichage écran : if ((debug.mode == AFFICHAGE_DEBUG_ECRAN) || (debug.mode == AFFICHAGE_DEBUG_ECRAN_ET_FICHIER)) { affichage_debug_creation_ligne(chaine, fichier, ligne, message, type); printf("%s \n", chaine); } // Affichage fichier : if ((debug.mode == AFFICHAGE_DEBUG_ECRAN_ET_FICHIER) || (debug.mode == AFFICHAGE_DEBUG_FICHIER)) { affichage_debug_creation_ligne(chaine, fichier, ligne, message, TYPE_MESSAGE_DEBUG_FICHIER_SEULEMENT); append_chaine_fichier(chaine, debug.nom_fichier); } } // ***************************************************************************************************************************************************************** // Fonction : Renvoit la ligne de debug formatée sous la forme '[JJ/MM/AAAA HH:mm:ss.mmm][ : ] '. De plus, la partie // du est colorée en fonction du 'type' de message si l'affichage est destiné à l'écran. // Reçoit : Le pointeur sur la chaine à créer, le fichier d'origine de la demande, la ligne dans el fichier où la demande a été faite, le message et le type de message. // Renvoit : Rien // ****************************************************************************************************************************************************************** void affichage_debug_creation_ligne(char * chaine, char *fichier, int ligne, char *message, char type) { char date_formatee[MAX_LONGUEUR_DATE_COURANTE]; char *fichier_court = ""; char prefixe_couleur[20]; char suffixe_couleur[20]; // Préparation de la chaine de date : date_courante(date_formatee, DATE_COURANTE_AVEC_MS, FORMAT_DATE_COURANTE_USER); // Calcul du préfixe et du suffixe de couleur à utiliser pour l'écran en fonction du type de message : switch(type) { case TYPE_MESSAGE_DEBUG_OK: { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_GREEN); strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } case TYPE_MESSAGE_DEBUG_WARNING: { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_YELLOW); strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } case TYPE_MESSAGE_DEBUG_ERREUR: { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_RED); strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } case TYPE_MESSAGE_DEBUG_DEBUG: { strcpy(prefixe_couleur, DEBUG_TEXT_COLOR_PURPLE); strcpy(suffixe_couleur, DEBUG_TEXT_COLOR_RESET); break; } case TYPE_MESSAGE_DEBUG_PAR_DEFAUT: case TYPE_MESSAGE_DEBUG_FICHIER_SEULEMENT: default: { strcpy(prefixe_couleur, ""); strcpy(suffixe_couleur, ""); break; } // Couleur de police par défaut } if (fichier[0] != 0) // Si le nom de fichier est vide, on n'affichera pas l'entête de ligne... { // Préparation du nom de fichier : fichier_court = strrchr (fichier, '/'); // Positionne le pointeur 'fichier_court' sur la première occurence de '\' en partant de la fin de la chaine 'fichier'. if (fichier_court == NULL) fichier_court = fichier; else fichier_court ++; // Le '+1' permet d'éliminer le '/' de début de chaine... } // Génération de la chaine complète : sprintf(chaine, "[%s] [%15s : %4i] %s %s %s", date_formatee, fichier_court, ligne, prefixe_couleur, message, suffixe_couleur); } /********************************************************************************************************************************************************************************** Fonction INIT_AFFICHAGE_DEBUG En fonction de la l'état de la variable "debug", affiche ou non les messages qui lui sont adressés **********************************************************************************************************************************************************************************/ int8_t init_affichage_debug(void) { char date_formatee[MAX_LONGUEUR_DATE_COURANTE]; uint32_t no_ligne; uint32_t nb_lignes; FILE * ptr_fichier_tmp; char ligne[MAX_LONGUEUR_LIGNE_FICHIER_LOG]; switch (debug.mode) // NB : la valeur de 'debug' est évaluée à l'initialisation en fonction du champs 'Mode_debug' de TB_Configuration. { // ****************************************************************************************************** case AFFICHAGE_DEBUG_AUCUN: // Ni sortie sur écran ni sortie sur fichier. { break; } // ****************************************************************************************************** case AFFICHAGE_DEBUG_ECRAN: // Sortie sur écran mais pas sur fichier. case AFFICHAGE_DEBUG_ECRAN_ET_FICHIER: // Sortie sur écran et sur fichier : { clrscr(); // Effacement de l'écran printf("\n----------------- Début d'initialisation --------------------\n\n"); if (debug.mode == AFFICHAGE_DEBUG_ECRAN) break; // Dans ce cas, on n'effectue pas la partie Fichier ci-dessous... } // ****************************************************************************************************** case AFFICHAGE_DEBUG_FICHIER: // Sortie sur fichier mais pas sur écran : { // Purge du fichier : debug.ptr_fichier = fopen(debug.nom_fichier, "a+"); // Ouverture fichier de log en mode "append et lecture" qui permet, s'il n'existe pas, qu'il soit créé (sinon, avec "r", il n'est pas créé tout seul) if (debug.ptr_fichier == NULL) { printf("Ouverture du fichier %s impossible. L'initialisation est arrêtée !\n", debug.nom_fichier); exit(0); // NB : le 'return(-1)' ne semble pas fonctionner. Comme on est sur des cas de démarrage, je n'ai pas cherché plus loin... break; } // Recherche du nombre de lignes du fichier : nb_lignes = 0; if (debug.ptr_fichier != NULL) { rewind(debug.ptr_fichier); // Retour au début du fichier de logs puisque l'ouverture en 'append' nous a amené à la fin... while (fgets(ligne, MAX_LONGUEUR_LIGNE_FICHIER_LOG, debug.ptr_fichier) != NULL) nb_lignes ++; } // Si NULL, on considère que le fichier est nouveau et le test suivant sera négatif (nb_lignes = 0)... // Vérification si besoin de purger le fichier : if (nb_lignes > MAX_NOMBRE_LIGNES_FICHIER_LOG) { no_ligne = 0; rewind(debug.ptr_fichier); // Retour au début du fichier de logs puisque le comptage nous a amené à la fin... // Lecture du fichier jusqu'à MAX_NOMBRE_LIGNES_FICHIER_LOG avant la fin : while (fgets(ligne, MAX_LONGUEUR_LIGNE_FICHIER_LOG, debug.ptr_fichier) != NULL) // Lecture d'une ligne { no_ligne ++; if (no_ligne == (nb_lignes - MAX_NOMBRE_LIGNES_FICHIER_LOG)) break; } ptr_fichier_tmp = fopen(NOM_FICHIER_DEBUG_TMP, "w"); // Ouverture fichier de log temporaire en mode "overwrite"... if (ptr_fichier_tmp != NULL) { // Réécriture complète du fichier en ne gardant que le nombre de lignes souhaité (en supprimant les premières) : date_courante(date_formatee, DATE_COURANTE_SANS_MS, FORMAT_DATE_COURANTE_USER); fprintf(ptr_fichier_tmp, "#### Fichier de log tronqué le %s (nombre de lignes ramené de %i à %i) #### \n\n", date_formatee, nb_lignes, MAX_NOMBRE_LIGNES_FICHIER_LOG); // Lecture de la fin du fichier : while (fgets(ligne, MAX_LONGUEUR_LIGNE_FICHIER_LOG, debug.ptr_fichier) != NULL) // Lecture d'une ligne { fputs(ligne, ptr_fichier_tmp); } fclose(ptr_fichier_tmp); fclose(debug.ptr_fichier); // Copie du fichier temporaire dans le fichier de logs : debug.ptr_fichier = fopen(debug.nom_fichier, "w"); // Ouverture fichier de log en mode "overwrite"... ptr_fichier_tmp = fopen(NOM_FICHIER_DEBUG_TMP, "r"); // Ouverture fichier de log temporaire en mode "read"... while (fgets(ligne, MAX_LONGUEUR_LIGNE_FICHIER_LOG, ptr_fichier_tmp) != NULL) // Lecture d'une ligne { fputs(ligne, debug.ptr_fichier); // Copie de la ligne } fclose(ptr_fichier_tmp); // Fermeture du fichier temporaire de logs fclose(debug.ptr_fichier); // Fermeture du fichier de logs } else { // Message d'erreur 'brut' car le mode débug n'est pas encore activé : printf("\033[31m"); // Police en rouge... printf("ERREUR d'ouverture du fichier temporaire [%s]....!", NOM_FICHIER_DEBUG_TMP); printf("\033[0m \n"); // Police par défaut et retour à la ligne... return(-1); } } else fclose(debug.ptr_fichier); // Ecriture de la ligne d'inititalisation : debug.ptr_fichier = fopen(debug.nom_fichier, "a"); // Ouverture fichier de log en mode "append"... if (debug.ptr_fichier == NULL) { // Message d'erreur 'brut' car le mode débug n'est pas encore activé : printf("\033[31m"); // Police en rouge... printf("ERREUR d'ouverture du fichier de Logs [%s]....!", debug.nom_fichier); printf("\033[0m \n"); // Police par défaut et retour à la ligne... return(-1); } fprintf(debug.ptr_fichier, "\n----------------- Début d'initialisation --------------------\n\n") ; fclose(debug.ptr_fichier); if (nb_lignes > MAX_NOMBRE_LIGNES_FICHIER_LOG) sprintf(ligne, "Purge du fichier de log effectuée. Fichier ramené de %i à %i lignes", nb_lignes, MAX_NOMBRE_LIGNES_FICHIER_LOG); else sprintf(ligne, "Fichier de log ouvert [%s]. Contient actuellement %i lignes (max = %i)", debug.nom_fichier, nb_lignes, MAX_NOMBRE_LIGNES_FICHIER_LOG); affichage_debug(__FILE__, __LINE__, ligne, TYPE_MESSAGE_DEBUG_OK); break; } } // Vidage du fichier de log de bus : ptr_fichier_tmp = fopen(NOM_FICHIER_DEBUG_BUS, "w"); // Le fichier est juste ouvert en écriture (donc il est créé s'il n'existe pas et il est vidé s'il existe) if (ptr_fichier_tmp != NULL) fclose(ptr_fichier_tmp); // S'il existait, on le referme immédiatement. return(0); } // ***************************************************************************************************************************************************************** // Fonction : Ouvre le fichier dont le nom est passé en paramètre, ajoute à la fin la chaine reçue et referme le fichier. // Reçoit : Le pointeur sur la chaine à ajouter et le nom du fichier de log. // Renvoit : Rien // ****************************************************************************************************************************************************************** void append_chaine_fichier(char * chaine, char * fichier) { FILE * ptr_fichier; ptr_fichier = fopen(fichier, "a"); // Ouverture fichier de log en mode "append"... if (ptr_fichier != NULL) { fprintf(ptr_fichier, "%s \n", chaine); fclose(ptr_fichier); } } // ***************************************************************************************************************************************************************** // Fonction : Retourne sous forme de chaine le type de carte passé en paramètre // Reçoit : Le type de carte (sous forme d'entier, selon les valeurs dans 'systeme.c') // Renvoit : Le pointeur sur la chaine de nom // ****************************************************************************************************************************************************************** char * nom_type_carte(uint8_t type) { switch(type) { case TYPE_CARTE_SHIPBASE : { return("ShipBase"); break; } case TYPE_CARTE_SP812 : { return("ShipPower812"); break; } case TYPE_CARTE_SBS : { return("ShipBS"); break; } case TYPE_CARTE_SP4AC : { return("ShipPower4AC"); break; } case TYPE_CARTE_SP8DC : { return("ShipPower8DC"); break; } default : { return("Inconnu..."); break; } } } \ No newline at end of file diff --git a/sources/debug.h b/sources/debug.h new file mode 100644 index 0000000..4a1bd1a --- /dev/null +++ b/sources/debug.h @@ -0,0 +1 @@ +//********************************************************************************************************************************************************************************** // Fichier DEBUG.H // // Contient tous les define liés aux fontions de debug et de logs... //**********************************************************************************************************************************************************************************/ // Maximums : #define MAX_LONGUEUR_NOM_FICHIER_DEBUG 100 #define MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG (MAX_LONGUEUR_REQUETE_MYSQL - 100) // Utilisé dans chaque fonction ayant besoin d'un buffer temporaire pour cosntruire le message de debug #define MAX_LONGUEUR_LIGNE_FICHIER_LOG MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG #define MAX_NOMBRE_LIGNES_FICHIER_LOG 100000 //2500 // En pratique, la taille du fichier de log n'est pas limitée (on fait des 'append' au fil de l'eau et la seule limite sera l'espace libre du disque). // Par contre, à chaque init (dans fonctions.c, 'init_affichage_debug()'), on purge toutes les lignes dépassant MAX_NOMBRE_LIGNES_FICHIER_LOG. // Mode d'affichage des messages de debug #define AFFICHAGE_DEBUG_AUCUN 0 #define AFFICHAGE_DEBUG_ECRAN 1 #define AFFICHAGE_DEBUG_FICHIER 2 #define AFFICHAGE_DEBUG_ECRAN_ET_FICHIER 3 // Types de messages de débug : #define TYPE_MESSAGE_DEBUG_PAR_DEFAUT 0 #define TYPE_MESSAGE_DEBUG_OK 1 #define TYPE_MESSAGE_DEBUG_WARNING 2 #define TYPE_MESSAGE_DEBUG_ERREUR 3 #define TYPE_MESSAGE_DEBUG_FICHIER_SEULEMENT 4 #define TYPE_MESSAGE_DEBUG_DEBUG 5 // Type de bus dont le debug est possible : #define TYPE_BUS_DEBUG_C_BUS 0 #define TYPE_BUS_DEBUG_M_BUS 1 #define TYPE_BUS_DEBUG_G_BUS 1 // Codes des couleurs d'affichage sur la console : #define DEBUG_TEXT_COLOR_RESET "\033[0m" // Reset la couleur précémment demandée #define DEBUG_TEXT_COLOR_BLACK "\033[0;30m" #define DEBUG_TEXT_COLOR_RED "\033[0;31m" #define DEBUG_TEXT_COLOR_GREEN "\033[0;32m" #define DEBUG_TEXT_COLOR_YELLOW "\033[0;33m" #define DEBUG_TEXT_COLOR_BLUE "\033[0;34m" #define DEBUG_TEXT_COLOR_PURPLE "\033[0;35m" #define DEBUG_TEXT_COLOR_CYAN "\033[0;36m" #define DEBUG_TEXT_COLOR_WHITE "\033[0;37m" \ No newline at end of file diff --git a/sources/ecran.c b/sources/ecran.c new file mode 100644 index 0000000..4a87d91 --- /dev/null +++ b/sources/ecran.c @@ -0,0 +1,229 @@ +// ********************************************************************************************************************************************************************************** +// Fichier ECRAN.C +// +// Contient toutes les fonctions liées à l'écran tactile. +// ********************************************************************************************************************************************************************************** + +// Include : +#include "main.h" + +// Prototypes : +int8_t set_luminosite_ecran(uint16_t luminosite); +void gestion_ecran(void); +int8_t init_ecran(void); + + +// Variables globales : +struct Struct_ecran ecran; + + +//================================================================================================================================================================== +// Fonction : Met à jour le niveau de luminosité de l'écran +// Reçoit : Le niveau de luminosité attendu (de 0 à 100%), en centièmes de pourcent. +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t set_luminosite_ecran(uint16_t luminosite) +{ + + if (luminosite > ECRAN_CONSIGNE_MAX) return(-1); + + if (luminosite == 0) + { + if (ecran.sequenceur.etat != ETAT_SEQUENCEUR_ECRAN_REPOS_ETEINT) ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_DEMANDE_EXTINCTION; + } + else + { + if (ecran.sequenceur.etat != ETAT_SEQUENCEUR_ECRAN_REPOS_ALLUME) ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_DEMANDE_ALLUMAGE; + else // Si on est déjà allumé, uniquement envoi de la consigne au Compagnon pour mise à jour de la consigne de backlight (reste allumé mais à une autre consigne) + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(ecran.consigne_backlight, 0); + else c_bus_down_commande_cde_backlight(ecran.consigne_backlight, ecran.consigne_leds); + } + } + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Fonction de plus haut niveau de gestion de l'écran +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void gestion_ecran(void) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(ecran.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_INIT : + { + if ((ecran.sequenceur.compteur == 0) && (c_bus.sequenceur.etat != ETAT_SEQUENCEUR_C_BUS_INIT)) + { + if (ecran.consigne_backlight == 0) ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_DEMANDE_EXTINCTION; + else ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_DEMANDE_ALLUMAGE; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_DEMANDE_EXTINCTION : + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(0, 0); + else c_bus_down_commande_cde_backlight(0, ecran.consigne_leds); + + ecran.sequenceur.compteur = ECRAN_TEMPO_EXTINCTION_BACKLIGHT_LVDS; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_BACKLIGHT; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_BACKLIGHT : + { + if (ecran.sequenceur.compteur == 0) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_pdown_hdmi_cmd_n, OFF); // Coupure de la commande du HDMI et donc du LVDS. 0 est l'adresse de ShipBase + + ecran.sequenceur.compteur = ECRAN_TEMPO_EXTINCTION_LVDS_VGX; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_LVDS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_LVDS : + { + if (ecran.sequenceur.compteur == 0) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_do_lcd_en_vgx, OFF); // .. puis coupure de la commande de VGx. 0 est l'adresse de ShipBAse + + ecran.sequenceur.compteur = ECRAN_TEMPO_EXTINCTION_VGX_VDD; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_VGX; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_VGX : + { + if (ecran.sequenceur.compteur == 0) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_do_lcd_en_vdd, OFF); // Coupure immédiate de la commande de VDD. 0 est l'adresse de ShipBase + + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_REPOS_ETEINT; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_REPOS_ETEINT : + { + break; + } + + + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_DEMANDE_ALLUMAGE : + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_do_lcd_en_vdd, ON); // Activation immédiate de la commande de VDD. 0 est l'adresse de ShipBase + + ecran.sequenceur.compteur = ECRAN_TEMPO_ALIMENTATION_VDD_LVDS; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VDD; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VDD : + { + if (ecran.sequenceur.compteur == 0) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_pdown_hdmi_cmd_n, ON); // Activation de la commande du HDMI qui émet alors du LVDS. 0 est l'adresse de ShipBase + + ecran.sequenceur.compteur = ECRAN_TEMPO_ALIMENTATION_LVDS_VGX; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_LVDS; + } + + break; + } + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_LVDS : + { + if (ecran.sequenceur.compteur == 0) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.sortie_gpio_cm_do_lcd_en_vgx, ON); // Activation immédiate de la commande de VGx. 0 est l'adresse de ShipBase + + ecran.sequenceur.compteur = ECRAN_TEMPO_ALIMENTATION_VGX_BACKLIGHT; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VGX; + } + + break; + } + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VGX : + { + if (ecran.sequenceur.compteur == 0) + { + if (ecran.validation_leds == 0) c_bus_down_commande_cde_backlight(ecran.consigne_backlight, 0); + else c_bus_down_commande_cde_backlight(ecran.consigne_backlight, ecran.consigne_leds); + + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_REPOS_ALLUME; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_ECRAN_REPOS_ALLUME : + { + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Ecran est inconnu [%i]. Il est repositionné au REPOS", ecran.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_INIT; + break; + } + } +} + + + +//================================================================================================================================================================== +// Fonction : Initialise les fonctions de l'écran +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t init_ecran(void) +{ + + ecran.sequenceur.compteur = ECRAN_TEMPO_INIT; + ecran.sequenceur.etat = ETAT_SEQUENCEUR_ECRAN_INIT; + + // Ajustement des variables selon la génération de ShipBase en cours : + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + ecran.sortie_gpio_cm_pdown_hdmi_cmd_n = CM3P_Pdown_HDMI_CMD_N; + ecran.sortie_gpio_cm_do_lcd_en_vgx = CM3P_DO_LCD_EN_VGx; + ecran.sortie_gpio_cm_do_lcd_en_vdd = CM3P_DO_LCD_EN_VDD; + } + else + { + ecran.sortie_gpio_cm_pdown_hdmi_cmd_n = CM4_Pdown_HDMI_CMD_N; + ecran.sortie_gpio_cm_do_lcd_en_vgx = CM4_DO_EN_LCD_VGx; + ecran.sortie_gpio_cm_do_lcd_en_vdd = CM4_DO_EN_LCD_VDD; + } + + return(0); +} \ No newline at end of file diff --git a/sources/ecran.h b/sources/ecran.h new file mode 100644 index 0000000..c90222d --- /dev/null +++ b/sources/ecran.h @@ -0,0 +1,47 @@ +//********************************************************************************************************************************************************************************** +// Fichier ECRAN.H +// +// Contient tous les define liés à l'écran... +//**********************************************************************************************************************************************************************************/ + +//#define CM3P_Pdown_HDMI_CMD_N est renseigné dans 'sorties.h' +//#define CM3P_DO_LCD_EN_VGx est renseigné dans 'sorties.h' +//#define CM3P_DO_LCD_EN_VDD est renseigné dans 'sorties.h' + + + // Maximums : +#define ECRAN_CONSIGNE_MAX 100 // Soit 100% +#define ECRAN_CONSIGNE_MIN 0 // Soit éteint +#define LEDS_CONSIGNE_MAX 100 // Soit 100% +#define LEDS_CONSIGNE_MIN 0 // Soit éteint + + + // Timings +#define ECRAN_TEMPO_INIT 2 // En x 10ms En pratique, on attend que le C-Bus soit prêt pour envoyer la commande de backlight... + +#define ECRAN_TEMPO_EXTINCTION_BACKLIGHT_LVDS 120 // En x 10ms Supérieur ou égal à 0ms selon datasheet de l'écran. Le choix est donc fonctionnel et d'attendre que le backlight soit éteint doucement avant de modifier les alims. +#define ECRAN_TEMPO_EXTINCTION_LVDS_VGX 2 // En x 10ms Supérieur à 0 selond datasheet de l'écran +#define ECRAN_TEMPO_EXTINCTION_VGX_VDD 2 // En x 10ms Entre 0 et 50ms depuis la coupure LVDS selon datasheet de l'écran (donc [ECRAN_TEMPO_EXTINCTION_LVDS_VGX + ECRAN_TEMPO_ALIMENTATION_VGX_VDD] entre 0 et 50ms) + +#define ECRAN_TEMPO_ALIMENTATION_VDD_LVDS 4 // En x 10ms Entre 0 et 50ms selon datasheet de l'écran +#define ECRAN_TEMPO_ALIMENTATION_LVDS_VGX 2 // En x 10ms Juste > 0 selon datasheet de l'écran +#define ECRAN_TEMPO_ALIMENTATION_VGX_BACKLIGHT 15 // En x 10ms Supérieur à 120ms selon datasheet de l'écran + + +// #define ECRAN_TEMPO_ALIMENTATION_VDD_VGX 2 // En x 10ms Tempo à l'allumage de l'écran entre l'alimentation de VDD puis de VGx + + + +#define ETAT_SEQUENCEUR_ECRAN_INIT 0 + +#define ETAT_SEQUENCEUR_ECRAN_DEMANDE_EXTINCTION 1 +#define ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_BACKLIGHT 2 +#define ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_LVDS 3 +#define ETAT_SEQUENCEUR_ECRAN_ATTENTE_EXTINCTION_VGX 4 +#define ETAT_SEQUENCEUR_ECRAN_REPOS_ETEINT 5 + +#define ETAT_SEQUENCEUR_ECRAN_DEMANDE_ALLUMAGE 6 +#define ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VDD 7 +#define ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_LVDS 8 +#define ETAT_SEQUENCEUR_ECRAN_ALLUMAGE_ATTENTE_VGX 9 +#define ETAT_SEQUENCEUR_ECRAN_REPOS_ALLUME 10 \ No newline at end of file diff --git a/sources/entrees.c b/sources/entrees.c new file mode 100644 index 0000000..64b9023 --- /dev/null +++ b/sources/entrees.c @@ -0,0 +1,884 @@ +//********************************************************************************************************************************************************************************** +// Fichier ENTREES.C +// +// Contient toutes les fonctions liées à la lecture de l'état des entrées +//**********************************************************************************************************************************************************************************/ + +// Includes : +#include "main.h" + +// Prototypes : +void gestion_entrees(void); +void gestion_entrees_aux(void); +void gestion_entrees_level_sensor(void); +void gestion_entrees_volt(void); +void gestion_entrees_nmea(void); +void gestion_entrees_ow(void); +void gestion_entrees_predefinies(void); +void gestion_entrees_core(void); +void gestion_entrees_gpio(void); +int8_t init_entrees(void); +int8_t init_entrees_aux(void); +int8_t init_entrees_level_sensor(void); +int8_t init_entrees_volt(void); +int8_t init_entrees_nmea(void); +int8_t init_entrees_ow(void); +int8_t init_entrees_predefinies(void); +int8_t init_entrees_core(void); +int8_t init_entrees_gpio(void); +float lecture_entree(uint8_t type, uint8_t module, uint8_t no, bool flag_immediat); +int16_t entree_no(uint8_t type, uint8_t module, uint8_t no); +uint8_t lecture_entree_gpio(uint8_t no, int8_t logique); + + +// Variables globales : +struct Struct_entrees entrees; +struct gpiod_chip *gpiod_chip0; // Nécessaire pour CM5 +struct gpiod_line_request *gpiod_entrees_request; // Nécessaire pour CM5 + + + +/********************************************************************************************************************************************************************************** + Fonction LECTURE_ENTREE + + Fonction utilisée pour connaitre l'état d'une entrée, quel que soit sont type. + Reçoit en paramètre : le Type d'entrée, le module comportant l'entrée, le n° sur le module et un flag permettant de forcer la lecture immédiate (pour GPIO) + Retourne : l'état de l'entrée + NB : Dans le cas où le flag est 'faux', l'appel à cette fonction remontera le dernier état connu, mis à jour par le gestionnaire correspondant au type. + NB2 : L'utilisation du flag n'est possible que pour les entrées de type GPIO (pour les autres, il est ignoré...). Ce flag est à utiliser avec modération... +**********************************************************************************************************************************************************************************/ +float lecture_entree(uint8_t type, uint8_t module, uint8_t no, bool flag_immediat) +{ +int16_t x; + + x = entree_no(type, module, no); + + switch(type) + { + //----------------------------- + case TYPE_ENTREES_AUXILIAIRES : + { + return(entrees.aux.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_LEVEL_SENSOR : + { + return(entrees.level_sensor.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_VOLTAGE : + { + return(entrees.volt.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_NMEA : + { + return(entrees.nmea.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_OW : + { + return(entrees.ow.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_PREDEFINIES : + { + return(entrees.predefinies.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_CORE : + { + return(entrees.core.entrees[x].valeur); + break; + } + //----------------------------- + case TYPE_ENTREES_GPIO : + { + if (flag_immediat) entrees.gpio.entrees[x].valeur = lecture_entree_gpio(no, entrees.gpio.entrees[x].logique); + return(entrees.gpio.entrees[x].valeur); + break; + } + //----------------------------- + default: ; + } + + return (0xFFFFFFFF); // Cas d'erreur +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES + + Lance le traitement régulier du gestionnaire de chaque type d'entrée +**********************************************************************************************************************************************************************************/ +void gestion_entrees(void) +{ + + gestion_entrees_aux(); + gestion_entrees_level_sensor(); + gestion_entrees_volt(); + gestion_entrees_nmea(); + gestion_entrees_ow(); + gestion_entrees_predefinies(); + gestion_entrees_core(); + gestion_entrees_gpio(); +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_AUX + + Fonction utilisée pour mettre à jour toutes les sorties GPIO de façon synchrones +**********************************************************************************************************************************************************************************/ +void gestion_entrees_aux(void) +{ + // Il n'y a rien à faire car l'état des entrées AUX est remonté automatiquement par le Compagnon via une commande UP. +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_LEVEL_SENSOR + +**********************************************************************************************************************************************************************************/ +void gestion_entrees_level_sensor(void) +{ + + switch(entrees.level_sensor.sequenceur.etat) + { + case ETAT_SEQUENCEUR_ENTREES_LEVEL_SENSOR_INIT: + { + if ((entrees.level_sensor.sequenceur.compteur == 0) && (systeme.flag_cm3p_ready)) + { + c_bus_down_commande_config_level_sensors(); // Commande de config des entrées Level Sensor vers le Compagnon sur C-Bus + + entrees.level_sensor.sequenceur.etat = ETAT_SEQUENCEUR_ENTREES_LEVEL_SENSOR_REPOS; + } + break; + } + + case ETAT_SEQUENCEUR_ENTREES_LEVEL_SENSOR_REPOS: + { + break; + } + + default : entrees.level_sensor.sequenceur.etat = ETAT_SEQUENCEUR_ENTREES_LEVEL_SENSOR_REPOS; + } +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_VOLT + +**********************************************************************************************************************************************************************************/ +void gestion_entrees_volt(void) +{ + // Il n'y a rien à faire car l'état des entrées VOLTAGE est remonté automatiquement par le Compagnon via une commande UP. +} + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_NMEA + +**********************************************************************************************************************************************************************************/ +void gestion_entrees_nmea(void) +{ + // Il n'y a rien à faire car l'état des entrées NMEA est remonté automatiquement par le Compagnon via une commande UP. +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_OW + +**********************************************************************************************************************************************************************************/ +void gestion_entrees_ow(void) +{ + // Il n'y a rien à faire car l'état des entrées OW est remonté automatiquement par le Compagnon via une commande UP. +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_PREDEFINIES + +**********************************************************************************************************************************************************************************/ +void gestion_entrees_predefinies(void) +{ + // Il n'y a rien à faire car l'état des entrées PREDEFINIES est remonté automatiquement par le Compagnon via les commandes UP. +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_CORE + + Séquenceur permettant la lecture des trames reçues sur la ligne Téléinfo du compteur EDF +**********************************************************************************************************************************************************************************/ +void gestion_entrees_core(void) +{ +int i; +char commande[MAX_TAILLE_COMMANDE_SHELL]; +char chaine[300]; + + if (entrees.core.sequenceur.compteur == 0) + { + entrees.core.sequenceur.compteur = PERIODE_LECTURE_ENTREES_CORE; // Réarmement du timer périodique + + strcpy(commande, "{ "); // Attention, l'espace après le '{' est obligatoire ! + for (i=0; i 0) // Dans le cas où on n'a pas effectué un comptage sur 1 seconde au moins, on ne traite pas la mesure de l'entrée : + { +// printf("Vitesse boucle principale = %i boucles/seconde\n", (supervision_boucle_principale.nb_boucles / supervision_boucle_principale.nb_secondes)); + supervision_boucle_principale.nb_boucles = 0; + supervision_boucle_principale.nb_secondes = 0; + } + } + } + strcat(commande, " }"); // Attention, l'espace avant le '{' est obligatoire ! + sprintf(chaine, " > %s/%s &", REPERTOIRE_TMP_CORE, NOM_FICHIER_ETAT_ENTREES_CORE); + strcat(commande, chaine); +//printf("Commande Shell = [%s]\n", commande); + commande_shell(commande); + } +} + + +/********************************************************************************************************************************************************************************** + Fonction ENTREES_GPIO + + Fonction utilisée pour rendre la lecture des entrées GPIO synchrones (tous les états lus d'un coup) +**********************************************************************************************************************************************************************************/ +void gestion_entrees_gpio(void) +{ +int i; + + if (entrees.gpio.sequenceur.compteur == 0) + { + entrees.gpio.sequenceur.compteur = PERIODE_LECTURE_ENTREES_GPIO; // Réarmement du timer périodique + + for (i=0; i0) ou pas (0). + heure = 0; + minute = 0; + + if (++ events.compteur_diviseur_conditions_horaires >= EVENTS_DIVISEUR_CONDITIONS_HORAIRES) + { + events.compteur_diviseur_conditions_horaires = 0; // A priori, toutes les 1s + + now = time (NULL); // Génère le timestamp de l'heure courante (UTC) + now += (systeme.fuseau_horaire + systeme.heure_ete) * 60 * 60; // Passage en heure locale + tm_now = *localtime (&now); // Convertit le timestamp en une structure lisible + + if (events.derniere_minute_traitee != tm_now.tm_min) // A priori, toutes les minutes, à 00s + { + jour = tm_now.tm_wday; + if (jour == 0) jour = 7; // de lundi (1) à dimanche (7) + heure = tm_now.tm_hour; // de 0 à 23 + minute = tm_now.tm_min; // de 0 à 59 + + events.derniere_minute_traitee = minute; // Pour utilisation lors des boucles suivantes + +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Traitement horaire des Events avec Jour=%i, Heure=%i et Minutes=%i", jour, heure, minute); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + } + + //================================================================== + // Appel du gestionnaire de chaque event valide : + for (i = 0; i < events.nb; i ++) + { + if (!events.events[i].flag_validite) continue; // Si l'event n'est pas valide, on ne le traite pas + + // Calcul du résultat du test dès lors que l'event en contient un : + // Cela permet d'abord de mettre à jour son état dans la BDD et être utilisé par Qt. + // Cela permet aussi de ne plus le calculer dans la suite du traitement. + if (events.events[i].condition_test.flag_validite) + { + test_temp = test_event(i); + if (events.events[i].condition_test.etat_test != test_temp) + { + events.events[i].condition_test.etat_test = test_temp; + events.events[i].flag_dde_maj_bdd = true; + } + } + + // Traitement de l'event : + gestion_event(i, jour, heure, minute); // Rappel : si les conditions horaires ne sont PAS applicables, 'jour == 0' + } + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + default : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "L'état du séquenceur EVENTS est inconnu [%i]. Il est repositionné au REPOS", events.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + events.sequenceur.etat = ETAT_SEQUENCEUR_EVENTS_REPOS; + break; + } + } + +} + +//================================================================================================================================================================== +// Fonction : Traite le séquenceur de test d'un évènement +// Reçoit : le n° de l'évènement +// Renvoit : Rien +//================================================================================================================================================================== +void gestion_event(uint16_t no_event, uint8_t jour, uint8_t heure, uint8_t minute) +{ +uint16_t i; +bool flag_repetition; +uint8_t compteur_repetitions; // Sécurite... +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + compteur_repetitions = 0; + + do { + flag_repetition = false; // Par défaut. Une éventuelle répétition doit être demandée explicitement. + + switch(events.events[no_event].sequenceur.etat) + { + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_INIT : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + if (events.events[no_event].sequenceur.compteur == 0) + { + // SDIR #68 (début) : + if (events.events[no_event].conditions_horaires.nb == 0) // S'il n'y a acune condition horaire : + { + // NB : la valeur de 'etat_test' a été mise à jour juste avant l'appel de 'gestion_event()' ci-dessus... + if (events.events[no_event].condition_test.etat_test) events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_ATTENTE_DESACTIVATION; // Si la condition est déjà vraie au démarrage, on passe en phase d'attenet du retour à False avant de traiter le premier front d'apparition. + else events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; // Si la condition est False, on peut dès à présent attendre le premier front. + } + else events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; // S'il y a une ou plusieurs conditions horaires, on passe au repos dans tous les cas, en attente de la condition horaire pour faire le premier test. + // SDIR #68 (fin) + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_REPOS : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + //==================================================== + // Conditions horaire : + if (events.events[no_event].conditions_horaires.nb > 0) // Cet event a une condition horaire (au moins une) + { + if (jour > 0) // Dans ce cas, on sait que les conditions horaires doivent être testées + { + // Parcours de toutes les conditions horaires de l'évent : + for (i = 0; i < events.events[no_event].conditions_horaires.nb; i ++) + { + if (!events.events[no_event].conditions_horaires.conditions[i].flag_validite) continue; + + // Test du jour : + if ((events.events[no_event].conditions_horaires.conditions[i].jour != JOUR_SEMAINE_TOUS_LES_JOURS) && + (events.events[no_event].conditions_horaires.conditions[i].jour != jour)) continue; + + // Test de l'heure et de la minute : + if (events.events[no_event].conditions_horaires.conditions[i].heure < 24) // La condition correspond à une heure normale : + { + if (events.events[no_event].conditions_horaires.conditions[i].heure != heure) continue; + if (events.events[no_event].conditions_horaires.conditions[i].minute != minute) continue; + } + else // La condition correspond à une heure spéciale : + { + if (systeme.heures_speciales[events.events[no_event].conditions_horaires.conditions[i].heure - 24].heures != heure) continue; // Comparaison entre 'heure' de l'heure spéciale et 'heure' actuelle + if (systeme.heures_speciales[events.events[no_event].conditions_horaires.conditions[i].heure - 24].minutes != minute) continue; // Comparaison entre 'minute' de l'heure spéciale et 'minute' actuelle + } +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Event #%i / condition #%i : Condition horaire validée", no_event, i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); +#endif + // Le déclenchement horaire est validé : + if (events.events[no_event].condition_test.flag_validite) // Si l'event comprend un test : + { + // Test de la condition : + if (events.events[no_event].condition_test.etat_test) + { +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Event #%i / condition #%i : Résultat du test = TRUE. Lancement de la Tempo (%i sec.)", no_event, i, events.events[no_event].sequenceur.compteur = events.events[no_event].tempo); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); +#endif + + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_LANCEMENT_TEMPO; + flag_repetition = true; // Permet le traitement du déclenchement sans attendre. + } +#ifdef __DEBUG_EVENTS + else + { + sprintf(tmp, "Event #%i / condition #%i : Résultat du test = FALSE ", no_event, i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#endif + } + else // Si l'event ne comprend pas de test : + { + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_DECLENCHEMENT; + flag_repetition = true; // Permet le traitement du déclenchement sans attendre. +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Event #%i / condition #%i : L'évènement ne comprend pas de test. Il est donc considéré comme déclenché (quelle que soit la valeur de Tempo [qui est de %i sec.])", no_event, i, events.events[no_event].sequenceur.compteur = events.events[no_event].tempo); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + } + } + break; // Si une condition horaire est valide sur cet event, on ne traite pas la suite (quel que soit le résultat du test horaire lui-même) + } + + + //==================================================== + // Test sans condition horaire : + if (events.events[no_event].condition_test.flag_validite) + { + if (events.events[no_event].compteur_diviseur_gestion > 0) events.events[no_event].compteur_diviseur_gestion --; + if (events.events[no_event].compteur_diviseur_gestion == 0) + { + events.events[no_event].compteur_diviseur_gestion = events.events[no_event].diviseur_gestion; // Rechargement du diviseur (sans effet si 'diviseur' = 0 ou 1) + + // Test de la condition : + if (events.events[no_event].condition_test.etat_test) + { +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Event #%i : Résultat du test = TRUE. Lancement de la Tempo (%i sec.)", no_event, events.events[no_event].sequenceur.compteur = events.events[no_event].tempo); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); +#endif + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_LANCEMENT_TEMPO; + flag_repetition = true; // Permet le traitement du déclenchement sans attendre. + } +#ifdef __DEBUG_EVENTS + else + { + sprintf(tmp, "Event #%i : Résultat du test = FALSE ", no_event); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#endif + } + } + + break; + } + + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_LANCEMENT_TEMPO : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + // Armement de la tempo si une tempo est demandée : + if (events.events[no_event].tempo > 0) + { + events.events[no_event].sequenceur.compteur = events.events[no_event].tempo * 10; // Passage de secondes en x100ms du compteur + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_ATTENTE_FIN_TEMPO; + } + // ou déclenchement direct si aucune tempo n'est demandée : + else + { + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_DECLENCHEMENT; + flag_repetition = true; // Permet le traitement du déclenchement sans attendre. + } + + break; + } + + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_ATTENTE_FIN_TEMPO : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + if (events.events[no_event].sequenceur.compteur == 0) + { + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_DECLENCHEMENT; + flag_repetition = true; // Permet de traiter le déclenchement dans la foulé, sana attendre le prochain appel au gestyionnaire d'évènements + } + else + { + // Test d'une éventuelle désactivation de l'Event avant la fin de la tempo : + if (!events.events[no_event].condition_test.etat_test) + { + sprintf(tmp, "L'état de l'évènement #%i est revenu à FAUX avant l'expiration de sa temporisation [%i secondes]. Il est repositionné au REPOS sans déclenchement.", no_event, events.events[no_event].tempo); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + events.events[no_event].sequenceur.compteur = 0; // RAZ du compteur qui n'est plus utile + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; // Retour au repos + } + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_DECLENCHEMENT : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "Déclenchement de l'évènement # %i [clé_primaire = %i]", no_event, events.events[no_event].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + + declenchement_event(no_event); + + if (events.events[no_event].conditions_horaires.nb > 0) events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; // S'il y a condition horaire, on revient directement au repos, en attente de la prochaine condition horaire. + else events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_ATTENTE_DESACTIVATION; // S'il n'y a PAS de condition horaire, on est obligé d'attendre la disparition de la condition avant de revenir au repos. + + break; + } + + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_EVENT_ATTENTE_DESACTIVATION : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + // Vérifie que la condition de l'évènement a disparue : + if (!events.events[no_event].condition_test.etat_test) + { +#ifdef __DEBUG_EVENTS + sprintf(tmp, "Event #%i : La condition de test est repassée à False. Le séquenceur repasse au repos.", no_event); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); +#endif + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; // Retour au repos + } + + break; + } + + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + default : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "L'état du séquenceur EVENT est inconnu [%i]. Il est repositionné au REPOS", events.events[no_event].sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + events.events[no_event].sequenceur.etat = ETAT_SEQUENCEUR_EVENT_REPOS; + break; + } + } + } while ((flag_repetition) && (compteur_repetitions ++ < MAX_REPETITIONS_SEQUENCEUR_EVENT)); + + + // Message d'erreur si le nombre de répétitions est trop élevé : + if (compteur_repetitions >= MAX_REPETITIONS_SEQUENCEUR_EVENT) + { + sprintf(tmp, "Le séquenceur EVENT a rencontré un nombre de répétitions trop élevé [%i] pour l'évènement #%i et à l'état du séquenceur %i. Les répétitions sur cet event sont arrêtées de façon forcée.", MAX_REPETITIONS_SEQUENCEUR_EVENT, no_event, events.events[no_event].sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Teste si la condition de l'évènement est vraie ou fausse +// Reçoit : Le numéro de l'évènement +// Retourne : Vrai ou Faux +//********************************************************************************************************************************************************************************** +bool test_event(uint16_t no_event) +{ +float valeur; + + + // Si l'event ne contient aucune condition de test, on retourne directement FALSE : + if (!events.events[no_event].condition_test.flag_validite) return (false); + + //================================================== + // Recherche de la valeur en cours de la source : + switch(events.events[no_event].condition_test.type_source) + { + case SOURCE_MESURE_CIRCUIT: + { + valeur = circuits.circuits[events.events[no_event].condition_test.no_source].variables[events.events[no_event].condition_test.no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_CAPTEUR: + { + valeur = capteurs.capteurs[events.events[no_event].condition_test.no_source].variables[events.events[no_event].condition_test.no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_RESERVOIR: + { + valeur = reservoirs.reservoirs[events.events[no_event].condition_test.no_source].variables[events.events[no_event].condition_test.no_variable].valeur_courante; + break; + } + case SOURCE_MESURE_BATTERIE: + { + valeur = batteries.batteries[events.events[no_event].condition_test.no_source].variables[events.events[no_event].condition_test.no_variable].valeur_courante; + break; + } + default: return(false); // Ne devrait pas arriver puisqu'on a renvoyé FALSE pour les Event ne contenant que des conditions horaires... + } +#ifdef __DEBUG_EVENTS +/* char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Test de l'évent #%i : Type_source=%i, N° source=%i, N° de variable=%i, Valeur= %.2f, Condition de test=%i", + no_event, + events.events[no_event].condition_test.type_source, + events.events[no_event].condition_test.no_source, + events.events[no_event].condition_test.no_variable, + valeur, + events.events[no_event].condition_test.test); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +*/ +#endif + + //================================================== + // Test selon le type de test de l'évènement : + switch(events.events[no_event].condition_test.test) + { + //------------------------------------- + case EVENTS_TYPE_TEST_STRICT_INFERIEUR: + { + return (valeur < events.events[no_event].condition_test.seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_INFERIEUR_OU_EGAL: + { + return (valeur <= events.events[no_event].condition_test.seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_EGAL: + { + return (fabs(valeur - events.events[no_event].condition_test.seuil) < FLT_MIN); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_SUPERIEUR_OU_EGAL: + { + return (valeur >= events.events[no_event].condition_test.seuil); + break; + } + + //------------------------------------- + case EVENTS_TYPE_TEST_STRICT_SUPERIEUR: + { + return (valeur > events.events[no_event].condition_test.seuil); + break; + } + + //------------------------------------- + default: + case EVENTS_TYPE_TEST_NON_DEFINI : + { + return (false); + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Traite le déclenchement d'un event +// Reçoit : Le numéro de l'évènement +// Retourne : 0 si Ok, -1 si erreur +//********************************************************************************************************************************************************************************** +int8_t declenchement_event(uint16_t no_event) +{ +uint16_t i; +uint16_t no_circuit; +uint8_t consigne; +char chaine[20]; + + // Parcours de toutes les 'action_circuits' configurées sur cet event : + for (i = 0; i < events.events[no_event].action_circuits.nb; i ++) + { + if (!events.events[no_event].action_circuits.action[i].flag_validite) continue; // On ne traite que les actions valides + + no_circuit = events.events[no_event].action_circuits.action[i].no_circuit; // Facilite la lecture... + + if (circuits.circuits[no_circuit].validation_cdes_events == CIRCUIT_VALIDATION_COMMANDES_EVENTS_OFF) continue; // Si le circuit concerné a son flag 'Validite_cdes_events' à FALSE, on ne fait aucune action dessus. + + // Calcul de la nouvelle consigne : + if (events.events[no_event].action_circuits.action[i].action == EVENTS_TYPE_ACTION_CIRCUIT_TOGGLE) + { + if (circuits.circuits[no_circuit].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante != 0) consigne = 0; + else consigne = MAX_CONSIGNE_CIRCUIT; + } + else consigne = events.events[no_event].action_circuits.action[i].action; + + // Application de la nouvelle consigne : + changement_consigne_circuit(no_circuit, consigne, CIRCUITS_ORIGINE_CONSIGNE_EVENT, events.events[no_event].cle_primaire); + +#ifdef __DEBUG_EVENTS + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + sprintf(tmp, "Event #%i : Consigne = %i appliquée sur circuit n° %i (clé_primaire=%i)", no_event, consigne, no_circuit, circuits.circuits[no_circuit].cle_primaire); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); +#endif + + // Enregistrement historique du changement de consigne du circuit : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_CIRCUITS, CIRCUIT_ACTION_EVENT, circuits.circuits[no_circuit].cle_primaire, events.events[no_event].cle_primaire, NULL); + } + + // Enregistrement historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_EVENTS, EVENT_DECLENCHEMENT, events.events[no_event].cle_primaire, 0, NULL); + + // Alarme message : + if (events.events[no_event].alarme_message > 0) + { + sprintf(chaine, "%i", events.events[no_event].cle_primaire); // Paramètres = 'id_event' + enregistrement_message_users(MESSAGE_USERS_CODE_DECLENCHEMENT_EVENT, chaine, MESSAGE_USERS_ETAT_ALERTE); + } + + // Alarme son : + if (events.events[no_event].alarme_son > 0) + { + emission_bip("button-35.mp3", 5, 15, 20); + } + + // Alarme LED : + if (events.events[no_event].alarme_led > 0) + { + c_bus_down_commande_cde_carte(0, C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME, C_BUS_COMMANDE_CDE_CARTE_ETAT_SYSTEME_ALARME); + } + + return(0); +} + + diff --git a/sources/events.h b/sources/events.h new file mode 100644 index 0000000..48e5564 --- /dev/null +++ b/sources/events.h @@ -0,0 +1,72 @@ +//********************************************************************************************************************************************************************************** +// Fichier EVENTS.H +// +// Contient tous les define liés aux évènements +// +//**********************************************************************************************************************************************************************************/ + + // Maximums : +#define MAX_NOMBRE_EVENTS 200 // Cf équivalent dans 'main.qml'. +#define MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT 15 // Cf équivalent dans 'main.qml'. +#define MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT 50 // Cf équivalent dans 'main.qml'. +#define MAX_REPETITIONS_SEQUENCEUR_EVENT 3 // Nombre maxi de répétitions du séquenceur d'un même evenènement. +#define MAX_HEURES_SPECIALES 9 // Selon liste ci-dessous + + + // Tempos : +#define EVENTS_TEMPO_INIT 500 // En x 10ms +#define EVENTS_PERIODE_CHECK 10 // En x 10ms + +#define EVENTS_DIVISEUR_CONDITIONS_HORAIRES 10 // Multiplie EVENTS_PERIODE_CHECK pour obtenir la période de traitement des conditions horaires. + + + // Heures spéciales : +#define HEURE_SPECIALE_OFFSET 24 // Les valeurs des heures spéciales telles que stockées dans des condtions horaires sont égales à [HEURE_SPECIALE_OFFSET + HEURE_SPECIALE_xxx] pour les distinguer des heures normales, de 00 à 23. + +#define HEURE_SPECIALE_FIN_NUIT 0 // -18° de l'horizon +#define HEURE_SPECIALE_AUBE_NAUTIQUE 1 // -12° de l'horizon +#define HEURE_SPECIALE_AUBE 2 // -6° de l'horizon +#define HEURE_SPECIALE_LEVER_SOLEIL 3 // -0.833° de l'horizon +#define HEURE_SPECIALE_ZENITH 4 +#define HEURE_SPECIALE_COUCHER_SOLEIL 5 // -0.833° de l'horizon +#define HEURE_SPECIALE_CREPUSCULE 6 // -6° de l'horizon +#define HEURE_SPECIALE_CREPUSCULE_NAUTIQUE 7 // -12° de l'horizon +#define HEURE_SPECIALE_DEBUT_NUIT 8 // -18° de l'horizon +// Attention : si ajout d'heures spéciales, penser à modifier MAX_HEURES_SPECIALES en début de ce fichier... +// NB : la valeur d'offset n'entre pas en compte dans ce MAX. + + // Jours de la semaine : +#define JOUR_SEMAINE_LUNDI 1 +#define JOUR_SEMAINE_MARDI 2 +#define JOUR_SEMAINE_MERCREDI 3 +#define JOUR_SEMAINE_JEUDI 4 +#define JOUR_SEMAINE_VENDREDI 5 +#define JOUR_SEMAINE_SAMEDI 6 +#define JOUR_SEMAINE_DIMANCHE 7 +#define JOUR_SEMAINE_TOUS_LES_JOURS 8 + + // Types de tests : +#define EVENTS_TYPE_TEST_NON_DEFINI 0 +#define EVENTS_TYPE_TEST_STRICT_INFERIEUR 1 +#define EVENTS_TYPE_TEST_INFERIEUR_OU_EGAL 2 +#define EVENTS_TYPE_TEST_EGAL 3 +#define EVENTS_TYPE_TEST_SUPERIEUR_OU_EGAL 4 +#define EVENTS_TYPE_TEST_STRICT_SUPERIEUR 5 + + // Types d'action sur circuit : +#define EVENTS_TYPE_ACTION_CIRCUIT_TOGGLE -1 +// Les autres actions sont l'application d'une consigne, entre 0 (Off) et MAX_CONSIGNE_CIRCUIT (100) + + // Etats du séquenceur Event : +#define ETAT_SEQUENCEUR_EVENT_INIT 0 +#define ETAT_SEQUENCEUR_EVENT_REPOS 1 +#define ETAT_SEQUENCEUR_EVENT_LANCEMENT_TEMPO 2 +#define ETAT_SEQUENCEUR_EVENT_ATTENTE_FIN_TEMPO 3 +#define ETAT_SEQUENCEUR_EVENT_DECLENCHEMENT 4 +#define ETAT_SEQUENCEUR_EVENT_ATTENTE_DESACTIVATION 5 + + // Etats du séquenceur Events : +#define ETAT_SEQUENCEUR_EVENTS_INIT 0 +#define ETAT_SEQUENCEUR_EVENTS_REPOS 1 + + diff --git a/sources/fonctions.c b/sources/fonctions.c new file mode 100644 index 0000000..31ff065 --- /dev/null +++ b/sources/fonctions.c @@ -0,0 +1,418 @@ +//********************************************************************************************************************************************************************************** +// 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); +} + + + + + + + + + + diff --git a/sources/fonctions.h b/sources/fonctions.h new file mode 100644 index 0000000..1e4dbb3 --- /dev/null +++ b/sources/fonctions.h @@ -0,0 +1,33 @@ +//********************************************************************************************************************************************************************************** +// Fichier FONCTIONS.H +// +// Contient tous les define liés aux fontions diverses... +//**********************************************************************************************************************************************************************************/ + + // Maximums : +#define MAX_LONGUEUR_DATE_COURANTE 60 + + // Timings : + + + // Formats de date (utilisé par la fonction DATE_COURANTE) : +#define DATE_COURANTE_SANS_MS 0 // La chaine constituée s'arrête à la seconde +#define DATE_COURANTE_AVEC_MS 1 // La date constituée comprend, après les secondes, le nombre de ns (9 chiffres) + +#define FORMAT_DATE_COURANTE_USER 0 // Mise en forme de façon lisible pour l'utilisateur (15/05/2018 14:12:23) +#define FORMAT_DATE_COURANTE_MYSQL 1 // Mise ne forme pour être compris par les requêtes MySql (2018-05-15 14:12:23) +#define FORMAT_DATE_COURANTE_SYSTEME 2 // Mise en forme pour être lue par un OS et par ordre chronologique (2018-05-15_14:12:23) +#define FORMAT_DATE_COURANTE_DATE_SEULE 3 // Uniquement la date sous le format JJ/MM/AAAA +#define FORMAT_DATE_COURANTE_HEURE_SEULE 4 // Uniquement l'heure sous le format hh:mm:ss +#define FORMAT_DATE_COURANTE_FICHIER 5 // Mise en forme pour marquer un nom de fichier (2021-01-08_14-12-23) +#define FORMAT_DATE_COURANTE_MODEM 6 // Mise en forme correspondant au format attendu par le modem : "yy/MM/dd,hh:mm:ss±zz", where characters indicate year (two last digits),month, day, hour, minutes, seconds and time zone (indicates the difference, expressed in quarters of an hour, between the local time and GMT; range -47...+48). E.g. 6th of May 2010, 00:01:52 GMT+2 hours equals to "10/05/06,00:01:52+08". +#define FORMAT_DATE_COURANTE_COMPRESSEE 7 // Date et heure sous la forme "YYMMJJ_hhmmss" + + + // Constantes de conversion : +#define CONVERSION_MS_TO_KNOTS 1.9438444924406047516198704103672L // = 3600 / 1852 +#define CONVERSION_RAD_TO_DEG 57.295779513082320876798154814105L // = 360 / (2xPI) = 180 / PI + +// Macros : +#define carre(a) (a)*(a) +#define cube(a) (a)*(a)*(a) \ No newline at end of file diff --git a/sources/g_bus.c b/sources/g_bus.c new file mode 100644 index 0000000..40db5f8 --- /dev/null +++ b/sources/g_bus.c @@ -0,0 +1,461 @@ +//********************************************************************************************************************************************************************************** +// Fichier G_BUS.C +// +// Contient toutes les fonctions liées à la gestion du G_Bus (liaison avec le récepteur GNSS ZOE-M8Q) +//**********************************************************************************************************************************************************************************/ + +#define __DEBUG_G_BUS // Commenter cette ligne : aucun message de debug de G_bus + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_g_bus(void); +void gestion_g_bus(void); +int8_t gestion_protocole_rx_g_bus(void); +void gestion_protocole_tx_g_bus(void); +int8_t g_bus_next_write_buffer_cdes(uint8_t priorite); + +// Variables globales : +struct Struct_g_bus g_bus; + + +/********************************************************************************************************************************************************************************** + Fonction INIT_G_BUS + + Fonction d'initialisation du C-Bus +**********************************************************************************************************************************************************************************/ +int8_t init_g_bus(void) +{ + + if ((systeme.generation_shipbase == GENERATION_SHIPBASE_V2) || (systeme.generation_shipbase == GENERATION_SHIPBASE_V3)) + { + //------------------------------------------------------------------------------------------------------ + // Init des variables générales : + + g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_INIT; + g_bus.sequenceur.compteur = G_BUS_TEMPO_INIT; + + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_INIT; + g_bus.protocole_rx.sequenceur.compteur = G_BUS_PROTOCOLE_RX_TEMPO_INIT; + + g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_INIT; + g_bus.protocole_tx.sequenceur.compteur = G_BUS_PROTOCOLE_TX_TEMPO_INIT; + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du G-BUS\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + } + else + { + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du G-BUS\t\t: Devalidé car non valide sur cette version de ShipBase", TYPE_MESSAGE_DEBUG_WARNING); + } + + return(0); +} + + + +/********************************************************************************************************************************************************************************** + Fonction GESTION_G_BUS + + Fonction de plus haut niveau de gestion du C-Bus +**********************************************************************************************************************************************************************************/ +void gestion_g_bus(void) +{ +int8_t ret; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (systeme.generation_shipbase != GENERATION_SHIPBASE_V2) return; + + switch(g_bus.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_G_BUS_INIT : + { + if (g_bus.sequenceur.compteur == 0) + { + //......................................................... + // Vidage du buffer de réception de tous les caractères reçus pendant la phase d'init : + serial0.ptr_debut_buffer_rx = 0; + serial0.ptr_fin_buffer_rx = 0; + + //......................................................... + // Vidage du buffer de commandes (utile dans le cas d'un reset du modem pour overflow) : + g_bus.down.ptr_debut_buffer_cdes = 0; + g_bus.down.ptr_fin_buffer_cdes = 0; + + //......................................................... + // Mise à jour des séquenceurs pour démarrage des envois : + g_bus.protocole_rx.sequenceur.compteur = 0; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS; + g_bus.protocole_tx.sequenceur.compteur = 0; + g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS; + + g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_G_BUS_REPOS : + { + + //================================= + // Gestion du G-Bus en émission : + + gestion_protocole_tx_g_bus(); // Vérifie si une commande (Down) est en attente. Si oui, elle la traite et l'émet. + + + //================================= + // Gestion du G-Bus en réception : + + ret = gestion_protocole_rx_g_bus(); + if (ret != 0) // Vérifie si une réponse (à une commande Down) a été reçue. + { +#ifdef __DEBUG_G_BUS +// sprintf(tmp, "Retour GNSS : Class=%02Xh, ID=%02Xh, Length=%i", g_bus.down.reponse.classe, g_bus.down.reponse.id, g_bus.down.reponse.lg_reponse); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + g_bus_down_analyse_reponse_commande(); + } + + break; + } + + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur M-bus est inconnu [%i]. Il est repositionné au REPOS", g_bus.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + g_bus.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_REPOS; + break; + } + } +} + + +/***************************************************************************************************************************************************************** + Fonction : Appelée en tâche de fond, cette fonction lit le buffer de réception de l'UART 0 et traite les trames reçues en provenance du GNSS. + Le protocole utilisé est le µBX uniquement (il est assez facile d'utiliser le protocole NMEA mais il est dévalidé au démarrage pour éviter d'avoir + les 2 simultanés et le µBX est bien plus complet...) + Reçoit : Rien + Renvoit : 0 si RAS, 1 si une réponse complète a été reçue +******************************************************************************************************************************************************************/ +int8_t gestion_protocole_rx_g_bus(void) +{ +uint8_t c; +int8_t reponse_recue; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + reponse_recue = 0; + + // Traitement de chaque caractère reçu : + while (serial0.ptr_debut_buffer_rx != serial0.ptr_fin_buffer_rx) + { + // Calcul de la prochaine position à lire (avec bouclage en fin de buffer tournant) : + if (++ serial0.ptr_debut_buffer_rx == MAX_TAILLE_RX_BUFFER_UART) serial0.ptr_debut_buffer_rx = 0; + + // Lecture du caractère dans le buffer : + c = serial0.buffer_rx[serial0.ptr_debut_buffer_rx]; // Lecture du caractère à traiter + + // Traitement du caractère reçu : + switch(g_bus.protocole_rx.sequenceur.etat) + { + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_INIT : + { + // NB : Le caractère lu est jeté tant que la phase d'init n'est pas terminée... + // NB 2 : le passage à l'état REPOS se fait par le séquenceur M-Bus, indépendemment de la réception ou non de caractères. + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS : + { + if (c == 0xB5) // Attente de réception du 'µ' de début de trame + { + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_SYNC_2; + } + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_SYNC_2 : + { + if (c == 0x62) // Attente de réception du 'b' de début de trame + { + g_bus.down.reponse.ck_a_attendu = 0; // Init avant de recevoir le premier octet à intégrer au calcul... + g_bus.down.reponse.ck_b_attendu = 0; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CLASS; + } + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CLASS : + { + g_bus.down.reponse.ck_a_attendu += c; // Calcul ... + g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS + g_bus.down.reponse.classe = c; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_ID; + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_ID : + { + g_bus.down.reponse.ck_a_attendu += c; // Calcul ... + g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS + g_bus.down.reponse.id = c; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_1; + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_1 : + { + g_bus.down.reponse.ck_a_attendu += c; // Calcul ... + g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS + g_bus.down.reponse.lg_reponse_attendue = c; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_2; + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_2 : + { + g_bus.down.reponse.ck_a_attendu += c; // Calcul ... + g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS + g_bus.down.reponse.lg_reponse_attendue |= ((uint16_t) c) << 8; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_DATA; + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_DATA : + { + g_bus.down.reponse.ck_a_attendu += c; // Calcul ... + g_bus.down.reponse.ck_b_attendu += g_bus.down.reponse.ck_a_attendu; // ... du CS + g_bus.down.reponse.reponse[g_bus.down.reponse.lg_reponse ++] = c; + if (g_bus.down.reponse.lg_reponse >= g_bus.down.reponse.lg_reponse_attendue) + { + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_A; + } + + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_A : + { + g_bus.down.reponse.ck_a_recu = c; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_B; + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_B : + { + g_bus.down.reponse.ck_b_recu = c; + + if ((g_bus.down.reponse.ck_a_attendu == g_bus.down.reponse.ck_a_recu) && (g_bus.down.reponse.ck_b_attendu == g_bus.down.reponse.ck_b_recu)) + { + reponse_recue = 1; + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS; + } + else + { +#ifdef __DEBUG_G_BUS + sprintf(tmp, "Le CS reçu [%02Xh|%02Xh] ne correspond pas au CS calculé [%02Xh|%02Xh]. Le séquenceur de réception est repositionné au REPOS", g_bus.down.reponse.ck_a_recu, g_bus.down.reponse.ck_b_recu, g_bus.down.reponse.ck_a_attendu, g_bus.down.reponse.ck_b_attendu); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); +#endif + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS; + } + + break; + } + + //------------------------------------------------------------------------------------ + default : + { + sprintf(tmp, "L'état du séquenceur G-Bus.Protocole_Rx est inconnu [%i]. Il est repositionné au REPOS", g_bus.protocole_rx.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + g_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS; + break; + } + } + + if (reponse_recue != 0) break; + + } + + return(reponse_recue); + +} + + +/***************************************************************************************************************************************************************** + Fonction : Appelée en tâche de fond, cette fonction vérifie si une commande est prête à être émise + dans le buffer de commandes Down. Si oui, elle gère via son séquenceur le formattage de la trame à émettre au ormat µBX + Reçoit : Rien + Renvoit : Rien +*****************************************************************************************************************************************************************/ +void gestion_protocole_tx_g_bus(void) +{ +uint16_t i; +uint8_t c; +uint8_t ck_a, ck_b; +uint8_t tx_buffer[G_BUS_MAX_TAILLE_BUFFER_TX]; +uint16_t lg_tx_buffer; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(g_bus.protocole_tx.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_INIT : + { + // NB : le passage à l'état REPOS se fait par le séquenceur G-Bus, indépendemment de la présence de commande à émettre + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS : + { + //.................................. + // Recherche d'une trame à émettre : + + if (g_bus.down.flag_attente_reponse) // Cas où une commande Down a déjà été émise et qu'on est en attente de réponse : + { + if (g_bus.protocole_tx.sequenceur.compteur_time_out == 0) // On vérifie alors si le time-out a expiré : + { + g_bus.down.flag_attente_reponse = FALSE; // RAZ du flag (qui sera éventuellement remis à 1 si la répétition a lieu) + g_bus.protocole_tx.repetitions ++; // Incrémentation du nombre de répétitions + if (g_bus.protocole_tx.repetitions > g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].max_repetitions) // Si le nombre maxi de répétitions a été dépassé, on laisse tomber la commande + { + +// !!! Il serait peut-être intéressant d'informer la couche supérieure que la commande est abandonnée sans avoir obtenu de réponse malgré N répétitions... +// ==>>> Si oui, c'est ici qu'il faut le mettre.... + + g_bus.protocole_tx.repetitions = 0; // RAZ du compteur pour utilisation ultérieure + break; // Sortie du séquenceur + } + } + else break; // Tant qu'on est en attente d'une réponse et que le time-out n'est pas expiré, on ne traite aucun envoi en mode Down + } + else // Sinon, recherche d'une nouvelle commande à émettre en mode Down : + { + if (g_bus.down.ptr_debut_buffer_cdes != g_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission... + { +// ATTENTION : gérer les priorités ...! + + g_bus.down.reponse.etat = G_BUS_REPONSE_ETAT_REPOS; // On prépare le traitement de la réponse à venir. + + if (++ g_bus.down.ptr_debut_buffer_cdes == G_BUS_MAX_TAILLE_BUFFER_CDES) g_bus.down.ptr_debut_buffer_cdes = 0; + +#ifdef __DEBUG_G_BUS +// g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde[g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].lg_cde] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. +// affichage_debug_bus(TYPE_BUS_DEBUG_G_BUS, "Tx", (char *) g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_G_BUS); +#endif + } + else break; // Il n'y a pas de nouvelle commande en attente d'émission + } + + //..................................... + // Préparation de la trame d'émission : + lg_tx_buffer = 0; + ck_a = 0; + ck_b = 0; + + // Ajout des caractères 'µb' en tête : + tx_buffer[lg_tx_buffer ++] = 0xB5; // 'µ' + tx_buffer[lg_tx_buffer ++] = 0x62; // 'b' + + // Ajout de la commande avec calcul du CS : + for (i=0; (i < g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < G_BUS_MAX_TAILLE_BUFFER_TX); i++) + { + c = g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].cde[i]; // Simplifie la lecture et les accès... + ck_a += c; // Calcul du premier élément de CS + ck_b += ck_a; // Calcul du second élément de CS + tx_buffer[lg_tx_buffer ++] = c; // Ajout du caractère dans le buffer d'émission + } + + // Ajout du CS en fin de trame : + tx_buffer[lg_tx_buffer ++] = ck_a; + tx_buffer[lg_tx_buffer ++] = ck_b; + + //............................. + // Envoi au driver d'émission : + write(config_serial0.filestream, tx_buffer, lg_tx_buffer); + + //.......................................................... + // Mise à jour des paramètres après l'émission de la trame : + g_bus.down.flag_attente_reponse = TRUE; + g_bus.protocole_tx.sequenceur.compteur_time_out = g_bus.down.buffer_cdes[g_bus.down.ptr_debut_buffer_cdes].time_out; + + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur M-Bus.Protocole_Tx est inconnu [%i]. Il est repositionné au REPOS", g_bus.protocole_tx.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + g_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS; + break; + } + } +} + + + +/***************************************************************************************************************************************************************** + Fonction : Incrémente le pointeur du buffer de commandes en attente d'émission vers G-Bus + La fonction gère : + - le rebouclage en fin de buffer (buffer circulaire) + - le dépassement (quand la fin rattrape le début) en émettant un message de debug et en choissant : + . soit d'écraser la commande la plus ancienne si elle est de priorité inférieure (strictement) + . soit refuser l'ajout en laissant le pointeur de fin du buffer à sa position et en renvoyant une erreur. + Reçoit : La priorité de la commande à ajouter (permet d'arbitrer dans la cas où le buffer est plein) + Renvoit : 0 si une position disponible a été trouvée, -1 si aucune position n'est disponible. +******************************************************************************************************************************************************************/ +int8_t g_bus_next_write_buffer_cdes(uint8_t priorite) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // !!! La gestion des priorités n'est pas traitée ! A vérifier mais si on écrase le début, celui-ci est probablement en cours d'utilisation... + // Donc ATTENTION !!!! + + + // Incrémentation : + if (++ g_bus.down.ptr_fin_buffer_cdes == G_BUS_MAX_TAILLE_BUFFER_CDES) g_bus.down.ptr_fin_buffer_cdes = 0; + + // Vérification si buffer plein : + if (g_bus.down.ptr_fin_buffer_cdes == g_bus.down.ptr_debut_buffer_cdes) + { + // Décrémentation du pointeur puisqu'on ne va pas accepter la nouvelle commande qui n'a pas une priorité suffisante : + if (g_bus.down.ptr_fin_buffer_cdes == 0) g_bus.down.ptr_fin_buffer_cdes = (G_BUS_MAX_TAILLE_BUFFER_CDES - 1); + else g_bus.down.ptr_fin_buffer_cdes --; + + // Message de débug : + sprintf(tmp, "Buffer de commandes M-Bus plein. La demande de stockage d'une nouvelle commande a été rejetée..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_BUS_BUFFER_COMMANDES_PLEIN, (uint32_t) 'M', 0, NULL); + + return(-1); + } + else return (0); +} + + + diff --git a/sources/g_bus.h b/sources/g_bus.h new file mode 100644 index 0000000..96b12d3 --- /dev/null +++ b/sources/g_bus.h @@ -0,0 +1,57 @@ +/********************************************************************************************************************************************************************************** + + Fichier G_BUS.H + + Contient tous les define liés au G_BUS + +**********************************************************************************************************************************************************************************/ + + // Maximums : +#define G_BUS_MAX_TAILLE_BUFFER_CDES 80 // Taille du buffer de commandes (donc on stocke plusieurs commandes entières !) G-bus en mode Down +#define G_BUS_PROTOCOLE_TX_MAX_REPETITIONS 3 // Nombre de répétitions des commandes émises sur G_Bus +#define G_BUS_MAX_TAILLE_BUFFER_TX 3000 +#define G_BUS_MAX_REFUS_BUFFER_CDES_PLEIN 12 // Au-delà de cette valeur, si un refus supplémentaire est rencontré, le Modem sera réinitialisé + + + // Tempos : +#define G_BUS_TEMPO_INIT 20 // En x 100ms +// #define G_BUS_TEMPO_IMPULSION_ACTIVATION_MODEM 12 // En x 100ms (Tmin = 1s selon datasheet du SIM7070G, page 26) +// #define G_BUS_TEMPO_POST_IMPULSION_ACTIVATION_MODEM 5 // En x 100ms Cette tempo est à relier au MAX_REPETITIONS_TEMPO_POST_ACTIVATION_MODEM ci-dessus. +#define G_BUS_PROTOCOLE_RX_TEMPO_INIT 10 // en x 100ms +#define G_BUS_PROTOCOLE_TX_TEMPO_INIT 10 // en x 100ms +#define G_BUS_TIME_OUT_REPONSE_TX 3 // En x 100ms + + + // Priorité des commandes : +#define PRIORITE_CDE_G_BUS_FAIBLE 0 +#define PRIORITE_CDE_G_BUS_HAUTE 1 + + // Mode de traitement de la commande par le protocole : +#define G_BUS_PROTOCOLE_NORMAL 0 +// #define G_BUS_PROTOCOLE_DATA_BINAIRE 1 // Dans ce cas, aucun CR n'est ajouté en fin de commande + + + // Etats du séquenceur G-Bus : +#define ETAT_SEQUENCEUR_G_BUS_INIT 0 +#define ETAT_SEQUENCEUR_G_BUS_REPOS 1 + + // Etats séquenceur du Protocole_Rx G-bus : +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_INIT 0 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_REPOS 1 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_SYNC_2 2 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CLASS 3 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_ID 4 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_1 5 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_LENGTH_2 6 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_DATA 7 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_A 8 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_RX_ATTENTE_CS_B 9 + + + // Etats séquenceur du Protocole_Tx G-bus : +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_INIT 0 +#define ETAT_SEQUENCEUR_G_BUS_PROTOCOLE_TX_REPOS 1 + + + + diff --git a/sources/g_commandes.c b/sources/g_commandes.c new file mode 100644 index 0000000..340bff6 --- /dev/null +++ b/sources/g_commandes.c @@ -0,0 +1,728 @@ +//********************************************************************************************************************************************************************************** +// Fichier G_COMMANDES.C +// +// Contient toutes les fonctions liées aux commandes envoyées et reçues sur G_BUS +//**********************************************************************************************************************************************************************************/ + + +//#define __DEBUG_G_COMMANDES_GENERALES //Décommenter cette ligne pour avoir les messages de debug concernant les commandes générales du GNSS +//#define __DEBUG_G_COMMANDE_NAV + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t g_bus_down_commande_libre(uint8_t classe, uint8_t id, uint8_t * ptr_cde, uint16_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +int8_t g_bus_down_analyse_reponse_commande(void); + + +// Variables globales : + + + + + +/***************************************************************************************************************************************************************** + Fonction : Commande libre formattée et envoyée vers le gestionnairer d'émission du protocole + Vérifie que le buffer de commandes à émettre sur G-Bus a un emplacement de libre. + Si oui, génère la commande correspondante dans le buffer de commandes et met à jour la longueur de la commande ainsi que + sa priorité. + Reçoit : La classe et l'ID de la commande, un pointeur sur le début de la commande (la chaine n'a PAS à se terminer par NULL), la longueur de la commande, + le mode protocole, la durée du time-out s'il doit être particulier (en x100ms) et le nombre maxi de répétitions (-1 si valeur par défaut). + Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +******************************************************************************************************************************************************************/ +int8_t g_bus_down_commande_libre(uint8_t classe, uint8_t id, uint8_t * ptr_cde, uint16_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions) +{ +uint16_t i; +uint8_t priorite; +struct Struct_commande_g_bus *buffer_cde; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Vérification des paramètres reçus : + if (mode_protocole != G_BUS_PROTOCOLE_NORMAL) return(-1); + + if (lg_cde >= G_BUS_MAX_TAILLE_CDE) return(-1); + + // Préparation de la commande : + priorite = PRIORITE_CDE_G_BUS_FAIBLE; + + if (g_bus_next_write_buffer_cdes(priorite) != -1) // On recherche le prochain slot de commande libre + { + g_bus.nb_refus_buffer = 0; // RAZ du compteur à chaque réussite + + buffer_cde = &g_bus.down.buffer_cdes[g_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + buffer_cde->lg_cde = 0; + + // Chargement de la classe et de l'ID : + buffer_cde->cde[buffer_cde->lg_cde ++] = classe; + buffer_cde->cde[buffer_cde->lg_cde ++] = id; + + // Chargement de la longueur : + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(lg_cde & 0x00FF); // LSB + buffer_cde->cde[buffer_cde->lg_cde ++] = (uint8_t)(lg_cde >> 8); // MSB + + // Chargement de la commande proprement-dite : + for (i = 0; i < lg_cde; i ++) + { + buffer_cde->cde[buffer_cde->lg_cde ++] = *(ptr_cde ++); + } + + // Code de la commande : + buffer_cde->code = (classe << 8) | id; // On obtient un code unique sur 16 bits en concatenant la classe et l'ID (simplifie la recherche directe ensuite) + + // Priorité de la commande : + buffer_cde->priorite = priorite; + + // Type de réponse attendue : + buffer_cde->mode_protocole = mode_protocole; + + // Time-out à utiliser pour la commande : + if (time_out > 0) buffer_cde->time_out = time_out; + else buffer_cde->time_out = G_BUS_TIME_OUT_REPONSE_TX; + + // Nombre max de répétitions : + if (max_repetitions >= 0) buffer_cde->max_repetitions = max_repetitions; + else buffer_cde->max_repetitions = G_BUS_PROTOCOLE_TX_MAX_REPETITIONS; + + return(0); + } + else + { + if (++ g_bus.nb_refus_buffer > G_BUS_MAX_REFUS_BUFFER_CDES_PLEIN) // Si les refus de nouvelle commandes sur M-Bus augmentent, on va réactiver le modem car il y a un sérieux problème... + { + sprintf(tmp, "Le nombre de refus de nouvelle commande dans le buffer de G-Bus est trop important (%i). Le récepteur GNSS va être réactivé", g_bus.nb_refus_buffer); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + // Enregistrement dans l'historique : +// enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_G_BUS_PLEIN, 0, g_bus.nb_refus_buffer, NULL); + +// modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_DEBUT_IMPULSION_RESET; // Déclenche une impulsion >12s de reset sur POWER_KEY + } + return (-1); // Si aucun slot n'est disponible, on laisse tomber la commande... + } +} + + +/***************************************************************************************************************************************************************** + Fonction : Analyse la réponse à la commande et effectue, si besoin, le traitement nécessaire. + Le principe est de d'abord de détecter la présence d'un éventuel code d'erreur dans la réponse et de stocker son code. + Ensuite, on saute à la fonction (en pratique, un case d'un grand switch) correspondant à la commande qui avait été émise et donc qui déclenche + cette réponse. Là, on peut avoir les grandes tâches suivantes : + 1- cas où on vient de recevoir un code d'erreur (y compris OK qui est considéré comme l'un d'eux) : dans ce cas, on décide ce qu'on doit faire + de ce code dans ce contexte. Souvent, il s'agit de ne pas tenir compte d'un OK intempestif (poru cela, on écrase l'erreur par G_BUS_ERREUR_AUCUNE). + 2- On effectue le traitement de la trame reçue, selon la fonction émise et le contexte. + 3- si on considère que le traitement est terminé, on écrase le code d'erreur pour le mettre à G_BUS_ERREUR_OK. Cette phase 3 est inutile pour pas + mal de commandes simples car leur trame de retour est suivie d'une trame OK qui libère le protocole tout seul. + En toute fin de fonction, on traite le code d'erreur subsistant. S'il est G_BUS_ERREUR_AUCUNE, on ne fait rien. S'il est G_BUS_ERREUR_OK on remet + le protocole au repos. + Reçoit : Rien (traite la réponse qui est stockée dans la structure g_bus.down.reponse.xxxx + Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a rencontré une erreur. +******************************************************************************************************************************************************************/ +int8_t g_bus_down_analyse_reponse_commande(void) +{ +uint8_t erreur; +uint16_t i, j; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + erreur = G_COMMANDE_ERREUR_AUCUNE; + + switch(g_bus.down.reponse.classe) + { + //=============================================== + case G_COMMANDE_CLASSE_ACK : + { + switch(g_bus.down.reponse.id) + { + //----------------------------- + case G_COMMANDE_UBX_ACK_NACK : + { + erreur = G_COMMANDE_ERREUR_NACK; + break; + } + + //----------------------------- + case G_COMMANDE_UBX_ACK_ACK : + { + break; + } + + //----------------------------- + default : + { + erreur = G_COMMANDE_ERREUR_ID; + break; + } + } + + break; + } + + //=============================================== + case G_COMMANDE_CLASSE_CFG : + { + switch(g_bus.down.reponse.id) + { + //----------------------------- + case G_COMMANDE_UBX_CFG_PRT : + { + break; + } + + //----------------------------- + case G_COMMANDE_UBX_CFG_PM2 : + { + break; + } + + //----------------------------- + case G_COMMANDE_UBX_CFG_PMS : + { + break; + } + + //----------------------------- + default : + { + erreur = G_COMMANDE_ERREUR_ID; + break; + } + } + + break; + } + + //=============================================== + case G_COMMANDE_CLASSE_MON : + { + switch(g_bus.down.reponse.id) + { + //----------------------------- + case G_COMMANDE_UBX_MON_VER : + { + if (g_bus.down.reponse.lg_reponse < 40) // Il y a normalement au minimum 40 octets en retour... + { + sprintf(tmp, "Retour de commande UBX_MON_VER en erreur : la longueur de la réponse [%i octets] est inférieure au minimum (40 octets)", g_bus.down.reponse.lg_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + i = 0; + sprintf(tmp, "Retour de commande UBX_MON_VER :"); + strcat(tmp, "\n\t"); // SwVersion (30 caractères) + strcat(tmp, (const char * restrict)(g_bus.down.reponse.reponse + i)); + i += 30; + strcat(tmp, "\n\t"); // HwVersion (10 caractères) + strcat(tmp, (const char * restrict)(g_bus.down.reponse.reponse + i)); + i += 10; + + while (i < g_bus.down.reponse.lg_reponse) + { + strcat(tmp, "\n\t"); // Autre groupe (toujours 30 caractères) + strcat(tmp, (const char * restrict)(g_bus.down.reponse.reponse + i)); + i += 30; + } +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + + break; + } + + //----------------------------- + default : + { + erreur = G_COMMANDE_ERREUR_ID; + break; + } + } + + break; + } + + //=============================================== + case G_COMMANDE_CLASSE_NAV : + { + switch(g_bus.down.reponse.id) + { + //----------------------------- + case G_COMMANDE_UBX_NAV_PVT : + //----------------------------- + { + if (g_bus.down.reponse.lg_reponse != 92) // Longueur fixe... + { + sprintf(tmp, "Retour de commande G_COMMANDE_UBX_NAV_PVT en erreur : la longueur de la réponse [%i octets] est différente de la longueur attendue(92 octets)", g_bus.down.reponse.lg_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + i = 0; + + //---------------------------------------- + // GPS time of week of the navigation epoch. : + i +=4; // Saut de iTOW car deprecated + + + struct Struct_date date; + struct Struct_heure heure; + //---------------------------------------- + // Année : + date.annee = g_bus.down.reponse.reponse[i] + ((uint16_t)(g_bus.down.reponse.reponse[i+1]) << 8); + i += 2; + + //---------------------------------------- + // Mois : + date.mois = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Jour : + date.jour = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Heure : + heure.heures = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Minutes : + heure.minutes = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Secondes : + heure.secondes = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Validity flags : + uint8_t validity; + validity = g_bus.down.reponse.reponse[i]; + i += 1; + bool flag_validity_valid_date = ((validity & 0x01) != 0) ? true : false; // 1 = valid UTC Date (see Time Validity section for details) + bool flag_validity_valid_time = ((validity & 0x02) != 0) ? true : false; // 1 = valid UTC time of day (see Time Validity section for details) + bool flag_validity_fully_resolved = ((validity & 0x04) != 0) ? true : false; // 1 = UTC time of day has been fully resolved (no seconds uncertainty). Cannot be used to check if time is completely solved. + bool flag_validity_valid_magnetic_declination = ((validity & 0x08) != 0) ? true : false; // 1 = valid magnetic declination + + //---------------------------------------- + // Time accuracy (ns) : + uint32_t time_accuracy; + time_accuracy = g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24); + i += 4; + + //---------------------------------------- + // Fraction of second (ns) : + int32_t nano; + nano = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + + //---------------------------------------- + // Fix type : + uint8_t fix_type; + fix_type = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Fix type flags : + uint8_t flags; + flags = g_bus.down.reponse.reponse[i]; + i += 1; + bool flag_flags_gnss_fix_ok = ((flags & 0x01) != 0) ? true : false; // 1 = valid fix (i.e within DOP & accuracy masks) + bool flag_flags_differential_corrections_applied = ((flags & 0x02) != 0) ? true : false; // 1 = differential corrections were applied + bool flag_flags_heading_of_vehicle_valid = ((flags & 0x20) != 0) ? true : false; // 1 = heading of vehicle is valid, only set if the receiver is in sensor fusion mode + uint8_t flags_psm_state = (flags >> 2) & 0x07; // Pas de desciptif dans la doc... + uint8_t flags_carrier_phase_range_solution = (flags >> 6) & 0x03; // Carrier phase range solution status: 0: no carrier phase range solution, 1: carrier phase range solution with floating ambiguities, 2: carrier phase range solution with fixed ambiguities + + //---------------------------------------- + // Fix type flags 2 : + uint8_t flags2; + flags2 = g_bus.down.reponse.reponse[i]; + i += 1; + bool flag_flags2_confirmed_available = ((flags2 & 0x20) != 0) ? true : false; // 1 = information about UTC Date and Time of Day validity confirmation is available (see Time Validity section for details) + bool flag_flags2_confirmed_date = ((flags2 & 0x40) != 0) ? true : false; // 1 = UTC Date validity could be confirmed (see Time Validity section for details) + bool flag_flags2_confirmed_time = ((flags2 & 0x80) != 0) ? true : false; // 1 = UTC Time of Day could be confirmed (see Time Validity section for details) + + //---------------------------------------- + // Number of satellites : + uint8_t siv; + siv = g_bus.down.reponse.reponse[i]; + i += 1; + + //---------------------------------------- + // Longitude (deg) : + int32_t tmp_longitude; + float longitude; + tmp_longitude = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + longitude = ((float)tmp_longitude) / 10000000; + + //---------------------------------------- + // Latitude (deg) : + int32_t tmp_latitude; + float latitude; + tmp_latitude = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + latitude = ((float)tmp_latitude) / 10000000; + + //---------------------------------------- + // Height above ellipsoid (mm) : + int32_t tmp_height_ellipsoid; + float height_ellipsoid; + tmp_height_ellipsoid = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + height_ellipsoid = ((float)tmp_height_ellipsoid) / 1000; + + //---------------------------------------- + // Height above mean sea level (mm) : + int32_t tmp_height_sea; + float height_sea; + tmp_height_sea = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + height_sea = ((float)tmp_height_sea) / 1000; + + //---------------------------------------- + // Horizontal accuracy (mm) : + uint32_t tmp_horizontal_accuracy; + float horizontal_accuracy; + tmp_horizontal_accuracy = g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24); + i += 4; + horizontal_accuracy = ((float)tmp_horizontal_accuracy) / 1000; + + //---------------------------------------- + // Vertical accuracy (mm) : + uint32_t tmp_vertical_accuracy; + float vertical_accuracy; + tmp_vertical_accuracy = g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24); + i += 4; + vertical_accuracy = ((float)tmp_vertical_accuracy) / 1000; + + //---------------------------------------- + // NED north velocity (mm/s) : + int32_t tmp_ned_north_velocity; + float ned_north_velocity; + tmp_ned_north_velocity = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; +// ned_north_velocity = (((float)tmp_ned_north_velocity) * 3.600) / 1000; // Conversion de 'mm/s' en 'km/h' + ned_north_velocity = (((float)tmp_ned_north_velocity) * 3.600) / 1852; // Conversion de 'mm/s' en 'Knts = noeuds' + + //---------------------------------------- + // NED east velocity (mm/s) : + int32_t tmp_ned_east_velocity; + float ned_east_velocity; + tmp_ned_east_velocity = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; +// ned_east_velocity = (((float)tmp_ned_east_velocity) * 3.600) / 1000; // Conversion de 'mm/s' en 'km/h' + ned_east_velocity = (((float)tmp_ned_east_velocity) * 3.600) / 1852; // Conversion de 'mm/s' en 'Knts = noeuds' + + + //---------------------------------------- + // NED down velocity (mm/s) : + int32_t tmp_ned_down_velocity; + float ned_down_velocity; + tmp_ned_down_velocity = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; +// ned_down_velocity = (((float)tmp_ned_down_velocity) * 3.600) / 1000; // Conversion de 'mm/s' en 'km/h' + ned_down_velocity = (((float)tmp_ned_down_velocity) * 3.600) / 1852; // Conversion de 'mm/s' en 'Knts = noeuds' + + //---------------------------------------- + // Ground speed (mm/s) : + int32_t tmp_ground_speed; + float ground_speed; + tmp_ground_speed = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; +// ground_speed = (((float)tmp_ground_speed) * 3.600) / 1000; // Conversion de 'mm/s' en 'km/h' + ground_speed = (((float)tmp_ground_speed) * 3.600) / 1852; // Conversion de 'mm/s' en 'Knts = noeuds' + + //---------------------------------------- + // Heading of motion (deg) : + int32_t tmp_heading_motion; + float heading_motion; + tmp_heading_motion = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + heading_motion = ((float)tmp_heading_motion) / 100000; + + //---------------------------------------- + // Speed accuracy estimate (mm/s): + uint32_t tmp_speed_accuracy; + float speed_accuracy; + tmp_speed_accuracy = g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24); + i += 4; +// speed_accuracy = (((float)tmp_speed_accuracy) * 3.600) / 1000; // Conversion de 'mm/s' en 'km/h' + speed_accuracy = (((float)tmp_speed_accuracy) * 3.600) / 1852; // Conversion de 'mm/s' en 'Knts = noeuds' + + //---------------------------------------- + // Heading accuracy estimate (deg): + uint32_t tmp_heading_accuracy; + float heading_accuracy; + tmp_heading_accuracy = g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24); + i += 4; + heading_accuracy = ((float)tmp_heading_accuracy) / 100000; + + //---------------------------------------- + // Position DOP : + uint16_t tmp_position_dop; + float position_dop; + tmp_position_dop = g_bus.down.reponse.reponse[i] + ((uint16_t)(g_bus.down.reponse.reponse[i+1]) << 8); + i += 2; + position_dop = ((float)tmp_position_dop) / 100; + + //---------------------------------------- + // Fix type flags 3 : + uint16_t flags3; + flags3 = g_bus.down.reponse.reponse[i] + ((uint16_t)(g_bus.down.reponse.reponse[i+1]) << 8); + i += 1; + bool flag_flags3_invalid_llh = ((flags3 & 0x01) != 0) ? true : false; // 1 = Invalid lon, lat, height and hMSL + uint8_t flags3_last_correction_age = (flags3 >> 1) & 0x0F; // Age of the most recently received differential correction: 0: Not available, 1: Age between 0 and 1 second, 2: Age between 1 (inclusive) and 2 seconds, 3: Age between 2 (inclusive) and 5 seconds, 4: Age between 5 (inclusive) and 10 seconds, 5: Age between 10 (inclusive) and 15 seconds, 6: Age between 15 (inclusive) and 20 seconds, 7: Age between 20 (inclusive) and 30 seconds, 8: Age between 30 (inclusive) and 45 seconds, 9: Age between 45 (inclusive) and 60 seconds, 10: Age between 60 (inclusive) and 90 seconds, 11: Age between 90 (inclusive) and 120 seconds, >=12: Age greater or equal than 120 seconds + + //---------------------------------------- + // Reserved (U1 x 4) : + i += 4; + + //---------------------------------------- + // Heading of vehicule (deg) : + int32_t tmp_heading_vehicule; + float heading_vehicule; + tmp_heading_vehicule = (int32_t) (g_bus.down.reponse.reponse[i] + ((uint32_t)(g_bus.down.reponse.reponse[i+1]) << 8) + ((uint32_t)(g_bus.down.reponse.reponse[i+2]) << 16) + ((uint32_t)(g_bus.down.reponse.reponse[i+3]) << 24)); + i += 4; + heading_vehicule = ((float)tmp_heading_vehicule) / 100000; + + //---------------------------------------- + // Magnetic declination (deg) : + int16_t tmp_magnetic_declination; + float magnetic_declination; + tmp_magnetic_declination = (int16_t) (g_bus.down.reponse.reponse[i] + ((uint16_t)(g_bus.down.reponse.reponse[i+1]) << 8)); + i += 2; + magnetic_declination = ((float)tmp_magnetic_declination) / 100; + + //---------------------------------------- + // Magnetic declination accuracy : + uint16_t tmp_magnetic_declination_accuracy; + float magnetic_declination_accuracy; + tmp_magnetic_declination_accuracy = g_bus.down.reponse.reponse[i] + ((uint16_t)(g_bus.down.reponse.reponse[i+1]) << 8); + i += 1; + magnetic_declination_accuracy = ((float)tmp_magnetic_declination_accuracy) / 100; + + + //----------------------------------------- + // TRAITEMENT : + //----------------------------------------- + + if (flag_validity_valid_date && flag_validity_valid_time) // Ces deux flags indiquent que l'heure et la date sont valides. + { + if (gnss.sequenceur.compteur_time_out == 0) // On n'utiulise la date et heure valide que périodiquement car inutils sinon. + { + gnss.sequenceur.compteur_time_out = GNSS_PERIODE_SET_DATE_HEURE; // Rechargement de la tempo pour éviter les mises à jour chaque seconde... + set_date_heure(date, heure); // Mise à jour de la date/heure système à partir de l'heure reçue du GNSS (toujours en UTC !) + c_bus_down_commande_config_date_heure(); // Mise à jour de la date/heure du Compagnon dans la foulée (lui aussi, toujours en UTC) +#ifdef __DEBUG_G_COMMANDES_GENERALES + affichage_debug(__FILE__, __LINE__, "Une commande de mise à l'heure a été envoyée au Compagnon", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + } + + + if (flag_flags_gnss_fix_ok) // C'est ce flag qui indique su la position est déterminée ou pas. + { +#ifdef __DEBUG_G_COMMANDES_GENERALES + affichage_debug(__FILE__, __LINE__, "L'aquisition de la position par le GNSS vient d'être faite avec succès", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + gnss.etat = ON; + + // L'utilisation d'une boucle sur toutes les entrées Prédéfinies est plus efficace que plusieurs appels à 'no_entree_predefinie(0, ENTREE_PREDEFINIE_LAT)' puis test de validité de l'entrée puis lecture de la valeur... + for (j = 0; j < MAX_NOMBRE_ENTREES_PREDEFINIES; j ++) + { + if (!entrees.predefinies.entrees[j].flag_validite) continue; + if (entrees.predefinies.entrees[j].module != 0) continue; // On ne cherche que les entrées de ShipBase + + switch(entrees.predefinies.entrees[j].no) + { + case ENTREE_PREDEFINIE_LAT: { entrees.predefinies.entrees[j].valeur = latitude; break; } + case ENTREE_PREDEFINIE_LON: { entrees.predefinies.entrees[j].valeur = longitude; break; } + case ENTREE_PREDEFINIE_ALT: { entrees.predefinies.entrees[j].valeur = height_sea; break; } + case ENTREE_PREDEFINIE_SOG: { entrees.predefinies.entrees[j].valeur = ground_speed; break; } + case ENTREE_PREDEFINIE_COG: { entrees.predefinies.entrees[j].valeur = heading_motion; break; } + case ENTREE_PREDEFINIE_SIV: { entrees.predefinies.entrees[j].valeur = siv; break; } + case ENTREE_PREDEFINIE_HPA: { entrees.predefinies.entrees[j].valeur = horizontal_accuracy; break; } + case ENTREE_PREDEFINIE_VPA: { entrees.predefinies.entrees[j].valeur = vertical_accuracy; break; } + } + } + } + else + { +#ifdef __DEBUG_G_COMMANDES_GENERALES + affichage_debug(__FILE__, __LINE__, "L'aquisition de la position par le GNSS n'est pas considérée comme valide (flag_flags_gnss_fix_ok = FALSE)", TYPE_MESSAGE_DEBUG_ERREUR); +#endif + gnss.etat = OFF; + + // SDIR # 149 : Début + // L'utilisation d'une boucle sur toutes les entrées Prédéfinies est plus efficace que plusieurs appels à 'no_entree_predefinie(0, ENTREE_PREDEFINIE_LAT)' puis test de validité de l'entrée puis lecture de la valeur... + for (j = 0; j < MAX_NOMBRE_ENTREES_PREDEFINIES; j ++) + { + if (!entrees.predefinies.entrees[j].flag_validite) continue; + if (entrees.predefinies.entrees[j].module != 0) continue; // On ne cherche que les entrées de ShipBase + + switch(entrees.predefinies.entrees[j].no) + { +// case ENTREE_PREDEFINIE_LAT: { entrees.predefinies.entrees[j].valeur = latitude; break; } +// case ENTREE_PREDEFINIE_LON: { entrees.predefinies.entrees[j].valeur = longitude; break; } +// case ENTREE_PREDEFINIE_ALT: { entrees.predefinies.entrees[j].valeur = height_sea; break; } + case ENTREE_PREDEFINIE_SOG: { entrees.predefinies.entrees[j].valeur = 0; break; } + case ENTREE_PREDEFINIE_COG: { entrees.predefinies.entrees[j].valeur = 0; break; } + case ENTREE_PREDEFINIE_SIV: { entrees.predefinies.entrees[j].valeur = 0; break; } + case ENTREE_PREDEFINIE_HPA: { entrees.predefinies.entrees[j].valeur = 0; break; } + case ENTREE_PREDEFINIE_VPA: { entrees.predefinies.entrees[j].valeur = 0; break; } + } + } + // SDIR # 149 : Fin + } + + + + + + +// NB : les tabulations ci-dessous correspondent à un affichage oiptimal. Il est conseillé de ne pas y toucher... +sprintf(tmp, "Réponse GNSS :\n \ + Date = %02i/%02i/%i %02i:%02i:%02i \n \ + Validity flags : Valid_date = %s, Valid_time = %s, Fully_resolved = %s, valid_magnetic_declination = %s \n \ + Time accuracy = %i ns \n \ + Fraction of seconds = %i ns \n \ + Fix type = %s \n \ + Fix Status Flags : Gnss_fix_ok = %s, Differential_corrections_applied = %s, Heading_of_vehicle_valid = %s, Psm_state = %i, Carrier_phase_range_solution = %i \n \ + Flags2 : Confirmed_available = %s, Confirmed_date = %s, Confirmed_time = %s \n \ + Nb de satellites en vue = %i \n \ + Longitude = %.8f° \n \ + Latitude = %.8f° \n \ + Height_ellipsoid = %.3f m \n \ + Height_sea = %.3f m \n \ + Horizontal_accuracy = %.3f m \n \ + Vertical_accuracy = %.3f m \n \ + Ned_north_velocity = %.3f knts \n \ + Ned_east_velocity = %.3f knts \n \ + Ned_down_velocity = %.3f knts \n \ + Ground_speed = %.3f knts \n \ + Heading_motion = %.5f° \n \ + Speed_accuracy = %.3f knts \n \ + Heading_accuracy = %.5f° \n \ + Position_DOP = %.2f \n \ + Flags3 : Invalid_llh = %s, flags3_last_correction_age = %i \n \ + Heading_vehicule = %.5f \n \ + Magnetic_declination = %.2f° \n \ + Magnetic_declination_accur. = %.2f ", + date.jour, date.mois, date.annee, heure.heures, heure.minutes, heure.secondes, + flag_validity_valid_date ? "true" : "false", flag_validity_valid_time ? "true" : "false", flag_validity_fully_resolved ? "true" : "false", flag_validity_valid_magnetic_declination ? "true" : "false", + time_accuracy, + nano, + (fix_type == 0) ? "No fix" : ((fix_type == 1) ? "Dead Reckoning only" : ((fix_type == 2) ? "2D-Fix" : ((fix_type == 3) ? "3D-Fix" : ((fix_type == 4) ? "GNSS + Dead Reckoning combined" : "Time only fix")))), + flag_flags_gnss_fix_ok ? "true" : "false", flag_flags_differential_corrections_applied ? "true" : "false", flag_flags_heading_of_vehicle_valid ? "true" : "false", flags_psm_state, flags_carrier_phase_range_solution, + flag_flags2_confirmed_available ? "true" : "false", flag_flags2_confirmed_date ? "true" : "false", flag_flags2_confirmed_time ? "true" : "false", + siv, + longitude, + latitude, + height_ellipsoid, + height_sea, + horizontal_accuracy, + vertical_accuracy, + ned_north_velocity, + ned_east_velocity, + ned_down_velocity, + ground_speed, + heading_motion, + speed_accuracy, + heading_accuracy, + position_dop, + flag_flags3_invalid_llh ? "true" : "false", flags3_last_correction_age, + heading_vehicule, + magnetic_declination, + magnetic_declination_accuracy + ); +#ifdef __DEBUG_G_COMMANDE_NAV // NB : on laisse faire le sprintf avant dans tous les cas pour éviter les warnings à la compilation... +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + } + + break; + } + + //----------------------------- + default : + { + erreur = G_COMMANDE_ERREUR_ID; + break; + } + } + + break; + } + + //=============================================== + // Autres classes : + case G_COMMANDE_CLASSE_RXM : + case G_COMMANDE_CLASSE_INF : + case G_COMMANDE_CLASSE_UPD : + case G_COMMANDE_CLASSE_AID : + case G_COMMANDE_CLASSE_TIM : + case G_COMMANDE_CLASSE_ESF : + case G_COMMANDE_CLASSE_MGA : + case G_COMMANDE_CLASSE_LOG : + case G_COMMANDE_CLASSE_SEC : + case G_COMMANDE_CLASSE_HNR : + default: + { + erreur = G_COMMANDE_ERREUR_CLASSE; + break; + } + } + + switch(erreur) + { + //--------------------------------- + case G_COMMANDE_ERREUR_AUCUNE : + { + g_bus.down.flag_attente_reponse = FALSE; // Libération du séquenceur d'émission pour passer à la commande suivante + g_bus.protocole_tx.repetitions = 0; // RAZ du nombre de répétitions pour usage futur + g_bus.protocole_tx.sequenceur.compteur_time_out = 0; // RAZ du time-out pour éviter tout déclenchement a postériori. + break; + } + + //--------------------------------- + case G_COMMANDE_ERREUR_CLASSE : + { + sprintf(tmp, "La réponse reçue sur G-Bus ne correspond à aucune classe connue [%02Xh]", g_bus.down.reponse.classe); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + break; + } + + //--------------------------------- + case G_COMMANDE_ERREUR_ID : + { + sprintf(tmp, "La réponse reçue sur G-Bus dans la classe %02Xh ne correspond à aucun ID connu [%02Xh]", g_bus.down.reponse.classe, g_bus.down.reponse.id); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + break; + } + + //--------------------------------- + case G_COMMANDE_ERREUR_NACK : + { + sprintf(tmp, "La réponse reçue sur G-Bus dans la classe [%02Xh] avec ID [%02Xh] est en erreur NACK", g_bus.down.reponse.classe, g_bus.down.reponse.id); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + break; + } + + } + + + + + g_bus.down.reponse.classe = 0; // Cette valeur semble n'être utilisée par aucune classe... + g_bus.down.reponse.lg_reponse = 0; // Repositionne le pointeur au début pour le traitement de la trame suivante. A faire dans tous les cas !! + + + return(0); +} + + + diff --git a/sources/g_commandes.h b/sources/g_commandes.h new file mode 100644 index 0000000..f5d2502 --- /dev/null +++ b/sources/g_commandes.h @@ -0,0 +1,68 @@ +//********************************************************************************************************************************************************************************** +// Fichier G_COMMANDES_.H +// +// Contient toutes définitions de constantes liées au commandes sur G_Bus +//**********************************************************************************************************************************************************************************/ + + + // Valeur maximales +#define G_BUS_MAX_TAILLE_CDE 300 +#define G_BUS_MAX_TAILLE_REPONSE 300 +#define G_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE 20 +#define G_COMMANDE_MAX_LONGUEUR_PARAMETRE_REPONSE 100 + + // Timings : +#define G_BUS_TIME_OUT_COMMANDE_UBX_NAV_PVT 15 // EN x 100ms // Cette valeur override la valeur par défaut du protocole pour cette coimmande en particulier (le GNSS faisant une acquisition toutes les secondes, le temps de réponse est, au pire, d'une seconde). + + + // Classes commandes : +#define G_COMMANDE_CLASSE_NAV 0x01 +#define G_COMMANDE_UBX_NAV_PVT 0x07 + +#define G_COMMANDE_CLASSE_RXM 0x02 + +#define G_COMMANDE_CLASSE_INF 0x04 + +#define G_COMMANDE_CLASSE_ACK 0x05 +#define G_COMMANDE_UBX_ACK_NACK 0x00 +#define G_COMMANDE_UBX_ACK_ACK 0x01 + +#define G_COMMANDE_CLASSE_CFG 0x06 +#define G_COMMANDE_UBX_CFG_PRT 0x00 +#define G_COMMANDE_UBX_CFG_PM2 0x3B +#define G_COMMANDE_UBX_CFG_PMS 0x86 + +#define G_COMMANDE_CLASSE_UPD 0x09 + +#define G_COMMANDE_CLASSE_MON 0x0A +#define G_COMMANDE_UBX_MON_VER 0x04 + +#define G_COMMANDE_CLASSE_AID 0x0B + +#define G_COMMANDE_CLASSE_TIM 0x0D + +#define G_COMMANDE_CLASSE_ESF 0x10 + +#define G_COMMANDE_CLASSE_MGA 0x13 + +#define G_COMMANDE_CLASSE_LOG 0x21 + +#define G_COMMANDE_CLASSE_SEC 0x27 + +#define G_COMMANDE_CLASSE_HNR 0x28 + + + + + // Codes d'erreur : +#define G_COMMANDE_ERREUR_AUCUNE 0 +#define G_COMMANDE_ERREUR_CLASSE 1 +#define G_COMMANDE_ERREUR_ID 2 +#define G_COMMANDE_ERREUR_NACK 3 + + + + + // Etat de décodage des réponses +#define G_BUS_REPONSE_ETAT_REPOS 0 +#define G_BUS_REPONSE_ETAT_ATTENTE_PDU 1 \ No newline at end of file diff --git a/sources/gnss.c b/sources/gnss.c new file mode 100644 index 0000000..9e3ff95 --- /dev/null +++ b/sources/gnss.c @@ -0,0 +1,225 @@ +//********************************************************************************************************************************************************************************** +// Fichier GNSS.C +// +// Contient toutes les fonctions liées à la gestion du GNSS sur ShiBase V2 (GNSS indépedant du gnss de ShipBaseV1) +//**********************************************************************************************************************************************************************************/ + +#define __DEBUG_GNSS //Décommenter cette ligne pour avoir les messages de debug du GNSS + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_gnss(void); +void gestion_gnss(void); + +// Variables globales : +struct Struct_gnss gnss; + + +/********************************************************************************************************************************************************************************** + Fonction INIT_GNSS + + Fonction d'initialisation du GNSS +**********************************************************************************************************************************************************************************/ +int8_t init_gnss(void) +{ + + if ((systeme.generation_shipbase == GENERATION_SHIPBASE_V2) || (systeme.generation_shipbase == GENERATION_SHIPBASE_V3)) + { + gnss.etat = OFF; // Par défaut. + + // 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 + + gnss.sequenceur.etat = ETAT_SEQUENCEUR_GNSS_INIT; + gnss.sequenceur.compteur = GNSS_TEMPO_INIT; + gnss.sequenceur.compteur_time_out = 0; // Permet de forcer une mise à l'heure dès la première réception d'une date/heure valide + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du GNSS\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + } + else + { + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du GNSS\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 Gnss ZOE-M8Q dans son ensemble. Il s'appuie sur les fonctions G-Bus et G_commandes pour y accéder. +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void gestion_gnss(void) +{ +uint16_t i; +uint8_t cde[G_BUS_MAX_TAILLE_CDE]; +uint16_t lg_cde; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (systeme.generation_shipbase != GENERATION_SHIPBASE_V2) return; + + switch(gnss.sequenceur.etat) + { + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_GNSS_INIT : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + if (gnss.sequenceur.compteur == 0) + { + // Activation de la sortie DO_GNSS_EXTINT : + // NB : l'usage de cette sortie n'est pas évident lors des essais d'avril 2023... + ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM4_DO_GNSS_EXTINT, ON); // 0 est l'adresse de ShipBase + + // Au démarrage, le GNSS émet en NMEA toutes les secondes. La priorité est de stopper ces émissions afin de ne laisser accessible sur l'UART que le protocole µBX. + //---------------------------------------------- + // Commande de suppression des émissions NMEA : + //---------------------------------------------- + // Cf Page 243 : 32.10.25.2 Port configuration for UART ports + lg_cde = 0; + + cde[lg_cde ++] = 1; // 0=DDC (I2C), 1=UART, 3=USB, 4=SPI + cde[lg_cde ++] = 0; // Reserved + cde[lg_cde ++] = 0; // TxReady LSB b0=Enable, etc... + cde[lg_cde ++] = 0; // TxReady MSB + cde[lg_cde ++] = 0b11000000; // Mode LLSB b6 & b7 = baud rate (0b11=8 bits) + cde[lg_cde ++] = 0b00001000; // Mode LSB b1, b2 & b3 = parity (0b10x=No_parity), b4 & b5 = stop bit (0b00=1_stop_bit) + cde[lg_cde ++] = 0; // Mode MSB reserved + cde[lg_cde ++] = 0; // Mode MMSB reserved + cde[lg_cde ++] = 0x80; // Baud rate LLSB 9600b/s = 0x0000 2580 + cde[lg_cde ++] = 0x25; // Baud rate LSB + cde[lg_cde ++] = 0x00; // Baud rate MSB + cde[lg_cde ++] = 0x00; // Baud rate MMSB + cde[lg_cde ++] = 0b00000001; // Masque protocoles In LSB b0 = UBX, b1=NMEA + cde[lg_cde ++] = 0; // Masque protocoles In MSB reserved + //cde[lg_cde ++] = 0b00000011; // Masque protocoles Out LSB b0 = UBX, b1=NMEA + cde[lg_cde ++] = 0b00000001; // Masque protocoles Out LSB b0 = UBX, b1=NMEA + cde[lg_cde ++] = 0; // Masque protocoles Out MSB reserved + cde[lg_cde ++] = 0; // Flag bits LSB + cde[lg_cde ++] = 0; // Flag bits MSB + cde[lg_cde ++] = 0; // Reserved + cde[lg_cde ++] = 0; // Reserved + + if (g_bus_down_commande_libre(G_COMMANDE_CLASSE_CFG, G_COMMANDE_UBX_CFG_PRT, cde, lg_cde, M_BUS_PROTOCOLE_NORMAL, 0, -1) == 0) affichage_debug(__FILE__, __LINE__, "L'UART du GNSS est passé en mode µBX uniquement", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + else affichage_debug(__FILE__, __LINE__, "Le passage de l'UART du GNSS en mode µBX uniquement a échoué", TYPE_MESSAGE_DEBUG_ERREUR); + + + + //---------------------------------------------- + // Commande lecture de la configuration du GNSS : + //---------------------------------------------- + lg_cde = 0; + + if (g_bus_down_commande_libre(G_COMMANDE_CLASSE_MON, G_COMMANDE_UBX_MON_VER, cde, lg_cde, M_BUS_PROTOCOLE_NORMAL, 0, -1) == 0) affichage_debug(__FILE__, __LINE__, "La commande de lecture de la version du GNSS a été effectuée avec succés", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + else affichage_debug(__FILE__, __LINE__, "La commande de lecture de la version du GNSS a échoué", TYPE_MESSAGE_DEBUG_ERREUR); + + +/* //---------------------------------------------- + // Commande de configuration étendue du power management : + //---------------------------------------------- + // Cf Page 235 : 32.10.23.1 Extended power management configuration + lg_cde = 0; + + cde[lg_cde ++] = 0x01; // Message version (0x01 for this version) + cde[lg_cde ++] = 0; // Reserved + cde[lg_cde ++] = 0; // Maximum time to spend in Acquisition state. If 0: bound disabled (see maxStartupStateDur) + cde[lg_cde ++] = 0; // Reserved + + cde[lg_cde ++] = 0b01100000; // PSM config flag LLSB b4 = ExtintSel : 0 (Extint0) ou 1 (Extint1), b5 = ExtintWake : 0 (disabled) ou 1 (enabled), b6 = ExtintBackup : 0 (disabled) ou 1 (enabled) + cde[lg_cde ++] = 0b00000000; // PSM config flag LSB b0 & b1 = LimitPeakCurrent : 00 (disabled) ou 01 (enabled) ou 10/11 (reserved), b2 = WaitTimeFix : 0 (wait for normal fix OK before starting on time) ou (1) wait for time fix OK before starting on time, b3 = UpdateRTC : 0 (do not wake up to update RTC. RTC is updated during normal on-time) ou 1 (update RTC. The receiver adds extra wake-up cycles to update the RTC), b4 = UpdateEPH = b4 : 0 (do not wake up to update Ephemeris data) ou 1 (update Ephemeris. The receiver adds extra wake-up cycles to update the Ephemeris data) + cde[lg_cde ++] = 0b00000010; // PSM config flag MSB b0 = DoNotEnterOFff : 0 (receiver enters [Inactive] Awaiting next search state) ou 1 (receiver does not enter [Inactive] Awaiting next search state but keeps trying to acquire a fix instead), b1 & b2 = Mode : 00 ON/OFF operation ou 01 cyclic tracking operation (PSMCT) ou 10/11 reserved + cde[lg_cde ++] = 0; // PSM config flag MMSB reserved + + cde[lg_cde ++] = 0xE8; // Position Update period LLSB (Uint32_t) en ms 1000ms = 0x0000 03E8 + cde[lg_cde ++] = 0x03; // Position Update period LSB (Uint32_t) en ms + cde[lg_cde ++] = 0x00; // Position Update period MSB (Uint32_t) en ms + cde[lg_cde ++] = 0x00; // Position Update period MMSB (Uint32_t) en ms + + cde[lg_cde ++] = 0x64; // Acquiisition retry period LLSB (Uint32_t) en ms 100ms = 0x0000 0064 + cde[lg_cde ++] = 0x00; // Acquiisition retry period LSB (Uint32_t) en ms + cde[lg_cde ++] = 0x00; // Acquiisition retry period MSB (Uint32_t) en ms + cde[lg_cde ++] = 0x00; // Acquiisition retry period MMSB (Uint32_t) en ms + + cde[lg_cde ++] = 0x00; // Grids offset relative to GPS start of week LLSB (Uint32_t) en ms Not used... + cde[lg_cde ++] = 0x00; // Acquiisition retry period LSB (Uint32_t) + cde[lg_cde ++] = 0x00; // Acquiisition retry period MSB (Uint32_t) + cde[lg_cde ++] = 0x00; // Acquiisition retry period MMSB (Uint32_t) + + cde[lg_cde ++] = 0x05; // Ontime - time to stay in TRACKING State LSB (uint16_t) en s + cde[lg_cde ++] = 0x00; // Ontime - time to stay in TRACKING State MSB (uint16_t) en s + + cde[lg_cde ++] = 0x05; // Minimal search time LSB (uint16_t) en s + cde[lg_cde ++] = 0x00; // Minimal search time MSB (uint16_t) + + for (i = 0; i < 20; i ++) cde[lg_cde ++] = 0; // Reserved + + if (g_bus_down_commande_libre(G_COMMANDE_CLASSE_CFG, G_COMMANDE_UBX_CFG_PM2, cde, lg_cde, M_BUS_PROTOCOLE_NORMAL, 0, -1) == 0) affichage_debug(__FILE__, __LINE__, "Le mode de pilotage du GNSS a ét configuré ", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + else affichage_debug(__FILE__, __LINE__, "La configuration du mode de pilotage du GNSS a échoué", TYPE_MESSAGE_DEBUG_ERREUR); +*/ + + //---------------------------------------------- + // Commande de configuration du Power Mode : + //---------------------------------------------- + // Cf Page 242 : 32.10.24.1 Power Mode Setup + lg_cde = 0; + + cde[lg_cde ++] = 0; // Version (0x00 for this version) + cde[lg_cde ++] = 0x03; // Power setup value : 0x00 = Full power, 0x01 = Balanced, 0x02 = Interval, 0x03 = Aggressive with 1 Hz, 0x04 = Aggressive with 2 Hz, 0x05 = Aggressive with 4 Hz, 0xFF = Invalid (only when polling) + + cde[lg_cde ++] = 0x00; // LSB Position update period and search period (en secondes). Recommended minimum period is 10 s, although the receiver accepts any value bigger than 5 s. Only valid when powerSetupValue set to Interval, otherwise must be set to '0'. + cde[lg_cde ++] = 0x00; // MSB ... + + cde[lg_cde ++] = 0x00; // LSB Duration of the ON phase, must be smaller than the period. Only valid when powerSetupValue set to Interval, otherwise must be set to '0'. + cde[lg_cde ++] = 0x00; // MSB ... + + for (i = 0; i < 2; i ++) cde[lg_cde ++] = 0; // Reserved + + if (g_bus_down_commande_libre(G_COMMANDE_CLASSE_CFG, G_COMMANDE_UBX_CFG_PMS, cde, lg_cde, M_BUS_PROTOCOLE_NORMAL, 0, -1) == 0) affichage_debug(__FILE__, __LINE__, "Le mode de pilotage du GNSS a été configuré ", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + else affichage_debug(__FILE__, __LINE__, "La configuration du mode de pilotage du GNSS a échoué", TYPE_MESSAGE_DEBUG_ERREUR); + + + + gnss.sequenceur.compteur = GNSS_TEMPO_ATTENTE_FIN_INIT; + gnss.sequenceur.etat = ETAT_SEQUENCEUR_GNSS_REPOS; + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_GNSS_REPOS : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + if (gnss.sequenceur.compteur == 0) + { + gnss.sequenceur.compteur = GNSS_PERIODE_MESURES; + + //---------------------------------------------- + // Commande de lecture de la position : + //---------------------------------------------- + lg_cde = 0; + + if (g_bus_down_commande_libre(G_COMMANDE_CLASSE_NAV, G_COMMANDE_UBX_NAV_PVT, cde, lg_cde, M_BUS_PROTOCOLE_NORMAL, G_BUS_TIME_OUT_COMMANDE_UBX_NAV_PVT, -1) == 0) ;//affichage_debug(__FILE__, __LINE__, "La commande de lecture de la position GNSS a été effectuée avec succés", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + else affichage_debug(__FILE__, __LINE__, "La commande de lecture de la position GNSS a échoué", TYPE_MESSAGE_DEBUG_ERREUR); + + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + default : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "L'état du séquenceur GNSS est inconnu [%i]. Il est repositionné au REPOS", gnss.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + gnss.sequenceur.etat = ETAT_SEQUENCEUR_GNSS_REPOS; + break; + } + } + +} \ No newline at end of file diff --git a/sources/gnss.h b/sources/gnss.h new file mode 100644 index 0000000..489ea06 --- /dev/null +++ b/sources/gnss.h @@ -0,0 +1,26 @@ +/********************************************************************************************************************************************************************************** + + Fichier GNSS.H + + Contient tous les define liés au GNSS + +**********************************************************************************************************************************************************************************/ + + // Maximums : + + // Tempos : +#define GNSS_TEMPO_INIT 20 // En x 100ms +#define GNSS_TEMPO_ATTENTE_FIN_INIT 30 // En x 100ms Tempo nécessaire pour s'assurer que toutes les commandes de configuration sont bien exécutées avant de lancer les pollings. +#define GNSS_PERIODE_MESURES 11 // En x 100ms Le GNSS effectue une mesure par seconde (1Hz) et ne répond à une commande de lecture qur lorsque une nouvelle acquisition est faite. Il est donc important d'avoir une période légèrement supérieure afin de ne pas remplir le buffer d'émission de G-Bus !! +#define GNSS_PERIODE_SET_DATE_HEURE 600 // EN x 100ms Période à laquelle l'heure GNSS sera prise en compte pour mise à l'heure du CM4 et envoyée au Compagnon + + // Fonctionnement GNSS : +// #define GNSS_MAX_NOMBRE_CYCLES 180 // Nombre de cycles d'acquisition avant d'abandonner. +// #define GNSS_NOMBRE_CYCLES_VALIDES 20 // Nombre de cycles valides (pas forcément consécutifs) sur une même période avant de considérer la période comme terminée. Cette valeur est importante pour avoir une précision suffisante... + + + // Etats du séquenceur Modem : +#define ETAT_SEQUENCEUR_GNSS_INIT 0 +#define ETAT_SEQUENCEUR_GNSS_REPOS 1 + + diff --git a/sources/hardware.c b/sources/hardware.c new file mode 100644 index 0000000..8761cf0 --- /dev/null +++ b/sources/hardware.c @@ -0,0 +1,312 @@ +//********************************************************************************************************************************************************************************** +// Fichier HARDWARE.C +// +// Contient toutes les fonctions liées à l'interface avec le matériel. +// En particulier, permet de s'affranchir de l'utilisation de la bibiliothèque BCM2835... +// +// Les documents utilisés sont essentiellement : +// - http://elinux.org/RPi_Low-level_peripherals très bon article dédié au Raspberry Pi et décrivant bien les différentes versions de RASPI. +// - http://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2835/BCM2835-ARM-Peripherals.pdf est la base de la documentation car la VRAIE doc du processeur... +//**********************************************************************************************************************************************************************************/ + +// Includes : +#include "main.h" + +// Prototypes : +int gestion_timer(void); +void timer(void); +int init_timer(void); +int init_memory_map(void); + +// Variables globales : +struct Struct_hardware hardware; + + + +/********************************************************************************************************************************************************************************** + Fonction TIMER + + Fonction de gestion du signal de timer Linux (programmé pour se déclencher toutes les 10ms) +**********************************************************************************************************************************************************************************/ +void timer(void) +{ +uint16_t i; + + + hardware.dizaines_ms ++; + + //-------------------------------------------------- 10 ms -------------------------------------------------- + // Capteurs.c : + if (capteurs.sequenceur.compteur > 0) capteurs.sequenceur.compteur --; + + // Circuits.c : + if (circuits.sequenceur.compteur > 0) circuits.sequenceur.compteur --; + for (i=0; i 0) circuits.circuits[i].sequenceur.compteur --; + } + + // Ecran.c + if (ecran.sequenceur.compteur > 0) ecran.sequenceur.compteur --; + + // Entrees.c : + if (entrees.core.sequenceur.compteur > 0) entrees.core.sequenceur.compteur --; + if (entrees.gpio.sequenceur.compteur > 0) entrees.gpio.sequenceur.compteur --; + if (entrees.level_sensor.sequenceur.compteur > 0) entrees.level_sensor.sequenceur.compteur --; + + // Events.c : + if (events.sequenceur.compteur > 0) events.sequenceur.compteur --; + + // Gestion_bdd.c : + if (bdd.compteur_synchro > 0) bdd.compteur_synchro --; + + // Systeme.c : + if (systeme.sequenceur.compteur > 0) systeme.sequenceur.compteur --; + + + //-------------------------------------------------- 100 ms -------------------------------------------------- + if ((hardware.dizaines_ms % 10) == 0) + { + // Audio.c + if (audio.sequenceur.compteur > 0) audio.sequenceur.compteur --; + + // Batteries.c : + if (batteries.sequenceur.compteur > 0) batteries.sequenceur.compteur --; + + // C_bus.c : + if (c_bus.sequenceur.compteur > 0) c_bus.sequenceur.compteur --; + if (c_bus.sequenceur.compteur_time_out > 0) c_bus.sequenceur.compteur_time_out --; + if (c_bus.protocole_rx.sequenceur.compteur > 0) c_bus.protocole_rx.sequenceur.compteur --; + if (c_bus.protocole_rx.sequenceur.compteur_time_out > 0) c_bus.protocole_rx.sequenceur.compteur_time_out --; + if (c_bus.protocole_tx.sequenceur.compteur > 0) c_bus.protocole_tx.sequenceur.compteur --; + if (c_bus.protocole_tx.sequenceur.compteur_time_out > 0) c_bus.protocole_tx.sequenceur.compteur_time_out --; + + // Circuits.c : + for (i=0; i 0) circuits.circuits[i].compteur_time_out --; + } + + // Events.c : + for (i=0; i 0) events.events[i].sequenceur.compteur --; + } + + // Fonctions.c : + if (tempo_delai_dixieme_sec > 0) tempo_delai_dixieme_sec --; // Utilisé par la fonction DELAI_DIXIEME_SECONDE. + + // G_bus.c : + if (g_bus.sequenceur.compteur > 0) g_bus.sequenceur.compteur --; + if (g_bus.sequenceur.compteur_time_out > 0) g_bus.sequenceur.compteur_time_out --; + if (g_bus.protocole_rx.sequenceur.compteur > 0) g_bus.protocole_rx.sequenceur.compteur --; + if (g_bus.protocole_rx.sequenceur.compteur_time_out > 0) g_bus.protocole_rx.sequenceur.compteur_time_out --; + if (g_bus.protocole_tx.sequenceur.compteur > 0) g_bus.protocole_tx.sequenceur.compteur --; + if (g_bus.protocole_tx.sequenceur.compteur_time_out > 0) g_bus.protocole_tx.sequenceur.compteur_time_out --; + + // Gnss.c : + if (gnss.sequenceur.compteur > 0) gnss.sequenceur.compteur --; + if (gnss.sequenceur.compteur_time_out > 0) gnss.sequenceur.compteur_time_out --; + + // Gestion_bdd.c : + if (bdd.compteur > 0) bdd.compteur --; + if (bdd.compteur_enregistrement_mesures > 0) bdd.compteur_enregistrement_mesures --; + + // M_bus.c : + if (m_bus.sequenceur.compteur > 0) m_bus.sequenceur.compteur --; + if (m_bus.sequenceur.compteur_time_out > 0) m_bus.sequenceur.compteur_time_out --; + if (m_bus.protocole_rx.sequenceur.compteur > 0) m_bus.protocole_rx.sequenceur.compteur --; + if (m_bus.protocole_rx.sequenceur.compteur_time_out > 0) m_bus.protocole_rx.sequenceur.compteur_time_out --; + if (m_bus.protocole_tx.sequenceur.compteur > 0) m_bus.protocole_tx.sequenceur.compteur --; + if (m_bus.protocole_tx.sequenceur.compteur_time_out > 0) m_bus.protocole_tx.sequenceur.compteur_time_out --; + + // Modem.c : + if (modem.sequenceur.compteur > 0) modem.sequenceur.compteur --; + if (modem.sequenceur.compteur_time_out > 0) modem.sequenceur.compteur_time_out --; + + // Nmea.c : + if (nmea.sequenceur.compteur > 0) nmea.sequenceur.compteur --; + + // Reseaux.c + if (reseaux.sequenceur.compteur > 0) reseaux.sequenceur.compteur --; + if (reseaux.compteur_check_adresses_ip > 0) reseaux.compteur_check_adresses_ip --; + + // Reservoirs.c : + if (reservoirs.sequenceur.compteur > 0) reservoirs.sequenceur.compteur --; + + // Systeme.c : + if (chargement_code.sequenceur.compteur > 0) chargement_code.sequenceur.compteur --; + if (chargement_code.sequenceur.compteur_time_out > 0) chargement_code.sequenceur.compteur_time_out --; + if (systeme.faucheuse.compteur > 0) systeme.faucheuse.compteur --; + if (systeme.sequenceur.compteur_time_out > 0) systeme.sequenceur.compteur_time_out --; + + // Usb.c + if (usb.sequenceur.compteur > 0) usb.sequenceur.compteur --; + } + + //-------------------------------------------------- 1000 ms = 1s -------------------------------------------------- + if (hardware.dizaines_ms >= 100) + { +// affichage_debug(__FILE__, __LINE__, "Timer: 1 seconde", TYPE_MESSAGE_DEBUG_WARNING); + + hardware.dizaines_ms = 0; // RAZ du compteur toutes les 100 boucles (1000 ms) + + // Batteries.c : + for (i=0; i 0) batteries.batteries[i].sequenceur.compteur --; + } + + for (i=0; i 0) systeme.modules_config[i].courant_maxi_compteur --; + } + + // Events.c : + if (events.sequenceur.compteur_time_out > 0) events.sequenceur.compteur_time_out --; + + // Reseaux.c : + if (reseaux.compteur_connexion_ship2net > 0) reseaux.compteur_connexion_ship2net --; + if (reseaux.compteur_scan_sta > 0) reseaux.compteur_scan_sta --; + if (reseaux.compteur_check_webgl > 0) reseaux.compteur_check_webgl --; + + // Systeme.c : + if (systeme.compteur_check_new_download > 0) systeme.compteur_check_new_download --; + if (systeme.compteur_sauvegarde_bdd > 0) systeme.compteur_sauvegarde_bdd --; + } +} + + +/********************************************************************************************************************************************************************************** + Fonction INIT_TIMER + + Initialise le Timer Linux +**********************************************************************************************************************************************************************************/ +int init_timer(void) +{ +struct itimerval valeur_timer; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Init des variables globales : + hardware.dizaines_ms = 0; + + // Mise en place du handler de signal vers la fonction TIMER() : + if (signal(SIGALRM, (void (*)(int)) timer) == SIG_ERR) + { + sprintf(tmp, "Mise en place du handler de timer impossible..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + // Calcul des paramètres de chargement du timer : + if ((BASE_TEMPS_TIMER/1000) > 0) + { + valeur_timer.it_value.tv_sec = BASE_TEMPS_TIMER / 1000; + valeur_timer.it_value.tv_usec = (BASE_TEMPS_TIMER % 1000) * 1000; + } + else + { + valeur_timer.it_value.tv_sec = 0; + valeur_timer.it_value.tv_usec = (BASE_TEMPS_TIMER * 1000); + } + valeur_timer.it_interval = valeur_timer.it_value; + + // Chargement et lancement du timer : + if (setitimer(ITIMER_REAL, &valeur_timer, NULL) == -1) + { + sprintf(tmp, "\tLancement du timer impossible..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + else + { + sprintf(tmp, "Lancement du timer Linux (période = %i ms)\t\t\t\t\t: OK", BASE_TEMPS_TIMER); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + + return (0); + +} + +/********************************************************************************************************************************************************************************** + Fonction INIT_MEMORY_MAP + + Créée une nouvelle projection dans l'espace d'adressage virtuel. Utilise la fonction MMAP de Linux. +**********************************************************************************************************************************************************************************/ +int init_memory_map() +{ +int mem_fd; +off_t offset; +void *ptr_map; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + //------------------------------------ + // Initialisation dépendant de la génération de ShipBase : + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + offset = BCM2837_ADRESSE_BASE_PERIPH + CM3P_OFFSET_GPIO_BASE; + + hardware.offset_gpio_out_set_0 = CM3P_OFFSET_GPIO_OUT_SET_0; + hardware.offset_gpio_out_set_1 = CM3P_OFFSET_GPIO_OUT_SET_1; + hardware.offset_gpio_out_clear_0 = CM3P_OFFSET_GPIO_OUT_CLEAR_0; + hardware.offset_gpio_out_clear_1 = CM3P_OFFSET_GPIO_OUT_CLEAR_1; + hardware.offset_gpio_pin_level_0 = CM3P_OFFSET_GPIO_PIN_LEVEL_0; + hardware.offset_gpio_pin_level_1 = CM3P_OFFSET_GPIO_PIN_LEVEL_1; + } + else + { + offset = BCM2711_ADRESSE_BASE_PERIPH + CM4_OFFSET_GPIO_BASE; + + hardware.offset_gpio_out_set_0 = CM4_OFFSET_GPIO_OUT_SET_0; + hardware.offset_gpio_out_set_1 = CM4_OFFSET_GPIO_OUT_SET_1; + hardware.offset_gpio_out_clear_0 = CM4_OFFSET_GPIO_OUT_CLEAR_0; + hardware.offset_gpio_out_clear_1 = CM4_OFFSET_GPIO_OUT_CLEAR_1; + hardware.offset_gpio_pin_level_0 = CM4_OFFSET_GPIO_PIN_LEVEL_0; + hardware.offset_gpio_pin_level_1 = CM4_OFFSET_GPIO_PIN_LEVEL_1; + } + + // Ouverture du fichier /dev/mem : + if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) + { + sprintf(tmp, "Ouverture de [/dev/mem] impossible..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return (-1); + } + else + { + // sprintf(tmp, "Ouverture de [/dev/mem] \t\t\t\t: OK"); + // affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + + // **************************************************************************************************************** + // mmap du GPIO : + ptr_map = mmap( + NULL, // Any adddress in our space will do (indique l'endroit où est stocké le mapping...) + BLOCK_SIZE, // Map length + PROT_READ|PROT_WRITE, // Enable reading & writting to mapped memory + MAP_SHARED, // Shared with other processes + mem_fd, // File to map + offset // Offset to GPIO peripheral + ); + + if (ptr_map == MAP_FAILED) + { + sprintf(tmp, "Erreur 'mmap' pour création de l'espace GPIO. Erreur : %p", ptr_map); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + else + { + sprintf(tmp, "Initialisation de 'mmap' pour création de l'espace GPIO\t\t\t: OK"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + + // Toujours utiliser un pointeur VOLATILE (permet de limiter l'optimisation du compilateur et de forcer TOUS les accès au matériel) + hardware.gpio_base = (volatile uint32_t *)ptr_map; + + // **************************************************************************************************************** + // Fermeture du fichier /dev/mem : + close(mem_fd); //No need to keep mem_fd open after mmap + + return 0; +} diff --git a/sources/hardware.h b/sources/hardware.h new file mode 100644 index 0000000..1668cb4 --- /dev/null +++ b/sources/hardware.h @@ -0,0 +1 @@ +/********************************************************************************************************************************************************************************** Fichier HARDWARE.H Contient tous les define liés à l'interface avec le matériel. **********************************************************************************************************************************************************************************/ // Nombre max physique : // Timings : #define BASE_TEMPS_TIMER 10 // En ms //********************************************************************************************************************************************************************************** // General : //********************************************************************************************************************************************************************************** #define PAGE_SIZE (4*1024) #define BLOCK_SIZE (4*1024) // CM3+ : #define BCM2837_ADRESSE_BASE_PERIPH 0x3F000000 // Adresse physique de base de la zone I/O PERIPHERALS à partir de RPI 3 (idem RPI 2) // CM4 : #define BCM2711_ADRESSE_BASE_PERIPH 0xFE000000 // Adresse physique de base de la zone I/O PERIPHERALS pour CM4 //********************************************************************************************************************************************************************************** // General Purpose IO (GPIO) : //********************************************************************************************************************************************************************************** // CM3+ : #define CM3P_OFFSET_GPIO_BASE 0x200000 #define CM3P_OFFSET_GPIO_OUT_SET_0 7 // Positionne à HIGH les sorties 0 à 31 (1 bit / sortie) - (cf page 95) #define CM3P_OFFSET_GPIO_OUT_SET_1 8 // Positionne à HIGH les sorties 32 à 53 (1 bit / sortie) - (cf page 95) #define CM3P_OFFSET_GPIO_OUT_CLEAR_0 10 // Positionne à LOW les sorties 0 à 31 (1 bit / sortie) - (cf page 95) #define CM3P_OFFSET_GPIO_OUT_CLEAR_1 11 // Positionne à LOW les sorties 32 à 53 (1 bit / sortie) - (cf page 95 & 96) #define CM3P_OFFSET_GPIO_PIN_LEVEL_0 13 // Lecture des IO 0 à 31 (1 bit / IO) - (cf page 96) #define CM3P_OFFSET_GPIO_PIN_LEVEL_1 14 // Lecture des IO 32 à 53 (1 bit / IO) - (cf page 96) #define CM3P_OFFSET_GPIO_PIN_PULL_UP_DOWN_ENABLE 37 // Config du type de pull-up/pull-down à utiliser lors de l'application de la ENABLE_CLOCK suivante (2 bits au total) - (cf page 101) #define CM3P_OFFSET_GPIO_PIN_PULL_UP_DOWN_ENABLE_CLOCK_0 38 // Config des IO 0 à 31 (1 bit / IO) qui seront soumis au pull-up/pull-down ci-dessus - (cf page 101) #define CM3P_OFFSET_GPIO_PIN_PULL_UP_DOWN_ENABLE_CLOCK_1 39 // Config des IO 32 à 53 (1 bit / IO) qui seront soumis au pull-up/pull-down ci-dessus - (cf page 101) #define CM3P_NO_PUP_PDN 0x00 // 00 = No resistor is selected #define CM3P_PULL_DOWN 0x01 // 01 = Enable Pull Down control #define CM3P_PULL_UP 0x02 // 10 = Enable Pull Up control // CM4 : #define CM4_OFFSET_GPIO_BASE 0x200000 #define CM4_OFFSET_GPIO_OUT_SET_0 7 // Positionne à HIGH les sorties 0 à 31 (1 bit / sortie) - (cf page 95) #define CM4_OFFSET_GPIO_OUT_SET_1 8 // Positionne à HIGH les sorties 32 à 53 (1 bit / sortie) - (cf page 95) #define CM4_OFFSET_GPIO_OUT_CLEAR_0 10 // Positionne à LOW les sorties 0 à 31 (1 bit / sortie) - (cf page 95) #define CM4_OFFSET_GPIO_OUT_CLEAR_1 11 // Positionne à LOW les sorties 32 à 53 (1 bit / sortie) - (cf page 95 & 96) #define CM4_OFFSET_GPIO_PIN_LEVEL_0 13 // Lecture des IO 0 à 31 (1 bit / IO) - (cf page 96) #define CM4_OFFSET_GPIO_PIN_LEVEL_1 14 // Lecture des IO 32 à 53 (1 bit / IO) - (cf page 96) #define CM4_OFFSET_GPIO_PUP_PDN_CNTRL_REG0 57 // The GPIO Pull-up / Pull-down Registers control the actuation of the internal pull-up/down resistors. Reading these registers gives the current pull-state. (cf page 74) #define CM4_OFFSET_GPIO_PUP_PDN_CNTRL_REG1 58 #define CM4_OFFSET_GPIO_PUP_PDN_CNTRL_REG2 59 #define CM4_OFFSET_GPIO_PUP_PDN_CNTRL_REG3 60 #define CM4_NO_PUP_PDN 0x00 // 00 = No resistor is selected #define CM4_PULL_UP 0x01 // 01 = Pull up resistor is selected #define CM4_PULL_DOWN 0x02 // 10 = Pull down resistor is selected // Type d'E/S utilisés dans GPIO_FUNCTION_SEL_x : #define GPIO_TYPE_INPUT 0x00 #define GPIO_TYPE_OUTPUT 0x01 #define GPIO_TYPE_ALT_FUNCTION_0 0x04 #define GPIO_TYPE_ALT_FUNCTION_1 0x05 #define GPIO_TYPE_ALT_FUNCTION_2 0x06 #define GPIO_TYPE_ALT_FUNCTION_3 0x07 #define GPIO_TYPE_ALT_FUNCTION_4 0x03 #define GPIO_TYPE_ALT_FUNCTION_5 0x02 #define GPIO_TYPE_UNKNOWN 0xFF // Type de pull-up/down : #define NO_PUP_PDN 0 #define PULL_DOWN 1 #define PULL_UP 2 \ No newline at end of file diff --git a/sources/localisation.h b/sources/localisation.h new file mode 100644 index 0000000..a110a8b --- /dev/null +++ b/sources/localisation.h @@ -0,0 +1,95 @@ +/********************************************************************************************************************************************************************************** + Fichier LOCALISATION.H + Contient toutes les définitions liées au site (donc tous les autres doivent être indépendants du site...) +**********************************************************************************************************************************************************************************/ + + // Base de données : +#define BDD_SERVER "localhost" +#define BDD_USER "koriolan" +#define BDD_PASSWORD "Mp7Hgf12bV9zm" +#define BDD_DATABASE "shipheart" + + + // Répertoires : +#define REPERTOIRE_SHIPHEART "/home/koriolan/shipheart" // Pas de '/' de fin... +#define REPERTOIRE_VERSIONS_CURRENT "/home/koriolan/shipheart/versions/current" // Pas de '/' de fin... +#define REPERTOIRE_LOGS "/home/koriolan/shipheart/logs" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG "/home/koriolan/shipheart/config" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG_IMAGES_ACCUEIL "/home/koriolan/shipheart/config/images_accueil" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG_IMAGES_SILHOUETTE "/home/koriolan/shipheart/config/images_silhouette" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG_RAPPORTS "/home/koriolan/shipheart/config/rapports" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG_SAUVEGARDES "/home/koriolan/shipheart/config/sauvegardes" // Pas de '/' de fin... +#define REPERTOIRE_CONFIG_SONS "/home/koriolan/shipheart/config/sons" // Pas de '/' de fin... +#define REPERTOIRE_UTILS "/home/koriolan/shipheart/utils" // Pas de '/' de fin... +#define REPERTOIRE_SERVER "/home/koriolan/shipheart/server" // Pas de '/' de fin... +#define REPERTOIRE_SERVER_UPLOAD "/home/koriolan/shipheart/server/upload" // Pas de '/' de fin... +#define REPERTOIRE_SERVER_DOWNLOAD "/home/koriolan/shipheart/server/download" // Pas de '/' de fin... +#define REPERTOIRE_VERSIONS "/home/koriolan/shipheart/versions" // Pas de '/' de fin... +#define REPERTOIRE_VERSIONS_A_VALIDER "/home/koriolan/shipheart/versions/a_valider" // Pas de '/' de fin... +#define REPERTOIRE_VERSIONS_A_CHARGER "/home/koriolan/shipheart/versions/a_charger" // Pas de '/' de fin... +#define REPERTOIRE_TMP "/home/koriolan/shipheart/tmp" // Pas de '/' de fin... +#define REPERTOIRE_TMP_BDD "/home/koriolan/shipheart/tmp/bdd" // Pas de '/' de fin... +#define REPERTOIRE_TMP_CORE "/home/koriolan/shipheart/tmp/core" // Pas de '/' de fin... +#define REPERTOIRE_TMP_SAUVEGARDE "/home/koriolan/shipheart/tmp/sauvegarde" // Pas de '/' de fin... +#define REPERTOIRE_TMP_USB "/home/koriolan/shipheart/tmp/usb" // Pas de '/' de fin... +#define REPERTOIRE_TMP_WIFI "/home/koriolan/shipheart/tmp/wifi" // Pas de '/' de fin... + + + // Fichiers : +#define NOM_FICHIER_DEBUG "/home/koriolan/shipheart/logs/shipheart_appli_log.txt" +#define NOM_FICHIER_DEBUG_TMP "/home/koriolan/shipheart/logs/shipheart_appli_log_tmp.txt" +#define NOM_FICHIER_DEBUG_BUS "/home/koriolan/shipheart/logs/shipheart_appli_bus_log.txt" +#define NOM_FICHIER_APPLICATION "shipheart_appli" +#define NOM_FICHIER_QT "shipheart_qt" +#define NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR "__list_download_serveur.txt" +#define NOM_FICHIER_SAUVEGARDE_BDD "shipheart" +#define NOM_FICHIER_CHECK_SAUVEGARDE "check_tmp" +#define NOM_FICHIER_WPA_SUPPLICANT "/etc/wpa_supplicant/wpa_supplicant.conf" +#define NOM_FICHIER_HOSTAPD "/etc/hostapd/hostapd.conf" +#define NOM_FICHIER_ARCHIVAGE "archivage.php" +#define NOM_FICHIER_CONNEXION_SHIP2NET "connexion_ship2net.php" +#define NOM_FICHIER_CONNEXION_SHIP2NET_LOG "connexion_ship2net_log.txt" +#define NOM_FICHIER_SCAN_WIFI_STA "scan_wifi_sta.txt" +#define NOM_FICHIER_ETAT_ENTREES_CORE "etat_entrees_core.txt" + +#define PREFIXE_NOM_FICHIER_ARCHIVAGE_LOG "log_archivage" + + + // Suffixes de fichiers : +#define SUFFIXE_NOUVEAU_FICHIER "_NEW" +#define SUFFIXE_ANCIEN_FICHIER "_OLD" +#define SUFFIXE_NOM_FICHIER_COMPRESSE_SAUVEGARDE_BDD "gz" // Extensions de type '.gz' +#define SUFFIXE_NOM_FICHIER_ENCRYPTE_SAUVEGARDE_BDD "enc" // Extensions de type '.gz.enc' puisque l'encryption est faite après la compression + + // Marqueurs de fichiers : +#define MARQUEUR_FICHIER_JSON ".json" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_TAR_GZIP ".tar.gz" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_ENC ".enc" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_TAR_GZIP_ENC ".tar.gz.enc" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_BASH ".sh" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_APPLICATION "shipheart_appli" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_QT "shipheart_qt" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_COMPAGNON_V1 "shipheart_compagnon_v1" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_COMPAGNON_V2 "shipheart_compagnon_v2" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_SP812 "shipheart_sp812" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_SBS "shipheart_sbs" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_SP4AC "shipheart_sp4ac" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_SP8DC "shipheart_sp8dc" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... +#define MARQUEUR_FICHIER_DONE ".DONE" // Attention : 'marqueur' veut dire qu'il est recherché partout dans le nom de fichier, pas uniquement à la fin... + + + + // Serveur distant : +#define SERVEUR_FTP_ADRESSE "ftp.cluster028.hosting.ovh.net" +#define SERVEUR_FTP_USER "ciuqsga-bateaux" +#define SERVEUR_FTP_PASSWORD "u7J9K45Df9Es" + +#define SERVEUR_SSH_ADRESSE "ssh.cluster028.hosting.ovh.net" +#define SERVEUR_SSH_USER SERVEUR_FTP_USER +#define SERVEUR_SSH_PASSWORD SERVEUR_FTP_PASSWORD + + +#define REPERTOIRE_SERVEUR "" // Pas de '/' à la fin... NB : le répertoire est /bateaux/ mais comme le USER 'ciuqsga-bateaux' accède uniquement au répertoire "/bateaux", ce REPERTOIRE_SERVEUR est vide. +#define REPERTOIRE_UPLOAD_DESTINATION "/upload" // Pas de '/' à la fin... +#define REPERTOIRE_DOWNLOAD_SOURCE "/download" // Pas de '/' à la fin... + diff --git a/sources/m_bus.c b/sources/m_bus.c new file mode 100644 index 0000000..f78e129 --- /dev/null +++ b/sources/m_bus.c @@ -0,0 +1,415 @@ +//********************************************************************************************************************************************************************************** +// Fichier M_BUS.C +// +// Contient toutes les fonctions liées à la gestion du M_Bus (liaison avec le Modem SIM7070G) +//**********************************************************************************************************************************************************************************/ + +#define __DEBUG_M_BUS 0 // Passer cette ligne à 0: aucun message de debug de M_bus, 1: les messages de debug de M_Bus sur l'écran, 2: sur fichier (cf NOM_FICHIER_DEBUG_BUS dans 'debug.h') ou 3: sur l'écran et fichier. + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_m_bus(void); +void gestion_m_bus(void); +int8_t gestion_protocole_rx_m_bus(void); +void gestion_protocole_tx_m_bus(void); +int8_t m_bus_next_write_buffer_cdes(uint8_t priorite); + +// Variables globales : +struct Struct_m_bus m_bus; + + +/********************************************************************************************************************************************************************************** + Fonction INIT_M_BUS + + Fonction d'initialisation du C-Bus +**********************************************************************************************************************************************************************************/ +int8_t init_m_bus(void) +{ + + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + //------------------------------------------------------------------------------------------------------ + // Init des variables générales : + + m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_INIT; + m_bus.sequenceur.compteur = M_BUS_TEMPO_INIT; + + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_INIT; + m_bus.protocole_rx.sequenceur.compteur = M_BUS_PROTOCOLE_RX_TEMPO_INIT; + + m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_INIT; + m_bus.protocole_tx.sequenceur.compteur = M_BUS_PROTOCOLE_TX_TEMPO_INIT; + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du M-BUS\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + } + else + { + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du M-BUS\t\t: Devalidé car non valide sur cette version de ShipBase", TYPE_MESSAGE_DEBUG_WARNING); + } + + return(0); +} + + + +/********************************************************************************************************************************************************************************** + Fonction GESTION_M_BUS + + Fonction de plus haut niveau de gestion du C-Bus +**********************************************************************************************************************************************************************************/ +void gestion_m_bus(void) +{ +int8_t ret; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (systeme.generation_shipbase != GENERATION_SHIPBASE_V1) return; + + switch(m_bus.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_M_BUS_INIT : + { + if (m_bus.sequenceur.compteur == 0) + { + //......................................................... + // Vidage du buffer de réception de tous les caractères reçus pendant la phase d'init : + serial0.ptr_debut_buffer_rx = 0; + serial0.ptr_fin_buffer_rx = 0; + + //......................................................... + // Vidage du buffer de commandes (utile dans le cas d'un reset du modem pour overflow) : + m_bus.down.ptr_debut_buffer_cdes = 0; + m_bus.down.ptr_fin_buffer_cdes = 0; + + //......................................................... + // Mise à jour des séquenceurs pour démarrage des envois : + m_bus.protocole_rx.sequenceur.compteur = 0; + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; + m_bus.protocole_tx.sequenceur.compteur = 0; + m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_REPOS; + + m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_M_BUS_REPOS : + { + if (modem.etat == ON) // Si le modem n'est pas ON, il est inutile d'essayer de communiquer avec lui. + { + //================================= + // Gestion du M-Bus en émission : + + gestion_protocole_tx_m_bus(); // Vérifie si une commande (Down) est en attente. Si oui, elle la traite et l'émet. + + + //================================= + // Gestion du M-Bus en réception : + + ret = gestion_protocole_rx_m_bus(); + if (ret != 0) // Vérifie si une réponse (à une commande Down) a été reçue. + { +#ifdef __DEBUG_M_BUS + m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. + if (ret == 1) affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Rx", (char *) m_bus.down.reponse.reponse, __DEBUG_M_BUS); // Cas d'une réponse normale + else + { + sprintf(tmp, "[%li data bytes]", strlen((const char *)m_bus.down.reponse.reponse)); + affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Rx", tmp, __DEBUG_M_BUS); // Cas d'une réponse en mode binaire + } +#endif + m_bus_down_analyse_reponse_commande(); + // Traitement de la réponse à la commande... + + + + } + } + + break; + } + + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur M-bus est inconnu [%i]. Il est repositionné au REPOS", m_bus.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + m_bus.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_REPOS; + break; + } + } +} + + +/***************************************************************************************************************************************************************** + Fonction : Appelée en tâche de fond, cette fonction lit le buffer de réception de l'UART 0 et traite les trames reçues (réponse du modem à une commande du CM3+) + Reçoit : Rien + Renvoit : 0 si RAS, 1 si une réponse complète a été reçue, 2 si réponse complète en mode binaire a été reçue +******************************************************************************************************************************************************************/ +int8_t gestion_protocole_rx_m_bus(void) +{ +uint8_t c; +int8_t reponse_recue; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + reponse_recue = 0; + + // Traitement de chaque caractère reçu : + while (serial0.ptr_debut_buffer_rx != serial0.ptr_fin_buffer_rx) + { + // Calcul de la prochaine position à lire (avec bouclage en fin de buffer tournant) : + if (++ serial0.ptr_debut_buffer_rx == MAX_TAILLE_RX_BUFFER_UART) serial0.ptr_debut_buffer_rx = 0; + + // Lecture du caractère dans le buffer : + c = serial0.buffer_rx[serial0.ptr_debut_buffer_rx]; // Lecture du caractère à traiter + + // Traitement du caractère reçu : + switch(m_bus.protocole_rx.sequenceur.etat) + { + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_INIT : + { + // NB : Le caractère lu est jeté tant que la phase d'init n'est pas terminée... + // NB 2 : le passage à l'état REPOS se fait par le séquenceur M-Bus, indépendemment de la réception ou non de caractères. + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS : + { + + if (c == ASCII_CR) // Attente de réception du CR de début de trame + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF; + } + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF : + { + + if (c == ASCII_CR) break; // Cas d'un CR qui suit un CR. On reste en attente d'un LF à venir... + + if (c == ASCII_LF) + { + if (m_bus.down.reponse.lg_reponse != 0) reponse_recue = 1; // Si la longueur actuelle de réponse est nulle, on ne fait rien (on va juste revenir en mode data pour le prochain caractère) + + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_DATA; // Dans tous les cas, après un CR/LF, on passe en mode Data car il arrive qu'un CR/LF soit juste un séparateur entre plusieurs blocs (il y a donc une fin mais pas de "début" suivant) + } + else + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; + } + + break; + } + + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_DATA : + { + + if (c != ASCII_CR) m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse ++] = c; + else + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF; + } + + break; + } + + //------------------------------------------------------------------------------------ + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE: + { + m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse ++] = c; + if (m_bus.down.reponse.lg_reponse == m_bus.down.reponse.lg_reponse_attendue) + { + reponse_recue = 2; + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; + } + + break; + } + + //------------------------------------------------------------------------------------ + default : + { + sprintf(tmp, "L'état du séquenceur M-Bus.Protocole_Rx est inconnu [%i]. Il est repositionné au REPOS", m_bus.protocole_rx.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS; + break; + } + } + + if (reponse_recue != 0) break; + + } + + return(reponse_recue); + +} + + +/***************************************************************************************************************************************************************** + Fonction : Appelée en tâche de fond, cette fonction vérifie si une commande est prête à être émise + dans le buffer de commandes Down. Si oui, elle gère via son séquenceur le formattage de la trame à émettre. + Reçoit : Rien + Renvoit : Rien +*****************************************************************************************************************************************************************/ +void gestion_protocole_tx_m_bus(void) +{ +uint16_t i; +uint8_t tx_buffer[M_BUS_MAX_TAILLE_BUFFER_TX]; +uint16_t lg_tx_buffer; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(m_bus.protocole_tx.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_INIT : + { + // NB : le passage à l'état REPOS se fait par le séquenceur C-Bus, indépendemment de la présence de commande à émettre + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_REPOS : + { + //.................................. + // Recherche d'une trame à émettre : + + if (m_bus.down.flag_attente_reponse) // Cas où une commande Down a déjà été émise et qu'on est en attente de réponse : + { + if (m_bus.protocole_tx.sequenceur.compteur_time_out == 0) // On vérifie alors si le time-out a expiré : + { + m_bus.down.flag_attente_reponse = FALSE; // RAZ du flag (qui sera éventuellement remis à 1 si la répétition a lieu) + m_bus.protocole_tx.repetitions ++; // Incrémentation du nombre de répétitions + if (m_bus.protocole_tx.repetitions > m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].max_repetitions) // Si le nombre maxi de répétitions a été dépassé, on laisse tomber la commande + { + +// !!! Il serait peut-être intéressant d'informer la couche supérieure que la commande est abandonnée sans avoir obtenu de réponse malgré N répétitions... +// ==>>> Si oui, c'est ici qu'il faut le mettre.... + + m_bus.protocole_tx.repetitions = 0; // RAZ du compteur pour utilisation ultérieure + break; // Sortie du séquenceur + } + } + else break; // Tant qu'on est en attente d'une réponse et que le time-out n'est pas expiré, on ne traite aucun envoi en mode Down + } + else // Sinon, recherche d'une nouvelle commande à émettre en mode Down : + { + if (m_bus.down.ptr_debut_buffer_cdes != m_bus.down.ptr_fin_buffer_cdes) // Cas où il y a une commande en attente d'émission... + { +// ATTENTION : gérer les priorités ...! + + m_bus.down.reponse.etat = M_BUS_REPONSE_ETAT_REPOS; // On prépare le traitement de la réponse à venir. + + if (++ m_bus.down.ptr_debut_buffer_cdes == M_BUS_MAX_TAILLE_BUFFER_CDES) m_bus.down.ptr_debut_buffer_cdes = 0; + +#ifdef __DEBUG_M_BUS + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde[m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].lg_cde] = 0; // On ajoute un /0 à la fin mais on ne change surtout pas la longueur qui est utilisée pour les transferts binaires. + if (m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].mode_protocole != M_BUS_PROTOCOLE_DATA_BINAIRE) affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Tx", (char *) m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde, __DEBUG_M_BUS); + else + { + sprintf(tmp, "[%i data bytes]", m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].lg_cde); + affichage_debug_bus(TYPE_BUS_DEBUG_M_BUS, "Tx", tmp, __DEBUG_M_BUS); // Cas d'une réponse en mode binaire + } +#endif + } + else break; // Il n'y a pas de nouvelle commande en attente d'émission + } + + //..................................... + // Préparation de la trame d'émission : + lg_tx_buffer = 0; + + for (i=0; (i < m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].lg_cde) && (lg_tx_buffer < M_BUS_MAX_TAILLE_BUFFER_TX); i++) + { + tx_buffer[lg_tx_buffer ++] = m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde[i]; + } + + if (m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].mode_protocole != M_BUS_PROTOCOLE_DATA_BINAIRE) + { + tx_buffer[lg_tx_buffer ++] = ASCII_CR; + } + + //............................. + // Envoi au driver d'émission : + write(config_serial0.filestream, tx_buffer, lg_tx_buffer); + + //.......................................................... + // Mise à jour des paramètres après l'émission de la trame : + m_bus.down.flag_attente_reponse = TRUE; + m_bus.protocole_tx.sequenceur.compteur_time_out = m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].time_out; + + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur M-Bus.Protocole_Tx est inconnu [%i]. Il est repositionné au REPOS", m_bus.protocole_tx.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + m_bus.protocole_tx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_REPOS; + break; + } + } +} + + + +/***************************************************************************************************************************************************************** + Fonction : Incrémente le pointeur du buffer de commandes en attente d'émission vers C-Bus + La fonction gère : + - le rebouclage en fin de buffer (buffer circulaire) + - le dépassement (quand la fin rattrape le début) en émettant un message de debug et en choissant : + . soit d'écraser la commande la plus ancienne si elle est de priorité inférieure (strictement) + . soit refuser l'ajout en laissant le pointeur de fin du buffer à sa position et en renvoyant une erreur. + Reçoit : La priorité de la commande à ajouter (permet d'arbitrer dans la cas où le buffer est plein) + Renvoit : 0 si une position disponible a été trouvée, -1 si aucune position n'est disponible. +******************************************************************************************************************************************************************/ +int8_t m_bus_next_write_buffer_cdes(uint8_t priorite) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // !!! La gestion des priorités n'est pas traitée ! A vérifier mais si on écrase le début, celui-ci est probablement en cours d'utilisation... + // Donc ATTENTION !!!! + + + // Incrémentation : + if (++ m_bus.down.ptr_fin_buffer_cdes == M_BUS_MAX_TAILLE_BUFFER_CDES) m_bus.down.ptr_fin_buffer_cdes = 0; + + // Vérification si buffer plein : + if (m_bus.down.ptr_fin_buffer_cdes == m_bus.down.ptr_debut_buffer_cdes) + { + // Décrémentation du pointeur puisqu'on ne va pas accepter la nouvelle commande qui n'a pas une priorité suffisante : + if (m_bus.down.ptr_fin_buffer_cdes == 0) m_bus.down.ptr_fin_buffer_cdes = (M_BUS_MAX_TAILLE_BUFFER_CDES - 1); + else m_bus.down.ptr_fin_buffer_cdes --; + + // Message de débug : + sprintf(tmp, "Buffer de commandes M-Bus plein. La demande de stockage d'une nouvelle commande a été rejetée..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_BUS_BUFFER_COMMANDES_PLEIN, (uint32_t) 'M', 0, NULL); + + return(-1); + } + else return (0); +} + + + diff --git a/sources/m_bus.h b/sources/m_bus.h new file mode 100644 index 0000000..039b876 --- /dev/null +++ b/sources/m_bus.h @@ -0,0 +1,57 @@ +/********************************************************************************************************************************************************************************** + + Fichier M_BUS.H + + Contient tous les define liés au M_BUS + +**********************************************************************************************************************************************************************************/ + + // Maximums : +#define M_BUS_MAX_TAILLE_BUFFER_CDES 80 // Taille du buffer de commandes (donc on stocke plusieurs commandes entières !) M-bus en mode Down +#define M_BUS_PROTOCOLE_TX_MAX_REPETITIONS 3 // Nombre de répétitions des commandes émises sur M_Bus +#define M_BUS_MAX_TAILLE_BUFFER_TX 3050 +#define M_BUS_MAX_TAILLE_CDE 3000 // Nécessaire pour FTP +#define M_BUS_MAX_TAILLE_REPONSE 3000 +#define M_BUS_MAX_REPETITIONS_TEMPO_POST_ACTIVATION_MODEM 30 // Cette valeur est à relier à la TEMPO_POST_ACTIVATION_MODEM. Le produit des deux donne la durée maximale avant de déclarer que le changement d'état attendu du modem a échoué. +#define M_BUS_MAX_REFUS_BUFFER_CDES_PLEIN 12 // Au-delà de cette valeur, si un refus supplémentaire est rencontré, le Modem sera réinitialisé + + + // Tempos : +#define M_BUS_TEMPO_INIT 20 // En x 100ms +#define M_BUS_TEMPO_IMPULSION_ACTIVATION_MODEM 12 // En x 100ms (Tmin = 1s selon datasheet du SIM7070G, page 26) +#define M_BUS_TEMPO_POST_IMPULSION_ACTIVATION_MODEM 5 // En x 100ms Cette tempo est à relier au MAX_REPETITIONS_TEMPO_POST_ACTIVATION_MODEM ci-dessus. +#define M_BUS_PROTOCOLE_RX_TEMPO_INIT 10 // en x 100ms +#define M_BUS_PROTOCOLE_TX_TEMPO_INIT 10 // en x 100ms +#define M_BUS_TIME_OUT_REPONSE_TX 3 // En x 100ms + + + // Priorité des commandes : +#define PRIORITE_CDE_M_BUS_FAIBLE 0 +#define PRIORITE_CDE_M_BUS_HAUTE 1 + + // Mode de traitement de la commande par le protocole : +#define M_BUS_PROTOCOLE_NORMAL 0 +#define M_BUS_PROTOCOLE_DATA_BINAIRE 1 // Dans ce cas, aucun CR n'est ajouté en fin de commande + + + // Etats du séquenceur C-Bus : +#define ETAT_SEQUENCEUR_M_BUS_INIT 0 +#define ETAT_SEQUENCEUR_M_BUS_INIT_ACTIVATION_MODEM 1 +#define ETAT_SEQUENCEUR_M_BUS_INIT_IMPULSION_ACTIVATION_MODEM 2 +#define ETAT_SEQUENCEUR_M_BUS_INIT_ATTENTE_FIN_ACTIVATION_MODEM 3 +#define ETAT_SEQUENCEUR_M_BUS_REPOS 4 + + // Etats séquenceur du Protocole_Rx C-bus : +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_INIT 0 +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_REPOS 1 +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_ATTENTE_LF 2 +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_DATA 3 +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE 4 + + // Etats séquenceur du Protocole_Tx C-bus : +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_INIT 0 +#define ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_TX_REPOS 1 + + + + diff --git a/sources/m_commandes.c b/sources/m_commandes.c new file mode 100644 index 0000000..18373dd --- /dev/null +++ b/sources/m_commandes.c @@ -0,0 +1,1937 @@ +//********************************************************************************************************************************************************************************** +// Fichier M_COMMANDES.C +// +// Contient toutes les fonctions liées aux commandes envoyées et reçues sur M_Bus +//**********************************************************************************************************************************************************************************/ + + +// #define __DEBUG_M_COMMANDES_GENERALES //Décommenter cette ligne pour avoir les messages de debug concernant les commandes générales du modem +// #define __DEBUG_M_COMMANDES_GNSS //Décommenter cette ligne pour avoir les messages de debug concernant les commandes GNSS +// #define __DEBUG_M_COMMANDES_SMS //Décommenter cette ligne pour avoir les messages de debug concernant les commandes SMS +// #define __DEBUG_M_COMMANDES_FTP_GET //Décommenter cette ligne pour avoir les messages de debug concernant les commandes FTP GET +// #define __DEBUG_M_COMMANDES_FTP_LIST //Décommenter cette ligne pour avoir les messages de debug concernant les commandes FTP LIST +// #define __DEBUG_M_COMMANDES_FTP_PUT //Décommenter cette ligne pour avoir les messages de debug concernant les commandes FTP PUT +// #define __DEBUG_M_COMMANDES_HTTP //Décommenter cette ligne pour avoir les messages de debug concernant les commandes HTTP + + + + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t m_bus_down_commande_libre(char * ptr_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +int8_t m_bus_down_commande_libre_binaire(uint16_t code, char * ptr_cde, uint32_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +int8_t m_bus_down_analyse_reponse_commande(void); +int8_t m_bus_down_recherche_code_erreur(void); + + +// Variables globales : + + + +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** +// DOWN +//*************************************************************************************************************************************************************** +//*************************************************************************************************************************************************************** + +/***************************************************************************************************************************************************************** + Fonction : Cette fonction est intermédiaire. Elle sert juste à calculer la taille de la commande à émettre avant de la transmettre à la fonction BINAIRE + qui suit. Cela évite d'alourdir l'appel des commandes en demandant un 'strlen("AT+xxxx")' alors que la chaine est déjà passée en paramètres. + De plus, la fonction BINAIRE est parfois appelée directement avec des commandes qui ne sont pas des chaines (et dans ce cas, on passe la longueur). + Reçoit : un pointeur sur le début de la commande (la chaine doit se terminer par NULL), le mode protocole, la durée du time-out s'il doit être particulier (en x100ms) + et le nombre maxi de répétitions (-1 si valeur par défaut) + Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +******************************************************************************************************************************************************************/ +int8_t m_bus_down_commande_libre(char * ptr_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions) +{ +uint16_t code; + + + if (strstr(ptr_cde, "ATE0") != NULL) code = M_COMMANDE_ATE; // Il est important de mettre le '0', sinon cette commande intercepte les SHSTATE... (ou alors, mettre ATE [après] SHSTATE) + else if (strstr(ptr_cde, "AT+CCID") != NULL) code = M_COMMANDE_CCID; + else if (strstr(ptr_cde, "AT+CFUN") != NULL) code = M_COMMANDE_CFUN; + else if (strstr(ptr_cde, "AT+CGMR") != NULL) code = M_COMMANDE_CGMR; + else if (strstr(ptr_cde, "AT+CGNAPN") != NULL) code = M_COMMANDE_CGNAPN; + else if (strstr(ptr_cde, "AT+CGNSINF") != NULL) code = M_COMMANDE_CGNSINF; + else if (strstr(ptr_cde, "AT+CGSN") != NULL) code = M_COMMANDE_CGSN; + else if (strstr(ptr_cde, "AT+CIMI") != NULL) code = M_COMMANDE_CIMI; + else if (strstr(ptr_cde, "AT+CMGL=") != NULL) code = M_COMMANDE_CMGL; + else if (strstr(ptr_cde, "AT+CNACT?") != NULL) code = M_COMMANDE_CNACT; + else if (strstr(ptr_cde, "AT+CNACT=0,") != NULL) code = M_COMMANDE_CNACT_0_X; + else if (strstr(ptr_cde, "AT+CNUM") != NULL) code = M_COMMANDE_CNUM; + else if (strstr(ptr_cde, "AT+CPSI?") != NULL) code = M_COMMANDE_CPSI; + else if (strstr(ptr_cde, "AT+CREG?") != NULL) code = M_COMMANDE_CREG; + else if (strstr(ptr_cde, "AT+CSQ") != NULL) code = M_COMMANDE_CSQ; + else if (strstr(ptr_cde, "AT+FTPGET=1") != NULL) code = M_COMMANDE_FTPGET_1; + else if (strstr(ptr_cde, "AT+FTPGET=2") != NULL) code = M_COMMANDE_FTPGET_2; + else if (strstr(ptr_cde, "AT+FTPLIST=1") != NULL) code = M_COMMANDE_FTPLIST_1; + else if (strstr(ptr_cde, "AT+FTPLIST=2") != NULL) code = M_COMMANDE_FTPLIST_2; + else if (strstr(ptr_cde, "AT+FTPPUT=1") != NULL) code = M_COMMANDE_FTPPUT_1; + else if (strstr(ptr_cde, "AT+FTPPUT=2") != NULL) code = M_COMMANDE_FTPPUT_2; + else if (strstr(ptr_cde, "AT+FTPSIZE") != NULL) code = M_COMMANDE_FTPSIZE; + else if (strstr(ptr_cde, "AT+SHREAD") != NULL) code = M_COMMANDE_SHREAD; + else if (strstr(ptr_cde, "AT+SHREQ") != NULL) code = M_COMMANDE_SHREQ; + else if (strstr(ptr_cde, "AT+SHSTATE?") != NULL) code = M_COMMANDE_SHSTATE; + else code = M_COMMANDE_INCONNUE; + + return(m_bus_down_commande_libre_binaire(code, ptr_cde, strlen(ptr_cde), mode_protocole, time_out, max_repetitions)); + +} + + +/***************************************************************************************************************************************************************** + Fonction : Commande libre BINAIRE formattée et envoyée vers le modem + Vérifie que le buffer de commandes à émettre sur C-Bus a un emplacement de libre. + Si oui, génère la commande correspondante dans le buffer de commandes et met à jour la longueur de la commande ainsi que + sa priorité. + Reçoit : Le code de la commande, un pointeur sur le début de la commande (la chaine n'a PAS à se terminer par NULL), la longueur de la commande, + le mode protocole, la durée du time-out s'il doit être particulier (en x100ms) et le nombre maxi de répétitions (-1 si valeur par défaut). + Renvoit : 0 si aucune erreur, -1 si la commande n'a pas pu être générée et stockée dans le buffer de commandes. +******************************************************************************************************************************************************************/ +int8_t m_bus_down_commande_libre_binaire(uint16_t code, char * ptr_cde, uint32_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions) +{ +uint8_t priorite; +struct Struct_commande_m_bus *buffer_cde; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Vérification des paramètres reçus : + if ((mode_protocole != M_BUS_PROTOCOLE_NORMAL) && + (mode_protocole != M_BUS_PROTOCOLE_DATA_BINAIRE)) return(-1); + + if (lg_cde >= M_BUS_MAX_TAILLE_CDE) return(-1); + + // Préparation de la commande : + priorite = PRIORITE_CDE_M_BUS_FAIBLE; + + if (m_bus_next_write_buffer_cdes(priorite) != -1) // On recherche le prochain slot de commande libre + { + m_bus.nb_refus_buffer = 0; // RAZ du compteur à chaque réussite + + buffer_cde = &m_bus.down.buffer_cdes[m_bus.down.ptr_fin_buffer_cdes]; // L'utilisation du pointeur de struct simplifie l'écriture ensuite... + buffer_cde->lg_cde = 0; + + // Chargement de la commande proprement-dite : + while (buffer_cde->lg_cde < lg_cde) + { + buffer_cde->cde[buffer_cde->lg_cde ++] = *(ptr_cde ++); + } + + // Code de la commande : + buffer_cde->code = code; + + // Priorité de la commande : + buffer_cde->priorite = priorite; + + // Type de réponse attendue : + buffer_cde->mode_protocole = mode_protocole; + + // Time-out à utiliser pour la commande : + if (time_out > 0) buffer_cde->time_out = time_out; + else buffer_cde->time_out = M_BUS_TIME_OUT_REPONSE_TX; + + // Nombre max de répétitions : + if (max_repetitions >= 0) buffer_cde->max_repetitions = max_repetitions; + else buffer_cde->max_repetitions = M_BUS_PROTOCOLE_TX_MAX_REPETITIONS; + + return(0); + } + else + { + if (++ m_bus.nb_refus_buffer > M_BUS_MAX_REFUS_BUFFER_CDES_PLEIN) // Si les refus de nouvelle commandes sur M-Bus augmentent, on va réactiver le modem car il y a un sérieux problème... + { + sprintf(tmp, "Le nombre de refus de nouvelle commande dans le buffer de M-Bus est trop important (%i). Le modem va être réactivé", m_bus.nb_refus_buffer); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_M_BUS_PLEIN, 0, m_bus.nb_refus_buffer, NULL); + + modem.sequenceur.etat = ETAT_SEQUENCEUR_MODEM_DEBUT_IMPULSION_RESET; // Déclenche une impulsion >12s de reset sur POWER_KEY + } + return (-1); // Si aucun slot n'est disponible, on laisse tomber la commande... + } +} + +/***************************************************************************************************************************************************************** + Fonction : Analyse la réponse à la commande et effectue, si besoin, le traitement nécessaire. + Le principe est de d'abord de détecter la présence d'un éventuel code d'erreur dans la réponse et de stocker son code. + Ensuite, on saute à la fonction (en pratique, un case d'un grand switch) correspondant à la commande qui avait été émise et donc qui déclenche + cette réponse. Là, on peut avoir les grandes tâches suivantes : + 1- cas où on vient de recevoir un code d'erreur (y compris OK qui est considéré comme l'un d'eux) : dans ce cas, on décide ce qu'on doit faire + de ce code dans ce contexte. Souvent, il s'agit de ne pas tenir compte d'un OK intempestif (poru cela, on écrase l'erreur par M_BUS_ERREUR_AUCUNE). + 2- On effectue le traitement de la trame reçue, selon la fonction émise et le contexte. + 3- si on considère que le traitement est terminé, on écrase le code d'erreur pour le mettre à M_BUS_ERREUR_OK. Cette phase 3 est inutile pour pas + mal de commandes simples car leur trame de retour est suivie d'une trame OK qui libère le protocole tout seul. + En toute fin de fonction, on traite le code d'erreur subsistant. S'il est M_BUS_ERREUR_AUCUNE, on ne fait rien. S'il est M_BUS_ERREUR_OK on remet + le protocole au repos. + Reçoit : Rien (traite la réponse qui est stockée dans la structure m_bus.down.reponse.xxxx + Renvoit : 0 si aucune erreur, -1 si l'analyse de la réponse a rencontré une erreur. +******************************************************************************************************************************************************************/ +int8_t m_bus_down_analyse_reponse_commande(void) +{ +int8_t erreur; +int16_t val_16; +uint16_t i, j; +uint16_t x; +char *ptr_chaine; +int32_t ptr_reponse; +int32_t lg_parametre; +char parametre[M_COMMANDE_MAX_LONGUEUR_PARAMETRE_REPONSE]; +char *ptr[M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE]; +MYSQL_RES *result = NULL; +MYSQL_ROW row = NULL; +char requete[MAX_LONGUEUR_REQUETE_MYSQL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse] = 0; // Ajout d'un caractère NULL de fin de chaine mais sans changer la longueur des données utiles (pas de 'lg_reponse ++' après l'affectation)... + erreur = m_bus_down_recherche_code_erreur(); + + switch(m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code) + { + //------------------------------------------------------------------------------------------------------ + // Commande d'echo ATEx : + case M_COMMANDE_ATE: + { + if (erreur == M_BUS_ERREUR_OK) + { + modem.etat_liaison = ON; // Mise à jour de l'état de la liaison dès qu'on a réussi un échange de commande ATEx réussi... + modem.sequenceur.compteur = 0; // ... et on force le compteur du modem à 0 car il est inutile d'attendre plus. + sprintf(tmp, "La liaison avec le modem est établie"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Numéro d'ICCID : + case M_COMMANDE_CCID: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='No_iccid' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *) m_bus.down.reponse.reponse) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour du numéro d'ICCID de la carte SIM dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='No_iccid' ", m_bus.down.reponse.reponse); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_ICCID, 0, 0, (char *) m_bus.down.reponse.reponse); + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture du n° d'ICCID (Integrated Circuit Card ID - numéro de série de la carte SIM) du modem reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Functions du Modem : + case M_COMMANDE_CFUN: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture des Functions du modem reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Révision du firmware : + case M_COMMANDE_CGMR: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture de la version du modem reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='Version_soft_modem' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *) m_bus.down.reponse.reponse) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour de la révision du firmware du modem dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Version_soft_modem' ", m_bus.down.reponse.reponse); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_REVISION_FIRMWARE, 0, 0, (char *) m_bus.down.reponse.reponse); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Acces Point Name (APN) : + case M_COMMANDE_CGNAPN: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture de l'Access Point Name (APN) reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + bool flag_validite = FALSE; + + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + // Traitement de chaque paramètre : + switch(i) + { + case 1 : // Validité : + { + if (atoi(ptr[i-1]) == 1) flag_validite = TRUE; + break; + } + + case 2 : // APN : + { + if (!flag_validite) break; // Si le premier paramètre n'est pas 1, on ne traite pas le second + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='Modem_apn' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *) ptr[i-1]) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour de l'APN dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Modem_apn' ", ptr[i-1]); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_APN, 0, 0, ptr[i-1]); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + default : ; + } + + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // GNSS Navigation Information : + // Exemples de réponse reçue sur M-Bus '+CGNSINF: 1,,,0.000000,0.000000,-18.000,,,1,,0.1,0.1,0.1,,,,9999000.0,6144.0' + // '+CGNSINF: 1,1,20201119125432.000,45.751259,4.802224,252.754,0.00,,0,,1.5,1.8,1.0,,6,,10.4,6.0' + case M_COMMANDE_CGNSINF: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "Réponse à la commande de lecture GNSS reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + bool flag_sortie = FALSE; + + ptr_reponse = recherche_caractere_dans_chaine((char *)m_bus.down.reponse.reponse, ':', 0); + + if (ptr_reponse != -1) + { + ptr_reponse += 2; // Le '+2' permet de démarrer la 'ptr_reponse' après le ": ". + + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (m_bus.down.reponse.reponse[ptr_reponse] != 0)); i++) + { + // Recherche du bloc paramètre suivant : + lg_parametre = recherche_caractere_dans_chaine((char *)m_bus.down.reponse.reponse, ',', ptr_reponse) - ptr_reponse; + + if (lg_parametre >= 0) // Cas intermédiaire (on a trouvé un ',') : + { + for (j = 0; j < lg_parametre; j ++) parametre[j] = m_bus.down.reponse.reponse[ptr_reponse ++]; + parametre[j] = 0; + ptr_reponse ++; // Permet d'échapper le séparateur ',' + } + else // Cas du dernier paramètre (on n'a pas trouvé de nouveau ',') : + { + for (j = 0; m_bus.down.reponse.reponse[ptr_reponse] != 0; j ++) parametre[j] = m_bus.down.reponse.reponse[ptr_reponse ++]; + parametre[j] = 0; + } + + // Traitement du paramètre : + switch(i) + { + //===================================================== + case 1 : // Run status (0 GNSS off, 1 GNSS on) : + { + if (atoi(parametre) != 1) flag_sortie = TRUE; + break; + } + + //===================================================== + case 2 : // Fix status (0 Not fixed position, 1 Fixed position.) : + { + if (atoi(parametre) != 1) flag_sortie = TRUE; + else modem.compteur_cycles_valides_gnss ++; // Le compteur est incrémenté à chaque fois qu'on reçoit une trame avec une position valide + + break; + } + + //===================================================== + case 3 : // UTC Date & Time : + { + if (strlen(parametre) != 0) + { + struct Struct_date date; + struct Struct_heure heure; + char chaine_temp[20]; + uint8_t n, m; + + n = 0; // 'n' balaie 'parametre' du début à la fin + + for (m=0; m<4; m++) chaine_temp[m] = parametre[n ++]; // 'm' balaie 'paramètre' sur un nombre défini de caractères, à partir de la position actuelle de 'n' + chaine_temp[m] = 0; + date.annee = atoi(chaine_temp); + + for (m=0; m<2; m++) chaine_temp[m] = parametre[n ++]; + chaine_temp[m] = 0; + date.mois = atoi(chaine_temp); + + for (m=0; m<2; m++) chaine_temp[m] = parametre[n ++]; + chaine_temp[m] = 0; + date.jour = atoi(chaine_temp); + + for (m=0; m<2; m++) chaine_temp[m] = parametre[n ++]; + chaine_temp[m] = 0; + heure.heures = atoi(chaine_temp); + + for (m=0; m<2; m++) chaine_temp[m] = parametre[n ++]; + chaine_temp[m] = 0; + heure.minutes = atoi(chaine_temp); + + for (m=0; m<2; m++) chaine_temp[m] = parametre[n ++]; + chaine_temp[m] = 0; + heure.secondes = atoi(chaine_temp); + + set_date_heure(date, heure); // Mise à jour de la date/heure système à partir de l'heure reçue du GNSS (toujours en UTC !) + +// SDIR #107 : +// c_bus_down_commande_config_date_heure(); // Mise à jour de la date/heure du Compagnon dans la foulée (lui aussi, toujours en UTC) + +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tUTC Date & Time = [%s], soit le %02i/%02i/%04i à %02i:%02i:%02i", parametre, date.jour, date.mois, date.annee, heure.heures, heure.minutes, heure.secondes); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 4 : // Latitude : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_LAT); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tLatitude = [%s], soit %i en millionnième de degré", parametre, entrees.predefinie.compagnon.lat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + break; + } + + //===================================================== + case 5 : // Longitude : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_LON); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tLongitude = [%s], soit %i en millionnième de degré", parametre, entrees.predefinie.compagnon.lon); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 6 : // Altitude : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_LAT); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) + { + if (parametre[0] == '-') entrees.predefinies.entrees[val_16].valeur = 0; // Si la valeur retournée est négative, on la fixe à 0... + else entrees.predefinies.entrees[val_16].valeur = atof(parametre) / 1000; + } +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tAltitude = [%s], soit ALT = %i mm", parametre, entrees.predefinie.compagnon.alt); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 7 : // Speed over ground (km/h) : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_SOG); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre) / 1.852; // Conversion en Knts (noeuds) +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tVitesse fond = [%s], soit SOG = %i", parametre, entrees.predefinie.compagnon.sog); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 8 : // Course over ground : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_COG); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tCap réel = [%s], soit COG = %i", parametre, entrees.predefinie.compagnon.cog); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 9 : // Fix mode : + { +// sprintf(tmp, "\tConstellation utilisée = [%s]", parametre); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + break; + } + + //===================================================== + case 11 : // HDOP : + { +// sprintf(tmp, "\tHDOP = [%s]", parametre); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + break; + } + + //===================================================== + case 12 : // PDOP : + { +// sprintf(tmp, "\tPDOP = [%s]", parametre); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + break; + } + + //===================================================== + case 13 : // VDOP : + { +// sprintf(tmp, "\tVDOP = [%s]", parametre); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + break; + } + + //===================================================== + case 15 : // GNSS Satellites in view : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_SIV); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tNombre satellites = [%s], soit SIV = %i", parametre, entrees.predefinie.compagnon.siv); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + //===================================================== + case 17 : // HPA : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_HPA); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS + sprintf(tmp, "\tHPA = [%s], soit %i m", parametre, entrees.predefinie.compagnon.hpa); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + //===================================================== + case 18 : // VPA : + { + if (strlen(parametre) != 0) + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_VPA); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atof(parametre); +#ifdef __DEBUG_M_COMMANDES_GNSS +sprintf(tmp, "\tVPA = [%s], soit %i m", parametre, entrees.predefinie.compagnon.vpa); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + break; + } + + default : ; + } + + if (flag_sortie) break; // Evite de continuer à parser la réponse alors que la chaine ne sera pas exploitable... + + } + + modem.sequenceur.compteur_time_out = 0; // Inutile l'attendre la fin du time-out puiqu'on a reçu la réponse + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Numéro d'IMEI : + case M_COMMANDE_CGSN: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture du n° d'EMEI reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='No_imei' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *) m_bus.down.reponse.reponse) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour du numéro d'IMEI du modem dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='No_imei' ", m_bus.down.reponse.reponse); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_IMEI, 0, 0, (char *) m_bus.down.reponse.reponse); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Numéro d'IMSI : + case M_COMMANDE_CIMI: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture du N° d'IMSI reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='No_imsi' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *) m_bus.down.reponse.reponse) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour du numéro d'IMSI de la carte SIM dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='No_imsi' ", m_bus.down.reponse.reponse); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_IMSI, 0, 0, (char *) m_bus.down.reponse.reponse); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture des SMS : + // ATTENTION : ce code ne fonctionne qu'à 3 conditions : + // 1. que la réception soit configurée en mode Texte (AT+CMGF=1) + // 2. que le message fasse moins de 140 caractèrs (sinon il semble qu'il est convertit en mode PDU automatiquement) + // 3. que le message ne comprenne pas de retour à la ligne. + case M_COMMANDE_CMGL: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "Réponse à la commande de lecture des SMS reçus CMGL : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + bool flag_sortie = FALSE; + + ptr_reponse = recherche_caractere_dans_chaine((char *)m_bus.down.reponse.reponse, ':', 0); + + if (ptr_reponse != -1) + { + ptr_reponse += 2; // Le '+2' permet de démarrer la 'ptr_reponse' après le ": ". + + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (m_bus.down.reponse.reponse[ptr_reponse] != 0)); i++) + { + // Recherche du bloc paramètre suivant : + lg_parametre = recherche_caractere_dans_chaine((char *)m_bus.down.reponse.reponse, ',', ptr_reponse) - ptr_reponse; + + if (lg_parametre >= 0) // Cas intermédiaire (on a trouvé un ',') : + { + for (j = 0; j < lg_parametre; j ++) parametre[j] = m_bus.down.reponse.reponse[ptr_reponse ++]; + parametre[j] = 0; + ptr_reponse ++; // Permet d'échapper le séparateur ',' + } + else // Cas du dernier paramètre (on n'a pas trouvé de nouveau ',') : + { + for (j = 0; m_bus.down.reponse.reponse[ptr_reponse] != 0; j ++) parametre[j] = m_bus.down.reponse.reponse[ptr_reponse ++]; + parametre[j] = 0; + } + + // Traitement du paramètre : + switch(i) + { + //===================================================== + case 1 : // Index : + { + modem.sms_en_cours.index = atoi(parametre); +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "\tIndex du message = %i", atoi(parametre)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + //===================================================== + case 2 : // Stat : + { +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "\tStat du message = [%s]", parametre); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + //===================================================== + case 3 : // Emetteur du message : + { + for(j = 0; parametre[j] != 0; j ++) parametre[j] = parametre[j+1]; // Supprime les '"' de début et de fin... + if (j > 1) parametre[j-2] = 0; + + strcpy(modem.sms_en_cours.numero_emetteur, parametre); +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "\tEmetteur du message = [%s]", parametre); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + //===================================================== + case 4 : // Alpha de l'émetteur : + { +#ifdef __DEBUG_M_COMMANDES_SMS + if (strlen(parametre) > 0) + { + sprintf(tmp, "\tAlpha de l'emetteur du message = [%s]", parametre); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + + break; + } + + //===================================================== + case 5 : // Date du message : + { + j = 0; + + modem.sms_en_cours.date[j ++] = '2'; // La date n'étant fournie que sur 2 digits, on ajoute le centenaire devant (20xx) + modem.sms_en_cours.date[j ++] = '0'; + modem.sms_en_cours.date[j ++] = parametre[1]; // Année (dizaines) NB : le rang 0 est occupé par le '"' de début de date + modem.sms_en_cours.date[j ++] = parametre[2]; // Année (unités) + + modem.sms_en_cours.date[j ++] = '-'; + + modem.sms_en_cours.date[j ++] = parametre[4]; // Mois (dizaines) + modem.sms_en_cours.date[j ++] = parametre[5]; // Mois (unités) + + modem.sms_en_cours.date[j ++] = '-'; + + modem.sms_en_cours.date[j ++] = parametre[7]; // jour (dizaines) + modem.sms_en_cours.date[j ++] = parametre[8]; // jour (unités) + + modem.sms_en_cours.date[j ++] = ' '; // Séparation avec l'heure à venir + modem.sms_en_cours.date[j ++] = 0; + + break; + } + + //===================================================== + case 6 : // Heure du message : + { + j = 11; + modem.sms_en_cours.date[j ++] = parametre[0]; // Heures (dizaines) + modem.sms_en_cours.date[j ++] = parametre[1]; // Heures (unités) + + modem.sms_en_cours.date[j ++] = ':'; + + modem.sms_en_cours.date[j ++] = parametre[3]; // Minutes (dizaines) + modem.sms_en_cours.date[j ++] = parametre[4]; // Minutes (unités) + + modem.sms_en_cours.date[j ++] = ':'; + + modem.sms_en_cours.date[j ++] = parametre[6]; // Secondes (dizaines) + modem.sms_en_cours.date[j ++] = parametre[7]; // Secondes (unités) + + modem.sms_en_cours.date[j ++] = 0; + +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "\tDate et heure du message = [%s]", modem.sms_en_cours.date); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + break; + } + + + default : + { +sprintf(tmp, "\tParamètre inconnu trouvé en [%i] position = [%s]", i, parametre); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + break; + } + } + + if (flag_sortie) break; // Evite de continuer à parser la réponse alors que la chaine ne sera pas exploitable... + + } + + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_CMGL_RECEPTION_DATA_MESSAGE; + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // APP Network Active : + case M_COMMANDE_CNACT: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Une réponse a la commande CNACT? été reçue de la part du modem : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + if (strstr((char *)m_bus.down.reponse.reponse, "+CNACT: 0,1") != NULL) // Cas où le PDP #0 est actif. + { + if (modem.etat_4g_lte == OFF) enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ETABLISSEMENT_LIAISON_4G_LTE, 0, 0, NULL); + modem.etat_4g_lte = ON; + break; + } + + if ((strstr((char *)m_bus.down.reponse.reponse, "+CNACT: 0,2") != NULL) || // Cas où l'AUTOACTIVE est toujours en cours sur PDP #0 mais pas encore activé + (strstr((char *)m_bus.down.reponse.reponse, "+CNACT: 0,0") != NULL)) // Cas où il n'y a aucune demande d'activation sur le PDP #0 + { + if (modem.etat_4g_lte == ON) enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_PERTE_LIAISON_4G_LTE, 0, 0, NULL); + modem.etat_4g_lte = OFF; + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Activation du PDP : + case M_COMMANDE_CNACT_0_X: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) erreur = M_BUS_ERREUR_AUCUNE; // Si la trame contient OK, on la neutralise afin de ne pas mettre fin à l'échange + break; // Utile, car la réponse du modem est en général une tram eOK suivie, bien plus tard, de la réponse proprement dite. + } + + if (strstr((char *)m_bus.down.reponse.reponse, ",ACTIVE") != NULL) modem.sequenceur.compteur_time_out = 0; // Si on a reçu le texte "+APP PDP: 0,ACTIVE", c'est que l'activation a été effective. + if (strstr((char *)m_bus.down.reponse.reponse, ",DEACTIVE") != NULL) modem.sequenceur.compteur_time_out = 0; // Si on a reçu le texte "+APP PDP: 0,DEACTIVE", c'est que l'activation n'est pas possible mais il est inutile d'attendre plus. + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Une réponse a la commande CNACT_0_X été reçue de la part du modem : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Numéro de téléphone : + case M_COMMANDE_CNUM: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture du N° de téléphone reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Recherche de la valeur antérieure : + sprintf(requete, "SELECT Valeur FROM TB_Systeme WHERE Validite=1 AND Nom='No_telephone' "); + mysql_query(bdd.pointeur, requete); // Envoi de la requête à MYSQL + result = mysql_store_result(bdd.pointeur); // Récup des résultats de la requête + while((row = mysql_fetch_row(result))) // Boucle sur tous les résultats de la requête + { + if (strcmp(row[0], (char *)m_bus.down.reponse.reponse) != 0) // Si la nouvelle valeur est différente de la précédente : + { + // Mise à jour du numéro de téléphone de la carte SIM dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='No_telephone' ", m_bus.down.reponse.reponse); + mysql_query(bdd.pointeur, requete); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_CHANGEMENT_NO_TELEPHONE, 0, 0, (char *) m_bus.down.reponse.reponse); + } + } + mysql_free_result(result); //Libération du jeu de résultat + + break; + } + + //------------------------------------------------------------------------------------------------------ + // UE System Information : + case M_COMMANDE_CPSI: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture des informations de l'UE reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + +// int16_t rssi; +// int16_t sinr; + + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + switch(i) // Traitement de chaque paramètre : + { + case 1 : // Champs System_mode : "NO SERVICE", "GSM", "LTE CAT-M1", "LTE NB-IOT". NB : ce n'est pas parce qu'on a une info "positive" sur ce champs que la connxion est active (cf CNACT) + { + // Archivage dans la BDD de l'état du réseau : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Modem_system_mode' ", ptr[i-1]); + mysql_query(bdd.pointeur, requete); + + break; + } + + case 2 : // Champs Operation_mode : + { + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%s' WHERE Nom='Modem_operation_mode' ", ptr[i-1]); + mysql_query(bdd.pointeur, requete); + + break; + } + + case 12 : // Champs RSRP (Current Reference Signal Received Power, en dbm). Cet indicateur est valable uniquement pour LTE mais est plus pertinent que RSSI (qui prend toutes sorte de signaux en compte, yc des signaux inutiles en 4G-LTE). + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_RSRP); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atoi(ptr[i-1]); + + break; + } + + case 13 : // Champs RSSI (Current Received signal strength indicator : en dbm + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_RSSI); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) entrees.predefinies.entrees[val_16].valeur = atoi(ptr[i-1]); + + break; + } + + case 14 : // Champs RSSN (Average reference signal-to-noise ratio of the serving cell. The value of SINR can be calculated according to , the formula is : SINR = 2 * - 20. The range of SINR is from -20 to 30 + { + val_16 = entree_no(TYPE_ENTREES_PREDEFINIES, 0, ENTREE_PREDEFINIE_SINR); + if ((val_16 >= 0) && (val_16 < MAX_NOMBRE_ENTREES_PREDEFINIES)) + { + entrees.predefinies.entrees[val_16].valeur = atoi(ptr[i-1]) * 4; // Permet d'obtenir une valeur entre 0 et 100% + if (entrees.predefinies.entrees[val_16].valeur > 100) entrees.predefinies.entrees[val_16].valeur = 100; + } + + break; + } + + default : ; + } + + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Network Registration : + case M_COMMANDE_CREG: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture des informations d'enregistrement réseau reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + switch(i) // Traitement de chaque paramètre : + { + case 1 : // Unsollicited code mode : 0 Disable network registration unsolicited result code => 1 Enable network registration unsolicited result code +CREG: 2 Enable network registration unsolicited result code with location information(2 is only for 7080 series module which support GPRS.) + { + + break; + } + + case 2 : // Network Regsitration Status : 0 Not registered, MT is not currently searching a new operator to register to; 1 Registered, home network; 2 Not registered, but MT is currently searching a new operator to register to; 3 Registration denied; 4 Unknown; 5 Registered, roaming + { + + break; + } + + default : ; + } + + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Signal Quality Report : + case M_COMMANDE_CSQ: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_GENERALES + sprintf(tmp, "Réponse à la commande de lecture du niveau CSQ reçue : [%s]", (char *)m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + int16_t rssi; + + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + // Traitement de chaque paramètre : + switch(i) + { + case 1 : // Champs RSSI : + { + x = atoi(ptr[i-1]); + + if (x == 0) rssi = -115; // 0 - 115 dBm or less + else if (x == 1) rssi = -111; // 1 - 111 dBm + else if ((x >= 2) && (x <= 30)) rssi = (2 * x) -114; // 2...30 - 110... - 54 dBm + else if ((x >= 31) && (x <= 98)) rssi = -52; // 31 - 52 dBm or greater + else rssi = 99; // 99 not known or not detectable + + // Mise à jour du niveau RSSI dans la BDD : + sprintf(requete, "UPDATE TB_Systeme SET Valeur='%i' WHERE Nom='Rssi' ", rssi); + mysql_query(bdd.pointeur, requete); + + break; + } + + case 2 : // Champs BER : Non traité... + + default : ; + } + + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Ouverture session FTP Get : + case M_COMMANDE_FTPGET_1: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Permet d'ignorer le "OK" qui arrive avant la trame proprement dite + +#ifdef __DEBUG_M_COMMANDES_FTP_GET + sprintf(tmp, "\tRetour de FTPGET 1 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) // Si on reçoit 2 paramètres en réponse à une commande de Mode 1, c'est que la commande a été correctement reçue. + { + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le code de status de la fonction GET... + modem.sequenceur.compteur = MODEM_DOWNLOAD_TEMPO_DEMANDE_DATA; // ... et on force le compteur du modem à cette tempo car il faut attendre un peu (si 0, ça ne fonctionne pas...) mais il est inutile d'aller jusqu'au time-out. + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Attente de la taille du bloc binaire à réceptionner par GET : + case M_COMMANDE_FTPGET_2: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_FTP_GET + sprintf(tmp, "\tRetour de FTPGET 2 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 1) x = atoi(ptr[i-1]); // On récupère le premier digit de la réponse (qui doit être '2' dans 'FTPGET: 2, xxxx' mais qui peut être '1' si on est en fin d'échange aticipé via la réception de 'FTPGET: 1, 0' + + if (i == 2) + { + if (x == 2) // Cas 'normal' de récepetion de 'FTPGET: 2,xxx' + { + download.taille_buffer_rx = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant la taille du buffer à recevoir... + + if (download.taille_buffer_rx != 0) + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE; // Passage du séquenceur du M-Bus en mode binaire... + m_bus.down.reponse.lg_reponse = 0; // ... avec un buffer de réception remis à 0... + m_bus.down.reponse.lg_reponse_attendue = download.taille_buffer_rx; // ... et un nombre d'octets à recevoir défini. Le protocole M-Bus va dorénavant traiter ce nombre d'octets sans se poser de question est déclarera une réponse reçu dès que le nombre de caractères aura été reçu. + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPGET_RECEPTION_DATA; // Et lorsque cette déclaration sera faite, on reviendra dans cette fonction mais dans l'état de séquenceur ad'hoc pour récupérer ces octets. + } + else // Il arrive qu'on reçoive un 'FTPGET: 2,0' dans le flux (notamment si le modem tarde à charger les données du serveur). Dans ce cas, on passe à l'état suivant, qui va renvoyer régulièrement de nouveaux 'FTPGET:2,1024' (pour recevoir de nouveaux blocs s'il y en a) et, in fine, recevra un 'FTPGET: 1,0' indiquant la fin du transfert. + { + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPGET_2_1; + } + } + else if (x == 1) // Cas de réception d'une fin d'échange de type 'FTPGET: 1,x' + { + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le status de 'FTPGET: 1, x'... A priori, 0 indiquant la fin de l'écgange mais si c'est 1, on va continuer après passage dans 'modem.c' + modem.sequenceur.compteur = 2; // A priori, cette tempo est inutile mais dans le doute, elle est laissée... + } + } + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Attente de la fin de transfert GET : + case M_COMMANDE_FTPGET_2_1: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) // On reçoit normalement toujours un Ok (si c'est une autre erreur, on traite l'erreur globalement) + { + download.status_get = 1; // On met donc le statut à jour pour le traitement dans 'modem.c' + if (download.taille_buffer_rx < download.taille_buffer_tx) // Cas d'un paquet qui ne correspond pas au maximum possible : il faut attendre soit le 'FTPGET:1,0' qui annonce la fin du transfert, soit 'FTPGET: 1,1' qui indique qu'on peut demander de nouveaux paquets. + { + // modem.sequenceur.compteur = 1; // Version minimale (1.08Mo en 120s) + modem.sequenceur.compteur = 3; // Version raisonnable (1.08Mo en 131s) + } + else // Si c'est un paquet complet, le modem doit avoir des données disponibles et il est inutile d'attendre avant de demander le suivant. + { + // modem.sequenceur.compteur = 0; // Version minimale (1.08Mo en 120s) + modem.sequenceur.compteur = 1; // Version raisonnable (1.08Mo en 131s) + } + } + break; + } + + +#ifdef __DEBUG_M_COMMANDES_FTP_GET + sprintf(tmp, "\tRetour de FTPGET 1 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) // Si on reçoit 2 paramètres en réponse à une commande de Mode 1, c'est que la commande a été correctement reçue. + { + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le code de status de la fonction GET... + modem.sequenceur.compteur = MODEM_DOWNLOAD_TEMPO_DEMANDE_DATA; // ... et on force le compteur du modem à cette tempo car il faut attendre un peu (si 0, ça ne fonctionne pas...) mais il est inutile d'aller jusqu'au time-out. + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture du contenu d'un répertoire distant FTPLIST : + case M_COMMANDE_FTPLIST_1: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; + +#ifdef __DEBUG_M_COMMANDES_FTP_LIST + sprintf(tmp, "\tRetour de FTPLIST 1 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) + { + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le code de status de la fonction GET... + modem.sequenceur.compteur = MODEM_DOWNLOAD_TEMPO_DEMANDE_LISTE; // ... et on force le compteur du modem à cette tempo car il faut attendre un peu (si 0, ça ne fonctionne pas...!) mais il est inutile d'aller jusqu'au time-out. + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture du contenu d'un répertoire distant FTPLIST : + case M_COMMANDE_FTPLIST_2: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_FTP_LIST + sprintf(tmp, "\tRetour de FTPLIST 2 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 1) x = atoi(ptr[i-1]); // On récupère le premier digit de la réponse (qui doit être '2' dans 'FTPGET: 2, xxxx' mais qui peut être '1' si on est en fin d'échange aticipé via la réception de 'FTPGET: 1, 0' + + if (i == 2) + { + if (x == 2) // Cas 'normal' de réception de 'FTPGET: 2,xxx' + { + download.taille_buffer_rx = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant la taille du buffer à recevoir... + + if (download.taille_buffer_rx != 0) + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE; // Passage du séquenceur du M-Bus en mode binaire... + m_bus.down.reponse.lg_reponse = 0; // ... avec un buffer de réception remis à 0... + m_bus.down.reponse.lg_reponse_attendue = download.taille_buffer_rx; // ... et un nombre d'octets à recevoir défini. Le protocole M-Bus va dorénavant traiter ce nombre d'octets sans se poser de question est déclarera une réponse reçu dès que le nombre de caractères aura été reçu. + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPLIST_RECEPTION_LISTE; // Et lorsque cette déclaration sera faite, on reviendra dans cette fonction mais dans l'état de séquenceur ad'hoc pour récupérer ces octets. + } + else // Il arrive qu'on reçoive un 'FTPGET: 2,0' dans le flux (notamment si le modem tarde à charger les données du serveur). Dans ce cas, on passe à l'état suivant, qui va renvoyer régulièrement de nouveaux 'FTPGET:2,1024' (pour recevoir de nouveaux blocs s'il y en a) et, in fine, recevra un 'FTPGET: 1,0' indiquant la fin du transfert. + { + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPLIST_2_1; + } + } + else if (x == 1) // Cas de réception d'une fin d'échange de type 'FTPGET: 1,x' + { + + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le status de 'FTPGET: 1, x'... A priori, 0 indiquant la fin de l'écgange mais si c'est 1, on va continuer après passage dans 'modem.c' + + sprintf(tmp, "\tRetour de FTPLIST 2 = '%s' interpreté comme un FTPLIST1:x où x=%i", m_bus.down.reponse.reponse, download.status_get); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + modem.sequenceur.compteur = 2; // A priori, cette tempo est inutile mais dans le doute, elle est laissée... + } + } + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Attente de la fin de transfert LIST : + case M_COMMANDE_FTPLIST_2_1: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) // On reçoit normalement toujours un Ok (si c'est une autre erreur, on traite l'erreur globalement) + { + download.status_get = 1; // On met donc le statut à jour pour le traitement dans 'modem.c' + if (download.taille_buffer_rx < download.taille_buffer_tx) // Cas d'un paquet qui ne correspond pas au maximum possible : il faut attendre soit le 'FTPGET:1,0' qui annonce la fin du transfert, soit 'FTPGET: 1,1' qui indique qu'on peut demander de nouveaux paquets. + { + modem.sequenceur.compteur = 40; // Cette tempo est 'importante' mais la lecture LIST est toujours de petite taille et cela permet de limiter les 'relances' après la dernère data et en attendant le 'FTPLIST: 1,0' de fin d'échange. + } + else // Si c'est un paquet complet, le modem doit avoir des données disponibles et il est inutile d'attendre avant de demander le suivant. + { + modem.sequenceur.compteur = 1; // Et on met une petite tempo entre le OK reçu et la suite (a priori, une nouvelle demande 'FTPLIST: 2,1024') + } + } + break; + } + + +#ifdef __DEBUG_M_COMMANDES_FTP_LIST + sprintf(tmp, "\tRetour de FTPLIST 2_1 = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) + { + download.status_get = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant le code de status de la fonction GET... + modem.sequenceur.compteur = MODEM_DOWNLOAD_TEMPO_DEMANDE_LISTE; // ... et on force le compteur du modem à cette tempo car il faut attendre un peu (si 0, ça ne fonctionne pas...!) mais il est inutile d'aller jusqu'au time-out. + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Ouverture session FTP Put : + case M_COMMANDE_FTPPUT_1: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) erreur = M_BUS_ERREUR_AUCUNE; // Si la trame contient OK, on la neutralise afin de ne pas mettre fin à l'échange + break; // Utile, car la réponse du modem est en général une trame OK suivie, bien plus tard, de la réponse proprement dite. + } + +#ifdef __DEBUG_M_COMMANDES_FTP_PUT + sprintf(tmp, "\tRetour de FTPPUT = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 3) // Si on reçoit 3 paramètres en réponse à une commande de Mode 1, c'est que la commande a été correctement reçue. + { + modem.max_taille_ftp_put = atoi(ptr[i-1]); // On stocke donc ce 3ème paramètre comme étant la nombre d'octet recevable par le modem... + modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_DEMANDE_ENVOI_DATA; // ... et on laisse juste une petite tempo sur le compteur du modem car il est inutile d'attendre plus (mais pas moins !) + } + ptr[i] = strtok(NULL, ","); + } + + if ((i-1) < 3) // (i-1) représente le nombre de paramètres reçus au total. S'il n'est pas égal à 3, c'est qu'il y a eu une erreur. + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_MODEM_ERREUR_FTP, 0, atoi(ptr[1]), (char *) m_bus.down.reponse.reponse); + + modem.max_taille_ftp_put = 0; // Permet d'arrêter le transfert... + modem.sequenceur.compteur = 0; // ... sans attendre plus longtemps + } + + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Demande d'écriture de données FTP Put : + case M_COMMANDE_FTPPUT_2: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; // Utile, notamment lorsqu'il y a une trame OK qui passe... + +#ifdef __DEBUG_M_COMMANDES_FTP_PUT + sprintf(tmp, "\tRetour de FTPPUT = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) // Si on reçoit 2 paramètres en réponse à la commande, c'est que la commande a été correctement reçue. + { + modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_DEMANDE_ENVOI_DATA; // On laisse juste une petite tempo sur le compteur du modem car il est inutile d'attendre plus (mais pas moins !) + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande ne comprend pas de trame CR/LF/ O K CR/LF. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture de la taille d'un fichier FTPSIZE : + case M_COMMANDE_FTPSIZE: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) erreur = M_BUS_ERREUR_AUCUNE; // Si la trame contient OK, on la neutralise afin de ne pas mettre fin à l'échange + break; // Utile, car la réponse du modem est en général une trame OK suivie, bien plus tard, de la réponse proprement dite. + } + +#ifdef __DEBUG_M_COMMANDES_FTP_PUT + sprintf(tmp, "\tRetour de FTPSIZE = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); // Positionnement sur le ':' + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 2) // Doit être à 0 si pas d'erreur, sinon code d'erreur + { + upload.status_get_size = atoi(ptr[i-1]); + if (upload.status_get_size != 0) // Si cette valeur est != 0, il est inutile de continuer... + { + modem.sequenceur.compteur = 0; + break; + } + } + + if (i == 3) // Si le second paramètre était bien 0, ce 3ième contient la taille du fichier : + { + upload.taille_fichier_serveur = atoi(ptr[i-1]); // On stocke donc ce 3ème paramètre comme étant la taille du fichier distant + modem.sequenceur.compteur = 0; // ... et on force le compteur du modem à 0 car il est inutile d'attendre plus. + } + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + + //------------------------------------------------------------------------------------------------------ + // Attente de la taille du bloc de ecaractères à réceptionner par SHREAD : + case M_COMMANDE_SHREAD: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) erreur = M_BUS_ERREUR_AUCUNE; // Si la trame contient OK, on la neutralise afin de ne pas mettre fin à l'échange + break; // Utile, car la réponse du modem est en général une trame OK suivie, bien plus tard, de la réponse proprement dite. + } + +#ifdef __DEBUG_M_COMMANDES_FTP_GET + sprintf(tmp, "\tRetour de SHREAD = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 1) + { + modem.http.taille_buffer_rx = atoi(ptr[i-1]); // On stocke donc ce 2ème paramètre comme étant la taille du buffer à recevoir... + + if (modem.http.taille_buffer_rx != 0) + { + m_bus.protocole_rx.sequenceur.etat = ETAT_SEQUENCEUR_M_BUS_PROTOCOLE_RX_FLUX_BINAIRE; // Passage du séquenceur du M-Bus en mode binaire... + m_bus.down.reponse.lg_reponse = 0; // ... avec un buffer de réception remis à 0... + m_bus.down.reponse.lg_reponse_attendue = modem.http.taille_buffer_rx; // ... et un nombre d'octets à recevoir défini. Le protocole M-Bus va dorénavant traiter ce nombre d'octets sans se poser de question est déclarera une réponse reçu dès que le nombre de caractères aura été reçu. + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_HTTP_GET_RECEPTION_DATA; // Et lorsque cette déclaration sera faite, on reviendra dans cette fonction mais dans l'état de séquenceur ad'hoc pour récupérer ces octets. + } + } + ptr[i] = strtok(NULL, ","); + } + } + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Requête GET en mode HTTP : + case M_COMMANDE_SHREQ: + { + if (erreur != M_BUS_ERREUR_AUCUNE) + { + if (erreur == M_BUS_ERREUR_OK) erreur = M_BUS_ERREUR_AUCUNE; // Si la trame contient OK, on la neutralise afin de ne pas mettre fin à l'échange + break; // Utile, car la réponse du modem est en général une trame OK suivie, bien plus tard, de la réponse proprement dite. + } + +#ifdef __DEBUG_M_COMMANDES_HTTP + sprintf(tmp, "\tRetour de SHREQ = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 1) // Doit contenir '"GET"' + { + if (strstr(ptr[i-1], "GET") == NULL) // S'il ne contient pas GET, c'est une erreur.... + { + modem.http.status_get = -1; + modem.sequenceur.compteur = 0; // Inutile d'attendre + } + } + + if (i == 2) + { + modem.http.status_get = atoi(ptr[i-1]); // Stocke le status de retour de la requête GET +//sprintf(tmp, "\tLe deuxième paramètre de retour de SHREQ est le status de la requête : [%i]", modem.http.status_get); +//affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + + if (i == 3) + { + modem.http.taille_retour_get = atoi(ptr[i-1]); // Stocke le status de retour de la requête GET.... +modem.sequenceur.compteur = 2; //MODEM_DOWNLOAD_TEMPO_DEMANDE_LISTE; // ... et on force le compteur du modem à cette tempo + +//sprintf(tmp, "\tLe troisième paramètre de retour de SHREQ est la tailel de la réponse : [%i] octets", modem.http.taille_retour_get); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Retour d'état de la connexion HTTP par la commande SHSTATE? : + case M_COMMANDE_SHSTATE: + { + if (erreur != M_BUS_ERREUR_AUCUNE) break; + +#ifdef __DEBUG_M_COMMANDES_HTTP + sprintf(tmp, "\tRetour de SHSTATE? = '%s'", m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 1) + { + modem.http.status_connexion = atoi(ptr[i-1]); // Stocke le status de la connexion ... +modem.sequenceur.compteur = 2; //MODEM_DOWNLOAD_TEMPO_DEMANDE_LISTE; // ... et on force le compteur du modem à cette tempo + } + + ptr[i] = strtok(NULL, ","); + } + } + + erreur = M_BUS_ERREUR_OK; // Le retour de cette commande comprend bien une trame CR/LF/ O K CR/LF mais AVANT la réponse proprement dite. Donc on "simule" l'arrivée de cette trame pour que le protocole soit repositionné au repos. + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Ecriture des données FTP Put : + case M_COMMANDE_FTPPUT_ENVOI_DATA: + { + if (erreur != M_BUS_ERREUR_AUCUNE) // ATTENTION : la trame de retour normale est '\r\nOK\n\n+FTPPUT: 1,1,1360\r\n' (c'est bien un double \n\n au lieu de \r\n au milieu) donc le protocole compte une seule trame. Il ne faut donc pas traiter le Ok comme tel sinon on pert le FTPPUT qui suit... NB : en cas d'erreur, on a l'erreur d'abord (avec ses \r\n de part et d'autre) puis le FTPPUT:... complet. + { + upload.code_erreur_ftpput_data = erreur; // Mémorisation du code d'erreur pour traitement ultérieur par modem.c + if (erreur == M_BUS_ERREUR_ERROR) // Cf le 'ATTENTION' ci-dessus : si ERROR, on peut faire break car le FTPPUT qui suit sera bien encadré comme il faut par les \r\n + { + erreur = M_BUS_ERREUR_AUCUNE; // On repositionne l'erreur à AUCUNE pour que l'échange ne soit pas terminé (on attend le FTPPUT avant de revenirt à modem.c) + break; // On peut alors sortir du traitement car il ne servirait à rien à ce stade (on attend le FTPPUT...) + } + } + + ptr_chaine = strstr((char *)m_bus.down.reponse.reponse, ": "); + + if (ptr_chaine != NULL) // Si NULL, c'est qu'il n'y a pas de ':' donc la trame n'est pas exploitable.... + { + ptr_chaine += 2; // Le '+2' permet de démarrer la 'ptr_chaine' après le ": ". + + ptr[0] = strtok(ptr_chaine, ","); // Extraction du premier segment avant le premier ',' + for (i=1; ((i < M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE) && (ptr[(i-1)] != NULL)); i++) + { + if (i == 3) // Si on reçoit 3 paramètres en réponse, c'est que la commande a été correctement reçue.... + { + modem.max_taille_ftp_put = atoi(ptr[i-1]); // ... on stocke ce 3ème paramètre comme étant la nombre d'octet recevable par le modem... + modem.sequenceur.compteur = MODEM_UPLOAD_TEMPO_DEMANDE_ENVOI_DATA; // ... et on laisse juste une petite tempo sur le compteur du modem car il est inutile d'attendre plus (mais pas moins !) + erreur = M_BUS_ERREUR_OK; // ... et on force la fin de l'échange car le OK n'est pas présent dans la trame de FTPPUT + } + + ptr[i] = strtok(NULL, ","); // Extraction du segment suivant + } + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture d'un bloc de données FTP List : + case M_COMMANDE_FTPLIST_RECEPTION_LISTE: + { + if (m_bus.down.reponse.lg_reponse == m_bus.down.reponse.lg_reponse_attendue) + { + sprintf(tmp, "%s/%s", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); + download.ptr_fichier_destination = fopen(tmp, "a"); // Ouverture du fichier en "append"... + if (download.ptr_fichier_destination != NULL) + { + for (i = 0; i < m_bus.down.reponse.lg_reponse; i ++) + { + fputc(m_bus.down.reponse.reponse[i], download.ptr_fichier_destination); // Ecriture dans le fichier de tous les caractères reçus dans le paquet + } + fseek(download.ptr_fichier_destination, 0, SEEK_END); // On profite du fait que le fichier local est ouvet pour aller... + download.taille_fichier_local = ftell(download.ptr_fichier_destination); // ... et rechercher la position du curseur => mise à jour la taille réelle du fichier local + + fclose(download.ptr_fichier_destination); // Fermeture du fichier + } + else + { + sprintf(tmp, "L'ouverture du fichier [%s/%s] a échoué. Le bloc de données reçu est perdu.", REPERTOIRE_SERVER_DOWNLOAD, NOM_FICHIER_LISTING_REPERTOIRE_DOWNLOAD_SERVEUR); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPLIST_2_1; // Pour attente du 'OK' qui suit le paquet de data puis le 'FTPLIST: 1,0' de fin de transfert + erreur = M_BUS_ERREUR_AUCUNE; + } + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Lecture d'un bloc de données FTP Get : + case M_COMMANDE_FTPGET_RECEPTION_DATA: + { + if (m_bus.down.reponse.lg_reponse == m_bus.down.reponse.lg_reponse_attendue) + { + sprintf(tmp, "%s%s", download.nom_repertoire_destination, download.nom_fichier_destination); + download.ptr_fichier_destination = fopen(tmp, "a"); // Ouverture du fichier en "append"... + if (download.ptr_fichier_destination != NULL) + { + for (i = 0; i < m_bus.down.reponse.lg_reponse; i ++) + { + fputc(m_bus.down.reponse.reponse[i], download.ptr_fichier_destination); // Ecriture dans le fichier de tous les caractères reçus dans le paquet + } + fseek(download.ptr_fichier_destination, 0, SEEK_END); // On profite du fait que le fichier local est ouvet pour aller... + download.taille_fichier_local = ftell(download.ptr_fichier_destination); // ... et rechercher la position du curseur => mise à jour la taille réelle du fichier local + + fclose(download.ptr_fichier_destination); // Fermeture du fichier + } + else + { + sprintf(tmp, "L'ouverture du fichier [%s%s] a échoué. Le bloc de données reçu est perdu.", download.nom_repertoire_destination, download.nom_fichier_destination); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_FTPGET_2_1; // Pour attente du 'OK' qui suit le paquet de data puis le 'FTPGET: 1,0' de fin de transfert + erreur = M_BUS_ERREUR_AUCUNE; + } + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Lecture d'un bloc de données HTTP Get : + case M_COMMANDE_HTTP_GET_RECEPTION_DATA: + { + if (m_bus.down.reponse.lg_reponse == m_bus.down.reponse.lg_reponse_attendue) + { + sprintf(tmp, "%s/%s", modem.http.nom_repertoire_destination, modem.http.nom_fichier_destination); + modem.http.ptr_fichier_destination = fopen(tmp, "a"); // Ouverture du fichier en "append"... + if (modem.http.ptr_fichier_destination != NULL) + { + for (i = 0; i < m_bus.down.reponse.lg_reponse; i ++) + { + fputc(m_bus.down.reponse.reponse[i], modem.http.ptr_fichier_destination); // Ecriture dans le fichier de tous les caractères reçus dans le paquet + } + fseek(modem.http.ptr_fichier_destination, 0, SEEK_END); // On profite du fait que le fichier local est ouvet pour aller... + modem.http.taille_fichier_local = ftell(modem.http.ptr_fichier_destination); // ... et rechercher la position du curseur => mise à jour la taille réelle du fichier local + + fclose(modem.http.ptr_fichier_destination); // Fermeture du fichier + + modem.http.no_paquet ++; + } + else + { + sprintf(tmp, "L'ouverture du fichier [%s%s] a échoué. Le bloc de données reçu est perdu et le transfert HTTP GET arrêté.", modem.http.nom_repertoire_destination, modem.http.nom_fichier_destination); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + erreur = M_BUS_ERREUR_ERROR; + } + + if (modem.http.taille_retour_get == modem.http.taille_fichier_local) + { + modem.sequenceur.compteur = 0; // il est inutile d'attendre plus + erreur = M_BUS_ERREUR_OK; // On termine l'échange (sans erreur) pour recenir vers modem.c + + } + else + { +#ifdef __DEBUG_M_COMMANDES_HTTP + sprintf(tmp, "Le paquet n° %i en retour de la requête HTTP GET a été reçu. Taille du paquet = %i octets, %i octets reçus depuis le début du transfert pour taille objectif = %i octets.", modem.http.no_paquet, m_bus.down.reponse.lg_reponse_attendue, modem.http.taille_fichier_local, modem.http.taille_retour_get); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_SHREAD; // Pour attente du 'SHREAD: ' suivant + + erreur = M_BUS_ERREUR_AUCUNE; // On ne revient pas encore vers modem.c puisque d'autres blocs sont à recevoir + } + } + + break; + } + + + //------------------------------------------------------------------------------------------------------ + // Lecture des data d'un message SMS : + case M_COMMANDE_CMGL_RECEPTION_DATA_MESSAGE: + { + m_bus.down.reponse.reponse[m_bus.down.reponse.lg_reponse] = 0; + + if (strlen((char *) m_bus.down.reponse.reponse) < (MODEM_MAX_TAILLE_MESSAGE_SMS - 1)) + { +// strcpy(modem.sms_en_cours.message, (char *) m_bus.down.reponse.reponse); // Récupération du texte du message + +// enregistrement_sms(modem.sms_en_cours.numero_emetteur, modem.sms_en_cours.date, modem.sms_en_cours.message); // Archivage du message dans la BDD + +// sprintf(tmp, "SMS reçu de [%s] à [%s] avec le message : [%s]", modem.sms_en_cours.numero_emetteur, modem.sms_en_cours.date, modem.sms_en_cours.message); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + +//if (strstr(modem.sms_en_cours.message, "RESET_NOW") != NULL) c_bus_down_commande_reset(); + + + } + else + { +#ifdef __DEBUG_M_COMMANDES_SMS + sprintf(tmp, "La longueur des data du message reçu est trop importante (%i). Le SMS ne peut pas être traité. Message = [%s]", strlen((char *) m_bus.down.reponse.reponse), m_bus.down.reponse.reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); +#endif + } + + sprintf(tmp, "AT+CMGD=%i", modem.sms_en_cours.index); + m_bus_down_commande_libre(tmp, M_BUS_PROTOCOLE_NORMAL, 0, -1); // Commande de suppression du SMS dans le modem + + + m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code = M_COMMANDE_CMGL; // Pour attente du 'OK' qui suit le paquet de data de fin de transfert + + break; + } + + //------------------------------------------------------------------------------------------------------ + // Autres commandes : + default: + { +// sprintf(tmp, "\tTrame de réponse reçue sur M-Bus = '%s'", m_bus.down.reponse.reponse); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + break; + } + } + + + + //------------------------------------------------------------------------------------------------------ + // Traitement du code d'erreur détecté dans la commande : + //------------------------------------------------------------------------------------------------------ + switch (erreur) + { + case M_BUS_ERREUR_AUCUNE : break; // La trame contenait des valeurs utiles et n'étaient pas une trame de fin d'échange. Les compteurs de time-out et de répétitions continuent de tourner librement. + + default: // Cas où on a reçu un code d'erreur connu : + { + if (m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].mode_protocole == M_BUS_PROTOCOLE_DATA_BINAIRE) + { + sprintf(tmp, "Réponse à la commande (code = %i) en erreur (contenu non affiché pour éviter les dépassements de buffers)", m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].code); + } + else sprintf(tmp, "Réponse à la commande [%s] en erreur : [%s] ", m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde, m_bus.down.reponse.reponse); + +// sprintf(tmp, "Réponse à la commande ["); +// strncat(tmp, (char *)m_bus.down.buffer_cdes[m_bus.down.ptr_debut_buffer_cdes].cde, 10); +// strcat(tmp, "...] en erreur : ["); +// strncat(tmp, (char *)m_bus.down.reponse.reponse, 10); +// strcat(tmp, "...]"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } // Pas de break volontairement.... + + case M_BUS_ERREUR_OK : // Si le code est "OK", c'est juste la fin des réponses à la commande + { + 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. + break; + } + } + + m_bus.down.reponse.lg_reponse = 0; // Repositionne le pointeur au début pour le traitement de la trame suivante. A faire dans tous les cas !! + + return(0); +} + + +/***************************************************************************************************************************************************************** + Fonction : Analyse la réponse à la commande et recherche si un code d'erreur parmi la liste possible est présent. + Reçoit : Rien (traite la réponse qui est stockée dans la structure m_bus.down.reponse.xxxx) + Renvoit : -1 si aucun code erreur n'a été trouvé, la valeur du coe erreur sinon. Attention la chaine "OK" est considérée comme un code erreur (valeur 0) +******************************************************************************************************************************************************************/ +int8_t m_bus_down_recherche_code_erreur(void) +{ + + if (strstr((char *)m_bus.down.reponse.reponse, "OK") != NULL) return(M_BUS_ERREUR_OK); + else if (strstr((char *)m_bus.down.reponse.reponse, "BUSY") != NULL) return(M_BUS_ERREUR_BUSY); + else if (strstr((char *)m_bus.down.reponse.reponse, "CONNECT") != NULL) return(M_BUS_ERREUR_CONNECT); + else if (strstr((char *)m_bus.down.reponse.reponse, "ERROR") != NULL) return(M_BUS_ERREUR_ERROR); + else if (strstr((char *)m_bus.down.reponse.reponse, "NO ANSWER") != NULL) return(M_BUS_ERREUR_NO_ANSWER); + else if (strstr((char *)m_bus.down.reponse.reponse, "NO CARRIER") != NULL) return(M_BUS_ERREUR_NO_CARRIER); + else if (strstr((char *)m_bus.down.reponse.reponse, "NO DIALTONE") != NULL) return(M_BUS_ERREUR_NO_DIALTONE); + else if (strstr((char *)m_bus.down.reponse.reponse, "PROCEEDING") != NULL) return(M_BUS_ERREUR_PROCEEDING); + else if (strstr((char *)m_bus.down.reponse.reponse, "RING") != NULL) return(M_BUS_ERREUR_RING); + else return(M_BUS_ERREUR_AUCUNE); +} + + diff --git a/sources/m_commandes.h b/sources/m_commandes.h new file mode 100644 index 0000000..c82f903 --- /dev/null +++ b/sources/m_commandes.h @@ -0,0 +1,68 @@ +//********************************************************************************************************************************************************************************** +// Fichier M_COMMANDES_.H +// +// Contient toutes définitions de constantes liées au commandes sur M_Bus +//**********************************************************************************************************************************************************************************/ + + + // Valeur maximales +#define M_COMMANDE_MAX_LONGUEUR_NOM_CDE 50 +#define M_COMMANDE_MAX_NOMBRE_PARAMETRES_REPONSE 20 +#define M_COMMANDE_MAX_LONGUEUR_PARAMETRE_REPONSE 100 + + // Codes commandes : +#define M_COMMANDE_INCONNUE 0 +#define M_COMMANDE_ATE 1 +#define M_COMMANDE_CCID 2 +#define M_COMMANDE_CFUN 3 +#define M_COMMANDE_CGMR 4 +#define M_COMMANDE_CGNAPN 5 +#define M_COMMANDE_CGNSINF 6 +#define M_COMMANDE_CGSN 7 +#define M_COMMANDE_CIMI 8 +#define M_COMMANDE_CMGL 9 +#define M_COMMANDE_CNACT 10 +#define M_COMMANDE_CNACT_0_X 11 +#define M_COMMANDE_CNUM 12 +#define M_COMMANDE_CPSI 13 +#define M_COMMANDE_CREG 14 +#define M_COMMANDE_CSQ 15 +#define M_COMMANDE_FTPGET_1 16 +#define M_COMMANDE_FTPGET_2 17 +#define M_COMMANDE_FTPGET_2_1 18 +#define M_COMMANDE_FTPLIST_1 19 +#define M_COMMANDE_FTPLIST_2 20 +#define M_COMMANDE_FTPLIST_2_1 21 +#define M_COMMANDE_FTPSIZE 22 +#define M_COMMANDE_FTPPUT_1 23 +#define M_COMMANDE_FTPPUT_2 24 +#define M_COMMANDE_SHREAD 25 +#define M_COMMANDE_SHREQ 26 +#define M_COMMANDE_SHSTATE 27 + +#define M_COMMANDE_FTPPUT_ENVOI_DATA 50 // Commande envoyée en binaire +#define M_COMMANDE_FTPLIST_RECEPTION_LISTE 51 // Réception d'un bloc de données (a priori ASCII mais pas forcément) contenant la liste lue +#define M_COMMANDE_FTPGET_RECEPTION_DATA 52 // Réception d'un bloc de données binaires contenant un bloc du fichier en cours de download +#define M_COMMANDE_HTTP_GET_RECEPTION_DATA 53 // Réception d'un bloc de données en HTTP suite à unr requête GET +#define M_COMMANDE_CMGL_RECEPTION_DATA_MESSAGE 54 // Réception du bloc de data du message reçu + + + + // Codes d'erreur (cf commande ATV, page 27) : +#define M_BUS_ERREUR_AUCUNE -1 +#define M_BUS_ERREUR_OK 0 +#define M_BUS_ERREUR_CONNECT 1 +#define M_BUS_ERREUR_RING 2 +#define M_BUS_ERREUR_NO_CARRIER 3 +#define M_BUS_ERREUR_ERROR 4 // Pas de 5.... +#define M_BUS_ERREUR_NO_DIALTONE 6 +#define M_BUS_ERREUR_BUSY 7 +#define M_BUS_ERREUR_NO_ANSWER 8 +#define M_BUS_ERREUR_PROCEEDING 9 + + + + + // Etat de décodage des réponses +#define M_BUS_REPONSE_ETAT_REPOS 0 +#define M_BUS_REPONSE_ETAT_ATTENTE_PDU 1 \ No newline at end of file diff --git a/sources/main.c b/sources/main.c new file mode 100644 index 0000000..33367ce --- /dev/null +++ b/sources/main.c @@ -0,0 +1,387 @@ +//*********************************************************************************************************************************************************************************** +// 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. +} + + diff --git a/sources/main.h b/sources/main.h new file mode 100644 index 0000000..1ea0258 --- /dev/null +++ b/sources/main.h @@ -0,0 +1,1600 @@ +// ********************************************************************************************************************************************************************************** +// Fichier MAIN.H +// +// Contient les principaux éléments à inclure : +// - fichiers INCLUDE +// - principaux DEFINE +// - les PROTOTYPAGES de fonction en EXTERN (les prototypes eux-mêmes sont à mettre dans le fichier contenant la fonction) +// - les DECLARATIONS de variables en EXTERN (les déclarations elles-mêmes sont à mettre dans le fichier *.c "principal" - dans le doute, dans MAIN.C) +// +// Ce fichier est destiné à être inclus dans TOUS les fichiers *.c du projet. +// ********************************************************************************************************************************************************************************** + + +// ********************************************************************************************************************************************************************************** +// Include : +// ********************************************************************************************************************************************************************************** + +#include +#include +#include +#include +#include +#include +#include // Nécessaire pour E/S GPIO sur CM5 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio.h" +#include "batteries.h" +#include "bdd.h" +#include "c_bus.h" +#include "c_commandes.h" +#include "capteurs.h" +#include "circuits.h" +#include "debug.h" +#include "ecran.h" +#include "entrees.h" +#include "events.h" +#include "fonctions.h" +#include "g_bus.h" +#include "g_commandes.h" +#include "gnss.h" +#include "hardware.h" +#include "localisation.h" +#include "m_bus.h" +#include "m_commandes.h" +#include "modem.h" +#include "nmea.h" +#include "reseaux.h" +#include "reservoirs.h" +#include "serial.h" +#include "sorties.h" +#include "systeme.h" +#include "usb.h" + + + +// ********************************************************************************************************************************************************************************** +// Define : +// ********************************************************************************************************************************************************************************** + + // Général : +#define OFF 0 +#define ON 1 + +#define FALSE 0 +#define TRUE 1 + +#define HIGH 1 +#define LOW 0 + +#define CLEAR 0 +#define SET 1 + +#define ENTREE 0 +#define SORTIE 1 + + // Logique d'alarme : +#define LOGIQUE_ALARME_NEGATIVE -1 // L'alerte/alarme est déclenchée lorsque la valeur mesurée passe en-dessous du seuil bas. +#define LOGIQUE_ALARME_INTERVALLE 0 // L'alerte/alarme est déclenchée lorsque la valeur mesurée passe en-dessous du seuil bas ou au-dessus du seuil haut.. +#define LOGIQUE_ALARME_POSITIVE 1 // L'alerte/alarme est déclenchée lorsque la valeur mesurée passe en-dessus du seuil haut. +#define LOGIQUE_ALARME_AUCUNE 2 // Aucune alerte/alarme n'est déclenchée + + +// ********************************************************************************************************************************************************************************** +// Structures : +// ********************************************************************************************************************************************************************************** + +struct Struct_date +{ + uint8_t jour; + uint8_t mois; + uint16_t annee; +}; +struct Struct_heure +{ + uint8_t heures; + uint8_t minutes; + uint8_t secondes; +}; +struct Struct_sequenceur +{ + uint8_t etat; + uint16_t compteur; + uint16_t compteur_time_out; +}; +struct Struct_valeur_niveau +{ + bool flag_validite; // Indique si ce point est valide ou pas. + float valeur; // Valeur exprimée dans la même unité que la variable concernée (donc dépend du 'Type_entrées'). + float niveau; // Niveau, entre 0 et 100% +}; +struct Struct_variable +{ + uint16_t cle_primaire; // Clé primaire dans TB_Variables + float marge; // Marge provenant de TB_Variables +}; +struct Struct_variables +{ + float valeur_courante; // Valeurs courante (filtrées mais pas forcément stockées en BDD) + float valeur_bdd; // Dernières valeurs stockées en BDD + float last_mesure; // Dernières valeurs stockées en BDD dans TB_Mesures_batteries + double cumul; // Cumul des variables (intégration) +}; + + + + + //--------------------------------------------------------------------------------------------- + // Audio.c : + //--------------------------------------------------------------------------------------------- +struct Struct_boucle_audio +{ + char nom_fichier[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier MP3 en cours d'utilisation + uint16_t nb_repetitions; // Nb de répétitions du son à chaque boucle + uint16_t periode_boucles; // Période en x100ms de chaque boucle + uint16_t nb_boucles; // Nb de boucles demandées + uint16_t compteur_boucles; // Compteur utilisé pour décompter le nb de boucles restant à exécuter +}; +struct Struct_audio +{ + float consigne_volume; // En % (0 à 100.00). Valeur issue de 'TB_Configuration.Consigne_volume' + uint16_t sortie_gpio_cm_do_audio_sd; // Prend la valeur de CM3P_DO_AUDIO_SD ou CM4_DO_AUDIO_SD selon la génération de ShipBase + uint16_t entree_gpio_cm_di_jack_detect; // Prend la valeur de CM4_DI_JACK_DETECT (ou 0 si CM3+ sur SBV1) selon la génération de ShipBase + uint8_t etat_presence_jack; // Indique si une prise jack est présente sur la sortie AUDIO (1) ou absente (0). Cette valeur est répliquée dans 'TB_Systeme.Presence_jack' + uint8_t fonction_jack; // Copie du champ 'TB_Configuration.Fonction_jack'. Indique l'effet de la présence d'une prise jack sur la sortie audio. 0= le son est coupé sur le HP quand la prise jack est présente, 1=le son est maintenu sur HP quand la prise jack est présente. + struct Struct_boucle_audio boucle; + struct Struct_sequenceur sequenceur; +}; + + //--------------------------------------------------------------------------------------------- + // Batteries.c : + //--------------------------------------------------------------------------------------------- +struct Struct_batterie +{ + uint16_t cle_primaire; // Valeur de Clé Primaire dans TB_Reservoirs + bool flag_validite; + int8_t etat_systeme; // 0: fonctionnement normal, -1: incohérence dans la config, -2: module physique affecté non disponible (absent ou type incompatible) + + uint8_t adresse_module; // Indique l'adresse K-Bus du module sur laquelle sont positionnées l'entrée de la batterie (entre 0 et MAX_NOMBRE_MODULES-1). + uint8_t type_entrees; // '0=Non défini, 3= Voltage Input, 5=Prédéfni, 6=IBS. Les autres valeurs sont invalides. + uint16_t no_entrees; // Stocke le numéro de 'entrees.volt/predefini.entrees[]' selon type_entrees. + + uint8_t type; // Liste de valeurs (image du champs 'Typ' dans TB_Batteries. Attention à ne pas confondre avec 'type_entrees' qui n'a rien à voir... Indique le type de batteries constituant le pack. 0=Non défini, 1=Pb – acide liquide, 2=Pb – AGM, 3=Pb – Gel, 4=Pb – Carbone, 10=Lithium + uint8_t tension_nominale; // 12 ou 24 (en Volts). + float capacite; // Indique, en Ah, la capacité du pack de batteries + float heures_capacite; // Durée pour laquelle la capacité est donnée + float coeff_peukert; // Coefficient de Peukert de la batterie. Toujours >=1. + float tension_pleine_charge; // Tension au-dessus de laquelle la batterie sera considérée comme complètement chargée (sous réserve d’autres paramètres et de durée). Exprimée en V. + float courant_pleine_charge; // Courant au-dessous duquel la batterie sera considérée comme complètement chargée (sous réserve d’autres paramètres et de durée). Exprimée en A. + uint16_t temporisation_pleine_charge; // Durée pendant laquelle tous les paramètres de validation de pleine charge doivent perdurer pour qu’un SOC de 100% soit déclaré. Exprimée en secondes. + float tension_charge_nulle; // Tension en-dessous de laquelle la batterie sera considérée comme complètement déchargée (sous réserve d’autres paramètres et de durée). Exprimée en V. + uint16_t temporisation_charge_nulle; // Durée pendant laquelle tous les paramètres de validation de charge nulle doivent perdurer pour qu’un SOC de 0% soit déclaré. Exprimée en secondes. + float resistance_interne; // Résistance interne de la batterie, à 25°C et plutôt pour des SOC faibles. Exprimée en mOhms. + float efficacite_charge; // Part d’énergie stockée dans la batterie vs l’énergie fournie à la batterie pendant la charge. Valeur entre ]0; 1]. + float efficacite_decharge; // Part de l’énergie prélevée dans la batterie vs l’énergie fournie par la batterie aux consommateurs. Valeur entre ]0; 1]. + float courant_repos; // Valeur en-dessous de laquelle la valeur absolue du courant différentiel sera considérée comme nul. Utilisé pour repasser en mode Open Voltage Mode (OVM) pour ajuster le SOC pendant les longues périodes dìnactivite. Exprimée en A. + uint32_t nombre_cycles; // Nombre de fois que la batterie a été déchargée à SOC<60% après avoir été rechargée à SOC=100% + uint8_t flag_increment_cycles; // Passe à 1 lorsque "Nombre_cycle" est incrémenté. Remis à 0 lorsque le SOC = 100% + uint32_t nombre_decharges_completes; // Nombre de fois que la batterie a été déchargée à SOC=0% après avoir été rechargée à SOC=100 % + uint8_t flag_increment_decharges_completes; // Passe à 1 lorsque "Nombre_decharges_completes" est incrémenté. Remis à 0 lorsque le SOC = 100% + + uint8_t temperature_coeff_filtrage; // Entre 1 et 255. + + uint8_t tension_coeff_filtrage; // Entre 1 et 255. + float tension_gain; // Coefficient à appliquer sur la tension lue sur l\'entrée [Valeur_réelle = valeur_lue_sur_l_entree x Tension_gain ]. + + uint8_t courant_differentiel_coeff_filtrage; // Entre 1 et 255. + + uint8_t soc_coeff_filtrage; // Entre 1 et 255. + + float eod; // En Ah. Energy of Discharge. + + struct Struct_valeur_niveau points_tension_soc[MAX_NOMBRE_POINTS_TENSION_SOC_BATTERIE]; // Contient des couples '[tension]/[soc]'. Le 'SOC' de la batterie est calculé par extrapolation linéaire entre les deux points dont le 'SOC' est la plus proche du SOC actuel de la batterie. + struct Struct_valeur_niveau points_temperature_resistance_interne[MAX_NOMBRE_POINTS_TEMPERATURE_RESISTANCE_INTERNE_BATTERIE]; // Contient des couples "[température]/[résistance_interne]", séparés par des '';''. Les [températures] sont exprimées en °C. Les [résistances_internes] sont exprimées en mOhms. La résistance interne de la batterie sera calculée par extrapolation linéaire entre les deux points dont la température est la plus proche de la température actuelle du pack. Exemple : "10/5.2" + uint8_t mode_enregistrement_mesures; // 0:aucun enregistrement de mesures pour ce capteur, 1: enregistrement de la valeur du capteur lors de ses changements d'état, 2: enregistrement périodique de la valeur du capteur indépendamment de ses changements d'état, 3: enregistrement périodique ET sur changement d'état + uint8_t mode_enregistrement_historique; // 0:l'enregistrement dans l'historique des changements d'état du capteur ne sont pas réalisées, 1: les changements d'état du capteur sont enregistrées dans l'historique + + struct Struct_variables variables[MAX_NOMBRE_VARIABLES_BATTERIES]; // Différentes valeurs de chaque variable de la batterie + uint16_t cumuls_nb_val; // Nombre d'échantillons stockés sur la période (identique pour toutes les variables qui sont cumulés dans des cumuls séparés mais en même temps) + + bool flag_dde_maj_bdd; // Est utilisé pour signaler qu'un UPDATE de l'état de la BDD est nécessaire (RAZ dans Gestion_bdd.c) + struct Struct_sequenceur sequenceur; // Séquenceur de gestion de la batterie. +}; + +struct Struct_batteries +{ + uint8_t compteur_affichage_debug; // Permet de ne pas déclencher l'affichage debug à chaque boucle mais seulement 1 fois sur N + struct Struct_variable variables[MAX_NOMBRE_VARIABLES_BATTERIES]; + struct Struct_batterie batteries[MAX_NOMBRE_BATTERIES]; + uint16_t nb; // Nb de batteries détectées dans 'bdd_init()'. Permet de réduire les boucles en exploitation à ce nombre plutôt que jusqu'à MAX_NOMBRE_BATTERIES. Attention : toutes les batteries dont le rang est < nb ne sont pas forcément valides. + struct Struct_sequenceur sequenceur; +}; + + //--------------------------------------------------------------------------------------------- + // Bdd_init : + //--------------------------------------------------------------------------------------------- + +struct Struct_bdd +{ + MYSQL *pointeur; + int16_t compteur_synchro; // Compteur spécifique utilisé pour vérifier fréquément si le flag de synchro a été positionné dans la BDD par un serveur web + int16_t compteur; // Compteur utilisé pour faire des mises à jour périodique, de façon asynchrone + int16_t compteur_enregistrement_mesures; // Compteur permettant de lancer un archivage des mesures à période fixe +}; + +struct Struct_scenarios +{ + int last_minute; // Contient la minute de la dernière action traitée (permet de déclencher dès le début de la "nouvelle minute") +}; + + + //--------------------------------------------------------------------------------------------- + // C_bus : + //--------------------------------------------------------------------------------------------- +struct Struct_commande_c_bus +{ + uint8_t cde[C_BUS_MAX_TAILLE_CDE]; // Contient la commande complète à émettre ou reçue + uint16_t lg_cde; // Contient la longueur de la commande + uint16_t priorite; // Défini la priorité (uniquement pour le UP) +}; +struct Struct_reponse_c_bus +{ + uint8_t reponse[C_BUS_MAX_TAILLE_REPONSE]; // Contient la commande complète à émettre ou reçue + uint16_t lg_reponse; // Contient la longueur de la commande + bool flag_ready; // Indique que la réponse est prête à être émise +}; +struct Struct_protocole_c_bus +{ + uint16_t cs; // Valeur en cours du CS + uint16_t cs_recu; // Valeur de CS reçu + uint8_t ptr_cs_recu; // Permet de compter les 2 octets de CS après le STX de fin + bool flag_dle; // Indique que le dernier caractère traité était un DLE. + uint8_t sens; // Contient le sens de la commande en cours de réception ('D' ou 'U') + uint8_t repetitions; // Contient le nombre de répétitions effectuées sur la commande en cours (uniquement pour Tx en mode DOWN) + struct Struct_sequenceur sequenceur; // Séquenceur du protocole +}; +struct Struct_c_bus_up +{ + struct Struct_commande_c_bus cde; // Commande reçue du Compagnon en cours de traitement + struct Struct_reponse_c_bus reponse; // Réponse à la commande reçue du Compagnon et qui va être émise +}; +struct Struct_c_bus_down +{ + struct Struct_commande_c_bus buffer_cdes[C_BUS_MAX_TAILLE_BUFFER_CDES]; // Buffer tournant contenant les commandes en attente d'émission sur le C_Bus UP + uint16_t ptr_debut_buffer_cdes; // Indique la position de la prochaine commande à émettre + uint16_t ptr_fin_buffer_cdes; // Indique la position de le dernière commande en attente d'émission + bool flag_attente_reponse; // Indique qu'une commande a été envoyée et qu'aucune trame de réponse n'a encore été obtenue + struct Struct_reponse_c_bus reponse; // Structure contenant la réponse à la dernière commande émise +}; +struct Struct_c_bus +{ + struct Struct_c_bus_up up; // Structure rassemblant les variables de gestion du bus montant + struct Struct_c_bus_down down; // Structure rassemblant les variables de gestion du bus descendant + struct Struct_protocole_c_bus protocole_rx; // Contient les informations de gestion du protocole en réception (donc reçoit les commandes Up et les réponses aux commandes Down) + struct Struct_protocole_c_bus protocole_tx; // Contient les informations de gestion du protocole en émission (donc émet les commandes Down et les réponses aux commandes Up) + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau du C-Bus +}; + + + //--------------------------------------------------------------------------------------------- + // Capteurs.c + //--------------------------------------------------------------------------------------------- +struct Struct_condition_booleenne +{ + bool flag_validite; + int8_t type_condition; // 0=condition normale, >0=début de groupe de conditions avec l\'opérateur suivant : 1=Ou, 2=Et, 3=Ou Exclusif, 10=Plus, 11=Moins, 12=Multiplié, 13=Divisé, <0=fin de groupe de conditions (même valeurs que début mais en négatif) + uint8_t type_source; // 0=Non défini, 1=circuit, 2=capteur, 3=réservoir, 4=batterie. + uint16_t id_source; // Clé primaire dans la table correspondante à Type_source. L'ID est utilisée uniquement au démarrage car lors de l'init des capteurs, les circuits, réservoirs, batteries (et certains capteurs) ne sont pas encore initialisés... + uint16_t no_source; // N° de la source dans la structure correspondante (équivalent de la clé_primaire mais pointe directement sur l'entrée de la structure dans l'appli) + uint16_t id_variable; // Clé primaire dans la table TB_Variables. L'ID est utilisée uniquement au démarrage car lors de l'init des capteurs, les circuits, réservoirs, batteries (et certains capteurs) ne sont pas encore initialisés... + uint8_t no_variable; // N° de la variable du type concerné (entre 0 et MAX_NOMBRE_VARIABLES_PAR_xxxxxx) + uint8_t test; // 0=Non défini, 1=strictement inférieure au seuil, 2=inférieure ou égale au seuil, 3=égale au seuil, 4=supérieure ou égale au seuil, 5= strictement supérieure au seuil. + float seuil; // Valeur, exprimée en unités de l'ID_Variable. +}; + +struct Struct_parenthese_calcul_capteur_booleen +{ + uint8_t operation; + bool resultat; +}; +struct Struct_calcul_capteur_booleen +{ + int16_t niveau; + struct Struct_parenthese_calcul_capteur_booleen parentheses[MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR / 2]; +}; + +struct Struct_conditions_booleennes +{ + struct Struct_condition_booleenne conditions[MAX_NOMBRE_CONDITIONS_PAR_CAPTEUR]; +}; + +struct Struct_capteur +{ + uint16_t cle_primaire; // Valeur de Clé Primaire dans TB_Capteurs + bool flag_validite; + int8_t etat_systeme; // 0: fonctionnement normal, -1: incohérence dans la config, -2: module physique affecté non disponible (absent ou type incompatible) + + + uint8_t adresse_module; // Indique le n° de module sur laquelle sont positionnées les entrées du capteur (entre 0 et MAX_NOMBRE_MODULES-1). + uint8_t type_entrees; // '0=Non défini, 1=Entrée Auxiliaire, 2=Entrée Level Sensor, 3= Voltage Input, 4=entrée OW, 5=Prédéfni, 7=Nmea, 8=Can (ou J1939), 20=Booléen calculé, 21=numérique calculé. Les autres valeurs sont invalides. + uint16_t no_entrees[MAX_NOMBRE_ENTREES_PAR_CAPTEUR]; // Stocke le n° de 'entrees.aux/level_sensor/volt/ow/predefinie/nmea/j1939.entrees[]' ou 'capteurs.capteurs[]', selon 'type_entrees'. + uint8_t nb_entrees; // Précise le nombre de valeurs utiles dans 'no_entrees[]'. Pour les capteurs calculés, le nombre de conditions puisqu'elles sont comptées dans 'Entrees' + + uint16_t id_variable; // Cle primaire dans TB_Variables indiquant quel type de variable est à utiliser pour le capteur + uint16_t no_variable; // Position dans 'capteurs.variables[]' de la variable affectée à ce capteur via 'id_variable'. Initialisée dans 'bdd_init()' à partir de 'id_variable'. + float offset; // Valeur d'offset à ajouter à la valeur lue avant son exploitation. Valeur_corrigée = (valeur_lue x Gain) + Offset + float gain; // Valeur de gain à appliquer à la valeur lue avant son exploitation. Valeur_corrigée = (valeur_lue x Gain) + Offset + float valeur_brute; // Contient la valeur brute du ou des capteurs. SDIR #135 + uint8_t valeur_coeff_filtrage; // Image du champs 'Coeff_filtrage' dans TB_Capteurs + uint8_t mode_enregistrement_mesures; // 0:aucun enregistrement de mesures pour ce capteur, 1: enregistrement de la valeur du capteur lors de ses changements d'état, 2: enregistrement périodique de la valeur du capteur indépendamment de ses changements d'état, 3: enregistrement périodique ET sur changement d'état + uint8_t mode_enregistrement_historique; // 0:l'enregistrement dans l'historique des changements d'état du capteur ne sont pas réalisées, 1: les changements d'état du capteur sont enregistrées dans l'historique + + struct Struct_conditions_booleennes conditions; + + struct Struct_variables variables[MAX_NOMBRE_VARIABLES_PAR_CAPTEUR]; // Différentes valeurs de chaque variable du capteur + uint16_t cumuls_nb_val; // Nombre d'échantillons stockés sur la période (identique pour toutes les variables qui sont cumulés dans des cumuls séparés mais en même temps) + + bool flag_dde_maj_bdd; // Est utilisé pour signaler qu'un UPDATE de l'état de la BDD est nécessaire (RAZ dans Gestion_bdd.c) + int8_t etat; // Indique si le capteur est normal (0), en alerte (1) ou en alarme (2). Ou s'il y a un défaut de capteur (-1) ou un défaut général (bus en CC, alimentation HS,...) (-2). +}; +struct Struct_capteurs +{ + struct Struct_variable variables[MAX_NOMBRE_VARIABLES_CAPTEURS]; // Attention : le fonctionnement de ce tableau pour les capteurs est différent du fonctionnement pour les batteries, circuits et réservoirs (la dimension est MAX_NOMBRE_VARIABLES_CAPTEURS et non MAX_NOMBRE_ENTREES_PAR_CAPTEUR) + struct Struct_capteur capteurs[MAX_NOMBRE_CAPTEURS]; + uint16_t nb; // Nb de capteurs détectés dans 'bdd_init()'. Permet de réduite les boucles en exploitation à ce nombre plutôt que jusqu'à MAX_NOMBRE_CAPTEURS. Attention : tous les capteurs dont le rang est < nb ne sont pas forcément valides. + struct Struct_sequenceur sequenceur; +}; + + + //--------------------------------------------------------------------------------------------- + // Circuits.c + //--------------------------------------------------------------------------------------------- +struct Struct_circuit_cumuls +{ + double tension; + double intensite; + double frequence; + double puissance; + uint16_t nb_val; // Nombre d'échantillons stockés sur la période (identique pour tous les paramètres qui sont cumulés dans des variables séparées mais en même temps) +}; + +struct Struct_circuit +{ + // Note importante : les circuits sont identifiés par leur 'no_circuit' sur une 'adresse_module' + // Ce numéro de circuit est compris entre 0 et (SP8xx_MAX_NOMBRE_CIRCUITS - 1) par exemple. + uint16_t cle_primaire; // Clé primaire dans la table 'TB_Circuits' + bool flag_validite; // Indique si ce circuit est valide (1) ou pas (0). La mise à jour de ce champs est uniquement manuelle ; cela permet, par exemple, de passer un circuit en NON AFFECTE. + int8_t etat_systeme; // 0: fonctionnement normal, -1: incohérence dans la config, -2: module physique affecté non disponible (absent ou type incompatible) + + uint16_t adresse_module; // Indique l'adresse K-Bus du module par laquel le circuit est commandé (entre 0 et MAX_NOMBRE_MODULES-1). + uint16_t no_circuit; // Indique le n° de circuit sur la carte (entre 0 et MAX_NOMBRE_CIRCUITS_PAR_CARTE-1) + int16_t id_circuit_esclave; // Si 0, aucun circuit esclave. Si > 0, indique qu\'il a un circuit esclave (la valeur est l'ID de circuit). Si < 0 : indique qu\'il est lui-même l\'esclave d\'un autre circuit (l'ID du circuit maitre est alors la valeur absolue du paramètre). NB : Cet ID n'est utilisé qu'entre l'init et le chargement de l'annuaire des cartes et donc des circuits. Il est alors remplacé par 'no_circuit_esclave'. + int16_t no_circuit_esclave; // Si -1, aucun circuit esclave. Si >= 0, indique qu\'il a un circuit esclave (la valeur est le n° de circuit). + uint16_t id_batterie; // Indique à quelle batterie le circuit est affecté (hérité de sa carte d'appartenance). 0 si aucune batterie n'est définie. + uint8_t etat_commande_carte; // Etat de la commande du circuit mis à jour par le logiciel à partir des informations d'état de la carte. 0=Normal, 1=Undervoltage, 2=Test + uint8_t mode_cde; // Indique le mode de commande du circuit : 0=On-Off, 1=On-Off soft, 2=Variable soft et 3=Cadencé + + uint8_t origine_consigne; // Contient l'origine de la consigne en cours : 0=ShipBase (ou, plus généralement, un User), >0=ID du capteur ayant généré le changement de consigne + uint16_t id_origine_consigne; // Contient l'ID ayant généré la consigne. Si 'origine_consigne'=Capteur, l'ID du capteur. + uint8_t mode_test; // Indique la position actuelle du switch de test du circuit (0=inactif, 1=activé) + + struct Struct_variables variables[MAX_NOMBRE_VARIABLES_CIRCUITS]; // Différentes valeurs de chaque variable du circuit + uint16_t cumuls_nb_val; // Nombre d'échantillons stockés sur la période (identique pour toutes les variables qui sont cumulés dans des cumuls séparés mais en même temps) + + uint8_t tension_coeff_filtrage; // Valeur du filtre IIR à utiliser pour calculer la valeur moyenne de la tension du le cicuit. + uint8_t intensite_coeff_filtrage; // Valeur du filtre IIR à utiliser pour calculer la valeur moyenne de l'intensité dans le cicuit. + uint8_t frequence_coeff_filtrage; // Valeur du filtre IIR à utiliser pour calculer la valeur moyenne de la fréquence dans le cicuit. + float intensite_seuil_haut; // Indique la valeur (en A) du seuil haut avant disjonction (inactif si 0) + uint16_t intensite_seuil_haut_tempo; // Tempo de filtrage entre le déclenchement et la disjonction (en ms) + uint8_t step_time; // Durée, en ms, de chaque pas de soft start/stop. Entre 10 et 255ms + uint16_t t_on; // Durée de l'impulsion ON en mode Cadencé (en 100ms) + uint16_t t_off; // Durée de l'impulsion OFF en mode Cadencé (en 100ms) + + uint8_t mode_enregistrement_mesures; // Défini la façon dont les mesures de ce circuit (intensité) sont enregistrées : A définir... + uint8_t mode_enregistrement_historique; // Défini la façon dont les évènements de ce circuit sont enregistrées : A définir... + + uint8_t etat; // Contient l'état actuel du circuit + uint32_t compteur_time_out; // Variable décomptée chaque seconde dans 'hardware.c' si sa valeur est > 0 + uint32_t time_out; // Armé lors d'un changement de commande de OFF à ON (ou à toute valeur différente de OFF). La commande est ramenée automatiquement à OFF en fin de time-out. En secondes (0=aucun time-out). + bool flag_dde_maj_bdd; // Est utilisé pour signaler qu'un UPDATE de l'état de la BDD est nécessaire (RAZ dans Gestion_bdd.c) + bool flag_changement_consigne_en_cours; // Indique qu'une demande de changement est en cours => le prochain retour d'état du circuit sera ignoré (SDIR #179) + + uint8_t validation_cdes_events; // Image du champ 'Validation_cdes_events' de TB_Circuits + struct Struct_sequenceur sequenceur; // Séquenceur de gestion du circuit +}; + struct Struct_circuits + { + struct Struct_variable variables[MAX_NOMBRE_VARIABLES_CIRCUITS]; + struct Struct_circuit circuits[MAX_NOMBRE_CIRCUITS]; // Structure contenant, pour chaque circuit, toutes les informations de configuration, d'état et de consigne. + uint16_t nb; // Nb de circuits détectés dans 'bdd_init()'. Permet de réduite les boucles en exploitation à ce nombre plutôt que jusqu'à MAX_NOMBRE_CIRCUITS. Attention : tous les circuits dont le rang est < nb ne sont pas forcément valides. + struct Struct_sequenceur sequenceur; + }; + + + //--------------------------------------------------------------------------------------------- + // Debug.c + //--------------------------------------------------------------------------------------------- + +struct Struct_debug +{ + int mode; // Mode de gestion des messages de debug. + char nom_fichier[MAX_LONGUEUR_NOM_FICHIER_DEBUG]; // Nom du fichier de debug à utiliser (si Mode le permet) + FILE *ptr_fichier; // Stocke le pointeur de fichier correspondant au fichier de debug. + +// char ligne[MAX_LONGUEUR_LIGNE_FICHIER_LOG]; // Stocke la ligne en cours de construction (permets de n'avoir qu'une ligne même en cas d'appel successifs à des fonctions de bas niveau) +// int flag_erreur_detectee; // Si 0, aucune erreur n'a été détectée. Sinon, il faut conserver le(s) dernier(s) fichier(s) tournant(s) +// int franchise_suppression; // Indique si on doit conserver des fichiers postérieurs à un évènement (et si >0, le nombre de secondes). +}; + + //--------------------------------------------------------------------------------------------- + // Ecran : + //--------------------------------------------------------------------------------------------- +struct Struct_ecran + { + float consigne_backlight; // En % (0 à 100.00). Valeur issue de 'TB_Configuration.Consigne_backlight' + float consigne_leds; // En % (0 à 100.00). Valeur issue de 'TB_Configuration.Consigne_leds' + int8_t validation_leds; // Valeur issue de 'TB_Configuration.Validation_leds' et indiquant si les LEDS doivent être activées ou non + + uint16_t sortie_gpio_cm_pdown_hdmi_cmd_n; // Prend la valeur de CM3P_Pdown_HDMI_CMD_N ou CM4_Pdown_HDMI_CMD_N selon la génération de ShipBase + uint16_t sortie_gpio_cm_do_lcd_en_vgx; // Prend la valeur de CM3P_DO_LCD_EN_VGx ou CM4_DO_EN_LCD_VGx selon la génération de ShipBase + uint16_t sortie_gpio_cm_do_lcd_en_vdd; // Prend la valeur de CM3P_DO_LCD_EN_VDD ou CM4_DO_EN_LCD_VDD selon la génération de ShipBase + + struct Struct_sequenceur sequenceur; + }; + + + //--------------------------------------------------------------------------------------------- + // Entrees : + //--------------------------------------------------------------------------------------------- +struct Struct_entree_aux +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t module; // Adresse du module portant l'entrée + uint8_t no; // Indique le N° d'entrée AUX affectée (1 à 32) + int8_t logique; // 0:logique normale; 1:logique inverséee + uint8_t valeur; // Dernier état connu de l'entrée (ON/OFF) +}; +struct Struct_entree_level_sensor +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t module; // Adresse du module portant l'entrée + uint8_t no; // Indique le N° de la sortie Level Sensor affectée (1 à 8) + int8_t mode; // 0:inconnu, 1:0-10V, 2:500R, 3:4-20mA, 4:autotest + float valeur; // Valeur de l'entrée +}; +struct Struct_entree_volt +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t module; // Adresse du module portant l'entrée + uint8_t no; // Indique le N° d'entrée VOLT affectée (1 à 4) + float valeur; // Dernier état connu de l'entrée +}; +struct Struct_entree_nmea +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t module; // Adresse du module portant l'entrée + uint32_t pgn; // Indique le N° du PGN affecté à l'entrée + uint8_t field; // Indique le N° du Field du PGN affecté à l'entrée + float valeur; // Dernier état connu de l'entrée +}; +struct Struct_entree_ow +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + bool flag_presence; // FALSE=absent, TRUE=présent + char rom_code[TAILLE_ROM_CODE_OW * 3]; // On a besoin de 3 caractères par octet (xx:) sauf pour le dernier (xx) + le NULL de fin + int8_t etat; // Contient le retour d'état du Compagnon pour ce device. 0=Ok, -1=erreur lors du dernier accès. NB : les tentatives d'accès continueront tant qu'une nouvelle découverte réseau ne sera pas lancée et l'absence définitive constatée (si c'est le cas...) + float valeur; // Dernier état connu de l'entrée. SDIR # 176 +}; +struct Struct_entree_predefinie +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t module; // Adresse du module portant l'entrée + uint8_t no; // Indique le N° d'entrée Prédéfinie affectée, de 1 (PAT) à 22 (SINR) voire au-delà. Cf 'entrees.h' pour la liste complète des n° et leur affectation + float valeur; // Valeur de l'entrée (mini = -32000 pour les Défauts Isolement à la masse et maxi = 20000µA pour les capteurs 4-20mA) +}; +struct Struct_entree_core +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + char nom[MAX_LONGUEUR_NOM_ENTREE]; // Contient l'étiquette de l'entrée CORE + uint16_t no; // N° arbitraire, entre 0 et MAX_NOMBRE_ENTREES_CORE -1 + float valeur; // Dernier état connu de l'entrée +}; +struct Struct_entree_gpio +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t no; // Indique le N° de l'E/S GPIO affectée (0 à 53) => Contrairement aux autres entrées dont le n° commence à 1, on considère qu'on garde ici la numérotation du CM4, de GPIO#0 à GPIO#53. D'ailleurs, le n° correspond toujours au nom (le n° de GPIO#27 est... 27) + uint8_t pull; // 0=Aucune, 1=PULL_DOWN, 2=PULL_UP + int8_t logique; // 1:logique normale; -1:logique inverséee + uint8_t valeur; // Dernier état connu de l'entrée (ON/OFF) +}; + +struct Struct_device_ow +{ + bool flag_dde_maj_bdd; // Indique si une mise à jour de la Valeur dans la BDD est nécessaire + int16_t no_entree; // Indice de 'entrees.ow.entrees[]' correspondant à ce device (ou -1 si aucune entrée n'est affectée à ce device). Permet d'affecter les retours de 'c_bus_up_commande_etat_entrees_ow()' à l'entrée correspondante. + char rom_code[TAILLE_ROM_CODE_OW * 3]; // On a besoin de 3 caractères par octet (xx:) sauf pour le dernier (xx) + le NULL de fin + uint8_t etat; // Cette valeur est utilisée pour mettre à jour TB_One_wire.Etat (alors que c'est entrees.ow.entrees[].etat qui sera utilisée pour mettre à jour le capteur qui utilise le device) + float valeur; // Cette valeur est utilisée pour mettre à jour TB_One_wire.Valeur (alors que c'est entrees.ow.entrees[].valeur qui sera utilisée pour mettre à jour le capteur qui utilise le device) +}; + +struct Struct_entrees_aux +{ + struct Struct_entree_aux entrees[MAX_NOMBRE_ENTREES_AUXILIAIRES]; + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des batteries, à des capteurs,...) + // NB : il n'y a pas besoin de séquenceur car l'état des entrées AUX est remonté automatiquement par le Compagnon via des commandes UP. +}; +struct Struct_entrees_level_sensor +{ + struct Struct_entree_level_sensor entrees[MAX_NOMBRE_ENTREES_LEVEL_SENSOR]; + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des batteries, à des capteurs,...) + struct Struct_sequenceur sequenceur; +}; +struct Struct_entrees_volt +{ + struct Struct_entree_volt entrees[MAX_NOMBRE_ENTREES_VOLTAGE]; + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des batteries, à des capteurs,...) + // NB : il n'y a pas besoin de séquenceur car l'état des entrées VOLT est remonté automatiquement par le Compagnon via des commandes UP. +}; +struct Struct_entrees_nmea +{ + struct Struct_entree_nmea entrees[MAX_NOMBRE_ENTREES_NMEA]; + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des batteries, à des capteurs,...) + // NB : il n'y a pas besoin de séquenceur car l'état des entrées NMEA est remonté automatiquement par le Compagnon via des commandes UP. +}; +struct Struct_entrees_ow +{ + struct Struct_entree_ow entrees[MAX_NOMBRE_ENTREES_OW]; + uint8_t defaut_bus; // Etat du bus OW dans son ensemble : 0 = Ok, 1 = défaut (a priori, CC) + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des capteurs,...) + struct Struct_device_ow devices[MAX_NOMBRE_DEVICES_OW]; // Utilisé essentiellement au démarrage pour stocker le retour d'annuaire OW du Compagnon (Rom_code des devices physiquement présents) + uint8_t nb_devices; + bool flag_maj_annuaire_en_cours; // Positionné par Systeme.c et RAZ par la fonction 'enregistrement_annuaire_ow()' + bool flag_premieres_valeurs_recues; // Initilisé à 0. Mis à TRUE par la première réception de valeurs OW depuis le Compagnon. +}; +struct Struc_entrees_predefinies +{ + struct Struct_entree_predefinie entrees[MAX_NOMBRE_ENTREES_PREDEFINIES]; + uint8_t nb; // Nb d'entrées trouvées comme étant affectées (à des batteries, à des capteurs,...) +}; +struct Struct_entrees_core +{ + struct Struct_entree_core entrees[MAX_NOMBRE_ENTREES_CORE]; + struct Struct_sequenceur sequenceur; +}; +struct Struct_entrees_gpio +{ + struct Struct_entree_gpio entrees[MAX_NOMBRE_ENTREES_GPIO]; + struct Struct_sequenceur sequenceur; +}; + + +struct Struct_entrees +{ + struct Struct_entrees_aux aux; + struct Struct_entrees_level_sensor level_sensor; + struct Struct_entrees_volt volt; + struct Struct_entrees_nmea nmea; + struct Struct_entrees_ow ow; + struct Struc_entrees_predefinies predefinies; + struct Struct_entrees_core core; + struct Struct_entrees_gpio gpio; + struct Struct_sequenceur sequenceur; +}; + + + //--------------------------------------------------------------------------------------------- + // Events.c : + //--------------------------------------------------------------------------------------------- +struct Struct_condition_horaire +{ + bool flag_validite; + uint8_t jour; // [jour] de 1 (lundi) à 7 (dimanche) ou 8 (tous les jours) + uint8_t heure; // [heure] de 0 à 23, 24 (fin de nuit), 25 (aube nautique), 26 (aube), 27 (lever du soleil), 28 (zénith), 29 (coucher du soleil), 30 (crépuscule), 31 (crépuscule nautique) ou 32 (début de nuit) + uint8_t minute; // [minute] entre 00 et 59 (ignorée si [heure] est > 23) +}; +struct Struct_condition_test +{ + bool flag_validite; + uint8_t type_source; // 0=condition horaire seule, 1=circuit, 2=capteur, 3=réservoir, 4=batterie. + uint16_t no_source; // N° de la source dans la structure correspondante (équivalent de la clé_primaire mais pointe directement sur l'entrée de la structure dans l'appli) + uint8_t no_variable; // N° de la variable du type concerné (entre 0 et MAX_NOMBRE_VARIABLES_PAR_xxxxxx) + uint8_t test; // 0=Non défini, >0=Déclenche si la valeur du capteur devient : 1=strictement inférieure au seuil, 2=inférieure ou égale au seuil, 3=égale au seuil, 4=supérieure ou égale au seuil, 5= strictement supérieure au seuil. + float seuil; // Valeur, exprimée en unités de l'ID_Variable. + bool etat_test; // Contient l'état actuel du résultat du test (évalué toutes les 100ms) +}; +struct Struct_conditions_horaires +{ + uint16_t nb; // Nombre de conditions horaires configurées pour cet event + struct Struct_condition_horaire conditions[MAX_NOMBRE_CONDITIONS_HORAIRES_PAR_EVENT]; // Infos des conditions horaires de l'event +}; +struct Struct_action_circuit +{ + bool flag_validite; + uint16_t no_circuit; + int8_t action; // -2=Aucune action, -1=toggle, 0-100=consigne +}; + +struct Struct_action_circuits +{ + struct Struct_action_circuit action[MAX_NOMBRE_ACTION_CIRCUITS_PAR_EVENT]; + uint16_t nb; // Nombre d'actions sur circuits utilisées sur cet event (nb : il est possible qu'à l'intérieur de ce Nb, certaines actions soient dévalidées) +}; + +struct Struct_event +{ + bool flag_validite; // Indique si l'event est valide ou pas + uint16_t cle_primaire; // Valeur de Clé Primaire dans TB_Events + uint8_t compteur_diviseur_gestion; // Si > 0, décrémenté à chaque fin de EVENTS_PERIODE_CHECK. Si = 0, traitement de 'gestion_event()' et rechargement avec 'diviseur_ecces' + uint8_t diviseur_gestion; // Exprimé en multiples de EVENTS_PERIODE_CHECK (100ms). Permet de ralentir le traitement de cet event, si besoin. 0=pas de division + + uint16_t tempo; // Temporisation, en secondes. Il faut que le test soit positif de façon permanente pendant cette durée pour que l'event soit déclenché. + + struct Struct_conditions_horaires conditions_horaires; // Infos des conditions horaires de l'event + struct Struct_condition_test condition_test; // Infos de test de l'event (il n'y en a qu'une par event) + + uint8_t alarme_son; // 0=pas d'alarme, 1=alarme + uint8_t alarme_led; // 0=pas d'alarme, 1=alarme + uint8_t alarme_soutien; // 0=pas d'alarme, 1=alarme + uint8_t alarme_source; // 0=pas d'alarme, 1=alarme + uint8_t alarme_sms; // 0=pas d'alarme, 1=alarme + uint8_t alarme_email; // 0=pas d'alarme, 1=alarme + uint8_t alarme_message; // 0=pas d'alarme, 1=alarme + + struct Struct_action_circuits action_circuits; // Infos des actions sur circuit de l'évènement + + bool flag_dde_maj_bdd; // Permet d'indiquer à 'synchro_bdd.c' qu'une mise à jour deans la BDD est nécessaire + struct Struct_sequenceur sequenceur; // Séquenceur spécifique à l'évènement +}; + + +struct Struct_events +{ + struct Struct_event events[MAX_NOMBRE_EVENTS]; + uint16_t nb; // Nombre d'évènements actuellement utilisés (permet de restreindre de MAX_NOMBRE_EVENTS à 'nb' les traitements) + + uint8_t compteur_diviseur_conditions_horaires; // Permet de ne traiter les conditions horaires qu'un sous-multiple de EVENTS_PERIODE_CHECK + uint8_t derniere_minute_traitee; // Contient la valeur de la dernière minute traitée. Permet de n'effectuer une analyse des condtions horaires qu'une fois par minute. + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau +}; + + + //--------------------------------------------------------------------------------------------- + // Fonctions.c + //--------------------------------------------------------------------------------------------- +struct Struct_supervision_boucle_principale +{ + int attente_premieres_mesures; // Permet de décaler le démarrage des mesures pour éviter les aléas de démarrage (notamment pour la valeur MIN) + time_t last_second; // Stocke la valeur de la dernière seconde lue (c'est à son changement qu'on met à zéro les compteurs) + int nb_en_cours; // Contient le nb de boucles en cours de comptage + int nb_boucles; // Nombre de boucles principales exécutées sur la dernière seconde. + int nb_secondes; // Indique le nombre de secondes correspondant au comptage dans 'nb_boucles' (permet de moyenner) +}; + + + //--------------------------------------------------------------------------------------------- + // G_bus : + //--------------------------------------------------------------------------------------------- +struct Struct_commande_g_bus +{ + uint8_t cde[G_BUS_MAX_TAILLE_CDE]; // Contient la commande complète à émettre ou reçue + uint16_t code; // Code de la commande (mis à jour automatiquement pour les commandes ASCII) + uint16_t lg_cde; // Contient la longueur de la commande + uint16_t priorite; // Défini la priorité (uniquement pour le Down) + uint8_t mode_protocole; // Précise comment le protocole va devoir traiter la commande (avec ou sans CR de fin par exemple) et/ou la réponse (si une réponse doit est attendue ou pas, par exemple) + uint16_t time_out; // Défini le time-out à utiliser pour cette commande (si 0, valeur par défaut de M-Bus) + int8_t max_repetitions; // Nombre maxi de répétitions pour l'échange en cours (si -1, valeur par défaut de M-Bus) +}; +struct Struct_reponse_g_bus +{ + uint8_t reponse[G_BUS_MAX_TAILLE_REPONSE]; // Contient la réponse complète reçue + uint8_t classe; // Valeur de CLASS reçue + uint8_t id; // Valeur d'ID reçue + uint16_t lg_reponse; // Contient la longueur de la réponse + uint16_t lg_reponse_attendue; // Contient le nombre d'octets qui sont attendus dans la réponse + uint8_t ck_a_attendu; // Contient l'octet A du CS attendu (en pratique, cette valeur est calculée au fil des réception et, comme celle-ci peut être exécutée en plusieurs appels de séquenceur, est stockée en variable globale) + uint8_t ck_b_attendu; // Contient l'octet B du CS attendu (en pratique, cette valeur est calculée au fil des réception et, comme celle-ci peut être exécutée en plusieurs appels de séquenceur, est stockée en variable globale) + uint8_t ck_a_recu; // Valeur de CK_A reçue + uint8_t ck_b_recu; // Valeur de CK_B reçue + uint8_t etat; // Indique l'état dans lequel se trouve le décodage de la commande +}; +struct Struct_protocole_g_bus +{ + uint8_t repetitions; // Contient le nombre de répétitions effectuées sur la commande en cours (en mode Tx) + struct Struct_sequenceur sequenceur; // Séquenceur du protocole +}; +struct Struct_g_bus_down +{ + struct Struct_commande_g_bus buffer_cdes[G_BUS_MAX_TAILLE_BUFFER_CDES]; // Buffer tournant contenant les commandes en attente d'émission sur le G_Bus Down + uint16_t ptr_debut_buffer_cdes; // Indique la position de la prochaine commande à émettre + uint16_t ptr_fin_buffer_cdes; // Indique la position de la dernière commande en attente d'émission + bool flag_attente_reponse; // Indique qu'une commande a été envoyée et qu'aucune trame de réponse n'a encore été obtenue + struct Struct_reponse_g_bus reponse; // Structure contenant la réponse à la dernière commande émise +}; +struct Struct_g_bus +{ + uint8_t nb_refus_buffer; // Indique le nombre de refus CONSECUTIFS de nouvelle commande dans le buffer G-Bus + struct Struct_g_bus_down down; // Structure rassemblant les variables de gestion du bus descendant + struct Struct_protocole_g_bus protocole_rx; // Contient les informations de gestion du protocole en réception (donc reçoit les réponses aux commandes Down) + struct Struct_protocole_g_bus protocole_tx; // Contient les informations de gestion du protocole en émission (donc émet les commandes Down) + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau du M-Bus +}; + + + //--------------------------------------------------------------------------------------------- + // Gnss.c : + //--------------------------------------------------------------------------------------------- +struct Struct_gnss +{ + uint8_t etat; // Indique si les dernières acquisitions sont Ok (ON) ou non valides (Off) + uint16_t repetitions; // Utilisé pour l'attente de changement de status du modem au démarrage + uint32_t compteur_cycles_gnss; // Utilisé pour calculer le nombre de cycles successifs de lecture GNSS + uint32_t compteur_cycles_valides_gnss; // Utilisé pour calculer le nombre de cycles valides de lecture GNSS + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau du M-Bus +}; + + + //--------------------------------------------------------------------------------------------- + // Hardware.c : + //--------------------------------------------------------------------------------------------- +struct Struct_hardware +{ + volatile uint32_t *gpio_base; // Attention, VOLATILE est impératif (permet de forcer le compilateur à ne PAS optimiser et donc à conserver tous les accès demandés) + uint32_t offset_gpio_out_set_0; + uint32_t offset_gpio_out_set_1; + uint32_t offset_gpio_out_clear_0; + uint32_t offset_gpio_out_clear_1; + uint32_t offset_gpio_pin_level_0; + uint32_t offset_gpio_pin_level_1; + + int16_t dizaines_ms; // Utilisé pour générer les périodes de 100ms = 10 x dizaines_ms et les périodes de 1s = 100 x dizaines_ms. +}; + + + //--------------------------------------------------------------------------------------------- + // M_bus : + //--------------------------------------------------------------------------------------------- +struct Struct_commande_m_bus +{ + uint8_t cde[M_BUS_MAX_TAILLE_CDE]; // Contient la commande complète à émettre ou reçue + uint16_t code; // Code de la commande (mis à jour automatiquement pour les commandes ASCII) + uint16_t lg_cde; // Contient la longueur de la commande + uint16_t priorite; // Défini la priorité (uniquement pour le Down) + uint8_t mode_protocole; // Précise comment le protocole va devoir traiter la commande (avec ou sans CR de fin par exemple) et/ou la réponse (si une réponse doit est attendue ou pas, par exemple) + uint16_t time_out; // Défini le time-out à utiliser pour cette commande (si 0, valeur par défaut de M-Bus) + int8_t max_repetitions; // Nombre maxi de répétitions pour l'échange en cours (si -1, valeur par défaut de M-Bus) +}; +struct Struct_reponse_m_bus +{ + uint8_t reponse[M_BUS_MAX_TAILLE_REPONSE]; // Contient la réponse complète reçue + uint16_t lg_reponse; // Contient la longueur de la réponse + uint16_t lg_reponse_attendue; // Contient le nombre d'octets qui sont attendus dans la réponse (utilisé en mode FLUX_BINAIRE) + uint8_t etat; // Indique l'état dans lequel se trouve le décodage de la commande +}; +struct Struct_protocole_m_bus +{ + uint8_t repetitions; // Contient le nombre de répétitions effectuées sur la commande en cours (en mode Tx) + struct Struct_sequenceur sequenceur; // Séquenceur du protocole +}; +struct Struct_m_bus_down +{ + struct Struct_commande_m_bus buffer_cdes[M_BUS_MAX_TAILLE_BUFFER_CDES]; // Buffer tournant contenant les commandes en attente d'émission sur le M_Bus Down + uint16_t ptr_debut_buffer_cdes; // Indique la position de la prochaine commande à émettre + uint16_t ptr_fin_buffer_cdes; // Indique la position de la dernière commande en attente d'émission + bool flag_attente_reponse; // Indique qu'une commande a été envoyée et qu'aucune trame de réponse n'a encore été obtenue + struct Struct_reponse_m_bus reponse; // Structure contenant la réponse à la dernière commande émise +}; +struct Struct_m_bus +{ + uint8_t nb_refus_buffer; // Indique le nombre de refus CONSECUTIFS de nouvelle commande dans le buffer M-Bus + struct Struct_m_bus_down down; // Structure rassemblant les variables de gestion du bus descendant + struct Struct_protocole_m_bus protocole_rx; // Contient les informations de gestion du protocole en réception (donc reçoit les réponses aux commandes Down) + struct Struct_protocole_m_bus protocole_tx; // Contient les informations de gestion du protocole en émission (donc émet les commandes Down) + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau du M-Bus +}; + + + //--------------------------------------------------------------------------------------------- + // Main.c : + //--------------------------------------------------------------------------------------------- + + + //--------------------------------------------------------------------------------------------- + // Modem.c : + //--------------------------------------------------------------------------------------------- +struct Struct_sms +{ + uint8_t index; // Contient l'index du SMS dans le modem (a priori, de 0 à 24) + char numero_emetteur[MODEM_MAX_TAILLE_NUMERO_EMETTEUR_SMS]; + char date[MAX_LONGUEUR_DATE_COURANTE]; + char message[MODEM_MAX_TAILLE_MESSAGE_SMS]; +}; + +struct Struct_http +{ + int8_t etat; // 0 au repos, 1= lancement demandé, 2= échange HTTP en cours + bool flag_https; // Indique si l'échange est à faire en HTTP (0) ou HTTPS (1) + char token[(2 * MAX_LONGUEUR_TOKEN_SHA256) + 1]; // Utilisé pour s"identifier lors de toutes les connexions HTTP + char url[MAX_LONGUEUR_URL]; // Nom de l'URL à utiliser pour la connexion (attention : sans la requête éventuelle) + char requete[MAX_LONGUEUR_REQUETE_HTTP]; // Nom de la requête à envoyer au serveur + char nom_repertoire_destination[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; // Nom du répertoire sur lequel le fichier sera téléchargé + char nom_fichier_destination[MAX_LONGUEUR_NOM_FICHIER]; // Nom du fichier qui sera créé à destination + FILE *ptr_fichier_destination; // Stocke le pointeur de fichier correspondant au fichier downloadé + uint32_t no_paquet; // Permet de suivre le n° du paquet en cours de transmission (0=premier paquet du fichier) + uint32_t taille_fichier_local; // Contient le nombre d'octets lus dans le fichier local une fois l'upload effectué + uint32_t taille_fichier_serveur; // Contient le nombre d'octets du fichier présent sur le serveir FTP distant avant le download + uint8_t buffer[MAX_TAILLE_BUFFER_DOWNLOAD]; // Stocke le buffer en cours de réception + uint32_t lg_buffer; // Indique la longueur utile du buffer + + int32_t status_connexion; // Contient le dernier status retourné par les fonctions de SHCONN de connexion au serveur (0= inconnu, -1=erreur, 1=connecté) + int32_t status_get; // Contient le dernier status retourné par les fonctions de GET REQ (en général, '200' est la valeur sans erreur ; entre 100 et 505) + uint32_t taille_retour_get; // Contient la taille du retour de requête, tel que retourné par le serveur. + uint32_t taille_buffer_rx; // Contient la taille du buffer tel que renvoyée par le modem et qui va être reçue. +}; + +struct Struct_modem +{ + uint8_t consigne; // Indique si l'état demandé pour le modem est activé (1) ou désactivé (0) + uint8_t etat; // Indique si le modem est activé (1) ou pas (0) + uint8_t etat_liaison; // Indique si la liaison a été établie avec le modem (notamment la liaison série avec l'AUTOBAUD) + uint8_t etat_4g_lte; // Indique si le réseau est activé ou pas (via CNACT=1 notamment) + uint8_t etat_presence_sim; // Indique si la carte est présente (1) ou absente (0) + uint32_t max_taille_ftp_put; // Valeur retournée par la commande AT+FTPPUT=1 et qui indique la taille maxi du prochain put qui sera fait + uint16_t repetitions; // Utilisé pour l'attente de changement de status du modem au démarrage + struct Struct_http http; // Contient les éléments de la connexion HTTP en cours + struct Struct_sms sms_en_cours; // Contient les éléments du SMS en cours de réception + uint32_t compteur_periode_gnss; // Utilisé pour calculer à quel moment lancer une acquisition sur le GNSS + uint32_t compteur_cycles_gnss; // Utilisé pour calculer le nombre de cycles successifs de lecture GNSS + uint32_t compteur_cycles_valides_gnss; // Utilisé pour calculer le nombre de cycles valides de lecture GNSS + uint32_t compteur_periode_upload; // Utilisé pour calculer à quel moment lancer une vérification d'upload (est-ce qu'il y a des fichiers à uploader sur le serveur) + uint32_t compteur_periode_download; // Utilisé pour calculer à quel moment lancer une recherche des fichiers à downloader depuis le serveur + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau du M-Bus +}; + + + //--------------------------------------------------------------------------------------------- + // Nmea.c + //--------------------------------------------------------------------------------------------- +struct Struct_pgn_nmea +{ + uint32_t pgn; + bool flag_validite; + bool flag_presence; + int8_t actif; // si 0, on ne traite que le timestamp. Si 1 : on traite les données à chaque réception (sous réserve de la période mini pour calmer le jeu). Si -1, on ne traite rien du tout (car le PGN n'est pas dans le catalogue NMEA) + int8_t type; // L'une des valeurs dans 'nmea.h' déterminée par la catalogue NMEA2000. + uint16_t periode_mini; // En x100ms. Déterminée par la catalogue NMEA2000. + uint8_t priority; // De 0 à 7 + uint8_t source; // De 0 à 253 normalement (254 = adresse NULL et 255 = broadcast, pas utilisé en Source) + uint32_t timestamp; // Timestamp de la dernière mise à jour. + uint8_t lg_data; // Entre 0 et NMEA_MAX_TAILLE_DATA_PGN = 223 + uint8_t data[NMEA_MAX_TAILLE_DATA_PGN]; // Attention : la valeurs au-delà de 'lg_data' ne sont pas garanties... +}; +struct Struct_entree_catalogue_pgn_nmea { + uint32_t pgn; + int8_t type; // -1: INCONNU, 0: SYSTEM_SINGLE_FRAME, 1: SYSTEM_FAST_PACKET, 2:DEFAULT_SINGLE_FRAME, 3: MANDATORY_FAST_PACKET, 4: DEFAULT_FAST_PACKET, 5: PROPRIETARY_FAST_PACKET + uint16_t periode_mini; // En x100ms + int8_t (*fonction_parse_pgn)(uint16_t rang, uint8_t mode); // Le 'rang' est l'indice dans 'nmea.annuaire[]' ; le 'mode' est l'une des valeurs définies dans 'nmea.h' (0: Création, 1: mise à jour, 2: suppression) +}; +struct Struct_nmea +{ + struct Struct_pgn_nmea annuaire[NMEA_MAX_NOMBRE_PGN]; // Stocke tous les PGN tels qu'envoyés par le Compagnon. C'est ensuite le rôle de nmea.c de traiter une nouvelle valeur d'un PGN pour mettre à jour TB_Nmea2000 avec tous les champs que contient ce PGN. + struct Struct_sequenceur sequenceur; // Séquenceur de plus haut niveau +}; + + + //--------------------------------------------------------------------------------------------- + // Reseaux.c + //--------------------------------------------------------------------------------------------- +struct Struct_wifi_sta +{ + int8_t cde; // Indique si le mode STA est actif (1) ou pas (0). La valeur -1 est utilisée au moment de l'init pour forcer une mise à jour. + char ssid[MAX_RESEAUX_WIFI_STA_TAILLE_SSID]; // Le nom du réseau Wifi auquel se connecter. + char bssid[MAX_RESEAUX_WIFI_STA_TAILLE_BSSID]; // L'adresse MAC du réseau Wifi auquel se connecter + char psk[MAX_RESEAUX_WIFI_STA_TAILLE_PSK]; // Mot de passe (Pres-Shared Key) du réseau Wifi auquel se connecter + char adresse_ip[TAILLE_ADRESSE_IP]; // Adresse IP de la liaison Wifi en mode STA sur le réseau du bord. + bool flag_dde_scan; // Positionné à vrai dès que TB_Configuration.Wifi_new_config == 2 +}; +struct Struct_wifi_ap +{ + int8_t cde; // Indique si le mode AP est actif (1) ou pas (0). La valeur -1 est utilisée au moment de l'init pour forcer une mise à jour. + char ssid[MAX_RESEAUX_WIFI_AP_TAILLE_SSID]; // Le nom du réseau Wifi généré. Entre 0 et 32 caractères. + char pwd[MAX_RESEAUX_WIFI_AP_TAILLE_PWD]; // Mot de passe (Passphrase). Entre 8 et 63 caractères ASCII. + uint8_t canal; // Le numero de canal à utiliser. Valeur de 1 à 140 voire plus selon la version de ShipBase + char adresse_ip[TAILLE_ADRESSE_IP]; // Adresse IP de ShipBase sur le réseau Wifi ShipHeart +}; +struct Struct_wifi +{ + bool flag_new_config; // Positionné à vrai dès que TB_Configuration.Wifi_new_config == 1 + struct Struct_wifi_sta sta; + struct Struct_wifi_ap ap; +}; +struct Struct_ethernet +{ + char adresse_ip[TAILLE_ADRESSE_IP]; // Adresse IP de la liaison ethernet sur le réseau du bord. +}; +struct Struct_webgl +{ + uint8_t validite; // 0: l'accès webGL est dévalidé, 1: l'accès est validé + char parametres[MAX_LONGUEUR_PARAMETRES_WEBGL]; // Chaine de caractères correspondant au lacement du process WebGL à faire (exemple : "-platform webgl:port=8081:wsserverport=8082") +}; +struct Struct_reseaux +{ + uint32_t compteur_check_adresses_ip; + uint32_t periode_check_adresses_ip; // Copie de TB_Configuration.Periode_check_adresses_ip + uint32_t compteur_connexion_ship2net; + uint32_t periode_connexion_ship2net; // Copie de TB_Configuration.Periode_connexion_ship2net + uint32_t compteur_scan_sta; + uint32_t periode_scan_sta; // Copie de TB_Configuration.Periode_scan_wifi_sta + uint16_t time_out_scan_sta; // Compteur décrémenté chaque TEMPO_ATTENTE_BOUCLE_SCAN_WIFI_STA (a priori,1s) qui permet de gérer le time-out de relecture du fichier de résultat du scan + struct Struct_wifi wifi_new; // Utilisé pour stocker temporairement les valeurs reçues via la BDD + struct Struct_wifi wifi; + struct Struct_ethernet ethernet; + uint32_t compteur_check_webgl; + struct Struct_webgl webgl_new[MAX_NOMBRE_PROCESS_WEBGL]; + struct Struct_webgl webgl[MAX_NOMBRE_PROCESS_WEBGL]; + struct Struct_sequenceur sequenceur; +}; + + + //--------------------------------------------------------------------------------------------- + // Réservoirs.c : + //--------------------------------------------------------------------------------------------- +struct Struct_reservoir +{ + uint16_t cle_primaire; // Valeur de Clé Primaire dans TB_Reservoirs + bool flag_validite; + int8_t etat_systeme; // 0: fonctionnement normal, -1: incohérence dans la config, -2: module physique affecté non disponible (absent ou type incompatible) + + uint8_t adresse_module; // Indique l'adresse du module sur laquelle sont positionnées les entrées du réservoir (entre 0 et MAX_NOMBRE_MODULES-1). + uint8_t type_entrees; // '0=Non défini, 1=Entrée Auxiliaire, 2=Entrée Level Sensor, 3= Voltage Input, 4=entrée OW, 5=Prédéfni, 7=Nmea, 8=Can (ou J1939), 20=Booléen calculé, 21=numérique calculé. Les autres valeurs sont invalides. + uint16_t no_entrees[MAX_NOMBRE_ENTREES_PAR_RESERVOIR]; // Stocke les numéros de 'entrees.aux/level_sensor.entrees[]' selon Type_entrée. + uint8_t nb_entrees; // Précise le nombre de valeurs utiles dans 'no_entrees[]' + + float valeur_brute; // Contient la valeur brute du ou des capteurs du réservoir. SDIR #288 + uint8_t etat_entrees; // 0=Ok, 1=Electrodes plongées dans le désordre + uint8_t niveau_coeff_filtrage; // Image du champs 'Niveau_coeff_filtrage' dans TB_Reservoirs + float niveau_seuil_haut; // Exprimé en centième de pourcentage, entre 0.00 et 100.00%. Ce seuil est utilisé pour l'affichage, pour l'historique et les alertes. + float niveau_seuil_bas; // Exprimé en centième de pourcentage, entre 0.00 et 100.00%. Ce seuil est utilisé pour l'affichage, pour l'historique et les alertes. + struct Struct_valeur_niveau liste_valeurs_niveaux[MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS]; // Contient des couples '[valeur]/[niveau]'. Le 'Niveau' du réservoir est calculé par extrapolation linéaire entre les deux points dont la 'valeur' est la plus proche de la Valeur actuelle du réservoir. + int8_t logique; // Par défaut, les alertes sont déclenchées lors du dépassement des seuils du haut vers le bas (Logique=-1). En passant à Logique=1, le principe est inversé ; les alertes sont alors déclenchées lors du dépassement du bas vers le haut. Si Logique=0, la zone normale est située entre le seuil bas et le seuil haut. Ce sont les dépassements au-dessous du seuil bas ou au-dessus du seuil haut qui déclenchent une alerte. + uint8_t mode_enregistrement_mesures; // 0:aucun enregistrement de mesures pour ce capteur, 1: enregistrement de la valeur du capteur lors de ses changements d'état, 2: enregistrement périodique de la valeur du capteur indépendamment de ses changements d'état, 3: enregistrement périodique ET sur changement d'état + uint8_t mode_enregistrement_historique; // 0:l'enregistrement dans l'historique des changements d'état du capteur ne sont pas réalisées, 1: les changements d'état du capteur sont enregistrées dans l'historique + + struct Struct_variables variables[MAX_NOMBRE_VARIABLES_RESERVOIRS]; // Différentes valeurs de chaque variable du réservoir + uint16_t cumuls_nb_val; // Nombre d'échantillons stockés sur la période (identique pour toutes les variables qui sont cumulés dans des cumuls séparés mais en même temps) + + bool flag_dde_maj_bdd; // Est utilisé pour signaler qu'un UPDATE de l'état de la BDD est nécessaire (RAZ dans Gestion_bdd.c) + struct Struct_sequenceur sequenceur; // Séquenceur de gestion du capteur. +}; + +struct Struct_reservoirs +{ + struct Struct_variable variables[MAX_NOMBRE_VARIABLES_RESERVOIRS]; + struct Struct_reservoir reservoirs[MAX_NOMBRE_RESERVOIRS]; + uint16_t nb; // Nb de réservoirs détectés dans 'bdd_init()'. Permet de réduite les boucles en exploitation à ce nombre plutôt que jusqu'à MAX_NOMBRE_RESERVOIRS. Attention : tous les réservoirs dont le rang est < nb ne sont pas forcément valides. + struct Struct_sequenceur sequenceur; +}; + + + //--------------------------------------------------------------------------------------------- + // Serial.c + //--------------------------------------------------------------------------------------------- +struct Struct_config_serial +{ + int8_t validite; // -1=l'UART n'est pas modifié, 0=l'UART est arrêté, 1=l'UART est activé (avec les paramètres de config suivants) + int32_t vitesse; // Contient la vitesse, en b/s + int8_t longueur; // Longueur des mots (8, 7, 6 ou 5) + int8_t parite; // 0=aucune, 1=impaire, 2=paire + int8_t stops; // 1=1 bit, 2=2 bits + int16_t filestream; // Contient le n° de port après ouvertue +}; + +struct Struct_serial +{ + uint8_t buffer_rx[MAX_TAILLE_RX_BUFFER_UART]; // Buffer tournant de réception des caractères reçus sur le port UART + uint16_t ptr_debut_buffer_rx; // Indique la position du prochain octet à lire + uint16_t ptr_fin_buffer_rx; // Indique la position du dernier octet à lire +}; + + + //--------------------------------------------------------------------------------------------- + // Sorties.c : + //--------------------------------------------------------------------------------------------- +struct Struct_sortie_gpio +{ + bool flag_validite; // FALSE=Non valide, TRUE=Valide + uint8_t no; // Indique le N° de l'E/S GPIO affectée (0 à 53) + int8_t logique; // 1:logique normale; -1:logique inverséee + uint8_t pull; // 0=Aucune, 1=PULL_DOWN, 2=PULL_UP + uint8_t valeur; // Etat de la sortie (ON/OFF) +}; +struct Struct_sorties_gpio +{ + struct Struct_sortie_gpio sorties[MAX_NOMBRE_SORTIES_GPIO]; +}; +struct Struct_sorties +{ + struct Struct_sorties_gpio gpio; +}; + + + //--------------------------------------------------------------------------------------------- + // Systeme.c + //--------------------------------------------------------------------------------------------- +struct Struct_module_config +{ + // Un module est identifiée par son 'adresse_module' dans la plupart des fonctions et dans les échanges avec le Compagnon. + // Ce numéro est l'adresse du module sur K_Bus (il n'y a donc pas de champs spécifique correspondant à cette adresse_k_bus). + // Ce numéro est compris entre 0 (ShipBase) et (MAX_NOMBRE_MODULES - 1). + // L'essentiel des champs ci-dessous correspondent à la table TB_Modules_config. La table TB_Modules_physiques sert essentiellement au démarrage pour vérifier la cohérence physique/config. + + // Paramètres de configuration lus dans la BDD : + int8_t type; // Type de carte (0=Compagnon, 1=Sp812, 2=SBS, 3=SP4AC, 4=SP8DC). + float alim_tension_seuil_bas; // Indique, en V, la tension en-dessous de laquelle un défaut de type UNDER_VOLTAGE sera déclenché. + uint16_t id_batterie; // Contient la clé primaire de la batterie à laquelle la carte est connectée (utilisée pour ShipBase et les ShipPower8xx notamment). 0 si aucune batterie n'est définie. + float courant_maxi; // Valeur (en A) de courant maxi pour la carte (notamment SP8XX). Si =0, la fonction est dévalidée. + uint16_t courant_maxi_tempo; // Tempo à appliquer avant de délester un circuit de la carte pour cause de courant maxi. En secondes. + + // Paramètres mis à jour par la fonction annuaire au démarrage : + bool flag_validite; // Indique si cette carte est valide (1) ou pas (0). Ce flag est mis à jour au démarrage : tous les modules présents dans TB_Modules_config ont ce flag à VRAI et tous les autres à FALSE. + bool flag_presence; // Indique si la carte est physiquement présente (1) ou pas (0) + bool flag_affecte; // Indique si le module est affecté à au moins un circuit, une batterie, un réservoir ou un capteur. + + // Variables d'état reçues des cartes et mis à jour toutes les secondes dans la BDD (notamment via 'c_bus_up_commande_etat_carte()' ) : + uint8_t origine_reset; // Contient l'origine du dernier reset de la carte + uint8_t alim_cde; // Etat de la commande de l'alim (5V2 sur SBS) + uint8_t alim_etat; // Etat de l'alimentation (0=Normal, 1=Under-Voltage). Attention : concerne soit l'alimentation 12-24V (ShipBase, SP8DC) ou 5V (SBS). + float alim_valeur; // Valeur, en V, de la tension d'alimentation. Attention : concerne soit l'alimentation 12-24V (ShipBase, SP8DC) ou 5V (SBS). + uint8_t mode_test; // Indique la position actuelle du switch de mode test de la carte (0=Off, 1=On) + uint8_t etat_commande_carte; // Etat de la commande des circuits mis à jour par le logiciel à partir des informations d'état de la carte. 0=Normal, 1=Undervoltage, 2=Test + uint16_t courant_maxi_compteur; // Permet de mesurer le temps de défaut de courant maxi par carte. En secondes + float courant_masses; // Valeur (en A) de courant de masse actuel sur la carte (notamment SP8DC). SDIR #318 +}; + +struct Struct_module_physique +{ + // L'essentiel des champs ci-dessous correspondent à la table TB_Modules_physique et sont mise à jour au démarrage par l'appli en fonction des infos reçues depuis le Compagnon + int8_t type; // Type de carte (0=Compagnon, 1=Sp812, 2=SBS, 3=SP4AC, 4=SP8DC). + bool flag_presence; // Indique si la carte est physiquement présente (1) ou pas (0) + uint8_t hardware_unique_id[MAX_TAILLE_UNIQUE_ID]; // Contient l'ID unique du µC de la carte + uint16_t hardware_flash_size; // Contient la taille de la flash du µC (en ko) + uint8_t hardware_package; // Contient le type de boitier du µC (cf doc ST) + uint8_t hardware_version; // Contient la configuration matérielle de la carte (état des switches de config sur la carte) + char soft_type[MAX_TAILLE_TYPE_SOFT]; // Contient le type de logiciel de la carte + char soft_version[MAX_TAILLE_VERSION_SOFT]; // Contient la version de soft actuelle de la carte + uint32_t soft_crc; // Contient le CRC du soft actuellement exécuté + uint8_t adresse_mac[TAILLE_ADRESSE_MAC]; // Contient l'adresse MAC de la carte (provenant du boitier dédié soudé) + uint8_t eeprom[MAX_TAILLE_EEPROM]; // Contient une image (probablement partielle) de l'EEPROM de la carte. Chaque octet du tableau correspond à l'octet en EEPROM (le codage sur 2 caractères hexa est fait au moment du stockage dans la BDD). Permet d'échanger des paramètres de config stockées dans la carte avec QT (via Compagnon et l'appli). + uint8_t resistance_terminaison; // Indique si la résistance de terminaison est activée sur la carte (1) ou pas (0) + uint8_t maj_soft_disponible; // Indique si une mise à jour de logiciel a été chargée dans la carte et est disponible pour être activée. Utilisée dans le séquenceur 'gestion_chargement_carte()' (auparavant était stocké en BDD mais retiré en 02/2022) +}; + + + + + +struct Struct_fichier_download +{ + char nom[MAX_LONGUEUR_NOM_FICHIER]; + uint32_t taille_locale; + uint32_t taille_serveur; + bool flag_a_telecharger; + bool flag_a_supprimer; +}; +struct Struct_download +{ + char nom_repertoire_destination[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; // Nom du répertoire sur lequel le fichier sera uploadé + char nom_fichier_destination[MAX_LONGUEUR_NOM_FICHIER]; // Nom du fichier qui sera créé à destination + char nom_repertoire_source[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; // Nom du répertoire duquel le fichier sera uploadé + char nom_fichier_source[MAX_LONGUEUR_NOM_FICHIER]; // Nom du fichier à transférer + FILE *ptr_fichier_destination; // Stocke le pointeur de fichier correspondant au fichier downloadé + uint32_t no_paquet; // Permet de suivre le n° du paquet en cours de transmission (0=premier paquet du fichier) + uint32_t taille_fichier_local; // Contient le nombre d'octets lus dans le fichier local une fois l'upload effectué + uint32_t taille_fichier_serveur; // Contient le nombre d'octets du fichier présent sur le serveir FTP distant avant le download + uint8_t buffer[MAX_TAILLE_BUFFER_DOWNLOAD]; // Stocke le buffer en cours de réception + uint32_t lg_buffer; // Indique la longueur utile du buffer + uint64_t secondes_debut; // Contient le timestamp (secondes) du début du download. Est utilisé pour calculer le temps total de chargement + uint64_t secondes_fin; // Contient le timestamp (secondes) du fin du download. Est utilisé pour calculer le temps total de chargement + + struct Struct_fichier_download liste_fichiers[MAX_NOMBRE_FICHIERS_DOWNLOAD]; // Contient la liste des fichiers à charger après la ecture du répertoire distant et comparaison avec le répertoire local + uint16_t nb_fichiers_liste; // Contient le nombre d'entrées valides dans la liste des fichiers + int16_t fichier_en_cours; // Contient le pointeur du fichier en cours de download dans 'liste_fichiers' (ou -1 si aucun n'est en cours) + + int32_t status_get; // Contient le dernier status retourné par les fonctions de download (en général, '1' est la valeur sans erreur) + uint32_t taille_buffer_tx; // Contient la taille du buffer demandée en lecture + uint32_t taille_buffer_rx; // Contient la taille du buffer tel que renvoyée par le modem +}; + +struct Struct_json_version +{ + char no_version[MAX_TAILLE_VERSION_SOFT]; // Contient la version du package. Par exemple : "1.0.7" + char updater[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier d'updater à utiliser. Par exemple : "updater_210722_171424.php" + char application[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier d'application à utiliser. Par exemple : "shipheart_appli_210719_180758" + char qt[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier d'application QT à utiliser. Par exemple : "shipheart_qt_210722_154703" + char compagnon[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier binaire du Compagnon à utiliser. Par exemple : "shipheart_compagnon_210716_191905.bin" + char sp812[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier binaire des SP812 à utiliser. Par exemple : "shipheart_sp812_210630_115719.bin" + char sbs[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier binaire des SBS à utiliser. Par exemple : "shipheart_sbs_210630_115719.bin" + char sp4ac[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier binaire des SP4AC à utiliser. Par exemple : "shipheart_sp4ac_210630_115719.bin" + char sp8dc[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier binaire des SP8DC à utiliser. Par exemple : "shipheart_sp8dc_230130_115719.bin" + char sql[MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier SQL à utiliser. Par exemple : "shipheart_sql_new.sql" + char force; // Contient l'état du flag Force du package. + char description[MAX_TAILLE_DESCRIPTION_PACKAGE]; // Contient la description du package. Exemple : "Version de test générée à la main" +}; + + +struct Struct_chargement_code +{ + char nom_fichier[MAX_NOMBRE_MODULES][MAX_LONGUEUR_NOM_FICHIER]; // Contient le nom du fichier à utiliser pour le prochain chargementsur cette carte (par exemple, 'ShipHeart_Compagnon.bin'). Si NULL, pas de chargement demandé pour cette carte. + uint32_t taille_fichier; // Contient le nombre d'octets du fichier + uint32_t crc_fichier; // Valeur de CRC indiquée en fin de fichier (mis là par le linker, juste après la création) + uint8_t adresse_module; // Adresse K-Bus du module concerné par le chargement. 0=Compagnon, >0 pour les cartes extensions + uint16_t no_bloc; // N° de bloc en cours de transfert + uint16_t taille_bloc; // Contient la taille du bloc en cours de transfert + uint32_t nb_transmis; // Indique le nombre d'octets transmis + bool flag_reponse; // Indique qu'une réponse à la dernière commande adressée au Compagnon a été reçue + uint8_t status_reponse; // Contient le code d'erreur retourné par le Compagnon à la dernière commande d'upload + uint8_t nb_repetitions; // Contient le nombre de répétitions en cours + uint32_t id_message_user; // Contient la clé primaire du message user en cours d'affichage et dont l'état est maintenu à jour lors du chargement... + struct Struct_sequenceur sequenceur; +}; +struct Struct_upload +{ + char nom_repertoire_destination[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; // Nom du répertoire sur lequel le fichier sera uploadé + char nom_fichier_destination[MAX_LONGUEUR_NOM_FICHIER]; // Nom du fichier qui sera créé à destination + char nom_repertoire_source[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; // Nom du répertoire duquel le fichier sera uploadé + char nom_fichier_source[MAX_LONGUEUR_NOM_FICHIER]; // Nom du fichier à transférer + FILE *ptr_fichier_source; // Stocke le pointeur de fichier correspondant au fichier à uploader + uint32_t ptr_debut; // Stocke la première position dans le fichier correspondant au début du buffer en cours d'émission + uint32_t ptr_fin; // Stocke la dernière position dans le fichier correspondant au début du buffer en cours d'émission + uint32_t no_paquet; // Permet de suivre le n° du paquet en cours de transmission (0=premier paquet du fichier) + uint32_t taille_theorique_fichier; // Taille lue du fichier à uploader + uint32_t taille_fichier_local; // Contient le nombre d'octets transmis depuis le début du transfert du fichier + uint32_t taille_fichier_serveur; // Contient le nombre d'octets du fichier présent sur le serveir FTP distant après l'upload + uint8_t buffer[MAX_TAILLE_BUFFER_UPLOAD]; // Stocke le buffer qui va être émis + uint32_t lg_buffer; // Indique la longueur utile du buffer + int32_t status_get_size; // Contient le statut de retour de la commande FTPSIZE (0 = OK, sinon code entre 61 et 80) + int8_t code_erreur_ftpput_data; // Contient le code d'erreur retourné après l'envoi des data dans une séquence FTPPUT (a priori, soit OK=0, soit ERROR=4) + uint8_t nb_repetitions; // Contient le nombre de répétitions en cours (utilisé lorsque'une erreur est détectée sur un bloc pour ne pas répéter ce bloc indéfiniement) + uint16_t nb_repetitions_total; // Contient le nb de répétitions de blocs effectuée sur l'ensemble de l'Upload du fichier (utilisé pour info en fin d'upload pour donner une idée de la qualité du transfert) + uint64_t secondes_debut; // Contient le timestamp (secondes) du début de l'upload. Est utilisé pour calculer le temps total de chargement + uint64_t secondes_fin; // Contient le timestamp (secondes) du fin de l'upload. Est utilisé pour calculer le temps total de chargement +}; + +struct Struct_faucheuse +{ + int32_t signal_recu; // Stocke la valeur reçue par le handler de signaux + bool flag_demande_reinit; // Positionné lorsqu'une réinit a été décidé pour informer que ça ne va pas tarder... (équivalent à 'signal_reçu' mais interne) + uint32_t id_message_user; // Contient l'ID du message user utilisé pendant la phase d'arrêt du système + uint16_t compteur; // Est utilisé pour temporiser l'arrêt +}; +struct Struct_compute_module +{ + uint8_t overvoltage_allowed; + uint8_t otp_programming_allowed; + uint8_t otp_reading_allowed; + uint8_t warranty_bit; + uint8_t new_style_revision; + uint8_t memory_size; + uint8_t manufacturer; + uint8_t processor; + uint8_t type; + uint8_t revision; + char chaine_version[MAX_LONGUEUR_CHAINE_VERSION_CM]; // Contient la chaine de caractère qui est remontée après init dans TB_Systeme.Version_cm +}; +struct Struct_systeme +{ + char nom_bateau[MAX_LONGUEUR_NOM_BATEAU]; // Valeur image de TB_Configuration.Nom_du_bateau + float courant_masses_maxi_sp8dc; // Valeur image de TB_Configuration.Courant_masses_maxi_sp8dc. SDIR #318 + + int8_t fuseau_horaire; // Fuseau horaire, exprimé en heures par rapport à UTC. De -12.00 à +14.00 + uint8_t heure_ete; // Offset sur le fuseau horaire. soit 0, soit +1 + struct Struct_heure heures_speciales[MAX_HEURES_SPECIALES]; // Contient les heures spéciales du jour. Récupérées via la BDD des calculs faits par Qt. Cf liste dans 'events.h'. + + uint8_t veille; // Indique si le système est en veille légère (1), profonde (2) ou pas en veille du tout (0) + struct Struct_module_config modules_config[MAX_NOMBRE_MODULES]; // Structure contenant, pour chaque module utilisé en config toutes les informations de configuration et d'état + struct Struct_module_physique modules_physiques[MAX_NOMBRE_MODULES]; // Structure contenant les informations de chaque module physiquement présent sur K-Bus + uint8_t mode_validation_maj; // Valeur de 'Mode_validation_maj' dans TB_COnfiguration. Indique comment les fichiers de mises à jour reçus du serveur doivent être traités. 0=aucune mise à jour (les fichiers reçus sont détruits), 1=validation manuelle de chaque mise à jour, 2= validation automatique des mises à jour. + uint8_t mode_maj_firmwares; //Indique comment les mise à jour du firmware des modules présents sur K-Bus doit être effectuée. 0=Normal : mise à jour si la version installée est antérieure à la version en cours. 1=Forçage si la version installée est différente de la version en cours (donc mise à jour même si la version installée est plus récente que la version en cours). 2=Forçage dans tous les cas (y compris si les deux versions sont identiques). + uint32_t periode_sauvegarde_bdd; // Valeur provenant de la BDD et indiquant la période (en secondes) à laquelle la BDD est sauvegardée (et donc uploadée vers le serveur) + bool flag_compagnon_ready; // Positionné à FALSE au démarrage, il est positionné à TRUE dès que le Compagnon est déclaré prêt à passer en opération normale + bool flag_cm3p_ready; // Positionné à FALSE au démarrage, il est positionné à TRUE dès que le CM3P est déclaré prêt à passer en opération normale + bool flag_new_download; // Positionné à TRUE (par 'modem.c') quand de nouveaux fichiers ont été downloadés du serveur. Permet à 'systeme.c' de lancer l'analyse de ces fichiers. + uint16_t compteur_sauvegarde_bdd; // En secondes + uint16_t compteur_check_new_download; // En secondes + uint32_t no_paquet; // N° du paquet en cours de constitution mais pas encore envoyé (issu de TB_Système au démarrage puis mis à jour à chaque envoi de paquet) + char cm3p_soft_version[MAX_TAILLE_VERSION_SOFT]; // Contient la version de soft de l'application + char package_version[MAX_TAILLE_VERSION_SOFT]; // Contient la version de package sous la forme 'AAMMJJ_HHMMSS' + char package_no_version[MAX_TAILLE_VERSION_SOFT]; // Contient le numéro de la version du package. Par exemple : "1.0.7" + uint8_t flag_r_term_etat_carte; // Si Vrai, considère que les flags de présence R_Term sont remontés dans les commandes 'etat_carte()' (compatibilité < 2.0.0). Si Faux (défaut), l'état des R_Term est remonté dans le retour d'annuaire K-Bus et pas dans etat_carte(). + struct Struct_compute_module cm; // Contient les informations relatives au Compute Module + uint8_t generation_shipbase; // Mis à jour au démarrage en recherchant le type de CM installé => 0 = inconnue, 1=CM3+, 2=CM4 + uint16_t entree_gpio_cm_di_from_uc; // Prend la valeur de CM3P_DI_FROM_UC ou CM4_DI_FROM_UC selon la génération de ShipBase + uint16_t sortie_gpio_cm_do_to_uc; // Prend la valeur de CM3P_DO_TO_UC ou CM4_DO_TO_UC selon la génération de ShipBase + struct Struct_sequenceur sequenceur; + struct Struct_faucheuse faucheuse; // Structure permettant la surveillance des signaux externes et la tempo en cas de demande de reset. +}; + + + //--------------------------------------------------------------------------------------------- + // Usb.c + //--------------------------------------------------------------------------------------------- +struct Struct_port_usb +{ + bool flag_validite; // Indique si le port est valide (1) ou pas (0) + uint8_t no_entree_defaut; // Contient le n° d'entrée GPIO recevant le défaut du contrôleur + uint8_t defaut; // Indique si le port est Ok (0) ou en défaut (1) +}; +struct Struct_usb +{ + int16_t no_circuit_usb_avant; // Contient le n° du circuit utilisé pour piloter les ports USB de la face avant ou -1 si aucun circuit n'est associé. + int16_t no_circuit_usb_arriere; // Contient le n° du circuit utilisé pour piloter le port USB arrière ou -1 si aucun circuit n'est associé. +// uint8_t consigne_usb; // Indique si les ports doivent être alimentés (1) ou pas (0) +// uint8_t consigne_usb_arriere; // Indique si le port USB arriere doit être alimenté (1) ou pas (0) + uint16_t sortie_gpio_cm_do_usb_avant_pwr_en; // Prend la valeur de CM3P_DO_USBPWR_EN ou CM4_DO_USBPWR_EN selon la génération de ShipBase + uint16_t sortie_gpio_cm_do_usb_arriere_pwr_en; // Prend la valeur de CM4_DO_USB_ARRIERE_PWR_EN selon la génération de ShipBase (uniquement sur V2) + struct Struct_port_usb ports[USB_MAX_NOMBRE_PORTS]; + struct Struct_sequenceur sequenceur; +}; + + +// ********************************************************************************************************************************************************************************** +// Prototypages : +// ********************************************************************************************************************************************************************************** + + // Audio.c : +extern void gestion_audio(void); +extern int8_t init_audio(void); +extern int8_t emission_bip(char * nom_fichier, uint16_t nb_repetitions, uint16_t periode_boucles, uint16_t nb_boucles); +extern int8_t set_volume(float volume, bool echo); +extern int8_t lecture_mp3(char * nom_fichier, uint16_t nb_repetitions); + + // Batteries.c +extern int8_t init_batteries(void); +extern void gestion_batteries(void); +extern int16_t batterie_cle_primaire(uint16_t cle_primaire); +extern int16_t variable_batterie_cle_primaire(uint16_t cle_primaire); + + // Bdd_admin.c : +extern int32_t sauvegarde_bdd(void); +extern int8_t archivage_bdd(char * chaine_date_fin, uint8_t type); + + // Bdd_fonctions.c : +extern int8_t init_annuaire_nmea(void); +extern int8_t enregistrement_date_pgn_nmea(uint16_t rang); +extern int8_t creation_pgn_field_nmea(uint16_t rang, uint8_t field, char * code_unite, uint8_t precision_accueil, uint8_t precision_mesures, char * nom); +extern int8_t enregistrement_pgn_field_nmea(uint16_t rang, uint8_t field, float val); +extern int8_t suppression_pgn_nmea(uint16_t rang); +extern void maj_etat_cm3p(uint8_t etat); +extern int8_t enregistrement_annuaire_k_bus(void); +extern int8_t enregistrement_annuaire_ow(uint8_t nb_devices); +extern void enregistrement_eeprom(uint8_t adresse_module, uint16_t longueur); +extern void enregistrement_historique(uint8_t type_source, uint8_t type, uint16_t id_source, float valeur, char * complement); +extern void enregistrement_mesures(uint8_t mode); +extern void enregistrement_mesure(uint8_t type, uint16_t no, uint16_t no_variable, char str_date[MAX_LONGUEUR_DATE_COURANTE], uint8_t mode); +extern int32_t enregistrement_message_users(int32_t code, char * parametres, int8_t etat_demande); +extern int8_t maj_message_users(uint32_t id, char * parametres, int8_t etat_demande); +extern int8_t clear_scan_wifi_sta(void); +extern int8_t enregistrement_scan_wifi_sta(char * bssid, char * nom, uint8_t canal, uint8_t qualite); +extern int8_t nom_capteur_cle_primaire(uint16_t id, char * nom); +extern int8_t nom_circuit_cle_primaire(uint16_t id, char * nom); + + // Bdd_init.c : +extern int8_t init_bdd(uint8_t mode_init); + + // Bdd_synchro.c : +extern void synchro_bdd(void); + + // C_bus.c +extern void gestion_c_bus(void); +extern void gestion_protocole_c_bus_down(void); +extern int8_t init_c_bus(void); +extern int8_t c_bus_next_write_buffer_cdes(uint8_t priorite); + + // C_commandes.c + + // Commandes Down : +extern int8_t c_bus_down_commande_config_date_heure(void); +extern int8_t c_bus_down_commande_config_carte(uint8_t adresse_module, uint8_t mode); +extern int8_t c_bus_down_commande_config_circuits(uint8_t adresse_module); +extern int8_t c_bus_down_commande_config_level_sensors(void); +extern int8_t c_bus_down_commande_config_ow(void); +extern int8_t c_bus_down_commande_config_batteries(uint8_t adresse_module); +extern int8_t c_bus_down_commande_config_pgn_nmea(uint16_t rang); + +extern int8_t c_bus_down_commande_reset(void); +extern int8_t c_bus_down_commande_cde_backlight(float consigne_ecran, float consigne_leds); +extern int8_t c_bus_down_commande_cde_circuits(uint8_t adresse_module); +extern int8_t c_bus_down_commande_cde_carte(uint8_t adresse_module, uint8_t cde, uint8_t consigne); + +extern int8_t c_bus_down_commande_lecture_date_heure(void); +extern int8_t c_bus_down_commande_lecture_id_carte(uint8_t adresse_module); + +extern int8_t c_bus_down_commande_annuaire_k_bus(void); +extern int8_t c_bus_down_commande_chargement_code(uint8_t mode, uint8_t adresse_module); +extern int8_t c_bus_down_commande_annuaire_ow(void); + + + // Réponses Down : +extern int8_t c_bus_down_reponse_config_date_heure(void); + +extern int8_t c_bus_down_reponse_cde_backlight(void); + +extern int8_t c_bus_down_reponse_lecture_date_heure(void); +extern int8_t c_bus_down_reponse_lecture_id_carte(void); + +extern int8_t c_bus_down_reponse_annuaire_k_bus(void); +extern int8_t c_bus_down_reponse_chargement_code(void); +extern int8_t c_bus_down_reponse_annuaire_ow(void); + +extern int8_t c_bus_down_recherche_reponse(void); + + + // Commandes UP : +extern uint8_t c_bus_up_commande_etat_carte(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_circuits(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_entrees_aux(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_level_sensors(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_entrees_volt(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_entrees_ow(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_entrees_batteries(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_commande_etat_pgn_nmea(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); + +extern uint8_t c_bus_up_commande_inconnue(struct Struct_commande_c_bus * commande, struct Struct_reponse_c_bus * reponse); +extern uint8_t c_bus_up_recherche_commande(void); + + // Capteurs.c +extern void gestion_capteurs(void); +extern int8_t init_capteurs(void); +extern int16_t capteur_cle_primaire(uint16_t cle_primaire); +extern int16_t variable_capteur_cle_primaire(uint16_t cle_primaire); + + // Circuits : +extern void gestion_circuits(void); +extern void gestion_circuit(uint16_t no_circuit); +extern int8_t changement_consigne_circuit(uint16_t no_circuit, uint8_t consigne, uint8_t origine_consigne, uint16_t id_origine_consigne); +extern int8_t init_circuits(void); +extern int16_t circuit_cle_primaire(uint16_t cle_primaire); +extern int16_t variable_circuit_cle_primaire(uint16_t cle_primaire); + + // Debug.c : +extern void clrscr(void); +extern void affichage_debug_bus(uint8_t bus, char * sens, char *commande, uint8_t output); +extern void affichage_debug(char *fichier, int ligne, char *message, char type); +extern void affichage_debug_creation_ligne(char * chaine, char *fichier, int ligne, char *message, char type); +extern int8_t init_affichage_debug(void); +extern void append_chaine_fichier(char * chaine, char * fichier); +extern char * nom_type_carte(uint8_t type); + + // Ecran.c +extern int8_t set_luminosite_ecran(uint16_t luminosite); +extern void gestion_ecran(void); +extern int8_t init_ecran(void); + + // Entrees.c +extern int16_t entree_cle_primaire(uint8_t type, uint16_t cle_primaire); +extern void gestion_entrees(void); +extern void gestion_entrees_gpio(void); +extern void gestion_entrees_core(void); +extern int8_t init_entrees(void); +extern int8_t init_entrees_gpio(void); +extern int8_t init_entrees_core(void); +extern float lecture_entree(uint8_t type, uint8_t module, uint8_t no, bool flag_immediat); +extern int16_t entree_no(uint8_t type, uint8_t module, uint8_t no); +extern uint8_t lecture_entree_gpio(uint8_t no, int8_t logique); + + // Events.c +extern int8_t init_events(void); +extern void gestion_events(void); + + // Fonctions.c +extern void date_courante(char* chaine, bool flag_ns, int format); +extern int8_t set_date_heure(struct Struct_date date, struct Struct_heure heure); +extern void delai_dixieme_sec(uint16_t tempo); +extern void delai_nano_sec(unsigned int tempo); +extern uint64_t timestamp_secondes(void); +extern int8_t tri_liste_valeurs_niveaux(struct Struct_valeur_niveau * liste, uint16_t taille_liste); +extern float calcul_niveau(struct Struct_valeur_niveau * liste, uint16_t taille_liste, float valeur); +extern float moyenne_mobile(float old_val, float new_val, uint16_t coefficient); +extern char * conversion_adresse_mac_chaine(uint8_t * adresse_mac, char * chaine, char separateur); +extern uint8_t * conversion_chaine_adresse_mac(char * chaine, uint8_t * adresse_mac); +extern char * conversion_rom_code_chaine(uint8_t * rom_code, char * chaine, char separateur); +extern char * conversion_unique_id_chaine(uint8_t * unique_id, char * chaine); +extern int32_t recherche_caractere_dans_chaine(char * chaine, char car, uint16_t debut); +extern char * chaine_minuscules(char * chaine); + + // G_Bus.c : +extern int8_t init_g_bus(void); +extern void gestion_g_bus(void); +extern int8_t gestion_protocole_rx_g_bus(void); +extern void gestion_protocole_tx_g_bus(void); +extern int8_t g_bus_next_write_buffer_cdes(uint8_t priorite); + + // G_commandes.c : +int8_t g_bus_down_commande_libre(uint8_t classe, uint8_t id, uint8_t * ptr_cde, uint16_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +extern int8_t g_bus_down_analyse_reponse_commande(void); + + // Gnss.c : +extern int8_t init_gnss(void); +extern void gestion_gnss(void); + + // Hardware.c : +extern void timer(void); +extern int init_timer(void); +extern int init_memory_map(void); + + // M_Bus : +extern int8_t init_m_bus(void); +extern void gestion_m_bus(void); +extern int8_t gestion_protocole_rx_m_bus(void); +extern void gestion_protocole_tx_m_bus(void); +extern int8_t m_bus_next_write_buffer_cdes(uint8_t priorite); + + // M_commandes : +extern int8_t m_bus_down_commande_libre(char * ptr_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +extern int8_t m_bus_down_commande_libre_binaire(uint16_t code, char * ptr_cde, uint32_t lg_cde, uint8_t mode_protocole, uint16_t time_out, int8_t max_repetitions); +extern int8_t m_bus_down_analyse_reponse_commande(void); +extern int8_t m_bus_down_recherche_code_erreur(void); + + // Modem.c : +extern int8_t init_modem(void); +extern void gestion_modem(void); + + // Main.c : +extern int8_t init(void); +extern void lancement_reinit(uint16_t tempo); +extern void sigintHandler(int dummy); + + // Nmea.c +extern int8_t init_nmea(void); +extern void gestion_nmea(void); +extern int8_t traitement_pgn_compagnon(struct Struct_pgn_nmea pgn); + + // Reseaux.c : +extern void gestion_reseaux(void); +extern int8_t init_reseaux(void); +extern int8_t get_ip_address(void); + + // Reservoirs.c +extern int8_t init_reservoirs(void); +extern void gestion_reservoirs(void); +extern int16_t reservoir_cle_primaire(uint16_t cle_primaire); +extern int16_t variable_reservoir_cle_primaire(uint16_t cle_primaire); + + // Serial.c +extern void gestion_rx_serial0(void); +extern void gestion_rx_serial1(void); +extern int init_serial0(void); +extern int init_serial1(void); + + // Sorties.c +extern void ecriture_sortie(uint8_t type, uint8_t module, uint8_t no, uint8_t valeur); +extern int8_t init_sorties(void); + + // Systeme.c +extern int8_t init_systeme(void); +extern void gestion_systeme(void); +extern void arret_programme(void); +extern void supervision(void); +extern int8_t re_configuration_systeme(void); +extern int32_t commande_shell(char * commande); +extern struct Struct_json_version parse_json_version(char * nom_fichier, bool verbose); +extern int8_t send_signal (int32_t pid, int32_t signal); +extern int32_t get_pid_from_name (char * nom, char * parametres); +extern int32_t get_pid_numbers_from_name (char * nom, char * parametres); + + // Usb.c +extern int8_t init_usb(void); +extern void gestion_usb(void); + + // Version.c +extern char * maj_version_logiciel(void); + + +// ********************************************************************************************************************************************************************************** +// Variables globales externes : +// ********************************************************************************************************************************************************************************** + + // Audio.c +extern struct Struct_audio audio; + + // Batteries.c +extern struct Struct_batteries batteries; + + // Circuits.c +extern struct Struct_circuits circuits; + + // Capteurs.c +extern struct Struct_capteurs capteurs; + + // C_bus.c +extern struct Struct_c_bus c_bus; + + // Circuits.c +extern struct Struct_circuits circuits; + + // Debug.c : +extern struct Struct_debug debug; + + // Ecran.c +extern struct Struct_ecran ecran; + + // Entrees.c : +extern struct Struct_entrees entrees; + + // Events.c +extern struct Struct_events events; + + // Fonctions.c : +extern volatile uint16_t tempo_delai_dixieme_sec; + + // G_bus.c : +extern struct Struct_g_bus g_bus; + + // Gestion_bdd.c : +extern struct Struct_bdd bdd; + + // Gnss.c +extern struct Struct_gnss gnss; + + // Hardware.c : +//extern volatile uint32_t *gpio; +extern struct Struct_hardware hardware; + + // M_Bus : +extern struct Struct_m_bus m_bus; + + // Main.c : + + + // Modem.c +extern struct Struct_modem modem; + + // Nmea.c +extern struct Struct_nmea nmea; +extern const struct Struct_entree_catalogue_pgn_nmea catalogue_pgns[NMEA_MAX_NOMBRE_PGN + 1]; + + // Reseaux.c : +extern struct Struct_reseaux reseaux; + + // Reservoirs.c +extern struct Struct_reservoirs reservoirs; + + // Serial.c : +extern struct Struct_config_serial config_serial0; +extern struct Struct_config_serial config_serial1; +extern struct Struct_serial serial0; +extern struct Struct_serial serial1; + + // Sorties : +extern struct Struct_sorties sorties; + + // Systeme.c +extern struct Struct_download download; +extern struct Struct_systeme systeme; +extern struct Struct_chargement_code chargement_code; +extern struct Struct_upload upload; +extern struct Struct_supervision_boucle_principale supervision_boucle_principale; +extern int8_t version_compute_module(void); + + // Usb.c +extern struct Struct_usb usb; \ No newline at end of file diff --git a/sources/modem.c b/sources/modem.c new file mode 100644 index 0000000..ff9774d --- /dev/null +++ b/sources/modem.c @@ -0,0 +1,1603 @@ +//********************************************************************************************************************************************************************************** +// 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; + } + } +} diff --git a/sources/modem.h b/sources/modem.h new file mode 100644 index 0000000..4de0ff0 --- /dev/null +++ b/sources/modem.h @@ -0,0 +1,145 @@ +/********************************************************************************************************************************************************************************** + + Fichier MODEM.H + + Contient tous les define liés au Modem + +**********************************************************************************************************************************************************************************/ + + // Maximums : +#define MODEM_MAX_REPETITIONS_TEMPO_ATTENTE_CHANGEMENT_ETAT 30 // Cette valeur est à relier à la TEMPO_ATTENTE_ACTIVATION. + // Le produit des deux donne la durée maximale avant de déclarer que le changement d'état attendu du modem a échoué. + // Après essais,la durée à la coupure d'alim est de 1.2s et à l'activation de 6.1s. Donc un maxi de l'ordre de 12-15s parait très bien. +#define MODEM_MAX_REPETITIONS_TEMPO_CHECK_INIT_LIAISON 30 // Cette valeur est à relier à la MODEM_TEMPO_CHECK_INIT_LIAISON. +#define MODEM_MAX_REPETITIONS_PAQUET_UPLOAD 3 // Permet de limiter le nombre de répétitions consécutives d'un même bloc lors d'un Upload. +#define MODEM_MAX_TAILLE_TRAME_FTPLIST 1024 // Cette valeur est liée au modem mais aucune doc (ni commande) ne donne la valeur exacte... +#define MODEM_MAX_NOMBRE_PAQUETS_LIST 5 // On considère qu'au-delà, c'est qu'il y a un souci... +#define MODEM_MAX_TAILLE_TRAME_FTPGET 1024 // Cette valeur est liée au modem mais aucune doc (ni commande) ne donne la valeur exacte... +#define MODEM_MAX_TAILLE_MESSAGE_SMS 170 // A priori, 140 suffit mlais il y a un flou sur les 160 +#define MODEM_MAX_TAILLE_NUMERO_EMETTEUR_SMS 18 // De la forme '+33 6 xx xx xx xx' + + // Tempos : +#define MODEM_PERIODE_SURVEILLANCE_MODEM 10 // En x 100ms +#define MODEM_TEMPO_APRES_RESET_SOFT 10 // En x 100ms +#define MODEM_TEMPO_ATTENTE_CHANGEMENT_ETAT 5 // En x 100ms Cette tempo est à relier au MAX_REPETITIONS_TEMPO_ATTENTE_ACTIVATION ci-dessus. +#define MODEM_TEMPO_ATTENTE_RESET 120 // En x 100ms Il ne semble pas y avoir de timing précis sur ce sujet mais en essayant avec différentes valeurs, il faut attendre au moins 6-8 secondes pour qu les premières commandes ATE passent correctement... +#define MODEM_TEMPO_CHECK_INIT_LIAISON 10 // En x 100ms Cette tempo n'est pas critique (mais de préférence supérieure aux répétitions x time-out = 900ms) car on boucle plusieurs fois tant qu'on n'a pas reçu un OK suite à la configuration de l'echo. Et si au bout du nombre de rtépétition on n'a pas réussi à établir la liaison, on réinitialise le modem. +#define MODEM_TEMPO_IMPULSION_CHANGEMENT_ETAT 15 // En x 100ms (Tmin = 1s selon datasheet du SIM7070G, page 26) +#define MODEM_TEMPO_IMPULSION_RESET 150 // En x 100ms (Tmin = 12s selon datasheet du SIM7070G, page 23) +#define MODEM_TEMPO_INIT 80 // En x 100ms Durée volontairement longue pour s'assurer qu'aucun changement d'état du modem n'est plus en cours au moment du démarrage effectif (évite de se retrouver à contre-temps et avoir besoin de plusieurs boucles pour se repositionner) +#define MODEM_TIME_OUT_ATTENTE_FIN_INIT 1200 // En x 100ms La valeur de retour de '+APP PDP: 0,ACTIVE' est très variable. Souvent de 600-800ms mais parfois de 20 voire 30s. Dans tous les cas, il est important d'attendre patiemment sous peine d'empiler les erreurs... + + +// Attention : les valeurs de MODEM_GNSS_PERIODE_ACQUISITION, MODEM_UPLOAD_PERIODE_CHECK +// et MODEM_DOWNLOAD_PERIODE_CHECK sont bien exécutées en secondes. MAIS le temps de traitement +// de chaque tâche (GNSS, Upload et Download) fige les 3 compteurs pendant son exécution. Cela +// implique que sur une période longue, le moment de déclenchement effectif ne peut pas être +// pré-estimé. Ce n'est a priori pas gênant (au contraire car cela mixe les timings de connexion), +// mais cela peut surprendre qu'en demandant un Upload toutes les heures à partir de 12h00, on n'ai +// pas le prochain à 13h00 et ainsi de suite. Ce sera probablement 13h01 ou 13h02 puis... 18h07 par +// exemple. + +#define MODEM_GNSS_PERIODE_ACQUISITION_SANS_4G_LTE 120 // En secondes Attention : la valeur réelle est obtenue par : [(MODEM_GNSS_PERIODE_ACQUISITION * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM] x [MODEM_PERIODE_SURVEILLANCE_MODEM x 100ms] +#define MODEM_GNSS_PERIODE_ACQUISITION_AVEC_4G_LTE 300 // En secondes Attention : la valeur réelle est obtenue par : [(MODEM_GNSS_PERIODE_ACQUISITION * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM] x [MODEM_PERIODE_SURVEILLANCE_MODEM x 100ms] +#define MODEM_GNSS_TEMPO_ATTENTE_COUPURE_4G_LTE 20 // En x 100ms Tempo entre la coupure de la 4G-LTE et l'alimentation du GNSS. +#define MODEM_GNSS_TEMPO_ATTENTE_ACTIVATION 50 // En x 100ms Tempo entre l'activation du GNSS et la première commande de lecture de localisation. Si la durée n'est pas suffisante, ce n'est pas grave (on commence alors à interroger le modem mais s'il n'a pas de data, on boucle) +#define MODEM_GNSS_TIME_OUT_ATTENTE_LECTURE 20 // En x 100ms Time-out entre l'envoi de la commande de lecture de la localisation et la réponse (en général, très rapide, quitte à recevoir une trame sans data si elles ne sont pas dispos) +#define MODEM_GNSS_TEMPO_ENTRE_LECTURES 10 // En x 100ms + +#define MODEM_HTTP_GET_PERIODE_CHECK 3602 // En secondes Attention : la valeur réelle est obtenue par : [(MODEM_HTTP_GET_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM] x [MODEM_PERIODE_SURVEILLANCE_MODEM x 100ms] +#define MODEM_HTTP_GET_REPETITIONS_SHCONN 1 // Il est inutile de répéter plus d'une fois car le délai est très long... +#define MODEM_HTTP_GET_TIME_OUT_SHCONN 200 // En x 100ms Time-out de réponse à une commande de connexion SHCONN. +#define MODEM_HTTP_GET_REPETITIONS_SHREQ 1 // Il est inutile de répéter plus d'une fois car le délai est très long... +#define MODEM_HTTP_GET_TIME_OUT_SHREQ 600 // En x 100ms Time-out de réponse à une commande GET. +#define MODEM_HTTP_GET_REPETITIONS_SHREAD 1 // Il est inutile de répéter plus d'une fois car le délai est très long... +#define MODEM_HTTP_GET_TIME_OUT_SHREAD 600 // En x 100ms Time-out de réponse à une commande GET. + + + +#define MODEM_UPLOAD_PERIODE_CHECK 600 // En secondes Attention : la valeur réelle est obtenue par : [(MODEM_UPLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM] x [MODEM_PERIODE_SURVEILLANCE_MODEM x 100ms] +#define MODEM_UPLOAD_TEMPO_DEMANDE_ENVOI_DATA 0 // En x 100ms Cette tempo permet d'attendre entre plusieurs phases de FTPPUT. Si ce délai est trop court, on génère des erreurs (qui se récupèrent mais qui, finalement, font échouer le transfert du fichier) +#define MODEM_UPLOAD_TEMPO_ATTENTE_ACCEPTATION_DONNEES 20 // En x 100ms Tempo entre la commande de PUT avec la taille du paquet et l'envoi du paquet (mesuré à 100ms environ) +#define MODEM_UPLOAD_TIME_OUT_ENVOI_DONNEES 50 // En x 100ms Tempo entre l'envoi du paquet de données et la recherche d'un autre paquet (mesuré à 125ms environ pour 1360 octets ; l'essentiel de ce temps étant l'émission des données => entre le dernier octet de données et l'Ack du modem, il y a à peine 5-6ms) +#define MODEM_UPLOAD_TEMPO_ATTENTE_FIN_TRANSFERT 500 // En x 100ms Tempo entre l'envoi de la demande de fermeture de port FTP (FTP=2,0) et la réception que le port est bien fermé +#define MODEM_UPLOAD_TEMPO_ATTENTE_OUVERTURE_SESSION 800 // En x 100ms Tempo entre l'envoi de la demande de connexion FTP et l'envoi des données (le time-out du modem est de 75s mais mesuré 1.6s....) +#define MODEM_UPLOAD_TIME_OUT_ATTENTE_GETSIZE 500 // En x 100ms Tempo d'attente après l'envoi de la commande de la commande FTPSIZE (mesurée à 1.2-1.3s). La tempo est remise à 0 dès réception de la réponse. + +#define MODEM_DOWNLOAD_PERIODE_CHECK 1200 // En secondes Attention : la valeur réelle est obtenue par : [(MODEM_DOWNLOAD_PERIODE_CHECK * 10) / MODEM_PERIODE_SURVEILLANCE_MODEM] x [MODEM_PERIODE_SURVEILLANCE_MODEM x 100ms] +#define MODEM_DOWNLOAD_TEMPO_DEMANDE_DATA 2 // En x 100ms Cette tempo permet d'attendre un peu entre la réception de la réponse à 'FTPGET=1' et la demande proprement-dite 'FTPGET=2,NB'. Si ce délai est trop court, on génère des erreurs (qui se récupèrent mais qui, finalement, font perdre du temps) +#define MODEM_DOWNLOAD_TEMPO_DEMANDE_LISTE 2 // En x 100ms Cette tempo permet d'attendre un peu entre la réception de la réponse à 'FTPLIST=1' et la demande proprement-dite 'FTPLIST=2,NB'. Si ce délai est trop court, on génère des erreurs (qui se récupèrent mais qui, finalement, font perdre du temps) +#define MODEM_DOWNLOAD_TIME_OUT_ATTENTE_DISPO_DATA 800 // En x 100ms Tempo entre l'envoi de la demande de connexion FTP et l'envoi des données (le time-out du modem est de 75s....) +#define MODEM_DOWNLOAD_TIME_OUT_ATTENTE_INFO 500 // En x 100ms +#define MODEM_DOWNLOAD_TIME_OUT_ATTENTE_LISTE 500 // En x 100ms +#define MODEM_DOWNLOAD_TIME_OUT_ATTENTE_RECEPTION_DATA 500 // En x 100ms + + + // Fonctionnement GNSS : +#define MODEM_GNSS_MAX_NOMBRE_CYCLES 180 // Nombre de cycles d'acquisition avant d'abandonner. Il vaut mieux que cette valeur x MODEM_GNSS_TEMPO_ENTRE_LECTURES soit < MODEM_GNSS_PERIODE_ACQUISITION +#define MODEM_GNSS_NOMBRE_CYCLES_VALIDES 20 // Nombre de cycles valides (pas forcément consécutifs) sur une même période avant de considérer la période comme terminée. Cette valeur est importante pour avoir une précision suffisante... + + + // Etat HTTP : +#define MODEM_HTTP_ETAT_REPOS 0 +#define MODEM_HTTP_ETAT_DEMANDE 1 +#define MODEM_HTTP_ETAT_EN_COURS 2 +#define MODEM_HTTP_ETAT_TERMINE 3 + + + // Demande de connexion par l'utilisateur : +#define MODEM_DEMANDE_UTILISATEUR_AUCUNE 0 +#define MODEM_DEMANDE_UTILISATEUR_DOWNLOAD 1 +#define MODEM_DEMANDE_UTILISATEUR_UPLOAD 2 +#define MODEM_DEMANDE_UTILISATEUR_DOWNLOAD_UPLOAD 3 + +#define MODEM_DEMANDE_UTILISATEUR_TEMPO_MINI 2 // En seconde. Indique la valeur à laquelle sera ramené le compteur de download et/ou upload lors de la demande (NB : si la valeur courante est inférieure, elle ne sera pas modifiée) + + // Demandes FTP : +// #define MODEM_DEMANDE_FTP_AUCUNE 0 +// #define MODEM_DEMANDE_FTP_GET 1 +// #define MODEM_DEMANDE_FTP_PUT 2 + + + // Etats du séquenceur Modem : +#define ETAT_SEQUENCEUR_MODEM_INIT 0 +#define ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_INIT 1 +#define ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_IMPULSION 2 +#define ETAT_SEQUENCEUR_MODEM_CHANGEMENT_ETAT_ATTENTE_STATUS 3 +#define ETAT_SEQUENCEUR_MODEM_INIT_LIAISON 4 +#define ETAT_SEQUENCEUR_MODEM_COMMANDES_INIT 5 +#define ETAT_SEQUENCEUR_MODEM_ATTENTE_FIN_INIT 6 +#define ETAT_SEQUENCEUR_MODEM_REPOS 7 + +#define ETAT_SEQUENCEUR_MODEM_GNSS_ACTIVATION 10 +#define ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_COUPURE_4G_LTE 11 +#define ETAT_SEQUENCEUR_MODEM_GNSS_ATTENTE_LECTURE 12 +#define ETAT_SEQUENCEUR_MODEM_GNSS_LECTURE 13 + +#define ETAT_SEQUENCEUR_MODEM_HTTP_GET_LANCEMENT 20 +#define ETAT_SEQUENCEUR_MODEM_HTTP_GET_ATTENTE_CONNEXION 21 +#define ETAT_SEQUENCEUR_MODEM_HTTP_GET_ATTENTE_RETOUR_REQUETE 22 +#define ETAT_SEQUENCEUR_MODEM_HTTP_GET_ATTENTE_RETOUR_DATA 23 +#define ETAT_SEQUENCEUR_MODEM_HTTP_GET_CLOTURE 24 + +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_LANCEMENT 30 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_INFORMATION 31 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_LISTE 32 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_RECHERCHE_FICHIER 33 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_DEBUT_FICHIER 34 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_DISPO_DATA 35 +#define ETAT_SEQUENCEUR_MODEM_FTP_GET_ATTENTE_RECEPTION_DATA 36 + +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT 40 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_OUVERTURE_SESSION 41 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_ACCEPTATION_DONNEES 42 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FIN_TRANSFERT 43 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_LANCEMENT_CHECK 44 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_GETSIZE 45 +#define ETAT_SEQUENCEUR_MODEM_FTP_PUT_ATTENTE_FERMETURE_SESSION 46 + + +#define ETAT_SEQUENCEUR_MODEM_DEBUT_IMPULSION_RESET 90 +#define ETAT_SEQUENCEUR_MODEM_FIN_IMPULSION_RESET 91 + + + + diff --git a/sources/nmea.c b/sources/nmea.c new file mode 100644 index 0000000..6f51707 --- /dev/null +++ b/sources/nmea.c @@ -0,0 +1,3083 @@ +//-----------------------------------------------------------------------------***************************************************************************************************** +// Fichier NMEA.C +// +// Contient toutes les fonctions liées à la gestion des PGN NMEA provenant du Compagnon +// !!!!! Pour intégrer un nouveau PGN, voir PROCEDURE_PGN ci-dessous !!!!! +//-----------------------------------------------------------------------------*****************************************************************************************************/ + +//#define __DEBUG_NMEA //Décommenter cette ligne pour avoir les messages de debug + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_nmea(void); +void gestion_nmea(void); +int8_t traitement_pgn_compagnon(struct Struct_pgn_nmea pgn); + +// Variables globales : +struct Struct_nmea nmea; + + +/********************************************************************************************************************************************************************************** + Fonction INIT_NMEA + + Fonction d'initialisation du réseau NMEA +**********************************************************************************************************************************************************************************/ +int8_t init_nmea(void) +{ + + nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_INIT; + nmea.sequenceur.compteur = NMEA_TEMPO_INIT; + nmea.sequenceur.compteur_time_out = 0; + + init_annuaire_nmea(); // Contrairement aux autres annuaires (k-bus, Ow), il n'est pas nécessaire d'attendre l'initialisation dans 'systeme.c' + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation du réseau NMEA\t: OK", TYPE_MESSAGE_DEBUG_OK); + + return(0); +} + + +//================================================================================================================================================================== +// Fonction : Appelée en tâche de fond, cette fonction gère le réseau NMEA dans son ensemble +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void gestion_nmea(void) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + switch(nmea.sequenceur.etat) + { + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_NMEA_INIT : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + if (nmea.sequenceur.compteur == 0) + { + nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_REPOS; + } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_NMEA_REPOS : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { +// if (nmea.sequenceur.compteur == 0) +// { +// nmea.sequenceur.compteur = NMEA_PERIODE_CHECK; // A priori, toutes les 100ms +// } + + break; + } + + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + default : + //---------------------------------------------------------------------------------------------------------------------------------------------------------- + { + sprintf(tmp, "L'état du séquenceur NMEA est inconnu [%i]. Il est repositionné au REPOS", nmea.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + nmea.sequenceur.etat = ETAT_SEQUENCEUR_NMEA_REPOS; + break; + } + } + +} + + +//================================================================================================================================================================== +// Fonction : Appelée lors de la réception d'une commande 'c_bus_up_commande_etat_pgn_nmea()' dans 'c_commandes.c', cette fonction traite un PGN reçu du Compagnon +// Reçoit : Le PGN à traiter +// Renvoit : 0 si traitement Ok, -1 si erreur. +//================================================================================================================================================================== +int8_t traitement_pgn_compagnon(struct Struct_pgn_nmea pgn) +{ +uint16_t i, j; +bool flag_pgn_present_dans_annuaire; +bool flag_pgn_present_dans_catalogue; +uint16_t indice_catalogue; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + //----------------------------------------------------------------------------------------- + // PGN déjà présent dans l'annuaire : + //----------------------------------------------------------------------------------------- + flag_pgn_present_dans_annuaire = false; + for (i = 0; i < NMEA_MAX_NOMBRE_PGN; i++) + { + if (!nmea.annuaire[i].flag_validite) continue; + + if (nmea.annuaire[i].pgn == pgn.pgn) + { + flag_pgn_present_dans_annuaire = true; + + // Mise à jour des dernières valeurs dans l'annuaire : + nmea.annuaire[i].priority = pgn.priority; + nmea.annuaire[i].source = pgn.source; + nmea.annuaire[i].timestamp = pgn.timestamp; + nmea.annuaire[i].lg_data = pgn.lg_data; + for (j = 0; j < pgn.lg_data; j ++) nmea.annuaire[i].data[j] = pgn.data[j]; + + //--------------------------- + // PGN Actif : + //--------------------------- + if (nmea.annuaire[i].actif == 1) + { + for (j = 0; ((j < NMEA_MAX_NOMBRE_PGN) && (catalogue_pgns[j].pgn != 0)); j ++) // Recherche des infos de config dans le catalogue (le PGN s'y trouve forcément car il a été créé dans l'annuaire...) + { + if (catalogue_pgns[j].pgn == pgn.pgn) // Le PGN a été trouvé dans le catalogue + { + // ------------------------------ + // Si le PGN a déjà été détecté par le Compagnon (il est donc complètement configuré sur l'Appli et sur le Compagnon) : + if (nmea.annuaire[i].flag_presence) + { + catalogue_pgns[j].fonction_parse_pgn(i, NMEA_PARSE_MODE_MISE_A_JOUR); // Appel de la fonction de parsing de ce PGN en mode MISE A JOUR. + } + + //------------------------------- + // Premier retour du Compagnon sur ce PGN : + else + { +#ifdef __DEBUG_NMEA + sprintf(tmp, "PGN %i : Ce PGN est Actif et c'est sa première apparition depuis le reset du Compagnon. Passage à Présent + config du Compagnon", nmea.annuaire[i].pgn); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + c_bus_down_commande_config_pgn_nmea(i); + nmea.annuaire[i].flag_presence = true; + } + + break; + } + } + } + + //--------------------------- + // PGN Inactif : + //--------------------------- + else if (nmea.annuaire[i].actif == 0) + { + enregistrement_date_pgn_nmea(i); + } + + //--------------------------- + // PGN désactivé car non connu dans le catalogue : + //--------------------------- + else ; + + break; // Il est inutile de poursuivre lorsque le PGN a été trouvé. + } + } + + if (flag_pgn_present_dans_annuaire) return (0); // Le traitement est terminé... + + + //----------------------------------------------------------------------------------------- + // PGN pas encore présent dans l'annuaire : + //----------------------------------------------------------------------------------------- + + //---------------------------------- + // PGN présent dans le catalogue ? : + flag_pgn_present_dans_catalogue = false; + for (i = 0; ((i < NMEA_MAX_NOMBRE_PGN) && (catalogue_pgns[i].pgn != 0)); i ++) + { + if (catalogue_pgns[i].pgn == pgn.pgn) // Le PGN a été trouvé dans le catalogue : + { + flag_pgn_present_dans_catalogue = true; + indice_catalogue = i; + + break; // Sortie de la boucle du catalogue puisqu'on a trouvé l'entrée. + } + } + + //---------------------------------- + // Ajout à l'annuaire : + for (i = 0; i < NMEA_MAX_NOMBRE_PGN; i ++) // On recherche ensuite le premier emplacement libre : + { + if (!nmea.annuaire[i].flag_validite) // L'emplacement est libre. + { + nmea.annuaire[i].flag_validite = true; + nmea.annuaire[i].flag_presence = true; + nmea.annuaire[i].pgn = pgn.pgn; + + //----------------------------------------------- + // PGN présent dans le catalogue : + //----------------------------------------------- + if (flag_pgn_present_dans_catalogue) + { + nmea.annuaire[i].actif = 0; // Par défaut, le PGN est Inactif puisqu'il ne pourra être passé à Actif que par Qt (qui a d'abord besoin qu'il soit créé...) + nmea.annuaire[i].priority = pgn.priority; + nmea.annuaire[i].source = pgn.source; + nmea.annuaire[i].timestamp = pgn.timestamp; + nmea.annuaire[i].type = catalogue_pgns[indice_catalogue].type; // Pas utile tout de suite (car Actif = 0) mais permet de ne pas le faire lors de l'activation qui se fait à plusieurs endroits (et de l'oublier - vécu...) + nmea.annuaire[i].periode_mini = catalogue_pgns[indice_catalogue].periode_mini; // Idem... + nmea.annuaire[i].lg_data = 0; +#ifdef __DEBUG_NMEA + sprintf(tmp, "Nouveau PGN enregistré en position %i : %i", i, nmea.annuaire[i].pgn); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + catalogue_pgns[indice_catalogue].fonction_parse_pgn(i, NMEA_PARSE_MODE_CREATION); // Appel de la fonction de parsing de ce PGN en mode CREATION. + // NB : on ne fait rien vers le Compagnon car, par défaut, les Fields de ce PGN sont tous créés en mode Inactifs. + } + + //----------------------------------------------- + // PGN absent du catalogue : + //----------------------------------------------- + else + { + nmea.annuaire[i].actif = -1; // NB : on garde le PGN dans l'annuaire pour qu'on le repère comme étant Désactivé (Actif = -1) à l'avenir. + +#ifdef __DEBUG_NMEA + sprintf(tmp, "Le PGN %i n'est pas référencé dans le catalogue NMEA2000. Il est donc désactivé (aucun traitement à l'avenir)...", pgn.pgn); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + + c_bus_down_commande_config_pgn_nmea(i); // Commande de désactivation (Actif = -1) vers le Compagnon pour qe ce dernier ne le traite plus à l'avenir. + + return (-1); + } + + break; // Sortie de la boucle de recherche d'un emplacement libre. + } + } + + //----------------------------------------------- + // Nombre de PGN trop important : + //----------------------------------------------- + if (i == NMEA_MAX_NOMBRE_PGN) + { + sprintf(tmp, "Le nombre maximal de PGN a été atteint (%i). Le PGN %i n'est pas traité...", NMEA_MAX_NOMBRE_PGN, pgn.pgn); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + return(0); +} + + +// PROCEDURE_PGN : +// +// Pour intégrer un nouveau PGN, il faut : +// 1- Dans 'nmea.h', il faut créer les n° de Field qui seront utilisés par ShipHeart. L'affectation des n° est théoriquement libre mais, par principe, on réutilise +// les n° utilisés dans la doc NMEA (Liste PGN NMEA 2024 - V3.002 au moment d'écrire ces lignes) +// 2- Créer une fonction de parsing du type 'Parse_pgn_xxxxxx()'. +// Le plus simple est de dupliquer la fonction template et remplacer xxxxxx par le code du PGN. +// Renseigner la longueur minimale (LG_DATA_MINI) que la trame doit faire pour pouvoir être traitée (a minima, jusqu'au dernier field qui sera décodé car aucun contrôle n'est fait ensuite). +// Dans le case NMEA_PARSE_MODE_CREATION, ajouter les appels correspondant aux fields que l'on souhaite intégrer. Penser à renseigner la désignation (celle +// qui sera affichée par défaut dans la liste des PGNs sur Qt - en Anglais). +// Dans le case NMEA_PARSE_MODE_MISE_A_JOUR, ajouter les différents fields dans l'ordre de lecture. Généralement, après SID. Prendre exemple sur les PGN existants +// pour dupliquer un traitement approchant. IMPORTANT : la valeur retournée DOIT être un nombre (float). +// 3- Ajouter le nouveau PGN dans le "Catalogue des PGNs" (plus bas, dans ce fichier). +// Ajouter une ligne dans l'ordre chrono des PGNs. Les valeurs à renseigner sont : +// - le code du Pgn, +// - le Type (pour cela, s'aider de la liste située en dessous, en commentaires), +// - La Période mini (x10ms). Elle permet de "limiter" les remontées de données de la part du Compagnon et, ainsi, limiter la charge (du Compagnon, de l'Appli, etc...) +// - la fonction de parsing créée au 2. +// Important : terminer la liste par une entrée avec le code de Pgn nul. C'est cette ligne qui est détectée pour terminer la liste. +// La taille maxi du catalogue est NMEA_MAX_NOMBRE_PGN mais un dépassement sera détecté par la compilation. +// 4- Compiler. +// 5- Tester avec le simulateur. +// 6- Lorsque les tests sont concluants, commenter le #define '#define DEBUG_PGN_xxxxxx' pour ne pas surcharger la sortie de debug. + + + +//-----------------------------------------------------------------------------************************************************************************************** +//-----------------------------------------------------------------------------************************************************************************************** +// +// Fonctions de Parsing de PNG : +// +//-----------------------------------------------------------------------------************************************************************************************** +//-----------------------------------------------------------------------------************************************************************************************** + +// L'aide sur les fonctions de parsing peut se trouver ici : https://ttlappalainen.github.io/NMEA2000/group__group__msg_parsers.html#ga3472b7dcf7ece7265708e20ee8bcf78a +// Rechercher sur la gauche le N° de PGN... + +// Extrait de 'shipheart_sql.sql' : +// (50,2, "Capt_sans_unite", "Sans unité", " ", 1, 0, 1, "2020-01-01 00:00:00"), +// (51,2, "Capt_tension_v", "Tension", "V", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (52,2, "Capt_tension_mv", "Tension", "mV", 10, 0, 1, "2020-01-01 00:00:00"), +// (53,2, "Capt_courant_a", "Intensité", "A", 0.01, 0, 1, "2020-01-01 00:00:00"), +// (54,2, "Capt_courant_ma", "Intensité", "mA", 0.5, 0, 1, "2020-01-01 00:00:00"), +// (55,2, "Capt_puissance_w", "Puissance", "W", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (56,2, "Capt_puissance_va", "Puissance apparente", "VA", 1, 0, 1, "2020-01-01 00:00:00"), +// (57,2, "Capt_puissance_db", "Puissance", "dB", 0.2, 0, 1, "2020-01-01 00:00:00"), +// (58,2, "Capt_frequence", "Fréquence", "Hz", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (59,2, "Capt_resistance", "Résistance", "Ohms", 1, 0, 1, "2020-01-01 00:00:00"), +// (60,2, "Capt_temperature", "Température", "°C", 0.1, 2, 1, "2020-01-01 00:00:00"), +// (61,2, "Capt_pression_atm", "Pression atmosphérique", "hPa", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (62,2, "Capt_humidite", "Humidité", "%HR", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (63,2, "Capt_acceleration", "Accélération", "g", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (64,2, "Capt_position", "Position", "°", 0.0001, 0, 1, "2020-01-01 00:00:00"), +// (65,2, "Capt_pourcentage", "Pourcentage", "%", 1, 0, 1, "2020-01-01 00:00:00"), +// (66,2, "Capt_distance_m", "Distance", "m", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (67,2, "Capt_distance_km", "Distance", "km", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (68,2, "Capt_distance_nq", "Distance", "nq", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (69,2, "Capt_vitesse_kts", "Vitesse", "kts", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (70,2, "Capt_vitesse_kmh", "Vitesse", "km/h", 0.1, 0, 1, "2020-01-01 00:00:00"), +// (71,2, "Capt_angle_degres", "Angle", "°", 1, 0, 1, "2020-01-01 00:00:00"); + + + +//================================================================================================================================================================== +// PGN : 126992 +// Fonction : System date/time, pri=3, period=1000 +//================================================================================================================================================================== +int8_t Parse_pgn_126992(uint16_t rang, uint8_t mode) +{ +// #define DEBUG_PGN_126992 +const uint8_t LG_DATA_MINI = 8; +uint8_t index; +uint8_t val_u8; +uint16_t val_u16; +uint32_t val_u32; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (nmea.annuaire[rang].pgn != 126992) return (-1); + + switch (mode) + { + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_CREATION: + { + creation_pgn_field_nmea(rang, NMEA_126992_TIME_SOURCE, "Capt_sans_unite", 0, 0, "Time source"); + creation_pgn_field_nmea(rang, NMEA_126992_SYSTEM_DATE, "Capt_sans_unite", 0, 0, "System date"); + creation_pgn_field_nmea(rang, NMEA_126992_SYSTEM_TIME, "Capt_sans_unite", 0, 0, "System time"); + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_MISE_A_JOUR: + { + if (nmea.annuaire[rang].lg_data < LG_DATA_MINI) + { + sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + return (-1); + } + + index = 0; + + //---------------------------------- + // Sequence ID : + index ++; + + //---------------------------------- + // Time source : + val_u8 = nmea.annuaire[rang].data[index ++]; + enregistrement_pgn_field_nmea(rang, NMEA_126992_TIME_SOURCE, (val_u8 & 0x0F) ); +#ifdef DEBUG_PGN_126992 +sprintf(tmp, "PGN %i : Time source = %i", nmea.annuaire[rang].pgn, (val_u8 & 0x0F) ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // System Date : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; + enregistrement_pgn_field_nmea(rang, NMEA_126992_SYSTEM_DATE, val_u16); +#ifdef DEBUG_PGN_126992 +sprintf(tmp, "PGN %i : System Date = %i", nmea.annuaire[rang].pgn, val_u16); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // System Time : + val_u32 = (uint32_t) nmea.annuaire[rang].data[index ++]; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 8; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 16; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 24; + enregistrement_pgn_field_nmea(rang, NMEA_126992_SYSTEM_TIME, ((float) val_u32 * 0.0001) ); +#ifdef DEBUG_PGN_126992 +sprintf(tmp, "PGN %i : System Time = %.4f", nmea.annuaire[rang].pgn, ((float) val_u32 * 0.0001)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_SUPPRESSION: + { + suppression_pgn_nmea(rang); + + break; + } + + //------------------------------------------------------------------------------ + default: break; + } + + return (0); +} + + +//================================================================================================================================================================== +// PGN : 129029 +// Fonction : GNSS Position Data, pri=3, period=1000 +//================================================================================================================================================================== +int8_t Parse_pgn_129029(uint16_t rang, uint8_t mode) +{ +// #define DEBUG_PGN_129029 +const uint8_t LG_DATA_MINI = 38; +uint8_t index; +uint8_t val_u8; +uint16_t val_u16; +uint32_t val_u32; +uint64_t val_u64; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (nmea.annuaire[rang].pgn != 129029) return (-1); + + switch (mode) + { + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_CREATION: + { + creation_pgn_field_nmea(rang, NMEA_129029_DAYS_SINCE_1970, "Capt_sans_unite", 0, 0, "Numer of days since January 1st, 1970"); + creation_pgn_field_nmea(rang, NMEA_129029_SECONDS_SINCE_MIDNIGHT, "Capt_sans_unite", 0, 0, "Number of seconds since midnight"); + creation_pgn_field_nmea(rang, NMEA_129029_LATITUDE, "Capt_latitude", 0, 0, "Latitude"); + creation_pgn_field_nmea(rang, NMEA_129029_LONGITUDE, "Capt_longitude", 0, 0, "Longitude"); + creation_pgn_field_nmea(rang, NMEA_129029_GNSS_TYPE, "Capt_sans_unite", 0, 0, "GNSS Type"); + creation_pgn_field_nmea(rang, NMEA_129029_GNSS_METHOD, "Capt_sans_unite", 0, 0, "GNSS Method"); + creation_pgn_field_nmea(rang, NMEA_129029_SATELLITES_NUMBER, "Capt_sans_unite", 0, 0, "Satellites number"); + creation_pgn_field_nmea(rang, NMEA_129029_HDOP, "Capt_sans_unite", 1, 2, "HDOP"); + creation_pgn_field_nmea(rang, NMEA_129029_PDOP, "Capt_sans_unite", 1, 2, "PDOP"); + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_MISE_A_JOUR: + { + if (nmea.annuaire[rang].lg_data < LG_DATA_MINI) + { + sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + return (-1); + } + + index = 0; + + //---------------------------------- + // Sequence ID : + index ++; + + //---------------------------------- + // Number of days since 1970 : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; + enregistrement_pgn_field_nmea(rang, NMEA_129029_DAYS_SINCE_1970, val_u16); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Number of days since 1970 = %i", nmea.annuaire[rang].pgn, val_u16); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Seconds since midnight : + val_u32 = (uint32_t) nmea.annuaire[rang].data[index ++]; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 8; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 16; + val_u32 |= ((uint32_t) nmea.annuaire[rang].data[index ++]) << 24; + enregistrement_pgn_field_nmea(rang, NMEA_129029_SECONDS_SINCE_MIDNIGHT, ((float) val_u32 * 0.0001) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Number of seconds since midnight = %.4f", nmea.annuaire[rang].pgn, ((float) val_u32 * 0.0001)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Latitude : + val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++]; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56; + enregistrement_pgn_field_nmea(rang, NMEA_129029_LATITUDE, ((float) val_u64) * (1e-16) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Latitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-16) ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Longitude : + val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++]; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56; + enregistrement_pgn_field_nmea(rang, NMEA_129029_LONGITUDE, ((float) val_u64) * (1e-16) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Longitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-16)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Altitude : + val_u64 = (uint64_t) nmea.annuaire[rang].data[index ++]; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 8; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 16; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 24; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 32; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 40; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 48; + val_u64 |= ((uint64_t) nmea.annuaire[rang].data[index ++]) << 56; + enregistrement_pgn_field_nmea(rang, NMEA_129029_ALTITUDE, ((float) val_u64) * (1e-6) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Altitude = %f", nmea.annuaire[rang].pgn, ((float) val_u64) * (1e-6)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // GNSS Type et GNSS Methode : + val_u8 = nmea.annuaire[rang].data[index ++]; + enregistrement_pgn_field_nmea(rang, NMEA_129029_GNSS_TYPE, (val_u8 & 0x0F) ); + enregistrement_pgn_field_nmea(rang, NMEA_129029_GNSS_METHOD, ((val_u8 >> 4) & 0x0F) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : GNSS Type = %i", nmea.annuaire[rang].pgn, (val_u8 & 0x0F) ); +sprintf(tmp, "PGN %i : GNSS Method = %i", nmea.annuaire[rang].pgn, ((val_u8 >> 4) & 0x0F) ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Integrity 2 bit, reserved 6 bits : + index ++; + + //---------------------------------- + // Nb satellites : + val_u8 = nmea.annuaire[rang].data[index ++]; + enregistrement_pgn_field_nmea(rang, NMEA_129029_SATELLITES_NUMBER, val_u8 ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : Satellites Number = %i", nmea.annuaire[rang].pgn, val_u8 ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // HDOP : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; + enregistrement_pgn_field_nmea(rang, NMEA_129029_HDOP, ((float) val_u16 * 0.01) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : HDOP = %.4f", nmea.annuaire[rang].pgn, ((float) val_u16 * 0.01)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // PDOP : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; + enregistrement_pgn_field_nmea(rang, NMEA_129029_PDOP, ((float) val_u16 * 0.01) ); +#ifdef DEBUG_PGN_129029 +sprintf(tmp, "PGN %i : PDOP = %.4f", nmea.annuaire[rang].pgn, ((float) val_u16 * 0.01)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Non traités : + // GeoidalSeparation=N2kMsg.Get4ByteDouble(0.01,Index); + // nReferenceStations=N2kMsg.GetByte(Index); + // if (nReferenceStations!=N2kUInt8NA && nReferenceStations>0) + // { + // Note that we return real number of stations, but we only have variabes for one. + // vi=N2kMsg.Get2ByteUInt(Index); + // ReferenceStationType=(tN2kGNSStype)(vi & 0x0f); + // ReferenceSationID=(vi>>4); + // AgeOfCorrection=N2kMsg.Get2ByteUDouble(0.01,Index); + // } + // else + // { + // ReferenceStationType = N2kGNSSt_GPS; + // ReferenceSationID = N2kInt16NA; + // AgeOfCorrection = N2kDoubleNA; + // } + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_SUPPRESSION: + { + suppression_pgn_nmea(rang); + + break; + } + + //------------------------------------------------------------------------------ + default: break; + } + + return (0); +} + + + +//================================================================================================================================================================== +// PGN : 129540 +// Fonction : GNSS Sats in View +//================================================================================================================================================================== +int8_t Parse_pgn_129540(uint16_t rang, uint8_t mode) +{ +//#define DEBUG_PGN_129540 +const uint8_t LG_DATA_MINI = 3; +uint8_t index; +uint8_t val_u8; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (nmea.annuaire[rang].pgn != 129540) return (-1); + + switch (mode) + { + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_CREATION: + { + creation_pgn_field_nmea(rang, NMEA_129540_SATELLITES_IN_VIEW, "Capt_sans_unite", 0, 0, "Satellites in view"); + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_MISE_A_JOUR: + { + if (nmea.annuaire[rang].lg_data < LG_DATA_MINI) + { + sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + return (-1); + } + + index = 0; + + //---------------------------------- + // Sequence ID : + index ++; + + //---------------------------------- + // Mode : + index ++; + + //---------------------------------- + // Satellites in view : + val_u8 = nmea.annuaire[rang].data[index ++]; + enregistrement_pgn_field_nmea(rang, NMEA_129540_SATELLITES_IN_VIEW, val_u8 ); +#ifdef DEBUG_PGN_129540 +sprintf(tmp, "PGN %i : Satellites in view = %i", nmea.annuaire[rang].pgn, val_u8 ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_SUPPRESSION: + { + suppression_pgn_nmea(rang); + + break; + } + + //------------------------------------------------------------------------------ + default: break; + } + + return (0); +} + + +//================================================================================================================================================================== +// PGN : 130306 +// Fonction : Wind Data +//================================================================================================================================================================== +int8_t Parse_pgn_130306(uint16_t rang, uint8_t mode) +{ +// #define DEBUG_PGN_130306 +const uint8_t LG_DATA_MINI = 6; +uint8_t index; +uint16_t val_u16; +uint8_t wind_ref; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + if (nmea.annuaire[rang].pgn != 130306) return (-1); + + switch (mode) + { + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_CREATION: + { + creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_NORTH, "Capt_vitesse_kts", 1, 1, "Wind Speed [True North]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_NORTH, "Capt_angle_degres", 0, 1, "Wind Angle [True North]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_MAGNETIC, "Capt_vitesse_kts", 1, 1, "Wind Speed [Magnetic]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_MAGNETIC, "Capt_angle_degres", 0, 1, "Wind Angle [Magnetic]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_APPARENT, "Capt_vitesse_kts", 1, 1, "Wind Speed [Apparent]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_APPARENT, "Capt_angle_degres", 0, 1, "Wind Angle [Apparent]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_BOAT, "Capt_vitesse_kts", 1, 1, "Wind Speed [True boat]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_BOAT, "Capt_angle_degres", 0, 1, "Wind Angle [True boat]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_SPEED_TRUE_WATER, "Capt_vitesse_kts", 1, 1, "Wind Speed [True water]"); + creation_pgn_field_nmea(rang, NMEA_130306_WIND_ANGLE_TRUE_WATER, "Capt_angle_degres", 0, 1, "Wind Angle [True water]"); + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_MISE_A_JOUR: + { + if (nmea.annuaire[rang].lg_data < LG_DATA_MINI) + { + sprintf(tmp, "La longueur de DATA du PGN %i n'est pas correcte pour être traitée en mode MISE A JOUR (reçu : %i, attendu : %i mini)", nmea.annuaire[rang].pgn, nmea.annuaire[rang].lg_data, LG_DATA_MINI); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + return (-1); + } + + // Recherche de la référence des infos de vent (vitesse et direction) qui suivent : + // True_North=0, Magnetic=1, Apparent=2, True_boat=3, True_water=4, Error=6, Unavailable=7 + wind_ref = nmea.annuaire[rang].data[5] & 0x07; + if (wind_ref > 4) + { +#ifdef DEBUG_PGN_130306 +sprintf(tmp, "La référence de vent du PGN %i n'est pas correcte (reçu : %i, attendu : 4 maxi). Le traitement de mise à jour est annulé...", nmea.annuaire[rang].pgn, wind_ref ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + return(-1); + } + +#ifdef DEBUG_PGN_130306 +char wind_ref_name[40]; +switch(wind_ref) +{ + case 0: { strcpy(wind_ref_name, "True North"); break; } + case 1: { strcpy(wind_ref_name, "Magnetic"); break; } + case 2: { strcpy(wind_ref_name, "Apparent"); break; } + case 3: { strcpy(wind_ref_name, "True boat"); break; } + case 4: { strcpy(wind_ref_name, "True water"); break; } + case 6: { strcpy(wind_ref_name, "Error"); break; } + case 7: { strcpy(wind_ref_name, "Unavailable"); break; } + default: { strcpy(wind_ref_name, "True North"); break; } +} +#endif + // Parse des data : + index = 0; + + //---------------------------------- + // Sequence ID : + index ++; + + //---------------------------------- + // Wind Speed : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; // En m/s + enregistrement_pgn_field_nmea(rang, (NMEA_130306_WIND_SPEED_TRUE_NORTH + wind_ref), (((float) val_u16 * 0.01) * CONVERSION_MS_TO_KNOTS) ); // En Knots +#ifdef DEBUG_PGN_130306 +sprintf(tmp, "PGN %i : Vitesse du vent [%s] = %.2Lf Kts", nmea.annuaire[rang].pgn, wind_ref_name, (((float) val_u16 * 0.01) * CONVERSION_MS_TO_KNOTS) ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + //---------------------------------- + // Wind Angle : + val_u16 = (uint16_t) nmea.annuaire[rang].data[index ++]; + val_u16 |= ((uint16_t) nmea.annuaire[rang].data[index ++]) << 8; // En Radians + enregistrement_pgn_field_nmea(rang, (NMEA_130306_WIND_ANGLE_TRUE_NORTH + wind_ref), (((float) val_u16 * 0.0001) * CONVERSION_RAD_TO_DEG) ); // En Degrés +#ifdef DEBUG_PGN_130306 +sprintf(tmp, "PGN %i : Angle du vent [%s] = %.1Lf° ", nmea.annuaire[rang].pgn, wind_ref_name, (((float) val_u16 * 0.0001) * CONVERSION_RAD_TO_DEG) ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + + //------------------------------------------------------------------------------ + case NMEA_PARSE_MODE_SUPPRESSION: + { + suppression_pgn_nmea(rang); + + break; + } + + //------------------------------------------------------------------------------ + default: break; + } + + return (0); +} + + + + + + + +//-----------------------------------------------------------------------------************************************************************************************** +//-----------------------------------------------------------------------------************************************************************************************** +// +// Catalogue des PGNs : +// +//-----------------------------------------------------------------------------************************************************************************************** +//-----------------------------------------------------------------------------************************************************************************************** + +// Pgn, Type, Période mini (x10ms), Fonction de parsing +const struct Struct_entree_catalogue_pgn_nmea catalogue_pgns[NMEA_MAX_NOMBRE_PGN + 1] = { + { 126992, NMEA_PGN_TYPE_DEFAULT_SINGLE_FRAME, 1000, &Parse_pgn_126992 }, // System date/time, pri=3, period=1000 + { 129029, NMEA_PGN_TYPE_DEFAULT_FAST_PACKET, 480, &Parse_pgn_129029 }, // GNSS Position Data, pri=3, period=1000 + { 129540, NMEA_PGN_TYPE_DEFAULT_FAST_PACKET, 480, &Parse_pgn_129540 }, // GNSS Sats in View, pri=6, period=1000 + { 130306, NMEA_PGN_TYPE_DEFAULT_SINGLE_FRAME, 100, &Parse_pgn_130306 }, // Wind Speed, pri=2, period=100 + { 0, 0, 0, NULL } // Ligne de fin de catalogue (PGN = 0) +}; + + + + + + + + + + + + + + + + + + +// Fonctions issues de NMEA2000.cpp : https://ttlappalainen.github.io/NMEA2000/_n_m_e_a2000_8cpp_source.html + +/* +//----------------------------------------------------------------------------- +const unsigned long DefTransmitMessages[] PROGMEM = { + 59392L, // ISO Acknowledgement, pri=6, period=NA + 59904L, // ISO Request, pri=6, period=NA +#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT) + TP_DT, // Multi packet data transfer, TP.DT, pri=6, period=NA + TP_CM, // Multi packet connection management, TP.CM, pri=6, period=NA +#endif + 60928L, // ISO Address Claim, pri=6, period=NA +#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT) + 126208L, // NMEA Request/Command/Acknowledge group function, pri=3, period=NA +#endif + 126464L, // PGN List (Transmit and Receive), pri=6, period=NA +#if !defined(N2K_NO_HEARTBEAT_SUPPORT) + 126993L, // Heartbeat, pri=7, period=60000 +#endif + 126996L, // Product information, pri=6, period=NA + 126998L, // Configuration information, pri=6, period=NA + 0}; + +//----------------------------------------------------------------------------- +const unsigned long DefReceiveMessages[] PROGMEM = { + 59392L, // ISO Acknowledgement, pri=6, period=NA + 59904L, // ISO Request, pri=6, period=NA +#if !defined(N2K_NO_ISO_MULTI_PACKET_SUPPORT) + TP_DT, // Multi packet data transfer, TP.DT + TP_CM, // Multi packet connection management, TP.CM +#endif + 60928L, // ISO Address Claim + 65240L, // Commanded Address +#if !defined(N2K_NO_GROUP_FUNCTION_SUPPORT) + 126208L, // NMEA Request/Command/Acknowledge group function +#endif + 0}; + +//----------------------------------------------------------------------------- +bool IsSingleFrameSystemMessage(unsigned long PGN) { + switch (PGN) { + case 59392L: // ISO Acknowledgement + case TP_DT: // Multi packet data transfer, TP.DT + case TP_CM: // Multi packet connection management, TP.CM + case 59904L: // ISO Request + case 60928L: // ISO Address Claim + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool IsFastPacketSystemMessage(unsigned long PGN) { + switch (PGN) { + case 65240L: // Commanded Address + case 126208L: // NMEA Request/Command/Acknowledge group function + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool IsDefaultSingleFrameMessage(unsigned long PGN) { + switch (PGN) { + case 126993L: // Heartbeat, pri=7, period=60000 + case 127245L: // Rudder, pri=2, period=100 + case 127250L: // Vessel Heading, pri=2, period=100 + case 127251L: // Rate of Turn, pri=2, period=100 + case 127257L: // Attitude, pri=3, period=1000 + case 127488L: // Engine parameters rapid, rapid Update, pri=2, period=100 + case 127493L: // Transmission parameters: dynamic, pri=2, period=100 + case 127501L: // Binary status report, pri=3, period=NA + case 127505L: // Fluid level, pri=6, period=2500 + case 127508L: // Battery Status, pri=6, period=1500 + case 128259L: // Boat speed, pri=2, period=1000 + case 128267L: // Water depth, pri=3, period=1000 + case 129025L: // Lat/lon rapid, pri=2, period=100 + case 129026L: // COG SOG rapid, pri=2, period=250 + case 129283L: // Cross Track Error, pri=3, period=1000 + case 130310L: // Outside Environmental parameters, pri=5, period=500 + case 130311L: // Environmental parameters, pri=5, period=500 + case 130312L: // Temperature, pri=5, period=2000 + case 130313L: // Humidity, pri=5, period=2000 + case 130314L: // Pressure, pri=5, period=2000 + case 130316L: // Temperature extended range, pri=5, period=2000 + case 130576L: // Small Craft Status (Trim Tab position), pri=2, period=200 + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool IsMandatoryFastPacketMessage(unsigned long PGN) { + switch (PGN) { + case 126464L: // PGN List (Transmit and Receive), pri=6, period=NA + case 126996L: // Product information, pri=6, period=NA + case 126998L: // Configuration information, pri=6, period=NA + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool IsDefaultFastPacketMessage(unsigned long PGN) { + switch (PGN) { + case 126983L: // Alert, pri=2, period=1000 + case 126984L: // Alert Response, pri=2, period=NA + case 126985L: // Alert Text, pri=2, period=10000 + case 126986L: // Alert Configuration, pri=2, period=NA + case 126987L: // Alert Threshold, pri=2, period=NA + case 126988L: // Alert Value, pri=2, period=10000 + case 127233L: // Man Overboard Notification(MOB), pri=3, period=NA + case 127237L: // Heading/Track control, pri=2, period=250 + case 127489L: // Engine parameters dynamic, pri=2, period=500 + case 127490L: // Electric Drive Status (Dynamic), pri=1, period=1500 + case 127491L: // Electric Energy Storage Status (Dynamic), pri=7, period=1500 + case 127494L: // Electric Drive Information, pri=4, period=NA + case 127495L: // Electric Energy Storage Information, pri=6, period=NA + case 127496L: // Trip fuel consumption, vessel, pri=5, period=1000 + case 127497L: // Trip fuel consumption, engine, pri=5, period=1000 + case 127498L: // Engine parameters static, pri=5, period=NA + case 127503L: // AC Input Status, pri=6, period=1500 + case 127504L: // AC Output Status, pri=6, period=1500 + case 127506L: // DC Detailed status, pri=6, period=1500 + case 127507L: // Charger status, pri=6, period=1500 + case 127509L: // Inverter status, pri=6, period=1500 + case 127510L: // Charger configuration status, pri=6, period=NA + case 127511L: // Inverter Configuration Status, pri=6, period=NA + case 127512L: // AGS configuration status, pri=6, period=NA + case 127513L: // Battery configuration status, pri=6, period=NA + case 127514L: // AGS Status, pri=6, period=1500 + case 128275L: // Distance log, pri=6, period=1000 + case 128520L: // Tracked Target Data, pri=2, period=1000 + case 128538L: // Elevator car status, pri=6, period=100 + case 129038L: // AIS Class A Position Report, pri=4, period=NA + case 129039L: // AIS Class B Position Report, pri=4, period=NA + case 129040L: // AIS Class B Extended Position Report, pri=4, period=NA + case 129041L: // AIS Aids to Navigation (AtoN) Report, pri=4, period=NA + case 129044L: // Datum, pri=6, period=10000 + case 129045L: // User Datum Settings, pri=6, period=NA + case 129284L: // Navigation info, pri=3, period=1000 + case 129285L: // Waypoint list, pri=3, period=NA + case 129301L: // Time to/from Mark, pri=3, period=1000 + case 129302L: // Bearing and Distance between two Marks, pri=6, period=NA + case 129538L: // GNSS Control Status, pri=6, period=NA + case 129541L: // GPS Almanac Data, pri=6, period=NA + case 129542L: // GNSS Pseudorange Noise Statistics, pri=6, period=1000 + case 129545L: // GNSS RAIM Output, pri=6, period=NA + case 129547L: // GNSS Pseudorange Error Statistics, pri=6, period=NA + case 129549L: // DGNSS Corrections, pri=6, period=NA + case 129551L: // GNSS Differential Correction Receiver Signal, pri=6, period=NA + case 129556L: // GLONASS Almanac Data, pri=6, period=NA + case 129792L: // AIS DGNSS Broadcast Binary Message, pri=6, period=NA + case 129793L: // AIS UTC and Date Report, pri=7, period=NA + case 129794L: // AIS Class A Static data, pri=6, period=NA + case 129795L: // AIS Addressed Binary Message, pri=5, period=NA + case 129796L: // AIS Acknowledge, pri=7, period=NA + case 129797L: // AIS Binary Broadcast Message, pri=5, period=NA + case 129798L: // AIS SAR Aircraft Position Report, pri=4, period=NA + case 129799L: // Radio Frequency/Mode/Power, pri=3, period=NA + case 129800L: // AIS UTC/Date Inquiry, pri=7, period=NA + case 129801L: // AIS Addressed Safety Related Message, pri=5, period=NA + case 129802L: // AIS Safety Related Broadcast Message, pri=5, period=NA + case 129803L: // AIS Interrogation PGN, pri=7, period=NA + case 129804L: // AIS Assignment Mode Command, pri=7, period=NA + case 129805L: // AIS Data Link Management Message, pri=7, period=NA + case 129806L: // AIS Channel Management, pri=7, period=NA + case 129807L: // AIS Group Assignment, pri=7, period=NA + case 129808L: // DSC Call Information, pri=8, period=NA + case 129809L: // AIS Class B Static Data: Part A, pri=6, period=NA + case 129810L: // AIS Class B Static Data Part B, pri=6, period=NA + case 129811L: // AIS Single Slot Binary Message, pri=5, period=NA + case 129812L: // AIS Multi Slot Binary Message, pri=5, period=NA + case 129813L: // AIS Long-Range Broadcast Message, pri=5, period=NA + case 130052L: // Loran-C TD Data, pri=3, period=1000 + case 130053L: // Loran-C Range Data, pri=3, period=1000 + case 130054L: // Loran-C Signal Data, pri=3, period=1000 + case 130060L: // Label, pri=7, period=NA + case 130061L: // Channel Source Configuration, pri=7, period=NA + case 130064L: // Route and WP Service - Database List, pri=7, period=NA + case 130065L: // Route and WP Service - Route List, pri=7, period=NA + case 130066L: // Route and WP Service - Route/WP-List Attributes, pri=7, period=NA + case 130067L: // Route and WP Service - Route - WP Name & Position, pri=7, period=NA + case 130068L: // Route and WP Service - Route - WP Name, pri=7, period=NA + case 130069L: // Route and WP Service - XTE Limit & Navigation Method, pri=7, period=NA + case 130070L: // Route and WP Service - WP Comment, pri=7, period=NA + case 130071L: // Route and WP Service - Route Comment, pri=7, period=NA + case 130072L: // Route and WP Service - Database Comment, pri=7, period=NA + case 130073L: // Route and WP Service - Radius of Turn, pri=7, period=NA + case 130074L: // Route and WP Service - WP List - WP Name & Position, pri=7, period=NA + case 130320L: // Tide Station Data, pri=6, period=1000 + case 130321L: // Salinity Station Data, pri=6, period=1000 + case 130322L: // Current Station Data, pri=6, period=1000 + case 130323L: // Meteorological Station Data, pri=6, period=1000 + case 130324L: // Moored Buoy Station Data, pri=6, period=1000 + case 130330L: // Lighting system settings, pri=7, period=NA + case 130561L: // Lighting zone, pri=7, period=NA + case 130562L: // Lighting scene, pri=7, period=NA + case 130563L: // Lighting device, pri=7, period=NA + case 130564L: // Lighting device enumeration, pri=7, period=NA + case 130565L: // Lighting color sequence, pri=7, period=NA + case 130566L: // Lighting program, pri=7, period=NA + case 130567L: // Watermaker Input Setting and Status, pri=6, period=2500 + case 130577L: // Direction Data PGN, pri=3, period=1000 + case 130578L: // Vessel Speed Components, pri=2, period=250 + // Entertainment PGNs + case 130569L: // Current File and Status, pri=6, period=500 + case 130570L: // Library Data File, pri=6, period=NA + case 130571L: // Library Data Group, pri=6, period=NA + case 130572L: // Library Data Search, pri=6, period=NA + case 130573L: // Supported Source Data, pri=6, period=NA + case 130574L: // Supported Zone Data, pri=6, period=NA + case 130580L: // System Configuration Status, pri=6, period=NA + case 130581L: // Zone Configuration Status, pri=6, period=NA + case 130583L: // Available Audio EQ Presets, pri=6, period=NA + case 130584L: // Bluetooth Devices, pri=6, period=NA + case 130586L: // Zone Configuration Status, pri=6, period=NA + return true; + } + return false; +} + +//----------------------------------------------------------------------------- +bool IsProprietaryFastPacketMessage(unsigned long PGN) { + return ( PGN==126720L ) || ( 130816L<=PGN && PGN<=131071L ); +} + +bool tNMEA2000::IsProprietaryMessage(unsigned long PGN) { + return IsProprietaryFastPacketMessage(PGN) || ( PGN==61184L ) || ( 65280L<=PGN && PGN<=65535L ); +} + +*/ + + + + + + + + + + + + + + + + + + + + + + + +// Fonctions issues de : +// https://ttlappalainen.github.io/NMEA2000/_n2k_messages_8cpp_source.html +// https://ttlappalainen.github.io/NMEA2000/_n2k_maretron_8cpp_source.html +// https://ttlappalainen.github.io/NMEA2000/_n_m_e_a2000_8cpp_source.html (en fin de fichier) + + +// Voir peut-être N2kMessagesEnumToStr.h : https://ttlappalainen.github.io/NMEA2000/_n2k_messages_enum_to_str_8h_source.html +// const char* tN2kTimeSourceStrs[] = { "GPS", "GLONASS", "radio station", "local cesium clock", "local rubidium clock", "local crystal clock" }; +// const char* tN2kFluidTypeStrs[] = { "Fuel", "Water", "Gray water", "Live well", "Oil", "Black water", "Gasoline Fuel", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Reserved", "Error", "Unavailable",}; + +/* + +//----------------------------------------------------------------------------- +// ISO Request +bool ParseN2kPGN59904(const tN2kMsg &N2kMsg, + unsigned long &RequestedPGN) +{ + int result=((N2kMsg.DataLen>=3) && (N2kMsg.DataLen<=8)); + RequestedPGN=0; + if (result) { + int Index=0; + RequestedPGN=N2kMsg.Get3ByteUInt(Index); + } + + return result; +} + + + + +//----------------------------------------------------------------------------- +// System time +bool ParseN2kPGN126992(const tN2kMsg &N2kMsg, + unsigned char &SID, + uint16_t &SystemDate, + double &SystemTime, + tN2kTimeSource &TimeSource) +{ + if (N2kMsg.PGN!=126992L) return false; + + int Index=0; + + SID=N2kMsg.GetByte(Index); + TimeSource=(tN2kTimeSource)(N2kMsg.GetByte(Index) & 0x0f); + SystemDate=N2kMsg.Get2ByteUInt(Index); + SystemTime=N2kMsg.Get4ByteUDouble(0.0001,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// AIS Safety Related Broadcast Message +bool ParseN2kPGN129802(const tN2kMsg &N2kMsg, + uint8_t &MessageID, + tN2kAISRepeat &Repeat, + uint32_t &SourceID, + tN2kAISTransceiverInformation &AISTransceiverInformation, + char * SafetyRelatedText, + size_t &SafetyRelatedTextMaxSize) +{ + if (N2kMsg.PGN!=129802L) return false; + + int Index=0; + unsigned char vb; + + vb=N2kMsg.GetByte(Index); + MessageID=(vb & 0x3f); + Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + SourceID = N2kMsg.Get4ByteUInt(Index) & 0x3fffffff; + AISTransceiverInformation = (tN2kAISTransceiverInformation)(N2kMsg.GetByte(Index) & 0x1f); + N2kMsg.GetVarStr(SafetyRelatedTextMaxSize, SafetyRelatedText, Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Product Information +bool ParseN2kPGN126996(const tN2kMsg& N2kMsg, + unsigned short &N2kVersion, + unsigned short &ProductCode, + int ModelIDSize, + char *ModelID, + int SwCodeSize, + char *SwCode, + int ModelVersionSize, + char *ModelVersion, + int ModelSerialCodeSize, + char *ModelSerialCode, + unsigned char &CertificationLevel, + unsigned char &LoadEquivalency) +{ + if (N2kMsg.PGN!=N2kPGNProductInformation) return false; + + int Index=0; + N2kVersion=N2kMsg.Get2ByteUInt(Index); + ProductCode=N2kMsg.Get2ByteUInt(Index); + N2kMsg.GetStr(ModelIDSize,ModelID,Max_N2kModelID_len,0xff,Index); + N2kMsg.GetStr(SwCodeSize,SwCode,Max_N2kSwCode_len,0xff,Index); + N2kMsg.GetStr(ModelVersionSize,ModelVersion,Max_N2kModelVersion_len,0xff,Index); + N2kMsg.GetStr(ModelSerialCodeSize,ModelSerialCode,Max_N2kModelSerialCode_len,0xff,Index); + CertificationLevel=N2kMsg.GetByte(Index); + LoadEquivalency=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Configuration Information +bool ParseN2kPGN126998(const tN2kMsg& N2kMsg, + size_t &ManufacturerInformationSize, + char *ManufacturerInformation, + size_t &InstallationDescription1Size, + char *InstallationDescription1, + size_t &InstallationDescription2Size, + char *InstallationDescription2) +{ + if (N2kMsg.PGN!=N2kPGNConfigurationInformation) return false; + + int Index=0; + return ( N2kMsg.GetVarStr(InstallationDescription1Size,InstallationDescription1,Index) && + N2kMsg.GetVarStr(InstallationDescription2Size,InstallationDescription2,Index) && + N2kMsg.GetVarStr(ManufacturerInformationSize,ManufacturerInformation,Index) ); +} + +//----------------------------------------------------------------------------- +// Man Overboard Notification +bool ParseN2kPGN127233(const tN2kMsg &N2kMsg, + unsigned char &SID, + uint32_t &MobEmitterId, + tN2kMOBStatus &MOBStatus, + double &ActivationTime, + tN2kMOBPositionSource &PositionSource, + uint16_t &PositionDate, + double &PositionTime, + double &Latitude, + double &Longitude, + tN2kHeadingReference &COGReference, + double &COG, + double &SOG, + uint32_t &MMSI, + tN2kMOBEmitterBatteryStatus &MOBEmitterBatteryStatus) +{ + if (N2kMsg.PGN!=127233L) return false; + int Index = 0; + SID = N2kMsg.GetByte(Index); + MobEmitterId = N2kMsg.Get4ByteUInt(Index); + MOBStatus = (tN2kMOBStatus)(N2kMsg.GetByte(Index) & 0x07); + ActivationTime = N2kMsg.Get4ByteUDouble(0.0001,Index); + PositionSource = (tN2kMOBPositionSource)(N2kMsg.GetByte(Index) & 0x07); + PositionDate = N2kMsg.Get2ByteUInt(Index); + PositionTime = N2kMsg.Get4ByteUDouble(0.0001,Index); + Latitude = N2kMsg.Get4ByteDouble(1e-7,Index); + Longitude = N2kMsg.Get4ByteDouble(1e-7,Index); + COGReference = (tN2kHeadingReference)(N2kMsg.GetByte(Index) & 0x03); + COG = N2kMsg.Get2ByteUDouble(0.0001,Index); + SOG = N2kMsg.Get2ByteUDouble(0.01,Index); + MMSI = N2kMsg.Get4ByteUInt(Index); + MOBEmitterBatteryStatus = (tN2kMOBEmitterBatteryStatus)(N2kMsg.GetByte(Index) & 0x07); + return true; +} + + +//----------------------------------------------------------------------------- +// Heading/Track control +// Angles should be in radians +bool ParseN2kPGN127237(const tN2kMsg &N2kMsg, + tN2kOnOff &RudderLimitExceeded, + tN2kOnOff &OffHeadingLimitExceeded, + tN2kOnOff &OffTrackLimitExceeded, + tN2kOnOff &Override, + tN2kSteeringMode &SteeringMode, + tN2kTurnMode &TurnMode, + tN2kHeadingReference &HeadingReference, + tN2kRudderDirectionOrder &CommandedRudderDirection, + double &CommandedRudderAngle, + double &HeadingToSteerCourse, + double &Track, + double &RudderLimit, + double &OffHeadingLimit, + double &RadiusOfTurnOrder, + double &RateOfTurnOrder, + double &OffTrackLimit, + double &VesselHeading) +{ + + if (N2kMsg.PGN!=127237L) return false; + int Index = 0; + unsigned char v; + v = N2kMsg.GetByte(Index); + RudderLimitExceeded = (tN2kOnOff)(v & 0x03); + OffHeadingLimitExceeded = (tN2kOnOff)((v >> 2) & 0x03); + OffTrackLimitExceeded = (tN2kOnOff)((v >> 4) & 0x03); + Override = (tN2kOnOff)((v >> 6) & 0x03); + v = N2kMsg.GetByte(Index); + SteeringMode = (tN2kSteeringMode)(v & 0x07); + TurnMode = (tN2kTurnMode)((v >> 3) & 0x07); + HeadingReference = (tN2kHeadingReference)((v >> 6) & 0x03); + CommandedRudderDirection = (tN2kRudderDirectionOrder)((N2kMsg.GetByte(Index) >> 5) & 0x07); + CommandedRudderAngle = N2kMsg.Get2ByteDouble(0.0001,Index); + HeadingToSteerCourse = N2kMsg.Get2ByteUDouble(0.0001,Index); + Track = N2kMsg.Get2ByteUDouble(0.0001,Index); + RudderLimit = N2kMsg.Get2ByteUDouble(0.0001,Index); + OffHeadingLimit = N2kMsg.Get2ByteUDouble(0.0001,Index); + RadiusOfTurnOrder = N2kMsg.Get2ByteDouble(1,Index); + RateOfTurnOrder = N2kMsg.Get2ByteDouble(3.125e-5,Index); + OffTrackLimit = N2kMsg.Get2ByteDouble(1,Index); + VesselHeading = N2kMsg.Get2ByteUDouble(0.0001,Index); + return true; +} + +//----------------------------------------------------------------------------- +// Rudder +// Angles should be in radians +bool ParseN2kPGN127245(const tN2kMsg &N2kMsg, + double &RudderPosition, + unsigned char &Instance, + tN2kRudderDirectionOrder &RudderDirectionOrder, + double &AngleOrder) +{ + if (N2kMsg.PGN!=127245L) return false; + + int Index=0; + Instance=N2kMsg.GetByte(Index); + RudderDirectionOrder=(tN2kRudderDirectionOrder)(N2kMsg.GetByte(Index)&0x7); + AngleOrder=N2kMsg.Get2ByteDouble(0.0001,Index); + RudderPosition=N2kMsg.Get2ByteDouble(0.0001,Index); + return true; +} + + +//----------------------------------------------------------------------------- +// Vessel Heading +// Angles should be in radians +bool ParseN2kPGN127250(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &Heading, + double &Deviation, + double &Variation, + tN2kHeadingReference &ref) +{ + if (N2kMsg.PGN!=127250L) return false; + + int Index=0; + + SID=N2kMsg.GetByte(Index); + Heading=N2kMsg.Get2ByteUDouble(0.0001,Index); + Deviation=N2kMsg.Get2ByteDouble(0.0001,Index); + Variation=N2kMsg.Get2ByteDouble(0.0001,Index); + ref=(tN2kHeadingReference)(N2kMsg.GetByte(Index)&0x03); + + return true; +} + + +//----------------------------------------------------------------------------- +// Rate of turn +// Angles should be in radians +bool ParseN2kPGN127251(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &RateOfTurn) +{ + if (N2kMsg.PGN!=127251L) return false; + + int Index=0; + + SID=N2kMsg.GetByte(Index); + RateOfTurn=N2kMsg.Get4ByteDouble(3.125E-08,Index); //1e-6/32.0 + + return true; +} + +//----------------------------------------------------------------------------- +// Heave +// - SID Sequence ID. If your device is e.g. boat speed and heading at same time, you can set same SID for different messages +// to indicate that they are measured at same time. +// - Heave Vertical displacement perpendicular to the earth’s surface in meters +// - Delay Delay added by calculations in seconds +// - DelaySource tN2kDelaySource +// Output: +// - N2kMsg NMEA2000 message ready to be send. +bool ParseN2kPGN127252(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &Heave, + double &Delay, + tN2kDelaySource &DelaySource) +{ + if (N2kMsg.PGN!=127252L) return false; + int Index = 0; + SID = N2kMsg.GetByte(Index); + Heave = N2kMsg.Get2ByteDouble(0.01,Index); + Delay = N2kMsg.Get2ByteUDouble(0.01,Index); + uint8_t v=N2kMsg.GetByte(Index); + DelaySource=(tN2kDelaySource)(v & 0x0f); + return true; +} + + +//----------------------------------------------------------------------------- +// Attitude +// Input: +// - SID Sequence ID. If your device is e.g. boat speed and heading at same time, you can set same SID for different messages +// to indicate that they are measured at same time. +// - Yaw Heading in radians. +// - Pitch Pitch in radians. Positive, when your bow rises. +// - Roll Roll in radians. Positive, when tilted right. +// Output: +// - N2kMsg NMEA2000 message ready to be send. +bool ParseN2kPGN127257(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &Yaw, + double &Pitch, + double &Roll) +{ + if (N2kMsg.PGN!=127257L) return false; + + int Index=0; + SID=N2kMsg.GetByte(Index); + Yaw=N2kMsg.Get2ByteDouble(0.0001,Index); + Pitch=N2kMsg.Get2ByteDouble(0.0001,Index); + Roll=N2kMsg.Get2ByteDouble(0.0001,Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Magnetic variation +bool ParseN2kPGN127258(const tN2kMsg &N2kMsg, + unsigned char &SID, + tN2kMagneticVariation &Source, + uint16_t &DaysSince1970, + double &Variation) +{ + if (N2kMsg.PGN!=127258L) return false; + + int Index=0; + SID=N2kMsg.GetByte(Index); + Source=(tN2kMagneticVariation) (N2kMsg.GetByte(Index) & 0x0f); + DaysSince1970=N2kMsg.Get2ByteUInt(Index); + Variation=N2kMsg.Get2ByteDouble(0.0001, Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Engine rapid param +bool ParseN2kPGN127488(const tN2kMsg &N2kMsg, + unsigned char &EngineInstance, + double &EngineSpeed, + double &EngineBoostPressure, + int8_t &EngineTiltTrim) +{ + if (N2kMsg.PGN!=127488L) return false; + + int Index=0; + + EngineInstance=N2kMsg.GetByte(Index); + EngineSpeed=N2kMsg.Get2ByteUDouble(0.25,Index); + EngineBoostPressure=N2kMsg.Get2ByteUDouble(100,Index); + EngineTiltTrim=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Engine parameters dynamic +bool ParseN2kPGN127489(const tN2kMsg &N2kMsg, + unsigned char &EngineInstance, + double &EngineOilPress, + double &EngineOilTemp, + double &EngineCoolantTemp, + double &AltenatorVoltage, + double &FuelRate, + double &EngineHours, + double &EngineCoolantPress, + double &EngineFuelPress, + int8_t &EngineLoad, + int8_t &EngineTorque, + tN2kEngineDiscreteStatus1 &Status1, + tN2kEngineDiscreteStatus2 &Status2) +{ + if (N2kMsg.PGN != 127489L) return false; + + int Index = 0; + + EngineInstance = N2kMsg.GetByte(Index); + EngineOilPress = N2kMsg.Get2ByteUDouble(100, Index); + EngineOilTemp = N2kMsg.Get2ByteUDouble(0.1, Index); + EngineCoolantTemp = N2kMsg.Get2ByteUDouble(0.01, Index); + AltenatorVoltage = N2kMsg.Get2ByteDouble(0.01, Index); + FuelRate = N2kMsg.Get2ByteDouble(0.1, Index); + EngineHours = N2kMsg.Get4ByteUDouble(1, Index); + EngineCoolantPress=N2kMsg.Get2ByteUDouble(100, Index); + EngineFuelPress=N2kMsg.Get2ByteUDouble(1000, Index); + N2kMsg.GetByte(Index); // reserved + Status1=N2kMsg.Get2ByteUInt(Index); // Discrete Status 1 + Status2=N2kMsg.Get2ByteUInt(Index); // Discrete Status 2 + EngineLoad=N2kMsg.GetByte(Index); + EngineTorque=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Transmission parameters, dynamic +bool ParseN2kPGN127493(const tN2kMsg &N2kMsg, + unsigned char &EngineInstance, + tN2kTransmissionGear &TransmissionGear, + double &OilPressure, + double &OilTemperature, + unsigned char &DiscreteStatus1) +{ + if (N2kMsg.PGN!=127493L) return false; + + int Index=0; + + EngineInstance=N2kMsg.GetByte(Index); + TransmissionGear=(tN2kTransmissionGear)(N2kMsg.GetByte(Index) & 0x03); + OilPressure=N2kMsg.Get2ByteUDouble(100,Index); + OilTemperature=N2kMsg.Get2ByteUDouble(0.1,Index); + DiscreteStatus1=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Trip Parameters, Engine +bool ParseN2kPGN127497(const tN2kMsg &N2kMsg, + unsigned char &EngineInstance, + double &TripFuelUsed, + double &FuelRateAverage, + double &FuelRateEconomy, + double &InstantaneousFuelEconomy) +{ + if (N2kMsg.PGN!=127497L) return false; + + int Index=0; + + EngineInstance=N2kMsg.GetByte(Index); + TripFuelUsed=N2kMsg.Get2ByteUDouble(1,Index); + FuelRateAverage=N2kMsg.Get2ByteDouble(0.1,Index); + FuelRateEconomy=N2kMsg.Get2ByteDouble(0.1,Index); + InstantaneousFuelEconomy=N2kMsg.Get2ByteDouble(0.1,Index); + + return true; +} + + + + + + + + + + +//----------------------------------------------------------------------------- +// Binary status + +//----------------------------------------------------------------------------- +tN2kOnOff N2kGetStatusOnBinaryStatus(tN2kBinaryStatus BankStatus, uint8_t ItemIndex) { + ItemIndex--; + if (ItemIndex>27) return N2kOnOff_Unavailable; + + return (tN2kOnOff)((BankStatus >> (2*ItemIndex)) & 0x03); +} + +//----------------------------------------------------------------------------- +void N2kSetStatusBinaryOnStatus(tN2kBinaryStatus &BankStatus, tN2kOnOff ItemStatus, uint8_t ItemIndex) { + ItemIndex--; + if (ItemIndex>27) return; + + tN2kBinaryStatus Mask = ~((tN2kBinaryStatus)3 << (2*ItemIndex)); + + BankStatus = (BankStatus & Mask) | ((tN2kBinaryStatus)ItemStatus << (2*ItemIndex)); +} + +//----------------------------------------------------------------------------- +void SetN2kPGN127501(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance, tN2kBinaryStatus BankStatus) { + N2kMsg.SetPGN(127501L); + N2kMsg.Priority=3; + BankStatus = (BankStatus << 8) | DeviceBankInstance; + N2kMsg.AddUInt64(BankStatus); +} + +//----------------------------------------------------------------------------- +// Binary status report +void SetN2kPGN127501(tN2kMsg &N2kMsg, unsigned char DeviceBankInstance + ,tN2kOnOff Status1 + ,tN2kOnOff Status2 + ,tN2kOnOff Status3 + ,tN2kOnOff Status4 + ) { + tN2kBinaryStatus BankStatus; + + N2kResetBinaryStatus(BankStatus); + BankStatus = (BankStatus << 2) | Status4; + BankStatus = (BankStatus << 2) | Status3; + BankStatus = (BankStatus << 2) | Status2; + BankStatus = (BankStatus << 2) | Status1; + SetN2kPGN127501(N2kMsg,DeviceBankInstance,BankStatus); +} + + + + + + + + + + + + + +//----------------------------------------------------------------------------- +bool ParseN2kPGN127501(const tN2kMsg &N2kMsg, + unsigned char &DeviceBankInstance, + tN2kOnOff &Status1, + tN2kOnOff &Status2, + tN2kOnOff &Status3, + tN2kOnOff &Status4) +{ + if (N2kMsg.PGN!=127501L) return false; + + int Index=0; + DeviceBankInstance=N2kMsg.GetByte(Index); + unsigned char b=N2kMsg.GetByte(Index); + Status1=(tN2kOnOff)(b & 0x03); + b>>=2; Status2=(tN2kOnOff)(b & 0x03); + b>>=2; Status3=(tN2kOnOff)(b & 0x03); + b>>=2; Status4=(tN2kOnOff)(b & 0x03); + + return true; +} + +//----------------------------------------------------------------------------- +bool ParseN2kPGN127501(const tN2kMsg &N2kMsg, + unsigned char &DeviceBankInstance, + tN2kBinaryStatus &BankStatus) +{ + if (N2kMsg.PGN!=127501L) return false; + + int Index=0; + BankStatus=N2kMsg.GetUInt64(Index); + DeviceBankInstance = BankStatus & 0xff; + BankStatus>>=8; + + return true; +} + + + + + + + + +//----------------------------------------------------------------------------- +// PGN 127502 - Switch Bank Control +bool ParseN2kPGN127502(const tN2kMsg &N2kMsg, + unsigned char &TargetBankInstance, + tN2kBinaryStatus &BankStatus) +{ + if (N2kMsg.PGN!=127502L) return false; + + int Index=0; + BankStatus=N2kMsg.GetUInt64(Index); + TargetBankInstance = BankStatus & 0xff; + BankStatus>>=8; + + return true; +} + + + +//----------------------------------------------------------------------------- +// Fluid level +bool ParseN2kPGN127505(const tN2kMsg &N2kMsg, + unsigned char &Instance, + tN2kFluidType &FluidType, + double &Level, + double &Capacity) +{ + if (N2kMsg.PGN!=127505L) return false; + + int Index=0; + unsigned char IFt=N2kMsg.GetByte(Index); + + Instance=IFt&0x0f; + FluidType=(tN2kFluidType)((IFt>>4)&0x0f); + Level=N2kMsg.Get2ByteDouble(0.004,Index); + Capacity=N2kMsg.Get4ByteUDouble(0.1,Index); + + return true; +} + + + +//----------------------------------------------------------------------------- +// DC Detailed Status +bool ParseN2kPGN127506(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &DCInstance, + tN2kDCType &DCType, + uint8_t &StateOfCharge, + uint8_t &StateOfHealth, + double &TimeRemaining, + double &RippleVoltage, + double &Capacity) +{ + if (N2kMsg.PGN!=127506L) return false; + int Index=0; + SID=N2kMsg.GetByte(Index); + DCInstance=N2kMsg.GetByte(Index); + DCType=(tN2kDCType)(N2kMsg.GetByte(Index)); + StateOfCharge=N2kMsg.GetByte(Index); + StateOfHealth=N2kMsg.GetByte(Index); + TimeRemaining=N2kMsg.Get2ByteUDouble(60,Index); + RippleVoltage=N2kMsg.Get2ByteUDouble(0.001,Index); + Capacity=N2kMsg.Get2ByteUDouble(3600,Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Charger Status +// Input: +// - Instance ChargerInstance. +// - BatteryInstance BatteryInstance. +// - Operating State see. tN2kChargeState +// - Charger Mode see. tN2kChargerMode +// - Charger Enable/Disable boolean +// - Equalization Pending boolean +// - Equalization Time Remaining double seconds +// +bool ParseN2kPGN127507(const tN2kMsg &N2kMsg, + unsigned char &Instance, + unsigned char &BatteryInstance, + tN2kChargeState &ChargeState, + tN2kChargerMode &ChargerMode, + tN2kOnOff &Enabled, + tN2kOnOff &EqualizationPending, + double &EqualizationTimeRemaining) +{ + if (N2kMsg.PGN!=127507UL) return false; + int Index=0; + Instance=N2kMsg.GetByte(Index); + BatteryInstance=N2kMsg.GetByte(Index); + unsigned char v; + v=N2kMsg.GetByte(Index); + ChargeState=(tN2kChargeState)(v&0x0f); + ChargerMode=(tN2kChargerMode)((v>>4)&0x0f); + v=N2kMsg.GetByte(Index); + Enabled=(tN2kOnOff)(v&0x03); + EqualizationPending=(tN2kOnOff)((v>>2)&0x03); + EqualizationTimeRemaining=N2kMsg.Get2ByteUDouble(60,Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Battery Status +// Temperatures should be in Kelvins +bool ParseN2kPGN127508(const tN2kMsg &N2kMsg, + unsigned char &BatteryInstance, + double &BatteryVoltage, + double &BatteryCurrent, + double &BatteryTemperature, + unsigned char &SID) +{ + if (N2kMsg.PGN!=127508L) return false; + int Index=0; + BatteryInstance=N2kMsg.GetByte(Index); + BatteryVoltage=N2kMsg.Get2ByteDouble(0.01,Index); + BatteryCurrent=N2kMsg.Get2ByteDouble(0.1,Index); + BatteryTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + SID=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Battery Configuration Status +bool ParseN2kPGN127513(const tN2kMsg &N2kMsg, + unsigned char &BatInstance, + tN2kBatType &BatType, + tN2kBatEqSupport &SupportsEqual, + tN2kBatNomVolt &BatNominalVoltage, + tN2kBatChem &BatChemistry, + double &BatCapacity, + int8_t &BatTemperatureCoefficient, + double &PeukertExponent, + int8_t &ChargeEfficiencyFactor) +{ + if (N2kMsg.PGN!=127513L) return false; + int Index=0; + unsigned char v; + BatInstance = N2kMsg.GetByte(Index); + v = N2kMsg.GetByte(Index); BatType=(tN2kBatType)(v & 0x0f); SupportsEqual=(tN2kBatEqSupport)((v>>4) & 0x03); + v = N2kMsg.GetByte(Index); BatNominalVoltage=(tN2kBatNomVolt)(v & 0x0f); BatChemistry=(tN2kBatChem)((v>>4) & 0x0f); + BatCapacity=N2kMsg.Get2ByteDouble(3600,Index); + BatTemperatureCoefficient=N2kMsg.GetByte(Index); + PeukertExponent=N2kMsg.Get1ByteUDouble(0.002,Index); PeukertExponent+=1; + ChargeEfficiencyFactor=N2kMsg.GetByte(Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Leeway +bool ParseN2kPGN128000(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &Leeway) +{ + if (N2kMsg.PGN!=128000L) return false; + + int Index=0; + + SID=N2kMsg.GetByte(Index); + Leeway=N2kMsg.Get2ByteDouble(0.0001,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Boat speed +bool ParseN2kPGN128259(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &WaterReferenced, + double &GroundReferenced, + tN2kSpeedWaterReferenceType &SWRT) +{ + if (N2kMsg.PGN!=128259L) return false; + + int Index=0; + + SID=N2kMsg.GetByte(Index); + WaterReferenced=N2kMsg.Get2ByteUDouble(0.01,Index); + GroundReferenced=N2kMsg.Get2ByteUDouble(0.01,Index); + SWRT=(tN2kSpeedWaterReferenceType)(N2kMsg.GetByte(Index)&0x0F); + + return true; +} + +//----------------------------------------------------------------------------- +// Water depth +bool ParseN2kPGN128267(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &DepthBelowTransducer, + double &Offset, + double &Range) +{ + if (N2kMsg.PGN!=128267L) return false; + + int Index=0; + SID=N2kMsg.GetByte(Index); + DepthBelowTransducer=N2kMsg.Get4ByteUDouble(0.01,Index); + Offset=N2kMsg.Get2ByteDouble(0.001,Index); + Range=N2kMsg.Get1ByteUDouble(10,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Distance log +bool ParseN2kPGN128275(const tN2kMsg &N2kMsg, + uint16_t &DaysSince1970, + double &SecondsSinceMidnight, + uint32_t &Log, + uint32_t &TripLog) +{ + if (N2kMsg.PGN!=128275L) return false; + + int Index=0; + + DaysSince1970=N2kMsg.Get2ByteUInt(Index); + SecondsSinceMidnight=N2kMsg.Get4ByteUDouble(0.0001,Index); + Log=N2kMsg.Get4ByteUDouble(1,Index); + TripLog=N2kMsg.Get4ByteUDouble(1,Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// PGN128776 - Windlass Control Status +// +bool ParseN2kPGN128776( + const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &WindlassIdentifier, + tN2kWindlassDirectionControl &WindlassDirectionControl, + unsigned char &SpeedControl, + tN2kSpeedType &SpeedControlType, + tN2kGenericStatusPair &AnchorDockingControl, + tN2kGenericStatusPair &PowerEnable, + tN2kGenericStatusPair &MechanicalLock, + tN2kGenericStatusPair &DeckAndAnchorWash, + tN2kGenericStatusPair &AnchorLight, + double &CommandTimeout, + tN2kWindlassControlEvents &WindlassControlEvents) +{ + if (N2kMsg.PGN!=128776L) return false; + int Index = 0; + unsigned char field; + SID = N2kMsg.GetByte(Index); + WindlassIdentifier = N2kMsg.GetByte(Index); + field = N2kMsg.GetByte(Index); + WindlassDirectionControl = (tN2kWindlassDirectionControl) (field & 0x03); + AnchorDockingControl = (tN2kGenericStatusPair) ((field >> 2) & 0x03); + SpeedControlType = (tN2kSpeedType) ((field >> 4) & 0x03); + SpeedControl = N2kMsg.GetByte(Index); + field = N2kMsg.GetByte(Index); + PowerEnable = (tN2kGenericStatusPair) (field & 0x03); + MechanicalLock = (tN2kGenericStatusPair) ((field >> 2) & 0x03); + DeckAndAnchorWash = (tN2kGenericStatusPair) ((field >> 4) & 0x03); + AnchorLight = (tN2kGenericStatusPair) ((field >> 6) & 0x03); + CommandTimeout = N2kMsg.Get1ByteUDouble(0.005, Index); + WindlassControlEvents.SetEvents(N2kMsg.GetByte(Index)); + return true; +} + +//----------------------------------------------------------------------------- +// PGN128777 Windlass Operating Status +// +bool ParseN2kPGN128777( + const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &WindlassIdentifier, + double &RodeCounterValue, + double &WindlassLineSpeed, + tN2kWindlassMotionStates &WindlassMotionStatus, + tN2kRodeTypeStates &RodeTypeStatus, + tN2kAnchorDockingStates &AnchorDockingStatus, + tN2kWindlassOperatingEvents &WindlassOperatingEvents) +{ + if (N2kMsg.PGN!=128777L) return false; + int Index = 0; + unsigned char field; + SID = N2kMsg.GetByte(Index); + WindlassIdentifier = N2kMsg.GetByte(Index); + field = N2kMsg.GetByte(Index); + WindlassMotionStatus = (tN2kWindlassMotionStates) (field & 0x03); + RodeTypeStatus = (tN2kRodeTypeStates) ((field >> 2) & 0x03); + RodeCounterValue = N2kMsg.Get2ByteUDouble(0.1, Index); + WindlassLineSpeed = N2kMsg.Get2ByteUDouble(0.01, Index); + field = N2kMsg.GetByte(Index); + AnchorDockingStatus = (tN2kAnchorDockingStates) (field & 0x03); + WindlassOperatingEvents.SetEvents(field >> 2); + return true; +} + +//----------------------------------------------------------------------------- +// PGN128778 - Windlass Monitoring Status +// +bool ParseN2kPGN128778( + const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &WindlassIdentifier, + double &TotalMotorTime, + double &ControllerVoltage, + double &MotorCurrent, + tN2kWindlassMonitoringEvents &WindlassMonitoringEvents) +{ + if (N2kMsg.PGN!=128778L) return false; + int Index = 0; + SID = N2kMsg.GetByte(Index); + WindlassIdentifier = N2kMsg.GetByte(Index); + WindlassMonitoringEvents.SetEvents(N2kMsg.GetByte(Index)); + ControllerVoltage = N2kMsg.Get1ByteUDouble(0.2, Index); + MotorCurrent = N2kMsg.Get1ByteUDouble(1.0, Index); + TotalMotorTime = N2kMsg.Get2ByteUDouble(60.0, Index); + return true; +} + +//----------------------------------------------------------------------------- +// Lat long rapid +bool ParseN2kPGN129025(const tN2kMsg &N2kMsg, + double &Latitude, + double &Longitude) +{ + if (N2kMsg.PGN!=129025L) return false; + + int Index = 0; + Latitude=N2kMsg.Get4ByteDouble(1e-7, Index); + Longitude=N2kMsg.Get4ByteDouble(1e-7, Index); + return true; +} + +//----------------------------------------------------------------------------- +// COG SOG rapid +// COG should be in radians +// SOG should be in m/s +bool ParseN2kPGN129026(const tN2kMsg &N2kMsg, + unsigned char &SID, + tN2kHeadingReference &ref, + double &COG, + double &SOG) +{ + if (N2kMsg.PGN!=129026L) return false; + int Index=0; + unsigned char b; + + SID=N2kMsg.GetByte(Index); + b=N2kMsg.GetByte(Index); ref=(tN2kHeadingReference)( b & 0x03 ); + COG=N2kMsg.Get2ByteUDouble(0.0001,Index); + SOG=N2kMsg.Get2ByteUDouble(0.01,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// GNSS Position Data +bool ParseN2kPGN129029(const tN2kMsg &N2kMsg, + unsigned char &SID, + uint16_t &DaysSince1970, + double &SecondsSinceMidnight, + double &Latitude, + double &Longitude, + double &Altitude, + tN2kGNSStype &GNSStype, + tN2kGNSSmethod &GNSSmethod, + uint8_t &nSatellites, + double &HDOP, + double &PDOP, + double &GeoidalSeparation, + uint8_t &nReferenceStations, + tN2kGNSStype &ReferenceStationType, + uint16_t &ReferenceSationID,double &AgeOfCorrection) +{ + if (N2kMsg.PGN!=129029L) return false; + int Index=0; + unsigned char vb; + int16_t vi; + + SID=N2kMsg.GetByte(Index); + DaysSince1970=N2kMsg.Get2ByteUInt(Index); + SecondsSinceMidnight=N2kMsg.Get4ByteUDouble(0.0001,Index); + Latitude=N2kMsg.Get8ByteDouble(1e-16,Index); + Longitude=N2kMsg.Get8ByteDouble(1e-16,Index); + Altitude=N2kMsg.Get8ByteDouble(1e-6,Index); + vb=N2kMsg.GetByte(Index); GNSStype=(tN2kGNSStype)(vb & 0x0f); GNSSmethod=(tN2kGNSSmethod)((vb>>4) & 0x0f); + vb=N2kMsg.GetByte(Index); // Integrity 2 bit, reserved 6 bits + nSatellites=N2kMsg.GetByte(Index); + HDOP=N2kMsg.Get2ByteDouble(0.01,Index); + PDOP=N2kMsg.Get2ByteDouble(0.01,Index); + GeoidalSeparation=N2kMsg.Get4ByteDouble(0.01,Index); + nReferenceStations=N2kMsg.GetByte(Index); + if (nReferenceStations!=N2kUInt8NA && nReferenceStations>0) { + // Note that we return real number of stations, but we only have variabes for one. + vi=N2kMsg.Get2ByteUInt(Index); ReferenceStationType=(tN2kGNSStype)(vi & 0x0f); ReferenceSationID=(vi>>4); + AgeOfCorrection=N2kMsg.Get2ByteUDouble(0.01,Index); + } else { + ReferenceStationType = N2kGNSSt_GPS; + ReferenceSationID = N2kInt16NA; + AgeOfCorrection = N2kDoubleNA; + } + + return true; +} + +//----------------------------------------------------------------------------- +// Date,Time & Local offset +bool ParseN2kPGN129033(const tN2kMsg &N2kMsg, + uint16_t &DaysSince1970, + double &SecondsSinceMidnight, + int16_t &LocalOffset) +{ + if ( N2kMsg.PGN != 129033L ) return false; + + int Index = 0; + + DaysSince1970 = N2kMsg.Get2ByteUInt(Index); + SecondsSinceMidnight = N2kMsg.Get4ByteUDouble(0.0001, Index); + LocalOffset = N2kMsg.Get2ByteInt(Index); + return true; +} + +//----------------------------------------------------------------------------- +// GNSS DOP data +bool ParseN2kPgn129539(const tN2kMsg& N2kMsg, + unsigned char& SID, + tN2kGNSSDOPmode& DesiredMode, + tN2kGNSSDOPmode& ActualMode, + double& HDOP, + double& VDOP, + double& TDOP) +{ + if(N2kMsg.PGN != 129539L) return false; + + unsigned char modes; + int Index = 0; + + SID = N2kMsg.GetByte(Index); + modes = N2kMsg.GetByte(Index); + DesiredMode = (tN2kGNSSDOPmode)( modes & 0x07 ); + ActualMode = (tN2kGNSSDOPmode)( (modes>>3) & 0x07 ); + HDOP = N2kMsg.Get2ByteDouble(0.01, Index); + VDOP = N2kMsg.Get2ByteDouble(0.01, Index); + TDOP = N2kMsg.Get2ByteDouble(0.01, Index); + return true; +} + +//----------------------------------------------------------------------------- +// GNSS Satellites in View + #define MaxSatelliteInfoCount 18 // Fast packet can hold this max stellites. Maybe later more with TP message. + bool ParseN2kPGN129540(const tN2kMsg& N2kMsg, + unsigned char& SID, + tN2kRangeResidualMode &Mode, + uint8_t& NumberOfSVs) +{ + if(N2kMsg.PGN != 129540L) return false; + + int Index = 0; + + SID = N2kMsg.GetByte(Index); + Mode = (tN2kRangeResidualMode)(N2kMsg.GetByte(Index) & 0x03); + NumberOfSVs = N2kMsg.GetByte(Index); + + return true; +} + +bool ParseN2kPGN129540(const tN2kMsg& N2kMsg, + uint8_t SVIndex, + tSatelliteInfo& SatelliteInfo) +{ + if(N2kMsg.PGN != 129540L) return false; + + int Index = 2; + uint8_t NumberOfSVs=N2kMsg.GetByte(Index); + bool ret=( NumberOfSVs>6 & 0x03); + UserID=N2kMsg.Get4ByteUInt(Index); + Longitude=N2kMsg.Get4ByteDouble(1e-07, Index); + Latitude=N2kMsg.Get4ByteDouble(1e-07, Index); + vb=N2kMsg.GetByte(Index); Accuracy=(vb & 0x01); RAIM=(vb>>1 & 0x01); Seconds=(vb>>2 & 0x3f); + COG=N2kMsg.Get2ByteUDouble(1e-04, Index); + SOG=N2kMsg.Get2ByteUDouble(0.01, Index); + vb=N2kMsg.GetByte(Index); // Communication State (19 bits) + vb=N2kMsg.GetByte(Index); + vb=N2kMsg.GetByte(Index); // AIS transceiver information (5 bits) + Heading=N2kMsg.Get2ByteUDouble(1e-04, Index); + ROT=N2kMsg.Get2ByteDouble(3.125E-05, Index); // 1e-3/32.0 + vb=N2kMsg.GetByte(Index); NavStatus=(tN2kAISNavStatus)(vb & 0x0f); + vb=N2kMsg.GetByte(Index); // Reserved + + return true; +} + +//----------------------------------------------------------------------------- +// AIS position report (class B 129039) +bool ParseN2kPGN129039(const tN2kMsg &N2kMsg, + uint8_t &MessageID, + tN2kAISRepeat &Repeat, + uint32_t &UserID, + double &Latitude, + double &Longitude, + bool &Accuracy, + bool &RAIM, + uint8_t &Seconds, + double &COG, + double &SOG, + tN2kAISTransceiverInformation &AISTransceiverInformation, + double &Heading, + tN2kAISUnit &Unit, + bool &Display, + bool &DSC, + bool &Band, + bool &Msg22, + tN2kAISMode &Mode, bool &State) +{ + if (N2kMsg.PGN!=129039L) return false; + + int Index=0; + unsigned char vb; + + vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + UserID=N2kMsg.Get4ByteUInt(Index); + Longitude=N2kMsg.Get4ByteDouble(1e-07, Index); + Latitude=N2kMsg.Get4ByteDouble(1e-07, Index); + vb=N2kMsg.GetByte(Index); Accuracy=(vb & 0x01); RAIM=(vb>>1 & 0x01); Seconds=(vb>>2 & 0x3f); + COG=N2kMsg.Get2ByteUDouble(1e-04, Index); + SOG=N2kMsg.Get2ByteUDouble(0.01, Index); + vb=N2kMsg.GetByte(Index); // Communication State (19 bits) + vb=N2kMsg.GetByte(Index); + vb=N2kMsg.GetByte(Index); // AIS transceiver information (5 bits) + AISTransceiverInformation = (tN2kAISTransceiverInformation)((vb >> 3) & 0x1f); + Heading=N2kMsg.Get2ByteUDouble(1e-04, Index); + vb=N2kMsg.GetByte(Index); // Regional application + vb=N2kMsg.GetByte(Index); + Unit=(tN2kAISUnit)(vb>>2 & 0x01); Display=(vb>>3 & 0x01); DSC=(vb>>4 & 0x01); + Band=(vb>>5 & 0x01); Msg22=(vb>>6 & 0x01); Mode=(tN2kAISMode)(vb>>7 & 0x01); + vb=N2kMsg.GetByte(Index); State=(vb & 0x01); + + return true; +} + +//----------------------------------------------------------------------------- +// AIS Aids to Navigation (AtoN) Report (PGN 129041) +bool ParseN2kPGN129041(const tN2kMsg &N2kMsg, + tN2kAISAtoNReportData &N2kData) +{ + if (N2kMsg.PGN!=129041L) return false; + + int Index=0; + unsigned char vb; + vb=N2kMsg.GetByte(Index); N2kData.MessageID=(vb & 0x3f); N2kData.Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + N2kData.UserID=N2kMsg.Get4ByteUInt(Index); + N2kData.Longitude=N2kMsg.Get4ByteDouble(1e-07, Index); + N2kData.Latitude=N2kMsg.Get4ByteDouble(1e-07, Index); + vb=N2kMsg.GetByte(Index); N2kData.Accuracy=(vb & 0x01); N2kData.RAIM=(vb>>1 & 0x01); N2kData.Seconds=(vb>>2 & 0x3f); + N2kData.Length=N2kMsg.Get2ByteDouble(0.1, Index); + N2kData.Beam=N2kMsg.Get2ByteDouble(0.1, Index); + N2kData.PositionReferenceStarboard=N2kMsg.Get2ByteDouble(0.1, Index); + N2kData.PositionReferenceTrueNorth=N2kMsg.Get2ByteDouble(0.1, Index); + vb=N2kMsg.GetByte(Index); + N2kData.AtoNType=(tN2kAISAtoNType)(vb & 0x1f); + N2kData.OffPositionIndicator=(vb >> 5 & 0x01); + N2kData.VirtualAtoNFlag=(vb >> 6 & 0x01); + N2kData.AssignedModeFlag=(vb >> 7 & 0x01); + N2kData.GNSSType = (tN2kGNSStype)((N2kMsg.GetByte(Index) >> 1) & 0x0f); + N2kData.AtoNStatus=N2kMsg.GetByte(Index); + N2kData.AISTransceiverInformation = (tN2kAISTransceiverInformation)(N2kMsg.GetByte(Index) & 0x1f); + size_t AtoNNameSize = sizeof(N2kData.AtoNName); + N2kMsg.GetVarStr(AtoNNameSize, (char*)N2kData.AtoNName, Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Cross Track Error +bool ParseN2kPGN129283(const tN2kMsg &N2kMsg, + unsigned char& SID, + tN2kXTEMode& XTEMode, + bool& NavigationTerminated, + double& XTE) +{ + if(N2kMsg.PGN != 129283L) + return false; + + int Index = 0; + unsigned char c; + SID = N2kMsg.GetByte(Index); + c = N2kMsg.GetByte(Index); + XTEMode = (tN2kXTEMode)(c & 0x0F); + NavigationTerminated = c & 0x40; + XTE = N2kMsg.Get4ByteDouble(0.01, Index); + return true; +} + +//----------------------------------------------------------------------------- +// Navigation info +bool ParseN2kPGN129284(const tN2kMsg &N2kMsg, + unsigned char& SID, + double& DistanceToWaypoint, + tN2kHeadingReference& BearingReference, + bool& PerpendicularCrossed, + bool& ArrivalCircleEntered, + tN2kDistanceCalculationType& CalculationType, + double& ETATime, + int16_t& ETADate, + double& BearingOriginToDestinationWaypoint, + double& BearingPositionToDestinationWaypoint, + uint32_t& OriginWaypointNumber, + uint32_t& DestinationWaypointNumber, + double& DestinationLatitude, + double& DestinationLongitude, + double& WaypointClosingVelocity) +{ + if(N2kMsg.PGN != 129284L) + return false; + + int Index=0; + unsigned char c; + SID = N2kMsg.GetByte(Index); + DistanceToWaypoint = N2kMsg.Get4ByteUDouble(0.01, Index); + c = N2kMsg.GetByte(Index); + BearingReference = c & 0x01 ? N2khr_magnetic : N2khr_true; + PerpendicularCrossed = c & 0x04; + ArrivalCircleEntered = c & 0x10; + CalculationType = c & 0x40 ? N2kdct_RhumbLine : N2kdct_GreatCircle; + ETATime = N2kMsg.Get4ByteUDouble(0.0001, Index); + ETADate = N2kMsg.Get2ByteUInt(Index); + BearingOriginToDestinationWaypoint = N2kMsg.Get2ByteUDouble(0.0001, Index); + BearingPositionToDestinationWaypoint = N2kMsg.Get2ByteUDouble(0.0001, Index); + OriginWaypointNumber = N2kMsg.Get4ByteUInt(Index); + DestinationWaypointNumber = N2kMsg.Get4ByteUInt(Index); + DestinationLatitude = N2kMsg.Get4ByteDouble(1e-07, Index); + DestinationLongitude = N2kMsg.Get4ByteDouble(1e-07, Index); + WaypointClosingVelocity = N2kMsg.Get2ByteDouble(0.01, Index); + return true; +} + +//----------------------------------------------------------------------------- +// Waypoint list : PGN129285 + + + +//----------------------------------------------------------------------------- +// AIS static data A +bool ParseN2kPGN129794(const tN2kMsg &N2kMsg, + uint8_t &MessageID, + tN2kAISRepeat &Repeat, + uint32_t &UserID, + uint32_t &IMOnumber, + char *Callsign, + size_t CallsignBufSize, + char *Name, + size_t NameBufSize, + uint8_t &VesselType, + double &Length, + double &Beam, + double &PosRefStbd, + double &PosRefBow, + uint16_t &ETAdate, + double &ETAtime, + double &Draught, + char *Destination, + size_t DestinationBufSize, + tN2kAISVersion &AISversion, + tN2kGNSStype &GNSStype, + tN2kAISDTE &DTE, + tN2kAISTransceiverInformation &AISinfo) +{ + if (N2kMsg.PGN!=129794L) return false; + + int Index=0; + unsigned char vb; + + vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + UserID=N2kMsg.Get4ByteUInt(Index); + IMOnumber=N2kMsg.Get4ByteUInt(Index); + N2kMsg.GetStr(CallsignBufSize, Callsign, 7, '@', Index); + N2kMsg.GetStr(NameBufSize, Name, 20, '@', Index); + VesselType=N2kMsg.GetByte(Index); + Length=N2kMsg.Get2ByteDouble(0.1, Index); + Beam=N2kMsg.Get2ByteDouble(0.1, Index); + PosRefStbd=N2kMsg.Get2ByteDouble(0.1, Index); + PosRefBow=N2kMsg.Get2ByteDouble(0.1, Index); + ETAdate=N2kMsg.Get2ByteUInt(Index); + ETAtime=N2kMsg.Get4ByteUDouble(0.0001, Index); + Draught=N2kMsg.Get2ByteDouble(0.01, Index); + N2kMsg.GetStr(DestinationBufSize, Destination, 20, '@', Index); + vb=N2kMsg.GetByte(Index); AISversion=(tN2kAISVersion)(vb & 0x03); GNSStype=(tN2kGNSStype)(vb>>2 & 0x0f); DTE=(tN2kAISDTE)(vb>>6 & 0x01); + vb=N2kMsg.GetByte(Index); AISinfo=(tN2kAISTransceiverInformation)(vb & 0x1f); + + return true; +} + +//----------------------------------------------------------------------------- +// AIS static data class B part A +bool ParseN2kPGN129809(const tN2kMsg &N2kMsg, + uint8_t &MessageID, + tN2kAISRepeat &Repeat, + uint32_t &UserID, + char *Name, + size_t NameBufSize) +{ + if (N2kMsg.PGN!=129809L) return false; + + int Index=0; + unsigned char vb; + + vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + UserID=N2kMsg.Get4ByteUInt(Index); + N2kMsg.GetStr(NameBufSize, Name, 20, '@', Index); + + return true; +} + +//----------------------------------------------------------------------------- +// AIS static data class B part B +bool ParseN2kPGN129810(const tN2kMsg &N2kMsg, + uint8_t &MessageID, + tN2kAISRepeat &Repeat, + uint32_t &UserID, + uint8_t &VesselType, + char *Vendor, + size_t VendorBufSize, + char *Callsign, + size_t CallsignBufSize, + double &Length, + double &Beam, + double &PosRefStbd, + double &PosRefBow, + uint32_t &MothershipID) +{ + if (N2kMsg.PGN!=129810L) return false; + + int Index=0; + unsigned char vb; + + vb=N2kMsg.GetByte(Index); MessageID=(vb & 0x3f); Repeat=(tN2kAISRepeat)(vb>>6 & 0x03); + UserID=N2kMsg.Get4ByteUInt(Index); + VesselType=N2kMsg.GetByte(Index); + N2kMsg.GetStr(VendorBufSize, Vendor, 7, '@', Index); + N2kMsg.GetStr(CallsignBufSize, Callsign, 7, '@', Index); + Length = N2kMsg.Get2ByteUDouble(0.1, Index); + Beam = N2kMsg.Get2ByteUDouble(0.1, Index); + PosRefStbd = N2kMsg.Get2ByteUDouble(0.1, Index); + PosRefBow = N2kMsg.Get2ByteUDouble(0.1, Index); + MothershipID = N2kMsg.Get4ByteUInt(Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Waypoint list PGN130074 + + +//----------------------------------------------------------------------------- +// Wind Speed +bool ParseN2kPGN130306(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &WindSpeed, + double &WindAngle, + tN2kWindReference &WindReference) +{ + if (N2kMsg.PGN!=130306L) return false; + int Index=0; + SID=N2kMsg.GetByte(Index); + WindSpeed=N2kMsg.Get2ByteUDouble(0.01,Index); + WindAngle=N2kMsg.Get2ByteUDouble(0.0001,Index); + WindReference=(tN2kWindReference)(N2kMsg.GetByte(Index)&0x07); + + return true; +} + +//----------------------------------------------------------------------------- +// Outside Environmental parameters +bool ParseN2kPGN130310(const tN2kMsg &N2kMsg, + unsigned char &SID, + double &WaterTemperature, + double &OutsideAmbientAirTemperature, + double &AtmosphericPressure) +{ + if (N2kMsg.PGN!=130310L) return false; + int Index=0; + SID=N2kMsg.GetByte(Index); + WaterTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + OutsideAmbientAirTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + AtmosphericPressure=N2kMsg.Get2ByteUDouble(100,Index); + + return true; +} + + +//----------------------------------------------------------------------------- +// Environmental parameters +bool ParseN2kPGN130311(const tN2kMsg &N2kMsg, + unsigned char &SID, + tN2kTempSource &TempSource, + double &Temperature, + tN2kHumiditySource &HumiditySource, + double &Humidity, + double &AtmosphericPressure) +{ + if (N2kMsg.PGN!=130311L) return false; + unsigned char vb; + int Index=0; + SID=N2kMsg.GetByte(Index); + vb=N2kMsg.GetByte(Index); TempSource=(tN2kTempSource)(vb & 0x3f); HumiditySource=(tN2kHumiditySource)(vb>>6 & 0x03); + Temperature=N2kMsg.Get2ByteUDouble(0.01,Index); + Humidity=N2kMsg.Get2ByteDouble(0.004,Index); + AtmosphericPressure=N2kMsg.Get2ByteUDouble(100,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Temperature +// Temperatures should be in Kelvins +bool ParseN2kPGN130312(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &TempInstance, + tN2kTempSource &TempSource, + double &ActualTemperature, + double &SetTemperature) +{ + if (N2kMsg.PGN!=130312L) return false; + int Index=0; + SID=N2kMsg.GetByte(Index); + TempInstance=N2kMsg.GetByte(Index); + TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index)); + ActualTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + SetTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Humidity +// Humidity should be in percent +bool ParseN2kPGN130313(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &HumidityInstance, + tN2kHumiditySource &HumiditySource, + double &ActualHumidity, + double &SetHumidity) +{ + if (N2kMsg.PGN != 130313L) return false; + int Index = 0; + SID=N2kMsg.GetByte(Index); + HumidityInstance=N2kMsg.GetByte(Index); + HumiditySource=(tN2kHumiditySource)N2kMsg.GetByte(Index); + ActualHumidity=N2kMsg.Get2ByteDouble(0.004, Index); + SetHumidity=N2kMsg.Get2ByteDouble(0.004, Index); + return true; +} + +//----------------------------------------------------------------------------- +// Actual Pressure +// Pressure should be in Pascals +bool ParseN2kPGN130314(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &PressureInstance, + tN2kPressureSource &PressureSource, + double &ActualPressure) +{ + if (N2kMsg.PGN != 130314L) return false; + int Index = 0; + SID=N2kMsg.GetByte(Index); + PressureInstance=N2kMsg.GetByte(Index); + PressureSource=(tN2kPressureSource)N2kMsg.GetByte(Index); + ActualPressure=N2kMsg.Get4ByteDouble(0.1, Index); + return true; +} + +//----------------------------------------------------------------------------- +// Set Pressure +// Pressure should be in Pascals PGN130315 + + +//----------------------------------------------------------------------------- +// Temperature extended range +// Temperatures should be in Kelvins +bool ParseN2kPGN130316(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &TempInstance, + tN2kTempSource &TempSource, + double &ActualTemperature, + double &SetTemperature) +{ + if (N2kMsg.PGN!=130316L) return false; + int Index=0; + SID=N2kMsg.GetByte(Index); + TempInstance=N2kMsg.GetByte(Index); + TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index)); + ActualTemperature=N2kMsg.Get3ByteDouble(0.001,Index); + SetTemperature=N2kMsg.Get2ByteUDouble(0.1,Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Meteorological Station Data +bool ParseN2kPGN130323(const tN2kMsg &N2kMsg, + tN2kMeteorlogicalStationData &N2kData) +{ + if (N2kMsg.PGN!=130323L) return false; + int Index=0; + unsigned char vb; + + vb = N2kMsg.GetByte(Index); + N2kData.Mode=(tN2kAISMode)(vb & 0x0f); + N2kData.SystemDate = N2kMsg.Get2ByteUInt(Index); + N2kData.SystemTime = N2kMsg.Get4ByteUDouble(0.0001,Index); + N2kData.Latitude = N2kMsg.Get4ByteDouble(1e-7,Index); + N2kData.Longitude = N2kMsg.Get4ByteDouble(1e-7,Index); + N2kData.WindSpeed = N2kMsg.Get2ByteUDouble(0.01,Index); + N2kData.WindDirection = N2kMsg.Get2ByteUDouble(0.0001,Index); + vb = N2kMsg.GetByte(Index); + N2kData.WindReference = (tN2kWindReference)(vb & 0x07); + N2kData.WindGusts = N2kMsg.Get2ByteUDouble(0.01,Index); + N2kData.AtmosphericPressure = N2kMsg.Get2ByteUDouble(100,Index); + N2kData.OutsideAmbientAirTemperature=N2kMsg.Get2ByteUDouble(0.01,Index); + size_t StationIDSize = sizeof(N2kData.StationID); + size_t StationNameSize = sizeof(N2kData.StationName); + N2kMsg.GetVarStr(StationIDSize, (char*)N2kData.StationID, Index); + N2kMsg.GetVarStr(StationNameSize, (char*)N2kData.StationName, Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Trim Tab Position +// Trim tab position is a percentage 0 to 100% where 0 is fully retracted and 100 is fully extended +bool ParseN2kPGN130576(const tN2kMsg &N2kMsg, + int8_t &PortTrimTab, + int8_t &StbdTrimTab) +{ + if (N2kMsg.PGN!=130576L) return false; + int Index=0; + PortTrimTab=N2kMsg.GetByte(Index); + StbdTrimTab=N2kMsg.GetByte(Index); + + return true; +} + +//----------------------------------------------------------------------------- +// Direction Data +bool ParseN2kPGN130577(const tN2kMsg &N2kMsg, + tN2kDataMode &DataMode, + tN2kHeadingReference &CogReference, + unsigned char &SID, + double &COG, + double &SOG, + double &Heading, + double &SpeedThroughWater, + double &Set, + double &Drift) +{ + if (N2kMsg.PGN!=130577L) return false; + int Index=0; + unsigned char v; + v = N2kMsg.GetByte(Index); + DataMode = (tN2kDataMode)(v & 0x0F); + CogReference = (tN2kHeadingReference)((v >> 4) & 0x03); + SID = N2kMsg.GetByte(Index); + COG = N2kMsg.Get2ByteUDouble(0.0001,Index); + SOG = N2kMsg.Get2ByteUDouble(0.01, Index); + Heading = N2kMsg.Get2ByteUDouble(0.0001, Index); + SpeedThroughWater= N2kMsg.Get2ByteUDouble(0.01, Index); + Set = N2kMsg.Get2ByteUDouble(0.0001, Index); + Drift = N2kMsg.Get2ByteUDouble(0.01, Index); + return true; +} + + +//----------------------------------------------------------------------------- +// Temperature High Range [MARETRON TMP100] +// Temperatures should be in Kelvin +bool ParseN2kMaretronPGN130823(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &TempInstance, + tN2kTempSource &TempSource, + double &ActualTemperature, + double &SetTemperature) +{ + if (N2kMsg.PGN!=130823L) return false; + int Index=0; + if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false; + SID=N2kMsg.GetByte(Index); + TempInstance=N2kMsg.GetByte(Index); + TempSource=(tN2kTempSource)(N2kMsg.GetByte(Index)); + ActualTemperature=N2kMsg.Get2ByteUDouble(0.1,Index); + SetTemperature=N2kMsg.Get2ByteUDouble(0.1,Index); + return true; +} + +//----------------------------------------------------------------------------- +// Fluid Flow Rate [MARETRON FFM100] +// Flow unit is 1 lt/hour +bool ParseN2kMaretronPGN65286(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &FlowRateInstance, + tN2kFluidType &FluidType, + double &FluidFlowRate) +{ + if (N2kMsg.PGN!=65286L) return false; + int Index=0; + if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false; + SID=N2kMsg.GetByte(Index); + FlowRateInstance=N2kMsg.GetByte(Index); + FluidType=(tN2kFluidType)(N2kMsg.GetByte(Index)); + FluidFlowRate=N2kMsg.Get3ByteDouble(0.1,Index); + return true; +} + +//----------------------------------------------------------------------------- +// Trip Volume [MARETRON FFM100] +// Volume unit is 1 litre +bool ParseN2kMaretronPGN65287(const tN2kMsg &N2kMsg, + unsigned char &SID, + unsigned char &VolumeInstance, + tN2kFluidType &FluidType, + double &TripVolume) +{ + if (N2kMsg.PGN!=65287L) return false; + int Index=0; + if (N2kMsg.Get2ByteUInt(Index)!=MaretronProprietary ) return false; + SID=N2kMsg.GetByte(Index); + VolumeInstance=N2kMsg.GetByte(Index); + FluidType=(tN2kFluidType)(N2kMsg.GetByte(Index)); + TripVolume=N2kMsg.Get3ByteDouble(1,Index); + return true; +} + + +*/ + + + + + + + + + + + + + +//=========================================================================================================================================== + +// Foonctions provenant de N2kMsg.cpp : https://ttlappalainen.github.io/NMEA2000/_n2k_msg_8cpp_source.html + + +/* +//----------------------------------------------------------------------------- +unsigned char tN2kMsg::GetByte(int &Index) const { + if (Index(2, index, buf); +} + +//----------------------------------------------------------------------------- +uint16_t GetBuf2ByteUInt(int &index, const unsigned char *buf) { + return GetBuf(2, index, buf); +} + +//----------------------------------------------------------------------------- +uint32_t GetBuf3ByteUInt(int &index, const unsigned char *buf) { + return GetBuf(3, index, buf); +} + +//----------------------------------------------------------------------------- +uint32_t GetBuf4ByteUInt(int &index, const unsigned char *buf) { + return GetBuf(4, index, buf); +} + +//----------------------------------------------------------------------------- +uint64_t GetBuf8ByteUInt(int &index, const unsigned char *buf) { + return GetBuf(8, index, buf); +} + +//----------------------------------------------------------------------------- +double GetBuf1ByteDouble(double precision, int &index, const unsigned char *buf, double def) { + int8_t vl = GetBuf(1, index, buf); + if (vl==0x7f) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf1ByteUDouble(double precision, int &index, const unsigned char *buf, double def) { + uint8_t vl = GetBuf(1, index, buf); + if (vl==0xff) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf2ByteDouble(double precision, int &index, const unsigned char *buf, double def) { + int16_t vl = GetBuf(2, index, buf); + if (vl==0x7fff) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf2ByteUDouble(double precision, int &index, const unsigned char *buf, double def) { + uint16_t vl = GetBuf(2, index, buf); + if (vl==0xffff) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf8ByteDouble(double precision, int &index, const unsigned char *buf, double def) { + int64_t vl = GetBuf(8, index, buf); + if (vl==0x7fffffffffffffffLL) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBufDouble(int &index, const unsigned char *buf, double def) { + int64_t vl = GetBuf(8, index, buf); + double ret; + // On avr double==float, so we test it also. Currently no handling for avr. + if ( sizeof(double)==8 && !N2kIsNA(vl) ) { + memcpy(&ret,&vl,8); + if ( isnan(ret) ) ret=def; + } else { + ret=def; + } + return ret; +} + +//----------------------------------------------------------------------------- +float GetBufFloat(int &index, const unsigned char *buf, float def) { + int32_t vl = GetBuf(4, index, buf); + float ret; + if ( !N2kIsNA(vl) ) { + memcpy(&ret,&vl,4); + if ( isnan(ret) ) ret=def; + } else { // On avr double==float + ret=def; + } + return ret; +} + +//----------------------------------------------------------------------------- +double GetBuf3ByteDouble(double precision, int &index, const unsigned char *buf, double def) { + int32_t vl = GetBuf(3, index, buf); + if (vl==0x007fffff) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf4ByteDouble(double precision, int &index, const unsigned char *buf, double def) { + int32_t vl = GetBuf(4, index, buf); + if (vl==0x7fffffff) return def; + + return vl * precision; +} + +//----------------------------------------------------------------------------- +double GetBuf4ByteUDouble(double precision, int &index, const unsigned char *buf, double def) { + uint32_t vl = GetBuf(4, index, buf); + if (vl==0xffffffff) return def; + + return vl * precision; +} + +*/ diff --git a/sources/nmea.h b/sources/nmea.h new file mode 100644 index 0000000..1a5bceb --- /dev/null +++ b/sources/nmea.h @@ -0,0 +1,72 @@ +//********************************************************************************************************************************************************************************** +// Fichier NMEA.H +// +// Contient tous les define liés au réseau NMEA +// +//**********************************************************************************************************************************************************************************/ + + // Maximums : +#define NMEA_MAX_NOMBRE_PGN 250 +#define NMEA_MAX_TAILLE_DATA_PGN 223 // C'est la taille maxi des DATA d'un Fast Packet +// #define NMEA_MAX_NOMBRE_PGN_ACTIFS 50 + + // Tempos : +#define NMEA_TEMPO_INIT 10 // En x 100ms +#define NMEA_PERIODE_CHECK 5 // EN x 100ms + + + // Type de PGN : +#define NMEA_PGN_TYPE_INCONNU -1 +#define NMEA_PGN_TYPE_SYSTEM_SINGLE_FRAME 0 +#define NMEA_PGN_TYPE_SYSTEM_FAST_PACKET 1 +#define NMEA_PGN_TYPE_DEFAULT_SINGLE_FRAME 2 +#define NMEA_PGN_TYPE_MANDATORY_FAST_PACKET 3 +#define NMEA_PGN_TYPE_DEFAULT_FAST_PACKET 4 +#define NMEA_PGN_TYPE_PROPRIETARY_FAST_PACKET 5 + + + // Mode de parse des PGN : +#define NMEA_PARSE_MODE_CREATION 0 +#define NMEA_PARSE_MODE_MISE_A_JOUR 1 +#define NMEA_PARSE_MODE_SUPPRESSION 2 + + + // Fields de PGN : +#define NMEA_126992_TIME_SOURCE 2 +// #define NMEA_126992_RESERVED 3 +#define NMEA_126992_SYSTEM_DATE 4 +#define NMEA_126992_SYSTEM_TIME 5 + +#define NMEA_129029_DAYS_SINCE_1970 2 +#define NMEA_129029_SECONDS_SINCE_MIDNIGHT 3 +#define NMEA_129029_LATITUDE 4 +#define NMEA_129029_LONGITUDE 5 +#define NMEA_129029_ALTITUDE 6 +#define NMEA_129029_GNSS_TYPE 7 +#define NMEA_129029_GNSS_METHOD 8 +//#define NMEA_129029_RESERVED 9 +#define NMEA_129029_SATELLITES_NUMBER 11 +#define NMEA_129029_HDOP 12 +#define NMEA_129029_PDOP 13 + +#define NMEA_129540_SATELLITES_IN_VIEW 4 + +#define NMEA_130306_WIND_SPEED_TRUE_NORTH 20 +#define NMEA_130306_WIND_ANGLE_TRUE_NORTH 30 +#define NMEA_130306_WIND_SPEED_MAGNETIC 21 +#define NMEA_130306_WIND_ANGLE_MAGNETIC 31 +#define NMEA_130306_WIND_SPEED_APPARENT 22 +#define NMEA_130306_WIND_ANGLE_APPARENT 32 +#define NMEA_130306_WIND_SPEED_TRUE_BOAT 23 +#define NMEA_130306_WIND_ANGLE_TRUE_BOAT 33 +#define NMEA_130306_WIND_SPEED_TRUE_WATER 24 +#define NMEA_130306_WIND_ANGLE_TRUE_WATER 34 + + + // Etats du séquenceur Nmea : +#define ETAT_SEQUENCEUR_NMEA_INIT 0 +#define ETAT_SEQUENCEUR_NMEA_REPOS 1 + + + + diff --git a/sources/reseaux.c b/sources/reseaux.c new file mode 100644 index 0000000..23eeecf --- /dev/null +++ b/sources/reseaux.c @@ -0,0 +1,1080 @@ +// ********************************************************************************************************************************************************************************** +// Fichier RESEAUX.C +// +// Contient toutes les fonctions liées aux réseaux (Ethernet et Wifi en mode STA et AP notamment). +// ********************************************************************************************************************************************************************************** + + +//#define __DEBUG_RESEAUX //Décommenter cette ligne pour avoir les messages de debug concernant les reseaux +//#define __DEBUG_SHIP2NET //Décommenter cette ligne pour avoir les messages de debug concernat les accès à Ship2net + +// Include : +#include "main.h" + +// Prototypes : +void gestion_reseaux(void); +int8_t init_reseaux(void); +int8_t connexion_ship2net(); +int8_t process_webgl(uint8_t no); +int8_t get_ip_address(void); +int8_t get_ssid_list(void); + + +// Variables globales : +struct Struct_reseaux reseaux; + + + +//********************************************************************************************************************************************************************************** +// Fonction : Gestion des interfaces Wifi +// Reçoit : Rien +// Retourne : Rien +//********************************************************************************************************************************************************************************** +void gestion_reseaux(void) +{ +uint16_t i; +FILE * ptr_fichier; +uint8_t channel; +char hw_mode[5]; // A priori, 'a' ou 'g'... +char commande[MAX_TAILLE_COMMANDE_SHELL]; +char ligne[MAX_LONGUEUR_LIGNE_FICHIER_CONFIG_RESEAUX]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(reseaux.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_INIT : + { + if (reseaux.sequenceur.compteur == 0) + { + reseaux.compteur_check_adresses_ip = RESEAUX_PERIODE_CHECK_ADRESSE_IP_INIT; // En plaçant l'init de la tempo ici, on est sûr qu'elle démarre après la phase d'initialisation + reseaux.compteur_connexion_ship2net = RESEAUX_PERIODE_CONNEXION_SHIP2NET_INIT; + reseaux.compteur_scan_sta = RESEAUX_PERIODE_SCAN_WIFI_STA_INIT; + reseaux.compteur_check_webgl = RESEAUX_PERIODE_CHECK_WEBGL_INIT; + + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_REPOS : + { + //================================== + // Nouvelle configuration Wifi reçue : + if (reseaux.wifi_new.flag_new_config) + { + reseaux.wifi_new.flag_new_config = FALSE; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_STA; + break; + } + + //================================== + // Demande de scan de Wifi STA reçue : + if (reseaux.wifi_new.sta.flag_dde_scan) + { + reseaux.wifi_new.sta.flag_dde_scan = false; + reseaux.compteur_scan_sta = reseaux.periode_scan_sta; // Réarmement du compteur automatique puisqu'une demande manuelle a été reçue + if ((reseaux.wifi.sta.cde == ON) && (reseaux.wifi_new.sta.cde == ON)) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_LANCEMENT; // Si le Wifi STA est OFF ou si on demande qu'il passe OFF, il est inutile de faire un scan (qui pourra bloquer le séquenceur 20 secondes si time-out) + break; + } + + //================================== + // Scan périodique du Wifi STA : + if ((reseaux.periode_scan_sta != 0) && (reseaux.compteur_scan_sta == 0)) + { + reseaux.compteur_scan_sta = reseaux.periode_scan_sta; + if ((reseaux.wifi.sta.cde == ON) && (reseaux.wifi_new.sta.cde == ON)) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_LANCEMENT; // Si le Wifi STA est OFF ou si on demande qu'il passe OFF, il est inutile de faire un scan (qui pourra bloquer le séquenceur 20 secondes si time-out) + break; // Pas obligatoire mais permet de lisser la charge + } + + //================================== + // Vérification périodique des adresses IP : + if ((reseaux.periode_check_adresses_ip != 0) && (reseaux.compteur_check_adresses_ip == 0)) + { + reseaux.compteur_check_adresses_ip = reseaux.periode_check_adresses_ip; + get_ip_address(); + break; // Pas obligatoire mais permet de lisser la charge + } + + //================================== + // Connexion Ship2net : + if ((reseaux.periode_connexion_ship2net != 0) && (reseaux.compteur_connexion_ship2net == 0)) + { + reseaux.compteur_connexion_ship2net = reseaux.periode_connexion_ship2net; + connexion_ship2net(); + break; // Pas obligatoire mais permet de lisser la charge + } + + //================================== + // Processus WebGL : + if (reseaux.compteur_check_webgl == 0) + { + reseaux.compteur_check_webgl = RESEAUX_PERIODE_CHECK_WEBGL; + + for (i = 0; i < MAX_NOMBRE_PROCESS_WEBGL; i++) + { + if ((reseaux.webgl_new[i].validite != reseaux.webgl[i].validite) || + (strcmp(reseaux.webgl_new[i].parametres, reseaux.webgl[i].parametres) != 0)) + { + process_webgl(i); + } + } + + break; // Pas obligatoire mais permet de lisser la charge + } + + break; + } + + //========================================================================================================= + // Wifi mode STA : + //========================================================================================================= + // La procédure est la suivante : + // - Si modification demandée du fichier 'wpa_supplicant' : + // - mise à jour du fichier, + // - redémarrage du service dhcp Attention : le restart du service redemarre wlan0 s'il était OFF. D'où la nécessité de traiter les commandes APRES le fichier... + // - redémarrage du daemon de service Pas sûr que ce soit aboslument nécessaire - A TESTER + // - Si changement de commande : + // - Si commande == ON, passage à up de wlan0 Utile s'il était Off ET qu'on n'a pas fait de modif de fichier (sinon, le restart de dhcp a dû le redémarrer) + // - Sinon (OFF), passage à down de wlan0 Utile s'il était On OU si on a fait une modif de fichier (et que le restart de dhcp l'a redémarré) + // - Fin pour STA + + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_STA : + { + // Vérification si une modification du fichier 'wpa_supplicant.conf' est nécessaire : + if ((strcmp(reseaux.wifi_new.sta.ssid, reseaux.wifi.sta.ssid) != 0) || + (strcmp(reseaux.wifi_new.sta.bssid, reseaux.wifi.sta.bssid) != 0) || + (strcmp(reseaux.wifi_new.sta.psk, reseaux.wifi.sta.psk) != 0) ) + { + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_WPA_SUPPLICANT; // Début de mise à jour du fichier + } + else // S'il n'y a pas besoin de modifier le fichier 'hostapd.conf' : + { + if (reseaux.wifi_new.sta.cde != reseaux.wifi.sta.cde) + { + if (reseaux.wifi_new.sta.cde == ON) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_UP; // Commande de mise à ON + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_DOWN; // Commande de mise à OFF + } + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI_STA; // Fin de traitement STA + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_WPA_SUPPLICANT : + { + // Regénération complete (plus simple...) du fichier de config 'wpa_supplicant.conf' : + ptr_fichier = fopen(NOM_FICHIER_WPA_SUPPLICANT, "w"); // Le fichier est juste ouvert en écriture (donc il est créé s'il n'existe pas et il est vidé s'il existe) + if (ptr_fichier != NULL) + { + sprintf(ligne, "country=FR\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "ap_scan=1\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "update_config=1\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "network={\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "\tssid=\"%s\"\n", reseaux.wifi_new.sta.ssid); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "\tpsk=\"%s\"\n", reseaux.wifi_new.sta.psk); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "\tbssid=%s\n", reseaux.wifi_new.sta.bssid); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "}\n"); + fputs(ligne, ptr_fichier); + + fclose(ptr_fichier); + +#ifdef __DEBUG_RESEAUX + sprintf(tmp, "Le fichier 'wpa_supplicant.conf' a été mis à jour avec succès."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else + { + sprintf(tmp, "/!\\ La mise à jour du fichier de 'wpa_supplicant.conf' n'est pas possible. La configuration se poursuit néanmoins..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_MAJ_FICHIER_WPA_SUPPLICANT; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DHCPCD_RESTART; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DHCPCD_RESTART : + { + if (reseaux.sequenceur.compteur == 0) + { + // Rechargement du service DHCPCD (mode STA) : + sprintf(commande, "sudo systemctl restart dhcpcd.service &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "Le rechargement de DHCPCD (Wifi STA) par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "Le rechargement de DHCPCD (Wifi STA) par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_DHCPCD_RESTART; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DAMEON_SERVICES_RESTART; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DAMEON_SERVICES_RESTART : + { + if (reseaux.sequenceur.compteur == 0) + { + // Rechargement du Daemon de services (mode STA) : + sprintf(commande, "sudo systemctl daemon-reload &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "Le rechargement du Daemon de Services (Wifi STA) par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "Le rechargement du Daemon de Services (Wifi STA) par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_DAMEON_SERVICES_RESTART; + + if (reseaux.wifi_new.sta.cde == ON) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_UP; // Commande de mise à ON + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_DOWN; // Commande de mise à OFF + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_UP : + { + if (reseaux.sequenceur.compteur == 0) + { + // Passage de l'interface WLAN0 (mode STA) à UP : + sprintf(commande, "sudo ip link set wlan0 up &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "La passage de l'interface WLAN0 (Wifi STA) à UP par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "La passage de l'interface WLAN0 (Wifi STA) à UP par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_COMMANDE_WIFI_STA_UP; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI_STA; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_DOWN : + { + if (reseaux.sequenceur.compteur == 0) + { + // Passage de l'interface WLAN0 (mode STA) à DOWN : + sprintf(commande, "sudo ip link set wlan0 down &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "La passage de l'interface WLAN0 (Wifi STA) à DOWN par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "La passage de l'interface WLAN0 (Wifi STA) à DOWN par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_COMMANDE_WIFI_STA_DOWN; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI_STA; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI_STA : + { + if (reseaux.sequenceur.compteur == 0) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_AP; + + break; + } + + + //========================================================================================================= + // Wifi mode AP : + //========================================================================================================= + // La procédure est la suivante : + // - Si modification demandée du fichier 'hostapd' : + // - mise à jour du fichier, + // - redémarrage du service hostapd + // - Si changement de commande : + // - Si commande == ON, passage à up de uap0 + // - Sinon (OFF), passage à down de uap0 + // - Fin pour AP + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_AP : + { + // Vérification si action sur AP est nécessaire : + if ((strcmp(reseaux.wifi_new.ap.ssid, reseaux.wifi.ap.ssid) != 0) || + (strcmp(reseaux.wifi_new.ap.pwd, reseaux.wifi.ap.pwd) != 0) || + (reseaux.wifi_new.ap.canal != reseaux.wifi.ap.canal) ) + { + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_HOSTPAD; + } + else // S'il n'y a pas besoin de modifier le fichier 'hostapd.conf' : + { + if (reseaux.wifi_new.ap.cde != reseaux.wifi.ap.cde) + { + if (reseaux.wifi_new.ap.cde == ON) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_UP; // Commande de mise à ON + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_DOWN; // Commande de mise à OFF + } + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI; // Fin de traitement + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_HOSTPAD : + { + // Regénération complete (plus simple...) du fichier de config 'hostapd.conf' : + ptr_fichier = fopen(NOM_FICHIER_HOSTAPD, "w"); // Le fichier est juste ouvert en écriture (donc il est créé s'il n'existe pas et il est vidé s'il existe) + if (ptr_fichier != NULL) + { + sprintf(ligne, "country_code=FR\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "interface=uap0\n"); + fputs(ligne, ptr_fichier); + + switch(reseaux.wifi_new.ap.canal) + { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + { + channel = reseaux.wifi_new.ap.canal; + strcpy(hw_mode, "g"); + break; + } + + case 36: + case 40: + case 44: + case 48: + // Les canaux > 48 déclenchent une erreurlors de la prise en compte par 'hostapd'... + // Ils apparaissent effectivement avec la mention '(no IR, radar detection)' dans le retour de 'iw list'. + /* case 52: + case 56: + case 60: + case 64: + case 100: + case 104: + case 108: + case 112: + case 116: + case 120: + case 124: + case 128: + case 132: + case 136: + case 140: + */ + { + channel = reseaux.wifi_new.ap.canal; + strcpy(hw_mode, "a"); + break; + } + default: + { + channel = 8; + strcpy(hw_mode, "g"); + } + } + + sprintf(ligne, "hw_mode=%s\n", hw_mode); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "channel=%i\n", channel); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "ssid=%s\n", reseaux.wifi_new.ap.ssid); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "wpa_passphrase=%s\n", reseaux.wifi_new.ap.pwd); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "macaddr_acl=0\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "auth_algs=1\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "ignore_broadcast_ssid=0\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "wpa=2\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "wpa_key_mgmt=WPA-PSK\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "wpa_pairwise=TKIP\n"); + fputs(ligne, ptr_fichier); + + sprintf(ligne, "rsn_pairwise=CCMP\n"); + fputs(ligne, ptr_fichier); + + fclose(ptr_fichier); +#ifdef __DEBUG_RESEAUX + sprintf(tmp, "Le fichier 'hostpad.conf' a été mis à jour avec succès."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else + { + sprintf(tmp, "/!\\ La mise à jour du fichier de 'hostapd' n'est pas possible. La configuration se poursuit néanmoins..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_MAJ_FICHIER_HOSTAPD; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_HOSTAPD_RESTART; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_HOSTAPD_RESTART : + { + if (reseaux.sequenceur.compteur == 0) + { + // Rechargement du service HOSTAPD (mode AP) : + sprintf(commande, "sudo systemctl restart hostapd.service &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "Le rechargement de HOSTAPD (Wifi AP) par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "Le rechargement de HOSTAPD (Wifi AP) par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_HOSTAPD_RESTART; + if (reseaux.wifi_new.ap.cde == ON) reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_UP; // Commande de mise à ON + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_DOWN; // Commande de mise à OFF + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_UP : + { + if (reseaux.sequenceur.compteur == 0) + { + // Passage de l'interface UAP0 (mode AP) à UP : + sprintf(commande, "sudo ip link set uap0 up &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "Le passage de l'interface UAP0 (Wifi AP) à UP par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "Le passage de l'interface UAP0 (Wifi AP) à UP par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_COMMANDE_WIFI_AP_UP; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI; + } + + break; + } + + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_DOWN : + { + if (reseaux.sequenceur.compteur == 0) + { + // Passage de l'interface UAP0 (mode AP) à DOWN : + sprintf(commande, "sudo ip link set uap0 down &"); // Le '&' permet de libérer l'exécution. La tempo en fin de Case permet de gérer l'attente... + if (commande_shell(commande) != 0) + { + sprintf(tmp, "Le passage de l'interface UAP0 (Wifi AP) à DOWN par la commande [%s] a rencontré une erreur. La procédure continue néanmoins...", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +#ifdef __DEBUG_RESEAUX + else + { + sprintf(tmp, "Le passage de l'interface UAP0 (Wifi AP) à DOWN par la commande [%s] a été effectuée avec succès.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +#endif + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_COMMANDE_WIFI_AP_DOWN; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI; // Le mode AP est déjà arrêté, on a terminé... + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI : + { + if (reseaux.sequenceur.compteur == 0) + { + strcpy(reseaux.wifi_new.sta.adresse_ip, reseaux.wifi.sta.adresse_ip); // Ce sont les 2 seuls champs qui sont à jour dans 'wifi' et pas dans 'wifi_new'.... + strcpy(reseaux.wifi_new.ap.adresse_ip, reseaux.wifi.ap.adresse_ip); + + reseaux.wifi = reseaux.wifi_new; // Mise à jour d'un coup de 'wifi' avec les valeurs de 'wifi_new'. + + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; + } + + break; + } + + + //========================================================================================================= + // Scan Wifi STA : + //========================================================================================================= + // La procédure est la suivante : + // - Lancement de la commande shell qui va réaliser le scan et déposer le résultat dans un fichier temporaire + // - Attente d'une seconde, et vérification si la taille du fichier est nulle. + // - si taille = 0, relance la tempo (1s) et boucle + // - si taille != 0, lecture du fichier, formatage de la liste et mise à jour dans la BDD + // - Fin du scan + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_LANCEMENT : + { + get_ssid_list(); // Lancement du scan proprement dit + + reseaux.time_out_scan_sta = RESEAUX_TIME_OUT_LECTURE_FICHIER_SCAN_STA; + reseaux.sequenceur.compteur = TEMPO_ATTENTE_BOUCLE_SCAN_WIFI_STA; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_ATTENTE_FIN; + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_ATTENTE_FIN : + { + if ((reseaux.wifi.sta.cde == OFF) || (reseaux.wifi_new.sta.cde == OFF)) // Si le Wifi STA est OFF ou si on demande à le passer OFF, inutile de continuer le scan qui risque de bloquer le séquenceur jusqu'à 20 secondes (de time-out) + { + sprintf(tmp, "La commande du Wifi STA est OFF ou une demande à passer OFF est en cours. Le scan du Wifi STA est donc arrêtée..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + reseaux.time_out_scan_sta = 0; + reseaux.sequenceur.compteur = 0; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; + } + + if (reseaux.sequenceur.compteur == 0) + { + // Lire la taille du fichier : + sprintf(tmp, "%s/%s", REPERTOIRE_TMP_WIFI, NOM_FICHIER_SCAN_WIFI_STA); + ptr_fichier = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... + + if (ptr_fichier != NULL) + { + fseek (ptr_fichier, 0, SEEK_END); // Place le curseur à la fin du fichier + if (ftell(ptr_fichier) != 0) // Lit la position du curseur, donc la taille... + { + fclose(ptr_fichier); // Fermeture du fichier (on duplique le close ici car il y a un break juste après...) + reseaux.time_out_scan_sta = 0; // RAZ "par principe" + reseaux.sequenceur.compteur = TEMPO_ATTENTE_FIN_SCAN_WIFI_STA; // Lancement petite tempo pour être sûr que le remplissage du fichier est terminé + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_TRAITEMENT; // Passage du séquenceur au traitement à la fin de cette petite tempo + break; + } + } + + fclose(ptr_fichier); // Fermeture du fichier si l'on n'est pas sorti avant avec un fichier non vide + + // Vérification s'il faut poursuivre l'attente ou considérer le scan comme ayant échoué : + if (-- reseaux.time_out_scan_sta != 0) reseaux.sequenceur.compteur = TEMPO_ATTENTE_BOUCLE_SCAN_WIFI_STA; // Si on n'a pas encore atteint le time-out, on relance une tempo d'attente + else reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; // Time out !! + } + + break; + } + + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_TRAITEMENT : + { + if (reseaux.sequenceur.compteur == 0) + { + sprintf(tmp, "%s/%s", REPERTOIRE_TMP_WIFI, NOM_FICHIER_SCAN_WIFI_STA); + ptr_fichier = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... + + if (ptr_fichier != NULL) + { +//fseek (ptr_fichier, 0, SEEK_SET); // Place le curseur au début du fichier + clear_scan_wifi_sta(); // Supprime les entrées non 'connues' de TB_Wifi_sta + + int val, tot; // L'utilisation de 'int' est guidée par l'utilisation de sscanf + char * ptr; + char chaine[100]; + char mac_ssid[100]; + char nom_ssid[100]; + uint8_t canal_ssid = 0; + uint8_t qualite_ssid = 0; + + + // Lecture d'une ligne : + while (fgets(commande, MAX_TAILLE_COMMANDE_SHELL, ptr_fichier) != NULL) + { + commande[strlen(commande) - 1] = 0; // Suppression du LF de fin + + //---------------------------------------------- + // Détection de début de cellule : + // Ligne du type : Cell 01 - Address: 94:A6:7E:B0:BE:C7 + if ((strstr(commande, "Cell") != NULL) && (strstr(commande, "- Address") != NULL)) + { + mac_ssid[0]= 0; + canal_ssid = 0; + qualite_ssid = 0; + nom_ssid[0] = 0; + } + + //---------------------------------------------- + ptr = strstr(commande, "Address: "); + if (ptr != NULL) // Ligne du type : Cell 01 - Address: 94:A6:7E:B0:BE:C7 + { + sprintf(mac_ssid, "%s", (ptr + strlen("Address: ")) ); // Positionnement du début de chaine après "Address: " donc extraction de "94:A6:7E:B0:BE:C7' + } + + //---------------------------------------------- + ptr = strstr(commande, "Channel:"); + if (ptr != NULL) // Ligne du type : Channel:44 + { + sprintf(chaine, "%s", (ptr + strlen("Channel:")) ); // Positionnement du début de chaine après "Channel:" donc extraction de "44" + canal_ssid = atoi(chaine); + } + + //---------------------------------------------- + ptr = strstr(commande, "Quality="); + if (ptr != NULL) // Ligne du type : Quality=57/70 Signal level=-53 dBm + { + sprintf(chaine, "%s", (ptr + strlen("Quality=")) ); // Positionnement du début de chaine après "Quality=" donc extraction de "57/70 Signal level=-53 dBm" + sscanf(chaine, "%d/%d", &val, &tot); // Extraction de 57 et 70 + if (tot != 0) qualite_ssid = ((val * 100) / tot); + else qualite_ssid = 1; // On évite le 0 qui ferait supprimer cette entrée (et le fait de ne pas décoder correctement le niveau n'est pas bloquant) + } + + //---------------------------------------------- + ptr = strstr(commande, "SSID:"); + if (ptr != NULL) // Ligne du type : ESSID:"Koriolan" + { + sprintf(nom_ssid, "%s", (ptr + strlen("SSID:") + 1) ); // Le '+1' permet de supprimer le '"' de début du nom + nom_ssid[strlen(nom_ssid) - 1] = 0; // Suppression du '"' de fin de nom + } + + // Traitement d'une cellule : + if ((mac_ssid[0] != 0) && (canal_ssid != 0) && (qualite_ssid != 0) && (nom_ssid[0] != 0)) + { +#ifdef __DEBUG_RESEAUX + sprintf(tmp, "SSID lu dans fichier de scan : %s [mac = %s, canal = %i et niveau = %i %%]", nom_ssid, mac_ssid, canal_ssid, qualite_ssid); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + enregistrement_scan_wifi_sta(mac_ssid, nom_ssid, canal_ssid, qualite_ssid); // Enregistrement du SSID dans la liste (ou mise à jour si déjà connu) + + // Pour ne pas enregistrer à nouveau ce SSID si d'autres champs suivent avant la prochaine cellule : + mac_ssid[0]= 0; + canal_ssid = 0; + qualite_ssid = 0; + nom_ssid[0] = 0; + } + } + } + else ; // Dans le cas où on ne peut pas ouvrir le fichier, on abandonne le scan... + + fclose(ptr_fichier); // Fermeture du fichier + + reseaux.sequenceur.compteur = 0; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; + + } + + break; + } + + + //========================================================================================================= + // Default : + //========================================================================================================= + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Reseaux est inconnu [%i]. Il est repositionné au REPOS", reseaux.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_REPOS; + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Init des interfaces Wifi +// Reçoit : Rien +// Retourne : 0 si aucune erreur, -1 sinon +//********************************************************************************************************************************************************************************** +int8_t init_reseaux(void) +{ +uint8_t i; + + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + reseaux.compteur_connexion_ship2net = 0; + reseaux.periode_connexion_ship2net = 0; // Permet de "bloquer" toute connexion réseau (la connexion se fait alors par 'modem.c') + } + else + { + // L'init des compteurs se fait dans la phase d'init du séquenceur. Cela permet de les déclncher après la tempo d'init du séquenceur. + } + + // Variables générales : + strcpy(reseaux.ethernet.adresse_ip, ADRESSE_IP_INCONNUE); + strcpy(reseaux.wifi.sta.adresse_ip, ADRESSE_IP_INCONNUE); + strcpy(reseaux.wifi.ap.adresse_ip, ADRESSE_IP_INCONNUE); + + reseaux.wifi_new = reseaux.wifi; // Permet par la suite de comparer 'wifi_new' avec 'wifi' pour détecter les changements + + reseaux.wifi.sta.cde = -1; // Valeur d'init pour forcer une mise à jour de la commande. + reseaux.wifi.ap.cde = -1; // Valeur d'init pour forcer une mise à jour de la commande. + reseaux.wifi_new.flag_new_config = TRUE; + + for (i = 0; i < MAX_NOMBRE_PROCESS_WEBGL; i++) reseaux.webgl[i].parametres[0] = 0; // Chaine vide pour être sûr de déclencher une analyse au premier passage si la config existe + + reseaux.sequenceur.compteur = RESEAUX_TEMPO_INIT; + reseaux.sequenceur.etat = ETAT_SEQUENCEUR_RESEAUX_INIT; + + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation des interfaces Wifi\t: OK", TYPE_MESSAGE_DEBUG_OK); + return(0); +} + + + +//================================================================================================================================================================== +// Fonction : Lancement de l'utilitaire de connexion à ship2net +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t connexion_ship2net(void) +{ +int8_t retour = 0; +int32_t pid_nb; +char commande[MAX_TAILLE_COMMANDE_SHELL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Récupération du nombre de process incluant 'connexion_ship2net.php' : + pid_nb = get_pid_numbers_from_name(NOM_FICHIER_CONNEXION_SHIP2NET, ""); + + // Connexion si aucun processus équivalent n'est déjà en cours : + if (pid_nb <= 0) // Il ny a aucun processus de connexion à Ship2net en cours... + { + 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(commande, "runuser -l koriolan --session-command='nice -n 18 php %s/%s %s >%s/%s &'", REPERTOIRE_UTILS, NOM_FICHIER_CONNEXION_SHIP2NET, conversion_adresse_mac_chaine(systeme.modules_physiques[0].adresse_mac, tmp, '_'), REPERTOIRE_LOGS, NOM_FICHIER_CONNEXION_SHIP2NET_LOG); // AVEC '&' de terminaison car c'était bien l'objectif, de faire exécuter cette tâche en tâche de fond...! + // Attention : le lancement de la commande ne doit PAS se faire depuis 'root' car la gestion des mots de passe est faite sur le user 'koriolan'.... Comme l'appli est lancée via 'sudo', il faut demander explicitement l'éxécution de la commande depuis le user 'koriolan'. + + commande_shell(commande); + + if (pid_nb == 0) + { + if (systeme.compteur_check_new_download > RESEAUX_PERIODE_CHECK_DOWWNLOAD_APRES_CONNEXION) // SDIR #295 + { + systeme.compteur_check_new_download = RESEAUX_PERIODE_CHECK_DOWWNLOAD_APRES_CONNEXION; // SDIR #196 : Raccourci la tempo pour qu'une vérification soir effectuée 60s après la connexion + } + + sprintf(tmp, "La commande de connexion à Ship2net [%s] vient d'être lancée en tâche de fond car aucun processus équivalent n'a été détecté en cours d'exécution", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + else + { + sprintf(tmp, "Le retour de recherche de processus de connexion à Ship2net en cours a détecté une erreur (%i). La connexion demandée est exécutée quand même...", pid_nb); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + retour = -1; + } + } + else // Il y a déjà un processus en cours : + { + sprintf(tmp, "Au moins un processus de connexion à Ship2net a été détecté en cours d'exécution (nb = %i). La connexion demandée n'est donc pas exécutée...", pid_nb); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + retour = -1; + } + + return(retour); +} + + +//================================================================================================================================================================== +// Fonction : Lancement de l'utilitaire de lancement/arrêt d'un process WebGL +// Reçoit : le n° de process WebGL à traiter (entre 0 et MAX_NOMBRE_PROCESS_WEBGL) +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t process_webgl(uint8_t no) +{ +int8_t retour = 0; +int32_t pid_nb; +int32_t pid; +char commande[MAX_TAILLE_COMMANDE_SHELL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + // Récupération du nombre de process incluant 'shipheart_qt' : + pid_nb = get_pid_numbers_from_name(NOM_FICHIER_QT, reseaux.webgl_new[no].parametres); + + //---------------------------------------------------------------------------------- + // Il n'y a aucun processus WebGL correspondant en cours : + if (pid_nb <= 0) + { + if (reseaux.webgl_new[no].validite > 0) // La commande est ON : + { + sprintf(commande, "runuser -l koriolan --session-command='%s/%s* %s >/dev/null 2>&1 &' ", REPERTOIRE_VERSIONS_CURRENT, NOM_FICHIER_QT, reseaux.webgl_new[no].parametres); // AVEC '&' de terminaison car c'était bien l'objectif, de faire exécuter cette tâche en tâche de fond...! + // Attention : le lancement de la commande ne doit PAS se faire depuis 'root' car la gestion des mots de passe est faite sur le user 'koriolan'.... Comme l'appli est lancée via 'sudo', il faut demander explicitement l'éxécution de la commande depuis le user 'koriolan'. + + sprintf(tmp, "Démarrage du processus WebGL #%i par la commande [ %s ]", no, commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + commande_shell(commande); + } + else ; // La commande est OFF, on ne fait rien... + } + //---------------------------------------------------------------------------------- + // Il y a un ou plusieurs processus WebGL correspondand en cours : + else + { + if (reseaux.webgl_new[no].validite > 0) ; // La commande est ON, on ne fait rien... + else // La commande est OFF : + { + pid = get_pid_from_name(NOM_FICHIER_QT, reseaux.webgl_new[no].parametres); + + if (pid >= 0) + { + sprintf(commande, "sudo kill %i ", pid); + sprintf(tmp, "Fermeture du processus WebGL #%i par la commande [ %s ]", no, commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + commande_shell(commande); + } + else + { + sprintf(tmp, "Le processus WebGL #%i semble être en cours d'exécution mais la recherche de son PID est impossible. Le processus ne peut donc pas être tué...", no); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + retour = -1; + } + } + } + + // Mise à jour des valeurs courantes : + reseaux.webgl[no].validite = reseaux.webgl_new[no].validite; + strcpy(reseaux.webgl[no].parametres, reseaux.webgl_new[no].parametres); + + return(retour); +} + + +//================================================================================================================================================================== +// Fonction : Récupère la liste des SSID accessibles via 'wlan0' (en mode STA donc) +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t get_ssid_list(void) +{ +char commande[MAX_TAILLE_COMMANDE_SHELL]; + + +// sprintf(commande, "sudo iw dev wlan0 scan | grep -i 'ssid\\|signal' | grep -i -v 'ssid list' >%s/%s & ", REPERTOIRE_TMP_WIFI, NOM_FICHIER_SCAN_WIFI_STA); // Le '&' est impératif à la fin car la commande prend plusieurs secondes en général... + sprintf(commande, "sudo iwlist wlan0 scan | grep -i 'Address:\\|Channel:\\|ESSID:\\|Quality=' >%s/%s & ", REPERTOIRE_TMP_WIFI, NOM_FICHIER_SCAN_WIFI_STA); // Le '&' est impératif à la fin car la commande prend plusieurs secondes en général... +//printf("Commande = [%s] \n", commande); + commande_shell(commande); + +/* +sudo iw dev wlan0 scan | grep -i 'ssid\|signal' | grep -i -v 'ssid list' + signal: -55.00 dBm + SSID: Koriolan + signal: -35.00 dBm + SSID: DIRECT-87-HP M277 LaserJet + signal: -78.00 dBm + SSID: ShipHeartkbane + signal: -62.00 dBm + SSID: Koriolan +*/ + +/* +sudo iwlist wlan0 scan | grep -i 'Address:\|Channel:\|ESSID:\|Quality=' + Cell 01 - Address: 94:A6:7E:B0:BE:C7 + Channel:44 + Quality=43/70 Signal level=-67 dBm + ESSID:"Koriolan" + Cell 02 - Address: 94:A6:7E:B0:BE:C6 + Channel:2 + Quality=59/70 Signal level=-51 dBm + ESSID:"Koriolan" + Cell 03 - Address: 46:1C:A8:3D:63:87 + Channel:6 + Quality=70/70 Signal level=-37 dBm + ESSID:"DIRECT-87-HP M277 LaserJet" + Cell 04 - Address: F8:F0:05:7D:22:0F + Channel:7 + Quality=34/70 Signal level=-76 dBm + ESSID:"ShipHeartkbane" +*/ + return (0); +} + + + +//================================================================================================================================================================== +// Fonction : Récupère l'adresse IP en cours +// Cette fonction a été trouvée ici : https://subscription.packtpub.com/book/cloud-and-networking/9781789349863/1/ch01lvl1sec22/listing-network-adapters-from-c +// dans la partie Unix. +// Par défaut, elle retourne quelque chose comme : +// lo IPv4 127.0.0.1 +// eth0 IPv4 192.168.1.31 +// wlan0 IPv4 192.168.1.155 +// uap0 IPv4 192.168.4.1 +// lo IPv6 ::1 +// eth0 IPv6 fdee:db1c:59f9:4580:4c1d:fd37:e2de:a1ec +// eth0 IPv6 fe80::7a6a:c1c1:b0f1:3b13%eth0 +// wlan0 IPv6 fdee:db1c:59f9:4580:b9d:6312:d3e8:3c09 +// wlan0 IPv6 fe80::46f3:ad7e:44b2:2802%wlan0 +// uap0 IPv6 fe80::e65f:1ff:feed:e67c%uap0 +// La version du site a été adaptée pour ne prendre en compte que les interfaces en IPV4 puis uniquement les interfaces appelées 'eth0', 'wlan0' et 'uap0' +// Mais il est facile de l'étendre à d'autres interfaces si besoin voire en IPV6. ATTENTION dans ce cas, certains tailles (comme 'family_size' seront à revoir...) +// A noter que les exemples trouvées le plus fréquemment, à base de 'socket()' et 'ioctl()', ne fonctionnent pas bien !! +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t get_ip_address(void) +{ + struct ifaddrs *addresses; + struct ifaddrs *address; + char chaine_ip[TAILLE_ADRESSE_IP]; + const uint32_t family_size = sizeof(struct sockaddr_in); + char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Recherche de la liste de toutes les interfaces disponibles : + if (getifaddrs(&addresses) == -1) + { + sprintf(tmp, "L'appel de la fonction 'getifaddrs()' a échoué. La recherche d'adresse IP ne peut pas se poursuivre..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + return -1; + } + + // Valeur par défaut, dans le cas où la liaison serait perdue (après la recherche des interfaces, car si celle-ci a échoué, on garde les anciennes valeurs) : + strcpy(reseaux.ethernet.adresse_ip, ADRESSE_IP_INCONNUE); + strcpy(reseaux.wifi.sta.adresse_ip, ADRESSE_IP_INCONNUE); + strcpy(reseaux.wifi.ap.adresse_ip, ADRESSE_IP_INCONNUE); + + // Boucle sur toutes les interfaces reçues : + address = addresses; + while(address) + { + int family = address->ifa_addr->sa_family; +// if (family == AF_INET || family == AF_INET6) // AF_INET correspond à IPV4 et AF_INET6 correspond à IPV6 + if (family == AF_INET) // On ne traite que les IPV4 pour le moment... + { + getnameinfo(address->ifa_addr, family_size, chaine_ip, TAILLE_ADRESSE_IP_V4, 0, 0, NI_NUMERICHOST); // Permet de récupérer la + + if (strstr(address->ifa_name, "eth0") != NULL) + { + if ((address->ifa_flags & IFF_RUNNING) != 0) strcpy(reseaux.ethernet.adresse_ip, chaine_ip); // Le test du bit 'IFF_RUNNING' est nécessaire car le port peut être UP (visible via IFF_UP équivalent de 'ifconfig') mais ne pas être physiquement connecté... +#ifdef __DEBUG_RESEAUX +sprintf(tmp, "Adresse IP sur le réseau Ethernet du bord = %s [IFF_RUNNING=%s]", reseaux.ethernet.adresse_ip, ((address->ifa_flags & IFF_RUNNING) == 0 ? "Non" : "Oui") ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (strstr(address->ifa_name, "wlan0") != NULL) + { + if ((address->ifa_flags & IFF_RUNNING) != 0) strcpy(reseaux.wifi.sta.adresse_ip, chaine_ip); // Le test du bit 'IFF_RUNNING' est nécessaire car le port peut être UP (visible via IFF_UP équivalent de 'ifconfig') mais ne pas être physiquement connecté... + +#ifdef __DEBUG_RESEAUX +sprintf(tmp, "Adresse IP sur le réseau Wifi du bord (connecté en mode STA) = %s [IFF_RUNNING=%s]", reseaux.wifi.sta.adresse_ip, ((address->ifa_flags & IFF_RUNNING) == 0 ? "Non" : "Oui") ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (strstr(address->ifa_name, "uap0") != NULL) + { + if ((address->ifa_flags & IFF_RUNNING) != 0) strcpy(reseaux.wifi.ap.adresse_ip, chaine_ip); // Le test du bit 'IFF_RUNNING' est nécessaire car le port peut être UP (visible via IFF_UP équivalent de 'ifconfig') mais ne pas être physiquement connecté... +#ifdef __DEBUG_RESEAUX +sprintf(tmp, "Adresse IP de ShipBase sur le réseau Wifi ShipHeart (mode AP) = %s [IFF_RUNNING=%s]", reseaux.wifi.ap.adresse_ip, ((address->ifa_flags & IFF_RUNNING) == 0 ? "Non" : "Oui") ); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + + +/* + printf("%s\t", address->ifa_name); + printf("%s\t", family == AF_INET ? "IPv4" : "IPv6"); + + + getnameinfo(address->ifa_addr, family_size, chaine_ip, TAILLE_ADRESSE_IP_V4, 0, 0, NI_NUMERICHOST); + printf("\t%s\n", chaine_ip);*/ + + } + address = address->ifa_next; + } + + freeifaddrs(addresses); + + + + return 0; +} \ No newline at end of file diff --git a/sources/reseaux.h b/sources/reseaux.h new file mode 100644 index 0000000..0b2860a --- /dev/null +++ b/sources/reseaux.h @@ -0,0 +1,72 @@ +//********************************************************************************************************************************************************************************** +// Fichier RESEAUX.H +// +// Contient tous les define liés au Wifi +//**********************************************************************************************************************************************************************************/ + + // Maximums : +#define MAX_RESEAUX_WIFI_STA_TAILLE_SSID 100 +#define MAX_RESEAUX_WIFI_STA_TAILLE_BSSID 20 // Du type "94:A6:7E:B0:BE:C7" +#define MAX_RESEAUX_WIFI_STA_TAILLE_PSK 100 +#define MAX_RESEAUX_WIFI_AP_TAILLE_SSID 32 +#define MAX_RESEAUX_WIFI_AP_TAILLE_PWD 63 +#define MAX_LONGUEUR_LIGNE_FICHIER_CONFIG_RESEAUX 150 // 63+1 caractères pour PWD + entête de paramètre (15 caractères pour celui-ci) +#define MAX_LONGUEUR_STA_SCAN_LIST 2000 // Une entrée comprend le nom du SSID (32 max), le canal (2 max) et la qualité de réception (3max) soit un peu moins de 40 caractères/SSID +#define MAX_NOMBRE_PROCESS_WEBGL 3 // A gérer conjointement avec Qt qui gère le paramétrage +#define MAX_LONGUEUR_PARAMETRES_WEBGL 200 // A priori, une chaine de la forme '-platform webgl:port=8081:wsserverport=8082' + + // Timings : +#define RESEAUX_TEMPO_INIT 50 // En x 100ms +#define RESEAUX_PERIODE_SURVEILLANCE 10 // En x 100ms +#define RESEAUX_PERIODE_CHECK_ADRESSE_IP_INIT 5 // En secondes, tempo avant la première recherche d'adresse IP +#define RESEAUX_PERIODE_CONNEXION_SHIP2NET_INIT 90 // En secondes +#define RESEAUX_PERIODE_SCAN_WIFI_STA_INIT 30 // En secondes +#define RESEAUX_PERIODE_CHECK_DOWWNLOAD_APRES_CONNEXION 60 // En secondes +#define RESEAUX_TIME_OUT_LECTURE_FICHIER_SCAN_STA 20 // En TEMPO_ATTENTE_BOUCLE_SCAN_WIFI_STA (a priori, 1s) +#define RESEAUX_PERIODE_CHECK_WEBGL_INIT 5 // En secondes +#define RESEAUX_PERIODE_CHECK_WEBGL 1 // En secondes + +#define TEMPO_ATTENTE_FIN_MAJ_FICHIER_WPA_SUPPLICANT 20 // En x 100ms. Mesuré à environ 400ms +#define TEMPO_ATTENTE_FIN_DHCPCD_RESTART 50 // En x 100ms. Les commandes en console durent 1.5-2s à l'oeil. +#define TEMPO_ATTENTE_FIN_DAMEON_SERVICES_RESTART 50 // En x 100ms. Les commandes en console durent 1s à l'oeil. +#define TEMPO_ATTENTE_FIN_COMMANDE_WIFI_STA_UP 80 // En x 100ms. Les commandes en console sont quasi instantanées +#define TEMPO_ATTENTE_FIN_COMMANDE_WIFI_STA_DOWN 80 // En x 100ms. Les commandes en console durent 700-800ms à l'oeil. + +#define TEMPO_ATTENTE_FIN_MAJ_FICHIER_HOSTAPD 20 // En x 100ms. Mesuré à environ 400ms +#define TEMPO_ATTENTE_FIN_HOSTAPD_RESTART 50 // En x 100ms. Les commandes en console durent 1.5-2s à l'oeil. +#define TEMPO_ATTENTE_FIN_COMMANDE_WIFI_AP_UP 80 // En x 100ms. Les commandes en console sont quasi instantanées +#define TEMPO_ATTENTE_FIN_COMMANDE_WIFI_AP_DOWN 80 // En x 100ms. Les commandes en console durent 700-800ms à l'oeil. + +#define TEMPO_ATTENTE_BOUCLE_SCAN_WIFI_STA 10 // En x 100ms. Cette tempo est utilisée pour vérifier régulièrement si le fichier de sortie du scan est toujours vide +#define TEMPO_ATTENTE_FIN_SCAN_WIFI_STA 3 // En x 100ms. Cette tempo permet correspond à l'attente entre la détection du fichier de sortie non vide et le traitement. Elle est fait pour le cas où la détection de fichier non vide se fait au milieu du remplissage du fichier (permet d'attendre la fin du remplissage dans ce cas) + + + // Modes : +#define RESEAUX_WIFI_MODE_STA 0 +#define RESEAUX_WIFI_MODE_AP 1 +#define RESEAUX_WIFI_MODE_STA_ET_AP 2 + + + // Etat séquenceur Wifi : +#define ETAT_SEQUENCEUR_RESEAUX_INIT 0 +#define ETAT_SEQUENCEUR_RESEAUX_REPOS 1 + +#define ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_STA 10 +#define ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_WPA_SUPPLICANT 11 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DHCPCD_RESTART 12 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_DAMEON_SERVICES_RESTART 13 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_UP 14 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_STA_DOWN 15 +#define ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI_STA 16 + +#define ETAT_SEQUENCEUR_RESEAUX_LANCEMENT_MISE_A_JOUR_WIFI_AP 20 +#define ETAT_SEQUENCEUR_RESEAUX_MAJ_FICHIER_HOSTPAD 21 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_HOSTAPD_RESTART 22 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_UP 23 +#define ETAT_SEQUENCEUR_RESEAUX_COMMANDE_WIFI_AP_DOWN 24 + +#define ETAT_SEQUENCEUR_RESEAUX_FIN_MISE_A_JOUR_WIFI 30 + +#define ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_LANCEMENT 40 +#define ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_ATTENTE_FIN 41 +#define ETAT_SEQUENCEUR_RESEAUX_SCAN_WIFI_STA_TRAITEMENT 42 diff --git a/sources/reservoirs.c b/sources/reservoirs.c new file mode 100644 index 0000000..af77e8a --- /dev/null +++ b/sources/reservoirs.c @@ -0,0 +1,258 @@ +//********************************************************************************************************************************************************************************** +// Fichier RESERVOIRS.C +// +// Contient toutes les fonctions liées aux réservoirs de ShipHeart +//**********************************************************************************************************************************************************************************/ + +// #define __DEBUG_RESERVOIRS + + +// Includes : +#include "main.h" + +// Prototypes : +int8_t init_reservoirs(void); +void gestion_reservoirs(void); +int16_t reservoir_cle_primaire(uint16_t cle_primaire); +int16_t variable_reservoir_cle_primaire(uint16_t cle_primaire); + + +// Variables globales : +struct Struct_reservoirs reservoirs; + + +/********************************************************************************************************************************************************************************** + Fonction : Initialise les différents paramètres des reservoirs au démarrage de l'application + Reçoit : Rien + Retour : -1 si erreur, 0 sinon. +**********************************************************************************************************************************************************************************/ +int8_t init_reservoirs(void) +{ + //------------------------------------------------------------------------------------------------------ + // Init des variables générales : + + reservoirs.sequenceur.etat = ETAT_SEQUENCEUR_RESERVOIRS_INIT; + reservoirs.sequenceur.compteur = RESERVOIRS_TEMPO_INIT; + + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation des reservoirs\t: OK", TYPE_MESSAGE_DEBUG_OK); + return(0); + +} + + +/********************************************************************************************************************************************************************************** + Fonction : Fonction de plus haut niveau de gestion reservoirs + Reçoit : Rien + Retour : Rien +**********************************************************************************************************************************************************************************/ +void gestion_reservoirs(void) +{ +uint16_t i, j; +uint16_t val_u16; +float val; +float niv; +bool flag_erreur; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + if (!systeme.flag_cm3p_ready) return; // Evite que le traitement soit fait au milieu d'une reconfiguration... + + switch(reservoirs.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESERVOIRS_INIT : + { + if ((systeme.flag_cm3p_ready) && // Permet de s'assurer que le cycle d'init système est terminé. En particulier, on est sûr que 'init_bdd()' puis 'annuaire_k_bus()' ont été traités. + (reservoirs.sequenceur.compteur == 0)) + { + +affichage_debug(__FILE__, __LINE__, "------------------RESERVOIRS INIT", TYPE_MESSAGE_DEBUG_WARNING); + + reservoirs.sequenceur.etat = ETAT_SEQUENCEUR_RESERVOIRS_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_RESERVOIRS_REPOS : + { + if (reservoirs.sequenceur.compteur == 0) + { + reservoirs.sequenceur.compteur = RESERVOIRS_PERIODE_MESURES; + + for (i=0; i 0) + { + reservoirs.reservoirs[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD + } + + reservoirs.reservoirs[i].valeur_brute = val; + // SDIR #288 : Fin + +#ifdef __DEBUG_RESERVOIRS +sprintf(tmp, "Réservoir #%i : valeur brute de l'entrée = %.2f", (i+1), val); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + break; + } + + //-------------------------------------------------------------------------- + default : + { + sprintf(tmp, "Le 'Type_entrées' du réservoir #%i est invalide [%i]. Le réservoir est dévalidé", (i+1), reservoirs.reservoirs[i].type_entrees); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + reservoirs.reservoirs[i].flag_validite = FALSE; + break; + } + } + + // Traitement : + if (flag_erreur == FALSE) + { + // Calcul de la nouvelle moyenne à partir de l'ancienne et de la valeur lue : + niv = calcul_niveau(reservoirs.reservoirs[i].liste_valeurs_niveaux, MAX_NOMBRE_VALEURS_NIVEAUX_RESERVOIRS, val); + if (niv >= 0) // S'il y a une erreur lors du calcul du niveau, on ne traite pas le résultat... + { + // Calcul de la nouvelle moyenne de valeur courante à partir de l'ancienne et de la valeur lue : + reservoirs.reservoirs[i].variables[0].valeur_courante = (((reservoirs.reservoirs[i].variables[0].valeur_courante * (reservoirs.reservoirs[i].niveau_coeff_filtrage - 1))) + niv) / reservoirs.reservoirs[i].niveau_coeff_filtrage; + if (reservoirs.reservoirs[i].variables[0].valeur_courante > 100) reservoirs.reservoirs[i].variables[0].valeur_courante = 100; + + // Mise à jour du cumul : + reservoirs.reservoirs[i].variables[0].cumul += reservoirs.reservoirs[i].variables[0].valeur_courante; + reservoirs.reservoirs[i].cumuls_nb_val ++; // MàJ du nombre d'échantillons dans le cumul + + // Traitement si la valeur a suffisemment évolué : +// if (fabs(reservoirs.reservoirs[i].variables[0].valeur_bdd - reservoirs.reservoirs[i].variables[0].valeur_courante) >= reservoirs.variables[0].marge) + if (fabs(reservoirs.reservoirs[i].variables[0].valeur_bdd - reservoirs.reservoirs[i].variables[0].valeur_courante) > 0) + { + reservoirs.reservoirs[i].flag_dde_maj_bdd = TRUE; // Flag pour mise à jour dans la BDD +#ifdef __DEBUG_RESERVOIRS +sprintf(tmp, "Réservoir #%i : Niveau = %.2f% (coeff filtrage niveau = %i)", i, reservoirs.reservoirs[i].variables[0].valeur_courante, reservoirs.reservoirs[i].niveau_coeff_filtrage); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + } + else + { + sprintf(tmp, "Erreur de calcul de niveau sur réservoir #%i avec Valeur = %.3f. La valeur lue n'est pas exploitée.\n", i, val); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + + // Mise à jour des valeurs BDD des variables : + if (reservoirs.reservoirs[i].flag_dde_maj_bdd) + { + for (j = 0; j < MAX_NOMBRE_VARIABLES_RESERVOIRS; j ++) reservoirs.reservoirs[i].variables[j].valeur_bdd = reservoirs.reservoirs[i].variables[j].valeur_courante; + } + } + } + + } + break; + } + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Réservoirs est inconnu [%i]. Il est repositionné au REPOS", reservoirs.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + reservoirs.sequenceur.etat = ETAT_SEQUENCEUR_RESERVOIRS_REPOS; + break; + } + } +} + + + +//********************************************************************************************************************************************************************************** +// Fonction : Recherche le réservoir dont la Clé_primaire est reçue en paramètre +// Reçoit : La Cle_primaire du réservoir à rechercher +// Retourne : le numéro de réservoir correspondant (ou -1 si aucun circuit valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_RESERVOIRS - 1. +//********************************************************************************************************************************************************************************** +int16_t reservoir_cle_primaire(uint16_t cle_primaire) +{ +uint16_t i; + + for (i = 0; i < reservoirs.nb; i ++) + { + if ((reservoirs.reservoirs[i].cle_primaire == cle_primaire) && (reservoirs.reservoirs[i].flag_validite)) return(i); + } + return (-1); // Si on est arrivé au bout de la boucle, on renvoi le code de 'non trouvé'. +} + + +//********************************************************************************************************************************************************************************** +// Fonction VARIABLE_RESERVOIR_CLE_PRIMAIRE +// Reçoit en paramètre : +// - La Cle_primaire de la variable à rechercher +// Retourne : +// - le numéro de variable correspondant (ou -1 si aucune variable valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_VARIABLES_RESERVOIRS-1. +//********************************************************************************************************************************************************************************** +int16_t variable_reservoir_cle_primaire(uint16_t cle_primaire) +{ +uint16_t i; + + for (i=0; i 0) + { + for (i=0; i 0) + { + for (i=0; i variable locale options : + if (tcgetattr(config_serial0.filestream, &options) < 0) + { + sprintf(tmp, "Initialisation de l'UART0 impossible : erreur lors de lecture des paramètres du port..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Mise à jour des paramètres non configurables dans la BDD : + + // Configuration du registre 'cflag' : + // options.c_cflag = 0; + options.c_cflag |= CLOCAL; // Ignore modem status lines + options.c_cflag |= CREAD; // Enable receiver + options.c_cflag &= ~CRTSCTS; // no hardware flowcontrol + // options.c_cflag |= CMSPAR; // force Even parity to SPACE + + // Configuration du registre 'lflag' : + // options.c_lflag = 0; + options.c_lflag &= ~ECHO; + options.c_lflag &= ~ECHOE; + options.c_lflag &= ~ECHONL; + options.c_lflag &= ~ICANON; + options.c_lflag &= ~ISIG; + options.c_lflag &= ~IEXTEN; + + // Configuration du registre 'iflag' : + // options.c_iflag = 0; + options.c_iflag &= ~IGNCR; // preserve carriage return + options.c_iflag &= ~INLCR; // Don't map NL to CR on input + options.c_iflag &= ~ICRNL; // Don't map CR to NL on input + options.c_iflag &= ~IUCLC; // Don't map upper-case to lower-case on input + options.c_iflag &= ~IUCLC; // Don't convert uppecase to lowercase on input + options.c_iflag &= ~IMAXBEL; // Don't beep on reception as input buffer is full + options.c_iflag &= ~IXANY; // Don't enable any character to restart outputno SW flowcontrol + options.c_iflag &= ~IXON; // Don't enable start output control + options.c_iflag &= ~IXOFF; // Don't enable stop output control (attention : doc pas très claire entre Xon et Xoff) + options.c_iflag |= IGNBRK; // ignore breaks + options.c_iflag &= ~ISTRIP; // Don't clear MSB (b8) on input + options.c_iflag &= ~IGNPAR; // Don't ignore (check) parity errors + options.c_iflag |= INPCK; // test parity + options.c_iflag &= ~PARMRK; // Don't use verbose parity error (Signaler les erreurs de parité par une séquence 255-0-car. ) + + // Configuration du registre 'oflag' : + options.c_oflag &= ~OPOST; // Don't use post-process output (?) + + // Configuration des caractères de contrôle 'c_cc' : + // options.c_cc[VEOL] = 0; + // options.c_cc[VEOL2] = 0; + // options.c_cc[VEOF] = 0x04; + + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation de la vitesse de communication : + switch(config_serial0.vitesse) + { + case 1200: { speed = B1200; break; } + case 2400: { speed = B2400; break; } + case 4800: { speed = B4800; break; } + case 9600: { speed = B9600; break; } + case 19200: { speed = B19200; break; } + case 38400: { speed = B38400; break; } + case 57600: { speed = B57600; break; } + case 115200: { speed = B115200; break; } + case 921600 : { speed = B921600; break; } + default : + { + sprintf(tmp, "\tInitialisation de l'UART0 impossible : vitesse de communication non valide (%i b/s) ...", config_serial0.vitesse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + // Mise à jour de la valeur dans la structure : + cfsetospeed(&options, speed); + cfsetispeed(&options, speed); + + // Mise à jour du message de configuration : + sprintf(chaine_temp, "%i b/s", config_serial0.vitesse); + strcat(tmp, chaine_temp); + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation du nombre de bits de données : + options.c_cflag &= ~CSIZE; // RAZ des bits concernés + + switch(config_serial0.longueur) + { + case 8: { options.c_cflag |= CS8; break; } + case 7: { options.c_cflag |= CS7; break; } + case 6: { options.c_cflag |= CS6; break; } + case 5: { options.c_cflag |= CS5; break; } + default : + { + sprintf(tmp, "Initialisation de l'UART0 impossible : nombre de bits de données non valide (%i bits) ...", config_serial0.longueur); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + // Mise à jour du message de configuration : + sprintf(chaine_temp, ", %i bits", config_serial0.longueur); + strcat(tmp, chaine_temp); + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation de la parité : + + // ATTENTION : les tests de parité ne semblent pas fonctionner !!! Leur configuration ne semble avoir aucun effet (pas d'erreur générée). + + switch(config_serial0.parite) + { + case 0: // Pas de parité : + { + options.c_cflag &= ~PARENB; /* Disable parity */ + strcat(tmp, ", pas de parité"); + break; + } + case 1: // Parité impaire : + { + options.c_cflag |= PARENB; // Parité validée + options.c_cflag |= PARODD; /* Odd parity */ + strcat(tmp, ", parité impaire"); + break; + } + case 2: // Parité paire : + { + options.c_cflag |= PARENB; // Parité validée + options.c_cflag &= ~PARODD; /* Even parity */ + strcat(tmp, ", parité paire"); + break; + } + default : + { + sprintf(tmp, "\tInitialisation de l'UART0 impossible : parité non valide (%i) ...", config_serial0.parite); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation du nombre de bits de stop : + switch(config_serial0.stops) + { + case 1: + { + options.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ + strcat(tmp, ", un bit de stop"); + break; + } + case 2: + { + options.c_cflag |= CSTOPB; /* activate double stop bits */ + strcat(tmp, ", deux bits de stop"); + break; + } + default : + { + sprintf(tmp, "Initialisation de l'UART0 impossible : nombre de bits de stop non valide (%i) ...", config_serial0.stops); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Mise à jour des nouveaux paramètres : + tcsetattr(config_serial0.filestream, TCSANOW, &options); // Mise à jour des nouvelles valeurs de configuration (depuis 'options') + delai_dixieme_sec(2); // Délai nécessaire pour que le flush fonctionne (testé avec délai = 1 : Ok) + tcflush(config_serial0.filestream, TCIOFLUSH); // Vidage du port (en Inputs et en Ouptuts) + + //-------------------------------------------------------------------------------------------------------------------------------- + // Finalisation du message de débug : + strcat(tmp, ")\t: OK"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + + return(0); + } + else // Cas où l'ouverture du port ne s'est pas passée correctement : + { + sprintf(tmp, "Initialisation de l'UART0 impossible : erreur lors de l'ouverture du port..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); + } + } + else // L'UART n'est pas validé : + { + sprintf(tmp, "La configuration en cours ne prévoit pas l'utilisation de l'UART0 (la configuration de celui-ci n'a pas été affectée par l'application)..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + return(0); // On ne considère pas cela comme une erreur et l'init peut continuer. + } +} + + +/********************************************************************************************************************************************************************************** + Fonction init_serial1 + + Fonction d'initialisation de la liaison 'serial1'. +**********************************************************************************************************************************************************************************/ +int init_serial1(void) +{ +char chaine_temp[MAX_TAILLE_CHAINE_TEMP_UARTS]; +struct termios options; +speed_t speed; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Paramètres de configuration (en dur car inutile de les importer de la BDD) : + config_serial1.validite = 1; + config_serial1.vitesse = 115200; // 1200 à 115200 + config_serial1.longueur = 8; // 7 ou 8 + config_serial1.parite = 0; // 0=No, 1=Odd et 2=Even + config_serial1.stops = 1; // 1 ou 2 + + + if (config_serial1.validite == 1) // Cas où la config en cours prévoit l'utilisation de l'UART1 : + { + + config_serial1.filestream = -1; + sprintf(tmp, "Initialisation de l'UART1 ("); + + config_serial1.filestream = open("/dev/serial1", O_RDWR | O_NOCTTY | O_NDELAY); //Open in non blocking read/write mode + // O_NDELAY / O_NONBLOCK (same function) - Enables nonblocking mode. When set read requests on the file can return immediately with a failure status + // if there is no input immediately available (instead of blocking). Likewise, write requests can also return + // immediately with a failure status if the output can't be written immediately. + // O_NOCTTY - When set and path identifies a terminal device, open() shall not cause the terminal device to become the controlling terminal for the process. + + if (config_serial1.filestream != -1) + { + + //The flags (defined in /usr/include/termios.h - see http://pubs.opengroup.org/onlinepubs/007908799/xsh/termios.h.html): + // Baud rate:- B1200, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400, B460800, B500000, B576000, B921600, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, B3500000, B4000000 + // CSIZE:- CS5, CS6, CS7, CS8 + // CLOCAL - Ignore modem status lines + // CREAD - Enable receiver + // IGNPAR = Ignore characters with parity errors + // ICRNL - Map CR to NL on input (Use for ASCII comms where you want to auto correct end of line characters - don't use for binary comms!) + // PARENB - Parity enable + // PARODD - Odd parity (else even) + + //-------------------------------------------------------------------------------------------------------------------------------- + // Lecture de la configuration en cours => variable locale options : + if (tcgetattr(config_serial1.filestream, &options) < 0) + { + sprintf(tmp, "Initialisation de l'UART1 impossible : erreur lors de lecture des paramètres du port..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Mise à jour des paramètres non configurables dans la BDD : + + // Configuration du registre 'cflag' : + // options.c_cflag = 0; + options.c_cflag |= CLOCAL; // Ignore modem status lines + options.c_cflag |= CREAD; // Enable receiver + options.c_cflag &= ~CRTSCTS; // no hardware flowcontrol + // options.c_cflag |= CMSPAR; // force Even parity to SPACE + + // Configuration du registre 'lflag' : + // options.c_lflag = 0; + options.c_lflag &= ~ECHO; + options.c_lflag &= ~ECHOE; + options.c_lflag &= ~ECHONL; + options.c_lflag &= ~ICANON; + options.c_lflag &= ~ISIG; + options.c_lflag &= ~IEXTEN; + + // Configuration du registre 'iflag' : + // options.c_iflag = 0; + options.c_iflag &= ~IGNCR; // preserve carriage return + options.c_iflag &= ~INLCR; // Don't map NL to CR on input + options.c_iflag &= ~ICRNL; // Don't map CR to NL on input + options.c_iflag &= ~IUCLC; // Don't map upper-case to lower-case on input + options.c_iflag &= ~IUCLC; // Don't convert uppecase to lowercase on input + options.c_iflag &= ~IMAXBEL; // Don't beep on reception as input buffer is full + options.c_iflag &= ~IXANY; // Don't enable any character to restart outputno SW flowcontrol + options.c_iflag &= ~IXON; // Don't enable start output control + options.c_iflag &= ~IXOFF; // Don't enable stop output control (attention : doc pas très claire entre Xon et Xoff) + options.c_iflag |= IGNBRK; // ignore breaks + options.c_iflag &= ~ISTRIP; // Don't clear MSB (b8) on input + options.c_iflag &= ~IGNPAR; // Don't ignore (check) parity errors + options.c_iflag |= INPCK; // test parity + options.c_iflag &= ~PARMRK; // Don't use verbose parity error (Signaler les erreurs de parité par une séquence 255-0-car. ) + + // Configuration du registre 'oflag' : + options.c_oflag &= ~OPOST; // Don't use post-process output (?) + + // Configuration des caractères de contrôle 'c_cc' : + // options.c_cc[VEOL] = 0; + // options.c_cc[VEOL2] = 0; + // options.c_cc[VEOF] = 0x04; + + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation de la vitesse de communication : + switch(config_serial1.vitesse) + { + case 1200: { speed = B1200; break; } + case 2400: { speed = B2400; break; } + case 4800: { speed = B4800; break; } + case 9600: { speed = B9600; break; } + case 19200: { speed = B19200; break; } + case 38400: { speed = B38400; break; } + case 57600: { speed = B57600; break; } + case 115200: { speed = B115200; break; } + case 921600 : { speed = B921600; break; } + default : + { + sprintf(tmp, "\tInitialisation de l'UART1 impossible : vitesse de communication non valide (%i b/s) ...", config_serial1.vitesse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + // Mise à jour de la valeur dans la structure : + cfsetospeed(&options, speed); + cfsetispeed(&options, speed); + + // Mise à jour du message de configuration : + sprintf(chaine_temp, "%i b/s", config_serial1.vitesse); + strcat(tmp, chaine_temp); + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation du nombre de bits de données : + options.c_cflag &= ~CSIZE; // RAZ des bits concernés + + switch(config_serial1.longueur) + { + case 8: { options.c_cflag |= CS8; break; } + case 7: { options.c_cflag |= CS7; break; } + case 6: { options.c_cflag |= CS6; break; } + case 5: { options.c_cflag |= CS5; break; } + default : + { + sprintf(tmp, "Initialisation de l'UART1 impossible : nombre de bits de données non valide (%i bits) ...", config_serial1.longueur); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + // Mise à jour du message de configuration : + sprintf(chaine_temp, ", %i bits", config_serial1.longueur); + strcat(tmp, chaine_temp); + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation de la parité : + + // ATTENTION : les tests de parité ne semblent pas fonctionner !!! Leur configuration ne semble avoir aucun effet (pas d'erreur générée). + + switch(config_serial1.parite) + { + case 0: // Pas de parité : + { + options.c_cflag &= ~PARENB; /* Disable parity */ + strcat(tmp, ", pas de parité"); + break; + } + case 1: // Parité impaire : + { + options.c_cflag |= PARENB; // Parité validée + options.c_cflag |= PARODD; /* Odd parity */ + strcat(tmp, ", parité impaire"); + break; + } + case 2: // Parité paire : + { + options.c_cflag |= PARENB; // Parité validée + options.c_cflag &= ~PARODD; /* Even parity */ + strcat(tmp, ", parité paire"); + break; + } + default : + { + sprintf(tmp, "\tInitialisation de l'UART1 impossible : parité non valide (%i) ...", config_serial1.parite); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Initialisation du nombre de bits de stop : + switch(config_serial1.stops) + { + case 1: + { + options.c_cflag &= ~CSTOPB; /* only need 1 stop bit */ + strcat(tmp, ", un bit de stop"); + break; + } + case 2: + { + options.c_cflag |= CSTOPB; /* activate double stop bits */ + strcat(tmp, ", deux bits de stop"); + break; + } + default : + { + sprintf(tmp, "Initialisation de l'UART1 impossible : nombre de bits de stop non valide (%i) ...", config_serial1.stops); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } + + //-------------------------------------------------------------------------------------------------------------------------------- + // Mise à jour des nouveaux paramètres : + tcsetattr(config_serial1.filestream, TCSANOW, &options); // Mise à jour des nouvelles valeurs de configuration (depuis 'options') + delai_dixieme_sec(2); // Délai nécessaire pour que le flush fonctionne (testé avec délai = 1 : Ok) + tcflush(config_serial1.filestream, TCIOFLUSH); // Vidage du port (en Inputs et en Ouptuts) + + //-------------------------------------------------------------------------------------------------------------------------------- + // Finalisation du message de débug : + strcat(tmp, ")\t: OK"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + + return(0); + } + else // Cas où l'ouverture du port ne s'est pas passée correctement : + { + sprintf(tmp, "Initialisation de l'UART1 impossible : erreur lors de l'ouverture du port..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return(-1); + } + } + else // L'UART n'est pas validé : + { + sprintf(tmp, "La configuration en cours ne prévoit pas l'utilisation de l'UART1 (la configuration de celui-ci n'a pas été affectée par l'application)..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + return(0); // On ne considère pas cela comme une erreur et l'init peut continuer. + } +} diff --git a/sources/serial.h b/sources/serial.h new file mode 100644 index 0000000..2cb3c62 --- /dev/null +++ b/sources/serial.h @@ -0,0 +1 @@ +/********************************************************************************************************************************************************************************** Fichier SERIAL.H Contient tous les define liés aux liaisons série **********************************************************************************************************************************************************************************/ // Maximums : #define MAX_TAILLE_RX_BUFFER_UART 10000 #define MAX_TAILLE_TMP_RX_BUFFER_UART 255 // NB : doit être < MAX_TAILLE_RX_BUFFER_UART #define MAX_TAILLE_CHAINE_TEMP_UARTS 200 // Codes ASCII : #define ASCII_NULL 0x00 #define ASCII_SOH 0x01 #define ASCII_STX 0x02 #define ASCII_ETX 0x03 #define ASCII_EOT 0x04 #define ASCII_ENQ 0x05 #define ASCII_ACK 0x06 #define ASCII_LF 0x0A #define ASCII_FF 0x0C #define ASCII_CR 0x0D #define ASCII_DLE 0x10 #define ASCII_NACK 0x15 #define ASCII_ETB 0x17 #define ASCII_ESC 0x1B #define ASCII_SP 0x20 \ No newline at end of file diff --git a/sources/sorties.c b/sources/sorties.c new file mode 100644 index 0000000..3941b47 --- /dev/null +++ b/sources/sorties.c @@ -0,0 +1,372 @@ +//********************************************************************************************************************************************************************************** +// Fichier SORTIES.C +// +// Contient toutes les fonctions liées à l'écriture sur les sorties, quel que soit leur type +//**********************************************************************************************************************************************************************************/ + +// Includes : +#include "main.h" + +// Prototypes : +void ecriture_sortie(uint8_t type, uint8_t module, uint8_t no, uint8_t valeur); +void ecriture_sortie_gpio(int16_t no, int8_t valeur, int8_t logique); +int8_t init_sorties(void); +int8_t init_sorties_gpio(void); +int16_t sortie_no(uint8_t type, uint8_t module, uint8_t no); + + +// Variables globales : +struct Struct_sorties sorties; +struct gpiod_chip *gpiod_chip0_sorties; // Nécessaire pour CM5 +struct gpiod_line_request *gpiod_sorties_request; // Nécessaire pour CM5 + + +/********************************************************************************************************************************************************************************** + Fonction ECRITURE_SORTIE + + Fonction utilisée pour mettre à jour l'état d'une sortie, quel que soit sont type. + Reçoit en paramètre : le Type de sortie, le module comportant la sortie, le n° sur le module et la valeur à appliquer +**********************************************************************************************************************************************************************************/ +void ecriture_sortie(uint8_t type, uint8_t module, uint8_t no, uint8_t valeur) +{ +int x; + + x = sortie_no(type, module, no); + + if (x != -1) + { + switch(type) + { + case TYPE_SORTIE_GPIO : + { + sorties.gpio.sorties[x].valeur = valeur; + + ecriture_sortie_gpio(sorties.gpio.sorties[x].no, sorties.gpio.sorties[x].valeur, sorties.gpio.sorties[x].logique); + + break; + } + } + } +} + + +/********************************************************************************************************************************************************************************** + Fonction INIT_SORTIES + + Intitialisation des entrées, quel que soit leur type. +**********************************************************************************************************************************************************************************/ +int8_t init_sorties(void) +{ + + // Init des sorties GPIO : + if (init_sorties_gpio() == -1) return(-1); + + return(0); +} + + +/********************************************************************************************************************************************************************************** + Fonction INIT_SORTIES_GPIO + + Configure les I/O du GPIO + Renvoi -1 en cas d'erreur lors de l'initialisation. +**********************************************************************************************************************************************************************************/ +int8_t init_sorties_gpio(void) +{ +int flag_erreur = 0; +uint8_t groupe; +uint8_t position_groupe; +uint8_t i; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + i = 0; + + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_MOD_WKUP; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_USB_AVANT_PWR_EN; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_TO_UC; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_Pdown_HDMI_CMD_N; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_WIFI_CHIP_EN; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_PWKEY_MOD; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_AUDIO_SD; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_TEST_TP1140; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_TEST_TP1141; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_TEST_TP1142; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_LCD_EN_VGx; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM3P_DO_LCD_EN_VDD; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + } + else // V2 et V3 : + { + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_TO_UC; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_USB_ARRIERE_PWR_EN; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_GNSS_EXTINT; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_AUDIO_SD; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_GNSS_SAFEBOOT; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_Pdown_HDMI_CMD_N; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_USB_AVANT_PWR_EN; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_EN_LCD_VGx; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + + sorties.gpio.sorties[i].flag_validite = TRUE; + sorties.gpio.sorties[i].no = CM4_DO_EN_LCD_VDD; + sorties.gpio.sorties[i].pull = NO_PUP_PDN; + sorties.gpio.sorties[i].logique = 1; + i ++; + } + + + //==================================================================================================== + // V3 (CM5) : réservation groupée des lignes GPIO via libgpiod, en sortie, + // avec valeur initiale LOW (comportement identique à l'ancienne méthode mmap au premier boot). + //==================================================================================================== + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V3) + { + unsigned int offsets[MAX_NOMBRE_SORTIES_GPIO]; + uint8_t nb_lignes = 0; + + struct gpiod_line_settings *settings = gpiod_line_settings_new(); + gpiod_line_settings_set_direction(settings, GPIOD_LINE_DIRECTION_OUTPUT); + gpiod_line_settings_set_output_value(settings, GPIOD_LINE_VALUE_INACTIVE); // Etat bas par défaut, cf discussion + + struct gpiod_line_config *line_cfg = gpiod_line_config_new(); + + struct gpiod_request_config *req_cfg = gpiod_request_config_new(); + gpiod_request_config_set_consumer(req_cfg, "shipheart_sorties"); + + gpiod_chip0_sorties = gpiod_chip_open("/dev/gpiochip0"); + if (gpiod_chip0_sorties == NULL) + { + sprintf(tmp, "Ouverture de /dev/gpiochip0 (sorties) impossible..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + + for (i=0; i +DIR *repertoire; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(systeme.sequenceur.etat) + { + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_INIT : + { + if (systeme.sequenceur.compteur == 0) + { + //-------------------------------------------------------------------- + // RAZ du séquenceur de chargement de code (utile lorsqu'une reconnaissance réseau est lancée au milieu d'un téléchargement) : + if (chargement_code.id_message_user != 0) + { + maj_message_users(chargement_code.id_message_user, "", MESSAGE_USERS_ETAT_SUPPRESSION); // Suppression du message s'il y en avait encore un d'actif (cas d'un redémarrage de procédure en cours) + chargement_code.id_message_user = 0; + } + chargement_code.sequenceur.compteur = 0; + chargement_code.sequenceur.compteur_time_out = 0; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; + + //-------------------------------------------------------------------- + // Lancement de la reconnaissance du réseau K-Bus dès qu'il est prêt : + if (c_bus.sequenceur.etat == ETAT_SEQUENCEUR_C_BUS_INIT) + { + affichage_debug(__FILE__, __LINE__, "C-Bus pas encore démarré. Attente...", TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + systeme.sequenceur.compteur = SYSTEME_TEMPO_ATTENTE_DISPO_C_BUS; + } + else + { + c_bus_down_commande_lecture_date_heure(); // Lecture de la date/heure depuis le Compagnon qui est le seul à être sauvegardé + c_bus_down_commande_annuaire_k_bus(); // Lecture de l'annuaire des cartes présentes dans le système + + affichage_debug(__FILE__, __LINE__, "Demande annuaire K-Bus envoyée\t: Ok", TYPE_MESSAGE_DEBUG_OK); + + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_K_BUS; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_K_BUS : + { + if (systeme.modules_physiques[0].flag_presence) // Le flag de présence du Compagnon a dû être positionné par le retour de la commande 'c_bus_down_commande_annuaire()' + { + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; + c_bus_down_commande_lecture_id_carte(i); // Envoi d'une commande de lecture_ID à chaque carte présente + } + + affichage_debug(__FILE__, __LINE__, "Lecture ID des modules\t\t: Ok", TYPE_MESSAGE_DEBUG_OK); + + systeme.sequenceur.compteur = SYSTEME_TEMPO_LECTURE_ID_CARTES; + systeme.sequenceur.compteur_time_out = SYSTEME_TIME_OUT_LECTURE_ID_CARTES; // SDIR #304 + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_LECTURE_ID_CARTES; + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_LECTURE_ID_CARTES : + { + if (systeme.sequenceur.compteur == 0) + { + flag_id_complete = TRUE; + + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + + if (systeme.modules_physiques[i].type == -1) // Si l'ID de la carte a été reçu, son type a forcément été mise à jour (>0) + { + flag_id_complete = FALSE; // S'il n'a pas été mis à jour, il faut attendre encore un peu... + break; // Inutile de continuer... + } + } + + if (flag_id_complete == TRUE) + { + enregistrement_annuaire_k_bus(); // A partir de la réponse à la commande 'annuaire_k_bus()' ci-dessus puis des 'lecture_id_carte()', on défini les cartes présentes ou pas et donc les circuits disponibles ou pas. + + affichage_debug(__FILE__, __LINE__, "Enregistrement annuaire K-Bus\t: Ok", TYPE_MESSAGE_DEBUG_OK); + + entrees.ow.flag_maj_annuaire_en_cours = TRUE; + c_bus_down_commande_annuaire_ow(); // Lecture de l'annuaire des devices OW présents dans le système + + systeme.sequenceur.compteur_time_out = SYSTEME_TIME_OUT_ANNUAIRE_ONE_WIRE; + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_ONE_WIRE; + } + else systeme.sequenceur.compteur = SYSTEME_TEMPO_LECTURE_ID_CARTES; // Si les ID de toutes les cartes n'ont pas été reçus, on lance une petite tempo supplémentaire... + } + + // SDIR #304 : Début + if (systeme.sequenceur.compteur_time_out == 0) + { + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_INIT; // On relance le séquenceur au départ pour effectuer une configuration complète de l'application en fonction du résultat de la reconnaissance réseau faite par le Compagnon sur K-Bus + systeme.sequenceur.compteur = 0; // Pas la peine d'attendre... + } + // SDIR #304 : Fin + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_ONE_WIRE : + { + if (!entrees.ow.flag_maj_annuaire_en_cours) + { + systeme.sequenceur.compteur_time_out = 0; // Arrêt du time-out + + affichage_debug(__FILE__, __LINE__, "Mise à jour annuaire One-Wire\t: Ok", TYPE_MESSAGE_DEBUG_OK); + + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_CHARGEMENT_CONFIG_CARTES; + break; + } + + if (systeme.sequenceur.compteur_time_out == 0) + { + affichage_debug(__FILE__, __LINE__, "Mise à jour annuaire One-Wire\t: Erreur de time-out", TYPE_MESSAGE_DEBUG_ERREUR); + + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_CHARGEMENT_CONFIG_CARTES; + break; + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_CHARGEMENT_CONFIG_CARTES : + { + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) // Envoi des commandes de config de chaque carte : + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + + switch(systeme.modules_physiques[i].type) + { + case TYPE_CARTE_SHIPBASE : + { + c_bus_down_commande_config_carte(i, C_BUS_CONFIG_LECTURE_SEULE); + break; + } + case TYPE_CARTE_SP812 : + case TYPE_CARTE_SP4AC : + case TYPE_CARTE_SP8DC : + { + c_bus_down_commande_config_carte(i, C_BUS_CONFIG_LECTURE_SEULE); + c_bus_down_commande_config_circuits(i); + c_bus_down_commande_cde_circuits(i); // NB : le flag 'flag_changement_consigne_en_cours' de chaque circuit de la carte n'est PAS positionné à TRUE. Cela ne doit pas poser de soucis au démarrage... (Cf SDIR #179) + break; + } + case TYPE_CARTE_SBS : + { + c_bus_down_commande_config_carte(i, C_BUS_CONFIG_LECTURE_SEULE); + c_bus_down_commande_config_batteries(i); + break; + } + default : ; + } + } + + affichage_debug(__FILE__, __LINE__, "Configuration des modules\t\t: Ok", TYPE_MESSAGE_DEBUG_OK); + + systeme.sequenceur.compteur = SYSTEME_TEMPO_ATTENTE_CONFIG_CARTES; // Cette tempo retarde la remonté de la sortie CM3P_READY et "bloque" donc au niveau du Compagnon la remontée de certaines commandes qui ne serviraient à rien. + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_ATTENTE_CONFIG_CARTES; + + break; + } + + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_ATTENTE_CONFIG_CARTES : + { + if (systeme.sequenceur.compteur == 0) + { + check_mise_a_jour(); // Vérification de la cohérence des versions de chaque module. Si besoin, la fonction lancera le séquenceur de chargement_code. + + maj_etat_cm3p(ON); // Maj du flag, de la BDD et de la sortie physique + + affichage_debug(__FILE__, __LINE__, "La configuration est terminée. La sortie CM3P_Ready est activée vers le Compagnon", TYPE_MESSAGE_DEBUG_OK); + printf("\n----------------- Fin d'initialisation --------------------\n\n"); + + // SDIR #301 : + //------------------------------------ + // Sauvegarde de la base de données : + affichage_debug(__FILE__, __LINE__, "Lancement sauvegarde BDD\t\t: OK", TYPE_MESSAGE_DEBUG_OK); +//ret = sauvegarde_bdd(); +ret=0; + if (ret != 0) + { + sprintf(tmp, "\tSauvegarde de la base de données : NOK (retour = %i)", ret); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else affichage_debug(__FILE__, __LINE__, "Fin de sauvegarde BDD\t\t: OK", TYPE_MESSAGE_DEBUG_OK); + // Fin de SDIR #301 + + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_REPOS; + } + + break; + } + + + //----------------------------------------------------------------------------------------------------------------------------------------- + // Cet état repos est balayé à chaque boucle principale, + // sans temporisation. Il est donc important de la garder + // la plus rapide possible dans son cas le plus général. + case ETAT_SEQUENCEUR_SYSTEME_REPOS : + { + //============================= + // Surveillance du Compagnon : + if (systeme.flag_compagnon_ready == FALSE) + { + if (lecture_entree(TYPE_ENTREES_GPIO, 0, systeme.entree_gpio_cm_di_from_uc, TRUE) != 0) // 0 est l'adresse de ShipBase + { + systeme.flag_compagnon_ready = TRUE; + affichage_debug(__FILE__, __LINE__, "L'entrée Compagnon_Ready est passée à ON", TYPE_MESSAGE_DEBUG_OK); + + re_configuration_systeme(); // Génère les commandes nécessaires sur C-Bus pour reconfigurer le Compagnon et le K-Bus + } + } + else + { + if (lecture_entree(TYPE_ENTREES_GPIO, 0, systeme.entree_gpio_cm_di_from_uc, TRUE) == 0) // 0 est l'adresse de ShipBase + { + systeme.flag_compagnon_ready = FALSE; + affichage_debug(__FILE__, __LINE__, "L'entrée Compagnon_Ready est passée à OFF", TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + //================================== + // Sauvegarde régulière de la BDD : + if (systeme.compteur_sauvegarde_bdd == 0) + { + systeme.compteur_sauvegarde_bdd = systeme.periode_sauvegarde_bdd; // Provenance = 'Periode_sauvegarde_bdd' de TB_Configuration +// sauvegarde_bdd(); + } + + //====================================================== + // Recherche de nouveaux fichiers downloadés à traiter : + if (systeme.compteur_check_new_download == 0) // SDIR #196 (Début) + { + systeme.compteur_check_new_download = SYSTEME_TEMPO_PERIODE_CHECK_NEW_DOWNLOAD; + systeme.flag_new_download = TRUE; + } // SDIR #196 (Fin) + + if ((systeme.flag_new_download == TRUE) && + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS)) // Si un chargement de code est en cours, on attend avant de traiter d'autre fichiers à downloader + { + // NB : le flag n'est remis à FALSE qu'après avoir traité tous les fichiers actuellement présents dans le répertoire download. + // Sinon, dans le cas de fréquence de Download "élevée", comme pendant les tests, on relançait un download en plein chargement de code + // => plantage car on partage les fichiers et les répertoires... + + // Listing des fichiers locaux : + 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 '..' + + //-------------------------------------------------------- + // Recherche des fichiers bash exécutables immédiatement : + // Ce sont les fichiers 'now*.sh' qui sont exécutés (en parallèle de l'application via l'utilisation d'un '&' au lancement). + // Il est important que le fichier bash se renomme lui-même avec le suffixe DONE afin de ne pas être traité à nouveau. + if ((strstr(dir->d_name, MARQUEUR_FICHIER_BASH) != NULL) && + (strstr(dir->d_name, "now_") != NULL) && // On ne traite que les fichiers qui contiennent (en pratique, commencent par) "now_"... + (strstr(dir->d_name, MARQUEUR_FICHIER_DONE) == NULL)) // ... MAIS qui ne contiennent pas le texte ".DONE" qui indique qu'ils ont été traités. + { +#ifdef __DEBUG_SYSTEME + sprintf(tmp, "Le fichier bash [%s] a été trouvé dans le répertoire download", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + // Affectation des droits d'exécution au fichier bash : + sprintf(commande, "sudo chmod +x %s/%s", REPERTOIRE_SERVER_DOWNLOAD, dir->d_name); // Sans '&' de terminaison car il faut que la commande soit terminée avant de poursuivre + commande_shell(commande); + + // Exécution du fichier bash : + sprintf(commande, "%s/%s &", REPERTOIRE_SERVER_DOWNLOAD, dir->d_name); // /!\ Ne pas oublier le '&' à la fin pour ne pas bloquer l'application !!!! + if (commande_shell(commande) == 0) + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_EXECUTION_BASH_DEBUT_OK, 0, 0, dir->d_name); + + sprintf(tmp, "Le fichier bash [%s] a été exécuté avec succès.", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + else + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_EXECUTION_BASH_DEBUT_NOK, 0, 0, dir->d_name); + + sprintf(tmp, "Le fichier bash [%s] n'a pas ou être exécuté correctement.", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + //-------------------------------------------------------- + // Recherche des fichiers encodés (a priori, des fichiers de version) : + if ((strstr(dir->d_name, MARQUEUR_FICHIER_TAR_GZIP_ENC) != NULL) && + (strstr(dir->d_name, MARQUEUR_FICHIER_DONE) == NULL)) // On ne traite que les fichiers qui ne contiennent pas le texte ".DONE" qui indique qu'ils ont été traités. + { + sprintf(tmp, "Le fichier archive [%s] a été trouvé dans le répertoire download", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + // Vérification du type de MAJ à appliquer en fonction du fichier reçu : + uint8_t mode_validation = systeme.mode_validation_maj; // La variable locale sera modifié au cours de l'analyse donc on récupère une copie au départ pour ne pas altérer l'image de la BDD + + if ((mode_validation == MODE_VALIDATION_MAJ_MANUELLE) || // Si le mode de MAJ est AUCUNE, il est inutile dde vérifier si la nouvelle version est valide + (mode_validation == MODE_VALIDATION_MAJ_AUTOMATIQUE)) + { + // Vérification que le fichier reçu est utilisable : + char *ptr_new[MAX_NOMBRE_PARAMETRES_LISTE_DOWNLOAD]; // NB : le define utilisé pour la taille n'est pas directement lié au sujet mais pratique... + char *ptr_now[MAX_NOMBRE_PARAMETRES_LISTE_DOWNLOAD]; + char version_new[MAX_LONGUEUR_NOM_FICHIER]; + char version_now[MAX_LONGUEUR_NOM_FICHIER]; + + strcpy(version_new, dir->d_name); // Copie dans une chaine locale car on va la modifier. Nom de la forme : 'ShipHeart_v_3_2_1_uf.tar.gz.enc' + ptr_new[0] = strstr(version_new, "."); // Recherche du premier '.' + if (ptr_new[0] != NULL) // Si on a trouvé un premier '.' : + { + *ptr_new[0] = 0; // Fin de chaine au premier point. Version maintenant de la forme : 'ShipHeart_v_3_2_1_uf' + + i = 0; + ptr_new[i] = strtok (version_new, "_"); // Extraction du premier segment avant le premier '_'. ATTENTION : si aucun délimiteur trouvé, la fonction renvoit la chaine entière (et pas NULL...) + while ((ptr_new[i] != NULL) && (i < MAX_NOMBRE_PARAMETRES_LISTE_DOWNLOAD)) + { + i ++; + ptr_new[i] = strtok (NULL, "_"); // Extraction du segment suivant le prochain '_' + } + + // Check du nombre de champs trouvés : + if (i == 6) // Valeur correcte = 6 segments autour des '_' dans 'ShipHeart_v_3_2_1_uf' + { + if ((ptr_new[5][1] != 't') && (ptr_new[5][1] != 'T')) // Si le flag 'Version_back_enable' n'est PAS 'true' (probablement 'false' donc), alors il faut vérifier l'antériorité de la version actuelle : + { + strcpy(version_now, systeme.package_no_version); // Copie dans une chaine locale car on va la modifier. Version de la forme : '3.2.1' + i = 0; + ptr_now[i] = strtok (version_now, "."); // Extraction du premier segment avant le premier '.'. ATTENTION : si aucun délimiteur trouvé, la fonction renvoit la chaine entière (et pas NULL...) + while ((ptr_now[i] != NULL) && (i < MAX_NOMBRE_PARAMETRES_LISTE_DOWNLOAD)) + { + i ++; + ptr_now[i] = strtok (NULL, "."); // Extraction du segment suivant le prochain '.' + } + + if (i == 3) // Valeur correcte = 3 segments autour des '.' dans '3.2.1' + { + do // Fake loop : + { + // Premier chiffre : + if (atoi(ptr_new[2]) < atoi(ptr_now[0])) // 1.y.z < 3.y.z + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + break; + } + if (atoi(ptr_new[2]) > atoi(ptr_now[0])) break; // 3.y.z > 1.y.z + // x = x + // Deucième chiffre : + if (atoi(ptr_new[3]) < atoi(ptr_now[1])) // 1.2.z < 1.3.z + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + break; + } + if (atoi(ptr_new[3]) > atoi(ptr_now[1])) break; // 1.3.z > 1.2.z + // x.y = x.y + // Troisème chiffre : + if (atoi(ptr_new[4]) < atoi(ptr_now[2])) // 1.2.3 < 1.2.4 + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + break; + } + if (atoi(ptr_new[4]) > atoi(ptr_now[2])) break; // 1.2.4 > 1.2.3 + + // Egalité des versions : + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; // 1.2.3 = 1.2.3 + + } while(0); + + if (mode_validation == MODE_VALIDATION_MAJ_AUCUNE) + { + sprintf(tmp, "Le fichier archive [%s] ne peut pas être traité car sa version n'est pas strictement postérieure à la version en cours [%s] et son flag [Version_back_enable] est False.", dir->d_name, systeme.package_no_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + else // Si la structure de version n'est pas de la forme "x.y.z', on rejette le fichier car on aun problème de structure générale : + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + + sprintf(tmp, "Le fichier archive [%s] ne peut pas être traité car la version actuelle [%s] a un format non valide.", dir->d_name, systeme.package_no_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + else // Si le flag 'Version_back_enable' est 'true', alors il est inutile de vérifier l'antériorité de la version actuelle... + { + sprintf(tmp, "Le fichier archive [%s] est considéré comme valide puisque son flag [Version_back_enable] est True.", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } + } + else // Si la structure de nom de fichier n'a pas 6 segments autour des '_', on le rejette : + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + + sprintf(tmp, "Le fichier archive [%s] ne peut pas être traité car son format est non valide (attendu [ShipHeart_v_3_2_1_uf.*]).", dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + else // Si la structure de nom de fichier n'a pas de '.' dans le nom, on ne le traite pas : + // NB : ce cas ne devrait pas arriver car on a détecté MARQUEUR_FICHIER_TAR_GZIP_ENC (qui contient 3 x '.' !) dans le nom pour arriver ici... Donc pas de message de debug. + { + mode_validation = MODE_VALIDATION_MAJ_AUCUNE; + } + } + else + { + sprintf(tmp, "Le mode de validation des mises à jour étant dévalidé [%i], le fichier [%s] n'est pas traité.", mode_validation, dir->d_name); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + + // Le traitement dépend du mode de validation en cours : + char nom_repertoire_destination[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL]; + uint16_t code_ok = 0, code_erreur = 0; // L'init sert juste à éviter un warning... + + //------------------------------------------------------------ + // Attente validation manuelle => Transfert vers A_VALIDER : + if (mode_validation == MODE_VALIDATION_MAJ_MANUELLE) + { + strcpy(nom_repertoire_destination, REPERTOIRE_VERSIONS_A_VALIDER); + code_ok = SYSTEME_COPIE_ARCHIVE_A_VALIDER_OK; + code_erreur = SYSTEME_COPIE_ARCHIVE_A_VALIDER_NOK; + } + + //------------------------------------------------------------------ + // Validation automatique -> Transfert directement dans A_CHARGER : + else if (mode_validation == MODE_VALIDATION_MAJ_AUTOMATIQUE) + { + strcpy(nom_repertoire_destination, REPERTOIRE_VERSIONS_A_CHARGER); + code_ok = SYSTEME_COPIE_ARCHIVE_A_CHARGER_OK; + code_erreur = SYSTEME_COPIE_ARCHIVE_A_CHARGER_NOK; + } + //----------------------------- + // Aucune mise à jour : + else ; + + + // Vidage des répertoires A_VALIDER et A_CHARGER (la méthode choisie est un peu violente mais efficace...) : + // NB : ce vidage est fait dans les 3 cas de 'Mode_validation maj'. En effet, même dans le cas de MAJ_AUCUNE, + // on considère que si on vient de recevoir un fichier et qu'on le jette, il faut s'assurer qu'il ne reste pas + // un "vieux" fichier dans A_CHARGER ou A_VALIDER... + sprintf(commande, "sudo rm -f %s/*", REPERTOIRE_VERSIONS_A_VALIDER); + commande_shell(commande); + + sprintf(commande, "sudo rm -f %s/*", REPERTOIRE_VERSIONS_A_CHARGER); + commande_shell(commande); + + sprintf(tmp, "Les répertoires A_CHARGER et A_VALIDER viennent d'être vidés suite à la réception d'une nouvelle version."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + + // Copie du fichier reçu dans le répertoire ad'hoc : + if ((mode_validation == MODE_VALIDATION_MAJ_MANUELLE) || + (mode_validation == MODE_VALIDATION_MAJ_AUTOMATIQUE)) + { + // Copie du fichier reçu : + sprintf(commande, "sudo cp -f %s/%s %s/%s", REPERTOIRE_SERVER_DOWNLOAD, dir->d_name, nom_repertoire_destination, dir->d_name); // NB : l'utilisation de '&' en fin de commande pose pb (la copie ne doit pas être terminée quand on tente de renommer ensuite le fichier ?) + if (commande_shell(commande) == 0) + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, code_ok, 0, 0, dir->d_name); + + sprintf(tmp, "Le fichier archive [%s] a été copié avec succès dans le répertoire [%s].", dir->d_name, nom_repertoire_destination); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + } + else + { + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, code_erreur, 0, 0, dir->d_name); + + sprintf(tmp, "Le fichier archive [%s] n'a pas ou être copié correctement dans le répertoire [%s].", dir->d_name, nom_repertoire_destination); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + // Renommage du fichier reçu avec le marqueur DONE (dans tous les cas) : + sprintf(commande, "mv %s/%s %s/%s%s", REPERTOIRE_SERVER_DOWNLOAD, dir->d_name, REPERTOIRE_SERVER_DOWNLOAD, dir->d_name, MARQUEUR_FICHIER_DONE); + commande_shell(commande); + + // Commande de synchro afin de s'assurer que les modifications de fichier seront bien prises en compte par le système : + sprintf(commande, "sudo sync"); + commande_shell(commande); + } + } + closedir(repertoire); // Fermeture du répertoire + } + + systeme.flag_new_download = FALSE; // Il faut RAZ le flag pour autoriser les downloads à venir. + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_SYSTEME_DEMARRAGE_IMPOSSIBLE : + { + + + break; + } + + + + //----------------------------------------------------------------------------------------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Système est inconnu [%i]. Il est repositionné au REPOS", systeme.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_REPOS; + break; + } + } + + + //==================================== + // Séquenceur de chargement du code : + gestion_chargement_code(); +} + + + +//================================================================================================================================================================== +// Fonction : Lecture du fichier JSON de la version courante puis comparaison entre les n° de version présents dans ce fichier, les fichiers présents dans CURRENT +// et, surtout, entre le fichier présent dans CURRENT et la version en cours d'éxécution sur les modules sur K-Bus. S'il y a écart, activation du +// séquenceur de chargement afin que les fichiers de current soient chargés dans les modules concernés sur K-Bus. +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void check_mise_a_jour(void) +{ +struct dirent *dir; // Les types 'dirent' ou 'DIR' sont déclarés dans le header +DIR *repertoire; +char nom_fichier[MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL + MAX_LONGUEUR_NOM_FICHIER]; +uint16_t i; +int16_t comp; +struct Struct_json_version json_version; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Parse du fichier de version : + sprintf(nom_fichier, "%s/%s%s", REPERTOIRE_VERSIONS_CURRENT, systeme.package_version, MARQUEUR_FICHIER_JSON); +#ifdef __DEBUG_SYSTEME_FICHIER_JSON + json_version = parse_json_version(nom_fichier, true); +#else + json_version = parse_json_version(nom_fichier, false); +#endif + + // Traitement des infos du fichier de version : + if (strstr(json_version.no_version, "ERREUR") == NULL) // Si la lecture du fichier de version s'est passée normalement + { + repertoire = opendir(REPERTOIRE_VERSIONS_CURRENT); // Ouverture du répertoire CURRENT + 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 '..' + + + //------------------------------------------------------ + // Application : + if (strstr(dir->d_name, MARQUEUR_FICHIER_APPLICATION) != NULL) + { + comp = strcmp(dir->d_name, json_version.application); + + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier Application [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier Application [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.application); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier Application [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.application); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + //------------------------------------------------------ + // Application QT : + if (strstr(dir->d_name, MARQUEUR_FICHIER_QT) != NULL) + { + comp = strcmp(dir->d_name, json_version.qt); + + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier QT [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier QT [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.qt); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier QT [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.qt); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + + //------------------------------------------------------ + // Firmware Compagnon : + if ( ((systeme.generation_shipbase == GENERATION_SHIPBASE_V1) && (strstr(dir->d_name, MARQUEUR_FICHIER_COMPAGNON_V1) != NULL)) || // SDIR #162 + (((systeme.generation_shipbase == GENERATION_SHIPBASE_V2) || (systeme.generation_shipbase == GENERATION_SHIPBASE_V3)) && (strstr(dir->d_name, MARQUEUR_FICHIER_COMPAGNON_V2) != NULL)) ) +// if (strstr(dir->d_name, MARQUEUR_FICHIER_COMPAGNON) != NULL) // SDIR #162 + { + comp = strcmp(dir->d_name, json_version.compagnon); + + // Comparaison entre le fichier réellement présent dans CURRENT et la version de firmware indiquée dans le fichier de version : + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier Compagnon [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier Compagnon [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.compagnon); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier Compagnon [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.compagnon); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + // Comparaison entre le fichier présent dans CURRENT et la version en cours d'éxécution sur le Compagnon : + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_COMPAGNON_V1, systeme.modules_physiques[0].soft_version); + else sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_COMPAGNON_V2, systeme.modules_physiques[0].soft_version); + + // SDIR #137 : + if (((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_NORMAL) && (strcmp(dir->d_name, nom_fichier) > 0)) || // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'excéution (flag de MAJ des firmwares = 0 = Normal) + ((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_SI_DIFFERENTS) && (strcmp(dir->d_name, nom_fichier) != 0)) || // ... OU si le fichier est juste différent mais le flag de MAJ des firmwares est à 1 + (systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_TOUJOURS) ) // ... OU le flag de MAJ des firmwares est à 2 + // Fin de SDIR #137 + { + if ((chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS) || + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT)) + { + strcpy(chargement_code.nom_fichier[0], dir->d_name); // Stockage du nom de fichier à utiliser pour le chargement de code + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT; + + sprintf(tmp, "Le fichier Compagnon [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution [%s]. Le fichier va être chargé sur le Compagnon", dir->d_name, nom_fichier); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le fichier Compagnon [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution [%s]. Mais le séquenceur de chargement de code est occupé (état = %i) et le code ne peut donc pas être chargé sur le Compagnon.", dir->d_name, systeme.modules_physiques[0].soft_version, chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + } + + + //------------------------------------------------------ + // Firmware SP812 : + if (strstr(dir->d_name, MARQUEUR_FICHIER_SP812) != NULL) + { + comp = strcmp(dir->d_name, json_version.sp812); + + // Comparaison entre le fichier réellement présent dans CURRENT et la version de firmware indiquée dans le fichier de version : + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP812 [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP812 [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.sp812); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier SP812 [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.sp812); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + + // Comparaison entre le fichier présent dans CURRENT et la version en cours d'éxécution sur chaque SP812 : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + if (systeme.modules_physiques[i].type != TYPE_CARTE_SP812) continue; // On ne traite que les cartes de type SP812 pour ce type de fichier de code + + sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_SP812, systeme.modules_physiques[i].soft_version); + + // SDIR #137 : + if (((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_NORMAL) && (strcmp(dir->d_name, nom_fichier) > 0)) || // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'excéution (flag de MAJ des firmwares = 0 = Normal) + ((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_SI_DIFFERENTS) && (strcmp(dir->d_name, nom_fichier) != 0)) || // ... OU si le fichier est juste différent mais le flag de MAJ des firmwares est à 1 + (systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_TOUJOURS) ) // ... OU le flag de MAJ des firmwares est à 2 + // Fin de SDIR #137 + { + if ((chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS) || + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT)) + { + strcpy(chargement_code.nom_fichier[i], dir->d_name); // Stockage du nom de fichier à utiliser pour le chargement de code + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT; + + sprintf(tmp, "Le fichier SP812 [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Le fichier va être chargé sur le SP812", dir->d_name, i, systeme.modules_physiques[i].soft_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le fichier SP812 [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Mais le séquenceur de chargementd e code est occupé (état = %i) et le code ne peut donc pas être chargé sur le SP812.", dir->d_name, i, systeme.modules_physiques[0].soft_version, chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + } + } + + //------------------------------------------------------ + // Firmware ShipBS : + if (strstr(dir->d_name, MARQUEUR_FICHIER_SBS) != NULL) + { + comp = strcmp(dir->d_name, json_version.sbs); + + // Comparaison entre le fichier réellement présent dans CURRENT et la version de firmware indiquée dans le fichier de version : + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier ShipBS [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier ShipBS [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.sbs); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier ShipBS [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.sbs); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + + // Comparaison entre le fichier présent dans CURRENT et la version en cours d'éxécution sur chaque ShipBS : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + if (systeme.modules_physiques[i].type != TYPE_CARTE_SBS) continue; // On ne traite que les cartes de type ShipBS pour ce type de fichier de code + + sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_SBS, systeme.modules_physiques[i].soft_version); + + // SDIR #137 : +// if (strcmp(dir->d_name, nom_fichier) > 0) // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'exécution, le fichier va être chargé + if (((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_NORMAL) && (strcmp(dir->d_name, nom_fichier) > 0)) || // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'excéution (flag de MAJ des firmwares = 0 = Normal) + ((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_SI_DIFFERENTS) && (strcmp(dir->d_name, nom_fichier) != 0)) || // ... OU si le fichier est juste différent mais le flag de MAJ des firmwares est à 1 + (systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_TOUJOURS) ) // ... OU le flag de MAJ des firmwares est à 2 + // Fin de SDIR #137 + { + if ((chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS) || + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT)) + { + strcpy(chargement_code.nom_fichier[i], dir->d_name); // Stockage du nom de fichier à utiliser pour le chargement de code + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT; + + sprintf(tmp, "Le fichier ShipBS [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Le fichier va être chargé sur le ShipBS", dir->d_name, i, systeme.modules_physiques[i].soft_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le fichier ShipBS [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Mais le séquenceur de chargementd e code est occupé (état = %i) et le code ne peut donc pas être chargé sur le ShipBS.", dir->d_name, i, systeme.modules_physiques[0].soft_version, chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + } + } + + + //------------------------------------------------------ + // Firmware SP4AC : + if (strstr(dir->d_name, MARQUEUR_FICHIER_SP4AC) != NULL) + { + comp = strcmp(dir->d_name, json_version.sp4ac); + + // Comparaison entre le fichier réellement présent dans CURRENT et la version de firmware indiquée dans le fichier de version : + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP4AC [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP4AC [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.sp4ac); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier SP4AC [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.sp4ac); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + + // Comparaison entre le fichier présent dans CURRENT et la version en cours d'éxécution sur chaque SP4AC : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + if (systeme.modules_physiques[i].type != TYPE_CARTE_SP4AC) continue; // On ne traite que les cartes de type SP4AC pour ce type de fichier de code + + sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_SP4AC, systeme.modules_physiques[i].soft_version); + + // SDIR #137 : +// if (strcmp(dir->d_name, nom_fichier) > 0) // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'exécution, le fichier va être chargé + if (((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_NORMAL) && (strcmp(dir->d_name, nom_fichier) > 0)) || // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'excéution (flag de MAJ des firmwares = 0 = Normal) + ((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_SI_DIFFERENTS) && (strcmp(dir->d_name, nom_fichier) != 0)) || // ... OU si le fichier est juste différent mais le flag de MAJ des firmwares est à 1 + (systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_TOUJOURS) ) // ... OU le flag de MAJ des firmwares est à 2 + // Fin de SDIR #137 + { + if ((chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS) || + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT)) + { + strcpy(chargement_code.nom_fichier[i], dir->d_name); // Stockage du nom de fichier à utiliser pour le chargement de code + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT; + + sprintf(tmp, "Le fichier SP4AC [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Le fichier va être chargé sur le SP4AC", dir->d_name, i, systeme.modules_physiques[i].soft_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le fichier SP4AC [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Mais le séquenceur de chargementd e code est occupé (état = %i) et le code ne peut donc pas être chargé sur le SP4AC.", dir->d_name, i, systeme.modules_physiques[0].soft_version, chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + } + } + + //------------------------------------------------------ + // Firmware SP8DC : + if (strstr(dir->d_name, MARQUEUR_FICHIER_SP8DC) != NULL) + { + comp = strcmp(dir->d_name, json_version.sp8dc); + + // Comparaison entre le fichier réellement présent dans CURRENT et la version de firmware indiquée dans le fichier de version : + if (comp == 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP8DC [%s] trouvé dans le répertoire CURRENT correspond bien au package en cours", dir->d_name); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + } + else if (comp > 0) + { +#ifdef __DEBUG_SYSTEME_MISE_A_JOUR +sprintf(tmp, "Le fichier SP8DC [%s] trouvé dans le répertoire CURRENT est postérieur au package en cours [%s]. Une mise à jour correcte serait nécessaire...", dir->d_name, json_version.sp8dc); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); +#endif + } + else + { + sprintf(tmp, "Le fichier SP8DC [%s] trouvé dans le répertoire CURRENT est antérieur au package en cours [%s]. Une mise à jour est impérative !!", dir->d_name, json_version.sp8dc); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + + + // Comparaison entre le fichier présent dans CURRENT et la version en cours d'éxécution sur chaque SP8DC : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + if (systeme.modules_physiques[i].type != TYPE_CARTE_SP8DC) continue; // On ne traite que les cartes de type SP8DC pour ce type de fichier de code + + sprintf(nom_fichier, "%s_%s.bin", MARQUEUR_FICHIER_SP8DC, systeme.modules_physiques[i].soft_version); + + // SDIR #137 : +// if (strcmp(dir->d_name, nom_fichier) > 0) // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'exécution, le fichier va être chargé + if (((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_NORMAL) && (strcmp(dir->d_name, nom_fichier) > 0)) || // Si le fichier dans CURRRENT est plus récent que le firmware en cours d'excéution (flag de MAJ des firmwares = 0 = Normal) + ((systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_SI_DIFFERENTS) && (strcmp(dir->d_name, nom_fichier) != 0)) || // ... OU si le fichier est juste différent mais le flag de MAJ des firmwares est à 1 + (systeme.mode_maj_firmwares == MODE_MAJ_FIRMWARES_TOUJOURS) ) // ... OU le flag de MAJ des firmwares est à 2 + // Fin de SDIR #137 + { + if ((chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS) || + (chargement_code.sequenceur.etat == ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT)) + { + strcpy(chargement_code.nom_fichier[i], dir->d_name); // Stockage du nom de fichier à utiliser pour le chargement de code + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT; + + sprintf(tmp, "Le fichier SP8DC [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Le fichier va être chargé sur le SP8DC", dir->d_name, i, systeme.modules_physiques[i].soft_version); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + else + { + sprintf(tmp, "Le fichier SP8DC [%s] trouvé dans le répertoire CURRENT est postérieur à la version en cours d'exécution sur la carte n° %i [%s]. Mais le séquenceur de chargement de code est occupé (état = %i) et le code ne peut donc pas être chargé sur le SP8DC.", dir->d_name, i, systeme.modules_physiques[0].soft_version, chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } + } + } + } + + + } + closedir(repertoire); // Fermeture du répertoire + } + } + else ; // Si la lecture du fichier de version n'a pas pu être faite, on ne fait rien... +} + + + +//================================================================================================================================================================== +// Fonction : Activé par la fonction 'check_mise_a_jour()' si un fichier présent dans CURRENT doit être chargé dans 1 ou plusieurs modules sur K-Bus. +// Dans ce cas, le séquenceur prend en charge pour toutes les cartes sur K-Bus concerné les phases de : +// - effacement de la flash, +// - chargement du fichier en gérant les multi-blocs +// - calcul du CRC du fichier et celui sur la cible +// - lancement procédure de reset lorsque tous les modules devant être chargés l'ont été. +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void gestion_chargement_code(void) +{ +FILE * ptr_fic; +uint8_t llsb, lsb, msb, mmsb; +uint16_t i; +// char commande[MAX_TAILLE_COMMANDE_SHELL]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(chargement_code.sequenceur.etat) + { + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_INIT : + { + if (chargement_code.sequenceur.compteur == 0) + { + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS : + { + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT : + { + chargement_code.nb_repetitions = 0; + + sprintf(tmp, "-1;0"); // Paramètres = 'no_module';'avancement_en_%' (no_module: -1=démarrage, 0-N=No_module en cours, -2=fin) + chargement_code.id_message_user = enregistrement_message_users(MESSAGE_USERS_CODE_MAJ_EN_COURS, tmp, MESSAGE_USERS_ETAT_ALERTE); + + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT_CARTE; + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT_CARTE : + { + // Recherche de la prochaine carte à charger : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + +// sprintf(tmp, "\tNom de fichier pour la carte %i = %s", i, chargement_code.nom_fichier[i]); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + + if (chargement_code.nom_fichier[i][0] != 0) // Si le nom de fichier est nul, c'est qu'il n'y a rien à charger pour cette carte + { + chargement_code.adresse_module = i; // Dès qu'on trouve une carte à charger, on mémorise son numéro ... + break; // ... et on passe au chargement proprement dit + } + } + + if (i == MAX_NOMBRE_MODULES) + { +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "Plus aucune carte n'a besoin d'un chargement"); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CHARGEMENT; + break; + } + + // Ouverture du fichier pour extraire les informations générales : + sprintf(tmp, "%s/%s", REPERTOIRE_VERSIONS_CURRENT, chargement_code.nom_fichier[chargement_code.adresse_module]); + ptr_fic = fopen(tmp, "rb"); // Ouverture du fichier en "lecture"... + + if (ptr_fic == NULL) // Si l'ouverture du fichier est impossible : + { + sprintf(tmp, "L'ouverture du fichier à charger '%s' n'est pas possible. Le chargement du code est abandonné...", chargement_code.nom_fichier[chargement_code.adresse_module]); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_DEBUT_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + break; + } + + // Préparation du chargement de code : + fseek (ptr_fic, 0, SEEK_END); // Place le curseur à la fin du fichier + chargement_code.taille_fichier = ftell(ptr_fic); // Lit la position du curseur, donc la taille... + + fseek (ptr_fic, -4, SEEK_END); // Place le curseur à 4 octets de la fin du fichier + llsb = (uint8_t)fgetc(ptr_fic); + lsb = (uint8_t)fgetc(ptr_fic); + msb = (uint8_t)fgetc(ptr_fic); + mmsb = (uint8_t)fgetc(ptr_fic); + chargement_code.crc_fichier = (uint32_t)(mmsb << 24) | (uint32_t)(msb << 16) | (uint32_t)(lsb << 8) | (uint32_t)llsb; + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "Fichier à charger : [%s] - %i octets - CRC = %08Xh", chargement_code.nom_fichier[chargement_code.adresse_module], chargement_code.taille_fichier, chargement_code.crc_fichier); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + chargement_code.no_bloc = 0; + chargement_code.nb_transmis = 0; + + fclose(ptr_fic); // Fermeture du fichier + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "Envoi de la commande d'effacement de la flash sur carte n° %i", chargement_code.adresse_module); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Mise à jour du message User pour l'informer de l'avancement : + sprintf(tmp, "%i;0", chargement_code.adresse_module); // Paramètres = 'no_module';'avancement_en_%' (no_module: -1=démarrage, 0-N=No_module en cours, -2=fin) + maj_message_users(chargement_code.id_message_user, tmp, MESSAGE_USERS_ETAT_ALERTE); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_DEBUT_OK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + + c_bus_down_commande_chargement_code(CHARGEMENT_CODE_MODE_EFFACEMENT_FLASH, chargement_code.adresse_module); // Commande d'effacement de la flash + + chargement_code.sequenceur.compteur = CHARGEMENT_CODE_TEMPO_ATTENTE_EFFACEMENT_FLASH; // De l'ordre de 2-3 secondes + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_EFFACEMENT_FLASH; + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_EFFACEMENT_FLASH : + { + if (chargement_code.sequenceur.compteur == 0) + { + if (chargement_code.status_reponse == 0) + { + c_bus_down_commande_chargement_code(CHARGEMENT_CODE_MODE_CHARGEMENT_BLOC, chargement_code.adresse_module); // Envoi du prochain bloc + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "\tChargement bloc n° %3i Ok (taille bloc = %3i octets, taille totale = %7i octets, soit %i%%)", chargement_code.no_bloc, chargement_code.taille_bloc, chargement_code.nb_transmis, ((chargement_code.nb_transmis * 100) / chargement_code.taille_fichier)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + chargement_code.sequenceur.compteur_time_out = CHARGEMENT_CODE_TIME_OUT_ATTENTE_ECRITURE_BLOC; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_CHARGEMENT_BLOC; + } + else + { + sprintf(tmp, "Retour d'effacement de flash en erreur sur la carte n° %i (erreur [%i])", chargement_code.adresse_module, chargement_code.status_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_ERASE_FLASH_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_CHARGEMENT_BLOC : + { + if (chargement_code.sequenceur.compteur_time_out == 0) + { + sprintf(tmp, "Déclenchement de time-out lors du chargement du bloc n° %i sur la carte n° %i", chargement_code.no_bloc, chargement_code.adresse_module); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + } + + if (chargement_code.flag_reponse == TRUE) + { + if (chargement_code.status_reponse == 0) + { + if (chargement_code.nb_transmis < chargement_code.taille_fichier) // Il reste des données à transmettre + { + chargement_code.no_bloc ++; + if (chargement_code.adresse_module == 0) chargement_code.sequenceur.compteur = CHARGEMENT_CODE_TEMPO_ATTENTE_INTER_BLOCS_COMPAGNON; // Petite attente si le chargement concerne le Compagnon... + else chargement_code.sequenceur.compteur = CHARGEMENT_CODE_TEMPO_ATTENTE_INTER_BLOCS_K_BUS; // ... mais attente plus importante s'il est destiné à une carte sur K-Bus (cette tempo permet au Compagnon d'écouler sur K-Bus les commandes consécutives) + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ATTENTE_INTER_BLOCS; + } + else + { + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "Demande de vérification du CRC sur la carte n° %i", chargement_code.adresse_module); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + c_bus_down_commande_chargement_code(CHARGEMENT_CODE_MODE_CHECK_CRC, chargement_code.adresse_module); // Demande de vérification du CRC + + chargement_code.sequenceur.compteur_time_out = CHARGEMENT_CODE_TIME_OUT_ATTENTE_ECRITURE_BLOC; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_VERIFICATION_CRC; + } + } + else + { + sprintf(tmp, "La dernière commande de chargement de code est revenue en erreur (code [%i]). Le séquenceur de chargement de code est repositionné au repos.", chargement_code.status_reponse); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_ATTENTE_INTER_BLOCS : + { + if (chargement_code.sequenceur.compteur == 0) + { + c_bus_down_commande_chargement_code(CHARGEMENT_CODE_MODE_CHARGEMENT_BLOC, chargement_code.adresse_module); // Envoi du prochain bloc + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE +sprintf(tmp, "\tChargement bloc n° %3i Ok (taille bloc = %3i octets, taille totale = %7i octets, soit %i%%)", chargement_code.no_bloc, chargement_code.taille_bloc, chargement_code.nb_transmis, ((chargement_code.nb_transmis * 100) / chargement_code.taille_fichier)); +affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + + // Mise à jour du message User pour l'informer de l'avancement : + sprintf(tmp, "%i;%i", chargement_code.adresse_module, (chargement_code.nb_transmis * 100) / chargement_code.taille_fichier); // Paramètres = 'no_module';'avancement_en_%' (no_module: -1=démarrage, 0-N=No_module en cours, -2=fin) + maj_message_users(chargement_code.id_message_user, tmp, MESSAGE_USERS_ETAT_ALERTE); + + chargement_code.sequenceur.compteur_time_out = CHARGEMENT_CODE_TIME_OUT_ATTENTE_ECRITURE_BLOC; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_CHARGEMENT_BLOC; + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_VERIFICATION_CRC : + { + if (chargement_code.sequenceur.compteur_time_out == 0) + { + sprintf(tmp, "Déclenchement de time-out lors de la vérification du CRC sur la carte n° %i", chargement_code.adresse_module); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_CRC_FLASH_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + } + + if (chargement_code.flag_reponse == TRUE) + { + if (chargement_code.status_reponse == 0) + { + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE + sprintf(tmp, "Le CRC de la nouvelle version de logiciel chargé sur la carte n° %i est valide", chargement_code.adresse_module); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CARTE; + } + else + { + sprintf(tmp, "Retour de vérification de CRC en erreur sur la carte n° %i. Le séquenceur de chargement est repositionné au repos. ", chargement_code.adresse_module); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_ECRITURE_FLASH_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR; + } + } + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CARTE : + { + for (i = (chargement_code.adresse_module + 1); i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + + if (strcmp(chargement_code.nom_fichier[i], chargement_code.nom_fichier[chargement_code.adresse_module]) == 0) break; // Si une carte à venir doit recevoir le même fichier + } + + systeme.modules_physiques[chargement_code.adresse_module].maj_soft_disponible = 1; // Mise à jour du flag de disponibilité d'une nouvelle version pour la carte + chargement_code.nom_fichier[chargement_code.adresse_module][0] = 0; // Suppression du besoin pour cette carte + +#ifdef __DEBUG_SYSTEME_CHARGEMENT_CODE + sprintf(tmp, "Le chargement de la nouvelle version de logiciel pour la carte n° %i est terminé", chargement_code.adresse_module); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); +#endif + chargement_code.nb_repetitions = 0; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT_CARTE; + + break; + } + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CHARGEMENT: + { + // RAZ de tous les noms de fichier de chargements (dans le doute) : + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) chargement_code.nom_fichier[i][0] = 0; + + // Activation de la nouvelle version sur toutes les cartes ayant reçu une nouvelle version de firmware : + bool flag_carte_chargee = false; + for (i = 0; i < MAX_NOMBRE_MODULES; i ++) + { + if (systeme.modules_physiques[i].flag_presence == FALSE) continue; // On ne taite que les cartes présentes + + if (systeme.modules_physiques[i].maj_soft_disponible != 0) + { + flag_carte_chargee = TRUE; + + c_bus_down_commande_chargement_code(CHARGEMENT_CODE_MODE_VALIDATION_VERSION, i); // Demande d'activation de la nouvelle version chargée + + systeme.modules_physiques[i].maj_soft_disponible = 0; // RAZ du flag + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_FIN_OK, 0, 0, chargement_code.nom_fichier[i]); + + sprintf(tmp, "Activation de la nouvelle version de firmware adressée à la carte n° %i", i); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + } + } + + if (flag_carte_chargee) + { + sprintf(tmp, "Une ou plusieurs cartes ont été chargées avec une nouvelle version de firmware => réinitialisation du système pour prise en compte par leur bootloader"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + sprintf(tmp, "-2;0"); // Paramètres = 'id_module';'avancement_en_%' (id_module: -1=démarrage, 0-N=Id_module en cours, -2=fin) + maj_message_users(chargement_code.id_message_user, tmp, MESSAGE_USERS_ETAT_ALERTE); + + chargement_code.sequenceur.compteur = CHARGEMENT_CODE_TEMPO_ATTENTE_FIN_ACTIVATION; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_ACTIVATION; + } + else + { + sprintf(tmp, "Aucune carte n'a été chargée avec une nouvelle version de firmware => la mise à jour est terminée"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + maj_message_users(chargement_code.id_message_user, "", MESSAGE_USERS_ETAT_SUPPRESSION); // Suppression du message devenu inutile + chargement_code.id_message_user = 0; + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; // Probablement inutile + } + + break; + } + + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_ACTIVATION: + { + if (chargement_code.sequenceur.compteur == 0) + { + maj_message_users(chargement_code.id_message_user, "", MESSAGE_USERS_ETAT_SUPPRESSION); // Suppression du message juste avant la réinit... + chargement_code.id_message_user = 0; + + lancement_reinit(0); // La valeur 0 indique d'utiliser la valeur par défaut + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; // Probablement inutile + } + + break; + } + + + //----------------------------------------------------------------------------------------------------------------------------------------- + case ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR: + { + if ((++ chargement_code.nb_repetitions) >= MAX_REPETITIONS_CHARGEMENT_CODE) + { + sprintf(tmp, "Le nombre de répétitions maximal a été atteint (%i). Le chargement de code est abandonné.", MAX_REPETITIONS_CHARGEMENT_CODE); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_CHARGEMENT_CODE_FIN_NOK, chargement_code.adresse_module, 0, chargement_code.nom_fichier[chargement_code.adresse_module]); + + maj_message_users(chargement_code.id_message_user, "", MESSAGE_USERS_ETAT_SUPPRESSION); // Suppression du message de mise à jour + chargement_code.id_message_user = 0; + + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; + } + else + { + sprintf(tmp, "Le chargement de code est relancé pour le %i ème fois sur cette carte", chargement_code.nb_repetitions); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT_CARTE; + } + + break; + } + + + //----------------------------------------------------------------------------------------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur de Chargement de Code est inconnu [%i]. Il est repositionné au REPOS", chargement_code.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + chargement_code.sequenceur.etat = ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS; + break; + } + } +} + + +//================================================================================================================================================================== +// Fonction : Fonction appelée à la fin de chaque boucle principale. Elle commence par faire une courte attente pour éviter que l'application de consomme 100% +// de temps CPU ! +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void supervision(void) +{ +struct timespec gettime_now; + + // Attente pour ralentir la boucle principale : + delai_nano_sec(SYSTEME_ATTENTE_BOUCLE_PRINCIPALE_TEMPO); + + // Supervision de la vitesse de boucle principale : + clock_gettime(CLOCK_REALTIME, &gettime_now); + if (supervision_boucle_principale.last_second == gettime_now.tv_sec) supervision_boucle_principale.nb_en_cours ++; + else + { + supervision_boucle_principale.last_second = gettime_now.tv_sec; + + if (supervision_boucle_principale.attente_premieres_mesures == 0) // Si on n'est plus dans la phase d'attente initiale : + { + supervision_boucle_principale.nb_boucles += supervision_boucle_principale.nb_en_cours; + supervision_boucle_principale.nb_secondes ++; + + //printf("Compteur de boucles principales = %i\n", supervision_boucle_principale.nb_en_cours); + + } + else supervision_boucle_principale.attente_premieres_mesures --; // En phase d'attente initiale, on décrémente le compteur d'attente... + + supervision_boucle_principale.nb_en_cours = 0; + } +} + + +//================================================================================================================================================================== +// Fonction : Fonction appelée suite à la sortie de la boucle principale (suite à réception d'un signal d'arrêt - de la part d'un utilisateur ou par une erreur +// majeure). Elle enregistre l'arrêt dans le fichier de log et l'historique (autant que possbible). Puis elle libère au maximum les ressources matérielles +// utilisées par l'application. +// Reçoit : Rien +// Renvoit : Rien +//================================================================================================================================================================== +void arret_programme(void) +{ +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + // Enregistrement dans le fichier de debug : + sprintf(tmp, "Arrêt du programme demandé. Fermeture en cours..."); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + // Enregistrement dans l'historique : + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_SYSTEME, SYSTEME_ARRET, 0, 0, NULL); + + // Info au Compagnon et MAJ dans la BDD avant fermeture : + maj_etat_cm3p(OFF); // Maj du flag, de la BDD et de la sortie physique + + //Fermeture de MySQL : + mysql_close(bdd.pointeur); + + // Désactivation des sorties de commandes d'alimentations : +// ecriture_sortie(TYPE_SORTIE_GPIO, 0, CM3P_DO_USBPWR_EN, OFF); // Coupure de l'alim des USB /!\ Il ne faut PAS couper en mode debuug ous peine de ne plus avoir accès au réseau Ethernet via le routeur USB/Ethernet....! + +// ecriture_sortie(TYPE_SORTIE_GPIO, 0, audio.cle_primaire_sortie_cm_do_audio_sd, OFF); // Coupure du circuit audio + +// ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.cle_primaire_sortie_cm_pdown_hdmi_cmd_n, OFF); // Coupure de la sortie du flux HDMI +// ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.cle_primaire_sortie_cm_do_lcd_en_vgx, OFF); // Coupure de l'alim VGx +// ecriture_sortie(TYPE_SORTIE_GPIO, 0, ecran.cle_primaire_sortie_cm_do_lcd_en_vdd, OFF); // Coupure de l'alim VDD (après VGx !) + + // Libération des ports série : + if ((config_serial0.validite == 1) && (config_serial0.filestream != -1)) close(config_serial0.filestream); + if ((config_serial1.validite == 1) && (config_serial1.filestream != -1)) close(config_serial1.filestream); + + // Libération des projections dans l'espace virtuel d'adressage (normalement fait automatiquement à la fermeture du processus mais on ne sait jamais...) : + munmap((uint32_t *)hardware.gpio_base, BLOCK_SIZE); +} + + + + + +//================================================================================================================================================================== +// Fonction : Fonction générant toutes les commandes lorsqu'une mise à jour de la confguration est nécessaire. +// Elle est notamment appelée lorsqu'une modif de configuration a été faite dans la BDD ou que le K-Bus a été réinitialisé +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t re_configuration_systeme(void) +{ + + // Forçage d'un enregistrement des mesures avant la reconfiguration (qui va RAZ tous les cumuls) (SDIR #174) : + bdd.compteur_enregistrement_mesures = 0; + enregistrement_mesures(BDD_ENREGISTREMENT_MESURE_NORMAL); // NB : le timer de mesure est rechargé à l'occasion avec la période normale + + // Rechargement (presque) complet de la BDD : + init_bdd(BDD_MODE_INIT_RELOAD); // Appelle 'maj_etat_cm3p()' puis réinitialise les fonctions CARTES et CIRCUITS. + + // Relance du séquenceur d'init : + systeme.sequenceur.etat = ETAT_SEQUENCEUR_SYSTEME_INIT; // On relance le séquenceur au départ pour effectuer une configuration complète de l'application en fonction du résultat de la reconnaissance réseau faite par le Compagnon sur K-Bus + systeme.sequenceur.compteur = 0; // Pas la peine d'attendre... + + // Mise à jour des fonctions audio : + set_volume(audio.consigne_volume, AUDIO_SET_VOLUME_ECHO_OFF); // Mise à jour du volume au niveau matériel. NB : l'alimentation de l'ampli est faite en fin de 'set_volume()' et tient compte de l'état de 'Fonction_jack' + + // Commandes spécifiques Compagnon : + c_bus_down_commande_config_level_sensors(); // Utile dans la cas où la configuration des Level Sensors aurait changé (par exempe de via QT et la BDD) + + return(0); +} + +//================================================================================================================================================================== +// Fonction : Lance l'exécution de la commande reçue en paramètre et vérifie son exécution. +// Reçoit : La chaine de la commande +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int32_t commande_shell(char * commande) +{ +int32_t retour; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + retour = system(commande); + if (retour != 0) + { + sprintf(tmp, "Le retour de la commande shell [%s] est en erreur (code = %i)", commande, retour); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + } +/* else + { + sprintf(tmp, "La commande shell [%s] a été lancée sans erreur.", commande); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_PAR_DEFAUT); + } +*/ + + return (retour); +} + + +//================================================================================================================================================================== +// Fonction : Lit le fichier dont le nom est reçu en paramètre et récupère les champs d'un fichier de version. +// Reçoit : Le nom du fichier JSON à parser + un flag de verbose +// Renvoit : Une structure contenant les champs d'un fichier de version. NB : si erreur, le champ 'no_version' contient la chaine "ERREUR" +//================================================================================================================================================================== +struct Struct_json_version parse_json_version(char * nom_fichier, bool verbose) +{ +FILE *fp; +char buffer[MAX_TAILLE_FICHIER_JSON]; +struct json_object *parsed_json; +struct json_object *obj_no_version; +struct json_object *obj_updater; +struct json_object *obj_application; +struct json_object *obj_qt; +struct json_object *obj_compagnon; +struct json_object *obj_sp812; +struct json_object *obj_sbs; +struct json_object *obj_sp4ac; +struct json_object *obj_sp8dc; +struct json_object *obj_sql; +struct json_object *obj_force; +struct json_object *obj_description; +struct Struct_json_version json_version; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + fp = fopen(nom_fichier,"r"); + if (fp == NULL) + { + sprintf(tmp, "Erreur d'ouverture du fichier [%s]\n", nom_fichier); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + strcpy(json_version.no_version, "ERREUR"); // Marqueur d'erreur + return (json_version); + } + + fread(&buffer, 1, MAX_TAILLE_FICHIER_JSON, fp); // Lecture du fichier dans Buffer. + + fclose(fp); + + parsed_json = json_tokener_parse(buffer); + + if (parsed_json == NULL) + { + sprintf(tmp, "Erreur de parse du fichier [%s].\n", nom_fichier); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + strcpy(json_version.no_version, "ERREUR"); // Marqueur d'erreur + return (json_version); + } + + json_object_object_get_ex(parsed_json, "NoVersion", &obj_no_version); + json_object_object_get_ex(parsed_json, "Updater", &obj_updater); + json_object_object_get_ex(parsed_json, "Application", &obj_application); + json_object_object_get_ex(parsed_json, "QT", &obj_qt); + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) json_object_object_get_ex(parsed_json, "Compagnon_v1", &obj_compagnon); + else json_object_object_get_ex(parsed_json, "Compagnon_v2", &obj_compagnon); + json_object_object_get_ex(parsed_json, "SP812", &obj_sp812); + json_object_object_get_ex(parsed_json, "SBS", &obj_sbs); + json_object_object_get_ex(parsed_json, "SP4AC", &obj_sp4ac); + json_object_object_get_ex(parsed_json, "SP8DC", &obj_sp8dc); + json_object_object_get_ex(parsed_json, "SQL", &obj_sql); + json_object_object_get_ex(parsed_json, "Force", &obj_force); + json_object_object_get_ex(parsed_json, "Description", &obj_description); + + + if (obj_no_version != NULL) strcpy(json_version.no_version, json_object_get_string(obj_no_version)); + else strcpy(json_version.no_version, "...inconnue..."); + + if (obj_updater != NULL) strcpy(json_version.updater, json_object_get_string(obj_updater)); + else strcpy(json_version.updater, "...inconnue..."); + + if (obj_application != NULL) strcpy(json_version.application, json_object_get_string(obj_application)); + else strcpy(json_version.application, "...inconnue..."); + + if (obj_qt != NULL) strcpy(json_version.qt, json_object_get_string(obj_qt)); + else strcpy(json_version.qt, "...inconnue..."); + + if (obj_compagnon != NULL) strcpy(json_version.compagnon, json_object_get_string(obj_compagnon)); + else strcpy(json_version.compagnon, "...inconnue..."); + + if (obj_sp812 != NULL) strcpy(json_version.sp812, json_object_get_string(obj_sp812)); + else strcpy(json_version.sp812, "...inconnue..."); + + if (obj_sbs != NULL) strcpy(json_version.sbs, json_object_get_string(obj_sbs)); + else strcpy(json_version.sbs, "...inconnue..."); + + if (obj_sp4ac != NULL) strcpy(json_version.sp4ac, json_object_get_string(obj_sp4ac)); + else strcpy(json_version.sp4ac, "...inconnue..."); + + if (obj_sp8dc != NULL) strcpy(json_version.sp8dc, json_object_get_string(obj_sp8dc)); + else strcpy(json_version.sp8dc, "...inconnue..."); + + if (obj_sql != NULL) strcpy(json_version.sql, json_object_get_string(obj_sql)); + else strcpy(json_version.sql, "...inconnue..."); + + if (obj_force != NULL) json_version.force = json_object_get_boolean(obj_force); + else json_version.force = FALSE; + + if (obj_description != NULL) strcpy(json_version.description, json_object_get_string(obj_description)); + else strcpy(json_version.description, "...inconnue..."); + + if (verbose) + { + printf("Fichier de version\t\t: [%s]\n", nom_fichier); + + printf("\tNo de version\t\t: [%s]\n", json_version.no_version); + printf("\tUpdater\t\t\t: [%s]\n", json_version.updater); + printf("\tApplication\t\t: [%s]\n", json_version.application); + printf("\tQT\t\t\t: [%s]\n", json_version.qt); + printf("\tCompagnon\t\t: [%s]\n", json_version.compagnon); + printf("\tSP812\t\t\t: [%s]\n", json_version.sp812); + printf("\tSBS\t\t\t: [%s]\n", json_version.sbs); + printf("\tSP4AC\t\t\t: [%s]\n", json_version.sp4ac); + printf("\tSP8DC\t\t\t: [%s]\n", json_version.sp8dc); + printf("\tSQL\t\t\t: [%s]\n", json_version.sql); + printf("\tFlag force\t\t: [%s]\n", json_version.force ? "true" : "false"); + printf("\tDescription\t\t: [%s]\n", json_version.description); + printf("\n"); + } + + return(json_version); +} + + +//================================================================================================================================================================== +// Fonction : Envoi le signal reçu au PID reçu (NB : àa peut être SIGKILL ou SIGTERM pour tuer le processus mais ça peut aussi être SIGHUP pour lui demander +// de recharger sa config (comme 'hostapd' par exemple). +// Reçoit : Le PID du processus à qui envoyer le signal et le signal à envoyer +// Renvoit : -1 si erreur, >=0 si Ok (si >0, cela doit représenter le nombre de processus à qui le signel a été envoyé - utilisable si PID est < 0. Pas testé...) +//================================================================================================================================================================== +int8_t send_signal (int32_t pid, int32_t signal) +{ +int32_t ret; + + ret = kill(pid, signal); +// printf("ret : %d",ret); + if ((ret < 0) || (ret > 127)) return(-1); // Ret contient -1 si erreur ou >=0 si Ok + else return(ret); +} + + +//================================================================================================================================================================== +// Fonction : Recherche le PID du processus dont le nom est passé en paramètre +// de recharger sa config (comme 'hostapd' par exemple). +// Reçoit : Le nom du processus à rechercher, les paramètres éventuels à rechercher +// Renvoit : -1 si erreur, >0=PID du processus trouvé +//================================================================================================================================================================== +int32_t get_pid_from_name (char * nom, char * parametres) +{ +#define MAX_LONGUEUR_NOM_PROCESSUS 50 +#define MAX_LONGUEUR_PARAMETRES_PROCESSUS 200 +#define MAX_BUFFER 1500 +char buf[MAX_BUFFER]; +char cde[MAX_LONGUEUR_NOM_PROCESSUS + MAX_LONGUEUR_PARAMETRES_PROCESSUS + 50]; + + if (strlen(nom) > MAX_LONGUEUR_NOM_PROCESSUS) return(-1); + if (strlen(parametres) > MAX_LONGUEUR_PARAMETRES_PROCESSUS) return(-1); + +// sprintf(cde, "pidof -s %s", nom); + sprintf(cde, "pgrep -f '%s.*%s' ", nom, parametres); // Le '.' indique n'importe quel caractère et le '*' s'applique au '.' pou rindiquer "peu importe ne nombre". + +// char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; +// sprintf(tmp, "\tCommande de 'get_pid_from_name' = [ %s ]", cde); +// affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + FILE *cmd_pipe = popen(cde, "r"); + + fgets(buf, MAX_BUFFER, cmd_pipe); + int32_t pid = strtoul(buf, NULL, 10); + + pclose(cmd_pipe); + + return(pid); +} + +//================================================================================================================================================================== +// Fonction : Recherche le nombre de PID actifs dont le nom est passé en paramètre +// Reçoit : Le nom du processus à rechercher, les paramètres éventuels à rechercher +// Renvoit : -1 si erreur, >0=nombre de PID incluant le nom recherché +//================================================================================================================================================================== +int32_t get_pid_numbers_from_name (char * nom, char * parametres) +{ +#define MAX_LONGUEUR_NOM_PROCESSUS 50 +#define MAX_LONGUEUR_PARAMETRES_PROCESSUS 200 +#define MAX_BUFFER 1500 +char buf[MAX_BUFFER]; +char cde[MAX_LONGUEUR_NOM_PROCESSUS + MAX_LONGUEUR_PARAMETRES_PROCESSUS + 50]; + + if (strlen(nom) > MAX_LONGUEUR_NOM_PROCESSUS) return(-1); + if (strlen(parametres) > MAX_LONGUEUR_PARAMETRES_PROCESSUS) return(-1); + + sprintf(cde, "ps aux | grep %s | grep ' %s' | grep -cv grep", nom, parametres); // Le '-c' permet d'obtenir uniquement le nombre d'occurences, le '-v' permet d'éliminer les lignes contenant GREP. + FILE *cmd_pipe = popen(cde, "r"); + + fgets(buf, MAX_BUFFER, cmd_pipe); + int32_t pid_nb = strtoul(buf, NULL, 10); + + pclose(cmd_pipe); + + return(pid_nb); +} + + + +//================================================================================================================================================================== +// Fonction : Recherche la révision du module sur lequel on est en cours d'exécution et en déduit le type et la version de module. +// Cf https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#new-style-revision-codes pour plus de précisions (ou des updates...) +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +int8_t version_compute_module(void) +{ +FILE *fp; +char revcode[32]; +uint32_t code; +char chaine_valeur[300]; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + fp = popen("cat /proc/cpuinfo | awk '/Revision/ {print $3}'", "r"); // récupération du code de Révision via la commande "cat /proc/cpuinfo | awk '/Revision/ {print $3}'". Exemple de retour : "c03141" + + if (fp == NULL) return(-1); // Cas où la commande n'a pas pu être exécutée correctement + + fgets(revcode, sizeof(revcode), fp); // Lecture de la ligne + pclose(fp); // Fermeturre du fichier + + code = strtol(revcode, NULL, 16); // Conversion en entier long depuis la chaine en hexa + + systeme.cm.overvoltage_allowed = (code >> 31) & 0x1; + systeme.cm.otp_programming_allowed = (code >> 30) & 0x1; + systeme.cm.otp_reading_allowed = (code >> 29) & 0x1; + systeme.cm.warranty_bit = (code >> 25) & 0x1; + systeme.cm.new_style_revision = (code >> 23) & 0x1; + systeme.cm.memory_size = (code >> 20) & 0x7; + systeme.cm.manufacturer = (code >> 16) & 0x0F; + systeme.cm.processor = (code >> 12) & 0x0F; + systeme.cm.type = (code >> 4) & 0xFF; + systeme.cm.revision = (code >> 0) & 0x0F; + + if (systeme.cm.new_style_revision == 0) // Ce bit indique le format du code de révision. Le 'new' type est actif depuis le RaspberryPi2 donc on devrait toujours être dans ce cas... + { + sprintf(tmp, "/!\\ Le bit de style de révision indique un ancien style de révision qui ne peut pas être lu...!"); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + return (-1); + } + else + { + systeme.cm.chaine_version[0] = 0; // Chaine vide. + sprintf(tmp, "Caractéristiques du module :"); + + //--------------------------------------------------------------- + switch (systeme.cm.type) + { + case 0x00 : { strcpy(chaine_valeur, "A"); break; } + case 0x01 : { strcpy(chaine_valeur, "B"); break; } + case 0x02 : { strcpy(chaine_valeur, "A+"); break; } + case 0x03 : { strcpy(chaine_valeur, "B+"); break; } + case 0x04 : { strcpy(chaine_valeur, "2B"); break; } + case 0x05 : { strcpy(chaine_valeur, "Alpha"); break; } + case 0x06 : { strcpy(chaine_valeur, "CM1"); break; } + + case 0x08 : { strcpy(chaine_valeur, "3B"); break; } + case 0x09 : { strcpy(chaine_valeur, "Zero"); break; } + case 0x0A : { strcpy(chaine_valeur, "CM3"); break; } + + case 0x0C : { strcpy(chaine_valeur, "Zero W"); break; } + case 0x0D : { strcpy(chaine_valeur, "3B+"); break; } + case 0x0E : { strcpy(chaine_valeur, "3A+"); break; } + case 0x0F : { strcpy(chaine_valeur, "Internal Use only"); break; } + case 0x10 : { strcpy(chaine_valeur, "CM3+"); break; } + case 0x11 : { strcpy(chaine_valeur, "4B"); break; } + case 0x12 : { strcpy(chaine_valeur, "Zero 2W"); break; } + case 0x13 : { strcpy(chaine_valeur, "400"); break; } + case 0x14 : { strcpy(chaine_valeur, "CM4"); break; } + case 0x15 : { strcpy(chaine_valeur, "CM4S"); break; } + case 0x18 : { strcpy(chaine_valeur, "CM5"); break; } + + case 0x07 : + case 0x0B : + default : + { + sprintf(chaine_valeur, "Inconnu [code = %i]", systeme.cm.type); + break; + } + } + affichage_ligne_version_compute_module(tmp, "Type de module", chaine_valeur); + + strcat(systeme.cm.chaine_version, chaine_valeur); + + + //--------------------------------------------------------------- + switch (systeme.cm.memory_size) + { + case 0 : { strcpy(chaine_valeur, "256Mo"); break; } + case 1 : { strcpy(chaine_valeur, "512Mo"); break; } + case 2 : { strcpy(chaine_valeur, "1Go"); break; } + case 3 : { strcpy(chaine_valeur, "2Go"); break; } + case 4 : { strcpy(chaine_valeur, "4Go"); break; } + case 5 : { strcpy(chaine_valeur, "8Go"); break; } + default : + { + sprintf(chaine_valeur, "Inconnue [code = %i]", systeme.cm.memory_size); + break; + } + } + affichage_ligne_version_compute_module(tmp, "Taille RAM", chaine_valeur); + + strcat(systeme.cm.chaine_version, "-"); + strcat(systeme.cm.chaine_version, chaine_valeur); + + + //--------------------------------------------------------------- + switch (systeme.cm.manufacturer) + { + case 0 : { strcpy(chaine_valeur, "SonyUK"); break; } // Eviter les espaces car moins ces chaines sont utilisées pour remonter la version de Compute Module dans TB_Systeme... + case 1 : { strcpy(chaine_valeur, "Egoman"); break; } + case 2 : { strcpy(chaine_valeur, "Embest"); break; } + case 3 : { strcpy(chaine_valeur, "SonyJapan"); break; } + case 4 : { strcpy(chaine_valeur, "Embest"); break; } + case 5 : { strcpy(chaine_valeur, "Stadium"); break; } + default : + { + sprintf(chaine_valeur, "Inconnu [code = %i]", systeme.cm.manufacturer); + break; + } + } + affichage_ligne_version_compute_module(tmp, "Fabricant", chaine_valeur); + + strcat(systeme.cm.chaine_version, "-"); + strcat(systeme.cm.chaine_version, chaine_valeur); + + + //--------------------------------------------------------------- + sprintf(chaine_valeur, "%i", systeme.cm.revision); + affichage_ligne_version_compute_module(tmp, "Revision", chaine_valeur); + + strcat(systeme.cm.chaine_version, "-Rev"); + strcat(systeme.cm.chaine_version, chaine_valeur); + + + //--------------------------------------------------------------- + if (systeme.cm.overvoltage_allowed == 0) strcpy(chaine_valeur, "Allowed"); + else strcpy(chaine_valeur, "Forbidden"); + affichage_ligne_version_compute_module(tmp, "Overvoltage", chaine_valeur); + + strcat(systeme.cm.chaine_version, "-"); + if (systeme.cm.overvoltage_allowed == 0) strcat(systeme.cm.chaine_version, "A"); + else strcat(systeme.cm.chaine_version, "F"); + + + //--------------------------------------------------------------- + if (systeme.cm.otp_programming_allowed == 0) strcpy(chaine_valeur, "Allowed"); + else strcpy(chaine_valeur, "Forbidden"); + affichage_ligne_version_compute_module(tmp, "OTP Programming", chaine_valeur); + + if (systeme.cm.otp_programming_allowed == 0) strcat(systeme.cm.chaine_version, "A"); + else strcat(systeme.cm.chaine_version, "F"); + + //--------------------------------------------------------------- + if (systeme.cm.otp_reading_allowed == 0) strcpy(chaine_valeur, "Allowed"); + else strcpy(chaine_valeur, "Forbidden"); + affichage_ligne_version_compute_module(tmp, "OTP Reading", chaine_valeur); + + if (systeme.cm.otp_reading_allowed == 0) strcat(systeme.cm.chaine_version, "A"); + else strcat(systeme.cm.chaine_version, "F"); + + //--------------------------------------------------------------- + if (systeme.cm.warranty_bit == 0) strcpy(chaine_valeur, "Intact"); + else strcpy(chaine_valeur, "Warranty has been voided by overclocking"); + affichage_ligne_version_compute_module(tmp, "Warranty bit", chaine_valeur); + + if (systeme.cm.warranty_bit == 0) strcat(systeme.cm.chaine_version, "I"); + else strcat(systeme.cm.chaine_version, "V"); + + + //--------------------------------------------------------------- + switch (systeme.cm.processor) + { + case 0 : { strcpy(chaine_valeur, "BCM2835"); break; } + case 1 : { strcpy(chaine_valeur, "BCM2836"); break; } + case 2 : { strcpy(chaine_valeur, "BCM2837"); break; } + case 3 : { strcpy(chaine_valeur, "BCM2711"); break; } + case 4 : { strcpy(chaine_valeur, "BCM2712"); break; } + default : + { + sprintf(chaine_valeur, "Inconnu [code = %i]", systeme.cm.processor); + break; + } + } + affichage_ligne_version_compute_module(tmp, "Processeur", chaine_valeur); + + + //--------------------------------------------------------------- + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_OK); + + //--------------------------------------------------------------- + // Détermination de la génération de ShipBase utilisée : + if (systeme.cm.type == 0x10) + { + systeme.generation_shipbase = GENERATION_SHIPBASE_V1; + affichage_debug(__FILE__, __LINE__, "Génération de ShipBase\t\t\t\t\t\t\t: Première génération", TYPE_MESSAGE_DEBUG_OK); + return 0; + } + else if (systeme.cm.type == 0x14) + { + systeme.generation_shipbase = GENERATION_SHIPBASE_V2; + affichage_debug(__FILE__, __LINE__, "Génération de ShipBase\t\t\t\t\t\t\t: Seconde génération", TYPE_MESSAGE_DEBUG_OK); + return 0; + } + else if (systeme.cm.type == 0x18) + { + systeme.generation_shipbase = GENERATION_SHIPBASE_V3; + affichage_debug(__FILE__, __LINE__, "Génération de ShipBase\t\t\t\t\t\t\t: Troisième génération", TYPE_MESSAGE_DEBUG_OK); + return 0; + } + else + { + systeme.generation_shipbase = GENERATION_SHIPBASE_INCONNUE; + sprintf(tmp, "Génération de ShipBase inconnue [type de Compute Module = %02Xh]. L'initialisation est arrêtée...", systeme.cm.type); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + return(-1); + } + } +} + +//================================================================================================================================================================== +// Fonction : Met en forme et affiche une ligne de version de comupte module +// Reçoit : Rien +// Renvoit : 0 si aucune erreur, -1 si une erreur a été détectée +//================================================================================================================================================================== +void affichage_ligne_version_compute_module(char * tmp, char * titre, char * valeur) +{ +char chaine[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + sprintf(chaine, "\n\t\t\t\t\t\t\t%-20s %s", titre, valeur); + strcat(tmp, chaine); +} + diff --git a/sources/systeme.h b/sources/systeme.h new file mode 100644 index 0000000..05526c1 --- /dev/null +++ b/sources/systeme.h @@ -0,0 +1,171 @@ +//********************************************************************************************************************************************************************************** +// Fichier SYSTEME.H +// +// Contient tous les define liés aux fonctions système... +//**********************************************************************************************************************************************************************************/ + + // Maximums : +#define MAX_NOMBRE_MODULES (32+1) // Nombre maximal de cartes (y compris Compagnon) commandées par ShipHeart. A priori, toutes sont sur le k-bus + Compagnon +#define MAX_NOMBRE_DEVICES_OW 32 // Nombre physique de device max (!= de MAX_NOMBRE_ENTREES_OW dans 'entrees.h') +#define MAX_TAILLE_UNIQUE_ID 12 // Nombre d'octets de l'Unique ID stocké en ROM du µC +#define MAX_TAILLE_TYPE_SOFT 20 // Taille de la chaine de caractère contenant les infos de type de logiciel (EXTENSION ou COMPAGNON) +#define MAX_TAILLE_VERSION_SOFT 100 // Taille de la chaine de caractères contenant les infos de version logicielle +#define MAX_TAILLE_EEPROM 256 // Taille, en octets, de la zone d'EEPROM utilisée pour stocker des paramètres de configuration. NB : cette dimension a un lien avec le champ 'Eeprom' dans TB_Modules_physiques qui fait 2xMAX_TAILLE_EEPROM pour pouvoir stocker chaque valeur sur 2 caractères alphas codés en hexa. +#define MAX_BOUCLES_CALCUL_CRC 3 // Nombre de boucles max pour vérifier la lecture du CRC de l'application +#define TAILLE_ADRESSE_MAC 6 // 6 octets stockés directement +#define TAILLE_ADRESSE_IP_V4 16 // en caractères, pour stocker sous forme de chaine. Exemple : '123.123.123.123' +#define TAILLE_ADRESSE_IP_V6 40 // en caractères, pour stocker sous forme de chaine. Exemple : '0123:0123:0123:0123:0123:0123:0123:0123' +#define TAILLE_ADRESSE_IP TAILLE_ADRESSE_IP_V6 // Dans le doute... +#define TAILLE_ROM_CODE_OW 8 // Nombre d'octets utilisés par les ROM Codes +#define MAX_TAILLE_CRC 20 // Nombre maxi de caractères pour un CRC +#define MAX_LONGUEUR_TOKEN_SHA256 32 // Le SHA256 est formé de... 256 bits, soit 32 octets. Attention : la chaine correspondante est formée de 65 caractères (32 x 2 car (hexa codé en Ascii) + NULL de fin) + +#define MAX_LONGUEUR_CHAINE_VERSION_CM 100 // Longueur maxi de la chaine représentant la version de Compute Module (renvoyée dans TB_Systeme.Version_cm) +#define MAX_LONGUEUR_NOM_REPERTOIRE_LOCAL 200 // Longueur maxi du nom d'un répertoire local +#define MAX_LONGUEUR_NOM_FICHIER 100 // Longueur maxi, y compris NULL de fin de chaine, d'un nom de fichier (SANS répertoire) +#define MAX_LONGUEUR_NOM_BATEAU 50 // Nomre maxi de caractères du nom de bateau +#define MAX_TAILLE_COMMANDE_SHELL 2000 // Taille maxi d'une commande Shell lancée via 'systeme()' +#define MAX_LONGUEUR_URL 256 // Longueur maxi de l'URL (notament en HTTP). A priori, selon retour de AT+SHCONF?, le max serait 512. +#define MAX_LONGUEUR_REQUETE_HTTP 512 // Longueur maxi de la requête HTTP (notamment GET). Cette longueur est vérifiable par 'AT+SHREQ=?' +#define MAX_TAILLE_FICHIER_UPLOAD 10000000 // En octets. Tout fichier de taille supérieure ne pourra pas être uploadé +#define MAX_TAILLE_BUFFER_UPLOAD 1500 // A priori, le modem limite à 1360 mais c'est quelque chose qui peut évoluer en fonction de la version de firmware, vorie en dynamique. +#define MAX_TAILLE_FICHIER_DOWNLOAD 10000000 // En octets. Tout fichier de taille supérieure ne pourra pas être downloadé +#define MAX_TAILLE_BUFFER_DOWNLOAD 5000 +#define MAX_NOMBRE_FICHIERS_DOWNLOAD 100 // Nombre maxi de fichiers qui pourront être traités (s'il y en a plus, ils seront traités quand les premiers seront effacés). +#define MAX_NOMBRE_PARAMETRES_LISTE_DOWNLOAD 12 // Nombre de paramètres d'une ligne FTPLIST (type commande 'ls -l') +#define MAX_TAILLE_BLOC_DATA_CHARGEMENT_CODE 256 // Taille maximale d'un bloc de data lors du chargement de code (nb : c'est cette quantité qui est émise par défaut. Or, le chargement ne fonctionne qu'avec des multiples de 4 octets. Donc, merci de laisser une valeur multiple de 4...!!!!!) +#define MAX_TAILLE_FICHIER_JSON 10000 // Taille maximale d'un fichier JSON +#define MAX_TAILLE_DESCRIPTION_PACKAGE 4096 // Taille maxi du champ 'Description' d'un fichier JSON de version. +#define MAX_REPETITIONS_CHARGEMENT_CODE 5 // Nb max de tentatives de répétitions lorsque le chargement sur une carte ne fonctionne pas + + + + // Timings : +#define SYSTEME_TEMPO_INIT 0 // En x 10ms +#define SYSTEME_TEMPO_ATTENTE_REINIT_PAR_DEFAUT 60 // En x 100ms Temporisation entre la réception d'une demande de réinit (transmise au Compagnon pour action) et l'arrêt "propre" de l'application. NB : sur le compagnon, l'exécution est temporisé de 8s ; il est donc prudent de caler cette tempo là en-dessous de cette valeur... +#define SYSTEME_TEMPO_PERIODE_CHECK_NEW_DOWNLOAD 3600 // En secondes En pratique, la tempo est ramenée à 1min après chaque connexion réseau. Cette valeur est donc un max si aucune connexion réseau n'est faite... +#define SYSTEME_TEMPO_REPORT_SAUVEGARDE 5 // En secondes Temporisation si une sauvagarde est demandée alors qu'un archivage est déjà en cours (non critique car la test d'archivage en cours sera effectué à la fin de cette tempo et, si besoin, relancée) +#define SYSTEME_TEMPO_ATTENTE_DISPO_C_BUS 10 // En x 10ms NB : tempo non critique car attentes multiples jusqu'à ce que C-Bus soit dispo +#define SYSTEME_TEMPO_LECTURE_ID_CARTES 1 // En x 10ms NB : Tempo non critique, elle permet juste de ne pas faire des tests en permanence en attendant la réception de tous les ID +#define SYSTEME_TIME_OUT_LECTURE_ID_CARTES 10 // En x 100ms SDIR #304 +#define SYSTEME_TIME_OUT_ANNUAIRE_ONE_WIRE 5 // En x 100ms +#define SYSTEME_TEMPO_ATTENTE_CONFIG_CARTES 30 // En x 10ms NB : tempo non critique car permet juste d'éviter de recevoir des commandes UP sur des cartes non configurées (donc générant des retours d'erreur vers le Compagnon). NB : les commandes UP génériques ET avec Haute Priorité passent quand même pendant ce temps... +#define SYSTEME_ATTENTE_DEMARRAGE_COMPTEUR_BOUCLE_PRINCIPALE 5 // en secondes. Défini l'attente avant prise en compte des mesures de nb de boucles principales +#define SYSTEME_ATTENTE_BOUCLE_PRINCIPALE_TEMPO 1000 // en nanosecondes théoriquement... Cf ci-dessous + // Mesures effectuées le 28/04/2018, le 02/09/2020 et le 26/03/2021 sur CM3+ pour ShipHeart : + // Valeur Boucles/secondes Load average @1min (première valeur de HTOP) + // 0 65 000 3.2 - 3.8 (sur 4.0 max !) (==> mise en commentaires du 'delai_nano_sec') + // 1 10 000 - 11000 3.1 - 3.8 + // 2 9 500 - 11 000 2.3 - 2.6 + // 5 9 500 - 10 500 2.3 - 2.6 + // 10 => environ 11 000 boucles/sec. Charge CPU = 40% + // 100 => environ 11 000 boucles/sec. Charge CPU = 40% + // 1 000 => environ 10 500 boucles/sec. Charge CPU = 39% + // 10 000 => environ 9 500 boucles/sec. Charge CPU = 35-38%. Load Average (@1min) = 0.25-0.30 avec pointes à 0.55 + // 100 000 => environ 4 300 boucles/sec. Charge CPU = 29% + // 1 000 000 => environ 800 boucles/sec. Charge CPU = 6% ==> déclenche des erreurs de bus SPI (plusieurs dizaines par jour... d'où ces mesures) + // 10 000 000 => environ 90 boucles/sec. Charge CPU = 1% + + // Nouvelles mesures sur CM4 (4Go) le 23/12/2023 : + // 1 000 => environ 12 000 boucles/sec (très stable !). Charge CPU = 30-35%. Load average 0.33-0.35 avec des pointes à 0.50 + // 10 000 => entre 7000 et 11000 (avg = 9500) boucles/sec (assez instable). Charge CPU = 28-35%. Load average 0.60-0.80 avec des pointes à 0.90 + + + +#define MISE_A_JOUR_TEMPO_INIT 20 // En x 100ms Dans tous les cas, l'init du séquenceur de Mise_à_jour ne se fera que lorsque l'analyse de l'annuaire aura été fait. Cette tempo n'est donc pas critique... +#define MISE_A_JOUR_TIME_OUT_HTTP_GET 600 // En x 100ms Time-out de l'échange HTTP GET + +#define CHARGEMENT_CODE_TEMPO_INIT 2 // En x 100ms // Il n'y a aucune raison d'attendre... +#define CHARGEMENT_CODE_TEMPO_ATTENTE_EFFACEMENT_FLASH 50 // En x 100ms // Durée théorique env. 1.9s voire 3.4s pour les L151 @12MHz +#define CHARGEMENT_CODE_TIME_OUT_ATTENTE_ECRITURE_BLOC 10 // En x 100ms +#define CHARGEMENT_CODE_TEMPO_ATTENTE_INTER_BLOCS_COMPAGNON 1 // En x 100ms +#define CHARGEMENT_CODE_TEMPO_ATTENTE_INTER_BLOCS_K_BUS 3 // En x 100ms // Tests Ok fait avec 200ms sur ShipBS (lent...) mais 2 noeuds sur K-Bus. Par sécurité, la tempo est laissée à 300ms... +#define CHARGEMENT_CODE_TEMPO_ATTENTE_FIN_ACTIVATION 50 // En x 100ms // Durée entre l'envoi des commandes d'activation après chargement et demande de réinitialisation + + + // Erreurs système : +#define ERREUR_SYSTEME_AUCUNE 0 +#define ERREUR_SYSTEME_CONFIG -1 +#define ERREUR_SYSTEME_RESEAU -2 + + + + + // Types de cartes : +#define TYPE_CARTE_INCONNU -1 +#define TYPE_CARTE_SHIPBASE 0 +#define TYPE_CARTE_SP812 1 +#define TYPE_CARTE_SBS 2 +#define TYPE_CARTE_SP4AC 3 +#define TYPE_CARTE_SP8DC 4 + +#define GENERATION_SHIPBASE_INCONNUE 0 +#define GENERATION_SHIPBASE_V1 1 // Entre 2020 et 2022, équipés de CM3+ +#define GENERATION_SHIPBASE_V2 2 // A partir de 2023, équipés de CM4. A noter que la 'hardware_version' reçue du Compagnon doit correspondre à cette évolution. +#define GENERATION_SHIPBASE_V3 3 // Les SHipBase V2, équipés de CM5 (début = 08/2026) + + // Adresses IP : +#define ADRESSE_IP_INCONNUE "inconnue" // Valeur utilisée par défaut, au démarrage de l'application. Attention, la taille doit être inférieure à TAILLE_ADRESSE_IP - 1. + + + // Demandes Système : +#define SYSTEME_SIGNAL_AUCUN 0 // Valeur par défaut. Les valeurs de signaux ont toute une valeur > 0 + + // Valeurs de Mode_validation_maj : +#define MODE_VALIDATION_MAJ_AUCUNE 0 +#define MODE_VALIDATION_MAJ_MANUELLE 1 +#define MODE_VALIDATION_MAJ_AUTOMATIQUE 2 + + // Valeurs de Mode_maj_firmwares : +#define MODE_MAJ_FIRMWARES_NORMAL 0 +#define MODE_MAJ_FIRMWARES_SI_DIFFERENTS 1 +#define MODE_MAJ_FIRMWARES_TOUJOURS 2 + + // Valeurs de Demande_maj_soft : +// #define DEMANDE_MAJ_SOFT_AUCUNE 0 // Valeur de repos +// #define DEMANDE_MAJ_SOFT_VALIDATION 1 // Vlaidation du fichier dans A_VALIDER qui sera copié dans A_CHARGER +// #define DEMANDE_MAJ_SOFT_CHARGEMENT 2 // Idem VALIDATION + demande de reset pour prise en compte immédiate +// #define DEMANDE_MAJ_SOFT_ANNULATION -1 // Suppression du fichier présent dans A_VALIDER ou dans A_CHARGER (les 2 sont vidés) + + // Etat de veille +#define SYSTEME_VEILLE_AUCUNE 0 +#define SYSTEME_VEILLE_LEGERE 1 +#define SYSTEME_VEILLE_PROFONDE 2 + + + // Modes de chargement de code : +#define CHARGEMENT_CODE_MODE_EFFACEMENT_FLASH 0 +#define CHARGEMENT_CODE_MODE_CHARGEMENT_BLOC 1 +#define CHARGEMENT_CODE_MODE_CHECK_CRC 2 +#define CHARGEMENT_CODE_MODE_VALIDATION_VERSION 3 + + + // Etat séquenceur Système : +#define ETAT_SEQUENCEUR_SYSTEME_INIT 0 +#define ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_K_BUS 1 +#define ETAT_SEQUENCEUR_SYSTEME_LECTURE_ID_CARTES 2 +#define ETAT_SEQUENCEUR_SYSTEME_ATTENTE_ANNUAIRE_ONE_WIRE 3 +#define ETAT_SEQUENCEUR_SYSTEME_CHARGEMENT_CONFIG_CARTES 4 +#define ETAT_SEQUENCEUR_SYSTEME_ATTENTE_CONFIG_CARTES 5 +#define ETAT_SEQUENCEUR_SYSTEME_REPOS 10 +#define ETAT_SEQUENCEUR_SYSTEME_DEMARRAGE_IMPOSSIBLE 20 + + + // Etat séquenceur Chargement de Code : +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_INIT 0 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_REPOS 1 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT 2 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_LANCEMENT_CARTE 3 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_EFFACEMENT_FLASH 4 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_CHARGEMENT_BLOC 5 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_ATTENTE_INTER_BLOCS 6 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_VERIFICATION_CRC 7 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CARTE 8 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_CHARGEMENT 9 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_FIN_ACTIVATION 10 +#define ETAT_SEQUENCEUR_CHARGEMENT_CODE_ERREUR 11 + + + + + diff --git a/sources/usb.c b/sources/usb.c new file mode 100644 index 0000000..9646510 --- /dev/null +++ b/sources/usb.c @@ -0,0 +1,238 @@ +// ********************************************************************************************************************************************************************************** +// Fichier USB.C +// +// Contient toutes les fonctions liées aux ports USB +// ********************************************************************************************************************************************************************************** + +// Include : +#include "main.h" + +// Prototypes : +int8_t init_usb(void); +void gestion_usb(void); + + +// Variables globales : +struct Struct_usb usb; + +uint16_t test; + +//********************************************************************************************************************************************************************************** +// Fonction : Gestion des ports USB +// Reçoit : Rien +// Retourne : Rien +//********************************************************************************************************************************************************************************** +void gestion_usb(void) +{ +uint8_t i; +char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; + + + switch(usb.sequenceur.etat) + { + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_USB_INIT : + { + if (usb.sequenceur.compteur == 0) + { + usb.sequenceur.etat = ETAT_SEQUENCEUR_USB_REPOS; + } + + break; + } + + //-------------------------------------------------------- + case ETAT_SEQUENCEUR_USB_REPOS : + { + if (usb.sequenceur.compteur == 0) + { + usb.sequenceur.compteur = USB_PERIODE_SURVEILLANCE; + + //--------------------------------------------- + // Recherche des éventuels défauts : + for (i = 0; i < USB_MAX_NOMBRE_PORTS; i ++) + { + if (usb.ports[i].flag_validite == FALSE) continue; + + if ((usb.ports[i].defaut == USB_ETAT_PORT_OK) && (lecture_entree(TYPE_ENTREES_GPIO, 0, usb.ports[i].no_entree_defaut, TRUE) == ON)) // 0 est l'adresse de ShipBase + { + usb.ports[i].defaut = USB_ETAT_PORT_DEFAUT; + + sprintf(tmp, "Le port USB n° %i vient de passer en défaut", (i+1)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_USB, USB_PASSAGE_DEFAUT, (i+1), 0, NULL); + } + + if ((usb.ports[i].defaut == USB_ETAT_PORT_DEFAUT) && (lecture_entree(TYPE_ENTREES_GPIO, 0, usb.ports[i].no_entree_defaut, TRUE) == OFF)) // 0 est l'adresse de ShipBase + { + usb.ports[i].defaut = USB_ETAT_PORT_OK; + + sprintf(tmp, "Le défaut sur le port USB n° %i vient de disparaitre", (i+1)); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_WARNING); + + enregistrement_historique(TYPE_SOURCE_HISTORIQUE_USB, USB_PASSAGE_OK, (i+1), 0, NULL); + } + } + + //------------------------------------------------ + // Ports avant - Lien avec le circuit de commande: + if (usb.no_circuit_usb_avant != -1) + { + // Etat des ports : + circuits.circuits[usb.no_circuit_usb_avant].etat = (usb.ports[USB_FACE_AVANT_HAUT].defaut << 2) | + (usb.ports[USB_FACE_AVANT_MILIEU].defaut << 1) | + (usb.ports[USB_FACE_AVANT_BAS].defaut); + + // Commande de l'alimentation : + if (circuits.circuits[usb.no_circuit_usb_avant].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante == OFF) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, usb.sortie_gpio_cm_do_usb_avant_pwr_en, OFF); // Coupure de l'alimentation des ports. 0 est l'adresse de ShipBase + } + else ecriture_sortie(TYPE_SORTIE_GPIO, 0, usb.sortie_gpio_cm_do_usb_avant_pwr_en, ON); // Commande de l'alimentation des ports. 0 est l'adresse de ShipBase + } + + //------------------------------------------------ + // Port arrière - Lien avec le circuit de commande: + if ((usb.ports[USB_FACE_ARRIERE].flag_validite) && + (usb.no_circuit_usb_arriere != -1)) + { + // Etat des ports : + circuits.circuits[usb.no_circuit_usb_arriere].etat = usb.ports[USB_FACE_ARRIERE].defaut; + + // Commande de l'alimentation : + if (circuits.circuits[usb.no_circuit_usb_arriere].variables[CIRCUIT_VARIABLE_CONSIGNE].valeur_courante == OFF) + { + ecriture_sortie(TYPE_SORTIE_GPIO, 0, usb.sortie_gpio_cm_do_usb_arriere_pwr_en, OFF); // Coupure de l'alimentation des ports. 0 est l'adresse de ShipBase + } + else ecriture_sortie(TYPE_SORTIE_GPIO, 0, usb.sortie_gpio_cm_do_usb_arriere_pwr_en, ON); // Commande de l'alimentation des ports. 0 est l'adresse de ShipBase + } + } + + break; + } + + + //-------------------------------------------------------- + default : + { + sprintf(tmp, "L'état du séquenceur Usb est inconnu [%i]. Il est repositionné au REPOS", usb.sequenceur.etat); + affichage_debug(__FILE__, __LINE__, tmp, TYPE_MESSAGE_DEBUG_ERREUR); + usb.sequenceur.etat = ETAT_SEQUENCEUR_USB_REPOS; + break; + } + } +} + + +//********************************************************************************************************************************************************************************** +// Fonction : Init des ports USB +// Reçoit : Rien +// Retourne : 0 si aucune erreur, -1 sinon +//********************************************************************************************************************************************************************************** +int8_t init_usb(void) +{ +uint8_t i; + + + if (systeme.generation_shipbase == GENERATION_SHIPBASE_V1) + { + // Init des paramètres de chaque port : + for (i = 0; i < USB_MAX_NOMBRE_PORTS; i ++) + { + switch(i) + { + case USB_FACE_AVANT_HAUT : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM3P_DI_USB3_ALERT_N; // Attention : on a gardé le références du schéma qui sont inversées (ici, c'est le port du HAUT) + break; + } + case USB_FACE_AVANT_MILIEU : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM3P_DI_USB2_ALERT_N; + break; + } + case USB_FACE_AVANT_BAS : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM3P_DI_USB1_ALERT_N; // Attention : on a gardé le références du schéma qui sont inversées (ici, c'est le port du BAS) + break; + } + case USB_FACE_ARRIERE : + { + usb.ports[i].flag_validite = FALSE; + usb.ports[i].no_entree_defaut = 0; + break; + } + default : + { + usb.ports[i].flag_validite = FALSE; + usb.ports[i].no_entree_defaut = 0; + break; + } + } + } + + // Affectation des sorties d'activation des ports : + usb.sortie_gpio_cm_do_usb_avant_pwr_en = CM3P_DO_USB_AVANT_PWR_EN; + usb.sortie_gpio_cm_do_usb_arriere_pwr_en = 0; + } + else // V2 & V3 + { + // Init des paramètres de chaque port : + for (i = 0; i < USB_MAX_NOMBRE_PORTS; i ++) + { + switch(i) + { + case USB_FACE_AVANT_HAUT : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM4_DI_USB_HAUT_ALERT_N; + break; + } + case USB_FACE_AVANT_MILIEU : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM4_DI_USB_MILIEU_ALERT_N; + break; + } + case USB_FACE_AVANT_BAS : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM4_DI_USB_BAS_ALERT_N; + break; + } + case USB_FACE_ARRIERE : + { + usb.ports[i].flag_validite = TRUE; + usb.ports[i].no_entree_defaut = CM4_DI_USB_ARRIERE_ALERT_N; + break; + } + default : + { + usb.ports[i].flag_validite = FALSE; + usb.ports[i].no_entree_defaut = 0; + break; + } + } + } + + // Affectation des sorties d'activation des ports : + usb.sortie_gpio_cm_do_usb_avant_pwr_en = CM4_DO_USB_AVANT_PWR_EN; + usb.sortie_gpio_cm_do_usb_arriere_pwr_en = CM4_DO_USB_ARRIERE_PWR_EN; + } + + // Variables générales : + usb.sequenceur.compteur = USB_TEMPO_INIT; + usb.sequenceur.etat = ETAT_SEQUENCEUR_USB_INIT; + + // Note importante : + // Les valeurs de 'usbb.no_circuit_usb_avant' et 'usb.no_circuit_usb_arriere' ont DEJA été mises à jour dans 'bdd_init.c' dans la partie CIRCUITS. + // Il ne faut donc surtout pas les initialiser (à -1 par exemple) ici...! + + // Affichage du message de débug : + affichage_debug(__FILE__, __LINE__, "Initialisation des ports USB\t: OK", TYPE_MESSAGE_DEBUG_OK); + return(0); +} \ No newline at end of file diff --git a/sources/usb.h b/sources/usb.h new file mode 100644 index 0000000..336e8eb --- /dev/null +++ b/sources/usb.h @@ -0,0 +1,29 @@ +//********************************************************************************************************************************************************************************** +// Fichier USB.H +// +// Contient tous les define liés aux ports USB +//**********************************************************************************************************************************************************************************/ + + + // Maximums : +#define USB_MAX_NOMBRE_PORTS 4 + + + // Timings : +#define USB_TEMPO_INIT 20 // En x 100ms +#define USB_PERIODE_SURVEILLANCE 10 // En x 100ms + + // Affectation des ports : +#define USB_FACE_AVANT_HAUT 0 +#define USB_FACE_AVANT_MILIEU 1 +#define USB_FACE_AVANT_BAS 2 +#define USB_FACE_ARRIERE 3 + + // Etat des ports : +#define USB_ETAT_PORT_OK 0 +#define USB_ETAT_PORT_DEFAUT 1 + + + // Etat séquenceur Usb : +#define ETAT_SEQUENCEUR_USB_INIT 0 +#define ETAT_SEQUENCEUR_USB_REPOS 1 \ No newline at end of file