//********************************************************************************************************************************************************************************** // Fichier EVENTS.C // // Contient toutes les fonctions liées à la gestion des évènements //**********************************************************************************************************************************************************************************/ //#define __DEBUG_EVENTS //Décommenter cette ligne pour avoir les messages de debug des évènements // Includes : #include "main.h" // Prototypes : int8_t init_events(void); void gestion_events(void); void gestion_event(uint16_t no_event, uint8_t jour, uint8_t heure, uint8_t minute); bool test_event(uint16_t no_event); int8_t declenchement_event(uint16_t no_event); // Variables globales : struct Struct_events events; /********************************************************************************************************************************************************************************** Fonction INIT_EVENTS Fonction d'initialisation des évènements **********************************************************************************************************************************************************************************/ int8_t init_events(void) { events.sequenceur.etat = ETAT_SEQUENCEUR_EVENTS_INIT; events.sequenceur.compteur = EVENTS_TEMPO_INIT; events.sequenceur.compteur_time_out = 0; events.derniere_minute_traitee = 60; // Valeur volontairement hors limite pour forcer le premier traitement. // Affichage du message de débug : affichage_debug(__FILE__, __LINE__, "Initialisation des évènements\t: OK", TYPE_MESSAGE_DEBUG_OK); return(0); } //================================================================================================================================================================== // Fonction : Appelée en tâche de fond, cette fonction gère les févènements dans leur ensemble // Reçoit : Rien // Renvoit : Rien //================================================================================================================================================================== void gestion_events(void) { uint16_t i; time_t now; struct tm tm_now; uint8_t jour; uint8_t heure; uint8_t minute; bool test_temp; char tmp[MAX_LONGUEUR_BUFFER_TEMPORAIRE_DEBUG]; switch(events.sequenceur.etat) { //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_EVENTS_INIT : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (events.sequenceur.compteur == 0) { events.sequenceur.etat = ETAT_SEQUENCEUR_EVENTS_REPOS; } break; } //---------------------------------------------------------------------------------------------------------------------------------------------------------- case ETAT_SEQUENCEUR_EVENTS_REPOS : //---------------------------------------------------------------------------------------------------------------------------------------------------------- { if (events.sequenceur.compteur == 0) { events.sequenceur.compteur = EVENTS_PERIODE_CHECK; // A priori, toutes les 100ms //===================================================================== // Préparation des conditions horaires : jour = 0; // Permet de savoir, in fine, si les conditions horaire sont applicables (>0) 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); }