/* * RoombaRecorderPanel - * * Copyright (c) 2006 Tod E. Kurt, tod@todbot.com, ThingM * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA * */ package roombacomm; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; import java.util.*; /** * A Panel containing controls for testing RoombaComm. * Normally put inside of a frame, for example see RoombaCommTest * */ public class RoombaRecorderPanel extends JPanel implements ActionListener,Runnable { JPanel ctrlPanel, selectPanel, buttonPanel, displayPanel; JComboBox portChoices; JCheckBox handshakeButton; JTextArea displayText; JButton connectButton; JSlider speedSlider; RoombaCommSerial roombacomm; Thread thread; ArrayList events; ArrayList eventTimes; //ActionEvent[] events; //long[] eventTimes; int eventCount; int eventIndex; int eventMax = 1000; // fixme: long playStartTime; long recStartTime; boolean recording = false; boolean playing = false; boolean looping = false; public RoombaRecorderPanel() { super(new BorderLayout()); roombacomm = new RoombaCommSerial(); events = new ArrayList(); eventTimes = new ArrayList(); eventIndex = 0; eventCount = 0; makeSelectPanel(); add(selectPanel, BorderLayout.NORTH); makeCtrlPanel(); add(ctrlPanel, BorderLayout.EAST); makeButtonPanel(); add(buttonPanel, BorderLayout.CENTER); makeDisplayPanel(); add(displayPanel, BorderLayout.SOUTH); updateDisplay("RoombaComm, version "+RoombaComm.VERSION+"\n"); //bindKeys(); thread = new Thread(this); thread.start(); } /** * Set to 'false' to hide the "h/w handshake" button, which seems to be * only needed on Windows */ public void setShowHardwareHandhake(boolean b) { handshakeButton.setVisible(b); } /** */ public boolean connect() { String portname = (String) portChoices.getSelectedItem(); roombacomm.debug=true; roombacomm.waitForDSR = handshakeButton.isSelected(); connectButton.setText("connecting"); if(!roombacomm.connect(portname)) { updateDisplay("Couldn't connect to "+portname+"\n"); connectButton.setText(" connect "); roombacomm.debug=false; return false; } updateDisplay("Roomba startup\n"); roombacomm.startup(); roombacomm.control(); roombacomm.pause(50); roombacomm.playNote(72, 10); // C , test note roombacomm.pause(200); connectButton.setText("disconnect"); connectButton.setActionCommand("disconnect"); updateDisplay("Roomba connected\n"); roombacomm.debug=true; return true; } /** */ public void disconnect() { roombacomm.disconnect(); connectButton.setText(" connect "); connectButton.setActionCommand("connect"); } public void stop() { //thread.interrupt(); if(recording) eventCount = eventIndex; recording = false; playing = false; updateDisplay("Stopped\n"); } public boolean play() { if(eventCount==0) { updateDisplay("nothing recorded\n"); return false; } if(recording) eventCount = eventIndex; playStartTime = millis(); playing = true; recording = false; eventIndex = 0; updateDisplay("Playback...\n"); return true; } public void record() { recStartTime = millis(); playing = false; recording = true; eventIndex = 0; updateDisplay("Recording...\n"); } public void setLooping(boolean b) { looping = b; } public boolean getLooping() { return looping; } public HashMap cut() { HashMap m = copy(); stop(); eventCount=0; return m; } public HashMap copy() { HashMap m = new HashMap(); m.put("events",events); m.put("eventTimes",eventTimes); m.put("portname", portChoices.getSelectedItem()); updateDisplay("Copied "+eventCount+" events\n"); return m; } public void paste(HashMap m) { stop(); ArrayList al1 = (ArrayList) m.get("events"); ArrayList al2 = (ArrayList) m.get("eventTimes"); portChoices.setSelectedItem(m.get("portname")); if(al1!= null && al2!=null) { events = al1; eventTimes = al2; eventCount = events.size(); updateDisplay("Pasted "+eventCount+" events\n"); } } /** Implement Runnable */ public void run() { boolean done = false; while(!done) { if(playing && eventCount !=0) { long t = ((Long)eventTimes.get(eventIndex)).longValue(); //long t = eventTimes[eventIndex]; if(millis() - playStartTime > t) { ActionEvent event = (ActionEvent) events.get(eventIndex); actionPerformed(event); eventIndex++; if(eventIndex == eventCount) { if(!looping) { playing = false; } else { eventIndex = 0; playStartTime = millis(); } } } } try { Thread.sleep(10); } catch(Exception e) { done = true; } } } /** * Implement Actionlistener, also this is the locus for record & playback * */ public void actionPerformed(ActionEvent event) { String action = event.getActionCommand(); String actstr = ""; if("comboBoxChanged".equals(action)) return; // don't care about comboBox if(recording) { long t = millis() - recStartTime; actstr += t+":r#"+eventIndex+": "; events.add(eventIndex, event); eventTimes.add(eventIndex, new Long(millis()-recStartTime)); eventIndex++; } if(playing) { long t = millis() - playStartTime; actstr += t+":p#"+eventIndex+": "; } updateDisplay(actstr+action+"\n"); /// DEBUG if("connect".equals(action)) { updateDisplay("connecting...\n"); connect(); return; } else if("disconnect".equals(action)) { updateDisplay("disconnecting.\n"); disconnect(); return; } else { try { int speed = Integer.parseInt(action); //updateDisplay("setting speed = "+speed+"\n"); roombacomm.setSpeed(speed); return; } catch(NumberFormatException e) { } } // stop right here if we're not connected if(!roombacomm.connected()) { updateDisplay("not connected!\n"); return; } //updateDisplay(action+"\n"); if("stop".equals(action)) { roombacomm.stop(); } else if("forward".equals(action)) { roombacomm.goForward(); } else if("backward".equals(action)) { roombacomm.goBackward(); } else if("spinleft".equals(action)) { roombacomm.spinLeft(); } else if("spinright".equals(action)) { roombacomm.spinRight(); } else if("turnleft".equals(action)) { roombacomm.turnLeft(); } else if("turnright".equals(action)) { roombacomm.turnRight(); } else if(action.matches("^turn[-+0-9]+$")) { int turnval = Integer.parseInt(action.substring(4)); System.out.println("turnval="+turnval); roombacomm.turn(turnval); } else if("test".equals(action)) { updateDisplay("Playing some notes\n"); roombacomm.playNote(72, 10); // C roombacomm.pause(200); roombacomm.playNote(79, 10); // G roombacomm.pause(200); roombacomm.playNote(76, 10); // E roombacomm.pause(200); updateDisplay("Spinning left, then right\n"); roombacomm.spinLeft(); roombacomm.pause(1000); roombacomm.spinRight(); roombacomm.pause(1000); roombacomm.stop(); updateDisplay("Going forward, then backward\n"); roombacomm.goForward(); roombacomm.pause(1000); roombacomm.goBackward(); roombacomm.pause(1000); roombacomm.stop(); } else if("reset".equals(action)) { roombacomm.stop(); roombacomm.startup(); roombacomm.control(); } else if("power-off".equals(action)) { roombacomm.powerOff(); } else if("wakeup".equals(action)) { roombacomm.wakeup(); } else if("beep-lo".equals(action)) { roombacomm.playNote(36, 10); // C1 roombacomm.pause(200); } else if("beep-hi".equals(action)) { roombacomm.playNote(120, 10); // C7 roombacomm.pause(200); } else if("clean".equals(action)) { roombacomm.clean(); } else if("spot".equals(action)) { roombacomm.spot(); } else if("vacuum-on".equals(action)) { roombacomm.vacuum(true); } else if("vacuum-off".equals(action)) { roombacomm.vacuum(false); } else if("blink-leds".equals(action)) { roombacomm.setLEDs(true,true,true, true,true,true, 255, 255); roombacomm.pause(300); roombacomm.setLEDs(false,false,false, false,false,false, 0, 128); } else if("sensors".equals(action)) { if(roombacomm.updateSensors()) updateDisplay(roombacomm.sensorsAsString()+"\n"); else updateDisplay("couldn't read Roomba. Is it connected?\n"); } } /** * */ void makeSelectPanel() { selectPanel = new JPanel(); //Create a combo box with choices. String[] ports = roombacomm.listPorts(); portChoices = new JComboBox(ports); portChoices.setSelectedIndex(0); connectButton = new JButton(); connectButton.setText(" connect "); connectButton.setActionCommand("connect"); handshakeButton = new JCheckBox("h/w
handshake"); //Add a border around the select panel. selectPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Select Roomba Port"), BorderFactory.createEmptyBorder())); selectPanel.add(portChoices); selectPanel.add(connectButton); selectPanel.add(handshakeButton); //Listen to events from the combo box. portChoices.addActionListener(this); connectButton.addActionListener(this); } /** * */ void makeCtrlPanel() { JPanel ctrlPanel1 = new JPanel(new GridLayout(3,3)); JButton but_turnleft = new JButton(createImageIcon("images/but_turnleft.png","turnleft")); ctrlPanel1.add(but_turnleft); JButton but_forward = new JButton(createImageIcon("images/but_forward.png","forward")); ctrlPanel1.add(but_forward); JButton but_turnright = new JButton(createImageIcon("images/but_turnright.png","turnright")); ctrlPanel1.add(but_turnright); JButton but_spinleft = new JButton(createImageIcon("images/but_spinleft.png","spinleft")); ctrlPanel1.add(but_spinleft); JButton but_stop = new JButton(createImageIcon("images/but_stop.png", "stop")); ctrlPanel1.add(but_stop); JButton but_spinright = new JButton(createImageIcon("images/but_spinright.png","spinright")); ctrlPanel1.add(but_spinright); ctrlPanel1.add(new JLabel()); JButton but_backward = new JButton(createImageIcon("images/but_backward.png","backward")); ctrlPanel1.add(but_backward); ctrlPanel1.add(new JLabel()); JLabel speedLabel = new JLabel("speed (mm/s)", JLabel.CENTER); JRadioButton speed050button = new JRadioButton("50"); JRadioButton speed100button = new JRadioButton("100"); JRadioButton speed150button = new JRadioButton("150"); JRadioButton speed250button = new JRadioButton("250"); JRadioButton speed400button = new JRadioButton("400"); JRadioButton speed500button = new JRadioButton("500"); speed050button.setVerticalTextPosition(SwingConstants.BOTTOM); speed050button.setHorizontalTextPosition(SwingConstants.CENTER); speed100button.setVerticalTextPosition(SwingConstants.BOTTOM); speed100button.setHorizontalTextPosition(SwingConstants.CENTER); speed150button.setVerticalTextPosition(SwingConstants.BOTTOM); speed150button.setHorizontalTextPosition(SwingConstants.CENTER); speed250button.setVerticalTextPosition(SwingConstants.BOTTOM); speed250button.setHorizontalTextPosition(SwingConstants.CENTER); speed400button.setVerticalTextPosition(SwingConstants.BOTTOM); speed400button.setHorizontalTextPosition(SwingConstants.CENTER); speed500button.setVerticalTextPosition(SwingConstants.BOTTOM); speed500button.setHorizontalTextPosition(SwingConstants.CENTER); speed250button.setSelected(true); speed050button.addActionListener(this); speed100button.addActionListener(this); speed150button.addActionListener(this); speed250button.addActionListener(this); speed400button.addActionListener(this); speed500button.addActionListener(this); ButtonGroup speedGroup = new ButtonGroup(); speedGroup.add(speed050button); speedGroup.add(speed100button); speedGroup.add(speed150button); speedGroup.add(speed250button); speedGroup.add(speed400button); speedGroup.add(speed500button); JPanel ctrlPanel2 = new JPanel(); ctrlPanel2.add(speed050button); ctrlPanel2.add(speed100button); ctrlPanel2.add(speed150button); ctrlPanel2.add(speed250button); ctrlPanel2.add(speed400button); ctrlPanel2.add(speed500button); ctrlPanel = new JPanel(); ctrlPanel.setLayout(new BoxLayout(ctrlPanel, BoxLayout.Y_AXIS)); ctrlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Movement"), BorderFactory.createEmptyBorder()));//5,5,5,5))); ctrlPanel.add(ctrlPanel1); ctrlPanel.add(ctrlPanel2); ctrlPanel.add(speedLabel); but_turnleft.setActionCommand("turnleft"); but_turnright.setActionCommand("turnright"); but_spinleft.setActionCommand("spinleft"); but_spinright.setActionCommand("spinright"); but_forward.setActionCommand("forward"); but_backward.setActionCommand("backward"); but_stop.setActionCommand("stop"); but_turnleft.addActionListener(this); but_turnright.addActionListener(this); but_spinleft.addActionListener(this); but_spinright.addActionListener(this); but_forward.addActionListener(this); but_backward.addActionListener(this); but_stop.addActionListener(this); } /** * */ void makeButtonPanel() { buttonPanel = new JPanel(new GridLayout(8,2)); buttonPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Commands"), BorderFactory.createEmptyBorder()));//5,5,5,5))); JButton but_reset = new JButton("reset"); JButton but_test = new JButton("test"); JButton but_power = new JButton("power-off"); JButton but_wakeup = new JButton("wakeup"); JButton but_beeplo = new JButton("beep-lo"); JButton but_beephi = new JButton("beep-hi"); JButton but_clean = new JButton("clean"); JButton but_spot = new JButton("spot"); JButton but_vacon = new JButton("vacuum-on"); JButton but_vacoff = new JButton("vacuum-off"); JButton but_blinkleds = new JButton("blink-leds"); JButton but_sensors = new JButton("sensors"); buttonPanel.add(but_reset); buttonPanel.add(but_test); buttonPanel.add(but_power); buttonPanel.add(but_wakeup); buttonPanel.add(but_beeplo); buttonPanel.add(but_beephi); buttonPanel.add(but_clean); buttonPanel.add(but_spot); buttonPanel.add(but_vacon); buttonPanel.add(but_vacoff); buttonPanel.add(but_blinkleds); buttonPanel.add(but_sensors); but_reset.addActionListener(this); but_test.addActionListener(this); but_power.addActionListener(this); but_wakeup.addActionListener(this); but_beeplo.addActionListener(this); but_beephi.addActionListener(this); but_clean.addActionListener(this); but_spot.addActionListener(this); but_vacon.addActionListener(this); but_vacoff.addActionListener(this); but_blinkleds.addActionListener(this); but_sensors.addActionListener(this); } /** * */ void makeDisplayPanel() { displayPanel = new JPanel(); displayPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Display"), BorderFactory.createEmptyBorder()));//1,1,1,1))); displayText = new JTextArea(5,30); displayText.setEditable(false); displayText.setLineWrap(true); DefaultCaret dc = new DefaultCaret(); // only works on Java 1.5+ //dc.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); displayText.setCaret(dc); JScrollPane scrollPane = new JScrollPane(displayText, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); displayPanel.add(scrollPane); } public void updateDisplay(String s) { displayText.append(s); displayText.setCaretPosition(displayText.getDocument().getLength()); } /** Returns an ImageIcon, or null if the path was invalid. */ protected static ImageIcon createImageIcon(String path, String description) { // yes, this is supposed to say "RoombaCommTest" java.net.URL imgURL = RoombaCommPanel.class.getResource(path); if (imgURL != null) { return new ImageIcon(imgURL, description); } else { System.err.println("Couldn't find file: " + path); return null; } } public int millis() { int millisOffset = 0; return (int) (System.currentTimeMillis() - millisOffset); } }