目標: ロボットのリソース(部品を表すオブジェクト)を取得し、GUIで遠隔操作する。
Materealライブラリではロボットは種類ごとにRobotインタフェースの実装クラスとして定義されており、車輪やカメラといった部品(リソース)の集合を持っています。ロボットを直接操作するためには、ロボットを表すインスタンスからリソースを取得し、そのリソースが持つメソッドを呼び出します。
車輪は複数のスレッドから操作されると指示が競合します。これを避けるため、排他制御が必要なリソースは一つのオブジェクトから取得されるとロックされ、他からは取得できなくなります。排他的に使用しているリソースは、使用後に解放する必要があります。
ネットタンサーを7秒間前進させて停止、終了するコードは次のようになります。(GoForward.javaとほぼ同内容)
requestResource(Class<Resource>, Object)の第二引数は、排他制御のためのロックに使われるだけなので、後のfreeResource(Resource, Object)の第二引数と同じオブジェクトを指していれば何でもよいです。
import jp.digitalmuseum.mr.Matereal; import jp.digitalmuseum.mr.entity.NetTansor; import jp.digitalmuseum.mr.entity.Robot; import jp.digitalmuseum.mr.resource.WheelsController; /** * Go forward for 7 seconds and stop. * * @author Jun KATO */ public class GoForward { public static void main(String[] args) { Robot robot = new NetTansor("Tansor", "http://192.168.32.94:8081"); WheelsController wheels = robot.requestResource(WheelsController.class, GoForward.class); wheels.goForward(); try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } finally { wheels.stopWheels(); robot.freeResource(wheels, GoForward.class); Matereal.getInstance().dispose(); } } }
なお、車輪の動作状況を見ることには排他制御は不要です。一般に、排他制御はリソースへの書き込みに関しては必要ですが、読み出しには不要です。
このような場合を考慮して、Materealでは同じリソースオブジェクトを異なるインタフェースで取得することができるようになっています。例えば、車輪を表すインタフェースはWheelsControllerとWheelsの二つあり、前者が排他制御を必要とするインタフェースで後者が読み取り専用のインタフェースです。排他制御が必要なインタフェースはExclusiveResourceを継承しています。それぞれ用意されているメソッドが違うことに注意してください。
次のようなコードは、車輪を操作する何らかの排他制御が働いているときにも成功します。
Wheels w = robot.requestResource(Wheels.class, this); System.out.println(w.getStatus());
基本が分かってしまえばあとはGUIプログラミングです。
以下のコードのベースはEclipseのVisual Editorで作りました。コンストラクタでロボットをインスタンス化し、リソースを取得しています。ボタンがクリックされたときに、前進・後退・停止といったリソースのメソッドを呼び出しています。また、終了時に呼び出されるdispose()でリソースを解放しています。
import java.awt.BorderLayout; import javax.swing.JPanel; import javax.swing.JFrame; import javax.swing.JButton; import jp.digitalmuseum.mr.entity.NetTansor; import jp.digitalmuseum.mr.entity.Robot; import jp.digitalmuseum.mr.resource.WheelsController; /** * Show a controller GUI for a robot. * * @author Jun KATO */ public class SimpleButtonController extends JFrame { private static final long serialVersionUID = 1L; private JPanel jContentPane = null; private JButton jStopButton = null; private JButton jForwardButton = null; private JButton jRightButton = null; private JButton jLeftButton = null; private JButton jBackwardButton = null; private transient Robot robot; private transient WheelsController wheels; public static void main(String[] args) { new SimpleButtonController(); } /** * This is the default constructor */ public SimpleButtonController() { super(); robot = new NetTansor("Tansor", "http://192.168.32.94:8081"); wheels = robot.requestResource(WheelsController.class, this); initialize(); setVisible(true); } public void dispose() { super.dispose(); robot.freeResource(wheels, this); } /** * This method initializes this * * @return void */ private void initialize() { this.setSize(300, 200); this.setContentPane(getJContentPane()); this.setTitle("Controller"); this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); } // (略) /** * This method initializes jForwardButton * * @return javax.swing.JButton */ private JButton getJForwardButton() { if (jForwardButton == null) { jForwardButton = new JButton(); jForwardButton.setText("Go Forward"); jForwardButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent e) { System.out.println("Go Forward"); wheels.goForward(); } }); } return jForwardButton; } // (略) }
全文はSimpleButtonController.javaを読んでください。
こちらのほうが、ロボットを操作する部分が簡潔なコードにまとまりました。
drawableFrame.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: case KeyEvent.VK_KP_UP: wheels.goForward(); break; case KeyEvent.VK_LEFT: case KeyEvent.VK_KP_LEFT: wheels.spinLeft(); break; case KeyEvent.VK_RIGHT: case KeyEvent.VK_KP_RIGHT: wheels.spinRight(); break; case KeyEvent.VK_DOWN: case KeyEvent.VK_KP_DOWN: wheels.goBackward(); break; case KeyEvent.VK_ESCAPE: default: wheels.stop(); break; } } });
全文はSimpleKeyController.javaを読んでください。
シンプルでないKeyController.javaもあります。こちらは差動車輪の左右の出力を動的に変えられます。また、出力値をリアルタイムにGUIで表示します。