Skip to content

Format & gestion d'erreurs

Eliot edited this page Apr 10, 2024 · 3 revisions

1. Sources d'erreurs/exceptions

Pendant que le bateau est en marche, il y a deux types d'erreurs possibles:

  • Perte de connection avec un capteur ou données corrompues: StreamReader
  • Données dépassant certaines limites (ex. températures trop élevées): DataConsumer/checks

2. Réaction aux erreurs

Dépendamment de l'erreur, on pourrait vouloir:

  • Envoyer des alertes au pilote
  • Entamer la procédure d'arrêt (et/ou un changement d'état du bateau)

3. Gestion des erreurs à partir du StreamReader

  • Quand le capteur devient déconnecté, la méthode run du StreamReader ajoutera None à la queue de log et enverra un objet de type ExceptionMessage (détails plus bas) avec le numéro du message.
  • Quand le capteur reçoit des données invalides ou corrompues 3 fois d'affilée, il entrera en mode "déconnecté" et ajoutera les même données qui viennent d'être décrites aux queues.
  • En cas d'un autre type d'erreur, les classes dérivées de StreamReader pourront lever une erreur de type SensorException qui permettra en même temps d'envoyer le type d'exception.

Il ne devrait pas y avoir de vérification d'erreur dans la classe StreamReader à part pour vérifier si le capteur est toujours connecté.

4. Gestion des erreurs à partir du DataConsumer

Quand le DataConsumer lit les données, il y a alors deux possibilités:

  1. La donnée est de type ExceptionMessage, dans ce cas il faut envoyer le message à l'interface et démarrer la procédure d'arrêt au besoin.
  2. On a reçu une donnée. Il faut alors faire la vérification appropriée dans checks. Dépendamment du résultat, on pourrait avoir à envoyer un message à l'interface.

5. Lecture des erreurs du côté de l'interface

Par défaut, l'interface n'aura pas de valeur pour certains capteurs. On peut alors considérer qu'ils ne sont pas connectés. Si la donnée actuelle pour un capteur est None plus tard dans l'exécution, alors on peut considérer que le capteur est déconnecté. Ne pas oublier que les capteurs doivent envoyer None dans les deux queues, incluant celle qui se rend à l'interface.

De plus, l'interface devra implémenter une méthode dispatch_message(msg: u16) (u16 veut dire entier non-signé sur 16 bits) qui pourra être appelée de manière non-synchronisée, c'est-à-dire qu'elle doit gérer les lock pour ses attributs, car elle pourrait être appelée par plusieurs threads en même temps. La classe de l'interface devra aussi contenir un dictionnaire avec le message à afficher pour chaque zone de l'interface (pas mal associée à un périphérique) ou une zone globale pour les périphériques qui ne sont pas affichés. (Note: ça serait bien d'avoir cette zone aussi, comme ça on peut connaitre le status des procédures de démarrage et d'arrêt). Si un message avec une plus haute priorité que le message en cours (ou si le message en cours est None) pour un périphérique donné, alors on remplace l'ancien message.

Chaque message sera aussi doté d'un minuteur, car certains messages pourraient seulement être affichés pour une certaine durée. C'est encore une foit à l'interface d'implémenter ça et de toujours mettre à jours ces valeurs. À noter que certains messages pourraient avoir une durée négative. Dans ce cas, il sont affichés indéfiniments (à moins d'être remplacés par des messages avec une plus haute priorité).

6. Codes et format des messages d'erreur

Tous les messages d'erreurs sont contenus dans des entiers à 16 bits. Les 8 bits les moins significatifs représentent la source, un plus petit nombre indiquant une priorité plus élevée. Les 8 bits plus significatifs contiendront le type de message et les messages moins prioritaires auront encore une fois un numéro plus petit.

Les codes pour les différents périphériques sont les suivants:

FUELCELLS                        = 0x00
FUELCELL_A                       = 0x01
FUELCELL_B                       = 0x02
DCDC_CONVERTER                   = 0x04

TEMPERATURES                     = 0x10 # les temperatures des 5 capteurs de temperature
TEMPERATURE_BATTERY_12V          = 0x11
TEMPERATURE_BATTERY_24V          = 0x12
TEMPERATURE_H2_PLATE             = 0x13
TEMPERATURE_H2_TANKS             = 0x14
TEMPERATURE_FUELCELL_CONTROLLERS = 0x15

BATTERY_GAUGES                   = 0x20
I2C_MULTIPLEX                    = 0x21
BATTERY_GAUGE_12V                = 0x22
BATTERY_GAUGE_24V                = 0x23

MANOMETERS                       = 0x40
MANOMETER_0                      = 0x41
MANOMETER_1                      = 0x42

START_BUTTON                     = 0x50
PRECHARGE                        = 0x51
ACTUATORS                        = 0x58
ACTUATOR_1                       = 0x59
ACTUATOR_2                       = 0x5A

ARDUINO                          = 0x60
INTERFACE                        = 0x61
CPU_TEMPERATURE                  = 0x62

TELEMETRY_SENSORS                = 0x70
IMU                              = 0x71
GYROSCOPE                        = 0x73
COMPASS                          = 0x75
ACCELEROMETER                    = 0x77
GPS                              = 0x78

Des périphériques similaires ont des numéros différents pour les différencier, mais ça ne devrait pas trop affecter la priorité (qui ne sera de toute façon pas très utile). Aussi, il y a des trous et des périphériques supplémentaires/regroupements qu'on n'utilisera surement pas pour permettre des modifications dans le futur sans briser ce qu'on aura. Noter que ces valeurs sont contenues dans les 8 bits moins significatifs des messages d'erreur.

Ensuite, les codes suivants seront dépendents des capteurs, mais numéros resteront globaux ou certains intervalles seront réservés (ex. warnings entre 0x20 et 0xA0, comme ca l'interface connait le niveau de sévérité du message et peut changer ça couleur en fonction):

CRITICAL_ERROR_EXIT = 0x00 # Le bateau demarre le protocole d'arret et le pilote doit imperativement evacuer
CRITICAL_ERROR = 0x01 # Le bateau demarre le protocole d'arret, mais le pilote peut rester sur le bateau

CONNECTED  = 0x10 # le peripherique s'est connecte (permet d'effacer le message de deconnection)
DISCONNECTED = 0x11 # le peripherique est deconnecte (moins important que l'arret complet, car on ne veut jamais effacer le message d'arret)

WARNING = 0x20 # les messages entre 0x20 et 0xA0 sont des avertissement qui peuvent etre definis par peripherique

INFO = 0xA0 # les message avec un identifiant plus grand que 0xA0 sont considérés comme des informations générales au besoin

Il sera possible pour les capteurs de définir des messages avec des numéros différents de ces derniers bien sûr! Par contres, ces numéros de base serviront à générer certains numéros de messsage automatiquement au besoin.

Tous codes de messages les plus communs seront définis dans un fichier de configuration, par exemple:

FUELCELL_A_CRITICAL_ERROR_EXIT = 0x0000
FUELCELL_A_CRITICAL_ERROR = 0x0100
FUELCELL_A_DISCONNECTED = 0x1000
#...
GPS_WARNING_POOR_CONNECTION = 0x2320

Les numéros sont fictifs, mais sont là pour donner une idée générale. Bien sur, l'idée serait d'avoir des messages les plus précis possibles.

Finalement un dictionnaire accessible par l'interface servira à donner plus d'informations sur les messages qui sont reçus:

MESSAGES_CONFIG = {
    FUELCELL_A_CRITICAL_ERROR_EXIT: ("EXIT IMMEDIATLY: Fuel cell A critical error", -1.0),
    FUELCELL_A_CRITICAL_ERROR: ("Fuel cell A critical error", -1.0),
    FUELCELL_A_CONNECTED: ("", 0.0),
    FUELCELL_A_DISCONNECTED: ("WARNING: Fuel cell A disconnected", -1.0),
    # ...
    GPS_WARNING_POOR_CONNECTION: ("Poor GPS connection; no data is being received", CONFIG["GPS"]["read_interval"] + 1),
}

Il faudrait potentiellement aussi avoir des messages par défaut au cas ou un type de message n'est pas dans ce dictionaire. Si l'interface doit afficher ce genre de message, il devra aussi être accompagné du numéro d'erreur pour qu'on puisse ajouter cette erreur à la liste dans le futur.

Note: le mot "périphérique" réfère ici à un capteur ou un actuateur dans le sens général (ex. capteur de température, fuel cells, actuateur pour valve, etc.)