Java 6 Swing IR2

ftComputing : Programme für die fischertechnik-Interfaces und -konstruktionskästen
  
ftComputing.de
Home
Back
Sitemap
Index
Links
Impressum
Mail

 

Industry Robot II mit Java 6 - Swing und BlueJ

Der Industry Robot II - 3-Achs-Roboter : Hier am Robo Interface, Programmierung über ftcomputing.robo.jar.

Aufbau der Robots nach Bauanleitung mit Säule an M1, Endtaster I1, Impulstaster I2, 
Arm waagerecht M2, I3, I4 und Arm senkrecht M3, I5, I6. Der Greifer an M4, I7, I8

Downloads : workSpace34.zip

Klasse SwingMot : Motor M3 fährt links, rechts und aus

Mit dem JLabel lblStatus
und den JButtons cmdLinks, cmdAus und cmdRechts auf dem JFrame

Importe :

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import ftcomputing.robo.*;

Klasse und Konstruktor

public class SwingMot extends JFrame implements ActionListener {
  private JLabel lblStatus;
  private JButton cmdLinks;
  private JButton cmdAus;
  private JButton cmdRechts;

  private FishFace ft;

  public SwingMot() {
    setTitle("Robo Interface und Swing");
    setLayout(new GridLayout(4,1));

    lblStatus = new JLabel("Motor an M3", JLabel.CENTER);
    add(lblStatus);
    cmdLinks = new JButton("Links");
    cmdLinks.addActionListener(this);
    add(cmdLinks);
    cmdAus = new JButton("Aus");
    cmdAus.addActionListener(this);
    add(cmdAus);
    cmdRechts = new JButton("Rechts");
    cmdRechts.addActionListener(this); 
    add(cmdRechts);

    ft = new FishFace();
    ft.openInterface(0, 0);
  }

Die Klasse SwingMot wird von JFrame abgeleitet und implementiert zur Auswertung der Button-Clicks einen ActionListener.
Im Konstruktor wird nach dem Text für den Frame-Titel das Layout der einzufügenden Controls festgelegt : 
Eine einspaltige Tabelle mit Platz für vier Elemente (es gibt noch andere Möglichkeiten).
Anschließend werden die Controls instantiiert und der Reihe nach auf dem Frame plaziert (add). Bei den JButtons wird noch der benötigte ActionListener (in diesem Fall der in der Klasse implementierte) hinzugefügt (addActionListener(this)).
Zum Schluß noch ein wenig FishFace.

ActionListener

public void actionPerformed(ActionEvent e) {
  Object cmd = e.getSource();
  if(cmd == cmdLinks) {
    ft.setMotor(Mot.M3, Dir.Left);
    lblStatus.setText("M3 : Links");
  }
  else if(cmd == cmdRechts) {
    ft.setMotor(Mot.M3, Dir.Right);
    lblStatus.setText("M3 : Rechts");
  }
  else {
    ft.setMotor(Mot.M3, Dir.Off);
    lblStatus.setText("M3 : Aus");
  }
}

Der ActionListener erfordert die Implementierung der Methode actionPerformed für die Bearbeitung eines MausClicks (Maus drücken und wieder loslassen). Die Methode wird gleichermaßen für alle JButtons eingesetzt.
In der Methode wird deswegen zunächst die Quelle des Ereignisses anhand des Methodenparameters bestimmt und danach verzweigt:
Bei JButton cmdLinks wird Mot.M3 auf Dir.Links geschaltet und dessen Funktion in lblStatus angezeigt. Die anderen Buttons entsprechend.

Test über Klasse MotMain

public class MotMain {
  public static void main() {
    SwingMot frmMain = new SwingMot();
    frmMain.setSize(280, 160);
    frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frmMain.setVisible(true);
  }
}

Getestet wird über die zweite Klasse MotMain und deren static void main Methode.
Hier wird zunächst eine Instanz von SwingMot erzeugt, die Größe des Frames wird angegeben. Zusätzlich wird festgelegt das ein Schließen der Form auch die gesamte Anwendung beendet (das ist sonst nicht der Fall, der Frame läuft dann im Verborgenen weiter). Zum Schluß wird der Frame noch sichtbar gemacht.

Klasse SwingMouse : Motor M3 läuft wenn mousePressed

Mit dem JLabel lblStatus
und den JButtons cmdLinks und cmdRechts auf dem JFrame

Importe :

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import ftcomputing.robo.*; wie gehabt

Bei der Steuerung eines Industry Robots ist es angenehmer, wenn der Motor schon beim mouseReleased abschaltet und nicht erst durch Drücken eines weiteren Buttons. Deswegen diese Variante

Klasse und Konstruktor

public class SwingMouse extends JFrame implements MouseListener {
  private JLabel lblStatus;
  private JButton cmdLinks;
  private JButton cmdRechts;

  private FishFace ft;

  public SwingMouse() {
    setTitle("Robo Interface mit Maus");
    setLayout(new GridLayout(3, 1));

    lblStatus = new JLabel("Motor an M3", JLabel.CENTER);
    add(lblStatus);
    cmdLinks = new JButton("Links");
    cmdLinks.addMouseListener(this);
    add(cmdLinks);
    cmdRechts = new JButton("Rechts");
    cmdRechts.addMouseListener(this); 
    add(cmdRechts);

    ft = new FishFace();
    ft.openInterface(0, 0);
  }
  public static void main() {
    SwingMouse frmMain = new SwingMouse();
    frmMain.setSize(280, 160);
    frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frmMain.setVisible(true);
  }

Neu ist hier eigentlich nur das implements MouseListener anstelle von ActionListener. Außerdem wurde die static void main in die Klasse integriert, man spart dann eine (sehr kleine Extraklasse).

MouseListener

public void mousePressed(MouseEvent e) {
  Object cmd = e.getSource();
  if(cmd == cmdLinks) {
    ft.setMotor(Mot.M3, Dir.Left);
    lblStatus.setText("M3 : Links");
  }
  else if(cmd == cmdRechts) {
    ft.setMotor(Mot.M3, Dir.Right);
    lblStatus.setText("M3 : Rechts");
  }
}
public void mouseReleased(MouseEvent e) {
  ft.setMotor(Mot.M3, Dir.Off);
  lblStatus.setText("M3 : Aus");
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited (MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}

Der MouseListener erfordert die Implementierung von insgesamt fünf Methoden. Wenn nicht alle benötigt werden reicht ein leerer Prozedurrumpf.
Hier in mousePressed nur noch Motor links und rechts und bei mouseReleased Motor aus. Vom Inhalt her wie bei SwingMot

Klasse RobTouch : Der ganze Robot wird gescheucht

Die Zahl der Buttons hat deutlich zugenommen. Die dahinter liegenden Funktionen unterscheiden sich : (Greifer) AUF, ZU und (Robot) HOME werden bei Klick auf den Button vollständig ausgeführt. Die anderen Funktionen werden bei mouseReleased abgebrochen.
Um die Buttons flexibler auf dem Frame anordnen zu können, wurden sie auf JPanel pnlMain untergebracht (JLabel auf pnlInfo).
Hinzugekommen ist noch ein WindowListener um endlich die FishFace-Instanz sauber beenden zu können

Klasse und Konstruktor

public class RobTouch extends JFrame implements MouseListener, WindowListener {

  private JLabel lblStatus;
  private JButton cmdAuf;
  private JButton cmdRauf;
  ......
  private FishFace ft;

  public RobTouch(int x, int y) {
    super("RobTouch");

    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setLayout(new BorderLayout());
    setSize(x, y);
    addWindowListener(this);

    JPanel pnlInfo = new JPanel();
    add(pnlInfo, BorderLayout.NORTH);
    lblStatus = new JLabel("Ready", JLabel.CENTER);
    pnlInfo.add(lblStatus);

    JPanel pnlMain = new JPanel();
    pnlMain.setLayout(new GridLayout(3, 3));
    add(pnlMain, BorderLayout.SOUTH);

    cmdAuf = new JButton("AUF");
    cmdAuf.addMouseListener(this);
    pnlMain.add(cmdAuf);
    ...
    ft = new FishFace();
    ft.openInterface(0, 0);
  }

  public static void main() {
    JFrame frm = new RobTouch(320, 160);
    frm.setLocation(300, 200);
    frm.setVisible(true);
  }

Aufbau (beinahe) wie gehabt, es ist aber deutlich mehr geworden. Das JFrame wird hier mit einem BorderLayout versehen. Das erlaubt die gezielte Plazierung der von pnlInfo und pnlMain oben (NORTH) bzw unten (SOUTH) auf dem JFrame. pnlMain mit den JButtons hat jetzt eine 3 * 3 Gitterlayout. Im main() wird mit setLocation(300, 200) zusätzlich die Position des JFrame auf dem Bildschirm festgelegt.

MouseListener

public void mousePressed(MouseEvent e) {
  Object cmd = e.getSource();
  if(cmd == cmdAuf) {
    lblStatus.setText("Greifer öffnet");
    ft.setMotor(Mot.M4, Dir.Left, Speed.Full, 9999);
  }
....

  else if(cmd == cmdHome) {
    lblStatus.setText("Robot fährt auf Home-Position");
    ft.setMotor(Mot.M1, Dir.Left, Speed.Full, 9999);
    ft.setMotor(Mot.M2, Dir.Left, Speed.Full, 9999);
    ft.setMotor(Mot.M3, Dir.Left, Speed.Full, 9999);
    ft.setMotor(Mot.M4, Dir.Left, Speed.Full, 9999);
  }
  else if(cmd == cmdRechts) {
    lblStatus.setText("Säule dreht nach rechts");
    ft.setMotor(Mot.M1, Dir.Right);
  }
....
}
public void mouseReleased(MouseEvent e) {
  Object cmd = e.getSource();
  if(cmd == cmdAuf) ft.waitForMotors(0, Mot.M4);
....
  else if(cmd == cmdHome) 
    ft.waitForMotors(0, Mot.M1, Mot.M2, Mot.M3, Mot.M4); 
  else if(cmd == cmdRechts) ft.setMotor(Mot.M1, Dir.Off);
....
  lblStatus.setText("Idle");
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}

Mit der Methode mousePressed werden die Actionen angestossen und mit mouseReleased abgeschlossen bzw. beendet. Am Verfahren hat sich weiter nichts geändert.

WindowListener

public void windowClosing(WindowEvent e) {
  ft.closeInterface();
}
public void windowClosed(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowOpened(WindowEvent e) {}

Eigentlich nur bei windowsClosing ein schlichtes ft.closeInterface(); Hinzu kommen dann aber noch die restlichen (Dummy) Methoden.

Klasse RobPos : RobTouch + Positionsanzeige

Aufbau wie gehabt, jetzt aber Anzeige der aktuellen Position in JLabel lblStatus.

MouseListener

public void mousePressed(MouseEvent e) {
  Object cmd = e.getSource();
....
  else if(cmd == cmdRauf) {
    lblStatus.setText("Arm geht nach oben");
    ft.setMotor(Mot.M3, Dir.Left, Speed.Full, 9999);
}
....
  else if(cmd == cmdRunter) {
    lblStatus.setText("Arm geht nach unten");
    ft.setMotor(Mot.M3, Dir.Right, Speed.Full, 9999);
  }
  else if(cmd == cmdRueck) {
    lblStatus.setText("Arm geht nach hinten");
    ft.setMotor(Mot.M2, Dir.Left, Speed.Full, 9999);
  }
}
public void mouseReleased(MouseEvent e) {
Object cmd = e.getSource();
....
  else if(cmd == cmdRauf) {
    ft.setMotor(Mot.M3, Dir.Off);
    actPos[Mot.M3-1] -= 9999 - ft.getCounter(Inp.I6);
    if(actPos[Mot.M3-1] < 0) actPos[Mot.M3-1] = 0;
    lblStatus.setText("Arm vertikal : " + actPos[Mot.M3-1]);
   }
....
  else if(cmd == cmdRunter) {
    ft.setMotor(Mot.M3, Dir.Off);
    actPos[Mot.M3-1] += 9999 - ft.getCounter(Inp.I6);
    lblStatus.setText("Arm vertikal : " + actPos[Mot.M3-1]);
  }
  else if(cmd == cmdRueck) {
    ft.setMotor(Mot.M2, Dir.Off); 
    actPos[Mot.M2-1] -= 9999 - ft.getCounter(Inp.I4);
    if(actPos[Mot.M2-1] < 0) actPos[Mot.M2-1] = 0;
    lblStatus.setText("Arm horizontal : " + actPos[Mot.M2-1]);
  }
}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseClicked(MouseEvent e) {}

Bei mousePressed cmd == cmdRauf wird die Action "Arm geht nach oben" angestoßen
Bei mouseReleased cmd == cmdRauf wird die Action durch setMotor(  Dir.Off   ) beendet und der aktuelle Zählerstand (der beim Start der Action zurückgesetzt wurde) ausgewertet. Der  hypothetische Weg von 9999 Impulsen wird um die tatsächliche Impulszahl vermindert und auf die letzte actPos addiert (Dir.Right) bzw. von der letzten actPos subtrahiert (Dir.Left). Bei Dir.Left wird zusätzlcih noch auf 0 korrigiert wenn der Motor zulange am Endtaster gewürgt hatte.

Klasse RobTeach : RobPos + TeachIn->Run

Aufbau des Robots wie gehabt. Hinzugekommen sind die Buttons :
CLEAR : Löschen der internen Positionsliste
ADD : Hinzufügen der aktuellen Position zu robListe
RUN : Betreiben des Robots nach robListe

mouseClicked

Wesentliches neues Element ist der Code für das bisher nur als Methodenrumpf vorliegende Ereignis mouseClicked und die dazu gehörende private Methode runListe.

public void mouseClicked(MouseEvent e) {
  Object cmd = e.getSource();
   if(cmd == cmdRun) {
     runListe();
   }
   else if(cmd == cmdClear) {
      anzPos = 0;
      lblStatus.setText("robListe gelöscht");
   }
   else if(cmd == cmdAdd) {
      for(int i = 0; i < 4; i++) robListe[anzPos][i] = actPos[i];
      if(anzPos < (robListe.length-1)) anzPos++;
      lblStatus.setText("Länge robListe : " + anzPos);
  }
}

private void runListe() {
   for(int i = 0; i < anzPos; i++) {
      for(int j = 0; j < 3; j++) {
         int zPos = robListe[i][j];
 
        if(zPos < actPos[j]) ft.setMotor(j+1, Dir.Left,Speed.Full, actPos[j]-zPos);
         else if(zPos > actPos[j]) ft.setMotor(j+1, Dir.Right, Speed.Full, zPos-actPos[j]);
      }
      ft.waitForMotors(0, Mot.M1, Mot.M2, Mot.M3);
      for(int j = 0; j < 3; j++) actPos[j] = robListe[i][j];
      if(robListe[i][3] == 0) {
        ft.setMotor(Mot.M4, Dir.Left, Speed.Full, 9999);
        ft.waitForMotors(0, Mot.M4);
        gripOpen = 0;
      } else if(gripOpen == 0){
           ft.setMotor(Mot.M4, Dir.Right, Speed.Full, GripClosed);
           ft.waitForMotors(0, Mot.M4);
           gripOpen = GripClosed;
      }
      lblStatus.setText("Run : " + i);
   }
}

Mit Button CLEAR wird der Zeiger auf den ersten freien Platz in robListe auf 0 zurückgesetzt
Mit ADD wird die aktuelle Position des Robots in die robListe eingetragen und der Zeiger auf den ersten freien Platz erhöht. Aus Gründen der Einfachheit wird bei voller robListe "auf der Stelle getreten".
RUN ruft runListe das die in robListe eingetragenen Robot-Positionen abarbeitet. Dazu zunächst eine äußere Schleife über die Einzelpositionen von robListe.
In der Schleife Starten der Motoren für Säule, Arm vertikal und Arm horizontal. Warten auf das Erreichen aller vorgegebenen Einzelpositionen und notieren der neuen Positionen in actPos.
Anschließend wird sich um den Greifer gekümmert.

Das wars dann auch schon. Die neuen Variablen-Deklarationen ... kann man der Source (diesmal aber Eclipse 3.4) entnehmen.

Wie gehts weiter : Hier nicht, aber der eigenen Phantasie und Tatkraft sind keine Grenzen gesetzt : 
- Überarbeiten der GUI : mehr Abstand, Rahmen
- Einfügen eines Listen-Widgets zur Anzeige der in robListe eingetragenen Positionen verbunden mit der Möglichkeit einzelne Einträge zu verändern/löschen.
- Speichern von robListe in eine Datei und Laden der Liste beim Start des Programms
- ....

Es bleibt noch viel zu tun.

Stand : 16.04.2010