FRCプログラムが動く仕組み-Part1

2020年3月25日

なぜかRobot.Javaに動作書いてるけど、なんで動くのかわからない!とか実際どうやって動いてるの!?という人のために。
飽きたら読むのやめても大丈夫です。プログラムする際に役立つことはあまりありません。

動作に重要でないものは省いて、// ○○って感じで書きます。
本当の意味でのコメントは/* ○○ */って感じにします。

HALとかは実装が見えない&解説もないので想像です。

Robot.JavaでRobotがTimed Robotを継承している前提で話します。

public final class Main {
  private Main() {
  }

  public static void main(String... args) {
    RobotBase.startRobot(Robot::new);
  }
}

まずおなじみmainメソッド。ここでこちらが作ったRobotのインスタンスを代入。

 public static  void startRobot(Supplier robotSupplier) {
// HAL(おそらくroboRIO上で動くクラス)の初期化
// カメラ初期化
// 使用状況のリポート
if (HAL.hasMain()) {
      /* HALのMainとは別スレッドでロボットを動かす */
      Thread thread = new Thread(() -> {
        runRobot(robotSupplier);
        HAL.exitMain();
      }, "robot main"); 
      thread.start();
      /* おそらくここでもエラーが起きないと抜けないループ処理がある */
      HAL.runMain();
      // 警告
      // 抜けたときのRobotの状態をゲット
      if (robot != null) {
	// ロボットの終了(通知するだけみたい)
      }
      // ロボットの終了を最大1秒待つ
    } else {
      runRobot(robotSupplier);
    }
    // コード終了
  }

HALと別スレッドなのは、監視役と動作役って分けるためだと思う。

続いてrunRobot()で、

private static  void runRobot(Supplier robotSupplier) {
    /* ロボットを開始させるとこれがDriverStationのコンソールに流れてくるよね */
    System.out.println("********** Robot program starting **********"); 

    try {
      // ロボットのインスタンスをもらう
    } catch (Throwable throwable) {
      // 原因の特定
      // エラーメッセージ
      /* このスレッド終了 */
      return; 
    }

    // ファイルにJavaとかWPIの情報書き込んでる?

    try {
      /** 
       * 自分たちの作ったRobotクラスのstartCompetition()を呼び出す。
       * ここでロボットがループする 
       */
      robot.startCompetition(); 
    } catch (Throwable throwable) {
      // 原因特定
      /* 見たことあるエラーメッセージかもしれない */
      DriverStation.reportError("Unhandled exception: " + throwable.toString(),
          throwable.getStackTrace()); 
    } finally {
      if (!suppressExitWarning) {
        /* startCompetitionは例外が起きない限り抜けることはない(ここの処理はされない) */
        /* 見慣れたエラーメッセージ */
     DriverStation.reportWarning("Robots should not quit, but yours did!", false);
          if (errorOnExit) {
            DriverStation.reportError(
                "The startCompetition() method (or methods called by it) should have "
                    + "handled the exception above.", false);
          } else {
            DriverStation.reportError("Unexpected return from startCompetition() method.", false);
          }
       }
    }
  }

最初のものはソフト側のミスでエラーが起きることはないと思う。主にハード、現実世界での問題が多い。
最後のはソフトがほとんど。
Robotの(ほんとはTimedRobotの)startCompetition()が呼ばれた。あともう少し。

public void startCompetition() {
    /** 
     * おなじみrobotInit()くん。
     * Robot内ではフィールドの生成を除いて何よりも先に実行される。 
     */
    robotInit(); 

    // シミュレーション時の初期化

    // DriverStationにEnableが可能であることを知らせる

    /* 永遠にループして、モードに応じた関数を呼び出す */
    while (true) {
      /* updateAlarm()で設定した時間まで待つ */
      long curTime = NotifierJNI.waitForNotifierAlarm(m_notifier);
      // 待ち時間0で再開したら終了(バグった時?)

      /* 今いるループが終わるべき時間を計算。m_periodは既定の周期(0.02秒) */
      m_expirationTime += m_period;
      /* アラームする時間(expirationTime)をアップデート */
      updateAlarm();

      /* ループされる関数ここで主な処理がある(この関数内でループするわけではない)*/
      loopFunc(); 
    }
  }

ここを見るに、0.02秒以内にloopFunc()が終われば待つし、超過してもエラーは出ず(※)、次のループの時間が削られるだけっぽい。
※ 超過してたらcurTimeが0になってループを抜けるかもしれないけど、コードがjavaで実装されておらず、調べても説明しかなくてわからない…
時間がどのように制御されているのかわかりましたね。ちなみにこれはTimedRobotの関数なので他のクラスを使った場合は挙動が違うかもしれません。

次でどのように処理がされているのかが明かされます!

量も多くなってきたので、いったんこのPartは終了します!
Part2へ続く。

まとめ

ロボットプログラムのフローチャート
図にするとこんな感じ。矢印としては適切じゃないところもあるけど、今回たどった関数に忠実に。
あと、ホントは縦にしたほうが良かったんだけど、loopFunc()の方はPart切ってたの忘れてた…
loopFunc()では縦になってることでしょう。