Files

416 lines
18 KiB
C

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