Files

569 lines
30 KiB
C

//**********************************************************************************************************************************************************************************
// 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);
}