Problèmes communs¶
Avertissement
Faites attention au code que vous prenez comme référence sur cette page ! Certains codes sont volontairement bogués afin de montrer les erreurs potentielles faciles à commettre.
Exceptions¶
Les exceptions sont des événements qui se produisent au cours de l’exécution d’un programme, perturbant le flux normal des instructions, utilisées dans les événements d’erreur ou les problèmes qui surviennent au cours de l’exécution. Une exception peut être rattrapée pour éviter la propagation, sinon toute exception qui n’est pas gérée entraînera l’arrêt immédiat du déroulement du programme.
Les exceptions les plus courantes sont les suivantes
NullPointerException
Il se produit lorsqu’on essaie d’appeler une méthode ou d’obtenir une propriété d’un objet à partir d’une variable ayant une valeur nulle, ce qui signifie essentiellement que la variable ne contient pas encore de valeur ou que la valeur n’existe pas.
Cette exception est l’une des plus courantes en FTC®, voici un exemple qui lance une NullPointerException :
public class CrashyOpMode extends OpMode { // This call to the "get" method here will throw a NullPointerException. // // The value of the "hardwareMap" variable is null at this point, due to // the way the SDK is limited to define the value of this variable, its // value is defined right before the init() (or runOpMode() in LinearOpModes) // method is called. Servo clawServo = hardwareMap.get(Servo.class, "claw"); @Override public void init() { // This statement won't ever be reached due to the // thrown NullPointerException, explained above, // since it happens before the OpMode starts execution. clawServo.setPosition(0.5); } // ... }
Ceci peut être corrigé en déplaçant la définition de la valeur de la variable « Servo » dans la méthode init (ou
runOpMode()dans les LinearOpModes) comme suit :
public class WorkingOpMode extends OpMode { Servo clawServo = null; @Override public void init() { // This won't throw a NullPointerException since the value of the // "hardwareMap" variable is defined at this point, but note that // the "get" method will return null if the name "claw" isn't // configured. (consult the "Using the SDK" section) clawServo = hardwareMap.get(Servo.class, "claw"); // This statement should be reached and executed now. // // Note that if the "claw" servo is not configured, the value returned // by the hardwareMap will be null as explained before, therefore, // a NullPointerException would be thrown here if that happens. clawServo.setPosition(0.5); } // ... }
TargetPositionNotSetException
Ce type d’exception est une exception personnalisée du SDK. Il signifie que vous avez changé le
RunModedu moteur en RUN_TO_POSITION avant de définir une position cible :
// This will throw a "TargetPositionNotSetException" here! motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION); // And this statement won't be reached. motor.setTargetPosition(1120);
Pour y remédier, il suffit d’intervertir l’ordre des instructions : définir d’abord la position de la cible, puis modifier le
RunMode:
// Setting the target position first motor.setTargetPosition(1120); // Then switching the RunMode motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);
Exception arithmétique
Se produit lors de l’exécution d’opérations arithmétiques illégales telles que la division par zéro :
int number = 128 / 0; // This will throw an ArithmeticException!
Elle peut être gérée en entourant le code susceptible de lancer ce type d’exception d’un « bloc de capture d’essai » <https://www.w3schools.com/java/java_try_catch.asp>`_ :
int number; // Declaring the variable in the outside scope try { // Giving it a value that will possibly throw an ArithmeticException number = 128 / 0; } catch (ArithmeticException e) { // Do something when the ArithmeticException happens. // (The value of the "number" variable will remain 0) }
Exception interrompue
Cela signifie que le SDK a demandé à l’OpMode de s’arrêter, et c’est considéré comme faisant partie du fonctionnement normal. Une interruption signifie qu’il a été demandé au thread en cours de se terminer, alors ne paniquez pas lorsque vous voyez un spam de ce type dans logcat !
Si vous appelez une méthode qui peut lancer une exception InterruptedException (comme
Thread.sleep()), elle doit être traitée comme ceci, avec la syntaxe try catch mentionnée plus haut :try { // Block for 500 milliseconds Thread.sleep(500); } catch(InterruptedException e) { // Tells the current thread (OpMode) to // end the execution as soon as possible Thread.currentThread().interrupt(); }
Notez que LinearOpMode contient déjà une méthode raccourcie sleep() qui fait cela sous le capot. (Et vous ne devriez pas utiliser les sleeps en OpMode puisqu’ils sont plus strictement contrôlés. Lisez les sections suivantes pour plus d’informations)
Comment le SDK gère les exceptions¶
À l’exception des exceptions interrompues et de certains autres cas spéciaux internes, qui entraînent simplement la fin du mode Op, le SDK FTC exécute une routine d“« arrêt d’urgence » lorsqu’une exception est déclenchée et n’est pas traitée correctement. Cette routine arrête le mode OpMode et affiche l’intégralité de la trace de la pile à l’écran. La trace de la pile peut également être visualisée via Logcat lorsque vous utilisez Android Studio.
Note
Avant le SDK 8.0, seule la première ligne de l’erreur s’affichait et il fallait sélectionner « Redémarrer le robot » dans le menu avant d’exécuter à nouveau un mode Op.
Il est généralement conseillé de déboguer tous les OpModes de manière approfondie avant tout match officiel, car ces exceptions sont perturbantes.
Coincé dans le démarrage, boucle, arrêt …¶
Les OpModes sont des programmes strictement contrôlés, dans le sens où le SDK exige qu’ils se déroulent d’une certaine manière avec les méthodes init(), loop(), etc. Si vous prenez plus d’un certain temps (5 secondes, ou 900 millisecondes dans les commandes d’arrêt) à exécuter une action dans l’une de ces méthodes, le SDK exécutera la routine d“« arrêt d’urgence » expliquée précédemment, avec le message d’erreur « stuck in action » (bloqué dans l’action).
public class StuckyOpMode extends OpMode {
// ...
@Override
public void loop() {
// Don't do this in a normal iterative OpMode!
// This will cause a "stuck in stop" error after
// 5 seconds, since iterative OpModes shouldn't
// be blocked by loops or any lengthy operation.
while(true) {
// ...
}
}
}
Si vous devez exécuter une action de longue durée dans votre OpMode, une autre option serait d’utiliser un LinearOpMode à la place.
Les LinearOpModes sont moins stricts puisque leur unique méthode runOpMode() peut circuler plus librement, mais ils ont toujours besoin d’être coopératifs pour arrêter les requêtes. Prenons l’exemple du code suivant :
public class StuckyLinearOpMode extends LinearOpMode {
@Override
public void runOpMode() {
// Wait for the driver to press PLAY on the DS
waitForStart();
while(true) {
// Do stuff infinitely
}
}
}
Ce code n’est pas coopératif avec les demandes d’arrêt, puisque la boucle while n’a pas de condition de sortie pour coopérer avec l’arrêt de l’OpMode, par conséquent, ce code provoquera une erreur « coincé dans l’arrêt » une fois qu’il sera arrêté dans la Driver Station.
Pour coopérer avec l’arrêt de l’OpMode, une condition opModeIsActive() ou !isStopRequested() doit être ajoutée à toutes les boucles bloquantes exécutées dans la méthode ``runOpMode()`. Consultez la page Mode LinearOp vs Mode Op pour plus d’informations sur ces méthodes.
Voici un exemple de mode de coopération LinearOpMode :
public class CooperativeLinearOpMode extends LinearOpMode {
@Override
public void runOpMode() {
while(someCondition && !isStopRequested()) {
// Do something while the "someOtherCondition"
// is true and the OpMode is not stopped.
}
// Wait for the driver to press PLAY on the DS
waitForStart();
while(someOtherCondition && opModeIsActive()) {
// Do something while the "someCondition" is true
// and the OpMode is running (started and not stopped).
}
}
}