//********************************************************************************************************************************************************************************** // 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