Problemas Comunes#

Advertencia

¡Tenga cuidado con el código que toma como referencia de esta página! Parte de él es intencionadamente defectuoso para demostrar posibles errores fáciles de cometer.

Excepciones#

Las excepciones son eventos que ocurren durante la ejecución de un programa, interrumpiendo el flujo normal de instrucciones, utilizadas en eventos de error o problemas que surgen durante el tiempo de ejecución. Una excepción puede ser atrapada para evitar su propagación, de lo contrario cualquier excepción que no sea manejada causará que el flujo del programa se detenga inmediatamente.

Algunos tipos comunes de excepciones son:

  • Excepción de puntero nulo/NullPointerException

    • Ocurre cuando se intenta llamar a un método u obtener una propiedad de un objeto de una variable con un valor nulo, lo que básicamente significa que la variable no contiene un valor todavía, o el valor no existe.

    • Esta excepción es una de las más comunes en FTC ®, a continuación se muestra un ejemplo que lanza una 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);
        }
    
        // ...
    
    }
    
    • Esto se puede arreglar moviendo la definición del valor de la variable «Servo» al método init (o runOpMode() en LinearOpModes) de la siguiente manera:

    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);
        }
    
        // ...
    
    }
    
  • Excepción de Posición del Objetivo No Establecido/ TargetPositionNotSetException

    • Este tipo de excepción es uno personalizado del SDK. Significa que has cambiado el RunMode del motor a RUN_TO_POSITION antes de establecer una posición como objetivo:

    // This will throw a "TargetPositionNotSetException" here!
    motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);
    
    // And this statement won't be reached.
    motor.setTargetPosition(1120);
    
    • Se soluciona simplemente cambiando el orden de las oraciones; fijando primero la posición objetivo y luego cambiando el RunMode:

    // Setting the target position first
    motor.setTargetPosition(1120);
    
    // Then switching the RunMode
    motor.setRunMode(DcMotor.RunMode.RUN_TO_POSITION);
    
  • ExcepciónAritmética/ ArithmeticException

    • Ocurre al realizar cualquier operación aritmética ilegal, como dividir por cero:

    int number = 128 / 0; // This will throw an ArithmeticException!
    
    • Puede gestionarse encerrando el código susceptible a lanzar este tipo de excepción con un bloque try catch:

    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)
    }
    
  • ExcepciónDeInterrupción/ InterruptedException

    • Significa que el SDK solicitó al OpMode que se detuviera, y se considera parte de la operación normal. ¡Una interrupción significa que el hilo actual ha sido solicitado para terminar, así que no te asustes cuando veas un spam de esos en logcat!

    • Si llamas a un método que posiblemente lanza una InterruptedException (como Thread.sleep()) debería ser manejado así, con la sintaxis try catch mencionada antes:

      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();
      }
      
    • Tenga en cuenta que LinearOpMode ya contiene un método abreviado sleep() que hace esto bajo el capó. (Y no deberías usar sleeps en OpMode ya que están más estrictamente controlados. Lee las siguientes secciones para más información)

Cómo gestiona el SDK las excepciones#

Excepto para InterruptedExceptions y algunos otros casos especiales internos, que simplemente hacen que el OpMode termine, el FTC SDK realiza una rutina de «parada de emergencia» cuando se lanza una excepción y no se maneja correctamente. Esto detiene el OpMode y muestra el stacktrace completo en pantalla. El stacktrace también se puede ver a través de Logcat cuando se utiliza Android Studio.

Nota

Antes del SDK 8.0, sólo se mostraba la primera línea del error y era necesario seleccionar «Reiniciar Robot» en el menú antes de volver a ejecutar un OpMode.

Por lo general, es una buena idea debugear/depurar ampliamente todos los OpModes antes de cualquier partido oficial, ya que estas excepciones son perjudiciales.

Atascado en start, loop, stop…#

Los OpModes son programas estrictamente controlados, en el sentido de que el SDK requiere que fluyan de una determinada manera con los métodos init(), loop(), etc. Si tardas más de un tiempo específico (5 segundos, o 900 milisegundos en comandos de parada) ejecutando una acción en cualquiera de estos métodos, el SDK realizará la rutina de «parada de emergencia» explicada antes, con el mensaje de error «atascado en acción».

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 necesita ejecutar algún tipo de acción larga en su OpMode, otra opción sería utilizar un LinearOpMode en su lugar.

Los LinearOpModes son menos estrictos ya que su único método runOpMode() puede fluir más libremente, pero aún necesitan ser cooperativos para detener peticiones. Tomemos el siguiente código como ejemplo:

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
        }
    }

}

Este código no coopera con las peticiones de parada, ya que el bucle while no tiene una condición de salida para cooperar con la parada del OpMode, por lo tanto, este código causará un error de «atascado en parada» una vez que se detenga en la estación del conductor.

Para cooperar con la parada del OpMode, se requiere añadir una condición opModeIsActive() o !isStopRequested() a todos los bucles de bloqueo ejecutados en el método runOpMode(). Consulta la página LinearOpMode vs OpMode para más información sobre estos métodos.

Un ejemplo de LinearOpMode cooperativo sería el siguiente:

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).
       }
   }

}