#ifndef RUN_INTERFACE_H
#define RUN_INTERFACE_H

/*!
  \file
  \brief 走行制御コマンドのインターフェース定義

  \author Satofumi KAMIMURA

  $Id: runInterface.h 439 2009-01-03 05:01:55Z satofumi $
*/

#include "objCoordinateCtrl.h"
#include "coordinateTypes.h"


/*!
  \brief 走行制御コマンドのインターフェース定義
*/
class RunInterface {
public:
  virtual ~RunInterface(void) {}

  /*!
    \brief エラー状態を示す文字列を返す
  */
  virtual const char* what(void) = 0;

  /*!
    \brief ポート指定の接続を行う

    通信に使用するポートをプログラム中で指定する場合に用いる

    \param device [i] 接続デバイス名
    \param baudrate [i] 接続ボーレート
    
    \retval 0 正常終了
    \retval 戻り値 < 0 エラー

    使用例
    \code
    RunCtrl run;
    if (run.connect("/dev/ttyS0") < 0) {
      printf("RunCtrl::connect: %s\n", run.what());
      exit(1);
    }
    \endcode
  */
  virtual int connect(const char* device, long baudrate) = 0;

  /*!
    \brief 引数の内容を評価して接続を行う

    引数は、argv[1] から評価される

    プログラム引数から通信に使用するポートを決定する場合に用いる

    \param argc [i] 引数の個数
    \param argv [i] 引数文字列を格納した配列

    使用例
    \code
    include <runCtrl.h>

    int main(int argc, char *argv[]) {
      try {
        RunCtrl run;
	if (run.connect(argc, argv) < 0) {
	  printf("RunCtrl::connect: %s\n", run.what());
	  exit(1);
	}
	...

      } chatch (std::exception& e) {
        printf("exception: %s\n", e.what());
      }
      return 0;
    }
    \endcode
  */
  virtual int connect(int argc, char *argv[]) = 0;

  /*!
    \brief ポートとの接続を切断する

    ポートとの接続を切断する

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);
    ...
    run.disconnect();
    \endcode
  */
  virtual void disconnect(void) = 0;

  /*!
    \brief 走行状態の待避

    走行パラメータの値、及び直前発行コマンドを再発行するための情報を待避する
  */
  virtual void push_runState(void) = 0;

  /*!
    \brief 走行状態の復帰

    待避しておいた走行状態を復帰する。これにより、現在の走行パラメータ設定、及び直前発行コマンドを再発行するための情報が待避しておいたものに置き換わる
  */
  virtual void pop_runState(void) = 0;

  /*!
    \brief モジュール内座標系における推定自己位置を返す

    ロボットの推定自己位置を返す。ここで返される値は、座標系の設定に依存しない

    \return ロボットの推定自己位置

    \image html crd_position.png 走行モジュールが管理しているロボット位置
  */
  virtual VXV::Position3D getLocalPosition(void) = 0;

  /*!
    \brief 推定自己位置を返す

    指定座標系からみたロボット位置を返す

    \param crd [i] 座標系
    
    \return 指定座標系におけるロボットの推定自己位置

    \image html crd_position.png 指定座標系におけるロボット位置

    仕様例
    \code
    #include <runCtrl.h>
    #include <typePrint.h>
    
    int main(int argc, argv) {
      RunCtrl run;
      run.connect(argc, argv);

      std::cout << run.getRunPosition() << std::endl;
      ...
    \endcode
  */
  virtual VXV::Position getRunPosition(const CoordinateCtrl* crd) = 0;

  /*!
    \brief 推定自己位置の更新
    
    ロボット位置を指定座標系の指定位置にあるように変更する

    \param position [i] 修正後の位置
    \param crd [i] 座標系

    \image html new_position.png ロボットの管理位置を修正
  */
  virtual void adjustRunPosition(const VXV::Position& position,
				 const CoordinateCtrl* crd) = 0;

  /*!
    \brief 座標系変更の影響を設定

    座標系の変更が、移動中のコマンドに影響を与えるかどうかを設定する。

    \param on [i] 座標系の変更が走行中の経路に影響を与える場合 true

    使用例
    \code
    run.followLine(VXV::Position(0, 0, deg(0)));
    VXV::Delay(1000);

    // ロボット位置を (0, 0, 90[deg]) という風に再設定する
    run.adjustRunPosition(VXV::Position(0, 0, deg(90)));

    // 以前の評価が run.coordinateUpdateDetect(false) であれば、
    // ロボットの移動経路は変更されない

    // 以前の評価が run.coordinateUpdateDetect(true) であれば、
    // ロボットの移動経路は新しい座標系の (0, 0, deg(0)) の直線に
    // 追従するように更新される
    \endcode

    \image html crd_update_detect.png 設定による走行経路の違い
  */
  virtual void coordinateUpdateDetect(bool on) = 0;

  /*!
    \brief サーボ制御

    サーボ状態を設定する

    \param on [i] サーボ状態にするとき true / サーボフリーにするとき false

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);
    ...

    run.servoCtrl(true);	// サーボをかける
    ...

    run.servoCtrl(false);	// サーボフリーにする
    \endcode
  */
  virtual void servoCtrl(bool on) = 0;

  /*!
    \brief 移動停止
  
    標準加速度で速度 0 まで減速する

    \image html move_stop.png コマンド発行後、しばらくして停止

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // ３秒だけ前進して、停止
    run.followLine(VXV::Position(0, 0, deg(0)), &run.FS);
    VXV::Delay(5000);
    run.stop();
    \endcode
  */
  virtual void stop(void) = 0;

  /*!
    \brief 直線追従

    座標系の指定された点を通る指定角度の直線へ追従走行する
    
    \image html move_followLine.png 指定直線経路への追従

    \param position [i] 追従直線を指定
    \param crd [i] 座標系

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 座標系の点 (0, 0) を通り、45度の向きの直線経路に対して追従走行
    run.followLine(VXV::Position(0, 0, deg(45)));
    \endcode
  */
  virtual void followLine(const VXV::Position& position,
			  const CoordinateCtrl* crd) = 0;

  /*!
    \brief 円弧追従

    座標系の指定された点を中心とする半径の円へ追従走行する

    \image html move_followCircle.png 指定円弧経路への追従

    \param center [i] 追従円弧の中心位置
    \param radius [i] 追従円の半径 [mm] (radius > 0 で CW, radius < 0 で CCW)
    \param crd [i] 座標系

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 中心が (500, 500) で半径 500 [mm] の円の軌跡に時計回りで追従走行
    run.followLine(VXV::Grid(500, 500), 500);
    \endcode
  */
  virtual void followCircle(const VXV::Grid& center, int radius,
			    const CoordinateCtrl* crd) = 0;

  /*!
    \brief 接線指定の円弧に追従

    指定直線に接する半径 r の円弧に追従

    \image html move_followCircleOnTangent.png 指定円弧経路への追従

    \param position [i] 接点を指定
    \param radius [i] 追従円の半径 (radius > 0 で CW, radius < 0 で CCW)
    \param crd [i] 座標系

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 座標系 (1000, 0) の点に接する円の軌跡に時計回りで追従走行
    // (結果的に、円の中心は (1000, -500) となる)
    run.followCircleOnTangent(VXV::Position(1000, 0, deg(0)), 1000);
    \endcode
  */
  virtual void followCircleOnTangent(const VXV::Position& position, int radius,
				     const CoordinateCtrl* crd) = 0;

  /*!
    \brief 停止直線の指定

    指定直線に垂直な直線上で停止

    \image html move_stopToLine.png 指定直線に垂直な直線上で停止

    \param position [i] 指定直線を指定
    \param crd [i] 座標系

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 座標系 (1000, 1000) で向き 45度に垂直な直線上で停止
    run.stopToLine(Posiiton(1000, 1000, deg(45)));
    \endcode
  */
  virtual void stopToLine(const VXV::Position& position,
			  const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定角度方向に回転

    座標系で指定された角度の方を向くように転回する
    
    \image html move_rotateToDirection.png 座標系の指定角度へ追従

    \param direction [i] 追従角度
    \param crd [i] 座標系

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 145度の方向を向く
    run.rotateToDirection(deg(145));
    \endcode
  */
  virtual void rotateToDirection(const VXV::Direction& direction,
				 const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定角度だけ回転

    指定された角度だけ相対的に転回する

    \image html move_rotateAngle.png 指定角度だけ転回する

    \param direction [i] 回転角度

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 時計回りに１転回する
    run.rotateAngle(deg(-360));
    \endcode
  */
  virtual void rotateAngle(const VXV::Direction& direction) = 0;

  /*!
    \brief 連続転回

    指定速度で回転し続ける

    \image html move_spin.png 指定速度で転回し続ける

    \param velocity [i] 転回速度

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 30[deg/sec]で時計回りに回転し続ける
    run.spin(deg(-30));
    \endcode
  */
  virtual void spin(const VXV::Direction& velocity) = 0;

  /*!
    \brief 過去の移動コマンドを再発行

    直前に発行した移動コマンドを再度発行する。発行の際には、過去にコマンドを発行したときの座標系が今も存在するのを確認するために、そのコマンドを発行したときの座標系を指定する必要がある。

    \param crd [i] 座標系

    使用例
    \code
    // 移動コマンドの発行
    run.followLine(VXV::Position(0, 0, deg(0)));
    VXV::Delay(1000);

    // 障害物があった場合など、直前に発行した移動コマンドを待避してから停止
    run.push_runState();
    run.stop();
    VXV::Delay(1000);

    // 移動コマンドの再発行
    run.pop_runState();
    run.lastMoveCommand(&run);
    \endcode
  */
  virtual void lastMoveCommand(const CoordinateCtrl* crd) = 0;


  /*!
    \brief 移動の制御状態が安定しているかを返す

    直線追従であれば、直線上を走行しているとき、停止コマンドであれば、停止しているときに true が返される。現在実行中のコマンドの制御目標を達成しているかどうかを返す

    \retval true 安定
    \retval false 安定にむけて制御中

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 目的の方向への回転速度がゼロ近くになったら、次の移動コマンドを発行
    run.rotateToDirection(deg(90));
    while (!run.isStable()) {
      VXV::Delay(100);
    }
    run.followLine(VXV::Position(0, 0, deg(90)));
    \endcode
  */
  virtual bool isStable(void) = 0;

  /*!
    \param 指定位置と推定自己位置との距離を返す
    
    推定自己位置と、指定座標系の点との距離を返す

    \image html judge_lengthToGrid.png 距離

    \param grid [i] 指定位置
    \param crd [i] 座標系

    \return 距離 [mm]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 移動しながら、座標 (1000, 1000) との距離を表示し続ける
    VXV::Grid grid(1000, 1000);

    run.followLine(VXV::Position(0, 0, deg(0)));
    while (1) {
      printf("length: %d\n", run.getLengthToGrid(grid));
      VXV::Delay(1000);
    }
    \endcode
  */
  virtual int getLengthToGrid(const VXV::Grid& grid,
			      const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定位置と推定自己位置との距離を返す

    \param grid [i] 指定位置
    \param crd [i] 座標系

    \return 距離 [mm]

    \attention getLengthToGrid を用いること
  */
  virtual int getLengthToBody(const VXV::Grid& grid,
			      const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定直線に垂直な直線と推定自己位置との距離を返す
    
    返される値は、指定直線の向きの方に自己位置があれば距離が、指定直線の向きとは反対側に自己位置があれば、距離に負号を付けた値が返される

    \image html judge_lengthToLine.png 直線との距離

    \param line [i] 指定直線
    \param crd [i] 座標系

    \return 距離 [mm]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 次の移動直線(1000, 0, deg(90))に近づいたらその直線への追従コマンドを発行
    VXV::Position next_line(1000, 0, deg(90));
    run.followLine(VXV::Position(0, 0, deg(0));
    while (run.getLengthToLine(next_line) < -300) {
      VXV::Delay(100);
    }
    run.followLine(next_line);
    \endcode
  */
  virtual int getLengthToLine(const VXV::Position& line,
			      const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定角度との差を返す
    
    座標系で指定された角度と推定自己位置の角度との差を返す

    \image html judge_angleToDirection.png 角度差

    \param t [i] 指定角度
    \param crd [i] 座標系

    \return 角度差
  */
  virtual VXV::Direction getAngleToDirection(const VXV::Direction& t,
					     const CoordinateCtrl* crd) = 0;

  /*!
    \brief 指定並進速度との差を返す

    指定した速度と現在の移動速度との差を返す

    \image html judge_straightVelDiff.png 速度差

    \param mm_vel [i] 指定並進速度 [mm/sec]
    
    \return 並進速度の差 [mm/sec]
  */
  virtual int getStraightVelDiff(int mm_vel) = 0;

  /*!
    \brief 指定回転速度との差を返す

    指定回転速度と現在の回転速度との差を返す
    
    \image html judge_rotateVelDiff.png 回転速度差

    \param t [i] 指定回転速度 [direction/sec]

    \return 回転速度の差 [direction/sec]
  */
  virtual VXV::Direction getRotateVelDiff(const VXV::Direction& t) = 0;

  /*!
    \brief 目標並進速度を設定

    直線追従や円弧追従などで実現しようとする並進速度を設定する

    \param mm_sec [i] 目標並進速度 [mm]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 並進速度を 100[mm/sec]に設定する
    run.setStraightRefVel(100);
    run.followLine(VXV::Position(0, 0, deg(0))); // 移動速度は 100[mm/sec]
    \endcode
  */
  virtual void setStraightRefVel(int mm_sec) = 0;

  /*!
    \brief 目標並進加速度を設定

    並進速度の制御に用いられる角速度を設定する

    \param mm_acc [i] 目標加速度 [mm]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 並進の加速度を 100[mm/sec^2]に設定する
    run.setStraightRefAcc(100);

    // 移動の加速度は 100[mm/sec^2]
    run.followLine(VXV::Position(0, 0, deg(0)));
    \endcode
  */
  virtual void setStraightRefAcc(int mm_acc) = 0;

  /*!
    \brief 目標角速度を設定

    目標角度への追従コマンドや転回コマンドの際に用いられる回転速度を設定する

    \param rotate_vel [i] 目標角速度 [VXV::Direction]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 回転角速度を 90[deg/sec]に設定する
    run.setRotateRefVel(deg(90));
    run.rotateAngle(deg(360));	// 回転角速度は 90[deg/sec]
    \endcode
  */
  virtual void setRotateRefVel(const VXV::Direction& rotate_vel) = 0;

  /*!
    \brief 目標角加速度を設定

    回転制御の際に用いられる角加速度を設定する

    \param rotate_acc [i] 目標角加速度 [VXV::Direction]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 回転角加速度を 90[deg/sec^2]に設定する
    run.setRotateRefAcc(deg(90));
    run.rotateAngle(deg(360));	// 回転角加速度は 90[deg/sec^2]
    \endcode
  */
  virtual void setRotateRefAcc(const VXV::Direction& rotate_acc) = 0;

  /*!
    \brief 追従時の曲率を設定

    推定自己位置が目標経路上にない場合、ここで設定される曲率を保ちながら目標経路に追従しようとする。従って、r が小さいほど急激に転回して経路追従を行い、r が大きければなだらかに目標経路に追従する

    \image html follow_curve.png

    \param mm [i] 追従時の曲率 [mm]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 追従の曲率を大きめに設定
    run.setCurveRadius(600);
    \endcode
  */
  virtual void setCurveRadius(int mm) = 0;

  /*!
    \brief 追従時の曲率の設定を取得

    現在の設定されている追従時の曲率の値を取得する

    \retval 追従時曲率の設定値

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 現在の曲率設定を取得して表示
    printf("curve radius: %d\n", run.getCurveRadius());
    \endcode
  */
  virtual int getCurveRadius(void) = 0;

  /*!
    \brief 並進速度の取得

    並進速度を取得する

    \retval 並進速度 [mm/sec]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 移動速度を表示し続ける
    run.followLine(Position(0, 0, deg(0)));
    while (1) {
      printf("vel: %d\n", run.getStraightVel());
      VXV::Delay(100);
    }
    \endcode
  */
  virtual int getStraightVel(void) = 0;

  /*!
    \brief 回転速度の取得

    回転速度を取得する

    \retval 回転角速度 [VXV::Direction]

    使用例
    \code
    RunCtrl run;
    run.connect(argc, argv);

    // 移動速度を表示し続ける
    run.spin(deg(90));
    while (1) {
      printf("vel: %d\n", run.getRotateVel().to_deg());
      VXV::Delay(100);
    }
    \endcode
  */
  virtual VXV::Direction getRotateVel(void) = 0;
};

#endif /* !RUN_INTERFACE_H */
