Files

479 lines
23 KiB
C

//**********************************************************************************************************************************************************************************
// 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<circuits.nb; j++)
{
if (circuits.circuits[j].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[j].adresse_module != i ) continue; // ... et ceux piloté par cette carte...
if (circuits.circuits[j].no_circuit >= 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<MAX_NOMBRE_CIRCUITS; i++)
{
circuits.circuits[i].sequenceur.etat = ETAT_SEQUENCEUR_CIRCUIT_INIT;
circuits.circuits[i].flag_dde_maj_bdd = FALSE;
}
// Affichage du message de débug :
affichage_debug(__FILE__, __LINE__, "Initialisation des circuits\t\t: OK", TYPE_MESSAGE_DEBUG_OK);
return(0);
}
//**********************************************************************************************************************************************************************************
// Fonction : Recherche le circuit dont la Clé_primaire est reçue en paramètre
// Reçoit : La Cle_primaire du circuit à rechercher
// Retourne : le numéro de circuit correspondant (ou -1 si aucun circuit valide ne correspond). La numérotation allant de 0 à MAX_NOMBRE_CIRCUITS - 1.
//**********************************************************************************************************************************************************************************
int16_t circuit_cle_primaire(uint16_t cle_primaire)
{
uint16_t i;
for (i = 0; i < circuits.nb; i ++)
{
if ((circuits.circuits[i].cle_primaire == cle_primaire) && (circuits.circuits[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_CIRCUIT_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_CIRCUITS-1.
//**********************************************************************************************************************************************************************************
int16_t variable_circuit_cle_primaire(uint16_t cle_primaire)
{
uint16_t i;
for (i=0; i<MAX_NOMBRE_VARIABLES_CIRCUITS; i++)
{
if (circuits.variables[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é'.
}