JavaFxとleJOSでEv3ラジコンを作る【後編】

## はじめに
この記事はleJOSの開発環境が整っていることが前提にしています。
詳しくはこちらの記事を参考にして下さい。
>
[【leJOS】mindstorm-EV3をJavaでプログラミングしよう【環境構築前編】
](http://qiita.com/murotani/items/2790f1ef5afed3a5d13c)

>
[【leJOS】mindstorm-EV3をJavaでプログラミングしよう【環境構築後編】](http://qiita.com/murotani/items/0972e6f36676c61e3f57)

##概要
この記事は以下の記事の続編となっています。leJOSのリモートコントロール用クラスremoteEv3とJavaFxを使ってEv3を制御するGUIアプリケーションの開発を解説します。

>
[JavaFxとleJOSでEv3ラジコンを作る【前編】](http://qiita.com/murotani/private/c82595246c270f9ced5b)

今回作成したものは簡単なラジコンアプリです。
(クリックで動画再生)
[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/sTPPtlvEhs4/0.jpg)](https://www.youtube.com/watch?v=sTPPtlvEhs4)

##プログラム
　ファイル構成は次のようになっています。
プロジェクト名はJavaFxEv3としました。
![スクリーンショット 2017-03-13 23.17.58.png](https://qiita-image-store.s3.amazonaws.com/0/149019/a4bf3254-7153-2d3b-bb30-58aa86e807c7.png)

###Form.fxml
ここにGUIを記述します。SceneBuilderを使って編集できます。

```xml:Form.fxml
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>

<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.FormController">
   <children>
      <Button fx:id="LeftButton" layoutX="100.0" layoutY="180.0" mnemonicParsing="false" onMousePressed="#LeftButtonPressed" onMouseReleased="#LeftButtonReleased" prefHeight="40.0" prefWidth="100.0" text="Left" />
      <Button fx:id="RightButton" layoutX="400.0" layoutY="180.0" mnemonicParsing="false" onMousePressed="#RightButtonPressed" onMouseReleased="#RightButtonReleased" prefHeight="40.0" prefWidth="100.0" text="Right" />
      <Button fx:id="ForwardButton" layoutX="250.0" layoutY="62.0" mnemonicParsing="false" onMousePressed="#ForwardButtonPressed" onMouseReleased="#ForwardButtonReleased" prefHeight="40.0" prefWidth="100.0" text="Forward" />
      <Button fx:id="BackwardButton" layoutX="250.0" layoutY="300.0" mnemonicParsing="false" onMousePressed="#BackwardButtonPressed" onMouseReleased="#BackwardButtonReleased" prefHeight="40.0" prefWidth="100.0" text="Backward" />
   </children>
</Pane>
```
####解説
それぞれのボタンにマウスで押された時と、マウスが離れた時に呼ばれるメソッドを登録しています。onMousePressedにボタンが押された時のメソッド、onMouseReleasedにボタンが離された時のメソッドを登録します。
![スクリーンショット 2017-03-18 12.51.02.png](https://qiita-image-store.s3.amazonaws.com/0/149019/ee1fd44f-59d7-a559-5e49-97f69f4722fe.png)

###application.css
GUIのスタイルを設定できます。

```css:application.css
/*　背景色の設定 */
.root{	
	-fx-background-color: white;
}
/* ボタンの色設定 */
.button {
    -fx-background-color: slateblue; -fx-text-fill: white;
}
/* ボタンマウスオーバー時の色設定 */
.button:hover{
    -fx-background-color: green; -fx-text-fill: white;
}
```
###Main.java

```java:Main.java
package application;
	
import java.net.MalformedURLException;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import lejos.remote.ev3.RMIRegulatedMotor;
import lejos.remote.ev3.RemoteEV3;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.fxml.FXMLLoader;


public class Main extends Application {
	public RemoteEV3 ev3 = null;
	public RMIRegulatedMotor leftMotor = null;
	public RMIRegulatedMotor rightMotor = null;

	@Override
	public void start(Stage primaryStage) {
		try {
			//リモートコントロールのためのインスタンス生成
			ev3 = new RemoteEV3("192.168.2.91");
			ev3.setDefault();
			//モーターオブジェクト作成
			leftMotor = ev3.createRegulatedMotor("A", 'L');
			rightMotor = ev3.createRegulatedMotor("B", 'L');
		} catch (RemoteException | MalformedURLException | NotBoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		try {
			FXMLLoader loader = new FXMLLoader(getClass().getResource("Form.fxml"));
			Pane root = (Pane)loader.load();		
			Scene scene = new Scene(root,600,400);
			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());	
			
			//コントローラにモーターオブジェクトを渡す
			FormController controller = loader.getController();
			controller.setThisLeftMotor(leftMotor);
			controller.setThisRightMotor(rightMotor);

			//画面表示
			primaryStage.setScene(scene);
			primaryStage.show();
			
			//画面が閉じられた時の処理
			primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
		          public void handle(WindowEvent we) {
		              System.out.println("Stage is closing");
		              try {
						leftMotor.close();
						rightMotor.close();
		              } catch (RemoteException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
		              }
		          }
		     });        
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		launch(args);
	}
}
```

####解説
RemoteEv3クラスを使ってモーターにアクセスするオブジェクトを作成します。

```java
			ev3 = new RemoteEV3("192.168.2.91");
			ev3.setDefault();
			//モーターオブジェクト作成
			leftMotor = ev3.createRegulatedMotor("A", 'L');
			rightMotor = ev3.createRegulatedMotor("B", 'L');
```
モーターオブジェクトをコントローラに渡します。

```java
            //コントローラにモーターオブジェクトを渡す
            FormController controller = loader.getController();
            controller.setThisLeftMotor(leftMotor);
            controller.setThisRightMotor(rightMotor);
```
プログラム終了時にモーターへのアクセスを閉じるようにします。これを設定しないとプログラムを再度起動した際にエラーが起きてしまいます。

```java
            primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                  public void handle(WindowEvent we) {
                      System.out.println("Stage is closing");
                      try {
                        leftMotor.close();
                        rightMotor.close();
                      } catch (RemoteException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                      }
                  }
             });      
```

###FormController.java
コントローラ部分です。GUIとメソッドを関連付けします。


```java:FormController.java
package application;

import java.rmi.RemoteException;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.input.MouseEvent;
import lejos.remote.ev3.RMIRegulatedMotor;

public class FormController {
	public RMIRegulatedMotor thisLeftMotor = null;
	public RMIRegulatedMotor thisRightMotor = null;
		
	public void setThisLeftMotor(RMIRegulatedMotor leftMotor) {
        thisLeftMotor = leftMotor;
    }
	
	public void setThisRightMotor(RMIRegulatedMotor rightMotor) {
        thisRightMotor = rightMotor;
    }
	
	@FXML
	Button LeftButton;
	@FXML
	Button RightButton;
	@FXML
	Button ForwardButton;
	@FXML
	Button BackwardButton;
	
	@FXML
    void initialize() {
        System.out.println("初期化処理");     
    }
	
	@FXML
	public void LeftButtonPressed(MouseEvent actionEvent) {
		try {
            thisLeftMotor.forward();
            thisRightMotor.backward();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void LeftButtonReleased(MouseEvent actionEvent) {
		try {
            thisLeftMotor.stop(true);
            thisRightMotor.stop(true);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void RightButtonPressed(MouseEvent actionEvent) {
		try {
			thisLeftMotor.backward();
            thisRightMotor.forward();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void RightButtonReleased(MouseEvent actionEvent) {
		try {
			thisLeftMotor.stop(true);
            thisRightMotor.stop(true);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void ForwardButtonPressed(MouseEvent actionEvent) {
		try {
            thisLeftMotor.forward();
            thisRightMotor.forward();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void ForwardButtonReleased(MouseEvent actionEvent) {
		try {
            thisLeftMotor.stop(true);
            thisRightMotor.stop(true);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void BackwardButtonPressed(MouseEvent actionEvent) {
		try {
            thisLeftMotor.backward();
            thisRightMotor.backward();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
	@FXML
	public void BackwardButtonReleased(MouseEvent actionEvent) {
		try {
            thisLeftMotor.stop(true);
            thisRightMotor.stop(true);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
	}
	
}
```
####解説
コントローラ内からモーターオブジェクトにアクセスするためにセッターを定義します。

```java
	public void setThisLeftMotor(RMIRegulatedMotor leftMotor) {
        thisLeftMotor = leftMotor;
    }
	
	public void setThisRightMotor(RMIRegulatedMotor rightMotor) {
        thisRightMotor = rightMotor;
    }
```

例えば以下のコードではLeftButtonが押された時に呼び出されるLeftButtonPressedメソッド、LeftButtonが離された時に呼び出されるLeftButtonReleasedメソッドを定義しています。

```java
    @FXML
    public void LeftButtonPressed(MouseEvent actionEvent) {
        try {
            thisLeftMotor.forward();
            thisRightMotor.backward();
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @FXML
    public void LeftButtonReleased(MouseEvent actionEvent) {
        try {
            thisLeftMotor.stop(true);
            thisRightMotor.stop(true);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
```
##まとめ
JavaFxとleJOSのRemoteEv3クラスを使ったGUIアプリの開発方法を紹介しました。
これを使えばセンサー値を取得してGUI上に可視化するといったこともできるでしょう。
RemoteEv3クラスに関してはこちらの記事も参考にして下さい。
>
[【leJOS】JavaでEV3のモーターをリモートコントロールしよう](http://qiita.com/murotani/items/a521e646032e08279ae0)
>
[【leJOS】JavaでEV3のセンサー値をリモートから取得する](http://qiita.com/murotani/items/e14c4b05b781ecf893d3)